commit f6fdea5355793cd014da1da68ebd2970eee8f126 Author: Allen Webster Date: Fri Sep 29 18:17:40 2023 -0700 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..79a9eda --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +test_data/input_data/*.4id + diff --git a/dist_files/4coder_fleury/bindings.4coder b/dist_files/4coder_fleury/bindings.4coder new file mode 100644 index 0000000..39b8acb --- /dev/null +++ b/dist_files/4coder_fleury/bindings.4coder @@ -0,0 +1,219 @@ + +keys_global = +{ + // NOTE(rjf): Meta + { "exit_4coder" , "F4", "Alt" }, + { "keyboard_macro_start_recording" , "U", "Control" }, + { "keyboard_macro_finish_recording", "U", "Control", "Shift" }, + { "keyboard_macro_replay", "U", "Alt" }, + { "change_active_panel", "Comma", "Control" }, + { "change_active_panel_backwards", "Comma", "Control", "Shift" }, + { "project_go_to_root_directory", "H", "Control" }, + { "f4_toggle_compilation_expand", "Insert" }, + { "change_to_build_panel", "Period", "Alt" }, + { "close_build_panel", "Comma", "Alt" }, + { "f4_switch_syntax_option", "Tick", "Control" }, + + // NOTE(rjf): Files + { "interactive_new", "N", "Control" }, + { "f4_setup_new_project", "N", "Control", "Shift" }, + { "interactive_open_or_new", "O", "Control" }, + { "f4_interactive_open_or_new_in_project", "O", "Alt" }, + { "f4_open_project", "O", "Control", "Shift" }, + { "interactive_kill_buffer", "K", "Control" }, + { "interactive_switch_buffer", "I", "Control" }, + { "save_all_dirty_buffers", "S", "Control", "Shift" }, + { "f4_recent_files_menu", "V", "Alt" }, + + // NOTE(rjf): Jump Lists + { "goto_next_jump", "N", "Alt" }, + { "goto_prev_jump", "N", "Alt", "Shift" }, + { "goto_first_jump", "M", "Alt", "Shift" }, + + // NOTE(rjf): Commands + { "execute_any_cli", "Z", "Alt" }, + { "execute_previous_cli", "Z", "Alt", "Shift" }, + { "command_lister", "X", "Alt" }, + + // NOTE(rjf): Project + { "build_in_build_panel", "M", "Alt" }, + { "project_command_lister", "X", "Alt", "Shift" }, + { "project_fkey_command", "F1", "Control" }, + { "project_fkey_command", "F2", "Control" }, + { "project_fkey_command", "F3", "Control" }, + { "project_fkey_command", "F4", "Control" }, + { "project_fkey_command", "F5", "Control" }, + { "project_fkey_command", "F6", "Control" }, + { "project_fkey_command", "F7", "Control" }, + { "project_fkey_command", "F8", "Control" }, + { "project_fkey_command", "F9", "Control" }, + { "project_fkey_command", "F10", "Control" }, + { "project_fkey_command", "F11", "Control" }, + { "project_fkey_command", "F12", "Control" }, + { "project_fkey_command", "F13", "Control" }, + { "project_fkey_command", "F14", "Control" }, + { "project_fkey_command", "F15", "Control" }, + { "project_fkey_command", "F16", "Control" }, + + // NOTE(rjf): Panels + { "open_panel_vsplit", "P", "Control" }, + { "open_panel_hsplit", "Minus", "Control" }, + { "close_panel", "P", "Control", "Shift" }, + { "f4_search_for_definition__project_wide", "J", "Control" }, + { "f4_search_for_definition__current_file", "J", "Control", "Shift" }, + { "jump_to_last_point", "J", "Alt" }, + +}; + +keys_file = +{ + // NOTE(rjf): None => Characters + // Ctrl => Tokens + // Alt => Alphanumeric/Camel + + // NOTE(rjf): Backspace/Delete + { "delete_char", "Delete" }, + { "f4_delete_token_boundary", "Delete", "Control" }, + { "f4_delete_alpha_numeric_or_camel_boundary", "Delete", "Alt" }, + { "backspace_char", "Backspace" }, + { "f4_backspace_token_boundary", "Backspace", "Control" }, + { "f4_backspace_alpha_numeric_or_camel_boundary", "Backspace", "Alt" }, + + // NOTE(rjf): Left/Right Arrow Keys + { "f4_move_left", "Left" }, + { "f4_move_left_token_boundary", "Left", "Control" }, + { "move_left_alpha_numeric_or_camel_boundary", "Left", "Alt" }, + { "f4_move_right", "Right" }, + { "f4_move_right_token_boundary", "Right", "Control" }, + { "move_right_alpha_numeric_or_camel_boundary", "Right", "Alt" }, + + // NOTE(rjf): Up/Down Arrow Keys + { "move_up", "Up" }, + { "move_down", "Down" }, + { "move_up_to_blank_line_end", "Up", "Control" }, + { "move_down_to_blank_line_end", "Down", "Control" }, + { "move_line_up", "Up", "Alt" }, + { "move_line_down", "Down", "Alt" }, + { "f4_move_to_prev_divider_comment", "Up", "Control", "Shift" }, + { "f4_move_to_next_divider_comment", "Down", "Control", "Shift" }, + { "f4_move_up_token_occurrence", "Up", "Control", "Alt" }, + { "f4_move_down_token_occurrence", "Down", "Control", "Alt" }, + + // NOTE(rjf): Alternative Movement Keys + { "seek_end_of_line", "End" }, + { "f4_home_first_non_whitespace", "Home" }, + { "page_up", "PageUp" }, + { "page_down", "PageDown" }, + { "goto_beginning_of_file", "PageUp", "Control" }, + { "goto_end_of_file", "PageDown", "Control" }, + + // NOTE(rjf): Mark + { "set_mark", "Space", "Control" }, + { "cursor_mark_swap", "M", "Control" }, + + // NOTE(rjf): Search + { "f4_search", "F", "Control" }, + { "f4_reverse_search", "R", "Control" }, + { "list_all_locations", "F", "Control", "Shift" }, + { "list_all_substring_locations_case_insensitive", "F", "Alt" }, + { "goto_line", "G", "Control" }, + { "list_all_locations_of_selection", "G", "Control", "Shift" }, + { "search_identifier", "T", "Control" }, + { "list_all_locations_of_identifier", "T", "Control", "Shift" }, + + // NOTE(rjf): Replace + { "replace_in_range", "A", "Control" }, + { "query_replace", "Q", "Control" }, + { "query_replace_identifier", "Q", "Control", "Shift" }, + { "query_replace_selection", "Q", "Alt" }, + + // NOTE(rjf): Clipboard + { "copy", "C", "Control" }, + { "paste_and_indent", "V", "Control" }, + { "paste_next_and_indent", "V", "Control", "Shift" }, + { "cut", "X", "Control" }, + + // NOTE(rjf): Lego + { "f4_lego_buffer_place", "F1", }, + { "f4_lego_buffer_place", "F2", }, + { "f4_lego_buffer_place", "F3", }, + { "f4_lego_buffer_place", "F4", }, + + // NOTE(rjf): Deletion + { "delete_range", "D", "Control" }, + { "delete_line", "D", "Control", "Shift" }, + + // NOTE(rjf): Insertion + { "duplicate_line", "L", "Control" }, + + // NOTE(rjf): View + { "center_view", "E", "Control" }, + { "left_adjust_view", "E", "Control", "Shift" }, + + // NOTE(rjf): File + { "kill_buffer", "K", "Control", "Shift" }, + { "reopen", "O", "Alt", "Shift" }, + { "save", "S", "Control" }, + { "save_all_dirty_buffers", "S", "Control", "Shift" }, + { "redo", "Y", "Control" }, + { "undo", "Z", "Control" }, + { "view_buffer_other_panel", "1", "Control" }, + + // NOTE(rjf): Meta + { "swap_panels", "2", "Control" }, + { "if_read_only_goto_position", "Return" }, + { "if_read_only_goto_position_same_panel", "Return", "Shift" }, + { "view_jump_list_with_lister", "Period", "Control", "Shift" }, + + // NOTE(rjf): Code Peek + { "f4_code_peek", "Tick", "Alt" }, + { "f4_code_peek_yank", "Tab", "Control" }, + { "f4_code_peek_clear", "Shift", "Alt" }, + + // NOTE(rjf): Go To Def + { "f4_go_to_definition", "Return", "Control" }, + { "f4_go_to_definition_same_panel", "Return", "Control", "Shift" }, + + // NOTE(rjf): Helpers + { "f4_write_zero_struct", "0", "Control" }, + { "f4_toggle_enclosure_side", "Semicolon", "Alt" }, +}; + +keys_code = +{ + { "f4_comment_selection", "Semicolon", "Control" }, + { "f4_uncomment_selection", "Semicolon", "Control", "Shift", }, + { "f4_autocomplete_or_indent", "Tab" }, + { "f4_unindent", "Tab", "Shift" }, + { "word_complete_drop_down", "Tab", "Shift", "Control" }, + { "write_block", "R", "Alt" }, + { "write_todo", "T", "Alt" }, + { "write_note", "Y", "Alt" }, + { "list_all_locations_of_type_definition", "D", "Alt" }, + { "list_all_locations_of_type_definition_of_identifier", "T", "Alt", "Shift" }, + { "open_long_braces", "LeftBracket", "Control" }, + { "open_long_braces_semicolon", "LeftBracket", "Control", "Shift" }, + { "open_long_braces_break", "RightBracket", "Control", "Shift" }, + { "select_surrounding_scope", "LeftBracket", "Alt" }, + { "select_surrounding_scope_maximal", "LeftBracket", "Alt", "Shift" }, + { "select_prev_scope_absolute", "RightBracket", "Alt" }, + { "select_prev_top_most_scope", "RightBracket", "Alt", "Shift" }, + { "select_next_scope_absolute", "Quote", "Alt" }, + { "select_next_scope_after_current", "Quote", "Alt", "Shift" }, + { "place_in_scope", "ForwardSlash", "Alt" }, + { "delete_current_scope", "Minus", "Alt" }, + { "if0_off", "I", "Alt" }, + { "open_file_in_quotes", "1", "Alt" }, + { "open_matching_file_cpp", "2", "Alt" }, + + { "f4_lego_store_range", "F5", "Alt" }, + { "f4_lego_store_range", "F6", "Alt" }, + { "f4_lego_store_range", "F7", "Alt" }, + { "f4_lego_store_range", "F8", "Alt" }, + + { "f4_lego_store_token", "F5" }, + { "f4_lego_store_token", "F6" }, + { "f4_lego_store_token", "F7" }, + { "f4_lego_store_token", "F8" }, + +}; \ No newline at end of file diff --git a/dist_files/4coder_fleury/config.4coder b/dist_files/4coder_fleury/config.4coder new file mode 100644 index 0000000..be2ffb9 --- /dev/null +++ b/dist_files/4coder_fleury/config.4coder @@ -0,0 +1,99 @@ +// Command Mapping +// "" - Leave the bindings unaltered - use this when writing your own customization! +// "choose" - Ask 4coder to choose based on platform. +// "default" - Use the default keybindings 4coder has always had. +// "mac-default" - Use keybindings similar to those found in other Mac applications. +mapping = ""; + +// MODE +// "4coder" - The default 4coder mode that has been around since the beginning of time (2015) +// "notepad-like" - Single "thin" cursor and highlight ranges like in notepad, sublime, notepad++, etc +mode = "4coder"; +bind_by_physical_key = false; + +// UI +use_scroll_bars = false; +use_file_bars = true; +use_error_highlight = true; +use_jump_highlight = true; +use_scope_highlight = true; +use_paren_helper = true; +use_comment_keywords = true; +lister_whole_word_backspace_when_modified = false; +show_line_number_margins = false; +enable_output_wrapping = false; + +enable_undo_fade_out = false; + +// cursor_roundess is a value [0,50] setting the radius of +// the cursor and mark's roundness as a percentage of their width +// (At 50 the left and right corners will be so round they form a semi-circle, +// hence 50 is the max) +cursor_roundness = 10; + +// mark_thickness is a pixel count value setting the +// thickness of the mark wire box in original mode +mark_thickness = 2; + +// lister_roundess is a value [0,50] setting the radius of +// the lister items' roundness as a percentage of their height +lister_roundness = 20; + +// Code Wrapping +treat_as_code = ".cpp.c.hpp.h.cc.cs.java.rs.glsl.m.mm.ds.md.4coder.jai.vert.frag"; +enable_virtual_whitespace = true; +virtual_whitespace_regular_indent = 1; +enable_code_wrapping = true; + +// This only applies to code files in code-wrapping mode. +// Plain text and code files without virtual-whitespace will not be effected. +automatically_indent_text_on_save = true; + +// When set to true, all unsaved changes will be saved on a build. +automatically_save_changes_on_build = true; + +// Load project on startup +automatically_load_project = true; + +// Indentation +indent_with_tabs = false; +indent_width = 4; +default_tab_width = 4; + +// Theme +default_theme_name = "theme-fleury"; +highlight_line_at_cursor = true; + +// Font +default_font_name = "liberation-mono.ttf"; +default_font_size = 14; +default_font_hinting = true; + +// aa modes: +// 8bit - mono-chrome 0-255 opacity channel per pixel +// 1bit - mono-chrome 0/1 opacity channel per pixel +default_font_aa_mode = "8bit"; + +// User +user_name = "rjf"; + +// Keyboard AltGr setting +lalt_lctrl_is_altgr = false; + +// Project setup configuration +default_compiler_bat = "cl"; +default_flags_bat = "-FC -GR- -EHa- -nologo -Zi"; +default_compiler_sh = "g++"; +default_flags_sh = "-g"; + +// NOTE(rjf): Fleury 4coder Disabling Options +// f4_disable_brace_highlight = true; +// f4_disable_close_brace_annotation = true; +// f4_disable_brace_lines = true; +// f4_disable_progress_bar = true; +// f4_disable_divider_comments = true; +// f4_disable_error_annotations = true; +// f4_disable_calc_comments = true; +// f4_poscontext_draw_at_bottom_of_buffer = true; +// f4_disable_poscontext = true; +// f4_disable_cursor_token_occurance = true; diff --git a/dist_files/4coder_fleury/custom_4coder.dll b/dist_files/4coder_fleury/custom_4coder.dll new file mode 100644 index 0000000..301a4cc Binary files /dev/null and b/dist_files/4coder_fleury/custom_4coder.dll differ diff --git a/dist_files/4coder_fleury/custom_4coder.pdb b/dist_files/4coder_fleury/custom_4coder.pdb new file mode 100644 index 0000000..5911dd4 Binary files /dev/null and b/dist_files/4coder_fleury/custom_4coder.pdb differ diff --git a/dist_files/4coder_fleury/custom_4coder.so b/dist_files/4coder_fleury/custom_4coder.so new file mode 100644 index 0000000..60c5c2f Binary files /dev/null and b/dist_files/4coder_fleury/custom_4coder.so differ diff --git a/dist_files/4coder_fleury/theme-fleury.4coder b/dist_files/4coder_fleury/theme-fleury.4coder new file mode 100644 index 0000000..5ce6409 --- /dev/null +++ b/dist_files/4coder_fleury/theme-fleury.4coder @@ -0,0 +1,64 @@ +defcolor_bar = 0xFF000000; +defcolor_base = 0xFFfcaa05; +defcolor_pop1 = 0xffde8150; +defcolor_pop2 = 0xFFFF0000; +defcolor_back = 0xFF020202; +defcolor_margin = 0xFF222425; +defcolor_margin_hover = 0xff63523d; +defcolor_margin_active = 0xff63523d; +defcolor_list_item = { 0xFF222425, defcolor_back}; +defcolor_list_item_hover = { 0xff362e25, defcolor_margin}; +defcolor_list_item_active = { 0xff63523d, defcolor_margin}; +defcolor_cursor = { 0xFF00EE00, 0xffe0741b, 0xff1be094, 0xffba60c4 }; +defcolor_at_cursor = 0xFF0C0C0C; +defcolor_highlight_cursor_line = 0xFF1E1E1E; +defcolor_highlight = 0xFF303040; +defcolor_at_highlight = 0xFFFF44DD; +defcolor_mark = 0xFF494949; +defcolor_text_default = 0xffb99468; +defcolor_comment = 0xff666666; +defcolor_comment_pop = { 0xff2ab34f, 0xFFdb2828 }; +defcolor_keyword = 0xfff0c674; +defcolor_str_constant = 0xffffa900; +defcolor_char_constant = 0xffffa900; +defcolor_int_constant = 0xffffa900; +defcolor_float_constant = 0xffffa900; +defcolor_bool_constant = 0xffffa900; +defcolor_preproc = 0xFFdc7575; +defcolor_include = 0xffffa900; +defcolor_special_character = 0xFFFF0000; +defcolor_ghost_character = 0xFF4E5E46; +defcolor_highlight_junk = 0xFF3A0000; +defcolor_highlight_white = 0xFF003A3A; +defcolor_paste = 0xFFDDEE00; +defcolor_undo = 0xFF00DDEE; +defcolor_back_cycle = { 0xFF020202, 0xFF020202, 0xFF020202, 0xFF020202, 0xFF020202, 0xFF020202, 0xFF020202, 0xFF100202, 0xFF300202, 0xFF500202, 0xFF700202}; +defcolor_text_cycle = { 0xFFA00000, 0xFF00A000, 0xFF0030B0, 0xFFA0A000 }; +defcolor_line_numbers_back = 0xFF101010; +defcolor_line_numbers_text = 0xFF404040; + +fleury_color_syntax_crap = 0xff5c4d3c; +fleury_color_operators = 0xFFbd2d2d; +fleury_color_inactive_pane_overlay = 0x44000000; +fleury_color_inactive_pane_background = 0xff000000; +fleury_color_file_progress_bar = 0x60634323; +fleury_color_brace_highlight = { 0xff8ffff2 }; +fleury_color_brace_line = { 0x809ba290 }; +fleury_color_brace_annotation = { 0x809ba290 }; +fleury_color_index_product_type = 0xFFedb211; +fleury_color_index_sum_type = 0xFFa7eb13; +fleury_color_index_function = 0xFFde451f; +fleury_color_index_macro = 0xFF2895c7; +fleury_color_index_constant = 0xff6eb535; +fleury_color_index_comment_tag = 0xffffae00; +fleury_color_index_decl = 0xffc9598a; +fleury_color_cursor_macro = 0xffde2368; +fleury_color_cursor_power_mode = 0xffefaf2f; +fleury_color_cursor_inactive = 0xFF880000; +fleury_color_plot_cycle = { 0xff03d3fc, 0xff22b80b, 0xfff0bb0c, 0xfff0500c }; +fleury_color_token_highlight = 0x88f2d357; +fleury_color_token_minor_highlight = 0x44d19045; +fleury_color_error_annotation = 0xffff0000; +fleury_color_lego_grab = 0xffefaf6f; +fleury_color_lego_splat = 0xffefaaef; +fleury_color_comment_user_name = 0xffffdd23; diff --git a/dist_files/audio_test/chtulthu.wav b/dist_files/audio_test/chtulthu.wav new file mode 100644 index 0000000..5eee56e Binary files /dev/null and b/dist_files/audio_test/chtulthu.wav differ diff --git a/dist_files/audio_test/hit.wav b/dist_files/audio_test/hit.wav new file mode 100644 index 0000000..370d270 Binary files /dev/null and b/dist_files/audio_test/hit.wav differ diff --git a/dist_files/audio_test/raygun_zap.wav b/dist_files/audio_test/raygun_zap.wav new file mode 100644 index 0000000..38a959c Binary files /dev/null and b/dist_files/audio_test/raygun_zap.wav differ diff --git a/dist_files/fonts/Inconsolata-Regular.ttf b/dist_files/fonts/Inconsolata-Regular.ttf new file mode 100644 index 0000000..a87ffba Binary files /dev/null and b/dist_files/fonts/Inconsolata-Regular.ttf differ diff --git a/dist_files/fonts/liberation-mono.ttf b/dist_files/fonts/liberation-mono.ttf new file mode 100644 index 0000000..61af64e Binary files /dev/null and b/dist_files/fonts/liberation-mono.ttf differ diff --git a/foreign/freetype2/freetype/config/ftconfig.h b/foreign/freetype2/freetype/config/ftconfig.h new file mode 100644 index 0000000..157a704 --- /dev/null +++ b/foreign/freetype2/freetype/config/ftconfig.h @@ -0,0 +1,473 @@ +/***************************************************************************/ +/* */ +/* ftconfig.h */ +/* */ +/* ANSI-specific configuration file (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This header file contains a number of macro definitions that are used */ + /* by the rest of the engine. Most of the macros here are automatically */ + /* determined at compile time, and you should not need to change it to */ + /* port FreeType, except to compile the library with a non-ANSI */ + /* compiler. */ + /* */ + /* Note however that if some specific modifications are needed, we */ + /* advise you to place a modified copy in your build directory. */ + /* */ + /* The build directory is usually `builds/', and contains */ + /* system-specific files that are always included first when building */ + /* the library. */ + /* */ + /* This ANSI version should stay in `include/config/'. */ + /* */ + /*************************************************************************/ + +#ifndef FTCONFIG_H_ +#define FTCONFIG_H_ + +#include +#include FT_CONFIG_OPTIONS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* PLATFORM-SPECIFIC CONFIGURATION MACROS */ + /* */ + /* These macros can be toggled to suit a specific system. The current */ + /* ones are defaults used to compile FreeType in an ANSI C environment */ + /* (16bit compilers are also supported). Copy this file to your own */ + /* `builds/' directory, and edit it to port the engine. */ + /* */ + /*************************************************************************/ + + + /* There are systems (like the Texas Instruments 'C54x) where a `char' */ + /* has 16 bits. ANSI C says that sizeof(char) is always 1. Since an */ + /* `int' has 16 bits also for this system, sizeof(int) gives 1 which */ + /* is probably unexpected. */ + /* */ + /* `CHAR_BIT' (defined in limits.h) gives the number of bits in a */ + /* `char' type. */ + +#ifndef FT_CHAR_BIT +#define FT_CHAR_BIT CHAR_BIT +#endif + + + /* The size of an `int' type. */ +#if FT_UINT_MAX == 0xFFFFUL +#define FT_SIZEOF_INT (16 / FT_CHAR_BIT) +#elif FT_UINT_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_INT (32 / FT_CHAR_BIT) +#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_INT (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `int' type!" +#endif + + /* The size of a `long' type. A five-byte `long' (as used e.g. on the */ + /* DM642) is recognized but avoided. */ +#if FT_ULONG_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_LONG (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `long' type!" +#endif + + + /* FT_UNUSED is a macro used to indicate that a given parameter is not */ + /* used -- this is only used to get rid of unpleasant compiler warnings */ +#ifndef FT_UNUSED +#define FT_UNUSED( arg ) ( (arg) = (arg) ) +#endif + + + /*************************************************************************/ + /* */ + /* AUTOMATIC CONFIGURATION MACROS */ + /* */ + /* These macros are computed from the ones defined above. Don't touch */ + /* their definition, unless you know precisely what you are doing. No */ + /* porter should need to mess with them. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Mac support */ + /* */ + /* This is the only necessary change, so it is defined here instead */ + /* providing a new configuration file. */ + /* */ +#if defined( __APPLE__ ) || ( defined( __MWERKS__ ) && defined( macintosh ) ) + /* no Carbon frameworks for 64bit 10.4.x */ + /* AvailabilityMacros.h is available since Mac OS X 10.2, */ + /* so guess the system version by maximum errno before inclusion */ +#include +#ifdef ECANCELED /* defined since 10.2 */ +#include "AvailabilityMacros.h" +#endif +#if defined( __LP64__ ) && \ + ( MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 ) +#undef FT_MACINTOSH +#endif + +#elif defined( __SC__ ) || defined( __MRC__ ) + /* Classic MacOS compilers */ +#include "ConditionalMacros.h" +#if TARGET_OS_MAC +#define FT_MACINTOSH 1 +#endif + +#endif + + + /*************************************************************************/ + /* */ + /*
*/ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int16 */ + /* */ + /* */ + /* A typedef for a 16bit signed integer type. */ + /* */ + typedef signed short FT_Int16; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt16 */ + /* */ + /* */ + /* A typedef for a 16bit unsigned integer type. */ + /* */ + typedef unsigned short FT_UInt16; + + /* */ + + + /* this #if 0 ... #endif clause is for documentation purposes */ +#if 0 + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int32 */ + /* */ + /* */ + /* A typedef for a 32bit signed integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef signed XXX FT_Int32; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt32 */ + /* */ + /* A typedef for a 32bit unsigned integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef unsigned XXX FT_UInt32; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_Int64 */ + /* */ + /* A typedef for a 64bit signed integer type. The size depends on */ + /* the configuration. Only defined if there is real 64bit support; */ + /* otherwise, it gets emulated with a structure (if necessary). */ + /* */ + typedef signed XXX FT_Int64; + + + /*************************************************************************/ + /* */ + /* */ + /* FT_UInt64 */ + /* */ + /* A typedef for a 64bit unsigned integer type. The size depends on */ + /* the configuration. Only defined if there is real 64bit support; */ + /* otherwise, it gets emulated with a structure (if necessary). */ + /* */ + typedef unsigned XXX FT_UInt64; + + /* */ + +#endif + +#if FT_SIZEOF_INT == (32 / FT_CHAR_BIT) + + typedef signed int FT_Int32; + typedef unsigned int FT_UInt32; + +#elif FT_SIZEOF_LONG == (32 / FT_CHAR_BIT) + + typedef signed long FT_Int32; + typedef unsigned long FT_UInt32; + +#else +#error "no 32bit type found -- please check your configuration files" +#endif + + + /* look up an integer type that is at least 32 bits */ +#if FT_SIZEOF_INT >= (32 / FT_CHAR_BIT) + + typedef int FT_Fast; + typedef unsigned int FT_UFast; + +#elif FT_SIZEOF_LONG >= (32 / FT_CHAR_BIT) + + typedef long FT_Fast; + typedef unsigned long FT_UFast; + +#endif + + + /* determine whether we have a 64-bit int type for platforms without */ + /* Autoconf */ +#if FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) + + /* FT_LONG64 must be defined if a 64-bit type is available */ +#define FT_LONG64 +#define FT_INT64 long +#define FT_UINT64 unsigned long + + /*************************************************************************/ + /* */ + /* A 64-bit data type may create compilation problems if you compile */ + /* in strict ANSI mode. To avoid them, we disable other 64-bit data */ + /* types if __STDC__ is defined. You can however ignore this rule */ + /* by defining the FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */ + /* */ +#elif !defined( __STDC__ ) || defined( FT_CONFIG_OPTION_FORCE_INT64 ) + +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L + +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#elif defined( _MSC_VER ) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 +#define FT_UINT64 unsigned __int64 + +#elif defined( __BORLANDC__ ) /* Borland C++ */ + + /* XXXX: We should probably check the value of __BORLANDC__ in order */ + /* to test the compiler version. */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 +#define FT_UINT64 unsigned __int64 + +#elif defined( __WATCOMC__ ) /* Watcom C++ */ + + /* Watcom doesn't provide 64-bit data types */ + +#elif defined( __MWERKS__ ) /* Metrowerks CodeWarrior */ + +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#elif defined( __GNUC__ ) + + /* GCC provides the `long long' type */ +#define FT_LONG64 +#define FT_INT64 long long int +#define FT_UINT64 unsigned long long int + +#endif /* __STDC_VERSION__ >= 199901L */ + +#endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */ + +#ifdef FT_LONG64 + typedef FT_INT64 FT_Int64; + typedef FT_UINT64 FT_UInt64; +#endif + + + /*************************************************************************/ + /* */ + /* miscellaneous */ + /* */ + /*************************************************************************/ + + +#define FT_BEGIN_STMNT do { +#define FT_END_STMNT } while ( 0 ) +#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT + + + /* typeof condition taken from gnulib's `intprops.h' header file */ +#if ( __GNUC__ >= 2 || \ + defined( __IBM__TYPEOF__ ) || \ + ( __SUNPRO_C >= 0x5110 && !__STDC__ ) ) +#define FT_TYPEOF( type ) (__typeof__ (type)) +#else +#define FT_TYPEOF( type ) /* empty */ +#endif + + +#ifdef FT_MAKE_OPTION_SINGLE_OBJECT + +#define FT_LOCAL( x ) static x +#define FT_LOCAL_DEF( x ) static x + +#else + +#ifdef __cplusplus +#define FT_LOCAL( x ) extern "C" x +#define FT_LOCAL_DEF( x ) extern "C" x +#else +#define FT_LOCAL( x ) extern x +#define FT_LOCAL_DEF( x ) x +#endif + +#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */ + +#define FT_LOCAL_ARRAY( x ) extern const x +#define FT_LOCAL_ARRAY_DEF( x ) const x + + +#ifndef FT_BASE + +#ifdef __cplusplus +#define FT_BASE( x ) extern "C" x +#else +#define FT_BASE( x ) extern x +#endif + +#endif /* !FT_BASE */ + + +#ifndef FT_BASE_DEF + +#ifdef __cplusplus +#define FT_BASE_DEF( x ) x +#else +#define FT_BASE_DEF( x ) x +#endif + +#endif /* !FT_BASE_DEF */ + + +#ifndef FT_EXPORT + +#ifdef __cplusplus +#define FT_EXPORT( x ) extern "C" x +#else +#define FT_EXPORT( x ) extern x +#endif + +#endif /* !FT_EXPORT */ + + +#ifndef FT_EXPORT_DEF + +#ifdef __cplusplus +#define FT_EXPORT_DEF( x ) extern "C" x +#else +#define FT_EXPORT_DEF( x ) extern x +#endif + +#endif /* !FT_EXPORT_DEF */ + + +#ifndef FT_EXPORT_VAR + +#ifdef __cplusplus +#define FT_EXPORT_VAR( x ) extern "C" x +#else +#define FT_EXPORT_VAR( x ) extern x +#endif + +#endif /* !FT_EXPORT_VAR */ + + /* The following macros are needed to compile the library with a */ + /* C++ compiler and with 16bit compilers. */ + /* */ + + /* This is special. Within C++, you must specify `extern "C"' for */ + /* functions which are used via function pointers, and you also */ + /* must do that for structures which contain function pointers to */ + /* assure C linkage -- it's not possible to have (local) anonymous */ + /* functions which are accessed by (global) function pointers. */ + /* */ + /* */ + /* FT_CALLBACK_DEF is used to _define_ a callback function. */ + /* */ + /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */ + /* contains pointers to callback functions. */ + /* */ + /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */ + /* that contains pointers to callback functions. */ + /* */ + /* */ + /* Some 16bit compilers have to redefine these macros to insert */ + /* the infamous `_cdecl' or `__fastcall' declarations. */ + /* */ +#ifndef FT_CALLBACK_DEF +#ifdef __cplusplus +#define FT_CALLBACK_DEF( x ) extern "C" x +#else +#define FT_CALLBACK_DEF( x ) static x +#endif +#endif /* FT_CALLBACK_DEF */ + +#ifndef FT_CALLBACK_TABLE +#ifdef __cplusplus +#define FT_CALLBACK_TABLE extern "C" +#define FT_CALLBACK_TABLE_DEF extern "C" +#else +#define FT_CALLBACK_TABLE extern +#define FT_CALLBACK_TABLE_DEF /* nothing */ +#endif +#endif /* FT_CALLBACK_TABLE */ + + +FT_END_HEADER + + +#endif /* FTCONFIG_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/config/ftheader.h b/foreign/freetype2/freetype/config/ftheader.h new file mode 100644 index 0000000..68e1483 --- /dev/null +++ b/foreign/freetype2/freetype/config/ftheader.h @@ -0,0 +1,833 @@ +/***************************************************************************/ +/* */ +/* ftheader.h */ +/* */ +/* Build macros of the FreeType 2 library. */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef FTHEADER_H_ +#define FTHEADER_H_ + + + /*@***********************************************************************/ + /* */ + /* */ + /* FT_BEGIN_HEADER */ + /* */ + /* */ + /* This macro is used in association with @FT_END_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_BEGIN_HEADER extern "C" { +#else +#define FT_BEGIN_HEADER /* nothing */ +#endif + + + /*@***********************************************************************/ + /* */ + /* */ + /* FT_END_HEADER */ + /* */ + /* */ + /* This macro is used in association with @FT_BEGIN_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_END_HEADER } +#else +#define FT_END_HEADER /* nothing */ +#endif + + + /*************************************************************************/ + /* */ + /* Aliases for the FreeType 2 public and configuration files. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /*
*/ + /* header_file_macros */ + /* */ + /* */ + /* Header File Macros */ + /* */ + /* <Abstract> */ + /* Macro definitions used to #include specific header files. */ + /* */ + /* <Description> */ + /* The following macros are defined to the name of specific */ + /* FreeType~2 header files. They can be used directly in #include */ + /* statements as in: */ + /* */ + /* { */ + /* #include FT_FREETYPE_H */ + /* #include FT_MULTIPLE_MASTERS_H */ + /* #include FT_GLYPH_H */ + /* } */ + /* */ + /* There are several reasons why we are now using macros to name */ + /* public header files. The first one is that such macros are not */ + /* limited to the infamous 8.3~naming rule required by DOS (and */ + /* `FT_MULTIPLE_MASTERS_H' is a lot more meaningful than `ftmm.h'). */ + /* */ + /* The second reason is that it allows for more flexibility in the */ + /* way FreeType~2 is installed on a given system. */ + /* */ + /*************************************************************************/ + + + /* configuration files */ + + /************************************************************************* + * + * @macro: + * FT_CONFIG_CONFIG_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 configuration data. + * + */ +#ifndef FT_CONFIG_CONFIG_H +#define FT_CONFIG_CONFIG_H <freetype/config/ftconfig.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_STANDARD_LIBRARY_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 interface to the standard C library functions. + * + */ +#ifndef FT_CONFIG_STANDARD_LIBRARY_H +#define FT_CONFIG_STANDARD_LIBRARY_H <freetype/config/ftstdlib.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_OPTIONS_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 project-specific configuration options. + * + */ +#ifndef FT_CONFIG_OPTIONS_H +#define FT_CONFIG_OPTIONS_H <freetype/config/ftoption.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_MODULES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 modules that are statically linked to new library + * instances in @FT_Init_FreeType. + * + */ +#ifndef FT_CONFIG_MODULES_H +#define FT_CONFIG_MODULES_H <freetype/config/ftmodule.h> +#endif + + /* */ + + /* public headers */ + + /************************************************************************* + * + * @macro: + * FT_FREETYPE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * base FreeType~2 API. + * + */ +#define FT_FREETYPE_H <freetype/freetype.h> + + + /************************************************************************* + * + * @macro: + * FT_ERRORS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 error codes (and messages). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_ERRORS_H <freetype/fterrors.h> + + + /************************************************************************* + * + * @macro: + * FT_MODULE_ERRORS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 module error offsets (and messages). + * + */ +#define FT_MODULE_ERRORS_H <freetype/ftmoderr.h> + + + /************************************************************************* + * + * @macro: + * FT_SYSTEM_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 interface to low-level operations (i.e., memory management + * and stream i/o). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_SYSTEM_H <freetype/ftsystem.h> + + + /************************************************************************* + * + * @macro: + * FT_IMAGE_H + * + * @description: + * A macro used in #include statements to name the file containing type + * definitions related to glyph images (i.e., bitmaps, outlines, + * scan-converter parameters). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_IMAGE_H <freetype/ftimage.h> + + + /************************************************************************* + * + * @macro: + * FT_TYPES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * basic data types defined by FreeType~2. + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_TYPES_H <freetype/fttypes.h> + + + /************************************************************************* + * + * @macro: + * FT_LIST_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list management API of FreeType~2. + * + * (Most applications will never need to include this file.) + * + */ +#define FT_LIST_H <freetype/ftlist.h> + + + /************************************************************************* + * + * @macro: + * FT_OUTLINE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * scalable outline management API of FreeType~2. + * + */ +#define FT_OUTLINE_H <freetype/ftoutln.h> + + + /************************************************************************* + * + * @macro: + * FT_SIZES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API which manages multiple @FT_Size objects per face. + * + */ +#define FT_SIZES_H <freetype/ftsizes.h> + + + /************************************************************************* + * + * @macro: + * FT_MODULE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * module management API of FreeType~2. + * + */ +#define FT_MODULE_H <freetype/ftmodapi.h> + + + /************************************************************************* + * + * @macro: + * FT_RENDER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * renderer module management API of FreeType~2. + * + */ +#define FT_RENDER_H <freetype/ftrender.h> + + + /************************************************************************* + * + * @macro: + * FT_AUTOHINTER_H + * + * @description: + * A macro used in #include statements to name the file containing + * structures and macros related to the auto-hinting module. + * + */ +#define FT_AUTOHINTER_H <freetype/ftautoh.h> + + + /************************************************************************* + * + * @macro: + * FT_CFF_DRIVER_H + * + * @description: + * A macro used in #include statements to name the file containing + * structures and macros related to the CFF driver module. + * + */ +#define FT_CFF_DRIVER_H <freetype/ftcffdrv.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_DRIVER_H + * + * @description: + * A macro used in #include statements to name the file containing + * structures and macros related to the TrueType driver module. + * + */ +#define FT_TRUETYPE_DRIVER_H <freetype/ftttdrv.h> + + + /************************************************************************* + * + * @macro: + * FT_TYPE1_TABLES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * types and API specific to the Type~1 format. + * + */ +#define FT_TYPE1_TABLES_H <freetype/t1tables.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_IDS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * enumeration values which identify name strings, languages, encodings, + * etc. This file really contains a _large_ set of constant macro + * definitions, taken from the TrueType and OpenType specifications. + * + */ +#define FT_TRUETYPE_IDS_H <freetype/ttnameid.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_TABLES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * types and API specific to the TrueType (as well as OpenType) format. + * + */ +#define FT_TRUETYPE_TABLES_H <freetype/tttables.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_TAGS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of TrueType four-byte `tags' which identify blocks in + * SFNT-based font formats (i.e., TrueType and OpenType). + * + */ +#define FT_TRUETYPE_TAGS_H <freetype/tttags.h> + + + /************************************************************************* + * + * @macro: + * FT_BDF_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which accesses BDF-specific strings from a + * face. + * + */ +#define FT_BDF_H <freetype/ftbdf.h> + + + /************************************************************************* + * + * @macro: + * FT_CID_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which access CID font information from a + * face. + * + */ +#define FT_CID_H <freetype/ftcid.h> + + + /************************************************************************* + * + * @macro: + * FT_GZIP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports gzip-compressed files. + * + */ +#define FT_GZIP_H <freetype/ftgzip.h> + + + /************************************************************************* + * + * @macro: + * FT_LZW_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports LZW-compressed files. + * + */ +#define FT_LZW_H <freetype/ftlzw.h> + + + /************************************************************************* + * + * @macro: + * FT_BZIP2_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports bzip2-compressed files. + * + */ +#define FT_BZIP2_H <freetype/ftbzip2.h> + + + /************************************************************************* + * + * @macro: + * FT_WINFONTS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports Windows FNT files. + * + */ +#define FT_WINFONTS_H <freetype/ftwinfnt.h> + + + /************************************************************************* + * + * @macro: + * FT_GLYPH_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional glyph management component. + * + */ +#define FT_GLYPH_H <freetype/ftglyph.h> + + + /************************************************************************* + * + * @macro: + * FT_BITMAP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional bitmap conversion component. + * + */ +#define FT_BITMAP_H <freetype/ftbitmap.h> + + + /************************************************************************* + * + * @macro: + * FT_BBOX_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional exact bounding box computation routines. + * + */ +#define FT_BBOX_H <freetype/ftbbox.h> + + + /************************************************************************* + * + * @macro: + * FT_CACHE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional FreeType~2 cache sub-system. + * + */ +#define FT_CACHE_H <freetype/ftcache.h> + + + /************************************************************************* + * + * @macro: + * FT_CACHE_IMAGE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `glyph image' API of the FreeType~2 cache sub-system. + * + * It is used to define a cache for @FT_Glyph elements. You can also + * use the API defined in @FT_CACHE_SMALL_BITMAPS_H if you only need to + * store small glyph bitmaps, as it will use less memory. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * glyph image-related cache declarations. + * + */ +#define FT_CACHE_IMAGE_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_CACHE_SMALL_BITMAPS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `small bitmaps' API of the FreeType~2 cache sub-system. + * + * It is used to define a cache for small glyph bitmaps in a relatively + * memory-efficient way. You can also use the API defined in + * @FT_CACHE_IMAGE_H if you want to cache arbitrary glyph images, + * including scalable outlines. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * small bitmaps-related cache declarations. + * + */ +#define FT_CACHE_SMALL_BITMAPS_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_CACHE_CHARMAP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `charmap' API of the FreeType~2 cache sub-system. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * charmap-based cache declarations. + * + */ +#define FT_CACHE_CHARMAP_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_MAC_H + * + * @description: + * A macro used in #include statements to name the file containing the + * Macintosh-specific FreeType~2 API. The latter is used to access + * fonts embedded in resource forks. + * + * This header file must be explicitly included by client applications + * compiled on the Mac (note that the base API still works though). + * + */ +#define FT_MAC_H <freetype/ftmac.h> + + + /************************************************************************* + * + * @macro: + * FT_MULTIPLE_MASTERS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional multiple-masters management API of FreeType~2. + * + */ +#define FT_MULTIPLE_MASTERS_H <freetype/ftmm.h> + + + /************************************************************************* + * + * @macro: + * FT_SFNT_NAMES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which accesses embedded `name' strings in + * SFNT-based font formats (i.e., TrueType and OpenType). + * + */ +#define FT_SFNT_NAMES_H <freetype/ftsnames.h> + + + /************************************************************************* + * + * @macro: + * FT_OPENTYPE_VALIDATE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which validates OpenType tables (BASE, GDEF, + * GPOS, GSUB, JSTF). + * + */ +#define FT_OPENTYPE_VALIDATE_H <freetype/ftotval.h> + + + /************************************************************************* + * + * @macro: + * FT_GX_VALIDATE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which validates TrueTypeGX/AAT tables (feat, + * mort, morx, bsln, just, kern, opbd, trak, prop). + * + */ +#define FT_GX_VALIDATE_H <freetype/ftgxval.h> + + + /************************************************************************* + * + * @macro: + * FT_PFR_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which accesses PFR-specific data. + * + */ +#define FT_PFR_H <freetype/ftpfr.h> + + + /************************************************************************* + * + * @macro: + * FT_STROKER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which provides functions to stroke outline paths. + */ +#define FT_STROKER_H <freetype/ftstroke.h> + + + /************************************************************************* + * + * @macro: + * FT_SYNTHESIS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs artificial obliquing and emboldening. + */ +#define FT_SYNTHESIS_H <freetype/ftsynth.h> + + + /************************************************************************* + * + * @macro: + * FT_FONT_FORMATS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which provides functions specific to font formats. + */ +#define FT_FONT_FORMATS_H <freetype/ftfntfmt.h> + + /* deprecated */ +#define FT_XFREE86_H FT_FONT_FORMATS_H + + + /************************************************************************* + * + * @macro: + * FT_TRIGONOMETRY_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs trigonometric computations (e.g., + * cosines and arc tangents). + */ +#define FT_TRIGONOMETRY_H <freetype/fttrigon.h> + + + /************************************************************************* + * + * @macro: + * FT_LCD_FILTER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs color filtering for subpixel rendering. + */ +#define FT_LCD_FILTER_H <freetype/ftlcdfil.h> + + + /************************************************************************* + * + * @macro: + * FT_UNPATENTED_HINTING_H + * + * @description: + * Deprecated. + */ +#define FT_UNPATENTED_HINTING_H <freetype/ttunpat.h> + + + /************************************************************************* + * + * @macro: + * FT_INCREMENTAL_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs incremental glyph loading. + */ +#define FT_INCREMENTAL_H <freetype/ftincrem.h> + + + /************************************************************************* + * + * @macro: + * FT_GASP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which returns entries from the TrueType GASP table. + */ +#define FT_GASP_H <freetype/ftgasp.h> + + + /************************************************************************* + * + * @macro: + * FT_ADVANCES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which returns individual and ranged glyph advances. + */ +#define FT_ADVANCES_H <freetype/ftadvanc.h> + + + /* */ + +#define FT_ERROR_DEFINITIONS_H <freetype/fterrdef.h> + + + /* The internals of the cache sub-system are no longer exposed. We */ + /* default to FT_CACHE_H at the moment just in case, but we know of */ + /* no rogue client that uses them. */ + /* */ +#define FT_CACHE_MANAGER_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_MRU_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_MANAGER_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_CACHE_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_GLYPH_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_IMAGE_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_SBITS_H <freetype/ftcache.h> + + +#define FT_INCREMENTAL_H <freetype/ftincrem.h> + +#define FT_TRUETYPE_UNPATENTED_H <freetype/ttunpat.h> + + + /* + * Include internal headers definitions from <internal/...> + * only when building the library. + */ +#ifdef FT2_BUILD_LIBRARY +#define FT_INTERNAL_INTERNAL_H <freetype/internal/internal.h> +#include FT_INTERNAL_INTERNAL_H +#endif /* FT2_BUILD_LIBRARY */ + + +#endif /* FTHEADER_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/config/ftmodule.h b/foreign/freetype2/freetype/config/ftmodule.h new file mode 100644 index 0000000..76d271a --- /dev/null +++ b/foreign/freetype2/freetype/config/ftmodule.h @@ -0,0 +1,32 @@ +/* + * This file registers the FreeType modules compiled into the library. + * + * If you use GNU make, this file IS NOT USED! Instead, it is created in + * the objects directory (normally `<topdir>/objs/') based on information + * from `<topdir>/modules.cfg'. + * + * Please read `docs/INSTALL.ANY' and `docs/CUSTOMIZE' how to compile + * FreeType without GNU make. + * + */ + +FT_USE_MODULE( FT_Module_Class, autofit_module_class ) +FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, cff_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t1cid_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, pfr_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, t42_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, winfnt_driver_class ) +FT_USE_MODULE( FT_Driver_ClassRec, pcf_driver_class ) +FT_USE_MODULE( FT_Module_Class, psaux_module_class ) +FT_USE_MODULE( FT_Module_Class, psnames_module_class ) +FT_USE_MODULE( FT_Module_Class, pshinter_module_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class ) +FT_USE_MODULE( FT_Module_Class, sfnt_module_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class ) +FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class ) +FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class ) + +/* EOF */ diff --git a/foreign/freetype2/freetype/config/ftoption.h b/foreign/freetype2/freetype/config/ftoption.h new file mode 100644 index 0000000..a8097fe --- /dev/null +++ b/foreign/freetype2/freetype/config/ftoption.h @@ -0,0 +1,851 @@ +/***************************************************************************/ +/* */ +/* ftoption.h */ +/* */ +/* User-selectable configuration macros (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTOPTION_H_ +#define FTOPTION_H_ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* USER-SELECTABLE CONFIGURATION MACROS */ + /* */ + /* This file contains the default configuration macro definitions for */ + /* a standard build of the FreeType library. There are three ways to */ + /* use this file to build project-specific versions of the library: */ + /* */ + /* - You can modify this file by hand, but this is not recommended in */ + /* cases where you would like to build several versions of the */ + /* library from a single source directory. */ + /* */ + /* - You can put a copy of this file in your build directory, more */ + /* precisely in `$BUILD/freetype/config/ftoption.h', where `$BUILD' */ + /* is the name of a directory that is included _before_ the FreeType */ + /* include path during compilation. */ + /* */ + /* The default FreeType Makefiles and Jamfiles use the build */ + /* directory `builds/<system>' by default, but you can easily change */ + /* that for your own projects. */ + /* */ + /* - Copy the file <ft2build.h> to `$BUILD/ft2build.h' and modify it */ + /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ + /* locate this file during the build. For example, */ + /* */ + /* #define FT_CONFIG_OPTIONS_H <myftoptions.h> */ + /* #include <freetype/config/ftheader.h> */ + /* */ + /* will use `$BUILD/myftoptions.h' instead of this file for macro */ + /* definitions. */ + /* */ + /* Note also that you can similarly pre-define the macro */ + /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ + /* that are statically linked to the library at compile time. By */ + /* default, this file is <freetype/config/ftmodule.h>. */ + /* */ + /* We highly recommend using the third method whenever possible. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Uncomment the line below if you want to activate sub-pixel rendering */ + /* (a.k.a. LCD rendering, or ClearType) in this build of the library. */ + /* */ + /* Note that this feature is covered by several Microsoft patents */ + /* and should not be activated in any default build of the library. */ + /* */ + /* This macro has no impact on the FreeType API, only on its */ + /* _implementation_. For example, using FT_RENDER_MODE_LCD when calling */ + /* FT_Render_Glyph still generates a bitmap that is 3 times wider than */ + /* the original size in case this macro isn't defined; however, each */ + /* triplet of subpixels has R=G=B. */ + /* */ + /* This is done to allow FreeType clients to run unmodified, forcing */ + /* them to display normal gray-level anti-aliased glyphs. */ + /* */ +/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + + + /*************************************************************************/ + /* */ + /* Many compilers provide a non-ANSI 64-bit data type that can be used */ + /* by FreeType to speed up some computations. However, this will create */ + /* some problems when compiling the library in strict ANSI mode. */ + /* */ + /* For this reason, the use of 64-bit integers is normally disabled when */ + /* the __STDC__ macro is defined. You can however disable this by */ + /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ + /* */ + /* For most compilers, this will only create compilation warnings when */ + /* building the library. */ + /* */ + /* ObNote: The compiler-specific 64-bit integers are detected in the */ + /* file `ftconfig.h' either statically or through the */ + /* `configure' script on supported platforms. */ + /* */ +#undef FT_CONFIG_OPTION_FORCE_INT64 + + + /*************************************************************************/ + /* */ + /* If this macro is defined, do not try to use an assembler version of */ + /* performance-critical functions (e.g. FT_MulFix). You should only do */ + /* that to verify that the assembler function works properly, or to */ + /* execute benchmark tests of the various implementations. */ +/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */ + + + /*************************************************************************/ + /* */ + /* If this macro is defined, try to use an inlined assembler version of */ + /* the `FT_MulFix' function, which is a `hotspot' when loading and */ + /* hinting glyphs, and which should be executed as fast as possible. */ + /* */ + /* Note that if your compiler or CPU is not supported, this will default */ + /* to the standard and portable implementation found in `ftcalc.c'. */ + /* */ +#define FT_CONFIG_OPTION_INLINE_MULFIX + + + /*************************************************************************/ + /* */ + /* LZW-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `compress' program. This is mostly used to parse many of the PCF */ + /* files that come with various X11 distributions. The implementation */ + /* uses NetBSD's `zopen' to partially uncompress the file on the fly */ + /* (see src/lzw/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +#define FT_CONFIG_OPTION_USE_LZW + + + /*************************************************************************/ + /* */ + /* Gzip-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `gzip' program. This is mostly used to parse many of the PCF files */ + /* that come with XFree86. The implementation uses `zlib' to */ + /* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. See also */ + /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ + /* */ +#define FT_CONFIG_OPTION_USE_ZLIB + + + /*************************************************************************/ + /* */ + /* ZLib library selection */ + /* */ + /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ + /* It allows FreeType's `ftgzip' component to link to the system's */ + /* installation of the ZLib library. This is useful on systems like */ + /* Unix or VMS where it generally is already available. */ + /* */ + /* If you let it undefined, the component will use its own copy */ + /* of the zlib sources instead. These have been modified to be */ + /* included directly within the component and *not* export external */ + /* function names. This allows you to link any program with FreeType */ + /* _and_ ZLib without linking conflicts. */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */ + + + /*************************************************************************/ + /* */ + /* Bzip2-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `bzip2' program. This is mostly used to parse many of the PCF */ + /* files that come with XFree86. The implementation uses `libbz2' to */ + /* partially uncompress the file on the fly (see src/bzip2/ftbzip2.c). */ + /* Contrary to gzip, bzip2 currently is not included and need to use */ + /* the system available bzip2 implementation. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_BZIP2 */ + + + /*************************************************************************/ + /* */ + /* Define to disable the use of file stream functions and types, FILE, */ + /* fopen() etc. Enables the use of smaller system libraries on embedded */ + /* systems that have multiple system libraries, some with or without */ + /* file stream support, in the cases where file stream support is not */ + /* necessary such as memory loading of font files. */ + /* */ +/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ + + + /*************************************************************************/ + /* */ + /* PNG bitmap support. */ + /* */ + /* FreeType now handles loading color bitmap glyphs in the PNG format. */ + /* This requires help from the external libpng library. Uncompressed */ + /* color bitmaps do not need any external libraries and will be */ + /* supported regardless of this configuration. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_PNG */ + + + /*************************************************************************/ + /* */ + /* HarfBuzz support. */ + /* */ + /* FreeType uses the HarfBuzz library to improve auto-hinting of */ + /* OpenType fonts. If available, many glyphs not directly addressable */ + /* by a font's character map will be hinted also. */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */ + + + /*************************************************************************/ + /* */ + /* DLL export compilation */ + /* */ + /* When compiling FreeType as a DLL, some systems/compilers need a */ + /* special keyword in front OR after the return type of function */ + /* declarations. */ + /* */ + /* Two macros are used within the FreeType source code to define */ + /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ + /* */ + /* FT_EXPORT( return_type ) */ + /* */ + /* is used in a function declaration, as in */ + /* */ + /* FT_EXPORT( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ); */ + /* */ + /* */ + /* FT_EXPORT_DEF( return_type ) */ + /* */ + /* is used in a function definition, as in */ + /* */ + /* FT_EXPORT_DEF( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ) */ + /* { */ + /* ... some code ... */ + /* return FT_Err_Ok; */ + /* } */ + /* */ + /* You can provide your own implementation of FT_EXPORT and */ + /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ + /* will be later automatically defined as `extern return_type' to */ + /* allow normal compilation. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_EXPORT(x) extern x */ +/* #define FT_EXPORT_DEF(x) x */ + + + /*************************************************************************/ + /* */ + /* Glyph Postscript Names handling */ + /* */ + /* By default, FreeType 2 is compiled with the `psnames' module. This */ + /* module is in charge of converting a glyph name string into a */ + /* Unicode value, or return a Macintosh standard glyph name for the */ + /* use with the TrueType `post' table. */ + /* */ + /* Undefine this macro if you do not want `psnames' compiled in your */ + /* build of FreeType. This has the following effects: */ + /* */ + /* - The TrueType driver will provide its own set of glyph names, */ + /* if you build it to support postscript names in the TrueType */ + /* `post' table. */ + /* */ + /* - The Type 1 driver will not be able to synthesize a Unicode */ + /* charmap out of the glyphs found in the fonts. */ + /* */ + /* You would normally undefine this configuration macro when building */ + /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ + /* */ +#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Postscript Names to Unicode Values support */ + /* */ + /* By default, FreeType 2 is built with the `PSNames' module compiled */ + /* in. Among other things, the module is used to convert a glyph name */ + /* into a Unicode value. This is especially useful in order to */ + /* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */ + /* through a big table named the `Adobe Glyph List' (AGL). */ + /* */ + /* Undefine this macro if you do not want the Adobe Glyph List */ + /* compiled in your `PSNames' module. The Type 1 driver will not be */ + /* able to synthesize a Unicode charmap out of the glyphs found in the */ + /* fonts. */ + /* */ +#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + + /*************************************************************************/ + /* */ + /* Support for Mac fonts */ + /* */ + /* Define this macro if you want support for outline fonts in Mac */ + /* format (mac dfont, mac resource, macbinary containing a mac */ + /* resource) on non-Mac platforms. */ + /* */ + /* Note that the `FOND' resource isn't checked. */ + /* */ +#define FT_CONFIG_OPTION_MAC_FONTS + + + /*************************************************************************/ + /* */ + /* Guessing methods to access embedded resource forks */ + /* */ + /* Enable extra Mac fonts support on non-Mac platforms (e.g. */ + /* GNU/Linux). */ + /* */ + /* Resource forks which include fonts data are stored sometimes in */ + /* locations which users or developers don't expected. In some cases, */ + /* resource forks start with some offset from the head of a file. In */ + /* other cases, the actual resource fork is stored in file different */ + /* from what the user specifies. If this option is activated, */ + /* FreeType tries to guess whether such offsets or different file */ + /* names must be used. */ + /* */ + /* Note that normal, direct access of resource forks is controlled via */ + /* the FT_CONFIG_OPTION_MAC_FONTS option. */ + /* */ +#ifdef FT_CONFIG_OPTION_MAC_FONTS +#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#endif + + + /*************************************************************************/ + /* */ + /* Allow the use of FT_Incremental_Interface to load typefaces that */ + /* contain no glyph data, but supply it via a callback function. */ + /* This is required by clients supporting document formats which */ + /* supply font data incrementally as the document is parsed, such */ + /* as the Ghostscript interpreter for the PostScript language. */ + /* */ +#define FT_CONFIG_OPTION_INCREMENTAL + + + /*************************************************************************/ + /* */ + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ + /* */ +#define FT_RENDER_POOL_SIZE 16384L + + + /*************************************************************************/ + /* */ + /* FT_MAX_MODULES */ + /* */ + /* The maximum number of modules that can be registered in a single */ + /* FreeType library object. 32 is the default. */ + /* */ +#define FT_MAX_MODULES 32 + + + /*************************************************************************/ + /* */ + /* Debug level */ + /* */ + /* FreeType can be compiled in debug or trace mode. In debug mode, */ + /* errors are reported through the `ftdebug' component. In trace */ + /* mode, additional messages are sent to the standard output during */ + /* execution. */ + /* */ + /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ + /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ + /* */ + /* Don't define any of these macros to compile in `release' mode! */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_LEVEL_ERROR */ +/* #define FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* Autofitter debugging */ + /* */ + /* If FT_DEBUG_AUTOFIT is defined, FreeType provides some means to */ + /* control the autofitter behaviour for debugging purposes with global */ + /* boolean variables (consequently, you should *never* enable this */ + /* while compiling in `release' mode): */ + /* */ + /* _af_debug_disable_horz_hints */ + /* _af_debug_disable_vert_hints */ + /* _af_debug_disable_blue_hints */ + /* */ + /* Additionally, the following functions provide dumps of various */ + /* internal autofit structures to stdout (using `printf'): */ + /* */ + /* af_glyph_hints_dump_points */ + /* af_glyph_hints_dump_segments */ + /* af_glyph_hints_dump_edges */ + /* af_glyph_hints_get_num_segments */ + /* af_glyph_hints_get_segment_offset */ + /* */ + /* As an argument, they use another global variable: */ + /* */ + /* _af_debug_hints */ + /* */ + /* Please have a look at the `ftgrid' demo program to see how those */ + /* variables and macros should be used. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_AUTOFIT */ + + + /*************************************************************************/ + /* */ + /* Memory Debugging */ + /* */ + /* FreeType now comes with an integrated memory debugger that is */ + /* capable of detecting simple errors like memory leaks or double */ + /* deletes. To compile it within your build of the library, you */ + /* should define FT_DEBUG_MEMORY here. */ + /* */ + /* Note that the memory debugger is only activated at runtime when */ + /* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_DEBUG_MEMORY */ + + + /*************************************************************************/ + /* */ + /* Module errors */ + /* */ + /* If this macro is set (which is _not_ the default), the higher byte */ + /* of an error code gives the module in which the error has occurred, */ + /* while the lower byte is the real error code. */ + /* */ + /* Setting this macro makes sense for debugging purposes only, since */ + /* it would break source compatibility of certain programs that use */ + /* FreeType 2. */ + /* */ + /* More details can be found in the files ftmoderr.h and fterrors.h. */ + /* */ +#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS + + + /*************************************************************************/ + /* */ + /* Position Independent Code */ + /* */ + /* If this macro is set (which is _not_ the default), FreeType2 will */ + /* avoid creating constants that require address fixups. Instead the */ + /* constants will be moved into a struct and additional intialization */ + /* code will be used. */ + /* */ + /* Setting this macro is needed for systems that prohibit address */ + /* fixups, such as BREW. */ + /* */ +/* #define FT_CONFIG_OPTION_PIC */ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** S F N T D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ + /* embedded bitmaps in all formats using the SFNT module (namely */ + /* TrueType & OpenType). */ + /* */ +#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ + /* load and enumerate the glyph Postscript names in a TrueType or */ + /* OpenType file. */ + /* */ + /* Note that when you do not compile the `PSNames' module by undefining */ + /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ + /* contain additional code used to read the PS Names table from a font. */ + /* */ + /* (By default, the module uses `PSNames' to extract glyph names.) */ + /* */ +#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ + /* access the internal name table in a SFNT-based format like TrueType */ + /* or OpenType. The name table contains various strings used to */ + /* describe the font, like family name, copyright, version, etc. It */ + /* does not contain any glyph name though. */ + /* */ + /* Accessing SFNT names is done through the functions declared in */ + /* `ftsnames.h'. */ + /* */ +#define TT_CONFIG_OPTION_SFNT_NAMES + + + /*************************************************************************/ + /* */ + /* TrueType CMap support */ + /* */ + /* Here you can fine-tune which TrueType CMap table format shall be */ + /* supported. */ +#define TT_CONFIG_CMAP_FORMAT_0 +#define TT_CONFIG_CMAP_FORMAT_2 +#define TT_CONFIG_CMAP_FORMAT_4 +#define TT_CONFIG_CMAP_FORMAT_6 +#define TT_CONFIG_CMAP_FORMAT_8 +#define TT_CONFIG_CMAP_FORMAT_10 +#define TT_CONFIG_CMAP_FORMAT_12 +#define TT_CONFIG_CMAP_FORMAT_13 +#define TT_CONFIG_CMAP_FORMAT_14 + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ + /* a bytecode interpreter in the TrueType driver. */ + /* */ + /* By undefining this, you will only compile the code necessary to load */ + /* TrueType glyphs without hinting. */ + /* */ + /* Do not #undef this macro here, since the build system might */ + /* define it for certain configurations only. */ + /* */ +#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ + /* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ + /* replaces the native TrueType hinting mechanism when anything but */ + /* FT_RENDER_MODE_MONO is requested. */ + /* */ + /* Enabling this causes the TrueType driver to ignore instructions under */ + /* certain conditions. This is done in accordance with the guide here, */ + /* with some minor differences: */ + /* */ + /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ + /* */ + /* By undefining this, you only compile the code necessary to hint */ + /* TrueType glyphs with native TT hinting. */ + /* */ + /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ + /* defined. */ + /* */ +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ + /* TrueType glyph loader to use Apple's definition of how to handle */ + /* component offsets in composite glyphs. */ + /* */ + /* Apple and MS disagree on the default behavior of component offsets */ + /* in composites. Apple says that they should be scaled by the scaling */ + /* factors in the transformation matrix (roughly, it's more complex) */ + /* while MS says they should not. OpenType defines two bits in the */ + /* composite flags array which can be used to disambiguate, but old */ + /* fonts will not have them. */ + /* */ + /* http://www.microsoft.com/typography/otspec/glyf.htm */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */ + /* */ +#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */ + /* support for Apple's distortable font technology (fvar, gvar, cvar, */ + /* and avar tables). This has many similarities to Type 1 Multiple */ + /* Masters support. */ + /* */ +#define TT_CONFIG_OPTION_GX_VAR_SUPPORT + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BDF if you want to include support for */ + /* an embedded `BDF ' table within SFNT-based bitmap formats. */ + /* */ +#define TT_CONFIG_OPTION_BDF + + + /*************************************************************************/ + /* */ + /* Option TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES controls the maximum */ + /* number of bytecode instructions executed for a single run of the */ + /* bytecode interpreter, needed to prevent infinite loops. You don't */ + /* want to change this except for very special situations (e.g., making */ + /* a library fuzzer spend less time to handle broken fonts). */ + /* */ + /* It is not expected that this value is ever modified by a configuring */ + /* script; instead, it gets surrounded with #ifndef ... #endif so that */ + /* the value can be set as a preprocessor option on the compiler's */ + /* command line. */ + /* */ +#ifndef TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES +#define TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES 1000000L +#endif + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* T1_MAX_DICT_DEPTH is the maximum depth of nest dictionaries and */ + /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ + /* required. */ + /* */ +#define T1_MAX_DICT_DEPTH 5 + + + /*************************************************************************/ + /* */ + /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ + /* calls during glyph loading. */ + /* */ +#define T1_MAX_SUBRS_CALLS 16 + + + /*************************************************************************/ + /* */ + /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ + /* minimum of 16 is required. */ + /* */ + /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ + /* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 256 + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of `t1afm', which is in charge of reading Type 1 AFM */ + /* files into an existing face. Note that if set, the T1 driver will be */ + /* unable to produce kerning distances. */ + /* */ +#undef T1_CONFIG_OPTION_NO_AFM + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of the Multiple Masters font support in the Type 1 */ + /* driver. */ + /* */ +#undef T1_CONFIG_OPTION_NO_MM_SUPPORT + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** C F F D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Using CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4} it is */ + /* possible to set up the default values of the four control points that */ + /* define the stem darkening behaviour of the (new) CFF engine. For */ + /* more details please read the documentation of the */ + /* `darkening-parameters' property of the cff driver module (file */ + /* `ftcffdrv.h'), which allows the control at run-time. */ + /* */ + /* Do *not* undefine these macros! */ + /* */ +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275 + +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333 +#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0 + + + /*************************************************************************/ + /* */ + /* CFF_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe CFF */ + /* engine gets compiled into FreeType. If defined, it is possible to */ + /* switch between the two engines using the `hinting-engine' property of */ + /* the cff driver module. */ + /* */ +/* #define CFF_CONFIG_OPTION_OLD_ENGINE */ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Compile autofit module with CJK (Chinese, Japanese, Korean) script */ + /* support. */ + /* */ +#define AF_CONFIG_OPTION_CJK + + /*************************************************************************/ + /* */ + /* Compile autofit module with Indic script support. */ + /* */ +#define AF_CONFIG_OPTION_INDIC + + /*************************************************************************/ + /* */ + /* Compile autofit module with warp hinting. The idea of the warping */ + /* code is to slightly scale and shift a glyph within a single dimension */ + /* so that as much of its segments are aligned (more or less) on the */ + /* grid. To find out the optimal scaling and shifting value, various */ + /* parameter combinations are tried and scored. */ + /* */ + /* This experimental option is active only if the rendering mode is */ + /* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */ + /* `warping' property of the auto-hinter (see file `ftautoh.h' for more */ + /* information; by default it is switched off). */ + /* */ +#define AF_CONFIG_OPTION_USE_WARPER + + /* */ + + + /* + * This macro is obsolete. Support has been removed in FreeType + * version 2.5. + */ +/* #define FT_CONFIG_OPTION_OLD_INTERNALS */ + + + /* + * This macro is defined if native TrueType hinting is requested by the + * definitions above. + */ +#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER +#define TT_USE_BYTECODE_INTERPRETER +#endif + + + /* + * Check CFF darkening parameters. The checks are the same as in function + * `cff_property_set' in file `cffdrivr.c'. + */ +#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \ + \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \ + CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500 +#error "Invalid CFF darkening parameters!" +#endif + +FT_END_HEADER + + +#endif /* FTOPTION_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/config/ftstdlib.h b/foreign/freetype2/freetype/config/ftstdlib.h new file mode 100644 index 0000000..9daea56 --- /dev/null +++ b/foreign/freetype2/freetype/config/ftstdlib.h @@ -0,0 +1,173 @@ +/***************************************************************************/ +/* */ +/* ftstdlib.h */ +/* */ +/* ANSI-specific library and header configuration file (specification */ +/* only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to group all #includes to the ANSI C library that */ + /* FreeType normally requires. It also defines macros to rename the */ + /* standard functions within the FreeType source code. */ + /* */ + /* Load a file which defines FTSTDLIB_H_ before this one to override it. */ + /* */ + /*************************************************************************/ + + +#ifndef FTSTDLIB_H_ +#define FTSTDLIB_H_ + + +#include <stddef.h> + +#define ft_ptrdiff_t ptrdiff_t + + + /**********************************************************************/ + /* */ + /* integer limits */ + /* */ + /* UINT_MAX and ULONG_MAX are used to automatically compute the size */ + /* of `int' and `long' in bytes at compile-time. So far, this works */ + /* for all platforms the library has been tested on. */ + /* */ + /* Note that on the extremely rare platforms that do not provide */ + /* integer types that are _exactly_ 16 and 32 bits wide (e.g. some */ + /* old Crays where `int' is 36 bits), we do not make any guarantee */ + /* about the correct behaviour of FT2 with all fonts. */ + /* */ + /* In these case, `ftconfig.h' will refuse to compile anyway with a */ + /* message like `couldn't find 32-bit type' or something similar. */ + /* */ + /**********************************************************************/ + + +#include <limits.h> + +#define FT_CHAR_BIT CHAR_BIT +#define FT_USHORT_MAX USHRT_MAX +#define FT_INT_MAX INT_MAX +#define FT_INT_MIN INT_MIN +#define FT_UINT_MAX UINT_MAX +#define FT_LONG_MAX LONG_MAX +#define FT_ULONG_MAX ULONG_MAX + + + /**********************************************************************/ + /* */ + /* character and string processing */ + /* */ + /**********************************************************************/ + + +#include <string.h> + +#define ft_memchr memchr +#define ft_memcmp memcmp +#define ft_memcpy memcpy +#define ft_memmove memmove +#define ft_memset memset +#define ft_strcat strcat +#define ft_strcmp strcmp +#define ft_strcpy strcpy +#define ft_strlen strlen +#define ft_strncmp strncmp +#define ft_strncpy strncpy +#define ft_strrchr strrchr +#define ft_strstr strstr + + + /**********************************************************************/ + /* */ + /* file handling */ + /* */ + /**********************************************************************/ + + +#include <stdio.h> + +#define FT_FILE FILE +#define ft_fclose fclose +#define ft_fopen fopen +#define ft_fread fread +#define ft_fseek fseek +#define ft_ftell ftell +#define ft_sprintf sprintf + + + /**********************************************************************/ + /* */ + /* sorting */ + /* */ + /**********************************************************************/ + + +#include <stdlib.h> + +#define ft_qsort qsort + + + /**********************************************************************/ + /* */ + /* memory allocation */ + /* */ + /**********************************************************************/ + + +#define ft_scalloc calloc +#define ft_sfree free +#define ft_smalloc malloc +#define ft_srealloc realloc + + + /**********************************************************************/ + /* */ + /* miscellaneous */ + /* */ + /**********************************************************************/ + + +#define ft_atol atol + + + /**********************************************************************/ + /* */ + /* execution control */ + /* */ + /**********************************************************************/ + + +#include <setjmp.h> + +#define ft_jmp_buf jmp_buf /* note: this cannot be a typedef since */ + /* jmp_buf is defined as a macro */ + /* on certain platforms */ + +#define ft_longjmp longjmp +#define ft_setjmp( b ) setjmp( *(ft_jmp_buf*) &(b) ) /* same thing here */ + + + /* the following is only used for debugging purposes, i.e., if */ + /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE are defined */ + +#include <stdarg.h> + + +#endif /* FTSTDLIB_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/freetype.h b/foreign/freetype2/freetype/freetype.h new file mode 100644 index 0000000..4666d48 --- /dev/null +++ b/foreign/freetype2/freetype/freetype.h @@ -0,0 +1,4272 @@ +/***************************************************************************/ +/* */ +/* freetype.h */ +/* */ +/* FreeType high-level API and common types (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FREETYPE_H_ +#define FREETYPE_H_ + + +#ifndef FT_FREETYPE_H +#error "`ft2build.h' hasn't been included yet!" +#error "Please always use macros to include FreeType header files." +#error "Example:" +#error " #include <ft2build.h>" +#error " #include FT_FREETYPE_H" +#endif + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_TYPES_H +#include FT_ERRORS_H + + +FT_BEGIN_HEADER + + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* header_inclusion */ + /* */ + /* <Title> */ + /* FreeType's header inclusion scheme */ + /* */ + /* <Abstract> */ + /* How client applications should include FreeType header files. */ + /* */ + /* <Description> */ + /* To be as flexible as possible (and for historical reasons), */ + /* FreeType uses a very special inclusion scheme to load header */ + /* files, for example */ + /* */ + /* { */ + /* #include <ft2build.h> */ + /* */ + /* #include FT_FREETYPE_H */ + /* #include FT_OUTLINE_H */ + /* } */ + /* */ + /* A compiler and its preprocessor only needs an include path to find */ + /* the file `ft2build.h'; the exact locations and names of the other */ + /* FreeType header files are hidden by preprocessor macro names, */ + /* loaded by `ft2build.h'. The API documentation always gives the */ + /* header macro name needed for a particular function. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* user_allocation */ + /* */ + /* <Title> */ + /* User allocation */ + /* */ + /* <Abstract> */ + /* How client applications should allocate FreeType data structures. */ + /* */ + /* <Description> */ + /* FreeType assumes that structures allocated by the user and passed */ + /* as arguments are zeroed out except for the actual data. In other */ + /* words, it is recommended to use `calloc' (or variants of it) */ + /* instead of `malloc' for allocation. */ + /* */ + /*************************************************************************/ + + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S I C T Y P E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* base_interface */ + /* */ + /* <Title> */ + /* Base Interface */ + /* */ + /* <Abstract> */ + /* The FreeType~2 base font interface. */ + /* */ + /* <Description> */ + /* This section describes the most important public high-level API */ + /* functions of FreeType~2. */ + /* */ + /* <Order> */ + /* FT_Library */ + /* FT_Face */ + /* FT_Size */ + /* FT_GlyphSlot */ + /* FT_CharMap */ + /* FT_Encoding */ + /* FT_ENC_TAG */ + /* */ + /* FT_FaceRec */ + /* */ + /* FT_FACE_FLAG_SCALABLE */ + /* FT_FACE_FLAG_FIXED_SIZES */ + /* FT_FACE_FLAG_FIXED_WIDTH */ + /* FT_FACE_FLAG_HORIZONTAL */ + /* FT_FACE_FLAG_VERTICAL */ + /* FT_FACE_FLAG_COLOR */ + /* FT_FACE_FLAG_SFNT */ + /* FT_FACE_FLAG_CID_KEYED */ + /* FT_FACE_FLAG_TRICKY */ + /* FT_FACE_FLAG_KERNING */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS */ + /* FT_FACE_FLAG_GLYPH_NAMES */ + /* FT_FACE_FLAG_EXTERNAL_STREAM */ + /* FT_FACE_FLAG_HINTER */ + /* */ + /* FT_HAS_HORIZONTAL */ + /* FT_HAS_VERTICAL */ + /* FT_HAS_KERNING */ + /* FT_HAS_FIXED_SIZES */ + /* FT_HAS_GLYPH_NAMES */ + /* FT_HAS_MULTIPLE_MASTERS */ + /* FT_HAS_COLOR */ + /* */ + /* FT_IS_SFNT */ + /* FT_IS_SCALABLE */ + /* FT_IS_FIXED_WIDTH */ + /* FT_IS_CID_KEYED */ + /* FT_IS_TRICKY */ + /* */ + /* FT_STYLE_FLAG_BOLD */ + /* FT_STYLE_FLAG_ITALIC */ + /* */ + /* FT_SizeRec */ + /* FT_Size_Metrics */ + /* */ + /* FT_GlyphSlotRec */ + /* FT_Glyph_Metrics */ + /* FT_SubGlyph */ + /* */ + /* FT_Bitmap_Size */ + /* */ + /* FT_Init_FreeType */ + /* FT_Done_FreeType */ + /* */ + /* FT_New_Face */ + /* FT_Done_Face */ + /* FT_Reference_Face */ + /* FT_New_Memory_Face */ + /* FT_Open_Face */ + /* FT_Open_Args */ + /* FT_Parameter */ + /* FT_Attach_File */ + /* FT_Attach_Stream */ + /* */ + /* FT_Set_Char_Size */ + /* FT_Set_Pixel_Sizes */ + /* FT_Request_Size */ + /* FT_Select_Size */ + /* FT_Size_Request_Type */ + /* FT_Size_RequestRec */ + /* FT_Size_Request */ + /* FT_Set_Transform */ + /* FT_Load_Glyph */ + /* FT_Get_Char_Index */ + /* FT_Get_First_Char */ + /* FT_Get_Next_Char */ + /* FT_Get_Name_Index */ + /* FT_Load_Char */ + /* */ + /* FT_OPEN_MEMORY */ + /* FT_OPEN_STREAM */ + /* FT_OPEN_PATHNAME */ + /* FT_OPEN_DRIVER */ + /* FT_OPEN_PARAMS */ + /* */ + /* FT_LOAD_DEFAULT */ + /* FT_LOAD_RENDER */ + /* FT_LOAD_MONOCHROME */ + /* FT_LOAD_LINEAR_DESIGN */ + /* FT_LOAD_NO_SCALE */ + /* FT_LOAD_NO_HINTING */ + /* FT_LOAD_NO_BITMAP */ + /* FT_LOAD_NO_AUTOHINT */ + /* FT_LOAD_COLOR */ + /* */ + /* FT_LOAD_VERTICAL_LAYOUT */ + /* FT_LOAD_IGNORE_TRANSFORM */ + /* FT_LOAD_FORCE_AUTOHINT */ + /* FT_LOAD_NO_RECURSE */ + /* FT_LOAD_PEDANTIC */ + /* */ + /* FT_LOAD_TARGET_NORMAL */ + /* FT_LOAD_TARGET_LIGHT */ + /* FT_LOAD_TARGET_MONO */ + /* FT_LOAD_TARGET_LCD */ + /* FT_LOAD_TARGET_LCD_V */ + /* */ + /* FT_LOAD_TARGET_MODE */ + /* */ + /* FT_Render_Glyph */ + /* FT_Render_Mode */ + /* FT_Get_Kerning */ + /* FT_Kerning_Mode */ + /* FT_Get_Track_Kerning */ + /* FT_Get_Glyph_Name */ + /* FT_Get_Postscript_Name */ + /* */ + /* FT_CharMapRec */ + /* FT_Select_Charmap */ + /* FT_Set_Charmap */ + /* FT_Get_Charmap_Index */ + /* */ + /* FT_Get_FSType_Flags */ + /* FT_Get_SubGlyph_Info */ + /* */ + /* FT_Face_Internal */ + /* FT_Size_Internal */ + /* FT_Slot_Internal */ + /* */ + /* FT_FACE_FLAG_XXX */ + /* FT_STYLE_FLAG_XXX */ + /* FT_OPEN_XXX */ + /* FT_LOAD_XXX */ + /* FT_LOAD_TARGET_XXX */ + /* FT_SUBGLYPH_FLAG_XXX */ + /* FT_FSTYPE_XXX */ + /* */ + /* FT_HAS_FAST_GLYPHS */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Glyph_Metrics */ + /* */ + /* <Description> */ + /* A structure used to model the metrics of a single glyph. The */ + /* values are expressed in 26.6 fractional pixel format; if the flag */ + /* @FT_LOAD_NO_SCALE has been used while loading the glyph, values */ + /* are expressed in font units instead. */ + /* */ + /* <Fields> */ + /* width :: */ + /* The glyph's width. */ + /* */ + /* height :: */ + /* The glyph's height. */ + /* */ + /* horiBearingX :: */ + /* Left side bearing for horizontal layout. */ + /* */ + /* horiBearingY :: */ + /* Top side bearing for horizontal layout. */ + /* */ + /* horiAdvance :: */ + /* Advance width for horizontal layout. */ + /* */ + /* vertBearingX :: */ + /* Left side bearing for vertical layout. */ + /* */ + /* vertBearingY :: */ + /* Top side bearing for vertical layout. Larger positive values */ + /* mean further below the vertical glyph origin. */ + /* */ + /* vertAdvance :: */ + /* Advance height for vertical layout. Positive values mean the */ + /* glyph has a positive advance downward. */ + /* */ + /* <Note> */ + /* If not disabled with @FT_LOAD_NO_HINTING, the values represent */ + /* dimensions of the hinted glyph (in case hinting is applicable). */ + /* */ + /* Stroking a glyph with an outside border does not increase */ + /* `horiAdvance' or `vertAdvance'; you have to manually adjust these */ + /* values to account for the added width and height. */ + /* */ + typedef struct FT_Glyph_Metrics_ + { + FT_Pos width; + FT_Pos height; + + FT_Pos horiBearingX; + FT_Pos horiBearingY; + FT_Pos horiAdvance; + + FT_Pos vertBearingX; + FT_Pos vertBearingY; + FT_Pos vertAdvance; + + } FT_Glyph_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap_Size */ + /* */ + /* <Description> */ + /* This structure models the metrics of a bitmap strike (i.e., a set */ + /* of glyphs for a given point size and resolution) in a bitmap font. */ + /* It is used for the `available_sizes' field of @FT_Face. */ + /* */ + /* <Fields> */ + /* height :: The vertical distance, in pixels, between two */ + /* consecutive baselines. It is always positive. */ + /* */ + /* width :: The average width, in pixels, of all glyphs in the */ + /* strike. */ + /* */ + /* size :: The nominal size of the strike in 26.6 fractional */ + /* points. This field is not very useful. */ + /* */ + /* x_ppem :: The horizontal ppem (nominal width) in 26.6 fractional */ + /* pixels. */ + /* */ + /* y_ppem :: The vertical ppem (nominal height) in 26.6 fractional */ + /* pixels. */ + /* */ + /* <Note> */ + /* Windows FNT: */ + /* The nominal size given in a FNT font is not reliable. Thus when */ + /* the driver finds it incorrect, it sets `size' to some calculated */ + /* values and sets `x_ppem' and `y_ppem' to the pixel width and */ + /* height given in the font, respectively. */ + /* */ + /* TrueType embedded bitmaps: */ + /* `size', `width', and `height' values are not contained in the */ + /* bitmap strike itself. They are computed from the global font */ + /* parameters. */ + /* */ + typedef struct FT_Bitmap_Size_ + { + FT_Short height; + FT_Short width; + + FT_Pos size; + + FT_Pos x_ppem; + FT_Pos y_ppem; + + } FT_Bitmap_Size; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Library */ + /* */ + /* <Description> */ + /* A handle to a FreeType library instance. Each `library' is */ + /* completely independent from the others; it is the `root' of a set */ + /* of objects like fonts, faces, sizes, etc. */ + /* */ + /* It also embeds a memory manager (see @FT_Memory), as well as a */ + /* scan-line converter object (see @FT_Raster). */ + /* */ + /* In multi-threaded applications it is easiest to use one */ + /* `FT_Library' object per thread. In case this is too cumbersome, */ + /* a single `FT_Library' object across threads is possible also */ + /* (since FreeType version 2.5.6), as long as a mutex lock is used */ + /* around @FT_New_Face and @FT_Done_Face. */ + /* */ + /* <Note> */ + /* Library objects are normally created by @FT_Init_FreeType, and */ + /* destroyed with @FT_Done_FreeType. If you need reference-counting */ + /* (cf. @FT_Reference_Library), use @FT_New_Library and */ + /* @FT_Done_Library. */ + /* */ + typedef struct FT_LibraryRec_ *FT_Library; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Module */ + /* */ + /* <Description> */ + /* A handle to a given FreeType module object. Each module can be a */ + /* font driver, a renderer, or anything else that provides services */ + /* to the formers. */ + /* */ + typedef struct FT_ModuleRec_* FT_Module; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Driver */ + /* */ + /* <Description> */ + /* A handle to a given FreeType font driver object. Each font driver */ + /* is a special module capable of creating faces from font files. */ + /* */ + typedef struct FT_DriverRec_* FT_Driver; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Renderer */ + /* */ + /* <Description> */ + /* A handle to a given FreeType renderer. A renderer is a special */ + /* module in charge of converting a glyph image to a bitmap, when */ + /* necessary. Each renderer supports a given glyph image format, and */ + /* one or more target surface depths. */ + /* */ + typedef struct FT_RendererRec_* FT_Renderer; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* base_interface */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face */ + /* */ + /* <Description> */ + /* A handle to a given typographic face object. A face object models */ + /* a given typeface, in a given style. */ + /* */ + /* <Note> */ + /* Each face object also owns a single @FT_GlyphSlot object, as well */ + /* as one or more @FT_Size objects. */ + /* */ + /* Use @FT_New_Face or @FT_Open_Face to create a new face object from */ + /* a given filepathname or a custom input stream. */ + /* */ + /* Use @FT_Done_Face to destroy it (along with its slot and sizes). */ + /* */ + /* An `FT_Face' object can only be safely used from one thread at a */ + /* time. Similarly, creation and destruction of `FT_Face' with the */ + /* same @FT_Library object can only be done from one thread at a */ + /* time. On the other hand, functions like @FT_Load_Glyph and its */ + /* siblings are thread-safe and do not need the lock to be held as */ + /* long as the same `FT_Face' object is not used from multiple */ + /* threads at the same time. */ + /* */ + /* <Also> */ + /* See @FT_FaceRec for the publicly accessible fields of a given face */ + /* object. */ + /* */ + typedef struct FT_FaceRec_* FT_Face; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size */ + /* */ + /* <Description> */ + /* A handle to an object used to model a face scaled to a given */ + /* character size. */ + /* */ + /* <Note> */ + /* Each @FT_Face has an _active_ @FT_Size object that is used by */ + /* functions like @FT_Load_Glyph to determine the scaling */ + /* transformation that in turn is used to load and hint glyphs and */ + /* metrics. */ + /* */ + /* You can use @FT_Set_Char_Size, @FT_Set_Pixel_Sizes, */ + /* @FT_Request_Size or even @FT_Select_Size to change the content */ + /* (i.e., the scaling values) of the active @FT_Size. */ + /* */ + /* You can use @FT_New_Size to create additional size objects for a */ + /* given @FT_Face, but they won't be used by other functions until */ + /* you activate it through @FT_Activate_Size. Only one size can be */ + /* activated at any given time per face. */ + /* */ + /* <Also> */ + /* See @FT_SizeRec for the publicly accessible fields of a given size */ + /* object. */ + /* */ + typedef struct FT_SizeRec_* FT_Size; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to a given `glyph slot'. A slot is a container where it */ + /* is possible to load any of the glyphs contained in its parent */ + /* face. */ + /* */ + /* In other words, each time you call @FT_Load_Glyph or */ + /* @FT_Load_Char, the slot's content is erased by the new glyph data, */ + /* i.e., the glyph's metrics, its image (bitmap or outline), and */ + /* other control information. */ + /* */ + /* <Also> */ + /* See @FT_GlyphSlotRec for the publicly accessible glyph fields. */ + /* */ + typedef struct FT_GlyphSlotRec_* FT_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_CharMap */ + /* */ + /* <Description> */ + /* A handle to a given character map. A charmap is used to translate */ + /* character codes in a given encoding into glyph indexes for its */ + /* parent's face. Some font formats may provide several charmaps per */ + /* font. */ + /* */ + /* Each face object owns zero or more charmaps, but only one of them */ + /* can be `active' and used by @FT_Get_Char_Index or @FT_Load_Char. */ + /* */ + /* The list of available charmaps in a face is available through the */ + /* `face->num_charmaps' and `face->charmaps' fields of @FT_FaceRec. */ + /* */ + /* The currently active charmap is available as `face->charmap'. */ + /* You should call @FT_Set_Charmap to change it. */ + /* */ + /* <Note> */ + /* When a new face is created (either through @FT_New_Face or */ + /* @FT_Open_Face), the library looks for a Unicode charmap within */ + /* the list and automatically activates it. */ + /* */ + /* <Also> */ + /* See @FT_CharMapRec for the publicly accessible fields of a given */ + /* character map. */ + /* */ + typedef struct FT_CharMapRec_* FT_CharMap; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_ENC_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags into an unsigned long. It is */ + /* used to define `encoding' identifiers (see @FT_Encoding). */ + /* */ + /* <Note> */ + /* Since many 16-bit compilers don't like 32-bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* { */ + /* #define FT_ENC_TAG( value, a, b, c, d ) value */ + /* } */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ + +#ifndef FT_ENC_TAG +#define FT_ENC_TAG( value, a, b, c, d ) \ + value = ( ( (FT_UInt32)(a) << 24 ) | \ + ( (FT_UInt32)(b) << 16 ) | \ + ( (FT_UInt32)(c) << 8 ) | \ + (FT_UInt32)(d) ) + +#endif /* FT_ENC_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Encoding */ + /* */ + /* <Description> */ + /* An enumeration used to specify character sets supported by */ + /* charmaps. Used in the @FT_Select_Charmap API function. */ + /* */ + /* <Note> */ + /* Despite the name, this enumeration lists specific character */ + /* repertories (i.e., charsets), and not text encoding methods (e.g., */ + /* UTF-8, UTF-16, etc.). */ + /* */ + /* Other encodings might be defined in the future. */ + /* */ + /* <Values> */ + /* FT_ENCODING_NONE :: */ + /* The encoding value~0 is reserved. */ + /* */ + /* FT_ENCODING_UNICODE :: */ + /* Corresponds to the Unicode character set. This value covers */ + /* all versions of the Unicode repertoire, including ASCII and */ + /* Latin-1. Most fonts include a Unicode charmap, but not all */ + /* of them. */ + /* */ + /* For example, if you want to access Unicode value U+1F028 (and */ + /* the font contains it), use value 0x1F028 as the input value for */ + /* @FT_Get_Char_Index. */ + /* */ + /* FT_ENCODING_MS_SYMBOL :: */ + /* Corresponds to the Microsoft Symbol encoding, used to encode */ + /* mathematical symbols and wingdings. For more information, see */ + /* `http://www.microsoft.com/typography/otspec/recom.htm', */ + /* `http://www.kostis.net/charsets/symbol.htm', and */ + /* `http://www.kostis.net/charsets/wingding.htm'. */ + /* */ + /* This encoding uses character codes from the PUA (Private Unicode */ + /* Area) in the range U+F020-U+F0FF. */ + /* */ + /* FT_ENCODING_SJIS :: */ + /* Corresponds to Japanese SJIS encoding. More info at */ + /* at `http://en.wikipedia.org/wiki/Shift_JIS'. */ + /* See note on multi-byte encodings below. */ + /* */ + /* FT_ENCODING_GB2312 :: */ + /* Corresponds to an encoding system for Simplified Chinese as used */ + /* used in mainland China. */ + /* */ + /* FT_ENCODING_BIG5 :: */ + /* Corresponds to an encoding system for Traditional Chinese as */ + /* used in Taiwan and Hong Kong. */ + /* */ + /* FT_ENCODING_WANSUNG :: */ + /* Corresponds to the Korean encoding system known as Wansung. */ + /* For more information see */ + /* `https://msdn.microsoft.com/en-US/goglobal/cc305154'. */ + /* */ + /* FT_ENCODING_JOHAB :: */ + /* The Korean standard character set (KS~C 5601-1992), which */ + /* corresponds to MS Windows code page 1361. This character set */ + /* includes all possible Hangeul character combinations. */ + /* */ + /* FT_ENCODING_ADOBE_LATIN_1 :: */ + /* Corresponds to a Latin-1 encoding as defined in a Type~1 */ + /* PostScript font. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_ADOBE_STANDARD :: */ + /* Corresponds to the Adobe Standard encoding, as found in Type~1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_EXPERT :: */ + /* Corresponds to the Adobe Expert encoding, as found in Type~1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_CUSTOM :: */ + /* Corresponds to a custom encoding, as found in Type~1, CFF, and */ + /* OpenType/CFF fonts. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_APPLE_ROMAN :: */ + /* Corresponds to the 8-bit Apple roman encoding. Many TrueType */ + /* and OpenType fonts contain a charmap for this encoding, since */ + /* older versions of Mac OS are able to use it. */ + /* */ + /* FT_ENCODING_OLD_LATIN_2 :: */ + /* This value is deprecated and was never used nor reported by */ + /* FreeType. Don't use or test for it. */ + /* */ + /* FT_ENCODING_MS_SJIS :: */ + /* Same as FT_ENCODING_SJIS. Deprecated. */ + /* */ + /* FT_ENCODING_MS_GB2312 :: */ + /* Same as FT_ENCODING_GB2312. Deprecated. */ + /* */ + /* FT_ENCODING_MS_BIG5 :: */ + /* Same as FT_ENCODING_BIG5. Deprecated. */ + /* */ + /* FT_ENCODING_MS_WANSUNG :: */ + /* Same as FT_ENCODING_WANSUNG. Deprecated. */ + /* */ + /* FT_ENCODING_MS_JOHAB :: */ + /* Same as FT_ENCODING_JOHAB. Deprecated. */ + /* */ + /* <Note> */ + /* By default, FreeType automatically synthesizes a Unicode charmap */ + /* for PostScript fonts, using their glyph names dictionaries. */ + /* However, it also reports the encodings defined explicitly in the */ + /* font file, for the cases when they are needed, with the Adobe */ + /* values as well. */ + /* */ + /* FT_ENCODING_NONE is set by the BDF and PCF drivers if the charmap */ + /* is neither Unicode nor ISO-8859-1 (otherwise it is set to */ + /* FT_ENCODING_UNICODE). Use @FT_Get_BDF_Charset_ID to find out */ + /* which encoding is really present. If, for example, the */ + /* `cs_registry' field is `KOI8' and the `cs_encoding' field is `R', */ + /* the font is encoded in KOI8-R. */ + /* */ + /* FT_ENCODING_NONE is always set (with a single exception) by the */ + /* winfonts driver. Use @FT_Get_WinFNT_Header and examine the */ + /* `charset' field of the @FT_WinFNT_HeaderRec structure to find out */ + /* which encoding is really present. For example, */ + /* @FT_WinFNT_ID_CP1251 (204) means Windows code page 1251 (for */ + /* Russian). */ + /* */ + /* FT_ENCODING_NONE is set if `platform_id' is @TT_PLATFORM_MACINTOSH */ + /* and `encoding_id' is not @TT_MAC_ID_ROMAN (otherwise it is set to */ + /* FT_ENCODING_APPLE_ROMAN). */ + /* */ + /* If `platform_id' is @TT_PLATFORM_MACINTOSH, use the function */ + /* @FT_Get_CMap_Language_ID to query the Mac language ID that may */ + /* be needed to be able to distinguish Apple encoding variants. See */ + /* */ + /* http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/Readme.txt */ + /* */ + /* to get an idea how to do that. Basically, if the language ID */ + /* is~0, don't use it, otherwise subtract 1 from the language ID. */ + /* Then examine `encoding_id'. If, for example, `encoding_id' is */ + /* @TT_MAC_ID_ROMAN and the language ID (minus~1) is */ + /* `TT_MAC_LANGID_GREEK', it is the Greek encoding, not Roman. */ + /* @TT_MAC_ID_ARABIC with `TT_MAC_LANGID_FARSI' means the Farsi */ + /* variant the Arabic encoding. */ + /* */ + typedef enum FT_Encoding_ + { + FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ), + + FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ), + FT_ENC_TAG( FT_ENCODING_UNICODE, 'u', 'n', 'i', 'c' ), + + FT_ENC_TAG( FT_ENCODING_SJIS, 's', 'j', 'i', 's' ), + FT_ENC_TAG( FT_ENCODING_GB2312, 'g', 'b', ' ', ' ' ), + FT_ENC_TAG( FT_ENCODING_BIG5, 'b', 'i', 'g', '5' ), + FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ), + FT_ENC_TAG( FT_ENCODING_JOHAB, 'j', 'o', 'h', 'a' ), + + /* for backwards compatibility */ + FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS, + FT_ENCODING_MS_GB2312 = FT_ENCODING_GB2312, + FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5, + FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG, + FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB, + + FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT, 'A', 'D', 'B', 'E' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM, 'A', 'D', 'B', 'C' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1, 'l', 'a', 't', '1' ), + + FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ), + + FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' ) + + } FT_Encoding; + + + /* these constants are deprecated; use the corresponding `FT_Encoding' */ + /* values instead */ +#define ft_encoding_none FT_ENCODING_NONE +#define ft_encoding_unicode FT_ENCODING_UNICODE +#define ft_encoding_symbol FT_ENCODING_MS_SYMBOL +#define ft_encoding_latin_1 FT_ENCODING_ADOBE_LATIN_1 +#define ft_encoding_latin_2 FT_ENCODING_OLD_LATIN_2 +#define ft_encoding_sjis FT_ENCODING_SJIS +#define ft_encoding_gb2312 FT_ENCODING_GB2312 +#define ft_encoding_big5 FT_ENCODING_BIG5 +#define ft_encoding_wansung FT_ENCODING_WANSUNG +#define ft_encoding_johab FT_ENCODING_JOHAB + +#define ft_encoding_adobe_standard FT_ENCODING_ADOBE_STANDARD +#define ft_encoding_adobe_expert FT_ENCODING_ADOBE_EXPERT +#define ft_encoding_adobe_custom FT_ENCODING_ADOBE_CUSTOM +#define ft_encoding_apple_roman FT_ENCODING_APPLE_ROMAN + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_CharMapRec */ + /* */ + /* <Description> */ + /* The base charmap structure. */ + /* */ + /* <Fields> */ + /* face :: A handle to the parent face object. */ + /* */ + /* encoding :: An @FT_Encoding tag identifying the charmap. Use */ + /* this with @FT_Select_Charmap. */ + /* */ + /* platform_id :: An ID number describing the platform for the */ + /* following encoding ID. This comes directly from */ + /* the TrueType specification and should be emulated */ + /* for other formats. */ + /* */ + /* encoding_id :: A platform specific encoding number. This also */ + /* comes from the TrueType specification and should be */ + /* emulated similarly. */ + /* */ + typedef struct FT_CharMapRec_ + { + FT_Face face; + FT_Encoding encoding; + FT_UShort platform_id; + FT_UShort encoding_id; + + } FT_CharMapRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S E O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Face_InternalRec' structure, used to */ + /* model private data of a given @FT_Face object. */ + /* */ + /* This structure might change between releases of FreeType~2 and is */ + /* not generally available to client applications. */ + /* */ + typedef struct FT_Face_InternalRec_* FT_Face_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_FaceRec */ + /* */ + /* <Description> */ + /* FreeType root face class structure. A face object models a */ + /* typeface in a font file. */ + /* */ + /* <Fields> */ + /* num_faces :: The number of faces in the font file. Some */ + /* font formats can have multiple faces in */ + /* a font file. */ + /* */ + /* face_index :: This field holds two different values. */ + /* Bits 0-15 are the index of the face in the */ + /* font file (starting with value~0). They */ + /* are set to~0 if there is only one face in */ + /* the font file. */ + /* */ + /* Bits 16-30 are relevant to GX variation */ + /* fonts only, holding the named instance */ + /* index for the current face index (starting */ + /* with value~1; value~0 indicates font access */ + /* without GX variation data). For non-GX */ + /* fonts, bits 16-30 are ignored. If we have */ + /* the third named instance of face~4, say, */ + /* `face_index' is set to 0x00030004. */ + /* */ + /* Bit 31 is always zero (this is, */ + /* `face_index' is always a positive value). */ + /* */ + /* face_flags :: A set of bit flags that give important */ + /* information about the face; see */ + /* @FT_FACE_FLAG_XXX for the details. */ + /* */ + /* style_flags :: The lower 16~bits contain a set of bit */ + /* flags indicating the style of the face; see */ + /* @FT_STYLE_FLAG_XXX for the details. Bits */ + /* 16-30 hold the number of named instances */ + /* available for the current face if we have a */ + /* GX variation (sub)font. Bit 31 is always */ + /* zero (this is, `style_flags' is always a */ + /* positive value). */ + /* */ + /* num_glyphs :: The number of glyphs in the face. If the */ + /* face is scalable and has sbits (see */ + /* `num_fixed_sizes'), it is set to the number */ + /* of outline glyphs. */ + /* */ + /* For CID-keyed fonts, this value gives the */ + /* highest CID used in the font. */ + /* */ + /* family_name :: The face's family name. This is an ASCII */ + /* string, usually in English, that describes */ + /* the typeface's family (like `Times New */ + /* Roman', `Bodoni', `Garamond', etc). This */ + /* is a least common denominator used to list */ + /* fonts. Some formats (TrueType & OpenType) */ + /* provide localized and Unicode versions of */ + /* this string. Applications should use the */ + /* format specific interface to access them. */ + /* Can be NULL (e.g., in fonts embedded in a */ + /* PDF file). */ + /* */ + /* In case the font doesn't provide a specific */ + /* family name entry, FreeType tries to */ + /* synthesize one, deriving it from other name */ + /* entries. */ + /* */ + /* style_name :: The face's style name. This is an ASCII */ + /* string, usually in English, that describes */ + /* the typeface's style (like `Italic', */ + /* `Bold', `Condensed', etc). Not all font */ + /* formats provide a style name, so this field */ + /* is optional, and can be set to NULL. As */ + /* for `family_name', some formats provide */ + /* localized and Unicode versions of this */ + /* string. Applications should use the format */ + /* specific interface to access them. */ + /* */ + /* num_fixed_sizes :: The number of bitmap strikes in the face. */ + /* Even if the face is scalable, there might */ + /* still be bitmap strikes, which are called */ + /* `sbits' in that case. */ + /* */ + /* available_sizes :: An array of @FT_Bitmap_Size for all bitmap */ + /* strikes in the face. It is set to NULL if */ + /* there is no bitmap strike. */ + /* */ + /* num_charmaps :: The number of charmaps in the face. */ + /* */ + /* charmaps :: An array of the charmaps of the face. */ + /* */ + /* generic :: A field reserved for client uses. See the */ + /* @FT_Generic type description. */ + /* */ + /* bbox :: The font bounding box. Coordinates are */ + /* expressed in font units (see */ + /* `units_per_EM'). The box is large enough */ + /* to contain any glyph from the font. Thus, */ + /* `bbox.yMax' can be seen as the `maximum */ + /* ascender', and `bbox.yMin' as the `minimum */ + /* descender'. Only relevant for scalable */ + /* formats. */ + /* */ + /* Note that the bounding box might be off by */ + /* (at least) one pixel for hinted fonts. See */ + /* @FT_Size_Metrics for further discussion. */ + /* */ + /* units_per_EM :: The number of font units per EM square for */ + /* this face. This is typically 2048 for */ + /* TrueType fonts, and 1000 for Type~1 fonts. */ + /* Only relevant for scalable formats. */ + /* */ + /* ascender :: The typographic ascender of the face, */ + /* expressed in font units. For font formats */ + /* not having this information, it is set to */ + /* `bbox.yMax'. Only relevant for scalable */ + /* formats. */ + /* */ + /* descender :: The typographic descender of the face, */ + /* expressed in font units. For font formats */ + /* not having this information, it is set to */ + /* `bbox.yMin'. Note that this field is */ + /* usually negative. Only relevant for */ + /* scalable formats. */ + /* */ + /* height :: This value is the vertical distance */ + /* between two consecutive baselines, */ + /* expressed in font units. It is always */ + /* positive. Only relevant for scalable */ + /* formats. */ + /* */ + /* If you want the global glyph height, use */ + /* `ascender - descender'. */ + /* */ + /* max_advance_width :: The maximum advance width, in font units, */ + /* for all glyphs in this face. This can be */ + /* used to make word wrapping computations */ + /* faster. Only relevant for scalable */ + /* formats. */ + /* */ + /* max_advance_height :: The maximum advance height, in font units, */ + /* for all glyphs in this face. This is only */ + /* relevant for vertical layouts, and is set */ + /* to `height' for fonts that do not provide */ + /* vertical metrics. Only relevant for */ + /* scalable formats. */ + /* */ + /* underline_position :: The position, in font units, of the */ + /* underline line for this face. It is the */ + /* center of the underlining stem. Only */ + /* relevant for scalable formats. */ + /* */ + /* underline_thickness :: The thickness, in font units, of the */ + /* underline for this face. Only relevant for */ + /* scalable formats. */ + /* */ + /* glyph :: The face's associated glyph slot(s). */ + /* */ + /* size :: The current active size for this face. */ + /* */ + /* charmap :: The current active charmap for this face. */ + /* */ + /* <Note> */ + /* Fields may be changed after a call to @FT_Attach_File or */ + /* @FT_Attach_Stream. */ + /* */ + typedef struct FT_FaceRec_ + { + FT_Long num_faces; + FT_Long face_index; + + FT_Long face_flags; + FT_Long style_flags; + + FT_Long num_glyphs; + + FT_String* family_name; + FT_String* style_name; + + FT_Int num_fixed_sizes; + FT_Bitmap_Size* available_sizes; + + FT_Int num_charmaps; + FT_CharMap* charmaps; + + FT_Generic generic; + + /*# The following member variables (down to `underline_thickness') */ + /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */ + /*# for bitmap fonts. */ + FT_BBox bbox; + + FT_UShort units_per_EM; + FT_Short ascender; + FT_Short descender; + FT_Short height; + + FT_Short max_advance_width; + FT_Short max_advance_height; + + FT_Short underline_position; + FT_Short underline_thickness; + + FT_GlyphSlot glyph; + FT_Size size; + FT_CharMap charmap; + + /*@private begin */ + + FT_Driver driver; + FT_Memory memory; + FT_Stream stream; + + FT_ListRec sizes_list; + + FT_Generic autohint; /* face-specific auto-hinter data */ + void* extensions; /* unused */ + + FT_Face_Internal internal; + + /*@private end */ + + } FT_FaceRec; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_FACE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used in the `face_flags' field of the */ + /* @FT_FaceRec structure. They inform client applications of */ + /* properties of the corresponding face. */ + /* */ + /* <Values> */ + /* FT_FACE_FLAG_SCALABLE :: */ + /* Indicates that the face contains outline glyphs. This doesn't */ + /* prevent bitmap strikes, i.e., a face can have both this and */ + /* and @FT_FACE_FLAG_FIXED_SIZES set. */ + /* */ + /* FT_FACE_FLAG_FIXED_SIZES :: */ + /* Indicates that the face contains bitmap strikes. See also the */ + /* `num_fixed_sizes' and `available_sizes' fields of @FT_FaceRec. */ + /* */ + /* FT_FACE_FLAG_FIXED_WIDTH :: */ + /* Indicates that the face contains fixed-width characters (like */ + /* Courier, Lucido, MonoType, etc.). */ + /* */ + /* FT_FACE_FLAG_SFNT :: */ + /* Indicates that the face uses the `sfnt' storage scheme. For */ + /* now, this means TrueType and OpenType. */ + /* */ + /* FT_FACE_FLAG_HORIZONTAL :: */ + /* Indicates that the face contains horizontal glyph metrics. This */ + /* should be set for all common formats. */ + /* */ + /* FT_FACE_FLAG_VERTICAL :: */ + /* Indicates that the face contains vertical glyph metrics. This */ + /* is only available in some formats, not all of them. */ + /* */ + /* FT_FACE_FLAG_KERNING :: */ + /* Indicates that the face contains kerning information. If set, */ + /* the kerning distance can be retrieved through the function */ + /* @FT_Get_Kerning. Otherwise the function always return the */ + /* vector (0,0). Note that FreeType doesn't handle kerning data */ + /* from the `GPOS' table (as present in some OpenType fonts). */ + /* */ + /* FT_FACE_FLAG_FAST_GLYPHS :: */ + /* THIS FLAG IS DEPRECATED. DO NOT USE OR TEST IT. */ + /* */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS :: */ + /* Indicates that the font contains multiple masters and is capable */ + /* of interpolating between them. See the multiple-masters */ + /* specific API for details. */ + /* */ + /* FT_FACE_FLAG_GLYPH_NAMES :: */ + /* Indicates that the font contains glyph names that can be */ + /* retrieved through @FT_Get_Glyph_Name. Note that some TrueType */ + /* fonts contain broken glyph name tables. Use the function */ + /* @FT_Has_PS_Glyph_Names when needed. */ + /* */ + /* FT_FACE_FLAG_EXTERNAL_STREAM :: */ + /* Used internally by FreeType to indicate that a face's stream was */ + /* provided by the client application and should not be destroyed */ + /* when @FT_Done_Face is called. Don't read or test this flag. */ + /* */ + /* FT_FACE_FLAG_HINTER :: */ + /* Set if the font driver has a hinting machine of its own. For */ + /* example, with TrueType fonts, it makes sense to use data from */ + /* the SFNT `gasp' table only if the native TrueType hinting engine */ + /* (with the bytecode interpreter) is available and active. */ + /* */ + /* FT_FACE_FLAG_CID_KEYED :: */ + /* Set if the font is CID-keyed. In that case, the font is not */ + /* accessed by glyph indices but by CID values. For subsetted */ + /* CID-keyed fonts this has the consequence that not all index */ + /* values are a valid argument to FT_Load_Glyph. Only the CID */ + /* values for which corresponding glyphs in the subsetted font */ + /* exist make FT_Load_Glyph return successfully; in all other cases */ + /* you get an `FT_Err_Invalid_Argument' error. */ + /* */ + /* Note that CID-keyed fonts that are in an SFNT wrapper don't */ + /* have this flag set since the glyphs are accessed in the normal */ + /* way (using contiguous indices); the `CID-ness' isn't visible to */ + /* the application. */ + /* */ + /* FT_FACE_FLAG_TRICKY :: */ + /* Set if the font is `tricky', this is, it always needs the */ + /* font format's native hinting engine to get a reasonable result. */ + /* A typical example is the Chinese font `mingli.ttf' that uses */ + /* TrueType bytecode instructions to move and scale all of its */ + /* subglyphs. */ + /* */ + /* It is not possible to auto-hint such fonts using */ + /* @FT_LOAD_FORCE_AUTOHINT; it will also ignore */ + /* @FT_LOAD_NO_HINTING. You have to set both @FT_LOAD_NO_HINTING */ + /* and @FT_LOAD_NO_AUTOHINT to really disable hinting; however, you */ + /* probably never want this except for demonstration purposes. */ + /* */ + /* Currently, there are about a dozen TrueType fonts in the list of */ + /* tricky fonts; they are hard-coded in file `ttobjs.c'. */ + /* */ + /* FT_FACE_FLAG_COLOR :: */ + /* Set if the font has color glyph tables. To access color glyphs */ + /* use @FT_LOAD_COLOR. */ + /* */ +#define FT_FACE_FLAG_SCALABLE ( 1L << 0 ) +#define FT_FACE_FLAG_FIXED_SIZES ( 1L << 1 ) +#define FT_FACE_FLAG_FIXED_WIDTH ( 1L << 2 ) +#define FT_FACE_FLAG_SFNT ( 1L << 3 ) +#define FT_FACE_FLAG_HORIZONTAL ( 1L << 4 ) +#define FT_FACE_FLAG_VERTICAL ( 1L << 5 ) +#define FT_FACE_FLAG_KERNING ( 1L << 6 ) +#define FT_FACE_FLAG_FAST_GLYPHS ( 1L << 7 ) +#define FT_FACE_FLAG_MULTIPLE_MASTERS ( 1L << 8 ) +#define FT_FACE_FLAG_GLYPH_NAMES ( 1L << 9 ) +#define FT_FACE_FLAG_EXTERNAL_STREAM ( 1L << 10 ) +#define FT_FACE_FLAG_HINTER ( 1L << 11 ) +#define FT_FACE_FLAG_CID_KEYED ( 1L << 12 ) +#define FT_FACE_FLAG_TRICKY ( 1L << 13 ) +#define FT_FACE_FLAG_COLOR ( 1L << 14 ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_HORIZONTAL( face ) + * + * @description: + * A macro that returns true whenever a face object contains + * horizontal metrics (this is true for all font formats though). + * + * @also: + * @FT_HAS_VERTICAL can be used to check for vertical metrics. + * + */ +#define FT_HAS_HORIZONTAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_HORIZONTAL ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_VERTICAL( face ) + * + * @description: + * A macro that returns true whenever a face object contains real + * vertical metrics (and not only synthesized ones). + * + */ +#define FT_HAS_VERTICAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_VERTICAL ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_KERNING( face ) + * + * @description: + * A macro that returns true whenever a face object contains kerning + * data that can be accessed with @FT_Get_Kerning. + * + */ +#define FT_HAS_KERNING( face ) \ + ( face->face_flags & FT_FACE_FLAG_KERNING ) + + + /************************************************************************* + * + * @macro: + * FT_IS_SCALABLE( face ) + * + * @description: + * A macro that returns true whenever a face object contains a scalable + * font face (true for TrueType, Type~1, Type~42, CID, OpenType/CFF, + * and PFR font formats. + * + */ +#define FT_IS_SCALABLE( face ) \ + ( face->face_flags & FT_FACE_FLAG_SCALABLE ) + + + /************************************************************************* + * + * @macro: + * FT_IS_SFNT( face ) + * + * @description: + * A macro that returns true whenever a face object contains a font + * whose format is based on the SFNT storage scheme. This usually + * means: TrueType fonts, OpenType fonts, as well as SFNT-based embedded + * bitmap fonts. + * + * If this macro is true, all functions defined in @FT_SFNT_NAMES_H and + * @FT_TRUETYPE_TABLES_H are available. + * + */ +#define FT_IS_SFNT( face ) \ + ( face->face_flags & FT_FACE_FLAG_SFNT ) + + + /************************************************************************* + * + * @macro: + * FT_IS_FIXED_WIDTH( face ) + * + * @description: + * A macro that returns true whenever a face object contains a font face + * that contains fixed-width (or `monospace', `fixed-pitch', etc.) + * glyphs. + * + */ +#define FT_IS_FIXED_WIDTH( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_FIXED_SIZES( face ) + * + * @description: + * A macro that returns true whenever a face object contains some + * embedded bitmaps. See the `available_sizes' field of the + * @FT_FaceRec structure. + * + */ +#define FT_HAS_FIXED_SIZES( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_SIZES ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_FAST_GLYPHS( face ) + * + * @description: + * Deprecated. + * + */ +#define FT_HAS_FAST_GLYPHS( face ) 0 + + + /************************************************************************* + * + * @macro: + * FT_HAS_GLYPH_NAMES( face ) + * + * @description: + * A macro that returns true whenever a face object contains some glyph + * names that can be accessed through @FT_Get_Glyph_Name. + * + */ +#define FT_HAS_GLYPH_NAMES( face ) \ + ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_MULTIPLE_MASTERS( face ) + * + * @description: + * A macro that returns true whenever a face object contains some + * multiple masters. The functions provided by @FT_MULTIPLE_MASTERS_H + * are then available to choose the exact design you want. + * + */ +#define FT_HAS_MULTIPLE_MASTERS( face ) \ + ( face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS ) + + + /************************************************************************* + * + * @macro: + * FT_IS_CID_KEYED( face ) + * + * @description: + * A macro that returns true whenever a face object contains a CID-keyed + * font. See the discussion of @FT_FACE_FLAG_CID_KEYED for more + * details. + * + * If this macro is true, all functions defined in @FT_CID_H are + * available. + * + */ +#define FT_IS_CID_KEYED( face ) \ + ( face->face_flags & FT_FACE_FLAG_CID_KEYED ) + + + /************************************************************************* + * + * @macro: + * FT_IS_TRICKY( face ) + * + * @description: + * A macro that returns true whenever a face represents a `tricky' font. + * See the discussion of @FT_FACE_FLAG_TRICKY for more details. + * + */ +#define FT_IS_TRICKY( face ) \ + ( face->face_flags & FT_FACE_FLAG_TRICKY ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_COLOR( face ) + * + * @description: + * A macro that returns true whenever a face object contains + * tables for color glyphs. + * + */ +#define FT_HAS_COLOR( face ) \ + ( face->face_flags & FT_FACE_FLAG_COLOR ) + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* FT_STYLE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used to indicate the style of a given face. */ + /* These are used in the `style_flags' field of @FT_FaceRec. */ + /* */ + /* <Values> */ + /* FT_STYLE_FLAG_ITALIC :: */ + /* Indicates that a given face style is italic or oblique. */ + /* */ + /* FT_STYLE_FLAG_BOLD :: */ + /* Indicates that a given face is bold. */ + /* */ + /* <Note> */ + /* The style information as provided by FreeType is very basic. More */ + /* details are beyond the scope and should be done on a higher level */ + /* (for example, by analyzing various fields of the `OS/2' table in */ + /* SFNT based fonts). */ + /* */ +#define FT_STYLE_FLAG_ITALIC ( 1 << 0 ) +#define FT_STYLE_FLAG_BOLD ( 1 << 1 ) + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Size_InternalRec' structure, used to */ + /* model private data of a given @FT_Size object. */ + /* */ + typedef struct FT_Size_InternalRec_* FT_Size_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_Metrics */ + /* */ + /* <Description> */ + /* The size metrics structure gives the metrics of a size object. */ + /* */ + /* <Fields> */ + /* x_ppem :: The width of the scaled EM square in pixels, hence */ + /* the term `ppem' (pixels per EM). It is also */ + /* referred to as `nominal width'. */ + /* */ + /* y_ppem :: The height of the scaled EM square in pixels, */ + /* hence the term `ppem' (pixels per EM). It is also */ + /* referred to as `nominal height'. */ + /* */ + /* x_scale :: A 16.16 fractional scaling value used to convert */ + /* horizontal metrics from font units to 26.6 */ + /* fractional pixels. Only relevant for scalable */ + /* font formats. */ + /* */ + /* y_scale :: A 16.16 fractional scaling value used to convert */ + /* vertical metrics from font units to 26.6 */ + /* fractional pixels. Only relevant for scalable */ + /* font formats. */ + /* */ + /* ascender :: The ascender in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* descender :: The descender in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* height :: The height in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* max_advance :: The maximum advance width in 26.6 fractional */ + /* pixels. See @FT_FaceRec for the details. */ + /* */ + /* <Note> */ + /* The scaling values, if relevant, are determined first during a */ + /* size changing operation. The remaining fields are then set by the */ + /* driver. For scalable formats, they are usually set to scaled */ + /* values of the corresponding fields in @FT_FaceRec. */ + /* */ + /* Note that due to glyph hinting, these values might not be exact */ + /* for certain fonts. Thus they must be treated as unreliable */ + /* with an error margin of at least one pixel! */ + /* */ + /* Indeed, the only way to get the exact metrics is to render _all_ */ + /* glyphs. As this would be a definite performance hit, it is up to */ + /* client applications to perform such computations. */ + /* */ + /* The FT_Size_Metrics structure is valid for bitmap fonts also. */ + /* */ + typedef struct FT_Size_Metrics_ + { + FT_UShort x_ppem; /* horizontal pixels per EM */ + FT_UShort y_ppem; /* vertical pixels per EM */ + + FT_Fixed x_scale; /* scaling values used to convert font */ + FT_Fixed y_scale; /* units to 26.6 fractional pixels */ + + FT_Pos ascender; /* ascender in 26.6 frac. pixels */ + FT_Pos descender; /* descender in 26.6 frac. pixels */ + FT_Pos height; /* text height in 26.6 frac. pixels */ + FT_Pos max_advance; /* max horizontal advance, in 26.6 pixels */ + + } FT_Size_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SizeRec */ + /* */ + /* <Description> */ + /* FreeType root size class structure. A size object models a face */ + /* object at a given size. */ + /* */ + /* <Fields> */ + /* face :: Handle to the parent face object. */ + /* */ + /* generic :: A typeless pointer, unused by the FreeType library or */ + /* any of its drivers. It can be used by client */ + /* applications to link their own data to each size */ + /* object. */ + /* */ + /* metrics :: Metrics for this size object. This field is read-only. */ + /* */ + typedef struct FT_SizeRec_ + { + FT_Face face; /* parent face object */ + FT_Generic generic; /* generic pointer for client uses */ + FT_Size_Metrics metrics; /* size metrics */ + FT_Size_Internal internal; + + } FT_SizeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SubGlyph */ + /* */ + /* <Description> */ + /* The subglyph structure is an internal object used to describe */ + /* subglyphs (for example, in the case of composites). */ + /* */ + /* <Note> */ + /* The subglyph implementation is not part of the high-level API, */ + /* hence the forward structure declaration. */ + /* */ + /* You can however retrieve subglyph information with */ + /* @FT_Get_SubGlyph_Info. */ + /* */ + typedef struct FT_SubGlyphRec_* FT_SubGlyph; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Slot_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Slot_InternalRec' structure, used to */ + /* model private data of a given @FT_GlyphSlot object. */ + /* */ + typedef struct FT_Slot_InternalRec_* FT_Slot_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphSlotRec */ + /* */ + /* <Description> */ + /* FreeType root glyph slot class structure. A glyph slot is a */ + /* container where individual glyphs can be loaded, be they in */ + /* outline or bitmap format. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library instance */ + /* this slot belongs to. */ + /* */ + /* face :: A handle to the parent face object. */ + /* */ + /* next :: In some cases (like some font tools), several */ + /* glyph slots per face object can be a good */ + /* thing. As this is rare, the glyph slots are */ + /* listed through a direct, single-linked list */ + /* using its `next' field. */ + /* */ + /* generic :: A typeless pointer unused by the FreeType */ + /* library or any of its drivers. It can be */ + /* used by client applications to link their own */ + /* data to each glyph slot object. */ + /* */ + /* metrics :: The metrics of the last loaded glyph in the */ + /* slot. The returned values depend on the last */ + /* load flags (see the @FT_Load_Glyph API */ + /* function) and can be expressed either in 26.6 */ + /* fractional pixels or font units. */ + /* */ + /* Note that even when the glyph image is */ + /* transformed, the metrics are not. */ + /* */ + /* linearHoriAdvance :: The advance width of the unhinted glyph. */ + /* Its value is expressed in 16.16 fractional */ + /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */ + /* when loading the glyph. This field can be */ + /* important to perform correct WYSIWYG layout. */ + /* Only relevant for outline glyphs. */ + /* */ + /* linearVertAdvance :: The advance height of the unhinted glyph. */ + /* Its value is expressed in 16.16 fractional */ + /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */ + /* when loading the glyph. This field can be */ + /* important to perform correct WYSIWYG layout. */ + /* Only relevant for outline glyphs. */ + /* */ + /* advance :: This shorthand is, depending on */ + /* @FT_LOAD_IGNORE_TRANSFORM, the transformed */ + /* (hinted) advance width for the glyph, in 26.6 */ + /* fractional pixel format. As specified with */ + /* @FT_LOAD_VERTICAL_LAYOUT, it uses either the */ + /* `horiAdvance' or the `vertAdvance' value of */ + /* `metrics' field. */ + /* */ + /* format :: This field indicates the format of the image */ + /* contained in the glyph slot. Typically */ + /* @FT_GLYPH_FORMAT_BITMAP, */ + /* @FT_GLYPH_FORMAT_OUTLINE, or */ + /* @FT_GLYPH_FORMAT_COMPOSITE, but others are */ + /* possible. */ + /* */ + /* bitmap :: This field is used as a bitmap descriptor */ + /* when the slot format is */ + /* @FT_GLYPH_FORMAT_BITMAP. Note that the */ + /* address and content of the bitmap buffer can */ + /* change between calls of @FT_Load_Glyph and a */ + /* few other functions. */ + /* */ + /* bitmap_left :: The bitmap's left bearing expressed in */ + /* integer pixels. Only valid if the format is */ + /* @FT_GLYPH_FORMAT_BITMAP, this is, if the */ + /* glyph slot contains a bitmap. */ + /* */ + /* bitmap_top :: The bitmap's top bearing expressed in integer */ + /* pixels. Remember that this is the distance */ + /* from the baseline to the top-most glyph */ + /* scanline, upwards y~coordinates being */ + /* *positive*. */ + /* */ + /* outline :: The outline descriptor for the current glyph */ + /* image if its format is */ + /* @FT_GLYPH_FORMAT_OUTLINE. Once a glyph is */ + /* loaded, `outline' can be transformed, */ + /* distorted, embolded, etc. However, it must */ + /* not be freed. */ + /* */ + /* num_subglyphs :: The number of subglyphs in a composite glyph. */ + /* This field is only valid for the composite */ + /* glyph format that should normally only be */ + /* loaded with the @FT_LOAD_NO_RECURSE flag. */ + /* */ + /* subglyphs :: An array of subglyph descriptors for */ + /* composite glyphs. There are `num_subglyphs' */ + /* elements in there. Currently internal to */ + /* FreeType. */ + /* */ + /* control_data :: Certain font drivers can also return the */ + /* control data for a given glyph image (e.g. */ + /* TrueType bytecode, Type~1 charstrings, etc.). */ + /* This field is a pointer to such data. */ + /* */ + /* control_len :: This is the length in bytes of the control */ + /* data. */ + /* */ + /* other :: Really wicked formats can use this pointer to */ + /* present their own glyph image to client */ + /* applications. Note that the application */ + /* needs to know about the image format. */ + /* */ + /* lsb_delta :: The difference between hinted and unhinted */ + /* left side bearing while auto-hinting is */ + /* active. Zero otherwise. */ + /* */ + /* rsb_delta :: The difference between hinted and unhinted */ + /* right side bearing while auto-hinting is */ + /* active. Zero otherwise. */ + /* */ + /* <Note> */ + /* If @FT_Load_Glyph is called with default flags (see */ + /* @FT_LOAD_DEFAULT) the glyph image is loaded in the glyph slot in */ + /* its native format (e.g., an outline glyph for TrueType and Type~1 */ + /* formats). */ + /* */ + /* This image can later be converted into a bitmap by calling */ + /* @FT_Render_Glyph. This function finds the current renderer for */ + /* the native image's format, then invokes it. */ + /* */ + /* The renderer is in charge of transforming the native image through */ + /* the slot's face transformation fields, then converting it into a */ + /* bitmap that is returned in `slot->bitmap'. */ + /* */ + /* Note that `slot->bitmap_left' and `slot->bitmap_top' are also used */ + /* to specify the position of the bitmap relative to the current pen */ + /* position (e.g., coordinates (0,0) on the baseline). Of course, */ + /* `slot->format' is also changed to @FT_GLYPH_FORMAT_BITMAP. */ + /* */ + /* <Note> */ + /* Here is a small pseudo code fragment that shows how to use */ + /* `lsb_delta' and `rsb_delta': */ + /* */ + /* { */ + /* FT_Pos origin_x = 0; */ + /* FT_Pos prev_rsb_delta = 0; */ + /* */ + /* */ + /* for all glyphs do */ + /* <compute kern between current and previous glyph and add it to */ + /* `origin_x'> */ + /* */ + /* <load glyph with `FT_Load_Glyph'> */ + /* */ + /* if ( prev_rsb_delta - face->glyph->lsb_delta >= 32 ) */ + /* origin_x -= 64; */ + /* else if ( prev_rsb_delta - face->glyph->lsb_delta < -32 ) */ + /* origin_x += 64; */ + /* */ + /* prev_rsb_delta = face->glyph->rsb_delta; */ + /* */ + /* <save glyph image, or render glyph, or ...> */ + /* */ + /* origin_x += face->glyph->advance.x; */ + /* endfor */ + /* } */ + /* */ + typedef struct FT_GlyphSlotRec_ + { + FT_Library library; + FT_Face face; + FT_GlyphSlot next; + FT_UInt reserved; /* retained for binary compatibility */ + FT_Generic generic; + + FT_Glyph_Metrics metrics; + FT_Fixed linearHoriAdvance; + FT_Fixed linearVertAdvance; + FT_Vector advance; + + FT_Glyph_Format format; + + FT_Bitmap bitmap; + FT_Int bitmap_left; + FT_Int bitmap_top; + + FT_Outline outline; + + FT_UInt num_subglyphs; + FT_SubGlyph subglyphs; + + void* control_data; + long control_len; + + FT_Pos lsb_delta; + FT_Pos rsb_delta; + + void* other; + + FT_Slot_Internal internal; + + } FT_GlyphSlotRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* F U N C T I O N S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Init_FreeType */ + /* */ + /* <Description> */ + /* Initialize a new FreeType library object. The set of modules */ + /* that are registered by this function is determined at build time. */ + /* */ + /* <Output> */ + /* alibrary :: A handle to a new library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* In case you want to provide your own memory allocating routines, */ + /* use @FT_New_Library instead, followed by a call to */ + /* @FT_Add_Default_Modules (or a series of calls to @FT_Add_Module). */ + /* */ + /* See the documentation of @FT_Library and @FT_Face for */ + /* multi-threading issues. */ + /* */ + /* If you need reference-counting (cf. @FT_Reference_Library), use */ + /* @FT_New_Library and @FT_Done_Library. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Init_FreeType( FT_Library *alibrary ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_FreeType */ + /* */ + /* <Description> */ + /* Destroy a given FreeType library object and all of its children, */ + /* including resources, drivers, faces, sizes, etc. */ + /* */ + /* <Input> */ + /* library :: A handle to the target library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_FreeType( FT_Library library ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OPEN_XXX */ + /* */ + /* <Description> */ + /* A list of bit field constants used within the `flags' field of the */ + /* @FT_Open_Args structure. */ + /* */ + /* <Values> */ + /* FT_OPEN_MEMORY :: This is a memory-based stream. */ + /* */ + /* FT_OPEN_STREAM :: Copy the stream from the `stream' field. */ + /* */ + /* FT_OPEN_PATHNAME :: Create a new input stream from a C~path */ + /* name. */ + /* */ + /* FT_OPEN_DRIVER :: Use the `driver' field. */ + /* */ + /* FT_OPEN_PARAMS :: Use the `num_params' and `params' fields. */ + /* */ + /* <Note> */ + /* The `FT_OPEN_MEMORY', `FT_OPEN_STREAM', and `FT_OPEN_PATHNAME' */ + /* flags are mutually exclusive. */ + /* */ +#define FT_OPEN_MEMORY 0x1 +#define FT_OPEN_STREAM 0x2 +#define FT_OPEN_PATHNAME 0x4 +#define FT_OPEN_DRIVER 0x8 +#define FT_OPEN_PARAMS 0x10 + + + /* these constants are deprecated; use the corresponding `FT_OPEN_XXX' */ + /* values instead */ +#define ft_open_memory FT_OPEN_MEMORY +#define ft_open_stream FT_OPEN_STREAM +#define ft_open_pathname FT_OPEN_PATHNAME +#define ft_open_driver FT_OPEN_DRIVER +#define ft_open_params FT_OPEN_PARAMS + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Parameter */ + /* */ + /* <Description> */ + /* A simple structure used to pass more or less generic parameters to */ + /* @FT_Open_Face. */ + /* */ + /* <Fields> */ + /* tag :: A four-byte identification tag. */ + /* */ + /* data :: A pointer to the parameter data. */ + /* */ + /* <Note> */ + /* The ID and function of parameters are driver-specific. See the */ + /* various FT_PARAM_TAG_XXX flags for more information. */ + /* */ + typedef struct FT_Parameter_ + { + FT_ULong tag; + FT_Pointer data; + + } FT_Parameter; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Open_Args */ + /* */ + /* <Description> */ + /* A structure used to indicate how to open a new font file or */ + /* stream. A pointer to such a structure can be used as a parameter */ + /* for the functions @FT_Open_Face and @FT_Attach_Stream. */ + /* */ + /* <Fields> */ + /* flags :: A set of bit flags indicating how to use the */ + /* structure. */ + /* */ + /* memory_base :: The first byte of the file in memory. */ + /* */ + /* memory_size :: The size in bytes of the file in memory. */ + /* */ + /* pathname :: A pointer to an 8-bit file pathname. */ + /* */ + /* stream :: A handle to a source stream object. */ + /* */ + /* driver :: This field is exclusively used by @FT_Open_Face; */ + /* it simply specifies the font driver to use to open */ + /* the face. If set to~0, FreeType tries to load the */ + /* face with each one of the drivers in its list. */ + /* */ + /* num_params :: The number of extra parameters. */ + /* */ + /* params :: Extra parameters passed to the font driver when */ + /* opening a new face. */ + /* */ + /* <Note> */ + /* The stream type is determined by the contents of `flags' that */ + /* are tested in the following order by @FT_Open_Face: */ + /* */ + /* If the @FT_OPEN_MEMORY bit is set, assume that this is a */ + /* memory file of `memory_size' bytes, located at `memory_address'. */ + /* The data are are not copied, and the client is responsible for */ + /* releasing and destroying them _after_ the corresponding call to */ + /* @FT_Done_Face. */ + /* */ + /* Otherwise, if the @FT_OPEN_STREAM bit is set, assume that a */ + /* custom input stream `stream' is used. */ + /* */ + /* Otherwise, if the @FT_OPEN_PATHNAME bit is set, assume that this */ + /* is a normal file and use `pathname' to open it. */ + /* */ + /* If the @FT_OPEN_DRIVER bit is set, @FT_Open_Face only tries to */ + /* open the file with the driver whose handler is in `driver'. */ + /* */ + /* If the @FT_OPEN_PARAMS bit is set, the parameters given by */ + /* `num_params' and `params' is used. They are ignored otherwise. */ + /* */ + /* Ideally, both the `pathname' and `params' fields should be tagged */ + /* as `const'; this is missing for API backwards compatibility. In */ + /* other words, applications should treat them as read-only. */ + /* */ + typedef struct FT_Open_Args_ + { + FT_UInt flags; + const FT_Byte* memory_base; + FT_Long memory_size; + FT_String* pathname; + FT_Stream stream; + FT_Module driver; + FT_Int num_params; + FT_Parameter* params; + + } FT_Open_Args; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face */ + /* */ + /* <Description> */ + /* This function calls @FT_Open_Face to open a font by its pathname. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* pathname :: A path to the font file. */ + /* */ + /* face_index :: See @FT_Open_Face for a detailed description of this */ + /* parameter. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Use @FT_Done_Face to destroy the created @FT_Face object (along */ + /* with its slot and sizes). */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face( FT_Library library, + const char* filepathname, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Memory_Face */ + /* */ + /* <Description> */ + /* This function calls @FT_Open_Face to open a font that has been */ + /* loaded into memory. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* file_base :: A pointer to the beginning of the font data. */ + /* */ + /* file_size :: The size of the memory chunk used by the font data. */ + /* */ + /* face_index :: See @FT_Open_Face for a detailed description of this */ + /* parameter. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You must not deallocate the memory before calling @FT_Done_Face. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Memory_Face( FT_Library library, + const FT_Byte* file_base, + FT_Long file_size, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Open_Face */ + /* */ + /* <Description> */ + /* Create a face object from a given resource described by */ + /* @FT_Open_Args. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* args :: A pointer to an `FT_Open_Args' structure that must */ + /* be filled by the caller. */ + /* */ + /* face_index :: This field holds two different values. Bits 0-15 */ + /* are the index of the face in the font file (starting */ + /* with value~0). Set it to~0 if there is only one */ + /* face in the font file. */ + /* */ + /* Bits 16-30 are relevant to GX variation fonts only, */ + /* specifying the named instance index for the current */ + /* face index (starting with value~1; value~0 makes */ + /* FreeType ignore named instances). For non-GX fonts, */ + /* bits 16-30 are ignored. Assuming that you want to */ + /* access the third named instance in face~4, */ + /* `face_index' should be set to 0x00030004. If you */ + /* want to access face~4 without GX variation handling, */ + /* simply set `face_index' to value~4. */ + /* */ + /* FT_Open_Face and its siblings can be used to quickly */ + /* check whether the font format of a given font */ + /* resource is supported by FreeType. In general, if */ + /* the `face_index' argument is negative, the */ + /* function's return value is~0 if the font format is */ + /* recognized, or non-zero otherwise. The function */ + /* allocates a more or less empty face handle in */ + /* `*aface' (if `aface' isn't NULL); the only two */ + /* useful fields in this special case are */ + /* `face->num_faces' and `face->style_flags'. For any */ + /* negative value of `face_index', `face->num_faces' */ + /* gives the number of faces within the font file. For */ + /* the negative value `-(N+1)' (with `N' a 16-bit */ + /* value), bits 16-30 in `face->style_flags' give the */ + /* number of named instances in face `N' if we have a */ + /* GX variation font (or zero otherwise). After */ + /* examination, the returned @FT_Face structure should */ + /* be deallocated with a call to @FT_Done_Face. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Unlike FreeType 1.x, this function automatically creates a glyph */ + /* slot for the face object that can be accessed directly through */ + /* `face->glyph'. */ + /* */ + /* Each new face object created with this function also owns a */ + /* default @FT_Size object, accessible as `face->size'. */ + /* */ + /* One @FT_Library instance can have multiple face objects, this is, */ + /* @FT_Open_Face and its siblings can be called multiple times using */ + /* the same `library' argument. */ + /* */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Face. */ + /* */ + /* To loop over all faces, use code similar to the following snippet */ + /* (omitting the error handling). */ + /* */ + /* { */ + /* ... */ + /* FT_Face face; */ + /* FT_Long i, num_faces; */ + /* */ + /* */ + /* error = FT_Open_Face( library, args, -1, &face ); */ + /* if ( error ) { ... } */ + /* */ + /* num_faces = face->num_faces; */ + /* FT_Done_Face( face ); */ + /* */ + /* for ( i = 0; i < num_faces; i++ ) */ + /* { */ + /* ... */ + /* error = FT_Open_Face( library, args, i, &face ); */ + /* ... */ + /* FT_Done_Face( face ); */ + /* ... */ + /* } */ + /* } */ + /* */ + /* To loop over all valid values for `face_index', use something */ + /* similar to the following snippet, again without error handling. */ + /* The code accesses all faces immediately (thus only a single call */ + /* of `FT_Open_Face' within the do-loop), with and without named */ + /* instances. */ + /* */ + /* { */ + /* ... */ + /* FT_Face face; */ + /* */ + /* FT_Long num_faces = 0; */ + /* FT_Long num_instances = 0; */ + /* */ + /* FT_Long face_idx = 0; */ + /* FT_Long instance_idx = 0; */ + /* */ + /* */ + /* do */ + /* { */ + /* FT_Long id = ( instance_idx << 16 ) + face_idx; */ + /* */ + /* */ + /* error = FT_Open_Face( library, args, id, &face ); */ + /* if ( error ) { ... } */ + /* */ + /* num_faces = face->num_faces; */ + /* num_instances = face->style_flags >> 16; */ + /* */ + /* ... */ + /* */ + /* FT_Done_Face( face ); */ + /* */ + /* if ( instance_idx < num_instances ) */ + /* instance_idx++; */ + /* else */ + /* { */ + /* face_idx++; */ + /* instance_idx = 0; */ + /* } */ + /* */ + /* } while ( face_idx < num_faces ) */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_Open_Face( FT_Library library, + const FT_Open_Args* args, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_File */ + /* */ + /* <Description> */ + /* This function calls @FT_Attach_Stream to attach a file. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* filepathname :: The pathname. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_File( FT_Face face, + const char* filepathname ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_Stream */ + /* */ + /* <Description> */ + /* `Attach' data to a face object. Normally, this is used to read */ + /* additional information for the face object. For example, you can */ + /* attach an AFM file that comes with a Type~1 font to get the */ + /* kerning values and other metrics. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* parameters :: A pointer to @FT_Open_Args that must be filled by */ + /* the caller. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The meaning of the `attach' (i.e., what really happens when the */ + /* new file is read) is not fixed by FreeType itself. It really */ + /* depends on the font format (and thus the font driver). */ + /* */ + /* Client applications are expected to know what they are doing */ + /* when invoking this function. Most drivers simply do not implement */ + /* file attachments. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_Stream( FT_Face face, + FT_Open_Args* parameters ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Reference_Face */ + /* */ + /* <Description> */ + /* A counter gets initialized to~1 at the time an @FT_Face structure */ + /* is created. This function increments the counter. @FT_Done_Face */ + /* then only destroys a face if the counter is~1, otherwise it simply */ + /* decrements the counter. */ + /* */ + /* This function helps in managing life-cycles of structures that */ + /* reference @FT_Face objects. */ + /* */ + /* <Input> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Since> */ + /* 2.4.2 */ + /* */ + FT_EXPORT( FT_Error ) + FT_Reference_Face( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Face */ + /* */ + /* <Description> */ + /* Discard a given face object, as well as all of its child slots and */ + /* sizes. */ + /* */ + /* <Input> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Face. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Face( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Select_Size */ + /* */ + /* <Description> */ + /* Select a bitmap strike. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* strike_index :: The index of the bitmap strike in the */ + /* `available_sizes' field of @FT_FaceRec structure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Select_Size( FT_Face face, + FT_Int strike_index ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Size_Request_Type */ + /* */ + /* <Description> */ + /* An enumeration type that lists the supported size request types. */ + /* */ + /* <Values> */ + /* FT_SIZE_REQUEST_TYPE_NOMINAL :: */ + /* The nominal size. The `units_per_EM' field of @FT_FaceRec is */ + /* used to determine both scaling values. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_REAL_DIM :: */ + /* The real dimension. The sum of the the `ascender' and (minus */ + /* of) the `descender' fields of @FT_FaceRec are used to determine */ + /* both scaling values. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_BBOX :: */ + /* The font bounding box. The width and height of the `bbox' field */ + /* of @FT_FaceRec are used to determine the horizontal and vertical */ + /* scaling value, respectively. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_CELL :: */ + /* The `max_advance_width' field of @FT_FaceRec is used to */ + /* determine the horizontal scaling value; the vertical scaling */ + /* value is determined the same way as */ + /* @FT_SIZE_REQUEST_TYPE_REAL_DIM does. Finally, both scaling */ + /* values are set to the smaller one. This type is useful if you */ + /* want to specify the font size for, say, a window of a given */ + /* dimension and 80x24 cells. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_SCALES :: */ + /* Specify the scaling values directly. */ + /* */ + /* <Note> */ + /* The above descriptions only apply to scalable formats. For bitmap */ + /* formats, the behaviour is up to the driver. */ + /* */ + /* See the note section of @FT_Size_Metrics if you wonder how size */ + /* requesting relates to scaling values. */ + /* */ + typedef enum FT_Size_Request_Type_ + { + FT_SIZE_REQUEST_TYPE_NOMINAL, + FT_SIZE_REQUEST_TYPE_REAL_DIM, + FT_SIZE_REQUEST_TYPE_BBOX, + FT_SIZE_REQUEST_TYPE_CELL, + FT_SIZE_REQUEST_TYPE_SCALES, + + FT_SIZE_REQUEST_TYPE_MAX + + } FT_Size_Request_Type; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_RequestRec */ + /* */ + /* <Description> */ + /* A structure used to model a size request. */ + /* */ + /* <Fields> */ + /* type :: See @FT_Size_Request_Type. */ + /* */ + /* width :: The desired width. */ + /* */ + /* height :: The desired height. */ + /* */ + /* horiResolution :: The horizontal resolution. If set to zero, */ + /* `width' is treated as a 26.6 fractional pixel */ + /* value. */ + /* */ + /* vertResolution :: The vertical resolution. If set to zero, */ + /* `height' is treated as a 26.6 fractional pixel */ + /* value. */ + /* */ + /* <Note> */ + /* If `width' is zero, then the horizontal scaling value is set equal */ + /* to the vertical scaling value, and vice versa. */ + /* */ + typedef struct FT_Size_RequestRec_ + { + FT_Size_Request_Type type; + FT_Long width; + FT_Long height; + FT_UInt horiResolution; + FT_UInt vertResolution; + + } FT_Size_RequestRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_Request */ + /* */ + /* <Description> */ + /* A handle to a size request structure. */ + /* */ + typedef struct FT_Size_RequestRec_ *FT_Size_Request; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Request_Size */ + /* */ + /* <Description> */ + /* Resize the scale of the active @FT_Size object in a face. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* req :: A pointer to a @FT_Size_RequestRec. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Although drivers may select the bitmap strike matching the */ + /* request, you should not rely on this if you intend to select a */ + /* particular bitmap strike. Use @FT_Select_Size instead in that */ + /* case. */ + /* */ + /* The relation between the requested size and the resulting glyph */ + /* size is dependent entirely on how the size is defined in the */ + /* source face. The font designer chooses the final size of each */ + /* glyph relative to this size. For more information refer to */ + /* `http://www.freetype.org/freetype2/docs/glyphs/glyphs-2.html' */ + /* */ + /* Don't use this function if you are using the FreeType cache API. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Request_Size( FT_Face face, + FT_Size_Request req ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Char_Size */ + /* */ + /* <Description> */ + /* This function calls @FT_Request_Size to request the nominal size */ + /* (in points). */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* char_width :: The nominal width, in 26.6 fractional points. */ + /* */ + /* char_height :: The nominal height, in 26.6 fractional points. */ + /* */ + /* horz_resolution :: The horizontal resolution in dpi. */ + /* */ + /* vert_resolution :: The vertical resolution in dpi. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If either the character width or height is zero, it is set equal */ + /* to the other value. */ + /* */ + /* If either the horizontal or vertical resolution is zero, it is set */ + /* equal to the other value. */ + /* */ + /* A character width or height smaller than 1pt is set to 1pt; if */ + /* both resolution values are zero, they are set to 72dpi. */ + /* */ + /* Don't use this function if you are using the FreeType cache API. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Char_Size( FT_Face face, + FT_F26Dot6 char_width, + FT_F26Dot6 char_height, + FT_UInt horz_resolution, + FT_UInt vert_resolution ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Pixel_Sizes */ + /* */ + /* <Description> */ + /* This function calls @FT_Request_Size to request the nominal size */ + /* (in pixels). */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* pixel_width :: The nominal width, in pixels. */ + /* */ + /* pixel_height :: The nominal height, in pixels. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You should not rely on the resulting glyphs matching, or being */ + /* constrained, to this pixel size. Refer to @FT_Request_Size to */ + /* understand how requested sizes relate to actual sizes. */ + /* */ + /* Don't use this function if you are using the FreeType cache API. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Pixel_Sizes( FT_Face face, + FT_UInt pixel_width, + FT_UInt pixel_height ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Glyph */ + /* */ + /* <Description> */ + /* A function used to load a single glyph into the glyph slot of a */ + /* face object. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object where the glyph */ + /* is loaded. */ + /* */ + /* <Input> */ + /* glyph_index :: The index of the glyph in the font file. For */ + /* CID-keyed fonts (either in PS or in CFF format) */ + /* this argument specifies the CID value. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The loaded glyph may be transformed. See @FT_Set_Transform for */ + /* the details. */ + /* */ + /* For subsetted CID-keyed fonts, `FT_Err_Invalid_Argument' is */ + /* returned for invalid CID values (this is, for CID values that */ + /* don't have a corresponding glyph in the font). See the discussion */ + /* of the @FT_FACE_FLAG_CID_KEYED flag for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Glyph( FT_Face face, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Char */ + /* */ + /* <Description> */ + /* A function used to load a single glyph into the glyph slot of a */ + /* face object, according to its character code. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object where the glyph */ + /* is loaded. */ + /* */ + /* <Input> */ + /* char_code :: The glyph's character code, according to the */ + /* current charmap used in the face. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function simply calls @FT_Get_Char_Index and @FT_Load_Glyph. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Char( FT_Face face, + FT_ULong char_code, + FT_Int32 load_flags ); + + + /************************************************************************* + * + * @enum: + * FT_LOAD_XXX + * + * @description: + * A list of bit field constants used with @FT_Load_Glyph to indicate + * what kind of operations to perform during glyph loading. + * + * @values: + * FT_LOAD_DEFAULT :: + * Corresponding to~0, this value is used as the default glyph load + * operation. In this case, the following happens: + * + * 1. FreeType looks for a bitmap for the glyph corresponding to the + * face's current size. If one is found, the function returns. + * The bitmap data can be accessed from the glyph slot (see note + * below). + * + * 2. If no embedded bitmap is searched or found, FreeType looks for a + * scalable outline. If one is found, it is loaded from the font + * file, scaled to device pixels, then `hinted' to the pixel grid + * in order to optimize it. The outline data can be accessed from + * the glyph slot (see note below). + * + * Note that by default, the glyph loader doesn't render outlines into + * bitmaps. The following flags are used to modify this default + * behaviour to more specific and useful cases. + * + * FT_LOAD_NO_SCALE :: + * Don't scale the loaded outline glyph but keep it in font units. + * + * This flag implies @FT_LOAD_NO_HINTING and @FT_LOAD_NO_BITMAP, and + * unsets @FT_LOAD_RENDER. + * + * If the font is `tricky' (see @FT_FACE_FLAG_TRICKY for more), using + * FT_LOAD_NO_SCALE usually yields meaningless outlines because the + * subglyphs must be scaled and positioned with hinting instructions. + * This can be solved by loading the font without FT_LOAD_NO_SCALE and + * setting the character size to `font->units_per_EM'. + * + * FT_LOAD_NO_HINTING :: + * Disable hinting. This generally generates `blurrier' bitmap glyphs + * when the glyph are rendered in any of the anti-aliased modes. See + * also the note below. + * + * This flag is implied by @FT_LOAD_NO_SCALE. + * + * FT_LOAD_RENDER :: + * Call @FT_Render_Glyph after the glyph is loaded. By default, the + * glyph is rendered in @FT_RENDER_MODE_NORMAL mode. This can be + * overridden by @FT_LOAD_TARGET_XXX or @FT_LOAD_MONOCHROME. + * + * This flag is unset by @FT_LOAD_NO_SCALE. + * + * FT_LOAD_NO_BITMAP :: + * Ignore bitmap strikes when loading. Bitmap-only fonts ignore this + * flag. + * + * @FT_LOAD_NO_SCALE always sets this flag. + * + * FT_LOAD_VERTICAL_LAYOUT :: + * Load the glyph for vertical text layout. In particular, the + * `advance' value in the @FT_GlyphSlotRec structure is set to the + * `vertAdvance' value of the `metrics' field. + * + * In case @FT_HAS_VERTICAL doesn't return true, you shouldn't use + * this flag currently. Reason is that in this case vertical metrics + * get synthesized, and those values are not always consistent across + * various font formats. + * + * FT_LOAD_FORCE_AUTOHINT :: + * Indicates that the auto-hinter is preferred over the font's native + * hinter. See also the note below. + * + * FT_LOAD_PEDANTIC :: + * Indicates that the font driver should perform pedantic verifications + * during glyph loading. This is mostly used to detect broken glyphs + * in fonts. By default, FreeType tries to handle broken fonts also. + * + * In particular, errors from the TrueType bytecode engine are not + * passed to the application if this flag is not set; this might + * result in partially hinted or distorted glyphs in case a glyph's + * bytecode is buggy. + * + * FT_LOAD_NO_RECURSE :: + * Indicate that the font driver should not load composite glyphs + * recursively. Instead, it should set the `num_subglyph' and + * `subglyphs' values of the glyph slot accordingly, and set + * `glyph->format' to @FT_GLYPH_FORMAT_COMPOSITE. The description of + * subglyphs can then be accessed with @FT_Get_SubGlyph_Info. + * + * This flag implies @FT_LOAD_NO_SCALE and @FT_LOAD_IGNORE_TRANSFORM. + * + * FT_LOAD_IGNORE_TRANSFORM :: + * Indicates that the transform matrix set by @FT_Set_Transform should + * be ignored. + * + * FT_LOAD_MONOCHROME :: + * This flag is used with @FT_LOAD_RENDER to indicate that you want to + * render an outline glyph to a 1-bit monochrome bitmap glyph, with + * 8~pixels packed into each byte of the bitmap data. + * + * Note that this has no effect on the hinting algorithm used. You + * should rather use @FT_LOAD_TARGET_MONO so that the + * monochrome-optimized hinting algorithm is used. + * + * FT_LOAD_LINEAR_DESIGN :: + * Indicates that the `linearHoriAdvance' and `linearVertAdvance' + * fields of @FT_GlyphSlotRec should be kept in font units. See + * @FT_GlyphSlotRec for details. + * + * FT_LOAD_NO_AUTOHINT :: + * Disable auto-hinter. See also the note below. + * + * FT_LOAD_COLOR :: + * This flag is used to request loading of color embedded-bitmap + * images. The resulting color bitmaps, if available, will have the + * @FT_PIXEL_MODE_BGRA format. When the flag is not used and color + * bitmaps are found, they will be converted to 256-level gray + * bitmaps transparently. Those bitmaps will be in the + * @FT_PIXEL_MODE_GRAY format. + * + * FT_LOAD_COMPUTE_METRICS :: + * This flag sets computing glyph metrics without the use of bundled + * metrics tables (for example, the `hdmx' table in TrueType fonts). + * Well-behaving fonts have optimized bundled metrics and these should + * be used. This flag is mainly used by font validating or font + * editing applications, which need to ignore, verify, or edit those + * tables. + * + * Currently, this flag is only implemented for TrueType fonts. + * + * FT_LOAD_CROP_BITMAP :: + * Ignored. Deprecated. + * + * FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH :: + * Ignored. Deprecated. + * + * @note: + * By default, hinting is enabled and the font's native hinter (see + * @FT_FACE_FLAG_HINTER) is preferred over the auto-hinter. You can + * disable hinting by setting @FT_LOAD_NO_HINTING or change the + * precedence by setting @FT_LOAD_FORCE_AUTOHINT. You can also set + * @FT_LOAD_NO_AUTOHINT in case you don't want the auto-hinter to be + * used at all. + * + * See the description of @FT_FACE_FLAG_TRICKY for a special exception + * (affecting only a handful of Asian fonts). + * + * Besides deciding which hinter to use, you can also decide which + * hinting algorithm to use. See @FT_LOAD_TARGET_XXX for details. + * + * Note that the auto-hinter needs a valid Unicode cmap (either a native + * one or synthesized by FreeType) for producing correct results. If a + * font provides an incorrect mapping (for example, assigning the + * character code U+005A, LATIN CAPITAL LETTER Z, to a glyph depicting a + * mathematical integral sign), the auto-hinter might produce useless + * results. + * + */ +#define FT_LOAD_DEFAULT 0x0 +#define FT_LOAD_NO_SCALE ( 1L << 0 ) +#define FT_LOAD_NO_HINTING ( 1L << 1 ) +#define FT_LOAD_RENDER ( 1L << 2 ) +#define FT_LOAD_NO_BITMAP ( 1L << 3 ) +#define FT_LOAD_VERTICAL_LAYOUT ( 1L << 4 ) +#define FT_LOAD_FORCE_AUTOHINT ( 1L << 5 ) +#define FT_LOAD_CROP_BITMAP ( 1L << 6 ) +#define FT_LOAD_PEDANTIC ( 1L << 7 ) +#define FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ( 1L << 9 ) +#define FT_LOAD_NO_RECURSE ( 1L << 10 ) +#define FT_LOAD_IGNORE_TRANSFORM ( 1L << 11 ) +#define FT_LOAD_MONOCHROME ( 1L << 12 ) +#define FT_LOAD_LINEAR_DESIGN ( 1L << 13 ) +#define FT_LOAD_NO_AUTOHINT ( 1L << 15 ) + /* Bits 16..19 are used by `FT_LOAD_TARGET_' */ +#define FT_LOAD_COLOR ( 1L << 20 ) +#define FT_LOAD_COMPUTE_METRICS ( 1L << 21 ) + + /* */ + + /* used internally only by certain font drivers! */ +#define FT_LOAD_ADVANCE_ONLY ( 1L << 8 ) +#define FT_LOAD_SBITS_ONLY ( 1L << 14 ) + + + /************************************************************************** + * + * @enum: + * FT_LOAD_TARGET_XXX + * + * @description: + * A list of values that are used to select a specific hinting algorithm + * to use by the hinter. You should OR one of these values to your + * `load_flags' when calling @FT_Load_Glyph. + * + * Note that font's native hinters may ignore the hinting algorithm you + * have specified (e.g., the TrueType bytecode interpreter). You can set + * @FT_LOAD_FORCE_AUTOHINT to ensure that the auto-hinter is used. + * + * @values: + * FT_LOAD_TARGET_NORMAL :: + * This corresponds to the default hinting algorithm, optimized for + * standard gray-level rendering. For monochrome output, use + * @FT_LOAD_TARGET_MONO instead. + * + * FT_LOAD_TARGET_LIGHT :: + * A lighter hinting algorithm for gray-level modes. Many generated + * glyphs are fuzzier but better resemble their original shape. This + * is achieved by snapping glyphs to the pixel grid only vertically + * (Y-axis), as is done by Microsoft's ClearType and Adobe's + * proprietary font renderer. This preserves inter-glyph spacing in + * horizontal text. The snapping is done either by the native font + * driver if the driver itself and the font support it or by the + * auto-hinter. + * + * FT_LOAD_TARGET_MONO :: + * Strong hinting algorithm that should only be used for monochrome + * output. The result is probably unpleasant if the glyph is rendered + * in non-monochrome modes. + * + * FT_LOAD_TARGET_LCD :: + * A variant of @FT_LOAD_TARGET_NORMAL optimized for horizontally + * decimated LCD displays. + * + * FT_LOAD_TARGET_LCD_V :: + * A variant of @FT_LOAD_TARGET_NORMAL optimized for vertically + * decimated LCD displays. + * + * @note: + * You should use only _one_ of the FT_LOAD_TARGET_XXX values in your + * `load_flags'. They can't be ORed. + * + * If @FT_LOAD_RENDER is also set, the glyph is rendered in the + * corresponding mode (i.e., the mode that matches the used algorithm + * best). An exeption is FT_LOAD_TARGET_MONO since it implies + * @FT_LOAD_MONOCHROME. + * + * You can use a hinting algorithm that doesn't correspond to the same + * rendering mode. As an example, it is possible to use the `light' + * hinting algorithm and have the results rendered in horizontal LCD + * pixel mode, with code like + * + * { + * FT_Load_Glyph( face, glyph_index, + * load_flags | FT_LOAD_TARGET_LIGHT ); + * + * FT_Render_Glyph( face->glyph, FT_RENDER_MODE_LCD ); + * } + * + */ +#define FT_LOAD_TARGET_( x ) ( (FT_Int32)( (x) & 15 ) << 16 ) + +#define FT_LOAD_TARGET_NORMAL FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL ) +#define FT_LOAD_TARGET_LIGHT FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT ) +#define FT_LOAD_TARGET_MONO FT_LOAD_TARGET_( FT_RENDER_MODE_MONO ) +#define FT_LOAD_TARGET_LCD FT_LOAD_TARGET_( FT_RENDER_MODE_LCD ) +#define FT_LOAD_TARGET_LCD_V FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V ) + + + /************************************************************************** + * + * @macro: + * FT_LOAD_TARGET_MODE + * + * @description: + * Return the @FT_Render_Mode corresponding to a given + * @FT_LOAD_TARGET_XXX value. + * + */ +#define FT_LOAD_TARGET_MODE( x ) ( (FT_Render_Mode)( ( (x) >> 16 ) & 15 ) ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Transform */ + /* */ + /* <Description> */ + /* A function used to set the transformation that is applied to glyph */ + /* images when they are loaded into a glyph slot through */ + /* @FT_Load_Glyph. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation's 2x2 matrix. Use~0 for */ + /* the identity matrix. */ + /* delta :: A pointer to the translation vector. Use~0 for the null */ + /* vector. */ + /* */ + /* <Note> */ + /* The transformation is only applied to scalable image formats after */ + /* the glyph has been loaded. It means that hinting is unaltered by */ + /* the transformation and is performed on the character size given in */ + /* the last call to @FT_Set_Char_Size or @FT_Set_Pixel_Sizes. */ + /* */ + /* Note that this also transforms the `face.glyph.advance' field, but */ + /* *not* the values in `face.glyph.metrics'. */ + /* */ + FT_EXPORT( void ) + FT_Set_Transform( FT_Face face, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Render_Mode */ + /* */ + /* <Description> */ + /* An enumeration type that lists the render modes supported by */ + /* FreeType~2. Each mode corresponds to a specific type of scanline */ + /* conversion performed on the outline. */ + /* */ + /* For bitmap fonts and embedded bitmaps the `bitmap->pixel_mode' */ + /* field in the @FT_GlyphSlotRec structure gives the format of the */ + /* returned bitmap. */ + /* */ + /* All modes except @FT_RENDER_MODE_MONO use 256 levels of opacity, */ + /* indicating pixel coverage. Use linear alpha blending and gamma */ + /* correction to correctly render non-monochrome glyph bitmaps onto a */ + /* surface; see @FT_Render_Glyph. */ + /* */ + /* <Values> */ + /* FT_RENDER_MODE_NORMAL :: */ + /* This is the default render mode; it corresponds to 8-bit */ + /* anti-aliased bitmaps. */ + /* */ + /* FT_RENDER_MODE_LIGHT :: */ + /* This is equivalent to @FT_RENDER_MODE_NORMAL. It is only */ + /* defined as a separate value because render modes are also used */ + /* indirectly to define hinting algorithm selectors. See */ + /* @FT_LOAD_TARGET_XXX for details. */ + /* */ + /* FT_RENDER_MODE_MONO :: */ + /* This mode corresponds to 1-bit bitmaps (with 2~levels of */ + /* opacity). */ + /* */ + /* FT_RENDER_MODE_LCD :: */ + /* This mode corresponds to horizontal RGB and BGR sub-pixel */ + /* displays like LCD screens. It produces 8-bit bitmaps that are */ + /* 3~times the width of the original glyph outline in pixels, and */ + /* which use the @FT_PIXEL_MODE_LCD mode. */ + /* */ + /* FT_RENDER_MODE_LCD_V :: */ + /* This mode corresponds to vertical RGB and BGR sub-pixel displays */ + /* (like PDA screens, rotated LCD displays, etc.). It produces */ + /* 8-bit bitmaps that are 3~times the height of the original */ + /* glyph outline in pixels and use the @FT_PIXEL_MODE_LCD_V mode. */ + /* */ + /* <Note> */ + /* The LCD-optimized glyph bitmaps produced by FT_Render_Glyph can be */ + /* filtered to reduce color-fringes by using @FT_Library_SetLcdFilter */ + /* (not active in the default builds). It is up to the caller to */ + /* either call @FT_Library_SetLcdFilter (if available) or do the */ + /* filtering itself. */ + /* */ + /* The selected render mode only affects vector glyphs of a font. */ + /* Embedded bitmaps often have a different pixel mode like */ + /* @FT_PIXEL_MODE_MONO. You can use @FT_Bitmap_Convert to transform */ + /* them into 8-bit pixmaps. */ + /* */ + typedef enum FT_Render_Mode_ + { + FT_RENDER_MODE_NORMAL = 0, + FT_RENDER_MODE_LIGHT, + FT_RENDER_MODE_MONO, + FT_RENDER_MODE_LCD, + FT_RENDER_MODE_LCD_V, + + FT_RENDER_MODE_MAX + + } FT_Render_Mode; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Render_Mode' values instead */ +#define ft_render_mode_normal FT_RENDER_MODE_NORMAL +#define ft_render_mode_mono FT_RENDER_MODE_MONO + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Render_Glyph */ + /* */ + /* <Description> */ + /* Convert a given glyph image to a bitmap. It does so by inspecting */ + /* the glyph image format, finding the relevant renderer, and */ + /* invoking it. */ + /* */ + /* <InOut> */ + /* slot :: A handle to the glyph slot containing the image to */ + /* convert. */ + /* */ + /* <Input> */ + /* render_mode :: This is the render mode used to render the glyph */ + /* image into a bitmap. See @FT_Render_Mode for a */ + /* list of possible values. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* To get meaningful results, font scaling values must be set with */ + /* functions like @FT_Set_Char_Size before calling FT_Render_Glyph. */ + /* */ + /* When FreeType outputs a bitmap of a glyph, it really outputs an */ + /* alpha coverage map. If a pixel is completely covered by a */ + /* filled-in outline, the bitmap contains 0xFF at that pixel, meaning */ + /* that 0xFF/0xFF fraction of that pixel is covered, meaning the */ + /* pixel is 100% black (or 0% bright). If a pixel is only 50% */ + /* covered (value 0x80), the pixel is made 50% black (50% bright or a */ + /* middle shade of grey). 0% covered means 0% black (100% bright or */ + /* white). */ + /* */ + /* On high-DPI screens like on smartphones and tablets, the pixels */ + /* are so small that their chance of being completely covered and */ + /* therefore completely black are fairly good. On the low-DPI */ + /* screens, however, the situation is different. The pixels are too */ + /* large for most of the details of a glyph and shades of gray are */ + /* the norm rather than the exception. */ + /* */ + /* This is relevant because all our screens have a second problem: */ + /* they are not linear. 1~+~1 is not~2. Twice the value does not */ + /* result in twice the brightness. When a pixel is only 50% covered, */ + /* the coverage map says 50% black, and this translates to a pixel */ + /* value of 128 when you use 8~bits per channel (0-255). However, */ + /* this does not translate to 50% brightness for that pixel on our */ + /* sRGB and gamma~2.2 screens. Due to their non-linearity, they */ + /* dwell longer in the darks and only a pixel value of about 186 */ + /* results in 50% brightness – 128 ends up too dark on both bright */ + /* and dark backgrounds. The net result is that dark text looks */ + /* burnt-out, pixely and blotchy on bright background, bright text */ + /* too frail on dark backgrounds, and colored text on colored */ + /* background (for example, red on green) seems to have dark halos or */ + /* `dirt' around it. The situation is especially ugly for diagonal */ + /* stems like in `w' glyph shapes where the quality of FreeType's */ + /* anti-aliasing depends on the correct display of grays. On */ + /* high-DPI screens where smaller, fully black pixels reign supreme, */ + /* this doesn't matter, but on our low-DPI screens with all the gray */ + /* shades, it does. 0% and 100% brightness are the same things in */ + /* linear and non-linear space, just all the shades in-between */ + /* aren't. */ + /* */ + /* The blending function for placing text over a background is */ + /* */ + /* { */ + /* dst = alpha * src + (1 - alpha) * dst , */ + /* } */ + /* */ + /* which is known as the OVER operator. */ + /* */ + /* To correctly composite an antialiased pixel of a glyph onto a */ + /* surface, */ + /* */ + /* 1. take the foreground and background colors (e.g., in sRGB space) */ + /* and apply gamma to get them in a linear space, */ + /* */ + /* 2. use OVER to blend the two linear colors using the glyph pixel */ + /* as the alpha value (remember, the glyph bitmap is an alpha */ + /* coverage bitmap), and */ + /* */ + /* 3. apply inverse gamma to the blended pixel and write it back to */ + /* the image. */ + /* */ + /* Internal testing at Adobe found that a target inverse gamma of~1.8 */ + /* for step~3 gives good results across a wide range of displays with */ + /* an sRGB gamma curve or a similar one. */ + /* */ + /* This process can cost performance. There is an approximation that */ + /* does not need to know about the background color; see */ + /* https://bel.fi/alankila/lcd/ and */ + /* https://bel.fi/alankila/lcd/alpcor.html for details. */ + /* */ + /* *ATTENTION*: Linear blending is even more important when dealing */ + /* with subpixel-rendered glyphs to prevent color-fringing! A */ + /* subpixel-rendered glyph must first be filtered with a filter that */ + /* gives equal weight to the three color primaries and does not */ + /* exceed a sum of 0x100, see section @lcd_filtering. Then the */ + /* only difference to gray linear blending is that subpixel-rendered */ + /* linear blending is done 3~times per pixel: red foreground subpixel */ + /* to red background subpixel and so on for green and blue. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Render_Glyph( FT_GlyphSlot slot, + FT_Render_Mode render_mode ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Kerning_Mode */ + /* */ + /* <Description> */ + /* An enumeration used to specify which kerning values to return in */ + /* @FT_Get_Kerning. */ + /* */ + /* <Values> */ + /* FT_KERNING_DEFAULT :: Return grid-fitted kerning distances in */ + /* pixels (value is~0). Whether they are */ + /* scaled depends on @FT_LOAD_NO_SCALE. */ + /* */ + /* FT_KERNING_UNFITTED :: Return un-grid-fitted kerning distances in */ + /* 26.6 fractional pixels. Whether they are */ + /* scaled depends on @FT_LOAD_NO_SCALE. */ + /* */ + /* FT_KERNING_UNSCALED :: Return the kerning vector in original font */ + /* units. */ + /* */ + /* <Note> */ + /* FT_KERNING_DEFAULT returns full pixel values; it also makes */ + /* FreeType heuristically scale down kerning distances at small ppem */ + /* values so that they don't become too big. */ + /* */ + typedef enum FT_Kerning_Mode_ + { + FT_KERNING_DEFAULT = 0, + FT_KERNING_UNFITTED, + FT_KERNING_UNSCALED + + } FT_Kerning_Mode; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Kerning_Mode' values instead */ +#define ft_kerning_default FT_KERNING_DEFAULT +#define ft_kerning_unfitted FT_KERNING_UNFITTED +#define ft_kerning_unscaled FT_KERNING_UNSCALED + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Kerning */ + /* */ + /* <Description> */ + /* Return the kerning vector between two glyphs of a same face. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* left_glyph :: The index of the left glyph in the kern pair. */ + /* */ + /* right_glyph :: The index of the right glyph in the kern pair. */ + /* */ + /* kern_mode :: See @FT_Kerning_Mode for more information. */ + /* Determines the scale and dimension of the returned */ + /* kerning vector. */ + /* */ + /* <Output> */ + /* akerning :: The kerning vector. This is either in font units, */ + /* fractional pixels (26.6 format), or pixels for */ + /* scalable formats, and in pixels for fixed-sizes */ + /* formats. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Only horizontal layouts (left-to-right & right-to-left) are */ + /* supported by this method. Other layouts, or more sophisticated */ + /* kernings, are out of the scope of this API function -- they can be */ + /* implemented through format-specific interfaces. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Kerning( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_UInt kern_mode, + FT_Vector *akerning ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Track_Kerning */ + /* */ + /* <Description> */ + /* Return the track kerning for a given face object at a given size. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* point_size :: The point size in 16.16 fractional points. */ + /* */ + /* degree :: The degree of tightness. Increasingly negative */ + /* values represent tighter track kerning, while */ + /* increasingly positive values represent looser track */ + /* kerning. Value zero means no track kerning. */ + /* */ + /* <Output> */ + /* akerning :: The kerning in 16.16 fractional points, to be */ + /* uniformly applied between all glyphs. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Currently, only the Type~1 font driver supports track kerning, */ + /* using data from AFM files (if attached with @FT_Attach_File or */ + /* @FT_Attach_Stream). */ + /* */ + /* Only very few AFM files come with track kerning data; please refer */ + /* to the Adobe's AFM specification for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Track_Kerning( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph_Name */ + /* */ + /* <Description> */ + /* Retrieve the ASCII name of a given glyph in a face. This only */ + /* works for those faces where @FT_HAS_GLYPH_NAMES(face) returns~1. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* glyph_index :: The glyph index. */ + /* */ + /* buffer_max :: The maximum number of bytes available in the */ + /* buffer. */ + /* */ + /* <Output> */ + /* buffer :: A pointer to a target buffer where the name is */ + /* copied to. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* An error is returned if the face doesn't provide glyph names or if */ + /* the glyph index is invalid. In all cases of failure, the first */ + /* byte of `buffer' is set to~0 to indicate an empty name. */ + /* */ + /* The glyph name is truncated to fit within the buffer if it is too */ + /* long. The returned string is always zero-terminated. */ + /* */ + /* Be aware that FreeType reorders glyph indices internally so that */ + /* glyph index~0 always corresponds to the `missing glyph' (called */ + /* `.notdef'). */ + /* */ + /* This function always returns an error if the config macro */ + /* `FT_CONFIG_OPTION_NO_GLYPH_NAMES' is not defined in `ftoptions.h'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph_Name( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Postscript_Name */ + /* */ + /* <Description> */ + /* Retrieve the ASCII PostScript name of a given face, if available. */ + /* This only works with PostScript and TrueType fonts. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* A pointer to the face's PostScript name. NULL if unavailable. */ + /* */ + /* <Note> */ + /* The returned pointer is owned by the face and is destroyed with */ + /* it. */ + /* */ + FT_EXPORT( const char* ) + FT_Get_Postscript_Name( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Select_Charmap */ + /* */ + /* <Description> */ + /* Select a given charmap by its encoding tag (as listed in */ + /* `freetype.h'). */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* encoding :: A handle to the selected encoding. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function returns an error if no charmap in the face */ + /* corresponds to the encoding queried here. */ + /* */ + /* Because many fonts contain more than a single cmap for Unicode */ + /* encoding, this function has some special code to select the one */ + /* that covers Unicode best (`best' in the sense that a UCS-4 cmap is */ + /* preferred to a UCS-2 cmap). It is thus preferable to */ + /* @FT_Set_Charmap in this case. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Select_Charmap( FT_Face face, + FT_Encoding encoding ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Charmap */ + /* */ + /* <Description> */ + /* Select a given charmap for character code to glyph index mapping. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* charmap :: A handle to the selected charmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function returns an error if the charmap is not part of */ + /* the face (i.e., if it is not listed in the `face->charmaps' */ + /* table). */ + /* */ + /* It also fails if a type~14 charmap is selected. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Charmap( FT_Face face, + FT_CharMap charmap ); + + + /************************************************************************* + * + * @function: + * FT_Get_Charmap_Index + * + * @description: + * Retrieve index of a given charmap. + * + * @input: + * charmap :: + * A handle to a charmap. + * + * @return: + * The index into the array of character maps within the face to which + * `charmap' belongs. If an error occurs, -1 is returned. + * + */ + FT_EXPORT( FT_Int ) + FT_Get_Charmap_Index( FT_CharMap charmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Char_Index */ + /* */ + /* <Description> */ + /* Return the glyph index of a given character code. This function */ + /* uses a charmap object to do the mapping. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* charcode :: The character code. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means `undefined character code'. */ + /* */ + /* <Note> */ + /* If you use FreeType to manipulate the contents of font files */ + /* directly, be aware that the glyph index returned by this function */ + /* doesn't always correspond to the internal indices used within the */ + /* file. This is done to ensure that value~0 always corresponds to */ + /* the `missing glyph'. If the first glyph is not named `.notdef', */ + /* then for Type~1 and Type~42 fonts, `.notdef' will be moved into */ + /* the glyph ID~0 position, and whatever was there will be moved to */ + /* the position `.notdef' had. For Type~1 fonts, if there is no */ + /* `.notdef' glyph at all, then one will be created at index~0 and */ + /* whatever was there will be moved to the last index -- Type~42 */ + /* fonts are considered invalid under this condition. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Char_Index( FT_Face face, + FT_ULong charcode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_First_Char */ + /* */ + /* <Description> */ + /* This function is used to return the first character code in the */ + /* current charmap of a given face. It also returns the */ + /* corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of first character code. 0~if charmap is */ + /* empty. */ + /* */ + /* <Return> */ + /* The charmap's first character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_Next_Char to be able to */ + /* parse all character codes available in a given charmap. The code */ + /* should look like this: */ + /* */ + /* { */ + /* FT_ULong charcode; */ + /* FT_UInt gindex; */ + /* */ + /* */ + /* charcode = FT_Get_First_Char( face, &gindex ); */ + /* while ( gindex != 0 ) */ + /* { */ + /* ... do something with (charcode,gindex) pair ... */ + /* */ + /* charcode = FT_Get_Next_Char( face, charcode, &gindex ); */ + /* } */ + /* } */ + /* */ + /* Be aware that character codes can have values up to 0xFFFFFFFF; */ + /* this might happen for non-Unicode or malformed cmaps. However, */ + /* even with regular Unicode encoding, so-called `last resort fonts' */ + /* (using SFNT cmap format 13, see function @FT_Get_CMap_Format) */ + /* normally have entries for all Unicode characters up to 0x1FFFFF, */ + /* which can cause *a lot* of iterations. */ + /* */ + /* Note that `*agindex' is set to~0 if the charmap is empty. The */ + /* result itself can be~0 in two cases: if the charmap is empty or */ + /* if the value~0 is the first valid character code. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_First_Char( FT_Face face, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Next_Char */ + /* */ + /* <Description> */ + /* This function is used to return the next character code in the */ + /* current charmap of a given face following the value `char_code', */ + /* as well as the corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* char_code :: The starting character code. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of next character code. 0~if charmap */ + /* is empty. */ + /* */ + /* <Return> */ + /* The charmap's next character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_First_Char to walk */ + /* over all character codes available in a given charmap. See the */ + /* note for this function for a simple code example. */ + /* */ + /* Note that `*agindex' is set to~0 when there are no more codes in */ + /* the charmap. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_Next_Char( FT_Face face, + FT_ULong char_code, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Name_Index */ + /* */ + /* <Description> */ + /* Return the glyph index of a given glyph name. This function uses */ + /* driver specific objects to do the translation. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* glyph_name :: The glyph name. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means `undefined character code'. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Name_Index( FT_Face face, + FT_String* glyph_name ); + + + /************************************************************************* + * + * @macro: + * FT_SUBGLYPH_FLAG_XXX + * + * @description: + * A list of constants used to describe subglyphs. Please refer to the + * TrueType specification for the meaning of the various flags. + * + * @values: + * FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS :: + * FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES :: + * FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID :: + * FT_SUBGLYPH_FLAG_SCALE :: + * FT_SUBGLYPH_FLAG_XY_SCALE :: + * FT_SUBGLYPH_FLAG_2X2 :: + * FT_SUBGLYPH_FLAG_USE_MY_METRICS :: + * + */ +#define FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS 1 +#define FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES 2 +#define FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID 4 +#define FT_SUBGLYPH_FLAG_SCALE 8 +#define FT_SUBGLYPH_FLAG_XY_SCALE 0x40 +#define FT_SUBGLYPH_FLAG_2X2 0x80 +#define FT_SUBGLYPH_FLAG_USE_MY_METRICS 0x200 + + + /************************************************************************* + * + * @func: + * FT_Get_SubGlyph_Info + * + * @description: + * Retrieve a description of a given subglyph. Only use it if + * `glyph->format' is @FT_GLYPH_FORMAT_COMPOSITE; an error is + * returned otherwise. + * + * @input: + * glyph :: + * The source glyph slot. + * + * sub_index :: + * The index of the subglyph. Must be less than + * `glyph->num_subglyphs'. + * + * @output: + * p_index :: + * The glyph index of the subglyph. + * + * p_flags :: + * The subglyph flags, see @FT_SUBGLYPH_FLAG_XXX. + * + * p_arg1 :: + * The subglyph's first argument (if any). + * + * p_arg2 :: + * The subglyph's second argument (if any). + * + * p_transform :: + * The subglyph transformation (if any). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The values of `*p_arg1', `*p_arg2', and `*p_transform' must be + * interpreted depending on the flags returned in `*p_flags'. See the + * TrueType specification for details. + * + */ + FT_EXPORT( FT_Error ) + FT_Get_SubGlyph_Info( FT_GlyphSlot glyph, + FT_UInt sub_index, + FT_Int *p_index, + FT_UInt *p_flags, + FT_Int *p_arg1, + FT_Int *p_arg2, + FT_Matrix *p_transform ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_FSTYPE_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used in the `fsType' field of the OS/2 table */ + /* in a TrueType or OpenType font and the `FSType' entry in a */ + /* PostScript font. These bit flags are returned by */ + /* @FT_Get_FSType_Flags; they inform client applications of embedding */ + /* and subsetting restrictions associated with a font. */ + /* */ + /* See */ + /* http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/FontPolicies.pdf */ + /* for more details. */ + /* */ + /* <Values> */ + /* FT_FSTYPE_INSTALLABLE_EMBEDDING :: */ + /* Fonts with no fsType bit set may be embedded and permanently */ + /* installed on the remote system by an application. */ + /* */ + /* FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING :: */ + /* Fonts that have only this bit set must not be modified, embedded */ + /* or exchanged in any manner without first obtaining permission of */ + /* the font software copyright owner. */ + /* */ + /* FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING :: */ + /* If this bit is set, the font may be embedded and temporarily */ + /* loaded on the remote system. Documents containing Preview & */ + /* Print fonts must be opened `read-only'; no edits can be applied */ + /* to the document. */ + /* */ + /* FT_FSTYPE_EDITABLE_EMBEDDING :: */ + /* If this bit is set, the font may be embedded but must only be */ + /* installed temporarily on other systems. In contrast to Preview */ + /* & Print fonts, documents containing editable fonts may be opened */ + /* for reading, editing is permitted, and changes may be saved. */ + /* */ + /* FT_FSTYPE_NO_SUBSETTING :: */ + /* If this bit is set, the font may not be subsetted prior to */ + /* embedding. */ + /* */ + /* FT_FSTYPE_BITMAP_EMBEDDING_ONLY :: */ + /* If this bit is set, only bitmaps contained in the font may be */ + /* embedded; no outline data may be embedded. If there are no */ + /* bitmaps available in the font, then the font is unembeddable. */ + /* */ + /* <Note> */ + /* The flags are ORed together, thus more than a single value can be */ + /* returned. */ + /* */ + /* While the fsType flags can indicate that a font may be embedded, a */ + /* license with the font vendor may be separately required to use the */ + /* font in this way. */ + /* */ +#define FT_FSTYPE_INSTALLABLE_EMBEDDING 0x0000 +#define FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING 0x0002 +#define FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING 0x0004 +#define FT_FSTYPE_EDITABLE_EMBEDDING 0x0008 +#define FT_FSTYPE_NO_SUBSETTING 0x0100 +#define FT_FSTYPE_BITMAP_EMBEDDING_ONLY 0x0200 + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_FSType_Flags */ + /* */ + /* <Description> */ + /* Return the fsType flags for a font. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* The fsType flags, @FT_FSTYPE_XXX. */ + /* */ + /* <Note> */ + /* Use this function rather than directly reading the `fs_type' field */ + /* in the @PS_FontInfoRec structure, which is only guaranteed to */ + /* return the correct results for Type~1 fonts. */ + /* */ + /* <Since> */ + /* 2.3.8 */ + /* */ + FT_EXPORT( FT_UShort ) + FT_Get_FSType_Flags( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* glyph_variants */ + /* */ + /* <Title> */ + /* Glyph Variants */ + /* */ + /* <Abstract> */ + /* The FreeType~2 interface to Unicode Ideographic Variation */ + /* Sequences (IVS), using the SFNT cmap format~14. */ + /* */ + /* <Description> */ + /* Many CJK characters have variant forms. They are a sort of grey */ + /* area somewhere between being totally irrelevant and semantically */ + /* distinct; for this reason, the Unicode consortium decided to */ + /* introduce Ideographic Variation Sequences (IVS), consisting of a */ + /* Unicode base character and one of 240 variant selectors */ + /* (U+E0100-U+E01EF), instead of further extending the already huge */ + /* code range for CJK characters. */ + /* */ + /* An IVS is registered and unique; for further details please refer */ + /* to Unicode Technical Standard #37, the Ideographic Variation */ + /* Database: */ + /* */ + /* http://www.unicode.org/reports/tr37/ */ + /* */ + /* To date (November 2014), the character with the most variants is */ + /* U+9089, having 32 such IVS. */ + /* */ + /* Adobe and MS decided to support IVS with a new cmap subtable */ + /* (format~14). It is an odd subtable because it is not a mapping of */ + /* input code points to glyphs, but contains lists of all variants */ + /* supported by the font. */ + /* */ + /* A variant may be either `default' or `non-default'. A default */ + /* variant is the one you will get for that code point if you look it */ + /* up in the standard Unicode cmap. A non-default variant is a */ + /* different glyph. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharVariantIndex */ + /* */ + /* <Description> */ + /* Return the glyph index of a given character code as modified by */ + /* the variation selector. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character code point in Unicode. */ + /* */ + /* variantSelector :: */ + /* The Unicode code point of the variation selector. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means either `undefined character code', or */ + /* `undefined selector code', or `no variation selector cmap */ + /* subtable', or `current CharMap is not Unicode'. */ + /* */ + /* <Note> */ + /* If you use FreeType to manipulate the contents of font files */ + /* directly, be aware that the glyph index returned by this function */ + /* doesn't always correspond to the internal indices used within */ + /* the file. This is done to ensure that value~0 always corresponds */ + /* to the `missing glyph'. */ + /* */ + /* This function is only meaningful if */ + /* a) the font has a variation selector cmap sub table, */ + /* and */ + /* b) the current charmap has a Unicode encoding. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Face_GetCharVariantIndex( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharVariantIsDefault */ + /* */ + /* <Description> */ + /* Check whether this variant of this Unicode character is the one to */ + /* be found in the `cmap'. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character codepoint in Unicode. */ + /* */ + /* variantSelector :: */ + /* The Unicode codepoint of the variation selector. */ + /* */ + /* <Return> */ + /* 1~if found in the standard (Unicode) cmap, 0~if found in the */ + /* variation selector cmap, or -1 if it is not a variant. */ + /* */ + /* <Note> */ + /* This function is only meaningful if the font has a variation */ + /* selector cmap subtable. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_Int ) + FT_Face_GetCharVariantIsDefault( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetVariantSelectors */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode variant selectors found */ + /* in the font. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* <Return> */ + /* A pointer to an array of selector code points, or NULL if there is */ + /* no valid variant selector cmap subtable. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetVariantSelectors( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetVariantsOfChar */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode variant selectors found */ + /* for the specified character code. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character codepoint in Unicode. */ + /* */ + /* <Return> */ + /* A pointer to an array of variant selector code points that are */ + /* active for the given character, or NULL if the corresponding list */ + /* is empty. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetVariantsOfChar( FT_Face face, + FT_ULong charcode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharsOfVariant */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode character codes found for */ + /* the specified variant selector. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* variantSelector :: */ + /* The variant selector code point in Unicode. */ + /* */ + /* <Return> */ + /* A list of all the code points that are specified by this selector */ + /* (both default and non-default codes are returned) or NULL if there */ + /* is no valid cmap or the variant selector is invalid. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetCharsOfVariant( FT_Face face, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /* <Title> */ + /* Computations */ + /* */ + /* <Abstract> */ + /* Crunching fixed numbers and vectors. */ + /* */ + /* <Description> */ + /* This section contains various functions used to perform */ + /* computations on 16.16 fixed-float numbers or 2d vectors. */ + /* */ + /* <Order> */ + /* FT_MulDiv */ + /* FT_MulFix */ + /* FT_DivFix */ + /* FT_RoundFix */ + /* FT_CeilFix */ + /* FT_FloorFix */ + /* FT_Vector_Transform */ + /* FT_Matrix_Multiply */ + /* FT_Matrix_Invert */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulDiv */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation `(a*b)/c' */ + /* with maximum accuracy (it uses a 64-bit intermediate integer */ + /* whenever necessary). */ + /* */ + /* This function isn't necessarily as fast as some processor specific */ + /* operations, but is at least completely portable. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. */ + /* c :: The divisor. */ + /* */ + /* <Return> */ + /* The result of `(a*b)/c'. This function never traps when trying to */ + /* divide by zero; it simply returns `MaxInt' or `MinInt' depending */ + /* on the signs of `a' and `b'. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulDiv( FT_Long a, + FT_Long b, + FT_Long c ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*b)/0x10000' with maximum accuracy. Most of the time this is */ + /* used to multiply a given value by a 16.16 fixed-point factor. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. Use a 16.16 factor here whenever */ + /* possible (see note below). */ + /* */ + /* <Return> */ + /* The result of `(a*b)/0x10000'. */ + /* */ + /* <Note> */ + /* This function has been optimized for the case where the absolute */ + /* value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */ + /* As this happens mainly when scaling from notional units to */ + /* fractional pixels in FreeType, it resulted in noticeable speed */ + /* improvements between versions 2.x and 1.x. */ + /* */ + /* As a conclusion, always try to place a 16.16 factor as the */ + /* _second_ argument of this function; this can make a great */ + /* difference. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulFix( FT_Long a, + FT_Long b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_DivFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*0x10000)/b' with maximum accuracy. Most of the time, this is */ + /* used to divide a given value by a 16.16 fixed-point factor. */ + /* */ + /* <Input> */ + /* a :: The numerator. */ + /* b :: The denominator. Use a 16.16 factor here. */ + /* */ + /* <Return> */ + /* The result of `(a*0x10000)/b'. */ + /* */ + FT_EXPORT( FT_Long ) + FT_DivFix( FT_Long a, + FT_Long b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_RoundFix */ + /* */ + /* <Description> */ + /* A very simple function used to round a 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number to be rounded. */ + /* */ + /* <Return> */ + /* `a' rounded to nearest 16.16 fixed integer, halfway cases away */ + /* from zero. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_RoundFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_CeilFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the ceiling function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the ceiling function is to be computed. */ + /* */ + /* <Return> */ + /* `a' rounded towards plus infinity. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_CeilFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_FloorFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the floor function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the floor function is to be computed. */ + /* */ + /* <Return> */ + /* `a' rounded towards minus infinity. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_FloorFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Vector_Transform */ + /* */ + /* <Description> */ + /* Transform a single vector through a 2x2 matrix. */ + /* */ + /* <InOut> */ + /* vector :: The target vector to transform. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the source 2x2 matrix. */ + /* */ + /* <Note> */ + /* The result is undefined if either `vector' or `matrix' is invalid. */ + /* */ + FT_EXPORT( void ) + FT_Vector_Transform( FT_Vector* vec, + const FT_Matrix* matrix ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* version */ + /* */ + /* <Title> */ + /* FreeType Version */ + /* */ + /* <Abstract> */ + /* Functions and macros related to FreeType versions. */ + /* */ + /* <Description> */ + /* Note that those functions and macros are of limited use because */ + /* even a new release of FreeType with only documentation changes */ + /* increases the version number. */ + /* */ + /* <Order> */ + /* FT_Library_Version */ + /* */ + /* FREETYPE_MAJOR */ + /* FREETYPE_MINOR */ + /* FREETYPE_PATCH */ + /* */ + /* FT_Face_CheckTrueTypePatents */ + /* FT_Face_SetUnpatentedHinting */ + /* */ + /* FREETYPE_XXX */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @enum: + * FREETYPE_XXX + * + * @description: + * These three macros identify the FreeType source code version. + * Use @FT_Library_Version to access them at runtime. + * + * @values: + * FREETYPE_MAJOR :: The major version number. + * FREETYPE_MINOR :: The minor version number. + * FREETYPE_PATCH :: The patch level. + * + * @note: + * The version number of FreeType if built as a dynamic link library + * with the `libtool' package is _not_ controlled by these three + * macros. + * + */ +#define FREETYPE_MAJOR 2 +#define FREETYPE_MINOR 6 +#define FREETYPE_PATCH 3 + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Library_Version */ + /* */ + /* <Description> */ + /* Return the version of the FreeType library being used. This is */ + /* useful when dynamically linking to the library, since one cannot */ + /* use the macros @FREETYPE_MAJOR, @FREETYPE_MINOR, and */ + /* @FREETYPE_PATCH. */ + /* */ + /* <Input> */ + /* library :: A source library handle. */ + /* */ + /* <Output> */ + /* amajor :: The major version number. */ + /* */ + /* aminor :: The minor version number. */ + /* */ + /* apatch :: The patch version number. */ + /* */ + /* <Note> */ + /* The reason why this function takes a `library' argument is because */ + /* certain programs implement library initialization in a custom way */ + /* that doesn't use @FT_Init_FreeType. */ + /* */ + /* In such cases, the library version might not be available before */ + /* the library object has been created. */ + /* */ + FT_EXPORT( void ) + FT_Library_Version( FT_Library library, + FT_Int *amajor, + FT_Int *aminor, + FT_Int *apatch ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_CheckTrueTypePatents */ + /* */ + /* <Description> */ + /* Deprecated, does nothing. */ + /* */ + /* <Input> */ + /* face :: A face handle. */ + /* */ + /* <Return> */ + /* Always returns false. */ + /* */ + /* <Note> */ + /* Since May 2010, TrueType hinting is no longer patented. */ + /* */ + /* <Since> */ + /* 2.3.5 */ + /* */ + FT_EXPORT( FT_Bool ) + FT_Face_CheckTrueTypePatents( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_SetUnpatentedHinting */ + /* */ + /* <Description> */ + /* Deprecated, does nothing. */ + /* */ + /* <Input> */ + /* face :: A face handle. */ + /* */ + /* value :: New boolean setting. */ + /* */ + /* <Return> */ + /* Always returns false. */ + /* */ + /* <Note> */ + /* Since May 2010, TrueType hinting is no longer patented. */ + /* */ + /* <Since> */ + /* 2.3.5 */ + /* */ + FT_EXPORT( FT_Bool ) + FT_Face_SetUnpatentedHinting( FT_Face face, + FT_Bool value ); + + /* */ + + +FT_END_HEADER + +#endif /* FREETYPE_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftadvanc.h b/foreign/freetype2/freetype/ftadvanc.h new file mode 100644 index 0000000..023dd84 --- /dev/null +++ b/foreign/freetype2/freetype/ftadvanc.h @@ -0,0 +1,187 @@ +/***************************************************************************/ +/* */ +/* ftadvanc.h */ +/* */ +/* Quick computation of advance widths (specification only). */ +/* */ +/* Copyright 2008-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTADVANC_H_ +#define FTADVANC_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * quick_advance + * + * @title: + * Quick retrieval of advance values + * + * @abstract: + * Retrieve horizontal and vertical advance values without processing + * glyph outlines, if possible. + * + * @description: + * This section contains functions to quickly extract advance values + * without handling glyph outlines, if possible. + * + * @order: + * FT_Get_Advance + * FT_Get_Advances + * + */ + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* FT_ADVANCE_FLAG_FAST_ONLY */ + /* */ + /* <Description> */ + /* A bit-flag to be OR-ed with the `flags' parameter of the */ + /* @FT_Get_Advance and @FT_Get_Advances functions. */ + /* */ + /* If set, it indicates that you want these functions to fail if the */ + /* corresponding hinting mode or font driver doesn't allow for very */ + /* quick advance computation. */ + /* */ + /* Typically, glyphs that are either unscaled, unhinted, bitmapped, */ + /* or light-hinted can have their advance width computed very */ + /* quickly. */ + /* */ + /* Normal and bytecode hinted modes that require loading, scaling, */ + /* and hinting of the glyph outline, are extremely slow by */ + /* comparison. */ + /* */ +#define FT_ADVANCE_FLAG_FAST_ONLY 0x20000000L + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Advance */ + /* */ + /* <Description> */ + /* Retrieve the advance value of a given glyph outline in an */ + /* @FT_Face. */ + /* */ + /* <Input> */ + /* face :: The source @FT_Face handle. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* load_flags :: A set of bit flags similar to those used when */ + /* calling @FT_Load_Glyph, used to determine what kind */ + /* of advances you need. */ + /* <Output> */ + /* padvance :: The advance value. If scaling is performed (based on */ + /* the value of `load_flags'), the advance value is in */ + /* 16.16 format. Otherwise, it is in font units. */ + /* */ + /* If @FT_LOAD_VERTICAL_LAYOUT is set, this is the */ + /* vertical advance corresponding to a vertical layout. */ + /* Otherwise, it is the horizontal advance in a */ + /* horizontal layout. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function may fail if you use @FT_ADVANCE_FLAG_FAST_ONLY and */ + /* if the corresponding font backend doesn't have a quick way to */ + /* retrieve the advances. */ + /* */ + /* A scaled advance is returned in 16.16 format but isn't transformed */ + /* by the affine transformation specified by @FT_Set_Transform. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Advance( FT_Face face, + FT_UInt gindex, + FT_Int32 load_flags, + FT_Fixed *padvance ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Advances */ + /* */ + /* <Description> */ + /* Retrieve the advance values of several glyph outlines in an */ + /* @FT_Face. */ + /* */ + /* <Input> */ + /* face :: The source @FT_Face handle. */ + /* */ + /* start :: The first glyph index. */ + /* */ + /* count :: The number of advance values you want to retrieve. */ + /* */ + /* load_flags :: A set of bit flags similar to those used when */ + /* calling @FT_Load_Glyph. */ + /* */ + /* <Output> */ + /* padvance :: The advance values. This array, to be provided by the */ + /* caller, must contain at least `count' elements. */ + /* */ + /* If scaling is performed (based on the value of */ + /* `load_flags'), the advance values are in 16.16 format. */ + /* Otherwise, they are in font units. */ + /* */ + /* If @FT_LOAD_VERTICAL_LAYOUT is set, these are the */ + /* vertical advances corresponding to a vertical layout. */ + /* Otherwise, they are the horizontal advances in a */ + /* horizontal layout. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + /* <Note> */ + /* This function may fail if you use @FT_ADVANCE_FLAG_FAST_ONLY and */ + /* if the corresponding font backend doesn't have a quick way to */ + /* retrieve the advances. */ + /* */ + /* Scaled advances are returned in 16.16 format but aren't */ + /* transformed by the affine transformation specified by */ + /* @FT_Set_Transform. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Advances( FT_Face face, + FT_UInt start, + FT_UInt count, + FT_Int32 load_flags, + FT_Fixed *padvances ); + + /* */ + + +FT_END_HEADER + +#endif /* FTADVANC_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftautoh.h b/foreign/freetype2/freetype/ftautoh.h new file mode 100644 index 0000000..8052dd2 --- /dev/null +++ b/foreign/freetype2/freetype/ftautoh.h @@ -0,0 +1,503 @@ +/***************************************************************************/ +/* */ +/* ftautoh.h */ +/* */ +/* FreeType API for controlling the auto-hinter (specification only). */ +/* */ +/* Copyright 2012-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTAUTOH_H_ +#define FTAUTOH_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * auto_hinter + * + * @title: + * The auto-hinter + * + * @abstract: + * Controlling the auto-hinting module. + * + * @description: + * While FreeType's auto-hinter doesn't expose API functions by itself, + * it is possible to control its behaviour with @FT_Property_Set and + * @FT_Property_Get. The following lists the available properties + * together with the necessary macros and structures. + * + * Note that the auto-hinter's module name is `autofitter' for + * historical reasons. + * + */ + + + /************************************************************************** + * + * @property: + * glyph-to-script-map + * + * @description: + * *Experimental* *only* + * + * The auto-hinter provides various script modules to hint glyphs. + * Examples of supported scripts are Latin or CJK. Before a glyph is + * auto-hinted, the Unicode character map of the font gets examined, and + * the script is then determined based on Unicode character ranges, see + * below. + * + * OpenType fonts, however, often provide much more glyphs than + * character codes (small caps, superscripts, ligatures, swashes, etc.), + * to be controlled by so-called `features'. Handling OpenType features + * can be quite complicated and thus needs a separate library on top of + * FreeType. + * + * The mapping between glyph indices and scripts (in the auto-hinter + * sense, see the @FT_AUTOHINTER_SCRIPT_XXX values) is stored as an + * array with `num_glyphs' elements, as found in the font's @FT_Face + * structure. The `glyph-to-script-map' property returns a pointer to + * this array, which can be modified as needed. Note that the + * modification should happen before the first glyph gets processed by + * the auto-hinter so that the global analysis of the font shapes + * actually uses the modified mapping. + * + * The following example code demonstrates how to access it (omitting + * the error handling). + * + * { + * FT_Library library; + * FT_Face face; + * FT_Prop_GlyphToScriptMap prop; + * + * + * FT_Init_FreeType( &library ); + * FT_New_Face( library, "foo.ttf", 0, &face ); + * + * prop.face = face; + * + * FT_Property_Get( library, "autofitter", + * "glyph-to-script-map", &prop ); + * + * // adjust `prop.map' as needed right here + * + * FT_Load_Glyph( face, ..., FT_LOAD_FORCE_AUTOHINT ); + * } + * + */ + + + /************************************************************************** + * + * @enum: + * FT_AUTOHINTER_SCRIPT_XXX + * + * @description: + * *Experimental* *only* + * + * A list of constants used for the @glyph-to-script-map property to + * specify the script submodule the auto-hinter should use for hinting a + * particular glyph. + * + * @values: + * FT_AUTOHINTER_SCRIPT_NONE :: + * Don't auto-hint this glyph. + * + * FT_AUTOHINTER_SCRIPT_LATIN :: + * Apply the latin auto-hinter. For the auto-hinter, `latin' is a + * very broad term, including Cyrillic and Greek also since characters + * from those scripts share the same design constraints. + * + * By default, characters from the following Unicode ranges are + * assigned to this submodule. + * + * { + * U+0020 - U+007F // Basic Latin (no control characters) + * U+00A0 - U+00FF // Latin-1 Supplement (no control characters) + * U+0100 - U+017F // Latin Extended-A + * U+0180 - U+024F // Latin Extended-B + * U+0250 - U+02AF // IPA Extensions + * U+02B0 - U+02FF // Spacing Modifier Letters + * U+0300 - U+036F // Combining Diacritical Marks + * U+0370 - U+03FF // Greek and Coptic + * U+0400 - U+04FF // Cyrillic + * U+0500 - U+052F // Cyrillic Supplement + * U+1D00 - U+1D7F // Phonetic Extensions + * U+1D80 - U+1DBF // Phonetic Extensions Supplement + * U+1DC0 - U+1DFF // Combining Diacritical Marks Supplement + * U+1E00 - U+1EFF // Latin Extended Additional + * U+1F00 - U+1FFF // Greek Extended + * U+2000 - U+206F // General Punctuation + * U+2070 - U+209F // Superscripts and Subscripts + * U+20A0 - U+20CF // Currency Symbols + * U+2150 - U+218F // Number Forms + * U+2460 - U+24FF // Enclosed Alphanumerics + * U+2C60 - U+2C7F // Latin Extended-C + * U+2DE0 - U+2DFF // Cyrillic Extended-A + * U+2E00 - U+2E7F // Supplemental Punctuation + * U+A640 - U+A69F // Cyrillic Extended-B + * U+A720 - U+A7FF // Latin Extended-D + * U+FB00 - U+FB06 // Alphab. Present. Forms (Latin Ligatures) + * U+1D400 - U+1D7FF // Mathematical Alphanumeric Symbols + * U+1F100 - U+1F1FF // Enclosed Alphanumeric Supplement + * } + * + * FT_AUTOHINTER_SCRIPT_CJK :: + * Apply the CJK auto-hinter, covering Chinese, Japanese, Korean, old + * Vietnamese, and some other scripts. + * + * By default, characters from the following Unicode ranges are + * assigned to this submodule. + * + * { + * U+1100 - U+11FF // Hangul Jamo + * U+2E80 - U+2EFF // CJK Radicals Supplement + * U+2F00 - U+2FDF // Kangxi Radicals + * U+2FF0 - U+2FFF // Ideographic Description Characters + * U+3000 - U+303F // CJK Symbols and Punctuation + * U+3040 - U+309F // Hiragana + * U+30A0 - U+30FF // Katakana + * U+3100 - U+312F // Bopomofo + * U+3130 - U+318F // Hangul Compatibility Jamo + * U+3190 - U+319F // Kanbun + * U+31A0 - U+31BF // Bopomofo Extended + * U+31C0 - U+31EF // CJK Strokes + * U+31F0 - U+31FF // Katakana Phonetic Extensions + * U+3200 - U+32FF // Enclosed CJK Letters and Months + * U+3300 - U+33FF // CJK Compatibility + * U+3400 - U+4DBF // CJK Unified Ideographs Extension A + * U+4DC0 - U+4DFF // Yijing Hexagram Symbols + * U+4E00 - U+9FFF // CJK Unified Ideographs + * U+A960 - U+A97F // Hangul Jamo Extended-A + * U+AC00 - U+D7AF // Hangul Syllables + * U+D7B0 - U+D7FF // Hangul Jamo Extended-B + * U+F900 - U+FAFF // CJK Compatibility Ideographs + * U+FE10 - U+FE1F // Vertical forms + * U+FE30 - U+FE4F // CJK Compatibility Forms + * U+FF00 - U+FFEF // Halfwidth and Fullwidth Forms + * U+1B000 - U+1B0FF // Kana Supplement + * U+1D300 - U+1D35F // Tai Xuan Hing Symbols + * U+1F200 - U+1F2FF // Enclosed Ideographic Supplement + * U+20000 - U+2A6DF // CJK Unified Ideographs Extension B + * U+2A700 - U+2B73F // CJK Unified Ideographs Extension C + * U+2B740 - U+2B81F // CJK Unified Ideographs Extension D + * U+2F800 - U+2FA1F // CJK Compatibility Ideographs Supplement + * } + * + * FT_AUTOHINTER_SCRIPT_INDIC :: + * Apply the indic auto-hinter, covering all major scripts from the + * Indian sub-continent and some other related scripts like Thai, Lao, + * or Tibetan. + * + * By default, characters from the following Unicode ranges are + * assigned to this submodule. + * + * { + * U+0900 - U+0DFF // Indic Range + * U+0F00 - U+0FFF // Tibetan + * U+1900 - U+194F // Limbu + * U+1B80 - U+1BBF // Sundanese + * U+1C80 - U+1CDF // Meetei Mayak + * U+A800 - U+A82F // Syloti Nagri + * U+11800 - U+118DF // Sharada + * } + * + * Note that currently Indic support is rudimentary only, missing blue + * zone support. + * + */ +#define FT_AUTOHINTER_SCRIPT_NONE 0 +#define FT_AUTOHINTER_SCRIPT_LATIN 1 +#define FT_AUTOHINTER_SCRIPT_CJK 2 +#define FT_AUTOHINTER_SCRIPT_INDIC 3 + + + /************************************************************************** + * + * @struct: + * FT_Prop_GlyphToScriptMap + * + * @description: + * *Experimental* *only* + * + * The data exchange structure for the @glyph-to-script-map property. + * + */ + typedef struct FT_Prop_GlyphToScriptMap_ + { + FT_Face face; + FT_UShort* map; + + } FT_Prop_GlyphToScriptMap; + + + /************************************************************************** + * + * @property: + * fallback-script + * + * @description: + * *Experimental* *only* + * + * If no auto-hinter script module can be assigned to a glyph, a + * fallback script gets assigned to it (see also the + * @glyph-to-script-map property). By default, this is + * @FT_AUTOHINTER_SCRIPT_CJK. Using the `fallback-script' property, + * this fallback value can be changed. + * + * { + * FT_Library library; + * FT_UInt fallback_script = FT_AUTOHINTER_SCRIPT_NONE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "autofitter", + * "fallback-script", &fallback_script ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * It's important to use the right timing for changing this value: The + * creation of the glyph-to-script map that eventually uses the + * fallback script value gets triggered either by setting or reading a + * face-specific property like @glyph-to-script-map, or by auto-hinting + * any glyph from that face. In particular, if you have already created + * an @FT_Face structure but not loaded any glyph (using the + * auto-hinter), a change of the fallback script will affect this face. + * + */ + + + /************************************************************************** + * + * @property: + * default-script + * + * @description: + * *Experimental* *only* + * + * If FreeType gets compiled with FT_CONFIG_OPTION_USE_HARFBUZZ to make + * the HarfBuzz library access OpenType features for getting better + * glyph coverages, this property sets the (auto-fitter) script to be + * used for the default (OpenType) script data of a font's GSUB table. + * Features for the default script are intended for all scripts not + * explicitly handled in GSUB; an example is a `dlig' feature, + * containing the combination of the characters `T', `E', and `L' to + * form a `TEL' ligature. + * + * By default, this is @FT_AUTOHINTER_SCRIPT_LATIN. Using the + * `default-script' property, this default value can be changed. + * + * { + * FT_Library library; + * FT_UInt default_script = FT_AUTOHINTER_SCRIPT_NONE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "autofitter", + * "default-script", &default_script ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * It's important to use the right timing for changing this value: The + * creation of the glyph-to-script map that eventually uses the + * default script value gets triggered either by setting or reading a + * face-specific property like @glyph-to-script-map, or by auto-hinting + * any glyph from that face. In particular, if you have already created + * an @FT_Face structure but not loaded any glyph (using the + * auto-hinter), a change of the default script will affect this face. + * + */ + + + /************************************************************************** + * + * @property: + * increase-x-height + * + * @description: + * For ppem values in the range 6~<= ppem <= `increase-x-height', round + * up the font's x~height much more often than normally. If the value + * is set to~0, which is the default, this feature is switched off. Use + * this property to improve the legibility of small font sizes if + * necessary. + * + * { + * FT_Library library; + * FT_Face face; + * FT_Prop_IncreaseXHeight prop; + * + * + * FT_Init_FreeType( &library ); + * FT_New_Face( library, "foo.ttf", 0, &face ); + * FT_Set_Char_Size( face, 10 * 64, 0, 72, 0 ); + * + * prop.face = face; + * prop.limit = 14; + * + * FT_Property_Set( library, "autofitter", + * "increase-x-height", &prop ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * Set this value right after calling @FT_Set_Char_Size, but before + * loading any glyph (using the auto-hinter). + * + */ + + + /************************************************************************** + * + * @struct: + * FT_Prop_IncreaseXHeight + * + * @description: + * The data exchange structure for the @increase-x-height property. + * + */ + typedef struct FT_Prop_IncreaseXHeight_ + { + FT_Face face; + FT_UInt limit; + + } FT_Prop_IncreaseXHeight; + + + /************************************************************************** + * + * @property: + * warping + * + * @description: + * *Experimental* *only* + * + * If FreeType gets compiled with option AF_CONFIG_OPTION_USE_WARPER to + * activate the warp hinting code in the auto-hinter, this property + * switches warping on and off. + * + * Warping only works in `light' auto-hinting mode. The idea of the + * code is to slightly scale and shift a glyph along the non-hinted + * dimension (which is usually the horizontal axis) so that as much of + * its segments are aligned (more or less) to the grid. To find out a + * glyph's optimal scaling and shifting value, various parameter + * combinations are tried and scored. + * + * By default, warping is off. The example below shows how to switch on + * warping (omitting the error handling). + * + * { + * FT_Library library; + * FT_Bool warping = 1; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "autofitter", + * "warping", &warping ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + * The warping code can also change advance widths. Have a look at the + * `lsb_delta' and `rsb_delta' fields in the @FT_GlyphSlotRec structure + * for details on improving inter-glyph distances while rendering. + * + * Since warping is a global property of the auto-hinter it is best to + * change its value before rendering any face. Otherwise, you should + * reload all faces that get auto-hinted in `light' hinting mode. + * + */ + + + /************************************************************************** + * + * @property: + * no-stem-darkening[autofit] + * + * @description: + * *Experimental* *only,* *requires* *linear* *alpha* *blending* *and* + * *gamma* *correction* + * + * Stem darkening emboldens glyphs at smaller sizes to make them more + * readable on common low-DPI screens when using linear alpha blending + * and gamma correction, see @FT_Render_Glyph. When not using linear + * alpha blending and gamma correction, glyphs will appear heavy and + * fuzzy! + * + * Gamma correction essentially lightens fonts since shades of grey are + * shifted to higher pixel values (=~higher brightness) to match the + * original intention to the reality of our screens. The side-effect is + * that glyphs `thin out'. Mac OS~X and Adobe's proprietary font + * rendering library implement a counter-measure: stem darkening at + * smaller sizes where shades of gray dominate. By emboldening a glyph + * slightly in relation to its pixel size, individual pixels get higher + * coverage of filled-in outlines and are therefore `blacker'. This + * counteracts the `thinning out' of glyphs, making text remain readable + * at smaller sizes. All glyphs that pass through the auto-hinter will + * be emboldened unless this property is set to TRUE. + * + * See the description of the CFF driver for algorithmic details. Total + * consistency with the CFF driver is currently not achieved because the + * emboldening method differs and glyphs must be scaled down on the + * Y-axis to keep outline points inside their precomputed blue zones. + * The smaller the size (especially 9ppem and down), the higher the loss + * of emboldening versus the CFF driver. + * + */ + + + /************************************************************************** + * + * @property: + * darkening-parameters[autofit] + * + * @description: + * *Experimental* *only* + * + * See the description of the CFF driver for details. This + * implementation appropriates the + * CFF_CONFIG_OPTION_DARKENING_PARAMETER_* #defines for consistency. + * Note the differences described in @no-stem-darkening[autofit]. + * + */ + + + /* */ + + +FT_END_HEADER + +#endif /* FTAUTOH_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftbbox.h b/foreign/freetype2/freetype/ftbbox.h new file mode 100644 index 0000000..2a4d214 --- /dev/null +++ b/foreign/freetype2/freetype/ftbbox.h @@ -0,0 +1,101 @@ +/***************************************************************************/ +/* */ +/* ftbbox.h */ +/* */ +/* FreeType exact bbox computation (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This component has a _single_ role: to compute exact outline bounding */ + /* boxes. */ + /* */ + /* It is separated from the rest of the engine for various technical */ + /* reasons. It may well be integrated in `ftoutln' later. */ + /* */ + /*************************************************************************/ + + +#ifndef FTBBOX_H_ +#define FTBBOX_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_BBox */ + /* */ + /* <Description> */ + /* Compute the exact bounding box of an outline. This is slower */ + /* than computing the control box. However, it uses an advanced */ + /* algorithm that returns _very_ quickly when the two boxes */ + /* coincide. Otherwise, the outline Bézier arcs are traversed to */ + /* extract their extrema. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source outline. */ + /* */ + /* <Output> */ + /* abbox :: The outline's exact bounding box. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If the font is tricky and the glyph has been loaded with */ + /* @FT_LOAD_NO_SCALE, the resulting BBox is meaningless. To get */ + /* reasonable values for the BBox it is necessary to load the glyph */ + /* at a large ppem value (so that the hinting instructions can */ + /* properly shift and scale the subglyphs), then extracting the BBox, */ + /* which can be eventually converted back to font units. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Get_BBox( FT_Outline* outline, + FT_BBox *abbox ); + + /* */ + + +FT_END_HEADER + +#endif /* FTBBOX_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/foreign/freetype2/freetype/ftbdf.h b/foreign/freetype2/freetype/ftbdf.h new file mode 100644 index 0000000..016dba0 --- /dev/null +++ b/foreign/freetype2/freetype/ftbdf.h @@ -0,0 +1,210 @@ +/***************************************************************************/ +/* */ +/* ftbdf.h */ +/* */ +/* FreeType API for accessing BDF-specific strings (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTBDF_H_ +#define FTBDF_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* bdf_fonts */ + /* */ + /* <Title> */ + /* BDF and PCF Files */ + /* */ + /* <Abstract> */ + /* BDF and PCF specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of functions specific to BDF */ + /* and PCF fonts. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @enum: + * BDF_PropertyType + * + * @description: + * A list of BDF property types. + * + * @values: + * BDF_PROPERTY_TYPE_NONE :: + * Value~0 is used to indicate a missing property. + * + * BDF_PROPERTY_TYPE_ATOM :: + * Property is a string atom. + * + * BDF_PROPERTY_TYPE_INTEGER :: + * Property is a 32-bit signed integer. + * + * BDF_PROPERTY_TYPE_CARDINAL :: + * Property is a 32-bit unsigned integer. + */ + typedef enum BDF_PropertyType_ + { + BDF_PROPERTY_TYPE_NONE = 0, + BDF_PROPERTY_TYPE_ATOM = 1, + BDF_PROPERTY_TYPE_INTEGER = 2, + BDF_PROPERTY_TYPE_CARDINAL = 3 + + } BDF_PropertyType; + + + /********************************************************************** + * + * @type: + * BDF_Property + * + * @description: + * A handle to a @BDF_PropertyRec structure to model a given + * BDF/PCF property. + */ + typedef struct BDF_PropertyRec_* BDF_Property; + + + /********************************************************************** + * + * @struct: + * BDF_PropertyRec + * + * @description: + * This structure models a given BDF/PCF property. + * + * @fields: + * type :: + * The property type. + * + * u.atom :: + * The atom string, if type is @BDF_PROPERTY_TYPE_ATOM. May be + * NULL, indicating an empty string. + * + * u.integer :: + * A signed integer, if type is @BDF_PROPERTY_TYPE_INTEGER. + * + * u.cardinal :: + * An unsigned integer, if type is @BDF_PROPERTY_TYPE_CARDINAL. + */ + typedef struct BDF_PropertyRec_ + { + BDF_PropertyType type; + union { + const char* atom; + FT_Int32 integer; + FT_UInt32 cardinal; + + } u; + + } BDF_PropertyRec; + + + /********************************************************************** + * + * @function: + * FT_Get_BDF_Charset_ID + * + * @description: + * Retrieve a BDF font character set identity, according to + * the BDF specification. + * + * @input: + * face :: + * A handle to the input face. + * + * @output: + * acharset_encoding :: + * Charset encoding, as a C~string, owned by the face. + * + * acharset_registry :: + * Charset registry, as a C~string, owned by the face. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with BDF faces, returning an error otherwise. + */ + FT_EXPORT( FT_Error ) + FT_Get_BDF_Charset_ID( FT_Face face, + const char* *acharset_encoding, + const char* *acharset_registry ); + + + /********************************************************************** + * + * @function: + * FT_Get_BDF_Property + * + * @description: + * Retrieve a BDF property from a BDF or PCF font file. + * + * @input: + * face :: A handle to the input face. + * + * name :: The property name. + * + * @output: + * aproperty :: The property. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function works with BDF _and_ PCF fonts. It returns an error + * otherwise. It also returns an error if the property is not in the + * font. + * + * A `property' is a either key-value pair within the STARTPROPERTIES + * ... ENDPROPERTIES block of a BDF font or a key-value pair from the + * `info->props' array within a `FontRec' structure of a PCF font. + * + * Integer properties are always stored as `signed' within PCF fonts; + * consequently, @BDF_PROPERTY_TYPE_CARDINAL is a possible return value + * for BDF fonts only. + * + * In case of error, `aproperty->type' is always set to + * @BDF_PROPERTY_TYPE_NONE. + */ + FT_EXPORT( FT_Error ) + FT_Get_BDF_Property( FT_Face face, + const char* prop_name, + BDF_PropertyRec *aproperty ); + + /* */ + +FT_END_HEADER + +#endif /* FTBDF_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftbitmap.h b/foreign/freetype2/freetype/ftbitmap.h new file mode 100644 index 0000000..0eac7b9 --- /dev/null +++ b/foreign/freetype2/freetype/ftbitmap.h @@ -0,0 +1,240 @@ +/***************************************************************************/ +/* */ +/* ftbitmap.h */ +/* */ +/* FreeType utility functions for bitmaps (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTBITMAP_H_ +#define FTBITMAP_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* bitmap_handling */ + /* */ + /* <Title> */ + /* Bitmap Handling */ + /* */ + /* <Abstract> */ + /* Handling FT_Bitmap objects. */ + /* */ + /* <Description> */ + /* This section contains functions for handling @FT_Bitmap objects. */ + /* Note that none of the functions changes the bitmap's `flow' (as */ + /* indicated by the sign of the `pitch' field in `FT_Bitmap'). */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Init */ + /* */ + /* <Description> */ + /* Initialize a pointer to an @FT_Bitmap structure. */ + /* */ + /* <InOut> */ + /* abitmap :: A pointer to the bitmap structure. */ + /* */ + /* <Note> */ + /* A deprecated name for the same function is `FT_Bitmap_New'. */ + /* */ + FT_EXPORT( void ) + FT_Bitmap_Init( FT_Bitmap *abitmap ); + + + /* deprecated */ + FT_EXPORT( void ) + FT_Bitmap_New( FT_Bitmap *abitmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Copy */ + /* */ + /* <Description> */ + /* Copy a bitmap into another one. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* source :: A handle to the source bitmap. */ + /* */ + /* <Output> */ + /* target :: A handle to the target bitmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Copy( FT_Library library, + const FT_Bitmap *source, + FT_Bitmap *target); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Embolden */ + /* */ + /* <Description> */ + /* Embolden a bitmap. The new bitmap will be about `xStrength' */ + /* pixels wider and `yStrength' pixels higher. The left and bottom */ + /* borders are kept unchanged. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* xStrength :: How strong the glyph is emboldened horizontally. */ + /* Expressed in 26.6 pixel format. */ + /* */ + /* yStrength :: How strong the glyph is emboldened vertically. */ + /* Expressed in 26.6 pixel format. */ + /* */ + /* <InOut> */ + /* bitmap :: A handle to the target bitmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The current implementation restricts `xStrength' to be less than */ + /* or equal to~8 if bitmap is of pixel_mode @FT_PIXEL_MODE_MONO. */ + /* */ + /* If you want to embolden the bitmap owned by a @FT_GlyphSlotRec, */ + /* you should call @FT_GlyphSlot_Own_Bitmap on the slot first. */ + /* */ + /* Bitmaps in @FT_PIXEL_MODE_GRAY2 and @FT_PIXEL_MODE_GRAY@ format */ + /* are converted to @FT_PIXEL_MODE_GRAY format (i.e., 8bpp). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Embolden( FT_Library library, + FT_Bitmap* bitmap, + FT_Pos xStrength, + FT_Pos yStrength ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Convert */ + /* */ + /* <Description> */ + /* Convert a bitmap object with depth 1bpp, 2bpp, 4bpp, 8bpp or 32bpp */ + /* to a bitmap object with depth 8bpp, making the number of used */ + /* bytes line (a.k.a. the `pitch') a multiple of `alignment'. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* source :: The source bitmap. */ + /* */ + /* alignment :: The pitch of the bitmap is a multiple of this */ + /* parameter. Common values are 1, 2, or 4. */ + /* */ + /* <Output> */ + /* target :: The target bitmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* It is possible to call @FT_Bitmap_Convert multiple times without */ + /* calling @FT_Bitmap_Done (the memory is simply reallocated). */ + /* */ + /* Use @FT_Bitmap_Done to finally remove the bitmap object. */ + /* */ + /* The `library' argument is taken to have access to FreeType's */ + /* memory handling functions. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Convert( FT_Library library, + const FT_Bitmap *source, + FT_Bitmap *target, + FT_Int alignment ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GlyphSlot_Own_Bitmap */ + /* */ + /* <Description> */ + /* Make sure that a glyph slot owns `slot->bitmap'. */ + /* */ + /* <Input> */ + /* slot :: The glyph slot. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function is to be used in combination with */ + /* @FT_Bitmap_Embolden. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GlyphSlot_Own_Bitmap( FT_GlyphSlot slot ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Bitmap_Done */ + /* */ + /* <Description> */ + /* Destroy a bitmap object initialized with @FT_Bitmap_Init. */ + /* */ + /* <Input> */ + /* library :: A handle to a library object. */ + /* */ + /* bitmap :: The bitmap object to be freed. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The `library' argument is taken to have access to FreeType's */ + /* memory handling functions. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Done( FT_Library library, + FT_Bitmap *bitmap ); + + + /* */ + + +FT_END_HEADER + +#endif /* FTBITMAP_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftbzip2.h b/foreign/freetype2/freetype/ftbzip2.h new file mode 100644 index 0000000..b7f2eee --- /dev/null +++ b/foreign/freetype2/freetype/ftbzip2.h @@ -0,0 +1,102 @@ +/***************************************************************************/ +/* */ +/* ftbzip2.h */ +/* */ +/* Bzip2-compressed stream support. */ +/* */ +/* Copyright 2010-2016 by */ +/* Joel Klinghed. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTBZIP2_H_ +#define FTBZIP2_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* bzip2 */ + /* */ + /* <Title> */ + /* BZIP2 Streams */ + /* */ + /* <Abstract> */ + /* Using bzip2-compressed font files. */ + /* */ + /* <Description> */ + /* This section contains the declaration of Bzip2-specific functions. */ + /* */ + /*************************************************************************/ + + + /************************************************************************ + * + * @function: + * FT_Stream_OpenBzip2 + * + * @description: + * Open a new stream to parse bzip2-compressed font files. This is + * mainly used to support the compressed `*.pcf.bz2' fonts that come + * with XFree86. + * + * @input: + * stream :: + * The target embedding stream. + * + * source :: + * The source stream. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source stream must be opened _before_ calling this function. + * + * Calling the internal function `FT_Stream_Close' on the new stream will + * *not* call `FT_Stream_Close' on the source stream. None of the stream + * objects will be released to the heap. + * + * The stream implementation is very basic and resets the decompression + * process each time seeking backwards is needed within the stream. + * + * In certain builds of the library, bzip2 compression recognition is + * automatically handled when calling @FT_New_Face or @FT_Open_Face. + * This means that if no font driver is capable of handling the raw + * compressed file, the library will try to open a bzip2 compressed stream + * from it and re-open the face with it. + * + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with bzip2 support. + */ + FT_EXPORT( FT_Error ) + FT_Stream_OpenBzip2( FT_Stream stream, + FT_Stream source ); + + /* */ + + +FT_END_HEADER + +#endif /* FTBZIP2_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftcache.h b/foreign/freetype2/freetype/ftcache.h new file mode 100644 index 0000000..6c9f2c4 --- /dev/null +++ b/foreign/freetype2/freetype/ftcache.h @@ -0,0 +1,1057 @@ +/***************************************************************************/ +/* */ +/* ftcache.h */ +/* */ +/* FreeType Cache subsystem (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCACHE_H_ +#define FTCACHE_H_ + + +#include <ft2build.h> +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /************************************************************************* + * + * <Section> + * cache_subsystem + * + * <Title> + * Cache Sub-System + * + * <Abstract> + * How to cache face, size, and glyph data with FreeType~2. + * + * <Description> + * This section describes the FreeType~2 cache sub-system, which is used + * to limit the number of concurrently opened @FT_Face and @FT_Size + * objects, as well as caching information like character maps and glyph + * images while limiting their maximum memory usage. + * + * Note that all types and functions begin with the `FTC_' prefix. + * + * The cache is highly portable and thus doesn't know anything about the + * fonts installed on your system, or how to access them. This implies + * the following scheme: + * + * First, available or installed font faces are uniquely identified by + * @FTC_FaceID values, provided to the cache by the client. Note that + * the cache only stores and compares these values, and doesn't try to + * interpret them in any way. + * + * Second, the cache calls, only when needed, a client-provided function + * to convert an @FTC_FaceID into a new @FT_Face object. The latter is + * then completely managed by the cache, including its termination + * through @FT_Done_Face. To monitor termination of face objects, the + * finalizer callback in the `generic' field of the @FT_Face object can + * be used, which might also be used to store the @FTC_FaceID of the + * face. + * + * Clients are free to map face IDs to anything else. The most simple + * usage is to associate them to a (pathname,face_index) pair that is + * used to call @FT_New_Face. However, more complex schemes are also + * possible. + * + * Note that for the cache to work correctly, the face ID values must be + * *persistent*, which means that the contents they point to should not + * change at runtime, or that their value should not become invalid. + * + * If this is unavoidable (e.g., when a font is uninstalled at runtime), + * you should call @FTC_Manager_RemoveFaceID as soon as possible, to let + * the cache get rid of any references to the old @FTC_FaceID it may + * keep internally. Failure to do so will lead to incorrect behaviour + * or even crashes. + * + * To use the cache, start with calling @FTC_Manager_New to create a new + * @FTC_Manager object, which models a single cache instance. You can + * then look up @FT_Face and @FT_Size objects with + * @FTC_Manager_LookupFace and @FTC_Manager_LookupSize, respectively. + * + * If you want to use the charmap caching, call @FTC_CMapCache_New, then + * later use @FTC_CMapCache_Lookup to perform the equivalent of + * @FT_Get_Char_Index, only much faster. + * + * If you want to use the @FT_Glyph caching, call @FTC_ImageCache, then + * later use @FTC_ImageCache_Lookup to retrieve the corresponding + * @FT_Glyph objects from the cache. + * + * If you need lots of small bitmaps, it is much more memory efficient + * to call @FTC_SBitCache_New followed by @FTC_SBitCache_Lookup. This + * returns @FTC_SBitRec structures, which are used to store small + * bitmaps directly. (A small bitmap is one whose metrics and + * dimensions all fit into 8-bit integers). + * + * We hope to also provide a kerning cache in the near future. + * + * + * <Order> + * FTC_Manager + * FTC_FaceID + * FTC_Face_Requester + * + * FTC_Manager_New + * FTC_Manager_Reset + * FTC_Manager_Done + * FTC_Manager_LookupFace + * FTC_Manager_LookupSize + * FTC_Manager_RemoveFaceID + * + * FTC_Node + * FTC_Node_Unref + * + * FTC_ImageCache + * FTC_ImageCache_New + * FTC_ImageCache_Lookup + * + * FTC_SBit + * FTC_SBitCache + * FTC_SBitCache_New + * FTC_SBitCache_Lookup + * + * FTC_CMapCache + * FTC_CMapCache_New + * FTC_CMapCache_Lookup + * + *************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BASIC TYPE DEFINITIONS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: FTC_FaceID + * + * @description: + * An opaque pointer type that is used to identity face objects. The + * contents of such objects is application-dependent. + * + * These pointers are typically used to point to a user-defined + * structure containing a font file path, and face index. + * + * @note: + * Never use NULL as a valid @FTC_FaceID. + * + * Face IDs are passed by the client to the cache manager that calls, + * when needed, the @FTC_Face_Requester to translate them into new + * @FT_Face objects. + * + * If the content of a given face ID changes at runtime, or if the value + * becomes invalid (e.g., when uninstalling a font), you should + * immediately call @FTC_Manager_RemoveFaceID before any other cache + * function. + * + * Failure to do so will result in incorrect behaviour or even + * memory leaks and crashes. + */ + typedef FT_Pointer FTC_FaceID; + + + /************************************************************************ + * + * @functype: + * FTC_Face_Requester + * + * @description: + * A callback function provided by client applications. It is used by + * the cache manager to translate a given @FTC_FaceID into a new valid + * @FT_Face object, on demand. + * + * <Input> + * face_id :: + * The face ID to resolve. + * + * library :: + * A handle to a FreeType library object. + * + * req_data :: + * Application-provided request data (see note below). + * + * <Output> + * aface :: + * A new @FT_Face handle. + * + * <Return> + * FreeType error code. 0~means success. + * + * <Note> + * The third parameter `req_data' is the same as the one passed by the + * client when @FTC_Manager_New is called. + * + * The face requester should not perform funny things on the returned + * face object, like creating a new @FT_Size for it, or setting a + * transformation through @FT_Set_Transform! + */ + typedef FT_Error + (*FTC_Face_Requester)( FTC_FaceID face_id, + FT_Library library, + FT_Pointer req_data, + FT_Face* aface ); + + /* */ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CACHE MANAGER OBJECT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_Manager */ + /* */ + /* <Description> */ + /* This object corresponds to one instance of the cache-subsystem. */ + /* It is used to cache one or more @FT_Face objects, along with */ + /* corresponding @FT_Size objects. */ + /* */ + /* The manager intentionally limits the total number of opened */ + /* @FT_Face and @FT_Size objects to control memory usage. See the */ + /* `max_faces' and `max_sizes' parameters of @FTC_Manager_New. */ + /* */ + /* The manager is also used to cache `nodes' of various types while */ + /* limiting their total memory usage. */ + /* */ + /* All limitations are enforced by keeping lists of managed objects */ + /* in most-recently-used order, and flushing old nodes to make room */ + /* for new ones. */ + /* */ + typedef struct FTC_ManagerRec_* FTC_Manager; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_Node */ + /* */ + /* <Description> */ + /* An opaque handle to a cache node object. Each cache node is */ + /* reference-counted. A node with a count of~0 might be flushed */ + /* out of a full cache whenever a lookup request is performed. */ + /* */ + /* If you look up nodes, you have the ability to `acquire' them, */ + /* i.e., to increment their reference count. This will prevent the */ + /* node from being flushed out of the cache until you explicitly */ + /* `release' it (see @FTC_Node_Unref). */ + /* */ + /* See also @FTC_SBitCache_Lookup and @FTC_ImageCache_Lookup. */ + /* */ + typedef struct FTC_NodeRec_* FTC_Node; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_New */ + /* */ + /* <Description> */ + /* Create a new cache manager. */ + /* */ + /* <Input> */ + /* library :: The parent FreeType library handle to use. */ + /* */ + /* max_faces :: Maximum number of opened @FT_Face objects managed by */ + /* this cache instance. Use~0 for defaults. */ + /* */ + /* max_sizes :: Maximum number of opened @FT_Size objects managed by */ + /* this cache instance. Use~0 for defaults. */ + /* */ + /* max_bytes :: Maximum number of bytes to use for cached data nodes. */ + /* Use~0 for defaults. Note that this value does not */ + /* account for managed @FT_Face and @FT_Size objects. */ + /* */ + /* requester :: An application-provided callback used to translate */ + /* face IDs into real @FT_Face objects. */ + /* */ + /* req_data :: A generic pointer that is passed to the requester */ + /* each time it is called (see @FTC_Face_Requester). */ + /* */ + /* <Output> */ + /* amanager :: A handle to a new manager object. 0~in case of */ + /* failure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_New( FT_Library library, + FT_UInt max_faces, + FT_UInt max_sizes, + FT_ULong max_bytes, + FTC_Face_Requester requester, + FT_Pointer req_data, + FTC_Manager *amanager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Reset */ + /* */ + /* <Description> */ + /* Empty a given cache manager. This simply gets rid of all the */ + /* currently cached @FT_Face and @FT_Size objects within the manager. */ + /* */ + /* <InOut> */ + /* manager :: A handle to the manager. */ + /* */ + FT_EXPORT( void ) + FTC_Manager_Reset( FTC_Manager manager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_Done */ + /* */ + /* <Description> */ + /* Destroy a given manager after emptying it. */ + /* */ + /* <Input> */ + /* manager :: A handle to the target cache manager object. */ + /* */ + FT_EXPORT( void ) + FTC_Manager_Done( FTC_Manager manager ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_LookupFace */ + /* */ + /* <Description> */ + /* Retrieve the @FT_Face object that corresponds to a given face ID */ + /* through a cache manager. */ + /* */ + /* <Input> */ + /* manager :: A handle to the cache manager. */ + /* */ + /* face_id :: The ID of the face object. */ + /* */ + /* <Output> */ + /* aface :: A handle to the face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned @FT_Face object is always owned by the manager. You */ + /* should never try to discard it yourself. */ + /* */ + /* The @FT_Face object doesn't necessarily have a current size object */ + /* (i.e., face->size can be~0). If you need a specific `font size', */ + /* use @FTC_Manager_LookupSize instead. */ + /* */ + /* Never change the face's transformation matrix (i.e., never call */ + /* the @FT_Set_Transform function) on a returned face! If you need */ + /* to transform glyphs, do it yourself after glyph loading. */ + /* */ + /* When you perform a lookup, out-of-memory errors are detected */ + /* _within_ the lookup and force incremental flushes of the cache */ + /* until enough memory is released for the lookup to succeed. */ + /* */ + /* If a lookup fails with `FT_Err_Out_Of_Memory' the cache has */ + /* already been completely flushed, and still no memory was available */ + /* for the operation. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_LookupFace( FTC_Manager manager, + FTC_FaceID face_id, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FTC_ScalerRec */ + /* */ + /* <Description> */ + /* A structure used to describe a given character size in either */ + /* pixels or points to the cache manager. See */ + /* @FTC_Manager_LookupSize. */ + /* */ + /* <Fields> */ + /* face_id :: The source face ID. */ + /* */ + /* width :: The character width. */ + /* */ + /* height :: The character height. */ + /* */ + /* pixel :: A Boolean. If 1, the `width' and `height' fields are */ + /* interpreted as integer pixel character sizes. */ + /* Otherwise, they are expressed as 1/64th of points. */ + /* */ + /* x_res :: Only used when `pixel' is value~0 to indicate the */ + /* horizontal resolution in dpi. */ + /* */ + /* y_res :: Only used when `pixel' is value~0 to indicate the */ + /* vertical resolution in dpi. */ + /* */ + /* <Note> */ + /* This type is mainly used to retrieve @FT_Size objects through the */ + /* cache manager. */ + /* */ + typedef struct FTC_ScalerRec_ + { + FTC_FaceID face_id; + FT_UInt width; + FT_UInt height; + FT_Int pixel; + FT_UInt x_res; + FT_UInt y_res; + + } FTC_ScalerRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FTC_Scaler */ + /* */ + /* <Description> */ + /* A handle to an @FTC_ScalerRec structure. */ + /* */ + typedef struct FTC_ScalerRec_* FTC_Scaler; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Manager_LookupSize */ + /* */ + /* <Description> */ + /* Retrieve the @FT_Size object that corresponds to a given */ + /* @FTC_ScalerRec pointer through a cache manager. */ + /* */ + /* <Input> */ + /* manager :: A handle to the cache manager. */ + /* */ + /* scaler :: A scaler handle. */ + /* */ + /* <Output> */ + /* asize :: A handle to the size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned @FT_Size object is always owned by the manager. You */ + /* should never try to discard it by yourself. */ + /* */ + /* You can access the parent @FT_Face object simply as `size->face' */ + /* if you need it. Note that this object is also owned by the */ + /* manager. */ + /* */ + /* <Note> */ + /* When you perform a lookup, out-of-memory errors are detected */ + /* _within_ the lookup and force incremental flushes of the cache */ + /* until enough memory is released for the lookup to succeed. */ + /* */ + /* If a lookup fails with `FT_Err_Out_Of_Memory' the cache has */ + /* already been completely flushed, and still no memory is available */ + /* for the operation. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_Manager_LookupSize( FTC_Manager manager, + FTC_Scaler scaler, + FT_Size *asize ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_Node_Unref */ + /* */ + /* <Description> */ + /* Decrement a cache node's internal reference count. When the count */ + /* reaches 0, it is not destroyed but becomes eligible for subsequent */ + /* cache flushes. */ + /* */ + /* <Input> */ + /* node :: The cache node handle. */ + /* */ + /* manager :: The cache manager handle. */ + /* */ + FT_EXPORT( void ) + FTC_Node_Unref( FTC_Node node, + FTC_Manager manager ); + + + /************************************************************************* + * + * @function: + * FTC_Manager_RemoveFaceID + * + * @description: + * A special function used to indicate to the cache manager that + * a given @FTC_FaceID is no longer valid, either because its + * content changed, or because it was deallocated or uninstalled. + * + * @input: + * manager :: + * The cache manager handle. + * + * face_id :: + * The @FTC_FaceID to be removed. + * + * @note: + * This function flushes all nodes from the cache corresponding to this + * `face_id', with the exception of nodes with a non-null reference + * count. + * + * Such nodes are however modified internally so as to never appear + * in later lookups with the same `face_id' value, and to be immediately + * destroyed when released by all their users. + * + */ + FT_EXPORT( void ) + FTC_Manager_RemoveFaceID( FTC_Manager manager, + FTC_FaceID face_id ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + /************************************************************************* + * + * @type: + * FTC_CMapCache + * + * @description: + * An opaque handle used to model a charmap cache. This cache is to + * hold character codes -> glyph indices mappings. + * + */ + typedef struct FTC_CMapCacheRec_* FTC_CMapCache; + + + /************************************************************************* + * + * @function: + * FTC_CMapCache_New + * + * @description: + * Create a new charmap cache. + * + * @input: + * manager :: + * A handle to the cache manager. + * + * @output: + * acache :: + * A new cache handle. NULL in case of error. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * Like all other caches, this one will be destroyed with the cache + * manager. + * + */ + FT_EXPORT( FT_Error ) + FTC_CMapCache_New( FTC_Manager manager, + FTC_CMapCache *acache ); + + + /************************************************************************ + * + * @function: + * FTC_CMapCache_Lookup + * + * @description: + * Translate a character code into a glyph index, using the charmap + * cache. + * + * @input: + * cache :: + * A charmap cache handle. + * + * face_id :: + * The source face ID. + * + * cmap_index :: + * The index of the charmap in the source face. Any negative value + * means to use the cache @FT_Face's default charmap. + * + * char_code :: + * The character code (in the corresponding charmap). + * + * @return: + * Glyph index. 0~means `no glyph'. + * + */ + FT_EXPORT( FT_UInt ) + FTC_CMapCache_Lookup( FTC_CMapCache cache, + FTC_FaceID face_id, + FT_Int cmap_index, + FT_UInt32 char_code ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cache_subsystem */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** IMAGE CACHE OBJECT *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /************************************************************************* + * + * @struct: + * FTC_ImageTypeRec + * + * @description: + * A structure used to model the type of images in a glyph cache. + * + * @fields: + * face_id :: + * The face ID. + * + * width :: + * The width in pixels. + * + * height :: + * The height in pixels. + * + * flags :: + * The load flags, as in @FT_Load_Glyph. + * + */ + typedef struct FTC_ImageTypeRec_ + { + FTC_FaceID face_id; + FT_UInt width; + FT_UInt height; + FT_Int32 flags; + + } FTC_ImageTypeRec; + + + /************************************************************************* + * + * @type: + * FTC_ImageType + * + * @description: + * A handle to an @FTC_ImageTypeRec structure. + * + */ + typedef struct FTC_ImageTypeRec_* FTC_ImageType; + + + /* */ + + +#define FTC_IMAGE_TYPE_COMPARE( d1, d2 ) \ + ( (d1)->face_id == (d2)->face_id && \ + (d1)->width == (d2)->width && \ + (d1)->flags == (d2)->flags ) + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_ImageCache */ + /* */ + /* <Description> */ + /* A handle to a glyph image cache object. They are designed to */ + /* hold many distinct glyph images while not exceeding a certain */ + /* memory threshold. */ + /* */ + typedef struct FTC_ImageCacheRec_* FTC_ImageCache; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_ImageCache_New */ + /* */ + /* <Description> */ + /* Create a new glyph image cache. */ + /* */ + /* <Input> */ + /* manager :: The parent manager for the image cache. */ + /* */ + /* <Output> */ + /* acache :: A handle to the new glyph image cache object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_New( FTC_Manager manager, + FTC_ImageCache *acache ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_ImageCache_Lookup */ + /* */ + /* <Description> */ + /* Retrieve a given glyph image from a glyph image cache. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source glyph image cache. */ + /* */ + /* type :: A pointer to a glyph image type descriptor. */ + /* */ + /* gindex :: The glyph index to retrieve. */ + /* */ + /* <Output> */ + /* aglyph :: The corresponding @FT_Glyph object. 0~in case of */ + /* failure. */ + /* */ + /* anode :: Used to return the address of of the corresponding cache */ + /* node after incrementing its reference count (see note */ + /* below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned glyph is owned and managed by the glyph image cache. */ + /* Never try to transform or discard it manually! You can however */ + /* create a copy with @FT_Glyph_Copy and modify the new one. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the glyph image, after increasing its reference */ + /* count. This ensures that the node (as well as the @FT_Glyph) will */ + /* always be kept in the cache until you call @FTC_Node_Unref to */ + /* `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the @FT_Glyph could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_Lookup( FTC_ImageCache cache, + FTC_ImageType type, + FT_UInt gindex, + FT_Glyph *aglyph, + FTC_Node *anode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_ImageCache_LookupScaler */ + /* */ + /* <Description> */ + /* A variant of @FTC_ImageCache_Lookup that uses an @FTC_ScalerRec */ + /* to specify the face ID and its size. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source glyph image cache. */ + /* */ + /* scaler :: A pointer to a scaler descriptor. */ + /* */ + /* load_flags :: The corresponding load flags. */ + /* */ + /* gindex :: The glyph index to retrieve. */ + /* */ + /* <Output> */ + /* aglyph :: The corresponding @FT_Glyph object. 0~in case of */ + /* failure. */ + /* */ + /* anode :: Used to return the address of of the corresponding */ + /* cache node after incrementing its reference count */ + /* (see note below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The returned glyph is owned and managed by the glyph image cache. */ + /* Never try to transform or discard it manually! You can however */ + /* create a copy with @FT_Glyph_Copy and modify the new one. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the glyph image, after increasing its reference */ + /* count. This ensures that the node (as well as the @FT_Glyph) will */ + /* always be kept in the cache until you call @FTC_Node_Unref to */ + /* `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the @FT_Glyph could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + /* Calls to @FT_Set_Char_Size and friends have no effect on cached */ + /* glyphs; you should always use the FreeType cache API instead. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_ImageCache_LookupScaler( FTC_ImageCache cache, + FTC_Scaler scaler, + FT_ULong load_flags, + FT_UInt gindex, + FT_Glyph *aglyph, + FTC_Node *anode ); + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_SBit */ + /* */ + /* <Description> */ + /* A handle to a small bitmap descriptor. See the @FTC_SBitRec */ + /* structure for details. */ + /* */ + typedef struct FTC_SBitRec_* FTC_SBit; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FTC_SBitRec */ + /* */ + /* <Description> */ + /* A very compact structure used to describe a small glyph bitmap. */ + /* */ + /* <Fields> */ + /* width :: The bitmap width in pixels. */ + /* */ + /* height :: The bitmap height in pixels. */ + /* */ + /* left :: The horizontal distance from the pen position to the */ + /* left bitmap border (a.k.a. `left side bearing', or */ + /* `lsb'). */ + /* */ + /* top :: The vertical distance from the pen position (on the */ + /* baseline) to the upper bitmap border (a.k.a. `top */ + /* side bearing'). The distance is positive for upwards */ + /* y~coordinates. */ + /* */ + /* format :: The format of the glyph bitmap (monochrome or gray). */ + /* */ + /* max_grays :: Maximum gray level value (in the range 1 to~255). */ + /* */ + /* pitch :: The number of bytes per bitmap line. May be positive */ + /* or negative. */ + /* */ + /* xadvance :: The horizontal advance width in pixels. */ + /* */ + /* yadvance :: The vertical advance height in pixels. */ + /* */ + /* buffer :: A pointer to the bitmap pixels. */ + /* */ + typedef struct FTC_SBitRec_ + { + FT_Byte width; + FT_Byte height; + FT_Char left; + FT_Char top; + + FT_Byte format; + FT_Byte max_grays; + FT_Short pitch; + FT_Char xadvance; + FT_Char yadvance; + + FT_Byte* buffer; + + } FTC_SBitRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FTC_SBitCache */ + /* */ + /* <Description> */ + /* A handle to a small bitmap cache. These are special cache objects */ + /* used to store small glyph bitmaps (and anti-aliased pixmaps) in a */ + /* much more efficient way than the traditional glyph image cache */ + /* implemented by @FTC_ImageCache. */ + /* */ + typedef struct FTC_SBitCacheRec_* FTC_SBitCache; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_SBitCache_New */ + /* */ + /* <Description> */ + /* Create a new cache to store small glyph bitmaps. */ + /* */ + /* <Input> */ + /* manager :: A handle to the source cache manager. */ + /* */ + /* <Output> */ + /* acache :: A handle to the new sbit cache. NULL in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_New( FTC_Manager manager, + FTC_SBitCache *acache ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_SBitCache_Lookup */ + /* */ + /* <Description> */ + /* Look up a given small glyph bitmap in a given sbit cache and */ + /* `lock' it to prevent its flushing from the cache until needed. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source sbit cache. */ + /* */ + /* type :: A pointer to the glyph image type descriptor. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* <Output> */ + /* sbit :: A handle to a small bitmap descriptor. */ + /* */ + /* anode :: Used to return the address of of the corresponding cache */ + /* node after incrementing its reference count (see note */ + /* below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The small bitmap descriptor and its bit buffer are owned by the */ + /* cache and should never be freed by the application. They might */ + /* as well disappear from memory on the next cache lookup, so don't */ + /* treat them as persistent data. */ + /* */ + /* The descriptor's `buffer' field is set to~0 to indicate a missing */ + /* glyph bitmap. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the bitmap, after increasing its reference count. */ + /* This ensures that the node (as well as the image) will always be */ + /* kept in the cache until you call @FTC_Node_Unref to `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the bitmap could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_Lookup( FTC_SBitCache cache, + FTC_ImageType type, + FT_UInt gindex, + FTC_SBit *sbit, + FTC_Node *anode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FTC_SBitCache_LookupScaler */ + /* */ + /* <Description> */ + /* A variant of @FTC_SBitCache_Lookup that uses an @FTC_ScalerRec */ + /* to specify the face ID and its size. */ + /* */ + /* <Input> */ + /* cache :: A handle to the source sbit cache. */ + /* */ + /* scaler :: A pointer to the scaler descriptor. */ + /* */ + /* load_flags :: The corresponding load flags. */ + /* */ + /* gindex :: The glyph index. */ + /* */ + /* <Output> */ + /* sbit :: A handle to a small bitmap descriptor. */ + /* */ + /* anode :: Used to return the address of of the corresponding */ + /* cache node after incrementing its reference count */ + /* (see note below). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The small bitmap descriptor and its bit buffer are owned by the */ + /* cache and should never be freed by the application. They might */ + /* as well disappear from memory on the next cache lookup, so don't */ + /* treat them as persistent data. */ + /* */ + /* The descriptor's `buffer' field is set to~0 to indicate a missing */ + /* glyph bitmap. */ + /* */ + /* If `anode' is _not_ NULL, it receives the address of the cache */ + /* node containing the bitmap, after increasing its reference count. */ + /* This ensures that the node (as well as the image) will always be */ + /* kept in the cache until you call @FTC_Node_Unref to `release' it. */ + /* */ + /* If `anode' is NULL, the cache node is left unchanged, which means */ + /* that the bitmap could be flushed out of the cache on the next */ + /* call to one of the caching sub-system APIs. Don't assume that it */ + /* is persistent! */ + /* */ + FT_EXPORT( FT_Error ) + FTC_SBitCache_LookupScaler( FTC_SBitCache cache, + FTC_Scaler scaler, + FT_ULong load_flags, + FT_UInt gindex, + FTC_SBit *sbit, + FTC_Node *anode ); + + /* */ + + +FT_END_HEADER + +#endif /* FTCACHE_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftcffdrv.h b/foreign/freetype2/freetype/ftcffdrv.h new file mode 100644 index 0000000..9dea980 --- /dev/null +++ b/foreign/freetype2/freetype/ftcffdrv.h @@ -0,0 +1,262 @@ +/***************************************************************************/ +/* */ +/* ftcffdrv.h */ +/* */ +/* FreeType API for controlling the CFF driver (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCFFDRV_H_ +#define FTCFFDRV_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * cff_driver + * + * @title: + * The CFF driver + * + * @abstract: + * Controlling the CFF driver module. + * + * @description: + * While FreeType's CFF driver doesn't expose API functions by itself, + * it is possible to control its behaviour with @FT_Property_Set and + * @FT_Property_Get. The list below gives the available properties + * together with the necessary macros and structures. + * + * The CFF driver's module name is `cff'. + * + * *Hinting* *and* *antialiasing* *principles* *of* *the* *new* *engine* + * + * The rasterizer is positioning horizontal features (e.g., ascender + * height & x-height, or crossbars) on the pixel grid and minimizing the + * amount of antialiasing applied to them, while placing vertical + * features (vertical stems) on the pixel grid without hinting, thus + * representing the stem position and weight accurately. Sometimes the + * vertical stems may be only partially black. In this context, + * `antialiasing' means that stems are not positioned exactly on pixel + * borders, causing a fuzzy appearance. + * + * There are two principles behind this approach. + * + * 1) No hinting in the horizontal direction: Unlike `superhinted' + * TrueType, which changes glyph widths to accommodate regular + * inter-glyph spacing, Adobe's approach is `faithful to the design' in + * representing both the glyph width and the inter-glyph spacing + * designed for the font. This makes the screen display as close as it + * can be to the result one would get with infinite resolution, while + * preserving what is considered the key characteristics of each glyph. + * Note that the distances between unhinted and grid-fitted positions at + * small sizes are comparable to kerning values and thus would be + * noticeable (and distracting) while reading if hinting were applied. + * + * One of the reasons to not hint horizontally is antialiasing for LCD + * screens: The pixel geometry of modern displays supplies three + * vertical sub-pixels as the eye moves horizontally across each visible + * pixel. On devices where we can be certain this characteristic is + * present a rasterizer can take advantage of the sub-pixels to add + * increments of weight. In Western writing systems this turns out to + * be the more critical direction anyway; the weights and spacing of + * vertical stems (see above) are central to Armenian, Cyrillic, Greek, + * and Latin type designs. Even when the rasterizer uses greyscale + * antialiasing instead of color (a necessary compromise when one + * doesn't know the screen characteristics), the unhinted vertical + * features preserve the design's weight and spacing much better than + * aliased type would. + * + * 2) Aligment in the vertical direction: Weights and spacing along the + * y~axis are less critical; what is much more important is the visual + * alignment of related features (like cap-height and x-height). The + * sense of alignment for these is enhanced by the sharpness of grid-fit + * edges, while the cruder vertical resolution (full pixels instead of + * 1/3 pixels) is less of a problem. + * + * On the technical side, horizontal alignment zones for ascender, + * x-height, and other important height values (traditionally called + * `blue zones') as defined in the font are positioned independently, + * each being rounded to the nearest pixel edge, taking care of + * overshoot suppression at small sizes, stem darkening, and scaling. + * + * Hstems (this is, hint values defined in the font to help align + * horizontal features) that fall within a blue zone are said to be + * `captured' and are aligned to that zone. Uncaptured stems are moved + * in one of four ways, top edge up or down, bottom edge up or down. + * Unless there are conflicting hstems, the smallest movement is taken + * to minimize distortion. + * + * @order: + * hinting-engine + * no-stem-darkening[cff] + * darkening-parameters[cff] + * + */ + + + /************************************************************************** + * + * @property: + * hinting-engine + * + * @description: + * Thanks to Adobe, which contributed a new hinting (and parsing) + * engine, an application can select between `freetype' and `adobe' if + * compiled with CFF_CONFIG_OPTION_OLD_ENGINE. If this configuration + * macro isn't defined, `hinting-engine' does nothing. + * + * The default engine is `freetype' if CFF_CONFIG_OPTION_OLD_ENGINE is + * defined, and `adobe' otherwise. + * + * The following example code demonstrates how to select Adobe's hinting + * engine (omitting the error handling). + * + * { + * FT_Library library; + * FT_UInt hinting_engine = FT_CFF_HINTING_ADOBE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "cff", + * "hinting-engine", &hinting_engine ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + + /************************************************************************** + * + * @enum: + * FT_CFF_HINTING_XXX + * + * @description: + * A list of constants used for the @hinting-engine property to select + * the hinting engine for CFF fonts. + * + * @values: + * FT_CFF_HINTING_FREETYPE :: + * Use the old FreeType hinting engine. + * + * FT_CFF_HINTING_ADOBE :: + * Use the hinting engine contributed by Adobe. + * + */ +#define FT_CFF_HINTING_FREETYPE 0 +#define FT_CFF_HINTING_ADOBE 1 + + + /************************************************************************** + * + * @property: + * no-stem-darkening[cff] + * + * @description: + * By default, the Adobe CFF engine darkens stems at smaller sizes, + * regardless of hinting, to enhance contrast. This feature requires + * a rendering system with proper gamma correction. Setting this + * property, stem darkening gets switched off. + * + * Note that stem darkening is never applied if @FT_LOAD_NO_SCALE is set. + * + * { + * FT_Library library; + * FT_Bool no_stem_darkening = TRUE; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "cff", + * "no-stem-darkening", &no_stem_darkening ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + + /************************************************************************** + * + * @property: + * darkening-parameters[cff] + * + * @description: + * By default, the Adobe CFF engine darkens stems as follows (if the + * `no-stem-darkening' property isn't set): + * + * { + * stem width <= 0.5px: darkening amount = 0.4px + * stem width = 1px: darkening amount = 0.275px + * stem width = 1.667px: darkening amount = 0.275px + * stem width >= 2.333px: darkening amount = 0px + * } + * + * and piecewise linear in-between. At configuration time, these four + * control points can be set with the macro + * `CFF_CONFIG_OPTION_DARKENING_PARAMETERS'. At runtime, the control + * points can be changed using the `darkening-parameters' property, as + * the following example demonstrates. + * + * { + * FT_Library library; + * FT_Int darken_params[8] = { 500, 300, // x1, y1 + * 1000, 200, // x2, y2 + * 1500, 100, // x3, y3 + * 2000, 0 }; // x4, y4 + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "cff", + * "darkening-parameters", darken_params ); + * } + * + * The x~values give the stem width, and the y~values the darkening + * amount. The unit is 1000th of pixels. All coordinate values must be + * positive; the x~values must be monotonically increasing; the + * y~values must be monotonically decreasing and smaller than or + * equal to 500 (corresponding to half a pixel); the slope of each + * linear piece must be shallower than -1 (e.g., -.4). + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + /* */ + + +FT_END_HEADER + + +#endif /* FTCFFDRV_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftchapters.h b/foreign/freetype2/freetype/ftchapters.h new file mode 100644 index 0000000..ab43895 --- /dev/null +++ b/foreign/freetype2/freetype/ftchapters.h @@ -0,0 +1,135 @@ +/***************************************************************************/ +/* */ +/* This file defines the structure of the FreeType reference. */ +/* It is used by the python script that generates the HTML files. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* general_remarks */ +/* */ +/* <Title> */ +/* General Remarks */ +/* */ +/* <Sections> */ +/* header_inclusion */ +/* user_allocation */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* core_api */ +/* */ +/* <Title> */ +/* Core API */ +/* */ +/* <Sections> */ +/* version */ +/* basic_types */ +/* base_interface */ +/* glyph_variants */ +/* glyph_management */ +/* mac_specific */ +/* sizes_management */ +/* header_file_macros */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* format_specific */ +/* */ +/* <Title> */ +/* Format-Specific API */ +/* */ +/* <Sections> */ +/* multiple_masters */ +/* truetype_tables */ +/* type1_tables */ +/* sfnt_names */ +/* bdf_fonts */ +/* cid_fonts */ +/* pfr_fonts */ +/* winfnt_fonts */ +/* font_formats */ +/* gasp_table */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* module_specific */ +/* */ +/* <Title> */ +/* Controlling FreeType Modules */ +/* */ +/* <Sections> */ +/* auto_hinter */ +/* cff_driver */ +/* tt_driver */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* cache_subsystem */ +/* */ +/* <Title> */ +/* Cache Sub-System */ +/* */ +/* <Sections> */ +/* cache_subsystem */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* support_api */ +/* */ +/* <Title> */ +/* Support API */ +/* */ +/* <Sections> */ +/* computations */ +/* list_processing */ +/* outline_processing */ +/* quick_advance */ +/* bitmap_handling */ +/* raster */ +/* glyph_stroker */ +/* system_interface */ +/* module_management */ +/* gzip */ +/* lzw */ +/* bzip2 */ +/* lcd_filtering */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* <Chapter> */ +/* error_codes */ +/* */ +/* <Title> */ +/* Error Codes */ +/* */ +/* <Sections> */ +/* error_enumerations */ +/* error_code_values */ +/* */ +/***************************************************************************/ diff --git a/foreign/freetype2/freetype/ftcid.h b/foreign/freetype2/freetype/ftcid.h new file mode 100644 index 0000000..140f2f8 --- /dev/null +++ b/foreign/freetype2/freetype/ftcid.h @@ -0,0 +1,168 @@ +/***************************************************************************/ +/* */ +/* ftcid.h */ +/* */ +/* FreeType API for accessing CID font information (specification). */ +/* */ +/* Copyright 2007-2016 by */ +/* Dereg Clegg and Michael Toftdal. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTCID_H_ +#define FTCID_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* cid_fonts */ + /* */ + /* <Title> */ + /* CID Fonts */ + /* */ + /* <Abstract> */ + /* CID-keyed font specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of CID-keyed font specific */ + /* functions. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @function: + * FT_Get_CID_Registry_Ordering_Supplement + * + * @description: + * Retrieve the Registry/Ordering/Supplement triple (also known as the + * "R/O/S") from a CID-keyed font. + * + * @input: + * face :: + * A handle to the input face. + * + * @output: + * registry :: + * The registry, as a C~string, owned by the face. + * + * ordering :: + * The ordering, as a C~string, owned by the face. + * + * supplement :: + * The supplement. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with CID faces, returning an error + * otherwise. + * + * @since: + * 2.3.6 + */ + FT_EXPORT( FT_Error ) + FT_Get_CID_Registry_Ordering_Supplement( FT_Face face, + const char* *registry, + const char* *ordering, + FT_Int *supplement); + + + /********************************************************************** + * + * @function: + * FT_Get_CID_Is_Internally_CID_Keyed + * + * @description: + * Retrieve the type of the input face, CID keyed or not. In + * constrast to the @FT_IS_CID_KEYED macro this function returns + * successfully also for CID-keyed fonts in an SNFT wrapper. + * + * @input: + * face :: + * A handle to the input face. + * + * @output: + * is_cid :: + * The type of the face as an @FT_Bool. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with CID faces and OpenType fonts, + * returning an error otherwise. + * + * @since: + * 2.3.9 + */ + FT_EXPORT( FT_Error ) + FT_Get_CID_Is_Internally_CID_Keyed( FT_Face face, + FT_Bool *is_cid ); + + + /********************************************************************** + * + * @function: + * FT_Get_CID_From_Glyph_Index + * + * @description: + * Retrieve the CID of the input glyph index. + * + * @input: + * face :: + * A handle to the input face. + * + * glyph_index :: + * The input glyph index. + * + * @output: + * cid :: + * The CID as an @FT_UInt. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with CID faces and OpenType fonts, + * returning an error otherwise. + * + * @since: + * 2.3.9 + */ + FT_EXPORT( FT_Error ) + FT_Get_CID_From_Glyph_Index( FT_Face face, + FT_UInt glyph_index, + FT_UInt *cid ); + + /* */ + + +FT_END_HEADER + +#endif /* FTCID_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/fterrdef.h b/foreign/freetype2/freetype/fterrdef.h new file mode 100644 index 0000000..3f53dd5 --- /dev/null +++ b/foreign/freetype2/freetype/fterrdef.h @@ -0,0 +1,276 @@ +/***************************************************************************/ +/* */ +/* fterrdef.h */ +/* */ +/* FreeType error codes (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* error_code_values */ + /* */ + /* <Title> */ + /* Error Code Values */ + /* */ + /* <Abstract> */ + /* All possible error codes returned by FreeType functions. */ + /* */ + /* <Description> */ + /* The list below is taken verbatim from the file `fterrdef.h' */ + /* (loaded automatically by including `FT_FREETYPE_H'). The first */ + /* argument of the `FT_ERROR_DEF_' macro is the error label; by */ + /* default, the prefix `FT_Err_' gets added so that you get error */ + /* names like `FT_Err_Cannot_Open_Resource'. The second argument is */ + /* the error code, and the last argument an error string, which is not */ + /* used by FreeType. */ + /* */ + /* Within your application you should *only* use error names and */ + /* *never* its numeric values! The latter might (and actually do) */ + /* change in forthcoming FreeType versions. */ + /* */ + /* Macro `FT_NOERRORDEF_' defines `FT_Err_Ok', which is always zero. */ + /* See the `Error Enumerations' subsection how to automatically */ + /* generate a list of error strings. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Err_XXX */ + /* */ + /*************************************************************************/ + + /* generic errors */ + + FT_NOERRORDEF_( Ok, 0x00, + "no error" ) + + FT_ERRORDEF_( Cannot_Open_Resource, 0x01, + "cannot open resource" ) + FT_ERRORDEF_( Unknown_File_Format, 0x02, + "unknown file format" ) + FT_ERRORDEF_( Invalid_File_Format, 0x03, + "broken file" ) + FT_ERRORDEF_( Invalid_Version, 0x04, + "invalid FreeType version" ) + FT_ERRORDEF_( Lower_Module_Version, 0x05, + "module version is too low" ) + FT_ERRORDEF_( Invalid_Argument, 0x06, + "invalid argument" ) + FT_ERRORDEF_( Unimplemented_Feature, 0x07, + "unimplemented feature" ) + FT_ERRORDEF_( Invalid_Table, 0x08, + "broken table" ) + FT_ERRORDEF_( Invalid_Offset, 0x09, + "broken offset within table" ) + FT_ERRORDEF_( Array_Too_Large, 0x0A, + "array allocation size too large" ) + FT_ERRORDEF_( Missing_Module, 0x0B, + "missing module" ) + FT_ERRORDEF_( Missing_Property, 0x0C, + "missing property" ) + + /* glyph/character errors */ + + FT_ERRORDEF_( Invalid_Glyph_Index, 0x10, + "invalid glyph index" ) + FT_ERRORDEF_( Invalid_Character_Code, 0x11, + "invalid character code" ) + FT_ERRORDEF_( Invalid_Glyph_Format, 0x12, + "unsupported glyph image format" ) + FT_ERRORDEF_( Cannot_Render_Glyph, 0x13, + "cannot render this glyph format" ) + FT_ERRORDEF_( Invalid_Outline, 0x14, + "invalid outline" ) + FT_ERRORDEF_( Invalid_Composite, 0x15, + "invalid composite glyph" ) + FT_ERRORDEF_( Too_Many_Hints, 0x16, + "too many hints" ) + FT_ERRORDEF_( Invalid_Pixel_Size, 0x17, + "invalid pixel size" ) + + /* handle errors */ + + FT_ERRORDEF_( Invalid_Handle, 0x20, + "invalid object handle" ) + FT_ERRORDEF_( Invalid_Library_Handle, 0x21, + "invalid library handle" ) + FT_ERRORDEF_( Invalid_Driver_Handle, 0x22, + "invalid module handle" ) + FT_ERRORDEF_( Invalid_Face_Handle, 0x23, + "invalid face handle" ) + FT_ERRORDEF_( Invalid_Size_Handle, 0x24, + "invalid size handle" ) + FT_ERRORDEF_( Invalid_Slot_Handle, 0x25, + "invalid glyph slot handle" ) + FT_ERRORDEF_( Invalid_CharMap_Handle, 0x26, + "invalid charmap handle" ) + FT_ERRORDEF_( Invalid_Cache_Handle, 0x27, + "invalid cache manager handle" ) + FT_ERRORDEF_( Invalid_Stream_Handle, 0x28, + "invalid stream handle" ) + + /* driver errors */ + + FT_ERRORDEF_( Too_Many_Drivers, 0x30, + "too many modules" ) + FT_ERRORDEF_( Too_Many_Extensions, 0x31, + "too many extensions" ) + + /* memory errors */ + + FT_ERRORDEF_( Out_Of_Memory, 0x40, + "out of memory" ) + FT_ERRORDEF_( Unlisted_Object, 0x41, + "unlisted object" ) + + /* stream errors */ + + FT_ERRORDEF_( Cannot_Open_Stream, 0x51, + "cannot open stream" ) + FT_ERRORDEF_( Invalid_Stream_Seek, 0x52, + "invalid stream seek" ) + FT_ERRORDEF_( Invalid_Stream_Skip, 0x53, + "invalid stream skip" ) + FT_ERRORDEF_( Invalid_Stream_Read, 0x54, + "invalid stream read" ) + FT_ERRORDEF_( Invalid_Stream_Operation, 0x55, + "invalid stream operation" ) + FT_ERRORDEF_( Invalid_Frame_Operation, 0x56, + "invalid frame operation" ) + FT_ERRORDEF_( Nested_Frame_Access, 0x57, + "nested frame access" ) + FT_ERRORDEF_( Invalid_Frame_Read, 0x58, + "invalid frame read" ) + + /* raster errors */ + + FT_ERRORDEF_( Raster_Uninitialized, 0x60, + "raster uninitialized" ) + FT_ERRORDEF_( Raster_Corrupted, 0x61, + "raster corrupted" ) + FT_ERRORDEF_( Raster_Overflow, 0x62, + "raster overflow" ) + FT_ERRORDEF_( Raster_Negative_Height, 0x63, + "negative height while rastering" ) + + /* cache errors */ + + FT_ERRORDEF_( Too_Many_Caches, 0x70, + "too many registered caches" ) + + /* TrueType and SFNT errors */ + + FT_ERRORDEF_( Invalid_Opcode, 0x80, + "invalid opcode" ) + FT_ERRORDEF_( Too_Few_Arguments, 0x81, + "too few arguments" ) + FT_ERRORDEF_( Stack_Overflow, 0x82, + "stack overflow" ) + FT_ERRORDEF_( Code_Overflow, 0x83, + "code overflow" ) + FT_ERRORDEF_( Bad_Argument, 0x84, + "bad argument" ) + FT_ERRORDEF_( Divide_By_Zero, 0x85, + "division by zero" ) + FT_ERRORDEF_( Invalid_Reference, 0x86, + "invalid reference" ) + FT_ERRORDEF_( Debug_OpCode, 0x87, + "found debug opcode" ) + FT_ERRORDEF_( ENDF_In_Exec_Stream, 0x88, + "found ENDF opcode in execution stream" ) + FT_ERRORDEF_( Nested_DEFS, 0x89, + "nested DEFS" ) + FT_ERRORDEF_( Invalid_CodeRange, 0x8A, + "invalid code range" ) + FT_ERRORDEF_( Execution_Too_Long, 0x8B, + "execution context too long" ) + FT_ERRORDEF_( Too_Many_Function_Defs, 0x8C, + "too many function definitions" ) + FT_ERRORDEF_( Too_Many_Instruction_Defs, 0x8D, + "too many instruction definitions" ) + FT_ERRORDEF_( Table_Missing, 0x8E, + "SFNT font table missing" ) + FT_ERRORDEF_( Horiz_Header_Missing, 0x8F, + "horizontal header (hhea) table missing" ) + FT_ERRORDEF_( Locations_Missing, 0x90, + "locations (loca) table missing" ) + FT_ERRORDEF_( Name_Table_Missing, 0x91, + "name table missing" ) + FT_ERRORDEF_( CMap_Table_Missing, 0x92, + "character map (cmap) table missing" ) + FT_ERRORDEF_( Hmtx_Table_Missing, 0x93, + "horizontal metrics (hmtx) table missing" ) + FT_ERRORDEF_( Post_Table_Missing, 0x94, + "PostScript (post) table missing" ) + FT_ERRORDEF_( Invalid_Horiz_Metrics, 0x95, + "invalid horizontal metrics" ) + FT_ERRORDEF_( Invalid_CharMap_Format, 0x96, + "invalid character map (cmap) format" ) + FT_ERRORDEF_( Invalid_PPem, 0x97, + "invalid ppem value" ) + FT_ERRORDEF_( Invalid_Vert_Metrics, 0x98, + "invalid vertical metrics" ) + FT_ERRORDEF_( Could_Not_Find_Context, 0x99, + "could not find context" ) + FT_ERRORDEF_( Invalid_Post_Table_Format, 0x9A, + "invalid PostScript (post) table format" ) + FT_ERRORDEF_( Invalid_Post_Table, 0x9B, + "invalid PostScript (post) table" ) + + /* CFF, CID, and Type 1 errors */ + + FT_ERRORDEF_( Syntax_Error, 0xA0, + "opcode syntax error" ) + FT_ERRORDEF_( Stack_Underflow, 0xA1, + "argument stack underflow" ) + FT_ERRORDEF_( Ignore, 0xA2, + "ignore" ) + FT_ERRORDEF_( No_Unicode_Glyph_Name, 0xA3, + "no Unicode glyph name found" ) + FT_ERRORDEF_( Glyph_Too_Big, 0xA4, + "glyph too big for hinting" ) + + /* BDF errors */ + + FT_ERRORDEF_( Missing_Startfont_Field, 0xB0, + "`STARTFONT' field missing" ) + FT_ERRORDEF_( Missing_Font_Field, 0xB1, + "`FONT' field missing" ) + FT_ERRORDEF_( Missing_Size_Field, 0xB2, + "`SIZE' field missing" ) + FT_ERRORDEF_( Missing_Fontboundingbox_Field, 0xB3, + "`FONTBOUNDINGBOX' field missing" ) + FT_ERRORDEF_( Missing_Chars_Field, 0xB4, + "`CHARS' field missing" ) + FT_ERRORDEF_( Missing_Startchar_Field, 0xB5, + "`STARTCHAR' field missing" ) + FT_ERRORDEF_( Missing_Encoding_Field, 0xB6, + "`ENCODING' field missing" ) + FT_ERRORDEF_( Missing_Bbx_Field, 0xB7, + "`BBX' field missing" ) + FT_ERRORDEF_( Bbx_Too_Big, 0xB8, + "`BBX' too big" ) + FT_ERRORDEF_( Corrupted_Font_Header, 0xB9, + "Font header corrupted or missing fields" ) + FT_ERRORDEF_( Corrupted_Font_Glyphs, 0xBA, + "Font glyphs corrupted or missing fields" ) + + /* */ + + +/* END */ diff --git a/foreign/freetype2/freetype/fterrors.h b/foreign/freetype2/freetype/fterrors.h new file mode 100644 index 0000000..e15bfb0 --- /dev/null +++ b/foreign/freetype2/freetype/fterrors.h @@ -0,0 +1,226 @@ +/***************************************************************************/ +/* */ +/* fterrors.h */ +/* */ +/* FreeType error code handling (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* error_enumerations */ + /* */ + /* <Title> */ + /* Error Enumerations */ + /* */ + /* <Abstract> */ + /* How to handle errors and error strings. */ + /* */ + /* <Description> */ + /* The header file `fterrors.h' (which is automatically included by */ + /* `freetype.h' defines the handling of FreeType's enumeration */ + /* constants. It can also be used to generate error message strings */ + /* with a small macro trick explained below. */ + /* */ + /* *Error* *Formats* */ + /* */ + /* The configuration macro FT_CONFIG_OPTION_USE_MODULE_ERRORS can be */ + /* defined in `ftoption.h' in order to make the higher byte indicate */ + /* the module where the error has happened (this is not compatible */ + /* with standard builds of FreeType 2, however). See the file */ + /* `ftmoderr.h' for more details. */ + /* */ + /* *Error* *Message* *Strings* */ + /* */ + /* Error definitions are set up with special macros that allow client */ + /* applications to build a table of error message strings. The */ + /* strings are not included in a normal build of FreeType 2 to */ + /* save space (most client applications do not use them). */ + /* */ + /* To do so, you have to define the following macros before including */ + /* this file. */ + /* */ + /* { */ + /* FT_ERROR_START_LIST */ + /* } */ + /* */ + /* This macro is called before anything else to define the start of */ + /* the error list. It is followed by several FT_ERROR_DEF calls. */ + /* */ + /* { */ + /* FT_ERROR_DEF( e, v, s ) */ + /* } */ + /* */ + /* This macro is called to define one single error. `e' is the error */ + /* code identifier (e.g., `Invalid_Argument'), `v' is the error's */ + /* numerical value, and `s' is the corresponding error string. */ + /* */ + /* { */ + /* FT_ERROR_END_LIST */ + /* } */ + /* */ + /* This macro ends the list. */ + /* */ + /* Additionally, you have to undefine `FTERRORS_H_' before #including */ + /* this file. */ + /* */ + /* Here is a simple example. */ + /* */ + /* { */ + /* #undef FTERRORS_H_ */ + /* #define FT_ERRORDEF( e, v, s ) { e, s }, */ + /* #define FT_ERROR_START_LIST { */ + /* #define FT_ERROR_END_LIST { 0, NULL } }; */ + /* */ + /* const struct */ + /* { */ + /* int err_code; */ + /* const char* err_msg; */ + /* } ft_errors[] = */ + /* */ + /* #include FT_ERRORS_H */ + /* } */ + /* */ + /* Note that `FT_Err_Ok' is _not_ defined with `FT_ERRORDEF' but with */ + /* `FT_NOERRORDEF'; it is always zero. */ + /* */ + /*************************************************************************/ + + /* */ + + /* In previous FreeType versions we used `__FTERRORS_H__'. However, */ + /* using two successive underscores in a non-system symbol name */ + /* violates the C (and C++) standard, so it was changed to the */ + /* current form. In spite of this, we have to make */ + /* */ + /* #undefine __FTERRORS_H__ */ + /* */ + /* work for backwards compatibility. */ + /* */ +#if !( defined( FTERRORS_H_ ) && defined ( __FTERRORS_H__ ) ) +#define FTERRORS_H_ +#define __FTERRORS_H__ + + + /* include module base error codes */ +#include FT_MODULE_ERRORS_H + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + + + /* FT_ERR_PREFIX is used as a prefix for error identifiers. */ + /* By default, we use `FT_Err_'. */ + /* */ +#ifndef FT_ERR_PREFIX +#define FT_ERR_PREFIX FT_Err_ +#endif + + + /* FT_ERR_BASE is used as the base for module-specific errors. */ + /* */ +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS + +#ifndef FT_ERR_BASE +#define FT_ERR_BASE FT_Mod_Err_Base +#endif + +#else + +#undef FT_ERR_BASE +#define FT_ERR_BASE 0 + +#endif /* FT_CONFIG_OPTION_USE_MODULE_ERRORS */ + + + /* If FT_ERRORDEF is not defined, we need to define a simple */ + /* enumeration type. */ + /* */ +#ifndef FT_ERRORDEF + +#define FT_ERRORDEF( e, v, s ) e = v, +#define FT_ERROR_START_LIST enum { +#define FT_ERROR_END_LIST FT_ERR_CAT( FT_ERR_PREFIX, Max ) }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_ERRORDEF */ + + + /* this macro is used to define an error */ +#define FT_ERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v + FT_ERR_BASE, s ) + + /* this is only used for <module>_Err_Ok, which must be 0! */ +#define FT_NOERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v, s ) + + +#ifdef FT_ERROR_START_LIST + FT_ERROR_START_LIST +#endif + + + /* now include the error codes */ +#include FT_ERROR_DEFINITIONS_H + + +#ifdef FT_ERROR_END_LIST + FT_ERROR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SIMPLE CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_ERROR_START_LIST +#undef FT_ERROR_END_LIST + +#undef FT_ERRORDEF +#undef FT_ERRORDEF_ +#undef FT_NOERRORDEF_ + +#undef FT_NEED_EXTERN_C +#undef FT_ERR_BASE + + /* FT_ERR_PREFIX is needed internally */ +#ifndef FT2_BUILD_LIBRARY +#undef FT_ERR_PREFIX +#endif + +#endif /* !(FTERRORS_H_ && __FTERRORS_H__) */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftfntfmt.h b/foreign/freetype2/freetype/ftfntfmt.h new file mode 100644 index 0000000..bd42324 --- /dev/null +++ b/foreign/freetype2/freetype/ftfntfmt.h @@ -0,0 +1,95 @@ +/***************************************************************************/ +/* */ +/* ftfntfmt.h */ +/* */ +/* Support functions for font formats. */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTFNTFMT_H_ +#define FTFNTFMT_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* font_formats */ + /* */ + /* <Title> */ + /* Font Formats */ + /* */ + /* <Abstract> */ + /* Getting the font format. */ + /* */ + /* <Description> */ + /* The single function in this section can be used to get the font */ + /* format. Note that this information is not needed normally; */ + /* however, there are special cases (like in PDF devices) where it is */ + /* important to differentiate, in spite of FreeType's uniform API. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Font_Format */ + /* */ + /* <Description> */ + /* Return a string describing the format of a given face. Possible */ + /* values are `TrueType', `Type~1', `BDF', `PCF', `Type~42', */ + /* `CID~Type~1', `CFF', `PFR', and `Windows~FNT'. */ + /* */ + /* The return value is suitable to be used as an X11 FONT_PROPERTY. */ + /* */ + /* <Input> */ + /* face :: */ + /* Input face handle. */ + /* */ + /* <Return> */ + /* Font format string. NULL in case of error. */ + /* */ + /* <Note> */ + /* A deprecated name for the same function is */ + /* `FT_Get_X11_Font_Format'. */ + /* */ + FT_EXPORT( const char* ) + FT_Get_Font_Format( FT_Face face ); + + + /* deprecated */ + FT_EXPORT( const char* ) + FT_Get_X11_Font_Format( FT_Face face ); + + + /* */ + + +FT_END_HEADER + +#endif /* FTFNTFMT_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftgasp.h b/foreign/freetype2/freetype/ftgasp.h new file mode 100644 index 0000000..3f5b3bc --- /dev/null +++ b/foreign/freetype2/freetype/ftgasp.h @@ -0,0 +1,129 @@ +/***************************************************************************/ +/* */ +/* ftgasp.h */ +/* */ +/* Access of TrueType's `gasp' table (specification). */ +/* */ +/* Copyright 2007-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGASP_H_ +#define FTGASP_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + + /*************************************************************************** + * + * @section: + * gasp_table + * + * @title: + * Gasp Table + * + * @abstract: + * Retrieving TrueType `gasp' table entries. + * + * @description: + * The function @FT_Get_Gasp can be used to query a TrueType or OpenType + * font for specific entries in its `gasp' table, if any. This is + * mainly useful when implementing native TrueType hinting with the + * bytecode interpreter to duplicate the Windows text rendering results. + */ + + /************************************************************************* + * + * @enum: + * FT_GASP_XXX + * + * @description: + * A list of values and/or bit-flags returned by the @FT_Get_Gasp + * function. + * + * @values: + * FT_GASP_NO_TABLE :: + * This special value means that there is no GASP table in this face. + * It is up to the client to decide what to do. + * + * FT_GASP_DO_GRIDFIT :: + * Grid-fitting and hinting should be performed at the specified ppem. + * This *really* means TrueType bytecode interpretation. If this bit + * is not set, no hinting gets applied. + * + * FT_GASP_DO_GRAY :: + * Anti-aliased rendering should be performed at the specified ppem. + * If not set, do monochrome rendering. + * + * FT_GASP_SYMMETRIC_SMOOTHING :: + * If set, smoothing along multiple axes must be used with ClearType. + * + * FT_GASP_SYMMETRIC_GRIDFIT :: + * Grid-fitting must be used with ClearType's symmetric smoothing. + * + * @note: + * The bit-flags `FT_GASP_DO_GRIDFIT' and `FT_GASP_DO_GRAY' are to be + * used for standard font rasterization only. Independently of that, + * `FT_GASP_SYMMETRIC_SMOOTHING' and `FT_GASP_SYMMETRIC_GRIDFIT' are to + * be used if ClearType is enabled (and `FT_GASP_DO_GRIDFIT' and + * `FT_GASP_DO_GRAY' are consequently ignored). + * + * `ClearType' is Microsoft's implementation of LCD rendering, partly + * protected by patents. + * + * @since: + * 2.3.0 + */ +#define FT_GASP_NO_TABLE -1 +#define FT_GASP_DO_GRIDFIT 0x01 +#define FT_GASP_DO_GRAY 0x02 +#define FT_GASP_SYMMETRIC_SMOOTHING 0x08 +#define FT_GASP_SYMMETRIC_GRIDFIT 0x10 + + + /************************************************************************* + * + * @func: + * FT_Get_Gasp + * + * @description: + * Read the `gasp' table from a TrueType or OpenType font file and + * return the entry corresponding to a given character pixel size. + * + * @input: + * face :: The source face handle. + * ppem :: The vertical character pixel size. + * + * @return: + * Bit flags (see @FT_GASP_XXX), or @FT_GASP_NO_TABLE if there is no + * `gasp' table in the face. + * + * @since: + * 2.3.0 + */ + FT_EXPORT( FT_Int ) + FT_Get_Gasp( FT_Face face, + FT_UInt ppem ); + + /* */ + + +#endif /* FTGASP_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftglyph.h b/foreign/freetype2/freetype/ftglyph.h new file mode 100644 index 0000000..d9840a8 --- /dev/null +++ b/foreign/freetype2/freetype/ftglyph.h @@ -0,0 +1,605 @@ +/***************************************************************************/ +/* */ +/* ftglyph.h */ +/* */ +/* FreeType convenience functions to handle glyphs (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file contains the definition of several convenience functions */ + /* that can be used by client applications to easily retrieve glyph */ + /* bitmaps and outlines from a given face. */ + /* */ + /* These functions should be optional if you are writing a font server */ + /* or text layout engine on top of FreeType. However, they are pretty */ + /* handy for many other simple uses of the library. */ + /* */ + /*************************************************************************/ + + +#ifndef FTGLYPH_H_ +#define FTGLYPH_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* glyph_management */ + /* */ + /* <Title> */ + /* Glyph Management */ + /* */ + /* <Abstract> */ + /* Generic interface to manage individual glyph data. */ + /* */ + /* <Description> */ + /* This section contains definitions used to manage glyph data */ + /* through generic FT_Glyph objects. Each of them can contain a */ + /* bitmap, a vector outline, or even images in other formats. */ + /* */ + /*************************************************************************/ + + + /* forward declaration to a private type */ + typedef struct FT_Glyph_Class_ FT_Glyph_Class; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Glyph */ + /* */ + /* <Description> */ + /* Handle to an object used to model generic glyph images. It is a */ + /* pointer to the @FT_GlyphRec structure and can contain a glyph */ + /* bitmap or pointer. */ + /* */ + /* <Note> */ + /* Glyph objects are not owned by the library. You must thus release */ + /* them manually (through @FT_Done_Glyph) _before_ calling */ + /* @FT_Done_FreeType. */ + /* */ + typedef struct FT_GlyphRec_* FT_Glyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphRec */ + /* */ + /* <Description> */ + /* The root glyph structure contains a given glyph image plus its */ + /* advance width in 16.16 fixed-point format. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library object. */ + /* */ + /* clazz :: A pointer to the glyph's class. Private. */ + /* */ + /* format :: The format of the glyph's image. */ + /* */ + /* advance :: A 16.16 vector that gives the glyph's advance width. */ + /* */ + typedef struct FT_GlyphRec_ + { + FT_Library library; + const FT_Glyph_Class* clazz; + FT_Glyph_Format format; + FT_Vector advance; + + } FT_GlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_BitmapGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model a bitmap glyph image. This is */ + /* a sub-class of @FT_Glyph, and a pointer to @FT_BitmapGlyphRec. */ + /* */ + typedef struct FT_BitmapGlyphRec_* FT_BitmapGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BitmapGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for bitmap glyph images. This really is a */ + /* `sub-class' of @FT_GlyphRec. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Glyph fields. */ + /* */ + /* left :: The left-side bearing, i.e., the horizontal distance */ + /* from the current pen position to the left border of the */ + /* glyph bitmap. */ + /* */ + /* top :: The top-side bearing, i.e., the vertical distance from */ + /* the current pen position to the top border of the glyph */ + /* bitmap. This distance is positive for upwards~y! */ + /* */ + /* bitmap :: A descriptor for the bitmap. */ + /* */ + /* <Note> */ + /* You can typecast an @FT_Glyph to @FT_BitmapGlyph if you have */ + /* `glyph->format == FT_GLYPH_FORMAT_BITMAP'. This lets you access */ + /* the bitmap's contents easily. */ + /* */ + /* The corresponding pixel buffer is always owned by @FT_BitmapGlyph */ + /* and is thus created and destroyed with it. */ + /* */ + typedef struct FT_BitmapGlyphRec_ + { + FT_GlyphRec root; + FT_Int left; + FT_Int top; + FT_Bitmap bitmap; + + } FT_BitmapGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_OutlineGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model an outline glyph image. This */ + /* is a sub-class of @FT_Glyph, and a pointer to @FT_OutlineGlyphRec. */ + /* */ + typedef struct FT_OutlineGlyphRec_* FT_OutlineGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_OutlineGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for outline (vectorial) glyph images. This */ + /* really is a `sub-class' of @FT_GlyphRec. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Glyph fields. */ + /* */ + /* outline :: A descriptor for the outline. */ + /* */ + /* <Note> */ + /* You can typecast an @FT_Glyph to @FT_OutlineGlyph if you have */ + /* `glyph->format == FT_GLYPH_FORMAT_OUTLINE'. This lets you access */ + /* the outline's content easily. */ + /* */ + /* As the outline is extracted from a glyph slot, its coordinates are */ + /* expressed normally in 26.6 pixels, unless the flag */ + /* @FT_LOAD_NO_SCALE was used in @FT_Load_Glyph() or @FT_Load_Char(). */ + /* */ + /* The outline's tables are always owned by the object and are */ + /* destroyed with it. */ + /* */ + typedef struct FT_OutlineGlyphRec_ + { + FT_GlyphRec root; + FT_Outline outline; + + } FT_OutlineGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph */ + /* */ + /* <Description> */ + /* A function used to extract a glyph image from a slot. Note that */ + /* the created @FT_Glyph object must be released with @FT_Done_Glyph. */ + /* */ + /* <Input> */ + /* slot :: A handle to the source glyph slot. */ + /* */ + /* <Output> */ + /* aglyph :: A handle to the glyph object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph( FT_GlyphSlot slot, + FT_Glyph *aglyph ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Copy */ + /* */ + /* <Description> */ + /* A function used to copy a glyph image. Note that the created */ + /* @FT_Glyph object must be released with @FT_Done_Glyph. */ + /* */ + /* <Input> */ + /* source :: A handle to the source glyph object. */ + /* */ + /* <Output> */ + /* target :: A handle to the target glyph object. 0~in case of */ + /* error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Copy( FT_Glyph source, + FT_Glyph *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Transform */ + /* */ + /* <Description> */ + /* Transform a glyph image if its format is scalable. */ + /* */ + /* <InOut> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to a 2x2 matrix to apply. */ + /* */ + /* delta :: A pointer to a 2d vector to apply. Coordinates are */ + /* expressed in 1/64th of a pixel. */ + /* */ + /* <Return> */ + /* FreeType error code (if not 0, the glyph format is not scalable). */ + /* */ + /* <Note> */ + /* The 2x2 transformation matrix is also applied to the glyph's */ + /* advance vector. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Transform( FT_Glyph glyph, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_BBox_Mode */ + /* */ + /* <Description> */ + /* The mode how the values of @FT_Glyph_Get_CBox are returned. */ + /* */ + /* <Values> */ + /* FT_GLYPH_BBOX_UNSCALED :: */ + /* Return unscaled font units. */ + /* */ + /* FT_GLYPH_BBOX_SUBPIXELS :: */ + /* Return unfitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_GRIDFIT :: */ + /* Return grid-fitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_TRUNCATE :: */ + /* Return coordinates in integer pixels. */ + /* */ + /* FT_GLYPH_BBOX_PIXELS :: */ + /* Return grid-fitted pixel coordinates. */ + /* */ + typedef enum FT_Glyph_BBox_Mode_ + { + FT_GLYPH_BBOX_UNSCALED = 0, + FT_GLYPH_BBOX_SUBPIXELS = 0, + FT_GLYPH_BBOX_GRIDFIT = 1, + FT_GLYPH_BBOX_TRUNCATE = 2, + FT_GLYPH_BBOX_PIXELS = 3 + + } FT_Glyph_BBox_Mode; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Glyph_BBox_Mode' values instead */ +#define ft_glyph_bbox_unscaled FT_GLYPH_BBOX_UNSCALED +#define ft_glyph_bbox_subpixels FT_GLYPH_BBOX_SUBPIXELS +#define ft_glyph_bbox_gridfit FT_GLYPH_BBOX_GRIDFIT +#define ft_glyph_bbox_truncate FT_GLYPH_BBOX_TRUNCATE +#define ft_glyph_bbox_pixels FT_GLYPH_BBOX_PIXELS + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Get_CBox */ + /* */ + /* <Description> */ + /* Return a glyph's `control box'. The control box encloses all the */ + /* outline's points, including Bézier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* that contains Bézier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component, which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the source glyph object. */ + /* */ + /* mode :: The mode that indicates how to interpret the returned */ + /* bounding box values. */ + /* */ + /* <Output> */ + /* acbox :: The glyph coordinate bounding box. Coordinates are */ + /* expressed in 1/64th of pixels if it is grid-fitted. */ + /* */ + /* <Note> */ + /* Coordinates are relative to the glyph origin, using the y~upwards */ + /* convention. */ + /* */ + /* If the glyph has been loaded with @FT_LOAD_NO_SCALE, `bbox_mode' */ + /* must be set to @FT_GLYPH_BBOX_UNSCALED to get unscaled font */ + /* units in 26.6 pixel format. The value @FT_GLYPH_BBOX_SUBPIXELS */ + /* is another name for this constant. */ + /* */ + /* If the font is tricky and the glyph has been loaded with */ + /* @FT_LOAD_NO_SCALE, the resulting CBox is meaningless. To get */ + /* reasonable values for the CBox it is necessary to load the glyph */ + /* at a large ppem value (so that the hinting instructions can */ + /* properly shift and scale the subglyphs), then extracting the CBox, */ + /* which can be eventually converted back to font units. */ + /* */ + /* Note that the maximum coordinates are exclusive, which means that */ + /* one can compute the width and height of the glyph image (be it in */ + /* integer or 26.6 pixels) as: */ + /* */ + /* { */ + /* width = bbox.xMax - bbox.xMin; */ + /* height = bbox.yMax - bbox.yMin; */ + /* } */ + /* */ + /* Note also that for 26.6 coordinates, if `bbox_mode' is set to */ + /* @FT_GLYPH_BBOX_GRIDFIT, the coordinates will also be grid-fitted, */ + /* which corresponds to: */ + /* */ + /* { */ + /* bbox.xMin = FLOOR(bbox.xMin); */ + /* bbox.yMin = FLOOR(bbox.yMin); */ + /* bbox.xMax = CEILING(bbox.xMax); */ + /* bbox.yMax = CEILING(bbox.yMax); */ + /* } */ + /* */ + /* To get the bbox in pixel coordinates, set `bbox_mode' to */ + /* @FT_GLYPH_BBOX_TRUNCATE. */ + /* */ + /* To get the bbox in grid-fitted pixel coordinates, set `bbox_mode' */ + /* to @FT_GLYPH_BBOX_PIXELS. */ + /* */ + FT_EXPORT( void ) + FT_Glyph_Get_CBox( FT_Glyph glyph, + FT_UInt bbox_mode, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_To_Bitmap */ + /* */ + /* <Description> */ + /* Convert a given glyph object to a bitmap glyph object. */ + /* */ + /* <InOut> */ + /* the_glyph :: A pointer to a handle to the target glyph. */ + /* */ + /* <Input> */ + /* render_mode :: An enumeration that describes how the data is */ + /* rendered. */ + /* */ + /* origin :: A pointer to a vector used to translate the glyph */ + /* image before rendering. Can be~0 (if no */ + /* translation). The origin is expressed in */ + /* 26.6 pixels. */ + /* */ + /* destroy :: A boolean that indicates that the original glyph */ + /* image should be destroyed by this function. It is */ + /* never destroyed in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function does nothing if the glyph format isn't scalable. */ + /* */ + /* The glyph image is translated with the `origin' vector before */ + /* rendering. */ + /* */ + /* The first parameter is a pointer to an @FT_Glyph handle, that will */ + /* be _replaced_ by this function (with newly allocated data). */ + /* Typically, you would use (omitting error handling): */ + /* */ + /* */ + /* { */ + /* FT_Glyph glyph; */ + /* FT_BitmapGlyph glyph_bitmap; */ + /* */ + /* */ + /* // load glyph */ + /* error = FT_Load_Char( face, glyph_index, FT_LOAD_DEFAUT ); */ + /* */ + /* // extract glyph image */ + /* error = FT_Get_Glyph( face->glyph, &glyph ); */ + /* */ + /* // convert to a bitmap (default render mode + destroying old) */ + /* if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) */ + /* { */ + /* error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, */ + /* 0, 1 ); */ + /* if ( error ) // `glyph' unchanged */ + /* ... */ + /* } */ + /* */ + /* // access bitmap content by typecasting */ + /* glyph_bitmap = (FT_BitmapGlyph)glyph; */ + /* */ + /* // do funny stuff with it, like blitting/drawing */ + /* ... */ + /* */ + /* // discard glyph image (bitmap or not) */ + /* FT_Done_Glyph( glyph ); */ + /* } */ + /* */ + /* */ + /* Here another example, again without error handling: */ + /* */ + /* */ + /* { */ + /* FT_Glyph glyphs[MAX_GLYPHS] */ + /* */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* error = FT_Load_Glyph( face, idx, FT_LOAD_DEFAULT ) || */ + /* FT_Get_Glyph ( face->glyph, &glyph[idx] ); */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* { */ + /* FT_Glyph bitmap = glyphs[idx]; */ + /* */ + /* */ + /* ... */ + /* */ + /* // after this call, `bitmap' no longer points into */ + /* // the `glyphs' array (and the old value isn't destroyed) */ + /* FT_Glyph_To_Bitmap( &bitmap, FT_RENDER_MODE_MONO, 0, 0 ); */ + /* */ + /* ... */ + /* */ + /* FT_Done_Glyph( bitmap ); */ + /* } */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* FT_Done_Glyph( glyphs[idx] ); */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_To_Bitmap( FT_Glyph* the_glyph, + FT_Render_Mode render_mode, + FT_Vector* origin, + FT_Bool destroy ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Glyph */ + /* */ + /* <Description> */ + /* Destroy a given glyph. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + FT_EXPORT( void ) + FT_Done_Glyph( FT_Glyph glyph ); + + /* */ + + + /* other helpful functions */ + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Multiply */ + /* */ + /* <Description> */ + /* Perform the matrix operation `b = a*b'. */ + /* */ + /* <Input> */ + /* a :: A pointer to matrix `a'. */ + /* */ + /* <InOut> */ + /* b :: A pointer to matrix `b'. */ + /* */ + /* <Note> */ + /* The result is undefined if either `a' or `b' is zero. */ + /* */ + FT_EXPORT( void ) + FT_Matrix_Multiply( const FT_Matrix* a, + FT_Matrix* b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Invert */ + /* */ + /* <Description> */ + /* Invert a 2x2 matrix. Return an error if it can't be inverted. */ + /* */ + /* <InOut> */ + /* matrix :: A pointer to the target matrix. Remains untouched in */ + /* case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Matrix_Invert( FT_Matrix* matrix ); + + /* */ + + +FT_END_HEADER + +#endif /* FTGLYPH_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/foreign/freetype2/freetype/ftgxval.h b/foreign/freetype2/freetype/ftgxval.h new file mode 100644 index 0000000..a58e86a --- /dev/null +++ b/foreign/freetype2/freetype/ftgxval.h @@ -0,0 +1,357 @@ +/***************************************************************************/ +/* */ +/* ftgxval.h */ +/* */ +/* FreeType API for validating TrueTypeGX/AAT tables (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* Masatake YAMATO, Redhat K.K, */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/***************************************************************************/ +/* */ +/* gxvalid is derived from both gxlayout module and otvalid module. */ +/* Development of gxlayout is supported by the Information-technology */ +/* Promotion Agency(IPA), Japan. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGXVAL_H_ +#define FTGXVAL_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* gx_validation */ + /* */ + /* <Title> */ + /* TrueTypeGX/AAT Validation */ + /* */ + /* <Abstract> */ + /* An API to validate TrueTypeGX/AAT tables. */ + /* */ + /* <Description> */ + /* This section contains the declaration of functions to validate */ + /* some TrueTypeGX tables (feat, mort, morx, bsln, just, kern, opbd, */ + /* trak, prop, lcar). */ + /* */ + /* <Order> */ + /* FT_TrueTypeGX_Validate */ + /* FT_TrueTypeGX_Free */ + /* */ + /* FT_ClassicKern_Validate */ + /* FT_ClassicKern_Free */ + /* */ + /* FT_VALIDATE_GX_LENGTH */ + /* FT_VALIDATE_GXXXX */ + /* FT_VALIDATE_CKERNXXX */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* */ + /* Warning: Use FT_VALIDATE_XXX to validate a table. */ + /* Following definitions are for gxvalid developers. */ + /* */ + /* */ + /*************************************************************************/ + +#define FT_VALIDATE_feat_INDEX 0 +#define FT_VALIDATE_mort_INDEX 1 +#define FT_VALIDATE_morx_INDEX 2 +#define FT_VALIDATE_bsln_INDEX 3 +#define FT_VALIDATE_just_INDEX 4 +#define FT_VALIDATE_kern_INDEX 5 +#define FT_VALIDATE_opbd_INDEX 6 +#define FT_VALIDATE_trak_INDEX 7 +#define FT_VALIDATE_prop_INDEX 8 +#define FT_VALIDATE_lcar_INDEX 9 +#define FT_VALIDATE_GX_LAST_INDEX FT_VALIDATE_lcar_INDEX + + + /************************************************************************* + * + * @macro: + * FT_VALIDATE_GX_LENGTH + * + * @description: + * The number of tables checked in this module. Use it as a parameter + * for the `table-length' argument of function @FT_TrueTypeGX_Validate. + */ +#define FT_VALIDATE_GX_LENGTH (FT_VALIDATE_GX_LAST_INDEX + 1) + + /* */ + + /* Up to 0x1000 is used by otvalid. + Ox2xxx is reserved for feature OT extension. */ +#define FT_VALIDATE_GX_START 0x4000 +#define FT_VALIDATE_GX_BITFIELD( tag ) \ + ( FT_VALIDATE_GX_START << FT_VALIDATE_##tag##_INDEX ) + + + /********************************************************************** + * + * @enum: + * FT_VALIDATE_GXXXX + * + * @description: + * A list of bit-field constants used with @FT_TrueTypeGX_Validate to + * indicate which TrueTypeGX/AAT Type tables should be validated. + * + * @values: + * FT_VALIDATE_feat :: + * Validate `feat' table. + * + * FT_VALIDATE_mort :: + * Validate `mort' table. + * + * FT_VALIDATE_morx :: + * Validate `morx' table. + * + * FT_VALIDATE_bsln :: + * Validate `bsln' table. + * + * FT_VALIDATE_just :: + * Validate `just' table. + * + * FT_VALIDATE_kern :: + * Validate `kern' table. + * + * FT_VALIDATE_opbd :: + * Validate `opbd' table. + * + * FT_VALIDATE_trak :: + * Validate `trak' table. + * + * FT_VALIDATE_prop :: + * Validate `prop' table. + * + * FT_VALIDATE_lcar :: + * Validate `lcar' table. + * + * FT_VALIDATE_GX :: + * Validate all TrueTypeGX tables (feat, mort, morx, bsln, just, kern, + * opbd, trak, prop and lcar). + * + */ + +#define FT_VALIDATE_feat FT_VALIDATE_GX_BITFIELD( feat ) +#define FT_VALIDATE_mort FT_VALIDATE_GX_BITFIELD( mort ) +#define FT_VALIDATE_morx FT_VALIDATE_GX_BITFIELD( morx ) +#define FT_VALIDATE_bsln FT_VALIDATE_GX_BITFIELD( bsln ) +#define FT_VALIDATE_just FT_VALIDATE_GX_BITFIELD( just ) +#define FT_VALIDATE_kern FT_VALIDATE_GX_BITFIELD( kern ) +#define FT_VALIDATE_opbd FT_VALIDATE_GX_BITFIELD( opbd ) +#define FT_VALIDATE_trak FT_VALIDATE_GX_BITFIELD( trak ) +#define FT_VALIDATE_prop FT_VALIDATE_GX_BITFIELD( prop ) +#define FT_VALIDATE_lcar FT_VALIDATE_GX_BITFIELD( lcar ) + +#define FT_VALIDATE_GX ( FT_VALIDATE_feat | \ + FT_VALIDATE_mort | \ + FT_VALIDATE_morx | \ + FT_VALIDATE_bsln | \ + FT_VALIDATE_just | \ + FT_VALIDATE_kern | \ + FT_VALIDATE_opbd | \ + FT_VALIDATE_trak | \ + FT_VALIDATE_prop | \ + FT_VALIDATE_lcar ) + + + /********************************************************************** + * + * @function: + * FT_TrueTypeGX_Validate + * + * @description: + * Validate various TrueTypeGX tables to assure that all offsets and + * indices are valid. The idea is that a higher-level library that + * actually does the text layout can access those tables without + * error checking (which can be quite time consuming). + * + * @input: + * face :: + * A handle to the input face. + * + * validation_flags :: + * A bit field that specifies the tables to be validated. See + * @FT_VALIDATE_GXXXX for possible values. + * + * table_length :: + * The size of the `tables' array. Normally, @FT_VALIDATE_GX_LENGTH + * should be passed. + * + * @output: + * tables :: + * The array where all validated sfnt tables are stored. + * The array itself must be allocated by a client. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with TrueTypeGX fonts, returning an error + * otherwise. + * + * After use, the application should deallocate the buffers pointed to by + * each `tables' element, by calling @FT_TrueTypeGX_Free. A NULL value + * indicates that the table either doesn't exist in the font, the + * application hasn't asked for validation, or the validator doesn't have + * the ability to validate the sfnt table. + */ + FT_EXPORT( FT_Error ) + FT_TrueTypeGX_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes tables[FT_VALIDATE_GX_LENGTH], + FT_UInt table_length ); + + + /********************************************************************** + * + * @function: + * FT_TrueTypeGX_Free + * + * @description: + * Free the buffer allocated by TrueTypeGX validator. + * + * @input: + * face :: + * A handle to the input face. + * + * table :: + * The pointer to the buffer allocated by + * @FT_TrueTypeGX_Validate. + * + * @note: + * This function must be used to free the buffer allocated by + * @FT_TrueTypeGX_Validate only. + */ + FT_EXPORT( void ) + FT_TrueTypeGX_Free( FT_Face face, + FT_Bytes table ); + + + /********************************************************************** + * + * @enum: + * FT_VALIDATE_CKERNXXX + * + * @description: + * A list of bit-field constants used with @FT_ClassicKern_Validate + * to indicate the classic kern dialect or dialects. If the selected + * type doesn't fit, @FT_ClassicKern_Validate regards the table as + * invalid. + * + * @values: + * FT_VALIDATE_MS :: + * Handle the `kern' table as a classic Microsoft kern table. + * + * FT_VALIDATE_APPLE :: + * Handle the `kern' table as a classic Apple kern table. + * + * FT_VALIDATE_CKERN :: + * Handle the `kern' as either classic Apple or Microsoft kern table. + */ +#define FT_VALIDATE_MS ( FT_VALIDATE_GX_START << 0 ) +#define FT_VALIDATE_APPLE ( FT_VALIDATE_GX_START << 1 ) + +#define FT_VALIDATE_CKERN ( FT_VALIDATE_MS | FT_VALIDATE_APPLE ) + + + /********************************************************************** + * + * @function: + * FT_ClassicKern_Validate + * + * @description: + * Validate classic (16-bit format) kern table to assure that the offsets + * and indices are valid. The idea is that a higher-level library that + * actually does the text layout can access those tables without error + * checking (which can be quite time consuming). + * + * The `kern' table validator in @FT_TrueTypeGX_Validate deals with both + * the new 32-bit format and the classic 16-bit format, while + * FT_ClassicKern_Validate only supports the classic 16-bit format. + * + * @input: + * face :: + * A handle to the input face. + * + * validation_flags :: + * A bit field that specifies the dialect to be validated. See + * @FT_VALIDATE_CKERNXXX for possible values. + * + * @output: + * ckern_table :: + * A pointer to the kern table. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * After use, the application should deallocate the buffers pointed to by + * `ckern_table', by calling @FT_ClassicKern_Free. A NULL value + * indicates that the table doesn't exist in the font. + */ + FT_EXPORT( FT_Error ) + FT_ClassicKern_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes *ckern_table ); + + + /********************************************************************** + * + * @function: + * FT_ClassicKern_Free + * + * @description: + * Free the buffer allocated by classic Kern validator. + * + * @input: + * face :: + * A handle to the input face. + * + * table :: + * The pointer to the buffer that is allocated by + * @FT_ClassicKern_Validate. + * + * @note: + * This function must be used to free the buffer allocated by + * @FT_ClassicKern_Validate only. + */ + FT_EXPORT( void ) + FT_ClassicKern_Free( FT_Face face, + FT_Bytes table ); + + /* */ + + +FT_END_HEADER + +#endif /* FTGXVAL_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftgzip.h b/foreign/freetype2/freetype/ftgzip.h new file mode 100644 index 0000000..9e658b0 --- /dev/null +++ b/foreign/freetype2/freetype/ftgzip.h @@ -0,0 +1,148 @@ +/***************************************************************************/ +/* */ +/* ftgzip.h */ +/* */ +/* Gzip-compressed stream support. */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTGZIP_H_ +#define FTGZIP_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* gzip */ + /* */ + /* <Title> */ + /* GZIP Streams */ + /* */ + /* <Abstract> */ + /* Using gzip-compressed font files. */ + /* */ + /* <Description> */ + /* This section contains the declaration of Gzip-specific functions. */ + /* */ + /*************************************************************************/ + + + /************************************************************************ + * + * @function: + * FT_Stream_OpenGzip + * + * @description: + * Open a new stream to parse gzip-compressed font files. This is + * mainly used to support the compressed `*.pcf.gz' fonts that come + * with XFree86. + * + * @input: + * stream :: + * The target embedding stream. + * + * source :: + * The source stream. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source stream must be opened _before_ calling this function. + * + * Calling the internal function `FT_Stream_Close' on the new stream will + * *not* call `FT_Stream_Close' on the source stream. None of the stream + * objects will be released to the heap. + * + * The stream implementation is very basic and resets the decompression + * process each time seeking backwards is needed within the stream. + * + * In certain builds of the library, gzip compression recognition is + * automatically handled when calling @FT_New_Face or @FT_Open_Face. + * This means that if no font driver is capable of handling the raw + * compressed file, the library will try to open a gzipped stream from + * it and re-open the face with it. + * + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with zlib support. + */ + FT_EXPORT( FT_Error ) + FT_Stream_OpenGzip( FT_Stream stream, + FT_Stream source ); + + + /************************************************************************ + * + * @function: + * FT_Gzip_Uncompress + * + * @description: + * Decompress a zipped input buffer into an output buffer. This function + * is modeled after zlib's `uncompress' function. + * + * @input: + * memory :: + * A FreeType memory handle. + * + * input :: + * The input buffer. + * + * input_len :: + * The length of the input buffer. + * + * @output: + * output:: + * The output buffer. + * + * @inout: + * output_len :: + * Before calling the function, this is the the total size of the + * output buffer, which must be large enough to hold the entire + * uncompressed data (so the size of the uncompressed data must be + * known in advance). After calling the function, `output_len' is the + * size of the used data in `output'. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with zlib support. + */ + FT_EXPORT( FT_Error ) + FT_Gzip_Uncompress( FT_Memory memory, + FT_Byte* output, + FT_ULong* output_len, + const FT_Byte* input, + FT_ULong input_len ); + + /* */ + + +FT_END_HEADER + +#endif /* FTGZIP_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftimage.h b/foreign/freetype2/freetype/ftimage.h new file mode 100644 index 0000000..1d557c9 --- /dev/null +++ b/foreign/freetype2/freetype/ftimage.h @@ -0,0 +1,1214 @@ +/***************************************************************************/ +/* */ +/* ftimage.h */ +/* */ +/* FreeType glyph image formats and default raster interface */ +/* (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Note: A `raster' is simply a scan-line converter, used to render */ + /* FT_Outlines into FT_Bitmaps. */ + /* */ + /*************************************************************************/ + + +#ifndef FTIMAGE_H_ +#define FTIMAGE_H_ + + + /* STANDALONE_ is from ftgrays.c */ +#ifndef STANDALONE_ +#include <ft2build.h> +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pos */ + /* */ + /* <Description> */ + /* The type FT_Pos is used to store vectorial coordinates. Depending */ + /* on the context, these can represent distances in integer font */ + /* units, or 16.16, or 26.6 fixed-point pixel coordinates. */ + /* */ + typedef signed long FT_Pos; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Vector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector; coordinates are of */ + /* the FT_Pos type. */ + /* */ + /* <Fields> */ + /* x :: The horizontal coordinate. */ + /* y :: The vertical coordinate. */ + /* */ + typedef struct FT_Vector_ + { + FT_Pos x; + FT_Pos y; + + } FT_Vector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BBox */ + /* */ + /* <Description> */ + /* A structure used to hold an outline's bounding box, i.e., the */ + /* coordinates of its extrema in the horizontal and vertical */ + /* directions. */ + /* */ + /* <Fields> */ + /* xMin :: The horizontal minimum (left-most). */ + /* */ + /* yMin :: The vertical minimum (bottom-most). */ + /* */ + /* xMax :: The horizontal maximum (right-most). */ + /* */ + /* yMax :: The vertical maximum (top-most). */ + /* */ + /* <Note> */ + /* The bounding box is specified with the coordinates of the lower */ + /* left and the upper right corner. In PostScript, those values are */ + /* often called (llx,lly) and (urx,ury), respectively. */ + /* */ + /* If `yMin' is negative, this value gives the glyph's descender. */ + /* Otherwise, the glyph doesn't descend below the baseline. */ + /* Similarly, if `ymax' is positive, this value gives the glyph's */ + /* ascender. */ + /* */ + /* `xMin' gives the horizontal distance from the glyph's origin to */ + /* the left edge of the glyph's bounding box. If `xMin' is negative, */ + /* the glyph extends to the left of the origin. */ + /* */ + typedef struct FT_BBox_ + { + FT_Pos xMin, yMin; + FT_Pos xMax, yMax; + + } FT_BBox; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Pixel_Mode */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of pixels in a */ + /* given bitmap. Note that additional formats may be added in the */ + /* future. */ + /* */ + /* <Values> */ + /* FT_PIXEL_MODE_NONE :: */ + /* Value~0 is reserved. */ + /* */ + /* FT_PIXEL_MODE_MONO :: */ + /* A monochrome bitmap, using 1~bit per pixel. Note that pixels */ + /* are stored in most-significant order (MSB), which means that */ + /* the left-most pixel in a byte has value 128. */ + /* */ + /* FT_PIXEL_MODE_GRAY :: */ + /* An 8-bit bitmap, generally used to represent anti-aliased glyph */ + /* images. Each pixel is stored in one byte. Note that the number */ + /* of `gray' levels is stored in the `num_grays' field of the */ + /* @FT_Bitmap structure (it generally is 256). */ + /* */ + /* FT_PIXEL_MODE_GRAY2 :: */ + /* A 2-bit per pixel bitmap, used to represent embedded */ + /* anti-aliased bitmaps in font files according to the OpenType */ + /* specification. We haven't found a single font using this */ + /* format, however. */ + /* */ + /* FT_PIXEL_MODE_GRAY4 :: */ + /* A 4-bit per pixel bitmap, representing embedded anti-aliased */ + /* bitmaps in font files according to the OpenType specification. */ + /* We haven't found a single font using this format, however. */ + /* */ + /* FT_PIXEL_MODE_LCD :: */ + /* An 8-bit bitmap, representing RGB or BGR decimated glyph images */ + /* used for display on LCD displays; the bitmap is three times */ + /* wider than the original glyph image. See also */ + /* @FT_RENDER_MODE_LCD. */ + /* */ + /* FT_PIXEL_MODE_LCD_V :: */ + /* An 8-bit bitmap, representing RGB or BGR decimated glyph images */ + /* used for display on rotated LCD displays; the bitmap is three */ + /* times taller than the original glyph image. See also */ + /* @FT_RENDER_MODE_LCD_V. */ + /* */ + /* FT_PIXEL_MODE_BGRA :: */ + /* An image with four 8-bit channels per pixel, representing a */ + /* color image (such as emoticons) with alpha channel. For each */ + /* pixel, the format is BGRA, which means, the blue channel comes */ + /* first in memory. The color channels are pre-multiplied and in */ + /* the sRGB colorspace. For example, full red at half-translucent */ + /* opacity will be represented as `00,00,80,80', not `00,00,FF,80'. */ + /* See also @FT_LOAD_COLOR. */ + /* */ + typedef enum FT_Pixel_Mode_ + { + FT_PIXEL_MODE_NONE = 0, + FT_PIXEL_MODE_MONO, + FT_PIXEL_MODE_GRAY, + FT_PIXEL_MODE_GRAY2, + FT_PIXEL_MODE_GRAY4, + FT_PIXEL_MODE_LCD, + FT_PIXEL_MODE_LCD_V, + FT_PIXEL_MODE_BGRA, + + FT_PIXEL_MODE_MAX /* do not remove */ + + } FT_Pixel_Mode; + + + /* these constants are deprecated; use the corresponding `FT_Pixel_Mode' */ + /* values instead. */ +#define ft_pixel_mode_none FT_PIXEL_MODE_NONE +#define ft_pixel_mode_mono FT_PIXEL_MODE_MONO +#define ft_pixel_mode_grays FT_PIXEL_MODE_GRAY +#define ft_pixel_mode_pal2 FT_PIXEL_MODE_GRAY2 +#define ft_pixel_mode_pal4 FT_PIXEL_MODE_GRAY4 + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap */ + /* */ + /* <Description> */ + /* A structure used to describe a bitmap or pixmap to the raster. */ + /* Note that we now manage pixmaps of various depths through the */ + /* `pixel_mode' field. */ + /* */ + /* <Fields> */ + /* rows :: The number of bitmap rows. */ + /* */ + /* width :: The number of pixels in bitmap row. */ + /* */ + /* pitch :: The pitch's absolute value is the number of bytes */ + /* taken by one bitmap row, including padding. */ + /* However, the pitch is positive when the bitmap has */ + /* a `down' flow, and negative when it has an `up' */ + /* flow. In all cases, the pitch is an offset to add */ + /* to a bitmap pointer in order to go down one row. */ + /* */ + /* Note that `padding' means the alignment of a */ + /* bitmap to a byte border, and FreeType functions */ + /* normally align to the smallest possible integer */ + /* value. */ + /* */ + /* For the B/W rasterizer, `pitch' is always an even */ + /* number. */ + /* */ + /* To change the pitch of a bitmap (say, to make it a */ + /* multiple of 4), use @FT_Bitmap_Convert. */ + /* Alternatively, you might use callback functions to */ + /* directly render to the application's surface; see */ + /* the file `example2.cpp' in the tutorial for a */ + /* demonstration. */ + /* */ + /* buffer :: A typeless pointer to the bitmap buffer. This */ + /* value should be aligned on 32-bit boundaries in */ + /* most cases. */ + /* */ + /* num_grays :: This field is only used with */ + /* @FT_PIXEL_MODE_GRAY; it gives the number of gray */ + /* levels used in the bitmap. */ + /* */ + /* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. */ + /* See @FT_Pixel_Mode for possible values. */ + /* */ + /* palette_mode :: This field is intended for paletted pixel modes; */ + /* it indicates how the palette is stored. Not */ + /* used currently. */ + /* */ + /* palette :: A typeless pointer to the bitmap palette; this */ + /* field is intended for paletted pixel modes. Not */ + /* used currently. */ + /* */ + typedef struct FT_Bitmap_ + { + unsigned int rows; + unsigned int width; + int pitch; + unsigned char* buffer; + unsigned short num_grays; + unsigned char pixel_mode; + unsigned char palette_mode; + void* palette; + + } FT_Bitmap; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline */ + /* */ + /* <Description> */ + /* This structure is used to describe an outline to the scan-line */ + /* converter. */ + /* */ + /* <Fields> */ + /* n_contours :: The number of contours in the outline. */ + /* */ + /* n_points :: The number of points in the outline. */ + /* */ + /* points :: A pointer to an array of `n_points' @FT_Vector */ + /* elements, giving the outline's point coordinates. */ + /* */ + /* tags :: A pointer to an array of `n_points' chars, giving */ + /* each outline point's type. */ + /* */ + /* If bit~0 is unset, the point is `off' the curve, */ + /* i.e., a Bézier control point, while it is `on' if */ + /* set. */ + /* */ + /* Bit~1 is meaningful for `off' points only. If set, */ + /* it indicates a third-order Bézier arc control point; */ + /* and a second-order control point if unset. */ + /* */ + /* If bit~2 is set, bits 5-7 contain the drop-out mode */ + /* (as defined in the OpenType specification; the value */ + /* is the same as the argument to the SCANMODE */ + /* instruction). */ + /* */ + /* Bits 3 and~4 are reserved for internal purposes. */ + /* */ + /* contours :: An array of `n_contours' shorts, giving the end */ + /* point of each contour within the outline. For */ + /* example, the first contour is defined by the points */ + /* `0' to `contours[0]', the second one is defined by */ + /* the points `contours[0]+1' to `contours[1]', etc. */ + /* */ + /* flags :: A set of bit flags used to characterize the outline */ + /* and give hints to the scan-converter and hinter on */ + /* how to convert/grid-fit it. See @FT_OUTLINE_XXX. */ + /* */ + /* <Note> */ + /* The B/W rasterizer only checks bit~2 in the `tags' array for the */ + /* first point of each contour. The drop-out mode as given with */ + /* @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, and */ + /* @FT_OUTLINE_INCLUDE_STUBS in `flags' is then overridden. */ + /* */ + typedef struct FT_Outline_ + { + short n_contours; /* number of contours in glyph */ + short n_points; /* number of points in the glyph */ + + FT_Vector* points; /* the outline's points */ + char* tags; /* the points flags */ + short* contours; /* the contour end points */ + + int flags; /* outline masks */ + + } FT_Outline; + + /* */ + + /* Following limits must be consistent with */ + /* FT_Outline.{n_contours,n_points} */ +#define FT_OUTLINE_CONTOURS_MAX SHRT_MAX +#define FT_OUTLINE_POINTS_MAX SHRT_MAX + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OUTLINE_XXX */ + /* */ + /* <Description> */ + /* A list of bit-field constants use for the flags in an outline's */ + /* `flags' field. */ + /* */ + /* <Values> */ + /* FT_OUTLINE_NONE :: */ + /* Value~0 is reserved. */ + /* */ + /* FT_OUTLINE_OWNER :: */ + /* If set, this flag indicates that the outline's field arrays */ + /* (i.e., `points', `flags', and `contours') are `owned' by the */ + /* outline object, and should thus be freed when it is destroyed. */ + /* */ + /* FT_OUTLINE_EVEN_ODD_FILL :: */ + /* By default, outlines are filled using the non-zero winding rule. */ + /* If set to 1, the outline will be filled using the even-odd fill */ + /* rule (only works with the smooth rasterizer). */ + /* */ + /* FT_OUTLINE_REVERSE_FILL :: */ + /* By default, outside contours of an outline are oriented in */ + /* clock-wise direction, as defined in the TrueType specification. */ + /* This flag is set if the outline uses the opposite direction */ + /* (typically for Type~1 fonts). This flag is ignored by the scan */ + /* converter. */ + /* */ + /* FT_OUTLINE_IGNORE_DROPOUTS :: */ + /* By default, the scan converter will try to detect drop-outs in */ + /* an outline and correct the glyph bitmap to ensure consistent */ + /* shape continuity. If set, this flag hints the scan-line */ + /* converter to ignore such cases. See below for more information. */ + /* */ + /* FT_OUTLINE_SMART_DROPOUTS :: */ + /* Select smart dropout control. If unset, use simple dropout */ + /* control. Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set. See */ + /* below for more information. */ + /* */ + /* FT_OUTLINE_INCLUDE_STUBS :: */ + /* If set, turn pixels on for `stubs', otherwise exclude them. */ + /* Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set. See below for */ + /* more information. */ + /* */ + /* FT_OUTLINE_HIGH_PRECISION :: */ + /* This flag indicates that the scan-line converter should try to */ + /* convert this outline to bitmaps with the highest possible */ + /* quality. It is typically set for small character sizes. Note */ + /* that this is only a hint that might be completely ignored by a */ + /* given scan-converter. */ + /* */ + /* FT_OUTLINE_SINGLE_PASS :: */ + /* This flag is set to force a given scan-converter to only use a */ + /* single pass over the outline to render a bitmap glyph image. */ + /* Normally, it is set for very large character sizes. It is only */ + /* a hint that might be completely ignored by a given */ + /* scan-converter. */ + /* */ + /* <Note> */ + /* The flags @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, */ + /* and @FT_OUTLINE_INCLUDE_STUBS are ignored by the smooth */ + /* rasterizer. */ + /* */ + /* There exists a second mechanism to pass the drop-out mode to the */ + /* B/W rasterizer; see the `tags' field in @FT_Outline. */ + /* */ + /* Please refer to the description of the `SCANTYPE' instruction in */ + /* the OpenType specification (in file `ttinst1.doc') how simple */ + /* drop-outs, smart drop-outs, and stubs are defined. */ + /* */ +#define FT_OUTLINE_NONE 0x0 +#define FT_OUTLINE_OWNER 0x1 +#define FT_OUTLINE_EVEN_ODD_FILL 0x2 +#define FT_OUTLINE_REVERSE_FILL 0x4 +#define FT_OUTLINE_IGNORE_DROPOUTS 0x8 +#define FT_OUTLINE_SMART_DROPOUTS 0x10 +#define FT_OUTLINE_INCLUDE_STUBS 0x20 + +#define FT_OUTLINE_HIGH_PRECISION 0x100 +#define FT_OUTLINE_SINGLE_PASS 0x200 + + + /* these constants are deprecated; use the corresponding */ + /* `FT_OUTLINE_XXX' values instead */ +#define ft_outline_none FT_OUTLINE_NONE +#define ft_outline_owner FT_OUTLINE_OWNER +#define ft_outline_even_odd_fill FT_OUTLINE_EVEN_ODD_FILL +#define ft_outline_reverse_fill FT_OUTLINE_REVERSE_FILL +#define ft_outline_ignore_dropouts FT_OUTLINE_IGNORE_DROPOUTS +#define ft_outline_high_precision FT_OUTLINE_HIGH_PRECISION +#define ft_outline_single_pass FT_OUTLINE_SINGLE_PASS + + /* */ + +#define FT_CURVE_TAG( flag ) ( flag & 3 ) + +#define FT_CURVE_TAG_ON 1 +#define FT_CURVE_TAG_CONIC 0 +#define FT_CURVE_TAG_CUBIC 2 + +#define FT_CURVE_TAG_HAS_SCANMODE 4 + +#define FT_CURVE_TAG_TOUCH_X 8 /* reserved for the TrueType hinter */ +#define FT_CURVE_TAG_TOUCH_Y 16 /* reserved for the TrueType hinter */ + +#define FT_CURVE_TAG_TOUCH_BOTH ( FT_CURVE_TAG_TOUCH_X | \ + FT_CURVE_TAG_TOUCH_Y ) + +#define FT_Curve_Tag_On FT_CURVE_TAG_ON +#define FT_Curve_Tag_Conic FT_CURVE_TAG_CONIC +#define FT_Curve_Tag_Cubic FT_CURVE_TAG_CUBIC +#define FT_Curve_Tag_Touch_X FT_CURVE_TAG_TOUCH_X +#define FT_Curve_Tag_Touch_Y FT_CURVE_TAG_TOUCH_Y + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_MoveToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `move */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `move to' is emitted to start a new contour in an outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `move to'. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_MoveToFunc)( const FT_Vector* to, + void* user ); + +#define FT_Outline_MoveTo_Func FT_Outline_MoveToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_LineToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `line */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `line to' is emitted to indicate a segment in the outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `line to'. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_LineToFunc)( const FT_Vector* to, + void* user ); + +#define FT_Outline_LineTo_Func FT_Outline_LineToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_ConicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `conic */ + /* to' function during outline walking or decomposition. */ + /* */ + /* A `conic to' is emitted to indicate a second-order Bézier arc in */ + /* the outline. */ + /* */ + /* <Input> */ + /* control :: An intermediate control point between the last position */ + /* and the new target in `to'. */ + /* */ + /* to :: A pointer to the target end point of the conic arc. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_ConicToFunc)( const FT_Vector* control, + const FT_Vector* to, + void* user ); + +#define FT_Outline_ConicTo_Func FT_Outline_ConicToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_CubicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `cubic */ + /* to' function during outline walking or decomposition. */ + /* */ + /* A `cubic to' is emitted to indicate a third-order Bézier arc. */ + /* */ + /* <Input> */ + /* control1 :: A pointer to the first Bézier control point. */ + /* */ + /* control2 :: A pointer to the second Bézier control point. */ + /* */ + /* to :: A pointer to the target end point. */ + /* */ + /* user :: A typeless pointer, which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_CubicToFunc)( const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to, + void* user ); + +#define FT_Outline_CubicTo_Func FT_Outline_CubicToFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline_Funcs */ + /* */ + /* <Description> */ + /* A structure to hold various function pointers used during outline */ + /* decomposition in order to emit segments, conic, and cubic Béziers. */ + /* */ + /* <Fields> */ + /* move_to :: The `move to' emitter. */ + /* */ + /* line_to :: The segment emitter. */ + /* */ + /* conic_to :: The second-order Bézier arc emitter. */ + /* */ + /* cubic_to :: The third-order Bézier arc emitter. */ + /* */ + /* shift :: The shift that is applied to coordinates before they */ + /* are sent to the emitter. */ + /* */ + /* delta :: The delta that is applied to coordinates before they */ + /* are sent to the emitter, but after the shift. */ + /* */ + /* <Note> */ + /* The point coordinates sent to the emitters are the transformed */ + /* version of the original coordinates (this is important for high */ + /* accuracy during scan-conversion). The transformation is simple: */ + /* */ + /* { */ + /* x' = (x << shift) - delta */ + /* y' = (x << shift) - delta */ + /* } */ + /* */ + /* Set the values of `shift' and `delta' to~0 to get the original */ + /* point coordinates. */ + /* */ + typedef struct FT_Outline_Funcs_ + { + FT_Outline_MoveToFunc move_to; + FT_Outline_LineToFunc line_to; + FT_Outline_ConicToFunc conic_to; + FT_Outline_CubicToFunc cubic_to; + + int shift; + FT_Pos delta; + + } FT_Outline_Funcs; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_IMAGE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags to an unsigned long type. */ + /* */ + /* <Note> */ + /* Since many 16-bit compilers don't like 32-bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* { */ + /* #define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) value */ + /* } */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ +#ifndef FT_IMAGE_TAG +#define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) \ + value = ( ( (unsigned long)_x1 << 24 ) | \ + ( (unsigned long)_x2 << 16 ) | \ + ( (unsigned long)_x3 << 8 ) | \ + (unsigned long)_x4 ) +#endif /* FT_IMAGE_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_Format */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of a given glyph */ + /* image. Note that this version of FreeType only supports two image */ + /* formats, even though future font drivers will be able to register */ + /* their own format. */ + /* */ + /* <Values> */ + /* FT_GLYPH_FORMAT_NONE :: */ + /* The value~0 is reserved. */ + /* */ + /* FT_GLYPH_FORMAT_COMPOSITE :: */ + /* The glyph image is a composite of several other images. This */ + /* format is _only_ used with @FT_LOAD_NO_RECURSE, and is used to */ + /* report compound glyphs (like accented characters). */ + /* */ + /* FT_GLYPH_FORMAT_BITMAP :: */ + /* The glyph image is a bitmap, and can be described as an */ + /* @FT_Bitmap. You generally need to access the `bitmap' field of */ + /* the @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_OUTLINE :: */ + /* The glyph image is a vectorial outline made of line segments */ + /* and Bézier arcs; it can be described as an @FT_Outline; you */ + /* generally want to access the `outline' field of the */ + /* @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_PLOTTER :: */ + /* The glyph image is a vectorial path with no inside and outside */ + /* contours. Some Type~1 fonts, like those in the Hershey family, */ + /* contain glyphs in this format. These are described as */ + /* @FT_Outline, but FreeType isn't currently capable of rendering */ + /* them correctly. */ + /* */ + typedef enum FT_Glyph_Format_ + { + FT_IMAGE_TAG( FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ), + + FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' ) + + } FT_Glyph_Format; + + + /* these constants are deprecated; use the corresponding */ + /* `FT_Glyph_Format' values instead. */ +#define ft_glyph_format_none FT_GLYPH_FORMAT_NONE +#define ft_glyph_format_composite FT_GLYPH_FORMAT_COMPOSITE +#define ft_glyph_format_bitmap FT_GLYPH_FORMAT_BITMAP +#define ft_glyph_format_outline FT_GLYPH_FORMAT_OUTLINE +#define ft_glyph_format_plotter FT_GLYPH_FORMAT_PLOTTER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** R A S T E R D E F I N I T I O N S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* A raster is a scan converter, in charge of rendering an outline into */ + /* a a bitmap. This section contains the public API for rasters. */ + /* */ + /* Note that in FreeType 2, all rasters are now encapsulated within */ + /* specific modules called `renderers'. See `ftrender.h' for more */ + /* details on renderers. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* raster */ + /* */ + /* <Title> */ + /* Scanline Converter */ + /* */ + /* <Abstract> */ + /* How vectorial outlines are converted into bitmaps and pixmaps. */ + /* */ + /* <Description> */ + /* This section contains technical definitions. */ + /* */ + /* <Order> */ + /* FT_Raster */ + /* FT_Span */ + /* FT_SpanFunc */ + /* */ + /* FT_Raster_Params */ + /* FT_RASTER_FLAG_XXX */ + /* */ + /* FT_Raster_NewFunc */ + /* FT_Raster_DoneFunc */ + /* FT_Raster_ResetFunc */ + /* FT_Raster_SetModeFunc */ + /* FT_Raster_RenderFunc */ + /* FT_Raster_Funcs */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Raster */ + /* */ + /* <Description> */ + /* An opaque handle (pointer) to a raster object. Each object can be */ + /* used independently to convert an outline into a bitmap or pixmap. */ + /* */ + typedef struct FT_RasterRec_* FT_Raster; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Span */ + /* */ + /* <Description> */ + /* A structure used to model a single span of gray pixels when */ + /* rendering an anti-aliased bitmap. */ + /* */ + /* <Fields> */ + /* x :: The span's horizontal start position. */ + /* */ + /* len :: The span's length in pixels. */ + /* */ + /* coverage :: The span color/coverage, ranging from 0 (background) */ + /* to 255 (foreground). */ + /* */ + /* <Note> */ + /* This structure is used by the span drawing callback type named */ + /* @FT_SpanFunc that takes the y~coordinate of the span as a */ + /* parameter. */ + /* */ + /* The coverage value is always between 0 and 255. If you want less */ + /* gray values, the callback function has to reduce them. */ + /* */ + typedef struct FT_Span_ + { + short x; + unsigned short len; + unsigned char coverage; + + } FT_Span; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_SpanFunc */ + /* */ + /* <Description> */ + /* A function used as a call-back by the anti-aliased renderer in */ + /* order to let client applications draw themselves the gray pixel */ + /* spans on each scan line. */ + /* */ + /* <Input> */ + /* y :: The scanline's y~coordinate. */ + /* */ + /* count :: The number of spans to draw on this scanline. */ + /* */ + /* spans :: A table of `count' spans to draw on the scanline. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Note> */ + /* This callback allows client applications to directly render the */ + /* gray spans of the anti-aliased bitmap to any kind of surfaces. */ + /* */ + /* This can be used to write anti-aliased outlines directly to a */ + /* given background bitmap, and even perform translucency. */ + /* */ + /* Note that the `count' field cannot be greater than a fixed value */ + /* defined by the `FT_MAX_GRAY_SPANS' configuration macro in */ + /* `ftoption.h'. By default, this value is set to~32, which means */ + /* that if there are more than 32~spans on a given scanline, the */ + /* callback is called several times with the same `y' parameter in */ + /* order to draw all callbacks. */ + /* */ + /* Otherwise, the callback is only called once per scan-line, and */ + /* only for those scanlines that do have `gray' pixels on them. */ + /* */ + typedef void + (*FT_SpanFunc)( int y, + int count, + const FT_Span* spans, + void* user ); + +#define FT_Raster_Span_Func FT_SpanFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitTest_Func */ + /* */ + /* <Description> */ + /* Deprecated, unimplemented. */ + /* */ + typedef int + (*FT_Raster_BitTest_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitSet_Func */ + /* */ + /* <Description> */ + /* Deprecated, unimplemented. */ + /* */ + typedef void + (*FT_Raster_BitSet_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_RASTER_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flag constants as used in the `flags' field of a */ + /* @FT_Raster_Params structure. */ + /* */ + /* <Values> */ + /* FT_RASTER_FLAG_DEFAULT :: This value is 0. */ + /* */ + /* FT_RASTER_FLAG_AA :: This flag is set to indicate that an */ + /* anti-aliased glyph image should be */ + /* generated. Otherwise, it will be */ + /* monochrome (1-bit). */ + /* */ + /* FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */ + /* rendering. In this mode, client */ + /* applications must provide their own span */ + /* callback. This lets them directly */ + /* draw or compose over an existing bitmap. */ + /* If this bit is not set, the target */ + /* pixmap's buffer _must_ be zeroed before */ + /* rendering. */ + /* */ + /* Direct rendering is only possible with */ + /* anti-aliased glyphs. */ + /* */ + /* FT_RASTER_FLAG_CLIP :: This flag is only used in direct */ + /* rendering mode. If set, the output will */ + /* be clipped to a box specified in the */ + /* `clip_box' field of the */ + /* @FT_Raster_Params structure. */ + /* */ + /* Note that by default, the glyph bitmap */ + /* is clipped to the target pixmap, except */ + /* in direct rendering mode where all spans */ + /* are generated if no clipping box is set. */ + /* */ +#define FT_RASTER_FLAG_DEFAULT 0x0 +#define FT_RASTER_FLAG_AA 0x1 +#define FT_RASTER_FLAG_DIRECT 0x2 +#define FT_RASTER_FLAG_CLIP 0x4 + + /* these constants are deprecated; use the corresponding */ + /* `FT_RASTER_FLAG_XXX' values instead */ +#define ft_raster_flag_default FT_RASTER_FLAG_DEFAULT +#define ft_raster_flag_aa FT_RASTER_FLAG_AA +#define ft_raster_flag_direct FT_RASTER_FLAG_DIRECT +#define ft_raster_flag_clip FT_RASTER_FLAG_CLIP + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Params */ + /* */ + /* <Description> */ + /* A structure to hold the arguments used by a raster's render */ + /* function. */ + /* */ + /* <Fields> */ + /* target :: The target bitmap. */ + /* */ + /* source :: A pointer to the source glyph image (e.g., an */ + /* @FT_Outline). */ + /* */ + /* flags :: The rendering flags. */ + /* */ + /* gray_spans :: The gray span drawing callback. */ + /* */ + /* black_spans :: Unused. */ + /* */ + /* bit_test :: Unused. */ + /* */ + /* bit_set :: Unused. */ + /* */ + /* user :: User-supplied data that is passed to each drawing */ + /* callback. */ + /* */ + /* clip_box :: An optional clipping box. It is only used in */ + /* direct rendering mode. Note that coordinates here */ + /* should be expressed in _integer_ pixels (and not in */ + /* 26.6 fixed-point units). */ + /* */ + /* <Note> */ + /* An anti-aliased glyph bitmap is drawn if the @FT_RASTER_FLAG_AA */ + /* bit flag is set in the `flags' field, otherwise a monochrome */ + /* bitmap is generated. */ + /* */ + /* If the @FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */ + /* raster will call the `gray_spans' callback to draw gray pixel */ + /* spans. This allows direct composition over a pre-existing bitmap */ + /* through user-provided callbacks to perform the span drawing and */ + /* composition. Not supported by the monochrome rasterizer. */ + /* */ + typedef struct FT_Raster_Params_ + { + const FT_Bitmap* target; + const void* source; + int flags; + FT_SpanFunc gray_spans; + FT_SpanFunc black_spans; /* unused */ + FT_Raster_BitTest_Func bit_test; /* unused */ + FT_Raster_BitSet_Func bit_set; /* unused */ + void* user; + FT_BBox clip_box; + + } FT_Raster_Params; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_NewFunc */ + /* */ + /* <Description> */ + /* A function used to create a new raster object. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory allocator. */ + /* */ + /* <Output> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + /* <Note> */ + /* The `memory' parameter is a typeless pointer in order to avoid */ + /* un-wanted dependencies on the rest of the FreeType code. In */ + /* practice, it is an @FT_Memory object, i.e., a handle to the */ + /* standard FreeType memory allocator. However, this field can be */ + /* completely ignored by a given raster implementation. */ + /* */ + typedef int + (*FT_Raster_NewFunc)( void* memory, + FT_Raster* raster ); + +#define FT_Raster_New_Func FT_Raster_NewFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_DoneFunc */ + /* */ + /* <Description> */ + /* A function used to destroy a given raster object. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + typedef void + (*FT_Raster_DoneFunc)( FT_Raster raster ); + +#define FT_Raster_Done_Func FT_Raster_DoneFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_ResetFunc */ + /* */ + /* <Description> */ + /* FreeType used to provide an area of memory called the `render */ + /* pool' available to all registered rasters. This was not thread */ + /* safe however and now FreeType never allocates this pool. NULL */ + /* is always passed in as pool_base. */ + /* */ + /* This function is called each time the render pool changes, or just */ + /* after a new raster object is created. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* pool_base :: The address in memory of the render pool. */ + /* */ + /* pool_size :: The size in bytes of the render pool. */ + /* */ + /* <Note> */ + /* Rasters should ignore the render pool and rely on dynamic or stack */ + /* allocation if they want to (a handle to the memory allocator is */ + /* passed to the raster constructor). */ + /* */ + typedef void + (*FT_Raster_ResetFunc)( FT_Raster raster, + unsigned char* pool_base, + unsigned long pool_size ); + +#define FT_Raster_Reset_Func FT_Raster_ResetFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_SetModeFunc */ + /* */ + /* <Description> */ + /* This function is a generic facility to change modes or attributes */ + /* in a given raster. This can be used for debugging purposes, or */ + /* simply to allow implementation-specific `features' in a given */ + /* raster module. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* mode :: A 4-byte tag used to name the mode or property. */ + /* */ + /* args :: A pointer to the new mode/property to use. */ + /* */ + typedef int + (*FT_Raster_SetModeFunc)( FT_Raster raster, + unsigned long mode, + void* args ); + +#define FT_Raster_Set_Mode_Func FT_Raster_SetModeFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_RenderFunc */ + /* */ + /* <Description> */ + /* Invoke a given raster to scan-convert a given glyph image into a */ + /* target bitmap. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + /* params :: A pointer to an @FT_Raster_Params structure used to */ + /* store the rendering parameters. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + /* <Note> */ + /* The exact format of the source image depends on the raster's glyph */ + /* format defined in its @FT_Raster_Funcs structure. It can be an */ + /* @FT_Outline or anything else in order to support a large array of */ + /* glyph formats. */ + /* */ + /* Note also that the render function can fail and return a */ + /* `FT_Err_Unimplemented_Feature' error code if the raster used does */ + /* not support direct composition. */ + /* */ + /* XXX: For now, the standard raster doesn't support direct */ + /* composition but this should change for the final release (see */ + /* the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c' */ + /* for examples of distinct implementations that support direct */ + /* composition). */ + /* */ + typedef int + (*FT_Raster_RenderFunc)( FT_Raster raster, + const FT_Raster_Params* params ); + +#define FT_Raster_Render_Func FT_Raster_RenderFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Funcs */ + /* */ + /* <Description> */ + /* A structure used to describe a given raster class to the library. */ + /* */ + /* <Fields> */ + /* glyph_format :: The supported glyph format for this raster. */ + /* */ + /* raster_new :: The raster constructor. */ + /* */ + /* raster_reset :: Used to reset the render pool within the raster. */ + /* */ + /* raster_render :: A function to render a glyph into a given bitmap. */ + /* */ + /* raster_done :: The raster destructor. */ + /* */ + typedef struct FT_Raster_Funcs_ + { + FT_Glyph_Format glyph_format; + FT_Raster_NewFunc raster_new; + FT_Raster_ResetFunc raster_reset; + FT_Raster_SetModeFunc raster_set_mode; + FT_Raster_RenderFunc raster_render; + FT_Raster_DoneFunc raster_done; + + } FT_Raster_Funcs; + + /* */ + + +FT_END_HEADER + +#endif /* FTIMAGE_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/foreign/freetype2/freetype/ftincrem.h b/foreign/freetype2/freetype/ftincrem.h new file mode 100644 index 0000000..46b58b7 --- /dev/null +++ b/foreign/freetype2/freetype/ftincrem.h @@ -0,0 +1,354 @@ +/***************************************************************************/ +/* */ +/* ftincrem.h */ +/* */ +/* FreeType incremental loading (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTINCREM_H_ +#define FTINCREM_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************** + * + * @section: + * incremental + * + * @title: + * Incremental Loading + * + * @abstract: + * Custom Glyph Loading. + * + * @description: + * This section contains various functions used to perform so-called + * `incremental' glyph loading. This is a mode where all glyphs loaded + * from a given @FT_Face are provided by the client application. + * + * Apart from that, all other tables are loaded normally from the font + * file. This mode is useful when FreeType is used within another + * engine, e.g., a PostScript Imaging Processor. + * + * To enable this mode, you must use @FT_Open_Face, passing an + * @FT_Parameter with the @FT_PARAM_TAG_INCREMENTAL tag and an + * @FT_Incremental_Interface value. See the comments for + * @FT_Incremental_InterfaceRec for an example. + * + */ + + + /*************************************************************************** + * + * @type: + * FT_Incremental + * + * @description: + * An opaque type describing a user-provided object used to implement + * `incremental' glyph loading within FreeType. This is used to support + * embedded fonts in certain environments (e.g., PostScript interpreters), + * where the glyph data isn't in the font file, or must be overridden by + * different values. + * + * @note: + * It is up to client applications to create and implement @FT_Incremental + * objects, as long as they provide implementations for the methods + * @FT_Incremental_GetGlyphDataFunc, @FT_Incremental_FreeGlyphDataFunc + * and @FT_Incremental_GetGlyphMetricsFunc. + * + * See the description of @FT_Incremental_InterfaceRec to understand how + * to use incremental objects with FreeType. + * + */ + typedef struct FT_IncrementalRec_* FT_Incremental; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_MetricsRec + * + * @description: + * A small structure used to contain the basic glyph metrics returned + * by the @FT_Incremental_GetGlyphMetricsFunc method. + * + * @fields: + * bearing_x :: + * Left bearing, in font units. + * + * bearing_y :: + * Top bearing, in font units. + * + * advance :: + * Horizontal component of glyph advance, in font units. + * + * advance_v :: + * Vertical component of glyph advance, in font units. + * + * @note: + * These correspond to horizontal or vertical metrics depending on the + * value of the `vertical' argument to the function + * @FT_Incremental_GetGlyphMetricsFunc. + * + */ + typedef struct FT_Incremental_MetricsRec_ + { + FT_Long bearing_x; + FT_Long bearing_y; + FT_Long advance; + FT_Long advance_v; /* since 2.3.12 */ + + } FT_Incremental_MetricsRec; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_Metrics + * + * @description: + * A handle to an @FT_Incremental_MetricsRec structure. + * + */ + typedef struct FT_Incremental_MetricsRec_* FT_Incremental_Metrics; + + + /*************************************************************************** + * + * @type: + * FT_Incremental_GetGlyphDataFunc + * + * @description: + * A function called by FreeType to access a given glyph's data bytes + * during @FT_Load_Glyph or @FT_Load_Char if incremental loading is + * enabled. + * + * Note that the format of the glyph's data bytes depends on the font + * file format. For TrueType, it must correspond to the raw bytes within + * the `glyf' table. For PostScript formats, it must correspond to the + * *unencrypted* charstring bytes, without any `lenIV' header. It is + * undefined for any other format. + * + * @input: + * incremental :: + * Handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * glyph_index :: + * Index of relevant glyph. + * + * @output: + * adata :: + * A structure describing the returned glyph data bytes (which will be + * accessed as a read-only byte block). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If this function returns successfully the method + * @FT_Incremental_FreeGlyphDataFunc will be called later to release + * the data bytes. + * + * Nested calls to @FT_Incremental_GetGlyphDataFunc can happen for + * compound glyphs. + * + */ + typedef FT_Error + (*FT_Incremental_GetGlyphDataFunc)( FT_Incremental incremental, + FT_UInt glyph_index, + FT_Data* adata ); + + + /*************************************************************************** + * + * @type: + * FT_Incremental_FreeGlyphDataFunc + * + * @description: + * A function used to release the glyph data bytes returned by a + * successful call to @FT_Incremental_GetGlyphDataFunc. + * + * @input: + * incremental :: + * A handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * data :: + * A structure describing the glyph data bytes (which will be accessed + * as a read-only byte block). + * + */ + typedef void + (*FT_Incremental_FreeGlyphDataFunc)( FT_Incremental incremental, + FT_Data* data ); + + + /*************************************************************************** + * + * @type: + * FT_Incremental_GetGlyphMetricsFunc + * + * @description: + * A function used to retrieve the basic metrics of a given glyph index + * before accessing its data. This is necessary because, in certain + * formats like TrueType, the metrics are stored in a different place from + * the glyph images proper. + * + * @input: + * incremental :: + * A handle to an opaque @FT_Incremental handle provided by the client + * application. + * + * glyph_index :: + * Index of relevant glyph. + * + * vertical :: + * If true, return vertical metrics. + * + * ametrics :: + * This parameter is used for both input and output. + * The original glyph metrics, if any, in font units. If metrics are + * not available all the values must be set to zero. + * + * @output: + * ametrics :: + * The replacement glyph metrics in font units. + * + */ + typedef FT_Error + (*FT_Incremental_GetGlyphMetricsFunc) + ( FT_Incremental incremental, + FT_UInt glyph_index, + FT_Bool vertical, + FT_Incremental_MetricsRec *ametrics ); + + + /************************************************************************** + * + * @struct: + * FT_Incremental_FuncsRec + * + * @description: + * A table of functions for accessing fonts that load data + * incrementally. Used in @FT_Incremental_InterfaceRec. + * + * @fields: + * get_glyph_data :: + * The function to get glyph data. Must not be null. + * + * free_glyph_data :: + * The function to release glyph data. Must not be null. + * + * get_glyph_metrics :: + * The function to get glyph metrics. May be null if the font does + * not provide overriding glyph metrics. + * + */ + typedef struct FT_Incremental_FuncsRec_ + { + FT_Incremental_GetGlyphDataFunc get_glyph_data; + FT_Incremental_FreeGlyphDataFunc free_glyph_data; + FT_Incremental_GetGlyphMetricsFunc get_glyph_metrics; + + } FT_Incremental_FuncsRec; + + + /*************************************************************************** + * + * @struct: + * FT_Incremental_InterfaceRec + * + * @description: + * A structure to be used with @FT_Open_Face to indicate that the user + * wants to support incremental glyph loading. You should use it with + * @FT_PARAM_TAG_INCREMENTAL as in the following example: + * + * { + * FT_Incremental_InterfaceRec inc_int; + * FT_Parameter parameter; + * FT_Open_Args open_args; + * + * + * // set up incremental descriptor + * inc_int.funcs = my_funcs; + * inc_int.object = my_object; + * + * // set up optional parameter + * parameter.tag = FT_PARAM_TAG_INCREMENTAL; + * parameter.data = &inc_int; + * + * // set up FT_Open_Args structure + * open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; + * open_args.pathname = my_font_pathname; + * open_args.num_params = 1; + * open_args.params = ¶meter; // we use one optional argument + * + * // open the font + * error = FT_Open_Face( library, &open_args, index, &face ); + * ... + * } + * + */ + typedef struct FT_Incremental_InterfaceRec_ + { + const FT_Incremental_FuncsRec* funcs; + FT_Incremental object; + + } FT_Incremental_InterfaceRec; + + + /*************************************************************************** + * + * @type: + * FT_Incremental_Interface + * + * @description: + * A pointer to an @FT_Incremental_InterfaceRec structure. + * + */ + typedef FT_Incremental_InterfaceRec* FT_Incremental_Interface; + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_INCREMENTAL + * + * @description: + * A constant used as the tag of @FT_Parameter structures to indicate + * an incremental loading object to be used by FreeType. + * + */ +#define FT_PARAM_TAG_INCREMENTAL FT_MAKE_TAG( 'i', 'n', 'c', 'r' ) + + /* */ + + +FT_END_HEADER + +#endif /* FTINCREM_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftlcdfil.h b/foreign/freetype2/freetype/ftlcdfil.h new file mode 100644 index 0000000..e06a895 --- /dev/null +++ b/foreign/freetype2/freetype/ftlcdfil.h @@ -0,0 +1,286 @@ +/***************************************************************************/ +/* */ +/* ftlcdfil.h */ +/* */ +/* FreeType API for color filtering of subpixel bitmap glyphs */ +/* (specification). */ +/* */ +/* Copyright 2006-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTLCDFIL_H_ +#define FTLCDFIL_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************** + * + * @section: + * lcd_filtering + * + * @title: + * LCD Filtering + * + * @abstract: + * Reduce color fringes of subpixel-rendered bitmaps. + * + * @description: + * Subpixel rendering exploits the color-striped structure of LCD + * pixels, increasing the available resolution in the direction of the + * stripe (usually horizontal RGB) by a factor of~3. Since these + * subpixels are color pixels, using them unfiltered creates severe + * color fringes. Use the @FT_Library_SetLcdFilter API to specify a + * low-pass filter, which is then applied to subpixel-rendered bitmaps + * generated through @FT_Render_Glyph. The filter sacrifices some of + * the higher resolution to reduce color fringes, making the glyph image + * slightly blurrier. Positional improvements will remain. + * + * Note that no filter is active by default, and that this function is + * *not* implemented in default builds of the library. You need to + * #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING in your `ftoption.h' file + * in order to activate it and explicitly call @FT_Library_SetLcdFilter + * to enable it. + * + * A filter should have two properties: + * + * 1) It should be normalized, meaning the sum of the 5~components + * should be 256 (0x100). It is possible to go above or under this + * target sum, however: going under means tossing out contrast, going + * over means invoking clamping and thereby non-linearities that + * increase contrast somewhat at the expense of greater distortion + * and color-fringing. Contrast is better enhanced through stem + * darkening. + * + * 2) It should be color-balanced, meaning a filter `{~a, b, c, b, a~}' + * where a~+ b~=~c. It distributes the computed coverage for one + * subpixel to all subpixels equally, sacrificing some won resolution + * but drastically reducing color-fringing. Positioning improvements + * remain! Note that color-fringing can only really be minimized + * when using a color-balanced filter and alpha-blending the glyph + * onto a surface in linear space; see @FT_Render_Glyph. + * + * Regarding the form, a filter can be a `boxy' filter or a `beveled' + * filter. Boxy filters are sharper but are less forgiving of non-ideal + * gamma curves of a screen (viewing angles!), beveled filters are + * fuzzier but more tolerant. + * + * Examples: + * + * - [0x10 0x40 0x70 0x40 0x10] is beveled and neither balanced nor + * normalized. + * + * - [0x1A 0x33 0x4D 0x33 0x1A] is beveled and balanced but not + * normalized. + * + * - [0x19 0x33 0x66 0x4c 0x19] is beveled and normalized but not + * balanced. + * + * - [0x00 0x4c 0x66 0x4c 0x00] is boxily beveled and normalized but not + * balanced. + * + * - [0x00 0x55 0x56 0x55 0x00] is boxy, normalized, and almost + * balanced. + * + * - [0x08 0x4D 0x56 0x4D 0x08] is beveled, normalized and, almost + * balanced. + * + * The filter affects glyph bitmaps rendered through @FT_Render_Glyph, + * @FT_Load_Glyph, and @FT_Load_Char. It does _not_ affect the output + * of @FT_Outline_Render and @FT_Outline_Get_Bitmap. + * + * If this feature is activated, the dimensions of LCD glyph bitmaps are + * either wider or taller than the dimensions of the corresponding + * outline with regard to the pixel grid. For example, for + * @FT_RENDER_MODE_LCD, the filter adds 3~subpixels to the left, and + * 3~subpixels to the right. The bitmap offset values are adjusted + * accordingly, so clients shouldn't need to modify their layout and + * glyph positioning code when enabling the filter. + * + * It is important to understand that linear alpha blending and gamma + * correction is critical for correctly rendering glyphs onto surfaces + * without artifacts and even more critical when subpixel rendering is + * involved. + * + * Each of the 3~alpha values (subpixels) is independently used to blend + * one color channel. That is, red alpha blends the red channel of the + * text color with the red channel of the background pixel. The + * distribution of density values by the color-balanced filter assumes + * alpha blending is done in linear space; only then color artifacts + * cancel out. + */ + + + /**************************************************************************** + * + * @enum: + * FT_LcdFilter + * + * @description: + * A list of values to identify various types of LCD filters. + * + * @values: + * FT_LCD_FILTER_NONE :: + * Do not perform filtering. When used with subpixel rendering, this + * results in sometimes severe color fringes. + * + * FT_LCD_FILTER_DEFAULT :: + * The default filter reduces color fringes considerably, at the cost + * of a slight blurriness in the output. + * + * It is a beveled, normalized, and color-balanced five-tap filter + * that is more forgiving to screens with non-ideal gamma curves and + * viewing angles. Note that while color-fringing is reduced, it can + * only be minimized by using linear alpha blending and gamma + * correction to render glyphs onto surfaces. The default filter + * weights are [0x08 0x4D 0x56 0x4D 0x08]. + * + * FT_LCD_FILTER_LIGHT :: + * The light filter is a variant that is sharper at the cost of + * slightly more color fringes than the default one. + * + * It is a boxy, normalized, and color-balanced three-tap filter that + * is less forgiving to screens with non-ideal gamma curves and + * viewing angles. This filter works best when the rendering system + * uses linear alpha blending and gamma correction to render glyphs + * onto surfaces. The light filter weights are + * [0x00 0x55 0x56 0x55 0x00]. + * + * FT_LCD_FILTER_LEGACY :: + * This filter corresponds to the original libXft color filter. It + * provides high contrast output but can exhibit really bad color + * fringes if glyphs are not extremely well hinted to the pixel grid. + * In other words, it only works well if the TrueType bytecode + * interpreter is enabled *and* high-quality hinted fonts are used. + * + * This filter is only provided for comparison purposes, and might be + * disabled or stay unsupported in the future. + * + * FT_LCD_FILTER_LEGACY1 :: + * For historical reasons, the FontConfig library returns a different + * enumeration value for legacy LCD filtering. To make code work that + * (incorrectly) forwards FontConfig's enumeration value to + * @FT_Library_SetLcdFilter without proper mapping, it is thus easiest + * to have another enumeration value, which is completely equal to + * `FT_LCD_FILTER_LEGACY'. + * + * @since: + * 2.3.0 (`FT_LCD_FILTER_LEGACY1' since 2.6.2) + */ + typedef enum FT_LcdFilter_ + { + FT_LCD_FILTER_NONE = 0, + FT_LCD_FILTER_DEFAULT = 1, + FT_LCD_FILTER_LIGHT = 2, + FT_LCD_FILTER_LEGACY1 = 3, + FT_LCD_FILTER_LEGACY = 16, + + FT_LCD_FILTER_MAX /* do not remove */ + + } FT_LcdFilter; + + + /************************************************************************** + * + * @func: + * FT_Library_SetLcdFilter + * + * @description: + * This function is used to apply color filtering to LCD decimated + * bitmaps, like the ones used when calling @FT_Render_Glyph with + * @FT_RENDER_MODE_LCD or @FT_RENDER_MODE_LCD_V. + * + * @input: + * library :: + * A handle to the target library instance. + * + * filter :: + * The filter type. + * + * You can use @FT_LCD_FILTER_NONE here to disable this feature, or + * @FT_LCD_FILTER_DEFAULT to use a default filter that should work + * well on most LCD screens. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This feature is always disabled by default. Clients must make an + * explicit call to this function with a `filter' value other than + * @FT_LCD_FILTER_NONE in order to enable it. + * + * Due to *PATENTS* covering subpixel rendering, this function doesn't + * do anything except returning `FT_Err_Unimplemented_Feature' if the + * configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING is not + * defined in your build of the library, which should correspond to all + * default builds of FreeType. + * + * @since: + * 2.3.0 + */ + FT_EXPORT( FT_Error ) + FT_Library_SetLcdFilter( FT_Library library, + FT_LcdFilter filter ); + + + /************************************************************************** + * + * @func: + * FT_Library_SetLcdFilterWeights + * + * @description: + * This function can be used to enable LCD filter with custom weights, + * instead of using presets in @FT_Library_SetLcdFilter. + * + * @input: + * library :: + * A handle to the target library instance. + * + * weights :: + * A pointer to an array; the function copies the first five bytes and + * uses them to specify the filter weights. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * Due to *PATENTS* covering subpixel rendering, this function doesn't + * do anything except returning `FT_Err_Unimplemented_Feature' if the + * configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING is not + * defined in your build of the library, which should correspond to all + * default builds of FreeType. + * + * @since: + * 2.4.0 + */ + FT_EXPORT( FT_Error ) + FT_Library_SetLcdFilterWeights( FT_Library library, + unsigned char *weights ); + + /* */ + + +FT_END_HEADER + +#endif /* FTLCDFIL_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftlist.h b/foreign/freetype2/freetype/ftlist.h new file mode 100644 index 0000000..82f437a --- /dev/null +++ b/foreign/freetype2/freetype/ftlist.h @@ -0,0 +1,276 @@ +/***************************************************************************/ +/* */ +/* ftlist.h */ +/* */ +/* Generic list support for FreeType (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file implements functions relative to list processing. Its */ + /* data structures are defined in `freetype.h'. */ + /* */ + /*************************************************************************/ + + +#ifndef FTLIST_H_ +#define FTLIST_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* list_processing */ + /* */ + /* <Title> */ + /* List Processing */ + /* */ + /* <Abstract> */ + /* Simple management of lists. */ + /* */ + /* <Description> */ + /* This section contains various definitions related to list */ + /* processing using doubly-linked nodes. */ + /* */ + /* <Order> */ + /* FT_List */ + /* FT_ListNode */ + /* FT_ListRec */ + /* FT_ListNodeRec */ + /* */ + /* FT_List_Add */ + /* FT_List_Insert */ + /* FT_List_Find */ + /* FT_List_Remove */ + /* FT_List_Up */ + /* FT_List_Iterate */ + /* FT_List_Iterator */ + /* FT_List_Finalize */ + /* FT_List_Destructor */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Find */ + /* */ + /* <Description> */ + /* Find the list node for a given listed object. */ + /* */ + /* <Input> */ + /* list :: A pointer to the parent list. */ + /* data :: The address of the listed object. */ + /* */ + /* <Return> */ + /* List node. NULL if it wasn't found. */ + /* */ + FT_EXPORT( FT_ListNode ) + FT_List_Find( FT_List list, + void* data ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Add */ + /* */ + /* <Description> */ + /* Append an element to the end of a list. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* node :: The node to append. */ + /* */ + FT_EXPORT( void ) + FT_List_Add( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Insert */ + /* */ + /* <Description> */ + /* Insert an element at the head of a list. */ + /* */ + /* <InOut> */ + /* list :: A pointer to parent list. */ + /* node :: The node to insert. */ + /* */ + FT_EXPORT( void ) + FT_List_Insert( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Remove */ + /* */ + /* <Description> */ + /* Remove a node from a list. This function doesn't check whether */ + /* the node is in the list! */ + /* */ + /* <Input> */ + /* node :: The node to remove. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* */ + FT_EXPORT( void ) + FT_List_Remove( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Up */ + /* */ + /* <Description> */ + /* Move a node to the head/top of a list. Used to maintain LRU */ + /* lists. */ + /* */ + /* <InOut> */ + /* list :: A pointer to the parent list. */ + /* node :: The node to move. */ + /* */ + FT_EXPORT( void ) + FT_List_Up( FT_List list, + FT_ListNode node ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_List_Iterator */ + /* */ + /* <Description> */ + /* An FT_List iterator function that is called during a list parse */ + /* by @FT_List_Iterate. */ + /* */ + /* <Input> */ + /* node :: The current iteration list node. */ + /* */ + /* user :: A typeless pointer passed to @FT_List_Iterate. */ + /* Can be used to point to the iteration's state. */ + /* */ + typedef FT_Error + (*FT_List_Iterator)( FT_ListNode node, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Iterate */ + /* */ + /* <Description> */ + /* Parse a list and calls a given iterator function on each element. */ + /* Note that parsing is stopped as soon as one of the iterator calls */ + /* returns a non-zero value. */ + /* */ + /* <Input> */ + /* list :: A handle to the list. */ + /* iterator :: An iterator function, called on each node of the list. */ + /* user :: A user-supplied field that is passed as the second */ + /* argument to the iterator. */ + /* */ + /* <Return> */ + /* The result (a FreeType error code) of the last iterator call. */ + /* */ + FT_EXPORT( FT_Error ) + FT_List_Iterate( FT_List list, + FT_List_Iterator iterator, + void* user ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_List_Destructor */ + /* */ + /* <Description> */ + /* An @FT_List iterator function that is called during a list */ + /* finalization by @FT_List_Finalize to destroy all elements in a */ + /* given list. */ + /* */ + /* <Input> */ + /* system :: The current system object. */ + /* */ + /* data :: The current object to destroy. */ + /* */ + /* user :: A typeless pointer passed to @FT_List_Iterate. It can */ + /* be used to point to the iteration's state. */ + /* */ + typedef void + (*FT_List_Destructor)( FT_Memory memory, + void* data, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_List_Finalize */ + /* */ + /* <Description> */ + /* Destroy all elements in the list as well as the list itself. */ + /* */ + /* <Input> */ + /* list :: A handle to the list. */ + /* */ + /* destroy :: A list destructor that will be applied to each element */ + /* of the list. Set this to NULL if not needed. */ + /* */ + /* memory :: The current memory object that handles deallocation. */ + /* */ + /* user :: A user-supplied field that is passed as the last */ + /* argument to the destructor. */ + /* */ + /* <Note> */ + /* This function expects that all nodes added by @FT_List_Add or */ + /* @FT_List_Insert have been dynamically allocated. */ + /* */ + FT_EXPORT( void ) + FT_List_Finalize( FT_List list, + FT_List_Destructor destroy, + FT_Memory memory, + void* user ); + + /* */ + + +FT_END_HEADER + +#endif /* FTLIST_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftlzw.h b/foreign/freetype2/freetype/ftlzw.h new file mode 100644 index 0000000..582e2c1 --- /dev/null +++ b/foreign/freetype2/freetype/ftlzw.h @@ -0,0 +1,99 @@ +/***************************************************************************/ +/* */ +/* ftlzw.h */ +/* */ +/* LZW-compressed stream support. */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTLZW_H_ +#define FTLZW_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* lzw */ + /* */ + /* <Title> */ + /* LZW Streams */ + /* */ + /* <Abstract> */ + /* Using LZW-compressed font files. */ + /* */ + /* <Description> */ + /* This section contains the declaration of LZW-specific functions. */ + /* */ + /*************************************************************************/ + + /************************************************************************ + * + * @function: + * FT_Stream_OpenLZW + * + * @description: + * Open a new stream to parse LZW-compressed font files. This is + * mainly used to support the compressed `*.pcf.Z' fonts that come + * with XFree86. + * + * @input: + * stream :: The target embedding stream. + * + * source :: The source stream. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source stream must be opened _before_ calling this function. + * + * Calling the internal function `FT_Stream_Close' on the new stream will + * *not* call `FT_Stream_Close' on the source stream. None of the stream + * objects will be released to the heap. + * + * The stream implementation is very basic and resets the decompression + * process each time seeking backwards is needed within the stream + * + * In certain builds of the library, LZW compression recognition is + * automatically handled when calling @FT_New_Face or @FT_Open_Face. + * This means that if no font driver is capable of handling the raw + * compressed file, the library will try to open a LZW stream from it + * and re-open the face with it. + * + * This function may return `FT_Err_Unimplemented_Feature' if your build + * of FreeType was not compiled with LZW support. + */ + FT_EXPORT( FT_Error ) + FT_Stream_OpenLZW( FT_Stream stream, + FT_Stream source ); + + /* */ + + +FT_END_HEADER + +#endif /* FTLZW_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftmac.h b/foreign/freetype2/freetype/ftmac.h new file mode 100644 index 0000000..adb15ca --- /dev/null +++ b/foreign/freetype2/freetype/ftmac.h @@ -0,0 +1,274 @@ +/***************************************************************************/ +/* */ +/* ftmac.h */ +/* */ +/* Additional Mac-specific API. */ +/* */ +/* Copyright 1996-2016 by */ +/* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* NOTE: Include this file after FT_FREETYPE_H and after any */ +/* Mac-specific headers (because this header uses Mac types such as */ +/* Handle, FSSpec, FSRef, etc.) */ +/* */ +/***************************************************************************/ + + +#ifndef FTMAC_H_ +#define FTMAC_H_ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + +/* gcc-3.4.1 and later can warn about functions tagged as deprecated */ +#ifndef FT_DEPRECATED_ATTRIBUTE +#if defined(__GNUC__) && \ + ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) +#define FT_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) +#else +#define FT_DEPRECATED_ATTRIBUTE +#endif +#endif + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* mac_specific */ + /* */ + /* <Title> */ + /* Mac Specific Interface */ + /* */ + /* <Abstract> */ + /* Only available on the Macintosh. */ + /* */ + /* <Description> */ + /* The following definitions are only available if FreeType is */ + /* compiled on a Macintosh. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FOND */ + /* */ + /* <Description> */ + /* Create a new face object from a FOND resource. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* fond :: A FOND resource. */ + /* */ + /* face_index :: Only supported for the -1 `sanity check' special */ + /* case. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Notes> */ + /* This function can be used to create @FT_Face objects from fonts */ + /* that are installed in the system as follows. */ + /* */ + /* { */ + /* fond = GetResource( 'FOND', fontName ); */ + /* error = FT_New_Face_From_FOND( library, fond, 0, &face ); */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FOND( FT_Library library, + Handle fond, + FT_Long face_index, + FT_Face *aface ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GetFile_From_Mac_Name */ + /* */ + /* <Description> */ + /* Return an FSSpec for the disk file containing the named font. */ + /* */ + /* <Input> */ + /* fontName :: Mac OS name of the font (e.g., Times New Roman */ + /* Bold). */ + /* */ + /* <Output> */ + /* pathSpec :: FSSpec to the file. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* face_index :: Index of the face. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GetFile_From_Mac_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GetFile_From_Mac_ATS_Name */ + /* */ + /* <Description> */ + /* Return an FSSpec for the disk file containing the named font. */ + /* */ + /* <Input> */ + /* fontName :: Mac OS name of the font in ATS framework. */ + /* */ + /* <Output> */ + /* pathSpec :: FSSpec to the file. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* face_index :: Index of the face. For passing to */ + /* @FT_New_Face_From_FSSpec. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GetFile_From_Mac_ATS_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_GetFilePath_From_Mac_ATS_Name */ + /* */ + /* <Description> */ + /* Return a pathname of the disk file and face index for given font */ + /* name that is handled by ATS framework. */ + /* */ + /* <Input> */ + /* fontName :: Mac OS name of the font in ATS framework. */ + /* */ + /* <Output> */ + /* path :: Buffer to store pathname of the file. For passing */ + /* to @FT_New_Face. The client must allocate this */ + /* buffer before calling this function. */ + /* */ + /* maxPathSize :: Lengths of the buffer `path' that client allocated. */ + /* */ + /* face_index :: Index of the face. For passing to @FT_New_Face. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_GetFilePath_From_Mac_ATS_Name( const char* fontName, + UInt8* path, + UInt32 maxPathSize, + FT_Long* face_index ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FSSpec */ + /* */ + /* <Description> */ + /* Create a new face object from a given resource and typeface index */ + /* using an FSSpec to the font file. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* spec :: FSSpec to the font file. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index~0. */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* @FT_New_Face_From_FSSpec is identical to @FT_New_Face except */ + /* it accepts an FSSpec instead of a path. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FSSpec( FT_Library library, + const FSSpec *spec, + FT_Long face_index, + FT_Face *aface ) + FT_DEPRECATED_ATTRIBUTE; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face_From_FSRef */ + /* */ + /* <Description> */ + /* Create a new face object from a given resource and typeface index */ + /* using an FSRef to the font file. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* spec :: FSRef to the font file. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index~0. */ + /* <Output> */ + /* aface :: A handle to a new face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* @FT_New_Face_From_FSRef is identical to @FT_New_Face except */ + /* it accepts an FSRef instead of a path. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FSRef( FT_Library library, + const FSRef *ref, + FT_Long face_index, + FT_Face *aface ) + FT_DEPRECATED_ATTRIBUTE; + + /* */ + + +FT_END_HEADER + + +#endif /* FTMAC_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftmm.h b/foreign/freetype2/freetype/ftmm.h new file mode 100644 index 0000000..6c05f0c --- /dev/null +++ b/foreign/freetype2/freetype/ftmm.h @@ -0,0 +1,384 @@ +/***************************************************************************/ +/* */ +/* ftmm.h */ +/* */ +/* FreeType Multiple Master font interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTMM_H_ +#define FTMM_H_ + + +#include <ft2build.h> +#include FT_TYPE1_TABLES_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* multiple_masters */ + /* */ + /* <Title> */ + /* Multiple Masters */ + /* */ + /* <Abstract> */ + /* How to manage Multiple Masters fonts. */ + /* */ + /* <Description> */ + /* The following types and functions are used to manage Multiple */ + /* Master fonts, i.e., the selection of specific design instances by */ + /* setting design axis coordinates. */ + /* */ + /* George Williams has extended this interface to make it work with */ + /* both Type~1 Multiple Masters fonts and GX distortable (var) */ + /* fonts. Some of these routines only work with MM fonts, others */ + /* will work with both types. They are similar enough that a */ + /* consistent interface makes sense. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_MM_Axis */ + /* */ + /* <Description> */ + /* A simple structure used to model a given axis in design space for */ + /* Multiple Masters fonts. */ + /* */ + /* This structure can't be used for GX var fonts. */ + /* */ + /* <Fields> */ + /* name :: The axis's name. */ + /* */ + /* minimum :: The axis's minimum design coordinate. */ + /* */ + /* maximum :: The axis's maximum design coordinate. */ + /* */ + typedef struct FT_MM_Axis_ + { + FT_String* name; + FT_Long minimum; + FT_Long maximum; + + } FT_MM_Axis; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Multi_Master */ + /* */ + /* <Description> */ + /* A structure used to model the axes and space of a Multiple Masters */ + /* font. */ + /* */ + /* This structure can't be used for GX var fonts. */ + /* */ + /* <Fields> */ + /* num_axis :: Number of axes. Cannot exceed~4. */ + /* */ + /* num_designs :: Number of designs; should be normally 2^num_axis */ + /* even though the Type~1 specification strangely */ + /* allows for intermediate designs to be present. */ + /* This number cannot exceed~16. */ + /* */ + /* axis :: A table of axis descriptors. */ + /* */ + typedef struct FT_Multi_Master_ + { + FT_UInt num_axis; + FT_UInt num_designs; + FT_MM_Axis axis[T1_MAX_MM_AXIS]; + + } FT_Multi_Master; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Var_Axis */ + /* */ + /* <Description> */ + /* A simple structure used to model a given axis in design space for */ + /* Multiple Masters and GX var fonts. */ + /* */ + /* <Fields> */ + /* name :: The axis's name. */ + /* Not always meaningful for GX. */ + /* */ + /* minimum :: The axis's minimum design coordinate. */ + /* */ + /* def :: The axis's default design coordinate. */ + /* FreeType computes meaningful default values for MM; it */ + /* is then an integer value, not in 16.16 format. */ + /* */ + /* maximum :: The axis's maximum design coordinate. */ + /* */ + /* tag :: The axis's tag (the GX equivalent to `name'). */ + /* FreeType provides default values for MM if possible. */ + /* */ + /* strid :: The entry in `name' table (another GX version of */ + /* `name'). */ + /* Not meaningful for MM. */ + /* */ + typedef struct FT_Var_Axis_ + { + FT_String* name; + + FT_Fixed minimum; + FT_Fixed def; + FT_Fixed maximum; + + FT_ULong tag; + FT_UInt strid; + + } FT_Var_Axis; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Var_Named_Style */ + /* */ + /* <Description> */ + /* A simple structure used to model a named style in a GX var font. */ + /* */ + /* This structure can't be used for MM fonts. */ + /* */ + /* <Fields> */ + /* coords :: The design coordinates for this style. */ + /* This is an array with one entry for each axis. */ + /* */ + /* strid :: The entry in `name' table identifying this style. */ + /* */ + typedef struct FT_Var_Named_Style_ + { + FT_Fixed* coords; + FT_UInt strid; + + } FT_Var_Named_Style; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_MM_Var */ + /* */ + /* <Description> */ + /* A structure used to model the axes and space of a Multiple Masters */ + /* or GX var distortable font. */ + /* */ + /* Some fields are specific to one format and not to the other. */ + /* */ + /* <Fields> */ + /* num_axis :: The number of axes. The maximum value is~4 for */ + /* MM; no limit in GX. */ + /* */ + /* num_designs :: The number of designs; should be normally */ + /* 2^num_axis for MM fonts. Not meaningful for GX */ + /* (where every glyph could have a different */ + /* number of designs). */ + /* */ + /* num_namedstyles :: The number of named styles; only meaningful for */ + /* GX that allows certain design coordinates to */ + /* have a string ID (in the `name' table) */ + /* associated with them. The font can tell the */ + /* user that, for example, Weight=1.5 is `Bold'. */ + /* */ + /* axis :: An axis descriptor table. */ + /* GX fonts contain slightly more data than MM. */ + /* Memory management of this pointer is done */ + /* internally by FreeType. */ + /* */ + /* namedstyle :: A named style table. */ + /* Only meaningful with GX. */ + /* Memory management of this pointer is done */ + /* internally by FreeType. */ + /* */ + typedef struct FT_MM_Var_ + { + FT_UInt num_axis; + FT_UInt num_designs; + FT_UInt num_namedstyles; + FT_Var_Axis* axis; + FT_Var_Named_Style* namedstyle; + + } FT_MM_Var; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Multi_Master */ + /* */ + /* <Description> */ + /* Retrieve the Multiple Master descriptor of a given font. */ + /* */ + /* This function can't be used with GX fonts. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Output> */ + /* amaster :: The Multiple Masters descriptor. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Multi_Master( FT_Face face, + FT_Multi_Master *amaster ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_MM_Var */ + /* */ + /* <Description> */ + /* Retrieve the Multiple Master/GX var descriptor of a given font. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Output> */ + /* amaster :: The Multiple Masters/GX var descriptor. */ + /* Allocates a data structure, which the user must */ + /* deallocate with `free' after use. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_MM_Var( FT_Face face, + FT_MM_Var* *amaster ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_MM_Design_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Masters fonts, choose an interpolated font design */ + /* through design coordinates. */ + /* */ + /* This function can't be used with GX fonts. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available design coordinates. If it */ + /* is larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use default values for the remaining axes. */ + /* */ + /* coords :: An array of design coordinates. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_MM_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Long* coords ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Var_Design_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Master or GX Var fonts, choose an interpolated font */ + /* design through design coordinates. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available design coordinates. If it */ + /* is larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use default values for the remaining axes. */ + /* */ + /* coords :: An array of design coordinates. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Var_Design_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_MM_Blend_Coordinates */ + /* */ + /* <Description> */ + /* For Multiple Masters and GX var fonts, choose an interpolated font */ + /* design through normalized blend coordinates. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face. */ + /* */ + /* <Input> */ + /* num_coords :: The number of available design coordinates. If it */ + /* is larger than the number of axes, ignore the excess */ + /* values. If it is smaller than the number of axes, */ + /* use default values for the remaining axes. */ + /* */ + /* coords :: The design coordinates array (each element must be */ + /* between 0 and 1.0). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_MM_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Var_Blend_Coordinates */ + /* */ + /* <Description> */ + /* This is another name of @FT_Set_MM_Blend_Coordinates. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Var_Blend_Coordinates( FT_Face face, + FT_UInt num_coords, + FT_Fixed* coords ); + + /* */ + + +FT_END_HEADER + +#endif /* FTMM_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftmodapi.h b/foreign/freetype2/freetype/ftmodapi.h new file mode 100644 index 0000000..b4d2758 --- /dev/null +++ b/foreign/freetype2/freetype/ftmodapi.h @@ -0,0 +1,667 @@ +/***************************************************************************/ +/* */ +/* ftmodapi.h */ +/* */ +/* FreeType modules public interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTMODAPI_H_ +#define FTMODAPI_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /* <Title> */ + /* Module Management */ + /* */ + /* <Abstract> */ + /* How to add, upgrade, remove, and control modules from FreeType. */ + /* */ + /* <Description> */ + /* The definitions below are used to manage modules within FreeType. */ + /* Modules can be added, upgraded, and removed at runtime. */ + /* Additionally, some module properties can be controlled also. */ + /* */ + /* Here is a list of possible values of the `module_name' field in */ + /* the @FT_Module_Class structure. */ + /* */ + /* { */ + /* autofitter */ + /* bdf */ + /* cff */ + /* gxvalid */ + /* otvalid */ + /* pcf */ + /* pfr */ + /* psaux */ + /* pshinter */ + /* psnames */ + /* raster1 */ + /* sfnt */ + /* smooth, smooth-lcd, smooth-lcdv */ + /* truetype */ + /* type1 */ + /* type42 */ + /* t1cid */ + /* winfonts */ + /* } */ + /* */ + /* Note that the FreeType Cache sub-system is not a FreeType module. */ + /* */ + /* <Order> */ + /* FT_Module */ + /* FT_Module_Constructor */ + /* FT_Module_Destructor */ + /* FT_Module_Requester */ + /* FT_Module_Class */ + /* */ + /* FT_Add_Module */ + /* FT_Get_Module */ + /* FT_Remove_Module */ + /* FT_Add_Default_Modules */ + /* */ + /* FT_Property_Set */ + /* FT_Property_Get */ + /* */ + /* FT_New_Library */ + /* FT_Done_Library */ + /* FT_Reference_Library */ + /* */ + /* FT_Renderer */ + /* FT_Renderer_Class */ + /* */ + /* FT_Get_Renderer */ + /* FT_Set_Renderer */ + /* */ + /* FT_Set_Debug_Hook */ + /* */ + /*************************************************************************/ + + + /* module bit flags */ +#define FT_MODULE_FONT_DRIVER 1 /* this module is a font driver */ +#define FT_MODULE_RENDERER 2 /* this module is a renderer */ +#define FT_MODULE_HINTER 4 /* this module is a glyph hinter */ +#define FT_MODULE_STYLER 8 /* this module is a styler */ + +#define FT_MODULE_DRIVER_SCALABLE 0x100 /* the driver supports */ + /* scalable fonts */ +#define FT_MODULE_DRIVER_NO_OUTLINES 0x200 /* the driver does not */ + /* support vector outlines */ +#define FT_MODULE_DRIVER_HAS_HINTER 0x400 /* the driver provides its */ + /* own hinter */ +#define FT_MODULE_DRIVER_HINTS_LIGHTLY 0x800 /* the driver's hinter */ + /* produces LIGHT hints */ + + + /* deprecated values */ +#define ft_module_font_driver FT_MODULE_FONT_DRIVER +#define ft_module_renderer FT_MODULE_RENDERER +#define ft_module_hinter FT_MODULE_HINTER +#define ft_module_styler FT_MODULE_STYLER + +#define ft_module_driver_scalable FT_MODULE_DRIVER_SCALABLE +#define ft_module_driver_no_outlines FT_MODULE_DRIVER_NO_OUTLINES +#define ft_module_driver_has_hinter FT_MODULE_DRIVER_HAS_HINTER +#define ft_module_driver_hints_lightly FT_MODULE_DRIVER_HINTS_LIGHTLY + + + typedef FT_Pointer FT_Module_Interface; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Constructor */ + /* */ + /* <Description> */ + /* A function used to initialize (not create) a new module object. */ + /* */ + /* <Input> */ + /* module :: The module to initialize. */ + /* */ + typedef FT_Error + (*FT_Module_Constructor)( FT_Module module ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Destructor */ + /* */ + /* <Description> */ + /* A function used to finalize (not destroy) a given module object. */ + /* */ + /* <Input> */ + /* module :: The module to finalize. */ + /* */ + typedef void + (*FT_Module_Destructor)( FT_Module module ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Module_Requester */ + /* */ + /* <Description> */ + /* A function used to query a given module for a specific interface. */ + /* */ + /* <Input> */ + /* module :: The module to be searched. */ + /* */ + /* name :: The name of the interface in the module. */ + /* */ + typedef FT_Module_Interface + (*FT_Module_Requester)( FT_Module module, + const char* name ); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Module_Class */ + /* */ + /* <Description> */ + /* The module class descriptor. */ + /* */ + /* <Fields> */ + /* module_flags :: Bit flags describing the module. */ + /* */ + /* module_size :: The size of one module object/instance in */ + /* bytes. */ + /* */ + /* module_name :: The name of the module. */ + /* */ + /* module_version :: The version, as a 16.16 fixed number */ + /* (major.minor). */ + /* */ + /* module_requires :: The version of FreeType this module requires, */ + /* as a 16.16 fixed number (major.minor). Starts */ + /* at version 2.0, i.e., 0x20000. */ + /* */ + /* module_init :: The initializing function. */ + /* */ + /* module_done :: The finalizing function. */ + /* */ + /* get_interface :: The interface requesting function. */ + /* */ + typedef struct FT_Module_Class_ + { + FT_ULong module_flags; + FT_Long module_size; + const FT_String* module_name; + FT_Fixed module_version; + FT_Fixed module_requires; + + const void* module_interface; + + FT_Module_Constructor module_init; + FT_Module_Destructor module_done; + FT_Module_Requester get_interface; + + } FT_Module_Class; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Add_Module */ + /* */ + /* <Description> */ + /* Add a new module to a given library instance. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* clazz :: A pointer to class descriptor for the module. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* An error will be returned if a module already exists by that name, */ + /* or if the module requires a version of FreeType that is too great. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Add_Module( FT_Library library, + const FT_Module_Class* clazz ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Module */ + /* */ + /* <Description> */ + /* Find a module by its name. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* module_name :: The module's name (as an ASCII string). */ + /* */ + /* <Return> */ + /* A module handle. 0~if none was found. */ + /* */ + /* <Note> */ + /* FreeType's internal modules aren't documented very well, and you */ + /* should look up the source code for details. */ + /* */ + FT_EXPORT( FT_Module ) + FT_Get_Module( FT_Library library, + const char* module_name ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Remove_Module */ + /* */ + /* <Description> */ + /* Remove a given module from a library instance. */ + /* */ + /* <InOut> */ + /* library :: A handle to a library object. */ + /* */ + /* <Input> */ + /* module :: A handle to a module object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The module object is destroyed by the function in case of success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Remove_Module( FT_Library library, + FT_Module module ); + + + /********************************************************************** + * + * @function: + * FT_Property_Set + * + * @description: + * Set a property for a given module. + * + * @input: + * library :: + * A handle to the library the module is part of. + * + * module_name :: + * The module name. + * + * property_name :: + * The property name. Properties are described in the `Synopsis' + * subsection of the module's documentation. + * + * Note that only a few modules have properties. + * + * value :: + * A generic pointer to a variable or structure that gives the new + * value of the property. The exact definition of `value' is + * dependent on the property; see the `Synopsis' subsection of the + * module's documentation. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `module_name' isn't a valid module name, or `property_name' + * doesn't specify a valid property, or if `value' doesn't represent a + * valid value for the given property, an error is returned. + * + * The following example sets property `bar' (a simple integer) in + * module `foo' to value~1. + * + * { + * FT_UInt bar; + * + * + * bar = 1; + * FT_Property_Set( library, "foo", "bar", &bar ); + * } + * + * Note that the FreeType Cache sub-system doesn't recognize module + * property changes. To avoid glyph lookup confusion within the cache + * you should call @FTC_Manager_Reset to completely flush the cache if + * a module property gets changed after @FTC_Manager_New has been + * called. + * + * It is not possible to set properties of the FreeType Cache + * sub-system itself with FT_Property_Set; use @FTC_Property_Set + * instead. + * + * @since: + * 2.4.11 + * + */ + FT_EXPORT( FT_Error ) + FT_Property_Set( FT_Library library, + const FT_String* module_name, + const FT_String* property_name, + const void* value ); + + + /********************************************************************** + * + * @function: + * FT_Property_Get + * + * @description: + * Get a module's property value. + * + * @input: + * library :: + * A handle to the library the module is part of. + * + * module_name :: + * The module name. + * + * property_name :: + * The property name. Properties are described in the `Synopsis' + * subsection of the module's documentation. + * + * @inout: + * value :: + * A generic pointer to a variable or structure that gives the + * value of the property. The exact definition of `value' is + * dependent on the property; see the `Synopsis' subsection of the + * module's documentation. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `module_name' isn't a valid module name, or `property_name' + * doesn't specify a valid property, or if `value' doesn't represent a + * valid value for the given property, an error is returned. + * + * The following example gets property `baz' (a range) in module `foo'. + * + * { + * typedef range_ + * { + * FT_Int32 min; + * FT_Int32 max; + * + * } range; + * + * range baz; + * + * + * FT_Property_Get( library, "foo", "baz", &baz ); + * } + * + * It is not possible to retrieve properties of the FreeType Cache + * sub-system with FT_Property_Get; use @FTC_Property_Get instead. + * + * @since: + * 2.4.11 + * + */ + FT_EXPORT( FT_Error ) + FT_Property_Get( FT_Library library, + const FT_String* module_name, + const FT_String* property_name, + void* value ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Reference_Library */ + /* */ + /* <Description> */ + /* A counter gets initialized to~1 at the time an @FT_Library */ + /* structure is created. This function increments the counter. */ + /* @FT_Done_Library then only destroys a library if the counter is~1, */ + /* otherwise it simply decrements the counter. */ + /* */ + /* This function helps in managing life-cycles of structures that */ + /* reference @FT_Library objects. */ + /* */ + /* <Input> */ + /* library :: A handle to a target library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Since> */ + /* 2.4.2 */ + /* */ + FT_EXPORT( FT_Error ) + FT_Reference_Library( FT_Library library ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Library */ + /* */ + /* <Description> */ + /* This function is used to create a new FreeType library instance */ + /* from a given memory object. It is thus possible to use libraries */ + /* with distinct memory allocators within the same program. Note, */ + /* however, that the used @FT_Memory structure is expected to remain */ + /* valid for the life of the @FT_Library object. */ + /* */ + /* Normally, you would call this function (followed by a call to */ + /* @FT_Add_Default_Modules or a series of calls to @FT_Add_Module) */ + /* instead of @FT_Init_FreeType to initialize the FreeType library. */ + /* */ + /* Don't use @FT_Done_FreeType but @FT_Done_Library to destroy a */ + /* library instance. */ + /* */ + /* <Input> */ + /* memory :: A handle to the original memory object. */ + /* */ + /* <Output> */ + /* alibrary :: A pointer to handle of a new library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Library. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Library( FT_Memory memory, + FT_Library *alibrary ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Library */ + /* */ + /* <Description> */ + /* Discard a given library object. This closes all drivers and */ + /* discards all resource objects. */ + /* */ + /* <Input> */ + /* library :: A handle to the target library. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* See the discussion of reference counters in the description of */ + /* @FT_Reference_Library. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Library( FT_Library library ); + + /* */ + + typedef void + (*FT_DebugHook_Func)( void* arg ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Debug_Hook */ + /* */ + /* <Description> */ + /* Set a debug hook function for debugging the interpreter of a font */ + /* format. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* hook_index :: The index of the debug hook. You should use the */ + /* values defined in `ftobjs.h', e.g., */ + /* `FT_DEBUG_HOOK_TRUETYPE'. */ + /* */ + /* debug_hook :: The function used to debug the interpreter. */ + /* */ + /* <Note> */ + /* Currently, four debug hook slots are available, but only two (for */ + /* the TrueType and the Type~1 interpreter) are defined. */ + /* */ + /* Since the internal headers of FreeType are no longer installed, */ + /* the symbol `FT_DEBUG_HOOK_TRUETYPE' isn't available publicly. */ + /* This is a bug and will be fixed in a forthcoming release. */ + /* */ + FT_EXPORT( void ) + FT_Set_Debug_Hook( FT_Library library, + FT_UInt hook_index, + FT_DebugHook_Func debug_hook ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Add_Default_Modules */ + /* */ + /* <Description> */ + /* Add the set of default drivers to a given library object. */ + /* This is only useful when you create a library object with */ + /* @FT_New_Library (usually to plug a custom memory manager). */ + /* */ + /* <InOut> */ + /* library :: A handle to a new library object. */ + /* */ + FT_EXPORT( void ) + FT_Add_Default_Modules( FT_Library library ); + + + + /************************************************************************** + * + * @section: + * truetype_engine + * + * @title: + * The TrueType Engine + * + * @abstract: + * TrueType bytecode support. + * + * @description: + * This section contains a function used to query the level of TrueType + * bytecode support compiled in this version of the library. + * + */ + + + /************************************************************************** + * + * @enum: + * FT_TrueTypeEngineType + * + * @description: + * A list of values describing which kind of TrueType bytecode + * engine is implemented in a given FT_Library instance. It is used + * by the @FT_Get_TrueType_Engine_Type function. + * + * @values: + * FT_TRUETYPE_ENGINE_TYPE_NONE :: + * The library doesn't implement any kind of bytecode interpreter. + * + * FT_TRUETYPE_ENGINE_TYPE_UNPATENTED :: + * Deprecated and removed. + * + * FT_TRUETYPE_ENGINE_TYPE_PATENTED :: + * The library implements a bytecode interpreter that covers + * the full instruction set of the TrueType virtual machine (this + * was governed by patents until May 2010, hence the name). + * + * @since: + * 2.2 + * + */ + typedef enum FT_TrueTypeEngineType_ + { + FT_TRUETYPE_ENGINE_TYPE_NONE = 0, + FT_TRUETYPE_ENGINE_TYPE_UNPATENTED, + FT_TRUETYPE_ENGINE_TYPE_PATENTED + + } FT_TrueTypeEngineType; + + + /************************************************************************** + * + * @func: + * FT_Get_TrueType_Engine_Type + * + * @description: + * Return an @FT_TrueTypeEngineType value to indicate which level of + * the TrueType virtual machine a given library instance supports. + * + * @input: + * library :: + * A library instance. + * + * @return: + * A value indicating which level is supported. + * + * @since: + * 2.2 + * + */ + FT_EXPORT( FT_TrueTypeEngineType ) + FT_Get_TrueType_Engine_Type( FT_Library library ); + + /* */ + + +FT_END_HEADER + +#endif /* FTMODAPI_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftmoderr.h b/foreign/freetype2/freetype/ftmoderr.h new file mode 100644 index 0000000..2a7671c --- /dev/null +++ b/foreign/freetype2/freetype/ftmoderr.h @@ -0,0 +1,194 @@ +/***************************************************************************/ +/* */ +/* ftmoderr.h */ +/* */ +/* FreeType module error offsets (specification). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the FreeType module error codes. */ + /* */ + /* If the macro FT_CONFIG_OPTION_USE_MODULE_ERRORS in `ftoption.h' is */ + /* set, the lower byte of an error value identifies the error code as */ + /* usual. In addition, the higher byte identifies the module. For */ + /* example, the error `FT_Err_Invalid_File_Format' has value 0x0003, the */ + /* error `TT_Err_Invalid_File_Format' has value 0x1303, the error */ + /* `T1_Err_Invalid_File_Format' has value 0x1403, etc. */ + /* */ + /* Note that `FT_Err_Ok', `TT_Err_Ok', etc. are always equal to zero, */ + /* including the high byte. */ + /* */ + /* If FT_CONFIG_OPTION_USE_MODULE_ERRORS isn't set, the higher byte of */ + /* an error value is set to zero. */ + /* */ + /* To hide the various `XXX_Err_' prefixes in the source code, FreeType */ + /* provides some macros in `fttypes.h'. */ + /* */ + /* FT_ERR( err ) */ + /* Add current error module prefix (as defined with the */ + /* `FT_ERR_PREFIX' macro) to `err'. For example, in the BDF module */ + /* the line */ + /* */ + /* error = FT_ERR( Invalid_Outline ); */ + /* */ + /* expands to */ + /* */ + /* error = BDF_Err_Invalid_Outline; */ + /* */ + /* For simplicity, you can always use `FT_Err_Ok' directly instead */ + /* of `FT_ERR( Ok )'. */ + /* */ + /* FT_ERR_EQ( errcode, err ) */ + /* FT_ERR_NEQ( errcode, err ) */ + /* Compare error code `errcode' with the error `err' for equality */ + /* and inequality, respectively. Example: */ + /* */ + /* if ( FT_ERR_EQ( error, Invalid_Outline ) ) */ + /* ... */ + /* */ + /* Using this macro you don't have to think about error prefixes. */ + /* Of course, if module errors are not active, the above example is */ + /* the same as */ + /* */ + /* if ( error == FT_Err_Invalid_Outline ) */ + /* ... */ + /* */ + /* FT_ERROR_BASE( errcode ) */ + /* FT_ERROR_MODULE( errcode ) */ + /* Get base error and module error code, respectively. */ + /* */ + /* */ + /* It can also be used to create a module error message table easily */ + /* with something like */ + /* */ + /* { */ + /* #undef FTMODERR_H_ */ + /* #define FT_MODERRDEF( e, v, s ) { FT_Mod_Err_ ## e, s }, */ + /* #define FT_MODERR_START_LIST { */ + /* #define FT_MODERR_END_LIST { 0, 0 } }; */ + /* */ + /* const struct */ + /* { */ + /* int mod_err_offset; */ + /* const char* mod_err_msg */ + /* } ft_mod_errors[] = */ + /* */ + /* #include FT_MODULE_ERRORS_H */ + /* } */ + /* */ + /*************************************************************************/ + + +#ifndef FTMODERR_H_ +#define FTMODERR_H_ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + +#ifndef FT_MODERRDEF + +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = v, +#else +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = 0, +#endif + +#define FT_MODERR_START_LIST enum { +#define FT_MODERR_END_LIST FT_Mod_Err_Max }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_MODERRDEF */ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** LIST MODULE ERROR BASES *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_MODERR_START_LIST + FT_MODERR_START_LIST +#endif + + + FT_MODERRDEF( Base, 0x000, "base module" ) + FT_MODERRDEF( Autofit, 0x100, "autofitter module" ) + FT_MODERRDEF( BDF, 0x200, "BDF module" ) + FT_MODERRDEF( Bzip2, 0x300, "Bzip2 module" ) + FT_MODERRDEF( Cache, 0x400, "cache module" ) + FT_MODERRDEF( CFF, 0x500, "CFF module" ) + FT_MODERRDEF( CID, 0x600, "CID module" ) + FT_MODERRDEF( Gzip, 0x700, "Gzip module" ) + FT_MODERRDEF( LZW, 0x800, "LZW module" ) + FT_MODERRDEF( OTvalid, 0x900, "OpenType validation module" ) + FT_MODERRDEF( PCF, 0xA00, "PCF module" ) + FT_MODERRDEF( PFR, 0xB00, "PFR module" ) + FT_MODERRDEF( PSaux, 0xC00, "PS auxiliary module" ) + FT_MODERRDEF( PShinter, 0xD00, "PS hinter module" ) + FT_MODERRDEF( PSnames, 0xE00, "PS names module" ) + FT_MODERRDEF( Raster, 0xF00, "raster module" ) + FT_MODERRDEF( SFNT, 0x1000, "SFNT module" ) + FT_MODERRDEF( Smooth, 0x1100, "smooth raster module" ) + FT_MODERRDEF( TrueType, 0x1200, "TrueType module" ) + FT_MODERRDEF( Type1, 0x1300, "Type 1 module" ) + FT_MODERRDEF( Type42, 0x1400, "Type 42 module" ) + FT_MODERRDEF( Winfonts, 0x1500, "Windows FON/FNT module" ) + FT_MODERRDEF( GXvalid, 0x1600, "GX validation module" ) + + +#ifdef FT_MODERR_END_LIST + FT_MODERR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_MODERR_START_LIST +#undef FT_MODERR_END_LIST +#undef FT_MODERRDEF +#undef FT_NEED_EXTERN_C + + +#endif /* FTMODERR_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftotval.h b/foreign/freetype2/freetype/ftotval.h new file mode 100644 index 0000000..c678ef3 --- /dev/null +++ b/foreign/freetype2/freetype/ftotval.h @@ -0,0 +1,204 @@ +/***************************************************************************/ +/* */ +/* ftotval.h */ +/* */ +/* FreeType API for validating OpenType tables (specification). */ +/* */ +/* Copyright 2004-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +/***************************************************************************/ +/* */ +/* */ +/* Warning: This module might be moved to a different library in the */ +/* future to avoid a tight dependency between FreeType and the */ +/* OpenType specification. */ +/* */ +/* */ +/***************************************************************************/ + + +#ifndef FTOTVAL_H_ +#define FTOTVAL_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* ot_validation */ + /* */ + /* <Title> */ + /* OpenType Validation */ + /* */ + /* <Abstract> */ + /* An API to validate OpenType tables. */ + /* */ + /* <Description> */ + /* This section contains the declaration of functions to validate */ + /* some OpenType tables (BASE, GDEF, GPOS, GSUB, JSTF, MATH). */ + /* */ + /* <Order> */ + /* FT_OpenType_Validate */ + /* FT_OpenType_Free */ + /* */ + /* FT_VALIDATE_OTXXX */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @enum: + * FT_VALIDATE_OTXXX + * + * @description: + * A list of bit-field constants used with @FT_OpenType_Validate to + * indicate which OpenType tables should be validated. + * + * @values: + * FT_VALIDATE_BASE :: + * Validate BASE table. + * + * FT_VALIDATE_GDEF :: + * Validate GDEF table. + * + * FT_VALIDATE_GPOS :: + * Validate GPOS table. + * + * FT_VALIDATE_GSUB :: + * Validate GSUB table. + * + * FT_VALIDATE_JSTF :: + * Validate JSTF table. + * + * FT_VALIDATE_MATH :: + * Validate MATH table. + * + * FT_VALIDATE_OT :: + * Validate all OpenType tables (BASE, GDEF, GPOS, GSUB, JSTF, MATH). + * + */ +#define FT_VALIDATE_BASE 0x0100 +#define FT_VALIDATE_GDEF 0x0200 +#define FT_VALIDATE_GPOS 0x0400 +#define FT_VALIDATE_GSUB 0x0800 +#define FT_VALIDATE_JSTF 0x1000 +#define FT_VALIDATE_MATH 0x2000 + +#define FT_VALIDATE_OT FT_VALIDATE_BASE | \ + FT_VALIDATE_GDEF | \ + FT_VALIDATE_GPOS | \ + FT_VALIDATE_GSUB | \ + FT_VALIDATE_JSTF | \ + FT_VALIDATE_MATH + + /********************************************************************** + * + * @function: + * FT_OpenType_Validate + * + * @description: + * Validate various OpenType tables to assure that all offsets and + * indices are valid. The idea is that a higher-level library that + * actually does the text layout can access those tables without + * error checking (which can be quite time consuming). + * + * @input: + * face :: + * A handle to the input face. + * + * validation_flags :: + * A bit field that specifies the tables to be validated. See + * @FT_VALIDATE_OTXXX for possible values. + * + * @output: + * BASE_table :: + * A pointer to the BASE table. + * + * GDEF_table :: + * A pointer to the GDEF table. + * + * GPOS_table :: + * A pointer to the GPOS table. + * + * GSUB_table :: + * A pointer to the GSUB table. + * + * JSTF_table :: + * A pointer to the JSTF table. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with OpenType fonts, returning an error + * otherwise. + * + * After use, the application should deallocate the five tables with + * @FT_OpenType_Free. A NULL value indicates that the table either + * doesn't exist in the font, or the application hasn't asked for + * validation. + */ + FT_EXPORT( FT_Error ) + FT_OpenType_Validate( FT_Face face, + FT_UInt validation_flags, + FT_Bytes *BASE_table, + FT_Bytes *GDEF_table, + FT_Bytes *GPOS_table, + FT_Bytes *GSUB_table, + FT_Bytes *JSTF_table ); + + /********************************************************************** + * + * @function: + * FT_OpenType_Free + * + * @description: + * Free the buffer allocated by OpenType validator. + * + * @input: + * face :: + * A handle to the input face. + * + * table :: + * The pointer to the buffer that is allocated by + * @FT_OpenType_Validate. + * + * @note: + * This function must be used to free the buffer allocated by + * @FT_OpenType_Validate only. + */ + FT_EXPORT( void ) + FT_OpenType_Free( FT_Face face, + FT_Bytes table ); + + /* */ + + +FT_END_HEADER + +#endif /* FTOTVAL_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftoutln.h b/foreign/freetype2/freetype/ftoutln.h new file mode 100644 index 0000000..6a64512 --- /dev/null +++ b/foreign/freetype2/freetype/ftoutln.h @@ -0,0 +1,574 @@ +/***************************************************************************/ +/* */ +/* ftoutln.h */ +/* */ +/* Support for the FT_Outline type used to store glyph shapes of */ +/* most scalable font formats (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTOUTLN_H_ +#define FTOUTLN_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /* <Title> */ + /* Outline Processing */ + /* */ + /* <Abstract> */ + /* Functions to create, transform, and render vectorial glyph images. */ + /* */ + /* <Description> */ + /* This section contains routines used to create and destroy scalable */ + /* glyph images known as `outlines'. These can also be measured, */ + /* transformed, and converted into bitmaps and pixmaps. */ + /* */ + /* <Order> */ + /* FT_Outline */ + /* FT_Outline_New */ + /* FT_Outline_Done */ + /* FT_Outline_Copy */ + /* FT_Outline_Translate */ + /* FT_Outline_Transform */ + /* FT_Outline_Embolden */ + /* FT_Outline_EmboldenXY */ + /* FT_Outline_Reverse */ + /* FT_Outline_Check */ + /* */ + /* FT_Outline_Get_CBox */ + /* FT_Outline_Get_BBox */ + /* */ + /* FT_Outline_Get_Bitmap */ + /* FT_Outline_Render */ + /* FT_Outline_Decompose */ + /* FT_Outline_Funcs */ + /* FT_Outline_MoveToFunc */ + /* FT_Outline_LineToFunc */ + /* FT_Outline_ConicToFunc */ + /* FT_Outline_CubicToFunc */ + /* */ + /* FT_Orientation */ + /* FT_Outline_Get_Orientation */ + /* */ + /* FT_OUTLINE_XXX */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Decompose */ + /* */ + /* <Description> */ + /* Walk over an outline's structure to decompose it into individual */ + /* segments and Bézier arcs. This function also emits `move to' */ + /* operations to indicate the start of new contours in the outline. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source target. */ + /* */ + /* func_interface :: A table of `emitters', i.e., function pointers */ + /* called during decomposition to indicate path */ + /* operations. */ + /* */ + /* <InOut> */ + /* user :: A typeless pointer that is passed to each */ + /* emitter during the decomposition. It can be */ + /* used to store the state during the */ + /* decomposition. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* A contour that contains a single point only is represented by a */ + /* `move to' operation followed by `line to' to the same point. In */ + /* most cases, it is best to filter this out before using the */ + /* outline for stroking purposes (otherwise it would result in a */ + /* visible dot when round caps are used). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Decompose( FT_Outline* outline, + const FT_Outline_Funcs* func_interface, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_New */ + /* */ + /* <Description> */ + /* Create a new outline of a given size. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object from where the */ + /* outline is allocated. Note however that the new */ + /* outline will *not* necessarily be *freed*, when */ + /* destroying the library, by @FT_Done_FreeType. */ + /* */ + /* numPoints :: The maximum number of points within the outline. */ + /* Must be smaller than or equal to 0xFFFF (65535). */ + /* */ + /* numContours :: The maximum number of contours within the outline. */ + /* This value must be in the range 0 to `numPoints'. */ + /* */ + /* <Output> */ + /* anoutline :: A handle to the new outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The reason why this function takes a `library' parameter is simply */ + /* to use the library's memory allocator. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_New( FT_Library library, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_New_Internal( FT_Memory memory, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Done */ + /* */ + /* <Description> */ + /* Destroy an outline created with @FT_Outline_New. */ + /* */ + /* <Input> */ + /* library :: A handle of the library object used to allocate the */ + /* outline. */ + /* */ + /* outline :: A pointer to the outline object to be discarded. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If the outline's `owner' field is not set, only the outline */ + /* descriptor will be released. */ + /* */ + /* The reason why this function takes an `library' parameter is */ + /* simply to use ft_mem_free(). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Done( FT_Library library, + FT_Outline* outline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_Done_Internal( FT_Memory memory, + FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Check */ + /* */ + /* <Description> */ + /* Check the contents of an outline descriptor. */ + /* */ + /* <Input> */ + /* outline :: A handle to a source outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Check( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_CBox */ + /* */ + /* <Description> */ + /* Return an outline's `control box'. The control box encloses all */ + /* the outline's points, including Bézier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* that contains Bézier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component, which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <Output> */ + /* acbox :: The outline's control box. */ + /* */ + /* <Note> */ + /* See @FT_Glyph_Get_CBox for a discussion of tricky fonts. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Get_CBox( const FT_Outline* outline, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Translate */ + /* */ + /* <Description> */ + /* Apply a simple translation to the points of an outline. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* xOffset :: The horizontal offset. */ + /* */ + /* yOffset :: The vertical offset. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Translate( const FT_Outline* outline, + FT_Pos xOffset, + FT_Pos yOffset ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Copy */ + /* */ + /* <Description> */ + /* Copy an outline into another one. Both objects must have the */ + /* same sizes (number of points & number of contours) when this */ + /* function is called. */ + /* */ + /* <Input> */ + /* source :: A handle to the source outline. */ + /* */ + /* <Output> */ + /* target :: A handle to the target outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Copy( const FT_Outline* source, + FT_Outline *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Transform */ + /* */ + /* <Description> */ + /* Apply a simple 2x2 matrix to all of an outline's points. Useful */ + /* for applying rotations, slanting, flipping, etc. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation matrix. */ + /* */ + /* <Note> */ + /* You can use @FT_Outline_Translate if you need to translate the */ + /* outline's points. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Transform( const FT_Outline* outline, + const FT_Matrix* matrix ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Embolden */ + /* */ + /* <Description> */ + /* Embolden an outline. The new outline will be at most 4~times */ + /* `strength' pixels wider and higher. You may think of the left and */ + /* bottom borders as unchanged. */ + /* */ + /* Negative `strength' values to reduce the outline thickness are */ + /* possible also. */ + /* */ + /* <InOut> */ + /* outline :: A handle to the target outline. */ + /* */ + /* <Input> */ + /* strength :: How strong the glyph is emboldened. Expressed in */ + /* 26.6 pixel format. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The used algorithm to increase or decrease the thickness of the */ + /* glyph doesn't change the number of points; this means that certain */ + /* situations like acute angles or intersections are sometimes */ + /* handled incorrectly. */ + /* */ + /* If you need `better' metrics values you should call */ + /* @FT_Outline_Get_CBox or @FT_Outline_Get_BBox. */ + /* */ + /* Example call: */ + /* */ + /* { */ + /* FT_Load_Glyph( face, index, FT_LOAD_DEFAULT ); */ + /* if ( face->glyph->format == FT_GLYPH_FORMAT_OUTLINE ) */ + /* FT_Outline_Embolden( &face->glyph->outline, strength ); */ + /* } */ + /* */ + /* To get meaningful results, font scaling values must be set with */ + /* functions like @FT_Set_Char_Size before calling FT_Render_Glyph. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Embolden( FT_Outline* outline, + FT_Pos strength ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_EmboldenXY */ + /* */ + /* <Description> */ + /* Embolden an outline. The new outline will be `xstrength' pixels */ + /* wider and `ystrength' pixels higher. Otherwise, it is similar to */ + /* @FT_Outline_Embolden, which uses the same strength in both */ + /* directions. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_EmboldenXY( FT_Outline* outline, + FT_Pos xstrength, + FT_Pos ystrength ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Reverse */ + /* */ + /* <Description> */ + /* Reverse the drawing direction of an outline. This is used to */ + /* ensure consistent fill conventions for mirrored glyphs. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Note> */ + /* This function toggles the bit flag @FT_OUTLINE_REVERSE_FILL in */ + /* the outline's `flags' field. */ + /* */ + /* It shouldn't be used by a normal client application, unless it */ + /* knows what it is doing. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Reverse( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_Bitmap */ + /* */ + /* <Description> */ + /* Render an outline within a bitmap. The outline's image is simply */ + /* OR-ed to the target bitmap. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <InOut> */ + /* abitmap :: A pointer to the target bitmap descriptor. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function does NOT CREATE the bitmap, it only renders an */ + /* outline image within the one you pass to it! Consequently, the */ + /* various fields in `abitmap' should be set accordingly. */ + /* */ + /* It will use the raster corresponding to the default glyph format. */ + /* */ + /* The value of the `num_grays' field in `abitmap' is ignored. If */ + /* you select the gray-level rasterizer, and you want less than 256 */ + /* gray levels, you have to use @FT_Outline_Render directly. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Get_Bitmap( FT_Library library, + FT_Outline* outline, + const FT_Bitmap *abitmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Render */ + /* */ + /* <Description> */ + /* Render an outline within a bitmap using the current scan-convert. */ + /* This function uses an @FT_Raster_Params structure as an argument, */ + /* allowing advanced features like direct composition, translucency, */ + /* etc. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <InOut> */ + /* params :: A pointer to an @FT_Raster_Params structure used to */ + /* describe the rendering operation. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You should know what you are doing and how @FT_Raster_Params works */ + /* to use this function. */ + /* */ + /* The field `params.source' will be set to `outline' before the scan */ + /* converter is called, which means that the value you give to it is */ + /* actually ignored. */ + /* */ + /* The gray-level rasterizer always uses 256 gray levels. If you */ + /* want less gray levels, you have to provide your own span callback. */ + /* See the @FT_RASTER_FLAG_DIRECT value of the `flags' field in the */ + /* @FT_Raster_Params structure for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Render( FT_Library library, + FT_Outline* outline, + FT_Raster_Params* params ); + + + /************************************************************************** + * + * @enum: + * FT_Orientation + * + * @description: + * A list of values used to describe an outline's contour orientation. + * + * The TrueType and PostScript specifications use different conventions + * to determine whether outline contours should be filled or unfilled. + * + * @values: + * FT_ORIENTATION_TRUETYPE :: + * According to the TrueType specification, clockwise contours must + * be filled, and counter-clockwise ones must be unfilled. + * + * FT_ORIENTATION_POSTSCRIPT :: + * According to the PostScript specification, counter-clockwise contours + * must be filled, and clockwise ones must be unfilled. + * + * FT_ORIENTATION_FILL_RIGHT :: + * This is identical to @FT_ORIENTATION_TRUETYPE, but is used to + * remember that in TrueType, everything that is to the right of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_FILL_LEFT :: + * This is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to + * remember that in PostScript, everything that is to the left of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_NONE :: + * The orientation cannot be determined. That is, different parts of + * the glyph have different orientation. + * + */ + typedef enum FT_Orientation_ + { + FT_ORIENTATION_TRUETYPE = 0, + FT_ORIENTATION_POSTSCRIPT = 1, + FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE, + FT_ORIENTATION_FILL_LEFT = FT_ORIENTATION_POSTSCRIPT, + FT_ORIENTATION_NONE + + } FT_Orientation; + + + /************************************************************************** + * + * @function: + * FT_Outline_Get_Orientation + * + * @description: + * This function analyzes a glyph outline and tries to compute its + * fill orientation (see @FT_Orientation). This is done by integrating + * the total area covered by the outline. The positive integral + * corresponds to the clockwise orientation and @FT_ORIENTATION_POSTSCRIPT + * is returned. The negative integral corresponds to the counter-clockwise + * orientation and @FT_ORIENTATION_TRUETYPE is returned. + * + * Note that this will return @FT_ORIENTATION_TRUETYPE for empty + * outlines. + * + * @input: + * outline :: + * A handle to the source outline. + * + * @return: + * The orientation. + * + */ + FT_EXPORT( FT_Orientation ) + FT_Outline_Get_Orientation( FT_Outline* outline ); + + /* */ + + +FT_END_HEADER + +#endif /* FTOUTLN_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/foreign/freetype2/freetype/ftpfr.h b/foreign/freetype2/freetype/ftpfr.h new file mode 100644 index 0000000..2e1bff2 --- /dev/null +++ b/foreign/freetype2/freetype/ftpfr.h @@ -0,0 +1,172 @@ +/***************************************************************************/ +/* */ +/* ftpfr.h */ +/* */ +/* FreeType API for accessing PFR-specific data (specification only). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTPFR_H_ +#define FTPFR_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* pfr_fonts */ + /* */ + /* <Title> */ + /* PFR Fonts */ + /* */ + /* <Abstract> */ + /* PFR/TrueDoc specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of PFR-specific functions. */ + /* */ + /*************************************************************************/ + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Metrics + * + * @description: + * Return the outline and metrics resolutions of a given PFR face. + * + * @input: + * face :: Handle to the input face. It can be a non-PFR face. + * + * @output: + * aoutline_resolution :: + * Outline resolution. This is equivalent to `face->units_per_EM' + * for non-PFR fonts. Optional (parameter can be NULL). + * + * ametrics_resolution :: + * Metrics resolution. This is equivalent to `outline_resolution' + * for non-PFR fonts. Optional (parameter can be NULL). + * + * ametrics_x_scale :: + * A 16.16 fixed-point number used to scale distance expressed + * in metrics units to device sub-pixels. This is equivalent to + * `face->size->x_scale', but for metrics only. Optional (parameter + * can be NULL). + * + * ametrics_y_scale :: + * Same as `ametrics_x_scale' but for the vertical direction. + * optional (parameter can be NULL). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If the input face is not a PFR, this function will return an error. + * However, in all cases, it will return valid values. + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Metrics( FT_Face face, + FT_UInt *aoutline_resolution, + FT_UInt *ametrics_resolution, + FT_Fixed *ametrics_x_scale, + FT_Fixed *ametrics_y_scale ); + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Kerning + * + * @description: + * Return the kerning pair corresponding to two glyphs in a PFR face. + * The distance is expressed in metrics units, unlike the result of + * @FT_Get_Kerning. + * + * @input: + * face :: A handle to the input face. + * + * left :: Index of the left glyph. + * + * right :: Index of the right glyph. + * + * @output: + * avector :: A kerning vector. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function always return distances in original PFR metrics + * units. This is unlike @FT_Get_Kerning with the @FT_KERNING_UNSCALED + * mode, which always returns distances converted to outline units. + * + * You can use the value of the `x_scale' and `y_scale' parameters + * returned by @FT_Get_PFR_Metrics to scale these to device sub-pixels. + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Kerning( FT_Face face, + FT_UInt left, + FT_UInt right, + FT_Vector *avector ); + + + /********************************************************************** + * + * @function: + * FT_Get_PFR_Advance + * + * @description: + * Return a given glyph advance, expressed in original metrics units, + * from a PFR font. + * + * @input: + * face :: A handle to the input face. + * + * gindex :: The glyph index. + * + * @output: + * aadvance :: The glyph advance in metrics units. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You can use the `x_scale' or `y_scale' results of @FT_Get_PFR_Metrics + * to convert the advance to device sub-pixels (i.e., 1/64th of pixels). + */ + FT_EXPORT( FT_Error ) + FT_Get_PFR_Advance( FT_Face face, + FT_UInt gindex, + FT_Pos *aadvance ); + + /* */ + + +FT_END_HEADER + +#endif /* FTPFR_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftrender.h b/foreign/freetype2/freetype/ftrender.h new file mode 100644 index 0000000..9f7ed9e --- /dev/null +++ b/foreign/freetype2/freetype/ftrender.h @@ -0,0 +1,232 @@ +/***************************************************************************/ +/* */ +/* ftrender.h */ +/* */ +/* FreeType renderer modules public interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTRENDER_H_ +#define FTRENDER_H_ + + +#include <ft2build.h> +#include FT_MODULE_H +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* module_management */ + /* */ + /*************************************************************************/ + + + /* create a new glyph object */ + typedef FT_Error + (*FT_Glyph_InitFunc)( FT_Glyph glyph, + FT_GlyphSlot slot ); + + /* destroys a given glyph object */ + typedef void + (*FT_Glyph_DoneFunc)( FT_Glyph glyph ); + + typedef void + (*FT_Glyph_TransformFunc)( FT_Glyph glyph, + const FT_Matrix* matrix, + const FT_Vector* delta ); + + typedef void + (*FT_Glyph_GetBBoxFunc)( FT_Glyph glyph, + FT_BBox* abbox ); + + typedef FT_Error + (*FT_Glyph_CopyFunc)( FT_Glyph source, + FT_Glyph target ); + + typedef FT_Error + (*FT_Glyph_PrepareFunc)( FT_Glyph glyph, + FT_GlyphSlot slot ); + +/* deprecated */ +#define FT_Glyph_Init_Func FT_Glyph_InitFunc +#define FT_Glyph_Done_Func FT_Glyph_DoneFunc +#define FT_Glyph_Transform_Func FT_Glyph_TransformFunc +#define FT_Glyph_BBox_Func FT_Glyph_GetBBoxFunc +#define FT_Glyph_Copy_Func FT_Glyph_CopyFunc +#define FT_Glyph_Prepare_Func FT_Glyph_PrepareFunc + + + struct FT_Glyph_Class_ + { + FT_Long glyph_size; + FT_Glyph_Format glyph_format; + FT_Glyph_InitFunc glyph_init; + FT_Glyph_DoneFunc glyph_done; + FT_Glyph_CopyFunc glyph_copy; + FT_Glyph_TransformFunc glyph_transform; + FT_Glyph_GetBBoxFunc glyph_bbox; + FT_Glyph_PrepareFunc glyph_prepare; + }; + + + typedef FT_Error + (*FT_Renderer_RenderFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + FT_UInt mode, + const FT_Vector* origin ); + + typedef FT_Error + (*FT_Renderer_TransformFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + const FT_Matrix* matrix, + const FT_Vector* delta ); + + + typedef void + (*FT_Renderer_GetCBoxFunc)( FT_Renderer renderer, + FT_GlyphSlot slot, + FT_BBox* cbox ); + + + typedef FT_Error + (*FT_Renderer_SetModeFunc)( FT_Renderer renderer, + FT_ULong mode_tag, + FT_Pointer mode_ptr ); + +/* deprecated identifiers */ +#define FTRenderer_render FT_Renderer_RenderFunc +#define FTRenderer_transform FT_Renderer_TransformFunc +#define FTRenderer_getCBox FT_Renderer_GetCBoxFunc +#define FTRenderer_setMode FT_Renderer_SetModeFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Renderer_Class */ + /* */ + /* <Description> */ + /* The renderer module class descriptor. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Module_Class fields. */ + /* */ + /* glyph_format :: The glyph image format this renderer handles. */ + /* */ + /* render_glyph :: A method used to render the image that is in a */ + /* given glyph slot into a bitmap. */ + /* */ + /* transform_glyph :: A method used to transform the image that is in */ + /* a given glyph slot. */ + /* */ + /* get_glyph_cbox :: A method used to access the glyph's cbox. */ + /* */ + /* set_mode :: A method used to pass additional parameters. */ + /* */ + /* raster_class :: For @FT_GLYPH_FORMAT_OUTLINE renderers only. */ + /* This is a pointer to its raster's class. */ + /* */ + typedef struct FT_Renderer_Class_ + { + FT_Module_Class root; + + FT_Glyph_Format glyph_format; + + FT_Renderer_RenderFunc render_glyph; + FT_Renderer_TransformFunc transform_glyph; + FT_Renderer_GetCBoxFunc get_glyph_cbox; + FT_Renderer_SetModeFunc set_mode; + + FT_Raster_Funcs* raster_class; + + } FT_Renderer_Class; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Renderer */ + /* */ + /* <Description> */ + /* Retrieve the current renderer for a given glyph format. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object. */ + /* */ + /* format :: The glyph format. */ + /* */ + /* <Return> */ + /* A renderer handle. 0~if none found. */ + /* */ + /* <Note> */ + /* An error will be returned if a module already exists by that name, */ + /* or if the module requires a version of FreeType that is too great. */ + /* */ + /* To add a new renderer, simply use @FT_Add_Module. To retrieve a */ + /* renderer by its name, use @FT_Get_Module. */ + /* */ + FT_EXPORT( FT_Renderer ) + FT_Get_Renderer( FT_Library library, + FT_Glyph_Format format ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Renderer */ + /* */ + /* <Description> */ + /* Set the current renderer to use, and set additional mode. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library object. */ + /* */ + /* <Input> */ + /* renderer :: A handle to the renderer object. */ + /* */ + /* num_params :: The number of additional parameters. */ + /* */ + /* parameters :: Additional parameters. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* In case of success, the renderer will be used to convert glyph */ + /* images in the renderer's known format into bitmaps. */ + /* */ + /* This doesn't change the current renderer for other formats. */ + /* */ + /* Currently, no FreeType renderer module uses `parameters'; you */ + /* should thus always pass NULL as the value. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Renderer( FT_Library library, + FT_Renderer renderer, + FT_UInt num_params, + FT_Parameter* parameters ); + + /* */ + + +FT_END_HEADER + +#endif /* FTRENDER_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftsizes.h b/foreign/freetype2/freetype/ftsizes.h new file mode 100644 index 0000000..55e0d5c --- /dev/null +++ b/foreign/freetype2/freetype/ftsizes.h @@ -0,0 +1,159 @@ +/***************************************************************************/ +/* */ +/* ftsizes.h */ +/* */ +/* FreeType size objects management (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Typical application would normally not need to use these functions. */ + /* However, they have been placed in a public API for the rare cases */ + /* where they are needed. */ + /* */ + /*************************************************************************/ + + +#ifndef FTSIZES_H_ +#define FTSIZES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* sizes_management */ + /* */ + /* <Title> */ + /* Size Management */ + /* */ + /* <Abstract> */ + /* Managing multiple sizes per face. */ + /* */ + /* <Description> */ + /* When creating a new face object (e.g., with @FT_New_Face), an */ + /* @FT_Size object is automatically created and used to store all */ + /* pixel-size dependent information, available in the `face->size' */ + /* field. */ + /* */ + /* It is however possible to create more sizes for a given face, */ + /* mostly in order to manage several character pixel sizes of the */ + /* same font family and style. See @FT_New_Size and @FT_Done_Size. */ + /* */ + /* Note that @FT_Set_Pixel_Sizes and @FT_Set_Char_Size only */ + /* modify the contents of the current `active' size; you thus need */ + /* to use @FT_Activate_Size to change it. */ + /* */ + /* 99% of applications won't need the functions provided here, */ + /* especially if they use the caching sub-system, so be cautious */ + /* when using these. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Size */ + /* */ + /* <Description> */ + /* Create a new size object from a given face object. */ + /* */ + /* <Input> */ + /* face :: A handle to a parent face object. */ + /* */ + /* <Output> */ + /* asize :: A handle to a new size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You need to call @FT_Activate_Size in order to select the new size */ + /* for upcoming calls to @FT_Set_Pixel_Sizes, @FT_Set_Char_Size, */ + /* @FT_Load_Glyph, @FT_Load_Char, etc. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Size( FT_Face face, + FT_Size* size ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Size */ + /* */ + /* <Description> */ + /* Discard a given size object. Note that @FT_Done_Face */ + /* automatically discards all size objects allocated with */ + /* @FT_New_Size. */ + /* */ + /* <Input> */ + /* size :: A handle to a target size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Size( FT_Size size ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Activate_Size */ + /* */ + /* <Description> */ + /* Even though it is possible to create several size objects for a */ + /* given face (see @FT_New_Size for details), functions like */ + /* @FT_Load_Glyph or @FT_Load_Char only use the one that has been */ + /* activated last to determine the `current character pixel size'. */ + /* */ + /* This function can be used to `activate' a previously created size */ + /* object. */ + /* */ + /* <Input> */ + /* size :: A handle to a target size object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If `face' is the size's parent face object, this function changes */ + /* the value of `face->size' to the input size handle. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Activate_Size( FT_Size size ); + + /* */ + + +FT_END_HEADER + +#endif /* FTSIZES_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftsnames.h b/foreign/freetype2/freetype/ftsnames.h new file mode 100644 index 0000000..a7b51c2 --- /dev/null +++ b/foreign/freetype2/freetype/ftsnames.h @@ -0,0 +1,200 @@ +/***************************************************************************/ +/* */ +/* ftsnames.h */ +/* */ +/* Simple interface to access SFNT name tables (which are used */ +/* to hold font names, copyright info, notices, etc.) (specification). */ +/* */ +/* This is _not_ used to retrieve glyph names! */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSNAMES_H_ +#define FTSNAMES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* sfnt_names */ + /* */ + /* <Title> */ + /* SFNT Names */ + /* */ + /* <Abstract> */ + /* Access the names embedded in TrueType and OpenType files. */ + /* */ + /* <Description> */ + /* The TrueType and OpenType specifications allow the inclusion of */ + /* a special `names table' in font files. This table contains */ + /* textual (and internationalized) information regarding the font, */ + /* like family name, copyright, version, etc. */ + /* */ + /* The definitions below are used to access them if available. */ + /* */ + /* Note that this has nothing to do with glyph names! */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SfntName */ + /* */ + /* <Description> */ + /* A structure used to model an SFNT `name' table entry. */ + /* */ + /* <Fields> */ + /* platform_id :: The platform ID for `string'. */ + /* */ + /* encoding_id :: The encoding ID for `string'. */ + /* */ + /* language_id :: The language ID for `string'. */ + /* */ + /* name_id :: An identifier for `string'. */ + /* */ + /* string :: The `name' string. Note that its format differs */ + /* depending on the (platform,encoding) pair. It can */ + /* be a Pascal String, a UTF-16 one, etc. */ + /* */ + /* Generally speaking, the string is not */ + /* zero-terminated. Please refer to the TrueType */ + /* specification for details. */ + /* */ + /* string_len :: The length of `string' in bytes. */ + /* */ + /* <Note> */ + /* Possible values for `platform_id', `encoding_id', `language_id', */ + /* and `name_id' are given in the file `ttnameid.h'. For details */ + /* please refer to the TrueType or OpenType specification. */ + /* */ + /* See also @TT_PLATFORM_XXX, @TT_APPLE_ID_XXX, @TT_MAC_ID_XXX, */ + /* @TT_ISO_ID_XXX, and @TT_MS_ID_XXX. */ + /* */ + typedef struct FT_SfntName_ + { + FT_UShort platform_id; + FT_UShort encoding_id; + FT_UShort language_id; + FT_UShort name_id; + + FT_Byte* string; /* this string is *not* null-terminated! */ + FT_UInt string_len; /* in bytes */ + + } FT_SfntName; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Name_Count */ + /* */ + /* <Description> */ + /* Retrieve the number of name strings in the SFNT `name' table. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* <Return> */ + /* The number of strings in the `name' table. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Sfnt_Name_Count( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Name */ + /* */ + /* <Description> */ + /* Retrieve a string of the SFNT `name' table for a given index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face. */ + /* */ + /* idx :: The index of the `name' string. */ + /* */ + /* <Output> */ + /* aname :: The indexed @FT_SfntName structure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The `string' array returned in the `aname' structure is not */ + /* null-terminated. The application should deallocate it if it is no */ + /* longer in use. */ + /* */ + /* Use @FT_Get_Sfnt_Name_Count to get the total number of available */ + /* `name' table entries, then do a loop until you get the right */ + /* platform, encoding, and name ID. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Sfnt_Name( FT_Face face, + FT_UInt idx, + FT_SfntName *aname ); + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY + * + * @description: + * A constant used as the tag of @FT_Parameter structures to make + * FT_Open_Face() ignore preferred family subfamily names in `name' + * table since OpenType version 1.4. For backwards compatibility with + * legacy systems that have a 4-face-per-family restriction. + * + */ +#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY FT_MAKE_TAG( 'i', 'g', 'p', 'f' ) + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY + * + * @description: + * A constant used as the tag of @FT_Parameter structures to make + * FT_Open_Face() ignore preferred subfamily names in `name' table since + * OpenType version 1.4. For backwards compatibility with legacy + * systems that have a 4-face-per-family restriction. + * + */ +#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY FT_MAKE_TAG( 'i', 'g', 'p', 's' ) + + /* */ + + +FT_END_HEADER + +#endif /* FTSNAMES_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftstroke.h b/foreign/freetype2/freetype/ftstroke.h new file mode 100644 index 0000000..b3b9922 --- /dev/null +++ b/foreign/freetype2/freetype/ftstroke.h @@ -0,0 +1,785 @@ +/***************************************************************************/ +/* */ +/* ftstroke.h */ +/* */ +/* FreeType path stroker (specification). */ +/* */ +/* Copyright 2002-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSTROKE_H_ +#define FTSTROKE_H_ + +#include <ft2build.h> +#include FT_OUTLINE_H +#include FT_GLYPH_H + + +FT_BEGIN_HEADER + + + /************************************************************************ + * + * @section: + * glyph_stroker + * + * @title: + * Glyph Stroker + * + * @abstract: + * Generating bordered and stroked glyphs. + * + * @description: + * This component generates stroked outlines of a given vectorial + * glyph. It also allows you to retrieve the `outside' and/or the + * `inside' borders of the stroke. + * + * This can be useful to generate `bordered' glyph, i.e., glyphs + * displayed with a coloured (and anti-aliased) border around their + * shape. + * + * @order: + * FT_Stroker + * + * FT_Stroker_LineJoin + * FT_Stroker_LineCap + * FT_StrokerBorder + * + * FT_Outline_GetInsideBorder + * FT_Outline_GetOutsideBorder + * + * FT_Glyph_Stroke + * FT_Glyph_StrokeBorder + * + * FT_Stroker_New + * FT_Stroker_Set + * FT_Stroker_Rewind + * FT_Stroker_ParseOutline + * FT_Stroker_Done + * + * FT_Stroker_BeginSubPath + * FT_Stroker_EndSubPath + * + * FT_Stroker_LineTo + * FT_Stroker_ConicTo + * FT_Stroker_CubicTo + * + * FT_Stroker_GetBorderCounts + * FT_Stroker_ExportBorder + * FT_Stroker_GetCounts + * FT_Stroker_Export + * + */ + + + /************************************************************** + * + * @type: + * FT_Stroker + * + * @description: + * Opaque handle to a path stroker object. + */ + typedef struct FT_StrokerRec_* FT_Stroker; + + + /************************************************************** + * + * @enum: + * FT_Stroker_LineJoin + * + * @description: + * These values determine how two joining lines are rendered + * in a stroker. + * + * @values: + * FT_STROKER_LINEJOIN_ROUND :: + * Used to render rounded line joins. Circular arcs are used + * to join two lines smoothly. + * + * FT_STROKER_LINEJOIN_BEVEL :: + * Used to render beveled line joins. The outer corner of + * the joined lines is filled by enclosing the triangular + * region of the corner with a straight line between the + * outer corners of each stroke. + * + * FT_STROKER_LINEJOIN_MITER_FIXED :: + * Used to render mitered line joins, with fixed bevels if the + * miter limit is exceeded. The outer edges of the strokes + * for the two segments are extended until they meet at an + * angle. If the segments meet at too sharp an angle (such + * that the miter would extend from the intersection of the + * segments a distance greater than the product of the miter + * limit value and the border radius), then a bevel join (see + * above) is used instead. This prevents long spikes being + * created. FT_STROKER_LINEJOIN_MITER_FIXED generates a miter + * line join as used in PostScript and PDF. + * + * FT_STROKER_LINEJOIN_MITER_VARIABLE :: + * FT_STROKER_LINEJOIN_MITER :: + * Used to render mitered line joins, with variable bevels if + * the miter limit is exceeded. The intersection of the + * strokes is clipped at a line perpendicular to the bisector + * of the angle between the strokes, at the distance from the + * intersection of the segments equal to the product of the + * miter limit value and the border radius. This prevents + * long spikes being created. + * FT_STROKER_LINEJOIN_MITER_VARIABLE generates a mitered line + * join as used in XPS. FT_STROKER_LINEJOIN_MITER is an alias + * for FT_STROKER_LINEJOIN_MITER_VARIABLE, retained for + * backwards compatibility. + */ + typedef enum FT_Stroker_LineJoin_ + { + FT_STROKER_LINEJOIN_ROUND = 0, + FT_STROKER_LINEJOIN_BEVEL = 1, + FT_STROKER_LINEJOIN_MITER_VARIABLE = 2, + FT_STROKER_LINEJOIN_MITER = FT_STROKER_LINEJOIN_MITER_VARIABLE, + FT_STROKER_LINEJOIN_MITER_FIXED = 3 + + } FT_Stroker_LineJoin; + + + /************************************************************** + * + * @enum: + * FT_Stroker_LineCap + * + * @description: + * These values determine how the end of opened sub-paths are + * rendered in a stroke. + * + * @values: + * FT_STROKER_LINECAP_BUTT :: + * The end of lines is rendered as a full stop on the last + * point itself. + * + * FT_STROKER_LINECAP_ROUND :: + * The end of lines is rendered as a half-circle around the + * last point. + * + * FT_STROKER_LINECAP_SQUARE :: + * The end of lines is rendered as a square around the + * last point. + */ + typedef enum FT_Stroker_LineCap_ + { + FT_STROKER_LINECAP_BUTT = 0, + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINECAP_SQUARE + + } FT_Stroker_LineCap; + + + /************************************************************** + * + * @enum: + * FT_StrokerBorder + * + * @description: + * These values are used to select a given stroke border + * in @FT_Stroker_GetBorderCounts and @FT_Stroker_ExportBorder. + * + * @values: + * FT_STROKER_BORDER_LEFT :: + * Select the left border, relative to the drawing direction. + * + * FT_STROKER_BORDER_RIGHT :: + * Select the right border, relative to the drawing direction. + * + * @note: + * Applications are generally interested in the `inside' and `outside' + * borders. However, there is no direct mapping between these and the + * `left' and `right' ones, since this really depends on the glyph's + * drawing orientation, which varies between font formats. + * + * You can however use @FT_Outline_GetInsideBorder and + * @FT_Outline_GetOutsideBorder to get these. + */ + typedef enum FT_StrokerBorder_ + { + FT_STROKER_BORDER_LEFT = 0, + FT_STROKER_BORDER_RIGHT + + } FT_StrokerBorder; + + + /************************************************************** + * + * @function: + * FT_Outline_GetInsideBorder + * + * @description: + * Retrieve the @FT_StrokerBorder value corresponding to the + * `inside' borders of a given outline. + * + * @input: + * outline :: + * The source outline handle. + * + * @return: + * The border index. @FT_STROKER_BORDER_RIGHT for empty or invalid + * outlines. + */ + FT_EXPORT( FT_StrokerBorder ) + FT_Outline_GetInsideBorder( FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Outline_GetOutsideBorder + * + * @description: + * Retrieve the @FT_StrokerBorder value corresponding to the + * `outside' borders of a given outline. + * + * @input: + * outline :: + * The source outline handle. + * + * @return: + * The border index. @FT_STROKER_BORDER_LEFT for empty or invalid + * outlines. + */ + FT_EXPORT( FT_StrokerBorder ) + FT_Outline_GetOutsideBorder( FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Stroker_New + * + * @description: + * Create a new stroker object. + * + * @input: + * library :: + * FreeType library handle. + * + * @output: + * astroker :: + * A new stroker object handle. NULL in case of error. + * + * @return: + * FreeType error code. 0~means success. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_New( FT_Library library, + FT_Stroker *astroker ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Set + * + * @description: + * Reset a stroker object's attributes. + * + * @input: + * stroker :: + * The target stroker handle. + * + * radius :: + * The border radius. + * + * line_cap :: + * The line cap style. + * + * line_join :: + * The line join style. + * + * miter_limit :: + * The miter limit for the FT_STROKER_LINEJOIN_MITER_FIXED and + * FT_STROKER_LINEJOIN_MITER_VARIABLE line join styles, + * expressed as 16.16 fixed-point value. + * + * @note: + * The radius is expressed in the same units as the outline + * coordinates. + * + * This function calls @FT_Stroker_Rewind automatically. + */ + FT_EXPORT( void ) + FT_Stroker_Set( FT_Stroker stroker, + FT_Fixed radius, + FT_Stroker_LineCap line_cap, + FT_Stroker_LineJoin line_join, + FT_Fixed miter_limit ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Rewind + * + * @description: + * Reset a stroker object without changing its attributes. + * You should call this function before beginning a new + * series of calls to @FT_Stroker_BeginSubPath or + * @FT_Stroker_EndSubPath. + * + * @input: + * stroker :: + * The target stroker handle. + */ + FT_EXPORT( void ) + FT_Stroker_Rewind( FT_Stroker stroker ); + + + /************************************************************** + * + * @function: + * FT_Stroker_ParseOutline + * + * @description: + * A convenience function used to parse a whole outline with + * the stroker. The resulting outline(s) can be retrieved + * later by functions like @FT_Stroker_GetCounts and @FT_Stroker_Export. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The source outline. + * + * opened :: + * A boolean. If~1, the outline is treated as an open path instead + * of a closed one. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `opened' is~0 (the default), the outline is treated as a closed + * path, and the stroker generates two distinct `border' outlines. + * + * If `opened' is~1, the outline is processed as an open path, and the + * stroker generates a single `stroke' outline. + * + * This function calls @FT_Stroker_Rewind automatically. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_ParseOutline( FT_Stroker stroker, + FT_Outline* outline, + FT_Bool opened ); + + + /************************************************************** + * + * @function: + * FT_Stroker_BeginSubPath + * + * @description: + * Start a new sub-path in the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * to :: + * A pointer to the start vector. + * + * open :: + * A boolean. If~1, the sub-path is treated as an open one. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function is useful when you need to stroke a path that is + * not stored as an @FT_Outline object. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_BeginSubPath( FT_Stroker stroker, + FT_Vector* to, + FT_Bool open ); + + + /************************************************************** + * + * @function: + * FT_Stroker_EndSubPath + * + * @description: + * Close the current sub-path in the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function after @FT_Stroker_BeginSubPath. + * If the subpath was not `opened', this function `draws' a + * single line segment to the start position when needed. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_EndSubPath( FT_Stroker stroker ); + + + /************************************************************** + * + * @function: + * FT_Stroker_LineTo + * + * @description: + * `Draw' a single line segment in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_LineTo( FT_Stroker stroker, + FT_Vector* to ); + + + /************************************************************** + * + * @function: + * FT_Stroker_ConicTo + * + * @description: + * `Draw' a single quadratic Bézier in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * control :: + * A pointer to a Bézier control point. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_ConicTo( FT_Stroker stroker, + FT_Vector* control, + FT_Vector* to ); + + + /************************************************************** + * + * @function: + * FT_Stroker_CubicTo + * + * @description: + * `Draw' a single cubic Bézier in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * control1 :: + * A pointer to the first Bézier control point. + * + * control2 :: + * A pointer to second Bézier control point. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_CubicTo( FT_Stroker stroker, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ); + + + /************************************************************** + * + * @function: + * FT_Stroker_GetBorderCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It returns the number of points and + * contours necessary to export one of the `border' or `stroke' + * outlines generated by the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * border :: + * The border index. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * When an outline, or a sub-path, is `closed', the stroker generates + * two independent `border' outlines, named `left' and `right'. + * + * When the outline, or a sub-path, is `opened', the stroker merges + * the `border' outlines with caps. The `left' border receives all + * points, while the `right' border becomes empty. + * + * Use the function @FT_Stroker_GetCounts instead if you want to + * retrieve the counts associated to both borders. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_GetBorderCounts( FT_Stroker stroker, + FT_StrokerBorder border, + FT_UInt *anum_points, + FT_UInt *anum_contours ); + + + /************************************************************** + * + * @function: + * FT_Stroker_ExportBorder + * + * @description: + * Call this function after @FT_Stroker_GetBorderCounts to + * export the corresponding border to your own @FT_Outline + * structure. + * + * Note that this function appends the border points and + * contours to your outline, but does not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * border :: + * The border index. + * + * outline :: + * The target outline handle. + * + * @note: + * Always call this function after @FT_Stroker_GetBorderCounts to + * get sure that there is enough room in your @FT_Outline object to + * receive all new data. + * + * When an outline, or a sub-path, is `closed', the stroker generates + * two independent `border' outlines, named `left' and `right'. + * + * When the outline, or a sub-path, is `opened', the stroker merges + * the `border' outlines with caps. The `left' border receives all + * points, while the `right' border becomes empty. + * + * Use the function @FT_Stroker_Export instead if you want to + * retrieve all borders at once. + */ + FT_EXPORT( void ) + FT_Stroker_ExportBorder( FT_Stroker stroker, + FT_StrokerBorder border, + FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Stroker_GetCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It returns the number of points and + * contours necessary to export all points/borders from the stroked + * outline/path. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0~means success. + */ + FT_EXPORT( FT_Error ) + FT_Stroker_GetCounts( FT_Stroker stroker, + FT_UInt *anum_points, + FT_UInt *anum_contours ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Export + * + * @description: + * Call this function after @FT_Stroker_GetBorderCounts to + * export all borders to your own @FT_Outline structure. + * + * Note that this function appends the border points and + * contours to your outline, but does not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The target outline handle. + */ + FT_EXPORT( void ) + FT_Stroker_Export( FT_Stroker stroker, + FT_Outline* outline ); + + + /************************************************************** + * + * @function: + * FT_Stroker_Done + * + * @description: + * Destroy a stroker object. + * + * @input: + * stroker :: + * A stroker handle. Can be NULL. + */ + FT_EXPORT( void ) + FT_Stroker_Done( FT_Stroker stroker ); + + + /************************************************************** + * + * @function: + * FT_Glyph_Stroke + * + * @description: + * Stroke a given outline glyph object with a given stroker. + * + * @inout: + * pglyph :: + * Source glyph handle on input, new glyph handle on output. + * + * @input: + * stroker :: + * A stroker handle. + * + * destroy :: + * A Boolean. If~1, the source glyph object is destroyed + * on success. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source glyph is untouched in case of error. + * + * Adding stroke may yield a significantly wider and taller glyph + * depending on how large of a radius was used to stroke the glyph. You + * may need to manually adjust horizontal and vertical advance amounts + * to account for this added size. + */ + FT_EXPORT( FT_Error ) + FT_Glyph_Stroke( FT_Glyph *pglyph, + FT_Stroker stroker, + FT_Bool destroy ); + + + /************************************************************** + * + * @function: + * FT_Glyph_StrokeBorder + * + * @description: + * Stroke a given outline glyph object with a given stroker, but + * only return either its inside or outside border. + * + * @inout: + * pglyph :: + * Source glyph handle on input, new glyph handle on output. + * + * @input: + * stroker :: + * A stroker handle. + * + * inside :: + * A Boolean. If~1, return the inside border, otherwise + * the outside border. + * + * destroy :: + * A Boolean. If~1, the source glyph object is destroyed + * on success. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The source glyph is untouched in case of error. + * + * Adding stroke may yield a significantly wider and taller glyph + * depending on how large of a radius was used to stroke the glyph. You + * may need to manually adjust horizontal and vertical advance amounts + * to account for this added size. + */ + FT_EXPORT( FT_Error ) + FT_Glyph_StrokeBorder( FT_Glyph *pglyph, + FT_Stroker stroker, + FT_Bool inside, + FT_Bool destroy ); + + /* */ + +FT_END_HEADER + +#endif /* FTSTROKE_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/foreign/freetype2/freetype/ftsynth.h b/foreign/freetype2/freetype/ftsynth.h new file mode 100644 index 0000000..fdfcb69 --- /dev/null +++ b/foreign/freetype2/freetype/ftsynth.h @@ -0,0 +1,84 @@ +/***************************************************************************/ +/* */ +/* ftsynth.h */ +/* */ +/* FreeType synthesizing code for emboldening and slanting */ +/* (specification). */ +/* */ +/* Copyright 2000-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /********* *********/ + /********* WARNING, THIS IS ALPHA CODE! THIS API *********/ + /********* IS DUE TO CHANGE UNTIL STRICTLY NOTIFIED BY THE *********/ + /********* FREETYPE DEVELOPMENT TEAM *********/ + /********* *********/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /* Main reason for not lifting the functions in this module to a */ + /* `standard' API is that the used parameters for emboldening and */ + /* slanting are not configurable. Consider the functions as a */ + /* code resource that should be copied into the application and */ + /* adapted to the particular needs. */ + + +#ifndef FTSYNTH_H_ +#define FTSYNTH_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /* Embolden a glyph by a `reasonable' value (which is highly a matter of */ + /* taste). This function is actually a convenience function, providing */ + /* a wrapper for @FT_Outline_Embolden and @FT_Bitmap_Embolden. */ + /* */ + /* For emboldened outlines the height, width, and advance metrics are */ + /* increased by the strength of the emboldening -- this even affects */ + /* mono-width fonts! */ + /* */ + /* You can also call @FT_Outline_Get_CBox to get precise values. */ + FT_EXPORT( void ) + FT_GlyphSlot_Embolden( FT_GlyphSlot slot ); + + /* Slant an outline glyph to the right by about 12 degrees. */ + FT_EXPORT( void ) + FT_GlyphSlot_Oblique( FT_GlyphSlot slot ); + + /* */ + + +FT_END_HEADER + +#endif /* FTSYNTH_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftsystem.h b/foreign/freetype2/freetype/ftsystem.h new file mode 100644 index 0000000..908ae07 --- /dev/null +++ b/foreign/freetype2/freetype/ftsystem.h @@ -0,0 +1,355 @@ +/***************************************************************************/ +/* */ +/* ftsystem.h */ +/* */ +/* FreeType low-level system interface definition (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTSYSTEM_H_ +#define FTSYSTEM_H_ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* system_interface */ + /* */ + /* <Title> */ + /* System Interface */ + /* */ + /* <Abstract> */ + /* How FreeType manages memory and i/o. */ + /* */ + /* <Description> */ + /* This section contains various definitions related to memory */ + /* management and i/o access. You need to understand this */ + /* information if you want to use a custom memory manager or you own */ + /* i/o streams. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* M E M O R Y M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Memory + * + * @description: + * A handle to a given memory manager object, defined with an + * @FT_MemoryRec structure. + * + */ + typedef struct FT_MemoryRec_* FT_Memory; + + + /************************************************************************* + * + * @functype: + * FT_Alloc_Func + * + * @description: + * A function used to allocate `size' bytes from `memory'. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * size :: + * The size in bytes to allocate. + * + * @return: + * Address of new memory block. 0~in case of failure. + * + */ + typedef void* + (*FT_Alloc_Func)( FT_Memory memory, + long size ); + + + /************************************************************************* + * + * @functype: + * FT_Free_Func + * + * @description: + * A function used to release a given block of memory. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * block :: + * The address of the target memory block. + * + */ + typedef void + (*FT_Free_Func)( FT_Memory memory, + void* block ); + + + /************************************************************************* + * + * @functype: + * FT_Realloc_Func + * + * @description: + * A function used to re-allocate a given block of memory. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * cur_size :: + * The block's current size in bytes. + * + * new_size :: + * The block's requested new size. + * + * block :: + * The block's current address. + * + * @return: + * New block address. 0~in case of memory shortage. + * + * @note: + * In case of error, the old block must still be available. + * + */ + typedef void* + (*FT_Realloc_Func)( FT_Memory memory, + long cur_size, + long new_size, + void* block ); + + + /************************************************************************* + * + * @struct: + * FT_MemoryRec + * + * @description: + * A structure used to describe a given memory manager to FreeType~2. + * + * @fields: + * user :: + * A generic typeless pointer for user data. + * + * alloc :: + * A pointer type to an allocation function. + * + * free :: + * A pointer type to an memory freeing function. + * + * realloc :: + * A pointer type to a reallocation function. + * + */ + struct FT_MemoryRec_ + { + void* user; + FT_Alloc_Func alloc; + FT_Free_Func free; + FT_Realloc_Func realloc; + }; + + + /*************************************************************************/ + /* */ + /* I / O M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Stream + * + * @description: + * A handle to an input stream. + * + * @also: + * See @FT_StreamRec for the publicly accessible fields of a given + * stream object. + * + */ + typedef struct FT_StreamRec_* FT_Stream; + + + /************************************************************************* + * + * @struct: + * FT_StreamDesc + * + * @description: + * A union type used to store either a long or a pointer. This is used + * to store a file descriptor or a `FILE*' in an input stream. + * + */ + typedef union FT_StreamDesc_ + { + long value; + void* pointer; + + } FT_StreamDesc; + + + /************************************************************************* + * + * @functype: + * FT_Stream_IoFunc + * + * @description: + * A function used to seek and read data from a given input stream. + * + * @input: + * stream :: + * A handle to the source stream. + * + * offset :: + * The offset of read in stream (always from start). + * + * buffer :: + * The address of the read buffer. + * + * count :: + * The number of bytes to read from the stream. + * + * @return: + * The number of bytes effectively read by the stream. + * + * @note: + * This function might be called to perform a seek or skip operation + * with a `count' of~0. A non-zero return value then indicates an + * error. + * + */ + typedef unsigned long + (*FT_Stream_IoFunc)( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ); + + + /************************************************************************* + * + * @functype: + * FT_Stream_CloseFunc + * + * @description: + * A function used to close a given input stream. + * + * @input: + * stream :: + * A handle to the target stream. + * + */ + typedef void + (*FT_Stream_CloseFunc)( FT_Stream stream ); + + + /************************************************************************* + * + * @struct: + * FT_StreamRec + * + * @description: + * A structure used to describe an input stream. + * + * @input: + * base :: + * For memory-based streams, this is the address of the first stream + * byte in memory. This field should always be set to NULL for + * disk-based streams. + * + * size :: + * The stream size in bytes. + * + * In case of compressed streams where the size is unknown before + * actually doing the decompression, the value is set to 0x7FFFFFFF. + * (Note that this size value can occur for normal streams also; it is + * thus just a hint.) + * + * pos :: + * The current position within the stream. + * + * descriptor :: + * This field is a union that can hold an integer or a pointer. It is + * used by stream implementations to store file descriptors or `FILE*' + * pointers. + * + * pathname :: + * This field is completely ignored by FreeType. However, it is often + * useful during debugging to use it to store the stream's filename + * (where available). + * + * read :: + * The stream's input function. + * + * close :: + * The stream's close function. + * + * memory :: + * The memory manager to use to preload frames. This is set + * internally by FreeType and shouldn't be touched by stream + * implementations. + * + * cursor :: + * This field is set and used internally by FreeType when parsing + * frames. + * + * limit :: + * This field is set and used internally by FreeType when parsing + * frames. + * + */ + typedef struct FT_StreamRec_ + { + unsigned char* base; + unsigned long size; + unsigned long pos; + + FT_StreamDesc descriptor; + FT_StreamDesc pathname; + FT_Stream_IoFunc read; + FT_Stream_CloseFunc close; + + FT_Memory memory; + unsigned char* cursor; + unsigned char* limit; + + } FT_StreamRec; + + /* */ + + +FT_END_HEADER + +#endif /* FTSYSTEM_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/fttrigon.h b/foreign/freetype2/freetype/fttrigon.h new file mode 100644 index 0000000..f789b52 --- /dev/null +++ b/foreign/freetype2/freetype/fttrigon.h @@ -0,0 +1,350 @@ +/***************************************************************************/ +/* */ +/* fttrigon.h */ +/* */ +/* FreeType trigonometric functions (specification). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTTRIGON_H_ +#define FTTRIGON_H_ + +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Angle + * + * @description: + * This type is used to model angle values in FreeType. Note that the + * angle is a 16.16 fixed-point value expressed in degrees. + * + */ + typedef FT_Fixed FT_Angle; + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_PI + * + * @description: + * The angle pi expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_PI ( 180L << 16 ) + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_2PI + * + * @description: + * The angle 2*pi expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_2PI ( FT_ANGLE_PI * 2 ) + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_PI2 + * + * @description: + * The angle pi/2 expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_PI2 ( FT_ANGLE_PI / 2 ) + + + /************************************************************************* + * + * @macro: + * FT_ANGLE_PI4 + * + * @description: + * The angle pi/4 expressed in @FT_Angle units. + * + */ +#define FT_ANGLE_PI4 ( FT_ANGLE_PI / 4 ) + + + /************************************************************************* + * + * @function: + * FT_Sin + * + * @description: + * Return the sinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The sinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @FT_Vector_Unit. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Sin( FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Cos + * + * @description: + * Return the cosinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The cosinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @FT_Vector_Unit. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Cos( FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Tan + * + * @description: + * Return the tangent of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The tangent value. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Tan( FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Atan2 + * + * @description: + * Return the arc-tangent corresponding to a given vector (x,y) in + * the 2d plane. + * + * @input: + * x :: + * The horizontal vector coordinate. + * + * y :: + * The vertical vector coordinate. + * + * @return: + * The arc-tangent value (i.e. angle). + * + */ + FT_EXPORT( FT_Angle ) + FT_Atan2( FT_Fixed x, + FT_Fixed y ); + + + /************************************************************************* + * + * @function: + * FT_Angle_Diff + * + * @description: + * Return the difference between two angles. The result is always + * constrained to the ]-PI..PI] interval. + * + * @input: + * angle1 :: + * First angle. + * + * angle2 :: + * Second angle. + * + * @return: + * Constrained value of `value2-value1'. + * + */ + FT_EXPORT( FT_Angle ) + FT_Angle_Diff( FT_Angle angle1, + FT_Angle angle2 ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Unit + * + * @description: + * Return the unit vector corresponding to a given angle. After the + * call, the value of `vec.x' will be `cos(angle)', and the value of + * `vec.y' will be `sin(angle)'. + * + * This function is useful to retrieve both the sinus and cosinus of a + * given angle quickly. + * + * @output: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ + FT_EXPORT( void ) + FT_Vector_Unit( FT_Vector* vec, + FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Rotate + * + * @description: + * Rotate a vector by a given angle. + * + * @inout: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ + FT_EXPORT( void ) + FT_Vector_Rotate( FT_Vector* vec, + FT_Angle angle ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Length + * + * @description: + * Return the length of a given vector. + * + * @input: + * vec :: + * The address of target vector. + * + * @return: + * The vector length, expressed in the same units that the original + * vector coordinates. + * + */ + FT_EXPORT( FT_Fixed ) + FT_Vector_Length( FT_Vector* vec ); + + + /************************************************************************* + * + * @function: + * FT_Vector_Polarize + * + * @description: + * Compute both the length and angle of a given vector. + * + * @input: + * vec :: + * The address of source vector. + * + * @output: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ + FT_EXPORT( void ) + FT_Vector_Polarize( FT_Vector* vec, + FT_Fixed *length, + FT_Angle *angle ); + + + /************************************************************************* + * + * @function: + * FT_Vector_From_Polar + * + * @description: + * Compute vector coordinates from a length and angle. + * + * @output: + * vec :: + * The address of source vector. + * + * @input: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ + FT_EXPORT( void ) + FT_Vector_From_Polar( FT_Vector* vec, + FT_Fixed length, + FT_Angle angle ); + + /* */ + + +FT_END_HEADER + +#endif /* FTTRIGON_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftttdrv.h b/foreign/freetype2/freetype/ftttdrv.h new file mode 100644 index 0000000..6c02e65 --- /dev/null +++ b/foreign/freetype2/freetype/ftttdrv.h @@ -0,0 +1,310 @@ +/***************************************************************************/ +/* */ +/* ftttdrv.h */ +/* */ +/* FreeType API for controlling the TrueType driver */ +/* (specification only). */ +/* */ +/* Copyright 2013-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTTTDRV_H_ +#define FTTTDRV_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /************************************************************************** + * + * @section: + * tt_driver + * + * @title: + * The TrueType driver + * + * @abstract: + * Controlling the TrueType driver module. + * + * @description: + * While FreeType's TrueType driver doesn't expose API functions by + * itself, it is possible to control its behaviour with @FT_Property_Set + * and @FT_Property_Get. The following lists the available properties + * together with the necessary macros and structures. + * + * The TrueType driver's module name is `truetype'. + * + * We start with a list of definitions, kindly provided by Greg + * Hitchcock. + * + * _Bi-Level_ _Rendering_ + * + * Monochromatic rendering, exclusively used in the early days of + * TrueType by both Apple and Microsoft. Microsoft's GDI interface + * supported hinting of the right-side bearing point, such that the + * advance width could be non-linear. Most often this was done to + * achieve some level of glyph symmetry. To enable reasonable + * performance (e.g., not having to run hinting on all glyphs just to + * get the widths) there was a bit in the head table indicating if the + * side bearing was hinted, and additional tables, `hdmx' and `LTSH', to + * cache hinting widths across multiple sizes and device aspect ratios. + * + * _Font_ _Smoothing_ + * + * Microsoft's GDI implementation of anti-aliasing. Not traditional + * anti-aliasing as the outlines were hinted before the sampling. The + * widths matched the bi-level rendering. + * + * _ClearType_ _Rendering_ + * + * Technique that uses physical subpixels to improve rendering on LCD + * (and other) displays. Because of the higher resolution, many methods + * of improving symmetry in glyphs through hinting the right-side + * bearing were no longer necessary. This lead to what GDI calls + * `natural widths' ClearType, see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec21. Since hinting + * has extra resolution, most non-linearity went away, but it is still + * possible for hints to change the advance widths in this mode. + * + * _ClearType_ _Compatible_ _Widths_ + * + * One of the earliest challenges with ClearType was allowing the + * implementation in GDI to be selected without requiring all UI and + * documents to reflow. To address this, a compatible method of + * rendering ClearType was added where the font hints are executed once + * to determine the width in bi-level rendering, and then re-run in + * ClearType, with the difference in widths being absorbed in the font + * hints for ClearType (mostly in the white space of hints); see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec20. Somewhat by + * definition, compatible width ClearType allows for non-linear widths, + * but only when the bi-level version has non-linear widths. + * + * _ClearType_ _Subpixel_ _Positioning_ + * + * One of the nice benefits of ClearType is the ability to more crisply + * display fractional widths; unfortunately, the GDI model of integer + * bitmaps did not support this. However, the WPF and Direct Write + * frameworks do support fractional widths. DWrite calls this `natural + * mode', not to be confused with GDI's `natural widths'. Subpixel + * positioning, in the current implementation of Direct Write, + * unfortunately does not support hinted advance widths, see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec22. Note that the + * TrueType interpreter fully allows the advance width to be adjusted in + * this mode, just the DWrite client will ignore those changes. + * + * _ClearType_ _Backwards_ _Compatibility_ + * + * This is a set of exceptions made in the TrueType interpreter to + * minimize hinting techniques that were problematic with the extra + * resolution of ClearType; see + * http://www.beatstamm.com/typography/RTRCh4.htm#Sec1 and + * http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx. + * This technique is not to be confused with ClearType compatible + * widths. ClearType backwards compatibility has no direct impact on + * changing advance widths, but there might be an indirect impact on + * disabling some deltas. This could be worked around in backwards + * compatibility mode. + * + * _Native_ _ClearType_ _Mode_ + * + * (Not to be confused with `natural widths'.) This mode removes all + * the exceptions in the TrueType interpreter when running with + * ClearType. Any issues on widths would still apply, though. + * + */ + + + /************************************************************************** + * + * @property: + * interpreter-version + * + * @description: + * Currently, two versions are available, representing the bytecode + * interpreter with and without subpixel hinting support, + * respectively. The default is subpixel support if + * TT_CONFIG_OPTION_SUBPIXEL_HINTING is defined, and no subpixel + * support otherwise (since it isn't available then). + * + * If subpixel hinting is on, many TrueType bytecode instructions behave + * differently compared to B/W or grayscale rendering (except if `native + * ClearType' is selected by the font). The main idea is to render at a + * much increased horizontal resolution, then sampling down the created + * output to subpixel precision. However, many older fonts are not + * suited to this and must be specially taken care of by applying + * (hardcoded) font-specific tweaks. + * + * Details on subpixel hinting and some of the necessary tweaks can be + * found in Greg Hitchcock's whitepaper at + * `http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx'. + * + * The following example code demonstrates how to activate subpixel + * hinting (omitting the error handling). + * + * { + * FT_Library library; + * FT_Face face; + * FT_UInt interpreter_version = TT_INTERPRETER_VERSION_38; + * + * + * FT_Init_FreeType( &library ); + * + * FT_Property_Set( library, "truetype", + * "interpreter-version", + * &interpreter_version ); + * } + * + * @note: + * This property can be used with @FT_Property_Get also. + * + */ + + + /************************************************************************** + * + * @enum: + * TT_INTERPRETER_VERSION_XXX + * + * @description: + * A list of constants used for the @interpreter-version property to + * select the hinting engine for Truetype fonts. + * + * The numeric value in the constant names represents the version + * number as returned by the `GETINFO' bytecode instruction. + * + * @values: + * TT_INTERPRETER_VERSION_35 :: + * Version~35 corresponds to MS rasterizer v.1.7 as used e.g. in + * Windows~98; only grayscale and B/W rasterizing is supported. + * + * TT_INTERPRETER_VERSION_38 :: + * Version~38 corresponds to MS rasterizer v.1.9; it is roughly + * equivalent to the hinting provided by DirectWrite ClearType (as + * can be found, for example, in the Internet Explorer~9 running on + * Windows~7). + * + * @note: + * This property controls the behaviour of the bytecode interpreter + * and thus how outlines get hinted. It does *not* control how glyph + * get rasterized! In particular, it does not control subpixel color + * filtering. + * + * If FreeType has not been compiled with configuration option + * FT_CONFIG_OPTION_SUBPIXEL_HINTING, selecting version~38 causes an + * `FT_Err_Unimplemented_Feature' error. + * + * Depending on the graphics framework, Microsoft uses different + * bytecode and rendering engines. As a consequence, the version + * numbers returned by a call to the `GETINFO' bytecode instruction are + * more convoluted than desired. + * + * Here are two tables that try to shed some light on the possible + * values for the MS rasterizer engine, together with the additional + * features introduced by it. + * + * { + * GETINFO framework version feature + * ------------------------------------------------------------------- + * 3 GDI (Win 3.1), v1.0 16-bit, first version + * TrueImage + * 33 GDI (Win NT 3.1), v1.5 32-bit + * HP Laserjet + * 34 GDI (Win 95) v1.6 font smoothing, + * new SCANTYPE opcode + * 35 GDI (Win 98/2000) v1.7 (UN)SCALED_COMPONENT_OFFSET + * bits in composite glyphs + * 36 MGDI (Win CE 2) v1.6+ classic ClearType + * 37 GDI (XP and later), v1.8 ClearType + * GDI+ old (before Vista) + * 38 GDI+ old (Vista, Win 7), v1.9 subpixel ClearType, + * WPF Y-direction ClearType, + * additional error checking + * 39 DWrite (before Win 8) v2.0 subpixel ClearType flags + * in GETINFO opcode, + * bug fixes + * 40 GDI+ (after Win 7), v2.1 Y-direction ClearType flag + * DWrite (Win 8) in GETINFO opcode, + * Gray ClearType + * } + * + * The `version' field gives a rough orientation only, since some + * applications provided certain features much earlier (as an example, + * Microsoft Reader used subpixel and Y-direction ClearType already in + * Windows 2000). Similarly, updates to a given framework might include + * improved hinting support. + * + * { + * version sampling rendering comment + * x y x y + * -------------------------------------------------------------- + * v1.0 normal normal B/W B/W bi-level + * v1.6 high high gray gray grayscale + * v1.8 high normal color-filter B/W (GDI) ClearType + * v1.9 high high color-filter gray Color ClearType + * v2.1 high normal gray B/W Gray ClearType + * v2.1 high high gray gray Gray ClearType + * } + * + * Color and Gray ClearType are the two available variants of + * `Y-direction ClearType', meaning grayscale rasterization along the + * Y-direction; the name used in the TrueType specification for this + * feature is `symmetric smoothing'. `Classic ClearType' is the + * original algorithm used before introducing a modified version in + * Win~XP. Another name for v1.6's grayscale rendering is `font + * smoothing', and `Color ClearType' is sometimes also called `DWrite + * ClearType'. To differentiate between today's Color ClearType and the + * earlier ClearType variant with B/W rendering along the vertical axis, + * the latter is sometimes called `GDI ClearType'. + * + * `Normal' and `high' sampling describe the (virtual) resolution to + * access the rasterized outline after the hinting process. `Normal' + * means 1 sample per grid line (i.e., B/W). In the current Microsoft + * implementation, `high' means an extra virtual resolution of 16x16 (or + * 16x1) grid lines per pixel for bytecode instructions like `MIRP'. + * After hinting, these 16 grid lines are mapped to 6x5 (or 6x1) grid + * lines for color filtering if Color ClearType is activated. + * + * Note that `Gray ClearType' is essentially the same as v1.6's + * grayscale rendering. However, the GETINFO instruction handles it + * differently: v1.6 returns bit~12 (hinting for grayscale), while v2.1 + * returns bits~13 (hinting for ClearType), 18 (symmetrical smoothing), + * and~19 (Gray ClearType). Also, this mode respects bits 2 and~3 for + * the version~1 gasp table exclusively (like Color ClearType), while + * v1.6 only respects the values of version~0 (bits 0 and~1). + * + * FreeType doesn't provide all capabilities of the most recent + * ClearType incarnation, thus we identify our subpixel support as + * version~38. + * + */ +#define TT_INTERPRETER_VERSION_35 35 +#define TT_INTERPRETER_VERSION_38 38 + + /* */ + + +FT_END_HEADER + + +#endif /* FTTTDRV_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/fttypes.h b/foreign/freetype2/freetype/fttypes.h new file mode 100644 index 0000000..2673e79 --- /dev/null +++ b/foreign/freetype2/freetype/fttypes.h @@ -0,0 +1,602 @@ +/***************************************************************************/ +/* */ +/* fttypes.h */ +/* */ +/* FreeType simple types definitions (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTTYPES_H_ +#define FTTYPES_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_SYSTEM_H +#include FT_IMAGE_H + +#include <stddef.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /* <Title> */ + /* Basic Data Types */ + /* */ + /* <Abstract> */ + /* The basic data types defined by the library. */ + /* */ + /* <Description> */ + /* This section contains the basic data types defined by FreeType~2, */ + /* ranging from simple scalar types to bitmap descriptors. More */ + /* font-specific structures are defined in a different section. */ + /* */ + /* <Order> */ + /* FT_Byte */ + /* FT_Bytes */ + /* FT_Char */ + /* FT_Int */ + /* FT_UInt */ + /* FT_Int16 */ + /* FT_UInt16 */ + /* FT_Int32 */ + /* FT_UInt32 */ + /* FT_Int64 */ + /* FT_UInt64 */ + /* FT_Short */ + /* FT_UShort */ + /* FT_Long */ + /* FT_ULong */ + /* FT_Bool */ + /* FT_Offset */ + /* FT_PtrDist */ + /* FT_String */ + /* FT_Tag */ + /* FT_Error */ + /* FT_Fixed */ + /* FT_Pointer */ + /* FT_Pos */ + /* FT_Vector */ + /* FT_BBox */ + /* FT_Matrix */ + /* FT_FWord */ + /* FT_UFWord */ + /* FT_F2Dot14 */ + /* FT_UnitVector */ + /* FT_F26Dot6 */ + /* FT_Data */ + /* */ + /* FT_MAKE_TAG */ + /* */ + /* FT_Generic */ + /* FT_Generic_Finalizer */ + /* */ + /* FT_Bitmap */ + /* FT_Pixel_Mode */ + /* FT_Palette_Mode */ + /* FT_Glyph_Format */ + /* FT_IMAGE_TAG */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Bool */ + /* */ + /* <Description> */ + /* A typedef of unsigned char, used for simple booleans. As usual, */ + /* values 1 and~0 represent true and false, respectively. */ + /* */ + typedef unsigned char FT_Bool; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_FWord */ + /* */ + /* <Description> */ + /* A signed 16-bit integer used to store a distance in original font */ + /* units. */ + /* */ + typedef signed short FT_FWord; /* distance in FUnits */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UFWord */ + /* */ + /* <Description> */ + /* An unsigned 16-bit integer used to store a distance in original */ + /* font units. */ + /* */ + typedef unsigned short FT_UFWord; /* unsigned distance */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Char */ + /* */ + /* <Description> */ + /* A simple typedef for the _signed_ char type. */ + /* */ + typedef signed char FT_Char; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Byte */ + /* */ + /* <Description> */ + /* A simple typedef for the _unsigned_ char type. */ + /* */ + typedef unsigned char FT_Byte; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Bytes */ + /* */ + /* <Description> */ + /* A typedef for constant memory areas. */ + /* */ + typedef const FT_Byte* FT_Bytes; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Tag */ + /* */ + /* <Description> */ + /* A typedef for 32-bit tags (as used in the SFNT format). */ + /* */ + typedef FT_UInt32 FT_Tag; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_String */ + /* */ + /* <Description> */ + /* A simple typedef for the char type, usually used for strings. */ + /* */ + typedef char FT_String; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Short */ + /* */ + /* <Description> */ + /* A typedef for signed short. */ + /* */ + typedef signed short FT_Short; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UShort */ + /* */ + /* <Description> */ + /* A typedef for unsigned short. */ + /* */ + typedef unsigned short FT_UShort; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int */ + /* */ + /* <Description> */ + /* A typedef for the int type. */ + /* */ + typedef signed int FT_Int; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt */ + /* */ + /* <Description> */ + /* A typedef for the unsigned int type. */ + /* */ + typedef unsigned int FT_UInt; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Long */ + /* */ + /* <Description> */ + /* A typedef for signed long. */ + /* */ + typedef signed long FT_Long; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ULong */ + /* */ + /* <Description> */ + /* A typedef for unsigned long. */ + /* */ + typedef unsigned long FT_ULong; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F2Dot14 */ + /* */ + /* <Description> */ + /* A signed 2.14 fixed-point type used for unit vectors. */ + /* */ + typedef signed short FT_F2Dot14; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F26Dot6 */ + /* */ + /* <Description> */ + /* A signed 26.6 fixed-point type used for vectorial pixel */ + /* coordinates. */ + /* */ + typedef signed long FT_F26Dot6; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Fixed */ + /* */ + /* <Description> */ + /* This type is used to store 16.16 fixed-point values, like scaling */ + /* values or matrix coefficients. */ + /* */ + typedef signed long FT_Fixed; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Error */ + /* */ + /* <Description> */ + /* The FreeType error code type. A value of~0 is always interpreted */ + /* as a successful operation. */ + /* */ + typedef int FT_Error; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pointer */ + /* */ + /* <Description> */ + /* A simple typedef for a typeless pointer. */ + /* */ + typedef void* FT_Pointer; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Offset */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI~C `size_t' type, i.e., the largest */ + /* _unsigned_ integer type used to express a file size or position, */ + /* or a memory block size. */ + /* */ + typedef size_t FT_Offset; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_PtrDist */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI~C `ptrdiff_t' type, i.e., the */ + /* largest _signed_ integer type used to express the distance */ + /* between two pointers. */ + /* */ + typedef ft_ptrdiff_t FT_PtrDist; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_UnitVector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector unit vector. Uses */ + /* FT_F2Dot14 types. */ + /* */ + /* <Fields> */ + /* x :: Horizontal coordinate. */ + /* */ + /* y :: Vertical coordinate. */ + /* */ + typedef struct FT_UnitVector_ + { + FT_F2Dot14 x; + FT_F2Dot14 y; + + } FT_UnitVector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Matrix */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2x2 matrix. Coefficients are */ + /* in 16.16 fixed-point format. The computation performed is: */ + /* */ + /* { */ + /* x' = x*xx + y*xy */ + /* y' = x*yx + y*yy */ + /* } */ + /* */ + /* <Fields> */ + /* xx :: Matrix coefficient. */ + /* */ + /* xy :: Matrix coefficient. */ + /* */ + /* yx :: Matrix coefficient. */ + /* */ + /* yy :: Matrix coefficient. */ + /* */ + typedef struct FT_Matrix_ + { + FT_Fixed xx, xy; + FT_Fixed yx, yy; + + } FT_Matrix; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Data */ + /* */ + /* <Description> */ + /* Read-only binary data represented as a pointer and a length. */ + /* */ + /* <Fields> */ + /* pointer :: The data. */ + /* */ + /* length :: The length of the data in bytes. */ + /* */ + typedef struct FT_Data_ + { + const FT_Byte* pointer; + FT_Int length; + + } FT_Data; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Generic_Finalizer */ + /* */ + /* <Description> */ + /* Describe a function used to destroy the `client' data of any */ + /* FreeType object. See the description of the @FT_Generic type for */ + /* details of usage. */ + /* */ + /* <Input> */ + /* The address of the FreeType object that is under finalization. */ + /* Its client data is accessed through its `generic' field. */ + /* */ + typedef void (*FT_Generic_Finalizer)(void* object); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Generic */ + /* */ + /* <Description> */ + /* Client applications often need to associate their own data to a */ + /* variety of FreeType core objects. For example, a text layout API */ + /* might want to associate a glyph cache to a given size object. */ + /* */ + /* Some FreeType object contains a `generic' field, of type */ + /* FT_Generic, which usage is left to client applications and font */ + /* servers. */ + /* */ + /* It can be used to store a pointer to client-specific data, as well */ + /* as the address of a `finalizer' function, which will be called by */ + /* FreeType when the object is destroyed (for example, the previous */ + /* client example would put the address of the glyph cache destructor */ + /* in the `finalizer' field). */ + /* */ + /* <Fields> */ + /* data :: A typeless pointer to any client-specified data. This */ + /* field is completely ignored by the FreeType library. */ + /* */ + /* finalizer :: A pointer to a `generic finalizer' function, which */ + /* will be called when the object is destroyed. If this */ + /* field is set to NULL, no code will be called. */ + /* */ + typedef struct FT_Generic_ + { + void* data; + FT_Generic_Finalizer finalizer; + + } FT_Generic; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_MAKE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags that are used to label */ + /* TrueType tables into an unsigned long, to be used within FreeType. */ + /* */ + /* <Note> */ + /* The produced values *must* be 32-bit integers. Don't redefine */ + /* this macro. */ + /* */ +#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ + (FT_Tag) \ + ( ( (FT_ULong)_x1 << 24 ) | \ + ( (FT_ULong)_x2 << 16 ) | \ + ( (FT_ULong)_x3 << 8 ) | \ + (FT_ULong)_x4 ) + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* L I S T M A N A G E M E N T */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* list_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ListNode */ + /* */ + /* <Description> */ + /* Many elements and objects in FreeType are listed through an */ + /* @FT_List record (see @FT_ListRec). As its name suggests, an */ + /* FT_ListNode is a handle to a single list element. */ + /* */ + typedef struct FT_ListNodeRec_* FT_ListNode; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_List */ + /* */ + /* <Description> */ + /* A handle to a list record (see @FT_ListRec). */ + /* */ + typedef struct FT_ListRec_* FT_List; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListNodeRec */ + /* */ + /* <Description> */ + /* A structure used to hold a single list element. */ + /* */ + /* <Fields> */ + /* prev :: The previous element in the list. NULL if first. */ + /* */ + /* next :: The next element in the list. NULL if last. */ + /* */ + /* data :: A typeless pointer to the listed object. */ + /* */ + typedef struct FT_ListNodeRec_ + { + FT_ListNode prev; + FT_ListNode next; + void* data; + + } FT_ListNodeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListRec */ + /* */ + /* <Description> */ + /* A structure used to hold a simple doubly-linked list. These are */ + /* used in many parts of FreeType. */ + /* */ + /* <Fields> */ + /* head :: The head (first element) of doubly-linked list. */ + /* */ + /* tail :: The tail (last element) of doubly-linked list. */ + /* */ + typedef struct FT_ListRec_ + { + FT_ListNode head; + FT_ListNode tail; + + } FT_ListRec; + + /* */ + + +#define FT_IS_EMPTY( list ) ( (list).head == 0 ) +#define FT_BOOL( x ) ( (FT_Bool)( x ) ) + + /* concatenate C tokens */ +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + + /* see `ftmoderr.h' for descriptions of the following macros */ + +#define FT_ERR( e ) FT_ERR_CAT( FT_ERR_PREFIX, e ) + +#define FT_ERROR_BASE( x ) ( (x) & 0xFF ) +#define FT_ERROR_MODULE( x ) ( (x) & 0xFF00U ) + +#define FT_ERR_EQ( x, e ) \ + ( FT_ERROR_BASE( x ) == FT_ERROR_BASE( FT_ERR( e ) ) ) +#define FT_ERR_NEQ( x, e ) \ + ( FT_ERROR_BASE( x ) != FT_ERROR_BASE( FT_ERR( e ) ) ) + + +FT_END_HEADER + +#endif /* FTTYPES_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ftwinfnt.h b/foreign/freetype2/freetype/ftwinfnt.h new file mode 100644 index 0000000..a1a715b --- /dev/null +++ b/foreign/freetype2/freetype/ftwinfnt.h @@ -0,0 +1,275 @@ +/***************************************************************************/ +/* */ +/* ftwinfnt.h */ +/* */ +/* FreeType API for accessing Windows fnt-specific data. */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTWINFNT_H_ +#define FTWINFNT_H_ + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* winfnt_fonts */ + /* */ + /* <Title> */ + /* Window FNT Files */ + /* */ + /* <Abstract> */ + /* Windows FNT specific API. */ + /* */ + /* <Description> */ + /* This section contains the declaration of Windows FNT specific */ + /* functions. */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @enum: + * FT_WinFNT_ID_XXX + * + * @description: + * A list of valid values for the `charset' byte in + * @FT_WinFNT_HeaderRec. Exact mapping tables for the various cpXXXX + * encodings (except for cp1361) can be found at + * ftp://ftp.unicode.org/Public in the MAPPINGS/VENDORS/MICSFT/WINDOWS + * subdirectory. cp1361 is roughly a superset of + * MAPPINGS/OBSOLETE/EASTASIA/KSC/JOHAB.TXT. + * + * @values: + * FT_WinFNT_ID_DEFAULT :: + * This is used for font enumeration and font creation as a + * `don't care' value. Valid font files don't contain this value. + * When querying for information about the character set of the font + * that is currently selected into a specified device context, this + * return value (of the related Windows API) simply denotes failure. + * + * FT_WinFNT_ID_SYMBOL :: + * There is no known mapping table available. + * + * FT_WinFNT_ID_MAC :: + * Mac Roman encoding. + * + * FT_WinFNT_ID_OEM :: + * From Michael Pöttgen <michael@poettgen.de>: + * + * The `Windows Font Mapping' article says that FT_WinFNT_ID_OEM + * is used for the charset of vector fonts, like `modern.fon', + * `roman.fon', and `script.fon' on Windows. + * + * The `CreateFont' documentation says: The FT_WinFNT_ID_OEM value + * specifies a character set that is operating-system dependent. + * + * The `IFIMETRICS' documentation from the `Windows Driver + * Development Kit' says: This font supports an OEM-specific + * character set. The OEM character set is system dependent. + * + * In general OEM, as opposed to ANSI (i.e., cp1252), denotes the + * second default codepage that most international versions of + * Windows have. It is one of the OEM codepages from + * + * https://msdn.microsoft.com/en-us/goglobal/bb964655, + * + * and is used for the `DOS boxes', to support legacy applications. + * A German Windows version for example usually uses ANSI codepage + * 1252 and OEM codepage 850. + * + * FT_WinFNT_ID_CP874 :: + * A superset of Thai TIS 620 and ISO 8859-11. + * + * FT_WinFNT_ID_CP932 :: + * A superset of Japanese Shift-JIS (with minor deviations). + * + * FT_WinFNT_ID_CP936 :: + * A superset of simplified Chinese GB 2312-1980 (with different + * ordering and minor deviations). + * + * FT_WinFNT_ID_CP949 :: + * A superset of Korean Hangul KS~C 5601-1987 (with different + * ordering and minor deviations). + * + * FT_WinFNT_ID_CP950 :: + * A superset of traditional Chinese Big~5 ETen (with different + * ordering and minor deviations). + * + * FT_WinFNT_ID_CP1250 :: + * A superset of East European ISO 8859-2 (with slightly different + * ordering). + * + * FT_WinFNT_ID_CP1251 :: + * A superset of Russian ISO 8859-5 (with different ordering). + * + * FT_WinFNT_ID_CP1252 :: + * ANSI encoding. A superset of ISO 8859-1. + * + * FT_WinFNT_ID_CP1253 :: + * A superset of Greek ISO 8859-7 (with minor modifications). + * + * FT_WinFNT_ID_CP1254 :: + * A superset of Turkish ISO 8859-9. + * + * FT_WinFNT_ID_CP1255 :: + * A superset of Hebrew ISO 8859-8 (with some modifications). + * + * FT_WinFNT_ID_CP1256 :: + * A superset of Arabic ISO 8859-6 (with different ordering). + * + * FT_WinFNT_ID_CP1257 :: + * A superset of Baltic ISO 8859-13 (with some deviations). + * + * FT_WinFNT_ID_CP1258 :: + * For Vietnamese. This encoding doesn't cover all necessary + * characters. + * + * FT_WinFNT_ID_CP1361 :: + * Korean (Johab). + */ + +#define FT_WinFNT_ID_CP1252 0 +#define FT_WinFNT_ID_DEFAULT 1 +#define FT_WinFNT_ID_SYMBOL 2 +#define FT_WinFNT_ID_MAC 77 +#define FT_WinFNT_ID_CP932 128 +#define FT_WinFNT_ID_CP949 129 +#define FT_WinFNT_ID_CP1361 130 +#define FT_WinFNT_ID_CP936 134 +#define FT_WinFNT_ID_CP950 136 +#define FT_WinFNT_ID_CP1253 161 +#define FT_WinFNT_ID_CP1254 162 +#define FT_WinFNT_ID_CP1258 163 +#define FT_WinFNT_ID_CP1255 177 +#define FT_WinFNT_ID_CP1256 178 +#define FT_WinFNT_ID_CP1257 186 +#define FT_WinFNT_ID_CP1251 204 +#define FT_WinFNT_ID_CP874 222 +#define FT_WinFNT_ID_CP1250 238 +#define FT_WinFNT_ID_OEM 255 + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_WinFNT_HeaderRec */ + /* */ + /* <Description> */ + /* Windows FNT Header info. */ + /* */ + typedef struct FT_WinFNT_HeaderRec_ + { + FT_UShort version; + FT_ULong file_size; + FT_Byte copyright[60]; + FT_UShort file_type; + FT_UShort nominal_point_size; + FT_UShort vertical_resolution; + FT_UShort horizontal_resolution; + FT_UShort ascent; + FT_UShort internal_leading; + FT_UShort external_leading; + FT_Byte italic; + FT_Byte underline; + FT_Byte strike_out; + FT_UShort weight; + FT_Byte charset; + FT_UShort pixel_width; + FT_UShort pixel_height; + FT_Byte pitch_and_family; + FT_UShort avg_width; + FT_UShort max_width; + FT_Byte first_char; + FT_Byte last_char; + FT_Byte default_char; + FT_Byte break_char; + FT_UShort bytes_per_row; + FT_ULong device_offset; + FT_ULong face_name_offset; + FT_ULong bits_pointer; + FT_ULong bits_offset; + FT_Byte reserved; + FT_ULong flags; + FT_UShort A_space; + FT_UShort B_space; + FT_UShort C_space; + FT_UShort color_table_offset; + FT_ULong reserved1[4]; + + } FT_WinFNT_HeaderRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_WinFNT_Header */ + /* */ + /* <Description> */ + /* A handle to an @FT_WinFNT_HeaderRec structure. */ + /* */ + typedef struct FT_WinFNT_HeaderRec_* FT_WinFNT_Header; + + + /********************************************************************** + * + * @function: + * FT_Get_WinFNT_Header + * + * @description: + * Retrieve a Windows FNT font info header. + * + * @input: + * face :: A handle to the input face. + * + * @output: + * aheader :: The WinFNT header. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * This function only works with Windows FNT faces, returning an error + * otherwise. + */ + FT_EXPORT( FT_Error ) + FT_Get_WinFNT_Header( FT_Face face, + FT_WinFNT_HeaderRec *aheader ); + + /* */ + + +FT_END_HEADER + +#endif /* FTWINFNT_H_ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/foreign/freetype2/freetype/t1tables.h b/foreign/freetype2/freetype/t1tables.h new file mode 100644 index 0000000..e272324 --- /dev/null +++ b/foreign/freetype2/freetype/t1tables.h @@ -0,0 +1,761 @@ +/***************************************************************************/ +/* */ +/* t1tables.h */ +/* */ +/* Basic Type 1/Type 2 tables definitions and interface (specification */ +/* only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef T1TABLES_H_ +#define T1TABLES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* type1_tables */ + /* */ + /* <Title> */ + /* Type 1 Tables */ + /* */ + /* <Abstract> */ + /* Type~1 (PostScript) specific font tables. */ + /* */ + /* <Description> */ + /* This section contains the definition of Type 1-specific tables, */ + /* including structures related to other PostScript font formats. */ + /* */ + /* <Order> */ + /* PS_FontInfoRec */ + /* PS_FontInfo */ + /* PS_PrivateRec */ + /* PS_Private */ + /* */ + /* CID_FaceDictRec */ + /* CID_FaceDict */ + /* CID_FaceInfoRec */ + /* CID_FaceInfo */ + /* */ + /* FT_Has_PS_Glyph_Names */ + /* FT_Get_PS_Font_Info */ + /* FT_Get_PS_Font_Private */ + /* FT_Get_PS_Font_Value */ + /* */ + /* T1_Blend_Flags */ + /* T1_EncodingType */ + /* PS_Dict_Keys */ + /* */ + /*************************************************************************/ + + + /* Note that we separate font data in PS_FontInfoRec and PS_PrivateRec */ + /* structures in order to support Multiple Master fonts. */ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_FontInfoRec */ + /* */ + /* <Description> */ + /* A structure used to model a Type~1 or Type~2 FontInfo dictionary. */ + /* Note that for Multiple Master fonts, each instance has its own */ + /* FontInfo dictionary. */ + /* */ + typedef struct PS_FontInfoRec_ + { + FT_String* version; + FT_String* notice; + FT_String* full_name; + FT_String* family_name; + FT_String* weight; + FT_Long italic_angle; + FT_Bool is_fixed_pitch; + FT_Short underline_position; + FT_UShort underline_thickness; + + } PS_FontInfoRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_FontInfo */ + /* */ + /* <Description> */ + /* A handle to a @PS_FontInfoRec structure. */ + /* */ + typedef struct PS_FontInfoRec_* PS_FontInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_FontInfo */ + /* */ + /* <Description> */ + /* This type is equivalent to @PS_FontInfoRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef PS_FontInfoRec T1_FontInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_PrivateRec */ + /* */ + /* <Description> */ + /* A structure used to model a Type~1 or Type~2 private dictionary. */ + /* Note that for Multiple Master fonts, each instance has its own */ + /* Private dictionary. */ + /* */ + typedef struct PS_PrivateRec_ + { + FT_Int unique_id; + FT_Int lenIV; + + FT_Byte num_blue_values; + FT_Byte num_other_blues; + FT_Byte num_family_blues; + FT_Byte num_family_other_blues; + + FT_Short blue_values[14]; + FT_Short other_blues[10]; + + FT_Short family_blues [14]; + FT_Short family_other_blues[10]; + + FT_Fixed blue_scale; + FT_Int blue_shift; + FT_Int blue_fuzz; + + FT_UShort standard_width[1]; + FT_UShort standard_height[1]; + + FT_Byte num_snap_widths; + FT_Byte num_snap_heights; + FT_Bool force_bold; + FT_Bool round_stem_up; + + FT_Short snap_widths [13]; /* including std width */ + FT_Short snap_heights[13]; /* including std height */ + + FT_Fixed expansion_factor; + + FT_Long language_group; + FT_Long password; + + FT_Short min_feature[2]; + + } PS_PrivateRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* PS_Private */ + /* */ + /* <Description> */ + /* A handle to a @PS_PrivateRec structure. */ + /* */ + typedef struct PS_PrivateRec_* PS_Private; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* T1_Private */ + /* */ + /* <Description> */ + /* This type is equivalent to @PS_PrivateRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef PS_PrivateRec T1_Private; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* T1_Blend_Flags */ + /* */ + /* <Description> */ + /* A set of flags used to indicate which fields are present in a */ + /* given blend dictionary (font info or private). Used to support */ + /* Multiple Masters fonts. */ + /* */ + /* <Values> */ + /* T1_BLEND_UNDERLINE_POSITION :: */ + /* T1_BLEND_UNDERLINE_THICKNESS :: */ + /* T1_BLEND_ITALIC_ANGLE :: */ + /* T1_BLEND_BLUE_VALUES :: */ + /* T1_BLEND_OTHER_BLUES :: */ + /* T1_BLEND_STANDARD_WIDTH :: */ + /* T1_BLEND_STANDARD_HEIGHT :: */ + /* T1_BLEND_STEM_SNAP_WIDTHS :: */ + /* T1_BLEND_STEM_SNAP_HEIGHTS :: */ + /* T1_BLEND_BLUE_SCALE :: */ + /* T1_BLEND_BLUE_SHIFT :: */ + /* T1_BLEND_FAMILY_BLUES :: */ + /* T1_BLEND_FAMILY_OTHER_BLUES :: */ + /* T1_BLEND_FORCE_BOLD :: */ + /* */ + typedef enum T1_Blend_Flags_ + { + /* required fields in a FontInfo blend dictionary */ + T1_BLEND_UNDERLINE_POSITION = 0, + T1_BLEND_UNDERLINE_THICKNESS, + T1_BLEND_ITALIC_ANGLE, + + /* required fields in a Private blend dictionary */ + T1_BLEND_BLUE_VALUES, + T1_BLEND_OTHER_BLUES, + T1_BLEND_STANDARD_WIDTH, + T1_BLEND_STANDARD_HEIGHT, + T1_BLEND_STEM_SNAP_WIDTHS, + T1_BLEND_STEM_SNAP_HEIGHTS, + T1_BLEND_BLUE_SCALE, + T1_BLEND_BLUE_SHIFT, + T1_BLEND_FAMILY_BLUES, + T1_BLEND_FAMILY_OTHER_BLUES, + T1_BLEND_FORCE_BOLD, + + T1_BLEND_MAX /* do not remove */ + + } T1_Blend_Flags; + + + /* these constants are deprecated; use the corresponding */ + /* `T1_Blend_Flags' values instead */ +#define t1_blend_underline_position T1_BLEND_UNDERLINE_POSITION +#define t1_blend_underline_thickness T1_BLEND_UNDERLINE_THICKNESS +#define t1_blend_italic_angle T1_BLEND_ITALIC_ANGLE +#define t1_blend_blue_values T1_BLEND_BLUE_VALUES +#define t1_blend_other_blues T1_BLEND_OTHER_BLUES +#define t1_blend_standard_widths T1_BLEND_STANDARD_WIDTH +#define t1_blend_standard_height T1_BLEND_STANDARD_HEIGHT +#define t1_blend_stem_snap_widths T1_BLEND_STEM_SNAP_WIDTHS +#define t1_blend_stem_snap_heights T1_BLEND_STEM_SNAP_HEIGHTS +#define t1_blend_blue_scale T1_BLEND_BLUE_SCALE +#define t1_blend_blue_shift T1_BLEND_BLUE_SHIFT +#define t1_blend_family_blues T1_BLEND_FAMILY_BLUES +#define t1_blend_family_other_blues T1_BLEND_FAMILY_OTHER_BLUES +#define t1_blend_force_bold T1_BLEND_FORCE_BOLD +#define t1_blend_max T1_BLEND_MAX + + /* */ + + + /* maximum number of Multiple Masters designs, as defined in the spec */ +#define T1_MAX_MM_DESIGNS 16 + + /* maximum number of Multiple Masters axes, as defined in the spec */ +#define T1_MAX_MM_AXIS 4 + + /* maximum number of elements in a design map */ +#define T1_MAX_MM_MAP_POINTS 20 + + + /* this structure is used to store the BlendDesignMap entry for an axis */ + typedef struct PS_DesignMap_ + { + FT_Byte num_points; + FT_Long* design_points; + FT_Fixed* blend_points; + + } PS_DesignMapRec, *PS_DesignMap; + + /* backwards-compatible definition */ + typedef PS_DesignMapRec T1_DesignMap; + + + typedef struct PS_BlendRec_ + { + FT_UInt num_designs; + FT_UInt num_axis; + + FT_String* axis_names[T1_MAX_MM_AXIS]; + FT_Fixed* design_pos[T1_MAX_MM_DESIGNS]; + PS_DesignMapRec design_map[T1_MAX_MM_AXIS]; + + FT_Fixed* weight_vector; + FT_Fixed* default_weight_vector; + + PS_FontInfo font_infos[T1_MAX_MM_DESIGNS + 1]; + PS_Private privates [T1_MAX_MM_DESIGNS + 1]; + + FT_ULong blend_bitflags; + + FT_BBox* bboxes [T1_MAX_MM_DESIGNS + 1]; + + /* since 2.3.0 */ + + /* undocumented, optional: the default design instance; */ + /* corresponds to default_weight_vector -- */ + /* num_default_design_vector == 0 means it is not present */ + /* in the font and associated metrics files */ + FT_UInt default_design_vector[T1_MAX_MM_DESIGNS]; + FT_UInt num_default_design_vector; + + } PS_BlendRec, *PS_Blend; + + + /* backwards-compatible definition */ + typedef PS_BlendRec T1_Blend; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceDictRec */ + /* */ + /* <Description> */ + /* A structure used to represent data in a CID top-level dictionary. */ + /* */ + typedef struct CID_FaceDictRec_ + { + PS_PrivateRec private_dict; + + FT_UInt len_buildchar; + FT_Fixed forcebold_threshold; + FT_Pos stroke_width; + FT_Fixed expansion_factor; + + FT_Byte paint_type; + FT_Byte font_type; + FT_Matrix font_matrix; + FT_Vector font_offset; + + FT_UInt num_subrs; + FT_ULong subrmap_offset; + FT_Int sd_bytes; + + } CID_FaceDictRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceDict */ + /* */ + /* <Description> */ + /* A handle to a @CID_FaceDictRec structure. */ + /* */ + typedef struct CID_FaceDictRec_* CID_FaceDict; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FontDict */ + /* */ + /* <Description> */ + /* This type is equivalent to @CID_FaceDictRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef CID_FaceDictRec CID_FontDict; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceInfoRec */ + /* */ + /* <Description> */ + /* A structure used to represent CID Face information. */ + /* */ + typedef struct CID_FaceInfoRec_ + { + FT_String* cid_font_name; + FT_Fixed cid_version; + FT_Int cid_font_type; + + FT_String* registry; + FT_String* ordering; + FT_Int supplement; + + PS_FontInfoRec font_info; + FT_BBox font_bbox; + FT_ULong uid_base; + + FT_Int num_xuid; + FT_ULong xuid[16]; + + FT_ULong cidmap_offset; + FT_Int fd_bytes; + FT_Int gd_bytes; + FT_ULong cid_count; + + FT_Int num_dicts; + CID_FaceDict font_dicts; + + FT_ULong data_offset; + + } CID_FaceInfoRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_FaceInfo */ + /* */ + /* <Description> */ + /* A handle to a @CID_FaceInfoRec structure. */ + /* */ + typedef struct CID_FaceInfoRec_* CID_FaceInfo; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* CID_Info */ + /* */ + /* <Description> */ + /* This type is equivalent to @CID_FaceInfoRec. It is deprecated but */ + /* kept to maintain source compatibility between various versions of */ + /* FreeType. */ + /* */ + typedef CID_FaceInfoRec CID_Info; + + + /************************************************************************ + * + * @function: + * FT_Has_PS_Glyph_Names + * + * @description: + * Return true if a given face provides reliable PostScript glyph + * names. This is similar to using the @FT_HAS_GLYPH_NAMES macro, + * except that certain fonts (mostly TrueType) contain incorrect + * glyph name tables. + * + * When this function returns true, the caller is sure that the glyph + * names returned by @FT_Get_Glyph_Name are reliable. + * + * @input: + * face :: + * face handle + * + * @return: + * Boolean. True if glyph names are reliable. + * + */ + FT_EXPORT( FT_Int ) + FT_Has_PS_Glyph_Names( FT_Face face ); + + + /************************************************************************ + * + * @function: + * FT_Get_PS_Font_Info + * + * @description: + * Retrieve the @PS_FontInfoRec structure corresponding to a given + * PostScript font. + * + * @input: + * face :: + * PostScript face handle. + * + * @output: + * afont_info :: + * Output font info structure pointer. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * String pointers within the @PS_FontInfoRec structure are owned by + * the face and don't need to be freed by the caller. Missing entries + * in the font's FontInfo dictionary are represented by NULL pointers. + * + * If the font's format is not PostScript-based, this function will + * return the `FT_Err_Invalid_Argument' error code. + * + */ + FT_EXPORT( FT_Error ) + FT_Get_PS_Font_Info( FT_Face face, + PS_FontInfo afont_info ); + + + /************************************************************************ + * + * @function: + * FT_Get_PS_Font_Private + * + * @description: + * Retrieve the @PS_PrivateRec structure corresponding to a given + * PostScript font. + * + * @input: + * face :: + * PostScript face handle. + * + * @output: + * afont_private :: + * Output private dictionary structure pointer. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The string pointers within the @PS_PrivateRec structure are owned by + * the face and don't need to be freed by the caller. + * + * If the font's format is not PostScript-based, this function returns + * the `FT_Err_Invalid_Argument' error code. + * + */ + FT_EXPORT( FT_Error ) + FT_Get_PS_Font_Private( FT_Face face, + PS_Private afont_private ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* T1_EncodingType */ + /* */ + /* <Description> */ + /* An enumeration describing the `Encoding' entry in a Type 1 */ + /* dictionary. */ + /* */ + /* <Values> */ + /* T1_ENCODING_TYPE_NONE :: */ + /* T1_ENCODING_TYPE_ARRAY :: */ + /* T1_ENCODING_TYPE_STANDARD :: */ + /* T1_ENCODING_TYPE_ISOLATIN1 :: */ + /* T1_ENCODING_TYPE_EXPERT :: */ + /* */ + typedef enum T1_EncodingType_ + { + T1_ENCODING_TYPE_NONE = 0, + T1_ENCODING_TYPE_ARRAY, + T1_ENCODING_TYPE_STANDARD, + T1_ENCODING_TYPE_ISOLATIN1, + T1_ENCODING_TYPE_EXPERT + + } T1_EncodingType; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* PS_Dict_Keys */ + /* */ + /* <Description> */ + /* An enumeration used in calls to @FT_Get_PS_Font_Value to identify */ + /* the Type~1 dictionary entry to retrieve. */ + /* */ + /* <Values> */ + /* PS_DICT_FONT_TYPE :: */ + /* PS_DICT_FONT_MATRIX :: */ + /* PS_DICT_FONT_BBOX :: */ + /* PS_DICT_PAINT_TYPE :: */ + /* PS_DICT_FONT_NAME :: */ + /* PS_DICT_UNIQUE_ID :: */ + /* PS_DICT_NUM_CHAR_STRINGS :: */ + /* PS_DICT_CHAR_STRING_KEY :: */ + /* PS_DICT_CHAR_STRING :: */ + /* PS_DICT_ENCODING_TYPE :: */ + /* PS_DICT_ENCODING_ENTRY :: */ + /* PS_DICT_NUM_SUBRS :: */ + /* PS_DICT_SUBR :: */ + /* PS_DICT_STD_HW :: */ + /* PS_DICT_STD_VW :: */ + /* PS_DICT_NUM_BLUE_VALUES :: */ + /* PS_DICT_BLUE_VALUE :: */ + /* PS_DICT_BLUE_FUZZ :: */ + /* PS_DICT_NUM_OTHER_BLUES :: */ + /* PS_DICT_OTHER_BLUE :: */ + /* PS_DICT_NUM_FAMILY_BLUES :: */ + /* PS_DICT_FAMILY_BLUE :: */ + /* PS_DICT_NUM_FAMILY_OTHER_BLUES :: */ + /* PS_DICT_FAMILY_OTHER_BLUE :: */ + /* PS_DICT_BLUE_SCALE :: */ + /* PS_DICT_BLUE_SHIFT :: */ + /* PS_DICT_NUM_STEM_SNAP_H :: */ + /* PS_DICT_STEM_SNAP_H :: */ + /* PS_DICT_NUM_STEM_SNAP_V :: */ + /* PS_DICT_STEM_SNAP_V :: */ + /* PS_DICT_FORCE_BOLD :: */ + /* PS_DICT_RND_STEM_UP :: */ + /* PS_DICT_MIN_FEATURE :: */ + /* PS_DICT_LEN_IV :: */ + /* PS_DICT_PASSWORD :: */ + /* PS_DICT_LANGUAGE_GROUP :: */ + /* PS_DICT_VERSION :: */ + /* PS_DICT_NOTICE :: */ + /* PS_DICT_FULL_NAME :: */ + /* PS_DICT_FAMILY_NAME :: */ + /* PS_DICT_WEIGHT :: */ + /* PS_DICT_IS_FIXED_PITCH :: */ + /* PS_DICT_UNDERLINE_POSITION :: */ + /* PS_DICT_UNDERLINE_THICKNESS :: */ + /* PS_DICT_FS_TYPE :: */ + /* PS_DICT_ITALIC_ANGLE :: */ + /* */ + typedef enum PS_Dict_Keys_ + { + /* conventionally in the font dictionary */ + PS_DICT_FONT_TYPE, /* FT_Byte */ + PS_DICT_FONT_MATRIX, /* FT_Fixed */ + PS_DICT_FONT_BBOX, /* FT_Fixed */ + PS_DICT_PAINT_TYPE, /* FT_Byte */ + PS_DICT_FONT_NAME, /* FT_String* */ + PS_DICT_UNIQUE_ID, /* FT_Int */ + PS_DICT_NUM_CHAR_STRINGS, /* FT_Int */ + PS_DICT_CHAR_STRING_KEY, /* FT_String* */ + PS_DICT_CHAR_STRING, /* FT_String* */ + PS_DICT_ENCODING_TYPE, /* T1_EncodingType */ + PS_DICT_ENCODING_ENTRY, /* FT_String* */ + + /* conventionally in the font Private dictionary */ + PS_DICT_NUM_SUBRS, /* FT_Int */ + PS_DICT_SUBR, /* FT_String* */ + PS_DICT_STD_HW, /* FT_UShort */ + PS_DICT_STD_VW, /* FT_UShort */ + PS_DICT_NUM_BLUE_VALUES, /* FT_Byte */ + PS_DICT_BLUE_VALUE, /* FT_Short */ + PS_DICT_BLUE_FUZZ, /* FT_Int */ + PS_DICT_NUM_OTHER_BLUES, /* FT_Byte */ + PS_DICT_OTHER_BLUE, /* FT_Short */ + PS_DICT_NUM_FAMILY_BLUES, /* FT_Byte */ + PS_DICT_FAMILY_BLUE, /* FT_Short */ + PS_DICT_NUM_FAMILY_OTHER_BLUES, /* FT_Byte */ + PS_DICT_FAMILY_OTHER_BLUE, /* FT_Short */ + PS_DICT_BLUE_SCALE, /* FT_Fixed */ + PS_DICT_BLUE_SHIFT, /* FT_Int */ + PS_DICT_NUM_STEM_SNAP_H, /* FT_Byte */ + PS_DICT_STEM_SNAP_H, /* FT_Short */ + PS_DICT_NUM_STEM_SNAP_V, /* FT_Byte */ + PS_DICT_STEM_SNAP_V, /* FT_Short */ + PS_DICT_FORCE_BOLD, /* FT_Bool */ + PS_DICT_RND_STEM_UP, /* FT_Bool */ + PS_DICT_MIN_FEATURE, /* FT_Short */ + PS_DICT_LEN_IV, /* FT_Int */ + PS_DICT_PASSWORD, /* FT_Long */ + PS_DICT_LANGUAGE_GROUP, /* FT_Long */ + + /* conventionally in the font FontInfo dictionary */ + PS_DICT_VERSION, /* FT_String* */ + PS_DICT_NOTICE, /* FT_String* */ + PS_DICT_FULL_NAME, /* FT_String* */ + PS_DICT_FAMILY_NAME, /* FT_String* */ + PS_DICT_WEIGHT, /* FT_String* */ + PS_DICT_IS_FIXED_PITCH, /* FT_Bool */ + PS_DICT_UNDERLINE_POSITION, /* FT_Short */ + PS_DICT_UNDERLINE_THICKNESS, /* FT_UShort */ + PS_DICT_FS_TYPE, /* FT_UShort */ + PS_DICT_ITALIC_ANGLE, /* FT_Long */ + + PS_DICT_MAX = PS_DICT_ITALIC_ANGLE + + } PS_Dict_Keys; + + + /************************************************************************ + * + * @function: + * FT_Get_PS_Font_Value + * + * @description: + * Retrieve the value for the supplied key from a PostScript font. + * + * @input: + * face :: + * PostScript face handle. + * + * key :: + * An enumeration value representing the dictionary key to retrieve. + * + * idx :: + * For array values, this specifies the index to be returned. + * + * value :: + * A pointer to memory into which to write the value. + * + * valen_len :: + * The size, in bytes, of the memory supplied for the value. + * + * @output: + * value :: + * The value matching the above key, if it exists. + * + * @return: + * The amount of memory (in bytes) required to hold the requested + * value (if it exists, -1 otherwise). + * + * @note: + * The values returned are not pointers into the internal structures of + * the face, but are `fresh' copies, so that the memory containing them + * belongs to the calling application. This also enforces the + * `read-only' nature of these values, i.e., this function cannot be + * used to manipulate the face. + * + * `value' is a void pointer because the values returned can be of + * various types. + * + * If either `value' is NULL or `value_len' is too small, just the + * required memory size for the requested entry is returned. + * + * The `idx' parameter is used, not only to retrieve elements of, for + * example, the FontMatrix or FontBBox, but also to retrieve name keys + * from the CharStrings dictionary, and the charstrings themselves. It + * is ignored for atomic values. + * + * PS_DICT_BLUE_SCALE returns a value that is scaled up by 1000. To + * get the value as in the font stream, you need to divide by + * 65536000.0 (to remove the FT_Fixed scale, and the x1000 scale). + * + * IMPORTANT: Only key/value pairs read by the FreeType interpreter can + * be retrieved. So, for example, PostScript procedures such as NP, + * ND, and RD are not available. Arbitrary keys are, obviously, not be + * available either. + * + * If the font's format is not PostScript-based, this function returns + * the `FT_Err_Invalid_Argument' error code. + * + */ + FT_EXPORT( FT_Long ) + FT_Get_PS_Font_Value( FT_Face face, + PS_Dict_Keys key, + FT_UInt idx, + void *value, + FT_Long value_len ); + + /* */ + +FT_END_HEADER + +#endif /* T1TABLES_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ttnameid.h b/foreign/freetype2/freetype/ttnameid.h new file mode 100644 index 0000000..ce707f1 --- /dev/null +++ b/foreign/freetype2/freetype/ttnameid.h @@ -0,0 +1,1237 @@ +/***************************************************************************/ +/* */ +/* ttnameid.h */ +/* */ +/* TrueType name ID definitions (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTNAMEID_H_ +#define TTNAMEID_H_ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* truetype_tables */ + /* */ + + + /*************************************************************************/ + /* */ + /* Possible values for the `platform' identifier code in the name */ + /* records of the TTF `name' table. */ + /* */ + /*************************************************************************/ + + + /*********************************************************************** + * + * @enum: + * TT_PLATFORM_XXX + * + * @description: + * A list of valid values for the `platform_id' identifier code in + * @FT_CharMapRec and @FT_SfntName structures. + * + * @values: + * TT_PLATFORM_APPLE_UNICODE :: + * Used by Apple to indicate a Unicode character map and/or name entry. + * See @TT_APPLE_ID_XXX for corresponding `encoding_id' values. Note + * that name entries in this format are coded as big-endian UCS-2 + * character codes _only_. + * + * TT_PLATFORM_MACINTOSH :: + * Used by Apple to indicate a MacOS-specific charmap and/or name entry. + * See @TT_MAC_ID_XXX for corresponding `encoding_id' values. Note that + * most TrueType fonts contain an Apple roman charmap to be usable on + * MacOS systems (even if they contain a Microsoft charmap as well). + * + * TT_PLATFORM_ISO :: + * This value was used to specify ISO/IEC 10646 charmaps. It is however + * now deprecated. See @TT_ISO_ID_XXX for a list of corresponding + * `encoding_id' values. + * + * TT_PLATFORM_MICROSOFT :: + * Used by Microsoft to indicate Windows-specific charmaps. See + * @TT_MS_ID_XXX for a list of corresponding `encoding_id' values. + * Note that most fonts contain a Unicode charmap using + * (TT_PLATFORM_MICROSOFT, @TT_MS_ID_UNICODE_CS). + * + * TT_PLATFORM_CUSTOM :: + * Used to indicate application-specific charmaps. + * + * TT_PLATFORM_ADOBE :: + * This value isn't part of any font format specification, but is used + * by FreeType to report Adobe-specific charmaps in an @FT_CharMapRec + * structure. See @TT_ADOBE_ID_XXX. + */ + +#define TT_PLATFORM_APPLE_UNICODE 0 +#define TT_PLATFORM_MACINTOSH 1 +#define TT_PLATFORM_ISO 2 /* deprecated */ +#define TT_PLATFORM_MICROSOFT 3 +#define TT_PLATFORM_CUSTOM 4 +#define TT_PLATFORM_ADOBE 7 /* artificial */ + + + /*********************************************************************** + * + * @enum: + * TT_APPLE_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_APPLE_UNICODE charmaps and name entries. + * + * @values: + * TT_APPLE_ID_DEFAULT :: + * Unicode version 1.0. + * + * TT_APPLE_ID_UNICODE_1_1 :: + * Unicode 1.1; specifies Hangul characters starting at U+34xx. + * + * TT_APPLE_ID_ISO_10646 :: + * Deprecated (identical to preceding). + * + * TT_APPLE_ID_UNICODE_2_0 :: + * Unicode 2.0 and beyond (UTF-16 BMP only). + * + * TT_APPLE_ID_UNICODE_32 :: + * Unicode 3.1 and beyond, using UTF-32. + * + * TT_APPLE_ID_VARIANT_SELECTOR :: + * From Adobe, not Apple. Not a normal cmap. Specifies variations + * on a real cmap. + */ + +#define TT_APPLE_ID_DEFAULT 0 /* Unicode 1.0 */ +#define TT_APPLE_ID_UNICODE_1_1 1 /* specify Hangul at U+34xx */ +#define TT_APPLE_ID_ISO_10646 2 /* deprecated */ +#define TT_APPLE_ID_UNICODE_2_0 3 /* or later */ +#define TT_APPLE_ID_UNICODE_32 4 /* 2.0 or later, full repertoire */ +#define TT_APPLE_ID_VARIANT_SELECTOR 5 /* variation selector data */ + + + /*********************************************************************** + * + * @enum: + * TT_MAC_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_MACINTOSH charmaps and name entries. + * + * @values: + * TT_MAC_ID_ROMAN :: + * TT_MAC_ID_JAPANESE :: + * TT_MAC_ID_TRADITIONAL_CHINESE :: + * TT_MAC_ID_KOREAN :: + * TT_MAC_ID_ARABIC :: + * TT_MAC_ID_HEBREW :: + * TT_MAC_ID_GREEK :: + * TT_MAC_ID_RUSSIAN :: + * TT_MAC_ID_RSYMBOL :: + * TT_MAC_ID_DEVANAGARI :: + * TT_MAC_ID_GURMUKHI :: + * TT_MAC_ID_GUJARATI :: + * TT_MAC_ID_ORIYA :: + * TT_MAC_ID_BENGALI :: + * TT_MAC_ID_TAMIL :: + * TT_MAC_ID_TELUGU :: + * TT_MAC_ID_KANNADA :: + * TT_MAC_ID_MALAYALAM :: + * TT_MAC_ID_SINHALESE :: + * TT_MAC_ID_BURMESE :: + * TT_MAC_ID_KHMER :: + * TT_MAC_ID_THAI :: + * TT_MAC_ID_LAOTIAN :: + * TT_MAC_ID_GEORGIAN :: + * TT_MAC_ID_ARMENIAN :: + * TT_MAC_ID_MALDIVIAN :: + * TT_MAC_ID_SIMPLIFIED_CHINESE :: + * TT_MAC_ID_TIBETAN :: + * TT_MAC_ID_MONGOLIAN :: + * TT_MAC_ID_GEEZ :: + * TT_MAC_ID_SLAVIC :: + * TT_MAC_ID_VIETNAMESE :: + * TT_MAC_ID_SINDHI :: + * TT_MAC_ID_UNINTERP :: + */ + +#define TT_MAC_ID_ROMAN 0 +#define TT_MAC_ID_JAPANESE 1 +#define TT_MAC_ID_TRADITIONAL_CHINESE 2 +#define TT_MAC_ID_KOREAN 3 +#define TT_MAC_ID_ARABIC 4 +#define TT_MAC_ID_HEBREW 5 +#define TT_MAC_ID_GREEK 6 +#define TT_MAC_ID_RUSSIAN 7 +#define TT_MAC_ID_RSYMBOL 8 +#define TT_MAC_ID_DEVANAGARI 9 +#define TT_MAC_ID_GURMUKHI 10 +#define TT_MAC_ID_GUJARATI 11 +#define TT_MAC_ID_ORIYA 12 +#define TT_MAC_ID_BENGALI 13 +#define TT_MAC_ID_TAMIL 14 +#define TT_MAC_ID_TELUGU 15 +#define TT_MAC_ID_KANNADA 16 +#define TT_MAC_ID_MALAYALAM 17 +#define TT_MAC_ID_SINHALESE 18 +#define TT_MAC_ID_BURMESE 19 +#define TT_MAC_ID_KHMER 20 +#define TT_MAC_ID_THAI 21 +#define TT_MAC_ID_LAOTIAN 22 +#define TT_MAC_ID_GEORGIAN 23 +#define TT_MAC_ID_ARMENIAN 24 +#define TT_MAC_ID_MALDIVIAN 25 +#define TT_MAC_ID_SIMPLIFIED_CHINESE 25 +#define TT_MAC_ID_TIBETAN 26 +#define TT_MAC_ID_MONGOLIAN 27 +#define TT_MAC_ID_GEEZ 28 +#define TT_MAC_ID_SLAVIC 29 +#define TT_MAC_ID_VIETNAMESE 30 +#define TT_MAC_ID_SINDHI 31 +#define TT_MAC_ID_UNINTERP 32 + + + /*********************************************************************** + * + * @enum: + * TT_ISO_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_ISO charmaps and name entries. + * + * Their use is now deprecated. + * + * @values: + * TT_ISO_ID_7BIT_ASCII :: + * ASCII. + * TT_ISO_ID_10646 :: + * ISO/10646. + * TT_ISO_ID_8859_1 :: + * Also known as Latin-1. + */ + +#define TT_ISO_ID_7BIT_ASCII 0 +#define TT_ISO_ID_10646 1 +#define TT_ISO_ID_8859_1 2 + + + /*********************************************************************** + * + * @enum: + * TT_MS_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_MICROSOFT charmaps and name entries. + * + * @values: + * TT_MS_ID_SYMBOL_CS :: + * Corresponds to Microsoft symbol encoding. See + * @FT_ENCODING_MS_SYMBOL. + * + * TT_MS_ID_UNICODE_CS :: + * Corresponds to a Microsoft WGL4 charmap, matching Unicode. See + * @FT_ENCODING_UNICODE. + * + * TT_MS_ID_SJIS :: + * Corresponds to SJIS Japanese encoding. See @FT_ENCODING_SJIS. + * + * TT_MS_ID_GB2312 :: + * Corresponds to Simplified Chinese as used in Mainland China. See + * @FT_ENCODING_GB2312. + * + * TT_MS_ID_BIG_5 :: + * Corresponds to Traditional Chinese as used in Taiwan and Hong Kong. + * See @FT_ENCODING_BIG5. + * + * TT_MS_ID_WANSUNG :: + * Corresponds to Korean Wansung encoding. See @FT_ENCODING_WANSUNG. + * + * TT_MS_ID_JOHAB :: + * Corresponds to Johab encoding. See @FT_ENCODING_JOHAB. + * + * TT_MS_ID_UCS_4 :: + * Corresponds to UCS-4 or UTF-32 charmaps. This has been added to + * the OpenType specification version 1.4 (mid-2001.) + */ + +#define TT_MS_ID_SYMBOL_CS 0 +#define TT_MS_ID_UNICODE_CS 1 +#define TT_MS_ID_SJIS 2 +#define TT_MS_ID_GB2312 3 +#define TT_MS_ID_BIG_5 4 +#define TT_MS_ID_WANSUNG 5 +#define TT_MS_ID_JOHAB 6 +#define TT_MS_ID_UCS_4 10 + + + /*********************************************************************** + * + * @enum: + * TT_ADOBE_ID_XXX + * + * @description: + * A list of valid values for the `encoding_id' for + * @TT_PLATFORM_ADOBE charmaps. This is a FreeType-specific extension! + * + * @values: + * TT_ADOBE_ID_STANDARD :: + * Adobe standard encoding. + * TT_ADOBE_ID_EXPERT :: + * Adobe expert encoding. + * TT_ADOBE_ID_CUSTOM :: + * Adobe custom encoding. + * TT_ADOBE_ID_LATIN_1 :: + * Adobe Latin~1 encoding. + */ + +#define TT_ADOBE_ID_STANDARD 0 +#define TT_ADOBE_ID_EXPERT 1 +#define TT_ADOBE_ID_CUSTOM 2 +#define TT_ADOBE_ID_LATIN_1 3 + + + /*************************************************************************/ + /* */ + /* Possible values of the language identifier field in the name records */ + /* of the TTF `name' table if the `platform' identifier code is */ + /* TT_PLATFORM_MACINTOSH. These values are also used as return values */ + /* for function @FT_Get_CMap_Language_ID. */ + /* */ + /* The canonical source for the Apple assigned Language ID's is at */ + /* */ + /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html */ + /* */ +#define TT_MAC_LANGID_ENGLISH 0 +#define TT_MAC_LANGID_FRENCH 1 +#define TT_MAC_LANGID_GERMAN 2 +#define TT_MAC_LANGID_ITALIAN 3 +#define TT_MAC_LANGID_DUTCH 4 +#define TT_MAC_LANGID_SWEDISH 5 +#define TT_MAC_LANGID_SPANISH 6 +#define TT_MAC_LANGID_DANISH 7 +#define TT_MAC_LANGID_PORTUGUESE 8 +#define TT_MAC_LANGID_NORWEGIAN 9 +#define TT_MAC_LANGID_HEBREW 10 +#define TT_MAC_LANGID_JAPANESE 11 +#define TT_MAC_LANGID_ARABIC 12 +#define TT_MAC_LANGID_FINNISH 13 +#define TT_MAC_LANGID_GREEK 14 +#define TT_MAC_LANGID_ICELANDIC 15 +#define TT_MAC_LANGID_MALTESE 16 +#define TT_MAC_LANGID_TURKISH 17 +#define TT_MAC_LANGID_CROATIAN 18 +#define TT_MAC_LANGID_CHINESE_TRADITIONAL 19 +#define TT_MAC_LANGID_URDU 20 +#define TT_MAC_LANGID_HINDI 21 +#define TT_MAC_LANGID_THAI 22 +#define TT_MAC_LANGID_KOREAN 23 +#define TT_MAC_LANGID_LITHUANIAN 24 +#define TT_MAC_LANGID_POLISH 25 +#define TT_MAC_LANGID_HUNGARIAN 26 +#define TT_MAC_LANGID_ESTONIAN 27 +#define TT_MAC_LANGID_LETTISH 28 +#define TT_MAC_LANGID_SAAMISK 29 +#define TT_MAC_LANGID_FAEROESE 30 +#define TT_MAC_LANGID_FARSI 31 +#define TT_MAC_LANGID_RUSSIAN 32 +#define TT_MAC_LANGID_CHINESE_SIMPLIFIED 33 +#define TT_MAC_LANGID_FLEMISH 34 +#define TT_MAC_LANGID_IRISH 35 +#define TT_MAC_LANGID_ALBANIAN 36 +#define TT_MAC_LANGID_ROMANIAN 37 +#define TT_MAC_LANGID_CZECH 38 +#define TT_MAC_LANGID_SLOVAK 39 +#define TT_MAC_LANGID_SLOVENIAN 40 +#define TT_MAC_LANGID_YIDDISH 41 +#define TT_MAC_LANGID_SERBIAN 42 +#define TT_MAC_LANGID_MACEDONIAN 43 +#define TT_MAC_LANGID_BULGARIAN 44 +#define TT_MAC_LANGID_UKRAINIAN 45 +#define TT_MAC_LANGID_BYELORUSSIAN 46 +#define TT_MAC_LANGID_UZBEK 47 +#define TT_MAC_LANGID_KAZAKH 48 +#define TT_MAC_LANGID_AZERBAIJANI 49 +#define TT_MAC_LANGID_AZERBAIJANI_CYRILLIC_SCRIPT 49 +#define TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT 50 +#define TT_MAC_LANGID_ARMENIAN 51 +#define TT_MAC_LANGID_GEORGIAN 52 +#define TT_MAC_LANGID_MOLDAVIAN 53 +#define TT_MAC_LANGID_KIRGHIZ 54 +#define TT_MAC_LANGID_TAJIKI 55 +#define TT_MAC_LANGID_TURKMEN 56 +#define TT_MAC_LANGID_MONGOLIAN 57 +#define TT_MAC_LANGID_MONGOLIAN_MONGOLIAN_SCRIPT 57 +#define TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT 58 +#define TT_MAC_LANGID_PASHTO 59 +#define TT_MAC_LANGID_KURDISH 60 +#define TT_MAC_LANGID_KASHMIRI 61 +#define TT_MAC_LANGID_SINDHI 62 +#define TT_MAC_LANGID_TIBETAN 63 +#define TT_MAC_LANGID_NEPALI 64 +#define TT_MAC_LANGID_SANSKRIT 65 +#define TT_MAC_LANGID_MARATHI 66 +#define TT_MAC_LANGID_BENGALI 67 +#define TT_MAC_LANGID_ASSAMESE 68 +#define TT_MAC_LANGID_GUJARATI 69 +#define TT_MAC_LANGID_PUNJABI 70 +#define TT_MAC_LANGID_ORIYA 71 +#define TT_MAC_LANGID_MALAYALAM 72 +#define TT_MAC_LANGID_KANNADA 73 +#define TT_MAC_LANGID_TAMIL 74 +#define TT_MAC_LANGID_TELUGU 75 +#define TT_MAC_LANGID_SINHALESE 76 +#define TT_MAC_LANGID_BURMESE 77 +#define TT_MAC_LANGID_KHMER 78 +#define TT_MAC_LANGID_LAO 79 +#define TT_MAC_LANGID_VIETNAMESE 80 +#define TT_MAC_LANGID_INDONESIAN 81 +#define TT_MAC_LANGID_TAGALOG 82 +#define TT_MAC_LANGID_MALAY_ROMAN_SCRIPT 83 +#define TT_MAC_LANGID_MALAY_ARABIC_SCRIPT 84 +#define TT_MAC_LANGID_AMHARIC 85 +#define TT_MAC_LANGID_TIGRINYA 86 +#define TT_MAC_LANGID_GALLA 87 +#define TT_MAC_LANGID_SOMALI 88 +#define TT_MAC_LANGID_SWAHILI 89 +#define TT_MAC_LANGID_RUANDA 90 +#define TT_MAC_LANGID_RUNDI 91 +#define TT_MAC_LANGID_CHEWA 92 +#define TT_MAC_LANGID_MALAGASY 93 +#define TT_MAC_LANGID_ESPERANTO 94 +#define TT_MAC_LANGID_WELSH 128 +#define TT_MAC_LANGID_BASQUE 129 +#define TT_MAC_LANGID_CATALAN 130 +#define TT_MAC_LANGID_LATIN 131 +#define TT_MAC_LANGID_QUECHUA 132 +#define TT_MAC_LANGID_GUARANI 133 +#define TT_MAC_LANGID_AYMARA 134 +#define TT_MAC_LANGID_TATAR 135 +#define TT_MAC_LANGID_UIGHUR 136 +#define TT_MAC_LANGID_DZONGKHA 137 +#define TT_MAC_LANGID_JAVANESE 138 +#define TT_MAC_LANGID_SUNDANESE 139 + + +#if 0 /* these seem to be errors that have been dropped */ + +#define TT_MAC_LANGID_SCOTTISH_GAELIC 140 +#define TT_MAC_LANGID_IRISH_GAELIC 141 + +#endif + + + /* The following codes are new as of 2000-03-10 */ +#define TT_MAC_LANGID_GALICIAN 140 +#define TT_MAC_LANGID_AFRIKAANS 141 +#define TT_MAC_LANGID_BRETON 142 +#define TT_MAC_LANGID_INUKTITUT 143 +#define TT_MAC_LANGID_SCOTTISH_GAELIC 144 +#define TT_MAC_LANGID_MANX_GAELIC 145 +#define TT_MAC_LANGID_IRISH_GAELIC 146 +#define TT_MAC_LANGID_TONGAN 147 +#define TT_MAC_LANGID_GREEK_POLYTONIC 148 +#define TT_MAC_LANGID_GREELANDIC 149 +#define TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT 150 + + + /*************************************************************************/ + /* */ + /* Possible values of the language identifier field in the name records */ + /* of the TTF `name' table if the `platform' identifier code is */ + /* TT_PLATFORM_MICROSOFT. */ + /* */ + /* The canonical source for the MS assigned LCIDs is */ + /* */ + /* http://www.microsoft.com/globaldev/reference/lcid-all.mspx */ + /* */ + +#define TT_MS_LANGID_ARABIC_GENERAL 0x0001 +#define TT_MS_LANGID_ARABIC_SAUDI_ARABIA 0x0401 +#define TT_MS_LANGID_ARABIC_IRAQ 0x0801 +#define TT_MS_LANGID_ARABIC_EGYPT 0x0C01 +#define TT_MS_LANGID_ARABIC_LIBYA 0x1001 +#define TT_MS_LANGID_ARABIC_ALGERIA 0x1401 +#define TT_MS_LANGID_ARABIC_MOROCCO 0x1801 +#define TT_MS_LANGID_ARABIC_TUNISIA 0x1C01 +#define TT_MS_LANGID_ARABIC_OMAN 0x2001 +#define TT_MS_LANGID_ARABIC_YEMEN 0x2401 +#define TT_MS_LANGID_ARABIC_SYRIA 0x2801 +#define TT_MS_LANGID_ARABIC_JORDAN 0x2C01 +#define TT_MS_LANGID_ARABIC_LEBANON 0x3001 +#define TT_MS_LANGID_ARABIC_KUWAIT 0x3401 +#define TT_MS_LANGID_ARABIC_UAE 0x3801 +#define TT_MS_LANGID_ARABIC_BAHRAIN 0x3C01 +#define TT_MS_LANGID_ARABIC_QATAR 0x4001 +#define TT_MS_LANGID_BULGARIAN_BULGARIA 0x0402 +#define TT_MS_LANGID_CATALAN_SPAIN 0x0403 +#define TT_MS_LANGID_CHINESE_GENERAL 0x0004 +#define TT_MS_LANGID_CHINESE_TAIWAN 0x0404 +#define TT_MS_LANGID_CHINESE_PRC 0x0804 +#define TT_MS_LANGID_CHINESE_HONG_KONG 0x0C04 +#define TT_MS_LANGID_CHINESE_SINGAPORE 0x1004 + +#if 1 /* this looks like the correct value */ +#define TT_MS_LANGID_CHINESE_MACAU 0x1404 +#else /* but beware, Microsoft may change its mind... + the most recent Word reference has the following: */ +#define TT_MS_LANGID_CHINESE_MACAU TT_MS_LANGID_CHINESE_HONG_KONG +#endif + +#if 0 /* used only with .NET `cultures'; commented out */ +#define TT_MS_LANGID_CHINESE_TRADITIONAL 0x7C04 +#endif + +#define TT_MS_LANGID_CZECH_CZECH_REPUBLIC 0x0405 +#define TT_MS_LANGID_DANISH_DENMARK 0x0406 +#define TT_MS_LANGID_GERMAN_GERMANY 0x0407 +#define TT_MS_LANGID_GERMAN_SWITZERLAND 0x0807 +#define TT_MS_LANGID_GERMAN_AUSTRIA 0x0C07 +#define TT_MS_LANGID_GERMAN_LUXEMBOURG 0x1007 +#define TT_MS_LANGID_GERMAN_LIECHTENSTEI 0x1407 +#define TT_MS_LANGID_GREEK_GREECE 0x0408 + + /* don't ask what this one means... It is commented out currently. */ +#if 0 +#define TT_MS_LANGID_GREEK_GREECE2 0x2008 +#endif + +#define TT_MS_LANGID_ENGLISH_GENERAL 0x0009 +#define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409 +#define TT_MS_LANGID_ENGLISH_UNITED_KINGDOM 0x0809 +#define TT_MS_LANGID_ENGLISH_AUSTRALIA 0x0C09 +#define TT_MS_LANGID_ENGLISH_CANADA 0x1009 +#define TT_MS_LANGID_ENGLISH_NEW_ZEALAND 0x1409 +#define TT_MS_LANGID_ENGLISH_IRELAND 0x1809 +#define TT_MS_LANGID_ENGLISH_SOUTH_AFRICA 0x1C09 +#define TT_MS_LANGID_ENGLISH_JAMAICA 0x2009 +#define TT_MS_LANGID_ENGLISH_CARIBBEAN 0x2409 +#define TT_MS_LANGID_ENGLISH_BELIZE 0x2809 +#define TT_MS_LANGID_ENGLISH_TRINIDAD 0x2C09 +#define TT_MS_LANGID_ENGLISH_ZIMBABWE 0x3009 +#define TT_MS_LANGID_ENGLISH_PHILIPPINES 0x3409 +#define TT_MS_LANGID_ENGLISH_INDONESIA 0x3809 +#define TT_MS_LANGID_ENGLISH_HONG_KONG 0x3C09 +#define TT_MS_LANGID_ENGLISH_INDIA 0x4009 +#define TT_MS_LANGID_ENGLISH_MALAYSIA 0x4409 +#define TT_MS_LANGID_ENGLISH_SINGAPORE 0x4809 +#define TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT 0x040A +#define TT_MS_LANGID_SPANISH_MEXICO 0x080A +#define TT_MS_LANGID_SPANISH_SPAIN_INTERNATIONAL_SORT 0x0C0A +#define TT_MS_LANGID_SPANISH_GUATEMALA 0x100A +#define TT_MS_LANGID_SPANISH_COSTA_RICA 0x140A +#define TT_MS_LANGID_SPANISH_PANAMA 0x180A +#define TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC 0x1C0A +#define TT_MS_LANGID_SPANISH_VENEZUELA 0x200A +#define TT_MS_LANGID_SPANISH_COLOMBIA 0x240A +#define TT_MS_LANGID_SPANISH_PERU 0x280A +#define TT_MS_LANGID_SPANISH_ARGENTINA 0x2C0A +#define TT_MS_LANGID_SPANISH_ECUADOR 0x300A +#define TT_MS_LANGID_SPANISH_CHILE 0x340A +#define TT_MS_LANGID_SPANISH_URUGUAY 0x380A +#define TT_MS_LANGID_SPANISH_PARAGUAY 0x3C0A +#define TT_MS_LANGID_SPANISH_BOLIVIA 0x400A +#define TT_MS_LANGID_SPANISH_EL_SALVADOR 0x440A +#define TT_MS_LANGID_SPANISH_HONDURAS 0x480A +#define TT_MS_LANGID_SPANISH_NICARAGUA 0x4C0A +#define TT_MS_LANGID_SPANISH_PUERTO_RICO 0x500A +#define TT_MS_LANGID_SPANISH_UNITED_STATES 0x540A + /* The following ID blatantly violate MS specs by using a */ + /* sublanguage > 0x1F. */ +#define TT_MS_LANGID_SPANISH_LATIN_AMERICA 0xE40AU +#define TT_MS_LANGID_FINNISH_FINLAND 0x040B +#define TT_MS_LANGID_FRENCH_FRANCE 0x040C +#define TT_MS_LANGID_FRENCH_BELGIUM 0x080C +#define TT_MS_LANGID_FRENCH_CANADA 0x0C0C +#define TT_MS_LANGID_FRENCH_SWITZERLAND 0x100C +#define TT_MS_LANGID_FRENCH_LUXEMBOURG 0x140C +#define TT_MS_LANGID_FRENCH_MONACO 0x180C +#define TT_MS_LANGID_FRENCH_WEST_INDIES 0x1C0C +#define TT_MS_LANGID_FRENCH_REUNION 0x200C +#define TT_MS_LANGID_FRENCH_CONGO 0x240C + /* which was formerly: */ +#define TT_MS_LANGID_FRENCH_ZAIRE TT_MS_LANGID_FRENCH_CONGO +#define TT_MS_LANGID_FRENCH_SENEGAL 0x280C +#define TT_MS_LANGID_FRENCH_CAMEROON 0x2C0C +#define TT_MS_LANGID_FRENCH_COTE_D_IVOIRE 0x300C +#define TT_MS_LANGID_FRENCH_MALI 0x340C +#define TT_MS_LANGID_FRENCH_MOROCCO 0x380C +#define TT_MS_LANGID_FRENCH_HAITI 0x3C0C + /* and another violation of the spec (see 0xE40AU) */ +#define TT_MS_LANGID_FRENCH_NORTH_AFRICA 0xE40CU +#define TT_MS_LANGID_HEBREW_ISRAEL 0x040D +#define TT_MS_LANGID_HUNGARIAN_HUNGARY 0x040E +#define TT_MS_LANGID_ICELANDIC_ICELAND 0x040F +#define TT_MS_LANGID_ITALIAN_ITALY 0x0410 +#define TT_MS_LANGID_ITALIAN_SWITZERLAND 0x0810 +#define TT_MS_LANGID_JAPANESE_JAPAN 0x0411 +#define TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA 0x0412 +#define TT_MS_LANGID_KOREAN_JOHAB_KOREA 0x0812 +#define TT_MS_LANGID_DUTCH_NETHERLANDS 0x0413 +#define TT_MS_LANGID_DUTCH_BELGIUM 0x0813 +#define TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL 0x0414 +#define TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK 0x0814 +#define TT_MS_LANGID_POLISH_POLAND 0x0415 +#define TT_MS_LANGID_PORTUGUESE_BRAZIL 0x0416 +#define TT_MS_LANGID_PORTUGUESE_PORTUGAL 0x0816 +#define TT_MS_LANGID_RHAETO_ROMANIC_SWITZERLAND 0x0417 +#define TT_MS_LANGID_ROMANIAN_ROMANIA 0x0418 +#define TT_MS_LANGID_MOLDAVIAN_MOLDAVIA 0x0818 +#define TT_MS_LANGID_RUSSIAN_RUSSIA 0x0419 +#define TT_MS_LANGID_RUSSIAN_MOLDAVIA 0x0819 +#define TT_MS_LANGID_CROATIAN_CROATIA 0x041A +#define TT_MS_LANGID_SERBIAN_SERBIA_LATIN 0x081A +#define TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC 0x0C1A + +#if 0 /* this used to be this value, but it looks like we were wrong */ +#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA 0x101A +#else /* current sources say */ +#define TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA 0x101A +#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA 0x141A + /* and XPsp2 Platform SDK added (2004-07-26) */ + /* Names are shortened to be significant within 40 chars. */ +#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN 0x181A +#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC 0x181A +#endif + +#define TT_MS_LANGID_SLOVAK_SLOVAKIA 0x041B +#define TT_MS_LANGID_ALBANIAN_ALBANIA 0x041C +#define TT_MS_LANGID_SWEDISH_SWEDEN 0x041D +#define TT_MS_LANGID_SWEDISH_FINLAND 0x081D +#define TT_MS_LANGID_THAI_THAILAND 0x041E +#define TT_MS_LANGID_TURKISH_TURKEY 0x041F +#define TT_MS_LANGID_URDU_PAKISTAN 0x0420 +#define TT_MS_LANGID_URDU_INDIA 0x0820 +#define TT_MS_LANGID_INDONESIAN_INDONESIA 0x0421 +#define TT_MS_LANGID_UKRAINIAN_UKRAINE 0x0422 +#define TT_MS_LANGID_BELARUSIAN_BELARUS 0x0423 +#define TT_MS_LANGID_SLOVENE_SLOVENIA 0x0424 +#define TT_MS_LANGID_ESTONIAN_ESTONIA 0x0425 +#define TT_MS_LANGID_LATVIAN_LATVIA 0x0426 +#define TT_MS_LANGID_LITHUANIAN_LITHUANIA 0x0427 +#define TT_MS_LANGID_CLASSIC_LITHUANIAN_LITHUANIA 0x0827 +#define TT_MS_LANGID_TAJIK_TAJIKISTAN 0x0428 +#define TT_MS_LANGID_FARSI_IRAN 0x0429 +#define TT_MS_LANGID_VIETNAMESE_VIET_NAM 0x042A +#define TT_MS_LANGID_ARMENIAN_ARMENIA 0x042B +#define TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN 0x042C +#define TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC 0x082C +#define TT_MS_LANGID_BASQUE_SPAIN 0x042D +#define TT_MS_LANGID_SORBIAN_GERMANY 0x042E +#define TT_MS_LANGID_MACEDONIAN_MACEDONIA 0x042F +#define TT_MS_LANGID_SUTU_SOUTH_AFRICA 0x0430 +#define TT_MS_LANGID_TSONGA_SOUTH_AFRICA 0x0431 +#define TT_MS_LANGID_TSWANA_SOUTH_AFRICA 0x0432 +#define TT_MS_LANGID_VENDA_SOUTH_AFRICA 0x0433 +#define TT_MS_LANGID_XHOSA_SOUTH_AFRICA 0x0434 +#define TT_MS_LANGID_ZULU_SOUTH_AFRICA 0x0435 +#define TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA 0x0436 +#define TT_MS_LANGID_GEORGIAN_GEORGIA 0x0437 +#define TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS 0x0438 +#define TT_MS_LANGID_HINDI_INDIA 0x0439 +#define TT_MS_LANGID_MALTESE_MALTA 0x043A + /* Added by XPsp2 Platform SDK (2004-07-26) */ +#define TT_MS_LANGID_SAMI_NORTHERN_NORWAY 0x043B +#define TT_MS_LANGID_SAMI_NORTHERN_SWEDEN 0x083B +#define TT_MS_LANGID_SAMI_NORTHERN_FINLAND 0x0C3B +#define TT_MS_LANGID_SAMI_LULE_NORWAY 0x103B +#define TT_MS_LANGID_SAMI_LULE_SWEDEN 0x143B +#define TT_MS_LANGID_SAMI_SOUTHERN_NORWAY 0x183B +#define TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN 0x1C3B +#define TT_MS_LANGID_SAMI_SKOLT_FINLAND 0x203B +#define TT_MS_LANGID_SAMI_INARI_FINLAND 0x243B + /* ... and we also keep our old identifier... */ +#define TT_MS_LANGID_SAAMI_LAPONIA 0x043B + +#if 0 /* this seems to be a previous inversion */ +#define TT_MS_LANGID_IRISH_GAELIC_IRELAND 0x043C +#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM 0x083C +#else +#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM 0x083C +#define TT_MS_LANGID_IRISH_GAELIC_IRELAND 0x043C +#endif + +#define TT_MS_LANGID_YIDDISH_GERMANY 0x043D +#define TT_MS_LANGID_MALAY_MALAYSIA 0x043E +#define TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM 0x083E +#define TT_MS_LANGID_KAZAK_KAZAKSTAN 0x043F +#define TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN /* Cyrillic*/ 0x0440 + /* alias declared in Windows 2000 */ +#define TT_MS_LANGID_KIRGHIZ_KIRGHIZ_REPUBLIC \ + TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN + +#define TT_MS_LANGID_SWAHILI_KENYA 0x0441 +#define TT_MS_LANGID_TURKMEN_TURKMENISTAN 0x0442 +#define TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN 0x0443 +#define TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC 0x0843 +#define TT_MS_LANGID_TATAR_TATARSTAN 0x0444 +#define TT_MS_LANGID_BENGALI_INDIA 0x0445 +#define TT_MS_LANGID_BENGALI_BANGLADESH 0x0845 +#define TT_MS_LANGID_PUNJABI_INDIA 0x0446 +#define TT_MS_LANGID_PUNJABI_ARABIC_PAKISTAN 0x0846 +#define TT_MS_LANGID_GUJARATI_INDIA 0x0447 +#define TT_MS_LANGID_ORIYA_INDIA 0x0448 +#define TT_MS_LANGID_TAMIL_INDIA 0x0449 +#define TT_MS_LANGID_TELUGU_INDIA 0x044A +#define TT_MS_LANGID_KANNADA_INDIA 0x044B +#define TT_MS_LANGID_MALAYALAM_INDIA 0x044C +#define TT_MS_LANGID_ASSAMESE_INDIA 0x044D +#define TT_MS_LANGID_MARATHI_INDIA 0x044E +#define TT_MS_LANGID_SANSKRIT_INDIA 0x044F +#define TT_MS_LANGID_MONGOLIAN_MONGOLIA /* Cyrillic */ 0x0450 +#define TT_MS_LANGID_MONGOLIAN_MONGOLIA_MONGOLIAN 0x0850 +#define TT_MS_LANGID_TIBETAN_CHINA 0x0451 + /* Don't use the next constant! It has */ + /* (1) the wrong spelling (Dzonghka) */ + /* (2) Microsoft doesn't officially define it -- */ + /* at least it is not in the List of Local */ + /* ID Values. */ + /* (3) Dzongkha is not the same language as */ + /* Tibetan, so merging it is wrong anyway. */ + /* */ + /* TT_MS_LANGID_TIBETAN_BHUTAN is correct, BTW. */ +#define TT_MS_LANGID_DZONGHKA_BHUTAN 0x0851 + +#if 0 + /* the following used to be defined */ +#define TT_MS_LANGID_TIBETAN_BHUTAN 0x0451 + /* ... but it was changed; */ +#else + /* So we will continue to #define it, but with the correct value */ +#define TT_MS_LANGID_TIBETAN_BHUTAN TT_MS_LANGID_DZONGHKA_BHUTAN +#endif + +#define TT_MS_LANGID_WELSH_WALES 0x0452 +#define TT_MS_LANGID_KHMER_CAMBODIA 0x0453 +#define TT_MS_LANGID_LAO_LAOS 0x0454 +#define TT_MS_LANGID_BURMESE_MYANMAR 0x0455 +#define TT_MS_LANGID_GALICIAN_SPAIN 0x0456 +#define TT_MS_LANGID_KONKANI_INDIA 0x0457 +#define TT_MS_LANGID_MANIPURI_INDIA /* Bengali */ 0x0458 +#define TT_MS_LANGID_SINDHI_INDIA /* Arabic */ 0x0459 +#define TT_MS_LANGID_SINDHI_PAKISTAN 0x0859 + /* Missing a LCID for Sindhi in Devanagari script */ +#define TT_MS_LANGID_SYRIAC_SYRIA 0x045A +#define TT_MS_LANGID_SINHALESE_SRI_LANKA 0x045B +#define TT_MS_LANGID_CHEROKEE_UNITED_STATES 0x045C +#define TT_MS_LANGID_INUKTITUT_CANADA 0x045D +#define TT_MS_LANGID_AMHARIC_ETHIOPIA 0x045E +#define TT_MS_LANGID_TAMAZIGHT_MOROCCO /* Arabic */ 0x045F +#define TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN 0x085F + /* Missing a LCID for Tifinagh script */ +#define TT_MS_LANGID_KASHMIRI_PAKISTAN /* Arabic */ 0x0460 + /* Spelled this way by XPsp2 Platform SDK (2004-07-26) */ + /* script is yet unclear... might be Arabic, Nagari or Sharada */ +#define TT_MS_LANGID_KASHMIRI_SASIA 0x0860 + /* ... and aliased (by MS) for compatibility reasons. */ +#define TT_MS_LANGID_KASHMIRI_INDIA TT_MS_LANGID_KASHMIRI_SASIA +#define TT_MS_LANGID_NEPALI_NEPAL 0x0461 +#define TT_MS_LANGID_NEPALI_INDIA 0x0861 +#define TT_MS_LANGID_FRISIAN_NETHERLANDS 0x0462 +#define TT_MS_LANGID_PASHTO_AFGHANISTAN 0x0463 +#define TT_MS_LANGID_FILIPINO_PHILIPPINES 0x0464 +#define TT_MS_LANGID_DHIVEHI_MALDIVES 0x0465 + /* alias declared in Windows 2000 */ +#define TT_MS_LANGID_DIVEHI_MALDIVES TT_MS_LANGID_DHIVEHI_MALDIVES +#define TT_MS_LANGID_EDO_NIGERIA 0x0466 +#define TT_MS_LANGID_FULFULDE_NIGERIA 0x0467 +#define TT_MS_LANGID_HAUSA_NIGERIA 0x0468 +#define TT_MS_LANGID_IBIBIO_NIGERIA 0x0469 +#define TT_MS_LANGID_YORUBA_NIGERIA 0x046A +#define TT_MS_LANGID_QUECHUA_BOLIVIA 0x046B +#define TT_MS_LANGID_QUECHUA_ECUADOR 0x086B +#define TT_MS_LANGID_QUECHUA_PERU 0x0C6B +#define TT_MS_LANGID_SEPEDI_SOUTH_AFRICA 0x046C + /* Also spelled by XPsp2 Platform SDK (2004-07-26) */ +#define TT_MS_LANGID_SOTHO_SOUTHERN_SOUTH_AFRICA \ + TT_MS_LANGID_SEPEDI_SOUTH_AFRICA + /* language codes 0x046D, 0x046E and 0x046F are (still) unknown. */ +#define TT_MS_LANGID_IGBO_NIGERIA 0x0470 +#define TT_MS_LANGID_KANURI_NIGERIA 0x0471 +#define TT_MS_LANGID_OROMO_ETHIOPIA 0x0472 +#define TT_MS_LANGID_TIGRIGNA_ETHIOPIA 0x0473 +#define TT_MS_LANGID_TIGRIGNA_ERYTHREA 0x0873 + /* also spelled in the `Passport SDK' list as: */ +#define TT_MS_LANGID_TIGRIGNA_ERYTREA TT_MS_LANGID_TIGRIGNA_ERYTHREA +#define TT_MS_LANGID_GUARANI_PARAGUAY 0x0474 +#define TT_MS_LANGID_HAWAIIAN_UNITED_STATES 0x0475 +#define TT_MS_LANGID_LATIN 0x0476 +#define TT_MS_LANGID_SOMALI_SOMALIA 0x0477 + /* Note: Yi does not have a (proper) ISO 639-2 code, since it is mostly */ + /* not written (but OTOH the peculiar writing system is worth */ + /* studying). */ +#define TT_MS_LANGID_YI_CHINA 0x0478 +#define TT_MS_LANGID_PAPIAMENTU_NETHERLANDS_ANTILLES 0x0479 + /* language codes from 0x047A to 0x047F are (still) unknown. */ +#define TT_MS_LANGID_UIGHUR_CHINA 0x0480 +#define TT_MS_LANGID_MAORI_NEW_ZEALAND 0x0481 + +#if 0 /* not deemed useful for fonts */ +#define TT_MS_LANGID_HUMAN_INTERFACE_DEVICE 0x04FF +#endif + + + /*************************************************************************/ + /* */ + /* Possible values of the `name' identifier field in the name records of */ + /* the TTF `name' table. These values are platform independent. */ + /* */ +#define TT_NAME_ID_COPYRIGHT 0 +#define TT_NAME_ID_FONT_FAMILY 1 +#define TT_NAME_ID_FONT_SUBFAMILY 2 +#define TT_NAME_ID_UNIQUE_ID 3 +#define TT_NAME_ID_FULL_NAME 4 +#define TT_NAME_ID_VERSION_STRING 5 +#define TT_NAME_ID_PS_NAME 6 +#define TT_NAME_ID_TRADEMARK 7 + + /* the following values are from the OpenType spec */ +#define TT_NAME_ID_MANUFACTURER 8 +#define TT_NAME_ID_DESIGNER 9 +#define TT_NAME_ID_DESCRIPTION 10 +#define TT_NAME_ID_VENDOR_URL 11 +#define TT_NAME_ID_DESIGNER_URL 12 +#define TT_NAME_ID_LICENSE 13 +#define TT_NAME_ID_LICENSE_URL 14 + /* number 15 is reserved */ +#define TT_NAME_ID_PREFERRED_FAMILY 16 +#define TT_NAME_ID_PREFERRED_SUBFAMILY 17 +#define TT_NAME_ID_MAC_FULL_NAME 18 + + /* The following code is new as of 2000-01-21 */ +#define TT_NAME_ID_SAMPLE_TEXT 19 + + /* This is new in OpenType 1.3 */ +#define TT_NAME_ID_CID_FINDFONT_NAME 20 + + /* This is new in OpenType 1.5 */ +#define TT_NAME_ID_WWS_FAMILY 21 +#define TT_NAME_ID_WWS_SUBFAMILY 22 + + + /*************************************************************************/ + /* */ + /* Bit mask values for the Unicode Ranges from the TTF `OS2 ' table. */ + /* */ + /* Updated 08-Nov-2008. */ + /* */ + + /* Bit 0 Basic Latin */ +#define TT_UCR_BASIC_LATIN (1L << 0) /* U+0020-U+007E */ + /* Bit 1 C1 Controls and Latin-1 Supplement */ +#define TT_UCR_LATIN1_SUPPLEMENT (1L << 1) /* U+0080-U+00FF */ + /* Bit 2 Latin Extended-A */ +#define TT_UCR_LATIN_EXTENDED_A (1L << 2) /* U+0100-U+017F */ + /* Bit 3 Latin Extended-B */ +#define TT_UCR_LATIN_EXTENDED_B (1L << 3) /* U+0180-U+024F */ + /* Bit 4 IPA Extensions */ + /* Phonetic Extensions */ + /* Phonetic Extensions Supplement */ +#define TT_UCR_IPA_EXTENSIONS (1L << 4) /* U+0250-U+02AF */ + /* U+1D00-U+1D7F */ + /* U+1D80-U+1DBF */ + /* Bit 5 Spacing Modifier Letters */ + /* Modifier Tone Letters */ +#define TT_UCR_SPACING_MODIFIER (1L << 5) /* U+02B0-U+02FF */ + /* U+A700-U+A71F */ + /* Bit 6 Combining Diacritical Marks */ + /* Combining Diacritical Marks Supplement */ +#define TT_UCR_COMBINING_DIACRITICS (1L << 6) /* U+0300-U+036F */ + /* U+1DC0-U+1DFF */ + /* Bit 7 Greek and Coptic */ +#define TT_UCR_GREEK (1L << 7) /* U+0370-U+03FF */ + /* Bit 8 Coptic */ +#define TT_UCR_COPTIC (1L << 8) /* U+2C80-U+2CFF */ + /* Bit 9 Cyrillic */ + /* Cyrillic Supplement */ + /* Cyrillic Extended-A */ + /* Cyrillic Extended-B */ +#define TT_UCR_CYRILLIC (1L << 9) /* U+0400-U+04FF */ + /* U+0500-U+052F */ + /* U+2DE0-U+2DFF */ + /* U+A640-U+A69F */ + /* Bit 10 Armenian */ +#define TT_UCR_ARMENIAN (1L << 10) /* U+0530-U+058F */ + /* Bit 11 Hebrew */ +#define TT_UCR_HEBREW (1L << 11) /* U+0590-U+05FF */ + /* Bit 12 Vai */ +#define TT_UCR_VAI (1L << 12) /* U+A500-U+A63F */ + /* Bit 13 Arabic */ + /* Arabic Supplement */ +#define TT_UCR_ARABIC (1L << 13) /* U+0600-U+06FF */ + /* U+0750-U+077F */ + /* Bit 14 NKo */ +#define TT_UCR_NKO (1L << 14) /* U+07C0-U+07FF */ + /* Bit 15 Devanagari */ +#define TT_UCR_DEVANAGARI (1L << 15) /* U+0900-U+097F */ + /* Bit 16 Bengali */ +#define TT_UCR_BENGALI (1L << 16) /* U+0980-U+09FF */ + /* Bit 17 Gurmukhi */ +#define TT_UCR_GURMUKHI (1L << 17) /* U+0A00-U+0A7F */ + /* Bit 18 Gujarati */ +#define TT_UCR_GUJARATI (1L << 18) /* U+0A80-U+0AFF */ + /* Bit 19 Oriya */ +#define TT_UCR_ORIYA (1L << 19) /* U+0B00-U+0B7F */ + /* Bit 20 Tamil */ +#define TT_UCR_TAMIL (1L << 20) /* U+0B80-U+0BFF */ + /* Bit 21 Telugu */ +#define TT_UCR_TELUGU (1L << 21) /* U+0C00-U+0C7F */ + /* Bit 22 Kannada */ +#define TT_UCR_KANNADA (1L << 22) /* U+0C80-U+0CFF */ + /* Bit 23 Malayalam */ +#define TT_UCR_MALAYALAM (1L << 23) /* U+0D00-U+0D7F */ + /* Bit 24 Thai */ +#define TT_UCR_THAI (1L << 24) /* U+0E00-U+0E7F */ + /* Bit 25 Lao */ +#define TT_UCR_LAO (1L << 25) /* U+0E80-U+0EFF */ + /* Bit 26 Georgian */ + /* Georgian Supplement */ +#define TT_UCR_GEORGIAN (1L << 26) /* U+10A0-U+10FF */ + /* U+2D00-U+2D2F */ + /* Bit 27 Balinese */ +#define TT_UCR_BALINESE (1L << 27) /* U+1B00-U+1B7F */ + /* Bit 28 Hangul Jamo */ +#define TT_UCR_HANGUL_JAMO (1L << 28) /* U+1100-U+11FF */ + /* Bit 29 Latin Extended Additional */ + /* Latin Extended-C */ + /* Latin Extended-D */ +#define TT_UCR_LATIN_EXTENDED_ADDITIONAL (1L << 29) /* U+1E00-U+1EFF */ + /* U+2C60-U+2C7F */ + /* U+A720-U+A7FF */ + /* Bit 30 Greek Extended */ +#define TT_UCR_GREEK_EXTENDED (1L << 30) /* U+1F00-U+1FFF */ + /* Bit 31 General Punctuation */ + /* Supplemental Punctuation */ +#define TT_UCR_GENERAL_PUNCTUATION (1L << 31) /* U+2000-U+206F */ + /* U+2E00-U+2E7F */ + /* Bit 32 Superscripts And Subscripts */ +#define TT_UCR_SUPERSCRIPTS_SUBSCRIPTS (1L << 0) /* U+2070-U+209F */ + /* Bit 33 Currency Symbols */ +#define TT_UCR_CURRENCY_SYMBOLS (1L << 1) /* U+20A0-U+20CF */ + /* Bit 34 Combining Diacritical Marks For Symbols */ +#define TT_UCR_COMBINING_DIACRITICS_SYMB (1L << 2) /* U+20D0-U+20FF */ + /* Bit 35 Letterlike Symbols */ +#define TT_UCR_LETTERLIKE_SYMBOLS (1L << 3) /* U+2100-U+214F */ + /* Bit 36 Number Forms */ +#define TT_UCR_NUMBER_FORMS (1L << 4) /* U+2150-U+218F */ + /* Bit 37 Arrows */ + /* Supplemental Arrows-A */ + /* Supplemental Arrows-B */ + /* Miscellaneous Symbols and Arrows */ +#define TT_UCR_ARROWS (1L << 5) /* U+2190-U+21FF */ + /* U+27F0-U+27FF */ + /* U+2900-U+297F */ + /* U+2B00-U+2BFF */ + /* Bit 38 Mathematical Operators */ + /* Supplemental Mathematical Operators */ + /* Miscellaneous Mathematical Symbols-A */ + /* Miscellaneous Mathematical Symbols-B */ +#define TT_UCR_MATHEMATICAL_OPERATORS (1L << 6) /* U+2200-U+22FF */ + /* U+2A00-U+2AFF */ + /* U+27C0-U+27EF */ + /* U+2980-U+29FF */ + /* Bit 39 Miscellaneous Technical */ +#define TT_UCR_MISCELLANEOUS_TECHNICAL (1L << 7) /* U+2300-U+23FF */ + /* Bit 40 Control Pictures */ +#define TT_UCR_CONTROL_PICTURES (1L << 8) /* U+2400-U+243F */ + /* Bit 41 Optical Character Recognition */ +#define TT_UCR_OCR (1L << 9) /* U+2440-U+245F */ + /* Bit 42 Enclosed Alphanumerics */ +#define TT_UCR_ENCLOSED_ALPHANUMERICS (1L << 10) /* U+2460-U+24FF */ + /* Bit 43 Box Drawing */ +#define TT_UCR_BOX_DRAWING (1L << 11) /* U+2500-U+257F */ + /* Bit 44 Block Elements */ +#define TT_UCR_BLOCK_ELEMENTS (1L << 12) /* U+2580-U+259F */ + /* Bit 45 Geometric Shapes */ +#define TT_UCR_GEOMETRIC_SHAPES (1L << 13) /* U+25A0-U+25FF */ + /* Bit 46 Miscellaneous Symbols */ +#define TT_UCR_MISCELLANEOUS_SYMBOLS (1L << 14) /* U+2600-U+26FF */ + /* Bit 47 Dingbats */ +#define TT_UCR_DINGBATS (1L << 15) /* U+2700-U+27BF */ + /* Bit 48 CJK Symbols and Punctuation */ +#define TT_UCR_CJK_SYMBOLS (1L << 16) /* U+3000-U+303F */ + /* Bit 49 Hiragana */ +#define TT_UCR_HIRAGANA (1L << 17) /* U+3040-U+309F */ + /* Bit 50 Katakana */ + /* Katakana Phonetic Extensions */ +#define TT_UCR_KATAKANA (1L << 18) /* U+30A0-U+30FF */ + /* U+31F0-U+31FF */ + /* Bit 51 Bopomofo */ + /* Bopomofo Extended */ +#define TT_UCR_BOPOMOFO (1L << 19) /* U+3100-U+312F */ + /* U+31A0-U+31BF */ + /* Bit 52 Hangul Compatibility Jamo */ +#define TT_UCR_HANGUL_COMPATIBILITY_JAMO (1L << 20) /* U+3130-U+318F */ + /* Bit 53 Phags-Pa */ +#define TT_UCR_CJK_MISC (1L << 21) /* U+A840-U+A87F */ +#define TT_UCR_KANBUN TT_UCR_CJK_MISC /* deprecated */ +#define TT_UCR_PHAGSPA + /* Bit 54 Enclosed CJK Letters and Months */ +#define TT_UCR_ENCLOSED_CJK_LETTERS_MONTHS (1L << 22) /* U+3200-U+32FF */ + /* Bit 55 CJK Compatibility */ +#define TT_UCR_CJK_COMPATIBILITY (1L << 23) /* U+3300-U+33FF */ + /* Bit 56 Hangul Syllables */ +#define TT_UCR_HANGUL (1L << 24) /* U+AC00-U+D7A3 */ + /* Bit 57 High Surrogates */ + /* High Private Use Surrogates */ + /* Low Surrogates */ + /* */ + /* According to OpenType specs v.1.3+, */ + /* setting bit 57 implies that there is */ + /* at least one codepoint beyond the */ + /* Basic Multilingual Plane that is */ + /* supported by this font. So it really */ + /* means >= U+10000 */ +#define TT_UCR_SURROGATES (1L << 25) /* U+D800-U+DB7F */ + /* U+DB80-U+DBFF */ + /* U+DC00-U+DFFF */ +#define TT_UCR_NON_PLANE_0 TT_UCR_SURROGATES + /* Bit 58 Phoenician */ +#define TT_UCR_PHOENICIAN (1L << 26) /*U+10900-U+1091F*/ + /* Bit 59 CJK Unified Ideographs */ + /* CJK Radicals Supplement */ + /* Kangxi Radicals */ + /* Ideographic Description Characters */ + /* CJK Unified Ideographs Extension A */ + /* CJK Unified Ideographs Extension B */ + /* Kanbun */ +#define TT_UCR_CJK_UNIFIED_IDEOGRAPHS (1L << 27) /* U+4E00-U+9FFF */ + /* U+2E80-U+2EFF */ + /* U+2F00-U+2FDF */ + /* U+2FF0-U+2FFF */ + /* U+3400-U+4DB5 */ + /*U+20000-U+2A6DF*/ + /* U+3190-U+319F */ + /* Bit 60 Private Use */ +#define TT_UCR_PRIVATE_USE (1L << 28) /* U+E000-U+F8FF */ + /* Bit 61 CJK Strokes */ + /* CJK Compatibility Ideographs */ + /* CJK Compatibility Ideographs Supplement */ +#define TT_UCR_CJK_COMPATIBILITY_IDEOGRAPHS (1L << 29) /* U+31C0-U+31EF */ + /* U+F900-U+FAFF */ + /*U+2F800-U+2FA1F*/ + /* Bit 62 Alphabetic Presentation Forms */ +#define TT_UCR_ALPHABETIC_PRESENTATION_FORMS (1L << 30) /* U+FB00-U+FB4F */ + /* Bit 63 Arabic Presentation Forms-A */ +#define TT_UCR_ARABIC_PRESENTATIONS_A (1L << 31) /* U+FB50-U+FDFF */ + /* Bit 64 Combining Half Marks */ +#define TT_UCR_COMBINING_HALF_MARKS (1L << 0) /* U+FE20-U+FE2F */ + /* Bit 65 Vertical forms */ + /* CJK Compatibility Forms */ +#define TT_UCR_CJK_COMPATIBILITY_FORMS (1L << 1) /* U+FE10-U+FE1F */ + /* U+FE30-U+FE4F */ + /* Bit 66 Small Form Variants */ +#define TT_UCR_SMALL_FORM_VARIANTS (1L << 2) /* U+FE50-U+FE6F */ + /* Bit 67 Arabic Presentation Forms-B */ +#define TT_UCR_ARABIC_PRESENTATIONS_B (1L << 3) /* U+FE70-U+FEFE */ + /* Bit 68 Halfwidth and Fullwidth Forms */ +#define TT_UCR_HALFWIDTH_FULLWIDTH_FORMS (1L << 4) /* U+FF00-U+FFEF */ + /* Bit 69 Specials */ +#define TT_UCR_SPECIALS (1L << 5) /* U+FFF0-U+FFFD */ + /* Bit 70 Tibetan */ +#define TT_UCR_TIBETAN (1L << 6) /* U+0F00-U+0FFF */ + /* Bit 71 Syriac */ +#define TT_UCR_SYRIAC (1L << 7) /* U+0700-U+074F */ + /* Bit 72 Thaana */ +#define TT_UCR_THAANA (1L << 8) /* U+0780-U+07BF */ + /* Bit 73 Sinhala */ +#define TT_UCR_SINHALA (1L << 9) /* U+0D80-U+0DFF */ + /* Bit 74 Myanmar */ +#define TT_UCR_MYANMAR (1L << 10) /* U+1000-U+109F */ + /* Bit 75 Ethiopic */ + /* Ethiopic Supplement */ + /* Ethiopic Extended */ +#define TT_UCR_ETHIOPIC (1L << 11) /* U+1200-U+137F */ + /* U+1380-U+139F */ + /* U+2D80-U+2DDF */ + /* Bit 76 Cherokee */ +#define TT_UCR_CHEROKEE (1L << 12) /* U+13A0-U+13FF */ + /* Bit 77 Unified Canadian Aboriginal Syllabics */ +#define TT_UCR_CANADIAN_ABORIGINAL_SYLLABICS (1L << 13) /* U+1400-U+167F */ + /* Bit 78 Ogham */ +#define TT_UCR_OGHAM (1L << 14) /* U+1680-U+169F */ + /* Bit 79 Runic */ +#define TT_UCR_RUNIC (1L << 15) /* U+16A0-U+16FF */ + /* Bit 80 Khmer */ + /* Khmer Symbols */ +#define TT_UCR_KHMER (1L << 16) /* U+1780-U+17FF */ + /* U+19E0-U+19FF */ + /* Bit 81 Mongolian */ +#define TT_UCR_MONGOLIAN (1L << 17) /* U+1800-U+18AF */ + /* Bit 82 Braille Patterns */ +#define TT_UCR_BRAILLE (1L << 18) /* U+2800-U+28FF */ + /* Bit 83 Yi Syllables */ + /* Yi Radicals */ +#define TT_UCR_YI (1L << 19) /* U+A000-U+A48F */ + /* U+A490-U+A4CF */ + /* Bit 84 Tagalog */ + /* Hanunoo */ + /* Buhid */ + /* Tagbanwa */ +#define TT_UCR_PHILIPPINE (1L << 20) /* U+1700-U+171F */ + /* U+1720-U+173F */ + /* U+1740-U+175F */ + /* U+1760-U+177F */ + /* Bit 85 Old Italic */ +#define TT_UCR_OLD_ITALIC (1L << 21) /*U+10300-U+1032F*/ + /* Bit 86 Gothic */ +#define TT_UCR_GOTHIC (1L << 22) /*U+10330-U+1034F*/ + /* Bit 87 Deseret */ +#define TT_UCR_DESERET (1L << 23) /*U+10400-U+1044F*/ + /* Bit 88 Byzantine Musical Symbols */ + /* Musical Symbols */ + /* Ancient Greek Musical Notation */ +#define TT_UCR_MUSICAL_SYMBOLS (1L << 24) /*U+1D000-U+1D0FF*/ + /*U+1D100-U+1D1FF*/ + /*U+1D200-U+1D24F*/ + /* Bit 89 Mathematical Alphanumeric Symbols */ +#define TT_UCR_MATH_ALPHANUMERIC_SYMBOLS (1L << 25) /*U+1D400-U+1D7FF*/ + /* Bit 90 Private Use (plane 15) */ + /* Private Use (plane 16) */ +#define TT_UCR_PRIVATE_USE_SUPPLEMENTARY (1L << 26) /*U+F0000-U+FFFFD*/ + /*U+100000-U+10FFFD*/ + /* Bit 91 Variation Selectors */ + /* Variation Selectors Supplement */ +#define TT_UCR_VARIATION_SELECTORS (1L << 27) /* U+FE00-U+FE0F */ + /*U+E0100-U+E01EF*/ + /* Bit 92 Tags */ +#define TT_UCR_TAGS (1L << 28) /*U+E0000-U+E007F*/ + /* Bit 93 Limbu */ +#define TT_UCR_LIMBU (1L << 29) /* U+1900-U+194F */ + /* Bit 94 Tai Le */ +#define TT_UCR_TAI_LE (1L << 30) /* U+1950-U+197F */ + /* Bit 95 New Tai Lue */ +#define TT_UCR_NEW_TAI_LUE (1L << 31) /* U+1980-U+19DF */ + /* Bit 96 Buginese */ +#define TT_UCR_BUGINESE (1L << 0) /* U+1A00-U+1A1F */ + /* Bit 97 Glagolitic */ +#define TT_UCR_GLAGOLITIC (1L << 1) /* U+2C00-U+2C5F */ + /* Bit 98 Tifinagh */ +#define TT_UCR_TIFINAGH (1L << 2) /* U+2D30-U+2D7F */ + /* Bit 99 Yijing Hexagram Symbols */ +#define TT_UCR_YIJING (1L << 3) /* U+4DC0-U+4DFF */ + /* Bit 100 Syloti Nagri */ +#define TT_UCR_SYLOTI_NAGRI (1L << 4) /* U+A800-U+A82F */ + /* Bit 101 Linear B Syllabary */ + /* Linear B Ideograms */ + /* Aegean Numbers */ +#define TT_UCR_LINEAR_B (1L << 5) /*U+10000-U+1007F*/ + /*U+10080-U+100FF*/ + /*U+10100-U+1013F*/ + /* Bit 102 Ancient Greek Numbers */ +#define TT_UCR_ANCIENT_GREEK_NUMBERS (1L << 6) /*U+10140-U+1018F*/ + /* Bit 103 Ugaritic */ +#define TT_UCR_UGARITIC (1L << 7) /*U+10380-U+1039F*/ + /* Bit 104 Old Persian */ +#define TT_UCR_OLD_PERSIAN (1L << 8) /*U+103A0-U+103DF*/ + /* Bit 105 Shavian */ +#define TT_UCR_SHAVIAN (1L << 9) /*U+10450-U+1047F*/ + /* Bit 106 Osmanya */ +#define TT_UCR_OSMANYA (1L << 10) /*U+10480-U+104AF*/ + /* Bit 107 Cypriot Syllabary */ +#define TT_UCR_CYPRIOT_SYLLABARY (1L << 11) /*U+10800-U+1083F*/ + /* Bit 108 Kharoshthi */ +#define TT_UCR_KHAROSHTHI (1L << 12) /*U+10A00-U+10A5F*/ + /* Bit 109 Tai Xuan Jing Symbols */ +#define TT_UCR_TAI_XUAN_JING (1L << 13) /*U+1D300-U+1D35F*/ + /* Bit 110 Cuneiform */ + /* Cuneiform Numbers and Punctuation */ +#define TT_UCR_CUNEIFORM (1L << 14) /*U+12000-U+123FF*/ + /*U+12400-U+1247F*/ + /* Bit 111 Counting Rod Numerals */ +#define TT_UCR_COUNTING_ROD_NUMERALS (1L << 15) /*U+1D360-U+1D37F*/ + /* Bit 112 Sundanese */ +#define TT_UCR_SUNDANESE (1L << 16) /* U+1B80-U+1BBF */ + /* Bit 113 Lepcha */ +#define TT_UCR_LEPCHA (1L << 17) /* U+1C00-U+1C4F */ + /* Bit 114 Ol Chiki */ +#define TT_UCR_OL_CHIKI (1L << 18) /* U+1C50-U+1C7F */ + /* Bit 115 Saurashtra */ +#define TT_UCR_SAURASHTRA (1L << 19) /* U+A880-U+A8DF */ + /* Bit 116 Kayah Li */ +#define TT_UCR_KAYAH_LI (1L << 20) /* U+A900-U+A92F */ + /* Bit 117 Rejang */ +#define TT_UCR_REJANG (1L << 21) /* U+A930-U+A95F */ + /* Bit 118 Cham */ +#define TT_UCR_CHAM (1L << 22) /* U+AA00-U+AA5F */ + /* Bit 119 Ancient Symbols */ +#define TT_UCR_ANCIENT_SYMBOLS (1L << 23) /*U+10190-U+101CF*/ + /* Bit 120 Phaistos Disc */ +#define TT_UCR_PHAISTOS_DISC (1L << 24) /*U+101D0-U+101FF*/ + /* Bit 121 Carian */ + /* Lycian */ + /* Lydian */ +#define TT_UCR_OLD_ANATOLIAN (1L << 25) /*U+102A0-U+102DF*/ + /*U+10280-U+1029F*/ + /*U+10920-U+1093F*/ + /* Bit 122 Domino Tiles */ + /* Mahjong Tiles */ +#define TT_UCR_GAME_TILES (1L << 26) /*U+1F030-U+1F09F*/ + /*U+1F000-U+1F02F*/ + /* Bit 123-127 Reserved for process-internal usage */ + + + /*************************************************************************/ + /* */ + /* Some compilers have a very limited length of identifiers. */ + /* */ +#if defined( __TURBOC__ ) && __TURBOC__ < 0x0410 || defined( __PACIFIC__ ) +#define HAVE_LIMIT_ON_IDENTS +#endif + + +#ifndef HAVE_LIMIT_ON_IDENTS + + + /*************************************************************************/ + /* */ + /* Here some alias #defines in order to be clearer. */ + /* */ + /* These are not always #defined to stay within the 31~character limit, */ + /* which some compilers have. */ + /* */ + /* Credits go to Dave Hoo <dhoo@flash.net> for pointing out that modern */ + /* Borland compilers (read: from BC++ 3.1 on) can increase this limit. */ + /* If you get a warning with such a compiler, use the -i40 switch. */ + /* */ +#define TT_UCR_ARABIC_PRESENTATION_FORMS_A \ + TT_UCR_ARABIC_PRESENTATIONS_A +#define TT_UCR_ARABIC_PRESENTATION_FORMS_B \ + TT_UCR_ARABIC_PRESENTATIONS_B + +#define TT_UCR_COMBINING_DIACRITICAL_MARKS \ + TT_UCR_COMBINING_DIACRITICS +#define TT_UCR_COMBINING_DIACRITICAL_MARKS_SYMB \ + TT_UCR_COMBINING_DIACRITICS_SYMB + + +#endif /* !HAVE_LIMIT_ON_IDENTS */ + + +FT_END_HEADER + +#endif /* TTNAMEID_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/tttables.h b/foreign/freetype2/freetype/tttables.h new file mode 100644 index 0000000..dfe3bcb --- /dev/null +++ b/foreign/freetype2/freetype/tttables.h @@ -0,0 +1,829 @@ +/***************************************************************************/ +/* */ +/* tttables.h */ +/* */ +/* Basic SFNT/TrueType tables definitions and interface */ +/* (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTTABLES_H_ +#define TTTABLES_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* <Section> */ + /* truetype_tables */ + /* */ + /* <Title> */ + /* TrueType Tables */ + /* */ + /* <Abstract> */ + /* TrueType specific table types and functions. */ + /* */ + /* <Description> */ + /* This section contains the definition of TrueType-specific tables */ + /* as well as some routines used to access and process them. */ + /* */ + /* <Order> */ + /* TT_Header */ + /* TT_HoriHeader */ + /* TT_VertHeader */ + /* TT_OS2 */ + /* TT_Postscript */ + /* TT_PCLT */ + /* TT_MaxProfile */ + /* */ + /* FT_Sfnt_Tag */ + /* FT_Get_Sfnt_Table */ + /* FT_Load_Sfnt_Table */ + /* FT_Sfnt_Table_Info */ + /* */ + /* FT_Get_CMap_Language_ID */ + /* FT_Get_CMap_Format */ + /* */ + /* FT_PARAM_TAG_UNPATENTED_HINTING */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Header */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType font header table. All */ + /* fields follow the TrueType specification. */ + /* */ + typedef struct TT_Header_ + { + FT_Fixed Table_Version; + FT_Fixed Font_Revision; + + FT_Long CheckSum_Adjust; + FT_Long Magic_Number; + + FT_UShort Flags; + FT_UShort Units_Per_EM; + + FT_Long Created [2]; + FT_Long Modified[2]; + + FT_Short xMin; + FT_Short yMin; + FT_Short xMax; + FT_Short yMax; + + FT_UShort Mac_Style; + FT_UShort Lowest_Rec_PPEM; + + FT_Short Font_Direction; + FT_Short Index_To_Loc_Format; + FT_Short Glyph_Data_Format; + + } TT_Header; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_HoriHeader */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType horizontal header, the `hhea' */ + /* table, as well as the corresponding horizontal metrics table, */ + /* i.e., the `hmtx' table. */ + /* */ + /* <Fields> */ + /* Version :: The table version. */ + /* */ + /* Ascender :: The font's ascender, i.e., the distance */ + /* from the baseline to the top-most of all */ + /* glyph points found in the font. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of the */ + /* glyphs found in the font (maybe ASCII). */ + /* */ + /* You should use the `sTypoAscender' field */ + /* of the OS/2 table instead if you want */ + /* the correct one. */ + /* */ + /* Descender :: The font's descender, i.e., the distance */ + /* from the baseline to the bottom-most of */ + /* all glyph points found in the font. It */ + /* is negative. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of the */ + /* glyphs found in the font (maybe ASCII). */ + /* */ + /* You should use the `sTypoDescender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Line_Gap :: The font's line gap, i.e., the distance */ + /* to add to the ascender and descender to */ + /* get the BTB, i.e., the */ + /* baseline-to-baseline distance for the */ + /* font. */ + /* */ + /* advance_Width_Max :: This field is the maximum of all advance */ + /* widths found in the font. It can be */ + /* used to compute the maximum width of an */ + /* arbitrary string of text. */ + /* */ + /* min_Left_Side_Bearing :: The minimum left side bearing of all */ + /* glyphs within the font. */ + /* */ + /* min_Right_Side_Bearing :: The minimum right side bearing of all */ + /* glyphs within the font. */ + /* */ + /* xMax_Extent :: The maximum horizontal extent (i.e., the */ + /* `width' of a glyph's bounding box) for */ + /* all glyphs in the font. */ + /* */ + /* caret_Slope_Rise :: The rise coefficient of the cursor's */ + /* slope of the cursor (slope=rise/run). */ + /* */ + /* caret_Slope_Run :: The run coefficient of the cursor's */ + /* slope. */ + /* */ + /* Reserved :: 8~reserved bytes. */ + /* */ + /* metric_Data_Format :: Always~0. */ + /* */ + /* number_Of_HMetrics :: Number of HMetrics entries in the `hmtx' */ + /* table -- this value can be smaller than */ + /* the total number of glyphs in the font. */ + /* */ + /* long_metrics :: A pointer into the `hmtx' table. */ + /* */ + /* short_metrics :: A pointer into the `hmtx' table. */ + /* */ + /* <Note> */ + /* IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should */ + /* be identical except for the names of their fields, */ + /* which are different. */ + /* */ + /* This ensures that a single function in the `ttload' */ + /* module is able to read both the horizontal and vertical */ + /* headers. */ + /* */ + typedef struct TT_HoriHeader_ + { + FT_Fixed Version; + FT_Short Ascender; + FT_Short Descender; + FT_Short Line_Gap; + + FT_UShort advance_Width_Max; /* advance width maximum */ + + FT_Short min_Left_Side_Bearing; /* minimum left-sb */ + FT_Short min_Right_Side_Bearing; /* minimum right-sb */ + FT_Short xMax_Extent; /* xmax extents */ + FT_Short caret_Slope_Rise; + FT_Short caret_Slope_Run; + FT_Short caret_Offset; + + FT_Short Reserved[4]; + + FT_Short metric_Data_Format; + FT_UShort number_Of_HMetrics; + + /* The following fields are not defined by the TrueType specification */ + /* but they are used to connect the metrics header to the relevant */ + /* `HMTX' table. */ + + void* long_metrics; + void* short_metrics; + + } TT_HoriHeader; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_VertHeader */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType vertical header, the `vhea' */ + /* table, as well as the corresponding vertical metrics table, i.e., */ + /* the `vmtx' table. */ + /* */ + /* <Fields> */ + /* Version :: The table version. */ + /* */ + /* Ascender :: The font's ascender, i.e., the distance */ + /* from the baseline to the top-most of */ + /* all glyph points found in the font. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of */ + /* the glyphs found in the font (maybe */ + /* ASCII). */ + /* */ + /* You should use the `sTypoAscender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Descender :: The font's descender, i.e., the */ + /* distance from the baseline to the */ + /* bottom-most of all glyph points found */ + /* in the font. It is negative. */ + /* */ + /* This value is invalid in many fonts, as */ + /* it is usually set by the font designer, */ + /* and often reflects only a portion of */ + /* the glyphs found in the font (maybe */ + /* ASCII). */ + /* */ + /* You should use the `sTypoDescender' */ + /* field of the OS/2 table instead if you */ + /* want the correct one. */ + /* */ + /* Line_Gap :: The font's line gap, i.e., the distance */ + /* to add to the ascender and descender to */ + /* get the BTB, i.e., the */ + /* baseline-to-baseline distance for the */ + /* font. */ + /* */ + /* advance_Height_Max :: This field is the maximum of all */ + /* advance heights found in the font. It */ + /* can be used to compute the maximum */ + /* height of an arbitrary string of text. */ + /* */ + /* min_Top_Side_Bearing :: The minimum top side bearing of all */ + /* glyphs within the font. */ + /* */ + /* min_Bottom_Side_Bearing :: The minimum bottom side bearing of all */ + /* glyphs within the font. */ + /* */ + /* yMax_Extent :: The maximum vertical extent (i.e., the */ + /* `height' of a glyph's bounding box) for */ + /* all glyphs in the font. */ + /* */ + /* caret_Slope_Rise :: The rise coefficient of the cursor's */ + /* slope of the cursor (slope=rise/run). */ + /* */ + /* caret_Slope_Run :: The run coefficient of the cursor's */ + /* slope. */ + /* */ + /* caret_Offset :: The cursor's offset for slanted fonts. */ + /* This value is `reserved' in vmtx */ + /* version 1.0. */ + /* */ + /* Reserved :: 8~reserved bytes. */ + /* */ + /* metric_Data_Format :: Always~0. */ + /* */ + /* number_Of_HMetrics :: Number of VMetrics entries in the */ + /* `vmtx' table -- this value can be */ + /* smaller than the total number of glyphs */ + /* in the font. */ + /* */ + /* long_metrics :: A pointer into the `vmtx' table. */ + /* */ + /* short_metrics :: A pointer into the `vmtx' table. */ + /* */ + /* <Note> */ + /* IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should */ + /* be identical except for the names of their fields, */ + /* which are different. */ + /* */ + /* This ensures that a single function in the `ttload' */ + /* module is able to read both the horizontal and vertical */ + /* headers. */ + /* */ + typedef struct TT_VertHeader_ + { + FT_Fixed Version; + FT_Short Ascender; + FT_Short Descender; + FT_Short Line_Gap; + + FT_UShort advance_Height_Max; /* advance height maximum */ + + FT_Short min_Top_Side_Bearing; /* minimum left-sb or top-sb */ + FT_Short min_Bottom_Side_Bearing; /* minimum right-sb or bottom-sb */ + FT_Short yMax_Extent; /* xmax or ymax extents */ + FT_Short caret_Slope_Rise; + FT_Short caret_Slope_Run; + FT_Short caret_Offset; + + FT_Short Reserved[4]; + + FT_Short metric_Data_Format; + FT_UShort number_Of_VMetrics; + + /* The following fields are not defined by the TrueType specification */ + /* but they're used to connect the metrics header to the relevant */ + /* `HMTX' or `VMTX' table. */ + + void* long_metrics; + void* short_metrics; + + } TT_VertHeader; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_OS2 */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType OS/2 table. All fields */ + /* comply to the OpenType specification. */ + /* */ + /* Note that we now support old Mac fonts that do not include an OS/2 */ + /* table. In this case, the `version' field is always set to 0xFFFF. */ + /* */ + typedef struct TT_OS2_ + { + FT_UShort version; /* 0x0001 - more or 0xFFFF */ + FT_Short xAvgCharWidth; + FT_UShort usWeightClass; + FT_UShort usWidthClass; + FT_UShort fsType; + FT_Short ySubscriptXSize; + FT_Short ySubscriptYSize; + FT_Short ySubscriptXOffset; + FT_Short ySubscriptYOffset; + FT_Short ySuperscriptXSize; + FT_Short ySuperscriptYSize; + FT_Short ySuperscriptXOffset; + FT_Short ySuperscriptYOffset; + FT_Short yStrikeoutSize; + FT_Short yStrikeoutPosition; + FT_Short sFamilyClass; + + FT_Byte panose[10]; + + FT_ULong ulUnicodeRange1; /* Bits 0-31 */ + FT_ULong ulUnicodeRange2; /* Bits 32-63 */ + FT_ULong ulUnicodeRange3; /* Bits 64-95 */ + FT_ULong ulUnicodeRange4; /* Bits 96-127 */ + + FT_Char achVendID[4]; + + FT_UShort fsSelection; + FT_UShort usFirstCharIndex; + FT_UShort usLastCharIndex; + FT_Short sTypoAscender; + FT_Short sTypoDescender; + FT_Short sTypoLineGap; + FT_UShort usWinAscent; + FT_UShort usWinDescent; + + /* only version 1 and higher: */ + + FT_ULong ulCodePageRange1; /* Bits 0-31 */ + FT_ULong ulCodePageRange2; /* Bits 32-63 */ + + /* only version 2 and higher: */ + + FT_Short sxHeight; + FT_Short sCapHeight; + FT_UShort usDefaultChar; + FT_UShort usBreakChar; + FT_UShort usMaxContext; + + /* only version 5 and higher: */ + + FT_UShort usLowerOpticalPointSize; /* in twips (1/20th points) */ + FT_UShort usUpperOpticalPointSize; /* in twips (1/20th points) */ + + } TT_OS2; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_Postscript */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType PostScript table. All fields */ + /* comply to the TrueType specification. This structure does not */ + /* reference the PostScript glyph names, which can be nevertheless */ + /* accessed with the `ttpost' module. */ + /* */ + typedef struct TT_Postscript_ + { + FT_Fixed FormatType; + FT_Fixed italicAngle; + FT_Short underlinePosition; + FT_Short underlineThickness; + FT_ULong isFixedPitch; + FT_ULong minMemType42; + FT_ULong maxMemType42; + FT_ULong minMemType1; + FT_ULong maxMemType1; + + /* Glyph names follow in the file, but we don't */ + /* load them by default. See the ttpost.c file. */ + + } TT_Postscript; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_PCLT */ + /* */ + /* <Description> */ + /* A structure used to model a TrueType PCLT table. All fields */ + /* comply to the TrueType specification. */ + /* */ + typedef struct TT_PCLT_ + { + FT_Fixed Version; + FT_ULong FontNumber; + FT_UShort Pitch; + FT_UShort xHeight; + FT_UShort Style; + FT_UShort TypeFamily; + FT_UShort CapHeight; + FT_UShort SymbolSet; + FT_Char TypeFace[16]; + FT_Char CharacterComplement[8]; + FT_Char FileName[6]; + FT_Char StrokeWeight; + FT_Char WidthType; + FT_Byte SerifStyle; + FT_Byte Reserved; + + } TT_PCLT; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* TT_MaxProfile */ + /* */ + /* <Description> */ + /* The maximum profile is a table containing many max values, which */ + /* can be used to pre-allocate arrays. This ensures that no memory */ + /* allocation occurs during a glyph load. */ + /* */ + /* <Fields> */ + /* version :: The version number. */ + /* */ + /* numGlyphs :: The number of glyphs in this TrueType */ + /* font. */ + /* */ + /* maxPoints :: The maximum number of points in a */ + /* non-composite TrueType glyph. See also */ + /* the structure element */ + /* `maxCompositePoints'. */ + /* */ + /* maxContours :: The maximum number of contours in a */ + /* non-composite TrueType glyph. See also */ + /* the structure element */ + /* `maxCompositeContours'. */ + /* */ + /* maxCompositePoints :: The maximum number of points in a */ + /* composite TrueType glyph. See also the */ + /* structure element `maxPoints'. */ + /* */ + /* maxCompositeContours :: The maximum number of contours in a */ + /* composite TrueType glyph. See also the */ + /* structure element `maxContours'. */ + /* */ + /* maxZones :: The maximum number of zones used for */ + /* glyph hinting. */ + /* */ + /* maxTwilightPoints :: The maximum number of points in the */ + /* twilight zone used for glyph hinting. */ + /* */ + /* maxStorage :: The maximum number of elements in the */ + /* storage area used for glyph hinting. */ + /* */ + /* maxFunctionDefs :: The maximum number of function */ + /* definitions in the TrueType bytecode for */ + /* this font. */ + /* */ + /* maxInstructionDefs :: The maximum number of instruction */ + /* definitions in the TrueType bytecode for */ + /* this font. */ + /* */ + /* maxStackElements :: The maximum number of stack elements used */ + /* during bytecode interpretation. */ + /* */ + /* maxSizeOfInstructions :: The maximum number of TrueType opcodes */ + /* used for glyph hinting. */ + /* */ + /* maxComponentElements :: The maximum number of simple (i.e., non- */ + /* composite) glyphs in a composite glyph. */ + /* */ + /* maxComponentDepth :: The maximum nesting depth of composite */ + /* glyphs. */ + /* */ + /* <Note> */ + /* This structure is only used during font loading. */ + /* */ + typedef struct TT_MaxProfile_ + { + FT_Fixed version; + FT_UShort numGlyphs; + FT_UShort maxPoints; + FT_UShort maxContours; + FT_UShort maxCompositePoints; + FT_UShort maxCompositeContours; + FT_UShort maxZones; + FT_UShort maxTwilightPoints; + FT_UShort maxStorage; + FT_UShort maxFunctionDefs; + FT_UShort maxInstructionDefs; + FT_UShort maxStackElements; + FT_UShort maxSizeOfInstructions; + FT_UShort maxComponentElements; + FT_UShort maxComponentDepth; + + } TT_MaxProfile; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Sfnt_Tag */ + /* */ + /* <Description> */ + /* An enumeration used to specify the index of an SFNT table. */ + /* Used in the @FT_Get_Sfnt_Table API function. */ + /* */ + /* <Values> */ + /* FT_SFNT_HEAD :: To access the font's @TT_Header structure. */ + /* */ + /* FT_SFNT_MAXP :: To access the font's @TT_MaxProfile structure. */ + /* */ + /* FT_SFNT_OS2 :: To access the font's @TT_OS2 structure. */ + /* */ + /* FT_SFNT_HHEA :: To access the font's @TT_HoriHeader structure. */ + /* */ + /* FT_SFNT_VHEA :: To access the font's @TT_VertHeader struture. */ + /* */ + /* FT_SFNT_POST :: To access the font's @TT_Postscript structure. */ + /* */ + /* FT_SFNT_PCLT :: To access the font's @TT_PCLT structure. */ + /* */ + typedef enum FT_Sfnt_Tag_ + { + FT_SFNT_HEAD, + FT_SFNT_MAXP, + FT_SFNT_OS2, + FT_SFNT_HHEA, + FT_SFNT_VHEA, + FT_SFNT_POST, + FT_SFNT_PCLT, + + FT_SFNT_MAX + + } FT_Sfnt_Tag; + + /* these constants are deprecated; use the corresponding `FT_Sfnt_Tag' */ + /* values instead */ +#define ft_sfnt_head FT_SFNT_HEAD +#define ft_sfnt_maxp FT_SFNT_MAXP +#define ft_sfnt_os2 FT_SFNT_OS2 +#define ft_sfnt_hhea FT_SFNT_HHEA +#define ft_sfnt_vhea FT_SFNT_VHEA +#define ft_sfnt_post FT_SFNT_POST +#define ft_sfnt_pclt FT_SFNT_PCLT + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Sfnt_Table */ + /* */ + /* <Description> */ + /* Return a pointer to a given SFNT table within a face. */ + /* */ + /* <Input> */ + /* face :: A handle to the source. */ + /* */ + /* tag :: The index of the SFNT table. */ + /* */ + /* <Return> */ + /* A type-less pointer to the table. This will be~0 in case of */ + /* error, or if the corresponding table was not found *OR* loaded */ + /* from the file. */ + /* */ + /* Use a typecast according to `tag' to access the structure */ + /* elements. */ + /* */ + /* <Note> */ + /* The table is owned by the face object and disappears with it. */ + /* */ + /* This function is only useful to access SFNT tables that are loaded */ + /* by the sfnt, truetype, and opentype drivers. See @FT_Sfnt_Tag for */ + /* a list. */ + /* */ + /* Here an example how to access the `vhea' table: */ + /* */ + /* { */ + /* TT_VertHeader* vert_header; */ + /* */ + /* */ + /* vert_header = */ + /* (TT_VertHeader*)FT_Get_Sfnt_Table( face, FT_SFNT_VHEA ); */ + /* } */ + /* */ + FT_EXPORT( void* ) + FT_Get_Sfnt_Table( FT_Face face, + FT_Sfnt_Tag tag ); + + + /************************************************************************** + * + * @function: + * FT_Load_Sfnt_Table + * + * @description: + * Load any font table into client memory. + * + * @input: + * face :: + * A handle to the source face. + * + * tag :: + * The four-byte tag of the table to load. Use the value~0 if you want + * to access the whole font file. Otherwise, you can use one of the + * definitions found in the @FT_TRUETYPE_TAGS_H file, or forge a new + * one with @FT_MAKE_TAG. + * + * offset :: + * The starting offset in the table (or file if tag == 0). + * + * @output: + * buffer :: + * The target buffer address. The client must ensure that the memory + * array is big enough to hold the data. + * + * @inout: + * length :: + * If the `length' parameter is NULL, then try to load the whole table. + * Return an error code if it fails. + * + * Else, if `*length' is~0, exit immediately while returning the + * table's (or file) full size in it. + * + * Else the number of bytes to read from the table or file, from the + * starting offset. + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If you need to determine the table's length you should first call this + * function with `*length' set to~0, as in the following example: + * + * { + * FT_ULong length = 0; + * + * + * error = FT_Load_Sfnt_Table( face, tag, 0, NULL, &length ); + * if ( error ) { ... table does not exist ... } + * + * buffer = malloc( length ); + * if ( buffer == NULL ) { ... not enough memory ... } + * + * error = FT_Load_Sfnt_Table( face, tag, 0, buffer, &length ); + * if ( error ) { ... could not load table ... } + * } + * + * Note that structures like @TT_Header or @TT_OS2 can't be used with + * this function; they are limited to @FT_Get_Sfnt_Table. Reason is that + * those structures depend on the processor architecture, with varying + * size (e.g. 32bit vs. 64bit) or order (big endian vs. little endian). + * + */ + FT_EXPORT( FT_Error ) + FT_Load_Sfnt_Table( FT_Face face, + FT_ULong tag, + FT_Long offset, + FT_Byte* buffer, + FT_ULong* length ); + + + /************************************************************************** + * + * @function: + * FT_Sfnt_Table_Info + * + * @description: + * Return information on an SFNT table. + * + * @input: + * face :: + * A handle to the source face. + * + * table_index :: + * The index of an SFNT table. The function returns + * FT_Err_Table_Missing for an invalid value. + * + * @inout: + * tag :: + * The name tag of the SFNT table. If the value is NULL, `table_index' + * is ignored, and `length' returns the number of SFNT tables in the + * font. + * + * @output: + * length :: + * The length of the SFNT table (or the number of SFNT tables, depending + * on `tag'). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * While parsing fonts, FreeType handles SFNT tables with length zero as + * missing. + * + */ + FT_EXPORT( FT_Error ) + FT_Sfnt_Table_Info( FT_Face face, + FT_UInt table_index, + FT_ULong *tag, + FT_ULong *length ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_CMap_Language_ID */ + /* */ + /* <Description> */ + /* Return TrueType/sfnt specific cmap language ID. Definitions of */ + /* language ID values are in `ttnameid.h'. */ + /* */ + /* <Input> */ + /* charmap :: */ + /* The target charmap. */ + /* */ + /* <Return> */ + /* The language ID of `charmap'. If `charmap' doesn't belong to a */ + /* TrueType/sfnt face, just return~0 as the default value. */ + /* */ + /* For a format~14 cmap (to access Unicode IVS), the return value is */ + /* 0xFFFFFFFF. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_CMap_Language_ID( FT_CharMap charmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_CMap_Format */ + /* */ + /* <Description> */ + /* Return TrueType/sfnt specific cmap format. */ + /* */ + /* <Input> */ + /* charmap :: */ + /* The target charmap. */ + /* */ + /* <Return> */ + /* The format of `charmap'. If `charmap' doesn't belong to a */ + /* TrueType/sfnt face, return -1. */ + /* */ + FT_EXPORT( FT_Long ) + FT_Get_CMap_Format( FT_CharMap charmap ); + + /* */ + + +FT_END_HEADER + +#endif /* TTTABLES_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/tttags.h b/foreign/freetype2/freetype/tttags.h new file mode 100644 index 0000000..f3c9aa5 --- /dev/null +++ b/foreign/freetype2/freetype/tttags.h @@ -0,0 +1,111 @@ +/***************************************************************************/ +/* */ +/* tttags.h */ +/* */ +/* Tags for TrueType and OpenType tables (specification only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTAGS_H_ +#define TTAGS_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + +#define TTAG_avar FT_MAKE_TAG( 'a', 'v', 'a', 'r' ) +#define TTAG_BASE FT_MAKE_TAG( 'B', 'A', 'S', 'E' ) +#define TTAG_bdat FT_MAKE_TAG( 'b', 'd', 'a', 't' ) +#define TTAG_BDF FT_MAKE_TAG( 'B', 'D', 'F', ' ' ) +#define TTAG_bhed FT_MAKE_TAG( 'b', 'h', 'e', 'd' ) +#define TTAG_bloc FT_MAKE_TAG( 'b', 'l', 'o', 'c' ) +#define TTAG_bsln FT_MAKE_TAG( 'b', 's', 'l', 'n' ) +#define TTAG_CBDT FT_MAKE_TAG( 'C', 'B', 'D', 'T' ) +#define TTAG_CBLC FT_MAKE_TAG( 'C', 'B', 'L', 'C' ) +#define TTAG_CFF FT_MAKE_TAG( 'C', 'F', 'F', ' ' ) +#define TTAG_CID FT_MAKE_TAG( 'C', 'I', 'D', ' ' ) +#define TTAG_cmap FT_MAKE_TAG( 'c', 'm', 'a', 'p' ) +#define TTAG_cvar FT_MAKE_TAG( 'c', 'v', 'a', 'r' ) +#define TTAG_cvt FT_MAKE_TAG( 'c', 'v', 't', ' ' ) +#define TTAG_DSIG FT_MAKE_TAG( 'D', 'S', 'I', 'G' ) +#define TTAG_EBDT FT_MAKE_TAG( 'E', 'B', 'D', 'T' ) +#define TTAG_EBLC FT_MAKE_TAG( 'E', 'B', 'L', 'C' ) +#define TTAG_EBSC FT_MAKE_TAG( 'E', 'B', 'S', 'C' ) +#define TTAG_feat FT_MAKE_TAG( 'f', 'e', 'a', 't' ) +#define TTAG_FOND FT_MAKE_TAG( 'F', 'O', 'N', 'D' ) +#define TTAG_fpgm FT_MAKE_TAG( 'f', 'p', 'g', 'm' ) +#define TTAG_fvar FT_MAKE_TAG( 'f', 'v', 'a', 'r' ) +#define TTAG_gasp FT_MAKE_TAG( 'g', 'a', 's', 'p' ) +#define TTAG_GDEF FT_MAKE_TAG( 'G', 'D', 'E', 'F' ) +#define TTAG_glyf FT_MAKE_TAG( 'g', 'l', 'y', 'f' ) +#define TTAG_GPOS FT_MAKE_TAG( 'G', 'P', 'O', 'S' ) +#define TTAG_GSUB FT_MAKE_TAG( 'G', 'S', 'U', 'B' ) +#define TTAG_gvar FT_MAKE_TAG( 'g', 'v', 'a', 'r' ) +#define TTAG_hdmx FT_MAKE_TAG( 'h', 'd', 'm', 'x' ) +#define TTAG_head FT_MAKE_TAG( 'h', 'e', 'a', 'd' ) +#define TTAG_hhea FT_MAKE_TAG( 'h', 'h', 'e', 'a' ) +#define TTAG_hmtx FT_MAKE_TAG( 'h', 'm', 't', 'x' ) +#define TTAG_JSTF FT_MAKE_TAG( 'J', 'S', 'T', 'F' ) +#define TTAG_just FT_MAKE_TAG( 'j', 'u', 's', 't' ) +#define TTAG_kern FT_MAKE_TAG( 'k', 'e', 'r', 'n' ) +#define TTAG_lcar FT_MAKE_TAG( 'l', 'c', 'a', 'r' ) +#define TTAG_loca FT_MAKE_TAG( 'l', 'o', 'c', 'a' ) +#define TTAG_LTSH FT_MAKE_TAG( 'L', 'T', 'S', 'H' ) +#define TTAG_LWFN FT_MAKE_TAG( 'L', 'W', 'F', 'N' ) +#define TTAG_MATH FT_MAKE_TAG( 'M', 'A', 'T', 'H' ) +#define TTAG_maxp FT_MAKE_TAG( 'm', 'a', 'x', 'p' ) +#define TTAG_META FT_MAKE_TAG( 'M', 'E', 'T', 'A' ) +#define TTAG_MMFX FT_MAKE_TAG( 'M', 'M', 'F', 'X' ) +#define TTAG_MMSD FT_MAKE_TAG( 'M', 'M', 'S', 'D' ) +#define TTAG_mort FT_MAKE_TAG( 'm', 'o', 'r', 't' ) +#define TTAG_morx FT_MAKE_TAG( 'm', 'o', 'r', 'x' ) +#define TTAG_name FT_MAKE_TAG( 'n', 'a', 'm', 'e' ) +#define TTAG_opbd FT_MAKE_TAG( 'o', 'p', 'b', 'd' ) +#define TTAG_OS2 FT_MAKE_TAG( 'O', 'S', '/', '2' ) +#define TTAG_OTTO FT_MAKE_TAG( 'O', 'T', 'T', 'O' ) +#define TTAG_PCLT FT_MAKE_TAG( 'P', 'C', 'L', 'T' ) +#define TTAG_POST FT_MAKE_TAG( 'P', 'O', 'S', 'T' ) +#define TTAG_post FT_MAKE_TAG( 'p', 'o', 's', 't' ) +#define TTAG_prep FT_MAKE_TAG( 'p', 'r', 'e', 'p' ) +#define TTAG_prop FT_MAKE_TAG( 'p', 'r', 'o', 'p' ) +#define TTAG_sbix FT_MAKE_TAG( 's', 'b', 'i', 'x' ) +#define TTAG_sfnt FT_MAKE_TAG( 's', 'f', 'n', 't' ) +#define TTAG_SING FT_MAKE_TAG( 'S', 'I', 'N', 'G' ) +#define TTAG_trak FT_MAKE_TAG( 't', 'r', 'a', 'k' ) +#define TTAG_true FT_MAKE_TAG( 't', 'r', 'u', 'e' ) +#define TTAG_ttc FT_MAKE_TAG( 't', 't', 'c', ' ' ) +#define TTAG_ttcf FT_MAKE_TAG( 't', 't', 'c', 'f' ) +#define TTAG_TYP1 FT_MAKE_TAG( 'T', 'Y', 'P', '1' ) +#define TTAG_typ1 FT_MAKE_TAG( 't', 'y', 'p', '1' ) +#define TTAG_VDMX FT_MAKE_TAG( 'V', 'D', 'M', 'X' ) +#define TTAG_vhea FT_MAKE_TAG( 'v', 'h', 'e', 'a' ) +#define TTAG_vmtx FT_MAKE_TAG( 'v', 'm', 't', 'x' ) +#define TTAG_wOFF FT_MAKE_TAG( 'w', 'O', 'F', 'F' ) + + +FT_END_HEADER + +#endif /* TTAGS_H_ */ + + +/* END */ diff --git a/foreign/freetype2/freetype/ttunpat.h b/foreign/freetype2/freetype/ttunpat.h new file mode 100644 index 0000000..ca4676b --- /dev/null +++ b/foreign/freetype2/freetype/ttunpat.h @@ -0,0 +1,63 @@ +/***************************************************************************/ +/* */ +/* ttunpat.h */ +/* */ +/* Definitions for the unpatented TrueType hinting system. */ +/* Obsolete, retained for backwards compatibility. */ +/* */ +/* Copyright 2003-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* Written by Graham Asher <graham.asher@btinternet.com> */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef TTUNPAT_H_ +#define TTUNPAT_H_ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************** + * + * @constant: + * FT_PARAM_TAG_UNPATENTED_HINTING + * + * @description: + * Deprecated. + * + * Previously: A constant used as the tag of an @FT_Parameter structure to + * indicate that unpatented methods only should be used by the TrueType + * bytecode interpreter for a typeface opened by @FT_Open_Face. + * + */ +#define FT_PARAM_TAG_UNPATENTED_HINTING FT_MAKE_TAG( 'u', 'n', 'p', 'a' ) + + /* */ + + +FT_END_HEADER + + +#endif /* TTUNPAT_H_ */ + + +/* END */ diff --git a/foreign/freetype2/ft2build.h b/foreign/freetype2/ft2build.h new file mode 100644 index 0000000..c89cb46 --- /dev/null +++ b/foreign/freetype2/ft2build.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* ft2build.h */ +/* */ +/* FreeType 2 build and setup macros. */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This is the `entry point' for FreeType header file inclusions. It is */ + /* the only header file which should be included directly; all other */ + /* FreeType header files should be accessed with macro names (after */ + /* including `ft2build.h'). */ + /* */ + /* A typical example is */ + /* */ + /* #include <ft2build.h> */ + /* #include FT_FREETYPE_H */ + /* */ + /*************************************************************************/ + + +#ifndef FT2BUILD_H_ +#define FT2BUILD_H_ + +#include <freetype/config/ftheader.h> + +#endif /* FT2BUILD_H_ */ + + +/* END */ diff --git a/foreign/gl/glext.h b/foreign/gl/glext.h new file mode 100644 index 0000000..c6a233a --- /dev/null +++ b/foreign/gl/glext.h @@ -0,0 +1,11124 @@ +#ifndef __glext_h_ +#define __glext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ +/* +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** http://www.opengl.org/registry/ +** +** Khronos $Revision: 24756 $ on $Date: 2014-01-14 03:42:29 -0800 (Tue, 14 Jan 2014) $ +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include <windows.h> +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +#define GL_GLEXT_VERSION 20140114 + +/* Generated C header for: + * API: gl + * Profile: compatibility + * Versions considered: .* + * Versions emitted: 1\.[2-9]|[234]\.[0-9] + * Default extensions included: gl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); +GLAPI void APIENTRY glClientActiveTexture (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1f (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1i (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1s (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2d (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2i (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2s (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3d (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3f (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3i (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3s (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4d (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4i (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4s (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFogCoordf (GLfloat coord); +GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); +GLAPI void APIENTRY glFogCoordd (GLdouble coord); +GLAPI void APIENTRY glFogCoorddv (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointer (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glSecondaryColor3b (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3d (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3f (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3i (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3iv (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3s (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ub (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3ui (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3us (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointer (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glWindowPos2d (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2f (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2i (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2iv (const GLint *v); +GLAPI void APIENTRY glWindowPos2s (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2sv (const GLshort *v); +GLAPI void APIENTRY glWindowPos3d (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3f (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3i (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3iv (const GLint *v); +GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +#include <stddef.h> +typedef ptrdiff_t GLsizeiptr; +typedef ptrdiff_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC2_ALPHA 0x858A +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +typedef char GLchar; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +typedef unsigned short GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glxext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GL_EXT_timer_query extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include <inttypes.h> +#elif defined(__sun__) || defined(__digital__) +#include <inttypes.h> +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include <inttypes.h> +#elif defined(__SCO__) || defined(__USLC__) +#include <stdint.h> +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && defined(__GNUC__) +#include <stdint.h> +#elif defined(_WIN32) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include <inttypes.h> +#endif +#endif +typedef uint64_t GLuint64; +typedef int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLfloat value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); +GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_DISPLAY_LIST 0x82E7 +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); +GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 +typedef uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +struct _cl_context; +struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClampColorARB (GLenum target, GLenum clamp); +#endif +#endif /* GL_ARB_color_buffer_float */ + +#ifndef GL_ARB_compatibility +#define GL_ARB_compatibility 1 +#endif /* GL_ARB_compatibility */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif /* GL_ARB_depth_texture */ + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersARB (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ARB_draw_buffers */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, void *string); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramStringARB (GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glBindProgramARB (GLenum target, GLuint program); +GLAPI void APIENTRY glDeleteProgramsARB (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glGenProgramsARB (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, void *string); +GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program); +#endif +#endif /* GL_ARB_fragment_program */ + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif /* GL_ARB_fragment_program_shadow */ + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif /* GL_ARB_fragment_shader */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 +typedef unsigned short GLhalfARB; +#define GL_HALF_FLOAT_ARB 0x140B +#endif /* GL_ARB_half_float_pixel */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTable (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTable (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glColorSubTable (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTable (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glConvolutionFilter1D (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterf (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteri (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilter (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilter (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +GLAPI void APIENTRY glGetHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogram (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogram (GLenum target); +GLAPI void APIENTRY glResetMinmax (GLenum target); +#endif +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); +typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint index); +GLAPI void APIENTRY glMatrixIndexubvARB (GLint size, const GLubyte *indices); +GLAPI void APIENTRY glMatrixIndexusvARB (GLint size, const GLushort *indices); +GLAPI void APIENTRY glMatrixIndexuivARB (GLint size, const GLuint *indices); +GLAPI void APIENTRY glMatrixIndexPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_ARB_matrix_palette */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLfloat value, GLboolean invert); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleCoverageARB (GLfloat value, GLboolean invert); +#endif +#endif /* GL_ARB_multisample */ + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glClientActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum target, const GLshort *v); +#endif +#endif /* GL_ARB_multitexture */ + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); +GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQueryARB (GLenum target); +GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_ARB_occlusion_query */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfARB (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_ARB_point_parameters */ + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif /* GL_ARB_point_sprite */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC) (GLenum map, GLsizei bufSize, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC) (GLenum map, GLsizei bufSize, GLuint *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC) (GLenum map, GLsizei bufSize, GLushort *values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC) (GLsizei bufSize, GLubyte *pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnMapdvARB (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +GLAPI void APIENTRY glGetnMapfvARB (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glGetnMapivARB (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +GLAPI void APIENTRY glGetnPixelMapfvARB (GLenum map, GLsizei bufSize, GLfloat *values); +GLAPI void APIENTRY glGetnPixelMapuivARB (GLenum map, GLsizei bufSize, GLuint *values); +GLAPI void APIENTRY glGetnPixelMapusvARB (GLenum map, GLsizei bufSize, GLushort *values); +GLAPI void APIENTRY glGetnPolygonStippleARB (GLsizei bufSize, GLubyte *pattern); +GLAPI void APIENTRY glGetnColorTableARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +GLAPI void APIENTRY glGetnConvolutionFilterARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +GLAPI void APIENTRY glGetnSeparableFilterARB (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +GLAPI void APIENTRY glGetnHistogramARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glGetnMinmaxARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef char GLcharARB; +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj); +GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname); +GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj); +GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType); +GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj); +GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); +GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj); +GLAPI void APIENTRY glLinkProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB programObj); +GLAPI void APIENTRY glValidateProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUniform1fARB (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2fARB (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1iARB (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2iARB (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3iARB (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4iARB (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB obj, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB obj, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB programObj, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB programObj, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformivARB (GLhandleARB programObj, GLint location, GLint *params); +GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif +#endif /* GL_ARB_shader_objects */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_lod +#define GL_ARB_shader_texture_lod 1 +#endif /* GL_ARB_shader_texture_lod */ + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif /* GL_ARB_shading_language_100 */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif /* GL_ARB_shadow */ + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif /* GL_ARB_shadow_ambient */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_MIN_SPARSE_LEVEL_ARB 0x919B +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, void *img); +#endif +#endif /* GL_ARB_texture_compression */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif /* GL_ARB_texture_cube_map */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif /* GL_ARB_texture_env_add */ + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif /* GL_ARB_texture_env_combine */ + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif /* GL_ARB_texture_env_crossbar */ + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif /* GL_ARB_texture_env_dot3 */ + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif /* GL_ARB_texture_float */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif /* GL_ARB_texture_rectangle */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *m); +#endif +#endif /* GL_ARB_transpose_matrix */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); +typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); +typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); +typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWeightbvARB (GLint size, const GLbyte *weights); +GLAPI void APIENTRY glWeightsvARB (GLint size, const GLshort *weights); +GLAPI void APIENTRY glWeightivARB (GLint size, const GLint *weights); +GLAPI void APIENTRY glWeightfvARB (GLint size, const GLfloat *weights); +GLAPI void APIENTRY glWeightdvARB (GLint size, const GLdouble *weights); +GLAPI void APIENTRY glWeightubvARB (GLint size, const GLubyte *weights); +GLAPI void APIENTRY glWeightusvARB (GLint size, const GLushort *weights); +GLAPI void APIENTRY glWeightuivARB (GLint size, const GLuint *weights); +GLAPI void APIENTRY glWeightPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexBlendARB (GLint count); +#endif +#endif /* GL_ARB_vertex_blend */ + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +typedef ptrdiff_t GLsizeiptrARB; +typedef ptrdiff_t GLintptrARB; +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindBufferARB (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffersARB (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffersARB (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer); +GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +GLAPI void *APIENTRY glMapBufferARB (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target); +GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_ARB_vertex_buffer_object */ + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttrib1dARB (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fARB (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sARB (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dARB (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fARB (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sARB (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sARB (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4sARB (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointerARB (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, void **pointer); +#endif +#endif /* GL_ARB_vertex_program */ + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB programObj, GLuint index, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB programObj, const GLcharARB *name); +#endif +#endif /* GL_ARB_vertex_shader */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dARB (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fARB (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iARB (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos2sARB (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svARB (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dARB (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fARB (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iARB (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos3sARB (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svARB (const GLshort *v); +#endif +#endif /* GL_ARB_window_pos */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC) (GLenum texture, GLbyte s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC) (GLenum texture, GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC) (GLbyte s); +typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC) (GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC) (GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC) (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX2BOESPROC) (GLbyte x); +typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX3BOESPROC) (GLbyte x, GLbyte y); +typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX4BOESPROC) (GLbyte x, GLbyte y, GLbyte z); +typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC) (const GLbyte *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiTexCoord1bOES (GLenum texture, GLbyte s); +GLAPI void APIENTRY glMultiTexCoord1bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord2bOES (GLenum texture, GLbyte s, GLbyte t); +GLAPI void APIENTRY glMultiTexCoord2bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord3bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glMultiTexCoord3bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord4bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glMultiTexCoord4bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glTexCoord1bOES (GLbyte s); +GLAPI void APIENTRY glTexCoord1bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord2bOES (GLbyte s, GLbyte t); +GLAPI void APIENTRY glTexCoord2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord3bOES (GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glTexCoord3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord4bOES (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glTexCoord4bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex2bOES (GLbyte x); +GLAPI void APIENTRY glVertex2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex3bOES (GLbyte x, GLbyte y); +GLAPI void APIENTRY glVertex3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex4bOES (GLbyte x, GLbyte y, GLbyte z); +GLAPI void APIENTRY glVertex4bvOES (const GLbyte *coords); +#endif +#endif /* GL_OES_byte_coordinates */ + +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif /* GL_OES_compressed_paletted_texture */ + +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +typedef GLint GLfixed; +#define GL_FIXED_OES 0x140C +typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLfixed ref); +typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLfixed depth); +typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum plane, GLfixed *equation); +typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (APIENTRYP PFNGLORTHOXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEOESPROC) (GLfixed value, GLboolean invert); +typedef void (APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLACCUMXOESPROC) (GLenum op, GLfixed value); +typedef void (APIENTRYP PFNGLBITMAPXOESPROC) (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCOLOR3XOESPROC) (GLfixed red, GLfixed green, GLfixed blue); +typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC) (GLfixed u); +typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC) (GLfixed u, GLfixed v); +typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC) (GLsizei n, GLenum type, const GLfixed *buffer); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETMAPXVOESPROC) (GLenum target, GLenum query, GLfixed *v); +typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC) (GLenum map, GLint size, GLfixed *values); +typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC) (GLenum target, GLint level, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLINDEXXOESPROC) (GLfixed component); +typedef void (APIENTRYP PFNGLINDEXXVOESPROC) (const GLfixed *component); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMAP1XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +typedef void (APIENTRYP PFNGLMAP2XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC) (GLint n, GLfixed u1, GLfixed u2); +typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC) (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC) (GLenum texture, GLfixed s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC) (GLenum texture, GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC) (GLfixed token); +typedef void (APIENTRYP PFNGLPIXELMAPXPROC) (GLenum map, GLint size, const GLfixed *values); +typedef void (APIENTRYP PFNGLPIXELSTOREXPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC) (GLfixed xfactor, GLfixed yfactor); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC) (GLsizei n, const GLuint *textures, const GLfixed *priorities); +typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRECTXOESPROC) (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +typedef void (APIENTRYP PFNGLRECTXVOESPROC) (const GLfixed *v1, const GLfixed *v2); +typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC) (GLfixed s); +typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC) (GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC) (GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC) (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLVERTEX2XOESPROC) (GLfixed x); +typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX3XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX4XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC) (const GLfixed *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAlphaFuncxOES (GLenum func, GLfixed ref); +GLAPI void APIENTRY glClearColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearDepthxOES (GLfixed depth); +GLAPI void APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GLAPI void APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glDepthRangexOES (GLfixed n, GLfixed f); +GLAPI void APIENTRY glFogxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glFogxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glFrustumxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glGetClipPlanexOES (GLenum plane, GLfixed *equation); +GLAPI void APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexEnvxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glLineWidthxOES (GLfixed width); +GLAPI void APIENTRY glLoadMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glMultMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord4xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GLAPI void APIENTRY glOrthoxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glPointSizexOES (GLfixed size); +GLAPI void APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GLAPI void APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glSampleCoverageOES (GLfixed value, GLboolean invert); +GLAPI void APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glAccumxOES (GLenum op, GLfixed value); +GLAPI void APIENTRY glBitmapxOES (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +GLAPI void APIENTRY glBlendColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearAccumxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glColor3xOES (GLfixed red, GLfixed green, GLfixed blue); +GLAPI void APIENTRY glColor3xvOES (const GLfixed *components); +GLAPI void APIENTRY glColor4xvOES (const GLfixed *components); +GLAPI void APIENTRY glConvolutionParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glConvolutionParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glEvalCoord1xOES (GLfixed u); +GLAPI void APIENTRY glEvalCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glEvalCoord2xOES (GLfixed u, GLfixed v); +GLAPI void APIENTRY glEvalCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glFeedbackBufferxOES (GLsizei n, GLenum type, const GLfixed *buffer); +GLAPI void APIENTRY glGetConvolutionParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetHistogramParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetLightxOES (GLenum light, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetMapxvOES (GLenum target, GLenum query, GLfixed *v); +GLAPI void APIENTRY glGetMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glGetPixelMapxv (GLenum map, GLint size, GLfixed *values); +GLAPI void APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexLevelParameterxvOES (GLenum target, GLint level, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glIndexxOES (GLfixed component); +GLAPI void APIENTRY glIndexxvOES (const GLfixed *component); +GLAPI void APIENTRY glLoadTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMap1xOES (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +GLAPI void APIENTRY glMap2xOES (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +GLAPI void APIENTRY glMapGrid1xOES (GLint n, GLfixed u1, GLfixed u2); +GLAPI void APIENTRY glMapGrid2xOES (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +GLAPI void APIENTRY glMultTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord1xOES (GLenum texture, GLfixed s); +GLAPI void APIENTRY glMultiTexCoord1xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord2xOES (GLenum texture, GLfixed s, GLfixed t); +GLAPI void APIENTRY glMultiTexCoord2xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord3xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glMultiTexCoord3xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord4xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glNormal3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glPassThroughxOES (GLfixed token); +GLAPI void APIENTRY glPixelMapx (GLenum map, GLint size, const GLfixed *values); +GLAPI void APIENTRY glPixelStorex (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelTransferxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelZoomxOES (GLfixed xfactor, GLfixed yfactor); +GLAPI void APIENTRY glPrioritizeTexturesxOES (GLsizei n, const GLuint *textures, const GLfixed *priorities); +GLAPI void APIENTRY glRasterPos2xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glRasterPos2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos3xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glRasterPos3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos4xOES (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +GLAPI void APIENTRY glRasterPos4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRectxOES (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +GLAPI void APIENTRY glRectxvOES (const GLfixed *v1, const GLfixed *v2); +GLAPI void APIENTRY glTexCoord1xOES (GLfixed s); +GLAPI void APIENTRY glTexCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord2xOES (GLfixed s, GLfixed t); +GLAPI void APIENTRY glTexCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord3xOES (GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glTexCoord3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord4xOES (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glTexCoord4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glVertex2xOES (GLfixed x); +GLAPI void APIENTRY glVertex2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex3xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glVertex3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex4xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glVertex4xvOES (const GLfixed *coords); +#endif +#endif /* GL_OES_fixed_point */ + +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed *mantissa, GLint *exponent); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLbitfield APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *exponent); +#endif +#endif /* GL_OES_query_matrix */ + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif /* GL_OES_read_format */ + +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); +typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum plane, GLfloat *equation); +typedef void (APIENTRYP PFNGLORTHOFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearDepthfOES (GLclampf depth); +GLAPI void APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GLAPI void APIENTRY glDepthRangefOES (GLclampf n, GLclampf f); +GLAPI void APIENTRY glFrustumfOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +GLAPI void APIENTRY glGetClipPlanefOES (GLenum plane, GLfloat *equation); +GLAPI void APIENTRY glOrthofOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#endif +#endif /* GL_OES_single_precision */ + +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif /* GL_3DFX_multisample */ + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 +typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTbufferMask3DFX (GLuint mask); +#endif +#endif /* GL_3DFX_tbuffer */ + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif /* GL_3DFX_texture_compression_FXT1 */ + +#ifndef GL_AMD_blend_minmax_factor +#define GL_AMD_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_AMD_blend_minmax_factor */ + +#ifndef GL_AMD_conservative_depth +#define GL_AMD_conservative_depth 1 +#endif /* GL_AMD_conservative_depth */ + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 +#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A +#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B +#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C +#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D +#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F +#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 +typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#endif +#endif /* GL_AMD_debug_output */ + +#ifndef GL_AMD_depth_clamp_separate +#define GL_AMD_depth_clamp_separate 1 +#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E +#define GL_DEPTH_CLAMP_FAR_AMD 0x901F +#endif /* GL_AMD_depth_clamp_separate */ + +#ifndef GL_AMD_draw_buffers_blend +#define GL_AMD_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncIndexedAMD (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateIndexedAMD (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glBlendEquationIndexedAMD (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_AMD_draw_buffers_blend */ + +#ifndef GL_AMD_interleaved_elements +#define GL_AMD_interleaved_elements 1 +#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4 +#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5 +typedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC) (GLuint index, GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribParameteriAMD (GLuint index, GLenum pname, GLint param); +#endif +#endif /* GL_AMD_interleaved_elements */ + +#ifndef GL_AMD_multi_draw_indirect +#define GL_AMD_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC) (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectAMD (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectAMD (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#endif +#endif /* GL_AMD_multi_draw_indirect */ + +#ifndef GL_AMD_name_gen_delete +#define GL_AMD_name_gen_delete 1 +#define GL_DATA_BUFFER_AMD 0x9151 +#define GL_PERFORMANCE_MONITOR_AMD 0x9152 +#define GL_QUERY_OBJECT_AMD 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 +#define GL_SAMPLER_OBJECT_AMD 0x9155 +typedef void (APIENTRYP PFNGLGENNAMESAMDPROC) (GLenum identifier, GLuint num, GLuint *names); +typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC) (GLenum identifier, GLuint num, const GLuint *names); +typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC) (GLenum identifier, GLuint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenNamesAMD (GLenum identifier, GLuint num, GLuint *names); +GLAPI void APIENTRY glDeleteNamesAMD (GLenum identifier, GLuint num, const GLuint *names); +GLAPI GLboolean APIENTRY glIsNameAMD (GLenum identifier, GLuint name); +#endif +#endif /* GL_AMD_name_gen_delete */ + +#ifndef GL_AMD_occlusion_query_event +#define GL_AMD_occlusion_query_event 1 +#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F +#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001 +#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002 +#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004 +#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008 +#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF +typedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC) (GLenum target, GLuint id, GLenum pname, GLuint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glQueryObjectParameteruiAMD (GLenum target, GLuint id, GLenum pname, GLuint param); +#endif +#endif /* GL_AMD_occlusion_query_event */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); +typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); +GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_AMD_pinned_memory +#define GL_AMD_pinned_memory 1 +#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 +#endif /* GL_AMD_pinned_memory */ + +#ifndef GL_AMD_query_buffer_object +#define GL_AMD_query_buffer_object 1 +#define GL_QUERY_BUFFER_AMD 0x9192 +#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 +#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 +#endif /* GL_AMD_query_buffer_object */ + +#ifndef GL_AMD_sample_positions +#define GL_AMD_sample_positions 1 +#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F +typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC) (GLenum pname, GLuint index, const GLfloat *val); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLfloat *val); +#endif +#endif /* GL_AMD_sample_positions */ + +#ifndef GL_AMD_seamless_cubemap_per_texture +#define GL_AMD_seamless_cubemap_per_texture 1 +#endif /* GL_AMD_seamless_cubemap_per_texture */ + +#ifndef GL_AMD_shader_atomic_counter_ops +#define GL_AMD_shader_atomic_counter_ops 1 +#endif /* GL_AMD_shader_atomic_counter_ops */ + +#ifndef GL_AMD_shader_stencil_export +#define GL_AMD_shader_stencil_export 1 +#endif /* GL_AMD_shader_stencil_export */ + +#ifndef GL_AMD_shader_trinary_minmax +#define GL_AMD_shader_trinary_minmax 1 +#endif /* GL_AMD_shader_trinary_minmax */ + +#ifndef GL_AMD_sparse_texture +#define GL_AMD_sparse_texture 1 +#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A +#define GL_MIN_SPARSE_LEVEL_AMD 0x919B +#define GL_MIN_LOD_WARNING_AMD 0x919C +#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 +typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC) (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorageSparseAMD (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +GLAPI void APIENTRY glTextureStorageSparseAMD (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif +#endif /* GL_AMD_sparse_texture */ + +#ifndef GL_AMD_stencil_operation_extended +#define GL_AMD_stencil_operation_extended 1 +#define GL_SET_AMD 0x874A +#define GL_REPLACE_VALUE_AMD 0x874B +#define GL_STENCIL_OP_VALUE_AMD 0x874C +#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D +typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC) (GLenum face, GLuint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value); +#endif +#endif /* GL_AMD_stencil_operation_extended */ + +#ifndef GL_AMD_texture_texture4 +#define GL_AMD_texture_texture4 1 +#endif /* GL_AMD_texture_texture4 */ + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#define GL_AMD_transform_feedback3_lines_triangles 1 +#endif /* GL_AMD_transform_feedback3_lines_triangles */ + +#ifndef GL_AMD_vertex_shader_layer +#define GL_AMD_vertex_shader_layer 1 +#endif /* GL_AMD_vertex_shader_layer */ + +#ifndef GL_AMD_vertex_shader_tessellator +#define GL_AMD_vertex_shader_tessellator 1 +#define GL_SAMPLER_BUFFER_AMD 0x9001 +#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 +#define GL_TESSELLATION_MODE_AMD 0x9004 +#define GL_TESSELLATION_FACTOR_AMD 0x9005 +#define GL_DISCRETE_AMD 0x9006 +#define GL_CONTINUOUS_AMD 0x9007 +typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); +GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); +#endif +#endif /* GL_AMD_vertex_shader_tessellator */ + +#ifndef GL_AMD_vertex_shader_viewport_index +#define GL_AMD_vertex_shader_viewport_index 1 +#endif /* GL_AMD_vertex_shader_viewport_index */ + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_APPLE_aux_depth_stencil 1 +#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 +#endif /* GL_APPLE_aux_depth_stencil */ + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif /* GL_APPLE_client_storage */ + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#define GL_ELEMENT_ARRAY_APPLE 0x8A0C +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E +typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerAPPLE (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif +#endif /* GL_APPLE_element_array */ + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); +typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); +typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenFencesAPPLE (GLsizei n, GLuint *fences); +GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glSetFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint fence); +GLAPI void APIENTRY glFinishFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum object, GLuint name); +GLAPI void APIENTRY glFinishObjectAPPLE (GLenum object, GLint name); +#endif +#endif /* GL_APPLE_fence */ + +#ifndef GL_APPLE_float_pixels +#define GL_APPLE_float_pixels 1 +#define GL_HALF_APPLE 0x140B +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#endif /* GL_APPLE_float_pixels */ + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 +typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum target, GLintptr offset, GLsizeiptr size); +#endif +#endif /* GL_APPLE_flush_buffer_range */ + +#ifndef GL_APPLE_object_purgeable +#define GL_APPLE_object_purgeable 1 +#define GL_BUFFER_OBJECT_APPLE 0x85B3 +#define GL_RELEASED_APPLE 0x8A19 +#define GL_VOLATILE_APPLE 0x8A1A +#define GL_RETAINED_APPLE 0x8A1B +#define GL_UNDEFINED_APPLE 0x8A1C +#define GL_PURGEABLE_APPLE 0x8A1D +typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC) (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glObjectPurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI GLenum APIENTRY glObjectUnpurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI void APIENTRY glGetObjectParameterivAPPLE (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#endif +#endif /* GL_APPLE_object_purgeable */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_APPLE_row_bytes +#define GL_APPLE_row_bytes 1 +#define GL_PACK_ROW_BYTES_APPLE 0x8A15 +#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 +#endif /* GL_APPLE_row_bytes */ + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif /* GL_APPLE_specular_vector */ + +#ifndef GL_APPLE_texture_range +#define GL_APPLE_texture_range 1 +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_APPLE_texture_range */ + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif /* GL_APPLE_transform_hint */ + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint array); +GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint array); +#endif +#endif /* GL_APPLE_vertex_array_object */ + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CLIENT_APPLE 0x85B4 +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum pname, GLint param); +#endif +#endif /* GL_APPLE_vertex_array_range */ + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_APPLE_vertex_program_evaluators 1 +#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 +#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 +#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 +#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 +#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 +#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 +#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 +#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 +#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 +#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glEnableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glDisableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI GLboolean APIENTRY glIsVertexAttribEnabledAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glMapVertexAttrib1dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib1fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +GLAPI void APIENTRY glMapVertexAttrib2dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib2fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#endif +#endif /* GL_APPLE_vertex_program_evaluators */ + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#define GL_YCBCR_422_APPLE 0x85B9 +#endif /* GL_APPLE_ycbcr_422 */ + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersATI (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ATI_draw_buffers */ + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerATI (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayATI (GLenum mode, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif +#endif /* GL_ATI_element_array */ + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBumpParameterivATI (GLenum pname, const GLint *param); +GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum pname, GLfloat *param); +#endif +#endif /* GL_ATI_envmap_bumpmap */ + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); +typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); +typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint range); +GLAPI void APIENTRY glBindFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glBeginFragmentShaderATI (void); +GLAPI void APIENTRY glEndFragmentShaderATI (void); +GLAPI void APIENTRY glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle); +GLAPI void APIENTRY glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle); +GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint dst, const GLfloat *value); +#endif +#endif /* GL_ATI_fragment_shader */ + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 +typedef void *(APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void *APIENTRY glMapObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer); +#endif +#endif /* GL_ATI_map_object_buffer */ + +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo 1 +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif /* GL_ATI_meminfo */ + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif /* GL_ATI_pixel_format_float */ + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPNTrianglesiATI (GLenum pname, GLint param); +GLAPI void APIENTRY glPNTrianglesfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_pn_triangles */ + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpSeparateATI (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif +#endif /* GL_ATI_separate_stencil */ + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif /* GL_ATI_text_fragment_shader */ + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif /* GL_ATI_texture_env_combine3 */ + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif /* GL_ATI_texture_float */ + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif /* GL_ATI_texture_mirror_once */ + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const void *pointer, GLenum usage); +typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei size, const void *pointer, GLenum usage); +GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint buffer, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectBufferivATI (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glFreeObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glArrayObjectATI (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum array, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetArrayObjectivATI (GLenum array, GLenum pname, GLint *params); +GLAPI void APIENTRY glVariantArrayObjectATI (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint id, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint id, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_array_object */ + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_attrib_array_object */ + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexStream1sATI (GLenum stream, GLshort x); +GLAPI void APIENTRY glVertexStream1svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream1iATI (GLenum stream, GLint x); +GLAPI void APIENTRY glVertexStream1ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream1fATI (GLenum stream, GLfloat x); +GLAPI void APIENTRY glVertexStream1fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream1dATI (GLenum stream, GLdouble x); +GLAPI void APIENTRY glVertexStream1dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream2sATI (GLenum stream, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexStream2svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream2iATI (GLenum stream, GLint x, GLint y); +GLAPI void APIENTRY glVertexStream2ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream2fATI (GLenum stream, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexStream2fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream2dATI (GLenum stream, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexStream2dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream3sATI (GLenum stream, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream3iATI (GLenum stream, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream3fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream3dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream4sATI (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexStream4svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream4iATI (GLenum stream, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexStream4ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream4fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexStream4fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream4dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexStream4dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glNormalStream3bATI (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +GLAPI void APIENTRY glNormalStream3bvATI (GLenum stream, const GLbyte *coords); +GLAPI void APIENTRY glNormalStream3sATI (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +GLAPI void APIENTRY glNormalStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glNormalStream3iATI (GLenum stream, GLint nx, GLint ny, GLint nz); +GLAPI void APIENTRY glNormalStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glNormalStream3fATI (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +GLAPI void APIENTRY glNormalStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glNormalStream3dATI (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +GLAPI void APIENTRY glNormalStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum stream); +GLAPI void APIENTRY glVertexBlendEnviATI (GLenum pname, GLint param); +GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_vertex_streams */ + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif /* GL_EXT_422_pixels */ + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#define GL_ABGR_EXT 0x8000 +#endif /* GL_EXT_abgr */ + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif /* GL_EXT_bgra */ + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF +typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); +typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); +typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformBufferEXT (GLuint program, GLint location, GLuint buffer); +GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint program, GLint location); +GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint program, GLint location); +#endif +#endif /* GL_EXT_bindable_uniform */ + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColorEXT (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#endif +#endif /* GL_EXT_blend_color */ + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_EXT_blend_equation_separate */ + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_EXT_blend_func_separate */ + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif /* GL_EXT_blend_logic_op */ + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_BLEND_EQUATION_EXT 0x8009 +typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationEXT (GLenum mode); +#endif +#endif /* GL_EXT_blend_minmax */ + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif /* GL_EXT_blend_subtract */ + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif /* GL_EXT_clip_volume_hint */ + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif /* GL_EXT_cmyka */ + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 +typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorSubTableEXT (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif +#endif /* GL_EXT_color_subtable */ + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 +typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count); +GLAPI void APIENTRY glUnlockArraysEXT (void); +#endif +#endif /* GL_EXT_compiled_vertex_array */ + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#endif +#endif /* GL_EXT_convolution */ + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); +typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); +typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); +typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); +typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); +typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); +typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); +typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); +typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); +typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); +typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTangent3bEXT (GLbyte tx, GLbyte ty, GLbyte tz); +GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glTangent3dEXT (GLdouble tx, GLdouble ty, GLdouble tz); +GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glTangent3fEXT (GLfloat tx, GLfloat ty, GLfloat tz); +GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glTangent3iEXT (GLint tx, GLint ty, GLint tz); +GLAPI void APIENTRY glTangent3ivEXT (const GLint *v); +GLAPI void APIENTRY glTangent3sEXT (GLshort tx, GLshort ty, GLshort tz); +GLAPI void APIENTRY glTangent3svEXT (const GLshort *v); +GLAPI void APIENTRY glBinormal3bEXT (GLbyte bx, GLbyte by, GLbyte bz); +GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glBinormal3dEXT (GLdouble bx, GLdouble by, GLdouble bz); +GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glBinormal3fEXT (GLfloat bx, GLfloat by, GLfloat bz); +GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glBinormal3iEXT (GLint bx, GLint by, GLint bz); +GLAPI void APIENTRY glBinormal3ivEXT (const GLint *v); +GLAPI void APIENTRY glBinormal3sEXT (GLshort bx, GLshort by, GLshort bz); +GLAPI void APIENTRY glBinormal3svEXT (const GLshort *v); +GLAPI void APIENTRY glTangentPointerEXT (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glBinormalPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_coordinate_frame */ + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_copy_texture */ + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC +typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullParameterdvEXT (GLenum pname, GLdouble *params); +GLAPI void APIENTRY glCullParameterfvEXT (GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_cull_vertex */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 +typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPopGroupMarkerEXT (void); +#endif +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthBoundsEXT (GLclampd zmin, GLclampd zmax); +#endif +#endif /* GL_EXT_depth_bounds_test */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F +typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); +typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); +typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); +GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); +GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); +GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); +GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); +GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); +GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); +GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); +GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); +GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); +GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); +GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); +GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); +GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); +GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); +GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); +#endif +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 +typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#endif +#endif /* GL_EXT_draw_buffers2 */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#endif +#endif /* GL_EXT_draw_range_elements */ + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogCoordfEXT (GLfloat coord); +GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *coord); +GLAPI void APIENTRY glFogCoorddEXT (GLdouble coord); +GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_fog_coord */ + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* GL_EXT_framebuffer_blit */ + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_framebuffer_multisample */ + +#ifndef GL_EXT_framebuffer_multisample_blit_scaled +#define GL_EXT_framebuffer_multisample_blit_scaled 1 +#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA +#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB +#endif /* GL_EXT_framebuffer_multisample_blit_scaled */ + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbufferEXT (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebufferEXT (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmapEXT (GLenum target); +#endif +#endif /* GL_EXT_framebuffer_object */ + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif /* GL_EXT_framebuffer_sRGB */ + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); +#endif +#endif /* GL_EXT_geometry_shader4 */ + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif +#endif /* GL_EXT_gpu_program_parameters */ + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 +typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1uiEXT (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2uiEXT (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value); +#endif +#endif /* GL_EXT_gpu_shader4 */ + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 +typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetHistogramEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmaxEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogramEXT (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmaxEXT (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogramEXT (GLenum target); +GLAPI void APIENTRY glResetMinmaxEXT (GLenum target); +#endif +#endif /* GL_EXT_histogram */ + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif /* GL_EXT_index_array_formats */ + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 +typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexFuncEXT (GLenum func, GLclampf ref); +#endif +#endif /* GL_EXT_index_func */ + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA +typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_index_material */ + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif /* GL_EXT_index_texture */ + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyTextureEXT (GLenum mode); +GLAPI void APIENTRY glTextureLightEXT (GLenum pname); +GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_light_texture */ + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif /* GL_EXT_misc_attribute */ + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#endif +#endif /* GL_EXT_multi_draw_arrays */ + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskEXT (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternEXT (GLenum pattern); +#endif +#endif /* GL_EXT_multisample */ + +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif /* GL_EXT_packed_depth_stencil */ + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif /* GL_EXT_packed_float */ + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif /* GL_EXT_packed_pixels */ + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, void *data); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableEXT (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glGetColorTableEXT (GLenum target, GLenum format, GLenum type, void *data); +GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_paletted_texture */ + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif /* GL_EXT_pixel_buffer_object */ + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTransformParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTransformParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_pixel_transform */ + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif /* GL_EXT_pixel_transform_color_table */ + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfEXT (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvEXT (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_EXT_point_parameters */ + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat factor, GLfloat bias); +#endif +#endif /* GL_EXT_polygon_offset */ + +#ifndef GL_EXT_provoking_vertex +#define GL_EXT_provoking_vertex 1 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_PROVOKING_VERTEX_EXT 0x8E4F +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProvokingVertexEXT (GLenum mode); +#endif +#endif /* GL_EXT_provoking_vertex */ + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif /* GL_EXT_rescale_normal */ + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3iEXT (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_secondary_color */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D +typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); +typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); +GLAPI void APIENTRY glActiveProgramEXT (GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif /* GL_EXT_separate_specular_color */ + +#ifndef GL_EXT_shader_image_load_store +#define GL_EXT_shader_image_load_store 1 +#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 +#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A +#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B +#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C +#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D +#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E +#define GL_IMAGE_1D_EXT 0x904C +#define GL_IMAGE_2D_EXT 0x904D +#define GL_IMAGE_3D_EXT 0x904E +#define GL_IMAGE_2D_RECT_EXT 0x904F +#define GL_IMAGE_CUBE_EXT 0x9050 +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_1D_ARRAY_EXT 0x9052 +#define GL_IMAGE_2D_ARRAY_EXT 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 +#define GL_INT_IMAGE_1D_EXT 0x9057 +#define GL_INT_IMAGE_2D_EXT 0x9058 +#define GL_INT_IMAGE_3D_EXT 0x9059 +#define GL_INT_IMAGE_2D_RECT_EXT 0x905A +#define GL_INT_IMAGE_CUBE_EXT 0x905B +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D +#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C +#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D +#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 +#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 +#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC) (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC) (GLbitfield barriers); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindImageTextureEXT (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers); +#endif +#endif /* GL_EXT_shader_image_load_store */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif /* GL_EXT_shadow_funcs */ + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif /* GL_EXT_shared_texture_palette */ + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 +typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilClearTagEXT (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif +#endif /* GL_EXT_stencil_clear_tag */ + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum face); +#endif +#endif /* GL_EXT_stencil_two_side */ + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif /* GL_EXT_stencil_wrap */ + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_subtexture */ + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif /* GL_EXT_texture */ + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage3DEXT (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_texture3D */ + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E +#endif /* GL_EXT_texture_array */ + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_EXT_texture_buffer_object */ + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif /* GL_EXT_texture_compression_latc */ + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif /* GL_EXT_texture_compression_rgtc */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_cube_map +#define GL_EXT_texture_cube_map 1 +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif /* GL_EXT_texture_cube_map */ + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif /* GL_EXT_texture_env_add */ + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif /* GL_EXT_texture_env_combine */ + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif /* GL_EXT_texture_env_dot3 */ + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif /* GL_EXT_texture_filter_anisotropic */ + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearColorIiEXT (GLint red, GLint green, GLint blue, GLint alpha); +GLAPI void APIENTRY glClearColorIuiEXT (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif +#endif /* GL_EXT_texture_integer */ + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif /* GL_EXT_texture_lod_bias */ + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif /* GL_EXT_texture_mirror_clamp */ + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei n, const GLuint *textures, GLboolean *residences); +GLAPI void APIENTRY glBindTextureEXT (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTexturesEXT (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint texture); +GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif +#endif /* GL_EXT_texture_object */ + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureNormalEXT (GLenum mode); +#endif +#endif /* GL_EXT_texture_perturb_normal */ + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif /* GL_EXT_texture_sRGB */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif /* GL_EXT_texture_shared_exponent */ + +#ifndef GL_EXT_texture_snorm +#define GL_EXT_texture_snorm 1 +#define GL_ALPHA_SNORM 0x9010 +#define GL_LUMINANCE_SNORM 0x9011 +#define GL_LUMINANCE_ALPHA_SNORM 0x9012 +#define GL_INTENSITY_SNORM 0x9013 +#define GL_ALPHA8_SNORM 0x9014 +#define GL_LUMINANCE8_SNORM 0x9015 +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define GL_INTENSITY8_SNORM 0x9017 +#define GL_ALPHA16_SNORM 0x9018 +#define GL_LUMINANCE16_SNORM 0x9019 +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A +#define GL_INTENSITY16_SNORM 0x901B +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#endif /* GL_EXT_texture_snorm */ + +#ifndef GL_EXT_texture_swizzle +#define GL_EXT_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 +#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 +#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 +#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 +#endif /* GL_EXT_texture_swizzle */ + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#define GL_TIME_ELAPSED_EXT 0x88BF +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64 *params); +#endif +#endif /* GL_EXT_timer_query */ + +#ifndef GL_EXT_transform_feedback +#define GL_EXT_transform_feedback 1 +#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F +#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C +#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 +#define GL_RASTERIZER_DISCARD_EXT 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackEXT (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackEXT (void); +GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#endif +#endif /* GL_EXT_transform_feedback */ + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, void **params); +typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glArrayElementEXT (GLint i); +GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer); +GLAPI void APIENTRY glGetPointervEXT (GLenum pname, void **params); +GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glVertexPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#endif +#endif /* GL_EXT_vertex_array */ + +#ifndef GL_EXT_vertex_array_bgra +#define GL_EXT_vertex_array_bgra 1 +#endif /* GL_EXT_vertex_array_bgra */ + +#ifndef GL_EXT_vertex_attrib_64bit +#define GL_EXT_vertex_attrib_64bit 1 +#define GL_DOUBLE_VEC2_EXT 0x8FFC +#define GL_DOUBLE_VEC3_EXT 0x8FFD +#define GL_DOUBLE_VEC4_EXT 0x8FFE +#define GL_DOUBLE_MAT2_EXT 0x8F46 +#define GL_DOUBLE_MAT3_EXT 0x8F47 +#define GL_DOUBLE_MAT4_EXT 0x8F48 +#define GL_DOUBLE_MAT2x3_EXT 0x8F49 +#define GL_DOUBLE_MAT2x4_EXT 0x8F4A +#define GL_DOUBLE_MAT3x2_EXT 0x8F4B +#define GL_DOUBLE_MAT3x4_EXT 0x8F4C +#define GL_DOUBLE_MAT4x2_EXT 0x8F4D +#define GL_DOUBLE_MAT4x3_EXT 0x8F4E +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC) (GLuint index, GLenum pname, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1dEXT (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2dEXT (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdvEXT (GLuint index, GLenum pname, GLdouble *params); +#endif +#endif /* GL_EXT_vertex_attrib_64bit */ + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); +typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); +typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); +typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); +typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); +typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); +typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); +typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); +typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); +typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); +typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const void *addr); +typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); +typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); +typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, void **data); +typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVertexShaderEXT (void); +GLAPI void APIENTRY glEndVertexShaderEXT (void); +GLAPI void APIENTRY glBindVertexShaderEXT (GLuint id); +GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint range); +GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint id); +GLAPI void APIENTRY glShaderOp1EXT (GLenum op, GLuint res, GLuint arg1); +GLAPI void APIENTRY glShaderOp2EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +GLAPI void APIENTRY glShaderOp3EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +GLAPI void APIENTRY glSwizzleEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glWriteMaskEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glInsertComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI void APIENTRY glExtractComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +GLAPI void APIENTRY glSetInvariantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glSetLocalConstantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glVariantbvEXT (GLuint id, const GLbyte *addr); +GLAPI void APIENTRY glVariantsvEXT (GLuint id, const GLshort *addr); +GLAPI void APIENTRY glVariantivEXT (GLuint id, const GLint *addr); +GLAPI void APIENTRY glVariantfvEXT (GLuint id, const GLfloat *addr); +GLAPI void APIENTRY glVariantdvEXT (GLuint id, const GLdouble *addr); +GLAPI void APIENTRY glVariantubvEXT (GLuint id, const GLubyte *addr); +GLAPI void APIENTRY glVariantusvEXT (GLuint id, const GLushort *addr); +GLAPI void APIENTRY glVariantuivEXT (GLuint id, const GLuint *addr); +GLAPI void APIENTRY glVariantPointerEXT (GLuint id, GLenum type, GLuint stride, const void *addr); +GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint id); +GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint id); +GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum light, GLenum value); +GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum face, GLenum value); +GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum unit, GLenum coord, GLenum value); +GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum unit, GLenum value); +GLAPI GLuint APIENTRY glBindParameterEXT (GLenum value); +GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap); +GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, void **data); +GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +#endif +#endif /* GL_EXT_vertex_shader */ + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT 0x1700 +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); +GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); +GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_vertex_weighting */ + +#ifndef GL_EXT_x11_sync_object +#define GL_EXT_x11_sync_object 1 +#define GL_SYNC_X11_FENCE_EXT 0x90E1 +typedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glImportSyncEXT (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#endif +#endif /* GL_EXT_x11_sync_object */ + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 +typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); +#endif +#endif /* GL_GREMEDY_frame_terminator */ + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 +typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const void *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei len, const void *string); +#endif +#endif /* GL_GREMEDY_string_marker */ + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif /* GL_HP_convolution_border_modes */ + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImageTransformParameteriHP (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glImageTransformParameterfHP (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glImageTransformParameterivHP (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_HP_image_transform */ + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif /* GL_HP_occlusion_test */ + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif /* GL_HP_texture_lighting */ + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#define GL_CULL_VERTEX_IBM 103050 +#endif /* GL_IBM_cull_vertex */ + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#endif +#endif /* GL_IBM_multimode_draw_arrays */ + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif /* GL_IBM_rasterpos_clip */ + +#ifndef GL_IBM_static_data +#define GL_IBM_static_data 1 +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 +typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushStaticDataIBM (GLenum target); +#endif +#endif /* GL_IBM_static_data */ + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_IBM_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif /* GL_IBM_texture_mirrored_repeat */ + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean **pointer, GLint ptrstride); +GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#endif +#endif /* GL_IBM_vertex_array_lists */ + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_INGR_blend_func_separate */ + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif /* GL_INGR_color_clamp */ + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#define GL_INTERLACE_READ_INGR 0x8568 +#endif /* GL_INGR_interlace_read */ + +#ifndef GL_INTEL_fragment_shader_ordering +#define GL_INTEL_fragment_shader_ordering 1 +#endif /* GL_INTEL_fragment_shader_ordering */ + +#ifndef GL_INTEL_map_texture +#define GL_INTEL_map_texture 1 +#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF +#define GL_LAYOUT_DEFAULT_INTEL 0 +#define GL_LAYOUT_LINEAR_INTEL 1 +#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 +typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level); +typedef void *(APIENTRYP PFNGLMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSyncTextureINTEL (GLuint texture); +GLAPI void APIENTRY glUnmapTexture2DINTEL (GLuint texture, GLint level); +GLAPI void *APIENTRY glMapTexture2DINTEL (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#endif +#endif /* GL_INTEL_map_texture */ + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const void **pointer); +GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const void **pointer); +#endif +#endif /* GL_INTEL_parallel_arrays */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 +typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); +typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); +typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); +typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); +typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); +typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); +GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); +GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); +GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); +GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); +GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#endif +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif /* GL_MESAX_texture_stack */ + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#define GL_PACK_INVERT_MESA 0x8758 +#endif /* GL_MESA_pack_invert */ + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 +typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glResizeBuffersMESA (void); +#endif +#endif /* GL_MESA_resize_buffers */ + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dMESA (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fMESA (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iMESA (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos2sMESA (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dMESA (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fMESA (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iMESA (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos3sMESA (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos4dMESA (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos4fMESA (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos4iMESA (GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos4sMESA (GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v); +#endif +#endif /* GL_MESA_window_pos */ + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif /* GL_MESA_ycbcr_texture */ + +#ifndef GL_NVX_conditional_render +#define GL_NVX_conditional_render 1 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNVX (GLuint id); +GLAPI void APIENTRY glEndConditionalRenderNVX (void); +#endif +#endif /* GL_NVX_conditional_render */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 +typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); +GLAPI void APIENTRY glBlendBarrierNV (void); +#endif +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif /* GL_NV_blend_square */ + +#ifndef GL_NV_compute_program5 +#define GL_NV_compute_program5 1 +#define GL_COMPUTE_PROGRAM_NV 0x90FB +#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC +#endif /* GL_NV_compute_program5 */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif /* GL_NV_copy_depth_to_color */ + +#ifndef GL_NV_copy_image +#define GL_NV_copy_image 1 +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyImageSubDataNV (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_NV_copy_image */ + +#ifndef GL_NV_deep_texture3D +#define GL_NV_deep_texture3D 1 +#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 +#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 +#endif /* GL_NV_deep_texture3D */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); +GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#define GL_DEPTH_CLAMP_NV 0x864F +#endif /* GL_NV_depth_clamp */ + +#ifndef GL_NV_draw_texture +#define GL_NV_draw_texture 1 +typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC) (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif +#endif /* GL_NV_draw_texture */ + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +GLAPI void APIENTRY glMapParameterivNV (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMapParameterfvNV (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +GLAPI void APIENTRY glGetMapParameterivNV (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapParameterfvNV (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum target, GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glEvalMapsNV (GLenum target, GLenum mode); +#endif +#endif /* GL_NV_evaluators */ + +#ifndef GL_NV_explicit_multisample +#define GL_NV_explicit_multisample 1 +#define GL_SAMPLE_POSITION_NV 0x8E50 +#define GL_SAMPLE_MASK_NV 0x8E51 +#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 +#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 +#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 +#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 +#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 +#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 +#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 +#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC) (GLuint index, GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC) (GLenum target, GLuint renderbuffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetMultisamplefvNV (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaskIndexedNV (GLuint index, GLbitfield mask); +GLAPI void APIENTRY glTexRenderbufferNV (GLenum target, GLuint renderbuffer); +#endif +#endif /* GL_NV_explicit_multisample */ + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); +GLAPI GLboolean APIENTRY glIsFenceNV (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceNV (GLuint fence); +GLAPI void APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); +GLAPI void APIENTRY glFinishFenceNV (GLuint fence); +GLAPI void APIENTRY glSetFenceNV (GLuint fence, GLenum condition); +#endif +#endif /* GL_NV_fence */ + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif /* GL_NV_float_buffer */ + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +#endif /* GL_NV_fog_distance */ + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif +#endif /* GL_NV_fragment_program */ + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif /* GL_NV_fragment_program2 */ + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif /* GL_NV_fragment_program4 */ + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif /* GL_NV_fragment_program_option */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 +typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramVertexLimitNV (GLenum target, GLint limit); +GLAPI void APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_NV_geometry_program4 */ + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif /* GL_NV_geometry_shader4 */ + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum target, GLuint index, GLuint *params); +#endif +#endif /* GL_NV_gpu_program4 */ + +#ifndef GL_NV_gpu_program5 +#define GL_NV_gpu_program5 1 +#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C +#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F +#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 +#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 +typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC) (GLenum target, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC) (GLenum target, GLuint index, GLuint *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramSubroutineParametersuivNV (GLenum target, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramSubroutineParameteruivNV (GLenum target, GLuint index, GLuint *param); +#endif +#endif /* GL_NV_gpu_program5 */ + +#ifndef GL_NV_gpu_program5_mem_extended +#define GL_NV_gpu_program5_mem_extended 1 +#endif /* GL_NV_gpu_program5_mem_extended */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +typedef int64_t GLint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); +GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); +GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); +GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); +GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); +GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 +typedef unsigned short GLhalfNV; +#define GL_HALF_FLOAT_NV 0x140B +typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); +typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); +typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex3hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex4hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glNormal3hNV (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor4hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV s); +GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum target, GLhalfNV s); +GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum target, GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog); +GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog); +GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight); +GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight); +GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x); +GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib3hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib4hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +#endif +#endif /* GL_NV_half_float */ + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif /* GL_NV_light_max_exponent */ + +#ifndef GL_NV_multisample_coverage +#define GL_NV_multisample_coverage 1 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#endif /* GL_NV_multisample_coverage */ + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif /* GL_NV_multisample_filter_hint */ + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glEndOcclusionQueryNV (void); +GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_occlusion_query */ + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif /* GL_NV_packed_depth_stencil */ + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#endif +#endif /* GL_NV_parameter_buffer_object */ + +#ifndef GL_NV_parameter_buffer_object2 +#define GL_NV_parameter_buffer_object2 1 +#endif /* GL_NV_parameter_buffer_object2 */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); +GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); +GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); +GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); +GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); +GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); +GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); +GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); +GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); +GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); +GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); +GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); +GLAPI void APIENTRY glPathColorGenNV (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +GLAPI void APIENTRY glPathTexGenNV (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glPathFogGenNV (GLenum genMode); +GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); +GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); +GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); +GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +GLAPI void APIENTRY glGetPathColorGenivNV (GLenum color, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathColorGenfvNV (GLenum color, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathTexGenivNV (GLenum texCoordSet, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathTexGenfvNV (GLenum texCoordSet, GLenum pname, GLfloat *value); +GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); +GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); +GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); +GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +#endif +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); +#endif +#endif /* GL_NV_pixel_data_range */ + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameterivNV (GLenum pname, const GLint *params); +#endif +#endif /* GL_NV_point_sprite */ + +#ifndef GL_NV_present_video +#define GL_NV_present_video 1 +#define GL_FRAME_NV 0x8E26 +#define GL_FIELDS_NV 0x8E27 +#define GL_CURRENT_TIME_NV 0x8E28 +#define GL_NUM_FILL_STREAMS_NV 0x8E29 +#define GL_PRESENT_TIME_NV 0x8E2A +#define GL_PRESENT_DURATION_NV 0x8E2B +typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC) (GLuint video_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC) (GLuint video_slot, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC) (GLuint video_slot, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC) (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPresentFrameKeyedNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +GLAPI void APIENTRY glPresentFrameDualFillNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +GLAPI void APIENTRY glGetVideoivNV (GLuint video_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideouivNV (GLuint video_slot, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetVideoi64vNV (GLuint video_slot, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVideoui64vNV (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_NV_present_video */ + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveRestartNV (void); +GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index); +#endif +#endif /* GL_NV_primitive_restart */ + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerParameterfvNV (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glCombinerParameterfNV (GLenum pname, GLfloat param); +GLAPI void APIENTRY glCombinerParameterivNV (GLenum pname, const GLint *params); +GLAPI void APIENTRY glCombinerParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glCombinerInputNV (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glCombinerOutputNV (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +GLAPI void APIENTRY glFinalCombinerInputNV (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum stage, GLenum portion, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum variable, GLenum pname, GLint *params); +#endif +#endif /* GL_NV_register_combiners */ + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum stage, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname, GLfloat *params); +#endif +#endif /* GL_NV_register_combiners2 */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 +typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); +typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); +typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); +typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); +typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); +GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); +GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); +GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); +GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); +GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); +GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); +GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); +GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); +GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); +GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_storage_buffer_object +#define GL_NV_shader_storage_buffer_object 1 +#endif /* GL_NV_shader_storage_buffer_object */ + +#ifndef GL_NV_tessellation_program5 +#define GL_NV_tessellation_program5 1 +#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 +#define GL_TESS_CONTROL_PROGRAM_NV 0x891E +#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F +#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 +#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 +#endif /* GL_NV_tessellation_program5 */ + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif /* GL_NV_texgen_emboss */ + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif /* GL_NV_texgen_reflection */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 +typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureBarrierNV (void); +#endif +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif /* GL_NV_texture_compression_vtc */ + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif /* GL_NV_texture_env_combine4 */ + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif /* GL_NV_texture_expand_normal */ + +#ifndef GL_NV_texture_multisample +#define GL_NV_texture_multisample 1 +#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045 +#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046 +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage2DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTexImage3DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#endif +#endif /* GL_NV_texture_multisample */ + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif /* GL_NV_texture_rectangle */ + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif /* GL_NV_texture_shader */ + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif /* GL_NV_texture_shader2 */ + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif /* GL_NV_texture_shader3 */ + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#define GL_LAYER_NV 0x8DAA +#define GL_NEXT_BUFFER_NV -2 +#define GL_SKIP_COMPONENTS4_NV -3 +#define GL_SKIP_COMPONENTS3_NV -4 +#define GL_SKIP_COMPONENTS2_NV -5 +#define GL_SKIP_COMPONENTS1_NV -6 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLuint count, const GLint *attribs, GLenum bufferMode); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackNV (void); +GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLuint count, const GLint *attribs, GLenum bufferMode); +GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name); +GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint program, GLuint index, GLint *location); +GLAPI void APIENTRY glTransformFeedbackStreamAttribsNV (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#endif +#endif /* GL_NV_transform_feedback */ + +#ifndef GL_NV_transform_feedback2 +#define GL_NV_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC) (GLenum mode, GLuint id); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindTransformFeedbackNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacksNV (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacksNV (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedbackNV (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedbackNV (void); +GLAPI void APIENTRY glResumeTransformFeedbackNV (void); +GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id); +#endif +#endif /* GL_NV_transform_feedback2 */ + +#ifndef GL_NV_vdpau_interop +#define GL_NV_vdpau_interop 1 +typedef GLintptr GLvdpauSurfaceNV; +#define GL_SURFACE_STATE_NV 0x86EB +#define GL_SURFACE_REGISTERED_NV 0x86FD +#define GL_SURFACE_MAPPED_NV 0x8700 +#define GL_WRITE_DISCARD_NV 0x88BE +typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const void *vdpDevice, const void *getProcAddress); +typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC) (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC) (GLvdpauSurfaceNV surface, GLenum access); +typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC) (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC) (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVDPAUInitNV (const void *vdpDevice, const void *getProcAddress); +GLAPI void APIENTRY glVDPAUFiniNV (void); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLboolean APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUGetSurfaceivNV (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glVDPAUSurfaceAccessNV (GLvdpauSurfaceNV surface, GLenum access); +GLAPI void APIENTRY glVDPAUMapSurfacesNV (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#endif +#endif /* GL_NV_vdpau_interop */ + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); +GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei length, const void *pointer); +#endif +#endif /* GL_NV_vertex_array_range */ + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif /* GL_NV_vertex_array_range2 */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); +GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 +typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); +GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); +typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); +typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei n, const GLuint *programs, GLboolean *residences); +GLAPI void APIENTRY glBindProgramNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glExecuteProgramNV (GLenum target, GLuint id, const GLfloat *params); +GLAPI void APIENTRY glGenProgramsNV (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum target, GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetProgramivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringNV (GLuint id, GLenum pname, GLubyte *program); +GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id); +GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramParameter4dvNV (GLenum target, GLuint index, const GLdouble *v); +GLAPI void APIENTRY glProgramParameter4fNV (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramParameter4fvNV (GLenum target, GLuint index, const GLfloat *v); +GLAPI void APIENTRY glProgramParameters4dvNV (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glProgramParameters4fvNV (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glTrackMatrixNV (GLenum target, GLuint address, GLenum matrix, GLenum transform); +GLAPI void APIENTRY glVertexAttribPointerNV (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexAttrib1dNV (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fNV (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sNV (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dNV (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fNV (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sNV (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sNV (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4sNV (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs1svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs2svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs3svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs4svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GLubyte *v); +#endif +#endif /* GL_NV_vertex_program */ + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif /* GL_NV_vertex_program1_1 */ + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif /* GL_NV_vertex_program2 */ + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#endif /* GL_NV_vertex_program2_option */ + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#endif /* GL_NV_vertex_program3 */ + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_vertex_program4 */ + +#ifndef GL_NV_video_capture +#define GL_NV_video_capture 1 +#define GL_VIDEO_BUFFER_NV 0x9020 +#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 +#define GL_FIELD_UPPER_NV 0x9022 +#define GL_FIELD_LOWER_NV 0x9023 +#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 +#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 +#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 +#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 +#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 +#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 +#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A +#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B +#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C +#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D +#define GL_PARTIAL_SUCCESS_NV 0x902E +#define GL_SUCCESS_NV 0x902F +#define GL_FAILURE_NV 0x9030 +#define GL_YCBYCR8_422_NV 0x9031 +#define GL_YCBAYCR8A_4224_NV 0x9032 +#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 +#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 +#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 +#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 +#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 +#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 +#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 +#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A +#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B +#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C +typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC) (GLuint video_capture_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC) (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glBindVideoCaptureStreamBufferNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +GLAPI void APIENTRY glBindVideoCaptureStreamTextureNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +GLAPI void APIENTRY glEndVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glGetVideoCaptureivNV (GLuint video_capture_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVideoCaptureStreamdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +GLAPI GLenum APIENTRY glVideoCaptureNV (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +GLAPI void APIENTRY glVideoCaptureStreamParameterivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#endif +#endif /* GL_NV_video_capture */ + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif /* GL_OML_interlace */ + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif /* GL_OML_resample */ + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif /* GL_OML_subsample */ + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 +typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glHintPGI (GLenum target, GLint mode); +#endif +#endif /* GL_PGI_misc_hints */ + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif /* GL_PGI_vertex_hints */ + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif /* GL_REND_screen_coordinates */ + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 +#endif /* GL_S3_s3tc */ + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C +typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_detail_texture */ + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C +typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogFuncSGIS (GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *points); +#endif +#endif /* GL_SGIS_fog_function */ + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif /* GL_SGIS_generate_mipmap */ + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskSGIS (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternSGIS (GLenum pattern); +#endif +#endif /* GL_SGIS_multisample */ + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum pname, GLfloat *params); +#endif +#endif /* GL_SGIS_pixel_texture */ + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif /* GL_SGIS_point_line_texgen */ + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvSGIS (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_SGIS_point_parameters */ + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 +typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_sharpen_texture */ + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F +typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage4DSGIS (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_SGIS_texture4D */ + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif /* GL_SGIS_texture_border_clamp */ + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF +typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif +#endif /* GL_SGIS_texture_color_mask */ + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif /* GL_SGIS_texture_edge_clamp */ + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 +typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); +typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum target, GLenum filter, GLfloat *weights); +GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif +#endif /* GL_SGIS_texture_filter4 */ + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif /* GL_SGIS_texture_lod */ + +#ifndef GL_SGIS_texture_select +#define GL_SGIS_texture_select 1 +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif /* GL_SGIS_texture_select */ + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#define GL_ASYNC_MARKER_SGIX 0x8329 +typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); +typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); +typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); +typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint marker); +GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *markerp); +GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *markerp); +GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei range); +GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint marker, GLsizei range); +GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint marker); +#endif +#endif /* GL_SGIX_async */ + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif /* GL_SGIX_async_histogram */ + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif /* GL_SGIX_async_pixel */ + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif /* GL_SGIX_blend_alpha_minmax */ + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif /* GL_SGIX_calligraphic_fragment */ + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif /* GL_SGIX_clipmap */ + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif /* GL_SGIX_convolution_accuracy */ + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif /* GL_SGIX_depth_pass_instrument */ + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif /* GL_SGIX_depth_texture */ + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 +typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushRasterSGIX (void); +#endif +#endif /* GL_SGIX_flush_raster */ + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif /* GL_SGIX_fog_offset */ + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 +typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum face, GLenum mode); +GLAPI void APIENTRY glFragmentLightfSGIX (GLenum light, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum light, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightiSGIX (GLenum light, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightivSGIX (GLenum light, GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum face, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum face, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum face, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum face, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum light, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum light, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum face, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum face, GLenum pname, GLint *params); +GLAPI void APIENTRY glLightEnviSGIX (GLenum pname, GLint param); +#endif +#endif /* GL_SGIX_fragment_lighting */ + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D +typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameZoomSGIX (GLint factor); +#endif +#endif /* GL_SGIX_framezoom */ + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 +typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const void *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum pname, const void *params); +#endif +#endif /* GL_SGIX_igloo_interface */ + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 +typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); +typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); +typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); +GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei size, GLint *buffer); +GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *marker_p); +GLAPI void APIENTRY glReadInstrumentsSGIX (GLint marker); +GLAPI void APIENTRY glStartInstrumentsSGIX (void); +GLAPI void APIENTRY glStopInstrumentsSGIX (GLint marker); +#endif +#endif /* GL_SGIX_instruments */ + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#define GL_INTERLACE_SGIX 0x8094 +#endif /* GL_SGIX_interlace */ + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif /* GL_SGIX_ir_instrument1 */ + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#define GL_LIST_PRIORITY_SGIX 0x8182 +typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint list, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetListParameterivSGIX (GLuint list, GLenum pname, GLint *params); +GLAPI void APIENTRY glListParameterfSGIX (GLuint list, GLenum pname, GLfloat param); +GLAPI void APIENTRY glListParameterfvSGIX (GLuint list, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glListParameteriSGIX (GLuint list, GLenum pname, GLint param); +GLAPI void APIENTRY glListParameterivSGIX (GLuint list, GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_list_priority */ + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B +typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenSGIX (GLenum mode); +#endif +#endif /* GL_SGIX_pixel_texture */ + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif /* GL_SGIX_pixel_tiles */ + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +GLAPI void APIENTRY glDeformSGIX (GLbitfield mask); +GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield mask); +#endif +#endif /* GL_SGIX_polynomial_ffd */ + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E +typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *equation); +#endif +#endif /* GL_SGIX_reference_plane */ + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#define GL_PACK_RESAMPLE_SGIX 0x842C +#define GL_UNPACK_RESAMPLE_SGIX 0x842D +#define GL_RESAMPLE_REPLICATE_SGIX 0x842E +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x842F +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif /* GL_SGIX_resample */ + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif /* GL_SGIX_scalebias_hint */ + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif /* GL_SGIX_shadow */ + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif /* GL_SGIX_shadow_ambient */ + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_sprite */ + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif /* GL_SGIX_subsample */ + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 +typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTagSampleBufferSGIX (void); +#endif +#endif /* GL_SGIX_tag_sample_buffer */ + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif /* GL_SGIX_texture_add_env */ + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif /* GL_SGIX_texture_coordinate_clamp */ + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif /* GL_SGIX_texture_lod_bias */ + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif /* GL_SGIX_texture_multi_buffer */ + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif /* GL_SGIX_texture_scale_bias */ + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif /* GL_SGIX_vertex_preclip */ + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif /* GL_SGIX_ycrcb */ + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif /* GL_SGIX_ycrcb_subsample */ + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif /* GL_SGIX_ycrcba */ + +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif /* GL_SGI_color_matrix */ + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableSGI (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameterivSGI (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTableSGI (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTableSGI (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_SGI_color_table */ + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif /* GL_SGI_texture_color_table */ + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFinishTextureSUNX (void); +#endif +#endif /* GL_SUNX_constant_data */ + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif /* GL_SUN_convolution_border_modes */ + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort factor); +GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint factor); +GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat factor); +GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble factor); +GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort factor); +GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint factor); +#endif +#endif /* GL_SUN_global_alpha */ + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif +#endif /* GL_SUN_mesh_array */ + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif /* GL_SUN_slice_accum */ + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint code); +GLAPI void APIENTRY glReplacementCodeusSUN (GLushort code); +GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code); +GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code); +GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code); +GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code); +GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const void **pointer); +#endif +#endif /* GL_SUN_triangle_list */ + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *rc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *rc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif +#endif /* GL_SUN_vertex */ + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif /* GL_WIN_phong_shading */ + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif /* GL_WIN_specular_fog */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/foreign/stb_image.h b/foreign/stb_image.h new file mode 100644 index 0000000..0a9de39 --- /dev/null +++ b/foreign/stb_image.h @@ -0,0 +1,6509 @@ +/* stb_image - v2.08 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) + optimize PNG + fix bug in interlaced PNG with user-specified channel count + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Bug fixes & warning fixes + Sean Barrett (jpeg, png, bmp) Marc LeBlanc + Nicolas Schulz (hdr, psd) Christpher Lloyd + Jonathan Dummer (tga) Dave Moore + Jean-Marc Lienher (gif) Won Chun + Tom Seddon (pic) the Horde3D community + Thatcher Ulrich (psd) Janez Zemva + Ken Miller (pgm, ppm) Jonathan Blow + urraka@github (animated gif) Laurent Gomila + Aruelien Pocheville + Ryamond Barbiero + David Woo + Extensions, features Martin Golini + Jetro Lauha (stbi_info) Roy Eltham + Martin "SpartanJ" Golini (stbi_info) Luke Graham + James "moose2000" Brown (iPhone PNG) Thomas Ruf + Ben "Disch" Wenger (io callbacks) John Bartholomew + Omar Cornut (1/2/4-bit PNG) Ken Hamada + Nicolas Guillemot (vertical flip) Cort Stratton + Richard Mitton (16-bit PSD) Blazej Dariusz Roszkowski + Thibault Reuille + Paul Du Bois + Guillaume George + Jerry Jansson + Hayaki Saito + Johan Duparc + Ronny Chevalier + Optimizations & bugfixes Michal Cichon + Fabian "ryg" Giesen Tero Hanninen + Arseny Kapoulkine Sergio Gonzalez + Cass Everitt + Engin Manap + If your name should be here but Martins Mozeiko + isn't, let Sean know. Joseph Thomson + Phil Jordan + Nathan Reed + Michaelangel007@github + Nick Verigakis + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// + + +#ifndef STBI_NO_STDIO +#include <stdio.h> +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include <stdarg.h> +#include <stddef.h> // ptrdiff_t on osx +#include <stdlib.h> +#include <string.h> + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include <math.h> // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include <stdio.h> +#endif + +#ifndef STBI_ASSERT +#include <assert.h> +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include <stdint.h> +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,sz) realloc(p,sz) +#define STBI_FREE(p) free(p) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) +#define STBI_SSE2 +#include <emmintrin.h> + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include <intrin.h> // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include <arm_neon.h> +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_flip(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_flip(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +#ifndef STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<<n) + 1 +static int const stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767}; + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) +{ + unsigned int k; + int sgn; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return stbi__err("bad component ID","Corrupt JPEG"); + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__rewind(s); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__jpeg j; + j.s = s; + return stbi__jpeg_info_raw(&j, x, y, comp); +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC(z->zout_start, limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncomperssed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + int filter_bytes = img_n; + int width = x; + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*img_n; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; + if (has_trans) + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_out_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a=255; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + offset = stbi__get32le(s); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + bpp = stbi__get16le(s); + if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + STBI_ASSERT(hsz == 108 || hsz == 124); + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if( sz > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + sz = stbi__get8(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + stbi__skip(s,9); + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + sz = stbi__get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi__rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + stbi__get16be(s); // discard palette start + stbi__get16be(s); // discard palette length + stbi__get8(s); // discard bits per palette color entry + stbi__get16be(s); // discard x origin + stbi__get16be(s); // discard y origin + if ( stbi__get16be(s) < 1 ) return 0; // test width + if ( stbi__get16be(s) < 1 ) return 0; // test height + sz = stbi__get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) + res = 0; + else + res = 1; + stbi__rewind(s); + return res; +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp = tga_bits_per_pixel / 8; + int tga_inverted = stbi__get8(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_comp = tga_palette_bits / 8; + } + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = stbi__get8(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB + if (tga_comp >= 3) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int bitdepth; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } else { + // Read the data. + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; y<height; ++y) { + int packet_idx; + + for(packet_idx=0; packet_idx < num_packets; ++packet_idx) { + stbi__pic_packet *packet = &packets[packet_idx]; + stbi_uc *dest = result+y*width*4; + + switch (packet->type) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;x<width;++x, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; i<count; ++i,dest+=4) + stbi__copyval(packet->channel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;i<count;++i, dest += 4) + stbi__copyval(packet->channel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;i<count;++i, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif g; + if (!stbi__gif_header(s, &g, comp, 1)) { + stbi__rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + + u = stbi__gif_load_next(s, &g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } + else if (g.out) + STBI_FREE(g.out); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { + stbi__rewind( s ); + return 0; + } + stbi__skip(s,12); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { + stbi__rewind( s ); + return 0; + } + if (hsz == 12) { + *x = stbi__get16le(s); + *y = stbi__get16le(s); + } else { + *x = stbi__get32le(s); + *y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) { + stbi__rewind( s ); + return 0; + } + *comp = stbi__get16le(s) / 8; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bit PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ diff --git a/foreign/stb_image_resize.h b/foreign/stb_image_resize.h new file mode 100644 index 0000000..088c88a --- /dev/null +++ b/foreign/stb_image_resize.h @@ -0,0 +1,2586 @@ +/* stb_image_resize - v0.90 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb + + Written with emphasis on usability, portability, and efficiency. (No + SIMD or threads, so it be easily outperformed by libs that use those.) + Only scaling and translation is supported, no rotations or shears. + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + QUICKSTART + stbir_resize_uint8( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, num_channels) + stbir_resize_float(...) + stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0) + stbir_resize_uint8_srgb_edgemode( + input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + // WRAP/REFLECT/ZERO + + FULL API + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + SRGB & FLOATING POINT REPRESENTATION + The sRGB functions presume IEEE floating point. If you do not have + IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use + a slower implementation. + + MEMORY ALLOCATION + The resize functions here perform a single memory allocation using + malloc. To control the memory allocation, before the #include that + triggers the implementation, do: + + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... + + Each resize function makes exactly one call to malloc/free, so to use + temp memory, store the temp memory in the context and return that. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + OPTIMIZATION + Define STBIR_SATURATE_INT to compute clamp values in-range using + integer operations instead of float operations. This may be faster + on some platforms. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters + to use, you can change the compile-time defaults with + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are used. For a list of + supported filters see the stbir_filter enum. To add a new filter, + write a filter function and add it to stbir__filter_info_table. + + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: + + #define STBIR_PROGRESS_REPORT(val) some_func(val) + + The parameter val is a float which goes from 0 to 1 as progress is made. + + For example: + + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" + + static void my_progress_report(float progress) + { + printf("Progress: %f%%\n", progress*100); + } + + MAX CHANNELS + If your image has more than 64 channels, define STBIR_MAX_CHANNELS + to the max you'll have. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how + the alpha channel of an image is processed. The important things + to know about this: + + 1. The best mathematically-behaved version of alpha to use is + called "premultiplied alpha", in which the other color channels + have had the alpha value multiplied in. If you use premultiplied + alpha, linear filtering (such as image resampling done by this + library, or performed in texture units on GPUs) does the "right + thing". While premultiplied alpha is standard in the movie CGI + industry, it is still uncommon in the videogame/real-time world. + + If you linearly filter non-premultiplied alpha, strange effects + occur. (For example, the average of 1% opaque bright green + and 99% opaque black produces 50% transparent dark green when + non-premultiplied, whereas premultiplied it produces 50% + transparent near-black. The former introduces green energy + that doesn't exist in the source image.) + + 2. Artists should not edit premultiplied-alpha images; artists + want non-premultiplied alpha images. Thus, art tools generally output + non-premultiplied alpha images. + + 3. You will get best results in most cases by converting images + to premultiplied alpha before processing them mathematically. + + 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the + resizer does not do anything special for the alpha channel; + it is resampled identically to other channels. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. + + 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, + then the resizer weights the contribution of input pixels + based on their alpha values, or, equivalently, it multiplies + the alpha value into the color channels, resamples, then divides + by the resultant alpha value. Input pixels which have alpha=0 do + not contribute at all to output pixels unless _all_ of the input + pixels affecting that output pixel have alpha=0, in which case + the result for that pixel is the same as it would be without + STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for + input images in integer formats. For input images in float format, + input pixels with alpha=0 have no effect, and output pixels + which have alpha=0 will be 0 in all channels. (For float images, + you can manually achieve the same result by adding a tiny epsilon + value to the alpha channel of every image, and then subtracting + or clamping it at the end.) + + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. + + 7. You can separately control whether the alpha channel is + interpreted as linear or affected by the colorspace. By default + it is linear; you almost never want to apply the colorspace. + (For example, graphics hardware does not apply sRGB conversion + to the alpha channel.) + + ADDITIONAL CONTRIBUTORS + Sean Barrett: API design, optimizations + + REVISIONS + 0.90 (2014-09-17) first released version + + LICENSE + + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy, + distribute, and modify this file as you see fit. + + TODO + Don't decode all of the image data when only processing a partial tile + Don't use full-width decode buffers when only processing a partial tile + When processing wide images, break processing into tiles so data fits in L1 cache + Installable filters? + Resize that respects alpha test coverage + (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: + https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) +*/ + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +#else +#include <stdint.h> +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +#endif + +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) +// * input_w is input image width (x-axis), input_h is input image height (y-axis) +// * stride is the offset between successive rows of image data in memory, in bytes. you can +// specify 0 to mean packed continuously in memory +// * alpha channel is treated identically to other channels. +// * colorspace is linear or sRGB as specified by function name +// * returned result is 1 for success or 0 in case of an error. +// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. +// * Memory required grows approximately linearly with input and output size, but with +// discontinuities at input_w == output_w and input_h == output_h. +// * These functions use a "default" resampling filter defined at compile time. To change the filter, +// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE +// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. + +STBIRDEF int stbir_resize_uint8(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_float(const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + + +// The following functions interpret image data as gamma-corrected sRGB. +// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, +// or otherwise provide the index of the alpha channel. Flags value +// of 0 will probably do the right thing if you're not sure what +// the flags mean. + +#define STBIR_ALPHA_CHANNEL_NONE -1 + +// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will +// use alpha-weighted resampling (effectively premultiplying, resampling, +// then unpremultiplying). +#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) +// The specified alpha channel should be handled as gamma-corrected value even +// when doing sRGB operations. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags); + + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode); + +////////////////////////////////////////////////////////////////////////////// +// +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Alpha-channel can be processed separately +// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE +// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) +// * Filter can be selected explicitly +// * uint16 image type +// * sRGB colorspace available for all types +// * context parameter for passing to STBIR_MALLOC + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 +} stbir_filter; + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +// The following functions are all identical except for the type of the image data + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + + + +////////////////////////////////////////////////////////////////////////////// +// +// Full-complexity API +// +// This extends the medium API as follows: +// +// * uint32 image type +// * not typesafe +// * separate filter types for each axis +// * separate edge modes for each axis +// * can specify scale explicitly for subpixel correctness +// * can specify image source tile using texture coordinates + +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , + + STBIR_MAX_TYPES +} stbir_datatype; + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context); + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset); + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1); +// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + + + + + +#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION + +#ifndef STBIR_ASSERT +#include <assert.h> +#define STBIR_ASSERT(x) assert(x) +#endif + +#ifdef STBIR_DEBUG +#define STBIR__DEBUG_ASSERT STBIR_ASSERT +#else +#define STBIR__DEBUG_ASSERT +#endif + +// If you hit this it means I haven't done it yet. +#define STBIR__UNIMPLEMENTED(x) STBIR_ASSERT(!(x)) + +// For memset +#include <string.h> + +#include <math.h> + +#ifndef STBIR_MALLOC +#include <stdlib.h> +#define STBIR_MALLOC(size,c) malloc(size) +#define STBIR_FREE(ptr,c) free(ptr) +#endif + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbir__inline inline +#else +#define stbir__inline +#endif +#else +#define stbir__inline __forceinline +#endif + + +// should produce compiler error if size is wrong +typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBIR__NOTUSED(v) (void)(v) +#else +#define STBIR__NOTUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + +#ifndef STBIR_PROGRESS_REPORT +#define STBIR_PROGRESS_REPORT(float_0_to_1) +#endif + +#ifndef STBIR_MAX_CHANNELS +#define STBIR_MAX_CHANNELS 64 +#endif + +#if STBIR_MAX_CHANNELS > 65536 +#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +// because we store the indices in 16-bit variables +#endif + +// This value is added to alpha just before premultiplication to avoid +// zeroing out color values. It is equivalent to 2^-80. If you don't want +// that behavior (it may interfere if you have floating point images with +// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to +// disable it. +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + + + +#ifdef _MSC_VER +#define STBIR__UNUSED_PARAM(v) (void)(v) +#else +#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) +#endif + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT +}; + +// Kernel function centered at 0 +typedef float (stbir__kernel_fn)(float x, float scale); +typedef float (stbir__support_fn)(float scale); + +typedef struct +{ + stbir__kernel_fn* kernel; + stbir__support_fn* support; +} stbir__filter_info; + +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; + + stbir__contributors* vertical_contributors; + float* vertical_coefficients; + + int decode_buffer_pixels; + float* decode_buffer; + + float* horizontal_buffer; + + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; + float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; +} stbir__info; + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static stbir__inline float stbir__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + +#ifdef STBIR_SATURATE_INT +static stbir__inline stbir_uint8 stbir__saturate8(int x) +{ + if ((unsigned int) x <= 255) + return x; + + if (x < 0) + return 0; + + return 255; +} + +static stbir__inline stbir_uint16 stbir__saturate16(int x) +{ + if ((unsigned int) x <= 65535) + return x; + + if (x < 0) + return 0; + + return 65535; +} +#endif + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +static float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +static float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + +#ifndef STBIR_NON_IEEE_FLOAT +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#else +// sRGB transition values, scaled by 1<<28 +static int stbir__srgb_offset_to_linear_scaled[256] = +{ + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, + 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, + 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, + 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, + 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, + 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, + 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, + 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, + 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, + 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, + 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, + 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float f) +{ + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; + + // Refine the guess with a short binary search. + i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + + return (stbir_uint8) v; +} +#endif + +static float stbir__filter_trapezoid(float x, float scale) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR__DEBUG_ASSERT(scale <= 1); + + x = (float)fabs(x); + + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale) +{ + STBIR__DEBUG_ASSERT(scale <= 1); + return 0.5f + scale / 2; +} + +static float stbir__filter_triangle(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + +static float stbir__filter_cubic(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (4 + x*x*(3*x - 6))/6; + else if (x < 2.0f) + return (8 + x*(-12 + x*(6 - x)))/6; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (16 + x*x*(21 * x - 36))/18; + else if (x < 2.0f) + return (32 + x*(-60 + x*(36 - 7*x)))/18; + + return (0.0f); +} + +static float stbir__support_zero(float s) +{ + STBIR__UNUSED_PARAM(s); + return 0; +} + +static float stbir__support_one(float s) +{ + STBIR__UNUSED_PARAM(s); + return 1; +} + +static float stbir__support_two(float s) +{ + STBIR__UNUSED_PARAM(s); + return 2; +} + +static stbir__filter_info stbir__filter_info_table[] = { + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, +}; + +stbir__inline static int stbir__use_upsampling(float ratio) +{ + return ratio > 1; +} + +stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->vertical_scale); +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) +{ + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); +} + +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) +{ + return stbir__get_filter_pixel_width(filter, scale) / 2; +} + +static int stbir__get_coefficient_width(stbir_filter filter, float scale) +{ + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); +} + +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +{ + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); +} + +static int stbir__get_total_horizontal_coefficients(stbir__info* info) +{ + return info->horizontal_num_contributors + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); +} + +static int stbir__get_total_vertical_coefficients(stbir__info* info) +{ + return info->vertical_num_contributors + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); +} + +static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +{ + return &contributors[n]; +} + +// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, +// if you change it here change it there too. +static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +{ + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; +} + +static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) +{ + switch (edge) + { + case STBIR_EDGE_ZERO: + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED + + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED + } + + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + //return n; // NOTREACHED + + default: + STBIR__UNIMPLEMENTED("Unimplemented edge type"); + return 0; + } +} + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); +} + +// What input pixels contribute to this output pixel? +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +{ + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); +} + +// What output pixels does this input pixel contribute to? +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +{ + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); +} + +static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + + STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; + + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } + + total_filter += coefficient_group[i]; + } + + STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + + STBIR__DEBUG_ASSERT(total_filter > 0.9); + STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; + + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + + STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; + + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } + + STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; + + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } + + STBIR__DEBUG_ASSERT(total > 0.9f); + STBIR__DEBUG_ASSERT(total < 1.1f); + + scale = 1 / total; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) + break; + } + } + + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max, width; + + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; + + contributors[j].n0 += skip; + + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } + + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); + + width = stbir__get_coefficient_width(filter, scale_ratio); + for (i = 0; i < max; i++) + { + if (i + skip >= width) + break; + + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); + } + + continue; + } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); +} + +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; + + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + + stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + + stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + + stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size); + } +} + +static float* stbir__get_decode_buffer(stbir__info* stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; +} + +#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + +static void stbir__decode_scanline(stbir__info* stbir_info, int n) +{ + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + int input_stride_bytes = stbir_info->input_stride_bytes; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; + int decode = STBIR__DECODE(type, colorspace); + + int x = -stbir_info->horizontal_filter_pixel_margin; + + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + } + + break; + + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) + { + int decode_pixel_index = x * channels; + + // If the alpha value is 0 it will clobber the color values. Make sure it's not. + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; +#ifndef STBIR_NO_ALPHA_EPSILON + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } +#endif + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } + + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } +} + +static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + +static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir_info->vertical_filter_pixel_width; + STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } + + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + + stbir_info->ring_buffer_last_scanline = n; + + return ring_buffer; +} + + +static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + //int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; + + STBIR__DEBUG_ASSERT(n1 >= n0); + STBIR__DEBUG_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR__DEBUG_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int input_w = stbir_info->input_w; + //int output_w = stbir_info->output_w; + //int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; + int max_x = input_w + filter_pixel_margin * 2; + + STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info)); + + switch (channels) { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + } + break; + + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + } + break; + + case 3: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + } + break; + + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + } + break; + + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } + break; + } +} + +static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + +// Get the specified scan line from the ring buffer. +static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); +} + + +static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) +{ + int x; + int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + float alpha = encode_buffer[pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[pixel_index + n] *= reciprocal_alpha; + + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). + } + } + + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x=0, num_nonalpha=0; x < channels; ++x) + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + nonalpha[num_nonalpha++] = (stbir_uint16)x; + + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) + #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) + + #ifdef STBIR__SATURATE_INT + #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * 255 )) + #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * 65535)) + #else + #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255 ) + #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535) + #endif + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); + } + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * 4294967295); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; + } + break; + + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; + int contributor = n; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + //int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + output_row_start = n * stbir_info->output_stride_bytes; + + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + + coefficient_counter = 0; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 1; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 2; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 3; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 4; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); +} + +static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + //int output_h = stbir_info->output_h; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; + //void* output_data = stbir_info->output_data; + float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + //int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + + switch (channels) { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; + } + } + } + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); + + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); + } +} + +static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +{ + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; + } + } + } +} + +static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir_info->vertical_filter_pixel_margin; + int max_y = stbir_info->input_h + pixel_margin; + + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; + + stbir__empty_ring_buffer(stbir_info, out_first_scanline); + + stbir__decode_and_resample_downsample(stbir_info, y); + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); + } + + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); +} + +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) +{ + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; +} + +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +{ + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; + + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + + info->horizontal_shift = s0 * info->input_w / (s1 - s0); + info->vertical_shift = t0 * info->input_h / (t1 - t0); + } +} + +static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) +{ + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; +} + +static stbir_uint32 stbir__calculate_memory(stbir__info *info) +{ + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + + info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); + + info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; + + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; +} + +static int stbir__resize_allocated(stbir__info *info, + const void* input_data, int input_stride_in_bytes, + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) +{ + size_t memory_required = stbir__calculate_memory(info); + + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; + +#ifdef STBIR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); +#endif + + STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) + return 0; + + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; + + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + + if (alpha_channel >= info->channels) + return 0; + + STBIR_ASSERT(tempmem); + + if (!tempmem) + return 0; + + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) + return 0; + + memset(tempmem, 0, tempmem_size_in_bytes); + + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; + + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; + + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; + + info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); + + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; + +#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) + + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; + + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + +#undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; + + stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + + STBIR_PROGRESS_REPORT(0); + + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); + + STBIR_PROGRESS_REPORT(1); + +#ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return 1; +} + + +static int stbir__resize_arbitrary( + void *alloc_context, + const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) +{ + stbir__info info; + int result; + size_t memory_required; + void* extra_memory; + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); + + if (!extra_memory) + return 0; + + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); + + STBIR_FREE(extra_memory, alloc_context); + + return result; +} + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset) +{ + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION diff --git a/foreign/stb_image_write.h b/foreign/stb_image_write.h new file mode 100644 index 0000000..ecc285f --- /dev/null +++ b/foreign/stb_image_write.h @@ -0,0 +1,993 @@ +/* stb_image_write - v1.00 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #define _CRT_SECURE_NO_WARNINGS + #define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include <stdio.h> +#endif // STBI_WRITE_NO_STDIO + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,sz) realloc(p,sz) +#define STBIW_FREE(p) free(p) +#endif +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include <assert.h> +#define STBIW_ASSERT(x) assert(x) +#endif + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = (unsigned char) va_arg(v, int); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = (unsigned char) x; + b[1] = (unsigned char) (x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=(unsigned char)x; + b[1]=(unsigned char)(x>>8); + b[2]=(unsigned char)(x>>16); + b[3]=(unsigned char)(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context,d,1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = (unsigned char) (len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = (unsigned char) (len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = (unsigned char) (length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = (unsigned char )(length & 0xff); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, (unsigned char) *bitbuffer); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack! + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, (unsigned char) (s2 >> 8)); + stbiw__sbpush(out, (unsigned char) s2); + stbiw__sbpush(out, (unsigned char) (s1 >> 8)); + stbiw__sbpush(out, (unsigned char) s1); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256]; + unsigned int crc = ~0u; + int i,j; + if (crc_table[1] == 0) + for(i=0; i < 256; i++) + for (crc_table[i]=i, j=0; j < 8; ++j) + crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0); + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return (unsigned char) a; + if (pb <= pc) return (unsigned char) b; + return (unsigned char) c; +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = (unsigned char) ctype[n]; + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ diff --git a/foreign/stb_truetype.h b/foreign/stb_truetype.h new file mode 100644 index 0000000..7138f18 --- /dev/null +++ b/foreign/stb_truetype.h @@ -0,0 +1,2666 @@ +// stb_truetype.h - v1.05 - public domain +// authored from 2009-2014 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// CUSTOMIZATIONS +// +// This particular version has been customized by Allen Webster. +// These customizations include: +// stbtt_GetBakedQuadUnrounded +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// Martins Mozeiko +// Cap Petschulat +// Omar Cornut +// github:aloucks +// Peter LaValle +// +// Misc other: +// Ryan Gordon +// +// VERSION HISTORY +// +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevokable license to copy +// and modify this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +unsigned char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLuint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include <stdio.h> +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_sort() to override this to avoid qsort + #ifndef STBTT_sort + #include <stdlib.h> + #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) + #endif + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include <math.h> + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include <math.h> + #define STBTT_sqrt(x) sqrt(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include <stdlib.h> + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include <assert.h> + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include <string.h> + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include <memory.h> + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata, // you allocate this, it's num_chars long + void *user_allocator_context); // context for overriden STBTT_malloc +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetBakedQuadUnrounded(stbtt_bakedchar *chardata, int pw, int ph, + int char_index, float *xpos, float *ypos, + stbtt_aligned_quad *q, int opengl_fillrule); +// Works just as stbtt_GetBakedQuad but does not round the x,y coordinates q to the pixels + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is // the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_char_in_range; + int num_chars_in_range; + stbtt_packedchar *chardata_for_range; // output +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. + + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s). The default (no oversampling) is achieved by +// h_oversample=1, v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +typedef struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*14); + } + } + return -1; +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + +typedef struct stbtt__active_edge +{ + int x,dx; + float ey; + struct stbtt__active_edge *next; + int valid; +} stbtt__active_edge; + +#define FIXSHIFT 10 +#define FIX (1 << FIXSHIFT) +#define FIXMASK (FIX-1) + +static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(e->y0 <= start_point); + if (!z) return z; + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = -STBTT_ifloor(FIX * -dxdy); + else + z->dx = STBTT_ifloor(FIX * dxdy); + z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); + z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->valid = e->invert ? 1 : -1; + return z; +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->valid; + } else { + int x1 = e->x; w += e->valid; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> FIXSHIFT; + int j = x1 >> FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->valid); + z->valid = 0; + STBTT_free(z, userdata); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + while (active) { + stbtt__active_edge *z = active; + active = active->next; + STBTT_free(z, userdata); + } + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +static int stbtt__edge_compare(const void *p, const void *q) +{ + stbtt__edge *a = (stbtt__edge *) p; + stbtt__edge *b = (stbtt__edge *) q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; + int vsubsample = result->h < 8 ? 15 : 5; + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata, void *user_allocator_callback) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo info; + info.userdata = user_allocator_callback; + if (!stbtt_InitFont(&info, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&info, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&info, first_char + i); + stbtt_GetGlyphHMetrics(&info, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&info, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&info, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + //*xpos += b->xadvance; +} + +STBTT_DEF void stbtt_GetBakedQuadUnrounded(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + + float x = *xpos + b->xoff; + float y = *ypos + b->yoff; + + q->x0 = x + d3d_bias; + q->y0 = y + d3d_bias; + q->x1 = x + b->x1 - b->x0 + d3d_bias; + q->y1 = y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + //*xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +typedef struct +{ + stbrp_coord x,y; + int id,w,h,was_packed; +} stbrp_rect; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + float recip_h = 1.0f / spc->h_oversample; + float recip_v = 1.0f / spc->v_oversample; + float sub_x = stbtt__oversample_shift(spc->h_oversample); + float sub_y = stbtt__oversample_shift(spc->v_oversample); + int i,j,k,n, return_value = 1; + stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars_in_range; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars_in_range; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + int x0,y0,x1,y1; + stbtt_GetCodepointBitmapBoxSubpixel(&info, ranges[i].first_unicode_char_in_range + j, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + stbrp_pack_rects(context, rects, k); + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); + for (j=0; j < ranges[i].num_chars_in_range; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int glyph = stbtt_FindGlyphIndex(&info, ranges[i].first_unicode_char_in_range + j); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(&info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(&info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_char_in_range = first_unicode_char_in_range; + range.num_chars_in_range = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION diff --git a/foreign/x64/freetype.lib b/foreign/x64/freetype.lib new file mode 100644 index 0000000..caf7dca Binary files /dev/null and b/foreign/x64/freetype.lib differ diff --git a/foreign/x64/libfreetype-mac.a b/foreign/x64/libfreetype-mac.a new file mode 100644 index 0000000..180adef Binary files /dev/null and b/foreign/x64/libfreetype-mac.a differ diff --git a/foreign/x86/freetype.lib b/foreign/x86/freetype.lib new file mode 100644 index 0000000..43f135c Binary files /dev/null and b/foreign/x86/freetype.lib differ diff --git a/foreign/x86/libfreetype-mac.a b/foreign/x86/libfreetype-mac.a new file mode 100644 index 0000000..d1e0c40 Binary files /dev/null and b/foreign/x86/libfreetype-mac.a differ diff --git a/res/4coder_icon.ico b/res/4coder_icon.ico new file mode 100644 index 0000000..141c7eb Binary files /dev/null and b/res/4coder_icon.ico differ diff --git a/res/icon.rc b/res/icon.rc new file mode 100644 index 0000000..3b70c83 --- /dev/null +++ b/res/icon.rc @@ -0,0 +1 @@ +main ICON 4coder_icon.ico diff --git a/res/icon.res b/res/icon.res new file mode 100644 index 0000000..dded693 Binary files /dev/null and b/res/icon.res differ diff --git a/res/small_icon.res b/res/small_icon.res new file mode 100644 index 0000000..314c5ec --- /dev/null +++ b/res/small_icon.res @@ -0,0 +1 @@ +Nonsense \ No newline at end of file diff --git a/site_resources/4coder_icon.ico b/site_resources/4coder_icon.ico new file mode 100644 index 0000000..141c7eb Binary files /dev/null and b/site_resources/4coder_icon.ico differ diff --git a/site_resources/4coder_logo_low_green.png b/site_resources/4coder_logo_low_green.png new file mode 100644 index 0000000..681fcf1 Binary files /dev/null and b/site_resources/4coder_logo_low_green.png differ diff --git a/site_resources/screen_1.png b/site_resources/screen_1.png new file mode 100644 index 0000000..a0ebd6e Binary files /dev/null and b/site_resources/screen_1.png differ diff --git a/site_resources/screen_2.png b/site_resources/screen_2.png new file mode 100644 index 0000000..904ffd7 Binary files /dev/null and b/site_resources/screen_2.png differ diff --git a/site_resources/screen_3.png b/site_resources/screen_3.png new file mode 100644 index 0000000..0d3b62f Binary files /dev/null and b/site_resources/screen_3.png differ diff --git a/test_data/input_data/mkdir.txt b/test_data/input_data/mkdir.txt new file mode 100644 index 0000000..f4ad4e3 --- /dev/null +++ b/test_data/input_data/mkdir.txt @@ -0,0 +1,3 @@ +Put this here so git will actually make the directory :D + + diff --git a/test_data/lots_of_files/4coder_buffer_types.h b/test_data/lots_of_files/4coder_buffer_types.h new file mode 100644 index 0000000..26f25cf --- /dev/null +++ b/test_data/lots_of_files/4coder_buffer_types.h @@ -0,0 +1,89 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 23.02.2016 + * + * Types shared by custom and application + * + */ + +// TOP + +#ifndef FRED_BUFFER_TYPES_H +#define FRED_BUFFER_TYPES_H + +typedef struct Full_Cursor{ + int pos; + int line, character; + float unwrapped_x, unwrapped_y; + float wrapped_x, wrapped_y; +} Full_Cursor; + +typedef enum{ + buffer_seek_pos, + buffer_seek_wrapped_xy, + buffer_seek_unwrapped_xy, + buffer_seek_line_char +} Buffer_Seek_Type; + +typedef struct Buffer_Seek{ + Buffer_Seek_Type type; + union{ + struct { int pos; }; + struct { int round_down; float x, y; }; + struct { int line, character; }; + }; +} Buffer_Seek; + +static Buffer_Seek +seek_pos(int pos){ + Buffer_Seek result; + result.type = buffer_seek_pos; + result.pos = pos; + return(result); +} + +static Buffer_Seek +seek_wrapped_xy(float x, float y, int round_down){ + Buffer_Seek result; + result.type = buffer_seek_wrapped_xy; + result.x = x; + result.y = y; + result.round_down = round_down; + return(result); +} + +static Buffer_Seek +seek_unwrapped_xy(float x, float y, int round_down){ + Buffer_Seek result; + result.type = buffer_seek_unwrapped_xy; + result.x = x; + result.y = y; + result.round_down = round_down; + return(result); +} + +static Buffer_Seek +seek_xy(float x, float y, int round_down, int unwrapped){ + Buffer_Seek result; + result.type = unwrapped?buffer_seek_unwrapped_xy:buffer_seek_wrapped_xy; + result.x = x; + result.y = y; + result.round_down = round_down; + return(result); +} + +static Buffer_Seek +seek_line_char(int line, int character){ + Buffer_Seek result; + result.type = buffer_seek_line_char; + result.line = line; + result.character = character; + return(result); +} + + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/4coder_config.h b/test_data/lots_of_files/4coder_config.h new file mode 100644 index 0000000..9784c34 --- /dev/null +++ b/test_data/lots_of_files/4coder_config.h @@ -0,0 +1,87 @@ +/* "4cpp" Open C++ Parser v0.1: Config + no warranty implied; use at your own risk + +NOTES ON USE: + This file is used to configure 4cpp options at the begining of 4cpp files. + It is not meant to be used directly. +*/ + +#ifdef FCPP_NO_CRT +# ifndef FCPP_NO_MALLOC +# define FCPP_NO_MALLOC +# endif +# ifndef FCPP_NO_ASSERT +# define FCPP_NO_ASSERT +# endif +# ifndef FCPP_NO_STRING +# define FCPP_NO_STRING +# endif +#endif + +#ifdef FCPP_FORBID_MALLOC +# define FCPP_NO_MALLOC +#endif + +#ifndef FCPP_NO_MALLOC +# include <stdlib.h> +#endif + +#ifndef FCPP_NO_ASSERT +# include <assert.h> +#endif + +#ifndef FCPP_NO_STRING +# include <string.h> +#endif + +#ifndef FCPP_NO_MALLOC +# ifndef FCPP_GET_MEMORY +# define FCPP_GET_MEMORY malloc +# endif +# ifndef FCPP_FREE_MEMORY +# define FCPP_FREE_MEMORY free +# endif +#else +# ifndef FCPP_FORBID_MALLOC +# ifndef FCPP_GET_MEMORY +# error Missing definition for FCPP_GET_MEMORY +# endif +# ifndef FCPP_FREE_MEMORY +# error Missing definition for FCPP_FREE_MEMORY +# endif +# endif +#endif + +#ifndef FCPP_NO_ASSERT +# ifndef FCPP_ASSERT +# define FCPP_ASSERT assert +# endif +#else +# ifndef FCPP_ASSERT +# define FCPP_ASSERT(x) +# endif +#endif + +#ifndef FCPP_NO_STRING +# ifndef FCPP_MEM_COPY +# define FCPP_MEM_COPY memcpy +# endif +# ifndef FCPP_MEM_MOVE +# define FCPP_MEM_MOVE memmove +# endif +#endif + +#ifndef FCPP_LINK +# ifdef FCPP_EXTERN +# define FCPP_LINK extern +# else +# define FCPP_LINK static +# endif +#endif + +#ifndef DrBegin +#define DrBegin() switch (s.__pc__){ case 0:; +#define DrEnd() default: Assert(!"Invalid __pc__"); } +#define DrYield(pc, n) { s.__pc__ = pc; *state = s; return(n); case pc:; } +#define DrReturn(n) { s.__pc__ = -1; return(n); } +#endif \ No newline at end of file diff --git a/test_data/lots_of_files/4coder_custom.cpp b/test_data/lots_of_files/4coder_custom.cpp new file mode 100644 index 0000000..dca8651 --- /dev/null +++ b/test_data/lots_of_files/4coder_custom.cpp @@ -0,0 +1,1224 @@ +// Set which customization you want to use with this define or write your own +#define Custom_Current Custom_Default + +#define Custom_Default 0 + +// The following customization schemes are power users only: +#define Custom_HandmadeHero 1 + + +// TOP +#include "4coder_custom.h" + +#define FCPP_STRING_IMPLEMENTATION +#include "4coder_string.h" + +#define UseInterfacesThatArePhasingOut 0 +#include "4coder_helper.h" + +#ifndef literal +#define literal(s) (s), (sizeof(s)-1) +#endif + +// NOTE(allen|a3.3): All of your custom ids should be enumerated +// as shown here, they may start at 0, and you can only have +// 2^24 of them so don't be wasteful! +enum My_Maps{ + my_code_map +}; + +HOOK_SIG(my_start){ + exec_command(app, cmdid_open_panel_vsplit); + exec_command(app, cmdid_change_active_panel); + + app->change_theme(app, literal("4coder")); + app->change_font(app, literal("liberation sans")); + + // Theme options: + // "4coder" + // "Handmade Hero" + // "Twilight" + // "Woverine" + // "stb" + + // Font options: + // "liberation sans" + // "liberation mono" + // "hack" + // "cutive mono" + // "inconsolata" + + // no meaning for return + return(0); +} + +HOOK_SIG(my_file_settings){ + // NOTE(allen|a4): In hooks that want parameters, such as this file + // created hook. The file created hook is guaranteed to have only + // and exactly one buffer parameter. In normal command callbacks + // there are no parameter buffers. + Buffer_Summary buffer = app->get_parameter_buffer(app, 0); + assert(buffer.exists); + + int treat_as_code = 0; + + if (buffer.file_name && buffer.size < (16 << 20)){ + String ext = file_extension(make_string(buffer.file_name, buffer.file_name_len)); + if (match(ext, make_lit_string("cpp"))) treat_as_code = 1; + else if (match(ext, make_lit_string("h"))) treat_as_code = 1; + else if (match(ext, make_lit_string("c"))) treat_as_code = 1; + else if (match(ext, make_lit_string("hpp"))) treat_as_code = 1; + } + + push_parameter(app, par_lex_as_cpp_file, treat_as_code); + push_parameter(app, par_wrap_lines, !treat_as_code); + push_parameter(app, par_key_mapid, (treat_as_code)?((int)my_code_map):((int)mapid_file)); + exec_command(app, cmdid_set_settings); + + // no meaning for return + return(0); +} + +unsigned char blink_t = 0; + +HOOK_SIG(my_frame){ + // NOTE(allen|a4): Please use me sparingly! This get's called roughly once every *33 ms* if everything is going well. + // But if you start doing a lot in here, there's nothing 4codes does to stop you from making it a lot slower. + + int result = 0; + Theme_Color theme_color_1[] = { + {Stag_Cursor, 0x00FF00}, + {Stag_At_Cursor, 0x000000} + }; + + Theme_Color theme_color_2[2] = { + {Stag_Cursor, 0x000000}, + {Stag_At_Cursor, 0xFFFFFF} + }; + + Theme_Color *theme_color; + + ++blink_t; + + if (blink_t == 20 || blink_t == 40){ + if (blink_t == 20){ + theme_color = theme_color_2; + } + else{ + theme_color = theme_color_1; + blink_t = 0; + } + + result = 1; + app->set_theme_colors(app, theme_color, 2); + } + + // return non-zero if you do anything that might change the screen! + // 4coder won't redraw unless you tell it you've changed something important. + // If you redraw *all* the time it's going to slow 4coder down and increase power consumption. + return(result); +} + +static void +write_string(Application_Links *app, String string){ + Buffer_Summary buffer = app->get_active_buffer(app); + app->buffer_replace_range(app, &buffer, buffer.buffer_cursor_pos, buffer.buffer_cursor_pos, string.str, string.size); +} + +CUSTOM_COMMAND_SIG(write_increment){ + write_string(app, make_lit_string("++")); +} + +CUSTOM_COMMAND_SIG(write_decrement){ + write_string(app, make_lit_string("--")); +} + +CUSTOM_COMMAND_SIG(write_allen_todo){ + write_string(app, make_lit_string("// TODO(allen): ")); +} + +CUSTOM_COMMAND_SIG(write_allen_note){ + write_string(app, make_lit_string("// NOTE(allen): ")); +} + +static void +basic_seek(Application_Links *app, Command_ID seek_type, unsigned int flags){ + push_parameter(app, par_flags, flags); + exec_command(app, seek_type); +} + +#define SEEK_COMMAND(n, dir, flags)\ +CUSTOM_COMMAND_SIG(seek_##n##_##dir){\ + basic_seek(app, cmdid_seek_##dir, flags);\ +} + +SEEK_COMMAND(whitespace, right, BoundryWhitespace) +SEEK_COMMAND(whitespace, left, BoundryWhitespace) +SEEK_COMMAND(token, right, BoundryToken) +SEEK_COMMAND(token, left, BoundryToken) +SEEK_COMMAND(white_or_token, right, BoundryToken | BoundryWhitespace) +SEEK_COMMAND(white_or_token, left, BoundryToken | BoundryWhitespace) +SEEK_COMMAND(alphanumeric, right, BoundryAlphanumeric) +SEEK_COMMAND(alphanumeric, left, BoundryAlphanumeric) +SEEK_COMMAND(alphanumeric_or_camel, right, BoundryAlphanumeric | BoundryCamelCase) +SEEK_COMMAND(alphanumeric_or_camel, left, BoundryAlphanumeric | BoundryCamelCase) + +static void +long_braces(Application_Links *app, char *text, int size){ + View_Summary view; + Buffer_Summary buffer; + int pos; + + view = app->get_active_view(app); + buffer = app->get_buffer(app, view.buffer_id); + + pos = view.cursor.pos; + app->buffer_replace_range(app, &buffer, pos, pos, text, size); + app->view_set_cursor(app, &view, seek_pos(pos + 2), 1); + + push_parameter(app, par_range_start, pos); + push_parameter(app, par_range_end, pos + size); + push_parameter(app, par_clear_blank_lines, 0); + exec_command(app, cmdid_auto_tab_range); +} + +CUSTOM_COMMAND_SIG(open_long_braces){ + char text[] = "{\n\n}"; + int size = sizeof(text) - 1; + long_braces(app, text, size); +} + +CUSTOM_COMMAND_SIG(open_long_braces_semicolon){ + char text[] = "{\n\n};"; + int size = sizeof(text) - 1; + long_braces(app, text, size); +} + +CUSTOM_COMMAND_SIG(open_long_braces_break){ + char text[] = "{\n\n}break;"; + int size = sizeof(text) - 1; + long_braces(app, text, size); +} + +CUSTOM_COMMAND_SIG(paren_wrap){ + View_Summary view; + Buffer_Summary buffer; + + char text1[] = "("; + int size1 = sizeof(text1) - 1; + + char text2[] = ")"; + int size2 = sizeof(text2) - 1; + + Range range; + int pos; + + view = app->get_active_view(app); + buffer = app->get_active_buffer(app); + + range = get_range(&view); + pos = range.max; + app->buffer_replace_range(app, &buffer, pos, pos, text2, size2); + + pos = range.min; + app->buffer_replace_range(app, &buffer, pos, pos, text1, size1); +} + +CUSTOM_COMMAND_SIG(if0_off){ + View_Summary view; + Buffer_Summary buffer; + + char text1[] = "\n#if 0"; + int size1 = sizeof(text1) - 1; + + char text2[] = "#endif\n"; + int size2 = sizeof(text2) - 1; + + Range range; + int pos; + + view = app->get_active_view(app); + buffer = app->get_active_buffer(app); + + range = get_range(&view); + pos = range.min; + + app->buffer_replace_range(app, &buffer, pos, pos, text1, size1); + + push_parameter(app, par_range_start, pos); + push_parameter(app, par_range_end, pos); + exec_command(app, cmdid_auto_tab_range); + + app->refresh_view(app, &view); + range = get_range(&view); + pos = range.max; + + app->buffer_replace_range(app, &buffer, pos, pos, text2, size2); + + push_parameter(app, par_range_start, pos); + push_parameter(app, par_range_end, pos); + exec_command(app, cmdid_auto_tab_range); +} + +CUSTOM_COMMAND_SIG(backspace_word){ + View_Summary view; + Buffer_Summary buffer; + int pos2, pos1; + + view = app->get_active_view(app); + + pos2 = view.cursor.pos; + exec_command(app, seek_alphanumeric_left); + app->refresh_view(app, &view); + pos1 = view.cursor.pos; + + buffer = app->get_buffer(app, view.buffer_id); + app->buffer_replace_range(app, &buffer, pos1, pos2, 0, 0); +} + +CUSTOM_COMMAND_SIG(snipe_token_or_word){ + View_Summary view; + Buffer_Summary buffer; + int pos1, pos2; + + view = app->get_active_view(app); + + push_parameter(app, par_flags, BoundryToken | BoundryWhitespace); + exec_command(app, cmdid_seek_left); + app->refresh_view(app, &view); + pos1 = view.cursor.pos; + + push_parameter(app, par_flags, BoundryToken | BoundryWhitespace); + exec_command(app, cmdid_seek_right); + app->refresh_view(app, &view); + pos2 = view.cursor.pos; + + Range range = make_range(pos1, pos2); + buffer = app->get_buffer(app, view.buffer_id); + app->buffer_replace_range(app, &buffer, range.start, range.end, 0, 0); +} + +CUSTOM_COMMAND_SIG(switch_to_compilation){ + View_Summary view; + Buffer_Summary buffer; + + char name[] = "*compilation*"; + int name_size = sizeof(name)-1; + + view = app->get_active_view(app); + buffer = app->get_buffer_by_name(app, name, name_size); + + app->view_set_buffer(app, &view, buffer.buffer_id); +} + +CUSTOM_COMMAND_SIG(move_up_10){ + View_Summary view; + float x, y; + + view = app->get_active_view(app); + x = view.preferred_x; + + if (view.unwrapped_lines){ + y = view.cursor.unwrapped_y; + } + else{ + y = view.cursor.wrapped_y; + } + + y -= 10*view.line_height; + + app->view_set_cursor(app, &view, seek_xy(x, y, 0, view.unwrapped_lines), 0); +} + +CUSTOM_COMMAND_SIG(move_down_10){ + View_Summary view; + float x, y; + + view = app->get_active_view(app); + x = view.preferred_x; + + if (view.unwrapped_lines){ + y = view.cursor.wrapped_y; + } + else{ + y = view.cursor.wrapped_y; + } + + y += 10*view.line_height; + + app->view_set_cursor(app, &view, seek_xy(x, y, 0, view.unwrapped_lines), 0); +} + +CUSTOM_COMMAND_SIG(open_file_in_quotes){ + View_Summary view; + Buffer_Summary buffer; + char short_file_name[128]; + int pos, start, end, size; + + view = app->get_active_view(app); + buffer = app->get_buffer(app, view.buffer_id); + pos = view.cursor.pos; + app->buffer_seek_delimiter(app, &buffer, pos, '"', 1, &end); + app->buffer_seek_delimiter(app, &buffer, pos, '"', 0, &start); + + ++start; + size = end - start; + + // NOTE(allen): This check is necessary because app->buffer_read_range + // requiers that the output buffer you provide is at least (end - start) bytes long. + if (size < sizeof(short_file_name)){ + char file_name_[256]; + String file_name = make_fixed_width_string(file_name_); + + app->buffer_read_range(app, &buffer, start, end, short_file_name); + + copy(&file_name, make_string(buffer.file_name, buffer.file_name_len)); + remove_last_folder(&file_name); + append(&file_name, make_string(short_file_name, size)); + + exec_command(app, cmdid_change_active_panel); + push_parameter(app, par_name, expand_str(file_name)); + exec_command(app, cmdid_interactive_open); + } +} + +CUSTOM_COMMAND_SIG(goto_line){ + int line_number; + Query_Bar bar; + char string_space[256]; + + bar.prompt = make_lit_string("Goto Line: "); + bar.string = make_fixed_width_string(string_space); + + if (query_user_number(app, &bar)){ + line_number = str_to_int(bar.string); + active_view_to_line(app, line_number); + } +} + +CUSTOM_COMMAND_SIG(search); +CUSTOM_COMMAND_SIG(reverse_search); + +static void +isearch(Application_Links *app, int start_reversed){ + View_Summary view; + Buffer_Summary buffer; + User_Input in; + Query_Bar bar; + + if (app->start_query_bar(app, &bar, 0) == 0) return; + + Range match; + int reverse = start_reversed; + int pos; + + view = app->get_active_view(app); + buffer = app->get_buffer(app, view.buffer_id); + + pos = view.cursor.pos; + match = make_range(pos, pos); + + char bar_string_space[256]; + bar.string = make_fixed_width_string(bar_string_space); + + String isearch = make_lit_string("I-Search: "); + String rsearch = make_lit_string("Reverse-I-Search: "); + + while (1){ + // NOTE(allen): Change the bar's prompt to match the current direction. + if (reverse) bar.prompt = rsearch; + else bar.prompt = isearch; + + in = app->get_user_input(app, EventOnAnyKey, EventOnEsc | EventOnButton); + if (in.abort) break; + + // NOTE(allen): If we're getting mouse events here it's a 4coder bug, because we + // only asked to intercept key events. + assert(in.type == UserInputKey); + + int made_change = 0; + if (in.key.keycode == '\n' || in.key.keycode == '\t'){ + break; + } + else if (in.key.character && key_is_unmodified(&in.key)){ + append(&bar.string, in.key.character); + made_change = 1; + } + else if (in.key.keycode == key_back){ + --bar.string.size; + made_change = 1; + } + + int step_forward = 0; + int step_backward = 0; + + if (CommandEqual(in.command, search) || + in.key.keycode == key_page_down || in.key.keycode == key_down) step_forward = 1; + if (CommandEqual(in.command, reverse_search) || + in.key.keycode == key_page_up || in.key.keycode == key_up) step_backward = 1; + + int start_pos = pos; + if (step_forward && reverse){ + start_pos = match.start + 1; + pos = start_pos; + reverse = 0; + step_forward = 0; + } + if (step_backward && !reverse){ + start_pos = match.start - 1; + pos = start_pos; + reverse = 1; + step_backward = 0; + } + + if (in.key.keycode != key_back){ + int new_pos; + if (reverse){ + app->buffer_seek_string(app, &buffer, start_pos - 1, bar.string.str, bar.string.size, 0, &new_pos); + if (new_pos >= 0){ + if (step_backward){ + pos = new_pos; + start_pos = new_pos; + app->buffer_seek_string(app, &buffer, start_pos - 1, bar.string.str, bar.string.size, 0, &new_pos); + if (new_pos < 0) new_pos = start_pos; + } + match.start = new_pos; + match.end = match.start + bar.string.size; + } + } + else{ + app->buffer_seek_string(app, &buffer, start_pos + 1, bar.string.str, bar.string.size, 1, &new_pos); + if (new_pos < buffer.size){ + if (step_forward){ + pos = new_pos; + start_pos = new_pos; + app->buffer_seek_string(app, &buffer, start_pos + 1, bar.string.str, bar.string.size, 1, &new_pos); + if (new_pos >= buffer.size) new_pos = start_pos; + } + match.start = new_pos; + match.end = match.start + bar.string.size; + } + } + } + else{ + match.end = match.start + bar.string.size; + } + + app->view_set_highlight(app, &view, match.start, match.end, 1); + } + app->view_set_highlight(app, &view, 0, 0, 0); + if (in.abort) return; + + app->view_set_cursor(app, &view, seek_pos(match.min), 1); +} + +CUSTOM_COMMAND_SIG(search){ + isearch(app, 0); +} + +CUSTOM_COMMAND_SIG(reverse_search){ + isearch(app, 1); +} + +CUSTOM_COMMAND_SIG(rewrite_as_single_caps){ + View_Summary view; + Buffer_Summary buffer; + Range range; + String string; + int is_first, i; + + exec_command(app, seek_token_left); + view = app->get_active_view(app); + range.min = view.cursor.pos; + + exec_command(app, seek_token_right); + app->refresh_view(app, &view); + range.max = view.cursor.pos; + + string.str = (char*)app->memory; + string.size = range.max - range.min; + assert(string.size < app->memory_size); + + buffer = app->get_buffer(app, view.buffer_id); + app->buffer_read_range(app, &buffer, range.min, range.max, string.str); + + is_first = 1; + for (i = 0; i < string.size; ++i){ + if (char_is_alpha_true(string.str[i])){ + if (is_first) is_first = 0; + else string.str[i] = char_to_lower(string.str[i]); + } + else{ + is_first = 1; + } + } + + app->buffer_replace_range(app, &buffer, range.min, range.max, string.str, string.size); +} + +// TODO(allen): +// get range by specific "word" type (for example "get token range") +// read range by specific "word" type +// Dream API for rewrite_as_single_caps: +#if 0 +{ + rewrite = get_rewrite(app, ByToken); + string = get_rewrite_string(app, &rewrite, app->memory, app->memory_size); + + is_first = 1; + for (i = 0; i < string.size; ++i){ + if (char_is_alpha_true(string.str[i])){ + if (is_first) is_first = 0; + else string.str[i] = char_to_lower(string.str[i]); + } + else{ + is_first = 1; + } + } + + do_rewrite(app, &rewrite, string); +} +#endif + +CUSTOM_COMMAND_SIG(replace_in_range){ + Query_Bar replace; + char replace_space[1024]; + replace.prompt = make_lit_string("Replace: "); + replace.string = make_fixed_width_string(replace_space); + + Query_Bar with; + char with_space[1024]; + with.prompt = make_lit_string("With: "); + with.string = make_fixed_width_string(with_space); + + if (!query_user_string(app, &replace)) return; + if (replace.string.size == 0) return; + + if (!query_user_string(app, &with)) return; + + String r, w; + r = replace.string; + w = with.string; + + Buffer_Summary buffer; + View_Summary view; + + view = app->get_active_view(app); + buffer = app->get_buffer(app, view.buffer_id); + + Range range = get_range(&view); + + int pos, new_pos; + pos = range.min; + app->buffer_seek_string(app, &buffer, pos, r.str, r.size, 1, &new_pos); + + while (new_pos + r.size < range.end){ + app->buffer_replace_range(app, &buffer, new_pos, new_pos + r.size, w.str, w.size); + range = get_range(&view); + pos = new_pos + w.size; + app->buffer_seek_string(app, &buffer, pos, r.str, r.size, 1, &new_pos); + } +} + +CUSTOM_COMMAND_SIG(query_replace){ + Query_Bar replace; + char replace_space[1024]; + replace.prompt = make_lit_string("Replace: "); + replace.string = make_fixed_width_string(replace_space); + + Query_Bar with; + char with_space[1024]; + with.prompt = make_lit_string("With: "); + with.string = make_fixed_width_string(with_space); + + if (!query_user_string(app, &replace)) return; + if (replace.string.size == 0) return; + + if (!query_user_string(app, &with)) return; + + String r, w; + r = replace.string; + w = with.string; + + Query_Bar bar; + Buffer_Summary buffer; + View_Summary view; + int pos, new_pos; + + bar.prompt = make_lit_string("Replace? (y)es, (n)ext, (esc)\n"); + bar.string = empty_string(); + + app->start_query_bar(app, &bar, 0); + + view = app->get_active_view(app); + buffer = app->get_buffer(app, view.buffer_id); + + pos = view.cursor.pos; + app->buffer_seek_string(app, &buffer, pos, r.str, r.size, 1, &new_pos); + + User_Input in = {0}; + while (new_pos < buffer.size){ + Range match = make_range(new_pos, new_pos + r.size); + app->view_set_highlight(app, &view, match.min, match.max, 1); + + in = app->get_user_input(app, EventOnAnyKey, EventOnButton); + if (in.abort || in.key.keycode == key_esc || !key_is_unmodified(&in.key)) break; + + if (in.key.character == 'y' || in.key.character == 'Y' || in.key.character == '\n' || in.key.character == '\t'){ + app->buffer_replace_range(app, &buffer, match.min, match.max, w.str, w.size); + pos = match.start + w.size; + } + else{ + pos = match.max; + } + + app->buffer_seek_string(app, &buffer, pos, r.str, r.size, 1, &new_pos); + } + + app->view_set_highlight(app, &view, 0, 0, 0); + if (in.abort) return; + + app->view_set_cursor(app, &view, seek_pos(pos), 1); +} + +CUSTOM_COMMAND_SIG(close_all_code){ + String extension; + Buffer_Summary buffer; + + for (buffer = app->get_buffer_first(app); + buffer.exists; + app->get_buffer_next(app, &buffer)){ + + extension = file_extension(make_string(buffer.file_name, buffer.file_name_len)); + if (match(extension, make_lit_string("cpp")) || + match(extension, make_lit_string("hpp")) || + match(extension, make_lit_string("c")) || + match(extension, make_lit_string("h"))){ + // + push_parameter(app, par_buffer_id, buffer.buffer_id); + exec_command(app, cmdid_kill_buffer); + } + } +} + +CUSTOM_COMMAND_SIG(open_all_code){ + // NOTE(allen|a3.4.4): This method of getting the hot directory works + // because this custom.cpp gives no special meaning to app->memory + // and doesn't set up a persistent allocation system within app->memory. + // push_directory isn't a very good option since it's tied to the parameter + // stack, so I am phasing that idea out now. + String dir = make_string(app->memory, 0, app->memory_size); + dir.size = app->directory_get_hot(app, dir.str, dir.memory_size); + int dir_size = dir.size; + + // NOTE(allen|a3.4.4): Here we get the list of files in this directory. + // Notice that we free_file_list at the end. + File_List list = app->get_file_list(app, dir.str, dir.size); + + for (int i = 0; i < list.count; ++i){ + File_Info *info = list.infos + i; + if (!info->folder){ + String extension = file_extension(info->filename); + if (match(extension, make_lit_string("cpp")) || + match(extension, make_lit_string("hpp")) || + match(extension, make_lit_string("c")) || + match(extension, make_lit_string("h"))){ + // NOTE(allen): There's no way in the 4coder API to use relative + // paths at the moment, so everything should be full paths. Which is + // managable. Here simply set the dir string size back to where it + // was originally, so that new appends overwrite old ones. + dir.size = dir_size; + append(&dir, info->filename); + push_parameter(app, par_name, dir.str, dir.size); + //push_parameter(app, par_do_in_background, 1); + exec_command(app, cmdid_interactive_open); + } + } + } + + app->free_file_list(app, list); +} + +CUSTOM_COMMAND_SIG(execute_any_cli){ + Query_Bar bar_out, bar_cmd; + String hot_directory; + char space[1024], more_space[1024], even_more_space[1024]; + + bar_out.prompt = make_lit_string("Output Buffer: "); + bar_out.string = make_fixed_width_string(space); + if (!query_user_string(app, &bar_out)) return; + + bar_cmd.prompt = make_lit_string("Command: "); + bar_cmd.string = make_fixed_width_string(more_space); + if (!query_user_string(app, &bar_cmd)) return; + + hot_directory = make_fixed_width_string(even_more_space); + hot_directory.size = app->directory_get_hot(app, hot_directory.str, hot_directory.memory_size); + + push_parameter(app, par_flags, CLI_OverlapWithConflict); + push_parameter(app, par_name, bar_out.string.str, bar_out.string.size); + push_parameter(app, par_cli_path, hot_directory.str, hot_directory.size); + push_parameter(app, par_cli_command, bar_cmd.string.str, bar_cmd.string.size); + exec_command(app, cmdid_command_line); +} + +CUSTOM_COMMAND_SIG(execute_arbitrary_command){ + // NOTE(allen): This isn't a super powerful version of this command, I will expand + // upon it so that it has all the cmdid_* commands by default. However, with this + // as an example you have everything you need to make it work already. You could + // even use app->memory to create a hash table in the start hook. + Query_Bar bar; + char space[1024]; + bar.prompt = make_lit_string("Command: "); + bar.string = make_fixed_width_string(space); + + if (!query_user_string(app, &bar)) return; + + // NOTE(allen): Here I chose to end this query bar because when I call another + // command it might ALSO have query bars and I don't want this one hanging + // around at that point. Since the bar exists on my stack the result of the query + // is still available in bar.string though. + app->end_query_bar(app, &bar, 0); + + if (match(bar.string, make_lit_string("open all code"))){ + exec_command(app, open_all_code); + } + else if(match(bar.string, make_lit_string("close all code"))){ + exec_command(app, close_all_code); + } + else if (match(bar.string, make_lit_string("open menu"))){ + exec_command(app, cmdid_open_menu); + } + else if (match(bar.string, make_lit_string("dos lines"))){ + exec_command(app, cmdid_eol_dosify); + } + else if (match(bar.string, make_lit_string("nix lines"))){ + exec_command(app, cmdid_eol_nixify); + } + else{ + // TODO(allen): feedback message + } +} + +CUSTOM_COMMAND_SIG(open_in_other){ + exec_command(app, cmdid_change_active_panel); + exec_command(app, cmdid_interactive_open); +} + +CUSTOM_COMMAND_SIG(open_my_files){ + // NOTE(allen|a3.1): EXAMPLE probably not useful in practice. + // + // The command cmdid_interactive_open can now open + // a file specified on the parameter stack. If the file does not exist + // cmdid_interactive_open behaves as usual. If par_do_in_background + // is set to true the command is prevented from changing the view under + // any circumstance. + push_parameter(app, par_name, literal("w:/4ed/data/test/basic.cpp")); + exec_command(app, cmdid_interactive_open); + + exec_command(app, cmdid_change_active_panel); + + char my_file[256]; + int my_file_len; + + my_file_len = sizeof("w:/4ed/data/test/basic.txt") - 1; + for (int i = 0; i < my_file_len; ++i){ + my_file[i] = ("w:/4ed/data/test/basic.txt")[i]; + } + + // NOTE(allen|a3.1): null terminators are not needed for strings. + push_parameter(app, par_name, my_file, my_file_len); + exec_command(app, cmdid_interactive_open); + + exec_command(app, cmdid_change_active_panel); +} + +CUSTOM_COMMAND_SIG(build_at_launch_location){ + // NOTE(allen|a3.3): EXAMPLE probably not all that useful in practice. + // + // An example of calling build by setting all + // parameters directly. This only works if build.bat can be called + // from the directory the application is launched at. + push_parameter(app, par_flags, CLI_OverlapWithConflict); + push_parameter(app, par_name, literal("*compilation*")); + push_parameter(app, par_cli_path, literal(".")); + push_parameter(app, par_cli_command, literal("build")); + exec_command(app, cmdid_command_line); +} + +CUSTOM_COMMAND_SIG(build_search){ + // NOTE(allen|a3.3): An example of traversing the filesystem through parent + // directories looking for a file, in this case a batch file to execute. + // + // + // Step 1: Grab all of the user memory (or, you know, less if you've got better + // thing to do with some of it). Make a string and store the hot directory in it. + // + // Step 2: app->file_exists queries the file system to see if "<somedir>/build.bat" exists. + // If it does exist several parameters are pushed and cmdid_command_line is executed: + // - par_flags: flags for specifiying behaviors + // CLI_OverlapWithConflict - (on by default) if another CLI is still using the output buffer + // that process is detached from the buffer and this process executes outputing to the buffer + // CLI_AlwaysBindToView - if set, the current view always switches to the output buffer + // even if the output buffer is open in another view + // + // - par_name: the name of the buffer to fill with the output from the process + // - par_buffer_id: the buffer_id of the buffer to to fill with output + // If both are set buffer_id is used and the name is ignored. + // If neither is set the command runs without storing output anywhere. + // + // - par_cli_path: sets the path from which the command is executed + // If this parameter is unset the command runs from the hot directory. + // + // - par_cli_command: sets the actual command to be executed, this can be almost any + // command that you could execute through a command line interface. + // If this parameter is unset the command get's it's command from the range between + // the mark and cursor. + // + // Step 3: If the batch file did not exist change the dir string to the parent directory using + // app->directory_cd. The cd function can also be used to navigate to subdirectories. + // It returns true if it can actually move in the specified direction, and false otherwise. + // + // This doesn't actually change the hot directory of 4coder, it's only effect is to + // modify the string you passed in to reflect the change in directory if that change was possible. + + int keep_going = 1; + int old_size; + String dir = make_string(app->memory, 0, app->memory_size); + dir.size = app->directory_get_hot(app, dir.str, dir.memory_size); + + while (keep_going){ + old_size = dir.size; + append(&dir, "build.bat"); + + if (app->file_exists(app, dir.str, dir.size)){ + dir.size = old_size; + + push_parameter(app, par_flags, CLI_OverlapWithConflict); + push_parameter(app, par_name, literal("*compilation*")); + push_parameter(app, par_cli_path, dir.str, dir.size); + + if (append(&dir, "build")){ + push_parameter(app, par_cli_command, dir.str, dir.size); + exec_command(app, cmdid_command_line); + } + else{ + app->clear_parameters(app); + } + + return; + } + dir.size = old_size; + + if (app->directory_cd(app, dir.str, &dir.size, dir.memory_size, literal("..")) == 0){ + keep_going = 0; + } + } + + // TODO(allen): feedback message - couldn't find build.bat +} + +CUSTOM_COMMAND_SIG(auto_tab_line_at_cursor){ + View_Summary view = app->get_active_view(app); + push_parameter(app, par_range_start, view.cursor.pos); + push_parameter(app, par_range_end, view.cursor.pos); + push_parameter(app, par_clear_blank_lines, 0); + exec_command(app, cmdid_auto_tab_range); +} + +CUSTOM_COMMAND_SIG(auto_tab_whole_file){ + Buffer_Summary buffer = app->get_active_buffer(app); + push_parameter(app, par_range_start, 0); + push_parameter(app, par_range_end, buffer.size); + exec_command(app, cmdid_auto_tab_range); +} + +CUSTOM_COMMAND_SIG(write_and_auto_tab){ + exec_command(app, cmdid_write_character); + exec_command(app, auto_tab_line_at_cursor); +} + +// NOTE(allen|a4) See 4coder_styles.h for a list of available style tags. +// There are style tags corresponding to every color in the theme editor. +CUSTOM_COMMAND_SIG(improve_theme){ + Theme_Color colors[] = { + {Stag_Bar, 0xFF0088}, + {Stag_Margin, 0x880088}, + {Stag_Margin_Hover, 0xAA0088}, + {Stag_Margin_Active, 0xDD0088}, + {Stag_Cursor, 0xFF0000}, + }; + + int count = ArrayCount(colors); + + app->set_theme_colors(app, colors, count); +} + +CUSTOM_COMMAND_SIG(ruin_theme){ + Theme_Color colors[] = { + {Stag_Bar, 0x888888}, + {Stag_Margin, 0x181818}, + {Stag_Margin_Hover, 0x252525}, + {Stag_Margin_Active, 0x323232}, + {Stag_Cursor, 0x00EE00}, + }; + + int count = ArrayCount(colors); + + app->set_theme_colors(app, colors, count); +} + +// NOTE(allen|a4): scroll rule information +// +// The parameters: +// target_x, target_y +// This is where the view would like to be for the purpose of +// following the cursor, doing mouse wheel work, etc. +// +// scroll_x, scroll_y +// These are pointers to where the scrolling actually is. If you bind +// the scroll rule it is you have to update these in some way to move +// the actual location of the scrolling. +// +// view_id +// This corresponds to which view is computing it's new scrolling position. +// This id DOES correspond to the views that View_Summary contains. +// This will always be between 1 and 16 (0 is a null id). +// See below for an example of having state that carries across scroll udpates. +// +// is_new_target +// If the target of the view is different from the last target in either x or y +// this is true, otherwise it is false. +// +// The return: +// Should be true if and only if scroll_x or scroll_y are changed. +// +// Don't try to use the app pointer in a scroll rule, you're asking for trouble. +// +// If you don't bind scroll_rule, nothing bad will happen, yo will get default +// 4coder scrolling behavior. +// + +struct Scroll_Velocity{ + float x, y; +}; + +Scroll_Velocity scroll_velocity_[16] = {0}; +Scroll_Velocity *scroll_velocity = scroll_velocity_ - 1; + +static int +smooth_camera_step(float target, float *current, float *vel, float S, float T){ + int result = 0; + float curr = *current; + float v = *vel; + if (curr != target){ + if (curr > target - .1f && curr < target + .1f){ + curr = target; + v = 1.f; + } + else{ + float L = curr + T*(target - curr); + + int sign = (target > curr) - (target < curr); + float V = curr + sign*v; + + if (sign > 0) curr = (L<V)?(L):(V); + else curr = (L>V)?(L):(V); + + if (curr == V){ + v *= S; + } + } + + *current = curr; + *vel = v; + result = 1; + } + return result; +} + +SCROLL_RULE_SIG(smooth_scroll_rule){ + Scroll_Velocity *velocity = scroll_velocity + view_id; + int result = 0; + if (velocity->x == 0.f){ + velocity->x = 1.f; + velocity->y = 1.f; + } + + if (smooth_camera_step(target_y, scroll_y, &velocity->y, 40.f, 1.f/4.f)){ + result = 1; + } + if (smooth_camera_step(target_x, scroll_x, &velocity->x, 40.f, 1.f/4.f)){ + result = 1; + } + + return(result); +} + +#if Custom_Current == Custom_HandmadeHero +# include "power/4coder_handmade_hero.cpp" +#endif + +extern "C" GET_BINDING_DATA(get_bindings){ + Bind_Helper context_actual = begin_bind_helper(data, size); + Bind_Helper *context = &context_actual; + +#if Custom_Current == Custom_HandmadeHero + casey_get_bindings(context); +#else + + // NOTE(allen|a3.1): Hooks have no loyalties to maps. All hooks are global + // and once set they always apply, regardless of what map is active. + set_hook(context, hook_start, my_start); + set_hook(context, hook_open_file, my_file_settings); + //set_hook(context, hook_frame, my_frame); // Example of a frame hook, but disabled by default. + + set_scroll_rule(context, smooth_scroll_rule); + + begin_map(context, mapid_global); + + bind(context, 'p', MDFR_CTRL, cmdid_open_panel_vsplit); + bind(context, '_', MDFR_CTRL, cmdid_open_panel_hsplit); + bind(context, 'P', MDFR_CTRL, cmdid_close_panel); + bind(context, 'n', MDFR_CTRL, cmdid_interactive_new); + bind(context, 'o', MDFR_CTRL, cmdid_interactive_open); + bind(context, ',', MDFR_CTRL, cmdid_change_active_panel); + bind(context, 'k', MDFR_CTRL, cmdid_interactive_kill_buffer); + bind(context, 'i', MDFR_CTRL, cmdid_interactive_switch_buffer); + bind(context, 'c', MDFR_ALT, cmdid_open_color_tweaker); + bind(context, 'o', MDFR_ALT, open_in_other); + + bind(context, 'm', MDFR_ALT, build_search); + bind(context, ',', MDFR_ALT, switch_to_compilation); + bind(context, 'x', MDFR_ALT, execute_arbitrary_command); + bind(context, 'z', MDFR_ALT, execute_any_cli); + + // NOTE(allen): These callbacks may not actually be useful to you, but + // go look at them and see what they do. + bind(context, 'M', MDFR_ALT | MDFR_CTRL, open_my_files); + bind(context, 'M', MDFR_ALT, build_at_launch_location); + + bind(context, '`', MDFR_ALT, improve_theme); + bind(context, '~', MDFR_ALT, ruin_theme); + + end_map(context); + + + begin_map(context, my_code_map); + + // NOTE(allen|a3.1): Set this map (my_code_map == mapid_user_custom) to + // inherit from mapid_file. When searching if a key is bound + // in this map, if it is not found here it will then search mapid_file. + // + // If this is not set, it defaults to mapid_global. + inherit_map(context, mapid_file); + + // NOTE(allen|a3.1): Children can override parent's bindings. + bind(context, key_right, MDFR_CTRL, seek_alphanumeric_or_camel_right); + bind(context, key_left, MDFR_CTRL, seek_alphanumeric_or_camel_left); + + // NOTE(allen|a3.2): Specific keys can override vanilla keys, + // and write character writes whichever character corresponds + // to the key that triggered the command. + bind(context, '\n', MDFR_NONE, write_and_auto_tab); + bind(context, '}', MDFR_NONE, write_and_auto_tab); + bind(context, ')', MDFR_NONE, write_and_auto_tab); + bind(context, ']', MDFR_NONE, write_and_auto_tab); + bind(context, ';', MDFR_NONE, write_and_auto_tab); + bind(context, '#', MDFR_NONE, write_and_auto_tab); + + bind(context, '\t', MDFR_NONE, cmdid_word_complete); + bind(context, '\t', MDFR_CTRL, cmdid_auto_tab_range); + bind(context, '\t', MDFR_SHIFT, auto_tab_line_at_cursor); + + bind(context, '=', MDFR_CTRL, write_increment); + bind(context, '-', MDFR_CTRL, write_decrement); + bind(context, 't', MDFR_ALT, write_allen_todo); + bind(context, 'n', MDFR_ALT, write_allen_note); + bind(context, '[', MDFR_CTRL, open_long_braces); + bind(context, '{', MDFR_CTRL, open_long_braces_semicolon); + bind(context, '}', MDFR_CTRL, open_long_braces_break); + bind(context, '9', MDFR_CTRL, paren_wrap); + bind(context, 'i', MDFR_ALT, if0_off); + bind(context, '1', MDFR_ALT, open_file_in_quotes); + + end_map(context); + + + begin_map(context, mapid_file); + + // NOTE(allen|a3.4.4): Binding this essentially binds + // all key combos that would normally insert a character + // into a buffer. If the code for the key is not an enum + // value such as key_left or key_back then it is a vanilla key. + // It is possible to override this binding for individual keys. + bind_vanilla_keys(context, cmdid_write_character); + + bind(context, key_left, MDFR_NONE, cmdid_move_left); + bind(context, key_right, MDFR_NONE, cmdid_move_right); + bind(context, key_del, MDFR_NONE, cmdid_delete); + bind(context, key_back, MDFR_NONE, cmdid_backspace); + bind(context, key_up, MDFR_NONE, cmdid_move_up); + bind(context, key_down, MDFR_NONE, cmdid_move_down); + bind(context, key_end, MDFR_NONE, cmdid_seek_end_of_line); + bind(context, key_home, MDFR_NONE, cmdid_seek_beginning_of_line); + bind(context, key_page_up, MDFR_NONE, cmdid_page_up); + bind(context, key_page_down, MDFR_NONE, cmdid_page_down); + + bind(context, key_right, MDFR_CTRL, seek_whitespace_right); + bind(context, key_left, MDFR_CTRL, seek_whitespace_left); + bind(context, key_up, MDFR_CTRL, cmdid_seek_whitespace_up); + bind(context, key_down, MDFR_CTRL, cmdid_seek_whitespace_down); + + bind(context, key_up, MDFR_ALT, move_up_10); + bind(context, key_down, MDFR_ALT, move_down_10); + + bind(context, key_back, MDFR_CTRL, backspace_word); + bind(context, key_back, MDFR_ALT, snipe_token_or_word); + + bind(context, ' ', MDFR_CTRL, cmdid_set_mark); + bind(context, 'm', MDFR_CTRL, cmdid_cursor_mark_swap); + bind(context, 'c', MDFR_CTRL, cmdid_copy); + bind(context, 'x', MDFR_CTRL, cmdid_cut); + bind(context, 'v', MDFR_CTRL, cmdid_paste); + bind(context, 'V', MDFR_CTRL, cmdid_paste_next); + bind(context, 'Z', MDFR_CTRL, cmdid_timeline_scrub); + bind(context, 'z', MDFR_CTRL, cmdid_undo); + bind(context, 'y', MDFR_CTRL, cmdid_redo); + bind(context, 'h', MDFR_CTRL, cmdid_history_backward); + bind(context, 'H', MDFR_CTRL, cmdid_history_forward); + bind(context, 'd', MDFR_CTRL, cmdid_delete_range); + bind(context, 'l', MDFR_CTRL, cmdid_toggle_line_wrap); + bind(context, 'L', MDFR_CTRL, cmdid_toggle_endline_mode); + bind(context, 'u', MDFR_CTRL, cmdid_to_uppercase); + bind(context, 'j', MDFR_CTRL, cmdid_to_lowercase); + bind(context, '?', MDFR_CTRL, cmdid_toggle_show_whitespace); + + bind(context, '~', MDFR_CTRL, cmdid_clean_all_lines); + bind(context, '1', MDFR_CTRL, cmdid_eol_dosify); + bind(context, '!', MDFR_CTRL, cmdid_eol_nixify); + + bind(context, 'f', MDFR_CTRL, search); + bind(context, 'r', MDFR_CTRL, reverse_search); + bind(context, 'g', MDFR_CTRL, goto_line); + bind(context, 'q', MDFR_CTRL, query_replace); + bind(context, 'a', MDFR_CTRL, replace_in_range); + bind(context, 's', MDFR_ALT, rewrite_as_single_caps); + + bind(context, 'K', MDFR_CTRL, cmdid_kill_buffer); + bind(context, 'O', MDFR_CTRL, cmdid_reopen); + bind(context, 'w', MDFR_CTRL, cmdid_interactive_save_as); + bind(context, 's', MDFR_CTRL, cmdid_save); + + bind(context, '\n', MDFR_SHIFT, write_and_auto_tab); + bind(context, ' ', MDFR_SHIFT, cmdid_write_character); + + end_map(context); +#endif + + end_bind_helper(context); + + return context->write_total; +} + + diff --git a/test_data/lots_of_files/4coder_custom.h b/test_data/lots_of_files/4coder_custom.h new file mode 100644 index 0000000..ac2a8bb --- /dev/null +++ b/test_data/lots_of_files/4coder_custom.h @@ -0,0 +1,587 @@ + +#include "4coder_version.h" +#include "4coder_keycodes.h" +#include "4coder_style.h" +#include "4coder_buffer_types.h" + +#ifndef FRED_STRING_STRUCT +#define FRED_STRING_STRUCT +typedef struct String{ + char *str; + int size; + int memory_size; +} String; + +typedef struct Offset_String{ + int offset; + int size; +} Offset_String; +#endif + +typedef unsigned char Code; + +typedef enum{ + MDFR_SHIFT_INDEX, + MDFR_CONTROL_INDEX, + MDFR_ALT_INDEX, + MDFR_CAPS_INDEX, + // always last + MDFR_INDEX_COUNT +} Key_Control; + +typedef struct Key_Event_Data{ + Code keycode; + Code character; + Code character_no_caps_lock; + + char modifiers[MDFR_INDEX_COUNT]; +} Key_Event_Data; + +typedef struct Mouse_State{ + char l, r; + char press_l, press_r; + char release_l, release_r; + char wheel; + char out_of_window; + int x, y; +} Mouse_State; + + +typedef union Range{ + struct{ + int min, max; + }; + struct{ + int start, end; + }; +} Range; + +inline Range +make_range(int p1, int p2){ + Range range; + if (p1 < p2){ + range.min = p1; + range.max = p2; + } + else{ + range.min = p2; + range.max = p1; + } + return(range); +} + + +typedef enum Dynamic_Type{ + dynamic_type_int, + dynamic_type_string, + // never below this + dynamic_type_count +} Dynamic_Type; + +typedef struct Dynamic{ + int type; + union{ + struct{ + int str_len; + char *str_value; + }; + int int_value; + }; +} Dynamic; + +inline Dynamic +dynamic_int(int x){ + Dynamic result; + result.type = dynamic_type_int; + result.int_value = x; + return result; +} + +inline Dynamic +dynamic_string(const char *string, int len){ + Dynamic result; + result.type = dynamic_type_string; + result.str_len = len; + result.str_value = (char*)(string); + return result; +} + +inline int +dynamic_to_int(Dynamic *dynamic){ + int result = 0; + if (dynamic->type == dynamic_type_int){ + result = dynamic->int_value; + } + return result; +} + +inline char* +dynamic_to_string(Dynamic *dynamic, int *len){ + char *result = 0; + if (dynamic->type == dynamic_type_string){ + result = dynamic->str_value; + *len = dynamic->str_len; + } + return result; +} + +inline int +dynamic_to_bool(Dynamic *dynamic){ + int result = 0; + if (dynamic->type == dynamic_type_int){ + result = (dynamic->int_value != 0); + } + else{ + result = 1; + } + return result; +} + +typedef struct File_Info{ + String filename; + int folder; +} File_Info; + +typedef struct File_List{ + // Ignore this, it's for internal stuff. + void *block; + + // The list of files and folders. + File_Info *infos; + int count; + + // Ignore this, it's for internal stuff. + int block_size; +} File_List; + +#define MDFR_NONE 0x0 +#define MDFR_CTRL 0x1 +#define MDFR_ALT 0x2 +#define MDFR_SHIFT 0x4 + +enum Command_ID{ + cmdid_null, + cmdid_write_character, + cmdid_seek_left, + cmdid_seek_right, + cmdid_seek_whitespace_up, + cmdid_seek_whitespace_down, + cmdid_word_complete, + cmdid_set_mark, + cmdid_copy, + cmdid_cut, + cmdid_paste, + cmdid_paste_next, + cmdid_delete_range, + cmdid_timeline_scrub, + cmdid_undo, + cmdid_redo, + cmdid_history_backward, + cmdid_history_forward, + cmdid_interactive_new, + cmdid_interactive_open, + cmdid_reopen, + cmdid_save, + cmdid_interactive_save_as, + cmdid_change_active_panel, + cmdid_interactive_switch_buffer, + cmdid_interactive_kill_buffer, + cmdid_kill_buffer, + cmdid_toggle_line_wrap, + cmdid_toggle_endline_mode, + cmdid_to_uppercase, + cmdid_to_lowercase, + cmdid_toggle_show_whitespace, + cmdid_clean_all_lines, + cmdid_eol_dosify, + cmdid_eol_nixify, + cmdid_auto_tab_range, + cmdid_open_panel_vsplit, + cmdid_open_panel_hsplit, + cmdid_close_panel, + cmdid_move_left, + cmdid_move_right, + cmdid_delete, + cmdid_backspace, + cmdid_move_up, + cmdid_move_down, + cmdid_seek_end_of_line, + cmdid_seek_beginning_of_line, + cmdid_page_up, + cmdid_page_down, + cmdid_open_color_tweaker, + cmdid_cursor_mark_swap, + cmdid_open_menu, + cmdid_set_settings, + cmdid_command_line, + // + cmdid_count +}; + +enum Param_ID{ + par_range_start, + par_range_end, + par_name, + par_buffer_id, + par_do_in_background, + par_flags, + par_lex_as_cpp_file, + par_wrap_lines, + par_key_mapid, + par_cli_path, + par_cli_command, + par_clear_blank_lines, + // never below this + par_type_count +}; + +#define CLI_OverlapWithConflict 0x1 +#define CLI_AlwaysBindToView 0x2 + +// These are regular hooks, any of them can be set to any function +// that matches the HOOK_SIG pattern. +enum Hook_ID{ + hook_start, + hook_open_file, + hook_frame, + // never below this + hook_type_count +}; + +// These are for special hooks, each must bind to specialized signatures +// that do not necessarily have access to the app pointer. +enum Special_Hook_ID{ + _hook_scroll_rule = hook_type_count, +}; + +// NOTE(allen): None of the members of *_Summary structs nor any of the +// data pointed to by the members should be modified, I would have made +// them all const... but that causes a lot problems for C++ reasons. +struct Buffer_Summary{ + int exists; + int ready; + int buffer_id; + + int size; + + int file_name_len; + int buffer_name_len; + char *file_name; + char *buffer_name; + + int buffer_cursor_pos; + int is_lexed; + int map_id; +}; + +struct View_Summary{ + int exists; + int view_id; + int buffer_id; + int locked_buffer_id; + int hidden_buffer_id; + + Full_Cursor cursor; + Full_Cursor mark; + float preferred_x; + int line_height; + int unwrapped_lines; +}; + +#define UserInputKey 0 +#define UserInputMouse 1 + +struct User_Input{ + int type; + int abort; + union{ + Key_Event_Data key; + Mouse_State mouse; + }; + unsigned long long command; +}; + +#define CommandEqual(c1,c2) ((unsigned long long)(c1) == (unsigned long long)(c2)) + +struct Query_Bar{ + String prompt; + String string; +}; + +struct Theme_Color{ + Style_Tag tag; + unsigned int color; +}; + + + +#define GET_BINDING_DATA(name) int name(void *data, int size) +#define CUSTOM_COMMAND_SIG(name) void name(struct Application_Links *app) +#define HOOK_SIG(name) int name(struct Application_Links *app) +#define SCROLL_RULE_SIG(name) int name(float target_x, float target_y, float *scroll_x, float *scroll_y, int view_id, int is_new_target) + +extern "C"{ + typedef CUSTOM_COMMAND_SIG(Custom_Command_Function); + typedef GET_BINDING_DATA(Get_Binding_Data_Function); + typedef HOOK_SIG(Hook_Function); + typedef SCROLL_RULE_SIG(Scroll_Rule_Function); +} + +struct Application_Links; + +// Command exectuion +#define PUSH_PARAMETER_SIG(n) void n(Application_Links *app, Dynamic param, Dynamic value) +#define PUSH_MEMORY_SIG(n) char* n(Application_Links *app, int len) +#define EXECUTE_COMMAND_SIG(n) void n(Application_Links *app, int command_id) +#define CLEAR_PARAMETERS_SIG(n) void n(Application_Links *app) + +// File system navigation +#define DIRECTORY_GET_HOT_SIG(n) int n(Application_Links *app, char *out, int capacity) +#define FILE_EXISTS_SIG(n) int n(Application_Links *app, char *filename, int len) +#define DIRECTORY_CD_SIG(n) int n(Application_Links *app, char *dir, int *len, int capacity, char *rel_path, int rel_len) +#define GET_FILE_LIST_SIG(n) File_List n(Application_Links *app, char *dir, int len) +#define FREE_FILE_LIST_SIG(n) void n(Application_Links *app, File_List list) + +// Direct buffer manipulation +#define GET_BUFFER_FIRST_SIG(n) Buffer_Summary n(Application_Links *app) +#define GET_BUFFER_NEXT_SIG(n) void n(Application_Links *app, Buffer_Summary *buffer) + +#define GET_BUFFER_SIG(n) Buffer_Summary n(Application_Links *app, int index) +#define GET_ACTIVE_BUFFER_SIG(n) Buffer_Summary n(Application_Links *app) +#define GET_PARAMETER_BUFFER_SIG(n) Buffer_Summary n(Application_Links *app, int param_index) +#define GET_BUFFER_BY_NAME(n) Buffer_Summary n(Application_Links *app, char *filename, int len) + +#define BUFFER_SEEK_DELIMITER_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, char delim, int seek_forward, int *out) +#define BUFFER_SEEK_STRING_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, char *str, int len, int seek_forward, int *out) + +#define REFRESH_BUFFER_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer) +#define BUFFER_READ_RANGE_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, int end, char *out) +#define BUFFER_REPLACE_RANGE_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int start, int end, char *str, int len) +#define BUFFER_SET_POS_SIG(n) int n(Application_Links *app, Buffer_Summary *buffer, int pos) + +// File view manipulation +#define GET_VIEW_FIRST_SIG(n) View_Summary n(Application_Links *app) +#define GET_VIEW_NEXT_SIG(n) void n(Application_Links *app, View_Summary *view) + +#define GET_VIEW_SIG(n) View_Summary n(Application_Links *app, int index) +#define GET_ACTIVE_VIEW_SIG(n) View_Summary n(Application_Links *app) + +#define REFRESH_VIEW_SIG(n) int n(Application_Links *app, View_Summary *view) +#define VIEW_SET_CURSOR_SIG(n) int n(Application_Links *app, View_Summary *view, Buffer_Seek seek, int set_preferred_x) +#define VIEW_SET_MARK_SIG(n) int n(Application_Links *app, View_Summary *view, Buffer_Seek seek) +#define VIEW_SET_HIGHLIGHT_SIG(n) int n(Application_Links *app, View_Summary *view, int start, int end, int turn_on) +#define VIEW_SET_BUFFER_SIG(n) int n(Application_Links *app, View_Summary *view, int buffer_id) + +// Directly get user input +#define GET_USER_INPUT_SIG(n) User_Input n(Application_Links *app, unsigned int get_type, unsigned int abort_type) + +// Queries +#define START_QUERY_BAR_SIG(n) int n(Application_Links *app, Query_Bar *bar, unsigned int flags) +#define END_QUERY_BAR_SIG(n) void n(Application_Links *app, Query_Bar *bar, unsigned int flags) + +// Color settings +#define CHANGE_THEME_SIG(n) void n(Application_Links *app, char *name, int len) +#define CHANGE_FONT_SIG(n) void n(Application_Links *app, char *name, int len) +#define SET_THEME_COLORS_SIG(n) void n(Application_Links *app, Theme_Color *colors, int count) + + +// Boundry type flags +#define BoundryWhitespace 0x1 +#define BoundryToken 0x2 +#define BoundryAlphanumeric 0x4 +#define BoundryCamelCase 0x8 + + + +// Input type flags +#define EventOnAnyKey 0x1 +#define EventOnEsc 0x2 +#define EventOnLeftButton 0x4 +#define EventOnRightButton 0x8 +#define EventOnWheel 0x10 +#define EventOnButton (EventOnLeftButton | EventOnRightButton | EventOnWheel) + +// NOTE(allen): These don't work so much, so let's pretend they're not here for now. +#define EventOnMouseMove 0x20 +#define EventOnMouse (EventOnButton | EventOnMouseMove) + + + +extern "C"{ + // Command exectuion + typedef EXECUTE_COMMAND_SIG(Exec_Command_Function); + typedef PUSH_PARAMETER_SIG(Push_Parameter_Function); + typedef PUSH_MEMORY_SIG(Push_Memory_Function); + typedef CLEAR_PARAMETERS_SIG(Clear_Parameters_Function); + + // File system navigation + typedef DIRECTORY_GET_HOT_SIG(Directory_Get_Hot_Function); + typedef FILE_EXISTS_SIG(File_Exists_Function); + typedef DIRECTORY_CD_SIG(Directory_CD_Function); + typedef GET_FILE_LIST_SIG(Get_File_List_Function); + typedef FREE_FILE_LIST_SIG(Free_File_List_Function); + + // Buffer manipulation + typedef GET_BUFFER_FIRST_SIG(Get_Buffer_First_Function); + typedef GET_BUFFER_NEXT_SIG(Get_Buffer_Next_Function); + + typedef GET_BUFFER_SIG(Get_Buffer_Function); + typedef GET_ACTIVE_BUFFER_SIG(Get_Active_Buffer_Function); + typedef GET_PARAMETER_BUFFER_SIG(Get_Parameter_Buffer_Function); + typedef GET_BUFFER_BY_NAME(Get_Buffer_By_Name_Function); + + typedef BUFFER_SEEK_DELIMITER_SIG(Buffer_Seek_Delimiter_Function); + typedef BUFFER_SEEK_STRING_SIG(Buffer_Seek_String_Function); + + typedef REFRESH_BUFFER_SIG(Refresh_Buffer_Function); + typedef BUFFER_READ_RANGE_SIG(Buffer_Read_Range_Function); + typedef BUFFER_REPLACE_RANGE_SIG(Buffer_Replace_Range_Function); + typedef BUFFER_SET_POS_SIG(Buffer_Set_Pos_Function); + + // View manipulation + typedef GET_VIEW_FIRST_SIG(Get_View_First_Function); + typedef GET_VIEW_NEXT_SIG(Get_View_Next_Function); + + typedef GET_VIEW_SIG(Get_View_Function); + typedef GET_ACTIVE_VIEW_SIG(Get_Active_View_Function); + + typedef REFRESH_VIEW_SIG(Refresh_View_Function); + typedef VIEW_SET_CURSOR_SIG(View_Set_Cursor_Function); + typedef VIEW_SET_MARK_SIG(View_Set_Mark_Function); + typedef VIEW_SET_HIGHLIGHT_SIG(View_Set_Highlight_Function); + typedef VIEW_SET_BUFFER_SIG(View_Set_Buffer_Function); + + // Directly get user input + typedef GET_USER_INPUT_SIG(Get_User_Input_Function); + + // Queries + typedef START_QUERY_BAR_SIG(Start_Query_Bar_Function); + typedef END_QUERY_BAR_SIG(End_Query_Bar_Function); + + // Color settings + typedef CHANGE_THEME_SIG(Change_Theme_Function); + typedef CHANGE_FONT_SIG(Change_Font_Function); + typedef SET_THEME_COLORS_SIG(Set_Theme_Colors_Function); +} + +struct Application_Links{ + // User data + void *memory; + int memory_size; + + // Command exectuion + Exec_Command_Function *exec_command_keep_stack; + Push_Parameter_Function *push_parameter; + Push_Memory_Function *push_memory; + Clear_Parameters_Function *clear_parameters; + + // File system navigation + Directory_Get_Hot_Function *directory_get_hot; + File_Exists_Function *file_exists; + Directory_CD_Function *directory_cd; + Get_File_List_Function *get_file_list; + Free_File_List_Function *free_file_list; + + // Buffer manipulation + Get_Buffer_First_Function *get_buffer_first; + Get_Buffer_Next_Function *get_buffer_next; + + Get_Buffer_Function *get_buffer; + Get_Active_Buffer_Function *get_active_buffer; + Get_Parameter_Buffer_Function *get_parameter_buffer; + Get_Buffer_By_Name_Function *get_buffer_by_name; + + Buffer_Seek_Delimiter_Function *buffer_seek_delimiter; + Buffer_Seek_String_Function *buffer_seek_string; + + Refresh_Buffer_Function *refresh_buffer; + Buffer_Read_Range_Function *buffer_read_range; + Buffer_Replace_Range_Function *buffer_replace_range; + Buffer_Set_Pos_Function *buffer_set_pos; + + // View manipulation + Get_View_First_Function *get_view_first; + Get_View_Next_Function *get_view_next; + + Get_View_Function *get_view; + Get_Active_View_Function *get_active_view; + + Refresh_View_Function *refresh_view; + View_Set_Cursor_Function *view_set_cursor; + View_Set_Mark_Function *view_set_mark; + View_Set_Highlight_Function *view_set_highlight; + View_Set_Buffer_Function *view_set_buffer; + + // Directly get user input + Get_User_Input_Function *get_user_input; + + // Queries + Start_Query_Bar_Function *start_query_bar; + End_Query_Bar_Function *end_query_bar; + + // Theme + Change_Theme_Function *change_theme; + Change_Font_Function *change_font; + Set_Theme_Colors_Function *set_theme_colors; + + // Internal + void *cmd_context; +}; + +#define _GET_VERSION_SIG(n) int n(int maj, int min, int patch) +typedef _GET_VERSION_SIG(_Get_Version_Function); + +extern "C" _GET_VERSION_SIG(get_alpha_4coder_version){ + int result = (maj == MAJOR && min == MINOR && patch == PATCH); + return(result); +} + +struct Custom_API{ + Get_Binding_Data_Function *get_bindings; + _Get_Version_Function *get_alpha_4coder_version; +}; + +// NOTE(allen): definitions for the buffer that communicates to 4ed.exe + +enum Binding_Unit_Type{ + unit_header, + unit_map_begin, + unit_binding, + unit_callback, + unit_inherit, + unit_hook +}; + +enum Map_ID{ + mapid_global = (1 << 24), + mapid_file, + + // NOTE(allen): mapid_nomap will remain empty even if you attempt to fill it + // it is for setting a map's parent to nothing, in cases where you don't want + // to inherit from global (which is the default). + mapid_nomap +}; + +struct Binding_Unit{ + Binding_Unit_Type type; + union{ + struct{ int total_size; int user_map_count; int error; } header; + + struct{ int mapid; int bind_count; } map_begin; + struct{ int mapid; } map_inherit; + struct{ + short code; + unsigned char modifiers; + int command_id; + } binding; + struct{ + short code; + unsigned char modifiers; + Custom_Command_Function *func; + } callback; + struct{ + int hook_id; + void *func; + } hook; + }; +}; diff --git a/test_data/lots_of_files/4coder_helper.h b/test_data/lots_of_files/4coder_helper.h new file mode 100644 index 0000000..97494c2 --- /dev/null +++ b/test_data/lots_of_files/4coder_helper.h @@ -0,0 +1,343 @@ +/* + * Bind helper struct and functions + */ + +struct Bind_Helper{ + Binding_Unit *cursor, *start, *end; + Binding_Unit *header, *group; + int write_total; + int error; +}; + +#define BH_ERR_NONE 0 +#define BH_ERR_MISSING_END 1 +#define BH_ERR_MISSING_BEGIN 2 +#define BH_ERR_OUT_OF_MEMORY 3 + +inline void +copy(char *dest, const char *src, int len){ + for (int i = 0; i < len; ++i){ + *dest++ = *src++; + } +} + +inline Binding_Unit* +write_unit(Bind_Helper *helper, Binding_Unit unit){ + Binding_Unit *p = 0; + helper->write_total += sizeof(*p); + if (helper->error == 0 && helper->cursor != helper->end){ + p = helper->cursor++; + *p = unit; + } + return p; +} + +inline char* +write_inline_string(Bind_Helper *helper, char *value, int len){ + char *dest = 0; + helper->write_total += len; + if (helper->error == 0){ + dest = (char*)helper->cursor; + int cursor_advance = len + sizeof(*helper->cursor) - 1; + cursor_advance /= sizeof(*helper->cursor); + cursor_advance *= sizeof(*helper->cursor); + helper->cursor += cursor_advance; + if (helper->cursor < helper->end){ + copy(dest, value, len); + } + else{ + helper->error = BH_ERR_OUT_OF_MEMORY; + } + } + return dest; +} + +inline Bind_Helper +begin_bind_helper(void *data, int size){ + Bind_Helper result; + + result.header = 0; + result.group = 0; + result.write_total = 0; + result.error = 0; + + result.cursor = (Binding_Unit*)data; + result.start = result.cursor; + result.end = result.start + size / sizeof(*result.cursor); + + Binding_Unit unit; + unit.type = unit_header; + unit.header.total_size = sizeof(*result.header); + result.header = write_unit(&result, unit); + result.header->header.user_map_count = 0; + + return result; +} + +inline void +begin_map(Bind_Helper *helper, int mapid){ + if (helper->group != 0 && helper->error == 0) helper->error = BH_ERR_MISSING_END; + if (!helper->error && mapid < mapid_global) ++helper->header->header.user_map_count; + + Binding_Unit unit; + unit.type = unit_map_begin; + unit.map_begin.mapid = mapid; + helper->group = write_unit(helper, unit); + helper->group->map_begin.bind_count = 0; +} + +inline void +end_map(Bind_Helper *helper){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + helper->group = 0; +} + +inline void +bind(Bind_Helper *helper, short code, unsigned char modifiers, int cmdid){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + if (!helper->error) ++helper->group->map_begin.bind_count; + + Binding_Unit unit; + unit.type = unit_binding; + unit.binding.command_id = cmdid; + unit.binding.code = code; + unit.binding.modifiers = modifiers; + + write_unit(helper, unit); +} + +inline void +bind(Bind_Helper *helper, short code, unsigned char modifiers, Custom_Command_Function *func){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + if (!helper->error) ++helper->group->map_begin.bind_count; + + Binding_Unit unit; + unit.type = unit_callback; + unit.callback.func = func; + unit.callback.code = code; + unit.callback.modifiers = modifiers; + + write_unit(helper, unit); +} + +inline void +bind_vanilla_keys(Bind_Helper *helper, int cmdid){ + bind(helper, 0, 0, cmdid); +} + +inline void +bind_vanilla_keys(Bind_Helper *helper, Custom_Command_Function *func){ + bind(helper, 0, 0, func); +} + +inline void +bind_vanilla_keys(Bind_Helper *helper, unsigned char modifiers, int cmdid){ + bind(helper, 0, modifiers, cmdid); +} + +inline void +bind_vanilla_keys(Bind_Helper *helper, unsigned char modifiers, Custom_Command_Function *func){ + bind(helper, 0, modifiers, func); +} + +inline void +inherit_map(Bind_Helper *helper, int mapid){ + if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + if (!helper->error && mapid < mapid_global) ++helper->header->header.user_map_count; + + Binding_Unit unit; + unit.type = unit_inherit; + unit.map_inherit.mapid = mapid; + + write_unit(helper, unit); +} + +inline void +set_hook(Bind_Helper *helper, int hook_id, Hook_Function *func){ + Binding_Unit unit; + unit.type = unit_hook; + unit.hook.hook_id = hook_id; + unit.hook.func = (void*) func; + + write_unit(helper, unit); +} + +inline void +set_scroll_rule(Bind_Helper *helper, Scroll_Rule_Function *func){ + Binding_Unit unit; + unit.type = unit_hook; + unit.hook.hook_id = _hook_scroll_rule; + unit.hook.func = (void*) func; + + write_unit(helper, unit); +} + +inline void +end_bind_helper(Bind_Helper *helper){ + if (helper->header){ + helper->header->header.total_size = (int)(helper->cursor - helper->start); + helper->header->header.error = helper->error; + } +} + +// NOTE(allen): Useful functions and overloads on app links +inline void +push_parameter(Application_Links *app, int param, int value){ + app->push_parameter(app, dynamic_int(param), dynamic_int(value)); +} + +inline void +push_parameter(Application_Links *app, int param, const char *value, int value_len){ + char *value_copy = app->push_memory(app, value_len+1); + copy(value_copy, value, value_len); + value_copy[value_len] = 0; + app->push_parameter(app, dynamic_int(param), dynamic_string(value_copy, value_len)); +} + +inline void +push_parameter(Application_Links *app, const char *param, int param_len, int value){ + char *param_copy = app->push_memory(app, param_len+1); + copy(param_copy, param, param_len); + param_copy[param_len] = 0; + app->push_parameter(app, dynamic_string(param_copy, param_len), dynamic_int(value)); +} + +inline void +push_parameter(Application_Links *app, const char *param, int param_len, const char *value, int value_len){ + char *param_copy = app->push_memory(app, param_len+1); + char *value_copy = app->push_memory(app, value_len+1); + copy(param_copy, param, param_len); + copy(value_copy, value, value_len); + value_copy[value_len] = 0; + param_copy[param_len] = 0; + + app->push_parameter(app, dynamic_string(param_copy, param_len), dynamic_string(value_copy, value_len)); +} + +inline Range +get_range(View_Summary *view){ + Range range; + range = make_range(view->cursor.pos, view->mark.pos); + return(range); +} + +inline void +exec_command(Application_Links *app, Command_ID id){ + app->exec_command_keep_stack(app, id); + app->clear_parameters(app); +} + +inline void +exec_command(Application_Links *app, Custom_Command_Function *func){ + func(app); + app->clear_parameters(app); +} + +inline void +active_view_to_line(Application_Links *app, int line_number){ + View_Summary view; + view = app->get_active_view(app); + + // NOTE(allen|a3.4.4): We don't have to worry about whether this is a valid line number. + // When it's not possible to place a cursor at the position for whatever reason it will set the + // cursor to a nearby valid position. + app->view_set_cursor(app, &view, seek_line_char(line_number, 0), 1); +} + +inline View_Summary +get_first_view_with_buffer(Application_Links *app, int buffer_id){ + View_Summary result = {}; + View_Summary test = {}; + + for(test = app->get_view_first(app); + test.exists; + app->get_view_next(app, &test)){ + if(test.locked_buffer_id == buffer_id){ + result = test; + break; + } + } + + return(result); +} + +inline int +key_is_unmodified(Key_Event_Data *key){ + char *mods = key->modifiers; + int unmodified = !mods[MDFR_CONTROL_INDEX] && !mods[MDFR_ALT_INDEX]; + return(unmodified); +} + +static int +query_user_general(Application_Links *app, Query_Bar *bar, int force_number){ + User_Input in; + int success = 1; + int good_character = 0; + + // NOTE(allen|a3.4.4): It will not cause an *error* if we continue on after failing to. + // start a query bar, but it will be unusual behavior from the point of view of the + // user, if this command starts intercepting input even though no prompt is shown. + // This will only happen if you have a lot of bars open already or if the current view + // doesn't support query bars. + if (app->start_query_bar(app, bar, 0) == 0) return 0; + + while (1){ + // NOTE(allen|a3.4.4): This call will block until the user does one of the input + // types specified in the flags. The first set of flags are inputs you'd like to intercept + // that you don't want to abort on. The second set are inputs that you'd like to cause + // the command to abort. If an event satisfies both flags, it is treated as an abort. + in = app->get_user_input(app, EventOnAnyKey, EventOnEsc | EventOnButton); + + // NOTE(allen|a3.4.4): The responsible thing to do on abort is to end the command + // without waiting on get_user_input again. + if (in.abort){ + success = 0; + break; + } + + good_character = 0; + if (key_is_unmodified(&in.key)){ + if (force_number){ + if (in.key.character >= '0' && in.key.character <= '9'){ + good_character = 1; + } + } + else{ + if (in.key.character != 0){ + good_character = 1; + } + } + } + + // NOTE(allen|a3.4.4): All we have to do to update the query bar is edit our + // local Query_Bar struct! This is handy because it means our Query_Bar + // is always correct for typical use without extra work updating the bar. + if (in.type == UserInputKey){ + if (in.key.keycode == '\n' || in.key.keycode == '\t'){ + break; + } + else if (in.key.keycode == key_back){ + --bar->string.size; + } + else if (good_character){ + append(&bar->string, in.key.character); + } + } + } + + return(success); +} + +inline int +query_user_string(Application_Links *app, Query_Bar *bar){ + int success = query_user_general(app, bar, 0); + return(success); +} + +inline int +query_user_number(Application_Links *app, Query_Bar *bar){ + int success = query_user_general(app, bar, 1); + return(success); +} + +inline String empty_string() {String Result = {}; return(Result);} diff --git a/test_data/lots_of_files/4coder_keycodes.h b/test_data/lots_of_files/4coder_keycodes.h new file mode 100644 index 0000000..92df28f --- /dev/null +++ b/test_data/lots_of_files/4coder_keycodes.h @@ -0,0 +1,30 @@ +enum Key_Code{ + key_back = 1, + key_up = 2, + key_down = 3, + key_left = 4, + key_right = 5, + key_del = 6, + key_insert = 7, + key_home = 8, + key_end = 11, + key_page_up = 12, + key_page_down = 13, + key_esc = 14, + key_f1 = 127, + key_f2 = 128, + key_f3 = 129, + key_f4 = 130, + key_f5 = 131, + key_f6 = 132, + key_f7 = 133, + key_f8 = 134, + key_f9 = 135, + key_f10 = 136, + key_f11 = 137, + key_f12 = 138, + key_f13 = 139, + key_f14 = 140, + key_f15 = 141, + key_f16 = 142, +}; diff --git a/test_data/lots_of_files/4coder_string.h b/test_data/lots_of_files/4coder_string.h new file mode 100644 index 0000000..f736dbe --- /dev/null +++ b/test_data/lots_of_files/4coder_string.h @@ -0,0 +1,1206 @@ +/* "4cpp" Open C++ Parser v0.1: String + no warranty implied; use at your own risk + +NOTES ON USE: + OPTIONS: + Set options by defining macros before including this file + + FCPP_STRING_IMPLEMENTATION - causes this file to output function implementations + - this option is unset after use so that future includes of this file + in the same unit do not continue to output implementations + + FCPP_LINK - defines linkage of non-inline functions, defaults to static + FCPP_EXTERN changes FCPP_LINK default to extern, this option is ignored if FCPP_LINK is defined + + include the file "4cpp_clear_config.h" if yo want to undefine all options for some reason + + HIDDEN DEPENDENCIES: + none + */ + +// TOP +// TODO(allen): +// - comments +// - memcpy / memmove replacements (different file for optimization options?) +// - examples and docs +// - finish a full set of tools for file name handling +// + +#include "4coder_config.h" + +#ifndef FCPP_STRING_INC +#define FCPP_STRING_INC + +#ifndef FRED_STRING_STRUCT +#define FRED_STRING_STRUCT +struct String{ + char *str; + int size; + int memory_size; +}; + +struct Offset_String{ + int offset; + int size; +}; +#endif + +inline bool char_not_slash(char c) { return (c != '\\' && c != '/'); } +inline bool char_is_slash(char c) { return (c == '\\' || c == '/'); } + +inline char char_to_upper(char c) { return (c >= 'a' && c <= 'z') ? c + (char)('A' - 'a') : c; } +inline char char_to_lower(char c) { return (c >= 'A' && c <= 'Z') ? c - (char)('A' - 'a') : c; } + +inline bool char_is_whitespace(char c) { return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); } +inline bool char_is_white_not_r(char c) { return (c == ' ' || c == '\n' || c == '\t'); } +inline bool char_is_lower(char c) { return (c >= 'a' && c <= 'z'); } +inline bool char_is_upper(char c) { return (c >= 'A' && c <= 'Z'); } +inline bool char_is_alpha(char c) { return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_'); } +inline bool char_is_alpha_true(char c) { return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'); } +inline bool char_is_numeric(char c) { return (c >= '0' && c <= '9'); } +inline bool char_is_alpha_numeric_true(char c) { return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'); } +inline bool char_is_alpha_numeric(char c) { return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_'); } +inline bool char_is_hex(char c) { return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f'; } +inline bool char_is_basic(char c) { return c >= ' ' && c <= '~'; } + +inline String make_string(void *s, int size, int mem_size); +inline String make_string(void *s, int size); + +#define make_lit_string(str) (make_string((char*)(str), sizeof(str)-1, sizeof(str))) +#define make_fixed_width_string(str) (make_string((char*)(str), 0, sizeof(str))) + +#define expand_str(s) ((s).str), ((s).size) + +inline String make_string_slowly(void *s); +inline char* make_c_str(String s); + +inline String substr(String str, int start); +inline String substr(String str, int start, int size); +inline String substr_slowly(char *s, int start); +inline String substr(char *s, int start, int size); +inline String tailstr(String s); + + +FCPP_LINK int str_size(char *s); + +FCPP_LINK bool match(char *a, char *b); +FCPP_LINK bool match(String a, char *b); +inline bool match(char *a, String b) { return match(b,a); } +FCPP_LINK bool match(String a, String b); + +FCPP_LINK bool match_part(char *a, char *b, int *len); +FCPP_LINK bool match_part(String a, char *b, int *len); +inline bool match_part(char *a, char *b) { int x; return match_part(a,b,&x); } +inline bool match_part(String a, char *b) { int x; return match_part(a,b,&x); } +FCPP_LINK bool match_part(char *a, String b); +FCPP_LINK bool match_part(String a, String b); + +FCPP_LINK bool match_unsensitive(char *a, char *b); +FCPP_LINK bool match_unsensitive(String a, char *b); +inline bool match_unsensitive(char *a, String b) { return match_unsensitive(b,a); } +FCPP_LINK bool match_unsensitive(String a, String b); + +FCPP_LINK bool match_part_unsensitive(char *a, char *b, int *len); +FCPP_LINK bool match_part_unsensitive(String a, char *b, int *len); +inline bool match_part_unsensitive(char *a, char *b) { int x; return match_part(a,b,&x); } +inline bool match_part_unsensitive(String a, char *b) { int x; return match_part(a,b,&x); } +FCPP_LINK bool match_part_unsensitive(char *a, String b); +FCPP_LINK bool match_part_unsensitive(String a, String b); + +FCPP_LINK int find(char *s, int start, char c); +FCPP_LINK int find(String s, int start, char c); +FCPP_LINK int find(char *s, int start, char *c); +FCPP_LINK int find(String s, int start, char *c); + +FCPP_LINK int find_substr(char *s, int start, String seek); +FCPP_LINK int find_substr(String s, int start, String seek); +FCPP_LINK int rfind_substr(String s, int start, String seek); + +FCPP_LINK int find_substr_unsensitive(char *s, int start, String seek); +FCPP_LINK int find_substr_unsensitive(String s, int start, String seek); + +inline bool has_substr(char *s, String seek) { return (s[find_substr(s, 0, seek)] != 0); } +inline bool has_substr(String s, String seek) { return (find_substr(s, 0, seek) < s.size); } + +inline bool has_substr_unsensitive(char *s, String seek) { return (s[find_substr_unsensitive(s, 0, seek)] != 0); } +inline bool has_substr_unsensitive(String s, String seek) { return (find_substr_unsensitive(s, 0, seek) < s.size); } + +FCPP_LINK int int_to_str_size(int x); +FCPP_LINK int int_to_str(int x, char *s_out); +FCPP_LINK bool int_to_str(int x, String *s_out); +FCPP_LINK bool append_int_to_str(int x, String *s_out); + +FCPP_LINK int str_to_int(char *s); +FCPP_LINK int str_to_int(String s); +FCPP_LINK int hexchar_to_int(char c); +FCPP_LINK char int_to_hexchar(int c); +FCPP_LINK int hexstr_to_int(String s); + +FCPP_LINK int copy_fast_unsafe(char *dest, char *src); +FCPP_LINK void copy_fast_unsafe(char *dest, String src); +FCPP_LINK bool copy_checked(String *dest, String src); +FCPP_LINK bool copy_partial(String *dest, char *src); +FCPP_LINK bool copy_partial(String *dest, String src); + +inline int copy(char *dest, char *src) { return copy_fast_unsafe(dest, src); } +inline void copy(String *dest, String src) { copy_checked(dest, src); } +inline void copy(String *dest, char *src) { copy_partial(dest, src); } + +FCPP_LINK bool append_checked(String *dest, String src); +FCPP_LINK bool append_partial(String *dest, char *src); +FCPP_LINK bool append_partial(String *dest, String src); + +FCPP_LINK bool append(String *dest, char c); +inline bool append(String *dest, String src) { return append_partial(dest, src); } +inline bool append(String *dest, char *src) { return append_partial(dest, src); } +inline bool terminate_with_null(String *str){ + bool result; + if (str->size < str->memory_size){ + str->str[str->size] = 0; + result = 1; + } + else{ + str->str[str->size-1] = 0; + result = 0; + } + return result; +} + +FCPP_LINK int compare(char *a, char *b); +FCPP_LINK int compare(String a, char *b); +inline int compare(char *a, String b) { return -compare(b,a); } +FCPP_LINK int compare(String a, String b); + +FCPP_LINK int reverse_seek_slash(String str); +FCPP_LINK int reverse_seek_slash(String str, int start_pos); +inline bool get_front_of_directory(String *dest, String dir) { return append_checked(dest, substr(dir, reverse_seek_slash(dir) + 1)); } +inline bool get_path_of_directory(String *dest, String dir) { return append_checked(dest, substr(dir, 0, reverse_seek_slash(dir) + 1)); } +FCPP_LINK bool set_last_folder(String *dir, char *folder_name, char slash); +FCPP_LINK bool set_last_folder(String *dir, String folder_name, char slash); +FCPP_LINK String file_extension(String str); +FCPP_LINK String file_extension_slowly(char *str); +FCPP_LINK char * file_extension_c(String str); +FCPP_LINK bool remove_last_folder(String *str); +FCPP_LINK void replace_char(String str, char replace, char with); + +inline String make_string(void *str, int size, int mem_size){ + String result; + result.str = (char*)str; + result.size = size; + result.memory_size = mem_size; + return result; +} + +inline String +make_string(void *str, int size){ + String result; + result.str = (char*)str; + result.size = size; + result.memory_size = size; + return result; +} + +inline String +make_string_slowly(void *str){ + String result; + result.str = (char*)str; + result.size = str_size((char*)str); + result.memory_size = result.size; + return result; +} + +inline char* +make_c_str(String str){ + if (str.size < str.memory_size){ + str.str[str.size] = 0; + } + else{ + str.str[str.memory_size-1] = 0; + } + return (char*)str.str; +} + +inline String +substr(String str, int start){ + String result; + result.str = str.str + start; + result.size = str.size - start; + return result; +} + +inline String +substr(String str, int start, int size){ + String result; + result.str = str.str + start; + if (start + size > str.size){ + result.size = str.size - start; + } + else{ + result.size = size; + } + return result; +} + +inline String +substr_slowly(char *str, int start){ + String result; + result.str = str + start; + result.size = str_size(result.str); + return result; +} + +inline String +substr(char *str, int start, int size){ + String result; + result.str = str + start; + result.size = size; + for (int i = 0; i < size; ++i){ + if (result.str[i] == 0){ + result.size = i; + break; + } + } + return result; +} + +inline String +tailstr(String str){ + String result; + result.str = str.str + str.size; + result.memory_size = str.memory_size - str.size; + result.size = 0; + return result; +} + +#endif // #ifndef FCPP_STRING_INC + +#ifdef FCPP_STRING_IMPLEMENTATION + +#ifndef FCPP_DID_STRING_IMPLEMENTATION +#define FCPP_DID_STRING_IMPLEMENTATION + +FCPP_LINK int +str_size(char *str){ + int i = 0; + while (str[i]) ++i; + return i; +} + +FCPP_LINK bool +match(char *a, char *b){ + for (int i = 0;; ++i){ + if (a[i] != b[i]){ + return 0; + } + if (a[i] == 0){ + return 1; + } + } +} + +FCPP_LINK bool +match(String a, char *b){ + int i = 0; + for (; i < a.size; ++i){ + if (a.str[i] != b[i]){ + return 0; + } + } + if (b[i] != 0){ + return 0; + } + return 1; +} + +FCPP_LINK bool +match(String a, String b){ + if (a.size != b.size){ + return 0; + } + for (int i = 0; i < b.size; ++i){ + if (a.str[i] != b.str[i]){ + return 0; + } + } + return 1; +} + +FCPP_LINK bool +match_part(char *a, char *b, int *len){ + int i; + for (i = 0; b[i] != 0; ++i){ + if (a[i] != b[i]){ + return 0; + } + } + *len = i; + return 1; +} + +FCPP_LINK bool +match_part(String a, char *b, int *len){ + int i; + for (i = 0; b[i] != 0; ++i){ + if (a.str[i] != b[i] || i == a.size){ + return 0; + } + } + *len = i; + return 1; +} + +FCPP_LINK bool +match_part(char *a, String b){ + for (int i = 0; i != b.size; ++i){ + if (a[i] != b.str[i]){ + return 0; + } + } + return 1; +} + +FCPP_LINK bool +match_part(String a, String b){ + if (a.size < b.size){ + return 0; + } + for (int i = 0; i < b.size; ++i){ + if (a.str[i] != b.str[i]){ + return 0; + } + } + return 1; +} + +FCPP_LINK bool +match_unsensitive(char *a, char *b){ + for (int i = 0;; ++i){ + if (char_to_upper(a[i]) != + char_to_upper(b[i])){ + return 0; + } + if (a[i] == 0){ + return 1; + } + } +} + +FCPP_LINK bool +match_unsensitive(String a, char *b){ + int i = 0; + for (; i < a.size; ++i){ + if (char_to_upper(a.str[i]) != + char_to_upper(b[i])){ + return 0; + } + } + if (b[i] != 0){ + return 0; + } + return 1; +} + +FCPP_LINK bool +match_unsensitive(String a, String b){ + if (a.size != b.size){ + return 0; + } + for (int i = 0; i < b.size; ++i){ + if (char_to_upper(a.str[i]) != + char_to_upper(b.str[i])){ + return 0; + } + } + return 1; +} + +FCPP_LINK bool +match_part_unsensitive(char *a, char *b, int *len){ + int i; + for (i = 0; b[i] != 0; ++i){ + if (char_to_upper(a[i]) != char_to_upper(b[i])){ + return 0; + } + } + *len = i; + return 1; +} + +FCPP_LINK bool +match_part_unsensitive(String a, char *b, int *len){ + int i; + for (i = 0; b[i] != 0; ++i){ + if (char_to_upper(a.str[i]) != char_to_upper(b[i]) || + i == a.size){ + return 0; + } + } + *len = i; + return 1; +} + +FCPP_LINK bool +match_part_unsensitive(char *a, String b){ + for (int i = 0; i != b.size; ++i){ + if (char_to_upper(a[i]) != char_to_upper(b.str[i])){ + return 0; + } + } + return 1; +} + +FCPP_LINK bool +match_part_unsensitive(String a, String b){ + if (a.size < b.size){ + return 0; + } + for (int i = 0; i < b.size; ++i){ + if (char_to_upper(a.str[i]) != char_to_upper(b.str[i])){ + return 0; + } + } + return 1; +} + +FCPP_LINK int +find(char *str, int start, char character){ + int i = start; + while (str[i] != character && str[i] != 0) ++i; + return i; +} + +FCPP_LINK int +find(String str, int start, char character){ + int i = start; + while (i < str.size && str.str[i] != character) ++i; + return i; +} + +FCPP_LINK int +find(char *str, int start, char *characters){ + int i = start, j; + while (str[i] != 0){ + for (j = 0; characters[j]; ++j){ + if (str[i] == characters[j]){ + return i; + } + } + ++i; + } + return i; +} + +FCPP_LINK int +find(String str, int start, char *characters){ + int i = start, j; + while (i < str.size){ + for (j = 0; characters[j]; ++j){ + if (str.str[i] == characters[j]){ + return i; + } + } + ++i; + } + return i; +} + +FCPP_LINK int +find_substr(char *str, int start, String seek){ + int i, j, k; + bool hit; + + if (seek.size == 0){ + return str_size(str); + } + for (i = start; str[i]; ++i){ + if (str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + if (str[k] != seek.str[j]){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return i; +} + +FCPP_LINK int +find_substr(String str, int start, String seek){ + int stop_at, i, j, k; + bool hit; + + if (seek.size == 0){ + return str.size; + } + stop_at = str.size - seek.size + 1; + for (i = start; i < stop_at; ++i){ + if (str.str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + if (str.str[k] != seek.str[j]){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return str.size; +} + +FCPP_LINK int +rfind_substr(String str, int start, String seek){ + int i, j, k; + bool hit; + + if (seek.size == 0){ + return -1; + } + if (start + seek.size > str.size){ + start = str.size - seek.size; + } + for (i = start; i >= 0; --i){ + if (str.str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + if (str.str[k] != seek.str[j]){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return -1; +} + +FCPP_LINK int +find_substr_unsensitive(char *str, int start, String seek){ + int i, j, k; + bool hit; + char a_upper, b_upper; + + if (seek.size == 0){ + return str_size(str); + } + for (i = start; str[i]; ++i){ + if (str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + a_upper = char_to_upper(str[k]); + b_upper = char_to_upper(seek.str[j]); + if (a_upper != b_upper){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return i; +} + +FCPP_LINK int +find_substr_unsensitive(String str, int start, String seek){ + int i, j, k; + int stop_at; + bool hit; + char a_upper, b_upper; + + if (seek.size == 0){ + return str.size; + } + stop_at = str.size - seek.size + 1; + for (i = start; i < stop_at; ++i){ + if (str.str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + a_upper = char_to_upper(str.str[k]); + b_upper = char_to_upper(seek.str[j]); + if (a_upper != b_upper){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return str.size; +} + +FCPP_LINK int +int_to_str_size(int x){ + int size; + if (x < 0){ + size = 0; + } + else{ + size = 1; + x /= 10; + while (x != 0){ + x /= 10; + ++size; + } + } + return size; +} + +FCPP_LINK int +int_to_str(int x, char *str){ + int size, i, j; + bool negative; + char temp; + + size = 0; + if (x == 0){ + str[0] = '0'; + str[1] = 0; + size = 1; + } + else{ + size = 0; + negative = 0; + if (x < 0){ + negative = 1; + x = -x; + str[size++] = '-'; + } + while (x != 0){ + i = x % 10; + x /= 10; + str[size++] = (char)('0' + i); + } + // NOTE(allen): Start i = 0 if not negative, start i = 1 if is negative + // because - should not be flipped if it is negative :) + for (i = negative, j = size-1; i < j; ++i, --j){ + temp = str[i]; + str[i] = str[j]; + str[j] = temp; + } + str[size] = 0; + } + return size; +} + +FCPP_LINK bool +int_to_str(int x, String *dest){ + bool result = 1; + char *str = dest->str; + int memory_size = dest->memory_size; + int size, i, j; + bool negative; + + if (x == 0){ + str[0] = '0'; + dest->size = 1; + } + else{ + size = 0; + negative = 0; + if (x < 0){ + negative = 1; + x = -x; + str[size++] = '-'; + } + while (x != 0){ + if (size == memory_size){ + result = 0; + break; + } + i = x % 10; + x /= 10; + str[size++] = (char)('0' + i); + } + if (result){ + // NOTE(allen): Start i = 0 if not negative, start i = 1 if is negative + // because - should not be flipped if it is negative :) + for (i = negative, j = size-1; i < j; ++i, --j){ + char temp = str[i]; + str[i] = str[j]; + str[j] = temp; + } + dest->size = size; + } + else{ + dest->size = 0; + } + } + return result; +} + +FCPP_LINK bool +append_int_to_str(int x, String *dest){ + String last_part = tailstr(*dest); + bool result = int_to_str(x, &last_part); + if (result){ + dest->size += last_part.size; + } + return result; +} + +FCPP_LINK int +str_to_int(char *str){ + int x = 0; + for (; *str; ++str){ + if (*str >= '0' || *str <= '9'){ + x *= 10; + x += *str - '0'; + } + else{ + x = 0; + break; + } + } + return(x); +} + +FCPP_LINK int +str_to_int(String str){ + int x, i; + if (str.size == 0){ + x = 0; + } + else{ + x = str.str[0] - '0'; + for (i = 1; i < str.size; ++i){ + x *= 10; + x += str.str[i] - '0'; + } + } + return x; +} + +FCPP_LINK int +hexchar_to_int(char c){ + int x; + if (c >= '0' && c <= '9'){ + x = c-'0'; + } + else if (c > 'F'){ + x = c+(10-'a'); + } + else{ + x = c+(10-'A'); + } + return x; +} + +FCPP_LINK char +int_to_hexchar(int x){ + return (x<10)?((char)x+'0'):((char)x+'a'-10); +} + +FCPP_LINK int +hexstr_to_int(String str){ + int x, i; + if (str.size == 0){ + x = 0; + } + else{ + x = hexchar_to_int(str.str[0]); + for (i = 1; i < str.size; ++i){ + x *= 0x10; + x += hexchar_to_int(str.str[i]); + } + } + return x; +} + +FCPP_LINK int +copy_fast_unsafe(char *dest, char *src){ + char *start = dest; + while (*src != 0){ + *dest = *src; + ++dest; + ++src; + } + return (int)(dest - start); +} + +FCPP_LINK void +copy_fast_unsafe(char *dest, String src){ + int i = 0; + while (i != src.size){ + dest[i] = src.str[i]; + ++i; + } +} + +FCPP_LINK bool +copy_checked(String *dest, String src){ + char *dest_str; + int i; + if (dest->memory_size < src.size){ + return 0; + } + dest_str = dest->str; + for (i = 0; i < src.size; ++i){ + dest_str[i] = src.str[i]; + } + dest->size = src.size; + return 1; +} + +FCPP_LINK bool +copy_partial(String *dest, char *src){ + int i = 0; + int memory_size = dest->memory_size; + char *dest_str = dest->str; + while (src[i] != 0){ + if (i >= memory_size){ + return 0; + } + dest_str[i] = src[i]; + ++i; + } + dest->size = i; + return 1; +} + +FCPP_LINK bool +copy_partial(String *dest, String src){ + bool result; + int memory_size = dest->memory_size; + char *dest_str = dest->str; + if (memory_size < src.size){ + result = 0; + for (int i = 0; i < memory_size; ++i){ + dest_str[i] = src.str[i]; + } + dest->size = memory_size; + } + else{ + result = 1; + for (int i = 0; i < src.size; ++i){ + dest_str[i] = src.str[i]; + } + dest->size = src.size; + } + return result; +} + +FCPP_LINK bool +append_checked(String *dest, String src){ + String end; + end = tailstr(*dest); + bool result = copy_checked(&end, src); + // NOTE(allen): This depends on end.size still being 0 if + // the check failed and no coppy occurred. + dest->size += end.size; + return result; +} + +FCPP_LINK bool +append_partial(String *dest, char *src){ + String end = tailstr(*dest); + bool result = copy_partial(&end, src); + dest->size += end.size; + return result; +} + +FCPP_LINK bool +append_partial(String *dest, String src){ + String end = tailstr(*dest); + bool result = copy_partial(&end, src); + dest->size += end.size; + return result; +} + +FCPP_LINK bool +append(String *dest, char c){ + bool result = 0; + if (dest->size < dest->memory_size){ + dest->str[dest->size++] = c; + result = 1; + } + return result; +} + +FCPP_LINK int +compare(char *a, char *b){ + int i = 0; + while (a[i] == b[i] && a[i] != 0){ + ++i; + } + return (a[i] > b[i]) - (a[i] < b[i]); +} + +FCPP_LINK int +compare(String a, char *b){ + int i = 0; + while (i < a.size && a.str[i] == b[i]){ + ++i; + } + if (i < a.size){ + return (a.str[i] > b[i]) - (a.str[i] < b[i]); + } + else{ + if (b[i] == 0){ + return 0; + } + else{ + return -1; + } + } +} + +FCPP_LINK int +compare(String a, String b){ + int i = 0; + while (i < a.size && i < b.size && a.str[i] == b.str[i]){ + ++i; + } + if (i < a.size && i < b.size){ + return (a.str[i] > b.str[i]) - (a.str[i] < b.str[i]); + } + else{ + return (a.size > b.size) - (a.size < b.size); + } +} + +FCPP_LINK int +reverse_seek_slash(String str, int pos){ + int i = str.size - 1 - pos; + while (i >= 0 && char_not_slash(str.str[i])){ + --i; + } + return i; +} + +FCPP_LINK int +reverse_seek_slash(String str){ + return(reverse_seek_slash(str, 0)); +} + +FCPP_LINK bool +set_last_folder(String *dir, char *folder_name, char slash){ + char str[2]; + bool result = 0; + int size = reverse_seek_slash(*dir) + 1; + dir->size = size; + str[0] = slash; + str[1] = 0; + if (append(dir, folder_name)){ + if (append(dir, str)){ + result = 1; + } + } + if (!result){ + dir->size = size; + } + return result; +} + +FCPP_LINK bool +set_last_folder(String *dir, String folder_name, char slash){ + char str[2]; + bool result = 0; + int size = reverse_seek_slash(*dir) + 1; + dir->size = size; + str[0] = slash; + str[1] = 0; + if (append(dir, folder_name)){ + if (append(dir, str)){ + result = 1; + } + } + if (!result){ + dir->size = size; + } + return result; +} + +FCPP_LINK String +file_extension(String str){ + int i; + for (i = str.size - 1; i >= 0; --i){ + if (str.str[i] == '.') break; + } + ++i; + return make_string(str.str+i, str.size-i); +} + +FCPP_LINK String +file_extension_slowly(char *str){ + int s, i; + for (s = 0; str[s]; ++s); + for (i = s - 1; i >= 0; --i){ + if (str[i] == '.') break; + } + ++i; + return make_string(str+i, s-i); +} + +FCPP_LINK char* +file_extension_c(String str){ + int i; + for (i = str.size - 1; i >= 0; --i){ + if (str.str[i] == '.') break; + } + ++i; + return str.str+i; +} + +FCPP_LINK bool +remove_last_folder(String *str){ + bool result = 0; + int end = reverse_seek_slash(*str, 1); + if (end >= 0){ + result = 1; + str->size = end + 1; + } + return(result); +} + +FCPP_LINK void +replace_char(String str, char replace, char with){ + char *s = str.str; + int i; + + for (i = 0; i < str.size; ++i, ++s){ + if (*s == replace) *s = with; + } +} + +// NOTE(allen): experimental section, things below here are +// not promoted to public API level yet. + +#ifndef ArrayCount +#define ArrayCount(a) ((sizeof(a))/sizeof(*a)) +#endif + +struct Absolutes{ + String a[8]; + int count; +}; + +FCPP_LINK void +get_absolutes(String name, Absolutes *absolutes, bool implicit_first, bool implicit_last){ + int count = 0; + int max = ArrayCount(absolutes->a) - 1; + if (implicit_last) --max; + + String str; + str.str = name.str; + str.size = 0; + str.memory_size = 0; + bool prev_was_wild = 0; + + if (implicit_first){ + absolutes->a[count++] = str; + prev_was_wild = 1; + } + + int i; + for (i = 0; i < name.size; ++i){ + if (name.str[i] == '*' && count < max){ + if (!prev_was_wild){ + str.memory_size = str.size; + absolutes->a[count++] = str; + str.size = 0; + } + str.str = name.str + i + 1; + prev_was_wild = 1; + } + else{ + ++str.size; + prev_was_wild = 0; + } + } + + str.memory_size = str.size; + absolutes->a[count++] = str; + + if (implicit_last){ + str.size = 0; + str.memory_size = 0; + absolutes->a[count++] = str; + } + + absolutes->count = count; +} + +FCPP_LINK bool +wildcard_match(Absolutes *absolutes, char *x, int case_sensitive){ + bool r = 1; + String *a = absolutes->a; + + bool (*match_func)(char*, String); + bool (*match_part_func)(char*, String); + + if (case_sensitive){ + match_func = match; + match_part_func = match_part; + } + else{ + match_func = match_unsensitive; + match_part_func = match_part_unsensitive; + } + + if (absolutes->count == 1){ + r = match_func(x, *a); + } + else{ + if (!match_part_func(x, *a)){ + r = 0; + } + else{ + String *max = a + absolutes->count - 1; + x += a->size; + ++a; + while (a < max){ + if (*x == 0){ + r = 0; + break; + } + if (match_part_func(x, *a)){ + x += a->size; + ++a; + } + else{ + ++x; + } + } + if (r && a->size > 0){ + r = 0; + while (*x != 0){ + if (match_part_func(x, *a) && *(x + a->size) == 0){ + r = 1; + break; + } + else{ + ++x; + } + } + } + } + } + return r; +} + +FCPP_LINK bool +wildcard_match(Absolutes *absolutes, String x, int case_sensitive){ + terminate_with_null(&x); + return wildcard_match(absolutes, x.str, case_sensitive); +} + +#endif // #ifndef FCPP_DID_STRING_IMPLEMENTATION + +#undef FCPP_STRING_IMPLEMENTATION +#endif // #ifdef FCPP_STRING_IMPLEMENTATION + +// BOTTOM + diff --git a/test_data/lots_of_files/4coder_style.h b/test_data/lots_of_files/4coder_style.h new file mode 100644 index 0000000..7c88d18 --- /dev/null +++ b/test_data/lots_of_files/4coder_style.h @@ -0,0 +1,33 @@ +enum Style_Tag{ +Stag_Bar, +Stag_Bar_Active, +Stag_Base, +Stag_Pop1, +Stag_Pop2, +Stag_Back, +Stag_Margin, +Stag_Margin_Hover, +Stag_Margin_Active, +Stag_Cursor, +Stag_At_Cursor, +Stag_Highlight, +Stag_At_Highlight, +Stag_Mark, +Stag_Default, +Stag_Comment, +Stag_Keyword, +Stag_Str_Constant, +Stag_Char_Constant, +Stag_Int_Constant, +Stag_Float_Constant, +Stag_Bool_Constant, +Stag_Preproc, +Stag_Include, +Stag_Special_Character, +Stag_Highlight_Junk, +Stag_Highlight_White, +Stag_Paste, +Stag_Undo, +Stag_Next_Undo, +}; + diff --git a/test_data/lots_of_files/4coder_version.h b/test_data/lots_of_files/4coder_version.h new file mode 100644 index 0000000..871cab4 --- /dev/null +++ b/test_data/lots_of_files/4coder_version.h @@ -0,0 +1,15 @@ +#define MAJOR 4 +#define MINOR 0 +#define PATCH 1 + +#define VN__(a,b,c) #a"."#b"."#c +#define VN_(a,b,c) VN__(a,b,c) +#define VERSION_NUMBER VN_(MAJOR,MINOR,PATCH) +#define VERSION_STRING "alpha " VERSION_NUMBER + +#ifdef FRED_SUPER +#define VERSION_TYPE " super!" +#else +#define VERSION_TYPE +#endif +#define VERSION VERSION_STRING VERSION_TYPE diff --git a/test_data/lots_of_files/4cpp_clear_config.h b/test_data/lots_of_files/4cpp_clear_config.h new file mode 100644 index 0000000..1ee671c --- /dev/null +++ b/test_data/lots_of_files/4cpp_clear_config.h @@ -0,0 +1,54 @@ +/* "4cpp" Open C++ Parser v0.1: Clear Config + no warranty implied; use at your own risk + +NOTES ON USE: + This file is used to clear options. The main use for this is for cases when you want + it include different portions of the library with different settings. So that the compiler + does not complain about redifintion, and so that you do not have to undef everything yourself + this is provided to undef everything at once. +*/ + +#ifdef FCPP_NO_CRT +#undef FCPP_NO_CRT +#endif + +#ifdef FCPP_NO_MALLOC +#undef FCPP_NO_MALLOC +#endif + +#ifdef FCPP_NO_ASSERT +#undef FCPP_NO_ASSERT +#endif + +#ifdef FCPP_NO_STRING +#undef FCPP_NO_STRING +#endif + +#ifdef FCPP_GET_MEMORY +#undef FCPP_GET_MEMORY +#endif + +#ifdef FCPP_FREE_MEMORY +#undef FCPP_FREE_MEMORY +#endif + +#ifdef FCPP_ASSERT +#undef FCPP_ASSERT +#endif + +#ifdef FCPP_MEM_COPY +#undef FCPP_MEM_COPY +#endif + +#ifdef FCPP_MEM_MOVE +#undef FCPP_MEM_MOVE +#endif + +#ifdef FCPP_LINK +#undef FCPP_LINK +#endif + +#ifdef FCPP_EXTERN +#undef FCPP_EXTERN +#endif + diff --git a/test_data/lots_of_files/4cpp_lexer.h b/test_data/lots_of_files/4cpp_lexer.h new file mode 100644 index 0000000..837af0c --- /dev/null +++ b/test_data/lots_of_files/4cpp_lexer.h @@ -0,0 +1,1949 @@ +/* "4cpp" Open C++ Parser v0.1: Lexer + no warranty implied; use at your own risk + +NOTES ON USE: + OPTIONS: + Set options by defining macros before including this file. + + FCPP_LEXER_IMPLEMENTATION - causes this file to output function implementations + - this option is unset after use so that future includes of this file + in the same unit do not continue to output implementations + + FCPP_NO_MALLOC - prevent including <stdlib.h> + FCPP_NO_ASSERT - prevent including <assert.h> + FCPP_NO_STRING - prevent including <string.h> + FCPP_NO_CRT - FCPP_NO_MALLOC & FCPP_NO_ASSERT & FCPP_NO_STRING + + FCPP_FORBID_MALLOC - one step above *NO_MALLOC with this set 4cpp functions that do allocations + are not allowed to be declared or defined at all, forcing the user to handle + allocation themselves + - implies FCPP_NO_MALLOC + + FCPP_GET_MEMORY - defines how to make allocations, interface of malloc, defaults to malloc + FCPP_FREE_MEMORY - defines how to free memory, interface of ree, defaults to free + (The above must be defined if FCPP_NO_MALLOC is set, unless FCPP_FORBID_MALLOC is set) + + FCPP_ASSERT - defines how to make assertions, interface of assert, defaults to assert + + FCPP_MEM_COPY - defines how to copy blocks of memory, interface of memcpy, defaults to memcpy + FCPP_MEM_MOVE - defines how to move blocks of memory, interface of memmove, defaults to memmove + (The above must be defined if FCPP_NO_STRING is set) + + FCPP_LINK - defines linkage of non-inline functions, defaults to static + FCPP_EXTERN - changes FCPP_LINK default to extern, this option is ignored if FCPP_LINK is defined + + include the file "4cpp_clear_config.h" if you want to undefine all options for some reason + + HIDDDEN DEPENDENCIES: + 4cpp is not a single file include library, there are dependencies between the files. + Be sure to include these dependencies before 4cpp_lexer.h: + + 4cpp_types.h + 4cpp_string.h +*/ + +// TOP +// TODO(allen): +// +// EASE OF USE AND DEPLOYMENT +// - make it easier to locate the list of function declarations +// - more C compatibility +// +// POTENTIAL +// - Experiment with optimizations. Sean's State machine? +// - Reserve 0th token for null? Put a EOF token at the end? +// - Pass Cpp_File and Cpp_Token_Stack by value instead of by pointer? +// +// CURRENT +// - lex in chunks +// + +#include "4coder_config.h" + +#ifndef FCPP_LEXER_INC +#define FCPP_LEXER_INC + +enum Cpp_Token_Type{ + CPP_TOKEN_JUNK, + CPP_TOKEN_COMMENT, + + CPP_TOKEN_KEY_TYPE, + CPP_TOKEN_KEY_MODIFIER, + CPP_TOKEN_KEY_QUALIFIER, + CPP_TOKEN_KEY_OPERATOR, // NOTE(allen): This type is not actually stored in tokens + CPP_TOKEN_KEY_CONTROL_FLOW, + CPP_TOKEN_KEY_CAST, + CPP_TOKEN_KEY_TYPE_DECLARATION, + CPP_TOKEN_KEY_ACCESS, + CPP_TOKEN_KEY_LINKAGE, + CPP_TOKEN_KEY_OTHER, + + CPP_TOKEN_IDENTIFIER, + CPP_TOKEN_INTEGER_CONSTANT, + CPP_TOKEN_CHARACTER_CONSTANT, + CPP_TOKEN_FLOATING_CONSTANT, + CPP_TOKEN_STRING_CONSTANT, + CPP_TOKEN_BOOLEAN_CONSTANT, + + CPP_TOKEN_STATIC_ASSERT, + + CPP_TOKEN_BRACKET_OPEN, + CPP_TOKEN_BRACKET_CLOSE, + CPP_TOKEN_PARENTHESE_OPEN, + CPP_TOKEN_PARENTHESE_CLOSE, + CPP_TOKEN_BRACE_OPEN, + CPP_TOKEN_BRACE_CLOSE, + CPP_TOKEN_SEMICOLON, + CPP_TOKEN_ELLIPSIS, + + // NOTE(allen): Ambiguous tokens, lexer only, + // parser figures out the real meaning + CPP_TOKEN_STAR, + CPP_TOKEN_AMPERSAND, + CPP_TOKEN_TILDE, + CPP_TOKEN_PLUS, + CPP_TOKEN_MINUS, + CPP_TOKEN_INCREMENT, + CPP_TOKEN_DECREMENT, + + // NOTE(allen): Precedence 1, LtoR + CPP_TOKEN_SCOPE, + + // NOTE(allen): Precedence 2, LtoR + CPP_TOKEN_POSTINC, // from increment, parser only + CPP_TOKEN_POSTDEC, // from decrement, parser only + CPP_TOKEN_FUNC_STYLE_CAST, // parser only + CPP_TOKEN_CPP_STYLE_CAST, + CPP_TOKEN_CALL, // from open paren, parser only + CPP_TOKEN_INDEX, // from bracket open, parser only + CPP_TOKEN_DOT, + CPP_TOKEN_ARROW, + + // NOTE(allen): Precedence 3, RtoL + CPP_TOKEN_PREINC, // from increment, parser only + CPP_TOKEN_PREDEC, // from decrement, parser only + CPP_TOKEN_POSITIVE, // from plus, parser only + CPP_TOKEN_NEGAITVE, // from minus, parser only + CPP_TOKEN_NOT, + CPP_TOKEN_BIT_NOT, // from tilde, direct from 'compl' + CPP_TOKEN_CAST, // from open paren, parser only + CPP_TOKEN_DEREF, // from star, parser only + CPP_TOKEN_TYPE_PTR, // from star, parser only + CPP_TOKEN_ADDRESS, // from ampersand, parser only + CPP_TOKEN_TYPE_REF, // from ampersand, parser only + CPP_TOKEN_SIZEOF, + CPP_TOKEN_ALIGNOF, + CPP_TOKEN_DECLTYPE, + CPP_TOKEN_TYPEID, + CPP_TOKEN_NEW, + CPP_TOKEN_DELETE, + CPP_TOKEN_NEW_ARRAY, // from new and bracket open, parser only + CPP_TOKEN_DELETE_ARRAY, // from delete and bracket open, parser only + + // NOTE(allen): Precedence 4, LtoR + CPP_TOKEN_PTRDOT, + CPP_TOKEN_PTRARROW, + + // NOTE(allen): Precedence 5, LtoR + CPP_TOKEN_MUL, // from start, parser only + CPP_TOKEN_DIV, + CPP_TOKEN_MOD, + + // NOTE(allen): Precedence 6, LtoR + CPP_TOKEN_ADD, // from plus, parser only + CPP_TOKEN_SUB, // from minus, parser only + + // NOTE(allen): Precedence 7, LtoR + CPP_TOKEN_LSHIFT, + CPP_TOKEN_RSHIFT, + + // NOTE(allen): Precedence 8, LtoR + CPP_TOKEN_LESS, + CPP_TOKEN_GRTR, + CPP_TOKEN_GRTREQ, + CPP_TOKEN_LESSEQ, + + // NOTE(allen): Precedence 9, LtoR + CPP_TOKEN_EQEQ, + CPP_TOKEN_NOTEQ, + + // NOTE(allen): Precedence 10, LtoR + CPP_TOKEN_BIT_AND, // from ampersand, direct from 'bitand' + + // NOTE(allen): Precedence 11, LtoR + CPP_TOKEN_BIT_XOR, + + // NOTE(allen): Precedence 12, LtoR + CPP_TOKEN_BIT_OR, + + // NOTE(allen): Precedence 13, LtoR + CPP_TOKEN_AND, + + // NOTE(allen): Precedence 14, LtoR + CPP_TOKEN_OR, + + // NOTE(allen): Precedence 15, RtoL + CPP_TOKEN_TERNARY_QMARK, + CPP_TOKEN_COLON, + CPP_TOKEN_THROW, + CPP_TOKEN_EQ, + CPP_TOKEN_ADDEQ, + CPP_TOKEN_SUBEQ, + CPP_TOKEN_MULEQ, + CPP_TOKEN_DIVEQ, + CPP_TOKEN_MODEQ, + CPP_TOKEN_LSHIFTEQ, + CPP_TOKEN_RSHIFTEQ, + CPP_TOKEN_ANDEQ, + CPP_TOKEN_OREQ, + CPP_TOKEN_XOREQ, + + // NOTE(allen): Precedence 16, LtoR + CPP_TOKEN_COMMA, + + CPP_PP_INCLUDE, + CPP_PP_DEFINE, + CPP_PP_UNDEF, + CPP_PP_IF, + CPP_PP_IFDEF, + CPP_PP_IFNDEF, + CPP_PP_ELSE, + CPP_PP_ELIF, + CPP_PP_ENDIF, + CPP_PP_ERROR, + CPP_PP_IMPORT, + CPP_PP_USING, + CPP_PP_LINE, + CPP_PP_PRAGMA, + CPP_PP_STRINGIFY, + CPP_PP_CONCAT, + CPP_PP_UNKNOWN, + CPP_TOKEN_DEFINED, + CPP_TOKEN_INCLUDE_FILE, + CPP_TOKEN_ERROR_MESSAGE, + + // NOTE(allen): used in the parser + CPP_TOKEN_EOF +}; + +// TODO(allen): This is a dumb redundant type... probably just +// move towards using String for this everywhere eventually. +struct Cpp_File{ + char *data; + int size; +}; + +Cpp_File +data_as_cpp_file(Data data){ + Cpp_File result; + result.data = (char*)data.data; + result.size = data.size; + return(result); +} + +struct Cpp_Token{ + Cpp_Token_Type type; + fcpp_i32 start, size; + fcpp_u16 state_flags; + fcpp_u16 flags; +}; + +enum Cpp_Token_Flag{ + CPP_TFLAG_IGNORE = 1 << 0, + CPP_TFLAG_PP_DIRECTIVE = 1 << 1, + CPP_TFLAG_PP_BODY = 1 << 2, + CPP_TFLAG_BAD_ENDING = 1 << 3, + CPP_TFLAG_MULTILINE = 1 << 4, + CPP_TFLAG_PARAMETERIZED = 1 << 5, + CPP_TFLAG_IS_OPERATOR = 1 << 6, + CPP_TFLAG_IS_KEYWORD = 1 << 7 +}; + +enum Cpp_Preprocessor_State{ + CPP_LEX_PP_DEFAULT, + CPP_LEX_PP_IDENTIFIER, + CPP_LEX_PP_MACRO_IDENTIFIER, + CPP_LEX_PP_INCLUDE, + CPP_LEX_PP_BODY, + CPP_LEX_PP_BODY_IF, + CPP_LEX_PP_NUMBER, + CPP_LEX_PP_ERROR, + CPP_LEX_PP_JUNK, + // NEVER ADD BELOW THIS + CPP_LEX_PP_COUNT +}; + +struct Cpp_Lex_Data{ + Cpp_Preprocessor_State pp_state; + fcpp_i32 pos; + fcpp_bool32 complete; +}; + +struct Cpp_Read_Result{ + Cpp_Token token; + fcpp_i32 pos; + fcpp_bool8 newline; + fcpp_bool8 has_result; +}; + +struct Cpp_Token_Stack{ + Cpp_Token *tokens; + int count, max_count; +}; + +struct Cpp_Token_Merge{ + Cpp_Token new_token; + fcpp_bool32 did_merge; +}; + +struct Seek_Result{ + fcpp_i32 pos; + fcpp_bool32 new_line; +}; + +struct Cpp_Get_Token_Result{ + fcpp_i32 token_index; + fcpp_bool32 in_whitespace; +}; + +// TODO(allen): revisit this keyword data declaration system +struct String_And_Flag{ + char *str; + fcpp_u32 flags; +}; + +struct String_List{ + String_And_Flag *data; + int count; +}; + +struct Sub_Match_List_Result{ + int index; + fcpp_i32 new_pos; +}; + +inline fcpp_u16 +cpp_token_set_pp_state(fcpp_u16 bitfield, Cpp_Preprocessor_State state_value){ + return (fcpp_u16)state_value; +} + +inline Cpp_Preprocessor_State +cpp_token_get_pp_state(fcpp_u16 bitfield){ + return (Cpp_Preprocessor_State)(bitfield); +} + +inline String +cpp_get_lexeme(char *str, Cpp_Token *token){ + String result; + result.str = str + token->start; + result.size = token->size; + return result; +} + +inline bool +is_keyword(Cpp_Token_Type type){ + return (type >= CPP_TOKEN_KEY_TYPE && type <= CPP_TOKEN_KEY_OTHER); +} + +FCPP_LINK Sub_Match_List_Result sub_match_list(Cpp_File file, int pos, String_List list, int sub_size); + +FCPP_LINK Seek_Result seek_unescaped_eol(char *data, int size, int pos); +FCPP_LINK Seek_Result seek_unescaped_delim(char *data, int size, int pos, char delim); +FCPP_LINK Seek_Result seek_block_comment_end(char *data, int size, int pos); + +FCPP_LINK Cpp_Read_Result cpp_read_whitespace(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_junk_line(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_operator(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_pp_operator(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_alpha_numeric(Cpp_File file, int pos, bool in_if_body); +inline Cpp_Read_Result cpp_read_alpha_numeric(Cpp_File file, int pos) { return cpp_read_alpha_numeric(file, pos, 0); } +FCPP_LINK Cpp_Read_Result cpp_read_number(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_string_litteral(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_character_litteral(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_line_comment(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_block_comment(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_preprocessor(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_pp_include_file(Cpp_File file, int pos); +FCPP_LINK Cpp_Read_Result cpp_read_pp_default_mode(Cpp_File file, int pos, bool in_if_body); +inline Cpp_Read_Result cpp_read_pp_default_mode(Cpp_File file, int pos) { return cpp_read_pp_default_mode(file, pos, 0); } + +FCPP_LINK Cpp_Token_Merge cpp_attempt_token_merge(Cpp_Token prev, Cpp_Token next); + +FCPP_LINK bool cpp_push_token_no_merge(Cpp_Token_Stack *stack, Cpp_Token token); +FCPP_LINK bool cpp_push_token_nonalloc(Cpp_Token_Stack *stack, Cpp_Token token); + +FCPP_LINK Cpp_Read_Result cpp_lex_step(Cpp_File file, Cpp_Lex_Data *lex); + +FCPP_LINK int cpp_lex_file_token_count(Cpp_File file); +FCPP_LINK Cpp_Lex_Data cpp_lex_file_nonalloc(Cpp_File file, Cpp_Token_Stack *stack, Cpp_Lex_Data data); +inline Cpp_Lex_Data cpp_lex_file_nonalloc(Cpp_File file, Cpp_Token_Stack *stack) { return cpp_lex_file_nonalloc(file, stack, {}); } + +FCPP_LINK Cpp_Get_Token_Result cpp_get_token(Cpp_Token_Stack *stack, int pos); + +FCPP_LINK int cpp_get_end_token(Cpp_Token_Stack *stack, int end); +FCPP_LINK void cpp_shift_token_starts(Cpp_Token_Stack *stack, int from_token, int amount); + +struct Cpp_Relex_State{ + Cpp_File file; + Cpp_Token_Stack *stack; + int start, end, amount; + int start_token_i; + int end_token_i; + int relex_start; + int tolerance; + int space_request; +}; + +FCPP_LINK Cpp_Relex_State cpp_relex_nonalloc_start(Cpp_File file, Cpp_Token_Stack *stack, int start, int end, int amount, int tolerance); +FCPP_LINK bool cpp_relex_nonalloc_main(Cpp_Relex_State state, Cpp_Token_Stack *stack); + +#ifndef FCPP_FORBID_MALLOC +FCPP_LINK Cpp_Token_Stack cpp_make_token_stack(int max); +FCPP_LINK void cpp_free_token_stack(Cpp_Token_Stack stack); +FCPP_LINK void cpp_resize_token_stack(Cpp_Token_Stack *stack, int new_max); + +FCPP_LINK void cpp_push_token(Cpp_Token_Stack *stack, Cpp_Token token); +FCPP_LINK void cpp_lex_file(Cpp_File file, Cpp_Token_Stack *stack); +FCPP_LINK bool cpp_relex_file_limited(Cpp_File file, Cpp_Token_Stack *stack, int start_i, int end_i, int amount, int extra_tolerance); +inline void cpp_relex_file(Cpp_File file, Cpp_Token_Stack *stack, int start_i, int end_i, int amount) +{ cpp_relex_file_limited(file, stack, start_i, end_i, amount, -1); } +#endif + +#define FCPP_STRING_LIST(x) {x, FCPP_COUNT(x)} + +// TODO(allen): shift towards storing in a context +FCPP_GLOBAL String_And_Flag int_suf_strings[] = { + {"ull"}, {"ULL"}, + {"llu"}, {"LLU"}, + {"ll"}, {"LL"}, + {"l"}, {"L"}, + {"u"}, {"U"} +}; + +FCPP_GLOBAL String_List int_sufs = FCPP_STRING_LIST(int_suf_strings); + +FCPP_GLOBAL String_And_Flag float_suf_strings[] = { + {"f"}, {"F"}, + {"l"}, {"L"} +}; +FCPP_GLOBAL String_List float_sufs = FCPP_STRING_LIST(float_suf_strings); + +FCPP_GLOBAL String_And_Flag bool_lit_strings[] = { + {"true"}, {"false"} +}; +FCPP_GLOBAL String_List bool_lits = FCPP_STRING_LIST(bool_lit_strings); + +FCPP_GLOBAL String_And_Flag keyword_strings[] = { + {"and", CPP_TOKEN_AND}, + {"and_eq", CPP_TOKEN_ANDEQ}, + {"bitand", CPP_TOKEN_BIT_AND}, + {"bitor", CPP_TOKEN_BIT_OR}, + {"or", CPP_TOKEN_OR}, + {"or_eq", CPP_TOKEN_OREQ}, + {"sizeof", CPP_TOKEN_SIZEOF}, + {"alignof", CPP_TOKEN_ALIGNOF}, + {"decltype", CPP_TOKEN_DECLTYPE}, + {"throw", CPP_TOKEN_THROW}, + {"new", CPP_TOKEN_NEW}, + {"delete", CPP_TOKEN_DELETE}, + {"xor", CPP_TOKEN_BIT_XOR}, + {"xor_eq", CPP_TOKEN_XOREQ}, + {"not", CPP_TOKEN_NOT}, + {"not_eq", CPP_TOKEN_NOTEQ}, + {"typeid", CPP_TOKEN_TYPEID}, + {"compl", CPP_TOKEN_BIT_NOT}, + + {"void", CPP_TOKEN_KEY_TYPE}, + {"bool", CPP_TOKEN_KEY_TYPE}, + {"char", CPP_TOKEN_KEY_TYPE}, + {"int", CPP_TOKEN_KEY_TYPE}, + {"float", CPP_TOKEN_KEY_TYPE}, + {"double", CPP_TOKEN_KEY_TYPE}, + + {"long", CPP_TOKEN_KEY_MODIFIER}, + {"short", CPP_TOKEN_KEY_MODIFIER}, + {"unsigned", CPP_TOKEN_KEY_MODIFIER}, + + {"const", CPP_TOKEN_KEY_QUALIFIER}, + {"volatile", CPP_TOKEN_KEY_QUALIFIER}, + + {"asm", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"break", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"case", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"catch", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"continue", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"default", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"do", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"else", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"for", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"goto", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"if", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"return", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"switch", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"try", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"while", CPP_TOKEN_KEY_CONTROL_FLOW}, + {"static_assert", CPP_TOKEN_KEY_CONTROL_FLOW}, + + {"const_cast", CPP_TOKEN_KEY_CAST}, + {"dynamic_cast", CPP_TOKEN_KEY_CAST}, + {"reinterpret_cast", CPP_TOKEN_KEY_CAST}, + {"static_cast", CPP_TOKEN_KEY_CAST}, + + {"class", CPP_TOKEN_KEY_TYPE_DECLARATION}, + {"enum", CPP_TOKEN_KEY_TYPE_DECLARATION}, + {"struct", CPP_TOKEN_KEY_TYPE_DECLARATION}, + {"typedef", CPP_TOKEN_KEY_TYPE_DECLARATION}, + {"union", CPP_TOKEN_KEY_TYPE_DECLARATION}, + {"template", CPP_TOKEN_KEY_TYPE_DECLARATION}, + {"typename", CPP_TOKEN_KEY_TYPE_DECLARATION}, + + {"friend", CPP_TOKEN_KEY_ACCESS}, + {"namespace", CPP_TOKEN_KEY_ACCESS}, + {"private", CPP_TOKEN_KEY_ACCESS}, + {"protected", CPP_TOKEN_KEY_ACCESS}, + {"public", CPP_TOKEN_KEY_ACCESS}, + {"using", CPP_TOKEN_KEY_ACCESS}, + + {"extern", CPP_TOKEN_KEY_LINKAGE}, + {"export", CPP_TOKEN_KEY_LINKAGE}, + {"inline", CPP_TOKEN_KEY_LINKAGE}, + {"static", CPP_TOKEN_KEY_LINKAGE}, + {"virtual", CPP_TOKEN_KEY_LINKAGE}, + + {"alignas", CPP_TOKEN_KEY_OTHER}, + {"explicit", CPP_TOKEN_KEY_OTHER}, + {"noexcept", CPP_TOKEN_KEY_OTHER}, + {"nullptr", CPP_TOKEN_KEY_OTHER}, + {"operator", CPP_TOKEN_KEY_OTHER}, + {"register", CPP_TOKEN_KEY_OTHER}, + {"this", CPP_TOKEN_KEY_OTHER}, + {"thread_local", CPP_TOKEN_KEY_OTHER}, +}; +FCPP_GLOBAL String_List keywords = FCPP_STRING_LIST(keyword_strings); + +FCPP_GLOBAL String_And_Flag op_strings[] = { + {"...", CPP_TOKEN_ELLIPSIS}, + {"<<=", CPP_TOKEN_LSHIFTEQ}, + {">>=", CPP_TOKEN_RSHIFTEQ}, + {"->*", CPP_TOKEN_PTRARROW}, + {"<<", CPP_TOKEN_LSHIFT}, + {">>", CPP_TOKEN_RSHIFT}, + {"&&", CPP_TOKEN_AND}, + {"||", CPP_TOKEN_OR}, + {"->", CPP_TOKEN_ARROW}, + {"++", CPP_TOKEN_INCREMENT}, + {"--", CPP_TOKEN_DECREMENT}, + {"::", CPP_TOKEN_SCOPE}, + {"+=", CPP_TOKEN_ADDEQ}, + {"-=", CPP_TOKEN_SUBEQ}, + {"*=", CPP_TOKEN_MULEQ}, + {"/=", CPP_TOKEN_DIVEQ}, + {"%=", CPP_TOKEN_MODEQ}, + {"&=", CPP_TOKEN_ANDEQ}, + {"|=", CPP_TOKEN_OREQ}, + {"^=", CPP_TOKEN_XOREQ}, + {"==", CPP_TOKEN_EQEQ}, + {">=", CPP_TOKEN_GRTREQ}, + {"<=", CPP_TOKEN_LESSEQ}, + {"!=", CPP_TOKEN_NOTEQ}, + {".*", CPP_TOKEN_PTRDOT}, + {"{", CPP_TOKEN_BRACE_OPEN}, + {"}", CPP_TOKEN_BRACE_CLOSE}, + {"[", CPP_TOKEN_BRACKET_OPEN}, + {"]", CPP_TOKEN_BRACKET_CLOSE}, + {"(", CPP_TOKEN_PARENTHESE_OPEN}, + {")", CPP_TOKEN_PARENTHESE_CLOSE}, + {"<", CPP_TOKEN_LESS}, + {">", CPP_TOKEN_GRTR}, + {"+", CPP_TOKEN_PLUS}, + {"-", CPP_TOKEN_MINUS}, + {"!", CPP_TOKEN_NOT}, + {"~", CPP_TOKEN_TILDE}, + {"*", CPP_TOKEN_STAR}, + {"&", CPP_TOKEN_AMPERSAND}, + {"|", CPP_TOKEN_BIT_OR}, + {"^", CPP_TOKEN_BIT_XOR}, + {"=", CPP_TOKEN_EQ}, + {",", CPP_TOKEN_COMMA}, + {":", CPP_TOKEN_COLON}, + {";", CPP_TOKEN_SEMICOLON}, + {"/", CPP_TOKEN_DIV}, + {"?", CPP_TOKEN_TERNARY_QMARK}, + {"%", CPP_TOKEN_MOD}, + {".", CPP_TOKEN_DOT}, +}; +FCPP_GLOBAL String_List ops = FCPP_STRING_LIST(op_strings); + +FCPP_GLOBAL String_And_Flag pp_op_strings[] = { + {"##", CPP_PP_CONCAT}, + {"#", CPP_PP_STRINGIFY}, +}; +FCPP_GLOBAL String_List pp_ops = FCPP_STRING_LIST(pp_op_strings); + +FCPP_GLOBAL String_And_Flag preprop_strings[] = { + {"include", CPP_PP_INCLUDE}, + {"INCLUDE", CPP_PP_INCLUDE}, + {"ifndef", CPP_PP_IFNDEF}, + {"IFNDEF", CPP_PP_IFNDEF}, + {"define", CPP_PP_DEFINE}, + {"DEFINE", CPP_PP_DEFINE}, + {"import", CPP_PP_IMPORT}, + {"IMPORT", CPP_PP_IMPORT}, + {"pragma", CPP_PP_PRAGMA}, + {"PRAGMA", CPP_PP_PRAGMA}, + {"undef", CPP_PP_UNDEF}, + {"UNDEF", CPP_PP_UNDEF}, + {"endif", CPP_PP_ENDIF}, + {"ENDIF", CPP_PP_ENDIF}, + {"error", CPP_PP_ERROR}, + {"ERROR", CPP_PP_ERROR}, + {"ifdef", CPP_PP_IFDEF}, + {"IFDEF", CPP_PP_IFDEF}, + {"using", CPP_PP_USING}, + {"USING", CPP_PP_USING}, + {"else", CPP_PP_ELSE}, + {"ELSE", CPP_PP_ELSE}, + {"elif", CPP_PP_ELIF}, + {"ELIF", CPP_PP_ELIF}, + {"line", CPP_PP_LINE}, + {"LINE", CPP_PP_LINE}, + {"if", CPP_PP_IF}, + {"IF", CPP_PP_IF}, +}; +FCPP_GLOBAL String_List preprops = FCPP_STRING_LIST(preprop_strings); + +#undef FCPP_STRING_LIST + +#endif // #ifndef FCPP_CPP_LEXER + +#ifdef FCPP_LEXER_IMPLEMENTATION + +#define _Assert FCPP_ASSERT +#define _TentativeAssert FCPP_ASSERT + +FCPP_LINK Sub_Match_List_Result +sub_match_list(Cpp_File file, int pos, String_List list, int sub_size){ + Sub_Match_List_Result result; + String str_main; + char *str_check; + int i,l; + + result.index = -1; + result.new_pos = pos; + str_main = make_string(file.data + pos, file.size - pos); + if (sub_size > 0){ + str_main = substr(str_main, 0, sub_size); + for (i = 0; i < list.count; ++i){ + str_check = list.data[i].str; + if (match(str_main, str_check)){ + result.index = i; + result.new_pos = pos + sub_size; + break; + } + } + } + else{ + for (i = 0; i < list.count; ++i){ + str_check = list.data[i].str; + if (match_part(str_main, str_check, &l)){ + result.index = i; + result.new_pos = pos + l; + break; + } + } + } + return result; +} + +FCPP_LINK Seek_Result +seek_unescaped_eol(char *data, int size, int pos){ + Seek_Result result = {}; + ++pos; + while (pos < size){ + if (data[pos] == '\\'){ + if (pos + 1 < size && + data[pos+1] == '\n'){ + result.new_line = 1; + ++pos; + } + else if (pos + 1 < size && + data[pos+1] == '\r' && + pos + 2 < size && + data[pos+2] == '\n'){ + result.new_line = 1; + pos += 2; + } + } + else if (data[pos] == '\n'){ + break; + } + ++pos; + } + ++pos; + + result.pos = pos; + return result; +} + +FCPP_LINK Seek_Result +seek_unescaped_delim(char *data, int size, int pos, char delim){ + Seek_Result result = {}; + bool escape = 0; + ++pos; + while (pos < size){ + if (data[pos] == '\n'){ + result.new_line = 1; + } + if (escape){ + escape = 0; + } + else{ + if (data[pos] == '\\'){ + escape = 1; + } + else if (data[pos] == delim){ + break; + } + } + ++pos; + } + ++pos; + + result.pos = pos; + return result; +} + +FCPP_LINK Seek_Result +seek_block_comment_end(char *data, int size, int pos){ + Seek_Result result = {}; + pos += 2; + while (pos < size){ + if (data[pos] == '*' && + pos + 1 < size && + data[pos+1] == '/'){ + break; + } + if (data[pos] == '\n'){ + result.new_line = 1; + } + ++pos; + } + pos += 2; + result.pos = pos; + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_whitespace(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + + while (pos < file.size && char_is_whitespace(file.data[pos])){ + if (file.data[pos] == '\n'){ + result.newline = 1; + } + ++pos; + } + + result.pos = pos; + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_junk_line(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.token.start = pos; + result.token.type = CPP_TOKEN_JUNK; + + bool comment_end = 0; + while (pos < file.size && file.data[pos] != '\n'){ + if (file.data[pos] == '/' && pos + 1 < file.size){ + if (file.data[pos + 1] == '/' || + file.data[pos + 1] == '*'){ + comment_end = 1; + break; + } + } + ++pos; + } + + if (comment_end){ + result.pos = pos; + result.token.size = pos - result.token.start; + } + else{ + while (pos > 0 && file.data[pos - 1] == '\r'){ + --pos; + } + if (pos > 0 && file.data[pos - 1] == '\\'){ + --pos; + } + result.pos = pos; + result.token.size = pos - result.token.start; + } + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_operator(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.pos = pos; + result.token.start = pos; + + Sub_Match_List_Result match; + match = sub_match_list(file, result.token.start, ops, -1); + + if (match.index != -1){ + result.pos = match.new_pos; + result.token.size = result.pos - result.token.start; + result.token.type = (Cpp_Token_Type)ops.data[match.index].flags; + result.token.flags |= CPP_TFLAG_IS_OPERATOR; + } + else{ + result.token.size = 1; + result.token.type = CPP_TOKEN_JUNK; + result.pos = pos + 1; + } + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_pp_operator(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.pos = pos; + result.token.start = pos; + + Sub_Match_List_Result match; + match = sub_match_list(file, result.token.start, pp_ops, -1); + + _Assert(match.index != -1); + result.pos = match.new_pos; + result.token.size = result.pos - result.token.start; + result.token.type = (Cpp_Token_Type)pp_ops.data[match.index].flags; + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_alpha_numeric(Cpp_File file, int pos, bool in_if_body){ + Cpp_Read_Result result = {}; + result.pos = pos; + result.token.start = pos; + + while (result.pos < file.size && + char_is_alpha_numeric(file.data[result.pos])){ + ++result.pos; + } + + result.token.size = result.pos - result.token.start; + + // TODO(allen): do better + if (in_if_body){ + String word; + word.size = result.token.size; + word.str = file.data + result.token.start; + if (match(word, "defined")){ + result.token.type = CPP_TOKEN_DEFINED; + result.token.flags |= CPP_TFLAG_IS_OPERATOR; + result.token.flags |= CPP_TFLAG_IS_KEYWORD; + } + } + + if (result.token.type == CPP_TOKEN_JUNK){ + Sub_Match_List_Result match; + match = sub_match_list(file, result.token.start, bool_lits, result.token.size); + + if (match.index != -1){ + result.token.type = CPP_TOKEN_BOOLEAN_CONSTANT; + result.token.flags |= CPP_TFLAG_IS_KEYWORD; + } + else{ + match = sub_match_list(file, result.token.start, keywords, result.token.size); + + if (match.index != -1){ + String_And_Flag data = keywords.data[match.index]; + result.token.type = (Cpp_Token_Type)data.flags; + result.token.flags |= CPP_TFLAG_IS_KEYWORD; + } + else{ + result.token.type = CPP_TOKEN_IDENTIFIER; + } + } + } + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_number(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.pos = pos; + result.token.start = pos; + + bool is_float = 0; + bool is_integer = 0; + bool is_oct = 0; + bool is_hex = 0; + bool is_zero = 0; + + if (file.data[pos] == '0'){ + if (pos+1 < file.size){ + char next = file.data[pos+1]; + if (next == 'x'){ + is_hex = 1; + is_integer = 1; + } + else if (next == '.'){ + is_float = 1; + ++result.pos; + } + else if (next >= '0' && next <= '9'){ + is_oct = 1; + is_integer = 1; + } + else{ + is_zero = 1; + is_integer = 1; + } + } + else{ + is_zero = 1; + is_integer = 1; + } + } + else if (file.data[pos] == '.'){ + is_float = 1; + } + + if (is_zero){ + ++result.pos; + } + else if (is_hex){ + ++result.pos; + char character; + do{ + ++result.pos; + if (result.pos >= file.size){ + break; + } + character = file.data[result.pos]; + } while(char_is_hex(character)); + } + else if (is_oct){ + char character; + do{ + ++result.pos; + if (result.pos >= file.size){ + break; + } + character = file.data[result.pos]; + }while(char_is_numeric(character)); + } + else{ + if (!is_float){ + is_integer = 1; + while (1){ + ++result.pos; + + if (result.pos >= file.size){ + break; + } + bool is_good = 0; + char character = file.data[result.pos]; + if (character >= '0' && character <= '9'){ + is_good = 1; + } + else if (character == '.'){ + is_integer = 0; + is_float = 1; + } + if (!is_good){ + break; + } + } + } + + if (is_float){ + bool e_mode = 0; + bool e_minus = 0; + bool is_good = 0; + char character; + + while (1){ + ++result.pos; + if (result.pos >= file.size){ + break; + } + is_good = 0; + character = file.data[result.pos]; + if (character >= '0' && character <= '9'){ + is_good = 1; + } + else{ + if (character == 'e' && !e_mode){ + e_mode = 1; + is_good = 1; + } + else if (character == '-' && e_mode && !e_minus){ + e_minus = 1; + is_good = 1; + } + } + if (!is_good){ + break; + } + } + } + } + + if (is_integer){ + Sub_Match_List_Result match = + sub_match_list(file, result.pos, int_sufs, -1); + if (match.index != -1){ + result.pos = match.new_pos; + } + result.token.type = CPP_TOKEN_INTEGER_CONSTANT; + result.token.size = result.pos - result.token.start; + } + else if (is_float){ + Sub_Match_List_Result match = + sub_match_list(file, result.pos, float_sufs, -1); + if (match.index != -1){ + result.pos = match.new_pos; + } + result.token.type = CPP_TOKEN_FLOATING_CONSTANT; + result.token.size = result.pos - result.token.start; + } + else{ + _Assert(!"This shouldn't happen!"); + } + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_string_litteral(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.token.start = pos; + + _Assert(file.data[pos] == '"'); + Seek_Result seek = seek_unescaped_delim(file.data, file.size, pos, '"'); + pos = seek.pos; + if (seek.new_line){ + result.token.flags |= CPP_TFLAG_MULTILINE; + } + + result.token.size = pos - result.token.start; + result.token.type = CPP_TOKEN_STRING_CONSTANT; + result.pos = pos; + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_character_litteral(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.token.start = pos; + + _Assert(file.data[pos] == '\''); + Seek_Result seek = seek_unescaped_delim(file.data, file.size, pos, '\''); + pos = seek.pos; + if (seek.new_line){ + result.token.flags |= CPP_TFLAG_MULTILINE; + } + + result.token.size = pos - result.token.start; + result.token.type = CPP_TOKEN_CHARACTER_CONSTANT; + result.pos = pos; + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_line_comment(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.token.start = pos; + + _Assert(file.data[pos] == '/' && file.data[pos + 1] == '/'); + + pos += 2; + while (pos < file.size){ + if (file.data[pos] == '\n'){ + break; + } + if (file.data[pos] == '\\'){ + if (pos + 1 < file.size && + file.data[pos + 1] == '\n'){ + ++pos; + } + else if (pos + 2 < file.size && + file.data[pos + 1] == '\r' && + file.data[pos + 2] == '\n'){ + pos += 2; + } + } + ++pos; + } + if (pos > 0 && file.data[pos-1] == '\r'){ + --pos; + } + result.token.size = pos - result.token.start; + result.token.type = CPP_TOKEN_COMMENT; + result.pos = pos; + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_block_comment(Cpp_File file, int pos){ + Cpp_Read_Result result = {}; + result.token.start = pos; + + _Assert(file.data[pos] == '/' && file.data[pos + 1] == '*'); + pos += 2; + while (pos < file.size){ + if (file.data[pos] == '*' && + pos + 1 < file.size && + file.data[pos+1] == '/'){ + break; + } + ++pos; + } + pos += 2; + result.token.size = pos - result.token.start; + result.token.type = CPP_TOKEN_COMMENT; + result.pos = pos; + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_preprocessor(Cpp_File file, int pos){ + _Assert(file.data[pos] == '#'); + Cpp_Read_Result result = {}; + result.token.start = pos; + result.token.type = CPP_PP_UNKNOWN; + result.token.flags |= CPP_TFLAG_PP_DIRECTIVE; + + ++pos; + while (pos < file.size && + (file.data[pos] == ' ' || + file.data[pos] == '\t')){ + ++pos; + } + + Sub_Match_List_Result match + = sub_match_list(file, pos, preprops, -1); + + if (match.index != -1){ + result.token.size = match.new_pos - result.token.start; + result.token.type = (Cpp_Token_Type)preprops.data[match.index].flags; + result.pos = match.new_pos; + } + else{ + while (pos < file.size && + !char_is_whitespace(file.data[pos])){ + ++pos; + } + result.token.size = pos - result.token.start; + result.pos = pos; + } + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_pp_include_file(Cpp_File file, int pos){ + char start = file.data[pos]; + _Assert(start == '<' || start == '"'); + + Cpp_Read_Result result = {}; + result.token.start = pos; + result.token.type = CPP_TOKEN_INCLUDE_FILE; + result.token.flags |= CPP_TFLAG_PP_BODY; + + char end; + if (start == '<'){ + end = '>'; + } + else{ + end = '"'; + } + + ++pos; + while (pos < file.size && file.data[pos] != end){ + if (file.data[pos] == '\n'){ + result.token.type = CPP_TOKEN_JUNK; + result.token.flags |= CPP_TFLAG_BAD_ENDING; + break; + } + if (file.data[pos] == '\\'){ + // TODO(allen): Not sure that this is 100% correct. + if (pos + 1 < file.size && + file.data[pos + 1] == '\n'){ + ++pos; + result.token.flags |= CPP_TFLAG_MULTILINE; + } + else if (pos + 2 < file.size && + file.data[pos + 1] == '\r' && + file.data[pos + 2] == '\n'){ + pos += 2; + result.token.flags |= CPP_TFLAG_MULTILINE; + } + } + ++pos; + } + + if (result.token.type != CPP_TOKEN_JUNK){ + if (pos < file.size){ + ++pos; + } + } + + result.token.size = pos - result.token.start; + result.pos = pos; + + return result; +} + +FCPP_LINK Cpp_Read_Result +cpp_read_pp_default_mode(Cpp_File file, int pos, bool in_if_body){ + char current = file.data[pos]; + Cpp_Read_Result result; + if (char_is_numeric(current)){ + result = cpp_read_number(file, pos); + } + else if (char_is_alpha(current)){ + result = cpp_read_alpha_numeric(file, pos, in_if_body); + } + else if (current == '.'){ + if (pos + 1 < file.size){ + char next = file.data[pos + 1]; + if (char_is_numeric(next)){ + result = cpp_read_number(file, pos); + } + else{ + result = cpp_read_operator(file, pos); + } + } + else{ + result = cpp_read_operator(file, pos); + } + } + + else if (current == '/'){ + if (pos + 1 < file.size){ + char next = file.data[pos + 1]; + if (next == '/'){ + result = cpp_read_line_comment(file, pos); + } + else if (next == '*'){ + result = cpp_read_block_comment(file, pos); + } + else{ + result = cpp_read_operator(file, pos); + } + } + else{ + result = cpp_read_operator(file, pos); + } + } + else if (current == '"'){ + result = cpp_read_string_litteral(file, pos); + } + else if (current == '\''){ + result = cpp_read_character_litteral(file, pos); + } + else{ + result = cpp_read_operator(file, pos); + } + + return result; +} + +FCPP_LINK Cpp_Token_Merge +cpp_attempt_token_merge(Cpp_Token prev_token, Cpp_Token next_token){ + Cpp_Token_Merge result = {}; + if (next_token.type == CPP_TOKEN_COMMENT && prev_token.type == CPP_TOKEN_COMMENT && + next_token.flags == prev_token.flags && next_token.state_flags == prev_token.state_flags){ + result.did_merge = 1; + prev_token.size = next_token.start + next_token.size - prev_token.start; + result.new_token = prev_token; + } + else if (next_token.type == CPP_TOKEN_JUNK && prev_token.type == CPP_TOKEN_JUNK && + next_token.flags == prev_token.flags && next_token.state_flags == prev_token.state_flags){ + result.did_merge = 1; + prev_token.size = next_token.start + next_token.size - prev_token.start; + result.new_token = prev_token; + } + return result; +} + +FCPP_LINK bool +cpp_push_token_no_merge(Cpp_Token_Stack *token_stack, Cpp_Token token){ + if (token_stack->count >= token_stack->max_count){ + return 0; + } + + token_stack->tokens[token_stack->count++] = token; + return 1; +} + +FCPP_LINK bool +cpp_push_token_nonalloc(Cpp_Token_Stack *token_stack, Cpp_Token token){ + Cpp_Token_Merge merge = {}; + + if (token_stack->count > 0){ + Cpp_Token prev_token = token_stack->tokens[token_stack->count - 1]; + merge = cpp_attempt_token_merge(prev_token, token); + if (merge.did_merge){ + token_stack->tokens[token_stack->count - 1] = merge.new_token; + } + } + + if (!merge.did_merge){ + if (token_stack->count >= token_stack->max_count){ + return 0; + } + + token_stack->tokens[token_stack->count++] = token; + } + + return 1; +} + +FCPP_LINK Cpp_Read_Result +cpp_lex_step(Cpp_File file, Cpp_Lex_Data *lex_data){ + Cpp_Lex_Data lex = *lex_data; + Cpp_Read_Result result = {}; + bool has_result = 1; + + fcpp_u16 state_flags = cpp_token_set_pp_state(0, lex.pp_state); + + char current = file.data[lex.pos]; + if (char_is_whitespace(current)){ + result = cpp_read_whitespace(file, lex.pos); + lex.pos = result.pos; + if (result.newline && lex.pp_state != CPP_LEX_PP_DEFAULT){ + lex.pp_state = CPP_LEX_PP_DEFAULT; + } + has_result = 0; + } + + else{ + if (lex.pp_state == CPP_LEX_PP_DEFAULT){ + // TODO(allen): Not first hard of the line? Then it's junk. + if (current == '#'){ + result = cpp_read_preprocessor(file, lex.pos); + lex.pos = result.pos; + switch (result.token.type){ + case CPP_PP_INCLUDE: + case CPP_PP_IMPORT: + case CPP_PP_USING: + lex.pp_state = CPP_LEX_PP_INCLUDE; + break; + case CPP_PP_DEFINE: + lex.pp_state = CPP_LEX_PP_MACRO_IDENTIFIER; + break; + case CPP_PP_UNDEF: + case CPP_PP_IFDEF: + case CPP_PP_IFNDEF: + lex.pp_state = CPP_LEX_PP_IDENTIFIER; + break; + case CPP_PP_IF: + case CPP_PP_ELIF: + lex.pp_state = CPP_LEX_PP_BODY_IF; + break; + case CPP_PP_PRAGMA: + lex.pp_state = CPP_LEX_PP_BODY; + break; + case CPP_PP_LINE: + lex.pp_state = CPP_LEX_PP_NUMBER; + break; + case CPP_PP_ERROR: + lex.pp_state = CPP_LEX_PP_ERROR; + break; + + case CPP_PP_UNKNOWN: + case CPP_PP_ELSE: + case CPP_PP_ENDIF: + lex.pp_state = CPP_LEX_PP_JUNK; + break; + } + } + else{ + result = cpp_read_pp_default_mode(file, lex.pos); + lex.pos = result.pos; + } + } + + else{ + if (current == '\\'){ + fcpp_i32 seek = lex.pos; + ++seek; + while (seek < file.size && file.data[seek] == '\r'){ + ++seek; + } + if ((seek < file.size && file.data[seek] == '\n') || seek >= file.size){ + lex.pos = seek + 1; + has_result = 0; + } + else{ + lex.pp_state = CPP_LEX_PP_JUNK; + result.token.type = CPP_TOKEN_JUNK; + result.token.start = lex.pos; + result.token.size = 1; + result.token.flags |= CPP_TFLAG_PP_BODY; + lex.pos = seek; + } + } + + else{ + switch (lex.pp_state){ + case CPP_LEX_PP_IDENTIFIER: + if (!char_is_alpha_numeric(current)){ + has_result = 0; + lex.pp_state = CPP_LEX_PP_JUNK; + } + else{ + result = cpp_read_alpha_numeric(file, lex.pos); + result.token.flags |= CPP_TFLAG_PP_BODY; + lex.pos = result.pos; + lex.pp_state = CPP_LEX_PP_JUNK; + } + break; + + case CPP_LEX_PP_MACRO_IDENTIFIER: + if (!char_is_alpha_numeric(current)){ + has_result = 0; + lex.pp_state = CPP_LEX_PP_JUNK; + } + else{ + result = cpp_read_alpha_numeric(file, lex.pos); + result.token.flags |= CPP_TFLAG_PP_BODY; + lex.pos = result.pos; + lex.pp_state = CPP_LEX_PP_BODY; + } + break; + + case CPP_LEX_PP_INCLUDE: + if (current != '"' && current != '<'){ + has_result = 0; + lex.pp_state = CPP_LEX_PP_JUNK; + } + else{ + result = cpp_read_pp_include_file(file, lex.pos); + lex.pos = result.pos; + lex.pp_state = CPP_LEX_PP_JUNK; + } + break; + + case CPP_LEX_PP_BODY: + if (current == '#'){ + result = cpp_read_pp_operator(file, lex.pos); + } + else{ + result = cpp_read_pp_default_mode(file, lex.pos); + } + lex.pos = result.pos; + result.token.flags |= CPP_TFLAG_PP_BODY; + break; + + case CPP_LEX_PP_BODY_IF: + if (current == '#'){ + result = cpp_read_pp_operator(file, lex.pos); + } + else{ + result = cpp_read_pp_default_mode(file, lex.pos, 1); + } + lex.pos = result.pos; + result.token.flags |= CPP_TFLAG_PP_BODY; + break; + + case CPP_LEX_PP_NUMBER: + if (!char_is_numeric(current)){ + has_result = 0; + lex.pp_state = CPP_LEX_PP_JUNK; + } + else{ + result = cpp_read_number(file, lex.pos); + lex.pos = result.pos; + result.token.flags |= CPP_TFLAG_PP_BODY; + lex.pp_state = CPP_LEX_PP_INCLUDE; + } + break; + + case CPP_LEX_PP_ERROR: + result = cpp_read_junk_line(file, lex.pos); + lex.pos = result.pos; + result.token.type = CPP_TOKEN_ERROR_MESSAGE; + result.token.flags |= CPP_TFLAG_PP_BODY; + break; + + default: + { + bool took_comment = 0; + if (current == '/' && lex.pos + 1 < file.size){ + if (file.data[lex.pos + 1] == '/'){ + result = cpp_read_line_comment(file, lex.pos); + lex.pp_state = CPP_LEX_PP_DEFAULT; + lex.pos = result.pos; + took_comment = 1; + }else if (file.data[lex.pos + 1] == '*'){ + result = cpp_read_block_comment(file, lex.pos); + lex.pos = result.pos; + took_comment = 1; + } + } + + if (!took_comment){ + result = cpp_read_junk_line(file, lex.pos); + lex.pos = result.pos; + result.token.flags |= CPP_TFLAG_PP_BODY; + } + }break; + + } + } + } + } + + result.token.state_flags = state_flags; + result.has_result = has_result; + + *lex_data = lex; + return result; +} + +FCPP_LINK int +cpp_lex_file_token_count(Cpp_File file){ + int count = 0; + Cpp_Lex_Data lex = {}; + Cpp_Token token = {}; + while (lex.pos < file.size){ + Cpp_Read_Result step_result = cpp_lex_step(file, &lex); + + if (step_result.has_result){ + if (count > 0){ + Cpp_Token_Merge merge = cpp_attempt_token_merge(token, step_result.token); + if (merge.did_merge){ + token = merge.new_token; + } + else{ + token = step_result.token; + ++count; + } + } + else{ + token = step_result.token; + ++count; + } + } + } + return count; +} + +FCPP_LINK Cpp_Lex_Data +cpp_lex_file_nonalloc(Cpp_File file, Cpp_Token_Stack *token_stack_out, Cpp_Lex_Data data){ + while (data.pos < file.size){ + Cpp_Lex_Data prev_lex = data; + Cpp_Read_Result step_result = cpp_lex_step(file, &data); + + if (step_result.has_result){ + if (!cpp_push_token_nonalloc(token_stack_out, step_result.token)){ + data = prev_lex; + return data; + } + } + } + + data.complete = 1; + return data; +} + +FCPP_LINK Cpp_Get_Token_Result +cpp_get_token(Cpp_Token_Stack *token_stack, int pos){ + int first, last; + first = 0; + last = token_stack->count; + + Cpp_Get_Token_Result result = {}; + if (token_stack->count > 0){ + for (;;){ + result.token_index = (first + last)/2; + + int this_start = token_stack->tokens[result.token_index].start; + int next_start; + if (result.token_index + 1 < token_stack->count){ + next_start = token_stack->tokens[result.token_index+1].start; + } + else{ + next_start = this_start + token_stack->tokens[result.token_index].size; + } + if (this_start <= pos && pos < next_start){ + break; + } + else if (pos < this_start){ + last = result.token_index; + } + else{ + first = result.token_index + 1; + } + if (first == last){ + result.token_index = first; + break; + } + } + + if (result.token_index == token_stack->count){ + --result.token_index; + result.in_whitespace = 1; + } + else{ + Cpp_Token *token = token_stack->tokens + result.token_index; + if (token->start + token->size <= pos){ + result.in_whitespace = 1; + } + } + } + else{ + result.token_index = -1; + result.in_whitespace = 1; + } + + return result; +} + +FCPP_LINK int +cpp_get_end_token(Cpp_Token_Stack *stack, int end){ + Cpp_Get_Token_Result result = cpp_get_token(stack, end); + if (result.token_index < 0) result.token_index = 0; + else if (end > stack->tokens[result.token_index].start) ++result.token_index; + return result.token_index; +} + +FCPP_LINK void +cpp_shift_token_starts(Cpp_Token_Stack *stack, int from_token_i, int amount){ + int count = stack->count; + Cpp_Token *token = stack->tokens + from_token_i; + for (int i = from_token_i; i < count; ++i, ++token){ + token->start += amount; + } +} + +FCPP_LINK Cpp_Relex_State +cpp_relex_nonalloc_start(Cpp_File file, Cpp_Token_Stack *stack, + int start, int end, int amount, int tolerance){ + Cpp_Relex_State state; + state.file = file; + state.stack = stack; + state.start = start; + state.end = end; + state.amount = amount; + state.tolerance = tolerance; + + Cpp_Get_Token_Result result = cpp_get_token(stack, start); + if (result.token_index <= 0){ + state.start_token_i = 0; + } + else{ + state.start_token_i = result.token_index-1; + } + + result = cpp_get_token(stack, end); + if (result.token_index < 0) result.token_index = 0; + else if (end > stack->tokens[result.token_index].start) ++result.token_index; + state.end_token_i = result.token_index; + + state.relex_start = stack->tokens[state.start_token_i].start; + if (start < state.relex_start) state.relex_start = start; + + state.space_request = state.end_token_i - state.start_token_i + tolerance + 1; + + return state; +} + +inline Cpp_Token +cpp__get_token(Cpp_Token_Stack *stack, Cpp_Token *tokens, int size, int index){ + Cpp_Token result; + if (index < stack->count){ + result = tokens[index]; + } + else{ + result.start = size; + result.size = 0; + result.type = CPP_TOKEN_EOF; + result.flags = 0; + result.state_flags = 0; + } + return result; +} + +FCPP_LINK bool +cpp_relex_nonalloc_main(Cpp_Relex_State *state, Cpp_Token_Stack *relex_stack, int *relex_end){ + Cpp_Token_Stack *stack = state->stack; + Cpp_Token *tokens = stack->tokens; + + cpp_shift_token_starts(stack, state->end_token_i, state->amount); + + Cpp_Lex_Data lex = {}; + lex.pp_state = cpp_token_get_pp_state(tokens[state->start_token_i].state_flags); + lex.pos = state->relex_start; + + int relex_end_i = state->end_token_i; + Cpp_Token match_token = cpp__get_token(stack, tokens, state->file.size, relex_end_i); + Cpp_Token end_token = match_token; + bool went_too_far = 0; + + for (;;){ + Cpp_Read_Result read = cpp_lex_step(state->file, &lex); + if (read.has_result){ + if (read.token.start == end_token.start && + read.token.size == end_token.size && + read.token.flags == end_token.flags && + read.token.state_flags == end_token.state_flags){ + break; + } + cpp_push_token_nonalloc(relex_stack, read.token); + + while (lex.pos > end_token.start && relex_end_i < stack->count){ + ++relex_end_i; + end_token = cpp__get_token(stack, tokens, state->file.size, relex_end_i); + } + if (relex_stack->count == relex_stack->max_count){ + went_too_far = 1; + break; + } + } + if (lex.pos >= state->file.size) break; + } + + if (!went_too_far){ + if (relex_stack->count > 0){ + if (state->start_token_i > 0){ + Cpp_Token_Merge merge = + cpp_attempt_token_merge(tokens[state->start_token_i - 1], + relex_stack->tokens[0]); + if (merge.did_merge){ + --state->start_token_i; + relex_stack->tokens[0] = merge.new_token; + } + } + + if (relex_end_i < state->stack->count){ + Cpp_Token_Merge merge = + cpp_attempt_token_merge(relex_stack->tokens[relex_stack->count-1], + tokens[relex_end_i]); + if (merge.did_merge){ + ++relex_end_i; + relex_stack->tokens[relex_stack->count-1] = merge.new_token; + } + } + } + + *relex_end = relex_end_i; + } + else{ + cpp_shift_token_starts(stack, state->end_token_i, -state->amount); + } + + return went_too_far; +} + +#ifndef FCPP_FORBID_MALLOC +FCPP_LINK Cpp_Token_Stack +cpp_make_token_stack(int starting_max){ + Cpp_Token_Stack token_stack; + token_stack.count = 0; + token_stack.max_count = starting_max; + token_stack.tokens = (Cpp_Token*)FCPP_GET_MEMORY(sizeof(Cpp_Token)*starting_max); + return token_stack; +} + +FCPP_LINK void +cpp_free_token_stack(Cpp_Token_Stack token_stack){ + FCPP_FREE_MEMORY(token_stack.tokens); +} + +FCPP_LINK void +cpp_resize_token_stack(Cpp_Token_Stack *token_stack, int new_max){ + Cpp_Token *new_tokens = (Cpp_Token*)FCPP_GET_MEMORY(sizeof(Cpp_Token)*new_max); + + if (new_tokens){ + FCPP_MEM_COPY(new_tokens, token_stack->tokens, sizeof(Cpp_Token)*token_stack->count); + FCPP_FREE_MEMORY(token_stack->tokens); + token_stack->tokens = new_tokens; + token_stack->max_count = new_max; + } +} + +FCPP_LINK void +cpp_push_token(Cpp_Token_Stack *token_stack, Cpp_Token token){ + if (!cpp_push_token_nonalloc(token_stack, token)){ + int new_max = 2*token_stack->max_count + 1; + cpp_resize_token_stack(token_stack, new_max); + bool result = cpp_push_token_nonalloc(token_stack, token); + _Assert(result); + } +} + +FCPP_LINK void +cpp_lex_file(Cpp_File file, Cpp_Token_Stack *token_stack_out){ + Cpp_Lex_Data lex = {}; + while (lex.pos < file.size){ + Cpp_Read_Result step_result = cpp_lex_step(file, &lex); + if (step_result.has_result){ + cpp_push_token(token_stack_out, step_result.token); + } + } +} + +FCPP_LINK bool +cpp_relex_file_limited(Cpp_File file, Cpp_Token_Stack *stack, + int start, int end, int amount, int tolerance){ +#if 0 + int start_token_i, end_token_i; + Cpp_Get_Token_Result get_result = cpp_get_token(token_stack, start_i); + start_token_i = get_result.token_index; + get_result = cpp_get_token(token_stack, end_i); + end_token_i = get_result.token_index; + if (end_token_i == -1){ + end_token_i = 0; + } + else if (end > token_stack->tokens[end_token_i].start){ + ++end_token_i; + } + cpp_shift_token_starts(token_stack, end_token_i, amount); + + int relex_start_i = start_token_i - 1; + if (relex_start_i < 0){ + relex_start_i = 0; + } + + int end_guess_i = end_token_i + 1; + if (end_guess_i > token_stack->count){ + --end_guess_i; + } +#endif + + int relex_start_i; + int end_token_i, end_guess_i; + { + Cpp_Get_Token_Result result = cpp_get_token(stack, start); + if (result.token_index <= 0){ + relex_start_i = 0; + } + else{ + relex_start_i = result.token_index-1; + } + + result = cpp_get_token(stack, end); + if (result.token_index < 0) result.token_index = 0; + else if (end > stack->tokens[result.token_index].start) ++result.token_index; + end_token_i = result.token_index; + end_guess_i = result.token_index+1; + } + + int relex_start = stack->tokens[relex_start_i].start; + if (start < relex_start) relex_start = start; + + cpp_shift_token_starts(stack, end_token_i, amount); + Cpp_Token_Stack relex_stack = cpp_make_token_stack((end_guess_i - relex_start_i + 1) * 3 / 2); + Cpp_Lex_Data lex = {}; + lex.pp_state = cpp_token_get_pp_state(stack->tokens[relex_start_i].state_flags); + lex.pos = relex_start; + bool went_too_far = 0; + + while (1){ + Cpp_Read_Result result = cpp_lex_step(file, &lex); + if (result.has_result){ + if (end_guess_i < stack->count && + result.token.start == stack->tokens[end_guess_i].start && + result.token.size == stack->tokens[end_guess_i].size && + result.token.flags == stack->tokens[end_guess_i].flags && + result.token.state_flags == stack->tokens[end_guess_i].state_flags){ + break; + } + else{ + cpp_push_token(&relex_stack, result.token); + while (lex.pos > stack->tokens[end_guess_i].start && + end_guess_i < stack->count){ + ++end_guess_i; + } + } + } + + if (lex.pos >= file.size){ + break; + } + + if (tolerance >= 0 && relex_stack.count + relex_start_i >= end_guess_i + tolerance){ + went_too_far = 1; + break; + } + } + + if (!went_too_far){ + int relex_end_i = end_guess_i; + + if (relex_stack.count > 0){ + if (relex_start_i > 0){ + Cpp_Token_Merge merge = cpp_attempt_token_merge(stack->tokens[relex_start_i - 1], + relex_stack.tokens[0]); + if (merge.did_merge){ + --relex_start_i; + relex_stack.tokens[0] = merge.new_token; + } + } + + if (relex_end_i < stack->count){ + Cpp_Token_Merge merge = cpp_attempt_token_merge(relex_stack.tokens[relex_stack.count - 1], + stack->tokens[relex_end_i]); + if (merge.did_merge){ + ++relex_end_i; + relex_stack.tokens[relex_stack.count - 1] = merge.new_token; + } + } + } + + int token_delete_amount = relex_end_i - relex_start_i; + int token_shift_amount = relex_stack.count - token_delete_amount; + + if (token_shift_amount != 0){ + int new_token_count = stack->count + token_shift_amount; + if (new_token_count > stack->max_count){ + int new_max = 2*stack->max_count + 1; + while (new_token_count > new_max){ + new_max = 2*new_max + 1; + } + cpp_resize_token_stack(stack, new_max); + } + + if (relex_end_i < stack->count){ + FCPP_MEM_MOVE(stack->tokens + relex_end_i + token_shift_amount, + stack->tokens + relex_end_i, sizeof(Cpp_Token)*(stack->count - relex_end_i)); + } + + stack->count += token_shift_amount; + } + + FCPP_MEM_COPY(stack->tokens + relex_start_i, relex_stack.tokens, sizeof(Cpp_Token)*relex_stack.count); + cpp_free_token_stack(relex_stack); + } + + else{ + cpp_shift_token_starts(stack, end_token_i, -amount); + cpp_free_token_stack(relex_stack); + } + + return went_too_far; +} +#endif + +#undef _Assert +#undef _TentativeAssert + +#undef FCPP_LEXER_IMPLEMENTATION +#endif // #ifdef FCPP_LEXER_IMPLEMENTATION + +// BOTTOM diff --git a/test_data/lots_of_files/4cpp_preprocessor.cpp b/test_data/lots_of_files/4cpp_preprocessor.cpp new file mode 100644 index 0000000..ce82589 --- /dev/null +++ b/test_data/lots_of_files/4cpp_preprocessor.cpp @@ -0,0 +1,19 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 30.11.2015 + * + * CPP preprocessor + * + */ + +// TOP + +#define byte unsigned char + + + +#undef byte + +// BOTTOM + diff --git a/test_data/lots_of_files/4cpp_types.h b/test_data/lots_of_files/4cpp_types.h new file mode 100644 index 0000000..0499d55 --- /dev/null +++ b/test_data/lots_of_files/4cpp_types.h @@ -0,0 +1,39 @@ +/* "4cpp" Open C++ Parser v0.1: Types + no warranty implied; use at your own risk + +NOTES ON USE: + This file is used to declare 4cpp fixed width integer and float types. + It is not meant to be used directly. +*/ + +// TODO(allen): +// - create non stdint.h version in case someone wants to exclude that header + +#include "4coder_config.h" + +#ifndef FCPP_TYPES +#define FCPP_TYPES + +#include <stdint.h> + +typedef uint8_t fcpp_u8; +typedef uint64_t fcpp_u64; +typedef uint32_t fcpp_u32; +typedef uint16_t fcpp_u16; + +typedef int8_t fcpp_i8; +typedef int64_t fcpp_i64; +typedef int32_t fcpp_i32; +typedef int16_t fcpp_i16; + +typedef fcpp_i32 fcpp_bool32; +typedef fcpp_i8 fcpp_bool8; + +typedef float fcpp_real32; +typedef double fcpp_real64; + +#define FCPP_GLOBAL static + +#define FCPP_COUNT(a) (sizeof(a)/sizeof(*(a))) + +#endif diff --git a/test_data/lots_of_files/4ed.cpp b/test_data/lots_of_files/4ed.cpp new file mode 100644 index 0000000..9b42d16 --- /dev/null +++ b/test_data/lots_of_files/4ed.cpp @@ -0,0 +1,4517 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +// TOP + +// App Structs + +enum App_State{ + APP_STATE_EDIT, + APP_STATE_RESIZING, + // never below this + APP_STATE_COUNT +}; + +struct App_State_Resizing{ + Panel_Divider *divider; + i32 min, max; +}; + +struct CLI_Process{ + CLI_Handles cli; + Editing_File *out_file; +}; + +struct CLI_List{ + CLI_Process *procs; + i32 count, max; +}; + +#define SysAppCreateView 0x1 +#define SysAppCreateNewBuffer 0x2 + +struct Sys_App_Binding{ + i32 sys_id; + i32 app_id; + + u32 success; + u32 fail; + Panel *panel; +}; + +struct Complete_State{ + Search_Set set; + Search_Iter iter; + Table hits; + String_Space str; + i32 word_start, word_end; + b32 initialized; +}; + +struct Command_Data{ + Models *models; + struct App_Vars *vars; + System_Functions *system; + Exchange *exchange; + Live_Views *live_set; + + Panel *panel; + View *view; + + i32 screen_width, screen_height; + Key_Event_Data key; + + Partition part; +}; + +struct App_Vars{ + Models models; + + CLI_List cli_processes; + + Sys_App_Binding *sys_app_bindings; + i32 sys_app_count, sys_app_max; + + Live_Views live_set; + + App_State state; + App_State_Resizing resizing; + Complete_State complete_state; + + Command_Data command_data; +}; + +internal i32 +app_get_or_add_map_index(Models *models, i32 mapid){ + i32 result; + i32 user_map_count = models->user_map_count; + i32 *map_id_table = models->map_id_table; + for (result = 0; result < user_map_count; ++result){ + if (map_id_table[result] == mapid) break; + if (map_id_table[result] == 0){ + map_id_table[result] = mapid; + break; + } + } + return result; +} + +internal i32 +app_get_map_index(Models *models, i32 mapid){ + i32 result; + i32 user_map_count = models->user_map_count; + i32 *map_id_table = models->map_id_table; + for (result = 0; result < user_map_count; ++result){ + if (map_id_table[result] == mapid) break; + if (map_id_table[result] == 0){ + result = user_map_count; + break; + } + } + return result; +} + +internal Command_Map* +app_get_map(Models *models, i32 mapid){ + Command_Map *map = 0; + if (mapid < mapid_global) map = models->user_maps + mapid; + else if (mapid == mapid_global) map = &models->map_top; + else if (mapid == mapid_file) map = &models->map_file; + return map; +} + +// Commands + +globalvar Application_Links app_links; + +#define USE_MODELS(n) Models *n = command->models +#define USE_VARS(n) App_Vars *n = command->vars +#define USE_PANEL(n) Panel *n = command->panel +#define USE_VIEW(n) View *n = command->view +#define USE_FILE(n,v) Editing_File *n = (v)->file +#define USE_EXCHANGE(n) Exchange *n = command->exchange + +#define REQ_OPEN_VIEW(n) View *n = command->panel->view; if (view_lock_level(n) > LockLevel_Open) return +#define REQ_READABLE_VIEW(n) View *n = command->panel->view; if (view_lock_level(n) > LockLevel_NoWrite) return + +#define REQ_FILE(n,v) Editing_File *n = (v)->file; if (!n) return +#define REQ_FILE_HISTORY(n,v) Editing_File *n = (v)->file; if (!n || !n->state.undo.undo.edits) return + +#define COMMAND_DECL(n) internal void command_##n(System_Functions *system, Command_Data *command, Command_Binding binding) + +struct Command_Parameter{ + i32 type; + union{ + struct{ + Dynamic param; + Dynamic value; + } param; + struct{ + i32 len; + char *str; + } inline_string; + }; +}; + +inline Command_Parameter* +param_next(Command_Parameter *param, Command_Parameter *end){ + Command_Parameter *result = param; + if (result->type == 0){ + ++result; + } + while (result->type != 0 && result < end){ + i32 len = result->inline_string.len; + len += sizeof(*result) - 1; + len -= (len % sizeof(*result)); + result = (Command_Parameter*)((char*)result + len + sizeof(*result)); + } + return result; +} + +inline Command_Parameter* +param_stack_first(Partition *part, Command_Parameter *end){ + Command_Parameter *result = (Command_Parameter*)part->base; + if (result->type != 0) result = param_next(result, end); + return result; +} + +inline Command_Parameter* +param_stack_end(Partition *part){ + return (Command_Parameter*)((char*)part->base + part->pos); +} + +internal View* +panel_make_empty(System_Functions *system, Exchange *exchange, App_Vars *vars, Panel *panel){ + Models *models = &vars->models; + View_And_ID new_view; + + Assert(panel->view == 0); + new_view = live_set_alloc_view(&vars->live_set, panel, models); + view_set_file(new_view.view, 0, models, 0, 0, 0); + new_view.view->map = app_get_map(models, mapid_global); + + return(new_view.view); +} + +COMMAND_DECL(null){ + AllowLocal(command); +} + +COMMAND_DECL(write_character){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + char character; + i32 pos, next_cursor_pos; + + character = command->key.character; + if (character != 0){ + pos = view->cursor.pos; + next_cursor_pos = view->cursor.pos + 1; + view_replace_range(system, models, view, pos, pos, &character, 1, next_cursor_pos); + view_cursor_move(view, next_cursor_pos); + } +} + +COMMAND_DECL(seek_whitespace_right){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_whitespace_right(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +internal i32 +seek_token_left(Cpp_Token_Stack *tokens, i32 pos){ + Cpp_Get_Token_Result get = cpp_get_token(tokens, pos); + if (get.token_index == -1){ + get.token_index = 0; + } + + Cpp_Token *token = tokens->tokens + get.token_index; + if (token->start == pos && get.token_index > 0){ + --token; + } + + return token->start; +} + +internal i32 +seek_token_right(Cpp_Token_Stack *tokens, i32 pos){ + Cpp_Get_Token_Result get = cpp_get_token(tokens, pos); + if (get.in_whitespace){ + ++get.token_index; + } + if (get.token_index >= tokens->count){ + get.token_index = tokens->count-1; + } + + Cpp_Token *token = tokens->tokens + get.token_index; + return token->start + token->size; +} + +COMMAND_DECL(seek_left){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + u32 flags = BoundryWhitespace; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_flags: + flags = dynamic_to_int(¶m->param.value); + break; + } + } + + i32 pos[4] = {0}; + + if (flags & (1)){ + pos[0] = buffer_seek_whitespace_left(&file->state.buffer, view->cursor.pos); + } + + if (flags & (1 << 1)){ + if (file->state.tokens_complete){ + pos[1] = seek_token_left(&file->state.token_stack, view->cursor.pos); + } + else{ + pos[1] = buffer_seek_whitespace_left(&file->state.buffer, view->cursor.pos); + } + } + + if (flags & (1 << 2)){ + pos[2] = buffer_seek_alphanumeric_left(&file->state.buffer, view->cursor.pos); + if (flags & (1 << 3)){ + pos[3] = buffer_seek_range_camel_left(&file->state.buffer, view->cursor.pos, pos[2]); + } + } + else{ + if (flags & (1 << 3)){ + pos[3] = buffer_seek_alphanumeric_or_camel_left(&file->state.buffer, view->cursor.pos); + } + } + + i32 new_pos = 0; + for (i32 i = 0; i < ArrayCount(pos); ++i){ + if (pos[i] > new_pos) new_pos = pos[i]; + } + + view_cursor_move(view, new_pos); +} + +COMMAND_DECL(seek_right){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + u32 flags = BoundryWhitespace; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_flags: + flags = dynamic_to_int(¶m->param.value); + break; + } + } + + i32 size = buffer_size(&file->state.buffer); + i32 pos[4]; + for (i32 i = 0; i < ArrayCount(pos); ++i) pos[i] = size; + + if (flags & (1)){ + pos[0] = buffer_seek_whitespace_right(&file->state.buffer, view->cursor.pos); + } + + if (flags & (1 << 1)){ + if (file->state.tokens_complete){ + pos[1] = seek_token_right(&file->state.token_stack, view->cursor.pos); + } + else{ + pos[1] = buffer_seek_whitespace_right(&file->state.buffer, view->cursor.pos); + } + } + + if (flags & (1 << 2)){ + pos[2] = buffer_seek_alphanumeric_right(&file->state.buffer, view->cursor.pos); + if (flags & (1 << 3)){ + pos[3] = buffer_seek_range_camel_right(&file->state.buffer, view->cursor.pos, pos[2]); + } + } + else{ + if (flags & (1 << 3)){ + pos[3] = buffer_seek_alphanumeric_or_camel_right(&file->state.buffer, view->cursor.pos); + } + } + + i32 new_pos = size; + for (i32 i = 0; i < ArrayCount(pos); ++i){ + if (pos[i] < new_pos) new_pos = pos[i]; + } + + view_cursor_move(view, new_pos); +} + +COMMAND_DECL(seek_whitespace_left){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_whitespace_left(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(seek_whitespace_up){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_whitespace_up(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(seek_whitespace_down){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_whitespace_down(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(seek_token_left){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + if (file->state.tokens_complete){ + i32 pos = seek_token_left(&file->state.token_stack, view->cursor.pos); + view_cursor_move(view, pos); + } +} + +COMMAND_DECL(seek_token_right){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + if (file->state.tokens_complete){ + i32 pos = seek_token_right(&file->state.token_stack, view->cursor.pos); + view_cursor_move(view, pos); + } +} + +COMMAND_DECL(seek_white_or_token_right){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 token_pos, white_pos; + if (file->state.tokens_complete){ + token_pos = seek_token_right(&file->state.token_stack, view->cursor.pos); + } + else{ + token_pos = buffer_size(&file->state.buffer); + } + white_pos = buffer_seek_whitespace_right(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, Min(token_pos, white_pos)); +} + +COMMAND_DECL(seek_white_or_token_left){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 token_pos, white_pos; + if (file->state.tokens_complete){ + token_pos = seek_token_left(&file->state.token_stack, view->cursor.pos); + } + else{ + token_pos = 0; + } + white_pos = buffer_seek_whitespace_left(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, Max(token_pos, white_pos)); +} + +COMMAND_DECL(seek_alphanumeric_right){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_alphanumeric_right(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(seek_alphanumeric_left){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_alphanumeric_left(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(seek_alphanumeric_or_camel_right){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_alphanumeric_or_camel_right(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(seek_alphanumeric_or_camel_left){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = buffer_seek_alphanumeric_or_camel_left(&file->state.buffer, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(word_complete){ + ProfileMomentFunction(); + USE_MODELS(models); + USE_VARS(vars); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + Partition *part = &models->mem.part; + General_Memory *general = &models->mem.general; + Working_Set *working_set = &models->working_set; + Complete_State *complete_state = &vars->complete_state; + Search_Range *ranges; + Search_Match match; + + 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 *spare; + i32 size; + + i32 match_size; + b32 do_init = 0; + + buffer = &file->state.buffer; + size_of_buffer = buffer_size(buffer); + + if (view->mode.rewrite != 2){ + do_init = 1; + } + view->next_mode.rewrite = 2; + + if (complete_state->initialized == 0){ + 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; + } + } + } + double_break:; + + size = word_end - word_start; + + if (size == 0){ + complete_state->initialized = 0; + return; + } + + complete_state->initialized = 1; + 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; + + { + File_Node *node, *used_nodes; + Editing_File *file_ptr; + i32 buffer_count, j; + + buffer_count = working_set->file_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; + + used_nodes = &working_set->used_sentinel; + j = 2; + for (dll_items(node, used_nodes)){ + file_ptr = (Editing_File*)node; + if (file_ptr != file){ + 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; + } + else{ + word_start = complete_state->word_start; + word_end = complete_state->word_end; + size = complete_state->iter.word.size; + } + + if (size > 0){ + 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, models, view, 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, models, view, 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; + } + } + } +} + +COMMAND_DECL(set_mark){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + view->mark = (i32)view->cursor.pos; +} + +COMMAND_DECL(copy){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + // TODO(allen): deduplicate + int r_start = 0, r_end = 0; + int start_set = 0, end_set = 0; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_range_start: + start_set = 1; + r_start = dynamic_to_int(¶m->param.value); + break; + + case par_range_end: + end_set = 1; + r_end = dynamic_to_int(¶m->param.value); + break; + } + } + + Range range = make_range(view->cursor.pos, view->mark); + if (start_set) range.start = r_start; + if (end_set) range.end = r_end; + if (range.start < range.end){ + clipboard_copy(system, &models->mem.general, &models->working_set, range, file); + } +} + +COMMAND_DECL(cut){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + // TODO(allen): deduplicate + int r_start = 0, r_end = 0; + int start_set = 0, end_set = 0; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_range_start: + start_set = 1; + r_start = dynamic_to_int(¶m->param.value); + break; + + case par_range_end: + end_set = 1; + r_end = dynamic_to_int(¶m->param.value); + break; + } + } + + Range range = make_range(view->cursor.pos, view->mark); + if (start_set) range.start = r_start; + if (end_set) range.end = r_end; + if (range.start < range.end){ + i32 next_cursor_pos = range.start; + + clipboard_copy(system, &models->mem.general, &models->working_set, range, file); + view_replace_range(system, models, view, range.start, range.end, 0, 0, next_cursor_pos); + + view->mark = range.start; + view_cursor_move(view, next_cursor_pos); + } +} + +COMMAND_DECL(paste){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + View_Iter iter; + String *src; + i32 pos_left, next_cursor_pos; + + if (models->working_set.clipboard_size > 0){ + view->next_mode.rewrite = 1; + + src = working_set_clipboard_head(&models->working_set); + pos_left = view->cursor.pos; + + next_cursor_pos = pos_left+src->size; + view_replace_range(system, models, view, pos_left, pos_left, src->str, src->size, next_cursor_pos); + + view_cursor_move(view, next_cursor_pos); + view->mark = pos_left; + + for (iter = file_view_iter_init(&models->layout, file, 0); + file_view_iter_good(iter); + iter = file_view_iter_next(iter)){ + view_post_paste_effect(iter.view, 20, pos_left, src->size, models->style.main.paste_color); + } + } +} + +COMMAND_DECL(paste_next){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + View_Iter iter; + Range range; + String *src; + i32 next_cursor_pos; + + if (models->working_set.clipboard_size > 0 && view->mode.rewrite == 1){ + view->next_mode.rewrite = 1; + + range = make_range(view->mark, view->cursor.pos); + src = working_set_clipboard_roll_down(&models->working_set); + next_cursor_pos = range.start+src->size; + view_replace_range(system, + models, view, range.start, range.end, + src->str, src->size, next_cursor_pos); + + view_cursor_move(view, next_cursor_pos); + view->mark = range.start; + + for (iter = file_view_iter_init(&models->layout, file, 0); + file_view_iter_good(iter); + iter = file_view_iter_next(iter)){ + view_post_paste_effect(iter.view, 20, range.start, src->size, models->style.main.paste_color); + } + } + else{ + command_paste(system, command, binding); + } +} + +COMMAND_DECL(delete_range){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + Range range; + i32 next_cursor_pos; + + range = make_range(view->cursor.pos, view->mark); + if (range.start < range.end){ + next_cursor_pos = range.start; + view_replace_range(system, models, view, range.start, range.end, 0, 0, next_cursor_pos); + view_cursor_move(view, next_cursor_pos); + view->mark = range.start; + } +} + +COMMAND_DECL(timeline_scrub){ + ProfileMomentFunction(); + REQ_OPEN_VIEW(view); + REQ_FILE_HISTORY(file, view); + + view_set_widget(view, FWIDG_TIMELINES); + view->widget.timeline.undo_line = 1; + view->widget.timeline.history_line = 1; +} + +COMMAND_DECL(undo){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE_HISTORY(file, view); + + view_undo(system, models, view); +} + +COMMAND_DECL(redo){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE_HISTORY(file, view); + + view_redo(system, models, view); +} + +COMMAND_DECL(history_backward){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE_HISTORY(file, view); + + view_history_step(system, models, view, hist_backward); +} + +COMMAND_DECL(history_forward){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE_HISTORY(file, view); + + view_history_step(system, models, view, hist_backward); +} + +COMMAND_DECL(interactive_new){ + ProfileMomentFunction(); + USE_MODELS(models); + USE_VIEW(view); + + view_show_interactive(system, view, &models->map_ui, + IAct_New, IInt_Sys_File_List, make_lit_string("New: ")); +} + +internal Sys_App_Binding* +app_push_file_binding(App_Vars *vars, int sys_id, int app_id){ + Sys_App_Binding *binding; + Assert(vars->sys_app_count < vars->sys_app_max); + binding = vars->sys_app_bindings + vars->sys_app_count++; + binding->sys_id = sys_id; + binding->app_id = app_id; + return(binding); +} + +struct App_Open_File_Result{ + Editing_File *file; + i32 sys_id; + i32 file_index; + b32 is_new; +}; + +COMMAND_DECL(interactive_open){ + ProfileMomentFunction(); + USE_MODELS(models); + USE_PANEL(panel); + USE_VIEW(view); + + Delay *delay = &models->delay1; + + char *filename = 0; + int filename_len = 0; + int do_in_background = 0; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + if (param->param.param.type == dynamic_type_int){ + if (param->param.param.int_value == par_name && + param->param.value.type == dynamic_type_string){ + filename = param->param.value.str_value; + filename_len = param->param.value.str_len; + } + else if (param->param.param.int_value == par_do_in_background){ + do_in_background = dynamic_to_int(¶m->param.value); + } + } + } + + if (filename){ + String string = make_string(filename, filename_len); + if (do_in_background){ + delayed_open_background(delay, string); + } + else{ + // TODO(allen): Change the behavior of all delayed_open/background + // calls so that they still allocate the buffer right away. This way + // it's still possible to get at the buffer if so wished in the API. + // The switch for this view doesn't need to happen until the file is ready. + delayed_open(delay, string, panel); + } + } + else{ + view_show_interactive(system, view, &models->map_ui, + IAct_Open, IInt_Sys_File_List, make_lit_string("Open: ")); + } +} + +internal void +view_file_in_panel(Command_Data *cmd, Panel *panel, Editing_File *file){ + System_Functions *system = cmd->system; + Models *models = cmd->models; + + Partition old_part; + Temp_Memory temp; + View *old_view; + Partition *part; + + old_view = cmd->view; + old_part = cmd->part; + + cmd->view = panel->view; + part = &models->mem.part; + temp = begin_temp_memory(part); + cmd->part = partition_sub_part(part, Kbytes(16)); + + view_set_file(panel->view, file, models, system, + models->hooks[hook_open_file], &app_links); + + cmd->part = old_part; + end_temp_memory(temp); + cmd->view = old_view; + + panel->view->map = app_get_map(models, file->settings.base_map_id); +} + +// TODO(allen): Improvements to reopen +// - Preserve existing token stack +// - Keep current version open and do some sort of diff to keep +// the cursor position correct +COMMAND_DECL(reopen){ + ProfileMomentFunction(); + USE_VARS(vars); + USE_MODELS(models); + USE_VIEW(view); + REQ_FILE(file, view); + USE_EXCHANGE(exchange); + + if (match(file->name.source_path, file->name.live_name)) return; + + i32 file_id = exchange_request_file(exchange, expand_str(file->name.source_path)); + i32 index = 0; + if (file_id){ + file_set_to_loading(file); + index = (i32)(file - models->working_set.files); + app_push_file_binding(vars, file_id, index); + + view_set_file(view, file, models, system, + models->hooks[hook_open_file], &app_links); + } + else{ + // TODO(allen): feedback message + } +} + +COMMAND_DECL(save){ + ProfileMomentFunction(); + USE_MODELS(models); + USE_VIEW(view); + USE_FILE(file, view); + + Delay *delay = &models->delay1; + + char *filename = 0; + int filename_len = 0; + int buffer_id = -1; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int v = dynamic_to_int(¶m->param.param); + if (v == par_name && param->param.value.type == dynamic_type_string){ + filename = param->param.value.str_value; + filename_len = param->param.value.str_len; + } + else if (v == par_buffer_id && param->param.value.type == dynamic_type_int){ + buffer_id = dynamic_to_int(¶m->param.value); + } + } + + String name = {}; + if (filename){ + name = make_string(filename, filename_len); + } + else if (file){ + name = file->name.source_path; + } + + if (name.size != 0){ + if (buffer_id == -1){ + if (file){ + delayed_save(delay, name, file); + } + } + else{ + file = models->working_set.files + buffer_id; + + if (!file->state.is_dummy && file_is_ready(file)){ + delayed_save(delay, name, file); + } + else{ + delayed_save(delay, name); + } + } + } +} + +COMMAND_DECL(interactive_save_as){ + ProfileMomentFunction(); + USE_VIEW(view); + USE_MODELS(models); + + view_show_interactive(system, view, &models->map_ui, + IAct_Save_As, IInt_Sys_File_List, make_lit_string("Save As: ")); +} + +COMMAND_DECL(change_active_panel){ + ProfileMomentFunction(); + USE_MODELS(models); + USE_PANEL(panel); + + panel = panel->next; + if (panel == &models->layout.used_sentinel){ + panel = panel->next; + } + models->layout.active_panel = (i32)(panel - models->layout.panels); +} + +COMMAND_DECL(interactive_switch_buffer){ + ProfileMomentFunction(); + USE_VIEW(view); + USE_MODELS(models); + + view_show_interactive(system, view, &models->map_ui, + IAct_Switch, IInt_Live_File_List, make_lit_string("Switch Buffer: ")); +} + +COMMAND_DECL(interactive_kill_buffer){ + ProfileMomentFunction(); + USE_VIEW(view); + USE_MODELS(models); + + view_show_interactive(system, view, &models->map_ui, + IAct_Kill, IInt_Live_File_List, make_lit_string("Kill Buffer: ")); +} + +COMMAND_DECL(kill_buffer){ + ProfileMomentFunction(); + USE_MODELS(models); + USE_VIEW(view); + USE_FILE(file, view); + + Delay *delay = &models->delay1; + int buffer_id = 0; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int v = dynamic_to_int(¶m->param.param); + if (v == par_buffer_id && param->param.value.type == dynamic_type_int){ + buffer_id = dynamic_to_int(¶m->param.value); + } + } + + if (buffer_id != 0){ + file = working_set_get_file(&models->working_set, buffer_id, 1).file; + if (file){ + delayed_kill(delay, file); + } + } + else if (file){ + delayed_try_kill(delay, file, view->panel); + } +} + +COMMAND_DECL(toggle_line_wrap){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + Relative_Scrolling scrolling = view_get_relative_scrolling(view); + if (view->unwrapped_lines){ + view->unwrapped_lines = 0; + file->settings.unwrapped_lines = 0; + view->target_x = 0; + view->cursor = + view_compute_cursor_from_pos(view, view->cursor.pos); + view->preferred_x = view->cursor.wrapped_x; + } + else{ + view->unwrapped_lines = 1; + file->settings.unwrapped_lines = 1; + view->cursor = + view_compute_cursor_from_pos(view, view->cursor.pos); + view->preferred_x = view->cursor.unwrapped_x; + } + view_set_relative_scrolling(view, scrolling); +} + +COMMAND_DECL(toggle_show_whitespace){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + view->show_whitespace = !view->show_whitespace; +} + +COMMAND_DECL(toggle_tokens){ +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + if (file->settings.tokens_exist){ + file_kill_tokens(system, &models->mem.general, file); + } + else{ + file_first_lex_parallel(system, &models->mem.general, file); + } +#endif +} + +internal void +case_change_range(System_Functions *system, + Mem_Options *mem, View *view, Editing_File *file, + u8 a, u8 z, u8 char_delta){ +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + Range range = make_range(view->cursor.pos, view->mark); + if (range.start < range.end){ + Edit_Step step = {}; + step.type = ED_NORMAL; + step.edit.start = range.start; + step.edit.end = range.end; + step.edit.len = range.end - range.start; + + if (file->state.still_lexing) + system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); + + file_update_history_before_edit(mem, file, step, 0, hist_normal); + + u8 *data = (u8*)file->state.buffer.data; + for (i32 i = range.start; i < range.end; ++i){ + if (data[i] >= a && data[i] <= z){ + data[i] += char_delta; + } + } + + if (file->state.token_stack.tokens) + file_relex_parallel(system, mem, file, range.start, range.end, 0); + } +#endif +} + +COMMAND_DECL(to_uppercase){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + case_change_range(system, &models->mem, view, file, 'a', 'z', (u8)('A' - 'a')); +} + +COMMAND_DECL(to_lowercase){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + case_change_range(system, &models->mem, view, file, 'A', 'Z', (u8)('a' - 'A')); +} + +COMMAND_DECL(clean_all_lines){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + view_clean_whitespace(system, models, view); +} + +COMMAND_DECL(eol_dosify){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + file->settings.dos_write_mode = 1; + file->state.last_4ed_edit_time = system->time(); +} + +COMMAND_DECL(eol_nixify){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + file->settings.dos_write_mode = 0; + file->state.last_4ed_edit_time = system->time(); +} + +COMMAND_DECL(auto_tab_range){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + int r_start = 0, r_end = 0; + int start_set = 0, end_set = 0; + int clear_blank_lines = 1; + + // TODO(allen): deduplicate + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_range_start: + start_set = 1; + r_start = dynamic_to_int(¶m->param.value); + break; + + case par_range_end: + end_set = 1; + r_end = dynamic_to_int(¶m->param.value); + break; + + case par_clear_blank_lines: + clear_blank_lines = dynamic_to_bool(¶m->param.value); + break; + } + } + + if (file->state.token_stack.tokens && file->state.tokens_complete && !file->state.still_lexing){ + Range range = make_range(view->cursor.pos, view->mark); + if (start_set) range.start = r_start; + if (end_set) range.end = r_end; + view_auto_tab_tokens(system, models, view, range.start, range.end, clear_blank_lines); + } +} + +COMMAND_DECL(open_panel_vsplit){ + ProfileMomentFunction(); + USE_VARS(vars); + USE_MODELS(models); + USE_PANEL(panel); + USE_EXCHANGE(exchange); + + if (models->layout.panel_count < models->layout.panel_max_count){ + Split_Result split = layout_split_panel(&models->layout, panel, 1); + + Panel *panel1 = panel; + Panel *panel2 = split.panel; + + panel2->screen_region = panel1->screen_region; + + panel2->full.x0 = split.divider->pos; + panel2->full.x1 = panel1->full.x1; + panel1->full.x1 = split.divider->pos; + + panel_fix_internal_area(panel1); + panel_fix_internal_area(panel2); + panel2->prev_inner = panel2->inner; + + models->layout.active_panel = (i32)(panel2 - models->layout.panels); + panel_make_empty(system, exchange, vars, panel2); + } +} + +COMMAND_DECL(open_panel_hsplit){ + ProfileMomentFunction(); + USE_VARS(vars); + USE_MODELS(models); + USE_PANEL(panel); + USE_EXCHANGE(exchange); + + if (models->layout.panel_count < models->layout.panel_max_count){ + Split_Result split = layout_split_panel(&models->layout, panel, 0); + + Panel *panel1 = panel; + Panel *panel2 = split.panel; + + panel2->screen_region = panel1->screen_region; + + panel2->full.y0 = split.divider->pos; + panel2->full.y1 = panel1->full.y1; + panel1->full.y1 = split.divider->pos; + + panel_fix_internal_area(panel1); + panel_fix_internal_area(panel2); + panel2->prev_inner = panel2->inner; + + models->layout.active_panel = (i32)(panel2 - models->layout.panels); + panel_make_empty(system, exchange, vars, panel2); + } +} + +COMMAND_DECL(close_panel){ + ProfileMomentFunction(); + USE_MODELS(models); + USE_PANEL(panel); + USE_VIEW(view); + USE_EXCHANGE(exchange); + + Panel *panel_ptr, *used_panels; + Divider_And_ID div, parent_div, child_div; + i32 child; + i32 parent; + i32 which_child; + i32 active; + + if (models->layout.panel_count > 1){ + live_set_free_view(system, exchange, command->live_set, view); + panel->view = 0; + + div = layout_get_divider(&models->layout, panel->parent); + + // This divider cannot have two child dividers. + Assert(div.divider->child1 == -1 || div.divider->child2 == -1); + + // Get the child who needs to fill in this node's spot + child = div.divider->child1; + if (child == -1) child = div.divider->child2; + + parent = div.divider->parent; + which_child = div.divider->which_child; + + // Fill the child in the slot this node use to hold + if (parent == -1){ + Assert(models->layout.root == div.id); + models->layout.root = child; + } + else{ + parent_div = layout_get_divider(&models->layout, parent); + if (which_child == -1){ + parent_div.divider->child1 = child; + } + else{ + parent_div.divider->child2 = child; + } + } + + // If there was a child divider, give it information about it's new parent. + if (child != -1){ + child_div = layout_get_divider(&models->layout, child); + child_div.divider->parent = parent; + child_div.divider->which_child = div.divider->which_child; + } + + // What is the new active panel? + active = -1; + if (child == -1){ + used_panels = &models->layout.used_sentinel; + for (dll_items(panel_ptr, used_panels)){ + if (panel_ptr != panel && panel_ptr->parent == div.id){ + panel_ptr->parent = parent; + panel_ptr->which_child = which_child; + active = (i32)(panel_ptr - models->layout.panels); + break; + } + } + } + else{ + panel_ptr = panel->next; + if (panel_ptr == &models->layout.used_sentinel) panel_ptr = panel_ptr->next; + Assert(panel_ptr != panel); + active = (i32)(panel_ptr - models->layout.panels); + } + + Assert(active != -1 && panel != models->layout.panels + active); + models->layout.active_panel = active; + + layout_free_divider(&models->layout, div.divider); + layout_free_panel(&models->layout, panel); + layout_fix_all_panels(&models->layout); + } +} + +COMMAND_DECL(move_left){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = view->cursor.pos; + if (pos > 0) --pos; + view_cursor_move(view, pos); +} + +COMMAND_DECL(move_right){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 size = buffer_size(&file->state.buffer); + i32 pos = view->cursor.pos; + if (pos < size) ++pos; + view_cursor_move(view, pos); +} + +COMMAND_DECL(delete){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + i32 size = buffer_size(&file->state.buffer); + i32 cursor_pos = view->cursor.pos; + if (0 < size && cursor_pos < size){ + i32 start, end; + start = cursor_pos; + end = cursor_pos+1; + + Assert(end - start > 0); + + i32 next_cursor_pos = start; + view_replace_range(system, models, view, start, end, 0, 0, next_cursor_pos); + view_cursor_move(view, next_cursor_pos); + } +} + +COMMAND_DECL(backspace){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_OPEN_VIEW(view); + REQ_FILE(file, view); + + i32 size = buffer_size(&file->state.buffer); + i32 cursor_pos = view->cursor.pos; + if (cursor_pos > 0 && cursor_pos <= size){ + i32 start, end; + end = cursor_pos; + start = cursor_pos-1; + + i32 shift = (end - start); + Assert(shift > 0); + + i32 next_cursor_pos = view->cursor.pos - shift; + view_replace_range(system, models, view, start, end, 0, 0, next_cursor_pos); + view_cursor_move(view, next_cursor_pos); + } +} + +COMMAND_DECL(move_up){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + f32 font_height = (f32)get_font_info(models->font_set, models->global_font.font_id)->height; + f32 cy = view_get_cursor_y(view)-font_height; + f32 px = view->preferred_x; + if (cy >= 0){ + view->cursor = view_compute_cursor_from_xy(view, px, cy); + file->state.cursor_pos = view->cursor.pos; + } +} + +COMMAND_DECL(move_down){ + ProfileMomentFunction(); + USE_MODELS(models); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + f32 font_height = (f32)get_font_info(models->font_set, models->global_font.font_id)->height; + f32 cy = view_get_cursor_y(view)+font_height; + f32 px = view->preferred_x; + view->cursor = view_compute_cursor_from_xy(view, px, cy); + file->state.cursor_pos = view->cursor.pos; +} + +COMMAND_DECL(seek_end_of_line){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = view_find_end_of_line(view, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(seek_beginning_of_line){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + + i32 pos = view_find_beginning_of_line(view, view->cursor.pos); + view_cursor_move(view, pos); +} + +COMMAND_DECL(page_down){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + + f32 height = view_compute_height(view); + f32 max_target_y = view_compute_max_target_y(view); + + view->target_y += height; + if (view->target_y > max_target_y) view->target_y = max_target_y; + + view->cursor = view_compute_cursor_from_xy( + view, 0, view->target_y + (height - view->font_height)*.5f); +} + +COMMAND_DECL(page_up){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + + f32 height = view_compute_height(view); + + view->target_y -= height; + if (view->target_y < 0) view->target_y = 0; + + view->cursor = view_compute_cursor_from_xy( + view, 0, view->target_y + (height - view->font_height)*.5f); +} + +COMMAND_DECL(open_color_tweaker){ + ProfileMomentFunction(); + USE_VIEW(view); + USE_MODELS(models); + + view_show_theme(view, &models->map_ui); +} + +COMMAND_DECL(open_config){ + ProfileMomentFunction(); + USE_VIEW(view); + USE_MODELS(models); + + view_show_config(view, &models->map_ui); +} + +COMMAND_DECL(open_menu){ + ProfileMomentFunction(); + USE_VIEW(view); + USE_MODELS(models); + + view_show_menu(view, &models->map_ui); +} + +COMMAND_DECL(close_minor_view){ + ProfileMomentFunction(); + USE_VIEW(view); + USE_MODELS(models); + + Command_Map *map = &models->map_top; + if (view->file){ + map = app_get_map(models, view->file->settings.base_map_id); + } + view_show_file(view, map); +} + +COMMAND_DECL(cursor_mark_swap){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + + i32 pos = view->cursor.pos; + view_cursor_move(view, view->mark); + view->mark = pos; +} + +COMMAND_DECL(user_callback){ + ProfileMomentFunction(); + if (binding.custom) binding.custom(&app_links); +} + +COMMAND_DECL(set_settings){ + ProfileMomentFunction(); + REQ_READABLE_VIEW(view); + REQ_FILE(file, view); + USE_MODELS(models); + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_lex_as_cpp_file: + { +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + int v = dynamic_to_bool(¶m->param.value); + if (file->settings.tokens_exist){ + if (!v) file_kill_tokens(system, &models->mem.general, file); + } + else{ + if (v) file_first_lex_parallel(system, &models->mem.general, file); + } +#endif + }break; + + case par_wrap_lines: + { + int v = dynamic_to_bool(¶m->param.value); + if (view->unwrapped_lines){ + if (v){ + view->unwrapped_lines = 0; + file->settings.unwrapped_lines = 0; + + if (!file->state.is_loading){ + Relative_Scrolling scrolling = view_get_relative_scrolling(view); + view->target_x = 0; + view->cursor = + view_compute_cursor_from_pos(view, view->cursor.pos); + view_set_relative_scrolling(view, scrolling); + } + } + } + else{ + if (!v){ + view->unwrapped_lines = 1; + file->settings.unwrapped_lines = 1; + + if (!file->state.is_loading){ + Relative_Scrolling scrolling = view_get_relative_scrolling(view); + view->cursor = + view_compute_cursor_from_pos(view, view->cursor.pos); + view_set_relative_scrolling(view, scrolling); + } + } + } + }break; + + case par_key_mapid: + { + int v = dynamic_to_int(¶m->param.value); + if (v == mapid_global) file->settings.base_map_id = mapid_global; + else if (v == mapid_file) file->settings.base_map_id = mapid_file; + else if (v < mapid_global){ + int index = app_get_map_index(models, v); + if (index < models->user_map_count) file->settings.base_map_id = v; + else file->settings.base_map_id = mapid_file; + } + }break; + } + } +} + +COMMAND_DECL(command_line){ + ProfileMomentFunction(); + USE_VARS(vars); + USE_MODELS(models); + USE_PANEL(panel); + USE_VIEW(view); + + Partition *part = &models->mem.part; + + char *buffer_name = 0; + char *path = 0; + char *script = 0; + + i32 buffer_id = 0; + i32 buffer_name_len = 0; + i32 path_len = 0; + i32 script_len = 0; + u32 flags = CLI_OverlapWithConflict; + b32 do_in_background = 0; + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_name: + { + char *new_buffer_name = dynamic_to_string(¶m->param.value, &buffer_name_len); + if (new_buffer_name){ + buffer_name = new_buffer_name; + } + }break; + + case par_buffer_id: + { + buffer_id = dynamic_to_int(¶m->param.value); + }break; + + case par_do_in_background: + { + do_in_background = 1; + }break; + + case par_cli_path: + { + char *new_cli_path = dynamic_to_string(¶m->param.value, &path_len); + if (new_cli_path){ + path = new_cli_path; + } + }break; + + case par_cli_command: + { + char *new_command = dynamic_to_string(¶m->param.value, &script_len); + if (new_command){ + script = new_command; + } + }break; + + case par_flags: + { + flags = (u32)dynamic_to_int(¶m->param.value); + }break; + } + } + + { + Working_Set *working_set = &models->working_set; + CLI_Process *procs = vars->cli_processes.procs, *proc = 0; + Get_File_Result file = {}; + b32 bind_to_new_view = !do_in_background; + + if (vars->cli_processes.count < vars->cli_processes.max){ + if (buffer_id){ + file = working_set_get_file(working_set, buffer_id, 1); + } + else if (buffer_name){ + file.file = working_set_contains(working_set, make_string(buffer_name, buffer_name_len)); + file.index = (i32)(file.file - working_set->files); + if (file.file == 0){ + file = working_set_get_available_file(working_set); + if (file.file == 0){ + // TODO(allen): feedback message - no available file + return; + } + } + } + + if (file.file){ + i32 proc_count = vars->cli_processes.count; + View_Iter iter; + i32 i; + + file_create_read_only(system, models, file.file, buffer_name); + file.file->settings.unimportant = 1; + table_add(&working_set->table, file.file->name.source_path, file.index); + + for (i = 0; i < proc_count; ++i){ + if (procs[i].out_file == file.file){ + if (flags & CLI_OverlapWithConflict) + procs[i].out_file = 0; + else + file.file = 0; + break; + } + } + + if (file.file){ + if (!(flags & CLI_AlwaysBindToView)){ + iter = file_view_iter_init(&models->layout, file.file, 0); + if (file_view_iter_good(iter)){ + bind_to_new_view = 0; + } + } + } + else{ + // TODO(allen): feedback message - file conflict + return; + } + } + + if (!path){ + path = models->hot_directory.string.str; + terminate_with_null(&models->hot_directory.string); + } + + { + Temp_Memory temp; + Range range; + Editing_File *view_file; + i32 size; + + temp = begin_temp_memory(part); + if (!script){ + view_file = view->file; + if (view_file){ + range = make_range(view->cursor.pos, view->mark); + size = range.end - range.start; + script = push_array(part, char, size + 1); + buffer_stringify(&view_file->state.buffer, range.start, range.end, script); + script[size] = 0; + } + else{ + script = " echo no script specified"; + } + } + + if (bind_to_new_view){ + view_file_in_panel(command, panel, file.file); + } + + proc = procs + vars->cli_processes.count++; + proc->out_file = file.file; + + if (!system->cli_call(path, script, &proc->cli)){ + --vars->cli_processes.count; + } + end_temp_memory(temp); + } + } + else{ + // TODO(allen): feedback message - no available process slot + return; + } + } +} + +internal void +update_command_data(App_Vars *vars, Command_Data *cmd){ + cmd->panel = cmd->models->layout.panels + cmd->models->layout.active_panel; + cmd->view = cmd->panel->view; +} + +globalvar Command_Function command_table[cmdid_count]; + +internal void +fill_buffer_summary(Buffer_Summary *buffer, Editing_File *file, Working_Set *working_set){ + *buffer = {}; + if (!file->state.is_dummy){ + buffer->exists = 1; + buffer->ready = file_is_ready(file); + + buffer->is_lexed = file->settings.tokens_exist; + buffer->buffer_id = (int)(file - working_set->files); + buffer->size = file->state.buffer.size; + buffer->buffer_cursor_pos = file->state.cursor_pos; + + buffer->file_name_len = file->name.source_path.size; + buffer->buffer_name_len = file->name.live_name.size; + buffer->file_name = file->name.source_path.str; + buffer->buffer_name = file->name.live_name.str; + + buffer->map_id = file->settings.base_map_id; + } +} + +internal void +fill_view_summary(View_Summary *view, View *vptr, Live_Views *live_set, Working_Set *working_set){ + i32 lock_level; + int buffer_id; + *view = {}; + + if (vptr->in_use){ + view->exists = 1; + view->view_id = (int)(vptr - live_set->views) + 1; + view->line_height = vptr->font_height; + view->unwrapped_lines = vptr->unwrapped_lines; + + if (vptr->file){ + lock_level = view_lock_level(vptr); + buffer_id = (int)(vptr->file - working_set->files); + + if (lock_level <= 0){ + view->buffer_id = buffer_id; + } + else{ + view->buffer_id = 0; + } + + if (lock_level <= 1){ + view->locked_buffer_id = buffer_id; + } + else{ + view->locked_buffer_id = 0; + } + + if (lock_level <= 2){ + view->hidden_buffer_id = buffer_id; + } + else{ + view->hidden_buffer_id = 0; + } + + view->mark = view_compute_cursor_from_pos(vptr, vptr->mark); + view->cursor = vptr->cursor; + view->preferred_x = vptr->preferred_x; + } + } +} + +extern "C"{ + EXECUTE_COMMAND_SIG(external_exec_command_keep_stack){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Command_Function function = command_table[command_id]; + Command_Binding binding = {}; + binding.function = function; + if (function) function(cmd->system, cmd, binding); + + update_command_data(cmd->vars, cmd); + } + + PUSH_PARAMETER_SIG(external_push_parameter){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Partition *part = &cmd->part; + Command_Parameter *cmd_param = push_struct(part, Command_Parameter); + cmd_param->type = 0; + cmd_param->param.param = param; + cmd_param->param.value = value; + } + + PUSH_MEMORY_SIG(external_push_memory){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Partition *part = &cmd->part; + Command_Parameter *base = push_struct(part, Command_Parameter); + char *result = push_array(part, char, len); + int full_len = len + sizeof(Command_Parameter) - 1; + full_len -= (full_len % sizeof(Command_Parameter)); + part->pos += full_len - len; + base->type = 1; + base->inline_string.str = result; + base->inline_string.len = len; + return(result); + } + + CLEAR_PARAMETERS_SIG(external_clear_parameters){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + cmd->part.pos = 0; + } + + DIRECTORY_GET_HOT_SIG(external_directory_get_hot){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Hot_Directory *hot = &cmd->models->hot_directory; + i32 copy_max = capacity - 1; + hot_directory_clean_end(hot); + if (copy_max > hot->string.size) + copy_max = hot->string.size; + memcpy(out, hot->string.str, copy_max); + out[copy_max] = 0; + return(hot->string.size); + } + + GET_FILE_LIST_SIG(external_get_file_list){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + System_Functions *system = cmd->system; + File_List result = {}; + system->set_file_list(&result, make_string(dir, len)); + return(result); + } + + FREE_FILE_LIST_SIG(external_free_file_list){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + System_Functions *system = cmd->system; + system->set_file_list(&list, make_string(0, 0)); + } + + GET_BUFFER_FIRST_SIG(external_get_buffer_first){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Working_Set *working_set = &cmd->models->working_set; + Buffer_Summary result = {}; + if (working_set->file_count > 0){ + fill_buffer_summary(&result, (Editing_File*)working_set->used_sentinel.next, working_set); + } + return(result); + } + + GET_BUFFER_NEXT_SIG(external_get_buffer_next){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Working_Set *working_set = &cmd->models->working_set; + Editing_File *file; + + file = working_set_get_file(working_set, buffer->buffer_id, 1).file; + if (file){ + file = (Editing_File*)file->node.next; + fill_buffer_summary(buffer, file, working_set); + } + else{ + *buffer = {}; + } + } + + GET_BUFFER_SIG(external_get_buffer){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Working_Set *working_set = &cmd->models->working_set; + Buffer_Summary buffer = {}; + Get_File_Result file; + + file = working_set_get_file(working_set, index, 1); + if (file.file){ + fill_buffer_summary(&buffer, file.file, working_set); + } + + return(buffer); + } + + GET_ACTIVE_BUFFER_SIG(external_get_active_buffer){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Buffer_Summary buffer = {}; + View *view = cmd->view; + Editing_File *file; + + if (view_lock_level(view) <= LockLevel_Open){ + file = view->file; + if (file){ + fill_buffer_summary(&buffer, file, &cmd->models->working_set); + } + } + + return(buffer); + } + + GET_PARAMETER_BUFFER_SIG(external_get_parameter_buffer){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Models *models = cmd->models; + Buffer_Summary buffer = {}; + + if (param_index >= 0 && param_index < models->buffer_param_count){ + buffer = external_get_buffer(app, models->buffer_param_indices[param_index]); + } + + return(buffer); + } + + GET_BUFFER_BY_NAME(external_get_buffer_by_name){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Buffer_Summary buffer = {}; + Editing_File *file; + Working_Set *working_set; + i32 index; + + working_set = &cmd->models->working_set; + if (table_find(&working_set->table, make_string(filename, len), &index)){ + file = working_set_get_file(working_set, index, 1).file; + if (file){ + fill_buffer_summary(&buffer, file, working_set); + } + } + + return(buffer); + } + + BUFFER_SEEK_DELIMITER_SIG(external_buffer_seek_delimiter){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Editing_File *file; + Working_Set *working_set; + int result = 0; + int size; + + if (buffer->exists){ + working_set = &cmd->models->working_set; + file = working_set_get_file(working_set, buffer->buffer_id, 1).file; + if (file && file_is_ready(file)){ + size = buffer_size(&file->state.buffer); + result = 1; + + if (start < 0 && !seek_forward) *out = start; + else if (start >= size && seek_forward) *out = start; + else{ + if (seek_forward){ + *out = buffer_seek_delimiter(&file->state.buffer, start, delim); + } + else{ + *out = buffer_reverse_seek_delimiter(&file->state.buffer, start, delim); + } + } + + fill_buffer_summary(buffer, file, working_set); + } + } + + return(result); + } + + BUFFER_SEEK_STRING_SIG(external_buffer_seek_string){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Models *models; + Editing_File *file; + Working_Set *working_set; + Partition *part; + Temp_Memory temp; + char *spare; + int result = 0; + int size; + + if (buffer->exists){ + models = cmd->models; + working_set = &models->working_set; + file = working_set_get_file(working_set, buffer->buffer_id, 1).file; + if (file && file_is_ready(file)){ + size = buffer_size(&file->state.buffer); + + if (start < 0 && !seek_forward) *out = start; + else if (start >= size && seek_forward) *out = start; + else{ + part = &models->mem.part; + temp = begin_temp_memory(part); + spare = push_array(part, char, len); + result = 1; + if (seek_forward){ + *out = buffer_find_string(&file->state.buffer, start, size, str, len, spare); + } + else{ + *out = buffer_rfind_string(&file->state.buffer, start, str, len, spare); + } + end_temp_memory(temp); + } + fill_buffer_summary(buffer, file, working_set); + } + } + + return(result); + } + + REFRESH_BUFFER_SIG(external_refresh_buffer){ + int result; + *buffer = external_get_buffer(app, buffer->buffer_id); + result = buffer->exists; + return(result); + } + + BUFFER_READ_RANGE_SIG(external_buffer_read_range){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Editing_File *file; + Working_Set *working_set; + int result = 0; + int size; + + if (buffer->exists){ + working_set = &cmd->models->working_set; + file = working_set_get_file(working_set, buffer->buffer_id, 1).file; + if (file && file_is_ready(file)){ + size = buffer_size(&file->state.buffer); + if (0 <= start && start <= end && end <= size){ + result = 1; + buffer_stringify(&file->state.buffer, start, end, out); + } + fill_buffer_summary(buffer, file, working_set); + } + } + + return(result); + } + + BUFFER_REPLACE_RANGE_SIG(external_buffer_replace_range){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Editing_File *file; + Working_Set *working_set; + + Models *models; + + int result = 0; + int size; + int next_cursor, pos; + + if (buffer->exists){ + models = cmd->models; + working_set = &models->working_set; + file = working_set_get_file(working_set, buffer->buffer_id, 1).file; + if (file && file_is_ready(file)){ + size = buffer_size(&file->state.buffer); + if (0 <= start && start <= end && end <= size){ + result = 1; + + pos = file->state.cursor_pos; + if (pos < start) next_cursor = pos; + else if (pos < end) next_cursor = start; + else next_cursor = pos + end - start - len; + + file_replace_range(cmd->system, models, file, start, end, str, len, next_cursor); + } + fill_buffer_summary(buffer, file, working_set); + } + } + + return(result); + } + + BUFFER_SET_POS_SIG(external_buffer_set_pos){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Editing_File *file; + Working_Set *working_set; + + int result = 0; + int size; + + if (buffer->exists){ + working_set = &cmd->models->working_set; + file = working_set_get_file(working_set, buffer->buffer_id, 1).file; + if (file && file_is_ready(file)){ + result = 1; + size = buffer_size(&file->state.buffer); + if (pos < 0) pos = 0; + if (pos > size) pos = size; + file->state.cursor_pos = pos; + fill_buffer_summary(buffer, file, working_set); + } + } + + return(result); + } + + GET_VIEW_FIRST_SIG(external_get_view_first){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Editing_Layout *layout = &cmd->models->layout; + View_Summary view = {}; + + Panel *panel = layout->used_sentinel.next; + + Assert(panel != &layout->used_sentinel); + fill_view_summary(&view, panel->view, &cmd->vars->live_set, &cmd->models->working_set); + + return(view); + } + + GET_VIEW_NEXT_SIG(external_get_view_next){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Editing_Layout *layout = &cmd->models->layout; + Live_Views *live_set = &cmd->vars->live_set; + View *vptr; + Panel *panel; + int index = view->view_id - 1; + + if (index >= 0 && index < live_set->max){ + vptr = live_set->views + index; + panel = vptr->panel; + if (panel) panel = panel->next; + if (panel && panel != &layout->used_sentinel){ + fill_view_summary(view, panel->view, &cmd->vars->live_set, &cmd->models->working_set); + } + else{ + *view = {}; + } + } + else{ + *view = {}; + } + } + + GET_VIEW_SIG(external_get_view){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + View_Summary view = {}; + Live_Views *live_set = cmd->live_set; + int max = live_set->max; + View *vptr; + + index -= 1; + if (index >= 0 && index < max){ + vptr = live_set->views + index; + fill_view_summary(&view, vptr, live_set, &cmd->models->working_set); + } + + return(view); + } + + GET_ACTIVE_VIEW_SIG(external_get_active_view){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + View_Summary view = {}; + + fill_view_summary(&view, cmd->view, &cmd->vars->live_set, &cmd->models->working_set); + + return(view); + } + + REFRESH_VIEW_SIG(external_refresh_view){ + int result; + *view = external_get_view(app, view->view_id); + result = view->exists; + return(result); + } + + VIEW_SET_CURSOR_SIG(external_view_set_cursor){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Live_Views *live_set; + View *vptr; + int result = 0; + int view_id; + + if (view->exists){ + live_set = cmd->live_set; + view_id = view->view_id - 1; + if (view_id >= 0 && view_id < live_set->max){ + vptr = live_set->views + view_id; + result = 1; + if (seek.type == buffer_seek_line_char && seek.character <= 0){ + seek.character = 1; + } + vptr->cursor = view_compute_cursor(vptr, seek); + if (set_preferred_x){ + vptr->preferred_x = view_get_cursor_x(vptr); + } + fill_view_summary(view, vptr, live_set, &cmd->models->working_set); + } + } + + return(result); + } + + VIEW_SET_MARK_SIG(external_view_set_mark){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Live_Views *live_set; + View *vptr; + Full_Cursor cursor; + int result = 0; + int view_id; + + if (view->exists){ + live_set = cmd->live_set; + view_id = view->view_id - 1; + if (view_id >= 0 && view_id < live_set->max){ + vptr = live_set->views + view_id; + result = 1; + if (seek.type != buffer_seek_pos){ + cursor = view_compute_cursor(vptr, seek); + vptr->mark = cursor.pos; + } + else{ + vptr->mark = seek.pos; + } + fill_view_summary(view, vptr, live_set, &cmd->models->working_set); + } + } + + return(result); + } + + VIEW_SET_HIGHLIGHT_SIG(external_view_set_highlight){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Live_Views *live_set; + View *vptr; + int result = 0; + int view_id; + + if (view->exists){ + live_set = cmd->live_set; + view_id = view->view_id - 1; + if (view_id >= 0 && view_id < live_set->max){ + vptr = live_set->views + view_id; + result = 1; + if (turn_on){ + view_set_temp_highlight(vptr, start, end); + } + else{ + vptr->show_temp_highlight = 0; + } + fill_view_summary(view, vptr, live_set, &cmd->models->working_set); + } + } + + return(result); + } + + VIEW_SET_BUFFER_SIG(external_view_set_buffer){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Live_Views *live_set; + View *vptr; + Get_File_Result file; + Working_Set *working_set; + Models *models; + int result = 0; + int view_id; + + if (view->exists){ + models = cmd->models; + live_set = cmd->live_set; + view_id = view->view_id - 1; + if (view_id >= 0 && view_id < live_set->max){ + vptr = live_set->views + view_id; + working_set = &models->working_set; + file = working_set_get_file(working_set, buffer_id, 1); + + if (file.file){ + result = 1; + if (file.file != vptr->file){ + view_set_file(vptr, file.file, models, + cmd->system, models->hooks[hook_open_file], &app_links); + } + } + + fill_view_summary(view, vptr, live_set, working_set); + } + } + + return(result); + } + + GET_USER_INPUT_SIG(external_get_user_input){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + System_Functions *system = cmd->system; + Coroutine *coroutine = cmd->models->command_coroutine; + User_Input result; + + Assert(coroutine); + *((u32*)coroutine->out+0) = get_type; + *((u32*)coroutine->out+1) = abort_type; + system->yield_coroutine(coroutine); + result = *(User_Input*)coroutine->in; + + return(result); + } + + START_QUERY_BAR_SIG(external_start_query_bar){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Query_Slot *slot = 0; + View *vptr; + + vptr = cmd->view; + + slot = alloc_query_slot(&vptr->query_set); + slot->query_bar = bar; + + return(slot != 0); + } + + END_QUERY_BAR_SIG(external_end_query_bar){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + View *vptr; + + vptr = cmd->view; + free_query_slot(&vptr->query_set, bar); + } + + CHANGE_THEME_SIG(external_change_theme){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Style_Library *styles = &cmd->models->styles; + String theme_name = make_string(name, len); + Style *s; + i32 i, count; + + count = styles->count; + s = styles->styles; + for (i = 0; i < count; ++i, ++s){ + if (match(s->name, theme_name)){ + style_copy(&cmd->models->style, s); + break; + } + } + } + + CHANGE_FONT_SIG(external_change_font){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Font_Set *set = cmd->models->font_set; + Style_Font *global_font = &cmd->models->global_font; + String font_name = make_string(name, len); + i16 font_id; + + if (font_set_extract(set, font_name, &font_id)){ + global_font->font_id = font_id; + global_font->font_changed = 1; + } + } + + SET_THEME_COLORS_SIG(external_set_theme_colors){ + Command_Data *cmd = (Command_Data*)app->cmd_context; + Style *style = &cmd->models->style; + Theme_Color *theme_color; + u32 *color; + i32 i; + + theme_color = colors; + for (i = 0; i < count; ++i, ++theme_color){ + color = style_index_by_tag(&style->main, theme_color->tag); + if (color) *color = theme_color->color | 0xFF000000; + } + } +} + +struct Command_In{ + Command_Data *cmd; + Command_Binding bind; +}; + +internal void +command_caller(Coroutine *coroutine){ + Command_In *cmd_in = (Command_In*)coroutine->in; + Command_Data *cmd = cmd_in->cmd; + View *view = cmd->view; + + // TODO(allen): this isn't really super awesome, could have issues if + // the file view get's change out under us. + view->next_mode = {}; + cmd_in->bind.function(cmd->system, cmd, cmd_in->bind); + view->mode = view->next_mode; +} + +internal void +app_links_init(System_Functions *system, void *data, int size){ + app_links.memory = data; + app_links.memory_size = size; + + app_links.exec_command_keep_stack = external_exec_command_keep_stack; + app_links.push_parameter = external_push_parameter; + app_links.push_memory = external_push_memory; + app_links.clear_parameters = external_clear_parameters; + + app_links.directory_get_hot = external_directory_get_hot; + app_links.file_exists = system->file_exists; + app_links.directory_cd = system->directory_cd; + app_links.get_file_list = external_get_file_list; + app_links.free_file_list = external_free_file_list; + + app_links.get_buffer_first = external_get_buffer_first; + app_links.get_buffer_next = external_get_buffer_next; + + app_links.get_buffer = external_get_buffer; + app_links.get_active_buffer = external_get_active_buffer; + app_links.get_parameter_buffer = external_get_parameter_buffer; + app_links.get_buffer_by_name = external_get_buffer_by_name; + + app_links.refresh_buffer = external_refresh_buffer; + app_links.buffer_seek_delimiter = external_buffer_seek_delimiter; + app_links.buffer_seek_string = external_buffer_seek_string; + app_links.buffer_read_range = external_buffer_read_range; + app_links.buffer_replace_range = external_buffer_replace_range; + + app_links.get_view_first = external_get_view_first; + app_links.get_view_next = external_get_view_next; + + app_links.get_view = external_get_view; + app_links.get_active_view = external_get_active_view; + + app_links.refresh_view = external_refresh_view; + app_links.view_set_cursor = external_view_set_cursor; + app_links.view_set_mark = external_view_set_mark; + app_links.view_set_highlight = external_view_set_highlight; + app_links.view_set_buffer = external_view_set_buffer; + + app_links.get_user_input = external_get_user_input; + + app_links.start_query_bar = external_start_query_bar; + app_links.end_query_bar = external_end_query_bar; + + app_links.change_theme = external_change_theme; + app_links.change_font = external_change_font; + app_links.set_theme_colors = external_set_theme_colors; +} + +internal void +setup_ui_commands(Command_Map *commands, Partition *part, Command_Map *parent){ + map_init(commands, part, 32, parent); + + commands->vanilla_keyboard_default.function = command_null; + + // TODO(allen): This is hacky, when the new UI stuff happens, let's fix it, and by that + // I mean actually fix it, don't just say you fixed it with something stupid again. + u8 mdfr; + u8 mdfr_array[] = {MDFR_NONE, MDFR_SHIFT, MDFR_CTRL, MDFR_SHIFT | MDFR_CTRL}; + for (i32 i = 0; i < 4; ++i){ + mdfr = mdfr_array[i]; + map_add(commands, key_left, mdfr, command_null); + map_add(commands, key_right, mdfr, command_null); + map_add(commands, key_up, mdfr, command_null); + map_add(commands, key_down, mdfr, command_null); + map_add(commands, key_back, mdfr, command_null); + map_add(commands, key_esc, mdfr, command_close_minor_view); + } +} + +internal void +setup_file_commands(Command_Map *commands, Partition *part, Command_Map *parent){ + map_init(commands, part, 10, parent); +} + +internal void +setup_top_commands(Command_Map *commands, Partition *part, Command_Map *parent){ + map_init(commands, part, 10, parent); +} + +internal void +setup_command_table(){ +#define SET(n) command_table[cmdid_##n] = command_##n + + SET(null); + SET(write_character); + SET(seek_left); + SET(seek_right); + SET(seek_whitespace_up); + SET(seek_whitespace_down); + SET(word_complete); + SET(set_mark); + SET(copy); + SET(cut); + SET(paste); + SET(paste_next); + SET(delete_range); + SET(timeline_scrub); + SET(undo); + SET(redo); + SET(history_backward); + SET(history_forward); + SET(interactive_new); + SET(interactive_open); + SET(reopen); + SET(save); + SET(interactive_save_as); + SET(change_active_panel); + SET(interactive_switch_buffer); + SET(interactive_kill_buffer); + SET(kill_buffer); + SET(toggle_line_wrap); + SET(to_uppercase); + SET(to_lowercase); + SET(toggle_show_whitespace); + SET(clean_all_lines); + SET(eol_dosify); + SET(eol_nixify); + SET(auto_tab_range); + SET(open_panel_vsplit); + SET(open_panel_hsplit); + SET(close_panel); + SET(move_left); + SET(move_right); + SET(delete); + SET(backspace); + SET(move_up); + SET(move_down); + SET(seek_end_of_line); + SET(seek_beginning_of_line); + SET(page_up); + SET(page_down); + SET(open_color_tweaker); + SET(cursor_mark_swap); + SET(open_menu); + SET(set_settings); + SET(command_line); + +#undef SET +} + +// App Functions + +internal void +app_hardcode_styles(Models *models){ + Interactive_Style file_info_style; + Style *styles, *style; + styles = models->styles.styles; + style = styles; + + i16 fonts = 1; + models->global_font.font_id = fonts + 0; + models->global_font.font_changed = 0; + + ///////////////// + style_set_name(style, make_lit_string("4coder")); + + style->main.back_color = 0xFF0C0C0C; + style->main.margin_color = 0xFF181818; + style->main.margin_hover_color = 0xFF252525; + style->main.margin_active_color = 0xFF323232; + style->main.cursor_color = 0xFF00EE00; + style->main.highlight_color = 0xFFDDEE00; + style->main.mark_color = 0xFF494949; + style->main.default_color = 0xFF90B080; + style->main.at_cursor_color = style->main.back_color; + style->main.at_highlight_color = 0xFFFF44DD; + style->main.comment_color = 0xFF2090F0; + style->main.keyword_color = 0xFFD08F20; + style->main.str_constant_color = 0xFF50FF30; + style->main.char_constant_color = style->main.str_constant_color; + style->main.int_constant_color = style->main.str_constant_color; + style->main.float_constant_color = style->main.str_constant_color; + style->main.bool_constant_color = style->main.str_constant_color; + style->main.include_color = style->main.str_constant_color; + style->main.preproc_color = style->main.default_color; + style->main.special_character_color = 0xFFFF0000; + + style->main.paste_color = 0xFFDDEE00; + style->main.undo_color = 0xFF00DDEE; + + style->main.highlight_junk_color = 0xff3a0000; + style->main.highlight_white_color = 0xff003a3a; + + file_info_style.bar_color = 0xFF888888; + file_info_style.bar_active_color = 0xFF666666; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF4444AA; + file_info_style.pop2_color = 0xFFFF0000; + style->main.file_info_style = file_info_style; + ++style; + + ///////////////// + style_set_name(style, make_lit_string("Handmade Hero")); + + style->main.back_color = 0xFF161616; + style->main.margin_color = 0xFF262626; + style->main.margin_hover_color = 0xFF333333; + style->main.margin_active_color = 0xFF404040; + style->main.cursor_color = 0xFF40FF40; + style->main.at_cursor_color = style->main.back_color; + style->main.mark_color = 0xFF808080; + style->main.highlight_color = 0xFF703419; + style->main.at_highlight_color = 0xFFCDAA7D; + style->main.default_color = 0xFFCDAA7D; + style->main.comment_color = 0xFF7F7F7F; + style->main.keyword_color = 0xFFCD950C; + style->main.str_constant_color = 0xFF6B8E23; + style->main.char_constant_color = style->main.str_constant_color; + style->main.int_constant_color = style->main.str_constant_color; + style->main.float_constant_color = style->main.str_constant_color; + style->main.bool_constant_color = style->main.str_constant_color; + style->main.include_color = style->main.str_constant_color; + style->main.preproc_color = style->main.default_color; + style->main.special_character_color = 0xFFFF0000; + + style->main.paste_color = 0xFFFFBB00; + style->main.undo_color = 0xFFFF00BB; + style->main.undo_color = 0xFF80005D; + + style->main.highlight_junk_color = 0xFF3A0000; + style->main.highlight_white_color = 0xFF003A3A; + + file_info_style.bar_color = 0xFFCACACA; + file_info_style.bar_active_color = 0xFFA8A8A8; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF03CF0C; + file_info_style.pop2_color = 0xFFFF0000; + style->main.file_info_style = file_info_style; + ++style; + + ///////////////// + style_set_name(style, make_lit_string("Twilight")); + + style->main.back_color = 0xFF090D12; + style->main.margin_color = 0xFF1A2634; + style->main.margin_hover_color = 0xFF2D415B; + style->main.margin_active_color = 0xFF405D82; + style->main.cursor_color = 0xFFEEE800; + style->main.at_cursor_color = style->main.back_color; + style->main.mark_color = 0xFF8BA8CC; + style->main.highlight_color = 0xFF037A7B; + style->main.at_highlight_color = 0xFFFEB56C; + style->main.default_color = 0xFFB7C19E; + style->main.comment_color = 0xFF20ECF0; + style->main.keyword_color = 0xFFD86909; + style->main.str_constant_color = 0xFFC4EA5D; + style->main.char_constant_color = style->main.str_constant_color; + style->main.int_constant_color = style->main.str_constant_color; + style->main.float_constant_color = style->main.str_constant_color; + style->main.bool_constant_color = style->main.str_constant_color; + style->main.include_color = style->main.str_constant_color; + style->main.preproc_color = style->main.default_color; + style->main.special_character_color = 0xFFFF0000; + + style->main.paste_color = 0xFFDDEE00; + style->main.undo_color = 0xFF00DDEE; + + style->main.highlight_junk_color = 0xff3a0000; + style->main.highlight_white_color = 0xFF151F2A; + + file_info_style.bar_color = 0xFF315E68; + file_info_style.bar_active_color = 0xFF0F3C46; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF1BFF0C; + file_info_style.pop2_color = 0xFFFF200D; + style->main.file_info_style = file_info_style; + ++style; + + ///////////////// + style_set_name(style, make_lit_string("Wolverine")); + + style->main.back_color = 0xFF070711; + style->main.margin_color = 0xFF111168; + style->main.margin_hover_color = 0xFF191996; + style->main.margin_active_color = 0xFF2121C3; + style->main.cursor_color = 0xFF7082F9; + style->main.at_cursor_color = 0xFF000014; + style->main.mark_color = 0xFF4b5028; + style->main.highlight_color = 0xFFDDEE00; + style->main.at_highlight_color = 0xFF000019; + style->main.default_color = 0xFF8C9740; + style->main.comment_color = 0xFF3A8B29; + style->main.keyword_color = 0xFFD6B109; + style->main.str_constant_color = 0xFFAF5FA7; + style->main.char_constant_color = style->main.str_constant_color; + style->main.int_constant_color = style->main.str_constant_color; + style->main.float_constant_color = style->main.str_constant_color; + style->main.bool_constant_color = style->main.str_constant_color; + style->main.include_color = style->main.str_constant_color; + style->main.preproc_color = style->main.default_color; + style->main.special_character_color = 0xFFFF0000; + + style->main.paste_color = 0xFF900090; + style->main.undo_color = 0xFF606090; + + style->main.highlight_junk_color = 0xff3a0000; + style->main.highlight_white_color = 0xff003a3a; + + file_info_style.bar_color = 0xFF7082F9; + file_info_style.bar_active_color = 0xFF4E60D7; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFFFAFA15; + file_info_style.pop2_color = 0xFFD20000; + style->main.file_info_style = file_info_style; + ++style; + + ///////////////// + style_set_name(style, make_lit_string("stb")); + + style->main.back_color = 0xFFD6D6D6; + style->main.margin_color = 0xFF9E9E9E; + style->main.margin_hover_color = 0xFF7E7E7E; + style->main.margin_active_color = 0xFF5C5C5C; + style->main.cursor_color = 0xFF000000; + style->main.at_cursor_color = 0xFFD6D6D6; + style->main.mark_color = 0xFF525252; + style->main.highlight_color = 0xFF0044FF; + style->main.at_highlight_color = 0xFFD6D6D6; + style->main.default_color = 0xFF000000; + style->main.comment_color = 0xFF005800; + style->main.keyword_color = 0xFF000000; + style->main.str_constant_color = 0xFF000000; + style->main.char_constant_color = style->main.str_constant_color; + style->main.int_constant_color = style->main.str_constant_color; + style->main.float_constant_color = style->main.str_constant_color; + style->main.bool_constant_color = style->main.str_constant_color; + style->main.include_color = style->main.str_constant_color; + style->main.preproc_color = style->main.default_color; + style->main.special_character_color = 0xFF9A0000; + + style->main.paste_color = 0xFF00B8B8; + style->main.undo_color = 0xFFB800B8; + + style->main.highlight_junk_color = 0xFFFF7878; + style->main.highlight_white_color = 0xFFBCBCBC; + + file_info_style.bar_color = 0xFF606060; + file_info_style.bar_active_color = 0xFF3E3E3E; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF1111DC; + file_info_style.pop2_color = 0xFFE80505; + style->main.file_info_style = file_info_style; + ++style; + + models->styles.count = (i32)(style - styles); + models->styles.max = ArrayCount(models->styles.styles); + style_copy(&models->style, models->styles.styles); +} + +char *_4coder_get_extension(const char *filename, int len, int *extension_len){ + char *c = (char*)(filename + len - 1); + char *end = c; + while (*c != '.' && c > filename) --c; + *extension_len = (int)(end - c); + return c+1; +} + +bool _4coder_str_match(const char *a, int len_a, const char *b, int len_b){ + bool result = 0; + if (len_a == len_b){ + char *end = (char*)(a + len_a); + while (a < end && *a == *b){ + ++a; ++b; + } + if (a == end) result = 1; + } + return result; +} + +enum Command_Line_Action{ + CLAct_Nothing, + CLAct_Ignore, + CLAct_UserFile, + CLAct_CustomDLL, + CLAct_InitialFilePosition, + CLAct_WindowSize, + CLAct_WindowMaximize, + CLAct_WindowPosition, + CLAct_Count +}; + +void +init_command_line_settings(App_Settings *settings, Plat_Settings *plat_settings, + Command_Line_Parameters clparams){ + char *arg; + Command_Line_Action action = CLAct_Nothing; + i32 i,index; + b32 strict = 0; + + settings->init_files_max = ArrayCount(settings->init_files); + for (i = 1; i <= clparams.argc; ++i){ + if (i == clparams.argc) arg = ""; + else arg = clparams.argv[i]; + switch (action){ + case CLAct_Nothing: + { + if (arg[0] == '-'){ + action = CLAct_Ignore; + switch (arg[1]){ + case 'u': action = CLAct_UserFile; strict = 0; break; + case 'U': action = CLAct_UserFile; strict = 1; break; + + case 'd': action = CLAct_CustomDLL; strict = 0; break; + case 'D': action = CLAct_CustomDLL; strict = 1; break; + + case 'i': action = CLAct_InitialFilePosition; break; + + case 'w': action = CLAct_WindowSize; break; + case 'W': action = CLAct_WindowMaximize; break; + case 'p': action = CLAct_WindowPosition; break; + } + } + else if (arg[0] != 0){ + if (settings->init_files_count < settings->init_files_max){ + index = settings->init_files_count++; + settings->init_files[index] = arg; + } + } + }break; + + case CLAct_UserFile: + { + settings->user_file_is_strict = strict; + if (i < clparams.argc){ + settings->user_file = clparams.argv[i]; + } + action = CLAct_Nothing; + }break; + + case CLAct_CustomDLL: + { + plat_settings->custom_dll_is_strict = strict; + if (i < clparams.argc){ + plat_settings->custom_dll = clparams.argv[i]; + } + action = CLAct_Nothing; + }break; + + case CLAct_InitialFilePosition: + { + if (i < clparams.argc){ + settings->initial_line = str_to_int(clparams.argv[i]); + } + action = CLAct_Nothing; + }break; + + case CLAct_WindowSize: + { + if (i + 1 < clparams.argc){ + plat_settings->set_window_size = 1; + plat_settings->window_w = str_to_int(clparams.argv[i]); + plat_settings->window_h = str_to_int(clparams.argv[i+1]); + + ++i; + } + action = CLAct_Nothing; + }break; + + case CLAct_WindowMaximize: + { + --i; + plat_settings->maximize_window = 1; + action = CLAct_Nothing; + }break; + + case CLAct_WindowPosition: + { + if (i + 1 < clparams.argc){ + plat_settings->set_window_pos = 1; + plat_settings->window_x = str_to_int(clparams.argv[i]); + plat_settings->window_y = str_to_int(clparams.argv[i+1]); + + ++i; + } + action = CLAct_Nothing; + }break; + } + } +} + +internal App_Vars* +app_setup_memory(Application_Memory *memory){ + Partition _partition = partition_open(memory->vars_memory, memory->vars_memory_size); + App_Vars *vars = push_struct(&_partition, App_Vars); + Assert(vars); + *vars = {}; + vars->models.mem.part = _partition; + + general_memory_open(&vars->models.mem.general, memory->target_memory, memory->target_memory_size); + + return(vars); +} + +internal i32 +execute_special_tool(void *memory, i32 size, Command_Line_Parameters clparams){ + i32 result; + char message[] = "tool was not specified or is invalid"; + result = sizeof(message) - 1; + memcpy(memory, message, result); + if (clparams.argc > 2){ + if (match(clparams.argv[2], "version")){ + result = sizeof(VERSION) - 1; + memcpy(memory, VERSION, result); + } + } + return(result); +} + +App_Read_Command_Line_Sig(app_read_command_line){ + App_Vars *vars; + i32 out_size = 0; + + if (clparams.argc > 1 && match(clparams.argv[1], "-T")){ + out_size = execute_special_tool(memory->target_memory, memory->target_memory_size, clparams); + } + else{ + vars = app_setup_memory(memory); + if (clparams.argc > 1){ + init_command_line_settings(&vars->models.settings, plat_settings, clparams); + } + else{ + vars->models.settings = {}; + } + *files = vars->models.settings.init_files; + *file_count = &vars->models.settings.init_files_count; + } + + return(out_size); +} + +extern "C" SCROLL_RULE_SIG(fallback_scroll_rule){ + int result = 0; + + if (target_x != *scroll_x){ + *scroll_x = target_x; + result = 1; + } + if (target_y != *scroll_y){ + *scroll_y = target_y; + result = 1; + } + + return(result); +} + + +App_Init_Sig(app_init){ + App_Vars *vars; + Models *models; + Partition *partition; + Panel *panels, *panel; + Panel_Divider *dividers, *div; + i32 panel_max_count; + i32 divider_max_count; + + app_links_init(system, memory->user_memory, memory->user_memory_size); + + vars = (App_Vars*)memory->vars_memory; + models = &vars->models; + + models->config_api = api; + app_links.cmd_context = &vars->command_data; + + partition = &models->mem.part; + target->partition = partition; + + { + i32 i; + + panel_max_count = models->layout.panel_max_count = 16; + divider_max_count = panel_max_count - 1; + models->layout.panel_count = 0; + + panels = push_array(partition, Panel, panel_max_count); + models->layout.panels = panels; + + dll_init_sentinel(&models->layout.free_sentinel); + dll_init_sentinel(&models->layout.used_sentinel); + + panel = panels; + for (i = 0; i < panel_max_count; ++i, ++panel){ + dll_insert(&models->layout.free_sentinel, panel); + } + + dividers = push_array(partition, Panel_Divider, divider_max_count); + models->layout.dividers = dividers; + + div = dividers; + for (i = 0; i < divider_max_count-1; ++i, ++div){ + div->next = (div + 1); + } + div->next = 0; + models->layout.free_divider = dividers; + } + + { + View *vptr = 0; + i32 i = 0; + i32 max = 0; + + vars->live_set.count = 0; + vars->live_set.max = panel_max_count; + + vars->live_set.views = push_array(partition, View, vars->live_set.max); + + dll_init_sentinel(&vars->live_set.free_sentinel); + + max = vars->live_set.max; + vptr = vars->live_set.views; + for (i = 0; i < max; ++i, ++vptr){ + dll_insert(&vars->live_set.free_sentinel, vptr); + } + } + + { + Command_Map *global; + i32 wanted_size = 0; + b32 did_top = 0; + b32 did_file = 0; + + models->scroll_rule = fallback_scroll_rule; + + setup_command_table(); + + global = &models->map_top; + Assert(models->config_api.get_bindings != 0); + + wanted_size = models->config_api.get_bindings(app_links.memory, app_links.memory_size); + + if (wanted_size <= app_links.memory_size){ + Command_Map *map_ptr = 0; + Binding_Unit *unit, *end; + i32 user_map_count; + + unit = (Binding_Unit*)app_links.memory; + if (unit->type == unit_header && unit->header.error == 0){ + end = unit + unit->header.total_size; + + user_map_count = unit->header.user_map_count; + + models->map_id_table = push_array( + &models->mem.part, i32, user_map_count); + + models->user_maps = push_array( + &models->mem.part, Command_Map, user_map_count); + + models->user_map_count = user_map_count; + + for (++unit; unit < end; ++unit){ + switch (unit->type){ + case unit_map_begin: + { + int table_max = unit->map_begin.bind_count * 3 / 2; + int mapid = unit->map_begin.mapid; + if (mapid == mapid_global){ + map_ptr = &models->map_top; + map_init(map_ptr, &models->mem.part, table_max, global); + did_top = 1; + } + else if (mapid == mapid_file){ + map_ptr = &models->map_file; + map_init(map_ptr, &models->mem.part, table_max, global); + did_file = 1; + } + else if (mapid < mapid_global){ + i32 index = app_get_or_add_map_index(models, mapid); + Assert(index < user_map_count); + map_ptr = models->user_maps + index; + map_init(map_ptr, &models->mem.part, table_max, global); + } + else map_ptr = 0; + }break; + + case unit_inherit: + if (map_ptr){ + Command_Map *parent = 0; + int mapid = unit->map_inherit.mapid; + if (mapid == mapid_global) parent = &models->map_top; + else if (mapid == mapid_file) parent = &models->map_file; + else if (mapid < mapid_global){ + i32 index = app_get_or_add_map_index(models, mapid); + if (index < user_map_count) parent = models->user_maps + index; + else parent = 0; + } + map_ptr->parent = parent; + }break; + + case unit_binding: + if (map_ptr){ + Command_Function func = 0; + if (unit->binding.command_id >= 0 && unit->binding.command_id < cmdid_count) + func = command_table[unit->binding.command_id]; + if (func){ + if (unit->binding.code == 0 && unit->binding.modifiers == 0){ + map_ptr->vanilla_keyboard_default.function = func; + map_ptr->vanilla_keyboard_default.custom_id = unit->binding.command_id; + } + else{ + map_add(map_ptr, unit->binding.code, unit->binding.modifiers, func, unit->binding.command_id); + } + } + } + break; + + case unit_callback: + if (map_ptr){ + Command_Function func = command_user_callback; + Custom_Command_Function *custom = unit->callback.func; + if (func){ + if (unit->callback.code == 0 && unit->callback.modifiers == 0){ + map_ptr->vanilla_keyboard_default.function = func; + map_ptr->vanilla_keyboard_default.custom = custom; + } + else{ + map_add(map_ptr, unit->callback.code, unit->callback.modifiers, func, custom); + } + } + } + break; + + case unit_hook: + { + int hook_id = unit->hook.hook_id; + if (hook_id >= 0){ + if (hook_id < hook_type_count){ + models->hooks[hook_id] = (Hook_Function*)unit->hook.func; + } + else{ + models->scroll_rule = (Scroll_Rule_Function*)unit->hook.func; + } + } + }break; + } + } + } + } + + memset(app_links.memory, 0, wanted_size); + if (!did_top) setup_top_commands(&models->map_top, &models->mem.part, global); + if (!did_file) setup_file_commands(&models->map_file, &models->mem.part, global); + +#if !defined(FRED_SUPER) + models->hooks[hook_start] = 0; +#endif + + setup_ui_commands(&models->map_ui, &models->mem.part, global); + + models->font_set = &target->font_set; + + font_set_init(models->font_set, partition, 16, 5); + } + + { + struct Font_Setup{ + char *c_file_name; + i32 file_name_len; + char *c_name; + i32 name_len; + i32 pt_size; + }; + +#define LitStr(n) n, sizeof(n)-1 + + Font_Setup font_setup[] = { + {LitStr("LiberationSans-Regular.ttf"), + LitStr("liberation sans"), + 16}, + + {LitStr("liberation-mono.ttf"), + LitStr("liberation mono"), + 16}, + + {LitStr("Hack-Regular.ttf"), + LitStr("hack"), + 16}, + + {LitStr("CutiveMono-Regular.ttf"), + LitStr("cutive mono"), + 16}, + + {LitStr("Inconsolata-Regular.ttf"), + LitStr("inconsolata"), + 16}, + + }; + i32 font_count = ArrayCount(font_setup); + + for (i32 i = 0; i < font_count; ++i){ + String file_name = make_string(font_setup[i].c_file_name, + font_setup[i].file_name_len); + String name = make_string(font_setup[i].c_name, + font_setup[i].name_len); + i32 pt_size = font_setup[i].pt_size; + + font_set_add(partition, models->font_set, file_name, name, pt_size); + } + } + + // NOTE(allen): file setup + { + models->working_set.file_count = 0; + models->working_set.file_max = 119; + models->working_set.files = push_array( + partition, Editing_File, models->working_set.file_max + 1); + + models->working_set.files[0].state.is_dummy = 1; + + dll_init_sentinel(&models->working_set.free_sentinel); + dll_init_sentinel(&models->working_set.used_sentinel); + + Editing_File *file = models->working_set.files + 1; + i32 max = models->working_set.file_max; + for (i32 i = 0; i < max; ++i, ++file){ + dll_insert(&models->working_set.free_sentinel, &file->node); + } + + models->working_set.table.max = models->working_set.file_max * 3 / 2; + models->working_set.table.count = 0; + models->working_set.table.table = push_array( + partition, File_Table_Entry, models->working_set.table.max); + memset(models->working_set.table.table, 0, sizeof(File_Table_Entry) * models->working_set.table.max); + } + + // NOTE(allen): clipboard setup + models->working_set.clipboard_max_size = ArrayCount(models->working_set.clipboards); + models->working_set.clipboard_size = 0; + models->working_set.clipboard_current = 0; + models->working_set.clipboard_rolling = 0; + + // TODO(allen): more robust allocation solution for the clipboard + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&models->mem.general, &models->working_set, clipboard.size); + copy(dest, make_string((char*)clipboard.str, clipboard.size)); + } + + // NOTE(allen): delay setup + models->delay1.general = &models->mem.general; + models->delay1.max = 16; + models->delay1.acts = (Delayed_Action*)general_memory_allocate( + &models->mem.general, models->delay1.max*sizeof(Delayed_Action), 0); + + models->delay2.general = &models->mem.general; + models->delay2.max = 16; + models->delay2.acts = (Delayed_Action*)general_memory_allocate( + &models->mem.general, models->delay2.max*sizeof(Delayed_Action), 0); + + // NOTE(allen): style setup + app_hardcode_styles(models); + + models->palette_size = 40; + models->palette = push_array(partition, u32, models->palette_size); + + // NOTE(allen): init first panel + Panel_And_ID p = layout_alloc_panel(&models->layout); + panel_make_empty(system, exchange, vars, p.panel); + models->layout.active_panel = p.id; + + String hdbase = make_fixed_width_string(models->hot_dir_base_); + hot_directory_init(&models->hot_directory, hdbase, current_directory, system->slash); + + // NOTE(allen): child proc list setup + i32 max_children = 16; + partition_align(partition, 8); + vars->cli_processes.procs = push_array(partition, CLI_Process, max_children); + vars->cli_processes.max = max_children; + vars->cli_processes.count = 0; + + // NOTE(allen): sys app binding setup + vars->sys_app_max = exchange->file.max; + vars->sys_app_count = 0; + vars->sys_app_bindings = (Sys_App_Binding*)push_array(partition, Sys_App_Binding, vars->sys_app_max); + + // NOTE(allen): parameter setup + models->buffer_param_max = 32; + models->buffer_param_count = 0; + models->buffer_param_indices = push_array(partition, i32, models->buffer_param_max); +} + +internal App_Open_File_Result +app_open_file_background(App_Vars *vars, Exchange *exchange, Working_Set *working_set, String filename){ + Get_File_Result file; + i32 file_id; + App_Open_File_Result result = {}; + + result.file = working_set_contains(working_set, filename); + if (result.file == 0){ + result.is_new = 1; + file = working_set_get_available_file(working_set); + if (file.file){ + result.file = file.file; + file_id = exchange_request_file(exchange, filename.str, filename.size); + if (file_id){ + file_init_strings(result.file); + file_set_name(working_set, result.file, filename.str); + file_set_to_loading(result.file); + table_add(&working_set->table, result.file->name.source_path, file.index); + + result.sys_id = file_id; + result.file_index = file.index; + } + else{ + working_set_free_file(working_set, file.file); + file.file = 0; + } + } + } + + return(result); +} + +App_Step_Sig(app_step){ + ProfileStart(OS_syncing); + Application_Step_Result app_result = *result; + app_result.redraw = force_redraw; + + App_Vars *vars = (App_Vars*)memory->vars_memory; + Models *models = &vars->models; + target->partition = &models->mem.part; + + if (first_step || !time_step){ + app_result.redraw = 1; + } + + // NOTE(allen): OS clipboard event handling + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&models->mem.general, &models->working_set, clipboard.size); + dest->size = eol_convert_in(dest->str, clipboard.str, clipboard.size); + } + + // TODO(allen): profile this make sure it's not costing me too much power. + // NOTE(allen): check files are up to date + { + File_Node *node, *used_nodes; + Editing_File *file; + u64 time_stamp; + + used_nodes = &models->working_set.used_sentinel; + for (dll_items(node, used_nodes)){ + file = (Editing_File*)node; + + time_stamp = system->file_time_stamp(make_c_str(file->name.source_path)); + + // TODO(allen): This is a bit wasteful! Let's dial it in a bit. + if (time_stamp > 0){ + file->state.last_sys_write_time = time_stamp; + if (file->state.last_sys_write_time != file->state.last_4ed_write_time){ + app_result.redraw = 1; + } + } + } + } + + // NOTE(allen): update child processes + if (time_step){ + Temp_Memory temp = begin_temp_memory(&models->mem.part); + u32 max = Kbytes(32); + char *dest = push_array(&models->mem.part, char, max); + u32 amount; + + i32 count = vars->cli_processes.count; + for (i32 i = 0; i < count; ++i){ + CLI_Process *proc = vars->cli_processes.procs + i; + Editing_File *out_file = proc->out_file; + + if (out_file != 0){ + i32 new_cursor = out_file->state.cursor_pos; + + for (system->cli_begin_update(&proc->cli); + system->cli_update_step(&proc->cli, dest, max, &amount);){ + amount = eol_in_place_convert_in(dest, amount); + + i32 end = buffer_size(&out_file->state.buffer); + file_replace_range(system, models, out_file, + end, end, dest, amount, end + amount, 1); + app_result.redraw = 1; + new_cursor = end + amount; + } + + if (system->cli_end_update(&proc->cli)){ + *proc = vars->cli_processes.procs[--count]; + --i; + + char str_space[256]; + String str = make_fixed_width_string(str_space); + append(&str, "exited with code "); + append_int_to_str(proc->cli.exit, &str); + + i32 end = buffer_size(&out_file->state.buffer); + file_replace_range(system, models, out_file, + end, end, str.str, str.size, end + str.size, 1); + app_result.redraw = 1; + new_cursor = end + str.size; + } + + new_cursor = 0; + + for (View_Iter iter = file_view_iter_init(&models->layout, out_file, 0); + file_view_iter_good(iter); + iter = file_view_iter_next(iter)){ + view_cursor_move(iter.view, new_cursor); + } + } + } + + vars->cli_processes.count = count; + end_temp_memory(temp); + } + + // NOTE(allen): reorganizing panels on screen + { + i32 prev_width = models->layout.full_width; + i32 prev_height = models->layout.full_height; + i32 current_width = target->width; + i32 current_height = target->height; + + Panel *panel, *used_panels; + View *view; + + models->layout.full_width = current_width; + models->layout.full_height = current_height; + + if (prev_width != current_width || prev_height != current_height){ + layout_refit(&models->layout, prev_width, prev_height); + + used_panels = &models->layout.used_sentinel; + for (dll_items(panel, used_panels)){ + view = panel->view; + Assert(view); + // TODO(allen): All responses to a panel changing size should + // be handled in the same place. + view_change_size(system, &models->mem.general, view); + } + + app_result.redraw = 1; + } + } + + // NOTE(allen): prepare input information + Key_Summary key_data = {}; + for (i32 i = 0; i < input->press_count; ++i){ + key_data.keys[key_data.count++] = input->press[i]; + } + for (i32 i = 0; i < input->hold_count; ++i){ + key_data.keys[key_data.count++] = input->hold[i]; + } + + mouse->wheel = -mouse->wheel; + + ProfileEnd(OS_syncing); + + ProfileStart(hover_status); + // NOTE(allen): detect mouse hover status + i32 mx = mouse->x; + i32 my = mouse->y; + b32 mouse_in_edit_area = 0; + b32 mouse_in_margin_area = 0; + Panel *mouse_panel, *used_panels; + + used_panels = &models->layout.used_sentinel; + for (dll_items(mouse_panel, used_panels)){ + if (hit_check(mx, my, mouse_panel->inner)){ + mouse_in_edit_area = 1; + break; + } + else if (hit_check(mx, my, mouse_panel->full)){ + mouse_in_margin_area = 1; + break; + } + } + + if (!(mouse_in_edit_area || mouse_in_margin_area)){ + mouse_panel = 0; + } + + b32 mouse_on_divider = 0; + b32 mouse_divider_vertical = 0; + i32 mouse_divider_id = 0; + i32 mouse_divider_side = 0; + + if (mouse_in_margin_area){ + Panel *panel = mouse_panel; + if (mx >= panel->inner.x0 && mx < panel->inner.x1){ + mouse_divider_vertical = 0; + if (my > panel->inner.y0){ + mouse_divider_side = -1; + } + else{ + mouse_divider_side = 1; + } + } + else{ + mouse_divider_vertical = 1; + if (mx > panel->inner.x0){ + mouse_divider_side = -1; + } + else{ + mouse_divider_side = 1; + } + } + + if (models->layout.panel_count > 1){ + i32 which_child; + mouse_divider_id = panel->parent; + which_child = panel->which_child; + for (;;){ + Divider_And_ID div = layout_get_divider(&models->layout, mouse_divider_id); + + if (which_child == mouse_divider_side && + div.divider->v_divider == mouse_divider_vertical){ + mouse_on_divider = 1; + break; + } + + if (mouse_divider_id == models->layout.root){ + break; + } + else{ + mouse_divider_id = div.divider->parent; + which_child = div.divider->which_child; + } + } + } + else{ + mouse_on_divider = 0; + mouse_divider_id = 0; + } + } + ProfileEnd(hover_status); + + // NOTE(allen): prepare to start executing commands + ProfileStart(prepare_commands); + + Command_Data *cmd = &vars->command_data; + + cmd->models = models; + cmd->vars = vars; + cmd->system = system; + cmd->exchange = exchange; + cmd->live_set = &vars->live_set; + + cmd->panel = models->layout.panels + models->layout.active_panel; + cmd->view = cmd->panel->view; + + cmd->screen_width = target->width; + cmd->screen_height = target->height; + + cmd->key = {}; + + Temp_Memory param_stack_temp = begin_temp_memory(&models->mem.part); + cmd->part = partition_sub_part(&models->mem.part, 16 << 10); + + if (first_step){ + if (models->hooks[hook_start]){ + models->hooks[hook_start](&app_links); + cmd->part.pos = 0; + } + + i32 i; + String file_name; + Panel *panel = models->layout.used_sentinel.next; + for (i = 0; i < models->settings.init_files_count; ++i, panel = panel->next){ + file_name = make_string_slowly(models->settings.init_files[i]); + + if (i < models->layout.panel_count){ + delayed_open(&models->delay1, file_name, panel); + if (i == 0){ + delayed_set_line(&models->delay1, panel, models->settings.initial_line); + } + } + else{ + delayed_open_background(&models->delay1, file_name); + } + } + } + ProfileEnd(prepare_commands); + + // NOTE(allen): process the command_coroutine if it is unfinished + ProfileStart(command_coroutine); + b8 consumed_input[6] = {0}; + + if (models->command_coroutine != 0){ + Coroutine *command_coroutine = models->command_coroutine; + u32 get_flags = models->command_coroutine_flags[0]; + u32 abort_flags = models->command_coroutine_flags[1]; + + get_flags |= abort_flags; + + if ((get_flags & EventOnAnyKey) || (get_flags & EventOnEsc)){ + for (i32 key_i = 0; key_i < key_data.count; ++key_i){ + Key_Event_Data key = get_single_key(&key_data, key_i); + View *view = cmd->view; + b32 pass_in = 0; + cmd->key = key; + + Command_Map *map = 0; + if (view) map = view->map; + if (map == 0) map = &models->map_top; + Command_Binding cmd_bind = map_extract_recursive(map, key); + + User_Input user_in; + user_in.type = UserInputKey; + user_in.key = key; + user_in.command = (unsigned long long)cmd_bind.custom; + user_in.abort = 0; + + if ((EventOnEsc & abort_flags) && key.keycode == key_esc){ + user_in.abort = 1; + } + else if (EventOnAnyKey & abort_flags){ + user_in.abort = 1; + } + + if (EventOnAnyKey & get_flags){ + pass_in = 1; + consumed_input[0] = 1; + } + if (key.keycode == key_esc){ + if (EventOnEsc & get_flags){ + pass_in = 1; + } + consumed_input[1] = 1; + } + + if (pass_in){ + models->command_coroutine = system->resume_coroutine(command_coroutine, &user_in, models->command_coroutine_flags); + app_result.redraw = 1; + + // TOOD(allen): Deduplicate + // TODO(allen): Allow a view to clean up however it wants after a command + // finishes, or after transfering to another view mid command. + if (view != 0 && models->command_coroutine == 0){ + init_query_set(&view->query_set); + } + if (models->command_coroutine == 0) break; + } + } + } + + if (models->command_coroutine != 0 && (get_flags & EventOnMouse)){ + View *view = cmd->view; + b32 pass_in = 0; + + User_Input user_in; + user_in.type = UserInputMouse; + user_in.mouse = *mouse; + user_in.command = 0; + user_in.abort = 0; + + if (abort_flags & EventOnMouseMove){ + user_in.abort = 1; + } + if (get_flags & EventOnMouseMove){ + pass_in = 1; + consumed_input[2] = 1; + } + + if (mouse->press_l || mouse->release_l || mouse->l){ + if (abort_flags & EventOnLeftButton){ + user_in.abort = 1; + } + if (get_flags & EventOnLeftButton){ + pass_in = 1; + consumed_input[3] = 1; + } + } + + if (mouse->press_r || mouse->release_r || mouse->r){ + if (abort_flags & EventOnRightButton){ + user_in.abort = 1; + } + if (get_flags & EventOnRightButton){ + pass_in = 1; + consumed_input[4] = 1; + } + } + + if (mouse->wheel != 0){ + if (abort_flags & EventOnWheel){ + user_in.abort = 1; + } + if (get_flags & EventOnWheel){ + pass_in = 1; + consumed_input[5] = 1; + } + } + + if (pass_in){ + models->command_coroutine = system->resume_coroutine(command_coroutine, &user_in, + models->command_coroutine_flags); + app_result.redraw = 1; + + // TOOD(allen): Deduplicate + // TODO(allen): Allow a view to clean up however it wants after a command finishes, + // or after transfering to another view mid command. + if (view != 0 && models->command_coroutine == 0){ + init_query_set(&view->query_set); + } + } + } + } + + update_command_data(vars, cmd); + + ProfileEnd(command_coroutine); + + ProfileStart(frame_hook); + if (models->hooks[hook_frame]){ + if ((models->hooks[hook_frame])(&app_links)){ + app_result.redraw = 1; + } + } + ProfileStart(frame_hook); + + // NOTE(allen): pass raw input to the panels + ProfileStart(step); + + Input_Summary dead_input = {}; + dead_input.mouse.x = mouse->x; + dead_input.mouse.y = mouse->y; + + Input_Summary active_input = {}; + active_input.mouse.x = mouse->x; + active_input.mouse.y = mouse->y; + if (!consumed_input[0]){ + active_input.keys = key_data; + } + else if (!consumed_input[1]){ + for (i32 i = 0; i < key_data.count; ++i){ + Key_Event_Data key = get_single_key(&key_data, i); + if (key.keycode == key_esc){ + active_input.keys.count = 1; + active_input.keys.keys[0] = key; + break; + } + } + } + + Mouse_State mouse_state = *mouse; + + if (consumed_input[3]){ + mouse_state.l = 0; + mouse_state.press_l = 0; + mouse_state.release_l = 0; + } + + if (consumed_input[4]){ + mouse_state.r = 0; + mouse_state.press_r = 0; + mouse_state.release_r = 0; + } + + if (consumed_input[5]){ + mouse_state.wheel = 0; + } + + { + Panel *panel, *used_panels; + View *view; + b32 active; + + used_panels = &models->layout.used_sentinel; + for (dll_items(panel, used_panels)){ + view = panel->view; + active = (panel == cmd->panel); + Input_Summary input = (active)?(active_input):(dead_input); + if (panel == mouse_panel && !mouse->out_of_window){ + input.mouse = mouse_state; + } + if (step_file_view(system, exchange, view, panel->inner, active, &input)){ + app_result.redraw = 1; + } + } + } + + update_command_data(vars, cmd); + ProfileEnd(step); + + // NOTE(allen): command execution + ProfileStart(command); + if (!consumed_input[0] || !consumed_input[1]){ + b32 consumed_input2[2] = {0}; + + for (i32 key_i = 0; key_i < key_data.count; ++key_i){ + if (models->command_coroutine != 0) break; + + switch (vars->state){ + case APP_STATE_EDIT: + { + Key_Event_Data key = get_single_key(&key_data, key_i); + b32 hit_esc = (key.keycode == key_esc); + cmd->key = key; + + if (hit_esc || !consumed_input[0]){ + View *view = cmd->view; + + Command_Map *map = 0; + if (view) map = view->map; + if (map == 0) map = &models->map_top; + Command_Binding cmd_bind = map_extract_recursive(map, key); + + if (cmd_bind.function){ + if (hit_esc){ + consumed_input2[1] = 1; + } + else{ + consumed_input2[0] = 1; + } + + Assert(models->command_coroutine == 0); + Coroutine *command_coroutine = system->create_coroutine(command_caller); + models->command_coroutine = command_coroutine; + + Command_In cmd_in; + cmd_in.cmd = cmd; + cmd_in.bind = cmd_bind; + + models->command_coroutine = system->launch_coroutine(models->command_coroutine, + &cmd_in, models->command_coroutine_flags); + models->prev_command = cmd_bind; + app_result.redraw = 1; + } + } + }break; + + case APP_STATE_RESIZING: + { + if (key_data.count > 0){ + vars->state = APP_STATE_EDIT; + } + }break; + } + } + + consumed_input[0] |= consumed_input2[0]; + consumed_input[1] |= consumed_input2[1]; + } + + update_command_data(vars, cmd); + ProfileEnd(command); + + ProfileStart(resizing); + // NOTE(allen): panel resizing + switch (vars->state){ + case APP_STATE_EDIT: + { + if (mouse->press_l && mouse_on_divider){ + vars->state = APP_STATE_RESIZING; + Divider_And_ID div = layout_get_divider(&models->layout, mouse_divider_id); + vars->resizing.divider = div.divider; + + i32 min, max; + { + i32 mid, MIN, MAX; + mid = div.divider->pos; + if (mouse_divider_vertical){ + MIN = 0; + MAX = MIN + models->layout.full_width; + } + else{ + MIN = 0; + MAX = MIN + models->layout.full_height; + } + min = MIN; + max = MAX; + + i32 divider_id = div.id; + do{ + Divider_And_ID other_div = layout_get_divider(&models->layout, divider_id); + b32 divider_match = (other_div.divider->v_divider == mouse_divider_vertical); + i32 pos = other_div.divider->pos; + if (divider_match && pos > mid && pos < max){ + max = pos; + } + else if (divider_match && pos < mid && pos > min){ + min = pos; + } + divider_id = other_div.divider->parent; + }while(divider_id != -1); + + Temp_Memory temp = begin_temp_memory(&models->mem.part); + i32 *divider_stack = push_array(&models->mem.part, i32, models->layout.panel_count); + i32 top = 0; + divider_stack[top++] = div.id; + + while (top > 0){ + Divider_And_ID other_div = layout_get_divider(&models->layout, divider_stack[--top]); + b32 divider_match = (other_div.divider->v_divider == mouse_divider_vertical); + i32 pos = other_div.divider->pos; + if (divider_match && pos > mid && pos < max){ + max = pos; + } + else if (divider_match && pos < mid && pos > min){ + min = pos; + } + if (other_div.divider->child1 != -1){ + divider_stack[top++] = other_div.divider->child1; + } + if (other_div.divider->child2 != -1){ + divider_stack[top++] = other_div.divider->child2; + } + } + + end_temp_memory(temp); + } + + vars->resizing.min = min; + vars->resizing.max = max; + } + }break; + + case APP_STATE_RESIZING: + { + app_result.redraw = 1; + if (mouse->l){ + Panel_Divider *divider = vars->resizing.divider; + if (divider->v_divider){ + divider->pos = mx; + } + else{ + divider->pos = my; + } + + if (divider->pos < vars->resizing.min){ + divider->pos = vars->resizing.min; + } + else if (divider->pos > vars->resizing.max){ + divider->pos = vars->resizing.max - 1; + } + + layout_fix_all_panels(&models->layout); + } + else{ + vars->state = APP_STATE_EDIT; + } + }break; + } + + if (mouse_in_edit_area && mouse_panel != 0 && mouse->press_l){ + models->layout.active_panel = (i32)(mouse_panel - models->layout.panels); + app_result.redraw = 1; + } + + update_command_data(vars, cmd); + ProfileEnd(resizing); + + // NOTE(allen): processing sys app bindings + ProfileStart(sys_app_bind_processing); + { + Mem_Options *mem = &models->mem; + General_Memory *general = &mem->general; + + for (i32 i = 0; i < vars->sys_app_count; ++i){ + Sys_App_Binding *binding; + b32 remove = 0; + b32 failed = 0; + binding = vars->sys_app_bindings + i; + + byte *data; + i32 size, max; + Editing_File *ed_file; + Editing_File_Preload preload_settings; + char *filename; + + Working_Set *working_set = &models->working_set; + + if (exchange_file_ready(exchange, binding->sys_id, &data, &size, &max)){ + ed_file = working_set_get_file(working_set, binding->app_id, 1).file; + Assert(ed_file); + + filename = exchange_file_filename(exchange, binding->sys_id); + preload_settings = ed_file->preload; + if (data){ + String val = make_string((char*)data, size); + // TODO(allen): reduce to just passing models + file_create_from_string(system, models, ed_file, filename, val); + + if (ed_file->settings.tokens_exist){ + file_first_lex_parallel(system, general, ed_file); + } + + if ((binding->success & SysAppCreateView) && binding->panel != 0){ + view_file_in_panel(cmd, binding->panel, ed_file); + } + + for (View_Iter iter = file_view_iter_init(&models->layout, ed_file, 0); + file_view_iter_good(iter); + iter = file_view_iter_next(iter)){ + view_measure_wraps(system, general, iter.view); + view_cursor_move(iter.view, preload_settings.start_line, 0); + } + + app_result.redraw = 1; + } + else{ + if (binding->fail & SysAppCreateNewBuffer){ + file_create_empty(system, models, ed_file, filename); + if (binding->fail & SysAppCreateView){ + view_file_in_panel(cmd, binding->panel, ed_file); + } + } + else{ + table_remove(&models->working_set.table, ed_file->name.source_path); + working_set_free_file(&models->working_set, ed_file); + } + + app_result.redraw = 1; + } + + exchange_free_file(exchange, binding->sys_id); + remove = 1; + } + + if (exchange_file_save_complete(exchange, binding->sys_id, &data, &size, &max, &failed)){ + Assert(remove == 0); + + if (data){ + general_memory_free(general, data); + exchange_clear_file(exchange, binding->sys_id); + } + + Editing_File *file = working_set_get_file(working_set, binding->app_id, 1).file; + if (file){ + file_synchronize_times(system, file, file->name.source_path.str); + } + + exchange_free_file(exchange, binding->sys_id); + remove = 1; + + // if (failed) { TODO(allen): saving error, now what? } + } + + if (remove){ + *binding = vars->sys_app_bindings[--vars->sys_app_count]; + --i; + } + } + } + ProfileEnd(sys_app_bind_processing); + + // NOTE(allen): process as many delayed actions as possible + ProfileStart(delayed_actions); + if (models->delay1.count > 0){ + Working_Set *working_set = &models->working_set; + Mem_Options *mem = &models->mem; + General_Memory *general = &mem->general; + + i32 count = models->delay1.count; + models->delay1.count = 0; + models->delay2.count = 0; + + Delayed_Action *act = models->delay1.acts; + for (i32 i = 0; i < count; ++i, ++act){ + String string = act->string; + Panel *panel = act->panel; + Editing_File *file = act->file; + i32 integer = act->integer; + + // TODO(allen): Paramter checking in each DACT case. + switch (act->type){ + case DACT_TOUCH_FILE: + { + if (file){ + Assert(!file->state.is_dummy); + dll_remove(&file->node); + dll_insert(&models->working_set.used_sentinel, &file->node); + } + }break; + + case DACT_OPEN: + case DACT_OPEN_BACKGROUND: + { + App_Open_File_Result result = {}; + { + String filename = string; + Get_File_Result file; + i32 file_id; + + filename.str[0] = char_to_lower(filename.str[0]); + + result.file = working_set_contains(working_set, filename); + if (result.file == 0){ + result.is_new = 1; + file = working_set_get_available_file(working_set); + if (file.file){ + result.file = file.file; + file_id = exchange_request_file(exchange, filename.str, filename.size); + if (file_id){ + file_init_strings(result.file); + file_set_name(working_set, result.file, filename.str); + file_set_to_loading(result.file); + table_add(&working_set->table, result.file->name.source_path, file.index); + + result.sys_id = file_id; + result.file_index = file.index; + } + else{ + working_set_free_file(working_set, file.file); + file.file = 0; + } + } + } + } + + if (result.is_new){ + if (result.file){ + if (result.sys_id){ + Sys_App_Binding *binding = app_push_file_binding(vars, result.sys_id, result.file_index); + binding->success = (act->type == DACT_OPEN) ? SysAppCreateView : 0; + binding->fail = 0; + binding->panel = panel; + } + else{ + delayed_action_repush(&models->delay2, act); + } + } + } + else{ + if (act->type == DACT_OPEN){ + Assert(result.file); + if (!result.file->state.is_loading){ + view_file_in_panel(cmd, panel, result.file); + } + } + } + }break; + + case DACT_SET_LINE: + { + // TODO(allen): deduplicate + Editing_File *file = 0; + if (panel){ + file = panel->view->file; + } + else if (string.str && string.size > 0){ + file = working_set_lookup_file(working_set, string); + } + if (file){ + if (file->state.is_loading){ + file->preload.start_line = integer; + } + else{ + // TODO(allen): write this case + } + } + }break; + + case DACT_SAVE_AS: + { + // TODO(allen): deduplicate + Editing_File *file = 0; + if (panel){ + file = panel->view->file; + } + else if (string.str && string.size > 0){ + file = working_set_lookup_file(working_set, string); + } + if (file){ + i32 sys_id = file_save_and_set_names(system, exchange, mem, working_set, file, string.str); + if (sys_id){ + app_push_file_binding(vars, sys_id, (i32)(file - working_set->files)); + } + else{ + delayed_action_repush(&models->delay2, act); + } + } + }break; + + case DACT_SAVE: + { + if (!file){ + if (panel){ + View *view = panel->view; + Assert(view); + file = view->file; + } + else{ + file = working_set_lookup_file(working_set, string); + } + } + // TODO(allen): We could handle the case where someone tries to save the same thing + // twice... that would be nice to have under control. + if (file){ + i32 sys_id = file_save(system, exchange, mem, file, file->name.source_path.str); + if (sys_id){ + // TODO(allen): This is fishy! Shouldn't we bind it to a file name instead? This file + // might be killed before we get notified that the saving is done! + app_push_file_binding(vars, sys_id, (i32)(file - working_set->files)); + } + else{ + delayed_action_repush(&models->delay2, act); + } + } + }break; + + case DACT_NEW: + { + Get_File_Result file = working_set_get_available_file(working_set); + file_create_empty(system, models, file.file, string.str); + table_add(&working_set->table, file.file->name.source_path, file.index); + + View *view = panel->view; + + view_set_file(view, file.file, models, system, + models->hooks[hook_open_file], &app_links); + view->map = app_get_map(models, file.file->settings.base_map_id); +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + if (file.file->settings.tokens_exist) + file_first_lex_parallel(system, general, file.file); +#endif + }break; + + case DACT_SWITCH: + { + if (!file && string.str){ + file = working_set_lookup_file(working_set, string); + + if (!file){ + file = working_set_contains(working_set, string); + } + } + + if (file){ + View *view = panel->view; + + view_set_file(view, file, models, system, + models->hooks[hook_open_file], &app_links); + view->map = app_get_map(models, file->settings.base_map_id); + } + }break; + + case DACT_KILL: + { + if (!file && string.str){ + file = working_set_lookup_file(working_set, string); + + if (!file){ + file = working_set_contains(working_set, string); + } + } + + if (file){ + table_remove(&working_set->table, file->name.source_path); + kill_file(system, exchange, models, file, + models->hooks[hook_open_file], &app_links); + } + }break; + + case DACT_TRY_KILL: + { + View *view = 0; + if (panel){ + view = panel->view; + } + else{ + view = (models->layout.panels + models->layout.active_panel)->view; + } + + if (!file && string.str){ + file = working_set_lookup_file(working_set, string); + + if (!file){ + file = working_set_contains(working_set, string); + } + } + + if (file){ + if (buffer_needs_save(file)){ + view_show_interactive(system, view, &models->map_ui, + IAct_Sure_To_Kill, IInt_Sure_To_Kill, make_lit_string("Are you sure?")); + copy(&view->dest, file->name.live_name); + } + else{ + table_remove(&working_set->table, file->name.source_path); + kill_file(system, exchange, models, file, + models->hooks[hook_open_file], &app_links); + } + } + }break; + } + + if (string.str){ + general_memory_free(general, string.str); + } + } + Swap(models->delay1, models->delay2); + } + + end_temp_memory(param_stack_temp); + ProfileEnd(delayed_actions); + + ProfileStart(resize); + // NOTE(allen): send resize messages to panels that have changed size + { + Panel *panel, *used_panels; + used_panels = &models->layout.used_sentinel; + for (dll_items(panel, used_panels)){ + i32_Rect prev = panel->prev_inner; + i32_Rect inner = panel->inner; + if (prev.x0 != inner.x0 || prev.y0 != inner.y0 || + prev.x1 != inner.x1 || prev.y1 != inner.y1){ + remeasure_file_view(system, panel->view, panel->inner); + } + panel->prev_inner = inner; + } + } + ProfileEnd(resize); + + ProfileStart(style_change); + // NOTE(allen): send style change messages if the style has changed + if (models->global_font.font_changed){ + models->global_font.font_changed = 0; + + File_Node *node, *used_nodes; + Editing_File *file; + Render_Font *font = get_font_info(models->font_set, models->global_font.font_id)->font; + float *advance_data = 0; + if (font) advance_data = font->advance_data; + + used_nodes = &models->working_set.used_sentinel; + for (dll_items(node, used_nodes)){ + file = (Editing_File*)node; + file_measure_starts_widths(system, &models->mem.general, &file->state.buffer, advance_data); + } + + Panel *panel, *used_panels; + used_panels = &models->layout.used_sentinel; + for (dll_items(panel, used_panels)){ + remeasure_file_view(system, panel->view, panel->inner); + } + } + ProfileEnd(style_change); + + ProfileStart(redraw); + if (mouse_panel != models->prev_mouse_panel) app_result.redraw = 1; + if (app_result.redraw){ + begin_render_section(target, system); + + target->clip_top = -1; + draw_push_clip(target, rect_from_target(target)); + + // NOTE(allen): render the panels + Panel *panel, *used_panels; + used_panels = &models->layout.used_sentinel; + for (dll_items(panel, used_panels)){ + i32_Rect full = panel->full; + i32_Rect inner = panel->inner; + + View *view = panel->view; + Style *style = &models->style; + + b32 active = (panel == cmd->panel); + u32 back_color = style->main.back_color; + draw_rectangle(target, full, back_color); + + draw_push_clip(target, panel->inner); + draw_file_view(system, exchange, view, cmd->view, panel->inner, active, target, &dead_input); + draw_pop_clip(target); + + u32 margin_color; + if (active){ + margin_color = style->main.margin_active_color; + } + else if (panel == mouse_panel){ + margin_color = style->main.margin_hover_color; + } + else{ + margin_color = style->main.margin_color; + } + draw_rectangle(target, i32R(full.x0, full.y0, full.x1, inner.y0), margin_color); + draw_rectangle(target, i32R(full.x0, inner.y1, full.x1, full.y1), margin_color); + draw_rectangle(target, i32R(full.x0, inner.y0, inner.x0, inner.y1), margin_color); + draw_rectangle(target, i32R(inner.x1, inner.y0, full.x1, inner.y1), margin_color); + } + + end_render_section(target, system); + } + ProfileEnd(redraw); + + ProfileStart(get_cursor); + // NOTE(allen): get cursor type + if (mouse_in_edit_area){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + } + else if (mouse_in_margin_area){ + if (mouse_on_divider){ + if (mouse_divider_vertical){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + } + else{ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_UPDOWN; + } + } + else{ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + } + } + models->prev_mouse_panel = mouse_panel; + ProfileEnd(get_cursor); + + app_result.lctrl_lalt_is_altgr = models->settings.lctrl_lalt_is_altgr; + *result = app_result; + + // end-of-app_step +} + +external App_Get_Functions_Sig(app_get_functions){ + App_Functions result = {}; + + result.read_command_line = app_read_command_line; + result.init = app_init; + result.step = app_step; + + return(result); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed.h b/test_data/lots_of_files/4ed.h new file mode 100644 index 0000000..c27572f --- /dev/null +++ b/test_data/lots_of_files/4ed.h @@ -0,0 +1,130 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application Layer for 4coder + * + */ + +// TOP + +#ifndef FRED_H +#define FRED_H + +struct Application_Memory{ + void *vars_memory; + i32 vars_memory_size; + void *target_memory; + i32 target_memory_size; + void *user_memory; + i32 user_memory_size; +}; + +#define KEY_INPUT_BUFFER_SIZE 4 +#define KEY_INPUT_BUFFER_DSIZE (KEY_INPUT_BUFFER_SIZE << 1) + +struct Key_Input_Data{ + Key_Event_Data press[KEY_INPUT_BUFFER_SIZE]; + Key_Event_Data hold[KEY_INPUT_BUFFER_SIZE]; + i32 press_count; + i32 hold_count; +}; + +struct Key_Summary{ + i32 count; + Key_Event_Data keys[KEY_INPUT_BUFFER_DSIZE]; +}; + +inline Key_Event_Data +get_single_key(Key_Summary *summary, i32 index){ + Assert(index >= 0 && index < summary->count); + Key_Event_Data key; + key = summary->keys[index]; + return key; +} + +struct Input_Summary{ + Mouse_State mouse; + Key_Summary keys; +}; + +struct Command_Line_Parameters{ + char **argv; + int argc; +}; + +struct Plat_Settings{ + char *custom_dll; + b32 custom_dll_is_strict; + + i32 window_w, window_h; + i32 window_x, window_y; + b8 set_window_pos, set_window_size; + b8 maximize_window; +}; + +#define App_Read_Command_Line_Sig(name) \ + i32 name(System_Functions *system, \ + Application_Memory *memory, \ + String current_directory, \ + Plat_Settings *plat_settings, \ + char ***files, i32 **file_count, \ + Command_Line_Parameters clparams \ + ) + +typedef App_Read_Command_Line_Sig(App_Read_Command_Line); + +#define App_Init_Sig(name) void \ +name(System_Functions *system, \ + Render_Target *target, \ + Application_Memory *memory, \ + Exchange *exchange, \ + String clipboard, \ + String current_directory, \ + Custom_API api) + +typedef App_Init_Sig(App_Init); + +enum Application_Mouse_Cursor{ + APP_MOUSE_CURSOR_DEFAULT, + APP_MOUSE_CURSOR_ARROW, + APP_MOUSE_CURSOR_IBEAM, + APP_MOUSE_CURSOR_LEFTRIGHT, + APP_MOUSE_CURSOR_UPDOWN, + // never below this + APP_MOUSE_CURSOR_COUNT +}; + +struct Application_Step_Result{ + Application_Mouse_Cursor mouse_cursor_type; + b32 redraw; + b32 lctrl_lalt_is_altgr; +}; + +#define App_Step_Sig(name) void \ + name(System_Functions *system, \ + Key_Input_Data *input, \ + Mouse_State *mouse, \ + Render_Target *target, \ + Application_Memory *memory, \ + Exchange *exchange, \ + String clipboard, \ + b32 time_step, b32 first_step, b32 force_redraw, \ + Application_Step_Result *result) + +typedef App_Step_Sig(App_Step); + +struct App_Functions{ + App_Read_Command_Line *read_command_line; + App_Init *init; + App_Step *step; +}; + +#define App_Get_Functions_Sig(name) App_Functions name() +typedef App_Get_Functions_Sig(App_Get_Functions); + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_app_settings.h b/test_data/lots_of_files/4ed_app_settings.h new file mode 100644 index 0000000..c277476 --- /dev/null +++ b/test_data/lots_of_files/4ed_app_settings.h @@ -0,0 +1,67 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 27.01.2016 + * + * Global app level settings definition + * + */ + +// TOP + +struct App_Settings{ + char *user_file; + b32 user_file_is_strict; + + char *init_files[8]; + i32 init_files_count; + i32 init_files_max; + + i32 initial_line; + b32 lctrl_lalt_is_altgr; +}; + +struct Models{ + Mem_Options mem; + App_Settings settings; + + Command_Map map_top; + Command_Map map_file; + Command_Map map_ui; + Command_Map *user_maps; + i32 *map_id_table; + i32 user_map_count; + + Command_Binding prev_command; + + Coroutine *command_coroutine; + u32 command_coroutine_flags[2]; + + Hook_Function *hooks[hook_type_count]; + + i32 *buffer_param_indices; + i32 buffer_param_count, buffer_param_max; + + Font_Set *font_set; + Style_Font global_font; + Style style; + Style_Library styles; + u32 *palette; + i32 palette_size; + + Editing_Layout layout; + Working_Set working_set; + + char hot_dir_base_[256]; + Hot_Directory hot_directory; + + Delay delay1, delay2; + + Panel *prev_mouse_panel; + + Custom_API config_api; + Scroll_Rule_Function *scroll_rule; +}; + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_app_target.cpp b/test_data/lots_of_files/4ed_app_target.cpp new file mode 100644 index 0000000..26d1958 --- /dev/null +++ b/test_data/lots_of_files/4ed_app_target.cpp @@ -0,0 +1,55 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 13.11.2015 + * + * Application layer build target + * + */ + +// TOP + +#include "4ed_config.h" + +#define BUFFER_EXPERIMENT_SCALPEL 0 + +#include "4ed_meta.h" + +#include "4cpp_types.h" +#define FCPP_STRING_IMPLEMENTATION +#include "4coder_string.h" + +#include "4ed_mem.cpp" +#include "4ed_math.cpp" + +#include "4coder_custom.h" +#include "4ed_system.h" +#include "4ed_rendering.h" +#include "4ed.h" + +#include "4ed_internal.h" + +#include "4tech_table.cpp" + +#define FCPP_LEXER_IMPLEMENTATION +#include "4cpp_lexer.h" + +#include "4ed_template.cpp" + +#include "4ed_font_set.cpp" +#include "4ed_rendering_helper.cpp" + +#include "4ed_style.h" +#include "4ed_style.cpp" +#include "4ed_exchange.cpp" +#include "4ed_command.cpp" +#include "4ed_file.cpp" +#include "4ed_gui.cpp" +#include "4ed_layout.cpp" +#include "4ed_delay.cpp" +#include "4ed_app_settings.h" +#include "4ed_file_view.cpp" +#include "4ed.cpp" + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_command.cpp b/test_data/lots_of_files/4ed_command.cpp new file mode 100644 index 0000000..4681ad2 --- /dev/null +++ b/test_data/lots_of_files/4ed_command.cpp @@ -0,0 +1,201 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 19.08.2015 + * + * Command management functions for 4coder + * + */ + +// TOP + +#define Command_Function_Sig(name) void (name)( \ + System_Functions *system, \ + struct Command_Data *command, \ + struct Command_Binding binding) + +typedef Command_Function_Sig(*Command_Function); + +struct Command_Binding{ + Command_Function function; + union{ + Custom_Command_Function *custom; + u64 custom_id; + }; + i64 hash; +}; + +struct Command_Map{ + Command_Map *parent; + Command_Binding vanilla_keyboard_default; + Command_Binding *commands; + i32 count, max; +}; + +internal void command_null(Command_Data *command); + +internal i64 +map_hash(u16 event_code, u8 modifiers){ + i64 result = (event_code << 8) | modifiers; + return result; +} + +internal b32 +map_add(Command_Map *map, u16 event_code, u8 modifiers, + Command_Function function, Custom_Command_Function *custom = 0){ + Assert(map->count * 8 < map->max * 7); + Command_Binding bind; + bind.function = function; + bind.custom = custom; + bind.hash = map_hash(event_code, modifiers); + + i32 max = map->max; + i32 index = bind.hash % max; + Command_Binding entry; + while ((entry = map->commands[index]).function && entry.hash != -1){ + if (entry.hash == bind.hash){ + return 1; + } + index = (index + 1) % max; + } + map->commands[index] = bind; + ++map->count; + return 0; +} + +inline b32 +map_add(Command_Map *map, u16 event_code, u8 modifiers, + Command_Function function, u64 custom_id){ + return (map_add(map, event_code, modifiers, function, (Custom_Command_Function*)custom_id)); +} + +internal b32 +map_find_entry(Command_Map *map, u16 event_code, u8 modifiers, + i32 *index_out){ + i64 hash = map_hash(event_code, modifiers); + i32 max = map->max; + i32 index = hash % map->max; + Command_Binding entry; + while ((entry = map->commands[index]).function){ + if (entry.hash == hash){ + *index_out = index; + return 1; + } + index = (index + 1) % max; + } + return 0; +} + +internal b32 +map_find(Command_Map *map, u16 event_code, u8 modifiers, + Command_Binding *bind_out){ + b32 result; + i32 index; + result = map_find_entry(map, event_code, modifiers, &index); + if (result){ + *bind_out = map->commands[index]; + } + return result; +} + +internal b32 +map_drop(Command_Map *map, u16 event_code, u8 modifiers){ + b32 result; + i32 index; + result = map_find_entry(map, event_code, modifiers, &index); + if (result){ + map->commands[index].function = 0; + map->commands[index].hash = -1; + } + return result; +} + +internal void +map_init(Command_Map *commands, Partition *part, i32 max, Command_Map *parent){ + max = ((max < 6)?(6):(max)); + commands->parent = parent; + commands->commands = push_array(part, Command_Binding, max); + memset(commands->commands, 0, max*sizeof(*commands->commands)); + commands->vanilla_keyboard_default = {}; + commands->max = max; + commands->count = 0; +} + +internal void +map_get_vanilla_keyboard_default(Command_Map *map, u8 command, + Command_Binding *bind_out){ + if (command == MDFR_NONE){ + *bind_out = map->vanilla_keyboard_default; + } +} + +inline u8 +apply_shift_to_code(u8 keycode){ + return !(keycode >= 0x20 && keycode < 0x7F && keycode != ' '); +} + +internal Command_Binding +map_extract(Command_Map *map, Key_Event_Data key){ + Command_Binding bind = {}; + + b32 ctrl = key.modifiers[MDFR_CONTROL_INDEX]; + b32 alt = key.modifiers[MDFR_ALT_INDEX]; + b32 shift = key.modifiers[MDFR_SHIFT_INDEX]; + u16 code; + u8 command = MDFR_NONE; + + //if (key.character_no_caps_lock != 0 && + // key.character_no_caps_lock != ' ') shift = 0; + + if (shift) command |= MDFR_SHIFT; + if (ctrl) command |= MDFR_CTRL; + if (alt) command |= MDFR_ALT; + + code = key.character_no_caps_lock; + if (code == 0){ + code = key.keycode; + map_find(map, code, command, &bind); + } + else{ + if (code != '\n' && code != '\t' && code != ' '){ + command &= ~(MDFR_SHIFT); + } + + map_find(map, code, command, &bind); + if (bind.function == 0){ + map_get_vanilla_keyboard_default(map, command, &bind); + } + } + + return(bind); +} + +internal Command_Binding +map_extract_recursive(Command_Map *map, Key_Event_Data key){ + Command_Binding cmd_bind = {}; + Command_Map *visited_maps[16] = {}; + i32 visited_top = 0; + + while (map){ + cmd_bind = map_extract(map, key); + if (cmd_bind.function == 0){ + if (visited_top < ArrayCount(visited_maps)){ + visited_maps[visited_top++] = map; + map = map->parent; + for (i32 i = 0; i < visited_top; ++i){ + if (map == visited_maps[i]){ + map = 0; + break; + } + } + } + else map = 0; + } + else map = 0; + } + + return(cmd_bind); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_config.h b/test_data/lots_of_files/4ed_config.h new file mode 100644 index 0000000..64697a2 --- /dev/null +++ b/test_data/lots_of_files/4ed_config.h @@ -0,0 +1,52 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 13.11.2014 + * + * Application layer build target + * + */ + +// TOP + +#ifdef FRED_NOT_PACKAGE + +#define FRED_INTERNAL 1 +#define FRED_SLOW 1 + +#define FRED_PRINT_DEBUG 1 +#define FRED_PRINT_DEBUG_FILE_LINE 0 +#define FRED_PROFILING 1 +#define FRED_PROFILING_OS 0 +#define FRED_FULL_ERRORS 0 + +#else + +#define FRED_SLOW 0 +#define FRED_INTERNAL 0 + +#define FRED_PRINT_DEBUG 0 +#define FRED_PRINT_DEBUG_FILE_LINE 0 +#define FRED_PROFILING 0 +#define FRED_PROFILING_OS 0 +#define FRED_FULL_ERRORS 0 + +#endif + +#if FRED_INTERNAL == 0 +#undef FRED_PRINT_DEBUG +#define FRED_PRINT_DEBUG 0 +#undef FRED_PROFILING +#define FRED_PROFILING 0 +#undef FRED_PROFILING_OS +#define FRED_PROFILING_OS 0 +#endif + +#if FRED_PRINT_DEBUG == 0 +#undef FRED_PRINT_DEBUG_FILE_LINE +#define FRED_PRINT_DEBUG_FILE_LINE 0 +#undef FRED_PRINT_DEBUG_FILE_LINE +#define FRED_PROFILING_OS 0 +#endif + +// BOTTOM diff --git a/test_data/lots_of_files/4ed_delay.cpp b/test_data/lots_of_files/4ed_delay.cpp new file mode 100644 index 0000000..c5b0871 --- /dev/null +++ b/test_data/lots_of_files/4ed_delay.cpp @@ -0,0 +1,131 @@ +enum Action_Type{ + DACT_OPEN, + DACT_OPEN_BACKGROUND, + DACT_SET_LINE, + DACT_SAVE_AS, + DACT_SAVE, + DACT_NEW, + DACT_SWITCH, + DACT_TRY_KILL, + DACT_KILL, + DACT_TOUCH_FILE, +}; + +struct Delayed_Action{ + Action_Type type; + String string; + Panel* panel; + Editing_File* file; + i32 integer; +}; + +struct Delay{ + General_Memory* general; + Delayed_Action* acts; + i32 count; + i32 max; +}; + +internal String +str_alloc_copy(General_Memory *general, String str){ + String result; + result.memory_size = str.memory_size + 1; + result.size = str.size; + result.str = (char*)general_memory_allocate(general, result.memory_size, 0); + memcpy(result.str, str.str, str.size); + result.str[result.size] = 0; + return(result);} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type){ + Delayed_Action *result; + if (delay->count == delay->max){ + delay->max *= 2; + delay->acts = (Delayed_Action*)general_memory_reallocate(delay->general, delay->acts, delay->count*sizeof(Delayed_Action), delay->max*sizeof(Delayed_Action), 0); + } + result = delay->acts + delay->count++; + *result = {}; + result->type = type; + return(result); +} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type, String string){ + Delayed_Action *result; + result = delayed_action_(delay, type); + result->string = str_alloc_copy(delay->general, string); + return(result); +} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type, Panel* panel){ + Delayed_Action *result; + result = delayed_action_(delay, type); + result->panel = panel; + return(result); +} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type, Editing_File* file){ + Delayed_Action *result; + result = delayed_action_(delay, type); + result->file = file; + return(result); +} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type, Editing_File* file, Panel* panel){ + Delayed_Action *result; + result = delayed_action_(delay, type); + result->file = file; + result->panel = panel; + return(result); +} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type, String string, Panel* panel){ + Delayed_Action *result; + result = delayed_action_(delay, type); + result->string = str_alloc_copy(delay->general, string); + result->panel = panel; + return(result); +} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type, String string, Editing_File* file){ + Delayed_Action *result; + result = delayed_action_(delay, type); + result->string = str_alloc_copy(delay->general, string); + result->file = file; + return(result); +} + +inline Delayed_Action* +delayed_action_(Delay *delay, Action_Type type, Panel* panel, i32 integer){ + Delayed_Action *result; + result = delayed_action_(delay, type); + result->panel = panel; + result->integer = integer; + return(result); +} + +inline Delayed_Action* +delayed_action_repush(Delay *delay, Delayed_Action *act){ + Delayed_Action *new_act = delayed_action_(delay, (Action_Type)0); + *new_act = *act; + if (act->string.str){ + new_act->string = str_alloc_copy(delay->general, act->string); + } + return(new_act); +} + +#define delayed_open(delay, ...) delayed_action_(delay, DACT_OPEN, __VA_ARGS__) +#define delayed_open_background(delay, ...) delayed_action_(delay, DACT_OPEN_BACKGROUND, __VA_ARGS__) +#define delayed_set_line(delay, ...) delayed_action_(delay, DACT_SET_LINE, __VA_ARGS__) +#define delayed_save_as(delay, ...) delayed_action_(delay, DACT_SAVE_AS, __VA_ARGS__) +#define delayed_save(delay, ...) delayed_action_(delay, DACT_SAVE, __VA_ARGS__) +#define delayed_new(delay, ...) delayed_action_(delay, DACT_NEW, __VA_ARGS__) +#define delayed_switch(delay, ...) delayed_action_(delay, DACT_SWITCH, __VA_ARGS__) +#define delayed_try_kill(delay, ...) delayed_action_(delay, DACT_TRY_KILL, __VA_ARGS__) +#define delayed_kill(delay, ...) delayed_action_(delay, DACT_KILL, __VA_ARGS__) +#define delayed_touch_file(delay, ...) delayed_action_(delay, DACT_TOUCH_FILE, __VA_ARGS__) diff --git a/test_data/lots_of_files/4ed_dll_reader.cpp b/test_data/lots_of_files/4ed_dll_reader.cpp new file mode 100644 index 0000000..ea4d638 --- /dev/null +++ b/test_data/lots_of_files/4ed_dll_reader.cpp @@ -0,0 +1,378 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 20.11.2015 + * + * DLL loader declarations for 4coder + * + */ + +// TOP + +// TODO(allen): +// Check the relocation table, if it contains anything that +// is platform specific generate an error to avoid calling +// into invalid code. + +i32 +dll_compare(char *a, char *b, i32 len){ + i32 result; + char *e; + + result = 0; + e = a + len; + for (;a < e && *a == *b; ++a, ++b); + if (a < e){ + if (*a < *b) result = -1; + else result = 1; + } + + return(result); +} + +enum DLL_Error{ + dll_err_too_small_for_header = 1, + dll_err_wrong_MZ_signature, + dll_err_wrong_DOS_error, + dll_err_wrong_PE_signature, + dll_err_unrecognized_bit_signature, +}; + +b32 +dll_parse_headers(Data file, DLL_Data *dll, i32 *error){ + b32 result; + i32 pe_offset; + i32 read_pos; + + result = 1; + if (file.size <= sizeof(DOS_Header) + DOS_error_size){ + if (error) *error = dll_err_too_small_for_header; + result = 0; + goto dll_parse_end; + } + + dll->dos_header = (DOS_Header*)file.data; + + if (dll_compare(dll->dos_header->signature, "MZ", 2) != 0){ + if (error) *error = dll_err_wrong_MZ_signature; + result = 0; + goto dll_parse_end; + } + + if (file.size <= DOS_error_offset + DOS_error_size){ + if (error) *error = dll_err_too_small_for_header; + result = 0; + goto dll_parse_end; + } + + if (dll_compare((char*)(file.data + DOS_error_offset), DOS_error_message, + sizeof(DOS_error_message) - 1) != 0){ + if (error) *error = dll_err_wrong_DOS_error; + result = 0; + goto dll_parse_end; + } + + pe_offset = dll->dos_header->e_lfanew; + read_pos = pe_offset; + + if (file.size <= read_pos + PE_header_size){ + if (error) *error = dll_err_too_small_for_header; + result = 0; + goto dll_parse_end; + } + + if (dll_compare((char*)(file.data + read_pos), + PE_header, PE_header_size) != 0){ + if (error) *error = dll_err_wrong_PE_signature; + result = 0; + goto dll_parse_end; + } + + read_pos += PE_header_size; + + if (file.size <= read_pos + sizeof(COFF_Header)){ + if (error) *error = dll_err_too_small_for_header; + result = 0; + goto dll_parse_end; + } + + dll->coff_header = (COFF_Header*)(file.data + read_pos); + read_pos += sizeof(COFF_Header); + + if (file.size <= read_pos + dll->coff_header->size_of_optional_header){ + if (error) *error = dll_err_too_small_for_header; + result = 0; + goto dll_parse_end; + } + + dll->opt_header_32 = (PE_Opt_Header_32Bit*)(file.data + read_pos); + dll->opt_header_64 = (PE_Opt_Header_64Bit*)(file.data + read_pos); + read_pos += dll->coff_header->size_of_optional_header; + + if (dll->opt_header_32->signature != bitsig_32bit && + dll->opt_header_32->signature != bitsig_64bit){ + if (error) *error = dll_err_unrecognized_bit_signature; + result = 0; + goto dll_parse_end; + } + + if (dll->opt_header_32->signature == bitsig_32bit) dll->is_64bit = 0; + else dll->is_64bit = 1; + + dll->section_defs = (PE_Section_Definition*)(file.data + read_pos); + +dll_parse_end: + return(result); +} + +i32 +dll_total_loaded_size(DLL_Data *dll){ + COFF_Header *coff_header; + PE_Section_Definition *section_def; + i32 result, section_end, i; + + coff_header = dll->coff_header; + section_def = dll->section_defs; + result = 0; + + for (i = 0; i < coff_header->number_of_sections; ++i, ++section_def){ + section_end = section_def->loaded_location + section_def->loaded_size; + if (section_end > result){ + result = section_end; + } + } + + return(result); +} + +b32 +dll_perform_reloc(DLL_Loaded *loaded){ + Data img; + byte *base; + Relocation_Block_Header *header; + Relocation_Block_Entry *entry; + Data_Directory *data_directory; + u32 cursor; + u32 bytes_in_table; + u32 block_end; + u32 type; + u32 offset; + b32 result; + b32 highadj_stage; + + u64 dif64; + + result = 1; + img = loaded->img; + if (loaded->is_64bit){ + data_directory = loaded->opt_header_64->data_directory; + dif64 = ((u64)img.data - (u64)loaded->opt_header_64->image_base); + } + else{ + data_directory = loaded->opt_header_32->data_directory; + dif64 = ((u64)img.data - (u64)loaded->opt_header_32->image_base); + } + data_directory += image_dir_base_reloc_table; + base = img.data + data_directory->virtual_address; + bytes_in_table = data_directory->size; + + highadj_stage = 1; + + + for (cursor = 0; cursor < bytes_in_table;){ + header = (Relocation_Block_Header*)(base + cursor); + block_end = cursor + header->block_size; + cursor += sizeof(Relocation_Block_Header); + + for (;cursor < block_end;){ + entry = (Relocation_Block_Entry*)(base + cursor); + cursor += sizeof(Relocation_Block_Entry); + + type = (u32)(entry->entry & reloc_entry_type_mask) >> reloc_entry_type_shift; + offset = (u32)(entry->entry & reloc_entry_offset_mask) + header->page_base_offset; + + switch (type){ + case image_base_absolute: break; + + case image_base_high: + case image_base_low: + case image_base_highlow: + case image_base_highadj: + case image_base_arm_mov32a: + case image_base_arm_mov32t: + case image_base_mips_jmpaddr16: + result = 0; + goto dll_reloc_end; + + case image_base_dir64: + *(u64*)(img.data + offset) += dif64; + break; + } + } + } + +dll_reloc_end: + return(result); +} + +b32 +dll_load_sections(Data img, DLL_Loaded *loaded, + Data file, DLL_Data *dll){ + COFF_Header *coff_header; + PE_Section_Definition *section_def; + u32 header_size; + u32 size; + u32 i; + + coff_header = dll->coff_header; + section_def = dll->section_defs; + + header_size = + (u32)((byte*)(section_def + coff_header->number_of_sections) - file.data); + + memcpy(img.data, file.data, header_size); + memset(img.data + header_size, 0, img.size - header_size); + + for (i = 0; i < coff_header->number_of_sections; ++i, ++section_def){ + size = section_def->loaded_size; + if (size > section_def->disk_size) + size = section_def->disk_size; + + memcpy(img.data + section_def->loaded_location, + file.data + section_def->disk_location, + size); + + if (dll_compare(section_def->name, ".text", 5) == 0){ + loaded->text_start = section_def->loaded_location; + loaded->text_size = section_def->loaded_size; + } + } + + return(1); +} + +void +dll_load(Data img, DLL_Loaded *loaded, Data file, DLL_Data *dll){ + Data_Directory *export_dir; + + dll_load_sections(img, loaded, file, dll); + loaded->img = img; + + loaded->dos_header = (DOS_Header*)((byte*)img.data + ((byte*)dll->dos_header - file.data)); + loaded->coff_header = (COFF_Header*)((byte*)img.data + ((byte*)dll->coff_header - file.data)); + + loaded->opt_header_32 = (PE_Opt_Header_32Bit*) + ((byte*)img.data + ((byte*)dll->opt_header_32 - file.data)); + loaded->opt_header_64 = (PE_Opt_Header_64Bit*) + ((byte*)img.data + ((byte*)dll->opt_header_64 - file.data)); + + loaded->section_defs = (PE_Section_Definition*) + ((byte*)img.data + ((byte*)dll->section_defs - file.data)); + + loaded->is_64bit = dll->is_64bit; + + if (dll->is_64bit){ + export_dir = dll->opt_header_64->data_directory; + } + else{ + export_dir = dll->opt_header_32->data_directory; + } + export_dir += image_dir_entry_export; + loaded->export_start = export_dir->virtual_address; + + dll_perform_reloc(loaded); +} + +void* +dll_load_function(DLL_Loaded *dll, char *func_name, i32 size){ + Data img; + DLL_Export_Directory_Table *export_dir; + DLL_Export_Address *address_ptr; + DLL_Export_Name *name_ptr; + void *result; + u32 count, i; + u32 result_offset; + u32 ordinal; + + img = dll->img; + export_dir = (DLL_Export_Directory_Table*)(img.data + dll->export_start); + + count = export_dir->number_of_name_pointers; + name_ptr = (DLL_Export_Name*)(img.data + export_dir->name_pointer_offset); + + result = 0; + for (i = 0; i < count; ++i, ++name_ptr){ + if (dll_compare((char*)img.data + name_ptr->name_offset, + func_name, size) == 0){ + ordinal = ((u16*)(img.data + export_dir->ordinal_offset))[i]; +#if 0 + // NOTE(allen): The MS docs say to do this, but + // it appears to just be downright incorrect. + ordinal -= export_dir->ordinal_base; +#endif + address_ptr = (DLL_Export_Address*)(img.data + export_dir->address_offset); + address_ptr += ordinal; + result_offset = address_ptr->export_offset; + result = (img.data + result_offset); + break; + } + } + + return(result); +} + +#define MachineCase(x) case x: result = #x; *len = sizeof(#x) - 1; break + +char* +dll_machine_type_str(u16 machine, i32 *len){ + char *result; + i32 extra; + + if (!len) len = &extra; + result = 0; + + switch (machine){ + MachineCase(intel_i386); + MachineCase(intel_i860); + + MachineCase(mips_r3000); + MachineCase(mips_little_endian); + MachineCase(mips_r10000); + + MachineCase(old_alpha_axp); + MachineCase(alpha_axp); + + MachineCase(hitachi_sh3); + MachineCase(hitachi_sh3_dsp); + MachineCase(hitachi_sh4); + MachineCase(hitachi_sh5); + + MachineCase(arm_little_endian); + MachineCase(thumb); + + MachineCase(matsushita_am33); + MachineCase(power_pc_little_endian); + MachineCase(power_pc_with_floating); + + MachineCase(intel_ia64); + MachineCase(mips16); + MachineCase(motorola_68000_series); + + MachineCase(alpha_axp_64_bit); + + MachineCase(mips_with_fpu); + MachineCase(mips16_with_fpu); + MachineCase(eft_byte_code); + + MachineCase(amd_amd64); + MachineCase(mitsubishi_m32r_little_endian); + MachineCase(clr_pure_msil); + } + + return(result); +} + +#undef MachineCase + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_dll_reader.h b/test_data/lots_of_files/4ed_dll_reader.h new file mode 100644 index 0000000..1ebd68d --- /dev/null +++ b/test_data/lots_of_files/4ed_dll_reader.h @@ -0,0 +1,441 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 20.11.2015 + * + * DLL loader declarations for 4coder + * + */ + +// TOP + +struct DOS_Header { + char signature[2]; + i16 lastsize; + i16 nblocks; + i16 nreloc; + i16 hdrsize; + i16 minalloc; + i16 maxalloc; + i16 ss; + i16 sp; + i16 checksum; + i16 ip; + i16 cs; + i16 relocpos; + i16 noverlay; + i16 reserved1[4]; + i16 oem_id; + i16 oem_info; + i16 reserved2[10]; + i32 e_lfanew; +}; + +enum Target_Machine_Code{ + intel_i386 = 0x14C, + intel_i860 = 0x14D, + + mips_r3000 = 0x162, + mips_little_endian = 0x166, + mips_r10000 = 0x168, + + old_alpha_axp = 0x183, + alpha_axp = 0x184, + + hitachi_sh3 = 0x1a2, + hitachi_sh3_dsp = 0x1a3, + hitachi_sh4 = 0x1a6, + hitachi_sh5 = 0x1a8, + + arm_little_endian = 0x1c0, + thumb = 0x1c2, + + matsushita_am33 = 0x1d3, + power_pc_little_endian = 0x1f0, + power_pc_with_floating = 0x1f1, + + intel_ia64 = 0x200, + mips16 = 0x266, + motorola_68000_series = 0x268, + + alpha_axp_64_bit = 0x284, + + mips_with_fpu = 0x366, + mips16_with_fpu = 0x466, + eft_byte_code = 0xebc, + + amd_amd64 = 0x8664, + mitsubishi_m32r_little_endian = 0x9041, + clr_pure_msil = 0xc0ee +}; + +#define file_is_exe 0x2 +#define file_is_non_reloctable 0x200 +#define file_is_dll 0x2000 + +struct COFF_Header{ + u16 machine; + u16 number_of_sections; + u32 time_date_stamp; + u32 pointer_to_symbol_table; + u32 number_of_symbols; + u16 size_of_optional_header; + u16 characteristics; +}; + +struct Data_Directory{ + u32 virtual_address; + u32 size; +}; + +// This version is untested +struct PE_Opt_Header_32Bit{ + // Universal Portion + i16 signature; + i8 major_linker_version; + i8 minor_linker_version; + i32 size_of_code; + i32 size_of_initialized_data; + i32 size_of_uninitialized_data; + i32 address_of_entry_point; + i32 base_of_code; + i32 base_of_data; + + // Windows Portion + i32 image_base; + i32 section_alignment; + i32 file_alignment; + i16 major_OS_version; + i16 minor_OS_version; + i16 major_image_version; + i16 minor_image_version; + i16 major_subsystem_version; + i16 minor_subsystem_version; + i32 reserved; + i32 size_of_image; + i32 size_of_headers; + i32 checksum; + i16 subsystem; + i16 DLL_characteristics; + i32 size_of_stack_reserve; + i32 size_of_stack_commit; + i32 size_of_heap_reserve; + i32 size_of_heap_commit; + i32 loader_flags; + i32 number_of_rva_and_sizes; + Data_Directory data_directory[16]; +}; + +struct PE_Opt_Header_64Bit{ + // Universal Portion + u16 signature; + u8 major_linker_version; + u8 minor_linker_version; + u32 size_of_code; + u32 size_of_initialized_data; + u32 size_of_uninitialized_data; + u32 address_of_entry_point; + u32 base_of_code; + + // Windows Portion + u64 image_base; + u32 section_alignment; + u32 file_alignment; + u16 major_OS_version; + u16 minor_OS_version; + u16 major_image_version; + u16 minor_image_version; + u16 major_subsystem_version; + u16 minor_subsystem_version; + u32 reserved; + u32 size_of_image; + u32 size_of_headers; + u32 checksum; + u16 subsystem; + u16 DLL_characteristics; + u64 size_of_stack_reserve; + u64 size_of_stack_commit; + u64 size_of_heap_reserve; + u64 size_of_heap_commit; + u32 loader_flags; + u32 number_of_rva_and_sizes; + Data_Directory data_directory[16]; +}; + +#define bitsig_32bit 267 +#define bitsig_64bit 523 + +#define image_dir_entry_export 0 +#define image_dir_entry_import 1 +#define image_dir_entry_resource 2 +#define image_dir_base_reloc_table 5 +#define image_dir_entry_bound_import 11 + +struct PE_Section_Definition{ + char name[8]; + u32 loaded_size; + u32 loaded_location; + u32 disk_size; + u32 disk_location; + u32 disk_relocs; + u32 reserved1; + u16 number_of_relocs; + u16 reserved2; + u32 flags; +}; + +#define image_scn_type_no_pad 0x00000008 +#define image_scn_cnt_code 0x00000020 +#define image_scn_cnt_initialized_data 0x00000040 +#define image_scn_cnt_uninitialized_data 0x00000080 +#define image_scn_lnk_other 0x00000100 +#define image_scn_lnk_info 0x00000200 +#define image_scn_lnk_remove 0x00000800 +#define image_scn_lnk_comdat 0x00001000 +#define image_scn_no_defer_spec_exc 0x00004000 +#define image_scn_gprel 0x00008000 +#define image_scn_mem_fardata 0x00008000 +#define image_scn_mem_purgeable 0x00020000 +#define image_scn_mem_16BIT 0x00020000 +#define image_scn_mem_locked 0x00040000 +#define image_scn_mem_preload 0x00080000 + +#define image_scn_align_1bytes 0x00100000 +#define image_scn_align_2bytes 0x00200000 +#define image_scn_align_4bytes 0x00300000 +#define image_scn_align_8bytes 0x00400000 +#define image_scn_align_16bytes 0x00500000 +#define image_scn_align_32bytes 0x00600000 +#define image_scn_align_64bytes 0x00700000 +#define image_scn_align_128bytes 0x00800000 +#define image_scn_align_256bytes 0x00900000 +#define image_scn_align_512bytes 0x00A00000 +#define image_scn_align_1024bytes 0x00B00000 +#define image_scn_align_2048bytes 0x00C00000 +#define image_scn_align_4096bytes 0x00D00000 +#define image_scn_align_8192bytes 0x00E00000 +#define image_scn_align_mask 0x00F00000 + +#define image_scn_lnk_nreloc_ovfl 0x01000000 +#define image_scn_mem_discardable 0x02000000 +#define image_scn_mem_not_cached 0x04000000 +#define image_scn_mem_not_paged 0x08000000 +#define image_scn_mem_shared 0x10000000 +#define image_scn_mem_execute 0x20000000 +#define image_scn_mem_read 0x40000000 +#define image_scn_mem_write 0x80000000 + +#pragma pack(push, 1) +struct COFF_Relocation{ + u32 virtual_address; + u32 symbol_table_index; + u16 type; +}; +#pragma pack(pop) + +enum Image_Rel_Amd64{ + image_rel_amd64_absolute = 0x00, + image_rel_amd64_addr64 = 0x01, + image_rel_amd64_addr32 = 0x02, + image_rel_amd64_addr32nb = 0x03, + image_rel_amd64_rel32 = 0x04, + image_rel_amd64_rel32_1 = 0x05, + image_rel_amd64_rel32_2 = 0x06, + image_rel_amd64_rel32_3 = 0x07, + image_rel_amd64_rel32_4 = 0x08, + image_rel_amd64_rel32_5 = 0x09, + image_rel_amd64_section = 0x0A, + image_rel_amd64_secrel = 0x0B, + image_rel_amd64_secrel7 = 0x0C, + image_rel_amd64_token = 0x0D, + image_rel_amd64_srel32 = 0x0E, + image_rel_amd64_pair = 0x0F, + image_rel_amd64_sspan32 = 0x10 +}; + +enum Image_Rel_Arm{ + image_rel_arm_absolute = 0x0, + image_rel_arm_addr32 = 0x1, + image_rel_arm_addr32nb = 0x2, + image_rel_arm_branch24 = 0x3, + image_rel_arm_branch11 = 0x4, + image_rel_arm_token = 0x5, + image_rel_arm_blx24 = 0x6, + image_rel_arm_blx11 = 0x7, + image_rel_arm_section = 0x8, + image_rel_arm_secrel = 0x9, + image_rel_arm_mov32a = 0xA, + image_rel_arm_mov32t = 0xB, + image_rel_arm_branch20t = 0xC, + image_rel_arm_branch24t = 0xD, + image_rel_arm_blx32t = 0xE +}; + +enum Image_Rel_Arm64{ + image_rel_arm64_absolute = 0x0, + image_rel_arm64_addr32 = 0x1, + image_rel_arm64_addr32nb = 0x2, + image_rel_arm64_branch26 = 0x3, + image_rel_arm64_pagebase_rel21 = 0x4, + image_rel_arm64_rel21 = 0x5, + image_rel_arm64_pageoffset_12a = 0x6, + image_rel_arm64_pageoffset_12l = 0x7, + image_rel_arm64_secrel = 0x8, + image_rel_arm64_secrel_low12a = 0x9, + image_rel_arm64_secrel_high12a = 0xA, + image_rel_arm64_secrel_low12l = 0xB, + image_rel_arm64_token = 0xC, + image_rel_arm64_section = 0xD, + image_rel_arm64_addr64 = 0xE +}; + +// NOTE(allen): +// skipped Hitachi SuperH +// skiiped IBM PowerPC + +enum Image_Rel_i386{ + image_rel_i386_absolute = 0x0, + image_rel_i386_dir16 = 0x1, + image_rel_i386_rel16 = 0x2, + image_rel_i386_dir32 = 0x3, + image_rel_i386_dir32nb = 0x4, + image_rel_i386_seg12 = 0x5, + image_rel_i386_section = 0x6, + image_rel_i386_secrel = 0x7, + image_rel_i386_token = 0x8, + image_rel_i386_secrel7 = 0x9, + image_rel_i386_rel32 = 0xA +}; + +// NOTE(allen): +// skipped ia64 +// skipped MIPS +// skiiped Mitsubishi + +struct Relocation_Block_Header{ + u32 page_base_offset; + u32 block_size; +}; + +#define reloc_entry_type_mask 0xF000 +#define reloc_entry_type_shift 12 +#define reloc_entry_offset_mask 0x0FFF + +struct Relocation_Block_Entry{ + u16 entry; +}; + +enum DLL_Relocation_Type{ + image_base_absolute, + // nothing + + image_base_high, + // add high 16 bits of diff to 16 bits at offset + + image_base_low, + // add low 16 bits of diff to 16 bits at offset + + image_base_highlow, + // adds all 32 bits to 32 bits at offset + + image_base_highadj, + // consumes two slots: high 16 bits at location, low 16 bits at next location + + image_base_arm_mov32a, + // mips: jump instruction; arm: MOVW+MOVT + + image_base_reserved1, + + image_base_arm_mov32t, + // MOVW+MOVT in Thumb mode + + image_base_reserved2, + + image_base_mips_jmpaddr16, + // mips16 jump instruction + + image_base_dir64 + // adds to 64 bits field +}; + +struct DLL_Data{ + DOS_Header *dos_header; + COFF_Header *coff_header; + PE_Opt_Header_32Bit *opt_header_32; + PE_Opt_Header_64Bit *opt_header_64; + PE_Section_Definition *section_defs; + b32 is_64bit; +}; + +struct DLL_Loaded{ + DOS_Header *dos_header; + COFF_Header *coff_header; + PE_Opt_Header_32Bit *opt_header_32; + PE_Opt_Header_64Bit *opt_header_64; + PE_Section_Definition *section_defs; + b32 is_64bit; + + Data img; + u32 export_start; + u32 text_start; + u32 text_size; +}; + +struct DLL_Export_Directory_Table{ + u32 export_flags; + u32 time_date_stamp; + u16 major_version; + u16 minor_version; + u32 name_offset; + u32 ordinal_base; + u32 number_of_addresses; + u32 number_of_name_pointers; + u32 address_offset; + u32 name_pointer_offset; + u32 ordinal_offset; +}; + +struct DLL_Export_Address{ + u32 export_offset; +}; + +struct DLL_Export_Name{ + u32 name_offset; +}; + +struct DLL_Export_Ordinal{ + u16 ordinal; +}; + +struct DLL_Debug_Entry{ + u32 characteristics; + u32 time_date_stamp; + u16 major_version; + u16 minor_version; + u32 type; + u32 size_of_data; + u32 offset_of_data; + u32 disk_offset_of_data; +} thingy; + +enum DLL_Debug_Type{ + img_dbg_type_unknown, + img_dbg_type_coff, + img_dbg_type_codeview, + img_dbg_type_fpo, + img_dbg_type_misc, + img_dbg_type_exception, + img_dbg_type_fixup, + img_dbg_type_omap_to_src, + img_dbg_type_omap_from_src +}; + +char DOS_error_message[] = "This program cannot be run in DOS mode."; +i32 DOS_error_offset = 0x4E; +i32 DOS_error_size = sizeof(DOS_error_message) - 1; + +char PE_header[] = {'P', 'E', 0, 0}; +i32 PE_header_size = 4; + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_exchange.cpp b/test_data/lots_of_files/4ed_exchange.cpp new file mode 100644 index 0000000..7908cac --- /dev/null +++ b/test_data/lots_of_files/4ed_exchange.cpp @@ -0,0 +1,234 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 9.12.2015 + * + * Exchange stuff + * + */ + +// TOP + +internal void +ex__file_insert(File_Slot *pos, File_Slot *file){ + pos->next->prev = file; + file->next = pos->next; + pos->next = file; + file->prev = pos; +} + +internal void +ex__file_remove(File_Slot *file){ + file->next->prev = file->prev; + file->prev->next = file->next; +} + +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 +ex__clear(File_Slot *file){ + file->data = 0; + file->size = 0; + file->max = 0; + file->flags = 0; +} + +internal File_Slot* +ex__get_file(Exchange *exchange){ + File_Exchange *files = &exchange->file; + File_Slot *file; + + ++files->num_active; + + file = files->available.next; + ex__file_remove(file); + ex__clear(file); + ex__file_insert(&files->active, file); + ex__check(files); + + return file; +} + +internal void +ex__set_filename(File_Slot *file, char *filename, int len){ + memcpy(file->filename, filename, len); + file->filename[len] = 0; + file->filename_len = len; +} + +internal i32 +exchange_request_file(Exchange *exchange, char *filename, int len){ + File_Exchange *files = &exchange->file; + i32 result = 0; + + if (len+1 < FileNameMax){ + if (files->num_active < files->max){ + File_Slot *file = ex__get_file(exchange); + ex__set_filename(file, filename, len); + + file->flags |= FEx_Request; + result = (int)(file - files->files) + 1; + } + } + + return result; +} + +internal b32 +exchange_file_ready(Exchange *exchange, i32 file_id, byte **data, int *size, int *max){ + File_Exchange *files = &exchange->file; + b32 result = 0; + + if (file_id > 0 && file_id <= files->max){ + File_Slot *file = files->files + file_id - 1; + if (file->flags & FEx_Ready){ + *data = file->data; + *size = file->size; + *max = file->max; + result = 1; + } + if (file->flags & FEx_Not_Exist){ + *data = 0; + *size = 0; + *max = 0; + result = 1; + } + } + + return result; +} + +internal b32 +exchange_file_does_not_exist(Exchange *exchange, i32 file_id){ + File_Exchange *files = &exchange->file; + b32 result = 1; + File_Slot *slot; + + if (file_id > 0 && file_id <= files->max){ + slot = files->files + file_id - 1; + if (!(slot->flags & FEx_Not_Exist)){ + result = 0; + } + } + + return result; +} + +internal i32 +exchange_save_file(Exchange *exchange, char *filename, int len, + byte *data, int size, int max){ + File_Exchange *files = &exchange->file; + i32 result = 0; + + if (len+1 < FileNameMax){ + if (files->num_active < files->max){ + File_Slot *file = ex__get_file(exchange); + ex__set_filename(file, filename, len); + + file->flags |= FEx_Save; + file->data = data; + file->size = size; + file->max = max; + + result = (int)(file - files->files) + 1; + } + } + + return result; +} + +internal b32 +exchange_file_save_complete(Exchange *exchange, i32 file_id, byte **data, int *size, int *max, int *failed){ + File_Exchange *files = &exchange->file; + b32 result = 0; + + if (file_id > 0 && file_id <= files->max){ + File_Slot *file = files->files + file_id - 1; + if (file->flags & FEx_Save_Complete || file->flags & FEx_Save_Failed){ + *data = file->data; + *size = file->size; + *max = file->max; + result = 1; + + *failed = (file->flags & FEx_Save_Complete)?(1):(0); + } + } + + return result; +} + +internal char* +exchange_file_filename(Exchange *exchange, i32 file_id, i32 *size = 0){ + File_Exchange *files = &exchange->file; + char *result = 0; + + if (file_id > 0 && file_id <= files->max){ + File_Slot *file = files->files + file_id - 1; + result = file->filename; + if (size) *size = file->filename_len; + } + + return result; +} + +internal void +exchange_free_file(Exchange *exchange, i32 file_id){ + File_Exchange *files = &exchange->file; + + if (file_id > 0 && file_id <= files->max){ + File_Slot *file = files->files + file_id - 1; + ex__file_remove(file); + ex__file_insert(&files->free_list, file); + ex__check(files); + } +} + +internal void +exchange_clear_file(Exchange *exchange, i32 file_id){ + File_Exchange *files = &exchange->file; + + if (file_id > 0 && file_id <= files->max){ + File_Slot *file = files->files + file_id - 1; + ex__clear(file); + } +} + +internal b32 +queue_job_is_pending(Work_Queue *queue, u32 job_id){ + b32 result; + u32 job_index; + Full_Job_Data *full_job; + + job_index = job_id % QUEUE_WRAP; + full_job = queue->jobs + job_index; + + Assert(full_job->id == job_id); + + result = 0; + if (full_job->running_thread != 0){ + result = 1; + } + + return(result); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_file.cpp b/test_data/lots_of_files/4ed_file.cpp new file mode 100644 index 0000000..bb474a7 --- /dev/null +++ b/test_data/lots_of_files/4ed_file.cpp @@ -0,0 +1,530 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 20.02.2016 + * + * File editing view for 4coder + * + */ + +// TOP + +#include "buffer/4coder_shared.cpp" + +#if BUFFER_EXPERIMENT_SCALPEL == 0 +#include "buffer/4coder_golden_array.cpp" +#define Buffer_Type Buffer +#elif BUFFER_EXPERIMENT_SCALPEL == 1 +#include "buffer/4coder_gap_buffer.cpp" +#define Buffer_Type Gap_Buffer +#elif BUFFER_EXPERIMENT_SCALPEL == 2 +#include "buffer/4coder_multi_gap_buffer.cpp" +#define Buffer_Type Multi_Gap_Buffer +#else +#include "buffer/4coder_rope_buffer.cpp" +#define Buffer_Type Rope_Buffer +#endif + +#include "buffer/4coder_buffer_abstract.cpp" + +enum Edit_Type{ + ED_NORMAL, + ED_REVERSE_NORMAL, + ED_UNDO, + ED_REDO, +}; + +struct Edit_Step{ + Edit_Type type; + union{ + struct{ + b32 can_merge; + Buffer_Edit edit; + i32 pre_pos; + i32 post_pos; + i32 next_block, prev_block; + }; + struct{ + i32 first_child; + i32 inverse_first_child; + i32 inverse_child_count; + i32 special_type; + }; + }; + i32 child_count; +}; + +struct Edit_Stack{ + u8 *strings; + i32 size, max; + + Edit_Step *edits; + i32 edit_count, edit_max; +}; + +struct Small_Edit_Stack{ + u8 *strings; + i32 size, max; + + Buffer_Edit *edits; + i32 edit_count, edit_max; +}; + +struct Undo_Data{ + Edit_Stack undo; + Edit_Stack redo; + Edit_Stack history; + Small_Edit_Stack children; + + i32 history_block_count, history_head_block; + i32 edit_history_cursor; + b32 current_block_normal; +}; + +struct Text_Effect{ + i32 start, end; + u32 color; + i32 tick_down, tick_max; +}; + +// NOTE(allen): The Editing_File struct is now divided into two +// parts. Variables in the Settings part can be set even when the +// file is still streaming in, and all operations except for the +// initial allocation of the file. +struct Editing_File_Settings{ + i32 base_map_id; + i32 dos_write_mode; + b32 unwrapped_lines; + b8 tokens_exist; + b8 is_initialized; + b8 unimportant; + b8 read_only; +}; + +// NOTE(allen): This part of the Editing_File is cleared whenever +// the contents of the file is set. +struct Editing_File_State{ + b32 is_dummy; + b32 is_loading; + + i16 font_id; + Buffer_Type buffer; + + i32 cursor_pos; + + Undo_Data undo; + + Cpp_Token_Stack token_stack; + Cpp_Token_Stack swap_stack; + u32 lex_job; + b32 tokens_complete; + b32 still_lexing; + + Text_Effect paste_effect; + + u64 last_4ed_write_time; + u64 last_4ed_edit_time; + u64 last_sys_write_time; +}; + +struct Editing_File_Preload{ + i32 start_line; +}; + +struct Editing_File_Name{ + char live_name_[256]; + String live_name; + + char source_path_[256]; + char extension_[16]; + String source_path; + String extension; +}; + +struct File_Node{ + File_Node *next, *prev; +}; + +struct Editing_File{ + File_Node node; + Editing_File_Settings settings; + union{ + Editing_File_State state; + Editing_File_Preload preload; + }; + Editing_File_Name name; +}; + +struct File_Table_Entry{ + String name; + u32 hash; + i32 id; +}; + +struct File_Table{ + File_Table_Entry *table; + i32 count, max; +}; + +internal u32 +get_file_hash(String name){ + u32 x = 5381; + int i = 0; + char c; + while (i < name.size){ + c = name.str[i++]; + x = ((x << 5) + x) + c; + } + return x; +} + +internal b32 +table_add(File_Table *table, String name, i32 id){ + Assert(table->count * 3 < table->max * 2); + + File_Table_Entry entry, e; + i32 i; + + entry.name = name; + entry.id = id; + entry.hash = get_file_hash(name); + i = entry.hash % table->max; + while ((e = table->table[i]).name.str){ + if (e.hash == entry.hash && match(e.name, entry.name)){ + return 1; + } + i = (i + 1) % table->max; + } + table->table[i] = entry; + ++table->count; + + return 0; +} + +internal b32 +table_find_pos(File_Table *table, String name, i32 *index){ + File_Table_Entry e; + i32 i; + u32 hash; + + hash = get_file_hash(name); + i = hash % table->max; + while ((e = table->table[i]).name.size){ + if (e.name.str && e.hash == hash && match(e.name, name)){ + *index = i; + return 1; + } + i = (i + 1) % table->max; + } + + return 0; +} + +inline b32 +table_find(File_Table *table, String name, i32 *id){ + i32 pos; + b32 r = table_find_pos(table, name, &pos); + if (r) *id = table->table[pos].id; + return r; +} + +inline b32 +table_remove(File_Table *table, String name){ + i32 pos; + b32 r = table_find_pos(table, name, &pos); + if (r){ + table->table[pos].name.str = 0; + --table->count; + } + return r; +} + +struct Working_Set{ + Editing_File *files; + i32 file_count, file_max; + File_Node free_sentinel; + File_Node used_sentinel; + + File_Table table; + + String clipboards[64]; + i32 clipboard_size, clipboard_max_size; + i32 clipboard_current, clipboard_rolling; +}; + +// Hot Directory + +struct Hot_Directory{ + String string; + File_List file_list; + char slash; +}; + +internal void +hot_directory_clean_end(Hot_Directory *hot_directory){ + String *str = &hot_directory->string; + if (str->size != 0 && str->str[str->size-1] != hot_directory->slash){ + str->size = reverse_seek_slash(*str) + 1; + str->str[str->size] = 0; + } +} + +internal i32 +hot_directory_quick_partition(File_Info *infos, i32 start, i32 pivot){ + File_Info *p = infos + pivot; + File_Info *a = infos + start; + for (i32 i = start; i < pivot; ++i, ++a){ + i32 comp = 0; + comp = p->folder - a->folder; + if (comp == 0) comp = compare(a->filename, p->filename); + if (comp < 0){ + Swap(*a, infos[start]); + ++start; + } + } + Swap(*p, infos[start]); + return start; +} + +internal void +hot_directory_quick_sort(File_Info *infos, i32 start, i32 pivot){ + i32 mid = hot_directory_quick_partition(infos, start, pivot); + if (start < mid-1) hot_directory_quick_sort(infos, start, mid-1); + if (mid+1 < pivot) hot_directory_quick_sort(infos, mid+1, pivot); +} + +inline void +hot_directory_fixup(Hot_Directory *hot_directory, Working_Set *working_set){ + File_List *files = &hot_directory->file_list; + if (files->count >= 2) + hot_directory_quick_sort(files->infos, 0, files->count - 1); +} + +inline void +hot_directory_set(System_Functions *system, Hot_Directory *hot_directory, + String str, Working_Set *working_set){ + b32 success = copy_checked(&hot_directory->string, str); + terminate_with_null(&hot_directory->string); + if (success){ + if (str.size > 0){ + system->set_file_list(&hot_directory->file_list, str); + } + else{ + system->set_file_list(&hot_directory->file_list, make_string((char*)1, 0)); + } + } + hot_directory_fixup(hot_directory, working_set); +} + +inline void +hot_directory_reload(System_Functions *system, Hot_Directory *hot_directory, Working_Set *working_set){ + system->set_file_list(&hot_directory->file_list, hot_directory->string); + hot_directory_fixup(hot_directory, working_set); +} + +internal void +hot_directory_init(Hot_Directory *hot_directory, String base, String dir, char slash){ + hot_directory->string = base; + hot_directory->string.str[255] = 0; + hot_directory->string.size = 0; + copy(&hot_directory->string, dir); + append(&hot_directory->string, slash); + hot_directory->slash = slash; +} + +struct Hot_Directory_Match{ + String filename; + b32 is_folder; +}; + +internal b32 +filename_match(String query, Absolutes *absolutes, String filename, b32 case_sensitive){ + b32 result; + result = (query.size == 0); + replace_char(query, '\\', '/'); + replace_char(filename, '\\', '/'); + if (!result) result = wildcard_match(absolutes, filename, case_sensitive); + return result; +} + +internal Hot_Directory_Match +hot_directory_first_match(Hot_Directory *hot_directory, + String str, + b32 include_files, + b32 exact_match, + b32 case_sensitive){ + Hot_Directory_Match result = {}; + + replace_char(str, '\\', '/'); + + Absolutes absolutes; + if (!exact_match) + get_absolutes(str, &absolutes, 1, 1); + + File_List *files = &hot_directory->file_list; + File_Info *info, *end; + end = files->infos + files->count; + for (info = files->infos; info != end; ++info){ + String filename = info->filename; + b32 is_match = 0; + if (exact_match){ + if (case_sensitive){ + if (match(filename, str)) is_match = 1; + } + else{ + if (match_unsensitive(filename, str)) is_match = 1; + } + } + else{ + if (filename_match(str, &absolutes, filename, case_sensitive)) is_match = 1; + } + + if (is_match){ + result.is_folder = info->folder; + result.filename = filename; + break; + } + } + + return result; +} + +enum File_Sync_State{ + SYNC_GOOD, + SYNC_BEHIND_OS, + SYNC_UNSAVED +}; + +inline File_Sync_State +buffer_get_sync(Editing_File *file){ + File_Sync_State result = SYNC_GOOD; + if (file->state.last_4ed_write_time != file->state.last_sys_write_time) + result = SYNC_BEHIND_OS; + else if (file->state.last_4ed_edit_time > file->state.last_sys_write_time) + result = SYNC_UNSAVED; + return result; +} + +inline b32 +buffer_needs_save(Editing_File *file){ + b32 result = 0; + if (file->settings.unimportant == 0) + if (buffer_get_sync(file) == SYNC_UNSAVED) + result = 1; + return(result); +} + +inline b32 +file_is_ready(Editing_File *file){ + b32 result = 0; + if (file && file->state.is_loading == 0){ + result = 1; + } + return(result); +} + +inline Editing_File* +working_set_contains(Working_Set *working, String filename){ + Editing_File *result = 0; + i32 id; + replace_char(filename, '\\', '/'); + if (table_find(&working->table, filename, &id)){ + if (id >= 0 && id <= working->file_max){ + result = working->files + id; + } + } + return (result); +} + +// TODO(allen): Pick better first options. +internal Editing_File* +working_set_lookup_file(Working_Set *working_set, String string){ + Editing_File *file = 0; + + replace_char(string, '\\', '/'); + + { + File_Node *node, *used_nodes; + used_nodes = &working_set->used_sentinel; + for (dll_items(node, used_nodes)){ + file = (Editing_File*)node; + if (string.size == 0 || match(string, file->name.live_name)){ + break; + } + } + if (node == used_nodes) file = 0; + } + + if (!file){ + File_Node *node, *used_nodes; + used_nodes = &working_set->used_sentinel; + for (dll_items(node, used_nodes)){ + file = (Editing_File*)node; + if (string.size == 0 || has_substr(file->name.live_name, string)){ + break; + } + } + if (node == used_nodes) file = 0; + } + + return (file); +} + +struct Get_File_Result{ + Editing_File *file; + i32 index; +}; + +internal Get_File_Result +working_set_get_available_file(Working_Set *working_set){ + Get_File_Result result = {}; + File_Node *node; + + if (working_set->file_count < working_set->file_max){ + node = working_set->free_sentinel.next; + Assert(node != &working_set->free_sentinel); + + result.file = (Editing_File*)node; + result.index = (i32)(result.file - working_set->files); + + ++working_set->file_count; + + dll_remove(node); + *result.file = {}; + dll_insert(&working_set->used_sentinel, node); + } + + return result; +} + +inline void +working_set_free_file(Working_Set *working_set, Editing_File *file){ + file->state.is_dummy = 1; + dll_remove(&file->node); + dll_insert(&working_set->free_sentinel, &file->node); + --working_set->file_count; +} + +inline Get_File_Result +working_set_get_file(Working_Set *working_set, i32 id, b32 require_active){ + Get_File_Result result = {}; + if (id > 0 && id <= working_set->file_max){ + result.file = working_set->files + id; + result.index = id; + if (result.file->state.is_dummy && require_active){ + result.file = 0; + result.index = 0; + } + } + return(result); +} + +inline void +file_set_to_loading(Editing_File *file){ + file->state = {}; + file->settings = {}; + file->state.is_loading = 1; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_file_view.cpp b/test_data/lots_of_files/4ed_file_view.cpp new file mode 100644 index 0000000..cb1ba82 --- /dev/null +++ b/test_data/lots_of_files/4ed_file_view.cpp @@ -0,0 +1,4164 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 19.08.2015 + * + * File editing view for 4coder + * + */ + +// TOP + +enum Interactive_Action{ + IAct_Open, + IAct_Save_As, + IAct_New, + IAct_Switch, + IAct_Kill, + IAct_Sure_To_Kill +}; + +enum Interactive_Interaction{ + IInt_Sys_File_List, + IInt_Live_File_List, + IInt_Sure_To_Kill +}; + +struct View_Mode{ + i32 rewrite; +}; + +enum View_Widget_Type{ + FWIDG_NONE, + FWIDG_TIMELINES, + // never below this + FWIDG_TYPE_COUNT +}; + +struct View_Widget{ + UI_State state; + View_Widget_Type type; + i32 height_; + struct{ + b32 undo_line; + b32 history_line; + } timeline; +}; + +enum View_UI{ + VUI_None, + VUI_Theme, + VUI_Interactive, + VUI_Menu, + VUI_Config, +}; + +enum Color_View_Mode{ + CV_Mode_Library, + CV_Mode_Import_File, + CV_Mode_Export_File, + CV_Mode_Import, + CV_Mode_Export, + CV_Mode_Import_Wait, + CV_Mode_Adjusting +}; + +struct View{ + View *next, *prev; + b32 in_use; + i32 id; + + Models *models; + + Panel *panel; + Command_Map *map; + + Editing_File *file; + + UI_State ui_state; + View_UI showing_ui; + + // interactive stuff + Interactive_Interaction interaction; + Interactive_Action action; + b32 finished; + char query_[256]; + char dest_[256]; + String query; + String dest; + i32 user_action; + + // theme stuff + View *hot_file_view; + u32 *palette; + i32 palette_size; + Color_View_Mode color_mode; + Super_Color color; + Color_Highlight highlight; + b32 p4c_only; + Style_Library inspecting_styles; + b8 import_export_check[64]; + i32 import_file_id; + + // file stuff + i32 font_advance; + i32 font_height; + + Full_Cursor cursor; + i32 mark; + f32 scroll_y, target_y, prev_target_y; + f32 scroll_x, target_x, prev_target_x; + f32 preferred_x; + i32 scroll_i; + f32 scroll_min_limit; + + Full_Cursor temp_highlight; + i32 temp_highlight_end_pos; + b32 show_temp_highlight; + + View_Mode mode, next_mode; + View_Widget widget; + Query_Set query_set; + i32 scrub_max; + + b32 unwrapped_lines; + b32 show_whitespace; + b32 file_locked; + + i32 line_count, line_max; + f32 *line_wrap_y; + + Command_Map *map_for_file; + b32 reinit_scrolling; +}; + +struct View_And_ID{ + View *view; + i32 id; +}; + +#define LockLevel_Open 0 +#define LockLevel_NoWrite 1 +#define LockLevel_NoUpdate 2 + +inline i32 +view_lock_level(View *view){ + i32 result = LockLevel_Open; + if (view->showing_ui != VUI_None) result = LockLevel_NoUpdate; + else if (view->file_locked || + (view->file && view->file->settings.read_only)) result = LockLevel_NoWrite; + return(result); +} + +inline f32 +view_compute_width(View *view){ + Panel *panel = view->panel; + return (f32)(panel->inner.x1 - panel->inner.x0); +} + +inline f32 +view_compute_height(View *view){ + Panel *panel = view->panel; + return (f32)(panel->inner.y1 - panel->inner.y0); +} + +struct View_Iter{ + View *view; + + Editing_File *file; + View *skip; + Panel *used_panels; + Panel *panel; +}; + +internal View_Iter +file_view_iter_next(View_Iter iter){ + View *file_view; + + for (iter.panel = iter.panel->next; iter.panel != iter.used_panels; iter.panel = iter.panel->next){ + file_view = iter.panel->view; + if (file_view != iter.skip && (file_view->file == iter.file || iter.file == 0)){ + iter.view = file_view; + break; + } + } + + return(iter); +} + +internal View_Iter +file_view_iter_init(Editing_Layout *layout, Editing_File *file, View *skip){ + View_Iter result; + result.used_panels = &layout->used_sentinel; + result.panel = result.used_panels; + result.file = file; + result.skip = skip; + + result = file_view_iter_next(result); + + return(result); +} + +internal b32 +file_view_iter_good(View_Iter iter){ + b32 result = (iter.panel != iter.used_panels); + return(result); +} + +inline b32 +starts_new_line(u8 character){ + return (character == '\n'); +} + +inline void +file_init_strings(Editing_File *file){ + file->name.source_path = make_fixed_width_string(file->name.source_path_); + file->name.live_name = make_fixed_width_string(file->name.live_name_); + file->name.extension = make_fixed_width_string(file->name.extension_); +} + +inline void +file_set_name(Working_Set *working_set, Editing_File *file, char *filename){ + String f, ext; + + Assert(file->name.live_name.str != 0); + + f = make_string_slowly(filename); + copy_checked(&file->name.source_path, f); + + file->name.live_name.size = 0; + get_front_of_directory(&file->name.live_name, f); + + if (file->name.source_path.size == file->name.live_name.size){ + file->name.extension.size = 0; + } + else{ + ext = file_extension(f); + copy(&file->name.extension, ext); + } + + { + File_Node *node, *used_nodes; + Editing_File *file_ptr; + i32 file_x, original_len; + b32 hit_conflict; + + used_nodes = &working_set->used_sentinel; + original_len = file->name.live_name.size; + hit_conflict = 1; + file_x = 0; + while (hit_conflict){ + hit_conflict = 0; + for (dll_items(node, used_nodes)){ + file_ptr = (Editing_File*)node; + if (file_ptr != file && file_is_ready(file_ptr)){ + if (match(file->name.live_name, file_ptr->name.live_name)){ + ++file_x; + hit_conflict = 1; + break; + } + } + } + + if (hit_conflict){ + file->name.live_name.size = original_len; + append(&file->name.live_name, " <"); + append_int_to_str(file_x, &file->name.live_name); + append(&file->name.live_name, ">"); + } + } + } +} + +inline void +file_synchronize_times(System_Functions *system, Editing_File *file, char *filename){ + u64 stamp = system->file_time_stamp(filename); + if (stamp > 0){ + file->state.last_4ed_write_time = stamp; + file->state.last_4ed_edit_time = stamp; + file->state.last_sys_write_time = stamp; + } +} + +internal i32 +file_save(System_Functions *system, Exchange *exchange, Mem_Options *mem, + Editing_File *file, char *filename){ + i32 result = 0; + + i32 max, size; + b32 dos_write_mode = file->settings.dos_write_mode; + char *data; + Buffer_Type *buffer = &file->state.buffer; + + if (dos_write_mode) + max = buffer_size(buffer) + buffer->line_count + 1; + else + max = buffer_size(buffer); + + data = (char*)general_memory_allocate(&mem->general, max, 0); + Assert(data); + + if (dos_write_mode) + size = buffer_convert_out(buffer, data, max); + else + buffer_stringify(buffer, 0, size = max, data); + + result = exchange_save_file(exchange, filename, str_size(filename), (byte*)data, size, max); + + if (result == 0){ + general_memory_free(&mem->general, data); + } + + file_synchronize_times(system, file, filename); + + return(result); +} + +inline b32 +file_save_and_set_names(System_Functions *system, Exchange *exchange, + Mem_Options *mem, Working_Set *working_set, Editing_File *file, + char *filename){ + b32 result = 0; + result = file_save(system, exchange, mem, file, filename); + if (result){ + file_set_name(working_set, file, filename); + } + return result; +} + +enum File_Bubble_Type{ + BUBBLE_BUFFER = 1, + BUBBLE_STARTS, + BUBBLE_WIDTHS, + BUBBLE_WRAPS, + BUBBLE_TOKENS, + BUBBLE_UNDO_STRING, + BUBBLE_UNDO, + BUBBLE_UNDO_CHILDREN, + // + FILE_BUBBLE_TYPE_END, +}; + +#define GROW_FAILED 0 +#define GROW_NOT_NEEDED 1 +#define GROW_SUCCESS 2 + +internal i32 +file_grow_starts_widths_as_needed(General_Memory *general, Buffer_Type *buffer, i32 additional_lines){ + b32 result = GROW_NOT_NEEDED; + i32 max = buffer->line_max; + i32 count = buffer->line_count; + i32 target_lines = count + additional_lines; + Assert(max == buffer->widths_max); + + if (target_lines > max || max == 0){ + max = LargeRoundUp(target_lines + max, Kbytes(1)); + + f32 *new_widths = (f32*)general_memory_reallocate( + general, buffer->line_widths, + sizeof(f32)*count, sizeof(f32)*max, BUBBLE_WIDTHS); + + i32 *new_lines = (i32*)general_memory_reallocate( + general, buffer->line_starts, + sizeof(i32)*count, sizeof(i32)*max, BUBBLE_STARTS); + + if (new_lines){ + buffer->line_starts = new_lines; + buffer->line_max = max; + } + if (new_widths){ + buffer->line_widths = new_widths; + buffer->widths_max = max; + } + if (new_lines && new_widths){ + result = GROW_SUCCESS; + } + else{ + result = GROW_FAILED; + } + } + + return(result); +} + +internal void +file_measure_starts_widths(System_Functions *system, General_Memory *general, + Buffer_Type *buffer, float *advance_data){ + ProfileMomentFunction(); + if (!buffer->line_starts){ + i32 max = buffer->line_max = Kbytes(1); + buffer->line_starts = (i32*)general_memory_allocate(general, max*sizeof(i32), BUBBLE_STARTS); + TentativeAssert(buffer->line_starts); + // TODO(allen): when unable to allocate? + } + if (!buffer->line_widths){ + i32 max = buffer->widths_max = Kbytes(1); + buffer->line_widths = (f32*)general_memory_allocate(general, max*sizeof(f32), BUBBLE_STARTS); + TentativeAssert(buffer->line_starts); + // TODO(allen): when unable to allocate? + } + + Buffer_Measure_Starts state = {}; + while (buffer_measure_starts_widths(&state, buffer, advance_data)){ + i32 count = state.count; + i32 max = buffer->line_max; + max = ((max + 1) << 1); + + { + i32 *new_lines = (i32*)general_memory_reallocate( + general, buffer->line_starts, sizeof(i32)*count, sizeof(i32)*max, BUBBLE_STARTS); + + // TODO(allen): when unable to grow? + TentativeAssert(new_lines); + buffer->line_starts = new_lines; + buffer->line_max = max; + } + + { + f32 *new_lines = (f32*) + general_memory_reallocate(general, buffer->line_widths, + sizeof(f32)*count, sizeof(f32)*max, BUBBLE_WIDTHS); + + // TODO(allen): when unable to grow? + TentativeAssert(new_lines); + buffer->line_widths = new_lines; + buffer->widths_max = max; + } + + } + buffer->line_count = state.count; + buffer->widths_count = state.count; +} + +struct Opaque_Font_Advance{ + void *data; + int stride; +}; + +inline Opaque_Font_Advance +get_opaque_font_advance(Render_Font *font){ + Opaque_Font_Advance result; + result.data = (char*)font->chardata + OffsetOfPtr(font->chardata, xadvance); + result.stride = sizeof(*font->chardata); + return result; +} + +internal void +file_remeasure_widths_(System_Functions *system, + General_Memory *general, Buffer_Type *buffer, Render_Font *font, + i32 line_start, i32 line_end, i32 line_shift){ + ProfileMomentFunction(); + file_grow_starts_widths_as_needed(general, buffer, line_shift); + buffer_remeasure_widths(buffer, font->advance_data, line_start, line_end, line_shift); +} + +inline i32 +view_wrapped_line_span(f32 line_width, f32 max_width){ + i32 line_count = CEIL32(line_width / max_width); + if (line_count == 0) line_count = 1; + return line_count; +} + +internal i32 +view_compute_lowest_line(View *view){ + i32 lowest_line = 0; + i32 last_line = view->line_count - 1; + if (last_line > 0){ + if (view->unwrapped_lines){ + lowest_line = last_line; + } + else{ + f32 wrap_y = view->line_wrap_y[last_line]; + lowest_line = FLOOR32(wrap_y / view->font_height); + f32 max_width = view_compute_width(view); + + Editing_File *file = view->file; + Assert(!file->state.is_dummy); + f32 width = file->state.buffer.line_widths[last_line]; + i32 line_span = view_wrapped_line_span(width, max_width); + lowest_line += line_span - 1; + } + } + return lowest_line; +} + +internal void +view_measure_wraps(System_Functions *system, + General_Memory *general, View *view){ + ProfileMomentFunction(); + Buffer_Type *buffer; + + buffer = &view->file->state.buffer; + i32 line_count = buffer->line_count; + + if (view->line_max < line_count){ + i32 max = view->line_max = LargeRoundUp(line_count, Kbytes(1)); + if (view->line_wrap_y){ + view->line_wrap_y = (real32*) + general_memory_reallocate_nocopy(general, view->line_wrap_y, sizeof(real32)*max, BUBBLE_WRAPS); + } + else{ + view->line_wrap_y = (real32*) + general_memory_allocate(general, sizeof(real32)*max, BUBBLE_WRAPS); + } + } + + f32 line_height = (f32)view->font_height; + f32 max_width = view_compute_width(view); + buffer_measure_wrap_y(buffer, view->line_wrap_y, line_height, max_width); + + view->line_count = line_count; +} + +internal void* +alloc_for_buffer(void *context, int *size){ + *size = LargeRoundUp(*size, Kbytes(4)); + void *data = general_memory_allocate((General_Memory*)context, *size, BUBBLE_BUFFER); + return data; +} + +internal void +file_create_from_string(System_Functions *system, Models *models, + Editing_File *file, char *filename, String val, b8 read_only = 0){ + + Font_Set *font_set = models->font_set; + Working_Set *working_set = &models->working_set; + General_Memory *general = &models->mem.general; + Partition *part = &models->mem.part; + Buffer_Init_Type init; + i32 page_size, scratch_size, init_success; + + file->state = {}; + + init = buffer_begin_init(&file->state.buffer, val.str, val.size); + for (; buffer_init_need_more(&init); ){ + page_size = buffer_init_page_size(&init); + page_size = LargeRoundUp(page_size, Kbytes(4)); + if (page_size < Kbytes(4)) page_size = Kbytes(4); + void *data = general_memory_allocate(general, page_size, BUBBLE_BUFFER); + buffer_init_provide_page(&init, data, page_size); + } + + scratch_size = partition_remaining(part); + Assert(scratch_size > 0); + init_success = buffer_end_init(&init, part->base + part->pos, scratch_size); + AllowLocal(init_success); + Assert(init_success); + + if (buffer_size(&file->state.buffer) < val.size){ + file->settings.dos_write_mode = 1; + } + + file_init_strings(file); + file_set_name(working_set, file, (char*)filename); + + file->state.font_id = models->global_font.font_id; + + file_synchronize_times(system, file, filename); + + Render_Font *font = get_font_info(font_set, file->state.font_id)->font; + float *advance_data = 0; + if (font) advance_data = font->advance_data; + file_measure_starts_widths(system, general, &file->state.buffer, advance_data); + + file->settings.read_only = read_only; + if (!read_only){ + i32 request_size = Kbytes(64); + file->state.undo.undo.max = request_size; + file->state.undo.undo.strings = (u8*)general_memory_allocate(general, request_size, BUBBLE_UNDO_STRING); + file->state.undo.undo.edit_max = request_size / sizeof(Edit_Step); + file->state.undo.undo.edits = (Edit_Step*)general_memory_allocate(general, request_size, BUBBLE_UNDO); + + file->state.undo.redo.max = request_size; + file->state.undo.redo.strings = (u8*)general_memory_allocate(general, request_size, BUBBLE_UNDO_STRING); + file->state.undo.redo.edit_max = request_size / sizeof(Edit_Step); + file->state.undo.redo.edits = (Edit_Step*)general_memory_allocate(general, request_size, BUBBLE_UNDO); + + file->state.undo.history.max = request_size; + file->state.undo.history.strings = (u8*)general_memory_allocate(general, request_size, BUBBLE_UNDO_STRING); + file->state.undo.history.edit_max = request_size / sizeof(Edit_Step); + file->state.undo.history.edits = (Edit_Step*)general_memory_allocate(general, request_size, BUBBLE_UNDO); + + file->state.undo.children.max = request_size; + file->state.undo.children.strings = (u8*)general_memory_allocate(general, request_size, BUBBLE_UNDO_STRING); + file->state.undo.children.edit_max = request_size / sizeof(Buffer_Edit); + file->state.undo.children.edits = (Buffer_Edit*)general_memory_allocate(general, request_size, BUBBLE_UNDO); + + file->state.undo.history_block_count = 1; + file->state.undo.history_head_block = 0; + file->state.undo.current_block_normal = 1; + } +} + +internal b32 +file_create_empty(System_Functions *system, + Models *models, Editing_File *file, char *filename){ + + file_create_from_string(system, models, file, filename, {}); + return (1); +} + +internal b32 +file_create_read_only(System_Functions *system, + Models *models, Editing_File *file, char *filename){ + + file_create_from_string(system, models, file, filename, {}, 1); + return (1); +} + +internal void +file_close(System_Functions *system, General_Memory *general, Editing_File *file){ + if (file->state.still_lexing){ + system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); + if (file->state.swap_stack.tokens){ + general_memory_free(general, file->state.swap_stack.tokens); + file->state.swap_stack.tokens = 0; + } + } + if (file->state.token_stack.tokens){ + general_memory_free(general, file->state.token_stack.tokens); + } + + Buffer_Type *buffer = &file->state.buffer; + if (buffer->data){ + general_memory_free(general, buffer->data); + general_memory_free(general, buffer->line_starts); + general_memory_free(general, buffer->line_widths); + } + + if (file->state.undo.undo.edits){ + general_memory_free(general, file->state.undo.undo.strings); + general_memory_free(general, file->state.undo.undo.edits); + + general_memory_free(general, file->state.undo.redo.strings); + general_memory_free(general, file->state.undo.redo.edits); + + general_memory_free(general, file->state.undo.history.strings); + general_memory_free(general, file->state.undo.history.edits); + + general_memory_free(general, file->state.undo.children.strings); + general_memory_free(general, file->state.undo.children.edits); + } +} + +struct Shift_Information{ + i32 start, end, amount; +}; + +internal +Job_Callback_Sig(job_full_lex){ + Editing_File *file = (Editing_File*)data[0]; + General_Memory *general = (General_Memory*)data[1]; + + Cpp_File cpp_file; + cpp_file.data = file->state.buffer.data; + cpp_file.size = file->state.buffer.size; + + Cpp_Token_Stack tokens; + tokens.tokens = (Cpp_Token*)memory->data; + tokens.max_count = memory->size / sizeof(Cpp_Token); + tokens.count = 0; + + Cpp_Lex_Data status; + status = cpp_lex_file_nonalloc(cpp_file, &tokens); + + while (!status.complete){ + system->grow_thread_memory(memory); + tokens.tokens = (Cpp_Token*)memory->data; + tokens.max_count = memory->size / sizeof(Cpp_Token); + status = cpp_lex_file_nonalloc(cpp_file, &tokens, status); + } + + i32 new_max = LargeRoundUp(tokens.count+1, Kbytes(1)); + + system->acquire_lock(FRAME_LOCK); + { + Assert(file->state.swap_stack.tokens == 0); + file->state.swap_stack.tokens = (Cpp_Token*) + general_memory_allocate(general, new_max*sizeof(Cpp_Token), BUBBLE_TOKENS); + } + system->release_lock(FRAME_LOCK); + + u8 *dest = (u8*)file->state.swap_stack.tokens; + u8 *src = (u8*)tokens.tokens; + + memcpy(dest, src, tokens.count*sizeof(Cpp_Token)); + + system->acquire_lock(FRAME_LOCK); + { + file->state.token_stack.count = tokens.count; + file->state.token_stack.max_count = new_max; + if (file->state.token_stack.tokens) + general_memory_free(general, file->state.token_stack.tokens); + file->state.token_stack.tokens = file->state.swap_stack.tokens; + file->state.swap_stack.tokens = 0; + } + system->release_lock(FRAME_LOCK); + + exchange->force_redraw = 1; + + // NOTE(allen): These are outside the locked section because I don't + // think getting these out of order will cause critical bugs, and I + // want to minimize what's done in locked sections. + file->state.tokens_complete = 1; + file->state.still_lexing = 0; +} + + +internal void +file_kill_tokens(System_Functions *system, + General_Memory *general, Editing_File *file){ + file->settings.tokens_exist = 0; + if (file->state.still_lexing){ + system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); + if (file->state.swap_stack.tokens){ + general_memory_free(general, file->state.swap_stack.tokens); + file->state.swap_stack.tokens = 0; + } + } + if (file->state.token_stack.tokens){ + general_memory_free(general, file->state.token_stack.tokens); + } + file->state.tokens_complete = 0; + file->state.token_stack = {}; +} + +#if BUFFER_EXPERIMENT_SCALPEL <= 0 +internal void +file_first_lex_parallel(System_Functions *system, + General_Memory *general, Editing_File *file){ + file->settings.tokens_exist = 1; + + if (file->state.is_loading == 0 && file->state.still_lexing == 0){ + Assert(file->state.token_stack.tokens == 0); + + file->state.tokens_complete = 0; + file->state.still_lexing = 1; + + Job_Data job; + job.callback = job_full_lex; + job.data[0] = file; + job.data[1] = general; + job.memory_request = Kbytes(64); + file->state.lex_job = system->post_job(BACKGROUND_THREADS, job); +#endif + } +} + +internal void +file_relex_parallel(System_Functions *system, + Mem_Options *mem, Editing_File *file, + i32 start_i, i32 end_i, i32 amount){ + General_Memory *general = &mem->general; + Partition *part = &mem->part; + if (file->state.token_stack.tokens == 0){ + file_first_lex_parallel(system, general, file); + return; + } + + b32 inline_lex = !file->state.still_lexing; + if (inline_lex){ + Cpp_File cpp_file; + cpp_file.data = file->state.buffer.data; + cpp_file.size = file->state.buffer.size; + + Cpp_Token_Stack *stack = &file->state.token_stack; + + Cpp_Relex_State state = + cpp_relex_nonalloc_start(cpp_file, stack, + start_i, end_i, amount, 100); + + Temp_Memory temp = begin_temp_memory(part); + i32 relex_end; + Cpp_Token_Stack relex_space; + relex_space.count = 0; + relex_space.max_count = state.space_request; + relex_space.tokens = push_array(part, Cpp_Token, relex_space.max_count); + if (cpp_relex_nonalloc_main(&state, &relex_space, &relex_end)){ + inline_lex = 0; + } + else{ + i32 delete_amount = relex_end - state.start_token_i; + i32 shift_amount = relex_space.count - delete_amount; + + if (shift_amount != 0){ + int new_count = stack->count + shift_amount; + if (new_count > stack->max_count){ + int new_max = LargeRoundUp(new_count, Kbytes(1)); + stack->tokens = (Cpp_Token*) + general_memory_reallocate(general, stack->tokens, + stack->count*sizeof(Cpp_Token), + new_max*sizeof(Cpp_Token), BUBBLE_TOKENS); + stack->max_count = new_max; + } + + int shift_size = stack->count - relex_end; + if (shift_size > 0){ + Cpp_Token *old_base = stack->tokens + relex_end; + memmove(old_base + shift_amount, old_base, + sizeof(Cpp_Token)*shift_size); + } + + stack->count += shift_amount; + } + + memcpy(state.stack->tokens + state.start_token_i, relex_space.tokens, + sizeof(Cpp_Token)*relex_space.count); + } + + end_temp_memory(temp); + } + + if (!inline_lex){ + i32 end_token_i = cpp_get_end_token(&file->state.token_stack, end_i); + cpp_shift_token_starts(&file->state.token_stack, end_token_i, amount); + --end_token_i; + if (end_token_i >= 0){ + Cpp_Token *token = file->state.token_stack.tokens + end_token_i; + if (token->start < end_i && token->start + token->size > end_i){ + token->size += amount; + } + } + + file->state.still_lexing = 1; + + Job_Data job; + job.callback = job_full_lex; + job.data[0] = file; + job.data[1] = general; + job.memory_request = Kbytes(64); + file->state.lex_job = system->post_job(BACKGROUND_THREADS, job); + } +} + +internal void +undo_stack_grow_string(General_Memory *general, Edit_Stack *stack, i32 extra_size){ + i32 old_max = stack->max; + u8 *old_str = stack->strings; + i32 new_max = old_max*2 + extra_size; + u8 *new_str = (u8*) + general_memory_reallocate(general, old_str, old_max, new_max); + stack->strings = new_str; + stack->max = new_max; +} + +internal void +undo_stack_grow_edits(General_Memory *general, Edit_Stack *stack){ + i32 old_max = stack->edit_max; + Edit_Step *old_eds = stack->edits; + i32 new_max = old_max*2 + 2; + Edit_Step *new_eds = (Edit_Step*) + general_memory_reallocate(general, old_eds, old_max*sizeof(Edit_Step), new_max*sizeof(Edit_Step)); + stack->edits = new_eds; + stack->edit_max = new_max; +} + +internal void +child_stack_grow_string(General_Memory *general, Small_Edit_Stack *stack, i32 extra_size){ + i32 old_max = stack->max; + u8 *old_str = stack->strings; + i32 new_max = old_max*2 + extra_size; + u8 *new_str = (u8*) + general_memory_reallocate(general, old_str, old_max, new_max); + stack->strings = new_str; + stack->max = new_max; +} + +internal void +child_stack_grow_edits(General_Memory *general, Small_Edit_Stack *stack, i32 amount){ + i32 old_max = stack->edit_max; + Buffer_Edit *old_eds = stack->edits; + i32 new_max = old_max*2 + amount; + Buffer_Edit *new_eds = (Buffer_Edit*) + general_memory_reallocate(general, old_eds, old_max*sizeof(Buffer_Edit), new_max*sizeof(Buffer_Edit)); + stack->edits = new_eds; + stack->edit_max = new_max; +} + +internal i32 +undo_children_push(General_Memory *general, Small_Edit_Stack *children, + Buffer_Edit *edits, i32 edit_count, u8 *strings, i32 string_size){ + i32 result = children->edit_count; + if (children->edit_count + edit_count > children->edit_max) + child_stack_grow_edits(general, children, edit_count); + + if (children->size + string_size > children->max) + child_stack_grow_string(general, children, string_size); + + memcpy(children->edits + children->edit_count, edits, edit_count*sizeof(Buffer_Edit)); + memcpy(children->strings + children->size, strings, string_size); + + Buffer_Edit *edit = children->edits + children->edit_count; + i32 start_pos = children->size; + for (i32 i = 0; i < edit_count; ++i, ++edit){ + edit->str_start += start_pos; + } + + children->edit_count += edit_count; + children->size += string_size; + + return result; +} + +struct Edit_Spec{ + u8 *str; + Edit_Step step; +}; + +internal Edit_Step* +file_post_undo(General_Memory *general, Editing_File *file, + Edit_Step step, bool32 do_merge, bool32 can_merge){ + if (step.type == ED_NORMAL){ + file->state.undo.redo.size = 0; + file->state.undo.redo.edit_count = 0; + } + + Edit_Stack *undo = &file->state.undo.undo; + Edit_Step *result = 0; + + if (step.child_count == 0){ + if (step.edit.end - step.edit.start + undo->size > undo->max) + undo_stack_grow_string(general, undo, step.edit.end - step.edit.start); + + Buffer_Edit inv; + buffer_invert_edit(&file->state.buffer, step.edit, &inv, + (char*)undo->strings, &undo->size, undo->max); + + Edit_Step inv_step = {}; + inv_step.edit = inv; + inv_step.pre_pos = step.pre_pos; + inv_step.post_pos = step.post_pos; + inv_step.can_merge = (b8)can_merge; + inv_step.type = ED_UNDO; + + bool32 did_merge = 0; + if (do_merge && undo->edit_count > 0){ + Edit_Step prev = undo->edits[undo->edit_count-1]; + if (prev.can_merge && inv_step.edit.len == 0 && prev.edit.len == 0){ + if (prev.edit.end == inv_step.edit.start){ + did_merge = 1; + inv_step.edit.start = prev.edit.start; + inv_step.pre_pos = prev.pre_pos; + } + } + } + + if (did_merge){ + result = undo->edits + (undo->edit_count-1); + *result = inv_step; + } + else{ + if (undo->edit_count == undo->edit_max) + undo_stack_grow_edits(general, undo); + result = undo->edits + (undo->edit_count++); + *result = inv_step; + } + } + else{ + Edit_Step inv_step = {}; + inv_step.type = ED_UNDO; + inv_step.first_child = step.inverse_first_child; + inv_step.inverse_first_child = step.first_child; + inv_step.special_type = step.special_type; + inv_step.child_count = step.inverse_child_count; + inv_step.inverse_child_count = step.child_count; + + if (undo->edit_count == undo->edit_max) + undo_stack_grow_edits(general, undo); + result = undo->edits + (undo->edit_count++); + *result = inv_step; + } + return result; +} + +inline void +undo_stack_pop(Edit_Stack *stack){ + if (stack->edit_count > 0){ + Edit_Step *edit = stack->edits + (--stack->edit_count); + stack->size -= edit->edit.len; + } +} + +internal void +file_post_redo(General_Memory *general, Editing_File *file, Edit_Step step){ + Edit_Stack *redo = &file->state.undo.redo; + + if (step.child_count == 0){ + if (step.edit.end - step.edit.start + redo->size > redo->max) + undo_stack_grow_string(general, redo, step.edit.end - step.edit.start); + + Buffer_Edit inv; + buffer_invert_edit(&file->state.buffer, step.edit, &inv, + (char*)redo->strings, &redo->size, redo->max); + + Edit_Step inv_step = {}; + inv_step.edit = inv; + inv_step.pre_pos = step.pre_pos; + inv_step.post_pos = step.post_pos; + inv_step.type = ED_REDO; + + if (redo->edit_count == redo->edit_max) + undo_stack_grow_edits(general, redo); + redo->edits[redo->edit_count++] = inv_step; + } + else{ + Edit_Step inv_step = {}; + inv_step.type = ED_REDO; + inv_step.first_child = step.inverse_first_child; + inv_step.inverse_first_child = step.first_child; + inv_step.special_type = step.special_type; + inv_step.child_count = step.inverse_child_count; + inv_step.inverse_child_count = step.child_count; + + if (redo->edit_count == redo->edit_max) + undo_stack_grow_edits(general, redo); + redo->edits[redo->edit_count++] = inv_step; + } +} + +inline void +file_post_history_block(Editing_File *file, i32 pos){ + Assert(file->state.undo.history_head_block < pos); + Assert(pos < file->state.undo.history.edit_count); + + Edit_Step *history = file->state.undo.history.edits; + Edit_Step *step = history + file->state.undo.history_head_block; + step->next_block = pos; + step = history + pos; + step->prev_block = file->state.undo.history_head_block; + file->state.undo.history_head_block = pos; + ++file->state.undo.history_block_count; +} + +inline void +file_unpost_history_block(Editing_File *file){ + Assert(file->state.undo.history_block_count > 1); + --file->state.undo.history_block_count; + Edit_Step *old_head = file->state.undo.history.edits + file->state.undo.history_head_block; + file->state.undo.history_head_block = old_head->prev_block; +} + +internal Edit_Step* +file_post_history(General_Memory *general, Editing_File *file, + Edit_Step step, b32 do_merge, b32 can_merge){ + Edit_Stack *history = &file->state.undo.history; + Edit_Step *result = 0; + + persist Edit_Type reverse_types[4]; + if (reverse_types[ED_UNDO] == 0){ + reverse_types[ED_NORMAL] = ED_REVERSE_NORMAL; + reverse_types[ED_REVERSE_NORMAL] = ED_NORMAL; + reverse_types[ED_UNDO] = ED_REDO; + reverse_types[ED_REDO] = ED_UNDO; + } + + if (step.child_count == 0){ + if (step.edit.end - step.edit.start + history->size > history->max) + undo_stack_grow_string(general, history, step.edit.end - step.edit.start); + + Buffer_Edit inv; + buffer_invert_edit(&file->state.buffer, step.edit, &inv, + (char*)history->strings, &history->size, history->max); + + Edit_Step inv_step = {}; + inv_step.edit = inv; + inv_step.pre_pos = step.pre_pos; + inv_step.post_pos = step.post_pos; + inv_step.can_merge = (b8)can_merge; + inv_step.type = reverse_types[step.type]; + + bool32 did_merge = 0; + if (do_merge && history->edit_count > 0){ + Edit_Step prev = history->edits[history->edit_count-1]; + if (prev.can_merge && inv_step.edit.len == 0 && prev.edit.len == 0){ + if (prev.edit.end == inv_step.edit.start){ + did_merge = 1; + inv_step.edit.start = prev.edit.start; + inv_step.pre_pos = prev.pre_pos; + } + } + } + + if (did_merge){ + result = history->edits + (history->edit_count-1); + } + else{ + if (history->edit_count == history->edit_max) + undo_stack_grow_edits(general, history); + result = history->edits + (history->edit_count++); + } + + *result = inv_step; + } + else{ + Edit_Step inv_step = {}; + inv_step.type = reverse_types[step.type]; + inv_step.first_child = step.inverse_first_child; + inv_step.inverse_first_child = step.first_child; + inv_step.special_type = step.special_type; + inv_step.inverse_child_count = step.child_count; + inv_step.child_count = step.inverse_child_count; + + if (history->edit_count == history->edit_max) + undo_stack_grow_edits(general, history); + result = history->edits + (history->edit_count++); + *result = inv_step; + } + + return result; +} + +inline Full_Cursor +view_compute_cursor_from_pos(View *view, i32 pos){ + Editing_File *file = view->file; + Models *models = view->models; + Render_Font *font = get_font_info(models->font_set, models->global_font.font_id)->font; + + Full_Cursor result = {}; + if (font){ + f32 max_width = view_compute_width(view); + result = buffer_cursor_from_pos(&file->state.buffer, pos, view->line_wrap_y, + max_width, (f32)view->font_height, font->advance_data); + } + return result; +} + +inline Full_Cursor +view_compute_cursor_from_unwrapped_xy(View *view, f32 seek_x, f32 seek_y, b32 round_down = 0){ + Editing_File *file = view->file; + Models *models = view->models; + Render_Font *font = get_font_info(models->font_set, models->global_font.font_id)->font; + + Full_Cursor result = {}; + if (font){ + f32 max_width = view_compute_width(view); + result = buffer_cursor_from_unwrapped_xy(&file->state.buffer, seek_x, seek_y, + round_down, view->line_wrap_y, + max_width, (f32)view->font_height, font->advance_data); + } + + return result; +} + +internal Full_Cursor +view_compute_cursor_from_wrapped_xy(View *view, f32 seek_x, f32 seek_y, b32 round_down = 0){ + Editing_File *file = view->file; + Models *models = view->models; + Render_Font *font = get_font_info(models->font_set, models->global_font.font_id)->font; + + Full_Cursor result = {}; + if (font){ + f32 max_width = view_compute_width(view); + result = buffer_cursor_from_wrapped_xy(&file->state.buffer, seek_x, seek_y, + round_down, view->line_wrap_y, + max_width, (f32)view->font_height, font->advance_data); + } + + return (result); +} + +internal Full_Cursor +view_compute_cursor_from_line_pos(View *view, i32 line, i32 pos){ + Editing_File *file = view->file; + Models *models = view->models; + Render_Font *font = get_font_info(models->font_set, models->global_font.font_id)->font; + + Full_Cursor result = {}; + if (font){ + f32 max_width = view_compute_width(view); + result = buffer_cursor_from_line_character(&file->state.buffer, line, pos, + view->line_wrap_y, max_width, (f32)view->font_height, font->advance_data); + } + + return (result); +} + +inline Full_Cursor +view_compute_cursor(View *view, Buffer_Seek seek){ + Full_Cursor result = {}; + + switch(seek.type){ + case buffer_seek_pos: + result = view_compute_cursor_from_pos(view, seek.pos); + break; + + case buffer_seek_wrapped_xy: + result = view_compute_cursor_from_wrapped_xy(view, seek.x, seek.y); + break; + + case buffer_seek_unwrapped_xy: + result = view_compute_cursor_from_unwrapped_xy(view, seek.x, seek.y); + break; + + case buffer_seek_line_char: + result = view_compute_cursor_from_line_pos(view, seek.line, seek.character); + break; + } + + return (result); +} + +inline Full_Cursor +view_compute_cursor_from_xy(View *view, f32 seek_x, f32 seek_y){ + Full_Cursor result; + if (view->unwrapped_lines) result = view_compute_cursor_from_unwrapped_xy(view, seek_x, seek_y); + else result = view_compute_cursor_from_wrapped_xy(view, seek_x, seek_y); + return result; +} + +inline void +view_set_temp_highlight(View *view, i32 pos, i32 end_pos){ + view->temp_highlight = view_compute_cursor_from_pos(view, pos); + view->temp_highlight_end_pos = end_pos; + view->show_temp_highlight = 1; +} + +inline i32 +view_get_cursor_pos(View *view){ + i32 result; + if (view->show_temp_highlight){ + result = view->temp_highlight.pos; + } + else{ + result = view->cursor.pos; + } + return result; +} + +inline f32 +view_get_cursor_x(View *view){ + f32 result; + Full_Cursor *cursor; + if (view->show_temp_highlight){ + cursor = &view->temp_highlight; + } + else{ + cursor = &view->cursor; + } + if (view->unwrapped_lines){ + result = cursor->unwrapped_x; + } + else{ + result = cursor->wrapped_x; + } + return result; +} + +inline f32 +view_get_cursor_y(View *view){ + Full_Cursor *cursor; + f32 result; + + if (view->show_temp_highlight) cursor = &view->temp_highlight; + else cursor = &view->cursor; + + if (view->unwrapped_lines) result = cursor->unwrapped_y; + else result = cursor->wrapped_y; + + return result; +} + +internal void +view_set_file( + // NOTE(allen): These parameters are always meaningful + View *view, Editing_File *file, Models *models, + + // NOTE(allen): Necessary when file != 0 + System_Functions *system, Hook_Function *open_hook, Application_Links *app, + + // other + b32 set_vui = 1){ + + Font_Info *fnt_info; + + // TODO(allen): This belongs somewhere else. + fnt_info = get_font_info(models->font_set, models->global_font.font_id); + view->font_advance = fnt_info->advance; + view->font_height = fnt_info->height; + + // NOTE(allen): Stuff that doesn't assume file exists. + view->file = file; + view->cursor = {}; + + // NOTE(allen): Stuff that does assume file exists. + if (file){ + //view->locked = file->settings.super_locked; + view->unwrapped_lines = file->settings.unwrapped_lines; + + if (file_is_ready(file)){ + view_measure_wraps(system, &models->mem.general, view); + view->cursor = view_compute_cursor_from_pos(view, file->state.cursor_pos); + + view->reinit_scrolling = 1; + } + } + + // TODO(allen): Bypass all this nonsense, it's a hack! Hooks need parameters! + // Just accept it and pass the file to the open hook when it is loaded. + if (file){ + if (open_hook && file->settings.is_initialized == 0){ + models->buffer_param_indices[models->buffer_param_count++] = (i32)(file - models->working_set.files); + open_hook(app); + models->buffer_param_count = 0; + file->settings.is_initialized = 1; + } + } + + if (set_vui){ + // TODO(allen): Fix this! There should be a way to easily separate setting a file, + // and switching to file mode, so that they don't cross over eachother like this. + view->ui_state = {}; + view->showing_ui = VUI_None; + } +} + +struct Relative_Scrolling{ + f32 scroll_x, scroll_y; + f32 target_x, target_y; + f32 scroll_min_limit; +}; + +internal Relative_Scrolling +view_get_relative_scrolling(View *view){ + Relative_Scrolling result; + f32 cursor_y; + cursor_y = view_get_cursor_y(view); + result.scroll_y = cursor_y - view->scroll_y; + result.target_y = cursor_y - view->target_y; + result.scroll_min_limit = view->scroll_min_limit; + return result; +} + +internal void +view_set_relative_scrolling(View *view, Relative_Scrolling scrolling){ + f32 cursor_y; + cursor_y = view_get_cursor_y(view); + view->scroll_y = cursor_y - scrolling.scroll_y; + view->target_y = cursor_y - scrolling.target_y; + if (view->target_y < scrolling.scroll_min_limit) view->target_y = scrolling.scroll_min_limit; +} + +inline void +view_cursor_move(View *view, Full_Cursor cursor){ + view->cursor = cursor; + view->preferred_x = view_get_cursor_x(view); + view->file->state.cursor_pos = view->cursor.pos; + view->show_temp_highlight = 0; +} + +inline void +view_cursor_move(View *view, i32 pos){ + Full_Cursor cursor = view_compute_cursor_from_pos(view, pos); + view_cursor_move(view, cursor); +} + +inline void +view_cursor_move(View *view, f32 x, f32 y, b32 round_down = 0){ + Full_Cursor cursor; + if (view->unwrapped_lines){ + cursor = view_compute_cursor_from_unwrapped_xy(view, x, y, round_down); + } + else{ + cursor = view_compute_cursor_from_wrapped_xy(view, x, y, round_down); + } + view_cursor_move(view, cursor); +} + +inline void +view_cursor_move(View *view, i32 line, i32 pos){ + Full_Cursor cursor = view_compute_cursor_from_line_pos(view, line, pos); + view_cursor_move(view, cursor); +} + +inline void +view_set_widget(View *view, View_Widget_Type type){ + view->widget.type = type; +} + + +inline i32_Rect +view_widget_rect(View *view, i32 font_height){ + Panel *panel = view->panel; + i32_Rect result = panel->inner; + + if (view->file){ + result.y0 = result.y0 + font_height + 2; + } + + return(result); +} + +enum History_Mode{ + hist_normal, + hist_backward, + hist_forward +}; + +internal void +file_update_history_before_edit(Mem_Options *mem, Editing_File *file, Edit_Step step, u8 *str, + History_Mode history_mode){ + if (!file->state.undo.undo.edits) return; + General_Memory *general = &mem->general; + + b32 can_merge = 0, do_merge = 0; + switch (step.type){ + case ED_NORMAL: + { + if (step.edit.len == 1 && str && char_is_alpha_numeric(*str)) can_merge = 1; + if (step.edit.len == 1 && str && (can_merge || char_is_whitespace(*str))) do_merge = 1; + + if (history_mode != hist_forward) + file_post_history(general, file, step, do_merge, can_merge); + + file_post_undo(general, file, step, do_merge, can_merge); + }break; + + case ED_REVERSE_NORMAL: + { + if (history_mode != hist_forward) + file_post_history(general, file, step, do_merge, can_merge); + + undo_stack_pop(&file->state.undo.undo); + + b32 restore_redos = 0; + Edit_Step *redo_end = 0; + + if (history_mode == hist_backward && file->state.undo.edit_history_cursor > 0){ + restore_redos = 1; + redo_end = file->state.undo.history.edits + (file->state.undo.edit_history_cursor - 1); + } + else if (history_mode == hist_forward && file->state.undo.history.edit_count > 0){ + restore_redos = 1; + redo_end = file->state.undo.history.edits + (file->state.undo.history.edit_count - 1); + } + + if (restore_redos){ + Edit_Step *redo_start = redo_end; + i32 steps_of_redo = 0; + i32 strings_of_redo = 0; + i32 undo_count = 0; + while (redo_start->type == ED_REDO || redo_start->type == ED_UNDO){ + if (redo_start->type == ED_REDO){ + if (undo_count > 0) --undo_count; + else{ + ++steps_of_redo; + strings_of_redo += redo_start->edit.len; + } + } + else{ + ++undo_count; + } + --redo_start; + } + + if (redo_start < redo_end){ + ++redo_start; + ++redo_end; + + if (file->state.undo.redo.edit_count + steps_of_redo > file->state.undo.redo.edit_max) + undo_stack_grow_edits(general, &file->state.undo.redo); + + if (file->state.undo.redo.size + strings_of_redo > file->state.undo.redo.max) + undo_stack_grow_string(general, &file->state.undo.redo, strings_of_redo); + + u8 *str_src = file->state.undo.history.strings + redo_end->edit.str_start; + u8 *str_dest_base = file->state.undo.redo.strings; + i32 str_redo_pos = file->state.undo.redo.size + strings_of_redo; + + Edit_Step *edit_src = redo_end; + Edit_Step *edit_dest = + file->state.undo.redo.edits + file->state.undo.redo.edit_count + steps_of_redo; + + i32 undo_count = 0; + for (i32 i = 0; i < steps_of_redo;){ + --edit_src; + str_src -= edit_src->edit.len; + if (edit_src->type == ED_REDO){ + if (undo_count > 0){ + --undo_count; + } + else{ + ++i; + + --edit_dest; + *edit_dest = *edit_src; + + str_redo_pos -= edit_dest->edit.len; + edit_dest->edit.str_start = str_redo_pos; + + memcpy(str_dest_base + str_redo_pos, str_src, edit_dest->edit.len); + } + } + else{ + ++undo_count; + } + } + Assert(undo_count == 0); + + file->state.undo.redo.size += strings_of_redo; + file->state.undo.redo.edit_count += steps_of_redo; + } + } + }break; + + case ED_UNDO: + { + if (history_mode != hist_forward) + file_post_history(general, file, step, do_merge, can_merge); + file_post_redo(general, file, step); + undo_stack_pop(&file->state.undo.undo); + }break; + + case ED_REDO: + { + if (step.edit.len == 1 && str && char_is_alpha_numeric(*str)) can_merge = 1; + if (step.edit.len == 1 && str && (can_merge || char_is_whitespace(*str))) do_merge = 1; + + if (history_mode != hist_forward) + file_post_history(general, file, step, do_merge, can_merge); + + file_post_undo(general, file, step, do_merge, can_merge); + undo_stack_pop(&file->state.undo.redo); + }break; + } + + if (history_mode != hist_forward){ + if (step.type == ED_UNDO || step.type == ED_REDO){ + if (file->state.undo.current_block_normal){ + file_post_history_block(file, file->state.undo.history.edit_count - 1); + file->state.undo.current_block_normal = 0; + } + } + else{ + if (!file->state.undo.current_block_normal){ + file_post_history_block(file, file->state.undo.history.edit_count - 1); + file->state.undo.current_block_normal = 1; + } + } + } + else{ + if (file->state.undo.history_head_block == file->state.undo.history.edit_count){ + file_unpost_history_block(file); + file->state.undo.current_block_normal = !file->state.undo.current_block_normal; + } + } + + if (history_mode == hist_normal){ + file->state.undo.edit_history_cursor = file->state.undo.history.edit_count; + } +} + +inline void +file_pre_edit_maintenance(System_Functions *system, + General_Memory *general, + Editing_File *file){ + if (file->state.still_lexing){ + system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); + if (file->state.swap_stack.tokens){ + general_memory_free(general, file->state.swap_stack.tokens); + file->state.swap_stack.tokens = 0; + } + } + file->state.last_4ed_edit_time = system->time(); +} + +struct Cursor_Fix_Descriptor{ + b32 is_batch; + union{ + struct{ + Buffer_Edit *batch; + i32 batch_size; + }; + struct{ + i32 start, end; + i32 shift_amount; + }; + }; +}; + +internal void +file_edit_cursor_fix(System_Functions *system, + Partition *part, General_Memory *general, + Editing_File *file, Editing_Layout *layout, + Cursor_Fix_Descriptor desc){ + + Full_Cursor temp_cursor; + Temp_Memory cursor_temp = begin_temp_memory(part); + i32 cursor_max = layout->panel_max_count * 2; + Cursor_With_Index *cursors = push_array(part, Cursor_With_Index, cursor_max); + + f32 y_offset = 0, y_position = 0; + i32 cursor_count = 0; + + View *view; + Panel *panel, *used_panels; + used_panels = &layout->used_sentinel; + + for (dll_items(panel, used_panels)){ + view = panel->view; + if (view->file == file){ + view_measure_wraps(system, general, view); + write_cursor_with_index(cursors, &cursor_count, view->cursor.pos); + write_cursor_with_index(cursors, &cursor_count, view->mark - 1); + write_cursor_with_index(cursors, &cursor_count, view->scroll_i - 1); + } + } + + if (cursor_count > 0){ + buffer_sort_cursors(cursors, cursor_count); + if (desc.is_batch){ + buffer_batch_edit_update_cursors(cursors, cursor_count, + desc.batch, desc.batch_size); + } + else{ + buffer_update_cursors(cursors, cursor_count, + desc.start, desc.end, + desc.shift_amount + (desc.end - desc.start)); + } + buffer_unsort_cursors(cursors, cursor_count); + + cursor_count = 0; + for (dll_items(panel, used_panels)){ + view = panel->view; + if (view && view->file == file){ + view_cursor_move(view, cursors[cursor_count++].pos); + view->preferred_x = view_get_cursor_x(view); + + view->mark = cursors[cursor_count++].pos + 1; + i32 new_scroll_i = cursors[cursor_count++].pos + 1; + if (view->scroll_i != new_scroll_i){ + view->scroll_i = new_scroll_i; + temp_cursor = view_compute_cursor_from_pos(view, view->scroll_i); + y_offset = MOD(view->scroll_y, view->font_height); + + if (view->unwrapped_lines){ + y_position = temp_cursor.unwrapped_y + y_offset; + view->target_y += (y_position - view->scroll_y); + view->scroll_y = y_position; + } + else{ + y_position = temp_cursor.wrapped_y + y_offset; + view->target_y += (y_position - view->scroll_y); + view->scroll_y = y_position; + } + } + } + } + } + + end_temp_memory(cursor_temp); +} + +internal void +file_do_single_edit(System_Functions *system, + Models *models, Editing_File *file, + Edit_Spec spec, History_Mode history_mode, b32 use_high_permission = 0){ + ProfileMomentFunction(); + if (!use_high_permission && file->settings.read_only) return; + + Mem_Options *mem = &models->mem; + Editing_Layout *layout = &models->layout; + + // NOTE(allen): fixing stuff beforewards???? + file_update_history_before_edit(mem, file, spec.step, spec.str, history_mode); + file_pre_edit_maintenance(system, &mem->general, file); + + // NOTE(allen): actual text replacement + i32 shift_amount = 0; + General_Memory *general = &mem->general; + Partition *part = &mem->part; + + char *str = (char*)spec.str; + i32 start = spec.step.edit.start; + i32 end = spec.step.edit.end; + i32 str_len = spec.step.edit.len; + + i32 scratch_size = partition_remaining(part); + + Assert(scratch_size > 0); + i32 request_amount = 0; + while (buffer_replace_range(&file->state.buffer, start, end, str, str_len, &shift_amount, + part->base + part->pos, scratch_size, &request_amount)){ + void *new_data = 0; + if (request_amount > 0){ + new_data = general_memory_allocate(general, request_amount, BUBBLE_BUFFER); + } + void *old_data = buffer_edit_provide_memory(&file->state.buffer, new_data, request_amount); + if (old_data) general_memory_free(general, old_data); + } + + Buffer_Type *buffer = &file->state.buffer; + i32 line_start = buffer_get_line_index(&file->state.buffer, start); + i32 line_end = buffer_get_line_index(&file->state.buffer, end); + i32 replaced_line_count = line_end - line_start; + i32 new_line_count = buffer_count_newlines(&file->state.buffer, start, start+str_len); + i32 line_shift = new_line_count - replaced_line_count; + + Render_Font *font = get_font_info(models->font_set, file->state.font_id)->font; + + file_grow_starts_widths_as_needed(general, buffer, line_shift); + buffer_remeasure_starts(buffer, line_start, line_end, line_shift, shift_amount); + buffer_remeasure_widths(buffer, font->advance_data, line_start, line_end, line_shift); + + Panel *panel, *used_panels; + used_panels = &layout->used_sentinel; + + for (dll_items(panel, used_panels)){ + View *view = panel->view; + if (view->file == file){ + view_measure_wraps(system, general, view); + } + } + +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + // NOTE(allen): fixing stuff afterwards + if (file->settings.tokens_exist) + file_relex_parallel(system, mem, file, start, end, shift_amount); +#endif + + Cursor_Fix_Descriptor desc = {}; + desc.start = start; + desc.end = end; + desc.shift_amount = shift_amount; + + file_edit_cursor_fix(system, part, general, + file, layout, desc); +} + +internal void +file_do_white_batch_edit(System_Functions *system, Models *models, Editing_File *file, + Edit_Spec spec, History_Mode history_mode, b32 use_high_permission = 0){ + ProfileMomentFunction(); + if (!use_high_permission && file->settings.read_only) return; + + Mem_Options *mem = &models->mem; + Editing_Layout *layout = &models->layout; + + // NOTE(allen): fixing stuff "beforewards"??? + Assert(spec.str == 0); + file_update_history_before_edit(mem, file, spec.step, 0, history_mode); + file_pre_edit_maintenance(system, &mem->general, file); + + // NOTE(allen): actual text replacement + General_Memory *general = &mem->general; + Partition *part = &mem->part; + + u8 *str_base = file->state.undo.children.strings; + i32 batch_size = spec.step.child_count; + Buffer_Edit *batch = file->state.undo.children.edits + spec.step.first_child; + + Assert(spec.step.first_child < file->state.undo.children.edit_count); + Assert(batch_size >= 0); + + i32 scratch_size = partition_remaining(part); + Buffer_Batch_State state = {}; + i32 request_amount; + while (buffer_batch_edit_step(&state, &file->state.buffer, batch, + (char*)str_base, batch_size, part->base + part->pos, + scratch_size, &request_amount)){ + void *new_data = 0; + if (request_amount > 0){ + new_data = general_memory_allocate(general, request_amount, BUBBLE_BUFFER); + } + void *old_data = buffer_edit_provide_memory(&file->state.buffer, new_data, request_amount); + if (old_data) general_memory_free(general, old_data); + } + + // NOTE(allen): meta data + { + Buffer_Measure_Starts state = {}; + Render_Font *font = get_font_info(models->font_set, file->state.font_id)->font; + float *advance_data = 0; + if (font) advance_data = font->advance_data; + buffer_measure_starts_widths(&state, &file->state.buffer, advance_data); + } + + // NOTE(allen): cursor fixing + { + Cursor_Fix_Descriptor desc = {}; + desc.is_batch = 1; + desc.batch = batch; + desc.batch_size = batch_size; + + file_edit_cursor_fix(system, part, general, file, layout, desc); + } + + // NOTE(allen): token fixing + if (file->state.tokens_complete){ + Cpp_Token_Stack tokens = file->state.token_stack; + Cpp_Token *token = tokens.tokens; + Cpp_Token *end_token = tokens.tokens + tokens.count; + + Buffer_Edit *edit = batch; + Buffer_Edit *end_edit = batch + batch_size; + + i32 shift_amount = 0; + i32 local_shift = 0; + + for (; token < end_token && edit < end_edit; ++edit){ + local_shift = (edit->len - (edit->end - edit->start)); + for (; token->start < edit->start && edit->start < token->start + token->size && + token < end_token; ++token){ + token->size += local_shift; + } + for (; token->start < edit->start && token < end_token; ++token){ + token->start += shift_amount; + } + shift_amount += local_shift; + } + for (; token < end_token; ++token){ + token->start += shift_amount; + } + } +} + +inline void +file_replace_range(System_Functions *system, Models *models, Editing_File *file, + i32 start, i32 end, char *str, i32 len, i32 next_cursor, b32 use_high_permission = 0){ + Edit_Spec spec = {}; + spec.step.type = ED_NORMAL; + spec.step.edit.start = start; + spec.step.edit.end = end; + spec.step.edit.len = len; + spec.step.pre_pos = file->state.cursor_pos; + spec.step.post_pos = next_cursor; + spec.str = (u8*)str; + file_do_single_edit(system, models, file, spec, hist_normal, use_high_permission); +} + +inline void +view_replace_range(System_Functions *system, Models *models, View *view, + i32 start, i32 end, char *str, i32 len, i32 next_cursor){ + file_replace_range(system, models, view->file, start, end, str, len, next_cursor); +} + +inline void +view_post_paste_effect(View *view, i32 ticks, i32 start, i32 size, u32 color){ + Editing_File *file = view->file; + + file->state.paste_effect.start = start; + file->state.paste_effect.end = start + size; + file->state.paste_effect.color = color; + file->state.paste_effect.tick_down = ticks; + file->state.paste_effect.tick_max = ticks; +} + +internal void +view_undo_redo(System_Functions *system, + Models *models, View *view, + Edit_Stack *stack, Edit_Type expected_type){ + Editing_File *file = view->file; + + if (stack->edit_count > 0){ + Edit_Step step = stack->edits[stack->edit_count-1]; + + Assert(step.type == expected_type); + + Edit_Spec spec = {}; + spec.step = step; + + if (step.child_count == 0){ + spec.step.edit.str_start = 0; + spec.str = stack->strings + step.edit.str_start; + + file_do_single_edit(system, models, file, spec, hist_normal); + + if (expected_type == ED_UNDO) view_cursor_move(view, step.pre_pos); + else view_cursor_move(view, step.post_pos); + view->mark = view->cursor.pos; + + view_post_paste_effect(view, 10, step.edit.start, step.edit.len, + models->style.main.undo_color); + } + else{ + TentativeAssert(spec.step.special_type == 1); + file_do_white_batch_edit(system, models, view->file, spec, hist_normal); + } + } +} + +inline void +view_undo(System_Functions *system, Models *models, View *view){ + view_undo_redo(system, models, view, &view->file->state.undo.undo, ED_UNDO); +} + +inline void +view_redo(System_Functions *system, Models *models, View *view){ + view_undo_redo(system, models, view, &view->file->state.undo.redo, ED_REDO); +} + +inline u8* +write_data(u8 *ptr, void *x, i32 size){ + memcpy(ptr, x, size); + return (ptr + size); +} + +#define UseFileHistoryDump 0 + +#if UseFileHistoryDump +internal void +file_dump_history(System_Functions *system, Mem_Options *mem, Editing_File *file, char *filename){ + if (!file->state.undo.undo.edits) return; + + i32 size = 0; + + size += sizeof(i32); + size += file->state.undo.undo.edit_count*sizeof(Edit_Step); + size += sizeof(i32); + size += file->state.undo.redo.edit_count*sizeof(Edit_Step); + size += sizeof(i32); + size += file->state.undo.history.edit_count*sizeof(Edit_Step); + size += sizeof(i32); + size += file->state.undo.children.edit_count*sizeof(Buffer_Edit); + + size += sizeof(i32); + size += file->state.undo.undo.size; + size += sizeof(i32); + size += file->state.undo.redo.size; + size += sizeof(i32); + size += file->state.undo.history.size; + size += sizeof(i32); + size += file->state.undo.children.size; + + Partition *part = &mem->part; + i32 remaining = partition_remaining(part); + if (size < remaining){ + u8 *data, *curs; + data = (u8*)part->base + part->pos; + curs = data; + curs = write_data(curs, &file->state.undo.undo.edit_count, 4); + curs = write_data(curs, &file->state.undo.redo.edit_count, 4); + curs = write_data(curs, &file->state.undo.history.edit_count, 4); + curs = write_data(curs, &file->state.undo.children.edit_count, 4); + curs = write_data(curs, &file->state.undo.undo.size, 4); + curs = write_data(curs, &file->state.undo.redo.size, 4); + curs = write_data(curs, &file->state.undo.history.size, 4); + curs = write_data(curs, &file->state.undo.children.size, 4); + + curs = write_data(curs, file->state.undo.undo.edits, sizeof(Edit_Step)*file->state.undo.undo.edit_count); + curs = write_data(curs, file->state.undo.redo.edits, sizeof(Edit_Step)*file->state.undo.redo.edit_count); + curs = write_data(curs, file->state.undo.history.edits, sizeof(Edit_Step)*file->state.undo.history.edit_count); + curs = write_data(curs, file->state.undo.children.edits, sizeof(Buffer_Edit)*file->state.undo.children.edit_count); + + curs = write_data(curs, file->state.undo.undo.strings, file->state.undo.undo.size); + curs = write_data(curs, file->state.undo.redo.strings, file->state.undo.redo.size); + curs = write_data(curs, file->state.undo.history.strings, file->state.undo.history.size); + curs = write_data(curs, file->state.undo.children.strings, file->state.undo.children.size); + + Assert((i32)(curs - data) == size); + system->save_file(filename, data, size); + } +} +#endif + +internal void +view_history_step(System_Functions *system, Models *models, View *view, History_Mode history_mode){ + Assert(history_mode != hist_normal); + + Editing_File *file = view->file; + + b32 do_history_step = 0; + Edit_Step step = {}; + if (history_mode == hist_backward){ + if (file->state.undo.edit_history_cursor > 0){ + do_history_step = 1; + step = file->state.undo.history.edits[--file->state.undo.edit_history_cursor]; + } + } + else{ + if (file->state.undo.edit_history_cursor < file->state.undo.history.edit_count){ + Assert(((file->state.undo.history.edit_count - file->state.undo.edit_history_cursor) & 1) == 0); + step = file->state.undo.history.edits[--file->state.undo.history.edit_count]; + file->state.undo.history.size -= step.edit.len; + ++file->state.undo.edit_history_cursor; + do_history_step = 1; + } + } + + if (do_history_step){ + Edit_Spec spec; + spec.step = step; + + if (spec.step.child_count == 0){ + spec.step.edit.str_start = 0; + spec.str = file->state.undo.history.strings + step.edit.str_start; + + file_do_single_edit(system, models, file, spec, history_mode); + + switch (spec.step.type){ + case ED_NORMAL: + case ED_REDO: + view_cursor_move(view, step.post_pos); + break; + + case ED_REVERSE_NORMAL: + case ED_UNDO: + view_cursor_move(view, step.pre_pos); + break; + } + view->mark = view->cursor.pos; + } + else{ + TentativeAssert(spec.step.special_type == 1); + file_do_white_batch_edit(system, models, view->file, spec, history_mode); + } + } +} + +// TODO(allen): write these as streamed operations +internal i32 +view_find_end_of_line(View *view, i32 pos){ +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + Editing_File *file = view->file; + char *data = file->state.buffer.data; + while (pos < file->state.buffer.size && data[pos] != '\n') ++pos; + if (pos > file->state.buffer.size) pos = file->state.buffer.size; +#endif + return pos; +} + +internal i32 +view_find_beginning_of_line(View *view, i32 pos){ +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + Editing_File *file = view->file; + char *data = file->state.buffer.data; + if (pos > 0){ + --pos; + while (pos > 0 && data[pos] != '\n') --pos; + if (pos != 0) ++pos; + } +#endif + return pos; +} + +internal i32 +view_find_beginning_of_next_line(View *view, i32 pos){ +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + Editing_File *file = view->file; + char *data = file->state.buffer.data; + while (pos < file->state.buffer.size && + !starts_new_line(data[pos])){ + ++pos; + } + if (pos < file->state.buffer.size){ + ++pos; + } +#endif + return pos; +} + +internal String* +working_set_next_clipboard_string(General_Memory *general, Working_Set *working, i32 str_size){ + String *result = 0; + i32 clipboard_current = working->clipboard_current; + if (working->clipboard_size == 0){ + clipboard_current = 0; + working->clipboard_size = 1; + } + else{ + ++clipboard_current; + if (clipboard_current >= working->clipboard_max_size){ + clipboard_current = 0; + } + else if (working->clipboard_size <= clipboard_current){ + working->clipboard_size = clipboard_current+1; + } + } + result = &working->clipboards[clipboard_current]; + working->clipboard_current = clipboard_current; + working->clipboard_rolling = clipboard_current; + char *new_str; + if (result->str){ + new_str = (char*)general_memory_reallocate(general, result->str, result->size, str_size); + } + else{ + new_str = (char*)general_memory_allocate(general, str_size+1); + } + // TODO(allen): What if new_str == 0? + *result = make_string(new_str, 0, str_size); + return result; +} + +internal String* +working_set_clipboard_head(Working_Set *working){ + String *result = 0; + if (working->clipboard_size > 0){ + i32 clipboard_index = working->clipboard_current; + working->clipboard_rolling = clipboard_index; + result = &working->clipboards[clipboard_index]; + } + return result; +} + +internal String* +working_set_clipboard_roll_down(Working_Set *working){ + String *result = 0; + if (working->clipboard_size > 0){ + i32 clipboard_index = working->clipboard_rolling; + --clipboard_index; + if (clipboard_index < 0){ + clipboard_index = working->clipboard_size-1; + } + working->clipboard_rolling = clipboard_index; + result = &working->clipboards[clipboard_index]; + } + return result; +} + +internal void +clipboard_copy(System_Functions *system, General_Memory *general, Working_Set *working, Range range, Editing_File *file){ + i32 size = range.end - range.start; + String *dest = working_set_next_clipboard_string(general, working, size); + buffer_stringify(&file->state.buffer, range.start, range.end, dest->str); + dest->size = size; + system->post_clipboard(*dest); +} + +internal Edit_Spec +file_compute_whitespace_edit(Mem_Options *mem, Editing_File *file, i32 cursor_pos, + Buffer_Edit *edits, char *str_base, i32 str_size, + Buffer_Edit *inverse_array, char *inv_str, i32 inv_max, + i32 edit_count){ + General_Memory *general = &mem->general; + + i32 inv_str_pos = 0; + Buffer_Invert_Batch state = {}; + if (buffer_invert_batch(&state, &file->state.buffer, edits, edit_count, + inverse_array, inv_str, &inv_str_pos, inv_max)) + Assert(0); + + i32 first_child = + undo_children_push(general, &file->state.undo.children, + edits, edit_count, (u8*)(str_base), str_size); + i32 inverse_first_child = + undo_children_push(general, &file->state.undo.children, + inverse_array, edit_count, (u8*)(inv_str), inv_str_pos); + + Edit_Spec spec = {}; + spec.step.type = ED_NORMAL; + spec.step.first_child = first_child; + spec.step.inverse_first_child = inverse_first_child; + spec.step.special_type = 1; + spec.step.child_count = edit_count; + spec.step.inverse_child_count = edit_count; + spec.step.pre_pos = cursor_pos; + spec.step.post_pos = cursor_pos; + + return spec; +} + +internal void +view_clean_whitespace(System_Functions *system, Models *models, View *view){ + Mem_Options *mem = &models->mem; + Editing_File *file = view->file; + + Partition *part = &mem->part; + i32 line_count = file->state.buffer.line_count; + i32 edit_max = line_count * 2; + i32 edit_count = 0; + + Assert(file && !file->state.is_dummy); + + Temp_Memory temp = begin_temp_memory(part); + Buffer_Edit *edits = push_array(part, Buffer_Edit, edit_max); + + char *str_base = (char*)part->base + part->pos; + i32 str_size = 0; + for (i32 line_i = 0; line_i < line_count; ++line_i){ + i32 start = file->state.buffer.line_starts[line_i]; + i32 preferred_indentation; + b32 all_whitespace = 0; + b32 all_space = 0; + i32 hard_start = + buffer_find_hard_start(&file->state.buffer, start, &all_whitespace, &all_space, + &preferred_indentation, 4); + + if (all_whitespace) preferred_indentation = 0; + + if ((all_whitespace && hard_start > start) || !all_space){ + Buffer_Edit new_edit; + new_edit.str_start = str_size; + str_size += preferred_indentation; + char *str = push_array(part, char, preferred_indentation); + for (i32 j = 0; j < preferred_indentation; ++j) str[j] = ' '; + new_edit.len = preferred_indentation; + new_edit.start = start; + new_edit.end = hard_start; + edits[edit_count++] = new_edit; + } + Assert(edit_count <= edit_max); + } + + if (edit_count > 0){ + Assert(buffer_batch_debug_sort_check(edits, edit_count)); + + // NOTE(allen): computing edit spec, doing batch edit + Buffer_Edit *inverse_array = push_array(part, Buffer_Edit, edit_count); + Assert(inverse_array); + + char *inv_str = (char*)part->base + part->pos; + Edit_Spec spec = + file_compute_whitespace_edit(mem, file, view->cursor.pos, edits, str_base, str_size, + inverse_array, inv_str, part->max - part->pos, edit_count); + + file_do_white_batch_edit(system, models, view->file, spec, hist_normal); + } + + end_temp_memory(temp); +} + +internal void +view_auto_tab_tokens(System_Functions *system, + Models *models, View *view, + i32 start, i32 end, b32 empty_blank_lines){ +#if BUFFER_EXPERIMENT_SCALPEL <= 0 + Editing_File *file = view->file; + Mem_Options *mem = &models->mem; + Partition *part = &mem->part; + Buffer *buffer = &file->state.buffer; + + Assert(file && !file->state.is_dummy); + Cpp_Token_Stack tokens = file->state.token_stack; + Assert(tokens.tokens); + + i32 line_start = buffer_get_line_index(buffer, start); + i32 line_end = buffer_get_line_index(buffer, end) + 1; + + i32 edit_max = (line_end - line_start) * 2; + i32 edit_count = 0; + + i32 indent_mark_count = line_end - line_start; + + Temp_Memory temp = begin_temp_memory(part); + i32 *indent_marks = push_array(part, i32, indent_mark_count); + { + i32 current_indent = 0; + i32 token_i; + Cpp_Token *token, *self_token; + + { + i32 start_pos = file->state.buffer.line_starts[line_start]; + Cpp_Get_Token_Result result = cpp_get_token(&tokens, start_pos); + token_i = result.token_index; + if (result.in_whitespace) token_i += 1; + self_token = tokens.tokens + token_i; + } + + i32 line = line_start - 1; + for (; line >= 0; --line){ + i32 start = file->state.buffer.line_starts[line]; + b32 all_whitespace = 0; + b32 all_space = 0; + buffer_find_hard_start(&file->state.buffer, start, + &all_whitespace, &all_space, ¤t_indent, 4); + if (!all_whitespace) break; + } + + if (line < 0){ + token_i = 0; + token = tokens.tokens + token_i; + } + else{ + i32 start_pos = file->state.buffer.line_starts[line]; + Cpp_Get_Token_Result result = cpp_get_token(&tokens, start_pos); + token_i = result.token_index; + if (result.in_whitespace) token_i += 1; + token = tokens.tokens + token_i; + + while (token >= tokens.tokens && + token->flags & CPP_TFLAG_PP_DIRECTIVE || + token->flags & CPP_TFLAG_PP_BODY){ + --token; + } + + if (token < tokens.tokens){ + ++token; + current_indent = 0; + } + else if (token->start < start_pos){ + line = buffer_get_line_index(&file->state.buffer, token->start); + i32 start = file->state.buffer.line_starts[line]; + b32 all_whitespace = 0; + b32 all_space = 0; + buffer_find_hard_start(&file->state.buffer, start, + &all_whitespace, &all_space, ¤t_indent, 4); + Assert(!all_whitespace); + } + } + + indent_marks -= line_start; + i32 line_i = line_start; + i32 next_line_start = file->state.buffer.line_starts[line_i]; + switch (token->type){ + case CPP_TOKEN_BRACKET_OPEN: current_indent += 4; break; + case CPP_TOKEN_PARENTHESE_OPEN: current_indent += 4; break; + case CPP_TOKEN_BRACE_OPEN: current_indent += 4; break; + } + + Cpp_Token *prev_token = token; + ++token; + for (; line_i < line_end; ++token_i, ++token){ + for (; token->start >= next_line_start && line_i < line_end;){ + i32 this_line_start = next_line_start; + next_line_start = file->state.buffer.line_starts[line_i+1]; + i32 this_indent; + if (prev_token && prev_token->type == CPP_TOKEN_COMMENT && + prev_token->start <= this_line_start && prev_token->start + prev_token->size > this_line_start){ + this_indent = -1; + } + else{ + this_indent = current_indent; + if (token->start < next_line_start){ + if (token->flags & CPP_TFLAG_PP_DIRECTIVE) this_indent = 0; + else{ + switch (token->type){ + case CPP_TOKEN_BRACKET_CLOSE: this_indent -= 4; break; + case CPP_TOKEN_PARENTHESE_CLOSE: this_indent -= 4; break; + case CPP_TOKEN_BRACE_CLOSE: this_indent -= 4; break; + case CPP_TOKEN_BRACE_OPEN: break; + default: + if (current_indent > 0 && prev_token){ + if (!(prev_token->flags & CPP_TFLAG_PP_BODY || + prev_token->flags & CPP_TFLAG_PP_DIRECTIVE)){ + switch (prev_token->type){ + case CPP_TOKEN_BRACKET_OPEN: case CPP_TOKEN_PARENTHESE_OPEN: + case CPP_TOKEN_BRACE_OPEN: case CPP_TOKEN_BRACE_CLOSE: + case CPP_TOKEN_SEMICOLON: case CPP_TOKEN_COLON: break; + case CPP_TOKEN_COMMA: case CPP_TOKEN_COMMENT: break; + default: this_indent += 4; + } + } + } + } + } + } + if (this_indent < 0) this_indent = 0; + } + indent_marks[line_i] = this_indent; + ++line_i; + } + + switch (token->type){ + case CPP_TOKEN_BRACKET_OPEN: current_indent += 4; break; + case CPP_TOKEN_BRACKET_CLOSE: current_indent -= 4; break; + case CPP_TOKEN_PARENTHESE_OPEN: current_indent += 4; break; + case CPP_TOKEN_PARENTHESE_CLOSE: current_indent -= 4; break; + case CPP_TOKEN_BRACE_OPEN: current_indent += 4; break; + case CPP_TOKEN_BRACE_CLOSE: current_indent -= 4; break; + } + prev_token = token; + } + } + + Buffer_Edit *edits = push_array(part, Buffer_Edit, edit_max); + + char *str_base = (char*)part->base + part->pos; + i32 str_size = 0; + for (i32 line_i = line_start; line_i < line_end; ++line_i){ + i32 start = file->state.buffer.line_starts[line_i]; + i32 preferred_indentation; + i32 correct_indentation; + b32 all_whitespace = 0; + b32 all_space = 0; + i32 hard_start = + buffer_find_hard_start(&file->state.buffer, start, &all_whitespace, &all_space, + &preferred_indentation, 4); + + correct_indentation = indent_marks[line_i]; + if (all_whitespace && empty_blank_lines) correct_indentation = 0; + if (correct_indentation == -1) correct_indentation = preferred_indentation; + + if ((all_whitespace && hard_start > start) || !all_space || correct_indentation != preferred_indentation){ + Buffer_Edit new_edit; + new_edit.str_start = str_size; + str_size += correct_indentation; + char *str = push_array(part, char, correct_indentation); + for (i32 j = 0; j < correct_indentation; ++j) str[j] = ' '; + new_edit.len = correct_indentation; + new_edit.start = start; + new_edit.end = hard_start; + edits[edit_count++] = new_edit; + } + + Assert(edit_count <= edit_max); + } + + if (edit_count > 0){ + Assert(buffer_batch_debug_sort_check(edits, edit_count)); + + // NOTE(allen): computing edit spec, doing batch edit + Buffer_Edit *inverse_array = push_array(part, Buffer_Edit, edit_count); + Assert(inverse_array); + + char *inv_str = (char*)part->base + part->pos; + Edit_Spec spec = + file_compute_whitespace_edit(mem, file, view->cursor.pos, edits, str_base, str_size, + inverse_array, inv_str, part->max - part->pos, edit_count); + + file_do_white_batch_edit(system, models, view->file, spec, hist_normal); + } + + { + b32 all_whitespace = 0; + b32 all_space = 0; + i32 preferred_indentation; + i32 start = view->cursor.pos; + i32 hard_start = buffer_find_hard_start( + &file->state.buffer, start, &all_whitespace, &all_space, + &preferred_indentation, 4); + + view_cursor_move(view, hard_start); + } + + end_temp_memory(temp); +#endif +} + +struct Get_Link_Result{ + b32 in_link; + i32 index; +}; + +internal u32* +style_get_color(Style *style, Cpp_Token token){ + u32 *result; + if (token.flags & CPP_TFLAG_IS_KEYWORD){ + if (token.type == CPP_TOKEN_BOOLEAN_CONSTANT){ + result = &style->main.bool_constant_color; + } + else{ + result = &style->main.keyword_color; + } + } + else if(token.flags & CPP_TFLAG_PP_DIRECTIVE){ + result = &style->main.preproc_color; + } + else{ + switch (token.type){ + case CPP_TOKEN_COMMENT: + result = &style->main.comment_color; + break; + + case CPP_TOKEN_STRING_CONSTANT: + result = &style->main.str_constant_color; + break; + + case CPP_TOKEN_CHARACTER_CONSTANT: + result = &style->main.char_constant_color; + break; + + case CPP_TOKEN_INTEGER_CONSTANT: + result = &style->main.int_constant_color; + break; + + case CPP_TOKEN_FLOATING_CONSTANT: + result = &style->main.float_constant_color; + break; + + case CPP_TOKEN_INCLUDE_FILE: + result = &style->main.include_color; + break; + + default: + result = &style->main.default_color; + } + } + return result; +} + +inline f32 +view_compute_max_target_y(i32 lowest_line, i32 line_height, real32 view_height){ + real32 max_target_y = ((lowest_line+.5f)*line_height) - view_height*.5f; + return max_target_y; +} + +internal real32 +view_compute_max_target_y(View *view){ + i32 lowest_line = view_compute_lowest_line(view); + i32 line_height = view->font_height; + real32 view_height = view_compute_height(view); + real32 max_target_y = view_compute_max_target_y( + lowest_line, line_height, view_height); + return max_target_y; +} + +internal void +remeasure_file_view(System_Functions *system, View *view, i32_Rect rect){ + if (file_is_ready(view->file)){ + Relative_Scrolling relative = view_get_relative_scrolling(view); + view_measure_wraps(system, &view->models->mem.general, view); + view_cursor_move(view, view->cursor.pos); + view->preferred_x = view_get_cursor_x(view); + view_set_relative_scrolling(view, relative); + } +} + +internal void +undo_shit(System_Functions *system, View *view, UI_State *state, UI_Layout *layout, + i32 total_count, i32 undo_count, i32 scrub_max){ + + Editing_File *file = view->file; + + if (view->widget.timeline.undo_line){ + if (do_button(1, state, layout, "- Undo", 1)){ + view->widget.timeline.undo_line = 0; + } + + if (view->widget.timeline.undo_line){ + Widget_ID wid = make_id(state, 2); + i32 new_count; + if (do_undo_slider(wid, state, layout, total_count, undo_count, 0, &new_count)){ + for (i32 i = 0; i < scrub_max && new_count < undo_count; ++i){ + view_undo(system, view->models, view); + --undo_count; + } + for (i32 i = 0; i < scrub_max && new_count > undo_count; ++i){ + view_redo(system, view->models, view); + ++undo_count; + } + } + } + } + else{ + if (do_button(1, state, layout, "+ Undo", 1)){ + view->widget.timeline.undo_line = 1; + } + } + + if (view->widget.timeline.history_line){ + if (do_button(3, state, layout, "- History", 1)){ + view->widget.timeline.history_line = 0; + } + + Widget_ID wid = make_id(state, 4); + if (view->widget.timeline.history_line){ + i32 new_count; + i32 mid = ((file->state.undo.history.edit_count + file->state.undo.edit_history_cursor) >> 1); + i32 count = file->state.undo.edit_history_cursor; + if (do_undo_slider(wid, state, layout, mid, count, &file->state.undo, &new_count)){ + for (i32 i = 0; i < scrub_max && new_count < count; ++i){ + view_history_step(system, view->models, view, hist_backward); + } + for (i32 i = 0; i < scrub_max && new_count > count; ++i){ + view_history_step(system, view->models, view, hist_forward); + } + } + } + } + else{ + if (do_button(3, state, layout, "+ History", 1)){ + view->widget.timeline.history_line = 1; + } + } +} + +internal void +draw_file_view_queries(View *view, UI_State *state, UI_Layout *layout){ + Widget_ID wid; + Query_Slot *slot; + Query_Bar *bar; + i32 i = 1; + + for (slot = view->query_set.used_slot; slot != 0; slot = slot->next){ + wid = make_id(state, i++); + bar = slot->query_bar; + do_text_field(wid, state, layout, bar->prompt, bar->string); + } +} + +inline void +view_show_menu(View *fview, Command_Map *gui_map){ + fview->ui_state = {}; + fview->map_for_file = fview->map; + fview->map = gui_map; + fview->showing_ui = VUI_Menu; +} + +inline void +view_show_config(View *fview, Command_Map *gui_map){ + fview->ui_state = {}; + fview->map_for_file = fview->map; + fview->map = gui_map; + fview->showing_ui = VUI_Config; +} + +inline void +view_show_interactive(System_Functions *system, View *view, Command_Map *gui_map, + Interactive_Action action, Interactive_Interaction interaction, String query){ + + Models *models = view->models; + + view->ui_state = {}; + view->map_for_file = view->map; + view->map = gui_map; + view->showing_ui = VUI_Interactive; + view->action = action; + view->interaction = interaction; + view->finished = 0; + + copy(&view->query, query); + view->dest.str[0] = 0; + view->dest.size = 0; + + hot_directory_clean_end(&models->hot_directory); + hot_directory_reload(system, &models->hot_directory, &models->working_set); +} + +inline void +view_show_theme(View *view, Command_Map *gui_map){ + view->ui_state = {}; + view->map_for_file = view->map; + view->map = gui_map; + view->showing_ui = VUI_Theme; + view->color_mode = CV_Mode_Library; + view->color = super_color_create(0xFF000000); +} + +inline void +view_show_file(View *view, Command_Map *file_map){ + view->ui_state = {}; + if (file_map){ + view->map = file_map; + } + else{ + view->map = view->map_for_file; + } + view->showing_ui = VUI_None; +} + +internal void +interactive_view_complete(View *view){ + Models *models = view->models; + Panel *panel = view->panel; + Editing_File *old_file = view->file; + + switch (view->action){ + case IAct_Open: + delayed_open(&models->delay1, models->hot_directory.string, panel); + delayed_touch_file(&models->delay1, old_file); + break; + + case IAct_Save_As: + delayed_save_as(&models->delay1, models->hot_directory.string, panel); + break; + + case IAct_New: + if (models->hot_directory.string.size > 0 && + !char_is_slash(models->hot_directory.string.str[models->hot_directory.string.size-1])){ + delayed_new(&models->delay1, models->hot_directory.string, panel); + } + break; + + case IAct_Switch: + delayed_switch(&models->delay1, view->dest, panel); + delayed_touch_file(&models->delay1, old_file); + break; + + case IAct_Kill: + delayed_try_kill(&models->delay1, view->dest); + break; + + case IAct_Sure_To_Kill: + switch (view->user_action){ + case 0: + delayed_kill(&models->delay1, view->dest); + break; + + case 1: + break; + + case 2: + // TODO(allen): This is fishy! What if the save doesn't happen this time around? + // We need to ensure delayed acts happen in order I think. + delayed_save(&models->delay1, view->dest); + delayed_kill(&models->delay1, view->dest); + break; + } + break; + } + view_show_file(view, 0); + + // TODO(allen): This is here to prevent the key press from being passed to the + // underlying file which is a giant pain. + view->file = 0; +} + +internal void +update_highlighting(View *view){ + View *file_view = view->hot_file_view; + if (!file_view){ + view->highlight = {}; + return; + } + + Editing_File *file = file_view->file; + if (!file || !file_is_ready(file)){ + view->highlight = {}; + return; + } + + Models *models = view->models; + + Style *style = &models->style; + i32 pos = view_get_cursor_pos(file_view); + char c = buffer_get_char(&file->state.buffer, pos); + + if (c == '\r'){ + view->highlight.ids[0] = + raw_ptr_dif(&style->main.special_character_color, style); + } + + else if (file->state.tokens_complete){ + Cpp_Token_Stack *tokens = &file->state.token_stack; + Cpp_Get_Token_Result result = cpp_get_token(tokens, pos); + Cpp_Token token = tokens->tokens[result.token_index]; + if (!result.in_whitespace){ + u32 *color = style_get_color(style, token); + view->highlight.ids[0] = raw_ptr_dif(color, style); + if (token.type == CPP_TOKEN_JUNK){ + view->highlight.ids[1] = + raw_ptr_dif(&style->main.highlight_junk_color, style); + } + else if (char_is_whitespace(c)){ + view->highlight.ids[1] = + raw_ptr_dif(&style->main.highlight_white_color, style); + } + else{ + view->highlight.ids[1] = 0; + } + } + else{ + view->highlight.ids[0] = 0; + view->highlight.ids[1] = + raw_ptr_dif(&style->main.highlight_white_color, style); + } + } + + else{ + if (char_is_whitespace(c)){ + view->highlight.ids[0] = 0; + view->highlight.ids[1] = + raw_ptr_dif(&style->main.highlight_white_color, style); + } + else{ + view->highlight.ids[0] = + raw_ptr_dif(&style->main.default_color, style); + view->highlight.ids[1] = 0; + } + } + + if (file_view->show_temp_highlight){ + view->highlight.ids[2] = + raw_ptr_dif(&style->main.highlight_color, style); + view->highlight.ids[3] = + raw_ptr_dif(&style->main.at_highlight_color, style); + } + else if (file->state.paste_effect.tick_down > 0){ + view->highlight.ids[2] = + raw_ptr_dif(&style->main.paste_color, style); + view->highlight.ids[3] = 0; + } + else{ + view->highlight.ids[2] = 0; + view->highlight.ids[3] = 0; + } +} + +internal b32 +theme_library_shit(System_Functions *system, Exchange *exchange, + View *view, UI_State *state, UI_Layout *layout){ + + Models *models = view->models; + Mem_Options *mem = &models->mem; + + i32 result = 0; + + Library_UI ui; + ui.state = state; + ui.layout = layout; + + ui.fonts = models->font_set; + ui.hot_directory = &models->hot_directory; + ui.styles = &models->styles; + + Color_View_Mode mode = view->color_mode; + + i32_Rect bar_rect = ui.layout->rect; + bar_rect.x0 = bar_rect.x1 - 20; + do_scroll_bar(ui.state, bar_rect); + + ui.layout->y -= FLOOR32(view->ui_state.view_y); + ui.layout->rect.x1 -= 20; + + b32 case_sensitive = 0; + + switch (mode){ + case CV_Mode_Library: + { + do_label(ui.state, ui.layout, literal("Current Theme - Click to Edit")); + if (do_style_preview(&ui, &models->style)){ + view->color_mode = CV_Mode_Adjusting; + view->ui_state.selected = {}; + ui.state->view_y = 0; + result = 1; + } + + begin_row(ui.layout, 3); + if (ui.state->style->name.size >= 1){ + if (do_button(-2, ui.state, ui.layout, "Save", 2)){ + //style_library_add(ui.styles, ui.state->style); + } + } + else{ + do_button(-2, ui.state, ui.layout, "~Need's Name~", 2); + } + if (do_button(-3, ui.state, ui.layout, "Import", 2)){ + view->color_mode = CV_Mode_Import_File; + hot_directory_clean_end(&models->hot_directory); + hot_directory_reload(system, &models->hot_directory, &models->working_set); + } + if (do_button(-4, ui.state, ui.layout, "Export", 2)){ + view->color_mode = CV_Mode_Export; + hot_directory_clean_end(&models->hot_directory); + hot_directory_reload(system, &models->hot_directory, &models->working_set); + memset(view->import_export_check, 0, sizeof(view->import_export_check)); + } + + do_label(ui.state, ui.layout, literal("Theme Library - Click to Select")); + + i32 style_count = models->styles.count; + Style *style = models->styles.styles; + for (i32 i = 0; i < style_count; ++i, ++style){ + if (do_style_preview(&ui, style)){ + style_copy(&models->style, style); + result = 1; + } + } + }break; + + case CV_Mode_Import_File: + { + do_label(ui.state, ui.layout, literal("Current Theme")); + do_style_preview(&ui, &models->style); + + b32 file_selected = 0; + + do_label(ui.state, ui.layout, literal("Import Which File?")); + begin_row(ui.layout, 2); + if (do_button(-2, ui.state, ui.layout, "*.p4c only", 2, 1, view->p4c_only)){ + view->p4c_only = !view->p4c_only; + } + if (do_button(-3, ui.state, ui.layout, "Cancel", 2)){ + view->color_mode = CV_Mode_Library; + } + + b32 new_dir = 0; + if (do_file_list_box(system, ui.state, ui.layout, + ui.hot_directory, view->p4c_only, 1, case_sensitive, + &new_dir, &file_selected, 0)){ + result = 1; + } + + if (new_dir){ + hot_directory_reload(system, ui.hot_directory, ui.state->working_set); + } + if (file_selected){ + memset(&view->inspecting_styles, 0, sizeof(Style_Library)); + memset(view->import_export_check, 1, + sizeof(view->import_export_check)); + + view->import_file_id = exchange_request_file(exchange, + models->hot_directory.string.str, + models->hot_directory.string.size); + view->color_mode = CV_Mode_Import_Wait; + + } + }break; + + case CV_Mode_Import_Wait: + { + Style *styles = view->inspecting_styles.styles; + Data file = {}; + i32 file_max = 0; + + i32 count = 0; + i32 max = ArrayCount(view->inspecting_styles.styles); + + AllowLocal(styles); + AllowLocal(max); + + if (exchange_file_ready(exchange, view->import_file_id, + &file.data, &file.size, &file_max)){ + if (file.data){ + if (0 /* && style_library_import(file, ui.fonts, styles, max, &count) */){ + view->color_mode = CV_Mode_Import; + } + else{ + view->color_mode = CV_Mode_Library; + } + view->inspecting_styles.count = count; + } + else{ + Assert(!"this shouldn't happen!"); + } + + exchange_free_file(exchange, view->import_file_id); + } + }break; + + case CV_Mode_Export_File: + { + do_label(ui.state, ui.layout, literal("Current Theme")); + do_style_preview(&ui, &models->style); + + b32 file_selected = 0; + + do_label(ui.state, ui.layout, literal("Export File Name?")); + begin_row(ui.layout, 2); + if (do_button(-2, ui.state, ui.layout, "Finish Export", 2)){ + file_selected = 1; + } + if (do_button(-3, ui.state, ui.layout, "Cancel", 2)){ + view->color_mode = CV_Mode_Library; + } + + b32 new_dir = 0; + if (do_file_list_box(system, ui.state, ui.layout, + ui.hot_directory, 1, 1, case_sensitive, + &new_dir, &file_selected, ".p4c")){ + result = 1; + } + + if (new_dir){ + hot_directory_reload(system, + ui.hot_directory, ui.state->working_set); + } + if (file_selected){ + i32 count = ui.styles->count; + Temp_Memory temp = begin_temp_memory(&mem->part); + Style **styles = push_array(&mem->part, Style*, sizeof(Style*)*count); + + Style *style = ui.styles->styles; + b8 *export_check = view->import_export_check; + i32 export_count = 0; + for (i32 i = 0; i < count; ++i, ++style){ + if (export_check[i]){ + styles[export_count++] = style; + } + } + char *data = push_array(&mem->part, char, ui.hot_directory->string.size + 5); + String str = make_string(data, 0, ui.hot_directory->string.size + 5); + copy(&str, ui.hot_directory->string); + append(&str, make_lit_string(".p4c")); + /*style_library_export(system, exchange, mem, &target->font_set, str.str, styles, export_count);*/ + + end_temp_memory(temp); + view->color_mode = CV_Mode_Library; + } + }break; + + case CV_Mode_Import: + { + do_label(ui.state, ui.layout, literal("Current Theme")); + do_style_preview(&ui, &models->style); + + i32 style_count = view->inspecting_styles.count; + Style *styles = view->inspecting_styles.styles; + b8 *import_check = view->import_export_check; + + do_label(ui.state, ui.layout, literal("Pack")); + begin_row(ui.layout, 2); + if (do_button(-2, ui.state, ui.layout, "Finish Import", 2)){ + Style *style = styles; + for (i32 i = 0; i < style_count; ++i, ++style){ + //if (import_check[i]) style_library_add(ui.styles, style); + } + view->color_mode = CV_Mode_Library; + } + if (do_button(-3, ui.state, ui.layout, "Cancel", 2)){ + view->color_mode = CV_Mode_Library; + } + + Style *style = styles; + for (i32 i = 0; i < style_count; ++i, ++style){ + if (do_style_preview(&ui, style, import_check[i])){ + import_check[i] = !import_check[i]; + result = 1; + } + } + }break; + + case CV_Mode_Export: + { + do_label(ui.state, ui.layout, literal("Current Theme")); + do_style_preview(&ui, &models->style); + + do_label(ui.state, ui.layout, literal("Export Which Themes?")); + begin_row(ui.layout, 2); + if (do_button(-2, ui.state, ui.layout, "Export", 2)){ + view->color_mode = CV_Mode_Export_File; + } + if (do_button(-3, ui.state, ui.layout, "Cancel", 2)){ + view->color_mode = CV_Mode_Library; + } + + i32 style_count = models->styles.count; + Style *style = models->styles.styles; + b8 *export_check = view->import_export_check; + for (i32 i = 0; i < style_count; ++i, ++style){ + if (do_style_preview(&ui, style, export_check[i])){ + export_check[i] = !export_check[i]; + result = 1; + } + } + }break; + } + + return (result); +} + +internal b32 +theme_adjusting_shit(View *view, UI_State *state, UI_Layout *layout, Super_Color *color){ + update_highlighting(view); + + Models *models = view->models; + + Style *style = &models->style; + i32 result = 0; + + Color_UI ui; + ui.state = state; + ui.layout = layout; + + ui.fonts = models->font_set; + ui.global_font = &models->global_font; + ui.highlight = view->highlight; + ui.color = view->color; + ui.has_hover_color = 0; + ui.state->sub_id1_change = 0; + ui.hex_advance = font_get_max_width(ui.fonts, ui.state->font_id, "0123456789abcdefx"); + ui.palette = models->palette; + ui.palette_size = models->palette_size; + + i32_Rect bar_rect = ui.layout->rect; + bar_rect.x0 = bar_rect.x1 - 20; + do_scroll_bar(ui.state, bar_rect); + + ui.layout->y -= FLOOR32(view->ui_state.view_y); + ui.layout->rect.x1 -= 20; + + if (do_button(-1, ui.state, ui.layout, "Back to Library", 2)){ + view->color_mode = CV_Mode_Library; + ui.state->view_y = 0; + } + + do_style_name(&ui); + do_font_switch(&ui); + + do_color_adjuster(&ui, &style->main.back_color, + style->main.default_color, style->main.back_color, + "Background"); + do_color_adjuster(&ui, &style->main.margin_color, + style->main.default_color, style->main.margin_color, + "Margin"); + do_color_adjuster(&ui, &style->main.margin_hover_color, + style->main.default_color, style->main.margin_hover_color, + "Margin Hover"); + do_color_adjuster(&ui, &style->main.margin_active_color, + style->main.default_color, style->main.margin_active_color, + "Margin Active"); + + do_color_adjuster(&ui, &style->main.cursor_color, + style->main.at_cursor_color, style->main.cursor_color, + "Cursor"); + do_color_adjuster(&ui, &style->main.at_cursor_color, + style->main.at_cursor_color, style->main.cursor_color, + "Text At Cursor"); + do_color_adjuster(&ui, &style->main.mark_color, + style->main.mark_color, style->main.back_color, + "Mark"); + + do_color_adjuster(&ui, &style->main.highlight_color, + style->main.at_highlight_color, style->main.highlight_color, + "Highlight"); + do_color_adjuster(&ui, &style->main.at_highlight_color, + style->main.at_highlight_color, style->main.highlight_color, + "Text At Highlight"); + + do_color_adjuster(&ui, &style->main.default_color, + style->main.default_color, style->main.back_color, + "Text Default"); + do_color_adjuster(&ui, &style->main.comment_color, + style->main.comment_color, style->main.back_color, + "Comment"); + do_color_adjuster(&ui, &style->main.keyword_color, + style->main.keyword_color, style->main.back_color, + "Keyword"); + do_color_adjuster(&ui, &style->main.str_constant_color, + style->main.str_constant_color, style->main.back_color, + "String Constant"); + do_color_adjuster(&ui, &style->main.char_constant_color, + style->main.char_constant_color, style->main.back_color, + "Character Constant"); + do_color_adjuster(&ui, &style->main.int_constant_color, + style->main.int_constant_color, style->main.back_color, + "Integer Constant"); + do_color_adjuster(&ui, &style->main.float_constant_color, + style->main.float_constant_color, style->main.back_color, + "Float Constant"); + do_color_adjuster(&ui, &style->main.bool_constant_color, + style->main.bool_constant_color, style->main.back_color, + "Boolean Constant"); + do_color_adjuster(&ui, &style->main.preproc_color, + style->main.preproc_color, style->main.back_color, + "Preprocessor"); + do_color_adjuster(&ui, &style->main.include_color, + style->main.include_color, style->main.back_color, + "Include Constant"); + do_color_adjuster(&ui, &style->main.special_character_color, + style->main.special_character_color, style->main.back_color, + "Special Character"); + + do_color_adjuster(&ui, &style->main.highlight_junk_color, + style->main.default_color, style->main.highlight_junk_color, + "Junk Highlight"); + do_color_adjuster(&ui, &style->main.highlight_white_color, + style->main.default_color, style->main.highlight_white_color, + "Whitespace Highlight"); + + do_color_adjuster(&ui, &style->main.paste_color, + style->main.paste_color, style->main.back_color, + "Paste Color"); + + Interactive_Style *bar_style = &style->main.file_info_style; + do_color_adjuster(&ui, &bar_style->bar_color, + bar_style->base_color, bar_style->bar_color, + "Bar"); + do_color_adjuster(&ui, &bar_style->base_color, + bar_style->base_color, bar_style->bar_color, + "Bar Text"); + do_color_adjuster(&ui, &bar_style->pop1_color, + bar_style->pop1_color, bar_style->bar_color, + "Bar Pop 1"); + do_color_adjuster(&ui, &bar_style->pop2_color, + bar_style->pop2_color, bar_style->bar_color, + "Bar Pop 2"); + + *color = ui.hover_color; + + return (result); +} + +internal b32 +theme_shit(System_Functions *system, Exchange *exchange, + View *view, View *active, UI_State *state, UI_Layout *layout, Super_Color *color){ + b32 result = 0; + + if (view != active){ + view->hot_file_view = active; + } + + switch (view->color_mode){ + case CV_Mode_Library: + case CV_Mode_Import_File: + case CV_Mode_Export_File: + case CV_Mode_Import: + case CV_Mode_Export: + case CV_Mode_Import_Wait: + if (theme_library_shit(system, exchange, view, state, layout)){ + result = 1; + } + break; + + case CV_Mode_Adjusting: + if (theme_adjusting_shit(view, state, layout, color)){ + result = 1; + } + break; + } + + return(result); +} + +internal b32 +interactive_shit(System_Functions *system, View *view, UI_State *state, UI_Layout *layout){ + b32 result = 0; + b32 new_dir = 0; + b32 complete = 0; + + Models *models = view->models; + + do_label(state, layout, view->query, 1.f); + + b32 case_sensitive = 0; + + b32 input_stage = state->input_stage; + Key_Summary *keys = state->keys; + + switch (view->interaction){ + case IInt_Sys_File_List: + { + b32 is_new = (view->action == IAct_New); + + if (do_file_list_box(system, state, layout, + &models->hot_directory, 0, !is_new, case_sensitive, + &new_dir, &complete, 0)){ + result = 1; + } + if (new_dir){ + hot_directory_reload(system, &models->hot_directory, &models->working_set); + } + }break; + + case IInt_Live_File_List: + { + if (do_live_file_list_box(system, state, layout, &models->working_set, &view->dest, &complete)){ + result = 1; + } + }break; + + case IInt_Sure_To_Kill: + { + i32 action = -1; + char s_[256]; + String s = make_fixed_width_string(s_); + append(&s, view->dest); + append(&s, " has unsaved changes, kill it?"); + do_label(state, layout, s, 1.f); + + i32 id = 0; + if (do_list_option(++id, state, layout, make_lit_string("(Y)es"))){ + action = 0; + } + + if (do_list_option(++id, state, layout, make_lit_string("(N)o"))){ + action = 1; + } + + if (do_list_option(++id, state, layout, make_lit_string("(S)ave and kill"))){ + action = 2; + } + + if (action == -1 && input_stage){ + i32 key_count = keys->count; + for (i32 i = 0; i < key_count; ++i){ + Key_Event_Data key = keys->keys[i]; + switch (key.character){ + case 'y': case 'Y': action = 0; break; + case 'n': case 'N': action = 1; break; + case 's': case 'S': action = 2; break; + } + if (action == -1 && key.keycode == key_esc) action = 1; + if (action != -1) break; + } + } + + if (action != -1){ + complete = 1; + view->user_action = action; + } + }break; + } + + if (complete){ + view->finished = 1; + interactive_view_complete(view); + result= 1; + } + + return(result); +} + +internal void +menu_shit(View *view, UI_State *state, UI_Layout *layout){ + i32 id = 0; + + do_label(state, layout, literal("Menu"), 2.f); + + if (do_list_option(++id, state, layout, make_lit_string("Theme Options"))){ + view_show_theme(view, view->map); + } + + if (do_list_option(++id, state, layout, make_lit_string("Keyboard Layout Options"))){ + view_show_config(view, view->map); + } +} + +internal void +config_shit(View *view, UI_State *state, UI_Layout *layout){ + i32 id = 0; + Models *models = view->models; + + do_label(state, layout, literal("Config"), 2.f); + + if (do_checkbox_list_option(++id, state, layout, make_lit_string("Left Ctrl + Left Alt = AltGr"), + models->settings.lctrl_lalt_is_altgr)){ + models->settings.lctrl_lalt_is_altgr = !models->settings.lctrl_lalt_is_altgr; + } +} + +struct File_Bar{ + f32 pos_x, pos_y; + f32 text_shift_x, text_shift_y; + i32_Rect rect; + i16 font_id; +}; + +internal void +intbar_draw_string(Render_Target *target, File_Bar *bar, String str, u32 char_color){ + i16 font_id = bar->font_id; + + draw_string(target, font_id, str, + (i32)(bar->pos_x + bar->text_shift_x), + (i32)(bar->pos_y + bar->text_shift_y), + char_color); + bar->pos_x += font_string_width(target, font_id, str); +} + +internal void +do_file_bar(View *view, Editing_File *file, UI_Layout *layout, Render_Target *target){ + File_Bar bar; + Models *models = view->models; + Style_Font *font = &models->global_font; + i32 line_height = view->font_height; + Interactive_Style bar_style = models->style.main.file_info_style; + + u32 back_color = bar_style.bar_color; + u32 base_color = bar_style.base_color; + u32 pop1_color = bar_style.pop1_color; + u32 pop2_color = bar_style.pop2_color; + + bar.rect = layout_rect(layout, line_height + 2); + + if (target){ + bar.font_id = font->font_id; + bar.pos_x = (f32)bar.rect.x0; + bar.pos_y = (f32)bar.rect.y0; + bar.text_shift_y = 2; + bar.text_shift_x = 0; + + draw_rectangle(target, bar.rect, back_color); + intbar_draw_string(target, &bar, file->name.live_name, base_color); + intbar_draw_string(target, &bar, make_lit_string(" -"), base_color); + + if (file->state.is_loading){ + intbar_draw_string(target, &bar, make_lit_string(" loading"), base_color); + } + else{ + char line_number_space[30]; + String line_number = make_string(line_number_space, 0, 30); + append(&line_number, " L#"); + append_int_to_str(view->cursor.line, &line_number); + + intbar_draw_string(target, &bar, line_number, base_color); + + intbar_draw_string(target, &bar, make_lit_string(" -"), base_color); + + if (file->settings.dos_write_mode){ + intbar_draw_string(target, &bar, make_lit_string(" dos"), base_color); + } + else{ + intbar_draw_string(target, &bar, make_lit_string(" nix"), base_color); + } + + if (file->state.still_lexing){ + intbar_draw_string(target, &bar, make_lit_string(" parsing"), pop1_color); + } + + if (!file->settings.unimportant){ + switch (buffer_get_sync(file)){ + case SYNC_BEHIND_OS: + { + persist String out_of_sync = make_lit_string(" !"); + intbar_draw_string(target, &bar, out_of_sync, pop2_color); + }break; + + case SYNC_UNSAVED: + { + persist String out_of_sync = make_lit_string(" *"); + intbar_draw_string(target, &bar, out_of_sync, pop2_color); + }break; + } + } + } + } +} + +internal void +view_reinit_scrolling(View *view){ + Editing_File *file = view->file; + f32 w, h; + f32 cursor_x, cursor_y; + f32 target_x, target_y; + + view->reinit_scrolling = 0; + + target_x = 0; + target_y = 0; + + if (file && file_is_ready(file)){ + cursor_x = view_get_cursor_x(view); + cursor_y = view_get_cursor_y(view); + + w = view_compute_width(view); + h = view_compute_height(view); + + if (cursor_x >= target_x + w){ + target_x = (f32)(cursor_x - w*.5f); + } + + target_y = (f32)FLOOR32(cursor_y - h*.5f); + if (target_y < view->scroll_min_limit) target_y = view->scroll_min_limit; + } + + view->target_x = target_x; + view->target_y = target_y; + view->scroll_x = target_x; + view->scroll_y = target_y; + view->prev_target_x = -1000.f; + view->prev_target_y = -1000.f; +} + +internal i32 +step_file_view(System_Functions *system, Exchange *exchange, View *view, i32_Rect rect, + b32 is_active, Input_Summary *user_input){ + + Models *models = view->models; + i32 result = 0; + Editing_File *file = view->file; + + i32 widget_height = 0; + { + UI_State state = + ui_state_init(&view->widget.state, 0, user_input, + &models->style, models->global_font.font_id, models->font_set, 0, 1); + + UI_Layout layout; + begin_layout(&layout, rect); + + switch (view->widget.type){ + case FWIDG_NONE: + { + if (file && view->showing_ui == VUI_None){ + do_file_bar(view, file, &layout, 0); + } + + draw_file_view_queries(view, &state, &layout); + }break; + + case FWIDG_TIMELINES: + { + i32 scrub_max = view->scrub_max; + i32 undo_count = file->state.undo.undo.edit_count; + i32 redo_count = file->state.undo.redo.edit_count; + i32 total_count = undo_count + redo_count; + + undo_shit(system, view, &state, &layout, total_count, undo_count, scrub_max); + }break; + } + + widget_height = layout.y - rect.y0; + if (ui_finish_frame(&view->widget.state, &state, &layout, rect, 0, 0)){ + result = 1; + } + } + + view->scroll_min_limit = (f32)-widget_height; + if (view->reinit_scrolling){ + view_reinit_scrolling(view); + } + + // TODO(allen): Split this into passive step and step that depends on input + if (view->showing_ui == VUI_None && file && !file->state.is_loading){ + f32 line_height = (f32)view->font_height; + f32 cursor_y = view_get_cursor_y(view); + f32 target_y = view->target_y; + f32 max_y = view_compute_height(view) - line_height*2; + i32 lowest_line = view_compute_lowest_line(view); + f32 max_target_y = view_compute_max_target_y(lowest_line, (i32)line_height, max_y); + f32 delta_y = 3.f*line_height; + f32 extra_top = (f32)widget_height; + f32 taken_top_space = line_height + extra_top; + + if (user_input->mouse.wheel != 0){ + f32 wheel_multiplier = 3.f; + f32 delta_target_y = delta_y*user_input->mouse.wheel*wheel_multiplier; + target_y += delta_target_y; + + if (target_y < -taken_top_space) target_y = -taken_top_space; + if (target_y > max_target_y) target_y = max_target_y; + + f32 old_cursor_y = cursor_y; + if (cursor_y >= target_y + max_y) cursor_y = target_y + max_y; + if (cursor_y < target_y + taken_top_space) cursor_y = target_y + taken_top_space; + + if (cursor_y != old_cursor_y){ + view->cursor = + view_compute_cursor_from_xy(view, + view->preferred_x, + cursor_y); + } + + result = 1; + } + + if (cursor_y > target_y + max_y){ + target_y = cursor_y - max_y + delta_y; + } + if (cursor_y < target_y + taken_top_space){ + target_y = cursor_y - delta_y - taken_top_space; + } + + if (target_y > max_target_y) target_y = max_target_y; + if (target_y < -extra_top) target_y = -extra_top; + view->target_y = target_y; + + f32 cursor_x = view_get_cursor_x(view); + f32 target_x = view->target_x; + f32 max_x = view_compute_width(view); + if (cursor_x < target_x){ + target_x = (f32)Max(0, cursor_x - max_x/2); + } + else if (cursor_x >= target_x + max_x){ + target_x = (f32)(cursor_x - max_x/2); + } + + view->target_x = target_x; + + b32 is_new_target = 0; + if (view->target_x != view->prev_target_x) is_new_target = 1; + if (view->target_y != view->prev_target_y) is_new_target = 1; + + if (view->models->scroll_rule( + view->target_x, view->target_y, + &view->scroll_x, &view->scroll_y, + (view->id) + 1, is_new_target)){ + result = 1; + } + + view->prev_target_x = view->target_x; + view->prev_target_y = view->target_y; + + if (file->state.paste_effect.tick_down > 0){ + --file->state.paste_effect.tick_down; + result = 1; + } + + if (user_input->mouse.press_l && is_active){ + f32 max_y = view_compute_height(view); + f32 rx = (f32)(user_input->mouse.x - rect.x0); + f32 ry = (f32)(user_input->mouse.y - rect.y0); + + if (ry >= extra_top){ + view_set_widget(view, FWIDG_NONE); + if (rx >= 0 && rx < max_x && ry >= 0 && ry < max_y){ + view_cursor_move(view, rx + view->scroll_x, ry + view->scroll_y, 1); + view->mode = {}; + } + } + result = 1; + } + + if (!is_active) view_set_widget(view, FWIDG_NONE); + } + + { + UI_State state = + ui_state_init(&view->ui_state, 0, user_input, + &models->style, models->global_font.font_id, models->font_set, &models->working_set, 1); + + UI_Layout layout; + begin_layout(&layout, rect); + + Super_Color color = {}; + + switch (view->showing_ui){ + case VUI_None: break; + case VUI_Theme: + { + theme_shit(system, exchange, view, 0, &state, &layout, &color); + }break; + case VUI_Interactive: + { + if (interactive_shit(system, view, &state, &layout)){ + result = 1; + } + }break; + case VUI_Menu: + { + menu_shit(view, &state, &layout); + }break; + case VUI_Config: + { + config_shit(view, &state, &layout); + }break; + } + + i32 did_activation = 0; + if (ui_finish_frame(&view->ui_state, &state, &layout, rect, 0, &did_activation)){ + result = 1; + } + if (did_activation){ + if (view->showing_ui == VUI_Theme){ + view->color = color; + result = 1; + } + } + } + + return(result); +} + +internal i32 +draw_file_loaded(View *view, i32_Rect rect, b32 is_active, Render_Target *target){ + Models *models = view->models; + Editing_File *file = view->file; + Style *style = &models->style; + i32 line_height = view->font_height; + + i32 max_x = rect.x1 - rect.x0; + i32 max_y = rect.y1 - rect.y0 + line_height; + + Assert(file && !file->state.is_dummy && buffer_good(&file->state.buffer)); + + b32 tokens_use = 0; + Cpp_Token_Stack token_stack = {}; + if (file){ + tokens_use = file->state.tokens_complete && (file->state.token_stack.count > 0); + token_stack = file->state.token_stack; + } + + Partition *part = &models->mem.part; + + Temp_Memory temp = begin_temp_memory(part); + + partition_align(part, 4); + i32 max = partition_remaining(part) / sizeof(Buffer_Render_Item); + Buffer_Render_Item *items = push_array(part, Buffer_Render_Item, max); + + i16 font_id = models->global_font.font_id; + Render_Font *font = get_font_info(models->font_set, font_id)->font; + float *advance_data = 0; + if (font) advance_data = font->advance_data; + + i32 count; + Full_Cursor render_cursor; + Buffer_Render_Options opts = {}; + + f32 *wraps = view->line_wrap_y; + f32 scroll_x = view->scroll_x; + f32 scroll_y = view->scroll_y; + + { + render_cursor = buffer_get_start_cursor(&file->state.buffer, wraps, scroll_y, + !view->unwrapped_lines, (f32)max_x, advance_data, (f32)line_height); + + view->scroll_i = render_cursor.pos; + + buffer_get_render_data(&file->state.buffer, items, max, &count, + (f32)rect.x0, (f32)rect.y0, + scroll_x, scroll_y, render_cursor, + !view->unwrapped_lines, + (f32)max_x, (f32)max_y, + advance_data, (f32)line_height, + opts); + } + + Assert(count > 0); + + i32 cursor_begin, cursor_end; + u32 cursor_color, at_cursor_color; + if (view->show_temp_highlight){ + cursor_begin = view->temp_highlight.pos; + cursor_end = view->temp_highlight_end_pos; + cursor_color = style->main.highlight_color; + at_cursor_color = style->main.at_highlight_color; + } + else{ + cursor_begin = view->cursor.pos; + cursor_end = cursor_begin + 1; + cursor_color = style->main.cursor_color; + at_cursor_color = style->main.at_cursor_color; + } + + i32 token_i = 0; + u32 main_color = style->main.default_color; + u32 special_color = style->main.special_character_color; + if (tokens_use){ + Cpp_Get_Token_Result result = cpp_get_token(&token_stack, items->index); + main_color = *style_get_color(style, token_stack.tokens[result.token_index]); + token_i = result.token_index + 1; + } + + u32 mark_color = style->main.mark_color; + Buffer_Render_Item *item = items; + i32 prev_ind = -1; + u32 highlight_color = 0; + u32 highlight_this_color = 0; + + for (i32 i = 0; i < count; ++i, ++item){ + i32 ind = item->index; + highlight_this_color = 0; + if (tokens_use && ind != prev_ind){ + Cpp_Token current_token = token_stack.tokens[token_i-1]; + + if (token_i < token_stack.count){ + if (ind >= token_stack.tokens[token_i].start){ + main_color = + *style_get_color(style, token_stack.tokens[token_i]); + current_token = token_stack.tokens[token_i]; + ++token_i; + } + else if (ind >= current_token.start + current_token.size){ + main_color = 0xFFFFFFFF; + } + } + + if (current_token.type == CPP_TOKEN_JUNK && + i >= current_token.start && i < current_token.start + current_token.size){ + highlight_color = style->main.highlight_junk_color; + } + else{ + highlight_color = 0; + } + } + + u32 char_color = main_color; + if (item->flags & BRFlag_Special_Character) char_color = special_color; + + f32_Rect char_rect = f32R(item->x0, item->y0, item->x1, item->y1); + if (view->show_whitespace && highlight_color == 0 && + char_is_whitespace((char)item->glyphid)){ + highlight_this_color = style->main.highlight_white_color; + } + else{ + highlight_this_color = highlight_color; + } + + if (cursor_begin <= ind && ind < cursor_end && (ind != prev_ind || cursor_begin < ind)){ + if (is_active){ + draw_rectangle(target, char_rect, cursor_color); + char_color = at_cursor_color; + } + else{ + if (!view->show_temp_highlight){ + draw_rectangle_outline(target, char_rect, cursor_color); + } + } + } + else if (highlight_this_color){ + draw_rectangle(target, char_rect, highlight_this_color); + } + + u32 fade_color = 0xFFFF00FF; + f32 fade_amount = 0.f; + + if (file->state.paste_effect.tick_down > 0 && + file->state.paste_effect.start <= ind && + ind < file->state.paste_effect.end){ + fade_color = file->state.paste_effect.color; + fade_amount = (f32)(file->state.paste_effect.tick_down) / file->state.paste_effect.tick_max; + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + if (ind == view->mark && prev_ind != ind){ + draw_rectangle_outline(target, char_rect, mark_color); + } + if (item->glyphid != 0){ + font_draw_glyph(target, font_id, (u8)item->glyphid, + item->x0, item->y0, char_color); + } + prev_ind = ind; + } + + end_temp_memory(temp); + + return(0); +} + +internal i32 +draw_file_view(System_Functions *system, Exchange *exchange, + View *view, View *active, i32_Rect rect, b32 is_active, + Render_Target *target, Input_Summary *user_input){ + + Models *models = view->models; + Editing_File *file = view->file; + i32 result = 0; + + i32 widget_height = 0; + { + UI_State state = + ui_state_init(&view->widget.state, target, 0, + &models->style, models->global_font.font_id, models->font_set, 0, 0); + + UI_Layout layout; + begin_layout(&layout, rect); + + switch (view->widget.type){ + case FWIDG_NONE: + { + if (file && view->showing_ui == VUI_None){ + do_file_bar(view, file, &layout, target); + } + + draw_file_view_queries(view, &state, &layout); + }break; + + case FWIDG_TIMELINES: + { + if (file){ + i32 undo_count = file->state.undo.undo.edit_count; + i32 redo_count = file->state.undo.redo.edit_count; + i32 total_count = undo_count + redo_count; + undo_shit(0, view, &state, &layout, total_count, undo_count, 0); + } + else{ + view->widget.type = FWIDG_NONE; + } + }break; + } + + widget_height = layout.y - rect.y0; + ui_finish_frame(&view->widget.state, &state, &layout, rect, 0, 0); + } + view->scroll_min_limit = (f32)-widget_height; + + { + rect.y0 += widget_height; + target->push_clip(target, rect); + + UI_State state = + ui_state_init(&view->ui_state, target, user_input, + &models->style, models->global_font.font_id, models->font_set, &models->working_set, 0); + + UI_Layout layout; + begin_layout(&layout, rect); + + rect.y0 -= widget_height; + + Super_Color color = {}; + + switch (view->showing_ui){ + case VUI_None: + { + if (file && file_is_ready(file)){ + if (view->reinit_scrolling){ + view_reinit_scrolling(view); + } + result = draw_file_loaded(view, rect, is_active, target); + } + }break; + + case VUI_Theme: + { + theme_shit(system, exchange, view, active, &state, &layout, &color); + }break; + + case VUI_Interactive: + { + interactive_shit(system, view, &state, &layout); + }break; + case VUI_Menu: + { + menu_shit(view, &state, &layout); + }break; + case VUI_Config: + { + config_shit(view, &state, &layout); + }break; + } + + ui_finish_frame(&view->ui_state, &state, &layout, rect, 0, 0); + + target->pop_clip(target); + } + + + return (result); +} + +// TODO(allen): Passing this hook and app pointer is a hack. It can go as soon as we start +// initializing files independently of setting them to views. +internal void +kill_file(System_Functions *system, Exchange *exchange, Models *models, Editing_File *file, + Hook_Function *open_hook, Application_Links *app){ + File_Node *node, *used; + + file_close(system, &models->mem.general, file); + working_set_free_file(&models->working_set, file); + + used = &models->working_set.used_sentinel; + node = used->next; + + for (View_Iter iter = file_view_iter_init(&models->layout, file, 0); + file_view_iter_good(iter); + iter = file_view_iter_next(iter)){ + if (node != used){ + iter.view->file = 0; + view_set_file(iter.view, (Editing_File*)node, models, system, open_hook, app, 0); + node = node->next; + } + else{ + iter.view->file = 0; + view_set_file(iter.view, 0, models, system, open_hook, app, 0); + } + } +} + +inline void +free_file_view(View *view){ + if (view->line_wrap_y) + general_memory_free(&view->models->mem.general, view->line_wrap_y); +} + +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); +} + +inline void +view_change_size(System_Functions *system, General_Memory *general, View *view){ + if (view->file){ + view_measure_wraps(system, general, view); + view->cursor = view_compute_cursor_from_pos(view, view->cursor.pos); + } +} + +struct Live_Views{ + View *views; + View free_sentinel; + i32 count, max; +}; + +internal View_And_ID +live_set_alloc_view(Live_Views *live_set, Panel *panel, Models *models){ + View_And_ID result = {}; + + Assert(live_set->count < live_set->max); + ++live_set->count; + + result.view = live_set->free_sentinel.next; + result.id = (i32)(result.view - live_set->views); + + dll_remove(result.view); + memset(result.view, 0, sizeof(View)); + result.view->id = result.id; + + result.view->in_use = 1; + panel->view = result.view; + result.view->panel = panel; + + result.view->models = models; + result.view->scrub_max = 1; + + // TODO(allen): Make "interactive" mode customizable just like the query bars! + result.view->query = make_fixed_width_string(result.view->query_); + result.view->dest = make_fixed_width_string(result.view->dest_); + + init_query_set(&result.view->query_set); + + return(result); +} + +inline void +live_set_free_view(System_Functions *system, Exchange *exchange, Live_Views *live_set, View *view){ + Assert(live_set->count > 0); + --live_set->count; + free_file_view(view); + dll_insert(&live_set->free_sentinel, view); + view->in_use = 0; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_font_set.cpp b/test_data/lots_of_files/4ed_font_set.cpp new file mode 100644 index 0000000..e041092 --- /dev/null +++ b/test_data/lots_of_files/4ed_font_set.cpp @@ -0,0 +1,230 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 18.12.2015 + * + * Font set for 4coder + * + */ + +// TOP + +inline u32 +font_hash(String name){ + u32 x = 5381; + char *p = name.str; + for (i32 i = 0; i < name.size; ++i, ++p){ + x = ((x << 5) + x) ^ (*p); + } + return(x); +} + +inline void +font__insert(Font_Slot *pos, Font_Slot *slot){ + Font_Slot *nex; + nex = pos->next; + + slot->next = nex; + slot->prev = pos; + nex->prev = slot; + pos->next = slot; +} + +inline void +font__remove(Font_Slot *slot){ + Font_Slot *n, *p; + n = slot->next; + p = slot->prev; + + p->next = n; + n->prev = p; +} + +internal void +font_set_init(Font_Set *set, Partition *partition, i32 max, i16 live_max){ + partition_align(partition, 8); + set->info = push_array(partition, Font_Info, max); + partition_align(partition, 8); + set->entries = push_array(partition, Font_Table_Entry, max); + set->count = 0; + set->max = max; + + partition_align(partition, 8); + set->font_block = push_block(partition, live_max*(sizeof(Render_Font) + sizeof(Font_Slot))); + + set->free_slots = {}; + set->used_slots = {}; + + dll_init_sentinel(&set->free_slots); + dll_init_sentinel(&set->used_slots); + + char *ptr = (char*)set->font_block; + for (i32 i = 0; i < live_max; ++i){ + dll_insert(&set->free_slots, (Font_Slot*)ptr); + ptr += sizeof(Font_Slot) + sizeof(Render_Font); + } + + set->font_used_flags = push_array(partition, b8, max); + set->live_max = live_max; +} + +internal b32 +font_set_can_add(Font_Set *set){ + b32 result = 0; + if (set->count*8 < set->max*7) result = 1; + return(result); +} + +internal void +font_set_add_hash(Font_Set *set, String name, i16 font_id){ + Font_Table_Entry entry; + entry.hash = font_hash(name); + entry.name = name; + entry.font_id = font_id; + + u32 i, j; + i = entry.hash % set->max; + j = i - 1; + if (i <= 1) j += set->max; + + for (; i != j; ++i){ + if (i == set->max) i = 0; + if (set->entries[i].font_id == 0){ + set->entries[i] = entry; + break; + } + } + + Assert(i != j); +} + +inline b32 +font_set_can_load(Font_Set *set){ + b32 result = (set->free_slots.next != &set->free_slots); + return(result); +} + +internal void +font_set_load(Partition *partition, Font_Set *set, i16 font_id){ + Font_Info *info = get_font_info(set, font_id); + Font_Slot *slot = set->free_slots.next; + Assert(slot != &set->free_slots); + font__remove(slot); + font__insert(&set->used_slots, slot); + + Render_Font *font = (Render_Font*)(slot + 1); + set->font_load(font, info->filename.str, info->pt_size, 4); + info->font = font; + slot->font_id = font_id; +} + +internal void +font_set_evict_lru(Font_Set *set){ + Font_Slot *slot = set->used_slots.prev; + Assert(slot != &set->used_slots); + + i16 font_id = slot->font_id; + Font_Info *info = get_font_info(set, font_id); + Assert(((Font_Slot*)info->font) - 1 == slot); + + set->release_font(info->font); + + info->font = 0; + slot->font_id = 0; + font__remove(slot); + font__insert(&set->free_slots, slot); +} + +internal void +font_set_use(Partition *partition, Font_Set *set, i16 font_id){ + b8 already_used; + already_used = set->font_used_flags[font_id-1]; + + if (!already_used){ + if (set->used_this_frame < set->live_max){ + ++set->used_this_frame; + set->font_used_flags[font_id-1] = 1; + already_used = 1; + } + } + + if (already_used){ + // TODO(allen): optimize if you don't mind!!!! + Font_Info *info = get_font_info(set, font_id); + Font_Slot *slot; + if (info->font == 0){ + if (!font_set_can_load(set)){ + font_set_evict_lru(set); + } + font_set_load(partition, set, font_id); + } + slot = ((Font_Slot*)info->font) - 1; + + font__remove(slot); + font__insert(&set->used_slots, slot); + } +} + +internal b32 +font_set_add(Partition *partition, Font_Set *set, + String filename, String name, i32 pt_size){ + b32 result = 0; + if (font_set_can_add(set)){ + i16 font_id = (i16)(++set->count); + Font_Info *info = get_font_info(set, font_id); + info->filename = filename; + info->name = name; + info->pt_size = pt_size; + set->font_info_load(partition, filename.str, pt_size, &info->height, &info->advance); + font_set_add_hash(set, name, font_id); + + if (font_set_can_load(set)){ + font_set_load(partition, set, font_id); + } + + result = 1; + } + return(result); +} + +internal b32 +font_set_find_pos(Font_Set *set, String name, u32 *position){ + b32 result; + u32 hash, i, j; + hash = font_hash(name); + i = hash % set->max; + j = i - 1; + if (j <= 1) j += set->max; + + result = 0; + Font_Table_Entry *entry; + for (; i != j; ++i){ + if (i == set->max) i = 0; + entry = set->entries + i; + if (entry->hash == hash){ + if (match(name, entry->name)){ + result = 1; + *position = i; + break; + } + } + } + + return(result); +} + +internal b32 +font_set_extract(Font_Set *set, String name, i16 *font_id){ + b32 result; + u32 position; + + result = font_set_find_pos(set, name, &position); + if (result){ + *font_id = set->entries[position].font_id; + } + + return(result); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_gui.cpp b/test_data/lots_of_files/4ed_gui.cpp new file mode 100644 index 0000000..59b4baf --- /dev/null +++ b/test_data/lots_of_files/4ed_gui.cpp @@ -0,0 +1,2227 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 20.02.2016 + * + * GUI system for 4coder + * + */ + +// TOP + +struct Query_Slot{ + Query_Slot *next; + Query_Bar *query_bar; +}; + +struct Query_Set{ + Query_Slot slots[8]; + Query_Slot *free_slot; + Query_Slot *used_slot; +}; + +internal void +init_query_set(Query_Set *set){ + Query_Slot *slot = set->slots; + int i; + + set->free_slot = slot; + set->used_slot = 0; + for (i = 0; i+1 < ArrayCount(set->slots); ++i, ++slot){ + slot->next = slot + 1; + } +} + +internal Query_Slot* +alloc_query_slot(Query_Set *set){ + Query_Slot *slot = set->free_slot; + if (slot != 0){ + set->free_slot = slot->next; + slot->next = set->used_slot; + set->used_slot = slot; + } + return(slot); +} + +internal void +free_query_slot(Query_Set *set, Query_Bar *match_bar){ + Query_Slot *slot = 0, *prev = 0; + + for (slot = set->used_slot; slot != 0; slot = slot->next){ + if (slot->query_bar == match_bar) break; + prev = slot; + } + + if (slot){ + if (prev){ + prev->next = slot->next; + } + else{ + set->used_slot = slot->next; + } + slot->next = set->free_slot; + set->free_slot = slot; + } +} + +struct Single_Line_Input_Step{ + b8 hit_newline; + b8 hit_ctrl_newline; + b8 hit_a_character; + b8 hit_backspace; + b8 hit_esc; + b8 made_a_change; + b8 did_command; + b8 no_file_match; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + b32 fast_folder_select; + b32 try_to_match; + b32 case_sensitive; +}; + +internal Single_Line_Input_Step +app_single_line_input_core(System_Functions *system, Working_Set *working_set, + Key_Event_Data key, Single_Line_Mode mode){ + Single_Line_Input_Step result = {}; + + if (key.keycode == key_back){ + result.hit_backspace = 1; + if (mode.string->size > 0){ + result.made_a_change = 1; + --mode.string->size; + switch (mode.type){ + case SINGLE_LINE_STRING: + mode.string->str[mode.string->size] = 0; break; + + case SINGLE_LINE_FILE: + { + char end_character = mode.string->str[mode.string->size]; + if (char_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + mode.string->str[mode.string->size] = 0; + hot_directory_set(system, mode.hot_directory, *mode.string, working_set); + } + else{ + mode.string->str[mode.string->size] = 0; + } + }break; + } + } + } + + else if (key.character == '\n' || key.character == '\t'){ + result.made_a_change = 1; + if (key.modifiers[MDFR_CONTROL_INDEX] || + key.modifiers[MDFR_ALT_INDEX]){ + result.hit_ctrl_newline = 1; + } + else{ + result.hit_newline = 1; + if (mode.fast_folder_select){ + Hot_Directory_Match match; + char front_name_space[256]; + String front_name = make_fixed_width_string(front_name_space); + get_front_of_directory(&front_name, *mode.string); + + match = + hot_directory_first_match(mode.hot_directory, front_name, 1, 1, mode.case_sensitive); + + if (mode.try_to_match && !match.filename.str){ + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0, mode.case_sensitive); + } + if (match.filename.str){ + if (match.is_folder){ + set_last_folder(mode.string, match.filename, mode.hot_directory->slash); + hot_directory_set(system, mode.hot_directory, *mode.string, working_set); + result.hit_newline = 0; + } + else{ + if (mode.try_to_match){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + append(mode.string, match.filename); + } + } + } + else{ + if (mode.try_to_match){ + result.no_file_match = 1; + } + } + } + } + } + + else if (key.keycode == key_esc){ + result.hit_esc = 1; + result.made_a_change = 1; + } + + else if (key.character){ + result.hit_a_character = 1; + if (!key.modifiers[MDFR_CONTROL_INDEX] && + !key.modifiers[MDFR_ALT_INDEX]){ + if (mode.string->size+1 < mode.string->memory_size){ + u8 new_character = (u8)key.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + mode.string->str[mode.string->size] = 0; + if (mode.type == SINGLE_LINE_FILE && char_is_slash(new_character)){ + hot_directory_set(system, mode.hot_directory, *mode.string, working_set); + } + result.made_a_change = 1; + } + } + else{ + result.did_command = 1; + result.made_a_change = 1; + } + } + + return result; +} + +inline Single_Line_Input_Step +app_single_line_input_step(System_Functions *system, Key_Event_Data key, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(system, 0, key, mode); +} + +inline Single_Line_Input_Step +app_single_file_input_step(System_Functions *system, + Working_Set *working_set, Key_Event_Data key, + String *string, Hot_Directory *hot_directory, + b32 fast_folder_select, b32 try_to_match, b32 case_sensitive){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.fast_folder_select = fast_folder_select; + mode.try_to_match = try_to_match; + mode.case_sensitive = case_sensitive; + return app_single_line_input_core(system, working_set, key, mode); +} + +inline Single_Line_Input_Step +app_single_number_input_step(System_Functions *system, Key_Event_Data key, String *string){ + Single_Line_Input_Step result = {}; + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + + char c = (char)key.character; + if (c == 0 || c == '\n' || char_is_numeric(c)) + result = app_single_line_input_core(system, 0, key, mode); + return result; +} + +struct Widget_ID{ + i32 id; + i32 sub_id0; + i32 sub_id1; + i32 sub_id2; +}; + +inline b32 +widget_match(Widget_ID s1, Widget_ID s2){ + return (s1.id == s2.id && s1.sub_id0 == s2.sub_id0 && + s1.sub_id1 == s2.sub_id1 && s1.sub_id2 == s2.sub_id2); +} + +struct UI_State{ + Render_Target *target; + Style *style; + Font_Set *font_set; + Mouse_State *mouse; + Key_Summary *keys; + Working_Set *working_set; + i16 font_id; + + Widget_ID selected, hover, hot; + b32 activate_me; + b32 redraw; + b32 input_stage; + i32 sub_id1_change; + + f32 height, view_y; +}; + +inline Widget_ID +make_id(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.id = id; + return r; +} + +inline Widget_ID +make_sub0(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id0 = id; + return r; +} + +inline Widget_ID +make_sub1(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id1 = id; + return r; +} + +inline Widget_ID +make_sub2(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id2 = id; + return r; +} + +inline b32 +is_selected(UI_State *state, Widget_ID id){ + return widget_match(state->selected, id); +} + +inline b32 +is_hot(UI_State *state, Widget_ID id){ + return widget_match(state->hot, id); +} + +inline b32 +is_hover(UI_State *state, Widget_ID id){ + return widget_match(state->hover, id); +} + +struct UI_Layout{ + i32 row_count; + i32 row_item_width; + i32 row_max_item_height; + + i32_Rect rect; + i32 x, y; +}; + +struct UI_Layout_Restore{ + UI_Layout layout; + UI_Layout *dest; +}; + +inline void +begin_layout(UI_Layout *layout, i32_Rect rect){ + layout->rect = rect; + layout->x = rect.x0; + layout->y = rect.y0; + layout->row_count = 0; + layout->row_max_item_height = 0; +} + +inline void +begin_row(UI_Layout *layout, i32 count){ + layout->row_count = count; + layout->row_item_width = (layout->rect.x1 - layout->x) / count; +} + +inline i32_Rect +layout_rect(UI_Layout *layout, i32 height){ + i32_Rect rect; + rect.x0 = layout->x; + rect.y0 = layout->y; + rect.x1 = rect.x0; + rect.y1 = rect.y0 + height; + if (layout->row_count > 0){ + --layout->row_count; + rect.x1 = rect.x0 + layout->row_item_width; + layout->x += layout->row_item_width; + layout->row_max_item_height = Max(height, layout->row_max_item_height); + } + if (layout->row_count == 0){ + rect.x1 = layout->rect.x1; + layout->row_max_item_height = Max(height, layout->row_max_item_height); + layout->y += layout->row_max_item_height; + layout->x = layout->rect.x0; + layout->row_max_item_height = 0; + } + return rect; +} + +inline UI_Layout_Restore +begin_sub_layout(UI_Layout *layout, i32_Rect area){ + UI_Layout_Restore restore; + restore.layout = *layout; + restore.dest = layout; + begin_layout(layout, area); + return restore; +} + +inline void +end_sub_layout(UI_Layout_Restore restore){ + *restore.dest = restore.layout; +} + +struct UI_Style{ + u32 dark, dim, bright; + u32 base, pop1, pop2; +}; + +internal UI_Style +get_ui_style(Style *style){ + UI_Style ui_style; + ui_style.dark = style->main.back_color; + ui_style.dim = style->main.margin_color; + ui_style.bright = style->main.margin_active_color; + ui_style.base = style->main.default_color; + ui_style.pop1 = style->main.file_info_style.pop1_color; + ui_style.pop2 = style->main.file_info_style.pop2_color; + return ui_style; +} + +internal UI_Style +get_ui_style_upper(Style *style){ + UI_Style ui_style; + ui_style.dark = style->main.margin_color; + ui_style.dim = style->main.margin_hover_color; + ui_style.bright = style->main.margin_active_color; + ui_style.base = style->main.default_color; + ui_style.pop1 = style->main.file_info_style.pop1_color; + ui_style.pop2 = style->main.file_info_style.pop2_color; + return ui_style; +} + +inline void +get_colors(UI_State *state, u32 *back, u32 *fore, Widget_ID wid, UI_Style style){ + bool32 hover = is_hover(state, wid); + bool32 hot = is_hot(state, wid); + i32 level = hot + hover; + switch (level){ + case 2: + *back = style.bright; + *fore = style.dark; + break; + case 1: + *back = style.dim; + *fore = style.bright; + break; + case 0: + *back = style.dark; + *fore = style.bright; + break; + } +} + +inline void +get_pop_color(UI_State *state, u32 *pop, Widget_ID wid, UI_Style style){ + bool32 hover = is_hover(state, wid); + bool32 hot = is_hot(state, wid); + i32 level = hot + hover; + switch (level){ + case 2: + *pop = style.pop1; + break; + case 1: + *pop = style.pop1; + break; + case 0: + *pop = style.pop1; + break; + } +} + +internal UI_State +ui_state_init(UI_State *state_in, Render_Target *target, Input_Summary *user_input, + Style *style, i16 font_id, Font_Set *font_set, Working_Set *working_set, + b32 input_stage){ + UI_State state = {}; + state.target = target; + state.style = style; + state.font_set = font_set; + state.font_id = font_id; + state.working_set = working_set; + if (user_input){ + state.mouse = &user_input->mouse; + state.keys = &user_input->keys; + } + state.selected = state_in->selected; + state.hot = state_in->hot; + if (input_stage) state.hover = {}; + else state.hover = state_in->hover; + state.redraw = 0; + state.activate_me = 0; + state.input_stage = input_stage; + state.height = state_in->height; + state.view_y = state_in->view_y; + return state; +} + +inline b32 +ui_state_match(UI_State a, UI_State b){ + return (widget_match(a.selected, b.selected) && + widget_match(a.hot, b.hot) && + widget_match(a.hover, b.hover)); +} + +internal b32 +ui_finish_frame(UI_State *persist_state, UI_State *state, UI_Layout *layout, i32_Rect rect, + b32 do_wheel, b32 *did_activation){ + b32 result = 0; + f32 h = layout->y + persist_state->view_y - rect.y0; + f32 max_y = h - (rect.y1 - rect.y0); + + persist_state->height = h; + persist_state->view_y = state->view_y; + + if (state->input_stage){ + Mouse_State *mouse = state->mouse; + Font_Set *font_set = state->font_set; + + if (mouse->wheel != 0 && do_wheel){ + i32 height = get_font_info(font_set, state->font_id)->height; + persist_state->view_y += mouse->wheel*height; + result = 1; + } + if (mouse->release_l && widget_match(state->hot, state->hover)){ + if (did_activation) *did_activation = 1; + if (state->activate_me){ + state->selected = state->hot; + } + } + if (!mouse->l && !mouse->r){ + state->hot = {}; + } + + if (!ui_state_match(*persist_state, *state) || state->redraw){ + result = 1; + } + + *persist_state = *state; + } + + if (persist_state->view_y >= max_y) persist_state->view_y = max_y; + if (persist_state->view_y < 0) persist_state->view_y = 0; + + return result; +} + +internal b32 +ui_do_button_input(UI_State *state, i32_Rect rect, Widget_ID id, bool32 activate, bool32 *right = 0){ + b32 result = 0; + Mouse_State *mouse = state->mouse; + b32 hover = hit_check(mouse->x, mouse->y, rect); + if (hover){ + state->hover = id; + if (activate) state->activate_me = 1; + if (mouse->press_l || (mouse->press_r && right)) state->hot = id; + if (mouse->l && mouse->r) state->hot = {}; + } + bool32 is_match = is_hot(state, id); + if (mouse->release_l && is_match){ + if (hover) result = 1; + state->redraw = 1; + } + if (right && mouse->release_r && is_match){ + if (hover) *right = 1; + state->redraw = 1; + } + return result; +} + +internal bool32 +ui_do_subdivided_button_input(UI_State *state, i32_Rect rect, i32 parts, Widget_ID id, bool32 activate, i32 *indx_out, bool32 *right = 0){ + bool32 result = 0; + real32 x0, x1; + i32_Rect sub_rect; + Widget_ID sub_widg = id; + real32 sub_width = (rect.x1 - rect.x0) / (real32)parts; + sub_rect.y0 = rect.y0; + sub_rect.y1 = rect.y1; + x1 = (real32)rect.x0; + + for (i32 i = 0; i < parts; ++i){ + x0 = x1; + x1 = x1 + sub_width; + sub_rect.x0 = TRUNC32(x0); + sub_rect.x1 = TRUNC32(x1); + sub_widg.sub_id2 = i; + if (ui_do_button_input(state, sub_rect, sub_widg, activate, right)){ + *indx_out = i; + break; + } + } + + return result; +} + +internal real32 +ui_do_vscroll_input(UI_State *state, i32_Rect top, i32_Rect bottom, i32_Rect slider, + Widget_ID id, real32 val, real32 step_amount, + real32 smin, real32 smax, real32 vmin, real32 vmax){ + Mouse_State *mouse = state->mouse; + i32 mx = mouse->x; + i32 my = mouse->y; + if (hit_check(mx, my, top)){ + state->hover = id; + state->hover.sub_id2 = 1; + } + if (hit_check(mx, my, bottom)){ + state->hover = id; + state->hover.sub_id2 = 2; + } + if (hit_check(mx, my, slider)){ + state->hover = id; + state->hover.sub_id2 = 3; + } + if (mouse->press_l) state->hot = state->hover; + if (id.id == state->hot.id){ + if (mouse->release_l){ + Widget_ID wid1, wid2; + wid1 = wid2 = id; + wid1.sub_id2 = 1; + wid2.sub_id2 = 2; + if (state->hot.sub_id2 == 1 && is_hover(state, wid1)) val -= step_amount; + if (state->hot.sub_id2 == 2 && is_hover(state, wid2)) val += step_amount; + state->redraw = 1; + } + if (state->hot.sub_id2 == 3){ + f32 S, L; + S = (f32)mouse->y - (slider.y1 - slider.y0) / 2; + if (S < smin) S = smin; + if (S > smax) S = smax; + L = unlerp(smin, S, smax); + val = lerp(vmin, L, vmax); + state->redraw = 1; + } + } + return val; +} + +internal b32 +ui_do_text_field_input(UI_State *state, String *str){ + b32 result = 0; + Key_Summary *keys = state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Event_Data key = get_single_key(keys, key_i); + char c = (char)key.character; + if (char_is_basic(c) && str->size < str->memory_size-1){ + str->str[str->size++] = c; + str->str[str->size] = 0; + } + else if (c == '\n'){ + result = 1; + } + else if (key.keycode == key_back && str->size > 0){ + str->str[--str->size] = 0; + } + } + return result; +} + +internal b32 +ui_do_file_field_input(System_Functions *system, UI_State *state, + Hot_Directory *hot_dir, b32 try_to_match, b32 case_sensitive){ + Key_Event_Data key; + Single_Line_Input_Step step; + String *str = &hot_dir->string; + Key_Summary *keys = state->keys; + i32 key_i; + b32 result = 0; + + terminate_with_null(str); + + for (key_i = 0; key_i < keys->count; ++key_i){ + key = get_single_key(keys, key_i); + step = + app_single_file_input_step(system, state->working_set, key, str, + hot_dir, 1, try_to_match, case_sensitive); + if ((step.hit_newline || step.hit_ctrl_newline) && !step.no_file_match) result = 1; + } + return result; +} + +internal b32 +ui_do_line_field_input(System_Functions *system, + UI_State *state, String *string){ + b32 result = 0; + Key_Summary *keys = state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Event_Data key = get_single_key(keys, key_i); + terminate_with_null(string); + Single_Line_Input_Step step = + app_single_line_input_step(system, key, string); + if (step.hit_newline || step.hit_ctrl_newline) result = 1; + } + return result; +} + +internal b32 +ui_do_slider_input(UI_State *state, i32_Rect rect, Widget_ID wid, + real32 min, real32 max, real32 *v){ + b32 result = 0; + ui_do_button_input(state, rect, wid, 0); + Mouse_State *mouse = state->mouse; + if (is_hot(state, wid)){ + result = 1; + *v = unlerp(min, (f32)mouse->x, max); + state->redraw = 1; + } + return result; +} + +internal bool32 +do_text_field(Widget_ID wid, UI_State *state, UI_Layout *layout, String prompt, String dest){ + b32 result = 0; + i32 character_h = get_font_info(state->font_set, state->font_id)->height; + + i32_Rect rect = layout_rect(layout, character_h); + + if (state->input_stage){ + ui_do_button_input(state, rect, wid, 1); + if (is_selected(state, wid)){ + if (ui_do_text_field_input(state, &dest)){ + result = 1; + } + } + } + else{ + Render_Target *target = state->target; + UI_Style ui_style = get_ui_style_upper(state->style); + u32 back, fore, prompt_pop; + get_colors(state, &back, &fore, wid, ui_style); + get_pop_color(state, &prompt_pop, wid, ui_style); + + draw_rectangle(target, rect, back); + + i32 x = draw_string(target, state->font_id, prompt, rect.x0, rect.y0 + 1, prompt_pop); + draw_string(target, state->font_id, dest, x, rect.y0 + 1, ui_style.base); + } + + return result; +} + +internal b32 +do_button(i32 id, UI_State *state, UI_Layout *layout, char *text, i32 height_mult, + b32 is_toggle = 0, b32 on = 0){ + b32 result = 0; + i16 font_id = state->font_id; + i32 character_h = get_font_info(state->font_set, font_id)->height; + + i32_Rect btn_rect = layout_rect(layout, character_h * height_mult); + if (height_mult > 1) btn_rect = get_inner_rect(btn_rect, 2); + else{ + btn_rect.x0 += 2; + btn_rect.x1 -= 2; + } + + Widget_ID wid = make_id(state, id); + + if (state->input_stage){ + if (ui_do_button_input(state, btn_rect, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + UI_Style ui_style = get_ui_style(state->style); + u32 back, fore, outline; + outline = ui_style.bright; + get_colors(state, &back, &fore, wid, ui_style); + + draw_rectangle(target, btn_rect, back); + draw_rectangle_outline(target, btn_rect, outline); + real32 text_width = font_string_width(target, font_id, text); + i32 box_width = btn_rect.x1 - btn_rect.x0; + i32 box_height = btn_rect.y1 - btn_rect.y0; + i32 x_pos = TRUNC32(btn_rect.x0 + (box_width - text_width)*.5f); + draw_string(target, font_id, text, x_pos, btn_rect.y0 + (box_height - character_h) / 2, fore); + + if (is_toggle){ + i32_Rect on_box = get_inner_rect(btn_rect, character_h/2); + on_box.x1 = on_box.x0 + (on_box.y1 - on_box.y0); + + if (on) draw_rectangle(target, on_box, fore); + else draw_rectangle(target, on_box, back); + draw_rectangle_outline(target, on_box, fore); + } + } + + return result; +} + +internal b32 +do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v, Undo_Data *undo, i32 *out){ + b32 result = 0; + i16 font_id = state->font_id; + i32 character_h = get_font_info(state->font_set, font_id)->height; + + i32_Rect containing_rect = layout_rect(layout, character_h); + + i32_Rect click_rect; + click_rect.x0 = containing_rect.x0 + character_h - 1; + click_rect.x1 = containing_rect.x1 - character_h + 1; + click_rect.y0 = containing_rect.y0 + 2; + click_rect.y1 = containing_rect.y1 - 2; + + if (state->input_stage){ + real32 l; + if (ui_do_slider_input(state, click_rect, wid, (real32)click_rect.x0, (real32)click_rect.x1, &l)){ + real32 v_new = lerp(0.f, l, (real32)max); + v = ROUND32(v_new); + result = 1; + if (out) *out = v; + } + } + else{ + Render_Target *target = state->target; + if (max > 0){ + UI_Style ui_style = get_ui_style_upper(state->style); + + real32 L = unlerp(0.f, (real32)v, (real32)max); + i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); + + i32 bar_top = ((click_rect.y0 + click_rect.y1) >> 1) - 1; + i32 bar_bottom = bar_top + 2; + + bool32 show_bar = 1; + real32 tick_step = (click_rect.x1 - click_rect.x0) / (real32)max; + bool32 show_ticks = 1; + if (tick_step <= 5.f) show_ticks = 0; + + if (undo == 0){ + if (show_bar){ + i32_Rect slider_rect; + slider_rect.x0 = click_rect.x0; + slider_rect.x1 = x; + slider_rect.y0 = bar_top; + slider_rect.y1 = bar_bottom; + + draw_rectangle(target, slider_rect, ui_style.dim); + + slider_rect.x0 = x; + slider_rect.x1 = click_rect.x1; + draw_rectangle(target, slider_rect, ui_style.pop1); + } + + if (show_ticks){ + f32_Rect tick; + tick.x0 = (real32)click_rect.x0 - 1; + tick.x1 = (real32)click_rect.x0 + 1; + tick.y0 = (real32)bar_top - 3; + tick.y1 = (real32)bar_bottom + 3; + + for (i32 i = 0; i < v; ++i){ + draw_rectangle(target, tick, ui_style.dim); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + + for (i32 i = v; i <= max; ++i){ + draw_rectangle(target, tick, ui_style.pop1); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + } + } + else{ + if (show_bar){ + i32_Rect slider_rect; + slider_rect.x0 = click_rect.x0; + slider_rect.y0 = bar_top; + slider_rect.y1 = bar_bottom; + + Edit_Step *history = undo->history.edits; + i32 block_count = undo->history_block_count; + Edit_Step *step = history; + for (i32 i = 0; i < block_count; ++i){ + u32 color; + if (step->type == ED_REDO || + step->type == ED_UNDO) color = ui_style.pop1; + else color = ui_style.dim; + + real32 L; + if (i + 1 == block_count){ + L = 1.f; + }else{ + step = history + step->next_block; + L = unlerp(0.f, (real32)(step - history), (real32)max); + } + if (L > 1.f) L = 1.f; + i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); + + slider_rect.x1 = x; + draw_rectangle(target, slider_rect, color); + slider_rect.x0 = slider_rect.x1; + + if (L == 1.f) break; + } + } + + if (show_ticks){ + f32_Rect tick; + tick.x0 = (real32)click_rect.x0 - 1; + tick.x1 = (real32)click_rect.x0 + 1; + tick.y0 = (real32)bar_top - 3; + tick.y1 = (real32)bar_bottom + 3; + + Edit_Step *history = undo->history.edits; + u32 color = ui_style.dim; + for (i32 i = 0; i <= max; ++i){ + if (i != max){ + if (history[i].type == ED_REDO) color = ui_style.pop1; + else if (history[i].type == ED_UNDO || + history[i].type == ED_NORMAL) color = ui_style.pop2; + else color = ui_style.dim; + } + draw_rectangle(target, tick, color); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + } + } + + i32_Rect slider_handle; + slider_handle.x0 = x - 2; + slider_handle.x1 = x + 2; + slider_handle.y0 = click_rect.y0; + slider_handle.y1 = click_rect.y1; + + draw_rectangle(target, slider_handle, ui_style.bright); + } + } + + return result; +} + +internal void +do_label(UI_State *state, UI_Layout *layout, char *text, int text_size, f32 height = 2.f){ + Style *style = state->style; + i16 font_id = state->font_id; + i32 line_height = get_font_info(state->font_set, font_id)->height; + i32_Rect label = layout_rect(layout, FLOOR32(line_height * height)); + + if (!state->input_stage){ + Render_Target *target = state->target; + u32 back = style->main.margin_color; + u32 fore = style->main.default_color; + draw_rectangle(target, label, back); + i32 height = label.y1 - label.y0; + + String textstr = make_string(text, text_size); + draw_string(target, font_id, textstr, label.x0, + label.y0 + (height - line_height)/2, fore); + } +} + +inline void +do_label(UI_State *state, UI_Layout *layout, String text, f32 height = 2.f){ + do_label(state, layout, text.str, text.size, height); +} + +internal void +do_scroll_bar(UI_State *state, i32_Rect rect){ + i32 id = 1; + i32 w = (rect.x1 - rect.x0); + i32 h = (rect.y1 - rect.y0); + + i32_Rect top_arrow, bottom_arrow; + top_arrow.x0 = rect.x0; + top_arrow.x1 = rect.x1; + top_arrow.y0 = rect.y0; + top_arrow.y1 = top_arrow.y0 + w; + + bottom_arrow.x0 = rect.x0; + bottom_arrow.x1 = rect.x1; + bottom_arrow.y1 = rect.y1; + bottom_arrow.y0 = bottom_arrow.y1 - w; + + f32 space_h = (f32)(bottom_arrow.y0 - top_arrow.y1); + if (space_h <= w) return; + + i32 slider_h = w; + + f32 view_hmin = 0; + f32 view_hmax = state->height - h; + f32 L = unlerp(view_hmin, state->view_y, view_hmax); + + f32 slider_hmin = (f32)top_arrow.y1; + f32 slider_hmax = (f32)bottom_arrow.y0 - slider_h; + f32 S = lerp(slider_hmin, L, slider_hmax); + + i32_Rect slider; + slider.x0 = rect.x0; + slider.x1 = rect.x1; + slider.y0 = FLOOR32(S); + slider.y1 = slider.y0 + slider_h; + + Widget_ID wid = make_id(state, id); + + if (state->input_stage){ + state->view_y = + ui_do_vscroll_input(state, top_arrow, bottom_arrow, slider, wid, state->view_y, + (f32)(get_font_info(state->font_set, state->font_id)->height), + slider_hmin, slider_hmax, view_hmin, view_hmax); + } + else{ + Render_Target *target = state->target; + + f32 x0, y0, x1, y1, x2, y2; + f32 w_1_2 = w*.5f; + f32 w_1_3 = w*.333333f; + f32 w_2_3 = w*.666667f; + + + UI_Style ui_style = get_ui_style(state->style); + u32 outline, back, fore; + + outline = ui_style.bright; + + wid.sub_id2 = 0; + + x0 = (w_1_2 + top_arrow.x0); + x1 = (w_1_3 + top_arrow.x0); + x2 = (w_2_3 + top_arrow.x0); + + ++wid.sub_id2; + y0 = (w_1_3 + top_arrow.y0); + y1 = (w_2_3 + top_arrow.y0); + y2 = (w_2_3 + top_arrow.y0); + get_colors(state, &back, &fore, wid, ui_style); + draw_rectangle(target, top_arrow, back); + draw_rectangle_outline(target, top_arrow, outline); + + ++wid.sub_id2; + y0 = (w_2_3 + bottom_arrow.y0); + y1 = (w_1_3 + bottom_arrow.y0); + y2 = (w_1_3 + bottom_arrow.y0); + get_colors(state, &back, &fore, wid, ui_style); + draw_rectangle(target, bottom_arrow, back); + draw_rectangle_outline(target, bottom_arrow, outline); + + ++wid.sub_id2; + get_colors(state, &back, &fore, wid, ui_style); + draw_rectangle(target, slider, back); + draw_rectangle_outline(target, slider, outline); + + draw_rectangle_outline(target, rect, outline); + } +} + +internal void +draw_gradient_slider(Render_Target *target, Vec4 base, i32 channel, + i32 steps, f32 top, f32_Rect slider, b32 hsla){ + Vec4 low, high; + f32 *lowv, *highv; + f32 x; + f32 next_x; + f32 x_step; + f32 v_step; + f32 m; + + x = (real32)slider.x0; + x_step = (real32)(slider.x1 - slider.x0) / steps; + v_step = top / steps; + m = 1.f / top; + lowv = &low.v[channel]; + highv = &high.v[channel]; + + if (hsla){ + for (i32 i = 0; i < steps; ++i){ + low = high = base; + *lowv = (i * v_step); + *highv = *lowv + v_step; + *lowv *= m; + *highv *= m; + low = hsla_to_rgba(low); + high = hsla_to_rgba(high); + + next_x = x + x_step; + draw_gradient_2corner_clipped( + target, x, slider.y0, next_x, slider.y1, + low, high); + x = next_x; + } + } + else{ + for (i32 i = 0; i < steps; ++i){ + low = high = base; + *lowv = (i * v_step); + *highv = *lowv + v_step; + *lowv *= m; + *highv *= m; + + next_x = x + x_step; + draw_gradient_2corner_clipped( + target, x, slider.y0, next_x, slider.y1, + low, high); + x = next_x; + } + } +} + +inline void +draw_hsl_slider(Render_Target *target, Vec4 base, i32 channel, + i32 steps, f32 top, f32_Rect slider){ + draw_gradient_slider(target, base, channel, steps, top, slider, 1); +} + +inline void +draw_rgb_slider(Render_Target *target, Vec4 base, i32 channel, + i32 steps, f32 top, f32_Rect slider){ + draw_gradient_slider(target, base, channel, steps, top, slider, 0); +} + +internal b32 +do_main_file_box(System_Functions *system, UI_State *state, UI_Layout *layout, + Hot_Directory *hot_directory, b32 try_to_match, b32 case_sensitive, char *end){ + b32 result = 0; + Style *style = state->style; + String *string = &hot_directory->string; + + i16 font_id = state->font_id; + i32 line_height = get_font_info(state->font_set, font_id)->height; + i32_Rect box = layout_rect(layout, line_height + 2); + + if (state->input_stage){ + if (ui_do_file_field_input(system, state, hot_directory, try_to_match, case_sensitive)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + u32 back = style->main.margin_color; + u32 fore = style->main.default_color; + u32 special = style->main.special_character_color; + draw_rectangle(target, box, back); + i32 x = box.x0; + x = draw_string(target, font_id, string->str, x, box.y0, fore); + if (end) draw_string(target, font_id, end, x, box.y0, special); + } + + layout->y = box.y1; + return result; +} + +internal b32 +do_main_string_box(System_Functions *system, UI_State *state, UI_Layout *layout, String *string){ + b32 result = 0; + Style *style = state->style; + + i16 font_id = state->font_id; + i32 line_height = get_font_info(state->font_set, font_id)->height; + i32_Rect box = layout_rect(layout, line_height + 2); + + if (state->input_stage){ + if (ui_do_line_field_input(system, state, string)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + u32 back = style->main.margin_color; + u32 fore = style->main.default_color; + draw_rectangle(target, box, back); + i32 x = box.x0; + x = draw_string(target, font_id, string->str, x, box.y0, fore); + } + + layout->y = box.y1; + return result; +} + +internal b32 +do_list_option(i32 id, UI_State *state, UI_Layout *layout, String text){ + b32 result = 0; + Style *style = state->style; + + i16 font_id = state->font_id; + i32 character_h = get_font_info(state->font_set, font_id)->height; + + i32_Rect box = layout_rect(layout, character_h*2); + Widget_ID wid = make_id(state, id); + + if (state->input_stage){ + if (ui_do_button_input(state, box, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + i32_Rect inner = get_inner_rect(box, 3); + u32 back, outline, fore, pop; + back = style->main.back_color; + fore = style->main.default_color; + pop = style->main.file_info_style.pop2_color; + if (is_hover(state, wid)) outline = style->main.margin_active_color; + else outline = style->main.margin_color; + + draw_rectangle(target, inner, back); + i32 x = inner.x0, y = box.y0 + character_h/2; + x = draw_string(target, font_id, text, x, y, fore); + draw_margin(target, box, inner, outline); + } + + layout->y = box.y1; + return result; +} + +internal b32 +do_checkbox_list_option(i32 id, UI_State *state, UI_Layout *layout, String text, b32 is_on){ + b32 result = 0; + Style *style = state->style; + + i16 font_id = state->font_id; + i32 character_h = get_font_info(state->font_set, font_id)->height; + + i32_Rect box = layout_rect(layout, character_h*2); + Widget_ID wid = make_id(state, id); + + if (state->input_stage){ + if (ui_do_button_input(state, box, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + i32_Rect inner = get_inner_rect(box, 3); + u32 back, outline, fore, pop, box_color; + back = style->main.back_color; + fore = style->main.default_color; + pop = style->main.file_info_style.pop2_color; + if (is_hover(state, wid)) outline = style->main.margin_active_color; + else outline = style->main.margin_color; + box_color = style->main.margin_active_color; + + draw_rectangle(target, inner, back); + + i32_Rect square; + square = get_inner_rect(inner, character_h/3); + square.x1 = square.x0 + (square.y1 - square.y0); + if (is_on) draw_rectangle(target, square, box_color); + else draw_margin(target, square, 1, box_color); + + i32 x = square.x1 + 3; + i32 y = box.y0 + character_h/2; + x = draw_string(target, font_id, text, x, y, fore); + draw_margin(target, box, inner, outline); + } + + layout->y = box.y1; + return result; +} + + +internal b32 +do_file_option(i32 id, UI_State *state, UI_Layout *layout, String filename, b32 is_folder, String extra, char slash){ + b32 result = 0; + Style *style = state->style; + i16 font_id = state->font_id; + i32 character_h = get_font_info(state->font_set, font_id)->height; + char slash_buf[2] = { slash, 0 }; + + i32_Rect box = layout_rect(layout, character_h*2); + Widget_ID wid = make_id(state, id); + + if (state->input_stage){ + if (ui_do_button_input(state, box, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + i32_Rect inner = get_inner_rect(box, 3); + u32 back, outline, fore, pop; + back = style->main.back_color; + fore = style->main.default_color; + pop = style->main.file_info_style.pop2_color; + if (is_hover(state, wid)) outline = style->main.margin_active_color; + else outline = style->main.margin_color; + + draw_rectangle(target, inner, back); + i32 x = inner.x0, y = box.y0 + character_h/2; + x = draw_string(target, font_id, filename, x, y, fore); + if (is_folder) x = draw_string(target, font_id, slash_buf, x, y, fore); + draw_string(target, font_id, extra, x, y, pop); + draw_margin(target, box, inner, outline); + } + + layout->y = box.y1; + return result; +} + +internal b32 +do_file_list_box(System_Functions *system, UI_State *state, UI_Layout *layout, + Hot_Directory *hot_dir, b32 has_filter, b32 try_to_match, b32 case_sensitive, + b32 *new_dir, b32 *selected, char *end){ + b32 result = 0; + File_List *files = &hot_dir->file_list; + + if (do_main_file_box(system, state, layout, hot_dir, try_to_match, case_sensitive, end)){ + *selected = 1; + terminate_with_null(&hot_dir->string); + } + else{ + persist String p4c_extension = make_lit_string("p4c"); + persist String message_loaded = make_lit_string(" LOADED"); + persist String message_unsaved = make_lit_string(" LOADED *"); + persist String message_unsynced = make_lit_string(" LOADED !"); + persist String message_nothing = {}; + + char front_name_space[256]; + String front_name = make_fixed_width_string(front_name_space); + get_front_of_directory(&front_name, hot_dir->string); + + Absolutes absolutes; + get_absolutes(front_name, &absolutes, 1, 1); + + char full_path_[256]; + String full_path = make_fixed_width_string(full_path_); + get_path_of_directory(&full_path, hot_dir->string); + i32 restore_size = full_path.size; + + i32 i; + File_Info *info, *end; + end = files->infos + files->count; + for (info = files->infos, i = 0; info != end; ++info, ++i){ + String filename = info->filename; + + append(&full_path, filename); + terminate_with_null(&full_path); + + Editing_File *file = working_set_contains(state->working_set, full_path); + full_path.size = restore_size; + + b8 is_folder = (info->folder != 0); + b8 ext_match = (match(file_extension(filename), p4c_extension) != 0); + b8 name_match = (filename_match(front_name, &absolutes, filename, case_sensitive) != 0); + b8 is_loaded = (file != 0 && file_is_ready(file)); + + String message = message_nothing; + if (is_loaded){ + switch (buffer_get_sync(file)){ + case SYNC_GOOD: message = message_loaded; break; + case SYNC_BEHIND_OS: message = message_unsynced; break; + case SYNC_UNSAVED: message = message_unsaved; break; + } + } + + if ((is_folder || !has_filter || ext_match) && name_match){ + if (do_file_option(100+i, state, layout, filename, is_folder, message, system->slash)){ + result = 1; + hot_directory_clean_end(hot_dir); + append(&hot_dir->string, filename); + if (is_folder){ + *new_dir = 1; + append(&hot_dir->string, system->slash); + } + else{ + *selected = 1; + } + terminate_with_null(&hot_dir->string); + } + } + } + } + + return result; +} + +internal b32 +do_live_file_list_box(System_Functions *system, UI_State *state, UI_Layout *layout, + Working_Set *working_set, String *string, b32 *selected){ + b32 result = 0; + + if (do_main_string_box(system, state, layout, string)){ + *selected = 1; + terminate_with_null(string); + } + else{ + persist String message_unsaved = make_lit_string(" *"); + persist String message_unsynced = make_lit_string(" !"); + persist String message_nothing = {}; + + Absolutes absolutes; + get_absolutes(*string, &absolutes, 1, 1); + + Editing_File *file; + File_Node *node, *used_nodes; + i32 i = 0; + used_nodes = &working_set->used_sentinel; + + for (dll_items(node, used_nodes)){ + file = (Editing_File*)node; + Assert(!file->state.is_dummy); + + String message = message_nothing; + switch (buffer_get_sync(file)){ + case SYNC_BEHIND_OS: message = message_unsynced; break; + case SYNC_UNSAVED: message = message_unsaved; break; + } + + if (filename_match(*string, &absolutes, file->name.live_name, 1)){ + if (do_file_option(100+i, state, layout, file->name.live_name, 0, message, system->slash)){ + result = 1; + *selected = 1; + copy(string, file->name.source_path); + terminate_with_null(string); + } + } + + ++i; + } + } + + return result; +} + +struct Super_Color{ + Vec4 hsla; + Vec4 rgba; + u32 *out; +}; + +internal Super_Color +super_color_create(u32 packed){ + Super_Color result = {}; + result.rgba = unpack_color4(packed); + result.hsla = rgba_to_hsla(result.rgba); + return result; +} + +internal void +super_color_post_hsla(Super_Color *color, Vec4 hsla){ + color->hsla = hsla; + if (hsla.h == 1.f) + hsla.h = 0.f; + color->rgba = hsla_to_rgba(hsla); + *color->out = pack_color4(color->rgba); +} + +internal void +super_color_post_rgba(Super_Color *color, Vec4 rgba){ + color->rgba = rgba; + color->hsla = rgba_to_hsla(rgba); + *color->out = pack_color4(rgba); +} + +internal void +super_color_post_packed(Super_Color *color, u32 packed){ + color->rgba = unpack_color4(packed); + color->hsla = rgba_to_hsla(color->rgba); + *color->out = packed; +} + +u32 super_color_clear_masks[] = {0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00}; +u32 super_color_shifts[] = {16, 8, 0}; + +internal u32 +super_color_post_byte(Super_Color *color, i32 channel, u8 byte){ + u32 packed = *color->out; + packed &= super_color_clear_masks[channel]; + packed |= (byte << super_color_shifts[channel]); + super_color_post_packed(color, packed); + return packed; +} + +struct Color_Highlight{ + i32 ids[4]; +}; + +struct Library_UI{ + UI_State *state; + UI_Layout *layout; + + Font_Set *fonts; + + Style_Library *styles; + Hot_Directory *hot_directory; +}; + +struct Color_UI{ + UI_State *state; + UI_Layout *layout; + + Font_Set *fonts; + Style_Font *global_font; + + f32 hex_advance; + u32 *palette; + i32 palette_size; + + Color_Highlight highlight; + Super_Color color; + + b32 has_hover_color; + Super_Color hover_color; +}; + +enum Channel_Field_Type{ + CF_DEC, + CF_HEX +}; + +internal void +do_single_slider(i32 sub_id, Color_UI *ui, i32 channel, b32 is_rgba, + i32 grad_steps, f32 top, f32_Rect slider, f32 v_handle, + i32_Rect rect){ + f32_Rect click_box = slider; + click_box.y0 -= v_handle; + + if (ui->state->input_stage){ + real32 v; + if (ui_do_slider_input(ui->state, i32R(click_box), make_sub1(ui->state, sub_id), slider.x0, slider.x1, &v)){ + Vec4 new_color; + if (is_rgba) new_color = ui->color.rgba; + else new_color = ui->color.hsla; + new_color.v[channel] = clamp(0.f, v, 1.f); + if (is_rgba) super_color_post_rgba(&ui->color, new_color); + else super_color_post_hsla(&ui->color, new_color); + } + } + else{ + Render_Target *target = ui->state->target; + Vec4 color; + real32 x; + if (is_rgba){ + color = ui->color.rgba; + draw_rgb_slider(target, V4(0,0,0,1.f), channel, 10, 100.f, slider); + } + else{ + i32 steps; + real32 top; + if (channel == 0){ + steps = 45; + top = 360.f; + } + else{ + steps = 10; + top = 100.f; + } + color = ui->color.hsla; + draw_hsl_slider(target, color, channel, steps, top, slider); + } + + x = lerp(slider.x0, color.v[channel], slider.x1); + draw_rectangle( + target, f32R(x, slider.y0, x + 1, slider.y1), 0xff000000); + + draw_rectangle( + target, f32R(x-2, click_box.y0, x+3, slider.y0-4), 0xff777777); + } +} + +internal void +do_hsl_sliders(Color_UI *ui, i32_Rect rect){ + real32 bar_width = (real32)(rect.x1 - rect.x0 - 20); + if (bar_width > 45){ + f32_Rect slider; + real32 y; + i32 sub_id; + + real32 v_full_space = 30.f; + real32 v_half_space = 15.f; + real32 v_quarter_space = 12.f; + real32 v_handle = 9.f; + + slider.x0 = rect.x0 + 10.f; + slider.x1 = slider.x0 + bar_width; + + sub_id = 0; + + i32 step_count[] = {45, 10, 10}; + real32 tops[] = {360.f, 100.f, 100.f}; + + y = rect.y0 + v_quarter_space; + for (i32 i = 0; i < 3; ++i){ + ++sub_id; + slider.y0 = y; + slider.y1 = slider.y0 + v_half_space; + do_single_slider(sub_id, ui, i, 0, step_count[i], tops[i], slider, v_handle, rect); + y += v_full_space; + } + } +} + +internal void +fill_buffer_color_channel(char *buffer, u8 x, Channel_Field_Type ftype){ + if (ftype == CF_DEC){ + u8 x0; + x0 = x / 10; + buffer[2] = (x - (10*x0)) + '0'; + x = x0; + x0 = x / 10; + buffer[1] = (x - (10*x0)) + '0'; + x = x0; + x0 = x / 10; + buffer[0] = (x - (10*x0)) + '0'; + } + else{ + u8 n; + n = x & 0xF; + buffer[1] = int_to_hexchar(n); + x >>= 4; + n = x & 0xF; + buffer[0] = int_to_hexchar(n); + } +} + +internal b32 +do_channel_field(i32 sub_id, Color_UI *ui, u8 *channel, Channel_Field_Type ftype, + i32 y, u32 color, u32 back, i32 x0, i32 x1){ + b32 result = 0; + + i16 font_id = ui->state->font_id; + i32 line_height = get_font_info(ui->state->font_set, font_id)->height; + i32_Rect hit_region; + hit_region.x0 = x0; + hit_region.x1 = x1; + hit_region.y0 = y; + hit_region.y1 = y + line_height; + + i32 digit_count; + if (ftype == CF_DEC) digit_count = 3; + else digit_count = 2; + + Render_Target *target = ui->state->target; + + if (ui->state->input_stage){ + i32 indx; + ui_do_subdivided_button_input(ui->state, hit_region, digit_count, + make_sub1(ui->state, sub_id), 1, &indx); + } + else{ + if (ui->state->hover.sub_id1 == sub_id && ui->state->selected.sub_id1 != sub_id){ + draw_rectangle(target, hit_region, back); + } + } + + char string_buffer[4]; + string_buffer[digit_count] = 0; + fill_buffer_color_channel(string_buffer, *channel, ftype); + + if (ui->state->selected.sub_id1 == sub_id){ + i32 indx = ui->state->selected.sub_id2; + if (ui->state->input_stage){ + Key_Summary *keys = ui->state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Event_Data key = get_single_key(keys, key_i); + + if (key.keycode == key_right){ + ++indx; + if (indx > digit_count-1) indx = 0; + } + if (key.keycode == key_left){ + --indx; + if (indx < 0) indx = digit_count-1; + } + + i32 new_value = *channel; + if (key.keycode == key_up || key.keycode == key_down){ + i32 place = digit_count-1-indx; + i32 base = (ftype == CF_DEC)?10:0x10; + i32 step_amount = 1; + while (place > 0){ + step_amount *= base; + --place; + } + if (key.keycode == key_down){ + step_amount = 0 - step_amount; + } + new_value += step_amount; + } + + u8 c = (u8)key.character; + bool32 is_good = (ftype == CF_DEC)?char_is_numeric(c):char_is_hex(c); + if (is_good){ + string_buffer[indx] = c; + if (ftype == CF_DEC) + new_value = str_to_int(make_string(string_buffer, 3)); + else + new_value = hexstr_to_int(make_string(string_buffer, 2)); + ++indx; + if (indx > digit_count-1) indx = 0; + } + + if (c == '\n'){ + switch (sub_id){ + case 1: case 2: + case 4: case 5: + ui->state->sub_id1_change = sub_id + 3; break; + + case 7: case 8: + ui->state->sub_id1_change = sub_id - 6; break; + } + } + + if (new_value != *channel){ + if (new_value > 255){ + *channel = 255; + } + else if (new_value < 0){ + *channel = 0; + } + else{ + *channel = (u8)new_value; + } + fill_buffer_color_channel(string_buffer, *channel, ftype); + result = 1; + } + ui->state->selected.sub_id2 = indx; + } + } + else{ + f32_Rect r = f32R(hit_region); + r.x0 += indx*ui->hex_advance+1; + r.x1 = r.x0+ui->hex_advance+1; + draw_rectangle(target, r, back); + } + } + + if (!ui->state->input_stage) + draw_string_mono(target, font_id, string_buffer, + (real32)x0 + 1, (real32)y, ui->hex_advance, + color); + + return result; +} + +internal void +do_rgb_sliders(Color_UI *ui, i32_Rect rect){ + i32 dec_x0, dec_x1; + dec_x0 = rect.x0 + 10; + dec_x1 = TRUNC32(dec_x0 + ui->hex_advance*3 + 2); + + i32 hex_x0, hex_x1; + hex_x0 = dec_x1 + 10; + hex_x1 = TRUNC32(hex_x0 + ui->hex_advance*2 + 2); + + rect.x0 = hex_x1; + real32 bar_width = (real32)(rect.x1 - rect.x0 - 20); + + f32_Rect slider; + f32 y; + i32 sub_id; + u8 channel; + + real32 v_full_space = 30.f; + real32 v_half_space = 15.f; + real32 v_quarter_space = 12.f; + real32 v_handle = 9.f; + + u32 packed_color = *ui->color.out; + + y = rect.y0 + v_quarter_space; + slider.x0 = rect.x0 + 10.f; + slider.x1 = slider.x0 + bar_width; + + sub_id = 0; + + persist i32 shifts[3] = { 16, 8, 0 }; + persist u32 fore_colors[3] = { 0xFFFF0000, 0xFF00FF00, 0xFF1919FF }; + persist u32 back_colors[3] = { 0xFF222222, 0xFF222222, 0xFF131313 }; + + for (i32 i = 0; i < 3; ++i){ + i32 shift = shifts[i]; + u32 fore = fore_colors[i]; + u32 back = back_colors[i]; + + ++sub_id; + channel = (packed_color >> shift) & 0xFF; + if (do_channel_field(sub_id, ui, &channel, CF_DEC, + (i32)y, fore, back, dec_x0, dec_x1)) + super_color_post_byte(&ui->color, i, channel); + + ++sub_id; + channel = (packed_color >> shift) & 0xFF; + if (do_channel_field(sub_id, ui, &channel, CF_HEX, + (i32)y, fore, back, hex_x0, hex_x1)) + super_color_post_byte(&ui->color, i, channel); + + ++sub_id; + slider.y0 = y; + slider.y1 = slider.y0 + v_half_space; + if (bar_width > 45.f) + do_single_slider(sub_id, ui, i, 1, 10, 100.f, slider, v_handle, rect); + y += v_full_space; + } +} + +struct Blob_Layout{ + i32_Rect rect; + i32 x, y; + i32 size, space; +}; + +internal void +begin_layout(Blob_Layout *layout, i32_Rect rect){ + layout->rect = rect; + layout->x = rect.x0 + 10; + layout->y = rect.y0; + layout->size = 20; + layout->space = 5; +} + +internal void +do_blob(Color_UI *ui, Blob_Layout *layout, u32 color, bool32 *set_me, i32 sub_id){ + i32_Rect rect = layout->rect; + f32_Rect blob; + blob.x0 = (real32)layout->x; + blob.y0 = (real32)layout->y; + blob.x1 = blob.x0 + layout->size; + blob.y1 = blob.y0 + layout->size; + + layout->y += layout->size + layout->space; + if (layout->y + layout->size + layout->space*2 > rect.y1){ + layout->y = rect.y0; + layout->x += layout->size + layout->space; + } + + if (ui->state->input_stage){ + bool32 right = 0; + if (ui_do_button_input(ui->state, i32R(blob), make_sub1(ui->state, sub_id), 0, &right)){ + super_color_post_packed(&ui->color, color); + } + else if (right) *set_me = 1; + } + else{ + Render_Target *target = ui->state->target; + draw_rectangle(target, blob, color); + persist u32 silver = 0xFFa0a0a0; + draw_rectangle_outline(target, blob, silver); + } +} + +inline void +do_blob(Color_UI *ui, Blob_Layout *layout, u32 *color, bool32 *set_me){ + i32 sub_id = (i32)((char*)color - (char*)ui->state->style); + do_blob(ui, layout, *color, set_me, sub_id); +} + +internal void +do_v_divide(Color_UI *ui, Blob_Layout *layout, i32 width){ + i32_Rect rect = layout->rect; + if (layout->y > rect.y0){ + layout->x += layout->size + layout->space; + } + layout->x += width; + layout->y = rect.y0; +} + +internal void +do_palette(Color_UI *ui, i32_Rect rect){ + Style *style = ui->state->style; + Blob_Layout layout; + begin_layout(&layout, rect); + bool32 set_me; + + do_blob(ui, &layout, &style->main.back_color, &set_me); + do_blob(ui, &layout, &style->main.margin_color, &set_me); + do_blob(ui, &layout, &style->main.margin_active_color, &set_me); + + do_blob(ui, &layout, &style->main.cursor_color, &set_me); + do_blob(ui, &layout, &style->main.at_cursor_color, &set_me); + do_blob(ui, &layout, &style->main.mark_color, &set_me); + + do_blob(ui, &layout, &style->main.highlight_color, &set_me); + do_blob(ui, &layout, &style->main.at_highlight_color, &set_me); + + do_blob(ui, &layout, &style->main.default_color, &set_me); + do_blob(ui, &layout, &style->main.comment_color, &set_me); + do_blob(ui, &layout, &style->main.keyword_color, &set_me); + do_blob(ui, &layout, &style->main.str_constant_color, &set_me); + do_blob(ui, &layout, &style->main.char_constant_color, &set_me); + do_blob(ui, &layout, &style->main.int_constant_color, &set_me); + do_blob(ui, &layout, &style->main.float_constant_color, &set_me); + do_blob(ui, &layout, &style->main.bool_constant_color, &set_me); + do_blob(ui, &layout, &style->main.include_color, &set_me); + do_blob(ui, &layout, &style->main.preproc_color, &set_me); + do_blob(ui, &layout, &style->main.special_character_color, &set_me); + + do_blob(ui, &layout, &style->main.highlight_junk_color, &set_me); + do_blob(ui, &layout, &style->main.highlight_white_color, &set_me); + + do_blob(ui, &layout, &style->main.paste_color, &set_me); + + do_blob(ui, &layout, &style->main.file_info_style.bar_color, &set_me); + do_blob(ui, &layout, &style->main.file_info_style.base_color, &set_me); + do_blob(ui, &layout, &style->main.file_info_style.pop1_color, &set_me); + do_blob(ui, &layout, &style->main.file_info_style.pop2_color, &set_me); + + do_v_divide(ui, &layout, 20); + + if (!ui->state->input_stage){ + Render_Target *target = ui->state->target; + draw_string(target, ui->state->font_id, "Global Palette: right click to save color", + layout.x, layout.rect.y0, style->main.default_color); + } + + layout.rect.y0 += layout.size + layout.space; + layout.y = layout.rect.y0; + i32 palette_size = ui->palette_size + 1000; + u32 *color = ui->palette; + for (i32 i = 1000; i < palette_size; ++i, ++color){ + set_me = 0; + do_blob(ui, &layout, *color, &set_me, i); + if (set_me){ + *color = *ui->color.out; + ui->state->redraw = 1; + } + } +} + +internal void +do_sub_button(i32 id, Color_UI *ui, char *text){ + i16 font_id = ui->state->font_id; + i32 line_height = get_font_info(ui->state->font_set, font_id)->height; + i32_Rect rect = layout_rect(ui->layout, line_height + 2); + + if (ui->state->input_stage){ + ui_do_button_input(ui->state, rect, make_sub0(ui->state, id), 1); + } + else{ + Render_Target *target = ui->state->target; + + u32 back_color, text_color; + text_color = 0xFFDDDDDD; + if (ui->state->selected.sub_id0 == id){ + back_color = 0xFF444444; + } + else if (ui->state->hover.sub_id0 == id){ + back_color = 0xFF222222; + } + else{ + back_color = 0xFF111111; + } + + draw_rectangle(target, rect, back_color); + draw_string(target, font_id, text, rect.x0, rect.y0 + 1, + text_color); + } +} + +internal void +do_color_adjuster(Color_UI *ui, u32 *color, + u32 text_color, u32 back_color, char *name){ + i32 id = raw_ptr_dif(color, ui->state->style); + i16 font_id = ui->state->font_id; + i32 character_h = get_font_info(ui->state->font_set, font_id)->height; + u32 text = 0, back = 0; + + i32_Rect bar = layout_rect(ui->layout, character_h); + + if (ui->state->input_stage){ + if (ui_do_button_input(ui->state, bar, make_id(ui->state, id), 1)){ + ui->has_hover_color = 1; + ui->hover_color = super_color_create(*color); + } + } + + else{ + Render_Target *target = ui->state->target; + u32 text_hover = 0xFF101010; + u32 back_hover = 0xFF999999; + if (ui->state->selected.id != id && ui->state->hover.id == id){ + text = text_hover; + back = back_hover; + } + else{ + text = text_color; + back = back_color; + } + + draw_rectangle(target, bar, back); + i32 end_pos = draw_string(target, font_id, name, bar.x0, bar.y0, text); + + real32 x_spacing = ui->hex_advance; + i32_Rect temp_rect = bar; + temp_rect.x0 = temp_rect.x1 - CEIL32(x_spacing * 9.f); + if (temp_rect.x0 >= end_pos + x_spacing){ + u32 n = *color; + char full_hex_string[] = "0x000000"; + for (i32 i = 7; i >= 2; --i){ + i32 m = (n & 0xF); + n >>= 4; + full_hex_string[i] = int_to_hexchar(m); + } + draw_string_mono(target, font_id, full_hex_string, + (f32)temp_rect.x0, (f32)bar.y0, + x_spacing, text); + } + + for (i32 i = 0; i < ArrayCount(ui->highlight.ids); ++i){ + if (ui->highlight.ids[i] == id){ + draw_rectangle_outline(target, f32R(bar), text_color); + break; + } + } + } + + if (ui->state->selected.id == id){ + Render_Target *target = ui->state->target; + i32_Rect expanded = layout_rect(ui->layout, 115 + (character_h + 2)); + UI_Layout_Restore restore = begin_sub_layout(ui->layout, expanded); + + ui->color.out = color; + + if (ui->state->input_stage){ + if (ui->state->selected.sub_id0 == 0){ + ui->state->selected.sub_id0 = 1; + } + } + else{ + draw_rectangle(target, expanded, 0xff000000); + } + + begin_row(ui->layout, 3); + do_sub_button(1, ui, "HSL"); + do_sub_button(2, ui, "RGB"); + do_sub_button(3, ui, "Palette"); + + i32_Rect sub_rect; + sub_rect = expanded; + sub_rect.y0 += 10 + character_h; + + switch (ui->state->selected.sub_id0){ + case 1: do_hsl_sliders(ui, sub_rect); break; + case 2: do_rgb_sliders(ui, sub_rect); break; + case 3: do_palette(ui, sub_rect); break; + } + + end_sub_layout(restore); + } +} + +internal void +do_style_name(Color_UI *ui){ + i32 id = -3; + + i16 font_id = ui->state->font_id; + i32 line_height = get_font_info(ui->state->font_set, font_id)->height; + + i32_Rect srect = layout_rect(ui->layout, line_height); + + Widget_ID wid = make_id(ui->state, id); + b32 selected = is_selected(ui->state, wid); + + if (ui->state->input_stage){ + if (!selected){ + ui_do_button_input(ui->state, srect, wid, 1); + } + else{ + Style *style = ui->state->style; + if (ui_do_text_field_input(ui->state, &style->name)){ + ui->state->selected = {}; + } + } + } + else{ + Render_Target *target = ui->state->target; + Style *style = ui->state->style; + u32 back, fore_text, fore_label; + if (selected){ + back = 0xFF000000; + fore_label = 0xFF808080; + fore_text = 0xFFFFFFFF; + } + else if (is_hover(ui->state, wid)){ + back = 0xFF999999; + fore_text = fore_label = 0xFF101010; + } + else{ + back = style->main.back_color; + fore_text = fore_label = style->main.default_color; + } + + draw_rectangle(target, srect, back); + i32 x = srect.x0; + x = draw_string(target, font_id, "NAME: ", + x, srect.y0, fore_label); + x = draw_string(target, font_id, style->name.str, + x, srect.y0, fore_text); + } +} + +internal b32 +do_font_option(Color_UI *ui, i16 font_id){ + b32 result = 0; + Font_Info *info = get_font_info(ui->state->font_set, font_id); + + i32 sub_id = (i32)(i64)(info); + i32_Rect orect = layout_rect(ui->layout, info->height); + + Widget_ID wid = make_sub0(ui->state, sub_id); + if (ui->state->input_stage){ + if (ui_do_button_input(ui->state, orect, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = ui->state->target; + u32 back, fore; + if (is_hover(ui->state, wid)){ + back = 0xFF999999; + fore = 0xFF101010; + } + else{ + back = 0xFF000000; + fore = 0xFFFFFFFF; + } + draw_rectangle(target, orect, back); + i32 x = orect.x0; + x = draw_string(target, font_id, "->", x, orect.y0, fore); + draw_string(target, font_id, info->name.str, x, orect.y0, fore); + } + + return result; +} + +internal void +do_font_switch(Color_UI *ui){ + i32 id = -2; + Render_Target *target = ui->state->target; + Font_Set *font_set = ui->state->font_set; + + i16 font_id = ui->state->font_id; + Font_Info *info = get_font_info(font_set, font_id); + i32 character_h = info->height; + + i32_Rect srect = layout_rect(ui->layout, character_h); + Widget_ID wid = make_id(ui->state, id); + + if (ui->state->input_stage){ + ui_do_button_input(ui->state, srect, wid, 1); + } + else{ + Style *style = ui->state->style; + u32 back, fore; + if (is_hover(ui->state, wid) && !is_selected(ui->state, wid)){ + back = 0xFF999999; + fore = 0xFF101010; + } + else{ + back = style->main.back_color; + fore = style->main.default_color; + } + draw_rectangle(target, srect, back); + i32 x = srect.x0; + x = draw_string(target, font_id, "FONT: ", + x, srect.y0, fore); + x = draw_string(target, font_id, info->name.str, + x, srect.y0, fore); + } + + if (is_selected(ui->state, wid)){ + srect = layout_rect(ui->layout, character_h/2); + if (!ui->state->input_stage) + draw_rectangle(target, srect, 0xFF000000); + + i32 count = font_set->count + 1; + + for (i16 i = 1; i < count; ++i){ + if (i == font_id) continue; + if (do_font_option(ui, i)){ + ui->global_font->font_id = i; + ui->global_font->font_changed = 1; + } + } + + srect = layout_rect(ui->layout, character_h/2); + if (!ui->state->input_stage) + draw_rectangle(target, srect, 0xFF000000); + } +} + +internal b32 +do_style_preview(Library_UI *ui, Style *style, i32 toggle = -1){ + b32 result = 0; + i32 id; + if (style == ui->state->style) id = 2; + else id = raw_ptr_dif(style, ui->styles->styles) + 100; + + i16 font_id = ui->state->font_id; + Font_Info *info = get_font_info(ui->state->font_set, font_id); + + i32_Rect prect = layout_rect(ui->layout, info->height*3 + 6); + + Widget_ID wid = make_id(ui->state, id); + + if (ui->state->input_stage){ + if (ui_do_button_input(ui->state, prect, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = ui->state->target; + u32 margin_color = style->main.margin_color; + if (is_hover(ui->state, wid)){ + margin_color = style->main.margin_active_color; + } + + i32_Rect inner; + if (toggle != -1){ + i32_Rect toggle_box = prect; + toggle_box.x1 = toggle_box.x0 + info->height*2 + 6; + prect.x0 = toggle_box.x1; + + inner = get_inner_rect(toggle_box, 3); + draw_margin(target, toggle_box, inner, margin_color); + draw_rectangle(target, inner, style->main.back_color); + + i32 d; + d = info->height/2; + i32_Rect b; + b.x0 = (inner.x1 + inner.x0)/2 - d; + b.y0 = (inner.y1 + inner.y0)/2 - d; + b.x1 = b.x0 + info->height; + b.y1 = b.y0 + info->height; + if (toggle) draw_rectangle(target, b, margin_color); + else draw_rectangle_outline(target, b, margin_color); + } + + inner = get_inner_rect(prect, 3); + draw_margin(target, prect, inner, margin_color); + draw_rectangle(target, inner, style->main.back_color); + + i32 text_y = inner.y0; + i32 text_x = inner.x0; + text_x = draw_string(target, font_id, style->name.str, + text_x, text_y, style->main.default_color); + i32 font_x = (i32)(inner.x1 - font_string_width(target, font_id, info->name.str)); + if (font_x > text_x + 10) + draw_string(target, font_id, info->name.str, + font_x, text_y, style->main.default_color); + + text_x = inner.x0; + text_y += info->height; + text_x = draw_string(target, font_id, "if ", text_x, text_y, + style->main.keyword_color); + text_x = draw_string(target, font_id, "(x < ", text_x, text_y, + style->main.default_color); + text_x = draw_string(target, font_id, "0", text_x, text_y, + style->main.int_constant_color); + text_x = draw_string(target, font_id, ") { x = ", text_x, text_y, + style->main.default_color); + text_x = draw_string(target, font_id, "0", text_x, text_y, + style->main.int_constant_color); + text_x = draw_string(target, font_id, "; } ", text_x, text_y, + style->main.default_color); + text_x = draw_string(target, font_id, "// comment", text_x, text_y, + style->main.comment_color); + + text_x = inner.x0; + text_y += info->height; + text_x = draw_string(target, font_id, "[] () {}; * -> +-/ <>= ! && || % ^", + text_x, text_y, style->main.default_color); + } + + ui->layout->y = prect.y1; + return result; +} + + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_internal.h b/test_data/lots_of_files/4ed_internal.h new file mode 100644 index 0000000..bd88a12 --- /dev/null +++ b/test_data/lots_of_files/4ed_internal.h @@ -0,0 +1,60 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 16.05.2015 + * + * Fascilities available for development but not intended for shipping. + * + */ + +// TOP + +#if FRED_INTERNAL == 1 +#define ProfileStart_(name, start, counter, hit, thread, n, c) + +#define ProfileEnd_(name, start, counter, hit, thread) + +#define ProfileMoment_(name, counter, thread) + +#if 0 + +#define ProfileStart(name) char *_pname_##name; i64 _pstart_##name; \ + i32 _pcounter_##name; u32 _phit_##name; \ + ProfileStart_(_pname_##name, _pstart_##name, _pcounter_##name, \ + _phit_##name, system->thread_get_id(thread), \ + #name, __COUNTER__) + +#define ProfileEnd(name) ProfileEnd_(_pname_##name, _pstart_##name, \ + _pcounter_##name, _phit_##name, \ + system->thread_get_id(thread)) + +#define ProfileMoment(name, thread) ProfileMoment_(#name, __COUNTER__, thread) +#define ProfileMomentFunction() ProfileMoment_(__FUNCTION__, __COUNTER__, 0) + +#else + +#define ProfileStart(name) +#define ProfileEnd(name) +#define ProfileMoment(name) +#define ProfileMomentFunction() + +#endif + + + +struct Sys_Bubble : public Bubble{ + i32 line_number; + char *file_name; +}; + +#else + +#define ProfileStart(name) +#define ProfileEnd(name) +#define ProfileMoment(name) +#define ProfileMomentFunction() + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_keyboard.cpp b/test_data/lots_of_files/4ed_keyboard.cpp new file mode 100644 index 0000000..54975f4 --- /dev/null +++ b/test_data/lots_of_files/4ed_keyboard.cpp @@ -0,0 +1,20 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 16.11.2014 + * + * Keyboard layer for 4coder + * + */ + +// TOP + +globalvar u8 keycode_lookup_table[255]; + +inline u8 +keycode_lookup(u8 system_code){ + return keycode_lookup_table[system_code]; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_keycodes.h b/test_data/lots_of_files/4ed_keycodes.h new file mode 100644 index 0000000..c8d6b77 --- /dev/null +++ b/test_data/lots_of_files/4ed_keycodes.h @@ -0,0 +1,30 @@ +enum Key_Code{ +key_back = 1, +key_up = 2, +key_down = 3, +key_left = 4, +key_right = 5, +key_del = 6, +key_insert = 7, +key_home = 8, +key_end = 11, +key_page_up = 12, +key_page_down = 13, +key_esc = 14, +key_f1 = 127, +key_f2 = 128, +key_f3 = 129, +key_f4 = 130, +key_f5 = 131, +key_f6 = 132, +key_f7 = 133, +key_f8 = 134, +key_f9 = 135, +key_f10 = 136, +key_f11 = 137, +key_f12 = 138, +key_f13 = 139, +key_f14 = 140, +key_f15 = 141, +key_f15 = 142, +} diff --git a/test_data/lots_of_files/4ed_layout.cpp b/test_data/lots_of_files/4ed_layout.cpp new file mode 100644 index 0000000..81d20fa --- /dev/null +++ b/test_data/lots_of_files/4ed_layout.cpp @@ -0,0 +1,313 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 19.08.2015 + * + * Panel layout and general view functions for 4coder + * + */ + +// TOP + +struct Panel_Divider{ + Panel_Divider *next; + i32 parent; + i32 which_child; + i32 child1, child2; + b32 v_divider; + i32 pos; +}; + +struct Screen_Region{ + i32_Rect full; + i32_Rect inner; + i32_Rect prev_inner; + i32 l_margin, r_margin; + i32 t_margin, b_margin; +}; + +struct Panel{ + Panel *next; + Panel *prev; + + struct View *view; + i32 parent; + i32 which_child; + + int ALLOCED; + + union{ + struct{ + i32_Rect full; + i32_Rect inner; + i32_Rect prev_inner; + i32 l_margin, r_margin; + i32 t_margin, b_margin; + }; + Screen_Region screen_region; + }; +}; + +struct Editing_Layout{ + Panel *panels; + Panel free_sentinel; + Panel used_sentinel; + Panel_Divider *dividers; + Panel_Divider *free_divider; + i32 panel_count, panel_max_count; + i32 root; + i32 active_panel; + i32 full_width, full_height; +}; + +struct Divider_And_ID{ + Panel_Divider* divider; + i32 id; +}; + +struct Panel_And_ID{ + Panel* panel; + i32 id; +}; + +internal void +panel_init(Panel *panel){ + panel->view = 0; + panel->parent = -1; + panel->which_child = 0; + panel->screen_region.full = {}; + panel->screen_region.inner = {}; + panel->screen_region.prev_inner = {}; + panel->l_margin = 3; + panel->r_margin = 3; + panel->t_margin = 3; + panel->b_margin = 3; +} + +internal Divider_And_ID +layout_alloc_divider(Editing_Layout *layout){ + Divider_And_ID result; + + Assert(layout->free_divider); + result.divider = layout->free_divider; + layout->free_divider = result.divider->next; + + *result.divider = {}; + result.divider->parent = -1; + result.divider->child1 = -1; + result.divider->child2 = -1; + result.id = (i32)(result.divider - layout->dividers); + if (layout->panel_count == 1){ + layout->root = result.id; + } + + return(result); +} + +internal Divider_And_ID +layout_get_divider(Editing_Layout *layout, i32 id){ + Divider_And_ID result; + + Assert(id >= 0 && id < layout->panel_max_count-1); + result.id = id; + result.divider = layout->dividers + id; + + return(result); +} + +internal void +layout_free_divider(Editing_Layout *layout, Panel_Divider *divider){ + divider->next = layout->free_divider; + layout->free_divider = divider; +} + +internal Panel_And_ID +layout_alloc_panel(Editing_Layout *layout){ + Panel_And_ID result = {}; + + Assert(layout->panel_count < layout->panel_max_count); + ++layout->panel_count; + + result.panel = layout->free_sentinel.next; + dll_remove(result.panel); + dll_insert(&layout->used_sentinel, result.panel); + + panel_init(result.panel); + + result.id = (i32)(result.panel - layout->panels); + + result.panel->ALLOCED = 1; + + return(result); +} + +internal void +layout_free_panel(Editing_Layout *layout, Panel *panel){ + dll_remove(panel); + dll_insert(&layout->free_sentinel, panel); + --layout->panel_count; + + panel->ALLOCED = 0; +} + +internal Divider_And_ID +layout_calc_divider_id(Editing_Layout *layout, Panel_Divider *divider){ + Divider_And_ID result; + result.divider = divider; + result.id = (i32)(divider - layout->dividers); + return result; +} + +struct Split_Result{ + Panel_Divider *divider; + Panel *panel; +}; + +internal Split_Result +layout_split_panel(Editing_Layout *layout, Panel *panel, b32 vertical){ + Split_Result result = {}; + Divider_And_ID div = {}, parent_div = {}; + Panel_And_ID new_panel = {}; + + div = layout_alloc_divider(layout); + if (panel->parent != -1){ + parent_div = layout_get_divider(layout, panel->parent); + if (panel->which_child == -1){ + parent_div.divider->child1 = div.id; + } + else{ + parent_div.divider->child2 = div.id; + } + } + + div.divider->parent = panel->parent; + div.divider->which_child = panel->which_child; + if (vertical){ + div.divider->v_divider = 1; + div.divider->pos = (panel->full.x0 + panel->full.x1) / 2; + } + else{ + div.divider->v_divider = 0; + div.divider->pos = (panel->full.y0 + panel->full.y1) / 2; + } + + new_panel = layout_alloc_panel(layout); + panel->parent = div.id; + panel->which_child = -1; + new_panel.panel->parent = div.id; + new_panel.panel->which_child = 1; + + result.divider = div.divider; + result.panel = new_panel.panel; + + return result; +} + +internal void +panel_fix_internal_area(Panel *panel){ + panel->inner.x0 = panel->full.x0 + panel->l_margin; + panel->inner.x1 = panel->full.x1 - panel->r_margin; + panel->inner.y0 = panel->full.y0 + panel->t_margin; + panel->inner.y1 = panel->full.y1 - panel->b_margin; +} + +internal void +layout_fix_all_panels(Editing_Layout *layout){ + Panel *panel; + Panel_Divider *dividers = layout->dividers; + i32 panel_count = layout->panel_count; + i32_Rect r; + i32 pos, which_child, action; + Divider_And_ID div; + + if (panel_count > 1){ + for (panel = layout->used_sentinel.next; + panel != &layout->used_sentinel; + panel = panel->next){ + + r.x0 = 0; + r.x1 = r.x0 + layout->full_width; + r.y0 = 0; + r.y1 = r.y0 + layout->full_height; + + which_child = panel->which_child; + + div.id = panel->parent; + + for (;;){ + Assert(div.id != -1); + div.divider = dividers + div.id; + pos = div.divider->pos; + + action = (div.divider->v_divider << 1) | (which_child > 0); + switch (action){ + case 0: // v_divider : 0, which_child : -1 + if (pos < r.y1) r.y1 = pos; + break; + case 1: // v_divider : 0, which_child : 1 + if (pos > r.y0) r.y0 = pos; + break; + case 2: // v_divider : 1, which_child : -1 + if (pos < r.x1) r.x1 = pos; + break; + case 3: // v_divider : 1, which_child : 1 + if (pos > r.x0) r.x0 = pos; + break; + } + + if (div.id != layout->root){ + div.id = div.divider->parent; + which_child = div.divider->which_child; + } + else{ + break; + } + } + + panel->full = r; + panel_fix_internal_area(panel); + } + } + + else{ + panel = layout->used_sentinel.next; + panel->full.x0 = 0; + panel->full.y0 = 0; + panel->full.x1 = layout->full_width; + panel->full.y1 = layout->full_height; + panel_fix_internal_area(panel); + } +} + +internal void +layout_refit(Editing_Layout *layout, i32 prev_width, i32 prev_height){ + + Panel_Divider *dividers = layout->dividers; + i32 max = layout->panel_max_count - 1; + + f32 h_ratio, v_ratio; + + Panel_Divider *divider = dividers; + i32 i; + + if (layout->panel_count > 1){ + Assert(prev_width != 0 && prev_height != 0); + + h_ratio = ((f32)layout->full_width) / prev_width; + v_ratio = ((f32)layout->full_height) / prev_height; + + for (i = 0; i < max; ++i, ++divider){ + if (divider->v_divider){ + divider->pos = ROUND32((divider->pos) * h_ratio); + } + else{ + divider->pos = ROUND32((divider->pos) * v_ratio); + } + } + } + + layout_fix_all_panels(layout); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_linux_keyboard.cpp b/test_data/lots_of_files/4ed_linux_keyboard.cpp new file mode 100644 index 0000000..5d7dbec --- /dev/null +++ b/test_data/lots_of_files/4ed_linux_keyboard.cpp @@ -0,0 +1,92 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 14.11.2015 + * + * Linux-US Keyboard layer for 4coder + * + */ + +// TOP + +#include "4ed_keyboard.cpp" + +internal void +keycode_init(Display* dpy){ +#if 0 + // NOTE(inso): these are for XInput, currently not used. + + keycode_lookup_table[KEY_BACKSPACE] = codes->back; + keycode_lookup_table[KEY_DELETE] = codes->del; + keycode_lookup_table[KEY_UP] = codes->up; + keycode_lookup_table[KEY_DOWN] = codes->down; + keycode_lookup_table[KEY_LEFT] = codes->left; + keycode_lookup_table[KEY_RIGHT] = codes->right; + keycode_lookup_table[KEY_INSERT] = codes->insert; + keycode_lookup_table[KEY_HOME] = codes->home; + keycode_lookup_table[KEY_END] = codes->end; + keycode_lookup_table[KEY_PAGEUP] = codes->page_up; + keycode_lookup_table[KEY_PAGEDOWN] = codes->page_down; + keycode_lookup_table[KEY_ESC] = codes->esc; +#endif + + // NOTE(inso): This looks a bit dumb, but it's the best way I can think of to do it, since: + // KeySyms are the type representing "virtual" keys, like XK_BackSpace, but they are 32-bit ints. + // KeyCodes are guaranteed to fit in 1 byte (and therefore the keycode_lookup_table) but + // have dynamic numbers assigned by the XServer. + // There is XKeysymToKeycode, but it only returns 1 KeyCode for a KeySym. I have my capslock + // rebound to esc, so there are two KeyCodes for the XK_Escape KeyCode but XKeysymToKeycode only + // gets one of them, hence the need for this crazy lookup which works correctly with rebound keys. + + memset(keycode_lookup_table, 0, sizeof(keycode_lookup_table)); + + struct SymMapping { + KeySym sym; + Code code; + } sym_table[] = { + { XK_BackSpace, key_back }, + { XK_Delete, key_del }, + { XK_Up, key_up }, + { XK_Down, key_down }, + { XK_Left, key_left }, + { XK_Right, key_right }, + { XK_Insert, key_insert }, + { XK_Home, key_home }, + { XK_End, key_end }, + { XK_Page_Up, key_page_up }, + { XK_Page_Down, key_page_down }, + { XK_Escape, key_esc } + }; + + const int table_size = sizeof(sym_table) / sizeof(struct SymMapping); + + int key_min, key_max, syms_per_code; + XDisplayKeycodes(dpy, &key_min, &key_max); + + int key_count = (key_max - key_min) + 1; + + KeySym* syms = XGetKeyboardMapping( + dpy, + key_min, + key_count, + &syms_per_code + ); + + if(!syms) return; + + int key = key_min; + for(int i = 0; i < key_count * syms_per_code; ++i){ + for(int j = 0; j < table_size; ++j){ + if(sym_table[j].sym == syms[i]){ + keycode_lookup_table[key + (i/syms_per_code)] = sym_table[j].code; + break; + } + } + } + + XFree(syms); + +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_math.cpp b/test_data/lots_of_files/4ed_math.cpp new file mode 100644 index 0000000..6892e94 --- /dev/null +++ b/test_data/lots_of_files/4ed_math.cpp @@ -0,0 +1,774 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 15.05.2015 + * + * Math functions for 4coder + * + */ + +// TOP + +/* + * Scalar operators + */ + +#define C_MATH 1 + +#define DEG_TO_RAD 0.0174533f + +#if C_MATH +#include <math.h> +#endif + +inline f32 +ABS(f32 x){ + if (x < 0) x = -x; + return x; +} + +inline f32 +MOD(f32 x, i32 m){ +#if C_MATH + f32 whole, frac; + frac = modff(x, &whole); + return ((i32)(whole) % m) + frac; +#endif +} + +inline f32 +SQRT(f32 x){ +#if C_MATH + return sqrt(x); +#endif +} + +inline f32 +SIN(f32 x_degrees){ +#if C_MATH + return sinf(x_degrees * DEG_TO_RAD); +#endif +} + +inline f32 +COS(f32 x_degrees){ +#if C_MATH + return cosf(x_degrees * DEG_TO_RAD); +#endif +} + +/* + * Rounding + */ + +inline i32 +TRUNC32(real32 x) { return (i32)x; } + +inline i32 +FLOOR32(real32 x) { return (i32)(x)-((x!=(i32)(x) && x<0)?1:0); } + +inline i32 +CEIL32(real32 x) { return (i32)(x)+((x!=(i32)(x) && x>0)?1:0); } + +inline i32 +ROUND32(real32 x) { return FLOOR32(x + .5f); } + +inline i32 +DIVCEIL32(i32 n, i32 d) { + i32 q = (n/d); + return q + (q*d < n); +} + +inline real32 +FRACPART32(real32 x) { return x - (i32)x; } + +inline u32 +ROUNDPOT32(u32 v){ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +/* + * Rectangles + */ + +struct i32_Rect{ + i32 x0, y0; + i32 x1, y1; +}; + +struct f32_Rect{ + f32 x0, y0; + f32 x1, y1; +}; + +inline i32_Rect +i32R(i32 l, i32 t, i32 r, i32 b){ + i32_Rect rect; + rect.x0 = l; rect.y0 = t; + rect.x1 = r; rect.y1 = b; + return rect; +} + +inline i32_Rect +i32R(f32_Rect r){ + i32_Rect rect; + rect.x0 = (i32)r.x0; + rect.y0 = (i32)r.y0; + rect.x1 = (i32)r.x1; + rect.y1 = (i32)r.y1; + return rect; +} + +inline i32_Rect +i32XYWH(i32 x, i32 y, i32 w, i32 h){ + i32_Rect rect; + rect.x0 = x; rect.y0 = y; + rect.x1 = x+w; rect.y1 = y+h; + return rect; +} + +inline f32_Rect +f32R(f32 l, f32 t, f32 r, f32 b){ + f32_Rect rect; + rect.x0 = l; rect.y0 = t; + rect.x1 = r; rect.y1 = b; + return rect; +} + +inline f32_Rect +f32R(i32_Rect r){ + f32_Rect rect; + rect.x0 = (f32)r.x0; + rect.y0 = (f32)r.y0; + rect.x1 = (f32)r.x1; + rect.y1 = (f32)r.y1; + return rect; +} + +inline f32_Rect +f32XYWH(f32 x, f32 y, f32 w, f32 h){ + f32_Rect rect; + rect.x0 = x; rect.y0 = y; + rect.x1 = x+w; rect.y1 = y+h; + return rect; +} + +inline b32 +hit_check(i32 x, i32 y, i32 x0, i32 y0, i32 x1, i32 y1){ + return (x >= x0 && x < x1 && y >= y0 && y < y1); +} + +inline b32 +hit_check(i32 x, i32 y, i32_Rect rect){ + return (hit_check(x, y, rect.x0, rect.y0, rect.x1, rect.y1)); +} + +inline b32 +hit_check(i32 x, i32 y, f32 x0, f32 y0, f32 x1, f32 y1){ + return (x >= x0 && x < x1 && y >= y0 && y < y1); +} + +inline b32 +hit_check(i32 x, i32 y, f32_Rect rect){ + return (hit_check(x, y, rect.x0, rect.y0, rect.x1, rect.y1)); +} + +inline b32 +positive_area(i32_Rect rect){ + return (rect.x0 < rect.x1 && rect.y0 < rect.y1); +} + +inline i32_Rect +get_inner_rect(i32_Rect outer, i32 margin){ + i32_Rect r; + r.x0 = outer.x0 + margin; + r.y0 = outer.y0 + margin; + r.x1 = outer.x1 - margin; + r.y1 = outer.y1 - margin; + return r; +} + +inline b32 +fits_inside(i32_Rect rect, i32_Rect outer){ + return (rect.x0 >= outer.x0 && rect.x1 <= outer.x1 && + rect.y0 >= outer.y0 && rect.y1 <= outer.y1); +} + +inline i32_Rect +rect_clamp_to_rect(i32_Rect rect, i32_Rect clamp_box){ + if (rect.x0 < clamp_box.x0) rect.x0 = clamp_box.x0; + if (rect.y0 < clamp_box.y0) rect.y0 = clamp_box.y0; + if (rect.x1 > clamp_box.x1) rect.x1 = clamp_box.x1; + if (rect.y1 > clamp_box.y1) rect.y1 = clamp_box.y1; + return rect; +} + +inline i32_Rect +rect_clamp_to_rect(i32 left, i32 top, i32 right, i32 bottom, i32_Rect clamp_box){ + return rect_clamp_to_rect(i32R(left, top, right, bottom), clamp_box); +} + +/* + * Vectors + */ + +struct Vec2{ + union{ + struct{ + real32 x, y; + }; + struct{ + real32 v[2]; + }; + }; +}; + +struct Vec3{ + union{ + struct{ + real32 x, y, z; + }; + struct{ + real32 r, g, b; + }; + struct{ + Vec2 xy; + real32 _z; + }; + struct{ + real32 _x; + Vec2 yz; + }; + struct{ + real32 v[3]; + }; + }; +}; + +struct Vec4{ + union{ + struct{ + real32 r, g, b, a; + }; + + struct{ + real32 h, s, l, __a; + }; + struct{ + real32 x, y, z, w; + }; + struct{ + Vec3 rgb; + real32 _a; + }; + struct{ + Vec3 xyz; + real32 _w; + }; + struct{ + real32 _x; + Vec3 yzw; + }; + struct{ + real32 v[4]; + }; + }; +}; + +inline internal Vec2 +V2(real32 x, real32 y){ + Vec2 result; + result.x = x; + result.y = y; + return result; +} + +inline internal Vec3 +V3(real32 x, real32 y, real32 z){ + Vec3 result; + result.x = x; + result.y = y; + result.z = z; + return result; +} + +inline internal Vec4 +V4(real32 x, real32 y, real32 z, real32 w){ + Vec4 result; + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; +} + +inline internal Vec2 +operator+(Vec2 a, Vec2 b){ + Vec2 result; + result.x = a.x + b.x; + result.y = a.y + b.y; + return result; +} + +inline internal Vec3 +operator+(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; + return result; +} + +inline internal Vec4 +operator+(Vec4 a, Vec4 b){ + Vec4 result; + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; + result.w = a.w + b.w; + return result; +} + +inline internal Vec2 +operator-(Vec2 a, Vec2 b){ + Vec2 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + return result; +} + +inline internal Vec3 +operator-(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + return result; +} + +inline internal Vec4 +operator-(Vec4 a, Vec4 b){ + Vec4 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + result.w = a.w - b.w; + return result; +} + +inline internal Vec2 +operator*(Vec2 a, real32 k){ + Vec2 result; + result.x = a.x * k; + result.y = a.y * k; + return result; +} + +inline internal Vec3 +operator*(Vec3 a, real32 k){ + Vec3 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + return result; +} + +inline internal Vec4 +operator*(Vec4 a, real32 k){ + Vec4 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + result.w = a.w * k; + return result; +} + +inline internal Vec2 +operator*(real32 k, Vec2 a){ + Vec2 result; + result.x = a.x * k; + result.y = a.y * k; + return result; +} + +inline internal Vec3 +operator*(real32 k, Vec3 a){ + Vec3 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + return result; +} + +inline internal Vec4 +operator*(real32 k, Vec4 a){ + Vec4 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + result.w = a.w * k; + return result; +} + +inline internal Vec2& +operator+=(Vec2 &a, Vec2 b){ + a = (a + b); + return a; +} + +inline internal Vec3& +operator+=(Vec3 &a, Vec3 b){ + a = (a + b); + return a; +} + +inline internal Vec4& +operator+=(Vec4 &a, Vec4 b){ + a = (a + b); + return a; +} + +inline internal Vec2& +operator-=(Vec2 &a, Vec2 b){ + a = (a - b); + return a; +} + +inline internal Vec3& +operator-=(Vec3 &a, Vec3 b){ + a = (a - b); + return a; +} + +inline internal Vec4& +operator-=(Vec4 &a, Vec4 b){ + a = (a - b); + return a; +} + +inline internal Vec2& +operator*=(Vec2 &a, real32 k){ + a = (a * k); + return a; +} + +inline internal Vec3& +operator*=(Vec3 &a, real32 k){ + a = (a * k); + return a; +} + +inline internal Vec4& +operator*=(Vec4 &a, real32 k){ + a = (a * k); + return a; +} + +inline internal real32 +dot(Vec2 a, Vec2 b){ + real32 result; + result = a.x*b.x + a.y*b.y; + return result; +} + +inline internal real32 +dot(Vec3 a, Vec3 b){ + real32 result; + result = a.x*b.x + a.y*b.y + a.z*b.z; + return result; +} + +inline internal real32 +dot(Vec4 a, Vec4 b){ + real32 result; + result = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; + return result; +} + +inline internal Vec3 +cross(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.y*b.z - b.y*a.z; + result.y = a.z*b.x - b.z*a.x; + result.z = a.x*b.y - b.x*a.y; + return result; +} + +inline internal Vec2 +hadamard(Vec2 a, Vec2 b){ + Vec2 result; + result.x = a.x*b.x; + result.y = a.y*b.y; + return result; +} + +inline internal Vec3 +hadamard(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.x*b.x; + result.y = a.y*b.y; + result.z = a.z*b.z; + return result; +} + +inline internal Vec4 +hadamard(Vec4 a, Vec4 b){ + Vec4 result; + result.x = a.x*b.x; + result.y = a.y*b.y; + result.z = a.z*b.z; + result.w = a.w*b.w; + return result; +} + +inline internal Vec2 +perp(Vec2 v){ + Vec2 result; + result.x = -v.y; + result.y = v.x; + return result; +} + +inline Vec2 +polar_to_cartesian(real32 theta_degrees, real32 length){ + Vec2 result; + result.x = COS(theta_degrees)*length; + result.y = SIN(theta_degrees)*length; + return result; +} + +inline Vec2 +rotate(Vec2 v, real32 theta_degrees){ + Vec2 result; + real32 c, s; + c = COS(theta_degrees); + s = SIN(theta_degrees); + result.x = v.x*c - v.y*s; + result.y = v.x*s + v.y*c; + return result; +} + +/* + * Coordinates + */ + +struct Matrix2{ + Vec2 x_axis; + Vec2 y_axis; +}; + +internal Matrix2 +invert(Vec2 x_axis, Vec2 y_axis){ + Matrix2 result = {}; + real32 det = 1.f / (x_axis.x*y_axis.y - x_axis.y*y_axis.x); + result.x_axis.x = y_axis.y*det; + result.y_axis.x = -y_axis.x*det; + result.x_axis.y = -x_axis.y*det; + result.y_axis.y = x_axis.x*det; + return result; +} + +internal Matrix2 +invert(Matrix2 m){ + Matrix2 result = {}; + real32 det = 1.f / (m.x_axis.x*m.y_axis.y - m.x_axis.y*m.y_axis.x); + result.x_axis.x = m.y_axis.y*det; + result.y_axis.x = -m.y_axis.x*det; + result.x_axis.y = -m.x_axis.y*det; + result.y_axis.y = m.x_axis.x*det; + return result; +} + +/* + * Lerps, Clamps, Thresholds, Etc + */ + +inline real32 +lerp(real32 a, real32 t, real32 b){ + return a + (b-a)*t; +} + +inline Vec2 +lerp(Vec2 a, real32 t, Vec2 b){ + return a + (b-a)*t; +} + +inline Vec3 +lerp(Vec3 a, real32 t, Vec3 b){ + return a + (b-a)*t; +} + +inline Vec4 +lerp(Vec4 a, real32 t, Vec4 b){ + return a + (b-a)*t; +} + +inline real32 +unlerp(real32 a, real32 x, real32 b){ + return (x - a) / (b - a); +} + +inline real32 +clamp(real32 a, real32 n, real32 z){ + return (n<a)?(a):((n>z)?(z):n); +} + +/* + * Color + */ + +// TODO(allen): Convert colors to Vec4 +inline internal u32 +color_blend(u32 a, real32 t, u32 b){ + union{ + u8 byte[4]; + u32 comp; + } A, B, R; + + A.comp = a; + B.comp = b; + + R.byte[0] = (u8)lerp(A.byte[0], t, B.byte[0]); + R.byte[1] = (u8)lerp(A.byte[1], t, B.byte[1]); + R.byte[2] = (u8)lerp(A.byte[2], t, B.byte[2]); + R.byte[3] = (u8)lerp(A.byte[3], t, B.byte[3]); + + return R.comp; +} + +internal Vec3 +unpack_color3(u32 color){ + Vec3 result; + result.r = ((color >> 16) & 0xFF) / 255.f; + result.g = ((color >> 8) & 0xFF) / 255.f; + result.b = ((color >> 0) & 0xFF) / 255.f; + return result; +} + +internal Vec4 +unpack_color4(u32 color){ + Vec4 result; + result.a = ((color >> 24) & 0xFF) / 255.f; + result.r = ((color >> 16) & 0xFF) / 255.f; + result.g = ((color >> 8) & 0xFF) / 255.f; + result.b = ((color >> 0) & 0xFF) / 255.f; + return result; +} + +internal u32 +pack_color4(Vec4 color){ + u32 result = + ((u8)(color.a * 255) << 24) | + ((u8)(color.r * 255) << 16) | + ((u8)(color.g * 255) << 8) | + ((u8)(color.b * 255) << 0); + return result; +} + +internal Vec4 +rgba_to_hsla(Vec4 rgba){ + Vec4 hsla = {}; + real32 max, min, delta; + i32 maxc; + hsla.a = rgba.a; + max = rgba.r; min = rgba.r; + maxc = 0; + if (rgba.r < rgba.g){ + max = rgba.g; + maxc = 1; + } + if (rgba.b > max){ + max = rgba.b; + maxc = 2; + } + if (rgba.r > rgba.g){ + min = rgba.g; + } + if (rgba.b < min){ + min = rgba.b; + } + delta = max - min; + + hsla.z = (max + min) * .5f; + if (delta == 0){ + hsla.x = 0.f; + hsla.y = 0.f; + } + else{ + switch (maxc){ + case 0: + { + hsla.x = (rgba.g - rgba.b) / delta; + hsla.x += (rgba.g < rgba.b) * 6.f; + }break; + + case 1: + { + hsla.x = (rgba.b - rgba.r) / delta; + hsla.x += 2.f; + }break; + + case 2: + { + hsla.x = (rgba.r - rgba.g) / delta; + hsla.x += 4.f; + }break; + } + hsla.x *= (1/6.f); // * 60 / 360 + hsla.y = delta / (1.f - ABS(2.f*hsla.z - 1.f)); + } + + return hsla; +} + +internal Vec4 +hsla_to_rgba(Vec4 hsla){ + if (hsla.h >= 1.f) hsla.h = 0.f; + Vec4 rgba = {}; + real32 C, X, m; + i32 H; + rgba.a = hsla.a; + C = (1.f - ABS(2*hsla.z - 1.f)) * hsla.y; + X = C * (1.f-ABS(MOD(hsla.x*6.f, 2)-1.f)); + m = hsla.z - C*.5f; + H = FLOOR32(hsla.x * 6.f); + switch (H){ + case 0: + rgba.r = C; rgba.g = X; rgba.b = 0; + break; + + case 1: + rgba.r = X; rgba.g = C; rgba.b = 0; + break; + + case 2: + rgba.r = 0; rgba.g = C; rgba.b = X; + break; + + case 3: + rgba.r = 0; rgba.g = X; rgba.b = C; + break; + + case 4: + rgba.r = X; rgba.g = 0; rgba.b = C; + break; + + case 5: + rgba.r = C; rgba.g = 0; rgba.b = X; + break; + } + rgba.r += m; + rgba.g += m; + rgba.b += m; + return rgba; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_mem.cpp b/test_data/lots_of_files/4ed_mem.cpp new file mode 100644 index 0000000..84120e7 --- /dev/null +++ b/test_data/lots_of_files/4ed_mem.cpp @@ -0,0 +1,256 @@ +/* + * 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){ + --boundary; + data->pos = (data->pos + boundary) & (~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/test_data/lots_of_files/4ed_meta.h b/test_data/lots_of_files/4ed_meta.h new file mode 100644 index 0000000..9793924 --- /dev/null +++ b/test_data/lots_of_files/4ed_meta.h @@ -0,0 +1,200 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Meta setup for project codename "4ed" + * + */ + +#ifndef FRED_META_H +#define FRED_META_H + +#include <string.h> +#include <stdint.h> + +typedef uint8_t u8; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; + +typedef int8_t i8; +typedef int64_t i64; +typedef int32_t i32; +typedef int16_t i16; + +typedef i32 bool32; +typedef i8 bool8; +typedef i32 b32; +typedef i8 b8; + +typedef uint8_t byte; + +typedef float real32; +typedef double real64; +typedef float f32; +typedef double f64; + +struct Data{ + byte *data; + i32 size; +}; + +#define external extern "C" +#define internal static +#define globalvar static +#define persist static + +#define globalconst static const + +inline i32 +raw_ptr_dif(void *a, void *b) { return (i32)((u8*)a - (u8*)b); } + +#define COMP_ID_(a,b,c,d) (d << 24) | (c << 16) | (b << 8) | a +#define COMPOSE_ID(a,b,c,d) (COMP_ID_((a),(b),(c),(d))) + +#define S(X) #X +#define S_(X) S(X) +#define S__LINE__ S_(__LINE__) + +#if FRED_PRINT_DEBUG == 1 +internal void +_OutDbgStr(u8*); +# include <stdio.h> +# if FRED_PRINT_DEBUG_FILE_LINE +# define FredDbg(con, size, ...) {_OutDbgStr((u8*)("FILE:"__FILE__"LINE:"S__LINE__"\n")); char msg[size]; sprintf(msg, __VA_ARGS__); _OutDbgStr((u8*)msg);} +# else +# define FredDbg(con, size, ...) {char msg[size]; sprintf(msg, __VA_ARGS__); _OutDbgStr((u8*)msg);} +# endif +#elif FRED_PRINT_DEBUG == 2 +# include <stdio.h> +# if FRED_PRINT_DEBUG_FILE_LINE +# define FredDbg(con, size, ...) {fprintf((con)->log, ("FILE:"__FILE__"LINE:"S__LINE__"\n")); fprintf(__VA_ARGS__);} +# else +# define FredDbg(con, size, ...) {fprintf((con)->log, __VA_ARGS__);} +# endif +#else +# define FredDbg(con, size, ...) +#endif + +#if FRED_INTERNAL && FRED_FULL_ERRORS +# include <stdio.h> +# define FatalErrorFormat(alt, size, ...) {char msg[size]; sprintf(msg, __VA_ARGS__); FatalError(msg);} +#else +# define FatalErrorFormat(alt, size, ...) {FatalError(alt);} +#endif + +#if FRED_SLOW +# define Assert(c) assert(c) +#else +# define Assert(c) +#endif + +#define TentativeAssert(c) Assert(c) + +#define FatalError(message) system_fatal_error((u8*)message) + +#define AllowLocal(name) (void)name +#define ArrayCount(array) (sizeof(array)/sizeof(array[0])) +#define OffsetOfStruct(S,c) ((i64)(& ((S*)0)->c )) +#define OffsetOfPtr(s,c) ((i64)((char*)(&(s)->c) - (char*)(s))) + +#define Swap(a,b) {auto t = a; a = b; b = t;} + +#ifndef literal +#define literal(s) s, (sizeof(s)-1) +#endif + +#define Min(a,b) (((a)<(b))?(a):(b)) +#define Max(a,b) (((a)>(b))?(a):(b)) + +#define TMax(t,v) globalconst t max_##t = v +TMax(u8, 255); +TMax(u16, 65535); +TMax(u32, 4294967295); +TMax(u64, 18446744073709551615U); + +TMax(i8, 127); +TMax(i16, 32767); +TMax(i32, 2147483647); +TMax(i64, 9223372036854775807); +#undef TMax + +#define TMin(t) globalconst t min_##t = 0 +TMin(u8); +TMin(u16); +TMin(u32); +TMin(u64); +#undef TMin + +#define TMin(t,v) globalconst t min_##t = ((t)v) +TMin(i8, -0xF0); +TMin(i16, -0xF000); +TMin(i32, -0xF00000); +TMin(i64, -0xF0000000LL); +#undef TMin + +internal i32 +LargeRoundUp(i32 x, i32 granularity){ + i32 original_x = x; + x /= granularity; + x *= granularity; + if (x < original_x){ + x += granularity; + } + return x; +} + +#define Bit_0 (1 << 0) +#define Bit_1 (1 << 1) +#define Bit_2 (1 << 2) +#define Bit_3 (1 << 3) +#define Bit_4 (1 << 4) +#define Bit_5 (1 << 5) +#define Bit_6 (1 << 6) +#define Bit_7 (1 << 7) + +#define Bit_8 (1 << 8) +#define Bit_9 (1 << 9) +#define Bit_10 (1 << 10) +#define Bit_11 (1 << 11) +#define Bit_12 (1 << 12) +#define Bit_13 (1 << 13) +#define Bit_14 (1 << 14) +#define Bit_15 (1 << 15) + +#define Bit_16 (1 << 16) +#define Bit_17 (1 << 17) +#define Bit_18 (1 << 18) +#define Bit_19 (1 << 19) +#define Bit_20 (1 << 20) +#define Bit_21 (1 << 21) +#define Bit_22 (1 << 22) +#define Bit_23 (1 << 23) + +#define Bit_24 (1 << 24) +#define Bit_25 (1 << 25) +#define Bit_26 (1 << 26) +#define Bit_27 (1 << 27) +#define Bit_28 (1 << 28) +#define Bit_29 (1 << 29) +#define Bit_30 (1 << 30) +#define Bit_31 (1 << 31) + +#define Byte_0 (0xFFU) +#define Byte_1 (0xFFU << 8) +#define Byte_2 (0xFFU << 16) +#define Byte_3 (0xFFU << 24) +#define Byte_4 (0xFFU << 32) +#define Byte_5 (0xFFU << 40) +#define Byte_6 (0xFFU << 48) +#define Byte_7 (0xFFU << 56) + +#define bytes(n) (n) +#define Kbytes(n) ((n) << 10) +#define Mbytes(n) ((n) << 20) +#define Gbytes(n) (((u64)n) << 30) +#define Tbytes(n) (((u64)n) << 40) + +#endif + diff --git a/test_data/lots_of_files/4ed_metagen.cpp b/test_data/lots_of_files/4ed_metagen.cpp new file mode 100644 index 0000000..7128de5 --- /dev/null +++ b/test_data/lots_of_files/4ed_metagen.cpp @@ -0,0 +1,462 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 25.02.2016 + * + * File editing view for 4coder + * + */ + +// TOP + +#define FCPP_STRING_IMPLEMENTATION +#include "4coder_string.h" + +struct Struct_Field{ + char *type; + char *name; +}; + +void to_lower(char *src, char *dst){ + char *c, ch; + for (c = src; *c != 0; ++c){ + ch = char_to_lower(*c); + *dst++ = ch; + } + *dst = 0; +} + +void to_upper(char *src, char *dst){ + char *c, ch; + for (c = src; *c != 0; ++c){ + ch = char_to_upper(*c); + *dst++ = ch; + } + *dst = 0; +} + +void to_camel(char *src, char *dst){ + char *c, ch; + int is_first = 1; + for (c = src; *c != 0; ++c){ + ch = *c; + if (char_is_alpha_numeric_true(ch)){ + if (is_first){ + is_first = 0; + ch = char_to_upper(ch); + } + else{ + ch = char_to_lower(ch); + } + } + else{ + is_first = 1; + } + *dst++ = ch; + } + *dst = 0; +} + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +void struct_begin(FILE *file, char *name){ + fprintf(file, "struct %s{\n", name); +} + +void struct_fields(FILE *file, Struct_Field *fields, int count){ + int i; + for (i = 0; i < count; ++i){ + fprintf(file, " %s %s;\n", fields[i].type, fields[i].name); + } +} + +void struct_end(FILE *file){ + fprintf(file, "};\n\n"); +} + + +void enum_begin(FILE *file, char *name){ + fprintf(file, "enum %s{\n", name); +} + + +char *keys_that_need_codes[] = { + "back", + "up", + "down", + "left", + "right", + "del", + "insert", + "home", + "end", + "page_up", + "page_down", + "esc", + + "f1", + "f2", + "f3", + "f4", + "f5", + "f6", + "f7", + "f8", + + "f9", + "f10", + "f11", + "f12", + "f13", + "f14", + "f15", + "f16", +}; + +char* generate_keycode_enum(){ + FILE *file; + char *filename = "4coder_keycodes.h"; + int i, count; + unsigned char code = 1; + + file = fopen(filename, "wb"); + fprintf(file, "enum Key_Code{\n"); + count = ArrayCount(keys_that_need_codes); + for (i = 0; i < count;){ + if (strcmp(keys_that_need_codes[i], "f1") == 0 && code < 0x7F){ + code = 0x7F; + } + switch (code){ + case '\n': code++; break; + case '\t': code++; break; + case 0x20: code = 0x7F; break; + default: + fprintf(file, " key_%s = %d,\n", keys_that_need_codes[i++], code++); + break; + } + } + fprintf(file, "};\n"); + fclose(file); + return(filename); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +char daction_enum_name[] = "Action_Type"; +char *daction_enum[] = { + "OPEN", + "OPEN_BACKGROUND", + "SET_LINE", + "SAVE_AS", + "SAVE", + "NEW", + "SWITCH", + "TRY_KILL", + "KILL", + "TOUCH_FILE", +}; + +char str_alloc_copy[] = +"internal String\n" +"str_alloc_copy(General_Memory *general, String str){\n" +" String result;\n" +" result.memory_size = str.memory_size + 1;\n" +" result.size = str.size;\n" +" result.str = (char*)general_memory_allocate(general, result.memory_size, 0);\n" +" memcpy(result.str, str.str, str.size);\n" +" result.str[result.size] = 0;\n" +" return(result);" +"}\n\n"; + +char daction_name[] = "Delayed_Action"; +Struct_Field daction_fields[] = { + {"Action_Type", "type"}, +}; +Struct_Field daction_fields_primary[] = { + {"String", "string"}, + {"Panel*", "panel"}, + {"Editing_File*", "file"}, + {"i32", "integer"}, +}; +enum Daction_Field_Handle{ + dfph_null, + dfph_string, + dfph_panel, + dfph_file, + dfph_integer, +}; +Daction_Field_Handle dact_param_sets[] = { + dfph_string, dfph_null, + dfph_panel, dfph_null, + dfph_file, dfph_null, + dfph_file, dfph_panel, dfph_null, + dfph_string, dfph_panel, dfph_null, + dfph_string, dfph_file, dfph_null, + dfph_panel, dfph_integer, dfph_null, +}; + +char delay_name[] = "Delay"; +Struct_Field delay_fields[] = { + {"General_Memory*", "general"}, + {"Delayed_Action*", "acts"}, + {"i32", "count"}, + {"i32", "max"}, +}; + +char delayed_action_function_top[] = +"inline Delayed_Action*\n" +"delayed_action_(Delay *delay, Action_Type type"; + +char delayed_action_function_bottom[] = +"){\n" +" Delayed_Action *result;\n" +" if (delay->count == delay->max){\n" +" delay->max *= 2;\n" +" delay->acts = (Delayed_Action*)general_memory_reallocate(" +"delay->general, delay->acts, delay->count*sizeof(Delayed_Action), delay->max*sizeof(Delayed_Action), 0);\n" +" }\n" +" result = delay->acts + delay->count++;\n" +" *result = {};\n" +" result->type = type;\n" +" return(result);\n" +"}\n\n"; + +char delayed_action_special_param[] = ", %s %s"; + +char delayed_action_specialized_middle[] = +"){\n" +" Delayed_Action *result;\n" +" result = delayed_action_(delay, type);\n"; + +char delayed_action_special_line[] = +" result->%s = %s;\n"; + +char delayed_action_special_string_line[] = +" result->%s = str_alloc_copy(delay->general, %s);\n"; + +char delayed_action_specialized_bottom[] = +" return(result);\n" +"}\n\n"; + +char delayed_action_macro[] = +"#define delayed_%s(delay, ...) delayed_action_(delay, DACT_%s, __VA_ARGS__)\n"; + +char delayed_action_repush_function[] = +"inline Delayed_Action*\n" +"delayed_action_repush(Delay *delay, Delayed_Action *act){\n" +" Delayed_Action *new_act = delayed_action_(delay, (Action_Type)0);\n" +" *new_act = *act;\n" +" if (act->string.str){\n" +" new_act->string = str_alloc_copy(delay->general, act->string);\n" +" }\n" +" return(new_act);\n" +"}\n\n"; + +char* generate_delayed_action(){ + FILE *file; + char *filename = "4ed_delay.cpp"; + char scratch[256]; + int i,j; + + file = fopen(filename, "wb"); + + fprintf(file, "enum %s{\n", daction_enum_name); + for (i = 0; i < ArrayCount(daction_enum); ++i){ + fprintf(file, " DACT_%s,\n", daction_enum[i]); + } + fprintf(file, "};\n\n"); + + struct_begin(file, daction_name); + struct_fields(file, daction_fields, ArrayCount(daction_fields)); + struct_fields(file, daction_fields_primary, ArrayCount(daction_fields_primary)); + struct_end(file); + + struct_begin(file, delay_name); + struct_fields(file, delay_fields, ArrayCount(delay_fields)); + struct_end(file); + + fprintf(file, "%s", str_alloc_copy); + fprintf(file, "%s%s", delayed_action_function_top, delayed_action_function_bottom); + + for (i = 0; i < ArrayCount(dact_param_sets); ++i){ + j = i; + fprintf(file, "%s", delayed_action_function_top); + for (; dact_param_sets[i] != dfph_null; ++i){ + Struct_Field field = daction_fields_primary[dact_param_sets[i] - 1]; + fprintf(file, delayed_action_special_param, field.type, field.name); + } + fprintf(file, "%s", delayed_action_specialized_middle); + for (; dact_param_sets[j] != dfph_null; ++j){ + int handle = (int)(dact_param_sets[j]); + Struct_Field field = daction_fields_primary[handle - 1]; + if (handle == dfph_string){ + fprintf(file, delayed_action_special_string_line, field.name, field.name); + } + else{ + fprintf(file, delayed_action_special_line, field.name, field.name); + } + } + fprintf(file, "%s", delayed_action_specialized_bottom); + } + + fprintf(file, "%s", delayed_action_repush_function); + + for (i = 0; i < ArrayCount(daction_enum); ++i){ + to_lower(daction_enum[i], scratch); + fprintf(file, delayed_action_macro, scratch, daction_enum[i]); + } + + return(filename); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +char* bar_style_fields[] = { + "bar", + "bar_active", + "base", + "pop1", + "pop2", +}; + +char* main_style_fields[] = { + "back", + "margin", + "margin_hover", + "margin_active", + "cursor", + "at_cursor", + "highlight", + "at_highlight", + "mark", + "default", + "comment", + "keyword", + "str_constant", + "char_constant", + "int_constant", + "float_constant", + "bool_constant", + "preproc", + "include", + "special_character", + "highlight_junk", + "highlight_white", + "paste", + "undo", + "next_undo", +}; + +static char* +make_style_tag(char *tag){ + char *str; + int len; + + len = (int)strlen(tag); + str = (char*)malloc(len + 1); + to_camel(tag, str); + str[len] = 0; + + return(str); +} + +char style_index_function_start[] = +"inline u32*\n" +"style_index_by_tag(Style_Main_Data *s, u32 tag){\n" +" u32 *result = 0;\n" +" switch (tag){\n"; + +char style_index_function_end[] = +" }\n" +" return(result);\n" +"}\n\n"; + +char style_case[] = " case Stag_%s: result = &s->%s_color; break;\n"; +char style_info_case[] = " case Stag_%s: result = &s->file_info_style.%s_color; break;\n"; + +char* generate_style(){ + char *filename = "4coder_style.h & 4ed_style.h"; + char filename_4coder[] = "4coder_style.h"; + char filename_4ed[] = "4ed_style.h"; + FILE *file; + char *tag; + int count, i; + + file = fopen(filename_4coder, "wb"); + enum_begin(file, "Style_Tag"); + { + count = ArrayCount(bar_style_fields); + for (i = 0; i < count; ++i){ + tag = make_style_tag(bar_style_fields[i]); + fprintf(file, "Stag_%s,\n", tag); + free(tag); + } + + count = ArrayCount(main_style_fields); + for (i = 0; i < count; ++i){ + tag = make_style_tag(main_style_fields[i]); + fprintf(file, "Stag_%s,\n", tag); + free(tag); + } + } + struct_end(file); + fclose(file); + + file = fopen(filename_4ed, "wb"); + struct_begin(file, "Interactive_Style"); + { + count = ArrayCount(bar_style_fields); + for (i = 0; i < count; ++i){ + fprintf(file, "u32 %s_color;\n", bar_style_fields[i]); + } + } + struct_end(file); + + struct_begin(file, "Style_Main_Data"); + { + count = ArrayCount(main_style_fields); + for (i = 0; i < count; ++i){ + fprintf(file, "u32 %s_color;\n", main_style_fields[i]); + } + fprintf(file, "Interactive_Style file_info_style;\n"); + } + struct_end(file); + + { + fprintf(file, "%s", style_index_function_start); + count = ArrayCount(bar_style_fields); + for (i = 0; i < count; ++i){ + tag = make_style_tag(bar_style_fields[i]); + fprintf(file, style_info_case, tag, bar_style_fields[i]); + free(tag); + } + + count = ArrayCount(main_style_fields); + for (i = 0; i < count; ++i){ + tag = make_style_tag(main_style_fields[i]); + fprintf(file, style_case, tag, main_style_fields[i]); + free(tag); + } + fprintf(file, "%s", style_index_function_end); + } + + fclose(file); + + return(filename); +} + +int main(){ + char *filename; + + filename = generate_keycode_enum(); + printf("gen success: %s\n", filename); + + filename = generate_delayed_action(); + printf("gen success: %s\n", filename); + + filename = generate_style(); + printf("gen success: %s\n", filename); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_rendering.cpp b/test_data/lots_of_files/4ed_rendering.cpp new file mode 100644 index 0000000..d4cecd5 --- /dev/null +++ b/test_data/lots_of_files/4ed_rendering.cpp @@ -0,0 +1,508 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.17.2014 + * + * Rendering layer for project codename "4ed" + * + */ + +// TOP + +inline void +draw_set_clip(Render_Target *target, i32_Rect clip_box){ + glScissor(clip_box.x0, + target->height - clip_box.y1, + clip_box.x1 - clip_box.x0, + clip_box.y1 - clip_box.y0); +} + +inline void +draw_bind_texture(Render_Target *target, i32 texid){ + if (target->bound_texture != texid){ + glBindTexture(GL_TEXTURE_2D, texid); + target->bound_texture = texid; + } +} + +inline void +draw_set_color(Render_Target *target, u32 color){ + if (target->color != color){ + target->color = color; + Vec4 c = unpack_color4(color); + glColor4f(c.r, c.g, c.b, c.a); + } +} + +#define PutStruct(s,x) *(s*)(target->push_buffer + target->size) = x; target->size += sizeof(s) + +internal void +draw_push_piece(Render_Target *target, Render_Piece_Combined piece){ + PutStruct(Render_Piece_Header, piece.header); + + switch (piece.header.type){ + case piece_type_rectangle: + case piece_type_outline: + PutStruct(Render_Piece_Rectangle, piece.rectangle); + break; + + case piece_type_gradient: + PutStruct(Render_Piece_Gradient, piece.gradient); + break; + + case piece_type_glyph: + case piece_type_mono_glyph: + PutStruct(Render_Piece_Glyph, piece.glyph); + break; + + case piece_type_mono_glyph_advance: + PutStruct(Render_Piece_Glyph_Advance, piece.glyph_advance); + break; + } + + Assert(target->size <= target->max); +} + +internal void +draw_push_piece_clip(Render_Target *target, i32_Rect clip_box){ + // TODO(allen): optimize out if there are two clip box changes in a row + Render_Piece_Change_Clip clip; + Render_Piece_Header header; + + header.type = piece_type_change_clip; + clip.box = clip_box; + + PutStruct(Render_Piece_Header, header); + PutStruct(Render_Piece_Change_Clip, clip); +} + +internal void +draw_push_clip(Render_Target *target, i32_Rect clip_box){ + Assert(target->clip_top == -1 || + fits_inside(clip_box, target->clip_boxes[target->clip_top])); + Assert(target->clip_top+1 < ArrayCount(target->clip_boxes)); + target->clip_boxes[++target->clip_top] = clip_box; + + draw_push_piece_clip(target, clip_box); +} + +internal void +draw_pop_clip(Render_Target *target){ + i32_Rect clip_box; + Assert(target->clip_top > 0); + --target->clip_top; + clip_box = target->clip_boxes[target->clip_top]; + + draw_push_piece_clip(target, clip_box); +} + +#define ExtractStruct(s) ((s*)cursor); cursor += sizeof(s) + +inline void +private_draw_rectangle(Render_Target *target, f32_Rect rect, u32 color){ + draw_set_color(target, color); + draw_bind_texture(target, 0); + glBegin(GL_QUADS); + { + glVertex2f(rect.x0, rect.y0); + glVertex2f(rect.x0, rect.y1); + glVertex2f(rect.x1, rect.y1); + glVertex2f(rect.x1, rect.y0); + } + glEnd(); +} + +inline void +private_draw_rectangle_outline(Render_Target *target, f32_Rect rect, u32 color){ + f32_Rect r; + r.x0 = rect.x0 + .5f; + r.y0 = rect.y0 + .5f; + r.x1 = rect.x1 - .5f; + r.y1 = rect.y1 - .5f; + + draw_set_color(target, color); + draw_bind_texture(target, 0); + glBegin(GL_LINE_STRIP); + { + glVertex2f(r.x0, r.y0); + glVertex2f(r.x1, r.y0); + glVertex2f(r.x1, r.y1); + glVertex2f(r.x0, r.y1); + glVertex2f(r.x0, r.y0); + } + glEnd(); +} + +inline void +private_draw_gradient(Render_Target *target, f32_Rect rect, + Vec4 color_left, Vec4 color_right){ + Vec4 cl = color_left; + Vec4 cr = color_right; + + draw_bind_texture(target, 0); + glBegin(GL_QUADS); + { + glColor4f(cl.r, cl.g, cl.b, cl.a); + glVertex2f(rect.x0, rect.y0); + glVertex2f(rect.x0, rect.y1); + + glColor4f(cr.r, cr.g, cr.b, cr.a); + glVertex2f(rect.x1, rect.y1); + glVertex2f(rect.x1, rect.y0); + } + glEnd(); +} + +inline void +private_draw_glyph(Render_Target *target, Render_Font *font, + u8 character, f32 x, f32 y, u32 color){ + f32 x_shift, y_shift; + x_shift = 0; + y_shift = (f32)font->ascent; + + x += x_shift; + y += y_shift; + + stbtt_aligned_quad q; + stbtt_GetPackedQuad(font->chardata, font->tex_width, font->tex_height, + character, &x, &y, &q, 0); + + draw_set_color(target, color); + draw_bind_texture(target, font->tex); + glBegin(GL_QUADS); + { + glTexCoord2f(q.s0, q.t1); glVertex2f(q.x0, q.y1); + glTexCoord2f(q.s1, q.t1); glVertex2f(q.x1, q.y1); + glTexCoord2f(q.s1, q.t0); glVertex2f(q.x1, q.y0); + glTexCoord2f(q.s0, q.t0); glVertex2f(q.x0, q.y0); + } + glEnd(); +} + +inline void +private_draw_glyph_mono(Render_Target *target, Render_Font *font, u8 character, + f32 x, f32 y, f32 advance, u32 color){ + f32 x_shift, y_shift; + i32 left = font->chardata[character].x0; + i32 right = font->chardata[character].x1; + i32 width = (right - left); + x_shift = (f32)(advance - width) * .5f - font->chardata[character].xoff; + y_shift = (f32)font->ascent; + + x += x_shift; + y += y_shift; + + stbtt_aligned_quad q; + stbtt_GetPackedQuad(font->chardata, font->tex_width, font->tex_height, + character, &x, &y, &q, 0); + + draw_set_color(target, color); + draw_bind_texture(target, font->tex); + glBegin(GL_QUADS); + { + glTexCoord2f(q.s0, q.t1); glVertex2f(q.x0, q.y1); + glTexCoord2f(q.s1, q.t1); glVertex2f(q.x1, q.y1); + glTexCoord2f(q.s1, q.t0); glVertex2f(q.x1, q.y0); + glTexCoord2f(q.s0, q.t0); glVertex2f(q.x0, q.y0); + } + glEnd(); +} + +inline void +private_draw_glyph_mono(Render_Target *target, Render_Font *font, u8 character, + real32 x, real32 y, u32 color){ + private_draw_glyph_mono(target, font, character, x, y, (f32)font->advance, color); +} + +internal void +launch_rendering(Render_Target *target){ + byte *cursor = target->push_buffer; + byte *cursor_end = cursor + target->size; + + for (; cursor < cursor_end;){ + Render_Piece_Header *header = ExtractStruct(Render_Piece_Header); + + i32 type = header->type; + switch (type){ + case piece_type_rectangle: + { + Render_Piece_Rectangle *rectangle = + ExtractStruct(Render_Piece_Rectangle); + private_draw_rectangle(target, rectangle->rect, rectangle->color); + }break; + + case piece_type_outline: + { + Render_Piece_Rectangle *rectangle = + ExtractStruct(Render_Piece_Rectangle); + private_draw_rectangle_outline(target, rectangle->rect, rectangle->color); + }break; + + case piece_type_gradient: + { + Render_Piece_Gradient *gradient = + ExtractStruct(Render_Piece_Gradient); + private_draw_gradient(target, gradient->rect, + unpack_color4(gradient->left_color), + unpack_color4(gradient->right_color)); + }break; + + case piece_type_glyph: + { + Render_Piece_Glyph *glyph = + ExtractStruct(Render_Piece_Glyph); + + Render_Font *font = get_font_info(&target->font_set, glyph->font_id)->font; + if (font) + private_draw_glyph(target, font, glyph->character, + glyph->pos.x, glyph->pos.y, glyph->color); + }break; + + case piece_type_mono_glyph: + { + Render_Piece_Glyph *glyph = + ExtractStruct(Render_Piece_Glyph); + + Render_Font *font = get_font_info(&target->font_set, glyph->font_id)->font; + if (font) + private_draw_glyph_mono(target, font, glyph->character, + glyph->pos.x, glyph->pos.y, glyph->color); + }break; + + case piece_type_mono_glyph_advance: + { + Render_Piece_Glyph_Advance *glyph = + ExtractStruct(Render_Piece_Glyph_Advance); + + Render_Font *font = get_font_info(&target->font_set, glyph->font_id)->font; + if (font) + private_draw_glyph_mono(target, font, glyph->character, + glyph->pos.x, glyph->pos.y, + glyph->advance, glyph->color); + }break; + + case piece_type_change_clip: + { + Render_Piece_Change_Clip *clip = + ExtractStruct(Render_Piece_Change_Clip); + draw_set_clip(target, clip->box); + }break; + } + } +} + +#undef ExtractStruct + +internal i32 +draw_font_info_load(Partition *partition, + char *filename_untranslated, + i32 pt_size, i32 *height, i32 *advance){ + + char space_[1024]; + String filename = make_fixed_width_string(space_); + b32 translate_success = system_to_binary_path(&filename, filename_untranslated); + if (!translate_success) return 0; + + i32 result = 1; + Data file; + file = system_load_file(filename.str); + + Temp_Memory temp = begin_temp_memory(partition); + stbtt_packedchar *chardata = push_array(partition, stbtt_packedchar, 256); + + i32 oversample = 2; + + i32 tex_width, tex_height; + tex_width = pt_size*128*oversample; + tex_height = pt_size*2*oversample; + void *block = push_block(partition, tex_width * tex_height); + + if (!file.data){ + result = 0; + } + else{ + stbtt_fontinfo font; + if (!stbtt_InitFont(&font, (u8*)file.data, 0)){ + result = 0; + } + else{ + i32 ascent, descent, line_gap; + f32 scale; + + stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); + scale = stbtt_ScaleForPixelHeight(&font, (f32)pt_size); + + f32 scaled_ascent, scaled_descent, scaled_line_gap; + + scaled_ascent = scale*ascent; + scaled_descent = scale*descent; + scaled_line_gap = scale*line_gap; + + i32 font_height = (i32)(scaled_ascent - scaled_descent + scaled_line_gap); + + stbtt_pack_context spc; + if (stbtt_PackBegin(&spc, (u8*)block, tex_width, tex_height, tex_width, 1, partition)){ + stbtt_PackSetOversampling(&spc, oversample, oversample); + if (stbtt_PackFontRange(&spc, (u8*)file.data, 0, + STBTT_POINT_SIZE((f32)pt_size), 0, 128, chardata)){ + // do nothing + } + else{ + result = 0; + } + + stbtt_PackEnd(&spc); + } + else{ + result = 0; + } + + if (result){ + i32 max_advance = 0; + for (u8 code_point = 0; code_point < 128; ++code_point){ + if (stbtt_FindGlyphIndex(&font, code_point) != 0){ + i32 adv = CEIL32(chardata[code_point].xadvance); + if (max_advance < adv){ + max_advance = adv; + } + } + } + + *height = font_height; + *advance = max_advance - 1; + } + } + + system_free_memory(file.data); + } + + end_temp_memory(temp); + + return(result); +} + +internal i32 +draw_font_load(void *base_block, i32 size, + Render_Font *font_out, + char *filename_untranslated, + i32 pt_size, + i32 tab_width){ + + char space_[1024]; + String filename = make_fixed_width_string(space_); + b32 translate_success = system_to_binary_path(&filename, filename_untranslated); + if (!translate_success) return 0; + + i32 result = 1; + Data file; + file = system_load_file(filename.str); + + Partition partition_ = partition_open(base_block, size); + Partition *partition = &partition_; + + stbtt_packedchar *chardata = font_out->chardata; + + i32 oversample = 2; + + i32 tex_width, tex_height; + tex_width = pt_size*128*oversample; + tex_height = pt_size*2*oversample; + void *block = push_block(partition, tex_width * tex_height); + + if (!file.data){ + result = 0; + } + + else{ + stbtt_fontinfo font; + if (!stbtt_InitFont(&font, (u8*)file.data, 0)){ + result = 0; + } + else{ + i32 ascent, descent, line_gap; + f32 scale; + + stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); + scale = stbtt_ScaleForPixelHeight(&font, (f32)pt_size); + + f32 scaled_ascent, scaled_descent, scaled_line_gap; + + scaled_ascent = scale*ascent; + scaled_descent = scale*descent; + scaled_line_gap = scale*line_gap; + + font_out->height = (i32)(scaled_ascent - scaled_descent + scaled_line_gap); + font_out->ascent = (i32)(scaled_ascent); + font_out->descent = (i32)(scaled_descent); + font_out->line_skip = (i32)(scaled_line_gap); + + font_out->tex_width = tex_width; + font_out->tex_height = tex_height; + + stbtt_pack_context spc; + + if (stbtt_PackBegin(&spc, (u8*)block, tex_width, tex_height, tex_width, 1, partition)){ + stbtt_PackSetOversampling(&spc, oversample, oversample); + if (stbtt_PackFontRange(&spc, (u8*)file.data, 0, + STBTT_POINT_SIZE((f32)pt_size), 0, 128, chardata)){ + // do nothing + } + else{ + result = 0; + } + + stbtt_PackEnd(&spc); + } + else{ + result = 0; + } + + if (result){ + GLuint font_tex; + glGenTextures(1, &font_tex); + glBindTexture(GL_TEXTURE_2D, font_tex); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex_width, tex_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, block); + + font_out->tex = font_tex; + glBindTexture(GL_TEXTURE_2D, 0); + + font_out->chardata['\r'] = font_out->chardata[' ']; + font_out->chardata['\n'] = font_out->chardata[' ']; + font_out->chardata['\t'] = font_out->chardata[' ']; + font_out->chardata['\t'].xadvance *= tab_width; + + i32 max_advance = 0; + for (u8 code_point = 0; code_point < 128; ++code_point){ + if (stbtt_FindGlyphIndex(&font, code_point) != 0){ + font_out->glyphs[code_point].exists = 1; + i32 advance = CEIL32(font_out->chardata[code_point].xadvance); + if (max_advance < advance) max_advance = advance; + font_out->advance_data[code_point] = font_out->chardata[code_point].xadvance; + } + else if (code_point == '\r' || code_point == '\n' || code_point == '\t'){ + font_out->advance_data[code_point] = font_out->chardata[code_point].xadvance; + } + } + font_out->advance = max_advance - 1; + } + + } + system_free_memory(file.data); + } + + return result; +} + +internal +Release_Font_Sig(draw_release_font){ + glDeleteTextures(1, &font->tex); + font->tex = 0; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_rendering.h b/test_data/lots_of_files/4ed_rendering.h new file mode 100644 index 0000000..b0fbccc --- /dev/null +++ b/test_data/lots_of_files/4ed_rendering.h @@ -0,0 +1,206 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 17.12.2014 + * + * Rendering layer for project codename "4ed" + * + */ + +// TOP + +#ifndef FRED_RENDERING_H +#define FRED_RENDERING_H + +internal void* +part_alloc(int size, void *context){ + Partition *part = (Partition*)context; + void *result = push_block(part, size); + return(result); +} + +internal void +part_free(void *ptr, void *context){ +} + +#define STBTT_malloc part_alloc +#define STBTT_free part_free + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +struct Glyph_Data{ + b32 exists; +}; + +struct Render_Font{ + char name_[24]; + String name; + b32 loaded; + + Glyph_Data glyphs[256]; + stbtt_packedchar chardata[256]; + float advance_data[256]; + i32 height, ascent, descent, line_skip; + i32 advance; + u32 tex; + i32 tex_width, tex_height; +}; + +enum Render_Piece_Type{ + piece_type_rectangle, + piece_type_outline, + piece_type_gradient, + piece_type_glyph, + piece_type_mono_glyph, + piece_type_mono_glyph_advance, + piece_type_change_clip +}; + +struct Render_Piece_Header{ + i32 type; +}; + +struct Render_Piece_Rectangle{ + f32_Rect rect; + u32 color; +}; + +struct Render_Piece_Gradient{ + f32_Rect rect; + u32 left_color, right_color; +}; + +struct Render_Piece_Glyph{ + Vec2 pos; + u32 color; + i16 font_id; + u8 character; +}; + +struct Render_Piece_Glyph_Advance{ + Vec2 pos; + u32 color; + f32 advance; + i16 font_id; + u8 character; +}; + +struct Render_Piece_Change_Clip{ + i32_Rect box; +}; + +struct Render_Piece_Combined{ + Render_Piece_Header header; + union{ + Render_Piece_Rectangle rectangle; + Render_Piece_Gradient gradient; + Render_Piece_Glyph glyph; + Render_Piece_Glyph_Advance glyph_advance; + }; +}; + +struct Render_Target; + +#define Draw_Push_Clip_Sig(name) void name(Render_Target *target, i32_Rect clip_box) +typedef Draw_Push_Clip_Sig(Draw_Push_Clip); + +#define Draw_Pop_Clip_Sig(name) void name(Render_Target *target) +typedef Draw_Pop_Clip_Sig(Draw_Pop_Clip); + +#define Draw_Push_Piece_Sig(name) void name(Render_Target *target, Render_Piece_Combined piece) +typedef Draw_Push_Piece_Sig(Draw_Push_Piece); + +#define Font_Load_Sig(name) i32 name( \ + Render_Font *font_out, \ + char *filename, \ + i32 pt_size, \ + i32 tab_width) +typedef Font_Load_Sig(Font_Load); + +#define Font_Info_Load_Sig(name) i32 name( \ + Partition *partition, \ + char *filename, \ + i32 pt_size, \ + i32 *height, \ + i32 *advance) +typedef Font_Info_Load_Sig(Font_Info_Load); + +#define Release_Font_Sig(name) void name(Render_Font *font) +typedef Release_Font_Sig(Release_Font); + +struct Font_Table_Entry{ + u32 hash; + String name; + i16 font_id; +}; + +struct Font_Info{ + Render_Font *font; + String filename; + String name; + i32 height, advance; + i32 pt_size; +}; + +struct Font_Slot{ + Font_Slot *next, *prev; + i16 font_id; + u8 padding[14]; +}; + +struct Font_Set{ + Font_Info *info; + Font_Table_Entry *entries; + u32 count, max; + + void *font_block; + Font_Slot free_slots; + Font_Slot used_slots; + + Font_Info_Load *font_info_load; + Font_Load *font_load; + Release_Font *release_font; + + b8 *font_used_flags; + i16 used_this_frame; + i16 live_max; +}; + +struct Render_Target{ + void *handle; + void *context; + i32_Rect clip_boxes[5]; + i32 clip_top; + i32 width, height; + i32 bound_texture; + u32 color; + + // TODO(allen): change this to a Partition + byte *push_buffer; + i32 size, max; + + // TODO(allen): rename this to font_partition + Font_Set font_set; + Partition *partition; + + Draw_Push_Clip *push_clip; + Draw_Pop_Clip *pop_clip; + Draw_Push_Piece *push_piece; +}; + +inline i32_Rect +rect_from_target(Render_Target *target){ + return i32R(0, 0, target->width, target->height); +} + +inline Font_Info* +get_font_info(Font_Set *set, i16 font_id){ + Font_Info *result = set->info + font_id - 1; + return(result); +} + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_rendering_helper.cpp b/test_data/lots_of_files/4ed_rendering_helper.cpp new file mode 100644 index 0000000..c2c1d75 --- /dev/null +++ b/test_data/lots_of_files/4ed_rendering_helper.cpp @@ -0,0 +1,267 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.17.2014 + * + * Rendering layer for project codename "4ed" + * + */ + +// TOP + +inline void +draw_push_clip(Render_Target *target, i32_Rect clip_box){ + target->push_clip(target, clip_box); +} + +inline void +draw_pop_clip(Render_Target *target){ + target->pop_clip(target); +} + +internal void +begin_render_section(Render_Target *target, System_Functions *system){ + Font_Set *font_set = &target->font_set; + system->acquire_lock(RENDER_LOCK); + font_set->used_this_frame = 0; + memset(font_set->font_used_flags, 0, font_set->max); + target->size = 0; + target->clip_top = -1; + + i32_Rect clip; + clip.x0 = 0; + clip.y0 = 0; + clip.x1 = target->width; + clip.y1 = target->height; + draw_push_clip(target, clip); +} + +internal void +end_render_section(Render_Target *target, System_Functions *system){ + Assert(target->clip_top == 0); + system->release_lock(RENDER_LOCK); +} + +internal void +draw_rectangle(Render_Target *target, i32_Rect rect, u32 color){ + Render_Piece_Combined piece; + piece.header.type = piece_type_rectangle; + piece.rectangle.rect = f32R(rect); + piece.rectangle.color = color; + target->push_piece(target, piece); +} + +internal void +draw_rectangle(Render_Target *target, f32_Rect rect, u32 color){ + Render_Piece_Combined piece; + piece.header.type = piece_type_rectangle; + piece.rectangle.rect = rect; + piece.rectangle.color = color; + target->push_piece(target, piece); +} + +internal void +draw_gradient_2corner_clipped(Render_Target *target, f32_Rect rect, + Vec4 left_color, Vec4 right_color){ + Render_Piece_Combined piece; + piece.header.type = piece_type_gradient; + piece.gradient.rect = rect; + piece.gradient.left_color = pack_color4(left_color); + piece.gradient.right_color = pack_color4(right_color); + target->push_piece(target, piece); +} + +inline void +draw_gradient_2corner_clipped(Render_Target *target, f32 l, f32 t, f32 r, f32 b, + Vec4 color_left, Vec4 color_right){ + draw_gradient_2corner_clipped(target, f32R(l,t,r,b), color_left, color_right); +} + +internal void +draw_rectangle_outline(Render_Target *target, f32_Rect rect, u32 color){ + Render_Piece_Combined piece; + piece.header.type = piece_type_outline; + piece.rectangle.rect = rect; + piece.rectangle.color = color; + target->push_piece(target, piece); +} + +inline void +draw_rectangle_outline(Render_Target *target, i32_Rect rect, u32 color){ + draw_rectangle_outline(target, f32R(rect), color); +} + +internal void +draw_margin(Render_Target *target, i32_Rect outer, i32_Rect inner, u32 color){ + draw_rectangle(target, i32R(outer.x0, outer.y0, outer.x1, inner.y0), color); + draw_rectangle(target, i32R(outer.x0, inner.y1, outer.x1, outer.y1), color); + draw_rectangle(target, i32R(outer.x0, inner.y0, inner.x0, inner.y1), color); + draw_rectangle(target, i32R(inner.x1, inner.y0, outer.x1, inner.y1), color); +} + +inline void +draw_margin(Render_Target *target, i32_Rect outer, i32 width, u32 color){ + i32_Rect inner = get_inner_rect(outer, width); + draw_margin(target, outer, inner, color); +} + +inline internal i32 +font_predict_size(i32 pt_size){ + return pt_size*pt_size*128; +} + +internal void +font_set_tabwidth(Render_Font *font, i32 tab_width){ + font->chardata['\t'].xadvance *= font->chardata[' '].xadvance * tab_width; +} + +internal void +font_draw_glyph_mono(Render_Target *target, i16 font_id, + u8 character, f32 x, f32 y, f32 advance, u32 color){ + Render_Piece_Combined piece; + piece.header.type = piece_type_mono_glyph; + piece.glyph.pos.x = x; + piece.glyph.pos.y = y; + piece.glyph.color = color; + piece.glyph.font_id = font_id; + piece.glyph.character = character; + target->push_piece(target, piece); + font_set_use(target->partition, &target->font_set, font_id); +} + +inline void +font_draw_glyph_mono(Render_Target *target, i16 font_id, + u8 character, f32 x, f32 y, u32 color){ + f32 advance = (f32)get_font_info(&target->font_set, font_id)->advance; + font_draw_glyph_mono(target, font_id, character, x, y, advance, color); +} + +internal void +font_draw_glyph(Render_Target *target, i16 font_id, + u8 character, f32 x, f32 y, u32 color){ + Render_Piece_Combined piece; + piece.header.type = piece_type_glyph; + piece.glyph.pos.x = x; + piece.glyph.pos.y = y; + piece.glyph.color = color; + piece.glyph.font_id = font_id; + piece.glyph.character = character; + target->push_piece(target, piece); + font_set_use(target->partition, &target->font_set, font_id); +} + +inline f32 +font_get_glyph_width(Render_Target *target, i16 font_id, u16 character){ + Render_Font *font = get_font_info(&target->font_set, font_id)->font; + f32 result = 0.f; + if (font) result = font->chardata[character].xadvance; + return (result); +} + +internal f32 +font_string_width(Render_Target *target, i16 font_id, char *str){ + f32 x = 0; + for (i32 i = 0; str[i]; ++i){ + u8 c = str[i]; + // TODO(allen): Someday let's not punt on the the unicode rendering + c = c % 128; + x += font_get_glyph_width(target, font_id, c); + } + return x; +} + +internal f32 +font_string_width(Render_Target *target, i16 font_id, String str){ + f32 x = 0; + for (i32 i = 0; i < str.size; ++i){ + u8 c = str.str[i]; + // TODO(allen): Someday let's not punt on the the unicode rendering + c = c % 128; + x += font_get_glyph_width(target, font_id, c); + } + return x; +} + +internal i32 +draw_string(Render_Target *target, i16 font_id, + char *str, i32 x_, i32 y, u32 color){ + real32 x = (real32)x_; + for (i32 i = 0; str[i]; ++i){ + u8 c = str[i]; + // TODO(allen): Someday let's not punt on the the unicode rendering + c = c % 128; + font_draw_glyph(target, font_id, c, x, (f32)y, color); + x += font_get_glyph_width(target, font_id, c); + } + return CEIL32(x); +} + +internal f32 +draw_string_mono(Render_Target *target, i16 font_id, + char *str, f32 x, f32 y, f32 advance, u32 color){ + for (i32 i = 0; str[i]; ++i){ + u8 c = str[i]; + // TODO(allen): Someday let's not punt on the the unicode rendering + c = c % 128; + font_draw_glyph_mono(target, font_id, c, x, y, advance, color); + x += advance; + } + return x; +} + +internal i32 +draw_string(Render_Target *target, i16 font_id, + String str, i32 x_, i32 y, u32 color){ + f32 x = (f32)x_; + for (i32 i = 0; i < str.size; ++i){ + u8 c = str.str[i]; + // TODO(allen): Someday let's not punt on the the unicode rendering + c = c % 128; + font_draw_glyph(target, font_id, c, + x, (f32)y, color); + x += font_get_glyph_width(target, font_id, c); + } + return CEIL32(x); +} + +internal f32 +draw_string_mono(Render_Target *target, i16 font_id, + String str, f32 x, f32 y, f32 advance, u32 color){ + for (i32 i = 0; i < str.size; ++i){ + u8 c = str.str[i]; + // TODO(allen): Someday let's not punt on the the unicode rendering + c = c % 128; + font_draw_glyph_mono(target, font_id, c, x, y, advance, color); + x += advance; + } + return x; +} + +internal f32 +font_get_max_width(Font_Set *font_set, i16 font_id, char *characters){ + Render_Font *font = get_font_info(font_set, font_id)->font; + f32 cx, x = 0; + if (font){ + stbtt_packedchar *chardata = font->chardata; + for (i32 i = 0; characters[i]; ++i){ + cx = chardata[characters[i]].xadvance; + if (x < cx) x = cx; + } + } + return x; +} + +internal f32 +font_get_string_width(Render_Target *target, i16 font_id, String string){ + f32 result = 0; + for (i32 i = 0; i < string.size; ++i){ + u8 c = string.str[i]; + // TODO(allen): Someday let's not punt on the the unicode rendering + c = c % 128; + font_get_glyph_width(target, font_id, c); + } + return result; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_style.cpp b/test_data/lots_of_files/4ed_style.cpp new file mode 100644 index 0000000..903f8c1 --- /dev/null +++ b/test_data/lots_of_files/4ed_style.cpp @@ -0,0 +1,102 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 28.08.2015 + * + * Styles for 4coder + * + */ + +// TOP + +// TODO(allen): +// Font changing UI should be in the library menu now, it's not tied to the fonts any more +// Get the import export stuff up and running for styles again + +struct Style_Font{ + i16 font_id; + i16 font_changed; +}; + +struct Style{ + char name_[24]; + String name; + Style_Main_Data main; +}; + +internal void +style_copy(Style *dst, Style *src){ + *dst = *src; + dst->name.str = dst->name_; +} + +internal void +style_set_name(Style *style, String name){ + i32 count = ArrayCount(style->name_); + style->name = make_string(style->name_, 0, count - 1); + copy(&style->name, name); + terminate_with_null(&style->name); +} + +struct Style_Library{ + Style styles[64]; + i32 count, max; +}; + +#if 0 +struct Style_File_Format{ + i32 name_size; + char name[24]; + + i32 color_specifier_count; +}; + +internal b32 +style_library_add(Style_Library *library, Style *style){ + b32 result = 0; + i32 count = library->count; + String my_name = style->name; + Style *ostyle = library->styles; + Style *out = 0; + // TODO(allen): hashtable for name lookup? + for (i32 i = 0; i < count; ++i, ++ostyle){ + if (match(my_name, ostyle->name)){ + out = ostyle; + break; + } + } + if (!out && count < library->max){ + out = library->styles + library->count++; + } + if (out){ + style_copy(out, style); + result = 1; + } + return result; +} + +internal Style_File_Format* +style_format_for_file(Font_Set *set, Style *style, Style_File_Format *out){ + out->name_size = style->name.size; + memcpy(out->name, style->name.str, ArrayCount(out->name)); + + Style_Color_Specifier *spec = (Style_Color_Specifier*)(out + 1); + i32 count = 0; + + for (u32 i = 0; i < Stag_Count; ++i){ + u32 *color = style_index_by_tag(style, i); + if (color){ + spec->tag = i; + spec->color = *color; + ++count; + ++spec; + } + } + out->color_specifier_count = count; + + return (Style_File_Format*)spec; +} +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_style.h b/test_data/lots_of_files/4ed_style.h new file mode 100644 index 0000000..0ae1e91 --- /dev/null +++ b/test_data/lots_of_files/4ed_style.h @@ -0,0 +1,75 @@ +struct Interactive_Style{ +u32 bar_color; +u32 bar_active_color; +u32 base_color; +u32 pop1_color; +u32 pop2_color; +}; + +struct Style_Main_Data{ +u32 back_color; +u32 margin_color; +u32 margin_hover_color; +u32 margin_active_color; +u32 cursor_color; +u32 at_cursor_color; +u32 highlight_color; +u32 at_highlight_color; +u32 mark_color; +u32 default_color; +u32 comment_color; +u32 keyword_color; +u32 str_constant_color; +u32 char_constant_color; +u32 int_constant_color; +u32 float_constant_color; +u32 bool_constant_color; +u32 preproc_color; +u32 include_color; +u32 special_character_color; +u32 highlight_junk_color; +u32 highlight_white_color; +u32 paste_color; +u32 undo_color; +u32 next_undo_color; +Interactive_Style file_info_style; +}; + +inline u32* +style_index_by_tag(Style_Main_Data *s, u32 tag){ + u32 *result = 0; + switch (tag){ + case Stag_Bar: result = &s->file_info_style.bar_color; break; + case Stag_Bar_Active: result = &s->file_info_style.bar_active_color; break; + case Stag_Base: result = &s->file_info_style.base_color; break; + case Stag_Pop1: result = &s->file_info_style.pop1_color; break; + case Stag_Pop2: result = &s->file_info_style.pop2_color; break; + case Stag_Back: result = &s->back_color; break; + case Stag_Margin: result = &s->margin_color; break; + case Stag_Margin_Hover: result = &s->margin_hover_color; break; + case Stag_Margin_Active: result = &s->margin_active_color; break; + case Stag_Cursor: result = &s->cursor_color; break; + case Stag_At_Cursor: result = &s->at_cursor_color; break; + case Stag_Highlight: result = &s->highlight_color; break; + case Stag_At_Highlight: result = &s->at_highlight_color; break; + case Stag_Mark: result = &s->mark_color; break; + case Stag_Default: result = &s->default_color; break; + case Stag_Comment: result = &s->comment_color; break; + case Stag_Keyword: result = &s->keyword_color; break; + case Stag_Str_Constant: result = &s->str_constant_color; break; + case Stag_Char_Constant: result = &s->char_constant_color; break; + case Stag_Int_Constant: result = &s->int_constant_color; break; + case Stag_Float_Constant: result = &s->float_constant_color; break; + case Stag_Bool_Constant: result = &s->bool_constant_color; break; + case Stag_Preproc: result = &s->preproc_color; break; + case Stag_Include: result = &s->include_color; break; + case Stag_Special_Character: result = &s->special_character_color; break; + case Stag_Highlight_Junk: result = &s->highlight_junk_color; break; + case Stag_Highlight_White: result = &s->highlight_white_color; break; + case Stag_Paste: result = &s->paste_color; break; + case Stag_Undo: result = &s->undo_color; break; + case Stag_Next_Undo: result = &s->next_undo_color; break; + } + return(result); +} + diff --git a/test_data/lots_of_files/4ed_system.h b/test_data/lots_of_files/4ed_system.h new file mode 100644 index 0000000..76e1c76 --- /dev/null +++ b/test_data/lots_of_files/4ed_system.h @@ -0,0 +1,253 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 21.01.2014 + * + * System functions for project codename "4ed" + * + */ + +// TOP + +struct Plat_Handle{ + u32 d[4]; +}; + +struct Unique_Hash{ + u32 d[4]; +}; + +#define Sys_File_Time_Stamp_Sig(name) u64 name(char *filename) +typedef Sys_File_Time_Stamp_Sig(System_File_Time_Stamp); + +// TODO(allen): make directory a char* to signal that it must be null terminated +#define Sys_Set_File_List_Sig(name) void name(File_List *file_list, String directory) +typedef Sys_Set_File_List_Sig(System_Set_File_List); + +#define Sys_File_Unique_Hash_Sig(name) Unique_Hash name(char *filename) +typedef Sys_File_Unique_Hash_Sig(System_File_Unique_Hash); + +#define Sys_Post_Clipboard_Sig(name) void name(String str) +typedef Sys_Post_Clipboard_Sig(System_Post_Clipboard); + +#define Sys_Time_Sig(name) u64 name() +typedef Sys_Time_Sig(System_Time); + +// cli +struct CLI_Handles{ + Plat_Handle proc; + Plat_Handle out_read; + Plat_Handle out_write; + u32 scratch_space[4]; + i32 exit; +}; + +#define Sys_CLI_Call_Sig(name) b32 name(char *path, char *script_name, CLI_Handles *cli_out) +typedef Sys_CLI_Call_Sig(System_CLI_Call); + +#define Sys_CLI_Begin_Update_Sig(name) void name(CLI_Handles *cli) +typedef Sys_CLI_Begin_Update_Sig(System_CLI_Begin_Update); + +#define Sys_CLI_Update_Step_Sig(name) b32 name(CLI_Handles *cli, char *dest, u32 max, u32 *amount) +typedef Sys_CLI_Update_Step_Sig(System_CLI_Update_Step); + +#define Sys_CLI_End_Update_Sig(name) b32 name(CLI_Handles *cli) +typedef Sys_CLI_End_Update_Sig(System_CLI_End_Update); + +// coroutine + +#define Coroutine_Function_Sig(name) void name(struct Coroutine *coroutine) +typedef Coroutine_Function_Sig(Coroutine_Function); + +struct Coroutine{ + Plat_Handle plat_handle; + Coroutine_Function *func; + void *yield_handle; + void *in; + void *out; +}; + +#define Sys_Create_Coroutine_Sig(name) Coroutine *name(Coroutine_Function *func) +typedef Sys_Create_Coroutine_Sig(System_Create_Coroutine); + +#define Sys_Launch_Coroutine_Sig(name) Coroutine *name(Coroutine *coroutine, void *in, void *out) +typedef Sys_Launch_Coroutine_Sig(System_Launch_Coroutine); + +#define Sys_Resume_Coroutine_Sig(name) Coroutine *name(Coroutine *coroutine, void *in, void *out) +typedef Sys_Resume_Coroutine_Sig(System_Resume_Coroutine); + +#define Sys_Yield_Coroutine_Sig(name) void name(Coroutine *coroutine) +typedef Sys_Yield_Coroutine_Sig(System_Yield_Coroutine); + +// thread +struct Thread_Context; + +enum Lock_ID{ + FRAME_LOCK, + INPUT_LOCK, + FONT_LOCK, + RENDER_LOCK, + CANCEL_LOCK0, + CANCEL_LOCK1, + CANCEL_LOCK2, + CANCEL_LOCK3, + CANCEL_LOCK4, + CANCEL_LOCK5, + CANCEL_LOCK6, + CANCEL_LOCK7, + LOCK_COUNT +}; + +enum Thread_Group_ID{ + BACKGROUND_THREADS, + THREAD_GROUP_COUNT +}; + +struct Thread_Memory{ + void *data; + i32 size; + i32 id; +}; + +struct Thread_Exchange; +struct System_Functions; + +#define Job_Callback_Sig(name) void name( \ + System_Functions *system, Thread_Context *thread, Thread_Memory *memory, \ + Thread_Exchange *exchange, void *data[2]) +typedef Job_Callback_Sig(Job_Callback); + +struct Job_Data{ + Job_Callback *callback; + void *data[2]; + i32 memory_request; +}; + +struct Full_Job_Data{ + Job_Data job; + + u32 job_memory_index; + u32 running_thread; + b32 finished; + u32 id; +}; + +struct Work_Queue{ + Full_Job_Data jobs[256]; + Plat_Handle semaphore; + volatile u32 write_position; + volatile u32 read_position; +}; + +#define THREAD_NOT_ASSIGNED 0xFFFFFFFF + +#define JOB_ID_WRAP (ArrayCount(queue->jobs) * 4) +#define QUEUE_WRAP (ArrayCount(queue->jobs)) + +struct Thread_Exchange{ + Work_Queue queues[THREAD_GROUP_COUNT]; + volatile u32 force_redraw; +}; + +#define Sys_Post_Job_Sig(name) u32 name(Thread_Group_ID group_id, Job_Data job) +typedef Sys_Post_Job_Sig(System_Post_Job); + +#define Sys_Cancel_Job_Sig(name) void name(Thread_Group_ID group_id, u32 job_id) +typedef Sys_Cancel_Job_Sig(System_Cancel_Job); + +#define Sys_Grow_Thread_Memory_Sig(name) void name(Thread_Memory *memory) +typedef Sys_Grow_Thread_Memory_Sig(System_Grow_Thread_Memory); + +#define Sys_Acquire_Lock_Sig(name) void name(i32 id) +typedef Sys_Acquire_Lock_Sig(System_Acquire_Lock); + +#define Sys_Release_Lock_Sig(name) void name(i32 id) +typedef Sys_Release_Lock_Sig(System_Release_Lock); + +// debug +#define INTERNAL_Sys_Sentinel_Sig(name) Bubble* name() +typedef INTERNAL_Sys_Sentinel_Sig(INTERNAL_System_Sentinel); + +#define INTERNAL_Sys_Get_Thread_States_Sig(name) void name(Thread_Group_ID id, b8 *running, i32 *pending) +typedef INTERNAL_Sys_Get_Thread_States_Sig(INTERNAL_System_Get_Thread_States); + +#define INTERNAL_Sys_Debug_Message_Sig(name) void name(char *message) +typedef INTERNAL_Sys_Debug_Message_Sig(INTERNAL_System_Debug_Message); + +struct System_Functions{ + // files: 3 + System_File_Time_Stamp *file_time_stamp; + System_Set_File_List *set_file_list; + System_File_Unique_Hash *file_unique_hash; + + // file system navigation (4coder_custom.h): 2 + File_Exists_Function *file_exists; + Directory_CD_Function *directory_cd; + + // clipboard: 1 + System_Post_Clipboard *post_clipboard; + + // time: 1 + System_Time *time; + + // coroutine: 4 + System_Create_Coroutine *create_coroutine; + System_Launch_Coroutine *launch_coroutine; + System_Resume_Coroutine *resume_coroutine; + System_Yield_Coroutine *yield_coroutine; + + // cli: 4 + System_CLI_Call *cli_call; + System_CLI_Begin_Update *cli_begin_update; + System_CLI_Update_Step *cli_update_step; + System_CLI_End_Update *cli_end_update; + + // threads: 5 + System_Post_Job *post_job; + System_Cancel_Job *cancel_job; + System_Grow_Thread_Memory *grow_thread_memory; + System_Acquire_Lock *acquire_lock; + System_Release_Lock *release_lock; + + // debug: 3 + INTERNAL_System_Sentinel *internal_sentinel; + INTERNAL_System_Get_Thread_States *internal_get_thread_states; + INTERNAL_System_Debug_Message *internal_debug_message; + + // non-function details + char slash; +}; + +#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/test_data/lots_of_files/4ed_template.cpp b/test_data/lots_of_files/4ed_template.cpp new file mode 100644 index 0000000..22be5e6 --- /dev/null +++ b/test_data/lots_of_files/4ed_template.cpp @@ -0,0 +1,54 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 01.03.2016 + * + * Templated code. + * + */ + +// TOP + +// NOTE(allen): This is an experiment, BUT remember a lot of people shit on templates. +// So if you start getting a wiff of stupidity from this back out immediately! +// +// experience 1: no badness, haven't seen any anoying template errors +// ... + +template<typename T> +inline void +dll_init_sentinel(T *sentinel){ + sentinel->next = sentinel; + sentinel->prev = sentinel; +} + +template<typename T> +inline void +dll_insert(T *pos, T *v){ + v->next = pos->next; + v->prev = pos; + pos->next = v; + v->next->prev = v; +} + +template<typename T> +inline void +dll_insert_back(T *pos, T *v){ + v->prev = pos->prev; + v->next = pos; + pos->prev = v; + v->prev->next = v; +} + +template<typename T> +inline void +dll_remove(T *v){ + v->next->prev = v->prev; + v->prev->next = v->next; +} + +// for(dll_items(iterator, sentinel_ptr)){...} +#define dll_items(it, st) ((it) = (st)->next); ((it) != (st)); ((it) = (it)->next) + +// BOTTOM + diff --git a/test_data/lots_of_files/4ed_ttf.cpp b/test_data/lots_of_files/4ed_ttf.cpp new file mode 100644 index 0000000..d80583a --- /dev/null +++ b/test_data/lots_of_files/4ed_ttf.cpp @@ -0,0 +1,360 @@ +/* + * YOUR INFO HERE! + */ + +// TOP + +typedef unsigned int Fixed16_16; + +struct Offset_Table{ + Fixed16_16 version; + unsigned short num_tables; + unsigned short search_range; + unsigned short entry_selector; + unsigned short range_shift; +}; + +struct Table_Directory_Entry{ + unsigned long tag; + unsigned long check_sum; + unsigned long offset; + unsigned long length; +}; + +#include <stdio.h> +#include <stdlib.h> + +struct Data{ + char *data; + int size; +}; + +Data +open_file(const char *filename){ + Data result = {}; + FILE *file; + file = fopen(filename, "rb"); + if (file){ + fseek(file, 0, SEEK_END); + result.size = ftell(file); + fseek(file, 0, SEEK_SET); + if (result.size > 0){ + result.data = (char*)malloc(result.size); + fread(result.data, result.size, 1, file); + } + fclose(file); + } + return(result); +} + +void +print(Offset_Table *offset){ + printf("version %d\n", offset->version >> 16); + printf("number of tables %d\n", (int)(offset->num_tables)); + printf("search range %d\n", (int)(offset->search_range)); + printf("entry selector %d\n", (int)(offset->entry_selector)); + printf("range shift %d\n", (int)(offset->range_shift)); +} + +void +print(Table_Directory_Entry *entry){ + printf("tag %.4s\n", &entry->tag); + printf("check sum %08x\n", entry->check_sum); + printf("offset %d\n", entry->offset); + printf("length %d\n", entry->length); +} + +void +byte_reverse(void *ptr, int len){ + char *c; + int i,j; + char t; + c = (char*)ptr; + for (i = 0, j = len-1; i < j; ++i, --j){ + t = c[i]; + c[i] = c[j]; + c[j] = t; + } +} + +void +byte_fix(Offset_Table *offset){ + byte_reverse(&offset->version, 4); + byte_reverse(&offset->num_tables, 2); + byte_reverse(&offset->search_range, 2); + byte_reverse(&offset->entry_selector, 2); + byte_reverse(&offset->range_shift, 2); +} + +void +byte_fix(Table_Directory_Entry *entry){ + byte_reverse(&entry->check_sum, 4); + byte_reverse(&entry->offset, 4); + byte_reverse(&entry->length, 4); +} + +struct cmap_Header{ + unsigned short version; + unsigned short num_subtables; +}; + +struct cmap_Subtable_Entry{ + unsigned short plat_id; + unsigned short plat_encoding_id; + unsigned long offset_from_cmap; +}; + +void +byte_fix(cmap_Header *header){ + byte_reverse(&header->version, 2); + byte_reverse(&header->num_subtables, 2); +} + +void +print(cmap_Header *header){ + printf("cmap tables:\n"); + printf("\tversion %d\n", (int)(header->version)); + printf("\tsubtables %d\n", (int)(header->num_subtables)); +} + +void +byte_fix(cmap_Subtable_Entry *entry){ + byte_reverse(&entry->plat_id, 2); + byte_reverse(&entry->plat_encoding_id, 2); + byte_reverse(&entry->offset_from_cmap, 4); +} + +struct cmap_Subtable_Format4_Header{ + unsigned short format; + unsigned short length; + unsigned short version; + unsigned short segment_count_2; + unsigned short search_range; + unsigned short entry_selector; + unsigned short range_shift; +}; + +void +print(cmap_Subtable_Entry *entry){ + printf("\tplatform id %d\n", (int)(entry->plat_id)); + printf("\tencoding id %d\n", (int)(entry->plat_encoding_id)); + printf("\toffset from cmap %d\n", (int)(entry->offset_from_cmap)); +} + +void +byte_fix(cmap_Subtable_Format4_Header *header){ + byte_reverse(&header->length, 2); + byte_reverse(&header->version, 2); + byte_reverse(&header->segment_count_2, 2); + byte_reverse(&header->search_range, 2); + byte_reverse(&header->entry_selector, 2); + byte_reverse(&header->range_shift, 2); +} + +void +print(cmap_Subtable_Format4_Header *header){ + printf("\t\tlength %d\n", header->length); + printf("\t\tversion %d\n", header->version); + printf("\t\tsegment count doubled %d\n", header->segment_count_2); + printf("\t\tsearch range %d\n", header->search_range); + printf("\t\tentry selector %d\n", header->entry_selector); + printf("\t\trange shift %d\n", header->range_shift); +} + +struct cmap_Subtable_Format4_Segments{ + unsigned short *end_code, *start_code; + unsigned short *id_delta, *id_range_offset; +}; + +void +byte_fix(cmap_Subtable_Format4_Segments segs, int segment_count){ + for (int i = 0; i < segment_count; ++i){ + byte_reverse(segs.end_code + i, 2); + } + + for (int i = 0; i < segment_count; ++i){ + byte_reverse(segs.start_code + i, 2); + } + + for (int i = 0; i < segment_count; ++i){ + byte_reverse(segs.id_delta + i, 2); + } + + for (int i = 0; i < segment_count; ++i){ + byte_reverse(segs.id_range_offset + i, 2); + } +} + +void +print(cmap_Subtable_Format4_Segments segs, int i){ + printf("\t\tsegment %d\n", i); + printf("\t\tend code %d\n", (int)(segs.end_code[i])); + printf("\t\tstart code %d\n", (int)(segs.start_code[i])); + printf("\t\tid delta %d\n", (int)(segs.id_delta[i])); + printf("\t\tid range offset %d\n", (int)(segs.id_range_offset[i])); +} + +void +parse_cmap_subtable4(char *start){ + char *cursor = start; + cmap_Subtable_Format4_Header *header = + (cmap_Subtable_Format4_Header*)cursor; + cursor = (char*)(header + 1); + + byte_fix(header); + print(header); + + int segment_count = (header->segment_count_2 >> 1); + + cmap_Subtable_Format4_Segments segs; + + segs.end_code = (unsigned short*)cursor; + cursor = (char*)(segs.end_code + segment_count); + cursor = cursor + sizeof(unsigned short); + + segs.start_code = (unsigned short*)cursor; + cursor = (char*)(segs.start_code + segment_count); + + segs.id_delta = (unsigned short*)cursor; + cursor = (char*)(segs.id_delta + segment_count); + + segs.id_range_offset = (unsigned short*)cursor; + cursor = (char*)(segs.id_range_offset + segment_count); + + byte_fix(segs, segment_count); + for (int i = 0; i < segment_count; ++i){ + printf("\n"); + print(segs, i); + } +} + +void +parse_cmap_subtable(char *start){ + char *cursor = start; + short *format = (short*)cursor; + byte_reverse(format, 2); + printf("\t\tformat %d\n", (int)(*format)); + + switch (*format){ + case 4: + parse_cmap_subtable4(start); + break; + } +} + +void +parse_cmap(char *start){ + char *cursor = start; + cmap_Header *header = (cmap_Header*)cursor; + cursor = (char*)(header + 1); + + byte_fix(header); + print(header); + + cmap_Subtable_Entry *entry = (cmap_Subtable_Entry*)cursor; + for (int i = 0; i < header->num_subtables; ++i, ++entry){ + byte_fix(entry); + printf("\n"); + print(entry); + + if (entry->plat_id == 3 && entry->plat_encoding_id == 1){ + printf("\n\tMicrosoft Unicode table:\n"); + parse_cmap_subtable(start + entry->offset_from_cmap); + } + } +} + +struct glyf_Glyph_Header{ + short num_contours; + short x_min; + short x_max; + short y_min; + short y_max; +}; + +void +byte_fix(glyf_Glyph_Header *header){ + byte_reverse(&header->num_contours, 2); + byte_reverse(&header->x_min, 2); + byte_reverse(&header->x_max, 2); + byte_reverse(&header->y_min, 2); + byte_reverse(&header->y_max, 2); +} + +void +print(glyf_Glyph_Header *header){ + printf("\tcontours %d\n", (int)(header->num_contours)); + printf("\tx min %d\n", (int)(header->x_min)); + printf("\tx max %d\n", (int)(header->x_max)); + printf("\ty min %d\n", (int)(header->y_min)); + printf("\ty max %d\n", (int)(header->y_max)); +} + +void +parse_glyf(char *start){ + char *cursor = start; + glyf_Glyph_Header *header = (glyf_Glyph_Header*)cursor; + cursor = (char*)(header + 1); + + byte_fix(header); + print(header); +} + +#define TTF_Tag(a,b,c,d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24)) +#define TTF_Tag_cmap TTF_Tag('c', 'm', 'a', 'p') + +#define TTF_Tag_glyf TTF_Tag('g', 'l', 'y', 'f') +#define TTF_Tag_fpgm TTF_Tag('f', 'p', 'g', 'm') +#define TTF_Tag_prep TTF_Tag('p', 'r', 'e', 'p') +#define TTF_Tag_cvt TTF_Tag('c', 'v', 't', ' ') +#define TTF_Tag_maxp TTF_Tag('m', 'a', 'x', 'p') + + +int +main(){ + Data file; + char *filename; + + filename = "test.ttf"; + file = open_file(filename); + if (file.data == 0){ + printf("could not open %s\n", filename); + return (1); + } + + char *cursor; + Offset_Table *offset; + + cursor = file.data; + offset = (Offset_Table*)cursor; + cursor = (char*)(offset + 1); + + + Table_Directory_Entry *directory_entries = (Table_Directory_Entry*)cursor; + + byte_fix(offset); + print(offset); + + int table_number = offset->num_tables; + + Table_Directory_Entry *entry = directory_entries; + for (int i = 0; i < table_number; ++i, ++entry){ + printf("\n"); + byte_fix(entry); + print(entry); + + switch (entry->tag){ + case TTF_Tag_cmap: + parse_cmap(file.data + entry->offset); + break; + + case TTF_Tag_glyf: + parse_glyf(file.data + entry->offset); + break; + } + } + + return (0); +} + +// BOTTOM diff --git a/test_data/lots_of_files/4ed_win32_keyboard.cpp b/test_data/lots_of_files/4ed_win32_keyboard.cpp new file mode 100644 index 0000000..dbe837c --- /dev/null +++ b/test_data/lots_of_files/4ed_win32_keyboard.cpp @@ -0,0 +1,31 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.17.2014 + * + * Win32-US Keyboard layer for 4coder + * + */ + +// TOP + +#include "4ed_keyboard.cpp" + +internal void +keycode_init(){ + keycode_lookup_table[VK_BACK] = key_back; + keycode_lookup_table[VK_DELETE] = key_del; + keycode_lookup_table[VK_UP] = key_up; + keycode_lookup_table[VK_DOWN] = key_down; + keycode_lookup_table[VK_LEFT] = key_left; + keycode_lookup_table[VK_RIGHT] = key_right; + keycode_lookup_table[VK_INSERT] = key_insert; + keycode_lookup_table[VK_HOME] = key_home; + keycode_lookup_table[VK_END] = key_end; + keycode_lookup_table[VK_PRIOR] = key_page_up; + keycode_lookup_table[VK_NEXT] = key_page_down; + keycode_lookup_table[VK_ESCAPE] = key_esc; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4tech_keycodes.h b/test_data/lots_of_files/4tech_keycodes.h new file mode 100644 index 0000000..4a94748 --- /dev/null +++ b/test_data/lots_of_files/4tech_keycodes.h @@ -0,0 +1,69 @@ +enum Key_Code_Names{ +key_back = 1, +key_up = 2, +key_down = 3, +key_left = 4, +key_right = 5, +key_del = 6, +key_insert = 7, +key_home = 8, +key_end = 11, +key_page_up = 12, +key_page_down = 13, +key_esc = 14, +key_mouse_left = 15, +key_mouse_right = 16, +key_f1 = 127, +key_f2 = 128, +key_f3 = 129, +key_f4 = 130, +key_f5 = 131, +key_f6 = 132, +key_f7 = 133, +key_f8 = 134, +key_f9 = 135, +key_f10 = 136, +key_f11 = 137, +key_f12 = 138, +key_f13 = 139, +key_f14 = 140, +key_f15 = 141, +key_f16 = 142, +}; +static char* +global_key_name(int key_code, int *size){ +char *result = 0; +switch(key_code){ +case key_back: result = "back"; *size = sizeof("back")-1; break; +case key_up: result = "up"; *size = sizeof("up")-1; break; +case key_down: result = "down"; *size = sizeof("down")-1; break; +case key_left: result = "left"; *size = sizeof("left")-1; break; +case key_right: result = "right"; *size = sizeof("right")-1; break; +case key_del: result = "del"; *size = sizeof("del")-1; break; +case key_insert: result = "insert"; *size = sizeof("insert")-1; break; +case key_home: result = "home"; *size = sizeof("home")-1; break; +case key_end: result = "end"; *size = sizeof("end")-1; break; +case key_page_up: result = "page_up"; *size = sizeof("page_up")-1; break; +case key_page_down: result = "page_down"; *size = sizeof("page_down")-1; break; +case key_esc: result = "esc"; *size = sizeof("esc")-1; break; +case key_mouse_left: result = "mouse_left"; *size = sizeof("mouse_left")-1; break; +case key_mouse_right: result = "mouse_right"; *size = sizeof("mouse_right")-1; break; +case key_f1: result = "f1"; *size = sizeof("f1")-1; break; +case key_f2: result = "f2"; *size = sizeof("f2")-1; break; +case key_f3: result = "f3"; *size = sizeof("f3")-1; break; +case key_f4: result = "f4"; *size = sizeof("f4")-1; break; +case key_f5: result = "f5"; *size = sizeof("f5")-1; break; +case key_f6: result = "f6"; *size = sizeof("f6")-1; break; +case key_f7: result = "f7"; *size = sizeof("f7")-1; break; +case key_f8: result = "f8"; *size = sizeof("f8")-1; break; +case key_f9: result = "f9"; *size = sizeof("f9")-1; break; +case key_f10: result = "f10"; *size = sizeof("f10")-1; break; +case key_f11: result = "f11"; *size = sizeof("f11")-1; break; +case key_f12: result = "f12"; *size = sizeof("f12")-1; break; +case key_f13: result = "f13"; *size = sizeof("f13")-1; break; +case key_f14: result = "f14"; *size = sizeof("f14")-1; break; +case key_f15: result = "f15"; *size = sizeof("f15")-1; break; +case key_f16: result = "f16"; *size = sizeof("f16")-1; break; +} +return(result); +} diff --git a/test_data/lots_of_files/4tech_math.cpp b/test_data/lots_of_files/4tech_math.cpp new file mode 100644 index 0000000..ed307e0 --- /dev/null +++ b/test_data/lots_of_files/4tech_math.cpp @@ -0,0 +1,695 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 15.05.2015 + * + * Math functions for 4coder + * + */ + +// TOP + +/* + * Scalar operators + */ + +#define C_MATH 1 + +#define DEG_TO_RAD 0.0174533f +#define RAD_TO_DEG 57.295780f + +#if C_MATH +#include <math.h> +#endif + +inline real32 +ABS(real32 x){ +#if C_MATH + return abs(x); +#endif +} + +inline real32 +MOD(real32 x, i32 m){ +#if C_MATH + real32 whole, frac; + frac = modf(x, &whole); + return ((i32)(whole) % m) + frac; +#endif +} + +inline real32 +SQRT(real32 x){ +#if C_MATH + return sqrt(x); +#endif +} + +inline real32 +SIN(real32 x_degrees){ +#if C_MATH + return sinf(x_degrees * DEG_TO_RAD); +#endif +} + +inline real32 +COS(real32 x_degrees){ +#if C_MATH + return cosf(x_degrees * DEG_TO_RAD); +#endif +} + +inline f32 +ATAN_DEG(f32 y, f32 x){ +#if C_MATH + return atan2f(y, x) * RAD_TO_DEG; +#endif +} + +inline f32 +POW(f32 base, f32 exp){ +#if C_MATH + return pow(base, exp); +#endif +} + +/* + * Rounding + */ + +inline i32 +TRUNC32(real32 x) { return (i32)x; } + +inline i32 +FLOOR32(real32 x) { return (i32)(x)-((x!=(i32)(x) && x<0)?1:0); } + +inline i32 +CEIL32(real32 x) { return (i32)(x)+((x!=(i32)(x) && x>0)?1:0); } + +inline i32 +ROUND32(real32 x) { return FLOOR32(x + .5f); } + +inline i32 +DIVCEIL32(i32 n, i32 d) { + i32 q = (n/d); + return q + (q*d < n); +} + +inline real32 +FRACPART32(real32 x) { return x - (i32)x; } + +/* + * Vectors + */ + +struct Vec2{ + union{ + struct{ + real32 x, y; + }; + struct{ + real32 v[2]; + }; + }; +}; + +struct Vec3{ + union{ + struct{ + real32 x, y, z; + }; + struct{ + real32 r, g, b; + }; + struct{ + Vec2 xy; + real32 _z; + }; + struct{ + real32 _x; + Vec2 yz; + }; + struct{ + real32 v[3]; + }; + }; +}; + +struct Vec4{ + union{ + struct{ + real32 r, g, b, a; + }; + + struct{ + real32 h, s, l, __a; + }; + struct{ + real32 x, y, z, w; + }; + struct{ + Vec3 rgb; + real32 _a; + }; + struct{ + Vec3 xyz; + real32 _w; + }; + struct{ + real32 _x; + Vec3 yzw; + }; + struct{ + real32 v[4]; + }; + }; +}; + +inline Vec2 +V2(real32 x, real32 y){ + Vec2 result; + result.x = x; + result.y = y; + return result; +} + +inline Vec3 +V3(real32 x, real32 y, real32 z){ + Vec3 result; + result.x = x; + result.y = y; + result.z = z; + return result; +} + +inline Vec4 +V4(real32 x, real32 y, real32 z, real32 w){ + Vec4 result; + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; +} + +inline Vec2 +operator+(Vec2 a, Vec2 b){ + Vec2 result; + result.x = a.x + b.x; + result.y = a.y + b.y; + return result; +} + +inline Vec3 +operator+(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; + return result; +} + +inline Vec4 +operator+(Vec4 a, Vec4 b){ + Vec4 result; + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; + result.w = a.w + b.w; + return result; +} + +inline Vec2 +operator-(Vec2 a, Vec2 b){ + Vec2 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + return result; +} + +inline Vec3 +operator-(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + return result; +} + +inline Vec4 +operator-(Vec4 a, Vec4 b){ + Vec4 result; + result.x = a.x - b.x; + result.y = a.y - b.y; + result.z = a.z - b.z; + result.w = a.w - b.w; + return result; +} + +inline Vec2 +operator*(Vec2 a, real32 k){ + Vec2 result; + result.x = a.x * k; + result.y = a.y * k; + return result; +} + +inline Vec3 +operator*(Vec3 a, real32 k){ + Vec3 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + return result; +} + +inline Vec4 +operator*(Vec4 a, real32 k){ + Vec4 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + result.w = a.w * k; + return result; +} + +inline Vec2 +operator*(real32 k, Vec2 a){ + Vec2 result; + result.x = a.x * k; + result.y = a.y * k; + return result; +} + +inline Vec3 +operator*(real32 k, Vec3 a){ + Vec3 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + return result; +} + +inline Vec4 +operator*(real32 k, Vec4 a){ + Vec4 result; + result.x = a.x * k; + result.y = a.y * k; + result.z = a.z * k; + result.w = a.w * k; + return result; +} + +inline Vec2& +operator+=(Vec2 &a, Vec2 b){ + a = (a + b); + return a; +} + +inline Vec3& +operator+=(Vec3 &a, Vec3 b){ + a = (a + b); + return a; +} + +inline Vec4& +operator+=(Vec4 &a, Vec4 b){ + a = (a + b); + return a; +} + +inline Vec2& +operator-=(Vec2 &a, Vec2 b){ + a = (a - b); + return a; +} + +inline Vec3& +operator-=(Vec3 &a, Vec3 b){ + a = (a - b); + return a; +} + +inline Vec4& +operator-=(Vec4 &a, Vec4 b){ + a = (a - b); + return a; +} + +inline Vec2& +operator*=(Vec2 &a, real32 k){ + a = (a * k); + return a; +} + +inline Vec3& +operator*=(Vec3 &a, real32 k){ + a = (a * k); + return a; +} + +inline Vec4& +operator*=(Vec4 &a, real32 k){ + a = (a * k); + return a; +} + +inline real32 +dot(Vec2 a, Vec2 b){ + real32 result; + result = a.x*b.x + a.y*b.y; + return result; +} + +inline real32 +dot(Vec3 a, Vec3 b){ + real32 result; + result = a.x*b.x + a.y*b.y + a.z*b.z; + return result; +} + +inline real32 +dot(Vec4 a, Vec4 b){ + real32 result; + result = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; + return result; +} + +inline Vec3 +cross(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.y*b.z - b.y*a.z; + result.y = a.z*b.x - b.z*a.x; + result.z = a.x*b.y - b.x*a.y; + return result; +} + +inline Vec2 +hadamard(Vec2 a, Vec2 b){ + Vec2 result; + result.x = a.x*b.x; + result.y = a.y*b.y; + return result; +} + +inline Vec3 +hadamard(Vec3 a, Vec3 b){ + Vec3 result; + result.x = a.x*b.x; + result.y = a.y*b.y; + result.z = a.z*b.z; + return result; +} + +inline Vec4 +hadamard(Vec4 a, Vec4 b){ + Vec4 result; + result.x = a.x*b.x; + result.y = a.y*b.y; + result.z = a.z*b.z; + result.w = a.w*b.w; + return result; +} + +inline Vec2 +perp(Vec2 v){ + Vec2 result; + result.x = -v.y; + result.y = v.x; + return result; +} + +inline Vec2 +polar_to_cartesian(real32 theta_degrees, real32 length){ + Vec2 result; + result.x = COS(theta_degrees)*length; + result.y = SIN(theta_degrees)*length; + return result; +} + +inline Vec2 +cis(f32 theta){ + Vec2 result; + result.x = COS(theta); + result.y = SIN(theta); + return result; +} + +inline Vec2 +rotate(Vec2 v, real32 theta_degrees){ + Vec2 result; + real32 c, s; + c = COS(theta_degrees); + s = SIN(theta_degrees); + result.x = v.x*c - v.y*s; + result.y = v.x*s + v.y*c; + return result; +} + +inline real32 +vector_projection(Vec2 x, Vec2 target){ + real32 target_magnitude = SQRT(dot(target, target)); + Vec2 unit_target = target * (1 / target_magnitude); + real32 result = dot(x, unit_target); + return(result); +} + +inline Vec2 +normalize(Vec2 x){ + f32 d = dot(x, x); + d = SQRT(d); + x *= (1.f/d); + return(x); +} + +inline Vec2 +normalize_radius(Vec2 x, f32 r){ + f32 d = dot(x, x); + d = SQRT(d); + r = r/d; + x *= r; + return(x); +} + +inline f32 +argument_degrees(Vec2 v){ + f32 r = ATAN_DEG(v.y, v.x); + return(r); +} + +/* + * Coordinates + */ + +struct Matrix2{ + Vec2 x_axis; + Vec2 y_axis; +}; + +internal Matrix2 +invert(Vec2 x_axis, Vec2 y_axis){ + Matrix2 result = {}; + real32 det = 1.f / (x_axis.x*y_axis.y - x_axis.y*y_axis.x); + result.x_axis.x = y_axis.y*det; + result.y_axis.x = -y_axis.x*det; + result.x_axis.y = -x_axis.y*det; + result.y_axis.y = x_axis.x*det; + return result; +} + +internal Matrix2 +invert(Matrix2 m){ + Matrix2 result = {}; + real32 det = 1.f / (m.x_axis.x*m.y_axis.y - m.x_axis.y*m.y_axis.x); + result.x_axis.x = m.y_axis.y*det; + result.y_axis.x = -m.y_axis.x*det; + result.x_axis.y = -m.x_axis.y*det; + result.y_axis.y = m.x_axis.x*det; + return result; +} + +/* + * Lerps, Clamps, Thresholds, Etc + */ + +inline real32 +lerp(real32 a, real32 t, real32 b){ + return a + (b-a)*t; +} + +inline Vec2 +lerp(Vec2 a, real32 t, Vec2 b){ + return a + (b-a)*t; +} + +inline Vec3 +lerp(Vec3 a, real32 t, Vec3 b){ + return a + (b-a)*t; +} + +inline Vec4 +lerp(Vec4 a, real32 t, Vec4 b){ + return a + (b-a)*t; +} + +inline real32 +unlerp(real32 a, real32 x, real32 b){ + return (x - a) / (b - a); +} + +inline real32 +clamp(real32 a, real32 n, real32 z){ + return (n<a)?(a):((n>z)?(z):n); +} + +/* + * Color + */ + +// TODO(allen): Convert colors to Vec4 +inline internal u32 +color_blend(u32 a, real32 t, u32 b){ + union{ + u8 byte[4]; + u32 comp; + } A, B, R; + + A.comp = a; + B.comp = b; + + R.byte[0] = (u8)lerp(A.byte[0], t, B.byte[0]); + R.byte[1] = (u8)lerp(A.byte[1], t, B.byte[1]); + R.byte[2] = (u8)lerp(A.byte[2], t, B.byte[2]); + R.byte[3] = (u8)lerp(A.byte[3], t, B.byte[3]); + + return R.comp; +} + +internal Vec3 +unpack_color3(u32 color){ + Vec3 result; + result.r = ((color >> 16) & 0xFF) / 255.f; + result.g = ((color >> 8) & 0xFF) / 255.f; + result.b = ((color >> 0) & 0xFF) / 255.f; + return result; +} + +internal Vec4 +unpack_color4(u32 color){ + Vec4 result; + result.a = ((color >> 24) & 0xFF) / 255.f; + result.r = ((color >> 16) & 0xFF) / 255.f; + result.g = ((color >> 8) & 0xFF) / 255.f; + result.b = ((color >> 0) & 0xFF) / 255.f; + return result; +} + +internal u32 +pack_color4(Vec4 color){ + u32 result = + ((u8)(color.a * 255) << 24) | + ((u8)(color.r * 255) << 16) | + ((u8)(color.g * 255) << 8) | + ((u8)(color.b * 255) << 0); + return result; +} + +internal Vec4 +rgba_to_hsla(Vec4 rgba){ + Vec4 hsla = {}; + real32 max, min, delta; + i32 maxc; + hsla.a = rgba.a; + max = rgba.r; min = rgba.r; + maxc = 0; + if (rgba.r < rgba.g){ + max = rgba.g; + maxc = 1; + } + if (rgba.b > max){ + max = rgba.b; + maxc = 2; + } + if (rgba.r > rgba.g){ + min = rgba.g; + } + if (rgba.b < min){ + min = rgba.b; + } + delta = max - min; + + hsla.z = (max + min) * .5f; + if (delta == 0){ + hsla.x = 0.f; + hsla.y = 0.f; + } + else{ + switch (maxc){ + case 0: + { + hsla.x = (rgba.g - rgba.b) / delta; + hsla.x += (rgba.g < rgba.b) * 6.f; + }break; + + case 1: + { + hsla.x = (rgba.b - rgba.r) / delta; + hsla.x += 2.f; + }break; + + case 2: + { + hsla.x = (rgba.r - rgba.g) / delta; + hsla.x += 4.f; + }break; + } + hsla.x *= (1/6.f); // * 60 / 360 + hsla.y = delta / (1.f - ABS(2.f*hsla.z - 1.f)); + } + + return hsla; +} + +internal Vec4 +hsla_to_rgba(Vec4 hsla){ + Vec4 rgba = {}; + real32 C, X, m; + i32 H; + rgba.a = hsla.a; + C = (1.f - ABS(2*hsla.z - 1.f)) * hsla.y; + X = C * (1.f-ABS(MOD(hsla.x*6.f, 2)-1.f)); + m = hsla.z - C*.5f; + H = FLOOR32(hsla.x * 6.f); + switch (H){ + case 0: + rgba.r = C; rgba.g = X; rgba.b = 0; + break; + + case 1: + rgba.r = X; rgba.g = C; rgba.b = 0; + break; + + case 2: + rgba.r = 0; rgba.g = C; rgba.b = X; + break; + + case 3: + rgba.r = 0; rgba.g = X; rgba.b = C; + break; + + case 4: + rgba.r = X; rgba.g = 0; rgba.b = C; + break; + + case 5: + rgba.r = C; rgba.g = 0; rgba.b = X; + break; + } + rgba.r += m; + rgba.g += m; + rgba.b += m; + return rgba; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/4tech_string.h b/test_data/lots_of_files/4tech_string.h new file mode 100644 index 0000000..04ce5d8 --- /dev/null +++ b/test_data/lots_of_files/4tech_string.h @@ -0,0 +1,1618 @@ + +// TOP + +#if defined(FSTRING_IMPLEMENTATION) && defined(FSTRING_GUARD) +#undef FSTRING_IMPLEMENTATION +#endif + +#include <stdint.h> + +#ifndef FSTRING_LINK +# define FSTRING_LINK static +#endif + +#ifndef FSTRING_INLINE +# define FSTRING_INLINE inline +#endif + +#ifndef FSTRING_STRUCT +#define FSTRING_STRUCT +typedef struct String{ + char *str; + int32_t size; + int32_t memory_size; +} String; + +typedef struct Offset_String{ + int32_t offset; + int32_t size; +} Offset_String; +#endif + +#ifndef fstr_bool +# define fstr_bool int32_t +#endif + +#ifndef literal +# define literal(s) (s), (sizeof(s)-1) +#endif + +#ifndef FCODER_STRING_H +#define FCODER_STRING_H + +FSTRING_INLINE fstr_bool char_is_slash(char c); +FSTRING_INLINE char char_to_upper(char c); +FSTRING_INLINE char char_to_lower(char c); +FSTRING_INLINE fstr_bool char_is_whitespace(char c); +FSTRING_INLINE fstr_bool char_is_alpha_numeric(char c); +FSTRING_INLINE fstr_bool char_is_alpha_numeric_true(char c); +FSTRING_INLINE fstr_bool char_is_alpha(char c); +FSTRING_INLINE fstr_bool char_is_alpha_true(char c); +FSTRING_INLINE fstr_bool char_is_hex(char c); +FSTRING_INLINE fstr_bool char_is_numeric(char c); +FSTRING_INLINE String string_zero(void); +FSTRING_INLINE String make_string_max(void *str, int32_t size, int32_t mem_size); +FSTRING_INLINE String make_string(void *str, int32_t size); +#ifndef make_lit_string +# define make_lit_string(s) (make_string_max((char*)(s), sizeof(s)-1, sizeof(s))) +#endif +#ifndef make_fixed_width_string +# define make_fixed_width_string(s) (make_string_max((char*)(s), 0, sizeof(s))) +#endif +#ifndef expand_str +# define expand_str(s) ((s).str), ((s).size) +#endif +FSTRING_LINK int32_t str_size(char *str); +FSTRING_INLINE String make_string_slowly(void *str); +FSTRING_INLINE String substr(String str, int32_t start); +FSTRING_INLINE String substr_start_size(String str, int32_t start, int32_t size); +FSTRING_LINK String skip_whitespace(String str); +FSTRING_LINK String chop_whitespace(String str); +FSTRING_LINK String skip_chop_whitespace(String str); +FSTRING_INLINE String tailstr(String str); +FSTRING_LINK fstr_bool match_cc(char *a, char *b); +FSTRING_LINK fstr_bool match_sc(String a, char *b); +FSTRING_INLINE fstr_bool match_cs(char *a, String b); +FSTRING_LINK fstr_bool match_ss(String a, String b); +FSTRING_LINK fstr_bool match_part_ccl(char *a, char *b, int32_t *len); +FSTRING_LINK fstr_bool match_part_scl(String a, char *b, int32_t *len); +FSTRING_INLINE fstr_bool match_part_cc(char *a, char *b); +FSTRING_INLINE fstr_bool match_part_sc(String a, char *b); +FSTRING_LINK fstr_bool match_part_cs(char *a, String b); +FSTRING_LINK fstr_bool match_part_ss(String a, String b); +FSTRING_LINK fstr_bool match_insensitive_cc(char *a, char *b); +FSTRING_LINK fstr_bool match_insensitive_sc(String a, char *b); +FSTRING_INLINE fstr_bool match_insensitive_cs(char *a, String b); +FSTRING_LINK fstr_bool match_insensitive_ss(String a, String b); +FSTRING_LINK fstr_bool match_part_insensitive_ccl(char *a, char *b, int32_t *len); +FSTRING_LINK fstr_bool match_part_insensitive_scl(String a, char *b, int32_t *len); +FSTRING_INLINE fstr_bool match_part_insensitive_cc(char *a, char *b); +FSTRING_INLINE fstr_bool match_part_insensitive_sc(String a, char *b); +FSTRING_LINK fstr_bool match_part_insensitive_cs(char *a, String b); +FSTRING_LINK fstr_bool match_part_insensitive_ss(String a, String b); +FSTRING_LINK int32_t compare_cc(char *a, char *b); +FSTRING_LINK int32_t compare_sc(String a, char *b); +FSTRING_INLINE int32_t compare_cs(char *a, String b); +FSTRING_LINK int32_t compare_ss(String a, String b); +FSTRING_LINK int32_t find_c(char *str, int32_t start, char character); +FSTRING_LINK int32_t find_s(String str, int32_t start, char character); +FSTRING_LINK int32_t find_chars_c(char *str, int32_t start, char *characters); +FSTRING_LINK int32_t find_chars_s(String str, int32_t start, char *characters); +FSTRING_LINK int32_t find_substr_c(char *str, int32_t start, String seek); +FSTRING_LINK int32_t find_substr_s(String str, int32_t start, String seek); +FSTRING_LINK int32_t rfind_substr(String str, int32_t start, String seek); +FSTRING_LINK int32_t find_substr_insensitive_c(char *str, int32_t start, String seek); +FSTRING_LINK int32_t find_substr_insensitive_s(String str, int32_t start, String seek); +FSTRING_INLINE fstr_bool has_substr_c(char *s, String seek); +FSTRING_INLINE fstr_bool has_substr_s(String s, String seek); +FSTRING_INLINE fstr_bool has_substr_insensitive_c(char *s, String seek); +FSTRING_INLINE fstr_bool has_substr_insensitive_s(String s, String seek); +FSTRING_LINK int32_t copy_fast_unsafe_cc(char *dest, char *src); +FSTRING_LINK int32_t copy_fast_unsafe_cs(char *dest, String src); +FSTRING_LINK fstr_bool copy_checked_ss(String *dest, String src); +FSTRING_LINK fstr_bool copy_partial_sc(String *dest, char *src); +FSTRING_LINK fstr_bool copy_partial_ss(String *dest, String src); +FSTRING_INLINE int32_t copy_cc(char *dest, char *src); +FSTRING_INLINE void copy_ss(String *dest, String src); +FSTRING_INLINE void copy_sc(String *dest, char *src); +FSTRING_LINK fstr_bool append_checked_ss(String *dest, String src); +FSTRING_LINK fstr_bool append_partial_sc(String *dest, char *src); +FSTRING_LINK fstr_bool append_partial_ss(String *dest, String src); +FSTRING_LINK fstr_bool append_char(String *dest, char c); +FSTRING_INLINE fstr_bool append_s(String *dest, String src); +FSTRING_INLINE fstr_bool append_c(String *dest, char *src); +FSTRING_LINK fstr_bool terminate_with_null(String *str); +FSTRING_LINK fstr_bool append_padding(String *dest, char c, int32_t target_size); +FSTRING_LINK void replace_char(String *str, char replace, char with); +FSTRING_LINK int32_t int_to_str_size(int32_t x); +FSTRING_LINK fstr_bool int_to_str(String *dest, int32_t x); +FSTRING_LINK fstr_bool append_int_to_str(String *dest, int32_t x); +FSTRING_LINK int32_t u64_to_str_size(uint64_t x); +FSTRING_LINK fstr_bool u64_to_str(String *dest, uint64_t x); +FSTRING_LINK fstr_bool append_u64_to_str(String *dest, uint64_t x); +FSTRING_LINK int32_t float_to_str_size(float x); +FSTRING_LINK fstr_bool append_float_to_str(String *dest, float x); +FSTRING_LINK fstr_bool float_to_str(String *dest, float x); +FSTRING_LINK fstr_bool str_is_int_s(String str); +FSTRING_LINK int32_t str_to_int_c(char *str); +FSTRING_LINK int32_t str_to_int_s(String str); +FSTRING_LINK int32_t hexchar_to_int(char c); +FSTRING_LINK char int_to_hexchar(int32_t x); +FSTRING_LINK uint32_t hexstr_to_int(String str); +FSTRING_LINK fstr_bool color_to_hexstr(String *s, uint32_t color); +FSTRING_LINK fstr_bool hexstr_to_color(String s, uint32_t *out); +FSTRING_LINK int32_t reverse_seek_slash_pos(String str, int32_t pos); +FSTRING_INLINE int32_t reverse_seek_slash(String str); +FSTRING_INLINE String front_of_directory(String dir); +FSTRING_INLINE String path_of_directory(String dir); +FSTRING_LINK fstr_bool set_last_folder_c(String *dir, char *folder_name, char slash); +FSTRING_LINK fstr_bool set_last_folder_s(String *dir, String folder_name, char slash); +FSTRING_LINK String file_extension(String str); +FSTRING_LINK fstr_bool remove_extension(String *str); +FSTRING_LINK fstr_bool remove_last_folder(String *str); +FSTRING_LINK fstr_bool string_set_match(String *str_set, int32_t count, String str, int32_t *match_index); + +#endif + + +// +// Character Helpers +// + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_slash(char c) +{ + return (c == '\\' || c == '/'); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE char +char_to_upper(char c) +{ + return (c >= 'a' && c <= 'z') ? c + (char)('A' - 'a') : c; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE char +char_to_lower(char c) +{ + return (c >= 'A' && c <= 'Z') ? c - (char)('A' - 'a') : c; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_whitespace(char c) +{ + return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_alpha_numeric(char c) +{ + return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_'); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_alpha_numeric_true(char c) +{ + return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_alpha(char c) +{ + return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_'); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_alpha_true(char c) +{ + return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_hex(char c) +{ + return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f'; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +char_is_numeric(char c) +{ + return (c >= '0' && c <= '9'); +} +#endif + + +// +// String Making Functions +// + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +string_zero() +{ + String str={0}; + return(str); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +make_string_max(void *str, int32_t size, int32_t mem_size) +{ + String result; + result.str = (char*)str; + result.size = size; + result.memory_size = mem_size; + return result; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +make_string(void *str, int32_t size){ + String result; + result.str = (char*)str; + result.size = size; + result.memory_size = size; + return result; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +str_size(char *str) +{ + int32_t i = 0; + while (str[i]) ++i; + return i; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +make_string_slowly(void *str) +{ + String result; + result.str = (char*)str; + result.size = str_size((char*)str); + result.memory_size = result.size; + return result; +} +#endif + +// TODO(allen): I don't love the substr rule, I chose +// substr(String, start) and substr(String, start, size) +// but I wish I had substr(String, start) and substr(String, start, end) + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +substr(String str, int32_t start) +{ + String result; + result.str = str.str + start; + result.size = str.size - start; + result.memory_size = 0; + return(result); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +substr_start_size(String str, int32_t start, int32_t size) +{ + String result; + result.str = str.str + start; + result.size = size; + if (start + size > str.size){ + result.size = str.size - start; + } + result.memory_size = 0; + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK String +skip_whitespace(String str) +{ + String result = {0}; + int32_t i = 0; + for (; i < str.size && char_is_whitespace(str.str[i]); ++i); + result = substr_start_size(str, i, str.size - i); + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK String +chop_whitespace(String str) +{ + String result = {0}; + int32_t i = str.size; + for (; i > 0 && char_is_whitespace(str.str[i-1]); --i); + result = substr_start_size(str, 0, i); + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK String +skip_chop_whitespace(String str) +{ + str = skip_whitespace(str); + str = chop_whitespace(str); + return(str); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +tailstr(String str) +{ + String result; + result.str = str.str + str.size; + result.memory_size = str.memory_size - str.size; + result.size = 0; + return(result); +} +#endif + + +// +// String Comparison +// + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_cc(char *a, char *b){ + for (int32_t i = 0;; ++i){ + if (a[i] != b[i]){ + return 0; + } + if (a[i] == 0){ + return 1; + } + } +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_sc(String a, char *b){ + int32_t i = 0; + for (; i < a.size; ++i){ + if (a.str[i] != b[i]){ + return 0; + } + } + if (b[i] != 0){ + return 0; + } + return 1; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +match_cs(char *a, String b){ + return(match_sc(b,a)); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_ss(String a, String b){ + if (a.size != b.size){ + return 0; + } + for (int32_t i = 0; i < b.size; ++i){ + if (a.str[i] != b.str[i]){ + return 0; + } + } + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_ccl(char *a, char *b, int32_t *len){ + int32_t i; + for (i = 0; b[i] != 0; ++i){ + if (a[i] != b[i]){ + return 0; + } + } + *len = i; + return 1; +} +#endif + + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_scl(String a, char *b, int32_t *len){ + int32_t i; + for (i = 0; b[i] != 0; ++i){ + if (a.str[i] != b[i] || i == a.size){ + return 0; + } + } + *len = i; + return 1; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +match_part_cc(char *a, char *b){ + int32_t x; + return match_part_ccl(a,b,&x); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +match_part_sc(String a, char *b){ + int32_t x; + return match_part_scl(a,b,&x); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_cs(char *a, String b){ + for (int32_t i = 0; i != b.size; ++i){ + if (a[i] != b.str[i]){ + return 0; + } + } + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_ss(String a, String b){ + if (a.size < b.size){ + return 0; + } + for (int32_t i = 0; i < b.size; ++i){ + if (a.str[i] != b.str[i]){ + return 0; + } + } + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_insensitive_cc(char *a, char *b){ + for (int32_t i = 0;; ++i){ + if (char_to_upper(a[i]) != + char_to_upper(b[i])){ + return 0; + } + if (a[i] == 0){ + return 1; + } + } +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_insensitive_sc(String a, char *b){ + int32_t i = 0; + for (; i < a.size; ++i){ + if (char_to_upper(a.str[i]) != + char_to_upper(b[i])){ + return 0; + } + } + if (b[i] != 0){ + return 0; + } + return 1; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +match_insensitive_cs(char *a, String b){ + return match_insensitive_sc(b,a); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_insensitive_ss(String a, String b){ + if (a.size != b.size){ + return 0; + } + for (int32_t i = 0; i < b.size; ++i){ + if (char_to_upper(a.str[i]) != + char_to_upper(b.str[i])){ + return 0; + } + } + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_insensitive_ccl(char *a, char *b, int32_t *len){ + int32_t i; + for (i = 0; b[i] != 0; ++i){ + if (char_to_upper(a[i]) != char_to_upper(b[i])){ + return 0; + } + } + *len = i; + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_insensitive_scl(String a, char *b, int32_t *len){ + int32_t i; + for (i = 0; b[i] != 0; ++i){ + if (char_to_upper(a.str[i]) != char_to_upper(b[i]) || + i == a.size){ + return 0; + } + } + *len = i; + return 1; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +match_part_insensitive_cc(char *a, char *b){ + int32_t x; + return match_part_ccl(a,b,&x); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +match_part_insensitive_sc(String a, char *b){ + int32_t x; + return match_part_scl(a,b,&x); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_insensitive_cs(char *a, String b){ + for (int32_t i = 0; i != b.size; ++i){ + if (char_to_upper(a[i]) != char_to_upper(b.str[i])){ + return 0; + } + } + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +match_part_insensitive_ss(String a, String b){ + if (a.size < b.size){ + return 0; + } + for (int32_t i = 0; i < b.size; ++i){ + if (char_to_upper(a.str[i]) != char_to_upper(b.str[i])){ + return 0; + } + } + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +compare_cc(char *a, char *b){ + int32_t i = 0; + while (a[i] == b[i] && a[i] != 0){ + ++i; + } + return (a[i] > b[i]) - (a[i] < b[i]); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +compare_sc(String a, char *b){ + int32_t i = 0; + while (i < a.size && a.str[i] == b[i]){ + ++i; + } + if (i < a.size){ + return (a.str[i] > b[i]) - (a.str[i] < b[i]); + } + else{ + if (b[i] == 0){ + return 0; + } + else{ + return -1; + } + } +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE int32_t +compare_cs(char *a, String b){ + return -compare_sc(b,a); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +compare_ss(String a, String b){ + int32_t i = 0; + while (i < a.size && i < b.size && a.str[i] == b.str[i]){ + ++i; + } + if (i < a.size && i < b.size){ + return (a.str[i] > b.str[i]) - (a.str[i] < b.str[i]); + } + else{ + return (a.size > b.size) - (a.size < b.size); + } +} +#endif + +// +// Finding Characters and Substrings +// + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_c(char *str, int32_t start, char character){ + int32_t i = start; + while (str[i] != character && str[i] != 0) ++i; + return i; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_s(String str, int32_t start, char character){ + int32_t i = start; + while (i < str.size && str.str[i] != character) ++i; + return i; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_chars_c(char *str, int32_t start, char *characters){ + int32_t i = start, j; + while (str[i] != 0){ + for (j = 0; characters[j]; ++j){ + if (str[i] == characters[j]){ + return i; + } + } + ++i; + } + return i; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_chars_s(String str, int32_t start, char *characters){ + int32_t i = start, j; + while (i < str.size){ + for (j = 0; characters[j]; ++j){ + if (str.str[i] == characters[j]){ + return i; + } + } + ++i; + } + return i; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_substr_c(char *str, int32_t start, String seek){ + int32_t i, j, k; + fstr_bool hit; + + if (seek.size == 0){ + return str_size(str); + } + for (i = start; str[i]; ++i){ + if (str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + if (str[k] != seek.str[j]){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return i; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_substr_s(String str, int32_t start, String seek){ + int32_t stop_at, i, j, k; + fstr_bool hit; + + if (seek.size == 0){ + return str.size; + } + stop_at = str.size - seek.size + 1; + for (i = start; i < stop_at; ++i){ + if (str.str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + if (str.str[k] != seek.str[j]){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return str.size; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +rfind_substr(String str, int32_t start, String seek){ + int32_t i, j, k; + fstr_bool hit; + + if (seek.size == 0){ + return -1; + } + if (start + seek.size > str.size){ + start = str.size - seek.size; + } + for (i = start; i >= 0; --i){ + if (str.str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + if (str.str[k] != seek.str[j]){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return -1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_substr_insensitive_c(char *str, int32_t start, String seek){ + int32_t i, j, k; + fstr_bool hit; + char a_upper, b_upper; + + if (seek.size == 0){ + return str_size(str); + } + for (i = start; str[i]; ++i){ + if (str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + a_upper = char_to_upper(str[k]); + b_upper = char_to_upper(seek.str[j]); + if (a_upper != b_upper){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return i; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +find_substr_insensitive_s(String str, int32_t start, String seek){ + int32_t i, j, k; + int32_t stop_at; + fstr_bool hit; + char a_upper, b_upper; + + if (seek.size == 0){ + return str.size; + } + stop_at = str.size - seek.size + 1; + for (i = start; i < stop_at; ++i){ + if (str.str[i] == seek.str[0]){ + hit = 1; + for (j = 1, k = i+1; j < seek.size; ++j, ++k){ + a_upper = char_to_upper(str.str[k]); + b_upper = char_to_upper(seek.str[j]); + if (a_upper != b_upper){ + hit = 0; + break; + } + } + if (hit){ + return i; + } + } + } + return str.size; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +has_substr_c(char *s, String seek){ + return (s[find_substr_c(s, 0, seek)] != 0); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +has_substr_s(String s, String seek){ + return (find_substr_s(s, 0, seek) < s.size); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +has_substr_insensitive_c(char *s, String seek){ + return (s[find_substr_insensitive_c(s, 0, seek)] != 0); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +has_substr_insensitive_s(String s, String seek){ + return (find_substr_insensitive_s(s, 0, seek) < s.size); +} +#endif + +// +// String Copies and Appends +// + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +copy_fast_unsafe_cc(char *dest, char *src){ + char *start = dest; + while (*src != 0){ + *dest = *src; + ++dest; + ++src; + } + return (int32_t)(dest - start); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +copy_fast_unsafe_cs(char *dest, String src){ + int32_t i = 0; + while (i != src.size){ + dest[i] = src.str[i]; + ++i; + } + return(src.size); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +copy_checked_ss(String *dest, String src){ + char *dest_str; + int32_t i; + if (dest->memory_size < src.size){ + return 0; + } + dest_str = dest->str; + for (i = 0; i < src.size; ++i){ + dest_str[i] = src.str[i]; + } + dest->size = src.size; + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +copy_partial_sc(String *dest, char *src){ + int32_t i = 0; + int32_t memory_size = dest->memory_size; + char *dest_str = dest->str; + while (src[i] != 0){ + if (i >= memory_size){ + return 0; + } + dest_str[i] = src[i]; + ++i; + } + dest->size = i; + return 1; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +copy_partial_ss(String *dest, String src){ + char *dest_str = dest->str; + int32_t memory_size = dest->memory_size; + fstr_bool result = 0; + if (memory_size >= src.size){ + result = 1; + memory_size = src.size; + } + for (int32_t i = 0; i < memory_size; ++i){ + dest_str[i] = src.str[i]; + } + dest->size = memory_size; + return result; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE int32_t +copy_cc(char *dest, char *src){ + return copy_fast_unsafe_cc(dest, src); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE void +copy_ss(String *dest, String src){ + copy_checked_ss(dest, src); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE void +copy_sc(String *dest, char *src){ + copy_partial_sc(dest, src); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_checked_ss(String *dest, String src){ + String end = tailstr(*dest); + fstr_bool result = copy_checked_ss(&end, src); + // NOTE(allen): This depends on end.size still being 0 if + // the check failed and no coppy occurred. + dest->size += end.size; + return result; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_partial_sc(String *dest, char *src){ + String end = tailstr(*dest); + fstr_bool result = copy_partial_sc(&end, src); + dest->size += end.size; + return result; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_partial_ss(String *dest, String src){ + String end = tailstr(*dest); + fstr_bool result = copy_partial_ss(&end, src); + dest->size += end.size; + return result; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_char(String *dest, char c){ + fstr_bool result = 0; + if (dest->size < dest->memory_size){ + dest->str[dest->size++] = c; + result = 1; + } + return result; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +append_ss(String *dest, String src){ + return append_partial_ss(dest, src); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE fstr_bool +append_sc(String *dest, char *src){ + return append_partial_sc(dest, src); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +terminate_with_null(String *str){ + fstr_bool result = 0; + if (str->size < str->memory_size){ + str->str[str->size] = 0; + result = 1; + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_padding(String *dest, char c, int32_t target_size){ + fstr_bool result = 1; + int32_t offset = target_size - dest->size; + int32_t r = 0; + if (offset > 0){ + for (r = 0; r < offset; ++r){ + if (append_char(dest, c) == 0){ + result = 0; + break; + } + } + } + return(result); +} +#endif + + +// +// Other Edits +// + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK void +replace_char(String *str, char replace, char with){ + char *s = str->str; + int32_t i = 0; + for (i = 0; i < str->size; ++i, ++s){ + if (*s == replace) *s = with; + } +} +#endif + +// +// String <-> Number Conversions +// + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +int_to_str_size(int32_t x){ + int32_t size = 1; + if (x < 0){ + size = 2; + } + x /= 10; + while (x != 0){ + x /= 10; + ++size; + } + return(size); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +int_to_str(String *dest, int32_t x){ + fstr_bool result = 1; + char *str = dest->str; + int32_t memory_size = dest->memory_size; + int32_t size, i, j; + fstr_bool negative; + + if (x == 0){ + str[0] = '0'; + dest->size = 1; + } + else{ + size = 0; + negative = 0; + if (x < 0){ + negative = 1; + x = -x; + str[size++] = '-'; + } + while (x != 0){ + if (size == memory_size){ + result = 0; + break; + } + i = x % 10; + x /= 10; + str[size++] = (char)('0' + i); + } + if (result){ + // NOTE(allen): Start i = 0 if not negative, start i = 1 if is negative + // because - should not be flipped if it is negative :) + for (i = negative, j = size-1; i < j; ++i, --j){ + char temp = str[i]; + str[i] = str[j]; + str[j] = temp; + } + dest->size = size; + } + else{ + dest->size = 0; + } + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_int_to_str(String *dest, int32_t x){ + String last_part = tailstr(*dest); + fstr_bool result = int_to_str(&last_part, x); + if (result){ + dest->size += last_part.size; + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +u64_to_str_size(uint64_t x){ + int32_t size; + if (x < 0){ + size = 0; + } + else{ + size = 1; + x /= 10; + while (x != 0){ + x /= 10; + ++size; + } + } + return(size); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +u64_to_str(String *dest, uint64_t x){ + fstr_bool result = 1; + char *str = dest->str; + int32_t memory_size = dest->memory_size; + int32_t size, i, j; + + if (x == 0){ + str[0] = '0'; + dest->size = 1; + } + else{ + size = 0; + while (x != 0){ + if (size == memory_size){ + result = 0; + break; + } + i = x % 10; + x /= 10; + str[size++] = (char)('0' + i); + } + if (result){ + for (i = 0, j = size-1; i < j; ++i, --j){ + char temp = str[i]; + str[i] = str[j]; + str[j] = temp; + } + dest->size = size; + } + else{ + dest->size = 0; + } + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_u64_to_str(String *dest, uint64_t x){ + String last_part = tailstr(*dest); + fstr_bool result = u64_to_str(&last_part, x); + if (result){ + dest->size += last_part.size; + } + return(result); +} +#endif + +#ifndef FSTRING_GUARD +typedef struct Float_To_Str_Variables{ + fstr_bool negative; + int32_t int_part; + int32_t dec_part; +} Float_To_Str_Variables; + +Float_To_Str_Variables +get_float_vars(float x){ + Float_To_Str_Variables vars = {0}; + + if (x < 0){ + vars.negative = 1; + x = -x; + } + + vars.int_part = (int32_t)(x); + vars.dec_part = (int32_t)((x - vars.int_part) * 1000); + + return(vars); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +float_to_str_size(float x){ + Float_To_Str_Variables vars = get_float_vars(x); + int32_t size = + vars.negative + int_to_str_size(vars.int_part) + 1 + int_to_str_size(vars.dec_part); + return(size); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +append_float_to_str(String *dest, float x){ + fstr_bool result = 1; + Float_To_Str_Variables vars = get_float_vars(x); + + if (vars.negative){ + append_char(dest, '-'); + } + + append_int_to_str(dest, vars.int_part); + append_char(dest, '.'); + append_int_to_str(dest, vars.dec_part); + + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +float_to_str(String *dest, float x){ + fstr_bool result = 1; + dest->size = 0; + append_float_to_str(dest, x); + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +str_is_int(String str){ + fstr_bool result = 1; + for (int32_t i = 0; i < str.size; ++i){ + if (!char_is_numeric(str.str[i])){ + result = 0; + break; + } + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +str_to_int_c(char *str){ + int32_t x = 0; + for (; *str; ++str){ + if (*str >= '0' || *str <= '9'){ + x *= 10; + x += *str - '0'; + } + else{ + x = 0; + break; + } + } + return(x); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +str_to_int_s(String str){ + int32_t x, i; + if (str.size == 0){ + x = 0; + } + else{ + x = str.str[0] - '0'; + for (i = 1; i < str.size; ++i){ + x *= 10; + x += str.str[i] - '0'; + } + } + return(x); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +hexchar_to_int(char c){ + int32_t x = 0; + if (c >= '0' && c <= '9'){ + x = c-'0'; + } + else if (c > 'F'){ + x = c+(10-'a'); + } + else{ + x = c+(10-'A'); + } + return(x); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK char +int_to_hexchar(int32_t x){ + return (x<10)?((char)x+'0'):((char)x+'a'-10); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK uint32_t +hexstr_to_int(String str){ + uint32_t x; + int32_t i; + if (str.size == 0){ + x = 0; + } + else{ + x = hexchar_to_int(str.str[0]); + for (i = 1; i < str.size; ++i){ + x *= 0x10; + x += hexchar_to_int(str.str[i]); + } + } + return(x); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +color_to_hexstr(String *s, uint32_t color){ + fstr_bool result = 0; + int32_t i; + + if (s->memory_size == 7 || s->memory_size == 8){ + result = 1; + s->size = 6; + s->str[6] = 0; + color = color & 0x00FFFFFF; + for (i = 5; i >= 0; --i){ + s->str[i] = int_to_hexchar(color & 0xF); + color >>= 4; + } + } + else if (s->memory_size > 8){ + result = 1; + s->size = 8; + s->str[8] = 0; + for (i = 7; i >= 0; --i){ + s->str[i] = int_to_hexchar(color & 0xF); + color >>= 4; + } + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +hexstr_to_color(String s, uint32_t *out){ + fstr_bool result = 0; + uint32_t color = 0; + if (s.size == 6){ + result = 1; + color = (unsigned int)hexstr_to_int(s); + color |= (0xFF << 24); + *out = color; + } + else if (s.size == 8){ + result = 1; + color = (unsigned int)hexstr_to_int(s); + *out = color; + } + return(result); +} +#endif + +// +// Directory String Management +// + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK int32_t +reverse_seek_slash_pos(String str, int32_t pos){ + int32_t i = str.size - 1 - pos; + while (i >= 0 && !char_is_slash(str.str[i])){ + --i; + } + return i; +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE int32_t +reverse_seek_slash(String str){ + return(reverse_seek_slash_pos(str, 0)); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +front_of_directory(String dir){ + return substr(dir, reverse_seek_slash(dir) + 1); +} +#endif + +#ifndef FSTRING_GUARD +FSTRING_INLINE String +path_of_directory(String dir){ + return substr_start_size(dir, 0, reverse_seek_slash(dir) + 1); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +set_last_folder_c(String *dir, char *folder_name, char slash){ + fstr_bool result = 0; + int32_t size = reverse_seek_slash(*dir) + 1; + dir->size = size; + if (append_sc(dir, folder_name)){ + if (append_char(dir, slash)){ + result = 1; + } + } + if (!result){ + dir->size = size; + } + return result; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +set_last_folder_s(String *dir, String folder_name, char slash){ + fstr_bool result = 0; + int32_t size = reverse_seek_slash(*dir) + 1; + dir->size = size; + if (append_ss(dir, folder_name)){ + if (append_char(dir, slash)){ + result = 1; + } + } + if (!result){ + dir->size = size; + } + return result; +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK String +file_extension(String str){ + int32_t i; + for (i = str.size - 1; i >= 0; --i){ + if (str.str[i] == '.') break; + } + ++i; + return make_string(str.str+i, str.size-i); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +remove_extension(String *str){ + fstr_bool result = 0; + char *s = str->str; + int32_t i; + for (i = str->size - 1; i >= 0; --i){ + if (s[i] == '.') break; + } + if (i >= 0){ + result = 1; + str->size = i; + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +remove_last_folder(String *str){ + fstr_bool result = 0; + int32_t end = reverse_seek_slash_pos(*str, 1); + if (end >= 0){ + result = 1; + str->size = end + 1; + } + return(result); +} +#endif + +// TODO(allen): Add hash-table extension to string sets. +#ifdef FSTRING_IMPLEMENTATION +FSTRING_LINK fstr_bool +string_set_match(String *str_set, int32_t count, String str, int32_t *match_index){ + fstr_bool result = 0; + int32_t i = 0; + for (; i < count; ++i, ++str_set){ + if (match_ss(*str_set, str)){ + *match_index = i; + result = 1; + break; + } + } + return(result); +} +#endif + +#ifdef FSTRING_IMPLEMENTATION +#undef FSTRING_IMPLEMENTATION +#endif + +#ifndef FSTRING_GUARD +#define FSTRING_GUARD +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/4tech_table.cpp b/test_data/lots_of_files/4tech_table.cpp new file mode 100644 index 0000000..e1d9280 --- /dev/null +++ b/test_data/lots_of_files/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/test_data/lots_of_files/Agent.cpp b/test_data/lots_of_files/Agent.cpp new file mode 100644 index 0000000..cced404 --- /dev/null +++ b/test_data/lots_of_files/Agent.cpp @@ -0,0 +1,424 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// Agent.cpp +// +// Source file containing code for the agent creation APIs. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +#include "concrtinternal.h" +#include <agents.h> + +namespace Concurrency +{ + +// A Filter function for a filter_block to check if the Agent has completed +bool _IsDone(agent_status const &status) +{ + return status == agent_done || status == agent_canceled; +} + +// A Filter function for a filter_block to check if the Agent has started (or completed) +bool _IsStarted(agent_status const &status) +{ + return _IsDone(status) || status == agent_started; +} + +/// <summary> +/// Creates an agent within the default scheduler, and places it any schedule +/// group of the scheduler's choosing. +/// </summary> +agent::agent() : + _M_fStartable(TRUE), _M_fCancelable(TRUE), _M_pScheduler(NULL), _M_pScheduleGroup(NULL) +{ + _Trace_agents(AGENTS_EVENT_CREATE, + details::_Trace_agents_get_id(this), + details::_Trace_agents_get_id(this)); + + send<agent_status> (_M_status, agent_created); +} + +/// <summary> +/// Create an agent within the specified scheduler, in a schedule group of the +/// scheduler's choosing. +/// </summary> +agent::agent(Scheduler& pScheduler) : + _M_fStartable(TRUE), _M_fCancelable(TRUE), _M_pScheduler(&pScheduler), _M_pScheduleGroup(NULL) +{ + _Trace_agents(AGENTS_EVENT_CREATE, + details::_Trace_agents_get_id(this), + details::_Trace_agents_get_id(this)); + + send<agent_status> (_M_status, agent_created); +} + +/// <summary> +/// Create an agent within the specified schedule group. The scheduler is implied +/// by the schedule group. +/// </summary> +agent::agent(ScheduleGroup& pGroup) : + _M_fStartable(TRUE), _M_fCancelable(TRUE), _M_pScheduler(NULL), _M_pScheduleGroup(&pGroup) +{ + _Trace_agents(AGENTS_EVENT_CREATE, + details::_Trace_agents_get_id(this), + details::_Trace_agents_get_id(this)); + + send<agent_status> (_M_status, agent_created); +} + +/// <summary> +/// Cleans up any resources that may have been created by the Agent. +/// </summary> +agent::~agent() +{ + _Trace_agents(AGENTS_EVENT_DESTROY, details::_Trace_agents_get_id(this)); +} + +/// <returns> +/// Returns a message source that can pass messages about the current state of the agent +/// </returns> +ISource<agent_status> * agent::status_port() +{ + return &_M_status; +} + +/// <returns> +/// Returns the current state of the agent. Note that this returned state could change +/// immediately after being returned. +/// </returns> +agent_status agent::status() +{ + return receive<agent_status>(_M_status); +} + +/// <summary> +/// Moves an Agent from the agent_created state to the agent_runnable state, and schedules it for execution. +/// </summary> +/// <returns> +/// true if the agent started correctly, false otherwise +/// </returns> +bool agent::start() +{ + if(_M_status.value() != agent_created) + { + return false; + } + + // + // Check if the agent is Startable. If the agent had already called start() or + // this variable was set to FALSE in cancel(), return false. + // + if(InterlockedCompareExchange(&_M_fStartable, FALSE, TRUE) == FALSE) + { + return false; + } + + _Trace_agents(AGENTS_EVENT_SCHEDULE, details::_Trace_agents_get_id(this)); + send<agent_status> (_M_status, agent_runnable); + + TaskProc proc = &Concurrency::agent::_Agent_task_wrapper; + if(_M_pScheduleGroup != NULL) + { + _M_pScheduleGroup->ScheduleTask(proc, this); + } + else if(_M_pScheduler != NULL) + { + _M_pScheduler->ScheduleTask(proc, this); + } + else + { + CurrentScheduler::ScheduleTask(proc, this); + } + + return true; +} + +/// <summary> +/// Moves an agent into the done state, indicating the completion of the agent +/// </summary> +/// <returns> +/// true if the agent is moved to the agent_done state, false otherwise +/// </returns> +bool agent::done() +{ + // + // current status + // + agent_status currentStatus = this->status(); + + // + // Indicate that the agent can no longer be started. + // + if (InterlockedCompareExchange(&_M_fStartable, FALSE, TRUE) != TRUE) + { + // + // agent is either canceled, started or completed run. + // + currentStatus = receive<agent_status>(_M_status, _IsStarted); + } + + // + // Agent is not cancelable anymore. + // + InterlockedExchange(&_M_fCancelable, FALSE); + + // + // Transition to agent_done state if it not already in one of + // the terminal states. + // + if ((currentStatus != agent_canceled) && (currentStatus != agent_done)) + { + send<agent_status> (_M_status, agent_done); + + return true; + } + + return false; +} + +/// <summary> +/// Moves an agent from the agent_created or agent_runnable to the agent_canceled state. +/// </summary> +/// <returns> +/// true if the agent was canceled correctly, false otherwise +/// </returns> +bool agent::cancel() +{ + // + // In case this agent has been canceled before it was even started + // mark it as no longer Startable and send a agent_canceled message to the + // status port + // + if(InterlockedCompareExchange(&_M_fStartable, FALSE, TRUE) == TRUE) + { + send<agent_status> (_M_status, agent_canceled); + } + + // + // Check to see if the agent is still Cancelable. Agents are initialized + // m_fCancelable == TRUE, and set to false either here in cancel(), so + // cancel() will not be called twice, or in the LWT, once the execution + // of the Agent task has begun. + // + if(InterlockedCompareExchange(&_M_fCancelable, FALSE, TRUE) == TRUE) + { + // Wait for the agent to reach a canceled state state + receive<agent_status>(_M_status, _IsDone); + + // The above InterlockedCompareExchange marked this agent for cancelation + // When the LWT that has been spun up tries to execute the task, it will + // find it has been canceled and will propagate out the canceled state to + // the state buffer. + return true; + } + + return false; +} + + +// Private helper class to order an input array of agents. This is used by +// wait_for_all and wait_for_one to create an array of appropriate order nodes. +// The template _OrderNode specifies an _Order_node_base that accepts agent_status. +// For example, _Reserving_node<agent_status> +template<class _OrderNode> +class _OrderBlock +{ +public: + + // Constructs an orderBlock which has an array of ordernodes connected to the agents. + // The ordernodes are given a filter method to filter out non-terminal agent states + _OrderBlock(size_t _Count, agent ** _PAgents, ITarget<size_t> * _PTarget) : _M_count(_Count) + { + // Create an array of order nodes + _M_ppNodes = _concrt_new _OrderNode*[_M_count]; + for (size_t i = 0; i < _M_count; i++) + { + _M_ppNodes[i] = _concrt_new _OrderNode(_PAgents[i]->status_port(), i, _PTarget, _IsDone); + } + } + + // Destroys the block + ~_OrderBlock() + { + for (size_t i = 0; i < _M_count; i++) + { + delete _M_ppNodes[i]; + } + + delete [] _M_ppNodes; + } + + // Retrieve the agent status for the agent at the given index + agent_status _Status(size_t _Index) + { + _CONCRT_ASSERT(_M_ppNodes[_Index]->has_value()); + + return _M_ppNodes[_Index]->value(); + } + +private: + + // Number of order nodes + size_t _M_count; + + // Array of order nodes + _OrderNode ** _M_ppNodes; +}; + + +/// <summary> +/// Wait for an agent to complete its task. A task is completed when it enters the agent_canceled, +/// or agent_done states. +/// </summary> +agent_status agent::wait(agent * pAgent, unsigned int timeout) +{ + if(pAgent == NULL) + { + throw std::invalid_argument("pAgent"); + } + + return receive<agent_status>(pAgent->status_port(), _IsDone, timeout); +} + +/// <summary> +/// Wait for all agents in a given Agent array to complete their tasks. A task is completed +/// when it enters the agent_canceled or agent_done states. +/// </summary> +void agent::wait_for_all(size_t count, agent ** pAgents, agent_status * pStatus, unsigned int timeout) +{ + if ( pAgents == NULL ) + { + throw std::invalid_argument("pAgents"); + } + + for (size_t i = 0; i < count; i++) + { + if ( pAgents[i] == NULL ) + { + throw std::invalid_argument("pAgents"); + } + } + + // Create the following network + // + // agent - orderNode - + // \ + // agent - orderNode - --call ~~~ single_assignment + // / + // agent - orderNode - + + single_assignment<size_t> _Sa; + volatile size_t _CompletedAgents = 0; + call<size_t> _Call([&](size_t const& _Index) + { + // Safe to access without synchronization since call blocks + // guarantee that the function is not called for multiple + // messages at the same time. + _CONCRT_ASSERT(_CompletedAgents < count); + if (++_CompletedAgents == count) + { + // All the agents have completed. Indicate the same by sending a message + // (initialize) to the single assignment. + send<size_t>(_Sa, 1); + } + }); + + _OrderBlock<_Greedy_node<agent_status>> _OrderedAgents(count, pAgents, &_Call); + + receive(&_Sa, timeout); + + // single_assignment has a message => all agents completed + // Retrieve their status messages. + if(pStatus != NULL) + { + for (size_t i = 0; i < count; i++) + { + pStatus[i] = _OrderedAgents._Status(i); + } + } +} + +/// <summary> +/// Wait for any one of the agents in a given AgentTask array to complete its task. A task is completed +/// when it enters the agent_canceled or agent_done states. +/// </summary> +void agent::wait_for_one(size_t count, agent ** pAgents, agent_status &status, size_t& index, unsigned int timeout) +{ + if ( pAgents == NULL ) + { + throw std::invalid_argument("pAgents"); + } + + for (size_t i = 0; i < count; i++) + { + if ( pAgents[i] == NULL ) + { + throw std::invalid_argument("pAgents"); + } + } + + // Create the following network + // + // agent - orderNode - + // \ + // agent - orderNode - --single_assignment + // / + // agent - orderNode - + + single_assignment<size_t> _Sa; + _OrderBlock<_Greedy_node<agent_status>> _OrderedAgents(count, pAgents, &_Sa); + + index = receive(&_Sa, timeout); + + // We were able to receive the index. Get the message (agent_status) + status = _OrderedAgents._Status(index); +} + +// A static wrapper function that calls the Run() method. Used for scheduling of the task +void agent::_Agent_task_wrapper(void* data) +{ + agent *pAgent = (agent *) data; + + if(InterlockedCompareExchange(&pAgent->_M_fCancelable, FALSE, TRUE) == TRUE) + { + send<agent_status> (pAgent->_M_status, agent_started); + + // Invoke the run() function of the agent. + _Trace_agents(AGENTS_EVENT_START, details::_Trace_agents_get_id(pAgent)); + pAgent->run(); + _Trace_agents(AGENTS_EVENT_END, details::_Trace_agents_get_id(pAgent), 0); + } + else + { + // This else path can be entered only if an agent was canceled before it + // ran. Send a agent_canceled message to the status. + send<agent_status> (pAgent->_M_status, agent_canceled); + } +} + +// Implementation of agent APIs that should not be publicly exposed + +namespace details +{ + static volatile runtime_object_identity s_RuntimeObjectIdentity = 0; + + _CRTIMP2 _Runtime_object::_Runtime_object() + { + // Increment the id by 2. This is done because certain blocks (like join) need to have + // a special message id to indicate a NULL id. In this case, we use -1. Incrementing by 2 + // will avoid any wrap-around issues causing us to hit -1. + runtime_object_identity id = InterlockedExchangeAdd((volatile long *) &s_RuntimeObjectIdentity, 2); + _CONCRT_ASSERT(id != -1); + _M_id = id; + } + + _CRTIMP2 _Runtime_object::_Runtime_object(runtime_object_identity _Id) : _M_id(_Id) + { + } +} // namespace details +} // namespace Concurrency diff --git a/test_data/lots_of_files/Base64.cpp b/test_data/lots_of_files/Base64.cpp new file mode 100644 index 0000000..0074860 --- /dev/null +++ b/test_data/lots_of_files/Base64.cpp @@ -0,0 +1,219 @@ +#include "Base64.h" + +#include <cmath> +#include <cstring> +#include <string> + +/** + * characters used for Base64 encoding + */ +const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +/** + * encode three bytes using base64 (RFC 3548) + * + * @param triple three bytes that should be encoded + * @param result buffer of four characters where the result is stored + */ +void _base64_encode_triple(unsigned char triple[3], char result[4]) +{ + int tripleValue, i; + + tripleValue = triple[0]; + tripleValue *= 256; + tripleValue += triple[1]; + tripleValue *= 256; + tripleValue += triple[2]; + + for (i=0; i<4; i++) + { + result[3-i] = BASE64_CHARS[tripleValue%64]; + tripleValue /= 64; + } +} + +/** + * encode an array of bytes using Base64 (RFC 3548) + * + * @param source the source buffer + * @param sourcelen the length of the source buffer + * @param target the target buffer + * @param targetlen the length of the target buffer + * @return 1 on success, 0 otherwise + */ +int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen) +{ + /* check if the result will fit in the target buffer */ + if ((sourcelen+2)/3*4 > targetlen-1) + return 0; + + /* encode all full triples */ + while (sourcelen >= 3) + { + _base64_encode_triple(source, target); + sourcelen -= 3; + source += 3; + target += 4; + } + + /* encode the last one or two characters */ + if (sourcelen > 0) + { + unsigned char temp[3]; + memset(temp, 0, sizeof(temp)); + memcpy(temp, source, sourcelen); + _base64_encode_triple(temp, target); + target[3] = '='; + if (sourcelen == 1) + target[2] = '='; + + target += 4; + } + + /* terminate the string */ + target[0] = 0; + + return 1; +} + +/** + * determine the value of a base64 encoding character + * + * @param base64char the character of which the value is searched + * @return the value in case of success (0-63), -1 on failure + */ +int _base64_char_value(char base64char) +{ + if (base64char >= 'A' && base64char <= 'Z') + return base64char-'A'; + if (base64char >= 'a' && base64char <= 'z') + return base64char-'a'+26; + if (base64char >= '0' && base64char <= '9') + return base64char-'0'+2*26; + if (base64char == '-') + return 2*26+10; + if (base64char == '_') + return 2*26+11; + return -1; +} + +/** + * decode a 4 char base64 encoded byte triple + * + * @param quadruple the 4 characters that should be decoded + * @param result the decoded data + * @return length of the result (1, 2 or 3), 0 on failure + */ +int _base64_decode_triple(char quadruple[4], unsigned char *result) +{ + int i, triple_value, bytes_to_decode = 3, only_equals_yet = 1; + int char_value[4]; + + for (i=0; i<4; i++) + char_value[i] = _base64_char_value(quadruple[i]); + + /* check if the characters are valid */ + for (i=3; i>=0; i--) + { + if (char_value[i]<0) + { + if (only_equals_yet && quadruple[i]=='=') + { + /* we will ignore this character anyway, make it something + * that does not break our calculations */ + char_value[i]=0; + bytes_to_decode--; + continue; + } + return 0; + } + /* after we got a real character, no other '=' are allowed anymore */ + only_equals_yet = 0; + } + + /* if we got "====" as input, bytes_to_decode is -1 */ + if (bytes_to_decode < 0) + bytes_to_decode = 0; + + /* make one big value out of the partial values */ + triple_value = char_value[0]; + triple_value *= 64; + triple_value += char_value[1]; + triple_value *= 64; + triple_value += char_value[2]; + triple_value *= 64; + triple_value += char_value[3]; + + /* break the big value into bytes */ + for (i=bytes_to_decode; i<3; i++) + triple_value /= 256; + for (i=bytes_to_decode-1; i>=0; i--) + { + result[i] = triple_value%256; + triple_value /= 256; + } + + return bytes_to_decode; +} + +/** + * decode base64 encoded data + * + * @param source the encoded data (zero terminated) + * @param target pointer to the target buffer + * @param targetlen length of the target buffer + * @return length of converted data on success, -1 otherwise + */ +size_t base64_decode(const char *source, unsigned char *target, size_t targetlen) +{ + char *src, *tmpptr; + char quadruple[4]; + unsigned char tmpresult[3]; + int i; + size_t tmplen = 3; + size_t converted = 0; + + /* concatenate '===' to the source to handle unpadded base64 data */ + src = (char *)malloc(strlen(source)+5); + if (src == NULL) + return -1; + strcpy(src, source); + strcat(src, "===="); + tmpptr = src; + + /* convert as long as we get a full result */ + while (tmplen == 3) + { + /* get 4 characters to convert */ + for (i=0; i<4; i++) + { + /* skip invalid characters - we won't reach the end */ + while (*tmpptr != '=' && _base64_char_value(*tmpptr)<0) + tmpptr++; + + quadruple[i] = *(tmpptr++); + } + + /* convert the characters */ + tmplen = _base64_decode_triple(quadruple, tmpresult); + + /* check if the fit in the result buffer */ + if (targetlen < tmplen) + { + free(src); + return -1; + } + + /* put the partial result in the result buffer */ + memcpy(target, tmpresult, tmplen); + target += tmplen; + targetlen -= tmplen; + converted += tmplen; + } + + free(src); + return converted; +} + +/** get memory consumption */ +size_t base64_needed(const size_t &iSize) {return 4*size_t(floorf((float(iSize)+2.0f)/3.0f))+1;} \ No newline at end of file diff --git a/test_data/lots_of_files/Base64.h b/test_data/lots_of_files/Base64.h new file mode 100644 index 0000000..7aac130 --- /dev/null +++ b/test_data/lots_of_files/Base64.h @@ -0,0 +1,62 @@ +#pragma once +#ifndef _BASE64_H_ +#define _BASE64_H_ + + +#define _HAS_EXCEPTIONS (0) +#define _CRT_SECURE_NO_WARNINGS + +#include <cstdlib> + + +/** + * encode three bytes using base64 (RFC 3548) + * + * @param triple three bytes that should be encoded + * @param result buffer of four characters where the result is stored + */ +void _base64_encode_triple(unsigned char triple[3], char result[4]); + +/** + * encode an array of bytes using Base64 (RFC 3548) + * + * @param source the source buffer + * @param sourcelen the length of the source buffer + * @param target the target buffer + * @param targetlen the length of the target buffer + * @return 1 on success, 0 otherwise + */ +int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen); + +/** + * determine the value of a base64 encoding character + * + * @param base64char the character of which the value is searched + * @return the value in case of success (0-63), -1 on failure + */ +int _base64_char_value(char base64char); + +/** + * decode a 4 char base64 encoded byte triple + * + * @param quadruple the 4 characters that should be decoded + * @param result the decoded data + * @return length of the result (1, 2 or 3), 0 on failure + */ +int _base64_decode_triple(char quadruple[4], unsigned char *result); + +/** + * decode base64 encoded data + * + * @param source the encoded data (zero terminated) + * @param target pointer to the target buffer + * @param targetlen length of the target buffer + * @return length of converted data on success, -1 otherwise + */ +size_t base64_decode(const char *source, unsigned char *target, size_t targetlen); + +/** get memory consumption */ +size_t base64_needed(const size_t &iSize); + + +#endif \ No newline at end of file diff --git a/test_data/lots_of_files/CacheLocalScheduleGroup.cpp b/test_data/lots_of_files/CacheLocalScheduleGroup.cpp new file mode 100644 index 0000000..f0e65cd --- /dev/null +++ b/test_data/lots_of_files/CacheLocalScheduleGroup.cpp @@ -0,0 +1,104 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// CacheLocalScheduleGroup.cpp +// +// Implementation file for CacheLocalScheduleGroup. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +#include "concrtinternal.h" + +namespace Concurrency +{ +namespace details +{ + /// <summary> + /// Puts a runnable context into the runnables collection in the schedule group. + /// </summary> + void CacheLocalScheduleGroupSegment::AddToRunnablesCollection(InternalContextBase* pContext) + { + m_runnableContexts.Enqueue(pContext); + } + + /// <summary> + /// Places a chore in the mailbox associated with this schedule group segment. + /// </summary> + /// <param name="pChore"> + /// The chore to mail. + /// </param> + /// <returns> + /// The mailbox slot into which the chore was placed. + /// </returns> + /// <remarks> + /// A mailed chore should also be placed on its regular work stealing queue. The mailing must come first and once mailed, the chore body + /// cannot be referenced until the slot is successfully claimed via a call to the ClaimSlot method. + /// </remarks> + Mailbox<_UnrealizedChore>::Slot CacheLocalScheduleGroupSegment::MailChore(_UnrealizedChore *pChore) + { + // + // There are two possible segments to which pChore can be accounted. One is the segment where it will appear on the WSQ -- the other is + // the segment where it will appear on the mailbox. Both are in the same group and hence we do not at present have reference counting + // issues. It will be attributed to the group it was picked up from which will further honor that affinity if the task blocks, etc... + // + ASSERT(!m_affinity._Is_system()); + Mailbox<_UnrealizedChore>::Slot affinitySlot = m_mailedTasks.Enqueue(pChore); + +#if _UMSTRACE && _CHORETRACK + OMTRACE(MTRACE_EVT_CHOREMAILED, this, SchedulerBase::FastCurrentContext(), NULL, pChore); + OMTRACE(MTRACE_EVT_CHOREMAILED, m_pOwningGroup, SchedulerBase::FastCurrentContext(), NULL, pChore); +#endif // _UMSTRACE && _CHORETRACK + + ASSERT(!affinitySlot.IsEmpty()); + return affinitySlot; + } + + /// <summary> + /// Notifies virtual processors that work affinitized to them has become available in the schedule group segment. + /// </summary> + void CacheLocalScheduleGroupSegment::NotifyAffinitizedWork() + { + SchedulerBase *pScheduler = m_pOwningGroup->GetScheduler(); + pScheduler->PostAffinityMessage(m_affinitySet); + + // + // If this item qualifies for the quick cache, stash it. + // + if (m_affinity._GetType() == location::_ExecutionResource) + { + pScheduler->SetQuickCacheSlot(m_maskIdIf, this); + } + } + + /// <summary> + /// Places a chore in a mailbox associated with the schedule group which is biased towards tasks being picked up from the specified + /// location. + /// </summary> + /// <param name="pChore"> + /// The chore to mail. + /// </param> + /// <param name="pPlacement"> + /// A pointer to a location where the chore will be mailed. + /// </param> + /// <returns> + /// The mailbox slot into which the chore was placed. + /// </returns> + /// <remarks> + /// A mailed chore should also be placed on its regular work stealing queue. The mailing must come first and once mailed, the chore body + /// cannot be referenced until the slot is successfully claimed via a call to the ClaimSlot method. + /// </remarks> + Mailbox<_UnrealizedChore>::Slot CacheLocalScheduleGroup::MailChore(_UnrealizedChore * pChore, + location * pPlacement, + ScheduleGroupSegmentBase ** ppDestinationSegment) + { + CacheLocalScheduleGroupSegment * pCacheLocalSegment = static_cast<CacheLocalScheduleGroupSegment *>(LocateSegment(pPlacement, true)); + *ppDestinationSegment = pCacheLocalSegment; + return pCacheLocalSegment->MailChore(pChore); + } + +} // namespace details +} // namespace Concurrency diff --git a/test_data/lots_of_files/CacheLocalScheduleGroup.h b/test_data/lots_of_files/CacheLocalScheduleGroup.h new file mode 100644 index 0000000..f6fc89e --- /dev/null +++ b/test_data/lots_of_files/CacheLocalScheduleGroup.h @@ -0,0 +1,159 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// CacheLocalScheduleGroup.h +// +// Header file containing CacheLocalScheduleGroup related declarations. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +#pragma once + +namespace Concurrency +{ +namespace details +{ + + class CacheLocalScheduleGroup; + + class CacheLocalScheduleGroupSegment : public ScheduleGroupSegmentBase + { + + public: + + // + // Public Methods + // + + /// <summary> + /// Constructs a cache local schedule group segment + /// </summary> + CacheLocalScheduleGroupSegment(ScheduleGroupBase *pOwningGroup, SchedulingRing *pOwningRing, location* pSegmentAffinity) : + ScheduleGroupSegmentBase(pOwningGroup, pOwningRing, pSegmentAffinity) + { + } + + /// <summary> + /// Places a chore in the mailbox associated with this schedule group segment. + /// </summary> + /// <param name="pChore"> + /// The chore to mail. + /// </param> + /// <returns> + /// The mailbox slot into which the chore was placed. + /// </returns> + /// <remarks> + /// A mailed chore should also be placed on its regular work stealing queue. The mailing must come first and once mailed, the chore body + /// cannot be referenced until the slot is successfully claimed via a call to the ClaimSlot method. + /// </remarks> + Mailbox<_UnrealizedChore>::Slot MailChore(_UnrealizedChore *pChore); + + /// <summary> + /// Notifies virtual processors that work affinitized to them has become available in the schedule group segment. + /// </summary> + virtual void NotifyAffinitizedWork(); + + protected: + + + private: + friend class SchedulerBase; + friend class CacheLocalScheduleGroup; + friend class ContextBase; + friend class ExternalContextBase; + friend class InternalContextBase; + friend class ThreadInternalContext; + friend class SchedulingNode; + friend class SchedulingRing; + friend class VirtualProcessor; + + // + // Private data + // + + // Each schedule group has three stores of work. It has a collection of runnable contexts, + // a FIFO queue of realized chores and a list of workqueues that hold unrealized chores. + + // A collection of Runnable contexts. + SafeSQueue<InternalContextBase, _HyperNonReentrantLock> m_runnableContexts; + + // + // Private methods + // + + /// <summary> + /// Puts a runnable context into the runnables collection in the schedule group. + /// </summary> + void AddToRunnablesCollection(InternalContextBase *pContext); + + InternalContextBase *GetRunnableContext() + { + if (m_runnableContexts.Empty()) + return NULL; + + InternalContextBase *pContext = m_runnableContexts.Dequeue(); +#if defined(_DEBUG) + SetContextDebugBits(pContext, CTX_DEBUGBIT_REMOVEDFROMRUNNABLES); +#endif // _DEBUG + return pContext; + } + }; + + class CacheLocalScheduleGroup : public ScheduleGroupBase + { + public: + + /// <summary> + /// Constructs a new cache local schedule group. + /// </summary> + CacheLocalScheduleGroup(SchedulerBase *pScheduler, location* pGroupPlacement) : + ScheduleGroupBase(pScheduler, pGroupPlacement) + { + m_kind = CacheLocalScheduling; + } + + /// <summary> + /// Places a chore in a mailbox associated with the schedule group which is biased towards tasks being picked up from the specified + /// location. + /// </summary> + /// <param name="pChore"> + /// The chore to mail. + /// </param> + /// <param name="pPlacement"> + /// A pointer to a location where the chore will be mailed. + /// </param> + /// <returns> + /// The mailbox slot into which the chore was placed. + /// </returns> + /// <remarks> + /// A mailed chore should also be placed on its regular work stealing queue. The mailing must come first and once mailed, the chore body + /// cannot be referenced until the slot is successfully claimed via a call to the ClaimSlot method. + /// </remarks> + virtual Mailbox<_UnrealizedChore>::Slot MailChore(_UnrealizedChore * pChore, + location * pPlacement, + ScheduleGroupSegmentBase ** ppDestinationSegment); + protected: + + /// <summary> + /// Allocates a new cache local schedule group segment within the specified group and ring with the specified affinity. + /// </summary> + /// <param name="pSegmentAffinity"> + /// The affinity for the segment. + /// </param> + /// <param name="pOwningRing"> + /// The scheduling ring to which the newly allocated segment will belong. + /// </param> + /// <returns> + /// A new cache local schedule group within the specified group and ring with the specified affinity. + /// </returns> + virtual ScheduleGroupSegmentBase* AllocateSegment(SchedulingRing *pOwningRing, location* pSegmentAffinity) + { + return _concrt_new CacheLocalScheduleGroupSegment(this, pOwningRing, pSegmentAffinity); + } + }; +} // namespace details +} // namespace Concurrency diff --git a/test_data/lots_of_files/ConcurrencySal.h b/test_data/lots_of_files/ConcurrencySal.h new file mode 100644 index 0000000..a552040 --- /dev/null +++ b/test_data/lots_of_files/ConcurrencySal.h @@ -0,0 +1,357 @@ +/*** +*concurrencysal.h - markers for documenting the concurrent semantics of APIs +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains macros for Concurrency SAL annotations. Definitions +* starting with _Internal are low level macros that are subject to change. +* Users should not use those low level macros directly. +* [ANSI] +* +* [Public] +* +****/ + +#ifndef CONCURRENCYSAL_H +#define CONCURRENCYSAL_H + +#pragma once + +#ifdef __cplusplus // [ +extern "C" { +#endif // ] + +#if !defined(__midl) && defined(_PREFAST_) && _MSC_VER >= 1000 && !defined(_SDV_) + +__ANNOTATION(SAL_guarded_by(__deferTypecheck void *)); +__ANNOTATION(SAL_write_guarded_by(__deferTypecheck void *)); +__ANNOTATION(SAL_requires_lock_held(__deferTypecheck void *)); +__ANNOTATION(SAL_requires_exclusive_lock_held(__deferTypecheck void *)); +__ANNOTATION(SAL_requires_shared_lock_held(__deferTypecheck void *)); +__ANNOTATION(SAL_requires_lock_not_held(__deferTypecheck void *)); +__ANNOTATION(SAL_requires_no_locks_held(void)); +__ANNOTATION(SAL_set_lock_count_to_zero(__deferTypecheck void *)); +__ANNOTATION(SAL_set_lock_count_to_one(__deferTypecheck void *)); +__ANNOTATION(SAL_acquires_lock(__deferTypecheck void *)); +__ANNOTATION(SAL_acquires_exclusive_lock(__deferTypecheck void *)); +__ANNOTATION(SAL_acquires_shared_lock(__deferTypecheck void *)); +__ANNOTATION(SAL_releases_lock(__deferTypecheck void *)); +__ANNOTATION(SAL_releases_exclusive_lock(__deferTypecheck void *)); +__ANNOTATION(SAL_releases_shared_lock(__deferTypecheck void *)); +__ANNOTATION(SAL_ignore_lock_match(__deferTypecheck void *)); +__ANNOTATION(SAL_has_lock_property(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_has_lock_level(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_lock_level_order(__deferTypecheck void *, __deferTypecheck void *)); +__ANNOTATION(SAL_no_competing_thread(void)); +__ANNOTATION(SAL_set_same_lock(__deferTypecheck void *, __deferTypecheck void *)); + +/* + * pre-defined global system locks + */ +extern int _Global_interlock_; +extern int _Global_cancel_spin_lock_; +extern int _Global_critical_region_; + +/* + * Annotation identifiers + */ +#define _Internal_create_CSAL_identifier_(id) const char id##[] = ""; + +_Internal_create_CSAL_identifier_(_Lock_kind_mutex_) +_Internal_create_CSAL_identifier_(_Lock_kind_event_) +_Internal_create_CSAL_identifier_(_Lock_kind_semaphore_) +_Internal_create_CSAL_identifier_(_Lock_kind_spin_lock_) +_Internal_create_CSAL_identifier_(_Lock_kind_critical_section_) + +/* + * data protection + */ +#define _Guarded_by_(lock) _SAL2_Source_(_Guarded_by_, (lock), _SA_annotes1(SAL_guarded_by,lock)) +#define _Write_guarded_by_(lock) _SAL2_Source_(_Write_guarded_by_, (lock), _SA_annotes1(SAL_write_guarded_by,lock)) +#define _Interlocked_ _Guarded_by_(_Global_interlock_) + +/* + * interlocked operand used in interlocked instructions + */ +#ifndef _Interlocked_operand_ +#define _Interlocked_operand_ _SAL2_Source_(_Interlocked_operand_, (), _Pre_ _SA_annotes0(SAL_interlocked)) +#endif + +/* + * caller/callee locking contracts + */ +#define _Requires_lock_held_(lock) _SAL2_Source_(_Requires_lock_held_, (lock), _Pre_ _SA_annotes1(SAL_requires_lock_held,lock)) +#define _Requires_exclusive_lock_held_(lock) _SAL2_Source_(_Requires_exclusive_lock_held_, (lock), _Pre_ _SA_annotes1(SAL_requires_exclusive_lock_held,lock)) +#define _Requires_shared_lock_held_(lock) _SAL2_Source_(_Requires_shared_lock_held_, (lock), _Pre_ _SA_annotes1(SAL_requires_shared_lock_held,lock)) + +#define _Requires_lock_not_held_(lock) _SAL2_Source_(_Requires_lock_not_held_, (lock), _Pre_ _SA_annotes1(SAL_requires_lock_not_held,lock)) +#define _Requires_no_locks_held_ _SAL2_Source_(_Requires_no_locks_held_, (), _Pre_ _SA_annotes0(SAL_requires_no_locks_held)) + +/* + * acquire/release locking side effects + */ +#define _Acquires_lock_(lock) _SAL2_Source_(_Acquires_lock_, (lock), _Post_ _SA_annotes1(SAL_acquires_lock,lock)) +#define _Acquires_exclusive_lock_(lock) _SAL2_Source_(_Acquires_exclusive_lock_, (lock), _Post_ _SA_annotes1(SAL_acquires_exclusive_lock,lock)) +#define _Acquires_shared_lock_(lock) _SAL2_Source_(_Acquires_shared_lock_, (lock), _Post_ _SA_annotes1(SAL_acquires_shared_lock,lock)) + +#define _Releases_lock_(lock) _SAL2_Source_(_Releases_lock_, (lock), _Post_ _SA_annotes1(SAL_releases_lock,lock)) +#define _Releases_exclusive_lock_(lock) _SAL2_Source_(_Releases_exclusive_lock_, (lock), _Post_ _SA_annotes1(SAL_releases_exclusive_lock,lock)) +#define _Releases_shared_lock_(lock) _SAL2_Source_(_Releases_shared_lock_, (lock), _Post_ _SA_annotes1(SAL_releases_shared_lock,lock)) + +/* + * acquire/release locking side effects for non-reentrant locks + */ +#define _Acquires_nonreentrant_lock_(lock) \ + _SAL2_Source_(_Acquires_nonreentrant_lock_, (lock), \ + _Requires_lock_not_held_(lock) \ + _Acquires_lock_(lock)) + +#define _Releases_nonreentrant_lock_(lock) \ + _SAL2_Source_(_Releases_nonreentrant_lock_, (lock), \ + _Requires_lock_held_(lock) \ + _Releases_lock_(lock)) + +#define _Post_same_lock_(a,b) _SAL2_Source_(_Post_same_lock_, (a,b), _Post_ _SA_annotes2(SAL_set_same_lock,a,b)) + +/* + * lock level + */ +#define _Create_lock_level_(level) _Internal_create_CSAL_identifier_(level) + +#define _Has_lock_level_(level) _SAL2_Source_(_Has_lock_level_, (level), _SA_annotes1(SAL_has_lock_level,#level)) + +#define _Internal_lock_level_order_(a,b) _SAL2_Source_(_Internal_lock_level_order_, (a,b), _SA_annotes2(SAL_lock_level_order,a,b)) +#define _Csalcat1_(x,y) x##y +#define _Csalcat2_(x,y) _Csalcat1_(x,y) + +#define _Lock_level_order_(a,b) \ + extern _Internal_lock_level_order_(a,b) void _Sal_order_##a##_##b(_In_z_ char*a, _In_z_ char*b); \ + static __inline void CSALCAT2(CSAL_LockOrder,__COUNTER__)(void){_Sal_order_##a##_##b(#a,#b);} + +/* + * threading context + */ +#define _No_competing_thread_ _SAL2_Source_(_No_competing_thread_, (), _Pre_ _SA_annotes0(SAL_no_competing_thread)) + +/* + * refinement and suppression + */ +extern _Acquires_lock_(*plock) void _Internal_acquires_lock_(void* plock); +extern _Releases_lock_(*plock) void _Internal_releases_lock_(void* plock); + +#define _Internal_set_lock_count_to_zero_(lock) _SAL2_Source_(Internal_set_lock_count_to_zero_, (lock), _Post_ _SA_annotes1(SAL_set_lock_count_to_zero,lock)) +#define _Internal_set_lock_count_to_one_(lock) _SAL2_Source_(_Internal_set_lock_count_to_one_, (lock), _Post_ _SA_annotes1(SAL_set_lock_count_to_one,lock)) + +extern _Internal_set_lock_count_to_one_(*plock) void _Internal_lock_held_(void* plock); +extern _Internal_set_lock_count_to_zero_(*plock) void _Internal_lock_not_held_(void* plock); +extern _Post_same_lock_(*plock1, *plock2) void _Internal_same_lock_(void* plock1, void* plock2); + +#define _Analysis_assume_lock_acquired_(lock) _Internal_acquires_lock_((void*)(&(lock))) +#define _Analysis_assume_lock_released_(lock) _Internal_releases_lock_((void*)(&(lock))) + +#define _Analysis_assume_lock_held_(lock) _Internal_lock_held_((void*)(&(lock))) +#define _Analysis_assume_lock_not_held_(lock) _Internal_lock_not_held_((void*)(&(lock))) +#define _Analysis_assume_same_lock_(lock1, lock2) _Internal_same_lock_((void*)(&(lock1)), (void*)(&(lock2))) + +/* + * _Function_ignore_lock_checking_ may be deprecated in future versions of SAL + */ +#define _Function_ignore_lock_checking_(lock) _SAL2_Source_(_Function_ignore_lock_checking_, (lock), _Pre_ _SA_annotes1(SAL_ignore_lock_match,lock)) +extern _Function_ignore_lock_checking_(*plock) void _Internal_suppress_lock_checking_(void* plock); + +/* + * _Analysis_suppress_lock_checking_ may be deprecated in future versions of SAL + */ +#define _Analysis_suppress_lock_checking_(lock) _Internal_suppress_lock_checking_((void*)(&(lock))); + +#define _Benign_race_begin_ __pragma(warning(push)) __pragma(warning(disable:26100 26101 26150 26130 26180 26131 26181 28112)) +#define _Benign_race_end_ __pragma(warning(pop)) + +#define _No_competing_thread_begin_ __pragma(warning(push)) __pragma(warning(disable:26100 26101 26150 26101 26151 26110 26160 26130 26180 26131 26181 28112)) +#define _No_competing_thread_end_ __pragma(warning(pop)) + +/* + * lock kinds + */ +#define _Has_lock_kind_(kind) _SAL2_Source_(_Has_lock_kind_, (kind), _SA_annotes1(SAL_has_lock_property,#kind)) + + +/* + * Old spelling + * Note: the old version may be deprecated in the future!!! + */ +extern int __system_interlock; +extern int __system_cancel_spinlock; +extern int __system_critical_region; + +#define __guarded_by(lock) _SAL1_1_Source_(__guarded_by, (lock), _SA_annotes1(SAL_guarded_by,lock)) +#define __write_guarded_by(lock) _SAL1_1_Source_(__write_guarded_by, (lock), _SA_annotes1(SAL_write_guarded_by,lock)) +#define __interlocked __guarded_by(_Global_interlock_) + +/* + * caller/callee locking contracts + */ +#define __requires_lock_held(lock) _SAL1_1_Source_(__requires_lock_held, (lock), __pre _SA_annotes1(SAL_requires_lock_held,lock)) +#define __requires_exclusive_lock_held(lock) _SAL1_1_Source_(__requires_exclusive_lock_held, (lock), __pre _SA_annotes1(SAL_requires_exclusive_lock_held,lock)) +#define __requires_shared_lock_held(lock) _SAL1_1_Source_(__requires_shared_lock_held, (lock), __pre _SA_annotes1(SAL_requires_shared_lock_held,lock)) + +#define __requires_lock_not_held(lock) _SAL1_1_Source_(__requires_lock_not_held, (lock), __pre _SA_annotes1(SAL_requires_lock_not_held,lock)) +#define __requires_no_locks_held _SAL1_1_Source_(__requires_no_locks_held, (), __pre _SA_annotes0(SAL_requires_no_locks_held)) + +/* + * acquire/release locking side effects + */ +#define __acquires_lock(lock) _SAL1_1_Source_(__acquires_lock, (lock), __post _SA_annotes1(SAL_acquires_lock,lock)) +#define __acquires_exclusive_lock(lock) _SAL1_1_Source_(__acquires_exclusive_lock, (lock), __post _SA_annotes1(SAL_acquires_exclusive_lock,lock)) +#define __acquires_shared_lock(lock) _SAL1_1_Source_(__acquires_shared_lock, (lock), __post _SA_annotes1(SAL_acquires_shared_lock,lock)) + +#define __releases_lock(lock) _SAL1_1_Source_(__releases_lock, (lock), __post _SA_annotes1(SAL_releases_lock,lock)) +#define __releases_exclusive_lock(lock) _SAL1_1_Source_(__releases_exclusive_lock, (lock),__post _SA_annotes1(SAL_releases_exclusive_lock,lock)) +#define __releases_shared_lock(lock) _SAL1_1_Source_(__releases_shared_lock, (lock), __post _SA_annotes1(SAL_releases_shared_lock,lock)) + +/* + * lock properties + * The following kind options are supported: + * __has_lock_property(MUTEX) + * __has_lock_property(EVENT) + * __has_lock_property(SEMAPHORE) + * __has_lock_property(OTHER_HANDLE) + * __has_lock_property(REENTRANT) + * __has_lock_property(NON_REENTRANT) + */ +#define __has_lock_property(kind) _SAL1_1_Source_(__has_lock_property, (kind), _SA_annotes1(SAL_has_lock_property,#kind)) + +/* + * lock level + */ +#define __declare_lock_level(level) _Internal_create_CSAL_identifier_(level) +#define __has_lock_level(level) _SAL1_1_Source_(__has_lock_level, (level), _SA_annotes1(SAL_has_lock_level,#level)) + +#define __internal_lock_level_order(a,b) _SAL1_1_Source_(__internal_lock_level_order, (a,b), _SA_annotes2(SAL_lock_level_order,#a,#b)) +#define CSALCAT1(x,y) x##y +#define CSALCAT2(x,y) CSALCAT1(x,y) + +#define __lock_level_order(a,b) \ + extern __internal_lock_level_order(a,b) void __sal_order_##a##_##b(__in_z char*a, __in_z char*b); \ + static __inline void CSALCAT2(CSAL_LockOrder,__COUNTER__)(void){__sal_order_##a##_##b(#a,#b);} + +/* + * threading context + */ +#define __no_competing_thread _SAL1_1_Source_(__no_competing_thread, (), __pre _SA_annotes0(SAL_no_competing_thread)) + +/* + * refinement and suppression + */ +extern __acquires_lock(*plock) void __internal_acquires_lock(void* plock); +extern __releases_lock(*plock) void __internal_releases_lock(void* plock); + +#define __analysis_assume_lock_acquired(lock) __internal_acquires_lock((void*)(&(lock))) +#define __analysis_assume_lock_released(lock) __internal_releases_lock((void*)(&(lock))) + +#define __function_ignore_lock_checking(lock) _SAL1_1_Source_(__function_ignore_lock_cleanup, (lock), __pre _SA_annotes1(SAL_ignore_lock_match,lock)) +extern __function_ignore_lock_checking(*plock) void __internal_suppress_lock_checking(void* plock); + +#define __analysis_suppress_lock_checking(lock) __internal_suppress_lock_checking((void*)(&(lock))); + +#define BENIGN_RACE_BEGIN __pragma(warning(push)) __pragma(warning(disable:26100 26150 26130 26180 26131 26181)) +#define BENIGN_RACE_END __pragma(warning(pop)) + +#define NO_COMPETING_THREAD_BEGIN __pragma(warning(push)) __pragma(warning(disable:26100 26150 26101 26151 26110 26160 26130 26180 26131 26181)) +#define NO_COMPETING_THREAD_END __pragma(warning(pop)) + +#else + +#ifndef _Interlocked_operand_ +#define _Interlocked_operand_ +#endif + +#define _Guarded_by_(lock) +#define _Write_guarded_by_(lock) +#define _Interlocked_ +#define _Requires_lock_held_(lock) +#define _Requires_exclusive_lock_held_(lock) +#define _Requires_shared_lock_held_(lock) +#define _Requires_lock_not_held_(lock) +#define _Requires_no_locks_held_ +#define _Acquires_lock_(lock) +#define _Acquires_exclusive_lock_(lock) +#define _Acquires_shared_lock_(lock) +#define _Releases_lock_(lock) +#define _Releases_exclusive_lock_(lock) +#define _Releases_shared_lock_(lock) +#define _Acquires_nonreentrant_lock_(lock) +#define _Releases_nonreentrant_lock_(lock) + +#define _Post_same_lock_(lock1,lock2) + +#define _Internal_set_lock_count_(lock, count) + +#define _Create_lock_level_(level) +#define _Has_lock_level_(level) +#define _Internal_lock_level_order_(a,b) +#define _Csalcat1_(x,y) +#define _Csalcat2_(x,y) +#define _Lock_level_order_(a,b) +#define _No_competing_thread_ +#define _Analysis_assume_lock_acquired_(lock) +#define _Analysis_assume_lock_released_(lock) +#define _Analysis_assume_lock_held_(lock) +#define _Analysis_assume_lock_not_held_(lock) +#define _Analysis_assume_same_lock_(lock1, lock2) +#define _Function_ignore_lock_checking_(lock) +#define _Analysis_suppress_lock_checking_(lock) + +#define _Benign_race_begin_ __pragma(warning(push)) +#define _Benign_race_end_ __pragma(warning(pop)) + +#define _No_competing_thread_begin_ __pragma(warning(push)) +#define _No_competing_thread_end_ __pragma(warning(pop)) + +#define _Has_lock_kind_(kind) + +/* + * Old spelling: will be deprecated + */ +#define __guarded_by(lock) +#define __write_guarded_by(lock) +#define __interlocked +#define __requires_lock_held(lock) +#define __requires_exclusive_lock_held(lock) +#define __requires_shared_lock_held(lock) +#define __requires_lock_not_held(lock) +#define __requires_no_locks_held +#define __acquires_lock(lock) +#define __acquires_exclusive_lock(lock) +#define __acquires_shared_lock(lock) +#define __releases_lock(lock) +#define __releases_exclusive_lock(lock) +#define __releases_shared_lock(lock) + +#define __has_lock_property(kind) +#define __declare_lock_level(level) +#define __has_lock_level(level) +#define __internal_lock_level_order(a,b) +#define CSALCAT1(x,y) +#define CSALCAT2(x,y) +#define __lock_level_order(a,b) +#define __no_competing_thread +#define __analysis_assume_lock_acquired(lock) +#define __analysis_assume_lock_released(lock) +#define __function_ignore_lock_checking(lock) +#define __analysis_suppress_lock_checking(lock) + +#define BENIGN_RACE_BEGIN __pragma(warning(push)) +#define BENIGN_RACE_END __pragma(warning(pop)) + +#define NO_COMPETING_THREAD_BEGIN __pragma(warning(push)) +#define NO_COMPETING_THREAD_END __pragma(warning(pop)) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif // CONCURRENCYSAL_H diff --git a/test_data/lots_of_files/MD5.cpp b/test_data/lots_of_files/MD5.cpp new file mode 100644 index 0000000..290a055 --- /dev/null +++ b/test_data/lots_of_files/MD5.cpp @@ -0,0 +1,366 @@ +/* MD5 + converted to C++ class by Frank Thilo (thilo@unix-ag.org) + for bzflag (http://www.bzflag.org) + + based on: + + md5.h and md5.c + reference implementation of RFC 1321 + +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +/* interface header */ +#include "MD5.h" + +#include <cstdlib> +#include <cstdio> +#include <cmath> +#include <sstream> + +// Constants for MD5Transform routine. +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +/////////////////////////////////////////////// + +// F, G, H and I are basic MD5 functions. +inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { + return (x&y) | (~x&z); +} + +inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { + return (x&z) | (y&~z); +} + +inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { + return x^y^z; +} + +inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { + return y ^ (x | ~z); +} + +// rotate_left rotates x left n bits. +inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { + return (x << n) | (x >> (32-n)); +} + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. +inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; +} + +inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + G(b,c,d) + x + ac, s) + b; +} + +inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + H(b,c,d) + x + ac, s) + b; +} + +inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + I(b,c,d) + x + ac, s) + b; +} + +////////////////////////////////////////////// + +// default ctor, just initailize +MD5::MD5() +{ + init(); +} + +////////////////////////////////////////////// + +// nifty shortcut ctor, compute MD5 for string and finalize it right away +MD5::MD5(const std::string &text) +{ + init(); + update(text.c_str(), (MD5::size_type)text.length()); + finalize(); +} + +////////////////////////////// + +void MD5::init() +{ + finalized=false; + + for (int i = 0; i < blocksize; ++i) buffer[i] = 0; + for (int i = 0; i < 16; ++i) digest[i] = 0; + + count[0] = 0; + count[1] = 0; + + // load magic initialization constants. + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; +} + +////////////////////////////// + +// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. +void MD5::decode(uint4 output[], const uint1 input[], size_type len) +{ + for (unsigned int i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | + (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); +} + +////////////////////////////// + +// encodes input (uint4) into output (unsigned char). Assumes len is +// a multiple of 4. +void MD5::encode(uint1 output[], const uint4 input[], size_type len) +{ + for (size_type i = 0, j = 0; j < len; i++, j += 4) { + output[j] = input[i] & 0xff; + output[j+1] = (input[i] >> 8) & 0xff; + output[j+2] = (input[i] >> 16) & 0xff; + output[j+3] = (input[i] >> 24) & 0xff; + } +} + +////////////////////////////// + +// apply MD5 algo on a block +void MD5::transform(const uint1 block[blocksize]) +{ + uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + decode (x, block, blocksize); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + // Zeroize sensitive information. + memset(x, 0, sizeof x); +} + +////////////////////////////// + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block +void MD5::update(const unsigned char input[], size_type length) +{ + // compute number of bytes mod 64 + size_type index = count[0] / 8 % blocksize; + + // Update number of bits + if ((count[0] += (length << 3)) < (length << 3)) + count[1]++; + count[1] += (length >> 29); + + // number of bytes we need to fill in buffer + size_type firstpart = 64 - index; + + size_type i; + + // transform as many times as possible. + if (length >= firstpart) + { + // fill buffer first, transform + memcpy(&buffer[index], input, firstpart); + transform(buffer); + + // transform chunks of blocksize (64 bytes) + for (i = firstpart; i + blocksize <= length; i += blocksize) + transform(&input[i]); + + index = 0; + } + else + i = 0; + + // buffer remaining input + memcpy(&buffer[index], &input[i], length-i); +} + +////////////////////////////// + +// for convenience provide a verson with signed char +void MD5::update(const char input[], size_type length) +{ + update((const unsigned char*)input, length); +} + +////////////////////////////// + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. +MD5& MD5::finalize() +{ + if (!finalized) { + static unsigned char padding[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + // Save number of bits + unsigned char bits[8]; + encode(bits, count, 8); + + // pad out to 56 mod 64. + size_type index = count[0] / 8 % 64; + size_type padLen = (index < 56) ? (56 - index) : (120 - index); + update(padding, padLen); + + // Append length (before padding) + update(bits, 8); + + // Store state in digest + encode(digest, state, 16); + + // Zeroize sensitive information. + memset(buffer, 0, sizeof buffer); + memset(count, 0, sizeof count); + + finalized=true; + } + + return *this; +} + +////////////////////////////// + +// return hex representation of digest as string +std::string MD5::hexdigest() const +{ + if (!finalized) + return ""; + + char buf[33]; + for (int i=0; i<16; i++) + sprintf(buf+i*2, "%02x", digest[i]); + buf[32]=0; + + return std::string(buf); +} + +////////////////////////////// + +std::ostream& operator<<(std::ostream& out, MD5 md5) +{ + return out << md5.hexdigest(); +} + +////////////////////////////// + +std::string md5(const std::string &str) +{ + MD5 md5 = MD5(str); + + return md5.hexdigest(); +} \ No newline at end of file diff --git a/test_data/lots_of_files/MD5.h b/test_data/lots_of_files/MD5.h new file mode 100644 index 0000000..f435290 --- /dev/null +++ b/test_data/lots_of_files/MD5.h @@ -0,0 +1,98 @@ +/* MD5 + converted to C++ class by Frank Thilo (thilo@unix-ag.org) + for bzflag (http://www.bzflag.org) + + based on: + + md5.h and md5.c + reference implementation of RFC 1321 + +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +#pragma once +#ifndef _MD5_H_ +#define _MD5_H_ + + +#define _HAS_EXCEPTIONS (0) +#define _CRT_SECURE_NO_WARNINGS + +#include <cstring> +#include <string> + + +// a small class for calculating MD5 hashes of strings or byte arrays +// it is not meant to be fast or secure +// +// usage: 1) feed it blocks of uchars with update() +// 2) finalize() +// 3) get hexdigest() string +// or +// MD5(std::string).hexdigest() +// +// assumes that char is 8 bit and int is 32 bit +class MD5 +{ +public: + typedef unsigned int size_type; // must be 32bit + + MD5(); + MD5(const std::string& text); + void update(const unsigned char *buf, size_type length); + void update(const char *buf, size_type length); + MD5& finalize(); + std::string hexdigest() const; + friend std::ostream& operator<<(std::ostream&, MD5 md5); + +private: + void init(); + typedef unsigned char uint1; // 8bit + typedef unsigned int uint4; // 32bit + enum {blocksize = 64}; // VC6 won't eat a const static int here + + void transform(const uint1 block[blocksize]); + static void decode(uint4 output[], const uint1 input[], size_type len); + static void encode(uint1 output[], const uint4 input[], size_type len); + + bool finalized; + uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk + uint4 count[2]; // 64bit counter for number of bits (lo, hi) + uint4 state[4]; // digest so far + uint1 digest[16]; // the result + + // low level logic operations + static inline uint4 F(uint4 x, uint4 y, uint4 z); + static inline uint4 G(uint4 x, uint4 y, uint4 z); + static inline uint4 H(uint4 x, uint4 y, uint4 z); + static inline uint4 I(uint4 x, uint4 y, uint4 z); + static inline uint4 rotate_left(uint4 x, int n); + static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); +}; + +std::string md5(const std::string &str); + +#endif diff --git a/test_data/lots_of_files/_ctype.c b/test_data/lots_of_files/_ctype.c new file mode 100644 index 0000000..9ba5e5c --- /dev/null +++ b/test_data/lots_of_files/_ctype.c @@ -0,0 +1,384 @@ +/*** +*_ctype.c - function versions of ctype macros +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This files provides function versions of the character +* classification and conversion macros in ctype.h. +* +*******************************************************************************/ + +/*** +*ctype - Function versions of ctype macros +* +*Purpose: +* Function versions of the macros in ctype.h. In order to define +* these, we use a trick -- we use parentheses so we can use the +* name in the function declaration without macro expansion, then +* we use the macro in the definition part. +* +* Functions defined: +* isalpha isupper islower +* isdigit isxdigit isspace +* ispunct isalnum isprint +* isgraph isctrl __isascii +* __toascii __iscsym __iscsymf +* isblank +* +*Entry: +* int c = character to be tested +*Exit: +* returns non-zero = character is of the requested type +* 0 = character is NOT of the requested type +* +*Exceptions: +* None. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <ctype.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> + +#ifdef _DEBUG +#define __fast_ch_check(a,b) _chvalidator(a,b) +#else /* _DEBUG */ +#define __fast_ch_check(a,b) (__initiallocinfo.pctype[(a)] & (b)) +#endif /* _DEBUG */ + +extern "C" +{ +extern __inline int (__cdecl _isalpha_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isalpha_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isalpha) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _ALPHA); + } + else + { + return (_isalpha_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isupper_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isupper_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isupper) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _UPPER); + } + else + { + return (_isupper_l)(c, NULL); + } +} + +extern __inline int (__cdecl _islower_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _islower_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl islower) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _LOWER); + } + else + { + return (_islower_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isdigit_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isdigit_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isdigit) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _DIGIT); + } + else + { + return (_isdigit_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isxdigit_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isxdigit_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isxdigit) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _HEX); + } + else + { + return (_isxdigit_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isspace_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isspace_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isspace) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _SPACE); + } + else + { + return (_isspace_l)(c, NULL); + } +} + +extern __inline int (__cdecl _ispunct_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _ispunct_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl ispunct) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _PUNCT); + } + else + { + return (_ispunct_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isblank_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isblank_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isblank) ( + int c + ) +{ + if (__locale_changed == 0) + { + return (c == '\t') ? _BLANK : __fast_ch_check(c, _BLANK); + } + else + { + return (_isblank_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isalnum_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isalnum_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isalnum) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _ALPHA|_DIGIT); + } + else + { + return (_isalnum_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isprint_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isprint_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isprint) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _BLANK|_PUNCT|_ALPHA|_DIGIT); + } + else + { + return (_isprint_l)(c, NULL); + } +} + +extern __inline int (__cdecl _isgraph_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _isgraph_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl isgraph) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _PUNCT|_ALPHA|_DIGIT); + } + else + { + return (_isgraph_l)(c, NULL); + } +} + +extern __inline int (__cdecl _iscntrl_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return _iscntrl_l(c, _loc_update.GetLocaleT()); +} + +extern __inline int (__cdecl iscntrl) ( + int c + ) +{ + if (__locale_changed == 0) + { + return __fast_ch_check(c, _CONTROL); + } + else + { + return (_iscntrl_l)(c, NULL); + } +} + +extern __inline int (__cdecl __isascii) ( + int c + ) +{ + return __isascii(c); +} + +extern __inline int (__cdecl __toascii) ( + int c + ) +{ + return __toascii(c); +} + +extern __inline int (__cdecl _iscsymf_l) ( + int c, + _locale_t plocinfo + ) +{ + return (_isalpha_l)(c, plocinfo) || (c) == '_'; +} +extern __inline int (__cdecl __iscsymf) ( + int c + ) +{ + return __iscsymf(c); +} + +extern __inline int (__cdecl _iscsym_l) ( + int c, + _locale_t plocinfo + ) +{ + return (_isalnum_l)(c, plocinfo) || (c) == '_'; +} + +extern __inline int (__cdecl __iscsym) ( + int c + ) +{ + return __iscsym((unsigned char)(c)); +} +} diff --git a/test_data/lots_of_files/_filbuf.c b/test_data/lots_of_files/_filbuf.c new file mode 100644 index 0000000..17c5d31 --- /dev/null +++ b/test_data/lots_of_files/_filbuf.c @@ -0,0 +1,211 @@ +/*** +*_filbuf.c - fill buffer and get character +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _filbuf() - fill buffer and read first character, allocate +* buffer if there is none. Used from getc(). +* defines _filwbuf() - fill buffer and read first wide character, allocate +* buffer if there is none. Used from getwc(). +* +*Note: +* this file is included in safecrt.lib build directly, plese refer +* to safecrt_filwbuf.c +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdio.h> +#include <file2.h> +#include <io.h> +#include <dbgint.h> +#include <malloc.h> +#include <internal.h> +#include <msdos.h> +#include <wchar.h> +#include <mtdll.h> +#include <tchar.h> + +#ifndef _UNICODE + +/*** +*int _filbuf(stream) - fill buffer and get first character +* +*Purpose: +* get a buffer if the file doesn't have one, read into it, return first +* char. try to get a buffer, if a user buffer is not assigned. called +* only from getc; intended for use only within library. assume no input +* stream is to remain unbuffered when memory is available unless it is +* marked _IONBF. at worst, give it a single char buffer. the need for a +* buffer, no matter how small, becomes evident when we consider the +* ungetc's necessary in scanf +* +* [NOTE: Multi-thread - _filbuf() assumes that the caller has aquired +* the stream lock, if needed.] +* +*Entry: +* FILE *stream - stream to read from +* +*Exit: +* returns first character from buffer (next character to be read) +* returns EOF if the FILE is actually a string, or not open for reading, +* or if open for writing or if no more chars to read. +* all fields in FILE structure may be changed except _file. +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl _filbuf ( + FILE *str + ) + +#else /* _UNICODE */ + +/*** +*int _filwbuf(stream) - fill buffer and get first wide character +* +*Purpose: +* get a buffer if the file doesn't have one, read into it, return first +* char. try to get a buffer, if a user buffer is not assigned. called +* only from getc; intended for use only within library. assume no input +* stream is to remain unbuffered when memory is available unless it is +* marked _IONBF. at worst, give it a single char buffer. the need for a +* buffer, no matter how small, becomes evident when we consider the +* ungetc's necessary in scanf +* +* [NOTE: Multi-thread - _filwbuf() assumes that the caller has aquired +* the stream lock, if needed.] +* +*Entry: +* FILE *stream - stream to read from +* +*Exit: +* returns first wide character from buffer (next character to be read) +* returns WEOF if the FILE is actually a string, or not open for reading, +* or if open for writing or if no more chars to read. +* all fields in FILE structure may be changed except _file. +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl _filwbuf ( + FILE *str + ) + +#endif /* _UNICODE */ + +{ + FILE *stream=NULL; + +#ifdef _UNICODE + int is_split_character = 0; + unsigned char leftover_low_order_byte = 0; +#endif /* _UNICODE */ + + /* In safecrt, we assume we always have a buffer */ + _VALIDATE_RETURN(str != NULL, EINVAL, _TEOF); + + /* Init pointer to _iob2 entry. */ + stream = str; + + if (!inuse(stream) || stream->_flag & _IOSTRG) + return(_TEOF); + + if (stream->_flag & _IOWRT) + { + stream->_flag |= _IOERR; + return(_TEOF); + } + + stream->_flag |= _IOREAD; + + /* Get a buffer, if necessary. */ + + if (!anybuf(stream)) + { +#ifndef _SAFECRT_IMPL + _getbuf(stream); +#else /* _SAFECRT_IMPL */ + /* In safecrt, we assume we always have a buffer */ + _VALIDATE_RETURN(FALSE, EINVAL, _TEOF); +#endif /* _SAFECRT_IMPL */ + } + else + { +#ifdef _UNICODE + /* When reading wchar_t elements, we must handle the case where a + two-byte character straddles the buffer boundary, with the low + order byte at the end of the old buffer and the high order byte + at the start of the new buffer. + + We do this here: if there is exactly one character left in the + buffer, we store that and set the is_split_character flag. After + we load the new buffer, we'll or this low order byte into the + result. */ + if (stream->_cnt == 1) + { + is_split_character = 1; + leftover_low_order_byte = (unsigned char)*stream->_ptr; + } +#endif /* _UNICODE */ + + stream->_ptr = stream->_base; + } + + stream->_cnt = _read(_fileno(stream), stream->_base, stream->_bufsiz); + +#ifndef _UNICODE + if ((stream->_cnt == 0) || (stream->_cnt == -1)) { +#else /* _UNICODE */ + if ((stream->_cnt == 0) || (stream->_cnt == 1) || stream->_cnt == -1) { +#endif /* _UNICODE */ + stream->_flag |= stream->_cnt ? _IOERR : _IOEOF; + stream->_cnt = 0; + return(_TEOF); + } + + if ( !(stream->_flag & (_IOWRT|_IORW)) && + ((_osfile_safe(_fileno(stream)) & (FTEXT|FEOFLAG)) == + (FTEXT|FEOFLAG)) ) + { + stream->_flag |= _IOCTRLZ; + } + + /* Check for small _bufsiz (_SMALL_BUFSIZ). If it is small and + if it is our buffer, then this must be the first _filbuf after + an fseek on a read-access-only stream. Restore _bufsiz to its + larger value (_INTERNAL_BUFSIZ) so that the next _filbuf call, + if one is made, will fill the whole buffer. */ + if ( (stream->_bufsiz == _SMALL_BUFSIZ) && (stream->_flag & + _IOMYBUF) && !(stream->_flag & _IOSETVBUF) ) + { + stream->_bufsiz = _INTERNAL_BUFSIZ; + } + +#ifndef _UNICODE + stream->_cnt--; + return(0xff & *stream->_ptr++); +#else /* _UNICODE */ + if (is_split_character) + { + /* If the character was split across buffers, we read only one byte + from the new buffer and or it with the leftover byte from the old + buffer. */ + unsigned char high_order_byte = (unsigned char)(*stream->_ptr); + wchar_t result = (high_order_byte << 8) | leftover_low_order_byte; + + --stream->_cnt; + ++stream->_ptr; + return (result); + } + else + { + stream->_cnt -= sizeof(wchar_t); + return (0xffff & *((wchar_t *)(stream->_ptr))++); + } +#endif /* _UNICODE */ + +} diff --git a/test_data/lots_of_files/_file.c b/test_data/lots_of_files/_file.c new file mode 100644 index 0000000..ca36911 --- /dev/null +++ b/test_data/lots_of_files/_file.c @@ -0,0 +1,352 @@ +/*** +*_file.c - Definition of _iob[], initializer and terminator. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines _iob[], the array of stdio control structures, the initializer +* and terminator routines, and the multithread locking for stdio. +* +*******************************************************************************/ + +#include <sect_attribs.h> +#include <cruntime.h> +#include <windows.h> +#include <stdio.h> +#include <file2.h> +#include <internal.h> +#include <malloc.h> +#include <rterr.h> +#include <dbgint.h> +#include <mtdll.h> + +/* + * Buffer for stdin. + */ + +char _bufin[_INTERNAL_BUFSIZ]; + +/* + * FILE descriptors; preset for stdin/out/err (note that the __tmpnum field + * is not initialized) + */ +FILE _iob[_IOB_ENTRIES] = { + /* _ptr, _cnt, _base, _flag, _file, _charbuf, _bufsiz */ + + /* stdin (_iob[0]) */ + + { _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ }, + + /* stdout (_iob[1]) */ + + { NULL, 0, NULL, _IOWRT, 1, 0, 0 }, + + /* stderr (_iob[3]) */ + + { NULL, 0, NULL, _IOWRT, 2, 0, 0 }, + +}; + + +/* These functions are for enabling STATIC_CPPLIB functionality */ +_CRTIMP FILE * __cdecl __iob_func(void) +{ + return _iob; +} + + +/* + * Pointer to array of FILE * or _FILEX * structures. + */ +void ** __piob; + +/* + * Number of open streams (set to _NSTREAM by default) + */ +#ifdef CRTDLL +int _nstream = _NSTREAM_; +#else /* CRTDLL */ +int _nstream; +#endif /* CRTDLL */ + + +/* + * Initializer and terminator for stdio + */ +int __cdecl __initstdio(void); +void __cdecl __endstdio(void); + +_CRTALLOC(".CRT$XIC") static _PIFV pinit = __initstdio; + +_CRTALLOC(".CRT$XPXA") static _PVFV pterm = __endstdio; + +#ifndef CRTDLL +/* + * _cflush is a dummy variable used to pull in _endstdio() when any STDIO + * routine is included in the user program. + */ +int _cflush = 0; +#endif /* CRTDLL */ + + +/*** +* __initstdio - Initialize the stdio system +* +*Purpose: +* Create and initialize the __piob array. +* +*Entry: <void> +* +*Exit: Returns _RT_STDIOINIT if error encountered. +* +*Uses: +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl __initstdio(void) +{ + int i; + +#ifndef CRTDLL + /* + * If the user has not supplied a definition of _nstream, set it + * to _NSTREAM_. If the user has supplied a value that is too small + * set _nstream to the minimum acceptable value (_IOB_ENTRIES). + */ + if ( _nstream == 0 ) + _nstream = _NSTREAM_; + else if ( _nstream < _IOB_ENTRIES ) + _nstream = _IOB_ENTRIES; +#endif /* CRTDLL */ + + /* + * Allocate the __piob array. Try for _nstream entries first. If this + * fails then reset _nstream to _IOB_ENTRIES and try again. If it + * still fails, bail out with an RTE. + */ + if ( (__piob = (void **)_calloc_crt( _nstream, sizeof(void *) )) == + NULL ) { + _nstream = _IOB_ENTRIES; + if ( (__piob = (void **)_calloc_crt( _nstream, sizeof(void *) )) + == NULL ) + return _RT_STDIOINIT; + } + + /* + * Initialize the first _IOB_ENTRIES to point to the corresponding + * entries in _iob[]. + */ + for ( i = 0 ; i < _IOB_ENTRIES ; i++ ) + __piob[i] = (void *)&_iob[i]; + + return 0; +} + + +/*** +* __endstdio - Terminate the stdio system +* +*Purpose: +* Terminate the stdio system +* +* (1) Flush all streams. (Do this even if we're going to +* call fcloseall since that routine won't do anything to the +* std streams.) +* +* (2) If returning to caller, close all streams. This is +* not necessary if the exe is terminating because the OS will +* close the files for us (much more efficiently, too). +* +*Entry: <void> +* +*Exit: <void> +* +*Uses: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl __endstdio(void) +{ + /* flush all streams */ + _flushall(); + + /* if in callable exit, close all streams */ + if (_exitflag) + _fcloseall(); + _free_crt(__piob); + __piob = NULL; +} + + +/*** +* _lock_file - Lock a FILE +* +*Purpose: +* Assert the lock for a stdio-level file +* +*Entry: +* pf = __piob[] entry (pointer to a FILE or _FILEX) +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _lock_file ( + FILE *pf + ) +{ + /* + * The way the FILE (pointed to by pf) is locked depends on whether + * it is part of _iob[] or not + */ + if ( (pf >= _iob) && (pf <= (&_iob[_IOB_ENTRIES-1])) ) + { + /* + * FILE lies in _iob[] so the lock lies in _locktable[]. + */ + _lock( _STREAM_LOCKS + (int)(pf - _iob) ); + /* We set _IOLOCKED to indicate we locked the stream */ + pf->_flag |= _IOLOCKED; + } + else + /* + * Not part of _iob[]. Therefore, *pf is a _FILEX and the + * lock field of the struct is an initialized critical + * section. + */ + EnterCriticalSection( &(((_FILEX *)pf)->lock) ); +} + + +/*** +* _lock_file2(i, s) - Lock the FILE +* +*Purpose: +* Assert the lock for a stdio-level file given by s == __piob[i]. +* +*Entry: +* s == __piob[i] +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _lock_file2 ( + int i, + void *s + ) +{ + /* + * The way the FILE is locked depends on whether it is part of _iob[] + * _iob[] or not + */ + if ( i < _IOB_ENTRIES ) + { + /* + * FILE lies in _iob[] so the lock lies in _locktable[]. + */ + _lock( _STREAM_LOCKS + i ); + /* We set _IOLOCKED to indicate we locked the stream */ + ((FILE*)s)->_flag |= _IOLOCKED; + } + else + /* + * Not part of _iob[]. Therefore, *s is a _FILEX and the + * lock field of the struct is an initialized critical + * section. + */ + EnterCriticalSection( &(((_FILEX *)s)->lock) ); +} + + +/*** +* _unlock_file - Unlock a FILE +* +*Purpose: +* Release the lock for a stdio-level file +* +*Entry: +* pf = __piob[] entry (pointer to a FILE or _FILEX) +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _unlock_file ( + FILE *pf + ) +{ + /* + * The way the FILE (pointed to by pf) is unlocked depends on whether + * it is part of _iob[] or not + */ + if ( (pf >= _iob) && (pf <= (&_iob[_IOB_ENTRIES-1])) ) + { + /* + * FILE lies in _iob[] so the lock lies in _locktable[]. + * We reset _IOLOCKED to indicate we unlock the stream. + */ + pf->_flag &= ~_IOLOCKED; + _unlock( _STREAM_LOCKS + (int)(pf - _iob) ); + } + else + /* + * Not part of _iob[]. Therefore, *pf is a _FILEX and the + * lock field of the struct is an initialized critical + * section. + */ + LeaveCriticalSection( &(((_FILEX *)pf)->lock) ); +} + + +/*** +* _unlock_file2(i, s) - Lock the FILE +* +*Purpose: +* Release the lock for a stdio-level file given by s == __piob[i]. +* +*Entry: +* s == __piob[i] +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _unlock_file2 ( + int i, + void *s + ) +{ + /* + * The way the FILE is locked depends on whether it is part of _iob[] + * _iob[] or not + */ + if ( i < _IOB_ENTRIES ) + { + /* + * FILE lies in _iob[] so the lock lies in _locktable[]. + * We reset _IOLOCKED to indicate we unlock the stream. + */ + ((FILE*)s)->_flag &= ~_IOLOCKED; + _unlock( _STREAM_LOCKS + i ); + } + else + /* + * Not part of _iob[]. Therefore, *s is a _FILEX and the + * lock field of the struct is an initialized critical + * section. + */ + LeaveCriticalSection( &(((_FILEX *)s)->lock) ); +} diff --git a/test_data/lots_of_files/_filwbuf.c b/test_data/lots_of_files/_filwbuf.c new file mode 100644 index 0000000..bbf94f5 --- /dev/null +++ b/test_data/lots_of_files/_filwbuf.c @@ -0,0 +1,23 @@ +/*** +*_filwbuf.c - fill buffer and get wide character +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _filwbuf() - fill buffer and read first character, allocate +* buffer if there is none. Used from getwc(). +* +*******************************************************************************/ + + + +#ifndef _UNICODE +#define _UNICODE 1 +#endif /* _UNICODE */ + +#ifndef UNICODE +#define UNICODE 1 +#endif /* UNICODE */ + +#include "_filbuf.c" + diff --git a/test_data/lots_of_files/_flsbuf.c b/test_data/lots_of_files/_flsbuf.c new file mode 100644 index 0000000..748a9c4 --- /dev/null +++ b/test_data/lots_of_files/_flsbuf.c @@ -0,0 +1,211 @@ +/*** +*_flsbuf.c - flush buffer and output character. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _flsbuf() - flush a file buffer and output a character. +* defines _flswbuf() - flush a file buffer and output a wide character. +* If no buffer, make one. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdio.h> +#include <file2.h> +#include <io.h> +#include <dbgint.h> +#include <malloc.h> +#include <msdos.h> +#include <wchar.h> +#include <internal.h> +#include <mtdll.h> +#include <tchar.h> + +#ifndef _UNICODE + +/*** +*int _flsbuf(ch, stream) - flush buffer and output character. +* +*Purpose: +* flush a buffer if this stream has one. if not, try to get one. put the +* next output char (ch) into the buffer (or output it immediately if this +* stream can't have a buffer). called only from putc. intended for use +* only within library. +* +* [NOTE: Multi-thread - It is assumed that the caller has aquired +* the stream lock.] +* +*Entry: +* FILE *stream - stream to flish and write on +* int ch - character to output. +* +*Exit: +* returns -1 if FILE is actually a string, or if can't write ch to +* unbuffered file, or if we flush a buffer but the number of chars +* written doesn't agree with buffer size. Otherwise returns ch. +* all fields in FILE struct can be affected except _file. +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl _flsbuf ( + int ch, + FILE *str + ) + +#else /* _UNICODE */ + +/*** +*int _flswbuf(ch, stream) - flush buffer and output wide character. +* +*Purpose: +* flush a buffer if this stream has one. if not, try to get one. put the +* next output wide char (ch) into the buffer (or output it immediately if this +* stream can't have a buffer). called only from putwc. intended for use +* only within library. +* +* [NOTE: Multi-thread - It is assumed that the caller has aquired +* the stream lock.] +* +*Entry: +* FILE *stream - stream to flish and write on +* int ch - wide character to output. +* +*Exit: +* returns -1 if FILE is actually a string, or if can't write ch to +* unbuffered file, or if we flush a buffer but the number of wide chars +* written doesn't agree with buffer size. Otherwise returns ch. +* all fields in FILE struct can be affected except _file. +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl _flswbuf ( + int ch, + FILE *str + ) + +#endif /* _UNICODE */ + +{ + + FILE *stream; + int charcount; + int written; + int fh; + + _ASSERTE(str != NULL); + + stream = str; + fh = _fileno(stream); + + if (!(stream->_flag & (_IOWRT|_IORW))) { + errno = EBADF; + stream->_flag |= _IOERR; + return(_TEOF); + } else if ((stream->_flag & _IOSTRG)) { + errno = ERANGE; + stream->_flag |= _IOERR; + return(_TEOF); + } + + /* Check that _IOREAD is not set or, if it is, then so is _IOEOF. Note + that _IOREAD and IOEOF both being set implies switching from read to + write at end-of-file, which is allowed by ANSI. Note that resetting + the _cnt and _ptr fields amounts to doing an fflush() on the stream + in this case. Note also that the _cnt field has to be reset to 0 for + the error path as well (i.e., _IOREAD set but _IOEOF not set) as + well as the non-error path. */ + + if (stream->_flag & _IOREAD) { + stream->_cnt = 0; + if (stream->_flag & _IOEOF) { + stream->_ptr = stream->_base; + stream->_flag &= ~_IOREAD; + } + else { + stream->_flag |= _IOERR; + return(_TEOF); + } + } + + stream->_flag |= _IOWRT; + stream->_flag &= ~_IOEOF; + written = charcount = stream->_cnt = 0; + + /* Get a buffer for this stream, if necessary. */ + if (!anybuf(stream)) { + + /* Do NOT get a buffer if (1) stream is stdout/stderr, and + (2) stream is NOT a tty. + [If stdout/stderr is a tty, we do NOT set up single char + buffering. This is so that later temporary buffering will + not be thwarted by the _IONBF bit being set (see + _stbuf/_ftbuf usage).] + */ + if (!( ((stream==stdout) || (stream==stderr)) + && (_isatty(fh)) )) + + _getbuf(stream); + + } /* end !anybuf() */ + + /* If big buffer is assigned to stream... */ + if (bigbuf(stream)) { + + _ASSERTE(("inconsistent IOB fields", stream->_ptr - stream->_base >= 0)); + + charcount = (int)(stream->_ptr - stream->_base); + stream->_ptr = stream->_base + sizeof(TCHAR); + stream->_cnt = stream->_bufsiz - (int)sizeof(TCHAR); + + if (charcount > 0) + written = _write(fh, stream->_base, charcount); + else + if (_osfile_safe(fh) & FAPPEND) + { + if( _lseeki64(fh,0L,SEEK_END)==-1) + { + stream->_flag |= _IOERR; + return(_TEOF); + } + } + +#ifndef _UNICODE + *stream->_base = (char)ch; +#else /* _UNICODE */ + *(wchar_t *)(stream->_base) = (wchar_t)(ch & 0xffff); +#endif /* _UNICODE */ + } + + /* Perform single character output (either _IONBF or no buffering) */ + else { + charcount = sizeof(TCHAR); +#ifndef _UNICODE + written = _write(fh, &ch, charcount); +#else /* _UNICODE */ + { + char mbc[4]; + + *(wchar_t *)mbc = (wchar_t)(ch & 0xffff); + written = _write(fh, mbc, charcount); + } +#endif /* _UNICODE */ + } + + /* See if the _write() was successful. */ + if (written != charcount) { + stream->_flag |= _IOERR; + return(_TEOF); + } + +#ifndef _UNICODE + return(ch & 0xff); +#else /* _UNICODE */ + return(ch & 0xffff); +#endif /* _UNICODE */ + +} diff --git a/test_data/lots_of_files/_flswbuf.c b/test_data/lots_of_files/_flswbuf.c new file mode 100644 index 0000000..9a59a87 --- /dev/null +++ b/test_data/lots_of_files/_flswbuf.c @@ -0,0 +1,23 @@ +/*** +*_flswbuf.c - flush buffer and output wide character. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _flswbuf() - flush a file buffer and output a wide character. +* If no buffer, make one. +* +*******************************************************************************/ + + + +#ifndef _UNICODE +#define _UNICODE 1 +#endif /* _UNICODE */ + +#ifndef UNICODE +#define UNICODE 1 +#endif /* UNICODE */ + +#include "_flsbuf.c" + diff --git a/test_data/lots_of_files/_fptostr.c b/test_data/lots_of_files/_fptostr.c new file mode 100644 index 0000000..a124c33 --- /dev/null +++ b/test_data/lots_of_files/_fptostr.c @@ -0,0 +1,109 @@ +/*** +*_fptostr.c - workhorse routine for converting floating point to string +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Workhorse routine for fcvt, ecvt. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <string.h> +#include <stddef.h> +#include <fltintrn.h> +#include <internal.h> + +/*** +*errno_t _fptostr(buf, size, digits, pflt) - workhorse floating point conversion +* +*Purpose: +* This is the workhorse routine for fcvt, ecvt. Here is where +* all the digits are put into a buffer and the rounding is +* performed and indicators of the decimal point position are set. Note, +* this must not change the mantissa field of pflt since routines which +* use this routine rely on this being unchanged. +* +*Entry: +* char *buf - the buffer in which the digits are to be put +* int digits - the number of digits which are to go into the buffer +* STRFLT pflt - a pointer to a structure containing information on the +* floating point value, including a string containing the +* non-zero significant digits of the mantissa. +* +*Exit: +* Changes the contents of the buffer and also may increment the decpt +* field of the structure pointer to by the 'pflt' parameter if overflow +* occurs during rounding (e.g. 9.999999... gets rounded to 10.000...). +* +*Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +errno_t __cdecl _fptostr ( + char *buf, + size_t sizeInBytes, + int digits, + STRFLT pflt + ) +{ + char *pbuf = buf; + char *mantissa = pflt->mantissa; + + /* validation section */ + _VALIDATE_RETURN_ERRCODE(buf != NULL, EINVAL); + _VALIDATE_RETURN_ERRCODE(sizeInBytes > 0, EINVAL); + buf[0] = '\0'; + /* the buffer will contains ndec decimal digits plus an optional + * overflow digit for the rounding + */ + _VALIDATE_RETURN_ERRCODE(sizeInBytes > (size_t)((digits > 0 ? digits : 0) + 1), ERANGE); + _VALIDATE_RETURN_ERRCODE(pflt != NULL, EINVAL); + + /* initialize the first digit in the buffer to '0' (NOTE - NOT '\0') + * and set the pointer to the second digit of the buffer. The first + * digit is used to handle overflow on rounding (e.g. 9.9999... + * becomes 10.000...) which requires a carry into the first digit. + */ + + *pbuf++ = '0'; + + /* Copy the digits of the value into the buffer (with 0 padding) + * and insert the terminating null character. + */ + + while (digits > 0) { + *pbuf++ = (*mantissa) ? *mantissa++ : (char)'0'; + digits--; + } + *pbuf = '\0'; + + /* do any rounding which may be needed. Note - if digits < 0 don't + * do any rounding since in this case, the rounding occurs in a digit + * which will not be output beause of the precision requested + */ + + if (digits >= 0 && *mantissa >= '5') { + pbuf--; + while (*pbuf == '9') + *pbuf-- = '0'; + *pbuf += 1; + } + + if (*buf == '1') { + /* the rounding caused overflow into the leading digit (e.g. + * 9.999.. went to 10.000...), so increment the decpt position + * by 1 + */ + pflt->decpt++; + } + else { + /* move the entire string to the left one digit to remove the + * unused overflow digit. + */ + memmove(buf, buf+1, strlen(buf+1)+1); + } + + return 0; +} diff --git a/test_data/lots_of_files/_freebuf.c b/test_data/lots_of_files/_freebuf.c new file mode 100644 index 0000000..8ad1d67 --- /dev/null +++ b/test_data/lots_of_files/_freebuf.c @@ -0,0 +1,56 @@ +/*** +*_freebuf.c - release a buffer from a stream +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _freebuf() - release a buffer from a stream +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdio.h> +#include <file2.h> +#include <dbgint.h> +#include <internal.h> +#include <stdlib.h> + +/*** +*void _freebuf(stream) - release a buffer from a stream +* +*Purpose: +* free a buffer if at all possible. free() the space if malloc'd by me. +* forget about trying to free a user's buffer for him; it may be static +* memory (not from malloc), so he has to take care of it. this function +* is not intended for use outside the library. +* +* Multi-thread notes: +* _freebuf() does NOT get the stream lock; it is assumed that the +* caller has already done this. +* +*Entry: +* FILE *stream - stream to free bufer on +* +*Exit: +* Buffer may be freed. +* No return value. +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _freebuf ( + FILE *stream + ) +{ + _ASSERTE(stream != NULL); + + if (inuse(stream) && mbuf(stream)) + { + _free_crt(stream->_base); + + stream->_flag &= ~(_IOMYBUF | _IOSETVBUF); + stream->_base = stream->_ptr = NULL; + stream->_cnt = 0; + } +} diff --git a/test_data/lots_of_files/_getbuf.c b/test_data/lots_of_files/_getbuf.c new file mode 100644 index 0000000..7d32061 --- /dev/null +++ b/test_data/lots_of_files/_getbuf.c @@ -0,0 +1,79 @@ +/*** +*_getbuf.c - Get a stream buffer +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Allocate a buffer and init stream data bases. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdio.h> +#include <file2.h> +#include <malloc.h> +#include <internal.h> +#include <dbgint.h> + +/*** +*_getbuf() - Allocate a buffer and init stream data bases +* +*Purpose: +* Allocates a buffer for a stream and inits the stream data bases. +* +* [NOTE 1: This routine assumes the caller has already checked to make +* sure the stream needs a buffer. +* +* [NOTE 2: Multi-thread - Assumes caller has aquired stream lock, if +* needed.] +* +*Entry: +* FILE *stream = stream to allocate a buffer for +* +*Exit: +* void +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _getbuf ( + FILE *str + ) +{ + FILE *stream; + + _ASSERTE(str != NULL); + +#if !defined (CRTDLL) + /* force library pre-termination procedure */ + _cflush++; +#endif /* !defined (CRTDLL) */ + + /* Init pointers */ + stream = str; + + + /* Try to get a big buffer */ + if (stream->_base = _malloc_crt(_INTERNAL_BUFSIZ)) + { + /* Got a big buffer */ + stream->_flag |= _IOMYBUF; + stream->_bufsiz = _INTERNAL_BUFSIZ; + } + + else { + + + /* Did NOT get a buffer - use single char buffering. */ + stream->_flag |= _IONBF; + stream->_base = (char *)&(stream->_charbuf); + stream->_bufsiz = 2; + + } + + stream->_ptr = stream->_base; + stream->_cnt = 0; + + return; +} diff --git a/test_data/lots_of_files/_mbslen.c b/test_data/lots_of_files/_mbslen.c new file mode 100644 index 0000000..4251ee9 --- /dev/null +++ b/test_data/lots_of_files/_mbslen.c @@ -0,0 +1,95 @@ +/*** +*_mbslen.c - Return number of multibyte characters in a multibyte string +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Return number of multibyte characters in a multibyte string +* excluding the terminal null. Locale-dependent. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> +#include <locale.h> +#include <dbgint.h> +#include <ctype.h> +#include <mbctype.h> +#include <internal.h> +#include <mtdll.h> +#include <setlocal.h> + +/*** +*_mbstrlen - Return number of multibyte characters in a multibyte string +* +*Purpose: +* Return number of multibyte characters in a multibyte string +* excluding the terminal null. Locale-dependent. +* +*Entry: +* char *s = string +* +*Exit: +* Returns the number of multibyte characters in the string, or +* Returns (size_t)-1 if the string contains an invalid multibyte character. +* Also, errno is set to EILSEQ. +* +*Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +size_t __cdecl _mbstrlen_l( + const char *s, + _locale_t plocinfo + ) +{ + size_t n; + _LocaleUpdate _loc_update(plocinfo); + + _ASSERTE (_loc_update.GetLocaleT()->locinfo->mb_cur_max == 1 || _loc_update.GetLocaleT()->locinfo->mb_cur_max == 2); + + if ( _loc_update.GetLocaleT()->locinfo->mb_cur_max == 1 ) + /* handle single byte character sets */ + return strlen(s); + + + /* verify all valid MB chars */ + if ( MultiByteToWideChar( _loc_update.GetLocaleT()->locinfo->lc_codepage, + MB_PRECOMPOSED | MB_ERR_INVALID_CHARS, + s, + -1, + NULL, + 0 ) == 0 ) + { + /* bad MB char */ + errno = EILSEQ; + return (size_t)-1; + } + + /* count MB chars */ + for (n = 0; *s; n++, s++) { + if ( _isleadbyte_l((unsigned char)*s, _loc_update.GetLocaleT()) ) + { + if (*++s == '\0') + break; + } + } + + + return(n); +} + +size_t __cdecl _mbstrlen( + const char *s + ) +{ + if (__locale_changed == 0) + { + return strlen(s); + } + else + { + return _mbstrlen_l(s, NULL); + } +} diff --git a/test_data/lots_of_files/_mbslen_s.c b/test_data/lots_of_files/_mbslen_s.c new file mode 100644 index 0000000..a56209f --- /dev/null +++ b/test_data/lots_of_files/_mbslen_s.c @@ -0,0 +1,111 @@ +/*** +*_mbslen_s.c - Return number of multibyte characters in a multibyte string +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Return number of multibyte characters in a multibyte string +* excluding the terminal null. Locale-dependent. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> +#include <ctype.h> +#include <locale.h> +#include <dbgint.h> +#include <internal.h> +#include <mtdll.h> +#include <setlocal.h> + +/*** +*_mbstrnlen - Return number of multibyte characters in a multibyte string +* +*Purpose: +* Return number of multibyte characters in a multibyte string +* excluding the terminal null. Locale-dependent. +* +*Entry: +* char *s = string +* size_t maxsize +* +*Exit: +* Returns the number of multibyte characters in the string, or +* (size_t)-1 if the string contains an invalid multibyte character and errno +* is set to EILSEQ. +* Only the first sizeInBytes bytes of the string are inspected: if the null +* terminator is not found, sizeInBytes is returned. +* If the string is null terminated in sizeInBytes bytes, the return value +* will always be less than sizeInBytes. +* If something goes wrong, (size_t)-1 is returned and errno is set to EINVAL. +* +*Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +size_t __cdecl _mbstrnlen_l( + const char *s, + size_t sizeInBytes, + _locale_t plocinfo + ) +{ + size_t n, size; + + + /* validation section */ + _VALIDATE_RETURN(s != NULL, EINVAL, (size_t)-1); + _VALIDATE_RETURN(sizeInBytes <= INT_MAX, EINVAL, (size_t)-1); + + + _LocaleUpdate _loc_update(plocinfo); + + _ASSERTE (_loc_update.GetLocaleT()->locinfo->mb_cur_max == 1 || _loc_update.GetLocaleT()->locinfo->mb_cur_max == 2); + + if ( _loc_update.GetLocaleT()->locinfo->mb_cur_max == 1 ) + /* handle single byte character sets */ + return (int)strnlen(s, sizeInBytes); + + + /* verify all valid MB chars */ + if ( MultiByteToWideChar( _loc_update.GetLocaleT()->locinfo->lc_codepage, + MB_PRECOMPOSED | MB_ERR_INVALID_CHARS, + s, + (int)sizeInBytes, + NULL, + 0 ) == 0 ) + { + /* bad MB char */ + errno = EILSEQ; + return (size_t)-1; + } + + /* count MB chars */ + /* Note that sizeInBytes here is the number of bytes, not mb characters! */ + for (n = 0, size = 0; size < sizeInBytes && *s; n++, s++, size++) + { + if ( _isleadbyte_l((unsigned char)*s, _loc_update.GetLocaleT()) ) + { + size++; + if (size >= sizeInBytes) + { + break; + } + if (*++s == '\0') + { + break; + } + } + } + + + return (size >= sizeInBytes ? sizeInBytes : n); +} + +size_t __cdecl _mbstrnlen( + const char *s, + size_t maxsize + ) +{ + return _mbstrnlen_l(s, maxsize, NULL); +} diff --git a/test_data/lots_of_files/_newmode.c b/test_data/lots_of_files/_newmode.c new file mode 100644 index 0000000..c4c1a8a --- /dev/null +++ b/test_data/lots_of_files/_newmode.c @@ -0,0 +1,31 @@ +/*** +*_newmode.c - set new() handler mode to not handle malloc failures +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Sets the global flag which controls whether the new() handler +* is called on malloc failures. The default behavior in Visual +* C++ v2.0 and later is not to, that malloc failures return NULL +* without calling the new handler. This object is linked in unless +* the special object NEWMODE.OBJ is manually linked. +* +* This source file is the complement of LINKOPTS/NEWMODE.C. +* +*******************************************************************************/ + + +#ifdef CRTDLL +#undef CRTDLL +#endif /* CRTDLL */ + +#ifdef MRTDLL +#undef MRTDLL +#endif /* MRTDLL */ + +#include <internal.h> + +/* enable new handler calls upon malloc failures */ + +int _newmode = 0; /* Malloc New Handler MODE */ + diff --git a/test_data/lots_of_files/_open.c b/test_data/lots_of_files/_open.c new file mode 100644 index 0000000..004ce60 --- /dev/null +++ b/test_data/lots_of_files/_open.c @@ -0,0 +1,289 @@ +/*** +*_open.c - open a stream, with string mode +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _openfile() - opens a stream, with string arguments for mode +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <file2.h> +#include <share.h> +#include <io.h> +#include <dbgint.h> +#include <internal.h> +#include <tchar.h> +#include <sys\stat.h> + +#define CMASK 0644 /* rw-r--r-- */ +#define P_CMASK 0666 /* different for Posix */ + +/*** +*FILE *_openfile(filename, mode, shflag, stream) - open a file with string +* mode and file sharing flag. +* +*Purpose: +* parse the string, looking for exactly one of {rwa}, at most one '+', +* at most one of {tb}, at most one of {cn}, at most one of {SR}, at most +* one 'T', and at most one 'D'. pass the result on as an int containing +* flags of what was found. open a file with proper mode if permissions +* allow. buffer not allocated until first i/o call is issued. intended +* for use inside library only +* +*Entry: +* char *filename - file to open +* char *mode - mode to use (see above) +* int shflag - file sharing flag +* FILE *stream - stream to use for file +* +*Exit: +* set stream's fields, and causes system file management by system calls +* returns stream or NULL if fails +* +*Exceptions: +* +*******************************************************************************/ + +FILE * __cdecl __topenfile ( + const _TSCHAR *filename, + const _TSCHAR *mode, + int shflag, + FILE *str + ) +{ + int modeflag = 0; + int streamflag = _commode; + int commodeset = 0; + int scanset = 0; + int whileflag; + int filedes; + FILE *stream; + BOOL encodingFlag = FALSE; + + _ASSERTE(filename != NULL); + _ASSERTE(mode != NULL); + _ASSERTE(str != NULL); + + /* Parse the user's specification string as set flags in + (1) modeflag - system call flags word + (2) streamflag - stream handle flags word. */ + + /* Skip leading spaces */ + while (*mode == _T(' ')) + { + ++mode; + } + + /* First mode character must be 'r', 'w', or 'a'. */ + + switch (*mode) { + case _T('r'): + modeflag = _O_RDONLY; + streamflag |= _IOREAD; + break; + case _T('w'): + modeflag = _O_WRONLY | _O_CREAT | _O_TRUNC; + streamflag |= _IOWRT; + break; + case _T('a'): + modeflag = _O_WRONLY | _O_CREAT | _O_APPEND; + streamflag |= _IOWRT; + break; + default: + _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL); + } + + /* There can be up to three more optional mode characters: + (1) A single '+' character, + (2) One of 't' and 'b' and + (3) One of 'c' and 'n'. + */ + + whileflag=1; + + while(*++mode && whileflag) + switch(*mode) { + + case _T(' '): + /* skip spaces */ + break; + + case _T('+'): + if (modeflag & _O_RDWR) + whileflag=0; + else { + modeflag |= _O_RDWR; + modeflag &= ~(_O_RDONLY | _O_WRONLY); + streamflag |= _IORW; + streamflag &= ~(_IOREAD | _IOWRT); + } + break; + + case _T('b'): + if (modeflag & (_O_TEXT | _O_BINARY)) + whileflag=0; + else + modeflag |= _O_BINARY; + break; + + case _T('t'): + if (modeflag & (_O_TEXT | _O_BINARY)) + whileflag=0; + else + modeflag |= _O_TEXT; + break; + + case _T('c'): + if (commodeset) + whileflag=0; + else { + commodeset = 1; + streamflag |= _IOCOMMIT; + } + break; + + case _T('n'): + if (commodeset) + whileflag=0; + else { + commodeset = 1; + streamflag &= ~_IOCOMMIT; + } + break; + + case _T('S'): + if (scanset) + whileflag=0; + else { + scanset = 1; + modeflag |= _O_SEQUENTIAL; + } + break; + + case _T('R'): + if (scanset) + whileflag=0; + else { + scanset = 1; + modeflag |= _O_RANDOM; + } + break; + + case _T('T'): + if (modeflag & _O_SHORT_LIVED) + whileflag=0; + else + modeflag |= _O_SHORT_LIVED; + break; + + case _T('D'): + if (modeflag & _O_TEMPORARY) + whileflag=0; + else + modeflag |= _O_TEMPORARY; + break; + case _T('N'): + modeflag |= _O_NOINHERIT; + break; + + case _T(','): + encodingFlag = TRUE; + whileflag = 0; + break; + + + default: + _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL); + } + if (encodingFlag) + { + static const _TSCHAR ccsField[] = _T("ccs"); + static const _TSCHAR utf8encoding[] = _T("UTF-8"); + static const _TSCHAR utf16encoding[] = _T("UTF-16LE"); + static const _TSCHAR unicodeencoding[] = _T("UNICODE"); + + /* Skip spaces */ + while (*mode == _T(' ')) + { + ++mode; + } + + /* + * The length that we want to compare is numbers of elements in + * csField -1 since this number also contains NULL terminator + */ + if (_tcsncmp(ccsField, mode, (_countof(ccsField))-1) != 0) + _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL); + + mode += _countof(ccsField)-1; + + /* Skip spaces */ + while (*mode == _T(' ')) + { + ++mode; + } + + /* Look for '=' */ + if (*mode != _T('=')) + { + _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL); + } + ++mode; + + /* Skip spaces */ + while (*mode == _T(' ')) + { + ++mode; + } + + if (_tcsnicmp(mode, utf8encoding, _countof(utf8encoding) - 1) == 0){ + mode += _countof(utf8encoding)-1; + modeflag |= _O_U8TEXT; + } + else if (_tcsnicmp(mode, utf16encoding, _countof(utf16encoding) - 1) == 0) { + mode += _countof(utf16encoding)-1; + modeflag |= _O_U16TEXT; + } + else if (_tcsnicmp(mode, unicodeencoding, _countof(unicodeencoding) - 1) == 0) { + mode += _countof(unicodeencoding)-1; + modeflag |= _O_WTEXT; + } + else + _VALIDATE_RETURN(("Invalid file open mode",0), EINVAL, NULL); + + } + + /* Skip trailing spaces */ + while (*mode == _T(' ')) + { + ++mode; + } + + _VALIDATE_RETURN( (*mode == _T('\0')), EINVAL, NULL); + + /* Try to open the file. Note that if neither 't' nor 'b' is + specified, _sopen will use the default. */ + + if (_tsopen_s(&filedes, filename, modeflag, shflag, _S_IREAD | _S_IWRITE) != 0) + return(NULL); + + /* Set up the stream data base. */ +#ifndef CRTDLL + _cflush++; /* force library pre-termination procedure */ +#endif /* CRTDLL */ + /* Init pointers */ + stream = str; + + stream->_flag = streamflag; + stream->_cnt = 0; + stream->_tmpfname = stream->_base = stream->_ptr = NULL; + + stream->_file = filedes; + + return(stream); +} diff --git a/test_data/lots_of_files/_setargv.c b/test_data/lots_of_files/_setargv.c new file mode 100644 index 0000000..82050a6 --- /dev/null +++ b/test_data/lots_of_files/_setargv.c @@ -0,0 +1,15 @@ +/*** +*_setargv.c - Wildcard argv[] expansion +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* _setargv performs wildcard argv[] expansion +* +* NOTE: This stub module scheme is compatible with NT build +* procedure. +* +*******************************************************************************/ + +#define WILDCARD 1 +#include "stdargv.c" diff --git a/test_data/lots_of_files/_sftbuf.c b/test_data/lots_of_files/_sftbuf.c new file mode 100644 index 0000000..92cd913 --- /dev/null +++ b/test_data/lots_of_files/_sftbuf.c @@ -0,0 +1,164 @@ +/*** +*_sftbuf.c - temporary buffering initialization and flushing +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* temporary buffering initialization and flushing. if stdout/err is +* unbuffered, buffer it temporarily so that string is sent to kernel as +* a batch of chars, not char-at-a-time. if appropriate, make buffering +* permanent. +* +* [NOTE 1: These routines assume that the temporary buffering is only +* used for output. In particular, note that _stbuf() sets _IOWRT.] +* +* [NOTE 2: It is valid for this module to assign a value directly to +* _flag instead of simply twiddling bits since we are initializing the +* buffer data base.] +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdio.h> +#include <file2.h> +#include <io.h> +#include <internal.h> +#include <malloc.h> +#include <mtdll.h> +#include <dbgint.h> + +/* Buffer pointers for stdout and stderr */ +void *_stdbuf[2] = { NULL, NULL}; + +/*** +*int _stbuf(stream) - set temp buffer on stdout, stdprn, stderr +* +*Purpose: +* if stdout/stderr is still unbuffered, buffer it. +* this function works intimately with _ftbuf, and accompanies it in +* bracketing normally unbuffered output. these functions intended for +* library use only. +* +* Multi-thread: It is assumed that the caller has already aquired the +* stream lock. +* +*Entry: +* FILE *stream - stream to temp buffer +* +*Exit: +* returns 1 if buffer initialized, 0 if not +* sets fields in stdout or stderr to indicate buffering +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl _stbuf ( + FILE *str + ) +{ + FILE *stream; + int index; + + _ASSERTE(str != NULL); + + /* Init near stream pointer */ + stream = str; + + /* do nothing if not a tty device */ + if (!_isatty(_fileno(stream))) + return(0); + + /* Make sure stream is stdout/stderr and init _stdbuf index */ + if (stream == stdout) + index = 0; + else if (stream == stderr) + index = 1; + else + return(0); + +#ifndef CRTDLL + /* force library pre-termination procedure */ + _cflush++; +#endif /* CRTDLL */ + + /* Make sure the stream is not already buffered. */ + if (anybuf(stream)) + return(0); + + /* Allocate a buffer for this stream if we haven't done so yet. */ + if ( (_stdbuf[index] == NULL) && + ((_stdbuf[index]=_malloc_crt(_INTERNAL_BUFSIZ)) == NULL) ) { + /* Cannot allocate buffer. Use _charbuf this time */ + stream->_ptr = stream->_base = (void *)&(stream->_charbuf); + stream->_cnt = stream->_bufsiz = 2; + } + else { + /* Set up the buffer */ + stream->_ptr = stream->_base = _stdbuf[index]; + stream->_cnt = stream->_bufsiz = _INTERNAL_BUFSIZ; + } + + stream->_flag |= (_IOWRT | _IOYOURBUF | _IOFLRTN); + + return(1); +} + + +/*** +*void _ftbuf(flag, stream) - take temp buffering off a stream +* +*Purpose: +* If stdout/stderr is being buffered and it is a device, _flush and +* dismantle the buffer. if it's not a device, leave the buffering on. +* This function works intimately with _stbuf, and accompanies it in +* bracketing normally unbuffered output. these functions intended for +* library use only +* +* Multi-thread: It is assumed that the caller has already aquired the +* stream lock. +* +*Entry: +* int flag - a flag to tell whether to dismantle temp buffering on a +* stream +* FILE *stream - the stream +* +*Exit: +* no return value +* sets fields in stdout/stderr +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl _ftbuf ( + int flag, + FILE *str + ) +{ + FILE *stream; + + _ASSERTE(flag == 0 || flag == 1); + + /* Init near stream pointers */ + stream = str; + + if (flag) { + + if (stream->_flag & _IOFLRTN) { + + /* Flush the stream and tear down temp buffering. */ + _flush(stream); + stream->_flag &= ~(_IOYOURBUF | _IOFLRTN); + stream->_bufsiz = 0; + stream->_base = stream->_ptr = NULL; + } + + /* Note: If we expand the functionality of the _IOFLRTN bit to + include other streams, we may want to clear that bit here under + an 'else' clause (i.e., clear bit in the case that we leave the + buffer permanently assigned. Given our current use of the bit, + the extra code is not needed. */ + + } /* end flag = 1 */ +} diff --git a/test_data/lots_of_files/_strerr.c b/test_data/lots_of_files/_strerr.c new file mode 100644 index 0000000..e5051f0 --- /dev/null +++ b/test_data/lots_of_files/_strerr.c @@ -0,0 +1,153 @@ +/*** +*_strerr.c - routine for indexing into system error list +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Returns system error message index by errno; conforms to the +* XENIX standard, much compatibility with 1983 uniforum draft standard. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> +#include <errmsg.h> +#include <syserr.h> +#include <string.h> +#include <tchar.h> +#include <malloc.h> +#include <mtdll.h> +#include <dbgint.h> +#include <internal.h> + +/* Max length of message = user_string(94)+system_string+2 */ +/* [NOTE: The mthread error message buffer is shared by both strerror + and _strerror so must be the max length of both. */ +#define _ERRMSGLEN_ (94+_SYS_MSGMAX+2) + +#ifdef _UNICODE +#define _terrmsg _werrmsg +#else /* _UNICODE */ +#define _terrmsg _errmsg +#endif /* _UNICODE */ + + +/*** +*char *_strerror(message) - get system error message +* +*Purpose: +* builds an error message consisting of the users error message +* (the message parameter), followed by ": ", followed by the system +* error message (index through errno), followed by a newline. If +* message is NULL or a null string, returns a pointer to just +* the system error message. +* +*Entry: +* char *message - user's message to prefix system error message +* +*Exit: +* returns pointer to static memory containing error message. +* returns NULL if malloc() fails in multi-thread versions. +* +*Exceptions: +* +*******************************************************************************/ + +#ifdef _UNICODE +wchar_t * __cdecl __wcserror( +#else /* _UNICODE */ +char * __cdecl _strerror ( +#endif /* _UNICODE */ + const _TCHAR *message + ) +{ + const char *sysErrorMsg = NULL; + _TCHAR *bldmsg; + _ptiddata ptd = _getptd_noexit(); + if (!ptd) + return NULL; + + /* Use per thread buffer area (malloc space, if necessary) */ + /* [NOTE: This buffer is shared between _strerror and streror.] */ + + if ( (ptd->_terrmsg == NULL) && ((ptd->_terrmsg = + _calloc_crt(_ERRMSGLEN_, sizeof(_TCHAR))) == NULL) ) + return(NULL); + bldmsg = ptd->_terrmsg; + + /* Build the error message */ + + bldmsg[0] = '\0'; + + if (message && *message) { + // should leave space for ": \n\0" + _ERRCHECK(_tcsncat_s( bldmsg, _ERRMSGLEN_, message, _ERRMSGLEN_-4 )); + _ERRCHECK(_tcscat_s( bldmsg, _ERRMSGLEN_, _T(": "))); + } + + // We should have extra space for "\n\0" + sysErrorMsg = _get_sys_err_msg(errno); + +#ifdef _UNICODE + _ERRCHECK(mbstowcs_s(NULL, bldmsg + wcslen(bldmsg), _ERRMSGLEN_ - wcslen(bldmsg), sysErrorMsg, _ERRMSGLEN_ - wcslen(bldmsg) - 2)); +#else /* _UNICODE */ + _ERRCHECK(strncat_s(bldmsg, _ERRMSGLEN_, sysErrorMsg, _ERRMSGLEN_ - strlen(bldmsg) - 2)); +#endif /* _UNICODE */ + + _ERRCHECK(_tcscat_s( bldmsg, _ERRMSGLEN_, _T("\n"))); + return bldmsg; +} + +/*** +*errno_t _strerror_s(buffer, sizeInTChars, message) - get system error message +* +*Purpose: +* builds an error message consisting of the users error message +* (the message parameter), followed by ": ", followed by the system +* error message (index through errno), followed by a newline. If +* message is NULL or a null string, returns a pointer to just +* the system error message. +* +*Entry: +* TCHAR * buffer - Destination buffer. +* size_t sizeInTChars - Size of the destination buffer. +* TCHAR * message - user's message to prefix system error message +* +*Exit: +* The error code. +* +*Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +#define _MIN_MSG_LENGTH 5 + +#ifdef _UNICODE +errno_t __cdecl __wcserror_s( +#else /* _UNICODE */ +errno_t __cdecl _strerror_s( +#endif /* _UNICODE */ + TCHAR* buffer, + size_t sizeInTChars, + const _TCHAR *message + ) +{ + errno_t e = 0; + + /* validation section */ + _VALIDATE_RETURN_ERRCODE(buffer != NULL, EINVAL); + _VALIDATE_RETURN_ERRCODE(sizeInTChars > 0, EINVAL); + buffer[0] = '\0'; + + if (message && + *message && + _tcslen(message) < (sizeInTChars - 2 - _MIN_MSG_LENGTH)) + { + _ERRCHECK(_tcscpy_s(buffer, sizeInTChars, message)); + _ERRCHECK(_tcscat_s(buffer, sizeInTChars, _T(": "))); + } + + /* append the error message at the end of the buffer */ + return _tcserror_s(buffer + _tcslen(buffer), sizeInTChars - _tcslen(buffer), errno); +} diff --git a/test_data/lots_of_files/_tolower.c b/test_data/lots_of_files/_tolower.c new file mode 100644 index 0000000..4f3f22f --- /dev/null +++ b/test_data/lots_of_files/_tolower.c @@ -0,0 +1,159 @@ +/*** +*_tolower.c - convert character to lower case +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines _Tolower(). +* +*******************************************************************************/ + +#include <cruntime.h> +#include <ctype.h> +#include <stddef.h> +#include <xlocinfo.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> +#include <awint.h> +#include <stdlib.h> +#include <dbgint.h> +#include <yvals.h> + +/* remove macro definitions of _tolower() and tolower() + */ +#undef _tolower +#undef tolower + +/*** +*int _Tolower(c) - convert character to lower case +* +*Purpose: +* _Tolower() is a version of tolower with a locale argument. +* +*Entry: +* c - int value of character to be converted +* const _Ctypevec * = pointer to locale info +* +*Exit: +* returns int value of lower case representation of c +* +*Exceptions: +* +*******************************************************************************/ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Tolower ( + int c, + const _Ctypevec *ploc + ) +{ + int size; + unsigned char inbuffer[3]; + unsigned char outbuffer[3]; + + UINT codepage; + const wchar_t *locale_name; + + if (ploc == 0) + { + locale_name = ___lc_locale_name_func()[LC_CTYPE]; + codepage = ___lc_codepage_func(); + } + else + { + locale_name = ploc->_LocaleName; + codepage = ploc->_Page; + } + + if (locale_name == NULL) + { + if ( (c >= 'A') && (c <= 'Z') ) + c = c + ('a' - 'A'); + return c; + } + + /* if checking case of c does not require API call, do it */ + if ((unsigned)c < 256) + { + if (ploc == 0) + { + if (!isupper(c)) + { + return c; + } + } + else + { + if (!(ploc->_Table[c] & _UPPER)) + { + return c; + } + } + } + + /* convert int c to multibyte string */ + if (ploc == 0 ? _cpp_isleadbyte((c >> 8) & 0xff) + : (ploc->_Table[(c >> 8) & 0xff] & _LEADBYTE) != 0) + { + inbuffer[0] = (c >> 8 & 0xff); + inbuffer[1] = (unsigned char)c; + inbuffer[2] = 0; + size = 2; + } else { + inbuffer[0] = (unsigned char)c; + inbuffer[1] = 0; + size = 1; + } + + /* convert wide char to lowercase */ + if (0 == (size = __crtLCMapStringA(NULL, locale_name, LCMAP_LOWERCASE, + (const char *)inbuffer, size, (char *)outbuffer, 3, codepage, TRUE))) + { + return c; + } + + /* construct integer return value */ + if (size == 1) + return ((int)outbuffer[0]); + else + return ((int)outbuffer[1] | ((int)outbuffer[0] << 8)); + +} + + +/*** +*_Ctypevec _Getctype() - get ctype info for current locale +* +*Purpose: +* +*Entry: +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +_CRTIMP2_PURE _Ctypevec __CLRCALL_PURE_OR_CDECL _Getctype() +{ + /* get ctype info for current locale */ + _Ctypevec ctype; + + ctype._Page = ___lc_codepage_func(); + ctype._Table = (const short *)_calloc_crt(256, sizeof (*__pctype_func())); + if (ctype._Table != 0) + { + memcpy((void *)ctype._Table, __pctype_func(), 256 * sizeof (*__pctype_func())); + ctype._Delfl = 1; + } + else + { + ctype._Table = (const short *)__pctype_func(); + ctype._Delfl = 0; + } + ctype._LocaleName = ___lc_locale_name_func()[LC_COLLATE]; + if (ctype._LocaleName) + ctype._LocaleName = _wcsdup(ctype._LocaleName); + + return (ctype); +} diff --git a/test_data/lots_of_files/_toupper.c b/test_data/lots_of_files/_toupper.c new file mode 100644 index 0000000..d251b0e --- /dev/null +++ b/test_data/lots_of_files/_toupper.c @@ -0,0 +1,119 @@ +/*** +*_toupper.c - convert character to uppercase +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines _Toupper() +* +*******************************************************************************/ + +#include <cruntime.h> +#include <ctype.h> +#include <stddef.h> +#include <xlocinfo.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> +#include <awint.h> +#include <yvals.h> + +/* remove macro definitions of _toupper() and toupper() + */ +#undef _toupper +#undef toupper + +/*** +*int _Toupper(c) - convert character to uppercase +* +*Purpose: +* _Toupper() is a version of toupper with a locale argument. +* +*Entry: +* c - int value of character to be converted +* const _Ctypevec * = pointer to locale info +* +*Exit: +* returns int value of uppercase representation of c +* +*Exceptions: +* +*******************************************************************************/ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Toupper ( + int c, + const _Ctypevec *ploc + ) +{ + int size; + unsigned char inbuffer[3]; + unsigned char outbuffer[3]; + + const wchar_t *locale_name; + UINT codepage; + + if (ploc == 0) + { + locale_name = ___lc_locale_name_func()[LC_CTYPE]; + codepage = ___lc_codepage_func(); + } + else + { + locale_name = ploc->_LocaleName; + codepage = ploc->_Page; + } + + if (locale_name == NULL) + { + if ( (c >= 'a') && (c <= 'z') ) + c = c - ('a' - 'A'); + return c; + } + + /* if checking case of c does not require API call, do it */ + if ((unsigned)c < 256) + { + if (ploc == 0) + { + if (!islower(c)) + { + return c; + } + } + else + { + if (!(ploc->_Table[c] & _LOWER)) + { + return c; + } + } + } + + /* convert int c to multibyte string */ + if (ploc == 0 ? _cpp_isleadbyte((c >> 8) & 0xff) + : (ploc->_Table[(c >> 8) & 0xff] & _LEADBYTE) != 0) + { + inbuffer[0] = (c >> 8 & 0xff); + inbuffer[1] = (unsigned char)c; + inbuffer[2] = 0; + size = 2; + } else { + inbuffer[0] = (unsigned char)c; + inbuffer[1] = 0; + size = 1; + } + + /* convert wide char to uppercase */ + if (0 == (size = __crtLCMapStringA(NULL, locale_name, LCMAP_UPPERCASE, + (const char *)inbuffer, size, (char *)outbuffer, 3, codepage, TRUE))) + { + return c; + } + + /* construct integer return value */ + if (size == 1) + return ((int)outbuffer[0]); + else + return ((int)outbuffer[1] | ((int)outbuffer[0] << 8)); + +} diff --git a/test_data/lots_of_files/_wcserr.c b/test_data/lots_of_files/_wcserr.c new file mode 100644 index 0000000..8a1fa49 --- /dev/null +++ b/test_data/lots_of_files/_wcserr.c @@ -0,0 +1,26 @@ +/*** +*_wcserr.c - routine for indexing into system error list +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Returns system error message index by errno; conforms to the +* XENIX standard, much compatibility with 1983 uniforum draft standard. +* (wide char version of _strerror()) +* +*******************************************************************************/ + + +#ifndef _UNICODE +#define _UNICODE 1 +#endif /* _UNICODE */ + +#ifndef UNICODE +#define UNICODE 1 +#endif /* UNICODE */ + +#undef _MBCS /* UNICODE not _MBCS */ + +#include "_strerr.c" + + diff --git a/test_data/lots_of_files/_wctype.c b/test_data/lots_of_files/_wctype.c new file mode 100644 index 0000000..b074a97 --- /dev/null +++ b/test_data/lots_of_files/_wctype.c @@ -0,0 +1,284 @@ +/*** +*_wctype.c - function versions of wctype macros +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file provides function versions of the wide character +* classification and conversion macros in ctype.h. +* +*******************************************************************************/ + + +/*** +*wctype - Function versions of wctype macros +* +*Purpose: +* Function versions of the wide char macros in ctype.h, +* including isleadbyte and iswascii. In order to define +* these, we use a trick -- we undefine the macro so we can use the +* name in the function declaration, then re-include the file so +* we can use the macro in the definition part. +* +* Functions defined: +* iswalpha iswupper iswlower +* iswdigit iswxdigit iswspace +* iswpunct iswalnum iswprint +* iswgraph iswctrl iswascii +* iswblank isleadbyte +* +*Entry: +* wchar_t c = character to be tested +*Exit: +* returns non-zero = character is of the requested type +* 0 = character is NOT of the requested type +* +*Exceptions: +* None. +* +*******************************************************************************/ + +#include <ctype.h> +#include <cruntime.h> +#include <stdlib.h> +#include <locale.h> +#include <mbctype.h> +#include <mtdll.h> +#include <setlocal.h> + +extern "C" +{ +extern __inline int (__cdecl _isleadbyte_l) ( + int c, + _locale_t plocinfo + ) +{ + _LocaleUpdate _loc_update(plocinfo); + return (_loc_update.GetLocaleT()->locinfo->pctype[(unsigned char)(c)] & _LEADBYTE); +} + +extern __inline int (__cdecl isleadbyte) ( + int c + ) +{ + return _isleadbyte_l(c, NULL); +} + +extern __inline int (__cdecl _iswalpha_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswalpha(c); +} + +extern __inline int (__cdecl iswalpha) ( + wint_t c + ) +{ + return iswalpha(c); +} + +extern __inline int (__cdecl _iswupper_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswupper(c); +} + +extern __inline int (__cdecl iswupper) ( + wint_t c + ) +{ + return iswupper(c); +} + +extern __inline int (__cdecl _iswlower_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswlower(c); +} + +extern __inline int (__cdecl iswlower) ( + wint_t c + ) +{ + return iswlower(c); +} + +extern __inline int (__cdecl _iswdigit_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswdigit(c); +} + +extern __inline int (__cdecl iswdigit) ( + wint_t c + ) +{ + return iswdigit(c); +} + +extern __inline int (__cdecl _iswxdigit_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswxdigit(c); +} + +extern __inline int (__cdecl iswxdigit) ( + wint_t c + ) +{ + return iswxdigit(c); +} + +extern __inline int (__cdecl _iswspace_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswspace(c); +} + +extern __inline int (__cdecl iswspace) ( + wint_t c + ) +{ + return iswspace(c); +} + +extern __inline int (__cdecl _iswpunct_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswpunct(c); +} + +extern __inline int (__cdecl iswpunct) ( + wint_t c + ) +{ + return iswpunct(c); +} + +extern __inline int (__cdecl _iswblank_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswblank(c); +} + +extern __inline int (__cdecl iswblank) ( + wint_t c + ) +{ + return iswblank(c); +} + +extern __inline int (__cdecl _iswalnum_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswalnum(c); +} + +extern __inline int (__cdecl iswalnum) ( + wint_t c + ) +{ + return iswalnum(c); +} + +extern __inline int (__cdecl _iswprint_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswprint(c); +} + +extern __inline int (__cdecl iswprint) ( + wint_t c + ) +{ + return iswprint(c); +} + +extern __inline int (__cdecl _iswgraph_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswgraph(c); +} + +extern __inline int (__cdecl iswgraph) ( + wint_t c + ) +{ + return iswgraph(c); +} + +extern __inline int (__cdecl _iswcntrl_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return iswcntrl(c); +} + +extern __inline int (__cdecl iswcntrl) ( + wint_t c + ) +{ + return iswcntrl(c); +} + +extern __inline int (__cdecl iswascii) ( + wint_t c + ) +{ + return iswascii(c); +} + +extern __inline int (__cdecl _iswcsym_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return __iswcsym(c); +} + +extern __inline int (__cdecl __iswcsym) ( + wint_t c + ) +{ + return __iswcsym(c); +} + +extern __inline int (__cdecl _iswcsymf_l) ( + wint_t c, + _locale_t plocinfo + ) +{ + return __iswcsymf(c); +} + +extern __inline int (__cdecl __iswcsymf) ( + wint_t c + ) +{ + return __iswcsymf(c); +} + +} diff --git a/test_data/lots_of_files/_wopen.c b/test_data/lots_of_files/_wopen.c new file mode 100644 index 0000000..ce56eed --- /dev/null +++ b/test_data/lots_of_files/_wopen.c @@ -0,0 +1,21 @@ +/*** +*_wopen.c - open a stream, with string mode (wchar_t version) +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _wopenfile() - opens a stream, with string arguments for mode +* +*******************************************************************************/ + + +#ifndef _UNICODE +#define _UNICODE 1 +#endif /* _UNICODE */ + +#ifndef UNICODE +#define UNICODE 1 +#endif /* UNICODE */ + +#include "_open.c" + diff --git a/test_data/lots_of_files/_wstargv.c b/test_data/lots_of_files/_wstargv.c new file mode 100644 index 0000000..e75acd2 --- /dev/null +++ b/test_data/lots_of_files/_wstargv.c @@ -0,0 +1,30 @@ +/*** +*_wstargv.c - Wildcard argv[] expansion (wchar_t version) +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* _wsetargv performs wildcard argv[] expansion +* +* NOTE: This stub module scheme is compatible with NT build +* procedure. +* +*******************************************************************************/ + + +#define WPRFLAG 1 + +#ifndef _UNICODE +#define _UNICODE 1 +#endif /* _UNICODE */ + +#ifndef UNICODE +#define UNICODE 1 +#endif /* UNICODE */ + +#undef _MBCS /* UNICODE not _MBCS */ + +#define WILDCARD 1 + +#include "stdargv.c" + diff --git a/test_data/lots_of_files/a_cmp.c b/test_data/lots_of_files/a_cmp.c new file mode 100644 index 0000000..20fd857 --- /dev/null +++ b/test_data/lots_of_files/a_cmp.c @@ -0,0 +1,279 @@ +/*** +*a_cmp.c - A version of CompareString. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Use either CompareStringA or CompareStringW depending on which is +* available +* +*******************************************************************************/ + +#include <cruntime.h> +#include <internal.h> +#include <dbgint.h> +#include <stdlib.h> +#include <locale.h> +#include <awint.h> +#include <dbgint.h> +#include <malloc.h> +#include <awint.h> +#include <mtdll.h> +#include <setlocal.h> + +/*** +*int __cdecl strncnt - count characters in a string, up to n. +* +*Purpose: +* Internal local support function. Counts characters in string before NULL. +* If NULL not found in n chars, then return n. +* +*Entry: +* const char *string - start of string +* int n - byte count +* +*Exit: +* returns number of bytes from start of string to +* NULL (exclusive), up to n. +* +*Exceptions: +* +*******************************************************************************/ + +static int __cdecl strncnt ( + const char *string, + int cnt + ) +{ + int n = cnt; + char *cp = (char *)string; + + while (n-- && *cp) + cp++; + + return cnt - n - 1; +} + +/*** +*int __cdecl __crtCompareStringA - Get type information about an ANSI string. +* +*Purpose: +* Internal support function. Assumes info in ANSI string format. Tries +* to use NLS API call CompareStringA if available and uses CompareStringW +* if it must. If neither are available it fails and returns 0. +* +*Entry: +* LPCWSTR LocaleName - locale context for the comparison. +* DWORD dwCmpFlags - see NT\Chicago docs +* LPCSTR lpStringn - multibyte string to be compared +* int cchCountn - char (byte) count (NOT including NULL) +* (-1 if NULL terminated) +* int code_page - for MB/WC conversion. If 0, use __lc_codepage +* +*Exit: +* Success: 1 - if lpString1 < lpString2 +* 2 - if lpString1 == lpString2 +* 3 - if lpString1 > lpString2 +* Failure: 0 +* +*Exceptions: +* +*******************************************************************************/ + +static int __cdecl __crtCompareStringA_stat( + _locale_t plocinfo, + LPCWSTR LocaleName, + DWORD dwCmpFlags, + LPCSTR lpString1, + int cchCount1, + LPCSTR lpString2, + int cchCount2, + int code_page + ) +{ + /* + * CompareString will compare past NULL. Must find NULL if in string + * before cchCountn chars. + */ + + if (cchCount1 > 0) + cchCount1 = strncnt(lpString1, cchCount1); + else if ( cchCount1 < -1 ) + return FALSE; + if (cchCount2 > 0) + cchCount2 = strncnt(lpString2, cchCount2); + else if ( cchCount2 < -1 ) + return FALSE; + + + int buff_size1; + int buff_size2; + wchar_t *wbuffer1; + wchar_t *wbuffer2; + int retcode = 0; + + /* + * Use __lc_codepage for conversion if code_page not specified + */ + + if (0 == code_page) + code_page = plocinfo->locinfo->lc_codepage; + + /* + * Special case: at least one count is zero + */ + + if (!cchCount1 || !cchCount2) + { + unsigned char *cp; // char pointer + CPINFO cpInfo; // struct for use with GetCPInfo + + /* both strings zero */ + if (cchCount1 == cchCount2) + return 2; + + /* string 1 greater */ + if (cchCount2 > 1) + return 1; + + /* string 2 greater */ + if (cchCount1 > 1) + return 3; + + /* + * one has zero count, the other has a count of one + * - if the one count is a naked lead byte, the strings are equal + * - otherwise it is a single character and they are unequal + */ + + if (GetCPInfo(code_page, &cpInfo) == FALSE) + return 0; + + _ASSERTE(cchCount1==0 && cchCount2==1 || cchCount1==1 && cchCount2==0); + + /* string 1 has count of 1 */ + if (cchCount1 > 0) + { + if (cpInfo.MaxCharSize < 2) + return 3; + + for ( cp = (unsigned char *)cpInfo.LeadByte ; + cp[0] && cp[1] ; + cp += 2 ) + if ( (*(unsigned char *)lpString1 >= cp[0]) && + (*(unsigned char *)lpString1 <= cp[1]) ) + return 2; + + return 3; + } + + /* string 2 has count of 1 */ + if (cchCount2 > 0) + { + if (cpInfo.MaxCharSize < 2) + return 1; + + for ( cp = (unsigned char *)cpInfo.LeadByte ; + cp[0] && cp[1] ; + cp += 2 ) + if ( (*(unsigned char *)lpString2 >= cp[0]) && + (*(unsigned char *)lpString2 <= cp[1]) ) + return 2; + + return 1; + } + } + + /* + * Convert strings and return the requested information. + */ + + /* find out how big a buffer we need (includes NULL if any) */ + if ( 0 == (buff_size1 = MultiByteToWideChar( code_page, + MB_PRECOMPOSED | + MB_ERR_INVALID_CHARS, + lpString1, + cchCount1, + NULL, + 0 )) ) + return 0; + + /* allocate enough space for chars */ + wbuffer1 = (wchar_t *)_calloca( buff_size1, sizeof(wchar_t) ); + if ( wbuffer1 == NULL ) { + return 0; + } + + /* do the conversion */ + if ( 0 == MultiByteToWideChar( code_page, + MB_PRECOMPOSED, + lpString1, + cchCount1, + wbuffer1, + buff_size1 ) ) + goto error_cleanup; + + /* find out how big a buffer we need (includes NULL if any) */ + if ( 0 == (buff_size2 = MultiByteToWideChar( code_page, + MB_PRECOMPOSED | + MB_ERR_INVALID_CHARS, + lpString2, + cchCount2, + NULL, + 0 )) ) + goto error_cleanup; + + /* allocate enough space for chars */ + wbuffer2 = (wchar_t *)_calloca( buff_size2, sizeof(wchar_t) ); + if ( wbuffer2 == NULL ) { + goto error_cleanup; + } + + /* do the conversion */ + if ( 0 != MultiByteToWideChar( code_page, + MB_PRECOMPOSED, + lpString2, + cchCount2, + wbuffer2, + buff_size2 ) ) + { + retcode = __crtCompareStringEx( LocaleName, + dwCmpFlags, + wbuffer1, + buff_size1, + wbuffer2, + buff_size2); + } + + _freea(wbuffer2); + +error_cleanup: + _freea(wbuffer1); + + return retcode; +} + +extern "C" int __cdecl __crtCompareStringA( + _locale_t plocinfo, + LPCWSTR LocaleName, + DWORD dwCmpFlags, + LPCSTR lpString1, + int cchCount1, + LPCSTR lpString2, + int cchCount2, + int code_page + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return __crtCompareStringA_stat( + _loc_update.GetLocaleT(), + LocaleName, + dwCmpFlags, + lpString1, + cchCount1, + lpString2, + cchCount2, + code_page + ); +} diff --git a/test_data/lots_of_files/a_env.c b/test_data/lots_of_files/a_env.c new file mode 100644 index 0000000..ed0e639 --- /dev/null +++ b/test_data/lots_of_files/a_env.c @@ -0,0 +1,96 @@ +/*** +*a_env.c - A version of GetEnvironmentStrings. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Use GetEnvironmentStringsW if available, otherwise use A version. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <internal.h> +#include <stdlib.h> +#include <setlocal.h> +#include <awint.h> +#include <dbgint.h> + +/*** +*LPVOID __cdecl __crtGetEnvironmentStringsA - Get normal environment block +* +*Purpose: +* Internal support function. Since GetEnvironmentStrings returns in OEM +* and we want ANSI (note that GetEnvironmentVariable returns ANSI!) and +* SetFileApistoAnsi() does not affect it, we have no choice but to +* obtain the block in wide character and convert to ANSI. +* +*Entry: +* VOID +* +*Exit: +* LPVOID - pointer to environment block +* +*Exceptions: +* +*******************************************************************************/ + +LPVOID __cdecl __crtGetEnvironmentStringsA( + VOID + ) +{ + wchar_t *wEnv; + wchar_t *wTmp; + char *aEnv = NULL; + int nSizeW; + int nSizeA; + + /* obtain wide environment block */ + + if ( NULL == (wEnv = GetEnvironmentStringsW()) ) + return NULL; + + /* look for double null that indicates end of block */ + wTmp = wEnv; + while ( *wTmp != L'\0' ) { + if ( *++wTmp == L'\0' ) + wTmp++; + } + + /* calculate total size of block, including all nulls */ + nSizeW = (int)(wTmp - wEnv + 1); + + /* find out how much space needed for multi-byte environment */ + nSizeA = WideCharToMultiByte( CP_ACP, + 0, + wEnv, + nSizeW, + NULL, + 0, + NULL, + NULL ); + + /* allocate space for multi-byte string */ + if ( (nSizeA == 0) || + ((aEnv = (char *)_malloc_crt(nSizeA)) == NULL) ) + { + FreeEnvironmentStringsW( wEnv ); + return NULL; + } + + /* do the conversion */ + if ( !WideCharToMultiByte( CP_ACP, + 0, + wEnv, + nSizeW, + aEnv, + nSizeA, + NULL, + NULL ) ) + { + _free_crt( aEnv ); + aEnv = NULL; + } + + FreeEnvironmentStringsW( wEnv ); + return aEnv; +} diff --git a/test_data/lots_of_files/a_loc.c b/test_data/lots_of_files/a_loc.c new file mode 100644 index 0000000..ffcf3c2 --- /dev/null +++ b/test_data/lots_of_files/a_loc.c @@ -0,0 +1,130 @@ +/*** +*a_loc.c - A versions of GetLocaleInfo. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Use either GetLocaleInfoA or GetLocaleInfoW depending on which is +* available +* +*******************************************************************************/ + +#include <cruntime.h> +#include <internal.h> +#include <stdlib.h> +#include <awint.h> +#include <dbgint.h> +#include <malloc.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> + +/*** +*int __cdecl __crtGetLocaleInfoA - Get locale info and return it as an ASCII +* string +* +*Purpose: +* Internal support function. Assumes info in ANSI string format. Tries +* to use NLS API call GetLocaleInfoA if available (Chicago) and uses +* GetLocaleInfoA if it must (NT). If neither are available it fails and +* returns 0. +* +*Entry: +* LPCWSTR LocaleName - locale context for the comparison. +* LCTYPE LCType - see NT\Chicago docs +* LPSTR lpLCData - pointer to memory to return data +* int cchData - char (byte) count of buffer (including NULL) +* (if 0, lpLCData is not referenced, size needed +* is returned) +* int code_page - for MB/WC conversion. If 0, use __lc_codepage +* +*Exit: +* Success: the number of characters copied (including NULL). +* Failure: 0 +* +*Exceptions: +* +*******************************************************************************/ + +static int __cdecl __crtGetLocaleInfoA_stat( + _locale_t plocinfo, + const wchar_t* LocaleName, + LCTYPE LCType, + LPSTR lpLCData, + int cchData + ) +{ + int retval = 0; + int code_page; + int buff_size; + wchar_t *wbuffer; + + /* + * Use __lc_codepage for conversion + */ + + code_page = plocinfo->locinfo->lc_codepage; + + /* find out how big buffer needs to be */ + if (0 == (buff_size = __crtGetLocaleInfoEx(LocaleName, LCType, NULL, 0))) + return 0; + + /* allocate buffer */ + wbuffer = (wchar_t *)_calloca( buff_size, sizeof(wchar_t) ); + if ( wbuffer == NULL ) { + return 0; + } + + /* get the info in wide format */ + if (0 == __crtGetLocaleInfoEx(LocaleName, LCType, wbuffer, buff_size)) + goto error_cleanup; + + /* convert from Wide Char to ANSI */ + if (0 == cchData) + { + /* convert into local buffer */ + retval = WideCharToMultiByte( code_page, + 0, + wbuffer, + -1, + NULL, + 0, + NULL, + NULL ); + } + else { + /* convert into user buffer */ + retval = WideCharToMultiByte( code_page, + 0, + wbuffer, + -1, + lpLCData, + cchData, + NULL, + NULL ); + } + +error_cleanup: + _freea(wbuffer); + + return retval; +} + +extern "C" int __cdecl __crtGetLocaleInfoA( + _locale_t plocinfo, + const wchar_t* LocaleName, + LCTYPE LCType, + LPSTR lpLCData, + int cchData + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return __crtGetLocaleInfoA_stat( + _loc_update.GetLocaleT(), + LocaleName, + LCType, + lpLCData, + cchData + ); +} diff --git a/test_data/lots_of_files/a_map.c b/test_data/lots_of_files/a_map.c new file mode 100644 index 0000000..ff9940b --- /dev/null +++ b/test_data/lots_of_files/a_map.c @@ -0,0 +1,272 @@ +/*** +*a_map.c - A version of LCMapString. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Use either LCMapStringA or LCMapStringW depending on which is available +* +*******************************************************************************/ + +#include <cruntime.h> +#include <internal.h> +#include <stdlib.h> +#include <locale.h> +#include <awint.h> +#include <dbgint.h> +#include <malloc.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> + +/*** +*int __cdecl strncnt - count characters in a string, up to n. +* +*Purpose: +* Internal local support function. Counts characters in string before +* null. If null is not found in n chars, then return n. +* +*Entry: +* const char *string - start of string +* int n - byte count +* +*Exit: +* returns number of bytes from start of string to +* null (exclusive), up to n. +* +*Exceptions: +* +*******************************************************************************/ + +static int __cdecl strncnt ( + const char *string, + int cnt + ) +{ + int n = cnt; + char *cp = (char *)string; + + while (n-- && *cp) + cp++; + + return cnt - n - 1; +} + + +/*** +*int __cdecl __crtLCMapStringA - Get type information about an ANSI string. +* +*Purpose: +* Internal support function. Assumes info in ANSI string format. Tries +* to use NLS API call LCMapStringA if available and uses LCMapStringW +* if it must. If neither are available it fails and returns 0. +* +*Entry: +* LPCWSTR LocaleName - locale context for the comparison. +* DWORD dwMapFlags - see NT\Chicago docs +* LPCSTR lpSrcStr - pointer to string to be mapped +* int cchSrc - wide char (word) count of input string +* (including NULL if any) +* (-1 if NULL terminated) +* LPSTR lpDestStr - pointer to memory to store mapping +* int cchDest - char (byte) count of buffer (including NULL) +* int code_page - for MB/WC conversion. If 0, use __lc_codepage +* BOOL bError - TRUE if MB_ERR_INVALID_CHARS set on call to +* MultiByteToWideChar when GetStringTypeW used. +* +*Exit: +* Success: number of chars written to lpDestStr (including NULL) +* Failure: 0 +* +*Exceptions: +* +*******************************************************************************/ + +static int __cdecl __crtLCMapStringA_stat( + _locale_t plocinfo, + LPCWSTR LocaleName, + DWORD dwMapFlags, + LPCSTR lpSrcStr, + int cchSrc, + LPSTR lpDestStr, + int cchDest, + int code_page, + BOOL bError + ) +{ + /* + * LCMapString will map past NULL. Must find NULL if in string + * before cchSrc characters. + */ + if (cchSrc > 0) { + int cchSrcCnt = strncnt(lpSrcStr, cchSrc); + /* + * Include NULL in cchSrc if lpSrcStr terminated within cchSrc bytes. + */ + if (cchSrcCnt < cchSrc) { + cchSrc = cchSrcCnt + 1; + } else { + cchSrc = cchSrcCnt; + } + } + + int retval = 0; + int inbuff_size; + int outbuff_size; + wchar_t *inwbuffer = NULL; + wchar_t *outwbuffer = NULL; + + /* + * Convert string and return the requested information. Note that + * we are converting to a wide string so there is not a + * one-to-one correspondence between number of wide chars in the + * input string and the number of *bytes* in the buffer. However, + * there had *better be* a one-to-one correspondence between the + * number of wide characters and the number of multibyte characters + * or the resulting mapped string will be worthless to the user. + */ + + /* + * Use __lc_codepage for conversion if code_page not specified + */ + + if (0 == code_page) + code_page = plocinfo->locinfo->lc_codepage; + + /* find out how big a buffer we need (includes NULL if any) */ + if ( 0 == (inbuff_size = + MultiByteToWideChar( code_page, + bError ? MB_PRECOMPOSED | + MB_ERR_INVALID_CHARS : + MB_PRECOMPOSED, + lpSrcStr, + cchSrc, + NULL, + 0 )) ) + return 0; + + /* allocate enough space for wide chars */ + inwbuffer = (wchar_t *)_calloca( inbuff_size, sizeof(wchar_t) ); + if ( inwbuffer == NULL ) { + return 0; + } + + /* do the conversion */ + if ( 0 == MultiByteToWideChar( code_page, + MB_PRECOMPOSED, + lpSrcStr, + cchSrc, + inwbuffer, + inbuff_size) ) + goto error_cleanup; + + /* get size required for string mapping */ + if ( 0 == (retval = __crtLCMapStringEx( LocaleName, + dwMapFlags, + inwbuffer, + inbuff_size, + NULL, + 0)) ) + goto error_cleanup; + + if (dwMapFlags & LCMAP_SORTKEY) { + /* retval is size in BYTES */ + + if (0 != cchDest) { + + if (retval > cchDest) + goto error_cleanup; + + /* do string mapping */ + if ( 0 == __crtLCMapStringEx( LocaleName, + dwMapFlags, + inwbuffer, + inbuff_size, + (LPWSTR)lpDestStr, + cchDest) ) + goto error_cleanup; + } + } + else { + /* retval is size in wide chars */ + + outbuff_size = retval; + + /* allocate enough space for wide chars (includes NULL if any) */ + outwbuffer = (wchar_t *)_calloca( outbuff_size, sizeof(wchar_t) ); + if ( outwbuffer == NULL ) { + goto error_cleanup; + } + + /* do string mapping */ + if ( 0 == __crtLCMapStringEx( LocaleName, + dwMapFlags, + inwbuffer, + inbuff_size, + outwbuffer, + outbuff_size) ) + goto error_cleanup; + + if (0 == cchDest) { + /* get size required */ + if ( 0 == (retval = + WideCharToMultiByte( code_page, + 0, + outwbuffer, + outbuff_size, + NULL, + 0, + NULL, + NULL )) ) + goto error_cleanup; + } + else { + /* convert mapping */ + if ( 0 == (retval = + WideCharToMultiByte( code_page, + 0, + outwbuffer, + outbuff_size, + lpDestStr, + cchDest, + NULL, + NULL )) ) + goto error_cleanup; + } + } + +error_cleanup: + if ( outwbuffer != NULL ) + _freea(outwbuffer); + + _freea(inwbuffer); + + return retval; +} + +extern "C" int __cdecl __crtLCMapStringA( + _locale_t plocinfo, + LPCWSTR LocaleName, + DWORD dwMapFlags, + LPCSTR lpSrcStr, + int cchSrc, + LPSTR lpDestStr, + int cchDest, + int code_page, + BOOL bError + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return __crtLCMapStringA_stat( + _loc_update.GetLocaleT(), + LocaleName, + dwMapFlags, + lpSrcStr, + cchSrc, + lpDestStr, + cchDest, + code_page, + bError + ); +} diff --git a/test_data/lots_of_files/a_str.c b/test_data/lots_of_files/a_str.c new file mode 100644 index 0000000..272d38c --- /dev/null +++ b/test_data/lots_of_files/a_str.c @@ -0,0 +1,141 @@ +/*** +*a_str.c - A version of GetStringType. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Use either GetStringTypeA or GetStringTypeW depending on which is +* unstubbed. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <internal.h> +#include <stdlib.h> +#include <locale.h> +#include <awint.h> +#include <dbgint.h> +#include <malloc.h> +#include <awint.h> +#include <mtdll.h> +#include <setlocal.h> + +/*** +*int __cdecl __crtGetStringTypeA - Get type information about an ANSI string. +* +*Purpose: +* Internal support function. Assumes info in ANSI string format. Tries +* to use NLS API call GetStringTypeA if available and uses GetStringTypeW +* if it must. If neither are available it fails and returns FALSE. +* +*Entry: +* DWORD dwInfoType - see NT\Chicago docs +* LPCSTR lpSrcStr - char (byte) string for which character types +* are requested +* int cchSrc - char (byte) count of lpSrcStr (including NULL +* if any) +* LPWORD lpCharType - word array to receive character type information +* (must be twice the size of lpSrcStr) +* int code_page - for MB/WC conversion. If 0, use __lc_codepage +* BOOL bError - TRUE if MB_ERR_INVALID_CHARS set on call to +* MultiByteToWideChar when GetStringTypeW used. +* +*Exit: +* Success: TRUE +* Failure: FALSE +* +*Exceptions: +* +*******************************************************************************/ + +static BOOL __cdecl __crtGetStringTypeA_stat( + _locale_t plocinfo, + DWORD dwInfoType, + LPCSTR lpSrcStr, + int cchSrc, + LPWORD lpCharType, + int code_page, + BOOL bError + ) +{ + int retval1; + int buff_size; + wchar_t *wbuffer; + BOOL retval2 = FALSE; + + /* + * Convert string and return the requested information. Note that + * we are converting to a wide character string so there is not a + * one-to-one correspondence between number of multibyte chars in the + * input string and the number of wide chars in the buffer. However, + * there had *better be* a one-to-one correspondence between the + * number of multibyte characters and the number of WORDs in the + * return buffer. + */ + + /* + * Use __lc_codepage for conversion if code_page not specified + */ + + if (0 == code_page) + code_page = plocinfo->locinfo->lc_codepage; + + /* find out how big a buffer we need */ + if ( 0 == (buff_size = MultiByteToWideChar( code_page, + bError ? + MB_PRECOMPOSED | + MB_ERR_INVALID_CHARS + : MB_PRECOMPOSED, + lpSrcStr, + cchSrc, + NULL, + 0 )) ) + return FALSE; + + /* allocate enough space for wide chars */ + wbuffer = (wchar_t *)_calloca( sizeof(wchar_t), buff_size ); + if ( wbuffer == NULL ) { + return FALSE; + } + (void)memset( wbuffer, 0, sizeof(wchar_t) * buff_size ); + + /* do the conversion */ + if ( 0 != (retval1 = MultiByteToWideChar( code_page, + MB_PRECOMPOSED, + lpSrcStr, + cchSrc, + wbuffer, + buff_size )) ) + /* obtain result */ + retval2 = GetStringTypeW( dwInfoType, + wbuffer, + retval1, + lpCharType ); + + _freea(wbuffer); + + return retval2; +} + +extern "C" BOOL __cdecl __crtGetStringTypeA( + _locale_t plocinfo, + DWORD dwInfoType, + LPCSTR lpSrcStr, + int cchSrc, + LPWORD lpCharType, + int code_page, + BOOL bError + ) +{ + _LocaleUpdate _loc_update(plocinfo); + + return __crtGetStringTypeA_stat( + _loc_update.GetLocaleT(), + dwInfoType, + lpSrcStr, + cchSrc, + lpCharType, + code_page, + bError + ); +} diff --git a/test_data/lots_of_files/abort.c b/test_data/lots_of_files/abort.c new file mode 100644 index 0000000..7f4dcc1 --- /dev/null +++ b/test_data/lots_of_files/abort.c @@ -0,0 +1,125 @@ +/*** +*abort.c - abort a program by raising SIGABRT +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines abort() - print a message and raise SIGABRT. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> +#include <internal.h> +#include <awint.h> +#include <rterr.h> +#include <signal.h> +#include <oscalls.h> +#include <mtdll.h> +#include <dbgint.h> + +#ifdef _DEBUG +#define _INIT_ABORT_BEHAVIOR _WRITE_ABORT_MSG +#else /* _DEBUG */ +#define _INIT_ABORT_BEHAVIOR _CALL_REPORTFAULT +#endif /* _DEBUG */ + +unsigned int __abort_behavior = _INIT_ABORT_BEHAVIOR; + +/*** +*void abort() - abort the current program by raising SIGABRT +* +*Purpose: +* print out an abort message and raise the SIGABRT signal. If the user +* hasn't defined an abort handler routine, terminate the program +* with exit status of 3 without cleaning up. +* +* Multi-thread version does not raise SIGABRT -- this isn't supported +* under multi-thread. +* +*Entry: +* None. +* +*Exit: +* Does not return. +* +*Uses: +* +*Exceptions: +* +*******************************************************************************/ + +void __cdecl abort ( + void + ) +{ + _PHNDLR sigabrt_act = SIG_DFL; + +#ifdef _DEBUG + if (__abort_behavior & _WRITE_ABORT_MSG) + { + /* write the abort message */ + _NMSG_WRITE(_RT_ABORT); + } +#endif /* _DEBUG */ + + + /* Check if the user installed a handler for SIGABRT. + * We need to read the user handler atomically in the case + * another thread is aborting while we change the signal + * handler. + */ + sigabrt_act = __get_sigabrt(); + if (sigabrt_act != SIG_DFL) + { + raise(SIGABRT); + } + + /* If there is no user handler for SIGABRT or if the user + * handler returns, then exit from the program anyway + */ + + if (__abort_behavior & _CALL_REPORTFAULT) + { +#if defined (_M_ARM) || defined (_CRT_APP) + __fastfail(FAST_FAIL_FATAL_APP_EXIT); +#else /* defined (_M_ARM) || defined (_CRT_APP) */ + if (IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE)) + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + + _call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE); +#endif /* defined (_M_ARM) || defined (_CRT_APP) */ + } + + + /* If we don't want to call ReportFault, then we call _exit(3), which is the + * same as invoking the default handler for SIGABRT + */ + + + _exit(3); +} + +/*** +*unsigned int _set_abort_behavior(unsigned int, unsigned int) - set the behavior on abort +* +*Purpose: +* +*Entry: +* unsigned int flags - the flags we want to set +* unsigned int mask - mask the flag values +* +*Exit: +* Return the old behavior flags +* +*Exceptions: +* None +* +*******************************************************************************/ + +unsigned int __cdecl _set_abort_behavior(unsigned int flags, unsigned int mask) +{ + unsigned int oldflags = __abort_behavior; + __abort_behavior = oldflags & (~mask) | flags & mask; + return oldflags; +} diff --git a/test_data/lots_of_files/abs.c b/test_data/lots_of_files/abs.c new file mode 100644 index 0000000..c777b07 --- /dev/null +++ b/test_data/lots_of_files/abs.c @@ -0,0 +1,44 @@ +/*** +*abs.c - find absolute value +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines abs() - find the absolute value of an int. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> + +#pragma function(abs, _abs64) + +/*** +*int abs(number) - find absolute value of number +* +*Purpose: +* Returns the absolute value of number (if number >= 0, returns number, +* else returns -number). +* +*Entry: +* int number - number to find absolute value of +* +*Exit: +* returns the aboslute value of number +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl abs ( + int number + ) +{ + return( number>=0 ? number : -number ); +} +__int64 __cdecl _abs64( + __int64 num + ) +{ + return (num >=0 ? num : -num); +} diff --git a/test_data/lots_of_files/access.c b/test_data/lots_of_files/access.c new file mode 100644 index 0000000..cfc0389 --- /dev/null +++ b/test_data/lots_of_files/access.c @@ -0,0 +1,135 @@ +/*** +*access.c - access function +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file has the _access() function which checks on file accessability. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <io.h> +#include <oscalls.h> +#include <stdlib.h> +#include <errno.h> +#include <msdos.h> +#include <internal.h> +#include <tchar.h> +#include <malloc.h> +#include <dbgint.h> + +/*** +*int _access(path, amode) - check whether file can be accessed under mode +* +*Purpose: +* Checks to see if the specified file exists and can be accessed +* in the given mode. +* +*Entry: +* _TSCHAR *path - pathname +* int amode - access mode +* (0 = exist only, 2 = write, 4 = read, 6 = read/write) +* +*Exit: +* returns 0 if file has given mode +* returns -1 and sets errno if file does not have given mode or +* does not exist +* +*Exceptions: +* +*******************************************************************************/ + +int __cdecl _taccess ( + const _TSCHAR *path, + int amode + ) +{ + errno_t e; + e = _taccess_s(path,amode); + + return e ? -1 : 0 ; +} + +/*** +*errno_t _access_s(path, amode) - check whether file can be accessed under mode +* +*Purpose: +* Checks to see if the specified file exists and can be accessed +* in the given mode. +* +*Entry: +* _TSCHAR *path - pathname +* int amode - access mode +* (0 = exist only, 2 = write, 4 = read, 6 = read/write) +* +*Exit: +* returns 0 if file has given mode +* returns errno_t for any other errors +* +*Exceptions: +* +*******************************************************************************/ +#ifndef _UNICODE + +errno_t __cdecl _access_s ( + const char *path, + int amode + ) +{ + wchar_t* pathw = NULL; + errno_t retval; + + if (path) + { + if (!__copy_path_to_wide_string(path, &pathw)) + return errno; + } + + /* call the wide-char variant */ + retval = _waccess_s(pathw, amode); + + _free_crt(pathw); /* _free_crt leaves errno alone if everything completes as expected */ + + return retval; +} + +#else /* _UNICODE */ + +errno_t __cdecl _waccess_s ( + const wchar_t *path, + int amode + ) +{ + + WIN32_FILE_ATTRIBUTE_DATA attr_data; + + _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE( (path != NULL), EINVAL); + _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE( ((amode & (~6)) == 0), EINVAL); + + if (!GetFileAttributesExW(path, GetFileExInfoStandard, (void*) &attr_data)) { + /* error occured -- map error code and return */ + _dosmaperr(GetLastError()); + return errno; + } + + if(attr_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + /* All directories have read & write access */ + return 0; + } + + /* no error; see if returned premission settings OK */ + if ( (attr_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && (amode & 2) ) { + /* no write permission on file, return error */ + _doserrno = E_access; + errno = EACCES; + return errno; + } + else + /* file exists and has requested permission setting */ + return 0; + +} + +#endif /* _UNICODE */ diff --git a/test_data/lots_of_files/agents.h b/test_data/lots_of_files/agents.h new file mode 100644 index 0000000..2ca59df --- /dev/null +++ b/test_data/lots_of_files/agents.h @@ -0,0 +1,13456 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* agents.h +* +* Main public header file for ConcRT's asynchronous agents layer. This is the only header file a +* C++ program must include to use asynchronous agents. +* +* The core runtime, Parallel Patterns Library (PPL), and resource manager are defined in separate header files. +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include <crtdefs.h> +#include <concrt.h> +#include <stdexcept> +#include <functional> +#include <tuple> +#include <type_traits> +#include <vector> +#include <concurrent_queue.h> + +#define _AGENTS_H + +#pragma pack(push,_CRT_PACKING) +#pragma warning(push) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation +#pragma warning(disable: 4702) // Unreachable code - needed for retail version code path +// Forward declarations + +/// <summary> +/// The <c>Concurrency</c> namespace provides classes and functions that provide access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ +/// <summary> +/// Each message instance has an identity that follows it as it is +/// cloned and passed between messaging components. This cannot be the +/// address of the message object. +/// </summary> +/**/ +typedef __int32 runtime_object_identity; + +/// <summary> +/// A lock holder that acquires a non-reentrant lock on instantiation and releases +/// it on destruction. +/// </summary> +/**/ +typedef ::Concurrency::details::_NonReentrantPPLLock::_Scoped_lock _NR_lock; + +/// <summary> +/// A lock holder that acquires a reentrant lock on instantiation and releases +/// it on destruction +/// </summary> +/**/ +typedef ::Concurrency::details::_ReentrantPPLLock::_Scoped_lock _R_lock; + + +//*************************************************************************** +// Internal namespace: +// +// Concurrency::details contains definitions to support routines in the public namespaces and macros. +// Clients should not directly interact with this namespace. +//*************************************************************************** + +namespace details +{ + //************************************************************************** + // Core Messaging Support: + //************************************************************************** + + // + // A base class to derive from that keeps unique IDs on its derived classes + // + class _Runtime_object : public _AllocBase + { + public: + // Creates a new runtime object. + _CRTIMP2 _Runtime_object(); + + // Creates a runtime object from an identity. + _CRTIMP2 _Runtime_object(::Concurrency::runtime_object_identity _Id); + + // Gets the runtime object identity. + virtual ::Concurrency::runtime_object_identity _GetId() const + { + return _M_id; + } + + protected: + // The runtime object identity. + ::Concurrency::runtime_object_identity _M_id; + }; + + // A queue used to hold the messages for the messaging blocks + template<class _Message> + class _Queue : public _AllocBase + { + protected: + // A pointer to the head of the queue. + _Message * _M_pHead; + + // A pointer to a pointer to the tail of the queue. + _Message ** _M_ppTail; + + // The number of elements presently stored in the queue. + size_t _M_count; + + public: + typedef typename _Message type; + + // Create a Queue + _Queue() : _M_pHead(NULL), _M_ppTail(&_M_pHead), _M_count(0) + { + } + + // Destroy the queue + ~_Queue() + { + } + + // Returns the count of items in the queue + size_t _Count() const + { + return _M_count; + } + + // Add an item to the tail of the queue + // + // Returns a Boolean indicating whether the operation succeeded. + bool _Enqueue(_Message *_Element) + { + _CONCRT_ASSERT(_Element->_M_pNext == NULL); + _CONCRT_ASSERT(*_M_ppTail == NULL); + + *_M_ppTail = _Element; + _Element->_M_pNext = NULL; + _M_ppTail = &(_Element->_M_pNext); + _M_count++; + + return true; + } + + // Remove the specified element from the queue + // + // Returns a Boolean indicating whether the operation succeeded, that is, the message was found in the queue. + bool _Remove(_Message * _OldElement) + { + bool _Result = false; + + _CONCRT_ASSERT(_OldElement != NULL); + + if (_M_pHead == _OldElement) + { + _M_pHead = _OldElement->_M_pNext; + if (_M_pHead == NULL) + { + _M_ppTail = &_M_pHead; + } + + _OldElement->_M_pNext = NULL; + _M_count--; + _Result = true; + } + else + { + _Message * _Next = NULL; + for (_Message * _Node = _M_pHead; _Node != NULL; _Node = _Next) + { + _Next = _Node->_M_pNext; + + if (_Node->_M_pNext == _OldElement) + { + _Node->_M_pNext = _OldElement->_M_pNext; + // if this is the last element of the _Queue + if (_Node->_M_pNext == NULL && _M_count == 1) + { + _M_ppTail = &_M_pHead; + } + + _OldElement->_M_pNext = NULL; + _M_count--; + _Result = true; + break; + } + } + } + + return _Result; + } + + // Dequeue an item from the head of queue + // + // Returns a pointer to the message found at the head of the queue. + _Message * _Dequeue() + { + if (_M_pHead == NULL) + { + return NULL; + } + + _Message * _Result = _M_pHead; + + _M_pHead = _Result->_M_pNext; + if (_M_pHead == NULL) + { + _M_ppTail = &_M_pHead; + } + + _Result->_M_pNext = NULL; + _M_count--; + return _Result; + } + + // Return the item at the head of the queue, without dequeuing + // + // Returns a pointer to the message found at the head of the queue. + _Message * _Peek() + { + return _M_pHead; + } + + // Return true if the ID matches the message at the head of the queue + bool _Is_head(runtime_object_identity _MsgId) + { + // Peek at the next message in the message buffer. Use it to + // check if the IDs match + _Message * _Msg = _M_pHead; + + if (_Msg == NULL || _Msg->msg_id() != _MsgId) + { + return false; + } + + return true; + } + }; + + // + // _Dynamic_array implements a container very similar to std::vector. + // However, it exposes a reduced subset of functionality that is + // geared towards use in network_link_registry. The array acess is not + // thread-safe. + // + template<class _Type> + class _Dynamic_array + { + public: + + typedef _Dynamic_array<_Type> _Myt; + + typedef _Type& reference; + typedef _Type const& const_reference; + + // + // Construct a dynamic array + // + _Dynamic_array() + { + _Init(); + } + + // + // Release any resources used by dynamic array + // + ~_Dynamic_array() + { + _Clear(); + } + + // + // Assignment operator. Copy the contents of _Right + // + _Myt& operator=(const _Myt& _Right) + { + if (this != &_Right) + { + // Remove all the elements + _Clear(); + + // Allocate space for the new elements + size_t _Size = _Right._Size(); + _Grow(_Size); + + // Copy over the new elements + for (size_t _I=0; _I < _Size; _I++) + { + _Push_back(_Right[_I]); + } + } + + return *this; + } + + // + // Clear all the elements in the array + // + void _Clear() + { + if (_M_array != NULL) + { + delete [] _M_array; + _Init(); + } + } + + // + // Add an element to the end of the array + // + void _Push_back(_Type const& _Element) + { + if (_M_index >= _M_size) + { + // Not enough space. Grow the array + size_t _NewSize = (_M_index + 1) * _S_growthFactor; + _Grow(_NewSize); + } + + _CONCRT_ASSERT(_M_index < _M_size); + _M_array[_M_index] = _Element; + _M_index++; + } + + // + // Index operation. Retrieve an element at the specified index. No bounds check is done. + // + reference operator[](size_t _Pos) + { + _CONCRT_ASSERT(_Pos < _M_size); + return _M_array[_Pos]; + } + + // + // Index operation. Retrieve an element at the specified index. No bounds check is done. + // + const_reference operator[](size_t _Pos) const + { + _CONCRT_ASSERT(_Pos < _M_size); + return _M_array[_Pos]; + } + + // + // Returns the count of elements in the array + // + size_t _Size() const + { + return _M_index; + } + + // + // Swap the contents of this array with _Right + // + void _Swap(_Myt& _Right) + { + if (this != &_Right) + { + // Swap the details. + _Type * _Array = _M_array; + size_t _Index = _M_index; + size_t _Size = _M_size; + + _M_array = _Right._M_array; + _M_index = _Right._M_index; + _M_size = _Right._M_size; + + _Right._M_array = _Array; + _Right._M_index = _Index; + _Right._M_size = _Size; + } + } + + private: + // + // Initialize the array + // + void _Init() + { + _M_array = NULL; + _M_index = 0; + _M_size = 0; + } + + // + // Grow the array to the given size. The old elements are copied over. + // + void _Grow(size_t _NewSize) + { + _CONCRT_ASSERT( _NewSize > _M_size ); + + _Type * _Array = new _Type[_NewSize]; + + if (_M_array != NULL) + { + // Copy over the elememts + for (size_t _I = 0; _I < _M_size; _I++) + { + _Array[_I] = _M_array[_I]; + } + + delete [] _M_array; + } + + _M_array = _Array; + _M_size = _NewSize; + } + + // Private data members + + // Array of elements + _Type * _M_array; + + // Index where the next element should be inserted + size_t _M_index; + + // Capacity of the array. + size_t _M_size; + + static const int _S_growthFactor = 2; + }; + + // + // Returns an identifier for the given object that could be used + // in an ETW trace (call to _Trace_agents) + // + template <class _Type> + __int64 _Trace_agents_get_id(_Type * _PObject) + { + return reinterpret_cast<__int64>(_PObject); + } + +} // namespace details + +//************************************************************************** +// Public Namespace: +// +// Anything in the Concurrency namespace is intended for direct client consumption. +// +//************************************************************************** + +// +// Forward declarations: +// +template<class _Type> class ISource; +template<class _Type> class ITarget; + +//************************************************************************** +// Network link registry +//************************************************************************** + +// Forward declaration for use in the iterator +template<class _Block> class network_link_registry; + +/// <summary> +/// Const iterator for network link registry. Message blocks should use +/// the link_registry::iterator type for iteration. +/// </summary> +/// <typeparam name="_Block"> +/// The network block type +/// </typeparam> +/**/ +template<class _Block> +class _Network_link_iterator +{ +public: + + typedef _Network_link_iterator<_Block> _Myt; + typedef network_link_registry<_Block> _MyContainer; + + // Element type + typedef _Block* _EType; + + // Const iterator - iterator shall not be used to modify the links + typedef _EType const& const_reference; + typedef _EType const* const_pointer; + + /// <summary> + /// Construct iterator + /// </summary> + /**/ + _Network_link_iterator(_MyContainer * _PNetwork_link, size_t _Index) : _M_pNetwork_link(_PNetwork_link), _M_index(_Index), _M_value(NULL) + { + _M_pNetwork_link->_Next_index(_M_index); + } + + /// <summary> + /// Copy construct an iterator + /// </summary> + /**/ + _Network_link_iterator(_Myt const& _Right) + { + _M_pNetwork_link = _Right._M_pNetwork_link; + _M_index = _Right._M_index; + } + + /// <summary> + /// Copy assign an iterator + /// </summary> + /**/ + _Myt const& operator=(_Myt const& _Right) + { + _M_pNetwork_link = _Right._M_pNetwork_link; + _M_index = _Right._M_index; + return *this; + } + + /// <summary> + /// Returns the object pointed to by the iterator + /// </summary> + /// <returns> + /// Reference to the object pointed to by the iterator + /// </returns> + /**/ + const_reference operator*() + { + _M_value = _M_pNetwork_link->_Get_element(_M_index); + return _M_value; + } + + /// <summary> + /// Returns a pointer to the class object + /// </summary> + /// <returns> + /// Returns a pointer to the class object + /// </returns> + /**/ + const_pointer operator->() const + { + return (&**this); + } + + /// <summary> + /// Pre-increment the iterator to point to the next element + /// </summary> + /// <returns> + /// Reference to the object pointer to by the iterator after + /// incrementing it + /// </returns> + /**/ + _Myt& operator++() + { + ++_M_index; + _M_pNetwork_link->_Next_index(_M_index); + return (*this); + } + + /// <summary> + /// Post-increment the iterator to point to the next element + /// </summary> + /// <returns> + /// Reference to the object pointer to by the iterator before + /// incrementing it + /// </returns> + /**/ + _Myt operator++(int) + { + _Myt _Tmp = *this; + ++*this; + return (_Tmp); + } + +private: + + // Pointer to the underlying container (network link registry) + _MyContainer * _M_pNetwork_link; + + // Current index + size_t _M_index; + + // Current value + _EType _M_value; +}; + +/// <summary> +/// The <c>network_link_registry</c> abstract base class manages the links between source +/// and target blocks. +/// </summary> +/// <typeparam name="_Block"> +/// The block data type being stored in the <c>network_link_registry</c>. +/// </typeparam> +/// <remarks> +/// The <c>network link registry</c> is not safe for concurrent access. +/// </remarks> +/// <seealso cref="single_link_registry Class"/> +/// <seealso cref="multi_link_registry Class"/> +/**/ +template<class _Block> +class network_link_registry +{ +public: + + /// <summary> + /// A type that represents the block type stored in the <c>network_link_registry</c> object. + /// </summary> + /**/ + typedef typename _Block type; + + /// <summary> + /// A type that represents an element pointer stored in the <c>network_link_registry</c> object. + /// </summary> + /**/ + typedef _Block * _EType; + + /// <summary> + /// A type that provides a reference to a <c>const</c> element stored in a + /// <c>network_link_registry</c> object for reading and performing const operations. + /// </summary> + /**/ + typedef _EType const& const_reference; + + /// <summary> + /// A type that provides a pointer to a <c>const</c> element in a + /// <c>network_link_registry</c> object. + /// </summary> + /**/ + typedef _EType const* const_pointer; + + // Make the iterators friends so that they can access some of the + // private routines such as _Get_element. + /**/ + friend class _Network_link_iterator<_Block>; + + /// <summary> + /// A type that provides an iterator that can read or modify any element in a + /// <c>network_link_registry</c> object. + /// </summary> + /**/ + typedef _Network_link_iterator<_Block> iterator; + + /// <summary> + /// When overridden in a derived class, adds a link to the <c>network_link_registry</c> + /// object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be added. + /// </param> + /**/ + virtual void add(_EType _Link) = 0; + + /// <summary> + /// When overridden in a derived class, removes a specified block from the + /// <c>network_link_registry</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be removed, if found. + /// </param> + /// <returns> + /// <c>true</c> if the link was found and removed, <c>false</c> otherwise. + /// </returns> + /**/ + virtual bool remove(_EType _Link) = 0; + + /// <summary> + /// When overridden in a derived class, searches the <c>network_link_registry</c> object + /// for a specified block. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block that is being searched for in the <c>network_link_registry</c> + /// object. + /// </param> + /// <returns> + /// <c>true</c> if the block was found, <c>false</c> otherwise. + /// </returns> + /**/ + virtual bool contains(_EType _Link) = 0; + + /// <summary> + /// When overridden in a derived class, returns the number of items in the + /// <c>network_link_registry</c> object. + /// </summary> + /// <returns> + /// The number of items in the <c>network_link_registry</c> object. + /// </returns> + /**/ + virtual size_t count() = 0; + + /// <summary> + /// When overridden in a derived class, returns an iterator to the first element in the + /// <c>network_link_registry</c> object. + /// </summary> + /// <remarks> + /// The end state of the iterator is indicated by a <c>NULL</c> link. + /// </remarks> + /// <returns> + /// An iterator addressing the first element in the <c>network_link_registry</c> object. + /// </returns> + /**/ + virtual iterator begin() = 0; + +protected: + + /// <summary> + /// Skips empty slots and updates the index to the next + /// non-empty slot. This is called by the iterator. + /// </summary> + /// <param name="_Index"> + /// A reference to the index that is to be updated. + /// </param> + /**/ + virtual void _Next_index(size_t& _Index) = 0; + + /// <summary> + /// Retrieves the element at the given index. If the index is out of bounds, + /// <c>NULL</c> is returned. Users need to use the iterator to access the links. + /// </summary> + /// <param name="_Index"> + /// Index of the link to be retrieved. + /// </param> + /// <returns> + /// The element in the registry at the index specified by the <paramref name="_Index"/> parameter. + /// </returns> + /**/ + virtual _EType _Get_element(size_t _Index) const = 0; +}; + +/// <summary> +/// The <c>single_link_registry</c> object is a <c>network_link_registry</c> that manages +/// only a single source or target block. +/// </summary> +/// <typeparam name="_Block"> +/// The block data type being stored in the <c>single_link_registry</c> object. +/// </typeparam> +/// <seealso cref="multi_link_registry Class"/> +/**/ +template<class _Block> +class single_link_registry : public network_link_registry<_Block> +{ +public: + + /// <summary> + /// Constructs a <c>single_link_registry</c> object. + /// </summary> + /**/ + single_link_registry() : _M_connectedLink(NULL) + { + } + + /// <summary> + /// Destroys the <c>single_link_registry</c> object. + /// </summary> + /// <remarks> + /// The method throws an <see cref="invalid_operation Class">invalid_operation</see> exception if + /// it is called before the link is removed. + /// </remarks> + /**/ + virtual ~single_link_registry() + { + // It is an error to delete link registry with links + // still present + if (count() != 0) + { + throw invalid_operation("Deleting link registry before removing all the links"); + } + } + + /// <summary> + /// Adds a link to the <c>single_link_registry</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be added. + /// </param> + /// <remarks> + /// The method throws an <see cref="invalid_link_target Class">invalid_link_target</see> exception + /// if there is already a link in this registry. + /// </remarks> + /**/ + virtual void add(_EType _Link) + { + if (_Link == NULL) + { + return; + } + + // Only one link can be added. + if (_M_connectedLink != NULL) + { + throw invalid_link_target("_Link"); + } + + _M_connectedLink = _Link; + } + + /// <summary> + /// Removes a link from the <c>single_link_registry</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be removed, if found. + /// </param> + /// <returns> + /// <c>true</c> if the link was found and removed, <c>false</c> otherwise. + /// </returns> + /**/ + virtual bool remove(_EType _Link) + { + if ((_Link != NULL) && (_M_connectedLink == _Link)) + { + _M_connectedLink = NULL; + return true; + } + + return false; + } + + /// <summary> + /// Searches the <c>single_link_registry</c> object for a specified block. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block that is to be searched for in the <c>single_link_registry</c> object. + /// </param> + /// <returns> + /// <c>true</c> if the link was found, <c>false</c> otherwise. + /// </returns> + /**/ + virtual bool contains(_EType _Link) + { + return ((_Link != NULL) && (_M_connectedLink == _Link)); + } + + /// <summary> + /// Counts the number of items in the <c>single_link_registry</c> object. + /// </summary> + /// <returns> + /// The number of items in the <c>single_link_registry</c> object. + /// </returns> + /**/ + virtual size_t count() + { + return (_M_connectedLink == NULL) ? 0 : 1; + } + + /// <summary> + /// Returns an iterator to the first element in the <c>single_link_registry</c> object. + /// </summary> + /// <remarks> + /// The end state is indicated by a <c>NULL</c> link. + /// </remarks> + /// <returns> + /// An iterator addressing the first element in the <c>single_link_registry</c> object. + /// </returns> + /**/ + virtual iterator begin() + { + return (iterator(this, 0)); + } + +protected: + + /// <summary> + /// Skips empty slots and updates the index to the next + /// non-empty slot. This is called by the iterator. + /// </summary> + /// <param name="_Index"> + /// A reference to the index that is to be updated. + /// </param> + /**/ + virtual void _Next_index(size_t& _Index) + { + if (_M_connectedLink == NULL) + { + _Index++; + } + } + + /// <summary> + /// Retrieves the element at the given index. If the index is out of bounds, + /// <c>NULL</c> is returned. Users need to use the iterator to access the links. + /// </summary> + /// <param name="_Index"> + /// The index of the link to be retrieved. + /// </param> + /// <returns> + /// The element in the registry at the index specified by the <paramref name="_Index"/> parameter. + /// </returns> + /**/ + virtual _EType _Get_element(size_t _Index) const + { + if (_Index == 0) + { + return _M_connectedLink; + } + + return NULL; + } + +private: + + // A single pointer is used to hold the link + _EType _M_connectedLink; +}; + +/// <summary> +/// The <c>multi_link_registry</c> object is a <c>network_link_registry</c> that manages multiple +/// source blocks or multiple target blocks. +/// </summary> +/// <typeparam name="_Block"> +/// The block data type being stored in the <c>multi_link_registry</c> object. +/// </typeparam> +/// <seealso cref="single_link_registry Class"/> +/**/ +template<class _Block> +class multi_link_registry : public network_link_registry<_Block> +{ +public: + + /// <summary> + /// Constructs a <c>multi_link_registry</c> object. + /// </summary> + /**/ + multi_link_registry() : _M_maxLinks(_NOT_SET) + { + } + + /// <summary> + /// Destroys the <c>multi_link_registry</c> object. + /// </summary> + /// <remarks> + /// The method throws an <see cref="invalid_operation Class">invalid_operation</see> exception if + /// called before all links are removed. + /// </remarks> + /**/ + virtual ~multi_link_registry() + { + // It is an error to delete link registry with links + // still present + if (count() != 0) + { + throw invalid_operation("Deleting link registry before removing all the links"); + } + } + + /// <summary> + /// Sets an upper bound on the number of links that the <c>multi_link_registry</c> object + /// can hold. + /// </summary> + /// <param name="_MaxLinks"> + /// The maximum number of links that the <c>multi_link_registry</c> object can hold. + /// </param> + /// <remarks> + /// After a bound is set, unlinking an entry will cause the <c>multi_link_registry</c> + /// object to enter an immutable state where further calls to <c>add</c> will throw an + /// <c>invalid_link_target</c> exception. + /// </remarks> + /**/ + void set_bound(size_t _MaxLinks) + { + _CONCRT_ASSERT(count() == 0); + _M_maxLinks = _MaxLinks; + } + + /// <summary> + /// Adds a link to the <c>multi_link_registry</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be added. + /// </param> + /// <remarks> + /// The method throws an <see cref="invalid_link_target Class">invalid_link_target</see> exception if + /// the link is already present in the registry, or if a bound has already been set with the <c>set_bound</c> + /// function and a link has since been removed. + /// </remarks> + /**/ + virtual void add(_EType _Link) + { + if (_Link == NULL) + { + return; + } + + _Add(_Link); + } + + /// <summary> + /// Removes a link from the <c>multi_link_registry</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be removed, if found. + /// </param> + /// <returns> + /// <c>true</c> if the link was found and removed, <c>false</c> otherwise. + /// </returns> + /**/ + virtual bool remove(_EType _Link) + { + if (_Link == NULL) + { + return false; + } + + return (_Remove(_Link)); + } + + /// <summary> + /// Searches the <c>multi_link_registry</c> object for a specified block. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block that is to be searched for in the <c>multi_link_registry</c> object. + /// </param> + /// <returns> + /// <c>true</c> if the specified block was found, <c>false</c> otherwise. + /// </returns> + /**/ + virtual bool contains(_EType _Link) + { + if (_Link == NULL) + { + return false; + } + + return (_Find(_Link) < _M_vector._Size()); + } + + /// <summary> + /// Counts the number of items in the <c>multi_link_registry</c> object. + /// </summary> + /// <returns> + /// The number of items in the <c>multi_link_registry</c> object. + /// </returns> + /**/ + virtual size_t count() + { + return _Count(); + } + + /// <summary> + /// Returns an iterator to the first element in the <c>multi_link_registry</c> object. + /// </summary> + /// <remarks> + /// The end state is indicated by a <c>NULL</c> link. + /// </remarks> + /// <returns> + /// An iterator addressing the first element in the <c>multi_link_registry</c> object. + /// </returns> + /**/ + virtual iterator begin() + { + return (iterator(this, 0)); + } + +protected: + + /// <summary> + /// Skips empty slots and updates the index to the next + /// non-empty slot. This is called by the iterator. + /// </summary> + /// <param name="_Index"> + /// A reference to the index that is to be updated. + /// </param> + /**/ + virtual void _Next_index(size_t& _Index) + { + size_t _Size = _M_vector._Size(); + while (_Index < _Size) + { + if (_M_vector[_Index] != NULL) + { + break; + } + + ++_Index; + } + } + + /// <summary> + /// Retrieves the element at the given index. If the index is out of bounds, + /// <c>NULL</c> is returned. Users need to use the iterator to access the links + /// </summary> + /// <param name="_Index"> + /// Index of the link to be retrieved. + /// </param> + /// <returns> + /// The element in the registry at the index specified by the <paramref name="_Index"/> parameter. + /// </returns> + /**/ + virtual _EType _Get_element(size_t _Index) const + { + if (_Index < _M_vector._Size()) + { + return _M_vector[_Index]; + } + + return NULL; + } + +private: + + /// <summary> + /// Adds a link to the <c>multi_link_registry</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be added. + /// </param> + /**/ + void _Add(_EType _Link) + { + size_t _Size = _M_vector._Size(); + size_t _Insert_pos = 0; + + _CONCRT_ASSERT(_Link != NULL); + + // If max links is set, ensure that inserting the new + // link will not exceed the bound. + if ((_M_maxLinks != _NOT_SET) && ((_Size+1) > (size_t) _M_maxLinks)) + { + throw invalid_link_target("_Link"); + } + + for (size_t _Index = 0; _Index < _Size; _Index++) + { + if (_M_vector[_Index] != NULL) + { + // We want to find the first NULL entry after all the + // non-NULL entries. + _Insert_pos = _Index + 1; + + // Throw if dupiclate entry is found + if (_M_vector[_Index] == _Link) + { + throw invalid_link_target("_Link"); + } + } + } + + if (_Insert_pos < _Size) + { + _M_vector[_Insert_pos] = _Link; + } + else + { + _M_vector._Push_back(_Link); + } + } + + /// <summary> + /// Removes a link from the <c>multi_link_registry</c> + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be removed, if found. + /// </param> + /// <returns> + /// <c>true</c> if the specified link was found and removed, <c>false</c> otherwise. + /// </returns> + /**/ + bool _Remove(_EType _Link) + { + _CONCRT_ASSERT(_Link != NULL); + + for (size_t _Index = 0; _Index < _M_vector._Size(); _Index++) + { + if (_M_vector[_Index] == _Link) + { + _M_vector[_Index] = NULL; + + // If max links is set, prevent new additions to the registry + if (_M_maxLinks != _NOT_SET && _M_maxLinks > 0) + { + // Setting the bound to 0. This causes add to always throw. + _M_maxLinks = 0; + } + + return true; + } + } + + return false; + } + + + /// <summary> + /// Searches the registry for the given link + /// </summary> + /// <param name="_Link"> + /// A pointer to a block that is to be searched. + /// </param> + /// <returns> + /// Index of the entry if found. + /// </returns> + /**/ + virtual size_t _Find(_EType _Link) + { + size_t _Index = 0; + for (_Index = 0; _Index < _M_vector._Size(); _Index++) + { + if (_M_vector[_Index] == _Link) + { + break; + } + } + + return _Index; + } + + /// <summary> + /// Returns the count of items in the registry. + /// </summary> + /// <returns> + /// The count of items in the registry. + /// </returns> + /**/ + size_t _Count() const + { + size_t _Count = 0; + + for (size_t _Index = 0; _Index < _M_vector._Size(); _Index++) + { + if (_M_vector[_Index] != NULL) + { + _Count++; + } + } + + return _Count; + } + + static const size_t _NOT_SET = SIZE_MAX; + + // Maximum number of links allowed. + size_t _M_maxLinks; + + // ::Concurrency::details::_Dynamic_array is used to hold the links + ::Concurrency::details::_Dynamic_array<_EType> _M_vector; +}; + +// Forward declaration for the iterator +template<class _LinkRegistry> class source_link_manager; + +/// <summary> +/// Const Iterator for referenced link manager. +/// </summary> +/// <typeparam name="_LinkRegistry"> +/// The underlying network link registry +/// </typeparam> +/**/ +template<class _LinkRegistry> +class _Source_link_iterator +{ +public: + + typedef typename _LinkRegistry::type _Block; + + typedef _Source_link_iterator<_LinkRegistry> _Myt; + typedef source_link_manager<_LinkRegistry> _MyContainer; + + // Element type + typedef _Block* _EType; + + // Const iterator - iterator shall not be used to modify the links + typedef _EType const& const_reference; + typedef _EType const* const_pointer; + + /// <summary> + /// Construct iterator + /// </summary> + /**/ + _Source_link_iterator(_MyContainer * _PNetwork_link, size_t _Index) : _M_pNetwork_link(_PNetwork_link), _M_index(_Index), _M_sentinel(NULL) + { + // Take a snapshot of the link registry. This will reference the registry. + _M_pNetwork_link->_To_array(_M_array); + } + + /// <summary> + /// Destruct iterator + /// </summary> + /**/ + virtual ~_Source_link_iterator() + { + if (_M_pNetwork_link != NULL) + { + _M_pNetwork_link->release(); + } + } + /// <summary> + /// Copy construct an iterator + /// </summary> + /**/ + _Source_link_iterator(_Myt const& _Right) + { + _M_pNetwork_link = _Right._M_pNetwork_link; + _M_index = _Right._M_index; + _M_array = _Right._M_array; + + _M_pNetwork_link->reference(); + } + + /// <summary> + /// Copy assign an iterator + /// </summary> + /**/ + _Myt const& operator=(_Myt const& _Right) + { + _MyContainer * _OldContainer = _M_pNetwork_link; + _CONCRT_ASSERT(_OldContainer != NULL); + + _M_pNetwork_link = _Right._M_pNetwork_link; + _M_index = _Right._M_index; + _M_array = _Right._M_array; + + if (_OldContainer != _M_pNetwork_link) + { + _OldContainer->release(); + _M_pNetwork_link->reference(); + } + + return *this; + } + + /// <summary> + /// Returns the object pointed to by the iterator + /// </summary> + /// <returns> + /// Reference to the object pointed to by the iterator + /// </returns> + /**/ + const_reference operator*() + { + return _Get(0); + } + + /// <summary> + /// Returns a pointer to the class object + /// </summary> + /// <returns> + /// Returns a pointer to the class object + /// </returns> + /**/ + const_pointer operator->() const + { + return (&**this); + } + + /// <summary> + /// Index operation. Retrieve an element at the specified index. + /// </summary> + /**/ + const_reference operator[](size_t _Pos) const + { + return _Get(_Pos); + } + + /// <summary> + /// Pre-increment the iterator to point to the next element + /// </summary> + /// <returns> + /// Reference to the object pointer to by the iterator after incrementing it + /// </returns> + /**/ + _Myt& operator++() + { + ++_M_index; + return (*this); + } + + /// <summary> + /// Post-increment the iterator to point to the next element + /// </summary> + /// <returns> + /// Reference to the object pointer to by the iterator before incrementing it + /// </returns> + /**/ + _Myt operator++(int) + { + _Myt _Tmp = *this; + ++*this; + return (_Tmp); + } + +private: + + // Get the element at the given offset. + const_reference _Get(size_t _Pos) const + { + size_t _Index = _M_index + _Pos; + if (_Index >= _M_array._Size()) + { + return _M_sentinel; + } + + return _M_array[_Index]; + } + + // Array to hold the snapshot of the link registry + ::Concurrency::details::_Dynamic_array<_EType> _M_array; + + // Pointer to the underlying container (network link registry) + _MyContainer * _M_pNetwork_link; + + // Current index + size_t _M_index; + + // Sentinel value to return on bounds overflow + _EType _M_sentinel; +}; + +/// <summary> +/// The <c>source_link_manager</c> object manages messaging block network links +/// to <c>ISource</c> blocks. +/// </summary> +/// <typeparam name="_LinkRegistry"> +/// The network link registry. +/// </typeparam> +/// <remarks> +/// Currently, the source blocks are reference counted. This is a wrapper on a +/// <c>network_link_registry</c> object that allows concurrent access to the links and +/// provides the ability to reference the links through callbacks. Message +/// blocks (<c>target_block</c>s or <c>propagator_block</c>s) should use this class +/// for their source links. +/// </remarks> +/// <seealso cref="single_link_registry Class"/> +/// <seealso cref="multi_link_registry Class"/> +/**/ +template<class _LinkRegistry> +class source_link_manager +{ +public: + + /// <summary> + /// The type of link registry being managed by the <c>source_link_manager</c> object. + /// </summary> + /**/ + typedef _LinkRegistry type; + + /// <summary> + /// The type of the blocks being managed by the <c>source_link_manager</c> object. + /// </summary> + /**/ + typedef typename _LinkRegistry::type _Block; + + /// <summary> + /// The method signature for a callback method for this <c>source_link_manager</c> object. + /// </summary> + /**/ + typedef std::tr1::function<void(_Block *, bool)> _Callback_method; + + /// <summary> + /// A type that represents a pointer to an element stored in the <c>source_link_manager</c> object. + /// </summary> + /**/ + typedef _Block * _EType; + + /// <summary> + /// A type that provides a reference to a <c>const</c> element stored in a <c>source_link_manager</c> object + /// for reading and performing const operations. + /// </summary> + /**/ + typedef _EType const& const_reference; + + /// <summary> + /// A type that provides a pointer to a <c>const</c> element in a <c>source_link_manager</c> object. + /// </summary> + /**/ + typedef _EType const* const_pointer; + + // Iterator + friend class _Source_link_iterator<_LinkRegistry>; + + /// <summary> + /// A type that provides an iterator that can read or modify any element in the + /// <c>source_link_manager</c> object. + /// </summary> + /**/ + typedef _Source_link_iterator<_LinkRegistry> iterator; + + /// <summary> + /// A type that provides a reentrant lock for the <c>source_link_manager</c> object. + /// </summary> + /**/ + typedef ::Concurrency::details::_ReentrantPPLLock _LockType; + + /// <summary> + /// A type that provides a RAII scoped lock holder for a lock. + /// </summary> + /**/ + typedef _LockType::_Scoped_lock _LockHolder; + + /// <summary> + /// Constructs a <c>source_link_manager</c> object. + /// </summary> + /**/ + source_link_manager() : _M_iteratorCount(0), _M_pLinkedTarget(NULL) + { + } + + /// <summary> + /// Destroys the <c>source_link_manager</c> object. + /// </summary> + /**/ + ~source_link_manager() + { + _CONCRT_ASSERT(_M_pendingRemove._Size() == 0); + } + + /// <summary> + /// Registers the target block that holds this <c>source_link_manager</c> object. + /// </summary> + /// <param name="_PTarget"> + /// The target block holding this <c>source_link_manager</c> object. + /// </param> + /**/ + void register_target_block(_Inout_ ITarget<typename _Block::source_type> * _PTarget) + { + _M_pLinkedTarget = _PTarget; + } + + /// <summary> + /// Sets the maximum number of source links that can be added to this + /// <c>source_link_manager</c> object. + /// </summary> + /// <param name="_MaxLinks"> + /// The maximum number of links. + /// </param> + /**/ + void set_bound(size_t _MaxLinks) + { + _M_links.set_bound(_MaxLinks); + } + + /// <summary> + /// Adds a source link to the <c>source_link_manager</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be added. + /// </param> + /**/ + void add(_EType _Link) + { + if (_Link == NULL) + { + return; + } + + { + _LockHolder _Lock(_M_lock); + _M_links.add(_Link); + + // We need to add the _Link first and then invoke the + // callback because _Add could throw. + + // As soon as the above lock is released, remove would + // find the link that was added and could unlink it before + // we are able to invoke the notification below. Keeping an + // active iterator would prevent that from happening. + _M_iteratorCount++; + } + + // Acquire a reference on this link by the target + _Link->acquire_ref(_M_pLinkedTarget); + + // Release the active iterator + release(); + } + + /// <summary> + /// Removes a link from the <c>source_link_manager</c> object. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block to be removed, if found. + /// </param> + /// <returns> + /// <c>true</c> if the link was found and removed, <c>false</c> otherwise. + /// </returns> + /**/ + bool remove(_EType _Link) + { + bool _Removed = false; + _EType _RemovedLink = NULL; + ITarget<typename _Block::source_type> * _LinkedTarget = _M_pLinkedTarget; + + if (_Link == NULL) + { + return false; + } + + { + _LockHolder _Lock(_M_lock); + _Removed = _M_links.remove(_Link); + + if (!_Removed) + { + // No change was made + return _Removed; + } + + if (_M_iteratorCount == 0) + { + // Set the removed link to indicate that + // notification callback needs to be invoked. + _RemovedLink = _Link; + } + else + { + // The iterator will complete the pending operation + _M_pendingRemove._Push_back(_Link); + } + } + + // NOTE: touching "this" pointer is dangerous as soon as the above lock is released + + // Release the reference for this link + if (_RemovedLink != NULL) + { + _RemovedLink->release_ref(_LinkedTarget); + } + + return _Removed; + } + + /// <summary> + /// Acquires a reference on the <c>source_link_manager</c> object. + /// </summary> + /**/ + void reference() + { + _LockHolder _Lock(_M_lock); + _M_iteratorCount++; + } + + /// <summary> + /// Releases the reference on the <c>source_link_manager</c> object. + /// </summary> + /**/ + void release() + { + ITarget<typename _Block::source_type> * _LinkedTarget = _M_pLinkedTarget; + ::Concurrency::details::_Dynamic_array<_EType> _LinksToRemove; + + { + _LockHolder _Lock(_M_lock); + _CONCRT_ASSERT(_M_iteratorCount > 0); + _M_iteratorCount--; + + if (_M_iteratorCount == 0) + { + if (_M_pendingRemove._Size() > 0) + { + // Snap the pending remove list with the lock held + _M_pendingRemove._Swap(_LinksToRemove); + } + } + } + + // NOTE: touching "this" pointer is dangerous as soon as the above lock is released + + // Release the references + size_t _Size = _LinksToRemove._Size(); + + for (size_t _I=0; _I < _Size; _I++) + { + _LinksToRemove[_I]->release_ref(_LinkedTarget); + } + } + + /// <summary> + /// Searches the <c>network_link_registry</c> within this <c>source_link_manager</c> + /// object for a specified block. + /// </summary> + /// <param name="_Link"> + /// A pointer to a block that is to be searched for in the <c>source_link_manager</c> object. + /// </param> + /// <returns> + /// <c>true</c> if the specified block was found, <c>false</c> otherwise. + /// </returns> + /**/ + bool contains(_EType _Link) + { + _LockHolder _Lock(_M_lock); + return _M_links.contains(_Link); + } + + /// <summary> + /// Counts the number of linked blocks in the <c>source_link_manager</c> object. + /// </summary> + /// <returns> + /// The number of linked blocks in the <c>source_link_manager</c> object. + /// </returns> + /**/ + size_t count() + { + _LockHolder _Lock(_M_lock); + return _M_links.count(); + } + + + /// <summary> + /// Returns an iterator to the first element in the <c>source_link_manager</c> object. + /// </summary> + /// <remarks> + /// The end state of the iterator is indicated by a <c>NULL</c> link. + /// </remarks> + /// <returns> + /// An iterator addressing the first element in the <c>source_link_manager</c> object. + /// </returns> + /**/ + iterator begin() + { + return (iterator(this, 0)); + } + +private: + + // Called by the iterator. This routine takes a snapshot of the links + // in the registry and copies it to the array provided. + void _To_array(::Concurrency::details::_Dynamic_array<_EType>& _Array) + { + _LockHolder _Lock(_M_lock); + _M_iteratorCount++; + + for(_LinkRegistry::iterator _Link = _M_links.begin(); *_Link != NULL; _Link++) + { + _Array._Push_back(*_Link); + } + } + + // Internal lock used for synchronization + _LockType _M_lock; + + // Count to indicate that an iterator is active + volatile long _M_iteratorCount; + + // A vector of all pending link remove operations + ::Concurrency::details::_Dynamic_array<_EType> _M_pendingRemove; + + // Underlying link registry + _LinkRegistry _M_links; + + // Target block holding this source link manager + ITarget<typename _Block::source_type> * volatile _M_pLinkedTarget; +}; + +/// <summary> +/// The valid responses for an offer of a <c>message</c> object to a block. +/// </summary> +/**/ +enum message_status +{ + /// <summary> + /// The target accepted the message. + /// </summary> + /**/ + accepted, + /// <summary> + /// The target did not accept the message. + /// </summary> + /**/ + declined, + /// <summary> + /// The target postponed the message. + /// </summary> + /**/ + postponed, + /// <summary> + /// The target tried to accept the message, but it was no longer available. + /// </summary> + /**/ + missed +}; + +/// <summary> +/// The basic message envelope containing the data payload being passed between +/// messaging blocks. +/// </summary> +/// <typeparam name="_Type"> +/// The data type of the payload within the message. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/**/ +template<class _Type> +class message : public ::Concurrency::details::_Runtime_object +{ + friend class ::Concurrency::details::_Queue<message<_Type>>; + +public: + /// <summary> + /// Constructs a <c>message</c> object. + /// </summary> + /// <param name="_P"> + /// The payload of this message. + /// </param> + /// <remarks> + /// The constructor that takes a pointer to a <c>message</c> object as an argument + /// throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if the parameter <paramref name="_Msg"/> is <c>NULL</c>. + /// </remarks> + /**/ + message(_Type const &_P) : payload(_P), _M_pNext(NULL), _M_refCount(0) { } + + /// <summary> + /// Constructs a <c>message</c> object. + /// </summary> + /// <param name="_P"> + /// The payload of this message. + /// </param> + /// <param name="_Id"> + /// The unique ID of this message. + /// </param> + /// <remarks> + /// The constructor that takes a pointer to a <c>message</c> object as an argument + /// throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if the parameter <paramref name="_Msg"/> is <c>NULL</c>. + /// </remarks> + /**/ + message(_Type const &_P, runtime_object_identity _Id) + : ::Concurrency::details::_Runtime_object(_Id), payload(_P), _M_pNext(NULL), _M_refCount(0) + { + } + + /// <summary> + /// Constructs a <c>message</c> object. + /// </summary> + /// <param name="_Msg"> + /// A reference or pointer to a <c>message</c> object. + /// </param> + /// <remarks> + /// The constructor that takes a pointer to a <c>message</c> object as an argument + /// throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if the parameter <paramref name="_Msg"/> is <c>NULL</c>. + /// </remarks> + /**/ + message(message const & _Msg) : payload(_Msg.payload), _M_pNext(NULL), _M_refCount(0) { } + + /// <summary> + /// Constructs a <c>message</c> object. + /// </summary> + /// <param name="_Msg"> + /// A reference or pointer to a <c>message</c> object. + /// </param> + /// <remarks> + /// This method throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if the parameter <paramref name="_Msg"/> is <c>NULL</c>. + /// </remarks> + /**/ + message(_In_ message const * _Msg) : payload((_Msg == NULL) ? NULL : _Msg->payload), _M_pNext(NULL), _M_refCount(0) + { + if (_Msg == NULL) + { + throw std::invalid_argument("_Msg"); + } + } + + /// <summary> + /// Destroys the <c>message</c> object. + /// </summary> + /**/ + virtual ~message() { } + + /// <summary> + /// Returns the ID of the <c>message</c> object. + /// </summary> + /// <returns> + /// The <c>runtime_object_identity</c> of the <c>message</c> object. + /// </returns> + /**/ + runtime_object_identity msg_id() const + { + return _M_id; + } + + /// <summary> + /// The payload of the <c>message</c> object. + /// </summary> + /**/ + _Type const payload; + + /// <summary> + /// Adds to the reference count for the <c>message</c> object. Used for message blocks that + /// need reference counting to determine message lifetimes. + /// </summary> + /// <returns> + /// The new value of the reference count. + /// </returns> + /**/ + long add_ref() + { + return _InterlockedIncrement(&_M_refCount); + } + + /// <summary> + /// Subtracts from the reference count for the <c>message</c> object. Used for message blocks that + /// need reference counting to determine message lifetimes. + /// </summary> + /// <returns> + /// The new value of the reference count. + /// </returns> + /**/ + long remove_ref() + { + return _InterlockedDecrement(&_M_refCount); + } + + /// <summary> + /// A type alias for <typeparamref name="_Type"/>. + /// </summary> + /**/ + typedef typename _Type type; + +private: + // The intrusive next pointer used by blocks that need + // to chain messages it's holding together + message * _M_pNext; + + // Avoid warnings about not generating assignment operators. + message<_Type> const &operator =(message<_Type> const &); + + // A reference count for the message + volatile long _M_refCount; +}; + +//************************************************************************** +// Message processor: +//************************************************************************** + +/// <summary> +/// The <c>message_processor</c> class is the abstract base class for processing of +/// <c>message</c> objects. There is no guarantee on the ordering of the messages. +/// </summary> +/// <typeparam name="_Type"> +/// The data type of the payload within messages handled by this <c>message_processor</c> object. +/// </typeparam> +/// <seealso cref="ordered_message_processor Class"/> +/**/ +template<class _Type> +class message_processor +{ +public: + /// <summary> + /// A type alias for <typeparamref name="_Type"/>. + /// </summary> + /**/ + typedef typename _Type type; + + /// <summary> + /// When overridden in a derived class, places messages into the block asynchronously. + /// </summary> + /// <param name="_Msg"> + /// A <c>message</c> object to send asynchronously. + /// </param> + /// <remarks> + /// Processor implementations should override this method. + /// </remarks> + /**/ + virtual void async_send(_Inout_opt_ message<_Type> * _Msg) = 0; + + /// <summary> + /// When overridden in a derived class, places messages into the block synchronously. + /// </summary> + /// <param name="_Msg"> + /// A <c>message</c> object to send synchronously. + /// </param> + /// <remarks> + /// Processor implementations should override this method. + /// </remarks> + /**/ + virtual void sync_send(_Inout_opt_ message<_Type> * _Msg) = 0; + + /// <summary> + /// When overridden in a derived class, waits for all asynchronous operations to complete. + /// </summary> + /// <remarks> + /// Processor implementations should override this method. + /// </remarks> + /**/ + virtual void wait() = 0; + +protected: + + /// <summary> + /// When overridden in a derived class, performs the forward processing of + /// messages into the block. Called once every time a new message is added and + /// the queue is found to be empty. + /// </summary> + /// <remarks> + /// Message block implementations should override this method. + /// </remarks> + /**/ + virtual void process_incoming_message() = 0; + + /// <summary> + /// Wrapper for <c>process_incoming_message</c> suitable for use as a argument to + /// <c>CreateThread</c> and other similar methods. + /// </summary> + /// <param name="_Data"> + /// A pointer to a message processor passed as a void pointer. + /// </param> + /**/ + static void __cdecl _Process_incoming_message_wrapper(void * _Data) + { + message_processor<_Type> * _PMessageProcessor = (message_processor<_Type> *) _Data; + _PMessageProcessor->process_incoming_message(); + } +}; + +/// <summary> +/// An <c>ordered_message_processor</c> is a <c>message_processor</c> that allows message blocks +/// to process messages in the order they were received. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type of messages handled by the processor. +/// </typeparam> +/**/ +template<class _Type> +class ordered_message_processor : public message_processor<_Type> +{ +public: + /// <summary> + /// The signature of the callback method invoked while processing messages. + /// </summary> + /**/ + typedef std::tr1::function<void(message<_Type> *)> _Handler_method; + + /// <summary> + /// The signature of the callback method invoked while propagating messages. + /// </summary> + /**/ + typedef std::tr1::function<void(void)> _Propagator_method; + + /// <summary> + /// A type alias for <typeparamref name="_Type"/>. + /// </summary> + /**/ + typedef _Type type; + + /// <summary> + /// Constructs an <c>ordered_message_processor</c> object. + /// </summary> + /// <remarks> + /// This <c>ordered_message_processor</c> will not schedule asynchronous or synchronous + /// handlers until the <c>initialize</c> function is called. + /// </remarks> + /**/ + ordered_message_processor() : + _M_queuedDataCount(0), + _M_stopProcessing(1), + _M_lwtCount(0), + _M_pScheduler(NULL), + _M_pScheduleGroup(NULL), + _M_handler(nullptr), + _M_processor(nullptr), + _M_propagator(nullptr) + { + } + + /// <summary> + /// Destroys the <c>ordered_message_processor</c> object. + /// </summary> + /// <remarks> + /// Waits for all outstanding asynchronous operations before destroying the processor. + /// </remarks> + /**/ + virtual ~ordered_message_processor() + { + wait(); + } + + /// <summary> + /// Initializes the <c>ordered_message_processor</c> object with the appropriate + /// callback function, scheduler and schedule group. + /// </summary> + /// <param name="_PScheduler"> + /// A pointer to the scheduler to be used for scheduling light-weight tasks. + /// </param> + /// <param name="_PScheduleGroup"> + /// A pointer to the schedule group to be used for scheduling light-weight tasks. + /// </param> + /// <param name="_Handler"> + /// The handler functor invoked during callback. + /// </param> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + void initialize(_Inout_opt_ Scheduler * _PScheduler, _Inout_opt_ ScheduleGroup * _PScheduleGroup, _Handler_method const& _Handler) + { + _M_pScheduler = _PScheduler; + _M_pScheduleGroup = _PScheduleGroup; + _M_handler = _Handler; + _M_stopProcessing = 0; + } + + /// <summary> + /// Initialize batched message processing + /// </summary> + /// <param name="_Processor"> + /// The processor functor invoked during callback. + /// </param> + /// <param name="_Propagator"> + /// The propagator functor invoked during callback. + /// </param> + virtual void initialize_batched_processing(_Handler_method const& _Processor, _Propagator_method const& _Propagator) + { + _M_processor = _Processor; + _M_propagator = _Propagator; + } + + /// <summary> + /// Synchronously queues up messages and starts a processing task, if this has not been done + /// already. + /// </summary> + /// <param name="_Msg"> + /// A pointer to a message. + /// </param> + /**/ + virtual void sync_send(_Inout_opt_ message<_Type> * _Msg) + { + if (_M_handler == NULL) + { + throw invalid_operation("sync_send called without registering a callback"); + } + + _Sync_send_helper(_Msg); + } + + /// <summary> + /// Asynchronously queues up messages and starts a processing task, if this has not been done + /// already. + /// </summary> + /// <param name="_Msg"> + /// A pointer to a message. + /// </param> + /**/ + virtual void async_send(_Inout_opt_ message<_Type> * _Msg) + { + if (_M_handler == NULL) + { + throw invalid_operation("async_send called without registering a callback"); + } + + // + // If there is a message to send, enqueue it in the processing queue. + // async_send can be sent a NULL message if the block wishes to reprocess + // the messages that are in its queue. For example, an unbounded_buffer + // that has its head node released after reservation. + // + if (_Msg != NULL) + { + _M_queuedMessages.push(_Msg); + } + + if (_InterlockedIncrement(&_M_queuedDataCount) == 1) + { + // Indicate that an LWT is in progress. This will cause the + // destructor to block. + _InterlockedIncrement(&_M_lwtCount); + + if (_M_stopProcessing == 0) + { + _CONCRT_ASSERT(_M_lwtCount > 0); + + _Trace_agents(AGENTS_EVENT_SCHEDULE, ::Concurrency::details::_Trace_agents_get_id(this)); + + TaskProc _Proc = &::Concurrency::ordered_message_processor<_Type>::_Process_incoming_message_wrapper; +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + if (_M_pScheduleGroup != NULL) + { + _M_pScheduleGroup->ScheduleTask(_Proc, this); + } + else if (_M_pScheduler != NULL) + { + _M_pScheduler->ScheduleTask(_Proc, this); + } + else + { +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + ::Concurrency::details::_CurrentScheduler::_ScheduleTask(_Proc, this); +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + // The LWT will decrement _M_lwtCount. + return; + } + + // If we get here then no task was scheduled. Decrement LWT count to reflect this fact + _InterlockedDecrement(&_M_lwtCount); + } + } + + /// <summary> + /// A processor-specific spin wait used in destructors of message blocks to make sure + /// that all asynchronous processing tasks have time to finish before destroying the block. + /// </summary> + /**/ + virtual void wait() + { + // Cease processing of any new messages + _InterlockedIncrement(&_M_stopProcessing); + + // This spin makes sure all previously initiated message processings + // will still process correctly. As soon as this count reaches zero, we can + // procede with the message block destructor. + ::Concurrency::details::_SpinWaitBackoffNone spinWait(::Concurrency::details::_Context::_Yield); + while(_M_lwtCount != 0) + { + spinWait._SpinOnce(); + } + + // Synchronize with sync_send + { + _NR_lock _Lock(_M_asyncSendLock); + _Clear_queued_messages(); + } + + } + +protected: + + /// <summary> + /// The processing function that is called asynchronously. It dequeues messages and begins + /// processing them. + /// </summary> + /**/ + virtual void process_incoming_message() + { + _Trace_agents(AGENTS_EVENT_START, ::Concurrency::details::_Trace_agents_get_id(this)); + long _Count = _Process_message_helper(); + _Trace_agents(AGENTS_EVENT_END, ::Concurrency::details::_Trace_agents_get_id(this), _Count); + + // Indicate that an LWT completed + _InterlockedDecrement(&_M_lwtCount); + + // Do not access any members here. If the count goes to + // 0 as a result of the above decrement, the object + // could be immediately deleted. + } + + private: + + void _Clear_queued_messages() + { + message<_Type> * _Msg = NULL; + while (_M_queuedMessages.try_pop(_Msg)) + { + delete _Msg; + } + } + + void _Sync_send_helper(message<_Type> * _Msg) + { + _NR_lock _Lock(_M_asyncSendLock); + + // Message block destructors sets the _M_stopProcessing flag to stop + // processing any more messages. This is required to guarantee + // that the destructor's wait_for_async_sends will complete + if (_M_stopProcessing == 0) + { + if (_M_queuedDataCount > 0) + { + long _Count = _InterlockedExchange((volatile long *) &_M_queuedDataCount, 0); + _Invoke_handler(_Count); + } + + _Invoke_handler(_Msg); + } + else + { + // Destructor is running. Do not process the message + // Delete the msg, if any. + if (_Msg != NULL) + { + delete _Msg; + } + } + + } + + // Helper function to dequeue and process messages to any targets + long _Process_message_helper() + { + _NR_lock _Lock(_M_asyncSendLock); + + long _Messages_processed = 0; + + // Do batched processing of messages + // Read off the number of messages to process in this iteration by snapping a count + volatile long _Count = _M_queuedDataCount; + bool _StopProcessing = false; + + // This count could be 0 if there was both a synchronous and asynchronous + // send occuring. One of them could have sent all of the messages for the other + while (_Count > 0) + { + // Process _Count number of messages + _Invoke_handler(_Count); + _Messages_processed += _Count; + + // Subtract the count and see if there are new things to process + volatile long _Orig = _InterlockedExchangeAdd((volatile long *) &_M_queuedDataCount, -_Count); + _CONCRT_ASSERT(_Orig >= _Count); + if (_Orig == _Count) + { + // Because _Count did not change, we processed everything there is to process + break; + } + + if (_StopProcessing) + { + break; + } + + // After reading the flag process the currently queued messages + // Any messages received after we observe this flag (to be set) will not + // be processed. + _StopProcessing = (_M_stopProcessing == 0) ? false : true; + + // Snap the count and try to process more + _Count = _M_queuedDataCount; + } + + return _Messages_processed; + } + + // Invoke the handler in the message block for the given + // count + void _Invoke_handler(long _Count) + { + // Process _Count number of messages + for(int _I = 0; _I < _Count; _I++) + { + message<_Type> * _Msg = NULL; + _M_queuedMessages.try_pop(_Msg); + if (_M_processor == NULL) + { + // If a processor function does not exist, the message processor is using single + // message processing rather than batched processing. There should also be no + // propagator function defined in this case. + _CONCRT_ASSERT(_M_propagator == NULL); + _M_handler(_Msg); + } + else + { + // Use the batched message processing function + _M_processor(_Msg); + } + } + + // Call the handler which propagates the message(s) + if (_M_propagator != NULL) + { + _M_propagator(); + } + } + + // Invoke the message block handler for the given message + void _Invoke_handler(message<_Type> * _Msg) + { + if (_M_processor == NULL) + { + // If a processor function does not exist, the message processor is using single + // message processing rather than batched processing. There should also be no + // propagator function defined in this case. + _CONCRT_ASSERT(_M_propagator == NULL); + _M_handler(_Msg); + } + else + { + // Use the batched message processing function + _M_processor(_Msg); + + // Call the handler which propagates the message(s) + if (_M_propagator != NULL) + { + _M_propagator(); + } + } + } + + private: + /// <summary> + /// A queue of the messages + /// </summary> + /**/ + concurrent_queue<message<_Type> *> _M_queuedMessages; + + /// <summary> + /// A lock to use for queueing incoming messages. + /// </summary> + /**/ + ::Concurrency::details::_NonReentrantPPLLock _M_asyncSendLock; + + /// <summary> + /// A count of the current number of messages to process. Used as a flag + /// to see if a new process message task needs to be created. + /// </summary> + /**/ + volatile long _M_queuedDataCount; + + /// <summary> + /// The scheduler to process messages on + /// </summary> + /**/ + Scheduler * _M_pScheduler; + + /// <summary> + /// The schedule group to process messages on + /// </summary> + /**/ + ScheduleGroup * _M_pScheduleGroup; + + /// <summary> + /// A flag set in the destructor of a block to cease processing of new messages. + /// This is required to guarantee that _M_queuedDataCount will get to 0 eventually. + /// </summary> + /**/ + volatile long _M_stopProcessing; + + /// <summary> + /// A counter to indicate the number of outstanding LWTs + /// </summary> + /**/ + volatile long _M_lwtCount; + + /// <summary> + /// A message handler object which exposes the callback to be invoked + /// </summary> + /**/ + _Handler_method _M_handler; + + /// <summary> + /// A message processing object which exposes the callback to be invoked + /// </summary> + /**/ + _Handler_method _M_processor; + + /// <summary> + /// A message propagating object which exposes the callback to be invoked + /// </summary> + /**/ + _Propagator_method _M_propagator; +}; + +/// <summary> +/// The <c>ITarget</c> class is the interface for all target blocks. Target blocks +/// consume messages offered to them by <c>ISource</c> blocks. +/// </summary> +/// <typeparam name="_Type"> +/// The data type of the payload within the messages accepted by the target block. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/// <seealso cref="ISource Class"/> +/**/ +template<class _Type> +class ITarget +{ + // + // ISource<T> is a friend class because calls to Source->link_target() + // and Source->unlink_target() need to call their respective + // Target->link_source() and Target->unlink_source() on the block they are + // linking/unlinking. Those functions are private here because we don't + // want users calling link_source() or unlink_source() directly. link_source/ + // unlink_source don't call respective link_target/unlink_target because an + // infinite loop would occur. + // + friend class ISource<_Type>; + +public: + /// <summary> + /// Destroys the <c>ITarget</c> object. + /// </summary> + /**/ + virtual ~ITarget() {} + + // It is important that calls to propagate do *not* take the same lock on an + // internal message structure that is used by Consume and the LWT. Doing so could + // result in a deadlock with the Consume call. + + /// <summary> + /// When overridden in a derived class, asynchronously passes a message from a source block to + /// this target block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if either the <paramref name="_PMessage"/> or <paramref name="_PSource"/> parameter is <c>NULL</c>. + /// </remarks> + /**/ + virtual message_status propagate(_Inout_opt_ message<_Type> * _PMessage, _Inout_opt_ ISource<_Type> * _PSource) = 0; + + /// <summary> + /// When overridden in a derived class, synchronously passes a message to the target block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if either the <paramref name="_PMessage"/> or <paramref name="_PSource"/> parameter is <c>NULL</c>. + /// <para>Using the <c>send</c> method outside of message initiation and to propagate messages + /// within a network is dangerous and can lead to deadlock.</para> + /// <para>When <c>send</c> returns, the message has either already been accepted, and transferred into + /// the target block, or it has been declined by the target.</para> + /// </remarks> + /**/ + virtual message_status send(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) = 0; + + /// <summary> + /// When overridden in a derived class, returns true or false depending on whether the + /// message block accepts messages offered by a source that is not linked to it. If the overridden method returns + /// <c>true</c>, the target cannot postpone an offered message, as consumption of a postponed message + /// at a later time requires the source to be identified in its sourse link registry. + /// </summary> + /// <returns> + /// <c>true</c> if the block can accept message from a source that is not linked to it + /// <c>false</c> otherwise. + /// </returns> + /**/ + virtual bool supports_anonymous_source() + { + return false; + } + + /// <summary> + /// A type alias for <typeparamref name="_Type"/>. + /// </summary> + /**/ + typedef typename _Type type; + + /// <summary> + /// The signature of any method used by the block that returns a <c>bool</c> value to determine + /// whether an offered message should be accepted. + /// </summary> + /**/ + typedef std::tr1::function<bool(_Type const&)> filter_method; + +protected: + + /// <summary> + /// When overridden in a derived class, links a specified source block to this <c>ITarget</c> block. + /// </summary> + /// <param name="_PSource"> + /// The <c>ISource</c> block being linked to this <c>ITarget</c> block. + /// </param> + /// <remarks> + /// This function should not be called directly on an <c>ITarget</c> block. Blocks should be connected together + /// using the <c>link_target</c> method on <c>ISource</c> blocks, which will invoke the <c>link_source</c> method + /// on the corresponding target. + /// </remarks> + /**/ + virtual void link_source(_Inout_ ISource<_Type> * _PSource) = 0; + + /// <summary> + /// When overridden in a derived class, unlinks a specified source block from this <c>ITarget</c> block. + /// </summary> + /// <param name="_PSource"> + /// The <c>ISource</c> block being unlinked from this <c>ITarget</c> block. + /// </param> + /// <remarks> + /// This function should not be called directly on an <c>ITarget</c> block. Blocks should be disconnected + /// using the <c>unlink_target</c> or <c>unlink_targets</c> methods on <c>ISource</c> blocks, which will invoke + /// the <c>unlink_source</c> method on the corresponding target. + /// </remarks> + /**/ + virtual void unlink_source(_Inout_ ISource<_Type> * _PSource) = 0; + + /// <summary> + /// When overridden in a derived class, unlinks all source blocks from this <c>ITarget</c> block. + /// </summary> + /**/ + virtual void unlink_sources() = 0; +}; + +/// <summary> +/// The <c>ISource</c> class is the interface for all source blocks. Source blocks +/// propagate messages to <c>ITarget</c> blocks. +/// </summary> +/// <typeparam name="_Type"> +/// The data type of the payload within the messages produced by the source block. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/// <seealso cref="ITarget Class"/> +/**/ +template<class _Type> +class ISource +{ +public: + /// <summary> + /// Destroys the <c>ISource</c> object. + /// </summary> + /**/ + virtual ~ISource() {} + + /// <summary> + /// When overridden in a derived class, links a target block to this <c>ISource</c> block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block being linked to this <c>ISource</c> block. + /// </param> + /**/ + virtual void link_target(_Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// When overridden in a derived class, unlinks a target block from this <c>ISource</c> block, + /// if found to be previously linked. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block being unlinked from this <c>ISource</c> block. + /// </param> + /**/ + virtual void unlink_target(_Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// When overridden in a derived class, unlinks all target blocks from this + /// <c>ISource</c> block. + /// </summary> + /**/ + virtual void unlink_targets() = 0; + + /// <summary> + /// When overridden in a derived class, accepts a message that was offered by this <c>ISource</c> block, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>accept</c> method. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /// <remarks> + /// The <c>accept</c> method is called by a target while a message is being offered by this <c>ISource</c> block. + /// The message pointer returned may be different from the one passed into the <c>propagate</c> method + /// of the <c>ITarget</c> block, if this source decides to make a copy of the message. + /// </remarks> + /**/ + virtual message<_Type> * accept(runtime_object_identity _MsgId, _Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// When overridden in a derived class, reserves a message previously offered by this <c>ISource</c> block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>reserve</c> method. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. Reservations can fail + /// for many reasons, including: the message was already reserved or accepted by another target, the source could + /// deny reservations, and so forth. + /// </returns> + /// <remarks> + /// After you call <c>reserve</c>, if it succeeds, you must call either <c>consume</c> or <c>release</c> + /// in order to take or give up possession of the message, respectively. + /// </remarks> + /**/ + virtual bool reserve(runtime_object_identity _MsgId, _Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// When overridden in a derived class, consumes a message previously offered by this <c>ISource</c> block + /// and successfully reserved by the target, transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the reserved <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>consume</c> method. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// The <c>consume</c> method is similar to <c>accept</c>, but must always be preceded by a call to <c>reserve</c> that + /// returned <c>true</c>. + /// </remarks> + /**/ + virtual message<_Type> * consume(runtime_object_identity _MsgId, _Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// When overridden in a derived class, releases a previous successful message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the reserved <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>release</c> method. + /// </param> + /**/ + virtual void release(runtime_object_identity _MsgId, _Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// When overridden in a derived class, acquires a reference count on this <c>ISource</c> block, to prevent deletion. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being linked to this source + /// during the <c>link_target</c> method. + /// </remarks> + /**/ + virtual void acquire_ref(_Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// When overridden in a derived class, releases a reference count on this <c>ISource</c> block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being unlinked from this source. + /// The source block is allowed to release any resources reserved for the target block. + /// </remarks> + /**/ + virtual void release_ref(_Inout_ ITarget<_Type> * _PTarget) = 0; + + /// <summary> + /// A type alias for <typeparamref name="_Type"/>. + /// </summary> + /**/ + typedef typename _Type source_type; + +protected: + /// <summary> + /// Links this source to a target. + /// </summary> + /// <param name="_PLinkFrom"> + /// A pointer to the target. + /// </param> + /// <remarks> + /// This function definition is required because ISource blocks the need to call + /// Target->link_source(), which is a private memeber of ITarget. ISource is + /// declared as a friend class, so this is an way for derived classes of ISource + /// to properly link/unlink their targets during link_target(), unlink_target() and + /// unlink_targets() + /// </remarks> + /**/ + void _Invoke_link_source(ITarget<_Type> * _PLinkFrom) + { + _PLinkFrom->link_source(this); + } + + /// <summary> + /// Unlinks this source from a target. + /// </summary> + /// <param name="_PUnlinkFrom"> + /// A pointer to the target. + /// </param> + /// <remarks> + /// This function definition is required because ISource blocks need to call + /// Target->unlink_source(), which is a private memeber of ITarget. ISource is + /// declared as a friend class, so this is an way for derived classes of ISource + /// to properly link/unlink their targets during link_target(), unlink_target() and + /// unlink_targets() + /// </remarks> + /**/ + void _Invoke_unlink_source(ITarget<_Type> * _PUnlinkFrom) + { + _PUnlinkFrom->unlink_source(this); + } +}; + +//************************************************************************** +// Target Block: +//************************************************************************** + +/// <summary> +/// The <c>target_block</c> class is an abstract base class that provides basic link management +/// functionality and error checking for target only blocks. +/// </summary> +/// <typeparam name="_SourceLinkRegistry"> +/// The link registry to be used for holding the source links. +/// </typeparam> +/// <typeparam name="_MessageProcessorType"> +/// The processor type for message processing. +/// </typeparam> +/// <seealso cref="ITarget Class"/> +/**/ +template<class _SourceLinkRegistry, + class _MessageProcessorType = ordered_message_processor<typename _SourceLinkRegistry::type::source_type>> +class target_block : public ITarget<typename _SourceLinkRegistry::type::source_type> +{ +public: + + /// <summary> + /// The type of the payload for the incoming messages to this <c>target_block</c> object. + /// </summary> + /**/ + typedef typename _SourceLinkRegistry::type::source_type _Source_type; + + /// <summary> + /// The type of the <c>source_link_manager</c> this <c>target_block</c> object. + /// </summary> + /**/ + typedef source_link_manager<_SourceLinkRegistry> _SourceLinkManager; + + /// <summary> + /// The type of the iterator for the <c>source_link_manager</c> for this <c>target_block</c> object. + /// </summary> + /**/ + typedef typename _SourceLinkManager::iterator source_iterator; + + /// <summary> + /// Constructs a <c>target_block</c> object. + /// </summary> + /**/ + target_block() : _M_pFilter(NULL), _M_fDeclineMessages(false) + { + _Trace_agents(AGENTS_EVENT_CREATE, + ::Concurrency::details::_Trace_agents_get_id(this), + ::Concurrency::details::_Trace_agents_get_id(&_M_messageProcessor)); + } + + /// <summary> + /// Destroys the <c>target_block</c> object. + /// </summary> + /**/ + virtual ~target_block() + { + // All sources should have been unlinked + _CONCRT_ASSERT(_M_connectedSources.count() == 0); + delete _M_pFilter; + + _Trace_agents(AGENTS_EVENT_DESTROY, ::Concurrency::details::_Trace_agents_get_id(this)); + } + + /// <summary> + /// Asynchronously passes a message from a source block to this target block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// <para> The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if either the <paramref name="_PMessage"/> or <paramref name="_PSource"/> parameter is <c>NULL</c>.</para> + /// </remarks> + /**/ + virtual message_status propagate(_Inout_opt_ message<_Source_type> * _PMessage, _Inout_opt_ ISource<_Source_type> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by <c>consume</c> and the LWT. Doing so could + // result in a deadlock. + + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + if (_M_fDeclineMessages) + { + return declined; + } + + if (_M_pFilter != NULL && !(*_M_pFilter)(_PMessage->payload)) + { + return declined; + } + + return propagate_message(_PMessage, _PSource); + } + + /// <summary> + /// Synchronously passes a message from a source block to this target block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if either the <paramref name="_PMessage"/> or <paramref name="_PSource"/> parameter is <c>NULL</c>. + /// <para>Using the <c>send</c> method outside of message initiation and to propagate messages + /// within a network is dangerous and can lead to deadlock.</para> + /// <para>When <c>send</c> returns, the message has either already been accepted, and transferred into + /// the target block, or it has been declined by the target.</para> + /// </remarks> + /**/ + virtual message_status send(_Inout_ message<_Source_type> * _PMessage, _Inout_ ISource<_Source_type> * _PSource) + { + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + if (_M_fDeclineMessages) + { + return declined; + } + + if (_M_pFilter != NULL && !(*_M_pFilter)(_PMessage->payload)) + { + return declined; + } + + return send_message(_PMessage, _PSource); + } + +protected: + + /// <summary> + /// When overridden in a derived class, this method asynchronously passes a message from an <c>ISource</c> + /// block to this <c>target_block</c> object. It is invoked by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(_Inout_ message<_Source_type> * _PMessage, _Inout_ ISource<_Source_type> * _PSource) = 0; + + /// <summary> + /// When overridden in a derived class, this method synchronously passes a message from an <c>ISource</c> + /// block to this <c>target_block</c> object. It is invoked by the <c>send</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// By default, this block returns <c>declined</c> unless overridden by a derived class. + /// </remarks> + /**/ + virtual message_status send_message(_Inout_ message<_Source_type> *, _Inout_ ISource<_Source_type> *) + { + // By default we do not allow send() + return declined; + } + + /// <summary> + /// Links a specified source block to this <c>target_block</c> object. + /// </summary> + /// <param name="_PSource"> + /// A pointer to the <c>ISource</c> block that is to be linked. + /// </param> + /// <remarks> + /// This function should not be called directly on a <c>target_block</c> object. Blocks should be connected together + /// using the <c>link_target</c> method on <c>ISource</c> blocks, which will invoke the <c>link_source</c> method + /// on the corresponding target. + /// </remarks> + /**/ + virtual void link_source(_Inout_ ISource<_Source_type> * _PSource) + { + _M_connectedSources.add(_PSource); + _Trace_agents(AGENTS_EVENT_LINK, + ::Concurrency::details::_Trace_agents_get_id(_PSource), + ::Concurrency::details::_Trace_agents_get_id(this)); + } + + /// <summary> + /// Unlinks a specified source block from this <c>target_block</c> object. + /// </summary> + /// <param name="_PSource"> + /// A pointer to the <c>ISource</c> block that is to be unlinked. + /// </param> + /// This function should not be called directly on n <c>target_block</c> object. Blocks should be disconnected + /// using the <c>unlink_target</c> or <c>unlink_targets</c> methods on <c>ISource</c> blocks, which will invoke + /// the <c>unlink_source</c> method on the corresponding target. + /**/ + virtual void unlink_source(_Inout_ ISource<_Source_type> * _PSource) + { + _Trace_agents(AGENTS_EVENT_UNLINK, + ::Concurrency::details::_Trace_agents_get_id(_PSource), + ::Concurrency::details::_Trace_agents_get_id(this)); + + _M_connectedSources.remove(_PSource); + } + + /// <summary> + /// Unlinks all source blocks from this <c>target_block</c> object. + /// </summary> + /**/ + virtual void unlink_sources() + { + for (source_iterator _Iter = _M_connectedSources.begin(); *_Iter != NULL; ++_Iter) + { + ISource<_Source_type> * _PSource = *_Iter; + _PSource->unlink_target(this); + } + } + + /// <summary> + /// When overridden in a derived class, processes a message that was accepted by this <c>target_block</c> object. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the message that is to be handled. + /// </param> + /**/ + virtual void process_message(message<_Source_type> *) + { + } + + // + // Utility routines + // + + /// <summary> + /// Registers a filter method that will be invoked on + /// every message received. + /// </summary> + /// <param name="_Filter"> + /// The filter method. + /// </param> + /**/ + void register_filter(filter_method const& _Filter) + { + if (_Filter != NULL) + { + _M_pFilter = new filter_method(_Filter); + } + } + + /// <summary> + /// Indicates to the block that new messages should be declined. + /// </summary> + /// <remarks> + /// This method is called by the destructor to ensure that new messages are declined while destruction is in progress. + /// </remarks> + /**/ + void decline_incoming_messages() + { + _M_fDeclineMessages = true; + } + + /// <summary> + /// Initializes the base object. Specifically, the <c>message_processor</c> object needs + /// to be initialized. + /// </summary> + /// <param name="_PScheduler"> + /// The scheduler to be used for scheduling tasks. + /// </param> + /// <param name="_PScheduleGroup"> + /// The schedule group to be used for scheduling tasks. + /// </param> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + void initialize_target(_Inout_opt_ Scheduler * _PScheduler = NULL, _Inout_opt_ ScheduleGroup * _PScheduleGroup = NULL) + { + // Register a callback with the processor + _M_messageProcessor.initialize(_PScheduler, _PScheduleGroup, + // Processing and Propagating function used by ordered_message_processors + [this](message<_Source_type> * _PMessage) + { + // Handle message by calling process_message to maintain CRT100 compatibility + this->process_message(_PMessage); + }); + + // Register this target block as the owner of the connected sources + _M_connectedSources.register_target_block(this); + } + + /// <summary> + /// Enables batched processing for this block. + /// </summary> + /**/ + void enable_batched_processing() + { + _M_messageProcessor.initialize_batched_processing( + // Processing function used by CRT110 + [this](message<_Source_type> * _PMessage) + { + // Handle message through new process_input_message to use CRT110 batch processing + this->process_input_messages(_PMessage); + }, nullptr); + } + + /// <summary> + /// Asynchronously sends a message for processing. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the message being sent. + /// </param> + /**/ + void async_send(_Inout_opt_ message<_Source_type> * _PMessage) + { + _M_messageProcessor.async_send(_PMessage); + } + + /// <summary> + /// Synchronously send a message for processing. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the message being sent. + /// </param> + /**/ + void sync_send(_Inout_opt_ message<_Source_type> * _PMessage) + { + _M_messageProcessor.sync_send(_PMessage); + } + + /// <summary> + /// Waits for all asynchronous propagations to complete. + /// </summary> + /// <remarks> + /// This method is used by message block destructors to ensure all asynchronous operations + /// have had time to finish before destroying the block. + /// </remarks> + /**/ + void wait_for_async_sends() + { + // Decline new messages to ensure that messages are not dropped during the wait + decline_incoming_messages(); + + _M_messageProcessor.wait(); + } + + /// <summary> + /// Unlinks all sources after waiting for outstanding asynchronous send operations to complete. + /// </summary> + /// <remarks> + /// All target blocks should call this routine to remove the sources in their destructor. + /// </remarks> + /**/ + void remove_sources() + { + wait_for_async_sends(); + + unlink_sources(); + } + + /// <summary> + /// Processes messages that are received as inputs. + /// </summary> + /**/ + virtual void process_input_messages(_Inout_ message<_Source_type> * _PMessage) + { + throw invalid_operation("To use batched processing, you must override process_input_messages in the message block."); + } + + /// <summary> + /// The container for all the sources connected to this block. + /// </summary> + /**/ + _SourceLinkManager _M_connectedSources; + + /// <summary> + /// The filter function which determines whether offered messages should be accepted. + /// </summary> + /**/ + filter_method * _M_pFilter; + + /// <summary> + /// A <c>bool</c> that is set to indicate that all messages should be declined + /// in preparation for deleting the block + /// <summary> + /**/ + bool _M_fDeclineMessages; + + /// <summary> + /// The <c>message_processor</c> for this <c>target_block</c>. + /// <summary> + /**/ + _MessageProcessorType _M_messageProcessor; +}; + +//************************************************************************** +// Source Block: +//************************************************************************** + +/// <summary> +/// The <c>source_block</c> class is an abstract base class for source-only blocks. The class +/// provides basic link management functionality as well as common error checks. +/// </summary> +/// <typeparam name="_TargetLinkRegistry"> +/// Link registry to be used for holding the target links. +/// </typeparam> +/// <typeparam name="_MessageProcessorType"> +/// Processor type for message processing. +/// </typeparam> +/// <remarks> +/// Message blocks should derive from this block to take advantage of link management and +/// synchronization provided by this class. +/// </remarks> +/// <seealso cref="ISource Class"/> +/**/ +template<class _TargetLinkRegistry, + class _MessageProcessorType = ordered_message_processor<typename _TargetLinkRegistry::type::type>> +class source_block : public ISource<typename _TargetLinkRegistry::type::type> +{ +public: + + /// <summary> + /// The payload type of messages handled by this <c>source_block</c>. + /// </summary> + /**/ + typedef typename _TargetLinkRegistry::type::type _Target_type; + + /// <summary> + /// The iterator to walk the connected targets. + /// </summary> + /**/ + typedef typename _TargetLinkRegistry::iterator target_iterator; + + /// <summary> + /// Constructs a <c>source_block</c> object. + /// </summary> + /**/ + source_block() : + _M_pReservedFor(NULL), + _M_reservedId(-1), + _M_referenceCount(0) + { + _Trace_agents(AGENTS_EVENT_CREATE, + ::Concurrency::details::_Trace_agents_get_id(this), + ::Concurrency::details::_Trace_agents_get_id(&_M_messageProcessor)); + } + + /// <summary> + /// Destroys the <c>source_block</c> object. + /// </summary> + /**/ + virtual ~source_block() + { + // All targets should have been unlinked + _CONCRT_ASSERT(_M_connectedTargets.count() == 0); + + _Trace_agents(AGENTS_EVENT_DESTROY, ::Concurrency::details::_Trace_agents_get_id(this)); + } + + /// <summary> + /// Links a target block to this <c>source_block</c> object. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to an <c>ITarget</c> block to link to this <c>source_block</c> object. + /// </param> + /// <remarks> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the + /// parameter <paramref name="_PTarget"/> is <c>NULL</c>. + /// </remarks> + /**/ + virtual void link_target(_Inout_ ITarget<_Target_type> * _PTarget) + { + _R_lock _Lock(_M_internalLock); + + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + _M_connectedTargets.add(_PTarget); + _Invoke_link_source(_PTarget); + link_target_notification(_PTarget); + } + + /// <summary> + /// Unlinks a target block from this <c>source_block</c> object. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to an <c>ITarget</c> block to unlink from this <c>source_block</c> object. + /// </param> + /// <remarks> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the + /// parameter <paramref name="_PTarget"/> is <c>NULL</c>. + /// </remarks> + /**/ + virtual void unlink_target(_Inout_ ITarget<_Target_type> * _PTarget) + { + _R_lock _Lock(_M_internalLock); + + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (_M_connectedTargets.remove(_PTarget)) + { + // We were able to remove the target from our list. + // Inform the target to unlink from us + _Invoke_unlink_source(_PTarget); + } + } + + /// <summary> + /// Unlinks all target blocks from this <c>source_block</c> object. + /// </summary> + /**/ + virtual void unlink_targets() + { + _R_lock _Lock(_M_internalLock); + + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<_Target_type> * _PTarget = *_Iter; + _CONCRT_ASSERT(_PTarget != NULL); + + unlink_target(_PTarget); + } + + // All the targets should be unlinked. + _CONCRT_ASSERT(_M_connectedTargets.count() == 0); + } + + /// <summary> + /// Accepts a message that was offered by this <c>source_block</c> object, transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>accept</c> method. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the + /// parameter <paramref name="_PTarget"/> is <c>NULL</c>. + /// <para> + /// The <c>accept</c> method is called by a target while a message is being offered by this <c>ISource</c> block. + /// The message pointer returned may be different from the one passed into the <c>propagate</c> method + /// of the <c>ITarget</c> block, if this source decides to make a copy of the message. + /// </para> + /// </remarks> + /**/ + virtual message<_Target_type> * accept(runtime_object_identity _MsgId, _Inout_ ITarget<_Target_type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + // Assert if the target is not connected + _CONCRT_ASSERT(_M_connectedTargets.contains(_PTarget)); + + return accept_message(_MsgId); + } + + /// <summary> + /// Reserves a message previously offered by this <c>source_block</c> object. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>reserve</c> method. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. Reservations can fail + /// for many reasons, including: the message was already reserved or accepted by another target, the source could + /// deny reservations, and so forth. + /// </returns> + /// <remarks> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the + /// parameter <paramref name="_PTarget"/> is <c>NULL</c>. + /// <para> + /// After you call <c>reserve</c>, if it succeeds, you must call either <c>consume</c> or <c>release</c> + /// in order to take or give up possession of the message, respectively. + /// </para> + /// </remarks> + /**/ + virtual bool reserve(runtime_object_identity _MsgId, _Inout_ ITarget<_Target_type> * _PTarget) + { + _R_lock _Lock(_M_internalLock); + + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if ( _M_pReservedFor != NULL) + { + // Someone else is holding the reservation + return false; + } + + if (!reserve_message(_MsgId)) + { + // Failed to reserve the msg ID + return false; + } + + // Save the reserving target and the msg ID + _M_pReservedFor = _PTarget; + _M_reservedId = _MsgId; + + return true; + } + + /// <summary> + /// Consumes a message previously offered by this <c>source_block</c> object and successfully reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the reserved <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>consume</c> method. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// <para> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the + /// parameter <paramref name="_PTarget"/> is <c>NULL</c>. + /// </para> + /// <para> + /// The method throws a <see cref="bad_target Class">bad_target</see> exception if the parameter <paramref name="_PTarget"/> + /// does not represent the target that called <c>reserve</c>. + /// </para> + /// <para> + /// The <c>consume</c> method is similar to <c>accept</c>, but must always be preceded by a call to <c>reserve</c> that + /// returned <c>true</c>. + /// </para> + /// </remarks> + /**/ + virtual message<_Target_type> * consume(runtime_object_identity _MsgId, _Inout_ ITarget<_Target_type> * _PTarget) + { + _R_lock _Lock(_M_internalLock); + + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (_M_pReservedFor == NULL || _PTarget != _M_pReservedFor) + { + throw bad_target(); + } + + message<_Target_type> * _Msg = consume_message(_MsgId); + + if (_Msg != NULL) + { + // Clear the reservation + // _M_pReservedId is intentionally not reset so that it can assist in debugging + _M_pReservedFor = NULL; + + // Reservation is assumed to block propagation. Notify that propagation can now be resumed + resume_propagation(); + } + + return _Msg; + } + + /// <summary> + /// Releases a previous successful message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the reserved <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>release</c> method. + /// </param> + /// <remarks> + /// <para> + /// The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the + /// parameter <paramref name="_PTarget"/> is <c>NULL</c>. + /// </para> + /// <para> + /// The method throws a <see cref="bad_target Class">bad_target</see> exception if the parameter <paramref name="_PTarget"/> + /// does not represent the target that called <c>reserve</c>. + /// </para> + /// </remarks> + /**/ + virtual void release(runtime_object_identity _MsgId, _Inout_ ITarget<_Target_type> * _PTarget) + { + _R_lock _Lock(_M_internalLock); + + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (_PTarget != _M_pReservedFor) + { + throw bad_target(); + } + + release_message(_MsgId); + + // Clear the reservation + // _M_pReservedId is intentionally not reset so that it can assist in debugging + _M_pReservedFor = NULL; + + // Reservation is assumed to block propagation. Notify that propagation can now be resumed + resume_propagation(); + } + + /// <summary> + /// Acquires a reference count on this <c>source_block</c> object, to prevent deletion. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being linked to this source + /// during the <c>link_target</c> method. + /// </remarks> + /**/ + virtual void acquire_ref(_Inout_ ITarget<_Target_type> *) + { + _InterlockedIncrement(&_M_referenceCount); + } + + /// <summary> + /// Releases a reference count on this <c>source_block</c> object. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being unlinked from this source. + /// The source block is allowed to release any resources reserved for the target block. + /// </remarks> + /**/ + virtual void release_ref(_Inout_ ITarget<_Target_type> * _PTarget) + { + if (_PTarget != NULL) + { + _R_lock _Lock(_M_internalLock); + + // We assume that each target would keep a single reference on its source, so + // we call unlink target notification on every release. Otherwise, we would be + // required to keep a reference count per target. + // Note: unlink_target_notification can check the value of this _PTarget pointer, but + // must not dereference it, as it may have already been deleted. + unlink_target_notification(_PTarget); + } + + _InterlockedDecrement(&_M_referenceCount); + + // It is *unsafe* to touch the "this" pointer after decrementing the reference count + } + +protected: + + // + // Protected methods that a derived class can override to customize + // the functionality + // + + /// <summary> + /// A callback that notifies that a new target has been linked to this <c>source_block</c> object. + /// </summary> + /// <param name="_PTarget"> + /// The <c>ITarget</c> block that was linked. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<_Target_type> *) + { + // By default, we restart propagation if there is no pending resrvation + if (_M_pReservedFor == NULL) + { + propagate_to_any_targets(NULL); + } + } + + /// <summary> + /// A callback that notifies that a target has been unlinked from this <c>source_block</c> object. + /// </summary> + /// <param name="_PTarget"> + /// The <c>ITarget</c> block that was unlinked. + /// </param> + /**/ + virtual void unlink_target_notification(_Inout_ ITarget<_Target_type> * _PTarget) + { + // At this point, the target has already been disconnected from the + // source. It is safe to check the value of this pointer, but not + // safe to dereference it, as it may have already been deleted. + + // If the target being unlinked is the one holding the reservation, + // release the reservation + if (_M_pReservedFor == _PTarget) + { + release(_M_reservedId, _PTarget); + } + } + + /// <summary> + /// When overridden in a derived class, accepts an offered message by the source. + /// Message blocks should override this method to validate the <paramref name="_MsgId"/> and + /// return a message. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the <c>message</c> object. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /// <remarks> + /// To transfer ownership, the original message pointer should be returned. To maintain + /// ownership, a copy of message payload needs to be made and returned. + /// </remarks> + /**/ + virtual message<_Target_type> * accept_message(runtime_object_identity _MsgId) = 0; + + /// <summary> + /// When overridden in a derived class, reserves a message previously offered by this + /// <c>source_block</c> object. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) = 0; + + /// <summary> + /// When overridden in a derived class, consumes a message that was previously reserved. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being consumed. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_Target_type> * consume_message(runtime_object_identity _MsgId) = 0; + + /// <summary> + /// When overridden in a derived class, releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) = 0; + + /// <summary> + /// When overridden in a derived class, resumes propagation after a reservation has been released. + /// </summary> + /**/ + virtual void resume_propagation() = 0; + + /// <summary> + /// Process input messages. This is only useful for propagator blocks, which derive from source_block + /// </summary> + /**/ + virtual void process_input_messages(_Inout_ message<_Target_type> * _PMessage) + { + // source_blocks do not need to process anything + } + + /// <summary> + /// Propagate messages to targets. + /// </summary> + /**/ + virtual void propagate_output_messages() + { + throw invalid_operation("To use batched processing, you must override propagate_output_messages in the message block."); + } + + /// <summary> + /// When overridden in a derived class, propagates the given message to any or all of the linked targets. + /// This is the main propagation routine for message blocks. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the message that is to be propagated. + /// </param> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<_Target_type> * _PMessage) + { + throw invalid_operation("To use ordered message processing, you must override propagate_to_any_targets in the message block."); + } + + // + // Utility routines + // + /// <summary> + /// Initializes the <c>message_propagator</c> within this <c>source_block</c>. + /// </summary> + /// <param name="_PScheduler"> + /// The scheduler to be used for scheduling tasks. + /// </param> + /// <param name="_PScheduleGroup"> + /// The schedule group to be used for scheduling tasks. + /// </param> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + void initialize_source(_Inout_opt_ Scheduler * _PScheduler = NULL, _Inout_opt_ ScheduleGroup * _PScheduleGroup = NULL) + { + // Register a callback + _M_messageProcessor.initialize(_PScheduler, _PScheduleGroup, + [this](message<_Target_type> * _PMessage) + { + this->_Handle_message(_PMessage); + }); + } + + /// <summary> + /// Enables batched processing for this block. + /// </summary> + /**/ + void enable_batched_processing() + { + // Register callbacks for CRT110 batched processing + _M_messageProcessor.initialize_batched_processing( + // Processing function used by CRT110 + [this](message<_Target_type> * _PMessage) + { + // Handle message through new process_input_message to use CRT110 batch processing + this->process_input_messages(_PMessage); + }, + [this](void) + { + this->_Propagate_message(); + }); + } + + /// <summary> + /// Synchronously queues up messages and starts a propagation task, if this has not been done + /// already. + /// </summary> + /// <param name="_Msg"> + /// A pointer to a <c>message</c> object to synchronously send. + /// </param> + /**/ + virtual void sync_send(_Inout_opt_ message<_Target_type> * _Msg) + { + // Caller shall not be holding any locks when calling this routine + _M_messageProcessor.sync_send(_Msg); + } + + /// <summary> + /// Asynchronously queues up messages and starts a propagation task, if this has not been done + /// already + /// </summary> + /// <param name="_Msg"> + /// A pointer to a <c>message</c> object to asynchronously send. + /// </param> + /**/ + virtual void async_send(_Inout_opt_ message<_Target_type> * _Msg) + { + _M_messageProcessor.async_send(_Msg); + } + + /// <summary> + /// Waits for all asynchronous propagations to complete. This propagator-specific spin wait is used + /// in destructors of message blocks to make sure that all asynchronous propagations have time to finish + /// before destroying the block. + /// </summary> + /**/ + void wait_for_outstanding_async_sends() + { + _M_messageProcessor.wait(); + } + + /// <summary> + /// Removes all target links for this source block. This should be called from the destructor. + /// </summary> + /**/ + void remove_targets() + { + // Wait for outstanding propagation to complete. + wait_for_outstanding_async_sends(); + + unlink_targets(); + + _Wait_on_ref(); + } + + // + // Protected members + // + + /// <summary> + /// Connected target that is holding a reservation + /// </summary> + /**/ + ITarget<_Target_type> * _M_pReservedFor; + + /// <summary> + /// Reserved message ID + /// </summary> + /**/ + runtime_object_identity _M_reservedId; + + /// <summary> + /// Connected targets + /// </summary> + /**/ + _TargetLinkRegistry _M_connectedTargets; + + /// <summary> + /// Processor used for asynchronous message handling + /// </summary> + /**/ + _MessageProcessorType _M_messageProcessor; + +private: + + /// Private methods + + + // Message handler callback for the propagator. Invokes propagate_to_any_targets + // which derived classes should implement. + /**/ + void _Handle_message(message<_Target_type> * _PMessage) + { + // Hold a lock to synchronize with unlink targets + _R_lock _Lock(_M_internalLock); + propagate_to_any_targets(_PMessage); + } + + // Message handler callback for the processor. Invokes process_input_messages + // which derived classes should implement. + /**/ + void _Process_message(message<_Target_type> * _PMessage) + { + // Don't need a lock to process the message + process_input_messages(_PMessage); + } + + // Message handler callback for the propagator. Invokes propagate_output_messages + // which derived classes should implement. + /**/ + void _Propagate_message() + { + // Hold a lock to synchronize with unlink targets + _R_lock _Lock(_M_internalLock); + propagate_output_messages(); + } + + // Wait for the reference on this block to drop to zero + /**/ + void _Wait_on_ref(long _RefCount = 0) + { + ::Concurrency::details::_SpinWaitBackoffNone spinWait; + while(_M_referenceCount != _RefCount) + { + spinWait._SpinOnce(); + } + } + + // Private Data members + + /// <summary> + /// Internal lock used for the following synchronization: + /// 1. Synchronize between link and unlink target + /// 2. Synchronize between propagate_to_any_targets and unlink_target + /// 3. Synchronize between reserve and consume/release + /// </summary> + /**/ + ::Concurrency::details::_ReentrantPPLLock _M_internalLock; + + volatile long _M_referenceCount; + +}; + +//************************************************************************** +// Propagator (source and target) Block: +//************************************************************************** +/// <summary> +/// The <c>propagator_block</c> class is an abstract base class for message blocks that are both a source and target. +/// It combines the functionality of both the <c>source_block</c> and <c>target_block</c> classes. +/// </summary> +/// <typeparam name="_TargetLinkRegistry"> +/// The link registry to be used for holding the target links. +/// </typeparam> +/// <typeparam name="_SourceLinkRegistry"> +/// The link registry to be used for holding the source links. +/// </typeparam> +/// <typeparam name="_MessageProcessorType"> +/// The processor type for message processing. +/// </typeparam> +/// <remarks> +/// To avoid multiple inheritance, the <c>propagator_block</c> class inherits from the <c>source_block</c> class and <c>ITarget</c> +/// abstract class. Most of the functionality in the <c>target_block</c> class is replicated here. +/// </remarks> +/// <seealso cref="source_block Class"/> +/// <seealso cref="ITarget Class"/> +/**/ +template<class _TargetLinkRegistry, class _SourceLinkRegistry, + class _MessageProcessorType = ordered_message_processor<typename _TargetLinkRegistry::type::type>> +class propagator_block : public source_block<_TargetLinkRegistry, _MessageProcessorType>, public ITarget<typename _SourceLinkRegistry::type::source_type> +{ +public: + + /// <summary> + /// The type of the payload for the incoming message to this <c>propagator_block</c>. + /// </summary> + /**/ + typedef typename _SourceLinkRegistry::type::source_type _Source_type; + + /// <summary> + /// The type of the <c>source_link_manager</c> this <c>propagator_block</c>. + /// </summary> + /**/ + typedef source_link_manager<_SourceLinkRegistry> _SourceLinkManager; + + /// <summary> + /// The type of the iterator for the <c>source_link_manager</c> for this <c>propagator_block</c>. + /// </summary> + /**/ + typedef typename _SourceLinkManager::iterator source_iterator; + + /// <summary> + /// Constructs a <c>propagator_block</c> object. + /// </summary> + /**/ + propagator_block() : _M_pFilter(NULL), _M_fDeclineMessages(false) + { + } + + /// <summary> + /// Destroys a <c>propagator_block</c> object. + /// </summary> + /**/ + virtual ~propagator_block() + { + remove_network_links(); + + delete _M_pFilter; + } + + /// <summary> + /// Asynchronously passes a message from a source block to this target block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// The <c>propagate</c> method is invoked on a target block by a linked source block. It queues up an + /// asynchronous task to handle the message, if one is not already queued or executing. + /// <para> The method throws an <see cref="invalid_argument Class">invalid_argument</see> exception + /// if either the <paramref name="_PMessage"/> or <paramref name="_PSource"/> parameter is <c>NULL</c>.</para> + /// </remarks> + /**/ + virtual message_status propagate(_Inout_opt_ message<_Source_type> * _PMessage, _Inout_opt_ ISource<_Source_type> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by <c>consume</c> and the LWT. Doing so could + // result in a deadlock. + + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + if (_M_fDeclineMessages) + { + return declined; + } + + if (_M_pFilter != NULL && !(*_M_pFilter)(_PMessage->payload)) + { + return declined; + } + + return propagate_message(_PMessage, _PSource); + } + + /// <summary> + /// Synchronously initiates a message to this block. Called by an <c>ISource</c> block. + /// When this function completes, the message will already have propagated into the block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// This method throws an <see cref="invalid_argument Class">invalid_argument</see> exception if either + /// the <paramref name="_PMessage"/> or <paramref name="_PSource"/> parameter is <c>NULL</c>. + /// </remarks> + /**/ + virtual message_status send(_Inout_ message<_Source_type> * _PMessage, _Inout_ ISource<_Source_type> * _PSource) + { + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + if (_M_fDeclineMessages) + { + return declined; + } + + if (_M_pFilter != NULL && !(*_M_pFilter)(_PMessage->payload)) + { + return declined; + } + + return send_message(_PMessage, _PSource); + } + +protected: + + /// <summary> + /// When overridden in a derived class, this method asynchronously passes a message from an <c>ISource</c> + /// block to this <c>propagator_block</c> object. It is invoked by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(_Inout_ message<_Source_type> * _PMessage, _Inout_ ISource<_Source_type> * _PSource) = 0; + + /// <summary> + /// When overridden in a derived class, this method synchronously passes a message from an <c>ISource</c> + /// block to this <c>propagator_block</c> object. It is invoked by the <c>send</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// By default, this block returns <c>declined</c> unless overridden by a derived class. + /// </remarks> + /**/ + virtual message_status send_message(_Inout_ message<_Source_type> *, _Inout_ ISource<_Source_type> *) + { + // By default we do not allow send() + return declined; + } + + /// <summary> + /// Links a specified source block to this <c>propagator_block</c> object. + /// </summary> + /// <param name="_PSource"> + /// A pointer to the <c>ISource</c> block that is to be linked. + /// </param> + /**/ + virtual void link_source(_Inout_ ISource<_Source_type> * _PSource) + { + _M_connectedSources.add(_PSource); + _Trace_agents(AGENTS_EVENT_LINK, + ::Concurrency::details::_Trace_agents_get_id(_PSource), + ::Concurrency::details::_Trace_agents_get_id(this)); + } + + /// <summary> + /// Unlinks a specified source block from this <c>propagator_block</c> object. + /// </summary> + /// <param name="_PSource"> + /// A pointer to the <c>ISource</c> block that is to be unlinked. + /// </param> + /**/ + virtual void unlink_source(_Inout_ ISource<_Source_type> * _PSource) + { + _Trace_agents(AGENTS_EVENT_UNLINK, + ::Concurrency::details::_Trace_agents_get_id(_PSource), + ::Concurrency::details::_Trace_agents_get_id(this)); + + _M_connectedSources.remove(_PSource); + } + + /// <summary> + /// Unlinks all source blocks from this <c>propagator_block</c> object. + /// </summary> + /**/ + virtual void unlink_sources() + { + for (source_iterator _Iter = _M_connectedSources.begin(); *_Iter != NULL; ++_Iter) + { + ISource<_Source_type> * _PSource = *_Iter; + _PSource->unlink_target(this); + } + } + + // + // Utility routines + // + + /// <summary> + /// Process input messages. This is only useful for propagator blocks, which derive from source_block + /// </summary> + /**/ + virtual void process_input_messages(_Inout_ message<_Target_type> * _PMessage) + { + throw invalid_operation("To use batched processing, you must override process_input_messages in the message block."); + } + + /// <summary> + /// Initializes the base object. Specifically, the <c>message_processor</c> object needs + /// to be initialized. + /// </summary> + /// <param name="_PScheduler"> + /// The scheduler to be used for scheduling tasks. + /// </param> + /// <param name="_PScheduleGroup"> + /// The schedule group to be used for scheduling tasks. + /// </param> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + void initialize_source_and_target(_Inout_opt_ Scheduler * _PScheduler = NULL, _Inout_opt_ ScheduleGroup * _PScheduleGroup = NULL) + { + initialize_source(_PScheduler, _PScheduleGroup); + + // Register this propagator block as the owner of the connected sources + _M_connectedSources.register_target_block(this); + } + + /// <summary> + /// Registers a filter method that will be invoked on every received message. + /// </summary> + /// <param name="_Filter"> + /// The filter method. + /// </param> + /**/ + void register_filter(filter_method const& _Filter) + { + if (_Filter != NULL) + { + _M_pFilter = new filter_method(_Filter); + } + } + + /// <summary> + /// Indicates to the block that new messages should be declined. + /// </summary> + /// <remarks> + /// This method is called by the destructor to ensure that new messages are declined while destruction is in progress. + /// </remarks> + /**/ + void decline_incoming_messages() + { + _M_fDeclineMessages = true; + } + + /// <summary> + /// Removes all the source and target network links from this <c>propagator_block</c> object. + /// </summary> + /**/ + void remove_network_links() + { + // Decline messages while the links are being removed + decline_incoming_messages(); + + // Remove all the target links. This waits for + // all outstanding async propagation operations. + remove_targets(); + + // unlink all sources. The above steps guarantee that + // they can be removed safely. + unlink_sources(); + } + + /// <summary> + /// The container for all the sources connected to this block. + /// </summary> + /**/ + _SourceLinkManager _M_connectedSources; + + /// <summary> + /// The filter function which determines whether offered messages should be accepted. + /// </summary> + /**/ + filter_method * _M_pFilter; + + /// <summary> + /// A <c>bool</c> that is set to indicate that all messages should be declined + /// in preparation for deleting the block + /// <summary> + /**/ + volatile bool _M_fDeclineMessages; +}; + +//************************************************************************** +// Unbounded Buffers: +//************************************************************************** + +/// <summary> +/// An <c>unbounded_buffer</c> messaging block is a multi-target, multi-source, ordered +/// <c>propagator_block</c> capable of storing an unbounded number of messages. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type of the messages stored and propagated by the buffer. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/// <seealso cref="overwrite_buffer Class"/> +/// <seealso cref="single_assignment Class"/> +/**/ +template<class _Type> +class unbounded_buffer : public propagator_block<multi_link_registry<ITarget<_Type>>, multi_link_registry<ISource<_Type>>> +{ +public: + /// <summary> + /// Constructs an <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>unbounded_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + unbounded_buffer() : + _M_fForceRepropagation(false) + { + initialize_source_and_target(); + enable_batched_processing(); + } + + /// <summary> + /// Constructs an <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>unbounded_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + unbounded_buffer(filter_method const& _Filter) : + _M_fForceRepropagation(false) + { + initialize_source_and_target(); + enable_batched_processing(); + register_filter(_Filter); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs an <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>unbounded_buffer</c> object is scheduled. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>unbounded_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + unbounded_buffer(Scheduler& _PScheduler) : + _M_fForceRepropagation(false) + { + initialize_source_and_target(&_PScheduler); + enable_batched_processing(); + } + + /// <summary> + /// Constructs an <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>unbounded_buffer</c> messaging block is scheduled. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>unbounded_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + unbounded_buffer(Scheduler& _PScheduler, filter_method const& _Filter) : + _M_fForceRepropagation(false) + { + initialize_source_and_target(&_PScheduler); + enable_batched_processing(); + register_filter(_Filter); + } + + /// <summary> + /// Constructs an <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>unbounded_buffer</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>unbounded_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + unbounded_buffer(ScheduleGroup& _PScheduleGroup) : + _M_fForceRepropagation(false) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + enable_batched_processing(); + } + + /// <summary> + /// Constructs an <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>unbounded_buffer</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>unbounded_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + unbounded_buffer(ScheduleGroup& _PScheduleGroup, filter_method const& _Filter) : + _M_fForceRepropagation(false) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + enable_batched_processing(); + register_filter(_Filter); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys the <c>unbounded_buffer</c> messaging block. + /// </summary> + /**/ + ~unbounded_buffer() + { + // Remove all links + remove_network_links(); + + // Clean up any messages left in this message block + _Delete_stored_messages(); + } + + /// <summary> + /// Adds an item to the <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_Item"> + /// The item to add. + /// </param> + /// <returns> + /// <c>true</c> if the item was accepted, <c>false</c> otherwise. + /// </returns> + /**/ + bool enqueue(_Type const& _Item) + { + return Concurrency::send<_Type>(this, _Item); + } + + /// <summary> + /// Removes an item from the <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <returns> + /// The payload of the message removed from the <c>unbounded_buffer</c>. + /// </returns> + /**/ + _Type dequeue() + { + return receive<_Type>(this); + } + + +protected: + + // + // propagator_block protected function implementations + // + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>unbounded_buffer</c> messaging block. + /// It is invoked by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by <c>consume</c> and the LWT. Doing so could + // result in a deadlock. + + message_status _Result = accepted; + + // Accept the message being propagated + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_PMessage != NULL) + { + async_send(_PMessage); + } + else + { + _Result = missed; + } + + return _Result; + } + + /// <summary> + /// Synchronously passes a message from an <c>ISource</c> block to this <c>unbounded_buffer</c> messaging block. + /// It is invoked by the <c>send</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status send_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_PMessage != NULL) + { + sync_send(_PMessage); + } + else + { + return missed; + } + + return accepted; + } + + /// <summary> + /// Overrides the <c>supports_anonymous_source</c> method to indicate that this block can + /// accept messages offered to it by a source that is not linked. + /// </summary> + /// <returns> + /// <c>true</c> because the block does not postpone offered messages. + /// </returns> + /**/ + virtual bool supports_anonymous_source() + { + return true; + } + + /// <summary> + /// Accepts a message that was offered by this <c>unbounded_buffer</c> messaging block, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /**/ + virtual message<_Type> * accept_message(runtime_object_identity _MsgId) + { + // + // Peek at the head message in the message buffer. If the IDs match + // dequeue and transfer ownership + // + message<_Type> * _Msg = NULL; + + if (_M_messageBuffer._Is_head(_MsgId)) + { + _Msg = _M_messageBuffer._Dequeue(); + } + + return _Msg; + } + + /// <summary> + /// Reserves a message previously offered by this <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) + { + // Allow reservation if this is the head message + return _M_messageBuffer._Is_head(_MsgId); + } + + /// <summary> + /// Consumes a message previously offered by the <c>unbounded_buffer</c> messaging block and reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being consumed. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_Type> * consume_message(runtime_object_identity _MsgId) + { + // By default, accept the message + return accept_message(_MsgId); + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) + { + // The head message is the one reserved. + if (!_M_messageBuffer._Is_head(_MsgId)) + { + throw message_not_found(); + } + } + + /// <summary> + /// Resumes propagation after a reservation has been released. + /// </summary> + /**/ + virtual void resume_propagation() + { + // If there are any messages in the buffer, propagate them out + if (_M_messageBuffer._Count() > 0) + { + // Set the flag to force a repropagation. This flag is cleared when a propagation happens + // The only functions that call this are release, consume, and link_target, all of which + // hold the internal lock, so the flag is guaranteed to be read by propagation, which also + // holds the same lock. + _M_fForceRepropagation = true; + + // async send a NULL value to initiate the repropagation + async_send(NULL); + } + } + + /// <summary> + /// A callback that notifies that a new target has been linked to this <c>unbounded_buffer</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<_Type> * _PTarget) + { + // If the message queue is blocked due to reservation + // there is no need to do any message propagation + if (_M_pReservedFor != NULL) + { + return; + } + + message<_Type> * _Msg = _M_messageBuffer._Peek(); + + if (_Msg != NULL) + { + // Propagate the head message to the new target + message_status _Status = _PTarget->propagate(_Msg, this); + + if (_Status == accepted) + { + // The target accepted the message, restart propagation. + _Propagate_priority_order(_M_messageBuffer); + } + + // If the status is anything other than accepted, then leave + // the message queue blocked. + } + } + + /// <summary> + /// Places the <c>message</c> <paramref name="_PMessage"/> in this <c>unbounded_buffer</c> messaging block and + /// tries to offer it to all of the linked targets. + /// </summary> + virtual void process_input_messages(_Inout_ message<_Type> * _PMessage) + { + if (_PMessage != NULL) + { + _M_processedMessages._Enqueue(_PMessage); + } + } + + /// <summary> + /// Places the <c>message</c> <paramref name="_PMessage"/> in this <c>unbounded_buffer</c> messaging block and + /// tries to offer it to all of the linked targets. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to a <c>message</c> object that this <c>unbounded_buffer</c> has taken ownership of. + /// </param> + /// <remarks> + /// If another message is already ahead of this one in the <c>unbounded_buffer</c>, + /// propagation to linked targets will not occur until any earlier messages have been accepted + /// or consumed. The first linked target to successfully <c>accept</c> or <c>consume</c> the + /// message takes ownership, and no other target can then get the message. + /// </remarks> + /**/ + virtual void propagate_output_messages() + { + // Move the messages from the processedMessages queue to the internal storage + // to make them ready for propagating out + + // If there are messages in the message queue, the queue is blocked and a + // propagation should not happen unless it has been forced using resume_propagation + bool _FIsBlocked = (_M_messageBuffer._Count() > 0); + + for(;;) + { + message<_Type> * _PInputMessage = _M_processedMessages._Dequeue(); + if(_PInputMessage == NULL) + { + break; + } + _M_messageBuffer._Enqueue(_PInputMessage); + } + + if (_M_fForceRepropagation == false && _FIsBlocked == true) + { + return; + } + + // Reset the repropagation flag because a propagation has started. + _M_fForceRepropagation = false; + + // Attempt to propagate messages to all the targets + _Propagate_priority_order(_M_messageBuffer); + } + +private: + + /// <summary> + /// Propagates messages in priority order. + /// </summary> + /// <param name="_MessageBuffer"> + /// Reference to a message queue with messages to be propagated + /// </param> + /**/ + void _Propagate_priority_order(::Concurrency::details::_Queue<message<_Target_type>> & _MessageBuffer) + { + message<_Target_type> * _Msg = _MessageBuffer._Peek(); + + // If someone has reserved the _Head message, don't propagate anymore + if (_M_pReservedFor != NULL) + { + return; + } + + while (_Msg != NULL) + { + message_status _Status = declined; + + // Always start from the first target that linked + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<_Target_type> * _PTarget = *_Iter; + _Status = _PTarget->propagate(_Msg, this); + + // Ownership of message changed. Do not propagate this + // message to any other target. + if (_Status == accepted) + { + break; + } + + // If the target just propagated to reserved this message, stop + // propagating it to others + if (_M_pReservedFor != NULL) + { + break; + } + } + + // If status is anything other than accepted, then the head message + // was not propagated out. Thus, nothing after it in the queue can + // be propagated out. Cease propagation. + if (_Status != accepted) + { + break; + } + + // Get the next message + _Msg = _MessageBuffer._Peek(); + } + } + + /// <summary> + /// Deletes all messages currently stored in this message block. Should be called + /// by the destructor to ensure any messages propagated in are cleaned up. + /// </summary> + /**/ + void _Delete_stored_messages() + { + // Input messages for this message block are in the base-class input buffer + // All messages in that buffer are guaranteed to have moved to the output + // buffer because the destructor first waits for all async sends to finish + // before reaching this point + + // Delete any messages remaining in the output queue + for (;;) + { + message<_Type> * _Msg = _M_messageBuffer._Dequeue(); + if (_Msg == NULL) + { + break; + } + delete _Msg; + } + } + + /// <summary> + /// Message queue used to store processed messages + /// </summary> + /**/ + ::Concurrency::details::_Queue<message<_Type>> _M_processedMessages; + + /// <summary> + /// Message queue used to store messages + /// </summary> + /**/ + ::Concurrency::details::_Queue<message<_Type>> _M_messageBuffer; + + /// <summary> + /// A bool to signal to the processor to force a repropagation to occur + /// </summary> + /**/ + bool _M_fForceRepropagation; + +private: + // + // Hide assignment operator and copy constructor + // + unbounded_buffer const &operator =(unbounded_buffer const&); // no assignment operator + unbounded_buffer(unbounded_buffer const &); // no copy constructor +}; + +//************************************************************************** +// Overwrite Buffers: +//************************************************************************** + +/// <summary> +/// An <c>overwrite_buffer</c> messaging block is a multi-target, multi-source, ordered +/// <c>propagator_block</c> capable of storing a single message at +/// a time. New messages overwrite previously held ones. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type of the messages stored and propagated by the buffer. +/// </typeparam> +/// <remarks> +/// An <c>overwrite_buffer</c> messaging block propagates out copies of its stored message to each of its targets. +/// <para>For more information, see <see cref="Asynchronous Message Blocks"/>.</para> +/// </remarks> +/// <seealso cref="unbounded_buffer Class"/> +/// <seealso cref="single_assignment Class"/> +/**/ +template<class _Type> +class overwrite_buffer : public propagator_block<multi_link_registry<ITarget<_Type>>, multi_link_registry<ISource<_Type>>> +{ +public: + /// <summary> + /// Constructs an <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>overwrite_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + overwrite_buffer() : + _M_fIsInitialized(false), _M_pMessage(NULL), _M_pReservedMessage(NULL) + { + initialize_source_and_target(); + } + + /// <summary> + /// Constructs an <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>overwrite_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + overwrite_buffer(filter_method const& _Filter) : + _M_fIsInitialized(false), _M_pMessage(NULL), _M_pReservedMessage(NULL) + { + initialize_source_and_target(); + register_filter(_Filter); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs an <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>overwrite_buffer</c> messaging block is scheduled. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>overwrite_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + overwrite_buffer(Scheduler& _PScheduler) : + _M_fIsInitialized(false), _M_pMessage(NULL), _M_pReservedMessage(NULL) + { + initialize_source_and_target(&_PScheduler); + } + + /// <summary> + /// Constructs an <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>overwrite_buffer</c> messaging block is scheduled. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>overwrite_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + overwrite_buffer(Scheduler& _PScheduler, + filter_method const& _Filter) : + _M_fIsInitialized(false), _M_pMessage(NULL), _M_pReservedMessage(NULL) + { + initialize_source_and_target(&_PScheduler); + register_filter(_Filter); + } + + /// <summary> + /// Constructs an <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>overwrite_buffer</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>overwrite_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + overwrite_buffer(ScheduleGroup& _PScheduleGroup) : + _M_fIsInitialized(false), _M_pMessage(NULL), _M_pReservedMessage(NULL) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + } + + /// <summary> + /// Constructs an <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>overwrite_buffer</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>overwrite_buffer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + overwrite_buffer(ScheduleGroup& _PScheduleGroup, + filter_method const& _Filter) : + _M_fIsInitialized(false), _M_pMessage(NULL), _M_pReservedMessage(NULL) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + register_filter(_Filter); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys the <c>overwrite_buffer</c> messaging block. + /// </summary> + /**/ + ~overwrite_buffer() + { + // Remove all links that are targets of this overwrite_buffer + remove_network_links(); + + // Clean up any messages left in this message block + _Delete_stored_messages(); + } + + /// <summary> + /// Checks whether this <c>overwrite_buffer</c> messaging block has a value yet. + /// </summary> + /// <returns> + /// <c>true</c> if the block has received a value, <c>false</c> otherwise. + /// </returns> + /**/ + bool has_value() const + { + return _M_fIsInitialized != 0; + } + + /// <summary> + /// Gets a reference to the current payload of the message being stored in the <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <returns> + /// The payload of the currently stored message. + /// </returns> + /// <remarks> + /// The value stored in the <c>overwrite_buffer</c> could change immediately after this method returns. This method will + /// wait until a message arrives if no message is currently stored in the <c>overwrite_buffer</c>. + /// </remarks> + /**/ + _Type value() + { + return receive<_Type>(this); + } + +protected: + + // + // propagator_block protected function implementation + // + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>overwrite_buffer</c> messaging block. + /// It is invoked by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by Consume and the LWT. Doing so could + // result in a deadlock with the Consume call. + + message_status _Result = accepted; + + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + // + // If message was accepted, set the member variables for + // this block and start the asynchronous propagation task + // + if (_PMessage != NULL) + { + // Add a reference for the async_send holding the message + _PMessage->add_ref(); + + async_send(_PMessage); + } + else + { + _Result = missed; + } + + return _Result; + } + + /// <summary> + /// Synchronously passes a message from an <c>ISource</c> block to this <c>overwrite_buffer</c> messaging block. + /// It is invoked by the <c>send</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status send_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + // + // If message was accepted, set the member variables for + // this block and start the asynchronous propagation task + // + if (_PMessage != NULL) + { + // Add a reference for the sync_send holding the message + _PMessage->add_ref(); + + sync_send(_PMessage); + } + else + { + return missed; + } + + return accepted; + } + + /// <summary> + /// Overrides the <c>supports_anonymous_source</c> method to indicate that this block can + /// accept messages offered to it by a source that is not linked. + /// </summary> + /// <returns> + /// <c>true</c> because the block does not postpone offered messages. + /// </returns> + /**/ + virtual bool supports_anonymous_source() + { + return true; + } + + /// <summary> + /// Accepts a message that was offered by this <c>overwrite_buffer</c> messaging block, + /// returning a copy of the message to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// The <c>overwrite_buffer</c> messaging block returns copies of the message + /// to its targets, rather than transferring ownership of the currently + /// held message. + /// </remarks> + /**/ + virtual message<_Type> * accept_message(runtime_object_identity _MsgId) + { + // + // If the internal message has not yet been initialized yet, return NULL + // + if (_M_pMessage == NULL) + { + return NULL; + } + + // + // Instead of returning the internal message, we return a copy of the + // message stored. + // + // Because we are returning a copy, the accept routine for an overwritebuffer + // does not need to grab the internalLock + // + message<_Type> * _Msg = NULL; + if (_M_pMessage->msg_id() == _MsgId) + { + _Msg = new message<_Type>(_M_pMessage->payload); + } + + return _Msg; + } + + /// <summary> + /// Reserves a message previously offered by this <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) + { + // Ensure that this message currently exists in the overwrite buffer + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + return false; + } + + // Can only reserve one message, any other blocks trying to reserve + // will return false + _CONCRT_ASSERT(_M_pReservedMessage == NULL); + + // Save this message away + _M_pReservedMessage = _M_pMessage; + + // Add a reference for this message to prevent deletion + _M_pReservedMessage->add_ref(); + + return true; + } + + /// <summary> + /// Consumes a message previously offered by the <c>overwrite_buffer</c> messaging block and reserved by the target, + /// returning a copy of the message to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being consumed. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_Type> * consume_message(runtime_object_identity _MsgId) + { + // Leave and return NULL if this msgId doesn't match the reserved message + // Otherwise this is a pull of a later overwritten message, and messages + // could them appear out of order. + if (_M_pReservedMessage != NULL && _M_pReservedMessage->msg_id() != _MsgId) + { + return NULL; + } + // This redundant assert is specifically to make the /analyze switch happy, which cannot recognize the same assertion above in if stmnt. + _CONCRT_ASSERT( _M_pReservedMessage != NULL ); + + _Type _Payload = _M_pReservedMessage->payload; + + // Take the reserved message + message<_Type> * _Result = new message<_Type>(_Payload); + + if (_M_pReservedMessage->remove_ref() == 0) + { + delete _M_pReservedMessage; + } + _M_pReservedMessage = NULL; + + return _Result; + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) + { + _CONCRT_ASSERT(_M_fIsInitialized); + _CONCRT_ASSERT(_M_pReservedMessage != NULL); + + if (_MsgId != _M_pReservedMessage->msg_id()) + { + throw message_not_found(); + } + + if (_M_pReservedMessage->remove_ref() == 0) + { + delete _M_pReservedMessage; + } + _M_pReservedMessage = NULL; + } + + /// <summary> + /// Resumes propagation after a reservation has been released. + /// </summary> + /**/ + virtual void resume_propagation() + { + // On reservation we do not stop propagation. So there + // is nothing to be done to resume propagation. + } + + /// <summary> + /// A callback that notifies that a new target has been linked to this <c>overwrite_buffer</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<_Type> * _PTarget) + { + // If there is a message available already, propagate it + if (_M_pMessage != NULL) + { + _PTarget->propagate(_M_pMessage, this); + } + } + + /// <summary> + /// Places the <c>message</c> <paramref name="_PMessage"/> in this <c>overwrite_buffer</c> messaging block and + /// offers it to all of the linked targets. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to a <c>message</c> object that this <c>overwrite_buffer</c> has taken ownership of. + /// </param> + /// <remarks> + /// This method overwrites the current message in the <c>overwrite_buffer</c> with the newly + /// accepted message <paramref name="_PMessage"/>. + /// </remarks> + /**/ + virtual void propagate_to_any_targets(_Inout_ message<_Type> * _PMessage) + { + // Move the message from the queuedMessages Buffer to the internal storage + + // Add a reference for the overwrite_buffer holding the message + _PMessage->add_ref(); + + if (_M_pMessage != NULL) + { + if (_M_pMessage->remove_ref() == 0) + { + delete _M_pMessage; + } + } + + _M_pMessage = _PMessage; + + // Now that message has been received, set this block as initialized + _M_fIsInitialized = true; + + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + // Overwrite buffers can propagate its message out + // to any number of Targets + + ITarget<_Type> * _PTarget = *_Iter; + _PTarget->propagate(_PMessage, this); + } + + if (_PMessage->remove_ref() == 0) + { + delete _PMessage; + } + } + +private: + + /// <summary> + /// Deletes all messages currently stored in this message block. Should be called + /// by the destructor to ensure any messages propagated in are cleaned up. + /// </summary> + /**/ + void _Delete_stored_messages() + { + // Input messages for this message block are in the base-class input buffer + // All messages in that buffer are guaranteed to have moved to the output + // buffer because the destructor first waits for all async sends to finish + // before reaching this point + + // The messages for an overwrite buffer are deleted when overwritten + // through reference counting. This final check is put in place in + // case any message still exists in the buffer when the overwrite_buffer + // is deleted. The reference count of this message has not yet reached + // zero because it hasn't been overwritten yet. It is safe because of + // we have finished all propagation. + if (_M_pMessage != NULL) + { + // A block can only have a reserved message after receiving a message + // at some point, so it must be within the above if-clause. + // Now delete the reserved message if it is non-NULL and different from + // the saved internal message + if (_M_pReservedMessage != NULL && _M_pReservedMessage != _M_pMessage) + { + delete _M_pReservedMessage; + } + delete _M_pMessage; + } + } + + // + // Private Data Members + // + + // The message being stored + message<_Type> * _M_pMessage; + + // The message being reserved + message<_Type> * _M_pReservedMessage; + + // The marker for whether the overwrite buffer has already been initialized + volatile bool _M_fIsInitialized; + +private: + // + // Hide assignment operator and copy constructor + // + overwrite_buffer const &operator =(overwrite_buffer const&); // no assignment operator + overwrite_buffer(overwrite_buffer const &); // no copy constructor +}; + +//************************************************************************** +// Call: +//************************************************************************** + +/// <summary> +/// A <c>call</c> messaging block is a multi-source, ordered <c>target_block</c> that +/// invokes a specified function when receiving a message. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type of the messages propagated to this block. +/// </typeparam> +/// <typeparam name="_FunctorType"> +/// The signature of functions that this block can accept. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/// <seealso cref="transformer Class"/> +/**/ +template<class _Type, class _FunctorType = std::tr1::function<void(_Type const&)>> +class call : public target_block<multi_link_registry<ISource<_Type>>> +{ + /// <summary> + /// The function type that this block executes upon receiving a <c>message</c>. + /// </summary> + /**/ + typedef _FunctorType _Call_method; + +public: + /// <summary> + /// Constructs a <c>call</c> messaging block. + /// </summary> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Call_method"/> is a functor with signature <c>void (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + call(_Call_method const& _Func) : + _M_pFunc(_Func) + { + initialize_target(); + enable_batched_processing(); + } + + /// <summary> + /// Constructs a <c>call</c> messaging block. + /// </summary> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Call_method"/> is a functor with signature <c>void (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + call(_Call_method const& _Func, + filter_method const& _Filter) : + _M_pFunc(_Func) + { + initialize_target(); + enable_batched_processing(); + register_filter(_Filter); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs a <c>call</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>call</c> messaging block is scheduled. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Call_method"/> is a functor with signature <c>void (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + call(Scheduler& _PScheduler, + _Call_method const& _Func) : + _M_pFunc(_Func) + { + initialize_target(&_PScheduler); + enable_batched_processing(); + } + + /// <summary> + /// Constructs a <c>call</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>call</c> messaging block is scheduled. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Call_method"/> is a functor with signature <c>void (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + call(Scheduler& _PScheduler, + _Call_method const& _Func, + filter_method const& _Filter) : + _M_pFunc(_Func) + { + initialize_target(&_PScheduler); + enable_batched_processing(); + register_filter(_Filter); + } + + /// <summary> + /// Constructs a <c>call</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>call</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Call_method"/> is a functor with signature <c>void (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + call(ScheduleGroup& _PScheduleGroup, + _Call_method const& _Func) : + _M_pFunc(_Func) + { + initialize_target(NULL, &_PScheduleGroup); + enable_batched_processing(); + } + + /// <summary> + /// Constructs a <c>call</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>call</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Call_method"/> is a functor with signature <c>void (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>call</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + call(ScheduleGroup& _PScheduleGroup, + _Call_method const& _Func, + filter_method const& _Filter) : + _M_pFunc(_Func) + { + initialize_target(NULL, &_PScheduleGroup); + enable_batched_processing(); + register_filter(_Filter); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys the <c>call</c> messaging block. + /// </summary> + /**/ + ~call() + { + remove_sources(); + } + +protected: + + // + // target_block protected function implementations + // + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>call</c> messaging block. It is invoked + /// by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by Consume and the LWT. Doing so could + // result in a deadlock with the Consume call. + + message_status _Result = accepted; + + // + // Accept the message being propagated + // Note: depending on the source block propagating the message + // this may not necessarily be the same message (pMessage) first + // passed into the function. + // + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_PMessage != NULL) + { + async_send(_PMessage); + } + else + { + _Result = missed; + } + + return _Result; + } + + /// <summary> + /// Synchronously passes a message from an <c>ISource</c> block to this <c>call</c> messaging block. It is invoked + /// by the <c>send</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status send_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + message_status _Result = accepted; + + // + // Accept the message being propagated + // Note: depending on the source block propagating the message + // this may not necessarily be the same message (pMessage) first + // passed into the function. + // + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_PMessage != NULL) + { + sync_send(_PMessage); + } + else + { + _Result = missed; + } + + return _Result; + } + + /// <summary> + /// Overrides the <c>supports_anonymous_source</c> method to indicate that this block can + /// accept messages offered to it by a source that is not linked. + /// </summary> + /// <returns> + /// <c>true</c> because the block does not postpone offered messages. + /// </returns> + /**/ + virtual bool supports_anonymous_source() + { + return true; + } + + /// <summary> + /// Processes a message that was accepted by this <c>call</c> messaging block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the message that is to be handled. + /// </param> + /**/ + virtual void process_message(_Inout_ message<_Type> * _PMessage) + { + // No longer necessary with CRT110 change + } + + /// <summary> + /// Executes the call function on the input messages. + /// </summary> + /**/ + virtual void process_input_messages(_Inout_ message<_Type> * _PMessage) + { + // Invoke the function provided by the user + _CONCRT_ASSERT(_PMessage != NULL); + _M_pFunc(_PMessage->payload); + delete _PMessage; + } + +private: + + // + // Private Data Members + // + + // The call method called by this block + _Call_method _M_pFunc; + +private: + // + // Hide assignment operator and copy constructor + // + call const &operator =(call const&); // no assignment operator + call(call const &); // no copy constructor +}; + +//************************************************************************** +// Transformer: +//************************************************************************** + +/// <summary> +/// A <c>transformer</c> messaging block is a single-target, multi-source, ordered +/// <c>propagator_block</c> which can accept messages of one type and is +/// capable of storing an unbounded number of messages of a different type. +/// </summary> +/// <typeparam name="_Input"> +/// The payload type of the messages accepted by the buffer. +/// </typeparam> +/// <typeparam name="_Output"> +/// The payload type of the messages stored and propagated out by the buffer. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/// <seealso cref="call Class"/> +/**/ +template<class _Input, class _Output> +class transformer : public propagator_block<single_link_registry<ITarget<_Output>>, multi_link_registry<ISource<_Input>>> +{ + typedef std::tr1::function<_Output(_Input const&)> _Transform_method; + +public: + /// <summary> + /// Constructs a <c>transformer</c> messaging block. + /// </summary> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_PTarget"> + /// A pointer to a target block to link with the transformer. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Transform_method"/> is a functor with signature <c>_Output (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + transformer(_Transform_method const& _Func, + _Inout_opt_ ITarget<_Output> * _PTarget = NULL) : + _M_pFunc(_Func) + { + initialize_source_and_target(); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + } + + /// <summary> + /// Constructs a <c>transformer</c> messaging block. + /// </summary> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_PTarget"> + /// A pointer to a target block to link with the transform. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Transform_method"/> is a functor with signature <c>_Output (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + transformer(_Transform_method const& _Func, + _Inout_opt_ ITarget<_Output> * _PTarget, + filter_method const& _Filter) : + _M_pFunc(_Func) + { + initialize_source_and_target(); + register_filter(_Filter); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs a <c>transformer</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>transformer</c> messaging block is scheduled. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_PTarget"> + /// A pointer to a target block to link with the transformer. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Transform_method"/> is a functor with signature <c>_Output (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + transformer(Scheduler& _PScheduler, + _Transform_method const& _Func, + _Inout_opt_ ITarget<_Output> * _PTarget = NULL) : + _M_pFunc(_Func) + { + initialize_source_and_target(&_PScheduler); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + } + + /// <summary> + /// Constructs a <c>transformer</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>transformer</c> messaging block is scheduled. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_PTarget"> + /// A pointer to a target block to link with the transformer. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Transform_method"/> is a functor with signature <c>_Output (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + transformer(Scheduler& _PScheduler, + _Transform_method const& _Func, + _Inout_opt_ ITarget<_Output> * _PTarget, + filter_method const& _Filter) : + _M_pFunc(_Func) + { + initialize_source_and_target(&_PScheduler); + register_filter(_Filter); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + } + + /// <summary> + /// Constructs a <c>transformer</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>transformer</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_PTarget"> + /// A pointer to a target block to link with the transformer. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Transform_method"/> is a functor with signature <c>_Output (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + transformer(ScheduleGroup& _PScheduleGroup, + _Transform_method const& _Func, + _Inout_opt_ ITarget<_Output> * _PTarget = NULL) : + _M_pFunc(_Func) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + } + + /// <summary> + /// Constructs a <c>transformer</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>transformer</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Func"> + /// A function that will be invoked for each accepted message. + /// </param> + /// <param name="_PTarget"> + /// A pointer to a target block to link with the transformer. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="_Transform_method"/> is a functor with signature <c>_Output (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to process a message.</para> + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Input const &)</c> + /// which is invoked by this <c>transformer</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + transformer(ScheduleGroup& _PScheduleGroup, + _Transform_method const& _Func, + _Inout_opt_ ITarget<_Output> * _PTarget, + filter_method const& _Filter) : + _M_pFunc(_Func) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + register_filter(_Filter); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys the <c>transformer</c> messaging block. + /// </summary> + /**/ + ~transformer() + { + // Remove all links + remove_network_links(); + + // Clean up any messages left in this message block + _Delete_stored_messages(); + } + +protected: + + // Propagator_block protected function implementations + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>transformer</c> messaging block. + /// It is invoked by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(_Inout_ message<_Input> * _PMessage, _Inout_ ISource<_Input> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by Consume and the LWT. Doing so could + // result in a deadlock with the Consume call. + + message_status _Result = accepted; + + // + // Accept the message being propagated + // Note: depending on the source block propagating the message + // this may not necessarily be the same message (pMessage) first + // passed into the function. + // + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_PMessage != NULL) + { + // Enqueue the input message + _M_inputMessages.push(_PMessage); + async_send(NULL); + } + else + { + _Result = missed; + } + + return _Result; + } + + /// <summary> + /// Synchronously passes a message from an <c>ISource</c> block to this <c>transformer</c> messaging block. + /// It is invoked by the <c>send</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status send_message(_Inout_ message<_Input> * _PMessage, _Inout_ ISource<_Input> * _PSource) + { + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_PMessage != NULL) + { + // Enqueue the input message + _M_inputMessages.push(_PMessage); + sync_send(NULL); + } + else + { + return missed; + } + + return accepted; + } + + /// <summary> + /// Overrides the <c>supports_anonymous_source</c> method to indicate that this block can + /// accept messages offered to it by a source that is not linked. + /// </summary> + /// <returns> + /// <c>true</c> because the block does not postpone offered messages. + /// </returns> + /**/ + virtual bool supports_anonymous_source() + { + return true; + } + + /// <summary> + /// Accepts a message that was offered by this <c>transformer</c> messaging block, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /**/ + virtual message<_Output> * accept_message(runtime_object_identity _MsgId) + { + // + // Peek at the head message in the message buffer. If the IDs match + // dequeue and transfer ownership + // + message<_Output> * _Msg = NULL; + + if (_M_messageBuffer._Is_head(_MsgId)) + { + _Msg = _M_messageBuffer._Dequeue(); + } + + return _Msg; + } + + /// <summary> + /// Reserves a message previously offered by this <c>transformer</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) + { + // Allow reservation if this is the head message + return _M_messageBuffer._Is_head(_MsgId); + } + + /// <summary> + /// Consumes a message previously offered by the <c>transformer</c> and reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being consumed. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_Output> * consume_message(runtime_object_identity _MsgId) + { + // By default, accept the message + return accept_message(_MsgId); + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) + { + // The head message is the one reserved. + if (!_M_messageBuffer._Is_head(_MsgId)) + { + throw message_not_found(); + } + } + + /// <summary> + /// Resumes propagation after a reservation has been released. + /// </summary> + /**/ + virtual void resume_propagation() + { + // If there are any messages in the buffer, propagate them out + if (_M_messageBuffer._Count() > 0) + { + // async send a NULL value to initiate the repropagation + async_send(NULL); + } + } + + /// <summary> + /// A callback that notifies that a new target has been linked to this <c>transformer</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<_Output> *) + { + // If the message queue is blocked due to reservation + // there is no need to do any message propagation + if (_M_pReservedFor != NULL) + { + return; + } + + _Propagate_priority_order(_M_messageBuffer); + } + + /// <summary> + /// Executes the transformer function on the input messages. + /// </summary> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<_Output> *) + { + message<_Output> * _Msg = NULL; + + // Process input message. + message<_Input> * _PInputMessage = NULL; + _M_inputMessages.try_pop(_PInputMessage); + + if (_PInputMessage != NULL) + { + // Invoke the TransformMethod on the data + // Let exceptions flow + _Output _Out = _M_pFunc(_PInputMessage->payload); + + // Reuse the input message ID + _Msg = new message<_Output>(_Out, _PInputMessage->msg_id()); + _M_messageBuffer._Enqueue(_Msg); + + // Message cleanup + delete _PInputMessage; + + if (!_M_messageBuffer._Is_head(_Msg->msg_id())) + { + return; + } + } + + _Propagate_priority_order(_M_messageBuffer); + } + +private: + + /// <summary> + /// Propagates messages in priority order. + /// </summary> + /// <param name="_MessageBuffer"> + /// Reference to a message queue with messages to be propagated + /// </param> + /**/ + void _Propagate_priority_order(::Concurrency::details::_Queue<message<_Target_type>> & _MessageBuffer) + { + message<_Target_type> * _Msg = _MessageBuffer._Peek(); + + // If someone has reserved the _Head message, don't propagate anymore + if (_M_pReservedFor != NULL) + { + return; + } + + while (_Msg != NULL) + { + message_status _Status = declined; + + // Always start from the first target that linked + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<_Target_type> * _PTarget = *_Iter; + _Status = _PTarget->propagate(_Msg, this); + + // Ownership of message changed. Do not propagate this + // message to any other target. + if (_Status == accepted) + { + break; + } + + // If the target just propagated to reserved this message, stop + // propagating it to others + if (_M_pReservedFor != NULL) + { + break; + } + } + + // If status is anything other than accepted, then the head message + // was not propagated out. Thus, nothing after it in the queue can + // be propagated out. Cease propagation. + if (_Status != accepted) + { + break; + } + + // Get the next message + _Msg = _MessageBuffer._Peek(); + } + } + + /// <summary> + /// Deletes all messages currently stored in this message block. Should be called + /// by the destructor to ensure any messages propagated in are cleaned up. + /// </summary> + /**/ + void _Delete_stored_messages() + { + // Delete input messages + // Because the transformer uses its own input queue, it's possible there are messages + // in this queue and no LWT will be executed to handle them. + message<_Input> * _PInputQueueMessage = NULL; + + while (_M_inputMessages.try_pop(_PInputQueueMessage)) + { + // Message cleanup + delete _PInputQueueMessage; + } + + // Delete any messages remaining in the output queue + for (;;) + { + message<_Output> * _Msg = _M_messageBuffer._Dequeue(); + if (_Msg == NULL) + { + break; + } + delete _Msg; + } + } + + // + // Private Data Members + // + + // The transformer method called by this block + _Transform_method _M_pFunc; + + // The queue of input messages for this Transformer block + concurrent_queue<message<_Input> *> _M_inputMessages; + + /// <summary> + /// Message queue used to store outbound messages + /// </summary> + /**/ + ::Concurrency::details::_Queue<message<_Output>> _M_messageBuffer; + +private: + // + // Hide assignment operator and copy constructor + // + transformer const &operator =(transformer const &); // no assignment operator + transformer(transformer const &); // no copy constructor +}; + +//************************************************************************** +// Timer: +//************************************************************************** +/// <summary> +/// A <c>timer</c> messaging block is a single-target <c>source_block</c> capable of sending +/// a message to its target after a specified time period has elapsed +/// or at specific intervals. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type of the output messages of this block. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/**/ +template<class _Type> +class timer : public Concurrency::details::_Timer, public source_block<single_link_registry<ITarget<_Type>>> +{ +private: + + /// <summary> + /// Tracks the state machine of the timer. + /// </summary> + /**/ + enum State + { + /// <summary> + /// The timer has been initialized, but not yet started. + /// </summary> + /**/ + Initialized, + /// <summary> + /// The timer has been started. + /// </summary> + /**/ + Started, + /// <summary> + /// The timer has started and been paused. + /// </summary> + /**/ + Paused, + /// <summary> + /// The timer has been stopped. + /// </summary> + /**/ + Stopped + }; + +public: + + /// <summary> + /// Constructs a <c>timer</c> messaging block that will fire a given message after a specified interval. + /// </summary> + /// <param name="_Ms"> + /// The number of milliseconds that must elapse after the call to start for the specified message + /// to be propagated downstream. + /// </param> + /// <param name="_Value"> + /// The value which will be propagated downstream when the timer elapses. + /// </param> + /// <param name="_PTarget"> + /// The target to which the timer will propagate its message. + /// </param> + /// <param name="_Repeating"> + /// If true, indicates that the timer will fire periodically every <paramref name="_Ms"/> milliseconds. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_Scheduler"/> + /// or <paramref name="_ScheduleGroup"/> parameters. + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + timer(unsigned int _Ms, _Type const& _Value, ITarget<_Type> *_PTarget = NULL, bool _Repeating = false) : + _Timer(_Ms, _Repeating) + { + _Initialize(_Value, _PTarget, _Repeating); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs a <c>timer</c> messaging block that will fire a given message after a specified interval. + /// </summary> + /// <param name="_Scheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>timer</c> messaging block is scheduled is scheduled. + /// </param> + /// <param name="_Ms"> + /// The number of milliseconds that must elapse after the call to start for the specified message + /// to be propagated downstream. + /// </param> + /// <param name="_Value"> + /// The value which will be propagated downstream when the timer elapses. + /// </param> + /// <param name="_PTarget"> + /// The target to which the timer will propagate its message. + /// </param> + /// <param name="_Repeating"> + /// If true, indicates that the timer will fire periodically every <paramref name="_Ms"/> milliseconds. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_Scheduler"/> + /// or <paramref name="_ScheduleGroup"/> parameters. + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + timer(Scheduler& _Scheduler, unsigned int _Ms, _Type const& _Value, _Inout_opt_ ITarget<_Type> *_PTarget = NULL, bool _Repeating = false) : + _Timer(_Ms, _Repeating) + { + _Initialize(_Value, _PTarget, _Repeating, &_Scheduler); + } + + /// <summary> + /// Constructs a <c>timer</c> messaging block that will fire a given message after a specified interval. + /// </summary> + /// <param name="_ScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>timer</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Ms"> + /// The number of milliseconds that must elapse after the call to start for the specified message + /// to be propagated downstream. + /// </param> + /// <param name="_Value"> + /// The value which will be propagated downstream when the timer elapses. + /// </param> + /// <param name="_PTarget"> + /// The target to which the timer will propagate its message. + /// </param> + /// <param name="_Repeating"> + /// If true, indicates that the timer will fire periodically every <paramref name="_Ms"/> milliseconds. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_Scheduler"/> + /// or <paramref name="_ScheduleGroup"/> parameters. + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + timer(ScheduleGroup& _ScheduleGroup, unsigned int _Ms, _Type const& _Value, _Inout_opt_ ITarget<_Type> *_PTarget = NULL, bool _Repeating = false) : + _Timer(_Ms, _Repeating) + { + _Initialize(_Value, _PTarget, _Repeating, NULL, &_ScheduleGroup); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys a <c>timer</c> messaging block. + /// </summary> + /**/ + ~timer() + { + // + // Make sure there are no more outstanding timer fires. Note that this does not mean that the LWT that was queued is finished, it only + // means that no more timers will fire after the return from _Stop. We still *MUST* wait on any outstanding LWTs. + // + if (_M_state == Started) + _Stop(); + + // Remove all the targets. This will wait for any outstanding LWTs + remove_targets(); + + // + // No more asynchronous operations can happen as of this point. + // + + // Clean up any messages left in this message block + _Delete_stored_messages(); + + if (_M_fReferencedScheduler) + { + ::Concurrency::details::_Scheduler(_M_pScheduler)._Release(); + } + } + + /// <summary> + /// Starts the <c>timer</c> messaging block. The specified number of milliseconds after this is called, the specified value will be propagated + /// downstream as a <c>message</c>. + /// </summary> + /**/ + void start() + { + if (_M_state == Initialized || _M_state == Paused) + { + _M_state = Started; + _Start(); + } + } + + /// <summary> + /// Stops the <c>timer</c> messaging block. + /// </summary> + /**/ + void stop() + { + if (_M_state == Started) + _Stop(); + + _M_state = Stopped; + } + + /// <summary> + /// Stops the <c>timer</c> messaging block. If it is a repeating <c>timer</c> messaging block, it can be restarted with a subsequent + /// <c>start()</c> call. For non-repeating timers, this has the same effect as a <c>stop</c> call. + /// </summary> + /**/ + void pause() + { + // + // Non repeating timers cannot pause. They go to a final stopped state on pause. + // + if (!_M_fRepeating) + { + stop(); + } + else + { + // Pause only a started timer. + + if (_M_state == Started) + { + _Stop(); + _M_state = Paused; + } + } + } + +protected: + + /// <summary> + /// Accepts a message that was offered by this <c>timer</c> messaging block, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /**/ + virtual message<_Type> * accept_message(runtime_object_identity _MsgId) + { + if (_M_pMessage == NULL || _MsgId != _M_pMessage->msg_id()) + { + return NULL; + } + + message<_Type> *_PMessage = _M_pMessage; + _M_pMessage = NULL; + + return _PMessage; + } + + /// <summary> + /// Reserves a message previously offered by this <c>timer</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) + { + // + // Semantically, every timer tick is the same value -- it doesn't matter the message ID. Because we can only + // have one target as well, we do not need to track anything here. + // + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + return false; + } + + return true; + } + + /// <summary> + /// Consumes a message previously offered by the <c>timer</c> and reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being consumed. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_Type> * consume_message(runtime_object_identity _MsgId) + { + return accept_message(_MsgId); + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) + { + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + throw message_not_found(); + } + + delete _M_pMessage; + _M_pMessage = NULL; + } + + /// <summary> + /// Resumes propagation after a reservation has been released. + /// </summary> + /**/ + virtual void resume_propagation() + { + // Because reservation doesn't prevent propagation there is + // no need to resume on consume/release. + } + + /// <summary> + /// A callback that notifies that a new target has been linked to this <c>timer</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<_Type> * _PTarget) + { + // If there is a timer message sitting around, it must be propagated to the target now. + + if (_M_pMessage != NULL) + { + _PTarget->propagate(_M_pMessage, this); + } + } + + /// <summary> + /// Tries to offer the message produced by the <c>timer</c> block to all of the linked targets. + /// </summary> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<_Type> *) + { + if (_M_pMessage == NULL) + { + _M_pMessage = _NewMessage(); + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<_Type> * _PTarget = *_Iter; + _PTarget->propagate(_M_pMessage, this); + } + } + } + +private: + + // The timer message we contain + message<_Type> *_M_pMessage; + + // Current state of the timer. + State _M_state; + + // The value to send on elapse of the timer. + _Type _M_value; + + // An indication of whether the timer is repeating. + bool _M_fRepeating; + + // A flag for whether we need to release a reference on the scheduler. + bool _M_fReferencedScheduler; + + // Scheduler used for the timer + Scheduler * _M_pScheduler; + + /// <summary> + /// Allocates a new message. + /// </summary> + /**/ + message<_Type>* _NewMessage() const + { + return new message<_Type>(_M_value); + } + + /// <summary> + /// Called when the timer fires. + /// </summary> + /**/ + virtual void _Fire() + { + async_send(NULL); + } + + /// <summary> + /// Common initialization. + /// </summary> + /// <param name="_Value"> + /// The value which will be propagated downstream when the timer elapses. + /// </param> + /// <param name="_PTarget"> + /// The target to which the timer will propagate its message. + /// </param> + /// <param name="_Repeating"> + /// If true, indicates that the timer will fire periodically every _Ms milliseconds. + /// </param> + /**/ + void _Initialize(const _Type& _Value, _Inout_ ITarget<_Type> *_PTarget, bool _Repeating, _Inout_opt_ Scheduler * _PScheduler = NULL, _Inout_opt_ ScheduleGroup * _PScheduleGroup = NULL) + { + _M_pMessage = NULL; + _M_value = _Value; + _M_fRepeating = _Repeating; + _M_state = Initialized; + _M_fReferencedScheduler = false; + + // + // If we are going to utilize the current scheduler for timer firing, we need to capture it now. Otherwise, + // the timer threads fired from Windows (what _Fire executes within) will wind up with a default scheduler + // attached -- probably not the semantic we want. + // + if (_PScheduleGroup == NULL && _PScheduler == NULL) + { + ::Concurrency::details::_Scheduler _sched = ::Concurrency::details::_CurrentScheduler::_Get(); + _PScheduler = _sched._GetScheduler(); + _sched._Reference(); + _M_fReferencedScheduler = true; + } + + _M_pScheduler = _PScheduler; + initialize_source(_PScheduler, _PScheduleGroup); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + } + + /// <summary> + /// Deletes all messages currently stored in this message block. Should be called + /// by the destructor to ensure any messages propagated in are cleaned up. + /// </summary> + /**/ + void _Delete_stored_messages() + { + // Input messages for this message block are in the base-class input buffer + // All messages in that buffer are guaranteed to have moved to the output + // buffer because the destructor first waits for all async sends to finish + // before reaching this point + + // Delete the message remaining in the output queue + if (_M_pMessage != NULL) + { + delete _M_pMessage; + } + } + +private: + // + // Hide assignment operator and copy constructor + // + timer const &operator =(timer const &); // no assignment operator + timer(timer const &); // no copy constructor +}; + +//************************************************************************** +// Single assignment: +//************************************************************************** + +/// <summary> +/// A <c>single_assignment</c> messaging block is a multi-target, multi-source, ordered +/// <c>propagator_block</c> capable of storing a single, write-once +/// <c>message</c>. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type of the message stored and propagated by the buffer. +/// </typeparam> +/// <remarks> +/// A <c>single_assignment</c> messaging block propagates out copies of its message to each target. +/// <para>For more information, see <see cref="Asynchronous Message Blocks"/>.</para> +/// </remarks> +/// <seealso cref="overwrite_buffer Class"/> +/// <seealso cref="unbounded_buffer Class"/> +/**/ +template<class _Type> +class single_assignment : public propagator_block<multi_link_registry<ITarget<_Type>>, multi_link_registry<ISource<_Type>>> +{ +public: + + /// <summary> + /// Constructs a <c>single_assignment</c> messaging block. + /// </summary> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>single_assignment</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + single_assignment() : + _M_fIsInitialized(false), _M_pMessage(NULL) + { + initialize_source_and_target(); + } + + /// <summary> + /// Constructs a <c>single_assignment</c> messaging block. + /// </summary> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>single_assignment</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + single_assignment(filter_method const& _Filter) : + _M_fIsInitialized(false), _M_pMessage(NULL) + { + initialize_source_and_target(); + register_filter(_Filter); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs a <c>single_assignment</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>single_assignment</c> messaging block is scheduled. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>single_assignment</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + single_assignment(Scheduler& _PScheduler) : + _M_fIsInitialized(false), _M_pMessage(NULL) + { + initialize_source_and_target(&_PScheduler); + } + + /// <summary> + /// Constructs a <c>single_assignment</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>single_assignment</c> messaging block is scheduled. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>single_assignment</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + single_assignment(Scheduler& _PScheduler, filter_method const& _Filter) : + _M_fIsInitialized(false), _M_pMessage(NULL) + { + initialize_source_and_target(&_PScheduler); + register_filter(_Filter); + } + + /// <summary> + /// Constructs a <c>single_assignment</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>single_assignment</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>single_assignment</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + single_assignment(ScheduleGroup& _PScheduleGroup) : + _M_fIsInitialized(false), _M_pMessage(NULL) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + } + + /// <summary> + /// Constructs a <c>single_assignment</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>single_assignment</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>single_assignment</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + single_assignment(ScheduleGroup& _PScheduleGroup, filter_method const& _Filter) : + _M_fIsInitialized(false), _M_pMessage(NULL) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + register_filter(_Filter); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys the <c>single_assignment</c> messaging block. + /// </summary> + /**/ + ~single_assignment() + { + // Remove all links + remove_network_links(); + + // Clean up any messages left in this message block + _Delete_stored_messages(); + } + + /// <summary> + /// Checks whether this <c>single_assignment</c> messaging block has been initialized with a value yet. + /// </summary> + /// <returns> + /// <c>true</c> if the block has received a value, <c>false</c> otherwise. + /// </returns> + /**/ + bool has_value() const + { + return (_M_pMessage != NULL); + } + + + /// <summary> + /// Gets a reference to the current payload of the message being stored in the <c>single_assignment</c> messaging block. + /// </summary> + /// <returns> + /// The payload of the stored message. + /// </returns> + /// <remarks> + /// This method will wait until a message arrives if no message is currently stored in the <c>single_assignment</c> messaging block. + /// </remarks> + /**/ + _Type const & value() + { + if (_M_pMessage == NULL) + { + receive<_Type>(this); + } + _CONCRT_ASSERT(_M_pMessage != NULL); + + return _M_pMessage->payload; + } + + +protected: + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>single_assignment</c> messaging block. + /// It is invoked by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by Consume and the LWT. Doing so could + // result in a deadlock with the Consume call. + + message_status _Result = accepted; + + // single_assignment messaging block can be initialized only once + if (_M_fIsInitialized) + { + return declined; + } + + { + _NR_lock _Lock(_M_propagationLock); + + if (_M_fIsInitialized) + { + _Result = declined; + } + else + { + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + // Set initialized flag only if we have a message + if (_PMessage != NULL) + { + _M_fIsInitialized = true; + } + else + { + _Result = missed; + } + } + } + + // + // If message was accepted, set the member variables for + // this block and start the asynchronous propagation task + // + if (_Result == accepted) + { + async_send(_PMessage); + } + + return _Result; + } + + /// <summary> + /// Synchronously passes a message from an <c>ISource</c> block to this <c>single_assignment</c> messaging block. + /// It is invoked by the <c>send</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status send_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + message_status _Result = accepted; + + // single_assignment messaging block can be initialized only once + if (_M_fIsInitialized) + { + return declined; + } + + { + _NR_lock _Lock(_M_propagationLock); + + if (_M_fIsInitialized) + { + _Result = declined; + } + else + { + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + // Set initialized flag only if we have a message + if (_PMessage != NULL) + { + _M_fIsInitialized = true; + } + else + { + _Result = missed; + } + } + } + + // + // If message was accepted, set the member variables for + // this block and start the asynchronous propagation task + // + if (_Result == accepted) + { + sync_send(_PMessage); + } + + return _Result; + } + + /// <summary> + /// Accepts a message that was offered by this <c>single_assignment</c> messaging block, + /// returning a copy of the message to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// The <c>single_assignment</c> messaging block returns copies of the message + /// to its targets, rather than transferring ownership of the currently + /// held message. + /// </remarks> + /**/ + virtual message<_Type> * accept_message(runtime_object_identity _MsgId) + { + // This check is to prevent spoofing and verify that the propagated message is + // the one that is accepted at the end. + if (_M_pMessage == NULL || _MsgId != _M_pMessage->msg_id()) + { + return NULL; + } + + // + // Instead of returning the internal message, we return a copy of the + // message passed in. + // + // Because we are returning a copy, the accept routine for a single_assignment + // does not need to grab the internal lock. + // + return (new message<_Type>(_M_pMessage->payload)); + } + + /// <summary> + /// Reserves a message previously offered by this <c>single_assignment</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) + { + if (_M_pMessage == NULL) + { + return false; + } + + if (_M_pMessage->msg_id() != _MsgId) + { + throw message_not_found(); + } + + return true; + } + + /// <summary> + /// Consumes a message previously offered by the <c>single_assignment</c> and reserved by the target, + /// returning a copy of the message to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being consumed. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_Type> * consume_message(runtime_object_identity _MsgId) + { + _CONCRT_ASSERT(_M_fIsInitialized); + + return accept_message(_MsgId); + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) + { + _CONCRT_ASSERT(_M_fIsInitialized); + + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + throw message_not_found(); + } + } + + /// <summary> + /// Resumes propagation after a reservation has been released. + /// </summary> + /**/ + virtual void resume_propagation() + { + // Because reservation doesn't stop propagation, we don't + // need to do anything on resume after consume/release. + } + + /// <summary> + /// A callback that notifies that a new target has been linked to this <c>single_assignment</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<_Type> * _PTarget) + { + // If there is a message available already, propagate it. + + if (_M_pMessage != NULL) + { + _PTarget->propagate(_M_pMessage, this); + } + } + /// <summary> + /// Places the <c>message</c> <paramref name="_PMessage"/> in this <c>single_assignment</c> messaging block and + /// offers it to all of the linked targets. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to a <c>message</c> that this <c>single_assignment</c> messaging block has taken ownership of. + /// </param> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<_Type> * _PMessage) + { + // Initialized flag should have been set by the propagate function using interlocked operation. + _CONCRT_ASSERT(_M_fIsInitialized); + + // Move the message to the internal storage + + _CONCRT_ASSERT(_M_pMessage == NULL); + _M_pMessage = _PMessage; + + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + // Single assignment can propagate its message out + // to any number of Targets + + ITarget<_Type> * _PTarget = *_Iter; + _PTarget->propagate(_PMessage, this); + } + } + +private: + + /// <summary> + /// Deletes all messages currently stored in this message block. Should be called + /// by the destructor to ensure any messages propagated in are cleaned up. + /// </summary> + /**/ + void _Delete_stored_messages() + { + // Input messages for this message block are in the base-class input buffer + // All messages in that buffer are guaranteed to have moved to the output + // buffer because the destructor first waits for all async sends to finish + // before reaching this point + + // The messages for a single_assignment are deleted at the end when + // single_assignment is deleted. + delete _M_pMessage; + } + + // + // Private Data Members + // + + // The message being stored + message<_Type> * _M_pMessage; + + // The lock used to protect propagation + ::Concurrency::details::_NonReentrantPPLLock _M_propagationLock; + + // The marker for whether the single_assignment has already been initialized + volatile bool _M_fIsInitialized; + +private: + // + // Hide assignment operator and copy constructor + // + single_assignment const & operator=(single_assignment const &); // no assignment operator + single_assignment(single_assignment const &); // no copy constructor +}; + +//************************************************************************** +// Join (single-type) +//************************************************************************** + +/// <summary> +/// The type of a <c>join</c> messaging block. +/// </summary> +/**/ +enum join_type { + /// <summary> + /// Greedy <c>join</c> messaging blocks immediately accept a message upon propagation. This is more efficient, + /// but has the possibility for live-lock, depending on the network configuration. + /// </summary> + /**/ + greedy = 0, + /// <summary> + /// Non-greedy <c>join</c> messaging blocks postpone messages and try and consume them after all have arrived. + /// These are guaranteed to work, but slower. + /// </summary> + /**/ + non_greedy = 1 +}; + +/// <summary> +/// A <c>join</c> messaging block is a single-target, multi-source, ordered +/// <c>propagator_block</c> which combines together messages of type <typeparamref name="_Type"/> from each +/// of its sources. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type of the messages joined and propagated by the block. +/// </typeparam> +/// <typeparam name="_Jtype"> +/// The kind of <c>join</c> block this is, either <c>greedy</c> or <c>non_greedy</c> +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/// <seealso cref="choice Class"/> +/// <seealso cref="multitype_join Class"/> +/// <seealso cref="join_type Enumeration"/> +/**/ +template<class _Type, join_type _Jtype = non_greedy> +class join : public propagator_block<single_link_registry<ITarget<std::vector<_Type>>>, multi_link_registry<ISource<_Type>>> +{ +public: + typedef typename std::vector<_Type> _OutputType; + + /// <summary> + /// Constructs a <c>join</c> messaging block. + /// </summary> + /// <param name="_NumInputs"> + /// The number of inputs this <c>join</c> block will be allowed. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>join</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + join(size_t _NumInputs) + : _M_messageArray(_NumInputs), + _M_savedMessageIdArray(_NumInputs) + { + _Initialize(_NumInputs); + } + + /// <summary> + /// Constructs a <c>join</c> messaging block. + /// </summary> + /// <param name="_NumInputs"> + /// The number of inputs this <c>join</c> block will be allowed. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>join</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + join(size_t _NumInputs, filter_method const& _Filter) + : _M_messageArray(_NumInputs), + _M_savedMessageIdArray(_NumInputs) + { + _Initialize(_NumInputs); + register_filter(_Filter); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs a <c>join</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>join</c> messaging block is scheduled. + /// </param> + /// <param name="_NumInputs"> + /// The number of inputs this <c>join</c> block will be allowed. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>join</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + join(Scheduler& _PScheduler, size_t _NumInputs) + : _M_messageArray(_NumInputs), + _M_savedMessageIdArray(_NumInputs) + { + _Initialize(_NumInputs, &_PScheduler); + } + + /// <summary> + /// Constructs a <c>join</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>join</c> messaging block is scheduled. + /// </param> + /// <param name="_NumInputs"> + /// The number of inputs this <c>join</c> block will be allowed. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>join</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + join(Scheduler& _PScheduler, size_t _NumInputs, filter_method const& _Filter) + : _M_messageArray(_NumInputs), + _M_savedMessageIdArray(_NumInputs) + { + _Initialize(_NumInputs, &_PScheduler); + register_filter(_Filter); + } + + /// <summary> + /// Constructs a <c>join</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>join</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_NumInputs"> + /// The number of inputs this <c>join</c> block will be allowed. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>join</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + join(ScheduleGroup& _PScheduleGroup, size_t _NumInputs) + : _M_messageArray(_NumInputs), + _M_savedMessageIdArray(_NumInputs) + { + _Initialize(_NumInputs, NULL, &_PScheduleGroup); + } + + /// <summary> + /// Constructs a <c>join</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>join</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_NumInputs"> + /// The number of inputs this <c>join</c> block will be allowed. + /// </param> + /// <param name="_Filter"> + /// A filter function which determines whether offered messages should be accepted. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// <para>The type <typeparamref name="filter_method"/> is a functor with signature <c>bool (_Type const &)</c> + /// which is invoked by this <c>join</c> messaging block to determine whether or not it should accept + /// an offered message.</para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + join(ScheduleGroup& _PScheduleGroup, size_t _NumInputs, filter_method const& _Filter) + : _M_messageArray(_NumInputs), + _M_savedMessageIdArray(_NumInputs) + { + _Initialize(_NumInputs, NULL, &_PScheduleGroup); + register_filter(_Filter); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys the <c>join</c> block. + /// </summary> + /**/ + ~join() + { + // Remove all links that are targets of this join + remove_network_links(); + + // Clean up any messages left in this message block + _Delete_stored_messages(); + + delete [] _M_savedIdBuffer; + } + +protected: + // + // propagator_block protected function implementations + // + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>join</c> messaging block. + /// It is invoked by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + message_status propagate_message(_Inout_ message<_Type> * _PMessage, _Inout_ ISource<_Type> * _PSource) + { + // It is important that calls to propagate do *not* take the same lock on the + // internal structure that is used by Consume and the LWT. Doing so could + // result in a deadlock with the Consume call. + + message_status _Ret_val = accepted; + + // + // Find the slot index of this source + // + size_t _Slot = 0; + bool _Found = false; + for (source_iterator _Iter = _M_connectedSources.begin(); *_Iter != NULL; ++_Iter) + { + if (*_Iter == _PSource) + { + _Found = true; + break; + } + + _Slot++; + } + + if (!_Found) + { + // If this source was not found in the array, this is not a connected source + // decline the message + return declined; + } + + _CONCRT_ASSERT(_Slot < _M_messageArray._M_count); + + bool fIsGreedy = (_Jtype == greedy); + + if (fIsGreedy) + { + // + // Greedy type joins immediately accept the message. + // + { + _NR_lock lockHolder(_M_propagationLock); + if (_M_messageArray._M_messages[_Slot] != NULL) + { + _M_savedMessageIdArray._M_savedIds[_Slot] = _PMessage->msg_id(); + _Ret_val = postponed; + } + } + + if (_Ret_val != postponed) + { + _M_messageArray._M_messages[_Slot] = _PSource->accept(_PMessage->msg_id(), this); + + if (_M_messageArray._M_messages[_Slot] != NULL) + { + if (_InterlockedDecrementSizeT(&_M_messagesRemaining) == 0) + { + // If messages have arrived on all links, start a propagation + // of the current message + async_send(NULL); + } + } + else + { + _Ret_val = missed; + } + } + } + else + { + // + // Non-greedy type joins save the message IDs until they have all arrived + // + + if (_InterlockedExchange((volatile long *) &_M_savedMessageIdArray._M_savedIds[_Slot], _PMessage->msg_id()) == -1) + { + // Decrement the message remaining count if this thread is switching + // the saved ID from -1 to a valid value. + if (_InterlockedDecrementSizeT(&_M_messagesRemaining) == 0) + { + async_send(NULL); + } + } + + // Always return postponed. This message will be consumed + // in the LWT + _Ret_val = postponed; + } + + return _Ret_val; + } + + /// <summary> + /// Accepts a message that was offered by this <c>join</c> messaging block, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /**/ + virtual message<_OutputType> * accept_message(runtime_object_identity _MsgId) + { + // + // Peek at the head message in the message buffer. If the IDs match + // dequeue and transfer ownership + // + message<_OutputType> * _Msg = NULL; + + if (_M_messageBuffer._Is_head(_MsgId)) + { + _Msg = _M_messageBuffer._Dequeue(); + } + + return _Msg; + } + + /// <summary> + /// Reserves a message previously offered by this <c>join</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) + { + // Allow reservation if this is the head message + return _M_messageBuffer._Is_head(_MsgId); + } + + /// <summary> + /// Consumes a message previously offered by the <c>join</c> messaging block and reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being consumed. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_OutputType> * consume_message(runtime_object_identity _MsgId) + { + // By default, accept the message + return accept_message(_MsgId); + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) + { + // The head message is the one reserved. + if (!_M_messageBuffer._Is_head(_MsgId)) + { + throw message_not_found(); + } + } + + /// <summary> + /// Resumes propagation after a reservation has been released. + /// </summary> + /**/ + virtual void resume_propagation() + { + // If there are any messages in the buffer, propagate them out + if (_M_messageBuffer._Count() > 0) + { + async_send(NULL); + } + } + + /// <summary> + /// A callback that notifies that a new target has been linked to this <c>join</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<std::vector<_Type>> *) + { + // If the message queue is blocked due to reservation + // there is no need to do any message propagation + if (_M_pReservedFor != NULL) + { + return; + } + + _Propagate_priority_order(_M_messageBuffer); + } + + /// <summary> + /// Constructs an output message containing an input message from each source when + /// they have all propagated a message. Sends this output message out to each of + /// its targets. + /// </summary> + /**/ + void propagate_to_any_targets(_Inout_opt_ message<_OutputType> *) + { + message<_OutputType> * _Msg = NULL; + // Create a new message from the input sources + // If messagesRemaining == 0, we have a new message to create. Otherwise, this is coming from + // a consume or release from the target. In that case we don't want to create a new message. + if (_M_messagesRemaining == 0) + { + // A greedy join can immediately create the message, a non-greedy + // join must try and consume all the messages it has postponed + _Msg = _Create_new_message(); + } + + if (_Msg == NULL) + { + // Create message failed. This happens in non_greedy joins when the + // reserve/consumption of a postponed message failed. + _Propagate_priority_order(_M_messageBuffer); + return; + } + + bool fIsGreedy = (_Jtype == greedy); + + // For a greedy join, reset the number of messages remaining + // Check to see if multiple messages have been passed in on any of the links, + // and postponed. If so, try and reserve/consume them now + if (fIsGreedy) + { + // Look at the saved IDs and reserve/consume any that have passed in while + // this join was waiting to complete + _CONCRT_ASSERT(_M_messageArray._M_count == _M_savedMessageIdArray._M_count); + + for (size_t i = 0; i < _M_messageArray._M_count; i++) + { + for(;;) + { + runtime_object_identity _Saved_id; + // Grab the current saved ID value. This value could be changing from based on any + // calls of source->propagate(this). If the message ID is different than what is snapped + // here, that means, the reserve below must fail. This is because reserve is trying + // to get the same source lock the propagate(this) call must be holding. + { + _NR_lock lockHolder(_M_propagationLock); + + _CONCRT_ASSERT(_M_messageArray._M_messages[i] != NULL); + + _Saved_id = _M_savedMessageIdArray._M_savedIds[i]; + + if (_Saved_id == -1) + { + _M_messageArray._M_messages[i] = NULL; + break; + } + else + { + _M_savedMessageIdArray._M_savedIds[i] = -1; + } + } + + if (_Saved_id != -1) + { + source_iterator _Iter = _M_connectedSources.begin(); + + ISource<_Type> * _PSource = _Iter[i]; + if ((_PSource != NULL) && _PSource->reserve(_Saved_id, this)) + { + _M_messageArray._M_messages[i] = _PSource->consume(_Saved_id, this); + _InterlockedDecrementSizeT(&_M_messagesRemaining); + break; + } + } + } + } + + // If messages have all been received, async_send again, this will start the + // LWT up to create a new message + if (_M_messagesRemaining == 0) + { + async_send(NULL); + } + } + + // Add the new message to the outbound queue + _M_messageBuffer._Enqueue(_Msg); + + if (!_M_messageBuffer._Is_head(_Msg->msg_id())) + { + // another message is at the head of the outbound message queue and blocked + // simply return + return; + } + + _Propagate_priority_order(_M_messageBuffer); + } + +private: + + // + // Private Methods + // + + /// <summary> + /// Propagate messages in priority order. + /// </summary> + /// <param name="_MessageBuffer"> + /// Reference to a message queue with messages to be propagated + /// </param> + /**/ + void _Propagate_priority_order(::Concurrency::details::_Queue<message<_Target_type>> & _MessageBuffer) + { + message<_Target_type> * _Msg = _MessageBuffer._Peek(); + + // If someone has reserved the _Head message, don't propagate anymore + if (_M_pReservedFor != NULL) + { + return; + } + + while (_Msg != NULL) + { + message_status _Status = declined; + + // Always start from the first target that linked + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<_Target_type> * _PTarget = *_Iter; + _Status = _PTarget->propagate(_Msg, this); + + // Ownership of message changed. Do not propagate this + // message to any other target. + if (_Status == accepted) + { + break; + } + + // If the target just propagated to reserved this message, stop + // propagating it to others + if (_M_pReservedFor != NULL) + { + break; + } + } + + // If status is anything other than accepted, then the head message + // was not propagated out. Thus, nothing after it in the queue can + // be propagated out. Cease propagation. + if (_Status != accepted) + { + break; + } + + // Get the next message + _Msg = _MessageBuffer._Peek(); + } + } + + /// <summary> + /// Constructs a new message from the data output. + /// </summary> + /// <returns> + /// The created message (NULL if creation failed) + /// </returns> + /**/ + message<std::vector<_Type>> * __cdecl _Create_new_message() + { + bool fIsNonGreedy = (_Jtype == non_greedy); + + // If this is a non-greedy join, check each source and try to consume their message + if (fIsNonGreedy) + { + + // The iterator _Iter below will ensure that it is safe to touch + // non-NULL source pointers. Take a snapshot. + std::vector<ISource<_Type> *> _Sources; + source_iterator _Iter = _M_connectedSources.begin(); + + while (*_Iter != NULL) + { + ISource<_Type> * _PSource = *_Iter; + + if (_PSource == NULL) + { + break; + } + + _Sources.push_back(_PSource); + ++_Iter; + } + + if (_Sources.size() != _M_messageArray._M_count) + { + // Some of the sources were unlinked. The join is broken + return NULL; + } + + // First, try and reserve all the messages. If a reservation fails, + // then release any reservations that had been made. + for (size_t i = 0; i < _M_savedMessageIdArray._M_count; i++) + { + // Snap the current saved ID into a buffer. This value can be changing behind the scenes from + // other source->propagate(msg, this) calls, but if so, that just means the reserve below will + // fail. + _InterlockedIncrementSizeT(&_M_messagesRemaining); + _M_savedIdBuffer[i] = _InterlockedExchange((volatile long *) &_M_savedMessageIdArray._M_savedIds[i], -1); + + _CONCRT_ASSERT(_M_savedIdBuffer[i] != -1); + + if (!_Sources[i]->reserve(_M_savedIdBuffer[i], this)) + { + // A reservation failed, release all reservations made up until + // this block, and wait for another message to arrive on this link + for (size_t j = 0; j < i; j++) + { + _Sources[j]->release(_M_savedIdBuffer[j], this); + if (_InterlockedCompareExchange((volatile long *) &_M_savedMessageIdArray._M_savedIds[j], _M_savedIdBuffer[j], -1) == -1) + { + if (_InterlockedDecrementSizeT(&_M_messagesRemaining) == 0) + { + async_send(NULL); + } + } + } + + // Return NULL to indicate that the create failed + return NULL; + } + } + + // Because everything has been reserved, consume all the messages. + // This is guaranteed to return true. + for (size_t i = 0; i < _M_messageArray._M_count; i++) + { + _M_messageArray._M_messages[i] = _Sources[i]->consume(_M_savedIdBuffer[i], this); + _M_savedIdBuffer[i] = -1; + } + } + + if (!fIsNonGreedy) + { + // Reinitialize how many messages are being waited for. + // This is safe because all messages have been received, thus no new async_sends for + // greedy joins can be called. + _M_messagesRemaining = _M_messageArray._M_count; + } + + std::vector<_Type> _OutputVector; + for (size_t i = 0; i < _M_messageArray._M_count; i++) + { + _CONCRT_ASSERT(_M_messageArray._M_messages[i] != NULL); + _OutputVector.push_back(_M_messageArray._M_messages[i]->payload); + + delete _M_messageArray._M_messages[i]; + if (fIsNonGreedy) + { + _M_messageArray._M_messages[i] = NULL; + } + } + return (new message<std::vector<_Type>>(_OutputVector)); + } + + /// <summary> + /// Initializes the <c>join</c> messaging block. + /// </summary> + /// <param name="_NumInputs"> + /// The number of inputs. + /// </param> + /// <param name="_PScheduler"> + /// The scheduler onto which the task to propagate the <c>join</c> block's message will be scheduled. + /// If unspecified, the <c>join</c> messaging block uses the default scheduler. + /// </param> + /// <param name="_PScheduleGroup"> + /// The schedule group into which the task to propagate the <c>join</c> block's message will be scheduled. + /// The scheduler used is implied by the schedule group. If unspecified, the <c>join</c> uses a schedule + /// group of the scheduler's choosing. + /// </param> + /**/ + void _Initialize(size_t _NumInputs, Scheduler * _PScheduler = NULL, ScheduleGroup * _PScheduleGroup = NULL) + { + initialize_source_and_target(_PScheduler, _PScheduleGroup); + + _M_connectedSources.set_bound(_NumInputs); + _M_messagesRemaining = _NumInputs; + + bool fIsNonGreedy = (_Jtype == non_greedy); + + if (fIsNonGreedy) + { + // Non greedy joins need a buffer to snap off saved message IDs to. + _M_savedIdBuffer = new runtime_object_identity[_NumInputs]; + memset(_M_savedIdBuffer, -1, sizeof(runtime_object_identity) * _NumInputs); + } + else + { + _M_savedIdBuffer = NULL; + } + } + + /// <summary> + /// Deletes all messages currently stored in this message block. Should be called + /// by the destructor to ensure any messages propagated in are cleaned up. + /// </summary> + /**/ + void _Delete_stored_messages() + { + // Input messages for this message block are in the base-class input buffer + // All messages in that buffer are guaranteed to have moved to the output + // buffer because the destructor first waits for all async sends to finish + // before reaching this point + + // Delete any messages remaining in the output queue + for (;;) + { + message<std::vector<_Type>> * _Msg = _M_messageBuffer._Dequeue(); + if (_Msg == NULL) + { + break; + } + delete _Msg; + } + } + + // The current number of messages remaining + volatile size_t _M_messagesRemaining; + + // An array containing the accepted messages of this join. + // Wrapped in a struct to enable debugger visualization. + struct _MessageArray + { + size_t _M_count; + message<_Type>** _M_messages; + + _MessageArray(size_t _NumInputs) + : _M_count(_NumInputs), + _M_messages(new message<_Type>*[_NumInputs]) + { + memset(_M_messages, 0, sizeof(message<_Type> *) * _NumInputs); + } + + ~_MessageArray() + { + for (size_t i = 0; i < _M_count; i++) + delete _M_messages[i]; + delete [] _M_messages; + } + }; + _MessageArray _M_messageArray; + + // An array containing the msg IDs of messages propagated to the array + // For greedy joins, this contains a log of other messages passed to this + // join after the first has been accepted + // For non-greedy joins, this contains the message ID of any message + // passed to it. + // Wrapped in a struct to enable debugger visualization. + struct _SavedMessageIdArray + { + size_t _M_count; + runtime_object_identity * _M_savedIds; + + _SavedMessageIdArray(size_t _NumInputs) + : _M_count(_NumInputs), + _M_savedIds(new runtime_object_identity[_NumInputs]) + { + memset(_M_savedIds, -1, sizeof(runtime_object_identity) * _NumInputs); + } + + ~_SavedMessageIdArray() + { + delete [] _M_savedIds; + } + }; + _SavedMessageIdArray _M_savedMessageIdArray; + + // Buffer for snapping saved IDs in non-greedy joins + runtime_object_identity * _M_savedIdBuffer; + + // A lock for modifying the buffer or the connected blocks + ::Concurrency::details::_NonReentrantPPLLock _M_propagationLock; + + // Queue to hold output messages + ::Concurrency::details::_Queue<message<std::vector<_Type>>> _M_messageBuffer; +}; + + +//************************************************************************** +// Multi-Type Choice and Join helper node: +//************************************************************************** + +/// <summary> +/// Base class for Helper node used in multi-type join and choice blocks +/// Order node is a single-target, single-source ordered propagator block +/// The main property of an order node is that it accepts a message of _Type +/// and outputs a message of int, with some unique assigned index number. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/**/ +template<class _Type> +class _Order_node_base: public propagator_block<single_link_registry<ITarget<size_t>>, multi_link_registry<ISource<_Type>>> +{ +public: + + /// <summary> + /// Constructs a _Order_node_base within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /**/ + _Order_node_base() : + _M_index(0), + _M_pReceiveMessage(NULL), + _M_pSendMessage(NULL) + { + } + + /// <summary> + /// Cleans up any resources that may have been created by the _Order_node. + /// </summary> + /**/ + ~_Order_node_base() + { + // The messages for an _Order_node_base are deleted at the end when + // _Order_node_base is deleted. + delete _M_pReceiveMessage; + delete _M_pSendMessage; + } + + /// <summary> + /// Checks whether this block has been initialized yet. + /// </summary> + /// <returns> + /// true, if the block has received a value, false otherwise. + /// </returns> + /**/ + bool has_value() const + { + return (_M_pReceiveMessage != NULL); + } + + /// <summary> + /// Gets a reference to the current payload of the message being stored. + /// </summary> + /// <returns> + /// The incoming payload. + /// </returns> + /**/ + _Type const & value() + { + _CONCRT_ASSERT(_M_pReceiveMessage != NULL); + + return _M_pReceiveMessage->payload; + } + + /// <summary> + /// Resets the _Order_node_base and prepares it for the next propagation + /// </summary> + /// <remarks> + /// _Reset is called from Populate_destination_tuple through propagate_to_any_targets() + /// thus, it always has the internal lock held. This is only used for _Greedy_node and + /// _Non_greedy_node. + /// </remarks> + /**/ + virtual void _Reset() = 0; + + /// <summary> + /// Reserves a message previously offered by the source. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A bool indicating whether the reservation worked or not + /// </returns> + /// <remarks> + /// After 'reserve' is called, either 'consume' or 'release' must be called. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity) + { + // reserve should never be called for this block. + _CONCRT_ASSERT(false); + + return false; + } + + /// <summary> + /// Consumes a message previously offered by the source and reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /// <remarks> + /// Similar to 'accept', but is always preceded by a call to 'reserve' + /// </remarks> + /**/ + virtual message<size_t> * consume_message(runtime_object_identity) + { + // consume should never be called for this block. + _CONCRT_ASSERT(false); + + return NULL; + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /**/ + virtual void release_message(runtime_object_identity) + { + // release should never be called for this block. + _CONCRT_ASSERT(false); + } + +protected: + + + /// <summary> + /// Resumes propagation after a reservation has been released + /// </summary> + /**/ + virtual void resume_propagation() + { + // Because there is only a single target, nothing needs + // to be done on resume + } + + /// <summary> + /// Notification that a target was linked to this source. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<size_t> *) + { + if (_M_pSendMessage != NULL) + { + propagate_to_any_targets(NULL); + } + } + + /// <summary> + /// Create a message that contains an index used to determine the source message + /// </summary> + /**/ + void _Create_send_message() + { + _M_pSendMessage = new message<size_t>(_M_index); + } + + /// <summary> + /// Validate constructor arguments and fully connect this _Order_node_base. + /// </summary> + /**/ + void _Initialize_order_node(ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, Scheduler * _PScheduler = NULL, ScheduleGroup * _PScheduleGroup = NULL) + { + if (_Index < 0) + { + throw std::invalid_argument("_Index"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + _M_index = _Index; + + initialize_source_and_target(_PScheduler, _PScheduleGroup); + + // Allow only a single source and ensure that they + // cannot be unlinked and relinked. + _M_connectedSources.set_bound(1); + + if (_PTarget != NULL) + { + link_target(_PTarget); + } + + _PSource->link_target(this); + } + + // + // Private Data Members + // + + // The message to be received from the source + message<_Type> * _M_pReceiveMessage; + + // The message to be sent to all targets + message<size_t> * _M_pSendMessage; + + // The index of the _Order_node_base in the user's construct + size_t _M_index; + +private: + // + // Hide assignment operator and copy constructor + // + _Order_node_base const & operator=(_Order_node_base const &); // no assignment operator + _Order_node_base(_Order_node_base const &); // no copy constructor +}; + + +/// <summary> +/// Helper class used in multi-type choice blocks +/// Ordered node is a single-target, single-source ordered propagator block +/// </summary> +/// +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/**/ +template<class _Type> +class _Reserving_node: public _Order_node_base<_Type> +{ +public: + /// <summary> + /// Constructs a _Reserving_node within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Reserving_node(ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_fIsInitialized(false), + _M_savedId(-1), + _M_pReservedSource(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget); + } + + /// <summary> + /// Constructs a _Reserving_node within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Reserving_node(ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_fIsInitialized(false), + _M_savedId(-1), + _M_pReservedSource(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget); + } + + /// <summary> + /// Constructs a _Reserving_node within the specified scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PScheduler"> + /// A reference to a scheduler instance. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Reserving_node(Scheduler& _PScheduler, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_fIsInitialized(false), + _M_savedId(-1), + _M_pReservedSource(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget, &_PScheduler); + } + + /// <summary> + /// Constructs a _Reserving_node within the specified scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PScheduler"> + /// A reference to a scheduler instance. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Reserving_node(Scheduler& _PScheduler, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_fIsInitialized(false), + _M_savedId(-1), + _M_pReservedSource(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget, &_PScheduler); + } + + /// <summary> + /// Constructs a _Order_node within the specified schedule group. The scheduler is implied + /// by the schedule group. + /// </summary> + /// <param name="_PScheduleGroup"> + /// A reference to a schedule group. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Reserving_node(ScheduleGroup& _PScheduleGroup, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_fIsInitialized(false), + _M_savedId(-1), + _M_pReservedSource(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget, NULL, &_PScheduleGroup); + } + + /// <summary> + /// Constructs a _Order_node within the specified schedule group. The scheduler is implied + /// by the schedule group. + /// </summary> + /// <param name="_PScheduleGroup"> + /// A reference to a schedule group. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Reserving_node(ScheduleGroup& _PScheduleGroup, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_fIsInitialized(false), + _M_savedId(-1), + _M_pReservedSource(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget, NULL, &_PScheduleGroup); + } + + /// <summary> + /// Cleans up any resources that may have been created by the _Reserving_node. + /// </summary> + /**/ + ~_Reserving_node() + { + if (_M_pReservedSource != NULL) + { + _M_pReservedSource = NULL; + _M_connectedSources.release(); + } + + // Remove all links + remove_network_links(); + } + + + /// <summary> + /// Resets the _Reserving_node and prepares it for the next propagation + /// </summary> + /// <remarks> + /// This function is not used in a _Reserving_node, which is only used for choice blocks + /// </remarks> + /**/ + virtual void _Reset() + { + } + +protected: + + // + // propagator_block protected function implementation + // + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>ITarget</c> block. It is invoked + /// by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// It is important that calls to propagate do *not* take the same lock on the + /// internal structure that is used by Consume and the light-weight task. Doing so could + /// result in a deadlock with the Consume call. + /// </remarks> + /**/ + virtual message_status propagate_message(message<_Type> * _PMessage, ISource<_Type> * _PSource) + { + message_status _Result = postponed; + + // _Order_node messaging block can be initialized only once, just like single_assignment. + if (_M_fIsInitialized) + { + return declined; + } + + // Reserve a message on the source until this _Order_node gets the feedback from + // the single_assignment on whether it has been selected. + _M_fIsInitialized = _PSource->reserve(_PMessage->msg_id(), this); + + // + // If message was successfully reserved, set the member variables for + // this messaging block and start the asynchronous propagation task. + // + if (_M_fIsInitialized) + { + _M_savedId = _PMessage->msg_id(); + async_send(NULL); + } + else + { + _Result = missed; + } + + return _Result; + } + + /// <summary> + /// Accept the message by making a copy of the payload. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /**/ + virtual message<size_t> * accept_message(runtime_object_identity _MsgId) + { + // This check is to prevent spoofing and verify that the propagated message is + // the one that is accepted at the end. + if (_M_pSendMessage == NULL || _MsgId != _M_pSendMessage->msg_id()) + { + return NULL; + } + + // If the source has disconnected then we can't allow for accept to succeed. + source_iterator _Iter = _M_connectedSources.begin(); + ISource<_Type>* _PSource = *_Iter; + + if (_PSource == NULL) + { + // source was disconnected. Fail accept. + return NULL; + } + + _M_pReceiveMessage = _PSource->consume(_M_savedId, this); + + _CONCRT_ASSERT(_M_pReceiveMessage != NULL); + + // + // Instead of returning the internal message, we return a copy of the + // message passed in. + // + // Because we are returning a copy, the accept routine for a _Order_node + // does not need to grab the internal lock. + // + return (new message<size_t>(_M_pSendMessage->payload)); + } + + /// <summary> + /// Takes the message and propagates it to all the targets of this _Order_node + /// </summary> + /// <param name="_PMessage"> + /// A pointer to a new message. + /// </param> + /// <remarks> + /// This function packages its _M_index into a message and immediately sends it to the targets. + /// </remarks> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<size_t> *) + { + if (_M_pSendMessage == NULL) + { + _Create_send_message(); + } + + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<size_t> * _PTarget = *_Iter; + _Propagate_to_target(_PTarget); + } + } + +private: + + /// <summary> + /// Propagate messages to the given target + /// </summary> + /**/ + message_status _Propagate_to_target(ITarget<size_t> * _PTarget) + { + message_status _Status = _PTarget->propagate(_M_pSendMessage, this); + + // If the message got rejected we have to release the hold on the source message. + if (_Status != accepted) + { + if (_M_savedId != -1) + { + // Release the reservation + source_iterator _Iter = _M_connectedSources.begin(); + ISource<_Type> * _PSource = *_Iter; + + if (_PSource != NULL) + { + _PSource->release(_M_savedId, this); + } + + // If the source was disconnected, then it would + // automatically release any reservation. So we + // should reset our savedId regardless. + _M_savedId = -1; + } + + } + + return _Status; + } + + // + // Private Data Members + // + + // The source where we have reserved a message + ISource<_Type> * _M_pReservedSource; + + // For greedy order-nodes, the message ID of subsequent messages sent to this node + // For non-greedy order nodes, the message ID of the message to reserve/consume + runtime_object_identity _M_savedId; + + // The marker that indicates that _Reserving_node has reserved a message + volatile bool _M_fIsInitialized; + +private: + // + // Hide assignment operator and copy constructor + // + _Reserving_node const & operator=(_Reserving_node const &); // no assignment operator + _Reserving_node(_Reserving_node const &); // no copy constructor +}; + + +/// <summary> +/// Helper class used in multi-type greedy join blocks +/// Ordered node is a single-target, single-source ordered propagator block +/// </summary> +/// +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/**/ +template<class _Type> +class _Greedy_node: public _Order_node_base<_Type> +{ +public: + /// <summary> + /// Constructs a _Greedy_node within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Greedy_node(ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_savedId(-1), + _M_pGreedyMessage(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget); + } + + /// <summary> + /// Constructs a _Greedy_node within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Greedy_node(ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_savedId(-1), + _M_pGreedyMessage(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget); + } + + /// <summary> + /// Constructs a _Greedy_node within the specified scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PScheduler"> + /// A reference to a scheduler instance. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Greedy_node(Scheduler& _PScheduler, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_savedId(-1), + _M_pGreedyMessage(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget, &_PScheduler); + } + + /// <summary> + /// Constructs a _Greedy_node within the specified scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PScheduler"> + /// A reference to a scheduler instance. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Greedy_node(Scheduler& _PScheduler, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_savedId(-1), + _M_pGreedyMessage(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget, &_PScheduler); + } + + /// <summary> + /// Constructs a _Greedy_node within the specified schedule group. The scheduler is implied + /// by the schedule group. + /// </summary> + /// <param name="_PScheduleGroup"> + /// A reference to a schedule group. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Greedy_node(ScheduleGroup& _PScheduleGroup, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_savedId(-1), + _M_pGreedyMessage(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget, NULL, &_PScheduleGroup); + } + + /// <summary> + /// Constructs a _Greedy_node within the specified schedule group. The scheduler is implied + /// by the schedule group. + /// </summary> + /// <param name="_PScheduleGroup"> + /// A reference to a schedule group. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Greedy_node(ScheduleGroup& _PScheduleGroup, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_savedId(-1), + _M_pGreedyMessage(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget, NULL, &_PScheduleGroup); + } + + /// <summary> + /// Cleans up any resources that may have been created by the _Greedy_node. + /// </summary> + /**/ + ~_Greedy_node() + { + // Remove all links + remove_network_links(); + + if (_M_pGreedyMessage != _M_pReceiveMessage) + { + delete _M_pGreedyMessage; + } + } + + /// <summary> + /// Resets the _Greedy_node and prepares it for the next propagation + /// </summary> + /// <remarks> + /// _Reset is called from Populate_destination_tuple through propagate_to_any_targets() + /// thus, it always has the internal lock held. + /// </remarks> + /**/ + void _Reset() + { + _R_lock _Lock(_M_resetLock); + + delete _M_pReceiveMessage; + _M_pReceiveMessage = NULL; + + delete _M_pSendMessage; + _M_pSendMessage = NULL; + + // + // For greedy type joins, look to see if any other messages have been + // passed to this _Greedy_node while the join was waiting for other + // messages to arrive. This function is already called with _M_resetLock + // held through propagate_to_any_targets(). + // + for(;;) + { + // Set the current saved ID as -1. Check to see if something was ready for consumption + // (if _Saved_id != -1) and consume it if possible. + runtime_object_identity _Saved_id; + + { + _NR_lock lockHolder(_M_propagationLock); + + _Saved_id = _M_savedId; + + if (_Saved_id == -1) + { + _M_pGreedyMessage = NULL; + break; + } + else + { + _M_savedId = -1; + } + } + + if (_Saved_id != -1) + { + source_iterator _Iter = _M_connectedSources.begin(); + + ISource<_Type> * _PSource = *_Iter; + if ((_PSource != NULL) && _PSource->reserve(_Saved_id, this)) + { + _M_pGreedyMessage = _PSource->consume(_Saved_id, this); + async_send(NULL); + break; + } + } + } + } + +protected: + + // + // propagator_block protected function implementation + // + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>ITarget</c> block. It is invoked + /// by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// It is important that calls to propagate do *not* take the same lock on the + /// internal structure that is used by Consume and the light-weight task. Doing so could + /// result in a deadlock with the Consume call. + /// </remarks> + /**/ + virtual message_status propagate_message(message<_Type> * _PMessage, ISource<_Type> * _PSource) + { + message_status _Result = postponed; + + bool _FDone = false; + + { + _NR_lock lockHolder(_M_propagationLock); + if (_M_pGreedyMessage != NULL) + { + _M_savedId = _PMessage->msg_id(); + _Result = postponed; + _FDone = true; + } + } + + if (!_FDone) + { + _M_pGreedyMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_M_pGreedyMessage != NULL) + { + _Result = accepted; + async_send(NULL); + } + else + { + _Result = missed; + } + } + + return _Result; + } + + /// <summary> + /// Accept the message by making a copy of the payload. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /**/ + virtual message<size_t> * accept_message(runtime_object_identity _MsgId) + { + // This check is to prevent spoofing and verify that the propagated message is + // the one that is accepted at the end. + if (_M_pSendMessage == NULL || _MsgId != _M_pSendMessage->msg_id()) + { + return NULL; + } + + // + // Instead of returning the internal message, we return a copy of the + // message passed in. + // + // Because we are returning a copy, the accept routine for a _Greedy_node + // does not need to grab the internal lock. + // + return (new message<size_t>(_M_pSendMessage->payload)); + } + + + /// <summary> + /// Takes the message and propagates it to all the targets of this _Greedy_node + /// </summary> + /// <param name="_PMessage"> + /// A pointer to a new message. + /// </param> + /// <remarks> + /// This function packages its _M_index into a message and immediately sends it to the targets. + /// </remarks> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<size_t> *) + { + _R_lock _Lock(_M_resetLock); + + if (_M_pSendMessage == NULL) + { + // Save the incoming message so that it can be consumed in the accept function + _M_pReceiveMessage = _M_pGreedyMessage; + _Create_send_message(); + } + + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<size_t> * _PTarget = *_Iter; + _PTarget->propagate(_M_pSendMessage, this); + } + } + +private: + + // + // Private Data Members + // + + // The message to be saved by a greedy order node + message<_Type> * _M_pGreedyMessage; + + // The lock used to protect propagation + ::Concurrency::details::_NonReentrantPPLLock _M_propagationLock; + + // The lock used to protect modification during a reset + ::Concurrency::details::_ReentrantPPLLock _M_resetLock; + + // For greedy order-nodes, the message ID of subsequent messages sent to this node + // For non-greedy order nodes, the message ID of the message to reserve/consume + runtime_object_identity _M_savedId; + +private: + // + // Hide assignment operator and copy constructor + // + _Greedy_node const & operator=(_Greedy_node const &); // no assignment operator + _Greedy_node(_Greedy_node const &); // no copy constructor +}; + + +/// <summary> +/// Helper class used in multi-type non-greedy join blocks +/// Ordered node is a single-target, single-source ordered propagator block +/// </summary> +/// +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/**/ +template<class _Type> +class _Non_greedy_node: public _Order_node_base<_Type> +{ +public: + /// <summary> + /// Constructs a _Non_greedy_node within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Non_greedy_node(ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_savedId(-1), + _M_reservedId(-1), + _M_pReservedSource(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget); + } + + /// <summary> + /// Constructs a _Non_greedy_node within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Non_greedy_node(ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_savedId(-1), + _M_reservedId(-1), + _M_pReservedSource(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget); + } + + /// <summary> + /// Constructs a _Non_greedy_node within the specified scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PScheduler"> + /// A reference to a scheduler instance. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Non_greedy_node(Scheduler& _PScheduler, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_savedId(-1), + _M_reservedId(-1), + _M_pReservedSource(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget, &_PScheduler); + } + + /// <summary> + /// Constructs a _Non_greedy_node within the specified scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PScheduler"> + /// A reference to a scheduler instance. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Non_greedy_node(Scheduler& _PScheduler, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_savedId(-1), + _M_reservedId(-1), + _M_pReservedSource(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget, &_PScheduler); + } + + /// <summary> + /// Constructs a _Non_greedy_node within the specified schedule group. The scheduler is implied + /// by the schedule group. + /// </summary> + /// <param name="_PScheduleGroup"> + /// A reference to a schedule group. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /**/ + _Non_greedy_node(ScheduleGroup& _PScheduleGroup, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget = NULL) : + _M_savedId(-1), + _M_reservedId(-1), + _M_pReservedSource(NULL) + { + _Initialize_order_node(_PSource, _Index, _PTarget, NULL, &_PScheduleGroup); + } + + /// <summary> + /// Constructs a _Non_greedy_node within the specified schedule group. The scheduler is implied + /// by the schedule group. + /// </summary> + /// <param name="_PScheduleGroup"> + /// A reference to a schedule group. + /// </param> + /// <param name="_PSource"> + /// The source of data passed into the node + /// </param> + /// <param name="_Index"> + /// The node's index, assigned from the outside. + /// </param> + /// <param name="_PTarget"> + /// The target to which the node will signal about having received its input data + /// </param> + /// <param name="_Filter"> + /// A reference to a filter function. + /// </param> + /**/ + _Non_greedy_node(ScheduleGroup& _PScheduleGroup, ISource<_Type> * _PSource, size_t _Index, ITarget<size_t> * _PTarget, filter_method const& _Filter) : + _M_savedId(-1), + _M_reservedId(-1), + _M_pReservedSource(NULL) + { + register_filter(_Filter); + _Initialize_order_node(_PSource, _Index, _PTarget, NULL, &_PScheduleGroup); + } + + /// <summary> + /// Cleans up any resources that may have been created by the _Order_node. + /// </summary> + /**/ + ~_Non_greedy_node() + { + if (_M_pReservedSource != NULL) + { + _M_pReservedSource = NULL; + _M_connectedSources.release(); + } + + // Remove all links + remove_network_links(); + } + + /// <summary> + /// Resets the _Order_node and prepares it for the next propagation + /// </summary> + /// <remarks> + /// _Reset is called from Populate_destination_tuple through propagate_to_any_targets() + /// thus, it always has the internal lock held. + /// </remarks> + /**/ + void _Reset() + { + _R_lock _Lock(_M_resetLock); + + delete _M_pReceiveMessage; + _M_pReceiveMessage = NULL; + + delete _M_pSendMessage; + _M_pSendMessage = NULL; + } + + /// <summary> + /// Called for a non_greedy type join block in order to reserve the message + /// in this join block + /// </summary> + /// <returns> + /// A bool indicating whether the reservation worked + /// </returns> + /**/ + bool _Reserve_received_message() + { + bool _Ret_val = false; + + // Order node has only a single source. + // Obtain an iterator to the first source. It will guarantee that the reference + // count on the source is maintained + source_iterator _Iter = _M_connectedSources.begin(); + ISource<_Type> * _PSource = *_Iter; + + if (_PSource != NULL) + { + // CAS out the current saved ID, in order to try and reserve it + runtime_object_identity _SavedId = _InterlockedExchange((volatile long *) &_M_savedId, -1); + + _Ret_val = _PSource->reserve(_SavedId, this); + // + // If this reserved failed, that means we need to wait for another message + // to come in on this link. _M_savedID was set to -1 to indicate to the _Order_node + // that it needs to async_send when that next message comes through + // + // If the reserve succeeds, save away the reserved ID. This will be use later in + // consume + // + if (_Ret_val) + { + _M_reservedId = _SavedId; + + // Acquire a reference on the source + _M_connectedSources.reference(); + _M_pReservedSource = _PSource; + } + } + + return _Ret_val; + } + + /// <summary> + /// Called for a non_greedy type join block in order to consume the message + /// in this join block that has been reserved + /// </summary> + /**/ + void _Consume_received_message() + { + if (_M_pReservedSource != NULL) + { + runtime_object_identity _SavedId = _M_reservedId; + _M_pReceiveMessage = _M_pReservedSource->consume(_SavedId, this); + + runtime_object_identity _OldId = NULL; + _OldId = _InterlockedExchange((volatile long *) &_M_reservedId, -1); + + _CONCRT_ASSERT(_OldId == _SavedId); + + // Release the reference on the source + _M_pReservedSource = NULL; + _M_connectedSources.release(); + } + } + + /// <summary> + /// Called for a non_greedy type join block release a reservation on this block + /// </summary> + /**/ + bool _Release_received_message() + { + bool retVal = false; + + if (_M_pReservedSource != NULL) + { + runtime_object_identity _SavedId = _M_reservedId; + // If the _M_savedId is still -1, then swap the succeeded one back + _M_pReservedSource->release(_SavedId, this); + + if (_InterlockedCompareExchange((volatile long *) &_M_savedId, _SavedId, -1) == -1) + { + retVal = true; + } + + // Release the reference on the source + _M_pReservedSource = NULL; + _M_connectedSources.release(); + } + + return retVal; + } + +protected: + + // + // propagator_block protected function implementation + // + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>ITarget</c> block. It is invoked + /// by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /// <remarks> + /// It is important that calls to propagate do *not* take the same lock on the + /// internal structure that is used by Consume and the light-weight task. Doing so could + /// result in a deadlock with the Consume call. + /// </remarks> + /**/ + virtual message_status propagate_message(message<_Type> * _PMessage, ISource<_Type> *) + { + // Change the message ID. If it was -1, that means an async-send needs to occur + if (_InterlockedExchange((volatile long *) &_M_savedId, _PMessage->msg_id()) == -1) + { + async_send(NULL); + } + + // Always return postponed. This message will be consumed + // in the LWT + + return postponed; + } + + /// <summary> + /// Accept the message by making a copy of the payload. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /**/ + virtual message<size_t> * accept_message(runtime_object_identity _MsgId) + { + // This check is to prevent spoofing and verify that the propagated message is + // the one that is accepted at the end. + if (_M_pSendMessage == NULL || _MsgId != _M_pSendMessage->msg_id()) + { + return NULL; + } + + // + // Instead of returning the internal message, we return a copy of the + // message passed in. + // + // Because we are returning a copy, the accept routine for a _Non_greedy_node + // does not need to grab the internal lock. + // + return (new message<size_t>(_M_pSendMessage->payload)); + } + + /// <summary> + /// Takes the message and propagates it to all the targets of this _Order_node + /// </summary> + /// <param name="_PMessage"> + /// A pointer to a new message. + /// </param> + /// <remarks> + /// This function packages its _M_index into a message and immediately sends it to the targets. + /// </remarks> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<size_t> *) + { + _R_lock _Lock(_M_resetLock); + + if (_M_pSendMessage == NULL) + { + _Create_send_message(); + } + + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<size_t> * _PTarget = *_Iter; + _PTarget->propagate(_M_pSendMessage, this); + } + } + +private: + + // + // Private Data Members + // + + // The source where we have reserved a message + ISource<_Type> * _M_pReservedSource; + + // The lock used to protect modification during a reset + ::Concurrency::details::_ReentrantPPLLock _M_resetLock; + + // For non-greedy order nodes, the message ID of the message to reserve/consume + runtime_object_identity _M_savedId; + + // For non-greedy order nodes, the reserved ID of the message that was reserved + runtime_object_identity _M_reservedId; + + // The marker that indicates that _Non_greedy_node has reserved a message + volatile bool _M_fIsInitialized; + +private: + // + // Hide assignment operator and copy constructor + // + _Non_greedy_node const & operator=(_Non_greedy_node const &); // no assignment operator + _Non_greedy_node(_Non_greedy_node const &); // no copy constructor +}; + +//************************************************************************** +// Choice: +//************************************************************************** + +/// <summary> +/// A <c>choice</c> messaging block is a multi-source, single-target block that represents a control-flow +/// interaction with a set of sources. The choice block will wait for any one of multiple sources to +/// produce a message and will propagate the index of the source that produced the message. +/// </summary> +/// <typeparam name="_Type"> +/// A <c>tuple</c>-based type representing the payloads of the input sources. +/// </typeparam> +/// <remarks> +/// The choice block ensures that only one of the incoming messages is consumed. +/// <para>For more information, see <see cref="Asynchronous Message Blocks"/>.</para> +/// </remarks> +/// <seealso cref="join Class"/> +/// <seealso cref="single_assignment Class"/> +/// <seealso cref="make_choice Function"/> +/// <seealso cref="tuple Class"/> +/**/ +template<class _Type> +class choice: public ISource<size_t> +{ +public: + + /// <summary> + /// Constructs a <c>choice</c> messaging block. + /// </summary> + /// <param name="_Tuple"> + /// A <c>tuple</c> of sources for the choice. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + explicit choice(_Type _Tuple) : _M_sourceTuple(_Tuple), _M_pScheduler(NULL), _M_pScheduleGroup(NULL) + { + _M_pSingleAssignment = new single_assignment<size_t>(); + _Initialize_choices<0>(); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs a <c>choice</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>choice</c> messaging block is scheduled. + /// </param> + /// <param name="_Tuple"> + /// A <c>tuple</c> of sources for the choice. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + choice(Scheduler& _PScheduler, _Type _Tuple) : _M_sourceTuple(_Tuple), _M_pScheduler(&_PScheduler), _M_pScheduleGroup(NULL) + { + _M_pSingleAssignment = new single_assignment<size_t>(_PScheduler); + _Initialize_choices<0>(); + } + + /// <summary> + /// Constructs a <c>choice</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>choice</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Tuple"> + /// A <c>tuple</c> of sources for the choice. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + choice(ScheduleGroup& _PScheduleGroup, _Type _Tuple) : _M_sourceTuple(_Tuple), _M_pScheduler(NULL), _M_pScheduleGroup(&_PScheduleGroup) + { + _M_pSingleAssignment = new single_assignment<size_t>(_PScheduleGroup); + _Initialize_choices<0>(); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Constructs a <c>choice</c> messaging block. + /// </summary> + /// <param name="_Choice"> + /// A <c>choice</c> messaging block to copy from. + /// Note that the original object is orphaned, making this a move constructor. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + choice(choice && _Choice) + { + // Copy scheduler group or scheduler to the new object. + _M_pScheduleGroup = _Choice._M_pScheduleGroup; + _M_pScheduler = _Choice._M_pScheduler; + + // Single assignment is heap allocated, so simply copy the pointer. If it already has + // a value, it will be preserved. + _M_pSingleAssignment = _Choice._M_pSingleAssignment; + _Choice._M_pSingleAssignment = NULL; + + // Invoke copy assignment for tuple to copy pointers to message blocks. + _M_sourceTuple = _Choice._M_sourceTuple; + + // Copy the pointers to order nodes to a new object and zero out in the old object. + memcpy(_M_pSourceChoices, _Choice._M_pSourceChoices, sizeof(_M_pSourceChoices)); + memset(_Choice._M_pSourceChoices, 0, sizeof(_M_pSourceChoices)); + } + + /// <summary> + /// Destroys the <c>choice</c> messaging block. + /// </summary> + /**/ + ~choice() + { + delete _M_pSingleAssignment; + _Delete_choices<0>(); + } + + /// <summary> + /// A type alias for <typeparamref name="_Type"/>. + /// </summary> + /**/ + typedef typename _Type type; + + /// <summary> + /// Checks whether this <c>choice</c> messaging block has been initialized with a value yet. + /// </summary> + /// <returns> + /// <c>true</c> if the block has received a value, <c>false</c> otherwise. + /// </returns> + /**/ + bool has_value() const + { + return _M_pSingleAssignment->has_value(); + } + + /// <summary> + /// Returns an index into the <c>tuple</c> representing the element selected by the + /// <c>choice</c> messaging block. + /// </summary> + /// <returns> + /// The message index. + /// </returns> + /// <remarks> + /// The message payload can be extracted using the <c>get</c> method. + /// </remarks> + /**/ + size_t index() + { + return _M_pSingleAssignment->value(); + } + + /// <summary> + /// Gets the message whose index was selected by the <c>choice</c> messaging block. + /// </summary> + /// <typeparam name="_Payload_type"> + /// The type of the message payload. + /// </typeparam> + /// <returns> + /// The payload of the message. + /// </returns> + /// <remarks> + /// Because a <c>choice</c> messaging block can take inputs with different payload types, you must specify + /// the type of the payload at the point of retrieval. You can determine the type based on the result of + /// the <c>index</c> method. + /// </remarks> + /**/ + template <typename _Payload_type> + _Payload_type const & value() + { + return reinterpret_cast<_Reserving_node<_Payload_type> *>(_M_pSourceChoices[_M_pSingleAssignment->value()])->value(); + } + + // + // ISource public function implementations + // + + /// <summary> + /// Links a target block to this <c>choice</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to an <c>ITarget</c> block to link to this <c>choice</c> messaging block. + /// </param> + /**/ + virtual void link_target(_Inout_ ITarget<size_t> * _PTarget) + { + _M_pSingleAssignment->link_target(_PTarget); + } + + /// <summary> + /// Unlinks a target block from this <c>choice</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to an <c>ITarget</c> block to unlink from this <c>choice</c> messaging block. + /// </param> + /**/ + virtual void unlink_target(_Inout_ ITarget<size_t> * _PTarget) + { + _M_pSingleAssignment->unlink_target(_PTarget); + } + + /// <summary> + /// Unlinks all targets from this <c>choice</c> messaging block. + /// </summary> + /// <remarks> + /// This method does not need to be called from the destructor because destructor for the internal + /// <c>single_assignment</c> block will unlink properly. + /// </remarks> + /**/ + virtual void unlink_targets() + { + _M_pSingleAssignment->unlink_targets(); + } + + /// <summary> + /// Accepts a message that was offered by this <c>choice</c> block, transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>accept</c> method. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /**/ + virtual message<size_t> * accept(runtime_object_identity _MsgId, _Inout_ ITarget<size_t> * _PTarget) + { + return _M_pSingleAssignment->accept(_MsgId, _PTarget); + } + + /// <summary> + /// Reserves a message previously offered by this <c>choice</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>reserve</c> method. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. Reservations can fail + /// for many reasons, including: the message was already reserved or accepted by another target, the source could + /// deny reservations, and so forth. + /// </returns> + /// <remarks> + /// After you call <c>reserve</c>, if it succeeds, you must call either <c>consume</c> or <c>release</c> + /// in order to take or give up possession of the message, respectively. + /// </remarks> + /**/ + virtual bool reserve(runtime_object_identity _MsgId, _Inout_ ITarget<size_t> * _PTarget) + { + return _M_pSingleAssignment->reserve(_MsgId, _PTarget); + } + + /// <summary> + /// Consumes a message previously offered by this <c>choice</c> messaging block and successfully reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the reserved <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>consume</c> method. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// The <c>consume</c> method is similar to <c>accept</c>, but must always be preceded by a call to <c>reserve</c> that + /// returned <c>true</c>. + /// </remarks> + /**/ + virtual message<size_t> * consume(runtime_object_identity _MsgId, _Inout_ ITarget<size_t> * _PTarget) + { + return _M_pSingleAssignment->consume(_MsgId, _PTarget); + } + + /// <summary> + /// Releases a previous successful message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>release</c> method. + /// </param> + /**/ + virtual void release(runtime_object_identity _MsgId, _Inout_ ITarget<size_t> * _PTarget) + { + _M_pSingleAssignment->release(_MsgId, _PTarget); + } + + /// <summary> + /// Acquires a reference count on this <c>choice</c> messaging block, to prevent deletion. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being linked to this source + /// during the <c>link_target</c> method. + /// </remarks> + /**/ + virtual void acquire_ref(_Inout_ ITarget<size_t> * _PTarget) + { + _M_pSingleAssignment->acquire_ref(_PTarget); + } + + /// <summary> + /// Releases a reference count on this <c>choice</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being unlinked from this source. + /// The source block is allowed to release any resources reserved for the target block. + /// </remarks> + /**/ + virtual void release_ref(_Inout_ ITarget<size_t> * _PTarget) + { + _M_pSingleAssignment->release_ref(_PTarget); + } + +private: + + /// <summary> + /// Constructs and initializes a _Reserving_node for each tuple messaging block passed in. + /// </summary> + /// <typeparam>The highest-number index of the choice's sources</typeparam> + /**/ + template<int _Index> + void _Initialize_choices() + { + std::tr1::tuple_element<_Index, _Type>::type _Item = std::tr1::get<_Index>(_M_sourceTuple); + _Reserving_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> * _Order_node_element = NULL; + + if (_M_pScheduleGroup != NULL) + { + _Order_node_element = new _Reserving_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (*_M_pScheduleGroup, _Item, _Index); + } + else if (_M_pScheduler != NULL) + { + _Order_node_element = new _Reserving_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (*_M_pScheduler, _Item, _Index); + } + else + { + _Order_node_element = new _Reserving_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (_Item, _Index); + } + + _M_pSourceChoices[_Index] = _Order_node_element; + _Order_node_element->link_target(_M_pSingleAssignment); + _Initialize_choices<_Index + 1>(); + } + + /// <summary> + /// Provides a sentinel template specialization for _Initialize_choices recursive + /// template expansion. + /// </summary> + /**/ + template<> void _Initialize_choices<std::tr1::tuple_size<_Type>::value>() + { + } + + /// <summary> + /// Deletes all _Reserving_node elements that were created in _Initialize_choices. + /// </summary> + /// <typeparam>The highest-number index of the choice's sources</typeparam> + /**/ + template<int _Index> + void _Delete_choices() + { + delete reinterpret_cast<_Reserving_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> *>(_M_pSourceChoices[_Index]); + _M_pSourceChoices[_Index] = NULL; + _Delete_choices<_Index + 1>(); + } + + /// <summary> + /// Provides a sentinel template specialization for _Delete_choices recursive + /// template expansion. + /// </summary> + /**/ + template<> void _Delete_choices<std::tr1::tuple_size<_Type>::value>() + { + } + + // Array of pointers to _Reserving_node elements representing each source + void * _M_pSourceChoices[std::tr1::tuple_size<_Type>::value]; + + // Single assignment which chooses between source messaging blocks + single_assignment<size_t> * _M_pSingleAssignment; + + // Tuple of messaging blocks that are sources to this choice + _Type _M_sourceTuple; + + // The scheduler to propagate messages on + Scheduler * _M_pScheduler; + + // The schedule group to propagate messages on + ScheduleGroup * _M_pScheduleGroup; + +private: + // + // Hide assignment operator + // + choice const &operator =(choice const &); // no assignment operator + choice(choice const &); // no copy constructor +}; + +// Templated factory functions that create a choice, three flavors + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/// <summary> +/// Constructs a <c>choice</c> messaging block from an optional <c>Scheduler</c> or <c>ScheduleGroup</c> +/// and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_PScheduler"> +/// The <c>Scheduler</c> object within which the propagation task for the <c>choice</c> messaging block is scheduled. +/// </param> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>choice</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="choice Class"/> +/// <seealso cref="Scheduler Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +choice<std::tuple<_Type1, _Type2, _Types...>> +make_choice(Scheduler& _PScheduler, _Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return choice<std::tuple<_Type1, _Type2, _Types...>>(_PScheduler, std::make_tuple(_Item1, _Item2, _Items...)); +} + +/// <summary> +/// Constructs a <c>choice</c> messaging block from an optional <c>Scheduler</c> or <c>ScheduleGroup</c> +/// and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_PScheduleGroup"> +/// The <c>ScheduleGroup</c> object within which the propagation task for the <c>choice</c> messaging block is scheduled. +/// The <c>Scheduler</c> object used is implied by the schedule group. +/// </param> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>choice</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="choice Class"/> +/// <seealso cref="ScheduleGroup Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +choice<std::tuple<_Type1, _Type2, _Types...>> +make_choice(ScheduleGroup& _PScheduleGroup, _Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return choice<std::tuple<_Type1, _Type2, _Types...>>(_PScheduleGroup, std::make_tuple(_Item1, _Item2, _Items...)); +} + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/// <summary> +/// Constructs a <c>choice</c> messaging block from an optional <c>Scheduler</c> or <c>ScheduleGroup</c> +/// and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>choice</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="choice Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +choice<std::tuple<_Type1, _Type2, _Types...>> +make_choice(_Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return choice<std::tuple<_Type1, _Type2, _Types...>>(std::make_tuple(_Item1, _Item2, _Items...)); +} + +//************************************************************************** +// Join: +//************************************************************************** + +// Template specialization used to unwrap the types from within a tuple. + +/**/ +template <typename _Tuple> struct _Unwrap; + +/// <summary> +/// Template specialization used to unwrap the types from within a tuple. +/// </summary> +/// <typeparam name="_Types"> +/// The types of the elements of the tuple. +/// </typeparam> +/**/ +template <typename... _Types> +struct _Unwrap<std::tuple<_Types...>> +{ + typedef std::tuple<typename std::remove_pointer<_Types>::type::source_type...> type; +}; + +/// <summary> +/// Defines a block allowing sources of distinct types to be joined. +/// Join node is a single-target, multi-source ordered propagator block +/// </summary> +/// <typeparam name="_Type"> +/// The payload tuple type +/// </typeparam> +/// <typeparam name="_Jtype"> +/// The kind of join this is, either 'greedy' or 'non-greedy' +/// </typeparam> +/**/ +template<typename _Type, typename _Destination_type, join_type _Jtype> +class _Join_node: public propagator_block<single_link_registry<ITarget<_Destination_type>>, multi_link_registry<ISource<size_t>>> +{ +public: + + /// <summary> + /// Constructs a join within the default scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /**/ + _Join_node() : _M_counter(std::tr1::tuple_size<_Destination_type>::value) + { + initialize_source_and_target(); + } + + /// <summary> + /// Constructs a join within the specified scheduler, and places it on any schedule + /// group of the scheduler's choosing. + /// </summary> + /// <param name="_PScheduler"> + /// A reference to a scheduler instance. + /// </param> + /**/ + _Join_node(Scheduler& _PScheduler) : _M_counter(std::tr1::tuple_size<_Destination_type>::value) + { + initialize_source_and_target(&_PScheduler); + } + + /// <summary> + /// Constructs a join within the specified schedule group. The scheduler is implied + /// by the schedule group. + /// </summary> + /// <param name="_PScheduleGroup"> + /// A reference to a schedule group. + /// </param> + /**/ + _Join_node(ScheduleGroup& _PScheduleGroup) : _M_counter(std::tr1::tuple_size<_Destination_type>::value) + { + initialize_source_and_target(NULL, &_PScheduleGroup); + } + + /// <summary> + /// Cleans up any resources that may have been created by the join. + /// </summary> + /**/ + ~_Join_node() + { + // Remove all links + remove_network_links(); + + // Clean up any messages left in this message block + _Delete_stored_messages(); + } + +protected: + + /// <summary> + /// Asynchronously passes a message from an <c>ISource</c> block to this <c>ITarget</c> block. It is invoked + /// by the <c>propagate</c> method, when called by a source block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to the <c>message</c> object. + /// </param> + /// <param name="_PSource"> + /// A pointer to the source block offering the message. + /// </param> + /// <returns> + /// A <see cref="message_status Enumeration">message_status</see> indication of what + /// the target decided to do with the message. + /// </returns> + /**/ + virtual message_status propagate_message(message<size_t> * _PMessage, ISource<size_t> * _PSource) + { + // This join block is connected to the _Order_node sources, which know not to send + // any more messages until join propagates them further. That is why join can + // always accept the incoming messages. + + _PMessage = _PSource->accept(_PMessage->msg_id(), this); + + // + // Source block created an int message only to notify join that the real + // payload is available. There is no need to keep this message around. + // + _CONCRT_ASSERT(_PMessage != NULL); + delete _PMessage; + + long _Ret_val = _InterlockedDecrement(&_M_counter); + + _CONCRT_ASSERT(_Ret_val >= 0); + + if (_Ret_val == 0) + { + // + // All source messages are now received so join can propagate them further + // + async_send(NULL); + } + + return accepted; + } + + /// <summary> + /// Accepts an offered message by the source, transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /**/ + virtual message<_Destination_type> * accept_message(runtime_object_identity _MsgId) + { + // + // Peek at the head message in the message buffer. If the IDs match + // dequeue and transfer ownership + // + message<_Destination_type> * _Msg = NULL; + + if (_M_messageBuffer._Is_head(_MsgId)) + { + _Msg = _M_messageBuffer._Dequeue(); + } + + return _Msg; + } + + /// <summary> + /// Reserves a message previously offered by the source. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A bool indicating whether the reservation worked or not. + /// </returns> + /// <remarks> + /// After <c>reserve</c> is called, if it returns <c>true</c>, either <c>consume</c> or <c>release</c> must be called + /// to either take or release ownership of the message. + /// </remarks> + /**/ + virtual bool reserve_message(runtime_object_identity _MsgId) + { + // Allow reservation if this is the head message + return _M_messageBuffer._Is_head(_MsgId); + } + + /// <summary> + /// Consumes a message previously offered by the source and reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /// <remarks> + /// <c>consume_message</c> is similar to <c>accept</c>, but is always preceded by a call to <c>reserve</c>. + /// </remarks> + /**/ + virtual message<_Destination_type> * consume_message(runtime_object_identity _MsgId) + { + // By default, accept the message + return accept_message(_MsgId); + } + + /// <summary> + /// Releases a previous message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The runtime object identity of the message. + /// </param> + /**/ + virtual void release_message(runtime_object_identity _MsgId) + { + // The head message is the one reserved. + if (!_M_messageBuffer._Is_head(_MsgId)) + { + throw message_not_found(); + } + } + + /// <summary> + /// Resumes propagation after a reservation has been released + /// </summary> + /**/ + virtual void resume_propagation() + { + // If there are any messages in the buffer, propagate them out + if (_M_messageBuffer._Count() > 0) + { + async_send(NULL); + } + } + + /// <summary> + /// Notification that a target was linked to this source. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the newly linked target. + /// </param> + /**/ + virtual void link_target_notification(_Inout_ ITarget<_Destination_type> *) + { + // There is only a single target. + _Propagate_priority_order(_M_messageBuffer); + } + + /// <summary> + /// Takes the message and propagates it to all the targets of this <c>join</c> block. + /// </summary> + /// <param name="_PMessage"> + /// A pointer to a new message. + /// </param> + /// <remarks> + /// This function packages source payloads into a tuple message and immediately sends it to the targets. + /// </remarks> + /**/ + virtual void propagate_to_any_targets(_Inout_opt_ message<_Destination_type> *) + { + message<_Destination_type> * _Msg = NULL; + + if (_M_counter == 0) + { + bool fIsNonGreedy = (_Jtype == non_greedy); + + if (fIsNonGreedy) + { + if (!_Non_greedy_acquire_messages()) + { + return; + } + } + + if (!fIsNonGreedy) + { + // Because a greedy join has captured all input, we can reset + // the counter to the total number of inputs + _InterlockedExchange(&_M_counter, std::tr1::tuple_size<_Destination_type>::value); + } + + _Msg = _Create_send_message(); + } + + if (_Msg != NULL) + { + _M_messageBuffer._Enqueue(_Msg); + + if (!_M_messageBuffer._Is_head(_Msg->msg_id())) + { + // another message is at the head of the outbound message queue and blocked + // simply return + return; + } + } + + _Propagate_priority_order(_M_messageBuffer); + } + +private: + + /// <summary> + /// Tries to reserve from all sources. If successful, it will consume all the messages + /// </summary> + /// <returns> + /// A bool indicating whether the consumption attempt worked. + /// </returns> + /// <typeparam name="_Index"> + /// The highest-number index of the join's sources + /// </typeparam> + /**/ + template<int _Index> + bool _Try_consume_source_messages(_Destination_type & _Destination_tuple, ISource<size_t> ** _Sources) + { + _Non_greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> * _Node = + static_cast<_Non_greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> *>(_Sources[_Index]); + + // Increment the counter once for each reservation + _InterlockedIncrement(&_M_counter); + + if (_Node->_Reserve_received_message()) + { + bool _Ret_val = _Try_consume_source_messages<_Index + 1>(_Destination_tuple, _Sources); + + if (_Ret_val) + { + _Node->_Consume_received_message(); + } + else + { + if (_Node->_Release_received_message()) + { + // If _Release_received_message() restored the ID, decrement the count for that + // restoration + if (_InterlockedDecrement(&_M_counter) == 0) + { + async_send(NULL); + } + } + } + + return _Ret_val; + } + + return false; + } + + /// <summary> + /// Provides a sentinel template specialization for _Try_consume_source_messages recursive + /// template expansion. + /// </summary> + /// <returns> + /// A bool indicating whether the consumption attempt worked. + /// </returns> + /**/ + template<> bool _Try_consume_source_messages<std::tr1::tuple_size<_Type>::value>(_Destination_type &, ISource<size_t> **) + { + return true; + } + + /// <summary> + /// Tries to acquire all of the messages from the _Non_greedy_nodes. Each node has already + /// indicated that it has received a message that it can try to reserve. This function + /// starts the reservation and consume process. + /// </summary> + /// <returns> + /// A bool indicating whether the reserve/consume of all messages succeeded. + /// </returns> + /**/ + bool _Non_greedy_acquire_messages() + { + _Destination_type _Destination_tuple; + + // Populate the sources buffer + ISource<size_t> * _Sources[std::tr1::tuple_size<_Type>::value]; + size_t _Index = 0; + + // Get an iterator which will keep a reference on the connected sources + source_iterator _Iter = _M_connectedSources.begin(); + + while (*_Iter != NULL) + { + ISource<size_t> * _PSource = *_Iter; + + if (_PSource == NULL) + { + // One of the sources disconnected + break; + } + + if (_Index >= std::tr1::tuple_size<_Type>::value) + { + // More sources that we expect + break; + } + + _Sources[_Index] = _PSource; + _Index++; + ++_Iter; + } + + // The order nodes should not have unlinked while the join node is + // active. + + if (_Index != std::tr1::tuple_size<_Type>::value) + { + // On debug build assert to help debugging + _CONCRT_ASSERT(_Index == std::tr1::tuple_size<_Type>::value); + return false; + } + + bool _IsAcquireSuccessful = _Try_consume_source_messages<0>(_Destination_tuple, _Sources); + + return _IsAcquireSuccessful; + } + + /// <summary> + /// Propagate messages in priority order + /// </summary> + /// <param name="_MessageBuffer"> + /// Reference to a message queue with messages to be propagated + /// </param> + /**/ + void _Propagate_priority_order(::Concurrency::details::_Queue<message<_Target_type>> & _MessageBuffer) + { + message<_Target_type> * _Msg = _MessageBuffer._Peek(); + + // If someone has reserved the _Head message, don't propagate anymore + if (_M_pReservedFor != NULL) + { + return; + } + + while (_Msg != NULL) + { + message_status _Status = declined; + + // Always start from the first target that linked + for (target_iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<_Target_type> * _PTarget = *_Iter; + _Status = _PTarget->propagate(_Msg, this); + + // Ownership of message changed. Do not propagate this + // message to any other target. + if (_Status == accepted) + { + break; + } + + // If the target just propagated to reserved this message, stop + // propagating it to others + if (_M_pReservedFor != NULL) + { + break; + } + } + + // If status is anything other than accepted, then the head message + // was not propagated out. Thus, nothing after it in the queue can + // be propagated out. Cease propagation. + if (_Status != accepted) + { + break; + } + + // Get the next message + _Msg = _MessageBuffer._Peek(); + } + } + + /// <summary> + /// Called when all the source messaging blocks have received their messages. The payloads are copied + /// into local tuple and then packaged into a message to be propagated: _M_pSendMessage. + /// </summary> + /**/ + message<_Destination_type> * _Create_send_message() + { + _Destination_type _Destination_tuple; + + // Populate the sources buffer + ISource<size_t> * _Sources[std::tr1::tuple_size<_Type>::value]; + size_t _Index = 0; + + // Get an iterator which will keep a reference on the connected sources + source_iterator _Iter = _M_connectedSources.begin(); + + while (*_Iter != NULL) + { + ISource<size_t> * _PSource = *_Iter; + + if (_PSource == NULL) + { + // One of the sources disconnected + break; + } + + // Avoid buffer overrun + if (_Index >= std::tr1::tuple_size<_Type>::value) + { + // More sources that we expect + break; + } + + _Sources[_Index] = *_Iter; + _Index++; + ++_Iter; + } + + // The order nodes should not have unlinked while the join node is + // active. + if (_Index != std::tr1::tuple_size<_Type>::value) + { + // On debug build assert to help debugging + _CONCRT_ASSERT(_Index == std::tr1::tuple_size<_Type>::value); + return NULL; + } + + _Populate_destination_tuple<0>(_Destination_tuple, _Sources); + + return new message<_Destination_type>(_Destination_tuple); + } + + /// <summary> + /// Deletes all messages currently stored in this message block. Should be called + /// by the destructor to ensure any messages propagated in are cleaned up. + /// </summary> + /**/ + void _Delete_stored_messages() + { + // Delete any messages remaining in the output queue + for (;;) + { + message<_Destination_type> * _Msg = _M_messageBuffer._Dequeue(); + if (_Msg == NULL) + { + break; + } + delete _Msg; + } + } + + /// <summary> + /// Copies payloads from all sources to destination tuple. + /// </summary> + /**/ + template<int _Index> + void _Populate_destination_tuple(_Destination_type & _Destination_tuple, ISource<size_t> ** _Sources) + { + _Order_node_base<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> * _Node = + static_cast<_Order_node_base<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> *>(_Sources[_Index]); + + std::tr1::get<_Index>(_Destination_tuple) = _Node->value(); + _Node->_Reset(); + + _Populate_destination_tuple<_Index + 1>(_Destination_tuple, _Sources); + } + + /// <summary> + /// Provides a sentinel template specialization for _Populate_destination_tuple recursive + /// template expansion. + /// </summary> + /**/ + template<> void _Populate_destination_tuple<std::tr1::tuple_size<_Type>::value>(_Destination_type &, ISource<size_t> **) + { + } + + // A tuple containing a collection of source messaging blocks + _Type _M_sourceTuple; + + // Counts messages received by sources of this node and is used to trigger propagation to targets + // This value starts at the total number of inputs and counts down to zero. When it reaches zero, + // a join of the inputs is started. + volatile long _M_counter; + + // Buffer to hold outgoing messages + ::Concurrency::details::_Queue<message<_Destination_type>> _M_messageBuffer; + +private: + // + // Hide assignment operator and copy constructor + // + _Join_node(const _Join_node & _Join); // no copy constructor + _Join_node const &operator =(_Join_node const &); // no assignment operator +}; + +/// <summary> +/// A <c>multitype_join</c> messaging block is a multi-source, single-target messaging block that +/// combines together messages of different types from each of its sources and offers a tuple +/// of the combined messages to its targets. +/// </summary> +/// <typeparam name="_Type"> +/// The <c>tuple</c> payload type of the messages joined and propagated by the block. +/// </typeparam> +/// <typeparam name="_Jtype"> +/// The kind of <c>join</c> block this is, either <c>greedy</c> or <c>non_greedy</c> +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Asynchronous Message Blocks"/>. +/// </remarks> +/// <seealso cref="choice Class"/> +/// <seealso cref="join Class"/> +/// <seealso cref="join_type Enumeration"/> +/// <seealso cref="make_join Function"/> +/// <seealso cref="make_greedy_join Function"/> +/// <seealso cref="tuple Class"/> +/**/ +template<typename _Type, join_type _Jtype = non_greedy> +class multitype_join: public ISource<typename _Unwrap<_Type>::type> +{ +public: + + typedef typename _Unwrap<_Type>::type _Destination_type; + + /// <summary> + /// Constructs a <c>multitype_join</c> messaging block. + /// </summary> + /// <param name="_Tuple"> + /// A <c>tuple</c> of sources for this <c>multitype_join</c> messaging block. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + explicit multitype_join(_Type _Tuple) : _M_sourceTuple(_Tuple), _M_pScheduler(NULL), _M_pScheduleGroup(NULL) + { + _M_pJoinNode = new _Join_node<_Type, _Destination_type, _Jtype>(); + _Initialize_joins<0>(); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs a <c>multitype_join</c> messaging block. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the propagation task for the <c>multitype_join</c> messaging block is scheduled. + /// </param> + /// <param name="_Tuple"> + /// A <c>tuple</c> of sources for this <c>multitype_join</c> messaging block. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + multitype_join(Scheduler& _PScheduler, _Type _Tuple) : _M_sourceTuple(_Tuple), _M_pScheduler(&_PScheduler), _M_pScheduleGroup(NULL) + { + _M_pJoinNode = new _Join_node<_Type, _Destination_type, _Jtype>(_PScheduler); + _Initialize_joins<0>(); + } + + /// <summary> + /// Constructs a <c>multitype_join</c> messaging block. + /// </summary> + /// <param name="_PScheduleGroup"> + /// The <c>ScheduleGroup</c> object within which the propagation task for the <c>multitype_join</c> messaging block is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <param name="_Tuple"> + /// A <c>tuple</c> of sources for this <c>multitype_join</c> messaging block. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + multitype_join(ScheduleGroup& _PScheduleGroup, _Type _Tuple) : _M_sourceTuple(_Tuple), _M_pScheduler(NULL), _M_pScheduleGroup(&_PScheduleGroup) + { + _M_pJoinNode = new _Join_node<_Type, _Destination_type, _Jtype>(_PScheduleGroup); + _Initialize_joins<0>(); + } +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Constructs a <c>multitype_join</c> messaging block. + /// </summary> + /// <param name="_Join"> + /// A <c>multitype_join</c> messaging block to copy from. + /// Note that the original object is orphaned, making this a move constructor. + /// </param> + /// <remarks> + /// <para> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PScheduleGroup"/> parameters. + /// </para> + /// <para> + /// Move construction is not performed under a lock, which means that it is up to the user + /// to make sure that there are no light-weight tasks in flight at the time of moving. + /// Otherwise, numerous races can occur, leading to exceptions or inconsistent state. + /// </para> + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + multitype_join(multitype_join && _Join) + { + // Copy scheduler group or scheduler to the new object. + _M_pScheduleGroup = _Join._M_pScheduleGroup; + _M_pScheduler = _Join._M_pScheduler; + + // Single assignment is heap allocated, so simply copy the pointer. If it already has + // a value, it will be preserved. + _M_pJoinNode = _Join._M_pJoinNode; + _Join._M_pJoinNode = NULL; + + // Invoke copy assignment for tuple to copy pointers to message blocks. + _M_sourceTuple = _Join._M_sourceTuple; + + // Copy the pointers to order nodes to a new object and zero out in the old object. + memcpy(_M_pSourceJoins, _Join._M_pSourceJoins, sizeof(_M_pSourceJoins)); + memset(_Join._M_pSourceJoins, 0, sizeof(_M_pSourceJoins)); + } + + /// <summary> + /// Destroys the <c>multitype_join</c> messaging block. + /// </summary> + /**/ + ~multitype_join() + { + delete _M_pJoinNode; + _Delete_joins<0>(); + } + + /// <summary> + /// A type alias for <typeparamref name="_Type"/>. + /// </summary> + /**/ + typedef typename _Type type; + + // + // ISource public function implementations + // + + /// <summary> + /// Links a target block to this <c>multitype_join</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to an <c>ITarget</c> block to link to this <c>multitype_join</c> messaging block. + /// </param> + /**/ + virtual void link_target(_Inout_ ITarget<_Destination_type> * _PTarget) + { + _M_pJoinNode->link_target(_PTarget); + } + + /// <summary> + /// Unlinks a target block from this <c>multitype_join</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to an <c>ITarget</c> block to unlink from this <c>multitype_join</c> messaging block. + /// </param> + /**/ + virtual void unlink_target(_Inout_ ITarget<_Destination_type> * _PTarget) + { + _M_pJoinNode->unlink_target(_PTarget); + } + + /// <summary> + /// Unlinks all targets from this <c>multitype_join</c> messaging block. + /// </summary> + /**/ + virtual void unlink_targets() + { + _M_pJoinNode->unlink_targets(); + } + + /// <summary> + /// Accepts a message that was offered by this <c>multitype_join</c> block, transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the offered <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>accept</c> method. + /// </param> + /// <returns> + /// A pointer to the message that the caller now has ownership of. + /// </returns> + /**/ + virtual message<_Destination_type> * accept(runtime_object_identity _MsgId, _Inout_ ITarget<_Destination_type> * _PTarget) + { + return _M_pJoinNode->accept(_MsgId, _PTarget); + } + + /// <summary> + /// Reserves a message previously offered by this <c>multitype_join</c> messaging block. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being reserved. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>reserve</c> method. + /// </param> + /// <returns> + /// <c>true</c> if the message was successfully reserved, <c>false</c> otherwise. Reservations can fail + /// for many reasons, including: the message was already reserved or accepted by another target, the source could + /// deny reservations, and so forth. + /// </returns> + /// <remarks> + /// After you call <c>reserve</c>, if it succeeds, you must call either <c>consume</c> or <c>release</c> + /// in order to take or give up possession of the message, respectively. + /// </remarks> + /**/ + virtual bool reserve(runtime_object_identity _MsgId, _Inout_ ITarget<_Destination_type> * _PTarget) + { + return _M_pJoinNode->reserve(_MsgId, _PTarget); + } + + /// <summary> + /// Consumes a message previously offered by the <c>multitype_join</c> messaging block and successfully reserved by the target, + /// transferring ownership to the caller. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the reserved <c>message</c> object. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>consume</c> method. + /// </param> + /// <returns> + /// A pointer to the <c>message</c> object that the caller now has ownership of. + /// </returns> + /// <remarks> + /// The <c>consume</c> method is similar to <c>accept</c>, but must always be preceded by a call to <c>reserve</c> that + /// returned <c>true</c>. + /// </remarks> + /**/ + virtual message<_Destination_type> * consume(runtime_object_identity _MsgId, _Inout_ ITarget<_Destination_type> * _PTarget) + { + return _M_pJoinNode->consume(_MsgId, _PTarget); + } + + /// <summary> + /// Releases a previous successful message reservation. + /// </summary> + /// <param name="_MsgId"> + /// The <c>runtime_object_identity</c> of the <c>message</c> object being released. + /// </param> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling the <c>release</c> method. + /// </param> + /**/ + virtual void release(runtime_object_identity _MsgId, _Inout_ ITarget<_Destination_type> * _PTarget) + { + _M_pJoinNode->release(_MsgId, _PTarget); + } + + /// <summary> + /// Acquires a reference count on this <c>multitype_join</c> messaging block, to prevent deletion. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being linked to this source + /// during the <c>link_target</c> method. + /// </remarks> + /**/ + virtual void acquire_ref(_Inout_ ITarget<_Destination_type> * _PTarget) + { + _M_pJoinNode->acquire_ref(_PTarget); + } + + /// <summary> + /// Releases a reference count on this <c>multiple_join</c> messaging block. + /// </summary> + /// <param name="_PTarget"> + /// A pointer to the target block that is calling this method. + /// </param> + /// <remarks> + /// This method is called by an <c>ITarget</c> object that is being unlinked from this source. + /// The source block is allowed to release any resources reserved for the target block. + /// </remarks> + /**/ + virtual void release_ref(_Inout_ ITarget<_Destination_type> * _PTarget) + { + _M_pJoinNode->release_ref(_PTarget); + } + +private: + + /// <summary> + /// Constructs and initializes a _Order_node for each tuple messaging block passed in. + /// </summary> + /// <typeparam name="_Index"> + /// The highest-number index of the multitype_join's sources + /// </typeparam> + /**/ + template<int _Index> + void _Initialize_joins() + { + std::tr1::tuple_element<_Index, _Type>::type _Item = std::tr1::get<_Index>(_M_sourceTuple); + _Order_node_base<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> * _Order_node_element = NULL; + + bool fIsNonGreedy = (_Jtype == non_greedy); + + if (fIsNonGreedy) + { + if (_M_pScheduleGroup != NULL) + { + _Order_node_element = new _Non_greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (*_M_pScheduleGroup, _Item, _Index); + } + else if (_M_pScheduler != NULL) + { + _Order_node_element = new _Non_greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (*_M_pScheduler, _Item, _Index); + } + else + { + _Order_node_element = new _Non_greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (_Item, _Index); + } + } + else + { + if (_M_pScheduleGroup != NULL) + { + _Order_node_element = new _Greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (*_M_pScheduleGroup, _Item, _Index); + } + else if (_M_pScheduler != NULL) + { + _Order_node_element = new _Greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (*_M_pScheduler, _Item, _Index); + } + else + { + _Order_node_element = new _Greedy_node<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> (_Item, _Index); + } + } + _M_pSourceJoins[_Index] = _Order_node_element; + _Order_node_element->link_target(_M_pJoinNode); + _Initialize_joins<_Index + 1>(); + } + + /// <summary> + /// Provides a sentinel template specialization for _Initialize_joins recursive + /// template expansion. + /// </summary> + /**/ + template<> void _Initialize_joins<std::tr1::tuple_size<_Type>::value>() + { + } + + /// <summary> + /// Deletes all _Order_node elements that were created in _Initialize_joins. + /// </summary> + /// <typeparam name="_Index"> + /// The highest-number index of the multitype_join's sources + /// </typeparam> + /**/ + template<int _Index> + void _Delete_joins() + { + delete reinterpret_cast<_Order_node_base<std::tr1::remove_pointer<std::tr1::tuple_element<_Index, _Type>::type>::type::source_type> *>(_M_pSourceJoins[_Index]); + _M_pSourceJoins[_Index] = NULL; + _Delete_joins<_Index + 1>(); + } + + /// <summary> + /// Provides a sentinel template specialization for _Delete_joins recursive + /// template expansion. + /// </summary> + /**/ + template<> void _Delete_joins<std::tr1::tuple_size<_Type>::value>() + { + } + + // Array of pointers to _Order_node elements representing each source + void * _M_pSourceJoins[std::tr1::tuple_size<_Type>::value]; + + // Join node that collects source messaging block messages + _Join_node<_Type, _Destination_type, _Jtype> * _M_pJoinNode; + + // Tuple of messaging blocks that are sources to this multitype_join + _Type _M_sourceTuple; + + // The scheduler to propagate messages on + Scheduler * _M_pScheduler; + + // The schedule group to propagate messages on + ScheduleGroup * _M_pScheduleGroup; + +private: + // + // Hide assignment operator + // + multitype_join const &operator =(multitype_join const &); // no assignment operator + multitype_join(multitype_join const &); // no copy constructor +}; + +// Templated factory functions that create a join, three flavors + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/// <summary> +/// Constructs a <c>non_greedy multitype_join</c> messaging block from an optional <c>Scheduler</c> +/// or <c>ScheduleGroup</c> and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_PScheduler"> +/// The <c>Scheduler</c> object within which the propagation task for the <c>multitype_join</c> messaging block is scheduled. +/// </param> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>non_greedy multitype_join</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="multitype_join Class"/> +/// <seealso cref="Scheduler Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +multitype_join<std::tuple<_Type1, _Type2, _Types...>> +make_join(Scheduler& _PScheduler, _Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return multitype_join<std::tuple<_Type1, _Type2, _Types...>>(_PScheduler, std::make_tuple(_Item1, _Item2, _Items...)); +} + +/// <summary> +/// Constructs a <c>non_greedy multitype_join</c> messaging block from an optional <c>Scheduler</c> +/// or <c>ScheduleGroup</c> and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_PScheduleGroup"> +/// The <c>ScheduleGroup</c> object within which the propagation task for the <c>multitype_join</c> messaging block is scheduled. +/// The <c>Scheduler</c> object used is implied by the schedule group. +/// </param> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>non_greedy multitype_join</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="multitype_join Class"/> +/// <seealso cref="ScheduleGroup Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +multitype_join<std::tuple<_Type1, _Type2, _Types...>> +make_join(ScheduleGroup& _PScheduleGroup, _Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return multitype_join<std::tuple<_Type1, _Type2, _Types...>>(_PScheduleGroup, std::make_tuple(_Item1, _Item2, _Items...)); +} + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/// <summary> +/// Constructs a <c>non_greedy multitype_join</c> messaging block from an optional <c>Scheduler</c> +/// or <c>ScheduleGroup</c> and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>non_greedy multitype_join</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="multitype_join Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +multitype_join<std::tuple<_Type1, _Type2, _Types...>> +make_join(_Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return multitype_join<std::tuple<_Type1, _Type2, _Types...>>(std::make_tuple(_Item1, _Item2, _Items...)); +} + +// Templated factory functions that create a *greedy* join, three flavors +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/// <summary> +/// Constructs a <c>greedy multitype_join</c> messaging block from an optional <c>Scheduler</c> +/// or <c>ScheduleGroup</c> and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_PScheduler"> +/// The <c>Scheduler</c> object within which the propagation task for the <c>multitype_join</c> messaging block +/// is scheduled. +/// </param> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>greedy multitype_join</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="multitype_join Class"/> +/// <seealso cref="Scheduler Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +multitype_join<std::tuple<_Type1, _Type2, _Types...>, greedy> +make_greedy_join(Scheduler& _PScheduler, _Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return multitype_join<std::tuple<_Type1, _Type2, _Types...>, greedy>(_PScheduler, std::make_tuple(_Item1, _Item2, _Items...)); +} + +/// <summary> +/// Constructs a <c>greedy multitype_join</c> messaging block from an optional <c>Scheduler</c> +/// or <c>ScheduleGroup</c> and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_PScheduleGroup"> +/// The <c>ScheduleGroup</c> object within which the propagation task for the <c>multitype_join</c> messaging block is scheduled. +/// The <c>Scheduler</c> object used is implied by the schedule group. +/// </param> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>greedy multitype_join</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="multitype_join Class"/> +/// <seealso cref="ScheduleGroup Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +multitype_join<std::tuple<_Type1, _Type2, _Types...>, greedy> +make_greedy_join(ScheduleGroup& _PScheduleGroup, _Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return multitype_join<std::tuple<_Type1, _Type2, _Types...>, greedy>(_PScheduleGroup, std::make_tuple(_Item1, _Item2, _Items...)); +} + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/// <summary> +/// Constructs a <c>greedy multitype_join</c> messaging block from an optional <c>Scheduler</c> +/// or <c>ScheduleGroup</c> and two or more input sources. +/// </summary> +/// <typeparam name="_Type1"> +/// The message block type of the first source. +/// </typeparam> +/// <typeparam name="_Type2"> +/// The message block type of the second source. +/// </typeparam> +/// <typeparam name="_Types"> +/// The message block types of additional sources. +/// </typeparam> +/// <param name="_Item1"> +/// The first source. +/// </param> +/// <param name="_Item2"> +/// The second source. +/// </param> +/// <param name="_Items"> +/// Additional sources. +/// </param> +/// <returns> +/// A <c>greedy multitype_join</c> message block with two or more input sources. +/// </returns> +/// <seealso cref="multitype_join Class"/> +/**/ +template<typename _Type1, typename _Type2, typename... _Types> +multitype_join<std::tuple<_Type1, _Type2, _Types...>, greedy> +make_greedy_join(_Type1 _Item1, _Type2 _Item2, _Types... _Items) +{ + return multitype_join<std::tuple<_Type1, _Type2, _Types...>, greedy>(std::make_tuple(_Item1, _Item2, _Items...)); +} + +//************************************************************************** +// Agents: +//************************************************************************** + +/// <summary> +/// The valid states for an <c>agent</c>. +/// </summary> +/// <remarks> +/// For more information, see <see cref="Asynchronous Agents"/>. +/// </remarks> +/**/ +enum agent_status { + /// <summary> + /// The <c>agent</c> has been created but not started. + /// </summary> + /**/ + agent_created, + /// <summary> + /// The <c>agent</c> has been started, but not entered its <c>run</c> method. + /// </summary> + /**/ + agent_runnable, + /// <summary> + /// The <c>agent</c> has started. + /// </summary> + /**/ + agent_started, + /// <summary> + /// The <c>agent</c> finished without being canceled. + /// </summary> + /**/ + agent_done, + /// <summary> + /// The <c>agent</c> was canceled. + /// </summary> + /**/ + agent_canceled +}; + +/// <summary> +/// A class intended to be used as a base class for all independent agents. It is used to hide +/// state from other agents and interact using message-passing. +/// </summary> +/// <remarks> +/// For more information, see <see cref="Asynchronous Agents"/>. +/// </remarks> +/**/ +class agent +{ +public: + /// <summary> + /// Constructs an agent. + /// </summary> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PGroup"/> parameters. + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + _CRTIMP2 agent(); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + /// <summary> + /// Constructs an agent. + /// </summary> + /// <param name="_PScheduler"> + /// The <c>Scheduler</c> object within which the execution task of the agent is scheduled. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PGroup"/> parameters. + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + _CRTIMP2 agent(Scheduler& _PScheduler); + + /// <summary> + /// Constructs an agent. + /// </summary> + /// <param name="_PGroup"> + /// The <c>ScheduleGroup</c> object within which the execution task of the agent is scheduled. + /// The <c>Scheduler</c> object used is implied by the schedule group. + /// </param> + /// <remarks> + /// The runtime uses the default scheduler if you do not specify the <paramref name="_PScheduler"/> + /// or <paramref name="_PGroup"/> parameters. + /// </remarks> + /// <seealso cref="Scheduler Class"/> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + _CRTIMP2 agent(ScheduleGroup& _PGroup); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Destroys the agent. + /// </summary> + /// <remarks> + /// It is an error to destroy an agent that is not in a terminal state (either <c>agent_done</c> or + /// <c>agent_canceled</c>). This can be avoided by waiting for the agent to reach a terminal state + /// in the destructor of a class that inherits from the <c>agent</c> class. + /// </remarks> + /**/ + _CRTIMP2 virtual ~agent(); + + /// <summary> + /// An asynchronous source of status information from the agent. + /// </summary> + /// <returns> + /// Returns a message source that can send messages about the current state of the agent. + /// </returns> + /**/ + _CRTIMP2 ISource<agent_status> * status_port(); + + /// <summary> + /// A synchronous source of status information from the agent. + /// </summary> + /// <returns> + /// Returns the current state of the agent. Note that this returned state could change + /// immediately after being returned. + /// </returns> + /// <seealso cref="agent_status Enumeration"/> + /**/ + _CRTIMP2 agent_status status(); + + /// <summary> + /// Moves an agent from the <c>agent_created</c> state to the <c>agent_runnable</c> state, and schedules it for execution. + /// </summary> + /// <returns> + /// <c>true</c> if the agent started correctly, <c>false</c> otherwise. An agent that has been canceled cannot be started. + /// </returns> + /// <seealso cref="agent_status Enumeration"/> + /**/ + _CRTIMP2 bool start(); + + /// <summary> + /// Moves an agent from either the <c>agent_created</c> or <c>agent_runnable</c> states to the <c>agent_canceled</c> state. + /// </summary> + /// <returns> + /// <c>true</c> if the agent was canceled, <c>false</c> otherwise. An agent cannot be canceled if it has already started + /// running or has already completed. + /// </returns> + /// <seealso cref="agent_status Enumeration"/> + /**/ + _CRTIMP2 bool cancel(); + + /// <summary> + /// Waits for an agent to complete its task. + /// </summary> + /// <param name="_PAgent"> + /// A pointer to the agent to wait for. + /// </param> + /// <param name="_Timeout"> + /// The maximum time for which to wait, in milliseconds. + /// </param> + /// <returns> + /// The <c>agent_status</c> of the agent when the wait completes. This can either be <c>agent_canceled</c> + /// or <c>agent_done</c>. + /// </returns> + /// <remarks> + /// An agent task is completed when the agent enters the <c>agent_canceled</c> or <c>agent_done</c> states. + /// <para>If the parameter <paramref name="_Timeout"/> has a value other than the constant <c>COOPERATIVE_TIMEOUT_INFINITE</c>, + /// the exception <see cref="operation_timed_out Class">operation_timed_out</see> is thrown if the specified amount + /// of time expires before the agent has completed its task.</para> + /// </remarks> + /// <seealso cref="agent::wait_for_all Method"/> + /// <seealso cref="agent::wait_for_one Method"/> + /// <seealso cref="agent_status Enumeration"/> + /**/ + _CRTIMP2 static agent_status __cdecl wait(_Inout_ agent * _PAgent, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE); + + /// <summary> + /// Waits for all of the specified agents to complete their tasks. + /// </summary> + /// <param name="_Count"> + /// The number of agent pointers present in the array <paramref name="_PAgents"/>. + /// </param> + /// <param name="_PAgents"> + /// An array of pointers to the agents to wait for. + /// </param> + /// <param name="_PStatus"> + /// A pointer to an array of agent statuses. Each status value will represent the status of the corresponding + /// agent when the method returns. + /// </param> + /// <param name="_Timeout"> + /// The maximum time for which to wait, in milliseconds. + /// </param> + /// <remarks> + /// An agent task is completed when the agent enters the <c>agent_canceled</c> or <c>agent_done</c> states. + /// <para>If the parameter <paramref name="_Timeout"/> has a value other than the constant <c>COOPERATIVE_TIMEOUT_INFINITE</c>, + /// the exception <see cref="operation_timed_out Class">operation_timed_out</see> is thrown if the specified amount + /// of time expires before the agent has completed its task.</para> + /// </remarks> + /// <seealso cref="agent::wait Method"/> + /// <seealso cref="agent::wait_for_one Method"/> + /// <seealso cref="agent_status Enumeration"/> + /**/ + _CRTIMP2 static void __cdecl wait_for_all(size_t _Count, _In_reads_(_Count) agent ** _PAgents, + _Out_writes_opt_(_Count) agent_status * _PStatus = NULL, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE); + + /// <summary> + /// Waits for any one of the specified agents to complete its task. + /// </summary> + /// <param name="_Count"> + /// The number of agent pointers present in the array <paramref name="_PAgents"/>. + /// </param> + /// <param name="_PAgents"> + /// An array of pointers to the agents to wait for. + /// </param> + /// <param name="_Status"> + /// A reference to a variable where the agent status will be placed. + /// </param> + /// <param name="_Index"> + /// A reference to a variable where the agent index will be placed. + /// </param> + /// <param name="_Timeout"> + /// The maximum time for which to wait, in milliseconds. + /// </param> + /// <remarks> + /// An agent task is completed when the agent enters the <c>agent_canceled</c> or <c>agent_done</c> states. + /// <para>If the parameter <paramref name="_Timeout"/> has a value other than the constant <c>COOPERATIVE_TIMEOUT_INFINITE</c>, + /// the exception <see cref="operation_timed_out Class">operation_timed_out</see> is thrown if the specified amount + /// of time expires before the agent has completed its task.</para> + /// </remarks> + /// <seealso cref="agent::wait Method"/> + /// <seealso cref="agent::wait_for_all Method"/> + /// <seealso cref="agent_status Enumeration"/> + /**/ + _CRTIMP2 static void __cdecl wait_for_one(size_t _Count, _In_reads_(_Count) agent ** _PAgents, agent_status& _Status, + size_t& _Index, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE); + +protected: + /// <summary> + /// Represents the main task of an agent. <c>run</c> should be overridden in a derived class, and specifies what + /// the agent should do after it has been started. + /// </summary> + /// <remarks> + /// The agent status is changed to <c>agent_started</c> right before this method is invoked. The method should + /// invoke <c>done</c> on the agent with an appropriate status before returning, and may not throw any + /// exceptions. + /// </remarks> + /**/ + virtual void run() = 0; + + /// <summary> + /// Moves an agent into the <c>agent_done</c> state, indicating that the agent has completed. + /// </summary> + /// <returns> + /// <c>true</c> if the agent is moved to the <c>agent_done</c> state, <c>false</c> otherwise. An agent that has + /// been canceled cannot be moved to the <c>agent_done</c> state. + /// </returns> + /// <remarks> + /// This method should be called at the end of the <c>run</c> method, when you know the execution of your agent + /// has completed. + /// </remarks> + /// <seealso cref="agent_status Enumeration"/> + /**/ + _CRTIMP2 bool done(); + + /// <summary> + /// Holds the current status of the agent. + /// </summary> + /**/ + overwrite_buffer<agent_status> _M_status; + +private: + + // A flag to check of whether the agent can be started + // This is initialized to TRUE and there is a race between Start() and Cancel() to set it + // to FALSE. Once Started or Canceled, further calls to Start() or Cancel() will return false. + /**/ + volatile long _M_fStartable; + + // A flag to check of whether the agent can be canceled + // This is initailized to TRUE and there is a race between Cancel() and the LWT executing + // a task that has been started to set it to FALSE. If Cancel() wins, the task will not be + // executed. If the LWT wins, Cancel() will return false. + /**/ + volatile long _M_fCancelable; + + // A static wrapper function that calls the Run() method. Used for scheduling of the task + /**/ + static void __cdecl _Agent_task_wrapper(void * data); + + Scheduler * _M_pScheduler; + ScheduleGroup * _M_pScheduleGroup; + + // + // Hide assignment operator and copy constructor + // + agent const &operator =(agent const&); // no assignment operator + agent(agent const &); // no copy constructor +}; + +//************************************************************************** +// Direct Messaging APIs: +//************************************************************************** + +/// <summary> +/// A general receive implementation, allowing a context to wait for data from +/// exactly one source and filter the values that are accepted. If the specified timeout is not +/// COOPERATIVE_TIMEOUT_INFINITE, an exception (operation_timed_out) will be thrown if the specified amount +/// of time expires before a message is received. Note that zero length timeouts should likely use +/// try_receive as opposed to receive with a timeout of zero as it is more efficient and does not +/// throw exceptions on timeouts. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/// <param name="_Src"> +/// A pointer to the source from which data is expected. +/// </param> +/// <param name="_Timeout"> +/// The maximum time for which the method should for the data, in milliseconds. +/// </param> +/// <param name="_Filter_proc"> +/// A pointer to a filter which will indicate whether to accept the data or not. +/// </param> +/// <returns> +/// A value from the source, of the payload type. +/// </returns> +/**/ +template <class _Type> +_Type _Receive_impl(ISource<_Type> * _Src, unsigned int _Timeout, typename ITarget<_Type>::filter_method const* _Filter_proc) +{ + // The Blocking Recipient messaging block class is internal to the receive function + class _Blocking_recipient : public ITarget<_Type> + { + public: + // Create an Blocking Recipient + _Blocking_recipient(ISource<_Type> * _PSource, + unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE) : + _M_pFilter(NULL), _M_pConnectedTo(NULL), _M_pMessage(NULL), _M_fState(_NotInitialized), _M_timeout(_Timeout) + { + _Connect(_PSource); + } + + // Create an Blocking Recipient + _Blocking_recipient(ISource<_Type> * _PSource, + filter_method const& _Filter, + unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE) : + _M_pFilter(NULL), _M_pConnectedTo(NULL), _M_pMessage(NULL), _M_fState(_NotInitialized), _M_timeout(_Timeout) + { + if (_Filter != NULL) + { + _M_pFilter = new filter_method(_Filter); + } + + _Connect(_PSource); + } + + // Cleans up any resources that may have been created by the BlockingRecipient. + ~_Blocking_recipient() + { + _Disconnect(); + + delete _M_pFilter; + delete _M_pMessage; + } + + // Gets the value of the message sent to this BlockingRecipient. Blocks by + // spinning until a message has arrived. + _Type _Value() + { + _Wait_for_message(); + + return _M_pMessage->payload; + } + + // The main propagation function for ITarget blocks. Called by a source + // block, generally within an asynchronous task to send messages to its targets. + virtual message_status propagate(message<_Type> * _PMessage, ISource<_Type> * _PSource) + { + // Throw exception if the message being propagated to this block is NULL + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + // Reject if the recipient has already received a message + if (_M_fState == _Initialized) + { + return declined; + } + + // Reject if the message does not meet the filter requirements + if (_M_pFilter != NULL && !(*_M_pFilter)(_PMessage->payload)) + { + return declined; + } + + // Accept the message + _CONCRT_ASSERT(_PSource != NULL); + _M_pMessage = _PSource->accept(_PMessage->msg_id(), this); + + if (_M_pMessage != NULL) + { + // Set the initialized flag on this block + if (_InterlockedExchange(&_M_fState, _Initialized) == _Blocked) + { + _M_ev.set(); + } + + return accepted; + } + + return missed; + } + + // Synchronously sends a message to this block. When this function completes the message will + // already have propagated into the block. + virtual message_status send(message<_Type> * _PMessage, ISource<_Type> * _PSource) + { + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + // Only the connected source is allowed to send messages + // to the blocking recepient. Decline messages without + // a source. + + return declined; + } + + private: + + // Link a source block + virtual void link_source(ISource<_Type> * _PSrc) + { + _M_pConnectedTo = _PSrc; + _PSrc->acquire_ref(this); + } + + // Remove a source messaging block for this BlockingRecipient + virtual void unlink_source(ISource<_Type> * _PSource) + { + if (_InterlockedCompareExchangePointer(reinterpret_cast<void *volatile *>(&_M_pConnectedTo), (void *)NULL, _PSource) == _PSource) + { + _PSource->release_ref(this); + } + } + + // Remove the source messaging block for this BlockingRecipient + virtual void unlink_sources() + { + ISource<_Type> * _PSource = reinterpret_cast<ISource<_Type> *>(_InterlockedExchangePointer(reinterpret_cast<void *volatile *>(&_M_pConnectedTo), (void *)NULL)); + if (_PSource != NULL) + { + _PSource->unlink_target(this); + _PSource->release_ref(this); + } + } + + + // Connect the blocking recipient to the source + void _Connect(ISource<_Type> * _PSource) + { + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + _PSource->link_target(this); + } + + // Cleanup the connection to the blocking recipient's source. There is no need + // to do anything about the associated context. + void _Disconnect() + { + unlink_sources(); + } + + // Internal function used to block while waiting for a message to arrive + // at this BlockingRecipient + void _Wait_for_message() + { + bool _Timeout = false; + + // If we haven't received a message yet, cooperatively block. + if (_InterlockedCompareExchange(&_M_fState, _Blocked, _NotInitialized) == _NotInitialized) + { + if (_M_ev.wait(_M_timeout) == COOPERATIVE_WAIT_TIMEOUT) + { + _Timeout = true; + } + } + + // Unlinking from our source guarantees that there are no threads in propagate + _Disconnect(); + + if (_M_fState != _Initialized) + { + // We had to have timed out if we came out of the wait + // without being initialized. + _CONCRT_ASSERT(_Timeout); + + throw operation_timed_out(); + } + } + + // States for this block + enum + { + _NotInitialized, + _Blocked, + _Initialized + }; + + volatile long _M_fState; + + // The source messaging block connected to this Recipient + ISource<_Type> * _M_pConnectedTo; + + // The message that was received + message<_Type> * volatile _M_pMessage; + + // The timeout. + unsigned int _M_timeout; + + // The event we wait upon + event _M_ev; + + // The filter that is called on this block before accepting a message + filter_method * _M_pFilter; + }; + + if (_Filter_proc != NULL) + { + _Blocking_recipient _Recipient(_Src, *_Filter_proc, _Timeout); + return _Recipient._Value(); + } + else + { + _Blocking_recipient _Recipient(_Src, _Timeout); + return _Recipient._Value(); + } +} + +/// <summary> +/// A general receive implementation, allowing a context to wait for data from +/// exactly one source and filter the values that are accepted. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_Timeout"> +/// The maximum time for which the method should for the data, in milliseconds. +/// </param> +/// <returns> +/// A value from the source, of the payload type. +/// </returns> +/// <remarks> +/// If the parameter <paramref name="_Timeout"/> has a value other than the constant <c>COOPERATIVE_TIMEOUT_INFINITE</c>, +/// the exception <see cref="operation_timed_out Class">operation_timed_out</see> is thrown if the specified amount +/// of time expires before a message is received. If you want a zero length timeout, you should use the +/// <see cref="try_receive Function">try_receive</see> function, as opposed to calling <c>receive</c> with a timeout +/// of <c>0</c> (zero), as it is more efficient and does not throw exceptions on timeouts. +/// <para>For more information, see <see cref="Message Passing Functions"/>.</para> +/// </remarks> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +_Type receive(_Inout_ ISource<_Type> * _Src, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE) +{ + return _Receive_impl(_Src, _Timeout, NULL); +} + +/// <summary> +/// A general receive implementation, allowing a context to wait for data from +/// exactly one source and filter the values that are accepted. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_Filter_proc"> +/// A filter function which determines whether messages should be accepted. +/// </param> +/// <param name="_Timeout"> +/// The maximum time for which the method should for the data, in milliseconds. +/// </param> +/// <returns> +/// A value from the source, of the payload type. +/// </returns> +/// <remarks> +/// If the parameter <paramref name="_Timeout"/> has a value other than the constant <c>COOPERATIVE_TIMEOUT_INFINITE</c>, +/// the exception <see cref="operation_timed_out Class">operation_timed_out</see> is thrown if the specified amount +/// of time expires before a message is received. If you want a zero length timeout, you should use the +/// <see cref="try_receive Function">try_receive</see> function, as opposed to calling <c>receive</c> with a timeout +/// of <c>0</c> (zero), as it is more efficient and does not throw exceptions on timeouts. +/// <para>For more information, see <see cref="Message Passing Functions"/>.</para> +/// </remarks> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +_Type receive(_Inout_ ISource<_Type> * _Src, typename ITarget<_Type>::filter_method const& _Filter_proc, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE) +{ + return _Receive_impl(_Src, _Timeout, &_Filter_proc); +} + +/// <summary> +/// A general receive implementation, allowing a context to wait for data from +/// exactly one source and filter the values that are accepted. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_Timeout"> +/// The maximum time for which the method should for the data, in milliseconds. +/// </param> +/// <returns> +/// A value from the source, of the payload type. +/// </returns> +/// <remarks> +/// If the parameter <paramref name="_Timeout"/> has a value other than the constant <c>COOPERATIVE_TIMEOUT_INFINITE</c>, +/// the exception <see cref="operation_timed_out Class">operation_timed_out</see> is thrown if the specified amount +/// of time expires before a message is received. If you want a zero length timeout, you should use the +/// <see cref="try_receive Function">try_receive</see> function, as opposed to calling <c>receive</c> with a timeout +/// of <c>0</c> (zero), as it is more efficient and does not throw exceptions on timeouts. +/// <para>For more information, see <see cref="Message Passing Functions"/>.</para> +/// </remarks> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +_Type receive(ISource<_Type> &_Src, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE) +{ + return _Receive_impl(&_Src, _Timeout, NULL); +} + +/// <summary> +/// A general receive implementation, allowing a context to wait for data from +/// exactly one source and filter the values that are accepted. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_Filter_proc"> +/// A filter function which determines whether messages should be accepted. +/// </param> +/// <param name="_Timeout"> +/// The maximum time for which the method should for the data, in milliseconds. +/// </param> +/// <returns> +/// A value from the source, of the payload type. +/// </returns> +/// <remarks> +/// If the parameter <paramref name="_Timeout"/> has a value other than the constant <c>COOPERATIVE_TIMEOUT_INFINITE</c>, +/// the exception <see cref="operation_timed_out Class">operation_timed_out</see> is thrown if the specified amount +/// of time expires before a message is received. If you want a zero length timeout, you should use the +/// <see cref="try_receive Function">try_receive</see> function, as opposed to calling <c>receive</c> with a timeout +/// of <c>0</c> (zero), as it is more efficient and does not throw exceptions on timeouts. +/// <para>For more information, see <see cref="Message Passing Functions"/>.</para> +/// </remarks> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +_Type receive(ISource<_Type> &_Src, typename ITarget<_Type>::filter_method const& _Filter_proc, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE) +{ + return _Receive_impl(&_Src, _Timeout, &_Filter_proc); +} + +/// <summary> +/// Helper function that implements try_receive +/// A general try-receive implementation, allowing a context to look for data from +/// exactly one source and filter the values that are accepted. If the data is not +/// ready, try_receive will return false. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/// <param name="_Src"> +/// A pointer to the source from which data is expected. +/// </param> +/// <param name="_value"> +/// A reference to a location where the result will be placed. +/// </param> +/// <param name="_Filter_proc"> +/// A pointer to a filter which will indicate whether to accept the data or not. +/// </param> +/// <returns> +/// A bool indicating whether a payload was placed in <paramref name="_value"/> or not. +/// </returns> +/**/ +template <class _Type> +bool _Try_receive_impl(ISource<_Type> * _Src, _Type & _value, typename ITarget<_Type>::filter_method const * _Filter_proc) +{ + // The Immediate Recipient messaging block class is internal to the receive function + class _Immediate_recipient : public ITarget<_Type> + { + public: + // Create an Immediate Recipient + _Immediate_recipient(ISource<_Type> * _PSource) : + _M_pFilter(NULL), _M_pConnectedTo(NULL), _M_pMessage(NULL), _M_isInitialized(0) + { + _Connect(_PSource); + } + + // Create an Immediate Recipient + _Immediate_recipient(ISource<_Type> * _PSource, + filter_method const& _Filter) : + _M_pFilter(NULL), _M_pConnectedTo(NULL), _M_pMessage(NULL), _M_isInitialized(0) + { + if (_Filter != NULL) + { + _M_pFilter = new filter_method(_Filter); + } + + _Connect(_PSource); + } + + // Cleans up any resources that may have been created by the ImmediateRecipient. + ~_Immediate_recipient() + { + _Disconnect(); + + delete _M_pFilter; + delete _M_pMessage; + } + + // Gets the value of the message sent to this ImmediateRecipient. + bool _Value(_Type & _value) + { + // Unlinking from our source guarantees that there are no threads in propagate + _Disconnect(); + + if (_M_pMessage != NULL) + { + _value = _M_pMessage->payload; + return true; + } + + return false; + } + + // The main propagation function for ITarget blocks. Called by a source + // block, generally within an asynchronous task to send messages to its targets. + virtual message_status propagate(message<_Type> * _PMessage, ISource<_Type> * _PSource) + { + message_status _Result = accepted; + + // Throw exception if the message being propagated to this block is NULL + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + // Reject if the recipient has already received a message + if (_M_isInitialized == 1) + { + return declined; + } + + // Reject if the message does not meet the filter requirements + if (_M_pFilter != NULL && !(*_M_pFilter)(_PMessage->payload)) + { + return declined; + } + + // Accept the message + _CONCRT_ASSERT(_PSource != NULL); + _M_pMessage = _PSource->accept(_PMessage->msg_id(), this); + + // Set the initialized flag on this block + + if (_M_pMessage != NULL) + { + // Fence to ensure that the above update to _M_pMessage is visible + _InterlockedExchange(&_M_isInitialized, 1); + _Result = accepted; + } + else + { + _Result = missed; + } + + return _Result; + } + + + // Synchronously sends a message to this block. When this function completes the message will + // already have propagated into the block. + virtual message_status send(message<_Type> * _PMessage, ISource<_Type> * _PSource) + { + if (_PMessage == NULL) + { + throw std::invalid_argument("_PMessage"); + } + + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + // Only the connected source is allowed to send messages + // to the blocking recepient. Decline messages without + // a source. + + return declined; + } + + private: + + // Add a source messaging block + virtual void link_source(ISource<_Type> * _PSrc) + { + _M_pConnectedTo = _PSrc; + _PSrc->acquire_ref(this); + } + + // Remove a source messaging block for this BlockingRecipient + virtual void unlink_source(ISource<_Type> * _PSource) + { + if (_InterlockedCompareExchangePointer(reinterpret_cast<void *volatile *>(&_M_pConnectedTo), (void *)NULL, _PSource) == _PSource) + { + _PSource->release_ref(this); + } + } + + // Remove the source messaging block for this BlockingRecipient + virtual void unlink_sources() + { + ISource<_Type> * _PSource = reinterpret_cast<ISource<_Type> *>(_InterlockedExchangePointer(reinterpret_cast<void *volatile *>(&_M_pConnectedTo), (void *)NULL)); + if (_PSource != NULL) + { + _PSource->unlink_target(this); + _PSource->release_ref(this); + } + } + + // Connect to a source block + void _Connect(ISource<_Type> * _PSource) + { + if (_PSource == NULL) + { + throw std::invalid_argument("_PSource"); + } + + _CONCRT_ASSERT(_M_isInitialized == 0); + + _PSource->link_target(this); + } + + // + // Cleanup the connection to the trigger's source. There is no need + // to do anything about the associated context. + // + void _Disconnect() + { + unlink_sources(); + } + + // The source messaging block connected to this Recipient + ISource<_Type> * _M_pConnectedTo; + + // The message that was received + message<_Type> * volatile _M_pMessage; + + // A flag for whether or not this block has been initialized with a value + volatile long _M_isInitialized; + + // The filter that is called on this block before accepting a message + filter_method * _M_pFilter; + }; + + if (_Filter_proc != NULL) + { + _Immediate_recipient _Recipient(_Src, *_Filter_proc); + return _Recipient._Value(_value); + } + else + { + _Immediate_recipient _Recipient(_Src); + return _Recipient._Value(_value); + } +} + +/// <summary> +/// A general try-receive implementation, allowing a context to look for data from +/// exactly one source and filter the values that are accepted. If the data is not +/// ready, the method will return false. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_value"> +/// A reference to a location where the result will be placed. +/// </param> +/// <returns> +/// A <c>bool</c> value indicating whether or not a payload was placed in <paramref name="_value"/>. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +bool try_receive(_Inout_ ISource<_Type> * _Src, _Type & _value) +{ + return _Try_receive_impl(_Src, _value, NULL); +} + +/// <summary> +/// A general try-receive implementation, allowing a context to look for data from +/// exactly one source and filter the values that are accepted. If the data is not +/// ready, the method will return false. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_value"> +/// A reference to a location where the result will be placed. +/// </param> +/// <param name="_Filter_proc"> +/// A filter function which determines whether messages should be accepted. +/// </param> +/// <returns> +/// A <c>bool</c> value indicating whether or not a payload was placed in <paramref name="_value"/>. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +bool try_receive(_Inout_ ISource<_Type> * _Src, _Type & _value, typename ITarget<_Type>::filter_method const& _Filter_proc) +{ + return _Try_receive_impl(_Src, _value, &_Filter_proc); +} + +/// <summary> +/// A general try-receive implementation, allowing a context to look for data from +/// exactly one source and filter the values that are accepted. If the data is not +/// ready, the method will return false. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_value"> +/// A reference to a location where the result will be placed. +/// </param> +/// <returns> +/// A <c>bool</c> value indicating whether or not a payload was placed in <paramref name="_value"/>. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +bool try_receive(ISource<_Type> & _Src, _Type & _value) +{ + return _Try_receive_impl(&_Src, _value, NULL); +} + +/// <summary> +/// A general try-receive implementation, allowing a context to look for data from +/// exactly one source and filter the values that are accepted. If the data is not +/// ready, the method will return false. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type +/// </typeparam> +/// <param name="_Src"> +/// A pointer or reference to the source from which data is expected. +/// </param> +/// <param name="_value"> +/// A reference to a location where the result will be placed. +/// </param> +/// <param name="_Filter_proc"> +/// A filter function which determines whether messages should be accepted. +/// </param> +/// <returns> +/// A <c>bool</c> value indicating whether or not a payload was placed in <paramref name="_value"/>. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="send Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +bool try_receive(ISource<_Type> & _Src, _Type & _value, typename ITarget<_Type>::filter_method const& _Filter_proc) +{ + return _Try_receive_impl(&_Src, _value, &_Filter_proc); +} + +namespace details +{ + //************************************************************************** + // Supporting blocks for send and asend + //************************************************************************** + + // Originator block that pushes messages to a target + template <class _Type> + class _AnonymousOriginator : public ISource<_Type> + { + public: + + typedef single_link_registry<ITarget<_Type>> _Target_registry; + + // Create an Originator + _AnonymousOriginator() : _M_pMessage(NULL), _M_pTarget(NULL) + { + } + + // Cleans up any resources that may have been created by the Originator. + virtual ~_AnonymousOriginator() + { + delete _M_pMessage; + } + + // Removes a target messaging block for this Originator + virtual void unlink_target(ITarget<_Type> * _PTarget) + { + throw invalid_operation("unlink_target is not supported on _AnonymousOriginator"); + } + + // Removes the target messaging block from this Originator + virtual void unlink_targets() + { + throw invalid_operation("unlink_targets is not supported on _AnonymousOriginator"); + } + + // Accept on this Originator is called by a target to take ownership of a + // propagated message + virtual message<_Type> * accept(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget != _M_pTarget) + { + return NULL; + } + + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + return NULL; + } + + // The IDs match, actaully transfer ownership of the message and + // unlink away from the target + message<_Type> * _Result = _M_pMessage; + + // The ownership of this message has changed. Set the internal pointer to NULL + // so it won't be deleted in the destructor + _M_pMessage = NULL; + + return _Result; + } + + // Reserve shall not be called by blocks that supports push + virtual bool reserve(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + throw invalid_operation("reserve is not supported on _AnonymousOriginator"); + } + + // Consume shall not be called by blocks that supports push + virtual message<_Type> * consume(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + throw invalid_operation("consume is not supported on _AnonymousOriginator"); + } + + // Release needs to be defined for ISource blocks, but Originator doesn't need to + // do anything for reservation release because there can only be one target block to read + // the data at a later time. + virtual void release(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + throw invalid_operation("release is not supported on _AnonymousOriginator"); + } + + virtual void acquire_ref(_Inout_ ITarget<_Type> *) + { + throw invalid_operation("acquire_ref is not supported on _AnonymousOriginator"); + } + + virtual void release_ref(_Inout_ ITarget<_Type> *) + { + throw invalid_operation("release_ref is not supported on _AnonymousOriginator"); + } + + private: + friend class _Originator; + + // Send the given value to the target + bool _internal_send(ITarget<_Type> * _PTarget, _Type const & _Value) + { + _M_pTarget = _PTarget; + + _CONCRT_ASSERT(_M_pTarget != NULL); + _CONCRT_ASSERT(_M_pTarget->supports_anonymous_source()); + + // Create the message + message_status _Status = declined; + message<_Type> * _Msg = new message<_Type>(_Value); + + _CONCRT_ASSERT(_M_pMessage == NULL); + _M_pMessage = _Msg; + + // Send the message + _Status = _M_pTarget->send(_M_pMessage, this); + + // If the message is declined, the destructor will + // delete the message + + // status should not be postponed. + _CONCRT_ASSERT(_Status != postponed); + return (_Status == accepted); + } + + bool _internal_asend(ITarget<_Type> * _PTarget, _Type const & _Value) + { + _M_pTarget = _PTarget; + + _CONCRT_ASSERT(_M_pTarget != NULL); + _CONCRT_ASSERT(_M_pTarget->supports_anonymous_source()); + + // Create the message + message_status _Status = declined; + message<_Type> * _Msg = new message<_Type>(_Value); + + _CONCRT_ASSERT(_M_pMessage == NULL); + _M_pMessage = _Msg; + + // Send the message + _Status = _M_pTarget->propagate(_M_pMessage, this); + + // If the message is declined, the destructor will + // delete the message + + // status should not be postponed. + if (_Status == postponed) + { + throw invalid_operation("Messages offered by _AnonymousOriginator shall not be postponed"); + } + + return (_Status == accepted); + } + + // Add a target messaging block for this Originator + virtual void link_target(ITarget<_Type> * _PTarget) + { + throw invalid_operation("link_target is not supported on _AnonymousOriginator"); + } + + // The message that will be propagated by the Originator + message<_Type> * _M_pMessage; + + // The single target for this block + ITarget<_Type> * _M_pTarget; + }; + + // The Originator messaging block class is internal to the send function. + template <class _Type> + class _SyncOriginator : public ISource<_Type> + { + public: + + typedef single_link_registry<ITarget<_Type>> _Target_registry; + + // Create an Originator + _SyncOriginator() : + _M_pMessage(NULL), + _M_fStatus(postponed), + _M_referenceCount(0) + { + } + + // Cleans up any resources that may have been created by the Originator. + virtual ~_SyncOriginator() + { + unlink_targets(); + + _Wait_on_ref(); + + delete _M_pMessage; + } + + // Removes a target messaging block for this Originator + virtual void unlink_target(ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + { + // Hold the lock to ensure that the target doesn't unlink while + // propagation is in progress. + _R_lock _Lock(_M_internalLock); + if (_M_connectedTargets.remove(_PTarget)) + { + _Invoke_unlink_source(_PTarget); + + // Indicate that the send is complete + _Done(declined); + } + } + } + + // Removes the target messaging block from this Originator + virtual void unlink_targets() + { + // Hold the lock to ensure that the target doesn't unlink while + // propagation is in progress. + _R_lock _Lock(_M_internalLock); + + for (_Target_registry::iterator _Iter = _M_connectedTargets.begin(); *_Iter != NULL; ++_Iter) + { + ITarget<_Type> * _PTarget = *_Iter; + if (_M_connectedTargets.remove(_PTarget)) + { + _Invoke_unlink_source(_PTarget); + } + } + + // All targets should be unlinked + _CONCRT_ASSERT(_M_connectedTargets.count() == 0); + + // Indicate that the send is complete + _Done(declined); + } + + // Accept on this Originator is called by a target to take ownership of a + // propagated message + virtual message<_Type> * accept(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + return NULL; + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + return NULL; + } + + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + return NULL; + } + + // The IDs match, actaully transfer ownership of the message and + // unlink away from the target + message<_Type> * _Result = _M_pMessage; + + // The ownership of this message has changed. Set the internal pointer to NULL + // so it won't be deleted in the destructor + _M_pMessage = NULL; + + // The message has been accepted/consumed, propagate indication that it has succeeded + _Done(accepted); + + return _Result; + } + + // Reserve needs to be defined for ISource blocks, but Originator doesn't need to + // do anything for reservation because there can only be one target block to read + // the data at a later time. + virtual bool reserve(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + return false; + } + + if (_M_pMessage->msg_id() != _MsgId) + { + return false; + } + + return true; + } + + // Consume is called by a target messaging block to take ownership of a + // previously reserved message. + virtual message<_Type> * consume(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + throw bad_target(); + } + + return accept(_MsgId, _PTarget); + } + + // Release needs to be defined for ISource blocks, but Originator doesn't need to + // do anything for reservation release because there can only be one target block to read + // the data at a later time. + virtual void release(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + throw bad_target(); + } + + if ((_M_pMessage == NULL) || (_M_pMessage->msg_id() != _MsgId)) + { + throw message_not_found(); + } + + // If the previously reserved message is released, then propagate + // declined to indicate that the message was not accepted. + _Done(declined); + } + + virtual void acquire_ref(_Inout_ ITarget<_Type> *) + { + _InterlockedIncrement(&_M_referenceCount); + } + + virtual void release_ref(_Inout_ ITarget<_Type> *) + { + _InterlockedDecrement(&_M_referenceCount); + } + + private: + + friend class _Originator; + + // Send the given value to the target + bool _internal_send(ITarget<_Type> * _PTarget, _Type const & _Value) + { + // _send should only be called once. + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + message_status _Status = declined; + message<_Type> * _Msg = new message<_Type>(_Value); + + { + // Hold the lock to ensure that the target doesn't unlink while + // propagation is in progress. + _R_lock _Lock(_M_internalLock); + + // link to the target, create a message and send it + link_target(_PTarget); + + _CONCRT_ASSERT(_M_pMessage == NULL); + _M_pMessage = _Msg; + + // Send the message synchronously to the target + _Status = _PTarget->send(_M_pMessage, this); + } + + if (_Status == postponed) + { + // If the target postponed the message, wait for it to + // be accepted/declined. + _Wait_for_completion(); + + // Procure the final status + _Status = _M_fStatus; + } + + // status should not be postponed. + _CONCRT_ASSERT(_Status != postponed); + + return (_Status == accepted); + } + + // Add a target messaging block for this Originator + virtual void link_target(ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + _M_connectedTargets.add(_PTarget); + _Invoke_link_source(_PTarget); + + // There should be no pending messages to propagate at this time. + _CONCRT_ASSERT(_M_pMessage == NULL); + } + + // Wait for the status to reach one of the terminal + // states (!= postponed) + void _Wait_for_completion() + { + // Wait for the event to be signalled + _M_ev.wait(COOPERATIVE_TIMEOUT_INFINITE); + _CONCRT_ASSERT(_M_fStatus != postponed); + + } + + void _Wait_on_ref() + { + ::Concurrency::details::_SpinWaitBackoffNone spinWait; + while(_M_referenceCount != 0) + { + spinWait._SpinOnce(); + } + } + + // Indicate that the send operation has completed + void _Done(message_status _Status) + { + // postponed is not a done state + _CONCRT_ASSERT(_Status != postponed); + + _M_fStatus = _Status; + _M_ev.set(); + } + + // The message that will be propagated by the Originator + message<_Type> * _M_pMessage; + + // Event to indicate completion + event _M_ev; + + // Final status of the send + volatile message_status _M_fStatus; + + // A lock for modifying the buffer or the connected blocks + ::Concurrency::details::_ReentrantPPLLock _M_internalLock; + + // Connected targets + _Target_registry _M_connectedTargets; + + volatile long _M_referenceCount; + }; + + // The Originator messaging block class is internal to the send function. + template <class _Type> + class _AsyncOriginator : public ISource<_Type> + { + public: + + typedef single_link_registry<ITarget<_Type>> _Target_registry; + + // Cleans up any resources that may have been created by the AsyncOriginator. + virtual ~_AsyncOriginator() + { + unlink_targets(); + + delete _M_pMessage; + } + + // Removes a target messaging block for this AsyncOriginator + virtual void unlink_target(ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + bool _Unlinked = false; + { + // Hold the lock to ensure that the target doesn't unlink while + // propagation is in progress. + _R_lock _Lock(_M_internalLock); + + if (_M_connectedTargets.remove(_PTarget)) + { + _Invoke_unlink_source(_PTarget); + _Unlinked = true; + } + } + + // Release the lock before decrementing the refcount. Otherwise, the + // lock release could corrupt memory. + if (_Unlinked) + { + _Release_ref(); + } + } + + // Removes the target messaging block from this AsyncOriginator + virtual void unlink_targets() + { + bool _Unlinked = false; + { + // Hold the lock to ensure that the target doesn't unlink while + // propagation is in progress. + _R_lock _Lock(_M_internalLock); + + for (_Target_registry::iterator _Iter = _M_connectedTargets.begin(); + *_Iter != NULL; + ++_Iter) + { + ITarget<_Type> * _PTarget = *_Iter; + if (_M_connectedTargets.remove(_PTarget)) + { + _Invoke_unlink_source(_PTarget); + _Unlinked = true; + } + + } + + // All targets should be unlinked + _CONCRT_ASSERT(_M_connectedTargets.count() == 0); + } + + // Release the lock before decrementing the refcount. Otherwise, the + // lock release could corrupt memory. + if (_Unlinked) + { + _Release_ref(); + } + } + + // Accept on this AsyncOriginator is called by a target to take ownership of a + // propagated message. This can only be called from propagate. + virtual message<_Type> * accept(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + return NULL; + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + return NULL; + } + + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + return NULL; + } + + // + // If the IDs match, actaully transfer ownership of the message. + // + message<_Type> * _Result = _M_pMessage; + _M_pMessage = NULL; + + return _Result; + } + + // Reserve needs to be defined for ISource blocks, but AsyncOriginator doesn't need to + // do anything for reservation because there can only be one target block to read + // the data at a later time. + virtual bool reserve(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + return false; + } + + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + return false; + } + + return true; + } + + // Consume is called by a target messaging block to take ownership of a + // previously reserved message. + virtual message<_Type> * consume(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + throw bad_target(); + } + + if (_M_pMessage == NULL || _M_pMessage->msg_id() != _MsgId) + { + return NULL; + } + + // The ownership of this message has changed. Set the internal pointer to NULL + // so it won't be deleted in the destructor + + message<_Type> * _Result = _M_pMessage; + _M_pMessage = NULL; + + // We are done. Unlink from the target. DO NOT TOUCH "this" pointer after unlink + unlink_target(_PTarget); + + return _Result; + } + + // Release needs to be defined for ISource blocks, but AsyncOriginator doesn't need to + // do anything for reservation release because there can only be one target block to read + // the data at a later time. + virtual void release(runtime_object_identity _MsgId, ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + if (!_M_connectedTargets.contains(_PTarget)) + { + throw bad_target(); + } + + if ((_M_pMessage == NULL) || (_M_pMessage->msg_id() != _MsgId)) + { + throw message_not_found(); + } + + // We can be connected to only 1 target. Unlink from the target. + // DO NOT TOUCH "this" pointer after unlink + unlink_target(_PTarget); + } + + virtual void acquire_ref(_Inout_ ITarget<_Type> *) + { + _Acquire_ref(); + } + + virtual void release_ref(_Inout_ ITarget<_Type> *) + { + _Release_ref(); + } + + private: + + friend class _Originator; + + // Create an AsyncOriginator (constructor is private to ensure that + // it is allocated on the heap). + _AsyncOriginator() : + _M_pMessage(NULL), + _M_refcount(0) + { + } + + // Send the given value to the target + bool _internal_send(ITarget<_Type> * _PTarget, _Type const & _Value) + { + // Keep a refcount so that this object doesn't get deleted if + // the target decides to unlink before we release our lock + _Acquire_ref(); + + message_status _Status = declined; + message<_Type> * _Msg = new message<_Type>(_Value); + + { + // Hold the lock to ensure that the target doesn't unlink while + // propagation is in progress. + _R_lock _Lock(_M_internalLock); + + // link to the target, create a message and send it + link_target(_PTarget); + + _CONCRT_ASSERT(_M_pMessage == NULL); + _M_pMessage = _Msg; + + _Status = _PTarget->propagate(_M_pMessage, this); + } + + // If the status is anything other than postponed, unlink away + // from the target and delete the AsyncOriginator. + if (_Status != postponed) + { + unlink_target(_PTarget); + } + + // Release the reference acquired above + _Release_ref(); + + return (_Status == accepted); + } + + // Add a target messaging block for this AsyncOriginator + virtual void link_target(ITarget<_Type> * _PTarget) + { + if (_PTarget == NULL) + { + throw std::invalid_argument("_PTarget"); + } + + // Acquire a reference that will be released by unlink_target + _Acquire_ref(); + _M_connectedTargets.add(_PTarget); + _Invoke_link_source(_PTarget); + + // There should be no pending messages to propagate at this time. + _CONCRT_ASSERT(_M_pMessage == NULL); + + } + + // Acquire a reference on the async originator object + void _Acquire_ref() + { + _InterlockedIncrement(&_M_refcount); + } + + // Release the reference on the async originator object. The object + // will be deleted when the reference count goes to 0. + void _Release_ref() + { + _CONCRT_ASSERT(_M_refcount > 0); + if (_InterlockedDecrement(&_M_refcount) == 0) + { + delete this; + } + } + + // The message that will be propagated by the AsyncOriginator + message<_Type> * _M_pMessage; + + // Reference count to manage object lifetime + volatile long _M_refcount; + + // The internal lock for this block for its message + ::Concurrency::details::_ReentrantPPLLock _M_internalLock; + + // connected targets + _Target_registry _M_connectedTargets; + }; + + // static class that exposes methods to initiate messages into + // a dataflow network + class _Originator + { + public: + + // Synchronous initiation of messages + template <class _Type> + static bool _send(ITarget<_Type> * _Trg, const _Type& _Data) + { + if (_Trg != NULL && _Trg->supports_anonymous_source()) + { + // _send will block until the message is accepted/rejected. + // Note that this invokes the send method on the target which + // would synchronously process the message. + _AnonymousOriginator<_Type> _Send_block; + return _Send_block._internal_send(_Trg, _Data); + } + else + { + // Create a blocking originator on the stack. _send will block until the + // message is accepted/rejected. + _SyncOriginator<_Type> _Orig; + return _Orig._internal_send(_Trg, _Data); + } + } + + // Asynchronous initiation of messages + template <class _Type> + static bool _asend(ITarget<_Type> * _Trg, const _Type& _Data) + { + // If the block can participate in posting messages without requiring a call back, use that + // method of initiating the message rather for efficiency purposes. + if (_Trg != NULL && _Trg->supports_anonymous_source()) + { + _AnonymousOriginator<_Type> _Asend_block; + return _Asend_block._internal_asend(_Trg, _Data); + } + else + { + // Needs to be allocated on the heap + _AsyncOriginator<_Type> * _AsyncOrig = new _AsyncOriginator<_Type>; + return _AsyncOrig->_internal_send(_Trg, _Data); + } + } + }; + +} // namespace details + +/// <summary> +/// A synchronous send operation, which waits until the target either accepts or declines the message. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Trg"> +/// A pointer or reference to the target to which data is sent. +/// </param> +/// <param name="_Data"> +/// A reference to the data to be sent. +/// </param> +/// <returns> +/// <c>true</c> if the message was accepted, <c>false</c> otherwise. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +bool send(_Inout_ ITarget<_Type> * _Trg, const _Type& _Data) +{ + return details::_Originator::_send(_Trg, _Data); +} + + +/// <summary> +/// A synchronous send operation, which waits until the target either accepts or declines the message. +/// </summary> +/// <typeparam name="_Type"> +/// The payload type. +/// </typeparam> +/// <param name="_Trg"> +/// A pointer or reference to the target to which data is sent. +/// </param> +/// <param name="_Data"> +/// A reference to the data to be sent. +/// </param> +/// <returns> +/// <c>true</c> if the message was accepted, <c>false</c> otherwise. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="asend Function"/> +/**/ +template <class _Type> +bool send(ITarget<_Type> &_Trg, const _Type &_Data) +{ + return send(&_Trg, _Data); +} + +/// <summary> +/// An asynchronous send operation, which schedules a task to propagate the data to the target block. +/// </summary> +/// <typeparam name="_Type"> +/// The type of the data to be sent. +/// </typeparam> +/// <param name="_Trg"> +/// A pointer or reference to the target to which data is sent. +/// </param> +/// <param name="_Data"> +/// A reference to the data to be sent. +/// </param> +/// <returns> +/// <c>true</c> if the message was accepted before the method returned, <c>false</c> otherwise. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="send Function"/> +/**/ +template <class _Type> +bool asend(_Inout_ ITarget<_Type> * _Trg, const _Type& _Data) +{ + return details::_Originator::_asend(_Trg, _Data); +} + + +/// <summary> +/// An asynchronous send operation, which schedules a task to propagate the value to the target block. +/// </summary> +/// <typeparam name="_Type"> +/// The type of the data to be sent. +/// </typeparam> +/// <param name="_Trg"> +/// A pointer or reference to the target to which data is sent. +/// </param> +/// <param name="_Data"> +/// A reference to the data to be sent. +/// </param> +/// <returns> +/// <c>true</c> if the message was accepted, <c>false</c> otherwise. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Message Passing Functions"/>. +/// </remarks> +/// <seealso cref="receive Function"/> +/// <seealso cref="try_receive Function"/> +/// <seealso cref="send Function"/> +/**/ +template <class _Type> +bool asend(ITarget<_Type> &_Trg, const _Type &_Data) +{ + return asend(&_Trg, _Data); +} + +/// <summary> +/// Associates the given name to the message block or agent in the ETW trace. +/// </summary> +/// <typeparam name="_Type"> +/// The type of the object. This is typically a message block or an agent. +/// </typeparam> +/// <param name="_PObject"> +/// A pointer to the message block or agent that is being named in the trace. +/// </param> +/// <param name="_Name"> +/// The name for the given object. +/// </param> +template <class _Type> +void Trace_agents_register_name(_Inout_ _Type * _PObject, _In_z_ const wchar_t * _Name) +{ + _Trace_agents(AGENTS_EVENT_NAME, ::Concurrency::details::_Trace_agents_get_id(_PObject), _Name); +} + +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma warning(pop) +#pragma pack(pop) diff --git a/test_data/lots_of_files/align.c b/test_data/lots_of_files/align.c new file mode 100644 index 0000000..5fbbf65 --- /dev/null +++ b/test_data/lots_of_files/align.c @@ -0,0 +1,474 @@ +/*** +*align.c - Aligned allocation, reallocation or freeing of memory in the heap +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines the _aligned_malloc(), +* _aligned_realloc(), +* _aligned_recalloc(), +* _aligned_offset_malloc(), +* _aligned_offset_realloc(), +* _aligned_offset_recalloc(), +* _aligned_free(), +* _aligned_msize() functions. +* +*******************************************************************************/ + +#include <dbgint.h> +#include <crtdbg.h> +#include <errno.h> +#include <string.h> +#include <malloc.h> +#include <stddef.h> +#include <stdlib.h> +#include <internal.h> + +#define IS_2_POW_N(X) (((X)&(X-1)) == 0) +#define PTR_SZ sizeof(void *) +/*** +* +* |1|___6___|2|3|4|_________5__________|_6_| +* +* 1 -> Pointer to start of the block allocated by malloc. +* 2 -> Value of 1. +* 3 -> Gap used to get 1 aligned on sizeof(void *). +* 4 -> Pointer to the start of data block. +* 4+5 -> Data block. +* 6 -> Wasted memory at rear of data block. +* 6 -> Wasted memory. +* +*******************************************************************************/ + +/*** +* void *_aligned_malloc_base(size_t size, size_t alignment) +* - Get a block of aligned memory from the heap. +* +* Purpose: +* Allocate of block of aligned memory aligned on the alignment of at least +* size bytes from the heap and return a pointer to it. +* +* Entry: +* size_t size - size of block requested +* size_t alignment - alignment of memory (needs to be a power of 2) +* +* Exit: +* Success: Pointer to memory block +* Failure: Null, errno is set +* +* Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +void * __cdecl _aligned_malloc_base( + size_t size, + size_t alignment + ) +{ + return _aligned_offset_malloc_base(size, alignment, 0); +} +/*** +* void *_aligned_offset_malloc_base(size_t size, size_t alignment, int offset) +* - Get a block of memory from the heap. +* +* Purpose: +* Allocate a block of memory which is shifted by offset from alignment of +* at least size bytes from the heap and return a pointer to it. +* +* Entry: +* size_t size - size of block of memory +* size_t alignment - alignment of memory (needs to be a power of 2) +* size_t offset - offset of memory from the alignment +* +* Exit: +* Success: Pointer to memory block +* Failure: Null, errno is set +* +* Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + + +void * __cdecl _aligned_offset_malloc_base( + size_t size, + size_t align, + size_t offset + ) +{ + uintptr_t ptr, retptr, gap; + size_t nonuser_size,block_size; + + /* validation section */ + _VALIDATE_RETURN(IS_2_POW_N(align), EINVAL, NULL); + _VALIDATE_RETURN(offset == 0 || offset < size, EINVAL, NULL); + + align = (align > PTR_SZ ? align : PTR_SZ) -1; + + /* gap = number of bytes needed to round up offset to align with PTR_SZ*/ + gap = (0 - offset)&(PTR_SZ -1); + + nonuser_size = PTR_SZ +gap +align; + block_size = nonuser_size + size; + _VALIDATE_RETURN_NOEXC(size <= block_size, ENOMEM, NULL) + + if ( (ptr =(uintptr_t)malloc(block_size)) == (uintptr_t)NULL) + return NULL; + + retptr =((ptr +nonuser_size+offset)&~align)- offset; + ((uintptr_t *)(retptr - gap))[-1] = ptr; + + return (void *)retptr; +} + +/*** +* +* void *_aligned_realloc_base(void * memblock, size_t size, size_t alignment) +* - Reallocate a block of aligned memory from the heap. +* +* Purpose: +* Reallocates of block of aligned memory aligned on the alignment of at +* least size bytes from the heap and return a pointer to it. Size can be +* either greater or less than the original size of the block. +* The reallocation may result in moving the block as well as changing the +* size. +* +* Entry: +* void *memblock - pointer to block in the heap previously allocated by +* call to _aligned_malloc(), _aligned_offset_malloc(), +* _aligned_realloc() or _aligned_offset_realloc(). +* size_t size - size of block requested +* size_t alignment - alignment of memory +* +* Exit: +* Success: Pointer to re-allocated memory block +* Failure: Null, errno is set +* +* Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +void * __cdecl _aligned_realloc_base( + void *memblock, + size_t size, + size_t alignment + ) +{ + return _aligned_offset_realloc_base(memblock, size, alignment, 0); +} + +/*** +* +* void *_aligned_recalloc_base(void * memblock, size_t count, size_t size, size_t alignment) +* - Reallocate a block of aligned memory from the heap. +* +* Purpose: +* Reallocates of block of aligned memory aligned on the alignment of at +* least size bytes from the heap and return a pointer to it. Size can be +* either greater or less than the original size of the block. +* The reallocation may result in moving the block as well as changing the +* size. +* +* Entry: +* void *memblock - pointer to block in the heap previously allocated by +* call to _aligned_malloc(), _aligned_offset_malloc(), +* _aligned_realloc() or _aligned_offset_realloc(). +* size_t count - count of items +* size_t size - size of item +* size_t alignment - alignment of memory +* +* Exit: +* Success: Pointer to re-allocated memory block +* Failure: Null, errno is set +* +* Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +void * __cdecl _aligned_recalloc_base( + void *memblock, + size_t count, + size_t size, + size_t alignment + ) +{ + return _aligned_offset_recalloc_base(memblock, count, size, alignment, 0); +} + +/*** +* +* void *_aligned_offset_realloc_base (void * memblock, size_t size, +* size_t alignment, int offset) +* - Reallocate a block of memory from the heap. +* +* Purpose: +* Reallocates a block of memory which is shifted by offset from +* alignment of at least size bytes from the heap and return a pointer +* to it. Size can be either greater or less than the original size of the +* block. +* +* Entry: +* void *memblock - pointer to block in the heap previously allocated by +* call to _aligned_malloc(), _aligned_offset_malloc(), +* _aligned_realloc() or _aligned_offset_realloc(). +* size_t size - size of block of memory +* size_t alignment - alignment of memory +* size_t offset - offset of memory from the alignment +* +* Exit: +* Success: Pointer to re-allocated memory block +* Failure: Null, errno is set +* +* Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +void * __cdecl _aligned_offset_realloc_base( + void *memblock, + size_t size, + size_t align, + size_t offset + ) +{ + uintptr_t ptr, retptr, gap, stptr, diff; + uintptr_t movsz, reqsz; + int bFree = 0; + + /* special cases */ + if (memblock == NULL) + { + return _aligned_offset_malloc_base(size, align, offset); + } + if (size == 0) + { + _aligned_free_base(memblock); + return NULL; + } + + /* validation section */ + _VALIDATE_RETURN(IS_2_POW_N(align), EINVAL, NULL); + _VALIDATE_RETURN(offset == 0 || offset < size, EINVAL, NULL); + + stptr = (uintptr_t)memblock; + + /* ptr points to the pointer to starting of the memory block */ + stptr = (stptr & ~(PTR_SZ -1)) - PTR_SZ; + + /* ptr is the pointer to the start of memory block*/ + stptr = *((uintptr_t *)stptr); + + align = (align > PTR_SZ ? align : PTR_SZ) -1; + /* gap = number of bytes needed to round up offset to align with PTR_SZ*/ + gap = (0 -offset)&(PTR_SZ -1); + + diff = (uintptr_t)memblock - stptr; + /* Mov size is min of the size of data available and sizw requested. + */ + CRT_WARNING_DISABLE_PUSH(22018, "Silence prefast about overflow/underflow"); + movsz = _msize((void *)stptr) - ((uintptr_t)memblock - stptr); + CRT_WARNING_POP + movsz = movsz > size? size: movsz; + reqsz = PTR_SZ +gap +align +size; + + _VALIDATE_RETURN_NOEXC(size <= reqsz, ENOMEM, NULL); + + /* First check if we can expand(reducing or expanding using expand) data + * safely, ie no data is lost. eg, reducing alignment and keeping size + * same might result in loss of data at the tail of data block while + * expanding. + * + * If no, use malloc to allocate the new data and move data. + * + * If yes, expand and then check if we need to move the data. + */ + if ((stptr +align +PTR_SZ +gap)<(uintptr_t)memblock) + { + if ((ptr = (uintptr_t)malloc(reqsz)) == (uintptr_t) NULL) + return NULL; + bFree = 1; + } + else + { + /* we need to save errno, which can be modified by _expand */ + errno_t save_errno = errno; + if ((ptr = (uintptr_t)_expand((void *)stptr, reqsz)) == (uintptr_t)NULL) + { + errno = save_errno; + if ((ptr = (uintptr_t)malloc(reqsz)) == (uintptr_t) NULL) + return NULL; + bFree = 1; + } + else + stptr = ptr; + } + + + if ( ptr == ((uintptr_t)memblock - diff) + && !( ((size_t)memblock + gap +offset) & ~(align) )) + { + return memblock; + } + + retptr =((ptr +PTR_SZ +gap +align +offset)&~align)- offset; + memmove((void *)retptr, (void *)(stptr + diff), movsz); + if ( bFree) + free ((void *)stptr); + + ((uintptr_t *)(retptr - gap))[-1] = ptr; + return (void *)retptr; +} + + +/*** +* +* size_t _aligned_msize_base(void *memblock, size_t align, size_t offset) +* +* Purpose: +* Computes the size of an aligned block. +* +* Entry: +* void * memblock - pointer to the aligned block of memory +* +* Exceptions: +* None. If memblock == NULL 0 is returned. +* +*******************************************************************************/ + +size_t __cdecl _aligned_msize_base(void *memblock, size_t align, size_t offset) +{ + size_t header_size = 0; /* Size of the header block */ + size_t footer_size = 0; /* Size of the footer block */ + size_t total_size = 0; /* total size of the allocated block */ + size_t user_size = 0; /* size of the user block*/ + uintptr_t gap = 0; /* keep the alignment of the data block */ + /* after the sizeof(void*) aligned pointer */ + /* to the beginning of the allocated block */ + uintptr_t ptr = 0; /* computes the beginning of the allocated block */ + + _VALIDATE_RETURN (memblock != NULL, EINVAL, -1); + + /* HEADER SIZE + FOOTER SIZE = GAP + ALIGN + SIZE OF A POINTER*/ + /* HEADER SIZE + USER SIZE + FOOTER SIZE = TOTAL SIZE */ + + ptr = (uintptr_t)memblock; /* ptr points to the start of the aligned memory block */ + ptr = (ptr & ~(PTR_SZ - 1)) - PTR_SZ; /* ptr is one position behind memblock */ + /* the value in ptr is the start of the real allocated block */ + ptr = *((uintptr_t *)ptr); /* after dereference ptr points to the beginning of the allocated block */ + + total_size = _msize((void*)ptr); + header_size = (uintptr_t) memblock - ptr; + gap = (0 - offset) & (PTR_SZ - 1); + /* Alignment cannot be less than sizeof(void*) */ + align = (align > PTR_SZ ? align : PTR_SZ) -1; + footer_size = gap + align + PTR_SZ - header_size; + user_size = total_size - header_size - footer_size; + + return user_size; +} + +/*** +* +* void *_aligned_offset_recalloc_base (void * memblock, size_t size, size_t count, size_t alignment, int offset) +* - Reallocate a block of memory from the heap. +* +* Purpose: +* Reallocates a block of memory which is shifted by offset from +* alignment of at least size bytes from the heap and return a pointer +* to it. Size can be either greater or less than the original size of the +* block. +* +* Entry: +* void *memblock - pointer to block in the heap previously allocated by +* call to _aligned_malloc(), _aligned_offset_malloc(), +* _aligned_realloc() or _aligned_offset_realloc(). +* size_t count - count of items +* size_t size - size of items +* size_t alignment - alignment of memory +* size_t offset - offset of memory from the alignment +* +* Exit: +* Success: Pointer to re-allocated memory block +* Failure: Null, errno is set +* +* Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +void * __cdecl _aligned_offset_recalloc_base( + void * memblock, + size_t count, + size_t size, + size_t align, + size_t offset + ) +{ + size_t user_size = 0; /* wanted size, passed to aligned realoc */ + size_t start_fill = 0; /* location where aligned recalloc starts to fill with 0 */ + /* filling must start from the end of the previous user block */ + void * retptr = NULL; /* result of aligned recalloc*/ + uintptr_t ptr = 0; /* points to the beginning of the allocated block*/ + + /* ensure that (size * num) does not overflow */ + if (count > 0) + { + _VALIDATE_RETURN_NOEXC((_HEAP_MAXREQ / count) >= size, ENOMEM, NULL); + } + + user_size = size * count; + + if (memblock != NULL) + { + start_fill = _aligned_msize(memblock, align, offset); + } + + retptr = _aligned_offset_realloc_base(memblock, user_size, align, offset); + + if (retptr != NULL) + { + if (start_fill < user_size) + { + memset ((char*)retptr + start_fill, 0, user_size - start_fill); + } + } + return retptr; +} + +/*** +* +* void *_aligned_free_base(void *memblock) +* - Free the memory which was allocated using _aligned_malloc or +* _aligned_offset_memory +* +* Purpose: +* Frees the aligned memory block which was allocated using _aligned_malloc. +* +* Entry: +* void * memblock - pointer to the block of memory +* +* Exceptions: +* None. If memblock == NULL we simply return without doing anything. +* +*******************************************************************************/ + +void __cdecl _aligned_free_base(void *memblock) +{ + uintptr_t ptr; + + if (memblock == NULL) + return; + + ptr = (uintptr_t)memblock; + + /* ptr points to the pointer to starting of the memory block */ + ptr = (ptr & ~(PTR_SZ -1)) - PTR_SZ; + + /* ptr is the pointer to the start of memory block*/ + ptr = *((uintptr_t *)ptr); + free((void *)ptr); +} + diff --git a/test_data/lots_of_files/align.h b/test_data/lots_of_files/align.h new file mode 100644 index 0000000..29020d4 --- /dev/null +++ b/test_data/lots_of_files/align.h @@ -0,0 +1,30 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// align.h +// +// Alignment / Packing definitions +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +#define WORD_ALIGN 1 +#define DWORD_ALIGN 3 +#define QWORD_ALIGN 7 +#define DQWORD_ALIGN 15 + +#ifdef _WIN64 +#define P2_ALIGN 15 +#else // !_WIN64 +#define P2_ALIGN 7 +#endif // _WIN64 + +#define ALIGNED_SIZE(size, alignment) (((size) + (alignment)) & ~(alignment)) + +#ifndef SIZEOF_ARRAY +#define SIZEOF_ARRAY(x) ((sizeof(x))/(sizeof(x[0]))) +#endif // SIZEOF_ARRAY + diff --git a/test_data/lots_of_files/ammintrin.h b/test_data/lots_of_files/ammintrin.h new file mode 100644 index 0000000..0877e48 --- /dev/null +++ b/test_data/lots_of_files/ammintrin.h @@ -0,0 +1,298 @@ +/**** +* Copyright (C) 2007-2008 Advanced Micro Devices Inc. All rights reserved. +* +* The information and source code contained herein is the exclusive +* property of Advanced Micro Devices and may not be disclosed, examined +* or reproduced in whole or in part without explicit written authorization +* from the company. +* +* ammintrin.h - Definitions for AMD-specific intrinsics +* +****/ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_AMM +#define _INCLUDED_AMM + +#if defined (_M_CEE_PURE) + #error ERROR: This file is not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#if defined __cplusplus +extern "C" { /* Intrinsics use C name-mangling. */ +#endif /* defined __cplusplus */ + +/* + * Vector integer comparison control macros + */ + +#define _MM_PCOMCTRL_LT 0 +#define _MM_PCOMCTRL_LE 1 +#define _MM_PCOMCTRL_GT 2 +#define _MM_PCOMCTRL_GE 3 +#define _MM_PCOMCTRL_EQ 4 +#define _MM_PCOMCTRL_NEQ 5 +#define _MM_PCOMCTRL_FALSE 6 +#define _MM_PCOMCTRL_TRUE 7 + +/* + * MACRO functions for vector integer comparisons + */ + +#define _mm_comlt_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epu8(v1, v2) _mm_com_epu8(v1, v2, _MM_PCOMCTRL_TRUE) + +#define _mm_comlt_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epu16(v1, v2) _mm_com_epu16(v1, v2, _MM_PCOMCTRL_TRUE) + +#define _mm_comlt_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epu32(v1, v2) _mm_com_epu32(v1, v2, _MM_PCOMCTRL_TRUE) + +#define _mm_comlt_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epu64(v1, v2) _mm_com_epu64(v1, v2, _MM_PCOMCTRL_TRUE) + +#define _mm_comlt_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epi8(v1, v2) _mm_com_epi8(v1, v2, _MM_PCOMCTRL_TRUE) + +#define _mm_comlt_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epi16(v1, v2) _mm_com_epi16(v1, v2, _MM_PCOMCTRL_TRUE) + +#define _mm_comlt_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epi32(v1, v2) _mm_com_epi32(v1, v2, _MM_PCOMCTRL_TRUE) + +#define _mm_comlt_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_LT) +#define _mm_comle_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_LE) +#define _mm_comgt_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_GT) +#define _mm_comge_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_GE) +#define _mm_comeq_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_EQ) +#define _mm_comneq_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_NEQ) +#define _mm_comfalse_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_FALSE) +#define _mm_comtrue_epi64(v1, v2) _mm_com_epi64(v1, v2, _MM_PCOMCTRL_TRUE) + +/* SSE5 intrinsics */ + +/* Float/double multiply-accumulate */ +__m128 _mm_macc_ps(__m128, __m128, __m128); +__m128d _mm_macc_pd(__m128d, __m128d, __m128d); +__m128 _mm_macc_ss(__m128, __m128, __m128); +__m128d _mm_macc_sd(__m128d, __m128d, __m128d); +__m128 _mm_maddsub_ps(__m128, __m128, __m128); +__m128d _mm_maddsub_pd(__m128d, __m128d, __m128d); +__m128 _mm_msubadd_ps(__m128, __m128, __m128); +__m128d _mm_msubadd_pd(__m128d, __m128d, __m128d); +__m128 _mm_msub_ps(__m128, __m128, __m128); +__m128d _mm_msub_pd(__m128d, __m128d, __m128d); +__m128 _mm_msub_ss(__m128, __m128, __m128); +__m128d _mm_msub_sd(__m128d, __m128d, __m128d); +__m128 _mm_nmacc_ps(__m128, __m128, __m128); +__m128d _mm_nmacc_pd(__m128d, __m128d, __m128d); +__m128 _mm_nmacc_ss(__m128, __m128, __m128); +__m128d _mm_nmacc_sd(__m128d, __m128d, __m128d); +__m128 _mm_nmsub_ps(__m128, __m128, __m128); +__m128d _mm_nmsub_pd(__m128d, __m128d, __m128d); +__m128 _mm_nmsub_ss(__m128, __m128, __m128); +__m128d _mm_nmsub_sd(__m128d, __m128d, __m128d); + +/* Integer multiply-accumulate */ +__m128i _mm_maccs_epi16(__m128i, __m128i, __m128i); +__m128i _mm_macc_epi16(__m128i, __m128i, __m128i); +__m128i _mm_maccsd_epi16(__m128i, __m128i, __m128i); +__m128i _mm_maccd_epi16(__m128i, __m128i, __m128i); +__m128i _mm_maccs_epi32(__m128i, __m128i, __m128i); +__m128i _mm_macc_epi32(__m128i, __m128i, __m128i); +__m128i _mm_maccslo_epi32(__m128i, __m128i, __m128i); +__m128i _mm_macclo_epi32(__m128i, __m128i, __m128i); +__m128i _mm_maccshi_epi32(__m128i, __m128i, __m128i); +__m128i _mm_macchi_epi32(__m128i, __m128i, __m128i); +__m128i _mm_maddsd_epi16(__m128i, __m128i, __m128i); +__m128i _mm_maddd_epi16(__m128i, __m128i, __m128i); + +/* Horizontal add/subtract */ +__m128i _mm_haddw_epi8(__m128i); +__m128i _mm_haddd_epi8(__m128i); +__m128i _mm_haddq_epi8(__m128i); +__m128i _mm_haddd_epi16(__m128i); +__m128i _mm_haddq_epi16(__m128i); +__m128i _mm_haddq_epi32(__m128i); +__m128i _mm_haddw_epu8(__m128i); +__m128i _mm_haddd_epu8(__m128i); +__m128i _mm_haddq_epu8(__m128i); +__m128i _mm_haddd_epu16(__m128i); +__m128i _mm_haddq_epu16(__m128i); +__m128i _mm_haddq_epu32(__m128i); +__m128i _mm_hsubw_epi8(__m128i); +__m128i _mm_hsubd_epi16(__m128i); +__m128i _mm_hsubq_epi32(__m128i); + +/* Vector conditional moves */ +__m128i _mm_cmov_si128(__m128i, __m128i, __m128i); +__m128i _mm_perm_epi8(__m128i, __m128i, __m128i); + +/* Vector shifts and rotates */ +__m128i _mm_rot_epi8(__m128i, __m128i); +__m128i _mm_rot_epi16(__m128i, __m128i); +__m128i _mm_rot_epi32(__m128i, __m128i); +__m128i _mm_rot_epi64(__m128i, __m128i); +__m128i _mm_roti_epi8(__m128i, int); +__m128i _mm_roti_epi16(__m128i, int); +__m128i _mm_roti_epi32(__m128i, int); +__m128i _mm_roti_epi64(__m128i, int); +__m128i _mm_shl_epi8(__m128i, __m128i); +__m128i _mm_shl_epi16(__m128i, __m128i); +__m128i _mm_shl_epi32(__m128i, __m128i); +__m128i _mm_shl_epi64(__m128i, __m128i); +__m128i _mm_sha_epi8(__m128i, __m128i); +__m128i _mm_sha_epi16(__m128i, __m128i); +__m128i _mm_sha_epi32(__m128i, __m128i); +__m128i _mm_sha_epi64(__m128i, __m128i); + +/* Vector integer comparisons */ + +__m128i _mm_com_epu8(__m128i, __m128i, int); +__m128i _mm_com_epu16(__m128i, __m128i, int); +__m128i _mm_com_epu32(__m128i, __m128i, int); +__m128i _mm_com_epu64(__m128i, __m128i, int); +__m128i _mm_com_epi8(__m128i, __m128i, int); +__m128i _mm_com_epi16(__m128i, __m128i, int); +__m128i _mm_com_epi32(__m128i, __m128i, int); +__m128i _mm_com_epi64(__m128i, __m128i, int); + +/* Precision control */ + +__m128 _mm_frcz_ps(__m128); +__m128d _mm_frcz_pd(__m128d); +__m128 _mm_frcz_ss(__m128, __m128); +__m128d _mm_frcz_sd(__m128d, __m128d); + +/* Control values for permute2 intrinsics */ +#define _MM_PERMUTE2_COPY 0 /* just copy the selected value */ +/* Note that using the constant 1 would have the same effect as 0 */ +#define _MM_PERMUTE2_ZEROIF1 2 /* zero selected value if src3 bit is 1 */ +#define _MM_PERMUTE2_ZEROIF0 3 /* zero selected value if src3 bit is 3 */ + +/* Permutation */ +__m128 _mm_permute2_ps(__m128, __m128, __m128i, int); +__m128d _mm_permute2_pd(__m128d, __m128d, __m128i, int); + + +/* YMM versions */ +__m256 _mm256_macc_ps(__m256, __m256, __m256); +__m256d _mm256_macc_pd(__m256d, __m256d, __m256d); +__m256 _mm256_maddsub_ps(__m256, __m256, __m256); +__m256d _mm256_maddsub_pd(__m256d, __m256d, __m256d); +__m256 _mm256_msubadd_ps(__m256, __m256, __m256); +__m256d _mm256_msubadd_pd(__m256d, __m256d, __m256d); +__m256 _mm256_msub_ps(__m256, __m256, __m256); +__m256d _mm256_msub_pd(__m256d, __m256d, __m256d); +__m256 _mm256_nmacc_ps(__m256, __m256, __m256); +__m256d _mm256_nmacc_pd(__m256d, __m256d, __m256d); +__m256 _mm256_nmsub_ps(__m256, __m256, __m256); +__m256d _mm256_nmsub_pd(__m256d, __m256d, __m256d); +__m256i _mm256_cmov_si256(__m256i, __m256i, __m256i); +__m256 _mm256_frcz_ps(__m256); +__m256d _mm256_frcz_pd(__m256d); +__m256 _mm256_permute2_ps(__m256, __m256, __m256i, int); +__m256d _mm256_permute2_pd(__m256d, __m256d, __m256i, int); + +/* LWP intrinsics */ +void __llwpcb(void *); +void *__slwpcb(); +void __lwpval32(unsigned int, unsigned int, unsigned int); +unsigned char __lwpins32(unsigned int, unsigned int, unsigned int); +#if defined (_M_X64) +void __lwpval64(unsigned __int64, unsigned int, unsigned int); +unsigned char __lwpins64(unsigned __int64, unsigned int, unsigned int); +#endif /* defined (_M_X64) */ + +/*BMI intrinsics */ +unsigned int _bextr_u32(unsigned int, unsigned int, unsigned int); +unsigned int _andn_u32(unsigned int, unsigned int); +unsigned int _tzcnt_u32(unsigned int); +unsigned int _lzcnt_u32(unsigned int); +unsigned int _blsr_u32(unsigned int); +unsigned int _blsmsk_u32(unsigned int); +unsigned int _blsi_u32(unsigned int); +#if defined (_M_X64) +unsigned __int64 _bextr_u64(unsigned __int64, unsigned int, unsigned int); +unsigned __int64 _andn_u64(unsigned __int64, unsigned __int64); +unsigned __int64 _tzcnt_u64(unsigned __int64); +unsigned __int64 _lzcnt_u64(unsigned __int64); +unsigned __int64 _blsr_u64(unsigned __int64); +unsigned __int64 _blsmsk_u64(unsigned __int64); +unsigned __int64 _blsi_u64(unsigned __int64); +#endif /* defined (_M_X64) */ + +/* TBM intrinsics */ +unsigned int _bextri_u32(unsigned int, unsigned int); +unsigned int _blcfill_u32(unsigned int); +unsigned int _blsfill_u32(unsigned int); +unsigned int _blcs_u32(unsigned int); +unsigned int _tzmsk_u32(unsigned int); +unsigned int _blcic_u32(unsigned int); +unsigned int _blsic_u32(unsigned int); +unsigned int _t1mskc_u32(unsigned int); +unsigned int _blcmsk_u32(unsigned int); +unsigned int _blci_u32(unsigned int); +#if defined (_M_X64) +unsigned __int64 _bextri_u64(unsigned __int64, unsigned int); +unsigned __int64 _blcfill_u64(unsigned __int64); +unsigned __int64 _blsfill_u64(unsigned __int64); +unsigned __int64 _blcs_u64(unsigned __int64); +unsigned __int64 _tzmsk_u64(unsigned __int64); +unsigned __int64 _blcic_u64(unsigned __int64); +unsigned __int64 _blsic_u64(unsigned __int64); +unsigned __int64 _t1mskc_u64(unsigned __int64); +unsigned __int64 _blcmsk_u64(unsigned __int64); +unsigned __int64 _blci_u64(unsigned __int64); +#endif /* defined (_M_X64) */ + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ +#endif /* _INCLUDED_AMM */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/amp.h b/test_data/lots_of_files/amp.h new file mode 100644 index 0000000..18b603c --- /dev/null +++ b/test_data/lots_of_files/amp.h @@ -0,0 +1,7554 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* amp.h +* +* C++ AMP Library +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include <crtdbg.h> +#include <vector> +#include <iterator> +#include <future> + +#include <amprt.h> +#include <xxamp.h> +#include <type_traits> + +#define _AMP_H + +#pragma pack(push,8) + + +namespace Concurrency +{ + +/// <summary> +/// Define an N-dimensional index point; which may also be viewed as a vector +/// based at the origin in N-space. +/// +/// The index<N> type represents an N-dimensional vector of int which specifies +/// a unique position in an N-dimensional space. The values in the coordinate +/// vector are ordered from most-significant to least-significant. Thus, in +/// 2-dimensional space, the index vector (5,3) represents the position at +/// row 5, column 3. +/// +/// The position is relative to the origin in the N-dimensional space, and can +/// contain negative component values. +/// </summary> +/// +/// <param name="_Rank"> +/// The dimensionality space into which this index applies, can be any integer +/// greater than 0. +/// </param> +template <int _Rank> class index +{ +public: + _CPP_AMP_VERIFY_RANK(_Rank, index); + + template <typename _Value_type, int _Rank> + friend class array; + + template <int _Rank, int _Element_size> + friend class details::_Array_view_shape; + + template <int _Rank, int _Element_size> + friend class details::_Array_view_base; + + static const int rank = _Rank; + typedef int value_type; + + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + index() __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAssign>::func(*this, 0); + } + + /// <summary> + /// Copy Constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from + /// </param> + index(const index<_Rank>& _Other) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAssign>::func(*this, _Other); + } + + /// <summary> + /// Constructor for index<1> + /// </summary> + /// <param name="_I"> + /// The value for initialization + /// </param> + explicit index(int _I) __GPU + { + static_assert(_Rank == 1, "This constructor can only be used to construct an index<1> object."); + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAssign>::func(*this, _I); + } + + /// <summary> + /// Constructor for index<2> + /// </summary> + /// <param name="_I0"> + /// The index value for dimension 0 + /// </param> + /// <param name="_I1"> + /// The index value for dimension 1 + /// </param> + index(int _I0, int _I1) __GPU + { + static_assert(_Rank == 2, "This constructor can only be used to construct an index<2> object."); + _M_base[0] = _I0; + _M_base[1] = _I1; + } + + /// <summary> + /// Constructor for index<3> + /// </summary> + /// <param name="_I0"> + /// The index value for dimension 0 + /// </param> + /// <param name="_I1"> + /// The index value for dimension 1 + /// </param> + /// <param name="_I2"> + /// The index value for dimension 2 + /// </param> + index(int _I0, int _I1, int _I2) __GPU + { + static_assert(_Rank == 3, "This constructor can only be used to construct an index<3> object."); + _M_base[0] = _I0; + _M_base[1] = _I1; + _M_base[2] = _I2; + } + + /// <summary> + /// Constructs an index<N> with the coordinate values provided the array + /// of int component values. If the coordinate array length is not N, + /// the behavior is undefined. + /// </summary> + /// <param name="_Array"> + /// A single-dimensional array with _Rank elements. + /// </param> + explicit index(const int _Array[_Rank]) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAssign>::func(*this, _Array); + } + + /// <summary> + /// copy-assignment operators + /// </summary> + index<_Rank>& operator=(const index<_Rank>& _Other) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAssign>::func(*this, _Other); + return *this; + } + + /// <summary> + /// Index operator. + /// </summary> + /// <param name="_Index"> + /// An integral value between 0 and _Rank-1. + /// </param> + /// <returns> + /// The corresponding value stored at _Index. + /// </returns> + int operator[] (unsigned _Index) const __GPU + { + return _M_base[_Index]; + } + + /// <summary> + /// Index operator. + /// </summary> + /// <param name="_Index"> + /// An integral value between 0 and _Rank-1. + /// </param> + /// <returns> + /// A reference to the corresponding value stored at _Index. + /// </returns> + int& operator[] (unsigned _Index) __GPU + { + return _M_base[_Index]; + } + + // Operations + + /// <summary> + /// Element-wise addition of this index with another index. + /// </summary> + /// <param name="_Rhs"> + /// The index to add + /// </param> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator+=(const index<_Rank>& _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAddEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Element-wise subtraction of this index with another index. + /// </summary> + /// <param name="_Rhs"> + /// The index to subtract + /// </param> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator-=(const index<_Rank>& _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opSubEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Adds an integer value to each element of this index. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to add + /// </param> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator+=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAddEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Subtracts an integer value from each element of this index. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to subtract. + /// </param> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator-=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opSubEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Multiplies each element of this index with an integer value. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to multiply. + /// </param> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator*=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opMulEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Divides each element of this index by an integer value. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to divide by. + /// </param> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator/=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opDivEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Modulus an integer value into each element of this index. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to modulus. + /// </param> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator%=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opModEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Pre-increments each element of this index. + /// </summary> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator++() __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAddEq>::func(*this, 1); + return *this; + } + + /// <summary> + /// Post-increments each element of this index. + /// </summary> + /// <returns> + /// The value of the unincremented index. + /// </returns> + index<_Rank> operator++(int) __GPU + { + index<_Rank> old_Index(*this); + details::_compound_assign_op_loop_helper<index<_Rank>, details::opAddEq>::func(*this, 1); + return old_Index; + } + + /// <summary> + /// Pre-decrements each element of this index. + /// </summary> + /// <returns> + /// A reference to this index. + /// </returns> + index<_Rank>& operator--() __GPU + { + details::_compound_assign_op_loop_helper<index<_Rank>, details::opSubEq>::func(*this, 1); + return *this; + } + + /// <summary> + /// Post-decrements each element of this index. + /// </summary> + /// <returns> + /// The value of the undecremented index. + /// </returns> + index operator--(int) __GPU + { + index<_Rank> old_Index(*this); + details::_compound_assign_op_loop_helper<index<_Rank>, details::opSubEq>::func(*this, 1); + return old_Index; + } + +private: + template<class _Tuple_type> + friend + _Tuple_type details::_Create_uninitialized_tuple() __GPU; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name=""> + /// Indicates that no initialization is necessary. + /// </param> + index(details::_eInitializeState) __GPU {} + // + // implementation details - end + + int _M_base[_Rank]; +}; + + +/// <summary> +/// The extent<N> type represents an N-dimensional vector of int which specifies +/// the bounds of an N-dimensional space with an origin of 0. The values in the +/// coordinate vector are ordered from most-significant to least-significant. +/// Thus, in 2-dimensional space, the extent vector (5,3) represents a space +/// with 5 rows and 3 columns. +/// +/// All components of an extent must be non-negative. +/// E.g. +/// extent<3> domain(2, 3, 4); +/// represents all points +/// index<3> _Index; +/// such that +/// 0 <= _Index[0] < 2; +/// 0 <= _Index[1] < 3; +/// 0 <= _Index[2] < 4; +/// </summary> +/// <param name="_Rank"> +/// The _Rank or the dimensionality of the index space. +/// </param> +template <int _Rank> class extent +{ +public: + _CPP_AMP_VERIFY_RANK(_Rank, extent); + + template <typename _Value_type, int _Rank> + friend class array; + + template <int _Rank, int _Element_size> + friend class details::_Array_view_shape; + + template <int _Rank, int _Element_size> + friend class details::_Array_view_base; + + static const int rank = _Rank; + typedef int value_type; + + + /// <summary> + /// Default constructor. The value at each dimension is initialized to zero. + /// </summary> + extent() __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAssign>::func(*this, 0); + } + + /// <summary> + /// Copy constructor. Constructs a new extent from the supplied argument _Other. + /// </summary> + /// <param name="_Other"> + /// The extent instance to be copied from . + /// </param> + extent(const extent<_Rank>& _Other) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAssign>::func(*this, _Other); + } + + /// <summary> + /// Constructor for extent<1>. + /// </summary> + /// <param name="_I"> + /// The value for initialization + /// </param> + explicit extent(int _I) __GPU + { + static_assert(_Rank == 1, "This constructor can only be used to construct an extent<1> object."); + _M_base[0] = _I; + } + + /// <summary> + /// Constructor for extent<2> + /// </summary> + /// <param name="_I0"> + /// The extent value for dimension 0 + /// </param> + /// <param name="_I1"> + /// The extent value for dimension 1 + /// </param> + extent(int _I0, int _I1) __GPU + { + static_assert(_Rank == 2, "This constructor can only be used to construct an extent<2> object."); + _M_base[0] = _I0; + _M_base[1] = _I1; + } + + /// <summary> + /// Constructor for extent<3> + /// </summary> + /// <param name="_I0"> + /// The extent value for dimension 0 + /// </param> + /// <param name="_I1"> + /// The extent value for dimension 1 + /// </param> + /// <param name="_I2"> + /// The extent value for dimension 2 + /// </param> + extent(int _I0, int _I1, int _I2) __GPU + { + static_assert(_Rank == 3, "This constructor can only be used to construct an extent<3> object."); + _M_base[0] = _I0; + _M_base[1] = _I1; + _M_base[2] = _I2; + } + + /// <summary> + /// Constructs an extent with the coordinate values provided the array + /// of int component values. If the coordinate array length is not N, + /// the behavior is undefined. + /// </summary> + /// <param name="_Array"> + /// A single-dimensional array with _Rank elements. + /// </param> + explicit extent(const int _Array[_Rank]) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAssign>::func(*this, _Array); + } + + /// <summary> + /// copy-assignment operator + /// </summary> + extent<_Rank>& operator=(const extent<_Rank>& _Other) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAssign>::func(*this, _Other); + return *this; + } + + /// <summary> + /// Index operator. + /// </summary> + /// <param name="_Index"> + /// An integral value between 0 and _Rank-1. + /// </param> + /// <returns> + /// The corresponding value stored at _Index. + /// </returns> + int operator[] (unsigned int _Index) const __GPU + { + return _M_base[_Index]; + } + + /// <summary> + /// Index operators. + /// </summary> + /// <param name="_Index"> + /// An integral value between 0 and _Rank-1. + /// </param> + /// <returns> + /// A reference to the value stored at _Index. + /// </returns> + int& operator[] (unsigned int _Index) __GPU + { + return _M_base[_Index]; + } + + /// <summary> + /// Returns the total linear size of this extent (in units of elements). + /// </summary> + unsigned int size() const __GPU + { + return static_cast<unsigned int>(_product_helper<extent<_Rank>>::func(_M_base)); + } + + /// <summary> + /// Tests whether the index "_Index" is properly contained within this extent. + /// </summary> + bool contains(const index<rank>& _Index) const __GPU + { + return details::_contains<extent<rank>, index<rank>, rank>::func(*this, _Index); + } + + /// <summary> + /// Produces a tiled_extent object with the tile extents given by _Dim0. + /// </summary> + template <int _Dim0> tiled_extent<_Dim0> tile() const __GPU + { + static_assert(rank == 1, "One-dimensional tile() method only available on extent<1>"); + static_assert(_Dim0>0, "All tile dimensions must be positive"); + + return tiled_extent<_Dim0>(*this); + } + + /// <summary> + /// Produces a tiled_extent object with the tile extents given by _Dim0, _Dim1 + /// </summary> + template <int _Dim0, int _Dim1> tiled_extent<_Dim0, _Dim1> tile() const __GPU + { + static_assert(rank == 2, "Two-dimensional tile() method only available on extent<2>"); + static_assert(_Dim0>0 && _Dim1>0, "All tile dimensions must be positive"); + + return tiled_extent<_Dim0, _Dim1>(*this); + } + + /// <summary> + /// Produces a tiled_extent object with the tile extents given by _Dim0, _Dim1, _Dim2. + /// </summary> + template <int _Dim0, int _Dim1, int _Dim2> tiled_extent<_Dim0, _Dim1, _Dim2> tile() const __GPU + { + static_assert(rank == 3, "Three-dimensional tile() method only available on extent<3>"); + static_assert(_Dim0>0 && _Dim1>0 && _Dim2>0, "All tile dimensions must be positive"); + + return tiled_extent<_Dim0, _Dim1, _Dim2>(*this); + } + + // Operations + + /// <summary> + /// Element-wise addition of this extent with an index. + /// </summary> + /// <param name="_Rhs"> + /// The index to add to this extent + /// </param> + /// <returns> + /// A new extent with the result of the computation. + /// </returns> + extent<_Rank> operator+(const index<_Rank>& _Rhs) __GPU + { + extent<_Rank> new_extent(details::_do_not_initialize); + details::_arithmetic_op_loop_helper<extent<_Rank>, details::opAdd>::func(new_extent, *this, _Rhs); + return new_extent; + } + + /// <summary> + /// Element-wise subtraction of this extent with an index. + /// </summary> + /// <param name="_Rhs"> + /// The index to subtract from this extent + /// </param> + /// <returns> + /// A new extent with the result of the computation. + /// </returns> + extent<_Rank> operator-(const index<_Rank>& _Rhs) __GPU + { + extent<_Rank> new_extent(details::_do_not_initialize); + details::_arithmetic_op_loop_helper<extent<_Rank>, details::opSub>::func(new_extent, *this, _Rhs); + return new_extent; + } + + /// <summary> + /// Element-wise addition of this extent with another extent. + /// </summary> + /// <param name="_Rhs"> + /// The extent to add + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator+=(const extent<_Rank>& _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAddEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Element-wise subtraction of this extent with another extent. + /// </summary> + /// <param name="_Rhs"> + /// The extent to subtract + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator-=(const extent<_Rank>& _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opSubEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Element-wise addition of this extent with an index. + /// </summary> + /// <param name="_Rhs"> + /// The index to add + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator+=(const index<_Rank>& _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAddEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Element-wise subtraction of this extent with an index. + /// </summary> + /// <param name="_Rhs"> + /// The index to subtract + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator-=(const index<_Rank>& _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opSubEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Adds an integer value to each element of this extent. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to add to this extent + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator+=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAddEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Subtracts an integer value from each element of this extent. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to subtract from this extent + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator-=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opSubEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Multiplies an integer value to each element of this extent. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to multiply into this extent + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator*=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opMulEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Divides an integer value into each element of this extent. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to divide into this extent + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator/=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opDivEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Modulus an integer value from each element of this extent. + /// </summary> + /// <param name="_Rhs"> + /// The integer value to modulo this extent + /// </param> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator%=(int _Rhs) __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opModEq>::func(*this, _Rhs); + return *this; + } + + /// <summary> + /// Pre-increments each element of this extent. + /// </summary> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator++() __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAddEq>::func(*this, 1); + return *this; + } + + /// <summary> + /// Post-increments each element of this extent. + /// </summary> + /// <returns> + /// The value of the unincremented extent. + /// </returns> + extent<_Rank> operator++(int) __GPU + { + extent<_Rank> old_extent(*this); + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opAddEq>::func(*this, 1); + return old_extent; + } + + /// <summary> + /// Pre-decrements each element of this extent. + /// </summary> + /// <returns> + /// A reference to this extent. + /// </returns> + extent<_Rank>& operator--() __GPU + { + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opSubEq>::func(*this, 1); + return *this; + } + + /// <summary> + /// Post-decrements each element of this extent. + /// </summary> + /// <returns> + /// The value of the undecremented extent. + /// </returns> + extent<_Rank> operator--(int) __GPU + { + extent<_Rank> old_extent(*this); + details::_compound_assign_op_loop_helper<extent<_Rank>, details::opSubEq>::func(*this, 1); + return old_extent; + } + + // implementation details (compiler helpers) - begin + + // Index mapping for simple zero-based extent domain. + index<_Rank> _map_index(const index<_Rank>& _Index) const __GPU { + return _Index; + } + +private: + template<class _Tuple_type> + friend + _Tuple_type details::_Create_uninitialized_tuple() __GPU; + /// <summary> + /// Constructor. + /// </summary> + /// <param name=""> + /// Indicates that no initialization is necessary. + /// </param> + extent(details::_eInitializeState) __GPU {} + + // the store + int _M_base[_Rank]; +}; + +template <int _Rank, template <int> class _Tuple_type> +bool operator==(const _Tuple_type<_Rank>& _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + return details::_cmp_op_loop_helper<_Tuple_type<_Rank>, details::opEq>::func(_Lhs, _Rhs); +} + +template <int _Rank, template <int> class _Tuple_type> +bool operator!=(const _Tuple_type<_Rank>& _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + return !details::_cmp_op_loop_helper<_Tuple_type<_Rank>, details::opEq>::func(_Lhs, _Rhs); +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator+(const _Tuple_type<_Rank>& _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opAdd>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator-(const _Tuple_type<_Rank>& _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opSub>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator+(const _Tuple_type<_Rank>& _Lhs, typename _Tuple_type<_Rank>::value_type _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opAdd>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator+(typename _Tuple_type<_Rank>::value_type _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opAdd>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator-(const _Tuple_type<_Rank>& _Lhs, typename _Tuple_type<_Rank>::value_type _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opSub>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator-(typename _Tuple_type<_Rank>::value_type _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opSub>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator*(const _Tuple_type<_Rank>& _Lhs, typename _Tuple_type<_Rank>::value_type _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opMul>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator*(typename _Tuple_type<_Rank>::value_type _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opMul>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator/(const _Tuple_type<_Rank>& _Lhs, typename _Tuple_type<_Rank>::value_type _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opDiv>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator/(typename _Tuple_type<_Rank>::value_type _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opDiv>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator%(const _Tuple_type<_Rank>& _Lhs, typename _Tuple_type<_Rank>::value_type _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opMod>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} + +template <int _Rank, template <int> class _Tuple_type> +_Tuple_type<_Rank> operator%(typename _Tuple_type<_Rank>::value_type _Lhs, const _Tuple_type<_Rank>& _Rhs) __GPU +{ + _Tuple_type<_Rank> new_Tuple = details::_Create_uninitialized_tuple<_Tuple_type<_Rank>>(); + details::_arithmetic_op_loop_helper<_Tuple_type<_Rank>, opMod>::func(new_Tuple, _Lhs, _Rhs); + return new_Tuple; +} +/// <summary> +/// The tile_barrier class is a capability class that is only creatable by +/// the system, and passed to a tiled parallel_for_each lambda as part of +/// the tiled_index parameter. It provides wait methods whose purpose is +/// to synchronize execution of threads running within the thread +/// group (tile). +/// </summary> +class tile_barrier +{ +public: + /// <summary> + /// Copy Constructor. The tile_barrier class does not have a public + /// default constructor or assignment operator, only copy-constructor. + /// </summary> + /// <param name="_Other"> + /// The tile_barrier instance to be copied from. + /// </param> +#pragma warning( suppress : 4100 ) // unreferenced formal parameter + tile_barrier(const tile_barrier& _Other) __GPU {} + + /// <summary> + /// Blocks execution of all threads in a tile until all all threads in the tile have reached this call. + /// Ensures that memory accesses are visible to other threads in the thread tile, and are executed according to program order + /// </summary> + void wait() const __GPU_ONLY + { + __dp_d3d_all_memory_fence_with_tile_barrier(); + } + + /// <summary> + /// Blocks execution of all threads in a tile until all all threads in the tile have reached this call. + /// Ensures that memory accesses are visible to other threads in the thread tile, and are executed according to program order + /// </summary> + void wait_with_all_memory_fence() const __GPU_ONLY + { + __dp_d3d_all_memory_fence_with_tile_barrier(); + } + + /// <summary> + /// Blocks execution of all threads in a tile until all all threads in the tile have reached this call. + /// Ensures that global memory accesses are visible to other threads in the thread tile, and are executed according to program order + /// </summary> + void wait_with_global_memory_fence() const __GPU_ONLY + { + __dp_d3d_device_memory_fence_with_tile_barrier(); + } + + /// <summary> + /// Blocks execution of all threads in a tile until all all threads in the tile have reached this call. + /// Ensures that tile_static memory accesses are visible to other threads in the thread tile, and are executed according to program order + /// </summary> + void wait_with_tile_static_memory_fence() const __GPU_ONLY + { + __dp_d3d_tile_static_memory_fence_with_tile_barrier(); + } +}; + +/// <summary> +/// A _Tiled_index_base is the base class of all three kinds of tiled_index to +/// share the common code. +/// </summary> +template <int _Rank> class _Tiled_index_base +{ +public: + + _CPP_AMP_VERIFY_RANK(_Rank, tiled_index); + + static const int rank = _Rank; + + /// <summary> + /// An index that represents the global index within an extent. + /// </summary> + const index<rank> global; + + /// <summary> + /// An index that represents the relative index within the current tile of a tiled_extent. + /// </summary> + const index<rank> local; + + /// <summary> + /// An index that represents the coordinates of the current tile of a tiled_extent. + /// </summary> + const index<rank> tile; + + /// <summary> + /// An index that represents the global coordinates of the origin of the current tile within a tiled_extent. + /// </summary> + const index<rank> tile_origin; + + /// <summary> + /// An object which represents a barrier within the current tile of threads. + /// </summary> + const tile_barrier barrier; + + /// <summary> + /// A Constructor that initializes data members using the given values. + /// </summary> + /// <param name="_Global"> + /// The global index to be copied from + /// </param> + /// <param name="_Local"> + /// The local index to be copied from + /// </param> + /// <param name="_Tile"> + /// The tile index to be copied from + /// </param> + /// <param name="_Tile_origin"> + /// The tile origin to be copied from + /// </param> + /// <param name="_Barrier"> + /// The barrier to be copied from + /// </param> + _Tiled_index_base(const index<rank>& _Global, + const index<rank>& _Local, + const index<rank>& _Tile, + const index<rank>& _Tile_origin, + const tile_barrier& _Barrier) __GPU + : global(_Global), local(_Local), tile(_Tile), tile_origin(_Tile_origin), barrier(_Barrier) + {} + + /// <summary> + /// Copy Constructor. + /// </summary> + /// <param name="_Other"> + /// The tile_index instance to be copied from . + /// </param> + _Tiled_index_base(const _Tiled_index_base& _Other) __GPU + : global(_Other.global), + local(_Other.local), + tile(_Other.tile), + tile_origin(_Other.tile_origin), + barrier(_Other.barrier) + {} + + /// <summary> + /// Implicit conversion operator that converts a tiled_index into an index. + /// The implicit conversion converts to the .global index member. + /// </summary> + operator const index<rank>() const __GPU + { + return global; + } + +private: + _Tiled_index_base& operator=(const _Tiled_index_base&) __GPU; +}; + +/// <summary> +/// A tiled_index is a set of indices of 1 to 3 dimensions which have been +/// subdivided into 1-, 2-, or 3-dimensional tiles in a tiled_extent. It has +/// three specialized forms: tiled_index<_Dim0>, tiled_index<_Dim0, _Dim1>, and +/// tiled_index<_Dim0, _Dim1, _Dim2>, where _Dim0-2 specify the length of the tile along +/// the each dimension, with _Dim0 being the most-significant dimension and _Dim2 +/// being the least-significant. +/// </summary> +template <int _Dim0, int _Dim1 = 0, int _Dim2 = 0> class tiled_index : public _Tiled_index_base<3> +{ +public: + /// <summary> + /// A Constructor that initializes data members using the given values. + /// </summary> + /// <param name="_Global"> + /// The global index to be copied from + /// </param> + /// <param name="_Local"> + /// The local index to be copied from + /// </param> + /// <param name="_Tile"> + /// The tile index to be copied from + /// </param> + /// <param name="_Tile_origin"> + /// The tile origin to be copied from + /// </param> + /// <param name="_Barrier"> + /// The barrier to be copied from + /// </param> + tiled_index(const index<rank>& _Global, + const index<rank>& _Local, + const index<rank>& _Tile, + const index<rank>& _Tile_origin, + const tile_barrier& _Barrier) __GPU + : _Tiled_index_base(_Global, _Local, _Tile, _Tile_origin, _Barrier) + {} + + /// <summary> + /// Copy Constructor. + /// </summary> + /// <param name="_Other"> + /// The tile_index instance to be copied from . + /// </param> + tiled_index(const tiled_index& _Other) __GPU + : _Tiled_index_base(_Other) + {} + + /// <summary> + /// Returns an instance of an extent that captures the values of the tiled_index + /// template arguments _Dim0, _Dim1, _Dim2 + /// </summary> + __declspec(property(get=get_tile_extent)) extent<rank> tile_extent; + extent<rank> get_tile_extent() __GPU { return extent<rank>(_Dim0, _Dim1, _Dim2); } + + /// <summary> + /// These constants allow access to the template arguments of tiled_index. + /// </summary> + static const int tile_dim0 = _Dim0; + static const int tile_dim1 = _Dim1; + static const int tile_dim2 = _Dim2; + +private: + tiled_index& operator=(const tiled_index&) __GPU; +}; + +template <int _Dim0, int _Dim1> +class tiled_index<_Dim0, _Dim1, 0> : public _Tiled_index_base<2> +{ +public: + /// <summary> + /// A Constructor that initializes data members using the given values. + /// </summary> + /// <param name="_Global"> + /// The global index to be copied from + /// </param> + /// <param name="_Local"> + /// The local index to be copied from + /// </param> + /// <param name="_Tile"> + /// The tile index to be copied from + /// </param> + /// <param name="_Tile_origin"> + /// The tile origin to be copied from + /// </param> + /// <param name="_Barrier"> + /// The barrier to be copied from + /// </param> + tiled_index(const index<rank>& _Global, + const index<rank>& _Local, + const index<rank>& _Tile, + const index<rank>& _Tile_origin, + const tile_barrier& _Barrier) __GPU + : _Tiled_index_base(_Global, _Local, _Tile, _Tile_origin, _Barrier) + {} + + /// <summary> + /// Copy Constructor. + /// </summary> + /// <param name="_Other"> + /// The tile_index instance to be copied from . + /// </param> + tiled_index(const tiled_index& _Other) __GPU + : _Tiled_index_base(_Other) + {} + + /// <summary> + /// Returns an instance of an extent that captures the values of the tiled_index + /// template arguments _Dim0, _Dim1 + /// </summary> + __declspec(property(get=get_tile_extent)) extent<rank> tile_extent; + extent<rank> get_tile_extent() __GPU { return extent<rank>(_Dim0, _Dim1); } + + /// <summary> + /// These constants allow access to the template arguments of tiled_index. + /// </summary> + static const int tile_dim0 = _Dim0; + static const int tile_dim1 = _Dim1; + +private: + tiled_index& operator=(const tiled_index&) __GPU; +}; + +template <int _Dim0> +class tiled_index<_Dim0, 0, 0> : public _Tiled_index_base<1> +{ +public: + /// <summary> + /// A Constructor that initializes data members using the given values. + /// </summary> + /// <param name="_Global"> + /// The global index to be copied from + /// </param> + /// <param name="_Local"> + /// The local index to be copied from + /// </param> + /// <param name="_Tile"> + /// The tile index to be copied from + /// </param> + /// <param name="_Tile_origin"> + /// The tile origin to be copied from + /// </param> + /// <param name="_Barrier"> + /// The barrier to be copied from + /// </param> + tiled_index(const index<rank>& _Global, + const index<rank>& _Local, + const index<rank>& _Tile, + const index<rank>& _Tile_origin, + const tile_barrier& _Barrier) __GPU + : _Tiled_index_base(_Global, _Local, _Tile, _Tile_origin, _Barrier) + {} + + /// <summary> + /// Copy Constructor. + /// </summary> + /// <param name="_Other"> + /// The tile_index instance to be copied from . + /// </param> + tiled_index(const tiled_index& _Other) __GPU + : _Tiled_index_base(_Other) + {} + + /// <summary> + /// Returns an instance of an extent that captures the values of the tiled_index + /// template argument _Dim0 + /// </summary> + __declspec(property(get=get_tile_extent)) extent<rank> tile_extent; + extent<rank> get_tile_extent() __GPU { return extent<rank>(_Dim0); } + + /// <summary> + /// These constants allow access to the template arguments of tiled_index. + /// </summary> + static const int tile_dim0 = _Dim0; + +private: + tiled_index& operator=(const tiled_index&) __GPU; +}; + + +/// <summary> +/// A tiled_extent is an extent of 1 to 3 dimensions which also subdivides the extent space into +/// 1-, 2-, or 3-dimensional tiles. It has three specialized forms: tiled_extent<_Dim0>, +/// tiled_extent<_Dim0,_Dim1>, and tiled_extent<_Dim0,_Dim1,_Dim2>, where _Dim0-2 specify the length of the tile +/// along each dimension, with _Dim0 being the most-significant dimension and _Dim2 being the +/// least-significant. +/// </summary> +template <int _Dim0, int _Dim1 /*=0*/, int _Dim2 /*=0*/> class tiled_extent : public Concurrency::extent<3> +{ +public: + + static_assert(_Dim0>0, "_Dim0 must be positive"); + static_assert(_Dim1>0, "_Dim1 must be positive"); + static_assert(_Dim2>0, "_Dim2 must be positive"); + + /// <summary> + /// Default constructor. + /// </summary> + tiled_extent() __GPU {} + + /// <summary> + /// Constructs a new tiled_extent from the supplied extent. + /// </summary> + tiled_extent(const Concurrency::extent<rank>& _Other) __GPU : Concurrency::extent<rank>(_Other) + {} + + /// <summary> + /// Copy constructor. Constructs a new tiled_extent from the supplied argument "_Other". + /// </summary> + tiled_extent(const tiled_extent& _Other) __GPU : Concurrency::extent<rank>(_Other) + {} + + /// <summary> + /// copy-assignment operator + /// </summary> + tiled_extent& operator=(const tiled_extent& _Other) __GPU + { + Concurrency::extent<rank>::operator=(_Other); + return *this; + } + + /// <summary> + /// Returns an instance of an extent that captures the values of the tiled_extent + /// template arguments _Dim0, _Dim1, _Dim2. + /// </summary> + __declspec(property(get=get_tile_extent)) Concurrency::extent<rank> tile_extent; + Concurrency::extent<rank> get_tile_extent() const __GPU + { + return Concurrency::extent<rank>(_Dim0, _Dim1, _Dim2); + } + + /// <summary> + /// Returns a new tiled_extent with extents adjusted up to be evenly divisible by the tile dimensions. + /// </summary> + tiled_extent pad() const __GPU + { + Concurrency::extent<rank> _New_extent(((static_cast<unsigned int>((*this)[0]) + _Dim0 - 1)/_Dim0) * _Dim0, + ((static_cast<unsigned int>((*this)[1]) + _Dim1 - 1)/_Dim1) * _Dim1, + ((static_cast<unsigned int>((*this)[2]) + _Dim2 - 1)/_Dim2) * _Dim2); + + return tiled_extent<_Dim0,_Dim1,_Dim2>(_New_extent); + } + + /// <summary> + /// Returns a new tiled_extent with extents adjusted down to be evenly divisible by the tile dimensions. + /// </summary> + tiled_extent truncate() const __GPU + { + Concurrency::extent<rank> _New_extent(((*this)[0]/_Dim0) * _Dim0, ((*this)[1]/_Dim1) * _Dim1, ((*this)[2]/_Dim2) * _Dim2); + return tiled_extent<_Dim0,_Dim1,_Dim2>(_New_extent); + } + + /// <summary> + /// These constants allow access to the template arguments of tiled_extent. + /// </summary> + static const int tile_dim0 = _Dim0; + static const int tile_dim1 = _Dim1; + static const int tile_dim2 = _Dim2; + + // implementation details (compiler helpers) - begin + + // Given the local index, the tile index, the global index, in the 0-based domain that + // has same extents as 'this', and a barrier object, return a tiled_index<_Dim0, _Dim1, _Dim2> into + // the 'this' tiled_extent domain. + tiled_index<_Dim0, _Dim1, _Dim2> _map_index(const index<rank>& _Local, const index<rank>& _Tile, const index<rank>& _Global, tile_barrier& _Barrier) const __GPU + { + index<rank> _Tile_origin = details::_Create_uninitialized_tuple<index<rank>>(); + details::_arithmetic_op_loop_helper<index<rank>, details::opMul>::func(_Tile_origin, _Tile, tile_extent); + return tiled_index<_Dim0, _Dim1, _Dim2>(_Global, _Local, _Tile, _Tile_origin, _Barrier); + } + // implementation details (compiler helpers) - end +}; + + +template <int _Dim0, int _Dim1> +class tiled_extent<_Dim0, _Dim1, 0> : public Concurrency::extent<2> +{ +public: + + static_assert(_Dim0>0, "_Dim0 must be positive"); + static_assert(_Dim1>0, "_Dim1 must be positive"); + + /// <summary> + /// Default constructor. + /// </summary> + tiled_extent() __GPU {} + + /// <summary> + /// Constructs a new tiled_extent from the supplied extent. + /// </summary> + tiled_extent(const Concurrency::extent<rank>& _Other) __GPU : Concurrency::extent<rank>(_Other) + {} + + /// <summary> + /// Copy constructor. Constructs a new tiled_extent from the supplied argument "_Other". + /// </summary> + tiled_extent(const tiled_extent& _Other) __GPU : Concurrency::extent<rank>(_Other) + {} + + /// <summary> + /// copy-assignment operator + /// </summary> + tiled_extent& operator=(const tiled_extent& _Other) __GPU + { + Concurrency::extent<rank>::operator=(_Other); + return *this; + } + + /// <summary> + /// Returns an instance of an extent that captures the values of the tiled_extent + /// template arguments _Dim0, _Dim1. + /// </summary> + __declspec(property(get=get_tile_extent)) Concurrency::extent<rank> tile_extent; + Concurrency::extent<rank> get_tile_extent() const __GPU + { + return Concurrency::extent<rank>(_Dim0, _Dim1); + } + + /// <summary> + /// Returns a new tiled_extent with extents adjusted up to be evenly divisible by the tile dimensions. + /// </summary> + tiled_extent pad() const __GPU + { + Concurrency::extent<rank> _New_extent(((static_cast<unsigned int>((*this)[0]) + _Dim0 - 1)/_Dim0) * _Dim0, + ((static_cast<unsigned int>((*this)[1]) + _Dim1 - 1)/_Dim1) * _Dim1); + return tiled_extent<_Dim0,_Dim1>(_New_extent); + } + + /// <summary> + /// Returns a new tiled_extent with extents adjusted down to be evenly divisible by the tile dimensions. + /// </summary> + tiled_extent truncate() const __GPU + { + Concurrency::extent<rank> _New_extent(((*this)[0]/_Dim0) * _Dim0, ((*this)[1]/_Dim1) * _Dim1); + return tiled_extent<_Dim0,_Dim1>(_New_extent); + } + + /// <summary> + /// These constants allow access to the template arguments of tiled_extent. + /// </summary> + static const int tile_dim0 = _Dim0; + static const int tile_dim1 = _Dim1; + + // implementation details (compiler helpers) - begin + + // Given the local index, the tile index, the global index, in the 0-based domain that + // has same extents as 'this', and a barrier object, return a tiled_index<_Dim0, _Dim1> into + // the 'this' tiled_extent domain. + tiled_index<_Dim0, _Dim1> _map_index(const index<rank>& _Local, const index<rank>& _Tile, const index<rank>& _Global, tile_barrier& _Barrier) const __GPU + { + index<rank> _Tile_origin = details::_Create_uninitialized_tuple<index<rank>>(); + details::_arithmetic_op_loop_helper<index<rank>, details::opMul>::func(_Tile_origin, _Tile, tile_extent); + return tiled_index<_Dim0, _Dim1>(_Global, _Local, _Tile, _Tile_origin, _Barrier); + } + // implementation details (compiler helpers) - end +}; + +template <int _Dim0> +class tiled_extent<_Dim0, 0, 0> : public Concurrency::extent<1> +{ +public: + + static_assert(_Dim0>0, "_Dim0 must be positive"); + + /// <summary> + /// Default constructor. + /// </summary> + tiled_extent() __GPU {} + + /// <summary> + /// Constructs a new tiled_extent from the supplied extent. + /// </summary> + tiled_extent(const Concurrency::extent<rank>& _Other) __GPU : Concurrency::extent<rank>(_Other) + {} + + /// <summary> + /// Copy constructor. Constructs a new tiled_extent from the supplied argument "_Other". + /// </summary> + tiled_extent(const tiled_extent& _Other) __GPU : Concurrency::extent<rank>(_Other) + {} + + /// <summary> + /// copy-assignment operator + /// </summary> + tiled_extent& operator=(const tiled_extent& _Other) __GPU + { + Concurrency::extent<rank>::operator=(_Other); + return *this; + } + + /// <summary> + /// Returns an instance of an extent that captures the values of the tiled_extent + /// template argument _Dim0. + /// </summary> + __declspec(property(get=get_tile_extent)) Concurrency::extent<rank> tile_extent; + Concurrency::extent<rank> get_tile_extent() const __GPU + { + return Concurrency::extent<rank>(_Dim0); + } + + /// <summary> + /// Returns a new tiled_extent with extents adjusted up to be evenly divisible by the tile dimensions. + /// </summary> + tiled_extent pad() const __GPU + { + Concurrency::extent<rank> _New_extent(((static_cast<unsigned int>((*this)[0]) + _Dim0 - 1)/_Dim0) * _Dim0); + return tiled_extent<_Dim0>(_New_extent); + } + + /// <summary> + /// Returns a new tiled_extent with extents adjusted down to be evenly divisible by the tile dimensions. + /// </summary> + tiled_extent truncate() const __GPU + { + Concurrency::extent<rank> _New_extent(((*this)[0]/_Dim0) * _Dim0); + return tiled_extent<_Dim0>(_New_extent); + } + + /// <summary> + /// These constants allow access to the template arguments of tiled_extent. + /// </summary> + static const int tile_dim0 = _Dim0; + + // implementation details (compiler helpers) - begin + + // Given the local index, the tile index, the global index, in the 0-based domain that + // has same extents as 'this', and a barrier object, return a tiled_index<_Dim0> into + // the 'this' tiled_extent domain. + tiled_index<_Dim0> _map_index(const index<rank>& _Local, const index<rank>& _Tile, const index<rank>& _Global, tile_barrier& _Barrier) const __GPU + { + index<rank> _Tile_origin = details::_Create_uninitialized_tuple<index<rank>>(); + details::_arithmetic_op_loop_helper<index<rank>, details::opMul>::func(_Tile_origin, _Tile, tile_extent); + return tiled_index<_Dim0>(_Global, _Local, _Tile, _Tile_origin, _Barrier); + } +}; + +namespace details +{ + +template <int _Old_element_size, int _New_element_size> +int _Calculate_reinterpreted_size(int _Old_size) __GPU_ONLY +{ + int _Total_size = _Old_element_size * _Old_size; + int _New_size = (_Total_size + _New_element_size - 1)/ _New_element_size; + + return _New_size; +} + + +template <int _Old_element_size, int _New_element_size> +int _Calculate_reinterpreted_size(int _Old_size) __CPU_ONLY +{ + int _Total_size = _Old_element_size * _Old_size; + int _New_size = (_Total_size + _New_element_size - 1)/ _New_element_size; + + if (_New_size * _New_element_size > _Total_size) + throw runtime_exception("Element type of reinterpret_as does not evenly divide into extent", E_INVALIDARG); + + return _New_size; +} + + +// This class defines the shape of an array view and provides +// the functionality of translating dimensional indices into +// flat offsets into the underlying buffer +template <int _Rank, int _Element_size /* in number of ints */> +class _Array_view_shape +{ + typedef _Array_flatten_helper<_Rank, typename Concurrency::extent<_Rank>::value_type, typename Concurrency::index<_Rank>::value_type> _Flatten_helper; + friend class _Array_view_shape<_Rank+1, _Element_size>; + +public: + /// <summary> + /// The extent of this array or view. + /// </summary> + __declspec(property(get=get_extent)) Concurrency::extent<_Rank> extent; + Concurrency::extent<_Rank> get_extent() const __GPU + { + return _M_view_extent; + } + + ~_Array_view_shape() __GPU {} + +protected: + int _Base_linear_offset() const __GPU + { + return (_M_total_linear_offset - (_Element_size * _Flatten_helper::func(_M_array_multiplier._M_base, _M_view_offset._M_base))); + } + + _Array_view_shape(const _Array_view_shape& _Other) __GPU + : + _M_array_extent(_Other._M_array_extent), + _M_array_multiplier(_Other._M_array_multiplier), + _M_view_offset(_Other._M_view_offset), + _M_total_linear_offset(_Other._M_total_linear_offset), + _M_view_extent(_Other._M_view_extent) + { + } + + // For "section" + _Array_view_shape(const _Array_view_shape& _Other, const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU + : + _M_array_extent(_Other._M_array_extent), + _M_array_multiplier(_Other._M_array_multiplier), + _M_view_offset(_Other._M_view_offset + _Section_origin), + _M_view_extent(_Section_extent) + { + _Is_valid_section(_Other._M_view_extent, _Section_origin, _Section_extent); + + _M_total_linear_offset = _Other._Base_linear_offset() + (_Element_size * _Flatten_helper::func(_M_array_multiplier._M_base, _M_view_offset._M_base)); + } + + _Array_view_shape(int _Base_linear_offset, const Concurrency::extent<_Rank>& _Array_extent) __GPU + : + _M_array_extent(_Array_extent), + _M_view_offset(index<_Rank>()), + _M_total_linear_offset(_Base_linear_offset), + _M_view_extent(_Array_extent) + { + _Initialize_multiplier(); + } + + _Array_view_shape(int _Base_linear_offset, const Concurrency::extent<_Rank>& _Array_extent, + const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU + : + _M_array_extent(_Array_extent), + _M_view_offset(_Section_origin), + _M_total_linear_offset(_Base_linear_offset), + _M_view_extent(_Section_extent) + { + _Is_valid_section(_Array_extent, _Section_origin, _Section_extent); + + _Initialize_multiplier(); + _M_total_linear_offset += (_Element_size * _Flatten_helper::func(_M_array_multiplier._M_base, _M_view_offset._M_base)); + } + + _Array_view_shape& operator=(const _Array_view_shape &_Other) __GPU + { + _M_array_extent = _Other._M_array_extent; + _M_array_multiplier = _Other._M_array_multiplier; + _M_view_offset = _Other._M_view_offset; + _M_total_linear_offset = _Other._M_total_linear_offset; + _M_view_extent = _Other._M_view_extent; + return *this; + } + + void _Project0(int _I, _Array_view_shape<_Rank-1,_Element_size>& _Projected_shape) const __GPU + { + static_assert(_Rank > 1, "Projection is only supported on array_views with a rank of 2 or higher"); + + _Is_valid_projection(_I, this->_M_view_extent); + + typedef Concurrency::extent<_Rank-1> _RES_EXT; + typedef Concurrency::extent<_Rank> _SRC_EXT; + typedef Concurrency::index<_Rank-1> _RES_IDX; + typedef Concurrency::index<_Rank> _SRC_IDX; + details::_project0<_RES_EXT, _SRC_EXT, _RES_IDX, _SRC_IDX, _Rank>::func( + _Projected_shape._M_array_extent, this->_M_array_extent, + _Projected_shape._M_array_multiplier, this->_M_array_multiplier, + _Projected_shape._M_view_offset, this->_M_view_offset, + _Projected_shape._M_view_extent, this->_M_view_extent); + + _Projected_shape._M_total_linear_offset = _M_total_linear_offset + (_Element_size * _I * _M_array_multiplier[0]); + } + + _Array_view_shape() __GPU + : _M_array_extent(details::_do_not_initialize), _M_array_multiplier(details::_do_not_initialize), + _M_view_offset(details::_do_not_initialize), _M_view_extent(details::_do_not_initialize) + { + } + +private: + + void _Initialize_multiplier() __GPU + { + details::_Is_valid_extent(_M_array_extent); + unsigned int _Ext = _M_array_extent[_Rank-1]; + details::_Array_init_helper<Concurrency::extent<_Rank>, Concurrency::extent<_Rank>>::func(_Ext, _M_array_multiplier, _M_array_extent); + } + +protected: + Concurrency::extent<_Rank> _M_array_extent; + Concurrency::extent<_Rank> _M_array_multiplier; + Concurrency::index<_Rank> _M_view_offset; + int _M_total_linear_offset; // in number of units + Concurrency::extent<_Rank> _M_view_extent; +}; + +template <int _Rank, int _Element_size> +class _Array_view_base : public _Array_view_shape<_Rank,_Element_size /* in number of ints */> +{ + template <int _R, int _S> + friend class _Array_view_base; + +public: + + typedef details::_Buffer_descriptor _Buffer_descriptor; + + ~_Array_view_base() __GPU + { + // Unregister the view; Do not throw exception + _Unregister(false); + } + +protected: + + _Array_view_base() __GPU {} + + _Array_view_base(const _Buffer_descriptor& _Buffer_desc, const _Array_view_shape& _Shape) __GPU + : + _M_buffer_descriptor(_Buffer_desc), + _Array_view_shape<_Rank, _Element_size>(_Shape) + { + // Register the view + _Register(); + } + + _Array_view_base(const _Array_view_base& _Other) __GPU + : + _M_buffer_descriptor(_Other._M_buffer_descriptor), + _Array_view_shape<_Rank, _Element_size>(_Other) + { + // Register the view + _Register_copy(_Other); + } + + _Array_view_base(const _Array_view_base& _Other, const Concurrency::extent<_Rank>& _Array_extent) __GPU + : + _M_buffer_descriptor(_Other._M_buffer_descriptor), + _Array_view_shape<_Rank, _Element_size>(_Other._Base_linear_offset(), _Array_extent) + { + // Register the view + _Register(); + } + + _Array_view_base(const _Array_view_base& _Other, const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU + : + _M_buffer_descriptor(_Other._M_buffer_descriptor), + _Array_view_shape<_Rank, _Element_size>(_Other, _Section_origin, _Section_extent) + { + // Register the view + _Register(); + } + + _Array_view_base(const _Buffer_descriptor& _Buffer_desc, const Concurrency::extent<_Rank>& _Array_extent) __GPU + : + _M_buffer_descriptor(_Buffer_desc), + _Array_view_shape<_Rank, _Element_size>(0,_Array_extent) + { + // Register the view + _Register(); + } + + _Array_view_base(const _Buffer_descriptor& _Buffer_desc, int _Base_linear_offset, const Concurrency::extent<_Rank>& _Array_extent) __GPU + : + _M_buffer_descriptor(_Buffer_desc), + _Array_view_shape<_Rank, _Element_size>(_Base_linear_offset,_Array_extent) + { + // Register the view + _Register(); + } + + _Array_view_base( + const _Buffer_descriptor& _Buffer_desc, + int _Base_linear_offset, + const Concurrency::extent<_Rank>& _Array_extent, + const Concurrency::index<_Rank>& _View_offset, + const Concurrency::extent<_Rank>& _View_extent + ) __GPU + : + _M_buffer_descriptor(_Buffer_desc), + _Array_view_shape<_Rank, _Element_size>(_Base_linear_offset,_Array_extent,_View_offset,_View_extent) + { + // Register the view + _Register(); + } + + _Array_view_base(const _Buffer_descriptor& _Buffer_desc, const Concurrency::extent<_Rank>& _Array_extent, + const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU + : + _M_buffer_descriptor(_Buffer_desc), + _Array_view_shape<_Rank, _Element_size>(0,_Array_extent,_Section_origin,_Section_extent) + { + // Register the view + _Register(); + } + + _Array_view_base(const Concurrency::extent<_Rank>& _Array_extent) __CPU_ONLY + : + _Array_view_shape<_Rank, _Element_size>(0,_Array_extent) + { + _Ubiquitous_buffer_ptr _PUBuf = _Ubiquitous_buffer::_Create_ubiquitous_buffer(_Array_extent.size(), _Element_size * sizeof(int)); + _M_buffer_descriptor = _Buffer_descriptor(NULL, _PUBuf, _No_access, _No_access); + + // Register the view + _Register(); + } + + _Array_view_base(_In_ void * _Data, const Concurrency::extent<_Rank>& _Array_extent) __CPU_ONLY + : + _Array_view_shape<_Rank, _Element_size>(0,_Array_extent) + { + if (_Data == NULL) { + throw runtime_exception("Invalid pointer argument (NULL) to array_view constructor", E_INVALIDARG); + } + + _Buffer_ptr _PBuf = _Buffer::_Create_buffer(_Data, accelerator(accelerator::cpu_accelerator).default_view, _Array_extent.size(), _Element_size * sizeof(int)); + _Ubiquitous_buffer_ptr _PUBuf = _Ubiquitous_buffer::_Create_ubiquitous_buffer(_PBuf); + _M_buffer_descriptor = _Buffer_descriptor(_Data, _PUBuf, _Read_write_access, _Read_write_access); + + // Register the view + _Register(); + } + + _Array_view_base(_In_ void * _Data, const Concurrency::extent<_Rank>& _Array_extent) __GPU_ONLY + : + _Array_view_shape<_Rank, _Element_size>(0,_Array_extent), _M_buffer_descriptor(_Data, NULL, _Read_write_access, _Read_write_access) + { + } + + _Array_view_base(const void * _Data, const Concurrency::extent<_Rank>& _Array_extent) __CPU_ONLY + : + _Array_view_shape<_Rank, _Element_size>(0,_Array_extent) + { + if (_Data == NULL) { + throw runtime_exception("Invalid pointer argument (NULL) to array_view constructor", E_INVALIDARG); + } + + _Buffer_ptr _PBuf = _Buffer::_Create_buffer(const_cast<void*>(_Data), accelerator(accelerator::cpu_accelerator).default_view, _Array_extent.size(), _Element_size * sizeof(int)); + _Ubiquitous_buffer_ptr _PUBuf = _Ubiquitous_buffer::_Create_ubiquitous_buffer(_PBuf); + _M_buffer_descriptor = _Buffer_descriptor(const_cast<void*>(_Data), _PUBuf, _Read_access, _Read_access); + + // Register the view + _Register(); + } + + _Array_view_base(const void * _Data, const Concurrency::extent<_Rank>& _Array_extent) __GPU_ONLY + : +#pragma warning( push ) +#pragma warning( disable : 4880 ) + // Casting away constness in amp restricted scope might result in + // undefined behavior, therefore, the compiler will report a level 1 warning + // for it. But the following const_cast is harmless thus we are suppressing + // this warning just for this line. + _Array_view_shape<_Rank, _Element_size>(0,_Array_extent), _M_buffer_descriptor(const_cast<void*>(_Data), NULL, _Read_access, _Read_access) +#pragma warning( pop ) + { + } + + _Array_view_base& operator=(const _Array_view_base &_Other) __GPU + { + if (this != &_Other) + { + // Unregister the current view + _Unregister(); + + _M_buffer_descriptor = _Other._M_buffer_descriptor; + _Array_view_shape<_Rank, _Element_size>::operator=(_Other); + + // Register the new view + _Register_copy(_Other); + } + + return *this; + } + + _Ret_ void * _Access(const index<_Rank>& _Index) const __GPU + { + int * _Ptr = reinterpret_cast<int *>(_M_buffer_descriptor._M_data_ptr); + return &_Ptr[_M_total_linear_offset + (_Element_size * _Flatten_helper::func(_M_array_multiplier._M_base, _Index._M_base))]; + } + + _Ret_ void * _Access(_Access_mode _Requested_mode, const index<_Rank>& _Index) const __CPU_ONLY + { + // Refresh the data ptr if we do not have requested access + if ((_M_buffer_descriptor._M_curr_cpu_access_mode & _Requested_mode) != _Requested_mode) { + _M_buffer_descriptor._Get_CPU_access(_Requested_mode); + } + + return _Access(_Index); + } + + _Ret_ void * _Access(_Access_mode _Requested_mode, const index<_Rank>& _Index) const __GPU_ONLY + { + UNREFERENCED_PARAMETER(_Requested_mode); + + return _Access(_Index); + } + + _Array_view_base _Section(const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) const __GPU + { + auto _View = _Array_view_base(*this, _Section_origin, _Section_extent); + + // Register the constructed view with the section buffer view shape + _View._Register(_Array_view_base::_Create_section_buffer_shape(this->_M_buffer_descriptor, _Section_origin, _Section_extent)); + + return _View; + } + + _Array_view_base _Section(const index<_Rank>& _Idx) const __GPU + { + return _Section(_Idx, this->extent - _Idx); + } + + void _Project0(int _I, _Array_view_base<_Rank-1,_Element_size>& _Projected_view) const __GPU + { + _Projected_view._M_buffer_descriptor = this->_M_buffer_descriptor; + _Array_view_shape<_Rank, _Element_size>::_Project0(_I, _Projected_view); + + // Register the constructed view with the projection buffer view shape + _Projected_view._Register(_Array_view_base::_Create_projection_buffer_shape(this->_M_buffer_descriptor, 0, _I)); + } + + template <int _New_element_size> + _Array_view_base<_Rank,_New_element_size> _Reinterpret_as() const __GPU + { + static_assert(_Rank==1, "reinterpret_as is only permissible on array views of rank 1"); + int _New_size = _Calculate_reinterpreted_size<_Element_size,_New_element_size>(_M_view_extent.size()); + return _Array_view_base<_Rank,_New_element_size>(this->_M_buffer_descriptor, + _M_total_linear_offset, + Concurrency::extent<_Rank>(_New_size)); + } + + template <int _New_rank> + _Array_view_base<_New_rank, _Element_size> _View_as(const Concurrency::extent<_New_rank>& _View_extent) const __GPU + { + static_assert(_Rank==1, "view_as is only permissible on array views of rank 1"); + return _Array_view_base<_New_rank, _Element_size>(this->_M_buffer_descriptor, + _M_total_linear_offset, + _View_extent, + index<_New_rank>(), + _View_extent); + } + + _Ret_ _View_shape* _Create_buffer_view_shape() const __CPU_ONLY + { + unsigned int bufElemSize = static_cast<unsigned int>(_M_buffer_descriptor._Get_buffer_ptr()->_Get_master_buffer_elem_size()); + unsigned int elemSize = _Element_size * sizeof(int); + + size_t linearOffsetInBytes = _Base_linear_offset() * sizeof(int); + + size_t baseLSDExtentInBytes = _M_array_extent[_Rank - 1]; + baseLSDExtentInBytes *= elemSize; + + size_t viewLSDOffsetInBytes = _M_view_offset[_Rank - 1]; + viewLSDOffsetInBytes *= elemSize; + + size_t viewLSDExtentInBytes = _M_view_extent[_Rank - 1]; + viewLSDExtentInBytes *= elemSize; + + // The base array extent, view extent, and view offset must be compatible with the underlying + // buffer's element size + if (((linearOffsetInBytes % bufElemSize) != 0) || + ((baseLSDExtentInBytes % bufElemSize) != 0) || + ((viewLSDOffsetInBytes % bufElemSize) != 0) || + ((viewLSDExtentInBytes % bufElemSize) != 0)) + { + throw runtime_exception("The array_view base extent, view offset and/or view extent is incompatible with the underlying buffer", E_FAIL); + } + + // The shape to be passed to the underlying buffer for registration must be in terms of + // the element size of the buffer + _ASSERTE((linearOffsetInBytes / bufElemSize) <= UINT_MAX); + unsigned int linearOffset = static_cast<unsigned int>(linearOffsetInBytes / bufElemSize); + + unsigned int baseExtent[_Rank]; + unsigned int viewOffset[_Rank]; + unsigned int viewExtent[_Rank]; +#pragma warning( push ) +#pragma warning( disable : 6294 ) +#pragma warning( disable : 6201 ) // Index '-1' is out of valid index range '0' to '0' for possibly stack allocated buffer 'baseExtent'. + for (int i = 0; i < _Rank - 1; ++i) { + baseExtent[i] = _M_array_extent[i]; + viewOffset[i] = _M_view_offset[i]; + viewExtent[i] = _M_view_extent[i]; + } +#pragma warning( pop ) + + // The extent in the least significant dimension needs to be adjusted for + // difference in element size between the buffer and ourselves + _ASSERTE((baseLSDExtentInBytes / bufElemSize) <= UINT_MAX); + baseExtent[_Rank - 1] = static_cast<unsigned int>(baseLSDExtentInBytes / bufElemSize); + + _ASSERTE((viewLSDOffsetInBytes / bufElemSize) <= UINT_MAX); + viewOffset[_Rank - 1] = static_cast<unsigned int>(viewLSDOffsetInBytes / bufElemSize); + + _ASSERTE((viewLSDExtentInBytes / bufElemSize) <= UINT_MAX); + viewExtent[_Rank - 1] = static_cast<unsigned int>(viewLSDExtentInBytes / bufElemSize); + + return _View_shape::_Create_view_shape(_Rank, linearOffset, baseExtent, viewOffset, viewExtent); + } + +protected: + + // Underlying storage + _Buffer_descriptor _M_buffer_descriptor; + +private: + + void _Register() __CPU_ONLY + { + _M_buffer_descriptor._Get_buffer_ptr()->_Register_view(_M_buffer_descriptor._Get_view_key(), + accelerator(accelerator::cpu_accelerator).default_view, + _Create_buffer_view_shape()); + + if (_M_buffer_descriptor._M_curr_cpu_access_mode != _No_access) + { + _Buffer_ptr _PBuf; + _Get_access_async(_M_buffer_descriptor._Get_view_key(), + accelerator(accelerator::cpu_accelerator).default_view, + _M_buffer_descriptor._M_curr_cpu_access_mode, + _PBuf)._Get(); + + _M_buffer_descriptor._M_data_ptr = _PBuf->_Get_host_ptr(); + } + } + + void _Register_copy(const _Array_view_base &_Other) __CPU_ONLY + { + _M_buffer_descriptor._Get_buffer_ptr()->_Register_view_copy(_M_buffer_descriptor._Get_view_key(), _Other._M_buffer_descriptor._Get_view_key()); + } + + void _Register(_In_ void* _Shape) __CPU_ONLY + { + if (_Shape == NULL) { + return; + } + + // Unregister and register with the right shape + _Unregister(); + + _M_buffer_descriptor._Get_buffer_ptr()->_Register_view(_M_buffer_descriptor._Get_view_key(), + accelerator(accelerator::cpu_accelerator).default_view, + reinterpret_cast<_View_shape*>(_Shape)); + + if (_M_buffer_descriptor._M_curr_cpu_access_mode != _No_access) + { + _Buffer_ptr _PBuf; + _Get_access_async(_M_buffer_descriptor._Get_view_key(), + accelerator(accelerator::cpu_accelerator).default_view, + _M_buffer_descriptor._M_curr_cpu_access_mode, + _PBuf)._Get(); + + _M_buffer_descriptor._M_data_ptr = _PBuf->_Get_host_ptr(); + } + } + + void _Unregister(bool _Throw_exception = true) __CPU_ONLY + { + if (!_Throw_exception && (std::current_exception() == nullptr)) { + _Throw_exception = true; + } + + try + { + _M_buffer_descriptor._Get_buffer_ptr()->_Unregister_view(_M_buffer_descriptor._Get_view_key()); + } + catch(...) + { + if (_Throw_exception) { + throw; + } + } + } + + static _Ret_ void* _Create_projection_buffer_shape(const _Buffer_descriptor& _Descriptor, unsigned int _Dim, int _Dim_offset) __CPU_ONLY + { + _View_shape* _Base_shape = _Get_buffer_view_shape(_Descriptor); + + std::vector<unsigned int> _New_view_extent(_Base_shape->_Get_rank()); + std::vector<unsigned int> _New_view_offset(_Base_shape->_Get_rank()); + bool *_New_projection_info = new bool[_Base_shape->_Get_rank()]; + for (unsigned int _I = 0; _I < _Base_shape->_Get_rank(); ++_I) + { + _New_view_extent[_I] = _Base_shape->_Get_view_extent()[_I]; + _New_view_offset[_I] = _Base_shape->_Get_view_offset()[_I]; + _New_projection_info[_I] = _Base_shape->_Get_projection_info()[_I]; + } + + // The _Dim'th non-projected dimension needs to be found + unsigned int _UnProjectedDimCount = 0; + for (unsigned int _I = 0; _I < _Base_shape->_Get_rank(); ++_I) + { + if (_Base_shape->_Get_projection_info()[_I]) { + continue; + } + + if (_UnProjectedDimCount == _Dim) { + _New_view_extent[_I] = 1; + _New_view_offset[_I] += _Dim_offset; + _New_projection_info[_I] = true; + break; + } + else { + _UnProjectedDimCount++; + } + } + + auto _PView_shape = _View_shape::_Create_view_shape(_Base_shape->_Get_rank(), + _Base_shape->_Get_linear_offset(), + _Base_shape->_Get_base_extent(), + _New_view_offset.data(), + _New_view_extent.data(), + _New_projection_info); + + delete [] _New_projection_info; + + return _PView_shape; + } + + static _Ret_ void* _Create_section_buffer_shape(const _Buffer_descriptor& _Descriptor, + const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __CPU_ONLY + { + _View_shape* _Base_shape = _Get_buffer_view_shape(_Descriptor); + if (_Base_shape->_Get_rank() == _Rank) { + return NULL; + } + + std::vector<unsigned int> _New_view_extent(_Base_shape->_Get_rank()); + std::vector<unsigned int> _New_view_offset(_Base_shape->_Get_rank()); + unsigned int _I = 0, _J = 0; + while (_I < _Base_shape->_Get_rank()) + { + if (_Base_shape->_Get_projection_info()[_I]) + { + _New_view_extent[_I] = _Base_shape->_Get_view_extent()[_I]; + _New_view_offset[_I] = _Base_shape->_Get_view_offset()[_I]; + } + else + { + // If _J is the least significant dimension, then we need to adjust the + // offset and extent for the underlying buffer's element size + if (_J == (_Rank - 1)) + { + unsigned int bufElemSize = static_cast<unsigned int>(_Descriptor._Get_buffer_ptr()->_Get_master_buffer_elem_size()); + unsigned int elemSize = _Element_size * sizeof(int); + + size_t sectionLSDOriginInBytes = _Section_origin[_J]; + sectionLSDOriginInBytes *= elemSize; + + size_t sectionLSDExtentInBytes = _Section_extent[_J]; + sectionLSDExtentInBytes *= elemSize; + + // The section offset and extent must be compatible with the underlying + // buffer's element size + if (((sectionLSDOriginInBytes % bufElemSize) != 0) || + ((sectionLSDExtentInBytes % bufElemSize) != 0)) + { + throw runtime_exception("The array_view section origin and/or extent is incompatible with the underlying buffer", E_FAIL); + } + + // The extent in the least significant dimension needs to be adjusted for + // difference in element size between the buffer and ourselves + _ASSERTE((sectionLSDOriginInBytes / bufElemSize) <= UINT_MAX); + _New_view_offset[_I] = _Base_shape->_Get_view_offset()[_I] + static_cast<unsigned int>(sectionLSDOriginInBytes / bufElemSize); + + _ASSERTE((sectionLSDExtentInBytes / bufElemSize) <= UINT_MAX); + _New_view_extent[_I] = static_cast<unsigned int>(sectionLSDExtentInBytes / bufElemSize); + } + else + { + _New_view_extent[_I] = _Section_extent[_J]; + _New_view_offset[_I] = _Base_shape->_Get_view_offset()[_I] + _Section_origin[_J]; + } + + _J++; + } + + _I++; + } + + _ASSERTE(_J == _Rank); + + return _View_shape::_Create_view_shape(_Base_shape->_Get_rank(), + _Base_shape->_Get_linear_offset(), + _Base_shape->_Get_base_extent(), + _New_view_offset.data(), + _New_view_extent.data(), + _Base_shape->_Get_projection_info()); + } + + void _Register() __GPU_ONLY {} + + void _Register_copy(const _Array_view_base &_Other) __GPU_ONLY + { + UNREFERENCED_PARAMETER(_Other); + } + + void _Register(_In_ void* _Shape) __GPU_ONLY + { + UNREFERENCED_PARAMETER(_Shape); + } + + void _Unregister(bool _Throw_exception = true) __GPU_ONLY + { + UNREFERENCED_PARAMETER(_Throw_exception); + } + + static _Ret_ void* _Create_projection_buffer_shape(const _Buffer_descriptor& _Descriptor, int _Dim, int _I) __GPU_ONLY + { + UNREFERENCED_PARAMETER(_Descriptor); + UNREFERENCED_PARAMETER(_Dim); + UNREFERENCED_PARAMETER(_I); + + return NULL; + } + + static _Ret_ void* _Create_section_buffer_shape(const _Buffer_descriptor& _Descriptor, const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU_ONLY + { + UNREFERENCED_PARAMETER(_Descriptor); + UNREFERENCED_PARAMETER(_Section_origin); + UNREFERENCED_PARAMETER(_Section_extent); + + return NULL; + } +}; + +template<typename _Container> +struct _Is_container +{ + template<class _Uty> static auto _Fn(_Uty _Val, decltype(_Val.size(), _Val.data(), 0)) -> std::true_type; + template<class _Uty> static auto _Fn(_Uty _Val, ...) -> std::false_type; + typedef decltype(_Fn(std::declval<_Container>(),0)) type; +}; + +} // namespace details + + +/// <summary> +/// An array_view is an N-dimensional view over data held in another container (such as array<T,N> +/// or other container. It exposes an indexing interface congruent to that of array<T,N>). +/// </summary> +/// <param name="_Rank"> +/// The number of dimensions of this array_view. +/// </param> +/// <param name="_Value_type"> +/// The type of the element. +/// </param> +template <typename _Value_type, int _Rank = 1> class array_view : public _Array_view_base<_Rank, sizeof(_Value_type)/sizeof(int)> +{ + typedef _Array_view_base<_Rank, sizeof(_Value_type)/sizeof(int)> _Base; + + _CPP_AMP_VERIFY_RANK(_Rank, array_view); + static_assert(0 == (sizeof(_Value_type) % sizeof(int)), "only value types whose size is a multiple of the size of an integer are allowed in array views"); + + friend class details::_Array_view_projection_helper<_Value_type,_Rank>; + friend class details::_Array_view_projection_helper<_Value_type,_Rank+1>; + + friend class array_view<_Value_type, _Rank>; + friend class array_view<const _Value_type, _Rank>; + + friend class array_view<_Value_type, _Rank+1>; + friend class array_view<const _Value_type, _Rank+1>; + + template <typename _T, int _R> + friend class array; + + friend const _Buffer_descriptor& details::_Get_buffer_descriptor<array_view<_Value_type, _Rank>>(const array_view<_Value_type, _Rank>& _Array) __GPU; + +public: + static const int rank = _Rank; + typedef typename _Value_type value_type; + + + /// <summary> + /// Destroys this array_view and reclaims resources. + /// </summary> + ~array_view() __GPU {} + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src array. The extent of the + /// array_view is that of the _Src array, and the origin of the array view is at zero. + /// </summary> + /// <param name="_Src"> + /// An array which contains the data that this array_view is bound to. + /// </param> + array_view(array<_Value_type,_Rank>& _Src) __GPU + : _Base(_Get_buffer_descriptor(_Src), _Src.extent) + { + _Initialize(); + } + + /// <summary> + /// Copy constructor. Shallow copy. + /// </summary> + array_view(const array_view& _Other) __GPU + : _Base(_Other) + { + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is not bound to a data source. + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + explicit array_view(const Concurrency::extent<_Rank>& _Extent) __CPU_ONLY + :_Base(_Extent) + { + _Initialize(_Extent.size(), true); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> array_view(const Concurrency::extent<_Rank>& _Extent, _Container& _Src) __CPU_ONLY + :_Base(_Src.data(),_Extent) + { + static_assert( std::is_same<decltype(_Src.data()), _Value_type*>::value, "container element type and array view element type must match"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + /// <param name="_Src"> + /// A pointer to the source data this array_view will bind to. If the number of elements pointed to + /// by _Src is less than the size of _Extent, undefined behavior results. + /// </param> + array_view(const Concurrency::extent<_Rank>& _Extent, _Value_type * _Src) __GPU + :_Base(_Src,_Extent) + { + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is not bound to a data source. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array_view. + /// </param> + explicit array_view(int _E0) __CPU_ONLY + :_Base(Concurrency::extent<1>(_E0)) + { + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(get_extent().size(), true); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + //// The length of the array_view is the same as the length of the container + /// </summary> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> explicit array_view(_Container& _Src, typename std::enable_if<details::_Is_container<_Container>::type::value,void **>::type = 0) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<1>(static_cast<int>(_Src.size()))) + { + if (_Src.size() > INT_MAX) { + throw runtime_exception("Invalid _Src container argument - _Src size is greater than INT_MAX", E_INVALIDARG); + } + static_assert( std::is_same<decltype(_Src.data()), _Value_type*>::value, "container element type and array view element type must match"); + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> explicit array_view(int _E0, _Container& _Src) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<1>(_E0)) + { + static_assert( std::is_same<decltype(_Src.data()), _Value_type*>::value, "container element type and array view element type must match"); + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is not bound to a data source. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + explicit array_view(int _E0, int _E1) __CPU_ONLY + :_Base(Concurrency::extent<2>(_E0,_E1)) + { + static_assert(_Rank == 2, "rank must be 2"); + _Initialize(get_extent().size(), true); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> explicit array_view(int _E0, int _E1, _Container& _Src) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<2>(_E0,_E1)) + { + static_assert( std::is_same<decltype(_Src.data()), _Value_type*>::value, "container element type and array view element type must match"); + static_assert(_Rank == 2, "rank must be 2"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is not bound to a data source. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array_view. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + explicit array_view(int _E0, int _E1, int _E2) __CPU_ONLY + :_Base(Concurrency::extent<3>(_E0,_E1,_E2)) + { + static_assert(_Rank == 3, "rank must be 3"); + _Initialize(get_extent().size(), true); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array_view. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> explicit array_view(int _E0, int _E1, int _E2, _Container& _Src) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<3>(_E0,_E1,_E2)) + { + static_assert( std::is_same<decltype(_Src.data()), _Value_type*>::value, "container element type and array view element type must match"); + static_assert(_Rank == 3, "rank must be 3"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0, undefined behavior results. + /// </param> + explicit array_view(int _E0, _In_ _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<1>(_E0)) + { + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the array _Src. + /// </summary> + /// <param name="_Src"> + /// An array which contains the data that this array_view is bound to. + /// </param> + template <typename _Arr_type, int _Size> explicit array_view(_In_ _Arr_type (&_Src) [_Size]) __GPU + :_Base(_Src, Concurrency::extent<1>(_Size)) + { + static_assert( std::is_same<typename std::remove_reference<decltype(*_Src)>::type, _Value_type>::value, "container element type and array view element type must match"); + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0*_E1, undefined behavior results. + /// </param> + explicit array_view(int _E0, int _E1, _In_ _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<2>(_E0,_E1)) + { + static_assert(_Rank == 2, "rank must be 2"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array_view. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0*_E1*_E2, undefined behavior results. + /// </param> + explicit array_view(int _E0, int _E1, int _E2, _In_ _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<3>(_E0,_E1,_E2)) + { + static_assert(_Rank == 3, "rank must be 3"); + _Initialize(); + } + + /// <summary> + /// Copy Assignment operator. Shallow copy. + /// </summary> + array_view& operator=(const array_view& _Other) __GPU + { + _Base::operator=(_Other); + return *this; + } + + /// <summary> + /// Copies elements from this array_view to the destination array. + /// </summary> + void copy_to(array<_Value_type,_Rank>& _Dest) const __CPU_ONLY + { + copy(*this,_Dest); + } + + /// <summary> + /// Copies elements from this array_view to the destination array_view. + /// </summary> + void copy_to(const array_view<_Value_type,_Rank>& _Dest) const __CPU_ONLY + { + copy(*this,_Dest); + } + + /// <summary> + /// Projects the most-significant dimension of this array_view. If the array_view rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Result_type operator[] (int _I) const __GPU + { + return details::_Array_view_projection_helper<_Value_type,_Rank>::_Project0(this, _I); + } + + /// <summary> + /// Get a reference to the element indexed by _Index. Unlike the other indexing operators for accessing the + /// array_view on the CPU, this method does not implicitly synchronize this array_view's contents to the CPU. + /// After accessing the array_view on a remote location or performing a copy operation involving this array_view + /// users are responsible to explicitly synchronize the array_view to the CPU before calling this method. + /// Failure to do so results in undefined behavior. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// Reference to the element indexed by _Index + /// </returns> + value_type& get_ref(const index<_Rank>& _Index) const __GPU + { + void *_Ptr = _Access(_Index); + return *reinterpret_cast<value_type*>(_Ptr); + } + + /// <summary> + /// Get the element value indexed by _I + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I + /// </returns> + value_type& operator[] (const index<_Rank>& _Index) const __GPU + { + return this->operator()(_Index); + } + + /// <summary> + /// Get the element value indexed by _I + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I + /// </returns> + value_type& operator() (const index<_Rank>& _Index) const __GPU + { + void * _Ptr = _Access(_Read_write_access, _Index); + return *reinterpret_cast<value_type*>(_Ptr); + } + + /// <summary> + /// Projects the most-significant dimension of this array_view. If the array_view rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Result_type operator() (int _I) const __GPU + { + return details::_Array_view_projection_helper<_Value_type,_Rank>::_Project0(this, _I); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1) + /// </returns> + value_type& operator() (int _I0, int _I1) const __GPU + { + static_assert(_Rank == 2, "value_type& array_view::operator()(int,int) is only permissible on array_view<T, 2>"); + return this->operator()(index<2>(_I0,_I1)); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1,_I2) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the index + /// </param> + /// <param name="_I2"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1,_I2) + /// </returns> + value_type& operator() (int _I0, int _I1, int _I2) const __GPU + { + static_assert(_Rank == 3, "value_type& array_view::operator()(int,int,int) is only permissible on array_view<T, 3>"); + return this->operator()(index<3>(_I0,_I1,_I2)); + } + + /// <summary> + /// Produces a subsection of the source array_view at the given origin and extent. + /// </summary> + /// <param name="_Section_origin"> + /// The origin of the section. + /// </param> + /// <param name="_Section_extent"> + /// The extent of the section + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) const __GPU + { + return _Convert<_Value_type>(_Section(_Section_origin, _Section_extent)); + } + + /// <summary> + /// Produces a subsection of the source array_view with origin specified by an index, with + /// an extent of (this->exent - _Idx). + /// </summary> + /// <param name="_Idx"> + /// The index that specifies the origin of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(const Concurrency::index<_Rank>& _Idx) const __GPU + { + return section(_Idx, this->extent - _Idx); + } + + /// <summary> + /// Produces a subsection of the source array_view with origin of zero, with + /// an extent of _Ext. + /// </summary> + /// <param name="_Ext"> + /// The extent of this section + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(const Concurrency::extent<_Rank>& _Ext) const __GPU + { + return section(Concurrency::index<_Rank>(), _Ext); + } + + /// <summary> + /// Produces a one-dimensional subsection of the source array_view with origin specified by the index + /// components _I0, with extent _E0. + /// </summary> + /// <param name="_I0"> + /// The origin of this section. + /// </param> + /// <param name="_E0"> + /// The extent of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(int _I0, int _E0) const __GPU + { + static_assert(_Rank == 1, "rank must be 1"); + return section(Concurrency::index<1>(_I0), Concurrency::extent<1>(_E0)); + } + + /// <summary> + /// Produces a two-dimensional subsection of the source array_view with origin specified by the index + /// components (_I0,_I1), with extent (_E0,_E1). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(int _I0, int _I1, int _E0, int _E1) const __GPU + { + static_assert(_Rank == 2, "rank must be 2"); + return section(Concurrency::index<2>(_I0,_I1), Concurrency::extent<2>(_E0,_E1)); + } + + /// <summary> + /// Produces a three-dimensional subsection of the source array_view with origin specified by the index + /// components (_I0,_I1,_I2), with extent (_E0,_E1,_E2). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the origin of this section. + /// </param> + /// <param name="_I2"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The next-to-most-significant component of the extent of this section. + /// </param> + /// <param name="_E2"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(int _I0, int _I1, int _I2, int _E0, int _E1, int _E2) const __GPU + { + static_assert(_Rank == 3, "rank must be 3"); + return section(Concurrency::index<3>(_I0,_I1,_I2), Concurrency::extent<3>(_E0,_E1,_E2)); + } + + /// <summary> + /// Produces a (possibly unsafe) reinterpretation of this array_view that is linear and with + /// a different element type. The size of _Value_type2 must evenly divide into the size of + /// this array. + /// </summary> + /// <returns> + /// A linear array_view with a reinterpreted element type. + /// </returns> + template <typename _Value_type2> array_view<_Value_type2, _Rank> reinterpret_as() const __GPU + { + return _Convert<_Value_type2>(this->template _Reinterpret_as<sizeof(_Value_type2)/sizeof(int)>()); + } + + /// <summary> + /// Produces an array_view of a different rank over this array_view's data. + /// </summary> + /// <param name="_View_extent"> + /// The reshaping extent. + /// </param> + /// <returns> + /// A reshaped array_view. + /// </returns> + template <int _New_rank> array_view<_Value_type,_New_rank> view_as(const Concurrency::extent<_New_rank>& _View_extent) const __GPU + { + return _Convert<_Value_type>(_View_as(_View_extent)); + } + + /// <summary> + /// Returns a pointer to the raw data of this array_view. + /// </summary> + _Ret_ _Value_type* data() const __GPU + { + static_assert(_Rank == 1, "array_view::data() is only permissible on array_view<T, 1>"); + return &this->operator[](index<_Rank>()); + } + + /// <summary> + /// Informs the array_view that its bound memory has been modified outside + /// the array_view interface. This will render all cached information stale. + /// </summary> + void refresh() const __CPU_ONLY + { + // If the array_view corresponds to a ubiquitous buffer with no data source, + // then refresh is a no-op + if (!_M_buffer_descriptor._Get_buffer_ptr()->_Has_data_source()) { + return; + } + + _Buffer_ptr _PBuf; + _Get_access_async(_M_buffer_descriptor._Get_view_key(), _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(), _Write_access, _PBuf)._Get(); + } + + /// <summary> + /// Asynchronously synchronizes any modifications made to "this" array_view to the specified accelerator_view. + /// </summary> + /// <param name="_Accl_view"> + /// The target accelerator_view to synchronize to. + /// </param> + /// <param name="_Access_type"> + /// The desired access_type on the target accelerator_view. + /// This parameter has a default value of access_type_read. + /// </param> + /// <returns> + /// A future upon which to wait for the operation to complete. + /// </returns> + concurrency::completion_future synchronize_to_async(const accelerator_view& _Accl_view, access_type _Access_type = access_type_read) const __CPU_ONLY + { + auto _Async_op_id = details::_Get_amp_trace()->_Launch_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + _Event _Ev; + + if (_Access_type != access_type_none) { + _Ev = _Get_access_async(_M_buffer_descriptor._Get_view_key(), _Accl_view, _Get_synchronize_access_mode(_Access_type), _PBuf); + } + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); + } + + /// <summary> + /// Asynchronously synchronizes any modifications made to "this" array_view to its source data. + /// </summary> + /// <param name="_Access_type"> + /// The desired access_type on the target accelerator_view. + /// This parameter has a default value of access_type_read. + /// </param> + /// <returns> + /// A future upon which to wait for the operation to complete. + /// </returns> + concurrency::completion_future synchronize_async(access_type _Access_type = access_type_read) const __CPU_ONLY + { + auto _Async_op_id = details::_Get_amp_trace()->_Launch_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + _Event _Ev; + + // If the array_view corresponds to a ubiquitous buffer with no data source, then synchronize is a no-op + if ((_Access_type != access_type_none) && _M_buffer_descriptor._Get_buffer_ptr()->_Has_data_source()) + { + _Ev = _Get_access_async(_M_buffer_descriptor._Get_view_key(), + _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(), + _Get_synchronize_access_mode(_Access_type), + _PBuf); + } + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); + } + + /// <summary> + /// Synchronizes any modifications made to "this" array_view to the specified accelerator_view. + /// </summary> + /// <param name="_Accl_view"> + /// The target accelerator_view to synchronize to. + /// </param> + /// <param name="_Access_type"> + /// The desired access_type on the target accelerator_view. + /// This parameter has a default value of access_type_read. + /// </param> + void synchronize_to(const accelerator_view& _Accl_view, access_type _Access_type = access_type_read) const __CPU_ONLY + { + auto _Span_id = details::_Get_amp_trace()->_Start_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + + if (_Access_type != access_type_none) { + _Get_access_async(_M_buffer_descriptor._Get_view_key(), _Accl_view, _Get_synchronize_access_mode(_Access_type), _PBuf)._Get(); + } + + details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + /// <summary> + /// Synchronizes any modifications made to "this" array_view to its source data. + /// </summary> + /// <param name="_Access_type"> + /// The desired access_type on the target accelerator_view. + /// This parameter has a default value of access_type_read. + /// </param> + void synchronize(access_type _Access_type = access_type_read) const __CPU_ONLY + { + auto _Span_id = details::_Get_amp_trace()->_Start_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + + // If the array_view corresponds to a ubiquitous buffer with no data source, then synchronize is a no-op + if ((_Access_type != access_type_none) && _M_buffer_descriptor._Get_buffer_ptr()->_Has_data_source()) + { + _Get_access_async(_M_buffer_descriptor._Get_view_key(), + _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(), + _Get_synchronize_access_mode(_Access_type), + _PBuf)._Get(); + } + + details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + /// <summary> + /// Discards the current data underlying this view. This is an optimization + /// hint to the runtime used to avoid copying the current contents of the view to a target + /// accelerator_view that it is accessed on, and its use is recommended if the existing + /// content is not needed. This method is only available in a restrict(cpu) context and + /// cannot be used in a restrict(amp) context. + /// </summary> + void discard_data() const __CPU_ONLY + { + _M_buffer_descriptor._Get_buffer_ptr()->_Discard(_M_buffer_descriptor._Get_view_key()); + } + + /// <summary> + /// Returns the accelerator_view where the data source of the array_view is located. + /// If the array_view does not have a data source, this API throws a runtime_exception + /// </summary> + accelerator_view get_source_accelerator_view() const + { + if (_M_buffer_descriptor._Get_buffer_ptr()->_Has_data_source()) { + return _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(); + } + else { + throw runtime_exception("Cannot query source accelerator_view for an array_view without a data source.", E_INVALIDARG); + } + } + + __declspec(property(get=get_source_accelerator_view)) accelerator_view source_accelerator_view; + +private: + template <typename _T, int _R> + static array_view<_T,_R> _Convert(const _Array_view_base<_R,sizeof(_T)/sizeof(int)>& _Other) __GPU + { + static_assert(sizeof(array_view<_T,_R>) == sizeof(_Array_view_base<_R,sizeof(_T)/sizeof(int)>), "ASSERT FAILURE: implementation relies on binary conversion between the two"); + return (*reinterpret_cast<const array_view<_T,_R>*>(&_Other)); + } + + void _Project0(int _I, array_view<_Value_type, _Rank-1> &_Projected_view) const __GPU + { + _Base::_Project0(_I, _Projected_view); + _Projected_view._Initialize(); + } + + array_view() __GPU {} + + array_view(const array_view& _Other, const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU + :_Base(_Other, _Section_origin, _Section_extent) + { + _Initialize(); + } + + array_view(_Buffer_descriptor& _Src_buffer, const Concurrency::extent<_Rank>& _Extent) __GPU + :_Base(_Src_buffer,_Extent) + { + _Initialize(); + } + + void _Initialize() __GPU + { + // Set the type access mode + _M_buffer_descriptor._M_type_access_mode = _Read_write_access; + } + + void _Initialize(size_t _Src_data_size, bool _Discard_data = false) __CPU_ONLY + { + // Ensure that the _Src_data_size is at least as big as the size + // of the array_view + if (_Src_data_size < this->extent.size()) { + throw runtime_exception("Invalid _Src container argument - _Src size is less than the size of the array_view.", E_INVALIDARG); + } + + _Initialize(); + + if (_Discard_data) { + discard_data(); + } + } + +}; // class array_view<T,R> + +// array_view<const T,R> +template <typename _Value_type, int _Rank> +class array_view<const _Value_type, _Rank> : public _Array_view_base<_Rank, sizeof(_Value_type)/sizeof(int)> +{ + _CPP_AMP_VERIFY_RANK(_Rank, array_view); + static_assert(0 == (sizeof(_Value_type) % sizeof(int)), "only value types whose size is a multiple of the size of an integer are allowed in array views"); + + typedef _Array_view_base<_Rank, sizeof(_Value_type)/sizeof(int)> _Base; + + friend class details::_Const_array_view_projection_helper<_Value_type,_Rank>; + friend class details::_Const_array_view_projection_helper<_Value_type,_Rank+1>; + + friend class array_view<_Value_type, _Rank>; + friend class array_view<const _Value_type, _Rank>; + + friend class array_view<_Value_type, _Rank+1>; + friend class array_view<const _Value_type, _Rank+1>; + + friend const _Buffer_descriptor& details::_Get_buffer_descriptor<array_view<const _Value_type, _Rank>>(const array_view<const _Value_type, _Rank>& _Array) __GPU; + +public: + static const int rank = _Rank; + typedef typename const _Value_type value_type; + + /// <summary> + /// Destroys this array_view and reclaims resources. + /// </summary> + ~array_view() __GPU {} + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src array. The extent of the + /// array_view is that of the _Src array, and the origin of the array view is at zero. + /// </summary> + /// <param name="_Src"> + /// An array which contains the data that this array_view is bound to. + /// </param> + array_view(const array<_Value_type,_Rank>& _Src) __GPU + :_Base(_Get_buffer_descriptor(_Src), _Src.extent) + { + _Initialize(); + } + + /// <summary> + /// Copy constructor. Shallow copy. + /// </summary> + array_view(const array_view<_Value_type,_Rank>& _Src) __GPU + :_Base(_Src) + { + _Initialize(); + } + + /// <summary> + /// Copy constructor. Shallow copy. + /// </summary> + array_view(const array_view<const _Value_type,_Rank>& _Src) __GPU + :_Base(_Src) + { + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> array_view(const Concurrency::extent<_Rank>& _Extent, const _Container& _Src) __CPU_ONLY + :_Base(_Src.data(),_Extent) + { + static_assert( std::is_same<typename std::remove_const<typename std::remove_reference<decltype(*_Src.data())>::type>::type, _Value_type>::value, "container element type and array view element type must match"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container; + //// The length of the array_view is the same as the length of the container + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> explicit array_view(const _Container& _Src, typename std::enable_if<details::_Is_container<_Container>::type::value,void **>::type = 0) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<1>(static_cast<int>(_Src.size()))) + { + if (_Src.size() > INT_MAX) { + throw runtime_exception("Invalid _Src container argument - _Src size is greater than INT_MAX", E_INVALIDARG); + } + static_assert( std::is_same<decltype(_Src.data()), const _Value_type*>::value, "container element type and array view element type must match"); + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> array_view(const Concurrency::extent<_Rank>& _Extent, _Container& _Src) __CPU_ONLY + :_Base(_Src.data(),_Extent) + { + static_assert( std::is_same<typename std::remove_const<typename std::remove_reference<decltype(*_Src.data())>::type>::type, _Value_type>::value, "container element type and array view element type must match"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + /// <param name="_Src"> + /// A pointer to the source data this array_view will bind to. If the number of elements pointed to + /// by _Src is less than the size of _Extent, undefined behavior results. + /// </param> + array_view(const Concurrency::extent<_Rank>& _Extent, const _Value_type * _Src) __GPU + :_Base(_Src,_Extent) + { + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_Extent"> + /// The extent of this array view. + /// </param> + /// <param name="_Src"> + /// A pointer to the source data this array_view will bind to. If the number of elements pointed to + /// by _Src is less than the size of _Extent, undefined behavior results. + /// </param> + array_view(const Concurrency::extent<_Rank>& _Extent, _In_ _Value_type * _Src) __GPU + :_Base(_Src,_Extent) + { + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> array_view(int _E0, const _Container& _Src) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<1>(_E0)) + { + static_assert( std::is_same<typename std::remove_const<typename std::remove_reference<decltype(*_Src.data())>::type>::type, _Value_type>::value, "container element type and array view element type must match"); + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container; + //// The length of the array_view is the same as the length of the container + /// </summary> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Arr_type, int _Size> explicit array_view(const _In_ _Arr_type (&_Src) [_Size]) __GPU + :_Base(_Src, Concurrency::extent<1>(_Size)) + { + static_assert( std::is_same<typename std::remove_const<typename std::remove_reference<decltype(*_Src)>::type>::type, _Value_type>::value, "container element type and array view element type must match"); + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> array_view(int _E0, int _E1, const _Container& _Src) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<2>(_E0,_E1)) + { + static_assert( std::is_same<typename std::remove_const<typename std::remove_reference<decltype(*_Src.data())>::type>::type, _Value_type>::value, "container element type and array view element type must match"); + static_assert(_Rank == 2, "rank must be 2"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data contained in the _Src container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array_view. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. + /// </param> + template <typename _Container> array_view(int _E0, int _E1, int _E2, const _Container& _Src) __CPU_ONLY + :_Base(_Src.data(), Concurrency::extent<3>(_E0,_E1,_E2)) + { + static_assert( std::is_same<typename std::remove_const<typename std::remove_reference<decltype(*_Src.data())>::type>::type, _Value_type>::value, "container element type and array view element type must match"); + static_assert(_Rank == 3, "rank must be 3"); + _Initialize(_Src.size()); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0, undefined behavior results. + /// </param> + array_view(int _E0, const _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<1>(_E0)) + { + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0*_E1, undefined behavior results. + /// </param> + array_view(int _E0, int _E1, const _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<2>(_E0,_E1)) + { + static_assert(_Rank == 2, "rank must be 2"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array_view. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0*_E1*_E2, undefined behavior results. + /// </param> + array_view(int _E0, int _E1, int _E2, const _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<3>(_E0,_E1,_E2)) + { + static_assert(_Rank == 3, "rank must be 3"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0, undefined behavior results. + /// </param> + array_view(int _E0, _In_ _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<1>(_E0)) + { + static_assert(_Rank == 1, "rank must be 1"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0*_E1, undefined behavior results. + /// </param> + array_view(int _E0, int _E1, _In_ _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<2>(_E0,_E1)) + { + static_assert(_Rank == 2, "rank must be 2"); + _Initialize(); + } + + /// <summary> + /// Construct an array_view which is bound to the data pointed to by _Src. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array_view. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array_view. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array_view. + /// </param> + /// <param name="_Src"> + /// A container which contains the data that this array_view is bound to. If the number of elements pointed to + /// by _Src is less than _E0*_E1*_E2, undefined behavior results. + /// </param> + array_view(int _E0, int _E1, int _E2, _In_ _Value_type * _Src) __GPU + :_Base(_Src, Concurrency::extent<3>(_E0,_E1,_E2)) + { + static_assert(_Rank == 3, "rank must be 3"); + _Initialize(); + } + + /// <summary> + /// Copy Assignment operator. Shallow copy. + /// </summary> + array_view& operator=(const array_view& _Other) __GPU + { + _Base::operator=(_Other); + return *this; + } + + /// <summary> + /// Copy Assignment operator. Shallow copy. + /// </summary> + array_view& operator=(const array_view<_Value_type, _Rank>& _Other) __GPU + { + _Base::operator=(_Other); + return *this; + } + + /// <summary> + /// Copies elements from this array_view to the destination array. + /// </summary> + void copy_to(array<_Value_type,_Rank>& _Dest) const __CPU_ONLY + { + copy(*this,_Dest); + } + + /// <summary> + /// Copies elements from this array_view to the destination array_view. + /// </summary> + void copy_to(const array_view<_Value_type,_Rank>& _Dest) const __CPU_ONLY + { + copy(*this,_Dest); + } + + /// <summary> + /// Projects the most-significant dimension of this array_view. If the array_view rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Const_result_type operator[] (int _I) const __GPU + { + return details::_Const_array_view_projection_helper<_Value_type,_Rank>::_Project0(this, _I); + } + + /// <summary> + /// Get a reference to the element indexed by _Index. Unlike the other indexing operators for accessing the + /// array_view on the CPU, this method does not implicitly synchronize this array_view's contents to the CPU. + /// After accessing the array_view on a remote location or performing a copy operation involving this array_view + /// users are responsible to explicitly synchronize the array_view to the CPU before calling this method. + /// Failure to do so results in undefined behavior. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// Reference to the element indexed by _Index + /// </returns> + value_type& get_ref(const index<_Rank>& _Index) const __GPU + { + void *_Ptr = _Access(_Index); + return *reinterpret_cast<value_type*>(_Ptr); + } + + /// <summary> + /// Get the element value indexed by _I + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I + /// </returns> + value_type& operator[] (const index<_Rank>& _Index) const __GPU + { + return this->operator()(_Index); + } + + /// <summary> + /// Get the element value indexed by _I + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I + /// </returns> + value_type& operator() (const index<_Rank>& _Index) const __GPU + { + void * _Ptr = _Access(_Read_access, _Index); + return *reinterpret_cast<value_type*>(_Ptr); + } + + /// <summary> + /// Projects the most-significant dimension of this array_view. If the array_view rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Const_result_type operator() (int _I) const __GPU + { + return details::_Const_array_view_projection_helper<_Value_type,_Rank>::_Project0(this, _I); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1) + /// </returns> + value_type& operator() (int _I0, int _I1) const __GPU + { + static_assert(_Rank == 2, "value_type& array_view::operator()(int,int) is only permissible on array_view<T, 2>"); + return this->operator()(index<2>(_I0,_I1)); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1,_I2) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the index + /// </param> + /// <param name="_I2"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1,_I2) + /// </returns> + value_type& operator() (int _I0, int _I1, int _I2) const __GPU + { + static_assert(_Rank == 3, "value_type& array_view::operator()(int,int,int) is only permissible on array_view<T, 3>"); + return this->operator()(index<3>(_I0,_I1,_I2)); + } + + /// <summary> + /// Produces a subsection of the source array_view at the given origin and extent. + /// </summary> + /// <param name="_Section_origin"> + /// The origin of the section. + /// </param> + /// <param name="_Section_extent"> + /// The extent of the section + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) const __GPU + { + return _Convert<_Value_type>(_Section(_Section_origin, _Section_extent)); + } + + /// <summary> + /// Produces a subsection of the source array_view with origin of zero, with + /// an extent of _Ext. + /// </summary> + /// <param name="_Ext"> + /// The extent of this section + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(const Concurrency::extent<_Rank>& _Ext) const __GPU + { + return section(Concurrency::index<_Rank>(), _Ext); + } + + /// <summary> + /// Produces a subsection of the source array_view with origin specified by an index, with + /// an extent of (this->exent - _Idx). + /// </summary> + /// <param name="_Idx"> + /// The index that specifies the origin of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(const Concurrency::index<_Rank>& _Idx) const __GPU + { + return section(_Idx, this->extent - _Idx); + } + + /// <summary> + /// Produces a one-dimensional subsection of the source array_view with origin specified by the index + /// components _I0, with extent _E0. + /// </summary> + /// <param name="_I0"> + /// The origin of this section. + /// </param> + /// <param name="_E0"> + /// The extent of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(int _I0, int _E0) const __GPU + { + static_assert(_Rank == 1, "rank must be 1"); + return section(Concurrency::index<1>(_I0), Concurrency::extent<1>(_E0)); + } + + /// <summary> + /// Produces a two-dimensional subsection of the source array_view with origin specified by the index + /// components (_I0,_I1), with extent (_E0,_E1). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(int _I0, int _I1, int _E0, int _E1) const __GPU + { + static_assert(_Rank == 2, "rank must be 2"); + return section(Concurrency::index<2>(_I0,_I1), Concurrency::extent<2>(_E0,_E1)); + } + + /// <summary> + /// Produces a three-dimensional subsection of the source array_view with origin specified by the index + /// components (_I0,_I1,_I2), with extent (_E0,_E1,_E2). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the origin of this section. + /// </param> + /// <param name="_I2"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The next-to-most-significant component of the extent of this section. + /// </param> + /// <param name="_E2"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view section(int _I0, int _I1, int _I2, int _E0, int _E1, int _E2) const __GPU + { + static_assert(_Rank == 3, "rank must be 3"); + return section(Concurrency::index<3>(_I0,_I1,_I2), Concurrency::extent<3>(_E0,_E1,_E2)); + } + + /// <summary> + /// Produces a (possibly unsafe) reinterpretation of this array_view that is linear and with + /// a different element type. The size of _Value_type2 must evenly divide into the size of + /// this array_view. + /// </summary> + /// <returns> + /// A linear array_view with a reinterpreted element type. + /// </returns> + template <typename _Value_type2> array_view<const _Value_type2, _Rank> reinterpret_as() const __GPU + { + return _Convert<_Value_type2>(this->template _Reinterpret_as<sizeof(_Value_type2)/sizeof(int)>()); + } + + /// <summary> + /// Produces an array_view of a different rank over this array_view's data. + /// </summary> + /// <param name="_View_extent"> + /// The reshaping extent. + /// </param> + /// <returns> + /// A reshaped array_view. + /// </returns> + template <int _New_rank> array_view<const _Value_type,_New_rank> view_as(const Concurrency::extent<_New_rank>& _View_extent) const __GPU + { + return _Convert<_Value_type>(_View_as(_View_extent)); + } + + /// <summary> + /// Returns a pointer to the raw data of this array_view. + /// </summary> + const _Value_type* data() const __GPU + { + static_assert(_Rank == 1, "array_view::data() is only permissible on array_view<T, 1>"); + return &this->operator[](index<_Rank>()); + } + + /// <summary> + /// Informs the array_view that its bound memory has been modified outside + /// the array_view interface. This will render all cached information stale. + /// </summary> + void refresh() const __CPU_ONLY + { + _Buffer_ptr _PBuf; + _Get_access_async(_M_buffer_descriptor._Get_view_key(), _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(), _Write_access, _PBuf)._Get(); + } + + /// <summary> + /// Asynchronously synchronizes any modifications made to "this" array_view to the specified accelerator_view. + /// </summary> + /// <param name="_Accl_view"> + /// The target accelerator_view to synchronize to. + /// </param> + /// <returns> + /// A future upon which to wait for the operation to complete. + /// </returns> + concurrency::completion_future synchronize_to_async(const accelerator_view& _Accl_view) const __CPU_ONLY + { + auto _Async_op_id = details::_Get_amp_trace()->_Launch_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + _Event _Ev; + + _Ev = _Get_access_async(_M_buffer_descriptor._Get_view_key(), _Accl_view, _Read_access, _PBuf); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); + } + + /// <summary> + /// Asynchronously synchronizes any modifications made to "this" array_view to its source data. + /// </summary> + /// <returns> + /// A future upon which to wait for the operation to complete. + /// </returns> + concurrency::completion_future synchronize_async() const __CPU_ONLY + { + auto _Async_op_id = details::_Get_amp_trace()->_Launch_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + _Event _Ev; + + // If the array_view corresponds to a ubiquitous buffer with no data source, + // then synchronize is a no-op + if (_M_buffer_descriptor._Get_buffer_ptr()->_Has_data_source()) { + _Ev = _Get_access_async(_M_buffer_descriptor._Get_view_key(), _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(), _Read_access, _PBuf); + } + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); + } + + /// <summary> + /// Synchronizes any modifications made to "this" array_view to the specified accelerator_view. + /// </summary> + /// <param name="_Accl_view"> + /// The target accelerator_view to synchronize to. + /// </param> + void synchronize_to(const accelerator_view& _Accl_view) const __CPU_ONLY + { + auto _Span_id = details::_Get_amp_trace()->_Start_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + + _Get_access_async(_M_buffer_descriptor._Get_view_key(), _Accl_view, _Read_access, _PBuf)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + /// <summary> + /// Synchronizes any modifications made to "this" array_view to its source data. + /// </summary> + void synchronize() const __CPU_ONLY + { + auto _Span_id = details::_Get_amp_trace()->_Start_array_view_synchronize_event_helper(_M_buffer_descriptor); + + _Buffer_ptr _PBuf; + + // If the array_view corresponds to a ubiquitous buffer with no data source, + // then synchronize is a no-op + if (_M_buffer_descriptor._Get_buffer_ptr()->_Has_data_source()) { + _Get_access_async(_M_buffer_descriptor._Get_view_key(), _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(), _Read_access, _PBuf)._Get(); + } + + details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + /// <summary> + /// Returns the accelerator_view where the data source of the array_view is located. + /// If the array_view does not have a data source, this API throws a runtime_exception + /// </summary> + accelerator_view get_source_accelerator_view() const + { + if (_M_buffer_descriptor._Get_buffer_ptr()->_Has_data_source()) { + return _M_buffer_descriptor._Get_buffer_ptr()->_Get_master_accelerator_view(); + } + else { + throw runtime_exception("Cannot query source accelerator_view for an array_view without a data source.", E_INVALIDARG); + } + } + + __declspec(property(get=get_source_accelerator_view)) accelerator_view source_accelerator_view; + +private: + template <typename _T, int _R> + static array_view<const _T,_R> _Convert(const _Array_view_base<_R,sizeof(_T)/sizeof(int)>& _Other) __GPU + { + static_assert(sizeof(array_view<const _T,_R>) == sizeof(_Array_view_base<_R,sizeof(_T)/sizeof(int)>), "ASSERT FAILURE: implementation relies on binary conversion between the two"); + return (*reinterpret_cast<const array_view<const _T,_R>*>(&_Other)); + } + + void _Project0(int _I, array_view<const _Value_type, _Rank-1> &_Projected_view) const __GPU + { + _Base::_Project0(_I, _Projected_view); + _Projected_view._Initialize(); + } + + array_view() __GPU {} + + array_view(const array_view& _Other, const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU + : + _Base(_Other, _Section_origin, _Section_extent) + { + _Initialize(); + } + + void _Initialize() __GPU + { + // Set the type access mode + _M_buffer_descriptor._M_type_access_mode = _Read_access; + } + + void _Initialize(size_t _Src_data_size) __CPU_ONLY + { + // Ensure that the _Src_data_size is at least as big as the size + // of the array_view + if (_Src_data_size < this->extent.size()) { + throw runtime_exception("Invalid _Src container argument - _Src size is less than the size of the array_view.", E_INVALIDARG); + } + + _Initialize(); + } + +}; // class array_view<const T,R> + +// Forward declarations for copy functions +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array<_Value_type,_Rank>& _Src, array<_Value_type,_Rank>& _Dest); +template <typename _Value_type, int _Rank> void copy(const array<_Value_type,_Rank>& _Src, array<_Value_type,_Rank>& _Dest); +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, InputIterator _SrcLast, array<_Value_type, _Rank> &_Dest); +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, InputIterator _SrcLast, array<_Value_type, _Rank> &_Dest); +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, array<_Value_type, _Rank> &_Dest); +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, array<_Value_type, _Rank> &_Dest); +template <typename OutputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array<_Value_type, _Rank> &_Src, OutputIterator _DestIter); +template <typename OutputIterator, typename _Value_type, int _Rank> void copy(const array<_Value_type, _Rank> &_Src, OutputIterator _DestIter); +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> void copy(const array<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<const _Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> void copy(const array_view<const _Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<_Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> void copy(const array_view<_Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<const _Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> void copy(const array_view<const _Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest); +template <typename _Value_type, int _Rank> void copy(const array_view<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest); +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, InputIterator _SrcLast, const array_view<_Value_type, _Rank> &_Dest); +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, const array_view<_Value_type, _Rank> &_Dest); +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, InputIterator _SrcLast, const array_view<_Value_type, _Rank> &_Dest); +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, const array_view<_Value_type, _Rank> &_Dest); +template <typename OutputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<_Value_type, _Rank> &_Src, OutputIterator _DestIter); +template <typename OutputIterator, typename _Value_type, int _Rank> void copy(const array_view<_Value_type, _Rank> &_Src, OutputIterator _DestIter); + +namespace direct3d +{ + template<typename _Value_type, int _Rank> + array<_Value_type, _Rank> make_array(const Concurrency::extent<_Rank> &_Extent, const Concurrency::accelerator_view &_Av, _In_ IUnknown *_D3D_buffer) __CPU_ONLY; +} + +/// <summary> +/// An array is a multi-dimensional data aggregate on a accelerator_view. +/// </summary> +/// <param name="_Rank"> +/// The dimensionality of this array. +/// </param> +/// <param name="_Value_type"> +/// The type of the elements in the array. +/// </param> +template <typename _Value_type, int _Rank = 1> class array +{ + // internal storage abstraction + typedef details::_Buffer_descriptor _Buffer_descriptor; + typedef _Array_flatten_helper<_Rank, typename Concurrency::extent<_Rank>::value_type, typename Concurrency::index<_Rank>::value_type> _Flatten_helper; + + _CPP_AMP_VERIFY_RANK(_Rank, array); + static_assert(!std::is_const<_Value_type>::value, "array<const _Value_type> is not supported"); + static_assert(0 == (sizeof(_Value_type) % sizeof(int)), "only value types whose size is a multiple of the size of an integer are allowed in array"); + + // Friends + template<typename _Value_type, int _Rank> + friend array<_Value_type,_Rank> direct3d::make_array(const Concurrency::extent<_Rank> &_Extent, const Concurrency::accelerator_view &_Av, _In_ IUnknown *_D3D_buffer) __CPU_ONLY; + friend const _Buffer_descriptor& details::_Get_buffer_descriptor<array<_Value_type,_Rank>>(const array<_Value_type,_Rank>& _Array) __GPU; + friend _Ret_ _Ubiquitous_buffer* details::_Get_buffer<array<_Value_type,_Rank>>(const array<_Value_type,_Rank>& _Array) __CPU_ONLY; + friend _Event details::_Get_access_async<array<_Value_type,_Rank>>(const array<_Value_type,_Rank>& _Array, _Access_mode _Mode, _Buffer_ptr &_Buf_ptr) __CPU_ONLY; + + public: + static const int rank = _Rank; + typedef typename _Value_type value_type; + + /// <summary> + /// Construct an array from extents + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + explicit array(const Concurrency::extent<_Rank> & _Extent) __CPU_ONLY + : _M_extent(_Extent) + { + _Initialize(details::_Select_default_accelerator().default_view, access_type_auto); + } + + /// <summary> + /// Construct array<T,1> with the extent _E0 + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + explicit array(int _E0) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int) is only permissible on array<T, 1>"); + _Initialize(details::_Select_default_accelerator().default_view, access_type_auto); + } + + /// <summary> + /// Construct an array<T,2> from two integer extents. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + explicit array(int _E0, int _E1) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int) is only permissible on array<T, 2>"); + _Initialize(details::_Select_default_accelerator().default_view, access_type_auto); + } + + /// <summary> + /// Construct an array<T,3> from three integer extents. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + explicit array(int _E0, int _E1, int _E2) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int) is only permissible on array<T, 3>"); + _Initialize(details::_Select_default_accelerator().default_view, access_type_auto); + } + + /// <summary> + /// Construct an array from extents, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + array(const Concurrency::extent<_Rank>& _Extent, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(_Extent) + { + _Initialize(_Av, _Cpu_access_type); + } + + /// <summary> + /// Construct array<T,1> with the extent _E0, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + array(int _E0, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, accelerator_view) is only permissible on array<T, 1>"); + _Initialize(_Av, _Cpu_access_type); + } + + /// <summary> + /// Construct an array<T,2> from two integer extents, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + array(int _E0, int _E1, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, accelerator_view) is only permissible on array<T, 2>"); + _Initialize(_Av, _Cpu_access_type); + } + + /// <summary> + /// Construct an array<T,3> from three integer extents, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + array(int _E0, int _E1, int _E2, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, accelerator_view) is only permissible on array<T, 3>"); + _Initialize(_Av, _Cpu_access_type); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + array(const Concurrency::extent<_Rank>& _Extent, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(_Extent) + { + _Initialize(_Av, _Associated_Av); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + array(int _E0, accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, accelerator_view, accelerator_view) is only permissible on array<T, 1>"); + _Initialize(_Av, _Associated_Av); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + array(int _E0, int _E1, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, accelerator_view, accelerator_view) is only permissible on array<T, 2>"); + _Initialize(_Av, _Associated_Av); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + array(int _E0, int _E1, int _E2, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, accelerator_view, accelerator_view) is only permissible on array<T, 3>"); + _Initialize(_Av, _Associated_Av); + } + + /// <summary> + /// Construct an array initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + template <typename _InputIterator> array(const Concurrency::extent<_Rank>& _Extent, _InputIterator _Src_first, _InputIterator _Src_last) __CPU_ONLY + : _M_extent(_Extent) + { + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from an iterator. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + template <typename _InputIterator> array(const Concurrency::extent<_Rank>& _Extent, _InputIterator _Src_first) __CPU_ONLY + : _M_extent(_Extent) + { + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + template <typename _InputIterator> array(int _E0, _InputIterator _Src_first, _InputIterator _Src_last) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, iterator, iterator) is only permissible on array<T, 1>"); + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from an iterator. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + template <typename _InputIterator> array(int _E0, _InputIterator _Src_first) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, iterator) is only permissible on array<T, 1>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, _InputIterator _Src_first, _InputIterator _Src_last) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, iterator, iterator) is only permissible on array<T, 2>"); + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from an iterator. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, _InputIterator _Src_first) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, iterator) is only permissible on array<T, 2>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from an iterator. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, int _E2, _InputIterator _Src_first, _InputIterator _Src_last) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, iterator, iterator) is only permissible on array<T, 3>"); + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from an iterator. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, int _E2, _InputIterator _Src_first) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, iterator) is only permissible on array<T, 3>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(details::_Select_default_accelerator().default_view, _Src_first, _Src_last, access_type_auto); + } + + /// <summary> + /// Construct an array initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(const Concurrency::extent<_Rank>& _Extent, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(_Extent) + { + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct an array initialized from an iterator into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(const Concurrency::extent<_Rank>& _Extent, _InputIterator _Src_first, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(_Extent) + { + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct an array initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(int _E0, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, iterator, iterator) is only permissible on array<T, 1>"); + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct an array initialized from an iterator into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(int _E0, _InputIterator _Src_first, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, iterator) is only permissible on array<T, 1>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct an array initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, iterator, iterator) is only permissible on array<T, 2>"); + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct an array initialized from an iterator into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, _InputIterator _Src_first, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, iterator) is only permissible on array<T, 2>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct an array initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, int _E2, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, iterator, iterator) is only permissible on array<T, 3>"); + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct an array initialized from an iterator into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, int _E2, _InputIterator _Src_first, Concurrency::accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, iterator) is only permissible on array<T, 3>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Src_first, _Src_last, _Cpu_access_type); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(const Concurrency::extent<_Rank>& _Extent, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(_Extent) + { + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from an iterator into a container. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(const Concurrency::extent<_Rank>& _Extent, _InputIterator _Src_first, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(_Extent) + { + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(int _E0, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, iterator, iterator, accelerator_view, accelerator_view) is only permissible on array<T, 1>"); + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from an iterator into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(int _E0, _InputIterator _Src_first, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) + : _M_extent(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "array(int, iterator, accelerator_view, accelerator_view) is only permissible on array<T, 1>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, iterator, iterator, accelerator_view, accelerator_view) is only permissible on array<T, 2>"); + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from an iterator into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, _InputIterator _Src_first, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "array(int, int, iterator, accelerator_view, accelerator_view) is only permissible on array<T, 2>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, int _E2, _InputIterator _Src_first, _InputIterator _Src_last, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, iterator, iterator, accelerator_view, accelerator_view) is only permissible on array<T, 3>"); + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_view, initialized from an iterator into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this array. + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this array. + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this array. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less + /// than this->extent.size(), undefined behavior results. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// An accelerator_view which specifies the preferred target location of the array. + /// </param> + template <typename _InputIterator> array(int _E0, int _E1, int _E2, _InputIterator _Src_first, Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + : _M_extent(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "array(int, int, int, iterator, accelerator_view, accelerator_view) is only permissible on array<T, 3>"); + + _InputIterator _Src_last = _Src_first; + std::advance(_Src_last, this->extent.size()); + + _Initialize(_Av, _Associated_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct an array initialized from an array_view. + /// </summary> + /// <param name="_Src"> + /// An array_view to copy from. + /// </param> + explicit array(const array_view<const _Value_type,_Rank>& _Src) __CPU_ONLY + :_M_extent(_Src.extent) + { + _Initialize(details::_Select_default_accelerator().default_view, access_type_auto); + Concurrency::copy(_Src,*this); + } + + /// <summary> + /// Construct an array initialized from an array_view, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Src"> + /// An array_view to copy from. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this array resides. + /// </param> + /// <param name="_Cpu_access_type"> + /// The desired access_type for the array on the CPU. This + /// parameter has a default value of access_type_auto leaving the + /// CPU access_type determination to the runtime. The actual + /// CPU access_type for the array can be queried using the + /// get_cpu_access_type method. + /// </param> + array(const array_view<const _Value_type,_Rank>& _Src, accelerator_view _Av, access_type _Cpu_access_type = access_type_auto) __CPU_ONLY + :_M_extent(_Src.extent) + { + _Initialize(_Av, _Cpu_access_type); + Concurrency::copy(_Src,*this); + } + + /// <summary> + /// Construct a staging array between two associated accelerator_views, initialized from an array_view. + /// </summary> + /// <param name="_Src"> + /// An array_view to copy from. + /// </param> + /// <param name="_Av"> + /// An accelerator_view which specifies the location of the array. + /// </param> + /// <param name="_Associated_Av"> + /// The accelerator_view that is associated with _Av. + /// </param> + array(const array_view<const _Value_type,_Rank>& _Src, accelerator_view _Av, accelerator_view _Associated_Av) __CPU_ONLY + :_M_extent(_Src.extent) + { + _Initialize(_Av, _Associated_Av); + Concurrency::copy(_Src,*this); + } + + /// <summary> + /// Copy constructor. Deep copy. + /// </summary> + array(const array& _Other) __CPU_ONLY + : _M_extent(_Other._M_extent) + { + _Initialize(_Other.accelerator_view, _Other.associated_accelerator_view); + Concurrency::copy(_Other, *this); + } + + /// <summary> + /// Move constructor. + /// </summary> + array(array && _Other) __CPU_ONLY + : _M_extent(_Other._M_extent), _M_multiplier(_Other._M_multiplier) + , _M_buffer_descriptor(_Other._M_buffer_descriptor) + { + // Register this + this->_Register_copy(_Other); + + // Release the _Other array + _Other._Unregister(); + _Other._M_buffer_descriptor._M_data_ptr = NULL; + _Other._M_buffer_descriptor._Set_buffer_ptr(NULL); + } + + /// <summary> + /// Copy Assignment operator. Deep copy. + /// </summary> + array & operator= (const array & _Other) __CPU_ONLY + { + if (this != &_Other) + { + // First unregister myself from the current buffer + _Unregister(); + + _M_extent = _Other._M_extent; + _Initialize(_Other.accelerator_view, _Other.associated_accelerator_view); + Concurrency::copy(_Other, *this); + } + return *this; + } + + /// <summary> + /// Move Assignment operator. + /// </summary> + array & operator= (array && _Other) __CPU_ONLY + { + if (this != &_Other) + { + // First unregister myself from the current buffer + _Unregister(); + + _M_extent = _Other._M_extent; + _M_multiplier = _Other._M_multiplier; + _M_buffer_descriptor = _Other._M_buffer_descriptor; + this->_Register_copy(_Other); + + // Release the _Other array + _Other._Unregister(); + _Other._M_buffer_descriptor._M_data_ptr = NULL; + _Other._M_buffer_descriptor._Set_buffer_ptr(NULL); + } + return *this; + } + + /// <summary> + /// Assignment operator from an array_view + /// </summary> + array& operator=(const array_view<const _Value_type,_Rank>& _Src) __CPU_ONLY + { + Concurrency::copy(_Src,*this); + return *this; + } + + /// <summary> + /// Copies elements from this array to the destination array. + /// </summary> + void copy_to(array<_Value_type,_Rank>& _Dest) const __CPU_ONLY + { + Concurrency::copy(*this, _Dest); + } + + /// <summary> + /// Copies elements from this array to the destination array_view. + /// </summary> + void copy_to(const array_view<_Value_type,_Rank>& _Dest) const __CPU_ONLY + { + Concurrency::copy(*this,_Dest); + } + + /// <summary> + /// Returns the extent that defines the shape of this array. + /// </summary> + __declspec(property(get=get_extent)) Concurrency::extent<_Rank> extent; + Concurrency::extent<_Rank> get_extent() const __GPU + { + return _M_extent; + } + + /// <summary> + /// Returns the accelerator_view where this array is located. + /// </summary> + __declspec(property(get=get_accelerator_view)) Concurrency::accelerator_view accelerator_view; + Concurrency::accelerator_view get_accelerator_view() const __CPU_ONLY + { + return _Get_buffer()->_Get_master_buffer()->_Get_access_on_accelerator_view(); + } + + /// <summary> + /// Returns the accelerator_view that is the preferred target where this array can be copied. + /// </summary> + __declspec(property(get=get_associated_accelerator_view)) Concurrency::accelerator_view associated_accelerator_view; + Concurrency::accelerator_view get_associated_accelerator_view() const __CPU_ONLY + { + return _Get_buffer()->_Get_master_buffer()->_Get_accelerator_view(); + } + + /// <summary> + /// Returns the CPU access_type allowed for this array. + /// </summary> + __declspec(property(get=get_cpu_access_type)) access_type cpu_access_type; + access_type get_cpu_access_type() const __CPU_ONLY + { + return _Get_buffer()->_Get_master_buffer()->_Get_allowed_host_access_type(); + } + + /// <summary> + /// Get the element value indexed by _I + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I + /// </returns> + value_type& operator[] (const index<_Rank>& _Index) __GPU + { + // Refresh the data ptr if needed + _Refresh_data_ptr(_Read_write_access); + + _Value_type * _Ptr = reinterpret_cast<_Value_type *>(_M_buffer_descriptor._M_data_ptr); + return _Ptr[_Flatten_helper::func(_M_multiplier._M_base, _Index._M_base)]; + } + + /// <summary> + /// Get the element value indexed by _I + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I + /// </returns> + const value_type& operator[] (const index<_Rank>& _Index) const __GPU + { + // Refresh the data ptr if needed +#pragma warning( push ) +#pragma warning( disable : 4880 ) + // Casting away constness in amp restricted scope might result in + // undefined behavior, therefore, the compiler will report a level 1 warning + // for it. But the following const_cast is harmless thus we are suppressing + // this warning just for this line. + const_cast<array*>(this)->_Refresh_data_ptr(_Read_access); +#pragma warning( pop ) + + _Value_type * _Ptr = reinterpret_cast<_Value_type *>(_M_buffer_descriptor._M_data_ptr); + return _Ptr[_Flatten_helper::func(_M_multiplier._M_base, _Index._M_base)]; + } + + /// <summary> + /// Projects the most-significant dimension of this array. If the array rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Result_type operator[](int _I) __GPU + { + return details::_Array_projection_helper<_Value_type,_Rank>::_Project0(this,_I); + } + + /// <summary> + /// Projects the most-significant dimension of this array. If the array rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Const_result_type operator[](int _I) const __GPU + { + return details::_Const_array_projection_helper<_Value_type,_Rank>::_Project0(this,_I); + } + + /// <summary> + /// Get the element value indexed by _I + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I + /// </returns> + value_type& operator() (const index<_Rank>& _Index) __GPU + { + return this->operator[](_Index); + } + + /// <summary> + /// Get the element value indexed by _Index + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index + /// </returns> + const value_type& operator() (const index<_Rank>& _Index) const __GPU + { + return this->operator[](_Index); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1) + /// </returns> + value_type& operator() (int _I0, int _I1) __GPU + { + static_assert(_Rank == 2, "value_type& array::operator()(int, int) is only permissible on array<T, 2>"); + return this->operator[](index<2>(_I0, _I1)); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1) + /// </returns> + const value_type& operator() (int _I0, int _I1) const __GPU + { + static_assert(_Rank == 2, "const value_type& array::operator()(int, int) is only permissible on array<T, 2>"); + return this->operator[](index<2>(_I0, _I1)); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1,_I2) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the index + /// </param> + /// <param name="_I2"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1,_I2) + /// </returns> + value_type& operator() (int _I0, int _I1, int _I2) __GPU + { + static_assert(_Rank == 3, "value_type& array::operator()(int, int, int) is only permissible on array<T, 3>"); + return this->operator[](index<3>(_I0, _I1, _I2)); + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1,_I2) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the index + /// </param> + /// <param name="_I2"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1,_I2) + /// </returns> + const value_type& operator() (int _I0, int _I1, int _I2) const __GPU + { + static_assert(_Rank == 3, "const value_type& array::operator()(int, int, int) const is only permissible on array<T, 3>"); + return this->operator[](index<3>(_I0, _I1, _I2)); + } + + /// <summary> + /// Projects the most-significant dimension of this array. If the array rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Result_type operator()(int _I) __GPU + { + return details::_Array_projection_helper<_Value_type,_Rank>::_Project0(this,_I); + } + + /// <summary> + /// Projects the most-significant dimension of this array. If the array rank is 1, this + /// produces a single element; otherwise it produces an array_view with one fewer dimensions. + /// </summary> + /// <param name="_I"> + /// The most-significant index component + /// </param> + /// <returns> + /// The element at index component _I, or an array_view projected on the most-significant dimension. + /// </returns> + typename details::_Projection_result_type<_Value_type,_Rank>::_Const_result_type operator()(int _I) const __GPU + { + return details::_Const_array_projection_helper<_Value_type,_Rank>::_Project0(this,_I); + } + + /// <summary> + /// Produces a subsection of the source array at the given origin and extent. + /// </summary> + /// <param name="_Section_origin"> + /// The origin of the section. + /// </param> + /// <param name="_Section_extent"> + /// The extent of the section + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<_Value_type,_Rank> section(const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) __GPU + { + array_view<_Value_type,_Rank> _T1(*this); + return _T1.section(_Section_origin, _Section_extent); + } + + /// <summary> + /// Produces a subsection of the source array at the given origin and extent. + /// </summary> + /// <param name="_Section_origin"> + /// The origin of the section. + /// </param> + /// <param name="_Section_extent"> + /// The extent of the section + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<const _Value_type,_Rank> section(const Concurrency::index<_Rank>& _Section_origin, const Concurrency::extent<_Rank>& _Section_extent) const __GPU + { + array_view<const _Value_type,_Rank> _T1(*this); + return _T1.section(_Section_origin, _Section_extent); + } + + /// <summary> + /// Produces a subsection of the source array_view with origin of zero, with + /// an extent of _Ext. + /// </summary> + /// <param name="_Ext"> + /// The extent of this section + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view<_Value_type,_Rank> section(const Concurrency::extent<_Rank>& _Ext) __GPU + { + return section(Concurrency::index<_Rank>(), _Ext); + } + + /// <summary> + /// Produces a subsection of the source array_view with origin of zero, with + /// an extent of _Ext. + /// </summary> + /// <param name="_Ext"> + /// The extent of this section + /// </param> + /// <returns> + /// A subsection of the array_view. + /// </returns> + array_view<const _Value_type,_Rank> section(const Concurrency::extent<_Rank>& _Ext) const __GPU + { + return section(Concurrency::index<_Rank>(), _Ext); + } + + /// <summary> + /// Produces a subsection of the source array with origin specified by an index, with + /// an extent of (this->exent - _Idx). + /// </summary> + /// <param name="_Idx"> + /// The index that specifies the origin of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<_Value_type,_Rank> section(const index<_Rank>& _Idx) __GPU + { + array_view<_Value_type,_Rank> _T1(*this); + return _T1.section(_Idx); + } + + /// <summary> + /// Produces a subsection of the source array with origin specified by an index, with + /// an extent of (this->exent - _Idx). + /// </summary> + /// <param name="_Idx"> + /// The index that specifies the origin of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<const _Value_type,_Rank> section(const index<_Rank>& _Idx) const __GPU + { + array_view<const _Value_type,_Rank> _T1(*this); + return _T1.section(_Idx); + } + + /// <summary> + /// Produces a one-dimensional subsection of the source array with origin specified by the index + /// components _I0, with extent _E0. + /// </summary> + /// <param name="_I0"> + /// The origin of this section. + /// </param> + /// <param name="_E0"> + /// The extent of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<_Value_type,1> section(int _I0, int _E0) __GPU + { + array_view<_Value_type,_Rank> _T1(*this); + return _T1.section(_I0,_E0); + } + + /// <summary> + /// Produces a one-dimensional subsection of the source array with origin specified by the index + /// components _I0, with extent _E0. + /// </summary> + /// <param name="_I0"> + /// The origin of this section. + /// </param> + /// <param name="_E0"> + /// The extent of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<const _Value_type,1> section(int _I0, int _E0) const __GPU + { + array_view<const _Value_type,_Rank> _T1(*this); + return _T1.section(_I0,_E0); + } + + /// <summary> + /// Produces a two-dimensional subsection of the source array with origin specified by the index + /// components (_I0,_I1), with extent (_E0,_E1). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<_Value_type,2> section(int _I0, int _I1, int _E0, int _E1) __GPU + { + array_view<_Value_type,_Rank> _T1(*this); + return _T1.section(_I0,_I1,_E0,_E1); + } + + /// <summary> + /// Produces a two-dimensional subsection of the source array with origin specified by the index + /// components (_I0,_I1), with extent (_E0,_E1). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<const _Value_type,2> section(int _I0, int _I1, int _E0, int _E1) const __GPU + { + array_view<const _Value_type,_Rank> _T1(*this); + return _T1.section(_I0,_I1,_E0,_E1); + } + + /// <summary> + /// Produces a three-dimensional subsection of the source array with origin specified by the index + /// components (_I0,_I1,_I2), with extent (_E0,_E1,_E2). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the origin of this section. + /// </param> + /// <param name="_I2"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The next-to-most-significant component of the extent of this section. + /// </param> + /// <param name="_E2"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<_Value_type,3> section(int _I0, int _I1, int _I2, int _E0, int _E1, int _E2) __GPU + { + array_view<_Value_type,_Rank> _T1(*this); + return _T1.section(_I0,_I1,_I2,_E0,_E1,_E2); + } + + /// <summary> + /// Produces a three-dimensional subsection of the source array with origin specified by the index + /// components (_I0,_I1,_I2), with extent (_E0,_E1,_E2). + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the origin of this section. + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the origin of this section. + /// </param> + /// <param name="_I2"> + /// The least-significant component of the origin of this section. + /// </param> + /// <param name="_E0"> + /// The most-significant component of the extent of this section. + /// </param> + /// <param name="_E1"> + /// The next-to-most-significant component of the extent of this section. + /// </param> + /// <param name="_E2"> + /// The least-significant component of the extent of this section. + /// </param> + /// <returns> + /// A subsection of the array. + /// </returns> + array_view<const _Value_type,3> section(int _I0, int _I1, int _I2, int _E0, int _E1, int _E2) const __GPU + { + array_view<const _Value_type,_Rank> _T1(*this); + return _T1.section(_I0,_I1,_I2,_E0,_E1,_E2); + } + + /// <summary> + /// Produces a (possibly unsafe) reinterpretation of this array that is linear and with + /// a different element type. + /// </summary> + /// <returns> + /// A linear array_view with a reinterpreted element type. + /// </returns> + template <typename _Value_type2> array_view<_Value_type2,1> reinterpret_as() __GPU + { + return array_view<_Value_type,1>(_M_buffer_descriptor, Concurrency::extent<1>(extent.size())).template reinterpret_as<_Value_type2>(); + } + + /// <summary> + /// Produces a (possibly unsafe) reinterpretation of this array that is linear and with + /// a different element type. + /// </summary> + /// <returns> + /// A linear array_view with a reinterpreted element type. + /// </returns> + template <typename _Value_type2> array_view<const _Value_type2,1> reinterpret_as() const __GPU + { +#pragma warning( push ) +#pragma warning( disable : 4880 ) + // Casting away constness in amp restricted scope might result in + // undefined behavior, therefore, the compiler will report a level 1 warning + // for it. But the following const_cast is harmless thus we are suppressing + // this warning just for this line. + return const_cast<array*>(this)->reinterpret_as<_Value_type2>(); +#pragma warning( pop ) + } + + /// <summary> + /// Produces an array_view of a different rank over this array's data. + /// </summary> + /// <param name="_View_extent"> + /// The reshaping extent. + /// </param> + /// <returns> + /// A reshaped array_view. + /// </returns> + template <int _New_rank> array_view<_Value_type,_New_rank> view_as(const Concurrency::extent<_New_rank>& _View_extent) __GPU + { + return array_view<_Value_type,_New_rank>(_M_buffer_descriptor, _View_extent); + } + + /// <summary> + /// Produces an array_view of a different rank over this array's data. + /// </summary> + /// <param name="_View_extent"> + /// The reshaping extent. + /// </param> + /// <returns> + /// A reshaped array_view. + /// </returns> + template <int _New_rank> array_view<const _Value_type,_New_rank> view_as(const Concurrency::extent<_New_rank>& _View_extent) const __GPU + { +#pragma warning( push ) +#pragma warning( disable : 4880 ) + // Casting away constness in amp restricted scope might result in + // undefined behavior, therefore, the compiler will report a level 1 warning + // for it. But the following const_cast is harmless thus we are suppressing + // this warning just for this line. + return const_cast<array*>(this)->view_as<_New_rank>(_View_extent); +#pragma warning( pop ) + } + + /// <summary> + /// Implicitly converts this array into a vector by copying. + /// </summary> + operator std::vector<_Value_type>() const __CPU_ONLY + { + std::vector<_Value_type> _return_vector(extent.size()); + Concurrency::copy(*this, _return_vector.begin()); + + return _return_vector; + } + + /// <summary> + /// Returns a pointer to the raw data of this array. + /// </summary> + _Ret_ _Value_type* data() __GPU + { + _Refresh_data_ptr(_Read_write_access, false /* _Exception */); + return reinterpret_cast<_Value_type*>(_M_buffer_descriptor._M_data_ptr); + } + + /// <summary> + /// Returns a pointer to the raw data of this array. + /// </summary> + const _Value_type* data() const __GPU + { +#pragma warning( push ) +#pragma warning( disable : 4880 ) + // Casting away constness in amp restricted scope might result in + // undefined behavior, therefore, the compiler will report a level 1 warning + // for it. But the following const_cast is harmless thus we are suppressing + // this warning just for this line. + const_cast<array*>(this)->_Refresh_data_ptr(_Read_access, false /* _Exception */); +#pragma warning( pop ) + return reinterpret_cast<const _Value_type*>(_M_buffer_descriptor._M_data_ptr); + } + + /// <summary> + /// Destroys this array and reclaims resources. + /// </summary> + ~array() __CPU_ONLY + { + bool _Can_throw = (std::current_exception() == nullptr); + + // Destructor should not throw if we are already processing + // an exception and another exception will result in termination + try { + _Unregister(); + } + catch(...) + { + if (_Can_throw) { + throw; + } + } + } + +private: + + // No default constructor + array() __CPU_ONLY; + + // Private constructor used by direct3d::make_array + array(const Concurrency::extent<_Rank>& _Extent, _Buffer_descriptor _Buffer_descriptor) + : _M_extent(_Extent), _M_buffer_descriptor(_Buffer_descriptor) + { + _Initialize(); + + // Register this + this->_Register(); + } + + // Initialize + unsigned int _Initialize() __CPU_ONLY + { + details::_Is_valid_extent(_M_extent); + + // Arrays always have a type access mode of '_Is_array_mode' + // This is the mechanism for differentiating between arrays and array_views by the runtime + _M_buffer_descriptor._M_type_access_mode = _Is_array_mode; + unsigned int totalExtent = _M_extent[_Rank-1]; + details::_Array_init_helper<Concurrency::extent<_Rank>, Concurrency::extent<_Rank>>::func(totalExtent, _M_multiplier, _M_extent); + + return totalExtent; + } + + // Initialize and allocate on specified accelerator_view + void _Initialize(Concurrency::accelerator_view _Av, access_type _Cpu_access_type) __CPU_ONLY + { + unsigned int totalExtent = _Initialize(); + // release the existing buffer if any before allocation new one + _M_buffer_descriptor._Set_buffer_ptr(NULL); + + _Buffer_ptr _PBuf = _Buffer::_Create_buffer(_Av, _Av, totalExtent, sizeof(_Value_type), false /* _Is_temp */, _Cpu_access_type); + + _M_buffer_descriptor._Set_buffer_ptr(_Ubiquitous_buffer::_Create_ubiquitous_buffer(_PBuf)); + _Register(); + } + + // Initialize and allocate on specified accelerator_view and copy specified data + template <typename _InputIterator> + void _Initialize(Concurrency::accelerator_view _Av, _InputIterator _Src_first, _InputIterator _Src_last, access_type _Cpu_access_type) __CPU_ONLY + { + _Initialize(_Av, _Cpu_access_type); + copy(_Src_first, _Src_last, *this); + } + + // Initialize and allocate on specified accelerator_views + void _Initialize(Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av) __CPU_ONLY + { + unsigned int totalExtent = _Initialize(); + + // Staging arrays can only be created if the accelerator_view is on the cpu_accelerator + _Buffer_ptr _PBuf = NULL; + + // release the existing buffer if any before allocation new one + _M_buffer_descriptor._Set_buffer_ptr(NULL); + + if (_Is_cpu_accelerator(_Av.accelerator)) + { + // If the accelerator _Associated_Av supports zero-copy and the default cpu access type + // for the accelerator is access_type_read_write, create a zero-copy buffer instead of a + // staging buffer + if (_Associated_Av.accelerator.supports_cpu_shared_memory && (_Get_recommended_buffer_host_access_mode(_Associated_Av) == _Read_write_access)) { + _PBuf = _Buffer::_Create_buffer(_Associated_Av, _Av, totalExtent, sizeof(_Value_type), false /* _Is_temp */, access_type_read_write); + } + else { + _PBuf = _Buffer::_Create_stage_buffer(_Associated_Av, _Av, totalExtent, sizeof(_Value_type)); + } + + _PBuf->_Map_buffer(_Read_write_access, true /* _Wait */); + } + else + { + _PBuf = _Buffer::_Create_buffer(_Av, _Av, totalExtent, sizeof(_Value_type), false /* _Is_temp */, access_type_auto); + } + + _M_buffer_descriptor._Set_buffer_ptr(_Ubiquitous_buffer::_Create_ubiquitous_buffer(_PBuf)); + _Register(); + } + + // Initialize and allocate on specified accelerator_views + template <typename _InputIterator> + void _Initialize(Concurrency::accelerator_view _Av, Concurrency::accelerator_view _Associated_Av, _InputIterator _Src_first, _InputIterator _Src_last) __CPU_ONLY + { + _Initialize(_Av, _Associated_Av); + copy(_Src_first, _Src_last, *this); + } + + void _Register() __CPU_ONLY + { + Concurrency::accelerator_view cpuAv = _Is_cpu_accelerator(this->accelerator_view.accelerator) ? + this->accelerator_view : accelerator(accelerator::cpu_accelerator).default_view; + _M_buffer_descriptor._Get_buffer_ptr()->_Register_view(_M_buffer_descriptor._Get_view_key(), cpuAv, _Create_buffer_view_shape()); + + _M_buffer_descriptor._Get_buffer_ptr()->_Discard(_M_buffer_descriptor._Get_view_key()); + + // If the array is on the CPU accelerator then we will ensure that the descriptor + // indicates CPU access + if (_Is_cpu_accelerator(this->accelerator_view.accelerator)) + { + _Buffer_ptr _PBuf = NULL; + this->_Get_access_async(_Read_write_access, _PBuf, false)._Get(); + } + } + + void _Register_copy(const array &_Other) __CPU_ONLY + { + _M_buffer_descriptor._Get_buffer_ptr()->_Register_view_copy(_M_buffer_descriptor._Get_view_key(), _Other._M_buffer_descriptor._Get_view_key()); + } + + void _Unregister() __CPU_ONLY + { + // No need to unregister if the array was moved causing the buffer ptr to be set to NULL + if (_M_buffer_descriptor._Get_buffer_ptr() != NULL) { + _M_buffer_descriptor._Get_buffer_ptr()->_Unregister_view(_M_buffer_descriptor._Get_view_key()); + } + } + + _Ret_ _Ubiquitous_buffer* _Get_buffer() __CPU_ONLY const + { + return _M_buffer_descriptor._Get_buffer_ptr(); + } + + _Event _Get_access_async(_Access_mode _Mode, _Buffer_ptr &_Buf_ptr, bool _Zero_copy_cpu_access = false) __CPU_ONLY const + { + _ASSERTE(!_Zero_copy_cpu_access || (_Get_buffer()->_Get_master_buffer()->_Get_allowed_host_access_mode() != _No_access)); + + _Buffer_ptr _PBuf; + Concurrency::accelerator_view _Access_av = _Zero_copy_cpu_access ? accelerator(accelerator::cpu_accelerator).default_view : this->accelerator_view; + _Event _Ev = details::_Get_access_async(_M_buffer_descriptor._Get_view_key(), + _Access_av, + _Mode, _PBuf); + _Buf_ptr = _PBuf; + + if (_Is_cpu_accelerator(_Access_av.accelerator)) { + _Ev = _Ev._Add_continuation(std::function<_Event()>([_PBuf, this]() mutable -> _Event { + const_cast<array*>(this)->_M_buffer_descriptor._M_data_ptr = _PBuf->_Get_host_ptr(); + return _Event(); + })); + } + + return _Ev; + } + + _Ret_ _View_shape* _Create_buffer_view_shape() const + { + _ASSERTE(_Get_buffer()->_Get_master_buffer_elem_size() == sizeof(_Value_type)); + + unsigned int _ZeroOffset[_Rank] = {0}; + unsigned int _View_extent[_Rank]; + for(int i=0; i<_Rank; ++i) + { + _View_extent[i] = static_cast<unsigned int>(this->_M_extent[i]); + } + return _View_shape::_Create_view_shape(static_cast<unsigned int>(_Rank), 0, &_View_extent[0], &_ZeroOffset[0], &_View_extent[0]); + } + + bool _Has_cpu_access() const __CPU_ONLY + { + return (_Get_buffer()->_Get_master_buffer()->_Get_allowed_host_access_mode() != _No_access); + } + + void _Refresh_data_ptr(_Access_mode _Requested_mode, bool _Exception = true) __CPU_ONLY + { + _ASSERTE(_Is_valid_access_mode(_Requested_mode)); + + // For an array that has CPU access, the maximum CPU access allowed is that allowed by + // the underlying _Buffer allocation + _Requested_mode = static_cast<_Access_mode>(_Requested_mode & _Get_buffer()->_Get_master_buffer()->_Get_allowed_host_access_mode()); + + // Refresh the data ptr if we do not have requested access + if ((_Requested_mode == _No_access) || ((_M_buffer_descriptor._M_curr_cpu_access_mode & _Requested_mode) != _Requested_mode)) + { + if (_Has_cpu_access() && (_Requested_mode != _No_access)) + { + auto _Span_id = details::_Get_amp_trace()->_Start_array_view_synchronize_event_helper(_M_buffer_descriptor); + _Buffer_ptr _PBuf; + bool _Zero_copy_cpu_access = !_Is_cpu_accelerator(this->accelerator_view.accelerator); + this->_Get_access_async(_Requested_mode, _PBuf, _Zero_copy_cpu_access)._Get(); + details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + else + { + if (_Exception) + { + if (!_Has_cpu_access()) { + throw runtime_exception("The array is not accessible on CPU.", E_FAIL); + } + else { + throw runtime_exception("The array is not accessible for reading on CPU.", E_FAIL); + } + } + } + } + } + + void _Refresh_data_ptr(_Access_mode _Requested_mode, bool _Exception = true) __GPU_ONLY + { + UNREFERENCED_PARAMETER(_Requested_mode); + UNREFERENCED_PARAMETER(_Exception); + } + +private: + // Data members + + Concurrency::extent<_Rank> _M_extent; + + // Descriptor of the buffer underlying the array + _Buffer_descriptor _M_buffer_descriptor; + + // The vector used for index calculation. + Concurrency::extent<_Rank> _M_multiplier; +}; + +namespace details +{ +template <typename _Value_type, int _Rank> +_Event _Copy_async_impl(const array<_Value_type,_Rank>& _Src, array<_Value_type,_Rank>& _Dest) +{ + if (_Src.extent.size() > _Dest.extent.size()) + { + throw runtime_exception("Invalid _Src argument. _Src size exceeds total size of the _Dest.", E_INVALIDARG); + } + + // We can obliterate the exisiting content of dest if it is about to be totally overwritten + _Access_mode _Dest_access_mode = (_Src.extent.size() == _Dest.extent.size()) ? _Write_access : _Read_write_access; + + _Buffer_ptr _PBufSrc, _PBufDest; + _Event _Ev = _Get_access_async(_Src, _Read_access, _PBufSrc); + _Ev = _Ev._Add_event(_Get_access_async(_Dest, _Dest_access_mode, _PBufDest)); + size_t _NumElemsToCopy = (_Src.extent.size() * sizeof(_Value_type)) / _PBufSrc->_Get_elem_size(); + return _Ev._Add_continuation(std::function<_Event()>([_PBufSrc, _PBufDest, _NumElemsToCopy]() mutable -> _Event { + return details::_Copy_impl(_PBufSrc, 0, _PBufDest, 0, _NumElemsToCopy); + })); +} + +template <typename InputIterator, typename _Value_type, int _Rank> +_Event _Copy_async_impl(InputIterator _SrcFirst, InputIterator _SrcLast, array<_Value_type, _Rank> &_Dest) +{ + size_t _NumElemsToCopy = std::distance(_SrcFirst, _SrcLast); + // We can obliterate the exisiting content of dest if it is about to be totally overwritten + _Access_mode _Dest_access_mode = (_NumElemsToCopy == _Dest.extent.size()) ? _Write_access : _Read_write_access; + _Buffer_ptr _PDestBuf; + _Event _Ev = _Get_access_async(_Dest, _Dest_access_mode, _PDestBuf); + + return _Ev._Add_continuation(std::function<_Event()>([_SrcFirst, _SrcLast, _PDestBuf, _NumElemsToCopy]() mutable -> _Event { + return details::_Copy_impl<InputIterator, _Value_type>(_SrcFirst, _SrcLast, _NumElemsToCopy, _PDestBuf, 0); + })); +} + +template <typename OutputIterator, typename _Value_type, int _Rank> +_Event _Copy_async_impl(const array<_Value_type, _Rank> &_Src, OutputIterator _DestIter) +{ + _Buffer_ptr _PSrcBuf; + _Event _Ev = _Get_access_async(_Src, _Read_access, _PSrcBuf); + size_t _NumElemsToCopy = (_Src.extent.size() * sizeof(_Value_type)) / _PSrcBuf->_Get_elem_size(); + return _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _NumElemsToCopy, _DestIter]() mutable -> _Event { + return details::_Copy_impl<OutputIterator, _Value_type>(_PSrcBuf, 0, _NumElemsToCopy, _DestIter); + })); +} + +template <typename _Value_type, int _Rank> +_Event _Copy_async_impl(const array<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + const _Buffer_descriptor &_SrcBufDesc = _Get_buffer_descriptor(_Src); + const _Buffer_descriptor &_DestBufDesc = _Get_buffer_descriptor(_Dest); + if (_SrcBufDesc._Get_buffer_ptr() == _DestBufDesc._Get_buffer_ptr()) { + throw runtime_exception("Cannot copy between overlapping regions of the same buffer.", E_INVALIDARG); + } + + _Buffer_ptr _PSrcBuf, _PDestBuf; + _Event _Ev = _Get_access_async(_Src, _Read_access, _PSrcBuf); + + // The source accelerator_view is driven by array's master location, + // therefore we can pass nullptr to avoid unnecessary computation + auto _AccelInfo = _Get_src_dest_accelerator_view(nullptr, &_DestBufDesc); + + _Ev = _Ev._Add_event(_Get_access_async(_DestBufDesc._Get_view_key(), _AccelInfo.second, _Write_access, _PDestBuf)); + _View_shape_ptr _PSrcShape = _Get_buffer_view_shape(_SrcBufDesc); + _View_shape_ptr _PDestShape = _Get_buffer_view_shape(_DestBufDesc); + return _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _PSrcShape, _PDestBuf, _PDestShape]() mutable -> _Event { + return details::_Copy_impl(_PSrcBuf, _PSrcShape, _PDestBuf, _PDestShape); + })); +} + +template <typename _Value_type, int _Rank> +_Event _Copy_async_impl(const array_view<const _Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest) +{ + const _Buffer_descriptor &_SrcBufDesc = _Get_buffer_descriptor(_Src); + const _Buffer_descriptor &_DestBufDesc = _Get_buffer_descriptor(_Dest); + if (_SrcBufDesc._Get_buffer_ptr() == _DestBufDesc._Get_buffer_ptr()) { + throw runtime_exception("Cannot copy between overlapping regions of the same buffer.", E_INVALIDARG); + } + + auto _AccelInfo = _Get_src_dest_accelerator_view(&_SrcBufDesc, &_DestBufDesc); + + _Buffer_ptr _PSrcBuf, _PDestBuf; + _Event _Ev = _Get_access_async(_SrcBufDesc._Get_view_key(), _AccelInfo.first, _Read_access, _PSrcBuf); + _Ev = _Ev._Add_event(_Get_access_async(_Dest, _Write_access, _PDestBuf)); + _View_shape_ptr _PSrcShape = _Get_buffer_view_shape(_SrcBufDesc); + _View_shape_ptr _PDestShape = _Get_buffer_view_shape(_DestBufDesc); + return _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _PSrcShape, _PDestBuf, _PDestShape]() mutable -> _Event { + return details::_Copy_impl(_PSrcBuf, _PSrcShape, _PDestBuf, _PDestShape); + })); +} + +template <typename _Value_type, int _Rank> +_Event _Copy_async_impl(const array_view<const _Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + const _Buffer_descriptor &_SrcBufDesc = _Get_buffer_descriptor(_Src); + const _Buffer_descriptor &_DestBufDesc = _Get_buffer_descriptor(_Dest); + _View_shape_ptr _PSrcShape = _Get_buffer_view_shape(_SrcBufDesc); + _View_shape_ptr _PDestShape = _Get_buffer_view_shape(_DestBufDesc); + if ((_SrcBufDesc._Get_buffer_ptr() == _DestBufDesc._Get_buffer_ptr()) && _PSrcShape->_Overlaps(_PDestShape)) { + throw runtime_exception("Cannot copy between overlapping regions of the same buffer.", E_INVALIDARG); + } + + auto _AccelInfo = _Get_src_dest_accelerator_view(&_SrcBufDesc, &_DestBufDesc); + + _Buffer_ptr _PSrcBuf, _PDestBuf; + _Event _Ev = _Get_access_async(_SrcBufDesc._Get_view_key(), _AccelInfo.first, _Read_access, _PSrcBuf); + _Ev = _Ev._Add_event(_Get_access_async(_DestBufDesc._Get_view_key(), _AccelInfo.second, _Write_access, _PDestBuf)); + return _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _PSrcShape, _PDestBuf, _PDestShape]() mutable -> _Event { + return details::_Copy_impl(_PSrcBuf, _PSrcShape, _PDestBuf, _PDestShape); + })); +} + +template <typename InputIterator, typename _Value_type, int _Rank> +_Event _Copy_async_impl(InputIterator _SrcFirst, InputIterator _SrcLast, const array_view<_Value_type, _Rank> &_Dest) +{ + static_assert(!std::is_const<_Value_type>::value, "Cannot copy to array_view<const _Value_type, _Rank>."); + + size_t _Src_size = std::distance(_SrcFirst, _SrcLast); + + // Source cannot be greater than destination + if (_Src_size > _Dest.extent.size()) + { + throw runtime_exception("Number of elements in range between [_SrcFirst, _SrcLast) exceeds total size of the _Dest.", E_INVALIDARG); + } + +#pragma warning( push ) +#pragma warning( disable : 4127 ) // Disable warning about constant conditional expression + // Higher ranks need to have as many elements as in _Dest array_view + if ((_Rank > 1) && (_Src_size != _Dest.extent.size())) + { + throw runtime_exception("For _Rank > 1 the number of elements in range between [_SrcFirst, _SrcLast) has to be equal to total size of the _Dest.", E_INVALIDARG); + } +#pragma warning( pop ) + + // We can obliterate the exisiting content of dest if it is about to be totally overwritten + _Access_mode _Dest_access_mode = (_Src_size == _Dest.extent.size()) ? _Write_access : _Read_write_access; + + // Get read-write access for array_view on cpu_accelerator and take underlying pointer to data + const _Buffer_descriptor &_DestBufDesc = _Get_buffer_descriptor(_Dest); + + auto _AccelInfo = _Get_src_dest_accelerator_view(nullptr, &_DestBufDesc); + + _Buffer_ptr _PDestBuf; + _Event _Ev = _Get_access_async(_DestBufDesc._Get_view_key(), _AccelInfo.second, _Dest_access_mode, _PDestBuf); + + _View_shape_ptr _Dst_shape = _Get_buffer_view_shape(_DestBufDesc); + + // If the _Dst shape is linear then perform a linear copy + unsigned int _Dst_linear_offset, _Dst_linear_size; + if (_Dst_shape->_Is_view_linear(_Dst_linear_offset, _Dst_linear_size)) + { + _Ev = _Ev._Add_continuation(std::function<_Event()>([_PDestBuf, _SrcFirst, _SrcLast, _Src_size, _Dst_linear_offset]() mutable -> _Event { + return details::_Copy_impl<InputIterator, _Value_type>(_SrcFirst, _SrcLast, _Src_size, _PDestBuf, _Dst_linear_offset); + })); + } + else + { + _View_shape_ptr _Reinterpreted_dst_shape = _Create_reinterpreted_shape(_Dst_shape, _PDestBuf->_Get_elem_size(), sizeof(_Value_type)); + + // Source has as many elements as in destination, reshape source to match destination shape + std::vector<unsigned int> _Src_offset(_Reinterpreted_dst_shape->_Get_rank(), 0); + _View_shape_ptr _Src_shape = details::_View_shape::_Create_view_shape(_Reinterpreted_dst_shape->_Get_rank(), 0 /* linear offset*/, + _Reinterpreted_dst_shape->_Get_view_extent(), _Src_offset.data(), + _Reinterpreted_dst_shape->_Get_view_extent()); + + _Ev = _Ev._Add_continuation(std::function<_Event()>([_PDestBuf, _SrcFirst, _Src_shape, _Dst_shape]() mutable -> _Event { + return details::_Copy_impl<InputIterator, _Value_type>(_SrcFirst, _Src_shape, _PDestBuf, _Dst_shape); + })); + } + + return _Ev; +} + +template <typename OutputIterator, typename _Value_type, int _Rank> +_Event _Copy_async_impl(const array_view<_Value_type, _Rank> &_Src, OutputIterator _DestIter) +{ + // Caller is responsible for passing valid _DestIter + + // Get read access for array_view on cpu_accelerator and take underlying pointer to data + const _Buffer_descriptor &_SrcBufDesc = _Get_buffer_descriptor(_Src); + + auto _AccelInfo = _Get_src_dest_accelerator_view(&_SrcBufDesc, nullptr); + + _Buffer_ptr _PSrcBuf; + _Event _Ev = _Get_access_async(_SrcBufDesc._Get_view_key(), _AccelInfo.first, _Read_access, _PSrcBuf); + + // Get source shape + _View_shape_ptr _Src_shape = _Get_buffer_view_shape(_SrcBufDesc); + + // If the _Src_shape is linear then perform a linear copy + unsigned int _Src_linear_offset, _Src_linear_size; + if (_Src_shape->_Is_view_linear(_Src_linear_offset, _Src_linear_size)) + { + _Ev = _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _Src_linear_offset, _Src_linear_size, _DestIter]() mutable -> _Event { + return details::_Copy_impl<OutputIterator, _Value_type>(_PSrcBuf, _Src_linear_offset, _Src_linear_size, _DestIter); + })); + } + else + { + _View_shape_ptr _Reinterpreted_src_shape = _Create_reinterpreted_shape(_Src_shape, _PSrcBuf->_Get_elem_size(), sizeof(_Value_type)); + + // Valid destination should have space for as many elements as in source array_view, reshape to match source view shape + std::vector<unsigned int> _Dst_offset(_Reinterpreted_src_shape->_Get_rank(), 0); + _View_shape_ptr _Dst_shape = details::_View_shape::_Create_view_shape(_Reinterpreted_src_shape->_Get_rank(), 0 /* linear offset*/, + _Reinterpreted_src_shape->_Get_view_extent(), _Dst_offset.data(), + _Reinterpreted_src_shape->_Get_view_extent()); + + _Ev = _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _Src_shape, _DestIter, _Dst_shape]() mutable -> _Event { + return details::_Copy_impl<OutputIterator, _Value_type>(_PSrcBuf, _Src_shape, _DestIter, _Dst_shape); + })); + } + + return _Ev; +} + +} + +/// <summary> +/// Asynchronously copies the contents of the source array into the destination array. +/// </summary> +/// <param name="_Src"> +/// The source array. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array<_Value_type,_Rank>& _Src, array<_Value_type,_Rank>& _Dest) +{ + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + auto _Ev = _Copy_async_impl(_Src, _Dest); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies the contents of the source array into the destination array. +/// </summary> +/// <param name="_Src"> +/// The source array. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +template <typename _Value_type, int _Rank> void copy(const array<_Value_type,_Rank>& _Src, array<_Value_type,_Rank>& _Dest) +{ + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + _Copy_async_impl(_Src, _Dest)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Asynchronously copies the elements in the range [_SrcFirst, _SrcLast) into the destination array. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container. +/// </param> +/// <param name="_SrcLast"> +/// An ending iterator into the source container. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, InputIterator _SrcLast, array<_Value_type, _Rank> &_Dest) +{ + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(nullptr, + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * std::distance(_SrcFirst, _SrcLast)); + + _Event _Ev = _Copy_async_impl(_SrcFirst, _SrcLast, _Dest); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies the elements in the range [_SrcFirst, _SrcLast) into the destination array. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container. +/// </param> +/// <param name="_SrcLast"> +/// An ending iterator into the source container. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, InputIterator _SrcLast, array<_Value_type, _Rank> &_Dest) +{ + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(nullptr, + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * std::distance(_SrcFirst, _SrcLast)); + + _Copy_async_impl(_SrcFirst, _SrcLast, _Dest)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Asynchronously copies the elements beginning at _SrcFirst into the destination array. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less +/// than _Dest.extent.size(), undefined behavior results. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, array<_Value_type, _Rank> &_Dest) +{ + InputIterator _SrcLast = _SrcFirst; + std::advance(_SrcLast, _Dest.extent.size()); + return copy_async(_SrcFirst, _SrcLast, _Dest); +} + +/// <summary> +/// Copies the elements beginning at _SrcFirst into the destination array. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less +/// than _Dest.extent.size(), undefined behavior results. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, array<_Value_type, _Rank> &_Dest) +{ + InputIterator _SrcLast = _SrcFirst; + std::advance(_SrcLast, _Dest.extent.size()); + copy(_SrcFirst, _SrcLast, _Dest); +} + +/// <summary> +/// Asynchronously copies the contents of the array into the destination beginning at _DestIter. +/// </summary> +/// <param name="_Src"> +/// The source array. +/// </param> +/// <param name="_DestIter"> +/// An output iterator to the beginning position at destination. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename OutputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array<_Value_type, _Rank> &_Src, OutputIterator _DestIter) +{ + _CPP_AMP_VERIFY_MUTABLE_ITERATOR(OutputIterator); + + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(details::_Get_buffer_descriptor(_Src), + nullptr, + sizeof(_Value_type) * _Src.extent.size()); + _Event _Ev = _Copy_async_impl(_Src, _DestIter); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies the contents of the array into the destination beginning at _DestIter. +/// </summary> +/// <param name="_Src"> +/// The source array. +/// </param> +/// <param name="_DestIter"> +/// An output iterator to the beginning position at destination. +/// </param> +template <typename OutputIterator, typename _Value_type, int _Rank> void copy(const array<_Value_type, _Rank> &_Src, OutputIterator _DestIter) +{ + _CPP_AMP_VERIFY_MUTABLE_ITERATOR(OutputIterator); + + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(details::_Get_buffer_descriptor(_Src), + nullptr, + sizeof(_Value_type) * _Src.extent.size()); + + _Copy_async_impl(_Src, _DestIter)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Asynchronously copies the contents of the source array into the destination array_view. +/// </summary> +/// <param name="_Src"> +/// The source array. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + _Event _Ev = _Copy_async_impl(_Src, _Dest); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies the contents of the source array into the destination array_view. +/// </summary> +/// <param name="_Src"> +/// The source array. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +template <typename _Value_type, int _Rank> void copy(const array<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + _Copy_async_impl(_Src, _Dest)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Asynchronously copies the contents of the source array_view into the destination array. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<const _Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest) +{ + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + _Event _Ev = _Copy_async_impl(_Src, _Dest); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies the contents of the source array_view into the destination array. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +template <typename _Value_type, int _Rank> void copy(const array_view<const _Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest) +{ + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + _Copy_async_impl(_Src, _Dest)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Asynchronously copies the contents of the source array_view into the destination array. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<_Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest) +{ + return copy_async<_Value_type, _Rank>(array_view<const _Value_type, _Rank>(_Src), _Dest); +} + +/// <summary> +/// Copies the contents of the source array_view into the destination array. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array. +/// </param> +template <typename _Value_type, int _Rank> void copy(const array_view<_Value_type, _Rank>& _Src, array<_Value_type, _Rank>& _Dest) +{ + copy<_Value_type, _Rank>(array_view<const _Value_type, _Rank>(_Src), _Dest); +} + +/// <summary> +/// Asynchronously copies the contents of the source array_view into the destination array_view. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<const _Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + _Event _Ev = _Copy_async_impl(_Src, _Dest); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies the contents of the source array_view into the destination array_view. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +template <typename _Value_type, int _Rank> void copy(const array_view<const _Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(details::_Get_buffer_descriptor(_Src), + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * _Src.extent.size()); + + _Copy_async_impl(_Src, _Dest)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Asynchronously copies the contents of the source array_view into the destination array_view. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + return copy_async<_Value_type, _Rank>(array_view<const _Value_type, _Rank>(_Src), _Dest); +} + +/// <summary> +/// Copies the contents of the source array_view into the destination array_view. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +template <typename _Value_type, int _Rank> void copy(const array_view<_Value_type, _Rank>& _Src, const array_view<_Value_type, _Rank>& _Dest) +{ + copy<_Value_type, _Rank>(array_view<const _Value_type, _Rank>(_Src), _Dest); +} + +/// <summary> +/// Asynchronously copies the elements in the range [_SrcFirst, _SrcLast) into the destination array_view. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container. +/// </param> +/// <param name="_SrcLast"> +/// An ending iterator into the source container. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, InputIterator _SrcLast, const array_view<_Value_type, _Rank> &_Dest) +{ + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(nullptr, + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * std::distance(_SrcFirst, _SrcLast)); + + _Event _Ev = _Copy_async_impl(_SrcFirst, _SrcLast, _Dest); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies the elements beginning at _SrcFirst into the destination array_view. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less +/// than _Dest.extent.size(), undefined behavior results. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename InputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(InputIterator _SrcFirst, const array_view<_Value_type, _Rank> &_Dest) +{ + InputIterator _SrcLast = _SrcFirst; + std::advance(_SrcLast, _Dest.extent.size()); + return copy_async(_SrcFirst, _SrcLast, _Dest); +} + +/// <summary> +/// Copies the elements in the range [_SrcFirst, _SrcLast) into the destination array_view. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container. +/// </param> +/// <param name="_SrcLast"> +/// An ending iterator into the source container. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, InputIterator _SrcLast, const array_view<_Value_type, _Rank> &_Dest) +{ + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(nullptr, + details::_Get_buffer_descriptor(_Dest), + sizeof(_Value_type) * std::distance(_SrcFirst, _SrcLast)); + + _Copy_async_impl(_SrcFirst, _SrcLast, _Dest)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies the contents of an STL container into the destination array_view. +/// </summary> +/// <param name="_SrcFirst"> +/// A beginning iterator into the source container; if the number of available container elements starting at this iterator position is less +/// than _Dest.extent.size(), undefined behavior results. +/// </param> +/// <param name="_Dest"> +/// The destination array_view. +/// </param> +template <typename InputIterator, typename _Value_type, int _Rank> void copy(InputIterator _SrcFirst, const array_view<_Value_type, _Rank> &_Dest) +{ + InputIterator _SrcLast = _SrcFirst; + std::advance(_SrcLast, _Dest.extent.size()); + copy(_SrcFirst, _SrcLast, _Dest); +} + +/// <summary> +/// Asynchronously copies the contents of the array_view into the destination beginning at _DestIter. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_DestIter"> +/// An output iterator to the beginning position at destination. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename OutputIterator, typename _Value_type, int _Rank> concurrency::completion_future copy_async(const array_view<_Value_type, _Rank> &_Src, OutputIterator _DestIter) +{ + _CPP_AMP_VERIFY_MUTABLE_ITERATOR(OutputIterator); + + // Caller is responsible for passing valid _DestIter + auto _Async_op_id = details::_Get_amp_trace()->_Launch_async_copy_event_helper(details::_Get_buffer_descriptor(_Src), + nullptr, + sizeof(_Value_type) * _Src.extent.size()); + + _Event _Ev = _Copy_async_impl(_Src, _DestIter); + + return details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies the contents of the array_view into the destination beginning at _DestIter. +/// </summary> +/// <param name="_Src"> +/// The source array_view. +/// </param> +/// <param name="_DestIter"> +/// An output iterator to the beginning position at destination. +/// </param> +template <typename OutputIterator, typename _Value_type, int _Rank> void copy(const array_view<_Value_type, _Rank> &_Src, OutputIterator _DestIter) +{ + _CPP_AMP_VERIFY_MUTABLE_ITERATOR(OutputIterator); + + auto _Span_id = details::_Get_amp_trace()->_Start_copy_event_helper(details::_Get_buffer_descriptor(_Src), + nullptr, + sizeof(_Value_type) * _Src.extent.size()); + + _Copy_async_impl(_Src, _DestIter)._Get(); + + details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +// Namespace for Direct3D specific functionality +namespace direct3d +{ + /// <summary> + /// Get the D3D buffer interface underlying an array. + /// </summary> + /// <param name="_Rank"> + /// The rank of the array to get underlying D3D buffer of. + /// </param> + /// <param name="_Value_type"> + /// The type of the elements in the array to get underlying D3D buffer of. + /// </param> + /// <param name="_Array"> + /// A array on a D3D accelerator_view for which the underlying D3D buffer interface is returned. + /// </param> + /// <returns> + /// The IUnknown interface pointer corresponding to the D3D buffer underlying the array. + /// </returns> + template<typename _Value_type, int _Rank> _Ret_ IUnknown *get_buffer(const array<_Value_type, _Rank> &_Array) __CPU_ONLY + { + _Buffer_ptr _PBuf; + _Get_access_async(_Array, _Read_write_access, _PBuf)._Get(); + return details::_D3D_interop::_Get_D3D_buffer(_PBuf); + } + + /// <summary> + /// Create an array from a D3D buffer interface pointer. + /// </summary> + /// <param name="_Rank"> + /// The rank of the array to be created from the D3D buffer. + /// </param> + /// <param name="_Value_type"> + /// The type of the elements of the array to be created from the D3D buffer. + /// </param> + /// <param name="_Extent"> + /// An extent that describes the shape of the array aggregate. + /// </param> + /// <param name="_Av"> + /// A D3D accelerator_view on which the array is to be created. + /// </param> + /// <param name="_D3D_buffer"> + /// IUnknown interface pointer of the D3D buffer to create the array from. + /// </param> + /// <returns> + /// A array created using the provided D3D buffer. + /// </returns> + template<typename _Value_type, int _Rank> array<_Value_type, _Rank> make_array(const Concurrency::extent<_Rank> &_Extent, const Concurrency::accelerator_view &_Av, _In_ IUnknown *_D3D_buffer) __CPU_ONLY + { + details::_Is_valid_extent(_Extent); + + if (_D3D_buffer == NULL) + { + throw runtime_exception("NULL D3D buffer pointer.", E_INVALIDARG); + } + + if (!details::_Is_D3D_accelerator_view(_Av)) + { + throw runtime_exception("Cannot create D3D buffer on a non-D3D accelerator_view.", E_INVALIDARG); + } + + _Ubiquitous_buffer_ptr _PBuf = _Ubiquitous_buffer::_Create_ubiquitous_buffer(_Buffer::_Create_buffer(_D3D_buffer, _Av, _Extent.size(), sizeof(_Value_type))); + return array<_Value_type, _Rank>(_Extent, _Buffer_descriptor(_PBuf->_Get_master_buffer()->_Get_host_ptr(), _PBuf, _Is_array_mode, _Read_write_access)); + } + +} // namespace Concurrency::direct3d + +//============================================================================= +// Atomic Operation Library +//============================================================================= + +#define AS_UINT_PTR(p) reinterpret_cast<unsigned int *>(p) +#define AS_UINT(v) *(reinterpret_cast<unsigned int *>(&(v))) +#define AS_INT(v) *(reinterpret_cast<int *>(&(v))) +#define AS_FLOAT(v) *(reinterpret_cast<float *>(&(v))) + +/// <summary> +/// Performs an atomic addition of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be added to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_add(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + unsigned int _Ret; + _Ret = __dp_d3d_interlocked_add(AS_UINT_PTR(_Dest), AS_UINT(_Value)); + return AS_INT(_Ret); +} + +/// <summary> +/// Performs an atomic addition of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be added to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_add(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_add(_Dest, _Value); +} + +/// <summary> +/// Performs an atomic subtraction of _Value from the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be subtracted from the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_sub(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + unsigned int _Ret; + int _Neg = -_Value; + _Ret = __dp_d3d_interlocked_add(AS_UINT_PTR(_Dest), AS_UINT(_Neg)); + return AS_INT(_Ret); +} + +/// <summary> +/// Performs an atomic subtraction of _Value from the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be subtracted from the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> + +inline unsigned int atomic_fetch_sub(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ +#pragma warning( push ) +#pragma warning( disable : 4146 ) + // Warning 4146: unary minus operator applied to unsigned type, result + // still unsigned. + // + // This is what we want here. The resulted unsigned value have the + // right binary representation for achieving subtraction + return __dp_d3d_interlocked_add(_Dest, (-_Value)); +#pragma warning( pop ) +} + + +/// <summary> +/// Performs an atomic increment to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_inc(_Inout_ int * _Dest) __GPU_ONLY +{ + unsigned int _Ret; + _Ret = __dp_d3d_interlocked_add(AS_UINT_PTR(_Dest), 1U); + return AS_INT(_Ret); +} + +/// <summary> +/// Performs an atomic increment to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_inc(_Inout_ unsigned int * _Dest) __GPU_ONLY +{ + return __dp_d3d_interlocked_add(_Dest, 1U); +} + +/// <summary> +/// Performs an atomic decrement to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_dec(_Inout_ int * _Dest) __GPU_ONLY +{ +#pragma warning( push ) +#pragma warning( disable : 4146 ) + // Warning 4146: unary minus operator applied to unsigned type, result + // still unsigned. + unsigned int _Ret; + _Ret = __dp_d3d_interlocked_add(AS_UINT_PTR(_Dest), (-(1U))); + return AS_INT(_Ret); +#pragma warning( pop ) +} + +/// <summary> +/// Performs an atomic decrement to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_dec(_Inout_ unsigned int * _Dest) __GPU_ONLY +{ +#pragma warning( push ) +#pragma warning( disable : 4146 ) + // Warning 4146: unary minus operator applied to unsigned type, result + // still unsigned. + return __dp_d3d_interlocked_add(_Dest, (-(1U))); +#pragma warning( pop ) +} + +/// <summary> +/// Sets the value of location pointed to by _Dest to _Value as an atomic operation +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be set to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_exchange(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + unsigned int _Ret = __dp_d3d_interlocked_exchange(AS_UINT_PTR(_Dest), AS_UINT(_Value)); + return AS_INT(_Ret); +} + +/// <summary> +/// Sets the value of location pointed to by _Dest to _Value as an atomic operation +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be set to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_exchange(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_exchange(_Dest, _Value); +} + +/// <summary> +/// Sets the value of location pointed to by _Dest to _Value as an atomic operation +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be set to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline float atomic_exchange(_Inout_ float * _Dest, float _Value) __GPU_ONLY +{ + unsigned int _Ret = __dp_d3d_interlocked_exchange(AS_UINT_PTR(_Dest), AS_UINT(_Value)); + return AS_FLOAT(_Ret); +} + +/// <summary> +/// Atomically, compares the value pointed to by _Dest for equality with that pointed to by _Expected_value, +/// and if true, returns true and replaces the value with _Value, and if false, returns false and updates the value +/// pointed to by _Expected_value with the value pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Expected_value"> +/// Pointer to the the value being compared to the value pointed to by _Dest. If the comparison is unsuccessful, +/// the value is updated with the value pointed to by _Dest +/// </param> +/// <param name="_Value"> +/// The value to be stored to the location pointed to by _Dest if the comparison is successful +/// </param> +/// <returns> +/// If the operation is successful, return true. Otherwise, false +/// </returns> +inline bool atomic_compare_exchange(_Inout_ int * _Dest, _Inout_ int * _Expected_value, int _Value) __GPU_ONLY +{ + int _Old = *_Expected_value; + unsigned int _Ret = __dp_d3d_interlocked_compare_exchange(AS_UINT_PTR(_Dest), AS_UINT(_Value), AS_UINT(_Old)); + if (_Ret == AS_UINT(_Old)) + { + return true; + } + else + { + *_Expected_value = AS_INT(_Ret); + return false; + } +} + +/// <summary> +/// Atomically, compares the value pointed to by _Dest for equality with that pointed to by _Expected_value, +/// and if true, returns true and replaces the value with _Value, and if false, returns false and updates the value +/// pointed to by _Expected_value with the value pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Expected_value"> +/// Pointer to the the value being compared to the value pointed to by _Dest. If the comparison is unsuccessful, +/// the value is updated with the value pointed to by _Dest +/// </param> +/// <param name="_Value"> +/// The value to be stored to the location pointed to by _Dest if the comparison is successful +/// </param> +/// <returns> +/// If the operation is successful, return true. Otherwise, false +/// </returns> +inline bool atomic_compare_exchange(_Inout_ unsigned int * _Dest, _Inout_ unsigned int * _Expected_value, unsigned int _Value) __GPU_ONLY +{ + unsigned int _Old = *_Expected_value; + unsigned int _Ret = __dp_d3d_interlocked_compare_exchange(_Dest, _Value, _Old); + if (_Ret == _Old) + { + return true; + } + else + { + *_Expected_value = _Ret; + return false; + } +} + +/// <summary> +/// Atomically computes the maximum of _Value and the value of the memory location pointed to +/// by _Dest, and stores the maximum value to the memory location +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be compared to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_max(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_max_int(_Dest, _Value); +} + +/// <summary> +/// Atomically computes the maximum of _Value and the value of the memory location pointed to +/// by _Dest, and stores the maximum value to the memory location +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be compared to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_max(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_max_uint(_Dest, _Value); +} + + +/// <summary> +/// Atomically computes the minimum of _Value and the value of the memory location pointed to +/// by _Dest, and stores the minimum value to the memory location +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be compared to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_min(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_min_int(_Dest, _Value); +} + +/// <summary> +/// Atomically computes the minimum of _Value and the value of the memory location pointed to +/// by _Dest, and stores the minimum value to the memory location +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to be compared to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_min(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_min_uint(_Dest, _Value); +} + +/// <summary> +/// Performs an atomic bitwise and operation of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to bitwise and to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_and(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + unsigned int _Ret; + _Ret = __dp_d3d_interlocked_and(AS_UINT_PTR(_Dest), AS_UINT(_Value)); + return AS_INT(_Ret); +} + +/// <summary> +/// Performs an atomic bitwise and operation of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to bitwise and to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_and(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_and(_Dest, _Value); +} + + +/// <summary> +/// Performs an atomic bitwise or operation of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to bitwise or to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_or(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + unsigned int _Ret; + _Ret = __dp_d3d_interlocked_or(AS_UINT_PTR(_Dest), AS_UINT(_Value)); + return AS_INT(_Ret); +} + +/// <summary> +/// Performs an atomic bitwise or operation of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to bitwise or to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_or(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_or(_Dest, _Value); +} + +/// <summary> +/// Performs an atomic bitwise xor operation of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to bitwise xor to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline int atomic_fetch_xor(_Inout_ int * _Dest, int _Value) __GPU_ONLY +{ + unsigned int _Ret; + _Ret = __dp_d3d_interlocked_xor(AS_UINT_PTR(_Dest), AS_UINT(_Value)); + return AS_INT(_Ret); +} + +/// <summary> +/// Performs an atomic bitwise xor operation of _Value to the memory location pointed to by _Dest +/// </summary> +/// <param name="_Dest"> +/// Pointer to the destination location +/// </param> +/// <param name="_Value"> +/// The value to bitwise xor to the location pointed to by _Dest +/// </param> +/// <returns> +/// The original value of the location pointed to by _Dest +/// </returns> +inline unsigned int atomic_fetch_xor(_Inout_ unsigned int * _Dest, unsigned int _Value) __GPU_ONLY +{ + return __dp_d3d_interlocked_xor(_Dest, _Value); +} + +//============================================================================= +// parallel_for_each +//============================================================================= + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain on an accelerator_view. +/// The accelerator_view is determined from the arrays and/or array_views captured by the kernel function, +/// or if no accelerator_view can be derived, the default is chosen. +/// </summary> +/// <param name="_Compute_domain"> +/// An extent which represents the set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "index<_Rank>" which performs the parallel computation. +/// </param> +template <int _Rank, typename _Kernel_type> void parallel_for_each(const extent<_Rank>& _Compute_domain, const _Kernel_type &_Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {accelerator::get_auto_selection_view()}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain that has been tiled into 3-dimensional +/// regions. The accelerator is determined from the arrays and/or array_views captured by the kernel function, +/// or if no accelerator can be derived, the default is chosen. +/// </summary> +/// <param name="_Compute_domain"> +/// A tiled_extent<_Dim0,_Dim1,_Dim2> which represents the tiled set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "tiled_index<_Dim0,_Dim1,_Dim2>" which performs the parallel computation. +/// </param> +template <int _Dim0, int _Dim1, int _Dim2, typename _Kernel_type> void parallel_for_each(const tiled_extent<_Dim0, _Dim1, _Dim2>& _Compute_domain, const _Kernel_type& _Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {accelerator::get_auto_selection_view()}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain that has been tiled into 2-dimensional +/// regions. The accelerator is determined from the arrays and/or array_views captured by the kernel function, +/// or if no accelerator can be derived, the default is chosen. +/// </summary> +/// <param name="_Compute_domain"> +/// A tiled_extent<_Dim0,_Dim1> which represents the tiled set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "tiled_index<_Dim0,_Dim1>" which performs the parallel computation. +/// </param> +template <int _Dim0, int _Dim1, typename _Kernel_type> void parallel_for_each(const tiled_extent<_Dim0, _Dim1>& _Compute_domain, const _Kernel_type& _Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {accelerator::get_auto_selection_view()}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain that has been tiled into 1-dimensional +/// regions. The accelerator is determined from the arrays and/or array_views captured by the kernel function, +/// or if no accelerator can be derived, the default is chosen. +/// </summary> +/// <param name="_Compute_domain"> +/// A tiled_extent<_Dim0> which represents the tiled set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "tiled_index<_Dim0>" which performs the parallel computation. +/// </param> +template <int _Dim0, typename _Kernel_type> void parallel_for_each(const tiled_extent<_Dim0>& _Compute_domain, const _Kernel_type& _Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {accelerator::get_auto_selection_view()}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain on an accelerator. +/// </summary> +/// <param name="_Accl_view"> +/// The accelerator_view upon which to run this parallel computation. +/// </param> +/// <param name="_Compute_domain"> +/// An extent which represents the set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "index<_Rank>" which performs the parallel computation. +/// </param> +template <int _Rank, typename _Kernel_type> void parallel_for_each(const accelerator_view& _Accl_view, const extent<_Rank>& _Compute_domain, const _Kernel_type& _Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {_Accl_view}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain that has been tiled into 3-dimensional +/// regions. +/// </summary> +/// <param name="_Accl_view"> +/// The accelerator_view upon which to run this parallel computation. +/// </param> +/// <param name="_Compute_domain"> +/// A tiled_extent<_Dim0,_Dim1,_Dim2> which represents the tiled set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "tiled_index<_Dim0,_Dim1,_Dim2>" which performs the parallel computation. +/// </param> +template <int _Dim0, int _Dim1, int _Dim2, typename _Kernel_type> void parallel_for_each(const accelerator_view& _Accl_view, const tiled_extent<_Dim0, _Dim1, _Dim2>& _Compute_domain, const _Kernel_type& _Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {_Accl_view}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain that has been tiled into 2-dimensional +/// regions. +/// </summary> +/// <param name="_Accl_view"> +/// The accelerator_view upon which to run this parallel computation. +/// </param> +/// <param name="_Compute_domain"> +/// A tiled_extent<_Dim0,_Dim1> which represents the tiled set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "tiled_index<_Dim0,_Dim1>" which performs the parallel computation. +/// </param> +template <int _Dim0, int _Dim1, typename _Kernel_type> void parallel_for_each(const accelerator_view& _Accl_view, const tiled_extent<_Dim0, _Dim1>& _Compute_domain, const _Kernel_type& _Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {_Accl_view}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + +/// <summary> +/// Invokes a parallel computation of a kernel function over a compute domain that has been tiled into 1-dimensional +/// regions. +/// </summary> +/// <param name="_Accl_view"> +/// The accelerator_view upon which to run this parallel computation. +/// </param> +/// <param name="_Compute_domain"> +/// A tiled_extent<_Dim0> which represents the tiled set of indices that form the compute domain. +/// </param> +/// <param name="_Kernel"> +/// A function object that takes an argument of type "tiled_index<_Dim0>" which performs the parallel computation. +/// </param> +template <int _Dim0, typename _Kernel_type> void parallel_for_each(const accelerator_view& _Accl_view, const tiled_extent<_Dim0>& _Compute_domain, const _Kernel_type& _Kernel) +{ + _Host_Scheduling_info _SchedulingInfo = {_Accl_view}; + details::_Parallel_for_each(&_SchedulingInfo, _Compute_domain, _Kernel); +} + + + +//============================================================================= + +extern "C" +{ + +// Debugging intrinsics +void direct3d_abort() __GPU_ONLY; +void direct3d_errorf(const char *, ...) __GPU_ONLY; +void direct3d_printf(const char *, ...) __GPU_ONLY; + +} + +////////////////////////////////////////////////////////////////////// +/// Memory fences and tile barriers + +#pragma warning( push ) +#pragma warning( disable : 4100 ) // unreferenced formal parameter + +/// <summary> +/// Ensures that memory accesses are visible to other threads in the thread tile, and are executed according to program order +/// </summary> +/// <param name="_Barrier"> +/// A tile_barrier object +/// </param> +inline void all_memory_fence(const tile_barrier & _Barrier) __GPU_ONLY +{ + __dp_d3d_all_memory_fence(); +} + +/// <summary> +/// Ensures that global memory accesses are visible to other threads in the thread tile, and are executed according to program order +/// </summary> +/// <param name="_Barrier"> +/// A tile_barrier object +/// </param> +inline void global_memory_fence(const tile_barrier & _Barrier) __GPU_ONLY +{ + __dp_d3d_device_memory_fence(); +} + +/// <summary> +/// Ensures that tile_static memory accesses are visible to other threads in the thread tile, and are executed according to program order +/// </summary> +/// <param name="_Barrier"> +/// A tile_barrier object +/// </param> +inline void tile_static_memory_fence(const tile_barrier & _Barrier) __GPU_ONLY +{ + __dp_d3d_tile_static_memory_fence(); +} + +#pragma warning( pop ) + + + +namespace direct3d +{ + +/// <summary> +/// Returns the absolute value of the argument +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <returns> +/// Returns the absolute value of the argument +/// </returns> +inline int abs(int _X) __GPU_ONLY +{ + return __dp_d3d_absi(_X); +} + +/// <summary> +/// Clamps _X to the specified _Min and _Max range +/// </summary> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <param name="_Min"> +/// Floating-point value +/// </param> +/// <param name="_Max"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns the clamped value of _X +/// </returns> +inline float clamp(float _X, float _Min, float _Max) __GPU_ONLY +{ + return __dp_d3d_clampf(_X, _Min, _Max); +} + +/// <summary> +/// Clamps _X to the specified _Min and _Max range +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <param name="_Min"> +/// Integer value +/// </param> +/// <param name="_Max"> +/// Integer value +/// </param> +/// <returns> +/// Returns the clamped value of _X +/// </returns> +inline int clamp(int _X, int _Min, int _Max) __GPU_ONLY +{ + return __dp_d3d_clampi(_X, _Min, _Max); +} + +/// <summary> +/// Counts the number of set bits in _X +/// </summary> +/// <param name="_X"> +/// Unsigned integer value +/// </param> +/// <returns> +/// Returns the number of set bits in _X +/// </returns> +inline unsigned int countbits(unsigned int _X) __GPU_ONLY +{ + return __dp_d3d_countbitsu(_X); +} + +/// <summary> +/// Gets the location of the first set bit in _X, starting from the highest order bit and working downward +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <returns> +/// Returns The location of the first set bit +/// </returns> +inline int firstbithigh(int _X) __GPU_ONLY +{ + return __dp_d3d_firstbithighi(_X); +} + +/// <summary> +/// Gets the location of the first set bit in _X, starting from the lowest order bit and working upward +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <returns> +/// Returns The location of the first set bit +/// </returns> +inline int firstbitlow(int _X) __GPU_ONLY +{ + return __dp_d3d_firstbitlowi(_X); +} + +/// <summary> +/// Determine the maximum numeric value of the arguments +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <param name="_Y"> +/// Integer value +/// </param> +/// <returns> +/// Return the maximum numeric value of the arguments +/// </returns> +inline int imax(int _X, int _Y) __GPU_ONLY +{ + return __dp_d3d_maxi(_X, _Y); +} + +/// <summary> +/// Determine the minimum numeric value of the arguments +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <param name="_Y"> +/// Integer value +/// </param> +/// <returns> +/// Return the minimum numeric value of the arguments +/// </returns> +inline int imin(int _X, int _Y) __GPU_ONLY +{ + return __dp_d3d_mini(_X, _Y); +} + +/// <summary> +/// Determine the maximum numeric value of the arguments +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <param name="_Y"> +/// Integer value +/// </param> +/// <returns> +/// Return the maximum numeric value of the arguments +/// </returns> +inline unsigned int umax(unsigned int _X, unsigned int _Y) __GPU_ONLY +{ + return __dp_d3d_maxu(_X, _Y); +} + +/// <summary> +/// Determine the minimum numeric value of the arguments +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <param name="_Y"> +/// Integer value +/// </param> +/// <returns> +/// Return the minimum numeric value of the arguments +/// </returns> +inline unsigned int umin(unsigned int _X, unsigned int _Y) __GPU_ONLY +{ + return __dp_d3d_minu(_X, _Y); +} + +/// <summary> +/// Performs an arithmetic multiply/add operation on three arguments: _X * _Y + _Z +/// </summary> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <param name="_Y"> +/// Floating-point value +/// </param> +/// <param name="_Z"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns _X * _Y + _Z +/// </returns> +inline float mad(float _X, float _Y, float _Z) __GPU_ONLY +{ + return __dp_d3d_madf(_X, _Y, _Z); +} + +/// <summary> +/// Performs an arithmetic multiply/add operation on three arguments: _X * _Y + _Z +/// </summary> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <param name="_Y"> +/// Floating-point value +/// </param> +/// <param name="_Z"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns _X * _Y + _Z +/// </returns> +inline double mad(double _X, double _Y, double _Z) __GPU_ONLY +{ + return __dp_d3d_madd(_X, _Y, _Z); +} + +/// <summary> +/// Performs an arithmetic multiply/add operation on three arguments: _X * _Y + _Z +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <param name="_Y"> +/// Integer value +/// </param> +/// <param name="_Z"> +/// Integer value +/// </param> +/// <returns> +/// Returns _X * _Y + _Z +/// </returns> +inline int mad(int _X, int _Y, int _Z) __GPU_ONLY +{ + return __dp_d3d_madi(_X, _Y, _Z); +} + +/// <summary> +/// Performs an arithmetic multiply/add operation on three arguments: _X * _Y + _Z +/// </summary> +/// <param name="_X"> +/// Unsigned integer value +/// </param> +/// <param name="_Y"> +/// Unsigned integer value +/// </param> +/// <param name="_Z"> +/// Unsigned integer value +/// </param> +/// <returns> +/// Returns _X * _Y + _Z +/// </returns> +inline unsigned int mad(unsigned int _X, unsigned int _Y, unsigned int _Z) __GPU_ONLY +{ + return __dp_d3d_madu(_X, _Y, _Z); +} + +/// <summary> +/// Generates a random value using the Perlin noise algorithm +/// </summary> +/// <param name="_X"> +/// Floating-point value from which to generate Perlin noise +/// </param> +/// <returns> +/// Returns The Perlin noise value within a range between -1 and 1 +/// </returns> +inline float noise(float _X) __GPU_ONLY +{ + return __dp_d3d_noisef(_X); +} + +/// <summary> +/// Converts _X from degrees to radians +/// </summary> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns _X converted from degrees to radians +/// </returns> +inline float radians(float _X) __GPU_ONLY +{ + return __dp_d3d_radiansf(_X); +} + +/// <summary> +/// Calculates a fast, approximate reciprocal of the argument +/// </summary> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns a fast, approximate reciprocal of the argument +/// </returns> +inline float rcp(float _X) __GPU_ONLY +{ + return __dp_d3d_rcpf(_X); +} + +/// <summary> +/// Reverses the order of the bits in _X +/// </summary> +/// <param name="_X"> +/// Unsigned integer value +/// </param> +/// <returns> +/// Returns the value with the bit order reversed in _X +/// </returns> +inline unsigned int reversebits(unsigned int _X) __GPU_ONLY +{ + return __dp_d3d_reversebitsu(_X); +} + +/// <summary> +/// Clamps _X within the range of 0 to 1 +/// </summary> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns _X clamped within the range of 0 to 1 +/// </returns> +inline float saturate(float _X) __GPU_ONLY +{ + return __dp_d3d_saturatef(_X); +} + +/// <summary> +/// Returns the sign of the argument +/// </summary> +/// <param name="_X"> +/// Integer value +/// </param> +/// <returns> +/// Returns the sign of the argument +/// </returns> +inline int sign(int _X) __GPU_ONLY +{ + return __dp_d3d_signi(_X); +} + +/// <summary> +/// Returns a smooth Hermite interpolation between 0 and 1, if _X is in the range [_Min, _Max]. +/// </summary> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <param name="_Min"> +/// Floating-point value +/// </param> +/// <param name="_Max"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns 0 if _X is less than _Min; 1 if _X is greater than _Max; otherwise, a value between 0 and 1 if _X is in the range [_Min, _Max] +/// </returns> +inline float smoothstep(float _Min, float _Max, float _X) __GPU_ONLY +{ + return __dp_d3d_smoothstepf(_Min, _Max, _X); +} + +/// <summary> +/// Compares two values, returning 0 or 1 based on which value is greater +/// </summary> +/// <param name="_Y"> +/// Floating-point value +/// </param> +/// <param name="_X"> +/// Floating-point value +/// </param> +/// <returns> +/// Returns 1 if the _X is greater than or equal to _Y; otherwise, 0 +/// </returns> +inline float step(float _Y, float _X) __GPU_ONLY +{ + return __dp_d3d_stepf(_Y, _X); +} + +} // namespace Concurrency::direct3d + +} // namespace Concurrency + +#include <xxamp_inl.h> + +namespace concurrency = Concurrency; + +#pragma pack(pop) +// End of file diff --git a/test_data/lots_of_files/amp_graphics.h b/test_data/lots_of_files/amp_graphics.h new file mode 100644 index 0000000..fa46ada --- /dev/null +++ b/test_data/lots_of_files/amp_graphics.h @@ -0,0 +1,4942 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* amp_graphics.h +* +* C++ AMP Graphics Library +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include <amp_short_vectors.h> +#include <array> +#include <dxgiformat.h> +#include <sstream> + +#define _AMP_GRAPHICS_H + +namespace Concurrency +{ + +namespace graphics +{ + +namespace details +{ + +#pragma warning( push ) +#pragma warning( disable : 6326 ) // Potential comparison of a constant with another constant + +template<typename _Ty> +struct _Short_vector_type_traits +{ + typedef void _Scalar_type; + static const bool _Is_valid_SVT_for_texture = false; + static const _Short_vector_base_type_id _Format_base_type_id = _Invalid_type; + static const unsigned int _Num_channels = 0; + static const unsigned int _Default_bits_per_channel = 0; +}; + +template<> +struct _Short_vector_type_traits<unsigned int> +{ + typedef unsigned int _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Uint_type; + static const unsigned int _Num_channels = 1; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<uint_2> +{ + typedef uint_2::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Uint_type; + static const unsigned int _Num_channels = 2; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<uint_3> +{ + typedef uint_3::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Uint_type; + static const unsigned int _Num_channels = 3; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<uint_4> +{ + typedef uint_4::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Uint_type; + static const unsigned int _Num_channels = 4; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<int> +{ + typedef int _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Int_type; + static const unsigned int _Num_channels = 1; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<int_2> +{ + typedef int_2::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Int_type; + static const unsigned int _Num_channels = 2; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<int_3> +{ + typedef int_3::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Int_type; + static const unsigned int _Num_channels = 3; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<int_4> +{ + typedef int_4::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Int_type; + static const unsigned int _Num_channels = 4; + static const unsigned int _Default_bits_per_channel = 32; +}; + + +template<> +struct _Short_vector_type_traits<float> +{ + typedef float _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Float_type; + static const unsigned int _Num_channels = 1; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<float_2> +{ + typedef float_2::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Float_type; + static const unsigned int _Num_channels = 2; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<float_3> +{ + typedef float_3::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Float_type; + static const unsigned int _Num_channels = 3; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<float_4> +{ + typedef float_4::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Float_type; + static const unsigned int _Num_channels = 4; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<unorm> +{ + typedef unorm _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Unorm_type; + static const unsigned int _Num_channels = 1; + static const unsigned int _Default_bits_per_channel = 16; +}; + +template<> +struct _Short_vector_type_traits<unorm_2> +{ + typedef unorm_2::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Unorm_type; + static const unsigned int _Num_channels = 2; + static const unsigned int _Default_bits_per_channel = 16; +}; + +template<> +struct _Short_vector_type_traits<unorm_3> +{ + typedef unorm_3::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = false; + static const _Short_vector_base_type_id _Format_base_type_id = _Invalid_type; + static const unsigned int _Num_channels = 0; + static const unsigned int _Default_bits_per_channel = 0; +}; + +template<> +struct _Short_vector_type_traits<unorm_4> +{ + typedef unorm_4::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Unorm_type; + static const unsigned int _Num_channels = 4; + static const unsigned int _Default_bits_per_channel = 16; +}; + +template<> +struct _Short_vector_type_traits<norm> +{ + typedef norm _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Norm_type; + static const unsigned int _Num_channels = 1; + static const unsigned int _Default_bits_per_channel = 16; +}; + +template<> +struct _Short_vector_type_traits<norm_2> +{ + typedef norm_2::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Norm_type; + static const unsigned int _Num_channels = 2; + static const unsigned int _Default_bits_per_channel = 16; +}; + +template<> +struct _Short_vector_type_traits<norm_3> +{ + typedef norm_3::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = false; + static const _Short_vector_base_type_id _Format_base_type_id = _Invalid_type; + static const unsigned int _Num_channels = 0; + static const unsigned int _Default_bits_per_channel = 0; +}; + +template<> +struct _Short_vector_type_traits<norm_4> +{ + typedef norm_4::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Norm_type; + static const unsigned int _Num_channels = 4; + static const unsigned int _Default_bits_per_channel = 16; +}; + + +template<> +struct _Short_vector_type_traits<double> +{ + typedef double _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Double_type; + static const unsigned int _Num_channels = 2; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<double_2> +{ + typedef double_2::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = true; + static const _Short_vector_base_type_id _Format_base_type_id = _Double_type; + static const unsigned int _Num_channels = 4; + static const unsigned int _Default_bits_per_channel = 32; +}; + +template<> +struct _Short_vector_type_traits<double_3> +{ + typedef double_3::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = false; + static const _Short_vector_base_type_id _Format_base_type_id = _Invalid_type; + static const unsigned int _Num_channels = 0; + static const unsigned int _Default_bits_per_channel = 0; +}; + +template<> +struct _Short_vector_type_traits<double_4> +{ + typedef double_4::value_type _Scalar_type; + static const bool _Is_valid_SVT_for_texture = false; + static const _Short_vector_base_type_id _Format_base_type_id = _Invalid_type; + static const unsigned int _Num_channels = 0; + static const unsigned int _Default_bits_per_channel = 0; +}; + +template<typename _Short_vector_type> +unsigned int _Get_default_bits_per_scalar_element() +{ + return _Short_vector_type_traits<_Short_vector_type>::_Format_base_type_id == _Double_type ? + _Short_vector_type_traits<_Short_vector_type>::_Default_bits_per_channel * 2 : + _Short_vector_type_traits<_Short_vector_type>::_Default_bits_per_channel; +} + +template<int _Rank> +std::array<size_t, 3> _Get_dimensions(const Concurrency::extent<_Rank> & _Ext, unsigned int _Mip_offset) +{ + std::array<size_t, 3> _Arr; + // For un-used dimensions, use value 1. + switch((_Rank)) { + case 1: + _Arr[0] = static_cast<size_t>((_Ext[0] >> _Mip_offset) ? (_Ext[0] >> _Mip_offset) : 1U); + _Arr[1] = 1; + _Arr[2] = 1; + break; + case 2: + _Arr[0] = static_cast<size_t>((_Ext[1] >> _Mip_offset) ? (_Ext[1] >> _Mip_offset) : 1U); + _Arr[1] = static_cast<size_t>((_Ext[0] >> _Mip_offset) ? (_Ext[0] >> _Mip_offset) : 1U); + _Arr[2] = 1; + break; + case 3: + _Arr[0] = static_cast<size_t>((_Ext[2] >> _Mip_offset) ? (_Ext[2] >> _Mip_offset) : 1U); + _Arr[1] = static_cast<size_t>((_Ext[1] >> _Mip_offset) ? (_Ext[1] >> _Mip_offset) : 1U); + _Arr[2] = static_cast<size_t>((_Ext[0] >> _Mip_offset) ? (_Ext[0] >> _Mip_offset) : 1U); + break; + default: + _ASSERTE(false); + _Arr[0] = 1; + _Arr[1] = 1; + _Arr[2] = 1; + break; + } + return _Arr; +} + +template <int _Rank> +std::array<size_t, 3> _Get_indices(const index<_Rank> &_Idx) +{ + std::array<size_t, 3> _Arr; + // For un-used dimensions, use value 0. + switch((_Rank)) { + case 1: + _Arr[0] = static_cast<size_t>(_Idx[0]); + _Arr[1] = 0; + _Arr[2] = 0; + break; + case 2: + _Arr[0] = static_cast<size_t>(_Idx[1]); + _Arr[1] = static_cast<size_t>(_Idx[0]); + _Arr[2] = 0; + break; + case 3: + _Arr[0] = static_cast<size_t>(_Idx[2]); + _Arr[1] = static_cast<size_t>(_Idx[1]); + _Arr[2] = static_cast<size_t>(_Idx[0]); + break; + default: + _ASSERTE(false); + _Arr[0] = 0; + _Arr[1] = 0; + _Arr[2] = 0; + break; + } + return _Arr; +} + +template<int _Rank> +Concurrency::extent<_Rank> _Create_extent(size_t _Width, size_t _Height, size_t _Depth) +{ + extent<_Rank> _Ext; + switch((_Rank)) { + case 1: + _Ext[0] = static_cast<int>(_Width); + break; + case 2: + _Ext[0] = static_cast<int>(_Height); + _Ext[1] = static_cast<int>(_Width); + break; + case 3: + _Ext[0] = static_cast<int>(_Depth); + _Ext[1] = static_cast<int>(_Height); + _Ext[2] = static_cast<int>(_Width); + break; + default: + _ASSERTE(false); + break; + } + return _Ext; +} + +// forward declaration +template <typename _Value_type, int _Rank> class _Texture_base; +template <typename _Value_type, int _Rank> +_Event _Copy_async_impl(const void * _Src, unsigned int _Src_byte_size, const _Texture_base<_Value_type, _Rank>& _Dst, const index<_Rank> &_Offset, const Concurrency::extent<_Rank> &_Copy_extent); +template <typename OutputIterator, typename _Value_type, int _Rank> +_Event _Copy_async_impl(const _Texture_base<_Value_type, _Rank> &_Src, OutputIterator _Dest_iter); + +template<typename _Value_type, int _Rank> +_Event _Copy_async_impl(const _Texture_base<_Value_type, _Rank>& _Src, const index<_Rank> &_Src_offset, + const _Texture_base<_Value_type, _Rank>& _Dst, const index<_Rank> &_Dst_offset, const extent<_Rank> &_Copy_extent); + +// The base class for texture, writeonly_texture_view +template <typename _Value_type, int _Rank> +class _Texture_base +{ + static_assert(_Rank > 0 && _Rank <= 3, "texture is only supported for rank 1, 2, and 3."); + static_assert(_Short_vector_type_traits<typename std::remove_const<_Value_type>::type>::_Is_valid_SVT_for_texture, "invalid value_type for a texture."); + + // Friends + template<typename _T> + friend const _Texture_descriptor& Concurrency::details::_Get_texture_descriptor(const _T& _Tex) __GPU; + template<typename _T> + friend _Ret_ _Texture* Concurrency::details::_Get_texture(const _T& _Tex) __CPU_ONLY; + template<typename _Value_type, int _Rank> + friend _Event _Copy_async_impl(const _Texture_base<_Value_type, _Rank>& _Src, const index<_Rank> &_Src_offset, + const _Texture_base<_Value_type, _Rank>& _Dst, const index<_Rank> &_Dst_offset, const extent<_Rank> &_Copy_extent) __CPU_ONLY; + +public: + static const int rank = _Rank; + typedef typename _Value_type value_type; + typedef typename _Short_vector_type_traits<_Value_type>::_Scalar_type scalar_type; + +public: + /// <summary> + /// Returns the extent that defines the shape of this texture or texture view. + /// </summary> + __declspec(property(get=get_extent)) Concurrency::extent<_Rank> extent; + Concurrency::extent<_Rank> get_extent() const __GPU + { + return _M_extent; + } + + /// <summary> + /// Returns the extent for specific mipmap level of this texture or texture view. + /// </summary> + /// <param> + /// Mipmap level for which extent should be calculated. + /// </param> + Concurrency::extent<_Rank> get_mipmap_extent(unsigned int _Mipmap_level) const __CPU_ONLY + { + if (_Mipmap_level >= this->get_mipmap_levels()) + { + std::stringstream _Err_msg; + _Err_msg << "Value for _Mipmap_level parameter (" << _Mipmap_level + << ") cannot be greater than or equal to number of mipmap levels (" + << this->get_mipmap_levels() << ") on the texture or texture view"; + + throw runtime_exception(_Err_msg.str().c_str(), E_INVALIDARG); + } + return Concurrency::details::_Get_extent_at_level(_M_extent, _Mipmap_level); + } + + /// <summary> + /// Returns the extent for specific mipmap level of this texture or texture view. + /// </summary> + /// <param> + /// Mipmap level for which extent should be calculated. + /// </param> + Concurrency::extent<_Rank> get_mipmap_extent(unsigned int _Mipmap_level) const __GPU_ONLY + { + return Concurrency::details::_Get_extent_at_level_unsafe(_M_extent, _Mipmap_level); + } + + /// <summary> + /// Returns the accelerator_view where this texture or texture view is located. + /// </summary> + __declspec(property(get=get_accelerator_view)) Concurrency::accelerator_view accelerator_view; + Concurrency::accelerator_view get_accelerator_view() const __CPU_ONLY + { + return _Get_texture()->_Get_access_on_accelerator_view(); + } + + /// <summary> + /// Returns the number of bits per scalar element + /// </summary> + __declspec(property(get=get_bits_per_scalar_element)) unsigned int bits_per_scalar_element; + unsigned int get_bits_per_scalar_element() const __CPU_ONLY + { + unsigned int _Bits_per_channel = _Get_texture()->_Get_bits_per_channel(); + return _Short_vector_type_traits<_Value_type>::_Format_base_type_id == _Double_type ? _Bits_per_channel * (sizeof(double)/sizeof(int)) : _Bits_per_channel; + } + + /// <summary> + /// Query how many mipmap levels are accessible by this texture (or texture view). + /// </summary> + /// <returns> + /// Returns number of mipmap levels accessible by this texture (or texture view). + /// </returns> + __declspec(property(get=get_mipmap_levels)) unsigned int mipmap_levels; + unsigned int get_mipmap_levels() const __GPU + { + return _M_texture_descriptor._Get_view_mipmap_levels(); + } + + /// <summary> + /// Returns the physical data length (in bytes) that is required in order to represent + /// the texture on the host side with its native format. + /// If the texture contains multiple mipmap levels the value represents the sum of physical data length for each accessible mipmap level by this texture (or texture view). + /// </summary> + __declspec(property(get=get_data_length)) unsigned int data_length; + unsigned int get_data_length() const __CPU_ONLY + { + return _Get_texture()->_Get_data_length(this->_Get_most_detailed_mipmap_level(), this->get_mipmap_levels()); + } + +protected: + // internal storage abstraction + typedef Concurrency::details::_Texture_descriptor _Texture_descriptor; + + _Texture_base() __CPU_ONLY + { + // This default ctor is required to enable move ctor for a derived types, + // empty _Texture_base is later initialized by move assigment operator + } + + _Texture_base(const Concurrency::extent<_Rank>& _Ext, unsigned int _Mipmap_levels = 1) __CPU_ONLY + : _M_extent(_Ext), _M_texture_descriptor(/*_Most_detailed_mipmap_level=*/0, _Mipmap_levels) + { + _Is_valid_extent(_M_extent); + _Are_valid_mipmap_parameters(/*_Most_detailed_mipmap_level=*/0, _Mipmap_levels); + + // Validate if we can generate _Mipmap_levels number of mipmap levels given the dimensionality of the texture + unsigned int _Max_mipmap_levels = _Get_max_mipmap_levels(_M_extent); + if (_Mipmap_levels > _Max_mipmap_levels) + { + std::stringstream _Err_msg; + _Err_msg << "The texture extent is too small to generate (" << _Mipmap_levels << ") mipmap levels, the maximum allowed is (" << _Max_mipmap_levels << ")"; + throw runtime_exception(_Err_msg.str().c_str(), E_INVALIDARG); + } + else if (_Mipmap_levels == 0) + { + // Generate full range of all mipmaps + // e.g. 2D 10x2 texture would have: 10x2, 5x1, 2x1, 1x1 (4 mipmap levels) + _Mipmap_levels = _Max_mipmap_levels; + } + _M_texture_descriptor._Set_view_mipmap_levels(_Mipmap_levels); + } + + // shallow copy for texture_views + _Texture_base(const _Texture_base & _Src) __GPU + : _M_extent(_Src._M_extent), _M_texture_descriptor(_Src._M_texture_descriptor) + { + } + + // shallow copy for texture_views that redefine range of mipmaps + _Texture_base(const _Texture_base & _Src, unsigned int _Most_detailed_mipmap_level, unsigned int _View_mipmap_levels) __CPU_ONLY + : _M_extent(_Get_extent_at_level(_Src.extent, _Most_detailed_mipmap_level)), _M_texture_descriptor(_Src._M_texture_descriptor, _Src._Get_most_detailed_mipmap_level() + _Most_detailed_mipmap_level, _View_mipmap_levels) + { + Concurrency::details::_Is_valid_mipmap_range(_Src.get_mipmap_levels(), _Most_detailed_mipmap_level, _View_mipmap_levels); + } + + // shallow copy for texture_views that in restrict(amp) context, the texture views can no longer redefine mipmap range, + // but read-write texture view needs to flatten to single mipmap level when created over a texture with multiple mipmap levels. + _Texture_base(const _Texture_base & _Src, bool _Flatten_mipmap_levels) __GPU_ONLY + : _M_extent(_Src.extent), _M_texture_descriptor(_Src._M_texture_descriptor, /*_Most_detailed_mipmap_level=*/0, _Flatten_mipmap_levels ? /*_View_mipmap_levels=*/1 : _Src.get_mipmap_levels()) + { + } + + // interop + _Texture_base(const Concurrency::extent<_Rank>& _Ext, const _Texture_descriptor & _Desc) __CPU_ONLY + : _M_extent(_Ext), _M_texture_descriptor(_Desc) + { + Concurrency::details::_Is_valid_extent(_M_extent); + } + + void _Copy_to(const _Texture_base & _Dest) const __CPU_ONLY + { + if (!(*this == _Dest)) + { + _ASSERTE(this->extent == _Dest.extent); + details::_Copy_async_impl(*this, index<_Rank>(), _Dest, index<_Rank>(), _Dest.extent)._Get(); + } + } + + bool operator==(const _Texture_base & _Other) const __CPU_ONLY + { + return _Other._M_extent == _M_extent && _Other._M_texture_descriptor == _M_texture_descriptor; + } + + ~_Texture_base() __GPU + { + } + + _Ret_ _Texture* _Get_texture() const __CPU_ONLY + { + return _M_texture_descriptor._Get_texture_ptr(); + } + + unsigned int _Get_most_detailed_mipmap_level() const __GPU + { + return _M_texture_descriptor._Get_most_detailed_mipmap_level(); + } + + bool _Are_mipmap_levels_overlapping(const _Texture_base &_Other) const __CPU_ONLY + { + return _M_texture_descriptor._Are_mipmap_levels_overlapping(&_Other._M_texture_descriptor); + } + +protected: + Concurrency::extent<_Rank> _M_extent; + _Texture_descriptor _M_texture_descriptor; +}; + +inline void _Is_valid_data_length(unsigned int _Num_elems, unsigned int _Bits_per_elem) +{ + unsigned long long _Bytes_per_elem = static_cast<unsigned long long>(_Bits_per_elem / 8U); + unsigned long long _Total_bytes = static_cast<unsigned long long>(_Num_elems) * _Bytes_per_elem; + if (_Total_bytes > static_cast<unsigned long long>(UINT_MAX)) + { + throw runtime_exception("Invalid - texture data_length exceeds UINT_MAX", E_INVALIDARG); + } +} + +} // namespace details + + +using Concurrency::graphics::details::_Short_vector_type_traits; + +// forward declarations +template <typename _Value_type, int _Rank> +class texture; +template <typename _Value_type, int _Rank> +class writeonly_texture_view; +template <typename _Value_type, int _Rank> +class texture_view; +class sampler; + +namespace direct3d +{ +template<typename _Value_type, int _Rank> +texture<_Value_type, _Rank> make_texture(const Concurrency::accelerator_view &_Av, _In_ IUnknown *_D3D_texture, DXGI_FORMAT _View_format = DXGI_FORMAT_UNKNOWN) __CPU_ONLY; + +sampler make_sampler(_In_ IUnknown *_D3D_sampler) __CPU_ONLY; +_Ret_ IUnknown * get_sampler(const Concurrency::accelerator_view &_Av, const sampler &_Sampler) __CPU_ONLY; + +} // namespace direct3d + +/// <summary> +/// A texture is a data aggregate on an accelerator_view in the extent domain. +/// It is a collection of variables, one for each element in an extent domain. +/// Each variable holds a value corresponding to C++ primitive type (unsigned int, +/// int, float, double), or scalar type norm, or unorm (defined in concurrency::graphics), +/// or eligible short vector types defined in concurrency::graphics. +/// </summary> +/// <param name="_Value_type"> +/// The type of the elements in the texture aggregates. +/// </param> +/// <param name="_Rank"> +/// The _Rank of the corresponding extent domain. +/// </param> +template <typename _Value_type, int _Rank> class texture : public details::_Texture_base<_Value_type, _Rank> +{ + template<typename _Value_type, int _Rank> + friend texture<_Value_type,_Rank> direct3d::make_texture(const Concurrency::accelerator_view &_Av, _In_ IUnknown *_D3D_texture, DXGI_FORMAT _View_format) __CPU_ONLY; + + static_assert(!std::is_const<_Value_type>::value, "const value type is not supported for texture."); + +public: + + /// <summary> + /// Construct a texture from extents. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the texture. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext) __CPU_ONLY + : _Texture_base(_Ext) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view); + } + + /// <summary> + /// Construct texture<T,1> with the extent _E0 + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + texture(int _E0) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int) is only permissible on texture<value_type, 1>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view); + } + + /// <summary> + /// Construct a texture<T,2> from two integer extents. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + texture(int _E0, int _E1) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int) is only permissible on texture<value_type, 2>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view); + } + + /// <summary> + /// Construct a texture<T,3> from three integer extents. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + texture(int _E0, int _E1, int _E2) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int) is only permissible on texture<value_type, 3>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view); + } + + /// <summary> + /// Construct a texture from extents, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Extent"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(_Ext) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av); + } + + /// <summary> + /// Construct a staging texture from extents, bound to a specific accelerator_view + /// and an associated accelerator_view that is the preferred location for copying + /// to/from this texture. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(_Ext) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av); + } + + /// <summary> + /// Construct a texture<T,1> with the extent _E0, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, accelerator_view) is only permissible on texture<value_type, 1>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av); + } + + /// <summary> + /// Construct a staging texture<T,1> with the extent _E0, bound to a specific + /// accelerator_view and an associated accelerator_view that is the preferred location + /// for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 1>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av); + } + + /// <summary> + /// Construct a texture<T,2> from two integer extents, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, int _E1, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, accelerator_view) is only permissible on texture<value_type, 2>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av); + } + + /// <summary> + /// Construct a staging texture<T,2> from two integer extents, bound to a + /// specific accelerator_view and an associated accelerator_view that is the + /// preferred location for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, int _E1, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 2>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av); + } + + /// <summary> + /// Construct a texture<T,3> from three integer extents, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, int _E1, int _E2, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, accelerator_view) is only permissible on texture<value_type, 3>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av); + } + + /// <summary> + /// Construct a staging texture<T,3> from three integer extents, bound to a + /// specific accelerator_view and an associated accelerator_view that is the preferred + /// location for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, int _E1, int _E2, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 3>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av); + } + + /// <summary> + /// Construct a texture initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + template<typename _Input_iterator> texture(const Concurrency::extent<_Rank>& _Ext, _Input_iterator _Src_first, _Input_iterator _Src_last) __CPU_ONLY + : _Texture_base(_Ext) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a texture<T,1> with the extent _E0 and from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + template<typename _Input_iterator> texture(int _E0, _Input_iterator _Src_first, _Input_iterator _Src_last) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, iterator, iterator) is only permissible on texture<value_type, 1>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a texture<T,2> with two integers and initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + template<typename _Input_iterator> texture(int _E0, int _E1, _Input_iterator _Src_first, _Input_iterator _Src_last) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, iterator, iterator) is only permissible on texture<value_type, 2>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Src_first, _Src_last); + } + + + /// <summary> + /// Construct a texture<T,3> with three integers and initialized from a pair of iterators into a container. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + template<typename _Input_iterator> texture(int _E0, int _E1, int _E2, _Input_iterator _Src_first, _Input_iterator _Src_last) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, iterator, iterator) is only permissible on texture<value_type, 3>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a texture initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + template<typename _Input_iterator> texture(const Concurrency::extent<_Rank>& _Ext, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(_Ext) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging texture initialized from a pair of iterators into a container, + /// bound to a specific accelerator_view and an associated accelerator_view that is the + /// preferred location for copying to/from this texture. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + template<typename _Input_iterator> texture(const Concurrency::extent<_Rank>& _Ext, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(_Ext) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a texture<T,1> with integer _E0 and initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + template<typename _Input_iterator> texture(int _E0, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, iterator, iterator, accelerator_view) is only permissible on texture<value_type, 1>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging texture<T,1> with integer _E0 and initialized from a pair of iterators + /// into a container, bound to a specific accelerator_view and an associated accelerator_view that is + /// the preferred location for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + template<typename _Input_iterator> texture(int _E0, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, iterator, iterator, accelerator_view, accelerator_view) is only permissible on texture<value_type, 1>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a texture<T,2> with two integers and initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + template<typename _Input_iterator> texture(int _E0, int _E1, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, iterator, iterator, accelerator_view) is only permissible on texture<value_type, 2>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging texture<T,2> with two integers and initialized from a pair of iterators + /// into a container, bound to a specific accelerator_view and an associated accelerator_view that is + /// the preferred location for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + template<typename _Input_iterator> texture(int _E0, int _E1, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, iterator, iterator, accelerator_view, accelerator_view) is only permissible on texture<value_type, 2>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a texture<T,3> with three integers and initialized from a pair of iterators into a container, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + template<typename _Input_iterator> texture(int _E0, int _E1, int _E2, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, iterator, iterator, accelerator_view) is only permissible on texture<value_type, 3>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a staging texture<T,3> with three integers and initialized from a pair of iterators + /// into a container, bound to a specific accelerator_view and an associated accelerator_view that is the + /// preferred location for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Src_last"> + /// An ending iterator into the source container. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + template<typename _Input_iterator> texture(int _E0, int _E1, int _E2, _Input_iterator _Src_first, _Input_iterator _Src_last, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, iterator, iterator, accelerator_view, accelerator_view) is only permissible on texture<value_type, 3>."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "texture cannot be constructed from unorm based short vectors via this constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "texture cannot be constructed from norm based short vectors via this constructor."); + _Initialize(_Av, _Associated_av, _Src_first, _Src_last); + } + + /// <summary> + /// Construct a texture from extents and specified bits per scalar element + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(_Ext) + { + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture from extents, specified bits per scalar element and number of mipmap levels + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Mipmap_levels"> + /// Number of mipmap levels in the underlying texture. + /// If 0 is specified, the texture will have full range of mipmap levels down to smallest possible size for the given extent. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, unsigned int _Bits_per_scalar_element, unsigned int _Mipmap_levels) __CPU_ONLY + : _Texture_base(_Ext, _Mipmap_levels) + { + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T,1> with integer _E0 and specified bits per scalar element + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(int _E0, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, unsigned int) is only permissible on texture<value_type, 1>."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T,2> with two integers and specified bits per scalar element + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(int _E0, int _E1, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, unsigned int) is only permissible on texture<value_type, 2>."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T,3> with three integers and specified bits per scalar element + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the next-to-most-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Src_first"> + /// A beginning iterator into the source container. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(int _E0, int _E1, int _E2, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, unsigned int) is only permissible on texture<value_type, 3>."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Bits_per_scalar_element); + } + + + /// <summary> + /// Construct a texture from extents and specified bits per scalar element, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(_Ext) + { + _Initialize(_Av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture from extents, specified bits per scalar element and number of mipmap levels + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Mipmap_levels"> + /// Number of mipmap levels in the underlying texture. + /// If 0 is specified, the texture will have full range of mipmap levels down to smallest possible size for the given extent. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, unsigned int _Bits_per_scalar_element, unsigned int _Mipmap_levels, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(_Ext, _Mipmap_levels) + { + _Initialize(_Av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture from extents and specified bits per scalar element, bound to a + /// specific accelerator_view and an associated accelerator_view that is the preferred location + /// for copying to/from this texture. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(_Ext) + { + _Initialize(_Av, _Associated_av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T, 1> with integer _E0 and specified bits per scalar element, bound to a specific accelerator. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, unsigned int, accelerator_view) is only permissible on texture<value_type, 1>."); + _Initialize(_Av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture<T, 1> with integer _E0 and specified bits per scalar element, + /// bound to a specific accelerator and an associated accelerator_view that is the preferred location + /// for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, unsigned int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 1>."); + _Initialize(_Av, _Associated_av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T,2> with two integers and specified bits per scalar element, bound to a specific accelerator. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, int _E1, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, unsigned int, accelerator_view) is only permissible on texture<value_type, 2>."); + _Initialize(_Av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture<T,2> with two integers and specified bits per scalar element, + /// bound to a specific accelerator and an associated accelerator_view that is the preferred location + /// for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, int _E1, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, unsigned int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 2>."); + _Initialize(_Av, _Associated_av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T,3> with three integers and specified bits per scalar element, bound to a specific accelerator. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, int _E1, int _E2, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, unsigned int, accelerator_view) is only permissible on texture<value_type, 3>."); + _Initialize(_Av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture<T,3> with three integers and specified bits per scalar element, + /// bound to a specific accelerator and an associated accelerator_view that is the preferred location + /// for copying to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, int _E1, int _E2, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, unsigned int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 3>."); + _Initialize(_Av, _Associated_av, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture from extents and specified bits per scalar element, initialized from a host buffer. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(_Ext) + { + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T,1> with integer _E0 and specified bits per scalar element, initialized from a host buffer. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(int _E0, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, void *, unsigned int, unsigned int) is only permissible on texture<value_type, 1>."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T,2> with two integers and specified bits per scalar element, initialized from a host buffer. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(int _E0, int _E1, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, void *, unsigned int, unsigned int) is only permissible on texture<value_type, 2>."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + + /// <summary> + /// Construct a texture<T,3> with three integers and specified bits per scalar element, initialized from a host buffer. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + texture(int _E0, int _E1, int _E2, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, void *, unsigned int, unsigned int) is only permissible on texture<value_type, 3>."); + _Initialize(Concurrency::details::_Select_default_accelerator().default_view, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture from extents and specified bits per scalar element, initialized from a host buffer, bound to a specific accelerator_view. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(_Ext) + { + _Initialize(_Av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture from extents and specified bits per scalar element, initialized from a host buffer, + /// bound to a specific accelerator_view and an associated accelerator_view that is the preferred location for copying + /// to/from this texture. + /// </summary> + /// <param name="_Ext"> + /// An extent that describes the shape of the texture. + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(const Concurrency::extent<_Rank>& _Ext, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(_Ext) + { + _Initialize(_Av, _Associated_av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T, 1> with integer _E0 and specified bits per scalar element, initialized from a host buffer, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, void *, unsigned int, unsigned int, accelerator_view) is only permissible on texture<value_type, 1>."); + _Initialize(_Av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture<T, 1> with integer _E0 and specified bits per scalar element, initialized from a host buffer, + /// bound to a specific accelerator_view and an associated accelerator_view that is the preferred location for copying + /// to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0)) + { + static_assert(_Rank == 1, "texture(int, void *, unsigned int, unsigned int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 1>."); + _Initialize(_Av, _Associated_av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T, 2> with two integers and specified bits per scalar element, initialized from a host buffer, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, int _E1, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, void *, unsigned int, unsigned int, accelerator_view) is only permissible on texture<value_type, 2>."); + _Initialize(_Av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture<T, 2> with two integers and specified bits per scalar element, initialized from a host buffer, + /// bound to a specific accelerator_view and an associated accelerator_view that is the preferred location for copying + /// to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (height). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, int _E1, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1)) + { + static_assert(_Rank == 2, "texture(int, int, void *, unsigned int, unsigned int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 2>."); + _Initialize(_Av, _Associated_av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture<T, 3> with three integers and specified bits per scalar element, initialized from a host buffer, bound to a specific accelerator_view. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(int _E0, int _E1, int _E2, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, void *, unsigned int, unsigned int, accelerator_view) is only permissible on texture<value_type, 3>."); + _Initialize(_Av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a staging texture<T, 3> with three integers and specified bits per scalar element, initialized from a host buffer, + /// bound to a specific accelerator_view and an associated accelerator_view that is the preferred location for copying + /// to/from this texture. + /// </summary> + /// <param name="_E0"> + /// An integer that is the length of the most-significant dimension of this texture (depth). + /// </param> + /// <param name="_E1"> + /// An integer that is the length of the least-significant dimension of this texture (height). + /// </param> + /// <param name="_E2"> + /// An integer that is the length of the least-significant dimension of this texture (width). + /// </param> + /// <param name="_Source"> + /// A pointer to a host buffer. + /// </param> + /// <param name="_Source_byte_size"> + /// Number of bytes in the source buffer. + /// </param> + /// <param name="_Bits_per_scalar_element"> + /// Number of bits per each scalar element in the underlying scalar type of the texture. + /// In general, supported value is 8, 16, 32, 64. + /// If 0 is specified, the number of bits picks defaulted value for the underlying scalar_type. + /// 64 is only valid for double based textures + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(int _E0, int _E1, int _E2, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element, const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + : _Texture_base(Concurrency::extent<_Rank>(_E0, _E1, _E2)) + { + static_assert(_Rank == 3, "texture(int, int, int, void *, unsigned int, unsigned int, accelerator_view, accelerator_view) is only permissible on texture<value_type, 3>."); + _Initialize(_Av, _Associated_av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + /// <summary> + /// Construct a texture from a texture_view. Deep copy + /// </summary> + /// <param name="_Src"> + /// The texture_view to copy from. + /// </param> + texture(const texture_view<_Value_type, _Rank> & _Src) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Src.accelerator_view, _Src); + } + + /// <summary> + /// Construct a texture from a read-only texture_view. Deep copy + /// </summary> + /// <param name="_Src"> + /// The read-only texture_view to copy from. + /// </param> + texture(const texture_view<const _Value_type, _Rank> & _Src) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Src.accelerator_view, _Src); + } + + /// <summary> + /// Construct a texture from a texture_view on another accelerator_view. Deep copy + /// </summary> + /// <param name="_Src"> + /// The texture_view to copy from. + /// </param> + /// <param name="_Acc_view"> + /// An accelerator_view where this texture resides. + /// </param> + texture(const texture_view<_Value_type, _Rank> & _Src, const Concurrency::accelerator_view & _Acc_view) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Acc_view, _Src); + } + + /// <summary> + /// Construct a texture from a read-only texture_view on another accelerator_view. Deep copy + /// </summary> + /// <param name="_Src"> + /// The read-only texture_view to copy from. + /// </param> + /// <param name="_Acc_view"> + /// An accelerator_view where this texture resides. + /// </param> + texture(const texture_view<const _Value_type, _Rank> & _Src, const Concurrency::accelerator_view & _Acc_view) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Acc_view, _Src); + } + + /// <summary> + /// Construct a staging texture from a texture_view on another accelerator_view. Deep copy + /// </summary> + /// <param name="_Src"> + /// The texture_view to copy from. + /// </param> + /// <param name="_Acc_view"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(const texture_view<_Value_type, _Rank> & _Src, const Concurrency::accelerator_view & _Acc_view, const Concurrency::accelerator_view& _Associated_av) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Acc_view, _Associated_av, _Src); + } + + /// <summary> + /// Construct a staging texture from a read-only texture_view on another accelerator_view. Deep copy + /// </summary> + /// <param name="_Src"> + /// The read-only texture_view to copy from. + /// </param> + /// <param name="_Acc_view"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(const texture_view<const _Value_type, _Rank> & _Src, const Concurrency::accelerator_view & _Acc_view, const Concurrency::accelerator_view& _Associated_av) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Acc_view, _Associated_av, _Src); + } + + /// <summary> + /// Copy constructor. Deep copy + /// </summary> + /// <param name="_Src"> + /// The texture to copy from. + /// </param> + texture(const texture & _Src) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Src.accelerator_view, _Src.associated_accelerator_view, _Src); + } + + /// <summary> + /// Move constructor + /// </summary> + /// <param name="_Other"> + /// The source texture to move from. + /// </param> + texture(texture && _Other) + { + *this = std::move(_Other); + } + + /// <summary> + /// Copy constructor. Deep copy + /// </summary> + /// <param name="_Src"> + /// The texture to copy from. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + texture(const texture & _Src, const Concurrency::accelerator_view & _Av) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Av, _Src); + } + + /// <summary> + /// Copy constructor. Deep copy + /// </summary> + /// <param name="_Src"> + /// The texture to copy from. + /// </param> + /// <param name="_Av"> + /// An accelerator_view where this texture resides. + /// </param> + /// <param name="_Associated_av"> + /// An accelerator_view which specifies the preferred target location for copies + /// to/from the texture. + /// </param> + texture(const texture & _Src, const Concurrency::accelerator_view & _Av, const Concurrency::accelerator_view& _Associated_av) + : _Texture_base(_Src.extent, _Src.get_mipmap_levels()) + { + _Initialize(_Av, _Associated_av, _Src); + } + + /// <summary> + /// Copy assignment operator. Deep copy + /// </summary> + /// <param name="_Src"> + /// The texture to copy from. + /// </param> + /// <returns> + /// A reference to this texture. + /// </returns> + texture& operator=(const texture & _Other) + { + if (this != &_Other) + { + _M_extent = _Other._M_extent; + _M_texture_descriptor._Set_view_mipmap_levels(_Other.get_mipmap_levels()); + _Initialize(_Other.accelerator_view, _Other.associated_accelerator_view, _Other); + } + return *this; + } + + /// <summary> + /// Move assignment operator + /// </summary> + /// <param name="_Other"> + /// The source texture to move from. + /// </param> + /// <returns> + /// A reference to this texture. + /// </returns> + texture& operator=(texture<_Value_type, _Rank> && _Other) + { + if (this != &_Other) + { + _M_extent = _Other._M_extent; + _M_texture_descriptor = _Other._M_texture_descriptor; + + _Other._M_texture_descriptor._M_data_ptr = NULL; + _Other._M_texture_descriptor._Set_texture_ptr(NULL); + } + return *this; + } + + /// <summary> + /// Copy-to, deep copy + /// </summary> + /// <param name="_Dest"> + /// The destination texture to copy to. + /// </param> + void copy_to(texture & _Dest) const + { + if (this->extent != _Dest.extent) + { + throw runtime_exception("The source and destination textures must have the exactly the same extent.", E_INVALIDARG); + } + + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(*this), + concurrency::details::_Get_texture_descriptor(_Dest), + this->get_data_length()); + + _Texture_base::_Copy_to(_Dest); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + /// <summary> + /// Copy-to, deep copy + /// </summary> + /// <param name="_Dest"> + /// The destination writeonly_texture_view to copy to. + /// </param> +#pragma warning( push ) +#pragma warning( disable : 4996 ) //writeonly_texture_view is deprecated + void copy_to(const writeonly_texture_view<_Value_type, _Rank> & _Dest) const + { + if (this->extent != _Dest.extent) + { + throw runtime_exception("The source and destination textures must have the exactly the same extent.", E_INVALIDARG); + } + + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(*this), + concurrency::details::_Get_texture_descriptor(_Dest), + this->get_data_length()); + + _Texture_base::_Copy_to(_Dest); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + /// <summary> + /// Destructor + /// </summary> + ~texture() __CPU_ONLY + { + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + const value_type operator[] (const index<_Rank>& _Index) const __GPU_ONLY + { + value_type _Tmp; + _Texture_read_helper<index<_Rank>, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Tmp, _Index, /*_Mip_level=*/0); + return _Tmp; + } + + /// <summary> + /// Get the element value indexed by _I. + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I. + /// </returns> + const value_type operator[] (int _I0) const __GPU_ONLY + { + static_assert(_Rank == 1, "value_type texture::operator[](int) is only permissible on texture<value_type, 1>."); + return (*this)[index<1>(_I0)]; + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + const value_type operator() (const index<_Rank>& _Index) const __GPU_ONLY + { + return (*this)[_Index]; + } + + /// <summary> + /// Get the element value indexed by _I0 + /// </summary> + /// <param name="_I0"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I0. + /// </returns> + const value_type operator() (int _I0) const __GPU_ONLY + { + static_assert(_Rank == 1, "value_type texture::operator()(int) is only permissible on texture<value_type, 1>."); + return (*this)[index<1>(_I0)]; + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1) + /// </returns> + const value_type operator() (int _I0, int _I1) const __GPU_ONLY + { + static_assert(_Rank == 2, "value_type texture::operator()(int, int) is only permissible on texture<value_type, 2>."); + return (*this)[index<2>(_I0, _I1)]; + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1,_I2) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the index + /// </param> + /// <param name="_I2"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1,_I2) + /// </returns> + const value_type operator() (int _I0, int _I1, int _I2) const __GPU_ONLY + { + static_assert(_Rank == 3, "value_type texture::operator()(int, int, int) is only permissible on texture<value_type, 3>."); + return (*this)[index<3>(_I0, _I1, _I2)]; + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + const value_type get(const index<_Rank>& _Index) const __GPU_ONLY + { + return (*this)[_Index]; + } + + /// <summary> + /// Set the element indexed by _Index with value _Value. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <param name="_Value"> + /// The value to be set to the element indexed by _Index. + /// </param> + void set(const index<_Rank>& _Index, const value_type& _Value) __GPU_ONLY + { + static_assert(_Short_vector_type_traits<_Value_type>::_Num_channels == 1, "Invalid value_type for set method."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "Invalid value_type for set method."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "Invalid value_type for set method."); + _Texture_write_helper<index<_Rank>, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Value, _Index); + } + + /// <summary> + /// Returns a CPU pointer to the raw data of this texture. + /// </summary> + _Ret_ void* data() __CPU_ONLY + { + return _Get_texture()->_Get_host_ptr(); + } + + /// <summary> + /// Returns a CPU pointer to the raw data of this texture. + /// </summary> + const void* data() const __CPU_ONLY + { + return _Get_texture()->_Get_host_ptr(); + } + + /// <summary> + /// Returns the row pitch (in bytes) of a 2D or 3D staging texture on the CPU to be + /// used for navigating the staging texture from row to row on the CPU. + /// </summary> + __declspec(property(get=get_row_pitch)) unsigned int row_pitch; + unsigned int get_row_pitch() const __CPU_ONLY + { + static_assert(_Rank >= 2, "row_pitch is only applicable to staging textures with rank 2 or higher."); + + if (!_Get_texture()->_Is_staging()) { + throw runtime_exception("row_pitch is only applicable to staging textures.", E_INVALIDARG); + } + + return static_cast<unsigned int>(_Get_texture()->_Get_row_pitch()); + } + + /// <summary> + /// Returns the depth pitch (in bytes) of a 3D staging texture on the CPU to be used + /// for navigating the staging texture from depth slice to depth slice on the CPU. + /// </summary> + __declspec(property(get=get_depth_pitch)) unsigned int depth_pitch; + unsigned int get_depth_pitch() const __CPU_ONLY + { + static_assert(_Rank == 3, "depth_pitch is only applicable to staging textures with rank 3."); + + if (!_Get_texture()->_Is_staging()) { + throw runtime_exception("depth_pitch is only applicable to staging textures.", E_INVALIDARG); + } + + return static_cast<unsigned int>(_Get_texture()->_Get_depth_pitch()); + } + + /// <summary> + /// Returns the accelerator_view that is the preferred target where this texture can be copied. + /// </summary> + __declspec(property(get=get_associated_accelerator_view)) Concurrency::accelerator_view associated_accelerator_view; + Concurrency::accelerator_view get_associated_accelerator_view() const __CPU_ONLY + { + return _Get_texture()->_Get_accelerator_view(); + } + +private: + // Private constructor used by make_texture to create a texture from D3D texture + texture(const Concurrency::extent<_Rank> & _Ext, const _Texture_descriptor & _Descriptor) + : details::_Texture_base<_Value_type, _Rank>(_Ext, _Descriptor) + { + } + + bool _Should_create_staging_texture(const Concurrency::accelerator_view &_Av, const Concurrency::accelerator_view &_Associated_av) + { + return (_Is_cpu_accelerator(_Av.accelerator) && !_Is_cpu_accelerator(_Associated_av.accelerator)); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av, unsigned int _Bits_per_scalar_element) __CPU_ONLY + { + if (_Bits_per_scalar_element != 8 && _Bits_per_scalar_element != 16 && + _Bits_per_scalar_element != 32 && _Bits_per_scalar_element != 64) + { + throw runtime_exception("Invalid _Bits_per_scalar_element argument - it can only be 8, 16, 32, or 64.", E_INVALIDARG); + } + + // special cases for 64 and for double based textures + +#pragma warning( push ) +#pragma warning( disable : 4127 ) // conditional expression is constant + if (_Bits_per_scalar_element == 64 && _Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Double_type) + { + throw runtime_exception("Invalid _Bits_per_scalar_element argument - 64 is only valid for texture of double based short vector types.", E_INVALIDARG); + } + + if (_Bits_per_scalar_element != 64 && _Short_vector_type_traits<_Value_type>::_Format_base_type_id == _Double_type) + { + throw runtime_exception("Invalid _Bits_per_scalar_element argument - it can only be 64 for texture of double based short vector types.", E_INVALIDARG); + } + + details::_Is_valid_data_length(_M_extent.size(), _Bits_per_scalar_element * _Short_vector_type_traits<_Value_type>::_Num_channels); + + // the rest of the check is done by _Texture::_Create_texture, it depends on the underlying supported DXGI formats. + + unsigned int _Bits_per_channel = _Bits_per_scalar_element; + + if (_Short_vector_type_traits<_Value_type>::_Format_base_type_id == _Double_type) + { + _Bits_per_channel = _Short_vector_type_traits<_Value_type>::_Default_bits_per_channel; + } + + std::array<size_t, 3> _Dimensions = Concurrency::graphics::details::_Get_dimensions(_M_extent, /*_Mip_offset=*/0); + + // release the old texture first before allocating new one to avoid the chance on hitting OOM + _M_texture_descriptor._Set_texture_ptr(NULL); + _Texture_ptr _Tex_ptr = NULL; + + // See if we need to allocate a staging texture + if (_Should_create_staging_texture(_Av, _Associated_av)) { + + if (_M_texture_descriptor._Get_view_mipmap_levels() > 1) + { + throw runtime_exception("Creating staging textures with mipmap levels > 1 is not supported", E_INVALIDARG); + } + + _Tex_ptr = _Texture::_Create_stage_texture( + _Associated_av, _Av, _Rank, _Dimensions[0], _Dimensions[1], _Dimensions[2], _M_texture_descriptor._Get_view_mipmap_levels(), + _Short_vector_type_traits<_Value_type>::_Format_base_type_id == _Double_type ? _Uint_type : _Short_vector_type_traits<_Value_type>::_Format_base_type_id, + _Short_vector_type_traits<_Value_type>::_Num_channels, + _Bits_per_channel); + + // Now map the texture + _Tex_ptr->_Map_buffer(_Write_access, true /* _Wait */); + } + else { + _Tex_ptr = _Texture::_Create_texture(_Av, _Rank, _Dimensions[0], _Dimensions[1], _Dimensions[2], _M_texture_descriptor._Get_view_mipmap_levels(), + _Short_vector_type_traits<_Value_type>::_Format_base_type_id == _Double_type ? _Uint_type : _Short_vector_type_traits<_Value_type>::_Format_base_type_id, + _Short_vector_type_traits<_Value_type>::_Num_channels, + _Bits_per_channel); + } + + _M_texture_descriptor._Set_texture_ptr(_Tex_ptr); +#pragma warning( pop ) + } + + void _Initialize(const Concurrency::accelerator_view& _Av, unsigned int _Bits_per_scalar_element) __CPU_ONLY + { + _Initialize(_Av, _Av, _Bits_per_scalar_element); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av) __CPU_ONLY + { + _Initialize(_Av, _Associated_av, Concurrency::graphics::details::_Get_default_bits_per_scalar_element<_Value_type>()); + } + + void _Initialize(const Concurrency::accelerator_view& _Av) __CPU_ONLY + { + _Initialize(_Av, _Av); + } + + template<typename _Input_iterator> + void _Initialize(const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av, _Input_iterator _Src_first, _Input_iterator _Src_last) __CPU_ONLY + { + _Initialize(_Av, _Associated_av); + + auto _Span_id = Concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(nullptr, + Concurrency::details::_Get_texture_descriptor(*this), + this->get_data_length()); + + Concurrency::graphics::details::_Copy_async_impl(_Src_first, _Src_last, *this, index<_Rank>(), this->extent)._Get(); + + Concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + template<typename _Input_iterator> + void _Initialize(const Concurrency::accelerator_view& _Av, _Input_iterator _Src_first, _Input_iterator _Src_last) __CPU_ONLY + { + _Initialize(_Av, _Av, _Src_first, _Src_last); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element) __CPU_ONLY + { + _Initialize(_Av, _Associated_av, _Bits_per_scalar_element); + Concurrency::graphics::copy(_Source, _Src_byte_size, *this); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const void * _Source, unsigned int _Src_byte_size, unsigned int _Bits_per_scalar_element) __CPU_ONLY + { + _Initialize(_Av, _Av, _Source, _Src_byte_size, _Bits_per_scalar_element); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av, const void * _Source, unsigned int _Src_byte_size) __CPU_ONLY + { + _Initialize(_Av, _Associated_av); + Concurrency::graphics::copy(_Source, _Src_byte_size, *this); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const void * _Source, unsigned int _Src_byte_size) __CPU_ONLY + { + _Initialize(_Av, _Av, _Source, _Src_byte_size); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const Concurrency::accelerator_view& _Associated_av, const details::_Texture_base<_Value_type, _Rank> & _Src) __CPU_ONLY + { + if (_Src.bits_per_scalar_element != 0) // _Src is not created via interop + { + _Initialize(_Av, _Associated_av, _Src.bits_per_scalar_element); + } + else // _Src is created via interop, create a new texture with the same properties as the existing one. + { + _Texture_ptr _New_tex; + if (_Should_create_staging_texture(_Av, _Associated_av)) + { + _New_tex = _Texture::_Clone_texture(concurrency::details::_Get_texture(_Src), _Associated_av, _Av); + } + else + { + _New_tex = _Texture::_Clone_texture(concurrency::details::_Get_texture(_Src), _Av, _Associated_av); + } + _M_texture_descriptor._Set_texture_ptr(_New_tex); + } + + auto _Span_id = Concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(Concurrency::details::_Get_texture_descriptor(_Src), + Concurrency::details::_Get_texture_descriptor(*this), + this->get_data_length()); + + Concurrency::graphics::details::_Copy_async_impl(_Src, index<_Rank>(), *this, index<_Rank>(), this->extent)._Get(); + + Concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + + void _Initialize(const Concurrency::accelerator_view& _Av, const details::_Texture_base<_Value_type, _Rank> & _Src) __CPU_ONLY + { + _Initialize(_Av, _Av, _Src); + } +}; + +/// <summary> +/// A writeonly_texture_view provides writeonly access to a texture. +/// </summary> +/// <param name="_Value_type"> +/// The type of the elements in the texture aggregates. +/// </param> +/// <param name="_Rank"> +/// The _Rank of the corresponding extent domain. +/// </param> +#pragma warning( push ) +#pragma warning( disable : 4996 ) //writeonly_texture_view is deprecated +template <typename _Value_type, int _Rank> class __declspec(deprecated("writeonly_texture_view is deprecated. Please use texture_view instead.")) writeonly_texture_view : public details::_Texture_base<_Value_type, _Rank> +{ + static_assert(!std::is_const<_Value_type>::value, "const value type is not supported for writeonly_texture_view."); + +public: + /// <summary> + /// Construct a writeonly_texture_view of a texture _Src. + /// </summary> + /// <param name="_Src"> + /// The texture on which the writeonly view is created. + /// </param> + writeonly_texture_view(texture<_Value_type, _Rank>& _Src) __CPU_ONLY + : _Texture_base(_Src, /*_Most_detailed_mipmap_level=*/0, /*_View_mipmap_levels=*/1) + { + _Texture* _Tex = _Get_texture(); + if ((_Tex->_Get_num_channels() == 3) && (_Tex->_Get_bits_per_channel() == 32)) { + throw runtime_exception("writeonly_texture_view cannot be created from a 3-channel texture with 32 bits per scalar element.", E_INVALIDARG); + } + if (_Tex->_Is_staging()) { + throw runtime_exception("writeonly_texture_view cannot be created from a staging texture object.", E_INVALIDARG); + } + } + + /// <summary> + /// Construct a writeonly_texture_view of a texture _Src. + /// </summary> + /// <param name="_Src"> + /// The texture on which the writeonly view is created. + /// </param> + writeonly_texture_view(texture<_Value_type, _Rank>& _Src) __GPU_ONLY + : _Texture_base(_Src, /*_Flatten_mipmap_levels=*/true) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Num_channels == 1, "Invalid value_type for the constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "Invalid value_type for the constructor."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "Invalid value_type for the constructor."); + } + + /// <summary> + /// Construct a writeonly_texture_view from another writeonly_texture_view. Both are views of the same texture. + /// </summary> + /// <param name="_Src"> + /// The writeonly_texture_view from which the current view is created. + /// </param> + writeonly_texture_view(const writeonly_texture_view<_Value_type, _Rank>& _Src) __GPU + : _Texture_base(_Src) + { + } + + /// <summary> + /// Assignment operator. This writeonly_texture_view becomes a view of the same texture which _Other is a view of. + /// </summary> + /// <param name="_Other"> + /// The source writeonly_texture_view. + /// </param> + writeonly_texture_view<_Value_type, _Rank>& operator=(const writeonly_texture_view<_Value_type, _Rank>& _Other) __GPU + { + if (this != &_Other) + { + _M_extent = _Other._M_extent; + _M_texture_descriptor = _Other._M_texture_descriptor; + } + return *this; + } + + /// <summary> + /// Destructor + /// </summary> + ~writeonly_texture_view() __GPU + { + } + + /// <summary> + /// Set the element indexed by _Index with value _Value. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <param name="_Value"> + /// The value to be set to the element indexed by _Index. + /// </param> + void set(const index<_Rank>& _Index, const value_type& _Value) const __GPU_ONLY + { + _Texture_write_helper<index<_Rank>, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Value, _Index); + } +}; +#pragma warning( pop ) + +/// <summary> +/// A texture_view provides read and write access to a texture. +/// Note that currently texture_view can only be used to read textures whose value type is int, unsigned int and float +/// with default 32 bit bpse. To read other texture formats, use texture_view<const _Value_type, _Rank>. +/// </summary> +/// <param name="_Value_type"> +/// The type of the elements in the texture aggregates. +/// </param> +/// <param name="_Rank"> +/// The _Rank of the corresponding extent domain. +/// </param> +template <typename _Value_type, int _Rank> class texture_view : public details::_Texture_base<_Value_type, _Rank> +{ + friend class texture_view<const _Value_type, _Rank>; + +public: + /// <summary> + /// Construct a texture_view of a texture _Src on host. + /// </summary> + /// <param name="_Src"> + /// The texture on which the texture_view is created. + /// </param> + /// <param name="_Mipmap_level"> + /// The specific mipmap level on a _Src texture that this read and write texture_view should bind to. + /// The default value 0, binds to the top mosted detail mipmap level. + /// </param> + texture_view(texture<_Value_type, _Rank>& _Src, unsigned int _Mipmap_level = 0) __CPU_ONLY + : _Texture_base(_Src, _Mipmap_level, /*_View_mipmap_levels=*/1) + { + if (_Get_texture()->_Is_staging()) { + throw runtime_exception("texture_view cannot be created from a staging texture object.", E_INVALIDARG); + } + } + + /// <summary> + /// Construct a texture_view of a texture _Src on an accelerator. + /// </summary> + /// <param name="_Src"> + /// The texture on which the texture_view is created. + /// </param> + texture_view(texture<_Value_type, _Rank>& _Src) __GPU_ONLY + : _Texture_base(_Src, /*_Flatten_mipmap_levels=*/true) + { + static_assert(_Short_vector_type_traits<_Value_type>::_Num_channels == 1, "writable texture_view can only be created from a single-component texture on an accelerator."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "writable texture_view cannot be created from a unorm texture on an accelerator."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "writable texture_view cannot be created from a norm texture on an accelerator."); + } + + /// <summary> + /// Construct a texture_view from another texture_view. Both are views of the same texture. + /// </summary> + /// <param name="_Other"> + /// The source texture_view. + /// </param> + texture_view(const texture_view<_Value_type, _Rank>& _Other) __GPU + : _Texture_base(_Other) + { + } + + /// <summary> + /// Assignment operator. This texture_view becomes a view of the same texture which _Other is a view of. + /// </summary> + /// <param name="_Other"> + /// The source texture_view. + /// </param> + texture_view<_Value_type, _Rank>& operator=(const texture_view<_Value_type, _Rank>& _Other) __GPU + { + if (this != &_Other) + { + _M_extent = _Other._M_extent; + _M_texture_descriptor = _Other._M_texture_descriptor; + } + return *this; + } + + /// <summary> + /// Destructor + /// </summary> + ~texture_view() __GPU + { + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + const value_type operator[] (const index<_Rank>& _Index) const __GPU_ONLY + { + static_assert(_Short_vector_type_traits<_Value_type>::_Num_channels == 1, "Read is only permissible on single-component writable texture_view."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Unorm_type, "Read is not permissible on a writable unorm texture_view."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Norm_type, "Read is not permissible on a writable norm texture_view."); + + value_type _Tmp; + _Texture_read_helper<index<_Rank>, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Tmp, _Index, /*_Mip_level=*/0); + return _Tmp; + } + + /// <summary> + /// Get the element value indexed by _I0. + /// </summary> + /// <param name="_I0"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I0. + /// </returns> + const value_type operator[] (int _I0) const __GPU_ONLY + { + static_assert(_Rank == 1, "const value_type operator[](int) is only permissible on texture_view<value_type, 1>."); + return (*this)[index<1>(_I0)]; + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + const value_type operator() (const index<_Rank>& _Index) const __GPU_ONLY + { + return (*this)[_Index]; + } + + /// <summary> + /// Get the element value indexed by _I0 + /// </summary> + /// <param name="_I0"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I0. + /// </returns> + const value_type operator() (int _I0) const __GPU_ONLY + { + static_assert(_Rank == 1, "const value_type operator()(int) is only permissible on texture_view<value_type, 1>."); + return (*this)[index<1>(_I0)]; + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1) + /// </returns> + const value_type operator() (int _I0, int _I1) const __GPU_ONLY + { + static_assert(_Rank == 2, "const value_type operator()(int, int) is only permissible on texture_view<value_type, 2>."); + return (*this)[index<2>(_I0, _I1)]; + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1,_I2) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the index + /// </param> + /// <param name="_I2"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1,_I2) + /// </returns> + const value_type operator() (int _I0, int _I1, int _I2) const __GPU_ONLY + { + static_assert(_Rank == 3, "const value_type operator()(int, int, int) is only permissible on texture_view<value_type, 3>."); + return (*this)[index<3>(_I0, _I1, _I2)]; + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + const value_type get(const index<_Rank>& _Index) const __GPU_ONLY + { + return (*this)[_Index]; + } + + /// <summary> + /// Set the element indexed by _Index with value _Value. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <param name="_Value"> + /// The value to be set to the element indexed by _Index. + /// </param> + void set(const index<_Rank>& _Index, const value_type& _Value) const __GPU_ONLY + { + _Texture_write_helper<index<_Rank>, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Value, _Index); + } +}; + +/// <summary> +/// filter modes supported for texture sampling +/// </summary> +enum filter_mode +{ + filter_point = 0, + filter_linear = 0x15, + filter_unknown = 0xFFFFFFFF, +}; + +/// <summary> +/// address modes supported for texture sampling +/// </summary> +enum address_mode +{ + address_wrap = 1, + address_mirror = 2, + address_clamp = 3, + address_border = 4, + address_unknown = 0xFFFFFFFF, +}; + +/// <summary> +/// A sampler class aggregates sampling configuration information to be used for texture sampling. +/// </summary> +class sampler +{ + friend sampler direct3d::make_sampler(_In_ IUnknown *_D3D_sampler) __CPU_ONLY; + friend _Ret_ IUnknown * direct3d::get_sampler(const Concurrency::accelerator_view &_Av, const sampler &_Sampler) __CPU_ONLY; + + template <typename _Value_type, int _Rank> + friend class texture_view; + +public: + /// <summary> + /// Constructs a sampler with default filter mode (filter_lienar, same for min, mag, mip), addressing + /// mode (address_clamp, same for all dimensions), and border color (float_4(0.0f, 0.0f, 0.0f, 0.0f)). + /// </summary> + sampler() __CPU_ONLY + : _M_filter_mode(filter_linear), + _M_address_mode(address_clamp), + _M_border_color(float_4(0.0f, 0.0f, 0.0f, 0.0f)) + { + _Initialize(); + } + + /// <summary> + /// Constructs a sampler with specified filter mode (same for min, mag, mip), but with default addressing + /// mode (address_clamp, same for all dimensions) and border color ( float_4(0.0f, 0.0f, 0.0f, 0.0f)). + /// </summary> + /// <param name="_Filter_mode"> + /// The filter mode to be used in sampling. + /// </param> + sampler(filter_mode _Filter_mode)__CPU_ONLY + : _M_filter_mode(_Filter_mode), + _M_address_mode(address_clamp), + _M_border_color(float_4(0.0f, 0.0f, 0.0f, 0.0f)) + { + _Initialize(); + } + + /// <summary> + /// Constructs a sampler with default filter mode (filter_linear, same for min, mag, mip), but specified + /// addressing mode (same for all dimensions) and border color. + /// </summary> + /// <param name="_Address_mode"> + /// The addressing mode to be used in sampling for all dimensions. + /// </param> + /// <param name="_Border_color"> + /// The border color to be used if address mode is address_border. If not specified, default value is float_4(0.f, 0.f, 0.f, 0.f). + /// </param> + sampler(address_mode _Address_mode, float_4 _Border_color = float_4(0.0f, 0.0f, 0.0f, 0.0f)) __CPU_ONLY + : _M_filter_mode(filter_linear), + _M_address_mode(_Address_mode), + _M_border_color(_Border_color) + { + _Initialize(); + } + + /// <summary> + /// Constructs a sampler with specified filter mode (same for min, mag, mip), addressing + /// mode (same for all dimensions) and the border color. + /// </summary> + /// <param name="_Filter_mode"> + /// The filter mode to be used in sampling. + /// </param> + /// <param name="_Address_mode"> + /// The addressing mode to be used in sampling for all dimensions. + /// </param> + /// <param name="_Border_color"> + /// The border color to be used if address mode is address_border. If not specified, default value is float_4(0.f, 0.f, 0.f, 0.f). + /// </param> + sampler(filter_mode _Filter_mode, address_mode _Address_mode, float_4 _Border_color = float_4(0.0f, 0.0f, 0.0f, 0.0f)) __CPU_ONLY + : _M_filter_mode(_Filter_mode), + _M_address_mode(_Address_mode), + _M_border_color(_Border_color) + { + _Initialize(); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// An object of type sampler from which to initialize this new sampler. + /// </param> + sampler(const sampler& _Other) __GPU + : _M_filter_mode(_Other._M_filter_mode), + _M_address_mode(_Other._M_address_mode), + _M_border_color(_Other._M_border_color), + _M_sampler_descriptor(_Other._M_sampler_descriptor) + { + } + + /// <summary> + /// Move constructor. + /// </summary> + /// <param name="_Other"> + /// The sampler to move from. + /// </param> + sampler(sampler &&_Other) __GPU + : _M_filter_mode(_Other._M_filter_mode), + _M_address_mode(_Other._M_address_mode), + _M_border_color(_Other._M_border_color), + _M_sampler_descriptor(_Other._M_sampler_descriptor) + { + _Other._M_sampler_descriptor._M_data_ptr = NULL; + _Other._M_sampler_descriptor._Set_sampler_ptr(NULL); + } + + /// <summary> + /// Assignment operator. + /// </summary> + /// <param name="_Other"> + /// An object of type sampler from which to copy into this sampler. + /// </param> + /// <returns> + /// A reference to this sampler. + /// </returns> + sampler& operator=(const sampler& _Other) __GPU + { + if (this != &_Other) + { + _M_filter_mode = _Other._M_filter_mode; + _M_address_mode = _Other._M_address_mode; + _M_border_color = _Other._M_border_color; + _M_sampler_descriptor = _Other._M_sampler_descriptor; + } + return *this; + } + + /// <summary> + /// Move assignment operator. + /// </summary> + /// <param name="_Other"> + /// An object of type sampler to move from. + /// </param> + /// <returns> + /// A reference to this sampler. + /// </returns> + sampler& operator=(sampler&& _Other) __GPU + { + if (this != &_Other) + { + _M_filter_mode = _Other._M_filter_mode; + _M_address_mode = _Other._M_address_mode; + _M_border_color = _Other._M_border_color; + _M_sampler_descriptor = _Other._M_sampler_descriptor; + _Other._M_sampler_descriptor._M_data_ptr = NULL; + _Other._M_sampler_descriptor._Set_sampler_ptr(NULL); + } + return *this; + } + + /// <summary> + /// Returns the sampler's filter mode + /// </summary> + __declspec(property(get=get_filter_mode)) Concurrency::graphics::filter_mode filter_mode; + Concurrency::graphics::filter_mode get_filter_mode() const __GPU + { + return _M_filter_mode; + } + + /// <summary> + /// Returns the sampler's address mode + /// </summary> + __declspec(property(get=get_address_mode)) Concurrency::graphics::address_mode address_mode; + Concurrency::graphics::address_mode get_address_mode() const __GPU + { + return _M_address_mode; + } + + /// <summary> + /// Returns the sampler's border value + /// </summary> + __declspec(property(get=get_border_color)) Concurrency::graphics::float_4 border_color; + Concurrency::graphics::float_4 get_border_color() const __GPU + { + return _M_border_color; + } + +private: + // internal storage abstraction + typedef Concurrency::details::_Sampler_descriptor _Sampler_descriptor; + + // a private constructor to be used for constructing a sampler via interop. + sampler(const _Sampler_descriptor & _Descriptor) __CPU_ONLY + : _M_sampler_descriptor(_Descriptor), + _M_filter_mode(filter_unknown), + _M_address_mode (address_unknown), + _M_border_color(float_4(0.0f, 0.0f, 0.0f, 0.0f)) + { + // Although we could query border value from the adopted sampler, but it's not that useful + // given that this is the only thing that we could query and when the address mode is not + // address_border, border value is not relevant. + } + + _Ret_ _Sampler* _Get_sampler_ptr() const __CPU_ONLY + { + return _M_sampler_descriptor._Get_sampler_ptr(); + } + + void _Initialize() __CPU_ONLY + { + // Check if the given filter_mode and address_mode are valid C++ AMP ones + if ((_M_filter_mode != filter_point && _M_filter_mode != filter_linear) || + (_M_address_mode != address_wrap && _M_address_mode != address_mirror && + _M_address_mode != address_clamp && _M_address_mode != address_border)) + { + throw runtime_exception("Invalid sampler configuration", E_INVALIDARG); + } + + _Sampler_ptr samplerPtr = _Sampler::_Create(_M_filter_mode, _M_address_mode, + _M_border_color.r, _M_border_color.g, _M_border_color.b, _M_border_color.a); + _M_sampler_descriptor._Set_sampler_ptr(samplerPtr); + } + + const _Sampler_descriptor & _Get_descriptor() const __GPU_ONLY + { + return _M_sampler_descriptor; + } + + _Sampler_descriptor _M_sampler_descriptor; + Concurrency::graphics::filter_mode _M_filter_mode; + Concurrency::graphics::address_mode _M_address_mode; + float_4 _M_border_color; +}; + +/// <summary> +/// A texture_view<const _Value_type, _Rank> provides read-only access and sampling capability to a texture. +/// </summary> +/// <param name="_Value_type"> +/// The type of the elements in the texture aggregates. +/// </param> +/// <param name="_Rank"> +/// The _Rank of the corresponding extent domain. +/// </param> +template <typename _Value_type, int _Rank> class texture_view<const _Value_type, _Rank> : public details::_Texture_base<_Value_type, _Rank> +{ +public: + typedef typename const _Value_type value_type; + typedef typename short_vector<float, _Rank>::type coordinates_type; + typedef typename short_vector<scalar_type, 4>::type gather_return_type; + + /// <summary> + /// Construct a read-only texture_view of a texture _Src on an accelerator. + /// </summary> + /// <param name="_Src"> + /// The texture on which the read-only view is created. + /// </param> + texture_view(const texture<_Value_type, _Rank>& _Src) __GPU_ONLY + : _Texture_base(_Src) + { + // only on the gpu it is not allowed + static_assert(_Short_vector_type_traits<_Value_type>::_Num_channels != 1, "Read-only texture_view cannot be created from single-component textures on an accelerator."); + } + + /// <summary> + /// Construct a texture_view of a texture _Src on the host. + /// </summary> + /// <param name="_Src"> + /// The texture on which the read-only view is created. + /// </param> + texture_view(const texture<_Value_type, _Rank>& _Src) __CPU_ONLY + : _Texture_base(_Src) + { + if (_Get_texture()->_Is_staging()) { + throw runtime_exception("Read-only texture_view cannot be created from a staging texture object.", E_INVALIDARG); + } + } + + /// <summary> + /// Construct a read-only texture_view with specific range of mipmap levels of a texture _Src on the host. + /// </summary> + /// <param name="_Src"> + /// The texture on which the read-only view is created. + /// </param> + /// <param name="_Most_detailed_mip"> + /// Most detailed mipmap level for the view. + /// </param> + /// <param name="_Mip_levels"> + /// The number of mipmap levels accessible for the view. + /// </param> + texture_view(const texture<_Value_type, _Rank>& _Src, unsigned int _Most_detailed_mip, unsigned int _Mip_levels) __CPU_ONLY + : _Texture_base(_Src, _Most_detailed_mip, _Mip_levels) + { + if (_Get_texture()->_Is_staging()) { + throw runtime_exception("Read-only texture_view cannot be created from a staging texture object.", E_INVALIDARG); + } + } + + /// <summary> + /// Construct a read-only texture_view of a writable texture_view. + /// </summary> + /// <param name="_Other"> + /// The writable texture view from which the read-only view is created. + /// </param> + texture_view(const texture_view<_Value_type, _Rank>& _Other) __CPU_ONLY + : _Texture_base(_Other) + { + } + + /// <summary> + /// Construct a read-only texture_view from another read-only texture_view. Both are views of the same texture. + /// </summary> + /// <param name="_Other"> + /// The source read-only texture_view. + /// </param> + texture_view(const texture_view<const _Value_type, _Rank>& _Other) __GPU + : _Texture_base(_Other) + { + } + + /// <summary> + /// Construct a read-only texture_view from another read-only texture_view. + /// Allows narrowing down the accessible range of mipmap levels for the texture_view. + /// Both are views of the same texture. + /// </summary> + /// <param name="_Other"> + /// The source read-only texture_view. + /// </param> + /// <param name="_Most_detailed_mip"> + /// Top level mipmap for the view, relative to the input texture_view. + /// </param> + /// <param name="_Mip_levels"> + /// The number of mipmap levels accessible for the view. + /// </param> + texture_view(const texture_view<const _Value_type, _Rank>& _Other, unsigned int _Most_detailed_mip, unsigned int _Mip_levels) __CPU_ONLY + : _Texture_base(_Other, _Most_detailed_mip, _Mip_levels) + { + } + + /// <summary> + /// Assignment operator. This read-only texture_view becomes a view of the same texture which _Other is a view of. + /// </summary> + /// <param name="_Other"> + /// The source read-only texture_view. + /// </param> + texture_view<const _Value_type, _Rank>& operator=(const texture_view<const _Value_type, _Rank>& _Other) __GPU + { + if (this != &_Other) + { + _M_extent = _Other._M_extent; + _M_texture_descriptor = _Other._M_texture_descriptor; + } + return *this; + } + + /// <summary> + /// Assignment operator from a writable texture_view. + /// This read-only texture_view becomes a view of the same texture which _Other is a view of. + /// </summary> + /// <param name="_Other"> + /// The source writable texture_view. + /// </param> + texture_view<const _Value_type, _Rank>& operator=(const texture_view<_Value_type, _Rank>& _Other) __CPU_ONLY + { + _M_extent = _Other._M_extent; + _M_texture_descriptor = _Other._M_texture_descriptor; + return *this; + } + + /// <summary> + /// Destructor + /// </summary> + ~texture_view() __GPU + { + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + value_type operator[] (const index<_Rank>& _Index) const __GPU_ONLY + { + _Value_type _Tmp; + _Texture_read_helper<index<_Rank>, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Tmp, _Index, /*_Mip_level=*/0); + return _Tmp; + } + + /// <summary> + /// Get the element value indexed by _I. + /// </summary> + /// <param name="_I"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I. + /// </returns> + value_type operator[] (int _I0) const __GPU_ONLY + { + static_assert(_Rank == 1, "value_type operator[](int) is only permissible on texture_view<value_type, 1>."); + return (*this)[index<1>(_I0)]; + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + value_type operator() (const index<_Rank>& _Index) const __GPU_ONLY + { + return (*this)[_Index]; + } + + /// <summary> + /// Get the element value indexed by _I0 + /// </summary> + /// <param name="_I0"> + /// The index. + /// </param> + /// <returns> + /// The element value indexed by _I0. + /// </returns> + value_type operator() (int _I0) const __GPU_ONLY + { + static_assert(_Rank == 1, "value_type texture_view::operator()(int) is only permissible on texture_view<value_type, 1>."); + return (*this)[index<1>(_I0)]; + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1) + /// </returns> + value_type operator() (int _I0, int _I1) const __GPU_ONLY + { + static_assert(_Rank == 2, "value_type texture_view::operator()(int, int) is only permissible on texture_view<value_type, 2>."); + return (*this)[index<2>(_I0, _I1)]; + } + + /// <summary> + /// Get the element value indexed by (_I0,_I1,_I2) + /// </summary> + /// <param name="_I0"> + /// The most-significant component of the index + /// </param> + /// <param name="_I1"> + /// The next-to-most-significant component of the index + /// </param> + /// <param name="_I2"> + /// The least-significant component of the index + /// </param> + /// <returns> + /// The element value indexed by (_I0,_I1,_I2) + /// </returns> + value_type operator() (int _I0, int _I1, int _I2) const __GPU_ONLY + { + static_assert(_Rank == 3, "value_type texture_view::operator()(int, int, int) is only permissible on texture_view<value_type, 3>."); + return (*this)[index<3>(_I0, _I1, _I2)]; + } + + /// <summary> + /// Get the element value indexed by _Index. + /// </summary> + /// <param name="_Index"> + /// The index. + /// </param> + /// <param name="_Mip_level"> + /// The mipmap level from which we should get indexed value. + /// The default value 0 represents most detailed mipmap level. + /// </param> + /// <returns> + /// The element value indexed by _Index. + /// </returns> + value_type get(const index<_Rank>& _Index, unsigned int _Mip_level = 0) const __GPU_ONLY + { + _Value_type _Tmp; + _Texture_read_helper<index<_Rank>, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Tmp, _Index, _Mip_level); + return _Tmp; + } + + /// <summary> + /// Sample the texture at the given coordinates and level of detail using the specified sampling configuration. + /// </summary> + /// <param name="_Sampler"> + /// The sampler that configures the sampling operation. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <param name="_Level_of_detail"> + /// The value specifies the mipmap level to sample from. + /// Fractional value is used to interpolate between two mipmap levels. + /// </param> + /// <returns> + /// The interpolated value. + /// </returns> + value_type sample(const sampler& _Sampler, const coordinates_type& _Coord, float _Level_of_detail = 0.0f) const __GPU_ONLY + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Uint_type, "sample is not allowed for uint component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Int_type, "sample is not allowed for int component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Double_type, "sample is not allowed for double component types in the texture value_type."); + + _Value_type _Tmp; + _Texture_sample_helper<coordinates_type, _Rank>::func(_M_texture_descriptor._M_data_ptr, _Sampler._Get_descriptor()._M_data_ptr, &_Tmp, _Coord, 4 /*Sampling*/, _Level_of_detail); + return _Tmp; + } + + /// <summary> + /// Sample the texture at the given coordinates and level of detail using the predefined sampling configuration. + /// </summary> + /// <param name="_Filter_mode"> + /// The filter mode of the predefined sampler to be used. + /// </param> + /// <param name="_Address_mode"> + /// The address mode of the predefined sampler to be used. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <param name="_Level_of_detail"> + /// The value specifies the mipmap level to sample from. + /// Fractional value is used to interpolate between two mipmap levels. + /// </param> + /// <returns> + /// The interpolated value. + /// </returns> + template<filter_mode _Filter_mode = filter_linear, address_mode _Address_mode = address_clamp> + value_type sample(const coordinates_type& _Coord, float _Level_of_detail = 0.0f) const __GPU_ONLY + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Uint_type, "sample is not allowed for uint component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Int_type, "sample is not allowed for int component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Double_type, "sample is not allowed for double component types in the texture value_type."); + static_assert((_Filter_mode == filter_point || _Filter_mode == filter_linear), "Invalid filter mode for sample method."); + static_assert((_Address_mode == address_wrap || _Address_mode == address_clamp || _Address_mode == address_mirror || _Address_mode == address_border), + "Invalid address mode for sample method."); + + _Value_type _Tmp; + // Predefined sampler id is constructed as filter_mode << 16 | address_mode. This is a contract between BE and runtime. Modify with caution! + _Texture_predefined_sample_helper<coordinates_type, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Tmp, _Coord, _Filter_mode << 16 |_Address_mode, 4 /*Sampling*/, _Level_of_detail); + return _Tmp; + } + + /// <summary> + /// Sample the texture at the given coordinates using the specified sampling configuration and return the red (x) component of the four texels samples. + /// </summary> + /// <param name="_Sampler"> + /// The sampler that configures the sampling operation. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the red (x) component of the 4 texel values sampled. + /// </returns> + const gather_return_type gather_red(const sampler& _Sampler, const coordinates_type& _Coord) const __GPU_ONLY + { + return _Gather(_Sampler, _Coord, 0); + } + + /// <summary> + /// Sample the texture at the given coordinates using the specified sampling configuration and return the green (y) component of the four texels samples. + /// </summary> + /// <param name="_Sampler"> + /// The sampler that configures the sampling operation. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the green (y) component of the 4 texel values sampled. + /// </returns> + const gather_return_type gather_green(const sampler& _Sampler, const coordinates_type& _Coord) const __GPU_ONLY + { + static_assert(1 < _Short_vector_type_traits<_Value_type>::_Num_channels, "gather_green is valid only for textures with 2 or more components in the value_type."); + + return _Gather(_Sampler, _Coord, 1); + } + + /// <summary> + /// Sample the texture at the given coordinates using the specified sampling configuration and return the blue (z) component of the four texels samples. + /// </summary> + /// <param name="_Sampler"> + /// The sampler that configures the sampling operation. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the blue (z) component of the 4 texel values sampled. + /// </returns> + const gather_return_type gather_blue(const sampler& _Sampler, const coordinates_type& _Coord) const __GPU_ONLY + { + static_assert(2 < _Short_vector_type_traits<_Value_type>::_Num_channels, "gather_blue is valid only for textures with 3 or more components in the value_type."); + + return _Gather(_Sampler, _Coord, 2); + } + + /// <summary> + /// Sample the texture at the given coordinates using the specified sampling configuration and return the alpha (w) component of the four texels samples. + /// </summary> + /// <param name="_Sampler"> + /// The sampler that configures the sampling operation. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the alpha (w) component of the 4 texel values sampled. + /// </returns> + const gather_return_type gather_alpha(const sampler& _Sampler, const coordinates_type& _Coord) const __GPU_ONLY + { + static_assert(3 < _Short_vector_type_traits<_Value_type>::_Num_channels, "gather_alpha is valid only for textures with 4 components in the value_type."); + + return _Gather(_Sampler, _Coord, 3); + } + + /// <summary> + /// Sample the texture at the given coordinates using the predefined sampling configuration and return the red (x) component of the four texels samples. + /// </summary> + /// <param name="_Address_mode"> + /// The address mode of the predefined sampler to be used. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the red (x) component of the 4 texel values sampled. + /// </returns> + template<address_mode _Address_mode = address_clamp> + const gather_return_type gather_red(const coordinates_type& _Coord) const __GPU_ONLY + { + return _Gather<_Address_mode>(_Coord, 0); + } + + /// <summary> + /// Sample the texture at the given coordinates using the predefined sampling configuration and return the green (y) component of the four texels samples. + /// </summary> + /// <param name="_Address_mode"> + /// The address mode of the predefined sampler to be used. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the green (y)component of the 4 texel values sampled. + /// </returns> + template<address_mode _Address_mode = address_clamp> + const gather_return_type gather_green(const coordinates_type& _Coord) const __GPU_ONLY + { + static_assert(1 < _Short_vector_type_traits<_Value_type>::_Num_channels, "gather_green is valid only for textures with 2 or more components in the value_type."); + + return _Gather<_Address_mode>(_Coord, 1); + } + + /// <summary> + /// Sample the texture at the given coordinates using the predefined sampling configuration and return the blue (z) component of the four texels samples. + /// </summary> + /// <param name="_Address_mode"> + /// The address mode of the predefined sampler to be used. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the blue (z) component of the 4 texel values sampled. + /// </returns> + template<address_mode _Address_mode = address_clamp> + const gather_return_type gather_blue(const coordinates_type& _Coord) const __GPU_ONLY + { + static_assert(2 < _Short_vector_type_traits<_Value_type>::_Num_channels, "gather_blue is valid only for textures with 3 or more components in the value_type."); + + return _Gather<_Address_mode>(_Coord, 2); + } + + /// <summary> + /// Sample the texture at the given coordinates using the predefined sampling configuration and return the alpha (w) component of the four texels samples. + /// </summary> + /// <param name="_Address_mode"> + /// The address mode of the predefined sampler to be used. + /// </param> + /// <param name="_Coord"> + /// Coordinate vector for sampling. + /// </param> + /// <returns> + /// Rank 4 short vector containing the alpha (w) component of the 4 texel values sampled. + /// </returns> + template<address_mode _Address_mode = address_clamp> + const gather_return_type gather_alpha(const coordinates_type& _Coord) const __GPU_ONLY + { + static_assert(3 < _Short_vector_type_traits<_Value_type>::_Num_channels, "gather_alpha is valid only for textures with 4 components in the value_type."); + + return _Gather<_Address_mode>(_Coord, 3); + } + +private: + const gather_return_type _Gather(const sampler& _Sampler, const coordinates_type& _Coord, unsigned int _Component) const __GPU_ONLY + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Uint_type, "gather is not allowed for uint component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Int_type, "gather is not allowed for int component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Double_type, "gather is not allowed for double component types in the texture value_type."); + static_assert(rank == 2, "gather methods are only permissible on texture_view<value_type, 2>."); + + gather_return_type _Tmp; + _Texture_sample_helper<coordinates_type, _Rank>::func(_M_texture_descriptor._M_data_ptr, _Sampler._Get_descriptor()._M_data_ptr, &_Tmp, _Coord, _Component, /*_Level_of_detail=*/0.0f); + return _Tmp; + } + + template<address_mode _Address_mode> + const gather_return_type _Gather(const coordinates_type& _Coord, unsigned int _Component) const __GPU_ONLY + { + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Uint_type, "gather is not allowed for uint component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Int_type, "gather is not allowed for int component types in the texture value_type."); + static_assert(_Short_vector_type_traits<_Value_type>::_Format_base_type_id != _Double_type, "gather is not allowed for double component types in the texture value_type."); + static_assert(rank == 2, "gather methods are only permissible on texture_view<value_type, 2>."); + static_assert((_Address_mode == address_wrap || _Address_mode == address_clamp || _Address_mode == address_mirror || _Address_mode == address_border), + "Invalid address mode for gather methods."); + + gather_return_type _Tmp; + // Predefined sampler id is constructed as filter_mode << 16 | address_mode. This is a contract between BE and runtime. Modify with caution! + // gather only used the address_mode of the sampler, internally we use filter_point so that the predefined sampler id scheme is same for both sample and gather. + _Texture_predefined_sample_helper<coordinates_type, _Rank>::func(_M_texture_descriptor._M_data_ptr, &_Tmp, _Coord, filter_point << 16 |_Address_mode, _Component, /*_Level_of_detail=*/0.0f); + return _Tmp; + } +}; + +namespace details +{ + +template <typename T> +struct texture_traits +{ + static const bool is_texture = false; + static const bool is_writable = false; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<texture<_Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = true; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<const texture<_Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = false; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<writeonly_texture_view<_Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = true; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<const writeonly_texture_view<_Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = true; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<texture_view<_Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = true; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<texture_view<const _Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = false; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<const texture_view<const _Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = false; +}; + +template <typename _Value_type, int _Rank> +struct texture_traits<const texture_view<_Value_type, _Rank>> +{ + static const bool is_texture = true; + static const bool is_writable = true; +}; + +// The helper function used by ETW and copy functions to calculate number of bytes for the copy operation given input section +template <typename _Value_type, int _Rank> +unsigned int _Get_section_size(const _Texture_base<_Value_type, _Rank> &_Tex, const extent<_Rank> &_Extent) +{ + _Texture* _Tex_ptr = _Get_texture(_Tex); + _Texture_descriptor _Tex_desc = _Get_texture_descriptor(_Tex); + + return _Tex_ptr->_Get_data_length(_Tex_desc._Get_most_detailed_mipmap_level(), _Tex_desc._Get_view_mipmap_levels(), _Get_dimensions(_Extent, /*Mip_offset=*/0).data()); +} + +template <typename _Input_iterator, typename _Value_type> +_Event _Copy_async_impl(_Input_iterator _First, _Input_iterator _Last, + _In_ _Texture *_Dst, const size_t *_Dst_offset, unsigned int _Dst_mipmap_level, + const size_t *_Copy_extent, const size_t *_Preferred_copy_chunk_extent = NULL) +{ + _ASSERTE(_Dst != nullptr); + _ASSERTE(_Dst_offset != nullptr); + _ASSERTE(_Copy_extent != nullptr); + + _ASSERTE((unsigned int)std::distance(_First, _Last) >= (_Copy_extent[0] * _Copy_extent[1] * _Copy_extent[2])); + + // The copy region should be within the bounds of the destination texture + _ASSERTE((_Dst_offset[0] + _Copy_extent[0]) <= _Dst->_Get_width(_Dst_mipmap_level)); + _ASSERTE((_Dst_offset[1] + _Copy_extent[1]) <= _Dst->_Get_height(_Dst_mipmap_level)); + _ASSERTE((_Dst_offset[2] + _Copy_extent[2]) <= _Dst->_Get_depth(_Dst_mipmap_level)); + +#pragma warning( push ) +#pragma warning( disable : 4127 ) // conditional expression is constant + if ((sizeof(_Value_type) > sizeof(unsigned char)) && (_Dst->_Get_bits_per_element() != (8U * sizeof(_Value_type)))) + { + throw runtime_exception("Iterator-based copy is not supported on textures where the size of the _Value_type is not equal to the texel size.", E_INVALIDARG); + } +#pragma warning( pop ) + + // If the dest is accessible on the host we can perform the copy entirely on the host + if (_Dst->_Get_host_ptr() != NULL) + { + // We have made sure that the three multiplications below won't cause integer overflow when creating the texture + _ASSERTE(((_Dst->_Get_bits_per_element() * _Copy_extent[0]) % (8U * sizeof(_Value_type))) == 0); + + size_t _Row_size = (_Dst->_Get_bits_per_element() * _Copy_extent[0]) >> 3; // in bytes + size_t _Depth_slice_size = _Row_size * _Copy_extent[1]; + + size_t _Row_pitch = _Dst->_Get_row_pitch(); + size_t _Depth_pitch = _Dst->_Get_depth_pitch(); + _ASSERTE(_Row_pitch >= _Row_size); + _ASSERTE(_Depth_pitch >= _Depth_slice_size); + + size_t _Dst_offset_in_bytes = ((_Dst_offset[0] * _Dst->_Get_bits_per_element()) >> 3) + + (_Dst_offset[1] * _Row_pitch) + (_Dst_offset[2] * _Depth_pitch); + + unsigned char *_PDest = reinterpret_cast<unsigned char*>(_Dst->_Get_host_ptr()) + _Dst_offset_in_bytes; + + _Copy_data_on_host(_Dst->_Get_rank(), _First, reinterpret_cast<_Value_type*>(_PDest), + _Row_size / sizeof(_Value_type), _Copy_extent[1], _Copy_extent[2], + _Row_pitch, _Depth_pitch, _Row_size / sizeof(_Value_type), _Depth_slice_size / sizeof(_Value_type)); + + return _Event(); + } + + // The dest is not accessbile on the host; we need to copy src to + // a temporary staging texture and launch a copy from the staging texture + // to the dest texture. + _Event _Ev; + + // Determine the copy chunk extent + std::array<size_t, 3> _Copy_chunk_extent; + if (_Preferred_copy_chunk_extent != NULL) + { + std::copy(&_Preferred_copy_chunk_extent[0], &_Preferred_copy_chunk_extent[3], _Copy_chunk_extent.begin()); + } + else + { + _Get_preferred_copy_chunk_extent(_Dst->_Get_rank(), _Copy_extent[0], _Copy_extent[1], _Copy_extent[2], _Dst->_Get_bits_per_element(), _Copy_chunk_extent.data()); + } + + std::array<size_t, 3> _Curr_copy_offset; + std::copy(&_Dst_offset[0], &_Dst_offset[3], _Curr_copy_offset.begin()); + + std::array<size_t, 3> _Remaining_copy_extent; + std::copy(&_Copy_extent[0], &_Copy_extent[3], _Remaining_copy_extent.begin()); + + bool _Truncated_copy = false; + do + { + _Texture_ptr _Dst_staging_tex_ptr; + std::array<size_t, 3> _Curr_copy_extent; + _Truncated_copy = _Get_chunked_staging_texture(_Dst, _Copy_chunk_extent.data(), _Remaining_copy_extent.data(), _Curr_copy_extent.data(), &_Dst_staging_tex_ptr); + + + // Now copy from the src pointer to the temp staging texture + _Dst_staging_tex_ptr->_Map_buffer(_Write_access, true /* _Wait */); + + std::array<size_t, 3> _Dst_staging_tex_offset; + _Dst_staging_tex_offset.fill(0); + _Event _Temp_ev = _Copy_async_impl<_Input_iterator, _Value_type>(_First, _Last, _Dst_staging_tex_ptr, + _Dst_staging_tex_offset.data(), /*_Dst_mipmap_level=*/0, _Curr_copy_extent.data(), _Copy_chunk_extent.data()); + + // Now chain a copy from the temporary staging texture to the _Dst texture + _Texture_ptr _Dst_tex_ptr = _Dst; + _Temp_ev = _Temp_ev._Add_continuation(std::function<_Event()>([_Dst_staging_tex_ptr, _Dst_tex_ptr, _Curr_copy_extent, + _Dst_staging_tex_offset, _Curr_copy_offset, _Dst_mipmap_level]() mutable -> _Event + { + return _Dst_staging_tex_ptr->_Copy_to_async(_Dst_tex_ptr, _Curr_copy_extent.data(), _Dst_staging_tex_offset.data(), _Curr_copy_offset.data(), /*_Src_mipmap_level=*/0, _Dst_mipmap_level); + })); + + _Ev = _Ev._Add_event(_Temp_ev); + + // Now adjust the _Src and _Dst offsets for the remaining part of the copy + if (_Truncated_copy) + { + // The offset only needs to be adjusted in the most significant dimension + _Curr_copy_offset[_Dst->_Get_rank() - 1] += _Curr_copy_extent[_Dst->_Get_rank() - 1]; + std::advance(_First, (((_Curr_copy_extent[0] * _Dst->_Get_bits_per_element()) >> 3) / sizeof(_Value_type)) * _Curr_copy_extent[1] * _Curr_copy_extent[2]); + } + + } while (_Truncated_copy); + + return _Ev; +} + +template <typename _Output_iterator, typename _Value_type> +_Event _Copy_async_impl(_Texture *_Tex, const size_t *_Tex_offset, unsigned int _Src_mipmap_level, const size_t *_Copy_extent, _Output_iterator _First, const size_t *_Preferred_copy_chunk_extent = NULL) +{ + _ASSERTE(_Tex != nullptr); + _ASSERTE(_Tex_offset != nullptr); + _ASSERTE(_Copy_extent != nullptr); + + // The copy region should be within the bounds of the source texture + _ASSERTE((_Tex_offset[0] + _Copy_extent[0]) <= _Tex->_Get_width(_Src_mipmap_level)); + _ASSERTE((_Tex_offset[1] + _Copy_extent[1]) <= _Tex->_Get_height(_Src_mipmap_level)); + _ASSERTE((_Tex_offset[2] + _Copy_extent[2]) <= _Tex->_Get_depth(_Src_mipmap_level)); + +#pragma warning( push ) +#pragma warning( disable : 4127 ) // conditional expression is constant + if ((sizeof(_Value_type) > sizeof(unsigned char)) && (_Tex->_Get_bits_per_element() != (8U * sizeof(_Value_type)))) + { + throw runtime_exception("Iterator-based copy is not supported on textures where the size of the _Value_type is not equal to the texel size.", E_INVALIDARG); + } +#pragma warning( pop ) + + // If the texture is available on the host then we can perform the copy entirely on the host + if (_Tex->_Get_host_ptr() != nullptr) + { + // We have made sure that the three multiplications below won't cause integer overflow when creating the texture + _ASSERTE(((_Tex->_Get_bits_per_element() * _Copy_extent[0]) % 8U) == 0); + + size_t _Row_size = (_Tex->_Get_bits_per_element() * _Copy_extent[0]) >> 3; // in bytes + size_t _Depth_slice_size = _Row_size * _Copy_extent[1]; + + size_t _Row_pitch = _Tex->_Get_row_pitch(); + size_t _Depth_pitch = _Tex->_Get_depth_pitch(); + _ASSERTE(_Row_pitch >= _Row_size); + _ASSERTE(_Depth_pitch >= _Depth_slice_size); + + size_t _Tex_offset_in_bytes = ((_Tex_offset[0] * _Tex->_Get_bits_per_element()) >> 3) + + (_Tex_offset[1] * _Row_pitch) + (_Tex_offset[2] * _Depth_pitch); + + unsigned char *_PTex = reinterpret_cast<unsigned char*>(_Tex->_Get_host_ptr()) + _Tex_offset_in_bytes; + + _Copy_data_on_host(_Tex->_Get_rank(), reinterpret_cast<_Value_type*>(_PTex), _First, + _Row_size / sizeof(_Value_type), _Copy_extent[1], _Copy_extent[2], + _Row_pitch, _Depth_pitch, _Row_size / sizeof(_Value_type), _Depth_slice_size / sizeof(_Value_type)); + + return _Event(); + } + + // The texture is not accessbile on the host; we need to copy to/from a staging + // texture before the copy to the destination. This is done in chunks, such that + // we can concurrently copy from the source texture to a staging texture while + // copying from a staging texture from a previous chunk to the destination. + _Event _Ev; + + // Determine the copy chunk extent + std::array<size_t, 3> _Copy_chunk_extent; + if (_Preferred_copy_chunk_extent != nullptr) + { + std::copy(&_Preferred_copy_chunk_extent[0], &_Preferred_copy_chunk_extent[3], _Copy_chunk_extent.begin()); + } + else + { + _Get_preferred_copy_chunk_extent(_Tex->_Get_rank(), _Copy_extent[0], _Copy_extent[1], _Copy_extent[2], _Tex->_Get_bits_per_element(), _Copy_chunk_extent.data()); + } + + std::array<size_t, 3> _Curr_copy_offset; + std::copy(&_Tex_offset[0], &_Tex_offset[3], _Curr_copy_offset.begin()); + + std::array<size_t, 3> _Remaining_copy_extent; + std::copy(&_Copy_extent[0], &_Copy_extent[3], _Remaining_copy_extent.begin()); + + bool _Truncated_copy = false; + + _Texture_ptr _Staging_tex_ptr; + std::array<size_t, 3> _Curr_copy_extent; + _Truncated_copy = _Get_chunked_staging_texture(_Tex, _Copy_chunk_extent.data(), _Remaining_copy_extent.data(), _Curr_copy_extent.data(), &_Staging_tex_ptr); + + // Now copy into the temp staging texture + std::array<size_t, 3> _Staging_tex_offset; + _Staging_tex_offset.fill(0); + _Event _Temp_ev = _Copy_async_impl(_Tex, _Curr_copy_offset.data(), _Src_mipmap_level, + _Staging_tex_ptr._Get_ptr(), _Staging_tex_offset.data(), /*_Dst_mipmap_level=*/0, + _Curr_copy_extent.data(), _Copy_chunk_extent.data()); + _Ev = _Ev._Add_event(_Temp_ev); + + // If we have finished our copy, we just need to add a continuation to copy + // from the temporary staging texture to the _Dst pointer + if (!_Truncated_copy) + { + return _Ev._Add_continuation(std::function<_Event()>([_Staging_tex_ptr, + _Curr_copy_extent, _Staging_tex_offset, _Copy_chunk_extent, _First]() mutable -> _Event + { + return _Copy_async_impl<_Output_iterator, _Value_type>(_Staging_tex_ptr, _Staging_tex_offset.data(), /*_Src_mipmap_level=*/0, _Curr_copy_extent.data(), _First, _Copy_chunk_extent.data()); + })); + } + else + { + // The copy was truncated. We need to recursively perform the rest of the copy + _Texture_ptr _Tex_ptr = _Tex; + _Curr_copy_offset[_Tex->_Get_rank() - 1] += _Curr_copy_extent[_Tex->_Get_rank() - 1]; + return _Ev._Add_continuation(std::function<_Event()>([_Staging_tex_ptr, _First, _Curr_copy_extent, + _Staging_tex_offset, _Tex_ptr, _Curr_copy_offset, _Remaining_copy_extent, _Copy_chunk_extent, _Src_mipmap_level]() mutable -> _Event + { + // Initiate copying of the remaining portion + _Output_iterator _New_dst_iter = _First; + _Advance_output_iterator<decltype(_New_dst_iter), size_t>(_New_dst_iter, (((_Curr_copy_extent[0] * _Tex_ptr->_Get_bits_per_element()) >> 3) / sizeof(_Value_type)) * _Curr_copy_extent[1] * _Curr_copy_extent[2]); + _Event _Ev1 = _Copy_async_impl<_Output_iterator, _Value_type>(_Tex_ptr, _Curr_copy_offset.data(), _Src_mipmap_level, _Remaining_copy_extent.data(), _New_dst_iter, _Copy_chunk_extent.data()); + + // Now copy the data from the temp staging buffer to the _Dst pointer + _Event _Ev2 = _Copy_async_impl<_Output_iterator, _Value_type>(_Staging_tex_ptr, _Staging_tex_offset.data(), /*_Src_mipmap_level=*/0, _Curr_copy_extent.data(), _First, _Copy_chunk_extent.data()); + + return _Ev2._Add_event(_Ev1); + })); + } +} + +template <typename _Value_type, int _Rank> +_Event _Copy_async_impl(const void * _Src, unsigned int _Src_byte_size, const _Texture_base<_Value_type, _Rank>& _Dst, const index<_Rank> &_Dst_offset, const extent<_Rank> &_Copy_extent) +{ + _Is_valid_section(_Dst.extent, _Dst_offset, _Copy_extent); + + if (_Dst.get_mipmap_levels() > 1) + { + throw runtime_exception("Invalid destination - multiple mipmap levels cannot be copied from source", E_INVALIDARG); + } + + if (_Src_byte_size < _Get_section_size(_Dst, _Copy_extent)) + { + if (_Dst.extent == _Copy_extent) + { + throw runtime_exception("Invalid _Src_byte_size argument. _Src_byte_size is smaller than the total size of _Dst.", E_INVALIDARG); + } + else + { + throw runtime_exception("Invalid _Src_byte_size argument. _Src_byte_size is smaller than the provided section of _Dst.", E_INVALIDARG); + } + } + + _Texture *_Dst_tex_ptr = _Get_texture(_Dst); + std::array<size_t, 3> _Copy_extent_arr = _Get_dimensions(_Copy_extent, /*_Mip_offset=*/0); + std::array<size_t, 3> _Dst_offset_arr = _Get_indices(_Dst_offset); + auto _First = stdext::make_unchecked_array_iterator(reinterpret_cast<const unsigned char*>(_Src)); + auto _Last = stdext::make_unchecked_array_iterator(reinterpret_cast<const unsigned char*>(_Src) + _Src_byte_size); + + return _Copy_async_impl<decltype(_First), unsigned char>(_First, _Last, _Dst_tex_ptr, _Dst_offset_arr.data(), _Get_texture_descriptor(_Dst)._Get_most_detailed_mipmap_level(), _Copy_extent_arr.data()); +} + +template<typename _Value_type, int _Rank> +_Event _Copy_async_impl(const _Texture_base<_Value_type, _Rank>& _Src, const index<_Rank> &_Src_offset, const extent<_Rank> &_Copy_extent, _Out_ void * _Dst, unsigned int _Dst_byte_size) +{ + _Is_valid_section(_Src.extent, _Src_offset, _Copy_extent); + + if (_Src.get_mipmap_levels() > 1) + { + throw runtime_exception("Invalid source - multiple mipmap levels cannot be copied to destination", E_INVALIDARG); + } + + if (_Get_section_size(_Src, _Copy_extent) > _Dst_byte_size) + { + if (_Src.extent == _Copy_extent) + { + throw runtime_exception("Invalid _Dst_byte_size argument. _Dst_byte_size is smaller than the size of _Src.", E_INVALIDARG); + } + else + { + throw runtime_exception("Invalid _Dst_byte_size argument. _Dst_byte_size is smaller than the provided section of _Src.", E_INVALIDARG); + } + } + + _Texture *_Src_tex_ptr = _Get_texture(_Src); + std::array<size_t, 3> _Copy_extent_arr = _Get_dimensions(_Copy_extent, /*_Mip_offset=*/0); + std::array<size_t, 3> _Src_offset_arr = _Get_indices(_Src_offset); + + auto _First = stdext::make_unchecked_array_iterator(reinterpret_cast<unsigned char*>(_Dst)); + + return _Copy_async_impl<decltype(_First), unsigned char>(_Src_tex_ptr, _Src_offset_arr.data(), _Get_texture_descriptor(_Src)._Get_most_detailed_mipmap_level(), _Copy_extent_arr.data(), _First); +} + +template <typename _Output_iterator, typename _Value_type, int _Rank> +_Event _Copy_async_impl(const _Texture_base<_Value_type, _Rank> &_Src, const index<_Rank> &_Src_offset, const extent<_Rank> &_Copy_extent, _Output_iterator _Dest_iter) +{ + _Is_valid_section(_Src.extent, _Src_offset, _Copy_extent); + + if (_Src.get_mipmap_levels() > 1) + { + throw runtime_exception("Invalid source - multiple mipmap levels cannot be copied to destination", E_INVALIDARG); + } + + _Texture *_Src_tex_ptr = _Get_texture(_Src); + std::array<size_t, 3> _Copy_extent_arr = _Get_dimensions(_Copy_extent, /*_Mip_offset=*/0); + std::array<size_t, 3> _Src_offset_arr = _Get_indices(_Src_offset); + + return _Copy_async_impl<_Output_iterator, _Value_type>(_Src_tex_ptr, _Src_offset_arr.data(), _Get_texture_descriptor(_Src)._Get_most_detailed_mipmap_level(), _Copy_extent_arr.data(), _Dest_iter); +} + +template <typename _Input_iterator, typename _Value_type, int _Rank> +_Event _Copy_async_impl(_Input_iterator _First, _Input_iterator _Last, const _Texture_base<_Value_type, _Rank>& _Dst, const index<_Rank> &_Dst_offset, const extent<_Rank> &_Copy_extent) +{ + _Is_valid_section(_Dst.extent, _Dst_offset, _Copy_extent); + if (static_cast<unsigned int>(std::distance(_First, _Last)) < _Copy_extent.size()) + { + throw runtime_exception("Inadequate amount of data supplied through the iterators", E_INVALIDARG); + } + + if (_Dst.get_mipmap_levels() > 1) + { + throw runtime_exception("Invalid destination - multiple mipmap levels cannot be copied from source", E_INVALIDARG); + } + + std::array<size_t, 3> _Copy_extent_arr = _Get_dimensions(_Copy_extent, /*_Mip_offset=*/0); + std::array<size_t, 3> _Dst_offset_arr = _Get_indices(_Dst_offset); + + _Texture *_Dst_tex_ptr = _Get_texture(_Dst); + return _Copy_async_impl<_Input_iterator, _Value_type>(_First, _Last, _Dst_tex_ptr, _Dst_offset_arr.data(), _Get_texture_descriptor(_Dst)._Get_most_detailed_mipmap_level(), _Copy_extent_arr.data()); +} + +template<typename _Value_type, int _Rank> +_Event _Copy_async_impl(const _Texture_base<_Value_type, _Rank>& _Src, const index<_Rank> &_Src_offset, + const _Texture_base<_Value_type, _Rank>& _Dst, const index<_Rank> &_Dst_offset, + const extent<_Rank> &_Copy_extent) +{ + _Is_valid_section(_Src.extent, _Src_offset, _Copy_extent); + _Is_valid_section(_Dst.extent, _Dst_offset, _Copy_extent); + + _Texture_descriptor _Src_tex_desc = _Get_texture_descriptor(_Src); + _Texture_descriptor _Dst_tex_desc = _Get_texture_descriptor(_Dst); + + if (_Src_tex_desc._Get_view_mipmap_levels() != _Dst_tex_desc._Get_view_mipmap_levels()) + { + throw runtime_exception("The source and destination textures must have the exactly the same number of mipmap levels for texture copy.", E_INVALIDARG); + } + + bool _Is_whole_texture_copy = (_Src_offset == _Dst_offset && _Src_offset == index<_Rank>() && _Src.extent == _Dst.extent && _Src.extent == _Copy_extent); + + if (_Src_tex_desc._Get_view_mipmap_levels() > 1 && !_Is_whole_texture_copy) + { + throw runtime_exception("Sections are not allowed when copy involves multiple mipmap levels", E_INVALIDARG); + } + + if (_Src_tex_desc._Are_mipmap_levels_overlapping(&_Dst_tex_desc)) + { + throw runtime_exception("The source and destination are overlapping areas on the same texture", E_INVALIDARG); + } + + _Texture* _Src_tex = _Get_texture(_Src); + _Texture* _Dst_tex = _Get_texture(_Dst); + + // Formats must be identical for non-adopted textures. Textures created through D3D interop are not subject to this test + // to allow copy between related, but not identical, formats. Attempting to copy between unrelated formats through interop + // will result in exceptions in debug mode and undefined behavior in release mode. + if (!_Src_tex->_Is_adopted() && !_Dst_tex->_Is_adopted() && (_Src_tex->_Get_texture_format() != _Dst_tex->_Get_texture_format())) + { + throw runtime_exception("The source and destination textures are not compatible.", E_INVALIDARG); + } + + std::array<size_t, 3> _Src_offset_arr = _Get_indices(_Src_offset); + std::array<size_t, 3> _Dst_offset_arr = _Get_indices(_Dst_offset); + + _Event _Copy_event; + + unsigned int _Src_most_detailed_mipmap_level = _Src_tex_desc._Get_most_detailed_mipmap_level(); + unsigned int _Dst_most_detailed_mipmap_level = _Dst_tex_desc._Get_most_detailed_mipmap_level(); + + // Copy all mipmap levels from source to destination one by one. + // Note that the offsets are not allowed therefore only dimensions need to be updated for subsequent mipmap levels + for (unsigned int _Mip_offset = 0; _Mip_offset < _Src_tex_desc._Get_view_mipmap_levels(); ++_Mip_offset) + { + std::array<size_t, 3> _Copy_extent_arr = _Get_dimensions(_Copy_extent, _Mip_offset); + + auto _Step_event = _Copy_async_impl(_Src_tex, _Src_offset_arr.data(), _Src_most_detailed_mipmap_level + _Mip_offset, + _Dst_tex, _Dst_offset_arr.data(), _Dst_most_detailed_mipmap_level + _Mip_offset, + _Copy_extent_arr.data()); + + _Copy_event = _Copy_event._Add_event(_Step_event); + } + + return _Copy_event; +} + +} // namespace details + +/// <summary> +/// Copies the contents of the source texture into the destination host buffer. +/// </summary> +/// <param name="_Rank"> +/// The rank of the source texture. +/// </param> +/// <param name="_Value_type"> +/// The type of the elements of the source texture. +/// </param> +/// <param name="_Src"> +/// The source texture or texture_view. +/// </param> +/// <param name="_Dst"> +/// The destination host buffer. +/// </param> +/// <param name="_Dst_byte_size"> +/// Number of bytes in the destination buffer. +/// </param> +template <typename _Src_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture, void>::type> void copy(const _Src_type &_Src, _Out_ void * _Dst, unsigned int _Dst_byte_size) +{ + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Src.extent)); + + details::_Copy_async_impl(_Src, index<_Src_type::rank>(), _Src.extent, _Dst, _Dst_byte_size)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies the contents of a section of the source texture into the destination host buffer. +/// </summary> +/// <param name="_Rank"> +/// The rank of the source texture. +/// </param> +/// <param name="_Value_type"> +/// The type of the elements of the source texture. +/// </param> +/// <param name="_Src"> +/// The source texture or texture_view. +/// </param> +/// <param name="_Src_offset"> +/// The offset into the source texture from which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +/// <param name="_Dst"> +/// The destination host buffer. +/// </param> +/// <param name="_Dst_byte_size"> +/// Number of bytes in the destination buffer. +/// </param> +template <typename _Src_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture, void>::type> void copy(const _Src_type &_Src, const index<_Src_type::rank> &_Src_offset, const extent<_Src_type::rank> &_Copy_extent, _Out_ void * _Dst, unsigned int _Dst_byte_size) +{ + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Copy_extent)); + + details::_Copy_async_impl(_Src, _Src_offset, _Copy_extent, _Dst, _Dst_byte_size)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + + +/// <summary> +/// Copies the contents of the source host buffer into the destination texture _Dst. +/// </summary> +/// <param name="_Rank"> +/// The rank of the destination texture. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture or texture_view. +/// </param> +/// <param name="_Src"> +/// The source host buffer. +/// </param> +/// <param name="_Src_byte_size"> +/// Number of bytes in the source buffer. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +template <typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> void copy(const void * _Src, unsigned int _Src_byte_size, _Dst_type & _Dst) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Dst.extent)); + + details::_Copy_async_impl(_Src, _Src_byte_size, _Dst, index<_Dst_type::rank>(), _Dst.extent)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies the contents of the source host buffer into a section of the destination texture _Dst. +/// </summary> +/// <param name="_Dst_type"> +/// The type of the destination texture or texture_view. +/// </param> +/// <param name="_Src"> +/// The source host buffer. +/// </param> +/// <param name="_Src_byte_size"> +/// Number of bytes in the source buffer. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +/// <param name="_Dst_offset"> +/// The offset into the destination texture to which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +template <typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> void copy(const void * _Src, unsigned int _Src_byte_size, _Dst_type & _Dst, + const index<_Dst_type::rank> &_Dst_offset, const extent<_Dst_type::rank> &_Copy_extent) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Copy_extent)); + + details::_Copy_async_impl(_Src, _Src_byte_size, _Dst, _Dst_offset, _Copy_extent)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + + +/// <summary> +/// Asynchronously copies the contents of the source texture into the destination host buffer. +/// </summary> +/// <param name="_Rank"> +/// The rank of the source texture. +/// </param> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="_Src"> +/// The source texture or texture_view. +/// </param> +/// <param name="_Dst"> +/// The destination host buffer. +/// </param> +/// <param name="_Dst_byte_size"> +/// Number of bytes in the destination buffer. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template<typename _Src_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture, void>::type> concurrency::completion_future copy_async(const _Src_type &_Src, _Out_ void * _Dst, unsigned int _Dst_byte_size) +{ + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Src.extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, index<_Src_type::rank>(), _Src.extent, _Dst, _Dst_byte_size); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies the contents of the provided section of the source texture into the destination host buffer. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="_Src"> +/// The source texture or texture_view. +/// </param> +/// <param name="_Src_offset"> +/// The offset into the source texture from which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +/// <param name="_Dst"> +/// The destination host buffer. +/// </param> +/// <param name="_Dst_byte_size"> +/// Number of bytes in the destination buffer. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template<typename _Src_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture, void>::type> concurrency::completion_future copy_async(const _Src_type &_Src, const index<_Src_type::rank> &_Src_offset, const extent<_Src_type::rank> &_Copy_extent, + _Out_ void * _Dst, unsigned int _Dst_byte_size) +{ + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Copy_extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, _Src_offset, _Copy_extent, _Dst, _Dst_byte_size); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies the contents of the source host buffer into the destination texture _Dst. +/// </summary> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_Src"> +/// The source host buffer. +/// </param> +/// <param name="_Src_byte_size"> +/// Number of bytes in the source buffer. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> concurrency::completion_future copy_async(const void * _Src, unsigned int _Src_byte_size, _Dst_type & _Dst) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Dst.extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, _Src_byte_size, _Dst, index<_Dst_type::rank>(), _Dst.extent); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies the contents of the source host buffer into a section of the destination texture _Dst. +/// </summary> +/// <param name="_Dst_type"> +/// The type of the elements of the destination texture. +/// </param> +/// <param name="_Src"> +/// The source host buffer. +/// </param> +/// <param name="_Src_byte_size"> +/// Number of bytes in the source buffer. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +/// <param name="_Dst_offset"> +/// The offset into the destination texture to which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> concurrency::completion_future copy_async(const void * _Src, unsigned int _Src_byte_size, _Dst_type & _Dst, + const index<_Dst_type::rank> &_Dst_offset, const extent<_Dst_type::rank> &_Copy_extent) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Copy_extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, _Src_byte_size, _Dst, _Dst_offset, _Copy_extent); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Copies data from the pair of source iterators into the destination texture _Dst. +/// </summary> +/// <param name="InputIterator"> +/// The input iterator type. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_First"> +/// The starting iterator for the copy. +/// </param> +/// <param name="_Last"> +/// The ending iterator for the copy. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +template <typename InputIterator, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> void copy(InputIterator _First, InputIterator _Last, _Dst_type &_Dst) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Dst.extent)); + + details::_Copy_async_impl(_First, _Last, _Dst, index<_Dst_type::rank>(), _Dst.extent)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies data from the pair of source iterators into a section of the destination texture _Dst. +/// </summary> +/// <param name="InputIterator"> +/// The input iterator type. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_First"> +/// The starting iterator for the copy. +/// </param> +/// <param name="_Last"> +/// The ending iterator for the copy. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +/// <param name="_Dst_offset"> +/// The offset into the destination texture to which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +template <typename InputIterator, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> void copy(InputIterator _First, InputIterator _Last, _Dst_type &_Dst, const index<_Dst_type::rank> &_Dst_offset, const extent<_Dst_type::rank> &_Copy_extent) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Copy_extent)); + + details::_Copy_async_impl(_First, _Last, _Dst, _Dst_offset, _Copy_extent)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies data from the source texture _Src into an output iterator. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="OutputIterator"> +/// The output iterator type. +/// </param> +/// <param name="_Dst"> +/// The starting iterator for the copy output. +/// </param> +template <typename _Src_type, typename OutputIterator, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && !details::texture_traits<OutputIterator>::is_texture, void>::type> void copy(const _Src_type &_Src, OutputIterator _Dst) +{ + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Src.extent)); + + details::_Copy_async_impl(_Src, index<_Src_type::rank>(), _Src.extent, _Dst)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies data from a section of the source texture _Src into an output iterator. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="OutputIterator"> +/// The output iterator type. +/// </param> +/// <param name="_Src_offset"> +/// The offset into the source texture from which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +/// <param name="_Dst"> +/// The starting iterator for the copy output. +/// </param> +template <typename _Src_type, typename OutputIterator, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && !details::texture_traits<OutputIterator>::is_texture, void>::type> void copy(const _Src_type &_Src, const index<_Src_type::rank> &_Src_offset, const extent<_Src_type::rank> &_Copy_extent, OutputIterator _Dst) +{ + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Copy_extent)); + + details::_Copy_async_impl(_Src, _Src_offset, _Copy_extent, _Dst)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies data from the source texture _Src into the destination texture _Dst. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_Src"> +/// The source texture from which to copy. +/// </param> +/// <param name="_Dst"> +/// The destination texture into which to copy. +/// </param> +template <typename _Src_type, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && details::texture_traits<_Dst_type>::is_texture, void>::type> void copy(const _Src_type &_Src, _Dst_type &_Dst) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + + if (_Src.extent != _Dst.extent) + { + throw runtime_exception("The source and destination textures must have the exactly the same extent for whole-texture copy.", E_INVALIDARG); + } + + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Dst.extent)); + + details::_Copy_async_impl(_Src, index<_Src_type::rank>(), _Dst, index<_Dst_type::rank>(), _Dst.extent)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Copies data from a section of the source texture _Src into a section of the destination texture _Dst. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_Src"> +/// The source texture from which to copy. +/// </param> +/// <param name="_Src_offset"> +/// The offset into the source texture from which to begin copying. +/// </param> +/// <param name="_Dst"> +/// The destination texture into which to copy. +/// </param> +/// <param name="_Dst_offset"> +/// The offset into the destination texture to which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +template <typename _Src_type, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && details::texture_traits<_Dst_type>::is_texture, void>::type> void copy(const _Src_type &_Src, const index<_Src_type::rank> &_Src_offset, _Dst_type &_Dst, const index<_Dst_type::rank> &_Dst_offset, const extent<_Src_type::rank> &_Copy_extent) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Span_id = concurrency::details::_Get_amp_trace()->_Start_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Src, _Copy_extent)); + + details::_Copy_async_impl(_Src, _Src_offset, _Dst, _Dst_offset, _Copy_extent)._Get(); + + concurrency::details::_Get_amp_trace()->_Write_end_event(_Span_id); +} + +/// <summary> +/// Asynchronously copies data from the pair of source iterators into the destination texture _Dst. +/// </summary> +/// <param name="InputIterator"> +/// The input iterator type. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_First"> +/// The starting iterator for the copy. +/// </param> +/// <param name="_Last"> +/// The ending iterator for the copy. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename InputIterator, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> concurrency::completion_future copy_async(InputIterator _First, InputIterator _Last, _Dst_type &_Dst) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Dst.extent)); + + _Event _Ev = details::_Copy_async_impl<InputIterator, _Dst_type::value_type, _Dst_type::rank>(_First, _Last, _Dst, index<_Dst_type::rank>(), _Dst.extent); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies data from the pair of source iterators into a section of the destination texture _Dst. +/// </summary> +/// <param name="InputIterator"> +/// The input iterator type. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_First"> +/// The starting iterator for the copy. +/// </param> +/// <param name="_Last"> +/// The ending iterator for the copy. +/// </param> +/// <param name="_Dst"> +/// The destination texture or texture_view. +/// </param> +/// <param name="_Dst_offset"> +/// The offset into the destination texture to which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename InputIterator, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Dst_type>::is_texture, void>::type> concurrency::completion_future copy_async(InputIterator _First, InputIterator _Last, _Dst_type &_Dst, + const index<_Dst_type::rank> &_Dst_offset, const extent<_Dst_type::rank> &_Copy_extent) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(nullptr, + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Copy_extent)); + + _Event _Ev = details::_Copy_async_impl<InputIterator, _Dst_type::value_type, _Dst_type::rank>(_First, _Last, _Dst, _Dst_offset, _Copy_extent); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies data from the source texture _Src into an output iterator. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="OutputIterator"> +/// The output iterator type. +/// </param> +/// <param name="_Dst"> +/// The starting iterator for the copy output. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Src_type, typename OutputIterator, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && !details::texture_traits<OutputIterator>::is_texture, void>::type> concurrency::completion_future copy_async(_Src_type &_Src, OutputIterator _Dst) +{ + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Src.extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, index<_Src_type::rank>(), _Src.extent, _Dst); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies data from a section of the source texture _Src into an output iterator. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="OutputIterator"> +/// The output iterator type. +/// </param> +/// <param name="_Src_offset"> +/// The offset into the source texture from which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +/// <param name="_Dst"> +/// The starting iterator for the copy output. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Src_type, typename OutputIterator, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && !details::texture_traits<OutputIterator>::is_texture, void>::type> concurrency::completion_future copy_async(_Src_type &_Src, const index<_Src_type::rank> &_Src_offset, const extent<_Src_type::rank> &_Copy_extent, OutputIterator _Dst) +{ + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + nullptr, + _Get_section_size(_Src, _Copy_extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, _Src_offset, _Copy_extent, _Dst); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies data from the source texture _Src into the destination texture _Dst. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_Src"> +/// The source texture from which to copy. +/// </param> +/// <param name="_Dst"> +/// The destination texture into which to copy. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Src_type, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && details::texture_traits<_Dst_type>::is_texture, void>::type> concurrency::completion_future copy_async(_Src_type &_Src, _Dst_type &_Dst) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + + if (_Src.extent != _Dst.extent) + { + throw runtime_exception("The source and destination textures must have the exactly the same extent for whole-texture copy.", E_INVALIDARG); + } + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Dst, _Dst.extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, index<_Src_type::rank>(), _Dst, index<_Dst_type::rank>(), _Dst.extent); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +/// <summary> +/// Asynchronously copies data from a section of the source texture _Src into the destination texture _Dst. +/// </summary> +/// <param name="_Src_type"> +/// The type of the source texture. +/// </param> +/// <param name="_Dst_type"> +/// The type of the destination texture. +/// </param> +/// <param name="_Src"> +/// The source texture from which to copy. +/// </param> +/// <param name="_Src_offset"> +/// The offset into the source texture from which to begin copying. +/// </param> +/// <param name="_Dst"> +/// The destination texture into which to copy. +/// </param> +/// <param name="_Dst_offset"> +/// The offset into the destination texture to which to begin copying. +/// </param> +/// <param name="_Copy_extent"> +/// The extent of the texture section to copy. +/// </param> +/// <returns> +/// A future upon which to wait for the operation to complete. +/// </returns> +template <typename _Src_type, typename _Dst_type, typename = typename std::enable_if<details::texture_traits<_Src_type>::is_texture && details::texture_traits<_Dst_type>::is_texture, void>::type> concurrency::completion_future copy_async(_Src_type &_Src, const index<_Src_type::rank> &_Src_offset, _Dst_type &_Dst, const index<_Dst_type::rank> &_Dst_offset, const extent<_Src_type::rank> &_Copy_extent) +{ + static_assert(details::texture_traits<_Dst_type>::is_writable, "Destination is not a writable texture type."); + + auto _Async_op_id = concurrency::details::_Get_amp_trace()->_Launch_async_copy_event_helper(concurrency::details::_Get_texture_descriptor(_Src), + concurrency::details::_Get_texture_descriptor(_Dst), + _Get_section_size(_Src, _Copy_extent)); + + _Event _Ev = details::_Copy_async_impl(_Src, _Src_offset, _Dst, _Dst_offset, _Copy_extent); + + return concurrency::details::_Get_amp_trace()->_Start_async_op_wait_event_helper(_Async_op_id, _Ev); +} + +namespace details +{ +template<int _Rank> +Concurrency::extent<_Rank> _Make_texture(const Concurrency::accelerator_view &_Av, _In_ IUnknown *_D3D_texture, _Texture_base_type_id _Id, _Inout_ _Texture ** _Tex, DXGI_FORMAT _View_format) __CPU_ONLY +{ + if (_D3D_texture == NULL) + { + throw runtime_exception("NULL D3D texture pointer.", E_INVALIDARG); + } + + if (!Concurrency::details::_Is_D3D_accelerator_view(_Av)) { + throw runtime_exception("Cannot create D3D texture on a non-D3D accelerator_view.", E_INVALIDARG); + } + + _Texture * _Tex_ptr = _Texture::_Adopt_texture(_Rank, _Id, _D3D_texture, _Av, _View_format); + if (_Tex_ptr->_Is_staging()) + { + _Tex_ptr->_Map_buffer(_Write_access, true /* _Wait */); + } + Concurrency::extent<_Rank> _Ext = Concurrency::graphics::details::_Create_extent<_Rank>(_Tex_ptr->_Get_width(), _Tex_ptr->_Get_height(), _Tex_ptr->_Get_depth()); + + _Is_valid_extent(_Ext); + details::_Is_valid_data_length(_Ext.size(), _Tex_ptr->_Get_bits_per_element()); + + *_Tex = _Tex_ptr; + return _Ext; +} + +#pragma warning( pop ) +} // namespace details + +namespace direct3d +{ + /// <summary> + /// Get the D3D texture interface underlying a texture. + /// </summary> + /// <param name="_Rank"> + /// The rank of the texture to get underlying D3D texture of. + /// </param> + /// <param name="_Value_type"> + /// The type of the elements in the texture to get underlying D3D texture of. + /// </param> + /// <param name="_Texture"> + /// A texture on a D3D accelerator_view for which the underlying D3D texture interface is returned. + /// </param> + /// <returns> + /// The IUnknown interface pointer corresponding to the D3D texture underlying the texture. + /// </returns> + template<typename _Value_type, int _Rank> _Ret_ IUnknown *get_texture(const texture<_Value_type, _Rank> &_Texture) __CPU_ONLY + { + return Concurrency::details::_D3D_interop::_Get_D3D_texture(Concurrency::details::_Get_texture(_Texture)); + } + + /// <summary> + /// Get the D3D texture interface underlying a texture viewed by a writeonly_texture_view. + /// </summary> + /// <param name="_Rank"> + /// The rank of the texture to get underlying D3D texture of. + /// </param> + /// <param name="_Value_type"> + /// The type of the elements in the texture to get underlying D3D texture of. + /// </param> + /// <param name="_Texture"> + /// A writeonly_texture_view of a texture on a D3D accelerator_view for which the underlying D3D texture interface is returned. + /// </param> + /// <returns> + /// The IUnknown interface pointer corresponding to the D3D texture underlying the texture. + /// </returns> +#pragma warning( push ) +#pragma warning( disable : 4996 ) //writeonly_texture_view is deprecated + template<typename _Value_type, int _Rank> _Ret_ IUnknown *get_texture(const writeonly_texture_view<_Value_type, _Rank> &_Texture) __CPU_ONLY + { + return Concurrency::details::_D3D_interop::_Get_D3D_buffer(Concurrency::details::_Get_texture(_Texture)); + } +#pragma warning( pop ) + + /// <summary> + /// Get the D3D texture interface underlying a texture viewed by a texture_view. + /// </summary> + /// <param name="_Rank"> + /// The rank of the texture to get underlying D3D texture of. + /// </param> + /// <param name="_Value_type"> + /// The type of the elements in the texture to get underlying D3D texture of. + /// </param> + /// <param name="_Texture"> + /// A texture_view of a texture on a D3D accelerator_view for which the underlying D3D texture interface is returned. + /// </param> + /// <returns> + /// The IUnknown interface pointer corresponding to the D3D texture underlying the texture. + /// </returns> + template<typename _Value_type, int _Rank> _Ret_ IUnknown *get_texture(const texture_view<_Value_type, _Rank> &_Texture) __CPU_ONLY + { + return Concurrency::details::_D3D_interop::_Get_D3D_buffer(Concurrency::details::_Get_texture(_Texture)); + } + + /// <summary> + /// Create an texture from a D3D texture interface pointer, optionally using the specified DXGI format for all + /// views on this texture. + /// </summary> + /// <param name="_Rank"> + /// The rank of the texture to be created from the D3D texture. + /// </param> + /// <param name="_Value_type"> + /// The type of the elements of the texture to be created from the D3D texture. + /// </param> + /// <param name="_Av"> + /// A D3D accelerator view on which the texture is to be created. + /// </param> + /// <param name="_D3D_texture"> + /// IUnknown interface pointer of the D3D texture to create the texture from. + /// </param> + /// <param name="_View_format"> + /// The DXGI format to use for views created from this texture. Pass DXGI_FORMAT_UNKNOWN (the default) + /// to derive the format from the underlying format of _D3D_texture and the _Value_type of this template. + /// The provided format must be compatible with the underlying format of _D3D_texture. + /// </param> + /// <returns> + /// A texture using the provided D3D texture. + /// </returns> + template<typename _Value_type, int _Rank> texture<_Value_type, _Rank> make_texture(const Concurrency::accelerator_view &_Av, _In_ IUnknown *_D3D_texture, + DXGI_FORMAT _View_format /*= DXGI_FORMAT_UKNNOWN*/) __CPU_ONLY + { + _Texture * _Tex_ptr = NULL; +#pragma warning( suppress: 6326 ) // Potential comparison of a constant with another constant + Concurrency::extent<_Rank> _Ext = Concurrency::graphics::details::_Make_texture<_Rank>(_Av, _D3D_texture, + _Short_vector_type_traits<_Value_type>::_Format_base_type_id == _Double_type ? _Uint_type : _Short_vector_type_traits<_Value_type>::_Format_base_type_id, + &_Tex_ptr, _View_format); + + _ASSERTE(_Tex_ptr); + return texture<_Value_type, _Rank>(_Ext, _Texture_descriptor(_Tex_ptr)); + } + + /// <summary> + /// Get the D3D sampler state interface on the given accelerator view that represents the specified sampler object. + /// </summary> + /// <param name="_Av"> + /// A D3D accelerator view on which the D3D sampler state is to be created. + /// </param> + /// <param name="_Sampler"> + /// A sampler object for which the underlying D3D sampler state interface is created. + /// </param> + /// <returns> + /// The IUnknown interface pointer corresponding to the D3D sampler state that represents the given sampler. + /// </returns> + inline _Ret_ IUnknown * get_sampler(const Concurrency::accelerator_view &_Av, const sampler &_Sampler) __CPU_ONLY + { + return Concurrency::details::_D3D_interop::_Get_D3D_sampler(_Av, _Sampler._Get_sampler_ptr()); + } + + /// <summary> + /// Create a sampler from a D3D sampler state interface pointer. + /// </summary> + /// <param name="_D3D_sampler"> + /// IUnknown interface pointer of the D3D sampler state to create the sampler from. + /// </param> + /// <returns> + /// A sampler represents the provided D3D sampler state. + /// </returns> + inline sampler make_sampler(_In_ IUnknown *_D3D_sampler) __CPU_ONLY + { + return sampler(_Sampler_descriptor(_Sampler::_Create(_D3D_interop::_Get_D3D_sampler_data_ptr(_D3D_sampler)))); + } + + /// <summary> + /// Compares a 4-byte reference value and an 8-byte source value and + /// accumulates a vector of 4 sums. Each sum corresponds to the masked + /// sum of absolute differences of different byte alignments between + /// the reference value and the source value. + /// </summary> + /// <param name="_Reference"> + /// The reference array of 4 bytes in one uint value + /// </param> + /// <param name="_Source"> + /// The source array of 8 bytes in a vector of two uint values. + /// </param> + /// <param name="_Accum"> + /// A vector of 4 values to be added to the masked sum of absolute + /// differences of the different byte alignments between the reference + /// value and the source value. + /// </param> + /// <returns> + /// Returns a vector of 4 sums. Each sum corresponds to the masked sum + /// of absolute differences of different byte alignments between the reference + /// value and the source value. + /// </returns> + inline uint4 msad4(uint _Reference, uint2 _Source, uint4 _Accum) __GPU_ONLY + { + uint4 _Tmp; + __dp_d3d_msad4(reinterpret_cast<uint*>(&_Tmp), _Reference, _Source.x, _Source.y, _Accum.x, _Accum.y, _Accum.z, _Accum.w); + return _Tmp; + } +} // namespace direct3d + +} //namespace graphics +} //namespace Concurrency + + + diff --git a/test_data/lots_of_files/amp_math.h b/test_data/lots_of_files/amp_math.h new file mode 100644 index 0000000..359a97a --- /dev/null +++ b/test_data/lots_of_files/amp_math.h @@ -0,0 +1,4351 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* amp_math.h +* +* C++ AMP Math Library +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include <cmath> +#include <amprt.h> + +#define _AMP_MATH_H + +extern "C" +{ + +//============================================================================= +// fast_math intrinsics. +//============================================================================= +float __dp_d3d_absf(float) __GPU_ONLY; +float __dp_d3d_acosf(float) __GPU_ONLY; +float __dp_d3d_asinf(float) __GPU_ONLY; +float __dp_d3d_atanf(float) __GPU_ONLY; +float __dp_d3d_atan2f(float, float) __GPU_ONLY; +float __dp_d3d_ceilf(float) __GPU_ONLY; +float __dp_d3d_cosf(float) __GPU_ONLY; +float __dp_d3d_coshf(float) __GPU_ONLY; +float __dp_d3d_expf(float) __GPU_ONLY; +float __dp_d3d_exp2f(float) __GPU_ONLY; +float __dp_d3d_floorf(float) __GPU_ONLY; +float __dp_d3d_fmodf(float, float) __GPU_ONLY; +float __dp_d3d_fracf(float) __GPU_ONLY; +float __dp_d3d_frexpf(float, _Out_ float *) __GPU_ONLY; +int __dp_d3d_isfinitef(float)__GPU_ONLY; +int __dp_d3d_isinff(float) __GPU_ONLY; +int __dp_d3d_isnanf(float) __GPU_ONLY; +float __dp_d3d_ldexpf(float, float) __GPU_ONLY; +float __dp_d3d_logf(float) __GPU_ONLY; +float __dp_d3d_log10f(float) __GPU_ONLY; +float __dp_d3d_log2f(float) __GPU_ONLY; +float __dp_d3d_maxf(float, float) __GPU_ONLY; +float __dp_d3d_minf(float, float) __GPU_ONLY; +float __dp_d3d_modff(float, _Out_ float *) __GPU_ONLY; +float __dp_d3d_powf(float, float) __GPU_ONLY; +float __dp_d3d_roundf(float) __GPU_ONLY; +float __dp_d3d_rsqrtf(float) __GPU_ONLY; +int __dp_d3d_signf(float) __GPU_ONLY; +float __dp_d3d_sincosf(float, _Out_ float *) __GPU_ONLY; +float __dp_d3d_sinf(float) __GPU_ONLY; +float __dp_d3d_sinhf(float) __GPU_ONLY; +float __dp_d3d_sqrtf(float) __GPU_ONLY; +float __dp_d3d_tanf(float) __GPU_ONLY; +float __dp_d3d_tanhf(float) __GPU_ONLY; +float __dp_d3d_truncf(float) __GPU_ONLY; + +//============================================================================= +// Single-precision precise_math intrinsics. +//============================================================================= +float __dp_math_acosf(float) __GPU_ONLY; +float __dp_math_acoshf(float) __GPU_ONLY; +float __dp_math_asinf(float) __GPU_ONLY; +float __dp_math_asinhf(float) __GPU_ONLY; +float __dp_math_atanf(float) __GPU_ONLY; +float __dp_math_atan2f(float, float) __GPU_ONLY; +float __dp_math_atanhf(float) __GPU_ONLY; +float __dp_math_cbrtf(float) __GPU_ONLY; +float __dp_math_ceilf(float) __GPU_ONLY; +float __dp_math_copysignf(float, float) __GPU_ONLY; +float __dp_math_cosf(float) __GPU_ONLY; +float __dp_math_coshf(float) __GPU_ONLY; +float __dp_math_cospif(float) __GPU_ONLY; +float __dp_math_erff(float) __GPU_ONLY; +float __dp_math_erfcf(float) __GPU_ONLY; +float __dp_math_erfinvf(float) __GPU_ONLY; +float __dp_math_erfcinvf(float) __GPU_ONLY; +float __dp_math_expf(float) __GPU_ONLY; +float __dp_math_exp2f(float) __GPU_ONLY; +float __dp_math_exp10f(float) __GPU_ONLY; +float __dp_math_expm1f(float) __GPU_ONLY; +float __dp_math_fabsf(float) __GPU_ONLY; +float __dp_math_fdimf(float, float) __GPU_ONLY; +float __dp_math_floorf(float) __GPU_ONLY; +float __dp_math_fmaf(float, float, float) __GPU_ONLY; +float __dp_math_fmaxf(float, float) __GPU_ONLY; +float __dp_math_fminf(float, float) __GPU_ONLY; +float __dp_math_fmodf(float, float) __GPU_ONLY; +int __dp_math_fpclassifyf(float) __GPU_ONLY; +float __dp_math_frexpf(float, _Out_ int *) __GPU_ONLY; +float __dp_math_hypotf(float, float) __GPU_ONLY; +int __dp_math_ilogbf(float) __GPU_ONLY; +int __dp_math_isfinitef(float) __GPU_ONLY; +int __dp_math_isinff(float) __GPU_ONLY; +int __dp_math_isnanf(float) __GPU_ONLY; +int __dp_math_isnormalf(float) __GPU_ONLY; +float __dp_math_ldexpf(float, int) __GPU_ONLY; +float __dp_math_lgammaf(float, _Out_ int *) __GPU_ONLY; +float __dp_math_logf(float) __GPU_ONLY; +float __dp_math_log10f(float) __GPU_ONLY; +float __dp_math_log2f(float) __GPU_ONLY; +float __dp_math_log1pf(float) __GPU_ONLY; +float __dp_math_logbf(float) __GPU_ONLY; +float __dp_math_modff(float, _Out_ float *) __GPU_ONLY; +float __dp_math_nanf(int) __GPU_ONLY; +float __dp_math_nearbyintf(float) __GPU_ONLY; +float __dp_math_nextafterf(float, float) __GPU_ONLY; +float __dp_math_phif(float) __GPU_ONLY; +float __dp_math_powf(float, float) __GPU_ONLY; +float __dp_math_probitf(float) __GPU_ONLY; +float __dp_math_rcbrtf(float) __GPU_ONLY; +float __dp_math_remainderf(float, float) __GPU_ONLY; +float __dp_math_remquof(float, float, _Out_ int *) __GPU_ONLY; +float __dp_math_roundf(float) __GPU_ONLY; +float __dp_math_rsqrtf(float) __GPU_ONLY; +float __dp_math_scalbf(float, float) __GPU_ONLY; +float __dp_math_scalbnf(float, int) __GPU_ONLY; +int __dp_math_signbitf(float) __GPU_ONLY; +float __dp_math_sinf(float) __GPU_ONLY; +float __dp_math_sincosf(float, _Out_ float *) __GPU_ONLY; +float __dp_math_sinpif(float) __GPU_ONLY; +float __dp_math_sinhf(float) __GPU_ONLY; +float __dp_math_sqrtf(float) __GPU_ONLY; +float __dp_math_tanf(float) __GPU_ONLY; +float __dp_math_tanhf(float) __GPU_ONLY; +float __dp_math_tanpif(float) __GPU_ONLY; +float __dp_math_tgammaf(float) __GPU_ONLY; +float __dp_math_truncf(float) __GPU_ONLY; + +//============================================================================= +// Double-precision precise_math intrinsics. +//============================================================================= +double __dp_math_acos(double) __GPU_ONLY; +double __dp_math_acosh(double) __GPU_ONLY; +double __dp_math_asin(double) __GPU_ONLY; +double __dp_math_asinh(double) __GPU_ONLY; +double __dp_math_atan(double) __GPU_ONLY; +double __dp_math_atan2(double, double) __GPU_ONLY; +double __dp_math_atanh(double) __GPU_ONLY; +double __dp_math_cbrt(double) __GPU_ONLY; +double __dp_math_ceil(double) __GPU_ONLY; +double __dp_math_copysign(double, double) __GPU_ONLY; +double __dp_math_cos(double) __GPU_ONLY; +double __dp_math_cosh(double) __GPU_ONLY; +double __dp_math_cospi(double) __GPU_ONLY; +double __dp_math_erf(double) __GPU_ONLY; +double __dp_math_erfc(double) __GPU_ONLY; +double __dp_math_erfinv(double) __GPU_ONLY; +double __dp_math_erfcinv(double) __GPU_ONLY; +double __dp_math_exp(double) __GPU_ONLY; +double __dp_math_exp2(double) __GPU_ONLY; +double __dp_math_exp10(double) __GPU_ONLY; +double __dp_math_expm1(double) __GPU_ONLY; +double __dp_math_fabs(double) __GPU_ONLY; +double __dp_math_fdim(double, double) __GPU_ONLY; +double __dp_math_floor(double) __GPU_ONLY; +double __dp_math_fma(double, double, double) __GPU_ONLY; +double __dp_math_fmax(double, double) __GPU_ONLY; +double __dp_math_fmin(double, double) __GPU_ONLY; +double __dp_math_fmod(double, double) __GPU_ONLY; +int __dp_math_fpclassify(double) __GPU_ONLY; +double __dp_math_frexp(double, _Out_ int *) __GPU_ONLY; +double __dp_math_hypot(double, double) __GPU_ONLY; +int __dp_math_ilogb(double) __GPU_ONLY; +int __dp_math_isfinite(double) __GPU_ONLY; +int __dp_math_isinf(double) __GPU_ONLY; +int __dp_math_isnan(double) __GPU_ONLY; +int __dp_math_isnormal(double) __GPU_ONLY; +double __dp_math_ldexp(double, int) __GPU_ONLY; +double __dp_math_lgamma(double, _Out_ int *) __GPU_ONLY; +double __dp_math_log(double) __GPU_ONLY; +double __dp_math_log10(double) __GPU_ONLY; +double __dp_math_log2(double) __GPU_ONLY; +double __dp_math_log1p(double) __GPU_ONLY; +double __dp_math_logb(double) __GPU_ONLY; +double __dp_math_modf(double, _Out_ double *) __GPU_ONLY; +double __dp_math_nan(int) __GPU_ONLY; +double __dp_math_nearbyint(double) __GPU_ONLY; +double __dp_math_nextafter(double, double) __GPU_ONLY; +double __dp_math_phi(double) __GPU_ONLY; +double __dp_math_pow(double, double) __GPU_ONLY; +double __dp_math_probit(double) __GPU_ONLY; +double __dp_math_rcbrt(double) __GPU_ONLY; +double __dp_math_remainder(double, double) __GPU_ONLY; +double __dp_math_remquo(double, double, _Out_ int *) __GPU_ONLY; +double __dp_math_round(double) __GPU_ONLY; +double __dp_math_rsqrt(double) __GPU_ONLY; +double __dp_math_scalb(double, double) __GPU_ONLY; +double __dp_math_scalbn(double, int) __GPU_ONLY; +int __dp_math_signbit(double) __GPU_ONLY; +double __dp_math_sin(double) __GPU_ONLY; +double __dp_math_sincos(double, _Out_ double *) __GPU_ONLY; +double __dp_math_sinpi(double) __GPU_ONLY; +double __dp_math_sinh(double) __GPU_ONLY; +double __dp_math_sqrt(double) __GPU_ONLY; +double __dp_math_tan(double) __GPU_ONLY; +double __dp_math_tanh(double) __GPU_ONLY; +double __dp_math_tanpi(double) __GPU_ONLY; +double __dp_math_tgamma(double) __GPU_ONLY; +double __dp_math_trunc(double) __GPU_ONLY; +} + +namespace Concurrency +{ +/// <summary> +/// Functions in the fast_math namespace have lower accuracy, and support +/// only single-precision float. +/// </summary> +namespace fast_math +{ + /// <summary> + /// Returns the absolute value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the absolute value of the argument + /// </returns> + inline float fabsf(float _X) __GPU_ONLY + { + return __dp_d3d_absf(_X); + } + + /// <summary> + /// Returns the absolute value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the absolute value of the argument + /// </returns> + inline float fabs(float _X) __GPU_ONLY + { + return __dp_d3d_absf(_X); + } + + /// <summary> + /// Calculates the arccosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arccosine value of the argument + /// </returns> + inline float acosf(float _X) __GPU_ONLY + { + return __dp_d3d_acosf(_X); + } + + /// <summary> + /// Calculates the arccosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arccosine value of the argument + /// </returns> + inline float acos(float _X) __GPU_ONLY + { + return __dp_d3d_acosf(_X); + } + + /// <summary> + /// Calculates the arcsine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arcsine value of the argument + /// </returns> + inline float asinf(float _X) __GPU_ONLY + { + return __dp_d3d_asinf(_X); + } + + /// <summary> + /// Calculates the arcsine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arcsine value of the argument + /// </returns> + inline float asin(float _X) __GPU_ONLY + { + return __dp_d3d_asinf(_X); + } + + /// <summary> + /// Calculates the arctangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of the argument + /// </returns> + inline float atanf(float _X) __GPU_ONLY + { + return __dp_d3d_atanf(_X); + } + + /// <summary> + /// Calculates the arctangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of the argument + /// </returns> + inline float atan(float _X) __GPU_ONLY + { + return __dp_d3d_atanf(_X); + } + + /// <summary> + /// Calculates the arctangent of _Y/_X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of _Y/_X + /// </returns> + inline float atan2f(float _Y, float _X) __GPU_ONLY + { + return __dp_d3d_atan2f(_Y, _X); + } + + /// <summary> + /// Calculates the arctangent of _Y/_X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of _Y/_X + /// </returns> + inline float atan2(float _Y, float _X) __GPU_ONLY + { + return __dp_d3d_atan2f(_Y, _X); + } + + /// <summary> + /// Calculates the ceiling of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the ceiling of the argument + /// </returns> + inline float ceilf(float _X) __GPU_ONLY + { + return __dp_d3d_ceilf(_X); + } + + /// <summary> + /// Calculates the ceiling of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the ceiling of the argument + /// </returns> + inline float ceil(float _X) __GPU_ONLY + { + return __dp_d3d_ceilf(_X); + } + + /// <summary> + /// Calculates the cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of the argument + /// </returns> + inline float cosf(float _X) __GPU_ONLY + { + return __dp_d3d_cosf(_X); + } + + /// <summary> + /// Calculates the cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of the argument + /// </returns> + inline float cos(float _X) __GPU_ONLY + { + return __dp_d3d_cosf(_X); + } + + /// <summary> + /// Calculates the hyperbolic cosine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic cosine value of the argument + /// </returns> + inline float coshf(float _X) __GPU_ONLY + { + return __dp_d3d_coshf(_X); + } + + /// <summary> + /// Calculates the hyperbolic cosine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic cosine value of the argument + /// </returns> + inline float cosh(float _X) __GPU_ONLY + { + return __dp_d3d_coshf(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument + /// </returns> + inline float expf(float _X) __GPU_ONLY + { + return __dp_d3d_expf(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument + /// </returns> + inline float exp(float _X) __GPU_ONLY + { + return __dp_d3d_expf(_X); + } + + /// <summary> + /// Calculates the base-2 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-2 exponential of the argument + /// </returns> + inline float exp2f(float _X) __GPU_ONLY + { + return __dp_d3d_exp2f(_X); + } + + /// <summary> + /// Calculates the base-2 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-2 exponential of the argument + /// </returns> + inline float exp2(float _X) __GPU_ONLY + { + return __dp_d3d_exp2f(_X); + } + + /// <summary> + /// Calculates the floor of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floor of the argument + /// </returns> + inline float floorf(float _X) __GPU_ONLY + { + return __dp_d3d_floorf(_X); + } + + /// <summary> + /// Calculates the floor of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floor of the argument + /// </returns> + inline float floor(float _X) __GPU_ONLY + { + return __dp_d3d_floorf(_X); + } + + /// <summary> + /// Determine the maximum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the maximum numeric value of the arguments + /// </returns> + inline float fmaxf(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_maxf(_X, _Y); + } + + /// <summary> + /// Determine the maximum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the maximum numeric value of the arguments + /// </returns> + inline float fmax(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_maxf(_X, _Y); + } + + /// <summary> + /// Determine the minimum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the minimum numeric value of the arguments + /// </returns> + inline float fminf(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_minf(_X, _Y); + } + + /// <summary> + /// Determine the minimum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the minimum numeric value of the arguments + /// </returns> + inline float fmin(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_minf(_X, _Y); + } + + /// <summary> + /// Calculates the floating-point remainder of _X/_Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floating-point remainder of _X/_Y + /// </returns> + inline float fmodf(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_fmodf(_X, _Y); + } + + /// <summary> + /// Calculates the floating-point remainder of _X/_Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floating-point remainder of _X/_Y + /// </returns> + inline float fmod(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_fmodf(_X, _Y); + } + + /// <summary> + /// Gets the mantissa and exponent of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Exp"> + /// Returns the integer exponent of _X in floating-point value + /// </param> + /// <returns> + /// Returns the mantissa _X + /// </returns> + inline float frexpf(float _X, _Out_ int * _Exp) __GPU_ONLY + { + float _FExp = 0.0f; + float _M = __dp_d3d_frexpf(_X, &_FExp); + *_Exp = static_cast<int>(_FExp); + // Currently, the mantissa returned by d3d's frexp is always positive + // Fetch the sign bit from _X to match cmath frexp + *reinterpret_cast<unsigned int*>(&_M) = *reinterpret_cast<unsigned int*>(&_M) | (*reinterpret_cast<unsigned int*>(&_X) & 0x80000000); + return _M; + } + + /// <summary> + /// Gets the mantissa and exponent of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Exp"> + /// Returns the integer exponent of _X in floating-point value + /// </param> + /// <returns> + /// Returns the mantissa _X + /// </returns> + inline float frexp(float _X, _Out_ int * _Exp) __GPU_ONLY + { + return frexpf(_X, _Exp); + } + + /// <summary> + /// Determines whether the argument has a finite value + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a finite value + /// </returns> + inline int isfinite(float _X) __GPU_ONLY + { + return __dp_d3d_isfinitef(_X); + } + + /// <summary> + /// Determines whether the argument is an infinity + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has an infinite value + /// </returns> + inline int isinf(float _X) __GPU_ONLY + { + return __dp_d3d_isinff(_X); + } + + /// <summary> + /// Determines whether the argument is a NaN + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a NaN value + /// </returns> + inline int isnan(float _X) __GPU_ONLY + { + return __dp_d3d_isnanf(_X); + } + + /// <summary> + /// Computes a real number from the mantissa and exponent + /// </summary> + /// <param name="_X"> + /// Floating-point value, mantissa + /// </param> + /// <param name="_Exp"> + /// Integer value, exponent + /// </param> + /// <returns> + /// Returns _X * 2^_Exp + /// </returns> + inline float ldexpf(float _X, int _Exp) __GPU_ONLY + { + float _FExp = static_cast<float>(_Exp); + return __dp_d3d_ldexpf(_X, _FExp); + } + + /// <summary> + /// Computes a real number from the mantissa and exponent + /// </summary> + /// <param name="_X"> + /// Floating-point value, mantissa + /// </param> + /// <param name="_Exp"> + /// Integer value, exponent + /// </param> + /// <returns> + /// Returns _X * 2^_Exp + /// </returns> + inline float ldexp(float _X, int _Exp) __GPU_ONLY + { + return ldexpf(_X, _Exp); + } + + /// <summary> + /// Calculates the base-e logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of the argument + /// </returns> + inline float logf(float _X) __GPU_ONLY + { + return __dp_d3d_logf(_X); + } + + /// <summary> + /// Calculates the base-e logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of the argument + /// </returns> + inline float log(float _X) __GPU_ONLY + { + return __dp_d3d_logf(_X); + } + + /// <summary> + /// Calculates the base-10 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline float log10f(float _X) __GPU_ONLY + { + return __dp_d3d_log10f(_X); + } + + /// <summary> + /// Calculates the base-10 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline float log10(float _X) __GPU_ONLY + { + return __dp_d3d_log10f(_X); + } + + /// <summary> + /// Calculates the base-2 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-2 logarithm of the argument + /// </returns> + inline float log2f(float _X) __GPU_ONLY + { + return __dp_d3d_log2f(_X); + } + + /// <summary> + /// Calculates the base-2 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-2 logarithm of the argument + /// </returns> + inline float log2(float _X) __GPU_ONLY + { + return __dp_d3d_log2f(_X); + } + + /// <summary> + /// Splits _X into fractional and integer parts. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Ip"> + /// Returns the integer portion of _X in floating-point value + /// </param> + /// <returns> + /// Returns the signed fractional portion of _X + /// </returns> + inline float modff(float _X, float * _Ip) __GPU_ONLY + { + return __dp_d3d_modff(_X, _Ip); + } + + /// <summary> + /// Splits _X into fractional and integer parts. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Ip"> + /// Returns the integer portion of _X in floating-point value + /// </param> + /// <returns> + /// Returns the signed fractional portion of _X + /// </returns> + inline float modf(float _X, float * _Ip) __GPU_ONLY + { + return __dp_d3d_modff(_X, _Ip); + } + + /// <summary> + /// Calculates _X raised to the power of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value, base + /// </param> + /// <param name="_Y"> + /// Floating-point value, exponent + /// </param> + /// <returns> + /// Returns the value of _X raised to the power of _Y + /// </returns> + inline float powf(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_powf(_X, _Y); + } + + /// <summary> + /// Calculates _X raised to the power of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value, base + /// </param> + /// <param name="_Y"> + /// Floating-point value, exponent + /// </param> + /// <returns> + /// Returns the value of _X raised to the power of _Y + /// </returns> + inline float pow(float _X, float _Y) __GPU_ONLY + { + return __dp_d3d_powf(_X, _Y); + } + + /// <summary> + /// Rounds _X to the nearest integer + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the nearest integer of _X + /// </returns> + inline float roundf(float _X) __GPU_ONLY + { + return __dp_d3d_roundf(_X); + } + + /// <summary> + /// Rounds _X to the nearest integer + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the nearest integer of _X + /// </returns> + inline float round(float _X) __GPU_ONLY + { + return __dp_d3d_roundf(_X); + } + + /// <summary> + /// Returns the reciprocal of the square root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the square root of the argument + /// </returns> + inline float rsqrtf(float _X) __GPU_ONLY + { + return __dp_d3d_rsqrtf(_X); + } + + /// <summary> + /// Returns the reciprocal of the square root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the square root of the argument + /// </returns> + inline float rsqrt(float _X) __GPU_ONLY + { + return __dp_d3d_rsqrtf(_X); + } + + /// <summary> + /// Returns the sign of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sign of the argument + /// </returns> + inline int signbitf(float _X) __GPU_ONLY + { + return __dp_d3d_signf(_X); + } + + /// <summary> + /// Returns the sign of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sign of the argument + /// </returns> + inline int signbit(float _X) __GPU_ONLY + { + return __dp_d3d_signf(_X); + } + + /// <summary> + /// Calculates the sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of the argument + /// </returns> + inline float sinf(float _X) __GPU_ONLY + { + return __dp_d3d_sinf(_X); + } + + /// <summary> + /// Calculates the sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of the argument + /// </returns> + inline float sin(float _X) __GPU_ONLY + { + return __dp_d3d_sinf(_X); + } + + /// <summary> + /// Calculates sine and cosine value of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_S"> + /// Returns the sine value of _X + /// </param> + /// <param name="_C"> + /// Returns the cosine value of _X + /// </param> + inline void sincosf(float _X, float * _S, float * _C) __GPU_ONLY + { + *_C = __dp_d3d_sincosf(_X, _S); + } + + /// <summary> + /// Calculates sine and cosine value of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_S"> + /// Returns the sine value of _X + /// </param> + /// <param name="_C"> + /// Returns the cosine value of _X + /// </param> + inline void sincos(float _X, float * _S, float * _C) __GPU_ONLY + { + *_C = __dp_d3d_sincosf(_X, _S); + } + + /// <summary> + /// Calculates the hyperbolic sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic sine value of the argument + /// </returns> + inline float sinhf(float _X) __GPU_ONLY + { + return __dp_d3d_sinhf(_X); + } + + /// <summary> + /// Calculates the hyperbolic sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic sine value of the argument + /// </returns> + inline float sinh(float _X) __GPU_ONLY + { + return __dp_d3d_sinhf(_X); + } + + /// <summary> + /// Calculates the squre root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the squre root of the argument + /// </returns> + inline float sqrtf(float _X) __GPU_ONLY + { + return __dp_d3d_sqrtf(_X); + } + + /// <summary> + /// Calculates the squre root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the squre root of the argument + /// </returns> + inline float sqrt(float _X) __GPU_ONLY + { + return __dp_d3d_sqrtf(_X); + } + + /// <summary> + /// Calculates the tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of the argument + /// </returns> + inline float tanf(float _X) __GPU_ONLY + { + return __dp_d3d_tanf(_X); + } + + /// <summary> + /// Calculates the tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of the argument + /// </returns> + inline float tan(float _X) __GPU_ONLY + { + return __dp_d3d_tanf(_X); + } + + /// <summary> + /// Calculates the hyperbolic tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic tangent value of the argument + /// </returns> + inline float tanhf(float _X) __GPU_ONLY + { + return __dp_d3d_tanhf(_X); + } + /// <summary> + /// Calculates the hyperbolic tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic tangent value of the argument + /// </returns> + inline float tanh(float _X) __GPU_ONLY + { + return __dp_d3d_tanhf(_X); + } + + /// <summary> + /// Truncates the argument to the integer component + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the integer component of the argument + /// </returns> + inline float truncf(float _X) __GPU_ONLY + { + return __dp_d3d_truncf(_X); + } + + /// <summary> + /// Truncates the argument to the integer component + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the integer component of the argument + /// </returns> + inline float trunc(float _X) __GPU_ONLY + { + return __dp_d3d_truncf(_X); + } + + //============================================================================= + // Import CMATH C++ functions: + //============================================================================= + using std::acosf; + using std::asinf; + using std::atanf; + using std::atan2f; + using std::ceilf; + using std::cosf; + using std::coshf; + using std::expf; + using std::fabsf; + using std::floorf; + using std::fmodf; + using std::frexpf; + using std::ldexpf; + using std::logf; + using std::log10f; + using std::modff; + using std::powf; + using std::sinf; + using std::sinhf; + using std::sqrtf; + using std::tanf; + using std::tanhf; + + using std::acos; + using std::asin; + using std::atan; + using std::atan2; + using std::ceil; + using std::cos; + using std::cosh; + using std::exp; + using std::fabs; + using std::floor; + using std::fmod; + using std::frexp; + using std::ldexp; + using std::log; + using std::log10; + using std::modf; + using std::pow; + using std::sin; + using std::sinh; + using std::sqrt; + using std::tan; + using std::tanh; + using std::exp2; + using std::exp2f; + using std::fmax; + using std::fmaxf; + using std::fmin; + using std::fminf; + using std::log2; + using std::log2f; + using std::round; + using std::roundf; + using std::trunc; + using std::truncf; + +} // namespace fast_math + + +/// <summary> +/// Functions in the precise_math namespace have higher accuracy, but require +/// double-precision support, which not all accelerators do. +/// </summary> +namespace precise_math +{ + /// <summary> + /// Calculates the arccosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arccosine value of the argument + /// </returns> + inline float acosf(float _X) __GPU_ONLY + { + return __dp_math_acosf(_X); + } + + /// <summary> + /// Calculates the arccosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arccosine value of the argument + /// </returns> + inline float acos(float _X) __GPU_ONLY + { + return __dp_math_acosf(_X); + } + + /// <summary> + /// Calculates the arccosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arccosine value of the argument + /// </returns> + inline double acos(double _X) __GPU_ONLY + { + return __dp_math_acos(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic cosine value of the argument + /// </returns> + inline float acoshf(float _X) __GPU_ONLY + { + return __dp_math_acoshf(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic cosine value of the argument + /// </returns> + inline float acosh(float _X) __GPU_ONLY + { + return __dp_math_acoshf(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic cosine value of the argument + /// </returns> + inline double acosh(double _X) __GPU_ONLY + { + return __dp_math_acosh(_X); + } + + + /// <summary> + /// Calculates the arcsine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arcsine value of the argument + /// </returns> + inline float asinf(float _X) __GPU_ONLY + { + return __dp_math_asinf(_X); + } + + /// <summary> + /// Calculates the arcsine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arcsine value of the argument + /// </returns> + inline float asin(float _X) __GPU_ONLY + { + return __dp_math_asinf(_X); + } + + /// <summary> + /// Calculates the arcsine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arcsine value of the argument + /// </returns> + inline double asin(double _X) __GPU_ONLY + { + return __dp_math_asin(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic sine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic sine value of the argument + /// </returns> + inline float asinhf(float _X) __GPU_ONLY + { + return __dp_math_asinhf(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic sine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic sine value of the argument + /// </returns> + inline float asinh(float _X) __GPU_ONLY + { + return __dp_math_asinhf(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic sine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic sine value of the argument + /// </returns> + inline double asinh(double _X) __GPU_ONLY + { + return __dp_math_asinh(_X); + } + + /// <summary> + /// Calculates the arctangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of the argument + /// </returns> + inline float atanf(float _X) __GPU_ONLY + { + return __dp_math_atanf(_X); + } + + /// <summary> + /// Calculates the arctangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of the argument + /// </returns> + inline float atan(float _X) __GPU_ONLY + { + return __dp_math_atanf(_X); + } + + /// <summary> + /// Calculates the arctangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of the argument + /// </returns> + inline double atan(double _X) __GPU_ONLY + { + return __dp_math_atan(_X); + } + + /// <summary> + /// Calculates the arctangent of _Y/_X + /// </summary> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of _Y/_X + /// </returns> + inline float atan2f(float _Y, float _X) __GPU_ONLY + { + return __dp_math_atan2f(_Y, _X); + } + + /// <summary> + /// Calculates the arctangent of _Y/_X + /// </summary> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of _Y/_X + /// </returns> + inline float atan2(float _Y, float _X) __GPU_ONLY + { + return __dp_math_atan2f(_Y, _X); + } + + /// <summary> + /// Calculates the arctangent of _Y/_X + /// </summary> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the arctangent value of _Y/_X + /// </returns> + inline double atan2(double _Y, double _X) __GPU_ONLY + { + return __dp_math_atan2(_Y, _X); + } + + + /// <summary> + /// Calculates the inverse hyperbolic tangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic tangent value of the argument + /// </returns> + inline float atanhf(float _X) __GPU_ONLY + { + return __dp_math_atanhf(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic tangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic tangent value of the argument + /// </returns> + inline float atanh(float _X) __GPU_ONLY + { + return __dp_math_atanhf(_X); + } + + /// <summary> + /// Calculates the inverse hyperbolic tangent of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse hyperbolic tangent value of the argument + /// </returns> + inline double atanh(double _X) __GPU_ONLY + { + return __dp_math_atanh(_X); + } + + /// <summary> + /// Computes the real cube root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the real cube root of the argument + /// </returns> + inline float cbrtf(float _X) __GPU_ONLY + { + return __dp_math_cbrtf(_X); + } + + /// <summary> + /// Computes the real cube root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the real cube root of the argument + /// </returns> + inline float cbrt(float _X) __GPU_ONLY + { + return __dp_math_cbrtf(_X); + } + + /// <summary> + /// Computes the real cube root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the real cube root of the argument + /// </returns> + inline double cbrt(double _X) __GPU_ONLY + { + return __dp_math_cbrt(_X); + } + + /// <summary> + /// Calculates the ceiling of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the ceiling of the argument + /// </returns> + inline float ceilf(float _X) __GPU_ONLY + { + return __dp_math_ceilf(_X); + } + + /// <summary> + /// Calculates the ceiling of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the ceiling of the argument + /// </returns> + inline float ceil(float _X) __GPU_ONLY + { + return __dp_math_ceilf(_X); + } + + /// <summary> + /// Calculates the ceiling of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the ceiling of the argument + /// </returns> + inline double ceil(double _X) __GPU_ONLY + { + return __dp_math_ceil(_X); + } + + /// <summary> + /// Produces a value with the magnitude of _X and the sign of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a value with the magnitude of _X and the sign of _Y + /// </returns> + inline float copysignf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_copysignf(_X, _Y); + } + + /// <summary> + /// Produces a value with the magnitude of _X and the sign of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a value with the magnitude of _X and the sign of _Y + /// </returns> + inline float copysign(float _X, float _Y) __GPU_ONLY + { + return __dp_math_copysignf(_X, _Y); + } + + /// <summary> + /// Produces a value with the magnitude of _X and the sign of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a value with the magnitude of _X and the sign of _Y + /// </returns> + inline double copysign(double _X, double _Y) __GPU_ONLY + { + return __dp_math_copysign(_X, _Y); + } + + /// <summary> + /// Calculates the cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of the argument + /// </returns> + inline float cosf(float _X) __GPU_ONLY + { + return __dp_math_cosf(_X); + } + + /// <summary> + /// Calculates the cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of the argument + /// </returns> + inline float cos(float _X) __GPU_ONLY + { + return __dp_math_cosf(_X); + } + + /// <summary> + /// Calculates the cosine of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of the argument + /// </returns> + inline double cos(double _X) __GPU_ONLY + { + return __dp_math_cos(_X); + } + + /// <summary> + /// Calculates the hyperbolic cosine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic cosine value of the argument + /// </returns> + inline float coshf(float _X) __GPU_ONLY + { + return __dp_math_coshf(_X); + } + + /// <summary> + /// Calculates the hyperbolic cosine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic cosine value of the argument + /// </returns> + inline float cosh(float _X) __GPU_ONLY + { + return __dp_math_coshf(_X); + } + + /// <summary> + /// Calculates the hyperbolic cosine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic cosine value of the argument + /// </returns> + inline double cosh(double _X) __GPU_ONLY + { + return __dp_math_cosh(_X); + } + + /// <summary> + /// Calculates the cosine value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of pi * _X + /// </returns> + inline float cospif(float _X) __GPU_ONLY + { + return __dp_math_cospif(_X); + } + + /// <summary> + /// Calculates the cosine value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of pi * _X + /// </returns> + inline float cospi(float _X) __GPU_ONLY + { + return __dp_math_cospif(_X); + } + + /// <summary> + /// Calculates the cosine value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cosine value of pi * _X + /// </returns> + inline double cospi(double _X) __GPU_ONLY + { + return __dp_math_cospi(_X); + } + + /// <summary> + /// Computes the error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the error function of _X + /// </returns> + inline float erff(float _X) __GPU_ONLY + { + return __dp_math_erff(_X); + } + + /// <summary> + /// Computes the error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the error function of _X + /// </returns> + inline float erf(float _X) __GPU_ONLY + { + return __dp_math_erff(_X); + } + + /// <summary> + /// Computes the error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the error function of _X + /// </returns> + inline double erf(double _X) __GPU_ONLY + { + return __dp_math_erf(_X); + } + + /// <summary> + /// Computes the complementary error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the complementary error function of _X + /// </returns> + inline float erfcf(float _X) __GPU_ONLY + { + return __dp_math_erfcf(_X); + } + + /// <summary> + /// Computes the complementary error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the complementary error function of _X + /// </returns> + inline float erfc(float _X) __GPU_ONLY + { + return __dp_math_erfcf(_X); + } + + /// <summary> + /// Computes the complementary error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the complementary error function of _X + /// </returns> + inline double erfc(double _X) __GPU_ONLY + { + return __dp_math_erfc(_X); + } + + /// <summary> + /// Computes the inverse error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse error function of _X + /// </returns> + inline float erfinvf(float _X) __GPU_ONLY + { + return __dp_math_erfinvf(_X); + } + + /// <summary> + /// Computes the inverse error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse error function of _X + /// </returns> + inline float erfinv(float _X) __GPU_ONLY + { + return __dp_math_erfinvf(_X); + } + + /// <summary> + /// Computes the inverse error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse error function of _X + /// </returns> + inline double erfinv(double _X) __GPU_ONLY + { + return __dp_math_erfinv(_X); + } + + /// <summary> + /// Computes the inverse complementary error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse complementary error function of _X + /// </returns> + inline float erfcinvf(float _X) __GPU_ONLY + { + return __dp_math_erfcinvf(_X); + } + + /// <summary> + /// Computes the inverse complementary error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse complementary error function of _X + /// </returns> + inline float erfcinv(float _X) __GPU_ONLY + { + return __dp_math_erfcinvf(_X); + } + + /// <summary> + /// Computes the inverse complementary error function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse complementary error function of _X + /// </returns> + inline double erfcinv(double _X) __GPU_ONLY + { + return __dp_math_erfcinv(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument + /// </returns> + inline float expf(float _X) __GPU_ONLY + { + return __dp_math_expf(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument + /// </returns> + inline float exp(float _X) __GPU_ONLY + { + return __dp_math_expf(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument + /// </returns> + inline double exp(double _X) __GPU_ONLY + { + return __dp_math_exp(_X); + } + + /// <summary> + /// Calculates the base-2 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-2 exponential of the argument + /// </returns> + inline float exp2f(float _X) __GPU_ONLY + { + return __dp_math_exp2f(_X); + } + + /// <summary> + /// Calculates the base-2 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-2 exponential of the argument + /// </returns> + inline float exp2(float _X) __GPU_ONLY + { + return __dp_math_exp2f(_X); + } + + /// <summary> + /// Calculates the base-2 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-2 exponential of the argument + /// </returns> + inline double exp2(double _X) __GPU_ONLY + { + return __dp_math_exp2(_X); + } + + /// <summary> + /// Calculates the base-10 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 exponential of the argument + /// </returns> + inline float exp10f(float _X) __GPU_ONLY + { + return __dp_math_exp10f(_X); + } + + /// <summary> + /// Calculates the base-10 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 exponential of the argument + /// </returns> + inline float exp10(float _X) __GPU_ONLY + { + return __dp_math_exp10f(_X); + } + + /// <summary> + /// Calculates the base-10 exponential of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 exponential of the argument + /// </returns> + inline double exp10(double _X) __GPU_ONLY + { + return __dp_math_exp10(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument, minus 1 + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument, minus 1 + /// </returns> + inline float expm1f(float _X) __GPU_ONLY + { + return __dp_math_expm1f(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument, minus 1 + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument, minus 1 + /// </returns> + inline float expm1(float _X) __GPU_ONLY + { + return __dp_math_expm1f(_X); + } + + /// <summary> + /// Calculates the base-e exponential of the argument, minus 1 + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e exponential of the argument, minus 1 + /// </returns> + inline double expm1(double _X) __GPU_ONLY + { + return __dp_math_expm1(_X); + } + + /// <summary> + /// Returns the absolute value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the absolute value of the argument + /// </returns> + inline float fabsf(float _X) __GPU_ONLY + { + return __dp_math_fabsf(_X); + } + + /// <summary> + /// Returns the absolute value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the absolute value of the argument + /// </returns> + inline float fabs(float _X) __GPU_ONLY + { + return __dp_math_fabsf(_X); + } + + /// <summary> + /// Returns the absolute value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the absolute value of the argument + /// </returns> + inline double fabs(double _X) __GPU_ONLY + { + return __dp_math_fabs(_X); + } + + /// <summary> + /// Determines the positive difference between the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X - _Y if _X > _Y; +0, otherwise + /// </returns> + inline float fdimf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fdimf(_X, _Y); + } + + + /// <summary> + /// Determines the positive difference between the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X - _Y if _X > _Y; +0, otherwise + /// </returns> + inline float fdim(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fdimf(_X, _Y); + } + + + /// <summary> + /// Determines the positive difference between the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X - _Y if _X > _Y; +0, otherwise + /// </returns> + inline double fdim(double _X, double _Y) __GPU_ONLY + { + return __dp_math_fdim(_X, _Y); + } + + /// <summary> + /// Calculates the floor of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floor of the argument + /// </returns> + inline float floorf(float _X) __GPU_ONLY + { + return __dp_math_floorf(_X); + } + + /// <summary> + /// Calculates the floor of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floor of the argument + /// </returns> + inline float floor(float _X) __GPU_ONLY + { + return __dp_math_floorf(_X); + } + + /// <summary> + /// Calculates the floor of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floor of the argument + /// </returns> + inline double floor(double _X) __GPU_ONLY + { + return __dp_math_floor(_X); + } + + /// <summary> + /// Compute (_X * _Y) + _Z, rounded as one ternary operation + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_Z"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns (_X * _Y) + _Z, rounded as one ternary operation + /// </returns> + inline float fmaf(float _X, float _Y, float _Z) __GPU_ONLY + { + return __dp_math_fmaf(_X, _Y, _Z); + } + + /// <summary> + /// Compute (_X * _Y) + _Z, rounded as one ternary operation + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_Z"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns (_X * _Y) + _Z, rounded as one ternary operation + /// </returns> + inline float fma(float _X, float _Y, float _Z) __GPU_ONLY + { + return __dp_math_fmaf(_X, _Y, _Z); + } + + /// <summary> + /// Compute (_X * _Y) + _Z, rounded as one ternary operation + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_Z"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns (_X * _Y) + _Z, rounded as one ternary operation + /// </returns> + inline double fma(double _X, double _Y, double _Z) __GPU_ONLY + { + return __dp_math_fma(_X, _Y, _Z); + } + + /// <summary> + /// Determine the maximum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the maximum numeric value of the arguments + /// </returns> + inline float fmaxf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fmaxf(_X, _Y); + } + + /// <summary> + /// Determine the maximum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the maximum numeric value of the arguments + /// </returns> + inline float fmax(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fmaxf(_X, _Y); + } + + /// <summary> + /// Determine the maximum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the maximum numeric value of the arguments + /// </returns> + inline double fmax(double _X, double _Y) __GPU_ONLY + { + return __dp_math_fmax(_X, _Y); + } + + /// <summary> + /// Determine the minimum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the minimum numeric value of the arguments + /// </returns> + inline float fminf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fminf(_X, _Y); + } + + /// <summary> + /// Determine the minimum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the minimum numeric value of the arguments + /// </returns> + inline float fmin(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fminf(_X, _Y); + } + + /// <summary> + /// Determine the minimum numeric value of the arguments + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Return the minimum numeric value of the arguments + /// </returns> + inline double fmin(double _X, double _Y) __GPU_ONLY + { + return __dp_math_fmin(_X, _Y); + } + + /// <summary> + /// Calculates the floating-point remainder of _X/_Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floating-point remainder of _X/_Y + /// </returns> + inline float fmodf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fmodf(_X, _Y); + } + + /// <summary> + /// Calculates the floating-point remainder of _X/_Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floating-point remainder of _X/_Y + /// </returns> + inline float fmod(float _X, float _Y) __GPU_ONLY + { + return __dp_math_fmodf(_X, _Y); + } + + /// <summary> + /// Calculates the floating-point remainder of _X/_Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the floating-point remainder of _X/_Y + /// </returns> + inline double fmod(double _X, double _Y) __GPU_ONLY + { + return __dp_math_fmod(_X, _Y); + } + + + /// <summary> + /// Classifies the argument value as NaN, infinite, normal, subnormal, zero + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the value of the number classification macro appropriate to the value of the argument. + /// </returns> + inline int fpclassify(float _X) __GPU_ONLY + { + return __dp_math_fpclassifyf(_X); + } + + /// <summary> + /// Classifies the argument value as NaN, infinite, normal, subnormal, zero + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the value of the number classification macro appropriate to the value of the argument. + /// </returns> + inline int fpclassify(double _X) __GPU_ONLY + { + return __dp_math_fpclassify(_X); + } + + /// <summary> + /// Gets the mantissa and exponent of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Exp"> + /// Returns the integer exponent of _X in floating-point value + /// </param> + /// <returns> + /// Returns the mantissa _X + /// </returns> + inline float frexpf(float _X, _Out_ int * _Exp) __GPU_ONLY + { + return __dp_math_frexpf(_X, _Exp); + } + + /// <summary> + /// Gets the mantissa and exponent of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Exp"> + /// Returns the integer exponent of _X in floating-point value + /// </param> + /// <returns> + /// Returns the mantissa _X + /// </returns> + inline float frexp(float _X, _Out_ int * _Exp) __GPU_ONLY + { + return __dp_math_frexpf(_X, _Exp); + } + + /// <summary> + /// Gets the mantissa and exponent of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Exp"> + /// Returns the integer exponent of _X in floating-point value + /// </param> + /// <returns> + /// Returns the mantissa _X + /// </returns> + inline double frexp(double _X, _Out_ int * _Exp) __GPU_ONLY + { + return __dp_math_frexp(_X, _Exp); + } + + /// <summary> + /// Computes the square root of the sum of the squares of _X and _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the square root of the sum of the squares of _X and _Y + /// </returns> + inline float hypotf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_hypotf(_X, _Y); + } + + /// <summary> + /// Computes the square root of the sum of the squares of _X and _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the square root of the sum of the squares of _X and _Y + /// </returns> + inline float hypot(float _X, float _Y) __GPU_ONLY + { + return __dp_math_hypotf(_X, _Y); + } + + /// <summary> + /// Computes the square root of the sum of the squares of _X and _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the square root of the sum of the squares of _X and _Y + /// </returns> + inline double hypot(double _X, double _Y) __GPU_ONLY + { + return __dp_math_hypot(_X, _Y); + } + + /// <summary> + /// Extract the exponent of _X as a signed int value + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the exponent of _X as a signed int value + /// </returns> + inline int ilogbf(float _X) __GPU_ONLY + { + return __dp_math_ilogbf(_X); + } + + /// <summary> + /// Extract the exponent of _X as a signed int value + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the exponent of _X as a signed int value + /// </returns> + inline int ilogb(float _X) __GPU_ONLY + { + return __dp_math_ilogbf(_X); + } + + /// <summary> + /// Extract the exponent of _X as a signed int value + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the exponent of _X as a signed int value + /// </returns> + inline int ilogb(double _X) __GPU_ONLY + { + return __dp_math_ilogb(_X); + } + + /// <summary> + /// Determines whether the argument has a finite value + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a finite value + /// </returns> + inline int isfinite(float _X) __GPU_ONLY + { + return __dp_math_isfinitef(_X); + } + + /// <summary> + /// Determines whether the argument has a finite value + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a finite value + /// </returns> + inline int isfinite(double _X) __GPU_ONLY + { + return __dp_math_isfinite(_X); + } + + /// <summary> + /// Determines whether the argument is an infinity + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has an infinite value + /// </returns> + inline int isinf(float _X) __GPU_ONLY + { + return __dp_math_isinff(_X); + } + + /// <summary> + /// Determines whether the argument is an infinity + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has an infinite value + /// </returns> + inline int isinf(double _X) __GPU_ONLY + { + return __dp_math_isinf(_X); + } + + /// <summary> + /// Determines whether the argument is a NaN + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a NaN value + /// </returns> + inline int isnan(float _X) __GPU_ONLY + { + return __dp_math_isnanf(_X); + } + + /// <summary> + /// Determines whether the argument is a NaN + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a NaN value + /// </returns> + inline int isnan(double _X) __GPU_ONLY + { + return __dp_math_isnan(_X); + } + + /// <summary> + /// Determines whether the argument is a normal + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a normal value + /// </returns> + inline int isnormal(float _X) __GPU_ONLY + { + return __dp_math_isnormalf(_X); + } + + /// <summary> + /// Determines whether the argument is a normal + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the argument has a normal value + /// </returns> + inline int isnormal(double _X) __GPU_ONLY + { + return __dp_math_isnormal(_X); + } + + /// <summary> + /// Computes a real number from the mantissa and exponent + /// </summary> + /// <param name="_X"> + /// Floating-point value, mantissa + /// </param> + /// <param name="_Exp"> + /// Integer value, exponent + /// </param> + /// <returns> + /// Returns _X * 2^_Exp + /// </returns> + inline float ldexpf(float _X, int _Exp) __GPU_ONLY + { + return __dp_math_ldexpf(_X, _Exp); + } + + /// <summary> + /// Computes a real number from the mantissa and exponent + /// </summary> + /// <param name="_X"> + /// Floating-point value, mantissa + /// </param> + /// <param name="_Exp"> + /// Integer value, exponent + /// </param> + /// <returns> + /// Returns _X * 2^_Exp + /// </returns> + inline float ldexp(float _X, int _Exp) __GPU_ONLY + { + return __dp_math_ldexpf(_X, _Exp); + } + + /// <summary> + /// Computes a real number from the mantissa and exponent + /// </summary> + /// <param name="_X"> + /// Floating-point value, mantissa + /// </param> + /// <param name="_Exp"> + /// Integer value, exponent + /// </param> + /// <returns> + /// Returns _X * 2^_Exp + /// </returns> + inline double ldexp(double _X, int _Exp) __GPU_ONLY + { + return __dp_math_ldexp(_X, _Exp); + } + + /// <summary> + /// Computes the natural logarithm of the absolute value of gamma of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Sign"> + /// Returns the sign + /// </param> + /// <returns> + /// Returns the natural logarithm of the absolute value of gamma of the argument + /// </returns> + inline float lgammaf(float _X, _Out_ int * _Sign) __GPU_ONLY + { + return __dp_math_lgammaf(_X, _Sign); + } + + /// <summary> + /// Computes the natural logarithm of the absolute value of gamma of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Sign"> + /// Returns the sign + /// </param> + /// <returns> + /// Returns the natural logarithm of the absolute value of gamma of the argument + /// </returns> + inline float lgamma(float _X, _Out_ int * _Sign) __GPU_ONLY + { + return __dp_math_lgammaf(_X, _Sign); + } + + /// <summary> + /// Computes the natural logarithm of the absolute value of gamma of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Sign"> + /// Returns the sign + /// </param> + /// <returns> + /// Returns the natural logarithm of the absolute value of gamma of the argument + /// </returns> + inline double lgamma(double _X, _Out_ int * _Sign) __GPU_ONLY + { + return __dp_math_lgamma(_X, _Sign); + } + + /// <summary> + /// Calculates the base-e logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of the argument + /// </returns> + inline float logf(float _X) __GPU_ONLY + { + return __dp_math_logf(_X); + } + + /// <summary> + /// Calculates the base-e logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of the argument + /// </returns> + inline float log(float _X) __GPU_ONLY + { + return __dp_math_logf(_X); + } + + /// <summary> + /// Calculates the base-e logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of the argument + /// </returns> + inline double log(double _X) __GPU_ONLY + { + return __dp_math_log(_X); + } + + /// <summary> + /// Calculates the base-10 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline float log10f(float _X) __GPU_ONLY + { + return __dp_math_log10f(_X); + } + + /// <summary> + /// Calculates the base-10 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline float log10(float _X) __GPU_ONLY + { + return __dp_math_log10f(_X); + } + + /// <summary> + /// Calculates the base-10 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline double log10(double _X) __GPU_ONLY + { + return __dp_math_log10(_X); + } + + /// <summary> + /// Calculates the base-2 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline float log2f(float _X) __GPU_ONLY + { + return __dp_math_log2f(_X); + } + + /// <summary> + /// Calculates the base-2 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline float log2(float _X) __GPU_ONLY + { + return __dp_math_log2f(_X); + } + + /// <summary> + /// Calculates the base-2 logarithm of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-10 logarithm of the argument + /// </returns> + inline double log2(double _X) __GPU_ONLY + { + return __dp_math_log2(_X); + } + + /// <summary> + /// Calculates the base-e logarithm of 1 plus the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of 1 plus the argument + /// </returns> + inline float log1pf(float _X) __GPU_ONLY + { + return __dp_math_log1pf(_X); + } + + /// <summary> + /// Calculates the base-e logarithm of 1 plus the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of 1 plus the argument + /// </returns> + inline float log1p(float _X) __GPU_ONLY + { + return __dp_math_log1pf(_X); + } + + /// <summary> + /// Calculates the base-e logarithm of 1 plus the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the base-e logarithm of 1 plus the argument + /// </returns> + inline double log1p(double _X) __GPU_ONLY + { + return __dp_math_log1p(_X); + } + + /// <summary> + /// Extracts the exponent of _X, as a signed integer value in floating-point format + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the signed exponent of _X + /// </returns> + inline float logbf(float _X) __GPU_ONLY + { + return __dp_math_logbf(_X); + } + + /// <summary> + /// Extracts the exponent of _X, as a signed integer value in floating-point format + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the signed exponent of _X + /// </returns> + inline float logb(float _X) __GPU_ONLY + { + return __dp_math_logbf(_X); + } + + /// <summary> + /// Extracts the exponent of _X, as a signed integer value in floating-point format + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the signed exponent of _X + /// </returns> + inline double logb(double _X) __GPU_ONLY + { + return __dp_math_logb(_X); + } + + /// <summary> + /// Splits _X into fractional and integer parts. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Iptr"> + /// Returns the integer portion of _X in floating-point value + /// </param> + /// <returns> + /// Returns the signed fractional portion of _X + /// </returns> + inline float modff(float _X, _Out_ float * _Iptr) __GPU_ONLY + { + return __dp_math_modff(_X, _Iptr); + } + + /// <summary> + /// Splits _X into fractional and integer parts. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Iptr"> + /// Returns the integer portion of _X in floating-point value + /// </param> + /// <returns> + /// Returns the signed fractional portion of _X + /// </returns> + inline float modf(float _X, _Out_ float * _Iptr) __GPU_ONLY + { + return __dp_math_modff(_X, _Iptr); + } + + /// <summary> + /// Splits _X into fractional and integer parts. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Iptr"> + /// Returns the integer portion of _X in floating-point value + /// </param> + /// <returns> + /// Returns the signed fractional portion of _X + /// </returns> + inline double modf(double _X, _Out_ double * _Iptr) __GPU_ONLY + { + return __dp_math_modf(_X, _Iptr); + } + + /// <summary> + /// Returns a quiet NaN + /// </summary> + /// <param name="_X"> + /// Integer value + /// </param> + /// <returns> + /// Returns a quiet NaN, if available, with the content indicated in _X + /// </returns> + inline float nanf(int _X) __GPU_ONLY + { + return __dp_math_nanf(_X); + } + + /// <summary> + /// Returns a quiet NaN + /// </summary> + /// <param name="_X"> + /// Integer value + /// </param> + /// <returns> + /// Returns a quiet NaN, if available, with the content indicated in _X + /// </returns> + inline double nan(int _X) __GPU_ONLY + { + return __dp_math_nan(_X); + } + + + /// <summary> + /// Rounds the argument to an integer value in + /// floating-point format, using the current rounding direction. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the rounded integer value. + /// </returns> + inline float nearbyintf(float _X) __GPU_ONLY + { + return __dp_math_nearbyintf(_X); + } + + /// <summary> + /// Rounds the argument to an integer value in + /// floating-point format, using the current rounding direction. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the rounded integer value. + /// </returns> + inline float nearbyint(float _X) __GPU_ONLY + { + return __dp_math_nearbyintf(_X); + } + + /// <summary> + /// Rounds the argument to an integer value in + /// floating-point format, using the current rounding direction. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the rounded integer value. + /// </returns> + inline double nearbyint(double _X) __GPU_ONLY + { + return __dp_math_nearbyint(_X); + } + + + /// <summary> + /// Determine the next representable value, in the type of the function, + /// after _X in the direction of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returnsthe the next representable value, in the type of the function, + /// after _X in the direction of _Y + /// </returns> + inline float nextafterf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_nextafterf(_X, _Y); + } + + /// <summary> + /// Determine the next representable value, in the type of the function, + /// after _X in the direction of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returnsthe the next representable value, in the type of the function, + /// after _X in the direction of _Y + /// </returns> + inline float nextafter(float _X, float _Y) __GPU_ONLY + { + return __dp_math_nextafterf(_X, _Y); + } + + /// <summary> + /// Determine the next representable value, in the type of the function, + /// after _X in the direction of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returnsthe the next representable value, in the type of the function, + /// after _X in the direction of _Y + /// </returns> + inline double nextafter(double _X, double _Y) __GPU_ONLY + { + return __dp_math_nextafter(_X, _Y); + } + + /// <summary> + /// Returns the cumulative distribution function of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cumulative distribution function of the argument + /// </returns> + inline float phif(float _X) __GPU_ONLY + { + return __dp_math_phif(_X); + } + + /// <summary> + /// Returns the cumulative distribution function of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cumulative distribution function of the argument + /// </returns> + inline float phi(float _X) __GPU_ONLY + { + return __dp_math_phif(_X); + } + + /// <summary> + /// Returns the cumulative distribution function of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the cumulative distribution function of the argument + /// </returns> + inline double phi(double _X) __GPU_ONLY + { + return __dp_math_phi(_X); + } + + /// <summary> + /// Calculates _X raised to the power of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value, base + /// </param> + /// <param name="_Y"> + /// Floating-point value, exponent + /// </param> + /// <returns> + /// Returns the value of _X raised to the power of _Y + /// </returns> + inline float powf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_powf(_X, _Y); + } + + /// <summary> + /// Calculates _X raised to the power of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value, base + /// </param> + /// <param name="_Y"> + /// Floating-point value, exponent + /// </param> + /// <returns> + /// Returns the value of _X raised to the power of _Y + /// </returns> + inline float pow(float _X, float _Y) __GPU_ONLY + { + return __dp_math_powf(_X, _Y); + } + + /// <summary> + /// Calculates _X raised to the power of _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value, base + /// </param> + /// <param name="_Y"> + /// Floating-point value, exponent + /// </param> + /// <returns> + /// Returns the value of _X raised to the power of _Y + /// </returns> + inline double pow(double _X, double _Y) __GPU_ONLY + { + return __dp_math_pow(_X, _Y); + } + + + /// <summary> + /// Returns the inverse cumulative distribution function of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse cumulative distribution function of the argument + /// </returns> + inline float probitf(float _X) __GPU_ONLY + { + return __dp_math_probitf(_X); + } + + /// <summary> + /// Returns the inverse cumulative distribution function of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse cumulative distribution function of the argument + /// </returns> + inline float probit(float _X) __GPU_ONLY + { + return __dp_math_probitf(_X); + } + + /// <summary> + /// Returns the inverse cumulative distribution function of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the inverse cumulative distribution function of the argument + /// </returns> + inline double probit(double _X) __GPU_ONLY + { + return __dp_math_probit(_X); + } + + /// <summary> + /// Returns the reciprocal of the cube root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the cube root of the argument + /// </returns> + inline float rcbrtf(float _X) __GPU_ONLY + { + return __dp_math_rcbrtf(_X); + } + + /// <summary> + /// Returns the reciprocal of the cube root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the cube root of the argument + /// </returns> + inline float rcbrt(float _X) __GPU_ONLY + { + return __dp_math_rcbrtf(_X); + } + + /// <summary> + /// Returns the reciprocal of the cube root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the cube root of the argument + /// </returns> + inline double rcbrt(double _X) __GPU_ONLY + { + return __dp_math_rcbrt(_X); + } + + /// <summary> + /// Computes the remainder: _X REM _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X REM _Y + /// </returns> + inline float remainderf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_remainderf(_X, _Y); + } + + /// <summary> + /// Computes the remainder: _X REM _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X REM _Y + /// </returns> + inline float remainder(float _X, float _Y) __GPU_ONLY + { + return __dp_math_remainderf(_X, _Y); + } + + /// <summary> + /// Computes the remainder: _X REM _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X REM _Y + /// </returns> + inline double remainder(double _X, double _Y) __GPU_ONLY + { + return __dp_math_remainder(_X, _Y); + } + + /// <summary> + /// Computes the same remainder as _X REM _Y. Also calculates + /// the lower 24 bits of the integral quotient _X/_Y, and + /// gives that value the same sign as _X/_Y. It stores this + /// signed value in the integer pointed to by _Quo. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_Quo"> + /// Pointer to an integer value + /// </param> + /// <returns> + /// Returns the remainder + /// </returns> + inline float remquof(float _X, float _Y, _Out_ int * _Quo) __GPU_ONLY + { + return __dp_math_remquof(_X, _Y, _Quo); + } + + /// <summary> + /// Computes the same remainder as _X REM _Y. Also calculates + /// the lower 24 bits of the integral quotient _X/_Y, and + /// gives that value the same sign as _X/_Y. It stores this + /// signed value in the integer pointed to by _Quo. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_Quo"> + /// Pointer to an integer value + /// </param> + /// <returns> + /// Returns the remainder + /// </returns> + inline float remquo(float _X, float _Y, _Out_ int * _Quo) __GPU_ONLY + { + return __dp_math_remquof(_X, _Y, _Quo); + } + + /// <summary> + /// Computes the same remainder as _X REM _Y. Also calculates + /// the lower 31 bits of the integral quotient _X/_Y, and + /// gives that value the same sign as _X/_Y. It stores this + /// signed value in the integer pointed to by _Quo. + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <param name="_Quo"> + /// Pointer to an integer value + /// </param> + /// <returns> + /// Returns the remainder + /// </returns> + inline double remquo(double _X, double _Y, _Out_ int * _Quo) __GPU_ONLY + { + return __dp_math_remquo(_X, _Y, _Quo); + } + + /// <summary> + /// Rounds _X to the nearest integer + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the nearest integer of _X + /// </returns> + inline float roundf(float _X) __GPU_ONLY + { + return __dp_math_roundf(_X); + } + + /// <summary> + /// Rounds _X to the nearest integer + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the nearest integer of _X + /// </returns> + inline float round(float _X) __GPU_ONLY + { + return __dp_math_roundf(_X); + } + + /// <summary> + /// Rounds _X to the nearest integer + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the nearest integer of _X + /// </returns> + inline double round(double _X) __GPU_ONLY + { + return __dp_math_round(_X); + } + + /// <summary> + /// Returns the reciprocal of the square root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the square root of the argument + /// </returns> + inline float rsqrtf(float _X) __GPU_ONLY + { + return __dp_math_rsqrtf(_X); + } + + /// <summary> + /// Returns the reciprocal of the square root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the square root of the argument + /// </returns> + inline float rsqrt(float _X) __GPU_ONLY + { + return __dp_math_rsqrtf(_X); + } + + /// <summary> + /// Returns the reciprocal of the square root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the reciprocal of the square root of the argument + /// </returns> + inline double rsqrt(double _X) __GPU_ONLY + { + return __dp_math_rsqrt(_X); + } + + /// <summary> + /// Multiplies _X by FLT_RADIX to the power _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X * (FLT_RADIX ^ _Y) + /// </returns> + inline float scalbf(float _X, float _Y) __GPU_ONLY + { + return __dp_math_scalbf(_X, _Y); + } + + /// <summary> + /// Multiplies _X by FLT_RADIX to the power _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X * (FLT_RADIX ^ _Y) + /// </returns> + inline float scalb(float _X, float _Y) __GPU_ONLY + { + return __dp_math_scalbf(_X, _Y); + } + + /// <summary> + /// Multiplies _X by FLT_RADIX to the power _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns _X * (FLT_RADIX ^ _Y) + /// </returns> + inline double scalb(double _X, double _Y) __GPU_ONLY + { + return __dp_math_scalb(_X, _Y); + } + + /// <summary> + /// Multiplies _X by FLT_RADIX to the power _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Integer value + /// </param> + /// <returns> + /// Returns _X * (FLT_RADIX ^ _Y) + /// </returns> + inline float scalbnf(float _X, int _Y) __GPU_ONLY + { + return __dp_math_scalbnf(_X, _Y); + } + + /// <summary> + /// Multiplies _X by FLT_RADIX to the power _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Integer value + /// </param> + /// <returns> + /// Returns _X * (FLT_RADIX ^ _Y) + /// </returns> + inline float scalbn(float _X, int _Y) __GPU_ONLY + { + return __dp_math_scalbnf(_X, _Y); + } + + /// <summary> + /// Multiplies _X by FLT_RADIX to the power _Y + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_Y"> + /// Integer value + /// </param> + /// <returns> + /// Returns _X * (FLT_RADIX ^ _Y) + /// </returns> + inline double scalbn(double _X, int _Y) __GPU_ONLY + { + return __dp_math_scalbn(_X, _Y); + } + + /// <summary> + /// Determines whether the sign of _X is negative + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the sign of _X + /// is negative + /// </returns> + inline int signbitf(float _X) __GPU_ONLY + { + return __dp_math_signbitf(_X); + } + + /// <summary> + /// Determines whether the sign of _X is negative + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the sign of _X + /// is negative + /// </returns> + inline int signbit(float _X) __GPU_ONLY + { + return __dp_math_signbitf(_X); + } + + /// <summary> + /// Determines whether the sign of _X is negative + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns a nonzero value if and only if the sign of _X + /// is negative + /// </returns> + inline int signbit(double _X) __GPU_ONLY + { + return __dp_math_signbit(_X); + } + + /// <summary> + /// Calculates the sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of the argument + /// </returns> + inline float sinf(float _X) __GPU_ONLY + { + return __dp_math_sinf(_X); + } + + /// <summary> + /// Calculates the sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of the argument + /// </returns> + inline float sin(float _X) __GPU_ONLY + { + return __dp_math_sinf(_X); + } + + /// <summary> + /// Calculates the sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of the argument + /// </returns> + inline double sin(double _X) __GPU_ONLY + { + return __dp_math_sin(_X); + } + + /// <summary> + /// Calculates sine and cosine value of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_S"> + /// Returns the sine value of _X + /// </param> + /// <param name="_C"> + /// Returns the cosine value of _X + /// </param> + inline void sincosf(float _X, _Out_ float * _S, _Out_ float * _C) __GPU_ONLY + { + *_C = __dp_math_sincosf(_X, _S); + } + + /// <summary> + /// Calculates sine and cosine value of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_S"> + /// Returns the sine value of _X + /// </param> + /// <param name="_C"> + /// Returns the cosine value of _X + /// </param> + inline void sincos(float _X, _Out_ float * _S, _Out_ float * _C) __GPU_ONLY + { + *_C = __dp_math_sincosf(_X, _S); + } + + /// <summary> + /// Calculates sine and cosine value of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <param name="_S"> + /// Returns the sine value of _X + /// </param> + /// <param name="_C"> + /// Returns the cosine value of _X + /// </param> + inline void sincos(double _X, _Out_ double * _S, _Out_ double * _C) __GPU_ONLY + { + *_C = __dp_math_sincos(_X, _S); + } + + /// <summary> + /// Calculates the hyperbolic sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic sine value of the argument + /// </returns> + inline float sinhf(float _X) __GPU_ONLY + { + return __dp_math_sinhf(_X); + } + + /// <summary> + /// Calculates the hyperbolic sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic sine value of the argument + /// </returns> + inline float sinh(float _X) __GPU_ONLY + { + return __dp_math_sinhf(_X); + } + + /// <summary> + /// Calculates the hyperbolic sine value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic sine value of the argument + /// </returns> + inline double sinh(double _X) __GPU_ONLY + { + return __dp_math_sinh(_X); + } + + /// <summary> + /// Calculates the sine value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of pi * _X + /// </returns> + inline float sinpif(float _X) __GPU_ONLY + { + return __dp_math_sinpif(_X); + } + + /// <summary> + /// Calculates the sine value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of pi * _X + /// </returns> + inline float sinpi(float _X) __GPU_ONLY + { + return __dp_math_sinpif(_X); + } + + /// <summary> + /// Calculates the sine value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the sine value of pi * _X + /// </returns> + inline double sinpi(double _X) __GPU_ONLY + { + return __dp_math_sinpi(_X); + } + + /// <summary> + /// Calculates the squre root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the squre root of the argument + /// </returns> + inline float sqrtf(float _X) __GPU_ONLY + { + return __dp_math_sqrtf(_X); + } + + /// <summary> + /// Calculates the squre root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the squre root of the argument + /// </returns> + inline float sqrt(float _X) __GPU_ONLY + { + return __dp_math_sqrtf(_X); + } + + /// <summary> + /// Calculates the squre root of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the squre root of the argument + /// </returns> + inline double sqrt(double _X) __GPU_ONLY + { + return __dp_math_sqrt(_X); + } + + /// <summary> + /// Computes the gamma function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the result of gamma function of _X + /// </returns> + inline float tgammaf(float _X) __GPU_ONLY + { + return __dp_math_tgammaf(_X); + } + + /// <summary> + /// Computes the gamma function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the result of gamma function of _X + /// </returns> + inline float tgamma(float _X) __GPU_ONLY + { + return __dp_math_tgammaf(_X); + } + + /// <summary> + /// Computes the gamma function of _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the result of gamma function of _X + /// </returns> + inline double tgamma(double _X) __GPU_ONLY + { + return __dp_math_tgamma(_X); + } + + /// <summary> + /// Calculates the tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of the argument + /// </returns> + inline float tanf(float _X) __GPU_ONLY + { + return __dp_math_tanf(_X); + } + + /// <summary> + /// Calculates the tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of the argument + /// </returns> + inline float tan(float _X) __GPU_ONLY + { + return __dp_math_tanf(_X); + } + + /// <summary> + /// Calculates the tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of the argument + /// </returns> + inline double tan(double _X) __GPU_ONLY + { + return __dp_math_tan(_X); + } + + /// <summary> + /// Calculates the hyperbolic tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic tangent value of the argument + /// </returns> + inline float tanhf(float _X) __GPU_ONLY + { + return __dp_math_tanhf(_X); + } + + /// <summary> + /// Calculates the hyperbolic tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic tangent value of the argument + /// </returns> + inline float tanh(float _X) __GPU_ONLY + { + return __dp_math_tanhf(_X); + } + + /// <summary> + /// Calculates the hyperbolic tangent value of the argument + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the hyperbolic tangent value of the argument + /// </returns> + inline double tanh(double _X) __GPU_ONLY + { + return __dp_math_tanh(_X); + } + + /// <summary> + /// Calculates the tangent value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of pi * _X + /// </returns> + inline float tanpif(float _X) __GPU_ONLY + { + return __dp_math_tanpif(_X); + } + + /// <summary> + /// Calculates the tangent value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of pi * _X + /// </returns> + inline float tanpi(float _X) __GPU_ONLY + { + return __dp_math_tanpif(_X); + } + + /// <summary> + /// Calculates the tangent value of pi * _X + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the tangent value of pi * _X + /// </returns> + inline double tanpi(double _X) __GPU_ONLY + { + return __dp_math_tanpi(_X); + } + + /// <summary> + /// Truncates the argument to the integer component + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the integer component of the argument + /// </returns> + inline float truncf(float _X) __GPU_ONLY + { + return __dp_math_truncf(_X); + } + + /// <summary> + /// Truncates the argument to the integer component + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the integer component of the argument + /// </returns> + inline float trunc(float _X) __GPU_ONLY + { + return __dp_math_truncf(_X); + } + + /// <summary> + /// Truncates the argument to the integer component + /// </summary> + /// <param name="_X"> + /// Floating-point value + /// </param> + /// <returns> + /// Returns the integer component of the argument + /// </returns> + inline double trunc(double _X) __GPU_ONLY + { + return __dp_math_trunc(_X); + } + + //============================================================================= + // Import CMATH C++ functions: + //============================================================================= + using std::acosf; + using std::asinf; + using std::atanf; + using std::atan2f; + using std::ceilf; + using std::cosf; + using std::coshf; + using std::expf; + using std::fabsf; + using std::floorf; + using std::fmodf; + using std::frexpf; + using std::hypotf; + using std::ldexpf; + using std::logf; + using std::log10f; + using std::modff; + using std::powf; + using std::sinf; + using std::sinhf; + using std::sqrtf; + using std::tanf; + using std::tanhf; + + using std::acos; + using std::asin; + using std::atan; + using std::atan2; + using std::ceil; + using std::cos; + using std::cosh; + using std::exp; + using std::fabs; + using std::floor; + using std::fmod; + using std::frexp; + using std::hypot; + using std::ldexp; + using std::log; + using std::log10; + using std::modf; + using std::pow; + using std::sin; + using std::sinh; + using std::sqrt; + using std::tan; + using std::tanh; + + using std::acosh; + using std::acoshf; + using std::asinh; + using std::asinhf; + using std::atanh; + using std::atanhf; + using std::cbrt; + using std::cbrtf; + using std::copysign; + using std::copysignf; + using std::erf; + using std::erfc; + using std::erfcf; + using std::erff; + using std::exp2; + using std::exp2f; + using std::expm1; + using std::expm1f; + using std::fdim; + using std::fdimf; + using std::fma; + using std::fmaf; + using std::fmax; + using std::fmaxf; + using std::fmin; + using std::fminf; + using std::ilogb; + using std::ilogbf; + using std::log1p; + using std::log1pf; + using std::log2; + using std::log2f; + using std::logb; + using std::logbf; + using std::nearbyint; + using std::nearbyintf; + using std::nextafter; + using std::nextafterf; + using std::remainder; + using std::remainderf; + using std::remquo; + using std::remquof; + using std::round; + using std::roundf; + using std::scalbn; + using std::scalbnf; + using std::tgamma; + using std::tgammaf; + using std::trunc; + using std::truncf; +} // namespace precise_math + +} // namespace Concurrency diff --git a/test_data/lots_of_files/amp_short_vectors.h b/test_data/lots_of_files/amp_short_vectors.h new file mode 100644 index 0000000..8c65915 --- /dev/null +++ b/test_data/lots_of_files/amp_short_vectors.h @@ -0,0 +1,24178 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* amp_short_vectors.h +* +* C++ AMP Short Vector Types +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + + +// !!! DO NOT HAND EDIT !!! +// This file was generated. + +#pragma once + +#pragma warning(push) +#pragma warning(disable : 4100) +#include <amp.h> +#define _AMP_SHORT_VECTORS_H +namespace Concurrency +{ + namespace graphics + { + class unorm; + class norm; + + /// <summary> + /// Represent a unorm number. + /// Each element is a floating point number in the range of [0.0f, 1.0f]. + /// </summary> + class unorm + { + friend class norm; + private: + float _Value; + void _Set(float _Val) __CPU_ONLY + { + _Val = _Val < 0.0f ? 0.0f : _Val; + _Val = _Val > 1.0f ? 1.0f : _Val; + _Value = _Val; + } + + void _Set(float _Val) __GPU_ONLY + { + _Value = Concurrency::direct3d::clamp(_Val, 0.0f, 1.0f); + } + public: + + /// <summary> + /// Default constructor. Initialize to 0.0f. + /// </summary> + unorm(void) __GPU + { + _Value = 0.0f; + } + + /// <summary> + /// Constructor. Initialize by clamping _V to the range of [0.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit unorm(float _V) __GPU + { + _Set(_V); + } + + /// <summary> + /// Constructor. Initialize by casting _V to float, then clamping to the range of [0.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit unorm(unsigned int _V) __GPU + { + _Set(static_cast<float>(_V)); + } + + /// <summary> + /// Constructor. Initialize by casting _V to float, then clamping to the range of [0.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit unorm(int _V) __GPU + { + _Set(static_cast<float>(_V)); + } + + /// <summary> + /// Constructor. Initialize by casting _V to float, then clamping to the range of [0.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit unorm(double _V) __GPU + { + _Set(static_cast<float>(_V)); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + unorm(const unorm& _Other) __GPU + { + _Value = _Other._Value; + } + + /// <summary> + /// Constructor. Initialize by clamping _Other to the range of [0.0f, 1.0f]. + /// </summary> + /// <param name="_Other"> + /// The norm object used to initialize. + /// </param> + inline explicit unorm(const norm& _Other) __GPU; + + unorm& operator=(const unorm& _Other) __GPU + { + _Value = _Other._Value; + return *this; + } + + /// <summary> + /// Conversion operator. Convert the unorm number to a floating point value. + /// </summary> + operator float(void) const __GPU + { + return _Value; + } + + unorm& operator+=(const unorm& _Other) __GPU + { + float _Res = _Value; + _Res += _Other._Value; + _Set(_Res); + return *this; + } + + unorm& operator-=(const unorm& _Other) __GPU + { + float _Res = _Value; + _Res -= _Other._Value; + _Set(_Res); + return *this; + } + + unorm& operator*=(const unorm& _Other) __GPU + { + float _Res = _Value; + _Res *= _Other._Value; + _Set(_Res); + return *this; + } + + unorm& operator/=(const unorm& _Other) __GPU + { + float _Res = _Value; + _Res /= _Other._Value; + _Set(_Res); + return *this; + } + + unorm& operator++() __GPU + { + float _Res = _Value; + ++_Res; + _Set(_Res); + return *this; + } + + unorm operator++(int) __GPU + { + unorm _Res = *this; + ++(*this); + return _Res; + } + + unorm& operator--() __GPU + { + float _Res = _Value; + --_Res; + _Set(_Res); + return *this; + } + + unorm operator--(int) __GPU + { + unorm _Res = *this; + --(*this); + return _Res; + } + + }; + + /// <summary> + /// Represent a norm number. + /// Each element is a floating point number in the range of [-1.0f, 1.0f]. + /// </summary> + class norm + { + friend class unorm; + private: + float _Value; + void _Set(float _Val) __CPU_ONLY + { + _Val = _Val < -1.0f ? -1.0f : _Val; + _Val = _Val > 1.0f ? 1.0f : _Val; + _Value = _Val; + } + + void _Set(float _Val) __GPU_ONLY + { + _Value = Concurrency::direct3d::clamp(_Val, -1.0f, 1.0f); + } + public: + + /// <summary> + /// Default constructor. Initialize to 0.0f. + /// </summary> + norm(void) __GPU + { + _Value = 0.0f; + } + + /// <summary> + /// Constructor. Initialize by clamping _V to the range of [-1.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit norm(float _V) __GPU + { + _Set(_V); + } + + /// <summary> + /// Constructor. Initialize by casting _V to float, then clamping to the range of [-1.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit norm(unsigned int _V) __GPU + { + _Set(static_cast<float>(_V)); + } + + /// <summary> + /// Constructor. Initialize by casting _V to float, then clamping to the range of [-1.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit norm(int _V) __GPU + { + _Set(static_cast<float>(_V)); + } + + /// <summary> + /// Constructor. Initialize by casting _V to float, then clamping to the range of [-1.0f, 1.0f]. + /// </summary> + /// <param name="_V"> + /// The value used to initialize. + /// </param> + explicit norm(double _V) __GPU + { + _Set(static_cast<float>(_V)); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + norm(const norm& _Other) __GPU + { + _Value = _Other._Value; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + norm(const unorm& _Other) __GPU + { + _Value = _Other._Value; + } + + norm& operator=(const norm& _Other) __GPU + { + _Value = _Other._Value; + return *this; + } + + /// <summary> + /// Conversion operator. Convert the norm number to a floating point value. + /// </summary> + operator float(void) const __GPU + { + return _Value; + } + + norm& operator+=(const norm& _Other) __GPU + { + float _Res = _Value; + _Res += _Other._Value; + _Set(_Res); + return *this; + } + + norm& operator-=(const norm& _Other) __GPU + { + float _Res = _Value; + _Res -= _Other._Value; + _Set(_Res); + return *this; + } + + norm& operator*=(const norm& _Other) __GPU + { + float _Res = _Value; + _Res *= _Other._Value; + _Set(_Res); + return *this; + } + + norm& operator/=(const norm& _Other) __GPU + { + float _Res = _Value; + _Res /= _Other._Value; + _Set(_Res); + return *this; + } + + norm& operator++() __GPU + { + float _Res = _Value; + ++_Res; + _Set(_Res); + return *this; + } + + norm operator++(int) __GPU + { + norm _Res = *this; + ++(*this); + return _Res; + } + + norm& operator--() __GPU + { + float _Res = _Value; + --_Res; + _Set(_Res); + return *this; + } + + norm operator--(int) __GPU + { + norm _Res = *this; + --(*this); + return _Res; + } + + norm operator-(void) const __GPU + { + norm _Ret; + _Ret._Value = -_Value; + return _Ret; + } + + }; + + unorm::unorm(const norm& _Other) __GPU + { + _Set(_Other._Value); + } + + inline unorm operator+(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return unorm(float(_Lhs) + float(_Rhs)); + } + + inline norm operator+(const norm& _Lhs, const norm& _Rhs) __GPU + { + return norm(float(_Lhs) + float(_Rhs)); + } + + inline unorm operator-(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return unorm(float(_Lhs) - float(_Rhs)); + } + + inline norm operator-(const norm& _Lhs, const norm& _Rhs) __GPU + { + return norm(float(_Lhs) - float(_Rhs)); + } + + inline unorm operator*(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return unorm(float(_Lhs) * float(_Rhs)); + } + + inline norm operator*(const norm& _Lhs, const norm& _Rhs) __GPU + { + return norm(float(_Lhs) * float(_Rhs)); + } + + inline unorm operator/(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return unorm(float(_Lhs) / float(_Rhs)); + } + + inline norm operator/(const norm& _Lhs, const norm& _Rhs) __GPU + { + return norm(float(_Lhs) / float(_Rhs)); + } + + inline bool operator==(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return float(_Lhs) == float(_Rhs); + } + + inline bool operator==(const norm& _Lhs, const norm& _Rhs) __GPU + { + return float(_Lhs) == float(_Rhs); + } + + inline bool operator!=(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return float(_Lhs) != float(_Rhs); + } + + inline bool operator!=(const norm& _Lhs, const norm& _Rhs) __GPU + { + return float(_Lhs) != float(_Rhs); + } + + inline bool operator>(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return float(_Lhs) > float(_Rhs); + } + + inline bool operator>(const norm& _Lhs, const norm& _Rhs) __GPU + { + return float(_Lhs) > float(_Rhs); + } + + inline bool operator<(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return float(_Lhs) < float(_Rhs); + } + + inline bool operator<(const norm& _Lhs, const norm& _Rhs) __GPU + { + return float(_Lhs) < float(_Rhs); + } + + inline bool operator>=(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return float(_Lhs) >= float(_Rhs); + } + + inline bool operator>=(const norm& _Lhs, const norm& _Rhs) __GPU + { + return float(_Lhs) >= float(_Rhs); + } + + inline bool operator<=(const unorm& _Lhs, const unorm& _Rhs) __GPU + { + return float(_Lhs) <= float(_Rhs); + } + + inline bool operator<=(const norm& _Lhs, const norm& _Rhs) __GPU + { + return float(_Lhs) <= float(_Rhs); + } + +#define UNORM_ZERO ((concurrency::graphics::unorm)0.0f) +#define UNORM_MIN ((concurrency::graphics::unorm)0.0f) +#define UNORM_MAX ((concurrency::graphics::unorm)1.0f) +#define NORM_ZERO ((concurrency::graphics::norm)0.0f) +#define NORM_MIN ((concurrency::graphics::norm)-1.0f) +#define NORM_MAX ((concurrency::graphics::norm)1.0f) + + + typedef unsigned int uint; + // Forward Declarations + class uint_2; + class uint_3; + class uint_4; + class int_2; + class int_3; + class int_4; + class float_2; + class float_3; + class float_4; + class unorm_2; + class unorm_3; + class unorm_4; + class norm_2; + class norm_3; + class norm_4; + class double_2; + class double_3; + class double_4; + + /// <summary> + /// Represent a short vector of 2 unsigned int's. + /// </summary> + class uint_2 + { + public: + typedef unsigned int value_type; + static const int size = 2; + private: + static const _Short_vector_base_type_id _Base_type_id = _Uint_type; + private: + value_type _M_x; + value_type _M_y; + + public: + /// <summary> + /// Property for accessing element 0 of this uint_2 as an unsigned int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unsigned int x; + /// <summary> + /// Property for accessing element 0 of this uint_2 as an unsigned int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unsigned int r; + + /// <summary> + /// Returns element 0 of this uint_2. + /// </summary> + /// <returns> + /// Element 0 of this uint_2. + /// </returns> + unsigned int get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this uint_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this uint_2. + /// </returns> + unsigned int& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this uint_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this uint_2. + /// </returns> + unsigned int& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this uint_2 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_x(unsigned int _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this uint_2 as an unsigned int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unsigned int y; + /// <summary> + /// Property for accessing element 1 of this uint_2 as an unsigned int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unsigned int g; + + /// <summary> + /// Returns element 1 of this uint_2. + /// </summary> + /// <returns> + /// Element 1 of this uint_2. + /// </returns> + unsigned int get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this uint_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this uint_2. + /// </returns> + unsigned int& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this uint_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this uint_2. + /// </returns> + unsigned int& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this uint_2 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_y(unsigned int _Value) __GPU + { + _M_y = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + uint_2() __GPU + { + _M_x = 0; + _M_y = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + uint_2(unsigned int _V0, unsigned int _V1) __GPU + { + _M_x = _V0; + _M_y = _V1; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + uint_2(unsigned int _V) __GPU + { + _M_x = _V; + _M_y = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_2(const int_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_2(const float_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_2(const unorm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_2(const norm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_2(const double_2& _Other) __GPU; + + uint_2 operator~() const __GPU + { + uint_2 _Value = *this; + return uint_2(~_Value.x, ~_Value.y); + } + + uint_2& operator++() __GPU + { + uint_2 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + *this = _Value; + return *this; + } + + uint_2 operator++(int) __GPU + { + uint_2 _Result = *this; + ++(*this); + return _Result; + } + + uint_2& operator--() __GPU + { + uint_2 _Value = *this; + --_Value._M_x; + --_Value._M_y; + *this = _Value; + return *this; + } + + uint_2 operator--(int) __GPU + { + uint_2 _Result = *this; + --(*this); + return _Result; + } + + uint_2& operator+=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator-=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator*=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator/=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator%=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x %= _Value2.x; + _Value1.y %= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator^=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x ^= _Value2.x; + _Value1.y ^= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator|=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x |= _Value2.x; + _Value1.y |= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator&=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x &= _Value2.x; + _Value1.y &= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator>>=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x >>= _Value2.x; + _Value1.y >>= _Value2.y; + *this = _Value1; + return *this; + } + + uint_2& operator<<=(const uint_2& _Other) __GPU + { + uint_2 _Value1 = *this; + uint_2 _Value2 = _Other; + _Value1.x <<= _Value2.x; + _Value1.y <<= _Value2.y; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this uint_2 as a uint_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) uint_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this uint_2 as a uint_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) uint_2 rg; + + /// <summary> + /// Returns a uint_2 that is composed of element 0, and element 1 of this uint_2. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_xy() const __GPU { + return uint_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this uint_2 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_xy(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this uint_2 as a uint_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) uint_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this uint_2 as a uint_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) uint_2 gr; + + /// <summary> + /// Returns a uint_2 that is composed of element 1, and element 0 of this uint_2. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_yx() const __GPU { + return uint_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this uint_2 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_yx(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + }; + + /// <summary> + /// Represent a short vector of 3 unsigned int's. + /// </summary> + class uint_3 + { + public: + typedef unsigned int value_type; + static const int size = 3; + private: + static const _Short_vector_base_type_id _Base_type_id = _Uint_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + + public: + /// <summary> + /// Property for accessing element 0 of this uint_3 as an unsigned int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unsigned int x; + /// <summary> + /// Property for accessing element 0 of this uint_3 as an unsigned int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unsigned int r; + + /// <summary> + /// Returns element 0 of this uint_3. + /// </summary> + /// <returns> + /// Element 0 of this uint_3. + /// </returns> + unsigned int get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this uint_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this uint_3. + /// </returns> + unsigned int& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this uint_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this uint_3. + /// </returns> + unsigned int& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this uint_3 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_x(unsigned int _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this uint_3 as an unsigned int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unsigned int y; + /// <summary> + /// Property for accessing element 1 of this uint_3 as an unsigned int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unsigned int g; + + /// <summary> + /// Returns element 1 of this uint_3. + /// </summary> + /// <returns> + /// Element 1 of this uint_3. + /// </returns> + unsigned int get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this uint_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this uint_3. + /// </returns> + unsigned int& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this uint_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this uint_3. + /// </returns> + unsigned int& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this uint_3 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_y(unsigned int _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this uint_3 as an unsigned int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unsigned int z; + /// <summary> + /// Property for accessing element 2 of this uint_3 as an unsigned int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unsigned int b; + + /// <summary> + /// Returns element 2 of this uint_3. + /// </summary> + /// <returns> + /// Element 2 of this uint_3. + /// </returns> + unsigned int get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this uint_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this uint_3. + /// </returns> + unsigned int& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this uint_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this uint_3. + /// </returns> + unsigned int& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this uint_3 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_z(unsigned int _Value) __GPU + { + _M_z = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + uint_3() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + uint_3(unsigned int _V0, unsigned int _V1, unsigned int _V2) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + uint_3(unsigned int _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_3(const int_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_3(const float_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_3(const unorm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_3(const norm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_3(const double_3& _Other) __GPU; + + uint_3 operator~() const __GPU + { + uint_3 _Value = *this; + return uint_3(~_Value.x, ~_Value.y, ~_Value.z); + } + + uint_3& operator++() __GPU + { + uint_3 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + *this = _Value; + return *this; + } + + uint_3 operator++(int) __GPU + { + uint_3 _Result = *this; + ++(*this); + return _Result; + } + + uint_3& operator--() __GPU + { + uint_3 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + *this = _Value; + return *this; + } + + uint_3 operator--(int) __GPU + { + uint_3 _Result = *this; + --(*this); + return _Result; + } + + uint_3& operator+=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator-=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator*=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator/=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator%=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x %= _Value2.x; + _Value1.y %= _Value2.y; + _Value1.z %= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator^=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x ^= _Value2.x; + _Value1.y ^= _Value2.y; + _Value1.z ^= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator|=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x |= _Value2.x; + _Value1.y |= _Value2.y; + _Value1.z |= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator&=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x &= _Value2.x; + _Value1.y &= _Value2.y; + _Value1.z &= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator>>=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x >>= _Value2.x; + _Value1.y >>= _Value2.y; + _Value1.z >>= _Value2.z; + *this = _Value1; + return *this; + } + + uint_3& operator<<=(const uint_3& _Other) __GPU + { + uint_3 _Value1 = *this; + uint_3 _Value2 = _Other; + _Value1.x <<= _Value2.x; + _Value1.y <<= _Value2.y; + _Value1.z <<= _Value2.z; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) uint_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) uint_2 rg; + + /// <summary> + /// Returns a uint_2 that is composed of element 0, and element 1 of this uint_3. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_xy() const __GPU { + return uint_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this uint_3 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_xy(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) uint_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) uint_2 rb; + + /// <summary> + /// Returns a uint_2 that is composed of element 0, and element 2 of this uint_3. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_xz() const __GPU { + return uint_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this uint_3 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_xz(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) uint_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) uint_2 gr; + + /// <summary> + /// Returns a uint_2 that is composed of element 1, and element 0 of this uint_3. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_yx() const __GPU { + return uint_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this uint_3 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_yx(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) uint_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) uint_2 gb; + + /// <summary> + /// Returns a uint_2 that is composed of element 1, and element 2 of this uint_3. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_yz() const __GPU { + return uint_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this uint_3 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_yz(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) uint_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) uint_2 br; + + /// <summary> + /// Returns a uint_2 that is composed of element 2, and element 0 of this uint_3. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_zx() const __GPU { + return uint_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this uint_3 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_zx(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) uint_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this uint_3 as a uint_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) uint_2 bg; + + /// <summary> + /// Returns a uint_2 that is composed of element 2, and element 1 of this uint_3. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_zy() const __GPU { + return uint_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this uint_3 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_zy(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) uint_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) uint_3 rgb; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 1, and element 2 of this uint_3. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xyz() const __GPU { + return uint_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this uint_3 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xyz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) uint_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) uint_3 rbg; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 2, and element 1 of this uint_3. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xzy() const __GPU { + return uint_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this uint_3 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xzy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) uint_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) uint_3 grb; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 0, and element 2 of this uint_3. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_yxz() const __GPU { + return uint_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this uint_3 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_yxz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) uint_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) uint_3 gbr; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 2, and element 0 of this uint_3. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_yzx() const __GPU { + return uint_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this uint_3 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_yzx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) uint_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) uint_3 brg; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 0, and element 1 of this uint_3. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zxy() const __GPU { + return uint_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this uint_3 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zxy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) uint_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this uint_3 as a uint_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) uint_3 bgr; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 1, and element 0 of this uint_3. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zyx() const __GPU { + return uint_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this uint_3 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zyx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + }; + + /// <summary> + /// Represent a short vector of 4 unsigned int's. + /// </summary> + class uint_4 + { + public: + typedef unsigned int value_type; + static const int size = 4; + private: + static const _Short_vector_base_type_id _Base_type_id = _Uint_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + value_type _M_w; + + public: + /// <summary> + /// Property for accessing element 0 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unsigned int x; + /// <summary> + /// Property for accessing element 0 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unsigned int r; + + /// <summary> + /// Returns element 0 of this uint_4. + /// </summary> + /// <returns> + /// Element 0 of this uint_4. + /// </returns> + unsigned int get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this uint_4. + /// </returns> + unsigned int& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this uint_4. + /// </returns> + unsigned int& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this uint_4 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_x(unsigned int _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unsigned int y; + /// <summary> + /// Property for accessing element 1 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unsigned int g; + + /// <summary> + /// Returns element 1 of this uint_4. + /// </summary> + /// <returns> + /// Element 1 of this uint_4. + /// </returns> + unsigned int get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this uint_4. + /// </returns> + unsigned int& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this uint_4. + /// </returns> + unsigned int& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this uint_4 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_y(unsigned int _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unsigned int z; + /// <summary> + /// Property for accessing element 2 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unsigned int b; + + /// <summary> + /// Returns element 2 of this uint_4. + /// </summary> + /// <returns> + /// Element 2 of this uint_4. + /// </returns> + unsigned int get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this uint_4. + /// </returns> + unsigned int& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this uint_4. + /// </returns> + unsigned int& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this uint_4 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_z(unsigned int _Value) __GPU + { + _M_z = _Value; + } + + /// <summary> + /// Property for accessing element 3 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) unsigned int w; + /// <summary> + /// Property for accessing element 3 of this uint_4 as an unsigned int. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) unsigned int a; + + /// <summary> + /// Returns element 3 of this uint_4. + /// </summary> + /// <returns> + /// Element 3 of this uint_4. + /// </returns> + unsigned int get_w() const __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this uint_4. + /// </returns> + unsigned int& ref_w() __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this uint_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this uint_4. + /// </returns> + unsigned int& ref_a() __GPU { + return _M_w; + } + + /// <summary> + /// Set element 3 of this uint_4 with an unsigned int. + /// </summary> + /// <param name="_Value"> + /// an unsigned int value. + /// </param> + void set_w(unsigned int _Value) __GPU + { + _M_w = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + uint_4() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + _M_w = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + uint_4(unsigned int _V0, unsigned int _V1, unsigned int _V2, unsigned int _V3) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + _M_w = _V3; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + uint_4(unsigned int _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + _M_w = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_4(const int_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_4(const float_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_4(const unorm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_4(const norm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline uint_4(const double_4& _Other) __GPU; + + uint_4 operator~() const __GPU + { + uint_4 _Value = *this; + return uint_4(~_Value.x, ~_Value.y, ~_Value.z, ~_Value.w); + } + + uint_4& operator++() __GPU + { + uint_4 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + ++_Value._M_w; + *this = _Value; + return *this; + } + + uint_4 operator++(int) __GPU + { + uint_4 _Result = *this; + ++(*this); + return _Result; + } + + uint_4& operator--() __GPU + { + uint_4 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + --_Value._M_w; + *this = _Value; + return *this; + } + + uint_4 operator--(int) __GPU + { + uint_4 _Result = *this; + --(*this); + return _Result; + } + + uint_4& operator+=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + _Value1.w += _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator-=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + _Value1.w -= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator*=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + _Value1.w *= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator/=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + _Value1.w /= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator%=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x %= _Value2.x; + _Value1.y %= _Value2.y; + _Value1.z %= _Value2.z; + _Value1.w %= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator^=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x ^= _Value2.x; + _Value1.y ^= _Value2.y; + _Value1.z ^= _Value2.z; + _Value1.w ^= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator|=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x |= _Value2.x; + _Value1.y |= _Value2.y; + _Value1.z |= _Value2.z; + _Value1.w |= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator&=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x &= _Value2.x; + _Value1.y &= _Value2.y; + _Value1.z &= _Value2.z; + _Value1.w &= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator>>=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x >>= _Value2.x; + _Value1.y >>= _Value2.y; + _Value1.z >>= _Value2.z; + _Value1.w >>= _Value2.w; + *this = _Value1; + return *this; + } + + uint_4& operator<<=(const uint_4& _Other) __GPU + { + uint_4 _Value1 = *this; + uint_4 _Value2 = _Other; + _Value1.x <<= _Value2.x; + _Value1.y <<= _Value2.y; + _Value1.z <<= _Value2.z; + _Value1.w <<= _Value2.w; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) uint_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) uint_2 rg; + + /// <summary> + /// Returns a uint_2 that is composed of element 0, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_xy() const __GPU { + return uint_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_xy(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) uint_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) uint_2 rb; + + /// <summary> + /// Returns a uint_2 that is composed of element 0, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_xz() const __GPU { + return uint_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_xz(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 3 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) uint_2 xw; + /// <summary> + /// Property for accessing element 0, and 3 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) uint_2 ra; + + /// <summary> + /// Returns a uint_2 that is composed of element 0, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_xw() const __GPU { + return uint_2(_M_x,_M_w); + } + + /// <summary> + /// Set element 0, and 3 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_xw(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) uint_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) uint_2 gr; + + /// <summary> + /// Returns a uint_2 that is composed of element 1, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_yx() const __GPU { + return uint_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_yx(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) uint_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) uint_2 gb; + + /// <summary> + /// Returns a uint_2 that is composed of element 1, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_yz() const __GPU { + return uint_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_yz(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 3 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) uint_2 yw; + /// <summary> + /// Property for accessing element 1, and 3 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) uint_2 ga; + + /// <summary> + /// Returns a uint_2 that is composed of element 1, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_yw() const __GPU { + return uint_2(_M_y,_M_w); + } + + /// <summary> + /// Set element 1, and 3 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_yw(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) uint_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) uint_2 br; + + /// <summary> + /// Returns a uint_2 that is composed of element 2, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_zx() const __GPU { + return uint_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_zx(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) uint_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) uint_2 bg; + + /// <summary> + /// Returns a uint_2 that is composed of element 2, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_zy() const __GPU { + return uint_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_zy(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 3 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) uint_2 zw; + /// <summary> + /// Property for accessing element 2, and 3 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) uint_2 ba; + + /// <summary> + /// Returns a uint_2 that is composed of element 2, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_zw() const __GPU { + return uint_2(_M_z,_M_w); + } + + /// <summary> + /// Set element 2, and 3 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_zw(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 0 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) uint_2 wx; + /// <summary> + /// Property for accessing element 3, and 0 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) uint_2 ar; + + /// <summary> + /// Returns a uint_2 that is composed of element 3, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_wx() const __GPU { + return uint_2(_M_w,_M_x); + } + + /// <summary> + /// Set element 3, and 0 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_wx(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 1 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) uint_2 wy; + /// <summary> + /// Property for accessing element 3, and 1 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) uint_2 ag; + + /// <summary> + /// Returns a uint_2 that is composed of element 3, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_wy() const __GPU { + return uint_2(_M_w,_M_y); + } + + /// <summary> + /// Set element 3, and 1 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_wy(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 2 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) uint_2 wz; + /// <summary> + /// Property for accessing element 3, and 2 of this uint_4 as a uint_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) uint_2 ab; + + /// <summary> + /// Returns a uint_2 that is composed of element 3, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_2. + /// </returns> + uint_2 get_wz() const __GPU { + return uint_2(_M_w,_M_z); + } + + /// <summary> + /// Set element 3, and 2 of this uint_4 with a uint_2. + /// </summary> + /// <param name="_Value"> + /// a uint_2 value. + /// </param> + void set_wz(const uint_2& _Value) __GPU + { + uint_2 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) uint_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) uint_3 rgb; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 1, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xyz() const __GPU { + return uint_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xyz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) uint_3 xyw; + /// <summary> + /// Property for accessing element 0, 1, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) uint_3 rga; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 1, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xyw() const __GPU { + return uint_3(_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 1, and 3 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xyw(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) uint_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) uint_3 rbg; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 2, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xzy() const __GPU { + return uint_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xzy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) uint_3 xzw; + /// <summary> + /// Property for accessing element 0, 2, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) uint_3 rba; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 2, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xzw() const __GPU { + return uint_3(_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 2, and 3 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xzw(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) uint_3 xwy; + /// <summary> + /// Property for accessing element 0, 3, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) uint_3 rag; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 3, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xwy() const __GPU { + return uint_3(_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 3, and 1 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xwy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) uint_3 xwz; + /// <summary> + /// Property for accessing element 0, 3, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) uint_3 rab; + + /// <summary> + /// Returns a uint_3 that is composed of element 0, element 3, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_xwz() const __GPU { + return uint_3(_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 3, and 2 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_xwz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) uint_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) uint_3 grb; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 0, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_yxz() const __GPU { + return uint_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_yxz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) uint_3 yxw; + /// <summary> + /// Property for accessing element 1, 0, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) uint_3 gra; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 0, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_yxw() const __GPU { + return uint_3(_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 0, and 3 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_yxw(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) uint_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) uint_3 gbr; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 2, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_yzx() const __GPU { + return uint_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_yzx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) uint_3 yzw; + /// <summary> + /// Property for accessing element 1, 2, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) uint_3 gba; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 2, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_yzw() const __GPU { + return uint_3(_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 2, and 3 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_yzw(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) uint_3 ywx; + /// <summary> + /// Property for accessing element 1, 3, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) uint_3 gar; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 3, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_ywx() const __GPU { + return uint_3(_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 3, and 0 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_ywx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) uint_3 ywz; + /// <summary> + /// Property for accessing element 1, 3, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) uint_3 gab; + + /// <summary> + /// Returns a uint_3 that is composed of element 1, element 3, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_ywz() const __GPU { + return uint_3(_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 3, and 2 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_ywz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) uint_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) uint_3 brg; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 0, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zxy() const __GPU { + return uint_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zxy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) uint_3 zxw; + /// <summary> + /// Property for accessing element 2, 0, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) uint_3 bra; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 0, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zxw() const __GPU { + return uint_3(_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 0, and 3 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zxw(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) uint_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) uint_3 bgr; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 1, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zyx() const __GPU { + return uint_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zyx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) uint_3 zyw; + /// <summary> + /// Property for accessing element 2, 1, and 3 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) uint_3 bga; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 1, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zyw() const __GPU { + return uint_3(_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 1, and 3 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zyw(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) uint_3 zwx; + /// <summary> + /// Property for accessing element 2, 3, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) uint_3 bar; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 3, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zwx() const __GPU { + return uint_3(_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 3, and 0 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zwx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) uint_3 zwy; + /// <summary> + /// Property for accessing element 2, 3, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) uint_3 bag; + + /// <summary> + /// Returns a uint_3 that is composed of element 2, element 3, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_zwy() const __GPU { + return uint_3(_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 3, and 1 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_zwy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) uint_3 wxy; + /// <summary> + /// Property for accessing element 3, 0, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) uint_3 arg; + + /// <summary> + /// Returns a uint_3 that is composed of element 3, element 0, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_wxy() const __GPU { + return uint_3(_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 0, and 1 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_wxy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) uint_3 wxz; + /// <summary> + /// Property for accessing element 3, 0, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) uint_3 arb; + + /// <summary> + /// Returns a uint_3 that is composed of element 3, element 0, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_wxz() const __GPU { + return uint_3(_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 0, and 2 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_wxz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) uint_3 wyx; + /// <summary> + /// Property for accessing element 3, 1, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) uint_3 agr; + + /// <summary> + /// Returns a uint_3 that is composed of element 3, element 1, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_wyx() const __GPU { + return uint_3(_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 1, and 0 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_wyx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) uint_3 wyz; + /// <summary> + /// Property for accessing element 3, 1, and 2 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) uint_3 agb; + + /// <summary> + /// Returns a uint_3 that is composed of element 3, element 1, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_wyz() const __GPU { + return uint_3(_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 1, and 2 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_wyz(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) uint_3 wzx; + /// <summary> + /// Property for accessing element 3, 2, and 0 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) uint_3 abr; + + /// <summary> + /// Returns a uint_3 that is composed of element 3, element 2, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_wzx() const __GPU { + return uint_3(_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 2, and 0 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_wzx(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) uint_3 wzy; + /// <summary> + /// Property for accessing element 3, 2, and 1 of this uint_4 as a uint_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) uint_3 abg; + + /// <summary> + /// Returns a uint_3 that is composed of element 3, element 2, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_3. + /// </returns> + uint_3 get_wzy() const __GPU { + return uint_3(_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 2, and 1 of this uint_4 with a uint_3. + /// </summary> + /// <param name="_Value"> + /// a uint_3 value. + /// </param> + void set_wzy(const uint_3& _Value) __GPU + { + uint_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) uint_4 xyzw; + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) uint_4 rgba; + + /// <summary> + /// Returns a uint_4 that is composed of element 0, element 1, element 2, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_xyzw() const __GPU { + return uint_4(_M_x,_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 1, 2, and 3 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_xyzw(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) uint_4 xywz; + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) uint_4 rgab; + + /// <summary> + /// Returns a uint_4 that is composed of element 0, element 1, element 3, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_xywz() const __GPU { + return uint_4(_M_x,_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 1, 3, and 2 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_xywz(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) uint_4 xzyw; + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) uint_4 rbga; + + /// <summary> + /// Returns a uint_4 that is composed of element 0, element 2, element 1, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_xzyw() const __GPU { + return uint_4(_M_x,_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 2, 1, and 3 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_xzyw(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) uint_4 xzwy; + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) uint_4 rbag; + + /// <summary> + /// Returns a uint_4 that is composed of element 0, element 2, element 3, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_xzwy() const __GPU { + return uint_4(_M_x,_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 2, 3, and 1 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_xzwy(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) uint_4 xwyz; + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) uint_4 ragb; + + /// <summary> + /// Returns a uint_4 that is composed of element 0, element 3, element 1, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_xwyz() const __GPU { + return uint_4(_M_x,_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 3, 1, and 2 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_xwyz(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) uint_4 xwzy; + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) uint_4 rabg; + + /// <summary> + /// Returns a uint_4 that is composed of element 0, element 3, element 2, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_xwzy() const __GPU { + return uint_4(_M_x,_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 3, 2, and 1 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_xwzy(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) uint_4 yxzw; + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) uint_4 grba; + + /// <summary> + /// Returns a uint_4 that is composed of element 1, element 0, element 2, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_yxzw() const __GPU { + return uint_4(_M_y,_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 0, 2, and 3 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_yxzw(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) uint_4 yxwz; + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) uint_4 grab; + + /// <summary> + /// Returns a uint_4 that is composed of element 1, element 0, element 3, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_yxwz() const __GPU { + return uint_4(_M_y,_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 0, 3, and 2 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_yxwz(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) uint_4 yzxw; + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) uint_4 gbra; + + /// <summary> + /// Returns a uint_4 that is composed of element 1, element 2, element 0, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_yzxw() const __GPU { + return uint_4(_M_y,_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 2, 0, and 3 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_yzxw(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) uint_4 yzwx; + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) uint_4 gbar; + + /// <summary> + /// Returns a uint_4 that is composed of element 1, element 2, element 3, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_yzwx() const __GPU { + return uint_4(_M_y,_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 2, 3, and 0 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_yzwx(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) uint_4 ywxz; + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) uint_4 garb; + + /// <summary> + /// Returns a uint_4 that is composed of element 1, element 3, element 0, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_ywxz() const __GPU { + return uint_4(_M_y,_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 3, 0, and 2 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_ywxz(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) uint_4 ywzx; + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) uint_4 gabr; + + /// <summary> + /// Returns a uint_4 that is composed of element 1, element 3, element 2, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_ywzx() const __GPU { + return uint_4(_M_y,_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 3, 2, and 0 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_ywzx(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) uint_4 zxyw; + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) uint_4 brga; + + /// <summary> + /// Returns a uint_4 that is composed of element 2, element 0, element 1, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_zxyw() const __GPU { + return uint_4(_M_z,_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 0, 1, and 3 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_zxyw(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) uint_4 zxwy; + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) uint_4 brag; + + /// <summary> + /// Returns a uint_4 that is composed of element 2, element 0, element 3, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_zxwy() const __GPU { + return uint_4(_M_z,_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 0, 3, and 1 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_zxwy(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) uint_4 zyxw; + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) uint_4 bgra; + + /// <summary> + /// Returns a uint_4 that is composed of element 2, element 1, element 0, and element 3 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_zyxw() const __GPU { + return uint_4(_M_z,_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 1, 0, and 3 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_zyxw(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) uint_4 zywx; + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) uint_4 bgar; + + /// <summary> + /// Returns a uint_4 that is composed of element 2, element 1, element 3, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_zywx() const __GPU { + return uint_4(_M_z,_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 1, 3, and 0 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_zywx(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) uint_4 zwxy; + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) uint_4 barg; + + /// <summary> + /// Returns a uint_4 that is composed of element 2, element 3, element 0, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_zwxy() const __GPU { + return uint_4(_M_z,_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 3, 0, and 1 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_zwxy(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) uint_4 zwyx; + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) uint_4 bagr; + + /// <summary> + /// Returns a uint_4 that is composed of element 2, element 3, element 1, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_zwyx() const __GPU { + return uint_4(_M_z,_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 3, 1, and 0 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_zwyx(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) uint_4 wxyz; + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) uint_4 argb; + + /// <summary> + /// Returns a uint_4 that is composed of element 3, element 0, element 1, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_wxyz() const __GPU { + return uint_4(_M_w,_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 0, 1, and 2 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_wxyz(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) uint_4 wxzy; + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) uint_4 arbg; + + /// <summary> + /// Returns a uint_4 that is composed of element 3, element 0, element 2, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_wxzy() const __GPU { + return uint_4(_M_w,_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 0, 2, and 1 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_wxzy(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) uint_4 wyxz; + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) uint_4 agrb; + + /// <summary> + /// Returns a uint_4 that is composed of element 3, element 1, element 0, and element 2 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_wyxz() const __GPU { + return uint_4(_M_w,_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 1, 0, and 2 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_wyxz(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) uint_4 wyzx; + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) uint_4 agbr; + + /// <summary> + /// Returns a uint_4 that is composed of element 3, element 1, element 2, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_wyzx() const __GPU { + return uint_4(_M_w,_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 1, 2, and 0 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_wyzx(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) uint_4 wzxy; + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) uint_4 abrg; + + /// <summary> + /// Returns a uint_4 that is composed of element 3, element 2, element 0, and element 1 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_wzxy() const __GPU { + return uint_4(_M_w,_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 2, 0, and 1 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_wzxy(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) uint_4 wzyx; + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this uint_4 as a uint_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) uint_4 abgr; + + /// <summary> + /// Returns a uint_4 that is composed of element 3, element 2, element 1, and element 0 of this uint_4. + /// </summary> + /// <returns> + /// a uint_4. + /// </returns> + uint_4 get_wzyx() const __GPU { + return uint_4(_M_w,_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 2, 1, and 0 of this uint_4 with a uint_4. + /// </summary> + /// <param name="_Value"> + /// a uint_4 value. + /// </param> + void set_wzyx(const uint_4& _Value) __GPU + { + uint_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + }; + + /// <summary> + /// Represent a short vector of 2 int's. + /// </summary> + class int_2 + { + public: + typedef int value_type; + static const int size = 2; + private: + static const _Short_vector_base_type_id _Base_type_id = _Int_type; + private: + value_type _M_x; + value_type _M_y; + + public: + /// <summary> + /// Property for accessing element 0 of this int_2 as an int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) int x; + /// <summary> + /// Property for accessing element 0 of this int_2 as an int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) int r; + + /// <summary> + /// Returns element 0 of this int_2. + /// </summary> + /// <returns> + /// Element 0 of this int_2. + /// </returns> + int get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this int_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this int_2. + /// </returns> + int& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this int_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this int_2. + /// </returns> + int& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this int_2 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_x(int _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this int_2 as an int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) int y; + /// <summary> + /// Property for accessing element 1 of this int_2 as an int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) int g; + + /// <summary> + /// Returns element 1 of this int_2. + /// </summary> + /// <returns> + /// Element 1 of this int_2. + /// </returns> + int get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this int_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this int_2. + /// </returns> + int& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this int_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this int_2. + /// </returns> + int& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this int_2 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_y(int _Value) __GPU + { + _M_y = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + int_2() __GPU + { + _M_x = 0; + _M_y = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + int_2(int _V0, int _V1) __GPU + { + _M_x = _V0; + _M_y = _V1; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + int_2(int _V) __GPU + { + _M_x = _V; + _M_y = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_2(const uint_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_2(const float_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_2(const unorm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_2(const norm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_2(const double_2& _Other) __GPU; + + int_2 operator~() const __GPU + { + int_2 _Value = *this; + return int_2(~_Value.x, ~_Value.y); + } + + int_2 operator-() const __GPU + { + int_2 _Value = *this; + return int_2(-_Value.x, -_Value.y); + } + + int_2& operator++() __GPU + { + int_2 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + *this = _Value; + return *this; + } + + int_2 operator++(int) __GPU + { + int_2 _Result = *this; + ++(*this); + return _Result; + } + + int_2& operator--() __GPU + { + int_2 _Value = *this; + --_Value._M_x; + --_Value._M_y; + *this = _Value; + return *this; + } + + int_2 operator--(int) __GPU + { + int_2 _Result = *this; + --(*this); + return _Result; + } + + int_2& operator+=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator-=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator*=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator/=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator%=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x %= _Value2.x; + _Value1.y %= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator^=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x ^= _Value2.x; + _Value1.y ^= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator|=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x |= _Value2.x; + _Value1.y |= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator&=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x &= _Value2.x; + _Value1.y &= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator>>=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x >>= _Value2.x; + _Value1.y >>= _Value2.y; + *this = _Value1; + return *this; + } + + int_2& operator<<=(const int_2& _Other) __GPU + { + int_2 _Value1 = *this; + int_2 _Value2 = _Other; + _Value1.x <<= _Value2.x; + _Value1.y <<= _Value2.y; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this int_2 as an int_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) int_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this int_2 as an int_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) int_2 rg; + + /// <summary> + /// Returns an int_2 that is composed of element 0, and element 1 of this int_2. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_xy() const __GPU { + return int_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this int_2 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_xy(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this int_2 as an int_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) int_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this int_2 as an int_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) int_2 gr; + + /// <summary> + /// Returns an int_2 that is composed of element 1, and element 0 of this int_2. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_yx() const __GPU { + return int_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this int_2 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_yx(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + }; + + /// <summary> + /// Represent a short vector of 3 int's. + /// </summary> + class int_3 + { + public: + typedef int value_type; + static const int size = 3; + private: + static const _Short_vector_base_type_id _Base_type_id = _Int_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + + public: + /// <summary> + /// Property for accessing element 0 of this int_3 as an int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) int x; + /// <summary> + /// Property for accessing element 0 of this int_3 as an int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) int r; + + /// <summary> + /// Returns element 0 of this int_3. + /// </summary> + /// <returns> + /// Element 0 of this int_3. + /// </returns> + int get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this int_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this int_3. + /// </returns> + int& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this int_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this int_3. + /// </returns> + int& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this int_3 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_x(int _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this int_3 as an int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) int y; + /// <summary> + /// Property for accessing element 1 of this int_3 as an int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) int g; + + /// <summary> + /// Returns element 1 of this int_3. + /// </summary> + /// <returns> + /// Element 1 of this int_3. + /// </returns> + int get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this int_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this int_3. + /// </returns> + int& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this int_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this int_3. + /// </returns> + int& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this int_3 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_y(int _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this int_3 as an int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) int z; + /// <summary> + /// Property for accessing element 2 of this int_3 as an int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) int b; + + /// <summary> + /// Returns element 2 of this int_3. + /// </summary> + /// <returns> + /// Element 2 of this int_3. + /// </returns> + int get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this int_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this int_3. + /// </returns> + int& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this int_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this int_3. + /// </returns> + int& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this int_3 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_z(int _Value) __GPU + { + _M_z = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + int_3() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + int_3(int _V0, int _V1, int _V2) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + int_3(int _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_3(const uint_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_3(const float_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_3(const unorm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_3(const norm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_3(const double_3& _Other) __GPU; + + int_3 operator~() const __GPU + { + int_3 _Value = *this; + return int_3(~_Value.x, ~_Value.y, ~_Value.z); + } + + int_3 operator-() const __GPU + { + int_3 _Value = *this; + return int_3(-_Value.x, -_Value.y, -_Value.z); + } + + int_3& operator++() __GPU + { + int_3 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + *this = _Value; + return *this; + } + + int_3 operator++(int) __GPU + { + int_3 _Result = *this; + ++(*this); + return _Result; + } + + int_3& operator--() __GPU + { + int_3 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + *this = _Value; + return *this; + } + + int_3 operator--(int) __GPU + { + int_3 _Result = *this; + --(*this); + return _Result; + } + + int_3& operator+=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator-=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator*=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator/=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator%=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x %= _Value2.x; + _Value1.y %= _Value2.y; + _Value1.z %= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator^=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x ^= _Value2.x; + _Value1.y ^= _Value2.y; + _Value1.z ^= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator|=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x |= _Value2.x; + _Value1.y |= _Value2.y; + _Value1.z |= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator&=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x &= _Value2.x; + _Value1.y &= _Value2.y; + _Value1.z &= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator>>=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x >>= _Value2.x; + _Value1.y >>= _Value2.y; + _Value1.z >>= _Value2.z; + *this = _Value1; + return *this; + } + + int_3& operator<<=(const int_3& _Other) __GPU + { + int_3 _Value1 = *this; + int_3 _Value2 = _Other; + _Value1.x <<= _Value2.x; + _Value1.y <<= _Value2.y; + _Value1.z <<= _Value2.z; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) int_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) int_2 rg; + + /// <summary> + /// Returns an int_2 that is composed of element 0, and element 1 of this int_3. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_xy() const __GPU { + return int_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this int_3 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_xy(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) int_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) int_2 rb; + + /// <summary> + /// Returns an int_2 that is composed of element 0, and element 2 of this int_3. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_xz() const __GPU { + return int_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this int_3 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_xz(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) int_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) int_2 gr; + + /// <summary> + /// Returns an int_2 that is composed of element 1, and element 0 of this int_3. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_yx() const __GPU { + return int_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this int_3 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_yx(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) int_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) int_2 gb; + + /// <summary> + /// Returns an int_2 that is composed of element 1, and element 2 of this int_3. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_yz() const __GPU { + return int_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this int_3 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_yz(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) int_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) int_2 br; + + /// <summary> + /// Returns an int_2 that is composed of element 2, and element 0 of this int_3. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_zx() const __GPU { + return int_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this int_3 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_zx(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) int_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this int_3 as an int_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) int_2 bg; + + /// <summary> + /// Returns an int_2 that is composed of element 2, and element 1 of this int_3. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_zy() const __GPU { + return int_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this int_3 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_zy(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) int_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) int_3 rgb; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 1, and element 2 of this int_3. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xyz() const __GPU { + return int_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this int_3 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xyz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) int_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) int_3 rbg; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 2, and element 1 of this int_3. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xzy() const __GPU { + return int_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this int_3 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xzy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) int_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) int_3 grb; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 0, and element 2 of this int_3. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_yxz() const __GPU { + return int_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this int_3 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_yxz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) int_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) int_3 gbr; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 2, and element 0 of this int_3. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_yzx() const __GPU { + return int_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this int_3 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_yzx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) int_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) int_3 brg; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 0, and element 1 of this int_3. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zxy() const __GPU { + return int_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this int_3 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zxy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) int_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this int_3 as an int_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) int_3 bgr; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 1, and element 0 of this int_3. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zyx() const __GPU { + return int_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this int_3 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zyx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + }; + + /// <summary> + /// Represent a short vector of 4 int's. + /// </summary> + class int_4 + { + public: + typedef int value_type; + static const int size = 4; + private: + static const _Short_vector_base_type_id _Base_type_id = _Int_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + value_type _M_w; + + public: + /// <summary> + /// Property for accessing element 0 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) int x; + /// <summary> + /// Property for accessing element 0 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) int r; + + /// <summary> + /// Returns element 0 of this int_4. + /// </summary> + /// <returns> + /// Element 0 of this int_4. + /// </returns> + int get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this int_4. + /// </returns> + int& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this int_4. + /// </returns> + int& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this int_4 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_x(int _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) int y; + /// <summary> + /// Property for accessing element 1 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) int g; + + /// <summary> + /// Returns element 1 of this int_4. + /// </summary> + /// <returns> + /// Element 1 of this int_4. + /// </returns> + int get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this int_4. + /// </returns> + int& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this int_4. + /// </returns> + int& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this int_4 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_y(int _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) int z; + /// <summary> + /// Property for accessing element 2 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) int b; + + /// <summary> + /// Returns element 2 of this int_4. + /// </summary> + /// <returns> + /// Element 2 of this int_4. + /// </returns> + int get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this int_4. + /// </returns> + int& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this int_4. + /// </returns> + int& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this int_4 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_z(int _Value) __GPU + { + _M_z = _Value; + } + + /// <summary> + /// Property for accessing element 3 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) int w; + /// <summary> + /// Property for accessing element 3 of this int_4 as an int. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) int a; + + /// <summary> + /// Returns element 3 of this int_4. + /// </summary> + /// <returns> + /// Element 3 of this int_4. + /// </returns> + int get_w() const __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this int_4. + /// </returns> + int& ref_w() __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this int_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this int_4. + /// </returns> + int& ref_a() __GPU { + return _M_w; + } + + /// <summary> + /// Set element 3 of this int_4 with an int. + /// </summary> + /// <param name="_Value"> + /// an int value. + /// </param> + void set_w(int _Value) __GPU + { + _M_w = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + int_4() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + _M_w = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + int_4(int _V0, int _V1, int _V2, int _V3) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + _M_w = _V3; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + int_4(int _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + _M_w = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_4(const uint_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_4(const float_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_4(const unorm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_4(const norm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline int_4(const double_4& _Other) __GPU; + + int_4 operator~() const __GPU + { + int_4 _Value = *this; + return int_4(~_Value.x, ~_Value.y, ~_Value.z, ~_Value.w); + } + + int_4 operator-() const __GPU + { + int_4 _Value = *this; + return int_4(-_Value.x, -_Value.y, -_Value.z, -_Value.w); + } + + int_4& operator++() __GPU + { + int_4 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + ++_Value._M_w; + *this = _Value; + return *this; + } + + int_4 operator++(int) __GPU + { + int_4 _Result = *this; + ++(*this); + return _Result; + } + + int_4& operator--() __GPU + { + int_4 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + --_Value._M_w; + *this = _Value; + return *this; + } + + int_4 operator--(int) __GPU + { + int_4 _Result = *this; + --(*this); + return _Result; + } + + int_4& operator+=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + _Value1.w += _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator-=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + _Value1.w -= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator*=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + _Value1.w *= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator/=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + _Value1.w /= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator%=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x %= _Value2.x; + _Value1.y %= _Value2.y; + _Value1.z %= _Value2.z; + _Value1.w %= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator^=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x ^= _Value2.x; + _Value1.y ^= _Value2.y; + _Value1.z ^= _Value2.z; + _Value1.w ^= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator|=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x |= _Value2.x; + _Value1.y |= _Value2.y; + _Value1.z |= _Value2.z; + _Value1.w |= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator&=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x &= _Value2.x; + _Value1.y &= _Value2.y; + _Value1.z &= _Value2.z; + _Value1.w &= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator>>=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x >>= _Value2.x; + _Value1.y >>= _Value2.y; + _Value1.z >>= _Value2.z; + _Value1.w >>= _Value2.w; + *this = _Value1; + return *this; + } + + int_4& operator<<=(const int_4& _Other) __GPU + { + int_4 _Value1 = *this; + int_4 _Value2 = _Other; + _Value1.x <<= _Value2.x; + _Value1.y <<= _Value2.y; + _Value1.z <<= _Value2.z; + _Value1.w <<= _Value2.w; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) int_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) int_2 rg; + + /// <summary> + /// Returns an int_2 that is composed of element 0, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_xy() const __GPU { + return int_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_xy(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) int_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) int_2 rb; + + /// <summary> + /// Returns an int_2 that is composed of element 0, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_xz() const __GPU { + return int_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_xz(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 3 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) int_2 xw; + /// <summary> + /// Property for accessing element 0, and 3 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) int_2 ra; + + /// <summary> + /// Returns an int_2 that is composed of element 0, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_xw() const __GPU { + return int_2(_M_x,_M_w); + } + + /// <summary> + /// Set element 0, and 3 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_xw(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) int_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) int_2 gr; + + /// <summary> + /// Returns an int_2 that is composed of element 1, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_yx() const __GPU { + return int_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_yx(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) int_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) int_2 gb; + + /// <summary> + /// Returns an int_2 that is composed of element 1, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_yz() const __GPU { + return int_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_yz(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 3 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) int_2 yw; + /// <summary> + /// Property for accessing element 1, and 3 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) int_2 ga; + + /// <summary> + /// Returns an int_2 that is composed of element 1, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_yw() const __GPU { + return int_2(_M_y,_M_w); + } + + /// <summary> + /// Set element 1, and 3 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_yw(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) int_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) int_2 br; + + /// <summary> + /// Returns an int_2 that is composed of element 2, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_zx() const __GPU { + return int_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_zx(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) int_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) int_2 bg; + + /// <summary> + /// Returns an int_2 that is composed of element 2, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_zy() const __GPU { + return int_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_zy(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 3 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) int_2 zw; + /// <summary> + /// Property for accessing element 2, and 3 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) int_2 ba; + + /// <summary> + /// Returns an int_2 that is composed of element 2, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_zw() const __GPU { + return int_2(_M_z,_M_w); + } + + /// <summary> + /// Set element 2, and 3 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_zw(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 0 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) int_2 wx; + /// <summary> + /// Property for accessing element 3, and 0 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) int_2 ar; + + /// <summary> + /// Returns an int_2 that is composed of element 3, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_wx() const __GPU { + return int_2(_M_w,_M_x); + } + + /// <summary> + /// Set element 3, and 0 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_wx(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 1 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) int_2 wy; + /// <summary> + /// Property for accessing element 3, and 1 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) int_2 ag; + + /// <summary> + /// Returns an int_2 that is composed of element 3, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_wy() const __GPU { + return int_2(_M_w,_M_y); + } + + /// <summary> + /// Set element 3, and 1 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_wy(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 2 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) int_2 wz; + /// <summary> + /// Property for accessing element 3, and 2 of this int_4 as an int_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) int_2 ab; + + /// <summary> + /// Returns an int_2 that is composed of element 3, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_2. + /// </returns> + int_2 get_wz() const __GPU { + return int_2(_M_w,_M_z); + } + + /// <summary> + /// Set element 3, and 2 of this int_4 with an int_2. + /// </summary> + /// <param name="_Value"> + /// an int_2 value. + /// </param> + void set_wz(const int_2& _Value) __GPU + { + int_2 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) int_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) int_3 rgb; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 1, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xyz() const __GPU { + return int_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xyz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) int_3 xyw; + /// <summary> + /// Property for accessing element 0, 1, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) int_3 rga; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 1, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xyw() const __GPU { + return int_3(_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 1, and 3 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xyw(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) int_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) int_3 rbg; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 2, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xzy() const __GPU { + return int_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xzy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) int_3 xzw; + /// <summary> + /// Property for accessing element 0, 2, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) int_3 rba; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 2, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xzw() const __GPU { + return int_3(_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 2, and 3 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xzw(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) int_3 xwy; + /// <summary> + /// Property for accessing element 0, 3, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) int_3 rag; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 3, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xwy() const __GPU { + return int_3(_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 3, and 1 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xwy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) int_3 xwz; + /// <summary> + /// Property for accessing element 0, 3, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) int_3 rab; + + /// <summary> + /// Returns an int_3 that is composed of element 0, element 3, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_xwz() const __GPU { + return int_3(_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 3, and 2 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_xwz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) int_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) int_3 grb; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 0, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_yxz() const __GPU { + return int_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_yxz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) int_3 yxw; + /// <summary> + /// Property for accessing element 1, 0, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) int_3 gra; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 0, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_yxw() const __GPU { + return int_3(_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 0, and 3 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_yxw(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) int_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) int_3 gbr; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 2, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_yzx() const __GPU { + return int_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_yzx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) int_3 yzw; + /// <summary> + /// Property for accessing element 1, 2, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) int_3 gba; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 2, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_yzw() const __GPU { + return int_3(_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 2, and 3 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_yzw(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) int_3 ywx; + /// <summary> + /// Property for accessing element 1, 3, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) int_3 gar; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 3, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_ywx() const __GPU { + return int_3(_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 3, and 0 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_ywx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) int_3 ywz; + /// <summary> + /// Property for accessing element 1, 3, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) int_3 gab; + + /// <summary> + /// Returns an int_3 that is composed of element 1, element 3, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_ywz() const __GPU { + return int_3(_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 3, and 2 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_ywz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) int_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) int_3 brg; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 0, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zxy() const __GPU { + return int_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zxy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) int_3 zxw; + /// <summary> + /// Property for accessing element 2, 0, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) int_3 bra; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 0, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zxw() const __GPU { + return int_3(_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 0, and 3 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zxw(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) int_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) int_3 bgr; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 1, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zyx() const __GPU { + return int_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zyx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) int_3 zyw; + /// <summary> + /// Property for accessing element 2, 1, and 3 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) int_3 bga; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 1, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zyw() const __GPU { + return int_3(_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 1, and 3 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zyw(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) int_3 zwx; + /// <summary> + /// Property for accessing element 2, 3, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) int_3 bar; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 3, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zwx() const __GPU { + return int_3(_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 3, and 0 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zwx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) int_3 zwy; + /// <summary> + /// Property for accessing element 2, 3, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) int_3 bag; + + /// <summary> + /// Returns an int_3 that is composed of element 2, element 3, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_zwy() const __GPU { + return int_3(_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 3, and 1 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_zwy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) int_3 wxy; + /// <summary> + /// Property for accessing element 3, 0, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) int_3 arg; + + /// <summary> + /// Returns an int_3 that is composed of element 3, element 0, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_wxy() const __GPU { + return int_3(_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 0, and 1 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_wxy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) int_3 wxz; + /// <summary> + /// Property for accessing element 3, 0, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) int_3 arb; + + /// <summary> + /// Returns an int_3 that is composed of element 3, element 0, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_wxz() const __GPU { + return int_3(_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 0, and 2 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_wxz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) int_3 wyx; + /// <summary> + /// Property for accessing element 3, 1, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) int_3 agr; + + /// <summary> + /// Returns an int_3 that is composed of element 3, element 1, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_wyx() const __GPU { + return int_3(_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 1, and 0 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_wyx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) int_3 wyz; + /// <summary> + /// Property for accessing element 3, 1, and 2 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) int_3 agb; + + /// <summary> + /// Returns an int_3 that is composed of element 3, element 1, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_wyz() const __GPU { + return int_3(_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 1, and 2 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_wyz(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) int_3 wzx; + /// <summary> + /// Property for accessing element 3, 2, and 0 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) int_3 abr; + + /// <summary> + /// Returns an int_3 that is composed of element 3, element 2, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_wzx() const __GPU { + return int_3(_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 2, and 0 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_wzx(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) int_3 wzy; + /// <summary> + /// Property for accessing element 3, 2, and 1 of this int_4 as an int_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) int_3 abg; + + /// <summary> + /// Returns an int_3 that is composed of element 3, element 2, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_3. + /// </returns> + int_3 get_wzy() const __GPU { + return int_3(_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 2, and 1 of this int_4 with an int_3. + /// </summary> + /// <param name="_Value"> + /// an int_3 value. + /// </param> + void set_wzy(const int_3& _Value) __GPU + { + int_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) int_4 xyzw; + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) int_4 rgba; + + /// <summary> + /// Returns an int_4 that is composed of element 0, element 1, element 2, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_xyzw() const __GPU { + return int_4(_M_x,_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 1, 2, and 3 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_xyzw(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) int_4 xywz; + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) int_4 rgab; + + /// <summary> + /// Returns an int_4 that is composed of element 0, element 1, element 3, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_xywz() const __GPU { + return int_4(_M_x,_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 1, 3, and 2 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_xywz(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) int_4 xzyw; + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) int_4 rbga; + + /// <summary> + /// Returns an int_4 that is composed of element 0, element 2, element 1, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_xzyw() const __GPU { + return int_4(_M_x,_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 2, 1, and 3 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_xzyw(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) int_4 xzwy; + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) int_4 rbag; + + /// <summary> + /// Returns an int_4 that is composed of element 0, element 2, element 3, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_xzwy() const __GPU { + return int_4(_M_x,_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 2, 3, and 1 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_xzwy(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) int_4 xwyz; + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) int_4 ragb; + + /// <summary> + /// Returns an int_4 that is composed of element 0, element 3, element 1, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_xwyz() const __GPU { + return int_4(_M_x,_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 3, 1, and 2 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_xwyz(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) int_4 xwzy; + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) int_4 rabg; + + /// <summary> + /// Returns an int_4 that is composed of element 0, element 3, element 2, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_xwzy() const __GPU { + return int_4(_M_x,_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 3, 2, and 1 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_xwzy(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) int_4 yxzw; + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) int_4 grba; + + /// <summary> + /// Returns an int_4 that is composed of element 1, element 0, element 2, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_yxzw() const __GPU { + return int_4(_M_y,_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 0, 2, and 3 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_yxzw(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) int_4 yxwz; + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) int_4 grab; + + /// <summary> + /// Returns an int_4 that is composed of element 1, element 0, element 3, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_yxwz() const __GPU { + return int_4(_M_y,_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 0, 3, and 2 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_yxwz(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) int_4 yzxw; + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) int_4 gbra; + + /// <summary> + /// Returns an int_4 that is composed of element 1, element 2, element 0, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_yzxw() const __GPU { + return int_4(_M_y,_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 2, 0, and 3 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_yzxw(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) int_4 yzwx; + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) int_4 gbar; + + /// <summary> + /// Returns an int_4 that is composed of element 1, element 2, element 3, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_yzwx() const __GPU { + return int_4(_M_y,_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 2, 3, and 0 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_yzwx(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) int_4 ywxz; + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) int_4 garb; + + /// <summary> + /// Returns an int_4 that is composed of element 1, element 3, element 0, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_ywxz() const __GPU { + return int_4(_M_y,_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 3, 0, and 2 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_ywxz(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) int_4 ywzx; + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) int_4 gabr; + + /// <summary> + /// Returns an int_4 that is composed of element 1, element 3, element 2, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_ywzx() const __GPU { + return int_4(_M_y,_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 3, 2, and 0 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_ywzx(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) int_4 zxyw; + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) int_4 brga; + + /// <summary> + /// Returns an int_4 that is composed of element 2, element 0, element 1, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_zxyw() const __GPU { + return int_4(_M_z,_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 0, 1, and 3 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_zxyw(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) int_4 zxwy; + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) int_4 brag; + + /// <summary> + /// Returns an int_4 that is composed of element 2, element 0, element 3, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_zxwy() const __GPU { + return int_4(_M_z,_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 0, 3, and 1 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_zxwy(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) int_4 zyxw; + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) int_4 bgra; + + /// <summary> + /// Returns an int_4 that is composed of element 2, element 1, element 0, and element 3 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_zyxw() const __GPU { + return int_4(_M_z,_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 1, 0, and 3 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_zyxw(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) int_4 zywx; + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) int_4 bgar; + + /// <summary> + /// Returns an int_4 that is composed of element 2, element 1, element 3, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_zywx() const __GPU { + return int_4(_M_z,_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 1, 3, and 0 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_zywx(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) int_4 zwxy; + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) int_4 barg; + + /// <summary> + /// Returns an int_4 that is composed of element 2, element 3, element 0, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_zwxy() const __GPU { + return int_4(_M_z,_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 3, 0, and 1 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_zwxy(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) int_4 zwyx; + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) int_4 bagr; + + /// <summary> + /// Returns an int_4 that is composed of element 2, element 3, element 1, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_zwyx() const __GPU { + return int_4(_M_z,_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 3, 1, and 0 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_zwyx(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) int_4 wxyz; + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) int_4 argb; + + /// <summary> + /// Returns an int_4 that is composed of element 3, element 0, element 1, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_wxyz() const __GPU { + return int_4(_M_w,_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 0, 1, and 2 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_wxyz(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) int_4 wxzy; + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) int_4 arbg; + + /// <summary> + /// Returns an int_4 that is composed of element 3, element 0, element 2, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_wxzy() const __GPU { + return int_4(_M_w,_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 0, 2, and 1 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_wxzy(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) int_4 wyxz; + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) int_4 agrb; + + /// <summary> + /// Returns an int_4 that is composed of element 3, element 1, element 0, and element 2 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_wyxz() const __GPU { + return int_4(_M_w,_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 1, 0, and 2 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_wyxz(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) int_4 wyzx; + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) int_4 agbr; + + /// <summary> + /// Returns an int_4 that is composed of element 3, element 1, element 2, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_wyzx() const __GPU { + return int_4(_M_w,_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 1, 2, and 0 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_wyzx(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) int_4 wzxy; + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) int_4 abrg; + + /// <summary> + /// Returns an int_4 that is composed of element 3, element 2, element 0, and element 1 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_wzxy() const __GPU { + return int_4(_M_w,_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 2, 0, and 1 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_wzxy(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) int_4 wzyx; + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this int_4 as an int_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) int_4 abgr; + + /// <summary> + /// Returns an int_4 that is composed of element 3, element 2, element 1, and element 0 of this int_4. + /// </summary> + /// <returns> + /// an int_4. + /// </returns> + int_4 get_wzyx() const __GPU { + return int_4(_M_w,_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 2, 1, and 0 of this int_4 with an int_4. + /// </summary> + /// <param name="_Value"> + /// an int_4 value. + /// </param> + void set_wzyx(const int_4& _Value) __GPU + { + int_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + }; + + /// <summary> + /// Represent a short vector of 2 float's. + /// </summary> + class float_2 + { + public: + typedef float value_type; + static const int size = 2; + private: + static const _Short_vector_base_type_id _Base_type_id = _Float_type; + private: + value_type _M_x; + value_type _M_y; + + public: + /// <summary> + /// Property for accessing element 0 of this float_2 as a float. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) float x; + /// <summary> + /// Property for accessing element 0 of this float_2 as a float. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) float r; + + /// <summary> + /// Returns element 0 of this float_2. + /// </summary> + /// <returns> + /// Element 0 of this float_2. + /// </returns> + float get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this float_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this float_2. + /// </returns> + float& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this float_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this float_2. + /// </returns> + float& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this float_2 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_x(float _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this float_2 as a float. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) float y; + /// <summary> + /// Property for accessing element 1 of this float_2 as a float. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) float g; + + /// <summary> + /// Returns element 1 of this float_2. + /// </summary> + /// <returns> + /// Element 1 of this float_2. + /// </returns> + float get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this float_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this float_2. + /// </returns> + float& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this float_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this float_2. + /// </returns> + float& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this float_2 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_y(float _Value) __GPU + { + _M_y = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + float_2() __GPU + { + _M_x = 0; + _M_y = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + float_2(float _V0, float _V1) __GPU + { + _M_x = _V0; + _M_y = _V1; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + float_2(float _V) __GPU + { + _M_x = _V; + _M_y = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_2(const uint_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_2(const int_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_2(const unorm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_2(const norm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_2(const double_2& _Other) __GPU; + + float_2 operator-() const __GPU + { + float_2 _Value = *this; + return float_2(-_Value.x, -_Value.y); + } + + float_2& operator++() __GPU + { + float_2 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + *this = _Value; + return *this; + } + + float_2 operator++(int) __GPU + { + float_2 _Result = *this; + ++(*this); + return _Result; + } + + float_2& operator--() __GPU + { + float_2 _Value = *this; + --_Value._M_x; + --_Value._M_y; + *this = _Value; + return *this; + } + + float_2 operator--(int) __GPU + { + float_2 _Result = *this; + --(*this); + return _Result; + } + + float_2& operator+=(const float_2& _Other) __GPU + { + float_2 _Value1 = *this; + float_2 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + *this = _Value1; + return *this; + } + + float_2& operator-=(const float_2& _Other) __GPU + { + float_2 _Value1 = *this; + float_2 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + *this = _Value1; + return *this; + } + + float_2& operator*=(const float_2& _Other) __GPU + { + float_2 _Value1 = *this; + float_2 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + *this = _Value1; + return *this; + } + + float_2& operator/=(const float_2& _Other) __GPU + { + float_2 _Value1 = *this; + float_2 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this float_2 as a float_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) float_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this float_2 as a float_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) float_2 rg; + + /// <summary> + /// Returns a float_2 that is composed of element 0, and element 1 of this float_2. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_xy() const __GPU { + return float_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this float_2 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_xy(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this float_2 as a float_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) float_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this float_2 as a float_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) float_2 gr; + + /// <summary> + /// Returns a float_2 that is composed of element 1, and element 0 of this float_2. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_yx() const __GPU { + return float_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this float_2 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_yx(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + }; + + /// <summary> + /// Represent a short vector of 3 float's. + /// </summary> + class float_3 + { + public: + typedef float value_type; + static const int size = 3; + private: + static const _Short_vector_base_type_id _Base_type_id = _Float_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + + public: + /// <summary> + /// Property for accessing element 0 of this float_3 as a float. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) float x; + /// <summary> + /// Property for accessing element 0 of this float_3 as a float. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) float r; + + /// <summary> + /// Returns element 0 of this float_3. + /// </summary> + /// <returns> + /// Element 0 of this float_3. + /// </returns> + float get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this float_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this float_3. + /// </returns> + float& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this float_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this float_3. + /// </returns> + float& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this float_3 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_x(float _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this float_3 as a float. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) float y; + /// <summary> + /// Property for accessing element 1 of this float_3 as a float. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) float g; + + /// <summary> + /// Returns element 1 of this float_3. + /// </summary> + /// <returns> + /// Element 1 of this float_3. + /// </returns> + float get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this float_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this float_3. + /// </returns> + float& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this float_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this float_3. + /// </returns> + float& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this float_3 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_y(float _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this float_3 as a float. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) float z; + /// <summary> + /// Property for accessing element 2 of this float_3 as a float. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) float b; + + /// <summary> + /// Returns element 2 of this float_3. + /// </summary> + /// <returns> + /// Element 2 of this float_3. + /// </returns> + float get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this float_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this float_3. + /// </returns> + float& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this float_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this float_3. + /// </returns> + float& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this float_3 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_z(float _Value) __GPU + { + _M_z = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + float_3() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + float_3(float _V0, float _V1, float _V2) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + float_3(float _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_3(const uint_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_3(const int_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_3(const unorm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_3(const norm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_3(const double_3& _Other) __GPU; + + float_3 operator-() const __GPU + { + float_3 _Value = *this; + return float_3(-_Value.x, -_Value.y, -_Value.z); + } + + float_3& operator++() __GPU + { + float_3 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + *this = _Value; + return *this; + } + + float_3 operator++(int) __GPU + { + float_3 _Result = *this; + ++(*this); + return _Result; + } + + float_3& operator--() __GPU + { + float_3 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + *this = _Value; + return *this; + } + + float_3 operator--(int) __GPU + { + float_3 _Result = *this; + --(*this); + return _Result; + } + + float_3& operator+=(const float_3& _Other) __GPU + { + float_3 _Value1 = *this; + float_3 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + *this = _Value1; + return *this; + } + + float_3& operator-=(const float_3& _Other) __GPU + { + float_3 _Value1 = *this; + float_3 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + *this = _Value1; + return *this; + } + + float_3& operator*=(const float_3& _Other) __GPU + { + float_3 _Value1 = *this; + float_3 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + *this = _Value1; + return *this; + } + + float_3& operator/=(const float_3& _Other) __GPU + { + float_3 _Value1 = *this; + float_3 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) float_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) float_2 rg; + + /// <summary> + /// Returns a float_2 that is composed of element 0, and element 1 of this float_3. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_xy() const __GPU { + return float_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this float_3 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_xy(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) float_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) float_2 rb; + + /// <summary> + /// Returns a float_2 that is composed of element 0, and element 2 of this float_3. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_xz() const __GPU { + return float_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this float_3 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_xz(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) float_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) float_2 gr; + + /// <summary> + /// Returns a float_2 that is composed of element 1, and element 0 of this float_3. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_yx() const __GPU { + return float_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this float_3 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_yx(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) float_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) float_2 gb; + + /// <summary> + /// Returns a float_2 that is composed of element 1, and element 2 of this float_3. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_yz() const __GPU { + return float_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this float_3 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_yz(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) float_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) float_2 br; + + /// <summary> + /// Returns a float_2 that is composed of element 2, and element 0 of this float_3. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_zx() const __GPU { + return float_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this float_3 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_zx(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) float_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this float_3 as a float_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) float_2 bg; + + /// <summary> + /// Returns a float_2 that is composed of element 2, and element 1 of this float_3. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_zy() const __GPU { + return float_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this float_3 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_zy(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) float_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) float_3 rgb; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 1, and element 2 of this float_3. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xyz() const __GPU { + return float_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this float_3 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xyz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) float_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) float_3 rbg; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 2, and element 1 of this float_3. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xzy() const __GPU { + return float_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this float_3 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xzy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) float_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) float_3 grb; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 0, and element 2 of this float_3. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_yxz() const __GPU { + return float_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this float_3 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_yxz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) float_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) float_3 gbr; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 2, and element 0 of this float_3. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_yzx() const __GPU { + return float_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this float_3 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_yzx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) float_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) float_3 brg; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 0, and element 1 of this float_3. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zxy() const __GPU { + return float_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this float_3 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zxy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) float_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this float_3 as a float_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) float_3 bgr; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 1, and element 0 of this float_3. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zyx() const __GPU { + return float_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this float_3 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zyx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + }; + + /// <summary> + /// Represent a short vector of 4 float's. + /// </summary> + class float_4 + { + public: + typedef float value_type; + static const int size = 4; + private: + static const _Short_vector_base_type_id _Base_type_id = _Float_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + value_type _M_w; + + public: + /// <summary> + /// Property for accessing element 0 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) float x; + /// <summary> + /// Property for accessing element 0 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) float r; + + /// <summary> + /// Returns element 0 of this float_4. + /// </summary> + /// <returns> + /// Element 0 of this float_4. + /// </returns> + float get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this float_4. + /// </returns> + float& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this float_4. + /// </returns> + float& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this float_4 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_x(float _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) float y; + /// <summary> + /// Property for accessing element 1 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) float g; + + /// <summary> + /// Returns element 1 of this float_4. + /// </summary> + /// <returns> + /// Element 1 of this float_4. + /// </returns> + float get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this float_4. + /// </returns> + float& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this float_4. + /// </returns> + float& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this float_4 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_y(float _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) float z; + /// <summary> + /// Property for accessing element 2 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) float b; + + /// <summary> + /// Returns element 2 of this float_4. + /// </summary> + /// <returns> + /// Element 2 of this float_4. + /// </returns> + float get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this float_4. + /// </returns> + float& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this float_4. + /// </returns> + float& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this float_4 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_z(float _Value) __GPU + { + _M_z = _Value; + } + + /// <summary> + /// Property for accessing element 3 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) float w; + /// <summary> + /// Property for accessing element 3 of this float_4 as a float. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) float a; + + /// <summary> + /// Returns element 3 of this float_4. + /// </summary> + /// <returns> + /// Element 3 of this float_4. + /// </returns> + float get_w() const __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this float_4. + /// </returns> + float& ref_w() __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this float_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this float_4. + /// </returns> + float& ref_a() __GPU { + return _M_w; + } + + /// <summary> + /// Set element 3 of this float_4 with a float. + /// </summary> + /// <param name="_Value"> + /// a float value. + /// </param> + void set_w(float _Value) __GPU + { + _M_w = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + float_4() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + _M_w = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + float_4(float _V0, float _V1, float _V2, float _V3) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + _M_w = _V3; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + float_4(float _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + _M_w = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_4(const uint_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_4(const int_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_4(const unorm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_4(const norm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline float_4(const double_4& _Other) __GPU; + + float_4 operator-() const __GPU + { + float_4 _Value = *this; + return float_4(-_Value.x, -_Value.y, -_Value.z, -_Value.w); + } + + float_4& operator++() __GPU + { + float_4 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + ++_Value._M_w; + *this = _Value; + return *this; + } + + float_4 operator++(int) __GPU + { + float_4 _Result = *this; + ++(*this); + return _Result; + } + + float_4& operator--() __GPU + { + float_4 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + --_Value._M_w; + *this = _Value; + return *this; + } + + float_4 operator--(int) __GPU + { + float_4 _Result = *this; + --(*this); + return _Result; + } + + float_4& operator+=(const float_4& _Other) __GPU + { + float_4 _Value1 = *this; + float_4 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + _Value1.w += _Value2.w; + *this = _Value1; + return *this; + } + + float_4& operator-=(const float_4& _Other) __GPU + { + float_4 _Value1 = *this; + float_4 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + _Value1.w -= _Value2.w; + *this = _Value1; + return *this; + } + + float_4& operator*=(const float_4& _Other) __GPU + { + float_4 _Value1 = *this; + float_4 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + _Value1.w *= _Value2.w; + *this = _Value1; + return *this; + } + + float_4& operator/=(const float_4& _Other) __GPU + { + float_4 _Value1 = *this; + float_4 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + _Value1.w /= _Value2.w; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) float_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) float_2 rg; + + /// <summary> + /// Returns a float_2 that is composed of element 0, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_xy() const __GPU { + return float_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_xy(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) float_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) float_2 rb; + + /// <summary> + /// Returns a float_2 that is composed of element 0, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_xz() const __GPU { + return float_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_xz(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 3 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) float_2 xw; + /// <summary> + /// Property for accessing element 0, and 3 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) float_2 ra; + + /// <summary> + /// Returns a float_2 that is composed of element 0, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_xw() const __GPU { + return float_2(_M_x,_M_w); + } + + /// <summary> + /// Set element 0, and 3 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_xw(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) float_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) float_2 gr; + + /// <summary> + /// Returns a float_2 that is composed of element 1, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_yx() const __GPU { + return float_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_yx(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) float_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) float_2 gb; + + /// <summary> + /// Returns a float_2 that is composed of element 1, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_yz() const __GPU { + return float_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_yz(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 3 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) float_2 yw; + /// <summary> + /// Property for accessing element 1, and 3 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) float_2 ga; + + /// <summary> + /// Returns a float_2 that is composed of element 1, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_yw() const __GPU { + return float_2(_M_y,_M_w); + } + + /// <summary> + /// Set element 1, and 3 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_yw(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) float_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) float_2 br; + + /// <summary> + /// Returns a float_2 that is composed of element 2, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_zx() const __GPU { + return float_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_zx(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) float_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) float_2 bg; + + /// <summary> + /// Returns a float_2 that is composed of element 2, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_zy() const __GPU { + return float_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_zy(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 3 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) float_2 zw; + /// <summary> + /// Property for accessing element 2, and 3 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) float_2 ba; + + /// <summary> + /// Returns a float_2 that is composed of element 2, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_zw() const __GPU { + return float_2(_M_z,_M_w); + } + + /// <summary> + /// Set element 2, and 3 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_zw(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 0 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) float_2 wx; + /// <summary> + /// Property for accessing element 3, and 0 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) float_2 ar; + + /// <summary> + /// Returns a float_2 that is composed of element 3, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_wx() const __GPU { + return float_2(_M_w,_M_x); + } + + /// <summary> + /// Set element 3, and 0 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_wx(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 1 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) float_2 wy; + /// <summary> + /// Property for accessing element 3, and 1 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) float_2 ag; + + /// <summary> + /// Returns a float_2 that is composed of element 3, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_wy() const __GPU { + return float_2(_M_w,_M_y); + } + + /// <summary> + /// Set element 3, and 1 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_wy(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 2 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) float_2 wz; + /// <summary> + /// Property for accessing element 3, and 2 of this float_4 as a float_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) float_2 ab; + + /// <summary> + /// Returns a float_2 that is composed of element 3, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_2. + /// </returns> + float_2 get_wz() const __GPU { + return float_2(_M_w,_M_z); + } + + /// <summary> + /// Set element 3, and 2 of this float_4 with a float_2. + /// </summary> + /// <param name="_Value"> + /// a float_2 value. + /// </param> + void set_wz(const float_2& _Value) __GPU + { + float_2 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) float_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) float_3 rgb; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 1, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xyz() const __GPU { + return float_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xyz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) float_3 xyw; + /// <summary> + /// Property for accessing element 0, 1, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) float_3 rga; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 1, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xyw() const __GPU { + return float_3(_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 1, and 3 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xyw(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) float_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) float_3 rbg; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 2, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xzy() const __GPU { + return float_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xzy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) float_3 xzw; + /// <summary> + /// Property for accessing element 0, 2, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) float_3 rba; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 2, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xzw() const __GPU { + return float_3(_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 2, and 3 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xzw(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) float_3 xwy; + /// <summary> + /// Property for accessing element 0, 3, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) float_3 rag; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 3, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xwy() const __GPU { + return float_3(_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 3, and 1 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xwy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) float_3 xwz; + /// <summary> + /// Property for accessing element 0, 3, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) float_3 rab; + + /// <summary> + /// Returns a float_3 that is composed of element 0, element 3, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_xwz() const __GPU { + return float_3(_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 3, and 2 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_xwz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) float_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) float_3 grb; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 0, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_yxz() const __GPU { + return float_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_yxz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) float_3 yxw; + /// <summary> + /// Property for accessing element 1, 0, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) float_3 gra; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 0, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_yxw() const __GPU { + return float_3(_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 0, and 3 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_yxw(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) float_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) float_3 gbr; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 2, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_yzx() const __GPU { + return float_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_yzx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) float_3 yzw; + /// <summary> + /// Property for accessing element 1, 2, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) float_3 gba; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 2, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_yzw() const __GPU { + return float_3(_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 2, and 3 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_yzw(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) float_3 ywx; + /// <summary> + /// Property for accessing element 1, 3, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) float_3 gar; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 3, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_ywx() const __GPU { + return float_3(_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 3, and 0 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_ywx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) float_3 ywz; + /// <summary> + /// Property for accessing element 1, 3, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) float_3 gab; + + /// <summary> + /// Returns a float_3 that is composed of element 1, element 3, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_ywz() const __GPU { + return float_3(_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 3, and 2 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_ywz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) float_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) float_3 brg; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 0, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zxy() const __GPU { + return float_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zxy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) float_3 zxw; + /// <summary> + /// Property for accessing element 2, 0, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) float_3 bra; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 0, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zxw() const __GPU { + return float_3(_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 0, and 3 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zxw(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) float_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) float_3 bgr; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 1, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zyx() const __GPU { + return float_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zyx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) float_3 zyw; + /// <summary> + /// Property for accessing element 2, 1, and 3 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) float_3 bga; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 1, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zyw() const __GPU { + return float_3(_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 1, and 3 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zyw(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) float_3 zwx; + /// <summary> + /// Property for accessing element 2, 3, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) float_3 bar; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 3, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zwx() const __GPU { + return float_3(_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 3, and 0 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zwx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) float_3 zwy; + /// <summary> + /// Property for accessing element 2, 3, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) float_3 bag; + + /// <summary> + /// Returns a float_3 that is composed of element 2, element 3, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_zwy() const __GPU { + return float_3(_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 3, and 1 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_zwy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) float_3 wxy; + /// <summary> + /// Property for accessing element 3, 0, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) float_3 arg; + + /// <summary> + /// Returns a float_3 that is composed of element 3, element 0, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_wxy() const __GPU { + return float_3(_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 0, and 1 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_wxy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) float_3 wxz; + /// <summary> + /// Property for accessing element 3, 0, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) float_3 arb; + + /// <summary> + /// Returns a float_3 that is composed of element 3, element 0, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_wxz() const __GPU { + return float_3(_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 0, and 2 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_wxz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) float_3 wyx; + /// <summary> + /// Property for accessing element 3, 1, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) float_3 agr; + + /// <summary> + /// Returns a float_3 that is composed of element 3, element 1, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_wyx() const __GPU { + return float_3(_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 1, and 0 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_wyx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) float_3 wyz; + /// <summary> + /// Property for accessing element 3, 1, and 2 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) float_3 agb; + + /// <summary> + /// Returns a float_3 that is composed of element 3, element 1, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_wyz() const __GPU { + return float_3(_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 1, and 2 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_wyz(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) float_3 wzx; + /// <summary> + /// Property for accessing element 3, 2, and 0 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) float_3 abr; + + /// <summary> + /// Returns a float_3 that is composed of element 3, element 2, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_wzx() const __GPU { + return float_3(_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 2, and 0 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_wzx(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) float_3 wzy; + /// <summary> + /// Property for accessing element 3, 2, and 1 of this float_4 as a float_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) float_3 abg; + + /// <summary> + /// Returns a float_3 that is composed of element 3, element 2, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_3. + /// </returns> + float_3 get_wzy() const __GPU { + return float_3(_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 2, and 1 of this float_4 with a float_3. + /// </summary> + /// <param name="_Value"> + /// a float_3 value. + /// </param> + void set_wzy(const float_3& _Value) __GPU + { + float_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) float_4 xyzw; + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) float_4 rgba; + + /// <summary> + /// Returns a float_4 that is composed of element 0, element 1, element 2, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_xyzw() const __GPU { + return float_4(_M_x,_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 1, 2, and 3 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_xyzw(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) float_4 xywz; + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) float_4 rgab; + + /// <summary> + /// Returns a float_4 that is composed of element 0, element 1, element 3, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_xywz() const __GPU { + return float_4(_M_x,_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 1, 3, and 2 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_xywz(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) float_4 xzyw; + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) float_4 rbga; + + /// <summary> + /// Returns a float_4 that is composed of element 0, element 2, element 1, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_xzyw() const __GPU { + return float_4(_M_x,_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 2, 1, and 3 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_xzyw(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) float_4 xzwy; + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) float_4 rbag; + + /// <summary> + /// Returns a float_4 that is composed of element 0, element 2, element 3, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_xzwy() const __GPU { + return float_4(_M_x,_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 2, 3, and 1 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_xzwy(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) float_4 xwyz; + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) float_4 ragb; + + /// <summary> + /// Returns a float_4 that is composed of element 0, element 3, element 1, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_xwyz() const __GPU { + return float_4(_M_x,_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 3, 1, and 2 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_xwyz(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) float_4 xwzy; + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) float_4 rabg; + + /// <summary> + /// Returns a float_4 that is composed of element 0, element 3, element 2, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_xwzy() const __GPU { + return float_4(_M_x,_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 3, 2, and 1 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_xwzy(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) float_4 yxzw; + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) float_4 grba; + + /// <summary> + /// Returns a float_4 that is composed of element 1, element 0, element 2, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_yxzw() const __GPU { + return float_4(_M_y,_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 0, 2, and 3 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_yxzw(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) float_4 yxwz; + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) float_4 grab; + + /// <summary> + /// Returns a float_4 that is composed of element 1, element 0, element 3, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_yxwz() const __GPU { + return float_4(_M_y,_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 0, 3, and 2 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_yxwz(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) float_4 yzxw; + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) float_4 gbra; + + /// <summary> + /// Returns a float_4 that is composed of element 1, element 2, element 0, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_yzxw() const __GPU { + return float_4(_M_y,_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 2, 0, and 3 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_yzxw(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) float_4 yzwx; + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) float_4 gbar; + + /// <summary> + /// Returns a float_4 that is composed of element 1, element 2, element 3, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_yzwx() const __GPU { + return float_4(_M_y,_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 2, 3, and 0 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_yzwx(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) float_4 ywxz; + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) float_4 garb; + + /// <summary> + /// Returns a float_4 that is composed of element 1, element 3, element 0, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_ywxz() const __GPU { + return float_4(_M_y,_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 3, 0, and 2 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_ywxz(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) float_4 ywzx; + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) float_4 gabr; + + /// <summary> + /// Returns a float_4 that is composed of element 1, element 3, element 2, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_ywzx() const __GPU { + return float_4(_M_y,_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 3, 2, and 0 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_ywzx(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) float_4 zxyw; + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) float_4 brga; + + /// <summary> + /// Returns a float_4 that is composed of element 2, element 0, element 1, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_zxyw() const __GPU { + return float_4(_M_z,_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 0, 1, and 3 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_zxyw(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) float_4 zxwy; + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) float_4 brag; + + /// <summary> + /// Returns a float_4 that is composed of element 2, element 0, element 3, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_zxwy() const __GPU { + return float_4(_M_z,_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 0, 3, and 1 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_zxwy(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) float_4 zyxw; + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) float_4 bgra; + + /// <summary> + /// Returns a float_4 that is composed of element 2, element 1, element 0, and element 3 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_zyxw() const __GPU { + return float_4(_M_z,_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 1, 0, and 3 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_zyxw(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) float_4 zywx; + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) float_4 bgar; + + /// <summary> + /// Returns a float_4 that is composed of element 2, element 1, element 3, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_zywx() const __GPU { + return float_4(_M_z,_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 1, 3, and 0 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_zywx(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) float_4 zwxy; + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) float_4 barg; + + /// <summary> + /// Returns a float_4 that is composed of element 2, element 3, element 0, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_zwxy() const __GPU { + return float_4(_M_z,_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 3, 0, and 1 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_zwxy(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) float_4 zwyx; + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) float_4 bagr; + + /// <summary> + /// Returns a float_4 that is composed of element 2, element 3, element 1, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_zwyx() const __GPU { + return float_4(_M_z,_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 3, 1, and 0 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_zwyx(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) float_4 wxyz; + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) float_4 argb; + + /// <summary> + /// Returns a float_4 that is composed of element 3, element 0, element 1, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_wxyz() const __GPU { + return float_4(_M_w,_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 0, 1, and 2 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_wxyz(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) float_4 wxzy; + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) float_4 arbg; + + /// <summary> + /// Returns a float_4 that is composed of element 3, element 0, element 2, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_wxzy() const __GPU { + return float_4(_M_w,_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 0, 2, and 1 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_wxzy(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) float_4 wyxz; + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) float_4 agrb; + + /// <summary> + /// Returns a float_4 that is composed of element 3, element 1, element 0, and element 2 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_wyxz() const __GPU { + return float_4(_M_w,_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 1, 0, and 2 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_wyxz(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) float_4 wyzx; + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) float_4 agbr; + + /// <summary> + /// Returns a float_4 that is composed of element 3, element 1, element 2, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_wyzx() const __GPU { + return float_4(_M_w,_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 1, 2, and 0 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_wyzx(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) float_4 wzxy; + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) float_4 abrg; + + /// <summary> + /// Returns a float_4 that is composed of element 3, element 2, element 0, and element 1 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_wzxy() const __GPU { + return float_4(_M_w,_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 2, 0, and 1 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_wzxy(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) float_4 wzyx; + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this float_4 as a float_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) float_4 abgr; + + /// <summary> + /// Returns a float_4 that is composed of element 3, element 2, element 1, and element 0 of this float_4. + /// </summary> + /// <returns> + /// a float_4. + /// </returns> + float_4 get_wzyx() const __GPU { + return float_4(_M_w,_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 2, 1, and 0 of this float_4 with a float_4. + /// </summary> + /// <param name="_Value"> + /// a float_4 value. + /// </param> + void set_wzyx(const float_4& _Value) __GPU + { + float_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + }; + + /// <summary> + /// Represent a short vector of 2 unorm's. + /// </summary> + class unorm_2 + { + public: + typedef unorm value_type; + static const int size = 2; + private: + static const _Short_vector_base_type_id _Base_type_id = _Unorm_type; + private: + value_type _M_x; + value_type _M_y; + + public: + /// <summary> + /// Property for accessing element 0 of this unorm_2 as a unorm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unorm x; + /// <summary> + /// Property for accessing element 0 of this unorm_2 as a unorm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unorm r; + + /// <summary> + /// Returns element 0 of this unorm_2. + /// </summary> + /// <returns> + /// Element 0 of this unorm_2. + /// </returns> + unorm get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this unorm_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this unorm_2. + /// </returns> + unorm& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this unorm_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this unorm_2. + /// </returns> + unorm& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this unorm_2 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_x(unorm _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this unorm_2 as a unorm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unorm y; + /// <summary> + /// Property for accessing element 1 of this unorm_2 as a unorm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unorm g; + + /// <summary> + /// Returns element 1 of this unorm_2. + /// </summary> + /// <returns> + /// Element 1 of this unorm_2. + /// </returns> + unorm get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this unorm_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this unorm_2. + /// </returns> + unorm& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this unorm_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this unorm_2. + /// </returns> + unorm& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this unorm_2 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_y(unorm _Value) __GPU + { + _M_y = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + unorm_2() __GPU + { + _M_x = unorm(0.0f); + _M_y = unorm(0.0f); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + unorm_2(unorm _V0, unorm _V1) __GPU + { + _M_x = _V0; + _M_y = _V1; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + unorm_2(float _V0, float _V1) __GPU + { + _M_x = unorm(_V0); + _M_y = unorm(_V1); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + unorm_2(unorm _V) __GPU + { + _M_x = _V; + _M_y = _V; + } + + explicit unorm_2(float _V) __GPU + { + _M_x = unorm(_V); + _M_y = unorm(_V); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + unorm_2(const unorm_2& _Other) __GPU + { + *this = _Other; + } + + unorm_2& operator=(const unorm_2& _Other) __GPU + { + _M_x = _Other._M_x; + _M_y = _Other._M_y; + return *this; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_2(const uint_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_2(const int_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_2(const float_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_2(const norm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_2(const double_2& _Other) __GPU; + + unorm_2& operator++() __GPU + { + unorm_2 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + *this = _Value; + return *this; + } + + unorm_2 operator++(int) __GPU + { + unorm_2 _Result = *this; + ++(*this); + return _Result; + } + + unorm_2& operator--() __GPU + { + unorm_2 _Value = *this; + --_Value._M_x; + --_Value._M_y; + *this = _Value; + return *this; + } + + unorm_2 operator--(int) __GPU + { + unorm_2 _Result = *this; + --(*this); + return _Result; + } + + unorm_2& operator+=(const unorm_2& _Other) __GPU + { + unorm_2 _Value1 = *this; + unorm_2 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + *this = _Value1; + return *this; + } + + unorm_2& operator-=(const unorm_2& _Other) __GPU + { + unorm_2 _Value1 = *this; + unorm_2 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + *this = _Value1; + return *this; + } + + unorm_2& operator*=(const unorm_2& _Other) __GPU + { + unorm_2 _Value1 = *this; + unorm_2 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + *this = _Value1; + return *this; + } + + unorm_2& operator/=(const unorm_2& _Other) __GPU + { + unorm_2 _Value1 = *this; + unorm_2 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this unorm_2 as a unorm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) unorm_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this unorm_2 as a unorm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) unorm_2 rg; + + /// <summary> + /// Returns a unorm_2 that is composed of element 0, and element 1 of this unorm_2. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_xy() const __GPU { + return unorm_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this unorm_2 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_xy(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this unorm_2 as a unorm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) unorm_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this unorm_2 as a unorm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) unorm_2 gr; + + /// <summary> + /// Returns a unorm_2 that is composed of element 1, and element 0 of this unorm_2. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_yx() const __GPU { + return unorm_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this unorm_2 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_yx(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + }; + + /// <summary> + /// Represent a short vector of 3 unorm's. + /// </summary> + class unorm_3 + { + public: + typedef unorm value_type; + static const int size = 3; + private: + static const _Short_vector_base_type_id _Base_type_id = _Unorm_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + + public: + /// <summary> + /// Property for accessing element 0 of this unorm_3 as a unorm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unorm x; + /// <summary> + /// Property for accessing element 0 of this unorm_3 as a unorm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unorm r; + + /// <summary> + /// Returns element 0 of this unorm_3. + /// </summary> + /// <returns> + /// Element 0 of this unorm_3. + /// </returns> + unorm get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this unorm_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this unorm_3. + /// </returns> + unorm& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this unorm_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this unorm_3. + /// </returns> + unorm& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this unorm_3 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_x(unorm _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this unorm_3 as a unorm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unorm y; + /// <summary> + /// Property for accessing element 1 of this unorm_3 as a unorm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unorm g; + + /// <summary> + /// Returns element 1 of this unorm_3. + /// </summary> + /// <returns> + /// Element 1 of this unorm_3. + /// </returns> + unorm get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this unorm_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this unorm_3. + /// </returns> + unorm& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this unorm_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this unorm_3. + /// </returns> + unorm& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this unorm_3 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_y(unorm _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this unorm_3 as a unorm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unorm z; + /// <summary> + /// Property for accessing element 2 of this unorm_3 as a unorm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unorm b; + + /// <summary> + /// Returns element 2 of this unorm_3. + /// </summary> + /// <returns> + /// Element 2 of this unorm_3. + /// </returns> + unorm get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this unorm_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this unorm_3. + /// </returns> + unorm& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this unorm_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this unorm_3. + /// </returns> + unorm& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this unorm_3 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_z(unorm _Value) __GPU + { + _M_z = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + unorm_3() __GPU + { + _M_x = unorm(0.0f); + _M_y = unorm(0.0f); + _M_z = unorm(0.0f); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + unorm_3(unorm _V0, unorm _V1, unorm _V2) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + unorm_3(float _V0, float _V1, float _V2) __GPU + { + _M_x = unorm(_V0); + _M_y = unorm(_V1); + _M_z = unorm(_V2); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + unorm_3(unorm _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + } + + explicit unorm_3(float _V) __GPU + { + _M_x = unorm(_V); + _M_y = unorm(_V); + _M_z = unorm(_V); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + unorm_3(const unorm_3& _Other) __GPU + { + *this = _Other; + } + + unorm_3& operator=(const unorm_3& _Other) __GPU + { + _M_x = _Other._M_x; + _M_y = _Other._M_y; + _M_z = _Other._M_z; + return *this; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_3(const uint_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_3(const int_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_3(const float_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_3(const norm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_3(const double_3& _Other) __GPU; + + unorm_3& operator++() __GPU + { + unorm_3 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + *this = _Value; + return *this; + } + + unorm_3 operator++(int) __GPU + { + unorm_3 _Result = *this; + ++(*this); + return _Result; + } + + unorm_3& operator--() __GPU + { + unorm_3 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + *this = _Value; + return *this; + } + + unorm_3 operator--(int) __GPU + { + unorm_3 _Result = *this; + --(*this); + return _Result; + } + + unorm_3& operator+=(const unorm_3& _Other) __GPU + { + unorm_3 _Value1 = *this; + unorm_3 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + *this = _Value1; + return *this; + } + + unorm_3& operator-=(const unorm_3& _Other) __GPU + { + unorm_3 _Value1 = *this; + unorm_3 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + *this = _Value1; + return *this; + } + + unorm_3& operator*=(const unorm_3& _Other) __GPU + { + unorm_3 _Value1 = *this; + unorm_3 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + *this = _Value1; + return *this; + } + + unorm_3& operator/=(const unorm_3& _Other) __GPU + { + unorm_3 _Value1 = *this; + unorm_3 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) unorm_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) unorm_2 rg; + + /// <summary> + /// Returns a unorm_2 that is composed of element 0, and element 1 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_xy() const __GPU { + return unorm_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this unorm_3 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_xy(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) unorm_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) unorm_2 rb; + + /// <summary> + /// Returns a unorm_2 that is composed of element 0, and element 2 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_xz() const __GPU { + return unorm_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this unorm_3 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_xz(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) unorm_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) unorm_2 gr; + + /// <summary> + /// Returns a unorm_2 that is composed of element 1, and element 0 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_yx() const __GPU { + return unorm_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this unorm_3 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_yx(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) unorm_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) unorm_2 gb; + + /// <summary> + /// Returns a unorm_2 that is composed of element 1, and element 2 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_yz() const __GPU { + return unorm_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this unorm_3 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_yz(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) unorm_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) unorm_2 br; + + /// <summary> + /// Returns a unorm_2 that is composed of element 2, and element 0 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_zx() const __GPU { + return unorm_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this unorm_3 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_zx(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) unorm_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this unorm_3 as a unorm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) unorm_2 bg; + + /// <summary> + /// Returns a unorm_2 that is composed of element 2, and element 1 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_zy() const __GPU { + return unorm_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this unorm_3 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_zy(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) unorm_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) unorm_3 rgb; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 1, and element 2 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xyz() const __GPU { + return unorm_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this unorm_3 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xyz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) unorm_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) unorm_3 rbg; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 2, and element 1 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xzy() const __GPU { + return unorm_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this unorm_3 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xzy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) unorm_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) unorm_3 grb; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 0, and element 2 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_yxz() const __GPU { + return unorm_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this unorm_3 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_yxz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) unorm_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) unorm_3 gbr; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 2, and element 0 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_yzx() const __GPU { + return unorm_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this unorm_3 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_yzx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) unorm_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) unorm_3 brg; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 0, and element 1 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zxy() const __GPU { + return unorm_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this unorm_3 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zxy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) unorm_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this unorm_3 as a unorm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) unorm_3 bgr; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 1, and element 0 of this unorm_3. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zyx() const __GPU { + return unorm_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this unorm_3 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zyx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + }; + + /// <summary> + /// Represent a short vector of 4 unorm's. + /// </summary> + class unorm_4 + { + public: + typedef unorm value_type; + static const int size = 4; + private: + static const _Short_vector_base_type_id _Base_type_id = _Unorm_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + value_type _M_w; + + public: + /// <summary> + /// Property for accessing element 0 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unorm x; + /// <summary> + /// Property for accessing element 0 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) unorm r; + + /// <summary> + /// Returns element 0 of this unorm_4. + /// </summary> + /// <returns> + /// Element 0 of this unorm_4. + /// </returns> + unorm get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this unorm_4. + /// </returns> + unorm& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this unorm_4. + /// </returns> + unorm& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this unorm_4 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_x(unorm _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unorm y; + /// <summary> + /// Property for accessing element 1 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) unorm g; + + /// <summary> + /// Returns element 1 of this unorm_4. + /// </summary> + /// <returns> + /// Element 1 of this unorm_4. + /// </returns> + unorm get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this unorm_4. + /// </returns> + unorm& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this unorm_4. + /// </returns> + unorm& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this unorm_4 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_y(unorm _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unorm z; + /// <summary> + /// Property for accessing element 2 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) unorm b; + + /// <summary> + /// Returns element 2 of this unorm_4. + /// </summary> + /// <returns> + /// Element 2 of this unorm_4. + /// </returns> + unorm get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this unorm_4. + /// </returns> + unorm& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this unorm_4. + /// </returns> + unorm& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this unorm_4 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_z(unorm _Value) __GPU + { + _M_z = _Value; + } + + /// <summary> + /// Property for accessing element 3 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) unorm w; + /// <summary> + /// Property for accessing element 3 of this unorm_4 as a unorm. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) unorm a; + + /// <summary> + /// Returns element 3 of this unorm_4. + /// </summary> + /// <returns> + /// Element 3 of this unorm_4. + /// </returns> + unorm get_w() const __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this unorm_4. + /// </returns> + unorm& ref_w() __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this unorm_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this unorm_4. + /// </returns> + unorm& ref_a() __GPU { + return _M_w; + } + + /// <summary> + /// Set element 3 of this unorm_4 with a unorm. + /// </summary> + /// <param name="_Value"> + /// a unorm value. + /// </param> + void set_w(unorm _Value) __GPU + { + _M_w = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + unorm_4() __GPU + { + _M_x = unorm(0.0f); + _M_y = unorm(0.0f); + _M_z = unorm(0.0f); + _M_w = unorm(0.0f); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + unorm_4(unorm _V0, unorm _V1, unorm _V2, unorm _V3) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + _M_w = _V3; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + unorm_4(float _V0, float _V1, float _V2, float _V3) __GPU + { + _M_x = unorm(_V0); + _M_y = unorm(_V1); + _M_z = unorm(_V2); + _M_w = unorm(_V3); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + unorm_4(unorm _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + _M_w = _V; + } + + explicit unorm_4(float _V) __GPU + { + _M_x = unorm(_V); + _M_y = unorm(_V); + _M_z = unorm(_V); + _M_w = unorm(_V); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + unorm_4(const unorm_4& _Other) __GPU + { + *this = _Other; + } + + unorm_4& operator=(const unorm_4& _Other) __GPU + { + _M_x = _Other._M_x; + _M_y = _Other._M_y; + _M_z = _Other._M_z; + _M_w = _Other._M_w; + return *this; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_4(const uint_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_4(const int_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_4(const float_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_4(const norm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline unorm_4(const double_4& _Other) __GPU; + + unorm_4& operator++() __GPU + { + unorm_4 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + ++_Value._M_w; + *this = _Value; + return *this; + } + + unorm_4 operator++(int) __GPU + { + unorm_4 _Result = *this; + ++(*this); + return _Result; + } + + unorm_4& operator--() __GPU + { + unorm_4 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + --_Value._M_w; + *this = _Value; + return *this; + } + + unorm_4 operator--(int) __GPU + { + unorm_4 _Result = *this; + --(*this); + return _Result; + } + + unorm_4& operator+=(const unorm_4& _Other) __GPU + { + unorm_4 _Value1 = *this; + unorm_4 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + _Value1.w += _Value2.w; + *this = _Value1; + return *this; + } + + unorm_4& operator-=(const unorm_4& _Other) __GPU + { + unorm_4 _Value1 = *this; + unorm_4 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + _Value1.w -= _Value2.w; + *this = _Value1; + return *this; + } + + unorm_4& operator*=(const unorm_4& _Other) __GPU + { + unorm_4 _Value1 = *this; + unorm_4 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + _Value1.w *= _Value2.w; + *this = _Value1; + return *this; + } + + unorm_4& operator/=(const unorm_4& _Other) __GPU + { + unorm_4 _Value1 = *this; + unorm_4 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + _Value1.w /= _Value2.w; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) unorm_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) unorm_2 rg; + + /// <summary> + /// Returns a unorm_2 that is composed of element 0, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_xy() const __GPU { + return unorm_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_xy(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) unorm_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) unorm_2 rb; + + /// <summary> + /// Returns a unorm_2 that is composed of element 0, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_xz() const __GPU { + return unorm_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_xz(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 3 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) unorm_2 xw; + /// <summary> + /// Property for accessing element 0, and 3 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) unorm_2 ra; + + /// <summary> + /// Returns a unorm_2 that is composed of element 0, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_xw() const __GPU { + return unorm_2(_M_x,_M_w); + } + + /// <summary> + /// Set element 0, and 3 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_xw(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) unorm_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) unorm_2 gr; + + /// <summary> + /// Returns a unorm_2 that is composed of element 1, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_yx() const __GPU { + return unorm_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_yx(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) unorm_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) unorm_2 gb; + + /// <summary> + /// Returns a unorm_2 that is composed of element 1, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_yz() const __GPU { + return unorm_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_yz(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 3 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) unorm_2 yw; + /// <summary> + /// Property for accessing element 1, and 3 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) unorm_2 ga; + + /// <summary> + /// Returns a unorm_2 that is composed of element 1, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_yw() const __GPU { + return unorm_2(_M_y,_M_w); + } + + /// <summary> + /// Set element 1, and 3 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_yw(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) unorm_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) unorm_2 br; + + /// <summary> + /// Returns a unorm_2 that is composed of element 2, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_zx() const __GPU { + return unorm_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_zx(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) unorm_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) unorm_2 bg; + + /// <summary> + /// Returns a unorm_2 that is composed of element 2, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_zy() const __GPU { + return unorm_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_zy(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 3 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) unorm_2 zw; + /// <summary> + /// Property for accessing element 2, and 3 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) unorm_2 ba; + + /// <summary> + /// Returns a unorm_2 that is composed of element 2, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_zw() const __GPU { + return unorm_2(_M_z,_M_w); + } + + /// <summary> + /// Set element 2, and 3 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_zw(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 0 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) unorm_2 wx; + /// <summary> + /// Property for accessing element 3, and 0 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) unorm_2 ar; + + /// <summary> + /// Returns a unorm_2 that is composed of element 3, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_wx() const __GPU { + return unorm_2(_M_w,_M_x); + } + + /// <summary> + /// Set element 3, and 0 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_wx(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 1 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) unorm_2 wy; + /// <summary> + /// Property for accessing element 3, and 1 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) unorm_2 ag; + + /// <summary> + /// Returns a unorm_2 that is composed of element 3, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_wy() const __GPU { + return unorm_2(_M_w,_M_y); + } + + /// <summary> + /// Set element 3, and 1 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_wy(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 2 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) unorm_2 wz; + /// <summary> + /// Property for accessing element 3, and 2 of this unorm_4 as a unorm_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) unorm_2 ab; + + /// <summary> + /// Returns a unorm_2 that is composed of element 3, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_2. + /// </returns> + unorm_2 get_wz() const __GPU { + return unorm_2(_M_w,_M_z); + } + + /// <summary> + /// Set element 3, and 2 of this unorm_4 with a unorm_2. + /// </summary> + /// <param name="_Value"> + /// a unorm_2 value. + /// </param> + void set_wz(const unorm_2& _Value) __GPU + { + unorm_2 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) unorm_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) unorm_3 rgb; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 1, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xyz() const __GPU { + return unorm_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xyz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) unorm_3 xyw; + /// <summary> + /// Property for accessing element 0, 1, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) unorm_3 rga; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 1, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xyw() const __GPU { + return unorm_3(_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 1, and 3 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xyw(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) unorm_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) unorm_3 rbg; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 2, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xzy() const __GPU { + return unorm_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xzy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) unorm_3 xzw; + /// <summary> + /// Property for accessing element 0, 2, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) unorm_3 rba; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 2, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xzw() const __GPU { + return unorm_3(_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 2, and 3 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xzw(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) unorm_3 xwy; + /// <summary> + /// Property for accessing element 0, 3, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) unorm_3 rag; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 3, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xwy() const __GPU { + return unorm_3(_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 3, and 1 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xwy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) unorm_3 xwz; + /// <summary> + /// Property for accessing element 0, 3, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) unorm_3 rab; + + /// <summary> + /// Returns a unorm_3 that is composed of element 0, element 3, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_xwz() const __GPU { + return unorm_3(_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 3, and 2 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_xwz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) unorm_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) unorm_3 grb; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 0, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_yxz() const __GPU { + return unorm_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_yxz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) unorm_3 yxw; + /// <summary> + /// Property for accessing element 1, 0, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) unorm_3 gra; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 0, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_yxw() const __GPU { + return unorm_3(_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 0, and 3 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_yxw(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) unorm_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) unorm_3 gbr; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 2, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_yzx() const __GPU { + return unorm_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_yzx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) unorm_3 yzw; + /// <summary> + /// Property for accessing element 1, 2, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) unorm_3 gba; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 2, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_yzw() const __GPU { + return unorm_3(_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 2, and 3 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_yzw(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) unorm_3 ywx; + /// <summary> + /// Property for accessing element 1, 3, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) unorm_3 gar; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 3, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_ywx() const __GPU { + return unorm_3(_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 3, and 0 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_ywx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) unorm_3 ywz; + /// <summary> + /// Property for accessing element 1, 3, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) unorm_3 gab; + + /// <summary> + /// Returns a unorm_3 that is composed of element 1, element 3, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_ywz() const __GPU { + return unorm_3(_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 3, and 2 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_ywz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) unorm_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) unorm_3 brg; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 0, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zxy() const __GPU { + return unorm_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zxy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) unorm_3 zxw; + /// <summary> + /// Property for accessing element 2, 0, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) unorm_3 bra; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 0, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zxw() const __GPU { + return unorm_3(_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 0, and 3 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zxw(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) unorm_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) unorm_3 bgr; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 1, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zyx() const __GPU { + return unorm_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zyx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) unorm_3 zyw; + /// <summary> + /// Property for accessing element 2, 1, and 3 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) unorm_3 bga; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 1, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zyw() const __GPU { + return unorm_3(_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 1, and 3 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zyw(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) unorm_3 zwx; + /// <summary> + /// Property for accessing element 2, 3, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) unorm_3 bar; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 3, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zwx() const __GPU { + return unorm_3(_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 3, and 0 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zwx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) unorm_3 zwy; + /// <summary> + /// Property for accessing element 2, 3, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) unorm_3 bag; + + /// <summary> + /// Returns a unorm_3 that is composed of element 2, element 3, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_zwy() const __GPU { + return unorm_3(_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 3, and 1 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_zwy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) unorm_3 wxy; + /// <summary> + /// Property for accessing element 3, 0, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) unorm_3 arg; + + /// <summary> + /// Returns a unorm_3 that is composed of element 3, element 0, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_wxy() const __GPU { + return unorm_3(_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 0, and 1 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_wxy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) unorm_3 wxz; + /// <summary> + /// Property for accessing element 3, 0, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) unorm_3 arb; + + /// <summary> + /// Returns a unorm_3 that is composed of element 3, element 0, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_wxz() const __GPU { + return unorm_3(_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 0, and 2 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_wxz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) unorm_3 wyx; + /// <summary> + /// Property for accessing element 3, 1, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) unorm_3 agr; + + /// <summary> + /// Returns a unorm_3 that is composed of element 3, element 1, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_wyx() const __GPU { + return unorm_3(_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 1, and 0 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_wyx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) unorm_3 wyz; + /// <summary> + /// Property for accessing element 3, 1, and 2 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) unorm_3 agb; + + /// <summary> + /// Returns a unorm_3 that is composed of element 3, element 1, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_wyz() const __GPU { + return unorm_3(_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 1, and 2 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_wyz(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) unorm_3 wzx; + /// <summary> + /// Property for accessing element 3, 2, and 0 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) unorm_3 abr; + + /// <summary> + /// Returns a unorm_3 that is composed of element 3, element 2, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_wzx() const __GPU { + return unorm_3(_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 2, and 0 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_wzx(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) unorm_3 wzy; + /// <summary> + /// Property for accessing element 3, 2, and 1 of this unorm_4 as a unorm_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) unorm_3 abg; + + /// <summary> + /// Returns a unorm_3 that is composed of element 3, element 2, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_3. + /// </returns> + unorm_3 get_wzy() const __GPU { + return unorm_3(_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 2, and 1 of this unorm_4 with a unorm_3. + /// </summary> + /// <param name="_Value"> + /// a unorm_3 value. + /// </param> + void set_wzy(const unorm_3& _Value) __GPU + { + unorm_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) unorm_4 xyzw; + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) unorm_4 rgba; + + /// <summary> + /// Returns a unorm_4 that is composed of element 0, element 1, element 2, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_xyzw() const __GPU { + return unorm_4(_M_x,_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 1, 2, and 3 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_xyzw(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) unorm_4 xywz; + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) unorm_4 rgab; + + /// <summary> + /// Returns a unorm_4 that is composed of element 0, element 1, element 3, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_xywz() const __GPU { + return unorm_4(_M_x,_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 1, 3, and 2 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_xywz(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) unorm_4 xzyw; + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) unorm_4 rbga; + + /// <summary> + /// Returns a unorm_4 that is composed of element 0, element 2, element 1, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_xzyw() const __GPU { + return unorm_4(_M_x,_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 2, 1, and 3 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_xzyw(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) unorm_4 xzwy; + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) unorm_4 rbag; + + /// <summary> + /// Returns a unorm_4 that is composed of element 0, element 2, element 3, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_xzwy() const __GPU { + return unorm_4(_M_x,_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 2, 3, and 1 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_xzwy(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) unorm_4 xwyz; + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) unorm_4 ragb; + + /// <summary> + /// Returns a unorm_4 that is composed of element 0, element 3, element 1, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_xwyz() const __GPU { + return unorm_4(_M_x,_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 3, 1, and 2 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_xwyz(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) unorm_4 xwzy; + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) unorm_4 rabg; + + /// <summary> + /// Returns a unorm_4 that is composed of element 0, element 3, element 2, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_xwzy() const __GPU { + return unorm_4(_M_x,_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 3, 2, and 1 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_xwzy(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) unorm_4 yxzw; + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) unorm_4 grba; + + /// <summary> + /// Returns a unorm_4 that is composed of element 1, element 0, element 2, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_yxzw() const __GPU { + return unorm_4(_M_y,_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 0, 2, and 3 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_yxzw(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) unorm_4 yxwz; + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) unorm_4 grab; + + /// <summary> + /// Returns a unorm_4 that is composed of element 1, element 0, element 3, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_yxwz() const __GPU { + return unorm_4(_M_y,_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 0, 3, and 2 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_yxwz(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) unorm_4 yzxw; + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) unorm_4 gbra; + + /// <summary> + /// Returns a unorm_4 that is composed of element 1, element 2, element 0, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_yzxw() const __GPU { + return unorm_4(_M_y,_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 2, 0, and 3 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_yzxw(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) unorm_4 yzwx; + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) unorm_4 gbar; + + /// <summary> + /// Returns a unorm_4 that is composed of element 1, element 2, element 3, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_yzwx() const __GPU { + return unorm_4(_M_y,_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 2, 3, and 0 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_yzwx(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) unorm_4 ywxz; + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) unorm_4 garb; + + /// <summary> + /// Returns a unorm_4 that is composed of element 1, element 3, element 0, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_ywxz() const __GPU { + return unorm_4(_M_y,_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 3, 0, and 2 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_ywxz(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) unorm_4 ywzx; + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) unorm_4 gabr; + + /// <summary> + /// Returns a unorm_4 that is composed of element 1, element 3, element 2, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_ywzx() const __GPU { + return unorm_4(_M_y,_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 3, 2, and 0 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_ywzx(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) unorm_4 zxyw; + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) unorm_4 brga; + + /// <summary> + /// Returns a unorm_4 that is composed of element 2, element 0, element 1, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_zxyw() const __GPU { + return unorm_4(_M_z,_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 0, 1, and 3 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_zxyw(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) unorm_4 zxwy; + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) unorm_4 brag; + + /// <summary> + /// Returns a unorm_4 that is composed of element 2, element 0, element 3, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_zxwy() const __GPU { + return unorm_4(_M_z,_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 0, 3, and 1 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_zxwy(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) unorm_4 zyxw; + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) unorm_4 bgra; + + /// <summary> + /// Returns a unorm_4 that is composed of element 2, element 1, element 0, and element 3 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_zyxw() const __GPU { + return unorm_4(_M_z,_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 1, 0, and 3 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_zyxw(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) unorm_4 zywx; + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) unorm_4 bgar; + + /// <summary> + /// Returns a unorm_4 that is composed of element 2, element 1, element 3, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_zywx() const __GPU { + return unorm_4(_M_z,_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 1, 3, and 0 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_zywx(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) unorm_4 zwxy; + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) unorm_4 barg; + + /// <summary> + /// Returns a unorm_4 that is composed of element 2, element 3, element 0, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_zwxy() const __GPU { + return unorm_4(_M_z,_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 3, 0, and 1 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_zwxy(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) unorm_4 zwyx; + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) unorm_4 bagr; + + /// <summary> + /// Returns a unorm_4 that is composed of element 2, element 3, element 1, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_zwyx() const __GPU { + return unorm_4(_M_z,_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 3, 1, and 0 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_zwyx(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) unorm_4 wxyz; + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) unorm_4 argb; + + /// <summary> + /// Returns a unorm_4 that is composed of element 3, element 0, element 1, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_wxyz() const __GPU { + return unorm_4(_M_w,_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 0, 1, and 2 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_wxyz(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) unorm_4 wxzy; + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) unorm_4 arbg; + + /// <summary> + /// Returns a unorm_4 that is composed of element 3, element 0, element 2, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_wxzy() const __GPU { + return unorm_4(_M_w,_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 0, 2, and 1 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_wxzy(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) unorm_4 wyxz; + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) unorm_4 agrb; + + /// <summary> + /// Returns a unorm_4 that is composed of element 3, element 1, element 0, and element 2 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_wyxz() const __GPU { + return unorm_4(_M_w,_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 1, 0, and 2 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_wyxz(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) unorm_4 wyzx; + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) unorm_4 agbr; + + /// <summary> + /// Returns a unorm_4 that is composed of element 3, element 1, element 2, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_wyzx() const __GPU { + return unorm_4(_M_w,_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 1, 2, and 0 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_wyzx(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) unorm_4 wzxy; + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) unorm_4 abrg; + + /// <summary> + /// Returns a unorm_4 that is composed of element 3, element 2, element 0, and element 1 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_wzxy() const __GPU { + return unorm_4(_M_w,_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 2, 0, and 1 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_wzxy(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) unorm_4 wzyx; + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this unorm_4 as a unorm_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) unorm_4 abgr; + + /// <summary> + /// Returns a unorm_4 that is composed of element 3, element 2, element 1, and element 0 of this unorm_4. + /// </summary> + /// <returns> + /// a unorm_4. + /// </returns> + unorm_4 get_wzyx() const __GPU { + return unorm_4(_M_w,_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 2, 1, and 0 of this unorm_4 with a unorm_4. + /// </summary> + /// <param name="_Value"> + /// a unorm_4 value. + /// </param> + void set_wzyx(const unorm_4& _Value) __GPU + { + unorm_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + }; + + /// <summary> + /// Represent a short vector of 2 norm's. + /// </summary> + class norm_2 + { + public: + typedef norm value_type; + static const int size = 2; + private: + static const _Short_vector_base_type_id _Base_type_id = _Norm_type; + private: + value_type _M_x; + value_type _M_y; + + public: + /// <summary> + /// Property for accessing element 0 of this norm_2 as a norm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) norm x; + /// <summary> + /// Property for accessing element 0 of this norm_2 as a norm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) norm r; + + /// <summary> + /// Returns element 0 of this norm_2. + /// </summary> + /// <returns> + /// Element 0 of this norm_2. + /// </returns> + norm get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this norm_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this norm_2. + /// </returns> + norm& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this norm_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this norm_2. + /// </returns> + norm& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this norm_2 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_x(norm _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this norm_2 as a norm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) norm y; + /// <summary> + /// Property for accessing element 1 of this norm_2 as a norm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) norm g; + + /// <summary> + /// Returns element 1 of this norm_2. + /// </summary> + /// <returns> + /// Element 1 of this norm_2. + /// </returns> + norm get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this norm_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this norm_2. + /// </returns> + norm& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this norm_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this norm_2. + /// </returns> + norm& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this norm_2 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_y(norm _Value) __GPU + { + _M_y = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + norm_2() __GPU + { + _M_x = norm(0.0f); + _M_y = norm(0.0f); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + norm_2(norm _V0, norm _V1) __GPU + { + _M_x = _V0; + _M_y = _V1; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + norm_2(float _V0, float _V1) __GPU + { + _M_x = norm(_V0); + _M_y = norm(_V1); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + norm_2(unorm _V0, unorm _V1) __GPU + { + _M_x = _V0; + _M_y = _V1; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + norm_2(norm _V) __GPU + { + _M_x = _V; + _M_y = _V; + } + + explicit norm_2(float _V) __GPU + { + _M_x = norm(_V); + _M_y = norm(_V); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + norm_2(const norm_2& _Other) __GPU + { + *this = _Other; + } + + norm_2& operator=(const norm_2& _Other) __GPU + { + _M_x = _Other._M_x; + _M_y = _Other._M_y; + return *this; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_2(const uint_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_2(const int_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_2(const float_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_2(const unorm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_2(const double_2& _Other) __GPU; + + norm_2 operator-() const __GPU + { + norm_2 _Value = *this; + return norm_2(-_Value.x, -_Value.y); + } + + norm_2& operator++() __GPU + { + norm_2 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + *this = _Value; + return *this; + } + + norm_2 operator++(int) __GPU + { + norm_2 _Result = *this; + ++(*this); + return _Result; + } + + norm_2& operator--() __GPU + { + norm_2 _Value = *this; + --_Value._M_x; + --_Value._M_y; + *this = _Value; + return *this; + } + + norm_2 operator--(int) __GPU + { + norm_2 _Result = *this; + --(*this); + return _Result; + } + + norm_2& operator+=(const norm_2& _Other) __GPU + { + norm_2 _Value1 = *this; + norm_2 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + *this = _Value1; + return *this; + } + + norm_2& operator-=(const norm_2& _Other) __GPU + { + norm_2 _Value1 = *this; + norm_2 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + *this = _Value1; + return *this; + } + + norm_2& operator*=(const norm_2& _Other) __GPU + { + norm_2 _Value1 = *this; + norm_2 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + *this = _Value1; + return *this; + } + + norm_2& operator/=(const norm_2& _Other) __GPU + { + norm_2 _Value1 = *this; + norm_2 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this norm_2 as a norm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) norm_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this norm_2 as a norm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) norm_2 rg; + + /// <summary> + /// Returns a norm_2 that is composed of element 0, and element 1 of this norm_2. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_xy() const __GPU { + return norm_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this norm_2 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_xy(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this norm_2 as a norm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) norm_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this norm_2 as a norm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) norm_2 gr; + + /// <summary> + /// Returns a norm_2 that is composed of element 1, and element 0 of this norm_2. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_yx() const __GPU { + return norm_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this norm_2 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_yx(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + }; + + /// <summary> + /// Represent a short vector of 3 norm's. + /// </summary> + class norm_3 + { + public: + typedef norm value_type; + static const int size = 3; + private: + static const _Short_vector_base_type_id _Base_type_id = _Norm_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + + public: + /// <summary> + /// Property for accessing element 0 of this norm_3 as a norm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) norm x; + /// <summary> + /// Property for accessing element 0 of this norm_3 as a norm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) norm r; + + /// <summary> + /// Returns element 0 of this norm_3. + /// </summary> + /// <returns> + /// Element 0 of this norm_3. + /// </returns> + norm get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this norm_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this norm_3. + /// </returns> + norm& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this norm_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this norm_3. + /// </returns> + norm& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this norm_3 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_x(norm _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this norm_3 as a norm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) norm y; + /// <summary> + /// Property for accessing element 1 of this norm_3 as a norm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) norm g; + + /// <summary> + /// Returns element 1 of this norm_3. + /// </summary> + /// <returns> + /// Element 1 of this norm_3. + /// </returns> + norm get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this norm_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this norm_3. + /// </returns> + norm& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this norm_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this norm_3. + /// </returns> + norm& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this norm_3 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_y(norm _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this norm_3 as a norm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) norm z; + /// <summary> + /// Property for accessing element 2 of this norm_3 as a norm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) norm b; + + /// <summary> + /// Returns element 2 of this norm_3. + /// </summary> + /// <returns> + /// Element 2 of this norm_3. + /// </returns> + norm get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this norm_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this norm_3. + /// </returns> + norm& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this norm_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this norm_3. + /// </returns> + norm& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this norm_3 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_z(norm _Value) __GPU + { + _M_z = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + norm_3() __GPU + { + _M_x = norm(0.0f); + _M_y = norm(0.0f); + _M_z = norm(0.0f); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + norm_3(norm _V0, norm _V1, norm _V2) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + norm_3(float _V0, float _V1, float _V2) __GPU + { + _M_x = norm(_V0); + _M_y = norm(_V1); + _M_z = norm(_V2); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + norm_3(unorm _V0, unorm _V1, unorm _V2) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + norm_3(norm _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + } + + explicit norm_3(float _V) __GPU + { + _M_x = norm(_V); + _M_y = norm(_V); + _M_z = norm(_V); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + norm_3(const norm_3& _Other) __GPU + { + *this = _Other; + } + + norm_3& operator=(const norm_3& _Other) __GPU + { + _M_x = _Other._M_x; + _M_y = _Other._M_y; + _M_z = _Other._M_z; + return *this; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_3(const uint_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_3(const int_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_3(const float_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_3(const unorm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_3(const double_3& _Other) __GPU; + + norm_3 operator-() const __GPU + { + norm_3 _Value = *this; + return norm_3(-_Value.x, -_Value.y, -_Value.z); + } + + norm_3& operator++() __GPU + { + norm_3 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + *this = _Value; + return *this; + } + + norm_3 operator++(int) __GPU + { + norm_3 _Result = *this; + ++(*this); + return _Result; + } + + norm_3& operator--() __GPU + { + norm_3 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + *this = _Value; + return *this; + } + + norm_3 operator--(int) __GPU + { + norm_3 _Result = *this; + --(*this); + return _Result; + } + + norm_3& operator+=(const norm_3& _Other) __GPU + { + norm_3 _Value1 = *this; + norm_3 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + *this = _Value1; + return *this; + } + + norm_3& operator-=(const norm_3& _Other) __GPU + { + norm_3 _Value1 = *this; + norm_3 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + *this = _Value1; + return *this; + } + + norm_3& operator*=(const norm_3& _Other) __GPU + { + norm_3 _Value1 = *this; + norm_3 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + *this = _Value1; + return *this; + } + + norm_3& operator/=(const norm_3& _Other) __GPU + { + norm_3 _Value1 = *this; + norm_3 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) norm_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) norm_2 rg; + + /// <summary> + /// Returns a norm_2 that is composed of element 0, and element 1 of this norm_3. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_xy() const __GPU { + return norm_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this norm_3 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_xy(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) norm_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) norm_2 rb; + + /// <summary> + /// Returns a norm_2 that is composed of element 0, and element 2 of this norm_3. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_xz() const __GPU { + return norm_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this norm_3 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_xz(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) norm_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) norm_2 gr; + + /// <summary> + /// Returns a norm_2 that is composed of element 1, and element 0 of this norm_3. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_yx() const __GPU { + return norm_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this norm_3 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_yx(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) norm_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) norm_2 gb; + + /// <summary> + /// Returns a norm_2 that is composed of element 1, and element 2 of this norm_3. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_yz() const __GPU { + return norm_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this norm_3 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_yz(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) norm_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) norm_2 br; + + /// <summary> + /// Returns a norm_2 that is composed of element 2, and element 0 of this norm_3. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_zx() const __GPU { + return norm_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this norm_3 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_zx(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) norm_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this norm_3 as a norm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) norm_2 bg; + + /// <summary> + /// Returns a norm_2 that is composed of element 2, and element 1 of this norm_3. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_zy() const __GPU { + return norm_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this norm_3 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_zy(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) norm_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) norm_3 rgb; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 1, and element 2 of this norm_3. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xyz() const __GPU { + return norm_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this norm_3 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xyz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) norm_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) norm_3 rbg; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 2, and element 1 of this norm_3. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xzy() const __GPU { + return norm_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this norm_3 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xzy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) norm_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) norm_3 grb; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 0, and element 2 of this norm_3. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_yxz() const __GPU { + return norm_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this norm_3 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_yxz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) norm_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) norm_3 gbr; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 2, and element 0 of this norm_3. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_yzx() const __GPU { + return norm_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this norm_3 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_yzx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) norm_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) norm_3 brg; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 0, and element 1 of this norm_3. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zxy() const __GPU { + return norm_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this norm_3 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zxy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) norm_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this norm_3 as a norm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) norm_3 bgr; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 1, and element 0 of this norm_3. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zyx() const __GPU { + return norm_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this norm_3 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zyx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + }; + + /// <summary> + /// Represent a short vector of 4 norm's. + /// </summary> + class norm_4 + { + public: + typedef norm value_type; + static const int size = 4; + private: + static const _Short_vector_base_type_id _Base_type_id = _Norm_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + value_type _M_w; + + public: + /// <summary> + /// Property for accessing element 0 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) norm x; + /// <summary> + /// Property for accessing element 0 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) norm r; + + /// <summary> + /// Returns element 0 of this norm_4. + /// </summary> + /// <returns> + /// Element 0 of this norm_4. + /// </returns> + norm get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this norm_4. + /// </returns> + norm& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this norm_4. + /// </returns> + norm& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this norm_4 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_x(norm _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) norm y; + /// <summary> + /// Property for accessing element 1 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) norm g; + + /// <summary> + /// Returns element 1 of this norm_4. + /// </summary> + /// <returns> + /// Element 1 of this norm_4. + /// </returns> + norm get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this norm_4. + /// </returns> + norm& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this norm_4. + /// </returns> + norm& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this norm_4 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_y(norm _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) norm z; + /// <summary> + /// Property for accessing element 2 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) norm b; + + /// <summary> + /// Returns element 2 of this norm_4. + /// </summary> + /// <returns> + /// Element 2 of this norm_4. + /// </returns> + norm get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this norm_4. + /// </returns> + norm& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this norm_4. + /// </returns> + norm& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this norm_4 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_z(norm _Value) __GPU + { + _M_z = _Value; + } + + /// <summary> + /// Property for accessing element 3 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) norm w; + /// <summary> + /// Property for accessing element 3 of this norm_4 as a norm. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) norm a; + + /// <summary> + /// Returns element 3 of this norm_4. + /// </summary> + /// <returns> + /// Element 3 of this norm_4. + /// </returns> + norm get_w() const __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this norm_4. + /// </returns> + norm& ref_w() __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this norm_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this norm_4. + /// </returns> + norm& ref_a() __GPU { + return _M_w; + } + + /// <summary> + /// Set element 3 of this norm_4 with a norm. + /// </summary> + /// <param name="_Value"> + /// a norm value. + /// </param> + void set_w(norm _Value) __GPU + { + _M_w = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + norm_4() __GPU + { + _M_x = norm(0.0f); + _M_y = norm(0.0f); + _M_z = norm(0.0f); + _M_w = norm(0.0f); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + norm_4(norm _V0, norm _V1, norm _V2, norm _V3) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + _M_w = _V3; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + norm_4(float _V0, float _V1, float _V2, float _V3) __GPU + { + _M_x = norm(_V0); + _M_y = norm(_V1); + _M_z = norm(_V2); + _M_w = norm(_V3); + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + norm_4(unorm _V0, unorm _V1, unorm _V2, unorm _V3) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + _M_w = _V3; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + norm_4(norm _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + _M_w = _V; + } + + explicit norm_4(float _V) __GPU + { + _M_x = norm(_V); + _M_y = norm(_V); + _M_z = norm(_V); + _M_w = norm(_V); + } + + /// <summary> + /// Copy constructor. + /// </summary> + /// <param name="_Other"> + /// The object to copy from. + /// </param> + norm_4(const norm_4& _Other) __GPU + { + *this = _Other; + } + + norm_4& operator=(const norm_4& _Other) __GPU + { + _M_x = _Other._M_x; + _M_y = _Other._M_y; + _M_z = _Other._M_z; + _M_w = _Other._M_w; + return *this; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_4(const uint_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_4(const int_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_4(const float_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_4(const unorm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline norm_4(const double_4& _Other) __GPU; + + norm_4 operator-() const __GPU + { + norm_4 _Value = *this; + return norm_4(-_Value.x, -_Value.y, -_Value.z, -_Value.w); + } + + norm_4& operator++() __GPU + { + norm_4 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + ++_Value._M_w; + *this = _Value; + return *this; + } + + norm_4 operator++(int) __GPU + { + norm_4 _Result = *this; + ++(*this); + return _Result; + } + + norm_4& operator--() __GPU + { + norm_4 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + --_Value._M_w; + *this = _Value; + return *this; + } + + norm_4 operator--(int) __GPU + { + norm_4 _Result = *this; + --(*this); + return _Result; + } + + norm_4& operator+=(const norm_4& _Other) __GPU + { + norm_4 _Value1 = *this; + norm_4 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + _Value1.w += _Value2.w; + *this = _Value1; + return *this; + } + + norm_4& operator-=(const norm_4& _Other) __GPU + { + norm_4 _Value1 = *this; + norm_4 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + _Value1.w -= _Value2.w; + *this = _Value1; + return *this; + } + + norm_4& operator*=(const norm_4& _Other) __GPU + { + norm_4 _Value1 = *this; + norm_4 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + _Value1.w *= _Value2.w; + *this = _Value1; + return *this; + } + + norm_4& operator/=(const norm_4& _Other) __GPU + { + norm_4 _Value1 = *this; + norm_4 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + _Value1.w /= _Value2.w; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) norm_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) norm_2 rg; + + /// <summary> + /// Returns a norm_2 that is composed of element 0, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_xy() const __GPU { + return norm_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_xy(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) norm_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) norm_2 rb; + + /// <summary> + /// Returns a norm_2 that is composed of element 0, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_xz() const __GPU { + return norm_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_xz(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 3 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) norm_2 xw; + /// <summary> + /// Property for accessing element 0, and 3 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) norm_2 ra; + + /// <summary> + /// Returns a norm_2 that is composed of element 0, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_xw() const __GPU { + return norm_2(_M_x,_M_w); + } + + /// <summary> + /// Set element 0, and 3 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_xw(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) norm_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) norm_2 gr; + + /// <summary> + /// Returns a norm_2 that is composed of element 1, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_yx() const __GPU { + return norm_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_yx(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) norm_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) norm_2 gb; + + /// <summary> + /// Returns a norm_2 that is composed of element 1, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_yz() const __GPU { + return norm_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_yz(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 3 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) norm_2 yw; + /// <summary> + /// Property for accessing element 1, and 3 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) norm_2 ga; + + /// <summary> + /// Returns a norm_2 that is composed of element 1, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_yw() const __GPU { + return norm_2(_M_y,_M_w); + } + + /// <summary> + /// Set element 1, and 3 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_yw(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) norm_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) norm_2 br; + + /// <summary> + /// Returns a norm_2 that is composed of element 2, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_zx() const __GPU { + return norm_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_zx(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) norm_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) norm_2 bg; + + /// <summary> + /// Returns a norm_2 that is composed of element 2, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_zy() const __GPU { + return norm_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_zy(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 3 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) norm_2 zw; + /// <summary> + /// Property for accessing element 2, and 3 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) norm_2 ba; + + /// <summary> + /// Returns a norm_2 that is composed of element 2, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_zw() const __GPU { + return norm_2(_M_z,_M_w); + } + + /// <summary> + /// Set element 2, and 3 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_zw(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 0 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) norm_2 wx; + /// <summary> + /// Property for accessing element 3, and 0 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) norm_2 ar; + + /// <summary> + /// Returns a norm_2 that is composed of element 3, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_wx() const __GPU { + return norm_2(_M_w,_M_x); + } + + /// <summary> + /// Set element 3, and 0 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_wx(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 1 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) norm_2 wy; + /// <summary> + /// Property for accessing element 3, and 1 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) norm_2 ag; + + /// <summary> + /// Returns a norm_2 that is composed of element 3, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_wy() const __GPU { + return norm_2(_M_w,_M_y); + } + + /// <summary> + /// Set element 3, and 1 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_wy(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 2 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) norm_2 wz; + /// <summary> + /// Property for accessing element 3, and 2 of this norm_4 as a norm_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) norm_2 ab; + + /// <summary> + /// Returns a norm_2 that is composed of element 3, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_2. + /// </returns> + norm_2 get_wz() const __GPU { + return norm_2(_M_w,_M_z); + } + + /// <summary> + /// Set element 3, and 2 of this norm_4 with a norm_2. + /// </summary> + /// <param name="_Value"> + /// a norm_2 value. + /// </param> + void set_wz(const norm_2& _Value) __GPU + { + norm_2 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) norm_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) norm_3 rgb; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 1, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xyz() const __GPU { + return norm_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xyz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) norm_3 xyw; + /// <summary> + /// Property for accessing element 0, 1, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) norm_3 rga; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 1, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xyw() const __GPU { + return norm_3(_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 1, and 3 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xyw(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) norm_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) norm_3 rbg; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 2, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xzy() const __GPU { + return norm_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xzy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) norm_3 xzw; + /// <summary> + /// Property for accessing element 0, 2, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) norm_3 rba; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 2, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xzw() const __GPU { + return norm_3(_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 2, and 3 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xzw(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) norm_3 xwy; + /// <summary> + /// Property for accessing element 0, 3, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) norm_3 rag; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 3, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xwy() const __GPU { + return norm_3(_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 3, and 1 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xwy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) norm_3 xwz; + /// <summary> + /// Property for accessing element 0, 3, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) norm_3 rab; + + /// <summary> + /// Returns a norm_3 that is composed of element 0, element 3, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_xwz() const __GPU { + return norm_3(_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 3, and 2 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_xwz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) norm_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) norm_3 grb; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 0, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_yxz() const __GPU { + return norm_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_yxz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) norm_3 yxw; + /// <summary> + /// Property for accessing element 1, 0, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) norm_3 gra; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 0, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_yxw() const __GPU { + return norm_3(_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 0, and 3 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_yxw(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) norm_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) norm_3 gbr; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 2, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_yzx() const __GPU { + return norm_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_yzx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) norm_3 yzw; + /// <summary> + /// Property for accessing element 1, 2, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) norm_3 gba; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 2, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_yzw() const __GPU { + return norm_3(_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 2, and 3 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_yzw(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) norm_3 ywx; + /// <summary> + /// Property for accessing element 1, 3, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) norm_3 gar; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 3, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_ywx() const __GPU { + return norm_3(_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 3, and 0 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_ywx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) norm_3 ywz; + /// <summary> + /// Property for accessing element 1, 3, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) norm_3 gab; + + /// <summary> + /// Returns a norm_3 that is composed of element 1, element 3, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_ywz() const __GPU { + return norm_3(_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 3, and 2 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_ywz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) norm_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) norm_3 brg; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 0, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zxy() const __GPU { + return norm_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zxy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) norm_3 zxw; + /// <summary> + /// Property for accessing element 2, 0, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) norm_3 bra; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 0, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zxw() const __GPU { + return norm_3(_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 0, and 3 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zxw(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) norm_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) norm_3 bgr; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 1, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zyx() const __GPU { + return norm_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zyx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) norm_3 zyw; + /// <summary> + /// Property for accessing element 2, 1, and 3 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) norm_3 bga; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 1, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zyw() const __GPU { + return norm_3(_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 1, and 3 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zyw(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) norm_3 zwx; + /// <summary> + /// Property for accessing element 2, 3, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) norm_3 bar; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 3, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zwx() const __GPU { + return norm_3(_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 3, and 0 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zwx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) norm_3 zwy; + /// <summary> + /// Property for accessing element 2, 3, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) norm_3 bag; + + /// <summary> + /// Returns a norm_3 that is composed of element 2, element 3, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_zwy() const __GPU { + return norm_3(_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 3, and 1 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_zwy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) norm_3 wxy; + /// <summary> + /// Property for accessing element 3, 0, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) norm_3 arg; + + /// <summary> + /// Returns a norm_3 that is composed of element 3, element 0, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_wxy() const __GPU { + return norm_3(_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 0, and 1 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_wxy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) norm_3 wxz; + /// <summary> + /// Property for accessing element 3, 0, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) norm_3 arb; + + /// <summary> + /// Returns a norm_3 that is composed of element 3, element 0, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_wxz() const __GPU { + return norm_3(_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 0, and 2 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_wxz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) norm_3 wyx; + /// <summary> + /// Property for accessing element 3, 1, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) norm_3 agr; + + /// <summary> + /// Returns a norm_3 that is composed of element 3, element 1, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_wyx() const __GPU { + return norm_3(_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 1, and 0 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_wyx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) norm_3 wyz; + /// <summary> + /// Property for accessing element 3, 1, and 2 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) norm_3 agb; + + /// <summary> + /// Returns a norm_3 that is composed of element 3, element 1, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_wyz() const __GPU { + return norm_3(_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 1, and 2 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_wyz(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) norm_3 wzx; + /// <summary> + /// Property for accessing element 3, 2, and 0 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) norm_3 abr; + + /// <summary> + /// Returns a norm_3 that is composed of element 3, element 2, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_wzx() const __GPU { + return norm_3(_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 2, and 0 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_wzx(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) norm_3 wzy; + /// <summary> + /// Property for accessing element 3, 2, and 1 of this norm_4 as a norm_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) norm_3 abg; + + /// <summary> + /// Returns a norm_3 that is composed of element 3, element 2, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_3. + /// </returns> + norm_3 get_wzy() const __GPU { + return norm_3(_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 2, and 1 of this norm_4 with a norm_3. + /// </summary> + /// <param name="_Value"> + /// a norm_3 value. + /// </param> + void set_wzy(const norm_3& _Value) __GPU + { + norm_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) norm_4 xyzw; + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) norm_4 rgba; + + /// <summary> + /// Returns a norm_4 that is composed of element 0, element 1, element 2, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_xyzw() const __GPU { + return norm_4(_M_x,_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 1, 2, and 3 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_xyzw(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) norm_4 xywz; + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) norm_4 rgab; + + /// <summary> + /// Returns a norm_4 that is composed of element 0, element 1, element 3, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_xywz() const __GPU { + return norm_4(_M_x,_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 1, 3, and 2 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_xywz(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) norm_4 xzyw; + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) norm_4 rbga; + + /// <summary> + /// Returns a norm_4 that is composed of element 0, element 2, element 1, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_xzyw() const __GPU { + return norm_4(_M_x,_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 2, 1, and 3 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_xzyw(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) norm_4 xzwy; + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) norm_4 rbag; + + /// <summary> + /// Returns a norm_4 that is composed of element 0, element 2, element 3, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_xzwy() const __GPU { + return norm_4(_M_x,_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 2, 3, and 1 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_xzwy(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) norm_4 xwyz; + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) norm_4 ragb; + + /// <summary> + /// Returns a norm_4 that is composed of element 0, element 3, element 1, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_xwyz() const __GPU { + return norm_4(_M_x,_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 3, 1, and 2 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_xwyz(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) norm_4 xwzy; + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) norm_4 rabg; + + /// <summary> + /// Returns a norm_4 that is composed of element 0, element 3, element 2, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_xwzy() const __GPU { + return norm_4(_M_x,_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 3, 2, and 1 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_xwzy(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) norm_4 yxzw; + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) norm_4 grba; + + /// <summary> + /// Returns a norm_4 that is composed of element 1, element 0, element 2, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_yxzw() const __GPU { + return norm_4(_M_y,_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 0, 2, and 3 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_yxzw(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) norm_4 yxwz; + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) norm_4 grab; + + /// <summary> + /// Returns a norm_4 that is composed of element 1, element 0, element 3, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_yxwz() const __GPU { + return norm_4(_M_y,_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 0, 3, and 2 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_yxwz(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) norm_4 yzxw; + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) norm_4 gbra; + + /// <summary> + /// Returns a norm_4 that is composed of element 1, element 2, element 0, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_yzxw() const __GPU { + return norm_4(_M_y,_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 2, 0, and 3 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_yzxw(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) norm_4 yzwx; + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) norm_4 gbar; + + /// <summary> + /// Returns a norm_4 that is composed of element 1, element 2, element 3, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_yzwx() const __GPU { + return norm_4(_M_y,_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 2, 3, and 0 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_yzwx(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) norm_4 ywxz; + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) norm_4 garb; + + /// <summary> + /// Returns a norm_4 that is composed of element 1, element 3, element 0, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_ywxz() const __GPU { + return norm_4(_M_y,_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 3, 0, and 2 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_ywxz(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) norm_4 ywzx; + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) norm_4 gabr; + + /// <summary> + /// Returns a norm_4 that is composed of element 1, element 3, element 2, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_ywzx() const __GPU { + return norm_4(_M_y,_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 3, 2, and 0 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_ywzx(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) norm_4 zxyw; + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) norm_4 brga; + + /// <summary> + /// Returns a norm_4 that is composed of element 2, element 0, element 1, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_zxyw() const __GPU { + return norm_4(_M_z,_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 0, 1, and 3 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_zxyw(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) norm_4 zxwy; + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) norm_4 brag; + + /// <summary> + /// Returns a norm_4 that is composed of element 2, element 0, element 3, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_zxwy() const __GPU { + return norm_4(_M_z,_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 0, 3, and 1 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_zxwy(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) norm_4 zyxw; + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) norm_4 bgra; + + /// <summary> + /// Returns a norm_4 that is composed of element 2, element 1, element 0, and element 3 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_zyxw() const __GPU { + return norm_4(_M_z,_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 1, 0, and 3 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_zyxw(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) norm_4 zywx; + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) norm_4 bgar; + + /// <summary> + /// Returns a norm_4 that is composed of element 2, element 1, element 3, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_zywx() const __GPU { + return norm_4(_M_z,_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 1, 3, and 0 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_zywx(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) norm_4 zwxy; + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) norm_4 barg; + + /// <summary> + /// Returns a norm_4 that is composed of element 2, element 3, element 0, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_zwxy() const __GPU { + return norm_4(_M_z,_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 3, 0, and 1 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_zwxy(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) norm_4 zwyx; + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) norm_4 bagr; + + /// <summary> + /// Returns a norm_4 that is composed of element 2, element 3, element 1, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_zwyx() const __GPU { + return norm_4(_M_z,_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 3, 1, and 0 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_zwyx(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) norm_4 wxyz; + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) norm_4 argb; + + /// <summary> + /// Returns a norm_4 that is composed of element 3, element 0, element 1, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_wxyz() const __GPU { + return norm_4(_M_w,_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 0, 1, and 2 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_wxyz(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) norm_4 wxzy; + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) norm_4 arbg; + + /// <summary> + /// Returns a norm_4 that is composed of element 3, element 0, element 2, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_wxzy() const __GPU { + return norm_4(_M_w,_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 0, 2, and 1 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_wxzy(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) norm_4 wyxz; + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) norm_4 agrb; + + /// <summary> + /// Returns a norm_4 that is composed of element 3, element 1, element 0, and element 2 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_wyxz() const __GPU { + return norm_4(_M_w,_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 1, 0, and 2 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_wyxz(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) norm_4 wyzx; + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) norm_4 agbr; + + /// <summary> + /// Returns a norm_4 that is composed of element 3, element 1, element 2, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_wyzx() const __GPU { + return norm_4(_M_w,_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 1, 2, and 0 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_wyzx(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) norm_4 wzxy; + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) norm_4 abrg; + + /// <summary> + /// Returns a norm_4 that is composed of element 3, element 2, element 0, and element 1 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_wzxy() const __GPU { + return norm_4(_M_w,_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 2, 0, and 1 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_wzxy(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) norm_4 wzyx; + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this norm_4 as a norm_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) norm_4 abgr; + + /// <summary> + /// Returns a norm_4 that is composed of element 3, element 2, element 1, and element 0 of this norm_4. + /// </summary> + /// <returns> + /// a norm_4. + /// </returns> + norm_4 get_wzyx() const __GPU { + return norm_4(_M_w,_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 2, 1, and 0 of this norm_4 with a norm_4. + /// </summary> + /// <param name="_Value"> + /// a norm_4 value. + /// </param> + void set_wzyx(const norm_4& _Value) __GPU + { + norm_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + }; + + /// <summary> + /// Represent a short vector of 2 double's. + /// </summary> + class double_2 + { + public: + typedef double value_type; + static const int size = 2; + private: + static const _Short_vector_base_type_id _Base_type_id = _Double_type; + private: + value_type _M_x; + value_type _M_y; + + public: + /// <summary> + /// Property for accessing element 0 of this double_2 as a double. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) double x; + /// <summary> + /// Property for accessing element 0 of this double_2 as a double. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) double r; + + /// <summary> + /// Returns element 0 of this double_2. + /// </summary> + /// <returns> + /// Element 0 of this double_2. + /// </returns> + double get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this double_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this double_2. + /// </returns> + double& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this double_2. + /// </summary> + /// <returns> + /// Reference to element 0 of this double_2. + /// </returns> + double& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this double_2 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_x(double _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this double_2 as a double. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) double y; + /// <summary> + /// Property for accessing element 1 of this double_2 as a double. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) double g; + + /// <summary> + /// Returns element 1 of this double_2. + /// </summary> + /// <returns> + /// Element 1 of this double_2. + /// </returns> + double get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this double_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this double_2. + /// </returns> + double& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this double_2. + /// </summary> + /// <returns> + /// Reference to element 1 of this double_2. + /// </returns> + double& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this double_2 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_y(double _Value) __GPU + { + _M_y = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + double_2() __GPU + { + _M_x = 0; + _M_y = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + double_2(double _V0, double _V1) __GPU + { + _M_x = _V0; + _M_y = _V1; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + double_2(double _V) __GPU + { + _M_x = _V; + _M_y = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_2(const uint_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_2(const int_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_2(const float_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_2(const unorm_2& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_2(const norm_2& _Other) __GPU; + + double_2 operator-() const __GPU + { + double_2 _Value = *this; + return double_2(-_Value.x, -_Value.y); + } + + double_2& operator++() __GPU + { + double_2 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + *this = _Value; + return *this; + } + + double_2 operator++(int) __GPU + { + double_2 _Result = *this; + ++(*this); + return _Result; + } + + double_2& operator--() __GPU + { + double_2 _Value = *this; + --_Value._M_x; + --_Value._M_y; + *this = _Value; + return *this; + } + + double_2 operator--(int) __GPU + { + double_2 _Result = *this; + --(*this); + return _Result; + } + + double_2& operator+=(const double_2& _Other) __GPU + { + double_2 _Value1 = *this; + double_2 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + *this = _Value1; + return *this; + } + + double_2& operator-=(const double_2& _Other) __GPU + { + double_2 _Value1 = *this; + double_2 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + *this = _Value1; + return *this; + } + + double_2& operator*=(const double_2& _Other) __GPU + { + double_2 _Value1 = *this; + double_2 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + *this = _Value1; + return *this; + } + + double_2& operator/=(const double_2& _Other) __GPU + { + double_2 _Value1 = *this; + double_2 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this double_2 as a double_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) double_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this double_2 as a double_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) double_2 rg; + + /// <summary> + /// Returns a double_2 that is composed of element 0, and element 1 of this double_2. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_xy() const __GPU { + return double_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this double_2 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_xy(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this double_2 as a double_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) double_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this double_2 as a double_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) double_2 gr; + + /// <summary> + /// Returns a double_2 that is composed of element 1, and element 0 of this double_2. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_yx() const __GPU { + return double_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this double_2 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_yx(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + }; + + /// <summary> + /// Represent a short vector of 3 double's. + /// </summary> + class double_3 + { + public: + typedef double value_type; + static const int size = 3; + private: + static const _Short_vector_base_type_id _Base_type_id = _Double_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + + public: + /// <summary> + /// Property for accessing element 0 of this double_3 as a double. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) double x; + /// <summary> + /// Property for accessing element 0 of this double_3 as a double. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) double r; + + /// <summary> + /// Returns element 0 of this double_3. + /// </summary> + /// <returns> + /// Element 0 of this double_3. + /// </returns> + double get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this double_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this double_3. + /// </returns> + double& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this double_3. + /// </summary> + /// <returns> + /// Reference to element 0 of this double_3. + /// </returns> + double& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this double_3 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_x(double _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this double_3 as a double. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) double y; + /// <summary> + /// Property for accessing element 1 of this double_3 as a double. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) double g; + + /// <summary> + /// Returns element 1 of this double_3. + /// </summary> + /// <returns> + /// Element 1 of this double_3. + /// </returns> + double get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this double_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this double_3. + /// </returns> + double& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this double_3. + /// </summary> + /// <returns> + /// Reference to element 1 of this double_3. + /// </returns> + double& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this double_3 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_y(double _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this double_3 as a double. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) double z; + /// <summary> + /// Property for accessing element 2 of this double_3 as a double. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) double b; + + /// <summary> + /// Returns element 2 of this double_3. + /// </summary> + /// <returns> + /// Element 2 of this double_3. + /// </returns> + double get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this double_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this double_3. + /// </returns> + double& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this double_3. + /// </summary> + /// <returns> + /// Reference to element 2 of this double_3. + /// </returns> + double& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this double_3 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_z(double _Value) __GPU + { + _M_z = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + double_3() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + double_3(double _V0, double _V1, double _V2) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + double_3(double _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_3(const uint_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_3(const int_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_3(const float_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_3(const unorm_3& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_3(const norm_3& _Other) __GPU; + + double_3 operator-() const __GPU + { + double_3 _Value = *this; + return double_3(-_Value.x, -_Value.y, -_Value.z); + } + + double_3& operator++() __GPU + { + double_3 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + *this = _Value; + return *this; + } + + double_3 operator++(int) __GPU + { + double_3 _Result = *this; + ++(*this); + return _Result; + } + + double_3& operator--() __GPU + { + double_3 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + *this = _Value; + return *this; + } + + double_3 operator--(int) __GPU + { + double_3 _Result = *this; + --(*this); + return _Result; + } + + double_3& operator+=(const double_3& _Other) __GPU + { + double_3 _Value1 = *this; + double_3 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + *this = _Value1; + return *this; + } + + double_3& operator-=(const double_3& _Other) __GPU + { + double_3 _Value1 = *this; + double_3 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + *this = _Value1; + return *this; + } + + double_3& operator*=(const double_3& _Other) __GPU + { + double_3 _Value1 = *this; + double_3 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + *this = _Value1; + return *this; + } + + double_3& operator/=(const double_3& _Other) __GPU + { + double_3 _Value1 = *this; + double_3 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) double_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) double_2 rg; + + /// <summary> + /// Returns a double_2 that is composed of element 0, and element 1 of this double_3. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_xy() const __GPU { + return double_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this double_3 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_xy(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) double_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) double_2 rb; + + /// <summary> + /// Returns a double_2 that is composed of element 0, and element 2 of this double_3. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_xz() const __GPU { + return double_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this double_3 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_xz(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) double_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) double_2 gr; + + /// <summary> + /// Returns a double_2 that is composed of element 1, and element 0 of this double_3. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_yx() const __GPU { + return double_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this double_3 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_yx(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) double_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) double_2 gb; + + /// <summary> + /// Returns a double_2 that is composed of element 1, and element 2 of this double_3. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_yz() const __GPU { + return double_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this double_3 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_yz(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) double_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) double_2 br; + + /// <summary> + /// Returns a double_2 that is composed of element 2, and element 0 of this double_3. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_zx() const __GPU { + return double_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this double_3 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_zx(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) double_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this double_3 as a double_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) double_2 bg; + + /// <summary> + /// Returns a double_2 that is composed of element 2, and element 1 of this double_3. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_zy() const __GPU { + return double_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this double_3 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_zy(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) double_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) double_3 rgb; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 1, and element 2 of this double_3. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xyz() const __GPU { + return double_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this double_3 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xyz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) double_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) double_3 rbg; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 2, and element 1 of this double_3. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xzy() const __GPU { + return double_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this double_3 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xzy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) double_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) double_3 grb; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 0, and element 2 of this double_3. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_yxz() const __GPU { + return double_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this double_3 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_yxz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) double_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) double_3 gbr; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 2, and element 0 of this double_3. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_yzx() const __GPU { + return double_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this double_3 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_yzx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) double_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) double_3 brg; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 0, and element 1 of this double_3. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zxy() const __GPU { + return double_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this double_3 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zxy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) double_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this double_3 as a double_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) double_3 bgr; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 1, and element 0 of this double_3. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zyx() const __GPU { + return double_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this double_3 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zyx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + }; + + /// <summary> + /// Represent a short vector of 4 double's. + /// </summary> + class double_4 + { + public: + typedef double value_type; + static const int size = 4; + private: + static const _Short_vector_base_type_id _Base_type_id = _Double_type; + private: + value_type _M_x; + value_type _M_y; + value_type _M_z; + value_type _M_w; + + public: + /// <summary> + /// Property for accessing element 0 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) double x; + /// <summary> + /// Property for accessing element 0 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_x, put=set_x) ) double r; + + /// <summary> + /// Returns element 0 of this double_4. + /// </summary> + /// <returns> + /// Element 0 of this double_4. + /// </returns> + double get_x() const __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this double_4. + /// </returns> + double& ref_x() __GPU { + return _M_x; + } + + /// <summary> + /// Returns reference to element 0 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 0 of this double_4. + /// </returns> + double& ref_r() __GPU { + return _M_x; + } + + /// <summary> + /// Set element 0 of this double_4 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_x(double _Value) __GPU + { + _M_x = _Value; + } + + /// <summary> + /// Property for accessing element 1 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) double y; + /// <summary> + /// Property for accessing element 1 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_y, put=set_y) ) double g; + + /// <summary> + /// Returns element 1 of this double_4. + /// </summary> + /// <returns> + /// Element 1 of this double_4. + /// </returns> + double get_y() const __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this double_4. + /// </returns> + double& ref_y() __GPU { + return _M_y; + } + + /// <summary> + /// Returns reference to element 1 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 1 of this double_4. + /// </returns> + double& ref_g() __GPU { + return _M_y; + } + + /// <summary> + /// Set element 1 of this double_4 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_y(double _Value) __GPU + { + _M_y = _Value; + } + + /// <summary> + /// Property for accessing element 2 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) double z; + /// <summary> + /// Property for accessing element 2 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_z, put=set_z) ) double b; + + /// <summary> + /// Returns element 2 of this double_4. + /// </summary> + /// <returns> + /// Element 2 of this double_4. + /// </returns> + double get_z() const __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this double_4. + /// </returns> + double& ref_z() __GPU { + return _M_z; + } + + /// <summary> + /// Returns reference to element 2 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 2 of this double_4. + /// </returns> + double& ref_b() __GPU { + return _M_z; + } + + /// <summary> + /// Set element 2 of this double_4 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_z(double _Value) __GPU + { + _M_z = _Value; + } + + /// <summary> + /// Property for accessing element 3 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) double w; + /// <summary> + /// Property for accessing element 3 of this double_4 as a double. + /// </summary> + __declspec( property( get=get_w, put=set_w) ) double a; + + /// <summary> + /// Returns element 3 of this double_4. + /// </summary> + /// <returns> + /// Element 3 of this double_4. + /// </returns> + double get_w() const __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this double_4. + /// </returns> + double& ref_w() __GPU { + return _M_w; + } + + /// <summary> + /// Returns reference to element 3 of this double_4. + /// </summary> + /// <returns> + /// Reference to element 3 of this double_4. + /// </returns> + double& ref_a() __GPU { + return _M_w; + } + + /// <summary> + /// Set element 3 of this double_4 with a double. + /// </summary> + /// <param name="_Value"> + /// a double value. + /// </param> + void set_w(double _Value) __GPU + { + _M_w = _Value; + } + + public: + /// <summary> + /// Default constructor, initializes all elements with 0. + /// </summary> + double_4() __GPU + { + _M_x = 0; + _M_y = 0; + _M_z = 0; + _M_w = 0; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V0"> + /// The value to initialize element 0. + /// </param> + /// <param name="_V1"> + /// The value to initialize element 1. + /// </param> + /// <param name="_V2"> + /// The value to initialize element 2. + /// </param> + /// <param name="_V3"> + /// The value to initialize element 3. + /// </param> + double_4(double _V0, double _V1, double _V2, double _V3) __GPU + { + _M_x = _V0; + _M_y = _V1; + _M_z = _V2; + _M_w = _V3; + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="_V"> + /// The value for initialization. + /// </param> + double_4(double _V) __GPU + { + _M_x = _V; + _M_y = _V; + _M_z = _V; + _M_w = _V; + } + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_4(const uint_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_4(const int_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_4(const float_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_4(const unorm_4& _Other) __GPU; + + /// <summary> + /// Constructor. + /// Each element is initialized by casting from the corresponding element in _Other. + /// </summary> + /// <param name="_Other"> + /// The object used to initialize. + /// </param> + explicit inline double_4(const norm_4& _Other) __GPU; + + double_4 operator-() const __GPU + { + double_4 _Value = *this; + return double_4(-_Value.x, -_Value.y, -_Value.z, -_Value.w); + } + + double_4& operator++() __GPU + { + double_4 _Value = *this; + ++_Value._M_x; + ++_Value._M_y; + ++_Value._M_z; + ++_Value._M_w; + *this = _Value; + return *this; + } + + double_4 operator++(int) __GPU + { + double_4 _Result = *this; + ++(*this); + return _Result; + } + + double_4& operator--() __GPU + { + double_4 _Value = *this; + --_Value._M_x; + --_Value._M_y; + --_Value._M_z; + --_Value._M_w; + *this = _Value; + return *this; + } + + double_4 operator--(int) __GPU + { + double_4 _Result = *this; + --(*this); + return _Result; + } + + double_4& operator+=(const double_4& _Other) __GPU + { + double_4 _Value1 = *this; + double_4 _Value2 = _Other; + _Value1.x += _Value2.x; + _Value1.y += _Value2.y; + _Value1.z += _Value2.z; + _Value1.w += _Value2.w; + *this = _Value1; + return *this; + } + + double_4& operator-=(const double_4& _Other) __GPU + { + double_4 _Value1 = *this; + double_4 _Value2 = _Other; + _Value1.x -= _Value2.x; + _Value1.y -= _Value2.y; + _Value1.z -= _Value2.z; + _Value1.w -= _Value2.w; + *this = _Value1; + return *this; + } + + double_4& operator*=(const double_4& _Other) __GPU + { + double_4 _Value1 = *this; + double_4 _Value2 = _Other; + _Value1.x *= _Value2.x; + _Value1.y *= _Value2.y; + _Value1.z *= _Value2.z; + _Value1.w *= _Value2.w; + *this = _Value1; + return *this; + } + + double_4& operator/=(const double_4& _Other) __GPU + { + double_4 _Value1 = *this; + double_4 _Value2 = _Other; + _Value1.x /= _Value2.x; + _Value1.y /= _Value2.y; + _Value1.z /= _Value2.z; + _Value1.w /= _Value2.w; + *this = _Value1; + return *this; + } + + public: + /// <summary> + /// Property for accessing element 0, and 1 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) double_2 xy; + /// <summary> + /// Property for accessing element 0, and 1 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_xy, put=set_xy) ) double_2 rg; + + /// <summary> + /// Returns a double_2 that is composed of element 0, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_xy() const __GPU { + return double_2(_M_x,_M_y); + } + + /// <summary> + /// Set element 0, and 1 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_xy(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 2 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) double_2 xz; + /// <summary> + /// Property for accessing element 0, and 2 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_xz, put=set_xz) ) double_2 rb; + + /// <summary> + /// Returns a double_2 that is composed of element 0, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_xz() const __GPU { + return double_2(_M_x,_M_z); + } + + /// <summary> + /// Set element 0, and 2 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_xz(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, and 3 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) double_2 xw; + /// <summary> + /// Property for accessing element 0, and 3 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_xw, put=set_xw) ) double_2 ra; + + /// <summary> + /// Returns a double_2 that is composed of element 0, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_xw() const __GPU { + return double_2(_M_x,_M_w); + } + + /// <summary> + /// Set element 0, and 3 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_xw(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 0 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) double_2 yx; + /// <summary> + /// Property for accessing element 1, and 0 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_yx, put=set_yx) ) double_2 gr; + + /// <summary> + /// Returns a double_2 that is composed of element 1, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_yx() const __GPU { + return double_2(_M_y,_M_x); + } + + /// <summary> + /// Set element 1, and 0 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_yx(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 2 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) double_2 yz; + /// <summary> + /// Property for accessing element 1, and 2 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_yz, put=set_yz) ) double_2 gb; + + /// <summary> + /// Returns a double_2 that is composed of element 1, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_yz() const __GPU { + return double_2(_M_y,_M_z); + } + + /// <summary> + /// Set element 1, and 2 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_yz(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 1, and 3 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) double_2 yw; + /// <summary> + /// Property for accessing element 1, and 3 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_yw, put=set_yw) ) double_2 ga; + + /// <summary> + /// Returns a double_2 that is composed of element 1, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_yw() const __GPU { + return double_2(_M_y,_M_w); + } + + /// <summary> + /// Set element 1, and 3 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_yw(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 0 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) double_2 zx; + /// <summary> + /// Property for accessing element 2, and 0 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_zx, put=set_zx) ) double_2 br; + + /// <summary> + /// Returns a double_2 that is composed of element 2, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_zx() const __GPU { + return double_2(_M_z,_M_x); + } + + /// <summary> + /// Set element 2, and 0 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_zx(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 1 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) double_2 zy; + /// <summary> + /// Property for accessing element 2, and 1 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_zy, put=set_zy) ) double_2 bg; + + /// <summary> + /// Returns a double_2 that is composed of element 2, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_zy() const __GPU { + return double_2(_M_z,_M_y); + } + + /// <summary> + /// Set element 2, and 1 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_zy(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 2, and 3 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) double_2 zw; + /// <summary> + /// Property for accessing element 2, and 3 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_zw, put=set_zw) ) double_2 ba; + + /// <summary> + /// Returns a double_2 that is composed of element 2, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_zw() const __GPU { + return double_2(_M_z,_M_w); + } + + /// <summary> + /// Set element 2, and 3 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_zw(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 0 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) double_2 wx; + /// <summary> + /// Property for accessing element 3, and 0 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_wx, put=set_wx) ) double_2 ar; + + /// <summary> + /// Returns a double_2 that is composed of element 3, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_wx() const __GPU { + return double_2(_M_w,_M_x); + } + + /// <summary> + /// Set element 3, and 0 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_wx(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 1 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) double_2 wy; + /// <summary> + /// Property for accessing element 3, and 1 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_wy, put=set_wy) ) double_2 ag; + + /// <summary> + /// Returns a double_2 that is composed of element 3, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_wy() const __GPU { + return double_2(_M_w,_M_y); + } + + /// <summary> + /// Set element 3, and 1 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_wy(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + } + + /// <summary> + /// Property for accessing element 3, and 2 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) double_2 wz; + /// <summary> + /// Property for accessing element 3, and 2 of this double_4 as a double_2. + /// </summary> + __declspec( property( get=get_wz, put=set_wz) ) double_2 ab; + + /// <summary> + /// Returns a double_2 that is composed of element 3, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_2. + /// </returns> + double_2 get_wz() const __GPU { + return double_2(_M_w,_M_z); + } + + /// <summary> + /// Set element 3, and 2 of this double_4 with a double_2. + /// </summary> + /// <param name="_Value"> + /// a double_2 value. + /// </param> + void set_wz(const double_2& _Value) __GPU + { + double_2 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + } + + /// <summary> + /// Property for accessing element 0, 1, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) double_3 xyz; + /// <summary> + /// Property for accessing element 0, 1, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xyz, put=set_xyz) ) double_3 rgb; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 1, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xyz() const __GPU { + return double_3(_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 1, and 2 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xyz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) double_3 xyw; + /// <summary> + /// Property for accessing element 0, 1, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xyw, put=set_xyw) ) double_3 rga; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 1, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xyw() const __GPU { + return double_3(_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 1, and 3 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xyw(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) double_3 xzy; + /// <summary> + /// Property for accessing element 0, 2, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xzy, put=set_xzy) ) double_3 rbg; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 2, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xzy() const __GPU { + return double_3(_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 2, and 1 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xzy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 2, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) double_3 xzw; + /// <summary> + /// Property for accessing element 0, 2, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xzw, put=set_xzw) ) double_3 rba; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 2, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xzw() const __GPU { + return double_3(_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 2, and 3 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xzw(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) double_3 xwy; + /// <summary> + /// Property for accessing element 0, 3, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xwy, put=set_xwy) ) double_3 rag; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 3, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xwy() const __GPU { + return double_3(_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 3, and 1 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xwy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 3, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) double_3 xwz; + /// <summary> + /// Property for accessing element 0, 3, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_xwz, put=set_xwz) ) double_3 rab; + + /// <summary> + /// Returns a double_3 that is composed of element 0, element 3, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_xwz() const __GPU { + return double_3(_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 3, and 2 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_xwz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) double_3 yxz; + /// <summary> + /// Property for accessing element 1, 0, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yxz, put=set_yxz) ) double_3 grb; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 0, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_yxz() const __GPU { + return double_3(_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 0, and 2 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_yxz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 0, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) double_3 yxw; + /// <summary> + /// Property for accessing element 1, 0, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yxw, put=set_yxw) ) double_3 gra; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 0, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_yxw() const __GPU { + return double_3(_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 0, and 3 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_yxw(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) double_3 yzx; + /// <summary> + /// Property for accessing element 1, 2, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yzx, put=set_yzx) ) double_3 gbr; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 2, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_yzx() const __GPU { + return double_3(_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 2, and 0 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_yzx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 2, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) double_3 yzw; + /// <summary> + /// Property for accessing element 1, 2, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_yzw, put=set_yzw) ) double_3 gba; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 2, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_yzw() const __GPU { + return double_3(_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 2, and 3 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_yzw(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) double_3 ywx; + /// <summary> + /// Property for accessing element 1, 3, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_ywx, put=set_ywx) ) double_3 gar; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 3, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_ywx() const __GPU { + return double_3(_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 3, and 0 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_ywx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 1, 3, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) double_3 ywz; + /// <summary> + /// Property for accessing element 1, 3, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_ywz, put=set_ywz) ) double_3 gab; + + /// <summary> + /// Returns a double_3 that is composed of element 1, element 3, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_ywz() const __GPU { + return double_3(_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 3, and 2 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_ywz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) double_3 zxy; + /// <summary> + /// Property for accessing element 2, 0, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zxy, put=set_zxy) ) double_3 brg; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 0, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zxy() const __GPU { + return double_3(_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 0, and 1 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zxy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 0, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) double_3 zxw; + /// <summary> + /// Property for accessing element 2, 0, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zxw, put=set_zxw) ) double_3 bra; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 0, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zxw() const __GPU { + return double_3(_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 0, and 3 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zxw(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) double_3 zyx; + /// <summary> + /// Property for accessing element 2, 1, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zyx, put=set_zyx) ) double_3 bgr; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 1, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zyx() const __GPU { + return double_3(_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 1, and 0 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zyx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 1, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) double_3 zyw; + /// <summary> + /// Property for accessing element 2, 1, and 3 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zyw, put=set_zyw) ) double_3 bga; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 1, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zyw() const __GPU { + return double_3(_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 1, and 3 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zyw(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) double_3 zwx; + /// <summary> + /// Property for accessing element 2, 3, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zwx, put=set_zwx) ) double_3 bar; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 3, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zwx() const __GPU { + return double_3(_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 3, and 0 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zwx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 2, 3, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) double_3 zwy; + /// <summary> + /// Property for accessing element 2, 3, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_zwy, put=set_zwy) ) double_3 bag; + + /// <summary> + /// Returns a double_3 that is composed of element 2, element 3, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_zwy() const __GPU { + return double_3(_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 3, and 1 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_zwy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) double_3 wxy; + /// <summary> + /// Property for accessing element 3, 0, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wxy, put=set_wxy) ) double_3 arg; + + /// <summary> + /// Returns a double_3 that is composed of element 3, element 0, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_wxy() const __GPU { + return double_3(_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 0, and 1 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_wxy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 0, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) double_3 wxz; + /// <summary> + /// Property for accessing element 3, 0, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wxz, put=set_wxz) ) double_3 arb; + + /// <summary> + /// Returns a double_3 that is composed of element 3, element 0, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_wxz() const __GPU { + return double_3(_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 0, and 2 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_wxz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) double_3 wyx; + /// <summary> + /// Property for accessing element 3, 1, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wyx, put=set_wyx) ) double_3 agr; + + /// <summary> + /// Returns a double_3 that is composed of element 3, element 1, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_wyx() const __GPU { + return double_3(_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 1, and 0 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_wyx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 1, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) double_3 wyz; + /// <summary> + /// Property for accessing element 3, 1, and 2 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wyz, put=set_wyz) ) double_3 agb; + + /// <summary> + /// Returns a double_3 that is composed of element 3, element 1, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_wyz() const __GPU { + return double_3(_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 1, and 2 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_wyz(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) double_3 wzx; + /// <summary> + /// Property for accessing element 3, 2, and 0 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wzx, put=set_wzx) ) double_3 abr; + + /// <summary> + /// Returns a double_3 that is composed of element 3, element 2, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_wzx() const __GPU { + return double_3(_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 2, and 0 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_wzx(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + } + + /// <summary> + /// Property for accessing element 3, 2, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) double_3 wzy; + /// <summary> + /// Property for accessing element 3, 2, and 1 of this double_4 as a double_3. + /// </summary> + __declspec( property( get=get_wzy, put=set_wzy) ) double_3 abg; + + /// <summary> + /// Returns a double_3 that is composed of element 3, element 2, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_3. + /// </returns> + double_3 get_wzy() const __GPU { + return double_3(_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 2, and 1 of this double_4 with a double_3. + /// </summary> + /// <param name="_Value"> + /// a double_3 value. + /// </param> + void set_wzy(const double_3& _Value) __GPU + { + double_3 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + } + + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) double_4 xyzw; + /// <summary> + /// Property for accessing element 0, 1, 2, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xyzw, put=set_xyzw) ) double_4 rgba; + + /// <summary> + /// Returns a double_4 that is composed of element 0, element 1, element 2, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_xyzw() const __GPU { + return double_4(_M_x,_M_y,_M_z,_M_w); + } + + /// <summary> + /// Set element 0, 1, 2, and 3 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_xyzw(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) double_4 xywz; + /// <summary> + /// Property for accessing element 0, 1, 3, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xywz, put=set_xywz) ) double_4 rgab; + + /// <summary> + /// Returns a double_4 that is composed of element 0, element 1, element 3, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_xywz() const __GPU { + return double_4(_M_x,_M_y,_M_w,_M_z); + } + + /// <summary> + /// Set element 0, 1, 3, and 2 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_xywz(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_x = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) double_4 xzyw; + /// <summary> + /// Property for accessing element 0, 2, 1, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xzyw, put=set_xzyw) ) double_4 rbga; + + /// <summary> + /// Returns a double_4 that is composed of element 0, element 2, element 1, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_xzyw() const __GPU { + return double_4(_M_x,_M_z,_M_y,_M_w); + } + + /// <summary> + /// Set element 0, 2, 1, and 3 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_xzyw(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) double_4 xzwy; + /// <summary> + /// Property for accessing element 0, 2, 3, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xzwy, put=set_xzwy) ) double_4 rbag; + + /// <summary> + /// Returns a double_4 that is composed of element 0, element 2, element 3, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_xzwy() const __GPU { + return double_4(_M_x,_M_z,_M_w,_M_y); + } + + /// <summary> + /// Set element 0, 2, 3, and 1 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_xzwy(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_x = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) double_4 xwyz; + /// <summary> + /// Property for accessing element 0, 3, 1, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xwyz, put=set_xwyz) ) double_4 ragb; + + /// <summary> + /// Returns a double_4 that is composed of element 0, element 3, element 1, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_xwyz() const __GPU { + return double_4(_M_x,_M_w,_M_y,_M_z); + } + + /// <summary> + /// Set element 0, 3, 1, and 2 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_xwyz(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) double_4 xwzy; + /// <summary> + /// Property for accessing element 0, 3, 2, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_xwzy, put=set_xwzy) ) double_4 rabg; + + /// <summary> + /// Returns a double_4 that is composed of element 0, element 3, element 2, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_xwzy() const __GPU { + return double_4(_M_x,_M_w,_M_z,_M_y); + } + + /// <summary> + /// Set element 0, 3, 2, and 1 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_xwzy(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_x = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) double_4 yxzw; + /// <summary> + /// Property for accessing element 1, 0, 2, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yxzw, put=set_yxzw) ) double_4 grba; + + /// <summary> + /// Returns a double_4 that is composed of element 1, element 0, element 2, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_yxzw() const __GPU { + return double_4(_M_y,_M_x,_M_z,_M_w); + } + + /// <summary> + /// Set element 1, 0, 2, and 3 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_yxzw(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) double_4 yxwz; + /// <summary> + /// Property for accessing element 1, 0, 3, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yxwz, put=set_yxwz) ) double_4 grab; + + /// <summary> + /// Returns a double_4 that is composed of element 1, element 0, element 3, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_yxwz() const __GPU { + return double_4(_M_y,_M_x,_M_w,_M_z); + } + + /// <summary> + /// Set element 1, 0, 3, and 2 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_yxwz(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_y = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) double_4 yzxw; + /// <summary> + /// Property for accessing element 1, 2, 0, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yzxw, put=set_yzxw) ) double_4 gbra; + + /// <summary> + /// Returns a double_4 that is composed of element 1, element 2, element 0, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_yzxw() const __GPU { + return double_4(_M_y,_M_z,_M_x,_M_w); + } + + /// <summary> + /// Set element 1, 2, 0, and 3 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_yzxw(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) double_4 yzwx; + /// <summary> + /// Property for accessing element 1, 2, 3, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_yzwx, put=set_yzwx) ) double_4 gbar; + + /// <summary> + /// Returns a double_4 that is composed of element 1, element 2, element 3, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_yzwx() const __GPU { + return double_4(_M_y,_M_z,_M_w,_M_x); + } + + /// <summary> + /// Set element 1, 2, 3, and 0 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_yzwx(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_y = _Val.x; + _M_z = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) double_4 ywxz; + /// <summary> + /// Property for accessing element 1, 3, 0, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_ywxz, put=set_ywxz) ) double_4 garb; + + /// <summary> + /// Returns a double_4 that is composed of element 1, element 3, element 0, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_ywxz() const __GPU { + return double_4(_M_y,_M_w,_M_x,_M_z); + } + + /// <summary> + /// Set element 1, 3, 0, and 2 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_ywxz(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) double_4 ywzx; + /// <summary> + /// Property for accessing element 1, 3, 2, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_ywzx, put=set_ywzx) ) double_4 gabr; + + /// <summary> + /// Returns a double_4 that is composed of element 1, element 3, element 2, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_ywzx() const __GPU { + return double_4(_M_y,_M_w,_M_z,_M_x); + } + + /// <summary> + /// Set element 1, 3, 2, and 0 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_ywzx(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_y = _Val.x; + _M_w = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) double_4 zxyw; + /// <summary> + /// Property for accessing element 2, 0, 1, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zxyw, put=set_zxyw) ) double_4 brga; + + /// <summary> + /// Returns a double_4 that is composed of element 2, element 0, element 1, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_zxyw() const __GPU { + return double_4(_M_z,_M_x,_M_y,_M_w); + } + + /// <summary> + /// Set element 2, 0, 1, and 3 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_zxyw(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) double_4 zxwy; + /// <summary> + /// Property for accessing element 2, 0, 3, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zxwy, put=set_zxwy) ) double_4 brag; + + /// <summary> + /// Returns a double_4 that is composed of element 2, element 0, element 3, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_zxwy() const __GPU { + return double_4(_M_z,_M_x,_M_w,_M_y); + } + + /// <summary> + /// Set element 2, 0, 3, and 1 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_zxwy(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_z = _Val.x; + _M_x = _Val.y; + _M_w = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) double_4 zyxw; + /// <summary> + /// Property for accessing element 2, 1, 0, and 3 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zyxw, put=set_zyxw) ) double_4 bgra; + + /// <summary> + /// Returns a double_4 that is composed of element 2, element 1, element 0, and element 3 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_zyxw() const __GPU { + return double_4(_M_z,_M_y,_M_x,_M_w); + } + + /// <summary> + /// Set element 2, 1, 0, and 3 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_zyxw(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_w = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) double_4 zywx; + /// <summary> + /// Property for accessing element 2, 1, 3, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zywx, put=set_zywx) ) double_4 bgar; + + /// <summary> + /// Returns a double_4 that is composed of element 2, element 1, element 3, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_zywx() const __GPU { + return double_4(_M_z,_M_y,_M_w,_M_x); + } + + /// <summary> + /// Set element 2, 1, 3, and 0 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_zywx(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_z = _Val.x; + _M_y = _Val.y; + _M_w = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) double_4 zwxy; + /// <summary> + /// Property for accessing element 2, 3, 0, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zwxy, put=set_zwxy) ) double_4 barg; + + /// <summary> + /// Returns a double_4 that is composed of element 2, element 3, element 0, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_zwxy() const __GPU { + return double_4(_M_z,_M_w,_M_x,_M_y); + } + + /// <summary> + /// Set element 2, 3, 0, and 1 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_zwxy(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) double_4 zwyx; + /// <summary> + /// Property for accessing element 2, 3, 1, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_zwyx, put=set_zwyx) ) double_4 bagr; + + /// <summary> + /// Returns a double_4 that is composed of element 2, element 3, element 1, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_zwyx() const __GPU { + return double_4(_M_z,_M_w,_M_y,_M_x); + } + + /// <summary> + /// Set element 2, 3, 1, and 0 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_zwyx(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_z = _Val.x; + _M_w = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) double_4 wxyz; + /// <summary> + /// Property for accessing element 3, 0, 1, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wxyz, put=set_wxyz) ) double_4 argb; + + /// <summary> + /// Returns a double_4 that is composed of element 3, element 0, element 1, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_wxyz() const __GPU { + return double_4(_M_w,_M_x,_M_y,_M_z); + } + + /// <summary> + /// Set element 3, 0, 1, and 2 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_wxyz(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_y = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) double_4 wxzy; + /// <summary> + /// Property for accessing element 3, 0, 2, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wxzy, put=set_wxzy) ) double_4 arbg; + + /// <summary> + /// Returns a double_4 that is composed of element 3, element 0, element 2, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_wxzy() const __GPU { + return double_4(_M_w,_M_x,_M_z,_M_y); + } + + /// <summary> + /// Set element 3, 0, 2, and 1 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_wxzy(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_w = _Val.x; + _M_x = _Val.y; + _M_z = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) double_4 wyxz; + /// <summary> + /// Property for accessing element 3, 1, 0, and 2 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wyxz, put=set_wyxz) ) double_4 agrb; + + /// <summary> + /// Returns a double_4 that is composed of element 3, element 1, element 0, and element 2 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_wyxz() const __GPU { + return double_4(_M_w,_M_y,_M_x,_M_z); + } + + /// <summary> + /// Set element 3, 1, 0, and 2 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_wyxz(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_x = _Val.z; + _M_z = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) double_4 wyzx; + /// <summary> + /// Property for accessing element 3, 1, 2, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wyzx, put=set_wyzx) ) double_4 agbr; + + /// <summary> + /// Returns a double_4 that is composed of element 3, element 1, element 2, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_wyzx() const __GPU { + return double_4(_M_w,_M_y,_M_z,_M_x); + } + + /// <summary> + /// Set element 3, 1, 2, and 0 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_wyzx(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_w = _Val.x; + _M_y = _Val.y; + _M_z = _Val.z; + _M_x = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) double_4 wzxy; + /// <summary> + /// Property for accessing element 3, 2, 0, and 1 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wzxy, put=set_wzxy) ) double_4 abrg; + + /// <summary> + /// Returns a double_4 that is composed of element 3, element 2, element 0, and element 1 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_wzxy() const __GPU { + return double_4(_M_w,_M_z,_M_x,_M_y); + } + + /// <summary> + /// Set element 3, 2, 0, and 1 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_wzxy(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_x = _Val.z; + _M_y = _Val.w; + } + + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) double_4 wzyx; + /// <summary> + /// Property for accessing element 3, 2, 1, and 0 of this double_4 as a double_4. + /// </summary> + __declspec( property( get=get_wzyx, put=set_wzyx) ) double_4 abgr; + + /// <summary> + /// Returns a double_4 that is composed of element 3, element 2, element 1, and element 0 of this double_4. + /// </summary> + /// <returns> + /// a double_4. + /// </returns> + double_4 get_wzyx() const __GPU { + return double_4(_M_w,_M_z,_M_y,_M_x); + } + + /// <summary> + /// Set element 3, 2, 1, and 0 of this double_4 with a double_4. + /// </summary> + /// <param name="_Value"> + /// a double_4 value. + /// </param> + void set_wzyx(const double_4& _Value) __GPU + { + double_4 _Val = _Value; + _M_w = _Val.x; + _M_z = _Val.y; + _M_y = _Val.z; + _M_x = _Val.w; + } + + }; + + + uint_2::uint_2(const int_2& _Other) __GPU + { + int_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + uint_2::uint_2(const float_2& _Other) __GPU + { + float_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + uint_2::uint_2(const unorm_2& _Other) __GPU + { + unorm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + uint_2::uint_2(const norm_2& _Other) __GPU + { + norm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + uint_2::uint_2(const double_2& _Other) __GPU + { + double_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + + uint_3::uint_3(const int_3& _Other) __GPU + { + int_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + uint_3::uint_3(const float_3& _Other) __GPU + { + float_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + uint_3::uint_3(const unorm_3& _Other) __GPU + { + unorm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + uint_3::uint_3(const norm_3& _Other) __GPU + { + norm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + uint_3::uint_3(const double_3& _Other) __GPU + { + double_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + + uint_4::uint_4(const int_4& _Other) __GPU + { + int_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + uint_4::uint_4(const float_4& _Other) __GPU + { + float_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + uint_4::uint_4(const unorm_4& _Other) __GPU + { + unorm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + uint_4::uint_4(const norm_4& _Other) __GPU + { + norm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + uint_4::uint_4(const double_4& _Other) __GPU + { + double_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + + int_2::int_2(const uint_2& _Other) __GPU + { + uint_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + int_2::int_2(const float_2& _Other) __GPU + { + float_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + int_2::int_2(const unorm_2& _Other) __GPU + { + unorm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + int_2::int_2(const norm_2& _Other) __GPU + { + norm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + int_2::int_2(const double_2& _Other) __GPU + { + double_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + + int_3::int_3(const uint_3& _Other) __GPU + { + uint_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + int_3::int_3(const float_3& _Other) __GPU + { + float_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + int_3::int_3(const unorm_3& _Other) __GPU + { + unorm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + int_3::int_3(const norm_3& _Other) __GPU + { + norm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + int_3::int_3(const double_3& _Other) __GPU + { + double_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + + int_4::int_4(const uint_4& _Other) __GPU + { + uint_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + int_4::int_4(const float_4& _Other) __GPU + { + float_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + int_4::int_4(const unorm_4& _Other) __GPU + { + unorm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + int_4::int_4(const norm_4& _Other) __GPU + { + norm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + int_4::int_4(const double_4& _Other) __GPU + { + double_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + + float_2::float_2(const uint_2& _Other) __GPU + { + uint_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + float_2::float_2(const int_2& _Other) __GPU + { + int_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + float_2::float_2(const unorm_2& _Other) __GPU + { + unorm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + float_2::float_2(const norm_2& _Other) __GPU + { + norm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + float_2::float_2(const double_2& _Other) __GPU + { + double_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + + float_3::float_3(const uint_3& _Other) __GPU + { + uint_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + float_3::float_3(const int_3& _Other) __GPU + { + int_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + float_3::float_3(const unorm_3& _Other) __GPU + { + unorm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + float_3::float_3(const norm_3& _Other) __GPU + { + norm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + float_3::float_3(const double_3& _Other) __GPU + { + double_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + + float_4::float_4(const uint_4& _Other) __GPU + { + uint_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + float_4::float_4(const int_4& _Other) __GPU + { + int_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + float_4::float_4(const unorm_4& _Other) __GPU + { + unorm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + float_4::float_4(const norm_4& _Other) __GPU + { + norm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + float_4::float_4(const double_4& _Other) __GPU + { + double_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + + unorm_2::unorm_2(const uint_2& _Other) __GPU + { + uint_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + unorm_2::unorm_2(const int_2& _Other) __GPU + { + int_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + unorm_2::unorm_2(const float_2& _Other) __GPU + { + float_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + unorm_2::unorm_2(const norm_2& _Other) __GPU + { + norm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + unorm_2::unorm_2(const double_2& _Other) __GPU + { + double_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + + unorm_3::unorm_3(const uint_3& _Other) __GPU + { + uint_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + unorm_3::unorm_3(const int_3& _Other) __GPU + { + int_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + unorm_3::unorm_3(const float_3& _Other) __GPU + { + float_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + unorm_3::unorm_3(const norm_3& _Other) __GPU + { + norm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + unorm_3::unorm_3(const double_3& _Other) __GPU + { + double_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + + unorm_4::unorm_4(const uint_4& _Other) __GPU + { + uint_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + unorm_4::unorm_4(const int_4& _Other) __GPU + { + int_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + unorm_4::unorm_4(const float_4& _Other) __GPU + { + float_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + unorm_4::unorm_4(const norm_4& _Other) __GPU + { + norm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + unorm_4::unorm_4(const double_4& _Other) __GPU + { + double_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + + norm_2::norm_2(const uint_2& _Other) __GPU + { + uint_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + norm_2::norm_2(const int_2& _Other) __GPU + { + int_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + norm_2::norm_2(const float_2& _Other) __GPU + { + float_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + norm_2::norm_2(const unorm_2& _Other) __GPU + { + unorm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + norm_2::norm_2(const double_2& _Other) __GPU + { + double_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + + norm_3::norm_3(const uint_3& _Other) __GPU + { + uint_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + norm_3::norm_3(const int_3& _Other) __GPU + { + int_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + norm_3::norm_3(const float_3& _Other) __GPU + { + float_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + norm_3::norm_3(const unorm_3& _Other) __GPU + { + unorm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + norm_3::norm_3(const double_3& _Other) __GPU + { + double_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + + norm_4::norm_4(const uint_4& _Other) __GPU + { + uint_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + norm_4::norm_4(const int_4& _Other) __GPU + { + int_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + norm_4::norm_4(const float_4& _Other) __GPU + { + float_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + norm_4::norm_4(const unorm_4& _Other) __GPU + { + unorm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + norm_4::norm_4(const double_4& _Other) __GPU + { + double_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + + double_2::double_2(const uint_2& _Other) __GPU + { + uint_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + double_2::double_2(const int_2& _Other) __GPU + { + int_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + double_2::double_2(const float_2& _Other) __GPU + { + float_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + double_2::double_2(const unorm_2& _Other) __GPU + { + unorm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + double_2::double_2(const norm_2& _Other) __GPU + { + norm_2 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + } + + + double_3::double_3(const uint_3& _Other) __GPU + { + uint_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + double_3::double_3(const int_3& _Other) __GPU + { + int_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + double_3::double_3(const float_3& _Other) __GPU + { + float_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + double_3::double_3(const unorm_3& _Other) __GPU + { + unorm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + double_3::double_3(const norm_3& _Other) __GPU + { + norm_3 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + } + + + double_4::double_4(const uint_4& _Other) __GPU + { + uint_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + double_4::double_4(const int_4& _Other) __GPU + { + int_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + double_4::double_4(const float_4& _Other) __GPU + { + float_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + double_4::double_4(const unorm_4& _Other) __GPU + { + unorm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + double_4::double_4(const norm_4& _Other) __GPU + { + norm_4 _Value = _Other; + _M_x = static_cast<value_type>(_Value.x); + _M_y = static_cast<value_type>(_Value.y); + _M_z = static_cast<value_type>(_Value.z); + _M_w = static_cast<value_type>(_Value.w); + } + + + + inline uint_2 operator+(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x + _Value2.x, _Value1.y + _Value2.y); + } + + inline uint_2 operator-(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x - _Value2.x, _Value1.y - _Value2.y); + } + + inline uint_2 operator*(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x * _Value2.x, _Value1.y * _Value2.y); + } + + inline uint_2 operator/(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x / _Value2.x, _Value1.y / _Value2.y); + } + + inline bool operator==(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y; + } + + inline bool operator!=(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y; + } + + inline uint_2 operator%(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x % _Value2.x, _Value1.y % _Value2.y); + } + + inline uint_2 operator^(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x ^ _Value2.x, _Value1.y ^ _Value2.y); + } + + inline uint_2 operator|(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x | _Value2.x, _Value1.y | _Value2.y); + } + + inline uint_2 operator&(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x & _Value2.x, _Value1.y & _Value2.y); + } + + inline uint_2 operator<<(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x << _Value2.x, _Value1.y << _Value2.y); + } + + inline uint_2 operator>>(const uint_2& _Lhs, const uint_2& _Rhs) __GPU + { + uint_2 _Value1 = _Lhs; + uint_2 _Value2 = _Rhs; + return uint_2(_Value1.x >> _Value2.x, _Value1.y >> _Value2.y); + } + + inline uint_3 operator+(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z); + } + + inline uint_3 operator-(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z); + } + + inline uint_3 operator*(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z); + } + + inline uint_3 operator/(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z); + } + + inline bool operator==(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z; + } + + inline bool operator!=(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z; + } + + inline uint_3 operator%(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x % _Value2.x, _Value1.y % _Value2.y, _Value1.z % _Value2.z); + } + + inline uint_3 operator^(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x ^ _Value2.x, _Value1.y ^ _Value2.y, _Value1.z ^ _Value2.z); + } + + inline uint_3 operator|(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x | _Value2.x, _Value1.y | _Value2.y, _Value1.z | _Value2.z); + } + + inline uint_3 operator&(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x & _Value2.x, _Value1.y & _Value2.y, _Value1.z & _Value2.z); + } + + inline uint_3 operator<<(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x << _Value2.x, _Value1.y << _Value2.y, _Value1.z << _Value2.z); + } + + inline uint_3 operator>>(const uint_3& _Lhs, const uint_3& _Rhs) __GPU + { + uint_3 _Value1 = _Lhs; + uint_3 _Value2 = _Rhs; + return uint_3(_Value1.x >> _Value2.x, _Value1.y >> _Value2.y, _Value1.z >> _Value2.z); + } + + inline uint_4 operator+(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z, _Value1.w + _Value2.w); + } + + inline uint_4 operator-(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z, _Value1.w - _Value2.w); + } + + inline uint_4 operator*(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z, _Value1.w * _Value2.w); + } + + inline uint_4 operator/(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z, _Value1.w / _Value2.w); + } + + inline bool operator==(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z && _Value1.w == _Value2.w; + } + + inline bool operator!=(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z || _Value1.w != _Value2.w; + } + + inline uint_4 operator%(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x % _Value2.x, _Value1.y % _Value2.y, _Value1.z % _Value2.z, _Value1.w % _Value2.w); + } + + inline uint_4 operator^(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x ^ _Value2.x, _Value1.y ^ _Value2.y, _Value1.z ^ _Value2.z, _Value1.w ^ _Value2.w); + } + + inline uint_4 operator|(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x | _Value2.x, _Value1.y | _Value2.y, _Value1.z | _Value2.z, _Value1.w | _Value2.w); + } + + inline uint_4 operator&(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x & _Value2.x, _Value1.y & _Value2.y, _Value1.z & _Value2.z, _Value1.w & _Value2.w); + } + + inline uint_4 operator<<(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x << _Value2.x, _Value1.y << _Value2.y, _Value1.z << _Value2.z, _Value1.w << _Value2.w); + } + + inline uint_4 operator>>(const uint_4& _Lhs, const uint_4& _Rhs) __GPU + { + uint_4 _Value1 = _Lhs; + uint_4 _Value2 = _Rhs; + return uint_4(_Value1.x >> _Value2.x, _Value1.y >> _Value2.y, _Value1.z >> _Value2.z, _Value1.w >> _Value2.w); + } + + inline int_2 operator+(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x + _Value2.x, _Value1.y + _Value2.y); + } + + inline int_2 operator-(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x - _Value2.x, _Value1.y - _Value2.y); + } + + inline int_2 operator*(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x * _Value2.x, _Value1.y * _Value2.y); + } + + inline int_2 operator/(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x / _Value2.x, _Value1.y / _Value2.y); + } + + inline bool operator==(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y; + } + + inline bool operator!=(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y; + } + + inline int_2 operator%(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x % _Value2.x, _Value1.y % _Value2.y); + } + + inline int_2 operator^(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x ^ _Value2.x, _Value1.y ^ _Value2.y); + } + + inline int_2 operator|(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x | _Value2.x, _Value1.y | _Value2.y); + } + + inline int_2 operator&(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x & _Value2.x, _Value1.y & _Value2.y); + } + + inline int_2 operator<<(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x << _Value2.x, _Value1.y << _Value2.y); + } + + inline int_2 operator>>(const int_2& _Lhs, const int_2& _Rhs) __GPU + { + int_2 _Value1 = _Lhs; + int_2 _Value2 = _Rhs; + return int_2(_Value1.x >> _Value2.x, _Value1.y >> _Value2.y); + } + + inline int_3 operator+(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z); + } + + inline int_3 operator-(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z); + } + + inline int_3 operator*(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z); + } + + inline int_3 operator/(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z); + } + + inline bool operator==(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z; + } + + inline bool operator!=(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z; + } + + inline int_3 operator%(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x % _Value2.x, _Value1.y % _Value2.y, _Value1.z % _Value2.z); + } + + inline int_3 operator^(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x ^ _Value2.x, _Value1.y ^ _Value2.y, _Value1.z ^ _Value2.z); + } + + inline int_3 operator|(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x | _Value2.x, _Value1.y | _Value2.y, _Value1.z | _Value2.z); + } + + inline int_3 operator&(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x & _Value2.x, _Value1.y & _Value2.y, _Value1.z & _Value2.z); + } + + inline int_3 operator<<(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x << _Value2.x, _Value1.y << _Value2.y, _Value1.z << _Value2.z); + } + + inline int_3 operator>>(const int_3& _Lhs, const int_3& _Rhs) __GPU + { + int_3 _Value1 = _Lhs; + int_3 _Value2 = _Rhs; + return int_3(_Value1.x >> _Value2.x, _Value1.y >> _Value2.y, _Value1.z >> _Value2.z); + } + + inline int_4 operator+(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z, _Value1.w + _Value2.w); + } + + inline int_4 operator-(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z, _Value1.w - _Value2.w); + } + + inline int_4 operator*(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z, _Value1.w * _Value2.w); + } + + inline int_4 operator/(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z, _Value1.w / _Value2.w); + } + + inline bool operator==(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z && _Value1.w == _Value2.w; + } + + inline bool operator!=(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z || _Value1.w != _Value2.w; + } + + inline int_4 operator%(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x % _Value2.x, _Value1.y % _Value2.y, _Value1.z % _Value2.z, _Value1.w % _Value2.w); + } + + inline int_4 operator^(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x ^ _Value2.x, _Value1.y ^ _Value2.y, _Value1.z ^ _Value2.z, _Value1.w ^ _Value2.w); + } + + inline int_4 operator|(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x | _Value2.x, _Value1.y | _Value2.y, _Value1.z | _Value2.z, _Value1.w | _Value2.w); + } + + inline int_4 operator&(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x & _Value2.x, _Value1.y & _Value2.y, _Value1.z & _Value2.z, _Value1.w & _Value2.w); + } + + inline int_4 operator<<(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x << _Value2.x, _Value1.y << _Value2.y, _Value1.z << _Value2.z, _Value1.w << _Value2.w); + } + + inline int_4 operator>>(const int_4& _Lhs, const int_4& _Rhs) __GPU + { + int_4 _Value1 = _Lhs; + int_4 _Value2 = _Rhs; + return int_4(_Value1.x >> _Value2.x, _Value1.y >> _Value2.y, _Value1.z >> _Value2.z, _Value1.w >> _Value2.w); + } + + inline float_2 operator+(const float_2& _Lhs, const float_2& _Rhs) __GPU + { + float_2 _Value1 = _Lhs; + float_2 _Value2 = _Rhs; + return float_2(_Value1.x + _Value2.x, _Value1.y + _Value2.y); + } + + inline float_2 operator-(const float_2& _Lhs, const float_2& _Rhs) __GPU + { + float_2 _Value1 = _Lhs; + float_2 _Value2 = _Rhs; + return float_2(_Value1.x - _Value2.x, _Value1.y - _Value2.y); + } + + inline float_2 operator*(const float_2& _Lhs, const float_2& _Rhs) __GPU + { + float_2 _Value1 = _Lhs; + float_2 _Value2 = _Rhs; + return float_2(_Value1.x * _Value2.x, _Value1.y * _Value2.y); + } + + inline float_2 operator/(const float_2& _Lhs, const float_2& _Rhs) __GPU + { + float_2 _Value1 = _Lhs; + float_2 _Value2 = _Rhs; + return float_2(_Value1.x / _Value2.x, _Value1.y / _Value2.y); + } + + inline bool operator==(const float_2& _Lhs, const float_2& _Rhs) __GPU + { + float_2 _Value1 = _Lhs; + float_2 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y; + } + + inline bool operator!=(const float_2& _Lhs, const float_2& _Rhs) __GPU + { + float_2 _Value1 = _Lhs; + float_2 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y; + } + + inline float_3 operator+(const float_3& _Lhs, const float_3& _Rhs) __GPU + { + float_3 _Value1 = _Lhs; + float_3 _Value2 = _Rhs; + return float_3(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z); + } + + inline float_3 operator-(const float_3& _Lhs, const float_3& _Rhs) __GPU + { + float_3 _Value1 = _Lhs; + float_3 _Value2 = _Rhs; + return float_3(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z); + } + + inline float_3 operator*(const float_3& _Lhs, const float_3& _Rhs) __GPU + { + float_3 _Value1 = _Lhs; + float_3 _Value2 = _Rhs; + return float_3(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z); + } + + inline float_3 operator/(const float_3& _Lhs, const float_3& _Rhs) __GPU + { + float_3 _Value1 = _Lhs; + float_3 _Value2 = _Rhs; + return float_3(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z); + } + + inline bool operator==(const float_3& _Lhs, const float_3& _Rhs) __GPU + { + float_3 _Value1 = _Lhs; + float_3 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z; + } + + inline bool operator!=(const float_3& _Lhs, const float_3& _Rhs) __GPU + { + float_3 _Value1 = _Lhs; + float_3 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z; + } + + inline float_4 operator+(const float_4& _Lhs, const float_4& _Rhs) __GPU + { + float_4 _Value1 = _Lhs; + float_4 _Value2 = _Rhs; + return float_4(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z, _Value1.w + _Value2.w); + } + + inline float_4 operator-(const float_4& _Lhs, const float_4& _Rhs) __GPU + { + float_4 _Value1 = _Lhs; + float_4 _Value2 = _Rhs; + return float_4(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z, _Value1.w - _Value2.w); + } + + inline float_4 operator*(const float_4& _Lhs, const float_4& _Rhs) __GPU + { + float_4 _Value1 = _Lhs; + float_4 _Value2 = _Rhs; + return float_4(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z, _Value1.w * _Value2.w); + } + + inline float_4 operator/(const float_4& _Lhs, const float_4& _Rhs) __GPU + { + float_4 _Value1 = _Lhs; + float_4 _Value2 = _Rhs; + return float_4(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z, _Value1.w / _Value2.w); + } + + inline bool operator==(const float_4& _Lhs, const float_4& _Rhs) __GPU + { + float_4 _Value1 = _Lhs; + float_4 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z && _Value1.w == _Value2.w; + } + + inline bool operator!=(const float_4& _Lhs, const float_4& _Rhs) __GPU + { + float_4 _Value1 = _Lhs; + float_4 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z || _Value1.w != _Value2.w; + } + + inline unorm_2 operator+(const unorm_2& _Lhs, const unorm_2& _Rhs) __GPU + { + unorm_2 _Value1 = _Lhs; + unorm_2 _Value2 = _Rhs; + return unorm_2(_Value1.x + _Value2.x, _Value1.y + _Value2.y); + } + + inline unorm_2 operator-(const unorm_2& _Lhs, const unorm_2& _Rhs) __GPU + { + unorm_2 _Value1 = _Lhs; + unorm_2 _Value2 = _Rhs; + return unorm_2(_Value1.x - _Value2.x, _Value1.y - _Value2.y); + } + + inline unorm_2 operator*(const unorm_2& _Lhs, const unorm_2& _Rhs) __GPU + { + unorm_2 _Value1 = _Lhs; + unorm_2 _Value2 = _Rhs; + return unorm_2(_Value1.x * _Value2.x, _Value1.y * _Value2.y); + } + + inline unorm_2 operator/(const unorm_2& _Lhs, const unorm_2& _Rhs) __GPU + { + unorm_2 _Value1 = _Lhs; + unorm_2 _Value2 = _Rhs; + return unorm_2(_Value1.x / _Value2.x, _Value1.y / _Value2.y); + } + + inline bool operator==(const unorm_2& _Lhs, const unorm_2& _Rhs) __GPU + { + unorm_2 _Value1 = _Lhs; + unorm_2 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y; + } + + inline bool operator!=(const unorm_2& _Lhs, const unorm_2& _Rhs) __GPU + { + unorm_2 _Value1 = _Lhs; + unorm_2 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y; + } + + inline unorm_3 operator+(const unorm_3& _Lhs, const unorm_3& _Rhs) __GPU + { + unorm_3 _Value1 = _Lhs; + unorm_3 _Value2 = _Rhs; + return unorm_3(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z); + } + + inline unorm_3 operator-(const unorm_3& _Lhs, const unorm_3& _Rhs) __GPU + { + unorm_3 _Value1 = _Lhs; + unorm_3 _Value2 = _Rhs; + return unorm_3(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z); + } + + inline unorm_3 operator*(const unorm_3& _Lhs, const unorm_3& _Rhs) __GPU + { + unorm_3 _Value1 = _Lhs; + unorm_3 _Value2 = _Rhs; + return unorm_3(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z); + } + + inline unorm_3 operator/(const unorm_3& _Lhs, const unorm_3& _Rhs) __GPU + { + unorm_3 _Value1 = _Lhs; + unorm_3 _Value2 = _Rhs; + return unorm_3(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z); + } + + inline bool operator==(const unorm_3& _Lhs, const unorm_3& _Rhs) __GPU + { + unorm_3 _Value1 = _Lhs; + unorm_3 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z; + } + + inline bool operator!=(const unorm_3& _Lhs, const unorm_3& _Rhs) __GPU + { + unorm_3 _Value1 = _Lhs; + unorm_3 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z; + } + + inline unorm_4 operator+(const unorm_4& _Lhs, const unorm_4& _Rhs) __GPU + { + unorm_4 _Value1 = _Lhs; + unorm_4 _Value2 = _Rhs; + return unorm_4(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z, _Value1.w + _Value2.w); + } + + inline unorm_4 operator-(const unorm_4& _Lhs, const unorm_4& _Rhs) __GPU + { + unorm_4 _Value1 = _Lhs; + unorm_4 _Value2 = _Rhs; + return unorm_4(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z, _Value1.w - _Value2.w); + } + + inline unorm_4 operator*(const unorm_4& _Lhs, const unorm_4& _Rhs) __GPU + { + unorm_4 _Value1 = _Lhs; + unorm_4 _Value2 = _Rhs; + return unorm_4(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z, _Value1.w * _Value2.w); + } + + inline unorm_4 operator/(const unorm_4& _Lhs, const unorm_4& _Rhs) __GPU + { + unorm_4 _Value1 = _Lhs; + unorm_4 _Value2 = _Rhs; + return unorm_4(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z, _Value1.w / _Value2.w); + } + + inline bool operator==(const unorm_4& _Lhs, const unorm_4& _Rhs) __GPU + { + unorm_4 _Value1 = _Lhs; + unorm_4 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z && _Value1.w == _Value2.w; + } + + inline bool operator!=(const unorm_4& _Lhs, const unorm_4& _Rhs) __GPU + { + unorm_4 _Value1 = _Lhs; + unorm_4 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z || _Value1.w != _Value2.w; + } + + inline norm_2 operator+(const norm_2& _Lhs, const norm_2& _Rhs) __GPU + { + norm_2 _Value1 = _Lhs; + norm_2 _Value2 = _Rhs; + return norm_2(_Value1.x + _Value2.x, _Value1.y + _Value2.y); + } + + inline norm_2 operator-(const norm_2& _Lhs, const norm_2& _Rhs) __GPU + { + norm_2 _Value1 = _Lhs; + norm_2 _Value2 = _Rhs; + return norm_2(_Value1.x - _Value2.x, _Value1.y - _Value2.y); + } + + inline norm_2 operator*(const norm_2& _Lhs, const norm_2& _Rhs) __GPU + { + norm_2 _Value1 = _Lhs; + norm_2 _Value2 = _Rhs; + return norm_2(_Value1.x * _Value2.x, _Value1.y * _Value2.y); + } + + inline norm_2 operator/(const norm_2& _Lhs, const norm_2& _Rhs) __GPU + { + norm_2 _Value1 = _Lhs; + norm_2 _Value2 = _Rhs; + return norm_2(_Value1.x / _Value2.x, _Value1.y / _Value2.y); + } + + inline bool operator==(const norm_2& _Lhs, const norm_2& _Rhs) __GPU + { + norm_2 _Value1 = _Lhs; + norm_2 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y; + } + + inline bool operator!=(const norm_2& _Lhs, const norm_2& _Rhs) __GPU + { + norm_2 _Value1 = _Lhs; + norm_2 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y; + } + + inline norm_3 operator+(const norm_3& _Lhs, const norm_3& _Rhs) __GPU + { + norm_3 _Value1 = _Lhs; + norm_3 _Value2 = _Rhs; + return norm_3(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z); + } + + inline norm_3 operator-(const norm_3& _Lhs, const norm_3& _Rhs) __GPU + { + norm_3 _Value1 = _Lhs; + norm_3 _Value2 = _Rhs; + return norm_3(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z); + } + + inline norm_3 operator*(const norm_3& _Lhs, const norm_3& _Rhs) __GPU + { + norm_3 _Value1 = _Lhs; + norm_3 _Value2 = _Rhs; + return norm_3(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z); + } + + inline norm_3 operator/(const norm_3& _Lhs, const norm_3& _Rhs) __GPU + { + norm_3 _Value1 = _Lhs; + norm_3 _Value2 = _Rhs; + return norm_3(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z); + } + + inline bool operator==(const norm_3& _Lhs, const norm_3& _Rhs) __GPU + { + norm_3 _Value1 = _Lhs; + norm_3 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z; + } + + inline bool operator!=(const norm_3& _Lhs, const norm_3& _Rhs) __GPU + { + norm_3 _Value1 = _Lhs; + norm_3 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z; + } + + inline norm_4 operator+(const norm_4& _Lhs, const norm_4& _Rhs) __GPU + { + norm_4 _Value1 = _Lhs; + norm_4 _Value2 = _Rhs; + return norm_4(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z, _Value1.w + _Value2.w); + } + + inline norm_4 operator-(const norm_4& _Lhs, const norm_4& _Rhs) __GPU + { + norm_4 _Value1 = _Lhs; + norm_4 _Value2 = _Rhs; + return norm_4(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z, _Value1.w - _Value2.w); + } + + inline norm_4 operator*(const norm_4& _Lhs, const norm_4& _Rhs) __GPU + { + norm_4 _Value1 = _Lhs; + norm_4 _Value2 = _Rhs; + return norm_4(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z, _Value1.w * _Value2.w); + } + + inline norm_4 operator/(const norm_4& _Lhs, const norm_4& _Rhs) __GPU + { + norm_4 _Value1 = _Lhs; + norm_4 _Value2 = _Rhs; + return norm_4(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z, _Value1.w / _Value2.w); + } + + inline bool operator==(const norm_4& _Lhs, const norm_4& _Rhs) __GPU + { + norm_4 _Value1 = _Lhs; + norm_4 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z && _Value1.w == _Value2.w; + } + + inline bool operator!=(const norm_4& _Lhs, const norm_4& _Rhs) __GPU + { + norm_4 _Value1 = _Lhs; + norm_4 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z || _Value1.w != _Value2.w; + } + + inline double_2 operator+(const double_2& _Lhs, const double_2& _Rhs) __GPU + { + double_2 _Value1 = _Lhs; + double_2 _Value2 = _Rhs; + return double_2(_Value1.x + _Value2.x, _Value1.y + _Value2.y); + } + + inline double_2 operator-(const double_2& _Lhs, const double_2& _Rhs) __GPU + { + double_2 _Value1 = _Lhs; + double_2 _Value2 = _Rhs; + return double_2(_Value1.x - _Value2.x, _Value1.y - _Value2.y); + } + + inline double_2 operator*(const double_2& _Lhs, const double_2& _Rhs) __GPU + { + double_2 _Value1 = _Lhs; + double_2 _Value2 = _Rhs; + return double_2(_Value1.x * _Value2.x, _Value1.y * _Value2.y); + } + + inline double_2 operator/(const double_2& _Lhs, const double_2& _Rhs) __GPU + { + double_2 _Value1 = _Lhs; + double_2 _Value2 = _Rhs; + return double_2(_Value1.x / _Value2.x, _Value1.y / _Value2.y); + } + + inline bool operator==(const double_2& _Lhs, const double_2& _Rhs) __GPU + { + double_2 _Value1 = _Lhs; + double_2 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y; + } + + inline bool operator!=(const double_2& _Lhs, const double_2& _Rhs) __GPU + { + double_2 _Value1 = _Lhs; + double_2 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y; + } + + inline double_3 operator+(const double_3& _Lhs, const double_3& _Rhs) __GPU + { + double_3 _Value1 = _Lhs; + double_3 _Value2 = _Rhs; + return double_3(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z); + } + + inline double_3 operator-(const double_3& _Lhs, const double_3& _Rhs) __GPU + { + double_3 _Value1 = _Lhs; + double_3 _Value2 = _Rhs; + return double_3(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z); + } + + inline double_3 operator*(const double_3& _Lhs, const double_3& _Rhs) __GPU + { + double_3 _Value1 = _Lhs; + double_3 _Value2 = _Rhs; + return double_3(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z); + } + + inline double_3 operator/(const double_3& _Lhs, const double_3& _Rhs) __GPU + { + double_3 _Value1 = _Lhs; + double_3 _Value2 = _Rhs; + return double_3(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z); + } + + inline bool operator==(const double_3& _Lhs, const double_3& _Rhs) __GPU + { + double_3 _Value1 = _Lhs; + double_3 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z; + } + + inline bool operator!=(const double_3& _Lhs, const double_3& _Rhs) __GPU + { + double_3 _Value1 = _Lhs; + double_3 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z; + } + + inline double_4 operator+(const double_4& _Lhs, const double_4& _Rhs) __GPU + { + double_4 _Value1 = _Lhs; + double_4 _Value2 = _Rhs; + return double_4(_Value1.x + _Value2.x, _Value1.y + _Value2.y, _Value1.z + _Value2.z, _Value1.w + _Value2.w); + } + + inline double_4 operator-(const double_4& _Lhs, const double_4& _Rhs) __GPU + { + double_4 _Value1 = _Lhs; + double_4 _Value2 = _Rhs; + return double_4(_Value1.x - _Value2.x, _Value1.y - _Value2.y, _Value1.z - _Value2.z, _Value1.w - _Value2.w); + } + + inline double_4 operator*(const double_4& _Lhs, const double_4& _Rhs) __GPU + { + double_4 _Value1 = _Lhs; + double_4 _Value2 = _Rhs; + return double_4(_Value1.x * _Value2.x, _Value1.y * _Value2.y, _Value1.z * _Value2.z, _Value1.w * _Value2.w); + } + + inline double_4 operator/(const double_4& _Lhs, const double_4& _Rhs) __GPU + { + double_4 _Value1 = _Lhs; + double_4 _Value2 = _Rhs; + return double_4(_Value1.x / _Value2.x, _Value1.y / _Value2.y, _Value1.z / _Value2.z, _Value1.w / _Value2.w); + } + + inline bool operator==(const double_4& _Lhs, const double_4& _Rhs) __GPU + { + double_4 _Value1 = _Lhs; + double_4 _Value2 = _Rhs; + return _Value1.x == _Value2.x && _Value1.y == _Value2.y && _Value1.z == _Value2.z && _Value1.w == _Value2.w; + } + + inline bool operator!=(const double_4& _Lhs, const double_4& _Rhs) __GPU + { + double_4 _Value1 = _Lhs; + double_4 _Value2 = _Rhs; + return _Value1.x != _Value2.x || _Value1.y != _Value2.y || _Value1.z != _Value2.z || _Value1.w != _Value2.w; + } + + namespace direct3d + { + + // typedefs + typedef graphics::uint_2 uint2; + typedef graphics::uint_3 uint3; + typedef graphics::uint_4 uint4; + typedef graphics::int_2 int2; + typedef graphics::int_3 int3; + typedef graphics::int_4 int4; + typedef graphics::float_2 float2; + typedef graphics::float_3 float3; + typedef graphics::float_4 float4; + typedef graphics::unorm_2 unorm2; + typedef graphics::unorm_3 unorm3; + typedef graphics::unorm_4 unorm4; + typedef graphics::norm_2 norm2; + typedef graphics::norm_3 norm3; + typedef graphics::norm_4 norm4; + typedef graphics::double_2 double2; + typedef graphics::double_3 double3; + typedef graphics::double_4 double4; + + + } // namespace direct3d + + /// <summary> + /// short_vector provides metaprogramming definitions which are useful for programming short vectors generically. + /// </summary> + /// <param name="_Scalar_type"> + /// The scalar type. + /// </param> + /// <param name="_Size"> + /// The size of the short vector. + /// </param> + template<typename _Scalar_type, int _Size> struct short_vector + { + short_vector() + { + static_assert(false, "short_vector is not supported for this scalar type (_T) and length (_N)"); + } + }; + + template<> + struct short_vector<unsigned int, 1> + { + typedef unsigned int type; + }; + + template<> + struct short_vector<unsigned int, 2> + { + typedef uint_2 type; + }; + + template<> + struct short_vector<unsigned int, 3> + { + typedef uint_3 type; + }; + + template<> + struct short_vector<unsigned int, 4> + { + typedef uint_4 type; + }; + + template<> + struct short_vector<int, 1> + { + typedef int type; + }; + + template<> + struct short_vector<int, 2> + { + typedef int_2 type; + }; + + template<> + struct short_vector<int, 3> + { + typedef int_3 type; + }; + + template<> + struct short_vector<int, 4> + { + typedef int_4 type; + }; + + template<> + struct short_vector<float, 1> + { + typedef float type; + }; + + template<> + struct short_vector<float, 2> + { + typedef float_2 type; + }; + + template<> + struct short_vector<float, 3> + { + typedef float_3 type; + }; + + template<> + struct short_vector<float, 4> + { + typedef float_4 type; + }; + + template<> + struct short_vector<unorm, 1> + { + typedef unorm type; + }; + + template<> + struct short_vector<unorm, 2> + { + typedef unorm_2 type; + }; + + template<> + struct short_vector<unorm, 3> + { + typedef unorm_3 type; + }; + + template<> + struct short_vector<unorm, 4> + { + typedef unorm_4 type; + }; + + template<> + struct short_vector<norm, 1> + { + typedef norm type; + }; + + template<> + struct short_vector<norm, 2> + { + typedef norm_2 type; + }; + + template<> + struct short_vector<norm, 3> + { + typedef norm_3 type; + }; + + template<> + struct short_vector<norm, 4> + { + typedef norm_4 type; + }; + + template<> + struct short_vector<double, 1> + { + typedef double type; + }; + + template<> + struct short_vector<double, 2> + { + typedef double_2 type; + }; + + template<> + struct short_vector<double, 3> + { + typedef double_3 type; + }; + + template<> + struct short_vector<double, 4> + { + typedef double_4 type; + }; + + /// <summary> + /// short_vector_traits allows retrival of the underlying vector length and scalar type of a short vector type or a scalar type + /// </summary> + /// <param name="_Type"> + /// The short vector type or a scalar type. + /// </param> + template<typename _Type> struct short_vector_traits + { + short_vector_traits() + { + static_assert(false, "short_vector_traits is not supported for this type (_Type)"); + } + }; + + template<> + struct short_vector_traits<unsigned int> + { + typedef unsigned int value_type; + static int const size = 1; + }; + + template<> + struct short_vector_traits<uint_2> + { + typedef unsigned int value_type; + static int const size = 2; + }; + + template<> + struct short_vector_traits<uint_3> + { + typedef unsigned int value_type; + static int const size = 3; + }; + + template<> + struct short_vector_traits<uint_4> + { + typedef unsigned int value_type; + static int const size = 4; + }; + + template<> + struct short_vector_traits<int> + { + typedef int value_type; + static int const size = 1; + }; + + template<> + struct short_vector_traits<int_2> + { + typedef int value_type; + static int const size = 2; + }; + + template<> + struct short_vector_traits<int_3> + { + typedef int value_type; + static int const size = 3; + }; + + template<> + struct short_vector_traits<int_4> + { + typedef int value_type; + static int const size = 4; + }; + + template<> + struct short_vector_traits<float> + { + typedef float value_type; + static int const size = 1; + }; + + template<> + struct short_vector_traits<float_2> + { + typedef float value_type; + static int const size = 2; + }; + + template<> + struct short_vector_traits<float_3> + { + typedef float value_type; + static int const size = 3; + }; + + template<> + struct short_vector_traits<float_4> + { + typedef float value_type; + static int const size = 4; + }; + + template<> + struct short_vector_traits<unorm> + { + typedef unorm value_type; + static int const size = 1; + }; + + template<> + struct short_vector_traits<unorm_2> + { + typedef unorm value_type; + static int const size = 2; + }; + + template<> + struct short_vector_traits<unorm_3> + { + typedef unorm value_type; + static int const size = 3; + }; + + template<> + struct short_vector_traits<unorm_4> + { + typedef unorm value_type; + static int const size = 4; + }; + + template<> + struct short_vector_traits<norm> + { + typedef norm value_type; + static int const size = 1; + }; + + template<> + struct short_vector_traits<norm_2> + { + typedef norm value_type; + static int const size = 2; + }; + + template<> + struct short_vector_traits<norm_3> + { + typedef norm value_type; + static int const size = 3; + }; + + template<> + struct short_vector_traits<norm_4> + { + typedef norm value_type; + static int const size = 4; + }; + + template<> + struct short_vector_traits<double> + { + typedef double value_type; + static int const size = 1; + }; + + template<> + struct short_vector_traits<double_2> + { + typedef double value_type; + static int const size = 2; + }; + + template<> + struct short_vector_traits<double_3> + { + typedef double value_type; + static int const size = 3; + }; + + template<> + struct short_vector_traits<double_4> + { + typedef double value_type; + static int const size = 4; + }; + + + } // namespace graphics + +} // namespace Concurrency +#pragma warning(pop) +// End of generated file diff --git a/test_data/lots_of_files/amprt.h b/test_data/lots_of_files/amprt.h new file mode 100644 index 0000000..6bdaed1 --- /dev/null +++ b/test_data/lots_of_files/amprt.h @@ -0,0 +1,4079 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* amprt.h +* +* Define the C++ interfaces exported by the C++ AMP runtime +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM) || defined (_M_ARM64) ) + #error ERROR: C++ AMP runtime is supported only on X64, X86, ARM, and ARM64 architectures. +#endif + +#if defined (_M_CEE) + #error ERROR: C++ AMP runtime is not supported when compiling /clr. +#endif + +#ifndef __cplusplus + #error ERROR: C++ AMP runtime is supported only for C++. +#endif + +#if !defined(_CXXAMP) + +#if defined(_DEBUG) + #pragma comment(lib, "vcampd") +#else // _DEBUG + #pragma comment(lib, "vcamp") +#endif // _DEBUG + +#endif // _CXXAMP + +#if !defined(_CXXAMP) + +#define __GPU restrict(amp,cpu) +#define __GPU_ONLY restrict(amp) +#define __CPU_ONLY + +#else + +#define __GPU +#define __GPU_ONLY +#define __CPU_ONLY + +#endif // _CXXAMP + +#include <exception> +#include <unknwn.h> +#include <crtdbg.h> +#include <string> +#include <vector> + +#if defined(_CXXAMP) +#include <strsafe.h> +#endif // _CXXAMP + +#include <future> +#include <functional> +#include <map> +#include <unordered_map> +#include <set> +#include <unordered_set> +#include <concrt.h> +#include <type_traits> + +#if !defined(_AMPIMP) +#define _AMPIMP __declspec(dllimport) +#endif + +#pragma pack(push,8) + +// Part of runtime-compiler interface +extern "C" +{ + // Access mode of fields + enum _Access_mode + { + _No_access = 0, + _Read_access = (1 << 0), + _Write_access = (1 << 1), + _Is_array_mode = (1 << 30), + _Read_write_access = _Read_access | _Write_access, + }; +} + +namespace Concurrency +{ + /// <summary> + /// Enumeration type used to denote the various types of access to data. + /// </summary> + enum access_type + { + access_type_none = 0, + access_type_read = (1 << 0), + access_type_write = (1 << 1), + access_type_read_write = access_type_read | access_type_write, + access_type_auto = (1 << 31), + }; + +// Forward declarations +class accelerator_view; +class accelerator; + +namespace details +{ + const size_t ERROR_MSG_BUFFER_SIZE = 1024; + + // A reference counter to be used as the base class for all reference counted types. + class _Reference_counter + { + public: + + // Constructor. + _Reference_counter() : _M_rc(0) {} + + // Destructor. + virtual ~_Reference_counter() {} + + // Add a reference. + // Thread-safe. + size_t _Add_reference() + { + return InterlockedIncrement(reinterpret_cast<LONG volatile*>(&_M_rc)); + } + + // Remove a reference. + // Thread-safe. + size_t _Remove_reference() + { + _ASSERTE(_M_rc > 0); + + size_t refCount = InterlockedDecrement(reinterpret_cast<LONG volatile*>(&_M_rc)); + + if (refCount == 0) + this->_Release(); + + return refCount; + } + + // Release the counter + _AMPIMP void _Release(); + + // Return the reference count value + size_t _Get_reference_count() + { + return _M_rc; + } + + private: + size_t _M_rc; + }; + + // A smart pointer to a reference counted object + // T must be a type derived from _Reference_counter + template <class T> + class _Reference_counted_obj_ptr + { + public: + + // Constructor + _Reference_counted_obj_ptr(T* _Ptr = NULL) : _M_obj_ptr(_Ptr) + { + _Init(); + } + + // Copy constructor + _Reference_counted_obj_ptr(const _Reference_counted_obj_ptr &_Other) : _M_obj_ptr(_Other._M_obj_ptr) + { + _Init(); + } + + // Move constructor + _Reference_counted_obj_ptr(_Reference_counted_obj_ptr &&_Other) : _M_obj_ptr(_Other._M_obj_ptr) + { + _Other._M_obj_ptr = nullptr; + // No change to ref-count + } + + // Destructor + ~_Reference_counted_obj_ptr() + { + if (_M_obj_ptr != NULL) { + _UnInitialize(_M_obj_ptr); + } + } + + // Assignment operator + _Reference_counted_obj_ptr& operator=(const _Reference_counted_obj_ptr &_Other) + { + if (_M_obj_ptr != _Other._M_obj_ptr) + { + T *oldPtr = _M_obj_ptr; + _M_obj_ptr = _Other._M_obj_ptr; + _Init(); + + if (oldPtr != NULL) { + _UnInitialize(oldPtr); + } + } + return *this; + } + + // Move-assignment operator + _Reference_counted_obj_ptr& operator=(_Reference_counted_obj_ptr &&_Other) + { + if (_M_obj_ptr != _Other._M_obj_ptr) + { + T *oldPtr = _M_obj_ptr; + _M_obj_ptr = _Other._M_obj_ptr; + _Other._M_obj_ptr = nullptr; + // No change to ref-count of the adopted pointer. + + if (oldPtr != nullptr) + { + _UnInitialize(oldPtr); + } + } + return *this; + } + + _Ret_ T* operator->() const + { + return _M_obj_ptr; + } + + T& operator*() const + { + return *_M_obj_ptr; + } + + operator T*() const + { + return _M_obj_ptr; + } + + _Ret_ T* _Get_ptr() const + { + return _M_obj_ptr; + } + + private: + T *_M_obj_ptr; + + void _Init() + { + if (_M_obj_ptr == NULL) + return; + + reinterpret_cast<_Reference_counter*>(_M_obj_ptr)->_Add_reference(); + } + + static void _UnInitialize(_In_ T *_Obj_ptr) + { + reinterpret_cast<_Reference_counter*>(_Obj_ptr)->_Remove_reference(); + } + }; + + // Forward declarations + class _Trace; + class _Amp_runtime_trace; + class _Buffer; + class _Texture; + class _Sampler; + class _Ubiquitous_buffer; + class _D3D_interop; + class _Accelerator_view_impl; + class _CPU_accelerator_view_impl; + class _D3D_accelerator_view_impl; + class _Accelerator_impl; + class _Event_impl; + class _DPC_runtime_factory; + class _View_shape; + struct _Buffer_descriptor; + class _Accelerator_view_hasher; + struct _DPC_shader_blob; + struct _View_info; + + // The enum specifies the base type for short vector type. + enum _Short_vector_base_type_id : unsigned int + { + _Uint_type = 0, + _Int_type = 1, + _Float_type = 2, + _Unorm_type = 3, + _Norm_type = 4, + _Double_type = 5, + _Invalid_type = 0xFFFFFFFF + }; + + typedef enum _Short_vector_base_type_id _Texture_base_type_id; + +} // namespace Concurrency::details + +typedef details::_Reference_counted_obj_ptr<details::_Accelerator_view_impl> _Accelerator_view_impl_ptr; +typedef details::_Reference_counted_obj_ptr<details::_Accelerator_impl> _Accelerator_impl_ptr; +typedef details::_Reference_counted_obj_ptr<details::_Buffer> _Buffer_ptr; +typedef details::_Reference_counted_obj_ptr<details::_Texture> _Texture_ptr; +typedef details::_Reference_counted_obj_ptr<details::_Sampler> _Sampler_ptr; +typedef details::_Reference_counted_obj_ptr<details::_Ubiquitous_buffer> _Ubiquitous_buffer_ptr; +typedef details::_Reference_counted_obj_ptr<details::_Event_impl> _Event_impl_ptr; +typedef details::_Reference_counted_obj_ptr<details::_View_shape> _View_shape_ptr; + +namespace details +{ + // The _Event class. + class _Event + { + friend class _Buffer; + friend class _Texture; + friend class accelerator_view; + friend class _D3D_accelerator_view_impl; + + public: + /// <summary> + /// Constructor of the _Event. + /// </summary> + _AMPIMP _Event(); + + /// <summary> + /// Destructor of the _Event. + /// </summary> + _AMPIMP ~_Event(); + + /// <summary> + /// Copy constructor + /// </summary> + _AMPIMP _Event(const _Event & _Other); + + /// <summary> + /// Assignment operator + /// </summary> + _AMPIMP _Event & operator=(const _Event & _Other); + + /// <summary> + /// Poll whether the _Event has completed or not. Swallows any exceptions + /// </summary> + /// <returns> + /// true, if the _Event has completed, false otherwise + /// </returns> + _AMPIMP bool _Is_finished_nothrow(); + + /// <summary> + /// Poll whether the _Event has completed or not and throws any exceptions that occur + /// </summary> + /// <returns> + /// true, if the _Event has completed, false otherwise + /// </returns> + _AMPIMP bool _Is_finished(); + + /// <summary> + /// Wait until the _Event completes and throw any exceptions that occur. + /// </summary> + _AMPIMP void _Get(); + + /// <summary> + /// Tells if this is an empty event + /// </summary> + /// <returns> + /// true, if the _Event is empty + /// false, otherwise + /// </returns> + _AMPIMP bool _Is_empty() const; + + /// <summary> + /// Creates an event which is an ordered collection of this and _Ev + /// </summary> + /// <returns> + /// The composite event + /// </returns> + _AMPIMP _Event _Add_event(_Event _Ev); + + /// <summary> + /// Creates an event which is an ordered collection of this and a continuation task + /// </summary> + /// <returns> + /// The composite event + /// </returns> + _AMPIMP _Event _Add_continuation(const std::function<_Event __cdecl ()> &_Continuation_task); + + /// <summary> + /// Return true if the other _Event is same as this _Event; false otherwise + /// </summary> + _AMPIMP bool operator==(const _Event &_Other) const; + + /// <summary> + /// Return false if the other _Event is same as this _Event; true otherwise + /// </summary> + _AMPIMP bool operator!=(const _Event &_Other) const; + + private: + + // Private constructor + _Event(_In_ _Event_impl* _Impl); + + _Event_impl_ptr _M_ptr_event_impl; + }; + + typedef _Buffer_descriptor *_View_key; + + _Ret_ _Accelerator_view_impl* _Get_accelerator_view_impl_ptr(const accelerator_view& _Accl_view); + _Ret_ _Accelerator_impl* _Get_accelerator_impl_ptr(const accelerator& _Accl); + _Event _Get_access_async(const _View_key _Key, accelerator_view _Av, _Access_mode _Mode, _Buffer_ptr &_Buf_ptr); + unsigned int _Get_mipmap_levels(const _Texture *_Tex); + + inline bool _Is_valid_access_mode(_Access_mode _Mode) + { + if ((_Mode != _Read_access) && + (_Mode != _Write_access) && + (_Mode != _Read_write_access)) + { + return false; + } + + return true; + } + + // Caution: Do not change this structure defintion. + // This struct is special and is processed by the FE to identify the buffers + // used in a parallel_for_each and to setup the _M_data_ptr with the appropriate + // buffer ptr value in the device code. + typedef struct _Buffer_descriptor + { + friend _Event _Get_access_async(const _View_key _Key, accelerator_view _Av, _Access_mode _Mode, _Buffer_ptr &_Buf_ptr); + + // _M_data_ptr points to the raw data underlying the buffer for accessing on host + mutable void *_M_data_ptr; + + private: + // _M_buffer_ptr points to a _Ubiquitous_buffer that holds the data in an 1D array. + // This is private to ensure that all assignments to this data member + // only happen through public functions which properly manage the + // ref count of the underlying buffer + _Ubiquitous_buffer *_M_buffer_ptr; + + public: + // _M_curr_cpu_access_mode specifies the current access mode of the data on the + // cpu accelerator_view specified at the time of registration of this view + _Access_mode _M_curr_cpu_access_mode; + + // _M_type_acess_mode specifies the access mode of the overlay type + // array_views set it to the appropriate access mode and for arrays it is + // always _Is_array_mode. + _Access_mode _M_type_access_mode; + + public: + // Public functions + + // Default constructor + _Buffer_descriptor() __GPU + : _M_data_ptr(NULL), _M_buffer_ptr(NULL), + _M_curr_cpu_access_mode(_No_access), _M_type_access_mode(_Is_array_mode) + { + } + + _Buffer_descriptor(_In_ void *_Data_ptr, _In_ _Ubiquitous_buffer *_Buffer_ptr, + _Access_mode _Curr_cpu_access_mode, _Access_mode _Type_mode) __GPU + : _M_data_ptr(_Data_ptr), _M_buffer_ptr(NULL), + _M_curr_cpu_access_mode(_Curr_cpu_access_mode), _M_type_access_mode(_Type_mode) + { + _Set_buffer_ptr(_Buffer_ptr); + } + + // Destructor + ~_Buffer_descriptor() __GPU + { + _Set_buffer_ptr(NULL); + } + + // Copy constructor + _Buffer_descriptor(const _Buffer_descriptor &_Other) __GPU + : _M_data_ptr(_Other._M_data_ptr), _M_buffer_ptr(NULL), + _M_curr_cpu_access_mode(_Other._M_curr_cpu_access_mode), _M_type_access_mode(_Other._M_type_access_mode) + { + _Set_buffer_ptr(_Other._M_buffer_ptr); + } + + // Assignment operator + _Buffer_descriptor& operator=(const _Buffer_descriptor &_Other) __GPU + { + if (this != &_Other) + { + _M_data_ptr = _Other._M_data_ptr; + _M_curr_cpu_access_mode = _Other._M_curr_cpu_access_mode; + _M_type_access_mode = _Other._M_type_access_mode; + _Set_buffer_ptr(_Other._M_buffer_ptr); + } + + return *this; + } + + _Ret_ _Ubiquitous_buffer* _Get_buffer_ptr() const __CPU_ONLY + { + return _M_buffer_ptr; + } + + void _Set_buffer_ptr(_In_opt_ _Ubiquitous_buffer *_Buffer_ptr) __CPU_ONLY + { + if (_M_buffer_ptr != _Buffer_ptr) + { + if (_M_buffer_ptr != NULL) { + reinterpret_cast<_Reference_counter*>(_M_buffer_ptr)->_Remove_reference(); + } + + _M_buffer_ptr = _Buffer_ptr; + + if (_M_buffer_ptr != NULL) { + reinterpret_cast<_Reference_counter*>(_M_buffer_ptr)->_Add_reference(); + } + } + } + +#if !defined(_CXXAMP) + void _Set_buffer_ptr(_In_opt_ _Ubiquitous_buffer *_Buffer_ptr) __GPU_ONLY + { + // No need to set the buffer ptr on the GPU + UNREFERENCED_PARAMETER(_Buffer_ptr); + _M_buffer_ptr = NULL; + } +#endif // _CXXAMP + + bool _Is_array() const + { + return (_M_type_access_mode == _Is_array_mode); + } + + _Ret_ _View_key _Get_view_key() + { + return this; + } + + const _View_key _Get_view_key() const + { + return ((const _View_key)(this)); + } + + _AMPIMP void _Get_CPU_access(_Access_mode _Requested_mode) const; + + } _Buffer_descriptor; + + // Caution: Do not change this structure defintion. + // This struct is special and is processed by the FE to identify the textures + // used in a parallel_for_each and to setup the _M_data_ptr with the appropriate + // texture ptr value in the device code. + typedef struct _Texture_descriptor + { + // _M_data_ptr points to the raw data underlying the texture + mutable IUnknown *_M_data_ptr; + + private: + // _M_texture_ptr points to a _Texture that holds the data + // This is private to ensure that all assignments to this data member + // only happen through public functions which properly manage the + // ref count of the underlying texture + _Texture *_M_texture_ptr; + + // The index of the most detailed (largest in size) mipmap level for the texture (or texture view) + // This value is always zero for the texture and might be non-zero for the texture views + unsigned int _M_most_detailed_mipmap_level; + + // Number of accessible mipmap levels for the texture (or texture view), + // e.g. if the texture has 3 mipmap levels ([0, 1, 2]), + // then read-only texture view with most detailed mipmap level equal to 1, can have 1 or 2 mipmap levels ([1] or [1, 2]). + // Further texture_views created on top of the texture view defined above can only narrow down the range of accessible mipmap levels. + unsigned int _M_view_mipmap_levels; + + public: + // Public functions + + // Default constructor + _Texture_descriptor() __GPU + : _M_data_ptr(NULL), _M_texture_ptr(NULL), _M_most_detailed_mipmap_level(0), _M_view_mipmap_levels(0) + { + // Enables move constructor + } + + // Constructor for the texture + _Texture_descriptor(unsigned int _Most_detailed_mipmap_level, unsigned int _View_mipmap_levels) __GPU + : _M_data_ptr(NULL), _M_texture_ptr(NULL), _M_most_detailed_mipmap_level(_Most_detailed_mipmap_level), _M_view_mipmap_levels(_View_mipmap_levels) + { + } + + // Constructor for the interop texture + _Texture_descriptor(_In_ _Texture * _Texture_ptr) : _M_data_ptr(NULL), _M_texture_ptr(NULL), _M_most_detailed_mipmap_level(0) __CPU_ONLY + { + _Set_texture_ptr(_Texture_ptr); + + // Adopt number of mipmap levels from underlying texture object + _M_view_mipmap_levels = _Get_mipmap_levels(_M_texture_ptr); + } + + // Destructor + ~_Texture_descriptor() __GPU + { + _Set_texture_ptr(NULL); + } + + // Copy constructor + _Texture_descriptor(const _Texture_descriptor &_Other) __GPU + : _M_data_ptr(_Other._M_data_ptr), _M_texture_ptr(NULL), + _M_most_detailed_mipmap_level(_Other._M_most_detailed_mipmap_level), _M_view_mipmap_levels(_Other._M_view_mipmap_levels) + { + _Set_texture_ptr(_Other._M_texture_ptr); + } + + // Copy constructor with ability to redefine mipmap information + _Texture_descriptor(const _Texture_descriptor &_Other, unsigned int _Most_detailed_mipmap_level, unsigned int _View_mipmap_levels) __GPU + : _M_data_ptr(_Other._M_data_ptr), _M_texture_ptr(NULL), + _M_most_detailed_mipmap_level(_Most_detailed_mipmap_level), _M_view_mipmap_levels(_View_mipmap_levels) + { + _Set_texture_ptr(_Other._M_texture_ptr); + } + + // Assignment operator + _Texture_descriptor& operator=(const _Texture_descriptor &_Other) __GPU + { + if (this != &_Other) + { + _M_data_ptr = _Other._M_data_ptr; + _Set_texture_ptr(_Other._M_texture_ptr); + _M_most_detailed_mipmap_level = _Other._M_most_detailed_mipmap_level; + _M_view_mipmap_levels = _Other._M_view_mipmap_levels; + } + + return *this; + } + + // Move constructor + _Texture_descriptor(_Texture_descriptor &&_Other) __CPU_ONLY + { + *this = std::move(_Other); + } + + bool operator==(const _Texture_descriptor &_Other) const __GPU + { + return _M_texture_ptr == _Other._M_texture_ptr + && _M_data_ptr == _Other._M_data_ptr + && _M_most_detailed_mipmap_level == _Other._M_most_detailed_mipmap_level + && _M_view_mipmap_levels == _Other._M_view_mipmap_levels; + } + + _Ret_ _Texture* _Get_texture_ptr() const __CPU_ONLY + { + _ASSERTE(_M_texture_ptr); + return _M_texture_ptr; + } + + unsigned int _Get_most_detailed_mipmap_level() const __GPU + { + return _M_most_detailed_mipmap_level; + } + + unsigned int _Get_view_mipmap_levels() const __GPU + { + return _M_view_mipmap_levels; + } + + void _Set_view_mipmap_levels(unsigned int _View_mipmap_levels) __CPU_ONLY + { + _M_view_mipmap_levels = _View_mipmap_levels; + } + + void _Set_texture_ptr(_In_opt_ _Texture *_Texture_ptr) __CPU_ONLY + { + if (_M_texture_ptr != _Texture_ptr) + { + if (_M_texture_ptr != NULL) { + reinterpret_cast<_Reference_counter*>(_M_texture_ptr)->_Remove_reference(); + } + + _M_texture_ptr = _Texture_ptr; + + if (_M_texture_ptr != NULL) { + reinterpret_cast<_Reference_counter*>(_M_texture_ptr)->_Add_reference(); + } + } + } + +#if !defined(_CXXAMP) + void _Set_texture_ptr(_In_opt_ _Texture *_Texture_ptr) __GPU_ONLY + { + // No need to set the texture ptr on the GPU + UNREFERENCED_PARAMETER(_Texture_ptr); + _M_texture_ptr = NULL; + } +#endif // _CXXAMP + + // This helper function is used to determine aliasing and copy violations + bool _Are_mipmap_levels_overlapping(const _Texture_descriptor *_Other) const __CPU_ONLY + { + _ASSERTE(_Other); + + if (this->_Get_texture_ptr() != _Other->_Get_texture_ptr()) + { + return false; + } + + return !((_M_most_detailed_mipmap_level < _Other->_M_most_detailed_mipmap_level) ? ((_M_most_detailed_mipmap_level + _M_view_mipmap_levels - 1) < _Other->_M_most_detailed_mipmap_level) + : ((_Other->_M_most_detailed_mipmap_level + _Other->_M_view_mipmap_levels - 1) < _M_most_detailed_mipmap_level)); + } + + } _Texture_descriptor; + + // Caution: Do not change this structure defintion. + // This struct is special and is processed by the FE to identify the samplers + // used in a parallel_for_each. + typedef struct _Sampler_descriptor + { + // _M_data_ptr points to the sampler on accelerator + mutable void *_M_data_ptr; + + private: + // _M_sampler_ptr points to a _Sampler that holds the underlying sampler + // representation. This is private to ensure that all assignments to this data member + // only happen through public functions which properly manage the + // ref count of the underlying _Sampler object. + _Sampler *_M_sampler_ptr; + + public: + // Public functions + + // Default constructor + _Sampler_descriptor() __GPU + : _M_data_ptr(NULL), _M_sampler_ptr(NULL) + { + } + + _Sampler_descriptor(_In_ _Sampler * _Sampler_ptr) __GPU + : _M_data_ptr(NULL), _M_sampler_ptr(NULL) + { + _Set_sampler_ptr(_Sampler_ptr); + } + + // Destructor + ~_Sampler_descriptor() __GPU + { + _Set_sampler_ptr(NULL); + } + + // Copy constructor + _Sampler_descriptor(const _Sampler_descriptor &_Other) __GPU + : _M_data_ptr(_Other._M_data_ptr), _M_sampler_ptr(NULL) + { + _Set_sampler_ptr(_Other._M_sampler_ptr); + } + + // Assignment operator + _Sampler_descriptor& operator=(const _Sampler_descriptor &_Other) __GPU + { + if (this != &_Other) + { + _M_data_ptr = _Other._M_data_ptr; + _Set_sampler_ptr(_Other._M_sampler_ptr); + } + + return *this; + } + + // Move constructor + _Sampler_descriptor(_Sampler_descriptor &&_Other) __CPU_ONLY + { + *this = std::move(_Other); + } + + bool operator==(const _Sampler_descriptor &_Other) const __GPU + { + return _M_sampler_ptr == _Other._M_sampler_ptr && _M_data_ptr == _Other._M_data_ptr; + } + + _Ret_ _Sampler* _Get_sampler_ptr() const __CPU_ONLY + { + return _M_sampler_ptr; + } + + void _Set_sampler_ptr(_In_opt_ _Sampler *_Sampler_ptr) __CPU_ONLY + { + if (_M_sampler_ptr != _Sampler_ptr) + { + if (_M_sampler_ptr != NULL) { + reinterpret_cast<_Reference_counter*>(_M_sampler_ptr)->_Remove_reference(); + } + + _M_sampler_ptr = _Sampler_ptr; + + if (_M_sampler_ptr != NULL) { + reinterpret_cast<_Reference_counter*>(_M_sampler_ptr)->_Add_reference(); + } + } + } + +#if !defined(_CXXAMP) + void _Set_sampler_ptr(_In_opt_ _Sampler *_Sampler_ptr) __GPU_ONLY + { + // No need to set the sampler ptr on the GPU + UNREFERENCED_PARAMETER(_Sampler_ptr); + _M_sampler_ptr = NULL; + } +#endif // _CXXAMP + + } _Sampler_descriptor; + +} // namespace Concurrency::details + +// Forward declaration +class accelerator; + +namespace details +{ + _AMPIMP size_t __cdecl _Get_num_devices(); + _AMPIMP _Ret_ _Accelerator_impl_ptr * __cdecl _Get_devices(); + _AMPIMP accelerator __cdecl _Select_default_accelerator(); + _AMPIMP bool __cdecl _Set_default_accelerator(_Accelerator_impl_ptr _Accl); + _AMPIMP bool __cdecl _Is_D3D_accelerator_view(const accelerator_view& _Av); + _AMPIMP void __cdecl _Register_async_event(const _Event &_Ev, const std::shared_future<void> &_Shared_future); + _AMPIMP _Access_mode __cdecl _Get_recommended_buffer_host_access_mode(const accelerator_view &_Av); +} + +/// <summary> +/// Queuing modes supported for accelerator views +/// </summary> +enum queuing_mode { + queuing_mode_immediate, + queuing_mode_automatic +}; + +/// <summary> +/// Exception thrown due to a C++ AMP runtime_exception. +/// This is the base type for all C++ AMP exception types. +/// </summary> +class runtime_exception : public std::exception +{ +public: + /// <summary> + /// Construct a runtime_exception exception with a message and an error code + /// </summary> + /// <param name="_Message"> + /// Descriptive message of error + /// </param> + /// <param name="_Hresult"> + /// HRESULT of error that caused this exception + /// </param> + _AMPIMP runtime_exception(const char * _Message, HRESULT _Hresult) throw(); + + /// <summary> + /// Construct a runtime_exception exception with an error code + /// </summary> + /// <param name="_Hresult"> + /// HRESULT of error that caused this exception + /// </param> + _AMPIMP explicit runtime_exception(HRESULT _Hresult) throw(); + + /// <summary> + /// Copy construct a runtime_exception exception + /// </summary> + /// <param name="_Other"> + /// The runtime_exception object to be copied from + /// </param> + _AMPIMP runtime_exception(const runtime_exception &_Other) throw(); + + /// <summary> + /// Assignment operator + /// </summary> + /// <param name="_Other"> + /// The runtime_exception object to be assigned from + /// </param> + _AMPIMP runtime_exception &operator=(const runtime_exception &_Other) throw(); + + /// <summary> + /// Destruct a runtime_exception exception object instance + /// </summary> + _AMPIMP virtual ~runtime_exception() throw(); + + /// <summary> + /// Get the error code that caused this exception + /// </summary> + /// <returns> + /// HRESULT of error that caused the exception + /// </returns> + _AMPIMP HRESULT get_error_code() const throw(); + +private: + HRESULT _M_error_code; +}; // class runtime_exception + +/// <summary> +/// Exception thrown when an underlying OS/DirectX call fails +/// due to lack of system or device memory +/// </summary> +class out_of_memory : public runtime_exception +{ +public: + /// <summary> + /// Construct an out_of_memory exception with a message + /// </summary> + /// <param name="_Message"> + /// Descriptive message of error + /// </param> + _AMPIMP explicit out_of_memory(const char * _Message) throw(); + + /// <summary> + /// Construct an out_of_memory exception + /// </summary> + _AMPIMP out_of_memory () throw(); +}; // class out_of_memory + +namespace direct3d +{ + /// <summary> + /// Get the D3D device interface underlying a accelerator_view. + /// </summary> + /// <param name="_Av"> + /// The D3D accelerator_view for which the underlying D3D device interface is returned. + /// </param> + /// <returns> + /// The IUnknown interface pointer of the D3D device underlying the accelerator_view. + /// </returns> + _AMPIMP _Ret_ IUnknown * __cdecl get_device(const accelerator_view &_Av); + + /// <summary> + /// Create a accelerator_view from a D3D device interface pointer. + /// </summary> + /// <param name="_D3D_device"> + /// The D3D device interface pointer to create the accelerator_view from. + /// </param> + /// <param name="_Qmode"> + /// The queuing_mode to be used for the newly created accelerator_view. + /// This parameter has a default value of queuing_mode_automatic. + /// </param> + /// <returns> + /// The accelerator_view created from the passed D3D device interface. + /// </returns> + _AMPIMP accelerator_view __cdecl create_accelerator_view(_In_ IUnknown *_D3D_device, queuing_mode _Qmode = queuing_mode_automatic); + + /// <summary> + /// Create and return a new accelerator view on the specified accelerator. + /// </summary> + /// <param name="_Accelerator"> + /// The accelerator on which the new accelerator_view is to be created. + /// </param> + /// <param name="_Disable_timeout"> + /// A boolean parameter that specifies whether timeout should be disabled + /// for the newly created accelerator_view. This corresponds to the + /// D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT flag for Direct3D device creation + /// and is used to indicate if the operating system should allow workloads + /// that take more than 2 seconds to execute, without resetting the device + /// per the Windows timeout detection and recovery mechanism. Use of this flag + /// is recommended if you need to perform time consuming tasks on the accelerator_view. + /// </param> + /// <param name="_Qmode"> + /// The queuing_mode to be used for the newly created accelerator_view. + /// This parameter has a default value of queuing_mode_automatic. + /// </param> + /// <returns> + /// The newly created accelerator_view. + /// </returns> + _AMPIMP accelerator_view __cdecl create_accelerator_view(accelerator& _Accelerator, bool _Disable_timeout, queuing_mode _Qmode = queuing_mode_automatic); + + /// <summary> + /// Returns a boolean flag indicating if timeout is disabled + /// for the specified accelerator_view. This corresponds to the + /// D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT flag for Direct3D device creation. + /// </summary> + /// <param name="_Accelerator_view"> + /// The accelerator_view for which the timeout disabled setting is to be queried. + /// </param> + /// <returns> + /// A boolean flag indicating if timeout is disabled for the specified accelerator_view. + /// </returns> + _AMPIMP bool __cdecl is_timeout_disabled(const accelerator_view& _Accelerator_view); + + /// <summary> + /// Acquire a lock on an accelerator_view for the purpose of safely performing D3D operations on resources shared + /// with the accelerator_view. The accelerator_view and all C++ AMP resources associated with this accelerator_view + /// internally take this lock when performing operations and will block while another thread holds the D3D access lock. + /// + /// This lock is non-recursive: It is undefined behavior to call this function from a thread that already holds the lock. + /// It is undefined behavior to perform operations on the accelerator_view or any data container associated with the + /// accelerator_view from the thread that holds the D3D access lock. + /// + /// See also scoped_d3d_access_lock, a RAII-style class for a scope-based D3D access lock. + /// </summary> + /// <param name="_Av"> + /// The accelerator_view to lock. + /// </param> + _AMPIMP void __cdecl d3d_access_lock(accelerator_view &_Av); + + /// <summary> + /// Attempt to acquire the D3D access lock on an accelerator_view without blocking. + /// </summary> + /// <param name="_Av"> + /// The accelerator_view to lock. + /// </param> + /// <returns> + /// true if the lock was acquired, or false if it is currently held by another thread. + /// </returns> + _AMPIMP bool __cdecl d3d_access_try_lock(accelerator_view &_Av); + + /// <summary> + /// Release the D3D access lock on the given accelerator_view. If the calling thread does + /// not hold the lock on the accelerator_view the results are undefined. + /// </summary> + /// <param name="_Av"> + /// The accelerator_view for which the lock is to be released. + /// </param> + _AMPIMP void __cdecl d3d_access_unlock(accelerator_view &_Av); + + /// <summary> + /// Tag type to indicate the D3D access lock should be adopted rather than + /// acquired. + /// </summary> + struct adopt_d3d_access_lock_t {}; + + /// <summary> + /// RAII wrapper for a D3D access lock on an accelerator_view. + /// </summary> + class scoped_d3d_access_lock + { + public: + /// <summary> + /// Acquire a D3D access lock on the given accelerator_view. The lock is released + /// when this object goes out of scope. Construction will block until the lock + /// is acquired. + /// </summary> + /// <param name="_Av"> + /// The accelerator_view to lock. + /// </param> + _AMPIMP explicit scoped_d3d_access_lock(accelerator_view &_Av); + + /// <summary> + /// Construct a scoped_d3d_access_lock on an accelerator_view for which the lock + /// is already held (e.g. was acquired by d3d_access_try_lock). The D3D access + /// lock must already be held by the calling thread and not controlled by any other + /// scoped_d3d_access_lock. + /// </summary> + /// <param name="_Av"> + /// The accelerator_view for the lock to adopt. + /// </param> + /// <param name="_T"> + /// The adopt_d3d_access_lock object. + /// </param> + _AMPIMP explicit scoped_d3d_access_lock(accelerator_view &_Av, adopt_d3d_access_lock_t _T); + + /// <summary> + /// Destructor for scoped_d3d_access_lock: unlock the accelerator_view. + /// </summary> + _AMPIMP ~scoped_d3d_access_lock(); + + /// <summary> + /// Move constructor for scoped_d3d_access_lock: Take ownership of + /// a lock from another scoped_d3d_access_lock. + /// </summary> + /// <param name="_Other"> + /// The accelerator_view from which to move. + /// </param> + _AMPIMP scoped_d3d_access_lock(scoped_d3d_access_lock &&_Other); + + /// <summary> + /// Move assignment operator for scoped_d3d_access_lock: Take ownership + /// of a lock from another scoped_d3d_access_lock, releasing the previous + /// lock. + /// </summary> + /// <param name="_Other"> + /// The accelerator_view from which to move. + /// </param> + /// <returns> + /// A reference to this scoped_accelerator_view_lock. + /// </returns> + _AMPIMP scoped_d3d_access_lock& operator=(scoped_d3d_access_lock &&_Other); + + private: + // No copy constructor + scoped_d3d_access_lock(const scoped_d3d_access_lock &_Other); + + // No assignment operator + scoped_d3d_access_lock & operator=(const scoped_d3d_access_lock &_Other); + + _Accelerator_view_impl_ptr _M_impl; + }; +} // namespace direct3d + +/// <summary> +/// Class represents a accelerator abstraction for C++ AMP data-parallel devices +/// </summary> +class accelerator +{ + friend class accelerator_view; + + friend class details::_Ubiquitous_buffer; + + friend _AMPIMP accelerator details::_Select_default_accelerator(); + + _AMPIMP friend accelerator_view __cdecl direct3d::create_accelerator_view(accelerator& _Accelerator, bool _Disable_timeout, queuing_mode _Qmode /* = queuing_mode_automatic */); + + friend _Ret_ details::_Accelerator_impl* details::_Get_accelerator_impl_ptr(const accelerator& _Accl); + +public: + + /// <summary> + /// String constant for default accelerator + /// </summary> + _AMPIMP static const wchar_t default_accelerator[]; + + /// <summary> + /// String constant for cpu accelerator + /// </summary> + _AMPIMP static const wchar_t cpu_accelerator[]; + + /// <summary> + /// String constant for direct3d WARP accelerator + /// </summary> + _AMPIMP static const wchar_t direct3d_warp[]; + + /// <summary> + /// String constant for direct3d reference accelerator + /// </summary> + _AMPIMP static const wchar_t direct3d_ref[]; + + /// <summary> + /// Construct a accelerator representing the default accelerator + /// </summary> + _AMPIMP accelerator(); + + /// <summary> + /// Construct a accelerator representing the accelerator with the + /// specified device instance path + /// </summary> + explicit accelerator(const std::wstring &_Device_path) : _M_impl(NULL) + { + _Init(_Device_path.c_str()); + } + + /// <summary> + /// Destructor + /// </summary> + _AMPIMP ~accelerator(); + + /// <summary> + /// Copy constructor + /// </summary> + _AMPIMP accelerator(const accelerator &_Other); + + /// <summary> + /// Assignment operator + /// </summary> + _AMPIMP accelerator &operator=(const accelerator &_Other); + + /// <summary> + /// Returns the vector of accelerator objects representing all available accelerators + /// </summary> + /// <returns> + /// The vector of available accelerators + /// </returns> + static inline std::vector<accelerator> get_all() + { + std::vector<accelerator> _AcceleratorVector; + size_t _NumDevices = details::_Get_num_devices(); + for (size_t _I = 0; (_I < _NumDevices); ++_I) + { + _AcceleratorVector.push_back(details::_Get_devices()[_I]); + } + + return _AcceleratorVector; + } + + /// <summary> + /// Sets the default accelerator to be used for any operation + /// that implicitly uses the default accelerator. This method + /// only succeeds if the runtime selected default accelerator + /// has not already been used in an operation that implicitly + /// uses the default accelerator + /// </summary> + /// <returns> + /// A boolean value indicating if the call succeeds in setting + /// the default accelerator + /// </returns> + static inline bool set_default(const std::wstring& _Path) + { + accelerator _Accl(_Path); + return details::_Set_default_accelerator(_Accl._M_impl); + } + + /// <summary> + /// Returns the auto selection accelerator_view which when specified + /// as the parallel_for_each target results in the target accelerator_view + /// for executing the parallel_for_each kernel to be automatically selected + /// by the runtime. For all other purposes, the accelerator_view returned + /// by this method is the same as the default accelerator_view of the default + /// accelerator + /// </summary> + _AMPIMP static accelerator_view __cdecl get_auto_selection_view(); + + /// <summary> + /// Returns the system-wide unique device instance path as a std::wstring + /// </summary> + std::wstring get_device_path() const + { + return _Get_device_path(); + } + + __declspec(property(get=get_device_path)) std::wstring device_path; + + /// <summary> + /// Get the version for this accelerator + /// </summary> + _AMPIMP unsigned int get_version() const; + __declspec(property(get=get_version)) unsigned int version; // hiword=major, loword=minor + + /// <summary> + /// Returns the device description as a std::wstring + /// </summary> + std::wstring get_description() const + { + return _Get_description(); + } + + __declspec(property(get=get_description)) std::wstring description; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator + /// was created with DEBUG layer enabled for extensive error reporting + /// </summary> + _AMPIMP bool get_is_debug() const; + __declspec(property(get=get_is_debug)) bool is_debug; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator is emulated. + /// This is true, for example, with the direct3d reference and WARP accelerators. + /// </summary> + _AMPIMP bool get_is_emulated() const; + __declspec(property(get=get_is_emulated)) bool is_emulated; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator + /// is attached to a display + /// </summary> + _AMPIMP bool get_has_display() const; + __declspec(property(get=get_has_display)) bool has_display; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator + /// supports full double precision (including double division, + /// precise_math functions, int to double, double to int conversions) + /// in a parallel_for_each kernel. + /// </summary> + _AMPIMP bool get_supports_double_precision() const; + __declspec(property(get=get_supports_double_precision)) bool supports_double_precision; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator + /// has limited double precision support (excludes double division, + /// precise_math functions, int to double, double to int conversions) + /// for a parallel_for_each kernel. + /// </summary> + _AMPIMP bool get_supports_limited_double_precision() const; + __declspec(property(get=get_supports_limited_double_precision)) bool supports_limited_double_precision; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator + /// supports memory accessible both by the accelerator and the CPU. + /// </summary> + _AMPIMP bool get_supports_cpu_shared_memory() const; + __declspec(property(get=get_supports_cpu_shared_memory)) bool supports_cpu_shared_memory; + + /// <summary> + /// Return the default accelerator view associated with this accelerator + /// </summary> + _AMPIMP accelerator_view get_default_view() const; + __declspec(property(get=get_default_view)) accelerator_view default_view; + + /// <summary> + /// Get the dedicated memory for this accelerator in KB + /// </summary> + _AMPIMP size_t get_dedicated_memory() const; + __declspec(property(get=get_dedicated_memory)) size_t dedicated_memory; + + /// <summary> + /// Get the default cpu access_type for buffers created on this accelerator + /// </summary> + _AMPIMP access_type get_default_cpu_access_type() const; + __declspec(property(get=get_default_cpu_access_type)) access_type default_cpu_access_type; + + /// <summary> + /// Set the default cpu access_type for arrays created on this accelerator + /// or for implicit memory allocations as part of array_views accessed + /// on this this accelerator. This method only succeeds if the default_cpu_access_type + /// for the accelerator has not already been overriden by a previous call to this method + /// and the runtime selected default_cpu_access_type for this accelerator has not yet + /// been used for allocating an array or for an implicit memory allocation backing an + /// array_view accessed on this accelerator. + /// </summary> + /// <param name="_Default_cpu_access_type"> + /// The default cpu access_type to be used for array/array_view memory allocations + /// on this accelerator. + /// </param> + /// <returns> + /// A boolean value indicating if the default cpu access_type for the accelerator + /// was successfully set. + /// </returns> + _AMPIMP bool set_default_cpu_access_type(access_type _Default_cpu_access_type); + + /// <summary> + /// Create and return a new accelerator view on this accelerator + /// with the specified queuing mode. When unspecified the accelerator_view + /// is created with queuing_mode_automatic queuing mode. + /// </summary> + _AMPIMP accelerator_view create_view(queuing_mode qmode = queuing_mode_automatic); + + /// <summary> + /// Return true if the other accelerator is same as this accelerator; false otherwise + /// </summary> + _AMPIMP bool operator==(const accelerator &_Other) const; + + /// <summary> + /// Return false if the other accelerator is same as this accelerator; true otherwise + /// </summary> + _AMPIMP bool operator!=(const accelerator &_Other) const; + +private: + + // Private constructor + _AMPIMP accelerator(_Accelerator_impl_ptr _Impl); + + // Private helper methods + _AMPIMP const wchar_t *_Get_device_path() const; + _AMPIMP const wchar_t *_Get_description() const; + + _AMPIMP void _Init(const wchar_t *_Path); + +private: + + _Accelerator_impl_ptr _M_impl; +}; + +/// <summary> +/// Class represents a future corresponding to a C++ AMP asynchronous operation +/// </summary> +class completion_future +{ + friend class details::_Amp_runtime_trace; +public: + + /// <summary> + /// Default constructor + /// </summary> + completion_future() + { + } + + /// <summary> + /// Copy constructor + /// </summary> + completion_future(const completion_future& _Other) + : _M_shared_future(_Other._M_shared_future), + _M_task(_Other._M_task) + { + } + + /// <summary> + /// Move constructor + /// </summary> + completion_future(completion_future&& _Other) + : _M_shared_future(std::move(_Other._M_shared_future)), + _M_task(std::move(_Other._M_task)) + { + } + + /// <summary> + /// Destructor + /// </summary> + ~completion_future() + { + } + + /// <summary> + /// Copy assignment operator + /// </summary> + completion_future& operator=(const completion_future& _Other) + { + if (this != &_Other) { + _M_shared_future = _Other._M_shared_future; + _M_task = _Other._M_task; + } + + return (*this); + } + + /// <summary> + /// Move assignment operator + /// </summary> + completion_future& operator=(completion_future&& _Other) + { + if (this != &_Other) { + _M_shared_future = std::move(_Other._M_shared_future); + _M_task = std::move(_Other._M_task); + } + + return (*this); + } + + /// <summary> + /// Waits until the associated asynchronous operation completes + /// Throws the stored exception if one was encountered during the + /// asynchronous operation + /// </summary> + void get() const + { + _M_shared_future.get(); + } + + /// <summary> + /// Returns true if the object is associated with an asynchronous + /// operation + /// </summary> + /// <returns> + /// true if the object is associated with an asynchronous operation + /// and false otherwise + /// </returns> + bool valid() const + { + return _M_shared_future.valid(); + } + + /// <summary> + /// Blocks until the associated asynchronous operation completes + /// </summary> + void wait() const + { + _M_shared_future.wait(); + } + + /// <summary> + /// Blocks until the associated asynchronous operation completes or + /// _Rel_time has elapsed + /// </summary> + /// <returns> + /// - future_status::deferred if the associated asynchronous operation is not running + /// - future_status::ready if the associated asynchronous operation is finished + /// - future_status::timeout if the time period specified has elapsed + /// </returns> + template <class _Rep, class _Period> + std::future_status wait_for(const std::chrono::duration<_Rep, _Period>& _Rel_time) const + { + return _M_shared_future.wait_for(_Rel_time); + } + + /// <summary> + /// Blocks until the associated asynchronous operation completes or + /// until the current time exceeds _Abs_time + /// </summary> + /// <returns> + /// - future_status::deferred if the associated asynchronous operation is not running + /// - future_status::ready if the associated asynchronous operation is finished + /// - future_status::timeout if the time point specified has been reached + /// </returns> + template <class _Clock, class _Duration> + std::future_status wait_until(const std::chrono::time_point<_Clock, _Duration>& _Abs_time) const + { + return _M_shared_future.wait_until(_Abs_time); + } + + /// <summary> + /// Returns a std::shared_future<void> object corresponding to the + /// associated asynchronous operation + /// </summary> + /// <returns> + /// A std::shared_future<void> object corresponding to the associated + /// asynchronous operation + /// </returns> + operator std::shared_future<void>() const + { + return _M_shared_future; + } + + /// <summary> + /// Chains a callback Functor to the completion_future to be executed + /// when the associated asynchronous operation finishes execution + /// </summary> + template <typename _Functor> + void then(const _Functor &_Func) const + { + this->to_task().then(_Func); + } + + /// <summary> + /// Returns a concurrency::task<void> object corresponding to the + /// associated asynchronous operation + /// </summary> + /// <returns> + /// A concurrency::task<void> object corresponding to the associated + /// asynchronous operation + /// </returns> + concurrency::task<void> to_task() const + { + return _M_task; + } + +private: + + // Private constructor + completion_future(const std::shared_future<void> &_Shared_future, + const concurrency::task<void>& _Task) + : _M_shared_future(_Shared_future), _M_task(_Task) + { + } + + std::shared_future<void> _M_shared_future; + concurrency::task<void> _M_task; +}; + +/// <summary> +/// Class represents a virtual device abstraction on a C++ AMP data-parallel accelerator +/// </summary> +class accelerator_view +{ + friend class accelerator; + friend class details::_Buffer; + friend class details::_Texture; + friend class details::_Sampler; + friend class details::_Ubiquitous_buffer; + friend class details::_D3D_interop; + friend class details::_D3D_accelerator_view_impl; + friend class details::_CPU_accelerator_view_impl; + friend class details::_Accelerator_view_hasher; + + _AMPIMP friend _Ret_ IUnknown * __cdecl direct3d::get_device(const accelerator_view &_Av); + + _AMPIMP friend accelerator_view __cdecl direct3d::create_accelerator_view(_In_ IUnknown *_D3D_device, queuing_mode qmode /* = queuing_mode_automatic */); + + _AMPIMP friend accelerator_view __cdecl direct3d::create_accelerator_view(accelerator& _Accelerator, bool _Disable_timeout, queuing_mode _Qmode /* = queuing_mode_automatic */); + + _AMPIMP friend bool __cdecl direct3d::is_timeout_disabled(const accelerator_view& _Accelerator_view); + + friend _Ret_ details::_Accelerator_view_impl* details::_Get_accelerator_view_impl_ptr(const accelerator_view& _Accl_view); + +public: + + /// <summary> + /// Destructor + /// </summary> + _AMPIMP ~accelerator_view(); + + /// <summary> + /// Copy constructor + /// </summary> + _AMPIMP accelerator_view(const accelerator_view &_Other); + + /// <summary> + /// Assignment operator + /// </summary> + _AMPIMP accelerator_view &operator=(const accelerator_view &_Other); + + /// <summary> + /// Get the accelerator for this accelerator view + /// </summary> + _AMPIMP accelerator get_accelerator() const; + __declspec(property(get=get_accelerator)) Concurrency::accelerator accelerator; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator view + /// was created with DEBUG layer enabled for extensive error reporting + /// </summary> + _AMPIMP bool get_is_debug() const; + __declspec(property(get=get_is_debug)) bool is_debug; + + /// <summary> + /// Get the version for this accelerator view + /// </summary> + _AMPIMP unsigned int get_version() const; + __declspec(property(get=get_version)) unsigned int version; // hiword=major, loword=minor + + /// <summary> + /// Get the queuing mode for this accelerator view + /// </summary> + _AMPIMP queuing_mode get_queuing_mode() const; + __declspec(property(get=get_queuing_mode)) Concurrency::queuing_mode queuing_mode; + + /// <summary> + /// Returns a boolean value indicating whether the accelerator view + /// when passed to a parallel_for_each would result in automatic + /// selection of an appropriate execution target by the runtime + /// </summary> + _AMPIMP bool get_is_auto_selection() const; + __declspec(property(get=get_is_auto_selection)) bool is_auto_selection; + + /// <summary> + /// Return true if the other accelerator view is same as this accelerator view; false otherwise + /// </summary> + _AMPIMP bool operator==(const accelerator_view &_Other) const; + + /// <summary> + /// Return false if the other accelerator view is same as this accelerator view; true otherwise + /// </summary> + _AMPIMP bool operator!=(const accelerator_view &_Other) const; + + /// <summary> + /// Waits for completion of all commands submitted so far to this accelerator_view + /// </summary> + _AMPIMP void wait(); + + /// <summary> + /// Submit all pending commands queued to this accelerator_view to the accelerator + /// for execution. + /// </summary> + _AMPIMP void flush(); + + /// <summary> + /// Return a future to track the completion of all commands submitted so far to this accelerator_view + /// </summary> + _AMPIMP concurrency::completion_future create_marker(); + +private: + + // No default constructor + accelerator_view(); + + // Private constructor + _AMPIMP accelerator_view(_Accelerator_view_impl_ptr _Impl, bool _Auto_selection = false); + +private: + + _Accelerator_view_impl_ptr _M_impl; + bool _M_auto_selection; +}; + +namespace details +{ + inline _Ret_ _Accelerator_view_impl* _Get_accelerator_view_impl_ptr(const accelerator_view& _Accl_view) + { + return _Accl_view._M_impl; + } + + inline _Ret_ _Accelerator_impl* _Get_accelerator_impl_ptr(const accelerator& _Accl) + { + return _Accl._M_impl; + } + + // Type defining a hasher for accelerator_view objects + // for use with std::unordered_set and std::unordered_map + class _Accelerator_view_hasher + { + public: + size_t operator()(const accelerator_view &_Accl_view) const + { + std::hash<_Accelerator_view_impl*> _HashFunctor; + return _HashFunctor(_Accl_view._M_impl._Get_ptr()); + } + }; + + typedef std::unordered_set<accelerator_view, _Accelerator_view_hasher> _Accelerator_view_unordered_set; + + // Describes the N dimensional shape of a view in a buffer + class _View_shape : public _Reference_counter + { + public: + + _AMPIMP static _Ret_ _View_shape* __cdecl _Create_view_shape(unsigned int _Rank, unsigned int _Linear_offset, + const unsigned int *_Base_extent, const unsigned int *_View_offset, + const unsigned int *_View_extent, const bool *_Projection_info = NULL); + + _AMPIMP _Ret_ _View_shape* _Get_reduced_shape_for_copy(); + + inline unsigned int _Get_rank() const + { + return _M_rank; + } + + inline unsigned int _Get_linear_offset() const + { + return _M_linear_offset; + } + + inline const unsigned int *_Get_base_extent() const + { + return _M_base_extent; + } + + inline const unsigned int *_Get_view_offset() const + { + return _M_view_offset; + } + inline const unsigned int *_Get_view_extent() const + { + return _M_view_extent; + } + + inline const bool *_Get_projection_info() const + { + return _M_projection_info; + } + + inline bool _Is_projection() const + { + return _M_projection_info[0]; + } + + inline bool _Is_valid(size_t _Buffer_size) const + { + // The end point of the base shape should not be greater than the size of the buffer + size_t endLinearOffset = _M_linear_offset + _Get_extent_size(_M_rank, _M_base_extent); + if (endLinearOffset > _Buffer_size) { + return false; + } + + return _Is_valid(); + } + + inline unsigned int _Get_view_size() const + { + return _Get_extent_size(_M_rank, _M_view_extent); + } + + inline unsigned int _Get_view_linear_offset() const + { + return _Get_linear_offset(_M_view_offset); + } + + static inline bool + _Compare_extent_with_elem_size(unsigned int _Rank, const unsigned int *_Extent1, size_t _Elem_size1, const unsigned int *_Extent2, size_t _Elem_size2) + { + _ASSERTE((_Rank >= 1) && (_Extent1 != NULL)&& (_Extent2 != NULL)); + + // The extents should match accounting for the element sizes of the respective buffers + if ((_Extent1[_Rank - 1] * _Elem_size1) != (_Extent2[_Rank - 1] * _Elem_size2)) + { + return false; + } + + // Now compare the extent in all but the least significant dimension + if ((_Rank > 1) && !_Compare_extent(_Rank - 1, _Extent1, _Extent2)) + { + return false; + } + + return true; + } + + + static inline bool + _Compare_extent(unsigned int _Rank, const unsigned int *_Extent1, const unsigned int *_Extent2) + { + for (size_t _I = 0; _I < _Rank; ++_I) { + if (_Extent1[_I] != _Extent2[_I]) { + return false; + } + } + + return true; + } + + inline bool _Is_view_linear(unsigned int &_Linear_offset, unsigned int &_Linear_size) const + { + // The effective rank for the purpose of determining linearity + // depends on the highest dimension in which the extent is not 1 + unsigned int _First_dim_with_non_unit_extent = 0; + while ((_First_dim_with_non_unit_extent < _M_rank) && (_M_view_extent[_First_dim_with_non_unit_extent] == 1)) { + _First_dim_with_non_unit_extent++; + } + + unsigned int _Effective_rank = (_M_rank - _First_dim_with_non_unit_extent); + + // It is linear if the effective rank is <= 1 or the base extent + // and view extent are same in all but the highest dimension with + // non-unit extent + if ((_Effective_rank <= 1) || + (_Compare_extent(_Effective_rank - 1, &_M_base_extent[_First_dim_with_non_unit_extent + 1], &_M_view_extent[_First_dim_with_non_unit_extent + 1]))) + { + _Linear_offset = _Get_view_linear_offset(); + _Linear_size = _Get_view_size(); + return true; + } + + return false; + } + + inline bool _Overlaps(const _View_shape* _Other) const + { + if (_Compare_base_shape(_Other)) + { + // If the base shapes are identical we will do the N-dimensional + // bounding box overlap test + + for (size_t _I = 0; _I < _M_rank; ++_I) + { + if (!_Intervals_overlap(_M_view_offset[_I], _M_view_offset[_I] + _M_view_extent[_I] - 1, + _Other->_M_view_offset[_I], _Other->_M_view_offset[_I] + _Other->_M_view_extent[_I] - 1)) + { + return false; + } + } + + return true; + } + else + { + // The base shapes are different. Check based on linear intervals + size_t firstStart = _Get_view_linear_offset(); + size_t firstEnd = firstStart + _Get_view_size() - 1; + + size_t secondStart = _Other->_Get_view_linear_offset(); + size_t secondEnd = secondStart + _Other->_Get_view_size() - 1; + + return _Intervals_overlap(firstStart, firstEnd, secondStart, secondEnd); + } + } + + inline bool _Subsumes(const _View_shape* _Other) const + { + // Subsumption test can only be done for shapes that have the same base shape or + // when both have a rank of 1 + if ((_M_rank == 1) && (_Other->_Get_rank() == 1)) + { + size_t thisStart = _Get_view_linear_offset(); + size_t thisEnd = thisStart + _Get_view_size() - 1; + + size_t otherStart = _Other->_Get_view_linear_offset(); + size_t otherEnd = otherStart + _Other->_Get_view_size() - 1; + + return ((otherStart >= thisStart) && (otherEnd <= thisEnd)); + } + + if (!_Compare_base_shape(_Other)) { + return false; + } + + if (!_Contains(_Other->_Get_view_offset())) { + return false; + } + + std::vector<unsigned int> otherEndPointIndex(_M_rank); + for (size_t _I = 0; _I < _M_rank; ++_I) { + otherEndPointIndex[_I] = _Other->_Get_view_offset()[_I] + _Other->_Get_view_extent()[_I] - 1; + } + + return _Contains(otherEndPointIndex.data()); + } + + private: + // Private constructor to force construction through the _Create_view_shape method + _View_shape(unsigned int _Rank, unsigned int _Linear_offset, + const unsigned int *_Base_extent, const unsigned int *_View_offset, + const unsigned int *_View_extent, const bool *_Projection_info); + + virtual ~_View_shape(); + + // No default constructor or copy/assignment + _View_shape(); + _View_shape(const _View_shape &_Other); + _View_shape(_View_shape &&_Other); + _View_shape& operator=(const _View_shape &_Other); + _View_shape& operator=(_View_shape &&_Other); + + // Helper methods + static bool _Intervals_overlap(size_t _First_start, size_t _First_end, + size_t _Second_start, size_t _Second_end) + { + // Order the intervals by their start points + if (_First_start > _Second_start) { + size_t temp = _First_start; + _First_start = _Second_start; + _Second_start = temp; + + temp = _First_end; + _First_end = _Second_end; + _Second_end = temp; + } + + // The start of the second one must be within the bounds of the first one + return (_Second_start <= _First_end); + } + + static unsigned int _Get_extent_size(unsigned int _Rank, const unsigned int *_Extent) + { + unsigned int totalExtent = 1; + for (size_t _I = 0; _I < _Rank; ++_I) { + totalExtent *= _Extent[_I]; + } + + return totalExtent; + } + + inline bool _Is_valid() const + { + if (_M_rank == 0) { + return false; + } + + // Ensure the _M_view_offset + _M_view_extent is within the bounds of _M_base_extent + size_t viewSize = 1; + + for (size_t _I = 0; _I < _M_rank; ++_I) + { + viewSize *= _M_view_extent[_I]; + if ((_M_view_offset[_I] + _M_view_extent[_I]) > _M_base_extent[_I]) { + return false; + } + } + + if (viewSize == 0) { + return false; + } + + return true; + } + + inline bool _Compare_base_shape(const _View_shape* _Other) const + { + return ((_M_rank == _Other->_M_rank) && + (_M_linear_offset == _Other->_M_linear_offset) && + _Compare_extent(_M_rank, _M_base_extent, _Other->_M_base_extent)); + } + + // Checks if the element at the specified index + // is contained within this view shape + // Assumes the rank of the index is same as the + // rank of this view's shape + inline bool _Contains(const unsigned int* _Element_index) const + { + for (size_t _I = 0; _I < _M_rank; ++_I) + { + if ((_Element_index[_I] < _M_view_offset[_I]) || + (_Element_index[_I] >= (_M_view_offset[_I] + _M_view_extent[_I]))) + { + return false; + } + } + + return true; + } + + inline unsigned int _Get_linear_offset(const unsigned int* _Element_index) const + { + unsigned int currMultiplier = 1; + unsigned int linearOffset = _M_linear_offset; + for (int _I = static_cast<int>(_M_rank - 1); _I >= 0; _I--) + { + linearOffset += (currMultiplier * _Element_index[_I]); + currMultiplier *= _M_base_extent[_I]; + } + + return linearOffset; + } + + private: + + unsigned int _M_rank; + unsigned int _M_linear_offset; + unsigned int *_M_base_extent; + unsigned int *_M_view_offset; + unsigned int *_M_view_extent; + bool *_M_projection_info; + }; + + // This function creates a new _View_shape object from an existing _View_shape object when the data underlying the view + // needs to be reinterpreted to use a different element size than the one used by the original view. + inline + _Ret_ _View_shape *_Create_reinterpreted_shape(const _View_shape* _Source_shape, size_t _Curr_elem_size, size_t _New_elem_size) + { + unsigned int _Rank = _Source_shape->_Get_rank(); + size_t _LinearOffsetInBytes = _Source_shape->_Get_linear_offset() * _Curr_elem_size; + size_t _BaseLSDExtentInBytes = (_Source_shape->_Get_base_extent())[_Rank - 1] * _Curr_elem_size; + size_t _ViewLSDOffsetInBytes = (_Source_shape->_Get_view_offset())[_Rank - 1] * _Curr_elem_size; + size_t _ViewLSDExtentInBytes = (_Source_shape->_Get_view_extent())[_Rank - 1] * _Curr_elem_size; + + _ASSERTE((_LinearOffsetInBytes % _New_elem_size) == 0); + _ASSERTE((_BaseLSDExtentInBytes % _New_elem_size) == 0); + _ASSERTE((_ViewLSDOffsetInBytes % _New_elem_size) == 0); + _ASSERTE((_ViewLSDExtentInBytes % _New_elem_size) == 0); + + size_t _Temp_val = _LinearOffsetInBytes / _New_elem_size; + _ASSERTE(_Temp_val <= UINT_MAX); + unsigned int _New_linear_offset = static_cast<unsigned int>(_Temp_val); + + std::vector<unsigned int> _New_base_extent(_Rank); + std::vector<unsigned int> _New_view_offset(_Rank); + std::vector<unsigned int> _New_view_extent(_Rank); + for (unsigned int i = 0; i < _Rank - 1; ++i) { + _New_base_extent[i] = (_Source_shape->_Get_base_extent())[i]; + _New_view_offset[i] = (_Source_shape->_Get_view_offset())[i]; + _New_view_extent[i] = (_Source_shape->_Get_view_extent())[i]; + } + + // The extent in the least significant dimension needs to be adjusted + _Temp_val = _BaseLSDExtentInBytes / _New_elem_size; + _ASSERTE(_Temp_val <= UINT_MAX); + _New_base_extent[_Rank - 1] = static_cast<unsigned int>(_Temp_val); + + _Temp_val = _ViewLSDOffsetInBytes / _New_elem_size; + _ASSERTE(_Temp_val <= UINT_MAX); + _New_view_offset[_Rank - 1] = static_cast<unsigned int>(_Temp_val); + + _Temp_val = _ViewLSDExtentInBytes / _New_elem_size; + _ASSERTE(_Temp_val <= UINT_MAX); + _New_view_extent[_Rank - 1] = static_cast<unsigned int>(_Temp_val); + + return _View_shape::_Create_view_shape(_Rank, _New_linear_offset, _New_base_extent.data(), _New_view_offset.data(), _New_view_extent.data()); + } + + inline _Access_mode _Get_synchronize_access_mode(access_type cpu_access_type) + { + switch(cpu_access_type) + { + case access_type_auto: + case access_type_read: + return _Read_access; + case access_type_write: + return _Write_access; + case access_type_read_write: + return _Read_write_access; + case access_type_none: + default: + _ASSERTE(false); + return _No_access; + } + } + + inline access_type _Get_cpu_access_type(_Access_mode _Cpu_access_mode) + { + access_type _Cpu_access_type = access_type_none; + if (_Cpu_access_mode & _Read_access) { + _Cpu_access_type = static_cast<access_type>(_Cpu_access_type | access_type_read); + } + + if (_Cpu_access_mode & _Write_access) { + _Cpu_access_type = static_cast<access_type>(_Cpu_access_type | access_type_write); + } + + return _Cpu_access_type; + } + + // Class manages a raw buffer in a accelerator view + class _Buffer : public _Reference_counter + { + friend class _CPU_accelerator_view_impl; + friend class _D3D_accelerator_view_impl; + friend class _D3D_temp_staging_cache; + + public: + + // Force construction through these static public method to ensure that _Buffer + // objects are allocated in the runtime + + // Allocate a new buffer on the specified accelerator_view + _AMPIMP static _Ret_ _Buffer * __cdecl _Create_buffer(accelerator_view _Accelerator_view, accelerator_view _Access_on_accelerator_view, size_t _Num_elems, + size_t _Elem_size, bool _Is_temp = false, access_type _Cpu_access_type = access_type_auto); + + // Create a buffer object from a pre-allocated storage on the specified accelerator_view. This can be thought + // of as the accelerator_view "adopting" the passed data buffer. + _AMPIMP static _Ret_ _Buffer * __cdecl _Create_buffer(_In_ void *_Data_ptr, accelerator_view _Accelerator_view, size_t _Num_elems, + size_t _Elem_size); + + // Create a staging buffer on the specified accelerator_view which can be accesed on the cpu upon mapping. + _AMPIMP static _Ret_ _Buffer * __cdecl _Create_stage_buffer(accelerator_view _Accelerator_view, accelerator_view _Access_on_accelerator_view, + size_t _Num_elems, size_t _Elem_size, bool _Is_temp = false); + + // Creates a temp staging buffer of the requested size. This function may create + // a staging buffer smaller than the requested size. + _AMPIMP static _Ret_ _Buffer * __cdecl _Get_temp_staging_buffer(accelerator_view _Av, size_t _Requested_num_elems, size_t _Elem_size); + + // Map a zero-copy or staging buffer for access on the CPU. + _AMPIMP void _Map_buffer(_Access_mode _Map_type, bool _Wait); + + // Asynchronously map a zero-copy or staging buffer for access on the CPU. + _AMPIMP _Event _Map_buffer_async(_Access_mode _Map_type); + + // Unmap a zero-copy or staging buffer denying CPU access + _AMPIMP void _Unmap_buffer(); + + // Copy data to _Dest asynchronously. + _AMPIMP _Event _Copy_to_async(_Out_ _Buffer * _Dest, size_t _Num_elems, size_t _Src_offset = 0, size_t _Dest_offset = 0); + + // Copy data to _Dest asynchronously. + _AMPIMP _Event _Copy_to_async(_Out_ _Buffer * _Dest, _View_shape_ptr _Src_shape, _View_shape_ptr _Dest_shape); + + _AMPIMP accelerator_view _Get_accelerator_view() const; + _AMPIMP accelerator_view _Get_access_on_accelerator_view() const; + + _AMPIMP void _Register_view(_In_ _View_key _Key); + _AMPIMP void _Unregister_view(_In_ _View_key _Key); + + // Return the raw data ptr - only a accelerator view implementation can interpret + // this raw pointer. This method should usually not be used in the AMP header files + // The _Get_host_ptr is the right way for accessing the host accesible ptr for a buffer + _Ret_ void * _Get_data_ptr() const + { + return _M_data_ptr; + } + + // Returns the host accessible ptr corresponding to the buffer. This would + // return NULL when the buffer is inaccesible on the CPU + _Ret_ void * _Get_host_ptr() const + { + return _M_host_ptr; + } + + size_t _Get_elem_size() const + { + return _M_elem_size; + } + + size_t _Get_num_elems() const + { + return _M_num_elems; + } + + _Ret_ _Accelerator_view_impl* _Get_accelerator_view_impl() const + { + return _M_accelerator_view; + } + + _Ret_ _Accelerator_view_impl* _Get_access_on_accelerator_view_impl() const + { + return _M_access_on_accelerator_view; + } + + bool _Owns_data() const + { + return _M_owns_data; + } + + _AMPIMP bool _Exclusively_owns_data(); + + bool _Is_staging() const + { + return _M_is_staging; + } + + _Access_mode _Get_allowed_host_access_mode() const + { + return _M_allowed_host_access_mode; + } + + access_type _Get_allowed_host_access_type() const + { + return _Get_cpu_access_type(_M_allowed_host_access_mode); + } + + bool _Is_host_accessible(_Access_mode _Requested_access_mode) const + { + return ((_Get_allowed_host_access_mode() & _Requested_access_mode) == _Requested_access_mode); + } + + _Access_mode _Get_current_host_access_mode() const + { + return _M_current_host_access_mode; + } + + bool _Is_temp() const + { + return _M_is_temp; + } + + bool _Is_adopted() const + { + // Is it adopted from interop? + return _M_is_adopted; + } + + bool _Is_buffer() const + { + return _M_is_buffer; + } + + _AMPIMP bool _Is_mappable() const; + + protected: + + // The _Buffer constructor is protected to force construction through the static + // _Create_buffer method to ensure the object is allocated in the runtime + _Buffer(_In_ _Accelerator_view_impl* _Av, _In_ void *_Buffer_data_ptr, _In_ void * _Host_ptr, + _Access_mode _Allowed_host_access_mode, _Access_mode _Current_host_access_mode, size_t _Num_elems, + size_t _Elem_size, bool _Owns_data, bool _Is_staging, bool _Is_temp, bool _Is_adopted); + + // protected destructor to force deletion through _Release + virtual ~_Buffer(); + + // No default consturctor, copy constructor and assignment operator + _Buffer(); + _Buffer(const _Buffer &rhs); + _Buffer &operator=(const _Buffer &rhs); + + void _Set_host_ptr(_In_ void *_Host_ptr, _Access_mode _Host_access_mode = _No_access) + { + _ASSERTE((_Host_ptr == NULL) || (_Host_access_mode != _No_access)); + + _M_host_ptr = _Host_ptr; + if (_Host_ptr == NULL) { + _M_current_host_access_mode = _No_access; + } + else { + _M_current_host_access_mode = _Host_access_mode; + } + } + + void _Set_data_ptr(_In_ IUnknown *_Data_ptr) + { + _M_data_ptr = _Data_ptr; + } + + protected: + _Accelerator_view_impl_ptr _M_accelerator_view; + _Accelerator_view_impl_ptr _M_access_on_accelerator_view; + void * _M_data_ptr; + void * _M_host_ptr; + _Access_mode _M_allowed_host_access_mode; + _Access_mode _M_current_host_access_mode; + size_t _M_elem_size; + size_t _M_num_elems; + bool _M_owns_data; + bool _M_is_staging; + + // Used to determine how to map the staging buffer after its involved in a copy + bool _M_is_temp; + + bool _M_is_adopted; + bool _M_is_buffer; + private: + // A set of view_keys to invalidate whenever the host ptr of a staging buffer is invalidated + std::unique_ptr<std::unordered_set<_View_key>> _M_view_keys; + Concurrency::critical_section _M_critical_section; + }; + + // Class manages a texture in a accelerator view + class _Texture : public _Buffer + { + friend class _CPU_accelerator_view_impl; + friend class _D3D_accelerator_view_impl; + friend class _D3D_temp_staging_cache; + + public: + + // Allocate a new texture on the specified accelerator_view + _AMPIMP static _Ret_ _Texture * __cdecl _Create_texture(accelerator_view _Accelerator_view, + unsigned int _Rank, + size_t _Width, size_t _Height, size_t _Depth, + unsigned int _Mip_levels, + _Short_vector_base_type_id _Type_id, + unsigned int _Num_channels, + unsigned int _Bits_per_channel, + bool _Is_temp = false); + + // Create a texture object from a pre-allocated storage on the specified accelerator_view. This can be thought + // of as the accelerator_view "adopting" the passed data buffer. + _AMPIMP static _Ret_ _Texture * __cdecl _Adopt_texture(unsigned int _Rank, _Texture_base_type_id _Id, + _In_ IUnknown *_Data_ptr, accelerator_view _Accelerator_view, + unsigned int _View_format); + + // Create a staging texture on the specified accelerator_view which can be accesed on the cpu upon mapping. + _AMPIMP static _Ret_ _Texture * __cdecl _Create_stage_texture(accelerator_view _Accelerator_view, accelerator_view _Access_on_accelerator_view, + unsigned int _Rank, + size_t _Width, size_t _Height, size_t _Depth, + unsigned int _Mip_levels, + unsigned int _Format, + bool _Is_temp = false); + + // Create a staging texture on the specified accelerator_view which can be accesed on the cpu upon mapping. + _AMPIMP static _Ret_ _Texture * __cdecl _Create_stage_texture(accelerator_view _Accelerator_view, accelerator_view _Access_on_accelerator_view, + unsigned int _Rank, + size_t _Width, size_t _Height, size_t _Depth, + unsigned int _Mip_levels, + _Short_vector_base_type_id _Type_id, + unsigned int _Num_channels, + unsigned int _Bits_per_channel); + + // Creates a temp staging texture. This function may create + // a staging texture smaller than the requested size. + _AMPIMP static _Ret_ _Texture * __cdecl _Get_temp_staging_texture(accelerator_view _Accelerator_view, + unsigned int _Rank, + size_t _Width, size_t _Height, size_t _Depth, + unsigned int _Mip_levels, + unsigned int _Format); + + // Constructs a new texture with the same properties as the given texture. + _AMPIMP static _Ret_ _Texture * __cdecl _Clone_texture(const _Texture *_Src, const accelerator_view &_Accelerator_view, const accelerator_view &_Associated_av); + + // Copy data to _Dest asynchronously for textures. The two textures must have been created with + // compatible physical formats. + _AMPIMP _Event _Copy_to_async(_Out_ _Texture * _Dest, const size_t *_Copy_extent, + const size_t *_Src_offset, const size_t *_Dst_offset, + unsigned int _Src_mipmap_level, unsigned int _Dst_mipmap_level); + + size_t _Get_width(unsigned int _Mip_offset = 0) const + { + return (_M_width >> _Mip_offset) ? (_M_width >> _Mip_offset) : 1U; + } + + size_t _Get_height(unsigned int _Mip_offset = 0) const + { + return (_M_height >> _Mip_offset) ? (_M_height >> _Mip_offset) : 1U; + } + + size_t _Get_depth(unsigned int _Mip_offset = 0) const + { + return (_M_depth >> _Mip_offset) ? (_M_depth >> _Mip_offset) : 1U; + } + + unsigned int _Get_rank() const + { + return _M_rank; + } + + unsigned int _Get_texture_format() const + { + return _M_texture_format; + } + + unsigned int _Get_view_format() const + { + return _M_view_format; + } + + unsigned int _Get_num_channels() const + { + return _M_num_channels; + } + + unsigned int _Get_bits_per_channel() const + { + // For texture adopted from interop, return 0. + return _Is_adopted() ? 0 : _M_bits_per_channel; + } + + unsigned int _Get_bits_per_element() const + { + return _M_bits_per_channel * _M_num_channels; + } + + unsigned int _Get_data_length(unsigned int _Most_detailed_mipmap_level, unsigned int _View_mipmap_levels, const size_t *_Extents = nullptr) const // in bytes + { + _ASSERTE(_View_mipmap_levels); + + unsigned long long _Bits_per_byte = 8ULL; + unsigned long long _Total_bytes = 0ULL; + + unsigned int _Mip_level = _Most_detailed_mipmap_level; + + // Sum up data length (in bytes) of all mipmap levels in the view + for (unsigned int _Mip_offset=0; _Mip_offset < _View_mipmap_levels; ++_Mip_offset) + { + unsigned long long _Width = 1ULL; + unsigned long long _Height = 1ULL; + unsigned long long _Depth = 1ULL; + + if (_Extents) + { + switch (_M_rank) + { + case 3: + _Depth = (_Extents[2] >> _Mip_level) ? (_Extents[2] >> _Mip_level) : 1U; + // deliberately fall thru + case 2: + _Height = (_Extents[1] >> _Mip_level) ? (_Extents[1] >> _Mip_level) : 1U; + // deliberately fall thru + case 1: + _Width = (_Extents[0] >> _Mip_level) ? (_Extents[0] >> _Mip_level) : 1U; + break; + default: + _ASSERTE(false); // textures are only rank 1-3 + } + } + else + { + _Width = _Get_width(_Mip_level); + _Height = _Get_height(_Mip_level); + _Depth = _Get_depth(_Mip_level); + } + + // Note _Get_bits_per_element() can be smaller than 8 + // Use unsigned long long to avoid integer overflow + _Total_bytes += ((_Width * _Height * _Depth * static_cast<unsigned long long>(_Get_bits_per_element())) + _Bits_per_byte - 1) / _Bits_per_byte; + + _Mip_level++; + } + + return static_cast<unsigned int>(_Total_bytes); + } + + unsigned int _Get_mip_levels() const + { + return _M_mip_levels; + } + + size_t _Get_row_pitch() const + { + return _M_row_pitch; + } + + void _Set_row_pitch(size_t _Val) + { + _M_row_pitch = _Val; + } + + size_t _Get_depth_pitch() const + { + return _M_depth_pitch; + } + + void _Set_depth_pitch(size_t _Val) + { + _M_depth_pitch = _Val; + } + + private: + + // The _Texture constructor is private to force construction through the static + // _Create_texture method to ensure the object is allocated in the runtime + _Texture(_In_ _Accelerator_view_impl* _Av, _In_ void *_Texture_data_ptr, _In_ void * _Host_ptr, + _Access_mode _Allowed_host_access_mode, _Access_mode _Current_host_access_mode, + unsigned int _Rank, + size_t _Width, size_t _Height, size_t _Depth, + unsigned int _Mip_levels, + unsigned int _Texture_format, + unsigned int _View_format, + unsigned int _Num_channels, + unsigned int _Bits_per_channel, + bool _Owns_data, bool _Is_staging, bool _Is_temp, bool _Is_adopted); + + // Private destructor to force deletion through _Release + ~_Texture(); + + // No default consturctor, copy constructor and assignment operator + _Texture(); + _Texture(const _Texture &rhs); + _Texture &operator=(const _Texture &rhs); + + // Texture only + unsigned int _M_rank; + size_t _M_width; + size_t _M_height; + size_t _M_depth; + unsigned int _M_texture_format; + unsigned int _M_view_format; + unsigned int _M_bits_per_channel; + unsigned int _M_num_channels; + unsigned int _M_mip_levels; + + size_t _M_row_pitch; + size_t _M_depth_pitch; + }; + + class _Sampler : public _Reference_counter + { + public: + // Create a new sampler with configurations exposed by C++ AMP. + _AMPIMP static _Ret_ _Sampler * __cdecl _Create( + unsigned int _Filter_mode, + unsigned int _Address_mode, + float _Border_r, + float _Border_g, + float _Border_b, + float _Border_a); + + // Create a sampler object given an adopted opaque data pointer + _AMPIMP static _Ret_ _Sampler * __cdecl _Create(_In_ void *_Data_ptr); + + // Return the raw data ptr - only an accelerator view implementation can interpret + // this raw pointer. This method should usually not be used in the AMP header files + _Ret_ void * _Get_data_ptr() const + { + return _M_data_ptr; + } + + bool _Is_adopted() const + { + // Is it adopted from interop? + return _M_is_adopted; + } + + unsigned int _Get_filter_mode() const + { + return _M_filter_mode; + } + + unsigned int _Get_address_mode() const + { + return _M_address_mode; + } + + const float* _Get_border_color() const + { + return &_M_border_color[0]; + } + + private: + // The _Sampler constructor is private to force construction through the static + // _Create method to ensure the object is allocated in the runtime + _Sampler(unsigned int _Filter_mode, unsigned int _Address_mode, float _Border_r, float _Border_g, float _Border_b, float _Border_a); + + _Sampler(_In_ void *_Data_ptr); + + // Private destructor to force deletion through _Release + ~_Sampler(); + + // No default consturctor, copy constructor and assignment operator + _Sampler(); + _Sampler(const _Sampler &rhs); + _Sampler &operator=(const _Sampler &rhs); + + void * _M_data_ptr; + bool _M_is_adopted; + unsigned int _M_filter_mode; + unsigned int _M_address_mode; + float _M_border_color[4]; + }; + + // Forward declaration for copy helper functions + _AMPIMP _Event __cdecl _Copy_impl(_In_ _Buffer *_Src, size_t _Src_offset, + _Out_ _Buffer * _Dst, size_t _Dest_offset, + size_t _Num_elems, size_t _Preferred_copy_chunk_num_elems = 0); + + _AMPIMP _Event __cdecl _Copy_async_impl(_In_ _Texture *_Src_tex, const size_t *_Src_offset, unsigned int _Src_mipmap_level, + _Out_ _Texture *_Dst_tex, const size_t *_Dst_offset, unsigned int _Dst_mipmap_level, + const size_t *_Copy_extent, const size_t *_Preferred_copy_chunk_extent = NULL); + + inline bool _Get_chunked_staging_texture(_In_ _Texture* _Tex, const size_t *_Copy_chunk_extent, _Inout_ size_t *_Remaining_copy_extent, _Out_ size_t *_Curr_copy_extent, _Out_ _Texture_ptr *_Staging_texture) + { + bool _Truncated_copy = false; + size_t _Allocation_extent[3] = { _Copy_chunk_extent[0], _Copy_chunk_extent[1], _Copy_chunk_extent[2] }; + + unsigned int _Most_sig_idx = _Tex->_Get_rank() - 1; + + if (_Allocation_extent[_Most_sig_idx] > _Remaining_copy_extent[_Most_sig_idx]) { + _Allocation_extent[_Most_sig_idx] = _Remaining_copy_extent[_Most_sig_idx]; + } + + _Texture_ptr _Stage = _Texture::_Get_temp_staging_texture(_Tex->_Get_accelerator_view(), _Tex->_Get_rank(), + _Allocation_extent[0], _Allocation_extent[1], _Allocation_extent[2], + /*_Mip_levels=*/1, _Tex->_Get_texture_format()); + + std::copy(&_Allocation_extent[0], &_Allocation_extent[3], stdext::make_unchecked_array_iterator(&_Curr_copy_extent[0])); + size_t _Staging_tex_extent[3] = {_Stage->_Get_width(), _Stage->_Get_height(), _Stage->_Get_depth()}; + if (_Curr_copy_extent[_Most_sig_idx] > _Staging_tex_extent[_Most_sig_idx]) { + _Curr_copy_extent[_Most_sig_idx] = _Staging_tex_extent[_Most_sig_idx]; + } + + // The truncation can however happen only in the most significant dimension and lower + // dimensions should not get truncated + if (_Curr_copy_extent[_Most_sig_idx] < _Remaining_copy_extent[_Most_sig_idx]) + { + _Remaining_copy_extent[_Most_sig_idx] -= _Curr_copy_extent[_Most_sig_idx]; + _Truncated_copy = true; + } + + for (unsigned int _I = 0; _I < _Most_sig_idx; _I++) + { + _ASSERTE(_Curr_copy_extent[_I] == _Remaining_copy_extent[_I]); + } + + *_Staging_texture = _Stage; + return _Truncated_copy; + } + + #pragma warning ( push ) + #pragma warning ( disable : 6101 ) + // Supress "warning C6101: Returning uninitialized memory '*_Dst'.: A successful" + // "path through the function does not set the named _Out_ parameter." + // The callers to _Copy_data_on_host all have static_assert that _Rank has to be 1, 2, or 3 dimensions for texture + // + template <typename _Input_iterator, typename _Value_type> + inline void _Copy_data_on_host(int _Rank, _Input_iterator _Src, _Out_ _Value_type *_Dst, + size_t _Width, size_t _Height, size_t _Depth, + size_t _Dst_row_pitch_in_bytes, size_t _Dst_depth_pitch_in_bytes, + size_t _Src_row_pitch, size_t _Src_depth_pitch) + { + switch(_Rank) + { + case 1: + { + _Input_iterator _End = _Src; + std::advance(_End, _Width); + std::copy(_Src, _End, stdext::make_unchecked_array_iterator(_Dst)); + } + break; + case 2: + { + unsigned char *_Dst_ptr = reinterpret_cast<unsigned char *>(_Dst); + _Input_iterator _Src_start = _Src; + for (size_t _I = 0; _I < _Height; _I++) + { + _Input_iterator _Src_end = _Src_start; + std::advance(_Src_end, _Width); + + std::copy(_Src_start, _Src_end, stdext::make_unchecked_array_iterator(reinterpret_cast<_Value_type*>(_Dst_ptr))); + + _Dst_ptr += _Dst_row_pitch_in_bytes; + std::advance(_Src_start, _Src_row_pitch); + } + } + break; + case 3: + { + unsigned char *_Dst_ptr_slice_start = reinterpret_cast<unsigned char *>(_Dst); + _Input_iterator _Src_depth_slice_start = _Src; + for (size_t _I = 0; _I < _Depth; _I++) + { + _Input_iterator _Src_start = _Src_depth_slice_start; + unsigned char *_Dst_ptr = _Dst_ptr_slice_start; + + for (size_t _J = 0; _J < _Height; _J++) + { + _Input_iterator _Src_end = _Src_start; + std::advance(_Src_end, _Width); + + std::copy(_Src_start, _Src_end, stdext::make_unchecked_array_iterator(reinterpret_cast<_Value_type*>(_Dst_ptr))); + + _Dst_ptr += _Dst_row_pitch_in_bytes; + std::advance(_Src_start, _Src_row_pitch); + } + + _Dst_ptr_slice_start += _Dst_depth_pitch_in_bytes; + std::advance(_Src_depth_slice_start, _Src_depth_pitch); + } + } + break; + default: + _ASSERTE(FALSE); + break; + } + } + #pragma warning ( pop ) // disable : 6101 + + template <typename _Output_iterator, typename _Value_type> + inline void _Copy_data_on_host(int _Rank, const _Value_type * _Src, _Output_iterator _Dst, + size_t _Width, size_t _Height, size_t _Depth, + size_t _Src_row_pitch_in_bytes, size_t _Src_depth_pitch_in_bytes, + size_t _Dst_row_pitch, size_t _Dst_depth_pitch) + { + switch(_Rank) + { + case 1: + { + const _Value_type * _End = _Src + _Width; + std::copy(stdext::make_unchecked_array_iterator(_Src), stdext::make_unchecked_array_iterator(_End), _Dst); + } + break; + case 2: + { + const unsigned char *_Src_ptr = reinterpret_cast<const unsigned char *>(_Src); + _Output_iterator _Dst_iter = _Dst; + for (size_t _I = 0; _I < _Height; _I++) + { + const _Value_type * _Src_end = reinterpret_cast<const _Value_type*>(_Src_ptr) + _Width; + + std::copy(stdext::make_unchecked_array_iterator(reinterpret_cast<const _Value_type*>(_Src_ptr)), stdext::make_unchecked_array_iterator(_Src_end), _Dst_iter); + std::advance(_Dst_iter, _Dst_row_pitch); + _Src_ptr += _Src_row_pitch_in_bytes; + } + } + break; + case 3: + { + const unsigned char *_Src_ptr_slice_start = reinterpret_cast<const unsigned char *>(_Src); + _Output_iterator _Dst_depth_slice_start = _Dst; + for (size_t _I = 0; _I < _Depth; _I++) + { + _Output_iterator _Dst_iter = _Dst_depth_slice_start; + const unsigned char *_Src_ptr = _Src_ptr_slice_start; + + for (size_t _J = 0; _J < _Height; _J++) + { + const _Value_type * _Src_end = reinterpret_cast<const _Value_type *>(_Src_ptr) + _Width; + + std::copy(stdext::make_unchecked_array_iterator(reinterpret_cast<const _Value_type*>(_Src_ptr)), stdext::make_unchecked_array_iterator(_Src_end), _Dst_iter); + + std::advance(_Dst_iter, _Dst_row_pitch); + _Src_ptr += _Src_row_pitch_in_bytes; + } + + _Src_ptr_slice_start += _Src_depth_pitch_in_bytes; + std::advance(_Dst_depth_slice_start, _Dst_depth_pitch); + } + } + break; + default: + _ASSERTE(FALSE); + break; + } + } + + _AMPIMP size_t __cdecl _Get_preferred_copy_chunk_size(size_t _Total_copy_size_in_bytes); + + inline size_t _Get_preferred_copy_chunk_num_elems(size_t _Total_num_elems, size_t _Elem_size) + { + size_t preferredChunkSize = _Get_preferred_copy_chunk_size(_Total_num_elems * _Elem_size); + + return (preferredChunkSize / _Elem_size); + } + + inline void _Get_preferred_copy_chunk_extent(unsigned int _Rank, size_t _Width, size_t _Height, + size_t _Depth, size_t _Bits_per_element, _Out_writes_(3) size_t *_Preferred_copy_chunk_extent) + { + _ASSERTE(_Preferred_copy_chunk_extent != nullptr); + + size_t requestedByteSize = static_cast<size_t>((static_cast<unsigned long long>(_Width) * + static_cast<unsigned long long>(_Height) * + static_cast<unsigned long long>(_Depth) * + static_cast<unsigned long long>(_Bits_per_element)) >> 3); + + size_t preferredChunkSize = _Get_preferred_copy_chunk_size(requestedByteSize); + + // Lets align the allocation size to the element size of the texture + size_t preferredCopyChunkNumElems = static_cast<size_t>((static_cast<unsigned long long>(preferredChunkSize) * 8U) / _Bits_per_element); + + // Lets truncate the dimensions of the requested staging texture. + // We only truncate in the most significant dimension + switch (_Rank) + { + case 1: + _Width = preferredCopyChunkNumElems; + break; + case 2: + _Height = (preferredCopyChunkNumElems + _Width - 1) / _Width; + break; + case 3: + _Depth = (preferredCopyChunkNumElems + (_Height * _Width) - 1) / (_Height * _Width); + break; + default: + _ASSERTE(false); + } + + _Preferred_copy_chunk_extent[0] = _Width; + _Preferred_copy_chunk_extent[1] = _Height; + _Preferred_copy_chunk_extent[2] = _Depth; + } + + // Finds the greatest common divisor of 2 unsigned integral numbers using Euclid's algorithm + template <typename _T> + inline _T _Greatest_common_divisor(_T _M, _T _N) + { + static_assert(std::is_unsigned<_T>::value, "This GCD function only supports unsigned integral types"); + + _ASSERTE((_M > 0) && (_N > 0)); + + if (_N > _M) { + std::swap(_N , _M); + } + + _T _Temp; + while (_N > 0) + { + _Temp = _N; + _N = _M % _N; + _M = _Temp; + } + + return _M; + } + + // Finds the least common multiple of 2 unsigned integral numbers using their greatest_common_divisor + template <typename _T> + inline _T _Least_common_multiple(_T _M, _T _N) + { + static_assert(std::is_unsigned<_T>::value, "This LCM function only supports unsigned integral types"); + + _ASSERTE((_M > 0) && (_N > 0)); + + _T _Gcd = _Greatest_common_divisor(_M, _N); + return ((_M / _Gcd) * _N); + } + + template <typename InputIterator, typename _Value_type> + inline _Event _Copy_impl(InputIterator _SrcFirst, InputIterator _SrcLast, size_t _NumElemsToCopy, + _Out_ _Buffer * _Dst, size_t _Dest_offset, size_t _Preferred_copy_chunk_num_elems = 0) + { + if (_NumElemsToCopy == 0) { + return _Event(); + } + + if (_Dst == NULL) { + throw runtime_exception("Failed to copy to buffer.", E_INVALIDARG); + } + +#pragma warning ( push ) +#pragma warning ( disable : 6001 ) // Using uninitialized memory '*_Dst' + if (((_NumElemsToCopy * sizeof(_Value_type)) + (_Dest_offset * _Dst->_Get_elem_size())) > (_Dst->_Get_num_elems() * _Dst->_Get_elem_size())) + { + throw runtime_exception("Invalid _Src argument(s). _Src size exceeds total size of the _Dest.", E_INVALIDARG); + } +#pragma warning ( pop ) + + _ASSERTE(_NumElemsToCopy == (size_t)(std::distance(_SrcFirst, _SrcLast))); + + // If the dest is host accessible for write then we do the copy on + // accelerator(accelerator::cpu_accelerator).default_view + if (_Dst->_Is_host_accessible(_Write_access)) + { + // Lets first map the _Dst buffer + _Event _Ev = _Dst->_Map_buffer_async(_Write_access); + + // The _Dest is accessible on host. We just need to do a std::copy using a raw pointer as OutputIterator + _Buffer_ptr _PDestBuf = _Dst; + _Ev = _Ev._Add_continuation(std::function<_Event()>([_PDestBuf,_Dest_offset, _SrcFirst, _SrcLast]() mutable -> _Event + { + _Value_type *_DestPtr = reinterpret_cast<_Value_type*>(reinterpret_cast<char*>(_PDestBuf->_Get_host_ptr()) + (_Dest_offset * _PDestBuf->_Get_elem_size())); + std::copy(_SrcFirst, _SrcLast, stdext::make_unchecked_array_iterator(_DestPtr)); + + return _Event(); + })); + + return _Ev; + } + else + { + // _Dest is on a device. Lets create a temp staging buffer on the _Dest accelerator_view and copy the input over + // We may create a staging buffer of size smaller than the copy size and in that case we will perform the copy + // as a series of smaller copies + _Buffer_ptr _PDestBuf = _Dst; + size_t _NumElemsToCopyRemaining = _NumElemsToCopy; + size_t _PreferredNumElemsToCopyPerChunk = _Preferred_copy_chunk_num_elems; + if (_PreferredNumElemsToCopyPerChunk == 0) { + // If a preferred copy chunk size was not specified, lets pick one based on the + // size of the copy + _PreferredNumElemsToCopyPerChunk = _Get_preferred_copy_chunk_num_elems(_NumElemsToCopy, sizeof(_Value_type)); + } + size_t _CurrDstOffset = _Dest_offset; + InputIterator _CurrStartIter = _SrcFirst; + _Event _Ev; + + size_t _Lcm = _Least_common_multiple(_Dst->_Get_elem_size(), sizeof(_Value_type)); + size_t _AdjustmentRatio = _Lcm / sizeof(_Value_type); + + do + { + size_t _AllocationNumElems = _PreferredNumElemsToCopyPerChunk; + if (_NumElemsToCopyRemaining < _AllocationNumElems) { + _AllocationNumElems = _NumElemsToCopyRemaining; + } + + _Buffer_ptr _PDestStagingBuf = _Buffer::_Get_temp_staging_buffer(_Dst->_Get_accelerator_view(), + _AllocationNumElems, sizeof(_Value_type)); + + _ASSERTE(_PDestStagingBuf != NULL); + _ASSERTE(_PDestStagingBuf->_Get_elem_size() == sizeof(_Value_type)); + + InputIterator _CurrEndIter = _CurrStartIter; + size_t _CurrNumElemsToCopy = _AllocationNumElems; + if (_CurrNumElemsToCopy > _PDestStagingBuf->_Get_num_elems()) { + _CurrNumElemsToCopy = _PDestStagingBuf->_Get_num_elems(); + } + + if (_NumElemsToCopyRemaining <= _CurrNumElemsToCopy) { + _CurrNumElemsToCopy = _NumElemsToCopyRemaining; + _CurrEndIter = _SrcLast; + } + else + { + // We need to adjust the _CurrNumElemsToCopy to be a multiple of the + // least common multiple of the destination buffer's element size and sizeof(_Value_type). + _CurrNumElemsToCopy = (_CurrNumElemsToCopy / _AdjustmentRatio) * _AdjustmentRatio; + std::advance(_CurrEndIter, _CurrNumElemsToCopy); + } + + _ASSERTE((_CurrNumElemsToCopy % _AdjustmentRatio) == 0); + + // This would not actually never block since we just created this staging buffer or are using + // a cached one that is not in use + _PDestStagingBuf->_Map_buffer(_Write_access, true /* _Wait */); + + // Copy from input to the staging using a raw pointer as OutputIterator + std::copy(_CurrStartIter, _CurrEndIter, stdext::make_unchecked_array_iterator(reinterpret_cast<_Value_type*>(_PDestStagingBuf->_Get_host_ptr()))); + + _Ev = _Ev._Add_event(_PDestStagingBuf->_Copy_to_async(_PDestBuf, _CurrNumElemsToCopy, 0, _CurrDstOffset)); + + // Adjust the iterators and offsets + _NumElemsToCopyRemaining -= _CurrNumElemsToCopy; + _CurrDstOffset += (_CurrNumElemsToCopy * sizeof(_Value_type)) / _Dst->_Get_elem_size(); + _CurrStartIter = _CurrEndIter; + + } while (_NumElemsToCopyRemaining != 0); + + return _Ev; + } + } + + // The std::advance method is only supported for InputIterators and hence we have a custom implementation + // which forwards to the std::advance if the iterator is an input iterator and uses a loop based advance + // implementation otherwise + template<typename _InputIterator, typename _Distance> + typename std::enable_if<std::is_base_of<std::input_iterator_tag, typename std::iterator_traits<_InputIterator>::iterator_category>::value>::type + _Advance_output_iterator(_InputIterator &_Iter, _Distance _N) + { + std::advance(_Iter, _N); + } + + template<typename _OutputIterator, typename _Distance> + typename std::enable_if<!std::is_base_of<std::input_iterator_tag, typename std::iterator_traits<_OutputIterator>::iterator_category>::value>::type + _Advance_output_iterator(_OutputIterator &_Iter, size_t _N) + { + for (size_t i = 0; i < _N; ++i) + { + _Iter++; + } + } + + template <typename OutputIterator, typename _Value_type> + inline _Event _Copy_impl(_In_ _Buffer *_Src, size_t _Src_offset, size_t _Num_elems, + OutputIterator _DestIter, size_t _Preferred_copy_chunk_num_elems = 0) + { + if ((_Src == NULL) || ((_Src_offset + _Num_elems) > _Src->_Get_num_elems())) { + throw runtime_exception("Failed to copy to buffer.", E_INVALIDARG); + } + + if (_Num_elems == 0) { + return _Event(); + } + + size_t _NumElemsToCopy = (_Num_elems * _Src->_Get_elem_size()) / sizeof(_Value_type); + + // If the src is host accessible for readthen we do the copy on + // accelerator(accelerator::cpu_accelerator).default_view + if (_Src->_Is_host_accessible(_Read_access)) + { + // Map the _Src buffer + _Event _Ev = _Src->_Map_buffer_async(_Read_access); + + // The _Src is accessible on host. We just need to do a std::copy using a raw pointer as OutputIterator + _Buffer_ptr _PSrcBuf = _Src; + _Ev = _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _Src_offset, _DestIter, _NumElemsToCopy]() mutable -> _Event + { + // The _Src is accessible on host. We just need to do a std::copy + const _Value_type *_PFirst = reinterpret_cast<const _Value_type*>(reinterpret_cast<char*>(_PSrcBuf->_Get_host_ptr()) + (_Src_offset * _PSrcBuf->_Get_elem_size())); + std::copy(_PFirst, _PFirst + _NumElemsToCopy, _DestIter); + + return _Event(); + })); + + return _Ev; + } + else + { + // The _Src is on the device. We need to copy it out to a temporary staging array + // We may create a staging buffer of size smaller than the copy size and in that case we will + // perform the copy as a series of smaller copies + + _Event _Ev; + + _Buffer_ptr _PSrcBuf = _Src; + size_t _PreferredNumElemsToCopyPerChunk = _Preferred_copy_chunk_num_elems; + if (_PreferredNumElemsToCopyPerChunk == 0) { + // If a preferred copy chunk size was not specified, lets pick one based on the + // size of the copy + _PreferredNumElemsToCopyPerChunk = _Get_preferred_copy_chunk_num_elems(_NumElemsToCopy, sizeof(_Value_type)); + } + + size_t _AllocationNumElems = _PreferredNumElemsToCopyPerChunk; + if (_NumElemsToCopy < _AllocationNumElems) { + _AllocationNumElems = _NumElemsToCopy; + } + + _Buffer_ptr _PSrcStagingBuf = _Buffer::_Get_temp_staging_buffer(_Src->_Get_accelerator_view(), + _AllocationNumElems, sizeof(_Value_type)); + + _ASSERTE(_PSrcStagingBuf != NULL); + _ASSERTE(_PSrcStagingBuf->_Get_elem_size() == sizeof(_Value_type)); + + // The total byte size of a copy chunk must be an integral multiple of both the + // source buffer's element size and sizeof(_Value_type). + size_t _Lcm = _Least_common_multiple(_Src->_Get_elem_size(), sizeof(_Value_type)); + size_t _AdjustmentRatio = _Lcm / sizeof(_Value_type); + + size_t _CurrNumElemsToCopy = _AllocationNumElems; + if (_CurrNumElemsToCopy > _PSrcStagingBuf->_Get_num_elems()) { + _CurrNumElemsToCopy = _PSrcStagingBuf->_Get_num_elems(); + } + if (_NumElemsToCopy <= _CurrNumElemsToCopy) + { + _CurrNumElemsToCopy = _NumElemsToCopy; + } + else + { + // We need to adjust the _StagingBufNumElems to be a multiple of the + // least common multiple of the source buffer's element size and sizeof(_Value_type). + _CurrNumElemsToCopy = (_CurrNumElemsToCopy / _AdjustmentRatio) * _AdjustmentRatio; + } + + _ASSERTE((_CurrNumElemsToCopy % _AdjustmentRatio) == 0); + + size_t _NumElemsToCopyRemaining = _NumElemsToCopy - _CurrNumElemsToCopy; + + _Ev = _PSrcBuf->_Copy_to_async(_PSrcStagingBuf, (_CurrNumElemsToCopy * sizeof(_Value_type)) / _PSrcBuf->_Get_elem_size(), _Src_offset, 0); + + if (_NumElemsToCopyRemaining != 0) + { + _Ev = _Ev._Add_continuation(std::function<_Event()>([_DestIter, _PSrcBuf, _PSrcStagingBuf, + _CurrNumElemsToCopy, _NumElemsToCopyRemaining, + _Src_offset, _PreferredNumElemsToCopyPerChunk]() mutable -> _Event + { + // Initiate an asynchronous copy of the remaining part so that this part of the copy + // makes progress while we consummate the copying of the first part + size_t _CurrSrcOffset = _Src_offset + ((_CurrNumElemsToCopy * sizeof(_Value_type)) / _PSrcBuf->_Get_elem_size()); + OutputIterator _CurrDestIter = _DestIter; + _Advance_output_iterator<decltype(_CurrDestIter), size_t>(_CurrDestIter, _CurrNumElemsToCopy); + _Event _Ret_ev = _Copy_impl<OutputIterator, _Value_type>(_PSrcBuf._Get_ptr(), _CurrSrcOffset, + (_NumElemsToCopyRemaining * sizeof(_Value_type)) / _PSrcBuf->_Get_elem_size(), + _CurrDestIter, _PreferredNumElemsToCopyPerChunk); + + // Now copy the data from staging buffer to the destination + _Value_type *_PFirst = reinterpret_cast<_Value_type*>(_PSrcStagingBuf->_Get_host_ptr()); + std::copy(_PFirst, _PFirst + _CurrNumElemsToCopy, _DestIter); + return _Ret_ev; + })); + } + else + { + _Ev = _Ev._Add_continuation(std::function<_Event()>([_DestIter, _PSrcStagingBuf, _CurrNumElemsToCopy]() mutable -> _Event + { + _Value_type *_PFirst = reinterpret_cast<_Value_type*>(_PSrcStagingBuf->_Get_host_ptr()); + std::copy(_PFirst, _PFirst + _CurrNumElemsToCopy, _DestIter); + return _Event(); + })); + } + + return _Ev; + } + } + + // Structured copy between buffers across AVs + _AMPIMP _Event __cdecl _Copy_impl(_In_ _Buffer *_Src, _View_shape_ptr _Src_shape, _Out_ _Buffer * _Dst, _View_shape_ptr _Dst_shape); + + struct _Array_copy_desc + { + _Array_copy_desc( + const unsigned int _Rank, + const unsigned int _Src_linear_offset, + const unsigned int * _Src_extents, + const unsigned int * _Src_copy_offset, + const unsigned int _Dst_linear_offset, + const unsigned int * _Dst_extents, + const unsigned int * _Dst_copy_offset, + const unsigned int * _Copy_extents) + { + this->_Rank = _Rank; + + this->_Src_linear_offset = _Src_linear_offset; + this->_Src_extents.assign( _Src_extents, _Src_extents + _Rank); + this->_Src_copy_offset.assign( _Src_copy_offset, _Src_copy_offset + _Rank); + + this->_Dst_linear_offset = _Dst_linear_offset; + this->_Dst_extents.assign( _Dst_extents, _Dst_extents + _Rank); + this->_Dst_copy_offset.assign( _Dst_copy_offset, _Dst_copy_offset + _Rank); + + this->_Copy_extents.assign( _Copy_extents, _Copy_extents + _Rank); + } + + _Array_copy_desc() {} + + unsigned int _Rank; + + // Shape of source + unsigned int _Src_linear_offset; + std::vector<unsigned int> _Src_extents; + std::vector<unsigned int> _Src_copy_offset; + + // Shape of destination + unsigned int _Dst_linear_offset; + std::vector<unsigned int> _Dst_extents; + std::vector<unsigned int> _Dst_copy_offset; + + // Shape of copy region + std::vector<unsigned int> _Copy_extents; + }; + + // Declaration + _AMPIMP HRESULT __cdecl _Recursive_array_copy(const _Array_copy_desc& _Desc, + unsigned int _Native_copy_rank, + std::function<HRESULT(const _Array_copy_desc &_Reduced)> _Native_copy_func); + + _AMPIMP std::pair<accelerator_view, accelerator_view> __cdecl _Get_src_dest_accelerator_view(_In_opt_ const _Buffer_descriptor *_SrcBuffDescPtr, + _In_opt_ const _Buffer_descriptor *_DestBuffDescPtr); + + // Iterator based copy function + template<typename _InputInterator, typename _OutputIterator> + inline _Event _Copy_impl_iter(_InputInterator _SrcFirst, _InputInterator _SrcLast, _OutputIterator _DstFirst) + { + std::copy(_SrcFirst, _SrcLast, _DstFirst); + return _Event(); + } + + // Iterator based copy function + template <typename InputIterator, typename _Value_type> + inline _Event _Copy_impl(InputIterator _SrcFirst, _View_shape_ptr _Src_shape, _Inout_ _Buffer * _Dst, _View_shape_ptr _Dst_shape) + { + _ASSERTE(_Dst != NULL); + _ASSERTE(_Src_shape != NULL); + _ASSERTE(_Dst_shape != NULL); + + if (_Src_shape->_Is_projection()) { + _Src_shape = _Src_shape->_Get_reduced_shape_for_copy(); + } + + if (_Dst_shape->_Is_projection()) { + _Dst_shape = _Dst_shape->_Get_reduced_shape_for_copy(); + } + + _ASSERTE(_Src_shape->_Get_rank() == _Dst_shape->_Get_rank()); + + _ASSERTE(_View_shape::_Compare_extent_with_elem_size(_Src_shape->_Get_rank(), _Src_shape->_Get_view_extent(), + sizeof(_Value_type), _Dst_shape->_Get_view_extent(), _Dst->_Get_elem_size())); + + if (_Dst->_Is_host_accessible(_Write_access)) + { + // The destination buffer is accesible on the host. Map the _Dst buffer + _Event _Ev = _Dst->_Map_buffer_async(_Write_access); + _Buffer_ptr _PDestBuf = _Dst; + return _Ev._Add_continuation(std::function<_Event()>([_SrcFirst, _Src_shape, _PDestBuf, _Dst_shape]() mutable -> _Event { + return _Copy_impl_iter(_SrcFirst, _Src_shape, stdext::make_unchecked_array_iterator(reinterpret_cast<_Value_type*>(_PDestBuf->_Get_host_ptr())), + _Create_reinterpreted_shape(_Dst_shape, _PDestBuf->_Get_elem_size(), sizeof(_Value_type))); + })); + } + else + { + // The dest buffer is not accesible on host. Lets create a temporary + // staging buffer on the destination buffer's accelerator_view + _Buffer_ptr _PTempStagingBuf = _Buffer::_Create_stage_buffer(_Dst->_Get_accelerator_view(), accelerator(accelerator::cpu_accelerator).default_view, + _Src_shape->_Get_view_size(), sizeof(_Value_type), true /* _Is_temp */); + + _PTempStagingBuf->_Map_buffer(_Write_access, true /* _Wait */); + _Value_type *_Dst_ptr = reinterpret_cast<_Value_type*>(_PTempStagingBuf->_Get_host_ptr()); + _Event _Ev = _Copy_impl_iter(_SrcFirst, _Src_shape, stdext::make_unchecked_array_iterator(_Dst_ptr), _Src_shape); + + // Now copy from the staging buffer to the destination buffer + _Buffer_ptr _PDestBuf = _Dst; + return _Ev._Add_continuation(std::function<_Event()>([_PTempStagingBuf, _Src_shape, _PDestBuf, _Dst_shape]() mutable -> _Event { + return _Copy_impl(_PTempStagingBuf, _Src_shape, _PDestBuf, _Dst_shape); + })); + } + } + + template <typename OutputIterator, typename _Value_type> + inline _Event _Copy_impl(_In_ _Buffer *_Src, _View_shape_ptr _Src_shape, OutputIterator _DestIter, _View_shape_ptr _Dst_shape) + { + _ASSERTE(_Src != NULL); + _ASSERTE(_Src_shape != NULL); + _ASSERTE(_Dst_shape != NULL); + + if (_Src_shape->_Is_projection()) { + _Src_shape = _Src_shape->_Get_reduced_shape_for_copy(); + } + + if (_Dst_shape->_Is_projection()) { + _Dst_shape = _Dst_shape->_Get_reduced_shape_for_copy(); + } + + _ASSERTE(_Src_shape->_Get_rank() == _Dst_shape->_Get_rank()); + + _ASSERTE(_View_shape::_Compare_extent_with_elem_size(_Src_shape->_Get_rank(), _Src_shape->_Get_view_extent(), + _Src->_Get_elem_size(), _Dst_shape->_Get_view_extent(), sizeof(_Value_type))); + + if (_Src->_Is_host_accessible(_Read_access)) + { + // The source buffer is accessible on the host. Map the _Src buffer + _Event _Ev = _Src->_Map_buffer_async(_Read_access); + + _Buffer_ptr _PSrcBuf = _Src; + return _Ev._Add_continuation(std::function<_Event()>([_PSrcBuf, _Src_shape, _DestIter, _Dst_shape]() mutable -> _Event { + return _Copy_impl_iter(reinterpret_cast<_Value_type*>(_PSrcBuf->_Get_host_ptr()), + _Create_reinterpreted_shape(_Src_shape, _PSrcBuf->_Get_elem_size(), sizeof(_Value_type)), + _DestIter, _Dst_shape); + })); + } + else + { + // The source buffer is not accessible on host. Lets create a temporary + // staging buffer on the source buffer's accelerator_view and initiate a copy + // from the source buffer to the temporary staging buffer + _Buffer_ptr _PTempStagingBuf = _Buffer::_Create_stage_buffer(_Src->_Get_accelerator_view(), accelerator(accelerator::cpu_accelerator).default_view, + _Dst_shape->_Get_view_size(), sizeof(_Value_type), true); + + _Event _Ev = _Src->_Copy_to_async(_PTempStagingBuf, _Src_shape, _Dst_shape); + return _Ev._Add_continuation(std::function<_Event()>([_PTempStagingBuf, _Dst_shape, _DestIter]() mutable -> _Event { + return _Copy_impl_iter(reinterpret_cast<_Value_type*>(_PTempStagingBuf->_Get_host_ptr()), + _Dst_shape, _DestIter, _Dst_shape); + })); + } + } + + // Iterator based structured copy function + template<typename _InputInterator, typename _OutputIterator> + inline _Event _Copy_impl_iter(_InputInterator _SrcIter, _View_shape_ptr _Src_shape, + _OutputIterator _DstIter, _View_shape_ptr _Dst_shape) + { + if (_Src_shape->_Is_projection()) { + _Src_shape = _Src_shape->_Get_reduced_shape_for_copy(); + } + + if (_Dst_shape->_Is_projection()) { + _Dst_shape = _Dst_shape->_Get_reduced_shape_for_copy(); + } + + _ASSERTE(_Src_shape->_Get_rank() == _Dst_shape->_Get_rank()); + _ASSERTE(_View_shape::_Compare_extent(_Src_shape->_Get_rank(), _Src_shape->_Get_view_extent(), _Dst_shape->_Get_view_extent())); + + // If both the _Src_shape and _Dst_shape are linear we can be more efficient + unsigned int _Src_linear_offset, _Src_linear_size, _Dst_linear_offset, _Dst_linear_size; + if (_Src_shape->_Is_view_linear(_Src_linear_offset, _Src_linear_size) && + _Dst_shape->_Is_view_linear(_Dst_linear_offset, _Dst_linear_size)) + { + _ASSERTE(_Src_linear_size == _Dst_linear_size); + + // These iterators might be not contiguous, therefore we use std::advance + std::advance(_SrcIter, _Src_linear_offset); + auto _SrcLast = _SrcIter; + std::advance(_SrcLast, _Src_linear_size); + std::advance(_DstIter, _Dst_linear_offset); + + return _Copy_impl_iter(_SrcIter, _SrcLast, _DstIter); + } + + std::vector<unsigned int> _Src_extent(_Src_shape->_Get_rank()); + std::vector<unsigned int> _Src_offset(_Src_shape->_Get_rank()); + std::vector<unsigned int> _Dst_extent(_Dst_shape->_Get_rank()); + std::vector<unsigned int> _Dst_offset(_Dst_shape->_Get_rank()); + std::vector<unsigned int> _Copy_extent(_Src_shape->_Get_rank()); + + for (size_t i = 0; i < _Src_shape->_Get_rank(); ++i) { + _Src_extent[i] = _Src_shape->_Get_base_extent()[i]; + _Src_offset[i] = _Src_shape->_Get_view_offset()[i]; + _Dst_extent[i] = _Dst_shape->_Get_base_extent()[i]; + _Dst_offset[i] = _Dst_shape->_Get_view_offset()[i]; + _Copy_extent[i] = _Src_shape->_Get_view_extent()[i]; + } + + _Array_copy_desc _Desc( + _Src_shape->_Get_rank(), + _Src_shape->_Get_linear_offset(), + _Src_extent.data(), + _Src_offset.data(), + _Dst_shape->_Get_linear_offset(), + _Dst_extent.data(), + _Dst_offset.data(), + _Copy_extent.data()); + + // Note: Capturing shape pointers would be incorrect, they are valid for setting up the call. + // They might be deleted right after this call completes. + HRESULT hr = _Recursive_array_copy(_Desc, 1, [_SrcIter, _DstIter](const _Array_copy_desc &_Reduced) -> HRESULT { + + auto _SrcFirst = _SrcIter; + auto _DstFirst = _DstIter; + + std::advance(_DstFirst, _Reduced._Dst_linear_offset + _Reduced._Dst_copy_offset[0]); + std::advance(_SrcFirst, _Reduced._Src_linear_offset + _Reduced._Src_copy_offset[0]); + auto _SrcLast = _SrcFirst; + std::advance(_SrcLast, _Reduced._Copy_extents[0]); + + std::copy(_SrcFirst, _SrcLast, _DstFirst); + + return S_OK; + }); + + if (FAILED(hr)) { + throw Concurrency::runtime_exception("Failed to copy between buffers", E_FAIL); + } + + return _Event(); + } + + // A ubiquitous buffer that provides access to the underlying data + // on any accelerator_view + class _Ubiquitous_buffer : public _Reference_counter + { + friend _Event _Get_access_async(const _View_key _Key, accelerator_view _Av, _Access_mode _Mode, _Buffer_ptr &_Buf_ptr); + friend _AMPIMP accelerator_view __cdecl _Select_copy_src_accelerator_view(_In_ _View_key _Src_view_key, const accelerator_view &_Dest_accelerator_view); + friend struct _DPC_call_handle; + + public: + + _AMPIMP static _Ret_ _Ubiquitous_buffer * __cdecl _Create_ubiquitous_buffer(size_t _Num_elems, size_t _Elem_size); + + _AMPIMP static _Ret_ _Ubiquitous_buffer * __cdecl _Create_ubiquitous_buffer(_Buffer_ptr _Master_buffer); + + // Register a new view on top of this _Ubiquitous_buffer + _AMPIMP void _Register_view(_In_ _View_key _Key, accelerator_view _Cpu_av, _View_shape_ptr _Shape); + + // Register a copy of an existing view registered with this _Ubiquitous_buffer + _AMPIMP void _Register_view_copy(_In_ _View_key _New_view_key, _In_ _View_key _Existing_view_key); + + // Unregister a view currently registered with this _Ubiquitous_buffer + _AMPIMP void _Unregister_view(_In_ _View_key _Key); + + // Obtain a specified mode of access to the specified view on the specified target + // accelerator_view. This method also serves the purpose of determining the + // amount of data copy expected to happen as part of this _Get_access request + // without actually performing the copies or state updates in the _Ubiquitous_buffer. This + // is used for reporting the implicit data copies that happen when accessing array_views + // in C++ AMP ETW events + _AMPIMP _Event _Get_access_async(_In_ _View_key _Key, _Accelerator_view_impl_ptr _Av_view_impl_ptr, + _Access_mode _Mode, _Buffer_ptr &_Buf_ptr, + _Inout_opt_ ULONGLONG *_Sync_size = nullptr); + + // Discard the content underlying this view + _AMPIMP void _Discard(_In_ _View_key _Key); + + // This method does not synchonize the copies. Should not be used for getting + // data access but only to get the underlying buffer's properties + _AMPIMP _Buffer_ptr _Get_master_buffer() const; + + _AMPIMP accelerator_view _Get_master_accelerator_view() const; + + _AMPIMP _View_shape_ptr _Get_view_shape(_In_ _View_key _Key); + + _Ret_ _Accelerator_view_impl* _Get_master_accelerator_view_impl() const + { + return _M_master_av; + } + + size_t _Get_master_buffer_elem_size() const + { + return _M_master_buffer_elem_size; + } + + size_t _Get_master_buffer_num_elems() const + { + return _M_master_buffer_num_elems; + } + + bool _Has_data_source() const + { + return _M_has_data_source; + } + + private: + + // The _Ubiquitous_buffer constructors are private to force construction through the static + // _Create_ubiquitous_buffer method to ensure the object is allocated in the runtime + _Ubiquitous_buffer(size_t _Num_elems, size_t _Elem_size); + _Ubiquitous_buffer(_In_ _Buffer* _Master_buffer); + + // Private destructor to force deletion through _Release + ~_Ubiquitous_buffer(); + + // No default consturctor, copy constructor and assignment operator + _Ubiquitous_buffer(); + _Ubiquitous_buffer(const _Ubiquitous_buffer &rhs); + _Ubiquitous_buffer &operator=(const _Ubiquitous_buffer &rhs); + + // Helper methods + + // Get access to a buffer on a specified accelerator for a specified pre-registered view. + // If _Sync_size parameter is not null, then function calculates number of bytes that we + // need to synchronize to get desired access. + _AMPIMP _Event _Get_access_async(_In_ _View_key _Key, accelerator_view _Av, _Access_mode _Mode, + _Buffer_ptr &_Buf_ptr, _Inout_opt_ ULONGLONG *_Sync_size = NULL); + + // Commit a view to the master buffer if needed. When the _Sync_size parameter is non-null + // this method just returns the amount of data to be copied as part of the commit, without + // actually performing the commit + _Event _Commit_view_async(_In_ _View_info *_Info, _Inout_ ULONGLONG *_Sync_size = nullptr); + + // Get the _Buffer_ptr corresponding to a specified accelerator_view. When the + // _Create parameter is true, it creates a new _Buffer if one does not already exist + // for that accelerator_view + _Ret_ _Buffer* _Get_buffer(_In_ _Accelerator_view_impl* _Av, bool _Create = true); + + // Sets a new access mode for the specified view + void _Set_new_access_mode(_Inout_ _View_info *_Info, _Access_mode _New_mode); + + // Unsets the discard flag from the specified view and all other + // overlapping views + void _Unset_discard_flag(_Inout_ _View_info *_Info); + + // Determines whether the data underlying the specified view has been discarded + // based on whether a subsuming view has the discard flag set. + bool _Should_discard(const _View_info *_Info) const; + + // Does this view have exclusive data which is not discarded, + // not on the master accelerator_view and also there is not other view + // that subsumes this view and is marked dirty + bool _Has_exclusive_data(const _View_info *_Info) const; + + // Based on the current state of overlapping views in the _Ubiquitous_buffer + // does the specified view require a data update on the target accelerator_view + // to fulfil an access request + bool _Requires_update_on_target_accelerator_view(const _View_info *_Info, + _Access_mode _Requested_mode, + _In_ _Accelerator_view_impl* _Target_acclerator_view) const; + + // This method iterates over all views in the specified commit list + // and flags them as "commit not needed" if that view is subsumed by another view present in the + // commit list + static void _Flag_redundant_commits(std::vector<std::pair<_View_info*, bool>> &_Commit_list); + + // This method returns the list of accelerator_views where the specified view already has + // a valid cached copy of the data and getting read access would not incur any data movement. + // The _Can_access_anywhere parameter is an output parameter used to indicate to the + // caller that the specified view can be accessed on any accelerator_view without incurring + // any data movement. This is true when there are no modified overlapping views that require + // synchronization and the specified view has the discard_data flag set. + // This method is used for determining the source accelerator_view for copy and p_f_e operations + // involving array_views + _Accelerator_view_unordered_set _Get_caching_info(_In_ _View_key _Key, _Out_opt_ bool *_Can_access_anywhere = NULL); + + _Accelerator_view_unordered_set _Get_caching_info_impl(_In_ _View_key _Key, _Out_opt_ bool *_Can_access_anywhere); + + _Ret_ _Accelerator_view_impl* _Determine_alternate_target_accelerator_view(_In_ _View_key _Key, + _In_ _Accelerator_view_impl* _Original_av, + _Access_mode _Mode); + + private: + + // Private data + + // The master accelerator_view for this _Ubiquitous_buffer + // which is specified at construction time + _Accelerator_view_impl_ptr _M_master_av; + + // The master _Buffer corresponding to this _Ubiquitous_buffer + // which is specified at construction time + _Buffer* _M_master_buffer; + + // The size of each element of the master buffer + size_t _M_master_buffer_elem_size; + + // The number of elements in the master buffer + size_t _M_master_buffer_num_elems; + + // Indicates if this ubiquitous buffer has an underlying data source + bool _M_has_data_source; + + // A map of pre-created _Buffers corresponding to different + // accelerator_views where the _Ubiquitous_buffer has already been + // accessed + std::map<_Accelerator_view_impl_ptr, _Buffer_ptr> _M_buffer_map; + + // A mapping between all registered view keys in this _Ubiquitous_buffer + // to their corresponding _View_info + std::unordered_map<_View_key, _View_info*> _M_view_map; + + // Set of distinct views of this buffer. As multiple copies of the same + // view may have been registered for this _Ubiquitous_buffer, this set + // maintains the set of distinct views which really matter for the + // caching protocol. Also, note that some view_info may not have any live registered + // and hence does not exist in the _M_view_map but may exist here since + // it has uncomiitted data which needs to be considered as part of the cache + // coherence protocol to prevent modifications underlying this view from being lost + std::unordered_set<_View_info*> _M_view_info_set; + + // Critical section object to protect the cache directory + Concurrency::critical_section _M_critical_section; + }; + + // Class defines functions for interoperability with D3D + class _D3D_interop + { + public: + _AMPIMP static _Ret_ IUnknown * __cdecl _Get_D3D_buffer(_In_ _Buffer *_Buffer_ptr); + _AMPIMP static _Ret_ IUnknown * __cdecl _Get_D3D_texture(_In_ _Texture *_Texture_ptr); + _AMPIMP static _Ret_ void * __cdecl _Get_D3D_sampler_data_ptr(_In_ IUnknown *_D3D_sampler); + _AMPIMP static void __cdecl _Release_D3D_sampler_data_ptr(_In_ void *_Sampler_data_ptr); + _AMPIMP static _Ret_ IUnknown * __cdecl _Get_D3D_sampler(const Concurrency::accelerator_view &_Av, _In_ _Sampler *_Sampler_ptr); + }; + + inline + _Event _Get_access_async(const _View_key _Key, accelerator_view _Av, _Access_mode _Mode, _Buffer_ptr &_Buf_ptr) + { + return _Key->_Get_buffer_ptr()->_Get_access_async(_Key->_Get_view_key(), _Av, _Mode, _Buf_ptr); + } + + inline + _Ret_ _View_shape* _Get_buffer_view_shape(const _Buffer_descriptor& _Descriptor) + { + return _Descriptor._Get_buffer_ptr()->_Get_view_shape(_Descriptor._Get_view_key()); + } + + inline + bool _Is_cpu_accelerator(const accelerator& _Accl) + { + return (_Accl.device_path == accelerator::cpu_accelerator); + } + +} // namespace Concurrency::details + +/// <summary> +/// Exception thrown when an underlying DirectX call fails +/// due to the Windows timeout detection and recovery mechanism +/// </summary> +class accelerator_view_removed : public runtime_exception +{ +public: + /// <summary> + /// Construct an accelerator_view_removed exception with a message and + /// a view removed reason code + /// </summary> + /// <param name="_Message"> + /// Descriptive message of error + /// </param> + /// <param name="_View_removed_reason"> + /// HRESULT error code indicating the cause of removal of the accelerator_view + /// </param> + _AMPIMP explicit accelerator_view_removed(const char * _Message, HRESULT _View_removed_reason) throw(); + + /// <summary> + /// Construct an accelerator_view_removed exception + /// </summary> + /// <param name="_View_removed_reason"> + /// HRESULT error code indicating the cause of removal of the accelerator_view + /// </param> + _AMPIMP explicit accelerator_view_removed(HRESULT _View_removed_reason) throw(); + + /// <summary> + /// Returns an HRESULT error code indicating the cause of the accelerator_view's removal + /// </summary> + /// <returns> + /// The HRESULT error code that indicates the cause of accelerator_view's removal + /// </returns> + _AMPIMP HRESULT get_view_removed_reason() const throw(); + +private: + + HRESULT _M_view_removed_reason_code; +}; // class accelerator_view_removed + +/// <summary> +/// Exception thrown when the runtime fails to launch a kernel +/// using the compute domain specified at the parallel_for_each call site. +/// </summary> +class invalid_compute_domain : public runtime_exception +{ +public: + /// <summary> + /// Construct an invalid_compute_domain exception with a message + /// </summary> + /// <param name="_Message"> + /// Descriptive message of error + /// </param> + _AMPIMP explicit invalid_compute_domain(const char * _Message) throw(); + + /// <summary> + /// Construct an invalid_compute_domain exception + /// </summary> + _AMPIMP invalid_compute_domain() throw(); +}; // class invalid_compute_domain + +/// <summary> +/// Exception thrown when an unsupported feature is used +/// </summary> +class unsupported_feature : public runtime_exception +{ +public: + /// <summary> + /// Construct an unsupported_feature exception with a message + /// </summary> + /// <param name="_Message"> + /// Descriptive message of error + /// </param> + _AMPIMP explicit unsupported_feature(const char * _Message) throw(); + + /// <summary> + /// Construct an unsupported_feature exception + /// </summary> + _AMPIMP unsupported_feature() throw(); +}; // class unsupported_feature + +} // namespace Concurrency + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// Compiler/Runtime Interface +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +#define HELPERAPI __cdecl + +using namespace Concurrency::details; + +extern "C" { + + // This structure is used for storing information about resources required by the kernel. + enum _Resource_kind + { + RESOURCE_BUFFER = 0, + RESOURCE_TEXTURE = 1, + RESOURCE_SAMPLER = 2, + }; + + struct _Device_resource_info + { + _Resource_kind _M_resource_kind; // buffer, texture, or sampler + + void * _M_desc; // Pointer to the _Buffer_descriptor/_Texture_descriptor/_Sampler_descriptor instance + // which underlies all device resource + + _Access_mode _M_formal_access_mode; // scalar: read-only + // const scalar ref: read-only + // scalar ref: ReadWrite + // array: ReadWrite + // const array: ReadOnly + size_t _M_actual_arg_num; + + BOOL _Is_buffer() const + { + return (_M_resource_kind == RESOURCE_BUFFER); + } + + BOOL _Is_texture() const + { + return (_M_resource_kind == RESOURCE_TEXTURE); + } + + BOOL _Is_sampler() const + { + return (_M_resource_kind == RESOURCE_SAMPLER); + } + + _Ret_ _Buffer_descriptor * _Get_buffer_desc() const + { + _ASSERTE(_Is_buffer()); + return reinterpret_cast<_Buffer_descriptor *>(_M_desc); + } + + _Ret_ _Texture_descriptor * _Get_texture_desc() const + { + _ASSERTE(_Is_texture()); + return reinterpret_cast<_Texture_descriptor *>(_M_desc); + } + + _Ret_ _Sampler_descriptor * _Get_sampler_desc() const + { + _ASSERTE(_Is_sampler()); + return reinterpret_cast<_Sampler_descriptor *>(_M_desc); + } + + _Ret_ void * _Get_resource_ptr() const + { + if (_Is_buffer()) + { + _Ubiquitous_buffer * _Tmp = _Get_buffer_desc()->_Get_buffer_ptr(); + return reinterpret_cast<void *>(_Tmp); + } + else if (_Is_texture()) + { + _Texture * _Tmp = _Get_texture_desc()->_Get_texture_ptr(); + return reinterpret_cast<void *>(_Tmp); + } + else + { + _ASSERTE(_Is_sampler()); + _Sampler * _Tmp = _Get_sampler_desc()->_Get_sampler_ptr(); + return reinterpret_cast<void *>(_Tmp); + } + } + }; + + // This structure is used for storing information about the const buffers + struct _Device_const_buffer_info + { + void * _M_data; // Pointer to the host data to intialize the + // constant buffer with + + size_t _M_const_buf_size; // Size of the const buffer in bytes + + unsigned int _M_is_debug_data; // Is this debug data which will be + // intialized by the runtime. 0 (false), 1 (true) + }; +} + +namespace Concurrency +{ +namespace details +{ + enum _DPC_kernel_func_kind + { + NON_ALIASED_SHADER = 0, // slot 0 + ALIASED_SHADER = 1, // slot 1 + NUM_SHADER_VERSIONS = 2 + }; + + struct _DPC_call_handle + { + _Accelerator_view_impl *_M_rv; + bool _M_is_explicit_target_acclview; + + // Info about the kernel function arguments + _Device_resource_info * _M_device_resource_info; + size_t _M_num_resources; + size_t _M_num_writable_buffers; + size_t _M_num_samplers; + + // Info about the host buffer created corresponding to the const buffer + _Device_const_buffer_info * _M_const_buffer_info; + size_t _M_num_const_buffers; + + bool _M_RW_aliasing; + + // Kernel funcs + _DPC_shader_blob * _M_shader_blobs[NUM_SHADER_VERSIONS]; + + // Compute domain info + int _M_is_flat_model; + unsigned int _M_compute_rank; + unsigned int * _M_grid_extents; + + // Kernel dispatch info + unsigned int _M_groupCountX; + unsigned int _M_groupCountY; + unsigned int _M_groupCountZ; + + // The shape of the group + unsigned int _M_groupExtentX; + unsigned int _M_groupExtentY; + unsigned int _M_groupExtentZ; + + _DPC_call_handle(const accelerator_view &_Accelerator_view) + { + if (!_Accelerator_view.is_auto_selection) { + _M_rv = _Get_accelerator_view_impl_ptr(_Accelerator_view); + } + else { + _M_rv = NULL; + } + + _M_is_explicit_target_acclview = false; + if (_M_rv != NULL) { + _M_is_explicit_target_acclview = true; + } + + _M_device_resource_info = NULL; + _M_num_resources = 0; + _M_num_writable_buffers = 0; + _M_num_samplers = 0; + + _M_const_buffer_info = NULL; + _M_num_const_buffers = 0; + + _M_RW_aliasing = false; + + for (size_t _I = 0; _I < NUM_SHADER_VERSIONS; _I++) + { + _M_shader_blobs[_I] = NULL; + } + + _M_is_flat_model = 0; + _M_compute_rank = 0; + _M_grid_extents = NULL; + + _M_groupCountX = 0; + _M_groupCountY = 0; + _M_groupCountZ = 0; + + _M_groupExtentX = 0; + _M_groupExtentY = 0; + _M_groupExtentZ = 0; + } + + ~_DPC_call_handle() + { + if (_M_grid_extents) { + delete [] _M_grid_extents; + } + } + + bool _Is_buffer_aliased(_In_ void *_Buffer_ptr) + { + return ((_M_aliased_buffer_set != nullptr) && (_M_aliased_buffer_set->find(_Buffer_ptr) != _M_aliased_buffer_set->end())); + } + + bool _Is_buffer_unaccessed(size_t _Buffer_idx) + { + return ((_M_is_device_buffer_unaccessed != nullptr) && _M_is_device_buffer_unaccessed->operator[](_Buffer_idx)); + } + + void _Set_buffer_unaccessed(size_t _Buffer_idx) + { + if (_M_is_device_buffer_unaccessed == nullptr) { + _M_is_device_buffer_unaccessed = std::unique_ptr<std::vector<bool>>(new std::vector<bool>(_M_num_resources, false)); + } + + _M_is_device_buffer_unaccessed->operator[](_Buffer_idx) = true; + } + + const int* _Get_redirect_indices() const + { + if (!_M_RW_aliasing) { + return nullptr; + } + + _ASSERTE(_M_Redirect_indices != nullptr); + + return _M_Redirect_indices->data(); + } + + void _Check_buffer_aliasing(); + void _Update_buffer_rw_property(); + void _Setup_aliasing_redirection_indices(); + void _Select_accelerator_view(); + void _Verify_buffers_against_accelerator_view(); + + private: + std::unique_ptr<std::unordered_set<void*>> _M_aliased_buffer_set; + std::unique_ptr<std::vector<bool>> _M_is_device_buffer_unaccessed; + // Info about read-write aliasing + std::unique_ptr<std::vector<int>> _M_Redirect_indices; + }; + + // This structure is used for passing the scheduling + // info to the parallel_for_each which is handed back + // to the compiler-runtime interface methods by the front-end + struct _Host_Scheduling_info + { + // The accelerator view to invoke a parallel_for_each on + accelerator_view _M_accelerator_view; + }; + +} // namespace Concurrency::details + + +/// <summary> +/// Uninitializes the C++ AMP runtime. It is legal to +/// call this function multiple times during an applications +/// lifetime. Calling any C++ AMP API afer calling this function +/// will reinitialize the C++ AMP runtime. Note that it is illegal +/// to use C++ AMP objects across calls to this function and doing +/// so will result in undefined behavior. Also, concurrently calling +/// this function and any other AMP APIs is illegal and would result +/// in undefined behavior. +/// </summary> +_AMPIMP void __cdecl amp_uninitialize(); + +} // namespace Concurrency + +extern "C" { + + // Return a compiler helper handle. + _AMPIMP _Ret_ _DPC_call_handle * HELPERAPI __dpc_create_call_handle(_In_ _Host_Scheduling_info *_Sch_info) throw(...); + + // Destroy the call handle + _AMPIMP void HELPERAPI __dpc_release_call_handle(_In_ _DPC_call_handle * _Handle) throw(...); + + _AMPIMP void HELPERAPI __dpc_set_device_resource_info(_In_ _DPC_call_handle * _Handle, _In_ _Device_resource_info * _DeviceResourceInfo, size_t _NumResources) throw(...); + + // Set const buffer info. + _AMPIMP void HELPERAPI __dpc_set_const_buffer_info(_In_ _DPC_call_handle * _Handle, _In_ _Device_const_buffer_info * _DeviceConstBufferInfo, size_t _NumConstBuffers) throw(...); + + // Set the kernel shader info + _AMPIMP void HELPERAPI __dpc_set_kernel_shader_info(_In_ _DPC_call_handle * _Handle, + _Inout_ void ** _ShaderBlobs) throw(...); + // Set kernel dispatch info + _AMPIMP void HELPERAPI __dpc_set_kernel_dispatch_info(_In_ _DPC_call_handle * _Handle, + unsigned int _ComputeRank, + _In_ int * _Extents, + unsigned int _GroupRank, + const unsigned int * _GroupExtents, + unsigned int & _GroupCountX, + unsigned int & _GroupCountY, + unsigned int & _GroupCountZ) throw(...); + + // Dispatch the kernel + _AMPIMP void HELPERAPI __dpc_dispatch_kernel(_In_ _DPC_call_handle * _Handle) throw(...); + +#ifdef _DEBUG + // Dispatch the kernel passed as a HLSL source level shader + // This function is to be used only for testing and debugging purposes + _AMPIMP void HELPERAPI __dpc_dispatch_kernel_test(_In_ _DPC_call_handle * _Handle, _In_ WCHAR* szFileName, LPCSTR szEntryPoint) throw(...); +#endif +} + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// C++ AMP ETW Provider +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +namespace Concurrency +{ +namespace details +{ + +// Thread-safe factory method for _Amp_runtime_trace object +_AMPIMP _Ret_ _Amp_runtime_trace* __cdecl _Get_amp_trace(); + +// Class that gathers C++ AMP diagnostic information and triggers events +class _Amp_runtime_trace +{ + +// Called by factory to create single instance of _Amp_runtime_trace type +friend BOOL CALLBACK _Init_amp_runtime_trace(PINIT_ONCE _Init_once, PVOID _Param, _Inout_ PVOID *_Context); + +public: + // Destructor for _Amp_runtime_trace, called at program termination + _AMPIMP ~_Amp_runtime_trace(); + + // End event is triggered by multiple other events such us StartComputeEvent to show exactly when given activity completed + _AMPIMP void _Write_end_event(ULONG _Span_id); + + // Add accelerator configuration information + // Note: This member function does not have to be exported, it is used by C++ AMP runtime factory + void _Add_accelerator_config_event(PVOID _Accelerator_id, LPCWSTR _Device_path, LPCWSTR _Device_description); + + // Used by callback function, to write all configuration data when new session is detected + // Note: This member function does not have to be exported, it is used by C++ AMP runtime factory + void _Write_all_accelerator_config_events(); + + // Started accelerator_view::wait operation + // Note: This member function does not have to be exported, it is used by C++ AMP runtime factory + ULONG _Start_accelerator_view_wait_event(PVOID _Accelerator_id, PVOID _Accelerator_view_id); + + // Launched accelerator_view::flush operation + // Note: This member function does not have to be exported, it is used by C++ AMP runtime factory + void _Launch_flush_event(PVOID _Accelerator_id, PVOID _Accelerator_view_id); + + // Launched accelerator_view::create_marker operation + // Note: This member function does not have to be exported, it is used by C++ AMP runtime factory + ULONG _Launch_marker(PVOID _Accelerator_id, PVOID _Accelerator_view_id); + + // Below are set of helpers that take various types that were available at event injection point and extract all necessary data + _AMPIMP ULONG _Start_parallel_for_each_event_helper(_In_ _DPC_call_handle *_Handle); + + // This helper wraps functor with wait start and wait end events + inline concurrency::completion_future _Start_async_op_wait_event_helper(ULONG _Async_op_id, _Event _Ev) + { + std::shared_future<void> retFuture; + concurrency::task_completion_event<void> retTaskCompletionEvent; + + // Create a std::shared_future by creating a deferred task through std::async that waits for the + // event _Ev to finish. Wrap functor with start and end events + retFuture = std::async(std::launch::sync, [=]() mutable { + try + { + if (_Async_op_id == _Amp_runtime_trace::_M_event_disabled) + { + _Ev._Get(); + } + else + { + auto _Span_id = details::_Get_amp_trace()->_Start_async_op_wait_event(_Async_op_id); + _Ev._Get(); + details::_Get_amp_trace()->_Write_end_event(_Span_id); + } + } + catch(...) + { + // If an exception is encountered when executing the asynchronous operation + // we should set the exception on the retTaskCompletionEvent so that it is + // appropriately cancelled and the exception is propagated to continuations + retTaskCompletionEvent.set_exception(std::current_exception()); + throw; + } + + retTaskCompletionEvent.set(); + }); + + // Register the async event with the runtime asynchronous events manager + _Register_async_event(_Ev, retFuture); + + // Lets issue a continuation just to swallow any exceptions that are encountered during the + // async operation and are never observed by the user or are just observed through the + // shared_future and not through the task + concurrency::task<void> retTask(retTaskCompletionEvent); + retTask.then([](concurrency::task<void> _Task) { + try { + _Task.get(); + } + catch(...) { + } + }); + + return Concurrency::completion_future(retFuture, retTask); + } + + _AMPIMP ULONG _Start_array_view_synchronize_event_helper(const _Buffer_descriptor &_Buff_desc); + _AMPIMP ULONG _Launch_array_view_synchronize_event_helper(const _Buffer_descriptor &_Buff_desc); + + // Helpers for buffers (array, array_view) + _AMPIMP ULONG _Start_copy_event_helper(const _Buffer_descriptor &_Src, const _Buffer_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Start_copy_event_helper(nullptr_t, const _Buffer_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Start_copy_event_helper(const _Buffer_descriptor &_Src, nullptr_t, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Launch_async_copy_event_helper(const _Buffer_descriptor &_Src, const _Buffer_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Launch_async_copy_event_helper(nullptr_t, const _Buffer_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Launch_async_copy_event_helper(const _Buffer_descriptor &_Src, nullptr_t, ULONGLONG _Num_bytes_for_copy); + + // Helper for textures + _AMPIMP ULONG _Start_copy_event_helper(const _Texture_descriptor &_Src, nullptr_t, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Start_copy_event_helper(nullptr_t, const _Texture_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Start_copy_event_helper(const _Texture_descriptor &_Src, const _Texture_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Launch_async_copy_event_helper(const _Texture_descriptor &_Src, nullptr_t, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Launch_async_copy_event_helper(nullptr_t, const _Texture_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + _AMPIMP ULONG _Launch_async_copy_event_helper(const _Texture_descriptor &_Src, const _Texture_descriptor &_Dest, ULONGLONG _Num_bytes_for_copy); + + void _Enable_provider(bool _Enable = true); + +private: + // Private constructor. This type is created by factory method + _Amp_runtime_trace(PVOID _Callback_function, _In_ _Trace *_Trace); + + // Disallow copy construction + _Amp_runtime_trace(const _Amp_runtime_trace&); + + // Disallow assignment operator + _Amp_runtime_trace& operator=(const _Amp_runtime_trace&); + + // Used internally to write configuation events + void _Write_accelerator_config_event(const std::tuple<PVOID, LPCWSTR, LPCWSTR> &_ConfigTuple); + + // Event triggered when computation is scheduled + ULONG _Start_parallel_for_each_event( + PVOID _Accelerator_id, + PVOID _Accelerator_view_id, + BOOL _Is_tiled_explicitly, + ULONGLONG _Num_of_tiles, + ULONG _Num_of_threads_per_tile, + BOOL _Is_aliased, + ULONG _Num_read_only_resources, + ULONG _Num_read_write_resources, + ULONGLONG _Size_of_all_resouces, + ULONG _Size_of_const_data, + ULONGLONG _Size_of_data_for_copy); + + // Synchronous copy operation has started + ULONG _Start_copy_event( + PVOID _Src_accelerator_id, + PVOID _Src_accelerator_view_id, + PVOID _Dst_accelerator_id, + PVOID _Dst_accelerator_view_id, + ULONGLONG _Num_bytes_for_copy, + BOOL _Is_src_staging, + BOOL _Is_dst_staging); + + // Asynchronous copy operation has been launched + ULONG _Launch_async_copy_event( + PVOID _Src_accelerator_id, + PVOID _Src_accelerator_view_id, + PVOID _Dst_accelerator_id, + PVOID _Dst_accelerator_view_id, + ULONGLONG _Num_bytes_for_copy, + BOOL _Is_src_staging, + BOOL _Is_dst_staging); + + // Started waiting for asynchronous operation to complete + _AMPIMP ULONG _Start_async_op_wait_event(ULONG _Async_op_id); + + // Started array_view::synchronize operation + ULONG _Start_array_view_synchronize_event(ULONGLONG _Num_bytes_to_synchronize); + + // Async array_view::synchronize operation has been launched + ULONG _Launch_array_view_synchronize_event(ULONGLONG _Num_bytes_to_synchronize); + + // Helper function that extracts information from buffer descriptor + std::tuple<PVOID, PVOID, BOOL> _Get_resource_diagnostic_info(const _Buffer_descriptor &_Buff_desc, accelerator_view _Accl_view) const; + + // Helper function that extracts information from texture descriptor + std::tuple<PVOID, PVOID, BOOL> _Get_resource_diagnostic_info(const _Texture_descriptor &_Tex_desc) const; + + // Generates unique identifiers for span_id and async_op_id + ULONG _Get_unique_identifier(); + + // Critical section object used by callback function to synchronize following situations: + // a) multiple sessions have started at the same time + // b) C++ AMP Runtime factory adds new accelerator config event to the collection + Concurrency::critical_section _M_critical_section; + + // Collection of all configuration events at the time of C++ AMP Runtime initialization + std::vector<std::tuple<PVOID, LPCWSTR, LPCWSTR>> _M_accelerator_configs; + + // Unique counter for span id and async operation id + volatile ULONG _M_counter; + + // Type that implements ITrace interface and writes events e.g. ETW events + _Trace* _M_trace_ptr; + + // Special value that we return to chain events if provider is disabled + static const ULONG _M_event_disabled = 0; +}; + +// Helper function to query the number of mipmap levels from texture object +inline unsigned int _Get_mipmap_levels(const _Texture *_Tex) +{ + _ASSERTE(_Tex); + return _Tex->_Get_mip_levels(); +} + +} // namespace Concurrency::details +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma pack(pop) diff --git a/test_data/lots_of_files/app_base.h b/test_data/lots_of_files/app_base.h new file mode 100644 index 0000000..113e78f --- /dev/null +++ b/test_data/lots_of_files/app_base.h @@ -0,0 +1,71 @@ +/* + * 4tech reusable platform layer interface + * by Allen Webster + * 20.08.2015 + * + */ + +// TOP + +#if FTECH_ALLOW_ASSERT +#include <assert.h> +#define _Assert assert +#else +#define _Assert(x) +#endif + +#define SOFTWARE_RENDER 0 +#define OPENGL_RENDER 1 + +#if FTECH_GL_RENDER +#define RENDER_MODE OPENGL_RENDER +#endif + +#if FTECH_SOFT_RENDER +#define RENDER_MODE SOFTWARE_RENDER +#endif + +#ifndef RENDER_MODE +#error render mode not set +#endif + +struct Mouse_State{ + bool32 in_window; + i32 x, y; + bool32 l, r; + bool32 pl, pr; + i16 wheel; +}; + +struct Memory{ + void *mem; + i32 size; +}; + +struct Render_Target{ + i32 w, h; +}; + +struct App_Step_Data{ + Memory memory; + Render_Target render_target; + Mouse_State mouse; +}; + +enum App_Mouse_Cursor{ + APP_MOUSE_CURSOR_UNSET, + APP_MOUSE_CURSOR_IBEAM, + APP_MOUSE_CURSOR_ARROW, + APP_MOUSE_CURSOR_LEFTRIGHT, + APP_MOUSE_CURSOR_UPDOWN +}; + +struct App_Step_Out{ + App_Mouse_Cursor mouse_cursor; +}; + +#define APP_STEP_SIG(name) void name(App_Step_Data *step, App_Step_Out *out) +APP_STEP_SIG(app_step); + +// BOTTOM + diff --git a/test_data/lots_of_files/app_color.cpp b/test_data/lots_of_files/app_color.cpp new file mode 100644 index 0000000..34483f5 --- /dev/null +++ b/test_data/lots_of_files/app_color.cpp @@ -0,0 +1,547 @@ +/* + * 4tech experiments + * by Allen Webster + * 20.08.2015 (dd.mm.yyyy) + * + */ + +// TOP + +global Vec4 black = {0.f, 0.f, 0.f, 1.f}; +global Vec4 white = {1.f, 1.f, 1.f, 1.f}; +global Vec4 gray = {0.5f, 0.5f, 0.5f, 1.f}; +global Vec4 red = {1.f, 0.f, 0.f, 1.f}; + +struct real32_Rect{ + real32 x0,y0,x1,y1; +}; + +inline real32_Rect +rect_around_point(Vec2 p, real32 r){ + real32_Rect result; + result.x0 = p.x - r; + result.x1 = p.x + r; + result.y0 = p.y - r; + result.y1 = p.y + r; + return result; +} + +internal void +mygl_clear(Vec4 color){ + glClearColor(color.r, color.g, color.b, color.a); + glClear(GL_COLOR_BUFFER_BIT); +} + +internal void +mygl_color(Vec4 color){ + glColor4f(color.r, color.g, color.b, color.a); +} + +internal void +mygl_rectangle(real32_Rect rect, Vec4 color){ + mygl_color(color); + glBegin(GL_QUADS); + { + glVertex2f(rect.x0, rect.y0); + glVertex2f(rect.x1, rect.y0); + glVertex2f(rect.x1, rect.y1); + glVertex2f(rect.x0, rect.y1); + } + glEnd(); +} + +internal void +mygl_rectangle_h_grad(real32_Rect rect, Vec4 left, Vec4 right){ + glBegin(GL_QUADS); + { + mygl_color(left); + glVertex2f(rect.x0, rect.y0); + glVertex2f(rect.x1, rect.y0); + + mygl_color(right); + glVertex2f(rect.x1, rect.y1); + glVertex2f(rect.x0, rect.y1); + } + glEnd(); +} + +internal void +mygl_polygon_outline(Vec2 *vertices, i32 vertex_count, Vec4 color){ + mygl_color(color); + glBegin(GL_LINE_LOOP); + { + for (i32 i = 0; i < vertex_count; ++i){ + glVertex2f(vertices[i].x, vertices[i].y); + } + } + glEnd(); +} + +internal void +mygl_line(Vec2 a, Vec2 b, Vec4 color){ + mygl_color(color); + glBegin(GL_LINES); + { + glVertex2f(a.x, a.y); + glVertex2f(b.x, b.y); + } + glEnd(); +} + +struct Skeleton_Draw_Point{ + i32 id; + i32 next_child; +}; + +internal void +mygl_skeleton(Tree_Skeleton_Vert *vertices, i32 count, i32 highlight_index){ + Vec4 highlight_color = {0.f, 0.f, 1.f, 1.f}; + Skeleton_Draw_Point stack[16]; + i32 top = 0; + stack[top++] = {0, 0}; + glBegin(GL_LINES); + while (top > 0){ + Skeleton_Draw_Point *pt = stack + top - 1; + Tree_Skeleton_Vert *vert = vertices + pt->id; + + bool32 done = 0; + if (pt->next_child >= 3){ + done = 1; + } + else{ + i32 child = vert->children[pt->next_child]; + if (child){ + Tree_Skeleton_Vert *child_vert = vertices + child; + if (child_vert->paint.r == 0.f && child_vert->paint.g == 0.f && + child_vert->paint.b == 0.f){ + int x = 1; AllowLocal(x); + } + if (highlight_index == child){ + mygl_color(highlight_color); + } + else{ + mygl_color(child_vert->paint); + } + glVertex2f(vert->pos.x, vert->pos.y); + glVertex2f(child_vert->pos.x, child_vert->pos.y); + ++pt->next_child; + stack[top++] = {child, 0}; + } + else{ + done = 1; + } + } + + if (done){ + --top; + } + } + glEnd(); +} + +internal void +mygl_branch_info(real32 x, real32 y, Branch_Info info){ + Vec4 color = {.6f, 0.f, .4f, 1.f}; + Vec2 a, b; + real32 x_step = 3.f; + i32 i; + + a.y = y; + + a.x = b.x = x; + b.y = y + info.length; + mygl_line(a, b, color); + + x += x_step; + a.x = b.x = x; + b.y = y + info.step; + mygl_line(a, b, color); + + for (i = 0; i < ArrayCount(info.contribution_list); ++i){ + x += x_step; + a.x = b.x = x; + b.y = y + info.contribution_list[i]; + mygl_line(a, b, color); + } +} + +struct Super_Color{ + Vec4 hsla; + Vec4 rgba; + u32 packed; +}; + +struct Memory_Part{ + char *base; + i32 pos, max; +}; + +internal void* +mempart_get(Memory_Part *part, i32 size){ + _Assert(part->pos + size < part->max); + void *result = part->base + part->pos; + part->pos += size; + return result; +} + +#define mempart_get_array(part,T,size) (T*)mempart_get(part, sizeof(T)*size); + +struct Temp_Memory{ + Memory_Part *part; + i32 start_pos; +}; + +internal Temp_Memory +tempmem_begin(Memory_Part *part){ + Temp_Memory temp; + temp.part = part; + temp.start_pos = part->pos; + return temp; +} + +internal void +tempmem_end(Temp_Memory temp){ + temp.part->pos = temp.start_pos; +} + +struct App_Vars{ + bool32 initialized; + Memory_Part part; + Tree_Parameters params; + i32 hot_spot_max; + bool32 show_skeleton; + bool32 show_ghost_skeleton; + bool32 show_tree; + bool32 show_hot_spots; + + i32 skeleton_limit; + i32 p_moving; + Hot_Spot *p; + + i32 inspecto_branch; +}; + +internal void +show_vec_stats(Vec4 v, real32 x, real32 y){ + real32_Rect h,s,l,f; + h = {x, y, 10.f + x, y}; + s = h; + s.x0 += 20.f; + s.x1 += 20.f; + l = s; + l.x0 += 20.f; + l.x1 += 20.f; + + h.y1 += 100.f*v.x; + s.y1 += 100.f*v.y; + l.y1 += 100.f*v.z; + + f.x0 = h.x0 - 5.f; + f.x1 = l.x1 + 5.f; + f.y0 = h.y0 - 5.f; + f.y1 = h.y0 + 105.f; + + mygl_rectangle(f, gray); + mygl_rectangle(h, white); + mygl_rectangle(s, white); + mygl_rectangle(l, white); +} + +internal bool32 +hit_check(i32 x, i32 y, real32_Rect r){ + return (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1); +} + +internal void +do_adjuster(i32 id, Mouse_State *mouse, real32 *v, real32 low, real32 high, real32_Rect rect, bool32 *mouse_consumed){ + mygl_rectangle(rect, V4(.3f, .3f, .3f, 1.f)); + + real32 x, scale; + if (mouse->l && hit_check(mouse->x, mouse->y, rect)){ + x = (real32)mouse->x; + scale = (x - rect.x0) / (rect.x1 - rect.x0); + *v = lerp(low, scale, high); + *mouse_consumed = 1; + } + else{ + scale = (*v - low) / (high - low); + x = lerp(rect.x0, scale, rect.x1); + } + + rect.x0 = x - 2.f; + rect.x1 = x + 3.f; + mygl_rectangle(rect, V4(.6f, .6f, .6f, 1.f)); +} + +internal void +do_adjuster(i32 id, Mouse_State *mouse, i32 *v, i32 low, i32 high, real32_Rect rect, bool32 *mouse_consumed){ + mygl_rectangle(rect, V4(.3f, .3f, .3f, 1.f)); + + real32 x, scale; + if (mouse->l && hit_check(mouse->x, mouse->y, rect)){ + x = (real32)mouse->x; + scale = (x - rect.x0) / (rect.x1 - rect.x0); + *v = FLOOR32(lerp((real32)low, scale, (real32)high + 1)); + if (*v > high) *v = high; + *mouse_consumed = 1; + } + + scale = (real32)(*v - low) / (real32)(high - low); + x = lerp(rect.x0, scale, rect.x1); + + rect.x0 = x - 2.f; + rect.x1 = x + 3.f; + mygl_rectangle(rect, V4(.6f, .6f, .6f, 1.f)); +} + +internal bool32 +do_button(i32 id, Mouse_State *mouse, bool32 active, real32_Rect rect, bool32 *mouse_consumed){ + bool32 result = 0; + if (active) mygl_rectangle(rect, V4(.6f, .6f, .6f, 1.f)); + else mygl_rectangle(rect, V4(.3f, .3f, .3f, 1.f)); + if (mouse->pl && hit_check(mouse->x, mouse->y, rect)){ + result = 1; + *mouse_consumed = 1; + } + return result; +} + +internal bool32 +do_draggable(i32 id, Mouse_State *mouse, bool32 active, real32_Rect rect, bool32 *mouse_consumed){ + bool32 result = 0; + if (!*mouse_consumed){ + if (active) mygl_rectangle(rect, V4(.6f, .6f, .6f, 1.f)); + else mygl_rectangle(rect, V4(.3f, .3f, .3f, 1.f)); + if (mouse->l && hit_check(mouse->x, mouse->y, rect)){ + result = 1; + *mouse_consumed = 1; + } + } + return result; +} + +internal bool32 +do_right_click(i32 id, Mouse_State *mouse, real32_Rect rect, bool32 *mouse_consumed){ + bool32 result = 0; + if (!*mouse_consumed){ + if (mouse->pr && hit_check(mouse->x, mouse->y, rect)){ + result = 1; + *mouse_consumed = 1; + } + } + return result; +} + +APP_STEP_SIG(app_step){ + _Assert(sizeof(App_Vars) < step->memory.size); + App_Vars *vars = (App_Vars*)step->memory.mem; + if (!vars->initialized){ + vars->initialized = 1; + vars->part.base = (char*)step->memory.mem; + vars->part.max = step->memory.size; + vars->part.pos = sizeof(App_Vars); + + Tree_Parameters params; + params.base = V2(500.f, 590.f); + params.levels = 6; + params.initial_step = 70.f; + params.step_decay = .8f; + params.initial_thickness = 10.f; + params.thickness_decay = .75f; + params.dtheta = 72.f; + params.min_weight_threshold = 1.f; + params.center_branch_weight = 1.f; + params.heat_half_distance = 30.f; + + vars->hot_spot_max = 10; + params.hot_spot_count = 3; + params.hot_spots = mempart_get_array(&vars->part, Hot_Spot, vars->hot_spot_max); + + params.hot_spots[0].spot.x = 500.f; + params.hot_spots[0].spot.y = 100.f; + + params.hot_spots[1].spot.x = 850.f; + params.hot_spots[1].spot.y = 400.f; + + params.hot_spots[2].spot.x = 300.f; + params.hot_spots[2].spot.y = 150.f; + + vars->params = params; + + vars->show_skeleton = 1; + vars->show_tree = 1; + vars->show_hot_spots = 1; + + vars->p_moving = -1; + vars->p = 0; + + vars->skeleton_limit = 0; + } + + mygl_clear(black); + + Mouse_State *mouse = &step->mouse; + bool32 mouse_consumed = 0; + i32 id = 1; + real32_Rect rect; + real32 low_point; + + Hot_Spot *hot_spot, *hot_spot_array; + i32 ind, i; + hot_spot_array = vars->params.hot_spots; + hot_spot = hot_spot_array; + for (i = 0; i < vars->params.hot_spot_count; ++i, ++hot_spot){ + ind = i + (1 << 16); + + rect = rect_around_point(hot_spot->spot, 4); + if (do_draggable(ind, mouse, (ind == vars->p_moving), rect, &mouse_consumed)){ + vars->p_moving = ind; + vars->p = hot_spot; + } + + if (do_right_click(ind, mouse, rect, &mouse_consumed)){ + vars->p_moving = -1; + --vars->params.hot_spot_count; + *hot_spot = hot_spot_array[vars->params.hot_spot_count]; + break; + } + } + + if (!mouse_consumed && mouse->pr){ + if (vars->params.hot_spot_count < vars->hot_spot_max){ + hot_spot_array[vars->params.hot_spot_count++].spot = V2((f32)mouse->x, (f32)mouse->y); + } + } + // TODO(allen): If the mouse isn't consumed yet, and it's a right click, add a point + + if (vars->p_moving != -1 && mouse->l){ + Hot_Spot *p = vars->p; + if (p != 0){ + p->spot.x = (real32)mouse->x; + p->spot.y = (real32)mouse->y; + } + } + + if (!mouse->l){ + vars->p_moving = -1; + } + + rect.x0 = 10.f; + rect.x1 = 210.f; + rect.y0 = 10.f; + rect.y1 = 30.f; + do_adjuster(id++, mouse, &vars->params.initial_step, 0.f, 200.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.step_decay, 0.f, 1.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.initial_thickness, 1.f, 50.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.thickness_decay, 0.f, 1.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.dtheta, 90.f, 0.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.levels, 1, 7, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.min_weight_threshold, 1.f, 100.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.center_branch_weight, 0.5f, 2.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->params.heat_half_distance, 10.f, 100.f, rect, &mouse_consumed); + rect.y0 += 30.f; + rect.y1 += 30.f; + do_adjuster(id++, mouse, &vars->inspecto_branch, 1, vars->skeleton_limit, rect, &mouse_consumed); + low_point = rect.y1; + + rect.x0 = rect.x1 + 10.f; + rect.x1 = rect.x0 + 20.f; + rect.y0 = 10.f; + rect.y1 = 30.f; + if (do_button(id++, mouse, vars->show_skeleton, rect, &mouse_consumed)){ + vars->show_skeleton = !vars->show_skeleton; + } + rect.y0 += 30.f; + rect.y1 += 30.f; + if (do_button(id++, mouse, vars->show_ghost_skeleton, rect, &mouse_consumed)){ + vars->show_ghost_skeleton = !vars->show_ghost_skeleton; + } + rect.y0 += 30.f; + rect.y1 += 30.f; + if (do_button(id++, mouse, vars->show_tree, rect, &mouse_consumed)){ + vars->show_tree = !vars->show_tree; + } + rect.y0 += 30.f; + rect.y1 += 30.f; + if (do_button(id++, mouse, vars->show_hot_spots, rect, &mouse_consumed)){ + vars->show_hot_spots = !vars->show_hot_spots; + } + rect.y0 += 30.f; + rect.y1 += 30.f; + if (do_button(id++, mouse, 0, rect, &mouse_consumed)){ + vars->skeleton_limit = 0; + } + if (low_point < rect.y1) low_point = rect.y1; + + Temp_Memory temp; + i32 init_skeleton_count, skeleton_count; + Tree_Skeleton_Vert *skeleton; + Branch_Info branch_info; + i32 natural_limit; + + temp = tempmem_begin(&vars->part); + init_skeleton_count = 2260; + skeleton = mempart_get_array(&vars->part, Tree_Skeleton_Vert, init_skeleton_count); + memset(skeleton, 0, sizeof(Tree_Skeleton_Vert)*init_skeleton_count); + ++vars->skeleton_limit; + branch_info = {}; + branch_info.which_branch = vars->inspecto_branch; + natural_limit = + tree_skeleton_gen(skeleton, init_skeleton_count, &skeleton_count, vars->params, vars->skeleton_limit, 0, + &branch_info, 1); + if (vars->skeleton_limit > natural_limit) vars->skeleton_limit = natural_limit; + + Tree_Skeleton_Vert *ghost_skeleton = 0; + i32 ghost_skeleton_count = 0; + if (vars->show_ghost_skeleton){ + ghost_skeleton = mempart_get_array(&vars->part, Tree_Skeleton_Vert, init_skeleton_count); + memset(ghost_skeleton, 0, sizeof(Tree_Skeleton_Vert)*init_skeleton_count); + tree_skeleton_gen(ghost_skeleton, init_skeleton_count, &ghost_skeleton_count, + vars->params, vars->skeleton_limit, 1, 0, 0); + } + + i32 init_vertex_count = 2260, vertex_count; + Vec2 *vertices = mempart_get_array(&vars->part, Vec2, init_vertex_count); + tree_gen(vertices, init_vertex_count, &vertex_count, vars->params, skeleton); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (vars->show_ghost_skeleton) + mygl_skeleton(ghost_skeleton, ghost_skeleton_count, -1); + + if (vars->show_skeleton) + mygl_skeleton(skeleton, skeleton_count, vars->inspecto_branch); + + if (vars->show_tree) + mygl_polygon_outline(vertices, vertex_count, brown); + + if (vars->show_hot_spots){ + for (i32 i = 0; i < vars->params.hot_spot_count; ++i){ + mygl_rectangle(rect_around_point(vars->params.hot_spots[i].spot, 2), V4(1.f, 1.f, 0.f, 1.f)); + } + } + + mygl_branch_info(5.f, low_point + 5.f, branch_info); + + tempmem_end(temp); + + out->mouse_cursor = APP_MOUSE_CURSOR_ARROW; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/arm_neon.h b/test_data/lots_of_files/arm_neon.h new file mode 100644 index 0000000..d07d681 --- /dev/null +++ b/test_data/lots_of_files/arm_neon.h @@ -0,0 +1,3024 @@ +/*** +* arm_neon.h - declarations/definitions for ARM NEON specific intrinsics +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This include file contains the declarations for ARM NEON intrinsic functions +* +****/ + +#pragma once + +#include <stdint.h> +#include <sal.h> + +#if !defined (_M_ARM) +#error This header is specific to ARM targets +#endif /* !defined (_M_ARM) */ + + +#if defined (__cplusplus) +extern "C" { +#endif /* defined (__cplusplus) */ + + +/////////////////////////////////////////////////////////////////////////////// +// +#if !defined (_ADVSIMD_ALIGN) +#if defined (__midl) +#define _ADVSIMD_ALIGN(x) +#else /* defined (__midl) */ +#define _ADVSIMD_ALIGN(x) __declspec(align(x)) +#endif /* defined (__midl) */ +#endif /* !defined (_ADVSIMD_ALIGN) */ + +#ifndef DUMMYNEONSTRUCT +#define DUMMYNEONSTRUCT s +#endif /* DUMMYNEONSTRUCT */ + +/////////////////////////////////////////////////////////////////////////////// +// +// ARM Advanced SIMD 64bit type +// +typedef union __declspec(intrin_type) _ADVSIMD_ALIGN(8) __n64 +{ + unsigned __int64 n64_u64[1]; + unsigned __int32 n64_u32[2]; + unsigned __int16 n64_u16[4]; + unsigned __int8 n64_u8[8]; + __int64 n64_i64[1]; + __int32 n64_i32[2]; + __int16 n64_i16[4]; + __int8 n64_i8[8]; + float n64_f32[2]; +} __n64; + + +/////////////////////////////////////////////////////////////////////////////// +// +// ARM Advanced SIMD 128bit type +// +typedef union __declspec(intrin_type) _ADVSIMD_ALIGN(8) __n128 +{ + unsigned __int64 n128_u64[2]; + unsigned __int32 n128_u32[4]; + unsigned __int16 n128_u16[8]; + unsigned __int8 n128_u8[16]; + __int64 n128_i64[2]; + __int32 n128_i32[4]; + __int16 n128_i16[8]; + __int8 n128_i8[16]; + float n128_f32[4]; + + struct + { + __n64 low64; + __n64 high64; + } DUMMYNEONSTRUCT; + +} __n128; + +typedef struct __n64x2 +{ + __n64 val[2]; +} __n64x2; + +typedef struct __n64x3 +{ + __n64 val[3]; +} __n64x3; + +typedef struct __n64x4 +{ + __n64 val[4]; +} __n64x4; + +typedef struct __n128x2 +{ + __n128 val[2]; +} __n128x2; + +typedef struct __n128x3 +{ + __n128 val[3]; +} __n128x3; + +typedef struct __n128x4 +{ + __n128 val[4]; +} __n128x4; + +/////////////////////////////////////////////////////////////////////////////// +// +typedef unsigned __int8 poly8_t; +typedef unsigned __int16 poly16_t; + +typedef float float32_t; + + +/////////////////////////////////////////////////////////////////////////////// +// +__inline _Post_equal_to_(p) __n64 *__int8ToN64(_In_ int8_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__int16ToN64(_In_ int16_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__int32ToN64(_In_ int32_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__int64ToN64(_In_ int64_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__uint8ToN64(_In_ uint8_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__uint16ToN64(_In_ uint16_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__uint32ToN64(_In_ uint32_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__uint64ToN64(_In_ uint64_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__poly8ToN64(_In_ poly8_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__poly16ToN64(_In_ poly16_t *p) { return (__n64 *)p; } +__inline _Post_equal_to_(p) __n64 *__float32ToN64(_In_ float32_t *p) { return (__n64 *)p; } + +__inline _Post_equal_to_(p) const __n64 *__int8ToN64_c(_In_ const int8_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__int16ToN64_c(_In_ const int16_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__int32ToN64_c(_In_ const int32_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__int64ToN64_c(_In_ const int64_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__uint8ToN64_c(_In_ const uint8_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__uint16ToN64_c(_In_ const uint16_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__uint32ToN64_c(_In_ const uint32_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__uint64ToN64_c(_In_ const uint64_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__poly8ToN64_c(_In_ const poly8_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__poly16ToN64_c(_In_ const poly16_t *p) { return (const __n64 *)p; } +__inline _Post_equal_to_(p) const __n64 *__float32ToN64_c(_In_ const float32_t *p) { return (const __n64 *)p; } + +__inline int32_t __int8ToInt32(int8_t i) { return (int32_t)i; } +__inline int32_t __int16ToInt32(int16_t i) { return (int32_t)i; } +__inline int32_t __int32ToInt32(int32_t i) { return (int32_t)i; } +__inline int64_t __int64ToInt64(int64_t i) { return (int64_t)i; } + +__inline int32_t __uint8ToInt32(uint8_t i) { return (int32_t)i; } +__inline int32_t __uint16ToInt32(uint16_t i) { return (int32_t)i; } +__inline int32_t __uint32ToInt32(uint32_t i) { return (int32_t)i; } +__inline int64_t __uint64ToInt64(uint64_t i) { return (int64_t)i; } + +__inline int32_t __poly8ToInt32(poly8_t i) { return (int32_t)i; } +__inline int32_t __poly16ToInt32(poly16_t i) { return (int32_t)i; } + +/////////////////////////////////////////////////////////////////////////////// +// +#define vshll_n_s8(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) <= 8, "invalid shift amount"), ((shift_amount) == 8) ? __internal_vshll_n_t2_s8((Dm)) : __internal_vshll_n_t1_s8((Dm), (shift_amount)) ) +#define vshll_n_s16(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) <= 16, "invalid shift amount"), ((shift_amount) == 16) ? __internal_vshll_n_t2_s16((Dm)) : __internal_vshll_n_t1_s16((Dm), (shift_amount)) ) +#define vshll_n_s32(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) <= 32, "invalid shift amount"), ((shift_amount) == 32) ? __internal_vshll_n_t2_s32((Dm)) : __internal_vshll_n_t1_s32((Dm), (shift_amount)) ) +#define vshll_n_u8(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) <= 8, "invalid shift amount"), ((shift_amount) == 8) ? __internal_vshll_n_t2_u8((Dm)) : __internal_vshll_n_t1_u8((Dm), (shift_amount)) ) +#define vshll_n_u16(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) <= 16, "invalid shift amount"), ((shift_amount) == 16) ? __internal_vshll_n_t2_u16((Dm)) : __internal_vshll_n_t1_u16((Dm), (shift_amount)) ) +#define vshll_n_u32(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) <= 32, "invalid shift amount"), ((shift_amount) == 32) ? __internal_vshll_n_t2_u32((Dm)) : __internal_vshll_n_t1_u32((Dm), (shift_amount)) ) + + +/////////////////////////////////////////////////////////////////////////////// +// +// { +++ auto-generated code begins (explicit types) + +typedef __n64 float32x2_t; +typedef __n64x2 float32x2x2_t; +typedef __n64x3 float32x2x3_t; +typedef __n64x4 float32x2x4_t; +typedef __n64 int8x8_t; +typedef __n64x2 int8x8x2_t; +typedef __n64x3 int8x8x3_t; +typedef __n64x4 int8x8x4_t; +typedef __n64 int16x4_t; +typedef __n64x2 int16x4x2_t; +typedef __n64x3 int16x4x3_t; +typedef __n64x4 int16x4x4_t; +typedef __n64 int32x2_t; +typedef __n64x2 int32x2x2_t; +typedef __n64x3 int32x2x3_t; +typedef __n64x4 int32x2x4_t; +typedef __n64 int64x1_t; +typedef __n64x2 int64x1x2_t; +typedef __n64x3 int64x1x3_t; +typedef __n64x4 int64x1x4_t; +typedef __n64 poly8x8_t; +typedef __n64x2 poly8x8x2_t; +typedef __n64x3 poly8x8x3_t; +typedef __n64x4 poly8x8x4_t; +typedef __n64 poly16x4_t; +typedef __n64x2 poly16x4x2_t; +typedef __n64x3 poly16x4x3_t; +typedef __n64x4 poly16x4x4_t; +typedef __n64 uint8x8_t; +typedef __n64x2 uint8x8x2_t; +typedef __n64x3 uint8x8x3_t; +typedef __n64x4 uint8x8x4_t; +typedef __n64 uint16x4_t; +typedef __n64x2 uint16x4x2_t; +typedef __n64x3 uint16x4x3_t; +typedef __n64x4 uint16x4x4_t; +typedef __n64 uint32x2_t; +typedef __n64x2 uint32x2x2_t; +typedef __n64x3 uint32x2x3_t; +typedef __n64x4 uint32x2x4_t; +typedef __n64 uint64x1_t; +typedef __n64x2 uint64x1x2_t; +typedef __n64x3 uint64x1x3_t; +typedef __n64x4 uint64x1x4_t; +typedef __n128 float32x4_t; +typedef __n128x2 float32x4x2_t; +typedef __n128x3 float32x4x3_t; +typedef __n128x4 float32x4x4_t; +typedef __n128 int8x16_t; +typedef __n128x2 int8x16x2_t; +typedef __n128x3 int8x16x3_t; +typedef __n128x4 int8x16x4_t; +typedef __n128 int16x8_t; +typedef __n128x2 int16x8x2_t; +typedef __n128x3 int16x8x3_t; +typedef __n128x4 int16x8x4_t; +typedef __n128 int32x4_t; +typedef __n128x2 int32x4x2_t; +typedef __n128x3 int32x4x3_t; +typedef __n128x4 int32x4x4_t; +typedef __n128 int64x2_t; +typedef __n128x2 int64x2x2_t; +typedef __n128x3 int64x2x3_t; +typedef __n128x4 int64x2x4_t; +typedef __n128 poly8x16_t; +typedef __n128x2 poly8x16x2_t; +typedef __n128x3 poly8x16x3_t; +typedef __n128x4 poly8x16x4_t; +typedef __n128 poly16x8_t; +typedef __n128x2 poly16x8x2_t; +typedef __n128x3 poly16x8x3_t; +typedef __n128x4 poly16x8x4_t; +typedef __n128 uint8x16_t; +typedef __n128x2 uint8x16x2_t; +typedef __n128x3 uint8x16x3_t; +typedef __n128x4 uint8x16x4_t; +typedef __n128 uint16x8_t; +typedef __n128x2 uint16x8x2_t; +typedef __n128x3 uint16x8x3_t; +typedef __n128x4 uint16x8x4_t; +typedef __n128 uint32x4_t; +typedef __n128x2 uint32x4x2_t; +typedef __n128x3 uint32x4x3_t; +typedef __n128x4 uint32x4x4_t; +typedef __n128 uint64x2_t; +typedef __n128x2 uint64x2x2_t; +typedef __n128x3 uint64x2x3_t; +typedef __n128x4 uint64x2x4_t; + +// } +++ auto-generated code ends (explicit types) + + +/////////////////////////////////////////////////////////////////////////////// +// +// { +++ auto-generated code begins (prototypes) + +__n64x2 __neon_DdDm_acc2(unsigned int _Enc, __n64, __n64); +__n64x2 __neon_Dx2Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64x2 __neon_Dx2Adr_acc(unsigned int _Enc, __n64x2, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64x3 __neon_Dx3Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64x3 __neon_Dx3Adr_acc(unsigned int _Enc, __n64x3, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64x4 __neon_Dx4Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64x4 __neon_Dx4Adr_acc(unsigned int _Enc, __n64x4, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64 __neon_DdDm(unsigned int _Enc, __n64); +__n64 __neon_DdDx2Dm(unsigned int _Enc, __n64x2, __n64); +__n64 __neon_DdDx2Dm_acc(unsigned int _Enc, __n64, __n64x2, __n64); +__n64 __neon_DdDx3Dm(unsigned int _Enc, __n64x3, __n64); +__n64 __neon_DdDx3Dm_acc(unsigned int _Enc, __n64, __n64x3, __n64); +__n64 __neon_DdDx4Dm(unsigned int _Enc, __n64x4, __n64); +__n64 __neon_DdDx4Dm_acc(unsigned int _Enc, __n64, __n64x4, __n64); +__n64 __neon_DdDm_acc(unsigned int _Enc, __n64, __n64); +__n64 __neon_DdDnDm(unsigned int _Enc, __n64, __n64); +__n64 __neon_DdDnDm_acc(unsigned int _Enc, __n64, __n64, __n64); +__n64 __neon_DdDnDmx(unsigned int _Enc, __n64, __n64); +__n64 __neon_DdDnDmx_acc(unsigned int _Enc, __n64, __n64, __n64); +__n64 __neon_DdDnFt(unsigned int, __n64, float); +__n64 __neon_DdDnFt_acc(unsigned int, __n64, __n64, float); +__n64 __neon_DdFt(unsigned int _Enc, float); +__n64 __neon_DdFt_acc(unsigned int _Enc, __n64, float); +__n64 __neon_D1Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64 __neon_D1Adr_acc(unsigned int _Enc, __n64, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n64 __neon_DdQm(unsigned int _Enc, __n128); +__n64 __neon_DdQm_high(unsigned int _Enc, __n128); +__n64 __neon_DdQm_low(unsigned int _Enc, __n128); +__n64 __neon_DdQnQm(unsigned int _Enc, __n128, __n128); +__n64 __neon_DdRt(unsigned int _Enc, int); +__n64 __neon_DdRtRt2(unsigned int _Enc, __int64); +__n64 __neon_DdRtRt2_acc(unsigned int _Enc, __n64, __int64); +__n64 __neon_DdRt_acc(unsigned int _Enc, __n64, int); +float __neon_FtDn(unsigned int _Enc, __n64); +float __neon_FtQn(unsigned int _Enc, __n128); +__n128x2 __neon_Qx2Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128x2 __neon_Qx2Adr_acc(unsigned int _Enc, __n128x2, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128x2 __neon_QdQm_acc2(unsigned int _Enc, __n128, __n128); +__n128x2 __neon_QdQm_acc3(unsigned int _Enc, __n128, __n128); +__n128x3 __neon_Qx3Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128x3 __neon_Qx3Adr_acc(unsigned int _Enc, __n128x3, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128x4 __neon_Qx4Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128x4 __neon_Qx4Adr_acc(unsigned int _Enc, __n128x4, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128 __neon_QdDm(unsigned int _Enc, __n64); +__n128 __neon_QdDnDm(unsigned int _Enc, __n64, __n64); +__n128 __neon_QdDnDm_acc(unsigned int _Enc, __n128, __n64, __n64); +__n128 __neon_QdDnDm_merge(unsigned int _Enc, __n64, __n64); +__n128 __neon_QdDnDmx(unsigned int _Enc, __n64, __n64); +__n128 __neon_QdDnDmx_acc(unsigned int _Enc, __n128, __n64, __n64); +__n128 __neon_QdFt(unsigned int _Enc, float); +__n128 __neon_QdFt_acc(unsigned int _Enc, __n128, float); +__n128 __neon_Q1Adr(unsigned int _Enc, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128 __neon_Q1Adr_acc(unsigned int _Enc, __n128, _In_reads_bytes_(_Inexpressible_(_Enc)) const __n64*); +__n128 __neon_QdQm(unsigned int _Enc, __n128); +__n128 __neon_QdQm_acc(unsigned int _Enc, __n128, __n128); +__n128 __neon_QdQnDm(unsigned int _Enc, __n128, __n64); +__n128 __neon_QdQnDmx(unsigned int _Enc, __n128, __n64); +__n128 __neon_QdQnDmx_acc(unsigned int _Enc, __n128, __n128, __n64); +__n128 __neon_QdQnFt(unsigned int, __n128, float); +__n128 __neon_QdQnFt_acc(unsigned int, __n128, __n128, float); +__n128 __neon_QdQnQm(unsigned int _Enc, __n128, __n128); +__n128 __neon_QdQnQm_acc(unsigned int _Enc, __n128, __n128, __n128); +__n128 __neon_QdRt(unsigned int _Enc, int); +__n128 __neon_QdRtRt2_acc(unsigned int _Enc, __n128, __int64); +__n128 __neon_QdRtRt2_dup(unsigned int _Enc, __int64); +__n128 __neon_QdRt_acc(unsigned int _Enc, __n128, int); +__int64 __neon_RtRt2Dm(unsigned int _Enc, __n64); +__int64 __neon_RtRt2Qm(unsigned int _Enc, __n128); +int __neon_RtDn(unsigned int _Enc, __n64); +int __neon_RtQn(unsigned int _Enc, __n128); +void __neon_AdrD1(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n64); +void __neon_AdrDx2(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n64x2); +void __neon_AdrDx2x(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n64x2); +void __neon_AdrDx3(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n64x3); +void __neon_AdrDx3x(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n64x3); +void __neon_AdrDx4(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n64x4); +void __neon_AdrDx4x(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n64x4); +void __neon_AdrQ1(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n128); +void __neon_AdrQx2(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n128x2); +void __neon_AdrQx2x(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n128x2); +void __neon_AdrQx3(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n128x3); +void __neon_AdrQx3x(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n128x3); +void __neon_AdrQx4(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n128x4); +void __neon_AdrQx4x(unsigned int _Enc, _Out_writes_bytes_(_Inexpressible_(_Enc)) __n64*, __n128x4); + +// } +++ auto-generated code ends (prototypes) + + +#if defined (__cplusplus) +} +#endif /* defined (__cplusplus) */ + + +/////////////////////////////////////////////////////////////////////////////// +// +// VLDx/VSTx alignment specifications +// + + +#define _NEON_ALIGN16(a) \ + ( \ + ((a) == 8) ? 0 : \ + ((a) == 16) ? 1 : \ + -1) + +#define _NEON_ALIGN32(a) \ + ( \ + ((a) == 8) ? 0 : \ + ((a) == 32) ? 1 : \ + -1) + +#define _NEON_ALIGN64(a) \ + ( \ + ((a) == 8) ? 0 : \ + ((a) == 64) ? 1 : \ + -1) + +#define _NEON_ALIGN64_128(a) \ + ( \ + ((a) == 8) ? 0 : \ + ((a) == 64) ? 1 : \ + ((a) == 128) ? 2 : \ + -1) + + +#define _NEON_ALIGN64_128_256(a) \ + ( \ + ((a) == 8) ? 0 : \ + ((a) == 64) ? 1 : \ + ((a) == 128) ? 2 : \ + ((a) == 256) ? 3 : \ + -1) + + +/////////////////////////////////////////////////////////////////////////////// +// +// { +++ auto-generated code begins (encoding macros) + +#define _NENC_0(x) ((x) & 0x1) +#define _NENC_11_8(x) (((x) << 8) & 0xf00) +#define _NENC_12(x) (((x) << 12) & 0x1000) +#define _NENC_16(x) (((x) << 16) & 0x10000) +#define _NENC_18_16(x) (((x) << 16) & 0x70000) +#define _NENC_19(x) (((x) << 19) & 0x80000) +#define _NENC_19_16(x) (((x) << 16) & 0xf0000) +#define _NENC_19_17(x) (((x) << 17) & 0xe0000) +#define _NENC_19_18(x) (((x) << 18) & 0xc0000) +#define _NENC_20_16(x) (((x) << 16) & 0x1f0000) +#define _NENC_21(x) (((x) << 21) & 0x200000) +#define _NENC_21_16(x) (((x) << 16) & 0x3f0000) +#define _NENC_21x6(x) (((x) << 6) & 0x40 | ((x) << 20) & 0x200000) +#define _NENC_21x6_5(x) (((x) << 5) & 0x60 | ((x) << 19) & 0x200000) +#define _NENC_4(x) (((x) << 4) & 0x10) +#define _NENC_5(x) (((x) << 5) & 0x20) +#define _NENC_5_4(x) (((x) << 4) & 0x30) +#define _NENC_5x3(x) (((x) << 3) & 0x8 | ((x) << 4) & 0x20) +#define _NENC_7(x) (((x) << 7) & 0x80) +#define _NENC_7_5(x) (((x) << 5) & 0xe0) +#define _NENC_7_6(x) (((x) << 6) & 0xc0) + +// } +++ auto-generated code ends (encoding macros) + + +/////////////////////////////////////////////////////////////////////////////// +// +// { +++ auto-generated code begins (Neon macros) + +// AES +#define aesd_p8(Qm) ( __neon_QdQm( 0xf3b00340, (Qm)) ) +#define aesd_s8(Qm) ( __neon_QdQm( 0xf3b00340, (Qm)) ) +#define aesd_u8(Qm) ( __neon_QdQm( 0xf3b00340, (Qm)) ) +#define aese_p8(Qm) ( __neon_QdQm( 0xf3b00300, (Qm)) ) +#define aese_s8(Qm) ( __neon_QdQm( 0xf3b00300, (Qm)) ) +#define aese_u8(Qm) ( __neon_QdQm( 0xf3b00300, (Qm)) ) +#define aesimc_p8(Qm) ( __neon_QdQm( 0xf3b003c0, (Qm)) ) +#define aesimc_s8(Qm) ( __neon_QdQm( 0xf3b003c0, (Qm)) ) +#define aesimc_u8(Qm) ( __neon_QdQm( 0xf3b003c0, (Qm)) ) +#define aesmc_p8(Qm) ( __neon_QdQm( 0xf3b00380, (Qm)) ) +#define aesmc_s8(Qm) ( __neon_QdQm( 0xf3b00380, (Qm)) ) +#define aesmc_u8(Qm) ( __neon_QdQm( 0xf3b00380, (Qm)) ) + +// SHA (2-operand) +#define sha1h_f32(Qm) ( __neon_QdQm( 0xf3b902c0, (Qm)) ) +#define sha1h_s32(Qm) ( __neon_QdQm( 0xf3b902c0, (Qm)) ) +#define sha1h_u32(Qm) ( __neon_QdQm( 0xf3b902c0, (Qm)) ) +#define sha1su1_f32(Qm) ( __neon_QdQm( 0xf3ba0380, (Qm)) ) +#define sha1su1_s32(Qm) ( __neon_QdQm( 0xf3ba0380, (Qm)) ) +#define sha1su1_u32(Qm) ( __neon_QdQm( 0xf3ba0380, (Qm)) ) +#define sha256su0_f32(Qm) ( __neon_QdQm( 0xf3ba03c0, (Qm)) ) +#define sha256su0_s32(Qm) ( __neon_QdQm( 0xf3ba03c0, (Qm)) ) +#define sha256su0_u32(Qm) ( __neon_QdQm( 0xf3ba03c0, (Qm)) ) + +// SHA (3-operand) +#define sha1c_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2000c40, (Qn), (Qm)) ) +#define sha1c_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2000c40, (Qn), (Qm)) ) +#define sha1c_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2000c40, (Qn), (Qm)) ) +#define sha1m_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2200c40, (Qn), (Qm)) ) +#define sha1m_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200c40, (Qn), (Qm)) ) +#define sha1m_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2200c40, (Qn), (Qm)) ) +#define sha1p_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2100c40, (Qn), (Qm)) ) +#define sha1p_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2100c40, (Qn), (Qm)) ) +#define sha1p_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2100c40, (Qn), (Qm)) ) +#define sha1su0_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2300c40, (Qn), (Qm)) ) +#define sha1su0_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2300c40, (Qn), (Qm)) ) +#define sha1su0_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2300c40, (Qn), (Qm)) ) +#define sha256h_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3000c40, (Qn), (Qm)) ) +#define sha256h_s32(Qn, Qm) ( __neon_QdQnQm( 0xf3000c40, (Qn), (Qm)) ) +#define sha256h_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3000c40, (Qn), (Qm)) ) +#define sha256h2_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3100c40, (Qn), (Qm)) ) +#define sha256h2_s32(Qn, Qm) ( __neon_QdQnQm( 0xf3100c40, (Qn), (Qm)) ) +#define sha256h2_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3100c40, (Qn), (Qm)) ) +#define sha256su1_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3200c40, (Qn), (Qm)) ) +#define sha256su1_s32(Qn, Qm) ( __neon_QdQnQm( 0xf3200c40, (Qn), (Qm)) ) +#define sha256su1_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200c40, (Qn), (Qm)) ) + +// VABA, VABAL +#define vaba_s16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2100710, (Dd), (Dn), (Dm)) ) +#define vaba_s32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2200710, (Dd), (Dn), (Dm)) ) +#define vaba_s8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2000710, (Dd), (Dn), (Dm)) ) +#define vaba_u16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100710, (Dd), (Dn), (Dm)) ) +#define vaba_u32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200710, (Dd), (Dn), (Dm)) ) +#define vaba_u8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3000710, (Dd), (Dn), (Dm)) ) +#define vabal_s16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2900500, (Qd), (Dn), (Dm)) ) +#define vabal_s32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2a00500, (Qd), (Dn), (Dm)) ) +#define vabal_s8(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2800500, (Qd), (Dn), (Dm)) ) +#define vabal_u16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3900500, (Qd), (Dn), (Dm)) ) +#define vabal_u32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3a00500, (Qd), (Dn), (Dm)) ) +#define vabal_u8(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3800500, (Qd), (Dn), (Dm)) ) +#define vabaq_s16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2100750, (Qd), (Qn), (Qm)) ) +#define vabaq_s32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2200750, (Qd), (Qn), (Qm)) ) +#define vabaq_s8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2000750, (Qd), (Qn), (Qm)) ) +#define vabaq_u16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100750, (Qd), (Qn), (Qm)) ) +#define vabaq_u32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200750, (Qd), (Qn), (Qm)) ) +#define vabaq_u8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3000750, (Qd), (Qn), (Qm)) ) + +// VABD (floating point) +#define vabd_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3200d00, (Dn), (Dm)) ) +#define vabdq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3200d40, (Qn), (Qm)) ) + +// VABD[L] (integer) +#define vabd_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100700, (Dn), (Dm)) ) +#define vabd_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200700, (Dn), (Dm)) ) +#define vabd_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000700, (Dn), (Dm)) ) +#define vabd_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100700, (Dn), (Dm)) ) +#define vabd_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200700, (Dn), (Dm)) ) +#define vabd_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000700, (Dn), (Dm)) ) +#define vabdl_s16(Dn, Dm) ( __neon_QdDnDm( 0xf2900700, (Dn), (Dm)) ) +#define vabdl_s32(Dn, Dm) ( __neon_QdDnDm( 0xf2a00700, (Dn), (Dm)) ) +#define vabdl_s8(Dn, Dm) ( __neon_QdDnDm( 0xf2800700, (Dn), (Dm)) ) +#define vabdl_u16(Dn, Dm) ( __neon_QdDnDm( 0xf3900700, (Dn), (Dm)) ) +#define vabdl_u32(Dn, Dm) ( __neon_QdDnDm( 0xf3a00700, (Dn), (Dm)) ) +#define vabdl_u8(Dn, Dm) ( __neon_QdDnDm( 0xf3800700, (Dn), (Dm)) ) +#define vabdq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100740, (Qn), (Qm)) ) +#define vabdq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200740, (Qn), (Qm)) ) +#define vabdq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000740, (Qn), (Qm)) ) +#define vabdq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100740, (Qn), (Qm)) ) +#define vabdq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200740, (Qn), (Qm)) ) +#define vabdq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000740, (Qn), (Qm)) ) + +// VABS, VNEG +#define vabs_f32(Dm) ( __neon_DdDm( 0xf3b90700, (Dm)) ) +#define vabs_s16(Dm) ( __neon_DdDm( 0xf3b50300, (Dm)) ) +#define vabs_s32(Dm) ( __neon_DdDm( 0xf3b90300, (Dm)) ) +#define vabs_s8(Dm) ( __neon_DdDm( 0xf3b10300, (Dm)) ) +#define vneg_f32(Dm) ( __neon_DdDm( 0xf3b90780, (Dm)) ) +#define vneg_s16(Dm) ( __neon_DdDm( 0xf3b50380, (Dm)) ) +#define vneg_s32(Dm) ( __neon_DdDm( 0xf3b90380, (Dm)) ) +#define vneg_s8(Dm) ( __neon_DdDm( 0xf3b10380, (Dm)) ) +#define vabsq_f32(Qm) ( __neon_QdQm( 0xf3b90740, (Qm)) ) +#define vabsq_s16(Qm) ( __neon_QdQm( 0xf3b50340, (Qm)) ) +#define vabsq_s32(Qm) ( __neon_QdQm( 0xf3b90340, (Qm)) ) +#define vabsq_s8(Qm) ( __neon_QdQm( 0xf3b10340, (Qm)) ) +#define vnegq_f32(Qm) ( __neon_QdQm( 0xf3b907c0, (Qm)) ) +#define vnegq_s16(Qm) ( __neon_QdQm( 0xf3b503c0, (Qm)) ) +#define vnegq_s32(Qm) ( __neon_QdQm( 0xf3b903c0, (Qm)) ) +#define vnegq_s8(Qm) ( __neon_QdQm( 0xf3b103c0, (Qm)) ) + +// VACGE, VACGT, VACLE, VACLT +#define vacge_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000e10, (Dn), (Dm)) ) +#define vacgt_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3200e10, (Dn), (Dm)) ) +#define vacle_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000e10, (Dm), (Dn)) ) +#define vaclt_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3200e10, (Dm), (Dn)) ) +#define vacgeq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3000e50, (Qn), (Qm)) ) +#define vacgtq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3200e50, (Qn), (Qm)) ) +#define vacleq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3000e50, (Qm), (Qn)) ) +#define vacltq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3200e50, (Qm), (Qn)) ) + +// VADD +#define vadd_f32(Dn, Dm) ( __neon_DdDnDm( 0xf2000d00, (Dn), (Dm)) ) +#define vadd_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100800, (Dn), (Dm)) ) +#define vadd_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200800, (Dn), (Dm)) ) +#define vadd_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300800, (Dn), (Dm)) ) +#define vadd_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000800, (Dn), (Dm)) ) +#define vadd_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2100800, (Dn), (Dm)) ) +#define vadd_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2200800, (Dn), (Dm)) ) +#define vadd_u64(Dn, Dm) ( __neon_DdDnDm( 0xf2300800, (Dn), (Dm)) ) +#define vadd_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2000800, (Dn), (Dm)) ) +#define vaddq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2000d40, (Qn), (Qm)) ) +#define vaddq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100840, (Qn), (Qm)) ) +#define vaddq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200840, (Qn), (Qm)) ) +#define vaddq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300840, (Qn), (Qm)) ) +#define vaddq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000840, (Qn), (Qm)) ) +#define vaddq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf2100840, (Qn), (Qm)) ) +#define vaddq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2200840, (Qn), (Qm)) ) +#define vaddq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf2300840, (Qn), (Qm)) ) +#define vaddq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf2000840, (Qn), (Qm)) ) + +// VADDHN, VRADDHN +#define vaddhn_s16(Qn, Qm) ( __neon_DdQnQm( 0xf2800400, (Qn), (Qm)) ) +#define vaddhn_s32(Qn, Qm) ( __neon_DdQnQm( 0xf2900400, (Qn), (Qm)) ) +#define vaddhn_s64(Qn, Qm) ( __neon_DdQnQm( 0xf2a00400, (Qn), (Qm)) ) +#define vaddhn_u16(Qn, Qm) ( __neon_DdQnQm( 0xf2800400, (Qn), (Qm)) ) +#define vaddhn_u32(Qn, Qm) ( __neon_DdQnQm( 0xf2900400, (Qn), (Qm)) ) +#define vaddhn_u64(Qn, Qm) ( __neon_DdQnQm( 0xf2a00400, (Qn), (Qm)) ) +#define vraddhn_s16(Qn, Qm) ( __neon_DdQnQm( 0xf3800400, (Qn), (Qm)) ) +#define vraddhn_s32(Qn, Qm) ( __neon_DdQnQm( 0xf3900400, (Qn), (Qm)) ) +#define vraddhn_s64(Qn, Qm) ( __neon_DdQnQm( 0xf3a00400, (Qn), (Qm)) ) +#define vraddhn_u16(Qn, Qm) ( __neon_DdQnQm( 0xf3800400, (Qn), (Qm)) ) +#define vraddhn_u32(Qn, Qm) ( __neon_DdQnQm( 0xf3900400, (Qn), (Qm)) ) +#define vraddhn_u64(Qn, Qm) ( __neon_DdQnQm( 0xf3a00400, (Qn), (Qm)) ) + +// VADDL, VADDW +#define vaddl_s16(Dn, Dm) ( __neon_QdDnDm( 0xf2900000, (Dn), (Dm)) ) +#define vaddl_s32(Dn, Dm) ( __neon_QdDnDm( 0xf2a00000, (Dn), (Dm)) ) +#define vaddl_s8(Dn, Dm) ( __neon_QdDnDm( 0xf2800000, (Dn), (Dm)) ) +#define vaddl_u16(Dn, Dm) ( __neon_QdDnDm( 0xf3900000, (Dn), (Dm)) ) +#define vaddl_u32(Dn, Dm) ( __neon_QdDnDm( 0xf3a00000, (Dn), (Dm)) ) +#define vaddl_u8(Dn, Dm) ( __neon_QdDnDm( 0xf3800000, (Dn), (Dm)) ) +#define vaddw_s16(Qn, Dm) ( __neon_QdQnDm( 0xf2900100, (Qn), (Dm)) ) +#define vaddw_s32(Qn, Dm) ( __neon_QdQnDm( 0xf2a00100, (Qn), (Dm)) ) +#define vaddw_s8(Qn, Dm) ( __neon_QdQnDm( 0xf2800100, (Qn), (Dm)) ) +#define vaddw_u16(Qn, Dm) ( __neon_QdQnDm( 0xf3900100, (Qn), (Dm)) ) +#define vaddw_u32(Qn, Dm) ( __neon_QdQnDm( 0xf3a00100, (Qn), (Dm)) ) +#define vaddw_u8(Qn, Dm) ( __neon_QdQnDm( 0xf3800100, (Qn), (Dm)) ) + +// VAND, VORR +#define vand_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vand_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vand_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vand_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vand_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vand_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vand_u64(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vand_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2000110, (Dn), (Dm)) ) +#define vorr_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vorr_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vorr_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vorr_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vorr_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vorr_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vorr_u64(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vorr_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2200110, (Dn), (Dm)) ) +#define vandq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vandq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vandq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vandq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vandq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vandq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vandq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vandq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf2000150, (Qn), (Qm)) ) +#define vorrq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) +#define vorrq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) +#define vorrq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) +#define vorrq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) +#define vorrq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) +#define vorrq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) +#define vorrq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) +#define vorrq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf2200150, (Qn), (Qm)) ) + +// VBIF, VBIT, VBSL +#define vbif_f32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_p16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_p8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_s16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_s32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_s64(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_s8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_u16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_u32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_u64(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbif_u8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3300110, (Dd), (Dn), (Dm)) ) +#define vbit_f32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_p16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_p8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_s16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_s32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_s64(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_s8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_u16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_u32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_u64(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbit_u8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200110, (Dd), (Dn), (Dm)) ) +#define vbsl_f32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_p16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_p8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_s16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_s32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_s64(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_s8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_u16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_u32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_u64(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbsl_u8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100110, (Dd), (Dn), (Dm)) ) +#define vbifq_f32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_p16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_p8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_s16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_s32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_s64(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_s8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_u16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_u32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_u64(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbifq_u8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3300150, (Qd), (Qn), (Qm)) ) +#define vbitq_f32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_p16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_p8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_s16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_s32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_s64(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_s8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_u16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_u32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_u64(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbitq_u8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200150, (Qd), (Qn), (Qm)) ) +#define vbslq_f32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_p16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_p8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_s16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_s32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_s64(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_s8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_u16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_u32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_u64(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) +#define vbslq_u8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100150, (Qd), (Qn), (Qm)) ) + +// VCEQ (immediate #0) +#define vceq_z_f32_ex(Dm) ( __neon_DdDm( 0xf3b90500, (Dm)) ) +#define vceq_z_s16_ex(Dm) ( __neon_DdDm( 0xf3b50100, (Dm)) ) +#define vceq_z_s32_ex(Dm) ( __neon_DdDm( 0xf3b90100, (Dm)) ) +#define vceq_z_s8_ex(Dm) ( __neon_DdDm( 0xf3b10100, (Dm)) ) +#define vceq_z_u16_ex(Dm) ( __neon_DdDm( 0xf3b50100, (Dm)) ) +#define vceq_z_u32_ex(Dm) ( __neon_DdDm( 0xf3b90100, (Dm)) ) +#define vceq_z_u8_ex(Dm) ( __neon_DdDm( 0xf3b10100, (Dm)) ) +#define vceqq_z_f32_ex(Qm) ( __neon_QdQm( 0xf3b90540, (Qm)) ) +#define vceqq_z_s16_ex(Qm) ( __neon_QdQm( 0xf3b50140, (Qm)) ) +#define vceqq_z_s32_ex(Qm) ( __neon_QdQm( 0xf3b90140, (Qm)) ) +#define vceqq_z_s8_ex(Qm) ( __neon_QdQm( 0xf3b10140, (Qm)) ) +#define vceqq_z_u16_ex(Qm) ( __neon_QdQm( 0xf3b50140, (Qm)) ) +#define vceqq_z_u32_ex(Qm) ( __neon_QdQm( 0xf3b90140, (Qm)) ) +#define vceqq_z_u8_ex(Qm) ( __neon_QdQm( 0xf3b10140, (Qm)) ) + +// VCEQ (register) +#define vceq_f32(Dn, Dm) ( __neon_DdDnDm( 0xf2000e00, (Dn), (Dm)) ) +#define vceq_p8(Dn, Dm) ( __neon_DdDnDm( 0xf3000810, (Dn), (Dm)) ) +#define vceq_s16(Dn, Dm) ( __neon_DdDnDm( 0xf3100810, (Dn), (Dm)) ) +#define vceq_s32(Dn, Dm) ( __neon_DdDnDm( 0xf3200810, (Dn), (Dm)) ) +#define vceq_s8(Dn, Dm) ( __neon_DdDnDm( 0xf3000810, (Dn), (Dm)) ) +#define vceq_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100810, (Dn), (Dm)) ) +#define vceq_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200810, (Dn), (Dm)) ) +#define vceq_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000810, (Dn), (Dm)) ) +#define vceqq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2000e40, (Qn), (Qm)) ) +#define vceqq_p8(Qn, Qm) ( __neon_QdQnQm( 0xf3000850, (Qn), (Qm)) ) +#define vceqq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf3100850, (Qn), (Qm)) ) +#define vceqq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf3200850, (Qn), (Qm)) ) +#define vceqq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf3000850, (Qn), (Qm)) ) +#define vceqq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100850, (Qn), (Qm)) ) +#define vceqq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200850, (Qn), (Qm)) ) +#define vceqq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000850, (Qn), (Qm)) ) + +// VCGE (immediate #0) +#define vcge_z_f32_ex(Dm) ( __neon_DdDm( 0xf3b90480, (Dm)) ) +#define vcge_z_s16_ex(Dm) ( __neon_DdDm( 0xf3b50080, (Dm)) ) +#define vcge_z_s32_ex(Dm) ( __neon_DdDm( 0xf3b90080, (Dm)) ) +#define vcge_z_s8_ex(Dm) ( __neon_DdDm( 0xf3b10080, (Dm)) ) +#define vcgeq_z_f32_ex(Qm) ( __neon_QdQm( 0xf3b904c0, (Qm)) ) +#define vcgeq_z_s16_ex(Qm) ( __neon_QdQm( 0xf3b500c0, (Qm)) ) +#define vcgeq_z_s32_ex(Qm) ( __neon_QdQm( 0xf3b900c0, (Qm)) ) +#define vcgeq_z_s8_ex(Qm) ( __neon_QdQm( 0xf3b100c0, (Qm)) ) + +// VCGE, VCLE (register) +#define vcge_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000e00, (Dn), (Dm)) ) +#define vcge_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100310, (Dn), (Dm)) ) +#define vcge_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200310, (Dn), (Dm)) ) +#define vcge_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000310, (Dn), (Dm)) ) +#define vcge_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100310, (Dn), (Dm)) ) +#define vcge_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200310, (Dn), (Dm)) ) +#define vcge_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000310, (Dn), (Dm)) ) +#define vcle_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000e00, (Dm), (Dn)) ) +#define vcle_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100310, (Dm), (Dn)) ) +#define vcle_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200310, (Dm), (Dn)) ) +#define vcle_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000310, (Dm), (Dn)) ) +#define vcle_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100310, (Dm), (Dn)) ) +#define vcle_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200310, (Dm), (Dn)) ) +#define vcle_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000310, (Dm), (Dn)) ) +#define vcgeq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3000e40, (Qn), (Qm)) ) +#define vcgeq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100350, (Qn), (Qm)) ) +#define vcgeq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200350, (Qn), (Qm)) ) +#define vcgeq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000350, (Qn), (Qm)) ) +#define vcgeq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100350, (Qn), (Qm)) ) +#define vcgeq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200350, (Qn), (Qm)) ) +#define vcgeq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000350, (Qn), (Qm)) ) +#define vcleq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3000e40, (Qm), (Qn)) ) +#define vcleq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100350, (Qm), (Qn)) ) +#define vcleq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200350, (Qm), (Qn)) ) +#define vcleq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000350, (Qm), (Qn)) ) +#define vcleq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100350, (Qm), (Qn)) ) +#define vcleq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200350, (Qm), (Qn)) ) +#define vcleq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000350, (Qm), (Qn)) ) + +// VCGT (immediate #0) +#define vcgt_z_f32_ex(Dm) ( __neon_DdDm( 0xf3b90400, (Dm)) ) +#define vcgt_z_s16_ex(Dm) ( __neon_DdDm( 0xf3b50000, (Dm)) ) +#define vcgt_z_s32_ex(Dm) ( __neon_DdDm( 0xf3b90000, (Dm)) ) +#define vcgt_z_s8_ex(Dm) ( __neon_DdDm( 0xf3b10000, (Dm)) ) +#define vcgtq_z_f32_ex(Qm) ( __neon_QdQm( 0xf3b90440, (Qm)) ) +#define vcgtq_z_s16_ex(Qm) ( __neon_QdQm( 0xf3b50040, (Qm)) ) +#define vcgtq_z_s32_ex(Qm) ( __neon_QdQm( 0xf3b90040, (Qm)) ) +#define vcgtq_z_s8_ex(Qm) ( __neon_QdQm( 0xf3b10040, (Qm)) ) + +// VCGT, VCLT (register) +#define vcgt_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3200e00, (Dn), (Dm)) ) +#define vcgt_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100300, (Dn), (Dm)) ) +#define vcgt_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200300, (Dn), (Dm)) ) +#define vcgt_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000300, (Dn), (Dm)) ) +#define vcgt_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100300, (Dn), (Dm)) ) +#define vcgt_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200300, (Dn), (Dm)) ) +#define vcgt_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000300, (Dn), (Dm)) ) +#define vclt_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3200e00, (Dm), (Dn)) ) +#define vclt_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100300, (Dm), (Dn)) ) +#define vclt_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200300, (Dm), (Dn)) ) +#define vclt_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000300, (Dm), (Dn)) ) +#define vclt_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100300, (Dm), (Dn)) ) +#define vclt_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200300, (Dm), (Dn)) ) +#define vclt_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000300, (Dm), (Dn)) ) +#define vcgtq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3200e40, (Qn), (Qm)) ) +#define vcgtq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100340, (Qn), (Qm)) ) +#define vcgtq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200340, (Qn), (Qm)) ) +#define vcgtq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000340, (Qn), (Qm)) ) +#define vcgtq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100340, (Qn), (Qm)) ) +#define vcgtq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200340, (Qn), (Qm)) ) +#define vcgtq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000340, (Qn), (Qm)) ) +#define vcltq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3200e40, (Qm), (Qn)) ) +#define vcltq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100340, (Qm), (Qn)) ) +#define vcltq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200340, (Qm), (Qn)) ) +#define vcltq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000340, (Qm), (Qn)) ) +#define vcltq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100340, (Qm), (Qn)) ) +#define vcltq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200340, (Qm), (Qn)) ) +#define vcltq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000340, (Qm), (Qn)) ) + +// VCLE (immediate #0) +#define vcle_z_f32_ex(Dm) ( __neon_DdDm( 0xf3b90580, (Dm)) ) +#define vcle_z_s16_ex(Dm) ( __neon_DdDm( 0xf3b50180, (Dm)) ) +#define vcle_z_s32_ex(Dm) ( __neon_DdDm( 0xf3b90180, (Dm)) ) +#define vcle_z_s8_ex(Dm) ( __neon_DdDm( 0xf3b10180, (Dm)) ) +#define vcleq_z_f32_ex(Qm) ( __neon_QdQm( 0xf3b905c0, (Qm)) ) +#define vcleq_z_s16_ex(Qm) ( __neon_QdQm( 0xf3b501c0, (Qm)) ) +#define vcleq_z_s32_ex(Qm) ( __neon_QdQm( 0xf3b901c0, (Qm)) ) +#define vcleq_z_s8_ex(Qm) ( __neon_QdQm( 0xf3b101c0, (Qm)) ) + +// VCLS, VCLZ +#define vcls_s16(Dm) ( __neon_DdDm( 0xf3b40400, (Dm)) ) +#define vcls_s32(Dm) ( __neon_DdDm( 0xf3b80400, (Dm)) ) +#define vcls_s8(Dm) ( __neon_DdDm( 0xf3b00400, (Dm)) ) +#define vclz_s16(Dm) ( __neon_DdDm( 0xf3b40480, (Dm)) ) +#define vclz_s32(Dm) ( __neon_DdDm( 0xf3b80480, (Dm)) ) +#define vclz_s8(Dm) ( __neon_DdDm( 0xf3b00480, (Dm)) ) +#define vclz_u16(Dm) ( __neon_DdDm( 0xf3b40480, (Dm)) ) +#define vclz_u32(Dm) ( __neon_DdDm( 0xf3b80480, (Dm)) ) +#define vclz_u8(Dm) ( __neon_DdDm( 0xf3b00480, (Dm)) ) +#define vclsq_s16(Qm) ( __neon_QdQm( 0xf3b40440, (Qm)) ) +#define vclsq_s32(Qm) ( __neon_QdQm( 0xf3b80440, (Qm)) ) +#define vclsq_s8(Qm) ( __neon_QdQm( 0xf3b00440, (Qm)) ) +#define vclzq_s16(Qm) ( __neon_QdQm( 0xf3b404c0, (Qm)) ) +#define vclzq_s32(Qm) ( __neon_QdQm( 0xf3b804c0, (Qm)) ) +#define vclzq_s8(Qm) ( __neon_QdQm( 0xf3b004c0, (Qm)) ) +#define vclzq_u16(Qm) ( __neon_QdQm( 0xf3b404c0, (Qm)) ) +#define vclzq_u32(Qm) ( __neon_QdQm( 0xf3b804c0, (Qm)) ) +#define vclzq_u8(Qm) ( __neon_QdQm( 0xf3b004c0, (Qm)) ) + +// VCLT (immediate #0) +#define vclt_z_f32_ex(Dm) ( __neon_DdDm( 0xf3b90600, (Dm)) ) +#define vclt_z_s16_ex(Dm) ( __neon_DdDm( 0xf3b50200, (Dm)) ) +#define vclt_z_s32_ex(Dm) ( __neon_DdDm( 0xf3b90200, (Dm)) ) +#define vclt_z_s8_ex(Dm) ( __neon_DdDm( 0xf3b10200, (Dm)) ) +#define vcltq_z_f32_ex(Qm) ( __neon_QdQm( 0xf3b90640, (Qm)) ) +#define vcltq_z_s16_ex(Qm) ( __neon_QdQm( 0xf3b50240, (Qm)) ) +#define vcltq_z_s32_ex(Qm) ( __neon_QdQm( 0xf3b90240, (Qm)) ) +#define vcltq_z_s8_ex(Qm) ( __neon_QdQm( 0xf3b10240, (Qm)) ) + +// VCNT +#define vcnt_p8(Dm) ( __neon_DdDm( 0xf3b00500, (Dm)) ) +#define vcnt_s8(Dm) ( __neon_DdDm( 0xf3b00500, (Dm)) ) +#define vcnt_u8(Dm) ( __neon_DdDm( 0xf3b00500, (Dm)) ) +#define vcntq_p8(Qm) ( __neon_QdQm( 0xf3b00540, (Qm)) ) +#define vcntq_s8(Qm) ( __neon_QdQm( 0xf3b00540, (Qm)) ) +#define vcntq_u8(Qm) ( __neon_QdQm( 0xf3b00540, (Qm)) ) + +// VCOMBINE (combine 2x64bit into a 128bit register) +#define vcombine_f32(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_p16(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_p8(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_s16(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_s32(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_s64(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_s8(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_u16(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_u32(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_u64(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) +#define vcombine_u8(Dn, Dm) ( __neon_QdDnDm_merge( 0x00000000, (Dn), (Dm)) ) + +// VCREATE (ARM core register pair to Neon 64bit register) +#define vcreate_f32(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_p16(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_p8(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_s16(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_s32(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_s64(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_s8(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_u16(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_u32(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_u64(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vcreate_u8(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) + +// VCVT (between floating-point and fixed-point) +#define vcvt_n_f32_s32(Dm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_DdDm( 0xf2800e10 | _NENC_21_16(64 - (fbits)), (Dm)) ) +#define vcvt_n_f32_u32(Dm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_DdDm( 0xf3800e10 | _NENC_21_16(64 - (fbits)), (Dm)) ) +#define vcvt_n_s32_f32(Dm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_DdDm( 0xf2800f10 | _NENC_21_16(64 - (fbits)), (Dm)) ) +#define vcvt_n_u32_f32(Dm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_DdDm( 0xf3800f10 | _NENC_21_16(64 - (fbits)), (Dm)) ) +#define vcvtq_n_f32_s32(Qm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_QdQm( 0xf2800e50 | _NENC_21_16(64 - (fbits)), (Qm)) ) +#define vcvtq_n_f32_u32(Qm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_QdQm( 0xf3800e50 | _NENC_21_16(64 - (fbits)), (Qm)) ) +#define vcvtq_n_s32_f32(Qm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_QdQm( 0xf2800f50 | _NENC_21_16(64 - (fbits)), (Qm)) ) +#define vcvtq_n_u32_f32(Qm, fbits) ( __static_assert((fbits) >= 1 && (fbits) <= 32, "invalid fbits value"), __neon_QdQm( 0xf3800f50 | _NENC_21_16(64 - (fbits)), (Qm)) ) + +// VCVT (between floating-point and integer with directed rounding) +#define vcvta_s32_f32(Dm) ( __neon_DdDm( 0xf3bb0000, (Dm)) ) +#define vcvta_u32_f32(Dm) ( __neon_DdDm( 0xf3bb0080, (Dm)) ) +#define vcvtm_s32_f32(Dm) ( __neon_DdDm( 0xf3bb0300, (Dm)) ) +#define vcvtm_u32_f32(Dm) ( __neon_DdDm( 0xf3bb0380, (Dm)) ) +#define vcvtn_s32_f32(Dm) ( __neon_DdDm( 0xf3bb0100, (Dm)) ) +#define vcvtn_u32_f32(Dm) ( __neon_DdDm( 0xf3bb0180, (Dm)) ) +#define vcvtp_s32_f32(Dm) ( __neon_DdDm( 0xf3bb0200, (Dm)) ) +#define vcvtp_u32_f32(Dm) ( __neon_DdDm( 0xf3bb0280, (Dm)) ) +#define vcvtaq_s32_f32(Qm) ( __neon_QdQm( 0xf3bb0040, (Qm)) ) +#define vcvtaq_u32_f32(Qm) ( __neon_QdQm( 0xf3bb00c0, (Qm)) ) +#define vcvtmq_s32_f32(Qm) ( __neon_QdQm( 0xf3bb0340, (Qm)) ) +#define vcvtmq_u32_f32(Qm) ( __neon_QdQm( 0xf3bb03c0, (Qm)) ) +#define vcvtnq_s32_f32(Qm) ( __neon_QdQm( 0xf3bb0140, (Qm)) ) +#define vcvtnq_u32_f32(Qm) ( __neon_QdQm( 0xf3bb01c0, (Qm)) ) +#define vcvtpq_s32_f32(Qm) ( __neon_QdQm( 0xf3bb0240, (Qm)) ) +#define vcvtpq_u32_f32(Qm) ( __neon_QdQm( 0xf3bb02c0, (Qm)) ) + +// VCVT (between floating-point and integer) +#define vcvt_f32_s32(Dm) ( __neon_DdDm( 0xf3bb0600, (Dm)) ) +#define vcvt_f32_u32(Dm) ( __neon_DdDm( 0xf3bb0680, (Dm)) ) +#define vcvt_s32_f32(Dm) ( __neon_DdDm( 0xf3bb0700, (Dm)) ) +#define vcvt_u32_f32(Dm) ( __neon_DdDm( 0xf3bb0780, (Dm)) ) +#define vcvtq_f32_s32(Qm) ( __neon_QdQm( 0xf3bb0640, (Qm)) ) +#define vcvtq_f32_u32(Qm) ( __neon_QdQm( 0xf3bb06c0, (Qm)) ) +#define vcvtq_s32_f32(Qm) ( __neon_QdQm( 0xf3bb0740, (Qm)) ) +#define vcvtq_u32_f32(Qm) ( __neon_QdQm( 0xf3bb07c0, (Qm)) ) + +// VDUP (scalar) +#define vdup_lane_f32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDm( 0xf3b40c00 | _NENC_19(lane), (Dm)) ) +#define vdup_lane_p16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDm( 0xf3b20c00 | _NENC_19_18(lane), (Dm)) ) +#define vdup_lane_p8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_DdDm( 0xf3b10c00 | _NENC_19_17(lane), (Dm)) ) +#define vdup_lane_s16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDm( 0xf3b20c00 | _NENC_19_18(lane), (Dm)) ) +#define vdup_lane_s32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDm( 0xf3b40c00 | _NENC_19(lane), (Dm)) ) +#define vdup_lane_s8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_DdDm( 0xf3b10c00 | _NENC_19_17(lane), (Dm)) ) +#define vdup_lane_u16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDm( 0xf3b20c00 | _NENC_19_18(lane), (Dm)) ) +#define vdup_lane_u32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDm( 0xf3b40c00 | _NENC_19(lane), (Dm)) ) +#define vdup_lane_u8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_DdDm( 0xf3b10c00 | _NENC_19_17(lane), (Dm)) ) +#define vdupq_lane_f32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDm( 0xf3b40c40 | _NENC_19(lane), (Dm)) ) +#define vdupq_lane_p16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDm( 0xf3b20c40 | _NENC_19_18(lane), (Dm)) ) +#define vdupq_lane_p8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_QdDm( 0xf3b10c40 | _NENC_19_17(lane), (Dm)) ) +#define vdupq_lane_s16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDm( 0xf3b20c40 | _NENC_19_18(lane), (Dm)) ) +#define vdupq_lane_s32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDm( 0xf3b40c40 | _NENC_19(lane), (Dm)) ) +#define vdupq_lane_s8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_QdDm( 0xf3b10c40 | _NENC_19_17(lane), (Dm)) ) +#define vdupq_lane_u16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDm( 0xf3b20c40 | _NENC_19_18(lane), (Dm)) ) +#define vdupq_lane_u32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDm( 0xf3b40c40 | _NENC_19(lane), (Dm)) ) +#define vdupq_lane_u8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_QdDm( 0xf3b10c40 | _NENC_19_17(lane), (Dm)) ) + +// VDUP, VMOV (ARM core register to Neon register) +#define vdup_n_f32(Ft) ( __neon_DdFt( 0xee800b10, (Ft)) ) +#define vmov_n_f32(Ft) ( __neon_DdFt( 0xee800b10, (Ft)) ) +#define vdup_n_p16(Rt) ( __neon_DdRt( 0xee800b30, __poly16ToInt32(Rt)) ) +#define vdup_n_p8(Rt) ( __neon_DdRt( 0xeec00b10, __poly8ToInt32(Rt)) ) +#define vdup_n_s16(Rt) ( __neon_DdRt( 0xee800b30, __int16ToInt32(Rt)) ) +#define vdup_n_s32(Rt) ( __neon_DdRt( 0xee800b10, __int32ToInt32(Rt)) ) +#define vdup_n_s8(Rt) ( __neon_DdRt( 0xeec00b10, __int8ToInt32(Rt)) ) +#define vdup_n_u16(Rt) ( __neon_DdRt( 0xee800b30, __uint16ToInt32(Rt)) ) +#define vdup_n_u32(Rt) ( __neon_DdRt( 0xee800b10, __uint32ToInt32(Rt)) ) +#define vdup_n_u8(Rt) ( __neon_DdRt( 0xeec00b10, __uint8ToInt32(Rt)) ) +#define vmov_n_p16(Rt) ( __neon_DdRt( 0xee800b30, __poly16ToInt32(Rt)) ) +#define vmov_n_p8(Rt) ( __neon_DdRt( 0xeec00b10, __poly8ToInt32(Rt)) ) +#define vmov_n_s16(Rt) ( __neon_DdRt( 0xee800b30, __int16ToInt32(Rt)) ) +#define vmov_n_s32(Rt) ( __neon_DdRt( 0xee800b10, __int32ToInt32(Rt)) ) +#define vmov_n_s8(Rt) ( __neon_DdRt( 0xeec00b10, __int8ToInt32(Rt)) ) +#define vmov_n_u16(Rt) ( __neon_DdRt( 0xee800b30, __uint16ToInt32(Rt)) ) +#define vmov_n_u32(Rt) ( __neon_DdRt( 0xee800b10, __uint32ToInt32(Rt)) ) +#define vmov_n_u8(Rt) ( __neon_DdRt( 0xeec00b10, __uint8ToInt32(Rt)) ) +#define vdupq_n_f32(Ft) ( __neon_QdFt( 0xeea00b10, (Ft)) ) +#define vmovq_n_f32(Ft) ( __neon_QdFt( 0xeea00b10, (Ft)) ) +#define vdupq_n_p16(Rt) ( __neon_QdRt( 0xeea00b30, __poly16ToInt32(Rt)) ) +#define vdupq_n_p8(Rt) ( __neon_QdRt( 0xeee00b10, __poly8ToInt32(Rt)) ) +#define vdupq_n_s16(Rt) ( __neon_QdRt( 0xeea00b30, __int16ToInt32(Rt)) ) +#define vdupq_n_s32(Rt) ( __neon_QdRt( 0xeea00b10, __int32ToInt32(Rt)) ) +#define vdupq_n_s8(Rt) ( __neon_QdRt( 0xeee00b10, __int8ToInt32(Rt)) ) +#define vdupq_n_u16(Rt) ( __neon_QdRt( 0xeea00b30, __uint16ToInt32(Rt)) ) +#define vdupq_n_u32(Rt) ( __neon_QdRt( 0xeea00b10, __uint32ToInt32(Rt)) ) +#define vdupq_n_u8(Rt) ( __neon_QdRt( 0xeee00b10, __uint8ToInt32(Rt)) ) +#define vmovq_n_p16(Rt) ( __neon_QdRt( 0xeea00b30, __poly16ToInt32(Rt)) ) +#define vmovq_n_p8(Rt) ( __neon_QdRt( 0xeee00b10, __poly8ToInt32(Rt)) ) +#define vmovq_n_s16(Rt) ( __neon_QdRt( 0xeea00b30, __int16ToInt32(Rt)) ) +#define vmovq_n_s32(Rt) ( __neon_QdRt( 0xeea00b10, __int32ToInt32(Rt)) ) +#define vmovq_n_s8(Rt) ( __neon_QdRt( 0xeee00b10, __int8ToInt32(Rt)) ) +#define vmovq_n_u16(Rt) ( __neon_QdRt( 0xeea00b30, __uint16ToInt32(Rt)) ) +#define vmovq_n_u32(Rt) ( __neon_QdRt( 0xeea00b10, __uint32ToInt32(Rt)) ) +#define vmovq_n_u8(Rt) ( __neon_QdRt( 0xeee00b10, __uint8ToInt32(Rt)) ) + +// VDUP.64, VMOV.64 (ARM core register pair to Neon registers) +#define vdup_n_s64(R64t) ( __neon_DdRtRt2( 0xec400b10, __int64ToInt64(R64t)) ) +#define vdup_n_u64(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vmov_n_s64(R64t) ( __neon_DdRtRt2( 0xec400b10, __int64ToInt64(R64t)) ) +#define vmov_n_u64(R64t) ( __neon_DdRtRt2( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vdupq_n_s64(R64t) ( __neon_QdRtRt2_dup( 0xec400b10, __int64ToInt64(R64t)) ) +#define vdupq_n_u64(R64t) ( __neon_QdRtRt2_dup( 0xec400b10, __uint64ToInt64(R64t)) ) +#define vmovq_n_s64(R64t) ( __neon_QdRtRt2_dup( 0xec400b10, __int64ToInt64(R64t)) ) +#define vmovq_n_u64(R64t) ( __neon_QdRtRt2_dup( 0xec400b10, __uint64ToInt64(R64t)) ) + +// VEOR, VBIC, VORN +#define vbic_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define vbic_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define vbic_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define vbic_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define vbic_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define vbic_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define vbic_u64(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define vbic_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2100110, (Dn), (Dm)) ) +#define veor_s16(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define veor_s32(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define veor_s64(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define veor_s8(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define veor_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define veor_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define veor_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define veor_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000110, (Dn), (Dm)) ) +#define vorn_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vorn_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vorn_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vorn_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vorn_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vorn_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vorn_u64(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vorn_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2300110, (Dn), (Dm)) ) +#define vbicq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define vbicq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define vbicq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define vbicq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define vbicq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define vbicq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define vbicq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define vbicq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf2100150, (Qn), (Qm)) ) +#define veorq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define veorq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define veorq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define veorq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define veorq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define veorq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define veorq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define veorq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000150, (Qn), (Qm)) ) +#define vornq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) +#define vornq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) +#define vornq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) +#define vornq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) +#define vornq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) +#define vornq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) +#define vornq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) +#define vornq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf2300150, (Qn), (Qm)) ) + +// VEXT +#define vext_f32(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 2, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 4), (Dn), (Dm)) ) +#define vext_p16(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 4, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 2), (Dn), (Dm)) ) +#define vext_p8(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 8, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8(pos), (Dn), (Dm)) ) +#define vext_s16(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 4, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 2), (Dn), (Dm)) ) +#define vext_s32(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 2, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 4), (Dn), (Dm)) ) +#define vext_s64(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 1, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 8), (Dn), (Dm)) ) +#define vext_s8(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 8, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8(pos), (Dn), (Dm)) ) +#define vext_u16(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 4, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 2), (Dn), (Dm)) ) +#define vext_u32(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 2, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 4), (Dn), (Dm)) ) +#define vext_u64(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 1, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8((pos) * 8), (Dn), (Dm)) ) +#define vext_u8(Dn, Dm, pos) ( __static_assert((pos) >= 0 && (pos) < 8, "invalid position value"), __neon_DdDnDm( 0xf2b00000 | _NENC_11_8(pos), (Dn), (Dm)) ) +#define vextq_f32(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 4, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 4), (Qn), (Qm)) ) +#define vextq_p16(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 8, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 2), (Qn), (Qm)) ) +#define vextq_p8(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 16, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8(pos), (Qn), (Qm)) ) +#define vextq_s16(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 8, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 2), (Qn), (Qm)) ) +#define vextq_s32(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 4, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 4), (Qn), (Qm)) ) +#define vextq_s64(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 2, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 8), (Qn), (Qm)) ) +#define vextq_s8(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 16, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8(pos), (Qn), (Qm)) ) +#define vextq_u16(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 8, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 2), (Qn), (Qm)) ) +#define vextq_u32(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 4, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 4), (Qn), (Qm)) ) +#define vextq_u64(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 2, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8((pos) * 8), (Qn), (Qm)) ) +#define vextq_u8(Qn, Qm, pos) ( __static_assert((pos) >= 0 && (pos) < 16, "invalid position value"), __neon_QdQnQm( 0xf2b00040 | _NENC_11_8(pos), (Qn), (Qm)) ) + +// VGET (access the 64bit high/low part of a 128bit register) +#define vget_high_f32(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_p16(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_p8(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_s16(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_s32(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_s64(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_s8(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_u16(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_u32(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_u64(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_high_u8(Qm) ( __neon_DdQm_high( 0x00000000, (Qm)) ) +#define vget_low_f32(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_p16(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_p8(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_s16(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_s32(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_s64(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_s8(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_u16(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_u32(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_u64(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) +#define vget_low_u8(Qm) ( __neon_DdQm_low( 0x00000000, (Qm)) ) + +// VHADD, VRHADD, VHSUB +#define vhadd_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100000, (Dn), (Dm)) ) +#define vhadd_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200000, (Dn), (Dm)) ) +#define vhadd_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000000, (Dn), (Dm)) ) +#define vhadd_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100000, (Dn), (Dm)) ) +#define vhadd_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200000, (Dn), (Dm)) ) +#define vhadd_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000000, (Dn), (Dm)) ) +#define vhsub_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100200, (Dn), (Dm)) ) +#define vhsub_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200200, (Dn), (Dm)) ) +#define vhsub_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000200, (Dn), (Dm)) ) +#define vhsub_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100200, (Dn), (Dm)) ) +#define vhsub_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200200, (Dn), (Dm)) ) +#define vhsub_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000200, (Dn), (Dm)) ) +#define vrhadd_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100100, (Dn), (Dm)) ) +#define vrhadd_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200100, (Dn), (Dm)) ) +#define vrhadd_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000100, (Dn), (Dm)) ) +#define vrhadd_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100100, (Dn), (Dm)) ) +#define vrhadd_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200100, (Dn), (Dm)) ) +#define vrhadd_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000100, (Dn), (Dm)) ) +#define vhaddq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100040, (Qn), (Qm)) ) +#define vhaddq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200040, (Qn), (Qm)) ) +#define vhaddq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000040, (Qn), (Qm)) ) +#define vhaddq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100040, (Qn), (Qm)) ) +#define vhaddq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200040, (Qn), (Qm)) ) +#define vhaddq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000040, (Qn), (Qm)) ) +#define vhsubq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100240, (Qn), (Qm)) ) +#define vhsubq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200240, (Qn), (Qm)) ) +#define vhsubq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000240, (Qn), (Qm)) ) +#define vhsubq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100240, (Qn), (Qm)) ) +#define vhsubq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200240, (Qn), (Qm)) ) +#define vhsubq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000240, (Qn), (Qm)) ) +#define vrhaddq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100140, (Qn), (Qm)) ) +#define vrhaddq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200140, (Qn), (Qm)) ) +#define vrhaddq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000140, (Qn), (Qm)) ) +#define vrhaddq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100140, (Qn), (Qm)) ) +#define vrhaddq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200140, (Qn), (Qm)) ) +#define vrhaddq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000140, (Qn), (Qm)) ) + +// VLD1 (multiple single elements) +#define vld1_f32(pcD) ( __neon_D1Adr( 0xf420078f, __float32ToN64_c(pcD)) ) +#define vld1_p16(pcD) ( __neon_D1Adr( 0xf420074f, __poly16ToN64_c(pcD)) ) +#define vld1_p8(pcD) ( __neon_D1Adr( 0xf420070f, __poly8ToN64_c(pcD)) ) +#define vld1_s16(pcD) ( __neon_D1Adr( 0xf420074f, __int16ToN64_c(pcD)) ) +#define vld1_s32(pcD) ( __neon_D1Adr( 0xf420078f, __int32ToN64_c(pcD)) ) +#define vld1_s64(pcD) ( __neon_D1Adr( 0xf42007cf, __int64ToN64_c(pcD)) ) +#define vld1_s8(pcD) ( __neon_D1Adr( 0xf420070f, __int8ToN64_c(pcD)) ) +#define vld1_u16(pcD) ( __neon_D1Adr( 0xf420074f, __uint16ToN64_c(pcD)) ) +#define vld1_u32(pcD) ( __neon_D1Adr( 0xf420078f, __uint32ToN64_c(pcD)) ) +#define vld1_u64(pcD) ( __neon_D1Adr( 0xf42007cf, __uint64ToN64_c(pcD)) ) +#define vld1_u8(pcD) ( __neon_D1Adr( 0xf420070f, __uint8ToN64_c(pcD)) ) +#define vld1_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420078f | _NENC_5_4(_NEON_ALIGN64(align)), __float32ToN64_c(pcD)) ) +#define vld1_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420074f | _NENC_5_4(_NEON_ALIGN64(align)), __poly16ToN64_c(pcD)) ) +#define vld1_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420070f | _NENC_5_4(_NEON_ALIGN64(align)), __poly8ToN64_c(pcD)) ) +#define vld1_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420074f | _NENC_5_4(_NEON_ALIGN64(align)), __int16ToN64_c(pcD)) ) +#define vld1_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420078f | _NENC_5_4(_NEON_ALIGN64(align)), __int32ToN64_c(pcD)) ) +#define vld1_s64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf42007cf | _NENC_5_4(_NEON_ALIGN64(align)), __int64ToN64_c(pcD)) ) +#define vld1_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420070f | _NENC_5_4(_NEON_ALIGN64(align)), __int8ToN64_c(pcD)) ) +#define vld1_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420074f | _NENC_5_4(_NEON_ALIGN64(align)), __uint16ToN64_c(pcD)) ) +#define vld1_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420078f | _NENC_5_4(_NEON_ALIGN64(align)), __uint32ToN64_c(pcD)) ) +#define vld1_u64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf42007cf | _NENC_5_4(_NEON_ALIGN64(align)), __uint64ToN64_c(pcD)) ) +#define vld1_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_D1Adr( 0xf420070f | _NENC_5_4(_NEON_ALIGN64(align)), __uint8ToN64_c(pcD)) ) +#define vld1q_f32(pcD) ( __neon_Q1Adr( 0xf4200a8f, __float32ToN64_c(pcD)) ) +#define vld1q_p16(pcD) ( __neon_Q1Adr( 0xf4200a4f, __poly16ToN64_c(pcD)) ) +#define vld1q_p8(pcD) ( __neon_Q1Adr( 0xf4200a0f, __poly8ToN64_c(pcD)) ) +#define vld1q_s16(pcD) ( __neon_Q1Adr( 0xf4200a4f, __int16ToN64_c(pcD)) ) +#define vld1q_s32(pcD) ( __neon_Q1Adr( 0xf4200a8f, __int32ToN64_c(pcD)) ) +#define vld1q_s64(pcD) ( __neon_Q1Adr( 0xf4200acf, __int64ToN64_c(pcD)) ) +#define vld1q_s8(pcD) ( __neon_Q1Adr( 0xf4200a0f, __int8ToN64_c(pcD)) ) +#define vld1q_u16(pcD) ( __neon_Q1Adr( 0xf4200a4f, __uint16ToN64_c(pcD)) ) +#define vld1q_u32(pcD) ( __neon_Q1Adr( 0xf4200a8f, __uint32ToN64_c(pcD)) ) +#define vld1q_u64(pcD) ( __neon_Q1Adr( 0xf4200acf, __uint64ToN64_c(pcD)) ) +#define vld1q_u8(pcD) ( __neon_Q1Adr( 0xf4200a0f, __uint8ToN64_c(pcD)) ) +#define vld1q_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a8f | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64_c(pcD)) ) +#define vld1q_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a4f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly16ToN64_c(pcD)) ) +#define vld1q_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a0f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly8ToN64_c(pcD)) ) +#define vld1q_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a4f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int16ToN64_c(pcD)) ) +#define vld1q_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a8f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64_c(pcD)) ) +#define vld1q_s64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __int64ToN64_c(pcD)) ) +#define vld1q_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a0f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int8ToN64_c(pcD)) ) +#define vld1q_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a4f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint16ToN64_c(pcD)) ) +#define vld1q_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a8f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64_c(pcD)) ) +#define vld1q_u64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint64ToN64_c(pcD)) ) +#define vld1q_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4200a0f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint8ToN64_c(pcD)) ) + +// VLD1 (single element to all lanes) +#define vld1_dup_f32(pcD) ( __neon_D1Adr( 0xf4a00c8f, __float32ToN64_c(pcD)) ) +#define vld1_dup_p16(pcD) ( __neon_D1Adr( 0xf4a00c4f, __poly16ToN64_c(pcD)) ) +#define vld1_dup_p8(pcD) ( __neon_D1Adr( 0xf4a00c0f, __poly8ToN64_c(pcD)) ) +#define vld1_dup_s16(pcD) ( __neon_D1Adr( 0xf4a00c4f, __int16ToN64_c(pcD)) ) +#define vld1_dup_s32(pcD) ( __neon_D1Adr( 0xf4a00c8f, __int32ToN64_c(pcD)) ) +#define vld1_dup_s8(pcD) ( __neon_D1Adr( 0xf4a00c0f, __int8ToN64_c(pcD)) ) +#define vld1_dup_u16(pcD) ( __neon_D1Adr( 0xf4a00c4f, __uint16ToN64_c(pcD)) ) +#define vld1_dup_u32(pcD) ( __neon_D1Adr( 0xf4a00c8f, __uint32ToN64_c(pcD)) ) +#define vld1_dup_u8(pcD) ( __neon_D1Adr( 0xf4a00c0f, __uint8ToN64_c(pcD)) ) +#define vld1q_dup_f32(pcD) ( __neon_Q1Adr( 0xf4a00caf, __float32ToN64_c(pcD)) ) +#define vld1q_dup_p16(pcD) ( __neon_Q1Adr( 0xf4a00c6f, __poly16ToN64_c(pcD)) ) +#define vld1q_dup_p8(pcD) ( __neon_Q1Adr( 0xf4a00c2f, __poly8ToN64_c(pcD)) ) +#define vld1q_dup_s16(pcD) ( __neon_Q1Adr( 0xf4a00c6f, __int16ToN64_c(pcD)) ) +#define vld1q_dup_s32(pcD) ( __neon_Q1Adr( 0xf4a00caf, __int32ToN64_c(pcD)) ) +#define vld1q_dup_s8(pcD) ( __neon_Q1Adr( 0xf4a00c2f, __int8ToN64_c(pcD)) ) +#define vld1q_dup_u16(pcD) ( __neon_Q1Adr( 0xf4a00c6f, __uint16ToN64_c(pcD)) ) +#define vld1q_dup_u32(pcD) ( __neon_Q1Adr( 0xf4a00caf, __uint32ToN64_c(pcD)) ) +#define vld1q_dup_u8(pcD) ( __neon_Q1Adr( 0xf4a00c2f, __uint8ToN64_c(pcD)) ) + +// VLD1 (single element to all lanes, aligned) +#define vld1_dup_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_D1Adr( 0xf4a00c8f | _NENC_4(_NEON_ALIGN32(align)), __float32ToN64_c(pcD)) ) +#define vld1_dup_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_D1Adr( 0xf4a00c4f | _NENC_4(_NEON_ALIGN16(align)), __poly16ToN64_c(pcD)) ) +#define vld1_dup_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_D1Adr( 0xf4a00c4f | _NENC_4(_NEON_ALIGN16(align)), __int16ToN64_c(pcD)) ) +#define vld1_dup_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_D1Adr( 0xf4a00c8f | _NENC_4(_NEON_ALIGN32(align)), __int32ToN64_c(pcD)) ) +#define vld1_dup_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_D1Adr( 0xf4a00c4f | _NENC_4(_NEON_ALIGN16(align)), __uint16ToN64_c(pcD)) ) +#define vld1_dup_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_D1Adr( 0xf4a00c8f | _NENC_4(_NEON_ALIGN32(align)), __uint32ToN64_c(pcD)) ) +#define vld1q_dup_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4a00caf | _NENC_4(_NEON_ALIGN32(align)), __float32ToN64_c(pcD)) ) +#define vld1q_dup_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4a00c6f | _NENC_4(_NEON_ALIGN16(align)), __poly16ToN64_c(pcD)) ) +#define vld1q_dup_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4a00c6f | _NENC_4(_NEON_ALIGN16(align)), __int16ToN64_c(pcD)) ) +#define vld1q_dup_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4a00caf | _NENC_4(_NEON_ALIGN32(align)), __int32ToN64_c(pcD)) ) +#define vld1q_dup_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4a00c6f | _NENC_4(_NEON_ALIGN16(align)), __uint16ToN64_c(pcD)) ) +#define vld1q_dup_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Q1Adr( 0xf4a00caf | _NENC_4(_NEON_ALIGN32(align)), __uint32ToN64_c(pcD)) ) + +// VLD1 (single element to one lane) +#define vld1_lane_f32(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0080f | _NENC_7(lane), (Dd), __float32ToN64_c(pcD)) ) +#define vld1_lane_p16(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0040f | _NENC_7_6(lane), (Dd), __poly16ToN64_c(pcD)) ) +#define vld1_lane_p8(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0000f | _NENC_7_5(lane), (Dd), __poly8ToN64_c(pcD)) ) +#define vld1_lane_s16(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0040f | _NENC_7_6(lane), (Dd), __int16ToN64_c(pcD)) ) +#define vld1_lane_s32(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0080f | _NENC_7(lane), (Dd), __int32ToN64_c(pcD)) ) +#define vld1_lane_s8(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0000f | _NENC_7_5(lane), (Dd), __int8ToN64_c(pcD)) ) +#define vld1_lane_u16(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0040f | _NENC_7_6(lane), (Dd), __uint16ToN64_c(pcD)) ) +#define vld1_lane_u32(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0080f | _NENC_7(lane), (Dd), __uint32ToN64_c(pcD)) ) +#define vld1_lane_u8(pcD, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_D1Adr_acc( 0xf4a0000f | _NENC_7_5(lane), (Dd), __uint8ToN64_c(pcD)) ) +#define vld1q_lane_f32(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Qd), __float32ToN64_c(pcD)) ) +#define vld1q_lane_p16(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Qd), __poly16ToN64_c(pcD)) ) +#define vld1q_lane_p8(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0000f | _NENC_7_5((lane) % 8) | _NENC_12((lane) >= 8 ? 1 : 0), (Qd), __poly8ToN64_c(pcD)) ) +#define vld1q_lane_s16(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Qd), __int16ToN64_c(pcD)) ) +#define vld1q_lane_s32(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Qd), __int32ToN64_c(pcD)) ) +#define vld1q_lane_s8(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0000f | _NENC_7_5((lane) % 8) | _NENC_12((lane) >= 8 ? 1 : 0), (Qd), __int8ToN64_c(pcD)) ) +#define vld1q_lane_u16(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Qd), __uint16ToN64_c(pcD)) ) +#define vld1q_lane_u32(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Qd), __uint32ToN64_c(pcD)) ) +#define vld1q_lane_u8(pcD, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_Q1Adr_acc( 0xf4a0000f | _NENC_7_5((lane) % 8) | _NENC_12((lane) >= 8 ? 1 : 0), (Qd), __uint8ToN64_c(pcD)) ) + +// VLD1 (single element to one lane, aligned) +#define vld1_lane_f32_ex(pcD, Dd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_D1Adr_acc( 0xf4a0080f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), (Dd), __float32ToN64_c(pcD)) ) +#define vld1_lane_p16_ex(pcD, Dd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_D1Adr_acc( 0xf4a0040f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN16(align)), (Dd), __poly16ToN64_c(pcD)) ) +#define vld1_lane_s16_ex(pcD, Dd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_D1Adr_acc( 0xf4a0040f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN16(align)), (Dd), __int16ToN64_c(pcD)) ) +#define vld1_lane_s32_ex(pcD, Dd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_D1Adr_acc( 0xf4a0080f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), (Dd), __int32ToN64_c(pcD)) ) +#define vld1_lane_u16_ex(pcD, Dd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_D1Adr_acc( 0xf4a0040f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN16(align)), (Dd), __uint16ToN64_c(pcD)) ) +#define vld1_lane_u32_ex(pcD, Dd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_D1Adr_acc( 0xf4a0080f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), (Dd), __uint32ToN64_c(pcD)) ) +#define vld1q_lane_f32_ex(pcD, Qd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Q1Adr_acc( 0xf4a0080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), (Qd), __float32ToN64_c(pcD)) ) +#define vld1q_lane_p16_ex(pcD, Qd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Q1Adr_acc( 0xf4a0040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN16(align)), (Qd), __poly16ToN64_c(pcD)) ) +#define vld1q_lane_s16_ex(pcD, Qd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Q1Adr_acc( 0xf4a0040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN16(align)), (Qd), __int16ToN64_c(pcD)) ) +#define vld1q_lane_s32_ex(pcD, Qd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Q1Adr_acc( 0xf4a0080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), (Qd), __int32ToN64_c(pcD)) ) +#define vld1q_lane_u16_ex(pcD, Qd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Q1Adr_acc( 0xf4a0040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN16(align)), (Qd), __uint16ToN64_c(pcD)) ) +#define vld1q_lane_u32_ex(pcD, Qd, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Q1Adr_acc( 0xf4a0080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), (Qd), __uint32ToN64_c(pcD)) ) + +// VLD2 (multiple 2-element structures) +#define vld2_f32(pcD) ( __neon_Dx2Adr( 0xf420088f, __float32ToN64_c(pcD)) ) +#define vld2_p16(pcD) ( __neon_Dx2Adr( 0xf420084f, __poly16ToN64_c(pcD)) ) +#define vld2_p8(pcD) ( __neon_Dx2Adr( 0xf420080f, __poly8ToN64_c(pcD)) ) +#define vld2_s16(pcD) ( __neon_Dx2Adr( 0xf420084f, __int16ToN64_c(pcD)) ) +#define vld2_s32(pcD) ( __neon_Dx2Adr( 0xf420088f, __int32ToN64_c(pcD)) ) +#define vld2_s8(pcD) ( __neon_Dx2Adr( 0xf420080f, __int8ToN64_c(pcD)) ) +#define vld2_u16(pcD) ( __neon_Dx2Adr( 0xf420084f, __uint16ToN64_c(pcD)) ) +#define vld2_u32(pcD) ( __neon_Dx2Adr( 0xf420088f, __uint32ToN64_c(pcD)) ) +#define vld2_u8(pcD) ( __neon_Dx2Adr( 0xf420080f, __uint8ToN64_c(pcD)) ) +#define vld2_s64(pcD) ( __neon_Dx2Adr( 0xf4200acf, __int64ToN64_c(pcD)) ) +#define vld2_u64(pcD) ( __neon_Dx2Adr( 0xf4200acf, __uint64ToN64_c(pcD)) ) +#define vld2_s64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4200acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __int64ToN64_c(pcD)) ) +#define vld2_u64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4200acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint64ToN64_c(pcD)) ) +#define vld2_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420088f | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64_c(pcD)) ) +#define vld2_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420084f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly16ToN64_c(pcD)) ) +#define vld2_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420080f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly8ToN64_c(pcD)) ) +#define vld2_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420084f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int16ToN64_c(pcD)) ) +#define vld2_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420088f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64_c(pcD)) ) +#define vld2_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420080f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int8ToN64_c(pcD)) ) +#define vld2_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420084f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint16ToN64_c(pcD)) ) +#define vld2_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420088f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64_c(pcD)) ) +#define vld2_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf420080f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint8ToN64_c(pcD)) ) +#define vld2q_f32(pcD) ( __neon_Qx2Adr( 0xf420098f, __float32ToN64_c(pcD)) ) +#define vld2q_p16(pcD) ( __neon_Qx2Adr( 0xf420094f, __poly16ToN64_c(pcD)) ) +#define vld2q_p8(pcD) ( __neon_Qx2Adr( 0xf420090f, __poly8ToN64_c(pcD)) ) +#define vld2q_s16(pcD) ( __neon_Qx2Adr( 0xf420094f, __int16ToN64_c(pcD)) ) +#define vld2q_s32(pcD) ( __neon_Qx2Adr( 0xf420098f, __int32ToN64_c(pcD)) ) +#define vld2q_s8(pcD) ( __neon_Qx2Adr( 0xf420090f, __int8ToN64_c(pcD)) ) +#define vld2q_u16(pcD) ( __neon_Qx2Adr( 0xf420094f, __uint16ToN64_c(pcD)) ) +#define vld2q_u32(pcD) ( __neon_Qx2Adr( 0xf420098f, __uint32ToN64_c(pcD)) ) +#define vld2q_u8(pcD) ( __neon_Qx2Adr( 0xf420090f, __uint8ToN64_c(pcD)) ) +#define vld2q_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420098f | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64_c(pcD)) ) +#define vld2q_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420094f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly16ToN64_c(pcD)) ) +#define vld2q_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420090f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly8ToN64_c(pcD)) ) +#define vld2q_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420094f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int16ToN64_c(pcD)) ) +#define vld2q_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420098f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64_c(pcD)) ) +#define vld2q_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420090f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int8ToN64_c(pcD)) ) +#define vld2q_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420094f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint16ToN64_c(pcD)) ) +#define vld2q_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420098f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64_c(pcD)) ) +#define vld2q_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx2Adr( 0xf420090f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint8ToN64_c(pcD)) ) + +// VLD2 (single 2-element structure to all lanes) +#define vld2_dup_f32(pcD) ( __neon_Dx2Adr( 0xf4a00d8f, __float32ToN64_c(pcD)) ) +#define vld2_dup_p16(pcD) ( __neon_Dx2Adr( 0xf4a00d4f, __poly16ToN64_c(pcD)) ) +#define vld2_dup_p8(pcD) ( __neon_Dx2Adr( 0xf4a00d0f, __poly8ToN64_c(pcD)) ) +#define vld2_dup_s16(pcD) ( __neon_Dx2Adr( 0xf4a00d4f, __int16ToN64_c(pcD)) ) +#define vld2_dup_s32(pcD) ( __neon_Dx2Adr( 0xf4a00d8f, __int32ToN64_c(pcD)) ) +#define vld2_dup_s8(pcD) ( __neon_Dx2Adr( 0xf4a00d0f, __int8ToN64_c(pcD)) ) +#define vld2_dup_u16(pcD) ( __neon_Dx2Adr( 0xf4a00d4f, __uint16ToN64_c(pcD)) ) +#define vld2_dup_u32(pcD) ( __neon_Dx2Adr( 0xf4a00d8f, __uint32ToN64_c(pcD)) ) +#define vld2_dup_u8(pcD) ( __neon_Dx2Adr( 0xf4a00d0f, __uint8ToN64_c(pcD)) ) +#define vld2_dup_s64(pcD) ( __neon_Dx2Adr( 0xf4200acf, __int64ToN64_c(pcD)) ) +#define vld2_dup_u64(pcD) ( __neon_Dx2Adr( 0xf4200acf, __uint64ToN64_c(pcD)) ) + +// VLD2 (single 2-element structure to all lanes, aligned) +#define vld2_dup_s64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4200acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __int64ToN64_c(pcD)) ) +#define vld2_dup_u64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4200acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint64ToN64_c(pcD)) ) +#define vld2_dup_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d8f | _NENC_4(_NEON_ALIGN64(align)), __float32ToN64_c(pcD)) ) +#define vld2_dup_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d4f | _NENC_4(_NEON_ALIGN32(align)), __poly16ToN64_c(pcD)) ) +#define vld2_dup_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d0f | _NENC_4(_NEON_ALIGN16(align)), __poly8ToN64_c(pcD)) ) +#define vld2_dup_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d4f | _NENC_4(_NEON_ALIGN32(align)), __int16ToN64_c(pcD)) ) +#define vld2_dup_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d8f | _NENC_4(_NEON_ALIGN64(align)), __int32ToN64_c(pcD)) ) +#define vld2_dup_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d0f | _NENC_4(_NEON_ALIGN16(align)), __int8ToN64_c(pcD)) ) +#define vld2_dup_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d4f | _NENC_4(_NEON_ALIGN32(align)), __uint16ToN64_c(pcD)) ) +#define vld2_dup_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d8f | _NENC_4(_NEON_ALIGN64(align)), __uint32ToN64_c(pcD)) ) +#define vld2_dup_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Dx2Adr( 0xf4a00d0f | _NENC_4(_NEON_ALIGN16(align)), __uint8ToN64_c(pcD)) ) + +// VLD2 (single 2-element structure to one lane) +#define vld2_lane_f32(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0090f | _NENC_7(lane), (D2), __float32ToN64_c(pcD)) ) +#define vld2_lane_p16(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0050f | _NENC_7_6(lane), (D2), __poly16ToN64_c(pcD)) ) +#define vld2_lane_p8(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0010f | _NENC_7_5(lane), (D2), __poly8ToN64_c(pcD)) ) +#define vld2_lane_s16(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0050f | _NENC_7_6(lane), (D2), __int16ToN64_c(pcD)) ) +#define vld2_lane_s32(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0090f | _NENC_7(lane), (D2), __int32ToN64_c(pcD)) ) +#define vld2_lane_s8(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0010f | _NENC_7_5(lane), (D2), __int8ToN64_c(pcD)) ) +#define vld2_lane_u16(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0050f | _NENC_7_6(lane), (D2), __uint16ToN64_c(pcD)) ) +#define vld2_lane_u32(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0090f | _NENC_7(lane), (D2), __uint32ToN64_c(pcD)) ) +#define vld2_lane_u8(pcD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx2Adr_acc( 0xf4a0010f | _NENC_7_5(lane), (D2), __uint8ToN64_c(pcD)) ) +#define vld2q_lane_f32(pcD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx2Adr_acc( 0xf4a0094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q2), __float32ToN64_c(pcD)) ) +#define vld2q_lane_p16(pcD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx2Adr_acc( 0xf4a0052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q2), __poly16ToN64_c(pcD)) ) +#define vld2q_lane_s16(pcD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx2Adr_acc( 0xf4a0052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q2), __int16ToN64_c(pcD)) ) +#define vld2q_lane_s32(pcD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx2Adr_acc( 0xf4a0094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q2), __int32ToN64_c(pcD)) ) +#define vld2q_lane_u16(pcD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx2Adr_acc( 0xf4a0052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q2), __uint16ToN64_c(pcD)) ) +#define vld2q_lane_u32(pcD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx2Adr_acc( 0xf4a0094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q2), __uint32ToN64_c(pcD)) ) + +// VLD2 (single 2-element structure to one lane, aligned) +#define vld2_lane_f32_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0090f | _NENC_7(lane) | _NENC_4(_NEON_ALIGN64(align)), (D2), __float32ToN64_c(pcD)) ) +#define vld2_lane_p16_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0050f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN32(align)), (D2), __poly16ToN64_c(pcD)) ) +#define vld2_lane_p8_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0010f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN16(align)), (D2), __poly8ToN64_c(pcD)) ) +#define vld2_lane_s16_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0050f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN32(align)), (D2), __int16ToN64_c(pcD)) ) +#define vld2_lane_s32_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0090f | _NENC_7(lane) | _NENC_4(_NEON_ALIGN64(align)), (D2), __int32ToN64_c(pcD)) ) +#define vld2_lane_s8_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0010f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN16(align)), (D2), __int8ToN64_c(pcD)) ) +#define vld2_lane_u16_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0050f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN32(align)), (D2), __uint16ToN64_c(pcD)) ) +#define vld2_lane_u32_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0090f | _NENC_7(lane) | _NENC_4(_NEON_ALIGN64(align)), (D2), __uint32ToN64_c(pcD)) ) +#define vld2_lane_u8_ex(pcD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_Dx2Adr_acc( 0xf4a0010f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN16(align)), (D2), __uint8ToN64_c(pcD)) ) +#define vld2q_lane_f32_ex(pcD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx2Adr_acc( 0xf4a0094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), (Q2), __float32ToN64_c(pcD)) ) +#define vld2q_lane_p16_ex(pcD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Qx2Adr_acc( 0xf4a0052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN32(align)), (Q2), __poly16ToN64_c(pcD)) ) +#define vld2q_lane_s16_ex(pcD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Qx2Adr_acc( 0xf4a0052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN32(align)), (Q2), __int16ToN64_c(pcD)) ) +#define vld2q_lane_s32_ex(pcD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx2Adr_acc( 0xf4a0094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), (Q2), __int32ToN64_c(pcD)) ) +#define vld2q_lane_u16_ex(pcD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Qx2Adr_acc( 0xf4a0052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN32(align)), (Q2), __uint16ToN64_c(pcD)) ) +#define vld2q_lane_u32_ex(pcD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx2Adr_acc( 0xf4a0094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), (Q2), __uint32ToN64_c(pcD)) ) + +// VLD3 (multiple 3-element structures) +#define vld3_f32(pcD) ( __neon_Dx3Adr( 0xf420048f, __float32ToN64_c(pcD)) ) +#define vld3_p16(pcD) ( __neon_Dx3Adr( 0xf420044f, __poly16ToN64_c(pcD)) ) +#define vld3_p8(pcD) ( __neon_Dx3Adr( 0xf420040f, __poly8ToN64_c(pcD)) ) +#define vld3_s16(pcD) ( __neon_Dx3Adr( 0xf420044f, __int16ToN64_c(pcD)) ) +#define vld3_s32(pcD) ( __neon_Dx3Adr( 0xf420048f, __int32ToN64_c(pcD)) ) +#define vld3_s8(pcD) ( __neon_Dx3Adr( 0xf420040f, __int8ToN64_c(pcD)) ) +#define vld3_u16(pcD) ( __neon_Dx3Adr( 0xf420044f, __uint16ToN64_c(pcD)) ) +#define vld3_u32(pcD) ( __neon_Dx3Adr( 0xf420048f, __uint32ToN64_c(pcD)) ) +#define vld3_u8(pcD) ( __neon_Dx3Adr( 0xf420040f, __uint8ToN64_c(pcD)) ) +#define vld3_s64(pcD) ( __neon_Dx3Adr( 0xf42006cf, __int64ToN64_c(pcD)) ) +#define vld3_u64(pcD) ( __neon_Dx3Adr( 0xf42006cf, __uint64ToN64_c(pcD)) ) +#define vld3_s64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf42006cf | _NENC_5_4(_NEON_ALIGN64(align)), __int64ToN64_c(pcD)) ) +#define vld3_u64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf42006cf | _NENC_5_4(_NEON_ALIGN64(align)), __uint64ToN64_c(pcD)) ) +#define vld3_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420048f | _NENC_5_4(_NEON_ALIGN64(align)), __float32ToN64_c(pcD)) ) +#define vld3_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420044f | _NENC_5_4(_NEON_ALIGN64(align)), __poly16ToN64_c(pcD)) ) +#define vld3_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420040f | _NENC_5_4(_NEON_ALIGN64(align)), __poly8ToN64_c(pcD)) ) +#define vld3_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420044f | _NENC_5_4(_NEON_ALIGN64(align)), __int16ToN64_c(pcD)) ) +#define vld3_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420048f | _NENC_5_4(_NEON_ALIGN64(align)), __int32ToN64_c(pcD)) ) +#define vld3_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420040f | _NENC_5_4(_NEON_ALIGN64(align)), __int8ToN64_c(pcD)) ) +#define vld3_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420044f | _NENC_5_4(_NEON_ALIGN64(align)), __uint16ToN64_c(pcD)) ) +#define vld3_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420048f | _NENC_5_4(_NEON_ALIGN64(align)), __uint32ToN64_c(pcD)) ) +#define vld3_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx3Adr( 0xf420040f | _NENC_5_4(_NEON_ALIGN64(align)), __uint8ToN64_c(pcD)) ) +#define vld3q_f32(pcD) ( __neon_Qx3Adr( 0xf420058f, __float32ToN64_c(pcD)) ) +#define vld3q_p16(pcD) ( __neon_Qx3Adr( 0xf420054f, __poly16ToN64_c(pcD)) ) +#define vld3q_p8(pcD) ( __neon_Qx3Adr( 0xf420050f, __poly8ToN64_c(pcD)) ) +#define vld3q_s16(pcD) ( __neon_Qx3Adr( 0xf420054f, __int16ToN64_c(pcD)) ) +#define vld3q_s32(pcD) ( __neon_Qx3Adr( 0xf420058f, __int32ToN64_c(pcD)) ) +#define vld3q_s8(pcD) ( __neon_Qx3Adr( 0xf420050f, __int8ToN64_c(pcD)) ) +#define vld3q_u16(pcD) ( __neon_Qx3Adr( 0xf420054f, __uint16ToN64_c(pcD)) ) +#define vld3q_u32(pcD) ( __neon_Qx3Adr( 0xf420058f, __uint32ToN64_c(pcD)) ) +#define vld3q_u8(pcD) ( __neon_Qx3Adr( 0xf420050f, __uint8ToN64_c(pcD)) ) +#define vld3q_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420058f | _NENC_5_4(_NEON_ALIGN64(align)), __float32ToN64_c(pcD)) ) +#define vld3q_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420054f | _NENC_5_4(_NEON_ALIGN64(align)), __poly16ToN64_c(pcD)) ) +#define vld3q_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420050f | _NENC_5_4(_NEON_ALIGN64(align)), __poly8ToN64_c(pcD)) ) +#define vld3q_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420054f | _NENC_5_4(_NEON_ALIGN64(align)), __int16ToN64_c(pcD)) ) +#define vld3q_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420058f | _NENC_5_4(_NEON_ALIGN64(align)), __int32ToN64_c(pcD)) ) +#define vld3q_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420050f | _NENC_5_4(_NEON_ALIGN64(align)), __int8ToN64_c(pcD)) ) +#define vld3q_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420054f | _NENC_5_4(_NEON_ALIGN64(align)), __uint16ToN64_c(pcD)) ) +#define vld3q_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420058f | _NENC_5_4(_NEON_ALIGN64(align)), __uint32ToN64_c(pcD)) ) +#define vld3q_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx3Adr( 0xf420050f | _NENC_5_4(_NEON_ALIGN64(align)), __uint8ToN64_c(pcD)) ) + +// VLD3 (single 3-element structure to all lanes) +#define vld3_dup_f32(pcD) ( __neon_Dx3Adr( 0xf4a00e8f, __float32ToN64_c(pcD)) ) +#define vld3_dup_p16(pcD) ( __neon_Dx3Adr( 0xf4a00e4f, __poly16ToN64_c(pcD)) ) +#define vld3_dup_p8(pcD) ( __neon_Dx3Adr( 0xf4a00e0f, __poly8ToN64_c(pcD)) ) +#define vld3_dup_s16(pcD) ( __neon_Dx3Adr( 0xf4a00e4f, __int16ToN64_c(pcD)) ) +#define vld3_dup_s32(pcD) ( __neon_Dx3Adr( 0xf4a00e8f, __int32ToN64_c(pcD)) ) +#define vld3_dup_s8(pcD) ( __neon_Dx3Adr( 0xf4a00e0f, __int8ToN64_c(pcD)) ) +#define vld3_dup_u16(pcD) ( __neon_Dx3Adr( 0xf4a00e4f, __uint16ToN64_c(pcD)) ) +#define vld3_dup_u32(pcD) ( __neon_Dx3Adr( 0xf4a00e8f, __uint32ToN64_c(pcD)) ) +#define vld3_dup_u8(pcD) ( __neon_Dx3Adr( 0xf4a00e0f, __uint8ToN64_c(pcD)) ) +#define vld3_dup_s64(pcD) ( __neon_Dx3Adr( 0xf42006cf, __int64ToN64_c(pcD)) ) +#define vld3_dup_u64(pcD) ( __neon_Dx3Adr( 0xf42006cf, __uint64ToN64_c(pcD)) ) + +// VLD3 (single 3-element structure to one lane) +#define vld3_lane_f32(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a00a0f | _NENC_7(lane), (D3), __float32ToN64_c(pcD)) ) +#define vld3_lane_p16(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a0060f | _NENC_7_6(lane), (D3), __poly16ToN64_c(pcD)) ) +#define vld3_lane_p8(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a0020f | _NENC_7_5(lane), (D3), __poly8ToN64_c(pcD)) ) +#define vld3_lane_s16(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a0060f | _NENC_7_6(lane), (D3), __int16ToN64_c(pcD)) ) +#define vld3_lane_s32(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a00a0f | _NENC_7(lane), (D3), __int32ToN64_c(pcD)) ) +#define vld3_lane_s8(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a0020f | _NENC_7_5(lane), (D3), __int8ToN64_c(pcD)) ) +#define vld3_lane_u16(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a0060f | _NENC_7_6(lane), (D3), __uint16ToN64_c(pcD)) ) +#define vld3_lane_u32(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a00a0f | _NENC_7(lane), (D3), __uint32ToN64_c(pcD)) ) +#define vld3_lane_u8(pcD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx3Adr_acc( 0xf4a0020f | _NENC_7_5(lane), (D3), __uint8ToN64_c(pcD)) ) +#define vld3q_lane_f32(pcD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx3Adr_acc( 0xf4a00a4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q3), __float32ToN64_c(pcD)) ) +#define vld3q_lane_p16(pcD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx3Adr_acc( 0xf4a0062f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q3), __poly16ToN64_c(pcD)) ) +#define vld3q_lane_s16(pcD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx3Adr_acc( 0xf4a0062f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q3), __int16ToN64_c(pcD)) ) +#define vld3q_lane_s32(pcD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx3Adr_acc( 0xf4a00a4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q3), __int32ToN64_c(pcD)) ) +#define vld3q_lane_u16(pcD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx3Adr_acc( 0xf4a0062f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q3), __uint16ToN64_c(pcD)) ) +#define vld3q_lane_u32(pcD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx3Adr_acc( 0xf4a00a4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q3), __uint32ToN64_c(pcD)) ) + +// VLD4 (multiple 4-element structures) +#define vld4_f32(pcD) ( __neon_Dx4Adr( 0xf420008f, __float32ToN64_c(pcD)) ) +#define vld4_p16(pcD) ( __neon_Dx4Adr( 0xf420004f, __poly16ToN64_c(pcD)) ) +#define vld4_p8(pcD) ( __neon_Dx4Adr( 0xf420000f, __poly8ToN64_c(pcD)) ) +#define vld4_s16(pcD) ( __neon_Dx4Adr( 0xf420004f, __int16ToN64_c(pcD)) ) +#define vld4_s32(pcD) ( __neon_Dx4Adr( 0xf420008f, __int32ToN64_c(pcD)) ) +#define vld4_s8(pcD) ( __neon_Dx4Adr( 0xf420000f, __int8ToN64_c(pcD)) ) +#define vld4_u16(pcD) ( __neon_Dx4Adr( 0xf420004f, __uint16ToN64_c(pcD)) ) +#define vld4_u32(pcD) ( __neon_Dx4Adr( 0xf420008f, __uint32ToN64_c(pcD)) ) +#define vld4_u8(pcD) ( __neon_Dx4Adr( 0xf420000f, __uint8ToN64_c(pcD)) ) +#define vld4_s64(pcD) ( __neon_Dx4Adr( 0xf42002cf, __int64ToN64_c(pcD)) ) +#define vld4_u64(pcD) ( __neon_Dx4Adr( 0xf42002cf, __uint64ToN64_c(pcD)) ) +#define vld4_s64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf42002cf | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int64ToN64_c(pcD)) ) +#define vld4_u64_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf42002cf | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint64ToN64_c(pcD)) ) +#define vld4_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420008f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __float32ToN64_c(pcD)) ) +#define vld4_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420004f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly16ToN64_c(pcD)) ) +#define vld4_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420000f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly8ToN64_c(pcD)) ) +#define vld4_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420004f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int16ToN64_c(pcD)) ) +#define vld4_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420008f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int32ToN64_c(pcD)) ) +#define vld4_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420000f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int8ToN64_c(pcD)) ) +#define vld4_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420004f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint16ToN64_c(pcD)) ) +#define vld4_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420008f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint32ToN64_c(pcD)) ) +#define vld4_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf420000f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint8ToN64_c(pcD)) ) +#define vld4q_f32(pcD) ( __neon_Qx4Adr( 0xf420018f, __float32ToN64_c(pcD)) ) +#define vld4q_p16(pcD) ( __neon_Qx4Adr( 0xf420014f, __poly16ToN64_c(pcD)) ) +#define vld4q_p8(pcD) ( __neon_Qx4Adr( 0xf420010f, __poly8ToN64_c(pcD)) ) +#define vld4q_s16(pcD) ( __neon_Qx4Adr( 0xf420014f, __int16ToN64_c(pcD)) ) +#define vld4q_s32(pcD) ( __neon_Qx4Adr( 0xf420018f, __int32ToN64_c(pcD)) ) +#define vld4q_s8(pcD) ( __neon_Qx4Adr( 0xf420010f, __int8ToN64_c(pcD)) ) +#define vld4q_u16(pcD) ( __neon_Qx4Adr( 0xf420014f, __uint16ToN64_c(pcD)) ) +#define vld4q_u32(pcD) ( __neon_Qx4Adr( 0xf420018f, __uint32ToN64_c(pcD)) ) +#define vld4q_u8(pcD) ( __neon_Qx4Adr( 0xf420010f, __uint8ToN64_c(pcD)) ) +#define vld4q_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420018f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __float32ToN64_c(pcD)) ) +#define vld4q_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420014f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly16ToN64_c(pcD)) ) +#define vld4q_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420010f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly8ToN64_c(pcD)) ) +#define vld4q_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420014f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int16ToN64_c(pcD)) ) +#define vld4q_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420018f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int32ToN64_c(pcD)) ) +#define vld4q_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420010f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int8ToN64_c(pcD)) ) +#define vld4q_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420014f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint16ToN64_c(pcD)) ) +#define vld4q_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420018f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint32ToN64_c(pcD)) ) +#define vld4q_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_Qx4Adr( 0xf420010f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint8ToN64_c(pcD)) ) + +// VLD4 (single 4-element structure to all lanes) +#define vld4_dup_f32(pcD) ( __neon_Dx4Adr( 0xf4a00f8f, __float32ToN64_c(pcD)) ) +#define vld4_dup_p16(pcD) ( __neon_Dx4Adr( 0xf4a00f4f, __poly16ToN64_c(pcD)) ) +#define vld4_dup_p8(pcD) ( __neon_Dx4Adr( 0xf4a00f0f, __poly8ToN64_c(pcD)) ) +#define vld4_dup_s16(pcD) ( __neon_Dx4Adr( 0xf4a00f4f, __int16ToN64_c(pcD)) ) +#define vld4_dup_s32(pcD) ( __neon_Dx4Adr( 0xf4a00f8f, __int32ToN64_c(pcD)) ) +#define vld4_dup_s8(pcD) ( __neon_Dx4Adr( 0xf4a00f0f, __int8ToN64_c(pcD)) ) +#define vld4_dup_u16(pcD) ( __neon_Dx4Adr( 0xf4a00f4f, __uint16ToN64_c(pcD)) ) +#define vld4_dup_u32(pcD) ( __neon_Dx4Adr( 0xf4a00f8f, __uint32ToN64_c(pcD)) ) +#define vld4_dup_u8(pcD) ( __neon_Dx4Adr( 0xf4a00f0f, __uint8ToN64_c(pcD)) ) +#define vld4_dup_s64(pcD) ( __neon_Dx4Adr( 0xf42002cf, __int64ToN64_c(pcD)) ) +#define vld4_dup_u64(pcD) ( __neon_Dx4Adr( 0xf42002cf, __uint64ToN64_c(pcD)) ) + +// VLD4 (single 4-element structure to all lanes, aligned) +#define vld4_dup_f32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f0f | _NENC_7_6(_NEON_ALIGN64_128(align) > 1 ? 3 : 2) | _NENC_4(_NEON_ALIGN64_128(align) > 0 ? 1 : 0), __float32ToN64_c(pcD)) ) +#define vld4_dup_p16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f4f | _NENC_4(_NEON_ALIGN64(align)), __poly16ToN64_c(pcD)) ) +#define vld4_dup_p8_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f0f | _NENC_4(_NEON_ALIGN32(align)), __poly8ToN64_c(pcD)) ) +#define vld4_dup_s16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f4f | _NENC_4(_NEON_ALIGN64(align)), __int16ToN64_c(pcD)) ) +#define vld4_dup_s32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f0f | _NENC_7_6(_NEON_ALIGN64_128(align) > 1 ? 3 : 2) | _NENC_4(_NEON_ALIGN64_128(align) > 0 ? 1 : 0), __int32ToN64_c(pcD)) ) +#define vld4_dup_s8_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f0f | _NENC_4(_NEON_ALIGN32(align)), __int8ToN64_c(pcD)) ) +#define vld4_dup_u16_ex(pcD, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f4f | _NENC_4(_NEON_ALIGN64(align)), __uint16ToN64_c(pcD)) ) +#define vld4_dup_u32_ex(pcD, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f0f | _NENC_7_6(_NEON_ALIGN64_128(align) > 1 ? 3 : 2) | _NENC_4(_NEON_ALIGN64_128(align) > 0 ? 1 : 0), __uint32ToN64_c(pcD)) ) +#define vld4_dup_u8_ex(pcD, align) ( __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx4Adr( 0xf4a00f0f | _NENC_4(_NEON_ALIGN32(align)), __uint8ToN64_c(pcD)) ) + +// VLD4 (single 4-element structure to one lane) +#define vld4_lane_f32(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a00b0f | _NENC_7(lane), (D4), __float32ToN64_c(pcD)) ) +#define vld4_lane_p16(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a0070f | _NENC_7_6(lane), (D4), __poly16ToN64_c(pcD)) ) +#define vld4_lane_p8(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a0030f | _NENC_7_5(lane), (D4), __poly8ToN64_c(pcD)) ) +#define vld4_lane_s16(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a0070f | _NENC_7_6(lane), (D4), __int16ToN64_c(pcD)) ) +#define vld4_lane_s32(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a00b0f | _NENC_7(lane), (D4), __int32ToN64_c(pcD)) ) +#define vld4_lane_s8(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a0030f | _NENC_7_5(lane), (D4), __int8ToN64_c(pcD)) ) +#define vld4_lane_u16(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a0070f | _NENC_7_6(lane), (D4), __uint16ToN64_c(pcD)) ) +#define vld4_lane_u32(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a00b0f | _NENC_7(lane), (D4), __uint32ToN64_c(pcD)) ) +#define vld4_lane_u8(pcD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Dx4Adr_acc( 0xf4a0030f | _NENC_7_5(lane), (D4), __uint8ToN64_c(pcD)) ) +#define vld4q_lane_f32(pcD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx4Adr_acc( 0xf4a00b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q4), __float32ToN64_c(pcD)) ) +#define vld4q_lane_p16(pcD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx4Adr_acc( 0xf4a0072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q4), __poly16ToN64_c(pcD)) ) +#define vld4q_lane_s16(pcD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx4Adr_acc( 0xf4a0072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q4), __int16ToN64_c(pcD)) ) +#define vld4q_lane_s32(pcD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx4Adr_acc( 0xf4a00b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q4), __int32ToN64_c(pcD)) ) +#define vld4q_lane_u16(pcD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_Qx4Adr_acc( 0xf4a0072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), (Q4), __uint16ToN64_c(pcD)) ) +#define vld4q_lane_u32(pcD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_Qx4Adr_acc( 0xf4a00b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), (Q4), __uint32ToN64_c(pcD)) ) + +// VLD4 (single 4-element structure to one lane, aligned) +#define vld4_lane_f32_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a00b0f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN64_128(align)), (D4), __float32ToN64_c(pcD)) ) +#define vld4_lane_p16_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a0070f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN64(align)), (D4), __poly16ToN64_c(pcD)) ) +#define vld4_lane_p8_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a0030f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN32(align)), (D4), __poly8ToN64_c(pcD)) ) +#define vld4_lane_s16_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a0070f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN64(align)), (D4), __int16ToN64_c(pcD)) ) +#define vld4_lane_s32_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a00b0f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN64_128(align)), (D4), __int32ToN64_c(pcD)) ) +#define vld4_lane_s8_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a0030f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN32(align)), (D4), __int8ToN64_c(pcD)) ) +#define vld4_lane_u16_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a0070f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN64(align)), (D4), __uint16ToN64_c(pcD)) ) +#define vld4_lane_u32_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a00b0f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN64_128(align)), (D4), __uint32ToN64_c(pcD)) ) +#define vld4_lane_u8_ex(pcD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_Dx4Adr_acc( 0xf4a0030f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN32(align)), (D4), __uint8ToN64_c(pcD)) ) +#define vld4q_lane_f32_ex(pcD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx4Adr_acc( 0xf4a00b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN64_128(align)), (Q4), __float32ToN64_c(pcD)) ) +#define vld4q_lane_p16_ex(pcD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx4Adr_acc( 0xf4a0072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), (Q4), __poly16ToN64_c(pcD)) ) +#define vld4q_lane_s16_ex(pcD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx4Adr_acc( 0xf4a0072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), (Q4), __int16ToN64_c(pcD)) ) +#define vld4q_lane_s32_ex(pcD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx4Adr_acc( 0xf4a00b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN64_128(align)), (Q4), __int32ToN64_c(pcD)) ) +#define vld4q_lane_u16_ex(pcD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_Qx4Adr_acc( 0xf4a0072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), (Q4), __uint16ToN64_c(pcD)) ) +#define vld4q_lane_u32_ex(pcD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_Qx4Adr_acc( 0xf4a00b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN64_128(align)), (Q4), __uint32ToN64_c(pcD)) ) + +// VMAX, VMIN (floating point) +#define vmax_f32(Dn, Dm) ( __neon_DdDnDm( 0xf2000f00, (Dn), (Dm)) ) +#define vmaxnm_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000f10, (Dn), (Dm)) ) +#define vmin_f32(Dn, Dm) ( __neon_DdDnDm( 0xf2200f00, (Dn), (Dm)) ) +#define vminnm_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3200f10, (Dn), (Dm)) ) +#define vmaxq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2000f40, (Qn), (Qm)) ) +#define vmaxnmq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3000f50, (Qn), (Qm)) ) +#define vminq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2200f40, (Qn), (Qm)) ) +#define vminnmq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3200f50, (Qn), (Qm)) ) + +// VMAX, VMIN (integer) +#define vmax_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100600, (Dn), (Dm)) ) +#define vmax_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200600, (Dn), (Dm)) ) +#define vmax_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000600, (Dn), (Dm)) ) +#define vmax_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100600, (Dn), (Dm)) ) +#define vmax_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200600, (Dn), (Dm)) ) +#define vmax_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000600, (Dn), (Dm)) ) +#define vmin_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100610, (Dn), (Dm)) ) +#define vmin_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200610, (Dn), (Dm)) ) +#define vmin_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000610, (Dn), (Dm)) ) +#define vmin_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100610, (Dn), (Dm)) ) +#define vmin_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200610, (Dn), (Dm)) ) +#define vmin_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000610, (Dn), (Dm)) ) +#define vmaxq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100640, (Qn), (Qm)) ) +#define vmaxq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200640, (Qn), (Qm)) ) +#define vmaxq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000640, (Qn), (Qm)) ) +#define vmaxq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100640, (Qn), (Qm)) ) +#define vmaxq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200640, (Qn), (Qm)) ) +#define vmaxq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000640, (Qn), (Qm)) ) +#define vminq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100650, (Qn), (Qm)) ) +#define vminq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200650, (Qn), (Qm)) ) +#define vminq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000650, (Qn), (Qm)) ) +#define vminq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100650, (Qn), (Qm)) ) +#define vminq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200650, (Qn), (Qm)) ) +#define vminq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000650, (Qn), (Qm)) ) + +// VMLA, VMLS (by scalar) +#define vmla_lane_f32(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2a00140 | _NENC_5(lane), (Dd), (Dn), (Dm)) ) +#define vmla_lane_s16(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2900040 | _NENC_5x3(lane), (Dd), (Dn), (Dm)) ) +#define vmla_lane_s32(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2a00040 | _NENC_5(lane), (Dd), (Dn), (Dm)) ) +#define vmla_lane_u16(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2900040 | _NENC_5x3(lane), (Dd), (Dn), (Dm)) ) +#define vmla_lane_u32(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2a00040 | _NENC_5(lane), (Dd), (Dn), (Dm)) ) +#define vmls_lane_f32(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2a00540 | _NENC_5(lane), (Dd), (Dn), (Dm)) ) +#define vmls_lane_s16(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2900440 | _NENC_5x3(lane), (Dd), (Dn), (Dm)) ) +#define vmls_lane_s32(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2a00440 | _NENC_5(lane), (Dd), (Dn), (Dm)) ) +#define vmls_lane_u16(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2900440 | _NENC_5x3(lane), (Dd), (Dn), (Dm)) ) +#define vmls_lane_u32(Dd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx_acc( 0xf2a00440 | _NENC_5(lane), (Dd), (Dn), (Dm)) ) +#define vmlaq_lane_f32(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3a00140 | _NENC_5(lane), (Qd), (Qn), (Dm)) ) +#define vmlaq_lane_s16(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3900040 | _NENC_5x3(lane), (Qd), (Qn), (Dm)) ) +#define vmlaq_lane_s32(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3a00040 | _NENC_5(lane), (Qd), (Qn), (Dm)) ) +#define vmlaq_lane_u16(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3900040 | _NENC_5x3(lane), (Qd), (Qn), (Dm)) ) +#define vmlaq_lane_u32(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3a00040 | _NENC_5(lane), (Qd), (Qn), (Dm)) ) +#define vmlsq_lane_f32(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3a00540 | _NENC_5(lane), (Qd), (Qn), (Dm)) ) +#define vmlsq_lane_s16(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3900440 | _NENC_5x3(lane), (Qd), (Qn), (Dm)) ) +#define vmlsq_lane_s32(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3a00440 | _NENC_5(lane), (Qd), (Qn), (Dm)) ) +#define vmlsq_lane_u16(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3900440 | _NENC_5x3(lane), (Qd), (Qn), (Dm)) ) +#define vmlsq_lane_u32(Qd, Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx_acc( 0xf3a00440 | _NENC_5(lane), (Qd), (Qn), (Dm)) ) + +// VMLA, VMLS (float, by scalar) +#define vmla_n_f32(Dd, Dn, Ft) ( __neon_DdDnFt_acc( 0xf2a00140, (Dd), (Dn), (Ft)) ) +#define vmls_n_f32(Dd, Dn, Ft) ( __neon_DdDnFt_acc( 0xf2a00540, (Dd), (Dn), (Ft)) ) +#define vmlaq_n_f32(Qd, Qn, Ft) ( __neon_QdQnFt_acc( 0xf3a00140, (Qd), (Qn), (Ft)) ) +#define vmlsq_n_f32(Qd, Qn, Ft) ( __neon_QdQnFt_acc( 0xf3a00540, (Qd), (Qn), (Ft)) ) + +// VMLA, VMLS (floating point) +#define vmla_f32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2000d10, (Dd), (Dn), (Dm)) ) +#define vmls_f32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2200d10, (Dd), (Dn), (Dm)) ) +#define vmlaq_f32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2000d50, (Qd), (Qn), (Qm)) ) +#define vmlsq_f32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2200d50, (Qd), (Qn), (Qm)) ) + +// VMLA, VMLS (integer) +#define vmla_s16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2100900, (Dd), (Dn), (Dm)) ) +#define vmla_s32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2200900, (Dd), (Dn), (Dm)) ) +#define vmla_s8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2000900, (Dd), (Dn), (Dm)) ) +#define vmla_u16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2100900, (Dd), (Dn), (Dm)) ) +#define vmla_u32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2200900, (Dd), (Dn), (Dm)) ) +#define vmla_u8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf2000900, (Dd), (Dn), (Dm)) ) +#define vmls_s16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100900, (Dd), (Dn), (Dm)) ) +#define vmls_s32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200900, (Dd), (Dn), (Dm)) ) +#define vmls_s8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3000900, (Dd), (Dn), (Dm)) ) +#define vmls_u16(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3100900, (Dd), (Dn), (Dm)) ) +#define vmls_u32(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3200900, (Dd), (Dn), (Dm)) ) +#define vmls_u8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3000900, (Dd), (Dn), (Dm)) ) +#define vmlaq_s16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2100940, (Qd), (Qn), (Qm)) ) +#define vmlaq_s32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2200940, (Qd), (Qn), (Qm)) ) +#define vmlaq_s8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2000940, (Qd), (Qn), (Qm)) ) +#define vmlaq_u16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2100940, (Qd), (Qn), (Qm)) ) +#define vmlaq_u32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2200940, (Qd), (Qn), (Qm)) ) +#define vmlaq_u8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf2000940, (Qd), (Qn), (Qm)) ) +#define vmlsq_s16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100940, (Qd), (Qn), (Qm)) ) +#define vmlsq_s32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200940, (Qd), (Qn), (Qm)) ) +#define vmlsq_s8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3000940, (Qd), (Qn), (Qm)) ) +#define vmlsq_u16(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3100940, (Qd), (Qn), (Qm)) ) +#define vmlsq_u32(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3200940, (Qd), (Qn), (Qm)) ) +#define vmlsq_u8(Qd, Qn, Qm) ( __neon_QdQnQm_acc( 0xf3000940, (Qd), (Qn), (Qm)) ) + +// VMLAL, VMLSL +#define vmlal_s16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2900800, (Qd), (Dn), (Dm)) ) +#define vmlal_s32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2a00800, (Qd), (Dn), (Dm)) ) +#define vmlal_s8(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2800800, (Qd), (Dn), (Dm)) ) +#define vmlal_u16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3900800, (Qd), (Dn), (Dm)) ) +#define vmlal_u32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3a00800, (Qd), (Dn), (Dm)) ) +#define vmlal_u8(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3800800, (Qd), (Dn), (Dm)) ) +#define vmlsl_s16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2900a00, (Qd), (Dn), (Dm)) ) +#define vmlsl_s32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2a00a00, (Qd), (Dn), (Dm)) ) +#define vmlsl_s8(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2800a00, (Qd), (Dn), (Dm)) ) +#define vmlsl_u16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3900a00, (Qd), (Dn), (Dm)) ) +#define vmlsl_u32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3a00a00, (Qd), (Dn), (Dm)) ) +#define vmlsl_u8(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf3800a00, (Qd), (Dn), (Dm)) ) + +// VMLAL, VMLSL (by scalar) +#define vmlal_lane_s16(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2900240 | _NENC_5x3(lane), (Qd), (Dn), (Dm)) ) +#define vmlal_lane_s32(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2a00240 | _NENC_5(lane), (Qd), (Dn), (Dm)) ) +#define vmlal_lane_u16(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx_acc( 0xf3900240 | _NENC_5x3(lane), (Qd), (Dn), (Dm)) ) +#define vmlal_lane_u32(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx_acc( 0xf3a00240 | _NENC_5(lane), (Qd), (Dn), (Dm)) ) +#define vmlsl_lane_s16(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2900640 | _NENC_5x3(lane), (Qd), (Dn), (Dm)) ) +#define vmlsl_lane_s32(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2a00640 | _NENC_5(lane), (Qd), (Dn), (Dm)) ) +#define vmlsl_lane_u16(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx_acc( 0xf3900640 | _NENC_5x3(lane), (Qd), (Dn), (Dm)) ) +#define vmlsl_lane_u32(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx_acc( 0xf3a00640 | _NENC_5(lane), (Qd), (Dn), (Dm)) ) + +// VMOV (ARM core register to scalar) +#define vset_lane_f32(Ft, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdFt_acc( 0xee000b10 | _NENC_21(lane), (Dd), (Ft)) ) +#define vset_lane_p16(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdRt_acc( 0xee000b30 | _NENC_21x6(lane), (Dd), __poly16ToInt32(Rt)) ) +#define vset_lane_p8(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_DdRt_acc( 0xee400b10 | _NENC_21x6_5(lane), (Dd), __poly8ToInt32(Rt)) ) +#define vset_lane_s16(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdRt_acc( 0xee000b30 | _NENC_21x6(lane), (Dd), __int16ToInt32(Rt)) ) +#define vset_lane_s32(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdRt_acc( 0xee000b10 | _NENC_21(lane), (Dd), __int32ToInt32(Rt)) ) +#define vset_lane_s8(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_DdRt_acc( 0xee400b10 | _NENC_21x6_5(lane), (Dd), __int8ToInt32(Rt)) ) +#define vset_lane_u16(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdRt_acc( 0xee000b30 | _NENC_21x6(lane), (Dd), __uint16ToInt32(Rt)) ) +#define vset_lane_u32(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdRt_acc( 0xee000b10 | _NENC_21(lane), (Dd), __uint32ToInt32(Rt)) ) +#define vset_lane_u8(Rt, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_DdRt_acc( 0xee400b10 | _NENC_21x6_5(lane), (Dd), __uint8ToInt32(Rt)) ) + +// VMOV (scalar to ARM core register) +#define vget_lane_f32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_FtDn( 0xee100b10 | _NENC_21(lane), (Dm)) ) +#define vget_lane_p16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), (poly16_t)__neon_RtDn( 0xee900b30 | _NENC_21x6(lane), (Dm)) ) +#define vget_lane_p8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), (poly8_t)__neon_RtDn( 0xeed00b10 | _NENC_21x6_5(lane), (Dm)) ) +#define vget_lane_s16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), (int16_t)__neon_RtDn( 0xee100b30 | _NENC_21x6(lane), (Dm)) ) +#define vget_lane_s8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), (int8_t)__neon_RtDn( 0xee500b10 | _NENC_21x6_5(lane), (Dm)) ) +#define vget_lane_s32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), (int32_t)__neon_RtDn( 0xee100b10 | _NENC_21(lane), (Dm)) ) +#define vget_lane_u16(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), (uint16_t)__neon_RtDn( 0xee900b30 | _NENC_21x6(lane), (Dm)) ) +#define vget_lane_u8(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), (uint8_t)__neon_RtDn( 0xeed00b10 | _NENC_21x6_5(lane), (Dm)) ) +#define vget_lane_u32(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), (uint32_t)__neon_RtDn( 0xee100b10 | _NENC_21(lane), (Dm)) ) + +// VMOV.64 (ARM core register pair to scalar) +#define vset_lane_s64(R64t, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 1, "invalid lane index"), __neon_DdRtRt2_acc( 0xec400b10, (Dd), __int64ToInt64(R64t)) ) +#define vset_lane_u64(R64t, Dd, lane) ( __static_assert((lane) >= 0 && (lane) < 1, "invalid lane index"), __neon_DdRtRt2_acc( 0xec400b10, (Dd), __uint64ToInt64(R64t)) ) +#define vsetq_lane_s64(R64t, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdRtRt2_acc( 0xec400b10 | _NENC_0(lane), (Qd), __int64ToInt64(R64t)) ) +#define vsetq_lane_u64(R64t, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdRtRt2_acc( 0xec400b10 | _NENC_0(lane), (Qd), __uint64ToInt64(R64t)) ) + +// VMOV.64 (scalar to ARM core register pair) +#define vget_lane_s64(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 1, "invalid lane index"), (int64_t)__neon_RtRt2Dm( 0xec500b10, (Dm)) ) +#define vget_lane_u64(Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 1, "invalid lane index"), (uint64_t)__neon_RtRt2Dm( 0xec500b10, (Dm)) ) +#define vgetq_lane_s64(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), (int64_t)__neon_RtRt2Qm( 0xec500b10 | _NENC_0(lane), (Qm)) ) +#define vgetq_lane_u64(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), (uint64_t)__neon_RtRt2Qm( 0xec500b10 | _NENC_0(lane), (Qm)) ) + +// VMOV.Q (ARM core register to scalar) +#define vsetq_lane_f32(Ft, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdFt_acc( 0xee000b10 | _NENC_16((lane) >= 2 ? 1 : 0) | _NENC_21((lane) % 2), (Qd), (Ft)) ) +#define vsetq_lane_p16(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_QdRt_acc( 0xee000b30 | _NENC_16((lane) >= 4 ? 1 : 0) | _NENC_21x6((lane) % 4), (Qd), __poly16ToInt32(Rt)) ) +#define vsetq_lane_p8(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_QdRt_acc( 0xee400b10 | _NENC_16((lane) >= 8 ? 1 : 0) | _NENC_21x6_5((lane) % 8), (Qd), __poly8ToInt32(Rt)) ) +#define vsetq_lane_s16(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_QdRt_acc( 0xee000b30 | _NENC_16((lane) >= 4 ? 1 : 0) | _NENC_21x6((lane) % 4), (Qd), __int16ToInt32(Rt)) ) +#define vsetq_lane_s32(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdRt_acc( 0xee000b10 | _NENC_16((lane) >= 2 ? 1 : 0) | _NENC_21((lane) % 2), (Qd), __int32ToInt32(Rt)) ) +#define vsetq_lane_s8(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_QdRt_acc( 0xee400b10 | _NENC_16((lane) >= 8 ? 1 : 0) | _NENC_21x6_5((lane) % 8), (Qd), __int8ToInt32(Rt)) ) +#define vsetq_lane_u16(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_QdRt_acc( 0xee000b30 | _NENC_16((lane) >= 4 ? 1 : 0) | _NENC_21x6((lane) % 4), (Qd), __uint16ToInt32(Rt)) ) +#define vsetq_lane_u32(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdRt_acc( 0xee000b10 | _NENC_16((lane) >= 2 ? 1 : 0) | _NENC_21((lane) % 2), (Qd), __uint32ToInt32(Rt)) ) +#define vsetq_lane_u8(Rt, Qd, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_QdRt_acc( 0xee400b10 | _NENC_16((lane) >= 8 ? 1 : 0) | _NENC_21x6_5((lane) % 8), (Qd), __uint8ToInt32(Rt)) ) + +// VMOV.Q (scalar to ARM core register) +#define vgetq_lane_f32(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_FtQn( 0xee100b10 | _NENC_16((lane) >= 2 ? 1 : 0) | _NENC_21((lane) % 2), (Qm)) ) +#define vgetq_lane_p16(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), (poly16_t)__neon_RtQn( 0xee900b30 | _NENC_16((lane) >= 4 ? 1 : 0) | _NENC_21x6((lane) % 4), (Qm)) ) +#define vgetq_lane_p8(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), (poly8_t)__neon_RtQn( 0xeed00b10 | _NENC_16((lane) >= 8 ? 1 : 0) | _NENC_21x6_5((lane) % 8), (Qm)) ) +#define vgetq_lane_s16(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), (int16_t)__neon_RtQn( 0xee100b30 | _NENC_16((lane) >= 4 ? 1 : 0) | _NENC_21x6((lane) % 4), (Qm)) ) +#define vgetq_lane_s8(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), (int8_t)__neon_RtQn( 0xee500b10 | _NENC_16((lane) >= 8 ? 1 : 0) | _NENC_21x6_5((lane) % 8), (Qm)) ) +#define vgetq_lane_s32(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), (int32_t)__neon_RtQn( 0xee100b10 | _NENC_16((lane) >= 2 ? 1 : 0) | _NENC_21((lane) % 2), (Qm)) ) +#define vgetq_lane_u16(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), (uint16_t)__neon_RtQn( 0xee900b30 | _NENC_16((lane) >= 4 ? 1 : 0) | _NENC_21x6((lane) % 4), (Qm)) ) +#define vgetq_lane_u8(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), (uint8_t)__neon_RtQn( 0xeed00b10 | _NENC_16((lane) >= 8 ? 1 : 0) | _NENC_21x6_5((lane) % 8), (Qm)) ) +#define vgetq_lane_u32(Qm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), (uint32_t)__neon_RtQn( 0xee100b10 | _NENC_16((lane) >= 2 ? 1 : 0) | _NENC_21((lane) % 2), (Qm)) ) + +// VMOVL +#define vmovl_s16(Dm) ( __neon_QdDm( 0xf2900a10, (Dm)) ) +#define vmovl_s32(Dm) ( __neon_QdDm( 0xf2a00a10, (Dm)) ) +#define vmovl_s8(Dm) ( __neon_QdDm( 0xf2880a10, (Dm)) ) +#define vmovl_u16(Dm) ( __neon_QdDm( 0xf3900a10, (Dm)) ) +#define vmovl_u32(Dm) ( __neon_QdDm( 0xf3a00a10, (Dm)) ) +#define vmovl_u8(Dm) ( __neon_QdDm( 0xf3880a10, (Dm)) ) + +// VMOVN +#define vmovn_s16(Qm) ( __neon_DdQm( 0xf3b20200, (Qm)) ) +#define vmovn_s32(Qm) ( __neon_DdQm( 0xf3b60200, (Qm)) ) +#define vmovn_s64(Qm) ( __neon_DdQm( 0xf3ba0200, (Qm)) ) +#define vmovn_u16(Qm) ( __neon_DdQm( 0xf3b20200, (Qm)) ) +#define vmovn_u32(Qm) ( __neon_DdQm( 0xf3b60200, (Qm)) ) +#define vmovn_u64(Qm) ( __neon_DdQm( 0xf3ba0200, (Qm)) ) + +// VMUL +#define vmul_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000d10, (Dn), (Dm)) ) +#define vmul_p8(Dn, Dm) ( __neon_DdDnDm( 0xf3000910, (Dn), (Dm)) ) +#define vmul_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100910, (Dn), (Dm)) ) +#define vmul_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200910, (Dn), (Dm)) ) +#define vmul_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000910, (Dn), (Dm)) ) +#define vmul_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2100910, (Dn), (Dm)) ) +#define vmul_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2200910, (Dn), (Dm)) ) +#define vmul_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2000910, (Dn), (Dm)) ) +#define vmulq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf3000d50, (Qn), (Qm)) ) +#define vmulq_p8(Qn, Qm) ( __neon_QdQnQm( 0xf3000950, (Qn), (Qm)) ) +#define vmulq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100950, (Qn), (Qm)) ) +#define vmulq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200950, (Qn), (Qm)) ) +#define vmulq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000950, (Qn), (Qm)) ) +#define vmulq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf2100950, (Qn), (Qm)) ) +#define vmulq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2200950, (Qn), (Qm)) ) +#define vmulq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf2000950, (Qn), (Qm)) ) + +// VMUL (by scalar - float) +#define vmul_n_f32(Dn, Ft) ( __neon_DdDnFt( 0xf2a00940, (Dn), (Ft)) ) +#define vmulq_n_f32(Qn, Ft) ( __neon_QdQnFt( 0xf3a00940, (Qn), (Ft)) ) + +// VMUL (by scalar) +#define vmul_lane_f32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx( 0xf2a00940 | _NENC_5(lane), (Dn), (Dm)) ) +#define vmul_lane_s16(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx( 0xf2900840 | _NENC_5x3(lane), (Dn), (Dm)) ) +#define vmul_lane_s32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx( 0xf2a00840 | _NENC_5(lane), (Dn), (Dm)) ) +#define vmul_lane_u16(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx( 0xf2900840 | _NENC_5x3(lane), (Dn), (Dm)) ) +#define vmul_lane_u32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx( 0xf2a00840 | _NENC_5(lane), (Dn), (Dm)) ) +#define vmulq_lane_f32(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx( 0xf3a00940 | _NENC_5(lane), (Qn), (Dm)) ) +#define vmulq_lane_s16(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx( 0xf3900840 | _NENC_5x3(lane), (Qn), (Dm)) ) +#define vmulq_lane_s32(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx( 0xf3a00840 | _NENC_5(lane), (Qn), (Dm)) ) +#define vmulq_lane_u16(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx( 0xf3900840 | _NENC_5x3(lane), (Qn), (Dm)) ) +#define vmulq_lane_u32(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx( 0xf3a00840 | _NENC_5(lane), (Qn), (Dm)) ) + +// VMULL +#define vmull_p64(Dn, Dm) ( __neon_QdDnDm( 0xf2a00e00, (Dn), (Dm)) ) +#define vmull_p8(Dn, Dm) ( __neon_QdDnDm( 0xf2800e00, (Dn), (Dm)) ) +#define vmull_s16(Dn, Dm) ( __neon_QdDnDm( 0xf2900c00, (Dn), (Dm)) ) +#define vmull_s32(Dn, Dm) ( __neon_QdDnDm( 0xf2a00c00, (Dn), (Dm)) ) +#define vmull_s8(Dn, Dm) ( __neon_QdDnDm( 0xf2800c00, (Dn), (Dm)) ) +#define vmull_u16(Dn, Dm) ( __neon_QdDnDm( 0xf3900c00, (Dn), (Dm)) ) +#define vmull_u32(Dn, Dm) ( __neon_QdDnDm( 0xf3a00c00, (Dn), (Dm)) ) +#define vmull_u8(Dn, Dm) ( __neon_QdDnDm( 0xf3800c00, (Dn), (Dm)) ) + +// VMULL (by scalar) +#define vmull_lane_s16(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx( 0xf2900a40 | _NENC_5x3(lane), (Dn), (Dm)) ) +#define vmull_lane_s32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx( 0xf2a00a40 | _NENC_5(lane), (Dn), (Dm)) ) +#define vmull_lane_u16(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx( 0xf3900a40 | _NENC_5x3(lane), (Dn), (Dm)) ) +#define vmull_lane_u32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx( 0xf3a00a40 | _NENC_5(lane), (Dn), (Dm)) ) + +// VMVN +#define vmvn_p16(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvn_p8(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvn_s16(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvn_s32(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvn_s8(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvn_u16(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvn_u32(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvn_u8(Dm) ( __neon_DdDm( 0xf3b00580, (Dm)) ) +#define vmvnq_p16(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) +#define vmvnq_p8(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) +#define vmvnq_s16(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) +#define vmvnq_s32(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) +#define vmvnq_s8(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) +#define vmvnq_u16(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) +#define vmvnq_u32(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) +#define vmvnq_u8(Qm) ( __neon_QdQm( 0xf3b005c0, (Qm)) ) + +// VPADAL +#define vpadal_s16(Dd, Dm) ( __neon_DdDm_acc( 0xf3b40600, (Dd), (Dm)) ) +#define vpadal_s32(Dd, Dm) ( __neon_DdDm_acc( 0xf3b80600, (Dd), (Dm)) ) +#define vpadal_s8(Dd, Dm) ( __neon_DdDm_acc( 0xf3b00600, (Dd), (Dm)) ) +#define vpadal_u16(Dd, Dm) ( __neon_DdDm_acc( 0xf3b40680, (Dd), (Dm)) ) +#define vpadal_u32(Dd, Dm) ( __neon_DdDm_acc( 0xf3b80680, (Dd), (Dm)) ) +#define vpadal_u8(Dd, Dm) ( __neon_DdDm_acc( 0xf3b00680, (Dd), (Dm)) ) +#define vpadalq_s16(Qd, Qm) ( __neon_QdQm_acc( 0xf3b40640, (Qd), (Qm)) ) +#define vpadalq_s32(Qd, Qm) ( __neon_QdQm_acc( 0xf3b80640, (Qd), (Qm)) ) +#define vpadalq_s8(Qd, Qm) ( __neon_QdQm_acc( 0xf3b00640, (Qd), (Qm)) ) +#define vpadalq_u16(Qd, Qm) ( __neon_QdQm_acc( 0xf3b406c0, (Qd), (Qm)) ) +#define vpadalq_u32(Qd, Qm) ( __neon_QdQm_acc( 0xf3b806c0, (Qd), (Qm)) ) +#define vpadalq_u8(Qd, Qm) ( __neon_QdQm_acc( 0xf3b006c0, (Qd), (Qm)) ) + +// VPADD (floating point) +#define vpadd_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000d00, (Dn), (Dm)) ) + +// VPADD (integer) +#define vpadd_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100b10, (Dn), (Dm)) ) +#define vpadd_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200b10, (Dn), (Dm)) ) +#define vpadd_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000b10, (Dn), (Dm)) ) +#define vpadd_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2100b10, (Dn), (Dm)) ) +#define vpadd_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2200b10, (Dn), (Dm)) ) +#define vpadd_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2000b10, (Dn), (Dm)) ) + +// VPADDL +#define vpaddl_s16(Dm) ( __neon_DdDm( 0xf3b40200, (Dm)) ) +#define vpaddl_s32(Dm) ( __neon_DdDm( 0xf3b80200, (Dm)) ) +#define vpaddl_s8(Dm) ( __neon_DdDm( 0xf3b00200, (Dm)) ) +#define vpaddl_u16(Dm) ( __neon_DdDm( 0xf3b40280, (Dm)) ) +#define vpaddl_u32(Dm) ( __neon_DdDm( 0xf3b80280, (Dm)) ) +#define vpaddl_u8(Dm) ( __neon_DdDm( 0xf3b00280, (Dm)) ) +#define vpaddlq_s16(Qm) ( __neon_QdQm( 0xf3b40240, (Qm)) ) +#define vpaddlq_s32(Qm) ( __neon_QdQm( 0xf3b80240, (Qm)) ) +#define vpaddlq_s8(Qm) ( __neon_QdQm( 0xf3b00240, (Qm)) ) +#define vpaddlq_u16(Qm) ( __neon_QdQm( 0xf3b402c0, (Qm)) ) +#define vpaddlq_u32(Qm) ( __neon_QdQm( 0xf3b802c0, (Qm)) ) +#define vpaddlq_u8(Qm) ( __neon_QdQm( 0xf3b002c0, (Qm)) ) + +// VPMAX, VPMIN (floating point) +#define vpmax_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3000f00, (Dn), (Dm)) ) +#define vpmin_f32(Dn, Dm) ( __neon_DdDnDm( 0xf3200f00, (Dn), (Dm)) ) + +// VPMAX, VPMIN (integer) +#define vpmax_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100a00, (Dn), (Dm)) ) +#define vpmax_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200a00, (Dn), (Dm)) ) +#define vpmax_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000a00, (Dn), (Dm)) ) +#define vpmax_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100a00, (Dn), (Dm)) ) +#define vpmax_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200a00, (Dn), (Dm)) ) +#define vpmax_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000a00, (Dn), (Dm)) ) +#define vpmin_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100a10, (Dn), (Dm)) ) +#define vpmin_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200a10, (Dn), (Dm)) ) +#define vpmin_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000a10, (Dn), (Dm)) ) +#define vpmin_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100a10, (Dn), (Dm)) ) +#define vpmin_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200a10, (Dn), (Dm)) ) +#define vpmin_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000a10, (Dn), (Dm)) ) + +// VQABS, VQNEG +#define vqabs_s16(Dm) ( __neon_DdDm( 0xf3b40700, (Dm)) ) +#define vqabs_s32(Dm) ( __neon_DdDm( 0xf3b80700, (Dm)) ) +#define vqabs_s8(Dm) ( __neon_DdDm( 0xf3b00700, (Dm)) ) +#define vqneg_s16(Dm) ( __neon_DdDm( 0xf3b40780, (Dm)) ) +#define vqneg_s32(Dm) ( __neon_DdDm( 0xf3b80780, (Dm)) ) +#define vqneg_s8(Dm) ( __neon_DdDm( 0xf3b00780, (Dm)) ) +#define vqabsq_s16(Qm) ( __neon_QdQm( 0xf3b40740, (Qm)) ) +#define vqabsq_s32(Qm) ( __neon_QdQm( 0xf3b80740, (Qm)) ) +#define vqabsq_s8(Qm) ( __neon_QdQm( 0xf3b00740, (Qm)) ) +#define vqnegq_s16(Qm) ( __neon_QdQm( 0xf3b407c0, (Qm)) ) +#define vqnegq_s32(Qm) ( __neon_QdQm( 0xf3b807c0, (Qm)) ) +#define vqnegq_s8(Qm) ( __neon_QdQm( 0xf3b007c0, (Qm)) ) + +// VQADD +#define vqadd_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100010, (Dn), (Dm)) ) +#define vqadd_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200010, (Dn), (Dm)) ) +#define vqadd_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300010, (Dn), (Dm)) ) +#define vqadd_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000010, (Dn), (Dm)) ) +#define vqadd_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100010, (Dn), (Dm)) ) +#define vqadd_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200010, (Dn), (Dm)) ) +#define vqadd_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3300010, (Dn), (Dm)) ) +#define vqadd_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000010, (Dn), (Dm)) ) +#define vqaddq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100050, (Qn), (Qm)) ) +#define vqaddq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200050, (Qn), (Qm)) ) +#define vqaddq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300050, (Qn), (Qm)) ) +#define vqaddq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000050, (Qn), (Qm)) ) +#define vqaddq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100050, (Qn), (Qm)) ) +#define vqaddq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200050, (Qn), (Qm)) ) +#define vqaddq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3300050, (Qn), (Qm)) ) +#define vqaddq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000050, (Qn), (Qm)) ) + +// VQDMLAL, VQDMLSL +#define vqdmlal_s16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2900900, (Qd), (Dn), (Dm)) ) +#define vqdmlal_s32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2a00900, (Qd), (Dn), (Dm)) ) +#define vqdmlsl_s16(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2900b00, (Qd), (Dn), (Dm)) ) +#define vqdmlsl_s32(Qd, Dn, Dm) ( __neon_QdDnDm_acc( 0xf2a00b00, (Qd), (Dn), (Dm)) ) + +// VQDMLAL, VQDMLSL (by scalar) +#define vqdmlal_lane_s16(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2900340 | _NENC_5x3(lane), (Qd), (Dn), (Dm)) ) +#define vqdmlal_lane_s32(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2a00340 | _NENC_5(lane), (Qd), (Dn), (Dm)) ) +#define vqdmlsl_lane_s16(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2900740 | _NENC_5x3(lane), (Qd), (Dn), (Dm)) ) +#define vqdmlsl_lane_s32(Qd, Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx_acc( 0xf2a00740 | _NENC_5(lane), (Qd), (Dn), (Dm)) ) + +// VQDMULH (by scalar) +#define vqdmulh_lane_s16(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx( 0xf2900c40 | _NENC_5x3(lane), (Dn), (Dm)) ) +#define vqdmulh_lane_s32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx( 0xf2a00c40 | _NENC_5(lane), (Dn), (Dm)) ) +#define vqrdmulh_lane_s16(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_DdDnDmx( 0xf2900d40 | _NENC_5x3(lane), (Dn), (Dm)) ) +#define vqrdmulh_lane_s32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_DdDnDmx( 0xf2a00d40 | _NENC_5(lane), (Dn), (Dm)) ) +#define vqdmulhq_lane_s16(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx( 0xf3900c40 | _NENC_5x3(lane), (Qn), (Dm)) ) +#define vqdmulhq_lane_s32(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx( 0xf3a00c40 | _NENC_5(lane), (Qn), (Dm)) ) +#define vqrdmulhq_lane_s16(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdQnDmx( 0xf3900d40 | _NENC_5x3(lane), (Qn), (Dm)) ) +#define vqrdmulhq_lane_s32(Qn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdQnDmx( 0xf3a00d40 | _NENC_5(lane), (Qn), (Dm)) ) + +// VQDMULH, VQRDMULH +#define vqdmulh_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100b00, (Dn), (Dm)) ) +#define vqdmulh_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200b00, (Dn), (Dm)) ) +#define vqrdmulh_s16(Dn, Dm) ( __neon_DdDnDm( 0xf3100b00, (Dn), (Dm)) ) +#define vqrdmulh_s32(Dn, Dm) ( __neon_DdDnDm( 0xf3200b00, (Dn), (Dm)) ) +#define vqdmulhq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100b40, (Qn), (Qm)) ) +#define vqdmulhq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200b40, (Qn), (Qm)) ) +#define vqrdmulhq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf3100b40, (Qn), (Qm)) ) +#define vqrdmulhq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf3200b40, (Qn), (Qm)) ) + +// VQDMULL +#define vqdmull_s16(Dn, Dm) ( __neon_QdDnDm( 0xf2900d00, (Dn), (Dm)) ) +#define vqdmull_s32(Dn, Dm) ( __neon_QdDnDm( 0xf2a00d00, (Dn), (Dm)) ) + +// VQDMULL (by scalar) +#define vqdmull_lane_s16(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_QdDnDmx( 0xf2900b40 | _NENC_5x3(lane), (Dn), (Dm)) ) +#define vqdmull_lane_s32(Dn, Dm, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_QdDnDmx( 0xf2a00b40 | _NENC_5(lane), (Dn), (Dm)) ) + +// VQMOVN, VQMOVUN +#define vqmovn_s16(Qm) ( __neon_DdQm( 0xf3b20280, (Qm)) ) +#define vqmovn_s32(Qm) ( __neon_DdQm( 0xf3b60280, (Qm)) ) +#define vqmovn_s64(Qm) ( __neon_DdQm( 0xf3ba0280, (Qm)) ) +#define vqmovn_u16(Qm) ( __neon_DdQm( 0xf3b202c0, (Qm)) ) +#define vqmovn_u32(Qm) ( __neon_DdQm( 0xf3b602c0, (Qm)) ) +#define vqmovn_u64(Qm) ( __neon_DdQm( 0xf3ba02c0, (Qm)) ) +#define vqmovun_s16(Qm) ( __neon_DdQm( 0xf3b20240, (Qm)) ) +#define vqmovun_s32(Qm) ( __neon_DdQm( 0xf3b60240, (Qm)) ) +#define vqmovun_s64(Qm) ( __neon_DdQm( 0xf3ba0240, (Qm)) ) + +// VQSHL, VQSHLU (immediate) +#define vqshl_n_s16(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm( 0xf2900710 | _NENC_19_16(shift_amount), (Dm)) ) +#define vqshl_n_s32(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_DdDm( 0xf2a00710 | _NENC_20_16(shift_amount), (Dm)) ) +#define vqshl_n_s64(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_DdDm( 0xf2800790 | _NENC_21_16(shift_amount), (Dm)) ) +#define vqshl_n_s8(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm( 0xf2880710 | _NENC_18_16(shift_amount), (Dm)) ) +#define vqshl_n_u16(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm( 0xf3900710 | _NENC_19_16(shift_amount), (Dm)) ) +#define vqshl_n_u32(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_DdDm( 0xf3a00710 | _NENC_20_16(shift_amount), (Dm)) ) +#define vqshl_n_u64(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_DdDm( 0xf3800790 | _NENC_21_16(shift_amount), (Dm)) ) +#define vqshl_n_u8(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm( 0xf3880710 | _NENC_18_16(shift_amount), (Dm)) ) +#define vqshlu_n_s16(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm( 0xf3900610 | _NENC_19_16(shift_amount), (Dm)) ) +#define vqshlu_n_s32(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_DdDm( 0xf3a00610 | _NENC_20_16(shift_amount), (Dm)) ) +#define vqshlu_n_s64(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_DdDm( 0xf3800690 | _NENC_21_16(shift_amount), (Dm)) ) +#define vqshlu_n_s8(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm( 0xf3880610 | _NENC_18_16(shift_amount), (Dm)) ) +#define vqshlq_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm( 0xf2900750 | _NENC_19_16(shift_amount), (Qm)) ) +#define vqshlq_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_QdQm( 0xf2a00750 | _NENC_20_16(shift_amount), (Qm)) ) +#define vqshlq_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_QdQm( 0xf28007d0 | _NENC_21_16(shift_amount), (Qm)) ) +#define vqshlq_n_s8(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm( 0xf2880750 | _NENC_18_16(shift_amount), (Qm)) ) +#define vqshlq_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm( 0xf3900750 | _NENC_19_16(shift_amount), (Qm)) ) +#define vqshlq_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_QdQm( 0xf3a00750 | _NENC_20_16(shift_amount), (Qm)) ) +#define vqshlq_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_QdQm( 0xf38007d0 | _NENC_21_16(shift_amount), (Qm)) ) +#define vqshlq_n_u8(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm( 0xf3880750 | _NENC_18_16(shift_amount), (Qm)) ) +#define vqshluq_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm( 0xf3900650 | _NENC_19_16(shift_amount), (Qm)) ) +#define vqshluq_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_QdQm( 0xf3a00650 | _NENC_20_16(shift_amount), (Qm)) ) +#define vqshluq_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_QdQm( 0xf38006d0 | _NENC_21_16(shift_amount), (Qm)) ) +#define vqshluq_n_s8(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm( 0xf3880650 | _NENC_18_16(shift_amount), (Qm)) ) + +// VQSHRN, VQSHRUN, VQRSHRN, VQRSHRUN (immediate) +#define vqrshrn_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf2880950 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vqrshrn_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf2900950 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vqrshrn_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf2a00950 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vqrshrn_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf3880950 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vqrshrn_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf3900950 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vqrshrn_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf3a00950 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vqrshrun_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf3880850 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vqrshrun_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf3900850 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vqrshrun_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf3a00850 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vqshrn_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf2880910 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vqshrn_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf2900910 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vqshrn_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf2a00910 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vqshrn_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf3880910 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vqshrn_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf3900910 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vqshrn_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf3a00910 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vqshrun_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf3880810 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vqshrun_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf3900810 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vqshrun_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf3a00810 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) + +// VQSUB +#define vqsub_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100210, (Dn), (Dm)) ) +#define vqsub_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200210, (Dn), (Dm)) ) +#define vqsub_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300210, (Dn), (Dm)) ) +#define vqsub_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000210, (Dn), (Dm)) ) +#define vqsub_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100210, (Dn), (Dm)) ) +#define vqsub_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200210, (Dn), (Dm)) ) +#define vqsub_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3300210, (Dn), (Dm)) ) +#define vqsub_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000210, (Dn), (Dm)) ) +#define vqsubq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100250, (Qn), (Qm)) ) +#define vqsubq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200250, (Qn), (Qm)) ) +#define vqsubq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300250, (Qn), (Qm)) ) +#define vqsubq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000250, (Qn), (Qm)) ) +#define vqsubq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100250, (Qn), (Qm)) ) +#define vqsubq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200250, (Qn), (Qm)) ) +#define vqsubq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3300250, (Qn), (Qm)) ) +#define vqsubq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000250, (Qn), (Qm)) ) + +// VRECPE, VRSQRTE +#define vrecpe_f32(Dm) ( __neon_DdDm( 0xf3bb0500, (Dm)) ) +#define vrecpe_u32(Dm) ( __neon_DdDm( 0xf3bb0400, (Dm)) ) +#define vrsqrte_f32(Dm) ( __neon_DdDm( 0xf3bb0580, (Dm)) ) +#define vrsqrte_u32(Dm) ( __neon_DdDm( 0xf3bb0480, (Dm)) ) +#define vrecpeq_f32(Qm) ( __neon_QdQm( 0xf3bb0540, (Qm)) ) +#define vrecpeq_u32(Qm) ( __neon_QdQm( 0xf3bb0440, (Qm)) ) +#define vrsqrteq_f32(Qm) ( __neon_QdQm( 0xf3bb05c0, (Qm)) ) +#define vrsqrteq_u32(Qm) ( __neon_QdQm( 0xf3bb04c0, (Qm)) ) + +// VRECPS +#define vrecps_f32(Dn, Dm) ( __neon_DdDnDm( 0xf2000f10, (Dn), (Dm)) ) +#define vrecpsq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2000f50, (Qn), (Qm)) ) + +// VREV +#define vrev16_p8(Dm) ( __neon_DdDm( 0xf3b00100, (Dm)) ) +#define vrev16_s8(Dm) ( __neon_DdDm( 0xf3b00100, (Dm)) ) +#define vrev16_u8(Dm) ( __neon_DdDm( 0xf3b00100, (Dm)) ) +#define vrev32_p16(Dm) ( __neon_DdDm( 0xf3b40080, (Dm)) ) +#define vrev32_p8(Dm) ( __neon_DdDm( 0xf3b00080, (Dm)) ) +#define vrev32_s16(Dm) ( __neon_DdDm( 0xf3b40080, (Dm)) ) +#define vrev32_s8(Dm) ( __neon_DdDm( 0xf3b00080, (Dm)) ) +#define vrev32_u16(Dm) ( __neon_DdDm( 0xf3b40080, (Dm)) ) +#define vrev32_u8(Dm) ( __neon_DdDm( 0xf3b00080, (Dm)) ) +#define vrev64_f32(Dm) ( __neon_DdDm( 0xf3b80000, (Dm)) ) +#define vrev64_p16(Dm) ( __neon_DdDm( 0xf3b40000, (Dm)) ) +#define vrev64_p8(Dm) ( __neon_DdDm( 0xf3b00000, (Dm)) ) +#define vrev64_s16(Dm) ( __neon_DdDm( 0xf3b40000, (Dm)) ) +#define vrev64_s32(Dm) ( __neon_DdDm( 0xf3b80000, (Dm)) ) +#define vrev64_s8(Dm) ( __neon_DdDm( 0xf3b00000, (Dm)) ) +#define vrev64_u16(Dm) ( __neon_DdDm( 0xf3b40000, (Dm)) ) +#define vrev64_u32(Dm) ( __neon_DdDm( 0xf3b80000, (Dm)) ) +#define vrev64_u8(Dm) ( __neon_DdDm( 0xf3b00000, (Dm)) ) +#define vrev16q_p8(Qm) ( __neon_QdQm( 0xf3b00140, (Qm)) ) +#define vrev16q_s8(Qm) ( __neon_QdQm( 0xf3b00140, (Qm)) ) +#define vrev16q_u8(Qm) ( __neon_QdQm( 0xf3b00140, (Qm)) ) +#define vrev32q_p16(Qm) ( __neon_QdQm( 0xf3b400c0, (Qm)) ) +#define vrev32q_p8(Qm) ( __neon_QdQm( 0xf3b000c0, (Qm)) ) +#define vrev32q_s16(Qm) ( __neon_QdQm( 0xf3b400c0, (Qm)) ) +#define vrev32q_s8(Qm) ( __neon_QdQm( 0xf3b000c0, (Qm)) ) +#define vrev32q_u16(Qm) ( __neon_QdQm( 0xf3b400c0, (Qm)) ) +#define vrev32q_u8(Qm) ( __neon_QdQm( 0xf3b000c0, (Qm)) ) +#define vrev64q_f32(Qm) ( __neon_QdQm( 0xf3b80040, (Qm)) ) +#define vrev64q_p16(Qm) ( __neon_QdQm( 0xf3b40040, (Qm)) ) +#define vrev64q_p8(Qm) ( __neon_QdQm( 0xf3b00040, (Qm)) ) +#define vrev64q_s16(Qm) ( __neon_QdQm( 0xf3b40040, (Qm)) ) +#define vrev64q_s32(Qm) ( __neon_QdQm( 0xf3b80040, (Qm)) ) +#define vrev64q_s8(Qm) ( __neon_QdQm( 0xf3b00040, (Qm)) ) +#define vrev64q_u16(Qm) ( __neon_QdQm( 0xf3b40040, (Qm)) ) +#define vrev64q_u32(Qm) ( __neon_QdQm( 0xf3b80040, (Qm)) ) +#define vrev64q_u8(Qm) ( __neon_QdQm( 0xf3b00040, (Qm)) ) + +// VRINT +#define vrnd_f32(Dm) ( __neon_DdDm( 0xf3ba0580, (Dm)) ) +#define vrnda_f32(Dm) ( __neon_DdDm( 0xf3ba0500, (Dm)) ) +#define vrndm_f32(Dm) ( __neon_DdDm( 0xf3ba0680, (Dm)) ) +#define vrndn_f32(Dm) ( __neon_DdDm( 0xf3ba0400, (Dm)) ) +#define vrndp_f32(Dm) ( __neon_DdDm( 0xf3ba0780, (Dm)) ) +#define vrndx_f32(Dm) ( __neon_DdDm( 0xf3ba0480, (Dm)) ) +#define vrndq_f32(Qm) ( __neon_QdQm( 0xf3ba05c0, (Qm)) ) +#define vrndaq_f32(Qm) ( __neon_QdQm( 0xf3ba0540, (Qm)) ) +#define vrndmq_f32(Qm) ( __neon_QdQm( 0xf3ba06c0, (Qm)) ) +#define vrndnq_f32(Qm) ( __neon_QdQm( 0xf3ba0440, (Qm)) ) +#define vrndpq_f32(Qm) ( __neon_QdQm( 0xf3ba07c0, (Qm)) ) +#define vrndxq_f32(Qm) ( __neon_QdQm( 0xf3ba04c0, (Qm)) ) + +// VRSQRTS +#define vrsqrts_f32(Dn, Dm) ( __neon_DdDnDm( 0xf2200f10, (Dn), (Dm)) ) +#define vrsqrtsq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2200f50, (Qn), (Qm)) ) + +// VSHL (immediate) +#define vshl_n_s16(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm( 0xf2900510 | _NENC_19_16(shift_amount), (Dm)) ) +#define vshl_n_s32(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_DdDm( 0xf2a00510 | _NENC_20_16(shift_amount), (Dm)) ) +#define vshl_n_s64(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_DdDm( 0xf2800590 | _NENC_21_16(shift_amount), (Dm)) ) +#define vshl_n_s8(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm( 0xf2880510 | _NENC_18_16(shift_amount), (Dm)) ) +#define vshl_n_u16(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm( 0xf2900510 | _NENC_19_16(shift_amount), (Dm)) ) +#define vshl_n_u32(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_DdDm( 0xf2a00510 | _NENC_20_16(shift_amount), (Dm)) ) +#define vshl_n_u64(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_DdDm( 0xf2800590 | _NENC_21_16(shift_amount), (Dm)) ) +#define vshl_n_u8(Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm( 0xf2880510 | _NENC_18_16(shift_amount), (Dm)) ) +#define vshlq_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm( 0xf2900550 | _NENC_19_16(shift_amount), (Qm)) ) +#define vshlq_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_QdQm( 0xf2a00550 | _NENC_20_16(shift_amount), (Qm)) ) +#define vshlq_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_QdQm( 0xf28005d0 | _NENC_21_16(shift_amount), (Qm)) ) +#define vshlq_n_s8(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm( 0xf2880550 | _NENC_18_16(shift_amount), (Qm)) ) +#define vshlq_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm( 0xf2900550 | _NENC_19_16(shift_amount), (Qm)) ) +#define vshlq_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_QdQm( 0xf2a00550 | _NENC_20_16(shift_amount), (Qm)) ) +#define vshlq_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_QdQm( 0xf28005d0 | _NENC_21_16(shift_amount), (Qm)) ) +#define vshlq_n_u8(Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm( 0xf2880550 | _NENC_18_16(shift_amount), (Qm)) ) + +// VSHL, VQSHL, VRSHL, VQRSHL (register) +#define vqrshl_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100510, (Dm), (Dn)) ) +#define vqrshl_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200510, (Dm), (Dn)) ) +#define vqrshl_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300510, (Dm), (Dn)) ) +#define vqrshl_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000510, (Dm), (Dn)) ) +#define vqrshl_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100510, (Dm), (Dn)) ) +#define vqrshl_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200510, (Dm), (Dn)) ) +#define vqrshl_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3300510, (Dm), (Dn)) ) +#define vqrshl_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000510, (Dm), (Dn)) ) +#define vqshl_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100410, (Dm), (Dn)) ) +#define vqshl_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200410, (Dm), (Dn)) ) +#define vqshl_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300410, (Dm), (Dn)) ) +#define vqshl_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000410, (Dm), (Dn)) ) +#define vqshl_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100410, (Dm), (Dn)) ) +#define vqshl_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200410, (Dm), (Dn)) ) +#define vqshl_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3300410, (Dm), (Dn)) ) +#define vqshl_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000410, (Dm), (Dn)) ) +#define vrshl_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100500, (Dm), (Dn)) ) +#define vrshl_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200500, (Dm), (Dn)) ) +#define vrshl_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300500, (Dm), (Dn)) ) +#define vrshl_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000500, (Dm), (Dn)) ) +#define vrshl_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100500, (Dm), (Dn)) ) +#define vrshl_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200500, (Dm), (Dn)) ) +#define vrshl_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3300500, (Dm), (Dn)) ) +#define vrshl_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000500, (Dm), (Dn)) ) +#define vshl_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100400, (Dm), (Dn)) ) +#define vshl_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200400, (Dm), (Dn)) ) +#define vshl_s64(Dn, Dm) ( __neon_DdDnDm( 0xf2300400, (Dm), (Dn)) ) +#define vshl_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000400, (Dm), (Dn)) ) +#define vshl_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100400, (Dm), (Dn)) ) +#define vshl_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200400, (Dm), (Dn)) ) +#define vshl_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3300400, (Dm), (Dn)) ) +#define vshl_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000400, (Dm), (Dn)) ) +#define vqrshlq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100550, (Qm), (Qn)) ) +#define vqrshlq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200550, (Qm), (Qn)) ) +#define vqrshlq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300550, (Qm), (Qn)) ) +#define vqrshlq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000550, (Qm), (Qn)) ) +#define vqrshlq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100550, (Qm), (Qn)) ) +#define vqrshlq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200550, (Qm), (Qn)) ) +#define vqrshlq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3300550, (Qm), (Qn)) ) +#define vqrshlq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000550, (Qm), (Qn)) ) +#define vqshlq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100450, (Qm), (Qn)) ) +#define vqshlq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200450, (Qm), (Qn)) ) +#define vqshlq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300450, (Qm), (Qn)) ) +#define vqshlq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000450, (Qm), (Qn)) ) +#define vqshlq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100450, (Qm), (Qn)) ) +#define vqshlq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200450, (Qm), (Qn)) ) +#define vqshlq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3300450, (Qm), (Qn)) ) +#define vqshlq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000450, (Qm), (Qn)) ) +#define vrshlq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100540, (Qm), (Qn)) ) +#define vrshlq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200540, (Qm), (Qn)) ) +#define vrshlq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300540, (Qm), (Qn)) ) +#define vrshlq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000540, (Qm), (Qn)) ) +#define vrshlq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100540, (Qm), (Qn)) ) +#define vrshlq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200540, (Qm), (Qn)) ) +#define vrshlq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3300540, (Qm), (Qn)) ) +#define vrshlq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000540, (Qm), (Qn)) ) +#define vshlq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100440, (Qm), (Qn)) ) +#define vshlq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200440, (Qm), (Qn)) ) +#define vshlq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf2300440, (Qm), (Qn)) ) +#define vshlq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000440, (Qm), (Qn)) ) +#define vshlq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100440, (Qm), (Qn)) ) +#define vshlq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200440, (Qm), (Qn)) ) +#define vshlq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3300440, (Qm), (Qn)) ) +#define vshlq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000440, (Qm), (Qn)) ) + +// VSHLL (shift_amount != size) +#define __internal_vshll_n_t1_s16(Dm, shift_amount) ( __neon_QdDm( 0xf2900a10 | _NENC_19_16(shift_amount), (Dm)) ) +#define __internal_vshll_n_t1_s32(Dm, shift_amount) ( __neon_QdDm( 0xf2a00a10 | _NENC_20_16(shift_amount), (Dm)) ) +#define __internal_vshll_n_t1_s8(Dm, shift_amount) ( __neon_QdDm( 0xf2880a10 | _NENC_18_16(shift_amount), (Dm)) ) +#define __internal_vshll_n_t1_u16(Dm, shift_amount) ( __neon_QdDm( 0xf3900a10 | _NENC_19_16(shift_amount), (Dm)) ) +#define __internal_vshll_n_t1_u32(Dm, shift_amount) ( __neon_QdDm( 0xf3a00a10 | _NENC_20_16(shift_amount), (Dm)) ) +#define __internal_vshll_n_t1_u8(Dm, shift_amount) ( __neon_QdDm( 0xf3880a10 | _NENC_18_16(shift_amount), (Dm)) ) + +// VSHLL (shift_amount == size) +#define __internal_vshll_n_t2_s16(Dm) ( __neon_QdDm( 0xf3b60300, (Dm)) ) +#define __internal_vshll_n_t2_s32(Dm) ( __neon_QdDm( 0xf3ba0300, (Dm)) ) +#define __internal_vshll_n_t2_s8(Dm) ( __neon_QdDm( 0xf3b20300, (Dm)) ) +#define __internal_vshll_n_t2_u16(Dm) ( __neon_QdDm( 0xf3b60300, (Dm)) ) +#define __internal_vshll_n_t2_u32(Dm) ( __neon_QdDm( 0xf3ba0300, (Dm)) ) +#define __internal_vshll_n_t2_u8(Dm) ( __neon_QdDm( 0xf3b20300, (Dm)) ) + +// VSHR, VRSHR (immediate) +#define vrshr_n_s16(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm( 0xf2900210 | _NENC_19_16(16 - (shift_amount)), (Dm)) ) +#define vrshr_n_s32(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm( 0xf2a00210 | _NENC_20_16(32 - (shift_amount)), (Dm)) ) +#define vrshr_n_s64(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm( 0xf2800290 | _NENC_21_16(64 - (shift_amount)), (Dm)) ) +#define vrshr_n_s8(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm( 0xf2880210 | _NENC_18_16(8 - (shift_amount)), (Dm)) ) +#define vrshr_n_u16(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm( 0xf3900210 | _NENC_19_16(16 - (shift_amount)), (Dm)) ) +#define vrshr_n_u32(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm( 0xf3a00210 | _NENC_20_16(32 - (shift_amount)), (Dm)) ) +#define vrshr_n_u64(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm( 0xf3800290 | _NENC_21_16(64 - (shift_amount)), (Dm)) ) +#define vrshr_n_u8(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm( 0xf3880210 | _NENC_18_16(8 - (shift_amount)), (Dm)) ) +#define vshr_n_s16(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm( 0xf2900010 | _NENC_19_16(16 - (shift_amount)), (Dm)) ) +#define vshr_n_s32(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm( 0xf2a00010 | _NENC_20_16(32 - (shift_amount)), (Dm)) ) +#define vshr_n_s64(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm( 0xf2800090 | _NENC_21_16(64 - (shift_amount)), (Dm)) ) +#define vshr_n_s8(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm( 0xf2880010 | _NENC_18_16(8 - (shift_amount)), (Dm)) ) +#define vshr_n_u16(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm( 0xf3900010 | _NENC_19_16(16 - (shift_amount)), (Dm)) ) +#define vshr_n_u32(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm( 0xf3a00010 | _NENC_20_16(32 - (shift_amount)), (Dm)) ) +#define vshr_n_u64(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm( 0xf3800090 | _NENC_21_16(64 - (shift_amount)), (Dm)) ) +#define vshr_n_u8(Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm( 0xf3880010 | _NENC_18_16(8 - (shift_amount)), (Dm)) ) +#define vrshrq_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm( 0xf2900250 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vrshrq_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm( 0xf2a00250 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vrshrq_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm( 0xf28002d0 | _NENC_21_16(64 - (shift_amount)), (Qm)) ) +#define vrshrq_n_s8(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm( 0xf2880250 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vrshrq_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm( 0xf3900250 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vrshrq_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm( 0xf3a00250 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vrshrq_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm( 0xf38002d0 | _NENC_21_16(64 - (shift_amount)), (Qm)) ) +#define vrshrq_n_u8(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm( 0xf3880250 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vshrq_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm( 0xf2900050 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vshrq_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm( 0xf2a00050 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vshrq_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm( 0xf28000d0 | _NENC_21_16(64 - (shift_amount)), (Qm)) ) +#define vshrq_n_s8(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm( 0xf2880050 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vshrq_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm( 0xf3900050 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vshrq_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm( 0xf3a00050 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vshrq_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm( 0xf38000d0 | _NENC_21_16(64 - (shift_amount)), (Qm)) ) +#define vshrq_n_u8(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm( 0xf3880050 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) + +// VSHRN, VRSHRN (immediate) +#define vrshrn_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf2880850 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vrshrn_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf2900850 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vrshrn_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf2a00850 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vrshrn_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf2880850 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vrshrn_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf2900850 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vrshrn_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf2a00850 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vshrn_n_s16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf2880810 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vshrn_n_s32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf2900810 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vshrn_n_s64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf2a00810 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) +#define vshrn_n_u16(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdQm( 0xf2880810 | _NENC_18_16(8 - (shift_amount)), (Qm)) ) +#define vshrn_n_u32(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdQm( 0xf2900810 | _NENC_19_16(16 - (shift_amount)), (Qm)) ) +#define vshrn_n_u64(Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdQm( 0xf2a00810 | _NENC_20_16(32 - (shift_amount)), (Qm)) ) + +// VSLI (immediate) +#define vsli_n_p16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900510 | _NENC_19_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_p8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880510 | _NENC_18_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_s16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900510 | _NENC_19_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_s32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_DdDm_acc( 0xf3a00510 | _NENC_20_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_s64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_DdDm_acc( 0xf3800590 | _NENC_21_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_s8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880510 | _NENC_18_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_u16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900510 | _NENC_19_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_u32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_DdDm_acc( 0xf3a00510 | _NENC_20_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_u64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_DdDm_acc( 0xf3800590 | _NENC_21_16(shift_amount), (Dd), (Dm)) ) +#define vsli_n_u8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880510 | _NENC_18_16(shift_amount), (Dd), (Dm)) ) +#define vsliq_n_p16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900550 | _NENC_19_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_p8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880550 | _NENC_18_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_s16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900550 | _NENC_19_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_s32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_QdQm_acc( 0xf3a00550 | _NENC_20_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_s64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_QdQm_acc( 0xf38005d0 | _NENC_21_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_s8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880550 | _NENC_18_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_u16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900550 | _NENC_19_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_u32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 32, "invalid shift amount"), __neon_QdQm_acc( 0xf3a00550 | _NENC_20_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_u64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 64, "invalid shift amount"), __neon_QdQm_acc( 0xf38005d0 | _NENC_21_16(shift_amount), (Qd), (Qm)) ) +#define vsliq_n_u8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 0 && (shift_amount) < 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880550 | _NENC_18_16(shift_amount), (Qd), (Qm)) ) + +// VSRA, VRSRA (immediate) +#define vrsra_n_s16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm_acc( 0xf2900310 | _NENC_19_16(16 - (shift_amount)), (Dd), (Dm)) ) +#define vrsra_n_s32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm_acc( 0xf2a00310 | _NENC_20_16(32 - (shift_amount)), (Dd), (Dm)) ) +#define vrsra_n_s64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm_acc( 0xf2800390 | _NENC_21_16(64 - (shift_amount)), (Dd), (Dm)) ) +#define vrsra_n_s8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm_acc( 0xf2880310 | _NENC_18_16(8 - (shift_amount)), (Dd), (Dm)) ) +#define vrsra_n_u16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900310 | _NENC_19_16(16 - (shift_amount)), (Dd), (Dm)) ) +#define vrsra_n_u32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm_acc( 0xf3a00310 | _NENC_20_16(32 - (shift_amount)), (Dd), (Dm)) ) +#define vrsra_n_u64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm_acc( 0xf3800390 | _NENC_21_16(64 - (shift_amount)), (Dd), (Dm)) ) +#define vrsra_n_u8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880310 | _NENC_18_16(8 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_s16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm_acc( 0xf2900110 | _NENC_19_16(16 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_s32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm_acc( 0xf2a00110 | _NENC_20_16(32 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_s64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm_acc( 0xf2800190 | _NENC_21_16(64 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_s8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm_acc( 0xf2880110 | _NENC_18_16(8 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_u16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900110 | _NENC_19_16(16 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_u32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm_acc( 0xf3a00110 | _NENC_20_16(32 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_u64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm_acc( 0xf3800190 | _NENC_21_16(64 - (shift_amount)), (Dd), (Dm)) ) +#define vsra_n_u8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880110 | _NENC_18_16(8 - (shift_amount)), (Dd), (Dm)) ) +#define vrsraq_n_s16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm_acc( 0xf2900350 | _NENC_19_16(16 - (shift_amount)), (Qd), (Qm)) ) +#define vrsraq_n_s32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm_acc( 0xf2a00350 | _NENC_20_16(32 - (shift_amount)), (Qd), (Qm)) ) +#define vrsraq_n_s64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm_acc( 0xf28003d0 | _NENC_21_16(64 - (shift_amount)), (Qd), (Qm)) ) +#define vrsraq_n_s8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm_acc( 0xf2880350 | _NENC_18_16(8 - (shift_amount)), (Qd), (Qm)) ) +#define vrsraq_n_u16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900350 | _NENC_19_16(16 - (shift_amount)), (Qd), (Qm)) ) +#define vrsraq_n_u32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm_acc( 0xf3a00350 | _NENC_20_16(32 - (shift_amount)), (Qd), (Qm)) ) +#define vrsraq_n_u64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm_acc( 0xf38003d0 | _NENC_21_16(64 - (shift_amount)), (Qd), (Qm)) ) +#define vrsraq_n_u8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880350 | _NENC_18_16(8 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_s16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm_acc( 0xf2900150 | _NENC_19_16(16 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_s32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm_acc( 0xf2a00150 | _NENC_20_16(32 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_s64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm_acc( 0xf28001d0 | _NENC_21_16(64 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_s8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm_acc( 0xf2880150 | _NENC_18_16(8 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_u16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900150 | _NENC_19_16(16 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_u32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm_acc( 0xf3a00150 | _NENC_20_16(32 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_u64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm_acc( 0xf38001d0 | _NENC_21_16(64 - (shift_amount)), (Qd), (Qm)) ) +#define vsraq_n_u8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880150 | _NENC_18_16(8 - (shift_amount)), (Qd), (Qm)) ) + +// VSRI (immediate) +#define vsri_n_p16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900410 | _NENC_19_16(16 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_p8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880410 | _NENC_18_16(8 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_s16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900410 | _NENC_19_16(16 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_s32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm_acc( 0xf3a00410 | _NENC_20_16(32 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_s64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm_acc( 0xf3800490 | _NENC_21_16(64 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_s8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880410 | _NENC_18_16(8 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_u16(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_DdDm_acc( 0xf3900410 | _NENC_19_16(16 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_u32(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_DdDm_acc( 0xf3a00410 | _NENC_20_16(32 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_u64(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_DdDm_acc( 0xf3800490 | _NENC_21_16(64 - (shift_amount)), (Dd), (Dm)) ) +#define vsri_n_u8(Dd, Dm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_DdDm_acc( 0xf3880410 | _NENC_18_16(8 - (shift_amount)), (Dd), (Dm)) ) +#define vsriq_n_p16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900450 | _NENC_19_16(16 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_p8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880450 | _NENC_18_16(8 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_s16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900450 | _NENC_19_16(16 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_s32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm_acc( 0xf3a00450 | _NENC_20_16(32 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_s64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm_acc( 0xf38004d0 | _NENC_21_16(64 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_s8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880450 | _NENC_18_16(8 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_u16(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 16, "invalid shift amount"), __neon_QdQm_acc( 0xf3900450 | _NENC_19_16(16 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_u32(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 32, "invalid shift amount"), __neon_QdQm_acc( 0xf3a00450 | _NENC_20_16(32 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_u64(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 64, "invalid shift amount"), __neon_QdQm_acc( 0xf38004d0 | _NENC_21_16(64 - (shift_amount)), (Qd), (Qm)) ) +#define vsriq_n_u8(Qd, Qm, shift_amount) ( __static_assert((shift_amount) >= 1 && (shift_amount) <= 8, "invalid shift amount"), __neon_QdQm_acc( 0xf3880450 | _NENC_18_16(8 - (shift_amount)), (Qd), (Qm)) ) + +// VST1 (multiple single elements) +#define vst1_f32(pD, D) ( __neon_AdrD1( 0xf400078f, __float32ToN64(pD), (D)) ) +#define vst1_p16(pD, D) ( __neon_AdrD1( 0xf400074f, __poly16ToN64(pD), (D)) ) +#define vst1_p8(pD, D) ( __neon_AdrD1( 0xf400070f, __poly8ToN64(pD), (D)) ) +#define vst1_s16(pD, D) ( __neon_AdrD1( 0xf400074f, __int16ToN64(pD), (D)) ) +#define vst1_s32(pD, D) ( __neon_AdrD1( 0xf400078f, __int32ToN64(pD), (D)) ) +#define vst1_s64(pD, D) ( __neon_AdrD1( 0xf40007cf, __int64ToN64(pD), (D)) ) +#define vst1_s8(pD, D) ( __neon_AdrD1( 0xf400070f, __int8ToN64(pD), (D)) ) +#define vst1_u16(pD, D) ( __neon_AdrD1( 0xf400074f, __uint16ToN64(pD), (D)) ) +#define vst1_u32(pD, D) ( __neon_AdrD1( 0xf400078f, __uint32ToN64(pD), (D)) ) +#define vst1_u64(pD, D) ( __neon_AdrD1( 0xf40007cf, __uint64ToN64(pD), (D)) ) +#define vst1_u8(pD, D) ( __neon_AdrD1( 0xf400070f, __uint8ToN64(pD), (D)) ) +#define vst1_f32_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400078f | _NENC_5_4(_NEON_ALIGN64(align)), __float32ToN64(pD), (D)) ) +#define vst1_p16_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400074f | _NENC_5_4(_NEON_ALIGN64(align)), __poly16ToN64(pD), (D)) ) +#define vst1_p8_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400070f | _NENC_5_4(_NEON_ALIGN64(align)), __poly8ToN64(pD), (D)) ) +#define vst1_s16_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400074f | _NENC_5_4(_NEON_ALIGN64(align)), __int16ToN64(pD), (D)) ) +#define vst1_s32_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400078f | _NENC_5_4(_NEON_ALIGN64(align)), __int32ToN64(pD), (D)) ) +#define vst1_s64_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf40007cf | _NENC_5_4(_NEON_ALIGN64(align)), __int64ToN64(pD), (D)) ) +#define vst1_s8_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400070f | _NENC_5_4(_NEON_ALIGN64(align)), __int8ToN64(pD), (D)) ) +#define vst1_u16_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400074f | _NENC_5_4(_NEON_ALIGN64(align)), __uint16ToN64(pD), (D)) ) +#define vst1_u32_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400078f | _NENC_5_4(_NEON_ALIGN64(align)), __uint32ToN64(pD), (D)) ) +#define vst1_u64_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf40007cf | _NENC_5_4(_NEON_ALIGN64(align)), __uint64ToN64(pD), (D)) ) +#define vst1_u8_ex(pD, D, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrD1( 0xf400070f | _NENC_5_4(_NEON_ALIGN64(align)), __uint8ToN64(pD), (D)) ) +#define vst1q_f32(pD, Q) ( __neon_AdrQ1( 0xf4000a8f, __float32ToN64(pD), (Q)) ) +#define vst1q_p16(pD, Q) ( __neon_AdrQ1( 0xf4000a4f, __poly16ToN64(pD), (Q)) ) +#define vst1q_p8(pD, Q) ( __neon_AdrQ1( 0xf4000a0f, __poly8ToN64(pD), (Q)) ) +#define vst1q_s16(pD, Q) ( __neon_AdrQ1( 0xf4000a4f, __int16ToN64(pD), (Q)) ) +#define vst1q_s32(pD, Q) ( __neon_AdrQ1( 0xf4000a8f, __int32ToN64(pD), (Q)) ) +#define vst1q_s64(pD, Q) ( __neon_AdrQ1( 0xf4000acf, __int64ToN64(pD), (Q)) ) +#define vst1q_s8(pD, Q) ( __neon_AdrQ1( 0xf4000a0f, __int8ToN64(pD), (Q)) ) +#define vst1q_u16(pD, Q) ( __neon_AdrQ1( 0xf4000a4f, __uint16ToN64(pD), (Q)) ) +#define vst1q_u32(pD, Q) ( __neon_AdrQ1( 0xf4000a8f, __uint32ToN64(pD), (Q)) ) +#define vst1q_u64(pD, Q) ( __neon_AdrQ1( 0xf4000acf, __uint64ToN64(pD), (Q)) ) +#define vst1q_u8(pD, Q) ( __neon_AdrQ1( 0xf4000a0f, __uint8ToN64(pD), (Q)) ) +#define vst1q_f32_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a8f | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64(pD), (Q)) ) +#define vst1q_p16_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a4f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly16ToN64(pD), (Q)) ) +#define vst1q_p8_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a0f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly8ToN64(pD), (Q)) ) +#define vst1q_s16_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a4f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int16ToN64(pD), (Q)) ) +#define vst1q_s32_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a8f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64(pD), (Q)) ) +#define vst1q_s64_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __int64ToN64(pD), (Q)) ) +#define vst1q_s8_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a0f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int8ToN64(pD), (Q)) ) +#define vst1q_u16_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a4f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint16ToN64(pD), (Q)) ) +#define vst1q_u32_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a8f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64(pD), (Q)) ) +#define vst1q_u64_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint64ToN64(pD), (Q)) ) +#define vst1q_u8_ex(pD, Q, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf4000a0f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint8ToN64(pD), (Q)) ) + +// VST1 (single element from one lane) +#define vst1_lane_f32(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrD1( 0xf480080f | _NENC_7(lane), __float32ToN64(pD), (D)) ) +#define vst1_lane_p16(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrD1( 0xf480040f | _NENC_7_6(lane), __poly16ToN64(pD), (D)) ) +#define vst1_lane_p8(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrD1( 0xf480000f | _NENC_7_5(lane), __poly8ToN64(pD), (D)) ) +#define vst1_lane_s16(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrD1( 0xf480040f | _NENC_7_6(lane), __int16ToN64(pD), (D)) ) +#define vst1_lane_s32(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrD1( 0xf480080f | _NENC_7(lane), __int32ToN64(pD), (D)) ) +#define vst1_lane_s8(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrD1( 0xf480000f | _NENC_7_5(lane), __int8ToN64(pD), (D)) ) +#define vst1_lane_u16(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrD1( 0xf480040f | _NENC_7_6(lane), __uint16ToN64(pD), (D)) ) +#define vst1_lane_u32(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrD1( 0xf480080f | _NENC_7(lane), __uint32ToN64(pD), (D)) ) +#define vst1_lane_u8(pD, D, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrD1( 0xf480000f | _NENC_7_5(lane), __uint8ToN64(pD), (D)) ) +#define vst1q_lane_f32(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQ1( 0xf480080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __float32ToN64(pD), (Q)) ) +#define vst1q_lane_p16(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQ1( 0xf480040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __poly16ToN64(pD), (Q)) ) +#define vst1q_lane_p8(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_AdrQ1( 0xf480000f | _NENC_7_5((lane) % 8) | _NENC_12((lane) >= 8 ? 1 : 0), __poly8ToN64(pD), (Q)) ) +#define vst1q_lane_s16(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQ1( 0xf480040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __int16ToN64(pD), (Q)) ) +#define vst1q_lane_s32(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQ1( 0xf480080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __int32ToN64(pD), (Q)) ) +#define vst1q_lane_s8(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_AdrQ1( 0xf480000f | _NENC_7_5((lane) % 8) | _NENC_12((lane) >= 8 ? 1 : 0), __int8ToN64(pD), (Q)) ) +#define vst1q_lane_u16(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQ1( 0xf480040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __uint16ToN64(pD), (Q)) ) +#define vst1q_lane_u32(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQ1( 0xf480080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __uint32ToN64(pD), (Q)) ) +#define vst1q_lane_u8(pD, Q, lane) ( __static_assert((lane) >= 0 && (lane) < 16, "invalid lane index"), __neon_AdrQ1( 0xf480000f | _NENC_7_5((lane) % 8) | _NENC_12((lane) >= 8 ? 1 : 0), __uint8ToN64(pD), (Q)) ) + +// VST1 (single element from one lane, aligned) +#define vst1_lane_f32_ex(pD, D, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrD1( 0xf480080f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), __float32ToN64(pD), (D)) ) +#define vst1_lane_p16_ex(pD, D, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrD1( 0xf480040f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN16(align)), __poly16ToN64(pD), (D)) ) +#define vst1_lane_s16_ex(pD, D, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrD1( 0xf480040f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN16(align)), __int16ToN64(pD), (D)) ) +#define vst1_lane_s32_ex(pD, D, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrD1( 0xf480080f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), __int32ToN64(pD), (D)) ) +#define vst1_lane_u16_ex(pD, D, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrD1( 0xf480040f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN16(align)), __uint16ToN64(pD), (D)) ) +#define vst1_lane_u32_ex(pD, D, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrD1( 0xf480080f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), __uint32ToN64(pD), (D)) ) +#define vst1q_lane_f32_ex(pD, Q, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf480080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), __float32ToN64(pD), (Q)) ) +#define vst1q_lane_p16_ex(pD, Q, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf480040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN16(align)), __poly16ToN64(pD), (Q)) ) +#define vst1q_lane_s16_ex(pD, Q, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf480040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN16(align)), __int16ToN64(pD), (Q)) ) +#define vst1q_lane_s32_ex(pD, Q, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf480080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), __int32ToN64(pD), (Q)) ) +#define vst1q_lane_u16_ex(pD, Q, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf480040f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN16(align)), __uint16ToN64(pD), (Q)) ) +#define vst1q_lane_u32_ex(pD, Q, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrQ1( 0xf480080f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN32(align) > 0 ? 3 : 0), __uint32ToN64(pD), (Q)) ) + +// VST2 (multiple 2-element structures) +#define vst2_f32(pD, D2) ( __neon_AdrDx2( 0xf400088f, __float32ToN64(pD), (D2)) ) +#define vst2_p16(pD, D2) ( __neon_AdrDx2( 0xf400084f, __poly16ToN64(pD), (D2)) ) +#define vst2_p8(pD, D2) ( __neon_AdrDx2( 0xf400080f, __poly8ToN64(pD), (D2)) ) +#define vst2_s16(pD, D2) ( __neon_AdrDx2( 0xf400084f, __int16ToN64(pD), (D2)) ) +#define vst2_s32(pD, D2) ( __neon_AdrDx2( 0xf400088f, __int32ToN64(pD), (D2)) ) +#define vst2_s8(pD, D2) ( __neon_AdrDx2( 0xf400080f, __int8ToN64(pD), (D2)) ) +#define vst2_u16(pD, D2) ( __neon_AdrDx2( 0xf400084f, __uint16ToN64(pD), (D2)) ) +#define vst2_u32(pD, D2) ( __neon_AdrDx2( 0xf400088f, __uint32ToN64(pD), (D2)) ) +#define vst2_u8(pD, D2) ( __neon_AdrDx2( 0xf400080f, __uint8ToN64(pD), (D2)) ) +#define vst2_s64(pD, D2) ( __neon_AdrDx2( 0xf4000acf, __int64ToN64(pD), (D2)) ) +#define vst2_u64(pD, D2) ( __neon_AdrDx2( 0xf4000acf, __uint64ToN64(pD), (D2)) ) +#define vst2_s64_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf4000acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __int64ToN64(pD), (D2)) ) +#define vst2_u64_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf4000acf | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint64ToN64(pD), (D2)) ) +#define vst2_f32_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400088f | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64(pD), (D2)) ) +#define vst2_p16_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400084f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly16ToN64(pD), (D2)) ) +#define vst2_p8_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400080f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly8ToN64(pD), (D2)) ) +#define vst2_s16_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400084f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int16ToN64(pD), (D2)) ) +#define vst2_s32_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400088f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64(pD), (D2)) ) +#define vst2_s8_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400080f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int8ToN64(pD), (D2)) ) +#define vst2_u16_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400084f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint16ToN64(pD), (D2)) ) +#define vst2_u32_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400088f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64(pD), (D2)) ) +#define vst2_u8_ex(pD, D2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx2( 0xf400080f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint8ToN64(pD), (D2)) ) +#define vst2q_f32(pD, Q2) ( __neon_AdrQx2( 0xf400098f, __float32ToN64(pD), (Q2)) ) +#define vst2q_p16(pD, Q2) ( __neon_AdrQx2( 0xf400094f, __poly16ToN64(pD), (Q2)) ) +#define vst2q_p8(pD, Q2) ( __neon_AdrQx2( 0xf400090f, __poly8ToN64(pD), (Q2)) ) +#define vst2q_s16(pD, Q2) ( __neon_AdrQx2( 0xf400094f, __int16ToN64(pD), (Q2)) ) +#define vst2q_s32(pD, Q2) ( __neon_AdrQx2( 0xf400098f, __int32ToN64(pD), (Q2)) ) +#define vst2q_s8(pD, Q2) ( __neon_AdrQx2( 0xf400090f, __int8ToN64(pD), (Q2)) ) +#define vst2q_u16(pD, Q2) ( __neon_AdrQx2( 0xf400094f, __uint16ToN64(pD), (Q2)) ) +#define vst2q_u32(pD, Q2) ( __neon_AdrQx2( 0xf400098f, __uint32ToN64(pD), (Q2)) ) +#define vst2q_u8(pD, Q2) ( __neon_AdrQx2( 0xf400090f, __uint8ToN64(pD), (Q2)) ) +#define vst2q_f32_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400098f | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64(pD), (Q2)) ) +#define vst2q_p16_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400094f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly16ToN64(pD), (Q2)) ) +#define vst2q_p8_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400090f | _NENC_5_4(_NEON_ALIGN64_128(align)), __poly8ToN64(pD), (Q2)) ) +#define vst2q_s16_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400094f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int16ToN64(pD), (Q2)) ) +#define vst2q_s32_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400098f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64(pD), (Q2)) ) +#define vst2q_s8_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400090f | _NENC_5_4(_NEON_ALIGN64_128(align)), __int8ToN64(pD), (Q2)) ) +#define vst2q_u16_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400094f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint16ToN64(pD), (Q2)) ) +#define vst2q_u32_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400098f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64(pD), (Q2)) ) +#define vst2q_u8_ex(pD, Q2, align) ( __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx2( 0xf400090f | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint8ToN64(pD), (Q2)) ) + +// VST2 (single 2-element structure from one lane) +#define vst2_lane_f32(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx2x( 0xf480090f | _NENC_7(lane), __float32ToN64(pD), (D2)) ) +#define vst2_lane_p16(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx2x( 0xf480050f | _NENC_7_6(lane), __poly16ToN64(pD), (D2)) ) +#define vst2_lane_p8(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx2x( 0xf480010f | _NENC_7_5(lane), __poly8ToN64(pD), (D2)) ) +#define vst2_lane_s16(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx2x( 0xf480050f | _NENC_7_6(lane), __int16ToN64(pD), (D2)) ) +#define vst2_lane_s32(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx2x( 0xf480090f | _NENC_7(lane), __int32ToN64(pD), (D2)) ) +#define vst2_lane_s8(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx2x( 0xf480010f | _NENC_7_5(lane), __int8ToN64(pD), (D2)) ) +#define vst2_lane_u16(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx2x( 0xf480050f | _NENC_7_6(lane), __uint16ToN64(pD), (D2)) ) +#define vst2_lane_u32(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx2x( 0xf480090f | _NENC_7(lane), __uint32ToN64(pD), (D2)) ) +#define vst2_lane_u8(pD, D2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx2x( 0xf480010f | _NENC_7_5(lane), __uint8ToN64(pD), (D2)) ) +#define vst2q_lane_f32(pD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx2x( 0xf480094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __float32ToN64(pD), (Q2)) ) +#define vst2q_lane_p16(pD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx2x( 0xf480052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __poly16ToN64(pD), (Q2)) ) +#define vst2q_lane_s16(pD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx2x( 0xf480052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __int16ToN64(pD), (Q2)) ) +#define vst2q_lane_s32(pD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx2x( 0xf480094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __int32ToN64(pD), (Q2)) ) +#define vst2q_lane_u16(pD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx2x( 0xf480052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __uint16ToN64(pD), (Q2)) ) +#define vst2q_lane_u32(pD, Q2, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx2x( 0xf480094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __uint32ToN64(pD), (Q2)) ) + +// VST2 (single 2-element structure from one lane, aligned) +#define vst2_lane_f32_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480090f | _NENC_7(lane) | _NENC_4(_NEON_ALIGN64(align)), __float32ToN64(pD), (D2)) ) +#define vst2_lane_p16_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480050f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN32(align)), __poly16ToN64(pD), (D2)) ) +#define vst2_lane_p8_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480010f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN16(align)), __poly8ToN64(pD), (D2)) ) +#define vst2_lane_s16_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480050f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN32(align)), __int16ToN64(pD), (D2)) ) +#define vst2_lane_s32_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480090f | _NENC_7(lane) | _NENC_4(_NEON_ALIGN64(align)), __int32ToN64(pD), (D2)) ) +#define vst2_lane_s8_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480010f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN16(align)), __int8ToN64(pD), (D2)) ) +#define vst2_lane_u16_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480050f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN32(align)), __uint16ToN64(pD), (D2)) ) +#define vst2_lane_u32_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480090f | _NENC_7(lane) | _NENC_4(_NEON_ALIGN64(align)), __uint32ToN64(pD), (D2)) ) +#define vst2_lane_u8_ex(pD, D2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN16(align) >= 0, "invalid align"), __neon_AdrDx2x( 0xf480010f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN16(align)), __uint8ToN64(pD), (D2)) ) +#define vst2q_lane_f32_ex(pD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx2x( 0xf480094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), __float32ToN64(pD), (Q2)) ) +#define vst2q_lane_p16_ex(pD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrQx2x( 0xf480052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN32(align)), __poly16ToN64(pD), (Q2)) ) +#define vst2q_lane_s16_ex(pD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrQx2x( 0xf480052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN32(align)), __int16ToN64(pD), (Q2)) ) +#define vst2q_lane_s32_ex(pD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx2x( 0xf480094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), __int32ToN64(pD), (Q2)) ) +#define vst2q_lane_u16_ex(pD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrQx2x( 0xf480052f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN32(align)), __uint16ToN64(pD), (Q2)) ) +#define vst2q_lane_u32_ex(pD, Q2, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx2x( 0xf480094f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), __uint32ToN64(pD), (Q2)) ) + +// VST3 (multiple 3-element structures) +#define vst3_f32(pD, D3) ( __neon_AdrDx3( 0xf400048f, __float32ToN64(pD), (D3)) ) +#define vst3_p16(pD, D3) ( __neon_AdrDx3( 0xf400044f, __poly16ToN64(pD), (D3)) ) +#define vst3_p8(pD, D3) ( __neon_AdrDx3( 0xf400040f, __poly8ToN64(pD), (D3)) ) +#define vst3_s16(pD, D3) ( __neon_AdrDx3( 0xf400044f, __int16ToN64(pD), (D3)) ) +#define vst3_s32(pD, D3) ( __neon_AdrDx3( 0xf400048f, __int32ToN64(pD), (D3)) ) +#define vst3_s8(pD, D3) ( __neon_AdrDx3( 0xf400040f, __int8ToN64(pD), (D3)) ) +#define vst3_u16(pD, D3) ( __neon_AdrDx3( 0xf400044f, __uint16ToN64(pD), (D3)) ) +#define vst3_u32(pD, D3) ( __neon_AdrDx3( 0xf400048f, __uint32ToN64(pD), (D3)) ) +#define vst3_u8(pD, D3) ( __neon_AdrDx3( 0xf400040f, __uint8ToN64(pD), (D3)) ) +#define vst3_s64(pD, D3) ( __neon_AdrDx3( 0xf40006cf, __int64ToN64(pD), (D3)) ) +#define vst3_u64(pD, D3) ( __neon_AdrDx3( 0xf40006cf, __uint64ToN64(pD), (D3)) ) +#define vst3_s64_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf40006cf | _NENC_4(_NEON_ALIGN64(align)), __int64ToN64(pD), (D3)) ) +#define vst3_u64_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf40006cf | _NENC_4(_NEON_ALIGN64(align)), __uint64ToN64(pD), (D3)) ) +#define vst3_f32_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400048f | _NENC_4(_NEON_ALIGN64(align)), __float32ToN64(pD), (D3)) ) +#define vst3_p16_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400044f | _NENC_4(_NEON_ALIGN64(align)), __poly16ToN64(pD), (D3)) ) +#define vst3_p8_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400040f | _NENC_4(_NEON_ALIGN64(align)), __poly8ToN64(pD), (D3)) ) +#define vst3_s16_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400044f | _NENC_4(_NEON_ALIGN64(align)), __int16ToN64(pD), (D3)) ) +#define vst3_s32_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400048f | _NENC_4(_NEON_ALIGN64(align)), __int32ToN64(pD), (D3)) ) +#define vst3_s8_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400040f | _NENC_4(_NEON_ALIGN64(align)), __int8ToN64(pD), (D3)) ) +#define vst3_u16_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400044f | _NENC_4(_NEON_ALIGN64(align)), __uint16ToN64(pD), (D3)) ) +#define vst3_u32_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400048f | _NENC_4(_NEON_ALIGN64(align)), __uint32ToN64(pD), (D3)) ) +#define vst3_u8_ex(pD, D3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx3( 0xf400040f | _NENC_4(_NEON_ALIGN64(align)), __uint8ToN64(pD), (D3)) ) +#define vst3q_f32(pD, Q3) ( __neon_AdrQx3( 0xf400058f, __float32ToN64(pD), (Q3)) ) +#define vst3q_p16(pD, Q3) ( __neon_AdrQx3( 0xf400054f, __poly16ToN64(pD), (Q3)) ) +#define vst3q_p8(pD, Q3) ( __neon_AdrQx3( 0xf400050f, __poly8ToN64(pD), (Q3)) ) +#define vst3q_s16(pD, Q3) ( __neon_AdrQx3( 0xf400054f, __int16ToN64(pD), (Q3)) ) +#define vst3q_s32(pD, Q3) ( __neon_AdrQx3( 0xf400058f, __int32ToN64(pD), (Q3)) ) +#define vst3q_s8(pD, Q3) ( __neon_AdrQx3( 0xf400050f, __int8ToN64(pD), (Q3)) ) +#define vst3q_u16(pD, Q3) ( __neon_AdrQx3( 0xf400054f, __uint16ToN64(pD), (Q3)) ) +#define vst3q_u32(pD, Q3) ( __neon_AdrQx3( 0xf400058f, __uint32ToN64(pD), (Q3)) ) +#define vst3q_u8(pD, Q3) ( __neon_AdrQx3( 0xf400050f, __uint8ToN64(pD), (Q3)) ) +#define vst3q_f32_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400058f | _NENC_4(_NEON_ALIGN64(align)), __float32ToN64(pD), (Q3)) ) +#define vst3q_p16_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400054f | _NENC_4(_NEON_ALIGN64(align)), __poly16ToN64(pD), (Q3)) ) +#define vst3q_p8_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400050f | _NENC_4(_NEON_ALIGN64(align)), __poly8ToN64(pD), (Q3)) ) +#define vst3q_s16_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400054f | _NENC_4(_NEON_ALIGN64(align)), __int16ToN64(pD), (Q3)) ) +#define vst3q_s32_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400058f | _NENC_4(_NEON_ALIGN64(align)), __int32ToN64(pD), (Q3)) ) +#define vst3q_s8_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400050f | _NENC_4(_NEON_ALIGN64(align)), __int8ToN64(pD), (Q3)) ) +#define vst3q_u16_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400054f | _NENC_4(_NEON_ALIGN64(align)), __uint16ToN64(pD), (Q3)) ) +#define vst3q_u32_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400058f | _NENC_4(_NEON_ALIGN64(align)), __uint32ToN64(pD), (Q3)) ) +#define vst3q_u8_ex(pD, Q3, align) ( __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx3( 0xf400050f | _NENC_4(_NEON_ALIGN64(align)), __uint8ToN64(pD), (Q3)) ) + +// VST3 (single 3-element structure from one lane) +#define vst3_lane_f32(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx3x( 0xf4800a0f | _NENC_7(lane), __float32ToN64(pD), (D3)) ) +#define vst3_lane_p16(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx3x( 0xf480060f | _NENC_7_6(lane), __poly16ToN64(pD), (D3)) ) +#define vst3_lane_p8(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx3x( 0xf480020f | _NENC_7_5(lane), __poly8ToN64(pD), (D3)) ) +#define vst3_lane_s16(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx3x( 0xf480060f | _NENC_7_6(lane), __int16ToN64(pD), (D3)) ) +#define vst3_lane_s32(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx3x( 0xf4800a0f | _NENC_7(lane), __int32ToN64(pD), (D3)) ) +#define vst3_lane_s8(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx3x( 0xf480020f | _NENC_7_5(lane), __int8ToN64(pD), (D3)) ) +#define vst3_lane_u16(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx3x( 0xf480060f | _NENC_7_6(lane), __uint16ToN64(pD), (D3)) ) +#define vst3_lane_u32(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx3x( 0xf4800a0f | _NENC_7(lane), __uint32ToN64(pD), (D3)) ) +#define vst3_lane_u8(pD, D3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx3x( 0xf480020f | _NENC_7_5(lane), __uint8ToN64(pD), (D3)) ) +#define vst3q_lane_f32(pD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx3x( 0xf4800a4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __float32ToN64(pD), (Q3)) ) +#define vst3q_lane_p16(pD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx3x( 0xf480062f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __poly16ToN64(pD), (Q3)) ) +#define vst3q_lane_s16(pD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx3x( 0xf480062f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __int16ToN64(pD), (Q3)) ) +#define vst3q_lane_s32(pD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx3x( 0xf4800a4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __int32ToN64(pD), (Q3)) ) +#define vst3q_lane_u16(pD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx3x( 0xf480062f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __uint16ToN64(pD), (Q3)) ) +#define vst3q_lane_u32(pD, Q3, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx3x( 0xf4800a4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __uint32ToN64(pD), (Q3)) ) + +// VST4 (multiple 4-element structures) +#define vst4_f32(pD, D4) ( __neon_AdrDx4( 0xf400008f, __float32ToN64(pD), (D4)) ) +#define vst4_p16(pD, D4) ( __neon_AdrDx4( 0xf400004f, __poly16ToN64(pD), (D4)) ) +#define vst4_p8(pD, D4) ( __neon_AdrDx4( 0xf400000f, __poly8ToN64(pD), (D4)) ) +#define vst4_s16(pD, D4) ( __neon_AdrDx4( 0xf400004f, __int16ToN64(pD), (D4)) ) +#define vst4_s32(pD, D4) ( __neon_AdrDx4( 0xf400008f, __int32ToN64(pD), (D4)) ) +#define vst4_s8(pD, D4) ( __neon_AdrDx4( 0xf400000f, __int8ToN64(pD), (D4)) ) +#define vst4_u16(pD, D4) ( __neon_AdrDx4( 0xf400004f, __uint16ToN64(pD), (D4)) ) +#define vst4_u32(pD, D4) ( __neon_AdrDx4( 0xf400008f, __uint32ToN64(pD), (D4)) ) +#define vst4_u8(pD, D4) ( __neon_AdrDx4( 0xf400000f, __uint8ToN64(pD), (D4)) ) +#define vst4_s64(pD, D4) ( __neon_AdrDx4( 0xf40002cf, __int64ToN64(pD), (D4)) ) +#define vst4_u64(pD, D4) ( __neon_AdrDx4( 0xf40002cf, __uint64ToN64(pD), (D4)) ) +#define vst4_s64_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf40002cf | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int64ToN64(pD), (D4)) ) +#define vst4_u64_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf40002cf | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint64ToN64(pD), (D4)) ) +#define vst4_f32_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400008f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __float32ToN64(pD), (D4)) ) +#define vst4_p16_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400004f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly16ToN64(pD), (D4)) ) +#define vst4_p8_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400000f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly8ToN64(pD), (D4)) ) +#define vst4_s16_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400004f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int16ToN64(pD), (D4)) ) +#define vst4_s32_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400008f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int32ToN64(pD), (D4)) ) +#define vst4_s8_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400000f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int8ToN64(pD), (D4)) ) +#define vst4_u16_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400004f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint16ToN64(pD), (D4)) ) +#define vst4_u32_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400008f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint32ToN64(pD), (D4)) ) +#define vst4_u8_ex(pD, D4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrDx4( 0xf400000f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint8ToN64(pD), (D4)) ) +#define vst4q_f32(pD, Q4) ( __neon_AdrQx4( 0xf400018f, __float32ToN64(pD), (Q4)) ) +#define vst4q_p16(pD, Q4) ( __neon_AdrQx4( 0xf400014f, __poly16ToN64(pD), (Q4)) ) +#define vst4q_p8(pD, Q4) ( __neon_AdrQx4( 0xf400010f, __poly8ToN64(pD), (Q4)) ) +#define vst4q_s16(pD, Q4) ( __neon_AdrQx4( 0xf400014f, __int16ToN64(pD), (Q4)) ) +#define vst4q_s32(pD, Q4) ( __neon_AdrQx4( 0xf400018f, __int32ToN64(pD), (Q4)) ) +#define vst4q_s8(pD, Q4) ( __neon_AdrQx4( 0xf400010f, __int8ToN64(pD), (Q4)) ) +#define vst4q_u16(pD, Q4) ( __neon_AdrQx4( 0xf400014f, __uint16ToN64(pD), (Q4)) ) +#define vst4q_u32(pD, Q4) ( __neon_AdrQx4( 0xf400018f, __uint32ToN64(pD), (Q4)) ) +#define vst4q_u8(pD, Q4) ( __neon_AdrQx4( 0xf400010f, __uint8ToN64(pD), (Q4)) ) +#define vst4q_f32_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400018f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __float32ToN64(pD), (Q4)) ) +#define vst4q_p16_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400014f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly16ToN64(pD), (Q4)) ) +#define vst4q_p8_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400010f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __poly8ToN64(pD), (Q4)) ) +#define vst4q_s16_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400014f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int16ToN64(pD), (Q4)) ) +#define vst4q_s32_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400018f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int32ToN64(pD), (Q4)) ) +#define vst4q_s8_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400010f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __int8ToN64(pD), (Q4)) ) +#define vst4q_u16_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400014f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint16ToN64(pD), (Q4)) ) +#define vst4q_u32_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400018f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint32ToN64(pD), (Q4)) ) +#define vst4q_u8_ex(pD, Q4, align) ( __static_assert(_NEON_ALIGN64_128_256(align) >= 0, "invalid align"), __neon_AdrQx4( 0xf400010f | _NENC_5_4(_NEON_ALIGN64_128_256(align)), __uint8ToN64(pD), (Q4)) ) + +// VST4 (single 4-element structure from one lane) +#define vst4_lane_f32(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx4x( 0xf4800b0f | _NENC_7(lane), __float32ToN64(pD), (D4)) ) +#define vst4_lane_p16(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx4x( 0xf480070f | _NENC_7_6(lane), __poly16ToN64(pD), (D4)) ) +#define vst4_lane_p8(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx4x( 0xf480030f | _NENC_7_5(lane), __poly8ToN64(pD), (D4)) ) +#define vst4_lane_s16(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx4x( 0xf480070f | _NENC_7_6(lane), __int16ToN64(pD), (D4)) ) +#define vst4_lane_s32(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx4x( 0xf4800b0f | _NENC_7(lane), __int32ToN64(pD), (D4)) ) +#define vst4_lane_s8(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx4x( 0xf480030f | _NENC_7_5(lane), __int8ToN64(pD), (D4)) ) +#define vst4_lane_u16(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrDx4x( 0xf480070f | _NENC_7_6(lane), __uint16ToN64(pD), (D4)) ) +#define vst4_lane_u32(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __neon_AdrDx4x( 0xf4800b0f | _NENC_7(lane), __uint32ToN64(pD), (D4)) ) +#define vst4_lane_u8(pD, D4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrDx4x( 0xf480030f | _NENC_7_5(lane), __uint8ToN64(pD), (D4)) ) +#define vst4q_lane_f32(pD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx4x( 0xf4800b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __float32ToN64(pD), (Q4)) ) +#define vst4q_lane_p16(pD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx4x( 0xf480072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __poly16ToN64(pD), (Q4)) ) +#define vst4q_lane_s16(pD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx4x( 0xf480072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __int16ToN64(pD), (Q4)) ) +#define vst4q_lane_s32(pD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx4x( 0xf4800b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __int32ToN64(pD), (Q4)) ) +#define vst4q_lane_u16(pD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __neon_AdrQx4x( 0xf480072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0), __uint16ToN64(pD), (Q4)) ) +#define vst4q_lane_u32(pD, Q4, lane) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __neon_AdrQx4x( 0xf4800b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0), __uint32ToN64(pD), (Q4)) ) + +// VST4 (single 4-element structure from one lane, aligned) +#define vst4_lane_f32_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf4800b0f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64(pD), (D4)) ) +#define vst4_lane_p16_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf480070f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN64(align)), __poly16ToN64(pD), (D4)) ) +#define vst4_lane_p8_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf480030f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN32(align)), __poly8ToN64(pD), (D4)) ) +#define vst4_lane_s16_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf480070f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN64(align)), __int16ToN64(pD), (D4)) ) +#define vst4_lane_s32_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf4800b0f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64(pD), (D4)) ) +#define vst4_lane_s8_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf480030f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN32(align)), __int8ToN64(pD), (D4)) ) +#define vst4_lane_u16_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf480070f | _NENC_7_6(lane) | _NENC_4(_NEON_ALIGN64(align)), __uint16ToN64(pD), (D4)) ) +#define vst4_lane_u32_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 2, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf4800b0f | _NENC_7(lane) | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64(pD), (D4)) ) +#define vst4_lane_u8_ex(pD, D4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN32(align) >= 0, "invalid align"), __neon_AdrDx4x( 0xf480030f | _NENC_7_5(lane) | _NENC_4(_NEON_ALIGN32(align)), __uint8ToN64(pD), (D4)) ) +#define vst4q_lane_f32_ex(pD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx4x( 0xf4800b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN64_128(align)), __float32ToN64(pD), (Q4)) ) +#define vst4q_lane_p16_ex(pD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx4x( 0xf480072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), __poly16ToN64(pD), (Q4)) ) +#define vst4q_lane_s16_ex(pD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx4x( 0xf480072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), __int16ToN64(pD), (Q4)) ) +#define vst4q_lane_s32_ex(pD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx4x( 0xf4800b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN64_128(align)), __int32ToN64(pD), (Q4)) ) +#define vst4q_lane_u16_ex(pD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 8, "invalid lane index"), __static_assert(_NEON_ALIGN64(align) >= 0, "invalid align"), __neon_AdrQx4x( 0xf480072f | _NENC_7_6((lane) % 4) | _NENC_12((lane) >= 4 ? 1 : 0) | _NENC_4(_NEON_ALIGN64(align)), __uint16ToN64(pD), (Q4)) ) +#define vst4q_lane_u32_ex(pD, Q4, lane, align) ( __static_assert((lane) >= 0 && (lane) < 4, "invalid lane index"), __static_assert(_NEON_ALIGN64_128(align) >= 0, "invalid align"), __neon_AdrQx4x( 0xf4800b4f | _NENC_7((lane) % 2) | _NENC_12((lane) >= 2 ? 1 : 0) | _NENC_5_4(_NEON_ALIGN64_128(align)), __uint32ToN64(pD), (Q4)) ) + +// VSUB +#define vsub_f32(Dn, Dm) ( __neon_DdDnDm( 0xf2200d00, (Dn), (Dm)) ) +#define vsub_s16(Dn, Dm) ( __neon_DdDnDm( 0xf3100800, (Dn), (Dm)) ) +#define vsub_s32(Dn, Dm) ( __neon_DdDnDm( 0xf3200800, (Dn), (Dm)) ) +#define vsub_s64(Dn, Dm) ( __neon_DdDnDm( 0xf3300800, (Dn), (Dm)) ) +#define vsub_s8(Dn, Dm) ( __neon_DdDnDm( 0xf3000800, (Dn), (Dm)) ) +#define vsub_u16(Dn, Dm) ( __neon_DdDnDm( 0xf3100800, (Dn), (Dm)) ) +#define vsub_u32(Dn, Dm) ( __neon_DdDnDm( 0xf3200800, (Dn), (Dm)) ) +#define vsub_u64(Dn, Dm) ( __neon_DdDnDm( 0xf3300800, (Dn), (Dm)) ) +#define vsub_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3000800, (Dn), (Dm)) ) +#define vsubq_f32(Qn, Qm) ( __neon_QdQnQm( 0xf2200d40, (Qn), (Qm)) ) +#define vsubq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf3100840, (Qn), (Qm)) ) +#define vsubq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf3200840, (Qn), (Qm)) ) +#define vsubq_s64(Qn, Qm) ( __neon_QdQnQm( 0xf3300840, (Qn), (Qm)) ) +#define vsubq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf3000840, (Qn), (Qm)) ) +#define vsubq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf3100840, (Qn), (Qm)) ) +#define vsubq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf3200840, (Qn), (Qm)) ) +#define vsubq_u64(Qn, Qm) ( __neon_QdQnQm( 0xf3300840, (Qn), (Qm)) ) +#define vsubq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf3000840, (Qn), (Qm)) ) + +// VSUBHN, VRSUBHN +#define vrsubhn_s16(Qn, Qm) ( __neon_DdQnQm( 0xf3800600, (Qn), (Qm)) ) +#define vrsubhn_s32(Qn, Qm) ( __neon_DdQnQm( 0xf3900600, (Qn), (Qm)) ) +#define vrsubhn_s64(Qn, Qm) ( __neon_DdQnQm( 0xf3a00600, (Qn), (Qm)) ) +#define vrsubhn_u16(Qn, Qm) ( __neon_DdQnQm( 0xf3800600, (Qn), (Qm)) ) +#define vrsubhn_u32(Qn, Qm) ( __neon_DdQnQm( 0xf3900600, (Qn), (Qm)) ) +#define vrsubhn_u64(Qn, Qm) ( __neon_DdQnQm( 0xf3a00600, (Qn), (Qm)) ) +#define vsubhn_s16(Qn, Qm) ( __neon_DdQnQm( 0xf2800600, (Qn), (Qm)) ) +#define vsubhn_s32(Qn, Qm) ( __neon_DdQnQm( 0xf2900600, (Qn), (Qm)) ) +#define vsubhn_s64(Qn, Qm) ( __neon_DdQnQm( 0xf2a00600, (Qn), (Qm)) ) +#define vsubhn_u16(Qn, Qm) ( __neon_DdQnQm( 0xf2800600, (Qn), (Qm)) ) +#define vsubhn_u32(Qn, Qm) ( __neon_DdQnQm( 0xf2900600, (Qn), (Qm)) ) +#define vsubhn_u64(Qn, Qm) ( __neon_DdQnQm( 0xf2a00600, (Qn), (Qm)) ) + +// VSUBL, VSUBW +#define vsubl_s16(Dn, Dm) ( __neon_QdDnDm( 0xf2900200, (Dn), (Dm)) ) +#define vsubl_s32(Dn, Dm) ( __neon_QdDnDm( 0xf2a00200, (Dn), (Dm)) ) +#define vsubl_s8(Dn, Dm) ( __neon_QdDnDm( 0xf2800200, (Dn), (Dm)) ) +#define vsubl_u16(Dn, Dm) ( __neon_QdDnDm( 0xf3900200, (Dn), (Dm)) ) +#define vsubl_u32(Dn, Dm) ( __neon_QdDnDm( 0xf3a00200, (Dn), (Dm)) ) +#define vsubl_u8(Dn, Dm) ( __neon_QdDnDm( 0xf3800200, (Dn), (Dm)) ) +#define vsubw_s16(Qn, Dm) ( __neon_QdQnDm( 0xf2900300, (Qn), (Dm)) ) +#define vsubw_s32(Qn, Dm) ( __neon_QdQnDm( 0xf2a00300, (Qn), (Dm)) ) +#define vsubw_s8(Qn, Dm) ( __neon_QdQnDm( 0xf2800300, (Qn), (Dm)) ) +#define vsubw_u16(Qn, Dm) ( __neon_QdQnDm( 0xf3900300, (Qn), (Dm)) ) +#define vsubw_u32(Qn, Dm) ( __neon_QdQnDm( 0xf3a00300, (Qn), (Dm)) ) +#define vsubw_u8(Qn, Dm) ( __neon_QdQnDm( 0xf3800300, (Qn), (Dm)) ) + +// VTBL, VTBX +#define vtbl2_p8(D2, Dm) ( __neon_DdDx2Dm( 0xf3b00900, (D2), (Dm)) ) +#define vtbl2_s8(D2, Dm) ( __neon_DdDx2Dm( 0xf3b00900, (D2), (Dm)) ) +#define vtbl2_u8(D2, Dm) ( __neon_DdDx2Dm( 0xf3b00900, (D2), (Dm)) ) +#define vtbx2_p8(Dd, D2, Dm) ( __neon_DdDx2Dm_acc( 0xf3b00940, (Dd), (D2), (Dm)) ) +#define vtbx2_s8(Dd, D2, Dm) ( __neon_DdDx2Dm_acc( 0xf3b00940, (Dd), (D2), (Dm)) ) +#define vtbx2_u8(Dd, D2, Dm) ( __neon_DdDx2Dm_acc( 0xf3b00940, (Dd), (D2), (Dm)) ) +#define vtbl3_p8(D3, Dm) ( __neon_DdDx3Dm( 0xf3b00a00, (D3), (Dm)) ) +#define vtbl3_s8(D3, Dm) ( __neon_DdDx3Dm( 0xf3b00a00, (D3), (Dm)) ) +#define vtbl3_u8(D3, Dm) ( __neon_DdDx3Dm( 0xf3b00a00, (D3), (Dm)) ) +#define vtbx3_p8(Dd, D3, Dm) ( __neon_DdDx3Dm_acc( 0xf3b00a40, (Dd), (D3), (Dm)) ) +#define vtbx3_s8(Dd, D3, Dm) ( __neon_DdDx3Dm_acc( 0xf3b00a40, (Dd), (D3), (Dm)) ) +#define vtbx3_u8(Dd, D3, Dm) ( __neon_DdDx3Dm_acc( 0xf3b00a40, (Dd), (D3), (Dm)) ) +#define vtbl4_p8(D4, Dm) ( __neon_DdDx4Dm( 0xf3b00b00, (D4), (Dm)) ) +#define vtbl4_s8(D4, Dm) ( __neon_DdDx4Dm( 0xf3b00b00, (D4), (Dm)) ) +#define vtbl4_u8(D4, Dm) ( __neon_DdDx4Dm( 0xf3b00b00, (D4), (Dm)) ) +#define vtbx4_p8(Dd, D4, Dm) ( __neon_DdDx4Dm_acc( 0xf3b00b40, (Dd), (D4), (Dm)) ) +#define vtbx4_s8(Dd, D4, Dm) ( __neon_DdDx4Dm_acc( 0xf3b00b40, (Dd), (D4), (Dm)) ) +#define vtbx4_u8(Dd, D4, Dm) ( __neon_DdDx4Dm_acc( 0xf3b00b40, (Dd), (D4), (Dm)) ) +#define vtbl1_p8(Dn, Dm) ( __neon_DdDnDm( 0xf3b00800, (Dn), (Dm)) ) +#define vtbl1_s8(Dn, Dm) ( __neon_DdDnDm( 0xf3b00800, (Dn), (Dm)) ) +#define vtbl1_u8(Dn, Dm) ( __neon_DdDnDm( 0xf3b00800, (Dn), (Dm)) ) +#define vtbx1_p8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3b00840, (Dd), (Dn), (Dm)) ) +#define vtbx1_s8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3b00840, (Dd), (Dn), (Dm)) ) +#define vtbx1_u8(Dd, Dn, Dm) ( __neon_DdDnDm_acc( 0xf3b00840, (Dd), (Dn), (Dm)) ) + +// VTRN +#define vtrn_f32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vtrn_p16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60080, (Dd), (Dm)) ) +#define vtrn_p8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20080, (Dd), (Dm)) ) +#define vtrn_s16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60080, (Dd), (Dm)) ) +#define vtrn_s32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vtrn_s8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20080, (Dd), (Dm)) ) +#define vtrn_u16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60080, (Dd), (Dm)) ) +#define vtrn_u32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vtrn_u8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20080, (Dd), (Dm)) ) +#define vtrnq_f32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba00c0, (Qd), (Qm)) ) +#define vtrnq_p16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b600c0, (Qd), (Qm)) ) +#define vtrnq_p8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b200c0, (Qd), (Qm)) ) +#define vtrnq_s16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b600c0, (Qd), (Qm)) ) +#define vtrnq_s32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba00c0, (Qd), (Qm)) ) +#define vtrnq_s8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b200c0, (Qd), (Qm)) ) +#define vtrnq_u16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b600c0, (Qd), (Qm)) ) +#define vtrnq_u32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba00c0, (Qd), (Qm)) ) +#define vtrnq_u8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b200c0, (Qd), (Qm)) ) + +// VTRNQ64 +#define vtrnq_s64(Qd, Qm) ( __neon_QdQm_acc3( 0x00000000, (Qd), (Qm)) ) +#define vtrnq_u64(Qd, Qm) ( __neon_QdQm_acc3( 0x00000000, (Qd), (Qm)) ) + +// VTST +#define vtst_p8(Dn, Dm) ( __neon_DdDnDm( 0xf2000810, (Dn), (Dm)) ) +#define vtst_s16(Dn, Dm) ( __neon_DdDnDm( 0xf2100810, (Dn), (Dm)) ) +#define vtst_s32(Dn, Dm) ( __neon_DdDnDm( 0xf2200810, (Dn), (Dm)) ) +#define vtst_s8(Dn, Dm) ( __neon_DdDnDm( 0xf2000810, (Dn), (Dm)) ) +#define vtst_u16(Dn, Dm) ( __neon_DdDnDm( 0xf2100810, (Dn), (Dm)) ) +#define vtst_u32(Dn, Dm) ( __neon_DdDnDm( 0xf2200810, (Dn), (Dm)) ) +#define vtst_u8(Dn, Dm) ( __neon_DdDnDm( 0xf2000810, (Dn), (Dm)) ) +#define vtstq_p8(Qn, Qm) ( __neon_QdQnQm( 0xf2000850, (Qn), (Qm)) ) +#define vtstq_s16(Qn, Qm) ( __neon_QdQnQm( 0xf2100850, (Qn), (Qm)) ) +#define vtstq_s32(Qn, Qm) ( __neon_QdQnQm( 0xf2200850, (Qn), (Qm)) ) +#define vtstq_s8(Qn, Qm) ( __neon_QdQnQm( 0xf2000850, (Qn), (Qm)) ) +#define vtstq_u16(Qn, Qm) ( __neon_QdQnQm( 0xf2100850, (Qn), (Qm)) ) +#define vtstq_u32(Qn, Qm) ( __neon_QdQnQm( 0xf2200850, (Qn), (Qm)) ) +#define vtstq_u8(Qn, Qm) ( __neon_QdQnQm( 0xf2000850, (Qn), (Qm)) ) + +// VUZP +#define vuzp_p16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60100, (Dd), (Dm)) ) +#define vuzp_p8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20100, (Dd), (Dm)) ) +#define vuzp_s16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60100, (Dd), (Dm)) ) +#define vuzp_s8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20100, (Dd), (Dm)) ) +#define vuzp_u16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60100, (Dd), (Dm)) ) +#define vuzp_u8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20100, (Dd), (Dm)) ) +#define vuzp_f32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vuzp_s32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vuzp_u32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vuzpq_f32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba0140, (Qd), (Qm)) ) +#define vuzpq_p16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b60140, (Qd), (Qm)) ) +#define vuzpq_p8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b20140, (Qd), (Qm)) ) +#define vuzpq_s16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b60140, (Qd), (Qm)) ) +#define vuzpq_s32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba0140, (Qd), (Qm)) ) +#define vuzpq_s8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b20140, (Qd), (Qm)) ) +#define vuzpq_u16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b60140, (Qd), (Qm)) ) +#define vuzpq_u32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba0140, (Qd), (Qm)) ) +#define vuzpq_u8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b20140, (Qd), (Qm)) ) + +// VZIP +#define vzip_p16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60180, (Dd), (Dm)) ) +#define vzip_p8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20180, (Dd), (Dm)) ) +#define vzip_s16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60180, (Dd), (Dm)) ) +#define vzip_s8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20180, (Dd), (Dm)) ) +#define vzip_u16(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b60180, (Dd), (Dm)) ) +#define vzip_u8(Dd, Dm) ( __neon_DdDm_acc2( 0xf3b20180, (Dd), (Dm)) ) +#define vzip_f32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vzip_s32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vzip_u32(Dd, Dm) ( __neon_DdDm_acc2( 0xf3ba0080, (Dd), (Dm)) ) +#define vzipq_f32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba01c0, (Qd), (Qm)) ) +#define vzipq_p16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b601c0, (Qd), (Qm)) ) +#define vzipq_p8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b201c0, (Qd), (Qm)) ) +#define vzipq_s16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b601c0, (Qd), (Qm)) ) +#define vzipq_s32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba01c0, (Qd), (Qm)) ) +#define vzipq_s8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b201c0, (Qd), (Qm)) ) +#define vzipq_u16(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b601c0, (Qd), (Qm)) ) +#define vzipq_u32(Qd, Qm) ( __neon_QdQm_acc2( 0xf3ba01c0, (Qd), (Qm)) ) +#define vzipq_u8(Qd, Qm) ( __neon_QdQm_acc2( 0xf3b201c0, (Qd), (Qm)) ) + +// } +++ auto-generated code ends (Neon macros) + + + +/////////////////////////////////////////////////////////////////////////////// +// +// { +++ auto-generated code begins (vreinterpret macros) + +#define vreinterpret_f32_s8(a) (a) +#define vreinterpret_f32_s16(a) (a) +#define vreinterpret_f32_s32(a) (a) +#define vreinterpret_f32_s64(a) (a) +#define vreinterpret_f32_p8(a) (a) +#define vreinterpret_f32_p16(a) (a) +#define vreinterpret_f32_u8(a) (a) +#define vreinterpret_f32_u16(a) (a) +#define vreinterpret_f32_u32(a) (a) +#define vreinterpret_f32_u64(a) (a) +#define vreinterpret_s8_f32(a) (a) +#define vreinterpret_s8_s16(a) (a) +#define vreinterpret_s8_s32(a) (a) +#define vreinterpret_s8_s64(a) (a) +#define vreinterpret_s8_p8(a) (a) +#define vreinterpret_s8_p16(a) (a) +#define vreinterpret_s8_u8(a) (a) +#define vreinterpret_s8_u16(a) (a) +#define vreinterpret_s8_u32(a) (a) +#define vreinterpret_s8_u64(a) (a) +#define vreinterpret_s16_f32(a) (a) +#define vreinterpret_s16_s8(a) (a) +#define vreinterpret_s16_s32(a) (a) +#define vreinterpret_s16_s64(a) (a) +#define vreinterpret_s16_p8(a) (a) +#define vreinterpret_s16_p16(a) (a) +#define vreinterpret_s16_u8(a) (a) +#define vreinterpret_s16_u16(a) (a) +#define vreinterpret_s16_u32(a) (a) +#define vreinterpret_s16_u64(a) (a) +#define vreinterpret_s32_f32(a) (a) +#define vreinterpret_s32_s8(a) (a) +#define vreinterpret_s32_s16(a) (a) +#define vreinterpret_s32_s64(a) (a) +#define vreinterpret_s32_p8(a) (a) +#define vreinterpret_s32_p16(a) (a) +#define vreinterpret_s32_u8(a) (a) +#define vreinterpret_s32_u16(a) (a) +#define vreinterpret_s32_u32(a) (a) +#define vreinterpret_s32_u64(a) (a) +#define vreinterpret_s64_f32(a) (a) +#define vreinterpret_s64_s8(a) (a) +#define vreinterpret_s64_s16(a) (a) +#define vreinterpret_s64_s32(a) (a) +#define vreinterpret_s64_p8(a) (a) +#define vreinterpret_s64_p16(a) (a) +#define vreinterpret_s64_u8(a) (a) +#define vreinterpret_s64_u16(a) (a) +#define vreinterpret_s64_u32(a) (a) +#define vreinterpret_s64_u64(a) (a) +#define vreinterpret_p8_f32(a) (a) +#define vreinterpret_p8_s8(a) (a) +#define vreinterpret_p8_s16(a) (a) +#define vreinterpret_p8_s32(a) (a) +#define vreinterpret_p8_s64(a) (a) +#define vreinterpret_p8_p16(a) (a) +#define vreinterpret_p8_u8(a) (a) +#define vreinterpret_p8_u16(a) (a) +#define vreinterpret_p8_u32(a) (a) +#define vreinterpret_p8_u64(a) (a) +#define vreinterpret_p16_f32(a) (a) +#define vreinterpret_p16_s8(a) (a) +#define vreinterpret_p16_s16(a) (a) +#define vreinterpret_p16_s32(a) (a) +#define vreinterpret_p16_s64(a) (a) +#define vreinterpret_p16_p8(a) (a) +#define vreinterpret_p16_u8(a) (a) +#define vreinterpret_p16_u16(a) (a) +#define vreinterpret_p16_u32(a) (a) +#define vreinterpret_p16_u64(a) (a) +#define vreinterpret_u8_f32(a) (a) +#define vreinterpret_u8_s8(a) (a) +#define vreinterpret_u8_s16(a) (a) +#define vreinterpret_u8_s32(a) (a) +#define vreinterpret_u8_s64(a) (a) +#define vreinterpret_u8_p8(a) (a) +#define vreinterpret_u8_p16(a) (a) +#define vreinterpret_u8_u16(a) (a) +#define vreinterpret_u8_u32(a) (a) +#define vreinterpret_u8_u64(a) (a) +#define vreinterpret_u16_f32(a) (a) +#define vreinterpret_u16_s8(a) (a) +#define vreinterpret_u16_s16(a) (a) +#define vreinterpret_u16_s32(a) (a) +#define vreinterpret_u16_s64(a) (a) +#define vreinterpret_u16_p8(a) (a) +#define vreinterpret_u16_p16(a) (a) +#define vreinterpret_u16_u8(a) (a) +#define vreinterpret_u16_u32(a) (a) +#define vreinterpret_u16_u64(a) (a) +#define vreinterpret_u32_f32(a) (a) +#define vreinterpret_u32_s8(a) (a) +#define vreinterpret_u32_s16(a) (a) +#define vreinterpret_u32_s32(a) (a) +#define vreinterpret_u32_s64(a) (a) +#define vreinterpret_u32_p8(a) (a) +#define vreinterpret_u32_p16(a) (a) +#define vreinterpret_u32_u8(a) (a) +#define vreinterpret_u32_u16(a) (a) +#define vreinterpret_u32_u64(a) (a) +#define vreinterpret_u64_f32(a) (a) +#define vreinterpret_u64_s8(a) (a) +#define vreinterpret_u64_s16(a) (a) +#define vreinterpret_u64_s32(a) (a) +#define vreinterpret_u64_s64(a) (a) +#define vreinterpret_u64_p8(a) (a) +#define vreinterpret_u64_p16(a) (a) +#define vreinterpret_u64_u8(a) (a) +#define vreinterpret_u64_u16(a) (a) +#define vreinterpret_u64_u32(a) (a) +#define vreinterpretq_f32_s8(a) (a) +#define vreinterpretq_f32_s16(a) (a) +#define vreinterpretq_f32_s32(a) (a) +#define vreinterpretq_f32_s64(a) (a) +#define vreinterpretq_f32_p8(a) (a) +#define vreinterpretq_f32_p16(a) (a) +#define vreinterpretq_f32_u8(a) (a) +#define vreinterpretq_f32_u16(a) (a) +#define vreinterpretq_f32_u32(a) (a) +#define vreinterpretq_f32_u64(a) (a) +#define vreinterpretq_s8_f32(a) (a) +#define vreinterpretq_s8_s16(a) (a) +#define vreinterpretq_s8_s32(a) (a) +#define vreinterpretq_s8_s64(a) (a) +#define vreinterpretq_s8_p8(a) (a) +#define vreinterpretq_s8_p16(a) (a) +#define vreinterpretq_s8_u8(a) (a) +#define vreinterpretq_s8_u16(a) (a) +#define vreinterpretq_s8_u32(a) (a) +#define vreinterpretq_s8_u64(a) (a) +#define vreinterpretq_s16_f32(a) (a) +#define vreinterpretq_s16_s8(a) (a) +#define vreinterpretq_s16_s32(a) (a) +#define vreinterpretq_s16_s64(a) (a) +#define vreinterpretq_s16_p8(a) (a) +#define vreinterpretq_s16_p16(a) (a) +#define vreinterpretq_s16_u8(a) (a) +#define vreinterpretq_s16_u16(a) (a) +#define vreinterpretq_s16_u32(a) (a) +#define vreinterpretq_s16_u64(a) (a) +#define vreinterpretq_s32_f32(a) (a) +#define vreinterpretq_s32_s8(a) (a) +#define vreinterpretq_s32_s16(a) (a) +#define vreinterpretq_s32_s64(a) (a) +#define vreinterpretq_s32_p8(a) (a) +#define vreinterpretq_s32_p16(a) (a) +#define vreinterpretq_s32_u8(a) (a) +#define vreinterpretq_s32_u16(a) (a) +#define vreinterpretq_s32_u32(a) (a) +#define vreinterpretq_s32_u64(a) (a) +#define vreinterpretq_s64_f32(a) (a) +#define vreinterpretq_s64_s8(a) (a) +#define vreinterpretq_s64_s16(a) (a) +#define vreinterpretq_s64_s32(a) (a) +#define vreinterpretq_s64_p8(a) (a) +#define vreinterpretq_s64_p16(a) (a) +#define vreinterpretq_s64_u8(a) (a) +#define vreinterpretq_s64_u16(a) (a) +#define vreinterpretq_s64_u32(a) (a) +#define vreinterpretq_s64_u64(a) (a) +#define vreinterpretq_p8_f32(a) (a) +#define vreinterpretq_p8_s8(a) (a) +#define vreinterpretq_p8_s16(a) (a) +#define vreinterpretq_p8_s32(a) (a) +#define vreinterpretq_p8_s64(a) (a) +#define vreinterpretq_p8_p16(a) (a) +#define vreinterpretq_p8_u8(a) (a) +#define vreinterpretq_p8_u16(a) (a) +#define vreinterpretq_p8_u32(a) (a) +#define vreinterpretq_p8_u64(a) (a) +#define vreinterpretq_p16_f32(a) (a) +#define vreinterpretq_p16_s8(a) (a) +#define vreinterpretq_p16_s16(a) (a) +#define vreinterpretq_p16_s32(a) (a) +#define vreinterpretq_p16_s64(a) (a) +#define vreinterpretq_p16_p8(a) (a) +#define vreinterpretq_p16_u8(a) (a) +#define vreinterpretq_p16_u16(a) (a) +#define vreinterpretq_p16_u32(a) (a) +#define vreinterpretq_p16_u64(a) (a) +#define vreinterpretq_u8_f32(a) (a) +#define vreinterpretq_u8_s8(a) (a) +#define vreinterpretq_u8_s16(a) (a) +#define vreinterpretq_u8_s32(a) (a) +#define vreinterpretq_u8_s64(a) (a) +#define vreinterpretq_u8_p8(a) (a) +#define vreinterpretq_u8_p16(a) (a) +#define vreinterpretq_u8_u16(a) (a) +#define vreinterpretq_u8_u32(a) (a) +#define vreinterpretq_u8_u64(a) (a) +#define vreinterpretq_u16_f32(a) (a) +#define vreinterpretq_u16_s8(a) (a) +#define vreinterpretq_u16_s16(a) (a) +#define vreinterpretq_u16_s32(a) (a) +#define vreinterpretq_u16_s64(a) (a) +#define vreinterpretq_u16_p8(a) (a) +#define vreinterpretq_u16_p16(a) (a) +#define vreinterpretq_u16_u8(a) (a) +#define vreinterpretq_u16_u32(a) (a) +#define vreinterpretq_u16_u64(a) (a) +#define vreinterpretq_u32_f32(a) (a) +#define vreinterpretq_u32_s8(a) (a) +#define vreinterpretq_u32_s16(a) (a) +#define vreinterpretq_u32_s32(a) (a) +#define vreinterpretq_u32_s64(a) (a) +#define vreinterpretq_u32_p8(a) (a) +#define vreinterpretq_u32_p16(a) (a) +#define vreinterpretq_u32_u8(a) (a) +#define vreinterpretq_u32_u16(a) (a) +#define vreinterpretq_u32_u64(a) (a) +#define vreinterpretq_u64_f32(a) (a) +#define vreinterpretq_u64_s8(a) (a) +#define vreinterpretq_u64_s16(a) (a) +#define vreinterpretq_u64_s32(a) (a) +#define vreinterpretq_u64_s64(a) (a) +#define vreinterpretq_u64_p8(a) (a) +#define vreinterpretq_u64_p16(a) (a) +#define vreinterpretq_u64_u8(a) (a) +#define vreinterpretq_u64_u16(a) (a) +#define vreinterpretq_u64_u32(a) (a) + +// } +++ auto-generated code ends (vreinterpret macros) + +// { +++ auto-generated code begins (Pseudo intrinsics) + +// Multiply by scalar +#define vmul_n_s16(Vd, Rt) vmul_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vmul_n_s32(Vd, Rt) vmul_lane_s32((Vd), vmov_n_s32(Rt), 0) +#define vmul_n_u16(Vd, Rt) vmul_lane_u16((Vd), vmov_n_u16(Rt), 0) +#define vmul_n_u32(Vd, Rt) vmul_lane_u32((Vd), vmov_n_u32(Rt), 0) +#define vmulq_n_s16(Vd, Rt) vmulq_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vmulq_n_s32(Vd, Rt) vmulq_lane_s32((Vd), vmov_n_s32(Rt), 0) +#define vmulq_n_u16(Vd, Rt) vmulq_lane_u16((Vd), vmov_n_u16(Rt), 0) +#define vmulq_n_u32(Vd, Rt) vmulq_lane_u32((Vd), vmov_n_u32(Rt), 0) +#define vmull_n_s16(Vd, Rt) vmull_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vmull_n_s32(Vd, Rt) vmull_lane_s32((Vd), vmov_n_s32(Rt), 0) +#define vmull_n_u16(Vd, Rt) vmull_lane_u16((Vd), vmov_n_u16(Rt), 0) +#define vmull_n_u32(Vd, Rt) vmull_lane_u32((Vd), vmov_n_u32(Rt), 0) +#define vqdmulh_n_s16(Vd, Rt) vqdmulh_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vqdmulh_n_s32(Vd, Rt) vqdmulh_lane_s32((Vd), vmov_n_s32(Rt), 0) +#define vqdmulhq_n_s16(Vd, Rt) vqdmulhq_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vqdmulhq_n_s32(Vd, Rt) vqdmulhq_lane_s32((Vd), vmov_n_s32(Rt), 0) +#define vqdmull_n_s16(Vd, Rt) vqdmull_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vqdmull_n_s32(Vd, Rt) vqdmull_lane_s32((Vd), vmov_n_s32(Rt), 0) +#define vqrdmulh_n_s16(Vd, Rt) vqrdmulh_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vqrdmulh_n_s32(Vd, Rt) vqrdmulh_lane_s32((Vd), vmov_n_s32(Rt), 0) +#define vqrdmulhq_n_s16(Vd, Rt) vqrdmulhq_lane_s16((Vd), vmov_n_s16(Rt), 0) +#define vqrdmulhq_n_s32(Vd, Rt) vqrdmulhq_lane_s32((Vd), vmov_n_s32(Rt), 0) + +// Multiply by scalar with accumulate +#define vmla_n_s16(Vd, Vn, Rt) vmla_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vmla_n_s32(Vd, Vn, Rt) vmla_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) +#define vmla_n_u16(Vd, Vn, Rt) vmla_lane_u16((Vd), (Vn), vmov_n_u16(Rt), 0) +#define vmla_n_u32(Vd, Vn, Rt) vmla_lane_u32((Vd), (Vn), vmov_n_u32(Rt), 0) +#define vmlaq_n_s16(Vd, Vn, Rt) vmlaq_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vmlaq_n_s32(Vd, Vn, Rt) vmlaq_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) +#define vmlaq_n_u16(Vd, Vn, Rt) vmlaq_lane_u16((Vd), (Vn), vmov_n_u16(Rt), 0) +#define vmlaq_n_u32(Vd, Vn, Rt) vmlaq_lane_u32((Vd), (Vn), vmov_n_u32(Rt), 0) +#define vmlal_n_s16(Vd, Vn, Rt) vmlal_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vmlal_n_s32(Vd, Vn, Rt) vmlal_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) +#define vmlal_n_u16(Vd, Vn, Rt) vmlal_lane_u16((Vd), (Vn), vmov_n_u16(Rt), 0) +#define vmlal_n_u32(Vd, Vn, Rt) vmlal_lane_u32((Vd), (Vn), vmov_n_u32(Rt), 0) +#define vmls_n_s16(Vd, Vn, Rt) vmls_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vmls_n_s32(Vd, Vn, Rt) vmls_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) +#define vmls_n_u16(Vd, Vn, Rt) vmls_lane_u16((Vd), (Vn), vmov_n_u16(Rt), 0) +#define vmls_n_u32(Vd, Vn, Rt) vmls_lane_u32((Vd), (Vn), vmov_n_u32(Rt), 0) +#define vmlsq_n_s16(Vd, Vn, Rt) vmlsq_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vmlsq_n_s32(Vd, Vn, Rt) vmlsq_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) +#define vmlsq_n_u16(Vd, Vn, Rt) vmlsq_lane_u16((Vd), (Vn), vmov_n_u16(Rt), 0) +#define vmlsq_n_u32(Vd, Vn, Rt) vmlsq_lane_u32((Vd), (Vn), vmov_n_u32(Rt), 0) +#define vmlsl_n_s16(Vd, Vn, Rt) vmlsl_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vmlsl_n_s32(Vd, Vn, Rt) vmlsl_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) +#define vmlsl_n_u16(Vd, Vn, Rt) vmlsl_lane_u16((Vd), (Vn), vmov_n_u16(Rt), 0) +#define vmlsl_n_u32(Vd, Vn, Rt) vmlsl_lane_u32((Vd), (Vn), vmov_n_u32(Rt), 0) +#define vqdmlal_n_s16(Vd, Vn, Rt) vqdmlal_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vqdmlal_n_s32(Vd, Vn, Rt) vqdmlal_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) +#define vqdmlsl_n_s16(Vd, Vn, Rt) vqdmlsl_lane_s16((Vd), (Vn), vmov_n_s16(Rt), 0) +#define vqdmlsl_n_s32(Vd, Vn, Rt) vqdmlsl_lane_s32((Vd), (Vn), vmov_n_s32(Rt), 0) + +// VDUP.64 (scalar) +#define vdup_lane_s64(Dn, lane) ( __static_assert(lane == 0, "invalid lane index"), (Dn) ) +#define vdup_lane_u64(Dn, lane) ( __static_assert(lane == 0, "invalid lane index"), (Dn) ) + +// VDUP.W.64 (scalar) +#define vdupq_lane_s64(Dn, lane) ( __static_assert(lane == 0, "invalid lane index"), vcombine_s64((Dn), (Dn)) ) +#define vdupq_lane_u64(Dn, lane) ( __static_assert(lane == 0, "invalid lane index"), vcombine_u64((Dn), (Dn)) ) + +// } +++ auto-generated code ends (Pseudo intrinsics) diff --git a/test_data/lots_of_files/armintr.h b/test_data/lots_of_files/armintr.h new file mode 100644 index 0000000..592c529 --- /dev/null +++ b/test_data/lots_of_files/armintr.h @@ -0,0 +1,598 @@ +/*** +* armintr.h - definitions and declarations for ARM specific intrinsics +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains constant definitions and external subroutine +* declarations for the ARM specific intriniscs +* +****/ + +#pragma once + + +#if !defined (_M_ARM) +#error This header is specific to ARM targets +#endif /* !defined (_M_ARM) */ + + +#if defined (__cplusplus) +extern "C" { +#endif /* defined (__cplusplus) */ + +typedef enum _tag_ARMINTR_SHIFT_T +{ + _ARM_LSR = 0, + _ARM_LSL = 1, + _ARM_ASR = 2, + _ARM_ROR = 3 +} +_ARMINTR_SHIFT_T; + + int __arm_gen_s_Rd_Rd (const unsigned int, int); + int __arm_gen_s_Rd_RdRn (const unsigned int, int, int); + int __arm_gen_s_Rd_RdRm (const unsigned int, int, int); + int __arm_gen_s_Rd_RnRm_g (const unsigned int, int, int); + int __arm_gen_s_Rd_RnRm_q (const unsigned int, int, int); + int __arm_gen_s_Rd_Rn (const unsigned int, int); + int __arm_gen_s_Rd_Rn_q (const unsigned int, int); + int __arm_gen_s_Rd_RnRm (const unsigned int, int, int); + int __arm_gen_s_Rd_Rm (const unsigned int, int); + int __arm_gen_s_Rd_Rm_q (const unsigned int, int); + int __arm_gen_s_Rd_Rm2 (const unsigned int, int); + __int64 __arm_gen_s_Rdn_RdnRmRs(const unsigned int, __int64, int, int); + __int64 __arm_gen_s_Rdn_RmRs (const unsigned int, int, int); + int __arm_gen_s_Rn_RmRs (const unsigned int, int, int); + int __arm_gen_s_Rn_RmRsRd (const unsigned int, int, int, int); +unsigned int __arm_gen_u_Rd_Rd (const unsigned int, unsigned int); +unsigned int __arm_gen_u_Rd_RdRn (const unsigned int, unsigned int, unsigned int); +unsigned int __arm_gen_u_Rd_RdRm (const unsigned int, unsigned int, unsigned int); +unsigned int __arm_gen_u_Rd_RnRm_g (const unsigned int, unsigned int, unsigned int); +unsigned int __arm_gen_u_Rd_Rn (const unsigned int, unsigned int); +unsigned int __arm_gen_u_Rd_RnRm (const unsigned int, unsigned int, unsigned int); +unsigned int __arm_gen_u_Rd_Rm (const unsigned int, unsigned int); +unsigned int __arm_gen_u_Rd_Rm2 (const unsigned int, unsigned int); +unsigned __int64 __arm_gen_u_Rdn_RdnRmRs(const unsigned int, unsigned __int64, unsigned int, unsigned int); +unsigned __int64 __arm_gen_u_Rdn_RmRs (const unsigned int, unsigned int, unsigned int); +unsigned int __arm_gen_u_Rn_RmRs (const unsigned int, unsigned int, unsigned int); +unsigned int __arm_gen_u_Rn_RmRsRd (const unsigned int, unsigned int, unsigned int, unsigned int); + +#define _ARMINTR_ASSERT(exp, msg) __static_assert(exp, msg) + +#define _ARMINTR_IN_RANGE(val, low, high) (((val) >= low) && ((val) <= high)) + +#define _ARMINTR_ASSERT_RANGE(val, low, high, name) \ + (_ARMINTR_ASSERT(_ARMINTR_IN_RANGE((val), (low), (high)), name " is out of range '" #low " - " #high "'")) +#define _ARMINTR_ASSERT_ROT(rot) \ + (_ARMINTR_ASSERT(((rot) & ~0x18U) == 0, "rotation must be 0, 8, 16, or 24")) +#define _ARMINTR_ASSERT_SAT_BIT(sat_bit, low, high) \ + (_ARMINTR_ASSERT_RANGE((sat_bit), low, high, "saturation bit position")) // low, high intentionally not in (parentheses) + +#if defined (_M_THUMB) +#define _ARMINTR_ASSERT_SAT_SHIFT(type, amt) \ + (_ARMINTR_ASSERT(((type) == _ARM_LSL) || ((type) == _ARM_ASR), "shift type must be _ARM_LSL or _ARM_ASR"), \ + _ARMINTR_ASSERT(((type) != _ARM_LSL) || _ARMINTR_IN_RANGE((amt), 0, 31), "shift is out of range '0 - 31'"), \ + _ARMINTR_ASSERT(((type) != _ARM_ASR) || _ARMINTR_IN_RANGE((amt), 1, 31), "shift is out of range '1 - 31'")) + +#else /* defined (_M_THUMB) */ +#define _ARMINTR_ASSERT_SAT_SHIFT(type, amt) \ + (_ARMINTR_ASSERT(((type) == _ARM_LSL) || ((type) == _ARM_ASR), "shift type must be _ARM_LSL or _ARM_ASR"), \ + _ARMINTR_ASSERT(((type) != _ARM_LSL) || _ARMINTR_IN_RANGE((amt), 0, 31), "shift is out of range '0 - 31'"), \ + _ARMINTR_ASSERT(((type) != _ARM_ASR) || _ARMINTR_IN_RANGE((amt), 1, 32), "shift is out of range '1 - 32'")) +#endif /* defined (_M_THUMB) */ + +#if defined (_M_THUMB) + +#define _ARMINTR_ENCODE_XTROT(rot) (((rot) & 0x18U) << 17) +#define _ARMINTR_ENCODE_PKHSHIFT(shift) ((((shift) & 0x1CU) << 26) | (((shift) & 0x3U) << 22)) +#define _ARMINTR_ENCODE_IMM5_16(imm) (((imm) & 0x1FU) << 16) +#define _ARMINTR_ENCODE_IMM5_7(imm) ((((imm) & 0x1CU) << 26) | (((imm) & 0x3U) << 22)) +#define _ARMINTR_ENCODE_SAT_SH(type, amt) ((((type) & 2U) << 4) | _ARMINTR_ENCODE_IMM5_7(amt)) +#define _ARMINTR_ENCODE_IMM4_16(bit) (((bit) & 0xFU) << 16) + +#define _ARMINTR_BFC 0x0000F36FU +#define _ARMINTR_BFI 0x0000F360U +#define _ARMINTR_SBFX 0x0000F340U +#define _ARMINTR_UBFX 0x0000F3C0U +#define _ARMINTR_QADD 0xF080FA80U +#define _ARMINTR_QDADD 0xF090FA80U +#define _ARMINTR_QDSUB 0xF0B0FA80U +#define _ARMINTR_QSUB 0xF0A0FA80U +#define _ARMINTR_QADD16 0xF010FA90U +#define _ARMINTR_QADD8 0xF010FA80U +#define _ARMINTR_QASX 0xF010FAA0U +#define _ARMINTR_QSAX 0xF010FAE0U +#define _ARMINTR_QSUB16 0xF010FAD0U +#define _ARMINTR_QSUB8 0xF010FAC0U +#define _ARMINTR_SHADD16 0xF020FA90U +#define _ARMINTR_SHADD8 0xF020FA80U +#define _ARMINTR_SHASX 0xF020FAA0U +#define _ARMINTR_SHSAX 0xF020FAE0U +#define _ARMINTR_SHSUB16 0xF020FAD0U +#define _ARMINTR_SHSUB8 0xF020FAC0U +#define _ARMINTR_UHADD16 0xF060FA90U +#define _ARMINTR_UHADD8 0xF060FA80U +#define _ARMINTR_UHASX 0xF060FAA0U +#define _ARMINTR_UHSAX 0xF060FAE0U +#define _ARMINTR_UHSUB16 0xF060FAD0U +#define _ARMINTR_UHSUB8 0xF060FAC0U +#define _ARMINTR_UQADD16 0xF050FA90U +#define _ARMINTR_UQADD8 0xF050FA80U +#define _ARMINTR_UQASX 0xF050FAA0U +#define _ARMINTR_UQSAX 0xF050FAE0U +#define _ARMINTR_UQSUB16 0xF050FAD0U +#define _ARMINTR_UQSUB8 0xF050FAC0U +#define _ARMINTR_SXTAB 0xF080FA40U +#define _ARMINTR_SXTAB16 0xF080FA20U +#define _ARMINTR_SXTAH 0xF080FA00U +#define _ARMINTR_UXTAB 0xF080FA50U +#define _ARMINTR_UXTAB16 0xF080FA30U +#define _ARMINTR_UXTAH 0xF080FA10U +#define _ARMINTR_SXTB 0xF080FA4FU +#define _ARMINTR_SXTB16 0xF080FA2FU +#define _ARMINTR_SXTH 0xF080FA0FU +#define _ARMINTR_UXTB 0xF080FA5FU +#define _ARMINTR_UXTB16 0xF080FA3FU +#define _ARMINTR_UXTBH 0xF080FA1FU +#define _ARMINTR_PKHBT 0x0000EAC0U +#define _ARMINTR_PKHTB 0x0020EAC0U +#define _ARMINTR_USAD8 0xF000FB70U +#define _ARMINTR_USADA8 0x0000FB70U +#define _ARMINTR_SADD16 0xF000FA90U +#define _ARMINTR_SADD8 0xF000FA80U +#define _ARMINTR_SASX 0xF000FAA0U +#define _ARMINTR_SSAX 0xF000FAE0U +#define _ARMINTR_SSUB16 0xF000FAD0U +#define _ARMINTR_SSUB8 0xF000FAC0U +#define _ARMINTR_UADD16 0xF040FA90U +#define _ARMINTR_UADD8 0xF040FA80U +#define _ARMINTR_UASX 0xF040FAA0U +#define _ARMINTR_USAX 0xF040FAE0U +#define _ARMINTR_USUB16 0xF040FAD0U +#define _ARMINTR_USUB8 0xF040FAC0U +#define _ARMINTR_SSAT 0x0000F300U +#define _ARMINTR_USAT 0x0000F380U +#define _ARMINTR_SSAT16 0x0000F320U +#define _ARMINTR_USAT16 0x0000F3A0U +#define _ARMINTR_CLZ 0xF080FAB0U +#define _ARMINTR_RBIT 0xF0A0FA90U +#define _ARMINTR_REV 0xF080FA90U +#define _ARMINTR_REV16 0xF090FA90U +#define _ARMINTR_REVSH 0xF0B0FA90U +#define _ARMINTR_SMLAD 0x0000FB20U +#define _ARMINTR_SMLADX 0x0010FB20U +#define _ARMINTR_SMLSD 0x0000FB40U +#define _ARMINTR_SMLSDX 0x0010FB40U +#define _ARMINTR_SMMLA 0x0000FB50U +#define _ARMINTR_SMMLAR 0x0010FB50U +#define _ARMINTR_SMMLS 0x0000FB60U +#define _ARMINTR_SMMLSR 0x0010FB60U +#define _ARMINTR_SMLABB 0x0000FB10U +#define _ARMINTR_SMLABT 0x0010FB10U +#define _ARMINTR_SMLATB 0x0020FB10U +#define _ARMINTR_SMLATT 0x0030FB10U +#define _ARMINTR_SMLAWB 0x0000FB30U +#define _ARMINTR_SMLAWT 0x0010FB30U +#define _ARMINTR_SMULWB 0xF000FB30U +#define _ARMINTR_SMULWT 0xF010FB30U +#define _ARMINTR_SMULBB 0xF000FB10U +#define _ARMINTR_SMULBT 0xF010FB10U +#define _ARMINTR_SMULTB 0xF020FB10U +#define _ARMINTR_SMULTT 0xF030FB10U +#define _ARMINTR_SMULL 0x0000FB80U +#define _ARMINTR_SMMUL 0xF000FB50U +#define _ARMINTR_SMMULR 0xF010FB50U +#define _ARMINTR_SMUAD 0xF000FB20U +#define _ARMINTR_SMUADX 0xF010FB20U +#define _ARMINTR_SMUSD 0xF000FB40U +#define _ARMINTR_SMUSDX 0xF010FB40U +#define _ARMINTR_SMLALBB 0x0080FBC0U +#define _ARMINTR_SMLALBT 0x0090FBC0U +#define _ARMINTR_SMLALTB 0x00A0FBC0U +#define _ARMINTR_SMLALTT 0x00B0FBC0U +#define _ARMINTR_SMLALD 0x00C0FBC0U +#define _ARMINTR_SMLALDX 0x00D0FBC0U +#define _ARMINTR_SMLSLD 0x00C0FBD0U +#define _ARMINTR_SMLSLDX 0x00D0FBD0U +#define _ARMINTR_SMLAL 0x0000FBC0U +#define _ARMINTR_UMLAL 0x0000FBE0U +#define _ARMINTR_UMAAL 0x0060FBE0U +#define _ARMINTR_UMULL 0x0000FBA0U +#define _ARMINTR_SDIV 0xF0F0FB90U +#define _ARMINTR_UDIV 0xF0F0FBB0U + +#define _ARMINTR_U_DDMx __arm_gen_u_Rd_RdRn +#define _ARMINTR_U_DMx __arm_gen_u_Rd_Rn +#define _ARMINTR_S_DMx __arm_gen_s_Rd_Rn + +#else /* defined (_M_THUMB) */ + +#define _ARMINTR_ENCODE_XTROT(rot) (((rot) & 0x18U) << 7) +#define _ARMINTR_ENCODE_PKHSHIFT(shift) (((shift) & 0x1FU) << 7) +#define _ARMINTR_ENCODE_IMM5_16(imm) (((imm) & 0x1FU) << 16) +#define _ARMINTR_ENCODE_IMM5_7(imm) (((imm) & 0x1FU) << 7) +#define _ARMINTR_ENCODE_SAT_SH(type, amt) ((((type) & 2U) << 5) | _ARMINTR_ENCODE_IMM5_7(amt)) +#define _ARMINTR_ENCODE_IMM4_16(bit) (((bit) & 0xFU) << 16) + +#define _ARMINTR_BFC 0x07C0001FU +#define _ARMINTR_BFI 0x07C00010U +#define _ARMINTR_SBFX 0x07A00050U +#define _ARMINTR_UBFX 0x07E00050U +#define _ARMINTR_QADD 0x01000050U +#define _ARMINTR_QDADD 0x01400050U +#define _ARMINTR_QDSUB 0x01600050U +#define _ARMINTR_QSUB 0x01200050U +#define _ARMINTR_QADD16 0x06200F10U +#define _ARMINTR_QADD8 0x06200F90U +#define _ARMINTR_QASX 0x06200F30U +#define _ARMINTR_QSAX 0x06200F50U +#define _ARMINTR_QSUB16 0x06200F70U +#define _ARMINTR_QSUB8 0x06200FF0U +#define _ARMINTR_SHADD16 0x06300F10U +#define _ARMINTR_SHADD8 0x06300F90U +#define _ARMINTR_SHASX 0x06300F30U +#define _ARMINTR_SHSAX 0x06300F50U +#define _ARMINTR_SHSUB16 0x06300F70U +#define _ARMINTR_SHSUB8 0x06300FF0U +#define _ARMINTR_UHADD16 0x06700F10U +#define _ARMINTR_UHADD8 0x06700F90U +#define _ARMINTR_UHASX 0x06700F30U +#define _ARMINTR_UHSAX 0x06700F50U +#define _ARMINTR_UHSUB16 0x06700F70U +#define _ARMINTR_UHSUB8 0x06700FF0U +#define _ARMINTR_UQADD16 0x06600F10U +#define _ARMINTR_UQADD8 0x06600F90U +#define _ARMINTR_UQASX 0x06600F30U +#define _ARMINTR_UQSAX 0x06600F50U +#define _ARMINTR_UQSUB16 0x06600F70U +#define _ARMINTR_UQSUB8 0x06600FF0U +#define _ARMINTR_SXTAB 0x06A00070U +#define _ARMINTR_SXTAB16 0x06800070U +#define _ARMINTR_SXTAH 0x06B00070U +#define _ARMINTR_UXTAB 0x06E00070U +#define _ARMINTR_UXTAB16 0x06C00070U +#define _ARMINTR_UXTAH 0x06F00070U +#define _ARMINTR_SXTB 0x06AF0070U +#define _ARMINTR_SXTB16 0x068F0070U +#define _ARMINTR_SXTH 0x06BF0070U +#define _ARMINTR_UXTB 0x06EF0070U +#define _ARMINTR_UXTB16 0x06CF0070U +#define _ARMINTR_UXTBH 0x06FF0070U +#define _ARMINTR_PKHBT 0x06800010U +#define _ARMINTR_PKHTB 0x06800050U +#define _ARMINTR_USAD8 0x0780F010U +#define _ARMINTR_USADA8 0x07800010U +#define _ARMINTR_SADD16 0x06100F10U +#define _ARMINTR_SADD8 0x06100F90U +#define _ARMINTR_SASX 0x06100F30U +#define _ARMINTR_SSAX 0x06100F50U +#define _ARMINTR_SSUB16 0x06100F70U +#define _ARMINTR_SSUB8 0x06100FF0U +#define _ARMINTR_UADD16 0x06500F10U +#define _ARMINTR_UADD8 0x06500F90U +#define _ARMINTR_UASX 0x06500F30U +#define _ARMINTR_USAX 0x06500F50U +#define _ARMINTR_USUB16 0x06500F70U +#define _ARMINTR_USUB8 0x06500FF0U +#define _ARMINTR_SSAT 0x06A00010U +#define _ARMINTR_USAT 0x06E00010U +#define _ARMINTR_SSAT16 0x06A00F30U +#define _ARMINTR_USAT16 0x06E00F30U +#define _ARMINTR_CLZ 0x016F0F10U +#define _ARMINTR_RBIT 0x06FF0F30U +#define _ARMINTR_REV 0x06BF0F30U +#define _ARMINTR_REV16 0x06BF0FB0U +#define _ARMINTR_REVSH 0x06FF0FB0U +#define _ARMINTR_SMLAD 0x07000010U +#define _ARMINTR_SMLADX 0x07000030U +#define _ARMINTR_SMLSD 0x07000050U +#define _ARMINTR_SMLSDX 0x07000070U +#define _ARMINTR_SMMLA 0x07500010U +#define _ARMINTR_SMMLAR 0x07500030U +#define _ARMINTR_SMMLS 0x075000D0U +#define _ARMINTR_SMMLSR 0x075000F0U +#define _ARMINTR_SMLABB 0x01000080U +#define _ARMINTR_SMLABT 0x010000C0U +#define _ARMINTR_SMLATB 0x010000A0U +#define _ARMINTR_SMLATT 0x010000E0U +#define _ARMINTR_SMLAWB 0x01200080U +#define _ARMINTR_SMLAWT 0x012000C0U +#define _ARMINTR_SMULWB 0x012000A0U +#define _ARMINTR_SMULWT 0x012000E0U +#define _ARMINTR_SMULBB 0x01600080U +#define _ARMINTR_SMULBT 0x016000C0U +#define _ARMINTR_SMULTB 0x016000A0U +#define _ARMINTR_SMULTT 0x016000E0U +#define _ARMINTR_SMULL 0x00C00090U +#define _ARMINTR_SMMUL 0x0750F010U +#define _ARMINTR_SMMULR 0x0750F030U +#define _ARMINTR_SMUAD 0x0700F010U +#define _ARMINTR_SMUADX 0x0700F030U +#define _ARMINTR_SMUSD 0x0700F050U +#define _ARMINTR_SMUSDX 0x0700F070U +#define _ARMINTR_SMLALBB 0x01400080U +#define _ARMINTR_SMLALBT 0x014000C0U +#define _ARMINTR_SMLALTB 0x014000A0U +#define _ARMINTR_SMLALTT 0x014000E0U +#define _ARMINTR_SMLALD 0x07400010U +#define _ARMINTR_SMLALDX 0x07400030U +#define _ARMINTR_SMLSLD 0x07400050U +#define _ARMINTR_SMLSLDX 0x07400070U +#define _ARMINTR_SMLAL 0x00E00090U +#define _ARMINTR_UMLAL 0x00A00090U +#define _ARMINTR_UMAAL 0x00400090U +#define _ARMINTR_UMULL 0x00800090U + +#define _ARMINTR_U_DDMx __arm_gen_u_Rd_RdRm +#define _ARMINTR_U_DMx __arm_gen_u_Rd_Rm +#define _ARMINTR_S_DMx __arm_gen_s_Rd_Rm + +#endif /* defined (_M_THUMB) */ + + +/* ARMv4 */ + +#define _arm_smlal(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLAL, (_RdHiLo), (_Rn), (_Rm))) +#define _arm_umlal(_RdHiLo, _Rn, _Rm) (__arm_gen_u_Rdn_RdnRmRs(_ARMINTR_UMLAL, (_RdHiLo), (_Rn), (_Rm))) + +/* ARMv5E */ + +#define _arm_clz(_Rm) (__arm_gen_u_Rd_Rm2(_ARMINTR_CLZ, (_Rm))) + +#define _arm_qadd(_Rm, _Rn) (__arm_gen_s_Rd_RnRm_q(_ARMINTR_QADD, (_Rn), (_Rm))) +#define _arm_qdadd(_Rm, _Rn) (__arm_gen_s_Rd_RnRm_q(_ARMINTR_QDADD, (_Rn), (_Rm))) +#define _arm_qdsub(_Rm, _Rn) (__arm_gen_s_Rd_RnRm_q(_ARMINTR_QDSUB, (_Rn), (_Rm))) +#define _arm_qsub(_Rm, _Rn) (__arm_gen_s_Rd_RnRm_q(_ARMINTR_QSUB, (_Rn), (_Rm))) + +#define _arm_smlabb(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLABB, (_Rn), (_Rm), (_Ra))) +#define _arm_smlabt(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLABT, (_Rn), (_Rm), (_Ra))) +#define _arm_smlatb(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLATB, (_Rn), (_Rm), (_Ra))) +#define _arm_smlatt(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLATT, (_Rn), (_Rm), (_Ra))) + +#define _arm_smlalbb(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLALBB, (_RdHiLo), (_Rn), (_Rm))) +#define _arm_smlalbt(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLALBT, (_RdHiLo), (_Rn), (_Rm))) +#define _arm_smlaltb(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLALTB, (_RdHiLo), (_Rn), (_Rm))) +#define _arm_smlaltt(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLALTT, (_RdHiLo), (_Rn), (_Rm))) + +#define _arm_smlawb(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLAWB, (_Rn), (_Rm), (_Ra))) +#define _arm_smlawt(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLAWT, (_Rn), (_Rm), (_Ra))) + +#define _arm_smulbb(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMULBB, (_Rn), (_Rm))) +#define _arm_smulbt(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMULBT, (_Rn), (_Rm))) +#define _arm_smultb(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMULTB, (_Rn), (_Rm))) +#define _arm_smultt(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMULTT, (_Rn), (_Rm))) + +#define _arm_smulwb(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMULWB, (_Rn), (_Rm))) +#define _arm_smulwt(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMULWT, (_Rn), (_Rm))) + +/* ARMv6 */ + +#define _arm_sadd16(_Rn, _Rm) (__arm_gen_s_Rd_RnRm_g(_ARMINTR_SADD16, (_Rn), (_Rm))) +#define _arm_sadd8(_Rn, _Rm) (__arm_gen_s_Rd_RnRm_g(_ARMINTR_SADD8, (_Rn), (_Rm))) +#define _arm_sasx(_Rn, _Rm) (__arm_gen_s_Rd_RnRm_g(_ARMINTR_SASX, (_Rn), (_Rm))) +#define _arm_ssax(_Rn, _Rm) (__arm_gen_s_Rd_RnRm_g(_ARMINTR_SSAX, (_Rn), (_Rm))) +#define _arm_ssub16(_Rn, _Rm) (__arm_gen_s_Rd_RnRm_g(_ARMINTR_SSUB16, (_Rn), (_Rm))) +#define _arm_ssub8(_Rn, _Rm) (__arm_gen_s_Rd_RnRm_g(_ARMINTR_SSUB8, (_Rn), (_Rm))) + +#define _arm_shadd16(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_SHADD16, (_Rn), (_Rm))) +#define _arm_shadd8(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_SHADD8, (_Rn), (_Rm))) +#define _arm_shasx(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_SHASX, (_Rn), (_Rm))) +#define _arm_shsax(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_SHSAX, (_Rn), (_Rm))) +#define _arm_shsub16(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_SHSUB16, (_Rn), (_Rm))) +#define _arm_shsub8(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_SHSUB8, (_Rn), (_Rm))) + +#define _arm_qadd16(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_QADD16, (_Rn), (_Rm))) +#define _arm_qadd8(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_QADD8, (_Rn), (_Rm))) +#define _arm_qasx(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_QASX, (_Rn), (_Rm))) +#define _arm_qsax(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_QSAX, (_Rn), (_Rm))) +#define _arm_qsub16(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_QSUB16, (_Rn), (_Rm))) +#define _arm_qsub8(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_QSUB8, (_Rn), (_Rm))) + +#define _arm_uadd16(_Rn, _Rm) (__arm_gen_u_Rd_RnRm_g(_ARMINTR_UADD16, (_Rn), (_Rm))) +#define _arm_uadd8(_Rn, _Rm) (__arm_gen_u_Rd_RnRm_g(_ARMINTR_UADD8, (_Rn), (_Rm))) +#define _arm_uasx(_Rn, _Rm) (__arm_gen_u_Rd_RnRm_g(_ARMINTR_UASX, (_Rn), (_Rm))) +#define _arm_usax(_Rn, _Rm) (__arm_gen_u_Rd_RnRm_g(_ARMINTR_USAX, (_Rn), (_Rm))) +#define _arm_usub16(_Rn, _Rm) (__arm_gen_u_Rd_RnRm_g(_ARMINTR_USUB16, (_Rn), (_Rm))) +#define _arm_usub8(_Rn, _Rm) (__arm_gen_u_Rd_RnRm_g(_ARMINTR_USUB8, (_Rn), (_Rm))) + +#define _arm_uhadd16(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UHADD16, (_Rn), (_Rm))) +#define _arm_uhadd8(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UHADD8, (_Rn), (_Rm))) +#define _arm_uhasx(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UHASX, (_Rn), (_Rm))) +#define _arm_uhsax(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UHSAX, (_Rn), (_Rm))) +#define _arm_uhsub16(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UHSUB16, (_Rn), (_Rm))) +#define _arm_uhsub8(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UHSUB8, (_Rn), (_Rm))) + +#define _arm_uqadd16(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UQADD16, (_Rn), (_Rm))) +#define _arm_uqadd8(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UQADD8, (_Rn), (_Rm))) +#define _arm_uqasx(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UQASX, (_Rn), (_Rm))) +#define _arm_uqsax(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UQSAX, (_Rn), (_Rm))) +#define _arm_uqsub16(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UQSUB16, (_Rn), (_Rm))) +#define _arm_uqsub8(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UQSUB8, (_Rn), (_Rm))) + +#define _arm_sxtab(_Rn, _Rm, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_s_Rd_RnRm(_ARMINTR_SXTAB | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn), (_Rm))) +#define _arm_sxtab16(_Rn, _Rm, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_s_Rd_RnRm(_ARMINTR_SXTAB16 | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn), (_Rm))) +#define _arm_sxtah(_Rn, _Rm, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_s_Rd_RnRm(_ARMINTR_SXTAH | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn), (_Rm))) + +#define _arm_uxtab(_Rn, _Rm, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_u_Rd_RnRm(_ARMINTR_UXTAB | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn), (_Rm))) +#define _arm_uxtab16(_Rn, _Rm, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_u_Rd_RnRm(_ARMINTR_UXTAB16 | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn), (_Rm))) +#define _arm_uxtah(_Rn, _Rm, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_u_Rd_RnRm(_ARMINTR_UXTAH | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn), (_Rm))) + +#define _arm_sxtb(_Rn, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_s_Rd_Rm(_ARMINTR_SXTB | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn))) +#define _arm_sxtb16(_Rn, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_s_Rd_Rm(_ARMINTR_SXTB16 | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn))) +#define _arm_sxth(_Rn, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_s_Rd_Rm(_ARMINTR_SXTH | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn))) + +#define _arm_uxtb(_Rn, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_u_Rd_Rm(_ARMINTR_UXTB | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn))) +#define _arm_uxtb16(_Rn, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_u_Rd_Rm(_ARMINTR_UXTB16 | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn))) +#define _arm_uxth(_Rn, _Rotation) (_ARMINTR_ASSERT_ROT(_Rotation), __arm_gen_u_Rd_Rm(_ARMINTR_UXTBH | _ARMINTR_ENCODE_XTROT(_Rotation), (_Rn))) + +#define _arm_pkhbt(_Rn, _Rm, _Lsl_imm) (_ARMINTR_ASSERT_RANGE((_Lsl_imm), 0, 31, "logical left shift"), \ + __arm_gen_s_Rd_RnRm(_ARMINTR_PKHBT | _ARMINTR_ENCODE_PKHSHIFT(_Lsl_imm), (_Rn), (_Rm))) +#define _arm_pkhtb(_Rn, _Rm, _Asr_imm) (_ARMINTR_ASSERT_RANGE((_Asr_imm), 1, 32, "arithmetic right shift"), \ + __arm_gen_s_Rd_RnRm(_ARMINTR_PKHTB | _ARMINTR_ENCODE_PKHSHIFT(_Asr_imm), (_Rn), (_Rm))) + +#define _arm_usad8(_Rn, _Rm) (__arm_gen_u_Rn_RmRs(_ARMINTR_USAD8, (_Rn), (_Rm))) + +#define _arm_usada8(_Rn, _Rm, _Ra) (__arm_gen_u_Rn_RmRsRd(_ARMINTR_USADA8, (_Rn), (_Rm), (_Ra))) + +#define _arm_ssat(_Sat_imm, _Rn, _Shift_type, _Shift_imm) (_ARMINTR_ASSERT_SAT_BIT((_Sat_imm), 1, 32), _ARMINTR_ASSERT_SAT_SHIFT((_Shift_type), (_Shift_imm)), \ + _ARMINTR_S_DMx(_ARMINTR_SSAT | _ARMINTR_ENCODE_IMM5_16((_Sat_imm) - 1) | _ARMINTR_ENCODE_SAT_SH((_Shift_type), (_Shift_imm)), (_Rn))) +#define _arm_usat(_Sat_imm, _Rn, _Shift_type, _Shift_imm) (_ARMINTR_ASSERT_SAT_BIT((_Sat_imm), 0, 31), _ARMINTR_ASSERT_SAT_SHIFT((_Shift_type), (_Shift_imm)), \ + _ARMINTR_S_DMx(_ARMINTR_USAT | _ARMINTR_ENCODE_IMM5_16(_Sat_imm) | _ARMINTR_ENCODE_SAT_SH((_Shift_type), (_Shift_imm)), (_Rn))) + +#define _arm_ssat16(_Sat_imm, _Rn) (_ARMINTR_ASSERT_SAT_BIT((_Sat_imm), 1, 16), \ + _ARMINTR_S_DMx(_ARMINTR_SSAT16 | _ARMINTR_ENCODE_IMM4_16((_Sat_imm) - 1), (_Rn))) +#define _arm_usat16(_Sat_imm, _Rn) (_ARMINTR_ASSERT_SAT_BIT((_Sat_imm), 0, 15), \ + _ARMINTR_S_DMx(_ARMINTR_USAT16 | _ARMINTR_ENCODE_IMM4_16(_Sat_imm), (_Rn))) + +#define _arm_rev(_Rm) (__arm_gen_u_Rd_Rm2(_ARMINTR_REV, (_Rm))) +#define _arm_rev16(_Rm) (__arm_gen_u_Rd_Rm2(_ARMINTR_REV16, (_Rm))) +#define _arm_revsh(_Rm) (__arm_gen_s_Rd_Rm2(_ARMINTR_REVSH, (_Rm))) + +#define _arm_smlad(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLAD, (_Rn), (_Rm), (_Ra))) +#define _arm_smladx(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLADX, (_Rn), (_Rm), (_Ra))) +#define _arm_smlsd(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLSD, (_Rn), (_Rm), (_Ra))) +#define _arm_smlsdx(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMLSDX, (_Rn), (_Rm), (_Ra))) + +#define _arm_smmla(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMMLA, (_Rn), (_Rm), (_Ra))) +#define _arm_smmlar(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMMLAR, (_Rn), (_Rm), (_Ra))) +#define _arm_smmls(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMMLS, (_Rn), (_Rm), (_Ra))) +#define _arm_smmlsr(_Rn, _Rm, _Ra) (__arm_gen_s_Rn_RmRsRd(_ARMINTR_SMMLSR, (_Rn), (_Rm), (_Ra))) + +#define _arm_smmul(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMMUL, (_Rn), (_Rm))) +#define _arm_smmulr(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMMULR, (_Rn), (_Rm))) + +#define _arm_smlald(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLALD, (_RdHiLo), (_Rn), (_Rm))) +#define _arm_smlaldx(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLALDX, (_RdHiLo), (_Rn), (_Rm))) +#define _arm_smlsld(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLSLD, (_RdHiLo), (_Rn), (_Rm))) +#define _arm_smlsldx(_RdHiLo, _Rn, _Rm) (__arm_gen_s_Rdn_RdnRmRs(_ARMINTR_SMLSLDX, (_RdHiLo), (_Rn), (_Rm))) + +#define _arm_smuad(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMUAD, (_Rn), (_Rm))) +#define _arm_smuadx(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMUADX, (_Rn), (_Rm))) +#define _arm_smusd(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMUSD, (_Rn), (_Rm))) +#define _arm_smusdx(_Rn, _Rm) (__arm_gen_s_Rn_RmRs(_ARMINTR_SMUSDX, (_Rn), (_Rm))) + +#define _arm_smull(_Rn, _Rm) (__arm_gen_s_Rdn_RmRs(_ARMINTR_SMULL, (_Rn), (_Rm))) +#define _arm_umull(_Rn, _Rm) (__arm_gen_u_Rdn_RmRs(_ARMINTR_UMULL, (_Rn), (_Rm))) + +#define _arm_umaal(_RdLo, _RdHi, _Rn, _Rm) (__arm_gen_u_Rdn_RdnRmRs(_ARMINTR_UMAAL, ((unsigned __int64)(_RdHi) << 32) | (_RdLo), (_Rn), (_Rm))) + +/* ARMv6T2 */ + +#define _arm_bfc(_Rd, _Lsb, _Width) (_ARMINTR_ASSERT_RANGE((_Lsb), 0, 31, "least significant bit"), _ARMINTR_ASSERT_RANGE((_Width), 1, 32-(_Lsb), "width"), \ + __arm_gen_u_Rd_Rd(_ARMINTR_BFC | _ARMINTR_ENCODE_IMM5_7(_Lsb) | _ARMINTR_ENCODE_IMM5_16((_Lsb) + (_Width) - 1), (_Rd))) +#define _arm_bfi(_Rd, _Rn, _Lsb, _Width) (_ARMINTR_ASSERT_RANGE((_Lsb), 0, 31, "least significant bit"), _ARMINTR_ASSERT_RANGE((_Width), 1, 32-(_Lsb), "width"), \ + _ARMINTR_U_DDMx(_ARMINTR_BFI | _ARMINTR_ENCODE_IMM5_7(_Lsb) | _ARMINTR_ENCODE_IMM5_16((_Lsb) + (_Width) - 1), (_Rd), (_Rn))) + +#define _arm_rbit(_Rm) (__arm_gen_u_Rd_Rm2(_ARMINTR_RBIT, (_Rm))) + +#define _arm_sbfx(_Rn, _Lsb, _Width) (_ARMINTR_ASSERT_RANGE((_Lsb), 0, 31, "least significant bit"), _ARMINTR_ASSERT_RANGE((_Width), 1, 32-(_Lsb), "width"), \ + _ARMINTR_S_DMx(_ARMINTR_SBFX | _ARMINTR_ENCODE_IMM5_7(_Lsb) | _ARMINTR_ENCODE_IMM5_16((_Width) - 1), (_Rn))) +#define _arm_ubfx(_Rn, _Lsb, _Width) (_ARMINTR_ASSERT_RANGE((_Lsb), 0, 31, "least significant bit"), _ARMINTR_ASSERT_RANGE((_Width), 1, 32-(_Lsb), "width"), \ + _ARMINTR_U_DMx(_ARMINTR_UBFX | _ARMINTR_ENCODE_IMM5_7(_Lsb) | _ARMINTR_ENCODE_IMM5_16((_Width) - 1), (_Rn))) + +/* ARMv7 */ + +#if defined (_M_THUMB) + +#define _arm_sdiv(_Rn, _Rm) (__arm_gen_s_Rd_RnRm(_ARMINTR_SDIV, (_Rn), (_Rm))) +#define _arm_udiv(_Rn, _Rm) (__arm_gen_u_Rd_RnRm(_ARMINTR_UDIV, (_Rn), (_Rm))) + +#endif /* defined (_M_THUMB) */ + +typedef enum _tag_ARMINTR_CPS_OP +{ + _ARM_CPS_ENABLE_INTERRUPTS = 1, + _ARM_CPS_DISABLE_INTERRUPTS = 2, + _ARM_CPS_CHANGE_MODE = 4 +} +_ARMINTR_CPS_OP; + +typedef enum _tag_ARMINTR_CPS_FLAG +{ + _ARM_CPS_INTERRUPT_FLAG_F = 1, + _ARM_CPS_INTERRUPT_FLAG_I = 2, + _ARM_CPS_INTERRUPT_FLAG_A = 4 +} +_ARMINTR_CPS_FLAG; + +void __cps(unsigned int _Ops, unsigned int _Flags, unsigned int _Mode); + + +typedef enum _tag_ARMINTR_BARRIER_TYPE +{ + _ARM_BARRIER_SY = 0xF, + _ARM_BARRIER_ST = 0xE, + _ARM_BARRIER_ISH = 0xB, + _ARM_BARRIER_ISHST = 0xA, + _ARM_BARRIER_NSH = 0x7, + _ARM_BARRIER_NSHST = 0x6, + _ARM_BARRIER_OSH = 0x3, + _ARM_BARRIER_OSHST = 0x2 +} +_ARMINTR_BARRIER_TYPE; + +void __dmb(unsigned int _Type); +void __dsb(unsigned int _Type); +void __isb(unsigned int _Type); + +/* ARMv7VE */ + +typedef enum _tag_ARMINTR_BANKED_REG +{ + _ARM_BANKED_R8_USR = 0x0, + _ARM_BANKED_R9_USR = 0x1, + _ARM_BANKED_R10_USR = 0x2, + _ARM_BANKED_R11_USR = 0x3, + _ARM_BANKED_R12_USR = 0x4, + _ARM_BANKED_R13_USR = 0x5, + _ARM_BANKED_SP_USR = 0x5, + _ARM_BANKED_R14_USR = 0x6, + _ARM_BANKED_LR_USR = 0x6, + _ARM_BANKED_R8_FIQ = 0x8, + _ARM_BANKED_R9_FIQ = 0x9, + _ARM_BANKED_R10_FIQ = 0xA, + _ARM_BANKED_R11_FIQ = 0xB, + _ARM_BANKED_R12_FIQ = 0xC, + _ARM_BANKED_R13_FIQ = 0xD, + _ARM_BANKED_SP_FIQ = 0xD, + _ARM_BANKED_R14_FIQ = 0xE, + _ARM_BANKED_LR_FIQ = 0xE, + _ARM_BANKED_R14_IRQ = 0x10, + _ARM_BANKED_LR_IRQ = 0x10, + _ARM_BANKED_R13_IRQ = 0x11, + _ARM_BANKED_SP_IRQ = 0x11, + _ARM_BANKED_R14_SVC = 0x12, + _ARM_BANKED_LR_SVC = 0x12, + _ARM_BANKED_R13_SVC = 0x13, + _ARM_BANKED_SP_SVC = 0x13, + _ARM_BANKED_R14_ABT = 0x14, + _ARM_BANKED_LR_ABT = 0x14, + _ARM_BANKED_R13_ABT = 0x15, + _ARM_BANKED_SP_ABT = 0x15, + _ARM_BANKED_R14_UND = 0x16, + _ARM_BANKED_LR_UND = 0x16, + _ARM_BANKED_R13_UND = 0x17, + _ARM_BANKED_SP_UND = 0x17, + _ARM_BANKED_R14_MON = 0x1C, + _ARM_BANKED_LR_MON = 0x1C, + _ARM_BANKED_R13_MON = 0x1D, + _ARM_BANKED_SP_MON = 0x1D, + _ARM_BANKED_ELR_HYP = 0x1E, + _ARM_BANKED_R13_HYP = 0x1F, + _ARM_BANKED_SP_HYP = 0x1F, + _ARM_BANKED_SPSR_FIQ = 0x2E, + _ARM_BANKED_SPSR_IRQ = 0x30, + _ARM_BANKED_SPSR_SVC = 0x32, + _ARM_BANKED_SPSR_ABT = 0x34, + _ARM_BANKED_SPSR_UND = 0x36, + _ARM_BANKED_SPSR_MON = 0x3C, + _ARM_BANKED_SPSR_HYP = 0x3E +}; + +void _WriteBankedReg(int _Value, int _Reg); +int _ReadBankedReg(int _Reg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/test_data/lots_of_files/asctime.c b/test_data/lots_of_files/asctime.c new file mode 100644 index 0000000..77f742c --- /dev/null +++ b/test_data/lots_of_files/asctime.c @@ -0,0 +1,219 @@ +/*** +*asctime.c - convert date/time structure to ASCII string +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Contains asctime() - convert a date/time structure to ASCII string. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <time.h> +#include <internal.h> +#include <internal_securecrt.h> +#include <mtdll.h> +#include <malloc.h> +#include <stddef.h> +#include <tchar.h> +#include <dbgint.h> + +#define _ASCBUFSIZE 26 +static _TSCHAR buf[_ASCBUFSIZE]; + +/* +** This prototype must be local to this file since the procedure is static +*/ + +static _TSCHAR * __cdecl store_dt(_TSCHAR *, int); + +static _TSCHAR * __cdecl store_dt ( + _TSCHAR *p, + int val + ) +{ + *p++ = (_TSCHAR)(_T('0') + val / 10); + *p++ = (_TSCHAR)(_T('0') + val % 10); + return(p); +} + + +/*** +*errno_t asctime_s(buffer, sizeInChars, time) - convert a structure time to +*ascii string +* +*Purpose: +* Converts a time stored in a struct tm to a charcater string. +* The string is always exactly 26 characters of the form +* Tue May 01 02:34:55 1984\n\0 +* +*Entry: +* struct tm *time - ptr to time structure +* _TSCHAR *buffer - ptr to output buffer +* size_t sizeInChars - size of the buffer in characters including sapce for +* NULL terminator +* +*Exit: +* errno_t = 0 success +* out buffer with time string. +* errno_t = correct error code +* out buffer NULL terminated if it is at least 1 character in size +* +*Exceptions: +* +*******************************************************************************/ + +errno_t __cdecl _tasctime_s ( + _TSCHAR *buffer, + size_t sizeInChars, + const struct tm *tb + ) +{ + _TSCHAR *p = buffer; + int day, mon; + int i; + + _VALIDATE_RETURN_ERRCODE( + ( buffer != NULL ) && ( sizeInChars > 0 ), + EINVAL + ) + + _RESET_STRING(buffer, sizeInChars); + + _VALIDATE_RETURN_ERRCODE( + ( sizeInChars >= _ASCBUFSIZE ), + EINVAL + ) + _VALIDATE_RETURN_ERRCODE( + ( tb != NULL ), + EINVAL + ) + _VALIDATE_RETURN_ERRCODE( + ( tb->tm_year >= 0 ), + EINVAL + ) + // month 0 based + _VALIDATE_RETURN_ERRCODE( + ( ( tb->tm_mon >= 0 ) && ( tb->tm_mon <= 11 ) ), + EINVAL + ) + // hour/min/sec 0 based + _VALIDATE_RETURN_ERRCODE( + ( ( tb->tm_hour >= 0 ) && ( tb->tm_hour <= 23 ) ), + EINVAL + ) + _VALIDATE_RETURN_ERRCODE( + ( ( tb->tm_min >= 0 ) && ( tb->tm_min <= 59 ) ), + EINVAL + ) + _VALIDATE_RETURN_ERRCODE( + ( ( tb->tm_sec >= 0 ) && ( tb->tm_sec <= 59 ) ), + EINVAL + ) + // day 1 based + _VALIDATE_RETURN_ERRCODE( + ( + ( tb->tm_mday >= 1 ) && + ( + // Day is in valid range for the month + ( ( _days[ tb->tm_mon + 1 ] - _days[ tb->tm_mon ] ) >= + tb->tm_mday ) || + // Special case for Feb in a leap year + ( + ( IS_LEAP_YEAR( tb->tm_year + 1900 ) ) && + ( tb->tm_mon == 1 ) && + ( tb->tm_mday <= 29 ) + ) + ) + ), + EINVAL + ) + // week day 0 based + _VALIDATE_RETURN_ERRCODE( + ( ( tb->tm_wday >= 0 ) && ( tb->tm_wday <= 6 ) ), + EINVAL + ) + + + /* copy day and month names into the buffer */ + + day = tb->tm_wday * 3; /* index to correct day string */ + mon = tb->tm_mon * 3; /* index to correct month string */ + for (i=0; i < 3; i++,p++) { + *p = *(__dnames + day + i); + *(p+4) = *(__mnames + mon + i); + } + + *p = _T(' '); /* blank between day and month */ + + p += 4; + + *p++ = _T(' '); + p = store_dt(p, tb->tm_mday); /* day of the month (1-31) */ + *p++ = _T(' '); + p = store_dt(p, tb->tm_hour); /* hours (0-23) */ + *p++ = _T(':'); + p = store_dt(p, tb->tm_min); /* minutes (0-59) */ + *p++ = _T(':'); + p = store_dt(p, tb->tm_sec); /* seconds (0-59) */ + *p++ = _T(' '); + p = store_dt(p, 19 + (tb->tm_year/100)); /* year (after 1900) */ + p = store_dt(p, tb->tm_year%100); + *p++ = _T('\n'); + *p = _T('\0'); + + + return 0; +} + +/*** +*char *asctime(time) - convert a structure time to ascii string +* +*Purpose: +* Converts a time stored in a struct tm to a charcater string. +* The string is always exactly 26 characters of the form +* Tue May 01 02:34:55 1984\n\0 +* +*Entry: +* struct tm *time - ptr to time structure +* +*Exit: +* returns pointer to static string with time string. +* +*Exceptions: +* +*******************************************************************************/ + +_TSCHAR * __cdecl _tasctime ( + const struct tm *tb + ) +{ + _TSCHAR *p = buf; + errno_t e = 0; + + _TSCHAR *retval; /* holds retval pointer */ + _ptiddata ptd = _getptd_noexit(); + + /* Use per thread buffer area (malloc space, if necessary) */ + if (ptd) { +#ifdef _UNICODE + if ( (ptd->_wasctimebuf != NULL) || ((ptd->_wasctimebuf = + (wchar_t *)_calloc_crt(_ASCBUFSIZE, sizeof(wchar_t))) != NULL) ) + p = ptd->_wasctimebuf; +#else /* _UNICODE */ + if ( (ptd->_asctimebuf != NULL) || ((ptd->_asctimebuf = + (char *)_calloc_crt(_ASCBUFSIZE, sizeof(char))) != NULL) ) + p = ptd->_asctimebuf; +#endif /* _UNICODE */ + } + + retval = p; /* save return value for later */ + + e = _tasctime_s( p, _ASCBUFSIZE, tb ); + if ( e != 0 ) + { + return NULL; + } + + return (retval); +} diff --git a/test_data/lots_of_files/assembler.c b/test_data/lots_of_files/assembler.c new file mode 100644 index 0000000..73474f3 --- /dev/null +++ b/test_data/lots_of_files/assembler.c @@ -0,0 +1,387 @@ +/* Assembler code fragment for LC-2K */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define MAXLINELENGTH 1000 + +int readAndParse(FILE *, char *, char *, char *, char *, char *); +int isNumber(char *, int *); + +typedef struct{ + char *word; + unsigned int hash; + int line_number; +} Label; + +typedef struct{ + char *word_space; + char *word_cursor; + Label *labels; + int count; + int max; +} Table; + +char * +copy_advance(char *dest, char *src){ + while (*dest++ = *src++); + return(dest); +} + +int +get_hash(char *string){ + unsigned int x, c; + x = 5236; + while (c = *string++){ + x += c + (x << 5) + (x << 1); + } + return(x); +} + +int +add_label(Table *table, char *string, int line_number){ + int r; + Label l, *ls; + int max; + unsigned int index; + + r = 0; + max = table->max; + ls = table->labels; + + l.word = table->word_cursor; + table->word_cursor = copy_advance(table->word_cursor, string); + l.hash = get_hash(string); + l.line_number = line_number; + index = l.hash % max; + + while (ls[index].word){ + if (ls[index].hash == l.hash){ + if (strcmp(ls[index].word, l.word) == 0){ + r = 1; + break; + } + } + index = (index + 1) % max; + } + + if (r == 0) ls[index] = l; + ++table->count; + + return(r); +} + +int +find_label(Table *table, char *string, int *line_number){ + int r; + Label l, *ls; + int max; + unsigned int hash,index; + + r = 0; + max = table->max; + ls = table->labels; + hash = get_hash(string); + index = hash % max; + + while (ls[index].word){ + if (ls[index].hash == hash){ + if (strcmp(ls[index].word, string) == 0){ + r = 1; + *line_number = ls[index].line_number; + break; + } + } + index = (index + 1) % max; + } + + return(r); +} + +#define OP_FILL -1 +#define OP_ADD 0 +#define OP_NAND 1 +#define OP_LW 2 +#define OP_SW 3 +#define OP_BEQ 4 +#define OP_JALR 5 +#define OP_HALT 6 +#define OP_NOOP 7 + +#define MIN16S -32768 +#define MAX16S 32767 + +int +encode_insanity(FILE *outFilePtr, int o, int a, int b, int c){ + int ins, r; + + r = 0; + if (o == OP_FILL){ + r = (b != 0 || c != 0); + } + else{ + r = (a < 0 || a > 7 || b < 0 || b > 7); + if (!r){ + switch (o){ + case OP_ADD: case OP_NAND: + r = (c < 0 || c > 7); break; + + case OP_LW: case OP_SW: + case OP_BEQ: case OP_JALR: + r = (c < MIN16S || c > MAX16S); break; + + case OP_HALT: case OP_NOOP: + r = (a != 0 || b != 0 || c != 0); break; + + default: r = 1; break; + } + } + } + + if (!r){ + if (o == OP_FILL){ + ins = a; + } + else{ + ins = 0; + ins |= (o << 22); + ins |= (a << 19); + ins |= (b << 16); + ins |= 0xFFFF & (c); + } + fprintf(outFilePtr, "%d\n", ins); + } + + return(r); +} + +int +main(int argc, char *argv[]) +{ + Table table; + char *inFileString, *outFileString; + FILE *inFilePtr, *outFilePtr; + char label[MAXLINELENGTH], opcode[MAXLINELENGTH], arg0[MAXLINELENGTH], + arg1[MAXLINELENGTH], arg2[MAXLINELENGTH]; + int max_instructions; + int line_number; + int i; + int o,a,b,c; + + if (argc != 3) { + printf("error: usage: %s <assembly-code-file> <machine-code-file>\n", + argv[0]); + exit(1); + } + + inFileString = argv[1]; + outFileString = argv[2]; + + inFilePtr = fopen(inFileString, "r"); + if (inFilePtr == NULL) { + printf("error in opening %s\n", inFileString); + exit(1); + } + outFilePtr = fopen(outFileString, "w"); + if (outFilePtr == NULL) { + printf("error in opening %s\n", outFileString); + exit(1); + } + + fseek(inFilePtr, 0, SEEK_END); + max_instructions = ftell(inFilePtr); + fseek(inFilePtr, 0, SEEK_SET); + max_instructions >>= 2; + + table.max = max_instructions * 3 / 2; + table.labels = (Label*)malloc(sizeof(Label)*table.max); + table.count = 0; + table.word_space = table.word_cursor = (char*)malloc((max_instructions << 2) + max_instructions); + memset(table.labels, 0, sizeof(Label)*table.max); + + line_number = 0; + while (readAndParse(inFilePtr, label, opcode, arg0, arg1, arg2)){ + if (*label != 0){ + if (add_label(&table, label, line_number)){ + printf("error two labels of same name: %s\n", label); + exit(1); + } + } + ++line_number; + } + + rewind(inFilePtr); + + line_number = 0; + while (readAndParse(inFilePtr, label, opcode, arg0, arg1, arg2)){ + if (strcmp(opcode, "add") == 0){ + o = 0; + sscanf(arg0, "%d", &a); + sscanf(arg1, "%d", &b); + sscanf(arg2, "%d", &c); + } + else if (strcmp(opcode, "nand") == 0){ + o = 1; + sscanf(arg0, "%d", &a); + sscanf(arg1, "%d", &b); + sscanf(arg2, "%d", &c); + } + else if (strcmp(opcode, "lw") == 0){ + o = 2; + sscanf(arg0, "%d", &a); + sscanf(arg1, "%d", &b); + if (!isNumber(arg2, &c)){ + if (!find_label(&table, arg2, &c)){ + printf("non-existant label: %s\n", arg2); + exit(1); + } + } + } + else if (strcmp(opcode, "sw") == 0){ + o = 3; + sscanf(arg0, "%d", &a); + sscanf(arg1, "%d", &b); + if (!isNumber(arg2, &c)){ + if (!find_label(&table, arg2, &c)){ + printf("non-existant label: %s\n", arg2); + exit(1); + } + } + } + else if (strcmp(opcode, "beq") == 0){ + o = 4; + sscanf(arg0, "%d", &a); + sscanf(arg1, "%d", &b); + if (!isNumber(arg2, &c)){ + if (!find_label(&table, arg2, &c)){ + printf("non-existant label: %s\n", arg2); + exit(1); + } + c = c - (line_number + 1); + } + } + else if (strcmp(opcode, "jalr") == 0){ + o = 5; + sscanf(arg0, "%d", &a); + sscanf(arg1, "%d", &b); + c = 0; + } + else if (strcmp(opcode, "halt") == 0){ + o = 6; + a = b = c = 0; + } + else if (strcmp(opcode, "noop") == 0){ + o = 7; + a = b = c = 0; + } + else if (strcmp(opcode, ".fill") == 0){ + o = -1; + if (!isNumber(arg0, &a)){ + if (!find_label(&table, arg0, &a)){ + printf("non-existant label: %s\n", arg0); + exit(1); + } + } + b = c = 0; + } + else{ + printf("not real instruction found: %s\n", opcode); + exit(1); + } + + if (encode_insanity(outFilePtr, o, a, b, c)){ + printf("encoding error on line %d\n", line_number + 1); + exit(1); + } + + ++line_number; + } + + free(table.word_space); + free(table.labels); + fclose(inFilePtr); + fclose(outFilePtr); + exit(0); + + return(0); +} + +/* + * Read and parse a line of the assembly-language file. Fields are returned + * in label, opcode, arg0, arg1, arg2 (these strings must have memory already + * allocated to them). + * + * Return values: + * 0 if reached end of file + * 1 if all went well + * + * exit(1) if line is too long. + */ +int +readAndParse(FILE *inFilePtr, char *label, char *opcode, char *arg0, + char *arg1, char *arg2) +{ + char line[MAXLINELENGTH + 1]; + char *ptr = line; + int l; + + /* delete prior values */ + label[0] = opcode[0] = arg0[0] = arg1[0] = arg2[0] = '\0'; + + /* read the line from the assembly-language file */ + if (fgets(line, MAXLINELENGTH, inFilePtr) == NULL) { + /* reached end of file */ + return(0); + } + + if (feof(inFilePtr)){ + l = strlen(line); + if (line[l-1] != '\n'){ + line[l] = '\n'; + line[l+1] = 0; + } + } + +#if 0 + char line[MAXLINELENGTH]; + char *ptr = line; + + /* delete prior values */ + label[0] = opcode[0] = arg0[0] = arg1[0] = arg2[0] = '\0'; + + /* read the line from the assembly-language file */ + if (fgets(line, MAXLINELENGTH, inFilePtr) == NULL) { + /* reached end of file */ + return(0); + } +#endif + + /* check for line too long (by looking for a \n) */ + if (strchr(line, '\n') == NULL) { + /* line too long */ + printf("error: line too long\n"); + exit(1); + } + + /* is there a label? */ + ptr = line; + if (sscanf(ptr, "%[^\t\n\r ]", label)) { + /* successfully read label; advance pointer over the label */ + ptr += strlen(label); + } + + /* + * Parse the rest of the line. Would be nice to have real regular + * expressions, but scanf will suffice. + */ + sscanf(ptr, "%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r ]", + opcode, arg0, arg1, arg2); + return(1); +} + +int +isNumber(char *string, int *i) +{ + return( (sscanf(string, "%d", i)) == 1); +} diff --git a/test_data/lots_of_files/assert.c b/test_data/lots_of_files/assert.c new file mode 100644 index 0000000..f386aaa --- /dev/null +++ b/test_data/lots_of_files/assert.c @@ -0,0 +1,380 @@ +/*** +*assert.c - Display a message and abort +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* +*******************************************************************************/ + +#include <cruntime.h> +#include <windows.h> +#include <file2.h> +#include <internal.h> +#include <stdlib.h> +#include <stdio.h> +#include <tchar.h> +#include <string.h> +#include <signal.h> +#include <awint.h> +#include <limits.h> +#include <dbgint.h> + +#ifdef NDEBUG +#undef NDEBUG +#endif /* NDEBUG */ +#define _ASSERT_OK +#include <assert.h> + +extern IMAGE_DOS_HEADER __ImageBase; + + +/* + * assertion format string for use with output to stderr + */ +static TCHAR _assertstring[] = _T("Assertion failed: %s, file %s, line %d\n"); + +/* Format of MessageBox for assertions: +* +* ================= Microsft Visual C++ Debug Library ================ +* +* Assertion Failed! +* +* Program: c:\test\mytest\foo.exe +* File: c:\test\mytest\bar.c +* Line: 69 +* +* Expression: <expression> +* +* For information on how your program can cause an assertion +* failure, see the Visual C++ documentation on asserts +* +* (Press Retry to debug the application - JIT must be enabled) +* +* =================================================================== +*/ + +/* + * assertion string components for message box + */ +#define BOXINTRO _T("Assertion failed!") +#define PROGINTRO _T("Program: ") +#define FILEINTRO _T("File: ") +#define LINEINTRO _T("Line: ") +#define EXPRINTRO _T("Expression: ") +#define INFOINTRO _T("For information on how your program can cause an assertion\n") \ + _T("failure, see the Visual C++ documentation on asserts") +#define HELPINTRO _T("(Press Retry to debug the application - JIT must be enabled)") + +static TCHAR * dotdotdot = _T("..."); +static TCHAR * newline = _T("\n"); +static TCHAR * dblnewline = _T("\n\n"); + +#define DOTDOTDOTSZ 3 +#define NEWLINESZ 1 +#define DBLNEWLINESZ 2 + +#define MAXLINELEN 64 /* max length for line in message box */ +#define ASSERTBUFSZ (MAXLINELEN * 9) /* 9 lines in message box */ + +#ifdef _UNICODE +void __cdecl _assert (const char *, const char *,unsigned); +#endif /* _UNICODE */ + +/*** +*_assert() - Display a message and abort +* +*Purpose: +* The assert macro calls this routine if the assert expression is +* true. By placing the assert code in a subroutine instead of within +* the body of the macro, programs that call assert multiple times will +* save space. +* +*Entry: +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +#ifdef _UNICODE +void __cdecl _wassert ( + const wchar_t *expr, + const wchar_t *filename, + unsigned lineno + ) +#else /* _UNICODE */ +void __cdecl _assert ( + + const char *expr, + const char *filename, + unsigned lineno + ) +#endif /* _UNICODE */ +{ +#if !defined (_CRT_APP) || defined (_DEBUG) + /* + * Build the assertion message, then write it out. The exact form + * depends on whether it is to be written out via stderr or the + * MessageBox API. + */ + if ( (_set_error_mode(_REPORT_ERRMODE)== _OUT_TO_STDERR) || + ((_set_error_mode(_REPORT_ERRMODE) == _OUT_TO_DEFAULT) && + (__app_type == _CONSOLE_APP)) ) + { +#ifdef _UNICODE + { + TCHAR assertbuf[ASSERTBUFSZ]; + HANDLE hErr ; + DWORD written; + + hErr = GetStdHandle(STD_ERROR_HANDLE); + + if(hErr!=INVALID_HANDLE_VALUE && hErr!=NULL) + { + if(swprintf(assertbuf, ASSERTBUFSZ,_assertstring,expr,filename,lineno) >= 0) + { + if(GetFileType(hErr) == FILE_TYPE_CHAR) + { + if(WriteConsoleW(hErr, assertbuf, (unsigned long)wcslen(assertbuf), &written, NULL)) + { + abort(); + } + } + } + } + } + +#endif /* _UNICODE */ + + /* + * Build message and write it out to stderr. It will be of the + * form: + * Assertion failed: <expr>, file <filename>, line <lineno> + */ + if ( !anybuf(stderr) ) + /* + * stderr is unused, hence unbuffered, as yet. set it to + * single character buffering (to avoid a malloc() of a + * stream buffer). + */ + (void) setvbuf(stderr, NULL, _IONBF, 0); + + + _ftprintf(stderr, _assertstring, expr, filename, lineno); + fflush(stderr); + + } + /* Display the message using a message box */ + else +#endif /* !defined (_CRT_APP) || defined (_DEBUG) */ + { + int nCode; + TCHAR * pch; + TCHAR assertbuf[ASSERTBUFSZ]; + TCHAR progname[MAX_PATH + 1]; + HMODULE hModule = NULL; + + /* + * Line 1: box intro line + */ + _ERRCHECK(_tcscpy_s( assertbuf, ASSERTBUFSZ, BOXINTRO )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dblnewline )); + + /* + * Line 2: program line + */ + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, PROGINTRO )); + +#if !defined (_DEBUG) && defined (_CRT_APP) + _ERRCHECK(_tcscpy_s( progname, MAX_PATH + 1, _T("Application"))); +#else /* !defined (_DEBUG) && defined (_CRT_APP) */ + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR) _ReturnAddress(), &hModule)) + { + hModule = NULL; + } +#ifdef CRTDLL + else if (hModule == (HMODULE)&__ImageBase) + { + // This is CRT DLL. Use the EXE instead + hModule = NULL; + } +#endif /* CRTDLL */ +#endif /* !defined(_DEBUG) && defined(_CRT_APP) */ + +#if !defined(_DEBUG) && defined(_CRT_APP) + _ERRCHECK(_tcscpy_s( progname, MAX_PATH + 1, _T("Application"))); +#else + progname[MAX_PATH] = _T('\0'); + if ( !GetModuleFileName(hModule, progname, MAX_PATH)) + _ERRCHECK(_tcscpy_s( progname, MAX_PATH + 1, _T("<program name unknown>"))); +#endif /* !defined (_DEBUG) && defined (_CRT_APP) */ + pch = (TCHAR *)progname; + + /* sizeof(PROGINTRO) includes the NULL terminator */ + if ( (sizeof(PROGINTRO)/sizeof(TCHAR)) + _tcslen(progname) + NEWLINESZ > MAXLINELEN ) + { + pch += ((sizeof(PROGINTRO)/sizeof(TCHAR)) + _tcslen(progname) + NEWLINESZ) - MAXLINELEN; + /* Only replace first (sizeof(TCHAR) * DOTDOTDOTSZ) bytes to ellipsis */ + _ERRCHECK(memcpy_s(pch, sizeof(TCHAR) * ((MAX_PATH + 1) - (pch - progname)), dotdotdot, sizeof(TCHAR) * DOTDOTDOTSZ )); + + } + + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, pch )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, newline )); + + /* + * Line 3: file line + */ + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, FILEINTRO )); + + /* sizeof(FILEINTRO) includes the NULL terminator */ + if ( (sizeof(FILEINTRO)/sizeof(TCHAR)) + _tcslen(filename) + NEWLINESZ > MAXLINELEN ) + { + size_t p, len, ffn; + + pch = (TCHAR *) filename; + ffn = MAXLINELEN - (sizeof(FILEINTRO)/sizeof(TCHAR)) - NEWLINESZ; + + for ( len = _tcslen(filename), p = 1; + pch[len - p] != _T('\\') && pch[len - p] != _T('/') && p < len; + p++ ); + + /* keeping pathname almost 2/3rd of full filename and rest + * is filename + */ + if ( (ffn - ffn/3) < (len - p) && ffn/3 > p ) + { + /* too long. using first part of path and the + filename string */ + _ERRCHECK(_tcsncat_s( assertbuf, ASSERTBUFSZ, pch, ffn - DOTDOTDOTSZ - p )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dotdotdot )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, pch + len - p )); + } + else if ( ffn - ffn/3 > len - p ) + { + /* pathname is smaller. keeping full pathname and putting + * dotdotdot in the middle of filename + */ + p = p/2; + _ERRCHECK(_tcsncat_s( assertbuf, ASSERTBUFSZ, pch, ffn - DOTDOTDOTSZ - p )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dotdotdot )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, pch + len - p )); + } + else + { + /* both are long. using first part of path. using first and + * last part of filename. + */ + _ERRCHECK(_tcsncat_s( assertbuf, ASSERTBUFSZ, pch, ffn - ffn/3 - DOTDOTDOTSZ )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dotdotdot )); + _ERRCHECK(_tcsncat_s( assertbuf, ASSERTBUFSZ, pch + len - p, ffn/6 - 1 )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dotdotdot )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, pch + len - (ffn/3 - ffn/6 - 2) )); + } + + } + else + /* plenty of room on the line, just append the filename */ + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, filename )); + + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, newline )); + + /* + * Line 4: line line + */ + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, LINEINTRO )); + _ERRCHECK(_itot_s( lineno, assertbuf + _tcslen(assertbuf), ASSERTBUFSZ - _tcslen(assertbuf), 10 )); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dblnewline )); + + /* + * Line 5: message line + */ + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, EXPRINTRO )); + + /* sizeof(HELPINTRO) includes the NULL terminator */ + + if ( _tcslen(assertbuf) + + _tcslen(expr) + + 2*DBLNEWLINESZ + + (sizeof(INFOINTRO)/sizeof(TCHAR)) - 1 + + (sizeof(HELPINTRO)/sizeof(TCHAR)) > ASSERTBUFSZ ) + { + _ERRCHECK(_tcsncat_s( assertbuf, ASSERTBUFSZ, expr, + ASSERTBUFSZ - + (_tcslen(assertbuf) + + DOTDOTDOTSZ + + 2*DBLNEWLINESZ + + (sizeof(INFOINTRO)/sizeof(TCHAR))-1 + + (sizeof(HELPINTRO)/sizeof(TCHAR))))); + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dotdotdot )); + } + else + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, expr )); + + _ERRCHECK(_tcscat_s( assertbuf, ASSERTBUFSZ, dblnewline )); + + /* + * Line 6, 7: info line + */ + + _ERRCHECK(_tcscat_s(assertbuf, ASSERTBUFSZ, INFOINTRO)); + _ERRCHECK(_tcscat_s(assertbuf, ASSERTBUFSZ, dblnewline )); + + /* + * Line 8: help line + */ + _ERRCHECK(_tcscat_s(assertbuf, ASSERTBUFSZ, HELPINTRO)); + +#if !defined (_DEBUG) && defined (_CRT_APP) + /* + * Break if debugger is attached only. + */ + if (IsDebuggerPresent()) + { + nCode = IDRETRY; +#ifdef _UNICODE + OutputDebugStringW(assertbuf); +#endif /* _UNICODE */ + } + else + { + nCode = IDABORT; + } +#else /* !defined (_DEBUG) && defined (_CRT_APP) */ + /* + * Write out via MessageBox + */ + nCode = __crtMessageBox(assertbuf, + _T("Microsoft Visual C++ Runtime Library"), + MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND); +#endif /* !defined (_DEBUG) && defined (_CRT_APP) */ + + /* Abort: abort the program */ + if (nCode == IDABORT) + { + /* raise abort signal */ + raise(SIGABRT); + + /* We usually won't get here, but it's possible that + SIGABRT was ignored. So exit the program anyway. */ + + _exit(3); + } + + /* Retry: call the debugger */ + if (nCode == IDRETRY) + { + __debugbreak(); + /* return to user code */ + return; + } + + /* Ignore: continue execution */ + if (nCode == IDIGNORE) + return; + } + + abort(); +} diff --git a/test_data/lots_of_files/assert.h b/test_data/lots_of_files/assert.h new file mode 100644 index 0000000..bb50d02 --- /dev/null +++ b/test_data/lots_of_files/assert.h @@ -0,0 +1,43 @@ +/*** +*assert.h - define the assert macro +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines the assert(exp) macro. +* [ANSI/System V] +* +* [Public] +* +****/ + +#ifdef _CRTBLD +#ifndef _ASSERT_OK +#error assert.h not for CRT internal use, use dbgint.h +#endif /* _ASSERT_OK */ +#include <cruntime.h> +#endif /* _CRTBLD */ + +#include <crtdefs.h> + +#undef assert + +#ifdef NDEBUG + +#define assert(_Expression) ((void)0) + +#else /* NDEBUG */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +_CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message, _In_z_ const wchar_t *_File, _In_ unsigned _Line); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) ) + +#endif /* NDEBUG */ diff --git a/test_data/lots_of_files/atodbl.c b/test_data/lots_of_files/atodbl.c new file mode 100644 index 0000000..7d7a1f2 --- /dev/null +++ b/test_data/lots_of_files/atodbl.c @@ -0,0 +1,18 @@ +/*** +*atodbl.c - convert a string to a floating point value +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Converts a string to a floating point value. +* +*******************************************************************************/ + +#include <fltintrn.h> +#include <float.h> +#include <math.h> +#include <mtdll.h> +#include <internal.h> +#include <setlocal.h> + +#include <atodbl.inl> diff --git a/test_data/lots_of_files/atof.c b/test_data/lots_of_files/atof.c new file mode 100644 index 0000000..a64315a --- /dev/null +++ b/test_data/lots_of_files/atof.c @@ -0,0 +1,101 @@ +/*** +*atof.c - convert char string to floating point number +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Converts a character string into a floating point number. +* +*******************************************************************************/ + +#include <stdlib.h> +#include <math.h> +#include <cruntime.h> +#include <fltintrn.h> +#include <string.h> +#include <ctype.h> +#include <mbctype.h> +#include <locale.h> +#include <internal.h> +#include <mtdll.h> +#include <setlocal.h> + +/*** +*double atof(nptr) - convert string to floating point number +* +*Purpose: +* atof recognizes an optional string of whitespace, then +* an optional sign, then a string of digits optionally +* containing a decimal point, then an optional e or E followed +* by an optionally signed integer, and converts all this to +* to a floating point number. The first unrecognized +* character ends the string. +* +*Entry: +* nptr - pointer to string to convert +* +*Exit: +* returns floating point value of character representation +* +*Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +double __cdecl _atof_l( + const char *nptr, + _locale_t plocinfo + ) +{ + + struct _flt fltstruct; /* temporary structure */ + _LocaleUpdate _loc_update(plocinfo); + + /* validation section */ + _VALIDATE_RETURN(nptr != NULL, EINVAL, 0.0); + + /* scan past leading space/tab characters */ + + while ( _isspace_l((int)(unsigned char)*nptr, _loc_update.GetLocaleT()) ) + nptr++; + + /* let _fltin routine do the rest of the work */ + + return( *(double *)&(_fltin2( &fltstruct, nptr, _loc_update.GetLocaleT())->dval) ); +} + +double __cdecl atof( + const char *nptr + ) +{ + return _atof_l(nptr, NULL); +} + +unsigned int __strgtold12 +( + _LDBL12 *pld12, + const char * *p_end_ptr, + const char * str, + int mult12, + int scale, + int decpt, + int implicit_E +) +{ + _LocaleUpdate _loc_update(NULL); + + return __strgtold12_l(pld12, p_end_ptr, str, mult12, scale, decpt, implicit_E, _loc_update.GetLocaleT()); +} + +unsigned __STRINGTOLD +( + _LDOUBLE *pld, + const char * *p_end_ptr, + const char *str, + int mult12 +) +{ + _LocaleUpdate _loc_update(NULL); + + return __STRINGTOLD_L(pld, p_end_ptr, str, mult12, _loc_update.GetLocaleT()); +} diff --git a/test_data/lots_of_files/atomic.c b/test_data/lots_of_files/atomic.c new file mode 100644 index 0000000..a21c95c --- /dev/null +++ b/test_data/lots_of_files/atomic.c @@ -0,0 +1,37 @@ +/* atomic.c -- implement shared_ptr spin lock */ +#include <yvals.h> + +#include <intrin.h> + #pragma warning(disable: 4793) + +_STD_BEGIN + /* SPIN LOCK FOR shared_ptr ATOMIC OPERATIONS */ +volatile long _Shared_ptr_flag; + +_CRTIMP2_PURE void __cdecl _Lock_shared_ptr_spin_lock() + { /* spin until _Shared_ptr_flag successfully set */ + #ifdef _M_ARM + while (_InterlockedExchange_acq(&_Shared_ptr_flag, 1)) + __yield(); + #else /* _M_ARM */ + while (_interlockedbittestandset(&_Shared_ptr_flag, 0)) /* set bit 0 */ + ; + #endif /* _M_ARM */ + } + +_CRTIMP2_PURE void __cdecl _Unlock_shared_ptr_spin_lock() + { /* release previously obtained lock */ + #ifdef _M_ARM + __dmb(_ARM_BARRIER_ISH); + __iso_volatile_store32((volatile int *) &_Shared_ptr_flag, 0); + #else /* _M_ARM */ + _interlockedbittestandreset(&_Shared_ptr_flag, 0); /* reset bit 0 */ + #endif /* _M_ARM */ + } + +_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/atonexit.c b/test_data/lots_of_files/atonexit.c new file mode 100644 index 0000000..539daf6 --- /dev/null +++ b/test_data/lots_of_files/atonexit.c @@ -0,0 +1,176 @@ +/*** +*atonexit.c - _onexit/atexit for using the MSVCRT* model of C run-time +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* atexit and _onexit are handled differently for EXE's and DLL's linked +* with MSVCRT.LIB to call MSVCRT*.DLL. Specifically, the _onexit/atexit +* list for DLL's must be maintained on a per-module basis and must be +* processed at DLL process detach . For EXE's the atexit/_onexit list +* should be maintained by MSVCRT*.DLL and processed at process exit. +* +*******************************************************************************/ + +/* + * SPECIAL BUILD MACRO! Note that atonexit.c is linked in with the client's + * code. It does not go into crtdll.dll! Therefore, it must be built under + * the _DLL switch (like user code) and CRTDLL must be undefined. + */ + +#undef CRTDLL +#ifndef _DLL +#define _DLL +#endif /* _DLL */ + +#include <cruntime.h> +#include <oscalls.h> +#include <internal.h> +#include <stdlib.h> +#include <mtdll.h> + +#include <rterr.h> +#include <sect_attribs.h> +#include <dbgint.h> + +int __cdecl __atonexitinit(void); +_CRTALLOC(".CRT$XIC") static _PIFV pinit = __atonexitinit; + +/* + * Pointers to beginning and end of the table of function pointers manipulated + * by _onexit()/atexit(). If this module is an EXE, _onexitbegin will be -1. + * Otherwise _onexitbegin will point to a block of malloc-ed memory used to + * maintain the DLL module's private onexit list of terminator routines. + * NOTE - the pointers are stored encoded. + */ + +_PVFV *__onexitbegin; +_PVFV *__onexitend; + +// This method initializes the onexit variables used by atonexit. +// Returns 0 if successful, or _RT_ONEXIT if there is no memory available +// for the function pointers table. +// Sometimes, __onexitinit is already initializing the onexit variables. +// __atonexitinit is needed for some corner cases +// where no other initialization is done (like for native process under managed). +// This is an alternate fix for performing the same initialization in mstartup.cpp, +// which causes _main symbol to be pulled in even when it shouldn't +// (by referencing extern __onexitbegin/__onexitend), +// causing unresolved symbol at link time. +int __cdecl __atonexitinit ( + void + ) +{ + _PVFV * onexitbegin; + + // Beginning and end of table should be either both initialized + // or both uninitialized. + _ASSERTE((__onexitbegin != NULL && __onexitend != NULL) || + (__onexitbegin == NULL && __onexitend == NULL)); + + // Sometimes, __onexitbegin/__onexitend + // might come already initialized via __onexitinit. + if (__onexitbegin != NULL) { + return 0; + } + + onexitbegin = (_PVFV *) _calloc_crt(32, sizeof(_PVFV)); + __onexitend = __onexitbegin = (_PVFV *) EncodePointer(onexitbegin); + + if ( onexitbegin == NULL ) + /* + * cannot allocate minimal required size. return + * fatal runtime error. + */ + return _RT_ONEXIT; + + *onexitbegin = (_PVFV) NULL; + + return 0; +} + +/*** +*_onexit, atexit - calls to _onexit & atexit in MSVCRT*.DLL +* +*Purpose: +* A DLL linked with MSVCRT.LIB must not call the standard _onexit or +* atexit exported from MSVCRT*.DLL, but an EXE linked with MSVCRT.LIB +* will call the standard versions of those two routines. The standard +* names are not exported from MSVCRT*.DLL, but the _imp__* names are, +* so this module can just invoke the standard versions if linked into +* an EXE module (indicated by __onexitbegin == -1). If this module is +* linked into a DLL (indicated by __onexitbegin != -1), it will call a +* helper routine in MSVCRT*.DLL called __dllonexit to manage the DLL's +* private onexit list. +* +*Entry: +* Same as the regular versions of _onexit, atexit. +* +*Exit: +* Same as the regular versions of _onexit, atexit. +* +*Exceptions: +* +*******************************************************************************/ + +extern _onexit_t __dllonexit(_onexit_t, _PVFV**, _PVFV**); + +#ifdef _M_IX86 +extern _onexit_t (__cdecl *_imp___onexit)(_onexit_t func); +#else /* _M_IX86 */ +extern _onexit_t (__cdecl *__imp__onexit)(_onexit_t func); +#endif /* _M_IX86 */ + +_onexit_t __cdecl _onexit ( + _onexit_t func + ) +{ + _PVFV * onexitbegin; + _PVFV * onexitend; + _onexit_t retval = NULL; + + onexitbegin = (_PVFV *) DecodePointer(__onexitbegin); + + if (onexitbegin == (_PVFV *)-1) + { + /* EXE */ +#ifdef _M_IX86 + return (*_imp___onexit)(func); +#else /* _M_IX86 */ + return (*__imp__onexit)(func); +#endif /* _M_IX86 */ + } + + /* + * Note that we decode/encode the onexit array pointers on the + * client side, not the CRT DLL side, to ease backwards compatibility. + * That means we have to take a lock on this side, making the lock + * found in __dllonexit redundant. + */ + + _lock(_EXIT_LOCK1); + + __try + { + onexitbegin = (_PVFV *) DecodePointer(__onexitbegin); + onexitend = (_PVFV *) DecodePointer(__onexitend); + + retval = __dllonexit((_onexit_t) EncodePointer(func), &onexitbegin, &onexitend); + + __onexitbegin = (_PVFV *) EncodePointer(onexitbegin); + __onexitend = (_PVFV *) EncodePointer(onexitend); + } + __finally + { + _unlock(_EXIT_LOCK1); + } + + return retval; +} + +int __cdecl atexit ( + _PVFV func + ) +{ + return (_onexit((_onexit_t)func) == NULL) ? -1 : 0; +} diff --git a/test_data/lots_of_files/atox.c b/test_data/lots_of_files/atox.c new file mode 100644 index 0000000..370fac3 --- /dev/null +++ b/test_data/lots_of_files/atox.c @@ -0,0 +1,170 @@ +/*** +*atox.c - atoi and atol conversion +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Converts a character string into an int or long. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <mtdll.h> +#include <stdlib.h> +#include <ctype.h> +#include <mbctype.h> +#include <tchar.h> +#include <setlocal.h> +#ifdef _MBCS +#undef _MBCS +#endif /* _MBCS */ +#include <tchar.h> + +/*** +*long atol(char *nptr) - Convert string to long +* +*Purpose: +* Converts ASCII string pointed to by nptr to binary. +* It behaves exactly like strtol(nptr, NULL, 10). +* Please refer to the strtol implementation for details. +* +*Entry: +* nptr = ptr to string to convert +* +* string format: [whitespace] [sign] [digits] +* +*Exit: +* Good return: +* result +* +* Overflow return: +* LONG_MAX or LONG_MIN +* errno == ERANGE +* +* No digits or other error condition return: +* 0 +* +*Exceptions: +* Input parameters are validated. Refer to the strtox function family. +* +*******************************************************************************/ + +long __cdecl _tstol( + const _TCHAR *nptr + ) +{ + return _tcstol(nptr, NULL, 10); +} + + +long __cdecl _tstol_l( + const _TCHAR *nptr, + _locale_t plocinfo + ) +{ + return _tcstol_l(nptr, NULL, 10, plocinfo); +} + + +/*** +*int atoi(char *nptr) - Convert string to int +* +*Purpose: +* Converts ASCII string pointed to by nptr to binary. +* It behaves exactly like (int)strtol(nptr, NULL, 10). +* Please refer to the strtol implementation for details. +* +*Entry: +* nptr = ptr to string to convert +* +*Exit: +* Good return: +* result +* +* Overflow return: +* INT_MAX or INT_MIN +* errno == ERANGE +* +* No digits or other error condition return: +* 0 +* +*Exceptions: +* Input parameters are validated. Refer to the strtox function family. +* +*******************************************************************************/ + +int __cdecl _tstoi( + const _TCHAR *nptr + ) +{ + return (int)_tstol(nptr); +} + +int __cdecl _tstoi_l( + const _TCHAR *nptr, + _locale_t plocinfo + ) +{ + return (int)_tstol_l(nptr, plocinfo); +} + +#ifndef _NO_INT64 + +/*** +*int atoi64(char *nptr) - Convert string to int64 +* +*Purpose: +* Converts ASCII string pointed to by nptr to binary. +* It behaves exactly like (int)strtoi64(nptr, NULL, 10). +* Please refer to the strtoi64 implementation for details. +* +*Entry: +* nptr = ptr to string to convert +* +*Exit: +* Good return: +* result +* +* Overflow return: +* _I64_MAX or _I64_MIN +* errno == ERANGE +* +* No digits or other error condition return: +* 0 +* +*Exceptions: +* Input parameters are validated. Refer to the strtox function family. +* +*******************************************************************************/ + +long long __cdecl _tstoll( + const _TCHAR *nptr + ) +{ + return _tcstoi64(nptr, NULL, 10); +} + +long long __cdecl _tstoll_l( + const _TCHAR *nptr, + _locale_t plocinfo + ) +{ + return _tcstoi64_l(nptr, NULL, 10, plocinfo); +} + +__int64 __cdecl _tstoi64( + const _TCHAR *nptr + ) +{ + return _tcstoi64(nptr, NULL, 10); +} + +__int64 __cdecl _tstoi64_l( + const _TCHAR *nptr, + _locale_t plocinfo + ) +{ + return _tcstoi64_l(nptr, NULL, 10, plocinfo); +} + +#endif /* _NO_INT64 */ diff --git a/test_data/lots_of_files/awint.h b/test_data/lots_of_files/awint.h new file mode 100644 index 0000000..4946a53 --- /dev/null +++ b/test_data/lots_of_files/awint.h @@ -0,0 +1,622 @@ +/*** +*awint.h - internal definitions for A&W Win32 wrapper routines. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Contains internal definitions/declarations for A&W wrapper functions. +* Not included in internal.h since windows.h is required for these. +* +* [Internal] +* +****/ + +#pragma once + +#ifdef _WIN32 + +#ifndef _INC_AWINC +#define _INC_AWINC + +#ifndef _CRTBLD +/* + * This is an internal C runtime header file. It is used when building + * the C runtimes only. It is not to be used as a public header file. + */ +#error ERROR: Use of C runtime library internal header file. +#endif /* _CRTBLD */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include <sal.h> +#include <windows.h> +#include <sdkddkver.h> + +// Minimum supported versions of Windows: +// NTDDI_WINXPSP2 for x86 and x64 +// NTDDI_WIN8 for ARM, CoreSys, App and KernelX + +#if (defined (_M_IX86) || defined (_M_X64)) && !defined (_CORESYS) && !defined (_CRT_APP) && !defined (_KERNELX) +#define _CRT_NTDDI_MIN NTDDI_WINXPSP2 +#else /* (defined (_M_IX86) || defined (_M_X64)) && !defined (_CORESYS) && !defined (_CRT_APP) && !defined (_KERNELX) */ +#define _CRT_NTDDI_MIN NTDDI_WIN8 +#endif /* (defined (_M_IX86) || defined (_M_X64)) && !defined (_CORESYS) && !defined (_CRT_APP) && !defined (_KERNELX) */ + +/* internal A&W routines */ +struct threadlocaleinfostruct; +typedef struct threadlocaleinfostruct * pthreadlocinfo; + +// Fast fail error codes +#define FAST_FAIL_VTGUARD_CHECK_FAILURE 1 +#define FAST_FAIL_STACK_COOKIE_CHECK_FAILURE 2 +#define FAST_FAIL_CORRUPT_LIST_ENTRY 3 +#define FAST_FAIL_INCORRECT_STACK 4 +#define FAST_FAIL_INVALID_ARG 5 +#define FAST_FAIL_GS_COOKIE_INIT 6 +#define FAST_FAIL_FATAL_APP_EXIT 7 +#define FAST_FAIL_RANGE_CHECK_FAILURE 8 + +// Remove when winnt.h has the definition +#ifndef PF_FASTFAIL_AVAILABLE +#define PF_FASTFAIL_AVAILABLE 23 +#endif /* PF_FASTFAIL_AVAILABLE */ + +int __cdecl __crtCompareStringW +( + _In_ LPCWSTR _LocaleName, + _In_ DWORD _DwCmpFlags, + _In_reads_(_CchCount1) LPCWSTR _LpString1, + _In_ int _CchCount1, + _In_reads_(_CchCount2) LPCWSTR _LpString2, + _In_ int _CchCount2 +); + +int __cdecl __crtCompareStringA +( + _In_opt_ _locale_t _Plocinfo, + _In_ LPCWSTR _LocaleName, + _In_ DWORD _DwCmpFlags, + _In_reads_(_CchCount1) LPCSTR _LpString1, + _In_ int _CchCount1, + _In_reads_(_CchCount2) LPCSTR _LpString2, + _In_ int _CchCount2, + _In_ int _Code_page); + +int __cdecl __crtGetLocaleInfoA +( + _In_opt_ _locale_t _Plocinfo, + _In_ LPCWSTR _LocaleName, + _In_ LCTYPE _LCType, + _Out_writes_opt_(_CchData) LPSTR _LpLCData, + _In_ int _CchData); + +int __cdecl __crtLCMapStringW +( + _In_ LPCWSTR _LocaleName, + _In_ DWORD _DWMapFlag, + _In_reads_(_CchSrc) LPCWSTR _LpSrcStr, + _In_ int _CchSrc, + _Out_writes_opt_(_CchDest) LPWSTR _LpDestStr, + _In_ int _CchDest); + +int __cdecl __crtLCMapStringA +( + _In_opt_ _locale_t _Plocinfo, + _In_ LPCWSTR _LocaleName, + _In_ DWORD _DwMapFlag, + _In_reads_(_CchSrc) LPCSTR _LpSrcStr, + _In_ int _CchSrc, + _Out_writes_opt_(_CchDest) LPSTR _LpDestStr, + _In_ int _CchDest, + _In_ int _Code_page, + _In_ BOOL _BError); + +BOOL __cdecl __crtGetStringTypeA +( + _In_opt_ _locale_t _Plocinfo, + _In_ DWORD _DWInfoType, + _In_ LPCSTR _LpSrcStr, + _In_ int _CchSrc, + _Out_ LPWORD _LpCharType, + _In_ int _Code_page, + _In_ BOOL _BError); + +LPVOID __cdecl __crtGetEnvironmentStringsA(VOID); +LPVOID __cdecl __crtGetEnvironmentStringsW(VOID); + +int __cdecl __crtMessageBoxA +( + _In_ LPCSTR _LpText, + _In_ LPCSTR _LpCaption, + _In_ UINT _UType); + +int __cdecl __crtMessageBoxW +( + _In_ LPCWSTR _LpText, + _In_ LPCWSTR _LpCaption, + _In_ UINT _UType); + +/* Helper function for Packaged apps */ +_CRTIMP BOOL __cdecl __crtIsPackagedApp(void); + +_CRTIMP WORD __cdecl __crtGetShowWindowMode(void); + +_CRTIMP void __cdecl __crtSetUnhandledExceptionFilter +( + _In_ LPTOP_LEVEL_EXCEPTION_FILTER exceptionFilter); + +#if defined (_M_IX86) || defined (_M_X64) + +_CRTIMP LONG __cdecl __crtUnhandledException +( + _In_ EXCEPTION_POINTERS *exceptionInfo); + +_CRTIMP void __cdecl __crtTerminateProcess +( + _In_ UINT uExitCode); +#endif /* defined (_M_IX86) || defined (_M_X64) */ + +#if defined (_M_X64) +_CRTIMP void __cdecl __crtCaptureCurrentContext +( + _Out_ CONTEXT *pContextRecord); + +_CRTIMP void __cdecl __crtCapturePreviousContext +( + _Out_ CONTEXT *pContextRecord); +#endif /* defined (_M_X64) */ + +#if _CRT_NTDDI_MIN >= NTDDI_VISTA + +/* Helper functions for thread-level storage Win32 APIs */ +#define __crtFlsAlloc(lpCallback) \ + FlsAlloc(lpCallback) + +#define __crtFlsFree(dwFlsIndex) \ + FlsFree(dwFlsIndex) + +#define __crtFlsGetValue(dwFlsIndex) \ + FlsGetValue(dwFlsIndex) + +#define __crtFlsSetValue(dwFlsIndex,lpFlsData) \ + FlsSetValue(dwFlsIndex,lpFlsData) + +#define __crtInitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, Flags) \ + InitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, Flags) + +#define __crtCreateEventExW(lpEventAttributes, lpName, dwFlags, dwDesiredAccess) \ + CreateEventExW(lpEventAttributes, lpName, dwFlags, dwDesiredAccess) + +#define __crtCreateSemaphoreExW(lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName, dwFlags, dwDesiredAccess) \ + CreateSemaphoreExW(lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName, dwFlags, dwDesiredAccess) + +#define __crtSetThreadStackGuarantee(StackSizeInBytes) \ + SetThreadStackGuarantee(StackSizeInBytes) + +#define __crtCreateThreadpoolTimer(pfnti, pv, pcbe) \ + CreateThreadpoolTimer(pfnti, pv, pcbe) + +#define __crtSetThreadpoolTimer(pti, pftDueTime, msPeriod, msWindowLength) \ + SetThreadpoolTimer(pti, pftDueTime, msPeriod, msWindowLength) + +#define __crtWaitForThreadpoolTimerCallbacks(pti, fCancelPendingCallbacks) \ + WaitForThreadpoolTimerCallbacks(pti, fCancelPendingCallbacks) + +#define __crtCloseThreadpoolTimer(pti) \ + CloseThreadpoolTimer(pti) + +#define __crtCreateThreadpoolWait(pfnwa, pv, pcbe) \ + CreateThreadpoolWait(pfnwa, pv, pcbe) + +#define __crtSetThreadpoolWait(pwa, h, pftTimeout) \ + SetThreadpoolWait(pwa, h, pftTimeout) + +#define __crtCloseThreadpoolWait(pwa) \ + CloseThreadpoolWait(pwa) + +// wrapper for FlushProcessWriteBuffers, available only on Vista+ +#define __crtFlushProcessWriteBuffers() \ + FlushProcessWriteBuffers() + +// wrapper for FreeLibraryWhenCallbackReturns, available only on Vista+ +#define __crtFreeLibraryWhenCallbackReturns(pci, mod) \ + FreeLibraryWhenCallbackReturns(pci, mod) + +// wrapper for GetCurrentProcessorNumber, available only on Vista+ +#define __crtGetCurrentProcessorNumber() \ + GetCurrentProcessorNumber() + +// wrapper for GetLogicalProcessorInformation, available only on Vista+ +#define __crtGetLogicalProcessorInformation(Buffer, ReturnLength) \ + GetLogicalProcessorInformation(Buffer, ReturnLength) + +// wrapper for CreateSymbolicLink, available only on Vista+ +#define __crtCreateSymbolicLinkW(lpSymlinkFileName, lpTargetFileName, dwFlags) \ + CreateSymbolicLinkW(lpSymlinkFileName, lpTargetFileName, dwFlags) + +#define __crtCompareStringEx(lpLocaleName, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2) \ + CompareStringEx(lpLocaleName, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, NULL, NULL, 0) + +#define __crtEnumSystemLocalesEx(lpLocaleEnumProcEx, dwFlags, lParam) \ + EnumSystemLocalesEx(lpLocaleEnumProcEx, dwFlags, lParam, NULL) + +#define __crtGetDateFormatEx(lpLocaleName, dwFlags, lpDate, lpFormat, lpDateStr, cchDate) \ + GetDateFormatEx(lpLocaleName, dwFlags, lpDate, lpFormat, lpDateStr, cchDate, NULL) + +#define __crtGetLocaleInfoEx(lpLocaleName, LCType, lpLCData, cchData) \ + GetLocaleInfoEx(lpLocaleName, LCType, lpLCData, cchData) + +#define __crtGetTimeFormatEx(lpLocaleName, dwFlags, lpTime, lpFormat, lpTimeStr, cchTime) \ + GetTimeFormatEx(lpLocaleName, dwFlags, lpTime, lpFormat, lpTimeStr, cchTime) + +#define __crtGetUserDefaultLocaleName(lpLocaleName, cchLocaleName) \ + GetUserDefaultLocaleName(lpLocaleName, cchLocaleName) + +#define __crtIsValidLocaleName(lpLocaleName) \ + IsValidLocaleName(lpLocaleName) + +#define __crtLCMapStringEx(lpLocaleName, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest) \ + LCMapStringEx(lpLocaleName, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest, NULL, NULL, 0) + +#define __crtGetFileInformationByHandleEx(hFile, FileInformationClass, lpFileInformation, dwBufferSize) \ + GetFileInformationByHandleEx(hFile, FileInformationClass, lpFileInformation, dwBufferSize) + +#define __crtSetFileInformationByHandle(hFile, FileInformationClass, lpFileInformation, dwBufferSize) \ + SetFileInformationByHandle(hFile, FileInformationClass, lpFileInformation, dwBufferSize) + +#define __crtGetTickCount64() \ + GetTickCount64() + +#else /* _CRT_NTDDI_MIN >= NTDDI_VISTA */ + +/* Downlevel NLS locale name <==> LCID conversion */ +LCID __cdecl __crtDownlevelLocaleNameToLCID( + LPCWSTR localeName); + +int __cdecl __crtDownlevelLCIDToLocaleName( + LCID lcid, + LPWSTR outLocaleName, + int cchLocaleName); + +_CRTIMP DWORD __cdecl __crtFlsAlloc( + __in PFLS_CALLBACK_FUNCTION lpCallback); + +_CRTIMP BOOL __cdecl __crtFlsFree( + __in DWORD dwFlsIndex); + +_CRTIMP PVOID __cdecl __crtFlsGetValue( + __in DWORD dwFlsIndex); + +_CRTIMP BOOL __cdecl __crtFlsSetValue( + __in DWORD dwFlsIndex, + __in_opt PVOID lpFlsData); + +// wrapper for InitializeCriticalSectionEx, available only on Vista+ +_CRTIMP BOOL __cdecl __crtInitializeCriticalSectionEx( + __out LPCRITICAL_SECTION lpCriticalSection, + __in DWORD dwSpinCount, + __in DWORD Flags); + +// wrapper for CreateEventExW, available only on Vista+ +_CRTIMP HANDLE __cdecl __crtCreateEventExW( + __in_opt LPSECURITY_ATTRIBUTES lpEventAttributes, + __in_opt LPCWSTR lpName, + __reserved DWORD dwFlags, + __in DWORD dwDesiredAccess); + +// wrapper for CreateSemaphoreExW, available only on Vista+ +_CRTIMP HANDLE __cdecl __crtCreateSemaphoreExW( + __in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, + __in LONG lInitialCount, + __in LONG lMaximumCount, + __in_opt LPCWSTR lpName, + __reserved DWORD dwFlags, + __in DWORD dwDesiredAccess); + +// wrapper for SetThreadStackGuarantee, available only on Vista+ +_CRTIMP BOOL __cdecl __crtSetThreadStackGuarantee( + PULONG StackSizeInBytes); + +// wrappers for thread pool APIs, available only on Vista+ +PTP_TIMER __cdecl __crtCreateThreadpoolTimer( + __in PTP_TIMER_CALLBACK pfnti, + __inout_opt PVOID pv, + __in_opt PTP_CALLBACK_ENVIRON pcbe); + +VOID __cdecl __crtSetThreadpoolTimer( + __inout PTP_TIMER pti, + __in_opt PFILETIME pftDueTime, + __in DWORD msPeriod, + __in_opt DWORD msWindowLength); + +VOID __cdecl __crtWaitForThreadpoolTimerCallbacks( + __inout PTP_TIMER pti, + __in BOOL fCancelPendingCallbacks); + +VOID __cdecl __crtCloseThreadpoolTimer( + __inout PTP_TIMER pti); + +PTP_WAIT __cdecl __crtCreateThreadpoolWait( + __in PTP_WAIT_CALLBACK pfnwa, + __inout_opt PVOID pv, + __in_opt PTP_CALLBACK_ENVIRON pcbe); + +VOID __cdecl __crtSetThreadpoolWait( + __inout PTP_WAIT pwa, + __in_opt HANDLE h, + __in_opt PFILETIME pftTimeout); + +VOID __cdecl __crtCloseThreadpoolWait( + __inout PTP_WAIT pwa); + +// wrapper for FlushProcessWriteBuffers, available only on Vista+ +VOID __cdecl __crtFlushProcessWriteBuffers(void); + +// wrapper for FreeLibraryWhenCallbackReturns, available only on Vista+ +VOID __cdecl __crtFreeLibraryWhenCallbackReturns( + __inout PTP_CALLBACK_INSTANCE pci, + __in HMODULE mod); + +// wrapper for GetCurrentProcessorNumber, available only on Vista+ +DWORD __cdecl __crtGetCurrentProcessorNumber(void); + +// wrapper for GetLogicalProcessorInformation, available only on Vista+ +BOOL __cdecl __crtGetLogicalProcessorInformation( + __out PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer, + __inout PDWORD ReturnLength); + +// wrapper for CreateSymbolicLink, available only on Vista+ +_CRTIMP BOOLEAN __cdecl __crtCreateSymbolicLinkW( + __in LPCWSTR lpSymlinkFileName, + __in LPCWSTR lpTargetFileName, + __in DWORD dwFlags); + +/* Helper functions for NLS-specific Win32 APIs */ +_CRTIMP int __cdecl __crtCompareStringEx( + __in_opt LPCWSTR lpLocaleName, + __in DWORD dwCmpFlags, + __in LPCWSTR lpString1, + __in int cchCount1, + __in LPCWSTR lpString2, + __in int cchCount2); + +_CRTIMP BOOL __cdecl __crtEnumSystemLocalesEx( + __in LOCALE_ENUMPROCEX lpLocaleEnumProcEx, + __in DWORD dwFlags, + __in LPARAM lParam); + +_CRTIMP int __cdecl __crtGetDateFormatEx( + __in_opt LPCWSTR lpLocaleName, + __in DWORD dwFlags, + __in_opt const SYSTEMTIME *lpDate, + __in_opt LPCWSTR lpFormat, + __out_opt LPWSTR lpDateStr, + __in int cchDate); + +_CRTIMP int __cdecl __crtGetLocaleInfoEx( + __in_opt LPCWSTR lpLocaleName, + __in LCTYPE LCType, + __out_opt LPWSTR lpLCData, + __in int cchData); + +_CRTIMP int __cdecl __crtGetTimeFormatEx( + __in_opt LPCWSTR lpLocaleName, + __in DWORD dwFlags, + __in_opt const SYSTEMTIME *lpTime, + __in_opt LPCWSTR lpFormat, + __out_opt LPWSTR lpTimeStr, + __in int cchTime); + +_CRTIMP int __cdecl __crtGetUserDefaultLocaleName( + __out LPWSTR lpLocaleName, + __in int cchLocaleName); + +_CRTIMP BOOL __cdecl __crtIsValidLocaleName( + __in LPCWSTR lpLocaleName); + +_CRTIMP int __cdecl __crtLCMapStringEx( + __in_opt LPCWSTR lpLocaleName, + __in DWORD dwMapFlags, + __in LPCWSTR lpSrcStr, + __in int cchSrc, + __out_opt LPWSTR lpDestStr, + __in int cchDest); + +// wrapper for GetFileInformationByHandleEx, available only on Vista+ +_CRTIMP BOOL __cdecl __crtGetFileInformationByHandleEx( + _In_ HANDLE hFile, + _In_ FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + _Out_ LPVOID lpFileInformation, + _In_ DWORD dwBufferSize); + +// wrapper for SetFileInformationByHandle, available only on Vista+ +_CRTIMP BOOL __cdecl __crtSetFileInformationByHandle( + _In_ HANDLE hFile, + _In_ FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + _In_ LPVOID lpFileInformation, + _In_ DWORD dwBufferSize); + +// wrapper function for GetTickCount64, available only on Vista+ +_CRTIMP ULONGLONG __cdecl __crtGetTickCount64(); + +#endif /* _CRT_NTDDI_MIN >= NTDDI_VISTA */ + +/* Helper function for Sleep */ +_CRTIMP void __cdecl __crtSleep(_In_ DWORD dwMilliseconds); + +// Helper to load all necessary Win32 API function pointers +void __cdecl __crtLoadWinApiPointers(void); + +enum wrapEncodedKERNEL32Functions +{ + eFlsAlloc = 0, + eFlsFree, + eFlsGetValue, + eFlsSetValue, + eInitializeCriticalSectionEx, + eCreateEventExW, + eCreateSemaphoreExW, + eSetThreadStackGuarantee, + eCreateThreadpoolTimer, + eSetThreadpoolTimer, + eWaitForThreadpoolTimerCallbacks, + eCloseThreadpoolTimer, + eCreateThreadpoolWait, + eSetThreadpoolWait, + eCloseThreadpoolWait, + eFlushProcessWriteBuffers, + eFreeLibraryWhenCallbackReturns, + eGetCurrentProcessorNumber, + eGetLogicalProcessorInformation, + eCreateSymbolicLinkW, + eSetDefaultDllDirectories, + eCompareStringEx, + eEnumSystemLocalesEx, + eGetDateFormatEx, + eGetLocaleInfoEx, + eGetTimeFormatEx, + eGetUserDefaultLocaleName, + eIsValidLocaleName, + eLCMapStringEx, + eGetCurrentPackageId, + eGetTickCount64, + eGetFileInformationByHandleExW, + eSetFileInformationByHandleW, + eMaxKernel32Function +}; + +extern PVOID encodedKERNEL32Functions[eMaxKernel32Function]; + +/* Needed for pointer encoding */ +extern UINT_PTR __security_cookie; + +typedef DWORD (WINAPI *PFNFLSALLOC)(PFLS_CALLBACK_FUNCTION); +typedef BOOL (WINAPI *PFNFLSFREE)(DWORD); +typedef PVOID (WINAPI *PFNFLSGETVALUE)(DWORD); +typedef BOOL (WINAPI *PFNFLSSETVALUE)(DWORD, PVOID); +typedef BOOL (WINAPI *PFNINITIALIZECRITICALSECTIONEX)(LPCRITICAL_SECTION, DWORD, DWORD); +typedef HANDLE (WINAPI *PFNCREATEEVENTEXW)(LPSECURITY_ATTRIBUTES, LPCWSTR, DWORD, DWORD); +typedef HANDLE (WINAPI *PFNCREATESEMAPHOREEXW)(LPSECURITY_ATTRIBUTES, LONG, LONG, LPCWSTR, DWORD, DWORD); +typedef BOOL (WINAPI *PFNSETTHREADSTACKGUARANTEE)(PULONG); +typedef PTP_TIMER (WINAPI *PFNCREATETHREADPOOLTIMER)(PTP_TIMER_CALLBACK, PVOID, PTP_CALLBACK_ENVIRON); +typedef VOID (WINAPI *PFNSETTHREADPOOLTIMER)(PTP_TIMER, PFILETIME, DWORD, DWORD); +typedef VOID (WINAPI *PFNWAITFORTHREADPOOLTIMERCALLBACKS)(PTP_TIMER, BOOL); +typedef VOID (WINAPI *PFNCLOSETHREADPOOLTIMER)(PTP_TIMER); +typedef PTP_WAIT (WINAPI *PFNCREATETHREADPOOLWAIT)(PTP_WAIT_CALLBACK, PVOID, PTP_CALLBACK_ENVIRON); +typedef VOID (WINAPI *PFNSETTHREADPOOLWAIT)(PTP_WAIT, HANDLE, PFILETIME); +typedef VOID (WINAPI *PFNCLOSETHREADPOOLWAIT)(PTP_WAIT); +typedef VOID (WINAPI *PFNFLUSHPROCESSWRITEBUFFERS)(void); +typedef VOID (WINAPI *PFNFREELIBRARYWHENCALLBACKRETURNS)(PTP_CALLBACK_INSTANCE, HMODULE); +typedef DWORD (WINAPI *PFNGETCURRENTPROCESSORNUMBER)(void); +typedef BOOL (WINAPI *PFNGETLOGICALPROCESSORINFORMATION)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); +typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINK)(LPCWSTR, LPCWSTR, DWORD); +typedef BOOL (WINAPI *PFNENUMSYSTEMLOCALESEX)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID); +typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWSTR, int, LPCWSTR, int, LPNLSVERSIONINFO, LPVOID, LPARAM); +typedef int (WINAPI *PFNGETDATEFORMATEX)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, int, LPCWSTR); +typedef int (WINAPI *PFNGETLOCALEINFOEX)(LPCWSTR, LCTYPE, LPWSTR, int); +typedef int (WINAPI *PFNGETTIMEFORMATEX)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, int); +typedef int (WINAPI *PFNGETUSERDEFAULTLOCALENAME)(LPWSTR, int); +typedef BOOL (WINAPI *PFNISVALIDLOCALENAME)(LPCWSTR); +typedef int (WINAPI *PFNLCMAPSTRINGEX)(LPCWSTR, DWORD, LPCWSTR, int, LPWSTR, int, LPNLSVERSIONINFO, LPVOID, LPARAM); +typedef ULONGLONG (WINAPI *PFNGETTICKCOUNT64)(void); +typedef BOOL (WINAPI *PFNGETFILEINFORMATIONBYHANDLEEX)(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); +typedef BOOL (WINAPI *PFNSETFILEINFORMATIONBYHANDLE)(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); + +// Use this macro for encoding and caching a function pointer from a DLL +#define STOREENCODEDFUNCTIONPOINTER(instance, libraryname, functionname) \ + encoded##libraryname##Functions[e##functionname] = (PVOID)(((UINT_PTR)GetProcAddress(instance, #functionname)) ^ __security_cookie); + +// Use this macro for decoding a cached function pointer from a DLL +#define IFDYNAMICGETCACHEDFUNCTION(libraryname, functiontypedef, functionname, functionpointer) \ + functiontypedef functionpointer = (functiontypedef)(((UINT_PTR)encoded##libraryname##Functions[e##functionname]) ^ __security_cookie); \ + if (functionpointer != NULL) + + +#ifdef _CRT_APP +/* + * Helper functions to work with WinRT threadpool threads + */ +HANDLE __cdecl __crtCreateWinRTThread( + _In_ LPTHREAD_START_ROUTINE lpStartAddress, + _In_opt_ LPVOID lpParameter, + __out_opt LPDWORD lpThreadId +); + +BOOL __cdecl __crtCloseWinRTThreadHandle(_In_opt_ HANDLE hThread); +HANDLE __cdecl __crtGetCurrentWinRTThread(void); +DWORD __cdecl __crtGetCurrentWinRTThreadId(void); +BOOL __cdecl __crtGetExitCodeWinRTThread(_In_ HANDLE hThread, _Out_ DWORD *pdwExitCode); +HANDLE __cdecl __crtGetWinRTThreadWaitableHandle(_In_ HANDLE hThread); +DWORD __cdecl __crtGetWinRTThreadId(_In_ HANDLE hThread); +BOOL __cdecl __crtSetExitCodeWinRTThread(_In_ HANDLE hThread, _In_ DWORD wExitCode); +DWORD __cdecl __crtWaitForWinRTThreadExit(_In_ HANDLE hThread, _In_ DWORD timeout); + +/* + * Helper functions to work with WinRT threadpool timers + */ + +/// <summary> +/// Create a timer +/// </summary> +/// <param name="lpStartAddress"> +/// [in] Pointer to the user function. +/// </param> +/// <param name="lpParameter"> +/// [in] Pointer to the user parameter. +/// </param> +/// <param name="duration"> +/// [in] The delay or period for the timer. +/// </param> +/// <param name="periodic"> +/// [in] Indicates that the timer to be created is periodic. +/// </param> +/// <returns> +/// A Handle to the timer +/// </returns> +HANDLE __cdecl __crtCreateWinRTTimer( + _In_ WAITORTIMERCALLBACK lpStartAddress, + _In_opt_ PVOID lpParameter, + _In_ unsigned int duration, + _In_ BOOL periodic +); + +/// <summary> +/// Deletes the given timer +/// </summary> +/// <param name="hTimer"> +/// [in] Handle to the timer. +/// </param> +/// <param name="waitForHandlers"> +/// [in] Indicates that the routine should wait for all outstanding callbacks to complete +/// </param> +void __cdecl __crtDeleteWinRTTimer(_In_opt_ HANDLE hTimer, _In_ BOOL waitForHandlers); + +/// <summary> +/// Cancels the given timer +/// </summary> +/// <param name="hTimer"> +/// [in] Handle to the timer. +/// </param> +/// <param name="waitForHandlers"> +/// [in] Indicates that the routine should wait for all outstanding callbacks to complete +/// </param> +void __cdecl __crtCancelWinRTTimer(_In_opt_ HANDLE hTimer, _In_ BOOL waitForHandlers); + + +void __cdecl __crtExitProcessWinRT(void); +DWORD __cdecl __crtGetFullPathNameWinRTW(const wchar_t* fileName, DWORD bufferLength, wchar_t* buffer); +DWORD __cdecl __crtGetFullPathNameWinRTA(const char* fileName, DWORD bufferLength, char* buffer); +DWORD __cdecl __crtGetTempPathWinRTW(unsigned int maxBuff, wchar_t* tempPathBuffer); +DWORD __cdecl __crtGetTempPathWinRTA(unsigned int maxBuff, char* tempPathBuffer); + +#endif /* _CRT_APP */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_AWINC */ + +#endif /* _WIN32 */ diff --git a/test_data/lots_of_files/backup.bat b/test_data/lots_of_files/backup.bat new file mode 100644 index 0000000..bd186bf --- /dev/null +++ b/test_data/lots_of_files/backup.bat @@ -0,0 +1,29 @@ +@echo off + +if not exist backup (mkdir backup) + +set DT=%DATE%_%TIME% +set DT=%DT:/=.% +set DT=%DT::=.% +set DEST=backup\%DT% + +mkdir %DEST% +copy %1\* %DEST%\* + + +REM Thanks to stackoverflow.com ~ dbenham for +REM this batch script. + +set MAXBACKUPS=%2 + +set "delMsg=" +for /f "skip=%MAXBACKUPS% delims=" %%a in ( + 'dir "backup\*" /t:c /a:d /o:-d /b' +) do ( + if not defined delMsg ( + set delMsg=1 + echo More than %MAXBACKUPS% found - only the %MAXBACKUPS% most recent folders will be preserved. + ) + rd /s /q "backup\%%a" +) + diff --git a/test_data/lots_of_files/binmode.c b/test_data/lots_of_files/binmode.c new file mode 100644 index 0000000..df1cf88 --- /dev/null +++ b/test_data/lots_of_files/binmode.c @@ -0,0 +1,20 @@ +/*** +*binmode.c - set global file mode to binary +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Sets the global file mode flag to binary. Linking with this file +* sets all files to be opened in binary mode. +* +*******************************************************************************/ + +#define SPECIAL_CRTEXE + +#include <cruntime.h> +#include <fcntl.h> +#include <stdlib.h> + +/* set default file mode */ +int _fmode = _O_BINARY; + diff --git a/test_data/lots_of_files/bsearch.c b/test_data/lots_of_files/bsearch.c new file mode 100644 index 0000000..b3be268 --- /dev/null +++ b/test_data/lots_of_files/bsearch.c @@ -0,0 +1,122 @@ +/*** +*bsearch.c - do a binary search +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines bsearch() - do a binary search an an array +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> +#include <search.h> +#include <internal.h> + +#if defined (_M_CEE) +#define __fileDECL __clrcall +#else /* defined (_M_CEE) */ +#define __fileDECL __cdecl +#endif /* defined (_M_CEE) */ +/*** +*char *bsearch() - do a binary search on an array +* +*Purpose: +* Does a binary search of a sorted array for a key. +* +*Entry: +* const char *key - key to search for +* const char *base - base of sorted array to search +* unsigned int num - number of elements in array +* unsigned int width - number of bytes per element +* int (*compare)() - pointer to function that compares two array +* elements, returning neg when #1 < #2, pos when #1 > #2, and +* 0 when they are equal. Function is passed pointers to two +* array elements. +* +*Exit: +* if key is found: +* returns pointer to occurrence of key in array +* if key is not found: +* returns NULL +* +*Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +#ifdef __USE_CONTEXT +#define __COMPARE(context, p1, p2) (*compare)(context, p1, p2) +#else /* __USE_CONTEXT */ +#define __COMPARE(context, p1, p2) (*compare)(p1, p2) +#endif /* __USE_CONTEXT */ + +#if !defined (_M_CEE) +_CRTIMP +#endif /* !defined (_M_CEE) */ + +SECURITYSAFECRITICAL_ATTRIBUTE +#ifdef __USE_CONTEXT +void * __fileDECL bsearch_s ( + const void *key, + const void *base, + size_t num, + size_t width, + int (__fileDECL *compare)(void *, const void *, const void *), + void *context + ) +#else /* __USE_CONTEXT */ +void * __fileDECL bsearch ( + const void *key, + const void *base, + size_t num, + size_t width, + int (__fileDECL *compare)(const void *, const void *) + ) +#endif /* __USE_CONTEXT */ +{ + char *lo = (char *)base; + char *hi = (char *)base + (num - 1) * width; + char *mid; + size_t half; + int result; + + /* validation section */ + _VALIDATE_RETURN(base != NULL || num == 0, EINVAL, NULL); + _VALIDATE_RETURN(width > 0, EINVAL, NULL); + _VALIDATE_RETURN(compare != NULL, EINVAL, NULL); + + /* + We allow a NULL key here because it breaks some older code and because we do not dereference + this ourselves so we can't be sure that it's a problem for the comparison function + */ + + while (lo <= hi) + { + if ((half = num / 2) != 0) + { + mid = lo + (num & 1 ? half : (half - 1)) * width; + if (!(result = __COMPARE(context, key, mid))) + return(mid); + else if (result < 0) + { + hi = mid - width; + num = num & 1 ? half : half-1; + } + else + { + lo = mid + width; + num = half; + } + } + else if (num) + return (__COMPARE(context, key, lo) ? NULL : lo); + else + break; + } + + return NULL; +} + +#undef __fileDECL +#undef __COMPARE diff --git a/test_data/lots_of_files/bsearch_s.c b/test_data/lots_of_files/bsearch_s.c new file mode 100644 index 0000000..783bf88 --- /dev/null +++ b/test_data/lots_of_files/bsearch_s.c @@ -0,0 +1,45 @@ +/*** +*bsearch_s.c - do a binary search +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines bsearch_s() - do a binary search an an array +* +*******************************************************************************/ + +/*** +*char *bsearch_s() - do a binary search on an array +* +*Purpose: +* Does a binary search of a sorted array for a key. +* +*Entry: +* const char *key - key to search for +* const char *base - base of sorted array to search +* unsigned int num - number of elements in array +* unsigned int width - number of bytes per element +* int (*compare)() - pointer to function that compares two array +* elements, returning neg when #1 < #2, pos when #1 > #2, and +* 0 when they are equal. Function is passed pointers to two +* array elements, together with a pointer to a context. +* void *context - pointer to the context in which the function is +* called. This context is passed to the comparison function. +* +*Exit: +* if key is found: +* returns pointer to occurrence of key in array +* if key is not found: +* returns NULL +* +*Exceptions: +* Input parameters are validated. Refer to the validation section of the function. +* +*******************************************************************************/ + +#ifdef __USE_CONTEXT +#error __USE_CONTEXT should be undefined +#endif /* __USE_CONTEXT */ + +#define __USE_CONTEXT +#include "bsearch.c" diff --git a/test_data/lots_of_files/bswap.c b/test_data/lots_of_files/bswap.c new file mode 100644 index 0000000..f93407a --- /dev/null +++ b/test_data/lots_of_files/bswap.c @@ -0,0 +1,65 @@ +/*** +*rotl.c - rotate an unsigned integer left +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _byteswap() - performs a byteswap on an unsigned integer. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> + +#pragma function(_byteswap_ulong, _byteswap_uint64, _byteswap_ushort) + +/*** +*unsigned long _byteswap_ulong(i) - long byteswap +* +*Purpose: +* Performs a byte swap on an unsigned integer. +* +*Entry: +* unsigned long i: value to swap +* +*Exit: +* returns swaped +* +*Exceptions: +* None. +* +*******************************************************************************/ + + +unsigned long __cdecl _byteswap_ulong(unsigned long i) +{ + unsigned int j; + j = (i << 24); + j += (i << 8) & 0x00FF0000; + j += (i >> 8) & 0x0000FF00; + j += (i >> 24); + return j; +} + +unsigned short __cdecl _byteswap_ushort(unsigned short i) +{ + unsigned short j; + j = (i << 8) ; + j += (i >> 8) ; + return j; +} + +unsigned __int64 __cdecl _byteswap_uint64(unsigned __int64 i) +{ + unsigned __int64 j; + j = (i << 56); + j += (i << 40)&0x00FF000000000000; + j += (i << 24)&0x0000FF0000000000; + j += (i << 8)&0x000000FF00000000; + j += (i >> 8)&0x00000000FF000000; + j += (i >> 24)&0x0000000000FF0000; + j += (i >> 40)&0x000000000000FF00; + j += (i >> 56); + return j; + +} diff --git a/test_data/lots_of_files/build.bat b/test_data/lots_of_files/build.bat new file mode 100644 index 0000000..8729da4 --- /dev/null +++ b/test_data/lots_of_files/build.bat @@ -0,0 +1,5 @@ +@echo off + +build_tools /Zi +REM build_all /Zi /DDEBUG /DDEVELOPER +REM build_all /O2 \ No newline at end of file diff --git a/test_data/lots_of_files/build_all.bat b/test_data/lots_of_files/build_all.bat new file mode 100644 index 0000000..384bcc8 --- /dev/null +++ b/test_data/lots_of_files/build_all.bat @@ -0,0 +1,45 @@ +@echo off + +SET NoCTime=0 +WHERE ctime >nul 2>nul +IF %ERRORLEVEL% NEQ 0 (SET NoCTime=1) + +IF %NoCTime% NEQ 1 (call "ctime" -begin shmup.ctm) + +set FirstError=0 + +IF NOT "%clset%"=="64" call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64 +set clset = 64 + +set WARNINGOPS=/W4 /wd4310 /wd4100 /wd4201 /wd4505 /wd4996 /wd4127 /wd4510 /wd4512 /wd4610 /wd4390 /WX +set WARNINGOPS=%WARNINGOPS% /wd4213 /wd4057 +set WARNINGOPS=%WARNINGOPS% /GR- /EHa- /nologo /FC +set WIN_LIBS=user32.lib winmm.lib gdi32.lib +set WIN_LIBS_DD=Ole32.lib Shell32.lib +set GL_LIBS=opengl32.lib +set USE_F=-I..\code\foreign +set BUILD_DLL=/LD /link /OPT:REF + +IF NOT EXIST ..\build mkdir ..\build + +copy ..\code\backup.bat ..\build\backup.bat > NUL + +pushd ..\build + +REM remove pdbs since we use randomly generated pdb names +del *.pdb > NUL 2> NUL + +set EXPORTS=/EXPORT:target_get_functions /EXPORT:bank_get_functions +cl %WARNINGOPS% %USE_F% %WIN_LIBS% %GL_LIBS% ..\code\cd_tyler_comp_unit.cpp /FeCDRenderer.dll %* %BUILD_DLL% /INCREMENTAL:NO %EXPORTS% /PDB:cd_rend_%random%.pdb +IF %ERRORLEVEL% NEQ 0 (set FirstError=1) + +set EXPORTS=/EXPORT:app_step +cl %WARNINGOPS% %USE_F% %GL_LIBS% ..\code\cd_app_comp_unit.cpp /FeCDGame.dll %* %BUILD_DLL% /INCREMENTAL:NO %EXPORTS% /PDB:cd_game_%random%.pdb +IF %ERRORLEVEL% NEQ 0 (set FirstError=1) + +cl %WARNINGOPS% %USE_F% %WIN_LIBS% %WIN_LIBS_DD% %GL_LIBS% lualib.lib ..\code\cd_comp_unit.cpp ..\code\foreign\DragAndDrop.c /FeCipherDrive %* /link /INCREMENTAL:NO /PDB:cd_plat_%random%.pdb +IF %ERRORLEVEL% NEQ 0 (set FirstError=1) +popd + +IF %NoCTime% NEQ 1 (call "ctime" -end shmup.ctm %FirstError%) + diff --git a/test_data/lots_of_files/build_lua.bat b/test_data/lots_of_files/build_lua.bat new file mode 100644 index 0000000..5e5aa7e --- /dev/null +++ b/test_data/lots_of_files/build_lua.bat @@ -0,0 +1,11 @@ +@echo off + +IF NOT "%clset%"=="64" call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64 +set clset = 64 + +set WARNINGOPS=/GR- /EHa- /nologo /FC + +pushd ..\build +cl %WARNINGOPS% -c -Zi ..\code\foreign\allen_lua_build_target.c +lib /nologo allen_lua_build_target.obj -OUT:lualib.lib +popd \ No newline at end of file diff --git a/test_data/lots_of_files/build_png_to_bmp.bat b/test_data/lots_of_files/build_png_to_bmp.bat new file mode 100644 index 0000000..778ac44 --- /dev/null +++ b/test_data/lots_of_files/build_png_to_bmp.bat @@ -0,0 +1,10 @@ +@echo off + +set WARNINGOPS=/W4 /wd4310 /wd4100 /wd4201 /wd4505 /wd4996 /wd4127 /wd4510 /wd4512 /wd4610 /wd4390 /WX +set WARNINGOPS=%WARNINGOPS% /GR- /EHa- /nologo /FC +set WIN_LIBS=user32.lib winmm.lib gdi32.lib opengl32.lib +set USE_F=-I..\code\foreign + +pushd ..\utils +cl %WARNINGOPS% %USE_F% ..\code\util\png_to_rgba_bmp.c /Zi /Fepng_to_bmp +popd diff --git a/test_data/lots_of_files/build_release.bat b/test_data/lots_of_files/build_release.bat new file mode 100644 index 0000000..7589691 --- /dev/null +++ b/test_data/lots_of_files/build_release.bat @@ -0,0 +1,3 @@ +@echo off + +build_all /O2 /DNDEBUG \ No newline at end of file diff --git a/test_data/lots_of_files/build_tools.bat b/test_data/lots_of_files/build_tools.bat new file mode 100644 index 0000000..318c60f --- /dev/null +++ b/test_data/lots_of_files/build_tools.bat @@ -0,0 +1,26 @@ +@echo off + +SET NoCTime=0 +WHERE ctime >nul 2>nul +IF %ERRORLEVEL% NEQ 0 (SET NoCTime=1) + +IF %NoCTime% NEQ 1 (call "ctime" -begin shmup.ctm) + +set FirstError=0 + +IF NOT "%clset%"=="64" call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64 +set clset = 64 + +set WARNINGOPS=/W4 /wd4310 /wd4100 /wd4201 /wd4505 /wd4996 /wd4127 /wd4510 /wd4512 /wd4610 /wd4390 /WX +set WARNINGOPS=%WARNINGOPS% /wd4213 /wd4057 +set WARNINGOPS=%WARNINGOPS% /GR- /EHa- /nologo /FC +set USE_F=-I..\code\foreign + +IF NOT EXIST ..\meta mkdir ..\meta + +pushd ..\meta +cl %WARNINGOPS% %USE_F% ..\code\tool_ttf_to_bmp.c /Fettf_to_bmp %* +IF %ERRORLEVEL% NEQ 0 (set FirstError=1) +popd + +IF %NoCTime% NEQ 1 (call "ctime" -end shmup.ctm %FirstError%) diff --git a/test_data/lots_of_files/cache.c b/test_data/lots_of_files/cache.c new file mode 100644 index 0000000..5a271af --- /dev/null +++ b/test_data/lots_of_files/cache.c @@ -0,0 +1,417 @@ + +/* + * Instruction-level simulator for the LC + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +enum actionType + {cacheToProcessor, processorToCache, memoryToCache, cacheToMemory, + cacheToNowhere}; +/* + * Log the specifics of each cache action. + * + * address is the starting word address of the range of data being transferred. + * size is the size of the range of data being transferred. + * type specifies the source and destination of the data being transferred. + * cacheToProcessor: reading data from the cache to the processor + * processorToCache: writing data from the processor to the cache + * memoryToCache: reading data from the memory to the cache + * cacheToMemory: evicting cache data by writing it to the memory + * cacheToNowhere: evicting cache data by throwing it away + */ +void +printAction(int address, int size, enum actionType type) +{ + printf("@@@ transferring word [%d-%d] ", address, address + size - 1); + if (type == cacheToProcessor) { + printf("from the cache to the processor\n"); + } else if (type == processorToCache) { + printf("from the processor to the cache\n"); + } else if (type == memoryToCache) { + printf("from the memory to the cache\n"); + } else if (type == cacheToMemory) { + printf("from the cache to the memory\n"); + } else if (type == cacheToNowhere) { + printf("from the cache to nowhere\n"); + } +} + +#define NUMMEMORY 65536 /* maximum number of words in memory */ +#define NUMREGS 8 /* number of machine registers */ +#define MAXLINELENGTH 1000 + +#define ADD 0 +#define NAND 1 +#define LW 2 +#define SW 3 +#define BEQ 4 +#define JALR 5 +#define HALT 6 +#define NOOP 7 + +typedef struct stateStruct { + int pc; + int mem_[NUMMEMORY]; + int reg[NUMREGS]; + int numMemory; +} stateType; + +typedef struct cacheStruct { + int blockSizeInWords; + int numberOfSets; + int blocksPerSet; + int *lruOrder; + int *marks; + + int blockOffsetMask; + int blockOffsetShift; + int setIndexMask; + int setIndexShift; + int tagMask; + int tagShift; +} cacheType; + +#define MarkValid 0x80000000 +#define MarkDirty 0x40000000 +#define MarkTag 0x3FFFFFFF + +void printState(stateType *); +void run(stateType, cacheType); +int convertNum(int); + +int load(stateType *state, cacheType *cache, int addr); +void store(stateType *state, cacheType *cache, int addr, int data); + +int log2(int x){ + int result = 0; + int i; + + for (i = 1; i < 32; ++i){ + if ((x&1) == 1){ + result = i; + break; + } + x >>= 1; + } + + return (result); +} + +int toint(char *s){ + int x; + x = *(s++) - '0'; + for (;*s;){ + x *= 10; + x += *(s++) - '0'; + } + return(x); +} + +#define SuppressState 1 + +int +main(int argc, char *argv[]) +{ + int i, j, k; + char line[MAXLINELENGTH]; + stateType state; + cacheType cache; + FILE *filePtr; + + if (argc != 5) { + printf("error: usage: %s <machine-code file> blockSize numberOfSets blocksPerSet\n", + argv[0]); + exit(1); + } + + cache.blockSizeInWords = toint(argv[2]); + cache.numberOfSets = toint(argv[3]); + cache.blocksPerSet = toint(argv[4]); + + cache.lruOrder = (int*)malloc(sizeof(int)*cache.numberOfSets*cache.blocksPerSet); + cache.marks = (int*)malloc(sizeof(int)*cache.numberOfSets*cache.blocksPerSet); + + memset(cache.marks, 0, sizeof(int)*cache.numberOfSets*cache.blocksPerSet); + + i=0; + for (j=0; j < cache.numberOfSets; ++j){ + for (k=0; k < cache.blocksPerSet; ++k, ++i){ + cache.lruOrder[i] = k; + } + } + + cache.blockOffsetShift = 0; + cache.blockOffsetMask = (cache.blockSizeInWords - 1); + cache.setIndexShift = log2(cache.blockSizeInWords) - 1; + cache.setIndexMask = (cache.numberOfSets - 1); + cache.tagShift = cache.setIndexShift + log2(cache.numberOfSets) - 1; + cache.tagMask = ((~0) ^ (cache.blockOffsetMask) ^ (cache.setIndexMask)) >> cache.tagShift; + + /* initialize memories and registers */ + for (i=0; i<NUMMEMORY; i++) { + state.mem_[i] = 0; + } + for (i=0; i<NUMREGS; i++) { + state.reg[i] = 0; + } + + state.pc=0; + + /* read machine-code file into instruction/data memory (starting at address 0) */ + + filePtr = fopen(argv[1], "r"); + if (filePtr == NULL) { + printf("error: can't open file %s\n", argv[1]); + perror("fopen"); + exit(1); + } + + for (state.numMemory=0; fgets(line, MAXLINELENGTH, filePtr) != NULL; + state.numMemory++) { + if (state.numMemory >= NUMMEMORY) { + printf("exceeded memory size\n"); + exit(1); + } + if (sscanf(line, "%d", state.mem_+state.numMemory) != 1) { + printf("error in reading address %d\n", state.numMemory); + exit(1); + } + printf("memory[%d]=%d\n", state.numMemory, state.mem_[state.numMemory]); + } + + printf("\n"); + + /* run never returns */ + run(state, cache); + + return(0); +} + +int computeAddress(cacheType *cache, int marks, int setIndex){ + int result; + result = ((marks & MarkTag) << cache->tagShift) | (setIndex << cache->setIndexShift); + return(result); +} + +int evictAndAlloc(cacheType *cache, int setIndex){ + int *setMarks; + int *lruOrder; + int wayIndex; + int baseAddress; + + lruOrder = cache->lruOrder + setIndex * cache->blocksPerSet; + setMarks = cache->marks + setIndex * cache->blocksPerSet; + wayIndex = lruOrder[0]; + baseAddress = computeAddress(cache, setMarks[wayIndex], setIndex); + if (setMarks[wayIndex] & MarkValid){ + if (setMarks[wayIndex] & MarkDirty){ + printAction(baseAddress, cache->blockSizeInWords, cacheToMemory); + } + else{ + printAction(baseAddress, cache->blockSizeInWords, cacheToNowhere); + } + } + + return(wayIndex); +} + +int lookup(cacheType *cache, int *setMarks, int tag, int *wayIndex){ + int hit, i; + + hit = 0; + for (i=0; i < cache->blocksPerSet; ++i){ + if ((setMarks[i] & MarkValid) && (setMarks[i] & MarkTag) == tag){ + hit = 1; + *wayIndex = i; + break; + } + } + + return(hit); +} + +int blockAddress(cacheType *cache, int addr){ + int result; + result = addr & (~cache->blockOffsetMask); + return(result); +} + +void useLine(cacheType *cache, int setIndex, int wayIndex){ + int *lruOrder; + int i; + + lruOrder = cache->lruOrder + setIndex * cache->blocksPerSet; + + for (i=0; i < cache->blocksPerSet; ++i){ + if (lruOrder[i] == wayIndex){ + memmove(lruOrder + i, lruOrder + i + 1, sizeof(int)*(cache->blocksPerSet - 1 - i)); + lruOrder[cache->blocksPerSet - 1] = wayIndex; + break; + } + } +} + +int load(stateType *state, cacheType *cache, int addr){ + int *setMarks; + int setIndex, wayIndex; + int tag; + int hit, i; + int baseAddress; + + setIndex = ((addr >> cache->setIndexShift) & cache->setIndexMask); + tag = ((addr >> cache->tagShift) & cache->tagMask); + setMarks = cache->marks + setIndex * cache->blocksPerSet; + hit = lookup(cache, setMarks, tag, &wayIndex); + + if (!hit){ + wayIndex = evictAndAlloc(cache, setIndex); + setMarks[wayIndex] = MarkValid | tag; + baseAddress = blockAddress(cache, addr); + printAction(baseAddress, cache->blockSizeInWords, memoryToCache); + } + + useLine(cache, setIndex, wayIndex); + printAction(addr, 1, cacheToProcessor); + + return(state->mem_[addr]); +} + +void store(stateType *state, cacheType *cache, int addr, int data){ + int *setMarks; + int setIndex, wayIndex; + int tag; + int hit, i; + int baseAddress; + + setIndex = ((addr >> cache->setIndexShift) & cache->setIndexMask); + tag = ((addr >> cache->tagShift) & cache->tagMask); + setMarks = cache->marks + setIndex * cache->blocksPerSet; + hit = lookup(cache, setMarks, tag, &wayIndex); + + if (!hit){ + wayIndex = evictAndAlloc(cache, setIndex); + setMarks[wayIndex] = MarkValid | tag; + baseAddress = blockAddress(cache, addr); + printAction(baseAddress, cache->blockSizeInWords, memoryToCache); + } + + setMarks[wayIndex] |= MarkDirty; + printAction(addr, 1, processorToCache); + + useLine(cache, setIndex, wayIndex); + state->mem_[addr] = data; +} + +void +run(stateType state, cacheType cache) +{ + int arg0, arg1, arg2, addressField; + int instructions=0; + int opcode; + int maxMem=-1; /* highest memory address touched during run */ + int instr; + + for (; 1; instructions++) { /* infinite loop, exits when it executes halt */ + printState(&state); + + if (state.pc < 0 || state.pc >= NUMMEMORY) { + printf("pc went out of the memory range\n"); + exit(1); + } + + maxMem = (state.pc > maxMem)?state.pc:maxMem; + + /* this is to make the following code easier to read */ + instr = load(&state, &cache, state.pc); + opcode = instr >> 22; + arg0 = (instr >> 19) & 0x7; + arg1 = (instr >> 16) & 0x7; + arg2 = instr & 0x7; /* only for add, nand */ + + addressField = convertNum(instr & 0xFFFF); /* for beq, lw, sw */ + state.pc++; + if (opcode == ADD) { + state.reg[arg2] = state.reg[arg0] + state.reg[arg1]; + } else if (opcode == NAND) { + state.reg[arg2] = ~(state.reg[arg0] & state.reg[arg1]); + } else if (opcode == LW) { + if (state.reg[arg0] + addressField < 0 || + state.reg[arg0] + addressField >= NUMMEMORY) { + printf("address out of bounds\n"); + exit(1); + } + state.reg[arg1] = load(&state, &cache, state.reg[arg0] + addressField); + if (state.reg[arg0] + addressField > maxMem) { + maxMem = state.reg[arg0] + addressField; + } + } else if (opcode == SW) { + if (state.reg[arg0] + addressField < 0 || + state.reg[arg0] + addressField >= NUMMEMORY) { + printf("address out of bounds\n"); + exit(1); + } + store(&state, &cache, state.reg[arg0] + addressField, state.reg[arg1]); + if (state.reg[arg0] + addressField > maxMem) { + maxMem = state.reg[arg0] + addressField; + } + } else if (opcode == BEQ) { + if (state.reg[arg0] == state.reg[arg1]) { + state.pc += addressField; + } + } else if (opcode == JALR) { + state.reg[arg1] = state.pc; + if(arg0 != 0) + state.pc = state.reg[arg0]; + else + state.pc = 0; + } else if (opcode == NOOP) { + } else if (opcode == HALT) { +#ifndef SuppressState + printf("machine halted\n"); + printf("total of %d instructions executed\n", instructions+1); + printf("final state of machine:\n"); +#endif + printState(&state); + exit(0); + } else { + printf("error: illegal opcode 0x%x\n", opcode); + exit(1); + } + state.reg[0] = 0; + } +} + +void +printState(stateType *statePtr) +{ +#ifndef SuppressState + int i; + printf("\nstate:\n"); + printf("\tpc %d\n", statePtr->pc); + printf("\tmemory:\n"); + for (i=0; i<statePtr->numMemory; i++) { + printf("\t\tmem[ %d ] %d\n", i, statePtr->mem_[i]); + } + printf("\tregisters:\n"); + for (i=0; i<NUMREGS; i++) { + printf("\t\treg[ %d ] %d\n", i, statePtr->reg[i]); + } + printf("end state\n"); +#endif +} + +int +convertNum(int num) +{ + /* convert a 16-bit number into a 32-bit Sun integer */ + if (num & (1<<15) ) { + num -= (1<<16); + } + return(num); +} + + + diff --git a/test_data/lots_of_files/calloc.c b/test_data/lots_of_files/calloc.c new file mode 100644 index 0000000..32fca43 --- /dev/null +++ b/test_data/lots_of_files/calloc.c @@ -0,0 +1,55 @@ +/*** +*calloc.c - allocate storage for an array from the heap +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines the calloc() function. +* +*******************************************************************************/ + +#include <malloc.h> +#include <string.h> +#include <winheap.h> +#include <windows.h> +#include <internal.h> +#include <mtdll.h> +#include <dbgint.h> +#include <rtcsup.h> + +void * __cdecl _calloc_impl (size_t num, size_t size, int * errno_tmp); + +/*** +*void *calloc(size_t num, size_t size) - allocate storage for an array from +* the heap +* +*Purpose: +* Allocate a block of memory from heap big enough for an array of num +* elements of size bytes each, initialize all bytes in the block to 0 +* and return a pointer to it. +* +*Entry: +* size_t num - number of elements in the array +* size_t size - size of each element +* +*Exit: +* Success: void pointer to allocated block +* Failure: NULL +* +*Uses: +* +*Exceptions: +* +*******************************************************************************/ + +void * __cdecl _calloc_base (size_t num, size_t size) +{ + int errno_tmp = 0; + void * pv = _calloc_impl(num, size, &errno_tmp); + + if ( pv == NULL && errno_tmp != 0 && _errno()) + { + errno = errno_tmp; // recall, #define errno *_errno() + } + return pv; +} diff --git a/test_data/lots_of_files/calloc_impl.c b/test_data/lots_of_files/calloc_impl.c new file mode 100644 index 0000000..6375487 --- /dev/null +++ b/test_data/lots_of_files/calloc_impl.c @@ -0,0 +1,69 @@ +/*** +*calloc_impl.c - implementation of _calloc_impl +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines the _calloc_impl() function. +* +*******************************************************************************/ + +#include <malloc.h> +#include <string.h> +#include <winheap.h> +#include <windows.h> +#include <internal.h> +#include <mtdll.h> +#include <dbgint.h> +#include <rtcsup.h> + +void * __cdecl _calloc_impl (size_t num, size_t size, int * errno_tmp) +{ + size_t size_orig; + void * pvReturn; + + /* ensure that (size * num) does not overflow */ + if (num > 0) + { + _VALIDATE_RETURN_NOEXC((_HEAP_MAXREQ / num) >= size, ENOMEM, NULL); + } + size_orig = size = size * num; + + + /* force nonzero size */ + if (size == 0) + size = 1; + + for (;;) + { + pvReturn = NULL; + + if (size <= _HEAP_MAXREQ) + { + if (pvReturn == NULL) + pvReturn = HeapAlloc(_crtheap, HEAP_ZERO_MEMORY, size); + } + + if (pvReturn || _newmode == 0) + { + RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, size_orig, 0)); + if (pvReturn == NULL) + { + if ( errno_tmp ) + *errno_tmp = ENOMEM; + } + return pvReturn; + } + + /* call installed new handler */ + if (!_callnewh(size)) + { + if ( errno_tmp ) + *errno_tmp = ENOMEM; + return NULL; + } + + /* new handler was successful -- try to allocate again */ + } + +} diff --git a/test_data/lots_of_files/cd.cpp b/test_data/lots_of_files/cd.cpp new file mode 100644 index 0000000..ab97887 --- /dev/null +++ b/test_data/lots_of_files/cd.cpp @@ -0,0 +1,770 @@ +/* +Main game code. +*/ + +// TOP + +#include "cd_random.h" +#include "cd_world.h" +#include "cd_colors.h" + +#define FTECH_STRING_IMPLEMENTATION +#include "4tech_string.h" + +#ifdef DEVELOPER +# include "cd_hierarchy_editor.h" +#endif + +#include "cd_world.cpp" + +inline i32 +relative_lane(i32 total_lanes, i32 half_visible_lanes, + i32 lane, i32 center){ + i32 result = lane - center; + if (result >= total_lanes - half_visible_lanes){ + result -= total_lanes; + } + else if (result <= -total_lanes + half_visible_lanes){ + result += total_lanes; + } + return(result); +} + +inline i32 +absolute_lane(i32 total_lanes, i32 relative_lane, i32 center){ + i32 result = relative_lane + center; + if (result >= total_lanes){ + result -= total_lanes; + } + if (result < 0){ + result += total_lanes; + } + return(result); +} + +enum Dev_Mode{ + DevMode_None, + DevMode_Dbg, + DevMode_Ed +}; + +struct App_Vars{ + Partition part; + b32 initialized; + Partition trans_part; + + Asset_Manifest *manifest_memory; + + World world; + f32 view_y, view_y_vel; + Random_Series random; +}; + +struct Dev_Vars{ + Partition part; + b32 initialized; + + i32 dev_mode; + + Hierarchy_Vars hierarchy_vars; + Partition manifest_part; + Partition edit_state_part; +}; + +void +establish_target(Render_Target *target, Partition *part){ + target->push_part = sub_partition(part, Kbytes(256)); + cd_memset(target->push_part.base, 0, Kbytes(256)); + + target->memory_size = Kbytes(256); + target->memory = push_block(part, target->memory_size); + cd_memset(target->memory, 0, target->memory_size); +} + +void +establish_bank(Asset_Bank *bank, Partition *part, i32 max_fonts, i32 max_images){ + bank->font_max_id = max_fonts; + if (max_fonts > 0){ + bank->fonts = push_array(part, Font, bank->font_max_id); + } + else{ + bank->fonts = 0; + } + + bank->image_max_id = max_images; + if (max_images > 0){ + bank->images = push_array(part, Image, bank->image_max_id); + } + else{ + bank->images = 0; + } +} + +inline b32 +btn_pressed(Key_State key){ + b32 result = (key.down && !key.prev_down); + return(result); +} + +inline b32 +btn_down(Key_State key){ + b32 result = (key.down); + return(result); +} + +inline b32 +btn_released(Key_State key){ + b32 result = (!key.down && key.prev_down); + return(result); +} + +inline b32 +btn_up(Key_State key){ + b32 result = (!key.down); + return(result); +} + +#ifdef DEVELOPER + +// TODO(allen): Rewrite these as an asset archive system +// for compression and mod/extension packing. + +static void +asset_file_backup(System_API *system){ + int len = 0; + + char space[1024]; + String script = make_fixed_width_string(space); + + append(&script, "backup.bat \""); + + + len = system->DBG_working_path(script.str + script.size, + script.memory_size - script.size); + script.size += len; + + append(&script, "\" "); + + append_int_to_str(&script, MAX_BACKUP_COUNT); + + terminate_with_null(&script); + + system->DBG_call_script(script.str); +} + +static void +asset_file_delete(System_API *system, char *name){ + system->DBG_dump_out(name, 0, 0); +} + +static b32 +asset_file_write(System_API *system, char *source, char *name){ + b32 result = false; + + if (system->DBG_copy(source, name)){ + result = true; + } + + return(result); +} + +static b32 +asset_file_exists(System_API *system, char *name){ + b32 result = false; + File_Dump dump = system->DBG_dump_begin(name); + + if (dump.size > 0){ + result = true; + system->DBG_dump_end(dump, 0); + } + + return(result); +} + +# include "cd_hierarchy_editor.cpp" +#endif + +extern "C" +App_Step_Sig(app_step){ + App_Vars *vars = (App_Vars*)memory.logical; + + if (!vars->initialized){ + vars->initialized = 1; + vars->part = make_partition(memory.logical, memory.logical_size); + push_type(&vars->part, App_Vars); + + vars->trans_part = make_partition(memory.transient, memory.transient_size); + + establish_target(main_target, &vars->trans_part); + establish_bank(bank, &vars->trans_part, DBG_FONT_COUNT, IMAGE_COUNT); + main_target->me_to_pixel_transform = unit_to_pixel_transform; + +#ifdef DEVELOPER + { + Dev_Vars *dev_vars = (Dev_Vars*)memory.developer; + vars->manifest_memory = (Asset_Manifest*) + manifest_load(system, &dev_vars->manifest_part); + } +#else + vars->manifest_memory = (Asset_Manifest*) + manifest_load(system, &vars->trans_part); +#endif + + if (vars->manifest_memory){ + Assert(ASSET_MANIFEST_VERSION == vars->manifest_memory->version); + + Asset_Walker walker = {0}; + + Temp_Memory temp = begin_temp(&vars->trans_part); + Asset_Bank_Setup setup = + bank->begin_setup(bank, &vars->trans_part, system); + + bank->bind_font(&setup, "LiberationSans-Regular.ttf", DBG_FONT); + bank->bind_font(&setup, "liberation-mono.ttf", DBG_MONO_FONT); + + for (Asset_Node *node = walk_first_asset_node(vars->manifest_memory, &walker); + node != 0; + node = walk_next_asset_node(vars->manifest_memory, &walker)){ + switch (node->type){ + case AssetType_Image: + bank->bind_bmp(&setup, node->name, node->image_id); + break; + } + } + + bank->end_setup(&setup); + end_temp(temp); + } + else{ + i32 manifest_size = Kbytes(8); +#ifdef DEVELOPER + { + Dev_Vars *dev_vars = (Dev_Vars*)memory.developer; + DBG_expand_partition(system, &dev_vars->manifest_part, manifest_size); + vars->manifest_memory = (Asset_Manifest*) + push_block(&dev_vars->manifest_part, manifest_size); + } +#else + vars->manifest_memory = (Asset_Manifest*) + push_block(&vars->trans_part, manifest_size); +#endif + initialize_empty_manifest(vars->manifest_memory, manifest_size); + + { + void *manifest = vars->manifest_memory; + Temp_Memory temp = begin_temp(&vars->trans_part); + Asset_Bank_Setup setup = + bank->begin_setup(bank, &vars->trans_part, system); + + bank->bind_font(&setup, "LiberationSans-Regular.ttf", DBG_FONT); + bank->bind_font(&setup, "liberation-mono.ttf", DBG_MONO_FONT); + + Manifest_Setup man_setup = {0}; + + begin_folder(manifest, &man_setup, "game", ROOT); + { + bank->bind_bmp(&setup, "covaris.bmp", BACKGROUND); + bind_image(manifest, &man_setup, "covaris.bmp", BACKGROUND); + + bank->bind_bmp(&setup, "Sheila.bmp", SHEILA); + bind_image(manifest, &man_setup, "Sheila.bmp", SHEILA); + + bank->bind_bmp(&setup, "HUD_Deck-Box.bmp", HUD_DECK); + bank->bind_bmp(&setup, "HUD_Distance_meter_ly.bmp", HUD_DISTANCE_METER_LY); + bank->bind_bmp(&setup, "HUD_Distance_meter_pc.bmp", HUD_DISTANCE_METER_PC); + bank->bind_bmp(&setup, "HUD_Fuel_Velocity_gauge.bmp", HUD_FUEL_VELOCITY); + bank->bind_bmp(&setup, "HUD_Indicator_off.bmp", HUD_INDICATOR_OFF); + bank->bind_bmp(&setup, "HUD_Indicator_on.bmp", HUD_INDICATOR_ON); + bank->bind_bmp(&setup, "HUD_Numbers.bmp", HUD_NUMBERS); + bank->bind_bmp(&setup, "HUD_Slider.bmp", HUD_SLIDER); + + begin_folder(manifest, &man_setup, "HUD", HUD_FOLDER); + bind_image(manifest, &man_setup, "HUD_Deck-Box.bmp", HUD_DECK); + bind_image(manifest, &man_setup, "HUD_Distance_meter_ly.bmp", HUD_DISTANCE_METER_LY); + bind_image(manifest, &man_setup, "HUD_Distance_meter_pc.bmp", HUD_DISTANCE_METER_PC); + bind_image(manifest, &man_setup, "HUD_Fuel_Velocity_gauge.bmp", HUD_FUEL_VELOCITY); + bind_image(manifest, &man_setup, "HUD_Indicator_off.bmp", HUD_INDICATOR_OFF); + bind_image(manifest, &man_setup, "HUD_Indicator_on.bmp", HUD_INDICATOR_ON); + bind_image(manifest, &man_setup, "HUD_Numbers.bmp", HUD_NUMBERS); + bind_image(manifest, &man_setup, "HUD_Slider.bmp", HUD_SLIDER); + end_folder(manifest, &man_setup); + + bank->bind_bmp(&setup, "Asteriod_1.bmp", ASTEROID1); + bank->bind_bmp(&setup, "Asteriod_2.bmp", ASTEROID2); + bank->bind_bmp(&setup, "Asteriod_3.bmp", ASTEROID3); + bank->bind_bmp(&setup, "Asteriod_16.bmp", ASTEROID4); + bank->bind_bmp(&setup, "Asteriod_17.bmp", ASTEROID5); + bank->bind_bmp(&setup, "Asteriod_18.bmp", ASTEROID6); + + begin_folder(manifest, &man_setup, "ass tree odds", ASS_TREE_ODD); + bind_image(manifest, &man_setup, "Asteriod_1.bmp", ASTEROID1); + bind_image(manifest, &man_setup, "Asteriod_2.bmp", ASTEROID2); + bind_image(manifest, &man_setup, "Asteriod_3.bmp", ASTEROID3); + bind_image(manifest, &man_setup, "Asteriod_16.bmp", ASTEROID4); + bind_image(manifest, &man_setup, "Asteriod_17.bmp", ASTEROID5); + bind_image(manifest, &man_setup, "Asteriod_18.bmp", ASTEROID6); + end_folder(manifest, &man_setup); + } + end_folder(manifest, &man_setup); + + bank->end_setup(&setup); + end_temp(temp); + } + } + + add_player(&vars->world); + add_asteroid_spawner(&vars->world, 0.5f); + } + + clear_target(main_target); + clear_target(dev_target); + + Render_Target *dbg_target = 0; + Render_Target *ed_target = 0; + + + // + // Developer Logic + // + +#ifdef DEVELOPER + Dev_Vars *dev_vars = (Dev_Vars*)memory.developer; + if (!dev_vars->initialized){ + dev_vars->initialized = 1; + dev_vars->part = make_partition(memory.developer, memory.developer_size); + push_type(&dev_vars->part, Dev_Vars); + + // TODO(allen): Don't use game's actual trans_part for dev code! + // Set aside another dev_trans_part instead if you must. + establish_target(dev_target, &vars->trans_part); + dev_target->me_to_pixel_transform = identity_transform; + + i32 asset_count = vars->manifest_memory->asset_node_count; + make_hierarchy_vars(system, &dev_vars->edit_state_part, &dev_vars->hierarchy_vars, asset_count); + } + + if (btn_pressed(dev_input->fkeys[1])){ + if (dev_vars->dev_mode != DevMode_Dbg){ + dev_vars->dev_mode = DevMode_Dbg; + } + else{ + dev_vars->dev_mode = DevMode_None; + } + } + else if (btn_pressed(dev_input->fkeys[2])){ + if (dev_vars->dev_mode != DevMode_Ed){ + dev_vars->dev_mode = DevMode_Ed; + } + else{ + dev_vars->dev_mode = DevMode_None; + } + } + + switch (dev_vars->dev_mode){ + case DevMode_None: break; + case DevMode_Dbg: dbg_target = dev_target; break; + case DevMode_Ed: ed_target = dev_target; break; + } +#endif + + + // + // Execute Game + // + + target_transform(main_target, TF_Pixels); + target_transform(dev_target, TF_Pixels); + + do_image_general(main_target, bank, 0, BACKGROUND, + Render_Exact, + main_target->dim*.5f, + v2(0,0), + v2(main_target->dim.x*.5f, 0), + v2(0, main_target->dim.y*.5f)); + + target_transform(main_target, TF_Units); + target_transform(dev_target, TF_Units); + + + // + // Establish Lanes + // + + f32 lane_height = visible_top / total_visible_lanes; + Entity *player_entity = get_player(&vars->world); + i32 player_lane = player_entity->lane; + + { + i32 half_rendered_lanes = half_visible_lanes+2; + + i32 i = -half_rendered_lanes; + i32 lane_index = player_lane + i; + if (lane_index < 0) lane_index += total_lanes; + + f32 lane_y = -lane_height*half_rendered_lanes; + + for (; i <= half_rendered_lanes; ++i){ + set_lane(main_target, dbg_target, lane_index, lane_y - vars->view_y); + lane_index = (lane_index + 1) % total_lanes; + lane_y += lane_height; + } + } + + + // + // Move Camera + // + + f32 dt = input->dt; + { + f32 lane_shift = lerp(0.f, player_entity->lane_lerp, lane_height); + f32 view_accel = 10.f; + f32 damping = 2.f; + + if (vars->view_y != lane_shift){ + f32 a = view_accel; + f32 v = vars->view_y_vel; + f32 y = vars->view_y; + + a = view_accel*(lane_shift - vars->view_y) + damping*(0 - v); + + y = 0.5f*dt*dt*a + dt*v + y; + v = dt*a + v; + + vars->view_y_vel = v; + vars->view_y = y; + } + } + + + // + // Run Simulation + // + + world_begin_simulation(&vars->world); + + f32 player_speed = 10.f; + + f32 lane_change_per_second = 3.f; + + Entity *entity = vars->world.entities; + u32 entity_count = vars->world.count; + + for (u32 entity_index = 0; + entity_index < entity_count; + ++entity_index, ++entity){ + + if (entity->lane_lerp_dir == 0 && + entity->lane_lerp != 0){ + if (entity->lane_lerp > 0){ + entity->lane_lerp_dir = 1; + } + else{ + entity->lane_lerp_dir = -1; + } + } + + switch (entity->type){ + case ET_Player: + { + if (btn_pressed(input->down)){ + entity->lane_lerp_dir = -1; + } + + if (btn_pressed(input->up)){ + entity->lane_lerp_dir = 1; + } + + if (entity->lane_lerp_dir > 0){ + if (entity->lane_lerp >= 0){ + entity->lane_lerp += entity->lane_lerp_dir * lane_change_per_second * dt; + if (entity->lane_lerp >= 1.f){ + entity->lane += 1; + entity->lane_lerp = 0; + entity->lane_lerp_dir = 0; + vars->view_y -= lane_height; + } + } + else{ + entity->lane_lerp += entity->lane_lerp_dir * lane_change_per_second * dt; + if (entity->lane_lerp >= 0.f){ + entity->lane_lerp = 0; + entity->lane_lerp_dir = 0; + } + } + } + else if (entity->lane_lerp_dir < 0){ + if (entity->lane_lerp <= 0){ + entity->lane_lerp += entity->lane_lerp_dir * lane_change_per_second * dt; + if (entity->lane_lerp <= -1.f){ + entity->lane -= 1; + entity->lane_lerp = 0; + entity->lane_lerp_dir = 0; + vars->view_y += lane_height; + } + } + else{ + entity->lane_lerp += entity->lane_lerp_dir * lane_change_per_second * dt; + if (entity->lane_lerp <= 0.f){ + entity->lane_lerp = 0; + entity->lane_lerp_dir = 0; + } + } + } + + if (entity->lane < 0) entity->lane += total_lanes; + if (entity->lane >= total_lanes) entity->lane -= total_lanes; + + vec2 pos; + pos.x = 0.f; + if (entity->lane_lerp >= 0){ + pos.y = slerp(0.f, entity->lane_lerp, lane_height) - vars->view_y; + } + else{ + pos.y = slerp(0.f, -entity->lane_lerp, -lane_height) - vars->view_y; + } + + do_image(main_target, bank, dbg_target, SHEILA, pos, 0.2f); + }break; + + case ET_Obstacle: + { + f32 speed = entity->speed + player_speed; + entity->x -= speed * dt; + + if (entity->x < -25.f){ + add_to_kill_list(&vars->world, entity); + } + + do_obstacle(main_target, dbg_target, + ET_Obstacle, entity->seed, + entity->x, entity->lane); + }break; + + case ET_Obstacle_Spawner: + { + f32 second_per_spawn = entity->second_per_spawn; + entity->spawn_clock += dt; + + while (entity->spawn_clock > second_per_spawn){ + i32 lane = absolute_lane(total_lanes, + random_between(&vars->random, + -half_visible_lanes*2, + half_visible_lanes*2), + player_lane); + add_asteroid(&vars->world, lane); + + entity->spawn_clock -= second_per_spawn; + } + }break; + + case ET_Null: + case ET_Count: + default: + InvalidCodePath; + } + } + + world_end_simulation(&vars->world); + + + // + // Render HUD + // + + target_transform(main_target, TF_Pixels); + target_transform(dev_target, TF_Pixels); + + f32 pos = main_target->dim.y; + + f32 gauge_width = main_target->dim.x/8.f; + f32 gauge_height = gauge_width/3.f; + + f32 deck_height = gauge_height; + f32 deck_width = gauge_width; + + f32 distance_height = gauge_height; + f32 distance_width = gauge_width; + + f32 indicator_width = 25.f; + f32 indicator_height = 25.f; + + f32 side_margin = 10.f; + f32 slider_width = 10.f; + f32 slider_height = 12.f; + f32 slider_offset = slider_height + 8.f; + + f32 extra_deck_width = 50.f; + + deck_width += extra_deck_width; + deck_height = deck_width/3.f; + + f32 deck_y_shift = 30.f; + + { + f32 fuel_amount = 0.5f; + f32 speed_amount = 0.5f; + + do_image_general(main_target, bank, 0, + HUD_FUEL_VELOCITY, Render_Exact, + v2(side_margin, pos), + v2(-1.f, 1.f), + v2(gauge_width, 0.f), + v2(0.f, gauge_height)); + + do_image_general(main_target, bank, 0, + HUD_SLIDER, Render_Exact, + v2(side_margin + gauge_width*fuel_amount*2.f, + pos - slider_offset), + v2(0.f, 1.f), + v2(slider_width, 0.f), + v2(0.f, -slider_height)); + + do_image_general(main_target, bank, 0, + HUD_SLIDER, Render_Exact, + v2(side_margin + gauge_width*speed_amount*2.f, + pos - gauge_height*2 + slider_offset + 1), + v2(0.f, 1.f), + v2(slider_width, 0.f), + v2(0.f, slider_height)); + } + + { + do_image_general(main_target, bank, 0, + HUD_DECK, Render_Exact, + v2(main_target->dim.x*.5f, pos+deck_y_shift), + v2(0.f, 1.f), + v2(deck_width, 0.f), + v2(0.f, deck_height)); + } + + { + do_image_general(main_target, bank, 0, + HUD_DISTANCE_METER_LY, Render_Exact, + v2(main_target->dim.x - side_margin, pos), + v2(1.f, 1.f), + v2(distance_width, 0.f), + v2(0.f, distance_height)); + } + + { + do_image_general(main_target, bank, 0, + HUD_INDICATOR_OFF, Render_Exact, + v2(1010.f, 760.f), + v2(1.f, 1.f), + v2(indicator_width, 0.f), + v2(0.f, indicator_height)); + + do_image_general(main_target, bank, 0, + HUD_INDICATOR_OFF, Render_Exact, + v2(1010.f, 760.f - indicator_height*2.f), + v2(1.f, 1.f), + v2(indicator_width, 0.f), + v2(0.f, indicator_height)); + } + + null_render(main_target); + main_target->execute(main_target, bank); + +#ifdef DEVELOPER + // + // Loop Info + // + + target_transform(dev_target, TF_Pixels); + + vec2 status_pos = v2(25.f, dev_target->dim.y - 25.f); + + f32 descend = 20.f; + f32 mouse_alpha = 0.5f; + f32 gui_alpha = 0.75f; + + if (loop_mode < 3){ + switch (loop_mode){ + case 1: + do_string(dev_target, bank, DBG_FONT, status_pos, + "Recording Loop", sizeof("Recording Loop") - 1, + white); + break; + + case 2: + do_dbg_rectangle(dev_target, + v4(dev_input->input.mx - 5.f, dev_input->input.my - 5.f, + dev_input->input.mx + 5.f, dev_input->input.my + 5.f), + v4(1.f, 1.f, 0.f, mouse_alpha)); + + do_string(dev_target, bank, DBG_FONT, status_pos, + "Playing Loop", sizeof("Playing Loop") - 1, + white); + break; + } + } + else{ + do_dbg_rectangle(dev_target, + v4(dev_input->input.mx - 5.f, dev_input->input.my - 5.f, + dev_input->input.mx + 5.f, dev_input->input.my + 5.f), + v4(1.f, 1.f, 0.f, mouse_alpha)); + + do_string(dev_target, bank, DBG_FONT, status_pos, + "Stopped", sizeof("Stopped") - 1, + white); + } + + + // + // Execute Debug + // + + if (dev_vars->dev_mode == DevMode_Dbg){ + do_dbg_rectangle(dbg_target, + v4(input->mx - 5.f, input->my - 5.f, + input->mx + 5.f, input->my + 5.f), + v4(0.f, 1.f, 0.f, mouse_alpha)); + + vec2 info_pos = status_pos; + info_pos.y -= descend; + + char space[64]; + String string = make_fixed_width_string(space); + + append(&string, "mouse = ("); + append_int_to_str(&string, dev_input->input.mx); + append(&string, ","); + append_int_to_str(&string, dev_input->input.my); + append(&string, ")"); + + do_string(dbg_target, bank, DBG_FONT, info_pos, + string.str, string.size, + white); + } + + + // + // Execute Editor + // + + if (dev_vars->dev_mode == DevMode_Ed){ + do_dbg_rectangle(ed_target, + v4(input->mx - 5.f, input->my - 5.f, + input->mx + 5.f, input->my + 5.f), + v4(0.f, 1.f, 0.f, mouse_alpha)); + + // TODO(allen): Need dev_trans_part + hierarchy_editor_step(system, &vars->trans_part, bank, + ed_target, dev_input, + &dev_vars->hierarchy_vars, + &dev_vars->manifest_part, + &dev_vars->edit_state_part, + status_pos, descend, gui_alpha); + vars->manifest_memory = (Asset_Manifest*)dev_vars->manifest_part.base; + + manifest_dump(system, vars->manifest_memory); + } +#endif + + null_render(dev_target); + dev_target->execute(dev_target, bank); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/cd.h b/test_data/lots_of_files/cd.h new file mode 100644 index 0000000..f6184bb --- /dev/null +++ b/test_data/lots_of_files/cd.h @@ -0,0 +1,95 @@ +/* + * Main game interface to platform layer. + */ + +// TOP + +#ifndef CD_H +#define CD_H + +struct Memory{ + void *logical; + u32 logical_size; + void *transient; + u32 transient_size; + +#ifdef DEVELOPER + void *developer; + u32 developer_size; +#endif +}; + +struct Key_State{ + b8 down, prev_down; +}; + +struct Input_State{ + f32 dt; + + i32 mx, my; + Key_State left_button; + Key_State right_button; + + Key_State *letter; + Key_State letter_[26]; + Key_State number[10]; + + Key_State up; + Key_State down; + Key_State left; + Key_State right; + + Key_State esc; +}; + +#ifdef DEVELOPER + +#include "4tech_keycodes.h" + +struct Key_Events{ + char events[32]; + i32 count; +}; + +struct Dev_File_Drop{ + char name[1024]; +}; + +struct Dev_Input_State{ + Input_State input; + + Key_State *fkeys; + Key_State fkeys_[12]; + + u32 drop_count; + Dev_File_Drop drops[128]; + f32 drop_x, drop_y; + + Key_Events keys; + +}; +#endif + +struct Render_Target; + +#define App_Step_Sig(n) void n( \ +System_API *system, \ +Memory memory, \ +Render_Target *main_target, \ +Render_Target *dev_target, \ +struct Asset_Bank *bank, \ +Input_State *input, \ +Dev_Input_State *dev_input, \ +i32 loop_mode) + +typedef App_Step_Sig(App_Step_Function); + +struct App_Functions{ + App_Step_Function *step; +}; + +#endif + +// BOTTOM + + diff --git a/test_data/lots_of_files/cd_app_comp_unit.cpp b/test_data/lots_of_files/cd_app_comp_unit.cpp new file mode 100644 index 0000000..d271068 --- /dev/null +++ b/test_data/lots_of_files/cd_app_comp_unit.cpp @@ -0,0 +1,21 @@ + +#include "cd_ids.h" +#include "cd_defines.h" +#include "cd_intrin.h" +#include "cd_standard_types.h" + +#include "cd_system.h" +#include "cd.h" + +#include "cd_partition.h" +#include "stb_truetype.h" +#include "cd_render_target.h" +#include "cd_asset_manifest.h" + +#ifdef DEVELOPER +#include "cd_debug.h" +#endif + +#include "cd_render_helper.cpp" +#include "cd_asset_manifest.cpp" +#include "cd.cpp" diff --git a/test_data/lots_of_files/cd_asset_manifest.cpp b/test_data/lots_of_files/cd_asset_manifest.cpp new file mode 100644 index 0000000..cb5a385 --- /dev/null +++ b/test_data/lots_of_files/cd_asset_manifest.cpp @@ -0,0 +1,476 @@ +/* + +Asset manifest operations +-Allen +06.06.2016 + +*/ + +// TOP + +inline rptr32 +to_rptr32(void *pos, void *base){ + rptr32 result = (rptr32)((char*)pos - (char*)base); + return(result); +} + +inline void* +to_ptr(rptr32 pos, void *base){ + void *result = ((char*)base + pos); + return(result); +} + +inline Asset_Node* +to_node_ptr(rptr32 pos, void *base){ + Asset_Node *result = (Asset_Node *)((char*)base + pos); + return(result); +} + +inline Asset_Node* +get_node(i32 image_id, Asset_Manifest *manifest){ + Asset_Node *node = to_node_ptr(manifest->asset_nodes, manifest); + node += image_id - 1; + return(node); +} + +inline i32 +get_image_id(rptr32 pos, Asset_Manifest *manifest){ + i32 dist = (pos - manifest->asset_nodes); + Assert(dist % sizeof(Asset_Node) == 0); + dist = 1 + (dist/sizeof(Asset_Node)); + return(dist); +} + +void +init_sentinel(Asset_Node *node, void *base){ + rptr32 self = to_rptr32(node, base); + node->next_sibling = self; + node->prev_sibling = self; +} + +void +insert_node(Asset_Node *node, Asset_Node *pos, void *base){ + rptr32 node_self = to_rptr32(node, base); + rptr32 pos_self = to_rptr32(pos, base); + rptr32 next_self = pos->next_sibling; + Asset_Node *next = to_node_ptr(next_self, base); + + node->prev_sibling = pos_self; + node->next_sibling = next_self; + + pos->next_sibling = node_self; + next->prev_sibling = node_self; +} + +void +remove_node(Asset_Node *node, void *base){ + rptr32 next_self = node->next_sibling; + rptr32 prev_self = node->prev_sibling; + Asset_Node *next = to_node_ptr(next_self, base); + Asset_Node *prev = to_node_ptr(prev_self, base); + + next->prev_sibling = prev_self; + prev->next_sibling = next_self; +} + +void +sibling_insert_before(Asset_Node *node, Asset_Node *pos, void *base){ + rptr32 node_self = to_rptr32(node, base); + rptr32 pos_self = to_rptr32(pos, base); + rptr32 prev_self = pos->prev_sibling; + Asset_Node *prev = to_node_ptr(prev_self, base); + + node->parent = pos->parent; + + node->next_sibling = pos_self; + node->prev_sibling = prev_self; + + pos->prev_sibling = node_self; + prev->next_sibling = node_self; +} + +void +sibling_insert_after(Asset_Node *node, Asset_Node *pos, void *base){ + rptr32 node_self = to_rptr32(node, base); + rptr32 pos_self = to_rptr32(pos, base); + rptr32 next_self = pos->next_sibling; + Asset_Node *next = to_node_ptr(next_self, base); + + node->parent = pos->parent; + + node->prev_sibling = pos_self; + node->next_sibling = next_self; + + pos->next_sibling = node_self; + next->prev_sibling = node_self; +} + +void +tree_remove(Asset_Node *node, void *base){ + rptr32 parent_self = node->parent; + rptr32 node_self = to_rptr32(node, base); + rptr32 next_self = node->next_sibling; + rptr32 prev_self = node->prev_sibling; + Asset_Node *parent = to_node_ptr(parent_self, base); + Asset_Node *next = to_node_ptr(next_self, base); + Asset_Node *prev = to_node_ptr(prev_self, base); + + next->prev_sibling = prev_self; + prev->next_sibling = next_self; + + if (parent->first_child == node_self){ + if (next_self != node_self){ + parent->first_child = next_self; + } + else{ + parent->first_child = 0; + } + } +} + +void +insert_under(Asset_Node *node, Asset_Node *parent, void *base, b32 insert_as_first){ + if (parent->first_child == 0){ + rptr32 node_self = to_rptr32(node, base); + rptr32 parent_self = to_rptr32(parent, base); + + parent->first_child = node_self; + node->next_sibling = node_self; + node->prev_sibling = node_self; + node->parent = parent_self; + } + else{ + Asset_Node *first = to_node_ptr(parent->first_child, base); + sibling_insert_before(node, first, base); + + if (insert_as_first){ + rptr32 node_self = to_rptr32(node, base); + parent->first_child = node_self; + } + } +} + +void +add_free_nodes(Asset_Manifest *manifest, Wrapped_Partition *part){ + i32 node_count = (manifest->part.max - manifest->part.pos) / sizeof(Asset_Node); + + if (node_count > 0){ + Asset_Node *nodes = push_array(part, Asset_Node, node_count); + + i32 j = manifest->asset_node_count + 1; + + if (manifest->asset_nodes == 0){ + manifest->asset_nodes = to_rptr32(nodes, manifest); + } + manifest->asset_node_count += node_count; + manifest->asset_free_count += node_count; + + Asset_Node *node = nodes; + for (i32 i = 0; i < node_count; ++i, ++j, ++node){ + insert_node(node, &manifest->free_sentinel, manifest); + node->image_id = j; + node->name[0] = 0; + } + } +} + +void +initialize_empty_manifest(void *manifest_memory, i32 size){ + Asset_Manifest *manifest = (Asset_Manifest*)manifest_memory; + manifest->version = ASSET_MANIFEST_VERSION; + + manifest->part = make_relative_partition(size); + + Wrapped_Partition part_ = make_wrapped_partition(&manifest->part, manifest_memory); + Wrapped_Partition *part = &part_; + + push_type(part, Asset_Manifest); + + init_sentinel(&manifest->free_sentinel, manifest_memory); + + manifest->asset_node_count = 0; + manifest->asset_free_count = 0; + + add_free_nodes(manifest, part); +} + +#define MANIFEST_NAME "CDmanifest" + +Asset_Manifest* +manifest_load(System_API *system, Partition *part){ + void *result = 0; + + Temp_Memory temp = begin_temp(part); + File_Dump dump = system->DBG_dump_begin(MANIFEST_NAME); + + if (dump.size > 0){ + result = push_block(part, dump.size); + + if (result == 0){ +#ifdef DEVELOPER + DBG_expand_partition(system, part, dump.size); + result = push_block(part, dump.size); +#else + InvalidCodePath; +#endif + } + + if (!system->DBG_dump_end(dump, result)){ + end_temp(temp); + result = 0; + } + } + + return((Asset_Manifest*)result); +} + +#ifdef DEVELOPER +void +manifest_dump(System_API *system, void *base){ + Asset_Manifest *manifest = (Asset_Manifest*)base; + i32 memory_size = manifest->part.max; + system->DBG_dump_out(MANIFEST_NAME, base, memory_size); +} +#endif + +void +allocate_node(Asset_Manifest *manifest, + Asset_Node *node, + char *filename, i32 len){ + --manifest->asset_free_count; + + remove_node(node, manifest); + cd_memcpy(node->name, filename, len+1); + + node->first_child = 0; + node->next_sibling = 0; + node->prev_sibling = 0; +} + +void +free_node(Asset_Manifest *manifest, + Asset_Node *node){ + + ++manifest->asset_free_count; + + insert_node(node, &manifest->free_sentinel, manifest); + cd_memcpy(node->name, "", 0); +} + +rptr32 +declare_image(void *manifest_memory, char *filename, i32 image_id, i32 expect_empty){ + rptr32 result = 0; + i32 len = 0; + + len = cd_strlen(filename); + + if (len != 0 && len < ASSET_MAX_NAME){ + Asset_Manifest *manifest = (Asset_Manifest*)manifest_memory; + Asset_Node *nodes = (Asset_Node*) + to_ptr(manifest->asset_nodes, manifest_memory); + Asset_Node *node = nodes + (image_id - 1); + + if (node->name[0] == 0 && expect_empty){ + allocate_node(manifest, node, filename, len); + result = to_rptr32(node, manifest_memory); + } + else if (node->name[0] != 0 && !expect_empty){ + cd_memcpy(node->name, filename, len+1); + result = to_rptr32(node, manifest_memory); + } + } + + return(result); +} + +rptr32 +bind_image(void *manifest_memory, Manifest_Setup *setup, char *filename, i32 image_id){ + rptr32 result = declare_image(manifest_memory, filename, image_id, true); + + if (setup->count > 0){ + Asset_Node *node = to_node_ptr(result, manifest_memory); + Asset_Node *parent = to_node_ptr(setup->parents[setup->count-1], manifest_memory); + + insert_under(node, parent, manifest_memory, false); + node->type = AssetType_Image; + } + + return(result); +} + +rptr32 +replace_image(void *manifest_memory, char *filename, i32 image_id){ + return(declare_image(manifest_memory, filename, image_id, false)); +} + +void +begin_folder(void *manifest_memory, Manifest_Setup *setup, char *name, i32 image_id){ + Assert(setup->count < ArrayCount(setup->parents)); + + rptr32 node_self = bind_image(manifest_memory, setup, name, image_id); + setup->parents[setup->count++] = node_self; + + Asset_Node *node = to_node_ptr(node_self, manifest_memory); + node->type = AssetType_GenericFolder; +} + +void +end_folder(void *manifest_memory, Manifest_Setup *setup){ + Assert(setup->count > 0); + --setup->count; +} + +int +new_image(void *manifest_memory, char *filename){ + i32 result = 0; + + i32 len = cd_strlen(filename); + + if (len != 0 && len < ASSET_MAX_NAME){ + Asset_Manifest *manifest = (Asset_Manifest*)manifest_memory; + Asset_Node *node = + to_node_ptr(manifest->free_sentinel.next_sibling, manifest_memory); + if (node != &manifest->free_sentinel){ + Assert(node->name[0] == 0); + allocate_node(manifest, node, filename, len); + result = 1; + } + } + + return(result); +} + +void +delete_image(void *manifest_memory, i32 image_id){ + Asset_Manifest *manifest = (Asset_Manifest*)manifest_memory; + Asset_Node *nodes = to_node_ptr(manifest->asset_nodes, manifest_memory); + Asset_Node *node = nodes + (image_id - 1); + + if (node->name[0] != 0){ + free_node(manifest, node); + } +} + +void +move_manifest_to_bigger_block(void *manifest_memory, void *new_memory, i32 size){ + Asset_Manifest *manifest = (Asset_Manifest*)manifest_memory; + + Assert(manifest->part.max < size); + + if (new_memory != manifest_memory){ + cd_memcpy(new_memory, manifest_memory, manifest->part.max); + } + manifest = (Asset_Manifest*)new_memory; + manifest->part.max = size; + + Wrapped_Partition part = make_wrapped_partition(&manifest->part, manifest_memory); + add_free_nodes(manifest, &part); +} + +#ifdef DEVELOPER +Asset_Node* +get_available_node(System_API *system, Partition *manifest_part, Asset_Manifest **manifest_memory_ptr){ + Asset_Manifest *manifest = (Asset_Manifest*)manifest_part->base; + + rptr32 node_self = manifest->free_sentinel.next_sibling; + Asset_Node *result = to_node_ptr(node_self, manifest); + + if (result == &manifest->free_sentinel){ + i32 pos = manifest_part->rel_part.pos; + Asset_Manifest *new_manifest = (Asset_Manifest*)push_block(manifest_part, pos); + if (new_manifest == 0){ + DBG_expand_partition(system, manifest_part, pos); + new_manifest = (Asset_Manifest*)push_block(manifest_part, pos); + Assert(new_manifest); + manifest = (Asset_Manifest*)manifest_part->base; + *manifest_memory_ptr = (Asset_Manifest*)manifest_part->base; + } + move_manifest_to_bigger_block(manifest, manifest, manifest_part->rel_part.pos); + + node_self = manifest->free_sentinel.next_sibling; + result = to_node_ptr(node_self, manifest); + Assert(result != &manifest->free_sentinel); + } + + allocate_node(manifest, result, "New", 3); + + return(result); +} +#endif + +Asset_Walker_Entry +new_walker_entry(rptr32 first, i32 level){ + Asset_Walker_Entry result; + result.first = first; + result.current = first; + result.level = level; + return(result); +} + +Asset_Node* +walk_first_asset_node(void *manifest_memory, Asset_Walker *walker){ + Asset_Manifest *manifest = (Asset_Manifest*)manifest_memory; + Asset_Node *nodes = to_node_ptr(manifest->asset_nodes, manifest_memory); + Asset_Node *node = nodes + (ROOT - 1); + + walker->current_level = 0; + + if (node->first_child){ + walker->stack[walker->top++] = new_walker_entry(node->first_child, 1); + } + + return(node); +} + +Asset_Node* +walk_next_asset_node(void *manifest_memory, Asset_Walker *walker){ + Asset_Node *node = 0; + + if (walker->top != 0){ + walker->current_level = walker->stack[walker->top-1].level; + } + + if (walker->top > 0){ + Asset_Walker_Entry *top = walker->stack + (walker->top - 1); + + node = to_node_ptr(top->current, manifest_memory); + + top->current = node->next_sibling; + + if (top->current == top->first){ + --walker->top; + } + + if (node->first_child){ + walker->stack[walker->top++] = + new_walker_entry(node->first_child, top->level + 1); + } + } + + return(node); +} + +Asset_Node* +walk_skip_children_asset_node(void *manifest_memory, Asset_Walker *walker){ + Asset_Node *node = 0; + + if (walker->top > 0){ + Asset_Walker_Entry *top = walker->stack + (walker->top - 1); + + while (top->level > walker->current_level){ + --walker->top; + if (walker->top > 0){ + top = walker->stack + (walker->top - 1); + } + else{ + break; + } + } + + node = walk_next_asset_node(manifest_memory, walker); + } + + return(node); +} + +// BOTTOM diff --git a/test_data/lots_of_files/cd_asset_manifest.h b/test_data/lots_of_files/cd_asset_manifest.h new file mode 100644 index 0000000..d83c6fb --- /dev/null +++ b/test_data/lots_of_files/cd_asset_manifest.h @@ -0,0 +1,97 @@ +/* + +Asset manifest data format definition +-Allen +06.06.2016 + +*/ + +// TOP + +#ifndef CD_ASSET_MANIFEST_H +#define CD_ASSET_MANIFEST_H + +#define ASSET_MANIFEST_VERSION 1 + +#define ASSET_MAX_NAME 32 + +// IMPORTANT(allen): +// If Asset_Node or Asset_Manifest are edited at all it will invalidate +// all CDmanifest files. +// +// If Asset_Flag or Asset_Type are rearranged it will invalidate all +// CDmanifest files. +// +// If we _have_ to invalidate CDmanifest files we will have to write a +// one time convert which is time consuming, so usually finding a method +// other that does not invalidate our data format will be best. +// +// If this turns into a big issue, I'll look into making a way to write +// these one time converts more quickly or to support a more flexible +// data format. +// +// Note we can insert enumeration values in Asset_Type right above +// AssetType_TypeCount safely, as that value is just meant to count how many +// asset types we have and should not actually be stored in CDmanifest. +// + +struct Asset_Node{ + rptr32 first_child; + rptr32 parent; + rptr32 next_sibling; + rptr32 prev_sibling; + + char name[ASSET_MAX_NAME]; + + i32 image_id; + i32 type; + u64 flags; + + char expansion_space[32]; +}; + +enum Asset_Flag{ + AssetFlag_None = 0x0, +}; + +enum Asset_Type{ + AssetType_GenericFolder, + AssetType_Image, + AssetType_ObstacleType, + + // this must be at the end + AssetType_TypeCount +}; + +struct Asset_Manifest{ + Relative_Partition part; + i32 version; + + Asset_Node free_sentinel; + rptr32 asset_nodes; + i32 asset_node_count; + i32 asset_free_count; + + char expansion_space[1008]; +}; + +struct Manifest_Setup{ + rptr32 parents[32]; + i32 count; +}; + +struct Asset_Walker_Entry{ + rptr32 first, current; + i32 level; +}; + +struct Asset_Walker{ + Asset_Walker_Entry stack[32]; + i32 top; + + i32 current_level; +}; + +#endif + +// BOTTOM diff --git a/test_data/lots_of_files/cd_colors.h b/test_data/lots_of_files/cd_colors.h new file mode 100644 index 0000000..50b604c --- /dev/null +++ b/test_data/lots_of_files/cd_colors.h @@ -0,0 +1,22 @@ +/* + +Common colors. +-Allen +28.06.2016 + +*/ + +// TOP + +#ifndef CD_COLORS_H +#define CD_COLORS_H + +static vec4 white = v4(1.f, 1.f, 1.f, 1.f); +static vec3 white3 = v3(1.f, 1.f, 1.f); + +static vec4 black = v4(0.f, 0.f, 0.f, 1.f); +static vec3 black3 = v3(0.f, 0.f, 0.f); + +#endif + +// BOTTOM \ No newline at end of file diff --git a/test_data/lots_of_files/cd_comp_unit.cpp b/test_data/lots_of_files/cd_comp_unit.cpp new file mode 100644 index 0000000..fb4299f --- /dev/null +++ b/test_data/lots_of_files/cd_comp_unit.cpp @@ -0,0 +1,29 @@ + +#include "cd_ids.h" +#include "cd_defines.h" +#include "cd_intrin.h" +#include "cd_standard_types.h" + +#include "cd_system.h" +#include "cd.h" + +#include "cd_partition.h" +#include "stb_truetype.h" +#include "cd_render_target.h" + +#ifdef DEVELOPER +#include "cd_debug.h" +#endif + +#include "cd_render_helper.cpp" + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +#include <windows.h> +#include <gl\gl.h> + +#include "cd_render_target.cpp" +#include "cd_debug_renderer.cpp" +#include "win32_cd.cpp" + diff --git a/test_data/lots_of_files/cd_debug.h b/test_data/lots_of_files/cd_debug.h new file mode 100644 index 0000000..3b112e4 --- /dev/null +++ b/test_data/lots_of_files/cd_debug.h @@ -0,0 +1,25 @@ +/* + * Debug systems + */ + +// TOP + +void +DBG_expand_partition(System_API *system, Partition *part, i32 min_expand_size){ + i32 new_size = part->rel_part.max*2; + if (part->rel_part.max < min_expand_size){ + new_size = min_expand_size*2; + } + + void *new_mem = system->DBG_memory_allocate(new_size); + if (part->base){ + cd_memcpy(new_mem, part->base, part->rel_part.pos); + system->DBG_memory_free(part->base); + cd_memset((char*)new_mem + part->rel_part.pos, 0, new_size - part->rel_part.pos); + } + + part->base = new_mem; + part->rel_part.max = new_size; +} + +// BOTTOM diff --git a/test_data/lots_of_files/cd_debug_renderer.cpp b/test_data/lots_of_files/cd_debug_renderer.cpp new file mode 100644 index 0000000..400fd82 --- /dev/null +++ b/test_data/lots_of_files/cd_debug_renderer.cpp @@ -0,0 +1,157 @@ +/* + * Implementation of render target for debug rendering + */ + +// TOP + +struct Dbg_Renderer_Vars{ + Partition part; + b32 initialized; +}; + +Render_Execute_Sig(dbg_render_execute){ + Dbg_Renderer_Vars *vars = (Dbg_Renderer_Vars*)target->memory; + vars->part = make_partition(target->memory, target->memory_size); + + if (!vars->initialized){ + vars->initialized = true; + } + + Render_Command_Header *h; + + for (h = (Render_Command_Header*)target->push_part.base; + h->type; + h = NextHeader(h)){ + + switch (h->type){ + case render_dbg_parallelagram: + { + Render_Dbg_Parallelagram *r = + (Render_Dbg_Parallelagram*)(h + 1); + + vec2 center = r->transform.pos; + vec2 half_x = r->transform.basis.x; + vec2 half_y = r->transform.basis.y; + vec2 p; + + glBindTexture(GL_TEXTURE_2D, 0); + glColor4f(r->color.r, r->color.g, r->color.b, r->color.a); + glBegin(GL_QUADS); + { + p = center - half_x + half_y; + glVertex2f(p.x, p.y); + + p = center + half_x + half_y; + glVertex2f(p.x, p.y); + + p = center + half_x - half_y; + glVertex2f(p.x, p.y); + + p = center - half_x - half_y; + glVertex2f(p.x, p.y); + } + glEnd(); + }break; + + case render_dbg_rectangle: + { + Render_Dbg_Rectangle *r = + (Render_Dbg_Rectangle*)(h + 1); + + glBindTexture(GL_TEXTURE_2D, 0); + glColor4f(r->color.r, r->color.g, r->color.b, r->color.a); + glBegin(GL_QUADS); + { + glVertex2f(r->rect.x0, r->rect.y0); + glVertex2f(r->rect.x1, r->rect.y0); + glVertex2f(r->rect.x1, r->rect.y1); + glVertex2f(r->rect.x0, r->rect.y1); + } + glEnd(); + }break; + + case render_image: + { + Render_Image *i = (Render_Image*)(h + 1); + Image *image = get_image(bank, i->image_id); + if (image){ + vec2 center = i->transform.pos; + vec2 half_x = i->transform.basis.x; + vec2 half_y = i->transform.basis.y; + f32 s = image->tex_x; + f32 t = image->tex_y; + + center = do_transform(center, target->me_to_pixel_transform); + + { + i32 texture_id = image->texture_id; + vec4 white = v4(1.f, 1.f, 1.f, 1.f); + + glBindTexture(GL_TEXTURE_2D, texture_id); + glColor4f(white.r, white.g, white.b, white.a); + glBegin(GL_QUADS); + { + vec2 p; + + p = center - half_x + half_y; + glTexCoord2f(0.f, t); + glVertex2f(p.x, p.y); + + p = center + half_x + half_y; + glTexCoord2f(s, t); + glVertex2f(p.x, p.y); + + p = center + half_x - half_y; + glTexCoord2f(s, 0.f); + glVertex2f(p.x, p.y); + + p = center - half_x - half_y; + glTexCoord2f(0.f, 0.f); + glVertex2f(p.x, p.y); + } + glEnd(); + } + } + }break; + + case render_glyph: + { + Render_Glyph *g = (Render_Glyph*)(h + 1); + Font *font = get_font(bank, g->font_id); + if (font){ + stbtt_aligned_quad q; + f32 y = g->pos.y; + + g->pos.y = 0; + stbtt_GetBakedQuad(font->chardata, font->texture_w, font->texture_h, + g->c, &g->pos.x, &g->pos.y, &q, 1); + + glBindTexture(GL_TEXTURE_2D, font->texture_id); + glColor4f(0.f, 0.f, 0.f, g->color.a); + glBegin(GL_QUADS); + { + glTexCoord2f(q.s0, q.t1); glVertex2f(q.x0 + 1, y - q.y1 - 1); + glTexCoord2f(q.s1, q.t1); glVertex2f(q.x1 + 1, y - q.y1 - 1); + glTexCoord2f(q.s1, q.t0); glVertex2f(q.x1 + 1, y - q.y0 - 1); + glTexCoord2f(q.s0, q.t0); glVertex2f(q.x0 + 1, y - q.y0 - 1); + } + glEnd(); + + glColor4f(g->color.r, g->color.g, g->color.b, g->color.a); + glBegin(GL_QUADS); + { + glTexCoord2f(q.s0, q.t1); glVertex2f(q.x0, y - q.y1); + glTexCoord2f(q.s1, q.t1); glVertex2f(q.x1, y - q.y1); + glTexCoord2f(q.s1, q.t0); glVertex2f(q.x1, y - q.y0); + glTexCoord2f(q.s0, q.t0); glVertex2f(q.x0, y - q.y0); + } + glEnd(); + } + }break; + } + + } +} + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_defines.h b/test_data/lots_of_files/cd_defines.h new file mode 100644 index 0000000..7703378 --- /dev/null +++ b/test_data/lots_of_files/cd_defines.h @@ -0,0 +1,152 @@ +/* + * Useful standard defines + */ + +// TOP + +#ifndef CD_DEFINES_H +#define CD_DEFINES_H + +#include <stdint.h> + +#define ArrayCount(a) (sizeof(a)/sizeof(*(a))) + +#define AllowLocal(x) ((void)x) + +#ifdef DEBUG +# define Assert(x) do{ if (!(x)) { *((int*)0) = 0; } }while(0) +#else +# define Assert(x) +#endif + +#define InvalidCodePath Assert(!"Invalid code path!") +#define NotImplemented Assert(!"Implement this!") + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef i32 rptr32; +typedef i64 rptr64; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef u8 b8; +typedef u16 b16; +typedef u32 b32; + +typedef float f32; +typedef double f64; + +#define Kbytes(n) ((n) << 10) +#define Mbytes(n) ((n) << 20) +#define Gbytes(n) ((u64)(n) << 30) +#define Tbytes(n) ((u64)(n) << 40) + +#define Min(a,b) (((a)<(b))?(a):(b)) +#define Max(a,b) (((a)>(b))?(a):(b)) + +#define PtrRead(ptr, type) *(type*)ptr; ptr += sizeof(type) + +#define max_u8 (u32)(0xFF) +#define max_u16 (u32)(0xFFFF) +#define max_u32 (u32)(0xFFFFFFFF) +#define max_u64 (u64)(0xFFFFFFFFFFFFFFFF) + +#define min_i8 (i32)(0x80) +#define min_i16 (i32)(0x8000) +#define min_i32 (i32)(0x80000000) +#define min_i64 (i64)(0x8000000000000000) + +#define max_i8 (i32)(0x7F) +#define max_i16 (i32)(0x7FFF) +#define max_i32 (i32)(0x7FFFFFFF) +#define max_i64 (i64)(0x7FFFFFFFFFFFFFFF) + +#define Bit_0 (1 << 0) +#define Bit_1 (1 << 1) +#define Bit_2 (1 << 2) +#define Bit_3 (1 << 3) +#define Bit_4 (1 << 4) +#define Bit_5 (1 << 5) +#define Bit_6 (1 << 6) +#define Bit_7 (1 << 7) + +#define Bit_8 (1 << 8) +#define Bit_9 (1 << 9) +#define Bit_10 (1 << 10) +#define Bit_11 (1 << 11) +#define Bit_12 (1 << 12) +#define Bit_13 (1 << 13) +#define Bit_14 (1 << 14) +#define Bit_15 (1 << 15) + +#define Bit_16 (1 << 16) +#define Bit_17 (1 << 17) +#define Bit_18 (1 << 18) +#define Bit_19 (1 << 19) +#define Bit_20 (1 << 20) +#define Bit_21 (1 << 21) +#define Bit_22 (1 << 22) +#define Bit_23 (1 << 23) + +#define Bit_24 (1 << 24) +#define Bit_25 (1 << 25) +#define Bit_26 (1 << 26) +#define Bit_27 (1 << 27) +#define Bit_28 (1 << 28) +#define Bit_29 (1 << 29) +#define Bit_30 (1 << 30) +#define Bit_31 (1 << 31) + + +// +// Resolution Defines +// + +#define DEFAULT_WIDTH 1366 +#define DEFAULT_HEIGHT 768 + +f32 visible_units = 20.f; +f32 unit_over_pixel = visible_units / DEFAULT_WIDTH; +f32 pixel_over_unit = DEFAULT_WIDTH / visible_units; +f32 visible_top = DEFAULT_HEIGHT * unit_over_pixel; + +f32 x_origin_p = 120.f; +f32 y_origin_p = DEFAULT_HEIGHT / 2.f; + +f32 x_origin_u = x_origin_p * unit_over_pixel; +f32 y_origin_u = y_origin_p * unit_over_pixel; + +// TODO(allen): I want these in a "dev-global" section +// where they can be edited, exported, and seen by any +// module. +i32 lanes_per_rail = 5; +i32 total_lanes = lanes_per_rail * 9; +i32 half_visible_lanes = 2; +i32 total_visible_lanes = 2*half_visible_lanes + 1; + + +// +// Asset Backup Defines +// + +// NOTE(allen): The maximum number of back up +// files to keep in existence in the backup +// folder. If this limit is exceeded the +// oldest backup is thrown out to make room. +#define MAX_BACKUP_COUNT 100 + +// NOTE(allen): The number of seconds between +// backup events. +#define BACKUP_FREQUENCY 120.f + + + +#endif + +// BOTTOM diff --git a/test_data/lots_of_files/cd_game_renderer.cpp b/test_data/lots_of_files/cd_game_renderer.cpp new file mode 100644 index 0000000..345cf81 --- /dev/null +++ b/test_data/lots_of_files/cd_game_renderer.cpp @@ -0,0 +1,164 @@ +/* + * Implementation of render target for debug rendering + */ + +// TOP + +#include "cd_random.h" +#include "cd_world.h" + +struct Game_Renderer_Vars{ + Partition part; + b32 initialized; +}; + +inline void +do_quad(u32 texture_id, vec4 color, + vec2 center, vec2 half_x, vec2 half_y, + f32 s, f32 t){ + vec2 p; + + glBindTexture(GL_TEXTURE_2D, texture_id); + glColor4f(color.r, color.g, color.b, color.a); + glBegin(GL_QUADS); + { + p = center - half_x + half_y; + glTexCoord2f(0.f, t); + glVertex2f(p.x, p.y); + + p = center + half_x + half_y; + glTexCoord2f(s, t); + glVertex2f(p.x, p.y); + + p = center + half_x - half_y; + glTexCoord2f(s, 0.f); + glVertex2f(p.x, p.y); + + p = center - half_x - half_y; + glTexCoord2f(0.f, 0.f); + glVertex2f(p.x, p.y); + } + glEnd(); +} + +Render_Execute_Sig(game_render_execute){ + Game_Renderer_Vars *vars = (Game_Renderer_Vars*)target->memory; + + if (!vars->initialized){ + vars->initialized = true; + vars->part = make_partition(target->memory, target->memory_size); + push_type(&vars->part, Game_Renderer_Vars); + } + + Transform me_to_pixel = target->me_to_pixel_transform; + + Partition *part = &vars->part; + Temp_Memory temp = begin_temp(part); + + f32 *lane_y = push_array(part, f32, total_lanes); + for (i32 i = 0; i < total_lanes; ++i){ + lane_y[i] = -5000.f; + } + + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + + Render_Command_Header *h; + for (h = (Render_Command_Header*)target->push_part.base; + h->type; + h = NextHeader(h)){ + switch (h->type){ + case render_lane: + { + Render_Lane *l = (Render_Lane*)(h + 1); + lane_y[l->lane_index] = l->y_position; + }break; + + case render_obstacle: + { + Render_Obstacle *o = (Render_Obstacle*)(h + 1); + Random_Series randgen = random_seed(o->seed); + Image *image = 0; + u32 image_id = 0; + + f32 theta = random_between(&randgen, 0.0f, 0.3f); + theta *= theta * o->x; + theta += random_between(&randgen, 0.f, 6.2f); + if (random_unilateral(&randgen) > 0.7f){ + theta *= -1.f; + } + + f32 scale = 0.5f; + + switch (o->type){ + case ET_Obstacle: + image_id = ASTEROID1 + random_choice(&randgen, 6); + scale = 0.4f; + break; + } + + image = get_image(bank, image_id); + if (image){ + vec2 center = v2(o->x, lane_y[o->lane]); + vec2 half_x = v2(image->width*scale, 0); + vec2 half_y = v2(0, image->height*scale); + f32 s = image->tex_x; + f32 t = image->tex_y; + + center = do_transform(center, me_to_pixel); + + Basis rotation = get_rotation(theta); + half_x = do_basis(half_x, rotation); + half_y = do_basis(half_y, rotation); + + do_quad(image->texture_id, v4(1.f, 1.f, 1.f, 1.f), + center, half_x, half_y, s, t); + } + }break; + + case render_image: + { + Render_Image *i = (Render_Image*)(h + 1); + Image *image = get_image(bank, i->image_id); + if (image){ + vec2 center = i->transform.pos; + vec2 half_x = i->transform.basis.x; + vec2 half_y = i->transform.basis.y; + f32 s = image->tex_x; + f32 t = image->tex_y; + + center = do_transform(center, me_to_pixel); + + do_quad(image->texture_id, v4(1.f, 1.f, 1.f, 1.f), + center, half_x, half_y, s, t); + } + }break; + } + } + + end_temp(temp); + + // NOTE(allen): In dev mode multiple renderers may + // be used in each frame so clearing the depth buffer + // when everything is done keeps everything in order. +#ifdef DEVELOPER + glClearDepth(1.f); +#endif +} + +Render_Get_Functions_Sig(target_get_functions){ + target->execute = game_render_execute; + target->init = init; + target->display = display; + target->set_screen = set_screen; +} + +Bank_Get_Functions_Sig(bank_get_functions){ + bank->begin_setup = begin_setup; + bank->end_setup = end_setup; + bank->bind_font = bind_font; + bank->bind_bmp = bind_bmp; + bank->replace_bmp = replace_bmp; +} + +// BOTTOM diff --git a/test_data/lots_of_files/cd_gl_defines.h b/test_data/lots_of_files/cd_gl_defines.h new file mode 100644 index 0000000..e5355b7 --- /dev/null +++ b/test_data/lots_of_files/cd_gl_defines.h @@ -0,0 +1,65 @@ + +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +typedef BOOL wglChoosePixelFormatARB_Function(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); + +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +typedef HGLRC wglCreateContextAttribsARB_Function(HDC hDC, HGLRC hshareContext, const int *attribList); + +typedef const char* wglGetExtensionsStringARB_Function(HDC hdc); diff --git a/test_data/lots_of_files/cd_hierarchy_editor.cpp b/test_data/lots_of_files/cd_hierarchy_editor.cpp new file mode 100644 index 0000000..872964c --- /dev/null +++ b/test_data/lots_of_files/cd_hierarchy_editor.cpp @@ -0,0 +1,844 @@ +/* + +Development tool: Hierarchy editor +-Allen +28.06.2016 + +*/ + + +// TOP + +static void +extend_hierarchy_vars(System_API *system, Partition *part, Hierarchy_Vars *vars, i32 new_assets){ + void *new_mem = push_array(part, Editor_Asset_State, new_assets); + if (new_mem == 0){ + DBG_expand_partition(system, part, new_assets*sizeof(Editor_Asset_State)); + new_mem = push_array(part, Editor_Asset_State, new_assets); + } + Assert(new_mem); + cd_memset(new_mem, 0, new_assets*sizeof(Editor_Asset_State)); + vars->asset_count += new_assets; +} + +static void +make_hierarchy_vars(System_API *system, Partition *part, Hierarchy_Vars *vars, i32 asset_count){ + extend_hierarchy_vars(system, part, vars, asset_count); + vars->asset_states = (Editor_Asset_State*)part->base; + vars->backup_timer = BACKUP_FREQUENCY*.5f; +} + +static void +clear_details(Slot_Details *details){ + cd_memset(details, 0, sizeof(*details)); +} + +static void +full_sibling_insert_before(Asset_Node *node, Asset_Node *pos, void *manifest_memory){ + i32 pos_parent_self = pos->parent; + Asset_Node *pos_parent = to_node_ptr(pos_parent_self, manifest_memory); + rptr32 pos_self = to_rptr32(pos, manifest_memory); + + // NOTE(allen): If we just say "sibling_insert_before" it will + // not update the "first_child" of pos_parent. So if we happen + // to be inserting before first here, we actually want to use + // an insert under call which can adjust the parent's first child. + if (pos_parent->first_child == pos_self){ + insert_under(node, pos_parent, manifest_memory, true); + } + else{ + sibling_insert_before(node, pos, manifest_memory); + } +} + +static b32 +gui_do_text_field(GUI *gui, GUI_State *state, String *str, u32 flags){ + b32 result = false; + + if (gui_do_command(gui, gui_make_text_field(str, flags))){ + GUI_id id = guid_1(str); + if ((flags & TextField_Selectable) && + (flags & TextField_StaticString)){ + if (guid_eq(state->selected, id)){ + result = true; + } + } + else{ + if (guid_eq(state->activated, id)){ + result = true; + } + } + } + + return(result); +} + +static b32 +gui_do_image_preview(GUI *gui, GUI_State *state, i32 image_id){ + b32 result = false; + + if (gui_do_command(gui, gui_make_image_preview(image_id))){ + result = true; + } + + return(result); +} + +static b32 +gui_do_deselect(GUI *gui, GUI_State *state){ + b32 result = false; + + if (gui_do_command(gui, gui_make_deselect())){ + result = true; + } + + return(result); +} + +static String type_name_strs[AssetType_TypeCount]; + +static void +hierarchy_editor_step(System_API *system, Partition *part, Asset_Bank *bank, + Render_Target *ed_target, Dev_Input_State *dev_input, + Hierarchy_Vars *vars, Partition *manifest_part, Partition *edit_part, + vec2 start_pos, f32 descend, f32 gui_alpha){ + Asset_Manifest *manifest_memory = (Asset_Manifest*)manifest_part->base; + + // TODO(allen): Could stand to metaprogram this + if (type_name_strs[0].str == 0){ + for (i32 i = 0; i < AssetType_TypeCount; ++i){ + String s = {0}; + + switch (i){ + case AssetType_GenericFolder: { + s = make_lit_string("Generic Folder"); + }break; + + case AssetType_Image: { + s = make_lit_string("Image"); + }break; + + case AssetType_ObstacleType: { + s = make_lit_string("Obstacle"); + }break; + } + + type_name_strs[i] = s; + } + } + + i32 new_asset_count = manifest_memory->asset_node_count - vars->asset_count; + if (new_asset_count > 0){ + extend_hierarchy_vars(system, edit_part, vars, new_asset_count); + } + vars->asset_states = (Editor_Asset_State*)edit_part->base; + + f32 indent = 16.f; + f32 height = 22.f; + f32 hit_margin = 5.f; + f32 v_gap = 5.f; + f32 half_v_gap = 2.5f; + f32 width = 400.f; + + // orange + vec3 gui_color_1 = v3(1.f, 0.75f, 0.f); AllowLocal(gui_color_1); + vec3 gui_color_2 = v3(1.f, 0.5f, 0.f); AllowLocal(gui_color_2); + vec3 gui_color_3 = v3(1.f, 0.25f, 0.f); AllowLocal(gui_color_3); + // purple + vec3 gui_color_4 = v3(1.f, 0.f, 0.25f); AllowLocal(gui_color_4); + vec3 gui_color_5 = v3(1.f, 0.f, 0.5f); AllowLocal(gui_color_5); + vec3 gui_color_6 = v3(1.f, 0.f, 0.75f); AllowLocal(gui_color_6); + // indigo + vec3 gui_color_7 = v3(0.25f, 0.f, 1.f); AllowLocal(gui_color_7); + vec3 gui_color_8 = v3(0.5f, 0.f, 1.f); AllowLocal(gui_color_8); + vec3 gui_color_9 = v3(0.75f, 0.f, 1.f); AllowLocal(gui_color_9); + // cyan + vec3 gui_color_10 = v3(0.f, 0.25f, 1.f); AllowLocal(gui_color_10); + vec3 gui_color_11 = v3(0.f, 0.5f, 1.f); AllowLocal(gui_color_11); + vec3 gui_color_12 = v3(0.f, 0.75f, 1.f); AllowLocal(gui_color_12); + // teal + vec3 gui_color_13 = v3(0.f, 1.f, 0.75f); AllowLocal(gui_color_13); + vec3 gui_color_14 = v3(0.f, 1.f, 0.5f); AllowLocal(gui_color_14); + vec3 gui_color_15 = v3(0.f, 1.f, 0.25f); AllowLocal(gui_color_15); + // mint + vec3 gui_color_16 = v3(0.25f, 1.f, 0.f); AllowLocal(gui_color_16); + vec3 gui_color_17 = v3(0.5f, 1.f, 0.f); AllowLocal(gui_color_17); + vec3 gui_color_18 = v3(0.75f, 1.f, 0.f); AllowLocal(gui_color_18); + + Temp_Memory temp = begin_temp(part); + + i32 op_max = 2; + i32 op_count = 0; + Tree_Operation *operations = push_array(part, Tree_Operation, op_max); + + vec2 info_pos = start_pos; + + info_pos.y -= descend; + + b32 has_drop = (dev_input->drop_count > 0); + i32 mx = dev_input->input.mx; + i32 my = dev_input->input.my; + + f32 drag_offset_x = 0; + f32 drag_offset_y = 0; + + Asset_Walker walker = {0}; + Asset_Node *node = walk_first_asset_node(manifest_memory, &walker); + for (; node != 0; ){ + Editor_Asset_State *edit_state = &vars->asset_states[node->image_id-1]; + + i32 level = walker.current_level; + + vec2 p0 = info_pos; + vec2 p1 = {0}; + vec2 pn = info_pos; + + pn.y -= height + v_gap; + p0.x += indent*level; + + if (vars->dragged_asset == node->image_id){ + drag_offset_x = mx - vars->dragged_offset_x - p0.x; + drag_offset_y = my - vars->dragged_offset_y - p0.y; + vars->dragged_level = level; + } + else if (level <= vars->dragged_level){ + drag_offset_x = 0; + drag_offset_y = 0; + } + + p0.x += drag_offset_x; + p0.y += drag_offset_y; + + p1.x = p0.x + width; + p1.y = p0.y - height; + if (p1.y < 0) break; + + f32 alpha_mult = 0.5f; + f32 above_alpha_mult = 0.f; + f32 below_alpha_mult = 0.f; + + f32_rect above_rect = v4(p0.x, p0.y, p1.x, p0.y + v_gap); + f32_rect slot_rect = v4(p0.x, p1.y, p1.x, p0.y); + f32_rect below_rect = v4(p0.x, pn.y, p1.x, p1.y); + + f32_rect above_target = v4(p0.x, p0.y - hit_margin, p1.x, p0.y + half_v_gap); + f32_rect hit_target = v4(p0.x, p1.y + hit_margin, p1.x, p0.y - hit_margin); + f32_rect below_target = v4(p0.x, p1.y - half_v_gap, p1.x, p1.y + hit_margin); + + b32 below_is_first_child = false; + + if (node->first_child && !edit_state->collapsed){ + below_is_first_child = true; + below_rect.x0 += indent; + below_rect.x1 += indent; + } + + enum{ + hit_none, + hit_above, + hit_direct, + hit_below + }; + + i32 hit_type = hit_none; + if (hit_check(mx, my, above_target)){ + above_alpha_mult = 1.f; + hit_type = hit_above; + } + else if (hit_check(mx, my, hit_target)){ + alpha_mult = 1.f; + hit_type = hit_direct; + } + else if (hit_check(mx, my, below_target)){ + below_alpha_mult = 1.f; + hit_type = hit_below; + } + + i32 pos_id = node->image_id; + + if (vars->clicked_asset){ + if (vars->clicked_asset == node->image_id && hit_type == hit_direct){ + if (btn_released(dev_input->input.left_button)){ + edit_state->collapsed = !edit_state->collapsed; + } + } + } + + else if (vars->right_clicked_asset){ + if (vars->right_clicked_asset == node->image_id && hit_type == hit_direct){ + if (btn_released(dev_input->input.right_button)){ + vars->detail_asset = node->image_id; + clear_details(&vars->details); + } + } + } + + else if (vars->dragged_asset){ + if (vars->dragged_asset != node->image_id){ + if (!btn_down(dev_input->input.left_button)){ + if (hit_type != hit_none){ + Assert(op_count + 2 <= op_max); + + i32 asset_id = vars->dragged_asset; + + i32 op_count_original = op_count; + operations[op_count++] = make_op(TreeOp_RemoveSubtree, asset_id, 0); + switch (hit_type){ + case hit_above: + { + if (pos_id == ROOT){ + op_count = op_count_original; + } + else{ + operations[op_count++] = + make_op(TreeOp_InsertSiblingSubtreeBefore, asset_id, pos_id); + } + }break; + + case hit_direct: + { + operations[op_count++] = + make_op(TreeOp_InsertLastChildSubtree, asset_id, pos_id); + }break; + + case hit_below: + { + if (below_is_first_child){ + operations[op_count++] = + make_op(TreeOp_InsertFirstChildSubtree, asset_id, pos_id); + } + else{ + if (pos_id == ROOT){ + op_count = op_count_original; + } + else{ + operations[op_count++] = + make_op(TreeOp_InsertSiblingSubtreeAfter, asset_id, pos_id); + } + } + }break; + } + } + } + } + } + + else if (has_drop){ + u32 count = dev_input->drop_count; AllowLocal(count); + Dev_File_Drop *drops = dev_input->drops; + u32 i = 0; + + String name = make_string_slowly(drops[i].name); + String file_name = front_of_directory(name); + + switch (hit_type){ + case hit_direct: + { + String replace_name = make_string_slowly(node->name); + + b32 do_replace = false; + + if (match(replace_name, file_name)){ + asset_file_write(system, name.str, file_name.str); + do_replace = true; + } + else{ + if (!asset_file_exists(system, file_name.str)){ + if (asset_file_write(system, name.str, file_name.str)){ + asset_file_delete(system, replace_name.str); + do_replace = true; + } + } + } + + if (do_replace){ + bank->replace_bmp(system, bank, part, + drops[i].name, node->image_id); + replace_image(manifest_memory, file_name.str, node->image_id); + } + }break; + + case hit_above: + { + if (!asset_file_exists(system, file_name.str)){ + Assert(op_count + 1 <= op_max); + Assert(pos_id <= manifest_memory->asset_node_count); + operations[op_count++] = make_op(TreeOp_NewNodeBefore, 0, pos_id, file_name.str); + asset_file_write(system, name.str, file_name.str); + } + }break; + + case hit_below: + { + if (!asset_file_exists(system, file_name.str)){ + Assert(op_count + 1 <= op_max); + if (below_is_first_child){ + i32 child_id = get_image_id(node->first_child, manifest_memory); + Assert(child_id <= manifest_memory->asset_node_count); + operations[op_count++] = make_op(TreeOp_NewNodeBefore, 0, child_id, file_name.str); + asset_file_write(system, name.str, file_name.str); + } + else{ + Assert(pos_id <= manifest_memory->asset_node_count); + operations[op_count++] = make_op(TreeOp_NewNodeAfter, 0, pos_id, file_name.str); + asset_file_write(system, name.str, file_name.str); + } + } + }break; + } + } + + else if (btn_down(dev_input->input.letter['R'])){ + switch (hit_type){ + case hit_direct: + { + if (btn_pressed(dev_input->input.left_button)){ + vars->dragged_asset = node->image_id; + vars->dragged_offset_y = my - p0.y; + vars->dragged_offset_x = mx - p0.x; + } + }break; + } + } + + else{ + switch (hit_type){ + case hit_direct: + { + if (btn_pressed(dev_input->input.left_button)){ + vars->clicked_asset = node->image_id; + } + else if (btn_pressed(dev_input->input.right_button)){ + vars->right_clicked_asset = node->image_id; + } + }break; + + case hit_above: + { + if (btn_pressed(dev_input->input.left_button)){ + Assert(op_count + 1 <= op_max); + Assert(pos_id <= manifest_memory->asset_node_count); + operations[op_count++] = make_op(TreeOp_NewNodeBefore, 0, pos_id); + } + }break; + + case hit_below: + { + if (btn_pressed(dev_input->input.left_button)){ + Assert(op_count + 1 <= op_max); + if (below_is_first_child){ + i32 child_id = get_image_id(node->first_child, manifest_memory); + Assert(child_id <= manifest_memory->asset_node_count); + operations[op_count++] = make_op(TreeOp_NewNodeBefore, 0, child_id); + } + else{ + Assert(pos_id <= manifest_memory->asset_node_count); + operations[op_count++] = make_op(TreeOp_NewNodeAfter, 0, pos_id); + } + + } + }break; + } + } + + if (vars->clicked_asset == node->image_id || + vars->right_clicked_asset == node->image_id){ + alpha_mult = clamp_bottom(0.8f, alpha_mult); + } + + switch (node->type){ + case AssetType_GenericFolder: + { + do_dbg_rectangle(ed_target, slot_rect, v4(gui_color_17, gui_alpha*alpha_mult)); + }break; + + case AssetType_Image: + { + do_dbg_rectangle(ed_target, slot_rect, v4(gui_color_2, gui_alpha*alpha_mult)); + }break; + + case AssetType_ObstacleType: + { + do_dbg_rectangle(ed_target, slot_rect, v4(gui_color_8, gui_alpha*alpha_mult)); + }break; + } + do_dbg_rectangle(ed_target, above_rect, v4(gui_color_1, gui_alpha*above_alpha_mult)); + do_dbg_rectangle(ed_target, below_rect, v4(gui_color_1, gui_alpha*below_alpha_mult)); + + vec2 c = {p0.x + 5.f, p1.y + 3.f}; + + if (node->first_child){ + if (edit_state->collapsed){ + do_string(ed_target, bank, DBG_FONT, c, + "+", 1, + white); + } + else{ + do_string(ed_target, bank, DBG_FONT, c, + "-", 1, + white); + } + c.x += 10.f; + } + + do_string(ed_target, bank, DBG_FONT, c, + node->name, cd_strlen(node->name), + white); + + info_pos.y -= height + v_gap; + + if (edit_state->collapsed){ + node = walk_skip_children_asset_node(manifest_memory, &walker); + } + else{ + node = walk_next_asset_node(manifest_memory, &walker); + } + } + + if (!btn_down(dev_input->input.left_button)){ + vars->clicked_asset = 0; + vars->dragged_asset = 0; + } + + if (!btn_down(dev_input->input.right_button)){ + vars->right_clicked_asset = 0; + } + + Tree_Operation *op = operations; + for (i32 i = 0; i < op_count; ++i, ++op){ + i32 image_id = op->id; + i32 pos_id = op->pos_id; + Asset_Node *node = get_node(image_id, manifest_memory); + Asset_Node *pos = get_node(pos_id, manifest_memory); + char *str = op->str; + + switch (op->type){ + case TreeOp_RemoveSubtree: + { + tree_remove(node, manifest_memory); + }break; + + case TreeOp_InsertLastChildSubtree: + { + insert_under(node, pos, manifest_memory, false); + }break; + + case TreeOp_InsertFirstChildSubtree: + { + insert_under(node, pos, manifest_memory, true); + }break; + + case TreeOp_InsertSiblingSubtreeBefore: + { + full_sibling_insert_before(node, pos, manifest_memory); + }break; + + case TreeOp_InsertSiblingSubtreeAfter: + { + sibling_insert_after(node, pos, manifest_memory); + }break; + + case TreeOp_NewNodeBefore: + { + Asset_Node *new_node = get_available_node(system, manifest_part, &manifest_memory); + // NOTE(allen): We need to recompute pointers after calls to get_available_node because + // can potentially rebase all the nodes. Perhaps we should eliminate the Asset_Node* + // parameters from the public interface to avoid this bug. + Asset_Node *pos = get_node(pos_id, manifest_memory); + if (str){ + replace_image(manifest_memory, str, new_node->image_id); + } + full_sibling_insert_before(new_node, pos, manifest_memory); + }break; + + case TreeOp_NewNodeAfter: + { + Asset_Node *new_node = get_available_node(system, manifest_part, &manifest_memory); + // NOTE(allen): We need to recompute pointers after calls to get_available_node because + // can potentially rebase all the nodes. Perhaps we should eliminate the Asset_Node* + // parameters from the public interface to avoid this bug. + Asset_Node *pos = get_node(pos_id, manifest_memory); + if (str){ + replace_image(manifest_memory, str, new_node->image_id); + } + sibling_insert_after(new_node, pos, manifest_memory); + }break; + } + } + + if (vars->detail_asset){ + Slot_Details *details = &vars->details; + + Asset_Node *node = get_node(vars->detail_asset, manifest_memory); + Editor_Asset_State *edit_state = + &vars->asset_states[node->image_id-1]; + AllowLocal(edit_state); + + if (!details->initialized){ + details->initialized = true; + cd_memcpy(details->name_field_space, node->name, sizeof(node->name)); + details->name_field = make_string_slowly(details->name_field_space); + details->name_field.memory_size = sizeof(node->name); + + details->type_name = make_fixed_width_string(details->type_name_space); + copy(&details->type_name, type_name_strs[node->type]); + } + + GUI gui = make_gui(part, 1024); + GUI_State *state = &details->state; + + // + // GUI declaration + // + + { + b32 doing_radio_buttons = false; + if (gui_do_text_field(&gui, state, &details->type_name, + TextField_Selectable | TextField_StaticString)){ + doing_radio_buttons = true; + vars->do_type_radio_buttons = true; + } + + if (vars->do_type_radio_buttons){ + i32 set_type = AssetType_TypeCount; + for (i32 i = 0; + i < AssetType_TypeCount; + ++i){ + if (gui_do_text_field(&gui, state, &type_name_strs[i], TextField_StaticString)){ + set_type = i; + } + } + + if (set_type < AssetType_TypeCount){ + vars->do_type_radio_buttons = false; + + b32 good_to_set = true; + + if (set_type == AssetType_Image){ + if (asset_file_exists(system, details->name_field.str)){ + good_to_set = false; + } + } + + if (good_to_set){ + node->type = set_type; + copy(&details->type_name, type_name_strs[set_type]); + } + } + } + + vars->do_type_radio_buttons = doing_radio_buttons; + + switch (node->type){ + case AssetType_GenericFolder: + { + if (gui_do_text_field(&gui, state, &details->name_field, TextField_Selectable)){ + cd_memcpy(node->name, details->name_field_space, sizeof(node->name)); + } + }break; + + case AssetType_Image: + { + if (gui_do_text_field(&gui, state, &details->name_field, TextField_Selectable)){ + terminate_with_null(&details->name_field); + if (!asset_file_exists(system, details->name_field.str)){ + String ext = file_extension(details->name_field); + + if (match(ext, "bmp")){ + if (asset_file_write(system, node->name, details->name_field.str)){ + asset_file_delete(system, node->name); + cd_memcpy(node->name, details->name_field.str, sizeof(node->name)); + } + } + } + } + gui_do_image_preview(&gui, state, node->image_id); + }break; + + case AssetType_ObstacleType: + { + if (gui_do_text_field(&gui, state, &details->name_field, TextField_Selectable)){ + cd_memcpy(node->name, details->name_field_space, sizeof(node->name)); + } + }break; + } + } + + vec2 p = start_pos + v2(width*1.5f, -height*2); + + // + // GUI execution + // + + { + GUI_State *state = &details->state; + b32 reload = false; + if (btn_pressed(dev_input->input.left_button)){ + state->hot = guid_zero(); + } + if (btn_released(dev_input->input.left_button)){ + state->selected = guid_zero(); + reload = true; + } + + state->activated = guid_zero(); + + GUI_Command *command = gui.commands; + state->activated = guid_zero(); + for (i32 i = 0; + i < gui.count; + ++i, ++command){ + switch (command->type){ + case guicom_text_field: + { + String *edit_str = command->text_field.edit_str; + + vec2 p0 = p; + vec2 p1 = p0 + v2(width, -height); + f32_rect rect = v4(p0.x, p1.y, p1.x, p0.y); + p.y -= height; + + vec3 color = gui_color_3; + f32 alpha_mult = 0.5f; + + if (command->text_field.flags == TextField_StaticString){ + color = gui_color_10; + } + + GUI_id id = guid_1(edit_str); + + if (hit_check(mx, my, rect)){ + alpha_mult = 1.f; + if (btn_pressed(dev_input->input.left_button)){ + state->hot = id; + } + else if (btn_released(dev_input->input.left_button) && guid_eq(state->hot, id)){ + state->hot = guid_zero(); + if (command->text_field.flags & TextField_Selectable){ + state->selected = id; + } + else{ + state->activated = id; + } + } + } + + if (guid_eq(state->hot, id)){ + alpha_mult = clamp_bottom(0.9f, alpha_mult); + } + + if (guid_eq(state->selected, id)){ + color = gui_color_4; + alpha_mult = 1.f; + + if (!(command->text_field.flags & TextField_StaticString)){ + i32 count = dev_input->keys.count; + for (i32 j = 0; + j < count; + ++j){ + char key = dev_input->keys.events[j]; + if (key == key_back){ + if (edit_str->size > 0){ + --edit_str->size; + terminate_with_null(edit_str); + } + } + else if (key == '\n'){ + state->activated = id; + state->selected = guid_zero(); + } + else if (key >= ' ' && key <= '~'){ + if (edit_str->size+1 < edit_str->memory_size){ + append(edit_str, key); + terminate_with_null(edit_str); + } + } + } + } + } + + do_dbg_rectangle(ed_target, rect, v4(color, gui_alpha*alpha_mult)); + + vec2 c = {p0.x + 5.f, p1.y + 3.f}; + do_string(ed_target, bank, DBG_FONT, c, + edit_str->str, edit_str->size, + white); + }break; + + case guicom_image_preview: + { + i32 image_id = command->image_preview.image_id; + + f32 max_w = width; + f32 max_h = height*10; + + f32 w = height; + f32 h = height; + + Image *image = get_image(bank, image_id); + if (image){ + w = image->width; + h = image->height; + + f32 ratio_w = 1.; + f32 ratio_h = 1.; + + if (w > max_w){ + ratio_w = max_w / w; + } + if (h > max_h){ + ratio_h = max_h / h; + } + + f32 ratio = ratio_w; + if (ratio > ratio_h){ + ratio = ratio_h; + } + + w *= ratio; + h *= ratio; + } + + vec2 p0 = p; + vec2 p1 = p + v2(w, -h); + f32_rect rect = v4(p0.x, p1.y, p1.x, p0.y); + + p = v2(p0.x, p1.y); + + vec2 x_axis = v2(w*.5f, 0); + vec2 y_axis = v2(0, h*.5f); + + do_dbg_rectangle(ed_target, rect, v4(black3, gui_alpha)); + do_image_general(ed_target, bank, 0, image_id, + Render_Exact, p0, v2(-1.f, 1.f), + x_axis, y_axis); + }break; + + case guicom_deselect: + { + state->selected = guid_zero(); + }break; + } + } + + if (reload){ + if (guid_eq(state->selected, guid_zero()) && + guid_eq(state->activated, guid_zero())){ + clear_details(details); + } + } + } + } + + end_temp(temp); + + vars->backup_timer -= dev_input->input.dt; + if (vars->backup_timer <= 0.f){ + vars->backup_timer = BACKUP_FREQUENCY; + asset_file_backup(system); + } +} + +// BOTTOM + + diff --git a/test_data/lots_of_files/cd_hierarchy_editor.h b/test_data/lots_of_files/cd_hierarchy_editor.h new file mode 100644 index 0000000..d5100e5 --- /dev/null +++ b/test_data/lots_of_files/cd_hierarchy_editor.h @@ -0,0 +1,231 @@ +/* + +Development tool: Hierarchy editor +-Allen +28.06.2016 + +*/ + + +// TOP + +#ifndef CD_HIERARCHY_EDITOR_H +#define CD_HIERARCHY_EDITOR_H + +// +// GUI +// + +struct GUI_id{ + u64 id[2]; +}; + +inline GUI_id +guid_zero(){ + GUI_id id = {0}; + return(id); +} + +inline GUI_id +guid_1(u64 x){ + GUI_id id = {x, 0}; + return(id); +} + +inline GUI_id +guid_1(void *p){ + GUI_id id = {(u64)p, 0}; + return(id); +} + +inline GUI_id +guid_2(u64 x, u64 y){ + GUI_id id = {x, y}; + return(id); +} + +inline GUI_id +guid_2(u64 x, void *y){ + GUI_id id = {x, (u64)y}; + return(id); +} + +inline GUI_id +guid_2(void *x, u64 y){ + GUI_id id = {(u64)x, y}; + return(id); +} + +inline GUI_id +guid_2(void *x, void *y){ + GUI_id id = {(u64)x, (u64)y}; + return(id); +} + +inline b32 +guid_eq(GUI_id a, GUI_id b){ + b32 result = false; + if (a.id[0] == b.id[0] && + a.id[1] == b.id[1]){ + result = true; + } + return(result); +} + +enum{ + guicom_text_field, + guicom_image_preview, + guicom_deselect +}; + +enum{ + TextField_StaticString = 0x1, + TextField_Selectable = 0x2 +}; + +struct GUI_Command{ + i32 type; + union{ + struct{ + String *edit_str; + u32 flags; + } text_field; + struct{ + i32 image_id; + } image_preview; + }; +}; + +struct GUI{ + GUI_Command *commands; + i32 count, max; +}; + +struct GUI_State{ + GUI_id activated; + GUI_id selected; + GUI_id hot; +}; + +inline GUI +make_gui(Partition *part, i32 max){ + GUI gui; + gui.commands = push_array(part, GUI_Command, max); + gui.count = 0; + gui.max = max; + return(gui); +} + +inline b32 +gui_do_command(GUI *gui, GUI_Command command){ + b32 result = false; + if (gui->count < gui->max){ + gui->commands[gui->count++] = command; + result = true; + } + return(result); +} + +inline GUI_Command +gui_make_text_field(String *str, u32 flags){ + GUI_Command command = {0}; + command.type = guicom_text_field; + command.text_field.edit_str = str; + command.text_field.flags = flags; + return(command); +} + +inline GUI_Command +gui_make_image_preview(i32 image_id){ + GUI_Command command = {0}; + command.type = guicom_image_preview; + command.image_preview.image_id = image_id; + return(command); +} + +inline GUI_Command +gui_make_deselect(){ + GUI_Command command = {0}; + command.type = guicom_deselect; + return(command); +} + + +// +// Control Variables +// + +struct Slot_Details{ + b32 initialized; + + char name_field_space[ASSET_MAX_NAME]; + String name_field; + + char type_name_space[ASSET_MAX_NAME]; + String type_name; + + GUI_State state; +}; + +struct Editor_Asset_State{ + b32 collapsed; +}; + +struct Hierarchy_Vars{ + Editor_Asset_State *asset_states; + i32 asset_count; + + i32 clicked_asset; + + i32 dragged_asset; + f32 dragged_offset_x; + f32 dragged_offset_y; + i32 dragged_level; + + i32 right_clicked_asset; + i32 detail_asset; + + f32 backup_timer; + + Slot_Details details; + b32 do_type_radio_buttons; +}; + +// +// Tree Operations +// + +struct Tree_Operation{ + i32 type; + i32 id; + i32 pos_id; + char *str; +}; + +enum Tree_Operation_Type{ + TreeOp_RemoveSubtree, + TreeOp_InsertLastChildSubtree, + TreeOp_InsertFirstChildSubtree, + TreeOp_InsertSiblingSubtreeBefore, + TreeOp_InsertSiblingSubtreeAfter, + TreeOp_NewNodeBefore, + TreeOp_NewNodeAfter +}; + + +inline Tree_Operation +make_op(i32 type, i32 id, i32 pos_id){ + Tree_Operation op = {type, id, pos_id, 0}; + return(op); +} + +inline Tree_Operation +make_op(i32 type, i32 id, i32 pos_id, char *str){ + Tree_Operation op = {type, id, pos_id, str}; + return(op); +} + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_ids.h b/test_data/lots_of_files/cd_ids.h new file mode 100644 index 0000000..d1b23d4 --- /dev/null +++ b/test_data/lots_of_files/cd_ids.h @@ -0,0 +1,42 @@ + +enum Dbg_Font_ID{ + DBG_FONT = 1, + DBG_MONO_FONT, + DBG_FONT_COUNT_ +}; + +#define DBG_FONT_COUNT (DBG_FONT_COUNT_ - 1) + +#define DBG_IMAGE_COUNT 0 + + +#define FONT_COUNT 0 + +enum Image_ID{ + ROOT = 1, + + BACKGROUND, + + SHEILA, + + HUD_FOLDER, + HUD_DECK, + HUD_DISTANCE_METER_PC, + HUD_DISTANCE_METER_LY, + HUD_FUEL_VELOCITY, + HUD_INDICATOR_OFF, + HUD_INDICATOR_ON, + HUD_NUMBERS, + HUD_SLIDER, + + ASS_TREE_ODD, + ASTEROID1, + ASTEROID2, + ASTEROID3, + ASTEROID4, + ASTEROID5, + ASTEROID6, + IMAGE_COUNT_ +}; + +#define IMAGE_COUNT (IMAGE_COUNT_ - 1) diff --git a/test_data/lots_of_files/cd_intrin.h b/test_data/lots_of_files/cd_intrin.h new file mode 100644 index 0000000..22374c7 --- /dev/null +++ b/test_data/lots_of_files/cd_intrin.h @@ -0,0 +1,96 @@ +/* + * Small functions that probably want to be sped up + * on a per platform basis in the future. + */ + +// TOP + +// NOTE(allen): GOAL, +// Let's be libc independent someday! +// Then we'll be totally badass. + +#include <math.h> +#include <string.h> + +f32 +COS(f32 x){ + return(cosf(x)); +} + +f32 +SIN(f32 x){ + return(sinf(x)); +} + +f32 +POW(f32 b, f32 e){ + return(powf(b, e)); +} + +void +cd_memset(void *mem, int val, int size){ + memset(mem, val, size); +} + +void +cd_memcpy(void *dst, void *src, int size){ + memcpy(dst, src, size); +} + +void +cd_memmove(void *dst, void *src, int size){ + memmove(dst, src, size); +} + +int +cd_strlen(char *s){ + char *b = s; + while (*s) ++s; + return ((int)(s - b)); +} + +f32 +lerp(f32 a, f32 t, f32 b){ + return(a + (b - a)*t); +} + +// TODO(allen): Reduce these equations out +// so that we can do it all in one expression. +f32 +slerp(f32 a, f32 t, f32 b){ + f32 a2 = lerp(a, 0.25f, b); + f32 b2 = lerp(a, 0.75f, b); + + f32 f1 = lerp(a, t, a2); + f32 f2 = lerp(a2, t, b2); + f32 f3 = lerp(b2, t, b); + + f32 g1 = lerp(f1, t, f2); + f32 g2 = lerp(f2, t, f3); + + f32 r = lerp(g1, t, g2); + + return(r); +} + +f32 +clamp(f32 a, f32 t, f32 b){ + if (t < a) t = a; + else if (t > b) t = b; + return(t); +} + +f32 +clamp_bottom(f32 a, f32 t){ + if (t < a) t = a; + return(t); +} + +f32 +clamp_top(f32 t, f32 b){ + if (t > b) t = b; + return(t); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_meta_asset_bank.h b/test_data/lots_of_files/cd_meta_asset_bank.h new file mode 100644 index 0000000..218f246 --- /dev/null +++ b/test_data/lots_of_files/cd_meta_asset_bank.h @@ -0,0 +1,34 @@ +/* + +Meta Asset data format definition +-Allen +15.07.2016 + +*/ + +// TOP + +#ifndef CD_META_ASSET_BANK_H +#define CD_META_ASSET_BANK_H + +#define METAASSET_VERSION 1 + +struct Proto_Obstacle{ + in32_t image_bank; +}; + +struct Meta_Asset_Bank{ + Proto_Obstacle *proto_obstacles; + i32 proto_obstacle_max_id; +}; + +struct Meta_Asset_Setup{ + Meta_Asset_Bank *bank; + Partition *trans_part; + System_API *system; +}; + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_partition.h b/test_data/lots_of_files/cd_partition.h new file mode 100644 index 0000000..5f3814c --- /dev/null +++ b/test_data/lots_of_files/cd_partition.h @@ -0,0 +1,175 @@ +/* + * Memory allocator: Partition + */ + +// TOP + +#ifndef CD_PARTITION_H +#define CD_PARTITION_H + +struct Relative_Partition{ + i32 pos, max; +}; + +struct Partition{ + Relative_Partition rel_part; + void *base; +}; + +struct Wrapped_Partition{ + Relative_Partition *rel_part; + void *base; +}; + +Partition +make_partition(void *mem, i32 size){ + Partition result; + result.base = mem; + result.rel_part.max = size; + result.rel_part.pos = 0; + return(result); +} + +Relative_Partition +make_relative_partition(i32 size){ + Relative_Partition result; + result.max = size; + result.pos = 0; + return(result); +} + +Wrapped_Partition +make_wrapped_partition(Relative_Partition *part, void *base){ + Wrapped_Partition wrapped; + wrapped.rel_part = part; + wrapped.base = base; + return(wrapped); +} + +void +clear_partition(Relative_Partition *part){ + part->pos = 0; +} + +void* +push_partition(Relative_Partition *part, void *base, i32 size){ + char *ptr = 0; + if (part->pos + size <= part->max){ + ptr = (char*)base; + ptr += part->pos; + part->pos += size; + } + return(ptr); +} + +void* +push_partition(Partition *part, i32 size){ + return(push_partition(&part->rel_part, part->base, size)); +} + +void* +push_partition(Wrapped_Partition *part, i32 size){ + return(push_partition(part->rel_part, part->base, size)); +} + +// NOTE(allen): alignment should always be a power of two. +void +align_partition(Relative_Partition *part, i32 alignment){ + part->pos = (part->pos + (alignment-1)) & (~(alignment-1)); +} + +void +align_partition(Partition *part, i32 alignment){ + align_partition(&part->rel_part, alignment); +} + +void +align_partition(Wrapped_Partition *part, i32 alignment){ + align_partition(part->rel_part, alignment); +} + +Relative_Partition +sub_partition(Relative_Partition *part, i32 size){ + Relative_Partition result = {0}; + void *base = 0; + align_partition(part, 8); + base = push_partition(part, 0, size); + if (base){ + result.max = size; + } + return(result); +} + +Partition +sub_partition(Relative_Partition *part, void *base, i32 size){ + Partition result = {}; + align_partition(part, 8); + result.base = push_partition(part, base, size); + if (result.base){ + result.rel_part.max = size; + } + return(result); +} + +Partition +sub_partition(Partition *part, i32 size){ + return(sub_partition(&part->rel_part, part->base, size)); +} + +i32 +get_pos(Relative_Partition *part){ + return(part->pos); +} + +i32 +get_pos(Partition *part){ + return(part->rel_part.pos); +} + +#define push_block(p, size) push_partition(p, size) +#define push_type(p, type) (type*)push_partition(p, sizeof(type)) +#define push_array(p, type, size) (type*)push_partition(p, size*sizeof(type)) + +struct Temp_Memory{ + Relative_Partition *part; + i32 pos; +}; + +Temp_Memory +begin_temp(Relative_Partition *part){ + Temp_Memory temp; + temp.part = part; + temp.pos = part->pos; + return(temp); +} + +Temp_Memory +begin_temp(Partition *part){ + Temp_Memory temp = begin_temp(&part->rel_part); + return(temp); +} + +Temp_Memory +begin_temp(Wrapped_Partition *part){ + Temp_Memory temp = begin_temp(part->rel_part); + return(temp); +} + +void +end_temp(Temp_Memory temp){ + temp.part->pos = temp.pos; +} + +void* +part_malloc(i32 size, void *user_data){ + void *result = push_partition((Partition*)user_data, size); + return(result); +} + +#define STBTT_malloc(s,u) part_malloc(s,u) +#define STBTT_free(d,u) do{ (void)d; (void)u; }while(0) + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_random.h b/test_data/lots_of_files/cd_random.h new file mode 100644 index 0000000..f99430c --- /dev/null +++ b/test_data/lots_of_files/cd_random.h @@ -0,0 +1,584 @@ + +#ifndef CD_RANDOM_H +#define CD_RANDOM_H + +// NOTE(allen): Borrowed from handmade hero code +// This should be considered temporary. Replace +// when it becomes important, or before we ship. + +#define MaxRandomNumber 0x05f5c21f +#define MinRandomNumber 0x000025a0 + +static u32 +RandomNumberTable[] = { + 0x4f0143b, 0x3402005, 0x26f2b01, 0x22796b6, 0x57343bb, 0x2d9954e, 0x06f9425, 0x1789180, + 0x57d8fab, 0x5365d9c, 0x0e9ec55, 0x2a623e0, 0x366e05d, 0x3759f45, 0x1b4d151, 0x35a5411, + 0x59e734b, 0x211c9e4, 0x1b0df4d, 0x50d423d, 0x3b18f5b, 0x5066bed, 0x0aa03be, 0x2b66c73, + 0x21a0ea3, 0x50d821c, 0x4628aed, 0x2456259, 0x2a178ad, 0x4b3bf40, 0x0185588, 0x5ecf044, + 0x48a6c64, 0x5585c49, 0x0bf0dae, 0x03aeb87, 0x0677916, 0x0aca65a, 0x42534d2, 0x226d712, + 0x03713ed, 0x4a5afa1, 0x4f2f545, 0x30dff53, 0x0d98c9d, 0x4b43caa, 0x54cfe7a, 0x5c5a54e, + 0x4816e78, 0x30f9b01, 0x110677f, 0x44c0f3e, 0x03ee6b3, 0x1ce05d2, 0x1beca77, 0x3011e81, + 0x1c0e238, 0x27039e5, 0x15242d4, 0x0386766, 0x10a7455, 0x5f2ea75, 0x4291cb8, 0x2b33e48, + 0x12405ca, 0x40bd964, 0x3792675, 0x4c38386, 0x204dce0, 0x41023d7, 0x0f70768, 0x43a95f7, + 0x16280f9, 0x42566bd, 0x160d0b5, 0x2b6dcfd, 0x390b16d, 0x41768ff, 0x26ed0f2, 0x2d906f1, + 0x1ade480, 0x44f6e26, 0x0c71504, 0x2a605c8, 0x4cf4128, 0x3983aa1, 0x2d20133, 0x4c677ca, + 0x014eed0, 0x3c94d24, 0x2bec8fc, 0x1006c95, 0x0a57f1a, 0x16d2803, 0x1fe5fd5, 0x2560dfe, + 0x03a77e0, 0x1ae35cc, 0x2e15cee, 0x45c1621, 0x4a8e6a2, 0x53b3af4, 0x16b64db, 0x2602c93, + 0x31fa92e, 0x3137d85, 0x4351ce9, 0x03ca2db, 0x4b7a27e, 0x28e3846, 0x289e3b4, 0x3e9a0fe, + 0x041fc34, 0x2400db4, 0x5740222, 0x072c880, 0x5330621, 0x305fac0, 0x30bba4c, 0x1d1bcc0, + 0x46e3749, 0x27f3a6b, 0x51dfd7e, 0x1b99797, 0x078f825, 0x0899824, 0x3d87dfb, 0x1506d51, + 0x41b2e1b, 0x5a7e30b, 0x42b665d, 0x40c1412, 0x2dfcfd6, 0x0f64f3f, 0x2effce1, 0x181eda6, + 0x0f7d529, 0x481718b, 0x4e872c9, 0x22ce1cc, 0x55269e9, 0x0e69c54, 0x0992a2f, 0x0e1cd99, + 0x3bcf0ed, 0x0dbc5f2, 0x2e58639, 0x2a8846f, 0x2737a5d, 0x3d5fc85, 0x1c1fc2d, 0x29ce3d6, + 0x4f8ee3f, 0x426dc27, 0x223db0e, 0x3e0d8d5, 0x5a28a7a, 0x2587f0a, 0x36a54cf, 0x23887bb, + 0x22cc929, 0x595e059, 0x33fed84, 0x4f7042e, 0x311c187, 0x430f215, 0x3ab4245, 0x2116700, + 0x30186b9, 0x5312195, 0x300262d, 0x35e2b65, 0x5280730, 0x1365bcd, 0x466dc33, 0x22bf5cc, + 0x34a3fb1, 0x172fb52, 0x0a9cde5, 0x142f318, 0x2731995, 0x58eb3bd, 0x1693e13, 0x25a9cf1, + 0x05c235e, 0x08a601d, 0x2432351, 0x2061136, 0x422f956, 0x33fba4a, 0x5c76788, 0x5b0387f, + 0x0e3a118, 0x4c4f4f0, 0x49726f7, 0x4600fb2, 0x284a1fc, 0x47d625f, 0x4a6127d, 0x18b4cc6, + 0x2d69328, 0x04a79b2, 0x59705c2, 0x550efbe, 0x3d43be5, 0x21d316b, 0x4e872d3, 0x34b4339, + 0x5ab279c, 0x31ab38f, 0x28dfd4a, 0x46c8148, 0x3222416, 0x299c85d, 0x0ff8604, 0x5b86120, + 0x3fe7e78, 0x0c421d7, 0x4c8dab6, 0x25e37ee, 0x42e2c78, 0x1ba3101, 0x1d26b60, 0x557e4bf, + 0x5c48926, 0x38b258a, 0x44728e4, 0x482e0dd, 0x4434fa2, 0x2fce17b, 0x350211d, 0x023370f, + 0x0f6657f, 0x23d4f4c, 0x3b18d0c, 0x3b1e0b6, 0x1dcdd39, 0x034761f, 0x4df128d, 0x2945257, + 0x460e7e4, 0x1ce4d80, 0x0a8e6a2, 0x5eb784b, 0x0853730, 0x2ea3a05, 0x09b717b, 0x5ce8362, + 0x5360703, 0x06c1609, 0x51604cf, 0x17e01d2, 0x40bf132, 0x32a9935, 0x15aa9d8, 0x4912351, + 0x51f7652, 0x027aa0a, 0x24addd6, 0x3d2c538, 0x3a7d54a, 0x40adb17, 0x0fdb245, 0x557c53d, + 0x4eb9775, 0x2c95a30, 0x4b3d29d, 0x468d608, 0x5bf85f6, 0x33de0cb, 0x394ca9e, 0x4d7166f, + 0x3cb570d, 0x3b7b3c9, 0x5292bfb, 0x09d2662, 0x0510f39, 0x3267933, 0x53a5fe9, 0x0873a43, + 0x59e9de2, 0x0b5c941, 0x24f78c4, 0x54fa1c8, 0x0da6aed, 0x4df5f5d, 0x1ea3ccc, 0x3ac14aa, + 0x40d25fd, 0x331a3ae, 0x5257579, 0x0030e95, 0x14995ad, 0x4879608, 0x3c8bbf4, 0x2715173, + 0x03654fd, 0x3767b2f, 0x3b90be2, 0x44fd28e, 0x023618d, 0x2bdcfdb, 0x0231780, 0x0bf178c, + 0x3166bab, 0x57327db, 0x203c21e, 0x40fc81f, 0x35ff817, 0x39f114f, 0x5ae772c, 0x3e6591d, + 0x5a192a5, 0x05b9ee7, 0x3349792, 0x34b012d, 0x2c0ddbf, 0x27aba82, 0x30ceeec, 0x2ec25f9, + 0x1aae3f5, 0x2773ffb, 0x196102d, 0x4bf0762, 0x42b929f, 0x354d296, 0x220ee6b, 0x3fc80c0, + 0x120d332, 0x01b7679, 0x2690014, 0x0563acf, 0x502ff24, 0x14cbc08, 0x2f6db6d, 0x4b741ac, + 0x39fa332, 0x20bcf75, 0x4b9b111, 0x0c8595a, 0x3d95fb4, 0x33bb9a1, 0x08932ee, 0x53bf013, + 0x2f2df9d, 0x1e70ce1, 0x4b13619, 0x4078eb6, 0x3ffc3ce, 0x5e56f4c, 0x3d456ca, 0x3130295, + 0x3c69442, 0x152dfc8, 0x0808c9b, 0x3bc2876, 0x141ac03, 0x1a962ea, 0x0baaa86, 0x48715cf, + 0x2910d74, 0x5023627, 0x0460e1e, 0x1ad8724, 0x1e402fc, 0x1892985, 0x0c039f6, 0x2d83a4a, + 0x1947c37, 0x3dd189a, 0x407d540, 0x02e9ef6, 0x5dc7b05, 0x0623d84, 0x47df5b4, 0x37b191f, + 0x39bc842, 0x2a89dcc, 0x444809e, 0x009b253, 0x106b1b6, 0x56bf97c, 0x35cd1a7, 0x5713178, + 0x52ce61b, 0x13ca28c, 0x186ab5f, 0x53a6c2e, 0x42e5924, 0x4214750, 0x407ab66, 0x199dc40, + 0x10bfae2, 0x54592e8, 0x4c9bfad, 0x589ac54, 0x2315075, 0x411068c, 0x1431637, 0x05de9e5, + 0x3365f7f, 0x55408fa, 0x4306140, 0x0a3e8e6, 0x27eff3b, 0x441f9ad, 0x1e746ba, 0x478f925, + 0x24848e3, 0x5709a30, 0x5a610f9, 0x31ae341, 0x2c460a3, 0x49fd92c, 0x505237d, 0x4a8cf5e, + 0x4c00f7a, 0x3dec4eb, 0x3c290ec, 0x3fe1a17, 0x0f28ff9, 0x0f6cda4, 0x14126bb, 0x519748e, + 0x2a051bc, 0x1a9e8c4, 0x169f87c, 0x0b0d11b, 0x212d2bd, 0x11819c3, 0x3b6dbcb, 0x4f0e0af, + 0x2ac62ef, 0x5e75958, 0x0ef9f89, 0x4bce875, 0x5008515, 0x301e851, 0x4e8dda8, 0x52ade69, + 0x466ec50, 0x117a672, 0x257437a, 0x43bcb00, 0x1f8f710, 0x4a54cfc, 0x08aeb47, 0x3433312, + 0x334d0ad, 0x349e3bb, 0x5847e7c, 0x2ae4a79, 0x4a69da5, 0x587e061, 0x31c06ee, 0x3303bc9, + 0x5349932, 0x2100ca8, 0x04bf772, 0x10e268d, 0x2105aee, 0x5bf4955, 0x02b5161, 0x49d4df2, + 0x167617c, 0x45c4246, 0x3bc5717, 0x5f38db2, 0x21e208d, 0x1232c86, 0x05df3f1, 0x3674470, + 0x068c79f, 0x32a1868, 0x229f61f, 0x179843b, 0x3865c40, 0x430cb20, 0x31fbff7, 0x26d64fa, + 0x27ee976, 0x03f0c7c, 0x265d3f8, 0x50d6255, 0x1a125ac, 0x1bd06aa, 0x53c4196, 0x0439728, + 0x4c46f9e, 0x0200d95, 0x1f76335, 0x1ad8da2, 0x523da1c, 0x12d7ace, 0x10bc9fe, 0x1689423, + 0x49f47bb, 0x549700b, 0x02f54e6, 0x3787067, 0x463b32b, 0x18db1c0, 0x44c4097, 0x5616172, + 0x23359d3, 0x0945145, 0x3a2d2b8, 0x50fddaf, 0x2f8e2e8, 0x5c6f839, 0x25d0091, 0x4ad3e06, + 0x53203f9, 0x2ef93a8, 0x4932a1a, 0x19f4bbd, 0x1ab9a75, 0x44c480a, 0x4e806c1, 0x1d57648, + 0x353faa0, 0x0d7ea94, 0x0808d11, 0x4aded6e, 0x4525c09, 0x36d4ee6, 0x463203d, 0x53049ac, + 0x1307e79, 0x1f4f1e9, 0x291bb6a, 0x055f1f9, 0x159b03c, 0x5945b54, 0x5bfbf5e, 0x251de9e, + 0x560b32d, 0x1ae529b, 0x49a4dd5, 0x4dd2a5d, 0x4ac261b, 0x4d48086, 0x5dd4289, 0x177a133, + 0x457495c, 0x2adbd76, 0x3776da4, 0x5275e1a, 0x1ad0e27, 0x3403f32, 0x082f3ea, 0x51f8f64, + 0x44dbfad, 0x4da1d15, 0x1bd8194, 0x0b98547, 0x0730172, 0x3d7d457, 0x09d0dad, 0x38fae32, + 0x17601bc, 0x21c440c, 0x50ba349, 0x44842ed, 0x0cb28e6, 0x4bceec7, 0x28f183f, 0x5952a31, + 0x2c98a95, 0x2359477, 0x401429c, 0x1052dd0, 0x04b42e7, 0x26654a2, 0x3f6418e, 0x25ef7be, + 0x0290f29, 0x175e101, 0x3cb28c0, 0x206dd81, 0x3909ab0, 0x441b637, 0x46e2bf5, 0x5c3b27b, + 0x2150cb5, 0x00418a4, 0x47a91b6, 0x02ce251, 0x23530c9, 0x072f631, 0x1394da2, 0x156e035, + 0x2e43c6f, 0x53129ef, 0x235c077, 0x51fb2d4, 0x20261fc, 0x1d8d8ce, 0x427b68c, 0x34805ef, + 0x24032e2, 0x2cdb326, 0x450f713, 0x5a9df2b, 0x4e0a5cb, 0x4d1bbbc, 0x0196e47, 0x3efc5b5, + 0x2a3fc7d, 0x1a1a983, 0x3b18b91, 0x290aedc, 0x3533ee7, 0x3bff002, 0x21b4683, 0x364e3b9, + 0x5ab4d51, 0x3d624b0, 0x332231e, 0x25af8dd, 0x24cb453, 0x18a5460, 0x12238b7, 0x37d5df2, + 0x24b7d60, 0x44db9ee, 0x4a71e48, 0x0472775, 0x0338867, 0x2b3c1c9, 0x2c5a402, 0x518ba40, + 0x3f92fc3, 0x04201c2, 0x4b1d69d, 0x3d56e30, 0x3552214, 0x13429e6, 0x3407619, 0x01beea0, + 0x5a8df4f, 0x5805a61, 0x4df5178, 0x0266082, 0x40df736, 0x0757347, 0x47d1d5f, 0x27695d3, + 0x00a81d5, 0x3759bc2, 0x12f3c66, 0x594fd93, 0x5c29d37, 0x363acd6, 0x1b4558c, 0x4abbda0, + 0x1127991, 0x35ab84c, 0x537fa78, 0x1f71ab8, 0x0e4adb2, 0x2293c6e, 0x361af30, 0x0483560, + 0x31d07c4, 0x0dd01bd, 0x3468e4f, 0x2012173, 0x4175b07, 0x38e8998, 0x3f39436, 0x451ac7d, + 0x10331b2, 0x4bde6fd, 0x03fe204, 0x47463cd, 0x5381bdd, 0x054b077, 0x2a17deb, 0x3d1b711, + 0x5ab1679, 0x490d81f, 0x37b3c0a, 0x12272c3, 0x3ba19f4, 0x2df475c, 0x2486527, 0x40e9c52, + 0x0fbd0e5, 0x272211c, 0x47fdfc3, 0x4cf2c1d, 0x56284eb, 0x44ee47d, 0x2409bfd, 0x02c2230, + 0x1be6f72, 0x3fc3c3d, 0x28f32d8, 0x3ed1670, 0x3f0cf4e, 0x4a68dd4, 0x2463da5, 0x44a3cd1, + 0x068f53d, 0x12315f5, 0x4410f0d, 0x2c846e0, 0x0e715e8, 0x249bab5, 0x21654a7, 0x511bb18, + 0x0985f31, 0x0ebc851, 0x0a33373, 0x5800fab, 0x549af43, 0x3055655, 0x30c152b, 0x0350664, + 0x5514e58, 0x2a119c1, 0x01fa149, 0x508b86e, 0x0e79d10, 0x2ac25a0, 0x0f8e26b, 0x18747c0, + 0x013e8be, 0x40b811f, 0x4f4ad82, 0x4f5a3c3, 0x3dd208a, 0x4904312, 0x5358f23, 0x4f123df, + 0x529dbf7, 0x3645edd, 0x0f2d9a7, 0x42e4890, 0x2cfbc18, 0x0b8d389, 0x061914a, 0x3e92dd2, + 0x2f54d4f, 0x4532db3, 0x4db46e0, 0x2baed0e, 0x555f307, 0x2278251, 0x1d82f56, 0x3f4b42a, + 0x13b2af5, 0x312cc8e, 0x36854bf, 0x0f9910e, 0x2d5b4e3, 0x21d3b41, 0x43d99b0, 0x0a94fa7, + 0x322ae0b, 0x2df0db5, 0x1f80b21, 0x4d493a2, 0x45b20f4, 0x059b9e5, 0x22d1e03, 0x4519213, + 0x34b6dc2, 0x0461c6b, 0x52bdd26, 0x4caec56, 0x06cc872, 0x4e6220f, 0x186e013, 0x352941d, + 0x1dcc7ed, 0x087b4c0, 0x5293c91, 0x2b3cc2f, 0x56ac502, 0x074cae5, 0x46ae019, 0x4463c0a, + 0x206727a, 0x24e5b66, 0x24837ae, 0x11be471, 0x4a82337, 0x09a27c7, 0x2ac6bdb, 0x432379e, + 0x4cf0a3c, 0x2c0b675, 0x457a53c, 0x380fa67, 0x057cb76, 0x4755c19, 0x2514f73, 0x229ed49, + 0x20a83ed, 0x4bed1fd, 0x0f25a96, 0x400f9fc, 0x572a6ac, 0x5d77a13, 0x118c6f1, 0x3d3a972, + 0x44517c0, 0x11a7445, 0x499f406, 0x3f1a984, 0x4c3eee7, 0x2437116, 0x1b6e3f5, 0x33be8b4, + 0x04a22e8, 0x24f774b, 0x58dc8e4, 0x3b60d45, 0x47fa696, 0x21a46c3, 0x4d933d3, 0x16e5692, + 0x00b754d, 0x132d07c, 0x1e107a1, 0x14e3e1a, 0x5be670f, 0x5f5c21f, 0x03d8a3a, 0x2df919d, + 0x27479d5, 0x12c3314, 0x4a41fcc, 0x1d1469f, 0x5f232ce, 0x2a7eac1, 0x39c8d8e, 0x372070c, + 0x14357eb, 0x4971f2f, 0x493279c, 0x237c448, 0x39cf0b5, 0x5564a0c, 0x519a53d, 0x3acf59e, + 0x4c4daa4, 0x43ed201, 0x0749e80, 0x0d0da35, 0x55ccdd2, 0x1100efa, 0x0f83ba5, 0x10354ae, + 0x4832e9f, 0x0fb02cf, 0x1f29418, 0x206922a, 0x3a4e142, 0x1d76057, 0x12a6457, 0x3a8334a, + 0x478b296, 0x2e3e67a, 0x43f658a, 0x0a78a4f, 0x28154b7, 0x5721f33, 0x0af825d, 0x43d2f9f, + 0x0769af7, 0x20b6a74, 0x1390d47, 0x10c180e, 0x0afa8a8, 0x45573ff, 0x0fa6c2c, 0x2623765, + 0x056bc12, 0x49475f6, 0x490feb2, 0x2383ca4, 0x00f0b2c, 0x4d87c98, 0x36d3c79, 0x2d864c8, + 0x3197be7, 0x1aae8b4, 0x4578a0f, 0x1087340, 0x4c606c9, 0x07d8127, 0x387a7d6, 0x297e540, + 0x51cf247, 0x0a05cda, 0x017c49a, 0x338ae3f, 0x2ef18e8, 0x35b528f, 0x44eca1d, 0x59c4bf8, + 0x48d476a, 0x3b1b4f8, 0x4a2116d, 0x2d902ed, 0x1a8816a, 0x0c43b3e, 0x3e09636, 0x3d2a966, + 0x46dff8f, 0x42f69cc, 0x3ab20b2, 0x4a2451a, 0x5738e33, 0x25aaf7b, 0x333cdd6, 0x5c2330e, + 0x2e0270f, 0x33be448, 0x3491c7d, 0x380d62d, 0x2745d0f, 0x35c7470, 0x09ea28f, 0x122334f, + 0x53b66c7, 0x2c43168, 0x00abffa, 0x31513e5, 0x4e626e5, 0x53951b8, 0x1b58365, 0x4c8304f, + 0x1bfdf22, 0x2d2665a, 0x377a97c, 0x4be7e47, 0x286a4bc, 0x4441582, 0x2910d6f, 0x5ae3fd3, + 0x5bec3bd, 0x05d7a87, 0x3d0e63e, 0x2e25d22, 0x4d94e7c, 0x2060072, 0x32f1602, 0x1fe1b55, + 0x1fbe3f0, 0x2260e5b, 0x5b342f0, 0x521c52e, 0x2987d07, 0x1f1c85e, 0x20e44f9, 0x1f05bca, + 0x169fbac, 0x39175ab, 0x24ec502, 0x16a2fbc, 0x52add2b, 0x0430101, 0x49b0884, 0x0663883, + 0x1d47ce4, 0x5c28141, 0x4edad42, 0x2d72fd4, 0x1d0423c, 0x16be1d4, 0x10d5582, 0x4b4d144, + 0x106a797, 0x0fb2dfd, 0x5872da2, 0x5deb6e7, 0x2d46a52, 0x03fc4c6, 0x465eda0, 0x56a4ab7, + 0x18f0ad6, 0x39922bc, 0x4679687, 0x093cb97, 0x17a9712, 0x2272202, 0x2ef0aac, 0x0ac5a21, + 0x003b7fb, 0x389e091, 0x4c1a086, 0x24dca55, 0x5ea6823, 0x040d9df, 0x0bd0ce2, 0x49d626e, + 0x190c17d, 0x0ff6f70, 0x1979a8f, 0x11186a9, 0x49989cd, 0x5e941d7, 0x4bb7c9e, 0x548365a, + 0x2a79eef, 0x0cb597c, 0x4f51060, 0x1ee9fdc, 0x306d709, 0x55304ab, 0x00fd4ec, 0x3e8d707, + 0x3b9df83, 0x17e89b3, 0x059a797, 0x53ad54f, 0x3f02037, 0x4fb45b0, 0x4c4e5b8, 0x1423eb8, + 0x1653c44, 0x2e38aa4, 0x1e68c2f, 0x13fa069, 0x01ba70a, 0x1f24565, 0x58578d7, 0x27f6cd0, + 0x3087105, 0x130dcfe, 0x4b8b133, 0x3d728e3, 0x29e9d3f, 0x57c89be, 0x2a879bb, 0x5369a2d, + 0x3a9479e, 0x42e6b24, 0x5cc574f, 0x07fc6b2, 0x34524da, 0x0c62ede, 0x2737bd1, 0x37c81a5, + 0x4ff34bb, 0x382dad6, 0x1f1651d, 0x55b5489, 0x12d6551, 0x0dfde3f, 0x327618b, 0x3d94c02, + 0x1a7783a, 0x55f3664, 0x2c1a0f1, 0x2ba90eb, 0x1f1615e, 0x4df5aae, 0x515bb9e, 0x0e76d9e, + 0x236975c, 0x48d18c2, 0x2d45e9a, 0x2a5078c, 0x3ce205a, 0x0331485, 0x3215fbd, 0x02b42ab, + 0x00877cd, 0x525ab2e, 0x42ae7e9, 0x06dbb02, 0x5d152c4, 0x213cebe, 0x241637b, 0x0e25980, + 0x402cb8b, 0x07fcc2c, 0x0458c17, 0x4d8e48c, 0x4034e6c, 0x5722b66, 0x231c2a5, 0x04f0c40, + 0x355b88d, 0x5d66d89, 0x31aa77e, 0x13a6520, 0x30d0a54, 0x1fa46a9, 0x1c3f12a, 0x2265dac, + 0x5a0f196, 0x13301b0, 0x14142b7, 0x3c7fe83, 0x1b69b24, 0x1630d4c, 0x225d6e7, 0x3c21269, + 0x396870e, 0x502f249, 0x062964e, 0x3af2b73, 0x2ece8d0, 0x043d51b, 0x555639e, 0x4f876f3, + 0x4b70f78, 0x4424878, 0x4875ad9, 0x3942259, 0x10b5f05, 0x4a7fc80, 0x10a158a, 0x4e58a20, + 0x2a08e9b, 0x5205495, 0x1080ca8, 0x4218d95, 0x2250695, 0x37ac9f9, 0x52c1e5d, 0x02ea66c, + 0x33c5c62, 0x4c382d1, 0x5a707fd, 0x3950982, 0x1eea02d, 0x110a8b3, 0x01813fd, 0x2c2bd90, + 0x5199fc4, 0x32f1c4a, 0x2ebd3ac, 0x177f817, 0x3b0dab9, 0x57627f2, 0x113ce41, 0x1b807e9, + 0x1163c5a, 0x5c4ff03, 0x4590255, 0x5e6d9d5, 0x11babdb, 0x2e39e58, 0x1b6775f, 0x4be9348, + 0x46d898d, 0x2e39ee0, 0x4255049, 0x23b62a0, 0x49c291d, 0x2ff5448, 0x35ffad9, 0x0b0f112, + 0x287ed0d, 0x2e08afc, 0x02604c6, 0x270d0a7, 0x5e23db0, 0x5f08a67, 0x54b97a5, 0x3cd4b63, + 0x14cd663, 0x5adb834, 0x35efacb, 0x3829409, 0x140af79, 0x57f3ca8, 0x36ef13d, 0x0526051, + 0x3cf7cdc, 0x2cbb0c6, 0x0a45db5, 0x2e24d44, 0x0babb6a, 0x1352ec7, 0x57d50e3, 0x5ea2f2a, + 0x2a5df44, 0x1ece4f3, 0x5cdb1a1, 0x2149271, 0x1c02f14, 0x12422f0, 0x50b1ec3, 0x0bc9ecf, + 0x58b524f, 0x5eafdb1, 0x3051784, 0x10f7027, 0x3e521ea, 0x12bbbed, 0x21d39f3, 0x00b6439, + 0x302a786, 0x5941694, 0x019263b, 0x1143879, 0x0284828, 0x2f0c134, 0x4daa53e, 0x18c79dd, + 0x0ebc04c, 0x41c1f82, 0x4a20774, 0x55aa739, 0x5008c43, 0x5a85f48, 0x132f366, 0x12fa823, + 0x2523e57, 0x50c530d, 0x1101f2b, 0x28fc06e, 0x2db225d, 0x3120204, 0x195ead6, 0x1b91f5f, + 0x3b81036, 0x3463df8, 0x0c841e5, 0x34742e6, 0x05cee33, 0x0023c29, 0x2d01120, 0x52052d9, + 0x2586da2, 0x17a4ff1, 0x144d892, 0x37a1fbd, 0x5728c20, 0x111db68, 0x071c954, 0x1d7d6fb, + 0x22f698f, 0x1b3e4e5, 0x4169e9c, 0x0b5c475, 0x53c61e3, 0x351a99a, 0x289b3e0, 0x5a26009, + 0x332d6bc, 0x09cd1da, 0x2f24c60, 0x1810dea, 0x20eab99, 0x1f82959, 0x242923e, 0x3fa533d, + 0x13c968c, 0x0c5ac4a, 0x4d9663a, 0x567bb0c, 0x3f81dbf, 0x41bbdce, 0x4333674, 0x3e6fee5, + 0x450a4c8, 0x3a4ecfd, 0x4488320, 0x58f2022, 0x0d184bd, 0x5a4fa4a, 0x3983f78, 0x5516b84, + 0x4d746d2, 0x1a547f2, 0x4f868ff, 0x4861717, 0x4433694, 0x3b18db2, 0x4e159eb, 0x40c14e7, + 0x4529e68, 0x22abeab, 0x5445043, 0x1dd9a4f, 0x0fec7ae, 0x5277d41, 0x02bf503, 0x2e8140a, + 0x519fce0, 0x2d938f9, 0x21e97d6, 0x498126b, 0x0a3f2ac, 0x47c418f, 0x39c82dc, 0x009f54e, + 0x3c2d48e, 0x31a2448, 0x3cdeaeb, 0x16f8de8, 0x2c1b9d2, 0x4e96233, 0x2c4d765, 0x22ee45d, + 0x0cf227e, 0x21503ca, 0x1a7c3b2, 0x172106b, 0x2e11b86, 0x5c054f1, 0x16eb4d5, 0x51ad377, + 0x40c8bc9, 0x434022b, 0x010253a, 0x4b0a760, 0x55d6753, 0x302a0da, 0x3dd962b, 0x115900c, + 0x0fc1c65, 0x46c43e4, 0x50dfed3, 0x1165787, 0x5092b58, 0x3786171, 0x0eda969, 0x236358a, + 0x41f64c8, 0x41edd6b, 0x405eb8a, 0x225032e, 0x4f7d171, 0x3747e1e, 0x53f5301, 0x47b4d53, + 0x1f1d387, 0x0c6f2be, 0x33548dc, 0x1e28765, 0x2addc10, 0x25b2638, 0x4a84b79, 0x2bfb8e2, + 0x2fc1423, 0x4df2f7f, 0x303cb5e, 0x32b260c, 0x2461db2, 0x59c634a, 0x5a6228b, 0x4a80950, + 0x1c1f344, 0x081d1fc, 0x0f67ca6, 0x2c47700, 0x27f10e3, 0x5db8801, 0x1ebe356, 0x58f3fcb, + 0x235caae, 0x51c952f, 0x4934e69, 0x54931f0, 0x48594a5, 0x3a446de, 0x21ef011, 0x5e14cf3, + 0x1653647, 0x5792933, 0x0c66660, 0x1417ff9, 0x4e791b3, 0x156a29f, 0x10b5348, 0x12563c8, + 0x1d427ab, 0x38070de, 0x36bdd3e, 0x5beafa9, 0x4d85d26, 0x39d29a8, 0x0f59892, 0x1c65b2f, + 0x08e6f61, 0x2e52ce4, 0x29da15f, 0x530704a, 0x0ae9b5c, 0x09c992a, 0x33dff84, 0x0a856ac, + 0x0191f81, 0x35f9a09, 0x0953d45, 0x04f16da, 0x352d165, 0x35b9362, 0x0113a39, 0x1bef7ea, + 0x3c63385, 0x264b75e, 0x5d3fd04, 0x4e69de6, 0x2fc31f4, 0x231d6b1, 0x597dd7a, 0x114f377, + 0x2703abb, 0x0b1bd8a, 0x2e7f772, 0x1113670, 0x4ea00dc, 0x2732188, 0x52fb19e, 0x1a47221, + 0x0c09d19, 0x1f7acd0, 0x29b7d5a, 0x505e3ee, 0x32dc6eb, 0x40df7a2, 0x3a28279, 0x5be11b7, + 0x30a1702, 0x4b235a6, 0x2f7107b, 0x3b6d6b4, 0x4d703b6, 0x5cc307e, 0x3bb96ce, 0x18842fc, + 0x2b80f8d, 0x004d675, 0x5a269f2, 0x1d12741, 0x004bc4c, 0x03ba065, 0x158dfbb, 0x3397cca, + 0x4726fb0, 0x3c97ac2, 0x536c3c5, 0x114c8d2, 0x17295ec, 0x1063964, 0x31ba123, 0x3657cbb, + 0x07e1f74, 0x44a93de, 0x265eb55, 0x40e5b4f, 0x3336081, 0x3662382, 0x2875f7f, 0x1d09377, + 0x1f326e0, 0x568a3c6, 0x54dbf44, 0x4d0d43a, 0x0bae2be, 0x2b5a2ff, 0x115b4f2, 0x26ebe80, + 0x0f8d4ce, 0x2a7f009, 0x35ee46a, 0x47a18c2, 0x5df011e, 0x281a9b6, 0x0e36737, 0x3fdd7e2, + 0x01a9c19, 0x41061d9, 0x5d9826a, 0x309ab2c, 0x0b3f00f, 0x0e7db39, 0x541b75b, 0x1bfcea9, + 0x26f31a7, 0x3beaba1, 0x0c034db, 0x4bc8fe4, 0x14a65f9, 0x088ac5a, 0x16f95e2, 0x53cfed3, + 0x53cf19b, 0x156d5de, 0x25e2675, 0x2246714, 0x493ceb5, 0x0ba4fe2, 0x510fae0, 0x04dc4e7, + 0x47e37f8, 0x4b24629, 0x395fd7e, 0x1e02c1d, 0x4efd1a1, 0x01acc51, 0x0075994, 0x4d03b84, + 0x0cfbf3a, 0x56299a9, 0x5734fc4, 0x133aff0, 0x3d6eb32, 0x3199114, 0x1b8fd85, 0x0f23e35, + 0x3a9dce4, 0x1c7f542, 0x0d26cae, 0x0e1f666, 0x48c60df, 0x37032bd, 0x2a156ec, 0x4629858, + 0x30ffe9d, 0x379d063, 0x41314f1, 0x00707f2, 0x4b9252a, 0x48a718d, 0x098f886, 0x549d28b, + 0x10c7bc5, 0x0960ae6, 0x3089baf, 0x0343766, 0x5df72de, 0x049efef, 0x0a49fb6, 0x4e16fc3, + 0x34d8ed9, 0x56b4051, 0x1c98f7b, 0x320cbe7, 0x537096b, 0x5441cdf, 0x0766d5d, 0x05ec0d4, + 0x3e08901, 0x0be9487, 0x4a6beb8, 0x5b21477, 0x2bf310b, 0x20820e9, 0x2ef59e9, 0x3103ba8, + 0x42b4919, 0x4a2e097, 0x4af4355, 0x375abfb, 0x149aab4, 0x17edbe6, 0x382af19, 0x09548db, + 0x5ae1343, 0x2f233f4, 0x0bc179e, 0x18ead63, 0x40c3d39, 0x41034cc, 0x34559a4, 0x0c1ff0b, + 0x4db2d93, 0x40b7b3d, 0x4077bca, 0x532d049, 0x044e2b6, 0x06f8b7d, 0x3eecac2, 0x0550672, + 0x55fac47, 0x4137ffc, 0x15d0020, 0x51fbfb9, 0x49bb51e, 0x32c4888, 0x3e457d7, 0x1c15554, + 0x3b504d0, 0x59b284e, 0x0523e86, 0x1be45c0, 0x0eea59e, 0x4f1a038, 0x38bc942, 0x4787c2a, + 0x2ae7f04, 0x5b09bd4, 0x09d1d7b, 0x4d01ac5, 0x4cfbb56, 0x546dc26, 0x5121172, 0x18f834a, + 0x0f6506a, 0x093b76f, 0x009e002, 0x5149ebd, 0x02c9f70, 0x4d34dd8, 0x104ca3c, 0x4a53032, + 0x4877b87, 0x4fcc679, 0x5a36f68, 0x0c32404, 0x5d8096f, 0x3c98b52, 0x44c19cd, 0x03b3427, + 0x32ad880, 0x2b9d5d4, 0x4d9063f, 0x038db59, 0x5079cf4, 0x48dd496, 0x2b6f5be, 0x068c5a7, + 0x247b711, 0x2bd8cf6, 0x165f476, 0x0eae49e, 0x5752063, 0x52a8982, 0x4a5e4cd, 0x36b1b86, + 0x088ddce, 0x3e200a8, 0x2a2abb1, 0x0906b1d, 0x3776b89, 0x4f7dd4e, 0x101059c, 0x507216f, + 0x2e4cfaf, 0x4ce9fed, 0x3ab2fe1, 0x50a12a4, 0x2f6a542, 0x15da5f4, 0x5a9209d, 0x597f4e9, + 0x017f80f, 0x15f399c, 0x0c9ce7c, 0x4654a25, 0x5702612, 0x1c0fb45, 0x1e072f2, 0x4999c66, + 0x2b73cdc, 0x260c545, 0x4ced04d, 0x5045de7, 0x33c3e34, 0x2ee1dea, 0x30c19bc, 0x1e35419, + 0x403fd93, 0x02e005c, 0x0287f38, 0x34444b1, 0x27bfb6a, 0x2a97e3e, 0x26edb95, 0x42cde37, + 0x11f9432, 0x4ba53ab, 0x048b2d5, 0x0d8d449, 0x382b394, 0x07c2f09, 0x319d5be, 0x15c46bf, + 0x464d2a5, 0x427a35d, 0x192adcb, 0x493c695, 0x5a43159, 0x4e48bad, 0x51f279d, 0x25eb340, + 0x1f7ca59, 0x210eb6b, 0x0b5cf49, 0x5c53229, 0x21e78d8, 0x5bdbd2f, 0x4a16c82, 0x224f1dc, + 0x3a35ef6, 0x0d20de4, 0x425694a, 0x42116a8, 0x1b48a8e, 0x41dc509, 0x2c21687, 0x15c1741, + 0x0d136af, 0x5e45cdc, 0x26cd0f8, 0x3f3aa2f, 0x546781e, 0x4db45b3, 0x39d8a30, 0x3adf782, + 0x26f72ae, 0x09896f5, 0x556a834, 0x413d575, 0x016621f, 0x008f315, 0x492a708, 0x0b18a90, + 0x4101314, 0x11946bb, 0x0f6367c, 0x25c70a1, 0x4d70f1a, 0x5ef874d, 0x3da0698, 0x11fc490, + 0x2d458e9, 0x0f1bbf7, 0x0ef33d5, 0x1276a9b, 0x2d3957b, 0x5dd6448, 0x15b393b, 0x4c2a9de, + 0x3512679, 0x0103dea, 0x1e849a1, 0x42f731f, 0x3fd319d, 0x3d99c48, 0x3e48aa1, 0x0b085c3, + 0x43ebf4e, 0x05f6a71, 0x5f2786f, 0x0a92532, 0x47966fe, 0x1f23c5b, 0x5f20fa3, 0x275b32c, + 0x0e0e197, 0x19f1e1c, 0x19ec924, 0x5c0ddeb, 0x1ea49c9, 0x517093b, 0x0f16a7a, 0x1e24ec7, + 0x10f8354, 0x5f374d9, 0x4bcfbfc, 0x1e432c3, 0x5c9185c, 0x4da5f77, 0x3f9fa2b, 0x3d94698, + 0x240e43c, 0x1b98a85, 0x29a5b54, 0x3da5921, 0x0512ecc, 0x2ec2c72, 0x1877392, 0x41c57da, + 0x3710d6e, 0x41c6e39, 0x5909c2c, 0x02462c4, 0x071a8b5, 0x3fe3a12, 0x4fb23b1, 0x44b498f, + 0x38ac507, 0x10c2559, 0x552201f, 0x40a3b55, 0x17b407a, 0x002fa5c, 0x36d3a59, 0x0076841, + 0x547146e, 0x5849a23, 0x0bc2992, 0x3e6db4c, 0x5d07279, 0x3f92719, 0x00f0c87, 0x46b2a13, + 0x4a9042b, 0x4a01ace, 0x2a1666b, 0x17cad59, 0x0055d39, 0x30feaf2, 0x5700758, 0x2076d47, + 0x10b8bd4, 0x03e3540, 0x057868a, 0x30d9a65, 0x0ba2aeb, 0x406eee7, 0x3018d2e, 0x1e88ab8, + 0x458be11, 0x502673e, 0x0b9827f, 0x21c132d, 0x4925635, 0x358346a, 0x013fafb, 0x138ef22, + 0x216f0ef, 0x22e74f4, 0x0fe43ad, 0x39aa6bb, 0x0338527, 0x0417d5a, 0x3f1ade7, 0x436d775, + 0x4f181ee, 0x38a503c, 0x0dab593, 0x2a6cec0, 0x0e7b9aa, 0x3aef7e0, 0x0144735, 0x3f5a541, + 0x1eb0c47, 0x10aaf07, 0x3fa9ce3, 0x1635d30, 0x24422ce, 0x0d9dd30, 0x00a3c9d, 0x27b022a, + 0x489b0dd, 0x20e39a5, 0x486527b, 0x4c9cb62, 0x459e483, 0x0d62620, 0x5db38d3, 0x207c98f, + 0x283eb60, 0x33e2029, 0x2c21207, 0x5331f2b, 0x4cc0677, 0x3b1b78f, 0x2fb0b1e, 0x48147fc, + 0x3216a0a, 0x49fa537, 0x40623bc, 0x1c558c7, 0x51f9c25, 0x0aad7de, 0x3a4fb50, 0x1cdf9e3, + 0x3e146c9, 0x0c8e3ee, 0x1ea0559, 0x325e196, 0x0efa24b, 0x0becebd, 0x2fc0a85, 0x537bfdf, + 0x08d2251, 0x245e670, 0x3a1f40f, 0x38eaa59, 0x2510add, 0x0f8468a, 0x2ce3db7, 0x21f7398, + 0x024e584, 0x08ab8a0, 0x435c955, 0x4ec5229, 0x5747d88, 0x005ef3f, 0x027dbb0, 0x521c97a, + 0x1deddb4, 0x3e24a06, 0x2b9927f, 0x4a6217a, 0x03fe580, 0x1389d0c, 0x29bb437, 0x0af3e95, + 0x3d33a0e, 0x0030be4, 0x51cd89a, 0x059e987, 0x3d930c5, 0x33c0753, 0x2f37e8c, 0x31af0a6, + 0x1b1621f, 0x169f344, 0x4bc0139, 0x0b1975d, 0x58b1e84, 0x28bb873, 0x3446308, 0x4cd9c01, + 0x002616b, 0x1e73446, 0x5f23b53, 0x137271b, 0x0ca6363, 0x40ac879, 0x4bcc1ed, 0x28a13ae, + 0x11b619c, 0x3e9019f, 0x1926006, 0x3a3a54a, 0x3d76168, 0x5267ce1, 0x1967dc6, 0x2d7af89, + 0x51b31c3, 0x53cb5b4, 0x3aaa5e8, 0x12e1d65, 0x3503e5f, 0x5c1e9f2, 0x29b50b0, 0x3384647, + 0x43262de, 0x5c70936, 0x2d807a0, 0x41c5a7c, 0x3fbda03, 0x2c1f1e6, 0x320cf41, 0x2cb2d7d, + 0x1e3920c, 0x4f5a23f, 0x3760d02, 0x4de6f1c, 0x01d4d18, 0x11fb98d, 0x38912a5, 0x5916d40, + 0x04689ef, 0x1a170d1, 0x124c7ab, 0x5e9c9d4, 0x28a8e66, 0x16314d7, 0x4ce82fb, 0x49afb27, + 0x4c951b4, 0x2485f8f, 0x0e35ebf, 0x0ca2f27, 0x3219e4c, 0x48cefe2, 0x46c993c, 0x1571e3c, + 0x4c32bc0, 0x4db864c, 0x532aed3, 0x42508b3, 0x199d00b, 0x2000602, 0x04564fd, 0x4fdc687, + 0x0445b9f, 0x2f604ce, 0x3b31e92, 0x22d0075, 0x326f9af, 0x16f07b4, 0x24a6c34, 0x21c75e0, + 0x166b8ec, 0x0908fd7, 0x2cc406f, 0x25adb93, 0x1f9eb7b, 0x33607f1, 0x18ba21e, 0x05d01ab, + 0x30e6df0, 0x05f82b5, 0x0eb298b, 0x31cae3e, 0x02541b1, 0x5b61ff2, 0x37be9ed, 0x50b2c5b, + 0x4a457f1, 0x1a6effe, 0x4fe6658, 0x52863fa, 0x34d5f48, 0x29e338a, 0x06aa806, 0x48a3a6b, + 0x5590035, 0x073a0d2, 0x47586dc, 0x3ebd552, 0x21f2f22, 0x36f0cb3, 0x0660cfe, 0x1b28067, + 0x02d7822, 0x43cc61c, 0x24945c1, 0x4ff4f56, 0x454457e, 0x39d73f0, 0x524f560, 0x28583ee, + 0x118e11f, 0x09cf8b8, 0x55e5d96, 0x441234c, 0x58b29ca, 0x44b4232, 0x241e2fd, 0x1f4c225, + 0x38dedc6, 0x18fe952, 0x3055b13, 0x1216fb2, 0x4bf3dcd, 0x1c0270e, 0x3f18487, 0x2a519c1, + 0x50c86ab, 0x0718baa, 0x5ae88c9, 0x4d4dde8, 0x0b5b828, 0x58f1e58, 0x0f433aa, 0x06d654a, + 0x55ff991, 0x2831e5c, 0x2857c1b, 0x176a550, 0x13fc35f, 0x06e026a, 0x3cf4df4, 0x36f6362, + 0x5b22e83, 0x56fa2bf, 0x021ee1b, 0x5a27285, 0x0f1be0c, 0x3b55833, 0x2e74fb3, 0x3028d10, + 0x0deaf0d, 0x2c01726, 0x3d813ed, 0x22ff72d, 0x57ef158, 0x5d486e5, 0x0d7a153, 0x290734a, + 0x44b627b, 0x4243d53, 0x1026d7d, 0x283f669, 0x53933f0, 0x18a88c8, 0x00ee52f, 0x2ea0a7e, + 0x3583f7c, 0x3eef362, 0x07d2338, 0x530fc54, 0x311e1d8, 0x2ce65b8, 0x0af845c, 0x1bb021a, + 0x174d03b, 0x05c8034, 0x4c37d70, 0x3836609, 0x042c4a6, 0x357a5fa, 0x3a3d935, 0x130e0e8, + 0x52207a3, 0x5af9e2f, 0x3b04ca9, 0x03e2ff2, 0x144aaeb, 0x09635e2, 0x2fc245b, 0x3df03d7, + 0x2199bdb, 0x004d94c, 0x1133f9a, 0x58be09b, 0x45fd9ca, 0x5e12d33, 0x49fc58f, 0x3d4748d, + 0x36e856c, 0x4668b58, 0x3d642a0, 0x39ae534, 0x554f2f8, 0x4d236b2, 0x21fe4af, 0x06a8e2c, + 0x2306f34, 0x04c1fe2, 0x06aa967, 0x4a257a9, 0x2aee7b2, 0x567d5ab, 0x1d585be, 0x37655c1, + 0x3340948, 0x1544a21, 0x12ab999, 0x10735d0, 0x436fe79, 0x5b82cc8, 0x0c764ea, 0x2ae33c0, + 0x23b3edb, 0x30d46cb, 0x20c88b1, 0x24f3442, 0x41b4d45, 0x4349fc7, 0x4dc93ce, 0x2adb2b5, + 0x028d2c5, 0x4e6d92c, 0x51f4d10, 0x2835b02, 0x5e68106, 0x027e994, 0x58cee93, 0x38e5384, + 0x060e425, 0x5894907, 0x1872a39, 0x5dc97dc, 0x0faf18b, 0x589293e, 0x176dfe7, 0x283b747, + 0x49ee8f7, 0x31ee47c, 0x0b8f55a, 0x34dc5f3, 0x1d0d382, 0x4505caf, 0x1d4949a, 0x210d47c, + 0x15cbb77, 0x2b09d89, 0x2254620, 0x1c8b42b, 0x285d7c5, 0x22e7646, 0x479da15, 0x26db31d, + 0x1517573, 0x3a20e62, 0x2ff3602, 0x126cf4d, 0x1397625, 0x2f8af37, 0x0a7525c, 0x5a0252e, + 0x0215d31, 0x1ed226a, 0x2f039c4, 0x0b59c8b, 0x4691d4a, 0x1a73bc2, 0x243cacf, 0x253bd15, + 0x1cb3243, 0x4c657ec, 0x5070d3a, 0x2594bff, 0x1576253, 0x18c4edb, 0x4ca98d2, 0x5780dcf, + 0x420aee3, 0x514fb01, 0x376d58f, 0x5f0ea00, 0x473f033, 0x390d67e, 0x5ab0869, 0x2a52661, + 0x5ea656e, 0x13f357c, 0x552e1ca, 0x3437bc6, 0x28f04b0, 0x5645d26, 0x3e232a7, 0x4911d25, + 0x2bef435, 0x26805f9, 0x479cbd3, 0x4a83f53, 0x35eb4d4, 0x37382a7, 0x12182da, 0x378ef4e, + 0x5d675bf, 0x4607e73, 0x4563509, 0x34c7b39, 0x30b5f01, 0x102b823, 0x3654c61, 0x011adf6, + 0x1420f9a, 0x52f6dd7, 0x2a29b1c, 0x055e1a2, 0x2a7a96f, 0x5c2219b, 0x11ae3ed, 0x54d00e5, + 0x0d75244, 0x20890de, 0x2386060, 0x4991209, 0x42e3272, 0x486bec1, 0x23c90f5, 0x3a69ab8, + 0x36b8a5a, 0x3035e18, 0x258ceda, 0x26529f3, 0x37ff474, 0x5cfed3e, 0x040bd12, 0x25206cf, + 0x24b0c8f, 0x22f998f, 0x19430df, 0x4ca367d, 0x12d5b26, 0x2d969ba, 0x3c55e74, 0x01c0b7b, + 0x0af26cf, 0x2d7eaf4, 0x48490fd, 0x2df5c47, 0x29e1571, 0x461a426, 0x4cf7183, 0x3b57677, + 0x11800b1, 0x0c35a56, 0x489b742, 0x2bbc17e, 0x32a6534, 0x15357b8, 0x0fce89d, 0x1bf17ca, + 0x0f3f070, 0x5852a68, 0x178a100, 0x1189c2c, 0x4b5d272, 0x0c05add, 0x3f5926d, 0x3f07f3a, + 0x510bae3, 0x1d8bc17, 0x2f58f6c, 0x3d7a38b, 0x1b0e9fb, 0x4a8ae7b, 0x3c5ac87, 0x00fa68f, + 0x16d388d, 0x2fc7d83, 0x3dc492a, 0x462edcf, 0x4a9ea66, 0x08aca0c, 0x3108c65, 0x19d70f7, + 0x5f4e3b2, 0x3f6f2f2, 0x1ec186a, 0x27bd6b1, 0x28be6d0, 0x0275b86, 0x4ededfe, 0x5d2f956, + 0x4618e8b, 0x37073dc, 0x19aa633, 0x0abee93, 0x1077f9c, 0x20cdf77, 0x007f0d8, 0x1799e7a, + 0x2c3178c, 0x0965bac, 0x2d359b0, 0x408f29d, 0x51000f3, 0x3733470, 0x318b9e6, 0x0961a75, + 0x0466429, 0x4488ea9, 0x31fd564, 0x403bdf7, 0x4951dc5, 0x4234b4b, 0x4b65e45, 0x266f19d, + 0x3d2f51d, 0x41fb039, 0x5b8b783, 0x1effbf8, 0x3e165aa, 0x53fa7ee, 0x43ff04a, 0x30e1109, + 0x4196df9, 0x0758778, 0x27f1d0e, 0x5e62f03, 0x31f7a76, 0x20ead9c, 0x0d9541d, 0x4f79664, + 0x18b089b, 0x060c739, 0x4a31ad2, 0x2069d56, 0x36071a8, 0x21c89e0, 0x119784e, 0x4ca88e7, + 0x06db3f2, 0x59d3dc5, 0x127075a, 0x1e4a5b2, 0x2f30268, 0x19dd50b, 0x4e1be7b, 0x4f99dd7, + 0x48d2118, 0x15a39d7, 0x5cd8deb, 0x2e79bcc, 0x595ba41, 0x40d2e17, 0x0bd620b, 0x352d160, + 0x4408ad9, 0x4fad40e, 0x175d3be, 0x1289445, 0x2e52a33, 0x41d222c, 0x246a470, 0x0f89c9b, + 0x08462e1, 0x35adbfc, 0x5898d97, 0x1911448, 0x0bce2ae, 0x33e7bb5, 0x1af7398, 0x44fc995, + 0x44add3a, 0x26d0042, 0x4fb0b56, 0x19aaf0d, 0x43b824b, 0x15556f0, 0x5d661b6, 0x52b2e53, + 0x3173fe1, 0x39c4476, 0x38f65d1, 0x2c586af, 0x499f431, 0x2502169, 0x4bb6cf8, 0x084be40, + 0x4fa7edd, 0x0f8d245, 0x4fa34e4, 0x4afdd6e, 0x11bed30, 0x11304b4, 0x3ef5aa8, 0x36a02e5, + 0x09d37d1, 0x1411abd, 0x3a5cc4d, 0x0c61cea, 0x09a49be, 0x0c090b1, 0x40b3ede, 0x511d625, + 0x227f229, 0x25926cd, 0x05bbea5, 0x5dfdbaa, 0x0576b65, 0x4cbce39, 0x1e4ae16, 0x0175202, + 0x248a07a, 0x53498e7, 0x32f9c52, 0x060dc7b, 0x2211b00, 0x4a33af0, 0x122ad57, 0x046a453, + 0x0ee1ccf, 0x29aacfe, 0x50e5496, 0x056e80e, 0x30b2afa, 0x4e6a1e1, 0x2230263, 0x395c8a5, + 0x4f3a073, 0x50ee280, 0x2797fe8, 0x01fb071, 0x15cc49f, 0x23d0a69, 0x469d6e7, 0x2376b0e, + 0x5f585a8, 0x03170d7, 0x448ae86, 0x22dd643, 0x02da337, 0x1dc0d21, 0x526c0d4, 0x0e2287e, + 0x1691da7, 0x03fa3e0, 0x50d992e, 0x3654049, 0x27bf370, 0x29752b7, 0x00c4f2d, 0x05c3a1b, + 0x2b799ed, 0x1875599, 0x0808bfb, 0x299736c, 0x3c5dd4b, 0x450739a, 0x2098741, 0x4c22dc4, + 0x24c30e0, 0x2eb4a23, 0x1024695, 0x3eb8606, 0x115c3f9, 0x13e2957, 0x211d8bb, 0x516efe9, + 0x270a6f2, 0x2246ff0, 0x16db06d, 0x4c2c4b4, 0x0c45ff7, 0x22a8c31, 0x42a6c6e, 0x4d977aa, + 0x051aeb9, 0x0474ee6, 0x2364861, 0x28b15e4, 0x351be5d, 0x4a04d04, 0x23d2c7c, 0x50e6106, + 0x122973c, 0x53d360a, 0x55ade5a, 0x25353f2, 0x14020b1, 0x07a3fcb, 0x5863185, 0x523b871, + 0x3e4bb40, 0x3b262bf, 0x0b747f8, 0x4b81acd, 0x4d812e2, 0x26451f5, 0x3642987, 0x3e1fba8, + 0x4dfaa75, 0x326a499, 0x067674c, 0x4143e68, 0x5b4442d, 0x4ae0ca3, 0x3eeb4fa, 0x2f0bb35, + 0x4ff1d0a, 0x3f3230d, 0x0ca1ef1, 0x35c5802, 0x4b4a32d, 0x3ba9432, 0x0286f9d, 0x1f04bb7, + 0x512a591, 0x24cef49, 0x330e7d4, 0x1e0835a, 0x56c2990, 0x341eb3c, 0x0afcdf0, 0x0134d7b, + 0x14cd429, 0x05ad768, 0x1f60303, 0x08499ae, 0x286f823, 0x118e722, 0x3bb1e30, 0x0ce88b7, + 0x36d14da, 0x4f4b1e5, 0x0f7bbb1, 0x35dbb0e, 0x50de390, 0x1ead31c, 0x0c60b5b, 0x505ef37, + 0x0335c6a, 0x1f90905, 0x5e4edbd, 0x528dbfa, 0x0748725, 0x4a01ae8, 0x5e259b7, 0x21a8f23, + 0x17d657c, 0x3ed484a, 0x2db3233, 0x1f466a2, 0x5a263c2, 0x48e72f6, 0x5746dad, 0x18198ef, + 0x5d8282b, 0x1287929, 0x24a88a0, 0x37b8c88, 0x2a16281, 0x457cf6d, 0x0146632, 0x3ef2526, + 0x3151eb2, 0x1b7c89f, 0x2c007e3, 0x363e1e9, 0x0598811, 0x327b2d8, 0x5e2e1cc, 0x1836d49, + 0x5661b99, 0x00e71cf, 0x57d9e36, 0x117c5a9, 0x2a8b2d8, 0x089dd30, 0x4c9d4c5, 0x1d0a14b, + 0x339d5c0, 0x52ac0ab, 0x2034390, 0x45d2cf5, 0x3a7ab93, 0x5b5cb76, 0x1a87b61, 0x1cab7da, + 0x3b7e43b, 0x409341c, 0x0e2ef7e, 0x5e60d9c, 0x53f60cb, 0x3f417e2, 0x2a1c002, 0x1034777, + 0x4433f92, 0x551ff6f, 0x01f748c, 0x2090b92, 0x140c8cf, 0x30a0cea, 0x1ace5d4, 0x179f8ce, + 0x5de2004, 0x0786bee, 0x119f9de, 0x00025a0, 0x4f5fb7a, 0x437fd92, 0x1f2cac8, 0x4a7f498, + 0x1fb3cb5, 0x465a4a8, 0x0c3a2a1, 0x0fda1ae, 0x25692f8, 0x5bc8e7a, 0x42168ec, 0x3e645aa, + 0x20e90df, 0x1dc2918, 0x5ab9415, 0x127bb74, 0x5bbd472, 0x2a77df3, 0x0905b81, 0x31bca0b, + 0x41b8f7f, 0x4fabfb9, 0x4eaf6e1, 0x27fc3a8, 0x17dc8db, 0x3bd6d1c, 0x203a431, 0x4842661, + 0x570bb6f, 0x04ea761, 0x17778d6, 0x0b48b9d, 0x5552bb7, 0x49769a2, 0x45dfa75, 0x422ac6a, + 0x576c090, 0x0ca14ca, 0x34c62bc, 0x081751f, 0x453fea1, 0x052905f, 0x4175f03, 0x08520cd, + 0x21d3b04, 0x339d658, 0x55f7280, 0x58371de, 0x3e9deb8, 0x44d451c, 0x1ee94f7, 0x452c2a2, + 0x03e84e9, 0x5d9b039, 0x4a88d26, 0x480e896, 0x42a8c01, 0x3256405, 0x3cf16cd, 0x1d079a7, + 0x54a0f8e, 0x57111cc, 0x0ce29ab, 0x0c13557, 0x57c21bb, 0x116a20e, 0x0d0bedc, 0x283dad5, + 0x3211b90, 0x23458ef, 0x59a3990, 0x4709fc3, 0x3603a2b, 0x48d039f, 0x0292ef8, 0x3bfdc8f, + 0x1cce36d, 0x31ce719, 0x2a62034, 0x5043b61, 0x0cde255, 0x5f03daf, 0x417b014, 0x11206b3, + 0x1889bfd, 0x520bbb3, 0x26dd839, 0x52631d3, 0x153c66e, 0x2a74e83, 0x560a6d3, 0x107b498, + 0x2731c2b, 0x078b567, 0x261e6f5, 0x01e6b1d, 0x5771c11, 0x1bdb1e1, 0x381610e, 0x472d2a2, + 0x1385452, 0x361478b, 0x5933539, 0x36a67b2, 0x01537ed, 0x27dee69, 0x122f9ea, 0x390bb34, + 0x242f98a, 0x414406d, 0x255e470, 0x58f4279, 0x55d565e, 0x5b7cd98, 0x09ff428, 0x5124e1f, + 0x56faf9f, 0x1698174, 0x4c199cc, 0x0f8eb73, 0x245ee3d, 0x1d863b8, 0x5f50e40, 0x151d9a8, + 0x5e0c9c3, 0x3155cb5, 0x1935dd7, 0x3947d51, 0x076deb2, 0x0553866, 0x1957a79, 0x54b0c06, + 0x550dcd1, 0x3f62c0d, 0x0ecd643, 0x54395aa, 0x19ef96e, 0x10110d6, 0x3c915cd, 0x4ca63f8, + 0x051848d, 0x1a97aa2, 0x5526242, 0x202ff5a, 0x104900d, 0x4d00a16, 0x5821221, 0x20ea420, + 0x2c53e99, 0x4863f5a, 0x1087219, 0x321381a, 0x00857fe, 0x0523134, 0x3940a6f, 0x3c896c0, + 0x22b6d33, 0x5c2ea31, 0x5885143, 0x13de62d, 0x019e3d1, 0x50980cb, 0x535f580, 0x2710b06, + 0x290daa2, 0x2e3ac61, 0x5c53610, 0x31dabcf, 0x32c481e, 0x2462a6e, 0x425ea08, 0x3a5c3ab, + 0x032da76, 0x56b1384, 0x1c3a922, 0x2b6238e, 0x408528f, 0x2ce13f1, 0x44824af, 0x4be4469, + 0x55ea833, 0x0a530f1, 0x2c80425, 0x0b80008, 0x19eeb5a, 0x02f5072, 0x24ca737, 0x3b82655, + 0x56007b0, 0x3d2340a, 0x33c376a, 0x44fba52, 0x435129c, 0x1c7cd3a, 0x46ecd77, 0x450619f, + 0x3a08631, 0x3c0b3f0, 0x27fffe7, 0x2b66951, 0x27091ca, 0x36ed4d6, 0x1cccef7, 0x0127f96, + 0x213a171, 0x0e79011, 0x4254f43, 0x0072ad6, 0x5b0923d, 0x47ad6df, 0x0c7fc83, 0x433e3ce, + 0x3d0eeb0, 0x0fa5843, 0x55ad491, 0x1d0a950, 0x22994be, 0x318a858, 0x4aa25d7, 0x25559c2, + 0x05c70af, 0x4a93f93, 0x37fdcb7, 0x24e503f, 0x48241f9, 0x2d22c7a, 0x4376be2, 0x470998c, + 0x54074ca, 0x29be05d, 0x446f05b, 0x5764f7e, 0x41d10fb, 0x5baf355, 0x23838ae, 0x12d6f15, + 0x15590c8, 0x0c2986e, 0x2140fb0, 0x01c8f12, 0x3eb8810, 0x2248264, 0x460f5a9, 0x2652ee5, + 0x219a0ef, 0x51d3bb5, 0x27a86a2, 0x48eb622, 0x2b4d204, 0x5f2d632, 0x478481d, 0x05d90e6, + 0x0720c7d, 0x5cb88c2, 0x2a5b47f, 0x5489f00, 0x2ea2205, 0x043dbdf, 0x1edb2d5, 0x284bf9b, + 0x25e9ab9, 0x20a3847, 0x06d0aab, 0x22614fd, 0x295a577, 0x472c3a7, 0x374321c, 0x15d6854, + 0x198c05a, 0x465a533, 0x49727e9, 0x1cf616a, 0x2d00605, 0x5d96304, 0x2f31d99, 0x0cb084b, + 0x24d1d93, 0x3b60288, 0x280ab4b, 0x55323da, 0x2dfa305, 0x3137fb4, 0x19e7683, 0x45b41bd, + 0x3373692, 0x5cf9888, 0x4f2c3eb, 0x47cdf41, 0x57802ee, 0x3817225, 0x45eb546, 0x03b990c, + 0x3d0ea4a, 0x4a88b6c, 0x1384d7a, 0x25f4043, 0x3893349, 0x3c45d06, 0x0bdb9c8, 0x1ea4134, + 0x4e59115, 0x18ff096, 0x51ec1ce, 0x47a8855, 0x1459e6c, 0x231fab0, 0x47fb36d, 0x1e8699e, + 0x2a1b348, 0x33e69df, 0x28f7c2b, 0x00c7026, 0x53d1bfa, 0x4fc8d68, 0x199ba96, 0x3564a58, + 0x2d6b5fd, 0x51766e2, 0x5c9db35, 0x32f7395, 0x5110f57, 0x427256e, 0x5180888, 0x1e7f590, + 0x0387b10, 0x3d290bd, 0x5210179, 0x346c191, 0x142c379, 0x34fd011, 0x48af6ce, 0x0d7f9c5, + 0x3160d2d, 0x45c87f6, 0x313a3e6, 0x3a47b87, 0x0cd32a6, 0x3b47aaa, 0x026596e, 0x20bc93f, + 0x5591c1b, 0x0478f5a, 0x0a5bec0, 0x2535115, 0x50df790, 0x0da9e58, 0x20b06b3, 0x5f32065, + 0x2454614, 0x4500835, 0x3f4d125, 0x040272b, 0x5000f06, 0x4348fa0, 0x33199a0, 0x3303796, + 0x30e1a5e, 0x5c418da, 0x46471e4, 0x4520823, 0x1bd208d, 0x4dbaacc, 0x4ee2acb, 0x092ff02, + 0x45d8c67, 0x3f7121e, 0x0b023c1, 0x31da2a7, 0x597d3e5, 0x089e4e5, 0x133e995, 0x45b6dc3, + 0x56fa322, 0x1cfd7a2, 0x3b9c495, 0x04c7ce3, 0x2adf0a9, 0x56be0ce, 0x4294773, 0x05f061e, + 0x4beb15b, 0x45ee7ca, 0x528659b, 0x022caed, 0x2789c77, 0x3385793, 0x3f5bf8a, 0x169a294, + 0x1bb1c49, 0x5e948c2, 0x0c09583, 0x1888a04, 0x3602b05, 0x411c064, 0x1c477da, 0x0a13c1f, + 0x3704ba8, 0x5dc8be4, 0x06ecba1, 0x3496c87, 0x39f3483, 0x338445e, 0x448d690, 0x3490561, + 0x47e3808, 0x162f30f, 0x16435e7, 0x44cb115, 0x3bfbe99, 0x57bbd9f, 0x3bbbeed, 0x5addf00, + 0x2e3a6a3, 0x40cbc01, 0x4ec7de2, 0x54e9ea7, 0x3993f95, 0x1364820, 0x0d62afc, 0x35efa55, + 0x03bc4b7, 0x0e1a554, 0x2b0c5bb, 0x058a1d6, 0x14d8cb6, 0x2ec334b, 0x466e7ff, 0x463688b, + 0x1f92f8e, 0x2eb1ff8, 0x07dadf6, 0x5eb8200, 0x42d6ecf, 0x18e3750, 0x3dbde74, 0x417a54b, + 0x227bb0b, 0x4bf00a0, 0x4fbec64, 0x05b1888, 0x58cf838, 0x24a914c, 0x34e03ad, 0x24f25ac, + 0x30d4ccf, 0x08deced, 0x2b70f57, 0x4126f97, 0x2b1dcf4, 0x35c947c, 0x04e3820, 0x249ba88, + 0x0de3d42, 0x5a78e22, 0x36c85e3, 0x0a8d44a, 0x07a8994, 0x2c9eccb, 0x48c6185, 0x2240860, + 0x43312c6, 0x050affc, 0x58d2a1c, 0x2e4ea14, 0x5ad4919, 0x266e6bf, 0x00bb8f2, 0x41dcae1, + 0x57df710, 0x210fc04, 0x2c5ac65, 0x457d709, 0x3655d93, 0x3bccd56, 0x0da53fa, 0x52f4d8e, + 0x53c27cc, 0x3e40fe5, 0x0126c6c, 0x2d5ac00, 0x0e435be, 0x3c9f6fb, 0x5074c02, 0x13d39d1, + 0x4c08d7d, 0x4bababb, 0x565310d, 0x1e2bd34, 0x36df254, 0x582af32, 0x2f8aaf9, 0x4bfad70, + 0x087094e, 0x110f201, 0x157c17a, 0x5cfd6a9, 0x473a6d6, 0x45d61e0, 0x1343ba2, 0x3d6eea5, + 0x2617104, 0x514a1b8, 0x1a9a661, 0x4447183, 0x38d4361, 0x57105b8, 0x4a153c8, 0x58ccc8f, + 0x261dfd6, 0x3493c91, 0x4d94838, 0x1348d71, 0x3a91b0b, 0x014037a, 0x43e5161, 0x3f286d9, + 0x59e3641, 0x3c830cf, 0x4c4022b, 0x4e2394a, 0x4707cb2, 0x26a1e8a, 0x4c6d0c4, 0x5e57744, + 0x01da8ec, 0x4971196, 0x1d9c73c, 0x5e19672, 0x4be3967, 0x02ed6e0, 0x350a8ee, 0x1774354, + 0x48d7478, 0x0ecebc8, 0x27c01cf, 0x1be3728, 0x22e7a82, 0x5cb8730, 0x06b3130, 0x1016de5, + 0x4c65718, 0x42e4aff, 0x421d5a7, 0x3e3da43, 0x5da8a59, 0x4e76367, 0x4699895, 0x417c513, + 0x3da9578, 0x5387a0e, 0x50ae88f, 0x5e1b541, 0x5534694, 0x12f1ce6, 0x3e247e5, 0x34df1b7, + 0x13c4508, 0x05e3d38, 0x0b57e4d, 0x1b12d24, 0x26908ac, 0x5ddb4c7, 0x380f95b, 0x4f1df61, + 0x59fe45e, 0x5158eed, 0x2de4584, 0x5367459, 0x1572d04, 0x14f4eed, 0x2a36851, 0x192154d, + 0x273d580, 0x571fd73, 0x3654fd0, 0x274977c, 0x4cd4d81, 0x3fff9dd, 0x0efa124, 0x231555f, + 0x28ddd8a, 0x4fe1059, 0x4edcc99, 0x2313e4e, 0x018f36c, 0x5a67b30, 0x1a48d8c, 0x00eb84d, + 0x1c0ed2c, 0x513cf0e, 0x3369c03, 0x2c0f984, 0x2b61109, 0x2e78bf9, 0x0003a5c, 0x42f746a, + 0x2d878b7, 0x380011e, 0x55adfd2, 0x319a26d, 0x4cf7a0a, 0x04c9de3, 0x468f995, 0x0bc4830, + 0x44b21a1, 0x55b57c8, 0x4e9d11b, 0x14a4899, 0x3835ea7, 0x44ea808, 0x2c8cf0f, 0x2fac41d, + 0x1e4b845, 0x4f730fd, 0x430b796, 0x12c4c54, 0x51d6a63, 0x44e1c86, 0x3ab0049, 0x3f61687, + 0x1553373, 0x25e44e4, 0x0247ace, 0x42b299a, 0x1901c52, 0x16479d5, 0x36bdecf, 0x421b211, + 0x1a8a824, 0x4c0f7e0, 0x319492d, 0x35627f2, 0x188ab51, 0x314079c, 0x16dfcb2, 0x0f35b2c, + 0x2399098, 0x138fb79, 0x4e82be1, 0x46c0cad, 0x346f686, 0x16e9ac6, 0x24be914, 0x3388e97, + 0x1914aa5, 0x3022504, 0x50e8bf5, 0x0d31655, 0x362d570, 0x480b8a9, 0x3ba52a9, 0x38ec748, + 0x036de59, 0x3d817a8, 0x4b90ddc, 0x0d00852, 0x5abe801, 0x40d9a1c, 0x5c5dfcf, 0x350dda4, + 0x1064be4, 0x5534e67, 0x512817b, 0x4edefec, 0x304dc64, 0x45af5bf, 0x539cddc, 0x1ef3d68, + 0x5257582, 0x30d4999, 0x3d7f582, 0x44fd7f2, 0x2c0a6f0, 0x0ff723a, 0x1a7915b, 0x03f171c, + 0x447d81b, 0x05ec207, 0x02c632d, 0x415084a, 0x23b1d42, 0x5104fd1, 0x5da4b32, 0x0f1a29d, + 0x1270205, 0x02680b8, 0x33a6c8c, 0x4f1f6bb, 0x30ab40b, 0x538e890, 0x0b6821f, 0x4c82906, + 0x406c9d3, 0x2048f01, 0x2fecbd7, 0x06c6ae7, 0x19e7a7d, 0x4dab4f4, 0x03a9cf5, 0x2af80f2, + 0x323d821, 0x290e051, 0x26d7324, 0x08bf312, 0x42e16b8, 0x39fc1e6, 0x5924f16, 0x5d286ef, + 0x26ed44d, 0x52fdcf7, 0x162908d, 0x4efc7ee, 0x5cbd41a, 0x06944bb, 0x3a11d24, 0x142dc47, + 0x444a2b7, 0x3affacb, 0x31a4d4b, 0x2f77002, 0x5625b5d, 0x45b9398, 0x1c3e0b1, 0x05b7689, + 0x1c8d103, 0x32e91d3, 0x1032bdb, 0x5a7bd94, 0x246077a, 0x046b82a, 0x4f98e1c, 0x090ede9, + 0x0f82b74, 0x59bb756, 0x2292b25, 0x529617c, 0x32535c6, 0x497059a, 0x48e0e6f, 0x536c1c3, + 0x38521c8, 0x486a8a0, 0x497a789, 0x2b6ba04, 0x564264f, 0x10500a7, 0x1ce2661, 0x409a859, + 0x342df5c, 0x3c057bc, 0x207e678, 0x530ab10, 0x561d66c, 0x4150294, 0x3a6792e, 0x4106a4c, + 0x4828d5d, 0x2fec6cd, 0x5cf5fe9, 0x596c81c, 0x292ac60, 0x12da2ea, 0x05d2ac4, 0x4bd5492, + 0x4b58470, 0x1d5c86d, 0x59f985e, 0x52b6914, 0x383a5b5, 0x293acb4, 0x0a74e03, 0x3860ca7, + 0x2f08fa1, 0x4add299, 0x55703fd, 0x5afc833, 0x3a66f50, 0x330494e, 0x2e736cc, 0x2cbe45d, + 0x331e9cd, 0x08d24c6, 0x56bee2d, 0x0ab1ac2, 0x5e84817, 0x153e60e, 0x12643cf, 0x3222123, + 0x270c969, 0x2c614ba, 0x04fa8e2, 0x54adf59, 0x20b207c, 0x085d318, 0x45d3985, 0x2674d86, + 0x234163a, 0x03f2807, 0x0d676da, 0x3eb3a3b, 0x1961553, 0x0cd4e2a, 0x2ad2ed4, 0x5484318, + 0x03d4e25, 0x25a7da3, 0x2853b36, 0x30009a3, 0x3c61b30, 0x0392556, 0x0cbb436, 0x12278b2, + 0x219e827, 0x2f318b6, 0x50bd090, 0x44a1397, 0x59814d3, 0x1429c61, 0x378d9c6, 0x39342cf, + 0x2c99c3b, 0x52c03ef, 0x42d67dd, 0x05eba18, 0x0428471, 0x21505aa, 0x21bd644, 0x1a32d5a, + 0x069b67c, 0x119af47, 0x44cda76, 0x428721d, 0x3ce54a0, 0x417b6dd, 0x3d6c2f7, 0x349ea4d, + 0x33a1aef, 0x5de2b23, 0x2f4ff01, 0x085bd7f, 0x4e32400, 0x39df021, 0x470b051, 0x216c3b2, + 0x194700b, 0x5def8ab, 0x1dd574a, 0x52d2a79, 0x0e6f573, 0x0591d11, 0x47d755a, 0x540aad5, + 0x539b8a5, 0x12c4c9e, 0x2a939da, 0x3cc8578, 0x5a66bc1, 0x10d4fb1, 0x0f263fa, 0x28c40c7, + 0x17b1e1f, 0x41cd24e, 0x2d332cc, 0x019a30f, 0x2f02597, 0x3d70fa1, 0x5b307bc, 0x5d29090, + 0x5e1aba0, 0x5dcbea0, 0x4a88eb9, 0x56350a0, 0x2639b0b, 0x46fcc3c, 0x35ee1a7, 0x267af5e, + 0x2e76f9a, 0x2cd6f27, 0x1365c28, 0x04c4861, 0x22de9c9, 0x53b2b1a, 0x04f1dca, 0x27d960e, + 0x2f177b2, 0x5284a05, 0x1e33d12, 0x42c0a67, 0x59fa2aa, 0x1a0bfc1, 0x508b1ad, 0x43f9a35, + 0x415bf73, 0x4b257a0, 0x5f1d3cd, 0x2061431, 0x2686613, 0x24364d8, 0x2781f11, 0x2363b81, + 0x5ad65c0, 0x526e4b6, 0x1135fdc, 0x54125f9, 0x3e266a2, 0x30b7901, 0x200ca3b, 0x56d7ac1, + 0x45fe545, 0x4eacbf3, 0x379615c, 0x22e51a4, 0x0ea253e, 0x2f40c0a, 0x08af0bf, 0x5c5e7fd, + 0x15033de, 0x5561d63, 0x04e1ee7, 0x2129b39, 0x420b6b9, 0x53c99e1, 0x2dde065, 0x081faec, + 0x0f31103, 0x39f8bf0, 0x359931b, 0x37a54e1, 0x296f38d, 0x354f896, 0x5502af3, 0x1f2c86b, + 0x2c7c1d3, 0x0922a02, 0x2aab8c3, 0x45b3daf, 0x476086b, 0x40adb31, 0x0f4824f, 0x5bf00ee, + 0x1cfdeac, 0x5bd4196, 0x4ed155a, 0x2d325f1, 0x1ff2cfd, 0x5b9c682, 0x45c47b4, 0x3474221, + 0x1d59731, 0x159149b, 0x173ca10, 0x2e71d45, 0x4cb939c, 0x05adec6, 0x0c043fd, 0x1279fe9, + 0x06c06a3, 0x584a237, 0x2c6b229, 0x51b3c32, 0x5972ffd, 0x0a2117e, 0x5cbe8e1, 0x4c62118, + 0x530ce01, 0x13d0fa7, 0x309f98d, 0x12d0cd4, 0x4fb8719, 0x24950e3, 0x0e09770, 0x350bd6d, + 0x50ec057, 0x3648f58, 0x240160d, 0x30cd6c5, 0x41231c1, 0x04c1b90, 0x0330b1f, 0x10f351a, + 0x4dcc8ac, 0x5249c69, 0x46bebf6, 0x154c29e, 0x17d3710, 0x0c9a97d, 0x2d12f1d, 0x1e21d00, + 0x0d8f827, 0x4060c8b, 0x4e283da, 0x504d6f5, 0x0266438, 0x26191c5, 0x4b84129, 0x573589b, + 0x1bc37bc, 0x291d612, 0x4e1436e, 0x22fbead, 0x1d33ee7, 0x3ce092b, 0x1882030, 0x56a3583, + 0x3437856, 0x454d80f, 0x42b35f5, 0x0d64675, 0x4d9cd11, 0x530a971, 0x2b2791a, 0x07afdbb, + 0x31717d8, 0x22a651c, 0x28185d6, 0x45cc923, 0x0919219, 0x4720d41, 0x1136da8, 0x0619da7, + 0x2298222, 0x527274f, 0x1847c68, 0x45ba143, 0x1fc68b9, 0x528bb16, 0x2e444f7, 0x292f753, + 0x3e862f5, 0x30c2f63, 0x0e37609, 0x2524fdb, 0x39fd53d, 0x18d66fe, 0x18fc78a, 0x3c7b3d6, + 0x462e8d8, 0x4ab5d33, 0x5817ed1, 0x3391293, 0x48cf2b1, 0x28396c5, 0x17c9aef, 0x37c4750, + 0x2fe7de9, 0x374cbce, 0x1a08def, 0x23986b9, 0x2070650, 0x0765f0f, 0x39482c5, 0x4b37890, + 0x2235d0f, 0x5240fb4, 0x0d94793, 0x57355af, 0x082d07b, 0x1feb469, 0x152aa9c, 0x3238cca, + 0x2a6f125, 0x1970455, 0x5ac228e, 0x2952852, 0x0118f16, 0x37e72bd, 0x24351c2, 0x0c56e11, + 0x4eb42e8, 0x32f111a, 0x1f621e9, 0x5e90794, 0x3226a64, 0x42ab6b2, 0x0643dbf, 0x30854bb, + 0x110e06a, 0x2ce06c1, 0x33796e8, 0x14d1b2c, 0x031070a, 0x4b9e9de, 0x334a5e1, 0x1ccca12, + 0x30c45a8, 0x46134f9, 0x3cddf1d, 0x43dac04, 0x45aa1c4, 0x2513849, 0x5299bcb, 0x5f40ca5, + 0x0665e26, 0x4b780eb, 0x074b842, 0x478b5d7, 0x0892d57, 0x5301411, 0x09cc5c0, 0x3f5305e, + 0x3fb9c41, 0x3770f92, 0x2a4def5, 0x4637f32, 0x0914c15, 0x0fe8210, 0x5606e29, 0x3bc2528, + 0x5e62145, 0x086ab0e, 0x01bad8e, 0x29c0749, 0x45e6b95, 0x3fc85de, 0x06e5ca7, 0x54d8dfe, + 0x3e76ffc, 0x24f7046, 0x52f69f4, 0x2123d7a, 0x1f0a9d9, 0x42785f4, 0x5960720, 0x452ee3d, + 0x5180935, 0x4aca308, 0x540c9d5, 0x21a9a0d, 0x07389a1, 0x1e03969, 0x5a9f5f5, 0x5b80384, + 0x3278cdd, 0x20315ff, 0x3b937c8, 0x2f3d8cc, 0x4c67fdf, 0x4c6e2c7, 0x0210d8b, 0x4dfb4ab, + 0x12445c9, 0x28a79f0, 0x271900a, 0x0376c2a, 0x0166b17, 0x28a672e, 0x34cac10, 0x413e828, + 0x4a9759b, 0x0ce0f53, 0x5d6d020, 0x1e7bde0, 0x2adb971, 0x11f26cd, 0x4575c09, 0x2e6236e, + 0x5be0df6, 0x1f552b8, 0x1ae0566, 0x28f71de, 0x2b84dbe, 0x2bcd4bd, 0x09be4d2, 0x4860e49, + 0x3cd20fc, 0x06f9064, 0x31edcf8, 0x251bca5, 0x34479a1, 0x3647845, 0x2ad3bf1, 0x073f9f0, + 0x24cb199, 0x57d71fc, 0x3f9ac7c, 0x412904d, 0x1eeacfe, 0x2c35c86, 0x09ddf75, 0x3033b89, + 0x4bd908d, 0x1996fcc, 0x2cd4f75, 0x2c92c3c, 0x33ae01a, 0x1093d71, 0x5aa7baa, 0x4a86577, + 0x25c4434, 0x41961f4, 0x5a6f3c7, 0x575d2fd, 0x3c4c39f, 0x545a974, 0x5570682, 0x4e1750b, + 0x3ef6ee7, 0x28d06af, 0x31a256f, 0x5334be8, 0x093e26f, 0x4796805, 0x5a228bd, 0x214203e, + 0x267537e, 0x54ee1ee, 0x35d2813, 0x4e3eed6, 0x29fc8ef, 0x0c2309d, 0x24c1e4a, 0x3828772, + 0x294cb93, 0x4e0d631, 0x16a8a5f, 0x06380ca, 0x146834b, 0x5d493a5, 0x3353f6b, 0x5dcf412, + 0x4a40a06, 0x19396c5, 0x3d40f48, 0x58f277b, 0x5082b70, 0x096896d, 0x213d04c, 0x11561bd, + 0x2405626, 0x1a30a65, 0x5bdf120, 0x2fe661a, 0x4f54188, 0x4773e64, 0x23bb84b, 0x2938adb, + 0x59d4767, 0x0dc0cce, 0x1fd8f50, 0x5de93a3, 0x307559a, 0x41c9c70, 0x33f296b, 0x49564cd, + 0x1728967, 0x5572a22, 0x167f580, 0x30cb478, 0x25d7b3e, 0x38b3504, 0x38484ce, 0x339f21b, + 0x1ef5cf7, 0x0b70d8c, 0x0b93ac3, 0x1afccbf, 0x5780fd3, 0x06558c8, 0x43b2a5d, 0x5b4626e, + 0x5e5121c, 0x302ffdb, 0x0e186df, 0x49a049f, 0x06fe3c5, 0x15574a8, 0x5d5f256, 0x2fa0ad6, + 0x0d545bc, 0x2c33e9f, 0x1ce8dea, 0x32c3c8c, 0x222e839, 0x3286b7f, 0x29f9000, 0x083cd91, + 0x32dc014, 0x4426bde, 0x2e36e6b, 0x38c03c0, 0x0b8930c, 0x2b2c4ac, 0x384bc6c, 0x228142e, + 0x42df93b, 0x4ca9a67, 0x0addfae, 0x26d6ff4, 0x2181387, 0x2ca2aa2, 0x520faf4, 0x3b89847, + 0x1c8936b, 0x4d43776, 0x13a9d3f, 0x29537e1, 0x07f3c86, 0x2431d30, 0x0fc8938, 0x021ca5b, + 0x493da18, 0x1f7d16b, 0x1cdd508, 0x3b2576b, 0x2d92e15, 0x46278dd, 0x002c05c, 0x002ffef, + 0x17c98df, 0x54a4042, 0x42ee518, 0x322a133, 0x4998b6a, 0x2249c3c, 0x51637d7, 0x10821ba, + 0x5430454, 0x22d7917, 0x0bebbcf, 0x1a085c0, 0x2d0a6b5, 0x508664a, 0x0ecfe27, 0x0ad943d, + 0x46cfaeb, 0x4ff27eb, 0x3d4bf1c, 0x2556e37, 0x5bbf38f, 0x27a0ded, 0x0678873, 0x4707dd8, + 0x07ec2c3, 0x3eeef64, 0x5c831e0, 0x32c1362, 0x304530b, 0x21d1066, 0x3bc740f, 0x46e4fef, + 0x1b52fc5, 0x3759882, 0x58354c6, 0x32b53e7, 0x4191ba0, 0x53aa864, 0x0dc68a0, 0x3069456, + 0x5d4cd11, 0x0ea1224, 0x12de9fb, 0x2677a63, 0x03a91a9, 0x5ca697c, 0x0addead, 0x2b219cc, + 0x02d21d5, 0x35bb686, 0x22ee455, 0x06bb267, 0x096f12d, 0x4aeba17, 0x331d452, 0x1a193d5, + 0x01b6fa2, 0x3158ad6, 0x3616ab3, 0x3f5f64d, 0x543c3be, 0x4f8e55a, 0x5865875, 0x1987e49, + 0x549e323, 0x0a7c16f, 0x5a977dd, 0x2a4fdd1, 0x07c0911, 0x11f6bb3, 0x4a05c34, 0x245754e, + 0x512e836, 0x36a0fef, 0x496d3f0, 0x041cb0f, 0x0f9b6ee, 0x3b10f70, 0x3bc2efc, 0x5719e3b, + 0x2aeda17, 0x17065f0, 0x3db44ff, 0x55d7b26, 0x2e1e859, 0x5869d14, 0x1310d92, 0x1e02d41, + 0x45c3134, 0x4072c88, 0x254c979, 0x5141c81, 0x56ac15b, 0x0cb4b87, 0x1ca522b, 0x53df04a, + 0x163bee7, 0x33c3686, 0x2823053, 0x1b4068c, 0x30a1e0a, 0x3bd6d3b, 0x4172407, 0x1734d9f, + 0x316842b, 0x27cab0e, 0x384aa6f, 0x486db5c, 0x57fb84f, 0x1eba66d, 0x5925c43, 0x0dd34c7, + 0x1983276, 0x1eb5679, 0x11ccf9f, 0x496f98c, 0x1c35b4b, 0x48b7444, 0x48bceed, 0x01d9c13, + 0x1ddee3b, 0x1127181, 0x5ac1a92, 0x1fd4bc5, 0x0b166cd, 0x107b011, 0x52c02c6, 0x199c690, + 0x490fed8, 0x2b77c22, 0x383bd32, 0x040d7df, 0x44ce3bf, 0x2c6e777, 0x3dd86af, 0x3fa677b, + 0x1d46fff, 0x146703c, 0x07dc71f, 0x05a6b46, 0x53660a3, 0x3b4b5c9, 0x4ec4cbb, 0x248ae53, + 0x0d5d155, 0x4363005, 0x2cbd064, 0x5c18f03, 0x214bedd, 0x42ef202, 0x41827cd, 0x27a8fe9, +}; + +struct Random_Series{ + u32 index; +}; + +inline Random_Series +random_seed(u32 Value){ + Random_Series series; + series.index = (Value % ArrayCount(RandomNumberTable)); + return(series); +} + +inline u32 +random_next_u32(Random_Series *series){ + u32 result = RandomNumberTable[series->index++]; + if(series->index >= ArrayCount(RandomNumberTable)){ + series->index = 0; + } + return(result); +} + +inline u32 +random_choice(Random_Series *series, u32 choice_count) +{ + u32 result = (random_next_u32(series) % choice_count); + return(result); +} + +inline f32 +random_unilateral(Random_Series *series) +{ + f32 Divisor = 1.0f / (f32)MaxRandomNumber; + f32 result = Divisor*(f32)random_next_u32(series); + return(result); +} + +inline f32 +random_bilateral(Random_Series *series) +{ + f32 result = 2.0f*random_unilateral(series) - 1.0f; + return(result); +} + +inline f32 +random_between(Random_Series *series, f32 min, f32 max) +{ + f32 result = min + (max - min) * random_unilateral(series); + return(result); +} + +inline i32 +random_between(Random_Series *series, i32 min, i32 max) +{ + i32 result = min + (i32)(random_next_u32(series)%((max + 1) - min)); + return(result); +} + +#endif \ No newline at end of file diff --git a/test_data/lots_of_files/cd_render_helper.cpp b/test_data/lots_of_files/cd_render_helper.cpp new file mode 100644 index 0000000..2dc5ea0 --- /dev/null +++ b/test_data/lots_of_files/cd_render_helper.cpp @@ -0,0 +1,275 @@ +/* + * Helper functions for core engine module while rendering. + */ + +// TOP + +Font* +get_font(Asset_Bank *bank, i32 font_id){ + Font *font = 0; + if (bank && font_id >= 1 && font_id <= bank->font_max_id){ + font = bank->fonts + font_id - 1; + } + return(font); +} + +f32* +get_font_advance(Asset_Bank *bank, i32 font_id){ + f32 *advance = 0; + Font *font = get_font(bank, font_id); + if (font){ + advance = font->advance; + } + return(advance); +} + +Image* +get_image(Asset_Bank *bank, i32 image_id){ + Image *result = 0; + if (bank && image_id >= 1 && image_id <= bank->image_max_id){ + result = bank->images + image_id - 1; + } + return(result); +} + +enum Tranform_Spec{ + TF_Pixels, + TF_Units, +}; + +Transform unit_to_pixel_transform = { + {v2(pixel_over_unit, 0.f), v2(0.f, pixel_over_unit)}, v2(x_origin_u, y_origin_u) +}; + +void +target_transform(Render_Target *target, Tranform_Spec t){ + switch (t){ + case TF_Pixels: + target->transform = invert_transform(target->me_to_pixel_transform); + break; + + case TF_Units: + target->transform = + transform_transform(unit_to_pixel_transform, invert_transform(target->me_to_pixel_transform)); + break; + + default: + InvalidCodePath; + } +} + +void +clear_target(Render_Target *target){ + if (target){ + target->push_part.rel_part.pos = 0; + } +} + +void +null_render(Render_Target *target){ + if (target){ + Render_Command_Header *h; + h = push_type(&target->push_part, Render_Command_Header); + h->type = render_null; + h->size = 0; + } +} + +void +do_dbg_rectangle(Render_Target *target, f32_rect rect, vec4 color){ + if (target){ + Render_Command_Header *h; + Render_Dbg_Rectangle *r; + + h = push_type(&target->push_part, Render_Command_Header); + h->type = render_dbg_rectangle; + h->size = sizeof(Render_Command_Header) + sizeof(Render_Dbg_Rectangle); + + rect.p[0] = do_transform(rect.p[0], target->transform); + rect.p[1] = do_transform(rect.p[1], target->transform); + + r = push_type(&target->push_part, Render_Dbg_Rectangle); + r->rect = rect; + r->color = color; + } +} + +void +do_dbg_parallelagram(Render_Target *target, vec2 pos, vec2 x_axis, vec2 y_axis, vec4 color){ + if (target){ + Render_Command_Header *h; + Render_Dbg_Parallelagram *r; + + h = push_type(&target->push_part, Render_Command_Header); + h->type = render_dbg_parallelagram; + h->size = sizeof(Render_Command_Header) + sizeof(Render_Dbg_Parallelagram); + + r = push_type(&target->push_part, Render_Dbg_Parallelagram); + r->transform.pos = do_transform(pos, target->transform); + r->transform.basis.x = x_axis; + r->transform.basis.y = y_axis; + r->color = color; + } +} + +void +do_string(Render_Target *target, Asset_Bank *bank, + i32 font_id, vec2 pos, char *str, int len, vec4 color){ + Render_Command_Header *h; + Render_Glyph *g; + i32 i; + char c; + f32 *advance; + + if (target){ + advance = get_font_advance(bank, font_id); + if (advance){ + for (i = 0; i < len; ++i){ + h = push_type(&target->push_part, Render_Command_Header); + h->type = render_glyph; + h->size = sizeof(Render_Command_Header) + sizeof(Render_Glyph); + + c = str[i] & (127); + g = push_type(&target->push_part, Render_Glyph); + g->pos = do_transform(pos, target->transform); + g->font_id = font_id; + g->c = c; + g->color = color; + + pos.x += advance[c]; + } + } + } +} + +#define Render_Normal 0x0 +#define Render_Exact 0x1 + +void +do_image_general(Render_Target *target, Asset_Bank *bank, + Render_Target *dbg, i32 image_id, + u32 flags, vec2 pos, vec2 origin, + vec2 x_axis, vec2 y_axis){ + Render_Command_Header *h; + Render_Image img = {0}; + + if (target){ + f32 x = 0, y = 0; + + Image *image = get_image(bank, image_id); + + if (Render_Exact & flags){ + x = 1.f; + y = 1.f; + } + else{ + if (image){ + x = (0.5f*image->width); + y = (0.5f*image->height); + } + else{ + x = 32.f; + y = 32.f; + } + } + + img.transform.basis.x = x_axis * x; + img.transform.basis.y = y_axis * y; + origin = do_basis(origin, img.transform.basis); + + pos.x -= origin.x; + pos.y -= origin.y; + + img.transform.pos = do_transform(pos, target->transform); + img.image_id = image_id; + + if (image){ + h = push_type(&target->push_part, Render_Command_Header); + h->type = render_image; + h->size = sizeof(Render_Command_Header) + sizeof(Render_Image); + + Render_Image *imgptr = push_type(&target->push_part, Render_Image); + *imgptr = img; + } + + do_dbg_parallelagram(dbg, + img.transform.pos, + img.transform.basis.x, + img.transform.basis.y, + v4(1.f, 0, 0, 0.25f)); + } +} + +void +do_image(Render_Target *target, Asset_Bank *bank, Render_Target *dbg, i32 image_id, + vec2 pos, f32 size = 1.f){ + vec2 x_axis, y_axis; + + if (target){ + x_axis = v2(size, 0.0f); + y_axis = v2(0.0f, size); + + do_image_general(target, bank, dbg, image_id, + Render_Normal, pos, v2(0,0), + x_axis, y_axis); + } +} + +void +do_image(Render_Target *target, Asset_Bank *bank, Render_Target *dbg, i32 image_id, + vec2 pos, vec2 origin, f32 size = 1.f){ + vec2 x_axis, y_axis; + + if (target){ + x_axis = v2(size, 0.0f); + y_axis = v2(0.0f, size); + + do_image_general(target, bank, dbg, image_id, + Render_Normal, pos, origin, x_axis, y_axis); + } +} + +void +set_lane(Render_Target *target, Render_Target *dbg, + i32 lane_index, f32 y){ + Render_Command_Header *h; + Render_Lane *l; + + if (target){ + h = push_type(&target->push_part, Render_Command_Header); + h->type = render_lane; + h->size = sizeof(Render_Command_Header) + sizeof(Render_Lane); + + l = push_type(&target->push_part, Render_Lane); + l->lane_index = lane_index; + l->y_position = y; + + do_dbg_parallelagram(dbg, + v2(0.f, y), + v2(2000.f, 0), + v2(0, 5.f), + v4(1.f, 0.5f, 0.f, 0.25f)); + } +} + +void +do_obstacle(Render_Target *target, Render_Target *dbg, + u32 type, u32 seed, f32 x, i32 lane){ + Render_Command_Header *h; + Render_Obstacle *o; + + if (target){ + h = push_type(&target->push_part, Render_Command_Header); + h->type = render_obstacle; + h->size = sizeof(Render_Command_Header) + sizeof(Render_Obstacle); + + o = push_type(&target->push_part, Render_Obstacle); + o->type = type; + o->seed = seed; + o->x = x; + o->lane = lane; + } +} + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_render_target.cpp b/test_data/lots_of_files/cd_render_target.cpp new file mode 100644 index 0000000..f54a347 --- /dev/null +++ b/test_data/lots_of_files/cd_render_target.cpp @@ -0,0 +1,425 @@ +/* + * General render target operations. + */ + +// TOP + +Bank_Begin_Setup_Sig(begin_setup){ + Asset_Bank_Setup setup = {0}; + setup.bank = bank; + setup.trans_part = trans_part; + setup.system = system; + return(setup); +} + +Bank_End_Setup_Sig(end_setup){} + +Bank_Bind_Font_Sig(bind_font){ + System_API *system = setup->system; + Asset_Bank *bank = setup->bank; + Partition *part = setup->trans_part; + Font *font = 0; + i32 i = 0; + + font = get_font(bank, font_id); + if (font){ + // TODO(tyler): Implementation of distance field text rendering for size-agnostic fonts + // TODO(allen): Pick a good size for the texture based + // on the size we want the font to be. + // TODO(allen): Background asset streaming. + Temp_Memory temp = begin_temp(part); + u8 *temp_bitmap = push_array(part, u8, 512*512); + File_Dump dump = system->DBG_dump_begin(file_name); + u8 *ttf_buffer = push_array(part, u8, dump.size); + + if (system->DBG_dump_end(dump, ttf_buffer)){ + stbtt_fontinfo fontinfo; + i32 ascent; + f32 scale; + f32 pixel_height = 16.f; + + stbtt_InitFont(&fontinfo, ttf_buffer, 0); + scale = stbtt_ScaleForPixelHeight(&fontinfo, pixel_height); + stbtt_GetFontVMetrics(&fontinfo, &ascent, 0, 0); + + font->texture_w = 512; + font->texture_h = 512; + font->pull_up = ascent*scale; + font->id = font_id; + + stbtt_BakeFontBitmap(ttf_buffer, 0, pixel_height, + temp_bitmap, font->texture_w, font->texture_h, + 0, 128, font->chardata, part); + + for (i = 0; i < 128; ++i){ + font->advance[i] = font->chardata[i].xadvance; + } + + glGenTextures(1, &font->texture_id); + glBindTexture(GL_TEXTURE_2D, font->texture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512, 512, + 0, GL_ALPHA, GL_UNSIGNED_BYTE, + temp_bitmap); + +#ifdef DEVELOPER + { + GLenum error = glGetError(); + GLenum copy = error; + AllowLocal(copy); + } +#endif + } + + end_temp(temp); + } +} + +struct Bmp_Data{ + i32 w, h, byte_pitch; + u16 bpp; + u8 *data; +}; + +struct Bmp_File_Info{ + u16 type; + u32 file_size; + u16 res1, res2; + u32 off_bits; + + u32 struct_size; + i32 w, h; + u16 planes; + u16 bpp; + u32 compression; + u32 image_size; + i32 xppm; + i32 yppm; + u32 colors_used; + u32 colors_important; +}; + +b32 +bmp_read(u8 *src_buffer, i32 size, Bmp_Data *bmp, Bmp_File_Info *bmp_info){ + u8 *ptr = src_buffer; + b32 result = 0; + + bmp_info->type = PtrRead(ptr, u16); + bmp_info->file_size = PtrRead(ptr, u32); + bmp_info->res1 = PtrRead(ptr, u16); + bmp_info->res2 = PtrRead(ptr, u16); + bmp_info->off_bits = PtrRead(ptr, u32); + + if (bmp_info->type == 0x4D42){ + bmp_info->struct_size = PtrRead(ptr, u32); + bmp_info->w = PtrRead(ptr, i32); + bmp_info->h = PtrRead(ptr, i32); + bmp_info->planes = PtrRead(ptr, u16); + bmp_info->bpp = PtrRead(ptr, u16); + bmp_info->compression = PtrRead(ptr, u32); + bmp_info->image_size = PtrRead(ptr, u32); + bmp_info->xppm = PtrRead(ptr, i32); + bmp_info->yppm = PtrRead(ptr, i32); + bmp_info->colors_used = PtrRead(ptr, u32); + bmp_info->colors_important = PtrRead(ptr, u32); + + bmp->w = bmp_info->w; + bmp->h = bmp_info->h; + bmp->byte_pitch = bmp_info->image_size / bmp->h; + bmp->bpp = bmp_info->bpp; + + if (bmp->byte_pitch == bmp->w*4){ + bmp->data = src_buffer + bmp_info->off_bits; + result = 1; + } + } + + return(result); +} + +struct POT_Texture{ + void *data; + u32 w, h; +}; + +u32 +round_up_pot(u32 x){ + --x; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + ++x; + return(x); +} + +POT_Texture +pot_bmp_texture(Partition *part, void *data, i32 w, i32 h, i32 pitch){ + POT_Texture result = {0}; + char *src_line = 0; + char *dst_line = 0; + char *pixel = 0; + char r,g,b,a; + i32 dst_pitch = 0; + i32 y = 0, x = 0; + + result.w = round_up_pot((u32)w); + result.h = round_up_pot((u32)h); + dst_pitch = result.w*4; + align_partition(part, 4); + result.data = push_array(part, char, result.h*dst_pitch); + memset(result.data, 0, result.h*dst_pitch); + + src_line = (char*)data; + dst_line = (char*)result.data; + + for (y = 0; y < h; ++y){ + memcpy(dst_line, src_line, pitch); + + pixel = dst_line; + for (x = 0; x < w; ++x){ + b = pixel[0]; + g = pixel[1]; + r = pixel[2]; + a = pixel[3]; + + pixel[0] = r; + pixel[1] = g; + pixel[2] = b; + pixel[3] = a; + + pixel += 4; + } + + src_line += pitch; + dst_line += dst_pitch; + } + + return(result); +} + +u32 +load_bmp(System_API *system, Partition *part, Image *image, i32 image_id, char *file_name){ + u32 prev_tex_id = 0; + if (image){ + // TODO(allen): Background asset streaming. + Bmp_Data bmp; + Bmp_File_Info bmp_info; + + Temp_Memory temp = begin_temp(part); + File_Dump dump = system->DBG_dump_begin(file_name); + u8 *bmp_buffer = push_array(part, u8, dump.size); + if (system->DBG_dump_end(dump, bmp_buffer)){ + if (bmp_read(bmp_buffer, dump.size, &bmp, &bmp_info)){ + POT_Texture texture = + pot_bmp_texture(part, bmp.data, bmp.w, bmp.h, bmp.byte_pitch); + + image->tex_x = (f32)bmp.w/(f32)texture.w; + image->tex_y = (f32)bmp.h/(f32)texture.h; + image->width = (f32)bmp.w; + image->height = (f32)bmp.h; + image->id = image_id; + + prev_tex_id = image->texture_id; + + glGenTextures(1, &image->texture_id); + glBindTexture(GL_TEXTURE_2D, image->texture_id); + + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + texture.w, texture.h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, + texture.data); + } + } + end_temp(temp); + } + return(prev_tex_id); +} + +Bank_Bind_BMP_Sig(bind_bmp){ + Asset_Bank *bank = setup->bank; + Partition *part = setup->trans_part; + Image *image = get_image(bank, image_id); + load_bmp(setup->system, part, image, image_id, file_name); +} + +Bank_Replace_BMP_Sig(replace_bmp){ + u32 prev_tex_id = 0; + Image *image = get_image(bank, image_id); + prev_tex_id = load_bmp(system, part, image, image_id, file_name); + if (prev_tex_id){ + glDeleteTextures(1, &prev_tex_id); + } +} + +#include <windows.h> +#include "cd_windows_render_vars.h" + +#include "cd_gl_defines.h" + +static void* +win32_load_gl_always(char *name, HMODULE module){ + void *p = (void *)wglGetProcAddress(name), *r = 0; + if(p == 0 || + (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || + (p == (void*)-1) ){ + r = (void *)GetProcAddress(module, name); + } + else{ + r = p; + } + return(r); +} + +// TODO(allen): Need to move anything that couples +// rendering to platform into a separate layer. +Init_Sig(init){ + Win32_Render_Vars *vars = (Win32_Render_Vars*)render_vars; + + // GL context initialization + { + PIXELFORMATDESCRIPTOR format; + int format_id; + BOOL success; + HDC dc; + + format.nSize = sizeof(format); + format.nVersion = 1; + format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; + format.iPixelType = PFD_TYPE_RGBA; + format.cColorBits = 32; + format.cRedBits = 0; + format.cRedShift = 0; + format.cGreenBits = 0; + format.cGreenShift = 0; + format.cBlueBits = 0; + format.cBlueShift = 0; + format.cAlphaBits = 0; + format.cAlphaShift = 0; + format.cAccumBits = 0; + format.cAccumRedBits = 0; + format.cAccumGreenBits = 0; + format.cAccumBlueBits = 0; + format.cAccumAlphaBits = 0; + format.cDepthBits = 24; + format.cStencilBits = 8; + format.cAuxBuffers = 0; + format.iLayerType = PFD_MAIN_PLANE; + format.bReserved = 0; + format.dwLayerMask = 0; + format.dwVisibleMask = 0; + format.dwDamageMask = 0; + + dc = GetDC(vars->hwnd); + Assert(dc); + format_id = ChoosePixelFormat(dc, &format); + Assert(format_id != 0); + success = SetPixelFormat(dc, format_id, &format); + Assert(success == TRUE); + + HGLRC glcontext = wglCreateContext(dc); + wglMakeCurrent(dc, glcontext); + + { + HMODULE module = LoadLibraryA("opengl32.dll"); + + wglCreateContextAttribsARB_Function *wglCreateContextAttribsARB = 0; + wglCreateContextAttribsARB = (wglCreateContextAttribsARB_Function*) + win32_load_gl_always("wglCreateContextAttribsARB", module); + + wglChoosePixelFormatARB_Function *wglChoosePixelFormatARB = 0; + wglChoosePixelFormatARB = (wglChoosePixelFormatARB_Function*) + win32_load_gl_always("wglChoosePixelFormatARB", module); + + if (wglCreateContextAttribsARB != 0 && wglChoosePixelFormatARB != 0){ + const int choosePixel_attribList[] = + { + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_DOUBLE_BUFFER_ARB, GL_TRUE, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, 32, + WGL_DEPTH_BITS_ARB, 24, + WGL_STENCIL_BITS_ARB, 8, + 0, + }; + + int extended_format_id; + UINT num_formats = 0; + BOOL result = 0; + + result = wglChoosePixelFormatARB(dc, + choosePixel_attribList, + 0, + 1, + &extended_format_id, + &num_formats); + + if (result != 0 && num_formats > 0){ + const int createContext_attribList[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 2, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + 0 + }; + + if (extended_format_id == format_id){ + HGLRC extended_context = + wglCreateContextAttribsARB(dc, + 0, + createContext_attribList); + if (extended_context){ + wglMakeCurrent(dc, extended_context); + wglDeleteContext(glcontext); + glcontext = extended_context; + + MessageBoxA(0,(char*)glGetString(GL_VERSION), "OPENGL VERSION",0); + } + } + } + } + } + + ReleaseDC(vars->hwnd, dc); + } + + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0,0,0,0); +} + +Display_Sig(display){ + Win32_Render_Vars *vars = (Win32_Render_Vars*)render_vars; + glFlush(); + HDC dc = GetDC(vars->hwnd); + SwapBuffers(dc); + ReleaseDC(vars->hwnd, dc); +} + +Set_Screen_Sig(set_screen){ + if (w > 0 && h > 0){ + glViewport(0, h - out_h, out_w, out_h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, out_w, 0, out_h, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glScissor(0, h - out_h, out_w, out_h); + } +} + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_render_target.h b/test_data/lots_of_files/cd_render_target.h new file mode 100644 index 0000000..123f2fa --- /dev/null +++ b/test_data/lots_of_files/cd_render_target.h @@ -0,0 +1,172 @@ +/* + * Interface between game and graphics module + */ + +// TOP + +#ifndef CD_RENDER_TARGET_H +#define CD_RENDER_TARGET_H + +#define Render_Get_Functions_Sig(n) void n(Render_Target *target) +typedef Render_Get_Functions_Sig(Render_Get_Functions); + +#define Render_Execute_Sig(n) void n(Render_Target *target, struct Asset_Bank *bank) +typedef Render_Execute_Sig(Render_Execute_Function); + +#define Init_Sig(n) void n(void *render_vars) +typedef Init_Sig(Init_Function); + +#define Display_Sig(n) void n(void *render_vars) +typedef Display_Sig(Display_Function); + +#define Set_Screen_Sig(n) void n(i32 w, i32 h, i32 out_w, i32 out_h) +typedef Set_Screen_Sig(Set_Screen_Function); + + +struct Asset_Bank_Setup{ + struct Asset_Bank *bank; + Partition *asset_part; + System_API *system; +}; + +#define Bank_Get_Functions_Sig(n) void n(Asset_Bank *bank) +typedef Bank_Get_Functions_Sig(Bank_Get_Functions); + +#define Bank_Begin_Setup_Sig(n) Asset_Bank_Setup n(struct Asset_Bank *bank, Partition *trans_part, System_API *system) +typedef Bank_Begin_Setup_Sig(Begin_Setup_Function); + +#define Bank_End_Setup_Sig(n) void n(Asset_Bank_Setup *setup) +typedef Bank_End_Setup_Sig(End_Setup_Function); + +#define Bank_Bind_Font_Sig(n) void n(Asset_Bank_Setup *setup, char *file_name, i32 font_id) +typedef Bank_Bind_Font_Sig(Bind_Font_Function); + +#define Bank_Bind_BMP_Sig(n) void n(Asset_Bank_Setup *setup, char *file_name, i32 image_id) +typedef Bank_Bind_BMP_Sig(Bind_BMP_Function); + +#define Bank_Replace_BMP_Sig(n) void n(System_API *system, struct Asset_Bank *bank, Partition *part, char *file_name, i32 image_id) +typedef Bank_Replace_BMP_Sig(Replace_BMP_Function); + + +struct Font{ + stbtt_bakedchar chardata[128]; + f32 advance[128]; + f32 pull_up; + i32 texture_w, texture_h; + u32 texture_id; + i32 id; +}; + +struct Image{ + f32 tex_x, tex_y; + f32 width, height; + u32 texture_id; + i32 id; +}; + +struct Layer +{ + f32 resolution; // 1 = 'default', 2 = 2*default, etc. + u32 fbo_id; + u32 color_tex, normal_tex, depth_tex; // more? -1 means not-assigned + i32 id; +}; + + +struct Asset_Bank{ + // Asset management + Begin_Setup_Function *begin_setup; + End_Setup_Function *end_setup; + Bind_Font_Function *bind_font; + Bind_BMP_Function *bind_bmp; + Replace_BMP_Function *replace_bmp; + + // TODO(allen): Turn fonts into just a set of bitmaps. + Font *fonts; + i32 font_max_id; + + // TODO(allen): Allow for growth in the images array durring development. + Image *images; + i32 image_max_id; +}; + +struct Render_Target{ + // Immediate execution render commands + Init_Function *init; + Display_Function *display; + Set_Screen_Function *set_screen; + + Partition push_part; + + vec2 dim; + + Transform me_to_pixel_transform; + Transform transform; + + Render_Execute_Function *execute; + + void *memory; + i32 memory_size; +}; + +enum Render_Command_Type{ + render_null, + + render_dbg_rectangle, + render_dbg_parallelagram, + render_glyph, // TODO(allen): render_string would be more awesome + render_image, + + // NOTE(allen): game specific stuff + render_lane, + render_obstacle, +}; +struct Render_Command_Header{ + Render_Command_Type type; + i32 size; +}; + +struct Render_Dbg_Rectangle{ + f32_rect rect; + vec4 color; +}; + +struct Render_Dbg_Parallelagram{ + Transform transform; + vec4 color; +}; + +struct Render_Glyph{ + vec4 color; + vec2 pos; + i32 font_id; + char c; + // NOTE(allen): pad out to multiples of 8 + char filler[3]; +}; + +struct Render_Image{ + Transform transform; + i32 image_id; + char filler[4]; +}; + +struct Render_Lane{ + i32 lane_index; + f32 y_position; +}; + +struct Render_Obstacle{ + u32 type; + u32 seed; + f32 x; + i32 lane; + char filler[4]; +}; + +#define NextHeader(h) (Render_Command_Header*)((char*)h + h->size) + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_standard_types.h b/test_data/lots_of_files/cd_standard_types.h new file mode 100644 index 0000000..84ec23b --- /dev/null +++ b/test_data/lots_of_files/cd_standard_types.h @@ -0,0 +1,231 @@ +/* + * Standard types accross modules. + */ + +// TOP + +#ifndef CD_STANDARD_TYPES_H +#define CD_STANDARD_TYPES_H + +union vec2{ + struct{ + f32 v[2]; + }; + struct{ + f32 x,y; + }; +}; + +union vec3{ + struct{ + f32 v[3]; + }; + struct{ + f32 r,g,b; + }; + struct{ + f32 x,y,z; + }; + struct{ + vec2 p; + }; +}; + +union vec4{ + struct{ + f32 v[4]; + }; + struct{ + f32 r,g,b,a; + }; + struct{ + f32 x,y,z,w; + }; + struct{ + f32 x0,y0,x1,y1; + }; + struct{ + vec2 p[2]; + }; + struct{ + vec3 xyz; + }; + struct{ + vec3 rgb; + }; +}; + +inline vec2 +v2(f32 x, f32 y){ + vec2 result; + result.x = x; + result.y = y; + return(result); +} + +inline vec3 +v3(f32 x, f32 y, f32 z){ + vec3 result; + result.x = x; + result.y = y; + result.z = z; + return(result); +} + +inline vec4 +v4(f32 x, f32 y, f32 z, f32 w){ + vec4 result; + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return(result); +} + +inline vec4 +v4(vec3 xyz, f32 w){ + vec4 result; + result.xyz = xyz; + result.w = w; + return(result); +} + +inline vec2 +operator+(vec2 a, vec2 b){ + a.x += b.x; + a.y += b.y; + return(a); +} + +inline vec2 +operator-(vec2 a, vec2 b){ + a.x -= b.x; + a.y -= b.y; + return(a); +} + +inline vec2 +operator-(vec2 a){ + a.x = -a.x; + a.y = -a.y; + return(a); +} + +inline vec2 +operator*(vec2 a, f32 k){ + a.x *= k; + a.y *= k; + return(a); +} + +typedef vec4 f32_rect; + +inline f32_rect +intersect(f32_rect a, f32_rect b){ + f32_rect result; + result.x0 = Max(a.x0, b.x0); + result.y0 = Max(a.y0, b.y0); + result.x1 = Min(a.x1, b.x1); + result.y1 = Min(a.y1, b.y1); + return(result); +} + +inline f32_rect +containing(f32_rect a, f32_rect b){ + f32_rect result; + result.x0 = Min(a.x0, b.x0); + result.y0 = Min(a.y0, b.y0); + result.x1 = Max(a.x1, b.x1); + result.y1 = Max(a.y1, b.y1); + return(result); +} + +inline b32 +hit_check(i32 x, i32 y, f32_rect r){ + b32 result = 0; + if (x >= r.x0 && x <= r.x1 && y >= r.y0 && y <= r.y1){ + result = 1; + } + return(result); +} + +static f32 pi32 = 3.1415926f; + +struct Basis{ + vec2 x, y; +}; + +inline Basis +get_rotation(f32 theta){ + Basis result = {0}; + result.x = v2(COS(theta), SIN(theta)); + result.y = v2(-SIN(theta), COS(theta)); + return(result); +} + +inline Basis +invert_basis(Basis b){ + f32 det; + Basis r; + + det = 1.f/(b.x.x*b.y.y - b.x.y*b.y.x); + r.x = v2(b.y.y, -b.x.y) * det; + r.y = v2(-b.y.x, b.x.x) * det; + + return(r); +} + +inline vec2 +do_basis(vec2 p, Basis b){ + vec2 q; + q.x = p.x*b.x.x + p.y*b.y.x; + q.y = p.x*b.x.y + p.y*b.y.y; + return(q); +} + +inline Basis +basis_basis(Basis p, Basis b){ + Basis r; + r.x = do_basis(p.x, b); + r.y = do_basis(p.y, b); + return(r); +} + +struct Transform{ + Basis basis; + vec2 pos; +}; + +inline Transform +invert_transform(Transform t){ + Transform r; + r.basis = invert_basis(t.basis); + r.pos = do_basis(-t.pos, t.basis); + return(r); +} + +inline vec2 +do_transform(vec2 p, Transform t){ + vec2 q; + q = p + t.pos; + p.x = q.x*t.basis.x.x + q.y*t.basis.y.x; + p.y = q.x*t.basis.x.y + q.y*t.basis.y.y; + return(p); +} + +inline Transform +transform_transform(Transform p, Transform t){ + Transform r; + r.basis = basis_basis(p.basis, t.basis); + r.pos = p.pos + do_basis(t.pos, invert_basis(p.basis)); + return(r); +} + +static Transform identity_transform = { + {v2(1.f, 0.f), v2(0.f, 1.f)}, v2(0.f, 0.f) +}; + +#endif + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_system.h b/test_data/lots_of_files/cd_system.h new file mode 100644 index 0000000..6f74de1 --- /dev/null +++ b/test_data/lots_of_files/cd_system.h @@ -0,0 +1,68 @@ +/* + +Platform layer services to other modules + +*/ + +// TOP + +#ifndef CD_SYSTEM_H +#define CD_SYSTEM_H + +struct Platform_Handle{ + u8 opaque_memory[128]; +}; + +#ifdef DEVELOPER + +struct File_Dump{ + Platform_Handle handle; + i32 size; +}; + +#define DBG_Dump_Begin(n) File_Dump n(char *file_name) +typedef DBG_Dump_Begin(DBG_Dump_Begin_Function); + +#define DBG_Dump_End(n) b32 n(File_Dump dump, void *buffer) +typedef DBG_Dump_End(DBG_Dump_End_Function); + +#define DBG_Dump_Out(n) b32 n(char *file_name, void *buffer, i32 size); +typedef DBG_Dump_Out(DBG_Dump_Out_Function); + +#define DBG_Copy(n) b32 n(char *source, char *name) +typedef DBG_Copy(DBG_Copy_Function); + +#define DBG_Call_Script(n) b32 n(char *script) +typedef DBG_Call_Script(DBG_Call_Script_Function); + +#define DBG_Module_Path(n) i32 n(char *out, i32 capacity) +typedef DBG_Module_Path(DBG_Module_Path_Function); + +#define DBG_Working_Path(n) i32 n(char *out, i32 capacity) +typedef DBG_Working_Path(DBG_Working_Path_Function); + +#define DBG_Memory_Allocate(n) void *n(i32 size) +typedef DBG_Memory_Allocate(DBG_Memory_Allocate_Function); + +#define DBG_Memory_Free(n) void n(void *ptr) +typedef DBG_Memory_Free(DBG_Memory_Free_Function); + +#endif + +struct System_API{ +#ifdef DEVELOPER + DBG_Dump_Begin_Function *DBG_dump_begin; + DBG_Dump_End_Function *DBG_dump_end; + DBG_Dump_Out_Function *DBG_dump_out; + DBG_Copy_Function *DBG_copy; + DBG_Call_Script_Function *DBG_call_script; + DBG_Module_Path_Function *DBG_module_path; + DBG_Working_Path_Function *DBG_working_path; + DBG_Memory_Allocate_Function *DBG_memory_allocate; + DBG_Memory_Free_Function *DBG_memory_free; +#endif +}; + +#endif + +// BOTTOM \ No newline at end of file diff --git a/test_data/lots_of_files/cd_tyler_comp_unit.cpp b/test_data/lots_of_files/cd_tyler_comp_unit.cpp new file mode 100644 index 0000000..6b9f118 --- /dev/null +++ b/test_data/lots_of_files/cd_tyler_comp_unit.cpp @@ -0,0 +1,44 @@ + +#include "cd_ids.h" +#include "cd_defines.h" +#include "cd_intrin.h" +#include "cd_standard_types.h" + +#include "cd_system.h" +#include "cd.h" + +#include "cd_partition.h" +#include "stb_truetype.h" +#include "cd_render_target.h" + +#ifdef DEVELOPER +#include "cd_debug.h" +#endif + + + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +#include <windows.h> +#include <gl\gl.h> + +#define FTECH_STRING_INC +#include "4tech_string.h" +#include "cd_partition.h" + +#include "cd_defines.h" +#include "cd_standard_types.h" +#include "cd_render_target.h" + +// NOTE(allen): Not sure what the "r/*" files were +// but I don't seem to have those files after doing +// a git clone so I'm just getting rid of them for now.+ + +#include "cd_render_helper.cpp" +#include "cd_render_target.cpp" + +#include "cd_game_renderer.cpp" + + + diff --git a/test_data/lots_of_files/cd_windows_render_vars.h b/test_data/lots_of_files/cd_windows_render_vars.h new file mode 100644 index 0000000..1a613b4 --- /dev/null +++ b/test_data/lots_of_files/cd_windows_render_vars.h @@ -0,0 +1,22 @@ +/* + +Defines that the platform layer needs to see +so it can pass info to the rendering layer. + +Win32-OpenGL + +11.06.2016 (dd.mm.yyyy) +-Allen + +*/ + +#ifndef CD_WINDOWS_RENDER_VARS_H +#define CD_WINDOWS_RENDER_VARS_H + +struct Win32_Render_Vars{ + HWND hwnd; +}; + +#endif + + diff --git a/test_data/lots_of_files/cd_world.cpp b/test_data/lots_of_files/cd_world.cpp new file mode 100644 index 0000000..7a55e8a --- /dev/null +++ b/test_data/lots_of_files/cd_world.cpp @@ -0,0 +1,184 @@ +/* + +Game World simulation management +Entity types + +*/ + +// TOP + +// TODO(allen): Check more carefully if this id is safe to use! +inline u16 +get_next_entity_id(u16 counter){ + u16 next; + switch(counter){ + case max_u16: case 0: next = 1; break; + default: next = counter+1; break; + } + return(next); +} + +#define freed_slot max_u16 + +static Entity* +add_entity(World *world){ + Entity *entity = 0; + u16 entity_index = 0; + + if (world->count < ArrayCount(world->entities)){ + entity_index = world->count++; + entity = &world->entities[entity_index]; + memset(entity, 0, sizeof(*entity)); + + entity->entity_id = world->next_id; + world->next_id = get_next_entity_id(world->next_id); + + { + Entity_Hash *table = world->hash_slots; + u16 max = ArrayCount(world->hash_slots); + u16 entity_id = entity->entity_id; + u16 hash = entity_id % max; + u16 hash_slot = hash; + + while (table[hash_slot].entity_id != 0 && + table[hash_slot].entity_id != freed_slot){ + ++hash_slot; + if (hash_slot == max) hash_slot = 0; + Assert(hash_slot != hash); + } + + table[hash_slot].entity_id = entity_id; + table[hash_slot].entity_index = entity_index; + entity->hash_slot = hash_slot; + } + + entity->seed = random_next_u32(&world->entity_seed_generator); + } + + return(entity); +} + +static void +update_entity_slot(World *world, u16 entity_id, u16 new_entity_index){ + Entity_Hash *table = world->hash_slots; + u16 max = ArrayCount(world->hash_slots); + u16 hash = entity_id % max; + u16 hash_slot = hash; + + while (table[hash_slot].entity_id != entity_id){ + ++hash_slot; + if (hash_slot == max) hash_slot = 0; + Assert(hash_slot != hash); + } + + table[hash_slot].entity_index = new_entity_index; +} + +static void +kill_entity(World *world, u16 entity_id){ + Entity_Hash *table = world->hash_slots; + u16 max = ArrayCount(world->hash_slots); + u16 hash = entity_id % max; + u16 hash_slot = hash; + + while (table[hash_slot].entity_id != entity_id){ + ++hash_slot; + if (hash_slot == max) hash_slot = 0; + Assert(hash_slot != hash); + } + + table[hash_slot].entity_id = freed_slot; + + { + Entity *entities = world->entities; + u16 entity_index = table[hash_slot].entity_index; + u16 new_entity_count = world->count-1; + + world->count = new_entity_count; + if (entity_index < new_entity_count){ + entities[entity_index] = entities[new_entity_count]; + update_entity_slot(world, entities[entity_index].entity_id, entity_index); + } + } +} + +inline void +world_begin_simulation(World *world){ + world->kill_count = 0; +} + +inline void +world_end_simulation(World *world){ + Entity *entities = world->entities; + u16 kill_count = world->kill_count; + u16 *kill_list = world->kill_list; + u16 kill_index = 0; + + for (u16 kill_i = 0; + kill_i < kill_count; + ++kill_i){ + kill_index = kill_list[kill_i]; + kill_entity(world, entities[kill_index].entity_id); + } +} + +inline void +add_to_kill_list(World *world, Entity *entity){ + u16 slot_index = (u16)(entity - world->entities); + Assert(world->kill_count < ArrayCount(world->kill_list)); + world->kill_list[world->kill_count++] = slot_index; +} + + + +static Entity* +add_player(World *world){ + Entity *result = add_entity(world); + if (result){ + result->type = ET_Player; + } + return(result); +} + +static Entity* +add_asteroid(World *world, i32 lane){ + Entity *result = add_entity(world); + if (result){ + result->type = ET_Obstacle; + result->x = 25.f; + result->speed = 0.f; + result->lane = lane; + } + return(result); +} + +static Entity* +add_asteroid_spawner(World *world, f32 second_per_spawn){ + Entity *result = add_entity(world); + if (result){ + result->type = ET_Obstacle_Spawner; + result->second_per_spawn = second_per_spawn; + } + return(result); +} + +static Entity* +get_player(World *world){ + Entity *result = 0; + + Entity *entity = world->entities; + u32 count = world->count; + for (u32 i = 0; + i < count; + ++i, ++entity){ + if (entity->type == ET_Player){ + result = entity; + break; + } + } + + return(result); +} + +// BOTTOM + diff --git a/test_data/lots_of_files/cd_world.h b/test_data/lots_of_files/cd_world.h new file mode 100644 index 0000000..834a66e --- /dev/null +++ b/test_data/lots_of_files/cd_world.h @@ -0,0 +1,53 @@ +/* + +Game World data structures. + +*/ + +#ifndef CD_WORLD_H +#define CD_WORLD_H + +enum Entity_Type{ + ET_Null, + ET_Player, + ET_Obstacle, + ET_Obstacle_Spawner, + ET_Count +}; + + +struct Entity{ + u32 type; + + f32 x; // position in coordinates relative to player + f32 speed; // speed in absolute coordinates + i32 lane; // lane in absolute coordinates + f32 lane_lerp; // [-1, 1] motion to a new lane + f32 lane_lerp_dir; // -1, 0, 1 indicates current lane motion + + f32 spawn_clock; + f32 second_per_spawn; + + u32 seed; + u16 entity_id; + u16 hash_slot; +}; + +struct Entity_Hash{ + u16 entity_id; + u16 entity_index; +}; + +struct World{ + Entity_Hash hash_slots[1031]; + Entity entities[512]; + u16 count; + + u16 kill_list[512]; + u16 kill_count; + + Random_Series entity_seed_generator; + u16 next_id; +}; + +#endif \ No newline at end of file diff --git a/test_data/lots_of_files/collection.h b/test_data/lots_of_files/collection.h new file mode 100644 index 0000000..5341f5f --- /dev/null +++ b/test_data/lots_of_files/collection.h @@ -0,0 +1,2190 @@ +/*** +* collection.h - Windows Runtime Collection/Iterator Wrappers +* +* Copyright (c) Microsoft Corporation. All rights reserved. +****/ + +#pragma once + +#ifndef _COLLECTION_H_ +#define _COLLECTION_H_ + +#ifndef RC_INVOKED + +#ifndef __cplusplus_winrt + #error collection.h requires the /ZW compiler option. +#endif + +#include <stddef.h> +#include <algorithm> +#include <array> +#include <exception> +#include <functional> +#include <iterator> +#include <map> +#include <memory> +#include <new> +#include <type_traits> +#include <unordered_map> +#include <utility> +#include <vector> +#include <agile.h> + +#define _COLLECTION_ATTRIBUTES [::Platform::Metadata::RuntimeClassName] [::Windows::Foundation::Metadata::Default] + +#define _COLLECTION_TRANSLATE \ +} catch (const ::std::bad_alloc&) { \ + throw ref new OutOfMemoryException; \ +} catch (const ::std::exception&) { \ + throw ref new FailureException; \ +} + +#ifndef _COLLECTION_WUXI + #define _COLLECTION_WUXI 1 +#endif + +#ifdef _WIN64 + #pragma pack(push, 16) +#else + #pragma pack(push, 8) +#endif + +#pragma warning(push, 4) + +#pragma warning(disable: 4451) // Usage of ref class 'Meow' inside this context can lead to invalid marshaling of object across contexts + +namespace Platform { + namespace Collections { + namespace Details { + namespace WFC = ::Windows::Foundation::Collections; + +#if _COLLECTION_WUXI + namespace WUXI = ::Windows::UI::Xaml::Interop; +#endif // _COLLECTION_WUXI + + typedef ::Windows::Foundation::EventRegistrationToken Token; + + inline void ValidateBounds(bool b) { + if (!b) { + throw ref new OutOfBoundsException; + } + } + + inline void ValidateCounter(const ::std::shared_ptr<unsigned int>& ctr, unsigned int good_ctr) { + if (*ctr != good_ctr) { + throw ref new ChangedStateException; + } + } + + inline void ValidateSize(size_t n) { + if (n > 0x7FFFFFFFUL) { + throw ref new OutOfMemoryException; + } + } + + template <typename T> struct AlwaysFalse + : public ::std::false_type { }; + + template <typename T> struct Wrap { + typedef T type; + }; + + template <typename T> struct Wrap<T^> + : public ::std::conditional<__is_winrt_agile(T), T^, Agile<T^>> { }; + + template <typename T> inline const T& MakeWrap(const T& t) { + return t; + } + + template <typename T> inline typename ::std::enable_if<!__is_winrt_agile(T), Agile<T^>>::type MakeWrap(T^ const & t) { + return Agile<T^>(t); + } + + template <typename T> inline const T& Unwrap(const T& t) { + return t; + } + + template <typename T> inline T^ Unwrap(const Agile<T^>& a) { + return a.Get(); + } + + template <typename T, typename U> struct VectorEnableIf + : public ::std::enable_if< ::std::is_same<T, U>::value || ::std::is_same<typename Wrap<T>::type, U>::value, void **> { }; + + template <typename X> inline void Init(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp) { + try { + ctr = ::std::make_shared<unsigned int>(0); + sp = ::std::make_shared<X>(); + _COLLECTION_TRANSLATE + } + + template <typename X, typename A> inline void Init(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp, A&& a) { + try { + ctr = ::std::make_shared<unsigned int>(0); + sp = ::std::make_shared<X>(::std::forward<A>(a)); + + ValidateSize(sp->size()); + _COLLECTION_TRANSLATE + } + + template <typename X, typename A, typename B> inline void Init(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp, A&& a, B&& b) { + try { + ctr = ::std::make_shared<unsigned int>(0); + sp = ::std::make_shared<X>(::std::forward<A>(a), ::std::forward<B>(b)); + + ValidateSize(sp->size()); + _COLLECTION_TRANSLATE + } + + template <typename X, typename A, typename B, typename C> inline void Init(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp, A&& a, B&& b, C&& c) { + try { + ctr = ::std::make_shared<unsigned int>(0); + sp = ::std::make_shared<X>(::std::forward<A>(a), ::std::forward<B>(b), ::std::forward<C>(c)); + + ValidateSize(sp->size()); + _COLLECTION_TRANSLATE + } + + template <typename X, typename A> inline void InitMoveVector(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp, A&& a) { + Init(ctr, sp, a.begin(), a.end()); + } + + template <typename X> inline void InitMoveVector(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp, X&& x) { + Init(ctr, sp, ::std::move(x)); + } + + template <typename K, typename V, typename M, typename InIt> inline void EmplaceWrappedRange(M& m, InIt first, InIt last) { + for ( ; first != last; ++first) { + ::std::pair<const K, V> p(*first); + + m.emplace(MakeWrap(p.first), MakeWrap(p.second)); + } + } + + template <typename K, typename V, typename X, typename A> inline void InitMoveMap(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp, A&& a) { + Init(ctr, sp, a.key_comp()); + + EmplaceWrappedRange<K, V>(*sp, a.begin(), a.end()); + } + + template <typename K, typename V, typename X> inline void InitMoveMap(::std::shared_ptr<unsigned int>& ctr, ::std::shared_ptr<X>& sp, X&& x) { + Init(ctr, sp, ::std::move(x)); + } + + inline void IncrementCounter(::std::shared_ptr<unsigned int>& ctr) { + if (++*ctr == static_cast<unsigned int>(-1)) { + // Wraparound is imminent! Create a fresh counter. + ctr = ::std::make_shared<unsigned int>(0); + } + } + + ref class VectorChangedEventArgs sealed : public _COLLECTION_ATTRIBUTES WFC::IVectorChangedEventArgs { + internal: + VectorChangedEventArgs(WFC::CollectionChange change, unsigned int index) + : m_change(change), m_index(index) { } + + public: + virtual property WFC::CollectionChange CollectionChange { + virtual WFC::CollectionChange get() { + return m_change; + } + } + + virtual property unsigned int Index { + virtual unsigned int get() { + if (m_change == WFC::CollectionChange::Reset) { + throw ref new FailureException; + } + + return m_index; + } + } + + private: + WFC::CollectionChange m_change; + unsigned int m_index; + }; + + template <typename K> ref class MapChangedEventArgsReset sealed : public _COLLECTION_ATTRIBUTES WFC::IMapChangedEventArgs<K> { + public: + virtual property WFC::CollectionChange CollectionChange { + virtual WFC::CollectionChange get() { + return WFC::CollectionChange::Reset; + } + } + + virtual property K Key { + virtual K get() { + throw ref new FailureException; + } + } + }; + + template <typename K> ref class MapChangedEventArgs sealed : public _COLLECTION_ATTRIBUTES WFC::IMapChangedEventArgs<K> { + internal: + MapChangedEventArgs(WFC::CollectionChange change, K key) + : m_change(change), m_key(key) { } + + public: + virtual property WFC::CollectionChange CollectionChange { + virtual WFC::CollectionChange get() { + return m_change; + } + } + + virtual property K Key { + virtual K get() { + return Unwrap(m_key); + } + } + + private: + WFC::CollectionChange m_change; + typename Wrap<K>::type m_key; + }; + + template <typename T, typename E> inline bool VectorIndexOf(const ::std::vector<typename Wrap<T>::type>& v, T value, unsigned int * index) { + auto pred = [&](const typename Wrap<T>::type& elem) { return E()(Unwrap(elem), value); }; + + *index = static_cast<unsigned int>(::std::find_if(v.begin(), v.end(), pred) - v.begin()); + + return *index < v.size(); + } + +#if _COLLECTION_WUXI + template <typename T> struct is_hat : public ::std::false_type { }; + + template <typename U> struct is_hat<U^> : public ::std::true_type { }; + + template <typename T, typename E> inline bool VectorBindableIndexOf(::std::false_type, const ::std::vector<typename Wrap<T>::type>& v, Object^ o, unsigned int * index) { + IBox<T>^ ib = dynamic_cast<IBox<T>^>(o); + + if (ib) { + return VectorIndexOf<T, E>(v, ib->Value, index); + } else { + *index = static_cast<unsigned int>(v.size()); + return false; + } + } + + template <typename T, typename E> inline bool VectorBindableIndexOf(::std::true_type, const ::std::vector<typename Wrap<T>::type>& v, Object^ o, unsigned int * index) { + T t = dynamic_cast<T>(o); + + if (!o || t) { + return VectorIndexOf<T, E>(v, t, index); + } else { + *index = static_cast<unsigned int>(v.size()); + return false; + } + } + + template <typename T, typename E> inline bool VectorBindableIndexOf(const ::std::vector<typename Wrap<T>::type>& v, Object^ o, unsigned int * index) { + return VectorBindableIndexOf<T, E>(is_hat<T>(), v, o, index); + } +#endif // _COLLECTION_WUXI + + template <typename T> inline unsigned int VectorGetMany(const ::std::vector<typename Wrap<T>::type>& v, unsigned int startIndex, WriteOnlyArray<T>^ dest) { + unsigned int capacity = dest->Length; + + unsigned int actual = static_cast<unsigned int>(v.size()) - startIndex; + + if (actual > capacity) { + actual = capacity; + } + + for (unsigned int i = 0; i < actual; ++i) { + dest->set(i, Unwrap(v[startIndex + i])); + } + + return actual; + } + + template <typename T> ref class IteratorForVectorView sealed + : public _COLLECTION_ATTRIBUTES WFC::IIterator<T> +#if _COLLECTION_WUXI + , public WUXI::IBindableIterator +#endif // _COLLECTION_WUXI + { + private: + typedef ::std::vector<typename Wrap<T>::type> WrappedVector; + typedef WFC::IIterator<T> WFC_Base; + +#if _COLLECTION_WUXI + typedef WUXI::IBindableIterator WUXI_Base; +#endif // _COLLECTION_WUXI + + internal: + IteratorForVectorView(const ::std::shared_ptr<unsigned int>& ctr, const ::std::shared_ptr<WrappedVector>& vec) + : m_ctr(ctr), m_vec(vec), m_good_ctr(*ctr), m_index(0) { } + + public: + virtual property T Current { + virtual T get() = WFC_Base::Current::get { + ValidateCounter(m_ctr, m_good_ctr); + + ValidateBounds(m_index < m_vec->size()); + + return Unwrap((*m_vec)[m_index]); + } + } + + virtual property bool HasCurrent { + virtual bool get() { + ValidateCounter(m_ctr, m_good_ctr); + + return m_index < m_vec->size(); + } + } + + virtual bool MoveNext() { + ValidateCounter(m_ctr, m_good_ctr); + + ValidateBounds(m_index < m_vec->size()); + + ++m_index; + return m_index < m_vec->size(); + } + + virtual unsigned int GetMany(WriteOnlyArray<T>^ dest) { + ValidateCounter(m_ctr, m_good_ctr); + + unsigned int actual = VectorGetMany(*m_vec, m_index, dest); + + m_index += actual; + + return actual; + } + + private: + +#if _COLLECTION_WUXI + virtual Object^ BindableCurrent() = WUXI_Base::Current::get { + return Current; + } +#endif // _COLLECTION_WUXI + + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedVector> m_vec; + unsigned int m_good_ctr; + unsigned int m_index; + }; + } // namespace Details + + template <typename T, typename E = ::std::equal_to<T>, bool = __is_valid_winrt_type(T)> ref class Vector; + template <typename T, typename E = ::std::equal_to<T>, bool = __is_valid_winrt_type(T)> ref class VectorView; + + template <typename T, typename E> ref class VectorView<T, E, false> { + static_assert(Details::AlwaysFalse<T>::value, "Platform::Collections::VectorView<T, E> requires T to be a valid Windows Runtime type."); + }; + + template <typename T, typename E, bool> ref class VectorView sealed + : public _COLLECTION_ATTRIBUTES Details::WFC::IVectorView<T> +#if _COLLECTION_WUXI + , public Details::WUXI::IBindableVectorView +#endif // _COLLECTION_WUXI + { + private: + typedef ::std::vector<typename Details::Wrap<T>::type> WrappedVector; + typedef Details::WFC::IVectorView<T> WFC_Base; + +#if _COLLECTION_WUXI + typedef Details::WUXI::IBindableVectorView WUXI_Base; +#endif // _COLLECTION_WUXI + + internal: + VectorView() { + Details::Init(m_ctr, m_vec); + + m_good_ctr = 0; + } + + explicit VectorView(unsigned int size) { + Details::Init(m_ctr, m_vec, size); + + m_good_ctr = 0; + } + + VectorView(unsigned int size, T value) { + Details::Init(m_ctr, m_vec, size, Details::MakeWrap(value)); + + m_good_ctr = 0; + } + + template <typename U> explicit VectorView(const ::std::vector<U>& v, typename Details::VectorEnableIf<T, U>::type = nullptr) { + Details::Init(m_ctr, m_vec, v.begin(), v.end()); + + m_good_ctr = 0; + } + + template <typename U> explicit VectorView(::std::vector<U>&& v, typename Details::VectorEnableIf<T, U>::type = nullptr) { + Details::InitMoveVector(m_ctr, m_vec, ::std::move(v)); + + m_good_ctr = 0; + } + + VectorView(const T * ptr, unsigned int size) { + Details::Init(m_ctr, m_vec, ptr, ptr + size); + + m_good_ctr = 0; + } + + template <size_t N> explicit VectorView(const T (&arr)[N]) { + Details::Init(m_ctr, m_vec, arr, arr + N); + + m_good_ctr = 0; + } + + template <size_t N> explicit VectorView(const ::std::array<T, N>& a) { + Details::Init(m_ctr, m_vec, a.begin(), a.end()); + + m_good_ctr = 0; + } + + explicit VectorView(const Array<T>^ arr) { + Details::Init(m_ctr, m_vec, arr->begin(), arr->end()); + + m_good_ctr = 0; + } + + template <typename InIt> VectorView(InIt first, InIt last) { + // SFINAE is unnecessary here. + + Details::Init(m_ctr, m_vec, first, last); + + m_good_ctr = 0; + } + + VectorView(::std::initializer_list<T> il) { + Details::Init(m_ctr, m_vec, il.begin(), il.end()); + + m_good_ctr = 0; + } + + public: + virtual Details::WFC::IIterator<T>^ First() = WFC_Base::First { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return ref new Details::IteratorForVectorView<T>(m_ctr, m_vec); + } + + virtual T GetAt(unsigned int index) = WFC_Base::GetAt { + Details::ValidateCounter(m_ctr, m_good_ctr); + + Details::ValidateBounds(index < m_vec->size()); + + return Details::Unwrap((*m_vec)[index]); + } + + virtual property unsigned int Size { + virtual unsigned int get() { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return static_cast<unsigned int>(m_vec->size()); + } + } + + virtual bool IndexOf(T value, unsigned int * index) = WFC_Base::IndexOf { + *index = 0; + + Details::ValidateCounter(m_ctr, m_good_ctr); + + return Details::VectorIndexOf<T, E>(*m_vec, value, index); + } + + virtual unsigned int GetMany(unsigned int startIndex, WriteOnlyArray<T>^ dest) { + Details::ValidateCounter(m_ctr, m_good_ctr); + + Details::ValidateBounds(startIndex <= m_vec->size()); + + return Details::VectorGetMany(*m_vec, startIndex, dest); + } + + private: + friend ref class Vector<T, E>; + + VectorView(const ::std::shared_ptr<unsigned int>& ctr, const ::std::shared_ptr<WrappedVector>& vec) + : m_ctr(ctr), m_vec(vec), m_good_ctr(*ctr) { } + +#if _COLLECTION_WUXI + virtual Details::WUXI::IBindableIterator^ BindableFirst() = WUXI_Base::First { + return safe_cast<Details::WUXI::IBindableIterator^>(First()); + } + + virtual Object^ BindableGetAt(unsigned int index) = WUXI_Base::GetAt { + return GetAt(index); + } + + virtual bool BindableIndexOf(Object^ value, unsigned int * index) = WUXI_Base::IndexOf { + *index = 0; + + Details::ValidateCounter(m_ctr, m_good_ctr); + + return Details::VectorBindableIndexOf<T, E>(*m_vec, value, index); + } +#endif // _COLLECTION_WUXI + + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedVector> m_vec; + unsigned int m_good_ctr; + }; + + template <typename T, typename E> ref class Vector<T, E, false> { + static_assert(Details::AlwaysFalse<T>::value, "Platform::Collections::Vector<T, E> requires T to be a valid Windows Runtime type."); + }; + + template <typename T, typename E, bool> ref class Vector sealed + : public _COLLECTION_ATTRIBUTES Details::WFC::IObservableVector<T> +#if _COLLECTION_WUXI + , public Details::WUXI::IBindableObservableVector +#endif // _COLLECTION_WUXI + { + private: + typedef ::std::vector<typename Details::Wrap<T>::type> WrappedVector; + typedef Details::WFC::IObservableVector<T> WFC_Base; + typedef Details::WFC::VectorChangedEventHandler<T> WFC_Handler; + +#if _COLLECTION_WUXI + typedef Details::WUXI::IBindableObservableVector WUXI_Base; + typedef Details::WUXI::BindableVectorChangedEventHandler WUXI_Handler; +#endif // _COLLECTION_WUXI + + internal: + Vector() { + Details::Init(m_ctr, m_vec); + + m_observed = false; + } + + explicit Vector(unsigned int size) { + Details::Init(m_ctr, m_vec, size); + + m_observed = false; + } + + Vector(unsigned int size, T value) { + Details::Init(m_ctr, m_vec, size, Details::MakeWrap(value)); + + m_observed = false; + } + + template <typename U> explicit Vector(const ::std::vector<U>& v, typename Details::VectorEnableIf<T, U>::type = nullptr) { + Details::Init(m_ctr, m_vec, v.begin(), v.end()); + + m_observed = false; + } + + template <typename U> explicit Vector(::std::vector<U>&& v, typename Details::VectorEnableIf<T, U>::type = nullptr) { + Details::InitMoveVector(m_ctr, m_vec, ::std::move(v)); + + m_observed = false; + } + + Vector(const T * ptr, unsigned int size) { + Details::Init(m_ctr, m_vec, ptr, ptr + size); + + m_observed = false; + } + + template <size_t N> explicit Vector(const T (&arr)[N]) { + Details::Init(m_ctr, m_vec, arr, arr + N); + + m_observed = false; + } + + template <size_t N> explicit Vector(const ::std::array<T, N>& a) { + Details::Init(m_ctr, m_vec, a.begin(), a.end()); + + m_observed = false; + } + + explicit Vector(const Array<T>^ arr) { + Details::Init(m_ctr, m_vec, arr->begin(), arr->end()); + + m_observed = false; + } + + template <typename InIt> Vector(InIt first, InIt last) { + // SFINAE is unnecessary here. + + Details::Init(m_ctr, m_vec, first, last); + + m_observed = false; + } + + Vector(::std::initializer_list<T> il) { + Details::Init(m_ctr, m_vec, il.begin(), il.end()); + + m_observed = false; + } + + public: + virtual Details::WFC::IIterator<T>^ First() = WFC_Base::First { + return ref new Details::IteratorForVectorView<T>(m_ctr, m_vec); + } + + virtual T GetAt(unsigned int index) = WFC_Base::GetAt { + Details::ValidateBounds(index < m_vec->size()); + + return Details::Unwrap((*m_vec)[index]); + } + + virtual property unsigned int Size { + virtual unsigned int get() { + return static_cast<unsigned int>(m_vec->size()); + } + } + + virtual bool IndexOf(T value, unsigned int * index) = WFC_Base::IndexOf { + *index = 0; + + return Details::VectorIndexOf<T, E>(*m_vec, value, index); + } + + virtual unsigned int GetMany(unsigned int startIndex, WriteOnlyArray<T>^ dest) { + Details::ValidateBounds(startIndex <= m_vec->size()); + + return Details::VectorGetMany(*m_vec, startIndex, dest); + } + + virtual Details::WFC::IVectorView<T>^ GetView() = WFC_Base::GetView { + return ref new VectorView<T, E>(m_ctr, m_vec); + } + + virtual void SetAt(unsigned int index, T item) = WFC_Base::SetAt { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateBounds(index < m_vec->size()); + + (*m_vec)[index] = item; + + NotifyChanged(index); + _COLLECTION_TRANSLATE + } + + virtual void InsertAt(unsigned int index, T item) = WFC_Base::InsertAt { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateBounds(index <= m_vec->size()); + + Details::ValidateSize(m_vec->size() + 1); + + Emplace(m_vec->begin() + index, item, ::std::is_same<T, bool>()); + + NotifyInserted(index); + _COLLECTION_TRANSLATE + } + + virtual void Append(T item) = WFC_Base::Append { + try { + Details::IncrementCounter(m_ctr); + + size_t n = m_vec->size(); + + Details::ValidateSize(n + 1); + + EmplaceBack(item, ::std::is_same<T, bool>()); + + NotifyInserted(static_cast<unsigned int>(n)); + _COLLECTION_TRANSLATE + } + + virtual void RemoveAt(unsigned int index) { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateBounds(index < m_vec->size()); + + m_vec->erase(m_vec->begin() + index); + + NotifyRemoved(index); + _COLLECTION_TRANSLATE + } + + virtual void RemoveAtEnd() { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateBounds(!m_vec->empty()); + + m_vec->pop_back(); + + NotifyRemoved(static_cast<unsigned int>(m_vec->size())); + _COLLECTION_TRANSLATE + } + + virtual void Clear() { + try { + Details::IncrementCounter(m_ctr); + + m_vec->clear(); + + NotifyReset(); + _COLLECTION_TRANSLATE + } + + virtual void ReplaceAll(const Array<T>^ arr) { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateSize(arr->Length); + + m_vec->assign(arr->begin(), arr->end()); + + NotifyReset(); + _COLLECTION_TRANSLATE + } + + virtual event WFC_Handler^ VectorChanged { + virtual Details::Token add(WFC_Handler^ e) = WFC_Base::VectorChanged::add { + m_observed = true; + return m_wfc_event += e; + } + + virtual void remove(Details::Token t) = WFC_Base::VectorChanged::remove { + m_wfc_event -= t; + } + }; + + private: + template <typename A, typename B> void Emplace(A&& a, B&& b, ::std::false_type) { + m_vec->emplace(::std::forward<A>(a), ::std::forward<B>(b)); + } + + template <typename A, typename B> void Emplace(A&& a, B&& b, ::std::true_type) { + m_vec->insert(::std::forward<A>(a), ::std::forward<B>(b)); + } + + template <typename A> void EmplaceBack(A&& a, ::std::false_type) { + m_vec->emplace_back(::std::forward<A>(a)); + } + + template <typename A> void EmplaceBack(A&& a, ::std::true_type) { + m_vec->push_back(::std::forward<A>(a)); + } + + void Notify(Details::WFC::CollectionChange change, unsigned int index) { + if (m_observed) { + auto args = ref new Details::VectorChangedEventArgs(change, index); + m_wfc_event(this, args); + +#if _COLLECTION_WUXI + m_wuxi_event(this, args); +#endif // _COLLECTION_WUXI + + } + } + + void NotifyReset() { + Notify(Details::WFC::CollectionChange::Reset, 0); + } + + void NotifyInserted(unsigned int index) { + Notify(Details::WFC::CollectionChange::ItemInserted, index); + } + + void NotifyRemoved(unsigned int index) { + Notify(Details::WFC::CollectionChange::ItemRemoved, index); + } + + void NotifyChanged(unsigned int index) { + Notify(Details::WFC::CollectionChange::ItemChanged, index); + } + +#if _COLLECTION_WUXI + virtual Details::WUXI::IBindableIterator^ BindableFirst() = WUXI_Base::First { + return safe_cast<Details::WUXI::IBindableIterator^>(First()); + } + + virtual Object^ BindableGetAt(unsigned int index) = WUXI_Base::GetAt { + return GetAt(index); + } + + virtual bool BindableIndexOf(Object^ value, unsigned int * index) = WUXI_Base::IndexOf { + *index = 0; + + return Details::VectorBindableIndexOf<T, E>(*m_vec, value, index); + } + + virtual Details::WUXI::IBindableVectorView^ BindableGetView() = WUXI_Base::GetView { + return safe_cast<Details::WUXI::IBindableVectorView^>(GetView()); + } + + virtual void BindableSetAt(unsigned int index, Object^ item) = WUXI_Base::SetAt { + SetAt(index, safe_cast<T>(item)); + } + + virtual void BindableInsertAt(unsigned int index, Object^ item) = WUXI_Base::InsertAt { + InsertAt(index, safe_cast<T>(item)); + } + + virtual void BindableAppend(Object^ item) = WUXI_Base::Append { + Append(safe_cast<T>(item)); + } + + virtual Details::Token BindableEventAdd(WUXI_Handler^ e) = WUXI_Base::VectorChanged::add { + m_observed = true; + return m_wuxi_event += e; + } + + virtual void BindableEventRemove(Details::Token t) = WUXI_Base::VectorChanged::remove { + m_wuxi_event -= t; + } +#endif // _COLLECTION_WUXI + + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedVector> m_vec; + bool m_observed; + + event WFC_Handler^ m_wfc_event; + +#if _COLLECTION_WUXI + event WUXI_Handler^ m_wuxi_event; +#endif // _COLLECTION_WUXI + + }; + + + namespace Details { + template <typename K, typename V> ref class KeyValuePair sealed : public _COLLECTION_ATTRIBUTES WFC::IKeyValuePair<K, V> { + internal: + KeyValuePair(const typename Wrap<K>::type& key, const typename Wrap<V>::type& value) + : m_key(key), m_value(value) { } + + public: + virtual property K Key { + virtual K get() { + return Unwrap(m_key); + } + } + + virtual property V Value { + virtual V get() { + return Unwrap(m_value); + } + } + + private: + typename Wrap<K>::type m_key; + typename Wrap<V>::type m_value; + }; + + template <typename K, typename F, bool = ::std::is_same<typename Wrap<K>::type, K>::value> class WrapFunc { + public: + typedef F type; + }; + + template <typename K, typename F> class WrapFunc<K, F, false> { + public: + typedef WrapFunc type; + + WrapFunc(const F& func) + : m_func(func) { } + + size_t operator()(const Agile<K>& k) const { + return m_func(k.Get()); + } + + bool operator()(const Agile<K>& l, const Agile<K>& r) const { + return m_func(l.Get(), r.Get()); + } + + private: + F m_func; + }; + + template <typename K, typename V, typename C> struct WrapMap { + typedef ::std::map<typename Wrap<K>::type, typename Wrap<V>::type, typename WrapFunc<K, C>::type> type; + }; + + template <typename K, typename V, typename H, typename P> struct WrapUnorderedMap { + typedef ::std::unordered_map<typename Wrap<K>::type, typename Wrap<V>::type, typename WrapFunc<K, H>::type, typename WrapFunc<K, P>::type> type; + }; + + template <typename K, typename V, typename WrappedMap> ref class IteratorForAnyMapView sealed : public _COLLECTION_ATTRIBUTES WFC::IIterator<WFC::IKeyValuePair<K, V>^> { + + internal: + IteratorForAnyMapView(const ::std::shared_ptr<unsigned int>& ctr, const ::std::shared_ptr<WrappedMap>& m) + : m_ctr(ctr), m_map(m), m_good_ctr(*ctr), m_iter(m->begin()) { } + + public: + virtual property WFC::IKeyValuePair<K, V>^ Current { + virtual WFC::IKeyValuePair<K, V>^ get() { + ValidateCounter(m_ctr, m_good_ctr); + + ValidateBounds(m_iter != m_map->end()); + + return ref new KeyValuePair<K, V>(m_iter->first, m_iter->second); + } + } + + virtual property bool HasCurrent { + virtual bool get() { + ValidateCounter(m_ctr, m_good_ctr); + + return m_iter != m_map->end(); + } + } + + virtual bool MoveNext() { + ValidateCounter(m_ctr, m_good_ctr); + + ValidateBounds(m_iter != m_map->end()); + + ++m_iter; + return m_iter != m_map->end(); + } + + virtual unsigned int GetMany(WriteOnlyArray<WFC::IKeyValuePair<K, V>^>^ dest) { + ValidateCounter(m_ctr, m_good_ctr); + + unsigned int capacity = dest->Length; + + unsigned int actual = 0; + + while (capacity > 0 && m_iter != m_map->end()) { + dest->set(actual, ref new KeyValuePair<K, V>(m_iter->first, m_iter->second)); + ++m_iter; + --capacity; + ++actual; + } + + return actual; + } + + private: + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedMap> m_map; + unsigned int m_good_ctr; + typename WrappedMap::const_iterator m_iter; + }; + } // namespace Details + + template <typename K, typename V, typename C = ::std::less<K>, bool = __is_valid_winrt_type(K), bool = __is_valid_winrt_type(V)> ref class Map; + template <typename K, typename V, typename C = ::std::less<K>, bool = __is_valid_winrt_type(K), bool = __is_valid_winrt_type(V)> ref class MapView; + template <typename K, typename V, typename H = ::std::hash<K>, typename P = ::std::equal_to<K>, bool = __is_valid_winrt_type(K), bool = __is_valid_winrt_type(V)> ref class UnorderedMap; + template <typename K, typename V, typename H = ::std::hash<K>, typename P = ::std::equal_to<K>, bool = __is_valid_winrt_type(K), bool = __is_valid_winrt_type(V)> ref class UnorderedMapView; + + template <typename K, typename V, typename C> ref class MapView<K, V, C, false, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::MapView<K, V, C> requires K and V to be valid Windows Runtime types."); + }; + + template <typename K, typename V, typename C> ref class MapView<K, V, C, false, true> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::MapView<K, V, C> requires K to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename C> ref class MapView<K, V, C, true, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::MapView<K, V, C> requires V to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename C, bool, bool> ref class MapView sealed : public _COLLECTION_ATTRIBUTES Details::WFC::IMapView<K, V> { + private: + typedef typename Details::WrapMap<K, V, C>::type WrappedMap; + typedef Details::IteratorForAnyMapView<K, V, WrappedMap> MyIterator; + friend ref class Map<K, V, C>; + + internal: + explicit MapView(const C& comp = C()) { + Details::Init(m_ctr, m_map, comp); + + m_good_ctr = 0; + } + + explicit MapView(const ::std::map<K, V, C>& m) { + Details::Init(m_ctr, m_map, m.key_comp()); + + Details::EmplaceWrappedRange<K, V>(*m_map, m.begin(), m.end()); + + m_good_ctr = 0; + } + + explicit MapView(::std::map<K, V, C>&& m) { + Details::InitMoveMap<K, V>(m_ctr, m_map, ::std::move(m)); + + m_good_ctr = 0; + } + + template <typename InIt> MapView(InIt first, InIt last, const C& comp = C()) { + Details::Init(m_ctr, m_map, comp); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_good_ctr = 0; + } + + MapView(::std::initializer_list< ::std::pair<const K, V>> il, const C& comp = C()) { + Details::Init(m_ctr, m_map, comp); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_good_ctr = 0; + } + + public: + virtual Details::WFC::IIterator<Details::WFC::IKeyValuePair<K, V>^>^ First() { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return ref new MyIterator(m_ctr, m_map); + } + + virtual V Lookup(K key) { + Details::ValidateCounter(m_ctr, m_good_ctr); + + auto i = m_map->find(Details::MakeWrap(key)); + + Details::ValidateBounds(i != m_map->end()); + + return Details::Unwrap(i->second); + } + + virtual property unsigned int Size { + virtual unsigned int get() { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return static_cast<unsigned int>(m_map->size()); + } + } + + virtual bool HasKey(K key) { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return m_map->find(Details::MakeWrap(key)) != m_map->end(); + } + + virtual void Split(Details::WFC::IMapView<K, V>^ * firstPartition, Details::WFC::IMapView<K, V>^ * secondPartition) { + *firstPartition = nullptr; + *secondPartition = nullptr; + + Details::ValidateCounter(m_ctr, m_good_ctr); + } + + private: + MapView(const ::std::shared_ptr<unsigned int>& ctr, const ::std::shared_ptr<WrappedMap>& m) + : m_ctr(ctr), m_map(m), m_good_ctr(*ctr) { } + + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedMap> m_map; + unsigned int m_good_ctr; + }; + + template <typename K, typename V, typename H, typename P> ref class UnorderedMapView<K, V, H, P, false, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::UnorderedMapView<K, V, H, P> requires K and V to be valid Windows Runtime types."); + }; + + template <typename K, typename V, typename H, typename P> ref class UnorderedMapView<K, V, H, P, false, true> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::UnorderedMapView<K, V, H, P> requires K to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename H, typename P> ref class UnorderedMapView<K, V, H, P, true, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::UnorderedMapView<K, V, H, P> requires V to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename H, typename P, bool, bool> ref class UnorderedMapView sealed + : public _COLLECTION_ATTRIBUTES Details::WFC::IMapView<K, V> { + private: + typedef typename Details::WrapUnorderedMap<K, V, H, P>::type WrappedMap; + typedef Details::IteratorForAnyMapView<K, V, WrappedMap> MyIterator; + friend ref class UnorderedMap<K, V, H, P>; + + internal: + UnorderedMapView() { + Details::Init(m_ctr, m_map); + + m_good_ctr = 0; + } + + explicit UnorderedMapView(size_t n) { + Details::Init(m_ctr, m_map, n, H(), P()); + + m_good_ctr = 0; + } + + UnorderedMapView(size_t n, const H& h) { + Details::Init(m_ctr, m_map, n, h, P()); + + m_good_ctr = 0; + } + + UnorderedMapView(size_t n, const H& h, const P& p) { + Details::Init(m_ctr, m_map, n, h, p); + + m_good_ctr = 0; + } + + explicit UnorderedMapView(const ::std::unordered_map<K, V, H, P>& m) { + Details::Init(m_ctr, m_map, m.bucket_count(), m.hash_function(), m.key_eq()); + + Details::EmplaceWrappedRange<K, V>(*m_map, m.begin(), m.end()); + + m_good_ctr = 0; + } + + explicit UnorderedMapView(::std::unordered_map<K, V, H, P>&& m) { + Details::InitMoveMap<K, V>(m_ctr, m_map, ::std::move(m)); + + m_good_ctr = 0; + } + + template <typename InIt> UnorderedMapView(InIt first, InIt last) { + Details::Init(m_ctr, m_map); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_good_ctr = 0; + } + + template <typename InIt> UnorderedMapView(InIt first, InIt last, size_t n) { + Details::Init(m_ctr, m_map, n, H(), P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_good_ctr = 0; + } + + template <typename InIt> UnorderedMapView(InIt first, InIt last, size_t n, const H& h) { + Details::Init(m_ctr, m_map, n, h, P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_good_ctr = 0; + } + + template <typename InIt> UnorderedMapView(InIt first, InIt last, size_t n, const H& h, const P& p) { + Details::Init(m_ctr, m_map, n, h, p); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_good_ctr = 0; + } + + UnorderedMapView(::std::initializer_list< ::std::pair<const K, V>> il) { + Details::Init(m_ctr, m_map); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_good_ctr = 0; + } + + UnorderedMapView(::std::initializer_list< ::std::pair<const K, V>> il, size_t n) { + Details::Init(m_ctr, m_map, n, H(), P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_good_ctr = 0; + } + + UnorderedMapView(::std::initializer_list< ::std::pair<const K, V>> il, size_t n, const H& h) { + Details::Init(m_ctr, m_map, n, h, P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_good_ctr = 0; + } + + UnorderedMapView(::std::initializer_list< ::std::pair<const K, V>> il, size_t n, const H& h, const P& p) { + Details::Init(m_ctr, m_map, n, h, p); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_good_ctr = 0; + } + + public: + virtual Details::WFC::IIterator<Details::WFC::IKeyValuePair<K, V>^>^ First() { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return ref new MyIterator(m_ctr, m_map); + } + + virtual V Lookup(K key) { + Details::ValidateCounter(m_ctr, m_good_ctr); + + auto i = m_map->find(Details::MakeWrap(key)); + + Details::ValidateBounds(i != m_map->end()); + + return Details::Unwrap(i->second); + } + + virtual property unsigned int Size { + virtual unsigned int get() { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return static_cast<unsigned int>(m_map->size()); + } + } + + virtual bool HasKey(K key) { + Details::ValidateCounter(m_ctr, m_good_ctr); + + return m_map->find(Details::MakeWrap(key)) != m_map->end(); + } + + virtual void Split(Details::WFC::IMapView<K, V>^ * firstPartition, Details::WFC::IMapView<K, V>^ * secondPartition) { + *firstPartition = nullptr; + *secondPartition = nullptr; + + Details::ValidateCounter(m_ctr, m_good_ctr); + } + + private: + UnorderedMapView(const ::std::shared_ptr<unsigned int>& ctr, const ::std::shared_ptr<WrappedMap>& m) + : m_ctr(ctr), m_map(m), m_good_ctr(*ctr) { } + + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedMap> m_map; + unsigned int m_good_ctr; + }; + + template <typename K, typename V, typename C> ref class Map<K, V, C, false, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::Map<K, V, C> requires K and V to be valid Windows Runtime types."); + }; + + template <typename K, typename V, typename C> ref class Map<K, V, C, false, true> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::Map<K, V, C> requires K to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename C> ref class Map<K, V, C, true, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::Map<K, V, C> requires V to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename C, bool, bool> ref class Map sealed : public _COLLECTION_ATTRIBUTES Details::WFC::IObservableMap<K, V> { + private: + typedef typename Details::WrapMap<K, V, C>::type WrappedMap; + typedef Details::IteratorForAnyMapView<K, V, WrappedMap> MyIterator; + typedef MapView<K, V, C> MyView; + typedef Details::WFC::MapChangedEventHandler<K, V> WFC_Handler; + + internal: + explicit Map(const C& comp = C()) { + Details::Init(m_ctr, m_map, comp); + + m_observed = false; + } + + explicit Map(const ::std::map<K, V, C>& m) { + Details::Init(m_ctr, m_map, m.key_comp()); + + Details::EmplaceWrappedRange<K, V>(*m_map, m.begin(), m.end()); + + m_observed = false; + } + + explicit Map(::std::map<K, V, C>&& m) { + Details::InitMoveMap<K, V>(m_ctr, m_map, ::std::move(m)); + + m_observed = false; + } + + template <typename InIt> Map(InIt first, InIt last, const C& comp = C()) { + Details::Init(m_ctr, m_map, comp); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_observed = false; + } + + Map(::std::initializer_list< ::std::pair<const K, V>> il, const C& comp = C()) { + Details::Init(m_ctr, m_map, comp); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_observed = false; + } + + public: + virtual Details::WFC::IIterator<Details::WFC::IKeyValuePair<K, V>^>^ First() { + return ref new MyIterator(m_ctr, m_map); + } + + virtual V Lookup(K key) { + auto i = m_map->find(Details::MakeWrap(key)); + + Details::ValidateBounds(i != m_map->end()); + + return Details::Unwrap(i->second); + } + + virtual property unsigned int Size { + virtual unsigned int get() { + return static_cast<unsigned int>(m_map->size()); + } + } + + virtual bool HasKey(K key) { + return m_map->find(Details::MakeWrap(key)) != m_map->end(); + } + + virtual Details::WFC::IMapView<K, V>^ GetView() { + return ref new MyView(m_ctr, m_map); + } + + virtual bool Insert(K key, V value) { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateSize(m_map->size() + 1); + + auto p = m_map->emplace(Details::MakeWrap(key), Details::MakeWrap(value)); + + if (p.second) { + NotifyInserted(key); + } else { + p.first->second = value; + NotifyChanged(key); + } + + return !p.second; + _COLLECTION_TRANSLATE + } + + virtual void Remove(K key) { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateBounds(m_map->erase(Details::MakeWrap(key)) == 1); + + NotifyRemoved(key); + _COLLECTION_TRANSLATE + } + + virtual void Clear() { + try { + Details::IncrementCounter(m_ctr); + + m_map->clear(); + + NotifyReset(); + _COLLECTION_TRANSLATE + } + + virtual event WFC_Handler^ MapChanged { + virtual Details::Token add(WFC_Handler^ e) { + m_observed = true; + return m_wfc_event += e; + } + + virtual void remove(Details::Token t) { + m_wfc_event -= t; + } + }; + + private: + void NotifyReset() { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgsReset<K>); + } + } + + void NotifyInserted(K key) { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgs<K>(Details::WFC::CollectionChange::ItemInserted, key)); + } + } + + void NotifyRemoved(K key) { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgs<K>(Details::WFC::CollectionChange::ItemRemoved, key)); + } + } + + void NotifyChanged(K key) { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgs<K>(Details::WFC::CollectionChange::ItemChanged, key)); + } + } + + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedMap> m_map; + bool m_observed; + + event WFC_Handler^ m_wfc_event; + }; + + template <typename K, typename V, typename H, typename P> ref class UnorderedMap<K, V, H, P, false, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::UnorderedMap<K, V, H, P> requires K and V to be valid Windows Runtime types."); + }; + + template <typename K, typename V, typename H, typename P> ref class UnorderedMap<K, V, H, P, false, true> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::UnorderedMap<K, V, H, P> requires K to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename H, typename P> ref class UnorderedMap<K, V, H, P, true, false> { + static_assert(Details::AlwaysFalse<K>::value, "Platform::Collections::UnorderedMap<K, V, H, P> requires V to be a valid Windows Runtime type."); + }; + + template <typename K, typename V, typename H, typename P, bool, bool> ref class UnorderedMap sealed : public _COLLECTION_ATTRIBUTES Details::WFC::IObservableMap<K, V> { + private: + typedef typename Details::WrapUnorderedMap<K, V, H, P>::type WrappedMap; + typedef Details::IteratorForAnyMapView<K, V, WrappedMap> MyIterator; + typedef UnorderedMapView<K, V, H, P> MyView; + typedef Details::WFC::MapChangedEventHandler<K, V> WFC_Handler; + + internal: + UnorderedMap() { + Details::Init(m_ctr, m_map); + + m_observed = false; + } + + explicit UnorderedMap(size_t n) { + Details::Init(m_ctr, m_map, n, H(), P()); + + m_observed = false; + } + + UnorderedMap(size_t n, const H& h) { + Details::Init(m_ctr, m_map, n, h, P()); + + m_observed = false; + } + + UnorderedMap(size_t n, const H& h, const P& p) { + Details::Init(m_ctr, m_map, n, h, p); + + m_observed = false; + } + + explicit UnorderedMap(const ::std::unordered_map<K, V, H, P>& m) { + Details::Init(m_ctr, m_map, m.bucket_count(), m.hash_function(), m.key_eq()); + + Details::EmplaceWrappedRange<K, V>(*m_map, m.begin(), m.end()); + + m_observed = false; + } + + explicit UnorderedMap(::std::unordered_map<K, V, H, P>&& m) { + Details::InitMoveMap<K, V>(m_ctr, m_map, ::std::move(m)); + + m_observed = false; + } + + template <typename InIt> UnorderedMap(InIt first, InIt last) { + Details::Init(m_ctr, m_map); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_observed = false; + } + + template <typename InIt> UnorderedMap(InIt first, InIt last, size_t n) { + Details::Init(m_ctr, m_map, n, H(), P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_observed = false; + } + + template <typename InIt> UnorderedMap(InIt first, InIt last, size_t n, const H& h) { + Details::Init(m_ctr, m_map, n, h, P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_observed = false; + } + + template <typename InIt> UnorderedMap(InIt first, InIt last, size_t n, const H& h, const P& p) { + Details::Init(m_ctr, m_map, n, h, p); + + Details::EmplaceWrappedRange<K, V>(*m_map, first, last); + + m_observed = false; + } + + UnorderedMap(::std::initializer_list< ::std::pair<const K, V>> il) { + Details::Init(m_ctr, m_map); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_observed = false; + } + + UnorderedMap(::std::initializer_list< ::std::pair<const K, V>> il, size_t n) { + Details::Init(m_ctr, m_map, n, H(), P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_observed = false; + } + + UnorderedMap(::std::initializer_list< ::std::pair<const K, V>> il, size_t n, const H& h) { + Details::Init(m_ctr, m_map, n, h, P()); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_observed = false; + } + + UnorderedMap(::std::initializer_list< ::std::pair<const K, V>> il, size_t n, const H& h, const P& p) { + Details::Init(m_ctr, m_map, n, h, p); + + Details::EmplaceWrappedRange<K, V>(*m_map, il.begin(), il.end()); + + m_observed = false; + } + + public: + virtual Details::WFC::IIterator<Details::WFC::IKeyValuePair<K, V>^>^ First() { + return ref new MyIterator(m_ctr, m_map); + } + + virtual V Lookup(K key) { + auto i = m_map->find(Details::MakeWrap(key)); + + Details::ValidateBounds(i != m_map->end()); + + return Details::Unwrap(i->second); + } + + virtual property unsigned int Size { + virtual unsigned int get() { + return static_cast<unsigned int>(m_map->size()); + } + } + + virtual bool HasKey(K key) { + return m_map->find(Details::MakeWrap(key)) != m_map->end(); + } + + virtual Details::WFC::IMapView<K, V>^ GetView() { + return ref new MyView(m_ctr, m_map); + } + + virtual bool Insert(K key, V value) { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateSize(m_map->size() + 1); + + auto p = m_map->emplace(Details::MakeWrap(key), Details::MakeWrap(value)); + + if (p.second) { + NotifyInserted(key); + } else { + p.first->second = value; + NotifyChanged(key); + } + + return !p.second; + _COLLECTION_TRANSLATE + } + + virtual void Remove(K key) { + try { + Details::IncrementCounter(m_ctr); + + Details::ValidateBounds(m_map->erase(Details::MakeWrap(key)) == 1); + + NotifyRemoved(key); + _COLLECTION_TRANSLATE + } + + virtual void Clear() { + try { + Details::IncrementCounter(m_ctr); + + m_map->clear(); + + NotifyReset(); + _COLLECTION_TRANSLATE + } + + virtual event WFC_Handler^ MapChanged { + virtual Details::Token add(WFC_Handler^ e) { + m_observed = true; + return m_wfc_event += e; + } + + virtual void remove(Details::Token t) { + m_wfc_event -= t; + } + }; + + private: + void NotifyReset() { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgsReset<K>); + } + } + + void NotifyInserted(K key) { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgs<K>(Details::WFC::CollectionChange::ItemInserted, key)); + } + } + + void NotifyRemoved(K key) { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgs<K>(Details::WFC::CollectionChange::ItemRemoved, key)); + } + } + + void NotifyChanged(K key) { + if (m_observed) { + m_wfc_event(this, ref new Details::MapChangedEventArgs<K>(Details::WFC::CollectionChange::ItemChanged, key)); + } + } + + ::std::shared_ptr<unsigned int> m_ctr; + ::std::shared_ptr<WrappedMap> m_map; + bool m_observed; + + event WFC_Handler^ m_wfc_event; + }; + + + template <typename X> class InputIterator; + template <typename T> class VectorIterator; + template <typename T> class VectorViewIterator; + template <typename T> class BackInsertIterator; + } // namespace Collections +} // namespace Platform + +template <typename X> struct ::std::_Is_checked_helper< ::Platform::Collections::InputIterator<X>> + : public ::std::true_type { }; + +template <typename T> struct ::std::_Is_checked_helper< ::Platform::Collections::VectorIterator<T>> + : public ::std::true_type { }; + +template <typename T> struct ::std::_Is_checked_helper< ::Platform::Collections::VectorViewIterator<T>> + : public ::std::true_type { }; + +template <typename T> struct ::std::_Is_checked_helper< ::Platform::Collections::BackInsertIterator<T>> + : public ::std::true_type { }; + +namespace Platform { + namespace Collections { + template <typename X> class InputIterator { + public: + typedef ::std::input_iterator_tag iterator_category; + typedef X value_type; + typedef ptrdiff_t difference_type; + typedef const X * pointer; + typedef const X & reference; + + InputIterator() { } + + explicit InputIterator(Details::WFC::IIterator<X>^ iter) { + if (iter->HasCurrent) { + m_iter = iter; + m_val = iter->Current; + } + } + + bool operator==(const InputIterator& other) const { + return !!m_iter == !!other.m_iter; + } + + bool operator!=(const InputIterator& other) const { + return !(*this == other); + } + + reference operator*() const { + return m_val; + } + + pointer operator->() const { + return &m_val; + } + + InputIterator& operator++() { + if (m_iter->MoveNext()) { + m_val = m_iter->Current; + } else { + m_iter = nullptr; + } + + return *this; + } + + InputIterator operator++(int) { + InputIterator old(*this); + ++*this; + return old; + } + + private: + Details::WFC::IIterator<X>^ m_iter; + X m_val; + }; + + namespace Details { + template <typename T> class VectorProxy { + public: + VectorProxy(WFC::IVector<T>^ v, ptrdiff_t n) + : m_v(v), m_i(static_cast<unsigned int>(n)) { } + + VectorProxy& operator=(const VectorProxy& other) { + m_v->SetAt(m_i, other.m_v->GetAt(other.m_i)); + return *this; + } + + VectorProxy& operator=(T t) { + m_v->SetAt(m_i, t); + return *this; + } + + operator T() const { + return m_v->GetAt(m_i); + } + + T operator->() const { + return m_v->GetAt(m_i); + } + + void swap(const VectorProxy& other) const { + T t1(m_v->GetAt(m_i)); + T t2(other.m_v->GetAt(other.m_i)); + + m_v->SetAt(m_i, t2); + other.m_v->SetAt(other.m_i, t1); + } + + void swap(T& t) const { + T temp(t); + t = m_v->GetAt(m_i); + m_v->SetAt(m_i, temp); + } + + private: + WFC::IVector<T>^ m_v; + unsigned int m_i; + }; + + template <typename T> inline void swap(const VectorProxy<T>& l, const VectorProxy<T>& r) { + l.swap(r); + } + + template <typename T> inline void swap(const VectorProxy<T>& p, T& t) { + p.swap(t); + } + + template <typename T> inline void swap(T& t, const VectorProxy<T>& p) { + p.swap(t); + } + + template <typename T> inline bool operator==(const VectorProxy<T>& l, const VectorProxy<T>& r) { + return static_cast<T>(l) == static_cast<T>(r); + } + + template <typename T> inline bool operator==(const VectorProxy<T>& l, const T& t) { + return static_cast<T>(l) == t; + } + + template <typename T> inline bool operator==(const T& t, const VectorProxy<T>& r) { + return t == static_cast<T>(r); + } + + template <typename T> inline bool operator!=(const VectorProxy<T>& l, const VectorProxy<T>& r) { + return static_cast<T>(l) != static_cast<T>(r); + } + + template <typename T> inline bool operator!=(const VectorProxy<T>& l, const T& t) { + return static_cast<T>(l) != t; + } + + template <typename T> inline bool operator!=(const T& t, const VectorProxy<T>& r) { + return t != static_cast<T>(r); + } + + template <typename T> inline bool operator<(const VectorProxy<T>& l, const VectorProxy<T>& r) { + return static_cast<T>(l) < static_cast<T>(r); + } + + template <typename T> inline bool operator<(const VectorProxy<T>& l, const T& t) { + return static_cast<T>(l) < t; + } + + template <typename T> inline bool operator<(const T& t, const VectorProxy<T>& r) { + return t < static_cast<T>(r); + } + + template <typename T> inline bool operator<=(const VectorProxy<T>& l, const VectorProxy<T>& r) { + return static_cast<T>(l) <= static_cast<T>(r); + } + + template <typename T> inline bool operator<=(const VectorProxy<T>& l, const T& t) { + return static_cast<T>(l) <= t; + } + + template <typename T> inline bool operator<=(const T& t, const VectorProxy<T>& r) { + return t <= static_cast<T>(r); + } + + template <typename T> inline bool operator>(const VectorProxy<T>& l, const VectorProxy<T>& r) { + return static_cast<T>(l) > static_cast<T>(r); + } + + template <typename T> inline bool operator>(const VectorProxy<T>& l, const T& t) { + return static_cast<T>(l) > t; + } + + template <typename T> inline bool operator>(const T& t, const VectorProxy<T>& r) { + return t > static_cast<T>(r); + } + + template <typename T> inline bool operator>=(const VectorProxy<T>& l, const VectorProxy<T>& r) { + return static_cast<T>(l) >= static_cast<T>(r); + } + + template <typename T> inline bool operator>=(const VectorProxy<T>& l, const T& t) { + return static_cast<T>(l) >= t; + } + + template <typename T> inline bool operator>=(const T& t, const VectorProxy<T>& r) { + return t >= static_cast<T>(r); + } + + template <typename T> class ArrowProxy { + public: + explicit ArrowProxy(T t) + : m_val(t) { } + + const T * operator->() const { + return &m_val; + } + + private: + T m_val; + }; + } // namespace Details + + template <typename T> class VectorIterator { + public: + typedef ::std::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef Details::VectorProxy<T> * pointer; + typedef Details::VectorProxy<T> reference; + + VectorIterator() + : m_v(nullptr), m_i(0) { } + + explicit VectorIterator(Details::WFC::IVector<T>^ v) + : m_v(v), m_i(0) { } + + reference operator*() const { + return reference(m_v, m_i); + } + + Details::ArrowProxy<T> operator->() const { + return Details::ArrowProxy<T>(m_v->GetAt(static_cast<unsigned int>(m_i))); + } + + reference operator[](difference_type n) const { + return reference(m_v, m_i + n); + } + + VectorIterator& operator++() { + ++m_i; + return *this; + } + + VectorIterator& operator--() { + --m_i; + return *this; + } + + VectorIterator operator++(int) { + VectorIterator old(*this); + ++*this; + return old; + } + + VectorIterator operator--(int) { + VectorIterator old(*this); + --*this; + return old; + } + + VectorIterator& operator+=(difference_type n) { + m_i += n; + return *this; + } + + VectorIterator& operator-=(difference_type n) { + m_i -= n; + return *this; + } + + VectorIterator operator+(difference_type n) const { + VectorIterator ret(*this); + ret += n; + return ret; + } + + VectorIterator operator-(difference_type n) const { + VectorIterator ret(*this); + ret -= n; + return ret; + } + + difference_type operator-(const VectorIterator& other) const { + return m_i - other.m_i; + } + + bool operator==(const VectorIterator& other) const { + return m_i == other.m_i; + } + + bool operator!=(const VectorIterator& other) const { + return m_i != other.m_i; + } + + bool operator<(const VectorIterator& other) const { + return m_i < other.m_i; + } + + bool operator>(const VectorIterator& other) const { + return m_i > other.m_i; + } + + bool operator<=(const VectorIterator& other) const { + return m_i <= other.m_i; + } + + bool operator>=(const VectorIterator& other) const { + return m_i >= other.m_i; + } + + private: + Details::WFC::IVector<T>^ m_v; + difference_type m_i; + }; + + template <typename T> inline VectorIterator<T> operator+(ptrdiff_t n, const VectorIterator<T>& i) { + return i + n; + } + + template <typename T> class VectorViewIterator { + public: + typedef ::std::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef T * pointer; + typedef T reference; + + VectorViewIterator() + : m_v(nullptr), m_i(0) { } + + explicit VectorViewIterator(Details::WFC::IVectorView<T>^ v) + : m_v(v), m_i(0) { } + + reference operator*() const { + return m_v->GetAt(static_cast<unsigned int>(m_i)); + } + + Details::ArrowProxy<T> operator->() const { + return Details::ArrowProxy<T>(m_v->GetAt(static_cast<unsigned int>(m_i))); + } + + reference operator[](difference_type n) const { + return m_v->GetAt(static_cast<unsigned int>(m_i + n)); + } + + VectorViewIterator& operator++() { + ++m_i; + return *this; + } + + VectorViewIterator& operator--() { + --m_i; + return *this; + } + + VectorViewIterator operator++(int) { + VectorViewIterator old(*this); + ++*this; + return old; + } + + VectorViewIterator operator--(int) { + VectorViewIterator old(*this); + --*this; + return old; + } + + VectorViewIterator& operator+=(difference_type n) { + m_i += n; + return *this; + } + + VectorViewIterator& operator-=(difference_type n) { + m_i -= n; + return *this; + } + + VectorViewIterator operator+(difference_type n) const { + VectorViewIterator ret(*this); + ret += n; + return ret; + } + + VectorViewIterator operator-(difference_type n) const { + VectorViewIterator ret(*this); + ret -= n; + return ret; + } + + difference_type operator-(const VectorViewIterator& other) const { + return m_i - other.m_i; + } + + bool operator==(const VectorViewIterator& other) const { + return m_i == other.m_i; + } + + bool operator!=(const VectorViewIterator& other) const { + return m_i != other.m_i; + } + + bool operator<(const VectorViewIterator& other) const { + return m_i < other.m_i; + } + + bool operator>(const VectorViewIterator& other) const { + return m_i > other.m_i; + } + + bool operator<=(const VectorViewIterator& other) const { + return m_i <= other.m_i; + } + + bool operator>=(const VectorViewIterator& other) const { + return m_i >= other.m_i; + } + + private: + Details::WFC::IVectorView<T>^ m_v; + difference_type m_i; + }; + + template <typename T> inline VectorViewIterator<T> operator+(ptrdiff_t n, const VectorViewIterator<T>& i) { + return i + n; + } + + template <typename T> class BackInsertIterator + : public ::std::iterator< ::std::output_iterator_tag, void, void, void, void> { + public: + explicit BackInsertIterator(Details::WFC::IVector<T>^ v) : m_v(v) { } + + BackInsertIterator& operator=(const T& t) { + m_v->Append(t); + return *this; + } + + BackInsertIterator& operator*() { + return *this; + } + + BackInsertIterator& operator++() { + return *this; + } + + BackInsertIterator operator++(int) { + return *this; + } + + private: + Details::WFC::IVector<T>^ m_v; + }; + + namespace Details { + template <typename T, typename I> inline ::std::vector<T> ToVector(I^ v) { + unsigned int size = v->Size; + + ::std::vector<T> ret(size); + + for (unsigned int actual = 0; actual < size; ) { + Array<T>^ arr = ref new Array<T>(size - actual); + + unsigned int n = v->GetMany(actual, arr); + + if (n == 0) { + throw ref new FailureException; + } + + ::std::copy_n(arr->begin(), n, ret.begin() + actual); + + actual += n; + } + + return ret; + } + } // namespace Details + } // namespace Collections +} // namespace Platform + +namespace Windows { + namespace Foundation { + namespace Collections { + template <typename X> inline ::Platform::Collections::InputIterator<X> begin(IIterable<X>^ i) { + return ::Platform::Collections::InputIterator<X>(i->First()); + } + + template <typename X> inline ::Platform::Collections::InputIterator<X> end(IIterable<X>^) { + return ::Platform::Collections::InputIterator<X>(); + } + + template <typename T> inline ::Platform::Collections::VectorIterator<T> begin(IVector<T>^ v) { + return ::Platform::Collections::VectorIterator<T>(v); + } + + template <typename T> inline ::Platform::Collections::VectorIterator<T> end(IVector<T>^ v) { + return ::Platform::Collections::VectorIterator<T>(v) + v->Size; + } + + template <typename T> inline ::Platform::Collections::VectorViewIterator<T> begin(IVectorView<T>^ v) { + return ::Platform::Collections::VectorViewIterator<T>(v); + } + + template <typename T> inline ::Platform::Collections::VectorViewIterator<T> end(IVectorView<T>^ v) { + return ::Platform::Collections::VectorViewIterator<T>(v) + v->Size; + } + + template <typename T> inline ::std::vector<T> to_vector(IVector<T>^ v) { + return ::Platform::Collections::Details::ToVector<T>(v); + } + + template <typename T> inline ::std::vector<T> to_vector(IVectorView<T>^ v) { + return ::Platform::Collections::Details::ToVector<T>(v); + } + + template <typename T> inline ::Platform::Collections::BackInsertIterator<T> back_inserter(IVector<T>^ v) { + return ::Platform::Collections::BackInsertIterator<T>(v); + } + + template <typename T> inline ::Platform::Collections::BackInsertIterator<T> back_inserter(IObservableVector<T>^ v) { + return ::Platform::Collections::BackInsertIterator<T>(v); + } + } // namespace Collections + } // namespace Foundation +} // namespace Windows + +namespace Platform { + namespace Collections { + template <typename T, typename E> inline BackInsertIterator<T> back_inserter(Vector<T, E>^ v) { + return BackInsertIterator<T>(v); + } + } // namespace Collections +} // namespace Platform + +template <> struct ::std::hash< ::Platform::String^> + : public ::std::unary_function< ::Platform::String^, size_t> { + + size_t operator()(::Platform::String^ s) const { + return ::std::_Hash_seq(reinterpret_cast<const unsigned char *>(s->Data()), s->Length() * sizeof(wchar_t)); + } +}; + +#undef _COLLECTION_ATTRIBUTES +#undef _COLLECTION_TRANSLATE +#undef _COLLECTION_WUXI + +#pragma warning(pop) +#pragma pack(pop) + +#endif // RC_INVOKED + +#endif // _COLLECTION_H_ diff --git a/test_data/lots_of_files/colors.cpp b/test_data/lots_of_files/colors.cpp new file mode 100644 index 0000000..63273d1 --- /dev/null +++ b/test_data/lots_of_files/colors.cpp @@ -0,0 +1,26 @@ +/* + * 4tech color research + * by Allen Webster + * 20.08.2015 + * + */ + +// TOP + +#define FTECH_GL_RENDER 1 +#define FTECH_ALLOW_ASSERT 1 + +#include "meta.h" +#include "app_base.h" + +#include <Windows.h> +#include <GL/gl.h> + +#include "4tech_math.cpp" +#include "tree_gen.cpp" +#include "app_color.cpp" + +#include "win32_base.cpp" + +// BOTTOM + diff --git a/test_data/lots_of_files/comdef.h b/test_data/lots_of_files/comdef.h new file mode 100644 index 0000000..5126cd9 --- /dev/null +++ b/test_data/lots_of_files/comdef.h @@ -0,0 +1,501 @@ +/*** +* comdef.h - Native C++ compiler COM support - main definitions header +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#if !defined(_INC_COMDEF) +#define _INC_COMDEF +#if !defined(RC_INVOKED) + +#ifndef __cplusplus +#error Native Compiler support only available in C++ compiler +#endif + +#ifdef _M_CEE_PURE +#error comdef.h header cannot be included under /clr:safe or /clr:pure +#endif + +#ifdef WINAPI_FAMILY +#include <winapifamily.h> +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP +#endif /* !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ +#if defined WINAPI_FAMILY_PHONE_APP && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +#define _COMDEF_WINAPI_FAMILY_PHONE_APP +#endif /* defined WINAPI_FAMILY_PHONE_APP && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) */ +#endif /* WINAPI_FAMILY */ + +#ifndef _COMDEF_WINAPI_FAMILY_PHONE_APP +#include <ole2.h> + +#include <comutil.h> +#endif /* _COMDEF_WINAPI_FAMILY_PHONE_APP */ + +#pragma warning(push) +#pragma warning(disable: 4244) +#pragma warning(disable: 4290) + +#ifdef _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP + +#include <roerrorapi.h> +#include <new.h> +#include <wchar.h> + +inline void __stdcall _com_issue_error(HRESULT hr); + +class _com_error +{ +protected: + static wchar_t* AllocateString(const wchar_t* message) + { + wchar_t* value = nullptr; + if (message != nullptr) + { + auto length = ::wcslen(message) + 1; // add 1 for null terminator + value = new (std::nothrow) wchar_t[length]; + if (value == nullptr) + { + _com_issue_error(E_OUTOFMEMORY); + } + + ::wmemcpy(value, message, length); + } + + return value; + } +public: + _com_error(HRESULT hr, const wchar_t* message) : m_hr(hr), m_message(nullptr) + { + m_message = AllocateString(message); + } + + _com_error(const _com_error& other) + { + m_hr = other.m_hr; + m_message = AllocateString(other.m_message); + } + + _com_error(_com_error&& other) + { + m_hr = other.m_hr; + m_message = other.m_message; + other.m_message = nullptr; + } + + ~_com_error() throw() + { + delete [] m_message; + } + + _com_error& operator=(const _com_error& other) + { + if (this != &other) + { + m_hr = other.m_hr; + delete [] m_message; + m_message = AllocateString(other.m_message); + } + return *this; + } + + _com_error& operator=(_com_error&& other) + { + if (this != &other) + { + m_hr = other.m_hr; + delete [] m_message; + m_message = other.m_message; + other.m_message = nullptr; + } + return *this; + } + + HRESULT Error() const throw() + { + return m_hr; + } + + const wchar_t* ErrorMessage() const throw() + { + if (m_message == nullptr) + { + wchar_t buffer[4096]; + if (::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, + nullptr, + m_hr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + buffer, + _countof(buffer), + nullptr)) + { + m_message = AllocateString(buffer); + } + } + return m_message; + } +protected: + HRESULT m_hr; + mutable wchar_t* m_message; +}; + +inline void __declspec(noreturn) __stdcall _com_raise_error(HRESULT hr, const wchar_t* message) +{ + size_t length = (message == nullptr) ? 0 : ::wcslen(message); + + if (UINT_MAX < length) + { + length = 0; + } + + ::Windows::Foundation::Diagnostics::OriginateError(hr, static_cast<unsigned int>(length), message); + throw _com_error(hr, message); +} + +typedef void (__stdcall *__errorPfnType)(HRESULT hr, const wchar_t* message); + +// throw exceptions by default +__declspec(selectany) __errorPfnType __errorPfn = &_com_raise_error; + +inline void __stdcall _com_issue_errorex(HRESULT hr, const wchar_t* message) +{ + __errorPfn(hr, message); +} + +inline void __stdcall _com_issue_error(HRESULT hr) +{ + __errorPfn(hr, nullptr); +} + +inline void __stdcall _set_com_error_handler(void (__stdcall *pHandler)(HRESULT, const wchar_t*)) +{ + __errorPfn = pHandler; +} + +#else + +#include <olectl.h> + +#ifdef _NATIVE_WCHAR_T_DEFINED +# ifdef _DEBUG +# pragma comment(lib, "comsuppwd.lib") +# else +# pragma comment(lib, "comsuppw.lib") +# endif +#else +# ifdef _DEBUG +# pragma comment(lib, "comsuppd.lib") +# else +# pragma comment(lib, "comsupp.lib") +# endif +#endif + +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "oleaut32.lib") + +class _com_error; + +void __declspec(noreturn) __stdcall + _com_raise_error(HRESULT hr, IErrorInfo* perrinfo = 0) ; + +void __stdcall + _set_com_error_handler(void (__stdcall *pHandler)(HRESULT hr, IErrorInfo* perrinfo)); + +void __stdcall + _com_issue_error(HRESULT) ; +void __stdcall + _com_issue_errorex(HRESULT, IUnknown*, REFIID) ; + +HRESULT __stdcall + _com_dispatch_propget(IDispatch*, DISPID, VARTYPE, void*) ; +HRESULT __cdecl + _com_dispatch_propput(IDispatch*, DISPID, VARTYPE, ...) ; +HRESULT __cdecl + _com_dispatch_method(IDispatch*, DISPID, WORD, VARTYPE, void*, + const wchar_t*, ...) ; + +HRESULT __stdcall + _com_dispatch_raw_propget(IDispatch*, DISPID, VARTYPE, void*) throw(); +HRESULT __cdecl + _com_dispatch_raw_propput(IDispatch*, DISPID, VARTYPE, ...) throw(); +HRESULT __cdecl + _com_dispatch_raw_method(IDispatch*, DISPID, WORD, VARTYPE, void*, + const wchar_t*, ...) throw(); + +class _com_error { +public: + // Constructors + // + _com_error(HRESULT hr, + IErrorInfo* perrinfo = NULL, + bool fAddRef = false) throw(); + _com_error(const _com_error& that) throw(); + + // Destructor + // + virtual ~_com_error() throw(); + + // Assignment operator + // + _com_error& operator=(const _com_error& that) throw(); + + // Accessors + // + HRESULT Error() const throw(); + WORD WCode() const throw(); + IErrorInfo * ErrorInfo() const throw(); + + // IErrorInfo method accessors + // + _bstr_t Description() const ; + DWORD HelpContext() const throw(); + _bstr_t HelpFile() const ; + _bstr_t Source() const ; + GUID GUID() const throw(); + + // FormatMessage accessors + // + const TCHAR * ErrorMessage() const throw(); + + // EXCEPINFO.wCode <-> HRESULT mappers + // + static HRESULT WCodeToHRESULT(WORD wCode) throw(); + static WORD HRESULTToWCode(HRESULT hr) throw(); + +private: + enum { + WCODE_HRESULT_FIRST = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x200), + WCODE_HRESULT_LAST = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF+1, 0) - 1 + }; + const HRESULT m_hresult; + IErrorInfo * m_perrinfo; + mutable TCHAR * m_pszMsg; +}; + +inline _com_error::_com_error(HRESULT hr, + IErrorInfo* perrinfo, + bool fAddRef) throw() + : m_hresult(hr), m_perrinfo(perrinfo), m_pszMsg(NULL) +{ + if (m_perrinfo != NULL && fAddRef) { + m_perrinfo->AddRef(); + } +} + +inline _com_error::_com_error(const _com_error& that) throw() + : m_hresult(that.m_hresult), m_perrinfo(that.m_perrinfo), m_pszMsg(NULL) +{ + if (m_perrinfo != NULL) { + m_perrinfo->AddRef(); + } +} + +inline _com_error::~_com_error() throw() +{ + if (m_perrinfo != NULL) { + m_perrinfo->Release(); + } + if (m_pszMsg != NULL) { + LocalFree((HLOCAL)m_pszMsg); + } +} + +inline _com_error& _com_error::operator=(const _com_error& that) throw() +{ + if (this != &that) { + this->_com_error::~_com_error(); + this->_com_error::_com_error(that); + } + return *this; +} + +inline HRESULT _com_error::Error() const throw() +{ + return m_hresult; +} + +inline WORD _com_error::WCode() const throw() +{ + return HRESULTToWCode(m_hresult); +} + +inline IErrorInfo * _com_error::ErrorInfo() const throw() +{ + if (m_perrinfo != NULL) { + m_perrinfo->AddRef(); + } + return m_perrinfo; +} + +inline _bstr_t _com_error::Description() const +{ + BSTR bstr = NULL; + if (m_perrinfo != NULL) { + if (FAILED(m_perrinfo->GetDescription(&bstr))) { + bstr = NULL; + } + } + return _bstr_t(bstr, false); +} + +inline DWORD _com_error::HelpContext() const throw() +{ + DWORD dwHelpContext = 0; + if (m_perrinfo != NULL) { + if (FAILED(m_perrinfo->GetHelpContext(&dwHelpContext))) { + dwHelpContext = 0; + } + } + return dwHelpContext; +} + +inline _bstr_t _com_error::HelpFile() const +{ + BSTR bstr = NULL; + if (m_perrinfo != NULL) { + if (FAILED(m_perrinfo->GetHelpFile(&bstr))) { + bstr = NULL; + } + } + return _bstr_t(bstr, false); +} + +inline _bstr_t _com_error::Source() const +{ + BSTR bstr = NULL; + if (m_perrinfo != NULL) { + if (FAILED(m_perrinfo->GetSource(&bstr))) { + bstr = NULL; + } + } + return _bstr_t(bstr, false); +} + +inline _GUID _com_error::GUID() const throw() +{ + _GUID guid; + _COM_MEMCPY_S(&guid, sizeof(_GUID), &__uuidof(NULL), sizeof(_GUID)); + if (m_perrinfo != NULL) { + if (FAILED(m_perrinfo->GetGUID(&guid))) { + _COM_MEMCPY_S(&guid, sizeof(_GUID), &__uuidof(NULL), sizeof(_GUID)); + } + } + return guid; +} + +inline const TCHAR * _com_error::ErrorMessage() const throw() +{ + if (m_pszMsg == NULL) { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER| + FORMAT_MESSAGE_FROM_SYSTEM| + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + m_hresult, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&m_pszMsg, + 0, + NULL); + if (m_pszMsg != NULL) { + int nLen = lstrlen(m_pszMsg); + if (nLen > 1 && m_pszMsg[nLen - 1] == '\n') { + m_pszMsg[nLen - 1] = 0; + if (m_pszMsg[nLen - 2] == '\r') { + m_pszMsg[nLen - 2] = 0; + } + } + } + else { + m_pszMsg = (LPTSTR)LocalAlloc(0, 32 * sizeof(TCHAR)); + if (m_pszMsg != NULL) { + WORD wCode = WCode(); + if (wCode != 0) { + _COM_PRINTF_S_1(m_pszMsg, 32, TEXT("IDispatch error #%d"), (int)wCode); + } + else { + _COM_PRINTF_S_1(m_pszMsg, 32, TEXT("Unknown error 0x%0lX"), m_hresult); + } + } + } + } + return m_pszMsg; +} + +inline HRESULT _com_error::WCodeToHRESULT(WORD wCode) throw() +{ + return wCode >= 0xFE00 ? WCODE_HRESULT_LAST : WCODE_HRESULT_FIRST + wCode; +} + +inline WORD _com_error::HRESULTToWCode(HRESULT hr) throw() +{ + return (hr >= WCODE_HRESULT_FIRST && hr <= WCODE_HRESULT_LAST) + ? WORD(hr - WCODE_HRESULT_FIRST) + : 0; +} + +// +// give missing types from dependent type libraries a chance +// +typedef int __missing_type__; + +#if !defined(_COM_SMARTPTR) + #if !defined(_INC_COMIP) + #include <comip.h> + #endif + #define _COM_SMARTPTR _com_ptr_t + #define _COM_SMARTPTR_LEVEL2 _com_IIID +#endif +#if defined(_COM_SMARTPTR) + #if !defined(_COM_SMARTPTR_TYPEDEF) + #if defined(_COM_SMARTPTR_LEVEL2) + #define _COM_SMARTPTR_TYPEDEF(Interface, IID) \ + typedef _COM_SMARTPTR<_COM_SMARTPTR_LEVEL2<Interface, &IID> > \ + Interface ## Ptr + #else + #define _COM_SMARTPTR_TYPEDEF(Interface, IID) \ + typedef _COM_SMARTPTR<Interface, &IID> \ + Interface ## Ptr + #endif + #endif +#endif + +#if !defined(_COM_NO_STANDARD_GUIDS_) + +// hard-coded smart pointer defs +#if defined(__IFontDisp_INTERFACE_DEFINED__) +__if_not_exists(Font) +{ + struct Font : IFontDisp {}; +} +_COM_SMARTPTR_TYPEDEF(Font, __uuidof(IDispatch)); +#endif +#if defined(__IFontEventsDisp_INTERFACE_DEFINED__) +__if_not_exists(FontEvents) +{ + struct FontEvents : IFontEventsDisp {}; +} +_COM_SMARTPTR_TYPEDEF(FontEvents, __uuidof(IDispatch)); +#endif +#if defined(__IPictureDisp_INTERFACE_DEFINED__) +__if_not_exists(Picture) +{ + struct Picture : IPictureDisp {}; +} +_COM_SMARTPTR_TYPEDEF(Picture, __uuidof(IDispatch)); +#endif + +#include "comdefsp.h" + +#endif /* _COM_NO_STANDARD_GUIDS_ */ + +#endif /* _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP */ + +#pragma warning(pop) + +#endif /* RC_INVOKED */ +#endif /* _INC_COMDEF */ diff --git a/test_data/lots_of_files/comdefsp.h b/test_data/lots_of_files/comdefsp.h new file mode 100644 index 0000000..d717cd5 --- /dev/null +++ b/test_data/lots_of_files/comdefsp.h @@ -0,0 +1,1323 @@ +/*** +* comdefsp.h - Native C++ compiler COM support - smart pointer support header +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#if !defined(_INC_COMDEFSP) +#define _INC_COMDEFSP +#if !defined(RC_INVOKED) + +#ifndef __cplusplus +#error Native Compiler support only available in C++ compiler +#endif + +#ifdef _M_CEE_PURE +#error comdefsp.h header cannot be included under /clr:safe or clr:pure +#endif + +#ifndef _COM_SMARTPTR_TYPEDEF +#error comdefsp.h requires comdef.h to be included first +#endif + +// interface smart pointer defs +#if defined(__AsyncIAdviseSink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(AsyncIAdviseSink, __uuidof(AsyncIAdviseSink)); +#endif // #if defined(__AsyncIAdviseSink_INTERFACE_DEFINED__) +#if defined(__AsyncIAdviseSink2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(AsyncIAdviseSink2, __uuidof(AsyncIAdviseSink2)); +#endif // #if defined(__AsyncIAdviseSink2_INTERFACE_DEFINED__) +#if defined(__AsyncIMultiQI_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(AsyncIMultiQI, __uuidof(AsyncIMultiQI)); +#endif // #if defined(__AsyncIMultiQI_INTERFACE_DEFINED__) +#if defined(__AsyncIPipeByte_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(AsyncIPipeByte, __uuidof(AsyncIPipeByte)); +#endif // #if defined(__AsyncIPipeByte_INTERFACE_DEFINED__) +#if defined(__AsyncIPipeDouble_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(AsyncIPipeDouble, __uuidof(AsyncIPipeDouble)); +#endif // #if defined(__AsyncIPipeDouble_INTERFACE_DEFINED__) +#if defined(__AsyncIPipeLong_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(AsyncIPipeLong, __uuidof(AsyncIPipeLong)); +#endif // #if defined(__AsyncIPipeLong_INTERFACE_DEFINED__) +#if defined(__AsyncIUnknown_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(AsyncIUnknown, __uuidof(AsyncIUnknown)); +#endif // #if defined(__AsyncIUnknown_INTERFACE_DEFINED__) +#if defined(__FolderItem_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(FolderItem, __uuidof(FolderItem)); +#endif // #if defined(__FolderItem_INTERFACE_DEFINED__) +#if defined(__FolderItemVerb_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(FolderItemVerb, __uuidof(FolderItemVerb)); +#endif // #if defined(__FolderItemVerb_INTERFACE_DEFINED__) +#if defined(__FolderItemVerbs_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(FolderItemVerbs, __uuidof(FolderItemVerbs)); +#endif // #if defined(__FolderItemVerbs_INTERFACE_DEFINED__) +#if defined(__FolderItems_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(FolderItems, __uuidof(FolderItems)); +#endif // #if defined(__FolderItems_INTERFACE_DEFINED__) +#if defined(__IAccessible_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAccessible, __uuidof(IAccessible)); +#endif // #if defined(__IAccessible_INTERFACE_DEFINED__) +#if defined(__IActiveScript_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScript, __uuidof(IActiveScript)); +#endif // #if defined(__IActiveScript_INTERFACE_DEFINED__) +#if defined(__IActiveScriptError_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptError, __uuidof(IActiveScriptError)); +#endif // #if defined(__IActiveScriptError_INTERFACE_DEFINED__) +#if defined(__IActiveScriptParse_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptParse, __uuidof(IActiveScriptParse)); +#endif // #if defined(__IActiveScriptParse_INTERFACE_DEFINED__) +#if defined(__IActiveScriptParseProcedure_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptParseProcedure, __uuidof(IActiveScriptParseProcedure)); +#endif // #if defined(__IActiveScriptParseProcedure_INTERFACE_DEFINED__) +#if defined(__IActiveScriptParseProcedureOld_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptParseProcedureOld, __uuidof(IActiveScriptParseProcedureOld)); +#endif // #if defined(__IActiveScriptParseProcedureOld_INTERFACE_DEFINED__) +#if defined(__IActiveScriptSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptSite, __uuidof(IActiveScriptSite)); +#endif // #if defined(__IActiveScriptSite_INTERFACE_DEFINED__) +#if defined(__IActiveScriptSiteInterruptPoll_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptSiteInterruptPoll, __uuidof(IActiveScriptSiteInterruptPoll)); +#endif // #if defined(__IActiveScriptSiteInterruptPoll_INTERFACE_DEFINED__) +#if defined(__IActiveScriptSiteWindow_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptSiteWindow, __uuidof(IActiveScriptSiteWindow)); +#endif // #if defined(__IActiveScriptSiteWindow_INTERFACE_DEFINED__) +#if defined(__IActiveScriptStats_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IActiveScriptStats, __uuidof(IActiveScriptStats)); +#endif // #if defined(__IActiveScriptStats_INTERFACE_DEFINED__) +#if defined(__IAddrExclusionControl_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAddrExclusionControl, __uuidof(IAddrExclusionControl)); +#endif // #if defined(__IAddrExclusionControl_INTERFACE_DEFINED__) +#if defined(__IAddrTrackingControl_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAddrTrackingControl, __uuidof(IAddrTrackingControl)); +#endif // #if defined(__IAddrTrackingControl_INTERFACE_DEFINED__) +#if defined(__IAdviseSink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAdviseSink, __uuidof(IAdviseSink)); +#endif // #if defined(__IAdviseSink_INTERFACE_DEFINED__) +#if defined(__IAdviseSink2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAdviseSink2, __uuidof(IAdviseSink2)); +#endif // #if defined(__IAdviseSink2_INTERFACE_DEFINED__) +#if defined(__IAdviseSinkEx_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAdviseSinkEx, __uuidof(IAdviseSinkEx)); +#endif // #if defined(__IAdviseSinkEx_INTERFACE_DEFINED__) +#if defined(__IAsyncManager_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAsyncManager, __uuidof(IAsyncManager)); +#endif // #if defined(__IAsyncManager_INTERFACE_DEFINED__) +#if defined(__IAsyncRpcChannelBuffer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAsyncRpcChannelBuffer, __uuidof(IAsyncRpcChannelBuffer)); +#endif // #if defined(__IAsyncRpcChannelBuffer_INTERFACE_DEFINED__) +#if defined(__IAuthenticate_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IAuthenticate, __uuidof(IAuthenticate)); +#endif // #if defined(__IAuthenticate_INTERFACE_DEFINED__) +#if defined(__IBindCtx_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IBindCtx, __uuidof(IBindCtx)); +#endif // #if defined(__IBindCtx_INTERFACE_DEFINED__) +#if defined(__IBindEventHandler_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IBindEventHandler, __uuidof(IBindEventHandler)); +#endif // #if defined(__IBindEventHandler_INTERFACE_DEFINED__) +#if defined(__IBindHost_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IBindHost, __uuidof(IBindHost)); +#endif // #if defined(__IBindHost_INTERFACE_DEFINED__) +#if defined(__IBindProtocol_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IBindProtocol, __uuidof(IBindProtocol)); +#endif // #if defined(__IBindProtocol_INTERFACE_DEFINED__) +#if defined(__IBindStatusCallback_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IBindStatusCallback, __uuidof(IBindStatusCallback)); +#endif // #if defined(__IBindStatusCallback_INTERFACE_DEFINED__) +#if defined(__IBinding_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IBinding, __uuidof(IBinding)); +#endif // #if defined(__IBinding_INTERFACE_DEFINED__) +#if defined(__IBlockingLock_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IBlockingLock, __uuidof(IBlockingLock)); +#endif // #if defined(__IBlockingLock_INTERFACE_DEFINED__) +#if defined(__ICSSFilter_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICSSFilter, __uuidof(ICSSFilter)); +#endif // #if defined(__ICSSFilter_INTERFACE_DEFINED__) +#if defined(__ICSSFilterSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICSSFilterSite, __uuidof(ICSSFilterSite)); +#endif // #if defined(__ICSSFilterSite_INTERFACE_DEFINED__) +#if defined(__ICallFactory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICallFactory, __uuidof(ICallFactory)); +#endif // #if defined(__ICallFactory_INTERFACE_DEFINED__) +#if defined(__ICancelMethodCalls_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICancelMethodCalls, __uuidof(ICancelMethodCalls)); +#endif // #if defined(__ICancelMethodCalls_INTERFACE_DEFINED__) +#if defined(__ICatInformation_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICatInformation, __uuidof(ICatInformation)); +#endif // #if defined(__ICatInformation_INTERFACE_DEFINED__) +#if defined(__ICatRegister_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICatRegister, __uuidof(ICatRegister)); +#endif // #if defined(__ICatRegister_INTERFACE_DEFINED__) +#if defined(__ICatalogFileInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICatalogFileInfo, __uuidof(ICatalogFileInfo)); +#endif // #if defined(__ICatalogFileInfo_INTERFACE_DEFINED__) +#if defined(__IChannelHook_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IChannelHook, __uuidof(IChannelHook)); +#endif // #if defined(__IChannelHook_INTERFACE_DEFINED__) +#if defined(__IChannelMgr_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IChannelMgr, __uuidof(IChannelMgr)); +#endif // #if defined(__IChannelMgr_INTERFACE_DEFINED__) +#if defined(__IClassActivator_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IClassActivator, __uuidof(IClassActivator)); +#endif // #if defined(__IClassActivator_INTERFACE_DEFINED__) +#if defined(__IClassFactory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IClassFactory, __uuidof(IClassFactory)); +#endif // #if defined(__IClassFactory_INTERFACE_DEFINED__) +#if defined(__IClassFactory2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IClassFactory2, __uuidof(IClassFactory2)); +#endif // #if defined(__IClassFactory2_INTERFACE_DEFINED__) +#if defined(__IClientSecurity_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IClientSecurity, __uuidof(IClientSecurity)); +#endif // #if defined(__IClientSecurity_INTERFACE_DEFINED__) +#if defined(__ICodeInstall_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICodeInstall, __uuidof(ICodeInstall)); +#endif // #if defined(__ICodeInstall_INTERFACE_DEFINED__) +#if defined(__IConnectionPoint_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IConnectionPoint, __uuidof(IConnectionPoint)); +#endif // #if defined(__IConnectionPoint_INTERFACE_DEFINED__) +#if defined(__IConnectionPointContainer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IConnectionPointContainer, __uuidof(IConnectionPointContainer)); +#endif // #if defined(__IConnectionPointContainer_INTERFACE_DEFINED__) +#if defined(__IContinue_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IContinue, __uuidof(IContinue)); +#endif // #if defined(__IContinue_INTERFACE_DEFINED__) +#if defined(__IContinueCallback_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IContinueCallback, __uuidof(IContinueCallback)); +#endif // #if defined(__IContinueCallback_INTERFACE_DEFINED__) +#if defined(__ICreateErrorInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICreateErrorInfo, __uuidof(ICreateErrorInfo)); +#endif // #if defined(__ICreateErrorInfo_INTERFACE_DEFINED__) +#if defined(__ICreateTypeInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICreateTypeInfo, __uuidof(ICreateTypeInfo)); +#endif // #if defined(__ICreateTypeInfo_INTERFACE_DEFINED__) +#if defined(__ICreateTypeInfo2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICreateTypeInfo2, __uuidof(ICreateTypeInfo2)); +#endif // #if defined(__ICreateTypeInfo2_INTERFACE_DEFINED__) +#if defined(__ICreateTypeLib_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICreateTypeLib, __uuidof(ICreateTypeLib)); +#endif // #if defined(__ICreateTypeLib_INTERFACE_DEFINED__) +#if defined(__ICreateTypeLib2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICreateTypeLib2, __uuidof(ICreateTypeLib2)); +#endif // #if defined(__ICreateTypeLib2_INTERFACE_DEFINED__) +#if defined(__ICustomDoc_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ICustomDoc, __uuidof(ICustomDoc)); +#endif // #if defined(__ICustomDoc_INTERFACE_DEFINED__) +#if defined(__IDataAdviseHolder_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDataAdviseHolder, __uuidof(IDataAdviseHolder)); +#endif // #if defined(__IDataAdviseHolder_INTERFACE_DEFINED__) +#if defined(__IDataFilter_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDataFilter, __uuidof(IDataFilter)); +#endif // #if defined(__IDataFilter_INTERFACE_DEFINED__) +#if defined(__IDataObject_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDataObject, __uuidof(IDataObject)); +#endif // #if defined(__IDataObject_INTERFACE_DEFINED__) +#if defined(__IDeskBand_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDeskBand, __uuidof(IDeskBand)); +#endif // #if defined(__IDeskBand_INTERFACE_DEFINED__) +#if defined(__IDirectWriterLock_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDirectWriterLock, __uuidof(IDirectWriterLock)); +#endif // #if defined(__IDirectWriterLock_INTERFACE_DEFINED__) +#if defined(__IDispError_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDispError, __uuidof(IDispError)); +#endif // #if defined(__IDispError_INTERFACE_DEFINED__) +#if defined(__IDispatch_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDispatch, __uuidof(IDispatch)); +#endif // #if defined(__IDispatch_INTERFACE_DEFINED__) +#if defined(__IDispatchEx_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDispatchEx, __uuidof(IDispatchEx)); +#endif // #if defined(__IDispatchEx_INTERFACE_DEFINED__) +#if defined(__IDocHostShowUI_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDocHostShowUI, __uuidof(IDocHostShowUI)); +#endif // #if defined(__IDocHostShowUI_INTERFACE_DEFINED__) +#if defined(__IDocHostUIHandler_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDocHostUIHandler, __uuidof(IDocHostUIHandler)); +#endif // #if defined(__IDocHostUIHandler_INTERFACE_DEFINED__) +#if defined(__IDockingWindow_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDockingWindow, __uuidof(IDockingWindow)); +#endif // #if defined(__IDockingWindow_INTERFACE_DEFINED__) +#if defined(__IDropSource_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDropSource, __uuidof(IDropSource)); +#endif // #if defined(__IDropSource_INTERFACE_DEFINED__) +#if defined(__IDropTarget_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDropTarget, __uuidof(IDropTarget)); +#endif // #if defined(__IDropTarget_INTERFACE_DEFINED__) +#if defined(__IDummyHICONIncluder_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IDummyHICONIncluder, __uuidof(IDummyHICONIncluder)); +#endif // #if defined(__IDummyHICONIncluder_INTERFACE_DEFINED__) +#if defined(__IEncodingFilterFactory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEncodingFilterFactory, __uuidof(IEncodingFilterFactory)); +#endif // #if defined(__IEncodingFilterFactory_INTERFACE_DEFINED__) +#if defined(__IEnumCATEGORYINFO_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumCATEGORYINFO, __uuidof(IEnumCATEGORYINFO)); +#endif // #if defined(__IEnumCATEGORYINFO_INTERFACE_DEFINED__) +#if defined(__IEnumChannels_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumChannels, __uuidof(IEnumChannels)); +#endif // #if defined(__IEnumChannels_INTERFACE_DEFINED__) +#if defined(__IEnumCodePage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumCodePage, __uuidof(IEnumCodePage)); +#endif // #if defined(__IEnumCodePage_INTERFACE_DEFINED__) +#if defined(__IEnumConnectionPoints_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumConnectionPoints, __uuidof(IEnumConnectionPoints)); +#endif // #if defined(__IEnumConnectionPoints_INTERFACE_DEFINED__) +#if defined(__IEnumConnections_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumConnections, __uuidof(IEnumConnections)); +#endif // #if defined(__IEnumConnections_INTERFACE_DEFINED__) +#if defined(__IEnumFORMATETC_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumFORMATETC, __uuidof(IEnumFORMATETC)); +#endif // #if defined(__IEnumFORMATETC_INTERFACE_DEFINED__) +#if defined(__IEnumGUID_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumGUID, __uuidof(IEnumGUID)); +#endif // #if defined(__IEnumGUID_INTERFACE_DEFINED__) +#if defined(__IEnumHLITEM_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumHLITEM, __uuidof(IEnumHLITEM)); +#endif // #if defined(__IEnumHLITEM_INTERFACE_DEFINED__) +#if defined(__IEnumIDList_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumIDList, __uuidof(IEnumIDList)); +#endif // #if defined(__IEnumIDList_INTERFACE_DEFINED__) +#if defined(__IEnumMoniker_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumMoniker, __uuidof(IEnumMoniker)); +#endif // #if defined(__IEnumMoniker_INTERFACE_DEFINED__) +#if defined(__IEnumOLEVERB_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumOLEVERB, __uuidof(IEnumOLEVERB)); +#endif // #if defined(__IEnumOLEVERB_INTERFACE_DEFINED__) +#if defined(__IEnumOleDocumentViews_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumOleDocumentViews, __uuidof(IEnumOleDocumentViews)); +#endif // #if defined(__IEnumOleDocumentViews_INTERFACE_DEFINED__) +#if defined(__IEnumOleUndoUnits_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumOleUndoUnits, __uuidof(IEnumOleUndoUnits)); +#endif // #if defined(__IEnumOleUndoUnits_INTERFACE_DEFINED__) +#if defined(__IEnumRfc1766_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumRfc1766, __uuidof(IEnumRfc1766)); +#endif // #if defined(__IEnumRfc1766_INTERFACE_DEFINED__) +#if defined(__IEnumSTATDATA_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumSTATDATA, __uuidof(IEnumSTATDATA)); +#endif // #if defined(__IEnumSTATDATA_INTERFACE_DEFINED__) +#if defined(__IEnumSTATPROPSETSTG_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumSTATPROPSETSTG, __uuidof(IEnumSTATPROPSETSTG)); +#endif // #if defined(__IEnumSTATPROPSETSTG_INTERFACE_DEFINED__) +#if defined(__IEnumSTATPROPSTG_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumSTATPROPSTG, __uuidof(IEnumSTATPROPSTG)); +#endif // #if defined(__IEnumSTATPROPSTG_INTERFACE_DEFINED__) +#if defined(__IEnumSTATSTG_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumSTATSTG, __uuidof(IEnumSTATSTG)); +#endif // #if defined(__IEnumSTATSTG_INTERFACE_DEFINED__) +#if defined(__IEnumSTATURL_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumSTATURL, __uuidof(IEnumSTATURL)); +#endif // #if defined(__IEnumSTATURL_INTERFACE_DEFINED__) +#if defined(__IEnumString_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumString, __uuidof(IEnumString)); +#endif // #if defined(__IEnumString_INTERFACE_DEFINED__) +#if defined(__IEnumUnknown_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumUnknown, __uuidof(IEnumUnknown)); +#endif // #if defined(__IEnumUnknown_INTERFACE_DEFINED__) +#if defined(__IEnumVARIANT_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IEnumVARIANT, __uuidof(IEnumVARIANT)); +#endif // #if defined(__IEnumVARIANT_INTERFACE_DEFINED__) +#if defined(__IErrorInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IErrorInfo, __uuidof(IErrorInfo)); +#endif // #if defined(__IErrorInfo_INTERFACE_DEFINED__) +#if defined(__IErrorLog_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IErrorLog, __uuidof(IErrorLog)); +#endif // #if defined(__IErrorLog_INTERFACE_DEFINED__) +#if defined(__IExtensionServices_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IExtensionServices, __uuidof(IExtensionServices)); +#endif // #if defined(__IExtensionServices_INTERFACE_DEFINED__) +#if defined(__IExternalConnection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IExternalConnection, __uuidof(IExternalConnection)); +#endif // #if defined(__IExternalConnection_INTERFACE_DEFINED__) +#if defined(__IFillLockBytes_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IFillLockBytes, __uuidof(IFillLockBytes)); +#endif // #if defined(__IFillLockBytes_INTERFACE_DEFINED__) +#if defined(__IFilter_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IFilter, __uuidof(IFilter)); +#endif // #if defined(__IFilter_INTERFACE_DEFINED__) +#if defined(__IFolderViewOC_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IFolderViewOC, __uuidof(IFolderViewOC)); +#endif // #if defined(__IFolderViewOC_INTERFACE_DEFINED__) +#if defined(__IFont_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IFont, __uuidof(IFont)); +#endif // #if defined(__IFont_INTERFACE_DEFINED__) +#if defined(__IFontDisp_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IFontDisp, __uuidof(IFontDisp)); +#endif // #if defined(__IFontDisp_INTERFACE_DEFINED__) +#if defined(__IFontEventsDisp_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IFontEventsDisp, __uuidof(IFontEventsDisp)); +#endif // #if defined(__IFontEventsDisp_INTERFACE_DEFINED__) +#if defined(__IForegroundTransfer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IForegroundTransfer, __uuidof(IForegroundTransfer)); +#endif // #if defined(__IForegroundTransfer_INTERFACE_DEFINED__) +#if defined(__IGlobalInterfaceTable_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IGlobalInterfaceTable, __uuidof(IGlobalInterfaceTable)); +#endif // #if defined(__IGlobalInterfaceTable_INTERFACE_DEFINED__) +#if defined(__IHTMLAnchorElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLAnchorElement, __uuidof(IHTMLAnchorElement)); +#endif // #if defined(__IHTMLAnchorElement_INTERFACE_DEFINED__) +#if defined(__IHTMLAreaElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLAreaElement, __uuidof(IHTMLAreaElement)); +#endif // #if defined(__IHTMLAreaElement_INTERFACE_DEFINED__) +#if defined(__IHTMLAreasCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLAreasCollection, __uuidof(IHTMLAreasCollection)); +#endif // #if defined(__IHTMLAreasCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLBGsound_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLBGsound, __uuidof(IHTMLBGsound)); +#endif // #if defined(__IHTMLBGsound_INTERFACE_DEFINED__) +#if defined(__IHTMLBRElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLBRElement, __uuidof(IHTMLBRElement)); +#endif // #if defined(__IHTMLBRElement_INTERFACE_DEFINED__) +#if defined(__IHTMLBaseElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLBaseElement, __uuidof(IHTMLBaseElement)); +#endif // #if defined(__IHTMLBaseElement_INTERFACE_DEFINED__) +#if defined(__IHTMLBaseFontElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLBaseFontElement, __uuidof(IHTMLBaseFontElement)); +#endif // #if defined(__IHTMLBaseFontElement_INTERFACE_DEFINED__) +#if defined(__IHTMLBlockElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLBlockElement, __uuidof(IHTMLBlockElement)); +#endif // #if defined(__IHTMLBlockElement_INTERFACE_DEFINED__) +#if defined(__IHTMLBodyElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLBodyElement, __uuidof(IHTMLBodyElement)); +#endif // #if defined(__IHTMLBodyElement_INTERFACE_DEFINED__) +#if defined(__IHTMLButtonElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLButtonElement, __uuidof(IHTMLButtonElement)); +#endif // #if defined(__IHTMLButtonElement_INTERFACE_DEFINED__) +#if defined(__IHTMLCommentElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLCommentElement, __uuidof(IHTMLCommentElement)); +#endif // #if defined(__IHTMLCommentElement_INTERFACE_DEFINED__) +#if defined(__IHTMLControlElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLControlElement, __uuidof(IHTMLControlElement)); +#endif // #if defined(__IHTMLControlElement_INTERFACE_DEFINED__) +#if defined(__IHTMLControlRange_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLControlRange, __uuidof(IHTMLControlRange)); +#endif // #if defined(__IHTMLControlRange_INTERFACE_DEFINED__) +#if defined(__IHTMLDDElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDDElement, __uuidof(IHTMLDDElement)); +#endif // #if defined(__IHTMLDDElement_INTERFACE_DEFINED__) +#if defined(__IHTMLDListElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDListElement, __uuidof(IHTMLDListElement)); +#endif // #if defined(__IHTMLDListElement_INTERFACE_DEFINED__) +#if defined(__IHTMLDTElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDTElement, __uuidof(IHTMLDTElement)); +#endif // #if defined(__IHTMLDTElement_INTERFACE_DEFINED__) +#if defined(__IHTMLDatabinding_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDatabinding, __uuidof(IHTMLDatabinding)); +#endif // #if defined(__IHTMLDatabinding_INTERFACE_DEFINED__) +#if defined(__IHTMLDialog_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDialog, __uuidof(IHTMLDialog)); +#endif // #if defined(__IHTMLDialog_INTERFACE_DEFINED__) +#if defined(__IHTMLDivElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDivElement, __uuidof(IHTMLDivElement)); +#endif // #if defined(__IHTMLDivElement_INTERFACE_DEFINED__) +#if defined(__IHTMLDivPosition_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDivPosition, __uuidof(IHTMLDivPosition)); +#endif // #if defined(__IHTMLDivPosition_INTERFACE_DEFINED__) +#if defined(__IHTMLDocument_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDocument, __uuidof(IHTMLDocument)); +#endif // #if defined(__IHTMLDocument_INTERFACE_DEFINED__) +#if defined(__IHTMLDocument2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLDocument2, __uuidof(IHTMLDocument2)); +#endif // #if defined(__IHTMLDocument2_INTERFACE_DEFINED__) +#if defined(__IHTMLElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLElement, __uuidof(IHTMLElement)); +#endif // #if defined(__IHTMLElement_INTERFACE_DEFINED__) +#if defined(__IHTMLElementCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLElementCollection, __uuidof(IHTMLElementCollection)); +#endif // #if defined(__IHTMLElementCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLEmbedElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLEmbedElement, __uuidof(IHTMLEmbedElement)); +#endif // #if defined(__IHTMLEmbedElement_INTERFACE_DEFINED__) +#if defined(__IHTMLEventObj_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLEventObj, __uuidof(IHTMLEventObj)); +#endif // #if defined(__IHTMLEventObj_INTERFACE_DEFINED__) +#if defined(__IHTMLFieldSetElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFieldSetElement, __uuidof(IHTMLFieldSetElement)); +#endif // #if defined(__IHTMLFieldSetElement_INTERFACE_DEFINED__) +#if defined(__IHTMLFiltersCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFiltersCollection, __uuidof(IHTMLFiltersCollection)); +#endif // #if defined(__IHTMLFiltersCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLFontElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFontElement, __uuidof(IHTMLFontElement)); +#endif // #if defined(__IHTMLFontElement_INTERFACE_DEFINED__) +#if defined(__IHTMLFontNamesCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFontNamesCollection, __uuidof(IHTMLFontNamesCollection)); +#endif // #if defined(__IHTMLFontNamesCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLFontSizesCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFontSizesCollection, __uuidof(IHTMLFontSizesCollection)); +#endif // #if defined(__IHTMLFontSizesCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLFormElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFormElement, __uuidof(IHTMLFormElement)); +#endif // #if defined(__IHTMLFormElement_INTERFACE_DEFINED__) +#if defined(__IHTMLFrameBase_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFrameBase, __uuidof(IHTMLFrameBase)); +#endif // #if defined(__IHTMLFrameBase_INTERFACE_DEFINED__) +#if defined(__IHTMLFrameElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFrameElement, __uuidof(IHTMLFrameElement)); +#endif // #if defined(__IHTMLFrameElement_INTERFACE_DEFINED__) +#if defined(__IHTMLFrameSetElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFrameSetElement, __uuidof(IHTMLFrameSetElement)); +#endif // #if defined(__IHTMLFrameSetElement_INTERFACE_DEFINED__) +#if defined(__IHTMLFramesCollection2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLFramesCollection2, __uuidof(IHTMLFramesCollection2)); +#endif // #if defined(__IHTMLFramesCollection2_INTERFACE_DEFINED__) +#if defined(__IHTMLHRElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLHRElement, __uuidof(IHTMLHRElement)); +#endif // #if defined(__IHTMLHRElement_INTERFACE_DEFINED__) +#if defined(__IHTMLHeaderElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLHeaderElement, __uuidof(IHTMLHeaderElement)); +#endif // #if defined(__IHTMLHeaderElement_INTERFACE_DEFINED__) +#if defined(__IHTMLIFrameElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLIFrameElement, __uuidof(IHTMLIFrameElement)); +#endif // #if defined(__IHTMLIFrameElement_INTERFACE_DEFINED__) +#if defined(__IHTMLImageElementFactory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLImageElementFactory, __uuidof(IHTMLImageElementFactory)); +#endif // #if defined(__IHTMLImageElementFactory_INTERFACE_DEFINED__) +#if defined(__IHTMLImgElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLImgElement, __uuidof(IHTMLImgElement)); +#endif // #if defined(__IHTMLImgElement_INTERFACE_DEFINED__) +#if defined(__IHTMLInputButtonElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLInputButtonElement, __uuidof(IHTMLInputButtonElement)); +#endif // #if defined(__IHTMLInputButtonElement_INTERFACE_DEFINED__) +#if defined(__IHTMLInputFileElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLInputFileElement, __uuidof(IHTMLInputFileElement)); +#endif // #if defined(__IHTMLInputFileElement_INTERFACE_DEFINED__) +#if defined(__IHTMLInputHiddenElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLInputHiddenElement, __uuidof(IHTMLInputHiddenElement)); +#endif // #if defined(__IHTMLInputHiddenElement_INTERFACE_DEFINED__) +#if defined(__IHTMLInputImage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLInputImage, __uuidof(IHTMLInputImage)); +#endif // #if defined(__IHTMLInputImage_INTERFACE_DEFINED__) +#if defined(__IHTMLInputTextElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLInputTextElement, __uuidof(IHTMLInputTextElement)); +#endif // #if defined(__IHTMLInputTextElement_INTERFACE_DEFINED__) +#if defined(__IHTMLIsIndexElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLIsIndexElement, __uuidof(IHTMLIsIndexElement)); +#endif // #if defined(__IHTMLIsIndexElement_INTERFACE_DEFINED__) +#if defined(__IHTMLLIElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLLIElement, __uuidof(IHTMLLIElement)); +#endif // #if defined(__IHTMLLIElement_INTERFACE_DEFINED__) +#if defined(__IHTMLLabelElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLLabelElement, __uuidof(IHTMLLabelElement)); +#endif // #if defined(__IHTMLLabelElement_INTERFACE_DEFINED__) +#if defined(__IHTMLLegendElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLLegendElement, __uuidof(IHTMLLegendElement)); +#endif // #if defined(__IHTMLLegendElement_INTERFACE_DEFINED__) +#if defined(__IHTMLLinkElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLLinkElement, __uuidof(IHTMLLinkElement)); +#endif // #if defined(__IHTMLLinkElement_INTERFACE_DEFINED__) +#if defined(__IHTMLListElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLListElement, __uuidof(IHTMLListElement)); +#endif // #if defined(__IHTMLListElement_INTERFACE_DEFINED__) +#if defined(__IHTMLLocation_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLLocation, __uuidof(IHTMLLocation)); +#endif // #if defined(__IHTMLLocation_INTERFACE_DEFINED__) +#if defined(__IHTMLMapElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLMapElement, __uuidof(IHTMLMapElement)); +#endif // #if defined(__IHTMLMapElement_INTERFACE_DEFINED__) +#if defined(__IHTMLMarqueeElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLMarqueeElement, __uuidof(IHTMLMarqueeElement)); +#endif // #if defined(__IHTMLMarqueeElement_INTERFACE_DEFINED__) +#if defined(__IHTMLMetaElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLMetaElement, __uuidof(IHTMLMetaElement)); +#endif // #if defined(__IHTMLMetaElement_INTERFACE_DEFINED__) +#if defined(__IHTMLMimeTypesCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLMimeTypesCollection, __uuidof(IHTMLMimeTypesCollection)); +#endif // #if defined(__IHTMLMimeTypesCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLNextIdElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLNextIdElement, __uuidof(IHTMLNextIdElement)); +#endif // #if defined(__IHTMLNextIdElement_INTERFACE_DEFINED__) +#if defined(__IHTMLNoShowElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLNoShowElement, __uuidof(IHTMLNoShowElement)); +#endif // #if defined(__IHTMLNoShowElement_INTERFACE_DEFINED__) +#if defined(__IHTMLOListElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLOListElement, __uuidof(IHTMLOListElement)); +#endif // #if defined(__IHTMLOListElement_INTERFACE_DEFINED__) +#if defined(__IHTMLObjectElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLObjectElement, __uuidof(IHTMLObjectElement)); +#endif // #if defined(__IHTMLObjectElement_INTERFACE_DEFINED__) +#if defined(__IHTMLOpsProfile_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLOpsProfile, __uuidof(IHTMLOpsProfile)); +#endif // #if defined(__IHTMLOpsProfile_INTERFACE_DEFINED__) +#if defined(__IHTMLOptionButtonElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLOptionButtonElement, __uuidof(IHTMLOptionButtonElement)); +#endif // #if defined(__IHTMLOptionButtonElement_INTERFACE_DEFINED__) +#if defined(__IHTMLOptionElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLOptionElement, __uuidof(IHTMLOptionElement)); +#endif // #if defined(__IHTMLOptionElement_INTERFACE_DEFINED__) +#if defined(__IHTMLOptionElementFactory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLOptionElementFactory, __uuidof(IHTMLOptionElementFactory)); +#endif // #if defined(__IHTMLOptionElementFactory_INTERFACE_DEFINED__) +#if defined(__IHTMLOptionsHolder_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLOptionsHolder, __uuidof(IHTMLOptionsHolder)); +#endif // #if defined(__IHTMLOptionsHolder_INTERFACE_DEFINED__) +#if defined(__IHTMLParaElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLParaElement, __uuidof(IHTMLParaElement)); +#endif // #if defined(__IHTMLParaElement_INTERFACE_DEFINED__) +#if defined(__IHTMLPhraseElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLPhraseElement, __uuidof(IHTMLPhraseElement)); +#endif // #if defined(__IHTMLPhraseElement_INTERFACE_DEFINED__) +#if defined(__IHTMLPluginsCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLPluginsCollection, __uuidof(IHTMLPluginsCollection)); +#endif // #if defined(__IHTMLPluginsCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLRuleStyle_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLRuleStyle, __uuidof(IHTMLRuleStyle)); +#endif // #if defined(__IHTMLRuleStyle_INTERFACE_DEFINED__) +#if defined(__IHTMLScreen_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLScreen, __uuidof(IHTMLScreen)); +#endif // #if defined(__IHTMLScreen_INTERFACE_DEFINED__) +#if defined(__IHTMLScriptElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLScriptElement, __uuidof(IHTMLScriptElement)); +#endif // #if defined(__IHTMLScriptElement_INTERFACE_DEFINED__) +#if defined(__IHTMLSelectElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLSelectElement, __uuidof(IHTMLSelectElement)); +#endif // #if defined(__IHTMLSelectElement_INTERFACE_DEFINED__) +#if defined(__IHTMLSelectionObject_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLSelectionObject, __uuidof(IHTMLSelectionObject)); +#endif // #if defined(__IHTMLSelectionObject_INTERFACE_DEFINED__) +#if defined(__IHTMLSpanElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLSpanElement, __uuidof(IHTMLSpanElement)); +#endif // #if defined(__IHTMLSpanElement_INTERFACE_DEFINED__) +#if defined(__IHTMLSpanFlow_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLSpanFlow, __uuidof(IHTMLSpanFlow)); +#endif // #if defined(__IHTMLSpanFlow_INTERFACE_DEFINED__) +#if defined(__IHTMLStyle_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLStyle, __uuidof(IHTMLStyle)); +#endif // #if defined(__IHTMLStyle_INTERFACE_DEFINED__) +#if defined(__IHTMLStyleElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLStyleElement, __uuidof(IHTMLStyleElement)); +#endif // #if defined(__IHTMLStyleElement_INTERFACE_DEFINED__) +#if defined(__IHTMLStyleFontFace_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLStyleFontFace, __uuidof(IHTMLStyleFontFace)); +#endif // #if defined(__IHTMLStyleFontFace_INTERFACE_DEFINED__) +#if defined(__IHTMLStyleSheet_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLStyleSheet, __uuidof(IHTMLStyleSheet)); +#endif // #if defined(__IHTMLStyleSheet_INTERFACE_DEFINED__) +#if defined(__IHTMLStyleSheetRule_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLStyleSheetRule, __uuidof(IHTMLStyleSheetRule)); +#endif // #if defined(__IHTMLStyleSheetRule_INTERFACE_DEFINED__) +#if defined(__IHTMLStyleSheetRulesCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLStyleSheetRulesCollection, __uuidof(IHTMLStyleSheetRulesCollection)); +#endif // #if defined(__IHTMLStyleSheetRulesCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLStyleSheetsCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLStyleSheetsCollection, __uuidof(IHTMLStyleSheetsCollection)); +#endif // #if defined(__IHTMLStyleSheetsCollection_INTERFACE_DEFINED__) +#if defined(__IHTMLTable_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTable, __uuidof(IHTMLTable)); +#endif // #if defined(__IHTMLTable_INTERFACE_DEFINED__) +#if defined(__IHTMLTableCaption_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTableCaption, __uuidof(IHTMLTableCaption)); +#endif // #if defined(__IHTMLTableCaption_INTERFACE_DEFINED__) +#if defined(__IHTMLTableCell_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTableCell, __uuidof(IHTMLTableCell)); +#endif // #if defined(__IHTMLTableCell_INTERFACE_DEFINED__) +#if defined(__IHTMLTableCol_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTableCol, __uuidof(IHTMLTableCol)); +#endif // #if defined(__IHTMLTableCol_INTERFACE_DEFINED__) +#if defined(__IHTMLTableRow_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTableRow, __uuidof(IHTMLTableRow)); +#endif // #if defined(__IHTMLTableRow_INTERFACE_DEFINED__) +#if defined(__IHTMLTableSection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTableSection, __uuidof(IHTMLTableSection)); +#endif // #if defined(__IHTMLTableSection_INTERFACE_DEFINED__) +#if defined(__IHTMLTextAreaElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTextAreaElement, __uuidof(IHTMLTextAreaElement)); +#endif // #if defined(__IHTMLTextAreaElement_INTERFACE_DEFINED__) +#if defined(__IHTMLTextContainer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTextContainer, __uuidof(IHTMLTextContainer)); +#endif // #if defined(__IHTMLTextContainer_INTERFACE_DEFINED__) +#if defined(__IHTMLTextElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTextElement, __uuidof(IHTMLTextElement)); +#endif // #if defined(__IHTMLTextElement_INTERFACE_DEFINED__) +#if defined(__IHTMLTitleElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTitleElement, __uuidof(IHTMLTitleElement)); +#endif // #if defined(__IHTMLTitleElement_INTERFACE_DEFINED__) +#if defined(__IHTMLTxtRange_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLTxtRange, __uuidof(IHTMLTxtRange)); +#endif // #if defined(__IHTMLTxtRange_INTERFACE_DEFINED__) +#if defined(__IHTMLUListElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLUListElement, __uuidof(IHTMLUListElement)); +#endif // #if defined(__IHTMLUListElement_INTERFACE_DEFINED__) +#if defined(__IHTMLUnknownElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLUnknownElement, __uuidof(IHTMLUnknownElement)); +#endif // #if defined(__IHTMLUnknownElement_INTERFACE_DEFINED__) +#if defined(__IHTMLWindow2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHTMLWindow2, __uuidof(IHTMLWindow2)); +#endif // #if defined(__IHTMLWindow2_INTERFACE_DEFINED__) +#if defined(__IHlink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHlink, __uuidof(IHlink)); +#endif // #if defined(__IHlink_INTERFACE_DEFINED__) +#if defined(__IHlinkBrowseContext_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHlinkBrowseContext, __uuidof(IHlinkBrowseContext)); +#endif // #if defined(__IHlinkBrowseContext_INTERFACE_DEFINED__) +#if defined(__IHlinkFrame_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHlinkFrame, __uuidof(IHlinkFrame)); +#endif // #if defined(__IHlinkFrame_INTERFACE_DEFINED__) +#if defined(__IHlinkSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHlinkSite, __uuidof(IHlinkSite)); +#endif // #if defined(__IHlinkSite_INTERFACE_DEFINED__) +#if defined(__IHlinkTarget_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHlinkTarget, __uuidof(IHlinkTarget)); +#endif // #if defined(__IHlinkTarget_INTERFACE_DEFINED__) +#if defined(__IHttpNegotiate_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHttpNegotiate, __uuidof(IHttpNegotiate)); +#endif // #if defined(__IHttpNegotiate_INTERFACE_DEFINED__) +#if defined(__IHttpNegotiate2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHttpNegotiate2, __uuidof(IHttpNegotiate2)); +#endif // #if defined(__IHttpNegotiate2_INTERFACE_DEFINED__) +#if defined(__IHttpSecurity_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IHttpSecurity, __uuidof(IHttpSecurity)); +#endif // #if defined(__IHttpSecurity_INTERFACE_DEFINED__) +#if defined(__IImageDecodeEventSink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IImageDecodeEventSink, __uuidof(IImageDecodeEventSink)); +#endif // #if defined(__IImageDecodeEventSink_INTERFACE_DEFINED__) +#if defined(__IImageDecodeFilter_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IImageDecodeFilter, __uuidof(IImageDecodeFilter)); +#endif // #if defined(__IImageDecodeFilter_INTERFACE_DEFINED__) +#if defined(__IInternalUnknown_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternalUnknown, __uuidof(IInternalUnknown)); +#endif // #if defined(__IInternalUnknown_INTERFACE_DEFINED__) +#if defined(__IInternet_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternet, __uuidof(IInternet)); +#endif // #if defined(__IInternet_INTERFACE_DEFINED__) +#if defined(__IInternetBindInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetBindInfo, __uuidof(IInternetBindInfo)); +#endif // #if defined(__IInternetBindInfo_INTERFACE_DEFINED__) +#if defined(__IInternetHostSecurityManager_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetHostSecurityManager, __uuidof(IInternetHostSecurityManager)); +#endif // #if defined(__IInternetHostSecurityManager_INTERFACE_DEFINED__) +#if defined(__IInternetPriority_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetPriority, __uuidof(IInternetPriority)); +#endif // #if defined(__IInternetPriority_INTERFACE_DEFINED__) +#if defined(__IInternetProtocol_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetProtocol, __uuidof(IInternetProtocol)); +#endif // #if defined(__IInternetProtocol_INTERFACE_DEFINED__) +#if defined(__IInternetProtocolInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetProtocolInfo, __uuidof(IInternetProtocolInfo)); +#endif // #if defined(__IInternetProtocolInfo_INTERFACE_DEFINED__) +#if defined(__IInternetProtocolRoot_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetProtocolRoot, __uuidof(IInternetProtocolRoot)); +#endif // #if defined(__IInternetProtocolRoot_INTERFACE_DEFINED__) +#if defined(__IInternetProtocolSink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetProtocolSink, __uuidof(IInternetProtocolSink)); +#endif // #if defined(__IInternetProtocolSink_INTERFACE_DEFINED__) +#if defined(__IInternetProtocolSinkStackable_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetProtocolSinkStackable, __uuidof(IInternetProtocolSinkStackable)); +#endif // #if defined(__IInternetProtocolSinkStackable_INTERFACE_DEFINED__) +#if defined(__IInternetSecurityManager_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetSecurityManager, __uuidof(IInternetSecurityManager)); +#endif // #if defined(__IInternetSecurityManager_INTERFACE_DEFINED__) +#if defined(__IInternetSecurityMgrSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetSecurityMgrSite, __uuidof(IInternetSecurityMgrSite)); +#endif // #if defined(__IInternetSecurityMgrSite_INTERFACE_DEFINED__) +#if defined(__IInternetSession_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetSession, __uuidof(IInternetSession)); +#endif // #if defined(__IInternetSession_INTERFACE_DEFINED__) +#if defined(__IInternetThreadSwitch_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetThreadSwitch, __uuidof(IInternetThreadSwitch)); +#endif // #if defined(__IInternetThreadSwitch_INTERFACE_DEFINED__) +#if defined(__IInternetZoneManager_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IInternetZoneManager, __uuidof(IInternetZoneManager)); +#endif // #if defined(__IInternetZoneManager_INTERFACE_DEFINED__) +#if defined(__ILayoutStorage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ILayoutStorage, __uuidof(ILayoutStorage)); +#endif // #if defined(__ILayoutStorage_INTERFACE_DEFINED__) +#if defined(__ILockBytes_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ILockBytes, __uuidof(ILockBytes)); +#endif // #if defined(__ILockBytes_INTERFACE_DEFINED__) +#if defined(__IMLangCodePages_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangCodePages, __uuidof(IMLangCodePages)); +#endif // #if defined(__IMLangCodePages_INTERFACE_DEFINED__) +#if defined(__IMLangConvertCharset_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangConvertCharset, __uuidof(IMLangConvertCharset)); +#endif // #if defined(__IMLangConvertCharset_INTERFACE_DEFINED__) +#if defined(__IMLangFontLink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangFontLink, __uuidof(IMLangFontLink)); +#endif // #if defined(__IMLangFontLink_INTERFACE_DEFINED__) +#if defined(__IMLangLineBreakConsole_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangLineBreakConsole, __uuidof(IMLangLineBreakConsole)); +#endif // #if defined(__IMLangLineBreakConsole_INTERFACE_DEFINED__) +#if defined(__IMLangString_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangString, __uuidof(IMLangString)); +#endif // #if defined(__IMLangString_INTERFACE_DEFINED__) +#if defined(__IMLangStringAStr_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangStringAStr, __uuidof(IMLangStringAStr)); +#endif // #if defined(__IMLangStringAStr_INTERFACE_DEFINED__) +#if defined(__IMLangStringBufA_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangStringBufA, __uuidof(IMLangStringBufA)); +#endif // #if defined(__IMLangStringBufA_INTERFACE_DEFINED__) +#if defined(__IMLangStringBufW_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangStringBufW, __uuidof(IMLangStringBufW)); +#endif // #if defined(__IMLangStringBufW_INTERFACE_DEFINED__) +#if defined(__IMLangStringWStr_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMLangStringWStr, __uuidof(IMLangStringWStr)); +#endif // #if defined(__IMLangStringWStr_INTERFACE_DEFINED__) +#if defined(__IMalloc_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMalloc, __uuidof(IMalloc)); +#endif // #if defined(__IMalloc_INTERFACE_DEFINED__) +#if defined(__IMallocSpy_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMallocSpy, __uuidof(IMallocSpy)); +#endif // #if defined(__IMallocSpy_INTERFACE_DEFINED__) +#if defined(__IMapMIMEToCLSID_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMapMIMEToCLSID, __uuidof(IMapMIMEToCLSID)); +#endif // #if defined(__IMapMIMEToCLSID_INTERFACE_DEFINED__) +#if defined(__IMarshal_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMarshal, __uuidof(IMarshal)); +#endif // #if defined(__IMarshal_INTERFACE_DEFINED__) +#if defined(__IMarshal2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMarshal2, __uuidof(IMarshal2)); +#endif // #if defined(__IMarshal2_INTERFACE_DEFINED__) +#if defined(__IMessageFilter_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMessageFilter, __uuidof(IMessageFilter)); +#endif // #if defined(__IMessageFilter_INTERFACE_DEFINED__) +#if defined(__IMimeInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMimeInfo, __uuidof(IMimeInfo)); +#endif // #if defined(__IMimeInfo_INTERFACE_DEFINED__) +#if defined(__IMoniker_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMoniker, __uuidof(IMoniker)); +#endif // #if defined(__IMoniker_INTERFACE_DEFINED__) +#if defined(__IMonikerProp_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMonikerProp, __uuidof(IMonikerProp)); +#endif // #if defined(__IMonikerProp_INTERFACE_DEFINED__) +#if defined(__IMultiLanguage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMultiLanguage, __uuidof(IMultiLanguage)); +#endif // #if defined(__IMultiLanguage_INTERFACE_DEFINED__) +#if defined(__IMultiQI_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IMultiQI, __uuidof(IMultiQI)); +#endif // #if defined(__IMultiQI_INTERFACE_DEFINED__) +#if defined(__IObjectIdentity_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IObjectIdentity, __uuidof(IObjectIdentity)); +#endif // #if defined(__IObjectIdentity_INTERFACE_DEFINED__) +#if defined(__IObjectSafety_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IObjectSafety, __uuidof(IObjectSafety)); +#endif // #if defined(__IObjectSafety_INTERFACE_DEFINED__) +#if defined(__IObjectWithSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IObjectWithSite, __uuidof(IObjectWithSite)); +#endif // #if defined(__IObjectWithSite_INTERFACE_DEFINED__) +#if defined(__IOleAdviseHolder_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleAdviseHolder, __uuidof(IOleAdviseHolder)); +#endif // #if defined(__IOleAdviseHolder_INTERFACE_DEFINED__) +#if defined(__IOleCache_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleCache, __uuidof(IOleCache)); +#endif // #if defined(__IOleCache_INTERFACE_DEFINED__) +#if defined(__IOleCache2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleCache2, __uuidof(IOleCache2)); +#endif // #if defined(__IOleCache2_INTERFACE_DEFINED__) +#if defined(__IOleCacheControl_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleCacheControl, __uuidof(IOleCacheControl)); +#endif // #if defined(__IOleCacheControl_INTERFACE_DEFINED__) +#if defined(__IOleClientSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleClientSite, __uuidof(IOleClientSite)); +#endif // #if defined(__IOleClientSite_INTERFACE_DEFINED__) +#if defined(__IOleCommandTarget_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleCommandTarget, __uuidof(IOleCommandTarget)); +#endif // #if defined(__IOleCommandTarget_INTERFACE_DEFINED__) +#if defined(__IOleContainer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleContainer, __uuidof(IOleContainer)); +#endif // #if defined(__IOleContainer_INTERFACE_DEFINED__) +#if defined(__IOleControl_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleControl, __uuidof(IOleControl)); +#endif // #if defined(__IOleControl_INTERFACE_DEFINED__) +#if defined(__IOleControlSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleControlSite, __uuidof(IOleControlSite)); +#endif // #if defined(__IOleControlSite_INTERFACE_DEFINED__) +#if defined(__IOleDocument_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleDocument, __uuidof(IOleDocument)); +#endif // #if defined(__IOleDocument_INTERFACE_DEFINED__) +#if defined(__IOleDocumentSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleDocumentSite, __uuidof(IOleDocumentSite)); +#endif // #if defined(__IOleDocumentSite_INTERFACE_DEFINED__) +#if defined(__IOleDocumentView_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleDocumentView, __uuidof(IOleDocumentView)); +#endif // #if defined(__IOleDocumentView_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceActiveObject_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceActiveObject, __uuidof(IOleInPlaceActiveObject)); +#endif // #if defined(__IOleInPlaceActiveObject_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceFrame_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceFrame, __uuidof(IOleInPlaceFrame)); +#endif // #if defined(__IOleInPlaceFrame_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceObject_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceObject, __uuidof(IOleInPlaceObject)); +#endif // #if defined(__IOleInPlaceObject_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceObjectWindowless_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceObjectWindowless, __uuidof(IOleInPlaceObjectWindowless)); +#endif // #if defined(__IOleInPlaceObjectWindowless_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceSite, __uuidof(IOleInPlaceSite)); +#endif // #if defined(__IOleInPlaceSite_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceSiteEx_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceSiteEx, __uuidof(IOleInPlaceSiteEx)); +#endif // #if defined(__IOleInPlaceSiteEx_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceSiteWindowless_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceSiteWindowless, __uuidof(IOleInPlaceSiteWindowless)); +#endif // #if defined(__IOleInPlaceSiteWindowless_INTERFACE_DEFINED__) +#if defined(__IOleInPlaceUIWindow_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleInPlaceUIWindow, __uuidof(IOleInPlaceUIWindow)); +#endif // #if defined(__IOleInPlaceUIWindow_INTERFACE_DEFINED__) +#if defined(__IOleItemContainer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleItemContainer, __uuidof(IOleItemContainer)); +#endif // #if defined(__IOleItemContainer_INTERFACE_DEFINED__) +#if defined(__IOleLink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleLink, __uuidof(IOleLink)); +#endif // #if defined(__IOleLink_INTERFACE_DEFINED__) +#if defined(__IOleObject_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleObject, __uuidof(IOleObject)); +#endif // #if defined(__IOleObject_INTERFACE_DEFINED__) +#if defined(__IOleParentUndoUnit_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleParentUndoUnit, __uuidof(IOleParentUndoUnit)); +#endif // #if defined(__IOleParentUndoUnit_INTERFACE_DEFINED__) +#if defined(__IOleUndoManager_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleUndoManager, __uuidof(IOleUndoManager)); +#endif // #if defined(__IOleUndoManager_INTERFACE_DEFINED__) +#if defined(__IOleUndoUnit_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleUndoUnit, __uuidof(IOleUndoUnit)); +#endif // #if defined(__IOleUndoUnit_INTERFACE_DEFINED__) +#if defined(__IOleWindow_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOleWindow, __uuidof(IOleWindow)); +#endif // #if defined(__IOleWindow_INTERFACE_DEFINED__) +#if defined(__IOmHistory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOmHistory, __uuidof(IOmHistory)); +#endif // #if defined(__IOmHistory_INTERFACE_DEFINED__) +#if defined(__IOmNavigator_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOmNavigator, __uuidof(IOmNavigator)); +#endif // #if defined(__IOmNavigator_INTERFACE_DEFINED__) +#if defined(__IOplockStorage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IOplockStorage, __uuidof(IOplockStorage)); +#endif // #if defined(__IOplockStorage_INTERFACE_DEFINED__) +#if defined(__IPSFactoryBuffer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPSFactoryBuffer, __uuidof(IPSFactoryBuffer)); +#endif // #if defined(__IPSFactoryBuffer_INTERFACE_DEFINED__) +#if defined(__IParseDisplayName_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IParseDisplayName, __uuidof(IParseDisplayName)); +#endif // #if defined(__IParseDisplayName_INTERFACE_DEFINED__) +#if defined(__IPerPropertyBrowsing_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPerPropertyBrowsing, __uuidof(IPerPropertyBrowsing)); +#endif // #if defined(__IPerPropertyBrowsing_INTERFACE_DEFINED__) +#if defined(__IPersist_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersist, __uuidof(IPersist)); +#endif // #if defined(__IPersist_INTERFACE_DEFINED__) +#if defined(__IPersistFile_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistFile, __uuidof(IPersistFile)); +#endif // #if defined(__IPersistFile_INTERFACE_DEFINED__) +#if defined(__IPersistFolder_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistFolder, __uuidof(IPersistFolder)); +#endif // #if defined(__IPersistFolder_INTERFACE_DEFINED__) +#if defined(__IPersistFolder2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistFolder2, __uuidof(IPersistFolder2)); +#endif // #if defined(__IPersistFolder2_INTERFACE_DEFINED__) +#if defined(__IPersistHistory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistHistory, __uuidof(IPersistHistory)); +#endif // #if defined(__IPersistHistory_INTERFACE_DEFINED__) +#if defined(__IPersistMemory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistMemory, __uuidof(IPersistMemory)); +#endif // #if defined(__IPersistMemory_INTERFACE_DEFINED__) +#if defined(__IPersistMoniker_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistMoniker, __uuidof(IPersistMoniker)); +#endif // #if defined(__IPersistMoniker_INTERFACE_DEFINED__) +#if defined(__IPersistPropertyBag_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistPropertyBag, __uuidof(IPersistPropertyBag)); +#endif // #if defined(__IPersistPropertyBag_INTERFACE_DEFINED__) +#if defined(__IPersistPropertyBag2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistPropertyBag2, __uuidof(IPersistPropertyBag2)); +#endif // #if defined(__IPersistPropertyBag2_INTERFACE_DEFINED__) +#if defined(__IPersistStorage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistStorage, __uuidof(IPersistStorage)); +#endif // #if defined(__IPersistStorage_INTERFACE_DEFINED__) +#if defined(__IPersistStream_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistStream, __uuidof(IPersistStream)); +#endif // #if defined(__IPersistStream_INTERFACE_DEFINED__) +#if defined(__IPersistStreamInit_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPersistStreamInit, __uuidof(IPersistStreamInit)); +#endif // #if defined(__IPersistStreamInit_INTERFACE_DEFINED__) +#if defined(__IPicture_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPicture, __uuidof(IPicture)); +#endif // #if defined(__IPicture_INTERFACE_DEFINED__) +#if defined(__IPictureDisp_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPictureDisp, __uuidof(IPictureDisp)); +#endif // #if defined(__IPictureDisp_INTERFACE_DEFINED__) +#if defined(__IPipeByte_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPipeByte, __uuidof(IPipeByte)); +#endif // #if defined(__IPipeByte_INTERFACE_DEFINED__) +#if defined(__IPipeDouble_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPipeDouble, __uuidof(IPipeDouble)); +#endif // #if defined(__IPipeDouble_INTERFACE_DEFINED__) +#if defined(__IPipeLong_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPipeLong, __uuidof(IPipeLong)); +#endif // #if defined(__IPipeLong_INTERFACE_DEFINED__) +#if defined(__IPointerInactive_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPointerInactive, __uuidof(IPointerInactive)); +#endif // #if defined(__IPointerInactive_INTERFACE_DEFINED__) +#if defined(__IPrint_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPrint, __uuidof(IPrint)); +#endif // #if defined(__IPrint_INTERFACE_DEFINED__) +#if defined(__IProgressNotify_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IProgressNotify, __uuidof(IProgressNotify)); +#endif // #if defined(__IProgressNotify_INTERFACE_DEFINED__) +#if defined(__IPropertyBag_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertyBag, __uuidof(IPropertyBag)); +#endif // #if defined(__IPropertyBag_INTERFACE_DEFINED__) +#if defined(__IPropertyBag2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertyBag2, __uuidof(IPropertyBag2)); +#endif // #if defined(__IPropertyBag2_INTERFACE_DEFINED__) +#if defined(__IPropertyNotifySink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertyNotifySink, __uuidof(IPropertyNotifySink)); +#endif // #if defined(__IPropertyNotifySink_INTERFACE_DEFINED__) +#if defined(__IPropertyPage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertyPage, __uuidof(IPropertyPage)); +#endif // #if defined(__IPropertyPage_INTERFACE_DEFINED__) +#if defined(__IPropertyPage2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertyPage2, __uuidof(IPropertyPage2)); +#endif // #if defined(__IPropertyPage2_INTERFACE_DEFINED__) +#if defined(__IPropertyPageSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertyPageSite, __uuidof(IPropertyPageSite)); +#endif // #if defined(__IPropertyPageSite_INTERFACE_DEFINED__) +#if defined(__IPropertySetStorage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertySetStorage, __uuidof(IPropertySetStorage)); +#endif // #if defined(__IPropertySetStorage_INTERFACE_DEFINED__) +#if defined(__IPropertyStorage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IPropertyStorage, __uuidof(IPropertyStorage)); +#endif // #if defined(__IPropertyStorage_INTERFACE_DEFINED__) +#if defined(__IProvideClassInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IProvideClassInfo, __uuidof(IProvideClassInfo)); +#endif // #if defined(__IProvideClassInfo_INTERFACE_DEFINED__) +#if defined(__IProvideClassInfo2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IProvideClassInfo2, __uuidof(IProvideClassInfo2)); +#endif // #if defined(__IProvideClassInfo2_INTERFACE_DEFINED__) +#if defined(__IProvideMultipleClassInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IProvideMultipleClassInfo, __uuidof(IProvideMultipleClassInfo)); +#endif // #if defined(__IProvideMultipleClassInfo_INTERFACE_DEFINED__) +#if defined(__IQuickActivate_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IQuickActivate, __uuidof(IQuickActivate)); +#endif // #if defined(__IQuickActivate_INTERFACE_DEFINED__) +#if defined(__IROTData_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IROTData, __uuidof(IROTData)); +#endif // #if defined(__IROTData_INTERFACE_DEFINED__) +#if defined(__IRecordInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRecordInfo, __uuidof(IRecordInfo)); +#endif // #if defined(__IRecordInfo_INTERFACE_DEFINED__) +#if defined(__IReleaseMarshalBuffers_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IReleaseMarshalBuffers, __uuidof(IReleaseMarshalBuffers)); +#endif // #if defined(__IReleaseMarshalBuffers_INTERFACE_DEFINED__) +#if defined(__IRootStorage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRootStorage, __uuidof(IRootStorage)); +#endif // #if defined(__IRootStorage_INTERFACE_DEFINED__) +#if defined(__IRpcChannelBuffer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcChannelBuffer, __uuidof(IRpcChannelBuffer)); +#endif // #if defined(__IRpcChannelBuffer_INTERFACE_DEFINED__) +#if defined(__IRpcChannelBuffer2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcChannelBuffer2, __uuidof(IRpcChannelBuffer2)); +#endif // #if defined(__IRpcChannelBuffer2_INTERFACE_DEFINED__) +#if defined(__IRpcChannelBuffer3_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcChannelBuffer3, __uuidof(IRpcChannelBuffer3)); +#endif // #if defined(__IRpcChannelBuffer3_INTERFACE_DEFINED__) +#if defined(__IRpcHelper_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcHelper, __uuidof(IRpcHelper)); +#endif // #if defined(__IRpcHelper_INTERFACE_DEFINED__) +#if defined(__IRpcOptions_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcOptions, __uuidof(IRpcOptions)); +#endif // #if defined(__IRpcOptions_INTERFACE_DEFINED__) +#if defined(__IRpcProxyBuffer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcProxyBuffer, __uuidof(IRpcProxyBuffer)); +#endif // #if defined(__IRpcProxyBuffer_INTERFACE_DEFINED__) +#if defined(__IRpcStubBuffer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcStubBuffer, __uuidof(IRpcStubBuffer)); +#endif // #if defined(__IRpcStubBuffer_INTERFACE_DEFINED__) +#if defined(__IRpcSyntaxNegotiate_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRpcSyntaxNegotiate, __uuidof(IRpcSyntaxNegotiate)); +#endif // #if defined(__IRpcSyntaxNegotiate_INTERFACE_DEFINED__) +#if defined(__IRunnableObject_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRunnableObject, __uuidof(IRunnableObject)); +#endif // #if defined(__IRunnableObject_INTERFACE_DEFINED__) +#if defined(__IRunningObjectTable_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IRunningObjectTable, __uuidof(IRunningObjectTable)); +#endif // #if defined(__IRunningObjectTable_INTERFACE_DEFINED__) +#if defined(__ISequentialStream_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISequentialStream, __uuidof(ISequentialStream)); +#endif // #if defined(__ISequentialStream_INTERFACE_DEFINED__) +#if defined(__IServerSecurity_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IServerSecurity, __uuidof(IServerSecurity)); +#endif // #if defined(__IServerSecurity_INTERFACE_DEFINED__) +#if defined(__IServiceProvider_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IServiceProvider, __uuidof(IServiceProvider)); +#endif // #if defined(__IServiceProvider_INTERFACE_DEFINED__) +#if defined(__IShellBrowser_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellBrowser, __uuidof(IShellBrowser)); +#endif // #if defined(__IShellBrowser_INTERFACE_DEFINED__) +#if defined(__IShellDispatch_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellDispatch, __uuidof(IShellDispatch)); +#endif // #if defined(__IShellDispatch_INTERFACE_DEFINED__) +#if defined(__IShellExtInit_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellExtInit, __uuidof(IShellExtInit)); +#endif // #if defined(__IShellExtInit_INTERFACE_DEFINED__) +#if defined(__IShellFolder_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellFolder, __uuidof(IShellFolder)); +#endif // #if defined(__IShellFolder_INTERFACE_DEFINED__) +#if defined(__IShellFolderViewDual_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellFolderViewDual, __uuidof(IShellFolderViewDual)); +#endif // #if defined(__IShellFolderViewDual_INTERFACE_DEFINED__) +#if defined(__IShellLinkA_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellLinkA, __uuidof(IShellLinkA)); +#endif // #if defined(__IShellLinkA_INTERFACE_DEFINED__) +#if defined(__IShellLinkDual_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellLinkDual, __uuidof(IShellLinkDual)); +#endif // #if defined(__IShellLinkDual_INTERFACE_DEFINED__) +#if defined(__IShellLinkW_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellLinkW, __uuidof(IShellLinkW)); +#endif // #if defined(__IShellLinkW_INTERFACE_DEFINED__) +#if defined(__IShellPropSheetExt_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellPropSheetExt, __uuidof(IShellPropSheetExt)); +#endif // #if defined(__IShellPropSheetExt_INTERFACE_DEFINED__) +#if defined(__IShellUIHelper_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellUIHelper, __uuidof(IShellUIHelper)); +#endif // #if defined(__IShellUIHelper_INTERFACE_DEFINED__) +#if defined(__IShellView_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellView, __uuidof(IShellView)); +#endif // #if defined(__IShellView_INTERFACE_DEFINED__) +#if defined(__IShellView2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellView2, __uuidof(IShellView2)); +#endif // #if defined(__IShellView2_INTERFACE_DEFINED__) +#if defined(__IShellWindows_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IShellWindows, __uuidof(IShellWindows)); +#endif // #if defined(__IShellWindows_INTERFACE_DEFINED__) +#if defined(__ISimpleFrameSite_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISimpleFrameSite, __uuidof(ISimpleFrameSite)); +#endif // #if defined(__ISimpleFrameSite_INTERFACE_DEFINED__) +#if defined(__ISoftDistExt_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISoftDistExt, __uuidof(ISoftDistExt)); +#endif // #if defined(__ISoftDistExt_INTERFACE_DEFINED__) +#if defined(__ISpecifyPropertyPages_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISpecifyPropertyPages, __uuidof(ISpecifyPropertyPages)); +#endif // #if defined(__ISpecifyPropertyPages_INTERFACE_DEFINED__) +#if defined(__IStdMarshalInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IStdMarshalInfo, __uuidof(IStdMarshalInfo)); +#endif // #if defined(__IStdMarshalInfo_INTERFACE_DEFINED__) +#if defined(__IStorage_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IStorage, __uuidof(IStorage)); +#endif // #if defined(__IStorage_INTERFACE_DEFINED__) +#if defined(__IStream_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IStream, __uuidof(IStream)); +#endif // #if defined(__IStream_INTERFACE_DEFINED__) +#if defined(__ISubscriptionMgr_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISubscriptionMgr, __uuidof(ISubscriptionMgr)); +#endif // #if defined(__ISubscriptionMgr_INTERFACE_DEFINED__) +#if defined(__ISupportErrorInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISupportErrorInfo, __uuidof(ISupportErrorInfo)); +#endif // #if defined(__ISupportErrorInfo_INTERFACE_DEFINED__) +#if defined(__ISurrogate_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISurrogate, __uuidof(ISurrogate)); +#endif // #if defined(__ISurrogate_INTERFACE_DEFINED__) +#if defined(__ISynchronize_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISynchronize, __uuidof(ISynchronize)); +#endif // #if defined(__ISynchronize_INTERFACE_DEFINED__) +#if defined(__ISynchronizeContainer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISynchronizeContainer, __uuidof(ISynchronizeContainer)); +#endif // #if defined(__ISynchronizeContainer_INTERFACE_DEFINED__) +#if defined(__ISynchronizeEvent_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISynchronizeEvent, __uuidof(ISynchronizeEvent)); +#endif // #if defined(__ISynchronizeEvent_INTERFACE_DEFINED__) +#if defined(__ISynchronizeHandle_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISynchronizeHandle, __uuidof(ISynchronizeHandle)); +#endif // #if defined(__ISynchronizeHandle_INTERFACE_DEFINED__) +#if defined(__ISynchronizeMutex_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ISynchronizeMutex, __uuidof(ISynchronizeMutex)); +#endif // #if defined(__ISynchronizeMutex_INTERFACE_DEFINED__) +#if defined(__IThumbnailExtractor_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IThumbnailExtractor, __uuidof(IThumbnailExtractor)); +#endif // #if defined(__IThumbnailExtractor_INTERFACE_DEFINED__) +#if defined(__ITimeAndNoticeControl_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITimeAndNoticeControl, __uuidof(ITimeAndNoticeControl)); +#endif // #if defined(__ITimeAndNoticeControl_INTERFACE_DEFINED__) +#if defined(__ITimer_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITimer, __uuidof(ITimer)); +#endif // #if defined(__ITimer_INTERFACE_DEFINED__) +#if defined(__ITimerService_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITimerService, __uuidof(ITimerService)); +#endif // #if defined(__ITimerService_INTERFACE_DEFINED__) +#if defined(__ITimerSink_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITimerSink, __uuidof(ITimerSink)); +#endif // #if defined(__ITimerSink_INTERFACE_DEFINED__) +#if defined(__ITypeChangeEvents_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeChangeEvents, __uuidof(ITypeChangeEvents)); +#endif // #if defined(__ITypeChangeEvents_INTERFACE_DEFINED__) +#if defined(__ITypeComp_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeComp, __uuidof(ITypeComp)); +#endif // #if defined(__ITypeComp_INTERFACE_DEFINED__) +#if defined(__ITypeFactory_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeFactory, __uuidof(ITypeFactory)); +#endif // #if defined(__ITypeFactory_INTERFACE_DEFINED__) +#if defined(__ITypeInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeInfo, __uuidof(ITypeInfo)); +#endif // #if defined(__ITypeInfo_INTERFACE_DEFINED__) +#if defined(__ITypeInfo2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeInfo2, __uuidof(ITypeInfo2)); +#endif // #if defined(__ITypeInfo2_INTERFACE_DEFINED__) +#if defined(__ITypeLib_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeLib, __uuidof(ITypeLib)); +#endif // #if defined(__ITypeLib_INTERFACE_DEFINED__) +#if defined(__ITypeLib2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeLib2, __uuidof(ITypeLib2)); +#endif // #if defined(__ITypeLib2_INTERFACE_DEFINED__) +#if defined(__ITypeMarshal_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(ITypeMarshal, __uuidof(ITypeMarshal)); +#endif // #if defined(__ITypeMarshal_INTERFACE_DEFINED__) +#if defined(__IUnknown_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IUnknown, __uuidof(IUnknown)); +#endif // #if defined(__IUnknown_INTERFACE_DEFINED__) +#if defined(__IUrlHistoryNotify_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IUrlHistoryNotify, __uuidof(IUrlHistoryNotify)); +#endif // #if defined(__IUrlHistoryNotify_INTERFACE_DEFINED__) +#if defined(__IUrlHistoryStg_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IUrlHistoryStg, __uuidof(IUrlHistoryStg)); +#endif // #if defined(__IUrlHistoryStg_INTERFACE_DEFINED__) +#if defined(__IUrlHistoryStg2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IUrlHistoryStg2, __uuidof(IUrlHistoryStg2)); +#endif // #if defined(__IUrlHistoryStg2_INTERFACE_DEFINED__) +#if defined(__IUrlMon_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IUrlMon, __uuidof(IUrlMon)); +#endif // #if defined(__IUrlMon_INTERFACE_DEFINED__) +#if defined(__IVariantChangeType_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IVariantChangeType, __uuidof(IVariantChangeType)); +#endif // #if defined(__IVariantChangeType_INTERFACE_DEFINED__) +#if defined(__IViewObject_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IViewObject, __uuidof(IViewObject)); +#endif // #if defined(__IViewObject_INTERFACE_DEFINED__) +#if defined(__IViewObject2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IViewObject2, __uuidof(IViewObject2)); +#endif // #if defined(__IViewObject2_INTERFACE_DEFINED__) +#if defined(__IViewObjectEx_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IViewObjectEx, __uuidof(IViewObjectEx)); +#endif // #if defined(__IViewObjectEx_INTERFACE_DEFINED__) +#if defined(__IWaitMultiple_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWaitMultiple, __uuidof(IWaitMultiple)); +#endif // #if defined(__IWaitMultiple_INTERFACE_DEFINED__) +#if defined(__IWebBrowser_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWebBrowser, __uuidof(IWebBrowser)); +#endif // #if defined(__IWebBrowser_INTERFACE_DEFINED__) +#if defined(__IWebBrowser2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWebBrowser2, __uuidof(IWebBrowser2)); +#endif // #if defined(__IWebBrowser2_INTERFACE_DEFINED__) +#if defined(__IWebBrowserApp_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWebBrowserApp, __uuidof(IWebBrowserApp)); +#endif // #if defined(__IWebBrowserApp_INTERFACE_DEFINED__) +#if defined(__IWinInetHttpInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWinInetHttpInfo, __uuidof(IWinInetHttpInfo)); +#endif // #if defined(__IWinInetHttpInfo_INTERFACE_DEFINED__) +#if defined(__IWinInetInfo_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWinInetInfo, __uuidof(IWinInetInfo)); +#endif // #if defined(__IWinInetInfo_INTERFACE_DEFINED__) +#if defined(__IWindowForBindingUI_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWindowForBindingUI, __uuidof(IWindowForBindingUI)); +#endif // #if defined(__IWindowForBindingUI_INTERFACE_DEFINED__) +#if defined(__IWrappedProtocol_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IWrappedProtocol, __uuidof(IWrappedProtocol)); +#endif // #if defined(__IWrappedProtocol_INTERFACE_DEFINED__) +#if defined(__IXMLAttribute_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLAttribute, __uuidof(IXMLAttribute)); +#endif // #if defined(__IXMLAttribute_INTERFACE_DEFINED__) +#if defined(__IXMLDOMAttribute_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMAttribute, __uuidof(IXMLDOMAttribute)); +#endif // #if defined(__IXMLDOMAttribute_INTERFACE_DEFINED__) +#if defined(__IXMLDOMCDATASection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMCDATASection, __uuidof(IXMLDOMCDATASection)); +#endif // #if defined(__IXMLDOMCDATASection_INTERFACE_DEFINED__) +#if defined(__IXMLDOMCharacterData_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMCharacterData, __uuidof(IXMLDOMCharacterData)); +#endif // #if defined(__IXMLDOMCharacterData_INTERFACE_DEFINED__) +#if defined(__IXMLDOMComment_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMComment, __uuidof(IXMLDOMComment)); +#endif // #if defined(__IXMLDOMComment_INTERFACE_DEFINED__) +#if defined(__IXMLDOMDocument_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMDocument, __uuidof(IXMLDOMDocument)); +#endif // #if defined(__IXMLDOMDocument_INTERFACE_DEFINED__) +#if defined(__IXMLDOMDocumentFragment_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMDocumentFragment, __uuidof(IXMLDOMDocumentFragment)); +#endif // #if defined(__IXMLDOMDocumentFragment_INTERFACE_DEFINED__) +#if defined(__IXMLDOMDocumentType_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMDocumentType, __uuidof(IXMLDOMDocumentType)); +#endif // #if defined(__IXMLDOMDocumentType_INTERFACE_DEFINED__) +#if defined(__IXMLDOMElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMElement, __uuidof(IXMLDOMElement)); +#endif // #if defined(__IXMLDOMElement_INTERFACE_DEFINED__) +#if defined(__IXMLDOMEntity_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMEntity, __uuidof(IXMLDOMEntity)); +#endif // #if defined(__IXMLDOMEntity_INTERFACE_DEFINED__) +#if defined(__IXMLDOMEntityReference_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMEntityReference, __uuidof(IXMLDOMEntityReference)); +#endif // #if defined(__IXMLDOMEntityReference_INTERFACE_DEFINED__) +#if defined(__IXMLDOMImplementation_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMImplementation, __uuidof(IXMLDOMImplementation)); +#endif // #if defined(__IXMLDOMImplementation_INTERFACE_DEFINED__) +#if defined(__IXMLDOMNamedNodeMap_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMNamedNodeMap, __uuidof(IXMLDOMNamedNodeMap)); +#endif // #if defined(__IXMLDOMNamedNodeMap_INTERFACE_DEFINED__) +#if defined(__IXMLDOMNode_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMNode, __uuidof(IXMLDOMNode)); +#endif // #if defined(__IXMLDOMNode_INTERFACE_DEFINED__) +#if defined(__IXMLDOMNodeList_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMNodeList, __uuidof(IXMLDOMNodeList)); +#endif // #if defined(__IXMLDOMNodeList_INTERFACE_DEFINED__) +#if defined(__IXMLDOMNotation_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMNotation, __uuidof(IXMLDOMNotation)); +#endif // #if defined(__IXMLDOMNotation_INTERFACE_DEFINED__) +#if defined(__IXMLDOMParseError_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMParseError, __uuidof(IXMLDOMParseError)); +#endif // #if defined(__IXMLDOMParseError_INTERFACE_DEFINED__) +#if defined(__IXMLDOMProcessingInstruction_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMProcessingInstruction, __uuidof(IXMLDOMProcessingInstruction)); +#endif // #if defined(__IXMLDOMProcessingInstruction_INTERFACE_DEFINED__) +#if defined(__IXMLDOMText_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDOMText, __uuidof(IXMLDOMText)); +#endif // #if defined(__IXMLDOMText_INTERFACE_DEFINED__) +#if defined(__IXMLDSOControl_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDSOControl, __uuidof(IXMLDSOControl)); +#endif // #if defined(__IXMLDSOControl_INTERFACE_DEFINED__) +#if defined(__IXMLDocument_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDocument, __uuidof(IXMLDocument)); +#endif // #if defined(__IXMLDocument_INTERFACE_DEFINED__) +#if defined(__IXMLDocument2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLDocument2, __uuidof(IXMLDocument2)); +#endif // #if defined(__IXMLDocument2_INTERFACE_DEFINED__) +#if defined(__IXMLElement_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLElement, __uuidof(IXMLElement)); +#endif // #if defined(__IXMLElement_INTERFACE_DEFINED__) +#if defined(__IXMLElement2_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLElement2, __uuidof(IXMLElement2)); +#endif // #if defined(__IXMLElement2_INTERFACE_DEFINED__) +#if defined(__IXMLElementCollection_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLElementCollection, __uuidof(IXMLElementCollection)); +#endif // #if defined(__IXMLElementCollection_INTERFACE_DEFINED__) +#if defined(__IXMLError_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLError, __uuidof(IXMLError)); +#endif // #if defined(__IXMLError_INTERFACE_DEFINED__) +#if defined(__IXMLHttpRequest_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXMLHttpRequest, __uuidof(IXMLHttpRequest)); +#endif // #if defined(__IXMLHttpRequest_INTERFACE_DEFINED__) +#if defined(__IXTLRuntime_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(IXTLRuntime, __uuidof(IXTLRuntime)); +#endif // #if defined(__IXTLRuntime_INTERFACE_DEFINED__) +#if defined(__OLEDBSimpleProvider_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(OLEDBSimpleProvider, __uuidof(OLEDBSimpleProvider)); +#endif // #if defined(__OLEDBSimpleProvider_INTERFACE_DEFINED__) +#if defined(__OLEDBSimpleProviderListener_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(OLEDBSimpleProviderListener, __uuidof(OLEDBSimpleProviderListener)); +#endif // #if defined(__OLEDBSimpleProviderListener_INTERFACE_DEFINED__) +#if defined(__XMLDOMDocumentEvents_INTERFACE_DEFINED__) +_COM_SMARTPTR_TYPEDEF(XMLDOMDocumentEvents, __uuidof(XMLDOMDocumentEvents)); +#endif // #if defined(__XMLDOMDocumentEvents_INTERFACE_DEFINED__) + +// coclass smart pointer defs +#if defined(__DOMFreeThreadedDocument_FWD_DEFINED__) +_COM_SMARTPTR_TYPEDEF(DOMFreeThreadedDocument, __uuidof(DOMFreeThreadedDocument)); +#endif // #if defined(__DOMFreeThreadedDocument_FWD_DEFINED__) +#if defined(__XMLDSOControl_FWD_DEFINED__) +_COM_SMARTPTR_TYPEDEF(XMLDSOControl, __uuidof(XMLDSOControl)); +#endif // #if defined(__XMLDSOControl_FWD_DEFINED__) +#if defined(__XMLDocument_FWD_DEFINED__) +_COM_SMARTPTR_TYPEDEF(XMLDocument, __uuidof(XMLDocument)); +#endif // #if defined(__XMLDocument_FWD_DEFINED__) +#if defined(__XMLHTTPRequest_FWD_DEFINED__) +_COM_SMARTPTR_TYPEDEF(XMLHTTPRequest, __uuidof(XMLHTTPRequest)); +#endif // #if defined(__XMLHTTPRequest_FWD_DEFINED__) +#endif /* RC_INVOKED */ +#endif /* _INC_COMDEFSP */ + diff --git a/test_data/lots_of_files/comip.h b/test_data/lots_of_files/comip.h new file mode 100644 index 0000000..829df79 --- /dev/null +++ b/test_data/lots_of_files/comip.h @@ -0,0 +1,993 @@ +/*** +* comip.h - Native C++ compiler COM support - COM interface pointers header +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifdef _M_CEE_PURE +#error comip.h header cannot be included under /clr:safe or /clr:pure +#endif + +#if !defined(_INC_COMIP) +#define _INC_COMIP + +#include <ole2.h> +#include <malloc.h> + +#include <comutil.h> + +#pragma warning(push) +#pragma warning(disable: 4290) + +#pragma push_macro("new") +#undef new + +#include <new.h> + +class _com_error; + +void __stdcall _com_issue_error(HRESULT); +struct __declspec(uuid("00000000-0000-0000-c000-000000000046")) IUnknown; + +// Provide Interface to IID association +// +template<typename _Interface, const IID* _IID /*= &__uuidof(_Interface)*/> +class _com_IIID { +public: + typedef _Interface Interface; + + static _Interface* GetInterfacePtr() throw() + { + return NULL; + } + + static _Interface& GetInterface() throw() + { + return *GetInterfacePtr(); + } + + static const IID& GetIID() throw() + { + return *_IID; + } +}; + +template<typename _IIID> class _com_ptr_t { +public: + // Declare interface type so that the type may be available outside + // the scope of this template. + // + typedef _IIID ThisIIID; + typedef typename _IIID::Interface Interface; + + // When the compiler supports references in template parameters, + // _CLSID will be changed to a reference. To avoid conversion + // difficulties this function should be used to obtain the + // CLSID. + // + static const IID& GetIID() throw() + { + return ThisIIID::GetIID(); + } + + // Constructs a smart-pointer from any other smart pointer. + // + template<typename _OtherIID> _com_ptr_t(const _com_ptr_t<_OtherIID>& p) + : m_pInterface(NULL) + { + HRESULT hr = _QueryInterface(p); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + } + + // Constructs a smart-pointer from any IUnknown-based interface pointer. + // + template<typename _InterfaceType> _com_ptr_t(_InterfaceType* p) + : m_pInterface(NULL) + { + HRESULT hr = _QueryInterface(p); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + } + + // Make sure correct ctor is called + // + template<> _com_ptr_t(_In_ LPSTR str) + { + new(this) _com_ptr_t(static_cast<LPCSTR> (str), NULL); + } + + // Make sure correct ctor is called + // + template<> _com_ptr_t(_In_ LPWSTR str) + { + new(this) _com_ptr_t(static_cast<LPCWSTR> (str), NULL); + } + + // Disable conversion using _com_ptr_t* specialization of + // template<typename _InterfaceType> _com_ptr_t(_InterfaceType* p) + // + template<> explicit _com_ptr_t(_com_ptr_t* p) + : m_pInterface(NULL) + { + if (p == NULL) { + _com_issue_error(E_POINTER); + } + else { + m_pInterface = p->m_pInterface; + AddRef(); + } + } + + // Default constructor. + // + _com_ptr_t() throw() + : m_pInterface(NULL) + { + } + + // This constructor is provided to allow NULL assignment. It will issue + // an error if any value other than null is assigned to the object. + // + _com_ptr_t(int null) + : m_pInterface(NULL) + { + if (null != 0) { + _com_issue_error(E_POINTER); + } + } + +#if defined(_NATIVE_NULLPTR_SUPPORTED) && !defined(_DO_NOT_USE_NULLPTR_IN_COM_PTR_T) + + // This constructor is provided to allow nullptr assignment. + _com_ptr_t(decltype(__nullptr)) : m_pInterface(NULL) { } + +#endif // defined(_NATIVE_NULLPTR_SUPPORTED) && !defined(_DO_NOT_USE_NULLPTR_IN_COM_PTR_T) + + // Copy the pointer and AddRef(). + // + _com_ptr_t(const _com_ptr_t& cp) throw() + : m_pInterface(cp.m_pInterface) + { + _AddRef(); + } + + // Saves the interface. + // + template<> _com_ptr_t(Interface* pInterface) throw() + : m_pInterface(pInterface) + { + _AddRef(); + } + + // Copies the pointer. If fAddRef is TRUE, the interface will + // be AddRef()ed. + // + _com_ptr_t(Interface* pInterface, bool fAddRef) throw() + : m_pInterface(pInterface) + { + if (fAddRef) { + _AddRef(); + } + } + + // Construct a pointer for a _variant_t object. + // + _com_ptr_t(const _variant_t& varSrc) + : m_pInterface(NULL) + { + HRESULT hr = QueryStdInterfaces(varSrc); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + } + + // Calls CoCreateClass with the provided CLSID. + // + explicit _com_ptr_t(const CLSID& clsid, IUnknown* pOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) + : m_pInterface(NULL) + { + HRESULT hr = CreateInstance(clsid, pOuter, dwClsContext); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + } + + // Calls CoCreateClass with the provided CLSID retrieved from + // the string. + // + explicit _com_ptr_t(LPCWSTR str, IUnknown* pOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) + : m_pInterface(NULL) + { + HRESULT hr = CreateInstance(str, pOuter, dwClsContext); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + } + + // Calls CoCreateClass with the provided SBCS CLSID retrieved from + // the string. + // + explicit _com_ptr_t(LPCSTR str, IUnknown* pOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) + : m_pInterface(NULL) + { + HRESULT hr = CreateInstance(str, pOuter, dwClsContext); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + } + + // Queries for interface. + // + template<typename _OtherIID> _com_ptr_t& operator=(const _com_ptr_t<_OtherIID>& p) + { + HRESULT hr = _QueryInterface(p); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + + return *this; + } + + // Queries for interface. + // + template<typename _InterfaceType> _com_ptr_t& operator=(_InterfaceType* p) + { + HRESULT hr = _QueryInterface(p); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + + return *this; + } + + // Saves the interface. + // + template<> _com_ptr_t& operator=(Interface* pInterface) throw() + { + if (m_pInterface != pInterface) { + Interface* pOldInterface = m_pInterface; + + m_pInterface = pInterface; + + _AddRef(); + + if (pOldInterface != NULL) { + pOldInterface->Release(); + } + } + + return *this; + } + + // Copies and AddRef()'s the interface. + // + _com_ptr_t& operator=(const _com_ptr_t& cp) throw() + { + return operator=(cp.m_pInterface); + } + + // This operator is provided to permit the assignment of NULL to the class. + // It will issue an error if any value other than NULL is assigned to it. + // + _com_ptr_t& operator=(int null) + { + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return operator=(reinterpret_cast<Interface*>(NULL)); + } + + // Construct a pointer for a _variant_t object. + // + _com_ptr_t& operator=(const _variant_t& varSrc) + { + HRESULT hr = QueryStdInterfaces(varSrc); + + if (FAILED(hr) && (hr != E_NOINTERFACE)) { + _com_issue_error(hr); + } + + return *this; + } + + // If we still have an interface then Release() it. The interface + // may be NULL if Detach() has previously been called, or if it was + // never set. + // + ~_com_ptr_t() throw() + { + _Release(); + } + + // Saves/sets the interface without AddRef()ing. This call + // will release any previously acquired interface. + // + void Attach(Interface* pInterface) throw() + { + _Release(); + m_pInterface = pInterface; + } + + // Saves/sets the interface only AddRef()ing if fAddRef is TRUE. + // This call will release any previously acquired interface. + // + void Attach(Interface* pInterface, bool fAddRef) throw() + { + _Release(); + m_pInterface = pInterface; + + if (fAddRef) { + if (pInterface == NULL) { + _com_issue_error(E_POINTER); + } + else { + pInterface->AddRef(); + } + } + } + + // Simply NULL the interface pointer so that it isn't Released()'ed. + // + Interface* Detach() throw() + { + Interface* const old = m_pInterface; + m_pInterface = NULL; + return old; + } + + // Return the interface. This value may be NULL. + // + operator Interface*() const throw() + { + return m_pInterface; + } + + // Queries for the unknown and return it + // Provides minimal level error checking before use. + // + operator Interface&() const + { + if (m_pInterface == NULL) { + _com_issue_error(E_POINTER); + } + + return *m_pInterface; + } + + // Allows an instance of this class to act as though it were the + // actual interface. Also provides minimal error checking. + // + Interface& operator*() const + { + if (m_pInterface == NULL) { + _com_issue_error(E_POINTER); + } + + return *m_pInterface; + } + + // Returns the address of the interface pointer contained in this + // class. This is useful when using the COM/OLE interfaces to create + // this interface. + // + Interface** operator&() throw() + { + _Release(); + m_pInterface = NULL; + return &m_pInterface; + } + + // Allows this class to be used as the interface itself. + // Also provides simple error checking. + // + Interface* operator->() const + { + if (m_pInterface == NULL) { + _com_issue_error(E_POINTER); + } + + return m_pInterface; + } + + // This operator is provided so that simple boolean expressions will + // work. For example: "if (p) ...". + // Returns TRUE if the pointer is not NULL. + // + operator bool() const throw() + { + return m_pInterface != NULL; + } + + // Compare two smart pointers + // + template<typename _OtherIID> bool operator==(const _com_ptr_t<_OtherIID>& p) const + { + return _CompareUnknown(p) == 0; + } + + // Compare two pointers + // + template<typename _InterfaceType> bool operator==(_InterfaceType* p) const + { + return _CompareUnknown(p) == 0; + } + + // Compare with other interface + // + template<> bool operator==(Interface* p) const + { + return (m_pInterface == p) + ? true + : _CompareUnknown(p) == 0; + } + + // Compare two smart pointers + // + template<> bool operator==(const _com_ptr_t& p) const throw() + { + return operator==(p.m_pInterface); + } + + // For comparison to NULL + // + bool operator==(int null) const + { + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return m_pInterface == NULL; + } + + // Compare two smart pointers + // + template<typename _OtherIID> bool operator!=(const _com_ptr_t<_OtherIID>& p) const + { + return !(operator==(p)); + } + + // Compare two pointers + // + template<typename _InterfaceType> bool operator!=(_InterfaceType* p) const + { + return !(operator==(p)); + } + + // For comparison to NULL + // + bool operator!=(int null) const + { + return !(operator==(null)); + } + + // Compare two smart pointers + // + template<typename _OtherIID> bool operator<(const _com_ptr_t<_OtherIID>& p) const + { + return _CompareUnknown(p) < 0; + } + + // Compare two pointers + // + template<typename _InterfaceType> bool operator<(_InterfaceType* p) const + { + return _CompareUnknown(p) < 0; + } + + // Compare two smart pointers + // + template<typename _OtherIID> bool operator>(const _com_ptr_t<_OtherIID>& p) const + { + return _CompareUnknown(p) > 0; + } + + // Compare two pointers + // + template<typename _InterfaceType> bool operator>(_InterfaceType* p) const + { + return _CompareUnknown(p) > 0; + } + + // Compare two smart pointers + // + template<typename _OtherIID> bool operator<=(const _com_ptr_t<_OtherIID>& p) const + { + return _CompareUnknown(p) <= 0; + } + + // Compare two pointers + // + template<typename _InterfaceType> bool operator<=(_InterfaceType* p) const + { + return _CompareUnknown(p) <= 0; + } + + // Compare two smart pointers + // + template<typename _OtherIID> bool operator>=(const _com_ptr_t<_OtherIID>& p) const + { + return _CompareUnknown(p) >= 0; + } + + // Compare two pointers + // + template<typename _InterfaceType> bool operator>=(_InterfaceType* p) const + { + return _CompareUnknown(p) >= 0; + } + + // Provides error-checking Release()ing of this interface. + // + void Release() + { + if (m_pInterface == NULL) { + _com_issue_error(E_POINTER); + } + else { + m_pInterface->Release(); + m_pInterface = NULL; + } + } + + // Provides error-checking AddRef()ing of this interface. + // + void AddRef() + { + if (m_pInterface == NULL) { + _com_issue_error(E_POINTER); + } + else { + m_pInterface->AddRef(); + } + } + + // Another way to get the interface pointer without casting. + // + Interface* GetInterfacePtr() const throw() + { + return m_pInterface; + } + + // Another way to get the interface pointer without casting. + // Use for [in, out] parameter passing + Interface*& GetInterfacePtr() throw() + { + return m_pInterface; + } + + // Loads an interface for the provided CLSID. + // Returns an HRESULT. Any previous interface is unconditionally released. + // + HRESULT CreateInstance(const CLSID& rclsid, IUnknown* pOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) throw() + { + HRESULT hr; + + _Release(); + + if (dwClsContext & (CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER)) { + IUnknown* pIUnknown; + hr = CoCreateInstance(rclsid, pOuter, dwClsContext, __uuidof(IUnknown), reinterpret_cast<void**>(&pIUnknown)); + + if (SUCCEEDED(hr)) { + hr = OleRun(pIUnknown); + + if (SUCCEEDED(hr)) { + hr = pIUnknown->QueryInterface(GetIID(), reinterpret_cast<void**>(&m_pInterface)); + } + + pIUnknown->Release(); + } + } + else { + hr = CoCreateInstance(rclsid, pOuter, dwClsContext, GetIID(), reinterpret_cast<void**>(&m_pInterface)); + } + + if (FAILED(hr)) { + // just in case refcount = 0 and dtor gets called + m_pInterface = NULL; + } + + return hr; + } + + // Creates the class specified by clsidString. clsidString may + // contain a class id, or a prog id string. + // + HRESULT CreateInstance(LPCWSTR clsidString, IUnknown* pOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) throw() + { + if (clsidString == NULL) { + return E_INVALIDARG; + } + + CLSID clsid; + HRESULT hr; + + if (clsidString[0] == L'{') { + hr = CLSIDFromString(const_cast<LPWSTR> (clsidString), &clsid); + } + else { + hr = CLSIDFromProgID(const_cast<LPWSTR> (clsidString), &clsid); + } + + if (FAILED(hr)) { + return hr; + } + + return CreateInstance(clsid, pOuter, dwClsContext); + } + + // Creates the class specified by SBCS clsidString. clsidString may + // contain a class id, or a prog id string. + // + HRESULT CreateInstance(LPCSTR clsidStringA, IUnknown* pOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) throw() + { + if (clsidStringA == NULL) { + return E_INVALIDARG; + } + + int size = lstrlenA(clsidStringA) + 1; + int destSize = MultiByteToWideChar(CP_ACP, 0, clsidStringA, size, NULL, 0); + + if (destSize == 0) { + return HRESULT_FROM_WIN32(GetLastError()); + } + + LPWSTR clsidStringW; + clsidStringW = static_cast<LPWSTR>(_malloca(destSize * sizeof(WCHAR))); + + if (clsidStringW == NULL) { + return E_OUTOFMEMORY; + } + + if (MultiByteToWideChar(CP_ACP, 0, clsidStringA, size, clsidStringW, destSize) == 0) { + _freea(clsidStringW); + return HRESULT_FROM_WIN32(GetLastError()); + } + + HRESULT hr=CreateInstance(clsidStringW, pOuter, dwClsContext); + _freea(clsidStringW); + return hr; + } + + // Attach to the active object specified by rclsid. + // Any previous interface is released. + // + HRESULT GetActiveObject(const CLSID& rclsid) throw() + { + _Release(); + + IUnknown* pIUnknown; + + HRESULT hr = ::GetActiveObject(rclsid, NULL, &pIUnknown); + + if (SUCCEEDED(hr)) { + hr = pIUnknown->QueryInterface(GetIID(), reinterpret_cast<void**>(&m_pInterface)); + + pIUnknown->Release(); + } + + if (FAILED(hr)) { + // just in case refcount = 0 and dtor gets called + m_pInterface = NULL; + } + + return hr; + } + + // Attach to the active object specified by clsidString. + // First convert the LPCWSTR to a CLSID. + // + HRESULT GetActiveObject(LPCWSTR clsidString) throw() + { + if (clsidString == NULL) { + return E_INVALIDARG; + } + + CLSID clsid; + HRESULT hr; + + if (clsidString[0] == '{') { + hr = CLSIDFromString(const_cast<LPWSTR> (clsidString), &clsid); + } + else { + hr = CLSIDFromProgID(const_cast<LPWSTR> (clsidString), &clsid); + } + + if (FAILED(hr)) { + return hr; + } + + return GetActiveObject(clsid); + } + + // Attach to the active object specified by clsidStringA. + // First convert the LPCSTR to a LPCWSTR. + // + HRESULT GetActiveObject(LPCSTR clsidStringA) throw() + { + if (clsidStringA == NULL) { + return E_INVALIDARG; + } + + int size = lstrlenA(clsidStringA) + 1; + int destSize = MultiByteToWideChar(CP_ACP, 0, clsidStringA, size, NULL, 0); + + LPWSTR clsidStringW; + __try { + clsidStringW = static_cast<LPWSTR>(_alloca(destSize * sizeof(WCHAR))); + } + __except (GetExceptionCode() == STATUS_STACK_OVERFLOW) { + clsidStringW = NULL; + } + + if (clsidStringW == NULL) { + return E_OUTOFMEMORY; + } + + if (MultiByteToWideChar(CP_ACP, 0, clsidStringA, size, clsidStringW, destSize) == 0) { + return HRESULT_FROM_WIN32(GetLastError()); + } + + return GetActiveObject(clsidStringW); + } + + // Performs the QI for the specified IID and returns it in p. + // As with all QIs, the interface will be AddRef'd. + // + template<typename _InterfaceType> HRESULT QueryInterface(const IID& iid, _InterfaceType*& p) throw () + { + if (m_pInterface != NULL) { + return m_pInterface->QueryInterface(iid, reinterpret_cast<void**>(&p)); + } + + return E_POINTER; + } + + // Performs the QI for the specified IID and returns it in p. + // As with all QIs, the interface will be AddRef'd. + // + template<typename _InterfaceType> HRESULT QueryInterface(const IID& iid, _InterfaceType** p) throw() + { + return QueryInterface(iid, *p); + } + +private: + // The Interface. + // + Interface* m_pInterface; + + // Releases only if the interface is not null. + // The interface is not set to NULL. + // + void _Release() throw() + { + if (m_pInterface != NULL) { + m_pInterface->Release(); + } + } + + // AddRefs only if the interface is not NULL + // + void _AddRef() throw() + { + if (m_pInterface != NULL) { + m_pInterface->AddRef(); + } + } + + // Performs a QI on pUnknown for the interface type returned + // for this class. The interface is stored. If pUnknown is + // NULL, or the QI fails, E_NOINTERFACE is returned and + // _pInterface is set to NULL. + // + template<typename _InterfacePtr> HRESULT _QueryInterface(_InterfacePtr p) throw() + { + HRESULT hr; + + // Can't QI NULL + // + if (p != NULL) { + // Query for this interface + // + Interface* pInterface; + hr = p->QueryInterface(GetIID(), reinterpret_cast<void**>(&pInterface)); + + // Save the interface without AddRef()ing. + // + Attach(SUCCEEDED(hr)? pInterface: NULL); + } + else { + operator=(static_cast<Interface*>(NULL)); + hr = E_NOINTERFACE; + } + + return hr; + } + + // Compares the provided pointer with this by obtaining IUnknown interfaces + // for each pointer and then returning the difference. + // + template<typename _InterfacePtr> int _CompareUnknown(_InterfacePtr p) const + { + IUnknown* pu1 = NULL; + IUnknown* pu2 = NULL; + + if (m_pInterface != NULL) { + HRESULT hr = m_pInterface->QueryInterface(__uuidof(IUnknown), reinterpret_cast<void**>(&pu1)); + + if (FAILED(hr)) { + pu1 = NULL; + _com_issue_error(hr); + } + else { + pu1->Release(); + } + } + + if (p != NULL) { + HRESULT hr = p->QueryInterface(__uuidof(IUnknown), reinterpret_cast<void**>(&pu2)); + + if (FAILED(hr)) { + pu2 = NULL; + _com_issue_error(hr); + } + else { + pu2->Release(); + } + } + + if (pu1 == pu2) + { + return 0; + } + + return (pu1 > pu2) ? 1 : -1; + } + + // Try to extract either IDispatch* or an IUnknown* from + // the VARIANT + // + HRESULT QueryStdInterfaces(const _variant_t& varSrc) throw() + { + if (V_VT(&varSrc) == VT_DISPATCH) { + return _QueryInterface(V_DISPATCH(&varSrc)); + } + + if (V_VT(&varSrc) == VT_UNKNOWN) { + return _QueryInterface(V_UNKNOWN(&varSrc)); + } + + // We have something other than an IUnknown or an IDispatch. + // Can we convert it to either one of these? + // Try IDispatch first + // + VARIANT varDest; + VariantInit(&varDest); + + HRESULT hr = VariantChangeType(&varDest, const_cast<VARIANT*>(static_cast<const VARIANT*>(&varSrc)), 0, VT_DISPATCH); + if (SUCCEEDED(hr)) { + hr = _QueryInterface(V_DISPATCH(&varDest)); + } + + if (hr == E_NOINTERFACE) { + // That failed ... so try IUnknown + // + VariantInit(&varDest); + hr = VariantChangeType(&varDest, const_cast<VARIANT*>(static_cast<const VARIANT*>(&varSrc)), 0, VT_UNKNOWN); + if (SUCCEEDED(hr)) { + hr = _QueryInterface(V_UNKNOWN(&varDest)); + } + } + + VariantClear(&varDest); + return hr; + } +}; + +// Reverse comparison operators for _com_ptr_t +// +template<typename _InterfaceType> bool operator==(int null, const _com_ptr_t<_InterfaceType>& p) +{ + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return p == NULL; +} + +template<typename _Interface, typename _InterfacePtr> bool operator==(_Interface* i, const _com_ptr_t<_InterfacePtr>& p) +{ + return p == i; +} + +template<typename _Interface> bool operator!=(int null, const _com_ptr_t<_Interface>& p) +{ + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return p != NULL; +} + +template<typename _Interface, typename _InterfacePtr> bool operator!=(_Interface* i, const _com_ptr_t<_InterfacePtr>& p) +{ + return p != i; +} + +template<typename _Interface> bool operator<(int null, const _com_ptr_t<_Interface>& p) +{ + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return p > NULL; +} + +template<typename _Interface, typename _InterfacePtr> bool operator<(_Interface* i, const _com_ptr_t<_InterfacePtr>& p) +{ + return p > i; +} + +template<typename _Interface> bool operator>(int null, const _com_ptr_t<_Interface>& p) +{ + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return p < NULL; +} + +template<typename _Interface, typename _InterfacePtr> bool operator>(_Interface* i, const _com_ptr_t<_InterfacePtr>& p) +{ + return p < i; +} + +template<typename _Interface> bool operator<=(int null, const _com_ptr_t<_Interface>& p) +{ + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return p >= NULL; +} + +template<typename _Interface, typename _InterfacePtr> bool operator<=(_Interface* i, const _com_ptr_t<_InterfacePtr>& p) +{ + return p >= i; +} + +template<typename _Interface> bool operator>=(int null, const _com_ptr_t<_Interface>& p) +{ + if (null != 0) { + _com_issue_error(E_POINTER); + } + + return p <= NULL; +} + +template<typename _Interface, typename _InterfacePtr> bool operator>=(_Interface* i, const _com_ptr_t<_InterfacePtr>& p) +{ + return p <= i; +} + +#pragma pop_macro("new") +#pragma warning(pop) + +#endif // _INC_COMIP diff --git a/test_data/lots_of_files/complex.h b/test_data/lots_of_files/complex.h new file mode 100644 index 0000000..6b185cc --- /dev/null +++ b/test_data/lots_of_files/complex.h @@ -0,0 +1,562 @@ +/*** +*complex.h - definitions and declarations for complex math library +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains constant definitions and external subroutine +* declarations for the complex math subroutine library. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _COMPLEX +#define _COMPLEX + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define _DCOMPLEX_(re, im) _Cbuild(re, im) +#define _FCOMPLEX_(re, im) _FCbuild(re, im) +#define _LCOMPLEX_(re, im) _LCbuild(re, im) + +#ifndef _C_COMPLEX_T +#define _C_COMPLEX_T + +typedef struct _C_double_complex +{ /* double complex */ + double _Val[2]; +} _C_double_complex; + +typedef struct _C_float_complex +{ /* float complex */ + float _Val[2]; +} _C_float_complex; + +typedef struct _C_ldouble_complex +{ /* long double complex */ + long double _Val[2]; +} _C_ldouble_complex; + +#endif /* _C_COMPLEX_T */ + +typedef _C_double_complex _Dcomplex; +typedef _C_float_complex _Fcomplex; +typedef _C_ldouble_complex _Lcomplex; + +#define _Complex_I _FCbuild(0.0F, 1.0F) + +_CRTIMP double __cdecl cimag(_In_ _Dcomplex _Z); +_CRTIMP double __cdecl creal(_In_ _Dcomplex _Z); +_CRTIMP float __cdecl cimagf(_In_ _Fcomplex _Z); +_CRTIMP float __cdecl crealf(_In_ _Fcomplex _Z); +_CRTIMP long double __cdecl cimagl(_In_ _Lcomplex _Z); +_CRTIMP long double __cdecl creall(_In_ _Lcomplex _Z); + +/* MACROS */ + +#define I _Complex_I + +/* FUNCTIONS */ +_CRTIMP double __cdecl cabs(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl cacos(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl cacosh(_In_ _Dcomplex _Z); +_CRTIMP double __cdecl carg(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl casin(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl casinh(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl catan(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl catanh(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl ccos(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl ccosh(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl cexp(_In_ _Dcomplex _Z); +_CRTIMP double __cdecl cimag(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl clog(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl clog10(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl conj(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl cpow(_In_ _Dcomplex _X, _In_ _Dcomplex _Y); +_CRTIMP _Dcomplex __cdecl cproj(_In_ _Dcomplex _Z); +_CRTIMP double __cdecl creal(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl csin(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl csinh(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl csqrt(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl ctan(_In_ _Dcomplex _Z); +_CRTIMP _Dcomplex __cdecl ctanh(_In_ _Dcomplex _Z); +_CRTIMP double __cdecl norm(_In_ _Dcomplex _Z); + +_CRTIMP float __cdecl cabsf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl cacosf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl cacoshf(_In_ _Fcomplex _Z); +_CRTIMP float __cdecl cargf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl casinf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl casinhf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl catanf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl catanhf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl ccosf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl ccoshf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl cexpf(_In_ _Fcomplex _Z); +_CRTIMP float __cdecl cimagf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl clogf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl clog10f(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl conjf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl cpowf(_In_ _Fcomplex _X, _In_ _Fcomplex _Y); +_CRTIMP _Fcomplex __cdecl cprojf(_In_ _Fcomplex _Z); +_CRTIMP float __cdecl crealf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl csinf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl csinhf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl csqrtf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl ctanf(_In_ _Fcomplex _Z); +_CRTIMP _Fcomplex __cdecl ctanhf(_In_ _Fcomplex _Z); +_CRTIMP float __cdecl normf(_In_ _Fcomplex _Z); + +_CRTIMP long double __cdecl cabsl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl cacosl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl cacoshl(_In_ _Lcomplex _Z); +_CRTIMP long double __cdecl cargl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl casinl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl casinhl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl catanl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl catanhl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl ccosl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl ccoshl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl cexpl(_In_ _Lcomplex _Z); +_CRTIMP long double __cdecl cimagl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl clogl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl clog10l(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl conjl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl cpowl(_In_ _Lcomplex _X, _In_ _Lcomplex _Y); +_CRTIMP _Lcomplex __cdecl cprojl(_In_ _Lcomplex _Z); +_CRTIMP long double __cdecl creall(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl csinl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl csinhl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl csqrtl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl ctanl(_In_ _Lcomplex _Z); +_CRTIMP _Lcomplex __cdecl ctanhl(_In_ _Lcomplex _Z); +_CRTIMP long double __cdecl norml(_In_ _Lcomplex _Z); + +_CRTIMP _Dcomplex __cdecl _Cbuild(_In_ double _Re, _In_ double _Im); +_CRTIMP _Dcomplex __cdecl _Cmulcc(_In_ _Dcomplex _X, _In_ _Dcomplex _Y); +_CRTIMP _Dcomplex __cdecl _Cmulcr(_In_ _Dcomplex _X, _In_ double _Y); + +_CRTIMP _Fcomplex __cdecl _FCbuild(_In_ float _Re, _In_ float _Im); +_CRTIMP _Fcomplex __cdecl _FCmulcc(_In_ _Fcomplex _X, _In_ _Fcomplex _Y); +_CRTIMP _Fcomplex __cdecl _FCmulcr(_In_ _Fcomplex _X, _In_ float _Y); + +_CRTIMP _Lcomplex __cdecl _LCbuild(_In_ long double _Re, _In_ long double _Im); +_CRTIMP _Lcomplex __cdecl _LCmulcc(_In_ _Lcomplex _X, _In_ _Lcomplex _Y); +_CRTIMP _Lcomplex __cdecl _LCmulcr(_In_ _Lcomplex _X, _In_ long double _Y); + +#ifdef __cplusplus +} + +extern "C++" +{ + // double complex OVERLOADS + inline _Dcomplex __CRTDECL acos(_In_ _Dcomplex _X) throw() + { // compute cacos + return (cacos(_X)); + } + + inline _Dcomplex __CRTDECL acosh(_In_ _Dcomplex _X) throw() + { // compute cacosh + return (cacosh(_X)); + } + + inline _Dcomplex __CRTDECL asin(_In_ _Dcomplex _X) throw() + { // compute casin + return (casin(_X)); + } + + inline _Dcomplex __CRTDECL asinh(_In_ _Dcomplex _X) throw() + { // compute casinh + return (casinh(_X)); + } + + inline _Dcomplex __CRTDECL atan(_In_ _Dcomplex _X) throw() + { // compute catan + return (catan(_X)); + } + + inline _Dcomplex __CRTDECL atanh(_In_ _Dcomplex _X) throw() + { // compute catanh + return (catanh(_X)); + } + + inline _Dcomplex __CRTDECL cos(_In_ _Dcomplex _X) throw() + { // compute ccos + return (ccos(_X)); + } + + inline _Dcomplex __CRTDECL cosh(_In_ _Dcomplex _X) throw() + { // compute ccosh + return (ccosh(_X)); + } + + inline _Dcomplex __CRTDECL proj(_In_ _Dcomplex _X) throw() + { // compute cproj + return (cproj(_X)); + } + + inline _Dcomplex __CRTDECL exp(_In_ _Dcomplex _X) throw() + { // compute cexp + return (cexp(_X)); + } + + inline _Dcomplex __CRTDECL log(_In_ _Dcomplex _X) throw() + { // compute clog + return (clog(_X)); + } + + inline _Dcomplex __CRTDECL log10(_In_ _Dcomplex _X) throw() + { // compute clog10 + return (clog10(_X)); + } + + inline _Dcomplex __CRTDECL pow(_In_ _Dcomplex _X, _In_ _Dcomplex _Y) throw() + { // compute cpow + return (cpow(_X, _Y)); + } + + inline _Dcomplex __CRTDECL sin(_In_ _Dcomplex _X) throw() + { // compute csin + return (csin(_X)); + } + + inline _Dcomplex __CRTDECL sinh(_In_ _Dcomplex _X) throw() + { // compute csinh + return (csinh(_X)); + } + + inline _Dcomplex __CRTDECL sqrt(_In_ _Dcomplex _X) throw() + { // compute csqrt + return (csqrt(_X)); + } + + inline _Dcomplex __CRTDECL tan(_In_ _Dcomplex _X) throw() + { // compute ctan + return (ctan(_X)); + } + + inline _Dcomplex __CRTDECL tanh(_In_ _Dcomplex _X) throw() + { // compute ctanh + return (ctanh(_X)); + } + + inline double __CRTDECL abs(_In_ _Dcomplex _X) throw() + { // compute cabs + return (cabs(_X)); + } + + inline double __CRTDECL arg(_In_ _Dcomplex _X) throw() + { // compute carg + return (carg(_X)); + } + + inline double __CRTDECL imag(_In_ _Dcomplex _X) throw() + { // compute cimag + return (cimag(_X)); + } + + inline double __CRTDECL real(_In_ _Dcomplex _X) throw() + { // compute creal + return (creal(_X)); + } + + // float complex OVERLOADS + inline _Fcomplex __CRTDECL acos(_In_ _Fcomplex _X) throw() + { // compute cacos + return (cacosf(_X)); + } + + inline _Fcomplex __CRTDECL acosh(_In_ _Fcomplex _X) throw() + { // compute cacosh + return (cacoshf(_X)); + } + + inline _Fcomplex __CRTDECL asin(_In_ _Fcomplex _X) throw() + { // compute casin + return (casinf(_X)); + } + + inline _Fcomplex __CRTDECL asinh(_In_ _Fcomplex _X) throw() + { // compute casinh + return (casinhf(_X)); + } + + inline _Fcomplex __CRTDECL atan(_In_ _Fcomplex _X) throw() + { // compute catan + return (catanf(_X)); + } + + inline _Fcomplex __CRTDECL atanh(_In_ _Fcomplex _X) throw() + { // compute catanh + return (catanhf(_X)); + } + + inline _Fcomplex __CRTDECL conj(_In_ _Fcomplex _X) throw() + { // compute conj + return (conjf(_X)); + } + + inline _Fcomplex __CRTDECL cos(_In_ _Fcomplex _X) throw() + { // compute ccos + return (ccosf(_X)); + } + + inline _Fcomplex __CRTDECL cosh(_In_ _Fcomplex _X) throw() + { // compute ccosh + return (ccoshf(_X)); + } + + inline _Fcomplex __CRTDECL cproj(_In_ _Fcomplex _X) throw() + { // compute cproj + return (cprojf(_X)); + } + + inline _Fcomplex __CRTDECL proj(_In_ _Fcomplex _X) throw() + { // compute cproj + return (cprojf(_X)); + } + + inline _Fcomplex __CRTDECL exp(_In_ _Fcomplex _X) throw() + { // compute cexp + return (cexpf(_X)); + } + + inline _Fcomplex __CRTDECL log(_In_ _Fcomplex _X) throw() + { // compute clog + return (clogf(_X)); + } + + inline _Fcomplex __CRTDECL log10(_In_ _Fcomplex _X) throw() + { // compute clog10 + return (clog10f(_X)); + } + + inline float __CRTDECL norm(_In_ _Fcomplex _X) throw() + { // compute norm + return (normf(_X)); + } + + inline _Fcomplex __CRTDECL pow(_In_ _Fcomplex _X, _In_ _Fcomplex _Y) throw() + { // compute cpow + return (cpowf(_X, _Y)); + } + + inline _Fcomplex __CRTDECL sin(_In_ _Fcomplex _X) throw() + { // compute csin + return (csinf(_X)); + } + + inline _Fcomplex __CRTDECL sinh(_In_ _Fcomplex _X) throw() + { // compute csinh + return (csinhf(_X)); + } + + inline _Fcomplex __CRTDECL sqrt(_In_ _Fcomplex _X) throw() + { // compute csqrt + return (csqrtf(_X)); + } + + inline _Fcomplex __CRTDECL tan(_In_ _Fcomplex _X) throw() + { // compute ctan + return (ctanf(_X)); + } + + inline _Fcomplex __CRTDECL tanh(_In_ _Fcomplex _X) throw() + { // compute ctanh + return (ctanhf(_X)); + } + + inline float __CRTDECL abs(_In_ _Fcomplex _X) throw() + { // compute cabs + return (cabsf(_X)); + } + + inline float __CRTDECL arg(_In_ _Fcomplex _X) throw() + { // compute carg + return (cargf(_X)); + } + + inline float __CRTDECL carg(_In_ _Fcomplex _X) throw() + { // compute carg + return (cargf(_X)); + } + + inline float __CRTDECL cimag(_In_ _Fcomplex _X) throw() + { // compute cimag + return (cimagf(_X)); + } + + inline float __CRTDECL creal(_In_ _Fcomplex _X) throw() + { // compute creal + return (crealf(_X)); + } + + inline float __CRTDECL imag(_In_ _Fcomplex _X) throw() + { // compute cimag + return (cimagf(_X)); + } + + inline float __CRTDECL real(_In_ _Fcomplex _X) throw() + { // compute creal + return (crealf(_X)); + } + + // long double complex OVERLOADS + inline _Lcomplex __CRTDECL acos(_In_ _Lcomplex _X) throw() + { // compute cacos + return (cacosl(_X)); + } + + inline _Lcomplex __CRTDECL acosh(_In_ _Lcomplex _X) throw() + { // compute cacosh + return (cacoshl(_X)); + } + + inline _Lcomplex __CRTDECL asin(_In_ _Lcomplex _X) throw() + { // compute casin + return (casinl(_X)); + } + + inline _Lcomplex __CRTDECL asinh(_In_ _Lcomplex _X) throw() + { // compute casinh + return (casinhl(_X)); + } + + inline _Lcomplex __CRTDECL atan(_In_ _Lcomplex _X) throw() + { // compute catan + return (catanl(_X)); + } + + inline _Lcomplex __CRTDECL atanh(_In_ _Lcomplex _X) throw() + { // compute catanh + return (catanhl(_X)); + } + + inline _Lcomplex __CRTDECL conj(_In_ _Lcomplex _X) throw() + { // compute conj + return (conjl(_X)); + } + + inline _Lcomplex __CRTDECL cos(_In_ _Lcomplex _X) throw() + { // compute ccos + return (ccosl(_X)); + } + + inline _Lcomplex __CRTDECL cosh(_In_ _Lcomplex _X) throw() + { // compute ccosh + return (ccoshl(_X)); + } + + inline _Lcomplex __CRTDECL cproj(_In_ _Lcomplex _X) throw() + { // compute cproj + return (cprojl(_X)); + } + + inline _Lcomplex __CRTDECL proj(_In_ _Lcomplex _X) throw() + { // compute cproj + return (cprojl(_X)); + } + + inline _Lcomplex __CRTDECL exp(_In_ _Lcomplex _X) throw() + { // compute cexp + return (cexpl(_X)); + } + + inline _Lcomplex __CRTDECL log(_In_ _Lcomplex _X) throw() + { // compute clog + return (clogl(_X)); + } + + inline _Lcomplex __CRTDECL log10(_In_ _Lcomplex _X) throw() + { // compute clog10 + return (clog10l(_X)); + } + + inline long double __CRTDECL norm(_In_ _Lcomplex _X) throw() + { // compute norm + return (norml(_X)); + } + + inline _Lcomplex __CRTDECL pow(_In_ _Lcomplex _X, _In_ _Lcomplex _Y) throw() + { // compute cpow + return (cpowl(_X, _Y)); + } + + inline _Lcomplex __CRTDECL sin(_In_ _Lcomplex _X) throw() + { // compute csin + return (csinl(_X)); + } + + inline _Lcomplex __CRTDECL sinh(_In_ _Lcomplex _X) throw() + { // compute csinh + return (csinhl(_X)); + } + + inline _Lcomplex __CRTDECL sqrt(_In_ _Lcomplex _X) throw() + { // compute csqrt + return (csqrtl(_X)); + } + + inline _Lcomplex __CRTDECL tan(_In_ _Lcomplex _X) throw() + { // compute ctan + return (ctanl(_X)); + } + + inline _Lcomplex __CRTDECL tanh(_In_ _Lcomplex _X) throw() + { // compute ctanh + return (ctanhl(_X)); + } + + inline long double __CRTDECL abs(_In_ _Lcomplex _X) throw() + { // compute cabs + return (cabsl(_X)); + } + + inline long double __CRTDECL arg(_In_ _Lcomplex _X) throw() + { // compute carg + return (cargl(_X)); + } + + inline long double __CRTDECL carg(_In_ _Lcomplex _X) throw() + { // compute carg + return (cargl(_X)); + } + + inline long double __CRTDECL cimag(_In_ _Lcomplex _X) throw() + { // compute cimag + return (cimagl(_X)); + } + + inline long double __CRTDECL creal(_In_ _Lcomplex _X) throw() + { // compute creal + return (creall(_X)); + } + + inline long double __CRTDECL imag(_In_ _Lcomplex _X) throw() + { // compute cimag + return (cimagl(_X)); + } + + inline long double __CRTDECL real(_In_ _Lcomplex _X) throw() + { // compute creal + return (creall(_X)); + } +} // extern "C++" +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _COMPLEX */ diff --git a/test_data/lots_of_files/comutil.h b/test_data/lots_of_files/comutil.h new file mode 100644 index 0000000..2e37d79 --- /dev/null +++ b/test_data/lots_of_files/comutil.h @@ -0,0 +1,2387 @@ +/*** +* comutil.h - Native C++ compiler COM support - BSTR, VARIANT wrappers header +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifdef _M_CEE_PURE +#error comutil.h header cannot be included under /clr:safe or /clr:pure +#endif + +#if !defined(_INC_COMUTIL) +#define _INC_COMUTIL + +#include <ole2.h> + +#if !defined(_COM_ASSERT) +# if defined(_DEBUG) +# include <assert.h> +# define _COM_ASSERT(x) assert(x) +# else +# define _COM_ASSERT(x) ((void)0) +# endif +#endif + +#if !defined(_SECURE_COMPILER_COM) +/* use secure versions by default if not specified otherwise */ +#define _SECURE_COMPILER_COM 1 +#endif + +#if _SECURE_COMPILER_COM && defined(__GOT_SECURE_LIB__) && __GOT_SECURE_LIB__ >= 200402L + +#include <stdio.h> + +# define _COM_MEMCPY_S(dest, destsize, src, count) memcpy_s(dest, destsize, src, count) +# if defined(UNICODE) +# define _COM_PRINTF_S_1(dest, destsize, format, arg1) swprintf_s(dest, destsize, format, arg1) +# else +# define _COM_PRINTF_S_1(dest, destsize, format, arg1) sprintf_s(dest, destsize, format, arg1) +# endif + +#else + +# define _COM_MEMCPY_S(dest, destsize, src, count) memcpy(dest, src, count) +# define _COM_PRINTF_S_1(dest, destsize, format, arg1) wsprintf(dest, format, arg1) + +#endif + +#pragma warning(push) +#pragma warning(disable: 4290) +#pragma warning(disable: 4310) + +#pragma push_macro("new") +#undef new + +/* Add macros if the macros were not defined. */ +#ifndef S_OK +#define S_OK ((HRESULT)0L) +#endif +#ifndef INTSAFE_E_ARITHMETIC_OVERFLOW +#define INTSAFE_E_ARITHMETIC_OVERFLOW ((HRESULT)0x80070216L) // 0x216 = 534 = ERROR_ARITHMETIC_OVERFLOW +#endif +#ifndef INTSAFE_UINT_MAX +#define INTSAFE_UINT_MAX 0xffffffff +#endif +#ifndef FAILED +#define FAILED(hr) (((HRESULT)(hr)) < 0) +#endif + +class _com_error; + +void __declspec(noreturn) __stdcall _com_issue_error(HRESULT); + +////////////////////////////////////////////////////////////////////////////// +// +// Forward class declarations +// +////////////////////////////////////////////////////////////////////////////// + +class _bstr_t; +class _variant_t; + +////////////////////////////////////////////////////////////////////////////// +// +// Error checking routines +// +////////////////////////////////////////////////////////////////////////////// + +namespace _com_util { + inline void CheckError(HRESULT hr) + { + if (FAILED(hr)) { + _com_issue_error(hr); + } + } + static HRESULT UIntAdd(UINT uAugend, UINT uAddend, UINT *puResult) + { + if((uAugend + uAddend) < uAddend) + { + return INTSAFE_E_ARITHMETIC_OVERFLOW; + } + *puResult = uAugend + uAddend; + return S_OK; + } + + static HRESULT UIntMult(UINT uMultiplicand, UINT uMultiplier, UINT *puResult) + { + ULONGLONG ull64Result = UInt32x32To64(uMultiplicand, uMultiplier); + if(ull64Result <= INTSAFE_UINT_MAX) + { + *puResult = (UINT)ull64Result; + return S_OK; + } + return INTSAFE_E_ARITHMETIC_OVERFLOW; + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Routines for handling conversions between BSTR and char* +// +////////////////////////////////////////////////////////////////////////////// + +namespace _com_util { + // Convert char * to BSTR + // + BSTR __stdcall ConvertStringToBSTR(const char* pSrc) ; + + // Convert BSTR to char * + // + char* __stdcall ConvertBSTRToString(BSTR pSrc) ; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Wrapper class for BSTR +// +////////////////////////////////////////////////////////////////////////////// + +class _bstr_t { +public: + // Constructors + // + _bstr_t() throw(); + _bstr_t(const _bstr_t& s) throw(); + _bstr_t(const char* s) ; + _bstr_t(const wchar_t* s) ; + _bstr_t(const _variant_t& var) ; + _bstr_t(BSTR bstr, bool fCopy) ; + + // Destructor + // + ~_bstr_t() throw(); + + // Assignment operators + // + _bstr_t& operator=(const _bstr_t& s) throw(); + _bstr_t& operator=(const char* s) ; + _bstr_t& operator=(const wchar_t* s) ; + _bstr_t& operator=(const _variant_t& var) ; + + // Operators + // + _bstr_t& operator+=(const _bstr_t& s) ; + _bstr_t operator+(const _bstr_t& s) const ; + + // Friend operators + // + friend _bstr_t operator+(const char* s1, const _bstr_t& s2) ; + friend _bstr_t operator+(const wchar_t* s1, const _bstr_t& s2) ; + + // Extractors + // + operator const wchar_t*() const throw(); + operator wchar_t*() const throw(); + operator const char*() const ; + operator char*() const ; + + // Comparison operators + // + bool operator!() const throw(); + bool operator==(const _bstr_t& str) const throw(); + bool operator!=(const _bstr_t& str) const throw(); + bool operator<(const _bstr_t& str) const throw(); + bool operator>(const _bstr_t& str) const throw(); + bool operator<=(const _bstr_t& str) const throw(); + bool operator>=(const _bstr_t& str) const throw(); + + // Low-level helper functions + // + BSTR copy(bool fCopy = true) const ; + unsigned int length() const throw(); + + // Binary string assign + // + void Assign(BSTR s) ; + + // Get the physical BSTR + // + BSTR& GetBSTR() ; + BSTR* GetAddress() ; + + // Attach to the internal BSTR w/o copying + // + void Attach(BSTR s) ; + + // Detach the internal BSTR + // + BSTR Detach(); + +private: + // Referenced counted wrapper + // + class Data_t { + public: + // Constructors + // + Data_t(const char* s) ; + Data_t(const wchar_t* s) ; + Data_t(BSTR bstr, bool fCopy) ; + Data_t(const _bstr_t& s1, const _bstr_t& s2) ; + + // Reference counting routines + // + unsigned long AddRef() throw(); + unsigned long Release() throw(); + unsigned long RefCount() const throw(); + + // Extractors + // + operator const wchar_t*() const throw(); + operator const char*() const ; + + // Low-level helper functions + // + const wchar_t* GetWString() const throw(); + wchar_t*& GetWString() throw(); + const char* GetString() const ; + + BSTR Copy() const ; + void Assign(BSTR s) ; + void Attach(BSTR s) throw(); + unsigned int Length() const throw(); + int Compare(const Data_t& str) const throw(); + + // Exception agnostic wrapper for new + // + void* operator new(size_t sz); + + private: + BSTR m_wstr; + mutable char* m_str; + unsigned long m_RefCount; + + // Never allow default construction + // + Data_t() throw(); + + // Never allow copy + // + Data_t(const Data_t& s) throw(); + + // Prevent deletes from outside. Release() must be used. + // + ~Data_t() throw(); + + void _Free() throw(); + }; + +private: + // Reference counted representation + // + Data_t* m_Data; + +private: + // Low-level utilities + // + void _AddRef() throw(); + void _Free() throw(); + int _Compare(const _bstr_t& str) const throw(); +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Constructors +// +////////////////////////////////////////////////////////////////////////////// + +// Default constructor +// +inline _bstr_t::_bstr_t() throw() + : m_Data(NULL) +{ +} + +// Copy constructor +// +inline _bstr_t::_bstr_t(const _bstr_t& s) throw() + : m_Data(s.m_Data) +{ + _AddRef(); +} + +// Construct a _bstr_t from a const char* +// +inline _bstr_t::_bstr_t(const char* s) + : m_Data(new Data_t(s)) +{ + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } +} + +// Construct a _bstr_t from a const whar_t* +// +inline _bstr_t::_bstr_t(const wchar_t* s) + : m_Data(new Data_t(s)) +{ + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } +} + +// Construct a _bstr_t from a BSTR. If fCopy is FALSE, give control of +// data to the _bstr_t without making a new copy. +// +inline _bstr_t::_bstr_t(BSTR bstr, bool fCopy) + : m_Data(new Data_t(bstr, fCopy)) +{ + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } +} + +// Destructor +// +inline _bstr_t::~_bstr_t() throw() +{ + _Free(); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Assignment operators +// +////////////////////////////////////////////////////////////////////////////// + +// Default assign operator +// +inline _bstr_t& _bstr_t::operator=(const _bstr_t& s) throw() +{ + if (this != &s) { + _Free(); + + m_Data = s.m_Data; + _AddRef(); + } + + return *this; +} + +// Assign a const char* to a _bstr_t +// +inline _bstr_t& _bstr_t::operator=(const char* s) +{ + _COM_ASSERT(s == NULL || static_cast<const char*>(*this) != s); + + if (s == NULL || static_cast<const char*>(*this) != s) + { + _Free(); + + m_Data = new Data_t(s); + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } + + return *this; +} + +// Assign a const wchar_t* to a _bstr_t +// +inline _bstr_t& _bstr_t::operator=(const wchar_t* s) +{ + _COM_ASSERT(s == NULL || static_cast<const wchar_t*>(*this) != s); + + if (s == NULL || static_cast<const wchar_t*>(*this) != s) + { + _Free(); + + m_Data = new Data_t(s); + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } + + return *this; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Operators +// +////////////////////////////////////////////////////////////////////////////// + +// Concatenate a _bstr_t onto this _bstr_t +// +inline _bstr_t& _bstr_t::operator+=(const _bstr_t& s) +{ + Data_t* newData = new Data_t(*this, s); + if (newData == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + else { + _Free(); + m_Data = newData; + } + + return *this; +} + +// Return the concatenation of this _bstr_t with another _bstr_t +// +inline _bstr_t _bstr_t::operator+(const _bstr_t& s) const +{ + _bstr_t b = *this; + b += s; + + return b; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Friend Operators +// +////////////////////////////////////////////////////////////////////////////// + +// Return the concatenation of a const char* with a _bstr_t +// +inline _bstr_t operator+(const char* s1, const _bstr_t& s2) +{ + _bstr_t b = s1; + b += s2; + + return b; +} + +// Return the concatenation of a const char* with a _bstr_t +// +inline _bstr_t operator+(const wchar_t* s1, const _bstr_t& s2) +{ + _bstr_t b = s1; + b += s2; + + return b; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Extractors +// +////////////////////////////////////////////////////////////////////////////// + +// Extract a const wchar_t* +// +inline _bstr_t::operator const wchar_t*() const throw() +{ + return (m_Data != NULL) ? m_Data->GetWString() : NULL; +} + +// Extract a wchar_t* +// +inline _bstr_t::operator wchar_t*() const throw() +{ + return const_cast<wchar_t*>((m_Data != NULL) ? m_Data->GetWString() : NULL); +} + +// Extract a const char_t* +// +inline _bstr_t::operator const char*() const +{ + return (m_Data != NULL) ? m_Data->GetString() : NULL; +} + +// Extract a char_t* +// +inline _bstr_t::operator char*() const +{ + return const_cast<char*>((m_Data != NULL) ? m_Data->GetString() : NULL); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Comparison operators +// +////////////////////////////////////////////////////////////////////////////// + +inline bool _bstr_t::operator!() const throw() +{ + return (m_Data != NULL) ? !m_Data->GetWString() : true; +} + +inline bool _bstr_t::operator==(const _bstr_t& str) const throw() +{ + return _Compare(str) == 0; +} + +inline bool _bstr_t::operator!=(const _bstr_t& str) const throw() +{ + return _Compare(str) != 0; +} + +inline bool _bstr_t::operator<(const _bstr_t& str) const throw() +{ + return _Compare(str) < 0; +} + +inline bool _bstr_t::operator>(const _bstr_t& str) const throw() +{ + return _Compare(str) > 0; +} + +inline bool _bstr_t::operator<=(const _bstr_t& str) const throw() +{ + return _Compare(str) <= 0; +} + +inline bool _bstr_t::operator>=(const _bstr_t& str) const throw() +{ + return _Compare(str) >= 0; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Low-level help functions +// +////////////////////////////////////////////////////////////////////////////// + +// Extract a copy of the wrapped BSTR +// +inline BSTR _bstr_t::copy(bool fCopy) const +{ + return (m_Data != NULL) ? (fCopy ? m_Data->Copy() : m_Data->GetWString()) : NULL; +} + +// Return the length of the wrapped BSTR +// +inline unsigned int _bstr_t::length() const throw() +{ + return (m_Data != NULL) ? m_Data->Length() : 0; +} + +// Binary string assign +// +inline void _bstr_t::Assign(BSTR s) +{ + _COM_ASSERT(s == NULL || m_Data == NULL || m_Data->GetWString() != s); + + if (s == NULL || m_Data == NULL || m_Data->GetWString() != s) + { + _Free(); + + m_Data = new Data_t(s, TRUE); + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } +} + +// Get the physical BSTR +// +inline BSTR& _bstr_t::GetBSTR() +{ + if (m_Data == NULL) { + m_Data = new Data_t(0, FALSE); + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } + return m_Data->GetWString(); +} + +// Get the address of the physical BSTR to pass as an 'out'-parameter +// +inline BSTR* _bstr_t::GetAddress() +{ + Attach(0); + return &m_Data->GetWString(); +} + +// Attach to the internal BSTR w/o copying +// +inline void _bstr_t::Attach(BSTR s) +{ + _Free(); + + m_Data = new Data_t(s, FALSE); + if (m_Data == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } +} + +// Detach the internal BSTR +// +inline BSTR _bstr_t::Detach() +{ + _COM_ASSERT(m_Data != NULL && m_Data->RefCount() == 1); + + if (m_Data != NULL && m_Data->RefCount() == 1) { + BSTR b = m_Data->GetWString(); + m_Data->GetWString() = NULL; + _Free(); + return b; + } + else { + _com_issue_error(E_POINTER); + } +} + +// AddRef the BSTR +// +inline void _bstr_t::_AddRef() throw() +{ + if (m_Data != NULL) { + m_Data->AddRef(); + } +} + +// Free the BSTR +// +inline void _bstr_t::_Free() throw() +{ + if (m_Data != NULL) { + m_Data->Release(); + m_Data = NULL; + } +} + +// Compare two _bstr_t objects +// +inline int _bstr_t::_Compare(const _bstr_t& str) const throw() +{ + if (m_Data == str.m_Data) { + return 0; + } + + if (m_Data == NULL) { + if (str.length() == 0) { + return 0; + } + else { + return -1; + } + } + + if (str.m_Data == NULL){ + if (this->length() == 0) { + return 0; + } + else { + return 1; + } + } + + return m_Data->Compare(*str.m_Data); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Reference counted wrapper - Constructors +// +////////////////////////////////////////////////////////////////////////////// + +// Construct a Data_t from a const char* +// +inline _bstr_t::Data_t::Data_t(const char* s) + : m_str(NULL), m_RefCount(1) +{ + m_wstr = _com_util::ConvertStringToBSTR(s); +} + +// Construct a Data_t from a const wchar_t* +// +inline _bstr_t::Data_t::Data_t(const wchar_t* s) + : m_str(NULL), m_RefCount(1) +{ + m_wstr = ::SysAllocString(s); + + if (m_wstr == NULL && s != NULL) { + _com_issue_error(E_OUTOFMEMORY); + } +} + +// Construct a Data_t from a BSTR. If fCopy is FALSE, give control of +// data to the Data_t without doing a SysAllocStringByteLen. +// +inline _bstr_t::Data_t::Data_t(BSTR bstr, bool fCopy) + : m_str(NULL), m_RefCount(1) +{ + if (fCopy && bstr != NULL) { + m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(bstr), + ::SysStringByteLen(bstr)); + + if (m_wstr == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } + else { + m_wstr = bstr; + } +} + +// Construct a Data_t from the concatenation of two _bstr_t objects +// +inline _bstr_t::Data_t::Data_t(const _bstr_t& s1, const _bstr_t& s2) + : m_str(NULL), m_RefCount(1) +{ + const unsigned int l1 = s1.length(); + const unsigned int l2 = s2.length(); + unsigned int l3; + + if (FAILED(_com_util::UIntAdd(l1, l2, &l3)) || + FAILED(_com_util::UIntMult(l3, sizeof(wchar_t), &l3))) + { + _com_issue_error(E_OUTOFMEMORY); + return; + } + + m_wstr = ::SysAllocStringByteLen(NULL, (l1 + l2) * sizeof(wchar_t)); + if (m_wstr == NULL) + { + if (l1 + l2 == 0) + { + return; + } + _com_issue_error(E_OUTOFMEMORY); + return; + } + + const wchar_t* wstr1 = static_cast<const wchar_t*>(s1); + + if (wstr1 != NULL) + { + _COM_MEMCPY_S(m_wstr, (l1 + l2 + 1) * sizeof(wchar_t), wstr1, (l1 + 1) * sizeof(wchar_t)); + } + + const wchar_t* wstr2 = static_cast<const wchar_t*>(s2); + + if (wstr2 != NULL) + { + _COM_MEMCPY_S(m_wstr + l1, (l2 + 1) * sizeof(wchar_t), wstr2, (l2 + 1) * sizeof(wchar_t)); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Reference counted wrapper - reference counting routines +// +////////////////////////////////////////////////////////////////////////////// + +inline unsigned long _bstr_t::Data_t::AddRef() throw() +{ + InterlockedIncrement(reinterpret_cast<long*>(&m_RefCount)); + return m_RefCount; +} + +inline unsigned long _bstr_t::Data_t::Release() throw() +{ + unsigned long cRef = InterlockedDecrement(reinterpret_cast<long*>(&m_RefCount)); + if (cRef == 0) { + delete this; + } + + return cRef; +} + +inline unsigned long _bstr_t::Data_t::RefCount() const throw() +{ + return m_RefCount; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Reference counted wrapper - extractors +// +////////////////////////////////////////////////////////////////////////////// + +// Extract a const wchar_t* +// +inline _bstr_t::Data_t::operator const wchar_t*() const throw() +{ + return m_wstr; +} + +// Extract a const char_t* +// +inline _bstr_t::Data_t::operator const char*() const +{ + return GetString(); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Reference counted wrapper - helper functions +// +////////////////////////////////////////////////////////////////////////////// + +inline const wchar_t* _bstr_t::Data_t::GetWString() const throw() +{ + return m_wstr; +} + +inline wchar_t*& _bstr_t::Data_t::GetWString() throw() +{ + return m_wstr; +} + +inline const char* _bstr_t::Data_t::GetString() const +{ + if (m_str == NULL) { + m_str = _com_util::ConvertBSTRToString(m_wstr); + } + + return m_str; +} + +// Return a copy of the wrapped BSTR +// +inline BSTR _bstr_t::Data_t::Copy() const +{ + if (m_wstr != NULL) { + BSTR bstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(m_wstr), ::SysStringByteLen(m_wstr)); + + if (bstr == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + + return bstr; + } + + return NULL; +} + +inline void _bstr_t::Data_t::Assign(BSTR s) +{ + _Free(); + + if (s != NULL) { + m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(s), ::SysStringByteLen(s)); + m_str = 0; + } +} + +inline void _bstr_t::Data_t::Attach(BSTR s) throw() +{ + _Free(); + + m_wstr = s; + m_str = 0; + m_RefCount = 1; +} + +// Return the length of the wrapper BSTR +// +inline unsigned int _bstr_t::Data_t::Length() const throw() +{ + return m_wstr ? ::SysStringLen(m_wstr) : 0; +} + +// Compare two wrapped BSTRs +// +inline int _bstr_t::Data_t::Compare(const _bstr_t::Data_t& str) const throw() +{ + // Dont need to check for NULL here, because + // SysStringLen will return 0 if you pass in NULL + const unsigned int l1 = ::SysStringLen(m_wstr); + const unsigned int l2 = ::SysStringLen(str.m_wstr); + + unsigned int len = l1; + if (len > l2) { + len = l2; + } + + BSTR bstr1 = m_wstr; + BSTR bstr2 = str.m_wstr; + + while (len-- > 0) { + if (*bstr1++ != *bstr2++) { + return bstr1[-1] - bstr2[-1]; + } + } + + return (l1 < l2) ? -1 : (l1 == l2) ? 0 : 1; +} + +// Exception agnostic wrapper for new +// +#ifdef _COM_OPERATOR_NEW_THROWS +inline void* _bstr_t::Data_t::operator new(size_t sz) +{ + try { + return ::operator new(sz); + } + catch (...) { + return NULL; + } +} +#else // _COM_OPERATOR_NEW_THROWS +inline void* _bstr_t::Data_t::operator new(size_t sz) +{ + return ::operator new(sz); +} +#endif // _COM_OPERATOR_NEW_THROWS + +// Destruct this object +// +inline _bstr_t::Data_t::~Data_t() throw() +{ + _Free(); +} + +// Free up this object +// +inline void _bstr_t::Data_t::_Free() throw() +{ + if (m_wstr != NULL) { + ::SysFreeString(m_wstr); + m_wstr = NULL; + } + + if (m_str != NULL) { + delete [] m_str; + m_str = NULL; + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Wrapper class for VARIANT +// +////////////////////////////////////////////////////////////////////////////// + +/* + * VARENUM usage key, + * + * * [V] - may appear in a VARIANT + * * [T] - may appear in a TYPEDESC + * * [P] - may appear in an OLE property set + * * [S] - may appear in a Safe Array + * * [C] - supported by class _variant_t + * + * + * VT_EMPTY [V] [P] nothing + * VT_NULL [V] [P] SQL style Null + * VT_I2 [V][T][P][S][C] 2 byte signed int + * VT_I4 [V][T][P][S][C] 4 byte signed int + * VT_R4 [V][T][P][S][C] 4 byte real + * VT_R8 [V][T][P][S][C] 8 byte real + * VT_CY [V][T][P][S][C] currency + * VT_DATE [V][T][P][S][C] date + * VT_BSTR [V][T][P][S][C] OLE Automation string + * VT_DISPATCH [V][T][P][S][C] IDispatch * + * VT_ERROR [V][T] [S][C] SCODE + * VT_BOOL [V][T][P][S][C] True=-1, False=0 + * VT_VARIANT [V][T][P][S] VARIANT * + * VT_UNKNOWN [V][T] [S][C] IUnknown * + * VT_DECIMAL [V][T] [S][C] 16 byte fixed point + * VT_I1 [T] signed char + * VT_UI1 [V][T][P][S][C] unsigned char + * VT_UI2 [T][P] unsigned short + * VT_UI4 [T][P] unsigned short + * VT_I8 [T][P] signed 64-bit int + * VT_UI8 [T][P] unsigned 64-bit int + * VT_INT [T] signed machine int + * VT_UINT [T] unsigned machine int + * VT_VOID [T] C style void + * VT_HRESULT [T] Standard return type + * VT_PTR [T] pointer type + * VT_SAFEARRAY [T] (use VT_ARRAY in VARIANT) + * VT_CARRAY [T] C style array + * VT_USERDEFINED [T] user defined type + * VT_LPSTR [T][P] null terminated string + * VT_LPWSTR [T][P] wide null terminated string + * VT_FILETIME [P] FILETIME + * VT_BLOB [P] Length prefixed bytes + * VT_STREAM [P] Name of the stream follows + * VT_STORAGE [P] Name of the storage follows + * VT_STREAMED_OBJECT [P] Stream contains an object + * VT_STORED_OBJECT [P] Storage contains an object + * VT_BLOB_OBJECT [P] Blob contains an object + * VT_CF [P] Clipboard format + * VT_CLSID [P] A Class ID + * VT_VECTOR [P] simple counted array + * VT_ARRAY [V] SAFEARRAY* + * VT_BYREF [V] void* for local use + */ + +class _variant_t : public ::tagVARIANT { +public: + // Constructors + // + _variant_t() throw(); + + _variant_t(const VARIANT& varSrc) ; + _variant_t(const VARIANT* pSrc) ; + _variant_t(const _variant_t& varSrc) ; + + _variant_t(VARIANT& varSrc, bool fCopy) ; // Attach VARIANT if !fCopy + + _variant_t(short sSrc, VARTYPE vtSrc = VT_I2) ; // Creates a VT_I2, or a VT_BOOL + _variant_t(long lSrc, VARTYPE vtSrc = VT_I4) ; // Creates a VT_I4, a VT_ERROR, or a VT_BOOL + _variant_t(float fltSrc) throw(); // Creates a VT_R4 + _variant_t(double dblSrc, VARTYPE vtSrc = VT_R8) ; // Creates a VT_R8, or a VT_DATE + _variant_t(const CY& cySrc) throw(); // Creates a VT_CY + _variant_t(const _bstr_t& bstrSrc) ; // Creates a VT_BSTR + _variant_t(const wchar_t *pSrc) ; // Creates a VT_BSTR + _variant_t(const char* pSrc) ; // Creates a VT_BSTR + _variant_t(IDispatch* pSrc, bool fAddRef = true) throw(); // Creates a VT_DISPATCH + _variant_t(bool boolSrc) throw(); // Creates a VT_BOOL + _variant_t(IUnknown* pSrc, bool fAddRef = true) throw(); // Creates a VT_UNKNOWN + _variant_t(const DECIMAL& decSrc) throw(); // Creates a VT_DECIMAL + _variant_t(BYTE bSrc) throw(); // Creates a VT_UI1 + + _variant_t(char cSrc) throw(); // Creates a VT_I1 + _variant_t(unsigned short usSrc) throw(); // Creates a VT_UI2 + _variant_t(unsigned long ulSrc) throw(); // Creates a VT_UI4 + _variant_t(int iSrc) throw(); // Creates a VT_INT + _variant_t(unsigned int uiSrc) throw(); // Creates a VT_UINT +#if (_WIN32_WINNT >= 0x0501) + _variant_t(__int64 i8Src) throw(); // Creates a VT_I8 + _variant_t(unsigned __int64 ui8Src) throw(); // Creates a VT_UI8 +#endif + + // Destructor + // + ~_variant_t() throw() ; + + // Extractors + // + operator short() const ; // Extracts a short from a VT_I2 + operator long() const ; // Extracts a long from a VT_I4 + operator float() const ; // Extracts a float from a VT_R4 + operator double() const ; // Extracts a double from a VT_R8 + operator CY() const ; // Extracts a CY from a VT_CY + operator _bstr_t() const ; // Extracts a _bstr_t from a VT_BSTR + operator IDispatch*() const ; // Extracts a IDispatch* from a VT_DISPATCH + operator bool() const ; // Extracts a bool from a VT_BOOL + operator IUnknown*() const ; // Extracts a IUnknown* from a VT_UNKNOWN + operator DECIMAL() const ; // Extracts a DECIMAL from a VT_DECIMAL + operator BYTE() const ; // Extracts a BTYE (unsigned char) from a VT_UI1 + operator VARIANT() const throw(); + + operator char() const ; // Extracts a char from a VT_I1 + operator unsigned short() const ; // Extracts a unsigned short from a VT_UI2 + operator unsigned long() const ; // Extracts a unsigned long from a VT_UI4 + operator int() const ; // Extracts a int from a VT_INT + operator unsigned int() const ; // Extracts a unsigned int from a VT_UINT +#if (_WIN32_WINNT >= 0x0501) + operator __int64() const ; // Extracts a __int64 from a VT_I8 + operator unsigned __int64() const ; // Extracts a unsigned __int64 from a VT_UI8 +#endif + + // Assignment operations + // + _variant_t& operator=(const VARIANT& varSrc) ; + _variant_t& operator=(const VARIANT* pSrc) ; + _variant_t& operator=(const _variant_t& varSrc) ; + + _variant_t& operator=(short sSrc) ; // Assign a VT_I2, or a VT_BOOL + _variant_t& operator=(long lSrc) ; // Assign a VT_I4, a VT_ERROR or a VT_BOOL + _variant_t& operator=(float fltSrc) ; // Assign a VT_R4 + _variant_t& operator=(double dblSrc) ; // Assign a VT_R8, or a VT_DATE + _variant_t& operator=(const CY& cySrc) ; // Assign a VT_CY + _variant_t& operator=(const _bstr_t& bstrSrc) ; // Assign a VT_BSTR + _variant_t& operator=(const wchar_t* pSrc) ; // Assign a VT_BSTR + _variant_t& operator=(const char* pSrc) ; // Assign a VT_BSTR + _variant_t& operator=(IDispatch* pSrc) ; // Assign a VT_DISPATCH + _variant_t& operator=(bool boolSrc) ; // Assign a VT_BOOL + _variant_t& operator=(IUnknown* pSrc) ; // Assign a VT_UNKNOWN + _variant_t& operator=(const DECIMAL& decSrc) ; // Assign a VT_DECIMAL + _variant_t& operator=(BYTE bSrc) ; // Assign a VT_UI1 + + _variant_t& operator=(char cSrc) ; // Assign a VT_I1 + _variant_t& operator=(unsigned short usSrc) ; // Assign a VT_UI2 + _variant_t& operator=(unsigned long ulSrc) ; // Assign a VT_UI4 + _variant_t& operator=(int iSrc) ; // Assign a VT_INT + _variant_t& operator=(unsigned int uiSrc) ; // Assign a VT_UINT +#if (_WIN32_WINNT >= 0x0501) + _variant_t& operator=(__int64 i8Src) ; // Assign a VT_I8 + _variant_t& operator=(unsigned __int64 ui8Src) ; // Assign a VT_UI8 +#endif + + // Comparison operations + // + bool operator==(const VARIANT& varSrc) const throw(); + bool operator==(const VARIANT* pSrc) const throw(); + + bool operator!=(const VARIANT& varSrc) const throw(); + bool operator!=(const VARIANT* pSrc) const throw(); + + // Low-level operations + // + void Clear() ; + + void Attach(VARIANT& varSrc) ; + VARIANT Detach() throw(); + + VARIANT& GetVARIANT() throw(); + VARIANT* GetAddress() ; + + void ChangeType(VARTYPE vartype, const _variant_t* pSrc = NULL) ; + + void SetString(const char* pSrc) ; // used to set ANSI string +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Constructors +// +////////////////////////////////////////////////////////////////////////////////////////// + +// Default constructor +// +inline _variant_t::_variant_t() throw() +{ + ::VariantInit(this); +} + +// Construct a _variant_t from a const VARIANT& +// +inline _variant_t::_variant_t(const VARIANT& varSrc) +{ + ::VariantInit(this); + _com_util::CheckError(::VariantCopy(this, const_cast<VARIANT*>(&varSrc))); +} + +// Construct a _variant_t from a const VARIANT* +// +inline _variant_t::_variant_t(const VARIANT* pSrc) +{ + if (pSrc == NULL) { + _com_issue_error(E_POINTER); + } + else { + ::VariantInit(this); + _com_util::CheckError(::VariantCopy(this, const_cast<VARIANT*>(pSrc))); + } +} + +// Construct a _variant_t from a const _variant_t& +// +inline _variant_t::_variant_t(const _variant_t& varSrc) +{ + ::VariantInit(this); + _com_util::CheckError(::VariantCopy(this, const_cast<VARIANT*>(static_cast<const VARIANT*>(&varSrc)))); +} + +// Construct a _variant_t from a VARIANT&. If fCopy is FALSE, give control of +// data to the _variant_t without doing a VariantCopy. +// +inline _variant_t::_variant_t(VARIANT& varSrc, bool fCopy) +{ + if (fCopy) { + ::VariantInit(this); + _com_util::CheckError(::VariantCopy(this, &varSrc)); + } + else { + _COM_MEMCPY_S(this, sizeof(varSrc), &varSrc, sizeof(varSrc)); + V_VT(&varSrc) = VT_EMPTY; + } +} + +// Construct either a VT_I2 VARIANT or a VT_BOOL VARIANT from +// a short (the default is VT_I2) +// +inline _variant_t::_variant_t(short sSrc, VARTYPE vtSrc) +{ + if ((vtSrc != VT_I2) && (vtSrc != VT_BOOL)) { + _com_issue_error(E_INVALIDARG); + return; + } + + if (vtSrc == VT_BOOL) { + V_VT(this) = VT_BOOL; + V_BOOL(this) = (sSrc ? VARIANT_TRUE : VARIANT_FALSE); + } + else { + V_VT(this) = VT_I2; + V_I2(this) = sSrc; + } +} + +// Construct either a VT_I4 VARIANT, a VT_BOOL VARIANT, or a +// VT_ERROR VARIANT from a long (the default is VT_I4) +// +inline _variant_t::_variant_t(long lSrc, VARTYPE vtSrc) +{ + if ((vtSrc != VT_I4) && (vtSrc != VT_ERROR) && (vtSrc != VT_BOOL)) { + _com_issue_error(E_INVALIDARG); + return; + } + + if (vtSrc == VT_ERROR) { + V_VT(this) = VT_ERROR; + V_ERROR(this) = lSrc; + } + else if (vtSrc == VT_BOOL) { + V_VT(this) = VT_BOOL; + V_BOOL(this) = (lSrc ? VARIANT_TRUE : VARIANT_FALSE); + } + else { + V_VT(this) = VT_I4; + V_I4(this) = lSrc; + } +} + +// Construct a VT_R4 VARIANT from a float +// +inline _variant_t::_variant_t(float fltSrc) throw() +{ + V_VT(this) = VT_R4; + V_R4(this) = fltSrc; +} + +// Construct either a VT_R8 VARIANT, or a VT_DATE VARIANT from +// a double (the default is VT_R8) +// +inline _variant_t::_variant_t(double dblSrc, VARTYPE vtSrc) +{ + if ((vtSrc != VT_R8) && (vtSrc != VT_DATE)) { + _com_issue_error(E_INVALIDARG); + return; + } + + if (vtSrc == VT_DATE) { + V_VT(this) = VT_DATE; + V_DATE(this) = dblSrc; + } + else { + V_VT(this) = VT_R8; + V_R8(this) = dblSrc; + } +} + +// Construct a VT_CY from a CY +// +inline _variant_t::_variant_t(const CY& cySrc) throw() +{ + V_VT(this) = VT_CY; + V_CY(this) = cySrc; +} + +// Construct a VT_BSTR VARIANT from a const _bstr_t& +// +inline _variant_t::_variant_t(const _bstr_t& bstrSrc) +{ + V_VT(this) = VT_BSTR; + + BSTR bstr = static_cast<wchar_t*>(bstrSrc); + if (bstr == NULL) { + V_BSTR(this) = NULL; + } + else { + V_BSTR(this) = ::SysAllocStringByteLen(reinterpret_cast<char*>(bstr), + ::SysStringByteLen(bstr)); + if (V_BSTR(this) == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } +} + +// Construct a VT_BSTR VARIANT from a const wchar_t* +// +inline _variant_t::_variant_t(const wchar_t* pSrc) +{ + V_VT(this) = VT_BSTR; + + V_BSTR(this) = ::SysAllocString(pSrc); + + if (V_BSTR(this) == NULL && pSrc != NULL) { + _com_issue_error(E_OUTOFMEMORY); + } +} + +// Construct a VT_BSTR VARIANT from a const char* +// +inline _variant_t::_variant_t(const char* pSrc) +{ + V_VT(this) = VT_BSTR; + V_BSTR(this) = _com_util::ConvertStringToBSTR(pSrc); +} + +// Construct a VT_DISPATCH VARIANT from an IDispatch* +// +inline _variant_t::_variant_t(IDispatch* pSrc, bool fAddRef) throw() +{ + V_VT(this) = VT_DISPATCH; + V_DISPATCH(this) = pSrc; + + // Need the AddRef() as VariantClear() calls Release(), unless fAddRef + // false indicates we're taking ownership + // + if (fAddRef) { + if (V_DISPATCH(this) != NULL) { + V_DISPATCH(this)->AddRef(); + } + } +} + +// Construct a VT_BOOL VARIANT from a bool +// +inline _variant_t::_variant_t(bool boolSrc) throw() +{ + V_VT(this) = VT_BOOL; + V_BOOL(this) = (boolSrc ? VARIANT_TRUE : VARIANT_FALSE); +} + +// Construct a VT_UNKNOWN VARIANT from an IUnknown* +// +inline _variant_t::_variant_t(IUnknown* pSrc, bool fAddRef) throw() +{ + V_VT(this) = VT_UNKNOWN; + V_UNKNOWN(this) = pSrc; + + // Need the AddRef() as VariantClear() calls Release(), unless fAddRef + // false indicates we're taking ownership + // + if (fAddRef) { + if (V_UNKNOWN(this) != NULL) { + V_UNKNOWN(this)->AddRef(); + } + } +} + +// Construct a VT_DECIMAL VARIANT from a DECIMAL +// +inline _variant_t::_variant_t(const DECIMAL& decSrc) throw() +{ + // Order is important here! Setting V_DECIMAL wipes out the entire VARIANT + // + V_DECIMAL(this) = decSrc; + V_VT(this) = VT_DECIMAL; +} + +// Construct a VT_UI1 VARIANT from a BYTE (unsigned char) +// +inline _variant_t::_variant_t(BYTE bSrc) throw() +{ + V_VT(this) = VT_UI1; + V_UI1(this) = bSrc; +} + +// Construct a VT_I1 VARIANT from a char +// +inline _variant_t::_variant_t(char cSrc) throw() +{ + V_VT(this) = VT_I1; + V_I1(this) = cSrc; +} + +// Construct a VT_UI2 VARIANT from a unsigned short +// +inline _variant_t::_variant_t(unsigned short usSrc) throw() +{ + V_VT(this) = VT_UI2; + V_UI2(this) = usSrc; +} + +// Construct a VT_UI4 VARIANT from a unsigned long +// +inline _variant_t::_variant_t(unsigned long ulSrc) throw() +{ + V_VT(this) = VT_UI4; + V_UI4(this) = ulSrc; +} + +// Construct a VT_INT VARIANT from a int +// +inline _variant_t::_variant_t(int iSrc) throw() +{ + V_VT(this) = VT_INT; + V_INT(this) = iSrc; +} + +// Construct a VT_UINT VARIANT from a unsigned int +// +inline _variant_t::_variant_t(unsigned int uiSrc) throw() +{ + V_VT(this) = VT_UINT; + V_UINT(this) = uiSrc; +} + +#if (_WIN32_WINNT >= 0x0501) +// Construct a VT_I8 VARIANT from a __int64 +// +inline _variant_t::_variant_t(__int64 i8Src) throw() +{ + V_VT(this) = VT_I8; + V_I8(this) = i8Src; +} + +// Construct a VT_UI8 VARIANT from a unsigned __int64 +// +inline _variant_t::_variant_t(unsigned __int64 ui8Src) throw() +{ + V_VT(this) = VT_UI8; + V_UI8(this) = ui8Src; +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Extractors +// +////////////////////////////////////////////////////////////////////////////////////////// + +// Extracts a VT_I2 into a short +// +inline _variant_t::operator short() const +{ + if (V_VT(this) == VT_I2) { + return V_I2(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_I2, this); + + return V_I2(&varDest); +} + +// Extracts a VT_I4 into a long +// +inline _variant_t::operator long() const +{ + if (V_VT(this) == VT_I4) { + return V_I4(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_I4, this); + + return V_I4(&varDest); +} + +// Extracts a VT_R4 into a float +// +inline _variant_t::operator float() const +{ + if (V_VT(this) == VT_R4) { + return V_R4(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_R4, this); + + return V_R4(&varDest); +} + +// Extracts a VT_R8 into a double +// +inline _variant_t::operator double() const +{ + if (V_VT(this) == VT_R8) { + return V_R8(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_R8, this); + + return V_R8(&varDest); +} + +// Extracts a VT_CY into a CY +// +inline _variant_t::operator CY() const +{ + if (V_VT(this) == VT_CY) { + return V_CY(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_CY, this); + + return V_CY(&varDest); +} + +// Extracts a VT_BSTR into a _bstr_t +// +inline _variant_t::operator _bstr_t() const +{ + if (V_VT(this) == VT_BSTR) { + return V_BSTR(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_BSTR, this); + + return V_BSTR(&varDest); +} + +// Extracts a VT_DISPATCH into an IDispatch* +// +inline _variant_t::operator IDispatch*() const +{ + if (V_VT(this) == VT_DISPATCH) { + if (V_DISPATCH(this) != NULL) { + V_DISPATCH(this)->AddRef(); + } + return V_DISPATCH(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_DISPATCH, this); + + if (V_DISPATCH(&varDest) != NULL) { + V_DISPATCH(&varDest)->AddRef(); + } + + return V_DISPATCH(&varDest); +} + +// Extract a VT_BOOL into a bool +// +inline _variant_t::operator bool() const +{ + if (V_VT(this) == VT_BOOL) { + return V_BOOL(this) ? true : false; + } + + _variant_t varDest; + varDest.ChangeType(VT_BOOL, this); + + return (V_BOOL(&varDest) == VARIANT_TRUE) ? true : false; +} + +// Extracts a VT_UNKNOWN into an IUnknown* +// +inline _variant_t::operator IUnknown*() const +{ + if (V_VT(this) == VT_UNKNOWN) { + if (V_UNKNOWN(this) != NULL) { + V_UNKNOWN(this)->AddRef(); + } + return V_UNKNOWN(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_UNKNOWN, this); + + if (V_UNKNOWN(&varDest) != NULL) { + V_UNKNOWN(&varDest)->AddRef(); + } + + return V_UNKNOWN(&varDest); +} + +// Extracts a VT_DECIMAL into a DECIMAL +// +inline _variant_t::operator DECIMAL() const +{ + if (V_VT(this) == VT_DECIMAL) { + return V_DECIMAL(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_DECIMAL, this); + + return V_DECIMAL(&varDest); +} + +// Extracts a VT_UI1 into a BYTE (unsigned char) +// +inline _variant_t::operator BYTE() const +{ + if (V_VT(this) == VT_UI1) { + return V_UI1(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_UI1, this); + + return V_UI1(&varDest); +} + +// Extract the physical VARIANT +// +inline _variant_t::operator VARIANT() const throw() +{ + return *(VARIANT*) this; +} + +// Extracts a VT_I1 into a char +// +inline _variant_t::operator char() const +{ + if (V_VT(this) == VT_I1) { + return V_I1(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_I1, this); + + return V_I1(&varDest); +} + +// Extracts a VT_UI2 into a unsigned short +// +inline _variant_t::operator unsigned short() const +{ + if (V_VT(this) == VT_UI2) { + return V_UI2(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_UI2, this); + + return V_UI2(&varDest); +} + +// Extracts a VT_UI4 into a unsigned long +// +inline _variant_t::operator unsigned long() const +{ + if (V_VT(this) == VT_UI4) { + return V_UI4(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_UI4, this); + + return V_UI4(&varDest); +} + +// Extracts a VT_INT into a int +// +inline _variant_t::operator int() const +{ + if (V_VT(this) == VT_INT) { + return V_INT(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_INT, this); + + return V_INT(&varDest); +} + +// Extracts a VT_UINT into a unsigned int +// +inline _variant_t::operator unsigned int() const +{ + if (V_VT(this) == VT_UINT) { + return V_UINT(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_UINT, this); + + return V_UINT(&varDest); +} + +#if (_WIN32_WINNT >= 0x0501) +// Extracts a VT_I8 into a __int64 +// +inline _variant_t::operator __int64() const +{ + if (V_VT(this) == VT_I8) { + return V_I8(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_I8, this); + + return V_I8(&varDest); +} + +// Extracts a VT_UI8 into a unsigned __int64 +// +inline _variant_t::operator unsigned __int64() const +{ + if (V_VT(this) == VT_UI8) { + return V_UI8(this); + } + + _variant_t varDest; + varDest.ChangeType(VT_UI8, this); + + return V_UI8(&varDest); +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Assignment operations +// +////////////////////////////////////////////////////////////////////////////////////////// + +// Assign a const VARIANT& (::VariantCopy handles everything) +// +inline _variant_t& _variant_t::operator=(const VARIANT& varSrc) +{ + _com_util::CheckError(::VariantCopy(this, const_cast<VARIANT*>(&varSrc))); + + return *this; +} + +// Assign a const VARIANT* (::VariantCopy handles everything) +// +inline _variant_t& _variant_t::operator=(const VARIANT* pSrc) +{ + if (pSrc == NULL) { + _com_issue_error(E_POINTER); + } + else { + _com_util::CheckError(::VariantCopy(this, const_cast<VARIANT*>(pSrc))); + } + + return *this; +} + +// Assign a const _variant_t& (::VariantCopy handles everything) +// +inline _variant_t& _variant_t::operator=(const _variant_t& varSrc) +{ + _com_util::CheckError(::VariantCopy(this, const_cast<VARIANT*>(static_cast<const VARIANT*>(&varSrc)))); + + return *this; +} + +// Assign a short creating either VT_I2 VARIANT or a +// VT_BOOL VARIANT (VT_I2 is the default) +// +inline _variant_t& _variant_t::operator=(short sSrc) +{ + if (V_VT(this) == VT_I2) { + V_I2(this) = sSrc; + } + else if (V_VT(this) == VT_BOOL) { + V_BOOL(this) = (sSrc ? VARIANT_TRUE : VARIANT_FALSE); + } + else { + // Clear the VARIANT and create a VT_I2 + // + Clear(); + + V_VT(this) = VT_I2; + V_I2(this) = sSrc; + } + + return *this; +} + +// Assign a long creating either VT_I4 VARIANT, a VT_ERROR VARIANT +// or a VT_BOOL VARIANT (VT_I4 is the default) +// +inline _variant_t& _variant_t::operator=(long lSrc) +{ + if (V_VT(this) == VT_I4) { + V_I4(this) = lSrc; + } + else if (V_VT(this) == VT_ERROR) { + V_ERROR(this) = lSrc; + } + else if (V_VT(this) == VT_BOOL) { + V_BOOL(this) = (lSrc ? VARIANT_TRUE : VARIANT_FALSE); + } + else { + // Clear the VARIANT and create a VT_I4 + // + Clear(); + + V_VT(this) = VT_I4; + V_I4(this) = lSrc; + } + + return *this; +} + +// Assign a float creating a VT_R4 VARIANT +// +inline _variant_t& _variant_t::operator=(float fltSrc) +{ + if (V_VT(this) != VT_R4) { + // Clear the VARIANT and create a VT_R4 + // + Clear(); + + V_VT(this) = VT_R4; + } + + V_R4(this) = fltSrc; + + return *this; +} + +// Assign a double creating either a VT_R8 VARIANT, or a VT_DATE +// VARIANT (VT_R8 is the default) +// +inline _variant_t& _variant_t::operator=(double dblSrc) +{ + if (V_VT(this) == VT_R8) { + V_R8(this) = dblSrc; + } + else if(V_VT(this) == VT_DATE) { + V_DATE(this) = dblSrc; + } + else { + // Clear the VARIANT and create a VT_R8 + // + Clear(); + + V_VT(this) = VT_R8; + V_R8(this) = dblSrc; + } + + return *this; +} + +// Assign a CY creating a VT_CY VARIANT +// +inline _variant_t& _variant_t::operator=(const CY& cySrc) +{ + if (V_VT(this) != VT_CY) { + // Clear the VARIANT and create a VT_CY + // + Clear(); + + V_VT(this) = VT_CY; + } + + V_CY(this) = cySrc; + + return *this; +} + +// Assign a const _bstr_t& creating a VT_BSTR VARIANT +// +inline _variant_t& _variant_t::operator=(const _bstr_t& bstrSrc) +{ + _COM_ASSERT(V_VT(this) != VT_BSTR || (BSTR) bstrSrc == NULL || V_BSTR(this) != (BSTR) bstrSrc); + + // Clear the VARIANT (This will SysFreeString() any previous occupant) + // + Clear(); + + V_VT(this) = VT_BSTR; + + if (!bstrSrc) { + V_BSTR(this) = NULL; + } + else { + BSTR bstr = static_cast<wchar_t*>(bstrSrc); + V_BSTR(this) = ::SysAllocStringByteLen(reinterpret_cast<char*>(bstr), + ::SysStringByteLen(bstr)); + + if (V_BSTR(this) == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } + + return *this; +} + +// Assign a const wchar_t* creating a VT_BSTR VARIANT +// +inline _variant_t& _variant_t::operator=(const wchar_t* pSrc) +{ + _COM_ASSERT(V_VT(this) != VT_BSTR || pSrc == NULL || V_BSTR(this) != pSrc); + + // Clear the VARIANT (This will SysFreeString() any previous occupant) + // + Clear(); + + V_VT(this) = VT_BSTR; + + if (pSrc == NULL) { + V_BSTR(this) = NULL; + } + else { + V_BSTR(this) = ::SysAllocString(pSrc); + + if (V_BSTR(this) == NULL) { + _com_issue_error(E_OUTOFMEMORY); + } + } + + return *this; +} + +// Assign a const char* creating a VT_BSTR VARIANT +// +inline _variant_t& _variant_t::operator=(const char* pSrc) +{ + _COM_ASSERT(V_VT(this) != (VT_I1 | VT_BYREF) || pSrc == NULL || V_I1REF(this) != pSrc); + + // Clear the VARIANT (This will SysFreeString() any previous occupant) + // + Clear(); + + V_VT(this) = VT_BSTR; + V_BSTR(this) = _com_util::ConvertStringToBSTR(pSrc); + + return *this; +} + +// Assign an IDispatch* creating a VT_DISPATCH VARIANT +// +inline _variant_t& _variant_t::operator=(IDispatch* pSrc) +{ + _COM_ASSERT(V_VT(this) != VT_DISPATCH || pSrc == 0 || V_DISPATCH(this) != pSrc); + + // Clear the VARIANT (This will Release() any previous occupant) + // + Clear(); + + V_VT(this) = VT_DISPATCH; + V_DISPATCH(this) = pSrc; + + if (V_DISPATCH(this) != NULL) { + // Need the AddRef() as VariantClear() calls Release() + // + V_DISPATCH(this)->AddRef(); + } + + return *this; +} + +// Assign a bool creating a VT_BOOL VARIANT +// +inline _variant_t& _variant_t::operator=(bool boolSrc) +{ + if (V_VT(this) != VT_BOOL) { + // Clear the VARIANT and create a VT_BOOL + // + Clear(); + + V_VT(this) = VT_BOOL; + } + + V_BOOL(this) = (boolSrc ? VARIANT_TRUE : VARIANT_FALSE); + + return *this; +} + +// Assign an IUnknown* creating a VT_UNKNOWN VARIANT +// +inline _variant_t& _variant_t::operator=(IUnknown* pSrc) +{ + _COM_ASSERT(V_VT(this) != VT_UNKNOWN || pSrc == NULL || V_UNKNOWN(this) != pSrc); + + // Clear VARIANT (This will Release() any previous occupant) + // + Clear(); + + V_VT(this) = VT_UNKNOWN; + V_UNKNOWN(this) = pSrc; + + if (V_UNKNOWN(this) != NULL) { + // Need the AddRef() as VariantClear() calls Release() + // + V_UNKNOWN(this)->AddRef(); + } + + return *this; +} + +// Assign a DECIMAL creating a VT_DECIMAL VARIANT +// +inline _variant_t& _variant_t::operator=(const DECIMAL& decSrc) +{ + if (V_VT(this) != VT_DECIMAL) { + // Clear the VARIANT + // + Clear(); + } + + // Order is important here! Setting V_DECIMAL wipes out the entire VARIANT + V_DECIMAL(this) = decSrc; + V_VT(this) = VT_DECIMAL; + + return *this; +} + +// Assign a BTYE (unsigned char) creating a VT_UI1 VARIANT +// +inline _variant_t& _variant_t::operator=(BYTE bSrc) +{ + if (V_VT(this) != VT_UI1) { + // Clear the VARIANT and create a VT_UI1 + // + Clear(); + + V_VT(this) = VT_UI1; + } + + V_UI1(this) = bSrc; + + return *this; +} + +// Assign a char creating a VT_I1 VARIANT +// +inline _variant_t& _variant_t::operator=(char cSrc) +{ + if (V_VT(this) != VT_I1) { + // Clear the VARIANT and create a VT_I1 + // + Clear(); + + V_VT(this) = VT_I1; + } + + V_I1(this) = cSrc; + + return *this; +} + +// Assign a char creating a VT_UI2 VARIANT +// +inline _variant_t& _variant_t::operator=(unsigned short usSrc) +{ + if (V_VT(this) != VT_UI2) { + // Clear the VARIANT and create a VT_UI2 + // + Clear(); + + V_VT(this) = VT_UI2; + } + + V_UI2(this) = usSrc; + + return *this; +} + +// Assign a char creating a VT_UI4 VARIANT +// +inline _variant_t& _variant_t::operator=(unsigned long ulSrc) +{ + if (V_VT(this) != VT_UI4) { + // Clear the VARIANT and create a VT_UI4 + // + Clear(); + + V_VT(this) = VT_UI4; + } + + V_UI4(this) = ulSrc; + + return *this; +} + +// Assign a char creating a VT_INT VARIANT +// +inline _variant_t& _variant_t::operator=(int iSrc) +{ + if (V_VT(this) != VT_INT) { + // Clear the VARIANT and create a VT_INT + // + Clear(); + + V_VT(this) = VT_INT; + } + + V_INT(this) = iSrc; + + return *this; +} + +// Assign a char creating a VT_UINT VARIANT +// +inline _variant_t& _variant_t::operator=(unsigned int uiSrc) +{ + if (V_VT(this) != VT_UINT) { + // Clear the VARIANT and create a VT_UINT + // + Clear(); + + V_VT(this) = VT_UINT; + } + + V_UINT(this) = uiSrc; + + return *this; +} + +#if (_WIN32_WINNT >= 0x0501) +// Assign a char creating a VT_I8 VARIANT +// +inline _variant_t& _variant_t::operator=(__int64 i8Src) +{ + if (V_VT(this) != VT_I8) { + // Clear the VARIANT and create a VT_I8 + // + Clear(); + + V_VT(this) = VT_I8; + } + + V_I8(this) = i8Src; + + return *this; +} + +// Assign a char creating a VT_UI8 VARIANT +// +inline _variant_t& _variant_t::operator=(unsigned __int64 ui8Src) +{ + if (V_VT(this) != VT_UI8) { + // Clear the VARIANT and create a VT_UI8 + // + Clear(); + + V_VT(this) = VT_UI8; + } + + V_UI8(this) = ui8Src; + + return *this; +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Comparison operations +// +////////////////////////////////////////////////////////////////////////////////////////// + +// Compare a _variant_t against a const VARIANT& for equality +// +inline bool _variant_t::operator==(const VARIANT& varSrc) const throw() +{ + return *this == &varSrc; +} + +#pragma warning(push) +#pragma warning(disable: 4702) // unreachable code + +// Compare a _variant_t against a const VARIANT* for equality +// +inline bool _variant_t::operator==(const VARIANT* pSrc) const throw() +{ + if (pSrc == NULL) { + return false; + } + + if (this == pSrc) { + return true; + } + + // + // Variants not equal if types don't match + // + if (V_VT(this) != V_VT(pSrc)) { + return false; + } + + // + // Check type specific values + // + switch (V_VT(this)) { + case VT_EMPTY: + case VT_NULL: + return true; + + case VT_I2: + return V_I2(this) == V_I2(pSrc); + + case VT_I4: + return V_I4(this) == V_I4(pSrc); + + case VT_R4: + return V_R4(this) == V_R4(pSrc); + + case VT_R8: + return V_R8(this) == V_R8(pSrc); + + case VT_CY: + return memcmp(&(V_CY(this)), &(V_CY(pSrc)), sizeof(CY)) == 0; + + case VT_DATE: + return V_DATE(this) == V_DATE(pSrc); + + case VT_BSTR: + return (::SysStringByteLen(V_BSTR(this)) == ::SysStringByteLen(V_BSTR(pSrc))) && + (memcmp(V_BSTR(this), V_BSTR(pSrc), ::SysStringByteLen(V_BSTR(this))) == 0); + + case VT_DISPATCH: + return V_DISPATCH(this) == V_DISPATCH(pSrc); + + case VT_ERROR: + return V_ERROR(this) == V_ERROR(pSrc); + + case VT_BOOL: + return V_BOOL(this) == V_BOOL(pSrc); + + case VT_UNKNOWN: + return V_UNKNOWN(this) == V_UNKNOWN(pSrc); + + case VT_DECIMAL: + return memcmp(&(V_DECIMAL(this)), &(V_DECIMAL(pSrc)), sizeof(DECIMAL)) == 0; + + case VT_UI1: + return V_UI1(this) == V_UI1(pSrc); + + case VT_I1: + return V_I1(this) == V_I1(pSrc); + + case VT_UI2: + return V_UI2(this) == V_UI2(pSrc); + + case VT_UI4: + return V_UI4(this) == V_UI4(pSrc); + + case VT_INT: + return V_INT(this) == V_INT(pSrc); + + case VT_UINT: + return V_UINT(this) == V_UINT(pSrc); + +#if (_WIN32_WINNT >= 0x0501) + case VT_I8: + return V_I8(this) == V_I8(pSrc); + + case VT_UI8: + return V_UI8(this) == V_UI8(pSrc); +#endif + + default: + _com_issue_error(E_INVALIDARG); + // fall through + } + + return false; +} + +#pragma warning(pop) + +// Compare a _variant_t against a const VARIANT& for in-equality +// +inline bool _variant_t::operator!=(const VARIANT& varSrc) const throw() +{ + return !(*this == &varSrc); +} + +// Compare a _variant_t against a const VARIANT* for in-equality +// +inline bool _variant_t::operator!=(const VARIANT* pSrc) const throw() +{ + return !(*this == pSrc); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Low-level operations +// +////////////////////////////////////////////////////////////////////////////////////////// + +// Clear the _variant_t +// +inline void _variant_t::Clear() +{ + _com_util::CheckError(::VariantClear(this)); +} + +inline void _variant_t::Attach(VARIANT& varSrc) +{ + // + // Free up previous VARIANT + // + Clear(); + + // + // Give control of data to _variant_t + // + _COM_MEMCPY_S(this, sizeof(varSrc), &varSrc, sizeof(varSrc)); + V_VT(&varSrc) = VT_EMPTY; +} + +inline VARIANT _variant_t::Detach() throw() +{ + VARIANT varResult = *this; + V_VT(this) = VT_EMPTY; + + return varResult; +} + +inline VARIANT& _variant_t::GetVARIANT() throw() +{ + return *(VARIANT*) this; +} + +inline VARIANT* _variant_t::GetAddress() +{ + Clear(); + return (VARIANT*) this; +} + +// Change the type and contents of this _variant_t to the type vartype and +// contents of pSrc +// +inline void _variant_t::ChangeType(VARTYPE vartype, const _variant_t* pSrc) +{ + // + // If pDest is NULL, convert type in place + // + if (pSrc == NULL) { + pSrc = this; + } + + if ((this != pSrc) || (vartype != V_VT(this))) { + _com_util::CheckError(::VariantChangeType(static_cast<VARIANT*>(this), + const_cast<VARIANT*>(static_cast<const VARIANT*>(pSrc)), + 0, vartype)); + } +} + +inline void _variant_t::SetString(const char* pSrc) +{ + operator=(pSrc); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Destructor +// +////////////////////////////////////////////////////////////////////////////////////////// + +inline _variant_t::~_variant_t() throw() +{ + ::VariantClear(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Mutually-dependent definitions +// +////////////////////////////////////////////////////////////////////////////////////////// + +// Construct a _bstr_t from a const _variant_t& +// +inline _bstr_t::_bstr_t(const _variant_t &var) + : m_Data(NULL) +{ + if (V_VT(&var) == VT_BSTR) { + *this = V_BSTR(&var); + return; + } + + _variant_t varDest; + varDest.ChangeType(VT_BSTR, &var); + + *this = V_BSTR(&varDest); +} + +// Assign a const _variant_t& to a _bstr_t +// +inline _bstr_t& _bstr_t::operator=(const _variant_t &var) +{ + if (V_VT(&var) == VT_BSTR) { + *this = V_BSTR(&var); + return *this; + } + + _variant_t varDest; + varDest.ChangeType(VT_BSTR, &var); + + *this = V_BSTR(&varDest); + + return *this; +} + +extern _variant_t vtMissing; + +#ifndef _USE_RAW +#define bstr_t _bstr_t +#define variant_t _variant_t +#endif + +#pragma pop_macro("new") + +#pragma warning(pop) + +#endif // _INC_COMUTIL diff --git a/test_data/lots_of_files/concrt.h b/test_data/lots_of_files/concrt.h new file mode 100644 index 0000000..75c13a8 --- /dev/null +++ b/test_data/lots_of_files/concrt.h @@ -0,0 +1,5867 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* concrt.h +* +* Main public header file for ConcRT. This is the only header file a C++ program must include to use the core concurrency runtime features. +* +* The Agents And Message Blocks Library and the Parallel Patterns Library (PPL) are defined in separate header files. +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include <crtdefs.h> + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) + #error ERROR: Concurrency Runtime is supported only on X64, X86 and ARM architectures. +#endif /* !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_M_CEE) + #error ERROR: Concurrency Runtime is not supported when compiling /clr. +#endif /* defined (_M_CEE) */ + +#ifndef __cplusplus + #error ERROR: Concurrency Runtime is supported only for C++. +#endif /* __cplusplus */ + +#define _CONCRT_H + +#include <exception> +#include <sal.h> +#include <limits.h> +#include <crtdbg.h> +#include <guiddef.h> +#include <intrin.h> +#include <new> + +#pragma pack(push,_CRT_PACKING) +#pragma push_macro("new") +#undef new + +// Forward declare structs needed from Windows header files + +struct _SECURITY_ATTRIBUTES; +typedef _SECURITY_ATTRIBUTES* LPSECURITY_ATTRIBUTES; + +struct _GROUP_AFFINITY; +typedef _GROUP_AFFINITY* PGROUP_AFFINITY; + +// Define essential types needed from Windows header files + +typedef unsigned long DWORD; +#ifndef _HRESULT_DEFINED +#define _HRESULT_DEFINED +#ifdef __midl +typedef LONG HRESULT; +#else /* __midl */ +typedef __success(return >= 0) long HRESULT; +#endif /* __midl */ +#endif /* _HRESULT_DEFINED */ +typedef void * HANDLE; + +// Undefine Yield that is possibly defined by windows.h, and _YieldProcessor + +#undef Yield +#undef _YieldProcessor + +#if (defined (_M_IX86) || defined (_M_X64)) +#define _YieldProcessor _mm_pause +#else /* (defined (_M_IX86) || defined (_M_X64)) */ +inline void _YieldProcessor() {} +#endif /* (defined (_M_IX86) || defined (_M_X64)) */ + +// Make sure the exchange pointer intrinsics works on x86 architecture + +#if defined (_M_IX86) && !defined(FIXED_592562) // Leave enabled until onflict with inline function in 8.1 SDK winnt.h header is fixed + +#undef _InterlockedExchangePointer +#undef _InterlockedCompareExchangePointer + +#define _InterlockedExchangePointer(_Target, _Value) reinterpret_cast<void *>(static_cast<__w64 long>(_InterlockedExchange( \ + static_cast<long volatile *>(reinterpret_cast<__w64 long volatile *>(static_cast<void * volatile *>(_Target))), \ + static_cast<long>(reinterpret_cast<__w64 long>(static_cast<void *>(_Value)))))) + +#define _InterlockedCompareExchangePointer(_Target, _Exchange, _Comparand) reinterpret_cast<void *>(static_cast<__w64 long>(_InterlockedCompareExchange( \ + static_cast<long volatile *>(reinterpret_cast<__w64 long volatile *>(static_cast<void * volatile *>(_Target))), \ + static_cast<long>(reinterpret_cast<__w64 long>(static_cast<void *>(_Exchange))), \ + static_cast<long>(reinterpret_cast<__w64 long>(static_cast<void *>(_Comparand)))))) + +#endif /* defined (_M_IX86) */ + +#if (defined (_M_IX86) || defined (_M_ARM)) + +#define _InterlockedIncrementSizeT(_Target) static_cast<size_t>(_InterlockedIncrement(reinterpret_cast<long volatile *>(_Target))) +#define _InterlockedDecrementSizeT(_Target) static_cast<size_t>(_InterlockedDecrement(reinterpret_cast<long volatile *>(_Target))) +#define _InterlockedCompareExchangeSizeT(_Target, _Exchange, _Comparand) static_cast<size_t>(_InterlockedCompareExchange( \ + reinterpret_cast<long volatile *>(_Target), \ + static_cast<long>(_Exchange), \ + static_cast<long>(_Comparand))) + +typedef _W64 unsigned long DWORD_PTR, *PDWORD_PTR; + +#else /* (defined (_M_IX86) || defined (_M_ARM)) */ + +#define _InterlockedIncrementSizeT(_Target) static_cast<size_t>(_InterlockedIncrement64(reinterpret_cast<__int64 volatile *>(_Target))) +#define _InterlockedDecrementSizeT(_Target) static_cast<size_t>(_InterlockedDecrement64(reinterpret_cast<__int64 volatile *>(_Target))) +#define _InterlockedCompareExchangeSizeT(_Target, _Exchange, _Comparand) static_cast<size_t>(_InterlockedCompareExchange64( \ + reinterpret_cast<__int64 volatile *>(_Target), \ + static_cast<__int64>(_Exchange), \ + static_cast<__int64>(_Comparand))) + +typedef unsigned __int64 DWORD_PTR, *PDWORD_PTR; + +#endif /* (defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_DEBUG) +#if _MSC_VER +// Turn off compiler warnings that are exacerbated by constructs in this +// file's definitions: + +// Warning C4127: conditional expression is constant. This is caused by +// the macros with "do { ... } while (false)" syntax. The syntax is +// a good way to ensure that a statement-like macro can be used in all +// contexts (specifically if statements), but the compiler warns about +// the "while (false)" part. + +#define _CONCRT_ASSERT(x) __pragma (warning (suppress: 4127)) do {_ASSERTE(x); __assume(x);} while(false) +#else +#define _CONCRT_ASSERT(x) do {_ASSERTE(x); __assume(x);} while(false) +#endif +#else /* defined (_DEBUG) */ +#define _CONCRT_ASSERT(x) __assume(x) +#endif /* defined (_DEBUG) */ + +// Used internally to represent the smallest unit in which to allocate hidden types + + +typedef void * _CONCRT_BUFFER; +#define _LISTENTRY_SIZE ((2 * sizeof(void *) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)) +#define _SAFERWLIST_SIZE ((3 * sizeof(void *) + 2 * sizeof(long) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)) + +/// <summary> +/// The <c>Concurrency</c> namespace provides classes and functions that access the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ +/// <summary> +/// Pauses the current context for a specified amount of time. +/// </summary> +/// <param name="_Milliseconds"> +/// The number of milliseconds the current context should be paused for. If the <paramref name="_Milliseconds"/> parameter is set to +/// the value <c>0</c>, the current context should yield execution to other runnable contexts before continuing. +/// </param> +/// <remarks> +/// If this method is called on a Concurrency Runtime scheduler context, the scheduler will find a different context to run on the underlying +/// resource. Because the scheduler is cooperative in nature, this context cannot resume exactly after the number of milliseconds specified. +/// If the scheduler is busy executing other tasks that do not cooperatively yield to the scheduler, the wait period could be +/// indefinite. +/// </remarks> +/**/ +_CRTIMP void __cdecl wait(unsigned int _Milliseconds); + +/// <summary> +/// Allocates a block of memory of the size specified from the Concurrency Runtime Caching Suballocator. +/// </summary> +/// <param name="_NumBytes"> +/// The number of bytes of memory to allocate. +/// </param> +/// <returns> +/// A pointer to newly allocated memory. +/// </returns> +/// <remarks> +/// For more information about which scenarios in your application could benefit from using the Caching Suballocator, +/// see <see cref="Task Scheduler (Concurrency Runtime)"/>. +/// </remarks> +/// <seealso cref="Concurrency::Free Function"/> +/**/ +_CRTIMP void * __cdecl Alloc(size_t _NumBytes); + +/// <summary> +/// Releases a block of memory previously allocated by the <c>Alloc</c> method to the Concurrency Runtime Caching Suballocator. +/// </summary> +/// <param name="_PAllocation"> +/// A pointer to memory previously allocated by the <c>Alloc</c> method which is to be freed. If the parameter <paramref name="_PAllocation"/> +/// is set to the value <c>NULL</c>, this method will ignore it and return immediately. +/// </param> +/// <remarks> +/// For more information about which scenarios in your application could benefit from using the Caching Suballocator, +/// see <see cref="Task Scheduler (Concurrency Runtime)"/>. +/// </remarks> +/// <seealso cref="Concurrency::Alloc Function"/> +/**/ +_CRTIMP void __cdecl Free(_Pre_maybenull_ _Post_invalid_ void * _PAllocation); + +/// <summary> +/// Concurrency::details contains definitions of support routines in the public namespaces and one or more macros. +/// Users should not directly interact with this internal namespace. +/// </summary> +/**/ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +/// <summary> +/// Restricts the execution resources used by the Concurrency Runtime internal worker threads to the affinity set specified. +/// <para> It is valid to call this method only before the Resource Manager has been created, or between two Resource Manager lifetimes. +/// It can be invoked multiple times as long as the Resource Manager does not exist at the time of invocation. After an affinity limit +/// has been set, it remains in effect until the next valid call to the <c>set_task_execution_resources</c> method.</para> +/// <para>The affinity mask provided need not be a subset of the process affinity mask. The process affinity will be updated if necessary.</para> +/// </summary> +/// <param name="_ProcessAffinityMask"> +/// The affinity mask that the Concurrency Runtime worker threads are to be restricted to. Use this method on a system with greater than 64 +/// hardware threads only if you want to limit the Concurrency Runtime to a subset of the current processor group. In general, you should +/// use the version of the method that accepts an array of group affinities as a parameter, to restrict affinity on machines with greater +/// than 64 hardware threads. +/// </param> +/// <remarks> +/// The method will throw an <see cref="invalid_operation Class">invalid_operation </see> exception if a Resource Manager is present at +/// the time it is invoked, and an <see cref="invalid_argument">invalid_argument </see> exception if the affinity specified results in an empty set of resources. +/// <para>The version of the method that takes an array of group affinities as a parameter should only be used on operating systems with version +/// Windows 7 or higher. Otherwise, an <see cref="invalid_operation Class">invalid_operation </see> exception is thrown.</para> +/// <para>Programatically modifying the process affinity after this method has been invoked will not cause the Resource Manager to re-evaluate +/// the affinity it is restricted to. Therefore, all changes to process affinity should be made before calling this method.</para> +/// </remarks> +/**/ +_CRTIMP void __cdecl set_task_execution_resources(DWORD_PTR _ProcessAffinityMask); + +/// <summary> +/// Restricts the execution resources used by the Concurrency Runtime internal worker threads to the affinity set specified. +/// <para> It is valid to call this method only before the Resource Manager has been created, or between two Resource Manager lifetimes. +/// It can be invoked multiple times as long as the Resource Manager does not exist at the time of invocation. After an affinity limit +/// has been set, it remains in effect until the next valid call to the <c>set_task_execution_resources</c> method.</para> +/// <para>The affinity mask provided need not be a subset of the process affinity mask. The process affinity will be updated if necessary.</para> +/// </summary> +/// <param name="_Count"> +/// The number of <c>GROUP_AFFINITY</c> entries in the array specified by the parameter <paramref name="_PGroupAffinity"/>. +/// </param> +/// <param name="_PGroupAffinity"> +/// An array of <c>GROUP_AFFINITY</c> entries. +/// </param> +/// <remarks> +/// The method will throw an <see cref="invalid_operation Class">invalid_operation </see> exception if a Resource Manager is present at +/// the time it is invoked, and an <see cref="invalid_argument">invalid_argument </see> exception if the affinity specified results in an empty set of resources. +/// <para>The version of the method that takes an array of group affinities as a parameter should only be used on operating systems with version +/// Windows 7 or higher. Otherwise, an <see cref="invalid_operation Class">invalid_operation </see> exception is thrown.</para> +/// <para>Programatically modifying the process affinity after this method has been invoked will not cause the Resource Manager to re-evaluate +/// the affinity it is restricted to. Therefore, all changes to process affinity should be made before calling this method.</para> +/// </remarks> +/**/ +_CRTIMP void __cdecl set_task_execution_resources(unsigned short _Count, PGROUP_AFFINITY _PGroupAffinity); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/// <summary> +/// An elementary abstraction for a task, defined as <c>void (__cdecl * TaskProc)(void *)</c>. A <c>TaskProc</c> is called to +/// invoke the body of a task. +/// </summary> +/**/ +typedef void (__cdecl * TaskProc)(void *); + +// +// Forward declarations: +// +class Scheduler; +class ScheduleGroup; +class Context; + +namespace details +{ + // + // Forward declarations: + // + class ContextBase; + class _TaskCollectionBase; + + // + // A utility to hide operator delete from certain objects while still allowing the runtime to delete them internally. + // + template<class _T> + void _InternalDeleteHelper(_T * _PObject) + { + delete _PObject; + } + + // The purpose of the class is solely to direct allocations of ConcRT classes + // through a single point, using an internal allocator. + struct _AllocBase + { + // Standard operator new + void * operator new(size_t _Size) + { + return Concurrency::Alloc(_Size); + } + + // Standard operator delete + void operator delete(void * _Ptr) throw() + { + Concurrency::Free(_Ptr); + } + + // Standard operator new, no-throw version + void * operator new(size_t _Size, const std::nothrow_t&) throw() + { + void * _Ptr; + + try + { + _Ptr = Concurrency::Alloc(_Size); + } + catch(...) + { + _Ptr = NULL; + } + + return (_Ptr); + } + + // Standard operator delete, no-throw version + void operator delete(void * _Ptr, const std::nothrow_t&) throw() + { + operator delete(_Ptr); + } + + // Standard operator new array + void * operator new[](size_t _Size) + { + return operator new(_Size); + } + + // Standard operator delete array + void operator delete[](void * _Ptr) throw() + { + operator delete(_Ptr); + } + + // Standard operator new array, no-throw version + void * operator new[](size_t _Size, const std::nothrow_t& _No_throw) throw () + { + return operator new(_Size, _No_throw); + } + + // Standard operator delete array, no-throw version + void operator delete[](void * _Ptr, const std::nothrow_t& _No_throw) throw() + { + operator delete(_Ptr, _No_throw); + } + + // Standard operator new with void* placement + void * operator new(size_t, void * _Location) throw() + { + return _Location; + } + + // Standard operator delete with void* placement + void operator delete(void *, void *) throw() + { + } + + // Standard operator new array with void* placement + void * __cdecl operator new[](size_t, void * _Location) throw() + { + return _Location; + } + + // Standard operator delete array with void* placement + void __cdecl operator delete[](void *, void *) throw() + { + } + }; + + // Stubs to allow the header files to access runtime functionality for WINAPI_PARTITION apps. + class _Context + { + public: + _CRTIMP _Context(::Concurrency::Context * _PContext = NULL) : _M_pContext(_PContext) {} + _CRTIMP static _Context __cdecl _CurrentContext(); + _CRTIMP static void __cdecl _Yield(); + _CRTIMP static void __cdecl _Oversubscribe(bool _BeginOversubscription); + _CRTIMP bool _IsSynchronouslyBlocked() const; + private: + ::Concurrency::Context * _M_pContext; + }; + + class _Scheduler + { + public: + _CRTIMP _Scheduler(::Concurrency::Scheduler * _PScheduler = NULL) : _M_pScheduler(_PScheduler) {} + _CRTIMP unsigned int _Reference(); + _CRTIMP unsigned int _Release(); + _CRTIMP Concurrency::Scheduler * _GetScheduler() { return _M_pScheduler; } + + private: + ::Concurrency::Scheduler * _M_pScheduler; + }; + + class _CurrentScheduler + { + public: + _CRTIMP static void __cdecl _ScheduleTask(TaskProc _Proc, void * _Data); + _CRTIMP static unsigned int __cdecl _Id(); + _CRTIMP static unsigned int __cdecl _GetNumberOfVirtualProcessors(); + _CRTIMP static _Scheduler __cdecl _Get(); + }; + + // + // Wrappers for atomic access + // + template <size_t _Size> + struct _Subatomic_impl { }; + + template<> + struct _Subatomic_impl<4> { + template <typename _Ty> + static void _StoreWithRelease(volatile _Ty& _Location, _Ty _Rhs) { + // For the compiler, a volatile write has release semantics. In addition, on ARM, + // the volatile write will emit a data memory barrier before the write. + _Location = _Rhs; + } + + template <typename _Ty> + static _Ty _LoadWithAquire(volatile _Ty& _Location) { + // For the compiler, a volatile read has acquire semantics. In addition, on ARM, + // the volatile read will emit a data memory barrier after the read. + return _Location; + } + + template <typename _Ty> + static _Ty _CompareAndSwap(volatile _Ty& _Location, _Ty _NewValue, _Ty _Comperand) { + return (_Ty)_InterlockedCompareExchange((volatile long*)&_Location, (long)_NewValue, (long)_Comperand); + } + + template <typename _Ty> + static _Ty _FetchAndAdd(volatile _Ty& _Location, _Ty _Addend) { + return (_Ty)_InterlockedExchangeAdd((volatile long*)&_Location, (long)_Addend); + } + + template <typename _Ty> + static _Ty _Increment(volatile _Ty& _Location) { + return (_Ty)_InterlockedIncrement((volatile long*)&_Location); + } + + template <typename _Ty> + static _Ty _Decrement(volatile _Ty& _Location) { + return (_Ty)_InterlockedDecrement((volatile long*)&_Location); + } + }; + +#if defined (_M_X64) + template<> + struct _Subatomic_impl<8> { + template <typename _Ty> + static void _StoreWithRelease(volatile _Ty& _Location, _Ty _Rhs) { + // For the compiler, a volatile write has release semantics. + _Location = _Rhs; + } + + template <typename _Ty> + static _Ty _LoadWithAquire(volatile _Ty& _Location) { + // For the compiler, a volatile read has acquire semantics. + return _Location; + } + + template <typename _Ty> + static _Ty _CompareAndSwap(volatile _Ty& _Location, _Ty _NewValue, _Ty _Comperand) { + return (_Ty)_InterlockedCompareExchange64((volatile __int64*)&_Location, (__int64)_NewValue, (__int64)_Comperand); + } + + template <typename _Ty> + static _Ty _FetchAndAdd(volatile _Ty& _Location, _Ty _Addend) { + return (_Ty)_InterlockedExchangeAdd64((volatile __int64*)&_Location, (__int64)_Addend); + } + + template <typename _Ty> + static _Ty _Increment(volatile _Ty& _Location) { + return (_Ty)_InterlockedIncrement64((volatile __int64*)&_Location); + } + + template <typename _Ty> + static _Ty _Decrement(volatile _Ty& _Location) { + return (_Ty)_InterlockedDecrement64((volatile __int64*)&_Location); + } + }; +#endif /* defined (_M_X64) */ + + + // + // Wrapper for atomic access. Only works for 4-byte or 8-byte types (for example, int, long, long long, size_t, pointer). + // Anything else might fail to compile. + // + template <typename _Ty> + class _Subatomic { + private: + volatile _Ty _M_value; + + public: + operator _Ty() const volatile { + return _Subatomic_impl<sizeof(_Ty)>::_LoadWithAquire(_M_value); + } + + _Ty operator=(_Ty _Rhs) { + _Subatomic_impl<sizeof(_Ty)>::_StoreWithRelease(_M_value, _Rhs); + return _Rhs; + } + + _Ty _CompareAndSwap(_Ty _NewValue, _Ty _Comperand) { + return _Subatomic_impl<sizeof(_Ty)>::_CompareAndSwap(_M_value, _NewValue, _Comperand); + } + + _Ty _FetchAndAdd(_Ty _Addend) { + return _Subatomic_impl<sizeof(_Ty)>::_FetchAndAdd(_M_value, _Addend); + } + + _Ty operator++() { + return _Subatomic_impl<sizeof(_Ty)>::_Increment(_M_value); + } + + _Ty operator++(int) { + return _Subatomic_impl<sizeof(_Ty)>::_Increment(_M_value) - 1; + } + + _Ty operator--() { + return _Subatomic_impl<sizeof(_Ty)>::_Decrement(_M_value); + } + + _Ty operator--(int) { + return _Subatomic_impl<sizeof(_Ty)>::_Decrement(_M_value) + 1; + } + + _Ty operator+=(_Ty _Addend) { + return _FetchAndAdd(_Addend) + _Addend; + } + }; + + // + // An internal exception that is used for cancellation. Users do not "see" this exception except through the + // resulting stack unwind. This exception should never be intercepted by user code. It is intended + // for use by the runtime only. + // + class _Interruption_exception : public std::exception + { + public: + explicit _CRTIMP _Interruption_exception(const char * _Message) throw(); + _CRTIMP _Interruption_exception() throw(); + }; + + // + // An RAII class that spin-waits on a "rented" flag. + // + class _SpinLock + { + private: + volatile long& _M_flag; + + public: + _CRTIMP _SpinLock(volatile long& _Flag); + _CRTIMP ~_SpinLock(); + + private: + _SpinLock(const _SpinLock&); + void operator=(const _SpinLock&); + }; + + // + // A class that holds the count used for spinning and is dependent + // on the number of hardware threads + // + struct _SpinCount + { + // Initializes the spinCount to either 0 or SPIN_COUNT, depending on + // the number of hardware threads. + static void __cdecl _Initialize(); + + // Returns the current value of s_spinCount + _CRTIMP static unsigned int __cdecl _Value(); + + // The number of iterations used for spinning + static unsigned int _S_spinCount; + }; + + /// <summary> + /// Default method for yielding during a spin wait + /// </summary> + /**/ + void _CRTIMP __cdecl _UnderlyingYield(); + + /// <summary> + /// Returns the hardware concurrency available to the Concurrency Runtime, taking into account process affinity, or any restrictions + /// in place because of the set_task_execution_resources method. + /// </summary> + /**/ + unsigned int _CRTIMP __cdecl _GetConcurrency(); + + /// <summary> + /// Implements busy wait with no backoff + /// </summary> + /**/ + template<unsigned int _YieldCount = 1> + class _CRTIMP _SpinWait + { + public: + + typedef void (__cdecl *_YieldFunction)(); + + /// <summary> + /// Construct a spin wait object + /// </summary> + /**/ + _SpinWait(_YieldFunction _YieldMethod = _UnderlyingYield) + : _M_yieldFunction(_YieldMethod), _M_state(_StateInitial) + { + // Defer initialization of other fields to _SpinOnce(). + } + + /// <summary> + /// Set a dynamic spin count. + /// </summary> + /**/ + void _SetSpinCount(unsigned int _Count) + { + _CONCRT_ASSERT(_M_state == _StateInitial); + if (_Count == 0) + { + // Specify a count of 0 if we are on a single proc. + _M_state = _StateSingle; + } + else + { + _M_currentSpin = _Count; + _M_currentYield = _YieldCount; + _M_state = _StateSpin; + } + } + + /// <summary> + /// Spins for one time quantum,until a maximum spin is reached. + /// </summary> + /// <returns> + /// false if spin count has reached steady state, true otherwise. + /// </returns> + /// <remarks> + /// If the spin count is not changing do not spin again + /// because there is either only one processor, or the maximum spin has been reached and blocking is + /// probably a better solution. However, if called again, SpinOnce will spin for a maximum spin count. + /// </remarks> + /**/ + bool _SpinOnce() + { + switch (_M_state) + { + case _StateSpin: + { + unsigned long _Count = _NumberOfSpins(); + + for (unsigned long _I = 0; _I < _Count; _I++) + { + _YieldProcessor(); + } + + if (!_ShouldSpinAgain()) + { + _M_state = (_M_currentYield == 0) ? _StateBlock : _StateYield; + } + + return true; + } + + case _StateYield: + _CONCRT_ASSERT(_M_currentYield > 0); + if (--_M_currentYield == 0) + { + _M_state = _StateBlock; + } + + // Execute the yield + _DoYield(); + return true; + + case _StateBlock: + // Reset to defaults if client does not block + _Reset(); + return false; + + case _StateSingle: + // No need to spin on a single processor: just execute the yield + _DoYield(); + return false; + + case _StateInitial: + // Reset counters to their default value and Spin once. + _Reset(); + return _SpinOnce(); + default: + // Unreached + return false; + }; + } + + protected: + + /// <summary> + /// State of the spin wait class. + /// </summary> + /**/ + enum _SpinState + { + _StateInitial, + _StateSpin, + _StateYield, + _StateBlock, + _StateSingle + }; + + /// <summary> + /// Yields its time slice using the specified yieldFunciton + /// </summary> + /**/ + void _DoYield() + { + bool _ShouldYield = (_YieldCount != 0); + if (_ShouldYield) + { + _CONCRT_ASSERT(_M_yieldFunction != NULL); + _M_yieldFunction(); + } + else + { + _YieldProcessor(); + } + } + + /// <summary> + /// Resets the counts and state to the default. + /// </summary> + /**/ + void _Reset() + { + _M_state = _StateInitial; + + // Reset to the default spin value. The value specified + // by the client is ignored on a reset. + _SetSpinCount(_SpinCount::_Value()); + + _CONCRT_ASSERT(_M_state != _StateInitial); + } + + /// <summary> + /// Determines the current spin count + /// </summary> + /// <returns> + /// The number of spins to execute for this iteration + /// </returns> + /**/ + unsigned long _NumberOfSpins() + { + return 1; + } + + /// <summary> + /// Determines whether maximum spin has been reached + /// </summary> + /// <returns> + /// false if spin count has reached steady state, true otherwise. + /// </returns> + /**/ + bool _ShouldSpinAgain() + { + return (--_M_currentSpin > 0); + } + + unsigned long _M_currentSpin; + unsigned long _M_currentYield; + _SpinState _M_state; + _YieldFunction _M_yieldFunction; + }; + + typedef _SpinWait<> _SpinWaitBackoffNone; + typedef _SpinWait<0> _SpinWaitNoYield; + + // + // This reentrant lock uses CRITICAL_SECTION and is intended for use when kernel blocking + // is desirable and where it is either known that the lock will be taken recursively in + // the same thread, or not known that a non-reentrant lock can be used safely. + // + class _ReentrantBlockingLock + { + public: + // Constructor for _ReentrantBlockingLock + _CRTIMP _ReentrantBlockingLock(); + + // Destructor for _ReentrantBlockingLock + _CRTIMP ~_ReentrantBlockingLock(); + + // Acquire the lock, spin if necessary + _CRTIMP void _Acquire(); + + // Tries to acquire the lock, does not spin. + // Returns true if the acquisition worked, false otherwise + _CRTIMP bool _TryAcquire(); + + // Releases the lock + _CRTIMP void _Release(); + + + // An exception safe RAII wrapper. + class _Scoped_lock + { + public: + // Constructs a holder and acquires the specified lock + explicit _Scoped_lock(_ReentrantBlockingLock& _Lock) : _M_lock(_Lock) + { + _M_lock._Acquire(); + } + + // Destroys the holder and releases the lock + ~_Scoped_lock() + { + _M_lock._Release(); + } + private: + _ReentrantBlockingLock& _M_lock; + + _Scoped_lock(const _Scoped_lock&); // no copy constructor + _Scoped_lock const & operator=(const _Scoped_lock&); // no assignment operator + }; + + private: + // Critical section requires windows.h. Hide the implementation so that + // user code need not include windows. + _CONCRT_BUFFER _M_criticalSection[(4 * sizeof(void *) + 2 * sizeof(long) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + }; + + // + // This reentrant lock is a pure spin lock and is intended for use when kernel blocking + // is desirable and where it is either known that the lock will be taken recursively in + // the same thread, or not known that a non-reentrant lock can be used safely. + // + class _ReentrantLock + { + public: + // Constructor for _ReentrantLock + _CRTIMP _ReentrantLock(); + + // Acquire the lock, spin if necessary + _CRTIMP void _Acquire(); + + // Tries to acquire the lock, does not spin + // Returns true if the acquisition worked, false otherwise + _CRTIMP bool _TryAcquire(); + + // Releases the lock + _CRTIMP void _Release(); + + // An exception safe RAII wrapper. + class _Scoped_lock + { + public: + // Constructs a holder and acquires the specified lock + explicit _Scoped_lock(_ReentrantLock& _Lock) : _M_lock(_Lock) + { + _M_lock._Acquire(); + } + + // Destroys the holder and releases the lock + ~_Scoped_lock() + { + _M_lock._Release(); + } + private: + _ReentrantLock& _M_lock; + + _Scoped_lock(const _Scoped_lock&); // no copy constructor + _Scoped_lock const & operator=(const _Scoped_lock&); // no assignment operator + }; + + private: + long _M_recursionCount; + volatile long _M_owner; + }; + + // + // This non-reentrant lock uses CRITICAL_SECTION and is intended for use in situations + // where it is known that the lock will not be taken recursively, and can be more + // efficiently implemented. + // + class _NonReentrantBlockingLock + { + public: + // Constructor for _NonReentrantBlockingLock + // + // The constructor is exported because _NonReentrantLock is + // included in DevUnitTests. + _CRTIMP _NonReentrantBlockingLock(); + + // Constructor for _NonReentrantBlockingLock + _CRTIMP ~_NonReentrantBlockingLock(); + + // Acquire the lock, spin if necessary + _CRTIMP void _Acquire(); + + // Tries to acquire the lock, does not spin + // Returns true if the lock is taken, false otherwise + _CRTIMP bool _TryAcquire(); + + // Releases the lock + _CRTIMP void _Release(); + + // An exception safe RAII wrapper. + class _Scoped_lock + { + public: + // Constructs a holder and acquires the specified lock + explicit _Scoped_lock(_NonReentrantBlockingLock& _Lock) : _M_lock(_Lock) + { + _M_lock._Acquire(); + } + + // Destroys the holder and releases the lock + ~_Scoped_lock() + { + _M_lock._Release(); + } + private: + _NonReentrantBlockingLock& _M_lock; + + _Scoped_lock(const _Scoped_lock&); // no copy constructor + _Scoped_lock const & operator=(const _Scoped_lock&); // no assignment operator + }; + + private: + // Critical section requires windows.h. Hide the implementation so that + // user code need not include windows.h + _CONCRT_BUFFER _M_criticalSection[(4 * sizeof(void *) + 2 * sizeof(long) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + }; + + // + // A Reader-Writer Lock is intended for use in situations with many readers and rare + // writers. + // + // A writer request immediately blocks future readers and then waits until all current + // readers drain. A reader request does not block future writers and must wait until + // all writers are done, even those that cut in front of it. In any race between a + // reader and a writer, the writer always wins. + // + class _ReaderWriterLock + { + public: + // Constructor for _ReaderWriterLock + // + // The constructor and destructor are exported because _ReaderWriterLock is + // included in DevUnitTests. + _CRTIMP _ReaderWriterLock(); + + // Acquire lock for reading. Spins until all writers finish, new writers + // can cut in front of a waiting reader. + _CRTIMP void _AcquireRead(); + + // Release lock for reading. The last reader changes m_state to State.kFree + _CRTIMP void _ReleaseRead(); + + // Acquire lock for writing. Spin until no readers exist, then acquire lock + // and prevent new readers. + _CRTIMP void _AcquireWrite(); + + // Release lock for writing. + _CRTIMP void _ReleaseWrite(); + + // Try to acquire the write lock, do not spin if unable to acquire. + // Returns true if the acquisition worked, false otherwise + _CRTIMP bool _TryAcquireWrite(); + + // Returns true if it is in write state, false otherwise + bool _HasWriteLock() const + { + return (_M_state == _Write); + } + + // Guarantees that all writers are out of the lock. This does nothing if there are no pending writers. + void _FlushWriteOwners(); + + // An exception safe RAII wrapper. + class _Scoped_lock + { + public: + // Constructs a holder and acquires the writer lock + explicit _Scoped_lock(_ReaderWriterLock& _Lock) : _M_lock(_Lock) + { + _M_lock._AcquireWrite(); + } + + // Destroys the holder and releases the writer lock + ~_Scoped_lock() + { + _M_lock._ReleaseWrite(); + } + + private: + + _ReaderWriterLock& _M_lock; + + _Scoped_lock(const _Scoped_lock&); // no copy constructor + _Scoped_lock const & operator=(const _Scoped_lock&); // no assignment operator + }; + + // An exception safe RAII wrapper for reads. + class _Scoped_lock_read + { + public: + // Constructs a holder and acquires the reader lock + explicit _Scoped_lock_read(_ReaderWriterLock& _Lock) : _M_lock(_Lock) + { + _M_lock._AcquireRead(); + } + + // Destroys the holder and releases the reader lock + ~_Scoped_lock_read() + { + _M_lock._ReleaseRead(); + } + + private: + + _ReaderWriterLock& _M_lock; + + _Scoped_lock_read(const _Scoped_lock_read&); // no copy constructor + _Scoped_lock_read const & operator=(const _Scoped_lock_read&); // no assignment operator + }; + + private: + // State enum where: + // -1 --> write mode + // 0 --> free + // n > 0 --> n readers have locked in read mode. + enum _State + { + _Write = -1, + _Free = 0, + _Read = 1 + }; + + // The current state of the lock, mapping to the State enum. This is also + // an indicator of the number of readers holding the lock, for any number > 0. + volatile long _M_state; + + // A writer increments this as soon as it wants to lock and decrements this + // after releasing the lock. To prevent writers from starving, a reader will + // wait until this counter is zero, and only then will try to obtain the lock. + volatile long _M_numberOfWriters; + + // Spin-Wait-Until variant + static void __cdecl _WaitEquals(volatile const long& _Location, long _Value, long _Mask = 0xFFFFFFFF); + }; + + // + // Exception safe RAII wrappers for _malloca() + // + + // + // _MallocaArrayHolder is used when the allocation size is known up front, and the memory must be allocated in a contiguous space + // + template<typename _ElemType> + class _MallocaArrayHolder + { + public: + + _MallocaArrayHolder() : _M_ElemArray(NULL), _M_ElemsConstructed(0) {} + + // _Initialize takes the pointer to the memory allocated by the user using _malloca + void _Initialize(_ElemType * _Elem) + { + // The object must be initialized exactly once + _CONCRT_ASSERT(_M_ElemArray == NULL && _M_ElemsConstructed == 0); + _M_ElemArray = _Elem; + _M_ElemsConstructed = 0; + } + + // _InitOnRawMalloca take the raw pointer returned by _malloca directly + // It will initialize itself with that pointer and return a strong typed pointer. + // To be noted that the constructor will NOT be called. + _ElemType * _InitOnRawMalloca(void * _MallocaRet) + { + if (_MallocaRet == nullptr) + throw std::bad_alloc(); + _Initialize(static_cast<_ElemType *>(_MallocaRet)); + return static_cast<_ElemType *>(_MallocaRet); + } + + // Register the next slot for destruction. Because we only keep the index of the last slot to be destructed, + // this method must be called sequentially from 0 to N where N < _ElemCount. + void _IncrementConstructedElemsCount() + { + _CONCRT_ASSERT(_M_ElemArray != NULL); // must already be initialized + _M_ElemsConstructed++; + } + + virtual ~_MallocaArrayHolder() + { + for( size_t _I=0; _I < _M_ElemsConstructed; ++_I ) + { + _M_ElemArray[_I]._ElemType::~_ElemType(); + } + // Works even when object was not initialized, that is, _M_ElemArray == NULL + _freea(_M_ElemArray); + } + private: + _ElemType * _M_ElemArray; + size_t _M_ElemsConstructed; + + // Copy construction and assignment are not supported. + _MallocaArrayHolder(const _MallocaArrayHolder & ); + _MallocaArrayHolder& operator = (const _MallocaArrayHolder & ); + }; + + // + // _MallocaListHolder is used when the allocation size is not known up front, and the elements are added to the list dynamically + // + template<typename _ElemType> + class _MallocaListHolder + { + public: + // Returns the size required to allocate the payload itself and the pointer to the next element + size_t _GetAllocationSize() const + { + return sizeof(_ElemNodeType); + } + + _MallocaListHolder() : _M_FirstNode(NULL) + { + } + + // Add the next element to the list. The memory is allocated in the caller's frame by _malloca + void _AddNode(_ElemType * _Elem) + { + _ElemNodeType * _Node = reinterpret_cast<_ElemNodeType *>(_Elem); + _Node->_M_Next = _M_FirstNode; + _M_FirstNode = reinterpret_cast<_ElemNodeType *>(_Elem); + } + + // _AddRawMallocaNode take the raw pointer returned by _malloca directly + // It will add that bucket of memory to the list and return a strong typed pointer. + // To be noted that the constructor will NOT be called. + _ElemType * _AddRawMallocaNode(void * _MallocaRet) + { + if (_MallocaRet == nullptr) + throw std::bad_alloc(); + _AddNode(static_cast<_ElemType *>(_MallocaRet)); + return static_cast<_ElemType *>(_MallocaRet); + } + + // Walk the list and destruct, then free each element + _At_(this->_M_FirstNode, _Pre_valid_) virtual ~_MallocaListHolder() + { + for( _ElemNodeType * _Node = _M_FirstNode; _Node != NULL; ) + { + auto _M_Next = _Node->_M_Next; + _Node->_M_Elem._ElemType::~_ElemType(); + _freea(_Node); + _Node = _M_Next; + } + } + + private: + + class _ElemNodeType + { + friend class _MallocaListHolder; + _ElemType _M_Elem; + _ElemNodeType * _M_Next; + // Always instantiated using malloc, so default constructor and destructor are not needed. + _ElemNodeType(); + ~_ElemNodeType(); + // Copy construction and assignment are not supported. + _ElemNodeType(const _ElemNodeType & ); + _ElemNodeType & operator = (const _ElemNodeType & ); + }; + + _ElemNodeType* _M_FirstNode; + + // Copy construction and assignment are not supported. + _MallocaListHolder(const _MallocaListHolder & ); + _MallocaListHolder & operator = (const _MallocaListHolder & ); + }; + + // Forward declarations + class _StructuredTaskCollection; + class _TaskCollection; + class _UnrealizedChore; +} // namespace details + +//************************************************************************** +// Public Namespace: +// +// Anything in the Concurrency namespace is intended for direct client consumption. +// +//************************************************************************** + +/// <summary> +/// This class describes an exception thrown because of a failure to acquire a critical resource in the Concurrency Runtime. +/// </summary> +/// <remarks> +/// This exception is typically thrown when a call to the operating system from within the Concurrency Runtime +/// fails. The error code which would normally be returned from a call to the Win32 method <c>GetLastError</c> is +/// converted to a value of type <c>HRESULT</c> and can be retrieved using the <c>get_error_code</c> method. +/// </remarks> +/**/ +class scheduler_resource_allocation_error : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>scheduler_resource_allocation_error</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /// <param name="_Hresult"> + /// The <c>HRESULT</c> value of the error that caused the exception. + /// </param> + /**/ + _CRTIMP scheduler_resource_allocation_error(_In_z_ const char * _Message, HRESULT _Hresult) throw(); + + /// <summary> + /// Constructs a <c>scheduler_resource_allocation_error</c> object. + /// </summary> + /// <param name="_Hresult"> + /// The <c>HRESULT</c> value of the error that caused the exception. + /// </param> + /**/ + explicit _CRTIMP scheduler_resource_allocation_error(HRESULT _Hresult) throw(); + + /// <summary> + /// Returns the error code that caused the exception. + /// </summary> + /// <returns> + /// The <c>HRESULT</c> value of the error that caused the exception. + /// </returns> + /**/ + _CRTIMP HRESULT get_error_code() const throw(); + +private: + HRESULT _Hresult; +}; + +/// <summary> +/// This class describes an exception thrown because of a failure to create a worker execution context in the Concurrency Runtime. +/// </summary> +/// <remarks> +/// This exception is typically thrown when a call to the operating system to create execution contexts from within the Concurrency Runtime +/// fails. Execution contexts are threads that execute tasks in the Concurrency Runtime. The error code which would normally be returned +/// from a call to the Win32 method <c>GetLastError</c> is converted to a value of type <c>HRESULT</c> and can be retrieved using the base +/// class method <c>get_error_code</c>. +/// </remarks> +/**/ +class scheduler_worker_creation_error : public scheduler_resource_allocation_error +{ +public: + /// <summary> + /// Constructs a <c>scheduler_worker_creation_error</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /// <param name="_Hresult"> + /// The <c>HRESULT</c> value of the error that caused the exception. + /// </param> + /**/ + _CRTIMP scheduler_worker_creation_error(_In_z_ const char * _Message, HRESULT _Hresult) throw(); + + /// <summary> + /// Constructs a <c>scheduler_worker_creation_error</c> object. + /// </summary> + /// <param name="_Hresult"> + /// The <c>HRESULT</c> value of the error that caused the exception. + /// </param> + /**/ + explicit _CRTIMP scheduler_worker_creation_error(HRESULT _Hresult) throw(); +}; + +/// <summary> +/// This class describes an exception thrown when an unsupported operating system is used. +/// </summary> +/**/ +class unsupported_os : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>unsupported_os</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP unsupported_os(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>unsupported_os</c> object. + /// </summary> + /**/ + _CRTIMP unsupported_os() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when an operation is performed which requires a scheduler +/// to be attached to the current context and one is not. +/// </summary> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="Scheduler::Attach Method"/> +/**/ +class scheduler_not_attached : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>scheduler_not_attached</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP scheduler_not_attached(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>scheduler_not_attached</c> object. + /// </summary> + /**/ + _CRTIMP scheduler_not_attached() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the <c>Attach</c> method is called on a <c>Scheduler</c> +/// object which is already attached to the current context. +/// </summary> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="Scheduler::Attach Method"/> +/**/ +class improper_scheduler_attach : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>improper_scheduler_attach</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP improper_scheduler_attach(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>improper_scheduler_attach</c> object. + /// </summary> + /**/ + _CRTIMP improper_scheduler_attach() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the <c>CurrentScheduler::Detach</c> method is called on +/// a context which has not been attached to any scheduler using the <c>Attach</c> method of a <c>Scheduler</c> object. +/// </summary> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="CurrentScheduler::Detach Method"/> +/// <seealso cref="Scheduler::Attach Method"/> +/**/ +class improper_scheduler_detach : public std::exception +{ +public: + + /// <summary> + /// Constructs an <c>improper_scheduler_detach</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP improper_scheduler_detach(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>improper_scheduler_detach</c> object. + /// </summary> + /**/ + _CRTIMP improper_scheduler_detach() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the <c>Reference</c> method is called on a <c>Scheduler</c> +/// object that is shutting down, from a context that is not part of that scheduler. +/// </summary> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="Scheduler::Reference Method"/> +/**/ +class improper_scheduler_reference : public std::exception +{ +public: + + /// <summary> + /// Constructs an <c>improper_scheduler_reference</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP improper_scheduler_reference(_In_z_ const char* _Message) throw(); + + /// <summary> + /// Constructs an <c>improper_scheduler_reference</c> object. + /// </summary> + /**/ + _CRTIMP improper_scheduler_reference() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the <c>Scheduler::SetDefaultSchedulerPolicy</c> method is +/// called when a default scheduler already exists within the process. +/// </summary> +/// <seealso cref="Scheduler::SetDefaultSchedulerPolicy Method"/> +/**/ +class default_scheduler_exists : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>default_scheduler_exists</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP default_scheduler_exists(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>default_scheduler_exists</c> object. + /// </summary> + /**/ + _CRTIMP default_scheduler_exists() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when calls to the <c>Block</c> and <c>Unblock</c> methods of a +/// <c>Context</c> object are not properly paired. +/// </summary> +/// <remarks> +/// Calls to the <c>Block</c> and <c>Unblock</c> methods of a <c>Context</c> object must always be properly paired. +/// The Concurrency Runtime allows the operations to happen in either order. For example, a call to <c>Block</c> +/// can be followed by a call to <c>Unblock</c>, or vice-versa. This exception would be thrown if, for instance, two calls to the +/// <c>Unblock</c> method were made in a row, on a <c>Context</c> object which was not blocked. +/// </remarks> +/// <seealso cref="Context Class"/> +/// <seealso cref="Context::Unblock Method"/> +/// <seealso cref="Context::Block Method"/> +/**/ +class context_unblock_unbalanced : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>context_unblock_unbalanced</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP context_unblock_unbalanced(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>context_unblock_unbalanced</c> object. + /// </summary> + /**/ + _CRTIMP context_unblock_unbalanced() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the <c>Unblock</c> method of a <c>Context</c> object is called +/// from the same context. This would indicate an attempt by a given context to unblock itself. +/// </summary> +/// <seealso cref="Context Class"/> +/// <seealso cref="Context::Unblock Method"/> +/**/ +class context_self_unblock : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>context_self_unblock</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP context_self_unblock(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>context_self_unblock</c> object. + /// </summary> + /**/ + _CRTIMP context_self_unblock() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when there are tasks still scheduled to a <c>task_group</c> or +/// <c>structured_task_group</c> object at the time that object's destructor executes. This exception will never be thrown +/// if the destructor is reached because of a stack unwinding as the result of an exception. +/// </summary> +/// <remarks> +/// Absent exception flow, you are responsible for calling either the <c>wait</c> or <c>run_and_wait</c> method of a <c>task_group</c> or +/// <c>structured_task_group</c> object before allowing that object to destruct. The runtime throws this exception as an +/// indication that you forgot to call the <c>wait</c> or <c>run_and_wait</c> method. +/// </remarks> +/// <seealso cref="task_group Class"/> +/// <seealso cref="task_group::wait Method"/> +/// <seealso cref="task_group::run_and_wait Method"/> +/// <seealso cref="structured_task_group Class"/> +/// <seealso cref="structured_task_group::wait Method"/> +/// <seealso cref="structured_task_group::run_and_wait Method"/> +/**/ +class missing_wait : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>missing_wait</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP missing_wait(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>missing_wait</c> object. + /// </summary> + /**/ + _CRTIMP missing_wait() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when a messaging block is given a pointer to a target which is +/// invalid for the operation being performed. +/// </summary> +/// <remarks> +/// This exception is typically thrown for reasons such as a target attempting to consume a message which is reserved +/// for a different target or releasing a reservation that it does not hold. +/// </remarks> +/// <seealso cref="Asynchronous Message Blocks"/> +/**/ +class bad_target : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>bad_target</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP bad_target(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>bad_target</c> object. + /// </summary> + /**/ + _CRTIMP bad_target() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when a messaging block is unable to find a requested message. +/// </summary> +/// <seealso cref="Asynchronous Message Blocks"/> +/**/ +class message_not_found : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>message_not_found</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP message_not_found(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>message_not_found</c> object. + /// </summary> + /**/ + _CRTIMP message_not_found() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the <c>link_target</c> method of a messaging block is +/// called and the messaging block is unable to link to the target. This can be the result of exceeding the number of +/// links the messaging block is allowed or attempting to link a specific target twice to the same source. +/// </summary> +/// <seealso cref="Asynchronous Message Blocks"/> +/**/ +class invalid_link_target : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>invalid_link_target</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP invalid_link_target(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>invalid_link_target</c> object. + /// </summary> + /**/ + _CRTIMP invalid_link_target() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when an invalid or unknown key is passed to a <c>SchedulerPolicy</c> +/// object constructor, or the <c>SetPolicyValue</c> method of a <c>SchedulerPolicy</c> object is passed a key that must +/// be changed using other means such as the <c>SetConcurrencyLimits</c> method. +/// </summary> +/// <seealso cref="SchedulerPolicy Class"/> +/// <seealso cref="PolicyElementKey Enumeration"/> +/// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> +/// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> +/**/ +class invalid_scheduler_policy_key : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>invalid_scheduler_policy_key</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP invalid_scheduler_policy_key(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>invalid_scheduler_policy_key</c> object. + /// </summary> + /**/ + _CRTIMP invalid_scheduler_policy_key() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when a policy key of a <c>SchedulerPolicy</c> object is +/// set to an invalid value for that key. +/// </summary> +/// <seealso cref="SchedulerPolicy Class"/> +/// <seealso cref="PolicyElementKey Enumeration"/> +/// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> +/// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> +/**/ +class invalid_scheduler_policy_value : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>invalid_scheduler_policy_value</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP invalid_scheduler_policy_value(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>invalid_scheduler_policy_value</c> object. + /// </summary> + /**/ + _CRTIMP invalid_scheduler_policy_value() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when an attempt is made to set the concurrency limits of a +/// <c>SchedulerPolicy</c> object such that the value of the <c>MinConcurrency</c> key is less than the value of the +/// <c>MaxConcurrency</c> key. +/// </summary> +/// <seealso cref="SchedulerPolicy Class"/> +/// <seealso cref="PolicyElementKey Enumeration"/> +/// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> +/**/ +class invalid_scheduler_policy_thread_specification : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>invalid_scheduler_policy_value</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP invalid_scheduler_policy_thread_specification(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>invalid_scheduler_policy_value</c> object. + /// </summary> + /**/ + _CRTIMP invalid_scheduler_policy_thread_specification() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when an invalid operation is performed that is not more accurately +/// described by another exception type thrown by the Concurrency Runtime. +/// </summary> +/// <remarks> +/// The various methods which throw this exception will generally document under what circumstances they will throw it. +/// </remarks> +/**/ +class invalid_operation : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>invalid_operation</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP invalid_operation(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>invalid_operation</c> object. + /// </summary> + /**/ + _CRTIMP invalid_operation() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the Concurrency Runtime detects that you neglected to call the +/// <c>CurrentScheduler::Detach</c> method on a context that attached to a second scheduler using the <c>Attach</c> method +/// of the <c>Scheduler</c> object. +/// </summary> +/// <remarks> +/// This exception is thrown only when you nest one scheduler inside another by calling the <c>Attach</c> method of a +/// <c>Scheduler</c> object on a context that is already owned by or attached to another scheduler. The Concurrency Runtime +/// throws this exception opportunistically when it can detect the scenario as an aid to locating the problem. Not every +/// instance of neglecting to call the <c>CurrentScheduler::Detach</c> method is guaranteed to throw this exception. +/// </remarks> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="CurrentScheduler::Detach Method"/> +/// <seealso cref="Scheduler::Attach Method"/> +/**/ +class nested_scheduler_missing_detach : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>nested_scheduler_missing_detach</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP nested_scheduler_missing_detach(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>nested_scheduler_missing_detach</c> object. + /// </summary> + /**/ + _CRTIMP nested_scheduler_missing_detach() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when an operation has timed out. +/// </summary> +/**/ +class operation_timed_out : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>operation_timed_out</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP operation_timed_out(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>operation_timed_out</c> object. + /// </summary> + /**/ + _CRTIMP operation_timed_out() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when a <c>task_handle</c> object is scheduled multiple times +/// using the <c>run</c> method of a <c>task_group</c> or <c>structured_task_group</c> object without an intervening +/// call to either the <c>wait</c> or <c>run_and_wait</c> methods. +/// </summary> +/// <seealso cref="task_handle Class"/> +/// <seealso cref="task_group Class"/> +/// <seealso cref="task_group::run Method"/> +/// <seealso cref="task_group::wait Method"/> +/// <seealso cref="task_group::run_and_wait Method"/> +/// <seealso cref="structured_task_group Class"/> +/// <seealso cref="structured_task_group::run Method"/> +/// <seealso cref="structured_task_group::wait Method"/> +/// <seealso cref="structured_task_group::run_and_wait Method"/> +/**/ +class invalid_multiple_scheduling : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>invalid_multiple_scheduling</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP invalid_multiple_scheduling(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>invalid_multiple_scheduling</c> object. + /// </summary> + /**/ + _CRTIMP invalid_multiple_scheduling() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when the <c>Context::Oversubscribe</c> method is called with +/// the <paramref name="_BeginOversubscription"/> parameter set to <c>false</c> without a prior call to the +/// <c>Context::Oversubscribe</c> method with the <paramref name="_BeginOversubscription"/> parameter set to <c>true</c>. +/// </summary> +/// <seealso cref="Context::Oversubscribe Method"/> +/**/ +class invalid_oversubscribe_operation : public std::exception +{ +public: + /// <summary> + /// Constructs an <c>invalid_oversubscribe_operation</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP invalid_oversubscribe_operation(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>invalid_oversubscribe_operation</c> object. + /// </summary> + /**/ + _CRTIMP invalid_oversubscribe_operation() throw(); +}; + +/// <summary> +/// This class describes an exception thrown when a lock is acquired improperly. +/// </summary> +/// <remarks> +/// Typically, this exception is thrown when an attempt is made to acquire a non-reentrant lock +/// recursively on the same context. +/// </remarks> +/// <seealso cref="critical_section Class"/> +/// <seealso cref="reader_writer_lock Class"/> +/**/ +class improper_lock : public std::exception +{ +public: + + /// <summary> + /// Constructs an <c>improper_lock exception</c>. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP improper_lock(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs an <c>improper_lock</c> exception. + /// </summary> + /**/ + _CRTIMP improper_lock() throw(); +}; + +/// <summary> +/// This class describes an exception thrown by the PPL tasks layer in order to force the current task +/// to cancel. It is also thrown by the <c>get()</c> method on <see cref="task Class">task</see>, for a +/// canceled task. +/// </summary> +/// <seealso cref="task::get Method"/> +/// <seealso cref="cancel_current_task Method"/> +/**/ +class task_canceled : public std::exception +{ +public: + /// <summary> + /// Constructs a <c>task_canceled</c> object. + /// </summary> + /// <param name="_Message"> + /// A descriptive message of the error. + /// </param> + /**/ + explicit _CRTIMP task_canceled(_In_z_ const char * _Message) throw(); + + /// <summary> + /// Constructs a <c>task_canceled</c> object. + /// </summary> + /**/ + _CRTIMP task_canceled() throw(); +}; + +/// <summary> +/// An abstraction of a physical location on hardware. +/// </summary> +/**/ +class location +{ +public: + + /// <summary> + /// Constructs a <c>location</c> object. + /// </summary> + /// <remarks> + /// A default constructed location represents the system as a whole. + /// </remarks> + /**/ + location() : + _M_type(_System), + _M_reserved(0), + _M_pBinding(NULL), + _M_ptr(NULL) + { + } + + /// <summary> + /// Constructs a <c>location</c> object. + /// </summary> + /**/ + location(const location& _Src) + { + _Assign(_Src); + } + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + + /// <summary> + /// Returns a <c>location</c> object which represents a given NUMA node. + /// </summary> + /// <param name="_NumaNodeNumber"> + /// The NUMA node number to construct a location for. + /// </param> + /// <returns> + /// A location representing the NUMA node specified by the <paramref name="_NumaNodeNumber"/> parameter. + /// </returns> + /**/ + _CRTIMP static location __cdecl from_numa_node(unsigned short _NumaNodeNumber); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + /// <summary> + /// Returns a <c>location</c> object representing the most specific place the calling thread is executing. + /// </summary> + /// <returns> + /// A location representing the most specific place the calling thread is executing. + /// </returns> + /**/ + _CRTIMP static location __cdecl current(); + + /// <summary> + /// Assigns the contents of a different <c>location</c> object to this one. + /// </summary> + /// <param name="_Rhs"> + /// The source <c>location</c> object. + /// </param> + /**/ + location& operator=(const location& _Rhs) + { + _Assign(_Rhs); + return *this; + } + + /// <summary> + /// Destroys a <c>location</c> object. + /// </summary> + /**/ + ~location() + { + } + + /// <summary> + /// Determines whether two <c>location</c> objects represent the same location. + /// </summary> + /// <returns> + /// <c>true</c> if the two locations are identical, and <c>false</c> otherwise. + /// </returns> + /**/ + bool operator==(const location& _Rhs) const + { + return (_M_type == _Rhs._M_type && _M_ptr == _Rhs._M_ptr); + } + + /// <summary> + /// Determines whether two <c>location</c> objects represent different location. + /// </summary> + /// <returns> + /// <c>true</c> if the two locations are different, <c>false</c> otherwise. + /// </returns> + /**/ + bool operator!=(const location& _Rhs) const + { + return !operator==(_Rhs); + } + + //************************************************** + // + // Runtime internal public pieces of location. No code outside the core of ConcRT can depend on anything + // below. It is internal implementation detail: + // + + /// <summary> + /// Returns a location representing the scheduling node that the calling thread is executing. + /// </summary> + /**/ + _CRTIMP static location __cdecl _Current_node(); + + /// <summary> + /// Describes the type of the given location. + /// </summary> + /**/ + enum _Type + { + /// <summary> + /// Indicates that the location represents the "system location". This has no specific affinity. + /// </summary> + _System, // _M_id is meaningless + + /// <summary> + /// Indicates that the location represents a particular NUMA node. + /// </summary> + _NumaNode, // _M_id is the Windows NUMA node number + + /// <summary> + /// Indicates that the location represents a particular scheduling node. + /// </summary> + _SchedulingNode, // _M_id is the unique identifier for the scheduling node + + /// <summary> + /// Indicates that the location represents a paritcular execution resource. + /// </summary> + _ExecutionResource, // _M_id is the unique identifier for the execution resource + }; + + /// <summary> + /// Constructs a specific location. + /// </summary> + /**/ + location(_Type _LocationType, unsigned int _Id, unsigned int _BindingId = 0, _Inout_opt_ void *_PBinding = NULL); + + /// <summary> + /// Determines whether two locations have an intersection. This is a fast intersection which avoids certain checks by knowing that + /// the *this* pointer is a virtual processor location for a validly bound virtual processor. + /// </summary> + /// <param name="_Rhs"> + /// The location to intersect with this. + /// </param> + /// <returns> + /// An indication as to whether the two locations intersect. + /// </returns> + /**/ + bool _FastVPIntersects(const location& _Rhs) const; + + /// <summary> + /// Determines whether two locations have an intersection. This is a fast intersection which avoids certain checks by knowing that + /// the *this* pointer is a node for a validly bound node. + /// </summary> + /// <param name="_Rhs"> + /// The location to intersect with this. + /// </param> + /// <returns> + /// An indication as to whether the two locations intersect. + /// </returns> + /**/ + bool _FastNodeIntersects(const location& _Rhs) const; + + /// <summary> + /// Assigns _Rhs to this location. + /// </summary> + /**/ + void _Assign(const location& _Rhs) + { + _M_type = _Rhs._M_type; + _M_reserved = _Rhs._M_reserved; + + _M_ptr = _Rhs._M_ptr; + + _M_bindingId = _Rhs._M_bindingId; + _M_pBinding = _Rhs._M_pBinding; + } + + /// <summary> + /// Internal routine that tells whether a location represents the "system location". This indicates no specific placement. + /// </summary> + /**/ + bool _Is_system() const + { + return (_Type)_M_type == _System; + } + + /// <summary> + /// Returns the internal binding as a specified object. + /// </summary> + /**/ + template<typename T> + T* _As() const + { + return reinterpret_cast<T *>(_M_pBinding); + } + + /// <summary> + /// Returns the ID which this location object represents. + /// </summary> + /**/ + unsigned int _GetId() const + { + return _M_id; + } + + /// <summary> + /// Returns the type which this location object represents. + /// </summary> + /**/ + _Type _GetType() const + { + return (_Type)_M_type; + } + + /// <summary> + /// Gets the binding ID for this location. + /// </summary> + /**/ + unsigned int _GetBindingId() const + { + return _M_bindingId; + } + +private: + + // Indicates the type of location (as _Type) + unsigned int _M_type : 28; + + // Flags on the location. Reserved for future use. + unsigned int _M_reserved : 4; + + // If the location has a tight binding, this is the unique identifier of the scheduler to which the binding has specific meaning. + unsigned int _M_bindingId; + + // Defines the agnostic (abstract hardware) binding of the location. + union + { + // The identifier for the binding (NUMA node number, scheduler node ID, execution resource ID) + unsigned int _M_id; + + // Pointer binding. + void *_M_ptr; + }; + + // The specific binding to a scheduler. (For example, a specific virtual processor for something like location::current() ) + // This will be NULL if there is no tight binding. + void *_M_pBinding; +}; + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +/// <summary> +/// Represents an abstraction for a schedule group. Schedule groups organize a set of related work that benefits from being +/// scheduled close together either temporally, by executing another task in the same group before moving to another group, or +/// spatially, by executing multiple items within the same group on the same NUMA node or physical socket. +/// </summary> +/// <seealso cref="CurrentScheduler Class"/> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="Task Scheduler (Concurrency Runtime)"/> +/**/ +class ScheduleGroup +{ +public: + + /// <summary> + /// Schedules a light-weight task within the schedule group. + /// </summary> + /// <param name="_Proc"> + /// A pointer to the function to execute to perform the body of the light-weight task. + /// </param> + /// <param name="_Data"> + /// A void pointer to the data that will be passed as a parameter to the body of the task. + /// </param> + /// <remarks> + /// Calling the <c>ScheduleTask</c> method implicitly places a reference count on the schedule group which is removed by the runtime + /// at an appropriate time after the task executes. + /// </remarks> + /// <seealso cref="ScheduleGroup::Reference Method"/> + /**/ + virtual void ScheduleTask(TaskProc _Proc, _Inout_opt_ void * _Data) =0; + + /// <summary> + /// Returns an identifier for the schedule group that is unique within the scheduler to which the group belongs. + /// </summary> + /// <returns> + /// An identifier for the schedule group that is unique within the scheduler to which the group belongs. + /// </returns> + /**/ + virtual unsigned int Id() const =0; + + /// <summary> + /// Increments the schedule group reference count. + /// </summary> + /// <returns> + /// The newly incremented reference count. + /// </returns> + /// <remarks> + /// This is typically used to manage the lifetime of the schedule group for composition. When the reference count of a schedule + /// group falls to zero, the schedule group is deleted by the runtime. A schedule group created using either the + /// <see cref="CurrentScheduler::CreateScheduleGroup Method">CurrentScheduler::CreateScheduleGroup</see> method, or the + /// <see cref="Scheduler::CreateScheduleGroup Method">Scheduler::CreateScheduleGroup</see> method starts out with a reference + /// count of one. + /// </remarks> + /// <seealso cref="ScheduleGroup::Release Method"/> + /// <seealso cref="CurrentScheduler::CreateScheduleGroup Method"/> + /// <seealso cref="Scheduler::CreateScheduleGroup Method"/> + /**/ + virtual unsigned int Reference() =0; + + /// <summary> + /// Decrements the scheduler group reference count. + /// </summary> + /// <returns> + /// The newly decremented reference count. + /// </returns> + /// <remarks> + /// This is typically used to manage the lifetime of the schedule group for composition. When the reference count of a schedule + /// group falls to zero, the schedule group is deleted by the runtime. After you have called the <c>Release</c> method the specific number + /// of times to remove the creation reference count and any additional references placed using the <c>Reference</c> method, you cannot + /// utilize the schedule group further. Doing so will result in undefined behavior. + /// <para>A schedule group is associated with a particular scheduler instance. You must ensure that all references to the + /// schedule group are released before all references to the scheduler are released, because the latter could result in the scheduler + /// being destroyed. Doing otherwise results in undefined behavior.</para> + /// </remarks> + /// <seealso cref="ScheduleGroup::Reference Method"/> + /// <seealso cref="CurrentScheduler::CreateScheduleGroup Method"/> + /// <seealso cref="Scheduler::CreateScheduleGroup Method"/> + /**/ + virtual unsigned int Release() =0; + +protected: + + // + // Privatize operator delete. Clients should utilize Release to relinquish a schedule group. + // + template<class _T> friend void Concurrency::details::_InternalDeleteHelper(_T * _PObject); + + virtual ~ScheduleGroup() {}; +}; + +/// <summary> +/// Special value for the policy keys <c>MinConcurrency</c> and <c>MaxConcurrency</c>. Defaults to the number of hardware +/// threads on the machine in the absence of other constraints. +/// </summary> +/// <seealso cref="PolicyElementKey Enumeration"/> +/**/ +const unsigned int MaxExecutionResources = 0xFFFFFFFF; + +/// <summary> +/// Special value for the policy key <c>ContextPriority</c> indicating that the thread priority of all contexts in the scheduler +/// should be the same as that of the thread which created the scheduler. +/// </summary> +/// <seealso cref="PolicyElementKey Enumeration"/> +/**/ +const unsigned int INHERIT_THREAD_PRIORITY = 0x0000F000; + +/// <summary> +/// Policy keys describing aspects of scheduler behavior. Each policy element is described by a key-value pair. For more information +/// about scheduler policies and their impact on schedulers, see <see cref="Task Scheduler (Concurrency Runtime)"/>. +/// </summary> +/// <seealso cref="SchedulerPolicy Class"/> +/// <seealso cref="CurrentScheduler Class"/> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="Task Scheduler (Concurrency Runtime)"/> +/**/ +enum PolicyElementKey +{ + /// <summary> + /// The type of threads that the scheduler will utilize for underlying execution contexts. For more information, see + /// <see cref="SchedulerType Enumeration"/>. + /// <para>Valid values : A member of the <c>SchedulerType</c> enumeration, for example, <c>ThreadScheduler</c></para> + /// <para>Default value : <c>ThreadScheduler</c>. This translates to Win32 threads on all operating systems.</para> + /// </summary> + /**/ + SchedulerKind, + + /// <summary> + /// The maximum concurrency level desired by the scheduler. The resource manager will try to initially allocate this many virtual processors. + /// The special value <see cref="MaxExecutionResources Constant">MaxExecutionResources</see> indicates that the desired concurrency level + /// is same as the number of hardware threads on the machine. If the value specified for <c>MinConcurrency</c> is greater than the number + /// of hardware threads on the machine and <c>MaxConcurrency</c> is specified as <c>MaxExecutionResources</c>, the value for <c>MaxConcurrency</c> + /// is raised to match what is set for <c>MinConcurrency</c>. + /// <para>Valid values : Positive integers and the special value <c>MaxExecutionResources</c></para> + /// <para>Default value : <c>MaxExecutionResources</c></para> + /// </summary> + /**/ + MaxConcurrency, + + /// <summary> + /// The minimum concurrency level that must be provided to the scheduler by the resource manager. The number of virtual processors assigned + /// to a scheduler will never go below the minimum. The special value <see cref="MaxExecutionResources Constant">MaxExecutionResources</see> + /// indicates that the minimum concurrency level is same as the number of hardware threads on the machine. If the value specified for + /// <c>MaxConcurrency</c> is less than the number of hardware threads on the machine and <c>MinConcurrency</c> is specified as + /// <c>MaxExecutionResources</c>, the value for <c>MinConcurrency</c> is lowered to match what is set for <c>MaxConcurrency</c>. + /// <para>Valid values : Non-negative integers and the special value <c>MaxExecutionResources</c>. Note that for scheduler policies + /// used for the construction of Concurrency Runtime schedulers, the value <c>0</c> is invalid.</para> + /// <para>Default value : <c>1</c></para> + /// </summary> + /**/ + MinConcurrency, + + /// <summary> + /// Tentative number of virtual processors per hardware thread. The target oversubscription factor can be increased by the Resource Manager, + /// if necessary, to satisfy <c>MaxConcurrency</c> with the hardware threads on the machine. + /// <para>Valid values : Positive integers</para> + /// <para>Default value : <c>1</c></para> + /// </summary> + /**/ + TargetOversubscriptionFactor, + + /// <summary> + /// When the <c>SchedulingProtocol</c> policy key is set to the value <c>EnhanceScheduleGroupLocality</c>, this specifies the maximum number + /// of runnable contexts allowed to be cached in per virtual processor local queues. Such contexts will typically run in last-in-first-out + /// (LIFO) order on the virtual processor that caused them to become runnable. Note that this policy key has no meaning when the + /// <c>SchedulingProtocol</c> key is set to the value <c>EnhanceForwardProgress</c>. + /// <para>Valid values : Non-negative integers</para> + /// <para>Default value : <c>8</c></para> + /// </summary> + /**/ + LocalContextCacheSize, + + /// <summary> + /// The reserved stack size of each context in the scheduler in kilobytes. + /// <para>Valid values : Positive integers</para> + /// <para>Default value : <c>0</c>, indicating that the process' default value for stack size be used.</para> + /// </summary> + /**/ + ContextStackSize, + + /// <summary> + /// The operating system thread priority of each context in the scheduler. If this key is set to the value <see cref="INHERIT_THREAD_PRIORITY Constant"> + /// INHERIT_THREAD_PRIORITY</see> the contexts in the scheduler will inherit the priority of the thread that created the scheduler. + /// <para>Valid values : Any of the valid values for the Windows <c>SetThreadPriority</c> function and the special value + /// <c>INHERIT_THREAD_PRIORITY</c></para> + /// <para>Default value : <c>THREAD_PRIORITY_NORMAL</c></para> + /// </summary> + /**/ + ContextPriority, + + /// <summary> + /// Describes which scheduling algorithm will be used by the scheduler. For more information, see <see cref="SchedulingProtocolType Enumeration"/>. + /// <para>Valid values : A member of the <c>SchedulingProtocolType</c> enumeration, either <c>EnhanceScheduleGroupLocality</c> + /// or <c>EnhanceForwardProgress</c></para> + /// <para>Default value : <c>EnhanceScheduleGroupLocality</c></para> + /// </summary> + /**/ + SchedulingProtocol, + + /// <summary> + /// Determines whether the resources for the scheduler will be rebalanced according to statistical information gathered from the + /// scheduler or only based on the subscription level of underlying hardware threads. For more information, see + /// <see cref="DynamicProgressFeedbackType Enumeration"/>. + /// <para>Valid values : A member of the <c>DynamicProgressFeedbackType</c> enumeration, either <c>ProgressFeedbackEnabled</c> or + /// <c>ProgressFeedbackDisabled</c></para> + /// <para>Default value : <c>ProgressFeedbackEnabled</c></para> + /// </summary> + /**/ + DynamicProgressFeedback, + + /// <summary> + /// Determines whether and how scheduler threads will initialize the Windows Runtime. This policy key only carries meaning for applications + /// executing on operating systems with version Windows 8 or higher. For more information, see <see cref="WinRTInitializationType Enumeration"/>. + /// <para>Valid values : A member of the <c>WinRTInitializationType</c> enumeration, either <c>InitializeWinRTAsMTA</c> or + /// <c>DoNotInitializeWinRT</c></para> + /// <para>Default value : <c>InitializeWinRTAsMTA</c> + /// </summary> + /**/ + WinRTInitialization, + + /// <summary> + /// The maximum policy element key. Not a valid element key. + /// </summary> + /**/ + MaxPolicyElementKey +}; + +/// <summary> +/// Used by the <c>SchedulerKind</c> policy to describe the type of threads that the scheduler should utilize for underlying execution contexts. +/// For more information on available scheduler policies, see <see cref="PolicyElementKey Enumeration"/>. +/// </summary> +/// <seealso cref="SchedulerPolicy Class"/> +/// <seealso cref="PolicyElementKey Enumeration"/> +/**/ +enum SchedulerType +{ + /// <summary> + /// Indicates an explicit request of regular Win32 threads. + /// </summary> + /**/ + ThreadScheduler, + + /// <summary> + /// User-mode schedulable (UMS) threads are not supported in the Concurrency Runtime in Visual Studio 2012. Using <c>UmsThreadDefault</c> + /// as a value for the <c>SchedulerType</c> policy will not result in an error. However, a scheduler created with that policy will + /// default to using Win32 threads. + /// </summary> + /**/ + UmsThreadDefault = ThreadScheduler +}; + +#pragma deprecated(UmsThreadDefault) + +/// <summary> +/// Used by the <c>SchedulingProtocol</c> policy to describe which scheduling algorithm will be utilized for the scheduler. For more +/// information on available scheduler policies, see <see cref="PolicyElementKey Enumeration"/>. +/// </summary> +/// <seealso cref="SchedulerPolicy Class"/> +/// <seealso cref="PolicyElementKey Enumeration"/> +/**/ +enum SchedulingProtocolType +{ + /// <summary> + /// The scheduler prefers to continue to work on tasks within the current schedule group before moving to another schedule group. + /// Unblocked contexts are cached per virtual-processor and are typically scheduled in a last-in-first-out (LIFO) fashion by the + /// virtual processor which unblocked them. + /// </summary> + /**/ + EnhanceScheduleGroupLocality, + + /// <summary> + /// The scheduler prefers to round-robin through schedule groups after executing each task. Unblocked contexts are typically + /// scheduled in a first-in-first-out (FIFO) fashion. Virtual processors do not cache unblocked contexts. + /// </summary> + /**/ + EnhanceForwardProgress +}; + +/// <summary> +/// Used by the <c>DynamicProgressFeedback</c> policy to describe whether resources for the scheduler will be rebalanced according to +/// statistical information gathered from the scheduler or only based on virtual processors going in and out of the idle state through +/// calls to the <c>Activate</c> and <c>Deactivate</c> methods on the <c>IVirtualProcessorRoot</c> interface. For more information +/// on available scheduler policies, see <see cref="PolicyElementKey Enumeration"/>. +/// </summary> +/// <seealso cref="PolicyElementKey Enumeration"/> +/**/ +enum DynamicProgressFeedbackType +{ + /// <summary> + /// The scheduler does not gather progress information. Rebalancing is done based solely on the subscription level of the underlying + /// hardware thread. For more information on subscription levels, see + /// <see cref="IExecutionResource::CurrentSubscriptionLevel Method">IExecutionResource::CurrentSubscriptionLevel</see>. + /// <para>This value is reserved for use by the runtime.</para> + /// </summary> + /**/ + ProgressFeedbackDisabled, + + /// <summary> + /// The scheduler gathers progress information and passes it to the resource manager. The resource manager will utilize this statistical + /// information to rebalance resources on behalf of the scheduler in addition to the subscription level of the underlying + /// hardware thread. For more information on subscription levels, see + /// <see cref="IExecutionResource::CurrentSubscriptionLevel Method">IExecutionResource::CurrentSubscriptionLevel</see>. + /// </summary> + /**/ + ProgressFeedbackEnabled +}; + +/// <summary> +/// Used by the <c>WinRTInitialization</c> policy to describe whether and how the Windows Runtime will be initialized on scheduler threads +/// for an application which runs on operating systems with version Windows 8 or higher. For more information on available scheduler policies, +/// see <see cref="PolicyElementKey Enumeration"/>. +/// </summary> +/// <seealso cref="PolicyElementKey Enumeration"/> +/**/ +enum WinRTInitializationType +{ + /// <summary> + /// When the application is run on operating systems with version Windows 8 or higher, each thread within the scheduler will initialize the + /// Windows Runtime and declare that it is part of the multithreaded apartment. + /// </summary> + /**/ + InitializeWinRTAsMTA, + + /// <summary> + /// When the application is run on operating systems with version Windows 8 or higher, threads within the scheduler will not initialize the + /// Windows Runtime . + /// </summary> + /**/ + DoNotInitializeWinRT +}; + +/// <summary> +/// The <c>SchedulerPolicy</c> class contains a set of key/value pairs, one for each policy element, that control the behavior of a +/// scheduler instance. +/// </summary> +/// <remarks> +/// For more information about the policies which can be controlled using the <c>SchedulerPolicy</c> class, see +/// <see cref="PolicyElementKey Enumeration"/>. +/// </remarks> +/// <seealso cref="PolicyElementKey Enumeration"/> +/// <seealso cref="CurrentScheduler Class"/> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="Task Scheduler (Concurrency Runtime)"/> +/**/ +class SchedulerPolicy +{ +public: + + /// <summary> + /// Constructs a new scheduler policy and populates it with values for <see cref="PolicyElementKey Enumeration">policy keys</see> + /// supported by Concurrency Runtime schedulers and the Resource Manager. + /// </summary> + /// <remarks> + /// <para>The first constructor creates a new scheduler policy where all policies will be initialized to their default values.</para> + /// <para>The second constructor creates a new scheduler policy that uses a named-parameter style of initialization. Values after + /// the <paramref name="_PolicyKeyCount"/> parameter are supplied as key/value pairs. Any policy key which is not specified in this + /// constructor will have its default value. This constructor could throw the exceptions <see cref="invalid_scheduler_policy_key Class"> + /// invalid_scheduler_policy_key</see>, <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value </see> or + /// <see cref="invalid_scheduler_policy_thread_specification Class"> invalid_scheduler_policy_thread_specification</see>.</para> + /// <para>The third constructor is a copy constructor. Often, the most convenient way to define a new scheduler policy is to copy an + /// existing policy and modify it using the <c>SetPolicyValue</c> or <c>SetConcurrencyLimits</c> methods.</para> + /// </remarks> + /// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::GetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + _CRTIMP SchedulerPolicy(); + + /// <summary> + /// Constructs a new scheduler policy and populates it with values for <see cref="PolicyElementKey Enumeration">policy keys</see> + /// supported by Concurrency Runtime schedulers and the Resource Manager. + /// </summary> + /// <param name="_PolicyKeyCount"> + /// The number of key/value pairs that follow the <paramref name="_PolicyKeyCount"/> parameter. + /// </param> + /// <remarks> + /// <para>The first constructor creates a new scheduler policy where all policies will be initialized to their default values.</para> + /// <para>The second constructor creates a new scheduler policy that uses a named-parameter style of initialization. Values after </para> + /// the <paramref name="_PolicyKeyCount"/> parameter are supplied as key/value pairs. Any policy key which is not specified in this + /// constructor will have its default value. This constructor could throw the exceptions <see cref="invalid_scheduler_policy_key Class"> + /// invalid_scheduler_policy_key</see>, <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value </see> or + /// <see cref="invalid_scheduler_policy_thread_specification Class"> invalid_scheduler_policy_thread_specification</see>. + /// <para>The third constructor is a copy constructor. Often, the most convenient way to define a new scheduler policy is to copy an + /// existing policy and modify it using the <c>SetPolicyValue</c> or <c>SetConcurrencyLimits</c> methods.</para> + /// </remarks> + /// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::GetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + _CRTIMP SchedulerPolicy(size_t _PolicyKeyCount, ...); + + /// <summary> + /// Constructs a new scheduler policy and populates it with values for <see cref="PolicyElementKey Enumeration">policy keys</see> + /// supported by Concurrency Runtime schedulers and the Resource Manager. + /// </summary> + /// <param name="_SrcPolicy"> + /// The source policy to copy. + /// </param> + /// <remarks> + /// <para>The first constructor creates a new scheduler policy where all policies will be initialized to their default values.</para> + /// <para>The second constructor creates a new scheduler policy that uses a named-parameter style of initialization. Values after </para> + /// the <paramref name="_PolicyKeyCount"/> parameter are supplied as key/value pairs. Any policy key which is not specified in this + /// constructor will have its default value. This constructor could throw the exceptions <see cref="invalid_scheduler_policy_key Class"> + /// invalid_scheduler_policy_key</see>, <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value </see> or + /// <see cref="invalid_scheduler_policy_thread_specification Class"> invalid_scheduler_policy_thread_specification</see>. + /// <para>The third constructor is a copy constructor. Often, the most convenient way to define a new scheduler policy is to copy an + /// existing policy and modify it using the <c>SetPolicyValue</c> or <c>SetConcurrencyLimits</c> methods.</para> + /// </remarks> + /// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::GetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + _CRTIMP SchedulerPolicy(const SchedulerPolicy& _SrcPolicy); + + /// <summary> + /// Assigns the scheduler policy from another scheduler policy. + /// </summary> + /// <param name="_RhsPolicy"> + /// The policy to assign to this policy. + /// </param> + /// <returns> + /// A reference to the scheduler policy. + /// </returns> + /// <remarks> + /// Often, the most convenient way to define a new scheduler policy is to copy an existing policy and modify it using the + /// <c>SetPolicyValue</c> or <c>SetConcurrencyLimits</c> methods. + /// </remarks> + /// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + _CRTIMP SchedulerPolicy& operator=(const SchedulerPolicy& _RhsPolicy); + + /// <summary> + /// Destroys a scheduler policy. + /// </summary> + /**/ + _CRTIMP ~SchedulerPolicy(); + + /// <summary> + /// Retrieves the value of the policy key supplied as the <paramref name="_Key"/> parameter. + /// </summary> + /// <param name="_Key"> + /// The policy key to retrieve a value for. + /// </param> + /// <returns> + /// If the key specified by the <paramref name="_Key"/> parameter is supported, the policy value for the key cast to an <c>unsigned int</c>. + /// </returns> + /// <remarks> + /// The method will throw <see cref="invalid_scheduler_policy_key Class">invalid_scheduler_policy_key</see> for an invalid policy key. + /// </remarks> + /// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + _CRTIMP unsigned int GetPolicyValue(PolicyElementKey _Key) const; + + /// <summary> + /// Sets the value of the policy key supplied as the <paramref name="_Key"/> parameter and returns the old value. + /// </summary> + /// <param name="_Key"> + /// The policy key to set a value for. + /// </param> + /// <param name="_Value"> + /// The value to set the policy key to. + /// </param> + /// <returns> + /// If the key specified by the <paramref name="_Key"/> parameter is supported, the old policy value for the key cast to an <c>unsigned int</c>. + /// </returns> + /// <remarks> + /// The method will throw <see cref="invalid_scheduler_policy_key Class">invalid_scheduler_policy_key </see> for an invalid policy key + /// or any policy key whose value cannot be set by the <c>SetPolicyValue</c> method. + /// <para>The method will throw <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value</see> for a value that + /// is not supported for the key specified by the <paramref name="_Key"/> parameter.</para> + /// <para>Note that this method is not allowed to set the <c>MinConcurrency</c> or <c>MaxConcurrency</c> policies. To set these values, use + /// the <see cref="SchedulerPolicy::SetConcurrencyLimits Method">SetConcurrencyLimits</see> method.</para> + /// </remarks> + /// <seealso cref="SchedulerPolicy::GetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::SetConcurrencyLimits Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + _CRTIMP unsigned int SetPolicyValue(PolicyElementKey _Key, unsigned int _Value); + + /// <summary> + /// Simultaneously sets the <c>MinConcurrency</c> and <c>MaxConcurrency</c> policies on the <c>SchedulerPolicy</c> object. + /// </summary> + /// <param name="_MinConcurrency"> + /// The value for the <c>MinConcurrency</c> policy key. + /// </param> + /// <param name="_MaxConcurrency"> + /// The value for the <c>MaxConcurrency</c> policy key. + /// </param> + /// <remarks> + /// The method will throw <see cref="invalid_scheduler_policy_thread_specification Class">invalid_scheduler_policy_thread_specification + /// </see> if the value specified for the <c>MinConcurrency</c> policy is greater than that specified for the <c>MaxConcurrency</c> policy. + /// <para>The method can also throw <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value </see> for other + /// invalid values.</para> + /// </remarks> + /// <seealso cref="SchedulerPolicy::GetPolicyValue Method"/> + /// <seealso cref="SchedulerPolicy::SetPolicyValue Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + _CRTIMP void SetConcurrencyLimits(unsigned int _MinConcurrency, unsigned int _MaxConcurrency = MaxExecutionResources); + + /// <summary> + /// Checks if this policy is a valid policy for a Concurrency Runtime scheduler. If it is not, an appropriate exception will be thrown. + /// </summary> + /// <remarks> + /// The method will throw <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value </see> if a policy value supplied + /// in the <c>SchedulerPolicy</c> object cannot be used to create a Concurrency Runtime scheduler. Note that such a policy is not necessarily + /// invalid. The Concurrency Runtime Resource Manager also utilizes the <c>SchedulerPolicy</c> class to describe requirements. + /// </remarks> + /**/ + void _ValidateConcRTPolicy() const; + +private: + + struct _PolicyBag + { + union + { + unsigned int _M_pPolicyBag[MaxPolicyElementKey]; + struct + { + SchedulerType _M_schedulerKind; + unsigned int _M_maxConcurrency; + unsigned int _M_minConcurrency; + unsigned int _M_targetOversubscriptionFactor; + unsigned int _M_localContextCacheSize; + unsigned int _M_contextStackSize; + unsigned int _M_contextPriority; + SchedulingProtocolType _M_schedulingProtocol; + DynamicProgressFeedbackType _M_dynamicProgressFeedback; + WinRTInitializationType _M_WinRTInitialization; + } _M_specificValues; + } _M_values; + } *_M_pPolicyBag; + + /// <summary> + /// Initializes the scheduler policy. + /// </summary> + /**/ + void _Initialize(size_t _PolicyKeyCount, va_list * _PArgs); + + /// <summary> + /// Make this policy a copy of the source policy. + /// </summary> + /**/ + void _Assign(const SchedulerPolicy& _SrcPolicy); + + /// <summary> + /// Returns true if the key supplied is a supported key. + /// </summary> + /**/ + static bool __cdecl _ValidPolicyKey(PolicyElementKey _Key); + + /// <summary> + /// Returns true if a policy value is in a valid range. + /// </summary> + /**/ + static bool __cdecl _ValidPolicyValue(PolicyElementKey _Key, unsigned int _Value); + + /// <summary> + /// Returns true if concurrency limit combinations are valid. + /// </summary> + /**/ + static bool __cdecl _AreConcurrencyLimitsValid(unsigned int _MinConcurrency, unsigned int _MaxConcurrency); + bool _AreConcurrencyLimitsValid() const; + + /// <summary> + /// Test the concurrency combinations of a policy. + /// </summary> + /**/ + bool _ArePolicyCombinationsValid() const; + + /// <summary> + /// Resolves one or more of the policy keys that are set to defaults, based on the characteristics of the underlying system. + /// </summary> + /**/ + void _ResolvePolicyValues(); + + /// <summary> + /// Stringify policy keys. + /// </summary> + /**/ + static char * __cdecl _StringFromPolicyKey(unsigned int _Index); +}; + +/// <summary> +/// Represents an abstraction for the current scheduler associated with the calling context. +/// </summary> +/// <remarks> +/// If there is no scheduler (see <see cref="Scheduler Class">Scheduler</see>) associated with the calling context, many +/// methods within the <c>CurrentScheduler</c> class will result in attachment of the process' default scheduler. This may +/// also imply that the process' default scheduler is created during such a call. +/// </remarks> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="PolicyElementKey Enumeration"/> +/// <seealso cref="Task Scheduler (Concurrency Runtime)"/> +/**/ +class CurrentScheduler +{ +private: + CurrentScheduler() {} + +public: + /// <summary> + /// Returns a unique identifier for the current scheduler. + /// </summary> + /// <returns> + /// If a scheduler is associated with the calling context, a unique identifier for that scheduler; otherwise, the value <c>-1</c>. + /// </returns> + /// <remarks> + /// This method will not result in scheduler attachment if the calling context is not already associated with a scheduler. + /// </remarks> + /**/ + _CRTIMP static unsigned int __cdecl Id(); + + /// <summary> + /// Returns a copy of the policy that the current scheduler was created with. + /// </summary> + /// <returns> + /// A copy of the policy that that the current scheduler was created with. + /// </returns> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// </remarks> + /// <seealso cref="SchedulerPolicy Class"/> + /**/ + _CRTIMP static SchedulerPolicy __cdecl GetPolicy(); + + /// <summary> + /// Returns a pointer to the scheduler associated with the calling context, also referred to as the current scheduler. + /// </summary> + /// <returns> + /// A pointer to the scheduler associated with the calling context (the current scheduler). + /// </returns> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. No additional reference is placed on the <c>Scheduler</c> object + /// returned by this method. + /// </remarks> + /**/ + _CRTIMP static Scheduler * __cdecl Get(); + + /// <summary> + /// Returns the current number of virtual processors for the scheduler associated with the calling context. + /// </summary> + /// <returns> + /// If a scheduler is associated with the calling context, the current number of virtual processors for that scheduler; otherwise, + /// the value <c>-1</c>. + /// </returns> + /// <remarks> + /// This method will not result in scheduler attachment if the calling context is not already associated with a scheduler. + /// <para>The return value from this method is an instantaneous sampling of the number of virtual processors for the scheduler associated + /// with the calling context. This value can be stale the moment it is returned.</para> + /// </remarks> + /**/ + _CRTIMP static unsigned int __cdecl GetNumberOfVirtualProcessors(); + + /// <summary> + /// Creates a new scheduler whose behavior is described by the <paramref name="_Policy"/> parameter and attaches it to the calling context. + /// The newly created scheduler will become the current scheduler for the calling context. + /// </summary> + /// <param name="_Policy"> + /// The scheduler policy that describes the behavior of the newly created scheduler. + /// </param> + /// <remarks> + /// The attachment of the scheduler to the calling context implicitly places a reference count on the scheduler. + /// <para>After a scheduler is created with the <c>Create</c> method, you must call the <see cref="CurrentScheduler::Detach Method"> + /// CurrentScheduler::Detach</see> method at some point in the future in order to allow the scheduler to shut down.</para> + /// <para>If this method is called from a context that is already attached to a different scheduler, the existing scheduler is remembered + /// as the previous scheduler, and the newly created scheduler becomes the current scheduler. When you call the <c>CurrentScheduler::Detach</c> + /// method at a later point, the previous scheduler is restored as the current scheduler.</para> + /// <para>This method can throw a variety of exceptions, including <see cref="scheduler_resource_allocation_error Class"> + /// scheduler_resource_allocation_error</see> and <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value</see>.</para> + /// </remarks> + /// <seealso cref="SchedulerPolicy Class"/> + /// <seealso cref="CurrentScheduler::Detach Method"/> + /// <seealso cref="Scheduler::Reference Method"/> + /// <seealso cref="Scheduler::Release Method"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /**/ + _CRTIMP static void __cdecl Create(const SchedulerPolicy& _Policy); + + /// <summary> + /// Detaches the current scheduler from the calling context and restores the previously attached scheduler as the current + /// scheduler, if one exists. After this method returns, the calling context is then managed by the scheduler that was previously + /// attached to the context using either the <c>CurrentScheduler::Create</c> or <c>Scheduler::Attach</c> method. + /// </summary> + /// <remarks> + /// The <c>Detach</c> method implicitly removes a reference count from the scheduler. + /// <para>If there is no scheduler attached to the calling context, calling this method will result in a <see cref="scheduler_not_attached Class"> + /// scheduler_not_attached</see> exception being thrown.</para> + /// <para>Calling this method from a context that is internal to and managed by a scheduler, or a context that was attached using + /// a method other than the <see cref="Scheduler::Attach Method">Scheduler::Attach</see> or <see cref="CurrentScheduler::Create Method"> + /// CurrentScheduler::Create</see> methods, will result in an <see cref="improper_scheduler_detach Class">improper_scheduler_detach</see> + /// exception being thrown.</para> + /// </remarks> + /// <seealso cref="Scheduler::Attach Method"/> + /// <seealso cref="CurrentScheduler::Create Method"/> + /**/ + _CRTIMP static void __cdecl Detach(); + + /// <summary> + /// Causes the Windows event handle passed in the <paramref name="_ShutdownEvent"/> parameter to be signaled when the scheduler associated with + /// the current context shuts down and destroys itself. At the time the event is signaled, all work that had been scheduled to the + /// scheduler is complete. Multiple shutdown events can be registered through this method. + /// </summary> + /// <param name="_ShutdownEvent"> + /// A handle to a Windows event object which will be signaled by the runtime when the scheduler associated with the current context + /// shuts down and destroys itself. + /// </param> + /// <remarks> + /// If there is no scheduler attached to the calling context, calling this method will result in a <see cref="scheduler_not_attached Class"> + /// scheduler_not_attached </see> exception being thrown. + /// </remarks> + /**/ + _CRTIMP static void __cdecl RegisterShutdownEvent(HANDLE _ShutdownEvent); + + /// <summary> + /// Creates a new schedule group within the scheduler associated with the calling context. The version that takes the parameter + /// <paramref name="_Placement"/> causes tasks within the newly created schedule group to be biased towards executing at the location + /// specified by that parameter. + /// </summary> + /// <returns> + /// A pointer to the newly created schedule group. This <c>ScheduleGroup</c> object has an initial reference count placed on it. + /// </returns> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// <para>You must invoke the <see cref="ScheduleGroup::Release Method">Release</see> method on a schedule group when you are + /// done scheduling work to it. The scheduler will destroy the schedule group when all work queued to it has completed.</para> + /// <para>Note that if you explicitly created this scheduler, you must release all references to schedule groups within it, before + /// you release your reference on the scheduler, by detaching the current context from it.</para> + /// </remarks> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="ScheduleGroup::Release Method"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="location Class"/> + /**/ + _CRTIMP static ScheduleGroup * __cdecl CreateScheduleGroup(); + + /// <summary> + /// Creates a new schedule group within the scheduler associated with the calling context. The version that takes the parameter + /// <paramref name="_Placement"/> causes tasks within the newly created schedule group to be biased towards executing at the location + /// specified by that parameter. + /// </summary> + /// <param name="_Placement"> + /// A reference to a location where the tasks within the schedule group will be biased towards executing at. + /// </param> + /// <returns> + /// A pointer to the newly created schedule group. This <c>ScheduleGroup</c> object has an initial reference count placed on it. + /// </returns> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// <para>You must invoke the <see cref="ScheduleGroup::Release Method">Release</see> method on a schedule group when you are + /// done scheduling work to it. The scheduler will destroy the schedule group when all work queued to it has completed.</para> + /// <para>Note that if you explicitly created this scheduler, you must release all references to schedule groups within it, before + /// you release your reference on the scheduler, by detaching the current context from it.</para> + /// </remarks> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="ScheduleGroup::Release Method"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="location Class"/> + /**/ + _CRTIMP static ScheduleGroup * __cdecl CreateScheduleGroup(location& _Placement); + + /// <summary> + /// Schedules a light-weight task within the scheduler associated with the calling context. The light-weight task will be placed + /// in a schedule group determined by the runtime. The version that takes the parameter <paramref name="_Placement"/> causes the task + /// to be biased towards executing at the specified location. + /// </summary> + /// <param name="_Proc"> + /// A pointer to the function to execute to perform the body of the light-weight task. + /// </param> + /// <param name="_Data"> + /// A void pointer to the data that will be passed as a parameter to the body of the task. + /// </param> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// </remarks> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="location Class"/> + /**/ + _CRTIMP static void __cdecl ScheduleTask(TaskProc _Proc, _Inout_opt_ void * _Data); + + /// <summary> + /// Schedules a light-weight task within the scheduler associated with the calling context. The light-weight task will be placed + /// in a schedule group determined by the runtime. The version that takes the parameter <paramref name="_Placement"/> causes the task + /// to be biased towards executing at the specified location. + /// </summary> + /// <param name="_Proc"> + /// A pointer to the function to execute to perform the body of the light-weight task. + /// </param> + /// <param name="_Data"> + /// A void pointer to the data that will be passed as a parameter to the body of the task. + /// </param> + /// <param name="_Placement"> + /// A reference to a location where the light-weight task will be biased towards executing at. + /// </param> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// </remarks> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="location Class"/> + /**/ + _CRTIMP static void __cdecl ScheduleTask(TaskProc _Proc, _Inout_opt_ void * _Data, location& _Placement); + + /// <summary> + /// Determines whether a given location is available on the current scheduler. + /// </summary> + /// <param name="_Placement"> + /// A reference to the location to query the current scheduler about. + /// </param> + /// <returns> + /// An indication of whether or not the location specified by the <paramref name="_Placement"/> argument is available on the current + /// scheduler. + /// </returns> + /// <remarks> + /// This method will not result in scheduler attachment if the calling context is not already associated with a scheduler. + /// <para>Note that the return value is an instantaneous sampling of whether the given location is available. In the presence of multiple + /// schedulers, dynamic resource management can add or take away resources from schedulers at any point. Should this happen, the given + /// location can change availability.</para> + /// </remarks> + /**/ + _CRTIMP static bool __cdecl IsAvailableLocation(const location& _Placement); +}; + +/// <summary> +/// Represents an abstraction for a Concurrency Runtime scheduler. +/// </summary> +/// <remarks> +/// The Concurrency Runtime scheduler uses execution contexts, which map to the operating system execution contexts, such as a thread, +/// to execute the work queued to it by your application. At any time, the concurrency level of a scheduler is equal to the number of virtual processor +/// granted to it by the Resource Manager. A virtual processor is an abstraction for a processing resource and maps to a hardware thread on the +/// underlying system. Only a single scheduler context can execute on a virtual processor at a given time. +/// <para> The Concurrency Runtime will create a default scheduler per process to execute parallel work. In addition you can create your own scheduler +/// instances and manipulate it using this class.</para> +/// </remarks> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="PolicyElementKey Enumeration"/> +/// <seealso cref="Task Scheduler (Concurrency Runtime)"/> +/**/ +class Scheduler +{ +protected: + /// <summary> + /// An object of the <c>Scheduler</c> class can only created using factory methods, or implicitly. + /// </summary> + /// <remarks> + /// The process' default scheduler is created implicitly when you utilize many of the runtime functions which require a scheduler + /// to be attached to the calling context. Methods within the <c>CurrentScheduler</c> class and features of the PPL and agents layers + /// typically perform implicit attachment. + /// <para>You can also create a scheduler explicitly through either the <c>CurrentScheduler::Create</c> method or the <c>Scheduler::Create</c> + /// method.</para> + /// </remarks> + /// <seealso cref="CurrentScheduler Class"/> + /// <seealso cref="CurrentScheduler::Create Method"/> + /// <seealso cref="Scheduler::Create Method"/> + /**/ + Scheduler() {} + + /// <summary> + /// An object of the <c>Scheduler</c> class is implicitly destroyed when all external references to it cease to exist. + /// </summary> + /**/ + virtual ~Scheduler() {} + +public: + + /// <summary> + /// Creates a new scheduler whose behavior is described by the <paramref name="_Policy"/> parameter, places an initial reference on + /// the scheduler, and returns a pointer to it. + /// </summary> + /// <param name="_Policy"> + /// The scheduler policy that describes behavior of the newly created scheduler. + /// </param> + /// <returns> + /// A pointer to a newly created scheduler. This <c>Scheduler</c> object has an initial reference count placed on it. + /// </returns> + /// <remarks> + /// After a scheduler is created with the <c>Create</c> method, you must call the <see cref="Release Method">Release</see> method at some point + /// in the future in order to remove the initial reference count and allow the scheduler to shut down. + /// <para>A scheduler created with this method is not attached to the calling context. It can be attached to a context using the + /// <see cref="Scheduler::Attach Method">Attach</see> method.</para> + /// <para>This method can throw a variety of exceptions, including <see cref="scheduler_resource_allocation_error Class"> + /// scheduler_resource_allocation_error</see> and <see cref="invalid_scheduler_policy_value Class">invalid_scheduler_policy_value</see>.</para> + /// </remarks> + /// <seealso cref="Scheduler::Release Method"/> + /// <seealso cref="Scheduler::Attach Method"/> + /// <seealso cref="CurrentScheduler::Create Method"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /**/ + _CRTIMP static Scheduler * __cdecl Create(const SchedulerPolicy& _Policy); + + /// <summary> + /// Returns a unique identifier for the scheduler. + /// </summary> + /// <returns> + /// A unique identifier for the scheduler. + /// </returns> + /**/ + virtual unsigned int Id() const =0; + + /// <summary> + /// Returns the current number of virtual processors for the scheduler. + /// </summary> + /// <returns> + /// The current number of virtual processors for the scheduler. + /// <para>The return value from this method is an instantaneous sampling of the number of virtual processors for the scheduler. + /// This value can be stale the moment it is returned.</para> + /// </returns> + /**/ + virtual unsigned int GetNumberOfVirtualProcessors() const =0; + + + /// <summary> + /// Returns a copy of the policy that the scheduler was created with. + /// </summary> + /// <returns> + /// A copy of the policy that the scheduler was created with. + /// </returns> + /// <seealso cref="SchedulerPolicy Class"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /**/ + virtual SchedulerPolicy GetPolicy() const =0; + + /// <summary> + /// Increments the scheduler reference count. + /// </summary> + /// <returns> + /// The newly incremented reference count. + /// </returns> + /// <remarks> + /// This is typically used to manage the lifetime of the scheduler for composition. When the reference count of a scheduler + /// falls to zero, the scheduler will shut down and destruct itself after all work on the scheduler has completed. + /// <para>The method will throw an <see cref="improper_scheduler_reference Class">improper_scheduler_reference</see> exception if the reference + /// count prior to calling the <c>Reference</c> method was zero and the call is made from a context that is not owned by the scheduler.</para> + /// </remarks> + /// <seealso cref="Scheduler::Release Method"/> + /// <seealso cref="Scheduler::Create Method"/> + /**/ + virtual unsigned int Reference() =0 ; + + /// <summary> + /// Decrements the scheduler reference count. + /// </summary> + /// <returns> + /// The newly decremented reference count. + /// </returns> + /// <remarks> + /// This is typically used to manage the lifetime of the scheduler for composition. When the reference count of a scheduler + /// falls to zero, the scheduler will shut down and destruct itself after all work on the scheduler has completed. + /// </remarks> + /// <seealso cref="Scheduler::Reference Method"/> + /// <seealso cref="Scheduler::Create Method"/> + /**/ + virtual unsigned int Release() =0; + + /// <summary> + /// Causes the Windows event handle passed in the <paramref name="_Event"/> parameter to be signaled when the scheduler + /// shuts down and destroys itself. At the time the event is signaled, all work that had been scheduled to the + /// scheduler is complete. Multiple shutdown events can be registered through this method. + /// </summary> + /// <param name="_Event"> + /// A handle to a Windows event object which will be signaled by the runtime when the scheduler shuts down and destroys itself. + /// </param> + /**/ + virtual void RegisterShutdownEvent(HANDLE _Event) =0; + + /// <summary> + /// Attaches the scheduler to the calling context. After this method returns, the calling context is managed by the scheduler and + /// the scheduler becomes the current scheduler. + /// </summary> + /// <remarks> + /// Attaching a scheduler implicitly places a reference on the scheduler. + /// <para>At some point in the future, you must call the <see cref="CurrentScheduler::Detach Method">CurrentScheduler::Detach</see> + /// method in order to allow the scheduler to shut down.</para> + /// <para>If this method is called from a context that is already attached to a different scheduler, the existing scheduler is remembered + /// as the previous scheduler, and the newly created scheduler becomes the current scheduler. When you call the <c>CurrentScheduler::Detach</c> + /// method at a later point, the previous scheduler is restored as the current scheduler.</para> + /// <para>This method will throw an <see cref="improper_scheduler_attach Class">improper_scheduler_attach</see> exception if this scheduler + /// is the current scheduler of the calling context.</para> + /// </remarks> + /// <seealso cref="CurrentScheduler::Detach Method"/> + /**/ + virtual void Attach() =0; + + /// <summary> + /// Allows a user defined policy to be used to create the default scheduler. This method can be called only when no default + /// scheduler exists within the process. After a default policy has been set, it remains in effect until the next valid call + /// to either the <c>SetDefaultSchedulerPolicy</c> or the <see cref="Scheduler::ResetDefaultSchedulerPolicy Method">ResetDefaultSchedulerPolicy + /// </see> method. + /// </summary> + /// <param name="_Policy"> + /// The policy to be set as the default scheduler policy. + /// </param> + /// <remarks> + /// If the <c>SetDefaultSchedulerPolicy</c> method is called when a default scheduler already exists within the process, the runtime + /// will throw a <see cref="default_scheduler_exists Class">default_scheduler_exists</see> exception. + /// </remarks> + /// <seealso cref="Scheduler::ResetDefaultSchedulerPolicy Method"/> + /// <seealso cref="SchedulerPolicy Class"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /**/ + _CRTIMP static void __cdecl SetDefaultSchedulerPolicy(const SchedulerPolicy& _Policy); + + /// <summary> + /// Resets the default scheduler policy to the runtime default. The next time a default scheduler is created, it will use the + /// runtime default policy settings. + /// </summary> + /// <remarks> + /// This method can be called while a default scheduler exists within the process. It will not affect the policy of the existing + /// default scheduler. However, if the default scheduler were to shutdown, and a new default were to be created at a later + /// point, the new scheduler would use the runtime default policy settings. + /// </remarks> + /// <seealso cref="Scheduler::SetDefaultSchedulerPolicy Method"/> + /// <seealso cref="SchedulerPolicy Class"/> + /**/ + _CRTIMP static void __cdecl ResetDefaultSchedulerPolicy(); + + /// <summary> + /// Creates a new schedule group within the scheduler. The version that takes the parameter <paramref name="_Placement"/> causes tasks + /// within the newly created schedule group to be biased towards executing at the location specified by that parameter. + /// </summary> + /// <returns> + /// A pointer to the newly created schedule group. This <c>ScheduleGroup</c> object has an initial reference count placed on it. + /// </returns> + /// <remarks> + /// You must invoke the <see cref="ScheduleGroup::Release Method">Release</see> method on a schedule group when you are + /// done scheduling work to it. The scheduler will destroy the schedule group when all work queued to it has completed. + /// <para>Note that if you explicitly created this scheduler, you must release all references to schedule groups within it, before + /// you release your references on the scheduler.</para> + /// </remarks> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="ScheduleGroup::Release Method"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="location Class"/> + /**/ + virtual ScheduleGroup * CreateScheduleGroup() =0; + + /// <summary> + /// Creates a new schedule group within the scheduler. The version that takes the parameter <paramref name="_Placement"/> causes tasks + /// within the newly created schedule group to be biased towards executing at the location specified by that parameter. + /// </summary> + /// <param name="_Placement"> + /// A reference to a location where the tasks within the schedule group will biased towards executing at. + /// </param> + /// <returns> + /// A pointer to the newly created schedule group. This <c>ScheduleGroup</c> object has an initial reference count placed on it. + /// </returns> + /// <remarks> + /// You must invoke the <see cref="ScheduleGroup::Release Method">Release</see> method on a schedule group when you are + /// done scheduling work to it. The scheduler will destroy the schedule group when all work queued to it has completed. + /// <para>Note that if you explicitly created this scheduler, you must release all references to schedule groups within it, before + /// you release your references on the scheduler.</para> + /// </remarks> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="ScheduleGroup::Release Method"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="location Class"/> + /**/ + virtual ScheduleGroup * CreateScheduleGroup(location& _Placement) =0; + + /// <summary> + /// Schedules a light-weight task within the scheduler. The light-weight task will be placed in a schedule group determined by the runtime. + /// The version that takes the parameter <paramref name="_Placement"/> causes the task to be biased towards executing at the specified location. + /// </summary> + /// <param name="_Proc"> + /// A pointer to the function to execute to perform the body of the light-weight task. + /// </param> + /// <param name="_Data"> + /// A void pointer to the data that will be passed as a parameter to the body of the task. + /// </param> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="location Class"/> + /**/ + virtual void ScheduleTask(TaskProc _Proc, _Inout_opt_ void * _Data) =0; + + /// <summary> + /// Schedules a light-weight task within the scheduler. The light-weight task will be placed in a schedule group determined by the runtime. + /// The version that takes the parameter <paramref name="_Placement"/> causes the task to be biased towards executing at the specified location. + /// </summary> + /// <param name="_Proc"> + /// A pointer to the function to execute to perform the body of the light-weight task. + /// </param> + /// <param name="_Data"> + /// A void pointer to the data that will be passed as a parameter to the body of the task. + /// </param> + /// <param name="_Placement"> + /// A reference to a location where the light-weight task will be biased towards executing at. + /// </param> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /// <seealso cref="ScheduleGroup Class"/> + /// <seealso cref="location Class"/> + /**/ + virtual void ScheduleTask(TaskProc _Proc, _Inout_opt_ void * _Data, location& _Placement) =0; + + /// <summary> + /// Determines whether a given location is available on the scheduler. + /// </summary> + /// <param name="_Placement"> + /// A reference to the location to query the scheduler about. + /// </param> + /// <returns> + /// An indication of whether or not the location specified by the <paramref name="_Placement"/> argument is available on the scheduler. + /// </returns> + /// <remarks> + /// Note that the return value is an instantaneous sampling of whether the given location is available. In the presence of multiple + /// schedulers, dynamic resource management can add or take away resources from schedulers at any point. Should this happen, the given + /// location can change availability. + /// </remarks> + /**/ + virtual bool IsAvailableLocation(const location& _Placement) const =0; +}; + +/// <summary> +/// Represents an abstraction for an execution context. +/// </summary> +/// <remarks> +/// The Concurrency Runtime scheduler (see <see cref="Scheduler Class">Scheduler</see>) uses execution contexts to execute the work queued +/// to it by your application. A Win32 thread is an example of an execution context on a Windows +/// operating system. +/// <para>At any time, the concurrency level of a scheduler is equal to the number of virtual processors granted to it by the Resource Manager. +/// A virtual processor is an abstraction for a processing resource and maps to a hardware thread on the underlying system. Only a single scheduler +/// context can execute on a virtual processor at a given time.</para> +/// <para> The scheduler is cooperative in nature and an executing context can yield its virtual processor to a different context at any time if +/// it wishes to enter a wait state. When its wait it satisfied, it cannot resume until an available virtual processor from the scheduler begins +/// executing it.</para> +/// </remarks> +/// <seealso cref="Scheduler Class"/> +/// <seealso cref="Task Scheduler (Concurrency Runtime)"/> +/**/ +class Context +{ +public: + /// <summary> + /// Returns an identifier for the context that is unique within the scheduler to which the context belongs. + /// </summary> + /// <returns> + /// An identifier for the context that is unique within the scheduler to which the context belongs. + /// </returns> + /**/ + virtual unsigned int GetId() const =0; + + /// <summary> + /// Returns an identifier for the virtual processor that the context is currently executing on. + /// </summary> + /// <returns> + /// If the context is currently executing on a virtual processor, an identifier for the virtual processor that the context + /// is currently executing on; otherwise, the value <c>-1</c>. + /// </returns> + /// <remarks> + /// The return value from this method is an instantaneous sampling of the virtual processor that the context is executing + /// on. This value can be stale the moment it is returned and cannot be relied upon. Typically, this method is used + /// for debugging or tracing purposes only. + /// </remarks> + /**/ + virtual unsigned int GetVirtualProcessorId() const =0; + + /// <summary> + /// Returns an identifier for the schedule group that the context is currently working on. + /// </summary> + /// <returns> + /// An identifier for the schedule group the context is currently working on. + /// </returns> + /// <remarks> + /// The return value from this method is an instantaneous sampling of the schedule group that the context is executing + /// on. If this method is called on a context other than the current context, the value can be stale the moment it is + /// returned and cannot be relied upon. Typically, this method is used for debugging or tracing purposes only. + /// </remarks> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + virtual unsigned int GetScheduleGroupId() const =0; + + /// <summary> + /// Returns an identifier for the current context that is unique within the scheduler to which the current context belongs. + /// </summary> + /// <returns> + /// If the current context is attached to a scheduler, an identifier for the current context that is unique within the scheduler + /// to which the current context belongs; otherwise, the value <c>-1</c>. + /// </returns> + /**/ + _CRTIMP static unsigned int __cdecl Id(); + + /// <summary> + /// Returns an identifier for the virtual processor that the current context is executing on. + /// </summary> + /// <returns> + /// If the current context is attached to a scheduler, an identifier for the virtual processor that the current context is + /// executing on; otherwise, the value <c>-1</c>. + /// </returns> + /// <remarks> + /// The return value from this method is an instantaneous sampling of the virtual processor that the current context is executing + /// on. This value can be stale the moment it is returned and cannot be relied upon. Typically, this method is used + /// for debugging or tracing purposes only. + /// </remarks> + /**/ + _CRTIMP static unsigned int __cdecl VirtualProcessorId(); + + /// <summary> + /// Returns an identifier for the schedule group that the current context is working on. + /// </summary> + /// <returns> + /// If the current context is attached to a scheduler and working on a schedule group, an identifier for the scheduler group that the + /// current context is working on; otherwise, the value <c>-1</c>. + /// </returns> + /// <seealso cref="ScheduleGroup Class"/> + /**/ + _CRTIMP static unsigned int __cdecl ScheduleGroupId(); + + /// <summary> + /// Blocks the current context. + /// </summary> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// <para>If the calling context is running on a virtual processor, the virtual processor will find another runnable context to + /// execute or can potentially create a new one.</para> + /// <para>After the <c>Block</c> method has been called or will be called, you must pair it with a call to the <see cref="Context::Unblock Method"> + /// Unblock</see> method from another execution context in order for it to run again. Be aware that there is a critical period between + /// the point where your code publishes its context for another thread to be able to call the <c>Unblock</c> method and the point + /// where the actual method call to <c>Block</c> is made. During this period, you must not call any method which + /// can in turn block and unblock for its own reasons (for example, acquiring a lock). Calls to the <c>Block</c> and <c>Unblock</c> method + /// do not track the reason for the blocking and unblocking. Only one object should have ownership of a <c>Block</c>-<c>Unblock</c> + /// pair.</para> + /// <para>This method can throw a variety of exceptions, including <see cref="scheduler_resource_allocation_error Class"> + /// scheduler_resource_allocation_error</see>.</para> + /// </remarks> + /// <seealso cref="Context::Unblock Method"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /**/ + _CRTIMP static void __cdecl Block(); + + /// <summary> + /// Unblocks the context and causes it to become runnable. + /// </summary> + /// <remarks> + /// It is perfectly legal for a call to the <c>Unblock</c> method to come before a corresponding call to the <see cref="Context::Block Method"> + /// Block</see> method. As long as calls to the <c>Block</c> and <c>Unblock</c> methods are properly paired, the runtime properly handles the natural race of + /// either ordering. An <c>Unblock</c> call coming before a <c>Block</c> call simply negates the effect of the <c>Block</c> call. + /// <para>There are several exceptions which can be thrown from this method. If a context attempts to call the <c>Unblock</c> method on + /// itself, a <see cref="context_self_unblock Class">context_self_unblock</see> exception will be thrown. If calls to <c>Block</c> and + /// <c>Unblock</c> are not properly paired (for example, two calls to <c>Unblock</c> are made for a context which is currently running), a + /// <see cref="context_unblock_unbalanced Class">context_unblock_unbalanced</see> exception will be thrown.</para> + /// + /// <para>Be aware that there is a critical period between the point where your code publishes its context for another thread to + /// be able to call the <c>Unblock</c> method and the point where the actual method call to <c>Block</c> is made. During this period, + /// you must not call any method which can in turn block and unblock for its own reasons (for example, acquiring a lock). + /// Calls to the <c>Block</c> and <c>Unblock</c> method do not track the reason for the blocking and unblocking. Only one object should have + /// ownership of a <c>Block</c> and <c>Unblock</c> pair.</para> + /// </remarks> + /// <seealso cref="Context::Block Method"/> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /**/ + virtual void Unblock() =0; + + /// <summary> + /// Determines whether or not the context is synchronously blocked. A context is considered to be synchronously + /// blocked if it explicitly performed an action which led to blocking. + /// </summary> + /// <returns> + /// Whether the context is synchronously blocked. + /// </returns> + /// <remarks> + /// A context is considered to be synchronously blocked if it explicitly performed an action which led to blocking. On the thread scheduler, + /// this would indicate a direct call to the <c>Context::Block</c> method or a synchronization object which was built using the + /// <c>Context::Block</c> method. + /// <para>The return value from this method is an instantaneous sample of whether the context is synchronously blocked. This value may + /// be stale the moment it is returned and can only be used under very specific circumstances.</para> + /// </remarks> + /// <seealso cref="Context::Block Method"/> + /**/ + virtual bool IsSynchronouslyBlocked() const =0; + + /// <summary> + /// Yields execution so that another context can execute. If no other context is available to yield to, + /// the method simply returns. + /// </summary> + /// <remarks> + /// This yield variant is intended for use within spin loops. + /// <para>This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context.</para> + /// </remarks> + /**/ + _CRTIMP static void __cdecl _SpinYield(); + + /// <summary> + /// Yields execution so that another context can execute. If no other context is available to yield to, the scheduler + /// can yield to another operating system thread. + /// </summary> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// </remarks> + /// <seealso cref="Context::Block Method"/> + /// <seealso cref="Context::Unblock Method"/> + /**/ + _CRTIMP static void __cdecl Yield(); + + /// <summary> + /// Returns an indication of whether the task collection which is currently executing inline on the current context + /// is in the midst of an active cancellation (or will be shortly). + /// </summary> + /// <returns> + /// If a scheduler is attached to the calling context and a task group is executing a task inline on that context, + /// an indication of whether that task group is in the midst of an active cancellation (or will be shortly); otherwise, + /// the value <c>false</c>. + /// <para>This method will not result in scheduler attachment if the calling context is not already associated with a scheduler.</para> + /// </returns> + /**/ + _CRTIMP static bool __cdecl IsCurrentTaskCollectionCanceling(); + + /// <summary> + /// Returns a pointer to the current context. + /// </summary> + /// <returns> + /// A pointer to the current context. + /// </returns> + /// <remarks> + /// This method will result in the process' default scheduler being created and/or attached to the calling context if there is no + /// scheduler currently associated with the calling context. + /// </remarks> + /**/ + _CRTIMP static Context * __cdecl CurrentContext(); + + /// <summary> + /// Injects an additional virtual processor into a scheduler for the duration of a block of code when invoked on a context executing + /// on one of the virtual processors in that scheduler. + /// </summary> + /// <param name="_BeginOversubscription"> + /// If <c>true</c>, an indication that an extra virtual processor should be added for the duration of the oversubscription. + /// If <c>false</c>, an indication that the oversubscription should end and the previously added virtual processor should be removed. + /// </param> + /// <seealso cref="Task Scheduler (Concurrency Runtime)"/> + /**/ + _CRTIMP static void __cdecl Oversubscribe(bool _BeginOversubscription); + +protected: + + // + // Privatize operator delete. The scheduler internally manages contexts. + // + template<class _T> friend void Concurrency::details::_InternalDeleteHelper(_T * _PObject); + + virtual ~Context() {}; +}; + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/// <summary> +/// Value indicating that a wait timed out. +/// </summary> +/// <seealso cref="event Class"/> +/// <seealso cref="event::wait Method"/> +/// <seealso cref="event::wait_for_multiple Method"/> +/**/ +const size_t COOPERATIVE_WAIT_TIMEOUT = SIZE_MAX; + +/// <summary> +/// Value indicating that a wait should never time out. +/// </summary> +/// <seealso cref="event Class"/> +/// <seealso cref="event::wait Method"/> +/// <seealso cref="event::wait_for_multiple Method"/> +/**/ +const unsigned int COOPERATIVE_TIMEOUT_INFINITE = (unsigned int)-1; + +/// <summary> +/// A non-reentrant mutex which is explicitly aware of the Concurrency Runtime. +/// </summary> +/// <remarks> +/// For more information, see <see cref="Synchronization Data Structures"/>. +/// </remarks> +/// <seealso cref="reader_writer_lock Class"/> +/**/ +class critical_section +{ +public: + + /// <summary> + /// Constructs a new critical section. + /// </summary> + /**/ + _CRTIMP critical_section(); + + /// <summary> + /// Destroys a critical section. + /// </summary> + /// <remarks> + /// It is expected that the lock is no longer held when the destructor runs. Allowing the critical section to destruct with the lock + /// still held results in undefined behavior. + /// </remarks> + /**/ + _CRTIMP ~critical_section(); + + /// <summary> + /// Acquires this critical section. + /// </summary> + /// <remarks> + /// It is often safer to utilize the <see cref="critical_section::scoped_lock Class">scoped_lock</see> construct to acquire and release + /// a <c>critical_section</c> object in an exception safe way. + /// <para>If the lock is already held by the calling context, an <see cref="improper_lock Class">improper_lock</see> exception will be + /// thrown.</para> + /// </remarks> + /// <seealso cref="critical_section::unlock Method"/> + /// <seealso cref="critical_section::scoped_lock Class"/> + /**/ + _CRTIMP void lock(); + + /// <summary> + /// Tries to acquire the lock without blocking. + /// </summary> + /// <returns> + /// If the lock was acquired, the value <c>true</c>; otherwise, the value <c>false</c>. + /// </returns> + /// <seealso cref="critical_section::unlock Method"/> + /**/ + _CRTIMP bool try_lock(); + + /// <summary> + /// Tries to acquire the lock without blocking for a specific number of milliseconds. + /// </summary> + /// <param name="_Timeout"> + /// The number of milliseconds to wait before timing out. + /// </param> + /// <returns> + /// If the lock was acquired, the value <c>true</c>; otherwise, the value <c>false</c>. + /// </returns> + /// <seealso cref="critical_section::unlock Method"/> + /**/ + _CRTIMP bool try_lock_for(unsigned int _Timeout); + + /// <summary> + /// Unlocks the critical section. + /// </summary> + /// <seealso cref="critical_section::lock Method"/> + /// <seealso cref="critical_section::try_lock Method"/> + /**/ + _CRTIMP void unlock(); + + /// <summary> + /// A reference to a <c>critical_section</c> object. + /// </summary> + /**/ + typedef critical_section& native_handle_type; + + /// <summary> + /// Returns a platform specific native handle, if one exists. + /// </summary> + /// <returns> + /// A reference to the critical section. + /// </returns> + /// <remarks> + /// A <c>critical_section</c> object is not associated with a platform specific native handle for the Windows operating system. + /// The method simply returns a reference to the object itself. + /// </remarks> + /**/ + _CRTIMP native_handle_type native_handle(); + + /// <summary> + /// Guarantees that if any context holds the lock at the time the method is called, that context has released + /// the lock before this method returns. + /// </summary> + /// <remarks> + /// If no context holds the lock at the instant this method is called, it returns instantly. + /// </remarks> + /**/ + void _Flush_current_owner(); + + /// <summary> + /// Acquires this critical section given a specific node to lock. + /// </summary> + /// <param name="_PLockingNode"> + /// The node that needs to own the lock. + /// </param> + /// <param name="_FHasExternalNode"> + /// An indication if the node being locked is external to the critical_section. + /// </param> + /// <remarks> + /// If the lock is already held by the calling context, an <see cref="improper_lock Class">.improper_lock</see> exception will be thrown. + /// </remarks> + /**/ + bool _Acquire_lock(void * _PLockingNode, bool _FHasExternalNode); + + /// <summary> + /// An exception safe RAII wrapper for a <c>critical_section</c> object. + /// </summary> + /**/ + class scoped_lock + { + public: + + /// <summary> + /// Constructs a <c>scoped_lock</c> object and acquires the <c>critical_section</c> object passed in the <paramref name="_Critical_section"/> + /// parameter. If the critical section is held by another thread, this call will block. + /// </summary> + /// <param name="_Critical_section"> + /// The critical section to lock. + /// </param> + /// <seealso cref="critical_section Class"/> + /**/ + explicit _CRTIMP scoped_lock(critical_section& _Critical_section); + + /// <summary> + /// Destroys a <c>scoped_lock</c> object and releases the critical section supplied in its constructor. + /// </summary> + /// <seealso cref="critical_section Class"/> + /**/ + _CRTIMP ~scoped_lock(); + + private: + + critical_section& _M_critical_section; + _CONCRT_BUFFER _M_node[(4 * sizeof(void *) + 2 * sizeof(unsigned int) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + + scoped_lock(const scoped_lock&); // no copy constructor + scoped_lock const & operator=(const scoped_lock&); // no assignment operator + }; + +private: + /// <summary> + /// The node allocated on the stack never really owns the lock because it would go out of scope and the insides would not be visible + /// in unlock() where it could potentially need to unblock the next in the queue. Instead, its state is transferred to the internal + /// node which is used as a scratch node. + /// </summary> + /// <param name="_PLockingNode"> + /// The node that needs to own the lock. + /// </param> + /**/ + void _Switch_to_active(void * _PLockingNode); + + _CONCRT_BUFFER _M_activeNode[(4 * sizeof(void *) + 2 * sizeof(unsigned int) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + void * volatile _M_pHead; + void * volatile _M_pTail; + + /// <summary> + /// Hide copy constructor for a critical section + /// </summary> + /**/ + critical_section(const critical_section&); + + /// <summary> + /// Hide assignment operator for a critical section + /// </summary> + /**/ + critical_section& operator=(const critical_section&); +}; + +/// <summary> +/// A writer-preference queue-based reader-writer lock with local only spinning. The lock grants first in - first out (FIFO) access to writers +/// and starves readers under a continuous load of writers. +/// </summary> +/// <remarks> +/// For more information, see <see cref="Synchronization Data Structures"/>. +/// </remarks> +/// <seealso cref="critical_section Class"/> +/**/ +class reader_writer_lock +{ +public: + + /// <summary> + /// Constructs a new <c>reader_writer_lock</c> object. + /// </summary> + /**/ + _CRTIMP reader_writer_lock(); + + /// <summary> + /// Destroys the <c>reader_writer_lock</c> object. + /// </summary> + /// <remarks> + /// It is expected that the lock is no longer held when the destructor runs. Allowing the reader writer lock to destruct with the lock + /// still held results in undefined behavior. + /// </remarks> + /**/ + _CRTIMP ~reader_writer_lock(); + + /// <summary> + /// Acquires the reader-writer lock as a writer. + /// </summary> + /// <remarks> + /// It is often safer to utilize the <see cref="reader_writer_lock::scoped_lock Class">scoped_lock</see> construct to acquire and release + /// a <c>reader_writer_lock</c> object as a writer in an exception safe way. + /// <para>After a writer attempts to acquire the lock, any future readers will block until the writers have successfully acquired + /// and released the lock. This lock is biased towards writers and can starve readers under a continuous load of writers.</para> + /// <para>Writers are chained so that a writer exiting the lock releases the next writer in line.</para> + /// <para>If the lock is already held by the calling context, an <see cref="improper_lock Class">improper_lock</see> exception will be + /// thrown.</para> + /// </remarks> + /// <seealso cref="reader_writer_lock::unlock Method"/> + /**/ + _CRTIMP void lock(); + + /// <summary> + /// Attempts to acquire the reader-writer lock as a writer without blocking. + /// </summary> + /// <returns> + /// If the lock was acquired, the value <c>true</c>; otherwise, the value <c>false</c>. + /// </returns> + /// <seealso cref="reader_writer_lock::unlock Method"/> + /**/ + _CRTIMP bool try_lock(); + + /// <summary> + /// Acquires the reader-writer lock as a reader. If there are writers, active readers have to wait until they are done. + /// The reader simply registers an interest in the lock and waits for writers to release it. + /// </summary> + /// <remarks> + /// It is often safer to utilize the <see cref="reader_writer_lock::scoped_lock_read Class">scoped_lock_read</see> construct to acquire + /// and release a <c>reader_writer_lock</c> object as a reader in an exception safe way. + /// <para>If there are writers waiting on the lock, the reader will wait until all writers in line have acquired + /// and released the lock. This lock is biased towards writers and can starve readers under a continuous load of writers.</para> + /// </remarks> + /// <seealso cref="reader_writer_lock::unlock Method"/> + /**/ + _CRTIMP void lock_read(); + + /// <summary> + /// Attempts to acquire the reader-writer lock as a reader without blocking. + /// </summary> + /// <returns> + /// If the lock was acquired, the value <c>true</c>; otherwise, the value <c>false</c>. + /// </returns> + /// <seealso cref="reader_writer_lock::unlock Method"/> + /**/ + _CRTIMP bool try_lock_read(); + + /// <summary> + /// Unlocks the reader-writer lock based on who locked it, reader or writer. + /// </summary> + /// <remarks> + /// If there are writers waiting on the lock, the release of the lock will always go to the next writer in FIFO + /// order. This lock is biased towards writers and can starve readers under a continuous load of writers. + /// </remarks> + /// <seealso cref="reader_writer_lock::lock Method"/> + /// <seealso cref="reader_writer_lock::lock_read Method"/> + /// <seealso cref="reader_writer_lock::try_lock Method"/> + /// <seealso cref="reader_writer_lock::try_lock_read Method"/> + /**/ + _CRTIMP void unlock(); + + /// <summary> + /// Acquires a write lock given a specific write node to lock. + /// </summary> + /// <param name="_PLockingNode"> + /// The node that needs to own the lock. + /// </param> + /// <param name="_FHasExternalNode"> + /// An indication if the node being locked is external to the <c>reader_writer_lock</c> object. + /// </param> + /// <remarks> + /// If the lock is already held by the calling context, an <see cref="improper_lock Class">.improper_lock</see> exception will be + /// thrown. + /// </remarks> + /**/ + void _Acquire_lock(void * _PLockingNode, bool _FHasExternalNode); + + /// <summary> + /// An exception safe RAII wrapper that can be used to acquire <c>reader_writer_lock</c> lock objects as a writer. + /// </summary> + /**/ + class scoped_lock + { + public: + /// <summary> + /// Constructs a <c>scoped_lock</c> object and acquires the <c>reader_writer_lock</c> object passed in the + /// <paramref name="_Reader_writer_lock"/> parameter as a writer. If the lock is held by another thread, this call will block. + /// </summary> + /// <param name="_Reader_writer_lock"> + /// The <c>reader_writer_lock</c> object to acquire as a writer. + /// </param> + /**/ + explicit _CRTIMP scoped_lock(reader_writer_lock& _Reader_writer_lock); + + /// <summary> + /// Destroys a <c>reader_writer_lock</c> object and releases the lock supplied in its constructor. + /// </summary> + /**/ + _CRTIMP ~scoped_lock(); + + private: + + reader_writer_lock& _M_reader_writer_lock; + _CONCRT_BUFFER _M_writerNode[(4 * sizeof(void *) + 2 * sizeof(unsigned int) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + + scoped_lock(const scoped_lock&); // no copy constructor + scoped_lock const & operator=(const scoped_lock&); // no assignment operator + }; + + /// <summary> + /// An exception safe RAII wrapper that can be used to acquire <c>reader_writer_lock</c> lock objects as a reader. + /// </summary> + /**/ + class scoped_lock_read + { + public: + /// <summary> + /// Constructs a <c>scoped_lock_read</c> object and acquires the <c>reader_writer_lock</c> object passed in the + /// <paramref name="_Reader_writer_lock"/> parameter as a reader. If the lock is held by another thread as a writer or there + /// are pending writers, this call will block. + /// </summary> + /// <param name="_Reader_writer_lock"> + /// The <c>reader_writer_lock</c> object to acquire as a reader. + /// </param> + /**/ + explicit _CRTIMP scoped_lock_read(reader_writer_lock& _Reader_writer_lock); + + /// <summary> + /// Destroys a <c>scoped_lock_read</c> object and releases the lock supplied in its constructor. + /// </summary> + /**/ + _CRTIMP ~scoped_lock_read(); + + private: + + reader_writer_lock& _M_reader_writer_lock; + + scoped_lock_read(const scoped_lock_read&); // no copy constructor + scoped_lock_read const & operator=(const scoped_lock_read&); // no assignment operator + }; + +private: + + /// <summary> + /// Called for the first context in the writer queue. It sets the queue head and it tries to + /// claim the lock if readers are not active. + /// </summary> + /// <param name="_PWriter"> + /// The first writer in the queue. + /// </param> + /**/ + bool _Set_next_writer(void * _PWriter); + + /// <summary> + /// Called when writers are done with the lock, or when lock was free for claiming by + /// the first reader coming in. If in the meantime there are more writers interested + /// the list of readers is finalized and they are convoyed, while head of the list + /// is reset to NULL. + /// </summary> + /// <returns> + /// Pointer to the head of the reader list. + /// </returns> + /**/ + void * _Get_reader_convoy(); + + /// <summary> + /// Called from unlock() when a writer is holding the lock. Writer unblocks the next writer in the list + /// and is being retired. If there are no more writers, but there are readers interested, then readers + /// are unblocked. + /// </summary> + /**/ + void _Unlock_writer(); + + /// <summary> + /// Called from unlock() when a reader is holding the lock. Reader count is decremented and if this + /// is the last reader it checks whether there are interested writers that need to be unblocked. + /// </summary> + /**/ + void _Unlock_reader(); + + /// <summary> + /// When the last writer leaves the lock, it needs to reset the tail to NULL so that the next coming + /// writer would know to try to grab the lock. If the CAS to NULL fails, then some other writer + /// managed to grab the tail before the reset, so this writer needs to wait until the link to + /// the next writer is complete before trying to release the next writer. + /// </summary> + /// <param name="_PWriter"> + /// Last writer in the queue. + /// </param> + /**/ + void _Remove_last_writer(void * _PWriter); + + /// <summary> + /// The writer node allocated on the stack never really owns the lock because it would go out of scope and the insides would not be + /// visible in unlock() where it could potentially need to unblock the next writer in the queue. Instead, its state is transferred to the internal + /// writer node which is used as a scratch node. + /// </summary> + /// <param name="_PWriter"> + /// The writer that needs to own the lock. + /// </param> + /**/ + void _Switch_to_active(void * _PWriter); + + _CONCRT_BUFFER _M_activeWriter[(4 * sizeof(void *) + 2 * sizeof(unsigned int) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + void * _M_pReaderHead; + void * _M_pWriterHead; + void * _M_pWriterTail; + volatile long _M_lockState; + + /// <summary> + /// Hide copy constructor for a reader_writer_lock + /// </summary> + /**/ + reader_writer_lock (const reader_writer_lock& _Lock); + + /// <summary> + /// Hide assignment operator for a reader_writer_lock + /// </summary> + /**/ + reader_writer_lock& operator=(const reader_writer_lock& _Lock); +}; + +/// <summary> +/// A manual reset event which is explicitly aware of the Concurrency Runtime. +/// </summary> +/// <remarks> +/// For more information, see <see cref="Synchronization Data Structures"/>. +/// </remarks> +/**/ +class event +{ +public: + + /// <summary> + /// Constructs a new event. + /// </summary> + /**/ + _CRTIMP event(); + + /// <summary> + /// Destroys an event. + /// </summary> + /// <remarks> + /// It is expected that there are no threads waiting on the event when the destructor runs. Allowing the event to destruct with threads + /// still waiting on it results in undefined behavior. + /// </remarks> + /**/ + _CRTIMP ~event(); + + /// <summary> + /// Waits for the event to become signaled. + /// </summary> + /// <param name="_Timeout"> + /// Indicates the number of milliseconds before the wait times out. The value <c>COOPERATIVE_TIMEOUT_INFINITE</c> signifies that + /// there is no timeout. + /// </param> + /// <returns> + /// If the wait was satisfied, the value <c>0</c> is returned; otherwise, the value <c>COOPERATIVE_WAIT_TIMEOUT</c> to indicate that + /// the wait timed out without the event becoming signaled. + /// </returns> + /// <seealso cref="event::set Method"/> + /// <seealso cref="COOPERATIVE_TIMEOUT_INFINITE Constant">COOPERATIVE_TIMEOUT_INFINITE</seealso> + /// <seealso cref="COOPERATIVE_WAIT_TIMEOUT Constant">COOPERATIVE_WAIT_TIMEOUT</seealso> + /**/ + _CRTIMP size_t wait(unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE); + + /// <summary> + /// Signals the event. + /// </summary> + /// <remarks> + /// Signaling the event can cause an arbitrary number of contexts waiting on the event to become runnable. + /// </remarks> + /// <seealso cref="event::wait Method"/> + /// <seealso cref="event::reset Method"/> + /**/ + _CRTIMP void set(); + + /// <summary> + /// Resets the event to a non-signaled state. + /// </summary> + /// <seealso cref="event::set Method"/> + /// <seealso cref="event::wait Method"/> + /**/ + _CRTIMP void reset(); + + /// <summary> + /// Waits for multiple events to become signaled. + /// </summary> + /// <param name="_PPEvents"> + /// An array of events to wait on. The number of events within the array is indicated by the <paramref name="_Count"/> parameter. + /// </param> + /// <param name="_Count"> + /// The count of events within the array supplied in the <paramref name="_PPEvents"/> parameter. + /// </param> + /// <param name="_FWaitAll"> + /// If set to the value <c>true</c>, the parameter specifies that all events within the array supplied in the <paramref name="_PPEvents"/> + /// parameter must become signaled in order to satisfy the wait. If set to the value <c>false</c>, it specifies that any event within the + /// array supplied in the <paramref name="_PPEvents"/> parameter becoming signaled will satisfy the wait. + /// </param> + /// <param name="_Timeout"> + /// Indicates the number of milliseconds before the wait times out. The value <c>COOPERATIVE_TIMEOUT_INFINITE</c> signifies that + /// there is no timeout. + /// </param> + /// <returns> + /// If the wait was satisfied, the index within the array supplied in the <paramref name="_PPEvents"/> parameter which satisfied + /// the wait condition; otherwise, the value <c>COOPERATIVE_WAIT_TIMEOUT</c> to indicate that the wait timed out without the condition + /// being satisfied. + /// </returns> + /// <remarks> + /// If the parameter <paramref name="_FWaitAll"/> is set to the value <c>true</c> to indicate that all events must become signaled to satisfy + /// the wait, the index returned by the function carries no special significance other than the fact that it is not the value + /// <c>COOPERATIVE_WAIT_TIMEOUT</c>. + /// </remarks> + /// <seealso cref="event::wait Method"/> + /// <seealso cref="COOPERATIVE_TIMEOUT_INFINITE Constant">COOPERATIVE_TIMEOUT_INFINITE</seealso> + /// <seealso cref="COOPERATIVE_WAIT_TIMEOUT Constant">COOPERATIVE_WAIT_TIMEOUT</seealso> + /**/ + _CRTIMP static size_t __cdecl wait_for_multiple(_In_reads_(_Count) event ** _PPEvents, size_t _Count, bool _FWaitAll, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE); + + + /// <summary> + /// Value indicating that a wait should never time out. + /// </summary> + static const unsigned int timeout_infinite = COOPERATIVE_TIMEOUT_INFINITE; +private: + + // Prevent bad usage of copy-constructor and copy-assignment + event(const event& _Event); + event& operator=(const event& _Event); + + void * volatile _M_pWaitChain; + void * _M_pResetChain; + Concurrency::critical_section _M_lock; +}; + +namespace details +{ + /// <summary> + /// A _Condition_variable which is explicitly aware of the Concurrency Runtime. + /// </summary> + /**/ + class _Condition_variable + { + public: + + /// <summary> + /// Constructs a new _Condition_variable. + /// </summary> + /**/ + _CRTIMP _Condition_variable(); + + /// <summary> + /// Destroys a _Condition_variable. + /// </summary> + /**/ + _CRTIMP ~_Condition_variable(); + + /// <summary> + /// Waits for the _Condition_variable to become signaled. The lock argument passed in is unlocked by the _Condition_variable + /// and relocked before the wait returns. + /// </summary> + /// <param name="_Lck"> + /// The critical_section to unlock before waiting and relock before the wait returns. + /// </param> + /// <seealso cref="critical_section Class"/> + /**/ + _CRTIMP void wait(Concurrency::critical_section& _Lck); + + /// <summary> + /// Waits for the _Condition_variable to become signaled. The lock argument passed in is unlocked by the _Condition_variable + /// and relocked before the wait returns. + /// </summary> + /// <param name="_Lck"> + /// The critical_section to unlock before waiting and relock before the wait returns. + /// </param> + /// <param name="_Timeout"> + /// A timeout, in milliseconds, for how long to wait for. + /// </param> + /// <seealso cref="critical_section Class"/> + /**/ + _CRTIMP bool wait_for(Concurrency::critical_section& _Lck, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE); + + /// <summary> + /// Notify a single waiter of the _Condition_variable. + /// </summary> + /**/ + _CRTIMP void notify_one(); + + /// <summary> + /// Notify all the waiters of the _Condition_variable. + /// </summary> + /**/ + _CRTIMP void notify_all(); + + private: + + // Prevent bad usage of copy-constructor and copy-assignment + _Condition_variable(const _Condition_variable& _Event); + _Condition_variable& operator=(const _Condition_variable& _Event); + + void * volatile _M_pWaitChain; + Concurrency::critical_section _M_lock; + }; + + // Base class for all reference counted objects + class _RefCounterBase + { + public: + + virtual ~_RefCounterBase() + { + _CONCRT_ASSERT(_M_refCount == 0); + } + + // Acquires a reference + // Returns the new reference count. + long _Reference() + { + long _Refcount = _InterlockedIncrement(&_M_refCount); + + // 0 - 1 transition is illegal + _CONCRT_ASSERT(_Refcount > 1); + return _Refcount; + } + + // Releases the reference + // Returns the new reference count + long _Release() + { + long _Refcount = _InterlockedDecrement(&_M_refCount); + _CONCRT_ASSERT(_Refcount >= 0); + + if (_Refcount == 0) + { + _Destroy(); + } + + return _Refcount; + } + + protected: + + // Allow derived classes to provide their own deleter + virtual void _Destroy() + { + delete this; + } + + // Only allow instantiation through derived class + _RefCounterBase(long _InitialCount = 1) : _M_refCount(_InitialCount) + { + _CONCRT_ASSERT(_M_refCount > 0); + } + + // Reference count + volatile long _M_refCount; + }; + + class _CancellationTokenState; + class _CancellationTokenRegistration; + + // This is a non-reentrant lock wrapper around the ConcRT critical-section + // and used by agents/messaging + class _NonReentrantPPLLock + { + public: + + // Constructor for _NonReentrantPPLLock + _CRTIMP _NonReentrantPPLLock(); + + // Acquire the lock, spin if necessary + _CRTIMP void _Acquire(void * _Lock_node); + + // Releases the lock + _CRTIMP void _Release(); + + // An exception safe RAII wrapper. + class _Scoped_lock + { + public: + // Constructs a holder and acquires the specified lock + _CRTIMP explicit _Scoped_lock(_NonReentrantPPLLock& _Lock); + + // Destroys the holder and releases the lock + _CRTIMP ~_Scoped_lock(); + + private: + _NonReentrantPPLLock& _M_lock; + _CONCRT_BUFFER _M_lockNode[(4 * sizeof(void *) + 2 * sizeof(unsigned int) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + + _Scoped_lock(const _Scoped_lock&); // no copy constructor + _Scoped_lock const & operator=(const _Scoped_lock&); // no assignment operator + }; + + private: + // critical_section + Concurrency::critical_section _M_criticalSection; + }; + + // This is a reentrant lock implemented using the ConcRT critical section + class _ReentrantPPLLock + { + public: + // Constructor for _ReentrantPPLLock + _CRTIMP _ReentrantPPLLock(); + + // Acquire the lock, spin if necessary + _CRTIMP void _Acquire(void * _Lock_node); + + // Releases the lock + _CRTIMP void _Release(); + + // An exception safe RAII wrapper. + class _Scoped_lock + { + public: + // Constructs a holder and acquires the specified lock + _CRTIMP explicit _Scoped_lock(_ReentrantPPLLock& _Lock); + + // Destroys the holder and releases the lock + _CRTIMP ~_Scoped_lock(); + + private: + _ReentrantPPLLock& _M_lock; + _CONCRT_BUFFER _M_lockNode[(4 * sizeof(void *) + 2 * sizeof(unsigned int) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + + _Scoped_lock(const _Scoped_lock&); // no copy constructor + _Scoped_lock const & operator=(const _Scoped_lock&); // no assignment operator + }; + + private: + // critical_section + Concurrency::critical_section _M_criticalSection; + + // The number of times this lock has been taken recursively + long _M_recursionCount; + + // The current owner of the lock + volatile long _M_owner; + }; + + struct _Chore + { + protected: + // Constructors. + explicit _Chore(TaskProc _PFunction) : m_pFunction(_PFunction) + { + } + + _Chore() + { + } + + virtual ~_Chore() + { + } + + public: + + // The function which invokes the work of the chore. + TaskProc m_pFunction; + }; + + // _UnrealizedChore represents an unrealized chore -- a unit of work that scheduled in a work + // stealing capacity. Some higher level construct (language or library) will map atop this to provide + // an usable abstraction to clients. + class _UnrealizedChore : public _Chore, public _AllocBase + { + public: + // Constructor for an unrealized chore. + _UnrealizedChore() : + _M_pTaskCollection(NULL) + { + } + virtual ~_UnrealizedChore() {} + + + // Method that executes the unrealized chore. + void _Invoke() + { + _M_pChoreFunction(this); + } + + // Sets the attachment state of the chore at the time of stealing. + void _SetDetached(bool _FDetached); + + // Returns the owning collection of the chore. + Concurrency::details::_TaskCollectionBase* _OwningCollection() const + { + return _M_pTaskCollection; + } + + // Set flag that indicates whether the scheduler owns the lifetime of the object and is responsible for freeing it. + // The flag is ignored by _StructuredTaskCollection + void _SetRuntimeOwnsLifetime(bool fValue) + { + _M_fRuntimeOwnsLifetime = fValue; + } + + // Returns the flag that indicates whether the scheduler owns the lifetime of the object and is responsible for freeing it. + // The flag is ignored by _StructuredTaskCollection + bool _GetRuntimeOwnsLifetime() const + { + return _M_fRuntimeOwnsLifetime; + } + + // Allocator to be used when runtime owns lifetime. + template <typename _ChoreType, typename _Function> + static _ChoreType * _InternalAlloc(const _Function& _Func) + { + // This is always invoked from the PPL layer by the user and can never be attached to the default scheduler. Therefore '_concrt_new' is not required here + _ChoreType * _Chore = new _ChoreType(_Func); + _Chore->_M_fRuntimeOwnsLifetime = true; + return _Chore; + } + + // Internal helper routine to prepare for execution as a stolen chore. + void _PrepareSteal(ContextBase *_PContext); + + protected: + // Invocation bridge between the _UnrealizedChore and PPL. + template <typename _ChoreType> + static void __cdecl _InvokeBridge(void * _PContext) + { + auto _PChore = static_cast<_ChoreType *>(_PContext); + (*_PChore)(); + } + + // Place associated task collection in a safe state. + _CRTIMP void _CheckTaskCollection(); + + private: + + friend class _StructuredTaskCollection; + friend class _TaskCollection; + typedef void (__cdecl * CHOREFUNC)(_UnrealizedChore * _PChore); + + // The collection of work to which this particular chore belongs. + Concurrency::details::_TaskCollectionBase * _M_pTaskCollection; + + // Internal invocation inside the scheduler. + CHOREFUNC _M_pChoreFunction; + + // Indicates whether the scheduler owns the lifetime of the object and is responsible for freeing it. + // This flag is ignored by _StructuredTaskCollection + bool _M_fRuntimeOwnsLifetime; + + // An indication of whether the chore (if stolen) was detached. + bool _M_fDetached; + + // Helper routines + void _PrepareStealStructured(ContextBase *_PContext); + void _PrepareStealUnstructured(ContextBase *_PContext); + + // The internal wrapper around invocation of stolen structured chores. + __declspec(noinline) + static void __cdecl _StructuredChoreWrapper(_UnrealizedChore * _PChore); + + // The internal wrapper around invocation of stolen unstructured chores. + __declspec(noinline) + static void __cdecl _UnstructuredChoreWrapper(_UnrealizedChore * _PChore); + + // To free memory allocated with _InternalAlloc. + static void _InternalFree(_UnrealizedChore * _PChore); + + // Cancellation via token to a stolen chore + static void __cdecl _CancelViaToken(::Concurrency::details::ContextBase *pContext); + }; + + // Represents possible results of waiting on a task collection. + enum _TaskCollectionStatus + { + _NotComplete, + _Completed, + _Canceled + }; + + // _TaskCollectionBase represents an abstract set of work and provides shared waiting semantics for stolen work. + class _TaskCollectionBase + { + public: + // Constructs a new task collection. + _TaskCollectionBase() : + _M_pTokenState(NULL), + _M_completedStolenChores(_CollectionNotInitialized), + _M_unpoppedChores(0), + _M_pException(NULL), + _M_inliningDepth(_S_notInlined) + { + } + + // Constructs a new task collection based on a given cancellation token. + _TaskCollectionBase(_CancellationTokenState *_PTokenState) : + _M_pTokenState(_PTokenState), + _M_completedStolenChores(_CollectionNotInitialized), + _M_unpoppedChores(0), + _M_pException(NULL), + _M_inliningDepth(_S_notInlined) + { + } + + // Returns the owning context of the task collection. + void * _OwningContext() const + { + return _M_pOwningContext; + } + + // Returns the inlining depth. + int _InliningDepth() const + { + return _M_inliningDepth; + } + + // Tells if the task collection is inlined - some thread somewhere is currently invoking wait on it. + bool _IsCurrentlyInlined() const + { + return (_M_inliningDepth != _S_notInlined); + } + + // Returns whether this is a structured collection or not. + bool _IsStructured() + { + return (_M_inlineFlags & _S_structured) != 0; + } + + // Returns the token state associated with this task collection + _CancellationTokenState *_GetTokenState(_CancellationTokenRegistration **_PRegistration = NULL); + + protected: + + friend class Concurrency::details::_UnrealizedChore; + friend class Concurrency::details::ContextBase; + + enum _TaskCollectionBaseState + { + _CollectionNotInitialized = LONG_MIN, + _CollectionInitializationInProgress = LONG_MIN+1, + _CollectionInitialized = 0 + }; + + // Returns the exception portion of _M_pException. + std::exception_ptr * _Exception() const + { + return (std::exception_ptr *) ((size_t)_M_pException & ~_S_cancelBitsMask); + } + + // Indicates whether or not this task collection has an abnormal exit. + bool _IsAbnormalExit() const + { + return _M_pException != NULL; + } + + // Returns the cancel flags. + size_t _CancelState() const + { + return (size_t) _M_pException & _S_cancelBitsMask; + } + + // Returns whether or not the collection is marked for cancellation. + bool _IsMarkedForCancellation() const + { + return (_CancelState() & _S_cancelBitsMask) != 0; + } + + // Returns whether an inline cancellation was performed. + bool _PerformedInlineCancel() const + { + _CONCRT_ASSERT(_CancelState() != _S_cancelStarted); + return _CancelState() == _S_cancelShotdownOwner; + } + + bool _PerformedPendingCancel() const + { + _CONCRT_ASSERT(_CancelState() != _S_cancelStarted); + return _CancelState() == _S_cancelDeferredShootdownOwner; + } + + // Returns the parent collection safely. + _TaskCollectionBase *_SafeGetParent() + { + return ((_M_inliningDepth != _S_notInlined) ? _M_pParent : NULL); + } + + // Called in order to determine whether this task collection will interrupt for a pending cancellation at or above it. + bool _WillInterruptForPendingCancel(); + + // Called when an exception is raised on a chore on a given task collection, this makes a determination of what to do with the exception + // and saves it for potential transport back to the thread performing a join on a chore collection. + void _RaisedException(); + + // Potentially rethrows the exception which was set with _RaisedException. The caller has responsibility to ensure that _RaisedException + // was called prior to calling this and that _M_pException has progressed beyond the _S_nonNull state. + void _RethrowException(); + + // Marks the collection for cancellation and returns whether the collection was marked. + bool _MarkCancellation(); + + // Finishes the cancellation state (changing from _S_cancelStarted to one of the other states). Note that only the + // thread which successfully marked cancellation can call this. + void _FinishCancelState(size_t _NewCancelState); + + // Called when a cancellation is raised on a chore on a given task collection. This makes a determination of what to do with the exception + // and saves it for potential transport back to the thread performing a join on a chore collection. Note that every other exception + // has precedence over a cancellation. + void _RaisedCancel(); + + // Tracks the parent collection. (For example, A task collection B created during execution of a chore C on task collection A is + // considered a child of A). + _TaskCollectionBase * _M_pParent; + + // Tracks the inlining depth of this collection for cancellation purposes and packs a series of definition bits. + int _M_inliningDepth : 28; + int _M_inlineFlags : 4; + + // The cancellation token for the task collection. + _CancellationTokenState *_M_pTokenState; + + // The context which owns the task collection. This is the context where the collection is created. + void * _M_pOwningContext; + + // The number of unpopped chores associated with the task collection (set by the derived + // class during chore association. + long _M_unpoppedChores; + + // The number of stolen chores executed so far. + volatile long _M_completedStolenChores; + + // The stored exception which has been marshaled from the thread a stolen chore ran upon to the thread that is waiting on the + // task collection. + // + // The lower two bits of _M_pException are utilized for the cancellation state machine. The upper 30 are the exception pointer. This implies + // that the exception pointer must be 4-byte aligned. Because of intermediate states, the exception pointer cannot be between 0x8 and 0xF. The heap should + // not be allocating such... + // + std::exception_ptr * _M_pException; + + // Cancellation states + static const size_t _S_cancelBitsMask = 0x3; + static const size_t _S_cancelNone = 0x0; + static const size_t _S_cancelStarted = 0x1; + static const size_t _S_cancelDeferredShootdownOwner = 0x2; + static const size_t _S_cancelShotdownOwner = 0x3; + + // Intermediate exceptions. + static const size_t _S_nonNull = 0x8; + static const size_t _S_cancelException = 0xC; + + // initialization state for inlining depth. + static const int _S_notInlined = -1; + + // Inline flags. + static const int _S_structured = 0x00000001; + static const int _S_localCancel = 0x00000002; + static const int _S_reserved = 0x0000000C; + + private: + + // Prevent bad usage of copy-constructor and copy-assignment + _TaskCollectionBase(const _TaskCollectionBase& _Collection); + _TaskCollectionBase& operator=(const _TaskCollectionBase& _Collection); + }; + + /// <summary> + /// Structured task collections represent groups of work which follow a strictly LIFO ordered paradigm + /// queueing and waiting respectively. They can only be waited on once and can only be used from a single thread of execution. + /// </summary> + /**/ + class _StructuredTaskCollection : public _TaskCollectionBase + { + public: + + /// <summary> + /// Construct a new structured task collection. + /// </summary> + /**/ + _StructuredTaskCollection() + { + _Construct(); + _M_pTokenState = NULL; + } + + /// <summary> + /// Construct a new structured task collection whose cancellation is goverened by the supplied cancellation token. + /// </summary> + /// <param name="_PTokenState"> + /// When this cancellation token is canceled, the structured task group will be canceled. + /// </param> + /**/ + _CRTIMP _StructuredTaskCollection(_CancellationTokenState *_PTokenState); + + /// <summary> + /// Destruct a task collection and wait on all associated work to finish. Clients must call '_StructuredTaskCollection::_Wait' + /// or '_StructuredTaskCollection::_RunAndWait' prior to destructing the object. If there are chores remaining in the queues, an + /// exception (missing_wait) is thrown. If the destructor is running because of exception unwinding, it will abort any scheduled work. + /// If another exception occurs because work is aborted, the process will terminate (C++ semantics). + /// </summary> + /**/ + _CRTIMP ~_StructuredTaskCollection(); + + /// <summary> + /// Schedules a chore that can potentially run in parallel. The chore is pushed onto the associated workstealing queue, and + /// will be executed in a LIFO order. Note that the specified chore can be scheduled only on a single task collection at a given time. + /// Any attempt to schedule the same chore multiple times on one or more task collection will result in an invalid_multiple_scheduling + /// exception. After the chore is guaranteed to have been executed (by calling the _Wait method), it can be rescheduled to an + /// arbitrary task collection. + /// </summary> + /// <param name="_PChore"> + /// The new unrealized chore to schedule + /// </param> + /// <param name="_PLocation"> + /// The location where the unrealized chore should execute. Specifying the value NULL here indicates that the unrealized chore does not + /// have specific placement. + /// </param> + /**/ + _CRTIMP void _Schedule(_UnrealizedChore * _PChore, location * _PLocation); + + /// <summary> + /// Schedules a chore that can potentially run in parallel. The chore is pushed onto the associated workstealing queue, and + /// will be executed in a LIFO order. Note that the specified chore can be scheduled only on a single task collection at a given time. + /// Any attempt to schedule the same chore multiple times on one or more task collection will result in an invalid_multiple_scheduling + /// exception. After the chore is guaranteed to have been executed (by calling the _Wait method), it can be rescheduled to an + /// arbitrary task collection. + /// </summary> + /// <param name="_PChore"> + /// The new unrealized chore to schedule + /// </param> + /**/ + _CRTIMP void _Schedule(_UnrealizedChore * _PChore); + + /// <summary> + /// Cancels work on the task collection. + /// </summary> + /**/ + _CRTIMP void _Cancel(); + + /// <summary> + /// Informs the caller whether or not the task collection is currently in the midst of cancellation. Note that this + /// does not necessarily indicate that Cancel was called on the collection (although such certainly qualifies this function + /// to return true). It can be the case that the task collection is executing inline and a task collection further up in the work + /// tree was canceled. In cases such as these where we can determine ahead of time that cancellation will flow through + /// this collection, true will be returned as well. + /// </summary> + /// <returns> + /// An indication of whether the task collection is in the midst of a cancellation (or is guaranteed to be shortly). + /// </returns> + /**/ + _CRTIMP bool _IsCanceling(); + + /// <summary> + /// A cancellation friendly wrapper with which to execute _PChore and then + /// waits for all chores running in the _StructuredTaskCollection to finish (normally or abnormally). This method encapsulates + /// all the running tasks in an exception handling block, and will re-throw any exceptions that occur in any of it tasks + /// (if those exceptions occur on another thread, they are marshaled from that thread to the thread where the _StructuredTaskCollection + /// was created, and re-thrown). After this function returns, the _StructuredTaskCollection cannot be used for scheduling further work. + /// </summary> + /// <param name="_PChore"> + /// An _UnrealizedChore which when non-null will be called to invoke the chore in a cancellation friendly manner. + /// </param> + /// <returns> + /// An indication of the status of the wait. + /// </returns> + /**/ + _CRTIMP _TaskCollectionStatus __stdcall _RunAndWait(_UnrealizedChore * _PChore = NULL); + + /// <summary> + /// Waits for all chores running in the _StructuredTaskCollection to finish (normally or abnormally). This method encapsulates + /// all the running tasks in an exception handling block, and will re-throw any exceptions that occur in any of it tasks + /// (if those exceptions occur on another thread, they are marshaled from that thread to the thread where the _StructuredTaskCollection + /// was created, and re-thrown). After this function returns, the _StructuredTaskCollection cannot be used for scheduling further work. + /// </summary> + /// <returns> + /// An indication of the status of the wait. + /// </returns> + /**/ + _TaskCollectionStatus _Wait() + { + return _RunAndWait(); + } + + /// <summary> + /// Called to cancel any contexts which stole chores from the given collection. + /// </summary> + /**/ + void _CancelStolenContexts(); + + private: + + friend class _UnrealizedChore; + + void _Construct() + { + _M_pOwningContext = NULL; + _M_inlineFlags = _S_structured; + } + + /// <summary> + /// Internal routine to abort work on the task collection. + /// </summary> + /**/ + _CRTIMP void _Abort(); + + /// <summary> + /// Internal routine to clean up after a cancellation token. + /// </summary> + _CRTIMP void _CleanupToken(); + + /// <summary> + /// Performs task cleanup normally done at destruction time. + /// </summary> + /**/ + bool _TaskCleanup() + { + // + // Users are required to call Wait() before letting the destructor run. Otherwise, throw. Note that before throwing, + // we must actually wait on the tasks because they contain pointers into stack frames and unwinding without the wait is + // instant stack corruption. + // + if (_M_unpoppedChores > 0) + { + _Abort(); + + if (!__uncaught_exception()) + { + return false; + } + } + + return true; + } + + /// <summary> + /// Internal initialization of the structured task collection + /// </summary> + /**/ + void _Initialize(); + + /// <summary> + /// Waits on a specified number of stolen chores. + /// </summary> + /// <param name="_StolenChoreCount"> + /// The number of stolen chores to wait on. + /// </param> + /**/ + void _WaitOnStolenChores(long _StolenChoreCount); + + /// <summary> + /// Indicates that a stolen chore has completed. + /// </summary> + /**/ + void _CountUp(); + + /// <summary> + /// The callback which is made when a cancellation occurs via a token associated with a structured_task_group on the boundary + /// of two cancellation tokens. + /// </summary> + /**/ + static void __cdecl _CancelViaToken(_StructuredTaskCollection *pCollection); + + // + // _StructuredTaskCollection::_M_event is used to construct an structured event object only when it is needed to block. The structured event object + // has no state to cleanup, therefore no dtor code is required. + // + _CONCRT_BUFFER _M_event[(sizeof(void*) + sizeof(_CONCRT_BUFFER) - 1) / sizeof(_CONCRT_BUFFER)]; + }; + + /// <summary> + /// Task collections represent groups of work which step outside the strict structuring of the + /// _StructuredTaskCollection definition. Any groups of work which do not follow LIFO ordering, are waited + /// on multiple times, or are passed between arbitrary threads require utilization of this definition + /// of a task collection. It has additional overhead over the _StructuredTaskCollection. + /// </summary> + /**/ + class _TaskCollection : public _TaskCollectionBase + { + public: + + /// <summary> + /// Constructs a new task collection. + /// </summary> + /**/ + _CRTIMP _TaskCollection(); + + /// <summary> + /// Constructs a new task collection whose cancellation is governed by the specified cancellation token state. + /// </summary> + /// <param name="_PTokenState"> + /// When this cancellation token is canceled, the task collection is canceled. + /// </param> + /**/ + _CRTIMP _TaskCollection(_CancellationTokenState *_PTokenState); + + /// <summary> + /// Destroys a task collection. Clients must call '_TaskCollection::_Wait' or '_TaskCollection::_RunAndWait' prior to destructing + /// the object. If there are chores remaining in the queues, an exception (missing_wait) is thrown. If the destructor + /// is running because of exception unwinding, it will abort any scheduled work. If another exception occurs because work + /// is aborted, the process will terminate (C++ semantics). + /// </summary> + /**/ + _CRTIMP ~_TaskCollection(); + + /// <summary> + /// Schedules a chore that can potentially run in parallel. The chore is pushed onto the associated workstealing queue, and + /// will be executed in a LIFO order. The tasks scheduled into a _TaskCollection are scheduled into the current scheduler. + /// Note that the specified chore can be scheduled only on a single task collection at a given time. Any attempt to schedule the same + /// chore multiple times on one or more task collections will result in an invalid_multiple_scheduling exception. After the chore is + /// guaranteed to have been executed (by calling the Wait method), it can be rescheduled to an arbitrary task collection. + /// </summary> + /// <param name="_PChore"> + /// The new unrealized chore to schedule + /// </param> + /// <param name="_PLocation"> + /// The location where the unrealized chore should execute. Specifying the value NULL here indicates that the unrealized chore does not + /// have specific placement. + /// </param> + /**/ + _CRTIMP void _Schedule(_UnrealizedChore * _PChore, location * _PLocation); + + /// <summary> + /// Schedules a chore that can potentially run in parallel. The chore is pushed onto the associated workstealing queue, and + /// will be executed in a LIFO order. The tasks scheduled into a _TaskCollection are scheduled into the current scheduler. + /// Note that the specified chore can be scheduled only on a single task collection at a given time. Any attempt to schedule the same + /// chore multiple times on one or more task collections will result in an invalid_multiple_scheduling exception. After the chore is + /// guaranteed to have been executed (by calling the Wait method), it can be rescheduled to an arbitrary task collection. + /// </summary> + /// <param name="_PChore"> + /// The new unrealized chore to schedule + /// </param> + /**/ + _CRTIMP void _Schedule(_UnrealizedChore * _PChore); + + /// <summary> + /// Cancels work on the task collection. + /// </summary> + /**/ + _CRTIMP void _Cancel(); + + /// <summary> + /// Informs the caller whether or not the task collection is currently in the midst of a cancellation. Note that this + /// does not necessarily indicate that Cancel was called on the collection (although such certainly qualifies this function + /// to return true). It can be the case that the task collection is executing inline and a task collection further up in the work + /// tree was canceled. In cases such as these where we can determine ahead of time that cancellation will flow through + /// this collection, true will be returned as well. + /// </summary> + /// <returns> + /// An indication of whether the task collection is in the midst of a cancellation (or is guaranteed to be shortly). + /// </returns> + /**/ + _CRTIMP bool _IsCanceling(); + + /// <summary> + /// A cancellation friendly wrapper with which to execute _PChore and then + /// waits for all chores running in the _TaskCollection to finish (normally or abnormally). This method encapsulates + /// all the running tasks in an exception handling block, and will re-throw any exceptions that occur in any of it tasks + /// (if those exceptions occur on another thread, they are marshaled from that thread to the thread where the _TaskCollection + /// was created, and re-thrown). After this function returns, the _TaskCollection cannot be used for scheduling further work. + /// </summary> + /// <param name="_PChore"> + /// An _UnrealizedChore which when non-null will be called to invoke the chore in a cancellation friendly manner. + /// </param> + /// <returns> + /// An indication of the status of the wait. + /// </returns> + /// </summary> + /**/ + _CRTIMP _TaskCollectionStatus __stdcall _RunAndWait(_UnrealizedChore * _PChore = NULL); + + /// <summary> + /// Waits for all chores running in the _TaskCollection to finish (normally or abnormally). This method encapsulates + /// all the running tasks in an exception handling block, and will re-throw any exceptions that occur in any of it tasks + /// (if those exceptions occur on another thread, they are marshaled from that thread to the thread where the _TaskCollection + /// was created, and re-thrown). After this function returns, the _TaskCollection cannot be used for scheduling further work. + /// </summary> + /// <returns> + /// An indication of the status of the wait. + /// </returns> + /// </summary> + /**/ + _TaskCollectionStatus _Wait() + { + return _RunAndWait(); + } + + /// <summary> + /// Returns whether this task collection is marked for abnormal exit. + /// </summary> + /**/ + bool _IsMarkedForAbnormalExit() const; + + /// <summary> + /// Returns the object which this is an alias for. + /// </summary> + /**/ + _TaskCollection * _OriginalCollection() const; + + /// <summary> + /// Returns whether the task collection is an alias. + /// </summary> + /**/ + bool _IsAlias() const; + + /// <summary> + /// Registers a notification handler for completion of chores + /// </summary> + /// <param name="_Func"> + /// The callback function + /// </param> + /// <param name="_PCompletionContext"> + /// The completion context for the callback function + /// </param> + /**/ + void _RegisterCompletionHandler(TaskProc _Func, void * _PCompletionContext); + + private: + + friend class _UnrealizedChore; + friend class Concurrency::details::ContextBase; + + /// <summary> + /// Determines if the task collection is a stale alias (an object which was left over from a deferred delete + /// of a direct alias but which happens to match the hash key for a newly allocated task collection) + /// </summary> + /**/ + bool _IsStaleAlias() const; + + /// <summary> + /// Releases an alias -- this will free it if the release is the last man out. + /// </summary> + /**/ + void _ReleaseAlias(); + + /// <summary> + /// Constructs an alias collection based on a specifed origin collection + /// </summary> + /// <param name="_POriginCollection"> + /// Specifies which collection the newly constructed one will alias + /// </param> + /// <param name="_FDirectAlias"> + /// Specifies whether the newly constructed collection is a direct alias + /// </param> + /**/ + _TaskCollection(_TaskCollection * _POriginCollection, bool _FDirectAlias); + + /// <summary> + /// Returns the local alias of a task collection on the current context. + /// </summary> + /**/ + _TaskCollection * _Alias(); + + /// <summary> + /// Internal routine to abort work on the task collection. + /// </summary> + /// <param name="fLeaveCanceled"> + /// An indication as to whether or not to leave the task collection canceled after the abort. + /// </param> + /**/ + void _Abort(bool fLeaveCanceled = false); + + /// <summary> + /// Returns whether the task collection is an indirect alias. + /// </summary> + /**/ + bool _IsIndirectAlias() const; + + /// <summary> + /// Returns whether the task collection is a direct alias. + /// </summary> + /**/ + bool _IsDirectAlias() const; + + /// <summary> + /// Returns whether this task collection has a direct alias. + /// </summary> + /**/ + bool _HasDirectAlias() const; + + /// <summary> + /// Cancels work on the task collection. This is an internal version. + /// </summary> + /// <param name="_InsideException"> + /// Indicates whether the cancellation is taking place because of + /// exception unwinding within the runtime + /// </param> + /// <param name="_PSnapPoint"> + /// A snapshot of the direct alias list which is what the call will effect + /// </param> + /**/ + void _Cancel(bool _InsideException, _TaskCollection * _PSnapPoint); + + /// <summary> + /// Called for every new chore put into the task collection. Assures appropriate synchronization with waiters. + /// </summary> + /**/ + void _NotifyNewChore(); + + /// <summary> + /// Called for every completed chore from the task collection. Assures appropriate synchronization with waiters. + /// </summary> + /// <param name="_PChore"> + /// An _UnrealizedChore which will be freed if its lifetime is owned by the Runtime. + /// </param> + /**/ + void _NotifyCompletedChoreAndFree(_UnrealizedChore * _PChore = NULL); + + /// <summary> + /// Waits on the given task collection and every alias. + /// </summary> + /// <param name="_PSnapPoint"> + /// A snapshot of the direct alias list which is what the call will effect + /// </param> + /**/ + void _FullAliasWait(_TaskCollection * _PSnapPoint); + + /// <summary> + /// Resets the task collection for future usage. + /// </summary> + /// <param name="_PSnapPoint"> + /// A snapshot of the direct alias list which is what the call will effect + /// </param> + /**/ + void _Reset(_TaskCollection * _PSnapPoint); + + /// <summary> + /// Called when an exception is raised on a chore on an unstructured task collection, this makes a determination of what to do with the exception + /// and saves it for potential transport back to the thread performing a join on a task collection. This specifically handles situations + /// on for unstructured task collections before calling _TaskCollectionBase::_RaisedException. + /// </summary> + /**/ + void _RaisedException(); + + /// <summary> + /// Called when a cancellation is raised on a chore on a given task collection. This makes a determination of what to do with the exception + /// and saves it for potential transport back to the thread performing a join on a chore collection. Note that every other exception + /// has precedence over a cancellation. + /// </summary> + /**/ + void _RaisedCancel(); + + /// <summary> + /// Called in order to set the cancellation status of the collection. + /// </summary> + /// <param name="_Status"> + /// The cancellation status to set + /// </param> + /// <returns> + /// An indication of whether the set succeeded. The set will fail if the task collection already has a cancellation status. + /// </returns> + /**/ + bool _SetCancelState(long _Status); + + /// <summary> + /// Called to cancel a single alias of a task collection from an arbitrary thread. + /// </summary> + /// <param name="_InsideException"> + /// Indicates whether the cancellation is taking place because of + /// exception unwinding within the runtime + /// </param> + /**/ + void _CancelFromArbitraryThread(bool _InsideException); + + /// <summary> + /// Cancels all direct aliases of the task collection. + /// </summary> + /// <param name="_InsideException"> + /// Indicates whether the cancellation is taking place because of + /// exception unwinding within the runtime + /// </param> + /// <param name="_PSnapPoint"> + /// A snapshot of the direct alias list which is what the call will effect + /// </param> + /**/ + void _CancelDirectAliases(bool _InsideException, _TaskCollection * _PSnapPoint); + + /// <summary> + /// Called to cancel any contexts which stole chores from the given collection. This is *PART* of a cancellation + /// scheme. The remainder must be handled by the derived class in particular. This should be called last. + /// </summary> + /// <param name="_InsideException"> + /// Indicates whether the cancellation is taking place because of + /// exception unwinding within the runtime + /// </param> + /// <param name="_FInlineGated"> + /// Indicates whether the inline context is safe and blocked from becoming inaccessible during + /// the duration of the call + /// </param> + /**/ + void _CancelStolenContexts(bool _InsideException, bool _FInlineGated); + + /// <summary> + /// Returns the steal tracking list. + /// </summary> + /**/ + void *_GetStealTrackingList() const; + + /// <summary> + /// Internal initialization of the task collection + /// </summary> + /**/ + void _Initialize(); + + /// <summary> + /// Performs an abortive sweep of the WSQ for inline stack overflow. + /// </summary> + /// <param name="_PCtx"> + /// The context to sweep + /// </param> + /**/ + void _AbortiveSweep(void *_PCtx); + + /// <summary> + /// A predicate function checking whether a given chore belongs to a given collection. + /// </summary> + /// <param name="_PChore"> + /// The chore to check + /// </param> + /// <param name="_PData"> + /// The data to check against + /// </param> + /// <returns> + /// Whether or not the chore belongs to the collection + /// </returns> + /**/ + static bool __cdecl _CollectionMatchPredicate(_UnrealizedChore *_PChore, void *_PData); + + /// <summary> + /// Called to sweep an aborted chore in the case of inline stack overflow. + /// </summary> + /// <param name="_PChore"> + /// The chore to sweep + /// </param> + /// <param name="_PData"> + /// The data that was passed to the sweep predicate + /// </param> + /// <returns> + /// An indication of whether the chore is now gone + /// </returns> + /**/ + static bool __cdecl _SweepAbortedChore(_UnrealizedChore *_PChore, void *_PData); + + /// <summary> + /// Performs task cleanup normally done at destruction time. + /// </summary> + /// <param name="fExceptional"> + /// An indication if the cleanup is exceptional and the collection should be left in a canceled state. + /// </param> + /**/ + bool _TaskCleanup(bool fExceptional); + + /// <summary> + /// Called when the task collection is canceled via a cancellation token. + /// </summary> + /**/ + static void __cdecl _CancelViaToken(_TaskCollection *pCollection); + + /// <summary> + /// Tracks contexts that have stolen chores from this collection. This is storage for an internal list and lock. Note that this list is only + /// used for detached schedule groups. + /// </summary> + /**/ + _CONCRT_BUFFER _M_stealTracker[_SAFERWLIST_SIZE]; + + /// <summary> + /// A count of active stealers for *CANCELLATION PURPOSES ONLY*. This is non-interlocked and guarded by the same lock as the + /// stealers list on this task collection. + /// </summary> + /**/ + long _M_activeStealersForCancellation; + + /// <summary> + /// An indication of the exit code of the chore. Anything non-zero here indicates cancellation of one + /// form or another. + /// </summary> + /**/ + volatile long _M_exitCode; + + /// <summary> + /// The status of the task collection. + /// </summary> + /**/ + volatile long _M_executionStatus; + + /// <summary> + /// An event on which to wait for stolen chores to complete. + /// </summary> + /**/ + event _M_event; + + _TaskCollection * _M_pOriginalCollection; + _TaskCollection * _M_pNextAlias; + void * _M_pTaskExtension; + + int _M_taskCookies[2]; + + volatile long _M_flags; + volatile long _M_chaining; + + DWORD _M_boundQueueId; + int _M_stackPos; + + TaskProc _M_completionHandler; + void * _M_pCompletionContext; + }; + + /// <summary> + /// The enum defines inlining scheduling policy for ppltasks. + /// Scheduling a chore or a functor with _TaskInliningMode will give + /// scheduler a hint on whether apply inline execution or not. + /// </summary> + /// <remarks> + /// As an optimization, we assigned an integer number to each option in the enum, + /// which efectively stands for the maximal inlining depth (threshold) for current chore, + /// and the scheduler will compare this threshold with current context's inlining depth to + /// make inline decision. + /// If the current context's inlining depth greater than this threshold, + /// the chore will be scheduled on a new context, otherwise the chore will be scheduled inline. + /// Minimal threshold 0 means do not inline; maximal threshold -1 (0xFFFFFFFF....) means always inline. + /// 16 is a good default inlining threshold we figured out from experiment. + /// </remarks> + enum _TaskInliningMode + { + // Disable inline scheduling + _NoInline = 0, + // Let runtime decide whether to do inline scheduling or not + _DefaultAutoInline = 16, + // Always do inline scheduling + _ForceInline = -1, + }; + + /// <summary> + /// RAII wrapper used to maintain and limit ppltask maximum inline schedule depth. + /// This class will keep a reference to the depth slot on current context. + /// </summary> + class _StackGuard + { + public: + _StackGuard() : _Depth(_GetCurrentInlineDepth()) + { + // _Depth is the reference to the depth slot on context. + ++_Depth; + } + ~_StackGuard() + { + // _Depth is the reference to the depth slot on context. + --_Depth; + } + + bool _ShouldInline(_TaskInliningMode _InliningMode) const + { + // As _TaskInliningMode is defined as inlining threshold, we can directly convert + // it into size_t, and compare with current context inlining depth. + return _Depth <= static_cast<size_t>(_InliningMode); + } + private: + size_t & _Depth; + _StackGuard & operator =(const _StackGuard &); + + /// <summary> + /// Return a reference to the ppltask inline schedule depth slot on current context + /// The inline depth will be set to 0 when the context is first initialized, + /// and the caller is responsible to maintain that depth. + /// </summary> + _CRTIMP static size_t & __cdecl _GetCurrentInlineDepth(); + }; + + /// <summary> + /// Async Task collections is a thin wrapper over task collection to cater to the execution of asynchronous + /// chores (or tasks defined in ppltasks.h). Specifically, they manage their own lifetime by using reference + /// counts. Scheduling a chore acquires a reference and on completion of its execution the reference is released. + /// </summary> + class _AsyncTaskCollection : public _RefCounterBase + { + public: + + /// <summary> + /// Constructs a new task collection whose cancellation is governed by the specified cancellation token state. + /// </summary> + /// <param name="_PTokenState"> + /// When this cancellation token is canceled, the task collection is canceled. + /// </param> + /// <returns> + /// Pointer to a new instance of _AsyncTaskCollection. + /// </returns> + _CRTIMP static _AsyncTaskCollection * __cdecl _NewCollection(_CancellationTokenState *_PTokenState); + + /// <summary> + /// Schedule a chore with automatic inlining. The chore is pushed onto the associated workstealing queue, and + /// will be executed in a LIFO order. The tasks scheduled into a _TaskCollection are scheduled into the current scheduler. + /// Note that the specified chore can be scheduled only on a single task collection at a given time. Any attempt to schedule the same + /// chore multiple times on one or more task collections will result in an invalid_multiple_scheduling exception. After the chore is + /// guaranteed to have been executed (by calling the Wait method), it can be rescheduled to an arbitrary task collection. + /// This schedule method will perform automatic inlining base on <paramref value="_InliningMode"/>. + /// </summary> + /// <param name="_PChore"> + /// The new unrealized chore need to be scheduled. The chore will be deleted after scheduling. + /// </param> + /// <param name="_InliningMode"> + /// The inlining scheduling policy for current chore. + /// </param> + /// <returns> + /// An indication of current chore status after scheduling. + /// </returns> + _TaskCollectionStatus _ScheduleWithAutoInline(_UnrealizedChore * _PChore, _TaskInliningMode _InliningMode) + { + _CONCRT_ASSERT(_PChore); + _Reference(); + + if (_InliningMode == _NoInline) + { + _M_taskCollection._Schedule(_PChore); + return _NotComplete; + } + else + { + _StackGuard _Guard; + if (_Guard._ShouldInline(_InliningMode)) + { + return _M_taskCollection._RunAndWait(_PChore); + } + else + { + _M_taskCollection._Schedule(_PChore); + return _NotComplete; + } + } + } + + /// <summary> + /// Cancels work on the task collection. + /// </summary> + void _Cancel() + { + _M_taskCollection._Cancel(); + } + + /// <summary> + /// A cancellation friendly wrapper with which to execute _PChore and then + /// waits for all chores running in the _TaskCollection to finish (normally or abnormally). This method encapsulates + /// all the running tasks in an exception handling block, and will re-throw any exceptions that occur in any of it tasks + /// (if those exceptions occur on another thread, they are marshaled from that thread to the thread where the _TaskCollection + /// was created, and re-thrown). After this function returns, the _TaskCollection cannot be used for scheduling further work. + /// </summary> + /// <param name="_PChore"> + /// An _UnrealizedChore which when non-null will be called to invoke the chore in a cancellation friendly manner. + /// </param> + /// <returns> + /// An indication of the status of the wait. + /// </returns> + _TaskCollectionStatus _RunAndWait() + { + // Note that _Guard is NOT unused variable, the constructor and destructor will be called to maintain inline depth. + _StackGuard _Guard; + return _M_taskCollection._RunAndWait(); + } + + private: + + void _NotificationHandler(); + + _CRTIMP virtual void _Destroy(); + + // Private constructor + _AsyncTaskCollection(_CancellationTokenState *_PTokenState); + + __declspec(noinline) + static void __cdecl _CompletionHandler(void * _PCompletionContext); + + private: + + // Underlying task collection where the chore is scheduled to run + _TaskCollection _M_taskCollection; + }; + + /// <summary> + /// Internal maintainence structure for beacons. + /// </summary> + struct _Beacon_reference + { + volatile long _M_signals; + }; + + typedef void (__cdecl * _UnobservedExceptionHandler)(void); + _CRTIMP void __cdecl _SetUnobservedExceptionHandler(_UnobservedExceptionHandler); + + // Used to report unobserved task exceptions in ppltasks.h + _CRTIMP void __cdecl _ReportUnobservedException(); + + /// <summary> + /// A cancellation beacon is a flag which can be polled in an inlinable fashion using the is_signaled method in lieu of polling on + /// the more expensive non inlinable is_current_task_group_canceling method. + /// </summary> + /// <remarks> + /// Cancellation beacons can be used only in the same way as structured_task_group and _StructuredTaskCollection. They are intended + /// as stack based objects utilized in strictly nested RAII fashion. A beacon can *NOT* be passed to another thread or allocated on the + /// heap. + /// </remarks> + class _Cancellation_beacon + { + public: + + _CRTIMP _Cancellation_beacon(); + + _CRTIMP ~_Cancellation_beacon(); + + bool _Is_signaled() const + { + return (_M_pRef->_M_signals != 0); + } + + // This method should only be called when the beacon is signaled. It confirms whether a cancellation is indeed happening and that the beacon + // was not flagged due to a false positive race. If the cancellation is not confirmed, the beacon is lowered. + _CRTIMP bool _Confirm_cancel(); + + void _Raise() + { + _InterlockedIncrement(&_M_pRef->_M_signals); + } + + void _Lower() + { + _InterlockedDecrement(&_M_pRef->_M_signals); + } + + private: + + _Beacon_reference *_M_pRef; + + }; + + // + // Internal stub class. + // + class _TimerStub; + + // + // Internal wrapper around timers in order to allow timer messaging blocks to share implementation with internal ConcRT runtime + // timers. + // + class _Timer + { + protected: + // Constructs a new timer. + // + // _Ms: The duration and period of the timer in milliseconds. + // _FRepeating: An indication of whether the timer is repeating (periodic) or not. + _CRTIMP _Timer(unsigned int _Ms, bool _FRepeating); + + // Destroys the timer. + _CRTIMP virtual ~_Timer(); + + // Starts the timer. + _CRTIMP void _Start(); + + // Stops the timer. + _CRTIMP void _Stop(); + + private: + friend class _TimerStub; + + // Called when the timer fires. + virtual void _Fire() =0; + + // The actual timer + HANDLE _M_hTimer; + + // The duration and period of the timer. + unsigned int _M_ms; + + // Whether the timer is repeating (periodic by _M_ms) + bool _M_fRepeating; + }; + + // + // Internal runtime structure that holds the trace flags and level for ETW events + // provided by the Concurrent runtime. + // + struct _CONCRT_TRACE_INFO + { + volatile unsigned long EnableFlags; // Determines which class of events to log + volatile unsigned char EnableLevel; // Determines the serverity of events to log + + void _EnableTrace(unsigned char level, unsigned long flags) + { + EnableFlags = flags; + EnableLevel = level; + } + + void _DisableTrace() + { + EnableLevel = 0; + EnableFlags = 0; + } + + bool _IsEnabled(unsigned char level, unsigned long flags) const + { + return ((level <= EnableLevel) && ((EnableFlags & flags) == flags)); + } + }; + + /// <summary> + /// Retrieves a pointer to the internal trace flags and level information for + /// the Concurrency runtime ETW provider. + /// </summary> + /**/ + _CRTIMP const _CONCRT_TRACE_INFO * _GetConcRTTraceInfo(); + + /// <summary> + /// Register ConcRT as an ETW Event Provider. + /// </summary> + /**/ + void _RegisterConcRTEventTracing(); + + /// <summary> + /// Unregister ConcRT as an ETW Event Provider. + /// </summary> + /**/ + void _UnregisterConcRTEventTracing(); + +} // namespace details + + +/// <summary> +/// Enables tracing in the Concurrency Runtime. This function is deprecated because ETW tracing is now on by default. +/// </summary> +/// <returns> +/// If tracing was correctly initiated, <c>S_OK</c> is returned; otherwise, <c>E_NOT_STARTED</c> is returned. +/// </returns> +/**/ +__declspec(deprecated("Concurrency::EnableTracing is a deprecated function.")) _CRTIMP HRESULT __cdecl EnableTracing(); + +/// <summary> +/// Disables tracing in the Concurrency Runtime. This function is deprecated because ETW tracing is unregistered by default. +/// </summary> +/// <returns> +/// If tracing was correctly disabled, <c>S_OK</c> is returned. If tracing was not previously initiated, +/// <c>E_NOT_STARTED</c> is returned +/// </returns> +/**/ +__declspec(deprecated("Concurrency::DisableTracing is a deprecated function.")) _CRTIMP HRESULT __cdecl DisableTracing(); + +/// <summary> +/// The types of events that can be traced using the tracing functionality offered by the Concurrency Runtime. +/// </summary> +/**/ +enum ConcRT_EventType +{ + /// <summary> + /// An event type used for miscellaneous events. + /// </summary> + /**/ + CONCRT_EVENT_GENERIC = 0, + /// <summary> + /// An event type that marks the beginning of a start/end event pair. + /// </summary> + /**/ + CONCRT_EVENT_START = 1, + /// <summary> + /// An event type that marks the beginning of a start/end event pair. + /// </summary> + /**/ + CONCRT_EVENT_END = 2, + /// <summary> + /// An event type that represents the act of a context blocking. + /// </summary> + /**/ + CONCRT_EVENT_BLOCK = 3, + /// <summary> + /// An event type that represents the act of unblocking a context. + /// </summary> + /**/ + CONCRT_EVENT_UNBLOCK = 4, + /// <summary> + /// An event type that represents the act of a context yielding. + /// </summary> + /**/ + CONCRT_EVENT_YIELD = 5, + /// <summary> + /// An event type that represents the act of a context becoming idle. + /// </summary> + /**/ + CONCRT_EVENT_IDLE = 6, + /// <summary> + /// An event type that represents the act of a attaching to a scheduler. + /// </summary> + /**/ + CONCRT_EVENT_ATTACH = 7, + /// <summary> + /// An event type that represents the act of a detaching from a scheduler. + /// </summary> + /**/ + CONCRT_EVENT_DETACH = 8, +}; + +// Common trace header structure for all ConcRT diagnostic events +// struct CONCRT_TRACE_EVENT_HEADER_COMMON +// { +// EVENT_TRACE_HEADER header; +// DWORD VirtualProcessorID; +// DWORD SchedulerID; +// DWORD ContextID; +// DWORD ScheduleGroupID; +// }; + +/// <summary> +/// The ETW provider GUID for the Concurrency Runtime. +/// </summary> +/**/ +extern "C" const __declspec(selectany) GUID ConcRT_ProviderGuid = { 0xF7B697A3, 0x4DB5, 0x4d3b, { 0xBE, 0x71, 0xC4, 0xD2, 0x84, 0xE6, 0x59, 0x2F } }; + +// +// GUIDS for events +// + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are not more specifically described by another category. +/// </summary> +/// <remarks> +/// This category of events is not currently fired by the Concurrency Runtime. +/// </remarks> +/**/ +extern "C" const __declspec(selectany) GUID ConcRTEventGuid = { 0x72B14A7D, 0x704C, 0x423e, { 0x92, 0xF8, 0x7E, 0x6D, 0x64, 0xBC, 0xB9, 0x2A } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to scheduler activity. +/// </summary> +/// <seealso cref="CurrentScheduler Class"/> +/// <seealso cref="Scheduler Class"/> +/**/ +extern "C" const __declspec(selectany) GUID SchedulerEventGuid = { 0xE2091F8A, 0x1E0A, 0x4731, { 0x84, 0xA2, 0x0D, 0xD5, 0x7C, 0x8A, 0x52, 0x61 } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to schedule groups. +/// </summary> +/// <remarks> +/// This category of events is not currently fired by the Concurrency Runtime. +/// </remarks> +/// <seealso cref="ScheduleGroup Class"/> +/**/ +extern "C" const __declspec(selectany) GUID ScheduleGroupEventGuid = { 0xE8A3BF1F, 0xA86B, 0x4390, { 0x9C, 0x60, 0x53, 0x90, 0xB9, 0x69, 0xD2, 0x2C } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to contexts. +/// </summary> +/// <seealso cref="Context Class"/> +/**/ +extern "C" const __declspec(selectany) GUID ContextEventGuid = { 0x5727A00F, 0x50BE, 0x4519, { 0x82, 0x56, 0xF7, 0x69, 0x98, 0x71, 0xFE, 0xCB } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to chores or tasks. +/// </summary> +/// <remarks> +/// This category of events is not currently fired by the Concurrency Runtime. +/// </remarks> +/// <seealso cref="task_group Class"/> +/// <seealso cref="structured_task_group Class"/> +/**/ +extern "C" const __declspec(selectany) GUID ChoreEventGuid = { 0x7E854EC7, 0xCDC4, 0x405a, { 0xB5, 0xB2, 0xAA, 0xF7, 0xC9, 0xE7, 0xD4, 0x0C } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to virtual processors. +/// </summary> +/**/ +extern "C" const __declspec(selectany) GUID VirtualProcessorEventGuid = { 0x2f27805f, 0x1676, 0x4ecc, { 0x96, 0xfa, 0x7e, 0xb0, 0x9d, 0x44, 0x30, 0x2f } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to locks. +/// </summary> +/// <remarks> +/// This category of events is not currently fired by the Concurrency Runtime. +/// </remarks> +/// <seealso cref="critical_section Class"/> +/// <seealso cref="reader_writer_lock Class"/> +/**/ +extern "C" const __declspec(selectany) GUID LockEventGuid = { 0x79A60DC6, 0x5FC8, 0x4952, { 0xA4, 0x1C, 0x11, 0x63, 0xAE, 0xEC, 0x5E, 0xB8 } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to the resource manager. +/// </summary> +/// <remarks> +/// This category of events is not currently fired by the Concurrency Runtime. +/// </remarks> +/// <seealso cref="IResourceManager Structure"/> +/**/ +extern "C" const __declspec(selectany) GUID ResourceManagerEventGuid = { 0x2718D25B, 0x5BF5, 0x4479, { 0x8E, 0x88, 0xBA, 0xBC, 0x64, 0xBD, 0xBF, 0xCA } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to usage of the <c>parallel_invoke</c> +/// function. +/// </summary> +/// <seealso cref="parallel_invoke Function"/> +/**/ +extern "C" const __declspec(selectany) GUID PPLParallelInvokeEventGuid = { 0xd1b5b133, 0xec3d, 0x49f4, { 0x98, 0xa3, 0x46, 0x4d, 0x1a, 0x9e, 0x46, 0x82 } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to usage of the <c>parallel_for</c> +/// function. +/// </summary> +/// <seealso cref="parallel_for Function"/> +/**/ +extern "C" const __declspec(selectany) GUID PPLParallelForEventGuid = { 0x31c8da6b, 0x6165, 0x4042, { 0x8b, 0x92, 0x94, 0x9e, 0x31, 0x5f, 0x4d, 0x84 } }; + +/// <summary> +/// A category GUID describing ETW events fired by the Concurrency Runtime that are directly related to usage of the <c>parallel_for_each</c> +/// function. +/// </summary> +/// <seealso cref="parallel_for_each Function"/> +/**/ +extern "C" const __declspec(selectany) GUID PPLParallelForeachEventGuid = { 0x5cb7d785, 0x9d66, 0x465d, { 0xba, 0xe1, 0x46, 0x11, 0x6, 0x1b, 0x54, 0x34 } }; + +/// <summary> +/// A category GUID ({B9B5B78C-0713-4898-A21A-C67949DCED07}) describing ETW events fired by the Agents library in the Concurrency Runtime. +/// </summary> +/**/ +extern "C" const __declspec(selectany) GUID AgentEventGuid = {0xb9b5b78c, 0x713, 0x4898, { 0xa2, 0x1a, 0xc6, 0x79, 0x49, 0xdc, 0xed, 0x7 } }; + +// Trace an event signaling a parallel function +_CRTIMP void __cdecl _Trace_ppl_function(const GUID& _Guid, unsigned char _Level, ConcRT_EventType _Type); + +/// <summary> +/// Trace flags for the event types +/// </summary> +/**/ +enum Concrt_TraceFlags +{ + SchedulerEventFlag = 0x1, + ContextEventFlag = 0x2, + VirtualProcessorEventFlag = 0x4, + ResourceManagerEventFlag = 0x8, + PPLEventFlag = 0x10, + AgentEventFlag = 0x20, + + AllEventsFlag = 0xFFFFFFFF +}; + +/// <summary> +/// The types of events that can be traced using the tracing functionality offered by the Agents Library +/// </summary> +/**/ +enum Agents_EventType +{ + /// <summary> + /// An event type that represents the creation of an object + /// </summary> + /**/ + AGENTS_EVENT_CREATE = 0, + + /// <summary> + /// An event type that represents the initiation of some processing + /// </summary> + /**/ + AGENTS_EVENT_START = 1, + + /// <summary> + /// An event type that represents the conclusion of some processing + /// </summary> + /**/ + AGENTS_EVENT_END = 2, + + /// <summary> + /// An event type that represents the deletion of an object + /// </summary> + /**/ + AGENTS_EVENT_DESTROY = 3, + + /// <summary> + /// An event type that represents the scheduling of a process + /// </summary> + /**/ + AGENTS_EVENT_SCHEDULE = 4, + + /// <summary> + /// An event type that represents the linking of message blocks + /// </summary> + /**/ + AGENTS_EVENT_LINK = 5, + + /// <summary> + /// An event type that represents the unlinking of message blocks + /// </summary> + /**/ + AGENTS_EVENT_UNLINK = 6, + + /// <summary> + /// An event type that represents the name for an object + /// </summary> + /**/ + AGENTS_EVENT_NAME = 7 + +}; + +// // Common trace payload for agents +// +// struct AGENTS_TRACE_PAYLOAD +// { +// // Identifier of the agent or message block that is emitting the event +// __int64 AgentId1; +// union +// { +// // The identifier of a target block for link/unlink event +// __int64 AgentId2; +// +// // Count of messages processed for the end event +// long Count; +// +// // Name of this agent for the purposes of the ETW trace +// wchar_t Name[32]; +// }; +// }; + +// Emit a trace event specific to the agents library of the given type and payload +_CRTIMP void __cdecl _Trace_agents(Agents_EventType _Type, __int64 agentId, ...); +} + +namespace concurrency = Concurrency; + +#pragma pop_macro("new") +#pragma pack(pop) diff --git a/test_data/lots_of_files/concrtrm.h b/test_data/lots_of_files/concrtrm.h new file mode 100644 index 0000000..7b0e084 --- /dev/null +++ b/test_data/lots_of_files/concrtrm.h @@ -0,0 +1,1459 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* concrtrm.h +* +* Main public header file for ConcRT's Resource Manager. This is the only header file a client +* must include to build atop the resource manager. +* +* The core runtime, the Agents and Message Blocks Library, and the Parallel Patterns Library (PPL) +* are defined in different header files. +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include <crtdefs.h> + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) + #error ERROR: Concurrency Runtime is supported only on X64, X86 and ARM architectures. +#endif /* !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_M_CEE) + #error ERROR: Concurrency Runtime is not supported when compiling /clr. +#endif /* defined (_M_CEE) */ + +#ifndef __cplusplus + #error ERROR: Concurrency Runtime is supported only for C++. +#endif /* __cplusplus */ + +#pragma pack(push,_CRT_PACKING) + +/// <summary> +/// The <c>Concurrency</c> namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + // + // Forward Declarations: + // + struct IScheduler; + struct IThreadProxy; + class SchedulerPolicy; + + /// <summary> + /// Used to denote the state a thread proxy is in, when it is executing a cooperative context switch to a different thread + /// proxy. + /// </summary> + /// <remarks> + /// A parameter of type <c>SwitchingProxyState</c> is passed in to the method <c>IThreadProxy::SwitchTo</c> to + /// instruct the Resource Manager how to treat the thread proxy that is making the call. + /// <para>For more information on how this type is used, see <see cref="IThreadProxy::SwitchTo Method">IThreadProxy::SwitchTo + /// </see>.</para> + /// </remarks> + /**/ + enum SwitchingProxyState + { + /// <summary> + /// Indicates that the calling thread is no longer needed by the scheduler and is being returned to the Resource Manager. The + /// context which was being dispatched is no longer able to be utilized by the Resource Manager. + /// </summary> + /**/ + Idle, + + /// <summary> + /// Indicates that the calling thread is cooperatively blocking and should be exclusively owned by the caller until subsequently + /// running again and performing other action. + /// </summary> + /**/ + Blocking, + + /// <summary> + /// Indicates that the calling thread is nesting a child scheduler and is needed by the caller, in order to attach to a + /// different scheduler. + /// </summary> + /**/ + Nesting + }; + + /// <summary> + /// The <c>DispatchState</c> structure is used to transfer state to the <c>IExecutionContext::Dispatch</c> method. It describes + /// the circumstances under which the <c>Dispatch</c> method is invoked on an <c>IExecutionContext</c> interface. + /// </summary> + /// <seealso cref="IExecutionContext::Dispatch Method"/> + /**/ + struct DispatchState + { + /// <summary> + /// Constructs a new <c>DispatchState</c> object. + /// </summary> + /**/ + DispatchState() : m_dispatchStateSize(sizeof(DispatchState)), m_fIsPreviousContextAsynchronouslyBlocked(0), m_reserved(0) + { + } + + /// <summary> + /// Size of this structure, which is used for versioning. + /// </summary> + /**/ + unsigned long m_dispatchStateSize; + + /// <summary> + /// Tells whether this context has entered the <c>Dispatch</c> method because the previous context asynchronously blocked. + /// This is used only on the UMS scheduling context, and is set to the value <c>0</c> for all other execution contexts. + /// </summary> + /// <seealso cref="IExecutionContext::Dispatch Method"/> + /**/ + unsigned int m_fIsPreviousContextAsynchronouslyBlocked : 1; + + /// <summary> + /// Bits reserved for future information passing. + /// </summary> + /// <seealso cref="IExecutionContext::Dispatch Method"/> + /**/ + unsigned int m_reserved : 31; + }; + + /// <summary> + /// An interface to an execution context which can run on a given virtual processor and be cooperatively context switched. + /// </summary> + /// <remarks> + /// If you are implementing a custom scheduler that interfaces with the Concurrency Runtime's Resource Manager, you will need + /// to implement the <c>IExecutionContext</c> interface. The threads created by the Resource Manager perform work on behalf + /// of your scheduler by executing the <c>IExecutionContext::Dispatch</c> method. + /// </remarks> + /// <seealso cref="IScheduler Structure"/> + /// <seealso cref="IThreadProxy Structure"/> + /**/ + struct IExecutionContext + { + /// <summary> + /// Returns a unique identifier for the execution context. + /// </summary> + /// <returns> + /// A unique integer identifier. + /// </returns> + /// <remarks> + /// You should use the method <c>GetExecutionContextId</c> to obtain a unique identifier for the object that implements the + /// <c>IExecutionContext</c> interface, before you use the interface as a parameter to methods supplied by the Resource Manager. + /// You are expected to return the same identifier when the <c>GetId</c> function is invoked. <para> An identifier obtained from a different + /// source could result in undefined behavior.</para> + /// </remarks> + /// <seealso cref="GetExecutionContextId Function"/> + /**/ + virtual unsigned int GetId() const =0; + + /// <summary> + /// Returns an interface to the scheduler this execution context belongs to. + /// </summary> + /// <returns> + /// An <c>IScheduler</c> interface. + /// </returns> + /// <remarks> + /// You are required to initialize the execution context with a valid <c>IScheduler</c> interface before you use it as a parameter to + /// methods supplied by the Resource Manager. + /// </remarks> + /**/ + virtual IScheduler * GetScheduler() =0; + + /// <summary> + /// Returns an interface to the thread proxy that is executing this context. + /// </summary> + /// <returns> + /// An <c>IThreadProxy</c> interface. If the execution context's thread proxy has not been initialized with a call to <c>SetProxy</c>, + /// the function must return <c>NULL</c>. + /// </returns> + /// <remarks> + /// The Resource Manager will invoke the <c>SetProxy</c> method on an execution context, with an <c>IThreadProxy</c> interface + /// as a parameter, prior to entering the <c>Dispatch</c> method on the on the context. You are expected to store this argument and return it + /// on calls to <c>GetProxy()</c>. + /// </remarks> + /// <seealso cref="IExecutionContext::SetProxy Method"/> + /**/ + virtual IThreadProxy * GetProxy() =0; + + /// <summary> + /// Associates a thread proxy with this execution context. The associated thread proxy invokes this method right before it starts + /// executing the context's <c>Dispatch</c> method. + /// </summary> + /// <param name="pThreadProxy"> + /// An interface to the thread proxy that is about to enter the <c>Dispatch</c> method on this execution context. + /// </param> + /// <remarks> + /// You are expected to save the parameter <paramref name="pThreadProxy"/> and return it on a call to the <c>GetProxy</c> method. + /// The Resource Manager guarantees that the thread proxy associated with the execution context will not change while the + /// thread proxy is executing the <c>Dispatch</c> method. + /// </remarks> + /// <seealso cref="IExecutionContext::GetProxy Method"/> + /**/ + virtual void SetProxy(_Inout_ IThreadProxy * pThreadProxy) =0; + + /// <summary> + /// The method that is called when a thread proxy starts executing a particular execution context. This should be the main worker + /// routine for your scheduler. + /// </summary> + /// <param name="pDispatchState"> + /// A pointer to the state under which this execution context is being dispatched. For more information on dispatch state, see + /// <see cref="DispatchState Structure">DispatchState</see>. + /// </param> + /**/ + virtual void Dispatch(_Inout_ DispatchState * pDispatchState) =0; + }; + + /// <summary> + /// An abstraction for a thread of execution. Depending on the <c>SchedulerType</c> policy key of the scheduler you create, the Resource + /// Manager will grant you a thread proxy that is backed by either a regular Win32 thread or a user-mode schedulable (UMS) thread. + /// UMS threads are supported on 64-bit operating systems with version Windows 7 and higher. + /// </summary> + /// <remarks> + /// Thread proxies are coupled to execution contexts represented by the interface <c>IExecutionContext</c> as a means of dispatching work. + /// </remarks> + /// <seealso cref="IExecutionContext Structure"/> + /// <seealso cref="IScheduler Structure"/> + /// <seealso cref="IVirtualProcessorRoot Structure"/> + /**/ + struct IThreadProxy + { + /// <summary> + /// Returns a unique identifier for the thread proxy. + /// </summary> + /// <returns> + /// A unique integer identifier. + /// </returns> + /**/ + virtual unsigned int GetId() const =0; + + /// <summary> + /// Performs a cooperative context switch from the currently executing context to a different one. + /// </summary> + /// <param name="pContext"> + /// The execution context to cooperatively switch to. + /// </param> + /// <param name="switchState"> + /// Indicates the state of the thread proxy that is executing the switch. The parameter is of type <typeparamref name="SwitchingProxyState"/>. + /// </param> + /// <remarks> + /// Use this method to switch from one execution context to another, from the <see cref="IExecutionContext::Dispatch Method"> + /// IExecutionContext::Dispatch </see> method of the first execution context. + /// The method associates the execution context <paramref name="pContext"/> with a thread proxy if it is not already associated + /// with one. The ownership of the current thread proxy is determined by the value you specify for the <paramref name="switchState"/> + /// argument. + /// + /// <para> Use the value <c>Idle</c> when you want to return the currently executing thread proxy to the Resource Manager. + /// Calling <c>SwitchTo</c> with the parameter <paramref name="switchState"/> set to <c>Idle</c> will cause + /// the execution context <paramref name="pContext"/> to start executing on the underlying execution resource. Ownership of + /// this thread proxy is transferred to the Resource Manager, and you are expected to return from the execution context's + /// <c>Dispatch</c> method soon after <c>SwitchTo</c> returns, in order to complete the transfer. The execution context that the + /// thread proxy was dispatching is disassociated from the thread proxy, and the scheduler is free to reuse it or destroy it + /// as it sees fit.</para> + /// + /// <para> Use the value <c>Blocking</c> when you want this thread proxy to enter a blocked state. Calling + /// <c>SwitchTo</c> with the parameter <paramref name="switchState"/> set to <c>Blocking</c> will cause the execution context + /// <paramref name="pContext"/> to start executing, and block the current thread proxy until it is resumed. The scheduler retains + /// ownership of the thread proxy when the thread proxy is in the <c>Blocking</c> state. The blocking thread proxy + /// can be resumed by calling the function <c>SwitchTo</c> to switch to this thread proxy's execution context. You can also + /// resume the thread proxy, by using its associated context to activate a virtual processor root. For more information on how + /// to do this, see <see cref="IVirtualProcessorRoot::Activate Method"> IVirtualProcessorRoot::Activate</see>.</para> + /// + /// <para> Use the value <c>Nesting</c> when you want to temporarily detach this thread proxy from the virtual processor root + /// it is running on, and the scheduler it is dispatching work for. Calling <c>SwitchTo</c> with the parameter <paramref name="switchState"/> + /// set to <c>Nesting</c> will cause the execution context <paramref name="pContext"/> to start executing and the + /// current thread proxy also continues executing without the need for a virtual processor root. The thread proxy is considered + /// to have left the scheduler until it calls the <see cref="IThreadProxy::SwitchOut Method">IThreadProxy::SwitchOut</see> + /// method at a later point in time. The <c>IThreadProxy::SwitchOut</c> method could block the thread proxy until a virtual + /// processor root is available to reschedule it.</para> + /// <para><c>SwitchTo</c> must be called on the <c>IThreadProxy</c> interface that represents the currently executing thread + /// or the results are undefined. The function throws <c>invalid_argument</c> if the parameter <paramref name="pContext"/> + /// is set to <c>NULL</c>.</para> + /// </remarks> + /// <seealso cref="SwitchingProxyState Enumeration"/> + /**/ + virtual void SwitchTo(_Inout_ IExecutionContext * pContext, SwitchingProxyState switchState) =0; + + /// <summary> + /// Disassociates the context from the underlying virtual processor root. + /// </summary> + /// <param name="switchState"> + /// Indicates the state of the thread proxy that is executing the switch. The parameter is of type <typeparamref name="SwitchingProxyState"/>. + /// </param> + /// <remarks> + /// Use <c>SwitchOut</c> if you need to disassociate a context from the virtual processor root it is executing on, for any reason. Depending + /// on the value you pass in to the parameter <paramref name="switchState"/>, and whether or not it is executing on a virtual processor root, + /// the call will either return immediately or block the thread proxy associated with the context. It is an error to call <c>SwitchOut</c> with + /// the parameter set to <c>Idle</c>. Doing so will result in an <see cref="invalid_argument Class">invalid_argument</see> exception. + /// + /// <para><c>SwitchOut</c> is useful when you want to reduce the number of virtual processor roots your scheduler has, either because the Resource + /// Manager has instructed you to do so, or because you requested a temporary oversubscribed virtual processor root, and are done with it. + /// In this case you should invoke the method <see cref="IVirtualProcessorRoot::Remove Method"/> on the virtual processor root, before making + /// a call to <c>SwitchOut</c> with the parameter <paramref name="switchState"/> set to <c>Blocking</c>. This will block the thread proxy and it + /// will resume execution when a different virtual processor root in the scheduler is available to execute it. The blocking thread proxy can be + /// resumed by calling the function <c>SwitchTo</c> to switch to this thread proxy's execution context. You can also resume the thread proxy, + /// by using its associated context to activate a virtual processor root. For more information on how to do this, see + /// <see cref="IVirtualProcessorRoot::Activate Method"> IVirtualProcessorRoot::Activate</see>.</para> + /// + /// <para><c>SwitchOut</c> may also be used when you want reinitialize the virtual processor so it may be activated in the future while either + /// blocking the thread proxy or temporarily detaching it from the virtual processor root it is running on, and the scheduler it is dispatching + /// work for. Use <c>SwitchOut</c> with the parameter <paramref name="switchState"/> set to <c>Blocking</c> if you wish to block the thread proxy. + /// It can later be resumed using either <c>SwitchTo</c> or <c>IVirtualProcessorRoot::Activate</c> as noted above. Use <c>SwitchOut</c> with the + /// parameter set to <c>Nesting</c> when you want to temporarily detach this thread proxy from the virtual processor root it is running on, + /// and the scheduler the virtual processor is associated with. Calling <c>SwitchOut</c> with the parameter <paramref name="switchState"/> + /// set to <c>Nesting</c> while it is executing on a virtual processor root will cause the root to be reinitialized and the current thread proxy + /// to continue executing without the need for one. The thread proxy is considered to have left the scheduler until it calls the + /// <see cref="IThreadProxy::SwitchOut Method">IThreadProxy::SwitchOut</see> method with <c>Blocking</c> at a later point in time. The second + /// call to <c>SwitchOut</c> with the parameter set to <c>Blocking</c> is intended to return the context to a blocked state so that it can be + /// resumed by either <c>SwitchTo</c> or <c>IVirtualProcessorRoot::Activate</c> in the scheduler it detached from. Because it was not executing + /// on a virtual processor root, no reinitialization takes place.</para> + /// + /// <para>A reinitialized virtual processor root is no different from a brand new virtual processor root your scheduler has been granted by the Resource + /// Manager. You can use it for execution by activating it with an execution context using <c>IVirtualProcessorRoot::Activate</c>.</para> + /// + /// <para><c>SwitchOut</c> must be called on the <c>IThreadProxy</c> interface that represents the currently executing thread + /// or the results are undefined.</para> + /// + /// <para>In the libraries and headers that shipped with Visual Studio 2010, this method did not take a parameter and did not reinitialize the + /// virtual processor root. To preserve old behavior when you upgrade to Visual Studio 2012, the default parameter value of <c>Blocking</c> is supplied.</para> + /// </remarks> + /**/ + virtual void SwitchOut(SwitchingProxyState switchState = Blocking) =0; + + /// <summary> + /// Causes the calling thread to yield execution to another thread that is ready to run on the current processor. The operating + /// system selects the next thread to be executed. + /// </summary> + /// <remarks> + /// When called by a thread proxy backed by a regular Windows thread, <c>YieldToSystem</c> behaves exactly like the Windows function + /// <c>SwitchToThread</c>. However, when called from user-mode schedulable (UMS) threads, the <c>SwitchToThread</c> function delegates the task + /// of picking the next thread to run to the user mode scheduler, not the operating system. To achieve the desired effect of switching + /// to a different ready thread in the system, use <c>YieldToSystem</c>. + /// <para><c>YieldToSystem</c> must be called on the <c>IThreadProxy</c> interface that represents the currently executing thread + /// or the results are undefined.</para> + /// </remarks> + /**/ + virtual void YieldToSystem() = 0; + }; + + /// <summary> + /// The type of critical region a context is inside. + /// </summary> + /// <seealso cref="IUMSThreadProxy Structure"/> + /**/ + enum CriticalRegionType + { + /// <summary> + /// Indicates that the context is outside any critical region. + /// </summary> + /**/ + OutsideCriticalRegion, + + /// <summary> + /// Indicates that the context is inside a critical region. When inside a critical region, asynchronous suspensions are hidden from + /// the scheduler. Should such a suspension happen, the Resource Manager will wait for the thread to become runnable and simply resume it instead + /// of invoking the scheduler again. Any locks taken inside such a region must be taken with extreme care. + /// </summary> + /**/ + InsideCriticalRegion, + + /// <summary> + /// Indicates that the context is inside a hyper-critical region. When inside a hyper-critical region, both synchronous and asynchronous + /// suspensions are hidden from the scheduler. Should such a suspension or blocking happen, the resource manager will wait for the thread to + /// become runnable and simply resume it instead of invoking the scheduler again. Locks taken inside such a region must never be shared with + /// code running outside such a region. Doing so will cause unpredictable deadlock. + /// </summary> + /**/ + InsideHyperCriticalRegion + }; + + /// <summary> + /// An abstraction for a thread of execution. If you want your scheduler to be granted user-mode schedulable (UMS) threads, set the value for the + /// scheduler policy element <c>SchedulerKind</c> to <c>UmsThreadDefault</c>, and implement the <c>IUMSScheduler</c> interface. + /// UMS threads are only supported on 64-bit operating systems with version Windows 7 and higher. + /// </summary> + /// <seealso cref="IUMSScheduler Structure"/> + /// <seealso cref="SchedulerType Enumeration"/> + /**/ + struct IUMSThreadProxy : public IThreadProxy + { + /// <summary> + /// Called in order to enter a critical region. When inside a critical region, the scheduler will not observe asynchronous blocking operations + /// that happen during the region. This means that the scheduler will not be reentered for page faults, thread suspensions, kernel asynchronous + /// procedure calls (APCs), and so forth, for a UMS thread. + /// </summary> + /// <returns> + /// The new depth of critical region. Critical regions are reentrant. + /// </returns> + /// <seealso cref="IUMSThreadProxy::ExitCriticalRegion Method"/> + /**/ + virtual int EnterCriticalRegion() =0; + + /// <summary> + /// Called in order to exit a critical region. + /// </summary> + /// <returns> + /// The new depth of critical region. Critical regions are reentrant. + /// </returns> + /// <seealso cref="IUMSThreadProxy::EnterCriticalRegion Method"/> + /**/ + virtual int ExitCriticalRegion() =0; + + /// <summary> + /// Called in order to enter a hyper-critical region. When inside a hyper-critical region, the scheduler will not observe any blocking operations + /// that happen during the region. This means the scheduler will not be reentered for blocking function calls, lock acquisition attempts which + /// block, page faults, thread suspensions, kernel asynchronous procedure calls (APCs), and so forth, for a UMS thread. + /// </summary> + /// <returns> + /// The new depth of hyper-critical region. Hyper-critical regions are reentrant. + /// </returns> + /// <remarks> + /// The scheduler must be extraordinarily careful about what methods it calls and what locks it acquires in such regions. If code in such a + /// region blocks on a lock that is held by something the scheduler is responsible for scheduling, deadlock may ensue. + /// </remarks> + /// <seealso cref="IUMSThreadProxy::ExitHyperCriticalRegion Method"/> + /**/ + virtual int EnterHyperCriticalRegion() =0; + + /// <summary> + /// Called in order to exit a hyper-critical region. + /// </summary> + /// <returns> + /// The new depth of hyper-critical region. Hyper-critical regions are reentrant. + /// </returns> + /// <seealso cref="IUMSThreadProxy::EnterHyperCriticalRegion Method"/> + /**/ + virtual int ExitHyperCriticalRegion() =0; + + /// <summary> + /// Returns what kind of critical region the thread proxy is within. Because hyper-critical regions are a superset of critical regions, if code + /// has entered a critical region and then a hyper-critical region, <c>InsideHyperCriticalRegion</c> will be returned. + /// </summary> + /// <returns> + /// The type of critical region the thread proxy is within. + /// </returns> + /// <seealso cref="CriticalRegionType Enumeration"/> + /**/ + virtual CriticalRegionType GetCriticalRegionType() const =0; + }; + + /// <summary> + /// An abstraction for a hardware thread. + /// </summary> + /// <remarks> + /// Execution resources can be standalone or associated with virtual processor roots. A standalone execution resource is created when + /// a thread in your application creates a thread subscription. The methods <see cref="ISchedulerProxy::SubscribeCurrentThread Method"> + /// ISchedulerProxy::SubscribeThread</see> and <see cref="ISchedulerProxy::RequestInitialVirtualProcessors Method"> + /// ISchedulerProxy::RequestInitialVirtualProcessors</see> create thread subscriptions, and return an <c>IExecutionResource</c> interface + /// representing the subscription. Creating a thread subscription is a way to inform the Resource Manager that a given thread will participate + /// in the work queued to a scheduler, along with the virtual processor roots Resource Manager assigns to the scheduler. + /// The Resource Manager uses the information to avoid oversubscribing hardware threads where it can. + /// </remarks> + /// <seealso cref="IVirtualProcessorRoot Structure"/> + /// <seealso cref="ISchedulerProxy::SubscribeCurrentThread Method"/> + /// <seealso cref="ISchedulerProxy::RequestInitialVirtualProcessors Method"/> + /**/ + struct IExecutionResource + { + /// <summary> + /// Returns a unique identifier for the processor node that this execution resource belongs to. + /// </summary> + /// <returns> + /// A unique identifier for a processor node. + /// </returns> + /// <remarks> + /// The Concurrency Runtime represents hardware threads on the system in groups of processor nodes. Nodes are usually derived from + /// the hardware topology of the system. For example, all processors on a specific socket or a specific NUMA node may belong to the + /// same processor node. The Resource Manager assigns unique identifiers to these nodes starting with <c>0</c> up to and including + /// <c>nodeCount - 1</c>, where <c>nodeCount</c> represents the total number of processor nodes on the system. + /// <para>The count of nodes can be obtained from the function <see cref="GetProcessorNodeCount Function">GetProcessorNodeCount</see>.</para> + /// </remarks> + /**/ + virtual unsigned int GetNodeId() const =0; + + /// <summary> + /// Returns a unique identifier for the hardware thread that this execution resource represents. + /// </summary> + /// <returns> + /// A unique identifier for the hardware thread underlying this execution resource. + /// </returns> + /// <remarks> + /// Each hardware thread is assigned a unique identifier by the Concurrency Runtime. If multiple execution resources are associated + /// hardware thread, they will all have the same execution resource identifier. + /// </remarks> + /**/ + virtual unsigned int GetExecutionResourceId() const =0; + + /// <summary> + /// Returns this execution resource to the Resource Manager. + /// </summary> + /// <param name="pScheduler"> + /// An interface to the scheduler making the request to remove this execution resource. + /// </param> + /// <remarks> + /// Use this method to return standalone execution resources as well as execution resources associated with virtual processor roots to + /// the Resource Manager. + /// <para>If this is a standalone execution resource you received from either of the methods <see cref="ISchedulerProxy::SubscribeCurrentThread Method"> + /// ISchedulerProxy::SubscribeCurrentThread</see> or <see cref="ISchedulerProxy::RequestInitialVirtualProcessors Method"> + /// ISchedulerProxy::RequestInitialVirtualProcessors</see>, calling the method <c>Remove</c> will end the thread subscription that the + /// resource was created to represent. You are required to end all thread subscriptions before shutting down a scheduler proxy, and must + /// call <c>Remove</c> from the thread that created the subscription.</para> + /// <para>Virtual processor roots, too, can be returned to the Resource Manager by invoking the <c>Remove</c> method, because the interface + /// <c>IVirtualProcessorRoot</c> inherits from the <c>IExecutionResource</c> interface. You may need to return a virtual processor root either + /// in response to a call to the <see cref="IScheduler::RemoveVirtualProcessors Method">IScheduler::RemoveVirtualProcessors</see> + /// method, or when you are done with an oversubscribed virtual processor root you obtained from the <see cref="ISchedulerProxy::CreateOversubscriber Method"> + /// ISchedulerProxy::CreateOversubscriber</see> method. For virtual processor roots, there are no restrictions on which thread can invoke + /// the <c>Remove</c> method.</para> + /// <para><c>invalid_argument</c> is thrown if the parameter <paramref name="pScheduler"/> is set to <c>NULL</c>.</para> + /// <para><c>invalid_operation</c> is thrown if the parameter <paramref name="pScheduler"/> is different from the scheduler that this + /// execution resource was created for, or, with a standalone execution resource, if the current thread is different from the + /// thread that created the thread subscription.</para> + /// </remarks> + /// <seealso cref="invalid_argument Class"/> + /// <seealso cref="invalid_operation Class"/> + /**/ + virtual void Remove(_Inout_ IScheduler * pScheduler) =0; + + /// <summary> + /// Returns the number of activated virtual processor roots and subscribed external threads currently associated with the underlying + /// hardware thread this execution resource represents. + /// </summary> + /// <returns> + /// The current subscription level. + /// </returns> + /// <remarks> + /// The subscription level tells you how many running threads are associated with the hardware thread. This only includes threads + /// the Resource Manager is aware of in the form of subscribed threads, and virtual processor roots that are actively executing + /// thread proxies. + /// <para>Calling the method <see cref="ISchedulerProxy::SubscribeCurrentThread Method">ISchedulerProxy::SubscribeCurrentThread</see>, + /// or the method <see cref="ISchedulerProxy::RequestInitialVirtualProcessors Method">ISchedulerProxy::RequestInitialVirtualProcessors + /// </see> with the parameter <paramref name="doSubscribeCurrentThread"/> set to the value <c>true</c> increments the subscription + /// level of a hardware thread by one. They also return an <c>IExecutionResource</c> interface representing the subscription. A + /// corresponding call to the <see cref="IExecutionResource::Remove Method"> IExecutionResource::Remove</see> decrements the + /// hardware thread's subscription level by one.</para> + /// <para>The act of activating a virtual processor root using the method <see cref="IVirtualProcessorRoot::Activate Method"> + /// IVirtualProcessorRoot::Activate</see> increments the subscription level of a hardware thread by one. The methods + /// <see cref="IVirtualProcessorRoot::Deactivate Method">IVirtualProcessorRoot::Deactivate</see>, or + /// <see cref="IExecutionResource::Remove Method">IExecutionResource::Remove</see> decrement the subscription level by one + /// when invoked on an activated virtual processor root.</para> + /// <para>The Resource Manager uses subscription level information as one of the ways in which to determine when to move resources + /// between schedulers.</para> + /// </remarks> + /**/ + virtual unsigned int CurrentSubscriptionLevel() const =0; + }; + + /// <summary> + /// An abstraction for a hardware thread on which a thread proxy can execute. + /// </summary> + /// <remarks> + /// Every virtual processor root has an associated execution resource. The <c>IVirtualProcessorRoot</c> interface inherits from the + /// <see cref="IExecutionResource Structure">IExecutionResource</see> interface. Multiple virtual processor roots may correspond to the same + /// underlying hardware thread. + /// <para>The Resource Manager grants virtual processor roots to schedulers in response to requests for resources. A scheduler can use + /// a virtual processor root to perform work by activating it with an execution context.</para> + /// </remarks> + /**/ + struct IVirtualProcessorRoot : public IExecutionResource + { + /// <summary> + /// Returns a unique identifier for the virtual processor root. + /// </summary> + /// <returns> + /// An integer identifier. + /// </returns> + /**/ + virtual unsigned int GetId() const =0; + + /// <summary> + /// Causes the thread proxy associated with the execution context interface <paramref name="pContext"/> to start executing on this + /// virtual processor root. + /// </summary> + /// <param name="pContext"> + /// An interface to the execution context that will be dispatched on this virtual processor root. + /// </param> + /// <remarks> + /// The Resource Manager will supply a thread proxy if one is not associated with the execution context interface <paramref name="pContext"/> + /// <para>The <c>Activate</c> method can be used to start executing work on a new virtual processor root returned by the Resource Manager, or to resume + /// the thread proxy on a virtual processor root that has deactivated or is about to deactivate. See <see cref="IVirtualProcessorRoot::Deactivate Method"> + /// IVirtualProcessorRoot::Deactivate</see> for more information on deactivation. When you are resuming a deactivated virtual processor + /// root, the parameter <paramref name="pContext"/> must be the same as the parameter used to deactivate the virtual processor root.</para> + /// <para> Once a virtual processor root has been activated for the first time, subsequent pairs of calls to <c>Deactivate</c> and + /// <c>Activate</c> may race with each other. This means it is acceptable for the Resource Manager to receive a call to <c>Activate</c> + /// before it receives the <c>Deactivate</c> call it was meant for.</para> + /// <para>When you activate a virtual processor root, you signal to the Resource Manager that this virtual processor root is currently + /// busy with work. If your scheduler cannot find any work to execute on this root, it is expected to invoke the <c>Deactivate</c> method + /// informing the Resource Manager that the virtual processor root is idle. The Resource Manager uses this data to + /// load balance the system.</para> + /// <para><c>invalid_argument</c> is thrown if the argument <paramref name="pContext"/> has the value <c>NULL</c>.</para> + /// <para><c>invalid_operation</c> is thrown if the argument <paramref name="pContext"/> does not represent the execution context that + /// was most recently dispatched by this virtual processor root.</para> + /// <para>The act of activating a virtual processor root increases the subscription level of the underlying hardware thread by one. For more + /// information on subscription levels, see <see cref="IExecutionResource::CurrentSubscriptionLevel Method"> + /// IExecutionResource::CurrentSubscriptionLevel</see>.</para> + /// </remarks> + /// <seealso cref="IVirtualProcessorRoot::Deactivate Method"/> + /// <seealso cref="IExecutionResource::CurrentSubscriptionLevel Method"/> + /**/ + virtual void Activate(_Inout_ IExecutionContext * pContext) =0; + + /// <summary> + /// Causes the thread proxy currently executing on this virtual processor root to stop dispatching the execution context. The thread proxy + /// will resume executing on a call to the <c>Activate</c> method. + /// </summary> + /// <param name="pContext"> + /// The context which is currently being dispatched by this root. + /// </param> + /// <returns> + /// A boolean value. A value of <c>true</c> indicates that the thread proxy returned from the <c>Deactivate</c> method in response to + /// a call to the <c>Activate</c> method. A value of <c>false</c> indicates that the thread proxy returned from the method in response + /// to a notification event in the Resource Manager. On a user-mode schedulable (UMS) thread scheduler, this indicates that items have + /// appeared on the scheduler's completion list, and the scheduler is required to handle them. + /// </returns> + /// <remarks> + /// Use this method to temporarily stop executing a virtual processor root when you cannot find any work in your scheduler. + /// A call to the <c>Deactivate</c> method must originate from within the <c>Dispatch</c> method of the execution context that + /// the virtual processor root was last activated with. In other words, the thread proxy invoking the <c>Deactivate</c> method + /// must be the one that is currently executing on the virtual processor root. Calling the method on a virtual processor + /// root you are not executing on could result in undefined behavior. + /// <para>A deactivated virtual processor root may be woken up with a call to the <c>Activate</c> method, with the same + /// argument that was passed in to the <c>Deactivate</c> method. The scheduler is responsible for ensuring that calls to the <c>Activate</c> + /// and <c>Deactivate</c> methods are paired, but they are not required to be received in a specific order. The Resource + /// Manager can handle receiving a call to the <c>Activate</c> method before it receives a call to the <c>Deactivate</c> method it was + /// meant for.</para> + /// <para>If a virtual processor root awakens and the return value from the <c>Deactivate</c> method is the value <c>false</c>, the scheduler + /// should query the UMS completion list via the <c>IUMSCompletionList::GetUnblockNotifications</c> method, act on that information, and + /// then subsequently call the <c>Deactivate</c> method again. This should be repeated until such time as the <c>Deactivate</c> method returns + /// the value <c>true</c>.</para> + /// <para><c>invalid_argument</c> is thrown if the argument <paramref name="pContext"/> has the value <c>NULL</c>.</para> + /// <para><c>invalid_operation</c> is thrown if the virtual processor root has never been activated, or the argument <paramref name="pContext"/> + /// does not represent the execution context that was most recently dispatched by this virtual processor root.</para> + /// <para>The act of deactivating a virtual processor root decreases the subscription level of the underlying hardware thread by one. For + /// more information on subscription levels, see <see cref="IExecutionResource::CurrentSubscriptionLevel Method"> + /// IExecutionResource::CurrentSubscriptionLevel</see>.</para> + /// </remarks> + /// <seealso cref="IVirtualProcessorRoot::Activate Method"/> + /// <seealso cref="IExecutionResource::CurrentSubscriptionLevel Method"/> + /// <seealso cref="IUMSCompletionList::GetUnblockNotifications Method"/> + /**/ + virtual bool Deactivate(_Inout_ IExecutionContext * pContext) =0; + + /// <summary> + /// Causes data stored in the memory hierarchy of individual processors to become visible to all processors on the system. + /// It ensures that a full memory fence has been executed on all processors before the method returns. + /// </summary> + /// <param name="pContext"> + /// The context which is currently being dispatched by this virtual processor root. + /// </param> + /// <remarks> + /// You may find this method useful when you want to synchronize deactivation of a virtual processor root with the addition of new work into + /// the scheduler. For performance reasons, you may decide to add work items to your scheduler without executing a memory barrier, which + /// means work items added by a thread executing on one processor are not immediately visible to all other processors. By using this method + /// in conjunction with the <c>Deactivate</c> method you can ensure that your scheduler does not deactivate all its virtual processor + /// roots while work items exist in your scheduler's collections. + /// <para> A call to the <c>EnsureAllTasksVisibleThe</c> method must originate from within the <c>Dispatch</c> method of the execution + /// context that the virtual processor root was last activated with. In other words, the thread proxy invoking the <c>EnsureAllTasksVisible</c> + /// method must be the one that is currently executing on the virtual processor root. Calling the method on a virtual processor + /// root you are not executing on could result in undefined behavior.</para> + /// <para><c>invalid_argument</c> is thrown if the argument <paramref name="pContext"/> has the value <c>NULL</c>.</para> + /// <para><c>invalid_operation</c> is thrown if the virtual processor root has never been activated, or the argument <paramref name="pContext"/> + /// does not represent the execution context that was most recently dispatched by this virtual processor root.</para> + /// </remarks> + /// <seealso cref="IVirtualProcessorRoot::Deactivate Method"/> + /**/ + virtual void EnsureAllTasksVisible(_Inout_ IExecutionContext *pContext) =0; + }; + + /// <summary> + /// An interface to an abstraction of a work scheduler. The Concurrency Runtime's Resource Manager uses this interface to communicate with work + /// schedulers. + /// </summary> + /// <remarks> + /// If you are implementing a custom scheduler that communicates with the Resource Manager, you should provide an implementation of the + /// <c>IScheduler</c> interface. This interface is one end of a two-way channel of communication between a scheduler and the + /// Resource Manager. The other end is represented by the <c>IResourceManager</c> and <c>ISchedulerProxy</c> interfaces which are + /// implemented by the Resource Manager. + /// </remarks> + /// <seealso cref="PolicyElementKey Enumeration"/> + /// <seealso cref="SchedulerPolicy Class"/> + /// <seealso cref="IExecutionContext Structure"/> + /// <seealso cref="IThreadProxy Structure"/> + /// <seealso cref="IVirtualProcessorRoot Structure"/> + /// <seealso cref="IResourceManager Structure"/> + /**/ + struct IScheduler + { + /// <summary> + /// Returns a unique identifier for the scheduler. + /// </summary> + /// <returns> + /// A unique integer identifier. + /// </returns> + /// <remarks> + /// You should use the <see cref="GetSchedulerId Function">GetSchedulerId</see> function to obtain a unique identifier for the object + /// that implements the <c>IScheduler</c> interface, before you use the interface as a parameter to methods supplied by the Resource Manager. + /// You are expected to return the same identifier when the <c>GetId</c> function is invoked. <para> An identifier obtained from a different + /// source could result in undefined behavior.</para> + /// </remarks> + /**/ + virtual unsigned int GetId() const =0; + + /// <summary> + /// Provides information related to task arrival and completion rates, and change in queue length for a scheduler. + /// </summary> + /// <param name="pTaskCompletionRate"> + /// The number of tasks that have been completed by the scheduler since the last call to this method. + /// </param> + /// <param name="pTaskArrivalRate"> + /// The number of tasks that have arrived in the scheduler since the last call to this method. + /// </param> + /// <param name="pNumberOfTasksEnqueued"> + /// The total number of tasks in all scheduler queues. + /// </param> + /// <remarks> + /// This method is invoked by the Resource Manager in order to gather statistics for a scheduler. The statistics gathered here + /// will be used to drive dynamic feedback algorithms to determine when it is appropriate to assign more resources to + /// the scheduler and when to take resources away. The values provided by the scheduler can be optimistic and do not necessarily + /// have to reflect the current count accurately. + /// <para> You should implement this method if you want the Resource Manager to use feedback about such things as task arrival to determine + /// how to balance resource between your scheduler and other schedulers registered with the Resource Manager. If you choose not to + /// gather statistics, you can set the policy key <c>DynamicProgressFeedback</c> to the value <c>DynamicProgressFeedbackDisabled</c> + /// in your scheduler's policy, and the Resource Manager will not invoke this method on your scheduler.</para> + /// <para>In the absence of statistical information, the Resource Manager will use hardware thread subscription levels to make + /// resource allocation and migration decisions. For more information on subscription levels, see + /// <see cref="IExecutionResource::CurrentSubscriptionLevel Method"> IExecutionResource::CurrentSubscriptionLevel</see>.</para> + /// </remarks> + /// <seealso cref="PolicyElementKey Enumeration"/> + /// <seealso cref="IExecutionResource::CurrentSubscriptionLevel Method"/> + /**/ + virtual void Statistics(_Out_ unsigned int * pTaskCompletionRate, _Out_ unsigned int * pTaskArrivalRate, _Out_ unsigned int * pNumberOfTasksEnqueued) =0; + + /// <summary> + /// Returns a copy of the scheduler's policy. For more information on scheduler policies, see <see cref="SchedulerPolicy Class"> + /// SchedulerPolicy</see>. + /// </summary> + /// <returns> + /// A copy of the scheduler's policy. + /// </returns> + /// <seealso cref="SchedulerPolicy Class"/> + /**/ + virtual SchedulerPolicy GetPolicy() const =0; + + /// <summary> + /// Provides a scheduler with a set of virtual processor roots for its use. Each <c>IVirtualProcessorRoot</c> interface represents + /// the right to execute a single thread that can perform work on behalf of the scheduler. + /// </summary> + /// <param name="ppVirtualProcessorRoots"> + /// An array of <c>IVirtualProcessorRoot</c> interfaces representing the virtual processor roots being added to the scheduler. + /// </param> + /// <param name="count"> + /// The number of <c>IVirtualProcessorRoot</c> interfaces in the array. + /// </param> + /// <remarks> + /// The Resource Manager invokes the <c>AddVirtualProcessor</c> method to grant an initial set of virtual processor roots to + /// a scheduler. It could also invoke the method to add virtual processor roots to the scheduler when it rebalances resources + /// among schedulers. + /// </remarks> + /// <seealso cref="IVirtualProcessorRoot Structure"/> + /// <seealso cref="IScheduler::RemoveVirtualProcessors Method"/> + /**/ + virtual void AddVirtualProcessors(_In_reads_(count) IVirtualProcessorRoot ** ppVirtualProcessorRoots, unsigned int count) =0; + + /// <summary> + /// Initiates the removal of virtual processor roots that were previously allocated to this scheduler. + /// </summary> + /// <param name="ppVirtualProcessorRoots"> + /// An array of <c>IVirtualProcessorRoot</c> interfaces representing the virtual processor roots to be removed. + /// </param> + /// <param name="count"> + /// The number of <c>IVirtualProcessorRoot</c> interfaces in the array. + /// </param> + /// <remarks> + /// The Resource Manager invokes the <c>RemoveVirtualProcessors</c> method to take back a set of virtual processor roots from + /// a scheduler. The scheduler is expected to invoke the <see cref="IExecutionResource::Remove Method">Remove</see> method on each + /// interface when it is done with the virtual processor roots. Do not use an <c>IVirtualProcessorRoot</c> interface once you have + /// invoked the <c>Remove</c> method on it. + /// <para>The parameter <paramref name="ppVirtualProcessorRoots"/> points to an array of interfaces. Among the set of virtual processor + /// roots to be removed, the roots have never been activated can be returned immediately using the <c>Remove</c> method. + /// The roots that have been activated and are either executing work, or have been deactivated and are waiting for work to arrive, should be + /// returned asynchronously. The scheduler must make every attempt to remove the virtual processor root as quickly as possible. + /// Delaying removal of the virtual processor roots may result in unintentional oversubscription within the scheduler.</para> + /// </remarks> + /// <seealso cref="IVirtualProcessorRoot Structure"/> + /// <seealso cref="IScheduler::RemoveVirtualProcessors Method"/> + /**/ + virtual void RemoveVirtualProcessors(_In_reads_(count) IVirtualProcessorRoot ** ppVirtualProcessorRoots, unsigned int count) =0; + + /// <summary> + /// Notifies this scheduler that the hardware threads represented by the set of virtual processor roots in the array + /// <paramref name="ppVirtualProcessorRoots"/> are not being used by other schedulers. + /// </summary> + /// <param name="ppVirtualProcessorRoots"> + /// An array of <c>IVirtualProcessorRoot</c> interfaces associated with hardware threads on which other schedulers have become idle. + /// </param> + /// <param name="count"> + /// The number of <c>IVirtualProcessorRoot</c> interfaces in the array. + /// </param> + /// <remarks> + /// It is possible for a particular hardware thread to be assigned to multiple schedulers at the same time. One reason for this could be + /// that there are not enough hardware threads on the system to satisfy the minimum concurrency for all schedulers, without sharing resources. + /// Another possibility is that resources are temporarily assigned to other schedulers when the owning scheduler is not using them, by way of + /// all its virtual processor roots on that hardware thread being deactivated. + /// <para>The subscription level of a hardware thread is denoted by the number of subscribed threads and activated virtual processor roots associated + /// with that hardware thread. From a particular scheduler's point of view, the external subscription level of a hardware thread is the portion + /// of the subscription other schedulers contribute to. Notifications that resources are externally busy are sent to a scheduler when the external + /// subscription level for a hardware thread falls to zero from a previous positive value.</para> + /// <para>Notifications via this method are only sent to schedulers that have a policy where the value for the <c>MinConcurrency</c> + /// policy key is equal to the value for the <c>MaxConcurrency</c> policy key. For more information on scheduler policies, + /// see <see cref="SchedulerPolicy Class">SchedulerPolicy</see>.</para> + /// <para>A scheduler that qualifies for notifications gets a set of initial notifications when it is created, informing it whether the + /// resources it was just assigned are externally busy or idle.</para> + /// </remarks> + /// <seealso cref="IExecutionResource::CurrentSubscriptionLevel Method"/> + /// <seealso cref="IScheduler::NotifyResourcesExternallyBusy Method"/> + /**/ + virtual void NotifyResourcesExternallyIdle(_In_reads_(count) IVirtualProcessorRoot ** ppVirtualProcessorRoots, unsigned int count) =0; + + /// <summary> + /// Notifies this scheduler that the hardware threads represented by the set of virtual processor roots in the array + /// <paramref name="ppVirtualProcessorRoots"/> are now being used by other schedulers. + /// </summary> + /// <param name="ppVirtualProcessorRoots"> + /// An array of <c>IVirtualProcessorRoot</c> interfaces associated with the hardware threads on which other schedulers have become busy. + /// </param> + /// <param name="count"> + /// The number of <c>IVirtualProcessorRoot</c> interfaces in the array. + /// </param> + /// <remarks> + /// It is possible for a particular hardware thread to be assigned to multiple schedulers at the same time. One reason for this could be + /// that there are not enough hardware threads on the system to satisfy the minimum concurrency for all schedulers, without sharing resources. + /// Another possibility is that resources are temporarily assigned to other schedulers when the owning scheduler is not using them, by way of + /// all its virtual processor roots on that hardware thread being deactivated. + /// <para>The subscription level of a hardware thread is denoted by the number of subscribed threads and activated virtual processor roots associated + /// with that hardware thread. From a particular scheduler's point of view, the external subscription level of a hardware thread is the portion + /// of the subscription other schedulers contribute to. Notifications that resources are externally busy are sent to a scheduler when the external + /// subscription level for a hardware thread moves from zero into positive territory.</para> + /// <para>Notifications via this method are only sent to schedulers that have a policy where the value for the <c>MinConcurrency</c> + /// policy key is equal to the value for the <c>MaxConcurrency</c> policy key. For more information on scheduler policies, + /// see <see cref="SchedulerPolicy Class">SchedulerPolicy</see>.</para> + /// <para>A scheduler that qualifies for notifications gets a set of initial notifications when it is created, informing it whether the + /// resources it was just assigned are externally busy or idle.</para> + /// </remarks> + /// <seealso cref="IExecutionResource::CurrentSubscriptionLevel Method"/> + /// <seealso cref="IScheduler::NotifyResourcesExternallyIdle Method"/> + /**/ + virtual void NotifyResourcesExternallyBusy(_In_reads_(count) IVirtualProcessorRoot ** ppVirtualProcessorRoots, unsigned int count) =0; + }; + + /// <summary> + /// Represents a notification from the Resource Manager that a thread proxy which blocked and triggered a return to the scheduler's + /// designated scheduling context has unblocked and is ready to be scheduled. This interface is invalid once the thread proxy's + /// associated execution context, returned from the <c>GetContext</c> method, is rescheduled. + /// </summary> + /// <seealso cref="IUMSScheduler Structure"/> + /// <seealso cref="IUMSCompletionList Structure"/> + /**/ + struct IUMSUnblockNotification + { + /// <summary> + /// Returns the <c>IExecutionContext</c> interface for the execution context associated with the thread proxy which has + /// unblocked. Once this method returns and the underlying execution context has been rescheduled via a call to the + /// <c>IThreadProxy::SwitchTo</c> method, this interface is no longer valid. + /// </summary> + /// <returns> + /// An <c>IExecutionContext</c> interface for the execution context to a thread proxy which has unblocked. + /// </returns> + /**/ + virtual IExecutionContext* GetContext() =0; + + /// <summary> + /// Returns the next <c>IUMSUnblockNotification</c> interface in the chain returned from the method + /// <c>IUMSCompletionList::GetUnblockNotifications</c>. + /// </summary> + /// <returns> + /// The next <c>IUMSUnblockNotification</c> interface in the chain returned from the method <c>IUMSCompletionList::GetUnblockNotifications</c>. + /// </returns> + /**/ + virtual IUMSUnblockNotification* GetNextUnblockNotification() =0; + }; + + /// <summary> + /// Represents a UMS completion list. When a UMS thread blocks, the scheduler's designated scheduling context is dispatched + /// in order to make a decision of what to schedule on the underlying virtual processor root while the original thread is blocked. When the + /// original thread unblocks, the operating system queues it to the completion list which is accessible through this interface. The scheduler can + /// query the completion list on the designated scheduling context or any other place it searches for work. + /// </summary> + /// <remarks> + /// A scheduler must be extraordinarily careful about what actions are performed after utilizing this interface to dequeue items from the completion + /// list. The items should be placed on the scheduler's list of runnable contexts and be generally accessible as soon as possible. It is entirely + /// possible that one of the dequeued items has been given ownership of an arbitrary lock. The scheduler can make no arbitrary function calls that may + /// block between the call to dequeue items and the placement of those items on a list that can be generally accessed from within the scheduler. + /// </remarks> + /// <seealso cref="IUMSScheduler Structure"/> + /// <seealso cref="IUMSUnblockNotification Structure"/> + /**/ + struct IUMSCompletionList + { + /// <summary> + /// Retrieves a chain of <c>IUMSUnblockNotification</c> interfaces representing execution contexts whose associated thread proxies + /// have unblocked since the last time this method was invoked. + /// </summary> + /// <returns> + /// A chain of <c>IUMSUnblockNotification</c> interfaces. + /// </returns> + /// <remarks> + /// The returned notifications are invalid once the execution contexts are rescheduled. + /// </remarks> + /// <seealso cref="IUMSUnblockNotification Structure"/> + /**/ + virtual IUMSUnblockNotification *GetUnblockNotifications() =0; + }; + + /// <summary> + /// An interface to an abstraction of a work scheduler that wants the Concurrency Runtime's Resource Manager to hand it user-mode + /// schedulable (UMS) threads. The Resource Manager uses this interface to communicate with UMS thread schedulers. The <c>IUMSScheduler</c> interface + /// inherits from the <c>IScheduler</c> interface. + /// </summary> + /// <remarks> + /// If you are implementing a custom scheduler that communicates with the Resource Manager, and you want UMS threads to be handed to your scheduler + /// instead of ordinary Win32 threads, you should provide an implementation of the <c>IUMSScheduler</c> interface. In addition, you should set the + /// policy value for the scheduler policy key <c>SchedulerKind</c> to be <c>UmsThreadDefault</c>. If the policy specifies UMS thread, the + /// <c>IScheduler</c> interface that is passed as a parameter to the <see cref="IResourceManager::RegisterScheduler Method">IResourceManager::RegisterScheduler + /// </see> method must be an <c>IUMSScheduler</c> interface. + /// <para>The Resource Manager is able to hand you UMS threads only on operating systems that have the UMS feature. 64-bit operating systems with + /// version Windows 7 and higher support UMS threads. If you create a scheduler policy with the <c>SchedulerKind</c> key set to the value + /// <c>UmsThreadDefault</c> and the underlying platform does not support UMS, the value of the <c>SchedulerKind</c> key on that policy will + /// be changed to the value <c>ThreadScheduler</c>. You should always read back this policy value before expecting to receive UMS threads.</para> + /// <para> The <c>IUMSScheduler</c> interface is one end of a two-way channel of communication between a scheduler and the Resource Manager. + /// The other end is represented by the <c>IResourceManager</c> and <c>ISchedulerProxy</c> interfaces, which are implemented by the Resource Manager.</para> + /// </remarks> + /// <seealso cref="PolicyElementKey Enumeration"/> + /// <seealso cref="IScheduler Structure"/> + /// <seealso cref="IUMSCompletionList Structure"/> + /// <seealso cref="IResourceManager Structure"/> + /**/ + struct IUMSScheduler : public IScheduler + { + /// <summary> + /// Assigns an <c>IUMSCompletionList</c> interface to a UMS thread scheduler. + /// </summary> + /// <param name="pCompletionList"> + /// The completion list interface for the scheduler. There is a single list per scheduler. + /// </param> + /// <remarks> + /// The Resource Manager will invoke this method on a scheduler that specifies it wants UMS threads, after the scheduler has requested an initial + /// allocation of resources. The scheduler can use the <c>IUMSCompletionList</c> interface to determine when UMS thread proxies have unblocked. + /// It is only valid to access this interface from a thread proxy running on a virtual processor root assigned to the UMS scheduler. + /// </remarks> + /// <seealso cref="IScheduler Structure"/> + /// <seealso cref="IUMSCompletionList Structure"/> + /**/ + virtual void SetCompletionList(_Inout_ IUMSCompletionList * pCompletionList) =0; + }; + + /// <summary> + /// The interface by which schedulers communicate with the Concurrency Runtime's Resource Manager to negotiate resource allocation. + /// </summary> + /// <remarks> + /// The Resource Manager hands an <c>ISchedulerProxy</c> interface to every scheduler that registers with it using the + /// <see cref="IResourceManager::RegisterScheduler Method">IResourceManager::RegisterScheduler</see> method. + /// </remarks> + /// <seealso cref="IScheduler Structure"/> + /// <seealso cref="IThreadProxy Structure"/> + /// <seealso cref="IVirtualProcessorRoot Structure"/> + /// <seealso cref="IResourceManager Structure"/> + /**/ + struct ISchedulerProxy + { + /// <summary> + /// Requests an initial allocation of virtual processor roots. Every virtual processor root represents the ability to execute one thread + /// that can perform work for the scheduler. + /// </summary> + /// <param name="doSubscribeCurrentThread"> + /// Whether to subscribe the current thread and account for it during resource allocation. + /// </param> + /// <returns> + /// The <c>IExecutionResource</c> interface for the current thread, if the parameter <paramref name="doSubscribeCurrentThread"/> has + /// the value <c>true</c>. If the value is <c>false</c>, the method returns <c>NULL</c>. + /// </returns> + /// <remarks> + /// Before a scheduler executes any work, it should use this method to request virtual processor roots from the Resource Manager. The Resource + /// Manager will access the scheduler's policy using <see cref="IScheduler::GetPolicy Method">IScheduler::GetPolicy</see> and use the + /// values for the policy keys <c>MinConcurrency</c>, <c>MaxConcurrency</c> and <c>TargetOversubscriptionFactor</c> to determine how many + /// hardware threads to assign to the scheduler initially and how many virtual processor roots to create for every hardware thread. + /// For more information on how scheduler policies are used to determine a scheduler's initial allocation, see <see cref="PolicyElementKey Enumeration"> + /// PolicyElementKey</see>. + /// <para>The Resource Manager grants resources to a scheduler by calling the method <see cref="IScheduler::AddVirtualProcessors Method"> + /// IScheduler::AddVirtualProcessors</see> with a list of virtual processor roots. The method is invoked as a callback into the scheduler + /// before this method returns.</para> + /// <para> If the scheduler requested subscription for the current thread by setting the parameter <paramref name="doSubscribeCurrentThread"/> + /// to <c>true</c>, the method returns an <c>IExecutionResource</c> interface. The subscription must be terminated at a later point by using + /// the <see cref="IExecutionResource::Remove Method">IExecutionResource::Remove</see> method.</para> + /// <para>When determining which hardware threads are selected, the Resource Manager will attempt to optimize for processor node affinity. + /// If subscription is requested for the current thread, it is an indication that the current thread intends to participate in the work assigned + /// to this scheduler. In such a case, the allocated virtual processors roots are located on the processor node the current thread is executing on, + /// if possible.</para> + /// <para>The act of subscribing a thread increases the subscription level of the underlying hardware thread by one. The subscription level is + /// reduced by one when the subscription is terminated. For more information on subscription levels, see + /// <see cref="IExecutionResource::CurrentSubscriptionLevel Method">IExecutionResource::CurrentSubscriptionLevel</see>.</para> + /// </remarks> + /**/ + virtual IExecutionResource * RequestInitialVirtualProcessors(bool doSubscribeCurrentThread) =0; + + /// <summary> + /// Notifies the Resource Manager that the scheduler is shutting down. This will cause the Resource Manager to immediately reclaim + /// all resources granted to the scheduler. + /// </summary> + /// <remarks> + /// All <c>IExecutionContext</c> interfaces the scheduler received as a result of subscribing an external thread using the methods + /// <c>ISchedulerProxy::RequestInitialVirtualProcessors</c> or <c>ISchedulerProxy::SubscribeCurrentThread</c> must be returned to the Resource + /// Manager using <c>IExecutionResource::Remove</c> before a scheduler shuts itself down. + /// <para>If your scheduler had any deactivated virtual processor roots, you must activate them using <see cref="IVirtualProcessorRoot::Activate Method"> + /// IVirtualProcessorRoot::Activate</see>, and have the thread proxies executing on them leave the <c>Dispatch</c> method of the execution contexts + /// they are dispatching before you invoke <c>Shutdown</c> on a scheduler proxy.</para> + /// <para>It is not necessary for the scheduler to individually return all of the virtual processor roots the Resource Manager granted to it via + /// calls to the <c>Remove</c> method because all virtual processors roots will be returned to the Resource Manager at shutdown.</para> + /// </remarks> + /// <seealso cref="ISchedulerProxy::RequestInitialVirtualProcessors Method"/> + /// <seealso cref="ISchedulerProxy::SubscribeCurrentThread Method"/> + /// <seealso cref="IExecutionResource::Remove Method"/> + /**/ + virtual void Shutdown() =0; + + /// <summary> + /// Associates an execution context with a thread proxy, if it is not already associated with one. + /// </summary> + /// <param name="pContext"> + /// An interface to the execution context to associate with a thread proxy. + /// </param> + /// <remarks> + /// Normally, the <see cref="IThreadProxy::SwitchTo Method">IThreadProxy::SwitchTo</see> method will bind a thread proxy to an + /// execution context on demand. There are, however, circumstances where it is necessary to bind a context in advance + /// to ensure that the <c>SwitchTo</c> method switches to an already bound context. This is the case on a UMS scheduling context as it + /// cannot call methods that allocate memory, and binding a thread proxy may involve memory allocation if a thread proxy is not readily + /// available in the free pool of the thread proxy factory. + /// <para><c>invalid_argument</c> is thrown if the parameter <paramref name="pContext"/> has the value <c>NULL</c>.</para> + /// </remarks> + /// <seealso cref="ISchedulerProxy::UnbindContext Method"/> + /**/ + virtual void BindContext(_Inout_ IExecutionContext * pContext) =0; + + /// <summary> + /// Disassociates a thread proxy from the execution context specified by the <paramref name="pContext"/> parameter and returns it + /// to the thread proxy factory's free pool. This method may only be called on an execution context which was bound via the + /// <see cref="ISchedulerProxy::BindContext Method">ISchedulerProxy::BindContext</see> method and has not yet been started via being + /// the <c>pContext</c> parameter of an <see cref="IThreadProxy::SwitchTo Method">IThreadProxy::SwitchTo</see> method call. + /// </summary> + /// <param name="pContext"> + /// The execution context to disassociate from its thread proxy. + /// </param> + /**/ + virtual void UnbindContext(_Inout_ IExecutionContext * pContext) =0; + + /// <summary> + /// Registers the current thread with the Resource Manager, associating it with this scheduler. + /// </summary> + /// <returns> + /// The <c>IExecutionResource</c> interfacing representing the current thread in the runtime. + /// </returns> + /// <remarks> + /// Use this method if you want the Resource Manager to account for the current thread while allocating resources to your scheduler and other + /// schedulers. It is especially useful when the thread plans to participate in the work queued to your scheduler, along with the virtual + /// processor roots the scheduler receives from the Resource Manager. The Resource Manager uses information to prevent unnecessary oversubscription + /// of hardware threads on the system. + /// <para>The execution resource received via this method should be returned to the Resource Manager using the + /// <see cref="IExecutionResource::Remove Method">IExecutionResource::Remove</see> method. The thread that calls the <c>Remove</c> method must be + /// the same thread that previously called the <c>SubscribeCurrentThread</c> method.</para> + /// <para>The act of subscribing a thread increases the subscription level of the underlying hardware thread by one. The subscription level is + /// reduced by one when the subscription is terminated. For more information on subscription levels, see + /// <see cref="IExecutionResource::CurrentSubscriptionLevel Method">IExecutionResource::CurrentSubscriptionLevel</see>.</para> + /// </remarks> + /**/ + virtual IExecutionResource * SubscribeCurrentThread() =0; + + /// <summary> + /// Creates a new virtual processor root on the hardware thread associated with an existing execution resource. + /// </summary> + /// <param name="pExecutionResource"> + /// An <c>IExecutionResource</c> interface that represents the hardware thread you want to oversubscribe. + /// </param> + /// <returns> + /// An <c>IVirtualProcessorRoot</c> interface. + /// </returns> + /// <remarks> + /// Use this method when your scheduler wants to oversubscribe a particular hardware thread for a limited amount of time. Once you are + /// done with the virtual processor root, you should return it to the resource manager by calling the + /// <see cref="IExecutionResource::Remove Method">Remove</see> method on the <c>IVirtualProcessorRoot</c> interface. + /// <para>You can even oversubscribe an existing virtual processor root, because the <c>IVirtualProcessorRoot</c> interface inherits from the + /// <c>IExecutionResource</c> interface.</para> + /// </remarks> + /**/ + virtual IVirtualProcessorRoot * CreateOversubscriber(_Inout_ IExecutionResource * pExecutionResource) =0; + }; + + /// <summary> + /// An interface to an execution resource as defined by the Resource Manager. + /// </summary> + /// <remarks> + /// This interface is typically utilized to walk the topology of the system as observed by the Resource Manager. + /// </remarks> + /**/ + struct ITopologyExecutionResource + { + /// <summary> + /// Returns an interface to the next execution resource in enumeration order. + /// </summary> + /// <returns> + /// An interface to the next execution resource in enumeration order. If there are no more nodes in enumeration order of the node to which + /// this execution resource belongs, this method will return the value <c>NULL</c>. + /// </returns> + /// <seealso cref="ITopologyNode::GetFirstExecutionResource Method"/> + /// <seealso cref="ITopologyNode Structure"/> + /**/ + virtual ITopologyExecutionResource *GetNext() const =0; + + /// <summary> + /// Returns the Resource Manager's unique identifier for this execution resource. + /// </summary> + /// <returns> + /// The Resource Manager's unique identifier for this execution resource. + /// </returns> + /**/ + virtual unsigned int GetId() const =0; + }; + + /// <summary> + /// An interface to a topology node as defined by the Resource Manager. A node contains one or more execution resources. + /// </summary> + /// <remarks> + /// This interface is typically utilized to walk the topology of the system as observed by the Resource Manager. + /// </remarks> + /**/ + struct ITopologyNode + { + /// <summary> + /// Returns an interface to the next topology node in enumeration order. + /// </summary> + /// <returns> + /// An interface to the next node in enumeration order. If there are no more nodes in enumeration order of the system topology, this method + /// will return the value <c>NULL</c>. + /// </returns> + /// <seealso cref="IResourceManager::GetFirstNode Method"/> + /// <seealso cref="ITopologyExecutionResource Structure"/> + /**/ + virtual ITopologyNode *GetNext() const =0; + + /// <summary> + /// Returns the Resource Manager's unique identifier for this node. + /// </summary> + /// <returns> + /// The Resource Manager's unique identifier for this node. + /// </returns> + /// <remarks> + /// The Concurrency Runtime represents hardware threads on the system in groups of processor nodes. Nodes are usually derived from + /// the hardware topology of the system. For example, all processors on a specific socket or a specific NUMA node may belong to the + /// same processor node. The Resource Manager assigns unique identifiers to these nodes starting with <c>0</c> up to and including + /// <c>nodeCount - 1</c>, where <c>nodeCount</c> represents the total number of processor nodes on the system. + /// <para>The count of nodes can be obtained from the function <see cref="GetProcessorNodeCount Function">GetProcessorNodeCount</see>.</para> + /// </remarks> + /**/ + virtual unsigned int GetId() const =0; + + /// <summary> + /// Returns the Windows assigned NUMA node number to which this Resource Maanger node belongs. + /// </summary> + /// <returns> + /// The Windows assigned NUMA node number to which this Resource Manager node belongs. + /// </returns> + /// <remarks> + /// A thread proxy running on a virtual processor root belonging to this node will have affinity to at least the NUMA node level for the NUMA + /// node returned by this method. + /// </remarks> + /**/ + virtual unsigned long GetNumaNode() const =0; + + /// <summary> + /// Returns the number of execution resources grouped together under this node. + /// </summary> + /// <returns> + /// The number of execution resources grouped together under this node. + /// </returns> + /// <seealso cref="ITopologyNode::GetFirstExecutionResource Method"/> + /**/ + virtual unsigned int GetExecutionResourceCount() const =0; + + /// <summary> + /// Returns the first execution resource grouped under this node in enumeration order. + /// </summary> + /// <returns> + /// The first execution resource grouped under this node in enumeration order. + /// </returns> + /// <seealso cref="ITopologyNode::GetExecutionResourceCount Method"/> + /**/ + virtual ITopologyExecutionResource *GetFirstExecutionResource() const =0; + + }; + + /// <summary> + /// Indicates support of the Resource Manager interface defined in Visual Studio 2010. + /// </summary> + /**/ + const unsigned int CONCRT_RM_VERSION_1 = 0x00010000; + + /// <summary> + /// An interface to the Concurrency Runtime's Resource Manager. This is the interface by which schedulers communicate with the + /// Resource Manager. + /// </summary> + /// <remarks> + /// Use the <see cref="CreateResourceManager Function">CreateResourceManager</see> function to obtain an interface to the singleton Resource Manager + /// instance. The method increments a reference count on the Resource Manager, and you should invoke the <see cref="IResourceManager::Release Method"> + /// IResourceManager::Release</see> method to release the reference when you are done with Resource Manager. Typically, each scheduler + /// you create will invoke this method during creation, and release the reference to the Resource Manager after it shuts down. + /// </remarks> + /// <seealso cref="ISchedulerProxy Structure"/> + /// <seealso cref="IScheduler Structure"/> + /**/ + struct IResourceManager + { + /// <summary> + /// Increments the reference count on the Resource Manager instance. + /// </summary> + /// <returns> + /// The resulting reference count. + /// </returns> + /// <seealso cref="IResourceManager::Release Method"/> + /**/ + virtual unsigned int Reference() =0; + + /// <summary> + /// Decrements the reference count on the Resource Manager instance. The Resource Manager is destroyed when its reference count goes to <c>0</c>. + /// </summary> + /// <returns> + /// The resulting reference count. + /// </returns> + /// <seealso cref="CreateResourceManager Function"/> + /// <seealso cref="IResourceManager::Reference Method"/> + /**/ + virtual unsigned int Release() =0; + + /// <summary> + /// Registers a scheduler with the Resource Manager. Once the scheduler is registered, it should communicate with the Resource Manager using the + /// <c>ISchedulerProxy</c> interface that is returned. + /// </summary> + /// <param name="pScheduler"> + /// An <c>IScheduler</c> interface to the scheduler to be registered. + /// </param> + /// <param name="version"> + /// The version of communication interface the scheduler is using to communicate with the Resource Manager. Using a version allows the Resource + /// Manager to evolve the communication interface while allowing schedulers to obtain access to older features. Schedulers that wish to use Resource + /// Manager features present in Visual Studio 2010 should use the version <c>CONCRT_RM_VERSION_1</c>. + /// </param> + /// <returns> + /// The <c>ISchedulerProxy</c> interface the Resource Manager has associated with your scheduler. Your scheduler should use this interface to + /// communicate with Resource Manager from this point on. + /// </returns> + /// <remarks> + /// Use this method to initiate communication with the Resource Manager. The method associates the <c>IScheduler</c> interface for your scheduler + /// with an <c>ISchedulerProxy</c> interface and hands it back to you. You can use the returned interface to request execution resources for use + /// by your scheduler, or to subscribe threads with the Resource Manager. The Resource Manager will use policy elements from the scheduler policy + /// returned by the <see cref="IScheduler::GetPolicy Method">IScheduler::GetPolicy</see> method to determine what type of threads the scheduler will + /// need to execute work. If your <c>SchedulerKind</c> policy key has the value <c>UmsThreadDefault</c> and the value is read back out of the + /// policy as the value <c>UmsThreadDefault</c>, the <c>IScheduler</c> interface passed to the method must be an <c>IUMSScheduler</c> interface. + /// <para>The method throws an <c>invalid_argument</c> exception if the parameter <paramref name="pScheduler"/> has the value <c>NULL</c> or if the + /// parameter <paramref name="version"/> is not a valid version for the communication interface.</para> + /// </remarks> + /// <seealso cref="IScheduler Structure"/> + /// <seealso cref="ISchedulerProxy Structure"/> + /// <seealso cref="SchedulerPolicy Class"/> + /// <seealso cref="PolicyElementKey Enumeration"/> + /**/ + virtual ISchedulerProxy *RegisterScheduler(_Inout_ IScheduler * pScheduler, unsigned int version) =0; + + /// <summary> + /// Returns the number of nodes available to the Resource Manager. + /// </summary> + /// <returns> + /// The number of nodes available to the Resource Manager. + /// </returns> + /**/ + virtual unsigned int GetAvailableNodeCount() const =0; + + /// <summary> + /// Returns the first node in enumeration order as defined by the Resource Manager. + /// </summary> + /// <returns> + /// The first node in enumeration order as defined by the Resource Manager. + /// </returns> + /// <seealso cref="ITopologyNode::GetExecutionResourceCount Method"/> + /**/ + virtual ITopologyNode* GetFirstNode() const =0; + + /// <summary> + /// Present only in debug builds of the runtime, this method is a test hook designed to facilitate testing of the Resource Manager on varying hardware + /// topologies, without requiring actual hardware matching the configuration. With retail builds of the runtime, this method will return without performing + /// any action. + /// </summary> + /// <param name="nodeCount"> + /// The number of processor nodes being simulated. + /// </param> + /// <param name="pCoreCount"> + /// An array that specifies the number of cores on each node. + /// </param> + /// <param name="pNodeDistance"> + /// A matrix specifying the node distance between any two nodes. This parameter can have the value <c>NULL</c>. + /// </param> + /// <param name="pProcessorGroups"> + /// An array that specifies the processor group each node belongs to. + /// </param> + /// <remarks> + /// <see cref="invalid_argument Class">invalid_argument</see> is thrown if the parameter <paramref name="nodeCount"/> has the value <c>0</c> was passed + /// in, or if the parameter <paramref name="pCoreCount"/> has the value <c>NULL</c>. + /// <para><see cref="invalid_operation Class">invalid_operation</see> is thrown if this method is called while other schedulers exist in the process.</para> + /// </remarks> + /**/ + virtual void CreateNodeTopology(unsigned int nodeCount, _In_reads_(nodeCount) unsigned int * pCoreCount, _In_reads_opt_(nodeCount) unsigned int ** pNodeDistance, _In_reads_(nodeCount) unsigned int * pProcessorGroups) =0; + + /// <summary> + /// An enumerated type that represents the operating system version. + /// </summary> + /**/ + static enum OSVersion + { + /// <summary> + /// An operating system prior to Windows XP. The Concurrency Runtime is not supported on operating + /// systems with a version earlier than Windows XP with Service Pack 3. + /// </summary> + /**/ + UnsupportedOS, + + /// <summary> + /// The Windows XP operating system. + /// </summary> + /**/ + XP, + + /// <summary> + /// The Windows 2003 Server operating system. + /// </summary> + /**/ + Win2k3, + + /// <summary> + /// The Windows Vista and Windows Server 2008 operating systems. + /// </summary> + /**/ + Vista, + + /// <summary> + /// The Windows 7 and Windows Server 2008 R2 operating systems. + /// </summary> + /**/ + Win7OrLater, + + /// <summary> + /// This value is preserved for legacy reasons. The Concurrency Runtime in Visual Studio 2012 does not support Windows user-mode schedulable + /// threads. + /// </summary> + /**/ + UmsThreadAwareOS, + + /// <summary> + /// Any operating system with version Windows 8 or higher. + /// </summary> + /**/ + Win8OrLater + }; + }; + + /// <summary> + /// Returns an interface that represents the singleton instance of the Concurrency Runtime's Resource Manager. The Resource Manager is responsible + /// for assigning resources to schedulers that want to cooperate with each other. + /// </summary> + /// <returns> + /// An <c>IResourceManager</c> interface. + /// </returns> + /// <remarks> + /// Multiple subsequent calls to this method will return the same instance of the Resource Manager. Each call to the method increments a reference + /// count on the Resource Manager, and must be matched with a call to the <see cref="IResourceManager::Release"> IResourceManager::Release</see> + /// method when your scheduler is done communicating with the Resource Manager. + /// <para><see cref="unsupported_os Class">unsupported_os</see> is thrown if the operating system is not supported by the Concurrency Runtime.</para> + /// </remarks> + /// <seealso cref="IResourceManager::OSVersion Enumeration"/> + /**/ + _CRTIMP IResourceManager* __cdecl CreateResourceManager(); + + /// <summary> + /// Returns the operating system version. + /// </summary> + /// <returns> + /// An enumerated value representing the operating system. + /// </returns> + /// <remarks> + /// <para><see cref="unsupported_os Class">unsupported_os</see> is thrown if the operating system is not supported by the Concurrency Runtime.</para> + /// </remarks> + /// <seealso cref="IResourceManager::OSVersion Enumeration"/> + /**/ + _CRTIMP IResourceManager::OSVersion __cdecl GetOSVersion(); + + /// <summary> + /// Returns a unique identifier that can be assigned to a scheduler that implements the <c>IScheduler</c> interface. + /// </summary> + /// <returns> + /// A unique identifier for a scheduler. + /// </returns> + /// <remarks> + /// Use this method to obtain an identifier for your scheduler before you pass an <c>IScheduler</c> interface as a parameter to any of the methods + /// offered by the Resource Manager. + /// </remarks> + /**/ + _CRTIMP unsigned int __cdecl GetSchedulerId(); + + /// <summary> + /// Returns a unique identifier that can be assigned to an execution context that implements the <c>IExecutionContext</c> interface. + /// </summary> + /// <returns> + /// A unique identifier for an execution context. + /// </returns> + /// <remarks> + /// Use this method to obtain an identifier for your execution context before you pass an <c>IExecutionContext</c> interface as a parameter to any + /// of the methods offered by the Resource Manager. + /// </remarks> + /**/ + _CRTIMP unsigned int __cdecl GetExecutionContextId(); + + /// <summary> + /// Returns the number of hardware threads on the underlying system. + /// </summary> + /// <returns> + /// The number of hardware threads. + /// </returns> + /// <remarks> + /// <para><see cref="unsupported_os Class">unsupported_os</see> is thrown if the operating system is not supported by the Concurrency Runtime.</para> + /// </remarks> + /// <seealso cref="IResourceManager::OSVersion Enumeration"/> + /**/ + _CRTIMP unsigned int __cdecl GetProcessorCount(); + + /// <summary> + /// Returns the number of NUMA nodes or processor packages on the underlying system. + /// </summary> + /// <returns> + /// The number of NUMA nodes or processor packages. + /// </returns> + /// <remarks> + /// If the system contains more NUMA nodes than processor packages, the number of NUMA nodes is returned, otherwise, the number of processor packages is returned. + /// <para><see cref="unsupported_os Class">unsupported_os</see> is thrown if the operating system is not supported by the Concurrency Runtime.</para> + /// </remarks> + /// <seealso cref="IResourceManager::OSVersion Enumeration"/> + /**/ + _CRTIMP unsigned int __cdecl GetProcessorNodeCount(); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ +} + +namespace concurrency = Concurrency; + +#pragma pack(pop) diff --git a/test_data/lots_of_files/concurrent_priority_queue.h b/test_data/lots_of_files/concurrent_priority_queue.h new file mode 100644 index 0000000..8aa82d3 --- /dev/null +++ b/test_data/lots_of_files/concurrent_priority_queue.h @@ -0,0 +1,824 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Microsoft would like to acknowledge that this concurrency data structure implementation +* is based on Intel's implementation in its Threading Building Blocks ("Intel Material"). +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* concurrent_priority_queue.h +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +/* + Intel Material Copyright 2005-2011 Intel Corporation. All Rights Reserved. +*/ + +#pragma once + +#include <crtdefs.h> +#include <memory> +#include <iterator> +#include <limits> +#include <algorithm> +#include <cstring> +#include <vector> +#include <crtdbg.h> +#include <concrt.h> + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) + #error ERROR: Concurrency Runtime is supported only on X64, X86 and ARM architectures. +#endif /* !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_M_CEE) + #error ERROR: Concurrency Runtime is not supported when compiling /clr. +#endif /* defined (_M_CEE) */ + +#pragma pack(push,_CRT_PACKING) +#pragma warning (push) +#pragma warning (disable: 4510 4512 4610) // disable warnings for compiler unable to generate constructor + +/// <summary> +/// The <c>concurrency</c> namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ + +namespace details +{ + /// <summary> + /// _Aggregated_operation base class + /// </summary> + template <typename _Derived> + class _Aggregated_operation + { + public: + volatile int _M_status; + _Derived * _M_pNext; + + _Aggregated_operation() : + _M_status(0), + _M_pNext(NULL) + { + } + }; + + /// <summary> + /// An aggregator for collecting operations coming from multiple sources and executing them serially on a single thread. + /// _Operation_type must be derived from _Aggregated_operation. The parameter _Handler_type is a functor that will be passed the + /// list of operations and is expected to handle each operation appropriately, setting the status of each operation to non-zero. + /// </summary> + template < typename _Operation_type, typename _Handler_type > + class _Aggregator + { + public: + _Aggregator() : _M_handler_busy(0) + { + _M_pPending_operations = NULL; + } + + ~_Aggregator() + { + } + + void _Initialize_handler(_Handler_type _Handler) + { + _M_handle_operations = _Handler; + } + + /// <summary> + /// Place operation in list and either handle list or wait for operation to complete. + /// </summary> + void _Execute(_Operation_type *_Op) + { + _Operation_type *_Res; + + // Insert the operation in the queue + do + { + _Op->_M_pNext = _Res = _M_pPending_operations; + } while (_InterlockedCompareExchangePointer(reinterpret_cast<void *volatile *>(&_M_pPending_operations), _Op, _Res) != _Res); + + if (_Res == NULL) + { + // This item was the first one in the list; this thread will handle the operations. Note that by the time the + // thread pops the list of operations to handle, there may be several operations queued up. + _Start_handle_operations(); + _CONCRT_ASSERT(_Op->_M_status != 0); + } + else + { + // This item was not the first one on the list; a different thread is going to handle this operation, wait for + // the result to be ready. + ::Concurrency::details::_SpinWaitBackoffNone _SpinWait; + while (_Op->_M_status == 0) + { + _SpinWait._SpinOnce(); + } + } + } + + private: + // An atomically updated list (also known as a mailbox) of pending operations + _Operation_type * volatile _M_pPending_operations; + + // Controls thread access to _M_handle_operations + volatile long _M_handler_busy; + + // The method that handles the operations + _Handler_type _M_handle_operations; + + // Trigger the handling of operations when the handler is free + void _Start_handle_operations() + { + _Operation_type * _POp_list; + + // Acquire the _M_handler_busy flag. Only one thread can possibly spin here at a time + ::Concurrency::details::_SpinWaitBackoffNone _SpinWait; + while (_M_handler_busy == 1) + { + _SpinWait._SpinOnce(); + } + + long _OldVal = _InterlockedExchange(&_M_handler_busy, 1); + _CONCRT_ASSERT(_OldVal == 0); + + // Get the list of pending operations + _POp_list = reinterpret_cast<_Operation_type *>(_InterlockedExchangePointer(reinterpret_cast<void * volatile *>(&_M_pPending_operations), NULL)); + + // Handle all the operations + _M_handle_operations(_POp_list); + + // Release the handler + _OldVal = _InterlockedExchange(&_M_handler_busy, 0); + _CONCRT_ASSERT(_OldVal == 1); + } + }; + +} // namespace details + +/// <summary> +/// The <c>concurrent_priority_queue</c> class is a container that allows multiple threads to concurrently push and pop items. +/// Items are popped in priority order where priority is determined by a functor supplied as a template argument. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements to be stored in the priority queue. +/// </typeparam> +/// <typeparam name="_Compare"> +/// The type of the function object that can compare two element values as sort keys to determine their relative order +/// in the priority queue. This argument is optional and the binary predicate <c>less<</c><typeparamref name="_Ty"/><c>></c> +/// is the default value. +/// </typeparam> +/// <typeparam name="_Ax"> +/// The type that represents the stored allocator object that encapsulates details about the allocation and +/// deallocation of memory for the concurrent priority queue. This argument is optional and the default value is +/// <c>allocator<</c><typeparamref name="_Ty"/><c>></c>. +/// </typeparam> +/// <remarks> +/// For detailed information on the <c>concurrent_priority_queue</c> class, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template <typename _Ty, typename _Compare=std::less<_Ty>, typename _Ax = std::allocator<_Ty> > +class concurrent_priority_queue +{ +public: + + /// <summary> + /// A type that represents the data type stored in a concurrent priority queue. + /// </summary> + /**/ + typedef _Ty value_type; + + /// <summary> + /// A type that represents a reference to an element of the type stored in a concurrent priority queue. + /// </summary> + /**/ + typedef _Ty& reference; + + /// <summary> + /// A type that represents a const reference to an element of the type stored in a concurrent priority queue. + /// </summary> + /**/ + typedef const _Ty& const_reference; + + /// <summary> + /// A type that counts the number of elements in a concurrent priority queue. + /// </summary> + /**/ + typedef size_t size_type; + + /// <summary> + /// A type that represents the allocator class for the concurrent priority queue. + /// </summary> + /**/ + typedef _Ax allocator_type; + + /// <summary> + /// Constructs a concurrent priority queue. + /// </summary> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the priority queue. + /// <para>The first constructor specifies an empty initial priority queue and optionally specifies an allocator.</para> + /// <para>The second constructor specifies a priority queue with an initial capacity <paramref name="_Init_capacity"/> and optionally specifies + /// an allocator.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>) and + /// optionally specifies an allocator.</para> + /// <para>The fourth and fifth constructors specify a copy of the priority queue <paramref name="_Src"/>.</para> + /// <para>The sixth and seventh constructors specify a move of the priority queue <paramref name="_Src"/>.</para> + /// </remarks> + /**/ + explicit concurrent_priority_queue(const allocator_type& _Al = allocator_type()) : _M_mark(0), _M_size(0), _M_data(_Al) + { + _M_my_aggregator._Initialize_handler(_My_functor_type(this)); + } + + /// <summary> + /// Constructs a concurrent priority queue. + /// </summary> + /// <param name="_Init_capacity"> + /// The initial capacity of the <c>concurrent_priority_queue</c> object. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the priority queue. + /// <para>The first constructor specifies an empty initial priority queue and optionally specifies an allocator.</para> + /// <para>The second constructor specifies a priority queue with an initial capacity <paramref name="_Init_capacity"/> and optionally specifies + /// an allocator.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>) and + /// optionally specifies an allocator.</para> + /// <para>The fourth and fifth constructors specify a copy of the priority queue <paramref name="_Src"/>.</para> + /// <para>The sixth and seventh constructors specify a move of the priority queue <paramref name="_Src"/>.</para> + /// </remarks> + /**/ + explicit concurrent_priority_queue(size_type _Init_capacity, const allocator_type& _Al = allocator_type()) : _M_mark(0), _M_size(0), _M_data(_Al) + { + _M_data.reserve(_Init_capacity); + _M_my_aggregator._Initialize_handler(_My_functor_type(this)); + } + + /// <summary> + /// Constructs a concurrent priority queue. + /// </summary> + /// <typeparam name="_InputIterator"> + /// The type of the input iterator. + /// </typeparam> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be copied. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be copied. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the priority queue. + /// <para>The first constructor specifies an empty initial priority queue and optionally specifies an allocator.</para> + /// <para>The second constructor specifies a priority queue with an initial capacity <paramref name="_Init_capacity"/> and optionally specifies + /// an allocator.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>) and + /// optionally specifies an allocator.</para> + /// <para>The fourth and fifth constructors specify a copy of the priority queue <paramref name="_Src"/>.</para> + /// <para>The sixth and seventh constructors specify a move of the priority queue <paramref name="_Src"/>.</para> + /// </remarks> + /**/ + template<typename _InputIterator> + concurrent_priority_queue(_InputIterator _Begin, _InputIterator _End, const allocator_type& _Al = allocator_type()) : _M_data(_Begin, _End, _Al) + { + // Set the mark to 0 to indicate that nothing is sorted. + _M_mark = 0; + _M_size = _M_data.size(); + _M_my_aggregator._Initialize_handler(_My_functor_type(this)); + _Heapify(); + } + + /// <summary> + /// Constructs a concurrent priority queue. + /// </summary> + /// <param name="_Src"> + /// The source <c>concurrent_priority_queue</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the priority queue. + /// <para>The first constructor specifies an empty initial priority queue and optionally specifies an allocator.</para> + /// <para>The second constructor specifies a priority queue with an initial capacity <paramref name="_Init_capacity"/> and optionally specifies + /// an allocator.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>) and + /// optionally specifies an allocator.</para> + /// <para>The fourth and fifth constructors specify a copy of the priority queue <paramref name="_Src"/>.</para> + /// <para>The sixth and seventh constructors specify a move of the priority queue <paramref name="_Src"/>.</para> + /// </remarks> + /**/ + concurrent_priority_queue(const concurrent_priority_queue& _Src) : _M_mark(_Src._M_mark), _M_size(_Src._M_size), _M_data(_Src._M_data) + { + _M_my_aggregator._Initialize_handler(_My_functor_type(this)); + _Heapify(); + } + + /// <summary> + /// Constructs a concurrent priority queue. + /// </summary> + /// <param name="_Src"> + /// The source <c>concurrent_priority_queue</c> object to copy or move elements from. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the priority queue. + /// <para>The first constructor specifies an empty initial priority queue and optionally specifies an allocator.</para> + /// <para>The second constructor specifies a priority queue with an initial capacity <paramref name="_Init_capacity"/> and optionally specifies + /// an allocator.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>) and + /// optionally specifies an allocator.</para> + /// <para>The fourth and fifth constructors specify a copy of the priority queue <paramref name="_Src"/>.</para> + /// <para>The sixth and seventh constructors specify a move of the priority queue <paramref name="_Src"/>.</para> + /// </remarks> + /**/ + concurrent_priority_queue(const concurrent_priority_queue& _Src, const allocator_type& _Al) : _M_mark(_Src._M_mark), _M_data(_Src._M_data.begin(), _Src._M_data.end(), _Al) + { + _M_size = _M_data.size(); + _M_my_aggregator._Initialize_handler(_My_functor_type(this)); + _Heapify(); + } + + /// <summary> + /// Constructs a concurrent priority queue. + /// </summary> + /// <param name="_Src"> + /// The source <c>concurrent_priority_queue</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the priority queue. + /// <para>The first constructor specifies an empty initial priority queue and optionally specifies an allocator.</para> + /// <para>The second constructor specifies a priority queue with an initial capacity <paramref name="_Init_capacity"/> and optionally specifies + /// an allocator.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>) and + /// optionally specifies an allocator.</para> + /// <para>The fourth and fifth constructors specify a copy of the priority queue <paramref name="_Src"/>.</para> + /// <para>The sixth and seventh constructors specify a move of the priority queue <paramref name="_Src"/>.</para> + /// </remarks> + /**/ + concurrent_priority_queue(concurrent_priority_queue&& _Src) : _M_mark(_Src._M_mark), _M_size(_Src._M_size), _M_data(std::move(_Src._M_data)) + { + // Set _M_mark and _M_size for the argument to 0 because its data has been moved over to this priority queue. + _Src._M_mark = 0; + _Src._M_size = 0; + _M_my_aggregator._Initialize_handler(_My_functor_type(this)); + _Heapify(); + } + + /// <summary> + /// Constructs a concurrent priority queue. + /// </summary> + /// <param name="_Src"> + /// The source <c>concurrent_priority_queue</c> object to copy or move elements from. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the priority queue. + /// <para>The first constructor specifies an empty initial priority queue and optionally specifies an allocator.</para> + /// <para>The second constructor specifies a priority queue with an initial capacity <paramref name="_Init_capacity"/> and optionally specifies + /// an allocator.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>) and + /// optionally specifies an allocator.</para> + /// <para>The fourth and fifth constructors specify a copy of the priority queue <paramref name="_Src"/>.</para> + /// <para>The sixth and seventh constructors specify a move of the priority queue <paramref name="_Src"/>.</para> + /// </remarks> + /**/ + concurrent_priority_queue(concurrent_priority_queue&& _Src, const allocator_type& _Al) : _M_mark(_Src._M_mark), _M_size(_Src._M_size), _M_data(std::move(_Src._M_data), _Al) + { + // Set _M_mark and _M_size for the argument to 0 because its data has been moved over to this priority queue. + _Src._M_mark = 0; + _Src._M_size = 0; + _M_my_aggregator._Initialize_handler(_My_functor_type(this)); + _Heapify(); + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_priority_queue</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Src"> + /// The source <c>concurrent_priority_queue</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_priority_queue</c> object. + /// </returns> + /**/ + concurrent_priority_queue& operator=(const concurrent_priority_queue& _Src) + { + if (this != &_Src) + { + std::vector<value_type, allocator_type>(_Src._M_data.begin(), _Src._M_data.end(), _Src._M_data.get_allocator()).swap(_M_data); + _M_mark = _Src._M_mark; + _M_size = _Src._M_size; + } + return *this; + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_priority_queue</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Src"> + /// The source <c>concurrent_priority_queue</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_priority_queue</c> object. + /// </returns> + /**/ + concurrent_priority_queue& operator=(concurrent_priority_queue&& _Src) + { + if (this != &_Src) + { + _M_data = std::move(_Src._M_data); + _M_mark = _Src._M_mark; + _M_size = _Src._M_size; + // Set _M_mark and _M_size for the arguement to 0 because its data has been moved over to this priority queue. + _Src._M_mark = 0; + _Src._M_size = 0; + } + return *this; + } + + /// <summary> + /// Tests if the concurrent priority queue is empty at the time this method is called. This method is concurrency-safe. + /// </summary> + /// <returns> + /// <c>true</c> if the priority queue was empty at the moment the function was called, <c>false</c> otherwise. + /// </returns> + /**/ + bool empty() const + { + return (_M_size == 0); + } + + /// <summary> + /// Returns the number of elements in the concurrent priority queue. This method is concurrency-safe. + /// </summary> + /// <returns> + /// The number of elements in this <c>concurrent_priority_queue</c> object. + /// </returns> + /// <remarks> + /// The returned size is guaranteed to include all elements added by calls to the function <c>push</c>. + /// However, it may not reflect results of pending concurrent operations. + /// </remarks> + /**/ + size_type size() const + { + return _M_size; + } + + /// <summary> + /// Adds an element to the concurrent priority queue. This method is concurrency-safe. + /// </summary> + /// <param name="_Elem"> + /// The element to be added to the concurrent priority queue. + /// </param> + void push(const value_type& _Elem) + { + _Cpq_operation _Op_data(_Elem, _PUSH_OP_COPY); + _M_my_aggregator._Execute(&_Op_data); + if (_Op_data._M_status == _FAILED) + { + // Rethrow the saved exception. + std::rethrow_exception(_Op_data._M_exception_ptr); + } + } + + /// <summary> + /// Adds an element to the concurrent priority queue. This method is concurrency-safe. + /// </summary> + /// <param name="_Elem"> + /// The element to be added to the concurrent priority queue. + /// </param> + void push(value_type&& _Elem) + { + _Cpq_operation _Op_data(_Elem, _PUSH_OP_MOVE); + _M_my_aggregator._Execute(&_Op_data); + if (_Op_data._M_status == _FAILED) + { + // Rethrow the saved exception + std::rethrow_exception(_Op_data._M_exception_ptr); + } + } + + /// <summary> + /// Removes and returns the highest priority element from the queue if the queue is non-empty. This method is concurrency-safe. + /// </summary> + /// <param name="_Elem"> + /// A reference to a variable that will be populated with the highest priority element, if the queue is non-empty. + /// </param> + /// <returns> + /// <c>true</c> if a value was popped, <c>false</c> otherwise. + /// </returns> + /**/ + bool try_pop(reference _Elem) + { + _Cpq_operation _Op_data(_POP_OP); + _Op_data._M_elem = &_Elem; + _M_my_aggregator._Execute(&_Op_data); + return (_Op_data._M_status == _SUCCEEDED); + } + + /// <summary> + /// Erases all elements in the concurrent priority. This method is not concurrency-safe. + /// </summary> + /// <remarks> + /// <c>clear</c> is not concurrency-safe. You must ensure that no other threads are invoking methods + /// on the concurrent priority queue when you call this method. <c>clear</c> does not free memory. + /// </remarks> + /**/ + void clear() + { + _M_data.clear(); + _M_mark = 0; + _M_size = 0; + } + + /// <summary> + /// Swaps the contents of two concurrent priority queues. This method is not concurrency-safe. + /// </summary> + /// <param name="_Queue"> + /// The <c>concurrent_priority_queue</c> object to swap contents with. + /// </param> + /**/ + void swap(concurrent_priority_queue& _Queue) + { + _M_data.swap(_Queue._M_data); + std::swap(_M_mark, _Queue._M_mark); + std::swap(_M_size, _Queue._M_size); + } + + /// <summary> + /// Returns a copy of the allocator used to construct the concurrent priority queue. This method is concurrency-safe. + /// </summary> + /// <returns> + /// A copy of the allocator used to construct the <c>concurrent_priority_queue</c> object. + /// </returns> + /**/ + allocator_type get_allocator() const { return _M_data.get_allocator(); } + + private: + enum _Operation_type {_INVALID_OP, _PUSH_OP_COPY, _PUSH_OP_MOVE, _POP_OP}; + enum _Operation_status { _WAIT=0, _SUCCEEDED, _FAILED }; + + class _Cpq_operation : public ::Concurrency::details::_Aggregated_operation<_Cpq_operation> + { + public: + _Operation_type _M_type; + union + { + value_type * _M_elem; + size_type _M_size; + }; + std::exception_ptr _M_exception_ptr; + + _Cpq_operation(const_reference _E, _Operation_type _T) : + _M_type(_T), _M_elem(const_cast<value_type*>(&_E)) {} + _Cpq_operation(value_type&& _E, _Operation_type _T) : + _M_type(_T), _M_elem(const_cast<value_type*>(&_E)) {} + _Cpq_operation(_Operation_type _T) : _M_type(_T) {} + }; + + class _My_functor_type + { + concurrent_priority_queue<_Ty, _Compare, _Ax> *_M_PCpq; + public: + _My_functor_type() {} + _My_functor_type(concurrent_priority_queue<_Ty, _Compare, _Ax> * _PCpq) : _M_PCpq(_PCpq) {} + void operator()(_Cpq_operation* _POp_list) + { + _M_PCpq->_M_handle_operations(_POp_list); + } + }; + + ::Concurrency::details::_Aggregator< _Cpq_operation, _My_functor_type > _M_my_aggregator; + + // Padding added to avoid false sharing + char _M_padding1[64 /* CACHE_LINE_SIZE */ - sizeof(::Concurrency::details::_Aggregator< _Cpq_operation, _My_functor_type >)]; + + // The point at which unsorted elements begin + size_type _M_mark; + + // Size of the concurrent priority queue. This is cached instead of using vector::size(), because that method is unsafe in the presence + // of concurrent pushes/pops. + volatile size_type _M_size; + + // The comparator function to determine relative priority between elements stored in the priority queue. + _Compare _M_compare; + + // Padding added to avoid false sharing + char _M_padding2[64 /* CACHE_LINE_SIZE */ - sizeof(size_type) - sizeof(_Compare)]; + + // Storage for the heap of elements in queue, plus unheapified elements. _M_data has the following structure: + // + // binary unheapified + // heap elements + // ____|_______|____ + // | | | + // v v v + // [_|...|_|_|...|_| |...| ] + // 0 ^ ^ ^ + // | | |__capacity + // | |__size + // |__mark + // + // Thus, _M_data stores the binary heap starting at position 0 through _M_mark-1 (it may be empty). Then there are 0 or more elements + // that have not yet been inserted into the heap, in positions _M_mark through size-1. + std::vector<value_type, allocator_type> _M_data; + + /// <summary> + /// Handle a batch of operations pending on the concurrent priority queue. Only one thread can execute this routine at a time. + /// </summary> + void _M_handle_operations(_Cpq_operation *_POp_list) + { + _Cpq_operation *_PTmp, *_PPop_list=NULL; + + _CONCRT_ASSERT(_M_mark == _M_data.size()); + + // First pass processes all constant time operations: pushes, some pops. + while (_POp_list != NULL) + { + _CONCRT_ASSERT(_POp_list->_M_type != _INVALID_OP); + _PTmp = _POp_list; + _POp_list = _POp_list->_M_pNext; + if (_PTmp->_M_type == _PUSH_OP_COPY) + { + try + { + _M_data.push_back(*(_PTmp->_M_elem)); + ++_M_size; + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _SUCCEEDED); + } + catch (...) + { + _PTmp->_M_exception_ptr = std::current_exception(); + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _FAILED); + } + } + else if (_PTmp->_M_type == _PUSH_OP_MOVE) + { + try + { + _M_data.push_back(std::move(*(_PTmp->_M_elem))); + ++_M_size; + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _SUCCEEDED); + } + catch (...) + { + _PTmp->_M_exception_ptr = std::current_exception(); + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _FAILED); + } + } + else + { + _CONCRT_ASSERT(_PTmp->_M_type == _POP_OP); + if (_M_mark < _M_size && _M_compare(_M_data[0], _M_data[_M_size - 1])) + { + // There are newly pushed elems and the last one is higher than top + // Because we're going to pop the last element, we can move the _M_data instead of copying it. + *(_PTmp->_M_elem) = std::move(_M_data[_M_size - 1]); + --_M_size; + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _SUCCEEDED); + _M_data.pop_back(); + + _CONCRT_ASSERT(_M_mark <= _M_size); + } + else + { + // No convenient item to pop; postpone + _PTmp->_M_pNext = _PPop_list; + _PPop_list = _PTmp; + } + } + } + + // Second pass processes pop operations. + while (_PPop_list != NULL) + { + _PTmp = _PPop_list; + _PPop_list = _PPop_list->_M_pNext; + _CONCRT_ASSERT(_PTmp->_M_type == _POP_OP); + + if (_M_size == 0) + { + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _FAILED); + } + else + { + _CONCRT_ASSERT(_M_mark <= _M_size); + + if (_M_mark < _M_size && _M_compare(_M_data[0], _M_data[_M_size - 1])) + { + // There are newly pushed elems and the last one is higher than top. + // Because we're going to pop the last element, we can move the _M_data instead of copying it. + *(_PTmp->_M_elem) = std::move(_M_data[_M_size - 1]); // copy the _M_data + --_M_size; + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _SUCCEEDED); + _M_data.pop_back(); + } + else + { + // Extract top and push last element down heap. + *(_PTmp->_M_elem) = std::move(_M_data[0]); // copy the _M_data + --_M_size; + _InterlockedExchange((volatile long *) &_PTmp->_M_status, _SUCCEEDED); + _Reheap(); + } + } + } + _CONCRT_ASSERT(_M_size == _M_data.size()); + // _Heapify any leftover pushed elements before doing the next batch of operations. + if (_M_mark < _M_size) + { + _Heapify(); + } + _CONCRT_ASSERT(_M_mark == _M_size); + } + + /// <summary> + /// Merge unsorted elements into heap. + /// </summary> + void _Heapify() + { + if (_M_mark == 0 && _M_size > 0) + { + _M_mark = 1; + } + + for (; _M_mark < _M_size; ++_M_mark) + { + // for each unheapified element under size + size_type _Cur_pos = _M_mark; + value_type _To_place = std::move(_M_data[_M_mark]); + do + { // push _To_place up the heap + size_type _Parent = (_Cur_pos - 1) >> 1; + if (!_M_compare(_M_data[_Parent], _To_place)) + { + break; + } + _M_data[_Cur_pos] = std::move(_M_data[_Parent]); + _Cur_pos = _Parent; + + } while( _Cur_pos != 0 ); + + _M_data[_Cur_pos] = std::move(_To_place); + } + } + + /// <summary> + /// Re-_Heapify by pushing last element down the heap from the root. + /// </summary> + void _Reheap() + { + size_type _Cur_pos=0, _Child=1; + // Use _M_data.size() instead of _M_size throughout this function, because _M_size has already + // been decremented to account for the pop right before Reheap was invoked. + while (_Child < _M_mark) + { + size_type _Target = _Child; + if (_Child + 1 < _M_mark && _M_compare(_M_data[_Child], _M_data[_Child + 1])) + { + ++_Target; + } + // _Target now has the higher priority _Child. + if (_M_compare(_M_data[_Target], _M_data[_M_data.size() - 1])) + { + break; + } + + _M_data[_Cur_pos] = std::move(_M_data[_Target]); + _Cur_pos = _Target; + _Child = (_Cur_pos << 1) + 1; + } + + _M_data[_Cur_pos] = std::move(_M_data[_M_data.size() - 1]); + _M_data.pop_back(); + + if (_M_mark > _M_data.size()) + { + _M_mark = _M_data.size(); + } + } +}; + +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma warning (pop) +#pragma pack(pop) diff --git a/test_data/lots_of_files/concurrent_queue.h b/test_data/lots_of_files/concurrent_queue.h new file mode 100644 index 0000000..afbf855 --- /dev/null +++ b/test_data/lots_of_files/concurrent_queue.h @@ -0,0 +1,830 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Microsoft would like to acknowledge that this concurrency data structure implementation +* is based on Intel's implementation in its Threading Building Blocks ("Intel Material"). +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* concurrent_queue.h +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +/* + Intel Material Copyright 2005-2008 Intel Corporation. All Rights Reserved. +*/ + +#pragma once + + +#include <crtdefs.h> +#include <memory> +#include <cstddef> +#include <crtdbg.h> +#include <concrt.h> +#include <utility> + +#define _PPL_CONTAINER + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) + #error ERROR: Concurrency Runtime is supported only on X64, X86, and ARM architectures. +#endif /* !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_M_CEE) + #error ERROR: Concurrency Runtime is not supported when compiling /clr. +#endif /* defined (_M_CEE) */ + +#pragma pack(push,_CRT_PACKING) +#pragma warning(push) +#pragma warning (disable: 4510 4512 4610) // disable warnings for compiler unable to generate constructor + + +/// <summary> +/// The <c>Concurrency</c> namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ + +template<typename _Ty, class _Ax = std::allocator<_Ty> > +class concurrent_queue; + +namespace details +{ + + class _Concurrent_queue_rep; + + typedef size_t _Ticket; + + class _Concurrent_queue_iterator_rep; + class _Concurrent_queue_iterator_base_v4; + template<typename _Container, typename _Value> class _Concurrent_queue_iterator; + + // Type-independent portion of concurrent_queue. + class _Concurrent_queue_base_v4 + { + // Internal representation + _Concurrent_queue_rep* _My_rep; + + friend class _Concurrent_queue_rep; + friend struct _Micro_queue; + friend class _Micro_queue_pop_finalizer; + friend class _Concurrent_queue_iterator_rep; + friend class _Concurrent_queue_iterator_base_v4; + protected: + // Prefix on a page + struct _Page + { + _Page* _Next; + size_t _Mask; + }; + + // Always a power of 2 + size_t _Items_per_page; + + // Size of an item + size_t _Item_size; + + private: + virtual void _Move_item( _Page& _Dst, size_t _Index, void* _Src ) = 0; + virtual void _Copy_item( _Page& _Dst, size_t _Index, const void* _Src ) = 0; + virtual void _Assign_and_destroy_item( void* _Dst, _Page& _Src, size_t _Index ) = 0; + protected: + _CRTIMP2 _Concurrent_queue_base_v4( size_t _Item_size ); + _CRTIMP2 virtual ~_Concurrent_queue_base_v4(); + + // Enqueue item at tail of queue + _CRTIMP2 void _Internal_push( const void* _Src ); + + // Enqueue item at tail of queue by move + _CRTIMP2 void _Internal_move_push( void* _Src ); + + // swap the internal representation + _CRTIMP2 void _Concurrent_queue_base_v4::_Internal_swap( _Concurrent_queue_base_v4& other ); + + // Attempt to dequeue item from queue. + /** NULL if there was no item to dequeue. */ + _CRTIMP2 bool _Internal_pop_if_present( void* _Dst ); + + // Get size of queue + _CRTIMP2 size_t _Internal_size() const; + + // Test instantaneous queue empty + _CRTIMP2 bool _Internal_empty() const; + + // custom allocator + virtual _Page *_Allocate_page() = 0; + + // custom de-allocator + virtual void _Deallocate_page( _Page *p ) = 0; + + // free any remaining pages + _CRTIMP2 void _Internal_finish_clear() ; + + // throw an exception + _CRTIMP2 void _Internal_throw_exception() const; + + private: + // Deny copy construction + _Concurrent_queue_base_v4( const _Concurrent_queue_base_v4& ); + + // Deny assignment + void operator=( const _Concurrent_queue_base_v4& ); + }; + + typedef _Concurrent_queue_base_v4 _Concurrent_queue_base ; + + + // A queue using simple locking. + /** For efficiency, this class has no constructor. + The caller is expected to zero-initialize it. */ + struct _Micro_queue + { + class _Pop_finalizer; + class _Push_finalizer; + + _Subatomic<_Concurrent_queue_base::_Page*> _Head_page; + _Subatomic<_Ticket> _Head_counter; + + _Subatomic<_Concurrent_queue_base::_Page*> _Tail_page; + _Subatomic<_Ticket> _Tail_counter; + + volatile long _Page_mutex_flag; + + void _Push( void* _Item, _Ticket _K, _Concurrent_queue_base& _Base, void (_Concurrent_queue_base::*moveOp)(_Concurrent_queue_base_v4::_Page&, size_t, void*)); + + bool _Pop( void* _Dest, _Ticket _K, _Concurrent_queue_base& _Base ); + }; + + // Disable warning C4324: structure was padded due to __declspec(align()) + // This padding is expected and necessary. + #pragma warning(push) + #pragma warning(disable: 4324) + + + // Internal representation of a ConcurrentQueue. + /** For efficiency, this class has no constructor. + The caller is expected to zero-initialize it. */ + class _Concurrent_queue_rep + { + private: + friend struct _Micro_queue; + + // Approximately n_queue/golden ratio + static const size_t _Phi = 3; + + public: + // Must be power of 2 + static const size_t _N_queue = 8; + + // Map ticket to an array index + static size_t _Index( _Ticket _K ) + { + return _K*_Phi%_N_queue; + } + + __declspec(align(64)) + _Subatomic<_Ticket> _Head_counter; + + __declspec(align(64)) + _Subatomic<_Ticket> _Tail_counter; + + __declspec(align(64)) + _Micro_queue _Array[_N_queue]; + + _Micro_queue& _Choose( _Ticket _K ) + { + // The formula here approximates LRU in a cache-oblivious way. + return _Array[_Index(_K)]; + } + }; + + #pragma warning(pop) + + + // Type-independent portion of _Concurrent_queue_iterator. + class _Concurrent_queue_iterator_base_v4 { + // Concurrentconcurrent_queue over which we are iterating. + /** NULL if one past last element in queue. */ + _Concurrent_queue_iterator_rep* _My_rep; + + template<typename _C, typename _Ty, typename _U> + friend bool operator==( const _Concurrent_queue_iterator<_C,_Ty>&, const _Concurrent_queue_iterator<_C,_U>& ); + + template<typename _C, typename _Ty, typename _U> + friend bool operator!=( const _Concurrent_queue_iterator<_C,_Ty>&, const _Concurrent_queue_iterator<_C,_U>& ); + protected: + // Pointer to current item + mutable void* _My_item; + + // Default constructor + _Concurrent_queue_iterator_base_v4() + : _My_rep(NULL), _My_item(NULL) + { + } + + // Copy constructor + _Concurrent_queue_iterator_base_v4( const _Concurrent_queue_iterator_base_v4& _I ) + : _My_rep(NULL), _My_item(NULL) + { + _Assign(_I); + } + + // Construct iterator pointing to head of queue. + _CRTIMP2 _Concurrent_queue_iterator_base_v4( const _Concurrent_queue_base& ); + + // Assignment + _CRTIMP2 void _Assign( const _Concurrent_queue_iterator_base_v4& ); + + // Advance iterator one step towards tail of queue. + _CRTIMP2 void _Advance(); + + // Destructor + _CRTIMP2 ~_Concurrent_queue_iterator_base_v4(); + }; + + typedef _Concurrent_queue_iterator_base_v4 concurrent_queue_iterator_base; + + // Meets requirements of a forward iterator for STL. + /** _Value is either the _Ty or const _Ty type of the container. */ + template<typename _Container, typename _Value> + class _Concurrent_queue_iterator: public _Concurrent_queue_iterator_base_v4, public std::iterator<std::forward_iterator_tag, _Value> + { + template<typename _Ty, class _Ax> friend class ::Concurrency::concurrent_queue; + + // Construct iterator pointing to head of queue. + _Concurrent_queue_iterator( const _Concurrent_queue_base& _Queue ) + : _Concurrent_queue_iterator_base_v4(_Queue) + { + } + public: + _Concurrent_queue_iterator() + { + } + + /** If _Value==_Container::value_type, then this routine is the copy constructor. + If _Value==const _Container::value_type, then this routine is a conversion constructor. */ + _Concurrent_queue_iterator( const _Concurrent_queue_iterator<_Container,typename _Container::value_type>& _Other ) + : _Concurrent_queue_iterator_base_v4(_Other) + { + } + + // Iterator assignment + _Concurrent_queue_iterator& operator=( const _Concurrent_queue_iterator& _Other ) + { + _Assign(_Other); + return *this; + } + + // Reference to current item + _Value& operator*() const + { + return *static_cast<_Value*>(_My_item); + } + + _Value* operator->() const + { + return &operator*(); + } + + // Advance to next item in queue + _Concurrent_queue_iterator& operator++() + { + _Advance(); + return *this; + } + + // Post increment + _Concurrent_queue_iterator operator++(int) + { + _Concurrent_queue_iterator _Result = *this; + _Advance(); + return _Result; + } + }; // _Concurrent_queue_iterator + + template<typename _Container, typename _Value> + struct std::_Is_checked_helper<_Concurrent_queue_iterator<_Container, _Value> > + : public true_type + { // mark _Concurrent_queue_iterator as checked. This suppresses warning C4996 + }; + + template<typename _C, typename _Ty, typename _U> + bool operator==( const _Concurrent_queue_iterator<_C,_Ty>& _I, const _Concurrent_queue_iterator<_C,_U>& _J ) + { + return _I._My_item==_J._My_item; + } + + template<typename _C, typename _Ty, typename _U> + bool operator!=( const _Concurrent_queue_iterator<_C,_Ty>& _I, const _Concurrent_queue_iterator<_C,_U>& _J ) + { + return _I._My_item!=_J._My_item; + } + +} // namespace details; + + +/// <summary> +/// The <c>concurrent_queue</c> class is a sequence container class that allows first-in, +/// first-out access to its elements. It enables a limited set of concurrency-safe operations, such as +/// <c>push</c> and <c>try_pop</c>. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements to be stored in the queue. +/// </typeparam> +/// <typeparam name="_Ax"> +/// The type that represents the stored allocator object that encapsulates details about the allocation and +/// deallocation of memory for this concurrent queue. This argument is optional and the default value is +/// <c>allocator<</c><typeparamref name="_Ty"/><c>></c>. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/**/ +template<typename _Ty, class _Ax> +class concurrent_queue: public ::Concurrency::details::_Concurrent_queue_base_v4 +{ + template<typename _Container, typename _Value> friend class ::Concurrency::details::_Concurrent_queue_iterator; + + // allocator type + typedef typename _Ax::template rebind<char>::other _Page_allocator_type; + _Page_allocator_type _My_allocator; + + // Class used to ensure exception-safety of method "pop" + class _Destroyer + { + private: + _Ty& _My_value; + + void operator=(const _Destroyer&); // prevent warning: assign operator can't be generated + public: + _Destroyer( _Ty& _Value ) + : _My_value(_Value) + { + } + + ~_Destroyer() + { + _My_value.~_Ty(); + } + }; + + _Ty& _Get_ref( _Page& _Pg, size_t _Index ) + { + _CONCRT_ASSERT( _Index<_Items_per_page ); + return static_cast<_Ty*>(static_cast<void*>(&_Pg+1))[_Index]; + } + + /*override*/ virtual void _Copy_item( _Page& _Dst, size_t _Index, const void* _Src ) + { + new( &_Get_ref(_Dst,_Index) ) _Ty(*static_cast<const _Ty*>(_Src)); + } + + /*override*/ virtual void _Move_item( _Page& _Dst, size_t _Index, void* _Src ) + { + new( &_Get_ref(_Dst,_Index) ) _Ty(std::move(*static_cast<_Ty*>(_Src))); + } + + /*override*/ virtual void _Assign_and_destroy_item( void* _Dst, _Page& _Src, size_t _Index ) + { + _Ty& _From = _Get_ref(_Src,_Index); + _Destroyer _D(_From); + if (_Dst != NULL) + { + *static_cast<_Ty*>(_Dst) = std::move(_From); + } + } + + /*overide*/ virtual _Page *_Allocate_page() + { + size_t _N = sizeof(_Page) + _Items_per_page*_Item_size; + _Page *_Pg = reinterpret_cast<_Page*>(_My_allocator.allocate( _N )); + if( !_Pg ) + _Internal_throw_exception(); + return _Pg; + } + + /*override*/ virtual void _Deallocate_page( _Page *_Pg ) + { + size_t _N = sizeof(_Page) + _Items_per_page*_Item_size; + _My_allocator.deallocate( reinterpret_cast<char*>(_Pg), _N ); + } + +public: + /// <summary> + /// A type that represents the data type stored in a concurrent queue. + /// </summary> + /**/ + typedef _Ty value_type; + + /// <summary> + /// A type that represents the allocator class for the concurrent queue. + /// </summary> + /**/ + typedef _Ax allocator_type; + + /// <summary> + /// A type that provides a reference to an element stored in a concurrent queue. + /// </summary> + /**/ + typedef _Ty& reference; + + /// <summary> + /// A type that provides a reference to a <c>const</c> element stored in a concurrent queue for reading and + /// performing <c>const</c> operations. + /// </summary> + /**/ + typedef const _Ty& const_reference; + + /// <summary> + /// A type that counts the number of elements in a concurrent queue. + /// </summary> + /**/ + typedef std::size_t size_type; + + /// <summary> + /// A type that provides the signed distance between two elements in a concurrent queue. + /// </summary> + /**/ + typedef std::ptrdiff_t difference_type; + + /// <summary> + /// Constructs a concurrent queue. + /// </summary> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the queue. + /// <para>The first constructor specifies an empty initial queue and explicitly specifies the allocator + /// type to be used.</para> + /// <para>The second constructor specifies a copy of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The third constructor specifies a move of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The fourth constructor specifies values supplied by the iterator range + /// [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + explicit concurrent_queue(const allocator_type &_Al = allocator_type()) + : _Concurrent_queue_base_v4( sizeof(_Ty) ), _My_allocator( _Al ) + { + } + + /// <summary> + /// Constructs a concurrent queue. + /// </summary> + /// <param name="_OtherQ"> + /// The source <c>concurrent_queue</c> object to copy or move elements from. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the queue. + /// <para>The first constructor specifies an empty initial queue and explicitly specifies the allocator + /// type to be used.</para> + /// <para>The second constructor specifies a copy of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The third constructor specifies a move of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The fourth constructor specifies values supplied by the iterator range + /// [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + concurrent_queue(const concurrent_queue& _OtherQ, const allocator_type &_Al = allocator_type()); + + /// <summary> + /// Constructs a concurrent queue. + /// </summary> + /// <param name="_OtherQ"> + /// The source <c>concurrent_queue</c> object to copy or move elements from. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the queue. + /// <para>The first constructor specifies an empty initial queue and explicitly specifies the allocator + /// type to be used.</para> + /// <para>The second constructor specifies a copy of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The third constructor specifies a move of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The fourth constructor specifies values supplied by the iterator range + /// [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + concurrent_queue(concurrent_queue&& _OtherQ, const allocator_type &_Al = allocator_type()); + + /// <summary> + /// Constructs a concurrent queue. + /// </summary> + /// <typeparam name="_InputIterator"> + /// The type of the input iterator that specifies a range of values. + /// </typeparam> + /// <param name="_Begin"> + /// Position of the first element in the range of elements to be copied. + /// </param> + /// <param name="_End"> + /// Position of the first element beyond the range of elements to be copied. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the queue. + /// <para>The first constructor specifies an empty initial queue and explicitly specifies the allocator + /// type to be used.</para> + /// <para>The second constructor specifies a copy of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The third constructor specifies a move of the concurrent queue <paramref name="_OtherQ"/>.</para> + /// <para>The fourth constructor specifies values supplied by the iterator range + /// [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + template<typename _InputIterator> + concurrent_queue(_InputIterator _Begin, _InputIterator _End) + : _Concurrent_queue_base_v4( sizeof(_Ty) ), _My_allocator( allocator_type() ) + { + while (_Begin != _End) + { + this->push(*_Begin); + ++_Begin; + } + } + + /// <summary> + /// Destroys the concurrent queue. + /// </summary> + /**/ + ~concurrent_queue(); + + /// <summary> + /// Enqueues an item at tail end of the concurrent queue. This method is concurrency-safe. + /// </summary> + /// <param name="_Src"> + /// The item to be added to the queue. + /// </param> + /// <remarks> + /// <c>push</c> is concurrency-safe with respect to calls to the methods <c>push</c>, <c>try_pop</c>, and <c>empty</c>. + /// </remarks> + /**/ + void push( const _Ty& _Src ) + { + _Internal_push( &_Src ); + } + + /// <summary> + /// Enqueues an item at tail end of the concurrent queue. This method is concurrency-safe. + /// </summary> + /// <param name="_Src"> + /// The item to be added to the queue. + /// </param> + /// <remarks> + /// <c>push</c> is concurrency-safe with respect to calls to the methods <c>push</c>, <c>try_pop</c>, and <c>empty</c>. + /// </remarks> + /**/ + void push( _Ty&& _Src ) + { + _Internal_move_push( &_Src ); + } + + /// <summary> + /// Dequeues an item from the queue if one is available. This method is concurrency-safe. + /// </summary> + /// <param name="_Dest"> + /// A reference to a location to store the dequeued item. + /// </param> + /// <returns> + /// <c>true</c> if an item was successfully dequeued,<c>false</c> otherwise. + /// </returns> + /// <remarks> + /// If an item was successfully dequeued, the parameter <paramref name="_Dest"/> receives the + /// dequeued value, the original value held in the queue is destroyed, and this function returns + /// <c>true</c>. If there was no item to dequeue, this function returns <c>false</c> without blocking, + /// and the contents of the <paramref name="_Dest"/> parameter are undefined. + /// <para><c>try_pop</c> is concurrency-safe with respect to calls to the methods <c>push</c>, <c>try_pop</c>, + /// and <c>empty</c>.</para> + /// </remarks> + /**/ + bool try_pop( _Ty& _Dest ) + { + return _Internal_pop_if_present( &_Dest ); + } + + /// <summary> + /// Returns the number of items in the queue. This method is not concurrency-safe. + /// </summary> + /// <returns> + /// The size of the concurrent queue. + /// </returns> + /// <remarks> + /// <c>unsafe_size</c> is not concurrency-safe and can produce incorrect results if called concurrently + /// with calls to the methods <c>push</c>, <c>try_pop</c>, and <c>empty</c>. + /// </remarks> + /**/ + size_type unsafe_size() const + { + return _Internal_size(); + } + + /// <summary> + /// Tests if the concurrent queue is empty at the moment this method is called. This method is concurrency-safe. + /// </summary> + /// <returns> + /// <c>true</c> if the concurrent queue was empty at the moment we looked, <c>false</c> otherwise. + /// </returns> + /// <remarks> + /// While this method is concurrency-safe with respect to calls to the methods <c>push</c>, <c>try_pop</c>, and + /// <c>empty</c>, the value returned might be incorrect by the time it is inspected by the calling thread. + /// </remarks> + /**/ + bool empty() const + { + return _Internal_empty(); + } + + /// <summary> + /// Returns a copy of the allocator used to construct the concurrent queue. This method is concurrency-safe. + /// </summary> + /// <returns> + /// A copy of the allocator used to construct the concurrent queue. + /// </returns> + /**/ + allocator_type get_allocator() const + { + return this->_My_allocator; + } + + /// <summary> + /// Clears the concurrent queue, destroying any currently enqueued elements. This method is not concurrency-safe. + /// </summary> + /**/ + void clear(); + + /// <summary> + /// A type that represents a non-thread-safe iterator over the elements in a concurrent queue. + /// </summary> + /**/ + typedef details::_Concurrent_queue_iterator<concurrent_queue,_Ty> iterator; + + /// <summary> + /// A type that represents a non-thread-safe <c>const</c> iterator over elements in a concurrent queue. + /// </summary> + /**/ + typedef details::_Concurrent_queue_iterator<concurrent_queue,const _Ty> const_iterator; + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// beginning of the concurrent queue. This method is not concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// beginning of the concurrent queue object. + /// </returns> + /// <remarks> + /// The iterators for the <c>concurrent_queue</c> class are primarily intended for debugging, as they are slow, and iteration + /// is not concurrency-safe with respect to other queue operations. + /// </remarks> + /**/ + iterator unsafe_begin() + { + return iterator(*this); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// end of the concurrent queue. This method is not concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// end of the concurrent queue. + /// </returns> + /// <remarks> + /// The iterators for the <c>concurrent_queue</c> class are primarily intended for debugging, as they are slow, and iteration + /// is not concurrency-safe with respect to other queue operations. + /// </remarks> + /**/ + iterator unsafe_end() + { + return iterator(); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// beginning of the concurrent queue. This method is not concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// beginning of the concurrent queue. + /// </returns> + /// <remarks> + /// The iterators for the <c>concurrent_queue</c> class are primarily intended for debugging, as they are slow, and iteration + /// is not concurrency-safe with respect to other queue operations. + /// </remarks> + /**/ + const_iterator unsafe_begin() const + { + return const_iterator(*this); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// end of the concurrent queue. This method is not concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the + /// end of the concurrent queue. + /// </returns> + /// <remarks> + /// The iterators for the <c>concurrent_queue</c> class are primarily intended for debugging, as they are slow, and iteration + /// is not concurrency-safe with respect to other queue operations. + /// </remarks> + /**/ + const_iterator unsafe_end() const + { + return const_iterator(); + } +}; + + +/// <summary> +/// Constructs a concurrent queue. +/// </summary> +/// <param name="_OtherQ"> +/// The source <c>concurrent_queue</c> object to copy or move elements from. +/// </param> +/// <param name="_Al"> +/// The allocator class to use with this object. +/// </param> +/// <remarks> +/// All constructors store an allocator object <paramref name="_Al"/> and initialize the queue. +/// <para>The first constructor specifies an empty initial queue and explicitly specifies the allocator +/// type to be used.</para> +/// <para>The second constructor specifies a copy of the concurrent queue <paramref name="_OtherQ"/>.</para> +/// <para>The third constructor specifies a move of the concurrent queue <paramref name="_OtherQ"/>.</para> +/// <para>The fourth constructor specifies values supplied by the iterator range +/// [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> +/// </remarks> +/**/ +template<typename _Ty, class _Ax> +concurrent_queue<_Ty,_Ax>::concurrent_queue(const concurrent_queue& _Queue, const allocator_type& _Al = allocator_type()) + : _Concurrent_queue_base_v4( sizeof(_Ty) ), _My_allocator(_Al) +{ + concurrent_queue::const_iterator _QEnd = _Queue.unsafe_end(); + for (concurrent_queue::const_iterator _It = _Queue.unsafe_begin(); _It != _QEnd; ++_It) + this->push(*_It); +} + +/// <summary> +/// Constructs a concurrent queue. +/// </summary> +/// <param name="_OtherQ"> +/// The source <c>concurrent_queue</c> object to copy or move elements from. +/// </param> +/// <remarks> +/// All constructors store an allocator object <paramref name="_Al"/> and initialize the queue. +/// <para>The first constructor specifies an empty initial queue and explicitly specifies the allocator +/// type to be used.</para> +/// <para>The second constructor specifies a copy of the concurrent queue <paramref name="_OtherQ"/>.</para> +/// <para>The third constructor specifies a move of the concurrent queue <paramref name="_OtherQ"/>.</para> +/// <para>The fourth constructor specifies values supplied by the iterator range +/// [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> +/// </remarks> +/**/ +template<typename _Ty, class _Ax> +concurrent_queue<_Ty,_Ax>::concurrent_queue(concurrent_queue&& _Queue, const allocator_type& _Al = allocator_type()) + : _Concurrent_queue_base_v4( sizeof(_Ty) ), _My_allocator(_Al) +{ + _Internal_swap(_Queue); +} + +/// <summary> +/// Destroys the concurrent queue. +/// </summary> +/**/ +template<typename _Ty, class _Ax> +concurrent_queue<_Ty,_Ax>::~concurrent_queue() +{ + clear(); + _Internal_finish_clear(); +} + +/// <summary> +/// Clears the concurrent queue, destroying any currently enqueued elements. This method is not concurrency-safe. +/// </summary> +/**/ +template<typename _Ty, class _Ax> +void concurrent_queue<_Ty,_Ax>::clear() +{ + while( !empty() ) + { + if (!_Internal_pop_if_present(NULL)) + { + _CONCRT_ASSERT(empty()); + break; + } + } +} + +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma warning(pop) +#pragma pack(pop) diff --git a/test_data/lots_of_files/concurrent_unordered_map.h b/test_data/lots_of_files/concurrent_unordered_map.h new file mode 100644 index 0000000..0634110 --- /dev/null +++ b/test_data/lots_of_files/concurrent_unordered_map.h @@ -0,0 +1,1396 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* concurrent_unordered_map.h +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include <utility> +#include "internal_concurrent_hash.h" + +#define _PPL_CONTAINER + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) + #error ERROR: Concurrency Runtime is supported only on X64, X86, and ARM architectures. +#endif /* !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_M_CEE) + #error ERROR: Concurrency Runtime is not supported when compiling /clr. +#endif /* defined (_M_CEE) */ + +#pragma pack(push,_CRT_PACKING) +#pragma warning(push) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation + +namespace Concurrency +{ +namespace details +{ +// Template class for hash map traits +template<typename _Key_type, typename _Element_type, typename _Key_comparator, typename _Allocator_type, bool _Allow_multimapping> +class _Concurrent_unordered_map_traits : public std::_Container_base +{ +public: + typedef std::pair<const _Key_type, _Element_type> value_type; + typedef _Key_type key_type; + typedef _Key_comparator _Key_compare; + + typedef typename _Allocator_type::template rebind<value_type>::other allocator_type; + + enum + { + _M_allow_multimapping = _Allow_multimapping + }; + + _Concurrent_unordered_map_traits() : _M_comparator() + { + } + + _Concurrent_unordered_map_traits(const _Key_compare& _Traits) : _M_comparator(_Traits) + { + } + + class _Value_compare : public std::binary_function<value_type, value_type, bool> + { + friend class _Concurrent_unordered_map_traits<_Key_type, _Element_type, _Key_comparator, _Allocator_type, _Allow_multimapping>; + + public: + bool operator()(const value_type& _Left, const value_type& _Right) const + { + return (_M_comparator(_Left.first, _Right.first)); + } + + _Value_compare(const _Key_compare& _Traits) : _M_comparator(_Traits) + { + } + + protected: + _Key_compare _M_comparator; // the comparator predicate for keys + }; + + template<class _Type1, class _Type2> + static const _Type1& _Key_function(const std::pair<_Type1, _Type2>& _Value) + { + return (_Value.first); + } + _Key_compare _M_comparator; // the comparator predicate for keys +}; +} // namespace details; + +/// <summary> +/// The <c>concurrent_unordered_map</c> class is a concurrency-safe container that controls a varying-length sequence of +/// elements of type <c>std::pair<const _Key_type, _Element_type></c>. The sequence is represented in a way that enables concurrency-safe +/// append, element access, iterator access, and iterator traversal operations. +/// </summary> +/// <typeparam name="_Key_type"> +/// The key type. +/// </typeparam> +/// <typeparam name="_Element_type"> +/// The mapped type. +/// </typeparam> +/// <typeparam name="_Hasher"> +/// The hash function object type. This argument is optional and the default value is +/// <c>std::tr1::hash<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Key_equality"> +/// The equality comparison function object type. This argument is optional and the default value is +/// <c>std::equal_to<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Allocator_type"> +/// The type that represents the stored allocator object that encapsulates details about the allocation and +/// deallocation of memory for the concurrent unordered map. This argument is optional and the default value is +/// <c>std::allocator<std::pair<</c><typeparamref name="_Key_type"/>, <typeparamref name="_Element_type"/><c>>></c>. +/// </typeparam> +/// <remarks> +/// For detailed information on the <c>concurrent_unordered_map</c> class, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template <typename _Key_type, typename _Element_type, typename _Hasher = std::tr1::hash<_Key_type>, typename _Key_equality = std::equal_to<_Key_type>, typename _Allocator_type = std::allocator<std::pair<const _Key_type, _Element_type> > > +class concurrent_unordered_map : public details::_Concurrent_hash< details::_Concurrent_unordered_map_traits<_Key_type, _Element_type, details::_Hash_compare<_Key_type, _Hasher, _Key_equality>, _Allocator_type, false> > +{ +public: + // Base type definitions + typedef concurrent_unordered_map<_Key_type, _Element_type, _Hasher, _Key_equality, _Allocator_type> _Mytype; + typedef details::_Hash_compare<_Key_type, _Hasher, _Key_equality> _Key_compare; + typedef details::_Concurrent_hash< details::_Concurrent_unordered_map_traits<_Key_type, _Element_type, _Key_compare, _Allocator_type, false> > _Mybase; + + /// <summary> + /// The type of an ordering key. + /// </summary> + /**/ + typedef _Key_type key_type; + + /// <summary> + /// The type of an element. + /// </summary> + /**/ + typedef typename _Mybase::value_type value_type; + + /// <summary> + /// The type of a mapped value associated with each key. + /// </summary> + /**/ + typedef _Element_type mapped_type; + + /// <summary> + /// The type of the hash function. + /// </summary> + /**/ + typedef _Hasher hasher; + + /// <summary> + /// The type of the comparison function. + /// </summary> + /**/ + typedef _Key_equality key_equal; + + /// <summary> + /// The type of an allocator for managing storage. + /// </summary> + /**/ + typedef typename _Mybase::allocator_type allocator_type; + + /// <summary> + /// The type of a pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::pointer pointer; + + /// <summary> + /// The type of a constant pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_pointer const_pointer; + + /// <summary> + /// The type of a reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::reference reference; + + /// <summary> + /// The type of a constant reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_reference const_reference; + + /// <summary> + /// The type of an unsigned distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::size_type size_type; + + /// <summary> + /// The type of a signed distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::difference_type difference_type; + + /// <summary> + /// The type of an iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator iterator; + + /// <summary> + /// The type of a constant iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_iterator; + + /// <summary> + /// The type of a bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator local_iterator; + + /// <summary> + /// The type of a constant bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_local_iterator; + + /// <summary> + /// Constructs a concurrent unordered map. + /// </summary> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered map. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered map. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered map. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered map. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered map. + /// <para>The first constructor specifies an empty initial map and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered map.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + explicit concurrent_unordered_map(size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), const key_equal& _Key_equality = key_equal(), + const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(_Hasher, _Key_equality), _Allocator) + { + this->rehash(_Number_of_buckets); + } + + /// <summary> + /// Constructs a concurrent unordered map. + /// </summary> + /// <param name="_Allocator"> + /// The allocator for this unordered map. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered map. + /// <para>The first constructor specifies an empty initial map and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered map.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_map(const allocator_type& _Allocator) : _Mybase(8, _Key_compare(), _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered map. + /// </summary> + /// <typeparam name="_Iterator"> + /// The type of the input iterator. + /// </typeparam> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be copied. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be copied. + /// </param> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered map. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered map. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered map. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered map. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered map. + /// <para>The first constructor specifies an empty initial map and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered map.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + template <typename _Iterator> + concurrent_unordered_map(_Iterator _Begin, _Iterator _End, size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), + const key_equal& _Key_equality = key_equal(), const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(), allocator_type()) + { + this->rehash(_Number_of_buckets); + for (; _Begin != _End; ++_Begin) + { + _Insert(*_Begin); + } + } + + /// <summary> + /// Constructs a concurrent unordered map. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_map</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered map. + /// <para>The first constructor specifies an empty initial map and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered map.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_map(const concurrent_unordered_map& _Umap) : _Mybase(_Umap) + { + } + + /// <summary> + /// Constructs a concurrent unordered map. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_map</c> object to copy or move elements from. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered map. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered map. + /// <para>The first constructor specifies an empty initial map and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered map.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_map(const concurrent_unordered_map& _Umap, const allocator_type& _Allocator) : _Mybase(_Umap, _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered map. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_map</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered map. + /// <para>The first constructor specifies an empty initial map and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered map.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered map <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_map(concurrent_unordered_map&& _Umap) : _Mybase(std::move(_Umap)) + { + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_map</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_map</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_map</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements a concurrent vector, <c>operator=</c> either copies or moves the contents of <paramref name="_Umap"/> into + /// the concurrent vector. + /// </remarks> + /**/ + concurrent_unordered_map& operator=(const concurrent_unordered_map& _Umap) + { + _Mybase::operator=(_Umap); + return (*this); + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_map</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_map</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_map</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements in a concurrent vector, <c>operator=</c> either copies or moves the contents of <paramref name="_Umap"/> into + /// the concurrent vector. + /// </remarks> + /**/ + concurrent_unordered_map& operator=(concurrent_unordered_map&& _Umap) + { + _Mybase::operator=(std::move(_Umap)); + return (*this); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_map</c> object. + /// </summary> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// A pair that contains an iterator and a boolean value. See the Remarks section for more details. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + std::pair<iterator, bool> insert(const value_type& _Value) + { + return _Insert(_Value); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_map</c> object. + /// </summary> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location of the object. If the key already exists in the container + /// an iterator pointing to the duplicate key location in the map is returned. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + iterator insert(const_iterator _Where, const value_type& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_map. + return _Insert(_Value).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_map</c> object. + /// </summary> + /// <typeparam name="_Iterator"> + /// The iterator type used for insertion. + /// </typeparam> + /// <param name="_First"> + /// The beginning of the range to insert. + /// </param> + /// <param name="_Last"> + /// The end of the range to insert. + /// </param> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Iterator> + void insert(_Iterator _First, _Iterator _Last) + { + _Insert(_First, _Last); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_map</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted into the map. + /// </typeparam> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// A pair that contains an iterator and a boolean value. See the Remarks section for more details. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + std::pair<iterator, bool> insert(_Valty&& _Value) + { + return _Insert(std::forward<_Valty>(_Value)); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_map</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted into the map. + /// </typeparam> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location of the object. If the key already exists in the container + /// an iterator pointing to the duplicate key location in the map is returned. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + typename std::tr1::enable_if<!std::tr1::is_same<const_iterator, + typename std::tr1::remove_reference<_Valty>::type>::value, iterator>::type + insert(const_iterator _Where, _Valty&& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_map. + return _Insert(std::forward<_Valty>(_Value)).first; + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_map</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Where"> + /// The iterator position to erase from. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by <see cref="concurrent_unordered_map::equal_range Method"> + /// concurrent_unordered_map::equal_range</see>(_Keyval). </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_map::end Method"> concurrent_unordered_map::end</see>() if no such element exists. The third + /// member function returns the number of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _Where) + { + return _Mybase::unsafe_erase(_Where); + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_map</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be erased. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be erased. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by <see cref="concurrent_unordered_map::equal_range Method"> + /// concurrent_unordered_map::equal_range</see>(_Keyval). </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_map::end Method"> concurrent_unordered_map::end</see>() if no such element exists. The third + /// member function returns the number of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _Begin, const_iterator _End) + { + return _Mybase::unsafe_erase(_Begin, _End); + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_map</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to erase. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by <see cref="concurrent_unordered_map::equal_range Method"> + /// concurrent_unordered_map::equal_range</see>(_Keyval). </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_map::end Method"> concurrent_unordered_map::end</see>() if no such element exists. The third + /// member function returns the number of elements it removes. + /// </returns> + /**/ + size_type unsafe_erase(const key_type& _Keyval) + { + return _Mybase::unsafe_erase(_Keyval); + } + + /// <summary> + /// Swaps the contents of two <c>concurrent_unordered_map</c> objects. This method is not concurrency-safe. + /// </summary> + /// <param name="_Umap"> + /// The <c>concurrent_unordered_map</c> object to swap with. + /// </param> + /**/ + void swap(concurrent_unordered_map& _Umap) + { + _Mybase::swap(_Umap); + } + + /// <summary> + /// Gets the stored hash function object. + /// </summary> + /// <returns> + /// The stored hash function object. + /// </returns> + /**/ + hasher hash_function() const + { + return _M_comparator._M_hash_object; + } + + /// <summary> + /// Gets the stored equality comparison function object. + /// </summary> + /// <returns> + /// The stored equality comparison function object. + /// </returns> + /**/ + key_equal key_eq() const + { + return _M_comparator._M_key_compare_object; + } + + /// <summary> + /// Finds or inserts an element with the specified key. This method is concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to find or insert. + /// </param> + /// <returns> + /// A reference to the data value of the found or inserted element. + /// </returns> + /// <remarks> + /// If the argument key value is not found, then it is inserted along with the default value of the data type. + /// <para> <c>operator[]</c> may be used to insert elements into a map <c>m</c> using <c>m[_Key] = DataValue;</c>, where + /// <c>DataValue</c> is the value of the <c>mapped_type</c> of the element with a key value of <c>_Key</c>.</para> + /// <para> When using <c>operator[]</c> to insert elements, the returned reference does not indicate whether an insertion + /// is changing a pre-existing element or creating a new one. The member functions <see cref="concurrent_unordered_map::find Method"> + /// find</see> and <see cref="concurrent_unordered_map::insert Method">insert</see> can be used to determine whether an element + /// with a specified key is already present before an insertion.</para> + /// </remarks> + /**/ + mapped_type& operator[](const key_type& _Keyval) + { + iterator _Where = find(_Keyval); + + if (_Where == end()) + { + _Where = _Insert(std::make_pair(_Keyval, mapped_type())).first; + } + + return ((*_Where).second); + } + + /// <summary> + /// Finds or inserts an element with the specified key. This method is concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to find or insert. + /// </param> + /// <returns> + /// A reference to the data value of the found or inserted element. + /// </returns> + /// <remarks> + /// If the argument key value is not found, then it is inserted along with the default value of the data type. + /// <para> <c>operator[]</c> may be used to insert elements into a map <c>m</c> using <c>m[_Key] = DataValue;</c>, where + /// <c>DataValue</c> is the value of the <c>mapped_type</c> of the element with a key value of <c>_Key</c>.</para> + /// <para> When using <c>operator[]</c> to insert elements, the returned reference does not indicate whether an insertion + /// is changing a pre-existing element or creating a new one. The member functions <see cref="concurrent_unordered_map::find Method"> + /// find</see> and <see cref="concurrent_unordered_map::insert Method">insert</see> can be used to determine whether an element + /// with a specified key is already present before an insertion.</para> + /// </remarks> + /**/ + mapped_type& operator[](key_type && _Keyval) + { + iterator _Where = find(_Keyval); + + if (_Where == end()) + { + _Where = _Insert(std::make_pair(std::forward<key_type>(_Keyval), mapped_type())).first; + } + + return ((*_Where).second); + } + + /// <summary> + /// Finds an element in a <c>concurrent_unordered_map</c> with a specified key value.. This method is concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to find. + /// </param> + /// <returns> + /// A reference to the data value of the element found. + /// </returns> + /// <remarks> + /// If the argument key value is not found, the function throws an object of class <c>out_of_range</c>. + /// </remarks> + /**/ + mapped_type& at(const key_type& _Keyval) + { + iterator _Where = find(_Keyval); + + if (_Where == end()) + { + throw std::out_of_range("invalid concurrent_unordered_map<K, T> key"); + } + + return ((*_Where).second); + } + + /// <summary> + /// Finds an element in a <c>concurrent_unordered_map</c> with a specified key value.. This method is concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to find. + /// </param> + /// <returns> + /// A reference to the data value of the element found. + /// </returns> + /// <remarks> + /// If the argument key value is not found, the function throws an object of class <c>out_of_range</c>. + /// </remarks> + /**/ + const mapped_type& at(const key_type& _Keyval) const + { + const_iterator _Where = find(_Keyval); + + if (_Where == end()) + { + throw std::out_of_range("invalid concurrent_unordered_map<K, T> key"); + } + + return ((*_Where).second); + } +}; + +/// <summary> +/// The <c>concurrent_unordered_multimap</c> class is an concurrency-safe container that controls a varying-length sequence of +/// elements of type <c>std::pair<const _Key_type, _Element_type></c>. The sequence is represented in a way that enables +/// concurrency-safe append, element access, iterator access and iterator traversal operations. +/// </summary> +/// <typeparam name="_Key_type"> +/// The key type. +/// </typeparam> +/// <typeparam name="_Element_type"> +/// The mapped type. +/// </typeparam> +/// <typeparam name="_Hasher"> +/// The hash function object type. This argument is optional and the default value is +/// <c>std::tr1::hash<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Key_equality"> +/// The equality comparison function object type. This argument is optional and the default value is +/// <c>std::equal_to<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Allocator_type"> +/// The type that represents the stored allocator object that encapsulates details about the allocation and +/// deallocation of memory for the concurrent vector. This argument is optional and the default value is +/// <c>std::allocator<std::pair<</c><typeparamref name="_Key_type"/>, <typeparamref name="_Element_type"/><c>>></c>. +/// </typeparam> +/// <remarks> +/// For detailed information on the <c>concurrent_unordered_multimap</c> class, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template <typename _Key_type, typename _Element_type, typename _Hasher = std::tr1::hash<_Key_type>, typename _Key_equality = std::equal_to<_Key_type>, typename _Allocator_type = std::allocator<std::pair<const _Key_type, _Element_type> > > +class concurrent_unordered_multimap : public details::_Concurrent_hash< details::_Concurrent_unordered_map_traits<_Key_type, _Element_type, details::_Hash_compare<_Key_type, _Hasher, _Key_equality>, _Allocator_type, true> > +{ +public: + // Base type definitions + typedef concurrent_unordered_multimap<_Key_type, _Element_type, _Hasher, _Key_equality, _Allocator_type> _Mytype; + typedef details::_Hash_compare<_Key_type, _Hasher, _Key_equality> _Key_compare; + typedef details::_Concurrent_hash< details::_Concurrent_unordered_map_traits<_Key_type, _Element_type, _Key_compare, _Allocator_type, true> > _Mybase; + + /// <summary> + /// The type of an ordering key. + /// </summary> + /**/ + typedef _Key_type key_type; + + /// <summary> + /// The type of an element. + /// </summary> + /**/ + typedef typename _Mybase::value_type value_type; + + /// <summary> + /// The type of a mapped value associated with each key. + /// </summary> + /**/ + typedef _Element_type mapped_type; + + /// <summary> + /// The type of the hash function. + /// </summary> + /**/ + typedef _Hasher hasher; + + /// <summary> + /// The type of the comparison function. + /// </summary> + /**/ + typedef _Key_equality key_equal; + + /// <summary> + /// The type of an allocator for managing storage. + /// </summary> + /**/ + typedef typename _Mybase::allocator_type allocator_type; + + /// <summary> + /// The type of a pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::pointer pointer; + + /// <summary> + /// The type of a constant pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_pointer const_pointer; + + /// <summary> + /// The type of a reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::reference reference; + + /// <summary> + /// The type of a constant reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_reference const_reference; + + /// <summary> + /// The type of an unsigned distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::size_type size_type; + + /// <summary> + /// The type of a signed distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::difference_type difference_type; + + /// <summary> + /// The type of an iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator iterator; + + /// <summary> + /// The type of a constant iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_iterator; + + /// <summary> + /// The type of a bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator local_iterator; + + /// <summary> + /// The type of a constant bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_local_iterator; + + /// <summary> + /// Constructs a concurrent unordered multimap. + /// </summary> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered multimap. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered multimap. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered multimap. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered multimap. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multimap. + /// <para>The first constructor specifies an empty initial multimap and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multimap.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + explicit concurrent_unordered_multimap(size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), const key_equal& _Key_equality = key_equal(), + const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(_Hasher, _Key_equality), _Allocator) + { + this->rehash(_Number_of_buckets); + } + + /// <summary> + /// Constructs a concurrent unordered multimap. + /// </summary> + /// <param name="_Allocator"> + /// The allocator for this unordered multimap. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multimap. + /// <para>The first constructor specifies an empty initial multimap and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multimap.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multimap(const allocator_type& _Allocator) : _Mybase(8, _Key_compare(), _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered multimap. + /// </summary> + /// <typeparam name="_Iterator"> + /// The type of the input iterator. + /// </typeparam> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be copied. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be copied. + /// </param> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered multimap. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered multimap. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered multimap. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered multimap. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multimap. + /// <para>The first constructor specifies an empty initial multimap and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multimap.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + template <typename _Iterator> + concurrent_unordered_multimap(_Iterator _Begin, _Iterator _End, size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), + const key_equal& _Key_equality = key_equal(), const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(), allocator_type()) + { + this->rehash(_Number_of_buckets); + for (; _Begin != _End; ++_Begin) + { + _Insert(*_Begin); + } + } + + /// <summary> + /// Constructs a concurrent unordered multimap. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_multimap</c> object to copy elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multimap. + /// <para>The first constructor specifies an empty initial multimap and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multimap.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multimap(const concurrent_unordered_multimap& _Umap) : _Mybase(_Umap) + { + } + + /// <summary> + /// Constructs a concurrent unordered multimap. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_multimap</c> object to copy elements from. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered multimap. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multimap. + /// <para>The first constructor specifies an empty initial multimap and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multimap.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multimap(const concurrent_unordered_multimap& _Umap, const allocator_type& _Allocator) : _Mybase(_Umap, _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered multimap. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_multimap</c> object to copy elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multimap. + /// <para>The first constructor specifies an empty initial multimap and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multimap.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multimap <paramref name="_Umap"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multimap(concurrent_unordered_multimap&& _Umap) : _Mybase(std::move(_Umap)) + { + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_multimap</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_multimap</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_multimap</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements in a concurrent unordered multimap, <c>operator=</c> either copies or moves the contents of + /// <paramref name="_Umap"/> into the concurrent unordered multimap. + /// </remarks> + /**/ + concurrent_unordered_multimap& operator=(const concurrent_unordered_multimap& _Umap) + { + _Mybase::operator=(_Umap); + return (*this); + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_multimap</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Umap"> + /// The source <c>concurrent_unordered_multimap</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_multimap</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements in a concurrent unordered multimap, <c>operator=</c> either copies or moves the contents of + /// <paramref name="_Umap"/> into the concurrent unordered multimap. + /// </remarks> + /**/ + concurrent_unordered_multimap& operator=(concurrent_unordered_multimap&& _Umap) + { + _Mybase::operator=(std::move(_Umap)); + return (*this); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multimap</c> object. + /// </summary> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + iterator insert(const value_type& _Value) + { + return _Insert(_Value).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multimap</c> object. + /// </summary> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + iterator insert(const_iterator _Where, const value_type& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_multimap. + return _Insert(_Value).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multimap</c> object. + /// </summary> + /// <typeparam name="_Iterator"> + /// The iterator type used for insertion. + /// </typeparam> + /// <param name="_First"> + /// The beginning of the range to insert. + /// </param> + /// <param name="_Last"> + /// The end of the range to insert. + /// </param> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Iterator> + void insert(_Iterator _First, _Iterator _Last) + { + _Insert(_First, _Last); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multimap</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted into the map. + /// </typeparam> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + iterator insert(_Valty&& _Value) + { + return _Insert(std::forward<_Valty>(_Value)).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multimap</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted into the map. + /// </typeparam> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + typename std::tr1::enable_if<!std::tr1::is_same<const_iterator, typename std::tr1::remove_reference<_Valty>::type>::value, iterator>::type + insert(const_iterator _Where, _Valty&& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_multimap. + return _Insert(std::forward<_Valty>(_Value)).first; + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_multimap</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Where"> + /// The iterator position to erase from. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by <see cref="concurrent_unordered_multimap::equal_range Method"> + /// concurrent_unordered_multimap::equal_range</see>(_Keyval). </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_multimap::end Method">concurrent_unordered_multimap::end</see>() if no such element exists. The third + /// member function returns the number of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _Where) + { + return _Mybase::unsafe_erase(_Where); + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_multimap</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to erase. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by <see cref="concurrent_unordered_multimap::equal_range Method"> + /// concurrent_unordered_multimap::equal_range</see>(_Keyval). </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_multimap::end Method">concurrent_unordered_multimap::end</see>() if no such element exists. The third + /// member function returns the number of elements it removes. + /// </returns> + /**/ + size_type unsafe_erase(const key_type& _Keyval) + { + return _Mybase::unsafe_erase(_Keyval); + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_multimap</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Begin"> + /// Position of the first element in the range of elements to be erased. + /// </param> + /// <param name="_End"> + /// Position of the first element beyond the range of elements to be erased. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by <see cref="concurrent_unordered_multimap::equal_range Method"> + /// concurrent_unordered_multimap::equal_range</see>(_Keyval). </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_multimap::end Method">concurrent_unordered_multimap::end</see>() if no such element exists. The third + /// member function returns the number of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _First, const_iterator _Last) + { + return _Mybase::unsafe_erase(_First, _Last); + } + + /// <summary> + /// Swaps the contents of two <c>concurrent_unordered_multimap</c> objects. This method is not concurrency-safe. + /// </summary> + /// <param name="_Umap"> + /// The <c>concurrent_unordered_multimap</c> object to swap with. + /// </param> + /**/ + void swap(concurrent_unordered_multimap& _Umap) + { + _Mybase::swap(_Umap); + } + + /// <summary> + /// Returns the stored hash function object. + /// </summary> + /// <returns> + /// The stored hash function object. + /// </returns> + /**/ + hasher hash_function() const + { + return _M_comparator._M_hash_object; + } + + /// <summary> + /// Returns the stored equality comparison function object. + /// </summary> + /// <returns> + /// The stored equality comparison function object. + /// </returns> + /**/ + key_equal key_eq() const + { + return _M_comparator._M_key_compare_object; + } +}; +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma warning(pop) +#pragma pack(pop) diff --git a/test_data/lots_of_files/concurrent_unordered_set.h b/test_data/lots_of_files/concurrent_unordered_set.h new file mode 100644 index 0000000..419edf3 --- /dev/null +++ b/test_data/lots_of_files/concurrent_unordered_set.h @@ -0,0 +1,1252 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* concurrent_unordered_set.h +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include <utility> +#include "internal_concurrent_hash.h" + +#define _PPL_CONTAINER + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) + #error ERROR: Concurrency Runtime is supported only on X64, X86 and ARM architectures. +#endif /* !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_M_CEE) + #error ERROR: Concurrency Runtime is not supported when compiling /clr. +#endif /* defined (_M_CEE) */ + +#pragma pack(push,_CRT_PACKING) +#pragma warning(push) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation + +namespace Concurrency +{ +namespace details +{ +// Template class for hash set traits +template<typename _Key_type, typename _Key_comparator, typename _Allocator_type, bool _Allow_multimapping> +class _Concurrent_unordered_set_traits : public std::_Container_base +{ +public: + typedef _Key_type value_type; + typedef _Key_type key_type; + typedef _Key_comparator _Key_compare; + + typedef typename _Allocator_type::template rebind<value_type>::other allocator_type; + + enum + { + _M_allow_multimapping = _Allow_multimapping + }; + + _Concurrent_unordered_set_traits() : _M_comparator() + { + } + + _Concurrent_unordered_set_traits(const _Key_comparator& _Traits) : _M_comparator(_Traits) + { + } + + typedef _Key_compare _Value_compare; + + static const _Key_type& _Key_function(const value_type& _Value) + { + return _Value; + } + + _Key_comparator _M_comparator; // the comparator predicate for keys +}; +} // namespace details; + +/// <summary> +/// The <c>concurrent_unordered_set</c> class is an concurrency-safe container that controls a varying-length sequence of +/// elements of type _Key_type. The sequence is represented in a way that enables concurrency-safe append, element access, +/// iterator access and iterator traversal operations. +/// </summary> +/// <typeparam name="_Key_type"> +/// The key type. +/// </typeparam> +/// <typeparam name="_Hasher"> +/// The hash function object type. This argument is optional and the default value is +/// <c>std::tr1::hash<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Key_equality"> +/// The equality comparison function object type. This argument is optional and the default value is +/// <c>std::equal_to<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Allocator_type"> +/// The type that represents the stored allocator object that encapsulates details about the allocation and +/// deallocation of memory for the concurrent unordered set. This argument is optional and the default value is +/// <c>std::allocator<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <remarks> +/// For detailed information on the <c>concurrent_unordered_set</c> class, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template <typename _Key_type, typename _Hasher = std::tr1::hash<_Key_type>, typename _Key_equality = std::equal_to<_Key_type>, typename _Allocator_type = std::allocator<_Key_type> > +class concurrent_unordered_set : public details::_Concurrent_hash< details::_Concurrent_unordered_set_traits<_Key_type, details::_Hash_compare<_Key_type, _Hasher, _Key_equality>, _Allocator_type, false> > +{ +public: + // Base type definitions + typedef concurrent_unordered_set<_Key_type, _Hasher, _Key_equality, _Allocator_type> _Mytype; + typedef details::_Hash_compare<_Key_type, _Hasher, _Key_equality> _Key_compare; + typedef details::_Concurrent_hash< details::_Concurrent_unordered_set_traits<_Key_type, _Key_compare, _Allocator_type, false> > _Mybase; + + /// <summary> + /// The type of an ordering key. + /// </summary> + /**/ + typedef _Key_type key_type; + + /// <summary> + /// The type of an element. + /// </summary> + /**/ + typedef typename _Mybase::value_type value_type; + + /// <summary> + /// The type of the hash function. + /// </summary> + /**/ + typedef _Hasher hasher; + + /// <summary> + /// The type of the comparison function. + /// </summary> + /**/ + typedef _Key_equality key_equal; + + /// <summary> + /// The type of an allocator for managing storage. + /// </summary> + /**/ + typedef typename _Mybase::allocator_type allocator_type; + + /// <summary> + /// The type of a pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::pointer pointer; + + /// <summary> + /// The type of a constant pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_pointer const_pointer; + + /// <summary> + /// The type of a reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::reference reference; + + /// <summary> + /// The type of a constant reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_reference const_reference; + + /// <summary> + /// The type of an unsigned distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::size_type size_type; + + /// <summary> + /// The type of a signed distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::difference_type difference_type; + + /// <summary> + /// The type of an iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator iterator; + + /// <summary> + /// The type of a constant iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_iterator; + + /// <summary> + /// The type of a bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator local_iterator; + + /// <summary> + /// The type of a constant bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_local_iterator; + + /// <summary> + /// Constructs a concurrent unordered set. + /// </summary> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered set. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered set. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered set. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered set. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered set. + /// <para>The first constructor specifies an empty initial set and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered set.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + explicit concurrent_unordered_set(size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), const key_equal& _Key_equality = key_equal(), + const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(_Hasher, _Key_equality), _Allocator) + { + this->rehash(_Number_of_buckets); + } + + /// <summary> + /// Constructs a concurrent unordered set. + /// </summary> + /// <param name="_Allocator"> + /// The allocator for this unordered set. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered set. + /// <para>The first constructor specifies an empty initial set and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered set.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_set(const allocator_type& _Allocator) : _Mybase(8, _Key_compare(), _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered set. + /// </summary> + /// <typeparam name="_Iterator"> + /// The type of the input iterator. + /// </typeparam> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be copied. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be copied. + /// </param> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered set. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered set. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered set. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered set. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered set. + /// <para>The first constructor specifies an empty initial set and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered set.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + template <typename _Iterator> + concurrent_unordered_set(_Iterator _First, _Iterator _Last, size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), + const key_equal& _Key_equality = key_equal(), const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(), allocator_type()) + { + this->rehash(_Number_of_buckets); + for (; _First != _Last; ++_First) + { + _Insert(*_First); + } + } + + /// <summary> + /// Constructs a concurrent unordered set. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_set</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered set. + /// <para>The first constructor specifies an empty initial set and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered set.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_set(const concurrent_unordered_set& _Uset) : _Mybase(_Uset) + { + } + + /// <summary> + /// Constructs a concurrent unordered set. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_set</c> object to copy or move elements from. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered set. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered set. + /// <para>The first constructor specifies an empty initial set and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered set.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_set(const concurrent_unordered_set& _Uset, const allocator_type& _Allocator) : _Mybase(_Uset, _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered set. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_set</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered set. + /// <para>The first constructor specifies an empty initial set and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered set.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered set <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_set(concurrent_unordered_set&& _Uset) : _Mybase(std::move(_Uset)) + { + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_set</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_set</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_set</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements in a concurrent unordered set, <c>operator=</c> either copies or moves the contents of + /// <paramref name="_Uset"/> into the concurrent unordered set. + /// </remarks> + /**/ + concurrent_unordered_set& operator=(const concurrent_unordered_set& _Uset) + { + _Mybase::operator=(_Uset); + return (*this); + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_set</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_set</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_set</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements in a concurrent unordered set, <c>operator=</c> either copies or moves the contents of + /// <paramref name="_Uset"/> into the concurrent unordered set. + /// </remarks> + /**/ + concurrent_unordered_set& operator=(concurrent_unordered_set&& _Uset) + { + _Mybase::operator=(std::move(_Uset)); + return (*this); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_set</c> object. + /// </summary> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// A pair that contains an iterator and a boolean value. See the Remarks section for more details. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + std::pair<iterator, bool> insert(const value_type& _Value) + { + return _Insert(_Value); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_set</c> object. + /// </summary> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location of the object. If the key already exists in the container + /// an iterator pointing to the duplicate key location in the set is returned. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + iterator insert(const_iterator _Where, const value_type& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_set. + return _Insert(_Value).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_set</c> object. + /// </summary> + /// <typeparam name="_Iterator"> + /// The iterator type used for insertion. + /// </typeparam> + /// <param name="_First"> + /// The beginning of the range to insert. + /// </param> + /// <param name="_Last"> + /// The end of the range to insert. + /// </param> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Iterator> + void insert(_Iterator _First, _Iterator _Last) + { + _Insert(_First, _Last); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_set</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted into the set. + /// </typeparam> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// A pair that contains an iterator and a boolean value. See the Remarks section for more details. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + std::pair<iterator, bool> insert(_Valty&& _Value) + { + return _Insert(std::forward<_Valty>(_Value)); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_set</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted into the set. + /// </typeparam> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location of the object. If the key already exists in the container + /// an iterator pointing to the duplicate key location in the set is returned. + /// </returns> + /// <remarks> + /// The first member function determines whether an element X exists in the sequence whose key has equivalent ordering to + /// that of <paramref name="_Value"/>. If not, it creates such an element X and initializes it with <paramref name="_Value"/>. + /// The function then determines the iterator <c>where</c> that designates X. If an insertion occurred, the function returns + /// <c>std::pair(where, true)</c>. Otherwise, it returns <c>std::pair(where, false)</c>. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + typename std::tr1::enable_if<!std::tr1::is_same<const_iterator, + typename std::tr1::remove_reference<_Valty>::type>::value, iterator>::type + insert(const_iterator _Where, _Valty&& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_set. + return _Insert(std::forward<_Valty>(_Value)).first; + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_set</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Where"> + /// The iterator position to erase from. + /// </param> + /// <remarks> + /// The first member function removes the element pointed to by <paramref name="_Where"/>. The second member function removes the elements + /// in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by + /// <see cref="concurrent_unordered_set::equal_range Method"/>(_Keyval).</para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_set::end Method"/>() if no such element exists. The third member function returns the number + /// of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _Where) + { + return _Mybase::unsafe_erase(_Where); + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_set</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to erase. + /// </param> + /// <remarks> + /// The first member function removes the element pointed to by <paramref name="_Where"/>. The second member function removes the elements + /// in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by + /// <see cref="concurrent_unordered_set::equal_range Method"/>(_Keyval).</para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_set::end Method"/>() if no such element exists. The third member function returns the number + /// of elements it removes. + /// </returns> + /**/ + size_type unsafe_erase(const key_type& _Keyval) + { + return _Mybase::unsafe_erase(_Keyval); + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_set</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be erased. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be erased. + /// </param> + /// <remarks> + /// The first member function removes the element pointed to by <paramref name="_Where"/>. The second member function removes the elements + /// in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by + /// <see cref="concurrent_unordered_set::equal_range Method"/>(_Keyval).</para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_set::end Method"/>() if no such element exists. The third member function returns the number + /// of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _First, const_iterator _Last) + { + return _Mybase::unsafe_erase(_First, _Last); + } + + /// <summary> + /// Swaps the contents of two <c>concurrent_unordered_set</c> objects. This method is not concurrency-safe. + /// </summary> + /// <param name="_Uset"> + /// The <c>concurrent_unordered_set</c> object to swap with. + /// </param> + /**/ + void swap(concurrent_unordered_set& _Uset) + { + _Mybase::swap(_Uset); + } + + /// <summary> + /// Returns the stored hash function object. + /// </summary> + /// <returns> + /// The stored hash function object. + /// </returns> + /**/ + hasher hash_function() const + { + return _M_comparator._M_hash_object; + } + + /// <summary> + /// Returns the stored equality comparison function object. + /// </summary> + /// <returns> + /// The stored equality comparison function object. + /// </returns> + /**/ + key_equal key_eq() const + { + return _M_comparator._M_key_compare_object; + } +}; + +/// <summary> +/// The <c>concurrent_unordered_multiset</c> class is an concurrency-safe container that controls a varying-length sequence of +/// elements of type _Key_type. The sequence is represented in a way that enables concurrency-safe append, element access, +/// iterator access and iterator traversal operations. +/// </summary> +/// <typeparam name="_Key_type"> +/// The key type. +/// </typeparam> +/// <typeparam name="_Hasher"> +/// The hash function object type. This argument is optional and the default value is +/// <c>std::tr1::hash<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Key_equality"> +/// The equality comparison function object type. This argument is optional and the default value is +/// <c>std::equal_to<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <typeparam name="_Allocator_type"> +/// The type that represents the stored allocator object that encapsulates details about the allocation and +/// deallocation of memory for the concurrent vector. This argument is optional and the default value is +/// <c>std::allocator<</c><typeparamref name="_Key_type"/><c>></c>. +/// </typeparam> +/// <remarks> +/// For detailed information on the <c>concurrent_unordered_multiset</c> class, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template <typename _Key_type, typename _Hasher = std::tr1::hash<_Key_type>, typename _Key_equality = std::equal_to<_Key_type>, typename _Allocator_type = std::allocator<_Key_type> > +class concurrent_unordered_multiset : public details::_Concurrent_hash< details::_Concurrent_unordered_set_traits<_Key_type, details::_Hash_compare<_Key_type, _Hasher, _Key_equality>, _Allocator_type, true> > +{ +public: + // Base type definitions + typedef concurrent_unordered_multiset<_Key_type, _Hasher, _Key_equality, _Allocator_type> _Mytype; + typedef details::_Hash_compare<_Key_type, _Hasher, _Key_equality> _Key_compare; + typedef details::_Concurrent_hash< details::_Concurrent_unordered_set_traits<_Key_type, _Key_compare, _Allocator_type, true> > _Mybase; + + /// <summary> + /// The type of an ordering key. + /// </summary> + /**/ + typedef _Key_type key_type; + + /// <summary> + /// The type of an element. + /// </summary> + /**/ + typedef typename _Mybase::value_type value_type; + + /// <summary> + /// The type of the hash function. + /// </summary> + /**/ + typedef _Hasher hasher; + + /// <summary> + /// The type of the comparison function. + /// </summary> + /**/ + typedef _Key_equality key_equal; + + /// <summary> + /// The type of an allocator for managing storage. + /// </summary> + /**/ + typedef typename _Mybase::allocator_type allocator_type; + + /// <summary> + /// The type of a pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::pointer pointer; + + /// <summary> + /// The type of a constant pointer to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_pointer const_pointer; + + /// <summary> + /// The type of a reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::reference reference; + + /// <summary> + /// The type of a constant reference to an element. + /// </summary> + /**/ + typedef typename _Mybase::const_reference const_reference; + + /// <summary> + /// The type of an unsigned distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::size_type size_type; + + /// <summary> + /// The type of a signed distance between two elements. + /// </summary> + /**/ + typedef typename _Mybase::difference_type difference_type; + + /// <summary> + /// The type of an iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator iterator; + + /// <summary> + /// The type of a constant iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_iterator; + + /// <summary> + /// The type of a bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::iterator local_iterator; + + /// <summary> + /// The type of a constant bucket iterator for the controlled sequence. + /// </summary> + /**/ + typedef typename _Mybase::const_iterator const_local_iterator; + + /// <summary> + /// Constructs a concurrent unordered multiset. + /// </summary> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered multiset. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered multiset. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered multiset. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered multiset. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multiset. + /// <para>The first constructor specifies an empty initial multiset and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multiset.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + explicit concurrent_unordered_multiset(size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), const key_equal& _Key_equality = key_equal(), + const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(_Hasher, _Key_equality), _Allocator) + { + this->rehash(_Number_of_buckets); + } + + /// <summary> + /// Constructs a concurrent unordered multiset. + /// </summary> + /// <param name="_Allocator"> + /// The allocator for this unordered multiset. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multiset. + /// <para>The first constructor specifies an empty initial multiset and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multiset.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multiset(const allocator_type& _Allocator) : _Mybase(8, _Key_compare(), _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered multiset. + /// </summary> + /// <typeparam name="_Iterator"> + /// The type of the input iterator. + /// </typeparam> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be copied. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be copied. + /// </param> + /// <param name="_Number_of_buckets"> + /// The initial number of buckets for this unordered multiset. + /// </param> + /// <param name="_Hasher"> + /// The hash function for this unordered multiset. + /// </param> + /// <param name="_Key_equality"> + /// The equality comparison function for this unordered multiset. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered multiset. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multiset. + /// <para>The first constructor specifies an empty initial multiset and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multiset.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + template <typename _Iterator> + concurrent_unordered_multiset(_Iterator _First, _Iterator _Last, size_type _Number_of_buckets = 8, const hasher& _Hasher = hasher(), + const key_equal& _Key_equality = key_equal(), const allocator_type& _Allocator = allocator_type()) + : _Mybase(_Number_of_buckets, _Key_compare(), allocator_type()) + { + this->rehash(_Number_of_buckets); + for (; _First != _Last; ++_First) + { + _Insert(*_First); + } + } + + /// <summary> + /// Constructs a concurrent unordered multiset. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_multiset</c> object to copy elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multiset. + /// <para>The first constructor specifies an empty initial multiset and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multiset.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multiset(const concurrent_unordered_multiset& _Uset) : _Mybase(_Uset) + { + } + + /// <summary> + /// Constructs a concurrent unordered multiset. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_multiset</c> object to copy elements from. + /// </param> + /// <param name="_Allocator"> + /// The allocator for this unordered multiset. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multiset. + /// <para>The first constructor specifies an empty initial multiset and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multiset.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multiset(const concurrent_unordered_multiset& _Uset, const allocator_type& _Allocator) : _Mybase(_Uset, _Allocator) + { + } + + /// <summary> + /// Constructs a concurrent unordered multiset. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_multiset</c> object to move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Allocator"/> and initialize the unordered multiset. + /// <para>The first constructor specifies an empty initial multiset and explicitly specifies the number of buckets, + /// hash function, equality function and allocator type to be used.</para> + /// <para>The second constructor specifies an allocator for the unordered multiset.</para> + /// <para>The third constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// <para>The fourth and fifth constructors specify a copy of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// <para>The last constructor specifies a move of the concurrent unordered multiset <paramref name="_Uset"/>.</para> + /// </remarks> + /**/ + concurrent_unordered_multiset(concurrent_unordered_multiset&& _Uset) : _Mybase(std::move(_Uset)) + { + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_multiset</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_multiset</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_multiset</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements in a concurrent unordered multiset, <c>operator=</c> either copies or moves the contents of + /// <paramref name="_Uset"/> into the concurrent unordered multiset. + /// </remarks> + /**/ + concurrent_unordered_multiset& operator=(const concurrent_unordered_multiset& _Uset) + { + _Mybase::operator=(_Uset); + return (*this); + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_unordered_multiset</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Uset"> + /// The source <c>concurrent_unordered_multiset</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_unordered_multiset</c> object. + /// </returns> + /// <remarks> + /// After erasing any existing elements in a concurrent unordered multiset, <c>operator=</c> either copies or moves the contents of + /// <paramref name="_Uset"/> into the concurrent unordered multiset. + /// </remarks> + /**/ + concurrent_unordered_multiset& operator=(concurrent_unordered_multiset&& _Uset) + { + _Mybase::operator=(std::move(_Uset)); + return (*this); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multiset</c> object. + /// </summary> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + iterator insert(const value_type& _Value) + { + return _Insert(_Value).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multiset</c> object. + /// </summary> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + iterator insert(const_iterator _Where, const value_type& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_multiset. + return _Insert(_Value).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multiset</c> object. + /// </summary> + /// <typeparam name="_Iterator"> + /// The iterator type used for insertion. + /// </typeparam> + /// <param name="_First"> + /// The beginning of the range to insert. + /// </param> + /// <param name="_Last"> + /// The end of the range to insert. + /// </param> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Iterator> + void insert(_Iterator _First, _Iterator _Last) + { + _Insert(_First, _Last); + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multiset</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted. + /// </typeparam> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + iterator insert(_Valty&& _Value) + { + return _Insert(std::forward<_Valty>(_Value)).first; + } + + /// <summary> + /// Adds elements to the <c>concurrent_unordered_multiset</c> object. + /// </summary> + /// <typeparam name="_Valty"> + /// The type of the value inserted. + /// </typeparam> + /// <param name="_Where"> + /// The starting location to search for an insertion point. + /// </param> + /// <param name="_Value"> + /// The value to be inserted. + /// </param> + /// <returns> + /// An iterator pointing to the insertion location. + /// </returns> + /// <remarks> + /// The first member function inserts the element <paramref name="_Value"/> in the controlled sequence, then returns the iterator + /// that designates the inserted element. + /// <para>The second member function returns insert(<paramref name="_Value"/>), using <paramref name="_Where"/> as a starting + /// place within the controlled sequence to search for the insertion point.</para> + /// <para>The third member function inserts the sequence of element values from the range [<paramref name="_First"/>, + /// <paramref name="_Last"/>).</para> + /// <para>The last two member functions behave the same as the first two, except that <paramref name="_Value"/> is used to + /// construct the inserted value.</para> + /// </remarks> + /**/ + template<class _Valty> + typename std::tr1::enable_if<!std::tr1::is_same<const_iterator, + typename std::tr1::remove_reference<_Valty>::type>::value, iterator>::type + insert(const_iterator _Where, _Valty&& _Value) + { + // Current implementation ignores the hint. The method is provided for compatibility with unordered_multiset. + return _Insert(std::forward<_Valty>(_Value)).first; + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_multiset</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Where"> + /// The iterator position to erase from. + /// </param> + /// <remarks> + /// The first member function removes the element pointed to by <paramref name="_Where"/>. The second member function removes the elements + /// in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by + /// <see cref="concurrent_unordered_multiset::equal_range Method"/>(_Keyval).</para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_multiset::end Method"/>() if no such element exists. The third member function returns the number + /// of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _Where) + { + return _Mybase::unsafe_erase(_Where); + } + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_multiset</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Begin"> + /// The position of the first element in the range of elements to be erased. + /// </param> + /// <param name="_End"> + /// The position of the first element beyond the range of elements to be erased. + /// </param> + /// <remarks> + /// The first member function removes the element pointed to by <paramref name="_Where"/>. The second member function removes the elements + /// in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by + /// <see cref="concurrent_unordered_multiset::equal_range Method"/>(_Keyval).</para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_multiset::end Method"/>() if no such element exists. The third member function returns the number + /// of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _First, const_iterator _Last) + { + return _Mybase::unsafe_erase(_First, _Last); + } + + + /// <summary> + /// Removes elements from the <c>concurrent_unordered_multiset</c> at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to erase. + /// </param> + /// <remarks> + /// The first member function removes the element pointed to by <paramref name="_Where"/>. The second member function removes the elements + /// in the range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// <para>The third member function removes the elements in the range delimited by + /// <see cref="concurrent_unordered_multiset::equal_range Method"/>(_Keyval).</para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or <see cref="concurrent_unordered_multiset::end Method"/>() if no such element exists. The third member function returns the number + /// of elements it removes. + /// </returns> + /**/ + size_type unsafe_erase(const key_type& _Keyval) + { + return _Mybase::unsafe_erase(_Keyval); + } + + /// <summary> + /// Swaps the contents of two <c>concurrent_unordered_multiset</c> objects. This method is not concurrency-safe. + /// </summary> + /// <param name="_Uset"> + /// The <c>concurrent_unordered_multiset</c> object to swap with. + /// </param> + /**/ + void swap(concurrent_unordered_multiset& _Uset) + { + _Mybase::swap(_Uset); + } + + /// <summary> + /// Returns the stored hash function object. + /// </summary> + /// <returns> + /// The stored hash function object. + /// </returns> + /**/ + hasher hash_function() const + { + return _M_comparator._M_hash_object; + } + + /// <summary> + /// The stored equality comparison function object. + /// </summary> + /// <returns> + /// The stored equality comparison function object. + /// </returns> + /**/ + key_equal key_eq() const + { + return _M_comparator._M_key_compare_object; + } +}; +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma warning(pop) +#pragma pack(pop) diff --git a/test_data/lots_of_files/concurrent_vector.h b/test_data/lots_of_files/concurrent_vector.h new file mode 100644 index 0000000..228be95 --- /dev/null +++ b/test_data/lots_of_files/concurrent_vector.h @@ -0,0 +1,1965 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Microsoft would like to acknowledge that this concurrency data structure implementation +* is based on the Intel implementation of its Threading Building Blocks ("Intel Material"). +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* concurrent_vector.h +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +/* + Intel Material Copyright 2005-2008 Intel Corporation. All Rights Reserved. +*/ + +#pragma once + +#include <crtdefs.h> +#include <memory> +#include <iterator> +#include <limits> +#include <algorithm> +#include <cstring> +#include <crtdbg.h> +#include <concrt.h> + +#define _PPL_CONTAINER + +#if !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) + #error ERROR: Concurrency Runtime is supported only on X64, X86 and ARM architectures. +#endif /* !(defined (_M_X64) || defined (_M_IX86) || defined (_M_ARM)) */ + +#if defined (_M_CEE) + #error ERROR: Concurrency Runtime is not supported when compiling /clr. +#endif /* defined (_M_CEE) */ + +#pragma pack(push,_CRT_PACKING) +#pragma warning (push) +#pragma warning (disable: 4510 4512 4610) // disable warnings for compiler unable to generate constructor + +/// <summary> +/// The <c>Concurrency</c> namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ + +template<typename _Ty, class _Ax = std::allocator<_Ty> > +class concurrent_vector; + +namespace details +{ + + // Bad allocation marker. + #define _BAD_ALLOC_MARKER reinterpret_cast<void*>(63) + + // Base class of concurrent vector implementation. + class _Concurrent_vector_base_v4 + { + protected: + + // Basic types declarations. + typedef size_t _Segment_index_t; + typedef size_t _Size_type; + + // Size constants + static const _Segment_index_t _Default_initial_segments = 1; // 2 initial items + + // Number of slots for segment's pointers inside the class + static const _Segment_index_t _Pointers_per_short_table = 3; // to fit into 8 words of entire structure + static const _Segment_index_t _Pointers_per_long_table = sizeof(_Segment_index_t) * 8; // one segment per bit + + // Segment pointer. Can be zero-initialized. + struct _Segment_t + { + void* _My_array; + }; + + // Data fields + + // allocator function pointer + void* (__cdecl *_My_vector_allocator_ptr)(_Concurrent_vector_base_v4 &, size_t); + + // embedded storage of segment pointers + _Segment_t _My_storage[_Pointers_per_short_table]; + + // Methods + + _Concurrent_vector_base_v4() + { + _My_early_size = 0; + _My_first_block = 0; // here is not _Default_initial_segments + for( _Segment_index_t _I = 0; _I < _Pointers_per_short_table; _I++) + _My_storage[_I]._My_array = NULL; + _My_segment = _My_storage; + } + _CRTIMP2 ~_Concurrent_vector_base_v4(); + + _CRTIMP2 static _Segment_index_t __cdecl _Segment_index_of( _Size_type _Index ); + + static _Segment_index_t _Segment_base( _Segment_index_t _K ) + { + return (_Segment_index_t(1)<<_K & ~_Segment_index_t(1)); + } + + static _Segment_index_t _Segment_base_index_of( _Segment_index_t &_Index ) + { + _Segment_index_t _K = _Segment_index_of( _Index ); + _Index -= _Segment_base(_K); + return _K; + } + + static _Size_type _Segment_size( _Segment_index_t _K ) + { + return _Segment_index_t(1)<<_K; // fake value for _K==0 + } + + // An operation on an n-element array starting at begin. + typedef void (__cdecl *_My_internal_array_op1)(void* _Begin, _Size_type _N ); + + // An operation on n-element destination array and n-element source array. + typedef void (__cdecl *_My_internal_array_op2)(void* _Dst, const void* _Src, _Size_type _N ); + + // Internal structure for shrink_to_fit(). + struct _Internal_segments_table + { + _Segment_index_t _First_block; + void* _Table[_Pointers_per_long_table]; + }; + + _CRTIMP2 void _Internal_reserve( _Size_type _N, _Size_type _Element_size, _Size_type _Max_size ); + _CRTIMP2 _Size_type _Internal_capacity() const; + void _Internal_grow( _Size_type _Start, _Size_type _Finish, _Size_type _Element_size, _My_internal_array_op2 _Init, const void *_Src ); + _Size_type _Internal_grow_segment( const _Size_type _Start, _Size_type _Finish, _Size_type _Element_size, _Segment_t** _PPSegment, _Size_type* _PSegStart, _Size_type* _PSegFinish ); + _CRTIMP2 _Size_type _Internal_grow_by( _Size_type _Delta, _Size_type _Element_size, _My_internal_array_op2 _Init, const void *_Src ); + _CRTIMP2 void* _Internal_push_back( _Size_type _Element_size, _Size_type& _Index ); + _CRTIMP2 _Segment_index_t _Internal_clear( _My_internal_array_op1 _Destroy ); + void _Internal_truncate( _Size_type _Old_size, _Size_type _New_size, _Size_type _Element_size, _My_internal_array_op1 _Destroy); + _CRTIMP2 void* _Internal_compact( _Size_type _Element_size, void *_Table, _My_internal_array_op1 _Destroy, _My_internal_array_op2 _Copy ); + _CRTIMP2 void _Internal_copy( const _Concurrent_vector_base_v4& _Src, _Size_type _Element_size, _My_internal_array_op2 _Copy ); + _CRTIMP2 void _Internal_assign( const _Concurrent_vector_base_v4& _Src, _Size_type _Element_size, + _My_internal_array_op1 _Destroy, _My_internal_array_op2 _Assign, _My_internal_array_op2 _Copy ); + _CRTIMP2 void _Internal_throw_exception(_Size_type) const; + _CRTIMP2 void _Internal_swap(_Concurrent_vector_base_v4&); + + _CRTIMP2 void _Internal_resize( _Size_type _New_size, _Size_type _Element_size, _Size_type _Max_size, _My_internal_array_op1 _Destroy, _My_internal_array_op2 _Init, const void* _Src); + _CRTIMP2 _Size_type _Internal_grow_to_at_least_with_result( _Size_type _New_size, _Size_type _Element_size, _My_internal_array_op2 _Init, const void *_Src ); + + // Count of segments in the first block. + _Subatomic<_Size_type> _My_first_block; + + // Requested size of vector. + _Subatomic<_Size_type> _My_early_size; + + // Pointer to the segments table. + _Subatomic<_Segment_t*> _My_segment; + + + private: + // Private functionality. + class _Helper; + friend class _Helper; + }; + + typedef _Concurrent_vector_base_v4 _Concurrent_vector_base; + + // Meets requirements of a forward iterator for STL.*/ + /** _Value is either the _Ty or const _Ty type of the container. */ + template<typename _Container, typename _Value> + class _Vector_iterator + { + // concurrent_vector over which we are iterating. + _Container* _My_vector; + + // Index into the vector. + size_t _My_index; + + // Caches _My_vector->_Internal_subscript(_My_index) + /** NULL if cached value is not available */ + mutable _Value* _My_item; + + template<typename _C, typename _Ty> + friend _Vector_iterator<_C,_Ty> operator+( ptrdiff_t _Offset, const _Vector_iterator<_C,_Ty>& _Vec ); + + template<typename _C, typename _Ty, typename _U> + friend bool operator==( const _Vector_iterator<_C,_Ty>&, const _Vector_iterator<_C,_U>& ); + + template<typename _C, typename _Ty, typename _U> + friend bool operator<( const _Vector_iterator<_C,_Ty>&, const _Vector_iterator<_C,_U>& ); + + template<typename _C, typename _Ty, typename _U> + friend ptrdiff_t operator-( const _Vector_iterator<_C,_Ty>&, const _Vector_iterator<_C,_U>& ); + + template<typename _C, typename _U> + friend class ::Concurrency::details::_Vector_iterator; + + template<typename _Ty, class _Ax> + friend class ::Concurrency::concurrent_vector; + + _Vector_iterator( const _Container& _Vec, size_t _Index, void* _Ptr = NULL ) + : _My_vector(const_cast<_Container*>(&_Vec)), + _My_index(_Index), + _My_item(static_cast<_Value*>(_Ptr)) + { + } + + public: + // Default constructor + _Vector_iterator() + : _My_vector(NULL), _My_index(~size_t(0)), _My_item(NULL) + { + } + + _Vector_iterator( const _Vector_iterator<_Container,typename _Container::value_type>& _Other ) + : _My_vector(_Other._My_vector), + _My_index(_Other._My_index), + _My_item(_Other._My_item) + { + } + + _Vector_iterator operator+( ptrdiff_t _Offset ) const + { + return _Vector_iterator( *_My_vector, _My_index+_Offset ); + } + _Vector_iterator& operator+=( ptrdiff_t _Offset ) + { + _My_index+=_Offset; + _My_item = NULL; + return *this; + } + _Vector_iterator operator-( ptrdiff_t _Offset ) const + { + return _Vector_iterator( *_My_vector, _My_index-_Offset ); + } + _Vector_iterator& operator-=( ptrdiff_t _Offset ) + { + _My_index-=_Offset; + _My_item = NULL; + return *this; + } + _Value& operator*() const + { + _Value* _Item = _My_item; + if( !_Item ) + _Item = _My_item = &_My_vector->_Internal_subscript(_My_index); + _CONCRT_ASSERT( _Item==&_My_vector->_Internal_subscript(_My_index)); // corrupt cache + return *_Item; + } + _Value& operator[]( ptrdiff_t _K ) const + { + return _My_vector->_Internal_subscript(_My_index+_K); + } + _Value* operator->() const + { + return &operator*(); + } + + // Pre increment + _Vector_iterator& operator++() + { + size_t _K = ++_My_index; + if( _My_item ) + { + // Following test uses 2's-complement wizardry. + if( (_K& (_K-2))==0 ) + { + // _K is a power of two that is at least _K-2. + _My_item= NULL; + } + else + { + ++_My_item; + } + } + return *this; + } + + // Pre decrement + _Vector_iterator& operator--() + { + _CONCRT_ASSERT( _My_index>0 ); // operator--() applied to iterator already at beginning of concurrent_vector. + size_t _K = _My_index--; + if( _My_item ) + { + // Following test uses 2's-complement wizardry. + if( (_K& (_K-2))==0 ) + { + // k is a power of two that is at least k-2. + _My_item= NULL; + } + else + { + --_My_item; + } + } + return *this; + } + + // Post increment + _Vector_iterator operator++(int) + { + _Vector_iterator _Result = *this; + operator++(); + return _Result; + } + + // Post decrement + _Vector_iterator operator--(int) + { + _Vector_iterator _Result = *this; + operator--(); + return _Result; + } + + // STL support + + typedef ptrdiff_t difference_type; + typedef _Value value_type; + typedef _Value* pointer; + typedef _Value& reference; + typedef std::random_access_iterator_tag iterator_category; + }; + + template<typename _Container, typename _Value> + struct std::_Is_checked_helper<_Vector_iterator<_Container, _Value> > + : public true_type + { // mark _Vector_iterator as checked. This supresses warning C4996 + }; + + template<typename _Container, typename _Ty> + _Vector_iterator<_Container,_Ty> operator+( ptrdiff_t _Offset, const _Vector_iterator<_Container,_Ty>& _Vec ) + { + return _Vector_iterator<_Container,_Ty>( *_Vec._My_vector, _Vec._My_index+_Offset ); + } + + template<typename _Container, typename _Ty, typename _U> + bool operator==( const _Vector_iterator<_Container,_Ty>& _I, const _Vector_iterator<_Container,_U>& _J ) + { + return _I._My_index==_J._My_index && _I._My_vector == _J._My_vector; + } + + template<typename _Container, typename _Ty, typename _U> + bool operator!=( const _Vector_iterator<_Container,_Ty>& _I, const _Vector_iterator<_Container,_U>& _J ) + { + return !(_I==_J); + } + + template<typename _Container, typename _Ty, typename _U> + bool operator<( const _Vector_iterator<_Container,_Ty>& _I, const _Vector_iterator<_Container,_U>& _J ) + { + return _I._My_index<_J._My_index && _I._My_vector == _J._My_vector; + } + + template<typename _Container, typename _Ty, typename _U> + bool operator>( const _Vector_iterator<_Container,_Ty>& _I, const _Vector_iterator<_Container,_U>& _J ) + { + return _J<_I; + } + + template<typename _Container, typename _Ty, typename _U> + bool operator>=( const _Vector_iterator<_Container,_Ty>& _I, const _Vector_iterator<_Container,_U>& _J ) + { + return !(_I<_J); + } + + template<typename _Container, typename _Ty, typename _U> + bool operator<=( const _Vector_iterator<_Container,_Ty>& _I, const _Vector_iterator<_Container,_U>& _J ) + { + return !(_J<_I); + } + + template<typename _Container, typename _Ty, typename _U> + ptrdiff_t operator-( const _Vector_iterator<_Container,_Ty>& _I, const _Vector_iterator<_Container,_U>& _J ) + { + return ptrdiff_t(_I._My_index)-ptrdiff_t(_J._My_index); + } + + template<typename _Ty, class _Ax> + class _Allocator_base + { + public: + typedef typename _Ax::template + rebind<_Ty>::other _Allocator_type; + _Allocator_type _My_allocator; + + _Allocator_base(const _Allocator_type &_Al = _Allocator_type() ) + : _My_allocator(_Al) + { + } + }; + +} // namespace details + +/// <summary> +/// The <c>concurrent_vector</c> class is a sequence container class that allows random access to any element. +/// It enables concurrency-safe append, element access, iterator access, and iterator traversal operations. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements to be stored in the vector. +/// </typeparam> +/// <typeparam name="_Ax"> +/// The type that represents the stored allocator object that encapsulates details about the allocation and +/// deallocation of memory for the concurrent vector. This argument is optional and the default value is +/// <c>allocator<</c><typeparamref name="_Ty"/><c>></c>. +/// </typeparam> +/// <remarks> +/// For detailed information on the <c>concurrent_vector</c> class, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class _Ax> +class concurrent_vector: protected details::_Allocator_base<_Ty, _Ax>, + private details::_Concurrent_vector_base_v4 +{ +private: + typedef concurrent_vector<_Ty, _Ax> _Myt; + + template<typename _C, typename _U> + friend class details::_Vector_iterator; + +public: + + /// <summary> + /// A type that counts the number of elements in a concurrent vector. + /// </summary> + /**/ + typedef details::_Concurrent_vector_base_v4::_Size_type size_type; + + /// <summary> + /// A type that represents the allocator class for the concurrent vector. + /// </summary> + /**/ + typedef typename details::_Allocator_base<_Ty, _Ax>::_Allocator_type allocator_type; + + /// <summary> + /// A type that represents the data type stored in a concurrent vector. + /// </summary> + /**/ + typedef _Ty value_type; + + /// <summary> + /// A type that provides the signed distance between two elements in a concurrent vector. + /// </summary> + /**/ + typedef ptrdiff_t difference_type; + + /// <summary> + /// A type that provides a reference to an element stored in a concurrent vector. + /// </summary> + /**/ + typedef _Ty& reference; + + /// <summary> + /// A type that provides a reference to a <c>const</c> element stored in a concurrent vector for reading and + /// performing <c>const</c> operations. + /// </summary> + /**/ + typedef const _Ty& const_reference; + + /// <summary> + /// A type that provides a pointer to an element in a concurrent vector. + /// </summary> + /**/ + typedef _Ty *pointer; + + /// <summary> + /// A type that provides a pointer to a <c>const</c> element in a concurrent vector. + /// </summary> + /**/ + typedef const _Ty *const_pointer; + + /// <summary> + /// A type that provides a random-access iterator that can read any element in a concurrent vector. Modification of an + /// element using the iterator is not concurrency-safe. + /// </summary> + /**/ + typedef details::_Vector_iterator<concurrent_vector,_Ty> iterator; + + /// <summary> + /// A type that provides a random-access iterator that can read a <c>const</c> element in a concurrent vector. + /// </summary> + /**/ + typedef details::_Vector_iterator<concurrent_vector,const _Ty> const_iterator; + + /// <summary> + /// A type that provides a random-access iterator that can read any element in a reversed concurrent vector. Modification of an + /// element using the iterator is not concurrency-safe. + /// </summary> + /**/ + typedef std::reverse_iterator<iterator> reverse_iterator; + + /// <summary> + /// A type that provides a random-access iterator that can read any <c>const</c> element in the concurrent vector. + /// </summary> + /**/ + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + /// <summary> + /// Constructs a concurrent vector. + /// </summary> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the vector. + /// <para>The first constructor specify an empty initial vector and explicitly specifies the allocator type. + /// to be used.</para> + /// <para>The second and third constructors specify a copy of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fourth constructor specifies a move of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fifth constructor specifies a repetition of a specified number (<paramref name="_N"/>) of elements of the default + /// value for class <typeparamref name="_Ty"/>.</para> + /// <para>The sixth constructor specifies a repetition of (<paramref name="_N"/>) elements of value <paramref name="_Item"/>.</para> + /// <para>The last constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + explicit concurrent_vector(const allocator_type &_Al = allocator_type()) + : details::_Allocator_base<_Ty, _Ax>(_Al) + { + _My_vector_allocator_ptr = &_Internal_allocator; + } + + /// <summary> + /// Constructs a concurrent vector. + /// </summary> + /// <param name="_Vector"> + /// The source <c>concurrent_vector</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the vector. + /// <para>The first constructor specify an empty initial vector and explicitly specifies the allocator type. + /// to be used.</para> + /// <para>The second and third constructors specify a copy of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fourth constructor specifies a move of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fifth constructor specifies a repetition of a specified number (<paramref name="_N"/>) of elements of the default + /// value for class <typeparamref name="_Ty"/>.</para> + /// <para>The sixth constructor specifies a repetition of (<paramref name="_N"/>) elements of value <paramref name="_Item"/>.</para> + /// <para>The last constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + concurrent_vector( const concurrent_vector& _Vector) + : details::_Allocator_base<_Ty, _Ax>(_Vector.get_allocator()) + { + _My_vector_allocator_ptr = &_Internal_allocator; + _Internal_copy(_Vector, sizeof(_Ty), &_Copy_array); + } + + /// <summary> + /// Constructs a concurrent vector. + /// </summary> + /// <typeparam name="M"> + /// The allocator type of the source vector. + /// </typeparam> + /// <param name="_Vector"> + /// The source <c>concurrent_vector</c> object to copy or move elements from. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the vector. + /// <para>The first constructor specify an empty initial vector and explicitly specifies the allocator type. + /// to be used.</para> + /// <para>The second and third constructors specify a copy of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fourth constructor specifies a move of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fifth constructor specifies a repetition of a specified number (<paramref name="_N"/>) of elements of the default + /// value for class <typeparamref name="_Ty"/>.</para> + /// <para>The sixth constructor specifies a repetition of (<paramref name="_N"/>) elements of value <paramref name="_Item"/>.</para> + /// <para>The last constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + template<class M> + concurrent_vector( const concurrent_vector<_Ty, M>& _Vector, const allocator_type& _Al = allocator_type() ) + : details::_Allocator_base<_Ty, _Ax>(_Al) + { + _My_vector_allocator_ptr = &_Internal_allocator; + _Internal_copy(_Vector._Internal_vector_base(), sizeof(_Ty), &_Copy_array); + } + + + /// <summary> + /// Constructs a concurrent vector. + /// </summary> + /// <param name="_Vector"> + /// The source <c>concurrent_vector</c> object to copy or move elements from. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the vector. + /// <para>The first constructor specify an empty initial vector and explicitly specifies the allocator type. + /// to be used.</para> + /// <para>The second and third constructors specify a copy of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fourth constructor specifies a move of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fifth constructor specifies a repetition of a specified number (<paramref name="_N"/>) of elements of the default + /// value for class <typeparamref name="_Ty"/>.</para> + /// <para>The sixth constructor specifies a repetition of (<paramref name="_N"/>) elements of value <paramref name="_Item"/>.</para> + /// <para>The last constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + concurrent_vector( concurrent_vector && _Vector) + : details::_Allocator_base<_Ty, _Ax>(_Vector.get_allocator()) + { + _My_vector_allocator_ptr = &_Internal_allocator; + _Concurrent_vector_base_v4::_Internal_swap(_Vector._Internal_vector_base()); + } + + /// <summary> + /// Constructs a concurrent vector. + /// </summary> + /// <param name="_N"> + /// The initial size of the <c>concurrent_vector</c> object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the vector. + /// <para>The first constructor specify an empty initial vector and explicitly specifies the allocator type. + /// to be used.</para> + /// <para>The second and third constructors specify a copy of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fourth constructor specifies a move of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fifth constructor specifies a repetition of a specified number (<paramref name="_N"/>) of elements of the default + /// value for class <typeparamref name="_Ty"/>.</para> + /// <para>The sixth constructor specifies a repetition of (<paramref name="_N"/>) elements of value <paramref name="_Item"/>.</para> + /// <para>The last constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + explicit concurrent_vector(size_type _N) + { + _My_vector_allocator_ptr = &_Internal_allocator; + if ( !_N ) return; + _Internal_reserve(_N, sizeof(_Ty), max_size()); _My_early_size = _N; + _CONCRT_ASSERT( _My_first_block == _Segment_index_of(_N-1)+1 ); + _Initialize_array(static_cast<_Ty*>(_My_segment[0]._My_array), NULL, _N); + } + + /// <summary> + /// Constructs a concurrent vector. + /// </summary> + /// <param name="_N"> + /// The initial capacity of the <c>concurrent_vector</c> object. + /// </param> + /// <param name="_Item"> + /// The value of elements in the constructed object. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the vector. + /// <para>The first constructor specify an empty initial vector and explicitly specifies the allocator type. + /// to be used.</para> + /// <para>The second and third constructors specify a copy of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fourth constructor specifies a move of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fifth constructor specifies a repetition of a specified number (<paramref name="_N"/>) of elements of the default + /// value for class <typeparamref name="_Ty"/>.</para> + /// <para>The sixth constructor specifies a repetition of (<paramref name="_N"/>) elements of value <paramref name="_Item"/>.</para> + /// <para>The last constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + concurrent_vector(size_type _N, const_reference _Item, const allocator_type& _Al = allocator_type()) + : details::_Allocator_base<_Ty, _Ax>(_Al) + { + _My_vector_allocator_ptr = &_Internal_allocator; + _Internal_assign( _N, _Item ); + } + + /// <summary> + /// Constructs a concurrent vector. + /// </summary> + /// <typeparam name="_InputIterator"> + /// The type of the input iterator. + /// </typeparam> + /// <param name="_Begin"> + /// Position of the first element in the range of elements to be copied. + /// </param> + /// <param name="_End"> + /// Position of the first element beyond the range of elements to be copied. + /// </param> + /// <param name="_Al"> + /// The allocator class to use with this object. + /// </param> + /// <remarks> + /// All constructors store an allocator object <paramref name="_Al"/> and initialize the vector. + /// <para>The first constructor specify an empty initial vector and explicitly specifies the allocator type. + /// to be used.</para> + /// <para>The second and third constructors specify a copy of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fourth constructor specifies a move of the concurrent vector <paramref name="_Vector"/>.</para> + /// <para>The fifth constructor specifies a repetition of a specified number (<paramref name="_N"/>) of elements of the default + /// value for class <typeparamref name="_Ty"/>.</para> + /// <para>The sixth constructor specifies a repetition of (<paramref name="_N"/>) elements of value <paramref name="_Item"/>.</para> + /// <para>The last constructor specifies values supplied by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>).</para> + /// </remarks> + /**/ + template<class _InputIterator> + concurrent_vector(_InputIterator _Begin, _InputIterator _End, const allocator_type &_Al = allocator_type()) + : details::_Allocator_base<_Ty, _Ax>(_Al) + { + _My_vector_allocator_ptr = &_Internal_allocator; + _Internal_assign(_Begin, _End, static_cast<_Is_integer_tag<std::numeric_limits<_InputIterator>::is_integer> *>(0) ); + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_vector</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Vector"> + /// The source <c>concurrent_vector</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_vector</c> object. + /// </returns> + /**/ + concurrent_vector& operator=( const concurrent_vector& _Vector ) + { + if( this != &_Vector ) + _Concurrent_vector_base_v4::_Internal_assign(_Vector, sizeof(_Ty), &_Destroy_array, &_Assign_array, &_Copy_array); + return *this; + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_vector</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <typeparam name="M"> + /// The allocator type of the source vector. + /// </typeparam> + /// <param name="_Vector"> + /// The source <c>concurrent_vector</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_vector</c> object. + /// </returns> + /**/ + template<class M> + concurrent_vector& operator=( const concurrent_vector<_Ty, M>& _Vector ) + { + if( static_cast<void*>( this ) != static_cast<const void*>( &_Vector ) ) + { + _Concurrent_vector_base_v4::_Internal_assign(_Vector._Internal_vector_base(), + sizeof(_Ty), &_Destroy_array, &_Assign_array, &_Copy_array); + } + return *this; + } + + /// <summary> + /// Assigns the contents of another <c>concurrent_vector</c> object to this one. This method is not concurrency-safe. + /// </summary> + /// <param name="_Vector"> + /// The source <c>concurrent_vector</c> object. + /// </param> + /// <returns> + /// A reference to this <c>concurrent_vector</c> object. + /// </returns> + /**/ + concurrent_vector& operator=( concurrent_vector && _Vector ) + { + if( static_cast<void*>( this ) != static_cast<const void*>( &_Vector ) ) + { + _Concurrent_vector_base_v4::_Internal_swap(_Vector._Internal_vector_base()); + _Vector.clear(); + } + return *this; + } + + /// <summary> + /// Grows this concurrent vector by <paramref name="_Delta"/> elements. This method is concurrency-safe. + /// </summary> + /// <param name="_Delta"> + /// The number of elements to append to the object. + /// </param> + /// <returns> + /// An iterator to first item appended. + /// </returns> + /// <remarks> + /// If <paramref name="_Item"/> is not specified, the new elements are default constructed. + /// </remarks> + /**/ + iterator grow_by( size_type _Delta ) + { + return iterator(*this, _Delta ? _Internal_grow_by( _Delta, sizeof(_Ty), &_Initialize_array, NULL ) : _My_early_size); + } + + /// <summary> + /// Grows this concurrent vector by <paramref name="_Delta"/> elements. This method is concurrency-safe. + /// </summary> + /// <param name="_Delta"> + /// The number of elements to append to the object. + /// </param> + /// <param name="_Item"> + /// The value to initialize the new elements with. + /// </param> + /// <returns> + /// An iterator to first item appended. + /// </returns> + /// <remarks> + /// If <paramref name="_Item"/> is not specified, the new elements are default constructed. + /// </remarks> + /**/ + iterator grow_by( size_type _Delta, const_reference _Item ) + { + return iterator(*this, _Delta ? _Internal_grow_by( _Delta, sizeof(_Ty), &_Initialize_array_by, static_cast<const void*>(&_Item) ) : _My_early_size); + } + + /// <summary> + /// Grows this concurrent vector until it has at least <paramref name="_N"/> elements. This method is concurrency-safe. + /// </summary> + /// <param name="_N"> + /// The new minimum size for the <c>concurrent_vector</c> object. + /// </param> + /// <returns> + /// An iterator that points to beginning of appended sequence, or to the element at index <paramref name="_N"/> if no + /// elements were appended. + /// </returns> + /**/ + iterator grow_to_at_least( size_type _N ) + { + size_type _M = 0; + if( _N ) + { + _M = _Internal_grow_to_at_least_with_result( _N, sizeof(_Ty), &_Initialize_array, NULL ); + if( _M > _N ) + _M = _N; + } + return iterator(*this, _M); + }; + + /// <summary> + /// Appends the given item to the end of the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <param name="_Item"> + /// The value to be appended. + /// </param> + /// <returns> + /// An iterator to item appended. + /// </returns> + /**/ + iterator push_back( const_reference _Item ) + { + size_type _K; + void *_Ptr = _Internal_push_back(sizeof(_Ty), _K); + _Internal_loop_guide _Loop(1, _Ptr); + _Loop._Init(&_Item); + return iterator(*this, _K, _Ptr); + } + + /// <summary> + /// Appends the given item to the end of the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <param name="_Item"> + /// The value to be appended. + /// </param> + /// <returns> + /// An iterator to item appended. + /// </returns> + /**/ + iterator push_back( _Ty &&_Item ) + { + size_type _K; + void *_Ptr = _Internal_push_back(sizeof(_Ty), _K); + new (_Ptr) _Ty( std::move(_Item)); + return iterator(*this, _K, _Ptr); + } + + /// <summary> + /// Provides access to the element at the given index in the concurrent vector. This method is concurrency-safe for read operations, + /// and also while growing the vector, as long as the you have ensured that the value <paramref name="_Index"/> is less than + /// the size of the concurrent vector. + /// </summary> + /// <param name="_Index"> + /// The index of the element to be retrieved. + /// </param> + /// <returns> + /// A reference to the item at the given index. + /// </returns> + /// <remarks> + /// The version of <c>operator []</c> that returns a non-<c>const</c> reference cannot be used to concurrently write to the element + /// from different threads. A different synchronization object should be used to synchronize concurrent read and write operations + /// to the same data element. + /// <para>No bounds checking is performed to ensure that <paramref name="_Index"/> is a valid index into the concurrent vector.</para> + /// </remarks> + /**/ + reference operator[]( size_type _Index ) + { + return _Internal_subscript(_Index); + } + + /// <summary> + /// Provides read access to element at the given index in the concurrent vector. This method is concurrency-safe for read operations, + /// and also while growing the vector, as long as the you have ensured that the value <paramref name="_Index"/> is less than + /// the size of the concurrent vector. + /// </summary> + /// <param name="_Index"> + /// The index of the element to be retrieved. + /// </param> + /// <returns> + /// A <c>const</c> reference to the item at the given index. + /// </returns> + /// <remarks> + /// The version of <c>operator []</c> that returns a non-<c>const</c> reference cannot be used to concurrently write to the element + /// from different threads. A different synchronization object should be used to synchronize concurrent read and write operations + /// to the same data element. + /// <para>No bounds checking is performed to ensure that <paramref name="_Index"/> is a valid index into the concurrent vector.</para> + /// </remarks> + /**/ + const_reference operator[]( size_type _Index ) const + { + return _Internal_subscript(_Index); + } + + /// <summary> + /// Provides access to the element at the given index in the concurrent vector. This method is concurrency-safe for read operations, + /// and also while growing the vector, as long as you have ensured that the value <paramref name="_Index"/> is less than + /// the size of the concurrent vector. + /// </summary> + /// <param name="_Index"> + /// The index of the element to be retrieved. + /// </param> + /// <returns> + /// A reference to the item at the given index. + /// </returns> + /// <remarks> + /// The version of the function <c>at</c> that returns a non-<c>const</c> reference cannot be used to concurrently write to the element + /// from different threads. A different synchronization object should be used to synchronize concurrent read and write operations + /// to the same data element. + /// <para>The method throws <c>out_of_range</c> if <paramref name="_Index"/> is greater than or equal to the size of the concurrent vector, + /// and <c>range_error</c> if the index is for a broken portion of the vector. For details on how a vector can become broken, + /// see <see cref="Parallel Containers and Objects"/>.</para> + /// </remarks> + /**/ + reference at( size_type _Index ) + { + return _Internal_subscript_with_exceptions(_Index); + } + + /// <summary> + /// Provides access to the element at the given index in the concurrent vector. This method is concurrency-safe for read operations, + /// and also while growing the vector, as long as you have ensured that the value <paramref name="_Index"/> is less than + /// the size of the concurrent vector. + /// </summary> + /// <param name="_Index"> + /// The index of the element to be retrieved. + /// </param> + /// <returns> + /// A <c>const</c> reference to the item at the given index. + /// </returns> + /// <remarks> + /// The version of the function <c>at</c> that returns a non-<c>const</c> reference cannot be used to concurrently write to the element + /// from different threads. A different synchronization object should be used to synchronize concurrent read and write operations + /// to the same data element. + /// <para>The method throws <c>out_of_range</c> if <paramref name="_Index"/> is greater than or equal to the size of the concurrent vector, + /// and <c>range_error</c> if the index is for a broken portion of the vector. For details on how a vector can become broken, + /// see <see cref="Parallel Containers and Objects"/>.</para> + /// </remarks> + /**/ + const_reference at( size_type _Index ) const + { + return _Internal_subscript_with_exceptions(_Index); + } + + /// <summary> + /// Returns the number of elements in the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// The number of elements in this <c>concurrent_vector</c> object. + /// </returns> + /// <remarks> + /// The returned size is guaranteed to include all elements appended by calls to the function <c>push_back</c>, + /// or grow operations that have completed prior to invoking this method. However, it may also include elements + /// that are allocated but still under construction by concurrent calls to any of the growth methods. + /// </remarks> + /**/ + size_type size() const + { + size_type _Sz = _My_early_size; + size_type _Cp = _Internal_capacity(); + return _Cp < _Sz ? _Cp : _Sz; + } + + /// <summary> + /// Tests if the concurrent vector is empty at the time this method is called. This method is concurrency-safe. + /// </summary> + /// <returns> + /// <c>true</c> if the vector was empty at the moment the function was called, <c>false</c> otherwise. + /// </returns> + /**/ + bool empty() const + { + return !_My_early_size; + } + + /// <summary> + /// Returns the maximum size to which the concurrent vector can grow without having to allocate more memory. + /// This method is concurrency-safe. + /// </summary> + /// <returns> + /// The maximum size to which the concurrent vector can grow without having to allocate more memory. + /// </returns> + /// <remarks> + /// Unlike an STL <c>vector</c>, a <c>concurrent_vector</c> object does not move existing elements if it allocates more memory. + /// </remarks> + /**/ + size_type capacity() const + { + return _Internal_capacity(); + } + + /// <summary> + /// Allocates enough space to grow the concurrent vector to size <paramref name="_N"/> without having to allocate more memory later. + /// This method is not concurrency-safe. + /// </summary> + /// <param name="_N"> + /// The number of elements to reserve space for. + /// </param> + /// <remarks> + /// <c>reserve</c> is not concurrency-safe. You must ensure that no other threads are invoking methods + /// on the concurrent vector when you call this method. The capacity of the concurrent vector after the method returns + /// may be bigger than the requested reservation. + /// </remarks> + /**/ + void reserve( size_type _N ) + { + if( _N ) + _Internal_reserve(_N, sizeof(_Ty), max_size()); + } + + /// <summary> + /// Compacts the internal representation of the concurrent vector to reduce fragmentation and optimize memory usage. + /// This method is not concurrency-safe. + /// </summary> + /// <remarks> + /// This method will internally re-allocate memory move elements around, invalidating all the iterators. + /// <c>shrink_to_fit</c> is not concurrency-safe. You must ensure that no other threads are invoking methods + /// on the concurrent vector when you call this function. + /// </remarks> + /**/ + void shrink_to_fit(); + + /// <summary> + /// Changes the size of the concurrent vector to the requested size, deleting or adding elements as + /// necessary. This method is not concurrency-safe. + /// </summary> + /// <param name="_N"> + /// The new size of the concurrent vector. + /// </param> + /// <remarks> + /// If the size of the container is less than the requested size, elements are added to the vector until it reaches the + /// requested size. If the size of the container is larger than the requested size, the elements closest to the end of the container + /// are deleted until the container reaches the size <paramref name="_N"/>. If the present size of the container is the same as the requested + /// size, no action is taken. + /// <para><c>resize</c> is not concurrency safe. You must ensure that no other threads are invoking methods + /// on the concurrent vector when you call this method.</para> + /// </remarks> + /**/ + void resize(size_type _N) + { + _Internal_resize( _N, sizeof(_Ty), max_size(), _Destroy_array, _Initialize_array, NULL); + } + + /// <summary> + /// Changes the size of the concurrent vector to the requested size, deleting or adding elements as + /// necessary. This method is not concurrency-safe. + /// </summary> + /// <param name="_N"> + /// The new size of the concurrent_vector. + /// </param> + /// <param name="_Val"> + /// The value of new elements added to the vector if the new size is larger than the original size. If the value is omitted, + /// the new objects are assigned the default value for their type. + /// </param> + /// <remarks> + /// If the size of the container is less than the requested size, elements are added to the vector until it reaches the + /// requested size. If the size of the container is larger than the requested size, the elements closest to the end of the container + /// are deleted until the container reaches the size <paramref name="_N"/>. If the present size of the container is the same as the requested + /// size, no action is taken. + /// <para><c>resize</c> is not concurrency safe. You must ensure that no other threads are invoking methods + /// on the concurrent vector when you call this method.</para> + /// </remarks> + /**/ + void resize(size_type _N, const _Ty& _Val) + { + _Internal_resize( _N, sizeof(_Ty), max_size(), _Destroy_array, _Initialize_array_by, static_cast<const void*>(&_Val) ); + } + + /// <summary> + /// Returns the maximum number of elements the concurrent vector can hold. This method is concurrency-safe. + /// </summary> + /// <returns> + /// The maximum number of elements the <c>concurrent_vector</c> object can hold. + /// </returns> + /**/ + size_type max_size() const + { + return (~size_type(0))/sizeof(_Ty); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the beginning of + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the beginning of + /// the concurrent vector. + /// </returns> + /**/ + iterator begin() + { + return iterator(*this,0); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the end of + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the end of + /// the concurrent vector. + /// </returns> + /**/ + iterator end() + { + return iterator(*this,size()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the beginning of + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the beginning of + /// the concurrent vector. + /// </returns> + /**/ + const_iterator begin() const + { + return const_iterator(*this,0); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the end of + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="iterator"/> or <typeparamref name="const_iterator"/> to the end of + /// the concurrent vector. + /// </returns> + /**/ + const_iterator end() const + { + return const_iterator(*this,size()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the beginning of + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the beginning of + /// the concurrent vector. + /// </returns> + /**/ + reverse_iterator rbegin() + { + return reverse_iterator(end()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the end of + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the end of + /// the concurrent vector. + /// </returns> + /**/ + reverse_iterator rend() + { + return reverse_iterator(begin()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the beginning + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the beginning of + /// the concurrent vector. + /// </returns> + /**/ + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(end()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the end of + /// the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="reverse_iterator"/> or <typeparamref name="const_reverse_iterator"/> to the end of + /// the concurrent vector. + /// </returns> + /**/ + const_reverse_iterator rend() const + { + return const_reverse_iterator(begin()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="const_iterator"/> to the beginning of the concurrent vector. + /// This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="const_iterator"/> to the beginning of the concurrent vector. + /// </returns> + /**/ + const_iterator cbegin() const + { + return (((const _Myt *)this)->begin()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="const_iterator"/> to the end of the concurrent vector. + /// This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="const_iterator"/> to the end of the concurrent vector. + /// </returns> + /**/ + const_iterator cend() const + { + return (((const _Myt *)this)->end()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="const_reverse_iterator"/> to the beginning of the concurrent vector. + /// This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="const_reverse_iterator"/> to the beginning of the concurrent vector. + /// </returns> + /**/ + const_reverse_iterator crbegin() const + { + return (((const _Myt *)this)->rbegin()); + } + + /// <summary> + /// Returns an iterator of type <typeparamref name="const_reverse_iterator"/> to the end of the concurrent vector. + /// This method is concurrency-safe. + /// </summary> + /// <returns> + /// An iterator of type <typeparamref name="const_reverse_iterator"/> to the end of the concurrent vector. + /// </returns> + /**/ + const_reverse_iterator crend() const + { + return (((const _Myt *)this)->rend()); + } + + /// <summary> + /// Returns a reference or a <c>const</c> reference to the first element in the concurrent vector. If the + /// concurrent vector is empty, the return value is undefined. This method is concurrency-safe. + /// </summary> + /// <returns> + /// A reference or a <c>const</c> reference to the first element in the concurrent vector. + /// </returns> + /**/ + reference front() + { + _CONCRT_ASSERT( size()>0 ); + return static_cast<_Ty*>(_My_segment[0]._My_array)[0]; + } + + /// <summary> + /// Returns a reference or a <c>const</c> reference to the first element in the concurrent vector. If the + /// concurrent vector is empty, the return value is undefined. This method is concurrency-safe. + /// </summary> + /// <returns> + /// A reference or a <c>const</c> reference to the first element in the <c>concurrent_vector</c> object. + /// </returns> + /**/ + const_reference front() const + { + _CONCRT_ASSERT( size()>0 ); + return static_cast<_Ty*>(_My_segment[0]._My_array)[0]; + } + + /// <summary> + /// Returns a reference or a <c>const</c> reference to the last element in the concurrent vector. If the + /// concurrent vector is empty, the return value is undefined. This method is concurrency-safe. + /// </summary> + /// <returns> + /// A reference or a <c>const</c> reference to the last element in the concurrent vector. + /// </returns> + /**/ + reference back() + { + _Size_type sz = size(); + _CONCRT_ASSERT( sz > 0 ); + return _Internal_subscript( sz-1 ); + } + + /// <summary> + /// Returns a reference or a <c>const</c> reference to the last element in the concurrent_vector. If the + /// concurrent vector is empty, the return value is undefined. This method is concurrency-safe. + /// </summary> + /// <returns> + /// A reference or a <c>const</c> reference to the last element in the concurrent vector. + /// </returns> + /**/ + const_reference back() const + { + _Size_type sz = size(); + _CONCRT_ASSERT( sz > 0 ); + return _Internal_subscript( sz-1 ); + } + + /// <summary> + /// Returns a copy of the allocator used to construct the concurrent vector. This method is concurrency-safe. + /// </summary> + /// <returns> + /// A copy of the allocator used to construct the <c>concurrent_vector</c> object. + /// </returns> + /**/ + allocator_type get_allocator() const + { + return this->_My_allocator; + } + + /// <summary> + /// Erases the elements of the concurrent vector and assigns to it either <paramref name="_N"/> copies of + /// <paramref name="_Item"/>, or values specified by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// This method is not concurrency-safe. + /// </summary> + /// <param name="_N"> + /// The number of items to copy into the concurrent vector. + /// </param> + /// <param name="_Item"> + /// Reference to a value used to fill the concurrent vector. + /// </param> + /// <remarks> + /// <c>assign</c> is not concurrency-safe. You must ensure that no other threads are invoking methods + /// on the concurrent vector when you call this method. + /// </remarks> + /**/ + void assign(size_type _N, const_reference _Item) + { + clear(); + _Internal_assign( _N, _Item ); + } + + /// <summary> + /// Erases the elements of the concurrent vector and assigns to it either <paramref name="_N"/> copies of <paramref name="_Item"/>, + /// or values specified by the iterator range [<paramref name="_Begin"/>, <paramref name="_End"/>). + /// This method is not concurrency-safe. + /// </summary> + /// <typeparam name="_InputIterator"> + /// The type of the specified iterator. + /// </typeparam> + /// <param name="_Begin"> + /// An iterator to the first element of the source range. + /// </param> + /// <param name="_End"> + /// An iterator to one past the last element of the source range. + /// </param> + /// <remarks> + /// <c>assign</c> is not concurrency-safe. You must ensure that no other threads are invoking methods + /// on the concurrent vector when you call this method. + /// </remarks> + /**/ + template<class _InputIterator> + void assign(_InputIterator _Begin, _InputIterator _End) + { + clear(); + _Internal_assign( _Begin, _End, static_cast<_Is_integer_tag<std::numeric_limits<_InputIterator>::is_integer> *>(0) ); + } + + /// <summary> + /// Swaps the contents of two concurrent vectors. This method is not concurrency-safe. + /// </summary> + /// <param name="_Vector"> + /// The <c>concurrent_vector</c> object to swap contents with. + /// </param> + /**/ + void swap(concurrent_vector &_Vector) + { + if( this != &_Vector ) + { + _Concurrent_vector_base_v4::_Internal_swap(static_cast<_Concurrent_vector_base_v4&>(_Vector)); + std::swap(this->_My_allocator, _Vector._My_allocator); + } + } + + /// <summary> + /// Erases all elements in the concurrent vector. This method is not concurrency-safe. + /// </summary> + /// <remarks> + /// <c>clear</c> is not concurrency-safe. You must ensure that no other threads are invoking methods + /// on the concurrent vector when you call this method. <c>clear</c> does not free internal arrays. To free internal arrays, + /// call the function <c>shrink_to_fit</c> after <c>clear</c>. + /// </remarks> + /**/ + void clear() + { + _Internal_clear(&_Destroy_array); + } + + /// <summary> + /// Erases all elements and destroys this concurrent vector. + /// </summary> + /**/ + ~concurrent_vector() + { + _Segment_t *_Table = _My_segment; + _Internal_free_segments( reinterpret_cast<void**>(_Table), _Internal_clear(&_Destroy_array), _My_first_block ); + // base class destructor call + } + + const ::Concurrency::details::_Concurrent_vector_base_v4 &_Internal_vector_base() const { return *this; } + ::Concurrency::details::_Concurrent_vector_base_v4 &_Internal_vector_base() { return *this; } +private: + + // Allocate _K items + static void * __cdecl _Internal_allocator(::Concurrency::details::_Concurrent_vector_base_v4 &_Vb, size_t _K) + { + return static_cast<concurrent_vector<_Ty, _Ax>&>(_Vb)._My_allocator.allocate(_K); + } + // Free _K segments from table + void _Internal_free_segments(void *_Table[], _Segment_index_t _K, _Segment_index_t _First_block); + + // Get reference to element at given _Index. + _Ty& _Internal_subscript( size_type _Index ) const; + + // Get reference to element at given _Index with errors checks + _Ty& _Internal_subscript_with_exceptions( size_type _Index ) const; + + // assign _N items by copying _Item + void _Internal_assign(size_type _N, const_reference _Item); + + // helper class + template<bool B> class _Is_integer_tag; + + // assign integer items by copying when arguments are treated as iterators. See C++ Standard 2003 23.1.1 p9 + template<class _I> + void _Internal_assign(_I _First, _I _Last, _Is_integer_tag<true> *) + { + _Internal_assign(static_cast<size_type>(_First), static_cast<_Ty>(_Last)); + } + // inline proxy assign by iterators + template<class _I> + void _Internal_assign(_I _First, _I _Last, _Is_integer_tag<false> *) { + internal_assign_iterators(_First, _Last); + } + // assign by iterators + template<class _I> + void internal_assign_iterators(_I _First, _I _Last); + + // Construct _N instances of _Ty, starting at "begin". + static void __cdecl _Initialize_array( void* _Begin, const void*, size_type _N ); + + // Construct _N instances of _Ty, starting at "begin". + static void __cdecl _Initialize_array_by( void* _Begin, const void* _Src, size_type _N ); + + // Construct _N instances of _Ty, starting at "begin". + static void __cdecl _Copy_array( void* _Dst, const void* _Src, size_type _N ); + + // Assign _N instances of _Ty, starting at "begin". + static void __cdecl _Assign_array( void* _Dst, const void* _Src, size_type _N ); + + // Destroy _N instances of _Ty, starting at "begin". + static void __cdecl _Destroy_array( void* _Begin, size_type _N ); + + // Exception-aware helper class for filling a segment by exception-danger operators of user class + class _Internal_loop_guide + { + public: + const pointer _My_array; + const size_type _N; + size_type _I; + _Internal_loop_guide(size_type _NTrials, void *_Ptr) + : _My_array(static_cast<pointer>(_Ptr)), _N(_NTrials), _I(0) + { + } + + void _Init() + { + for(; _I < _N; ++_I) + new( &_My_array[_I] ) _Ty(); + } + void _Init(const void *_Src) + { + for(; _I < _N; ++_I) + new( &_My_array[_I] ) _Ty(*static_cast<const _Ty*>(_Src)); + } + void _Copy(const void *_Src) + { + for(; _I < _N; ++_I) + new( &_My_array[_I] ) _Ty(static_cast<const _Ty*>(_Src)[_I]); + } + void _Assign(const void *_Src) + { + for(; _I < _N; ++_I) + _My_array[_I] = static_cast<const _Ty*>(_Src)[_I]; + } + template<class _It> void _Iterate(_It &_Src) + { + for(; _I < _N; ++_I, ++_Src) + new( &_My_array[_I] ) _Ty( *_Src ); + } + ~_Internal_loop_guide() + { + if(_I < _N) // if exception raised, do zeroing on the rest of items + std::memset(_My_array+_I, 0, (_N-_I)*sizeof(value_type)); + } + private: + void operator=(const _Internal_loop_guide&); // prevent warning: assign operator can't be generated + }; +}; + +/// <summary> +/// Compacts the internal representation of the concurrent vector to reduce fragmentation and optimize memory usage. +/// </summary> +/// <remarks> +/// This method will internally re-allocate memory move elements around, invalidating all the iterators. +/// <c>shrink_to_fit</c> is not concurrency-safe. You must ensure that no other threads are invoking methods +/// on the concurrent vector when you call this function. +/// </remarks> +/**/ +template<typename _Ty, class _Ax> +void concurrent_vector<_Ty, _Ax>::shrink_to_fit() +{ + _Internal_segments_table _Old = { 0, nullptr }; + try + { + if( _Internal_compact( sizeof(_Ty), &_Old, &_Destroy_array, &_Copy_array ) ) + _Internal_free_segments( _Old._Table, _Pointers_per_long_table, _Old._First_block ); // Free joined and unnecessary segments + } + catch(...) + { + if( _Old._First_block ) // Free segment allocated for compacting. Only for support of exceptions in ctor of user _Ty[pe] + _Internal_free_segments( _Old._Table, 1, _Old._First_block ); + throw; + } +} + +template<typename _Ty, class _Ax> +void concurrent_vector<_Ty, _Ax>::_Internal_free_segments(void *_Table[], _Segment_index_t _K, _Segment_index_t _First_block) +{ + // Free the arrays + while( _K > _First_block ) + { + --_K; + _Ty* _Array = static_cast<_Ty*>(_Table[_K]); + _Table[_K] = NULL; + if( _Array > _BAD_ALLOC_MARKER ) // check for correct segment pointer + this->_My_allocator.deallocate( _Array, _Segment_size(_K) ); + } + _Ty* _Array = static_cast<_Ty*>(_Table[0]); + if( _Array > _BAD_ALLOC_MARKER ) + { + _CONCRT_ASSERT( _First_block > 0 ); + while(_K > 0) + _Table[--_K] = NULL; + this->_My_allocator.deallocate( _Array, _Segment_size(_First_block) ); + } +} + +template<typename _Ty, class _Ax> +_Ty& concurrent_vector<_Ty, _Ax>::_Internal_subscript( size_type _Index ) const +{ + _CONCRT_ASSERT( _Index<_My_early_size ); // index out of bounds + size_type _J = _Index; + _Segment_index_t _K = _Segment_base_index_of( _J ); + _CONCRT_ASSERT( _My_segment != (_Segment_t*)_My_storage || _K < _Pointers_per_short_table ); // index is under construction + // no need in load_with_acquire because the thread works in its own space or gets + _Ty* _Array = static_cast<_Ty*>(_My_segment[_K]._My_array); + _CONCRT_ASSERT( _Array != _BAD_ALLOC_MARKER ); // instance may be broken by bad allocation; use at() instead + _CONCRT_ASSERT( _Array != NULL ); // index is being allocated + return _Array[_J]; +} + +template<typename _Ty, class _Ax> +_Ty& concurrent_vector<_Ty, _Ax>::_Internal_subscript_with_exceptions( size_type _Index ) const +{ + if( _Index >= _My_early_size ) + _Internal_throw_exception(0); // throw std::out_of_range + size_type _J = _Index; + _Segment_index_t _K = _Segment_base_index_of( _J ); + if( _My_segment == (_Segment_t*)_My_storage && _K >= _Pointers_per_short_table ) + _Internal_throw_exception(1); // throw std::out_of_range + void *_Array = _My_segment[_K]._My_array; // no need in load_with_acquire + if( _Array <= _BAD_ALLOC_MARKER ) // check for correct segment pointer + _Internal_throw_exception(2); // throw std::range_error + return static_cast<_Ty*>(_Array)[_J]; +} + +template<typename _Ty, class _Ax> +void concurrent_vector<_Ty, _Ax>::_Internal_assign(size_type _N, const_reference _Item) +{ + _CONCRT_ASSERT( _My_early_size == 0 ); + if( !_N ) + return; + _Internal_reserve(_N, sizeof(_Ty), max_size()); + _My_early_size = _N; + _Segment_index_t _K = 0; + _Size_type _Sz = _Segment_size( _My_first_block ); + while (_Sz < _N) + { + _Initialize_array_by(static_cast<_Ty*>(_My_segment[_K]._My_array), static_cast<const void*>(&_Item), _Sz); + _N -= _Sz; + if (!_K) + { + _K = _My_first_block; + } + else { + ++_K; + _Sz <<= 1; + } + } + _Initialize_array_by(static_cast<_Ty*>(_My_segment[_K]._My_array), static_cast<const void*>(&_Item), _N); +} + +template<typename _Ty, class _Ax> template<class _I> +void concurrent_vector<_Ty, _Ax>::internal_assign_iterators(_I _First, _I _Last) +{ + _CONCRT_ASSERT(_My_early_size == 0); + size_type _N = std::distance(_First, _Last); + if( !_N ) return; + _Internal_reserve(_N, sizeof(_Ty), max_size()); + _My_early_size = _N; + _Segment_index_t _K = 0; + _Size_type _Sz = _Segment_size( _My_first_block ); + while (_Sz < _N) + { + _Internal_loop_guide _Loop(_Sz, _My_segment[_K]._My_array); + _Loop._Iterate(_First); + _N -= _Sz; + if (!_K) + { + _K = _My_first_block; + } + else { + ++_K; + _Sz <<= 1; + } + } + + _Internal_loop_guide _Loop(_N, _My_segment[_K]._My_array); + _Loop._Iterate(_First); +} + +template<typename _Ty, class _Ax> +void __cdecl concurrent_vector<_Ty, _Ax>::_Initialize_array( void* _Begin, const void *, size_type _N ) +{ + _Internal_loop_guide _Loop(_N, _Begin); _Loop._Init(); +} + +template<typename _Ty, class _Ax> +void __cdecl concurrent_vector<_Ty, _Ax>::_Initialize_array_by( void* _Begin, const void *_Src, size_type _N ) +{ + _Internal_loop_guide _Loop(_N, _Begin); _Loop._Init(_Src); +} + +template<typename _Ty, class _Ax> +void __cdecl concurrent_vector<_Ty, _Ax>::_Copy_array( void* _Dst, const void* _Src, size_type _N ) { + _Internal_loop_guide _Loop(_N, _Dst); _Loop._Copy(_Src); +} + +template<typename _Ty, class _Ax> +void __cdecl concurrent_vector<_Ty, _Ax>::_Assign_array( void* _Dst, const void* _Src, size_type _N ) +{ + _Internal_loop_guide _Loop(_N, _Dst); _Loop._Assign(_Src); +} + +#pragma warning(push) +#pragma warning(disable: 4189) /* local variable _Array is initialized but not used - the compiler optimizes away calls to the destructor */ + +template<typename _Ty, class _Ax> +void __cdecl concurrent_vector<_Ty, _Ax>::_Destroy_array( void* _Begin, size_type _N ) +{ + _Ty* _Array = static_cast<_Ty*>(_Begin); + for( size_type _J=_N; _J>0; --_J ) + _Array[_J-1].~_Ty(); // destructors are supposed to not throw any exceptions +} + +#pragma warning(pop) + +/// <summary> +/// Tests if the <c>concurrent_vector</c> object on the left side of the operator is equal to the <c>concurrent_vector</c> +/// object on the right side. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements stored in the concurrent vectors. +/// </typeparam> +/// <typeparam name="A1"> +/// The allocator type of the first <c>concurrent_vector</c> object. +/// </typeparam> +/// <typeparam name="A2"> +/// The allocator type of the second <c>concurrent_vector</c> object. +/// </typeparam> +/// <param name="_A"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <param name="_B"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <returns> +/// <c>true</c> if the concurrent vector on the left side of the operator is equal to the concurrent vector on the right side +/// of the operator; otherwise <c>false</c>. +/// </returns> +/// <remarks> +/// Two concurrent vectors are equal if they have the same number of elements and their respective elements have the same values. +/// Otherwise, they are unequal. +/// <para> This method is not concurrency-safe with respect to other methods that could modify either of the concurrent vectors +/// <paramref name="_A"/> or <paramref name="_B"/>.</para> +/// </remarks> +/// <seealso cref="concurrent_vector Class"/> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class A1, class A2> +inline bool operator==(const concurrent_vector<_Ty, A1> &_A, const concurrent_vector<_Ty, A2> &_B) +{ + // Simply: return _A.size() == _B.size() && std::equal(_A.begin(), _A.end(), _B.begin()); + if(_A.size() != _B.size()) + return false; + typename concurrent_vector<_Ty, A1>::const_iterator _I(_A.begin()); + typename concurrent_vector<_Ty, A2>::const_iterator _J(_B.begin()); + for(; _I != _A.end(); ++_I, ++_J) + { + if( !(*_I == *_J) ) + return false; + } + return true; +} + +/// <summary> +/// Tests if the <c>concurrent_vector</c> object on the left side of the operator is not equal to the <c>concurrent_vector</c> +/// object on the right side. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements stored in the concurrent vectors. +/// </typeparam> +/// <typeparam name="A1"> +/// The allocator type of the first <c>concurrent_vector</c> object. +/// </typeparam> +/// <typeparam name="A2"> +/// The allocator type of the second <c>concurrent_vector</c> object. +/// </typeparam> +/// <param name="_A"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <param name="_B"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <returns> +/// <c>true</c> if the concurrent vectors are not equal; <c>false</c> if the concurrent vectors are equal. +/// </returns> +/// <remarks> +/// Two concurrent vectors are equal if they have the same number of elements and their respective elements have the same +/// values. Otherwise, they are unequal. +/// <para> This method is not concurrency-safe with respect to other methods that could modify either of the concurrent vectors +/// <paramref name="_A"/> or <paramref name="_B"/>.</para> +/// </remarks> +/// <seealso cref="concurrent_vector Class"/> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class A1, class A2> +inline bool operator!=(const concurrent_vector<_Ty, A1> &_A, const concurrent_vector<_Ty, A2> &_B) +{ + return !(_A == _B); +} + +/// <summary> +/// Tests if the <c>concurrent_vector</c> object on the left side of the operator is less than the <c>concurrent_vector</c> +/// object on the right side. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements stored in the concurrent vectors. +/// </typeparam> +/// <typeparam name="A1"> +/// The allocator type of the first <c>concurrent_vector</c> object. +/// </typeparam> +/// <typeparam name="A2"> +/// The allocator type of the second <c>concurrent_vector</c> object. +/// </typeparam> +/// <param name="_A"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <param name="_B"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <returns> +/// <c>true</c> if the concurrent vector on the left side of the operator is less than the concurrent vector +/// on the right side of the operator; otherwise <c>false</c>. +/// </returns> +/// <remarks> +/// The behavior of this operator is identical to the equivalent operator for the <c>vector</c> class in the <c>std</c> +/// namespace. +/// <para> This method is not concurrency-safe with respect to other methods that could modify either of the concurrent vectors +/// <paramref name="_A"/> or <paramref name="_B"/>.</para> +/// </remarks> +/// <seealso cref="concurrent_vector Class"/> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class A1, class A2> +inline bool operator<(const concurrent_vector<_Ty, A1> &_A, const concurrent_vector<_Ty, A2> &_B) +{ + return (std::lexicographical_compare(_A.begin(), _A.end(), _B.begin(), _B.end())); +} + +/// <summary> +/// Tests if the <c>concurrent_vector</c> object on the left side of the operator is greater than the <c>concurrent_vector</c> +/// object on the right side. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements stored in the concurrent vectors. +/// </typeparam> +/// <typeparam name="A1"> +/// The allocator type of the first <c>concurrent_vector</c> object. +/// </typeparam> +/// <typeparam name="A2"> +/// The allocator type of the second <c>concurrent_vector</c> object. +/// </typeparam> +/// <param name="_A"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <param name="_B"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <returns> +/// <c>true</c> if the concurrent vector on the left side of the operator is greater than the concurrent vector +/// on the right side of the operator; otherwise <c>false</c>. +/// </returns> +/// <remarks> +/// The behavior of this operator is identical to the equivalent operator for the <c>vector</c> class in the <c>std</c> +/// namespace. +/// <para> This method is not concurrency-safe with respect to other methods that could modify either of the concurrent vectors +/// <paramref name="_A"/> or <paramref name="_B"/>.</para> +/// </remarks> +/// <seealso cref="concurrent_vector Class"/> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class A1, class A2> +inline bool operator>(const concurrent_vector<_Ty, A1> &_A, const concurrent_vector<_Ty, A2> &_B) +{ + return _B < _A; +} + +/// <summary> +/// Tests if the <c>concurrent_vector</c> object on the left side of the operator is less than or equal to the <c>concurrent_vector</c> +/// object on the right side. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements stored in the concurrent vectors. +/// </typeparam> +/// <typeparam name="A1"> +/// The allocator type of the first <c>concurrent_vector</c> object. +/// </typeparam> +/// <typeparam name="A2"> +/// The allocator type of the second <c>concurrent_vector</c> object. +/// </typeparam> +/// <param name="_A"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <param name="_B"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <returns> +/// <c>true</c> if the concurrent vector on the left side of the operator is less than or equal to the concurrent vector +/// on the right side of the operator; otherwise <c>false</c>. +/// </returns> +/// <remarks> +/// The behavior of this operator is identical to the equivalent operator for the <c>vector</c> class in the <c>std</c> +/// namespace. +/// <para> This method is not concurrency-safe with respect to other methods that could modify either of the concurrent vectors +/// <paramref name="_A"/> or <paramref name="_B"/>.</para> +/// </remarks> +/// <seealso cref="concurrent_vector Class"/> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class A1, class A2> +inline bool operator<=(const concurrent_vector<_Ty, A1> &_A, const concurrent_vector<_Ty, A2> &_B) +{ + return !(_B < _A); +} + +/// <summary> +/// Tests if the <c>concurrent_vector</c> object on the left side of the operator is greater than or equal to the <c>concurrent_vector</c> +/// object on the right side. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements stored in the concurrent vectors. +/// </typeparam> +/// <typeparam name="A1"> +/// The allocator type of the first <c>concurrent_vector</c> object. +/// </typeparam> +/// <typeparam name="A2"> +/// The allocator type of the second <c>concurrent_vector</c> object. +/// </typeparam> +/// <param name="_A"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <param name="_B"> +/// An object of type <c>concurrent_vector</c>. +/// </param> +/// <returns> +/// <c>true</c> if the concurrent vector on the left side of the operator is greater than or equal to the concurrent vector +/// on the right side of the operator; otherwise <c>false</c>. +/// </returns> +/// <remarks> +/// The behavior of this operator is identical to the equivalent operator for the <c>vector</c> class in the <c>std</c> +/// namespace. +/// <para> This method is not concurrency-safe with respect to other methods that could modify either of the concurrent vectors +/// <paramref name="_A"/> or <paramref name="_B"/>.</para> +/// </remarks> +/// <seealso cref="concurrent_vector Class"/> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class A1, class A2> +inline bool operator>=(const concurrent_vector<_Ty, A1> &_A, const concurrent_vector<_Ty, A2> &_B) +{ + return !(_A < _B); +} + +/// <summary> +/// Exchanges the elements of two <c>concurrent_vector</c> objects. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the elements stored in the concurrent vectors. +/// </typeparam> +/// <typeparam name="_Ax"> +/// The allocator type of the concurrent vectors. +/// </typeparam> +/// <param name="_B"> +/// The concurrent vector providing the elements to be swapped, or the vector whose elements are to be exchanged with those of the +/// concurrent vector <paramref name="_A"/>. +/// </param> +/// <param name="_A"> +/// The concurrent vector whose elements are to be exchanged with those of the concurrent vector <paramref name="_B"/>. +/// </param> +/// <remarks> +/// The template function is an algorithm specialized on the container class <c>concurrent_vector</c> to execute the member function +/// <paramref name="_A"/>.<see cref="concurrent_vector::swap Method">concurrent_vector::swap</see>(<paramref name="_B"/>). These are +/// instances of the partial ordering of function templates by the compiler. When template functions are overloaded in such a way that +/// the match of the template with the function call is not unique, then the compiler will select the most specialized version of the +/// template function. The general version of the template function, <c>template <class T> void swap(T&, T&)</c>, in the +/// algorithm class works by assignment and is a slow operation. The specialized version in each container is much faster as it can +/// work with the internal representation of the container class. +/// <para> This method is not concurrency-safe. You must ensure that no other threads are performing operations on either of the concurrent +/// vectors when you call this method.</para> +/// </remarks> +/// <seealso cref="concurrent_vector Class"/> +/// <seealso cref="Parallel Containers and Objects"/> +/**/ +template<typename _Ty, class _Ax> +inline void swap(concurrent_vector<_Ty, _Ax> &_A, concurrent_vector<_Ty, _Ax> &_B) +{ + _A.swap( _B ); +} + +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma warning (pop) +#pragma pack(pop) diff --git a/test_data/lots_of_files/ctype.h b/test_data/lots_of_files/ctype.h new file mode 100644 index 0000000..8a29bc1 --- /dev/null +++ b/test_data/lots_of_files/ctype.h @@ -0,0 +1,390 @@ +/*** +*ctype.h - character conversion macros and ctype macros +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines macros for character classification/conversion. +* [ANSI/System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_CTYPE +#define _INC_CTYPE + +#include <crtdefs.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef WEOF +#define WEOF (wint_t)(0xFFFF) +#endif /* WEOF */ + +#ifndef _CRT_CTYPEDATA_DEFINED +#define _CRT_CTYPEDATA_DEFINED +#ifndef _CTYPE_DISABLE_MACROS + +#ifdef _CRTBLD +extern const unsigned short __newctype[]; +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP const unsigned short ** __cdecl __p__pctype(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#endif /* _CRTBLD */ + +#ifndef __PCTYPE_FUNC +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define __PCTYPE_FUNC _pctype +#else /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ +#define __PCTYPE_FUNC __pctype_func() +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ +#endif /* __PCTYPE_FUNC */ + +_CRTIMP const unsigned short * __cdecl __pctype_func(void); +#if !defined (_M_CEE_PURE) +_CRTIMP extern const unsigned short *_pctype; +#else /* !defined (_M_CEE_PURE) */ +#define _pctype (__pctype_func()) +#endif /* !defined (_M_CEE_PURE) */ +#endif /* _CTYPE_DISABLE_MACROS */ +#endif /* _CRT_CTYPEDATA_DEFINED */ + +#ifndef _CRT_WCTYPEDATA_DEFINED +#define _CRT_WCTYPEDATA_DEFINED +#ifndef _CTYPE_DISABLE_MACROS +#if !defined (_M_CEE_PURE) +_CRTIMP extern const unsigned short _wctype[]; +#endif /* !defined (_M_CEE_PURE) */ + +#ifdef _CRTBLD +extern const unsigned short __newctype[]; +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP const wctype_t ** __cdecl __p__pwctype(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#endif /* _CRTBLD */ + +_CRTIMP const wctype_t * __cdecl __pwctype_func(void); +#if !defined (_M_CEE_PURE) +_CRTIMP extern const wctype_t *_pwctype; +#else /* !defined (_M_CEE_PURE) */ +#define _pwctype (__pwctype_func()) +#endif /* !defined (_M_CEE_PURE) */ +#endif /* _CTYPE_DISABLE_MACROS */ +#endif /* _CRT_WCTYPEDATA_DEFINED */ + +#ifdef _CRTBLD +#ifndef _CTYPE_DISABLE_MACROS +extern const unsigned char __newclmap[]; +extern const unsigned char __newcumap[]; +#endif /* _CTYPE_DISABLE_MACROS */ + +extern pthreadlocinfo __ptlocinfo; +extern pthreadmbcinfo __ptmbcinfo; +extern int __globallocalestatus; +extern int __locale_changed; +extern struct threadlocaleinfostruct __initiallocinfo; +extern _locale_tstruct __initiallocalestructinfo; +pthreadlocinfo __cdecl __updatetlocinfo(void); +pthreadmbcinfo __cdecl __updatetmbcinfo(void); +#endif /* _CRTBLD */ + + +/* set bit masks for the possible character types */ + +#define _UPPER 0x1 /* upper case letter */ +#define _LOWER 0x2 /* lower case letter */ +#define _DIGIT 0x4 /* digit[0-9] */ +#define _SPACE 0x8 /* space, tab, carriage return, newline, */ + /* vertical tab or form feed */ +#define _PUNCT 0x10 /* punctuation character */ +#define _CONTROL 0x20 /* control character */ +#define _BLANK 0x40 /* space char (tab handled separately) */ +#define _HEX 0x80 /* hexadecimal digit */ + +#define _LEADBYTE 0x8000 /* multibyte leadbyte */ +#define _ALPHA (0x0100|_UPPER|_LOWER) /* alphabetic character */ + + +/* character classification function prototypes */ + +#ifndef _CTYPE_DEFINED + +_Check_return_ _CRTIMP int __cdecl _isctype(_In_ int _C, _In_ int _Type); +_Check_return_ _CRTIMP int __cdecl _isctype_l(_In_ int _C, _In_ int _Type, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl isalpha(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isalpha_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl isupper(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isupper_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl islower(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _islower_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl isdigit(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isdigit_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl isxdigit(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isxdigit_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl isspace(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isspace_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl ispunct(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _ispunct_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl isblank(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isblank_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl isalnum(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isalnum_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl isprint(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isprint_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl isgraph(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isgraph_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iscntrl(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _iscntrl_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl toupper(_In_ int _C); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl tolower(_In_ int _C); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl _tolower(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _tolower_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRT_JIT_INTRINSIC _CRTIMP int __cdecl _toupper(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _toupper_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl __isascii(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl __toascii(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl __iscsymf(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl __iscsym(_In_ int _C); +#define _CTYPE_DEFINED +#endif /* _CTYPE_DEFINED */ + +#ifndef _WCTYPE_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +/* character classification function prototypes */ + +_Check_return_ _CRTIMP int __cdecl iswalpha(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswalpha_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswupper(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswupper_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswlower(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswlower_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswdigit(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswdigit_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswxdigit(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswxdigit_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswspace(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswspace_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswpunct(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswpunct_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswblank(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswblank_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswalnum(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswalnum_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswprint(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswprint_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswgraph(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswgraph_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswcntrl(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswcntrl_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswascii(_In_ wint_t _C); + +_Check_return_ _CRTIMP wint_t __cdecl towupper(_In_ wint_t _C); +_Check_return_ _CRTIMP wint_t __cdecl _towupper_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP wint_t __cdecl towlower(_In_ wint_t _C); +_Check_return_ _CRTIMP wint_t __cdecl _towlower_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswctype(_In_ wint_t _C, _In_ wctype_t _Type); +_Check_return_ _CRTIMP int __cdecl _iswctype_l(_In_ wint_t _C, _In_ wctype_t _Type, _In_opt_ _locale_t _Locale); + +_Check_return_ _CRTIMP int __cdecl __iswcsymf(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswcsymf_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl __iswcsym(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswcsym_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP int __cdecl isleadbyte(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isleadbyte_l(_In_ int _C, _In_opt_ _locale_t _Locale); +_CRT_OBSOLETE(iswctype) _CRTIMP int __cdecl is_wctype(_In_ wint_t _C, _In_ wctype_t _Type); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#define _WCTYPE_DEFINED +#endif /* _WCTYPE_DEFINED */ + +/* the character classification macro definitions */ + +#ifndef _CTYPE_DISABLE_MACROS + +/* + * Maximum number of bytes in multi-byte character in the current locale + * (also defined in stdlib.h). + */ +#ifndef MB_CUR_MAX +#ifdef _CRTBLD +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP int * __cdecl __p___mb_cur_max(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#define __MB_CUR_MAX(ptloci) (ptloci)->mb_cur_max +#endif /* _CRTBLD */ + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define MB_CUR_MAX __mb_cur_max +#else /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ +#define MB_CUR_MAX ___mb_cur_max_func() +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ +#if !defined (_M_CEE_PURE) +/* No data exports in pure code */ +_CRTIMP extern int __mb_cur_max; +#else /* !defined (_M_CEE_PURE) */ +#define __mb_cur_max (___mb_cur_max_func()) +#endif /* !defined (_M_CEE_PURE) */ +_CRTIMP int __cdecl ___mb_cur_max_func(void); +_CRTIMP int __cdecl ___mb_cur_max_l_func(_locale_t); +#endif /* MB_CUR_MAX */ + +/* Introduced to detect error when character testing functions are called + * with illegal input of integer. + */ +#ifdef _DEBUG +_CRTIMP int __cdecl _chvalidator(_In_ int _Ch, _In_ int _Mask); +#define __chvalidchk(a,b) _chvalidator(a,b) +#else /* _DEBUG */ +#define __chvalidchk(a,b) (__PCTYPE_FUNC[(a)] & (b)) +#endif /* _DEBUG */ + + +#ifdef _CRTBLD +#define __ascii_isalpha(c) ( __chvalidchk(c, _ALPHA)) +#define __ascii_isdigit(c) ( __chvalidchk(c, _DIGIT)) +#define __ascii_tolower(c) ( (((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c) ) +#define __ascii_toupper(c) ( (((c) >= 'a') && ((c) <= 'z')) ? ((c) - 'a' + 'A') : (c) ) +#define __ascii_iswalpha(c) ( ('A' <= (c) && (c) <= 'Z') || ( 'a' <= (c) && (c) <= 'z')) +#define __ascii_iswdigit(c) ( '0' <= (c) && (c) <= '9') +#define __ascii_towlower(c) ( (((c) >= L'A') && ((c) <= L'Z')) ? ((c) - L'A' + L'a') : (c) ) +#define __ascii_towupper(c) ( (((c) >= L'a') && ((c) <= L'z')) ? ((c) - L'a' + L'A') : (c) ) +#endif /* _CRTBLD */ + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#ifndef __cplusplus +#define isalpha(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_ALPHA) : __chvalidchk(_c, _ALPHA)) +#define isupper(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_UPPER) : __chvalidchk(_c, _UPPER)) +#define islower(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_LOWER) : __chvalidchk(_c, _LOWER)) +#define isdigit(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_DIGIT) : __chvalidchk(_c, _DIGIT)) +#define isxdigit(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_HEX) : __chvalidchk(_c, _HEX)) +#define isspace(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_SPACE) : __chvalidchk(_c, _SPACE)) +#define ispunct(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_PUNCT) : __chvalidchk(_c, _PUNCT)) +#define isblank(_c) (MB_CUR_MAX > 1 ? (((_c) == '\t') ? _BLANK : _isctype(_c,_BLANK)) : (((_c) == '\t') ? _BLANK : __chvalidchk(_c, _BLANK))) +#define isalnum(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_ALPHA|_DIGIT) : __chvalidchk(_c, (_ALPHA|_DIGIT))) +#define isprint(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT) : __chvalidchk(_c, (_BLANK|_PUNCT|_ALPHA|_DIGIT))) +#define isgraph(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_PUNCT|_ALPHA|_DIGIT) : __chvalidchk(_c, (_PUNCT|_ALPHA|_DIGIT))) +#define iscntrl(_c) (MB_CUR_MAX > 1 ? _isctype(_c,_CONTROL) : __chvalidchk(_c, _CONTROL)) +#endif /* __cplusplus */ +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ + +#ifdef _DEBUG +_CRTIMP int __cdecl _chvalidator_l(_In_opt_ _locale_t, _In_ int _Ch, _In_ int _Mask); +#define _chvalidchk_l(_Char, _Flag, _Locale) _chvalidator_l(_Locale, _Char, _Flag) +#else /* _DEBUG */ +#define _chvalidchk_l(_Char, _Flag, _Locale) (_Locale==NULL ? __chvalidchk(_Char, _Flag) : ((_locale_t)_Locale)->locinfo->pctype[_Char] & (_Flag)) +#endif /* _DEBUG */ + +#ifdef _CRTBLD +#define __ascii_isalpha_l(_Char, _Locale) ( _chvalidchk_l(_Char, _ALPHA, _Locale)) +#define __ascii_isdigit_l(_Char, _Locale) ( _chvalidchk_l(_Char, _DIGIT, _Locale)) +#endif /* _CRTBLD */ + +#define _ischartype_l(_Char, _Flag, _Locale) ( ((_Locale)!=NULL && (((_locale_t)(_Locale))->locinfo->mb_cur_max) > 1) ? _isctype_l(_Char, (_Flag), _Locale) : _chvalidchk_l(_Char,_Flag,_Locale)) +#define _isalpha_l(_Char, _Locale) _ischartype_l(_Char, _ALPHA, _Locale) +#define _isupper_l(_Char, _Locale) _ischartype_l(_Char, _UPPER, _Locale) +#define _islower_l(_Char, _Locale) _ischartype_l(_Char, _LOWER, _Locale) +#define _isdigit_l(_Char, _Locale) _ischartype_l(_Char, _DIGIT, _Locale) +#define _isxdigit_l(_Char, _Locale) _ischartype_l(_Char, _HEX, _Locale) +#define _isspace_l(_Char, _Locale) _ischartype_l(_Char, _SPACE, _Locale) +#define _ispunct_l(_Char, _Locale) _ischartype_l(_Char, _PUNCT, _Locale) +#define _isblank_l(_Char, _Locale) (((_Char) == '\t') ? _BLANK : _ischartype_l(_Char, _BLANK, _Locale)) +#define _isalnum_l(_Char, _Locale) _ischartype_l(_Char, _ALPHA|_DIGIT, _Locale) +#define _isprint_l(_Char, _Locale) _ischartype_l(_Char, _BLANK|_PUNCT|_ALPHA|_DIGIT, _Locale) +#define _isgraph_l(_Char, _Locale) _ischartype_l(_Char, _PUNCT|_ALPHA|_DIGIT, _Locale) +#define _iscntrl_l(_Char, _Locale) _ischartype_l(_Char, _CONTROL, _Locale) + +#define _tolower(_Char) ( (_Char)-'A'+'a' ) +#define _toupper(_Char) ( (_Char)-'a'+'A' ) + +#define __isascii(_Char) ( (unsigned)(_Char) < 0x80 ) +#define __toascii(_Char) ( (_Char) & 0x7f ) + +#ifndef _WCTYPE_INLINE_DEFINED + +#ifdef _CRTBLD +#define _CRT_WCTYPE_NOINLINE +#else /* _CRTBLD */ +#undef _CRT_WCTYPE_NOINLINE +#endif /* _CRTBLD */ + +#if !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (MRTDLL) || defined (_CRT_WCTYPE_NOINLINE) +#define iswalpha(_c) ( iswctype(_c,_ALPHA) ) +#define iswupper(_c) ( iswctype(_c,_UPPER) ) +#define iswlower(_c) ( iswctype(_c,_LOWER) ) +#define iswdigit(_c) ( iswctype(_c,_DIGIT) ) +#define iswxdigit(_c) ( iswctype(_c,_HEX) ) +#define iswspace(_c) ( iswctype(_c,_SPACE) ) +#define iswpunct(_c) ( iswctype(_c,_PUNCT) ) +#define iswblank(_c) (((_c) == '\t') ? _BLANK : iswctype(_c,_BLANK) ) +#define iswalnum(_c) ( iswctype(_c,_ALPHA|_DIGIT) ) +#define iswprint(_c) ( iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT) ) +#define iswgraph(_c) ( iswctype(_c,_PUNCT|_ALPHA|_DIGIT) ) +#define iswcntrl(_c) ( iswctype(_c,_CONTROL) ) +#define iswascii(_c) ( (unsigned)(_c) < 0x80 ) + +#define _iswalpha_l(_c,_p) ( iswctype(_c,_ALPHA) ) +#define _iswupper_l(_c,_p) ( iswctype(_c,_UPPER) ) +#define _iswlower_l(_c,_p) ( iswctype(_c,_LOWER) ) +#define _iswdigit_l(_c,_p) ( iswctype(_c,_DIGIT) ) +#define _iswxdigit_l(_c,_p) ( iswctype(_c,_HEX) ) +#define _iswspace_l(_c,_p) ( iswctype(_c,_SPACE) ) +#define _iswpunct_l(_c,_p) ( iswctype(_c,_PUNCT) ) +#define _iswblank_l(_c,_p) (((_c) == '\t') ? _BLANK : iswctype(_c,_BLANK) ) +#define _iswalnum_l(_c,_p) ( iswctype(_c,_ALPHA|_DIGIT) ) +#define _iswprint_l(_c,_p) ( iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT) ) +#define _iswgraph_l(_c,_p) ( iswctype(_c,_PUNCT|_ALPHA|_DIGIT) ) +#define _iswcntrl_l(_c,_p) ( iswctype(_c,_CONTROL) ) +#endif /* !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (MRTDLL) || defined (_CRT_WCTYPE_NOINLINE) */ +#define _WCTYPE_INLINE_DEFINED +#endif /* _WCTYPE_INLINE_DEFINED */ + +/* MS C version 2.0 extended ctype macros */ + +#define __iscsymf(_c) (isalpha(_c) || ((_c) == '_')) +#define __iscsym(_c) (isalnum(_c) || ((_c) == '_')) +#define __iswcsymf(_c) (iswalpha(_c) || ((_c) == '_')) +#define __iswcsym(_c) (iswalnum(_c) || ((_c) == '_')) + +#define _iscsymf_l(_c, _p) (_isalpha_l(_c, _p) || ((_c) == '_')) +#define _iscsym_l(_c, _p) (_isalnum_l(_c, _p) || ((_c) == '_')) +#define _iswcsymf_l(_c, _p) (iswalpha(_c) || ((_c) == '_')) +#define _iswcsym_l(_c, _p) (iswalnum(_c) || ((_c) == '_')) + +#endif /* _CTYPE_DISABLE_MACROS */ + + +#if !__STDC__ + +/* Non-ANSI names for compatibility */ + +#ifndef _CTYPE_DEFINED +_Check_return_ _CRT_NONSTDC_DEPRECATE(__isascii) _CRTIMP int __cdecl isascii(_In_ int _C); +_Check_return_ _CRT_NONSTDC_DEPRECATE(__toascii) _CRTIMP int __cdecl toascii(_In_ int _C); +_Check_return_ _CRT_NONSTDC_DEPRECATE(__iscsymf) _CRTIMP int __cdecl iscsymf(_In_ int _C); +_Check_return_ _CRT_NONSTDC_DEPRECATE(__iscsym) _CRTIMP int __cdecl iscsym(_In_ int _C); +#else /* _CTYPE_DEFINED */ +#define isascii __isascii +#define toascii __toascii +#define iscsymf __iscsymf +#define iscsym __iscsym +#endif /* _CTYPE_DEFINED */ + +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_CTYPE */ diff --git a/test_data/lots_of_files/delayhlp.cpp b/test_data/lots_of_files/delayhlp.cpp new file mode 100644 index 0000000..3bee358 --- /dev/null +++ b/test_data/lots_of_files/delayhlp.cpp @@ -0,0 +1,445 @@ +// +// DelayHlp.cpp +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Implement the delay load helper routines. +// + +// Build instructions +// cl -c -O1 -Z7 -Zl -W3 delayhlp.cpp +// +// For ISOLATION_AWARE_ENABLED calls to LoadLibrary(), you will need to add +// a definition for ISOLATION_AWARE_ENABLED to the command line above, eg: +// cl -c -O1 -Z7 -Zl -W3 -DISOLATION_AWARE_ENABLED=1 delayhlp.cpp +// +// +// Then, you can either link directly with this new object file, or replace the one in +// delayimp.lib with your new one, eg: +// lib /out:delayimp.lib delayhlp.obj +// + + +#define WIN32_LEAN_AND_MEAN +#define STRICT +#include <windows.h> + +#include "DelayImp.h" + +// +// Local copies of strlen, memcmp, and memcpy to make sure we do not need the CRT +// + +static inline size_t +__strlen(const char * sz) { + const char *szEnd = sz; + + while( *szEnd++ ) { + ; + } + + return szEnd - sz - 1; + } + +static inline int +__memcmp(const void * pv1, const void * pv2, size_t cb) { + if (!cb) { + return 0; + } + + while ( --cb && *(char *)pv1 == *(char *)pv2 ) { + pv1 = (char *)pv1 + 1; + pv2 = (char *)pv2 + 1; + } + + return *((unsigned char *)pv1) - *((unsigned char *)pv2); + } + +static inline void * +__memcpy(void * pvDst, const void * pvSrc, size_t cb) { + + void * pvRet = pvDst; + + // + // copy from lower addresses to higher addresses + // + while (cb--) { + *(char *)pvDst = *(char *)pvSrc; + pvDst = (char *)pvDst + 1; + pvSrc = (char *)pvSrc + 1; + } + + return pvRet; + } + + +// utility function for calculating the index of the current import +// for all the tables (INT, BIAT, UIAT, and IAT). +inline unsigned +IndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase) { + return (unsigned) (pitdCur - pitdBase); + } + +// C++ template utility function for converting RVAs to pointers +// +#if defined(_M_IA64) +#pragma section(".base", long, read) +extern "C" +__declspec(allocate(".base")) +const IMAGE_DOS_HEADER __ImageBase; +#else +extern "C" +const IMAGE_DOS_HEADER __ImageBase; +#endif + +template <class X> +X PFromRva(RVA rva) { + return X(PBYTE(&__ImageBase) + rva); + } + +// utility function for calculating the count of imports given the base +// of the IAT. NB: this only works on a valid IAT! +inline unsigned +CountOfImports(PCImgThunkData pitdBase) { + unsigned cRet = 0; + PCImgThunkData pitd = pitdBase; + while (pitd->u1.Function) { + pitd++; + cRet++; + } + return cRet; + } + +// For our own internal use, we convert to the old +// format for convenience. +// +struct InternalImgDelayDescr { + DWORD grAttrs; // attributes + LPCSTR szName; // pointer to dll name + HMODULE * phmod; // address of module handle + PImgThunkData pIAT; // address of the IAT + PCImgThunkData pINT; // address of the INT + PCImgThunkData pBoundIAT; // address of the optional bound IAT + PCImgThunkData pUnloadIAT; // address of optional copy of original IAT + DWORD dwTimeStamp; // 0 if not bound, + // O.W. date/time stamp of DLL bound to (Old BIND) + }; + +typedef InternalImgDelayDescr * PIIDD; +typedef const InternalImgDelayDescr * PCIIDD; + +static inline +PIMAGE_NT_HEADERS WINAPI +PinhFromImageBase(HMODULE hmod) { + return PIMAGE_NT_HEADERS(PBYTE(hmod) + PIMAGE_DOS_HEADER(hmod)->e_lfanew); + } + +static inline +void WINAPI +OverlayIAT(PImgThunkData pitdDst, PCImgThunkData pitdSrc) { + __memcpy(pitdDst, pitdSrc, CountOfImports(pitdDst) * sizeof IMAGE_THUNK_DATA); + } + +static inline +DWORD WINAPI +TimeStampOfImage(PIMAGE_NT_HEADERS pinh) { + return pinh->FileHeader.TimeDateStamp; + } + +static inline +bool WINAPI +FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh, HMODULE hmod) { + return UINT_PTR(hmod) == pinh->OptionalHeader.ImageBase; + } + +static +PCImgDelayDescr +PiddFromDllName(LPCSTR szDll) { + PCImgDelayDescr piddRet = NULL; + PIMAGE_NT_HEADERS pinh = PinhFromImageBase(HMODULE(&__ImageBase)); + + // Scan the Delay load IAT/INT for the dll in question + // + if (pinh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size != 0) { + PCImgDelayDescr pidd = PFromRva<PCImgDelayDescr>( + pinh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress + ); + + // Check all of the dlls listed up to the NULL one. + // + while (pidd->rvaDLLName != 0) { + // Check to see if it is the DLL we want + // Intentionally case sensitive to avoid complication of using the CRT + // for those that don't use the CRT...the user can replace this with + // a variant of a case insenstive comparison routine. + // + LPCSTR szDllCur = PFromRva<LPCSTR>(pidd->rvaDLLName); + size_t cchDllCur = __strlen(szDllCur); + if (cchDllCur == __strlen(szDll) && __memcmp(szDll, szDllCur, cchDllCur) == 0) { + piddRet = pidd; + break; + } + + pidd++; + } + } + return piddRet; + } + +// Do the InterlockedExchange magic +// +#ifdef _M_IX86 + +#undef InterlockedExchangePointer +#define InterlockedExchangePointer(Target, Value) \ + (PVOID)(LONG_PTR)InterlockedExchange((PLONG)(Target), (LONG)(LONG_PTR)(Value)) + +#if (_MSC_VER >= 1300) +typedef __w64 unsigned long *PULONG_PTR; +#else +typedef unsigned long *PULONG_PTR; +#endif + +#endif + +extern "C" +FARPROC WINAPI +__delayLoadHelper2( + PCImgDelayDescr pidd, + FARPROC * ppfnIATEntry + ) { + + // Set up some data we use for the hook procs but also useful for + // our own use + // + InternalImgDelayDescr idd = { + pidd->grAttrs, + PFromRva<LPCSTR>(pidd->rvaDLLName), + PFromRva<HMODULE*>(pidd->rvaHmod), + PFromRva<PImgThunkData>(pidd->rvaIAT), + PFromRva<PCImgThunkData>(pidd->rvaINT), + PFromRva<PCImgThunkData>(pidd->rvaBoundIAT), + PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT), + pidd->dwTimeStamp + }; + + DelayLoadInfo dli = { + sizeof DelayLoadInfo, + pidd, + ppfnIATEntry, + idd.szName, + { 0 }, + 0, + 0, + 0 + }; + + if (0 == (idd.grAttrs & dlattrRva)) { + PDelayLoadInfo rgpdli[1] = { &dli }; + + RaiseException( + VcppException(ERROR_SEVERITY_ERROR, ERROR_INVALID_PARAMETER), + 0, + 1, + PULONG_PTR(rgpdli) + ); + return 0; + } + + HMODULE hmod = *idd.phmod; + + // Calculate the index for the IAT entry in the import address table + // N.B. The INT entries are ordered the same as the IAT entries so + // the calculation can be done on the IAT side. + // + const unsigned iIAT = IndexFromPImgThunkData(PCImgThunkData(ppfnIATEntry), idd.pIAT); + const unsigned iINT = iIAT; + + PCImgThunkData pitd = &(idd.pINT[iINT]); + + dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal); + + if (dli.dlp.fImportByName) { + dli.dlp.szProcName = LPCSTR(PFromRva<PIMAGE_IMPORT_BY_NAME>(RVA(UINT_PTR(pitd->u1.AddressOfData)))->Name); + } + else { + dli.dlp.dwOrdinal = DWORD(IMAGE_ORDINAL(pitd->u1.Ordinal)); + } + + // Call the initial hook. If it exists and returns a function pointer, + // abort the rest of the processing and just return it for the call. + // + FARPROC pfnRet = NULL; + + if (__pfnDliNotifyHook2) { + pfnRet = ((*__pfnDliNotifyHook2)(dliStartProcessing, &dli)); + + if (pfnRet != NULL) { + goto HookBypass; + } + } + + // Check to see if we need to try to load the library. + // + if (hmod == 0) { + if (__pfnDliNotifyHook2) { + hmod = HMODULE(((*__pfnDliNotifyHook2)(dliNotePreLoadLibrary, &dli))); + } + if (hmod == 0) { + hmod = ::LoadLibraryEx(dli.szDll, NULL, 0); + } + if (hmod == 0) { + dli.dwLastError = ::GetLastError(); + if (__pfnDliFailureHook2) { + // when the hook is called on LoadLibrary failure, it will + // return 0 for failure and an hmod for the lib if it fixed + // the problem. + // + hmod = HMODULE((*__pfnDliFailureHook2)(dliFailLoadLib, &dli)); + } + + if (hmod == 0) { + PDelayLoadInfo rgpdli[1] = { &dli }; + + RaiseException( + VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND), + 0, + 1, + PULONG_PTR(rgpdli) + ); + + // If we get to here, we blindly assume that the handler of the exception + // has magically fixed everything up and left the function pointer in + // dli.pfnCur. + // + return dli.pfnCur; + } + } + + // Store the library handle. If it is already there, we infer + // that another thread got there first, and we need to do a + // FreeLibrary() to reduce the refcount + // + HMODULE hmodT = HMODULE(InterlockedExchangePointer((PVOID *) idd.phmod, PVOID(hmod))); + if (hmodT == hmod) { + ::FreeLibrary(hmod); + } + } + + // Go for the procedure now. + // + dli.hmodCur = hmod; + if (__pfnDliNotifyHook2) { + pfnRet = (*__pfnDliNotifyHook2)(dliNotePreGetProcAddress, &dli); + } + if (pfnRet == 0) { + if (pidd->rvaBoundIAT && pidd->dwTimeStamp) { + // bound imports exist...check the timestamp from the target image + // + PIMAGE_NT_HEADERS pinh(PinhFromImageBase(hmod)); + + if (pinh->Signature == IMAGE_NT_SIGNATURE && + TimeStampOfImage(pinh) == idd.dwTimeStamp && + FLoadedAtPreferredAddress(pinh, hmod)) { + + // Everything is good to go, if we have a decent address + // in the bound IAT! + // + pfnRet = FARPROC(UINT_PTR(idd.pBoundIAT[iIAT].u1.Function)); + if (pfnRet != 0) { + goto SetEntryHookBypass; + } + } + } + + pfnRet = ::GetProcAddress(hmod, dli.dlp.szProcName); + } + + if (pfnRet == 0) { + dli.dwLastError = ::GetLastError(); + if (__pfnDliFailureHook2) { + // when the hook is called on GetProcAddress failure, it will + // return 0 on failure and a valid proc address on success + // + pfnRet = (*__pfnDliFailureHook2)(dliFailGetProc, &dli); + } + if (pfnRet == 0) { + PDelayLoadInfo rgpdli[1] = { &dli }; + + RaiseException( + VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND), + 0, + 1, + PULONG_PTR(rgpdli) + ); + + // If we get to here, we blindly assume that the handler of the exception + // has magically fixed everything up and left the function pointer in + // dli.pfnCur. + // + pfnRet = dli.pfnCur; + } + } + +SetEntryHookBypass: + *ppfnIATEntry = pfnRet; + +HookBypass: + if (__pfnDliNotifyHook2) { + dli.dwLastError = 0; + dli.hmodCur = hmod; + dli.pfnCur = pfnRet; + (*__pfnDliNotifyHook2)(dliNoteEndProcessing, &dli); + } + return pfnRet; + } + +extern "C" +BOOL WINAPI +__FUnloadDelayLoadedDLL2(LPCSTR szDll) { + BOOL fRet = FALSE; + PCImgDelayDescr pidd = PiddFromDllName(szDll); + + if ((pidd != NULL) && + (pidd->rvaUnloadIAT != 0)) { + HMODULE * phmod = PFromRva<HMODULE*>(pidd->rvaHmod); + HMODULE hmod = *phmod; + if (hmod != NULL) { + OverlayIAT( + PFromRva<PImgThunkData>(pidd->rvaIAT), + PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT) + ); + ::FreeLibrary(hmod); + *phmod = NULL; + fRet = TRUE; + } + + } + return fRet; + } + +extern "C" +HRESULT WINAPI +__HrLoadAllImportsForDll(LPCSTR szDll) { + HRESULT hrRet = HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND); + PCImgDelayDescr pidd = PiddFromDllName(szDll); + + if (pidd != NULL) { + // Found a matching DLL name, now process it. + // + // Set up the internal structure + // + FARPROC * ppfnIATEntry = PFromRva<FARPROC*>(pidd->rvaIAT); + size_t cpfnIATEntries = CountOfImports(PCImgThunkData(ppfnIATEntry)); + FARPROC * ppfnIATEntryMax = ppfnIATEntry + cpfnIATEntries; + + for (;ppfnIATEntry < ppfnIATEntryMax; ppfnIATEntry++) { + __delayLoadHelper2(pidd, ppfnIATEntry); + } + + // Done, indicate some semblance of success + // + hrRet = S_OK; + } + return hrRet; + } diff --git a/test_data/lots_of_files/delayimp.h b/test_data/lots_of_files/delayimp.h new file mode 100644 index 0000000..0243a01 --- /dev/null +++ b/test_data/lots_of_files/delayimp.h @@ -0,0 +1,130 @@ +// +// DelayImp.h +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Define structures and prototypes necessary for delay loading of imports +// +#pragma once + +#define _DELAY_IMP_VER 2 + +#if defined(__cplusplus) +#define ExternC extern "C" +#else +#define ExternC extern +#endif + +typedef IMAGE_THUNK_DATA * PImgThunkData; +typedef const IMAGE_THUNK_DATA * PCImgThunkData; +typedef DWORD RVA; + +typedef struct ImgDelayDescr { + DWORD grAttrs; // attributes + RVA rvaDLLName; // RVA to dll name + RVA rvaHmod; // RVA of module handle + RVA rvaIAT; // RVA of the IAT + RVA rvaINT; // RVA of the INT + RVA rvaBoundIAT; // RVA of the optional bound IAT + RVA rvaUnloadIAT; // RVA of optional copy of original IAT + DWORD dwTimeStamp; // 0 if not bound, + // O.W. date/time stamp of DLL bound to (Old BIND) + } ImgDelayDescr, * PImgDelayDescr; + +typedef const ImgDelayDescr * PCImgDelayDescr; + +enum DLAttr { // Delay Load Attributes + dlattrRva = 0x1, // RVAs are used instead of pointers + // Having this set indicates a VC7.0 + // and above delay load descriptor. + }; + +// +// Delay load import hook notifications +// +enum { + dliStartProcessing, // used to bypass or note helper only + dliNoteStartProcessing = dliStartProcessing, + + dliNotePreLoadLibrary, // called just before LoadLibrary, can + // override w/ new HMODULE return val + dliNotePreGetProcAddress, // called just before GetProcAddress, can + // override w/ new FARPROC return value + dliFailLoadLib, // failed to load library, fix it by + // returning a valid HMODULE + dliFailGetProc, // failed to get proc address, fix it by + // returning a valid FARPROC + dliNoteEndProcessing, // called after all processing is done, no + // no bypass possible at this point except + // by longjmp()/throw()/RaiseException. + }; + +typedef struct DelayLoadProc { + BOOL fImportByName; + union { + LPCSTR szProcName; + DWORD dwOrdinal; + }; + } DelayLoadProc; + +typedef struct DelayLoadInfo { + DWORD cb; // size of structure + PCImgDelayDescr pidd; // raw form of data (everything is there) + FARPROC * ppfn; // points to address of function to load + LPCSTR szDll; // name of dll + DelayLoadProc dlp; // name or ordinal of procedure + HMODULE hmodCur; // the hInstance of the library we have loaded + FARPROC pfnCur; // the actual function that will be called + DWORD dwLastError;// error received (if an error notification) + } DelayLoadInfo, * PDelayLoadInfo; + +typedef FARPROC (WINAPI *PfnDliHook)( + unsigned dliNotify, + PDelayLoadInfo pdli + ); + +// +// Unload support +// + +// routine definition; takes a pointer to a name to unload +// +ExternC +BOOL WINAPI +__FUnloadDelayLoadedDLL2(LPCSTR szDll); + +// +// Snap load support +// +ExternC +HRESULT WINAPI +__HrLoadAllImportsForDll(LPCSTR szDll); + + +// +// Exception information +// +//#define FACILITY_VISUALCPP ((LONG)0x6d) now defined in winerror.h +#define VcppException(sev,err) ((sev) | (FACILITY_VISUALCPP<<16) | err) + +// +// Hook pointers +// + +// The "notify hook" gets called for every call to the +// delay load helper. This allows a user to hook every call and +// skip the delay load helper entirely. +// +// dliNotify == { +// dliStartProcessing | +// dliNotePreLoadLibrary | +// dliNotePreGetProc | +// dliNoteEndProcessing} +// on this call. +// +ExternC +PfnDliHook __pfnDliNotifyHook2; + +// This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc} +ExternC +PfnDliHook __pfnDliFailureHook2; diff --git a/test_data/lots_of_files/direct.h b/test_data/lots_of_files/direct.h new file mode 100644 index 0000000..0e57f5e --- /dev/null +++ b/test_data/lots_of_files/direct.h @@ -0,0 +1,155 @@ +/*** +*direct.h - function declarations for directory handling/creation +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This include file contains the function declarations for the library +* functions related to directory handling and creation. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_DIRECT +#define _INC_DIRECT + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +/* _getdiskfree structure for _getdiskfree() */ +#ifndef _DISKFREE_T_DEFINED + +struct _diskfree_t { + unsigned total_clusters; + unsigned avail_clusters; + unsigned sectors_per_cluster; + unsigned bytes_per_sector; + }; + +#define _DISKFREE_T_DEFINED +#endif /* _DISKFREE_T_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/* function prototypes */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_getcwd") +#pragma push_macro("_getdcwd") +#undef _getcwd +#undef _getdcwd +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _Ret_maybenull_z_ _CRTIMP char * __cdecl _getcwd(_Out_writes_opt_(_SizeInBytes) char * _DstBuf, _In_ int _SizeInBytes); +_Check_return_ _Ret_maybenull_z_ _CRTIMP char * __cdecl _getdcwd(_In_ int _Drive, _Out_writes_opt_(_SizeInBytes) char * _DstBuf, _In_ int _SizeInBytes); +#define _getdcwd_nolock _getdcwd + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_getcwd") +#pragma pop_macro("_getdcwd") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP int __cdecl _chdir(_In_z_ const char * _Path); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP int __cdecl _mkdir(_In_z_ const char * _Path); +_Check_return_ _CRTIMP int __cdecl _rmdir(_In_z_ const char * _Path); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +_Check_return_ _CRTIMP int __cdecl _chdrive(_In_ int _Drive); +_Check_return_ _CRTIMP int __cdecl _getdrive(void); +_Check_return_ _CRTIMP unsigned long __cdecl _getdrives(void); + +#ifndef _GETDISKFREE_DEFINED +_Check_return_ _CRTIMP unsigned __cdecl _getdiskfree(_In_ unsigned _Drive, _Out_ struct _diskfree_t * _DiskFree); +#define _GETDISKFREE_DEFINED +#endif /* _GETDISKFREE_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifndef _WDIRECT_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wgetcwd") +#pragma push_macro("_wgetdcwd") +#undef _wgetcwd +#undef _wgetdcwd +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _Ret_maybenull_z_ _CRTIMP wchar_t * __cdecl _wgetcwd(_Out_writes_opt_(_SizeInWords) wchar_t * _DstBuf, _In_ int _SizeInWords); +_Check_return_ _Ret_maybenull_z_ _CRTIMP wchar_t * __cdecl _wgetdcwd(_In_ int _Drive, _Out_writes_opt_(_SizeInWords) wchar_t * _DstBuf, _In_ int _SizeInWords); +#define _wgetdcwd_nolock _wgetdcwd + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wgetcwd") +#pragma pop_macro("_wgetdcwd") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP int __cdecl _wchdir(_In_z_ const wchar_t * _Path); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP int __cdecl _wmkdir(_In_z_ const wchar_t * _Path); +_Check_return_ _CRTIMP int __cdecl _wrmdir(_In_z_ const wchar_t * _Path); + +#define _WDIRECT_DEFINED +#endif /* _WDIRECT_DEFINED */ + +#if !__STDC__ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +/* Non-ANSI names for compatibility */ +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("getcwd") +#undef getcwd +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _Ret_maybenull_z_ _CRT_NONSTDC_DEPRECATE(_getcwd) _CRTIMP char * __cdecl getcwd(_Out_writes_opt_(_SizeInBytes) char * _DstBuf, _In_ int _SizeInBytes); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("getcwd") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRT_NONSTDC_DEPRECATE(_chdir) _CRTIMP int __cdecl chdir(_In_z_ const char * _Path); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRT_NONSTDC_DEPRECATE(_mkdir) _CRTIMP int __cdecl mkdir(_In_z_ const char * _Path); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_rmdir) _CRTIMP int __cdecl rmdir(_In_z_ const char * _Path); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +#define diskfree_t _diskfree_t +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_DIRECT */ diff --git a/test_data/lots_of_files/dll_reader.cpp b/test_data/lots_of_files/dll_reader.cpp new file mode 100644 index 0000000..59b0035 --- /dev/null +++ b/test_data/lots_of_files/dll_reader.cpp @@ -0,0 +1,206 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +// TOP + +#include "4ed_meta.h" +#include "4ed_dll_reader.h" +#include "4ed_dll_reader.cpp" + +i32 +compare(char *a, char *b, i32 len){ + i32 result; + char *e; + + result = 0; + e = a + len; + for (;a < e && *a == *b; ++a, ++b); + if (a < e){ + if (*a < *b) result = -1; + else result = 1; + } + + return(result); +} + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +Data +load_file(char *filename){ + Data result; + FILE * file; + + result = {}; + file = fopen(filename, "rb"); + if (!file){ + printf("file %s not found\n", filename); + } + else{ + fseek(file, 0, SEEK_END); + result.size = ftell(file); + fseek(file, 0, SEEK_SET); + result.data = (byte*)malloc(result.size); + fread(result.data, 1, result.size, file); + fclose(file); + } + + return(result); +} + +void +show_reloc_block(Data file, DLL_Data *dll, PE_Section_Definition *reloc_section){ + byte *base; + Relocation_Block_Header *header; + Relocation_Block_Entry *entry; + u32 cursor; + u32 bytes_in_table; + u32 block_end; + + base = file.data + reloc_section->disk_location; + if (dll->is_64bit) bytes_in_table = dll->opt_header_64->data_directory[image_dir_base_reloc_table].size; + else bytes_in_table = dll->opt_header_32->data_directory[image_dir_base_reloc_table].size; + + for (cursor = 0; cursor < bytes_in_table;){ + header = (Relocation_Block_Header*)(base + cursor); + block_end = cursor + header->block_size; + cursor += sizeof(Relocation_Block_Header); + + printf("block-size: %d\n", header->block_size); + printf("offset-base: %d\n", header->page_base_offset); + + for (;cursor < block_end;){ + entry = (Relocation_Block_Entry*)(base + cursor); + cursor += sizeof(Relocation_Block_Entry); + printf("reloc: type %d offset %d\n", + (i32)(entry->entry & reloc_entry_type_mask) >> reloc_entry_type_shift, + (i32)(entry->entry & reloc_entry_offset_mask)); + } + } +} + +typedef int (Function)(int a, int b); + +#include <Windows.h> + +#define UseWinDll 0 + +int +main(int argc, char **argv){ + Function *func; + i32 x; + +#if UseWinDll + HMODULE module; + + if (argc < 2){ + printf("usage: dll_reader <dll-file>\n"); + exit(1); + } + + module = LoadLibraryA(argv[1]); + + if (!module){ + printf("failed to load file %s\n", argv[1]); + exit(1); + } + + func = (Function*)GetProcAddress(module, "test_func"); + +#else + Data file, img; + DLL_Data dll; + DLL_Loaded dll_loaded; + PE_Section_Definition *section_def; + i32 error; + i32 i; + + if (argc < 2){ + printf("usage: dll_reader <dll-file>\n"); + exit(1); + } + + file = load_file(argv[1]); + + if (!file.data){ + printf("failed to load file %s\n", argv[1]); + exit(1); + } + + if (!dll_parse_headers(file, &dll, &error)){ + printf("header error %d\n", error); + exit(1); + } + + printf("this appears to be a dll\n"); + + printf("symbol-count: %d symbol-addr: %d\n", + dll.coff_header->number_of_symbols, + dll.coff_header->pointer_to_symbol_table); + + if (dll.is_64bit) printf("64bit\n"); + else printf("32bit\n"); + + printf("built for machine: %s\n", dll_machine_type_str(dll.coff_header->machine, 0)); + + if (dll.is_64bit){ + printf("number of directories: %d\n", dll.opt_header_64->number_of_rva_and_sizes); + } + else{ + printf("number of directories: %d\n", dll.opt_header_32->number_of_rva_and_sizes); + } + + printf("\nbeginning section decode now\n"); + + section_def = dll.section_defs; + for (i = 0; i < dll.coff_header->number_of_sections; ++i, ++section_def){ + if (section_def->name[7] == 0){ + printf("name: %s\n", section_def->name); + } + else{ + printf("name: %.*s\n", 8, section_def->name); + } + printf("img-size: %d img-loc: %d\ndisk-size: %d disk-loc: %d\n", + section_def->loaded_size, section_def->loaded_location, + section_def->disk_size, section_def->disk_location); + + if (compare(section_def->name, ".reloc", 6) == 0){ + show_reloc_block(file, &dll, section_def); + } + } + + img.size = dll_total_loaded_size(&dll); + printf("image size: %d\n", img.size); + + img.data = (byte*) + VirtualAlloc((LPVOID)Tbytes(3), img.size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + dll_load(img, &dll_loaded, file, &dll); + + DWORD _extra; + VirtualProtect(img.data + dll_loaded.text_start, + dll_loaded.text_size, + PAGE_EXECUTE_READ, + &_extra); + + func = (Function*)dll_load_function(&dll_loaded, "test_func", 9); +#endif + + x = func(10, 20); + printf("%d\n", x); + + x = func(1, 2); + printf("%d\n", x); + + return(0); +} + +// BOTTOM diff --git a/test_data/lots_of_files/dos.h b/test_data/lots_of_files/dos.h new file mode 100644 index 0000000..511574b --- /dev/null +++ b/test_data/lots_of_files/dos.h @@ -0,0 +1,78 @@ +/*** +*dos.h - definitions for MS-DOS interface routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines the structs and unions used for the direct DOS interface +* routines; includes macros to access the segment and offset +* values of far pointers, so that they may be used by the routines; and +* provides function prototypes for direct DOS interface functions. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_DOS +#define _INC_DOS + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _DISKFREE_T_DEFINED +/* _getdiskfree structure (duplicated in DIRECT.H) */ +struct _diskfree_t { + unsigned total_clusters; + unsigned avail_clusters; + unsigned sectors_per_cluster; + unsigned bytes_per_sector; + }; + +#define _DISKFREE_T_DEFINED +#endif /* _DISKFREE_T_DEFINED */ + +/* File attribute constants */ + +#define _A_NORMAL 0x00 /* Normal file - No read/write restrictions */ +#define _A_RDONLY 0x01 /* Read only file */ +#define _A_HIDDEN 0x02 /* Hidden file */ +#define _A_SYSTEM 0x04 /* System file */ +#define _A_SUBDIR 0x10 /* Subdirectory */ +#define _A_ARCH 0x20 /* Archive file */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/* Function prototypes */ +#ifndef _GETDISKFREE_DEFINED +_Check_return_ _CRTIMP unsigned __cdecl _getdiskfree(_In_ unsigned _Drive, _Out_ struct _diskfree_t * _DiskFree); +#define _GETDISKFREE_DEFINED +#endif /* _GETDISKFREE_DEFINED */ +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifdef _M_IX86 +void __cdecl _disable(void); +void __cdecl _enable(void); +#endif /* _M_IX86 */ + +#if !__STDC__ +/* Non-ANSI name for compatibility */ +#define diskfree_t _diskfree_t +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_DOS */ diff --git a/test_data/lots_of_files/dvec.h b/test_data/lots_of_files/dvec.h new file mode 100644 index 0000000..3a7a901 --- /dev/null +++ b/test_data/lots_of_files/dvec.h @@ -0,0 +1,1630 @@ +/*** +*** Copyright (C) 1985-2011 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * Definition of a C++ class interface to Intel(R) Pentium(R) 4 processor SSE2 intrinsics. + * + * File name : dvec.h class definitions + * + * Concept: A C++ abstraction of Intel(R) Pentium(R) 4 processor SSE2 + * designed to improve programmer productivity. Speed and accuracy are + * sacrificed for utility. Facilitates an easy transition to compiler + * intrinsics or assembly language. + * + */ + +#ifndef _DVEC_H_INCLUDED +#define _DVEC_H_INCLUDED +#ifndef RC_INVOKED + +#if !defined __cplusplus + #error ERROR: This file is only supported in C++ compilations! +#endif /* !defined __cplusplus */ + +#if defined (_M_CEE_PURE) + #error ERROR: This file is not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <immintrin.h> /* SSE2 intrinsic function definition include file */ +#include <fvec.h> +#include <crtdefs.h> + +#ifndef _VEC_ASSERT +#ifdef NDEBUG + #define _VEC_ASSERT(_Expression) ((void)0) +#else /* NDEBUG */ +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + _CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message, _In_z_ const wchar_t *_File, _In_ unsigned _Line); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + + #define _VEC_ASSERT(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) ) +#endif /* NDEBUG */ +#endif /* _VEC_ASSERT */ + +#ifdef _MSC_VER +#pragma pack(push,_CRT_PACKING) +#endif /* _MSC_VER */ + +/* Define _ENABLE_VEC_DEBUG to enable std::ostream inserters for debug output */ +#if defined (_ENABLE_VEC_DEBUG) + #include <iostream> +#endif /* defined (_ENABLE_VEC_DEBUG) */ + +#pragma pack(push,16) /* Must ensure class & union 16-B aligned */ + +const union +{ + int i[4]; + __m128d m; +} __f64vec2_abs_mask_cheat = {0xffffffff, 0x7fffffff, 0xffffffff, 0x7fffffff}; + +#define _f64vec2_abs_mask ((F64vec2)__f64vec2_abs_mask_cheat.m) + +/* EMM Functionality Intrinsics */ + +class I8vec16; /* 16 elements, each element a signed or unsigned char data type */ +class Is8vec16; /* 16 elements, each element a signed char data type */ +class Iu8vec16; /* 16 elements, each element an unsigned char data type */ +class I16vec8; /* 8 elements, each element a signed or unsigned short */ +class Is16vec8; /* 8 elements, each element a signed short */ +class Iu16vec8; /* 8 elements, each element an unsigned short */ +class I32vec4; /* 4 elements, each element a signed or unsigned long */ +class Is32vec4; /* 4 elements, each element a signed long */ +class Iu32vec4; /* 4 elements, each element a unsigned long */ +class I64vec2; /* 2 element, each a __m64 data type */ +class I128vec1; /* 1 element, a __m128i data type */ + +#define _MM_16UB(element,vector) (*((unsigned char*)&##vector + ##element)) +#define _MM_16B(element,vector) (*((signed char*)&##vector + ##element)) + +#define _MM_8UW(element,vector) (*((unsigned short*)&##vector + ##element)) +#define _MM_8W(element,vector) (*((short*)&##vector + ##element)) + +#define _MM_4UDW(element,vector) (*((unsigned int*)&##vector + ##element)) +#define _MM_4DW(element,vector) (*((int*)&##vector + ##element)) + +#define _MM_2QW(element,vector) (*((__int64*)&##vector + ##element)) + + +/* We need a m128i constant, keeping performance in mind*/ + +#pragma warning(push) +#pragma warning(disable : 4640) +inline const __m128i get_mask128() +{ + static const __m128i mask128 = _mm_set1_epi64(M64(0xffffffffffffffffi64)); + return mask128; +} +#pragma warning(pop) + + +//DEVDIV Remove alais created in public\sdk\inc\winnt.h +#ifdef M128 +#undef M128 +#endif /* M128 */ +#ifdef PM128 +#undef PM128 +#endif /* PM128 */ +//end DEVDIV + +/* M128 Class: + * 1 element, a __m128i data type + * Contructors & Logical Operations + */ + +class M128 +{ +protected: + __m128i vec; + +public: + M128() { } + M128(__m128i mm) { vec = mm; } + + operator __m128i() const { return vec; } + + /* Logical Operations */ + M128& operator&=(const M128 &a) { return *this = (M128) _mm_and_si128(vec,a); } + M128& operator|=(const M128 &a) { return *this = (M128) _mm_or_si128(vec,a); } + M128& operator^=(const M128 &a) { return *this = (M128) _mm_xor_si128(vec,a); } + +}; + +inline M128 operator&(const M128 &a, const M128 &b) { return _mm_and_si128(a,b); } +inline M128 operator|(const M128 &a, const M128 &b) { return _mm_or_si128(a,b); } +inline M128 operator^(const M128 &a, const M128 &b) { return _mm_xor_si128(a,b); } +inline M128 andnot(const M128 &a, const M128 &b) { return _mm_andnot_si128(a,b); } + +/* I128vec1 Class: + * 1 element, a __m128i data type + * Contains Operations which can operate on any __m6128i data type + */ + +class I128vec1 : public M128 +{ +public: + I128vec1() { } + I128vec1(__m128i mm) : M128(mm) { } + + I128vec1& operator= (const M128 &a) { return *this = (I128vec1) a; } + I128vec1& operator&=(const M128 &a) { return *this = (I128vec1) _mm_and_si128(vec,a); } + I128vec1& operator|=(const M128 &a) { return *this = (I128vec1) _mm_or_si128(vec,a); } + I128vec1& operator^=(const M128 &a) { return *this = (I128vec1) _mm_xor_si128(vec,a); } + +}; + +/* I64vec2 Class: + * 2 elements, each element signed or unsigned 64-bit integer + */ +class I64vec2 : public M128 +{ +public: + I64vec2() { } + I64vec2(__m128i mm) : M128(mm) { } + + I64vec2(__m64 q1, __m64 q0) + { + _MM_2QW(0,vec) = *(__int64*)&q0; + _MM_2QW(1,vec) = *(__int64*)&q1; + } + + /* Assignment Operator */ + I64vec2& operator= (const M128 &a) { return *this = (I64vec2) a; } + + /* Logical Assignment Operators */ + I64vec2& operator&=(const M128 &a) { return *this = (I64vec2) _mm_and_si128(vec,a); } + I64vec2& operator|=(const M128 &a) { return *this = (I64vec2) _mm_or_si128(vec,a); } + I64vec2& operator^=(const M128 &a) { return *this = (I64vec2) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + I64vec2& operator +=(const I64vec2 &a) { return *this = (I64vec2) _mm_add_epi64(vec,a); } + I64vec2& operator -=(const I64vec2 &a) { return *this = (I64vec2) _mm_sub_epi64(vec,a); } + + /* Shift Logical Operators */ + I64vec2 operator<<(const I64vec2 &a) { return _mm_sll_epi64(vec,a); } + I64vec2 operator<<(int count) { return _mm_slli_epi64(vec,count); } + I64vec2& operator<<=(const I64vec2 &a) { return *this = (I64vec2) _mm_sll_epi64(vec,a); } + I64vec2& operator<<=(int count) { return *this = (I64vec2) _mm_slli_epi64(vec,count); } + I64vec2 operator>>(const I64vec2 &a) { return _mm_srl_epi64(vec,a); } + I64vec2 operator>>(int count) { return _mm_srli_epi64(vec,count); } + I64vec2& operator>>=(const I64vec2 &a) { return *this = (I64vec2) _mm_srl_epi64(vec,a); } + I64vec2& operator>>=(int count) { return *this = (I64vec2) _mm_srli_epi64(vec,count); } + + /* Element Access for Debug, No data modified */ + const __int64& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 2); /* Only 2 elements to access */ + return _MM_2QW(i,vec); + } + + /* Element Access and Assignment for Debug */ + __int64& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 2); /* Only 2 elements to access */ + return _MM_2QW(i,vec); + } + + +}; + +/* Unpacks */ +inline I64vec2 unpack_low(const I64vec2 &a, const I64vec2 &b) {return _mm_unpacklo_epi64(a,b); } +inline I64vec2 unpack_high(const I64vec2 &a, const I64vec2 &b) {return _mm_unpackhi_epi64(a,b); } + +/* I32vec4 Class: + * 4 elements, each element either a signed or unsigned int + */ +class I32vec4 : public M128 +{ +public: + I32vec4() { } + I32vec4(__m128i mm) : M128(mm) { } + I32vec4(int i3, int i2, int i1, int i0) {vec = _mm_set_epi32(i3, i2, i1, i0);} + + /* Assignment Operator */ + I32vec4& operator= (const M128 &a) { return *this = (I32vec4) a; } + + /* Logicals Operators */ + I32vec4& operator&=(const M128 &a) { return *this = (I32vec4) _mm_and_si128(vec,a); } + I32vec4& operator|=(const M128 &a) { return *this = (I32vec4) _mm_or_si128(vec,a); } + I32vec4& operator^=(const M128 &a) { return *this = (I32vec4) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + I32vec4& operator +=(const I32vec4 &a) { return *this = (I32vec4)_mm_add_epi32(vec,a); } + I32vec4& operator -=(const I32vec4 &a) { return *this = (I32vec4)_mm_sub_epi32(vec,a); } + + /* Shift Logical Operators */ + I32vec4 operator<<(const I32vec4 &a) { return _mm_sll_epi32(vec,a); } + I32vec4 operator<<(int count) { return _mm_slli_epi32(vec,count); } + I32vec4& operator<<=(const I32vec4 &a) { return *this = (I32vec4)_mm_sll_epi32(vec,a); } + I32vec4& operator<<=(int count) { return *this = (I32vec4)_mm_slli_epi32(vec,count); } + +}; + +inline I32vec4 cmpeq(const I32vec4 &a, const I32vec4 &b) { return _mm_cmpeq_epi32(a,b); } +inline I32vec4 cmpneq(const I32vec4 &a, const I32vec4 &b) { return _mm_andnot_si128(_mm_cmpeq_epi32(a,b), get_mask128()); } + +inline I32vec4 unpack_low(const I32vec4 &a, const I32vec4 &b) { return _mm_unpacklo_epi32(a,b); } +inline I32vec4 unpack_high(const I32vec4 &a, const I32vec4 &b) { return _mm_unpackhi_epi32(a,b); } + +/* Is32vec4 Class: + * 4 elements, each element signed integer + */ +class Is32vec4 : public I32vec4 +{ +public: + Is32vec4() { } + Is32vec4(__m128i mm) : I32vec4(mm) { } + Is32vec4(int i3, int i2, int i1, int i0) : I32vec4(i3, i2, i1, i0){} + + /* Assignment Operator */ + Is32vec4& operator= (const M128 &a) { return *this = (Is32vec4) a; } + + /* Logical Operators */ + Is32vec4& operator&=(const M128 &a) { return *this = (Is32vec4) _mm_and_si128(vec,a); } + Is32vec4& operator|=(const M128 &a) { return *this = (Is32vec4) _mm_or_si128(vec,a); } + Is32vec4& operator^=(const M128 &a) { return *this = (Is32vec4) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Is32vec4& operator +=(const I32vec4 &a) { return *this = (Is32vec4)_mm_add_epi32(vec,a); } + Is32vec4& operator -=(const I32vec4 &a) { return *this = (Is32vec4)_mm_sub_epi32(vec,a); } + + /* Shift Logical Operators */ + Is32vec4 operator<<(const M128 &a) { return _mm_sll_epi32(vec,a); } + Is32vec4 operator<<(int count) { return _mm_slli_epi32(vec,count); } + Is32vec4& operator<<=(const M128 &a) { return *this = (Is32vec4)_mm_sll_epi32(vec,a); } + Is32vec4& operator<<=(int count) { return *this = (Is32vec4)_mm_slli_epi32(vec,count); } + /* Shift Arithmetic Operations */ + Is32vec4 operator>>(const M128 &a) { return _mm_sra_epi32(vec,a); } + Is32vec4 operator>>(int count) { return _mm_srai_epi32(vec,count); } + Is32vec4& operator>>=(const M128 &a) { return *this = (Is32vec4) _mm_sra_epi32(vec,a); } + Is32vec4& operator>>=(int count) { return *this = (Is32vec4) _mm_srai_epi32(vec,count); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Is32vec4 &a) + { + os << "[3]:" << _MM_4DW(3,a) + << " [2]:" << _MM_4DW(2,a) + << " [1]:" << _MM_4DW(1,a) + << " [0]:" << _MM_4DW(0,a); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const int& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4DW(i,vec); + } + + /* Element Access for Debug */ + int& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4DW(i,vec); + } +}; + +/* Compares */ +inline Is32vec4 cmpeq(const Is32vec4 &a, const Is32vec4 &b) { return _mm_cmpeq_epi32(a,b); } +inline Is32vec4 cmpneq(const Is32vec4 &a, const Is32vec4 &b) { return _mm_andnot_si128(_mm_cmpeq_epi32(a,b), get_mask128()); } +inline Is32vec4 cmpgt(const Is32vec4 &a, const Is32vec4 &b) { return _mm_cmpgt_epi32(a,b); } +inline Is32vec4 cmplt(const Is32vec4 &a, const Is32vec4 &b) { return _mm_cmpgt_epi32(b,a); } + +/* Unpacks */ +inline Is32vec4 unpack_low(const Is32vec4 &a, const Is32vec4 &b) { return _mm_unpacklo_epi32(a,b); } +inline Is32vec4 unpack_high(const Is32vec4 &a, const Is32vec4 &b) { return _mm_unpackhi_epi32(a,b); } + +/* Iu32vec4 Class: + * 4 elements, each element unsigned int + */ +class Iu32vec4 : public I32vec4 +{ +public: + Iu32vec4() { } + Iu32vec4(__m128i mm) : I32vec4(mm) { } + Iu32vec4(unsigned int ui3, unsigned int ui2, unsigned int ui1, unsigned int ui0) + : I32vec4(ui3, ui2, ui1, ui0) { } + + /* Assignment Operator */ + Iu32vec4& operator= (const M128 &a) { return *this = (Iu32vec4) a; } + + /* Logical Assignment Operators */ + Iu32vec4& operator&=(const M128 &a) { return *this = (Iu32vec4) _mm_and_si128(vec,a); } + Iu32vec4& operator|=(const M128 &a) { return *this = (Iu32vec4) _mm_or_si128(vec,a); } + Iu32vec4& operator^=(const M128 &a) { return *this = (Iu32vec4) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Iu32vec4& operator +=(const I32vec4 &a) { return *this = (Iu32vec4)_mm_add_epi32(vec,a); } + Iu32vec4& operator -=(const I32vec4 &a) { return *this = (Iu32vec4)_mm_sub_epi32(vec,a); } + + /* Shift Logical Operators */ + Iu32vec4 operator<<(const M128 &a) { return _mm_sll_epi32(vec,a); } + Iu32vec4 operator<<(int count) { return _mm_slli_epi32(vec,count); } + Iu32vec4& operator<<=(const M128 &a) { return *this = (Iu32vec4)_mm_sll_epi32(vec,a); } + Iu32vec4& operator<<=(int count) { return *this = (Iu32vec4)_mm_slli_epi32(vec,count); } + Iu32vec4 operator>>(const M128 &a) { return _mm_srl_epi32(vec,a); } + Iu32vec4 operator>>(int count) { return _mm_srli_epi32(vec,count); } + Iu32vec4& operator>>=(const M128 &a) { return *this = (Iu32vec4) _mm_srl_epi32(vec,a); } + Iu32vec4& operator>>=(int count) { return *this = (Iu32vec4) _mm_srli_epi32(vec,count); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Iu32vec4 &a) + { + os << "[3]:" << _MM_4UDW(3,a) + << " [2]:" << _MM_4UDW(2,a) + << " [1]:" << _MM_4UDW(1,a) + << " [0]:" << _MM_4UDW(0,a); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const unsigned int& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4UDW(i,vec); + } + + /* Element Access and Assignment for Debug */ + unsigned int& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4UDW(i,vec); + } +}; + +inline I64vec2 operator*(const Iu32vec4 &a, const Iu32vec4 &b) { return _mm_mul_epu32(a,b); } +inline Iu32vec4 cmpeq(const Iu32vec4 &a, const Iu32vec4 &b) { return _mm_cmpeq_epi32(a,b); } +inline Iu32vec4 cmpneq(const Iu32vec4 &a, const Iu32vec4 &b) { return _mm_andnot_si128(_mm_cmpeq_epi32(a,b), get_mask128()); } + +inline Iu32vec4 unpack_low(const Iu32vec4 &a, const Iu32vec4 &b) { return _mm_unpacklo_epi32(a,b); } +inline Iu32vec4 unpack_high(const Iu32vec4 &a, const Iu32vec4 &b) { return _mm_unpackhi_epi32(a,b); } + +/* I16vec8 Class: + * 8 elements, each element either unsigned or signed short + */ +class I16vec8 : public M128 +{ +public: + I16vec8() { } + I16vec8(__m128i mm) : M128(mm) { } + I16vec8(short s7, short s6, short s5, short s4, short s3, short s2, short s1, short s0) + { + vec = _mm_set_epi16(s7, s6, s5, s4, s3, s2, s1, s0); + } + + /* Assignment Operator */ + I16vec8& operator= (const M128 &a) { return *this = (I16vec8) a; } + + /* Logical Assignment Operators */ + I16vec8& operator&=(const M128 &a) { return *this = (I16vec8) _mm_and_si128(vec,a); } + I16vec8& operator|=(const M128 &a) { return *this = (I16vec8) _mm_or_si128(vec,a); } + I16vec8& operator^=(const M128 &a) { return *this = (I16vec8) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + I16vec8& operator +=(const I16vec8 &a) { return *this = (I16vec8) _mm_add_epi16(vec,a); } + I16vec8& operator -=(const I16vec8 &a) { return *this = (I16vec8) _mm_sub_epi16(vec,a); } + I16vec8& operator *=(const I16vec8 &a) { return *this = (I16vec8) _mm_mullo_epi16(vec,a); } + + /* Shift Logical Operators */ + I16vec8 operator<<(const M128 &a) { return _mm_sll_epi16(vec,a); } + I16vec8 operator<<(int count) { return _mm_slli_epi16(vec,count); } + I16vec8& operator<<=(const M128 &a) { return *this = (I16vec8)_mm_sll_epi16(vec,a); } + I16vec8& operator<<=(int count) { return *this = (I16vec8)_mm_slli_epi16(vec,count); } + +}; + + +inline I16vec8 operator*(const I16vec8 &a, const I16vec8 &b) { return _mm_mullo_epi16(a,b); } + +inline I16vec8 cmpeq(const I16vec8 &a, const I16vec8 &b) { return _mm_cmpeq_epi16(a,b); } +inline I16vec8 cmpneq(const I16vec8 &a, const I16vec8 &b) { return _mm_andnot_si128(_mm_cmpeq_epi16(a,b), get_mask128()); } + +inline I16vec8 unpack_low(const I16vec8 &a, const I16vec8 &b) { return _mm_unpacklo_epi16(a,b); } +inline I16vec8 unpack_high(const I16vec8 &a, const I16vec8 &b) { return _mm_unpackhi_epi16(a,b); } + +/* Is16vec8 Class: + * 8 elements, each element signed short + */ +class Is16vec8 : public I16vec8 +{ +public: + Is16vec8() { } + Is16vec8(__m128i mm) : I16vec8(mm) { } + Is16vec8(signed short s7, signed short s6, signed short s5, + signed short s4, signed short s3, signed short s2, + signed short s1, signed short s0) + : I16vec8(s7, s6, s5, s4, s3, s2, s1, s0) { } + + /* Assignment Operator */ + Is16vec8& operator= (const M128 &a) { return *this = (Is16vec8) a; } + + /* Logical Assignment Operators */ + Is16vec8& operator&=(const M128 &a) { return *this = (Is16vec8) _mm_and_si128(vec,a); } + Is16vec8& operator|=(const M128 &a) { return *this = (Is16vec8) _mm_or_si128(vec,a); } + Is16vec8& operator^=(const M128 &a) { return *this = (Is16vec8) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Is16vec8& operator +=(const I16vec8 &a) { return *this = (Is16vec8) _mm_add_epi16(vec,a); } + Is16vec8& operator -=(const I16vec8 &a) { return *this = (Is16vec8) _mm_sub_epi16(vec,a); } + Is16vec8& operator *=(const I16vec8 &a) { return *this = (Is16vec8) _mm_mullo_epi16(vec,a); } + + /* Shift Logical Operators */ + Is16vec8 operator<<(const M128 &a) { return _mm_sll_epi16(vec,a); } + Is16vec8 operator<<(int count) { return _mm_slli_epi16(vec,count); } + Is16vec8& operator<<=(const M128 &a) { return *this = (Is16vec8)_mm_sll_epi16(vec,a); } + Is16vec8& operator<<=(int count) { return *this = (Is16vec8)_mm_slli_epi16(vec,count); } + /* Shift Arithmetic Operators */ + Is16vec8 operator>>(const M128 &a) { return _mm_sra_epi16(vec,a); } + Is16vec8 operator>>(int count) { return _mm_srai_epi16(vec,count); } + Is16vec8& operator>>=(const M128 &a) { return *this = (Is16vec8)_mm_sra_epi16(vec,a); } + Is16vec8& operator>>=(int count) { return *this = (Is16vec8)_mm_srai_epi16(vec,count); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Is16vec8 &a) + { + os << "[7]:" << _MM_8W(7,a) + << " [6]:" << _MM_8W(6,a) + << " [5]:" << _MM_8W(5,a) + << " [4]:" << _MM_8W(4,a) + << " [3]:" << _MM_8W(3,a) + << " [2]:" << _MM_8W(2,a) + << " [1]:" << _MM_8W(1,a) + << " [0]:" << _MM_8W(0,a); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const signed short& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8W(i,vec); + } + + /* Element Access and Assignment for Debug */ + signed short& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8W(i,vec); + } +}; + +inline Is16vec8 operator*(const Is16vec8 &a, const Is16vec8 &b) { return _mm_mullo_epi16(a,b); } + + +/* Additional Is16vec8 functions: compares, unpacks, sat add/sub */ +inline Is16vec8 cmpeq(const Is16vec8 &a, const Is16vec8 &b) { return _mm_cmpeq_epi16(a,b); } +inline Is16vec8 cmpneq(const Is16vec8 &a, const Is16vec8 &b) { return _mm_andnot_si128(_mm_cmpeq_epi16(a,b), get_mask128()); } +inline Is16vec8 cmpgt(const Is16vec8 &a, const Is16vec8 &b) { return _mm_cmpgt_epi16(a,b); } +inline Is16vec8 cmplt(const Is16vec8 &a, const Is16vec8 &b) { return _mm_cmpgt_epi16(b,a); } + +inline Is16vec8 unpack_low(const Is16vec8 &a, const Is16vec8 &b) { return _mm_unpacklo_epi16(a,b); } +inline Is16vec8 unpack_high(const Is16vec8 &a, const Is16vec8 &b) { return _mm_unpackhi_epi16(a,b); } + +inline Is16vec8 mul_high(const Is16vec8 &a, const Is16vec8 &b) { return _mm_mulhi_epi16(a,b); } +inline Is32vec4 mul_add(const Is16vec8 &a, const Is16vec8 &b) { return _mm_madd_epi16(a,b);} + +inline Is16vec8 sat_add(const Is16vec8 &a, const Is16vec8 &b) { return _mm_adds_epi16(a,b); } +inline Is16vec8 sat_sub(const Is16vec8 &a, const Is16vec8 &b) { return _mm_subs_epi16(a,b); } + +inline Is16vec8 simd_max(const Is16vec8 &a, const Is16vec8 &b) { return _mm_max_epi16(a,b); } +inline Is16vec8 simd_min(const Is16vec8 &a, const Is16vec8 &b) { return _mm_min_epi16(a,b); } + + +/* Iu16vec8 Class: + * 8 elements, each element unsigned short + */ +class Iu16vec8 : public I16vec8 +{ +public: + Iu16vec8() { } + Iu16vec8(__m128i mm) : I16vec8(mm) { } + Iu16vec8(unsigned short s7, unsigned short s6, unsigned short s5, + unsigned short s4, unsigned short s3, unsigned short s2, + unsigned short s1, unsigned short s0) + : I16vec8(s7, s6, s5, s4, s3, s2, s1, s0) { } + + /* Assignment Operator */ + Iu16vec8& operator= (const M128 &a) { return *this = (Iu16vec8) a; } + /* Logical Assignment Operators */ + Iu16vec8& operator&=(const M128 &a) { return *this = (Iu16vec8) _mm_and_si128(vec,a); } + Iu16vec8& operator|=(const M128 &a) { return *this = (Iu16vec8) _mm_or_si128(vec,a); } + Iu16vec8& operator^=(const M128 &a) { return *this = (Iu16vec8) _mm_xor_si128(vec,a); } + /* Addition & Subtraction Assignment Operators */ + Iu16vec8& operator +=(const I16vec8 &a) { return *this = (Iu16vec8) _mm_add_epi16(vec,a); } + Iu16vec8& operator -=(const I16vec8 &a) { return *this = (Iu16vec8) _mm_sub_epi16(vec,a); } + Iu16vec8& operator *=(const I16vec8 &a) { return *this = (Iu16vec8) _mm_mullo_epi16(vec,a); } + + /* Shift Logical Operators */ + Iu16vec8 operator<<(const M128 &a) { return _mm_sll_epi16(vec,a); } + Iu16vec8 operator<<(int count) { return _mm_slli_epi16(vec,count); } + Iu16vec8& operator<<=(const M128 &a) { return *this = (Iu16vec8)_mm_sll_epi16(vec,a); } + Iu16vec8& operator<<=(int count) { return *this = (Iu16vec8)_mm_slli_epi16(vec,count); } + Iu16vec8 operator>>(const M128 &a) { return _mm_srl_epi16(vec,a); } + Iu16vec8 operator>>(int count) { return _mm_srli_epi16(vec,count); } + Iu16vec8& operator>>=(const M128 &a) { return *this = (Iu16vec8) _mm_srl_epi16(vec,a); } + Iu16vec8& operator>>=(int count) { return *this = (Iu16vec8) _mm_srli_epi16(vec,count); } + + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator << (std::ostream &os, const Iu16vec8 &a) + { + os << "[7]:" << (unsigned short)(_MM_8UW(7,a)) + << " [6]:" << (unsigned short)(_MM_8UW(6,a)) + << " [5]:" << (unsigned short)(_MM_8UW(5,a)) + << " [4]:" << (unsigned short)(_MM_8UW(4,a)) + << " [3]:" << (unsigned short)(_MM_8UW(3,a)) + << " [2]:" << (unsigned short)(_MM_8UW(2,a)) + << " [1]:" << (unsigned short)(_MM_8UW(1,a)) + << " [0]:" << (unsigned short)(_MM_8UW(0,a)); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const unsigned short& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8UW(i,vec); + } + + /* Element Access for Debug */ + unsigned short& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8UW(i,vec); + } +}; + +inline Iu16vec8 operator*(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_mullo_epi16(a,b); } + +/* Additional Iu16vec8 functions: cmpeq,cmpneq, unpacks, sat add/sub */ +inline Iu16vec8 cmpeq(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_cmpeq_epi16(a,b); } +inline Iu16vec8 cmpneq(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_andnot_si128(_mm_cmpeq_epi16(a,b), get_mask128()); } + +inline Iu16vec8 unpack_low(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_unpacklo_epi16(a,b); } +inline Iu16vec8 unpack_high(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_unpackhi_epi16(a,b); } + +inline Iu16vec8 sat_add(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_adds_epu16(a,b); } +inline Iu16vec8 sat_sub(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_subs_epu16(a,b); } + +inline Iu16vec8 simd_avg(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_avg_epu16(a,b); } +inline I16vec8 mul_high(const Iu16vec8 &a, const Iu16vec8 &b) { return _mm_mulhi_epu16(a,b); } + +/* I8vec16 Class: + * 16 elements, each element either unsigned or signed char + */ +class I8vec16 : public M128 +{ +public: + I8vec16() { } + I8vec16(__m128i mm) : M128(mm) { } + I8vec16(char s15, char s14, char s13, char s12, char s11, char s10, + char s9, char s8, char s7, char s6, char s5, char s4, + char s3, char s2, char s1, char s0) + { + vec = _mm_set_epi8(s15, s14, s13, s12, s11, s10, s9, s8, s7, s6, s5, s4, s3, s2, s1, s0); + } + + /* Assignment Operator */ + I8vec16& operator= (const M128 &a) { return *this = (I8vec16) a; } + + /* Logical Assignment Operators */ + I8vec16& operator&=(const M128 &a) { return *this = (I8vec16) _mm_and_si128(vec,a); } + I8vec16& operator|=(const M128 &a) { return *this = (I8vec16) _mm_or_si128(vec,a); } + I8vec16& operator^=(const M128 &a) { return *this = (I8vec16) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + I8vec16& operator +=(const I8vec16 &a) { return *this = (I8vec16) _mm_add_epi8(vec,a); } + I8vec16& operator -=(const I8vec16 &a) { return *this = (I8vec16) _mm_sub_epi8(vec,a); } + +}; + +inline I8vec16 cmpeq(const I8vec16 &a, const I8vec16 &b) { return _mm_cmpeq_epi8(a,b); } +inline I8vec16 cmpneq(const I8vec16 &a, const I8vec16 &b) { return _mm_andnot_si128(_mm_cmpeq_epi8(a,b), get_mask128()); } + +inline I8vec16 unpack_low(const I8vec16 &a, const I8vec16 &b) { return _mm_unpacklo_epi8(a,b); } +inline I8vec16 unpack_high(const I8vec16 &a, const I8vec16 &b) { return _mm_unpackhi_epi8(a,b); } + +/* Is8vec16 Class: + * 16 elements, each element a signed char + */ +class Is8vec16 : public I8vec16 +{ +public: + Is8vec16() { } + Is8vec16(__m128i mm) : I8vec16(mm) { } + Is8vec16(char s15, char s14, char s13, char s12, char s11, char s10, + char s9, char s8, char s7, char s6, char s5, char s4, + char s3, char s2, char s1, char s0) + : I8vec16(s15, s14, s13, s12, s11, s10, s9, s8, + s7, s6, s5, s4, s3, s2, s1, s0) { } + + /* Assignment Operator */ + Is8vec16& operator= (const M128 &a) { return *this = (Is8vec16) a; } + + /* Logical Assignment Operators */ + Is8vec16& operator&=(const M128 &a) { return *this = (Is8vec16) _mm_and_si128(vec,a); } + Is8vec16& operator|=(const M128 &a) { return *this = (Is8vec16) _mm_or_si128(vec,a); } + Is8vec16& operator^=(const M128 &a) { return *this = (Is8vec16) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Is8vec16& operator +=(const I8vec16 &a) { return *this = (Is8vec16) _mm_add_epi8(vec,a); } + Is8vec16& operator -=(const I8vec16 &a) { return *this = (Is8vec16) _mm_sub_epi8(vec,a); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator << (std::ostream &os, const Is8vec16 &a) + { + os << "[15]:" << short(_MM_16B(15,a)) + << " [14]:" << short(_MM_16B(14,a)) + << " [13]:" << short(_MM_16B(13,a)) + << " [12]:" << short(_MM_16B(12,a)) + << " [11]:" << short(_MM_16B(11,a)) + << " [10]:" << short(_MM_16B(10,a)) + << " [9]:" << short(_MM_16B(9,a)) + << " [8]:" << short(_MM_16B(8,a)) + << " [7]:" << short(_MM_16B(7,a)) + << " [6]:" << short(_MM_16B(6,a)) + << " [5]:" << short(_MM_16B(5,a)) + << " [4]:" << short(_MM_16B(4,a)) + << " [3]:" << short(_MM_16B(3,a)) + << " [2]:" << short(_MM_16B(2,a)) + << " [1]:" << short(_MM_16B(1,a)) + << " [0]:" << short(_MM_16B(0,a)); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const signed char& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 16); /* Only 16 elements to access */ + return _MM_16B(i,vec); + } + + /* Element Access for Debug */ + signed char& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 16); /* Only 16 elements to access */ + return _MM_16B(i,vec); + } + +}; + +inline Is8vec16 cmpeq(const Is8vec16 &a, const Is8vec16 &b) { return _mm_cmpeq_epi8(a,b); } +inline Is8vec16 cmpneq(const Is8vec16 &a, const Is8vec16 &b) { return _mm_andnot_si128(_mm_cmpeq_epi8(a,b), get_mask128()); } +inline Is8vec16 cmpgt(const Is8vec16 &a, const Is8vec16 &b) { return _mm_cmpgt_epi8(a,b); } +inline Is8vec16 cmplt(const Is8vec16 &a, const Is8vec16 &b) { return _mm_cmplt_epi8(a,b); } + +inline Is8vec16 unpack_low(const Is8vec16 &a, const Is8vec16 &b) { return _mm_unpacklo_epi8(a,b); } +inline Is8vec16 unpack_high(const Is8vec16 &a, const Is8vec16 &b) { return _mm_unpackhi_epi8(a,b); } + +inline Is8vec16 sat_add(const Is8vec16 &a, const Is8vec16 &b) { return _mm_adds_epi8(a,b); } +inline Is8vec16 sat_sub(const Is8vec16 &a, const Is8vec16 &b) { return _mm_subs_epi8(a,b); } + +/* Iu8vec16 Class: + * 16 elements, each element a unsigned char + */ +class Iu8vec16 : public I8vec16 +{ +public: + Iu8vec16() { } + Iu8vec16(__m128i mm) : I8vec16(mm) { } + Iu8vec16(unsigned char u15, unsigned char u14, unsigned char u13, + unsigned char u12, unsigned char u11, unsigned char u10, + unsigned char u9, unsigned char u8, unsigned char u7, + unsigned char u6, unsigned char u5, unsigned char u4, + unsigned char u3, unsigned char u2, unsigned char u1, + unsigned char u0) + : I8vec16(u15, u14, u13, u12, u11, u10, u9, u8, + u7, u6, u5, u4, u3, u2, u1, u0) { } + + /* Assignment Operator */ + Iu8vec16& operator= (const M128 &a) { return *this = (Iu8vec16) a; } + + /* Logical Assignment Operators */ + Iu8vec16& operator&=(const M128 &a) { return *this = (Iu8vec16) _mm_and_si128(vec,a); } + Iu8vec16& operator|=(const M128 &a) { return *this = (Iu8vec16) _mm_or_si128(vec,a); } + Iu8vec16& operator^=(const M128 &a) { return *this = (Iu8vec16) _mm_xor_si128(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Iu8vec16& operator +=(const I8vec16 &a) { return *this = (Iu8vec16) _mm_add_epi8(vec,a); } + Iu8vec16& operator -=(const I8vec16 &a) { return *this = (Iu8vec16) _mm_sub_epi8(vec,a); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator << (std::ostream &os, const Iu8vec16 &a) + { + os << "[15]:" << (unsigned char)(_MM_16UB(15,a)) + << " [14]:" << (unsigned char)(_MM_16UB(14,a)) + << " [13]:" << (unsigned char)(_MM_16UB(13,a)) + << " [12]:" << (unsigned char)(_MM_16UB(12,a)) + << " [11]:" << (unsigned char)(_MM_16UB(11,a)) + << " [10]:" << (unsigned char)(_MM_16UB(10,a)) + << " [9]:" << (unsigned char)(_MM_16UB(9,a)) + << " [8]:" << (unsigned char)(_MM_16UB(8,a)) + << " [7]:" << (unsigned char)(_MM_16UB(7,a)) + << " [6]:" << (unsigned char)(_MM_16UB(6,a)) + << " [5]:" << (unsigned char)(_MM_16UB(5,a)) + << " [4]:" << (unsigned char)(_MM_16UB(4,a)) + << " [3]:" << (unsigned char)(_MM_16UB(3,a)) + << " [2]:" << (unsigned char)(_MM_16UB(2,a)) + << " [1]:" << (unsigned char)(_MM_16UB(1,a)) + << " [0]:" << (unsigned char)(_MM_16UB(0,a)); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const unsigned char& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 16); /* Only 16 elements to access */ + return _MM_16UB(i,vec); + } + + /* Element Access for Debug */ + unsigned char& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 16); /* Only 16 elements to access */ + return _MM_16UB(i,vec); + } + +}; + +inline Iu8vec16 cmpeq(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_cmpeq_epi8(a,b); } +inline Iu8vec16 cmpneq(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_andnot_si128(_mm_cmpeq_epi8(a,b), get_mask128()); } + +inline Iu8vec16 unpack_low(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_unpacklo_epi8(a,b); } +inline Iu8vec16 unpack_high(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_unpackhi_epi8(a,b); } + +inline Iu8vec16 sat_add(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_adds_epu8(a,b); } +inline Iu8vec16 sat_sub(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_subs_epu8(a,b); } + +inline I64vec2 sum_abs(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_sad_epu8(a,b); } + +inline Iu8vec16 simd_avg(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_avg_epu8(a,b); } +inline Iu8vec16 simd_max(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_max_epu8(a,b); } +inline Iu8vec16 simd_min(const Iu8vec16 &a, const Iu8vec16 &b) { return _mm_min_epu8(a,b); } + +/* Pack & Saturates */ + +inline Is16vec8 pack_sat(const Is32vec4 &a, const Is32vec4 &b) { return _mm_packs_epi32(a,b); } +inline Is8vec16 pack_sat(const Is16vec8 &a, const Is16vec8 &b) { return _mm_packs_epi16(a,b); } +inline Iu8vec16 packu_sat(const Is16vec8 &a, const Is16vec8 &b) { return _mm_packus_epi16(a,b);} + + /********************************* Logicals ****************************************/ +#define IVEC128_LOGICALS(vect,element) \ +inline I##vect##vec##element operator& (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _mm_and_si128( a,b); } \ +inline I##vect##vec##element operator| (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _mm_or_si128( a,b); } \ +inline I##vect##vec##element operator^ (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _mm_xor_si128( a,b); } \ +inline I##vect##vec##element andnot (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _mm_andnot_si128( a,b); } + +IVEC128_LOGICALS(8,16) +IVEC128_LOGICALS(u8,16) +IVEC128_LOGICALS(s8,16) +IVEC128_LOGICALS(16,8) +IVEC128_LOGICALS(u16,8) +IVEC128_LOGICALS(s16,8) +IVEC128_LOGICALS(32,4) +IVEC128_LOGICALS(u32,4) +IVEC128_LOGICALS(s32,4) +IVEC128_LOGICALS(64,2) +IVEC128_LOGICALS(128,1) +#undef IVEC128_LOGICALS + + /********************************* Add & Sub ****************************************/ +#define IVEC128_ADD_SUB(vect,element,opsize) \ +inline I##vect##vec##element operator+ (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _mm_add_##opsize( a,b); } \ +inline I##vect##vec##element operator- (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _mm_sub_##opsize( a,b); } + +IVEC128_ADD_SUB(8,16, epi8) +IVEC128_ADD_SUB(u8,16, epi8) +IVEC128_ADD_SUB(s8,16, epi8) +IVEC128_ADD_SUB(16,8, epi16) +IVEC128_ADD_SUB(u16,8, epi16) +IVEC128_ADD_SUB(s16,8, epi16) +IVEC128_ADD_SUB(32,4, epi32) +IVEC128_ADD_SUB(u32,4, epi32) +IVEC128_ADD_SUB(s32,4, epi32) +IVEC128_ADD_SUB(64,2, epi64) +#undef IVEC128_ADD_SUB + + /************************* Conditional Select ******************************** + * version of: retval = (a OP b)? c : d; * + * Where OP is one of the possible comparision operators. * + * Example: r = select_eq(a,b,c,d); * + * if "member at position x of the vector a" == * + * "member at position x of vector b" * + * assign the corresponding member in r from c, else assign from d. * + ************************* Conditional Select ********************************/ + +#define IVEC128_SELECT(vect12,vect34,element,selop) \ + inline I##vect34##vec##element select_##selop ( \ + const I##vect12##vec##element &a, \ + const I##vect12##vec##element &b, \ + const I##vect34##vec##element &c, \ + const I##vect34##vec##element &d) \ +{ \ + I##vect12##vec##element mask = cmp##selop(a,b); \ + return ( I##vect34##vec##element (mask & c ) | \ + I##vect34##vec##element ((_mm_andnot_si128(mask, d )))); \ +} + +IVEC128_SELECT(8,s8,16,eq) +IVEC128_SELECT(8,u8,16,eq) +IVEC128_SELECT(8,8,16,eq) +IVEC128_SELECT(8,s8,16,neq) +IVEC128_SELECT(8,u8,16,neq) +IVEC128_SELECT(8,8,16,neq) + +IVEC128_SELECT(16,s16,8,eq) +IVEC128_SELECT(16,u16,8,eq) +IVEC128_SELECT(16,16,8,eq) +IVEC128_SELECT(16,s16,8,neq) +IVEC128_SELECT(16,u16,8,neq) +IVEC128_SELECT(16,16,8,neq) + +IVEC128_SELECT(32,s32,4,eq) +IVEC128_SELECT(32,u32,4,eq) +IVEC128_SELECT(32,32,4,eq) +IVEC128_SELECT(32,s32,4,neq) +IVEC128_SELECT(32,u32,4,neq) +IVEC128_SELECT(32,32,4,neq) + +IVEC128_SELECT(s8,s8,16,gt) +IVEC128_SELECT(s8,u8,16,gt) +IVEC128_SELECT(s8,8,16,gt) +IVEC128_SELECT(s8,s8,16,lt) +IVEC128_SELECT(s8,u8,16,lt) +IVEC128_SELECT(s8,8,16,lt) + +IVEC128_SELECT(s16,s16,8,gt) +IVEC128_SELECT(s16,u16,8,gt) +IVEC128_SELECT(s16,16,8,gt) +IVEC128_SELECT(s16,s16,8,lt) +IVEC128_SELECT(s16,u16,8,lt) +IVEC128_SELECT(s16,16,8,lt) + + +#undef IVEC128_SELECT + + +class F64vec2 +{ +protected: + __m128d vec; +public: + + /* Constructors: __m128d, 2 doubles */ + F64vec2() {} + + /* initialize 2 DP FP with __m128d data type */ + F64vec2(__m128d m) { vec = m;} + + /* initialize 2 DP FPs with 2 doubles */ + F64vec2(double d1, double d0) { vec= _mm_set_pd(d1,d0); } + + /* Explicitly initialize each of 2 DP FPs with same double */ + EXPLICIT F64vec2(double d) { vec = _mm_set1_pd(d); } + + /* Conversion functions */ + operator __m128d() const { return vec; } /* Convert to __m128d */ + + /* Logical Operators */ + friend F64vec2 operator &(const F64vec2 &a, const F64vec2 &b) { return _mm_and_pd(a,b); } + friend F64vec2 operator |(const F64vec2 &a, const F64vec2 &b) { return _mm_or_pd(a,b); } + friend F64vec2 operator ^(const F64vec2 &a, const F64vec2 &b) { return _mm_xor_pd(a,b); } + + /* Arithmetic Operators */ + friend F64vec2 operator +(const F64vec2 &a, const F64vec2 &b) { return _mm_add_pd(a,b); } + friend F64vec2 operator -(const F64vec2 &a, const F64vec2 &b) { return _mm_sub_pd(a,b); } + friend F64vec2 operator *(const F64vec2 &a, const F64vec2 &b) { return _mm_mul_pd(a,b); } + friend F64vec2 operator /(const F64vec2 &a, const F64vec2 &b) { return _mm_div_pd(a,b); } + + F64vec2& operator +=(const F64vec2 &a) { return *this = _mm_add_pd(vec,a); } + F64vec2& operator -=(const F64vec2 &a) { return *this = _mm_sub_pd(vec,a); } + F64vec2& operator *=(const F64vec2 &a) { return *this = _mm_mul_pd(vec,a); } + F64vec2& operator /=(const F64vec2 &a) { return *this = _mm_div_pd(vec,a); } + F64vec2& operator &=(const F64vec2 &a) { return *this = _mm_and_pd(vec,a); } + F64vec2& operator |=(const F64vec2 &a) { return *this = _mm_or_pd(vec,a); } + F64vec2& operator ^=(const F64vec2 &a) { return *this = _mm_xor_pd(vec,a); } + + /* Horizontal Add */ + friend double add_horizontal(const F64vec2 &a) + { + F64vec2 ftemp = _mm_add_sd(a,_mm_shuffle_pd(a, a, 1)); + return _mm_cvtsd_f64(ftemp); + } + + /* And Not */ + friend F64vec2 andnot(const F64vec2 &a, const F64vec2 &b) { return _mm_andnot_pd(a,b); } + + /* Square Root */ + friend F64vec2 sqrt(const F64vec2 &a) { return _mm_sqrt_pd(a); } + + /* Compares: Mask is returned */ + /* Macros expand to all compare intrinsics. Example: + friend F64vec2 cmpeq(const F64vec2 &a, const F64vec2 &b) + { return _mm_cmpeq_ps(a,b);} */ + #define F64vec2_COMP(op) \ + friend F64vec2 cmp##op (const F64vec2 &a, const F64vec2 &b) { return _mm_cmp##op##_pd(a,b); } + F64vec2_COMP(eq) /* expanded to cmpeq(a,b) */ + F64vec2_COMP(lt) /* expanded to cmplt(a,b) */ + F64vec2_COMP(le) /* expanded to cmple(a,b) */ + F64vec2_COMP(gt) /* expanded to cmpgt(a,b) */ + F64vec2_COMP(ge) /* expanded to cmpge(a,b) */ + F64vec2_COMP(ngt) /* expanded to cmpngt(a,b) */ + F64vec2_COMP(nge) /* expanded to cmpnge(a,b) */ + F64vec2_COMP(neq) /* expanded to cmpneq(a,b) */ + F64vec2_COMP(nlt) /* expanded to cmpnlt(a,b) */ + F64vec2_COMP(nle) /* expanded to cmpnle(a,b) */ + #undef F64vec2_COMP + + /* Min and Max */ + friend F64vec2 simd_min(const F64vec2 &a, const F64vec2 &b) { return _mm_min_pd(a,b); } + friend F64vec2 simd_max(const F64vec2 &a, const F64vec2 &b) { return _mm_max_pd(a,b); } + + /* Absolute value */ + friend F64vec2 abs(const F64vec2 &a) + { + return _mm_and_pd(a, _f64vec2_abs_mask); + } + + /* Compare lower DP FP values */ + #define F64vec2_COMI(op) \ + friend int comi##op (const F64vec2 &a, const F64vec2 &b) { return _mm_comi##op##_sd(a,b); } + F64vec2_COMI(eq) /* expanded to comieq(a,b) */ + F64vec2_COMI(lt) /* expanded to comilt(a,b) */ + F64vec2_COMI(le) /* expanded to comile(a,b) */ + F64vec2_COMI(gt) /* expanded to comigt(a,b) */ + F64vec2_COMI(ge) /* expanded to comige(a,b) */ + F64vec2_COMI(neq) /* expanded to comineq(a,b) */ + #undef F64vec2_COMI + + /* Compare lower DP FP values */ + #define F64vec2_UCOMI(op) \ + friend int ucomi##op (const F64vec2 &a, const F64vec2 &b) { return _mm_ucomi##op##_sd(a,b); } + F64vec2_UCOMI(eq) /* expanded to ucomieq(a,b) */ + F64vec2_UCOMI(lt) /* expanded to ucomilt(a,b) */ + F64vec2_UCOMI(le) /* expanded to ucomile(a,b) */ + F64vec2_UCOMI(gt) /* expanded to ucomigt(a,b) */ + F64vec2_UCOMI(ge) /* expanded to ucomige(a,b) */ + F64vec2_UCOMI(neq) /* expanded to ucomineq(a,b) */ + #undef F64vec2_UCOMI + + /* Debug Features */ +#if defined (_ENABLE_VEC_DEBUG) + /* Output */ + friend std::ostream & operator<<(std::ostream & os, const F64vec2 &a) + { + /* To use: cout << "Elements of F64vec2 fvec are: " << fvec; */ + double *dp = (double*)&a; + os << "[1]:" << *(dp+1) + << " [0]:" << *dp; + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + /* Element Access Only, no modifications to elements*/ + const double& operator[](int i) const + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 1)); /* User should only access elements 0-1 */ + double *dp = (double*)&vec; + return *(dp+i); + } + /* Element Access and Modification*/ + double& operator[](int i) + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 1)); /* User should only access elements 0-1 */ + double *dp = (double*)&vec; + return *(dp+i); + } +}; + + /* Miscellaneous */ + +/* Interleave low order data elements of a and b into destination */ +inline F64vec2 unpack_low(const F64vec2 &a, const F64vec2 &b) +{ return _mm_unpacklo_pd(a, b); } + +/* Interleave high order data elements of a and b into target */ +inline F64vec2 unpack_high(const F64vec2 &a, const F64vec2 &b) +{ return _mm_unpackhi_pd(a, b); } + +/* Move Mask to Integer returns 4 bit mask formed of most significant bits of a */ +inline int move_mask(const F64vec2 &a) +{ return _mm_movemask_pd(a);} + + /* Data Motion Functions */ + +/* Load Unaligned loadu_pd: Unaligned */ +inline void loadu(F64vec2 &a, double *p) +{ a = _mm_loadu_pd(p); } + +/* Store Temporal storeu_pd: Unaligned */ +inline void storeu(double *p, const F64vec2 &a) +{ _mm_storeu_pd(p, a); } + + /* Cacheability Support */ + +/* Non-Temporal Store */ +inline void store_nta(double *p, F64vec2 &a) +{ _mm_stream_pd(p,a);} + +#define F64vec2_SELECT(op) \ +inline F64vec2 select_##op (const F64vec2 &a, const F64vec2 &b, const F64vec2 &c, const F64vec2 &d) \ +{ \ + F64vec2 mask = _mm_cmp##op##_pd(a,b); \ + return( (mask & c) | F64vec2((_mm_andnot_pd(mask,d)))); \ +} +F64vec2_SELECT(eq) /* generates select_eq(a,b) */ +F64vec2_SELECT(lt) /* generates select_lt(a,b) */ +F64vec2_SELECT(le) /* generates select_le(a,b) */ +F64vec2_SELECT(gt) /* generates select_gt(a,b) */ +F64vec2_SELECT(ge) /* generates select_ge(a,b) */ +F64vec2_SELECT(neq) /* generates select_neq(a,b) */ +F64vec2_SELECT(nlt) /* generates select_nlt(a,b) */ +F64vec2_SELECT(nle) /* generates select_nle(a,b) */ +#undef F64vec2_SELECT + +/* Convert the lower DP FP value of a to a 32 bit signed integer using Truncate*/ +inline int F64vec2ToInt(const F64vec2 &a) +{ + + return _mm_cvttsd_si32(a); + +} + +/* Convert the 4 SP FP values of a to DP FP values */ +inline F64vec2 F32vec4ToF64vec2(const F32vec4 &a) +{ + return _mm_cvtps_pd(a); +} + +/* Convert the 2 DP FP values of a to SP FP values */ +inline F32vec4 F64vec2ToF32vec4(const F64vec2 &a) +{ + return _mm_cvtpd_ps(a); +} + +/* Convert the signed int in b to a DP FP value. Upper DP FP value in a passed through */ +inline F64vec2 IntToF64vec2(const F64vec2 &a, int b) +{ + return _mm_cvtsi32_sd(a,b); +} + +#pragma pack(pop) /* 16-B aligned */ + + /******************************************************************************/ + /************** Interface classes for Intel(R) AVX intrinsics *****************/ + /******************************************************************************/ + +/* + * class F32vec8 + * + * Represents 256-bit vector composed of 8 single precision floating point elements. + */ +class F32vec8 +{ +protected: + __m256 vec; + +public: + + /* Constructors: __m256, 8 floats, 1 float */ + F32vec8() {} + + /* initialize 8 SP FP with __m256 data type */ + F32vec8(__m256 m) { vec = m; } + + /* initialize 8 SP FPs with 8 floats */ + F32vec8(float f7, float f6, float f5, float f4, float f3, float f2, float f1, float f0) + { + vec = _mm256_set_ps(f7,f6,f5,f4,f3,f2,f1,f0); + } + + /* Explicitly initialize each of 8 SP FPs with same float */ + EXPLICIT F32vec8(float f) { vec = _mm256_set1_ps(f); } + + /* Explicitly initialize each of 8 SP FPs with same double */ + EXPLICIT F32vec8(double d) { vec = _mm256_set1_ps((float) d); } + + /* Assignment operations */ + F32vec8& operator =(float f) + { + vec = _mm256_set1_ps(f); + return *this; + } + + F32vec8& operator =(double d) + { + vec = _mm256_set1_ps((float) d); + return *this; + } + + /* Conversion functions */ + operator __m256() const { return vec; } + + /* Logical Operators */ + friend F32vec8 operator &(const F32vec8 &a, const F32vec8 &b) { return _mm256_and_ps(a,b); } + friend F32vec8 operator |(const F32vec8 &a, const F32vec8 &b) { return _mm256_or_ps(a,b); } + friend F32vec8 operator ^(const F32vec8 &a, const F32vec8 &b) { return _mm256_xor_ps(a,b); } + + /* Arithmetic Operators */ + friend F32vec8 operator +(const F32vec8 &a, const F32vec8 &b) { return _mm256_add_ps(a,b); } + friend F32vec8 operator -(const F32vec8 &a, const F32vec8 &b) { return _mm256_sub_ps(a,b); } + friend F32vec8 operator *(const F32vec8 &a, const F32vec8 &b) { return _mm256_mul_ps(a,b); } + friend F32vec8 operator /(const F32vec8 &a, const F32vec8 &b) { return _mm256_div_ps(a,b); } + + F32vec8& operator +=(const F32vec8 &a) { return *this = _mm256_add_ps(vec,a); } + F32vec8& operator -=(const F32vec8 &a) { return *this = _mm256_sub_ps(vec,a); } + F32vec8& operator *=(const F32vec8 &a) { return *this = _mm256_mul_ps(vec,a); } + F32vec8& operator /=(const F32vec8 &a) { return *this = _mm256_div_ps(vec,a); } + F32vec8& operator &=(const F32vec8 &a) { return *this = _mm256_and_ps(vec,a); } + F32vec8& operator |=(const F32vec8 &a) { return *this = _mm256_or_ps(vec,a); } + F32vec8& operator ^=(const F32vec8 &a) { return *this = _mm256_xor_ps(vec,a); } + + /* Horizontal Add */ + friend float add_horizontal(const F32vec8 &a) + { + F32vec8 temp = _mm256_add_ps(a, _mm256_permute_ps(a, 0xee)); + temp = _mm256_add_ps(temp, _mm256_movehdup_ps(temp)); + return _mm_cvtss_f32(_mm_add_ss(_mm256_castps256_ps128(temp), _mm256_extractf128_ps(temp,1))); + } + + /* And Not */ + friend F32vec8 andnot(const F32vec8 &a, const F32vec8 &b) { return _mm256_andnot_ps(a,b); } + + /* Square Root */ + friend F32vec8 sqrt(const F32vec8 &a) { return _mm256_sqrt_ps(a); } + + /* Reciprocal */ + friend F32vec8 rcp(const F32vec8 &a) { return _mm256_rcp_ps(a); } + + /* Reciprocal Square Root */ + friend F32vec8 rsqrt(const F32vec8 &a) { return _mm256_rsqrt_ps(a); } + + /* + * NewtonRaphson Reciprocal + * [2 * rcpps(x) - (x * rcpps(x) * rcpps(x))] + */ + friend F32vec8 rcp_nr(const F32vec8 &a) + { + F32vec8 Ra0 = _mm256_rcp_ps(a); + return _mm256_sub_ps(_mm256_add_ps(Ra0, Ra0), _mm256_mul_ps(_mm256_mul_ps(Ra0, a), Ra0)); + } + + /* + * NewtonRaphson Reciprocal Square Root + * 0.5 * rsqrtps * (3 - x * rsqrtps(x) * rsqrtps(x)) + */ + friend F32vec8 rsqrt_nr(const F32vec8 &a) + { +#pragma warning(push) +#pragma warning(disable:4640) + static const F32vec8 fvecf0pt5(0.5f); + static const F32vec8 fvecf3pt0(3.0f); +#pragma warning(pop) + F32vec8 Ra0 = _mm256_rsqrt_ps(a); + return (fvecf0pt5 * Ra0) * (fvecf3pt0 - (a * Ra0) * Ra0); + + } + + /* Compares: Mask is returned */ + friend F32vec8 cmp_eq(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_EQ_OQ); } + friend F32vec8 cmp_lt(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_LT_OS); } + friend F32vec8 cmp_le(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_LE_OS); } + friend F32vec8 cmp_gt(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_GT_OS); } + friend F32vec8 cmp_ge(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_GE_OS); } + friend F32vec8 cmp_neq(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_NEQ_UQ); } + friend F32vec8 cmp_nlt(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_NLT_US); } + friend F32vec8 cmp_nle(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_NLE_US); } + friend F32vec8 cmp_ngt(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_NGT_US); } + friend F32vec8 cmp_nge(const F32vec8 &a, const F32vec8 &b) + { return _mm256_cmp_ps(a, b, _CMP_NGE_US); } + + /* Min and Max */ + friend F32vec8 simd_min(const F32vec8 &a, const F32vec8 &b) + { return _mm256_min_ps(a,b); } + friend F32vec8 simd_max(const F32vec8 &a, const F32vec8 &b) + { return _mm256_max_ps(a,b); } + + /* Absolute value */ + friend F32vec8 abs(const F32vec8 &a) + { + static const union + { + int i[8]; + __m256 m; + } __f32vec8_abs_mask = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, + 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; + return _mm256_and_ps(a, __f32vec8_abs_mask.m); + } + + /* Debug Features */ +#if defined (_ENABLE_VEC_DEBUG) + /* Output */ + friend DVEC_STD ostream & operator<<(DVEC_STD ostream &os, const F32vec8 &a) + { + /* To use: cout << "Elements of F32vec8 fvec are: " << fvec; */ + float *fp = (float*) &a; + os << "[7]:" << *(fp+7) + << " [6]:" << *(fp+6) + << " [5]:" << *(fp+5) + << " [4]:" << *(fp+4) + << " [3]:" << *(fp+3) + << " [2]:" << *(fp+2) + << " [1]:" << *(fp+1) + << " [0]:" << *fp; + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access Only, no modifications to elements*/ + const float& operator[](int i) const + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 7)); + float *fp = (float*)&vec; + return *(fp+i); + } + + /* Element Access and Modification*/ + float& operator[](int i) + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 7)); + float *fp = (float*)&vec; + return *(fp+i); + } +}; + + /* Miscellaneous */ + +/* Interleave low order data elements of a and b into destination */ +inline F32vec8 unpack_low(const F32vec8 &a, const F32vec8 &b){ + return _mm256_unpacklo_ps(a, b); } + +/* Interleave high order data elements of a and b into target */ +inline F32vec8 unpack_high(const F32vec8 &a, const F32vec8 &b){ + return _mm256_unpackhi_ps(a, b); } + +/* Move Mask to Integer returns 8 bit mask formed of most significant bits of a */ +inline int move_mask(const F32vec8 &a){ + return _mm256_movemask_ps(a); } + + /* Data Motion Functions */ + +/* Load Unaligned loadu_ps: Unaligned */ +inline void loadu(F32vec8 &a, const float *p){ + a = _mm256_loadu_ps(p); } + +/* Store Unaligned storeu_ps: Unaligned */ +inline void storeu(float *p, const F32vec8 &a){ + _mm256_storeu_ps(p, a); } + + /* Cacheability Support */ + +/* Non-Temporal Store */ +inline void store_nta(float *p, const F32vec8 &a){ + _mm256_stream_ps(p, a); } + + /* Conditional moves */ + +/* Masked load */ +inline void maskload(F32vec8 &a, const float *p, const F32vec8 &m){ + a = _mm256_maskload_ps(p, _mm256_castps_si256(m)); } + +inline void maskload(F32vec4 &a, const float *p, const F32vec4 &m){ + a = _mm_maskload_ps(p, _mm_castps_si128(m)); } + +/* Masked store */ +inline void maskstore(float *p, const F32vec8 &a, const F32vec8 &m){ + _mm256_maskstore_ps(p, _mm256_castps_si256(m), a); } + +inline void maskstore(float *p, const F32vec4 &a, const F32vec4 &m){ + _mm_maskstore_ps(p, _mm_castps_si128(m), a); } + + /* Conditional Selects */ + +inline F32vec8 select_eq(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_EQ_OQ)); } + +inline F32vec8 select_lt(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_LT_OS)); } + +inline F32vec8 select_le(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_LE_OS)); } + +inline F32vec8 select_gt(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_GT_OS)); } + +inline F32vec8 select_ge(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_GE_OS)); } + +inline F32vec8 select_neq(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_NEQ_UQ)); } + +inline F32vec8 select_nlt(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_NLT_US)); } + +inline F32vec8 select_nle(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_NLE_US)); } + +inline F32vec8 select_ngt(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_NGT_US)); } + +inline F32vec8 select_nge(const F32vec8 &a, const F32vec8 &b, const F32vec8 &c, const F32vec8 &d){ + return _mm256_blendv_ps(d, c, _mm256_cmp_ps(a, b, _CMP_NGE_US)); } + +/* + * class F64vec4 + * + * Represents 256-bit vector composed of 4 double precision floating point elements. + */ +class F64vec4 +{ +protected: + __m256d vec; + +public: + + /* Constructors: __m256d, 4 doubles */ + F64vec4() {} + + /* initialize 4 DP FP with __m256d data type */ + F64vec4(__m256d m) { vec = m; } + + /* initialize 4 DP FPs with 4 doubles */ + F64vec4(double d3, double d2, double d1, double d0) + { + vec = _mm256_set_pd(d3,d2,d1,d0); + } + + /* Explicitly initialize each of 4 DP FPs with same double */ + EXPLICIT F64vec4(double d) { vec = _mm256_set1_pd(d); } + + /* Conversion functions */ + operator __m256d() const { return vec; } + + /* Logical Operators */ + friend F64vec4 operator &(const F64vec4 &a, const F64vec4 &b) { return _mm256_and_pd(a,b); } + friend F64vec4 operator |(const F64vec4 &a, const F64vec4 &b) { return _mm256_or_pd(a,b); } + friend F64vec4 operator ^(const F64vec4 &a, const F64vec4 &b) { return _mm256_xor_pd(a,b); } + + /* Arithmetic Operators */ + friend F64vec4 operator +(const F64vec4 &a, const F64vec4 &b) { return _mm256_add_pd(a,b); } + friend F64vec4 operator -(const F64vec4 &a, const F64vec4 &b) { return _mm256_sub_pd(a,b); } + friend F64vec4 operator *(const F64vec4 &a, const F64vec4 &b) { return _mm256_mul_pd(a,b); } + friend F64vec4 operator /(const F64vec4 &a, const F64vec4 &b) { return _mm256_div_pd(a,b); } + + F64vec4& operator +=(const F64vec4 &a) { return *this = _mm256_add_pd(vec,a); } + F64vec4& operator -=(const F64vec4 &a) { return *this = _mm256_sub_pd(vec,a); } + F64vec4& operator *=(const F64vec4 &a) { return *this = _mm256_mul_pd(vec,a); } + F64vec4& operator /=(const F64vec4 &a) { return *this = _mm256_div_pd(vec,a); } + F64vec4& operator &=(const F64vec4 &a) { return *this = _mm256_and_pd(vec,a); } + F64vec4& operator |=(const F64vec4 &a) { return *this = _mm256_or_pd(vec,a); } + F64vec4& operator ^=(const F64vec4 &a) { return *this = _mm256_xor_pd(vec,a); } + + /* Horizontal Add */ + friend double add_horizontal(const F64vec4 &a) + { + F64vec4 temp = _mm256_add_pd(a, _mm256_permute_pd(a,0x05)); + return _mm_cvtsd_f64(_mm_add_sd(_mm256_castpd256_pd128(temp), _mm256_extractf128_pd(temp,1))); + } + + /* And Not */ + friend F64vec4 andnot(const F64vec4 &a, const F64vec4 &b) { return _mm256_andnot_pd(a,b); } + + /* Square Root */ + friend F64vec4 sqrt(const F64vec4 &a) { return _mm256_sqrt_pd(a); } + + /* Compares: Mask is returned */ + friend F64vec4 cmp_eq(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_EQ_OQ); } + friend F64vec4 cmp_lt(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_LT_OS); } + friend F64vec4 cmp_le(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_LE_OS); } + friend F64vec4 cmp_gt(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_GT_OS); } + friend F64vec4 cmp_ge(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_GE_OS); } + friend F64vec4 cmp_neq(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_NEQ_UQ); } + friend F64vec4 cmp_nlt(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_NLT_US); } + friend F64vec4 cmp_nle(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_NLE_US); } + friend F64vec4 cmp_ngt(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_NGT_US); } + friend F64vec4 cmp_nge(const F64vec4 &a, const F64vec4 &b) + { return _mm256_cmp_pd(a, b, _CMP_NGE_US); } + + /* Min and Max */ + friend F64vec4 simd_min(const F64vec4 &a, const F64vec4 &b) + { return _mm256_min_pd(a,b); } + friend F64vec4 simd_max(const F64vec4 &a, const F64vec4 &b) + { return _mm256_max_pd(a,b); } + + /* Absolute value */ + friend F64vec4 abs(const F64vec4 &a) + { + static const union + { + int i[8]; + __m256d m; + } __f64vec4_abs_mask = { 0xffffffff, 0x7fffffff, 0xffffffff, 0x7fffffff, + 0xffffffff, 0x7fffffff, 0xffffffff, 0x7fffffff}; + return _mm256_and_pd(a, __f64vec4_abs_mask.m); + } + + /* Debug Features */ +#if defined (_ENABLE_VEC_DEBUG) + /* Output */ + friend DVEC_STD ostream & operator<<(DVEC_STD ostream &os, const F64vec4 &a) + { + /* To use: cout << "Elements of F64vec4 fvec are: " << fvec; */ + double *dp = (double*) &a; + os << "[3]:" << *(dp+3) + << " [2]:" << *(dp+2) + << " [3]:" << *(dp+1) + << " [0]:" << *dp; + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access Only, no modifications to elements */ + const double& operator[](int i) const + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 3)); + double *dp = (double*)&vec; + return *(dp+i); + } + /* Element Access and Modification*/ + double& operator[](int i) + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 3)); + double *dp = (double*)&vec; + return *(dp+i); + } +}; + + /* Miscellaneous */ + +/* Interleave low order data elements of a and b into destination */ +inline F64vec4 unpack_low(const F64vec4 &a, const F64vec4 &b){ + return _mm256_unpacklo_pd(a, b); } + +/* Interleave high order data elements of a and b into target */ +inline F64vec4 unpack_high(const F64vec4 &a, const F64vec4 &b){ + return _mm256_unpackhi_pd(a, b); } + +/* Move Mask to Integer returns 4 bit mask formed of most significant bits of a */ +inline int move_mask(const F64vec4 &a){ + return _mm256_movemask_pd(a); } + + /* Data Motion Functions */ + +/* Load Unaligned loadu_pd: Unaligned */ +inline void loadu(F64vec4 &a, double *p){ + a = _mm256_loadu_pd(p); } + +/* Store Unaligned storeu_pd: Unaligned */ +inline void storeu(double *p, const F64vec4 &a){ + _mm256_storeu_pd(p, a); } + + /* Cacheability Support */ + +/* Non-Temporal Store */ +inline void store_nta(double *p, const F64vec4 &a){ + _mm256_stream_pd(p, a); } + + /* Conditional moves */ + +/* Masked load */ +inline void maskload(F64vec4 &a, const double *p, const F64vec4 &m){ + a = _mm256_maskload_pd(p, _mm256_castpd_si256(m)); } + +inline void maskload(F64vec2 &a, const double *p, const F64vec2 &m){ + a = _mm_maskload_pd(p, _mm_castpd_si128(m)); } + +/* Masked store */ +inline void maskstore(double *p, const F64vec4 &a, const F64vec4 &m){ + _mm256_maskstore_pd(p, _mm256_castpd_si256(m), a); } + +inline void maskstore(double *p, const F64vec2 &a, const F64vec2 &m){ + _mm_maskstore_pd(p, _mm_castpd_si128(m), a); } + + /* Conditional Selects */ + +inline F64vec4 select_eq(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_EQ_OQ)); } + +inline F64vec4 select_lt(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_LT_OS)); } + +inline F64vec4 select_le(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_LE_OS)); } + +inline F64vec4 select_gt(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_GT_OS)); } + +inline F64vec4 select_ge(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_GE_OS)); } + +inline F64vec4 select_neq(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_NEQ_UQ)); } + +inline F64vec4 select_nlt(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_NLT_US)); } + +inline F64vec4 select_nle(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_NLE_US)); } + +inline F64vec4 select_ngt(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_NGT_US)); } + +inline F64vec4 select_nge(const F64vec4 &a, const F64vec4 &b, const F64vec4 &c, const F64vec4 &d){ + return _mm256_blendv_pd(d, c, _mm256_cmp_pd(a, b, _CMP_NGE_US)); } + + /* Conversion Functions */ + +/* Convert the 4 SP FP values of a to 4 DP FP values */ +inline F64vec4 F32vec4ToF64vec4(const F32vec4 &a){ + return _mm256_cvtps_pd(a); } + +/* Convert the 4 DP FP values of a to 4 SP FP values */ +inline F32vec4 F64vec4ToF32vec8(const F64vec4 &a){ + return _mm256_cvtpd_ps(a); } + +#undef DVEC_DEFINE_OUTPUT_OPERATORS +#undef DVEC_STD + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* RC_INVOKED */ +#endif /* _DVEC_H_INCLUDED */ diff --git a/test_data/lots_of_files/eh.h b/test_data/lots_of_files/eh.h new file mode 100644 index 0000000..98edabc --- /dev/null +++ b/test_data/lots_of_files/eh.h @@ -0,0 +1,84 @@ +/*** +*eh.h - User include file for exception handling. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* User include file for exception handling. +* +* [Public] +* +****/ + +#pragma once + +#include <crtdefs.h> + +#ifndef _INC_EH +#define _INC_EH +#ifndef RC_INVOKED + +/* Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifndef __cplusplus +#error "eh.h is only for C++!" +#endif /* __cplusplus */ + +/* terminate_handler is the standard name; terminate_function is supported for historical reasons */ +#ifndef _M_CEE_PURE +typedef void (__cdecl *terminate_function)(); +typedef void (__cdecl *terminate_handler)(); +typedef void (__cdecl *unexpected_function)(); +typedef void (__cdecl *unexpected_handler)(); +#else /* _M_CEE_PURE */ +typedef void (__clrcall *terminate_function)(); +typedef void (__clrcall *terminate_handler)(); +typedef void (__clrcall *unexpected_function)(); +typedef void (__clrcall *unexpected_handler)(); +#endif /* _M_CEE_PURE */ + +#ifdef _M_CEE +typedef void (__clrcall *__terminate_function_m)(); +typedef void (__clrcall *__terminate_handler_m)(); +typedef void (__clrcall *__unexpected_function_m)(); +typedef void (__clrcall *__unexpected_handler_m)(); +#endif /* _M_CEE */ + +struct _EXCEPTION_POINTERS; +#ifndef _M_CEE_PURE +typedef void (__cdecl *_se_translator_function)(unsigned int, struct _EXCEPTION_POINTERS*); +#endif /* _M_CEE_PURE */ + +_CRTIMP __declspec(noreturn) void __cdecl terminate(void); +_CRTIMP __declspec(noreturn) void __cdecl unexpected(void); + +_CRTIMP int __cdecl _is_exception_typeof(_In_ const type_info &_Type, _In_ struct _EXCEPTION_POINTERS * _ExceptionPtr); + +#ifndef _M_CEE_PURE +/* only __clrcall versions provided by the MRT exist in pure */ +_CRTIMP terminate_function __cdecl set_terminate(_In_opt_ terminate_function _NewPtFunc); +extern "C" _CRTIMP terminate_function __cdecl _get_terminate(void); +_CRTIMP unexpected_function __cdecl set_unexpected(_In_opt_ unexpected_function _NewPtFunc); +extern "C" _CRTIMP unexpected_function __cdecl _get_unexpected(void); +#endif /* _M_CEE_PURE */ + +#ifndef _M_CEE_PURE +/* set_se_translator cannot be a managed implementation, and so cannot be called from _M_CEE_PURE code */ +_CRTIMP _se_translator_function __cdecl _set_se_translator(_In_opt_ _se_translator_function _NewPtFunc); +#endif /* _M_CEE_PURE */ +_CRTIMP bool __cdecl __uncaught_exception(); + +/* + * These overload helps in resolving NULL + */ +#ifdef _M_CEE +_CRTIMP terminate_function __cdecl set_terminate(_In_ int _Zero); +_CRTIMP unexpected_function __cdecl set_unexpected(_In_ int _Zero); +#endif /* _M_CEE */ + +#pragma pack(pop) +#endif /* RC_INVOKED */ +#endif /* _INC_EH */ diff --git a/test_data/lots_of_files/emmintrin.h b/test_data/lots_of_files/emmintrin.h new file mode 100644 index 0000000..9b7aef4 --- /dev/null +++ b/test_data/lots_of_files/emmintrin.h @@ -0,0 +1,415 @@ +/*** +*** Copyright (C) 1985-1999 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * emmintrin.h + * + * Principal header file for Willamette New Instruction intrinsics + * + * The intrinsics package can be used in 2 ways, based whether or not + * _EMM_FUNCTIONALITY is defined; if it is, the C implementation + * will be used (the "functional intrinsics"). + */ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_EMM +#define _INCLUDED_EMM + +#if defined (_M_CEE_PURE) + #error ERROR: EMM intrinsics not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +/* + * the __m128 & __m64 types are required for the intrinsics + */ +#include <xmmintrin.h> + +typedef union __declspec(intrin_type) _CRT_ALIGN(16) __m128i { + __int8 m128i_i8[16]; + __int16 m128i_i16[8]; + __int32 m128i_i32[4]; + __int64 m128i_i64[2]; + unsigned __int8 m128i_u8[16]; + unsigned __int16 m128i_u16[8]; + unsigned __int32 m128i_u32[4]; + unsigned __int64 m128i_u64[2]; +} __m128i; + +typedef struct __declspec(intrin_type) _CRT_ALIGN(16) __m128d { + double m128d_f64[2]; +} __m128d; + +/* + * Macro function for shuffle + */ +#define _MM_SHUFFLE2(x,y) (((x)<<1) | (y)) + + /*****************************************************/ + /* INTRINSICS FUNCTION PROTOTYPES START HERE */ + /*****************************************************/ + +#if defined __cplusplus +extern "C" { /* Begin "C" */ + /* Intrinsics use C name-mangling. */ +#endif /* defined __cplusplus */ + +/* + * DP, arithmetic + */ + +extern __m128d _mm_add_sd(__m128d _A, __m128d _B); +extern __m128d _mm_add_pd(__m128d _A, __m128d _B); +extern __m128d _mm_sub_sd(__m128d _A, __m128d _B); +extern __m128d _mm_sub_pd(__m128d _A, __m128d _B); +extern __m128d _mm_mul_sd(__m128d _A, __m128d _B); +extern __m128d _mm_mul_pd(__m128d _A, __m128d _B); +extern __m128d _mm_sqrt_sd(__m128d _A, __m128d _B); +extern __m128d _mm_sqrt_pd(__m128d _A); +extern __m128d _mm_div_sd(__m128d _A, __m128d _B); +extern __m128d _mm_div_pd(__m128d _A, __m128d _B); +extern __m128d _mm_min_sd(__m128d _A, __m128d _B); +extern __m128d _mm_min_pd(__m128d _A, __m128d _B); +extern __m128d _mm_max_sd(__m128d _A, __m128d _B); +extern __m128d _mm_max_pd(__m128d _A, __m128d _B); + +/* + * DP, logicals + */ + +extern __m128d _mm_and_pd(__m128d _A, __m128d _B); +extern __m128d _mm_andnot_pd(__m128d _A, __m128d _B); +extern __m128d _mm_or_pd(__m128d _A, __m128d _B); +extern __m128d _mm_xor_pd(__m128d _A, __m128d _B); + +/* + * DP, comparisons + */ + +extern __m128d _mm_cmpeq_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpeq_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmplt_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmplt_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmple_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmple_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpgt_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpgt_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpge_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpge_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpneq_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpneq_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpnlt_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpnlt_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpnle_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpnle_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpngt_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpngt_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpnge_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpnge_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpord_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpord_sd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpunord_pd(__m128d _A, __m128d _B); +extern __m128d _mm_cmpunord_sd(__m128d _A, __m128d _B); +extern int _mm_comieq_sd(__m128d _A, __m128d _B); +extern int _mm_comilt_sd(__m128d _A, __m128d _B); +extern int _mm_comile_sd(__m128d _A, __m128d _B); +extern int _mm_comigt_sd(__m128d _A, __m128d _B); +extern int _mm_comige_sd(__m128d _A, __m128d _B); +extern int _mm_comineq_sd(__m128d _A, __m128d _B); +extern int _mm_ucomieq_sd(__m128d _A, __m128d _B); +extern int _mm_ucomilt_sd(__m128d _A, __m128d _B); +extern int _mm_ucomile_sd(__m128d _A, __m128d _B); +extern int _mm_ucomigt_sd(__m128d _A, __m128d _B); +extern int _mm_ucomige_sd(__m128d _A, __m128d _B); +extern int _mm_ucomineq_sd(__m128d _A, __m128d _B); + +/* + * DP, converts + */ + +extern __m128d _mm_cvtepi32_pd(__m128i _A); +extern __m128i _mm_cvtpd_epi32(__m128d _A); +extern __m128i _mm_cvttpd_epi32(__m128d _A); +extern __m128 _mm_cvtepi32_ps(__m128i _A); +extern __m128i _mm_cvtps_epi32(__m128 _A); +extern __m128i _mm_cvttps_epi32(__m128 _A); +extern __m128 _mm_cvtpd_ps(__m128d _A); +extern __m128d _mm_cvtps_pd(__m128 _A); +extern __m128 _mm_cvtsd_ss(__m128 _A, __m128d _B); +extern __m128d _mm_cvtss_sd(__m128d _A, __m128 _B); + +extern int _mm_cvtsd_si32(__m128d _A); +extern int _mm_cvttsd_si32(__m128d _A); +extern __m128d _mm_cvtsi32_sd(__m128d _A, int _B); + +extern __m64 _mm_cvtpd_pi32(__m128d _A); +extern __m64 _mm_cvttpd_pi32(__m128d _A); +extern __m128d _mm_cvtpi32_pd(__m64 _A); + +/* + * DP, misc + */ + +extern __m128d _mm_unpackhi_pd(__m128d _A, __m128d _B); +extern __m128d _mm_unpacklo_pd(__m128d _A, __m128d _B); +extern int _mm_movemask_pd(__m128d _A); +extern __m128d _mm_shuffle_pd(__m128d _A, __m128d _B, int _I); + +/* + * DP, loads + */ + +extern __m128d _mm_load_pd(double const*_Dp); +extern __m128d _mm_load1_pd(double const*_Dp); +extern __m128d _mm_loadr_pd(double const*_Dp); +extern __m128d _mm_loadu_pd(double const*_Dp); +extern __m128d _mm_load_sd(double const*_Dp); +extern __m128d _mm_loadh_pd(__m128d _A, double const*_Dp); +extern __m128d _mm_loadl_pd(__m128d _A, double const*_Dp); + +/* + * DP, sets + */ + +extern __m128d _mm_set_sd(double _W); +extern __m128d _mm_set1_pd(double _A); +extern __m128d _mm_set_pd(double _Z, double _Y); +extern __m128d _mm_setr_pd(double _Y, double _Z); +extern __m128d _mm_setzero_pd(void); +extern __m128d _mm_move_sd(__m128d _A, __m128d _B); + +/* + * DP, stores + */ + +extern void _mm_store_sd(double *_Dp, __m128d _A); +extern void _mm_store1_pd(double *_Dp, __m128d _A); +extern void _mm_store_pd(double *_Dp, __m128d _A); +extern void _mm_storeu_pd(double *_Dp, __m128d _A); +extern void _mm_storer_pd(double *_Dp, __m128d _A); +extern void _mm_storeh_pd(double *_Dp, __m128d _A); +extern void _mm_storel_pd(double *_Dp, __m128d _A); + +/* + * Integer, arithmetic + */ + +extern __m128i _mm_add_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_add_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_add_epi32(__m128i _A, __m128i _B); +extern __m64 _mm_add_si64(__m64 _A, __m64 _B); +extern __m128i _mm_add_epi64(__m128i _A, __m128i _B); +extern __m128i _mm_adds_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_adds_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_adds_epu8(__m128i _A, __m128i _B); +extern __m128i _mm_adds_epu16(__m128i _A, __m128i _B); +extern __m128i _mm_avg_epu8(__m128i _A, __m128i _B); +extern __m128i _mm_avg_epu16(__m128i _A, __m128i _B); +extern __m128i _mm_madd_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_max_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_max_epu8(__m128i _A, __m128i _B); +extern __m128i _mm_min_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_min_epu8(__m128i _A, __m128i _B); +extern __m128i _mm_mulhi_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_mulhi_epu16(__m128i _A, __m128i _B); +extern __m128i _mm_mullo_epi16(__m128i _A, __m128i _B); +extern __m64 _mm_mul_su32(__m64 _A, __m64 _B); +extern __m128i _mm_mul_epu32(__m128i _A, __m128i _B); +extern __m128i _mm_sad_epu8(__m128i _A, __m128i _B); +extern __m128i _mm_sub_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_sub_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_sub_epi32(__m128i _A, __m128i _B); +extern __m64 _mm_sub_si64(__m64 _A, __m64 _B); +extern __m128i _mm_sub_epi64(__m128i _A, __m128i _B); +extern __m128i _mm_subs_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_subs_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_subs_epu8(__m128i _A, __m128i _B); +extern __m128i _mm_subs_epu16(__m128i _A, __m128i _B); + +/* + * Integer, logicals + */ + +extern __m128i _mm_and_si128(__m128i _A, __m128i _B); +extern __m128i _mm_andnot_si128(__m128i _A, __m128i _B); +extern __m128i _mm_or_si128(__m128i _A, __m128i _B); +extern __m128i _mm_xor_si128(__m128i _A, __m128i _B); + +/* + * Integer, shifts + */ + +extern __m128i _mm_slli_si128(__m128i _A, int _Imm); +extern __m128i _mm_slli_epi16(__m128i _A, int _Count); +extern __m128i _mm_sll_epi16(__m128i _A, __m128i _Count); +extern __m128i _mm_slli_epi32(__m128i _A, int _Count); +extern __m128i _mm_sll_epi32(__m128i _A, __m128i _Count); +extern __m128i _mm_slli_epi64(__m128i _A, int _Count); +extern __m128i _mm_sll_epi64(__m128i _A, __m128i _Count); +extern __m128i _mm_srai_epi16(__m128i _A, int _Count); +extern __m128i _mm_sra_epi16(__m128i _A, __m128i _Count); +extern __m128i _mm_srai_epi32(__m128i _A, int _Count); +extern __m128i _mm_sra_epi32(__m128i _A, __m128i _Count); +extern __m128i _mm_srli_si128(__m128i _A, int _Imm); +extern __m128i _mm_srli_epi16(__m128i _A, int _Count); +extern __m128i _mm_srl_epi16(__m128i _A, __m128i _Count); +extern __m128i _mm_srli_epi32(__m128i _A, int _Count); +extern __m128i _mm_srl_epi32(__m128i _A, __m128i _Count); +extern __m128i _mm_srli_epi64(__m128i _A, int _Count); +extern __m128i _mm_srl_epi64(__m128i _A, __m128i _Count); + +/* + * Integer, comparisons + */ + +extern __m128i _mm_cmpeq_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_cmpeq_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_cmpeq_epi32(__m128i _A, __m128i _B); +extern __m128i _mm_cmpgt_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_cmpgt_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_cmpgt_epi32(__m128i _A, __m128i _B); +extern __m128i _mm_cmplt_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_cmplt_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_cmplt_epi32(__m128i _A, __m128i _B); + +/* + * Integer, converts + */ + +extern __m128i _mm_cvtsi32_si128(int _A); +extern int _mm_cvtsi128_si32(__m128i _A); + +/* + * Integer, misc + */ + +extern __m128i _mm_packs_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_packs_epi32(__m128i _A, __m128i _B); +extern __m128i _mm_packus_epi16(__m128i _A, __m128i _B); +extern int _mm_extract_epi16(__m128i _A, int _Imm); +extern __m128i _mm_insert_epi16(__m128i _A, int _B, int _Imm); +extern int _mm_movemask_epi8(__m128i _A); +extern __m128i _mm_shuffle_epi32(__m128i _A, int _Imm); +extern __m128i _mm_shufflehi_epi16(__m128i _A, int _Imm); +extern __m128i _mm_shufflelo_epi16(__m128i _A, int _Imm); +extern __m128i _mm_unpackhi_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_unpackhi_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_unpackhi_epi32(__m128i _A, __m128i _B); +extern __m128i _mm_unpackhi_epi64(__m128i _A, __m128i _B); +extern __m128i _mm_unpacklo_epi8(__m128i _A, __m128i _B); +extern __m128i _mm_unpacklo_epi16(__m128i _A, __m128i _B); +extern __m128i _mm_unpacklo_epi32(__m128i _A, __m128i _B); +extern __m128i _mm_unpacklo_epi64(__m128i _A, __m128i _B); + +/* + * Integer, loads + */ + +extern __m128i _mm_load_si128(__m128i const*_P); +extern __m128i _mm_loadu_si128(__m128i const*_P); +extern __m128i _mm_loadl_epi64(__m128i const*_P); + +/* + * Integer, sets + */ + +extern __m128i _mm_set_epi64(__m64 _Q1, __m64 _Q0); +extern __m128i _mm_set_epi32(int _I3, int _I2, int _I1, int _I0); +extern __m128i _mm_set_epi16(short _W7, short _W6, short _W5, short _W4, + short _W3, short _W2, short _W1, short _W0); +extern __m128i _mm_set_epi8(char _B15, char _B14, char _B13, char _B12, + char _B11, char _B10, char _B9, char _B8, + char _B7, char _B6, char _B5, char _B4, + char _B3, char _B2, char _B1, char _B0); +extern __m128i _mm_set1_epi64(__m64 _Q); +extern __m128i _mm_set1_epi32(int _I); +extern __m128i _mm_set1_epi16(short _W); +extern __m128i _mm_set1_epi8(char _B); +extern __m128i _mm_setl_epi64(__m128i _Q); +extern __m128i _mm_setr_epi64(__m64 _Q0, __m64 _Q1); +extern __m128i _mm_setr_epi32(int _I0, int _I1, int _I2, int _I3); +extern __m128i _mm_setr_epi16(short _W0, short _W1, short _W2, short _W3, + short _W4, short _W5, short _W6, short _W7); +extern __m128i _mm_setr_epi8(char _B15, char _B14, char _B13, char _B12, + char _B11, char _B10, char _B9, char _B8, + char _B7, char _B6, char _B5, char _B4, + char _B3, char _B2, char _B1, char _B0); +extern __m128i _mm_setzero_si128(void); + +/* + * Integer, stores + */ + +extern void _mm_store_si128(__m128i *_P, __m128i _B); +extern void _mm_storeu_si128(__m128i *_P, __m128i _B); +extern void _mm_storel_epi64(__m128i *_P, __m128i _Q); +extern void _mm_maskmoveu_si128(__m128i _D, __m128i _N, char *_P); + +/* + * Integer, moves + */ + +extern __m128i _mm_move_epi64(__m128i _Q); +extern __m128i _mm_movpi64_epi64(__m64 _Q); +extern __m64 _mm_movepi64_pi64(__m128i _Q); + +/* + * Cacheability support + */ + +extern void _mm_stream_pd(double *_Dp, __m128d _A); +extern void _mm_stream_si128(__m128i *_P, __m128i _A); +extern void _mm_clflush(void const*_P); +extern void _mm_lfence(void); +extern void _mm_mfence(void); +extern void _mm_stream_si32(int *_P, int _I); +extern void _mm_pause(void); + +/* + * New convert to float + */ + +extern double _mm_cvtsd_f64(__m128d _A); + +/* + * Support for casting between various SP, DP, INT vector types. + * Note that these do no conversion of values, they just change + * the type. + */ + +extern __m128 _mm_castpd_ps(__m128d); +extern __m128i _mm_castpd_si128(__m128d); +extern __m128d _mm_castps_pd(__m128); +extern __m128i _mm_castps_si128(__m128); +extern __m128 _mm_castsi128_ps(__m128i); +extern __m128d _mm_castsi128_pd(__m128i); + +/* + * Support for 64-bit extension intrinsics + */ + +#if defined (_M_X64) +extern __int64 _mm_cvtsd_si64(__m128d); +extern __int64 _mm_cvttsd_si64(__m128d); +extern __m128d _mm_cvtsi64_sd(__m128d, __int64); +extern __m128i _mm_cvtsi64_si128(__int64); +extern __int64 _mm_cvtsi128_si64(__m128i); +/* Alternate intrinsic name definitions */ +#define _mm_stream_si64 _mm_stream_si64x +#endif /* defined (_M_X64) */ + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INCLUDED_EMM */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/errno.h b/test_data/lots_of_files/errno.h new file mode 100644 index 0000000..db9aa41 --- /dev/null +++ b/test_data/lots_of_files/errno.h @@ -0,0 +1,138 @@ +/*** +*errno.h - system wide error numbers (set by system calls) +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines the system-wide error numbers (set by +* system calls). Conforms to the XENIX standard. Extended +* for compatibility with Uniforum standard. +* [System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_ERRNO +#define _INC_ERRNO + +#include <crtdefs.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Declare reference to errno */ + +#ifndef _CRT_ERRNO_DEFINED +#define _CRT_ERRNO_DEFINED +_CRTIMP extern int * __cdecl _errno(void); +#define errno (*_errno()) + +errno_t __cdecl _set_errno(_In_ int _Value); +errno_t __cdecl _get_errno(_Out_ int * _Value); +#endif /* _CRT_ERRNO_DEFINED */ + +/* Error Codes */ + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define EDEADLK 36 +#define ENAMETOOLONG 38 +#define ENOLCK 39 +#define ENOSYS 40 +#define ENOTEMPTY 41 + +/* Error codes used in the Secure CRT functions */ + +#ifndef RC_INVOKED +#if !defined (_SECURECRT_ERRCODE_VALUES_DEFINED) +#define _SECURECRT_ERRCODE_VALUES_DEFINED +#define EINVAL 22 +#define ERANGE 34 +#define EILSEQ 42 +#define STRUNCATE 80 +#endif /* !defined (_SECURECRT_ERRCODE_VALUES_DEFINED) */ +#endif /* RC_INVOKED */ + +/* Support EDEADLOCK for compatibility with older MS-C versions */ +#define EDEADLOCK EDEADLK + +/* POSIX SUPPLEMENT */ +#define EADDRINUSE 100 +#define EADDRNOTAVAIL 101 +#define EAFNOSUPPORT 102 +#define EALREADY 103 +#define EBADMSG 104 +#define ECANCELED 105 +#define ECONNABORTED 106 +#define ECONNREFUSED 107 +#define ECONNRESET 108 +#define EDESTADDRREQ 109 +#define EHOSTUNREACH 110 +#define EIDRM 111 +#define EINPROGRESS 112 +#define EISCONN 113 +#define ELOOP 114 +#define EMSGSIZE 115 +#define ENETDOWN 116 +#define ENETRESET 117 +#define ENETUNREACH 118 +#define ENOBUFS 119 +#define ENODATA 120 +#define ENOLINK 121 +#define ENOMSG 122 +#define ENOPROTOOPT 123 +#define ENOSR 124 +#define ENOSTR 125 +#define ENOTCONN 126 +#define ENOTRECOVERABLE 127 +#define ENOTSOCK 128 +#define ENOTSUP 129 +#define EOPNOTSUPP 130 +#define EOTHER 131 +#define EOVERFLOW 132 +#define EOWNERDEAD 133 +#define EPROTO 134 +#define EPROTONOSUPPORT 135 +#define EPROTOTYPE 136 +#define ETIME 137 +#define ETIMEDOUT 138 +#define ETXTBSY 139 +#define EWOULDBLOCK 140 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_ERRNO */ diff --git a/test_data/lots_of_files/euler.cpp b/test_data/lots_of_files/euler.cpp new file mode 100644 index 0000000..ad30c54 --- /dev/null +++ b/test_data/lots_of_files/euler.cpp @@ -0,0 +1,93 @@ +/* + * YOUR INFO HERE! + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#define EULER_PROBLEM 14 + +#if EULER_PROBLEM == 1 +#include "multiples_of_3_and_5.cpp" +#elif EULER_PROBLEM == 2 +#include "even_fibonacci_numbers.cpp" +#elif EULER_PROBLEM == 3 +#include "largest_prime_factor.cpp" +#elif EULER_PROBLEM == 4 +#include "largest_palindrome_product.cpp" +#elif EULER_PROBLEM == 5 +#include "smallest_multiple.cpp" +#elif EULER_PROBLEM == 6 +#include "sum_square_difference.cpp" +#elif EULER_PROBLEM == 7 +#include "prime_10001st.cpp" +#elif EULER_PROBLEM == 8 +#include "largest_product_in_series.cpp" +#elif EULER_PROBLEM == 9 +#include "special_pythagorean_triplet.cpp" +#elif EULER_PROBLEM == 10 +#include "summation_of_primes.cpp" +#elif EULER_PROBLEM == 11 +#include "largest_product_in_grid.cpp" +#elif EULER_PROBLEM == 12 +#include "highly_divisible_triangular_number.cpp" +#elif EULER_PROBLEM == 13 +#include "large_sum.cpp" +#elif EULER_PROBLEM == 14 +#include "largest_collatz_sequence.cpp" +#endif + +Euler_Result dummy_test(Euler_In in){ + Euler_Result result = {}; + return result; +} + +#include <Windows.h> + +int time(){ + int result = 0; + + LARGE_INTEGER t; + if (QueryPerformanceCounter(&t)){ + result = (int)t.QuadPart; + } + + return result; +} + +int main(){ + Euler_In in; + Euler_Result answer; + int start, ms, dummy_ms; + + in = {}; + + start = time(); + answer = dummy_test(in); + dummy_ms = time() - start; + start = time(); + answer = euler_main(in); + ms = time() - start; + + printf("------RESULT------\n"); + euler_print(answer, in); + +#ifdef EULER_CHECK + printf("------CHECK-------\n"); + if (euler_check(answer, in)){ + printf("passed\n"); + } + else{ + printf("failed\n"); + } +#endif + + printf("------TIME--------\n"); + printf("TOTAL: %d\n", ms); + printf("DUMMY: %d\n", dummy_ms); + printf("ADJUSTED: %d\n", ms - dummy_ms); + + return 0; +} diff --git a/test_data/lots_of_files/even_fibonacci_numbers.cpp b/test_data/lots_of_files/even_fibonacci_numbers.cpp new file mode 100644 index 0000000..5ba5747 --- /dev/null +++ b/test_data/lots_of_files/even_fibonacci_numbers.cpp @@ -0,0 +1,70 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +int sum_even_fibonacci(int top){ + int counter = 0; + + int a, b, c; + a = 1; + b = 1; + c = 2; + + for (;c < top;){ + counter += c; + + a = b + c; + b = c + a; + c = a + b; + } +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{}; + +struct Euler_Result{ + int counter; +}; + +static const int TOP = 4000000; + +inline Euler_Result euler_main(Euler_In in){ + Euler_Result result; + result.counter = sum_even_fibonacci(TOP); + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.counter); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result; + + int counter = 0; + + int a, b, c; + a = 0; + b = 1; + c = 0; + + for (;c < TOP;){ + if (c % 2 == 0){ + counter += c; + } + c = a + b; + a = b; + b = c; + } + + printf("answer = %d\n", counter); + result = (counter == answer.counter); + + return result; +} +#endif + diff --git a/test_data/lots_of_files/excpt.h b/test_data/lots_of_files/excpt.h new file mode 100644 index 0000000..b912d58 --- /dev/null +++ b/test_data/lots_of_files/excpt.h @@ -0,0 +1,180 @@ +/*** +*excpt.h - defines exception values, types and routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains the definitions and prototypes for the compiler- +* dependent intrinsics, support functions and keywords which implement +* the structured exception handling extensions. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_EXCPT +#define _INC_EXCPT + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Exception disposition return values. + */ +typedef enum _EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} EXCEPTION_DISPOSITION; + + +/* + * Prototype for SEH support function. + */ + +#ifdef _M_IX86 + +/* + * Declarations to keep MS C 8 (386/486) compiler happy + */ +struct _EXCEPTION_RECORD; +struct _CONTEXT; + +EXCEPTION_DISPOSITION __cdecl _except_handler ( + _In_ struct _EXCEPTION_RECORD *_ExceptionRecord, + _In_ void * _EstablisherFrame, + _Inout_ struct _CONTEXT *_ContextRecord, + _Inout_ void * _DispatcherContext + ); + +#elif defined (_M_X64) || defined (_M_ARM) + +/* + * Declarations to keep AMD64 compiler happy + */ +struct _EXCEPTION_RECORD; +struct _CONTEXT; +struct _DISPATCHER_CONTEXT; + +#ifndef _M_CEE_PURE + +_CRTIMP EXCEPTION_DISPOSITION __C_specific_handler ( + _In_ struct _EXCEPTION_RECORD * ExceptionRecord, + _In_ void * EstablisherFrame, + _Inout_ struct _CONTEXT * ContextRecord, + _Inout_ struct _DISPATCHER_CONTEXT * DispatcherContext +); + +#endif /* _M_CEE_PURE */ + +#endif /* defined (_M_X64) || defined (_M_ARM) */ + + +/* + * Keywords and intrinsics for SEH + */ + +#define GetExceptionCode _exception_code +#define exception_code _exception_code +#define GetExceptionInformation (struct _EXCEPTION_POINTERS *)_exception_info +#define exception_info (struct _EXCEPTION_POINTERS *)_exception_info +#define AbnormalTermination _abnormal_termination +#define abnormal_termination _abnormal_termination + +unsigned long __cdecl _exception_code(void); +void * __cdecl _exception_info(void); +int __cdecl _abnormal_termination(void); + + +/* + * Legal values for expression in except(). + */ + +#define EXCEPTION_EXECUTE_HANDLER 1 +#define EXCEPTION_CONTINUE_SEARCH 0 +#define EXCEPTION_CONTINUE_EXECUTION -1 + +#ifdef _CRTBLD +/* + * for convenience, define a type name for a pointer to signal-handler + */ + +typedef void (__cdecl * _PHNDLR)(int); + +/* + * Exception-action table used by the C runtime to identify and dispose of + * exceptions corresponding to C runtime errors or C signals. + */ +struct _XCPT_ACTION { + + /* + * exception code or number. defined by the host OS. + */ + unsigned long XcptNum; + + /* + * signal code or number. defined by the C runtime. + */ + int SigNum; + + /* + * exception action code. either a special code or the address of + * a handler function. always determines how the exception filter + * should dispose of the exception. + */ + _PHNDLR XcptAction; +}; + +extern const struct _XCPT_ACTION _XcptActTab[]; + +/* + * number of entries in the exception-action table + */ +extern const int _XcptActTabCount; + +/* + * size of exception-action table (in bytes) + */ +extern const int _XcptActTabSize; + +/* + * index of the first floating point exception entry + */ +extern const int _First_FPE_Indx; + +/* + * number of FPE entries + */ +extern const int _Num_FPE; + +/* + * return values and prototype for the exception filter function used in the + * C startup + */ + +struct _EXCEPTION_POINTERS; + +int __cdecl __CppXcptFilter(_In_ unsigned long _ExceptionNum, _In_ struct _EXCEPTION_POINTERS * _ExceptionPtr); +int __cdecl _XcptFilter(_In_ unsigned long _ExceptionNum, _In_ struct _EXCEPTION_POINTERS * _ExceptionPtr); + +#endif /* _CRTBLD */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_EXCPT */ diff --git a/test_data/lots_of_files/exp.c b/test_data/lots_of_files/exp.c new file mode 100644 index 0000000..2d84098 --- /dev/null +++ b/test_data/lots_of_files/exp.c @@ -0,0 +1,41 @@ +/* + * YOUR INFO HERE! + */ + + +int combo(int n, int r){ + int data[2000]; + int rl; + int i,j; + + if (r == 0) return 1; + if (r == n) return 1; + rl = n - r; + if (r == 1) return n; + if (rl == 1) return n; + if (rl < r) r = rl; + r -= 2; + rl = n - r; + + if (r == 0){ +in0: + if (i == rl) goto out0; + j = j + i; + i = i + 1; + goto in0; +out0: + return j; + } + + i = 4; + j = 6; +in1: + data[i] = j; + if (i == rl) goto out1; + j = j + i; + i = i + 1; + goto in1; +out1: + +} + diff --git a/test_data/lots_of_files/fcntl.h b/test_data/lots_of_files/fcntl.h new file mode 100644 index 0000000..9a5d13a --- /dev/null +++ b/test_data/lots_of_files/fcntl.h @@ -0,0 +1,84 @@ +/*** +*fcntl.h - file control options used by open() +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines constants for the file control options used +* by the _open() function. +* [System V] +* +* [Public] +* +****/ + +#pragma once + +#include <crtdefs.h> + +#ifndef _INC_FCNTL +#define _INC_FCNTL + +#define _O_RDONLY 0x0000 /* open for reading only */ +#define _O_WRONLY 0x0001 /* open for writing only */ +#define _O_RDWR 0x0002 /* open for reading and writing */ +#define _O_APPEND 0x0008 /* writes done at eof */ + +#define _O_CREAT 0x0100 /* create and open file */ +#define _O_TRUNC 0x0200 /* open and truncate */ +#define _O_EXCL 0x0400 /* open only if file doesn't already exist */ + +/* O_TEXT files have <cr><lf> sequences translated to <lf> on read()'s, +** and <lf> sequences translated to <cr><lf> on write()'s +*/ + +#define _O_TEXT 0x4000 /* file mode is text (translated) */ +#define _O_BINARY 0x8000 /* file mode is binary (untranslated) */ +#define _O_WTEXT 0x10000 /* file mode is UTF16 (translated) */ +#define _O_U16TEXT 0x20000 /* file mode is UTF16 no BOM (translated) */ +#define _O_U8TEXT 0x40000 /* file mode is UTF8 no BOM (translated) */ + +/* macro to translate the C 2.0 name used to force binary mode for files */ + +#define _O_RAW _O_BINARY + +/* Open handle inherit bit */ + +#define _O_NOINHERIT 0x0080 /* child process doesn't inherit file */ + +/* Temporary file bit - file is deleted when last handle is closed */ + +#define _O_TEMPORARY 0x0040 /* temporary file bit */ + +/* temporary access hint */ + +#define _O_SHORT_LIVED 0x1000 /* temporary storage file, try not to flush */ + +/* directory access hint */ + +#define _O_OBTAIN_DIR 0x2000 /* get information about a directory */ + +/* sequential/random access hints */ + +#define _O_SEQUENTIAL 0x0020 /* file access is primarily sequential */ +#define _O_RANDOM 0x0010 /* file access is primarily random */ + +#if !__STDC__ +/* Non-ANSI names for compatibility */ +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDWR +#define O_APPEND _O_APPEND +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC +#define O_EXCL _O_EXCL +#define O_TEXT _O_TEXT +#define O_BINARY _O_BINARY +#define O_RAW _O_BINARY +#define O_TEMPORARY _O_TEMPORARY +#define O_NOINHERIT _O_NOINHERIT +#define O_SEQUENTIAL _O_SEQUENTIAL +#define O_RANDOM _O_RANDOM +#endif /* !__STDC__ */ + +#endif /* _INC_FCNTL */ diff --git a/test_data/lots_of_files/fenv.h b/test_data/lots_of_files/fenv.h new file mode 100644 index 0000000..dd43914 --- /dev/null +++ b/test_data/lots_of_files/fenv.h @@ -0,0 +1,147 @@ +/*** +*fenv.h - definitions and declarations for floating point environment library +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains constant definitions and external subroutine +* declarations for the floating point environment subroutine library. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _FENV +#define _FENV + +#include <float.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if !defined(_M_CEE) || defined(_CRTBLD) + +typedef unsigned long fexcept_t; +typedef struct fenv_t +{ + unsigned long _Fe_ctl, _Fe_stat; +} fenv_t; + +#define FE_INEXACT _SW_INEXACT /* _EM_INEXACT 0x00000001 inexact (precision) */ +#define FE_UNDERFLOW _SW_UNDERFLOW /* _EM_UNDERFLOW 0x00000002 underflow */ +#define FE_OVERFLOW _SW_OVERFLOW /* _EM_OVERFLOW 0x00000004 overflow */ +#define FE_DIVBYZERO _SW_ZERODIVIDE /* _EM_ZERODIVIDE 0x00000008 zero divide */ +#define FE_INVALID _SW_INVALID /* _EM_INVALID 0x00000010 invalid */ + +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +_CRTIMP int __cdecl fegetenv(_Inout_ fenv_t *); +_CRTIMP int __cdecl fesetenv(_In_ const fenv_t *); +_CRTIMP int __cdecl feclearexcept(_In_ int); +_CRTIMP int __cdecl feholdexcept(_Inout_ fenv_t *); +_CRTIMP int __cdecl fetestexcept(_In_ int); +_CRTIMP int __cdecl fegetexceptflag(_Inout_ fexcept_t *, _In_ int); +_CRTIMP int __cdecl fesetexceptflag(_In_ const fexcept_t *, _In_ int); + +__declspec(selectany) extern const fenv_t _Fenv0 = {0, 0}; + +#define FE_DFL_ENV (&_Fenv0) + +#endif /* !defined(_M_CEE) || defined(_CRTBLD) */ + +#define FE_TONEAREST 0x0000 +#define FE_UPWARD 0x0100 +#define FE_DOWNWARD 0x0200 +#define FE_TOWARDZERO 0x0300 + +#define FE_ROUND_MASK 0x0300 + +_CRTIMP int __cdecl fegetround(void); +_CRTIMP int __cdecl fesetround(_In_ int); + +#if !defined(_M_CEE) && !defined(_CRTBLD) + +/* +* feraiseexcept is inline in this header so that it will be compiled with +* the /arch setting specified in the consuming application, rather than the +* /arch:IA32 setting which is the default for the CRT library itself. +* +* feupdateenv is inline because it calls feraiseexcept. +*/ +__inline int __CRTDECL feraiseexcept(_In_ int _Except) +{ + #define __DBL_BIG 1e+300 /* may raise inexact too */ + + static struct + { + int _Except_Val; + double _Num, _Denom; + } const _Table[] = + { /* raise exception by evaluating num / denom */ + {FE_INVALID, 0.0, 0.0}, + {FE_DIVBYZERO, 1.0, 0.0}, + {FE_OVERFLOW, __DBL_BIG, 1.0 / __DBL_BIG}, + {FE_UNDERFLOW, 1.0 / __DBL_BIG, __DBL_BIG}, + {FE_INEXACT, 2.0, 3.0} + }; + + fexcept_t _Flags = 0; + double _Ans = 0.0; + int _N; + + if ((_Except &= FE_ALL_EXCEPT) == 0) + { + return 0; + } + + /* get the current status flags */ + fegetexceptflag(&_Flags, FE_ALL_EXCEPT); + + _Except &= ~_Flags; + _Flags |= _Except; + + /* set the new status flags */ + fesetexceptflag(&_Flags, FE_ALL_EXCEPT); + + /* raise the exceptions not masked */ + for (_N = 0; _N < sizeof(_Table) / sizeof(_Table[0]); ++_N) + { + if ((_Except & _Table[_N]._Except_Val) != 0) + { + _Ans = _Table[_N]._Num / _Table[_N]._Denom; + } + } + + return 0; +} + +__inline int __CRTDECL feupdateenv(_In_ const fenv_t *_Penv) +{ + int _Except = fetestexcept(FE_ALL_EXCEPT); + + if (fesetenv(_Penv) != 0 || feraiseexcept(_Except) != 0) + { + return 1; + } + + return 0; +} + +#endif /* !defined(_M_CEE) && !defined(_CRTBLD) */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _FENV */ diff --git a/test_data/lots_of_files/float.h b/test_data/lots_of_files/float.h new file mode 100644 index 0000000..c166859 --- /dev/null +++ b/test_data/lots_of_files/float.h @@ -0,0 +1,342 @@ +/*** +*float.h - constants for floating point values +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains defines for a number of implementation dependent +* values which are commonly used by sophisticated numerical (floating +* point) programs. +* [ANSI] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_FLOAT +#define _INC_FLOAT + +#include <crtdefs.h> +#include <crtwrn.h> + +/* Define _CRT_MANAGED_FP_DEPRECATE */ +#ifndef _CRT_MANAGED_FP_DEPRECATE +#ifdef _CRT_MANAGED_FP_NO_DEPRECATE +#define _CRT_MANAGED_FP_DEPRECATE +#else /* _CRT_MANAGED_FP_NO_DEPRECATE */ +#if defined (_M_CEE) +#define _CRT_MANAGED_FP_DEPRECATE _CRT_DEPRECATE_TEXT("Direct floating point control is not supported or reliable from within managed code. ") +#else /* defined (_M_CEE) */ +#define _CRT_MANAGED_FP_DEPRECATE +#endif /* defined (_M_CEE) */ +#endif /* _CRT_MANAGED_FP_NO_DEPRECATE */ +#endif /* _CRT_MANAGED_FP_DEPRECATE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* +Define the floating point precision used. + +For x86, results are in double precision (unless /arch:sse2 + is used, in which case results are in source precision). + +For x64 and ARM, results are in source precision. + +If the compiler is invoked with /fp:fast, the compiler is + allowed to use the fastest precision and even mix within + a single function, so precision is indeterminable. + +Note that manipulating the floating point behavior using the + float_control/fenv_access/fp_contract #pragmas may alter the + actual floating point evaluation method, which may in turn + invalidate the value of FLT_EVAL_METHOD. +*/ + +#if defined(_M_FP_FAST) +#define FLT_EVAL_METHOD -1 +#else +#if defined(_M_IX86) +#if _M_IX86_FP == 2 +#define FLT_EVAL_METHOD 0 +#else +#define FLT_EVAL_METHOD 2 +#endif +#else +#define FLT_EVAL_METHOD 0 +#endif +#endif + +#define DBL_DIG 15 /* # of decimal digits of precision */ +#define DBL_EPSILON 2.2204460492503131e-016 /* smallest such that 1.0+DBL_EPSILON != 1.0 */ +#define DBL_MANT_DIG 53 /* # of bits in mantissa */ +#define DBL_MAX 1.7976931348623158e+308 /* max value */ +#define DBL_MAX_10_EXP 308 /* max decimal exponent */ +#define DBL_MAX_EXP 1024 /* max binary exponent */ +#define DBL_MIN 2.2250738585072014e-308 /* min positive value */ +#define DBL_MIN_10_EXP (-307) /* min decimal exponent */ +#define DBL_MIN_EXP (-1021) /* min binary exponent */ +#define _DBL_RADIX 2 /* exponent radix */ +#define _DBL_ROUNDS 1 /* addition rounding: near */ + +#define FLT_DIG 6 /* # of decimal digits of precision */ +#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */ +#define FLT_GUARD 0 +#define FLT_MANT_DIG 24 /* # of bits in mantissa */ +#define FLT_MAX 3.402823466e+38F /* max value */ +#define FLT_MAX_10_EXP 38 /* max decimal exponent */ +#define FLT_MAX_EXP 128 /* max binary exponent */ +#define FLT_MIN 1.175494351e-38F /* min positive value */ +#define FLT_MIN_10_EXP (-37) /* min decimal exponent */ +#define FLT_MIN_EXP (-125) /* min binary exponent */ +#define FLT_NORMALIZE 0 +#define FLT_RADIX 2 /* exponent radix */ +#define FLT_ROUNDS 1 /* addition rounding: near */ + +#define LDBL_DIG DBL_DIG /* # of decimal digits of precision */ +#define LDBL_EPSILON DBL_EPSILON /* smallest such that 1.0+LDBL_EPSILON != 1.0 */ +#define LDBL_MANT_DIG DBL_MANT_DIG /* # of bits in mantissa */ +#define LDBL_MAX DBL_MAX /* max value */ +#define LDBL_MAX_10_EXP DBL_MAX_10_EXP /* max decimal exponent */ +#define LDBL_MAX_EXP DBL_MAX_EXP /* max binary exponent */ +#define LDBL_MIN DBL_MIN /* min positive value */ +#define LDBL_MIN_10_EXP DBL_MIN_10_EXP /* min decimal exponent */ +#define LDBL_MIN_EXP DBL_MIN_EXP /* min binary exponent */ +#define _LDBL_RADIX DBL_RADIX /* exponent radix */ +#define _LDBL_ROUNDS DBL_ROUNDS /* addition rounding: near */ + +#define DECIMAL_DIG 10 /* float rounded to decimal and back */ + +/* Function prototypes */ + +/* Reading or writing the floating point control/status words is not supported in managed code */ + +_CRT_MANAGED_FP_DEPRECATE _CRTIMP unsigned int __cdecl _clearfp(void); +#pragma warning(push) +#pragma warning(disable: 4141) +_CRT_MANAGED_FP_DEPRECATE _CRT_INSECURE_DEPRECATE(_controlfp_s) _CRTIMP unsigned int __cdecl _controlfp(_In_ unsigned int _NewValue,_In_ unsigned int _Mask); +#pragma warning(pop) +_CRT_MANAGED_FP_DEPRECATE _CRTIMP void __cdecl _set_controlfp(_In_ unsigned int _NewValue, _In_ unsigned int _Mask); +_CRT_MANAGED_FP_DEPRECATE _CRTIMP errno_t __cdecl _controlfp_s(_Out_opt_ unsigned int *_CurrentState, _In_ unsigned int _NewValue, _In_ unsigned int _Mask); +_CRT_MANAGED_FP_DEPRECATE _CRTIMP unsigned int __cdecl _statusfp(void); +_CRT_MANAGED_FP_DEPRECATE _CRTIMP void __cdecl _fpreset(void); + +#if defined (_M_IX86) +_CRT_MANAGED_FP_DEPRECATE _CRTIMP void __cdecl _statusfp2(_Out_opt_ unsigned int *_X86_status, _Out_opt_ unsigned int *_SSE2_status); +#endif /* defined (_M_IX86) */ + +#define _clear87 _clearfp +#define _status87 _statusfp + +/* + * Abstract User Status Word bit definitions + */ + +#define _SW_INEXACT 0x00000001 /* inexact (precision) */ +#define _SW_UNDERFLOW 0x00000002 /* underflow */ +#define _SW_OVERFLOW 0x00000004 /* overflow */ +#define _SW_ZERODIVIDE 0x00000008 /* zero divide */ +#define _SW_INVALID 0x00000010 /* invalid */ +#define _SW_DENORMAL 0x00080000 /* denormal status bit */ + +/* + * New Control Bit that specifies the ambiguity in control word. + */ +#define _EM_AMBIGUIOUS 0x80000000 /* for backwards compatibility old spelling */ +#define _EM_AMBIGUOUS 0x80000000 + +/* + * Abstract User Control Word Mask and bit definitions + */ +#define _MCW_EM 0x0008001f /* interrupt Exception Masks */ +#define _EM_INEXACT 0x00000001 /* inexact (precision) */ +#define _EM_UNDERFLOW 0x00000002 /* underflow */ +#define _EM_OVERFLOW 0x00000004 /* overflow */ +#define _EM_ZERODIVIDE 0x00000008 /* zero divide */ +#define _EM_INVALID 0x00000010 /* invalid */ +#define _EM_DENORMAL 0x00080000 /* denormal exception mask (_control87 only) */ + +#define _MCW_RC 0x00000300 /* Rounding Control */ +#define _RC_NEAR 0x00000000 /* near */ +#define _RC_DOWN 0x00000100 /* down */ +#define _RC_UP 0x00000200 /* up */ +#define _RC_CHOP 0x00000300 /* chop */ + +/* + * i386 specific definitions + */ +#define _MCW_PC 0x00030000 /* Precision Control */ +#define _PC_64 0x00000000 /* 64 bits */ +#define _PC_53 0x00010000 /* 53 bits */ +#define _PC_24 0x00020000 /* 24 bits */ + +#define _MCW_IC 0x00040000 /* Infinity Control */ +#define _IC_AFFINE 0x00040000 /* affine */ +#define _IC_PROJECTIVE 0x00000000 /* projective */ + +/* + * RISC specific definitions + */ + +#define _MCW_DN 0x03000000 /* Denormal Control */ +#define _DN_SAVE 0x00000000 /* save denormal results and operands */ +#define _DN_FLUSH 0x01000000 /* flush denormal results and operands to zero */ +#define _DN_FLUSH_OPERANDS_SAVE_RESULTS 0x02000000 /* flush operands to zero and save results */ +#define _DN_SAVE_OPERANDS_FLUSH_RESULTS 0x03000000 /* save operands and flush results to zero */ + +/* initial Control Word value */ + +#if defined (_M_IX86) + +#define _CW_DEFAULT ( _RC_NEAR + _PC_53 + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL) + +#elif defined (_M_X64) || defined (_M_ARM) + +#define _CW_DEFAULT ( _RC_NEAR + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL) + +#endif /* defined (_M_X64) || defined (_M_ARM) */ + +_CRT_MANAGED_FP_DEPRECATE _CRTIMP unsigned int __cdecl _control87(_In_ unsigned int _NewValue,_In_ unsigned int _Mask); +#if defined (_M_IX86) +_CRT_MANAGED_FP_DEPRECATE _CRTIMP int __cdecl __control87_2(_In_ unsigned int _NewValue, _In_ unsigned int _Mask, + _Out_opt_ unsigned int* _X86_cw, _Out_opt_ unsigned int* _Sse2_cw); +#endif /* defined (_M_IX86) */ + +/* Global variable holding floating point error code */ + +_Check_return_ _CRTIMP extern int * __cdecl __fpecode(void); +#define _fpecode (*__fpecode()) + +/* invalid subconditions (_SW_INVALID also set) */ + +#define _SW_UNEMULATED 0x0040 /* unemulated instruction */ +#define _SW_SQRTNEG 0x0080 /* square root of a neg number */ +#define _SW_STACKOVERFLOW 0x0200 /* FP stack overflow */ +#define _SW_STACKUNDERFLOW 0x0400 /* FP stack underflow */ + +/* Floating point error signals and return codes */ + +#define _FPE_INVALID 0x81 +#define _FPE_DENORMAL 0x82 +#define _FPE_ZERODIVIDE 0x83 +#define _FPE_OVERFLOW 0x84 +#define _FPE_UNDERFLOW 0x85 +#define _FPE_INEXACT 0x86 + +#define _FPE_UNEMULATED 0x87 +#define _FPE_SQRTNEG 0x88 +#define _FPE_STACKOVERFLOW 0x8a +#define _FPE_STACKUNDERFLOW 0x8b + +#define _FPE_EXPLICITGEN 0x8c /* raise( SIGFPE ); */ + +#define _FPE_MULTIPLE_TRAPS 0x8d /* on x86 with arch:SSE2 OS returns these exceptions */ +#define _FPE_MULTIPLE_FAULTS 0x8e + +/* IEEE recommended functions */ + +_Check_return_ _CRTIMP double __cdecl _copysign(_In_ double _Number, _In_ double _Sign); +_Check_return_ _CRTIMP double __cdecl _chgsign(_In_ double _X); +_Check_return_ _CRTIMP double __cdecl _scalb(_In_ double _X, _In_ long _Y); +_Check_return_ _CRTIMP double __cdecl _logb(_In_ double _X); +_Check_return_ _CRTIMP double __cdecl _nextafter(_In_ double _X, _In_ double _Y); +_Check_return_ _CRTIMP int __cdecl _finite(_In_ double _X); +_Check_return_ _CRTIMP int __cdecl _isnan(_In_ double _X); +_Check_return_ _CRTIMP int __cdecl _fpclass(_In_ double _X); + +#ifdef _M_X64 +_Check_return_ _CRTIMP float __cdecl _scalbf(_In_ float _X, _In_ long _Y); +#endif /* _M_X64 */ + +#define _FPCLASS_SNAN 0x0001 /* signaling NaN */ +#define _FPCLASS_QNAN 0x0002 /* quiet NaN */ +#define _FPCLASS_NINF 0x0004 /* negative infinity */ +#define _FPCLASS_NN 0x0008 /* negative normal */ +#define _FPCLASS_ND 0x0010 /* negative denormal */ +#define _FPCLASS_NZ 0x0020 /* -0 */ +#define _FPCLASS_PZ 0x0040 /* +0 */ +#define _FPCLASS_PD 0x0080 /* positive denormal */ +#define _FPCLASS_PN 0x0100 /* positive normal */ +#define _FPCLASS_PINF 0x0200 /* positive infinity */ + + +#if !__STDC__ + +/* Non-ANSI names for compatibility */ + +#define clear87 _clear87 +#define status87 _status87 +#define control87 _control87 + +_CRT_MANAGED_FP_DEPRECATE _CRTIMP void __cdecl fpreset(void); + +#define DBL_RADIX _DBL_RADIX +#define DBL_ROUNDS _DBL_ROUNDS + +#define LDBL_RADIX _LDBL_RADIX +#define LDBL_ROUNDS _LDBL_ROUNDS + +#define EM_AMBIGUIOUS _EM_AMBIGUOUS /* for backwards compatibility old spelling */ +#define EM_AMBIGUOUS _EM_AMBIGUOUS + +#define MCW_EM _MCW_EM +#define EM_INVALID _EM_INVALID +#define EM_DENORMAL _EM_DENORMAL +#define EM_ZERODIVIDE _EM_ZERODIVIDE +#define EM_OVERFLOW _EM_OVERFLOW +#define EM_UNDERFLOW _EM_UNDERFLOW +#define EM_INEXACT _EM_INEXACT + +#define MCW_IC _MCW_IC +#define IC_AFFINE _IC_AFFINE +#define IC_PROJECTIVE _IC_PROJECTIVE + +#define MCW_RC _MCW_RC +#define RC_CHOP _RC_CHOP +#define RC_UP _RC_UP +#define RC_DOWN _RC_DOWN +#define RC_NEAR _RC_NEAR + +#define MCW_PC _MCW_PC +#define PC_24 _PC_24 +#define PC_53 _PC_53 +#define PC_64 _PC_64 + +#define CW_DEFAULT _CW_DEFAULT + +#define SW_INVALID _SW_INVALID +#define SW_DENORMAL _SW_DENORMAL +#define SW_ZERODIVIDE _SW_ZERODIVIDE +#define SW_OVERFLOW _SW_OVERFLOW +#define SW_UNDERFLOW _SW_UNDERFLOW +#define SW_INEXACT _SW_INEXACT + +#define SW_UNEMULATED _SW_UNEMULATED +#define SW_SQRTNEG _SW_SQRTNEG +#define SW_STACKOVERFLOW _SW_STACKOVERFLOW +#define SW_STACKUNDERFLOW _SW_STACKUNDERFLOW + +#define FPE_INVALID _FPE_INVALID +#define FPE_DENORMAL _FPE_DENORMAL +#define FPE_ZERODIVIDE _FPE_ZERODIVIDE +#define FPE_OVERFLOW _FPE_OVERFLOW +#define FPE_UNDERFLOW _FPE_UNDERFLOW +#define FPE_INEXACT _FPE_INEXACT + +#define FPE_UNEMULATED _FPE_UNEMULATED +#define FPE_SQRTNEG _FPE_SQRTNEG +#define FPE_STACKOVERFLOW _FPE_STACKOVERFLOW +#define FPE_STACKUNDERFLOW _FPE_STACKUNDERFLOW + +#define FPE_EXPLICITGEN _FPE_EXPLICITGEN + +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_FLOAT */ diff --git a/test_data/lots_of_files/fpieee.h b/test_data/lots_of_files/fpieee.h new file mode 100644 index 0000000..bc948f0 --- /dev/null +++ b/test_data/lots_of_files/fpieee.h @@ -0,0 +1,499 @@ +/*** +*fpieee.h - Definitions for floating point IEEE exception handling +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains constant and type definitions for handling +* floating point exceptions [ANSI/IEEE std. 754] +* +* [Public] +* +****/ + +#pragma once +#ifndef __midl +#ifndef _INC_FPIEEE +#define _INC_FPIEEE + +#if defined (_M_CEE_PURE) + #error ERROR: This file is not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <crtdefs.h> + +#ifndef __assembler + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +/* Disable C4324: structure was padded due to __declspec(align()) */ +#pragma warning(push) +#pragma warning(disable: 4324) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Define floating point IEEE compare result values. + */ + +typedef enum { + _FpCompareEqual, + _FpCompareGreater, + _FpCompareLess, + _FpCompareUnordered +} _FPIEEE_COMPARE_RESULT; + +/* + * Define floating point format and result precision values. + */ + +typedef enum { + _FpFormatFp32, + _FpFormatFp64, + _FpFormatFp80, + _FpFormatFp128, + _FpFormatI16, + _FpFormatI32, + _FpFormatI64, + _FpFormatU16, + _FpFormatU32, + _FpFormatU64, + _FpFormatBcd80, + _FpFormatCompare, + _FpFormatString, +} _FPIEEE_FORMAT; + +/* + * Define operation code values. + */ + +typedef enum { + _FpCodeUnspecified, + _FpCodeAdd, + _FpCodeSubtract, + _FpCodeMultiply, + _FpCodeDivide, + _FpCodeSquareRoot, + _FpCodeRemainder, + _FpCodeCompare, + _FpCodeConvert, + _FpCodeRound, + _FpCodeTruncate, + _FpCodeFloor, + _FpCodeCeil, + _FpCodeAcos, + _FpCodeAsin, + _FpCodeAtan, + _FpCodeAtan2, + _FpCodeCabs, + _FpCodeCos, + _FpCodeCosh, + _FpCodeExp, + _FpCodeFabs, + _FpCodeFmod, + _FpCodeFrexp, + _FpCodeHypot, + _FpCodeLdexp, + _FpCodeLog, + _FpCodeLog10, + _FpCodeModf, + _FpCodePow, + _FpCodeSin, + _FpCodeSinh, + _FpCodeTan, + _FpCodeTanh, + _FpCodeY0, + _FpCodeY1, + _FpCodeYn, + _FpCodeLogb, + _FpCodeNextafter, + _FpCodeNegate, + _FpCodeFmin, /* XMMI */ + _FpCodeFmax, /* XMMI */ + _FpCodeConvertTrunc, /* XMMI */ + _XMMIAddps, /* XMMI */ + _XMMIAddss, + _XMMISubps, + _XMMISubss, + _XMMIMulps, + _XMMIMulss, + _XMMIDivps, + _XMMIDivss, + _XMMISqrtps, + _XMMISqrtss, + _XMMIMaxps, + _XMMIMaxss, + _XMMIMinps, + _XMMIMinss, + _XMMICmpps, + _XMMICmpss, + _XMMIComiss, + _XMMIUComiss, + _XMMICvtpi2ps, + _XMMICvtsi2ss, + _XMMICvtps2pi, + _XMMICvtss2si, + _XMMICvttps2pi, + _XMMICvttss2si, + _XMMIAddsubps, /* XMMI for PNI */ + _XMMIHaddps, /* XMMI for PNI */ + _XMMIHsubps, /* XMMI for PNI */ + _XMMIRoundps, /* 66 0F 3A 08 */ + _XMMIRoundss, /* 66 0F 3A 0A */ + _XMMIDpps, /* 66 0F 3A 40 */ + _XMMI2Addpd, /* XMMI2 */ + _XMMI2Addsd, + _XMMI2Subpd, + _XMMI2Subsd, + _XMMI2Mulpd, + _XMMI2Mulsd, + _XMMI2Divpd, + _XMMI2Divsd, + _XMMI2Sqrtpd, + _XMMI2Sqrtsd, + _XMMI2Maxpd, + _XMMI2Maxsd, + _XMMI2Minpd, + _XMMI2Minsd, + _XMMI2Cmppd, + _XMMI2Cmpsd, + _XMMI2Comisd, + _XMMI2UComisd, + _XMMI2Cvtpd2pi, /* 66 2D */ + _XMMI2Cvtsd2si, /* F2 */ + _XMMI2Cvttpd2pi, /* 66 2C */ + _XMMI2Cvttsd2si, /* F2 */ + _XMMI2Cvtps2pd, /* 0F 5A */ + _XMMI2Cvtss2sd, /* F3 */ + _XMMI2Cvtpd2ps, /* 66 */ + _XMMI2Cvtsd2ss, /* F2 */ + _XMMI2Cvtdq2ps, /* 0F 5B */ + _XMMI2Cvttps2dq, /* F3 */ + _XMMI2Cvtps2dq, /* 66 */ + _XMMI2Cvttpd2dq, /* 66 0F E6 */ + _XMMI2Cvtpd2dq, /* F2 */ + _XMMI2Addsubpd, /* 66 0F D0 */ + _XMMI2Haddpd, /* 66 0F 7C */ + _XMMI2Hsubpd, /* 66 0F 7D */ + _XMMI2Roundpd, /* 66 0F 3A 09 */ + _XMMI2Roundsd, /* 66 0F 3A 0B */ + _XMMI2Dppd, /* 66 0F 3A 41 */ +} _FP_OPERATION_CODE; + +#endif /* __assembler */ + +#ifdef _CRTBLD +#ifndef __assembler + +#define OP_UNSPEC _FpCodeUnspecified +#define OP_ADD _FpCodeAdd +#define OP_SUB _FpCodeSubtract +#define OP_MUL _FpCodeMultiply +#define OP_DIV _FpCodeDivide +#define OP_REM _FpCodeRemainder +#define OP_COMP _FpCodeCompare +#define OP_CVT _FpCodeConvert +#define OP_RND _FpCodeRound +#define OP_TRUNC _FpCodeTruncate + +#define OP_EXP _FpCodeExp + +#define OP_POW _FpCodePow +#define OP_LOG _FpCodeLog +#define OP_LOG10 _FpCodeLog10 +#define OP_SINH _FpCodeSinh +#define OP_COSH _FpCodeCosh +#define OP_TANH _FpCodeTanh +#define OP_ASIN _FpCodeAsin +#define OP_ACOS _FpCodeAcos +#define OP_ATAN _FpCodeAtan +#define OP_ATAN2 _FpCodeAtan2 +#define OP_SQRT _FpCodeSquareRoot +#define OP_SIN _FpCodeSin +#define OP_COS _FpCodeCos +#define OP_TAN _FpCodeTan +#define OP_CEIL _FpCodeCeil +#define OP_FLOOR _FpCodeFloor +#define OP_ABS _FpCodeFabs +#define OP_MODF _FpCodeModf +#define OP_LDEXP _FpCodeLdexp +#define OP_CABS _FpCodeCabs +#define OP_HYPOT _FpCodeHypot +#define OP_FMOD _FpCodeFmod +#define OP_FREXP _FpCodeFrexp +#define OP_Y0 _FpCodeY0 +#define OP_Y1 _FpCodeY1 +#define OP_YN _FpCodeYn + +#define OP_LOGB _FpCodeLogb +#define OP_NEXTAFTER _FpCodeNextafter + +/* XMMI */ +#define OP_ADDPS _XMMIAddps +#define OP_ADDSS _XMMIAddss +#define OP_SUBPS _XMMISubps +#define OP_SUBSS _XMMISubss +#define OP_MULPS _XMMIMulps +#define OP_MULSS _XMMIMulss +#define OP_DIVPS _XMMIDivps +#define OP_DIVSS _XMMIDivss +#define OP_SQRTPS _XMMISqrtps +#define OP_SQRTSS _XMMISqrtss +#define OP_MAXPS _XMMIMaxps +#define OP_MAXSS _XMMIMaxss +#define OP_MINPS _XMMIMinps +#define OP_MINSS _XMMIMinss +#define OP_CMPPS _XMMICmpps +#define OP_CMPSS _XMMICmpss +#define OP_COMISS _XMMIComiss +#define OP_UCOMISS _XMMIUComiss +#define OP_CVTPI2PS _XMMICvtpi2ps +#define OP_CVTSI2SS _XMMICvtsi2ss +#define OP_CVTPS2PI _XMMICvtps2pi +#define OP_CVTSS2SI _XMMICvtss2si +#define OP_CVTTPS2PI _XMMICvttps2pi +#define OP_CVTTSS2SI _XMMICvttss2si +#define OP_ADDSUBPS _XMMIAddsubps +#define OP_HADDPS _XMMIHaddps +#define OP_HSUBPS _XMMIHsubps +#define OP_ROUNDPS _XMMIRoundps +#define OP_ROUNDSS _XMMIRoundss +#define OP_DPPS _XMMIDpps +/* XMMI */ + +/* XMMI2 */ +#define OP_ADDPD _XMMI2Addpd /* XMMI2 */ +#define OP_ADDSD _XMMI2Addsd +#define OP_SUBPD _XMMI2Subpd +#define OP_SUBSD _XMMI2Subsd +#define OP_MULPD _XMMI2Mulpd +#define OP_MULSD _XMMI2Mulsd +#define OP_DIVPD _XMMI2Divpd +#define OP_DIVSD _XMMI2Divsd +#define OP_SQRTPD _XMMI2Sqrtpd +#define OP_SQRTSD _XMMI2Sqrtsd +#define OP_MAXPD _XMMI2Maxpd +#define OP_MAXSD _XMMI2Maxsd +#define OP_MINPD _XMMI2Minpd +#define OP_MINSD _XMMI2Minsd +#define OP_CMPPD _XMMI2Cmppd +#define OP_CMPSD _XMMI2Cmpsd +#define OP_COMISD _XMMI2Comisd +#define OP_UCOMISD _XMMI2UComisd +#define OP_CVTPD2PI _XMMI2Cvtpd2pi /* 66 2D */ +#define OP_CVTSD2SI _XMMI2Cvtsd2si /* F2 */ +#define OP_CVTTPD2PI _XMMI2Cvttpd2pi /* 66 2C */ +#define OP_CVTTSD2SI _XMMI2Cvttsd2si /* F2 */ +#define OP_CVTPS2PD _XMMI2Cvtps2pd /* 0F 5A */ +#define OP_CVTSS2SD _XMMI2Cvtss2sd /* F3 */ +#define OP_CVTPD2PS _XMMI2Cvtpd2ps /* 66 */ +#define OP_CVTSD2SS _XMMI2Cvtsd2ss /* F2 */ +#define OP_CVTDQ2PS _XMMI2Cvtdq2ps /* 0F 5B */ +#define OP_CVTTPS2DQ _XMMI2Cvttps2dq /* F3 */ +#define OP_CVTPS2DQ _XMMI2Cvtps2dq /* 66 */ +#define OP_CVTTPD2DQ _XMMI2Cvttpd2dq /* 66 0F E6 */ +#define OP_CVTPD2DQ _XMMI2Cvtpd2dq /* F2 */ +#define OP_ADDSUBPD _XMMI2Addsubpd /* 66 0F D0 */ +#define OP_HADDPD _XMMI2Haddpd /* 66 0F 7C */ +#define OP_HSUBPD _XMMI2Hsubpd /* 66 0F 7D */ +#define OP_ROUNDPD _XMMI2Roundpd /* 66 0F 3A 09 */ +#define OP_ROUNDSD _XMMI2Roundsd /* 66 0F 3A 0B */ +#define OP_DPPD _XMMI2Dppd /* 66 0F 3A 41 */ +/* XMMI2 */ + +#else /* __assembler */ + +/* This must be the same as the enumerator _FP_OPERATION_CODE ! */ +#define OP_UNSPEC 0 +#define OP_ADD 1 +#define OP_SUB 2 +#define OP_MUL 3 +#define OP_DIV 4 +#define OP_SQRT 5 +#define OP_REM 6 +#define OP_COMP 7 +#define OP_CVT 8 +#define OP_RND 9 +#define OP_TRUNC 10 +#define OP_FLOOR 11 +#define OP_CEIL 12 +#define OP_ACOS 13 +#define OP_ASIN 14 +#define OP_ATAN 15 +#define OP_ATAN2 16 +#define OP_CABS 17 +#define OP_COS 18 +#define OP_COSH 19 +#define OP_EXP 20 +#define OP_ABS 21 /* same as OP_FABS */ +#define OP_FABS 21 /* same as OP_ABS */ +#define OP_FMOD 22 +#define OP_FREXP 23 +#define OP_HYPOT 24 +#define OP_LDEXP 25 +#define OP_LOG 26 +#define OP_LOG10 27 +#define OP_MODF 28 +#define OP_POW 29 +#define OP_SIN 30 +#define OP_SINH 31 +#define OP_TAN 32 +#define OP_TANH 33 +#define OP_Y0 34 +#define OP_Y1 35 +#define OP_YN 36 +#define OP_LOGB 37 +#define OP_NEXTAFTER 38 +#define OP_NEG 39 + +#endif /* __assembler */ +#endif /* _CRTBLD */ + +/* + * Define rounding modes. + */ + +#ifndef __assembler + +typedef enum { + _FpRoundNearest, + _FpRoundMinusInfinity, + _FpRoundPlusInfinity, + _FpRoundChopped +} _FPIEEE_ROUNDING_MODE; + +typedef enum { + _FpPrecisionFull, + _FpPrecision53, + _FpPrecision24, +} _FPIEEE_PRECISION; + + +/* + * Define floating point context record + */ + +typedef float _FP32; +typedef double _FP64; +typedef short _I16; +typedef int _I32; +typedef unsigned short _U16; +typedef unsigned int _U32; +typedef __int64 _Q64; + +#ifdef _CRTBLD +typedef struct { + unsigned long W[4]; +} _U32ARRAY; +#endif /* _CRTBLD */ + +typedef struct +{ + unsigned short W[5]; +} _FP80; + +typedef struct _CRT_ALIGN(16) +{ + unsigned long W[4]; +} _FP128; + +typedef struct _CRT_ALIGN(8) +{ + unsigned long W[2]; +} _I64; + +typedef struct _CRT_ALIGN(8) +{ + unsigned long W[2]; +} _U64; + +typedef struct +{ + unsigned short W[5]; +} _BCD80; + +typedef struct _CRT_ALIGN(16) +{ + _Q64 W[2]; +} _FPQ64; + +typedef struct { + union { + _FP32 Fp32Value; + _FP64 Fp64Value; + _FP80 Fp80Value; + _FP128 Fp128Value; + _I16 I16Value; + _I32 I32Value; + _I64 I64Value; + _U16 U16Value; + _U32 U32Value; + _U64 U64Value; + _BCD80 Bcd80Value; + char *StringValue; + int CompareValue; +#ifdef _CRTBLD + _U32ARRAY U32ArrayValue; +#endif /* _CRTBLD */ + _Q64 Q64Value; + _FPQ64 Fpq64Value; + } Value; + + unsigned int OperandValid : 1; + unsigned int Format : 4; + +} _FPIEEE_VALUE; + + +typedef struct { + unsigned int Inexact : 1; + unsigned int Underflow : 1; + unsigned int Overflow : 1; + unsigned int ZeroDivide : 1; + unsigned int InvalidOperation : 1; +} _FPIEEE_EXCEPTION_FLAGS; + + +typedef struct { + unsigned int RoundingMode : 2; + unsigned int Precision : 3; + unsigned int Operation :12; + _FPIEEE_EXCEPTION_FLAGS Cause; + _FPIEEE_EXCEPTION_FLAGS Enable; + _FPIEEE_EXCEPTION_FLAGS Status; + _FPIEEE_VALUE Operand1; + _FPIEEE_VALUE Operand2; + _FPIEEE_VALUE Result; +} _FPIEEE_RECORD, *_PFPIEEE_RECORD; + + +struct _EXCEPTION_POINTERS; + +/* + * Floating point IEEE exception filter routine + */ + +_CRTIMP int __cdecl _fpieee_flt( + _In_ unsigned long _ExceptionCode, + _In_ struct _EXCEPTION_POINTERS * _PtExceptionPtr, + _In_ int (__cdecl * _Handler)(_FPIEEE_RECORD *) + ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma warning(pop) +#pragma pack(pop) +#endif /* __assembler */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INC_FPIEEE */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/fsm.c b/test_data/lots_of_files/fsm.c new file mode 100644 index 0000000..a47918b --- /dev/null +++ b/test_data/lots_of_files/fsm.c @@ -0,0 +1,320 @@ +/* FSM for LC */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define NUMMEMORY 65536 /* maximum number of words in memory */ +#define NUMREGS 8 /* number of machine registers */ +#define MAXLINELENGTH 1000 + +typedef struct stateStruct { + int pc; + int mem[NUMMEMORY]; + int reg[NUMREGS]; + int memoryAddress; + int memoryData; + int instrReg; + int aluOperand; + int aluResult; + int numMemory; +} stateType; + +void printState(stateType *, char *); +void run(stateType); +int memoryAccess(stateType *, int); +int convertNum(int); + +int +main(int argc, char *argv[]) +{ + int i; + char line[MAXLINELENGTH]; + stateType state; + FILE *filePtr; + + if (argc != 2) { + printf("error: usage: %s <machine-code file>\n", argv[0]); + exit(1); + } + + /* initialize memories and registers */ + for (i=0; i<NUMMEMORY; i++) { + state.mem[i] = 0; + } + for (i=0; i<NUMREGS; i++) { + state.reg[i] = 0; + } + + state.pc=0; + + /* + * read machine-code file into instruction/data memory + * (starting at address 0) + */ + + filePtr = fopen(argv[1], "r"); + if (filePtr == NULL) { + printf("error: can't open file %s\n", argv[1]); + perror("fopen"); + exit(1); + } + + for (state.numMemory=0; fgets(line, MAXLINELENGTH, filePtr) != NULL; + state.numMemory++) { + if (sscanf(line, "%d", state.mem+state.numMemory) != 1) { + printf("error in reading address %d\n", state.numMemory); + exit(1); + } + printf("memory[%d]=%d\n", state.numMemory, state.mem[state.numMemory]); + } + + printf("\n"); + + /* run never returns */ + run(state); + + return 0; +} + +void +printState_(stateType *statePtr, char *stateName, int showAll) +{ + int i; + static int cycle = 0; + printf("\n@@@\nstate %s (cycle %d)\n", stateName, cycle++); + if (showAll){ + printf("\tpc %d\n", statePtr->pc); + printf("\tmemory:\n"); + for (i=0; i<statePtr->numMemory; i++) { + printf("\t\tmem[ %d ] %d\n", i, statePtr->mem[i]); + } + printf("\tregisters:\n"); + for (i=0; i<NUMREGS; i++) { + printf("\t\treg[ %d ] %d\n", i, statePtr->reg[i]); + } + printf("\tinternal registers:\n"); + printf("\t\tmemoryAddress %d\n", statePtr->memoryAddress); + printf("\t\tmemoryData %d\n", statePtr->memoryData); + printf("\t\tinstrReg %d\n", statePtr->instrReg); + printf("\t\taluOperand %d\n", statePtr->aluOperand); + printf("\t\taluResult %d\n", statePtr->aluResult); + } +} + +#define printState(s,n) printState_(s,n,1) + +void +printStateReal(stateType *statePtr, char *stateName) +{ + printState_(statePtr, stateName, 1); +} + +/* + * Access memory: + * readFlag=1 ==> read from memory + * readFlag=0 ==> write to memory + * Return 1 if the memory operation was successful, otherwise return 0 + */ +int +memoryAccess(stateType *statePtr, int readFlag) +{ + static int lastAddress = -1; + static int lastReadFlag = 0; + static int lastData = 0; + static int delay = 0; + + if (statePtr->memoryAddress < 0 || statePtr->memoryAddress >= NUMMEMORY) { + printf("memory address out of range\n"); + exit(1); + } + + /* + * If this is a new access, reset the delay clock. + */ + if ( (statePtr->memoryAddress != lastAddress) || + (readFlag != lastReadFlag) || + (readFlag == 0 && lastData != statePtr->memoryData) ) { + delay = statePtr->memoryAddress % 3; + lastAddress = statePtr->memoryAddress; + lastReadFlag = readFlag; + lastData = statePtr->memoryData; + } + + if (delay == 0) { + /* memory is ready */ + if (readFlag) { + statePtr->memoryData = statePtr->mem[statePtr->memoryAddress]; + } else { + statePtr->mem[statePtr->memoryAddress] = statePtr->memoryData; + } + return 1; + } else { + /* memory is not ready */ + delay--; + return 0; + } +} + +int +convertNum(int num) +{ + /* convert a 16-bit number into a 32-bit integer */ + if (num & (1 << 15) ) { + num -= (1 << 16); + } + return num; +} + +#define OP_ADD 0 +#define OP_NAND 1 +#define OP_LW 2 +#define OP_SW 3 +#define OP_BEQ 4 +#define OP_JALR 5 +#define OP_HALT 6 +#define OP_NOOP 7 + +void +run(stateType state) +{ + int bus; + +fetch: + printState(&state, "fetch"); + bus = state.pc; + state.memoryAddress = bus; + state.pc++; + goto fetch2; + +fetch2: + printState(&state, "fetch2"); + if (memoryAccess(&state, 1)) goto decode1; + goto fetch2; + +decode1: + printState(&state, "decode1"); + bus = state.memoryData; + state.instrReg = bus; + goto decode2; + +decode2: + printState(&state, "decode2"); + bus = state.reg[(state.instrReg >> 19) & 7]; + state.aluOperand = bus; + if ((state.instrReg>>22) == OP_ADD) goto add1; + if ((state.instrReg>>22) == OP_NAND) goto nand1; + if ((state.instrReg>>22) == OP_LW) goto lw1; + if ((state.instrReg>>22) == OP_SW) goto sw1; + if ((state.instrReg>>22) == OP_BEQ) goto beq1; + if ((state.instrReg>>22) == OP_JALR) goto jalr1; + if ((state.instrReg>>22) == OP_HALT) goto halt; + if ((state.instrReg>>22) == OP_NOOP) goto fetch; + exit(1); + +add1: + printState(&state, "add1"); + bus = state.reg[(state.instrReg >> 16) & 7]; + state.aluResult = state.aluOperand + bus; + goto alu_finish; + +nand1: + printState(&state, "nand1"); + bus = state.reg[(state.instrReg >> 16) & 7]; + state.aluResult = ~(state.aluOperand & bus); + goto alu_finish; + +alu_finish: + printState(&state, "alu_finish"); + bus = state.aluResult; + state.reg[(state.instrReg) & 7] = bus; + goto fetch; + +lw1: + printState(&state, "lw1"); + bus = convertNum(state.instrReg & 0xFFFF); + state.aluResult = state.aluOperand + bus; + goto lw2; + +lw2: + printState(&state, "lw2"); + bus = state.aluResult; + state.memoryAddress = bus; + goto lw3; + +lw3: + printState(&state, "lw3"); + if (memoryAccess(&state, 1)) goto lw4; + goto lw3; + +lw4: + printState(&state, "lw4"); + bus = state.memoryData; + state.reg[(state.instrReg >> 16) & 7] = bus; + goto fetch; + +sw1: + printState(&state, "sw1"); + bus = convertNum(state.instrReg & 0xFFFF); + state.aluResult = state.aluOperand + bus; + goto sw2; + +sw2: + printState(&state, "sw2"); + bus = state.aluResult; + state.memoryAddress = bus; + goto sw3; + +sw3: + printState(&state, "sw3"); + bus = state.reg[(state.instrReg >> 16) & 7]; + state.memoryData = bus; + goto sw4; + +sw4: + printState(&state, "sw4"); + if (memoryAccess(&state, 0)) goto fetch; + goto sw4; + +beq1: + printState(&state, "beq1"); + bus = state.reg[(state.instrReg >> 16) & 7]; + state.aluResult = state.aluOperand - bus; + goto beq2; + +beq2: + printState(&state, "beq2"); + bus = convertNum(state.instrReg & 0xFFFF); + state.aluOperand = bus; + if (state.aluResult == 0) goto beq3; + goto fetch; + +beq3: + printState(&state, "beq3"); + bus = state.pc; + state.aluResult = state.aluOperand + bus; + goto beq4; + +beq4: + printState(&state, "beq4"); + bus = state.aluResult; + state.pc = bus; + goto fetch; + +jalr1: + printState(&state, "jalr1"); + bus = state.pc; + state.reg[(state.instrReg >> 16) & 7] = bus; + goto jalr2; + +jalr2: + printState(&state, "jalr2"); + bus = state.reg[(state.instrReg >> 19) & 7]; + state.pc = bus; + goto fetch; + +halt: + printStateReal(&state, "halt"); + exit(0); +} + diff --git a/test_data/lots_of_files/ftmisc.h b/test_data/lots_of_files/ftmisc.h new file mode 100644 index 0000000..981ce32 --- /dev/null +++ b/test_data/lots_of_files/ftmisc.h @@ -0,0 +1,142 @@ +/***************************************************************************/ +/* */ +/* ftmisc.h */ +/* */ +/* Miscellaneous macros for stand-alone rasterizer (specification */ +/* only). */ +/* */ +/* Copyright 2005-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /***************************************************/ + /* */ + /* This file is *not* portable! You have to adapt */ + /* its definitions to your platform. */ + /* */ + /***************************************************/ + +#ifndef FTMISC_H_ +#define FTMISC_H_ + + + /* memset */ +#include FT_CONFIG_STANDARD_LIBRARY_H + +#define FT_BEGIN_HEADER +#define FT_END_HEADER + +#define FT_LOCAL_DEF( x ) static x + + + /* from include/freetype/fttypes.h */ + + typedef unsigned char FT_Byte; + typedef signed int FT_Int; + typedef unsigned int FT_UInt; + typedef signed long FT_Long; + typedef unsigned long FT_ULong; + typedef signed long FT_F26Dot6; + typedef int FT_Error; + +#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ + ( ( (FT_ULong)_x1 << 24 ) | \ + ( (FT_ULong)_x2 << 16 ) | \ + ( (FT_ULong)_x3 << 8 ) | \ + (FT_ULong)_x4 ) + + + /* from include/freetype/ftsystem.h */ + + typedef struct FT_MemoryRec_* FT_Memory; + + typedef void* (*FT_Alloc_Func)( FT_Memory memory, + long size ); + + typedef void (*FT_Free_Func)( FT_Memory memory, + void* block ); + + typedef void* (*FT_Realloc_Func)( FT_Memory memory, + long cur_size, + long new_size, + void* block ); + + typedef struct FT_MemoryRec_ + { + void* user; + + FT_Alloc_Func alloc; + FT_Free_Func free; + FT_Realloc_Func realloc; + + } FT_MemoryRec; + + + /* from src/ftcalc.c */ + +#if ( defined _WIN32 || defined _WIN64 ) + + typedef __int64 FT_Int64; + +#else + +#include "inttypes.h" + + typedef int64_t FT_Int64; + +#endif + + + static FT_Long + FT_MulDiv( FT_Long a, + FT_Long b, + FT_Long c ) + { + FT_Int s; + FT_Long d; + + + s = 1; + if ( a < 0 ) { a = -a; s = -1; } + if ( b < 0 ) { b = -b; s = -s; } + if ( c < 0 ) { c = -c; s = -s; } + + d = (FT_Long)( c > 0 ? ( (FT_Int64)a * b + ( c >> 1 ) ) / c + : 0x7FFFFFFFL ); + + return ( s > 0 ) ? d : -d; + } + + + static FT_Long + FT_MulDiv_No_Round( FT_Long a, + FT_Long b, + FT_Long c ) + { + FT_Int s; + FT_Long d; + + + s = 1; + if ( a < 0 ) { a = -a; s = -1; } + if ( b < 0 ) { b = -b; s = -s; } + if ( c < 0 ) { c = -c; s = -s; } + + d = (FT_Long)( c > 0 ? (FT_Int64)a * b / c + : 0x7FFFFFFFL ); + + return ( s > 0 ) ? d : -d; + } + +#endif /* FTMISC_H_ */ + + +/* END */ diff --git a/test_data/lots_of_files/ftraster.c b/test_data/lots_of_files/ftraster.c new file mode 100644 index 0000000..d6c5ded --- /dev/null +++ b/test_data/lots_of_files/ftraster.c @@ -0,0 +1,3200 @@ +/***************************************************************************/ +/* */ +/* ftraster.c */ +/* */ +/* The FreeType glyph rasterizer (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This file can be compiled without the rest of the FreeType engine, by */ + /* defining the STANDALONE_ macro when compiling it. You also need to */ + /* put the files `ftimage.h' and `ftmisc.h' into the $(incdir) */ + /* directory. Typically, you should do something like */ + /* */ + /* - copy `src/raster/ftraster.c' (this file) to your current directory */ + /* */ + /* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' to your */ + /* current directory */ + /* */ + /* - compile `ftraster' with the STANDALONE_ macro defined, as in */ + /* */ + /* cc -c -DSTANDALONE_ ftraster.c */ + /* */ + /* The renderer can be initialized with a call to */ + /* `ft_standard_raster.raster_new'; a bitmap can be generated */ + /* with a call to `ft_standard_raster.raster_render'. */ + /* */ + /* See the comments and documentation in the file `ftimage.h' for more */ + /* details on how the raster works. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This is a rewrite of the FreeType 1.x scan-line converter */ + /* */ + /*************************************************************************/ + +#ifdef STANDALONE_ + + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ +#define FT_RENDER_POOL_SIZE 16384L + +#define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h> + +#include <string.h> /* for memset */ + +#include "ftmisc.h" +#include "ftimage.h" + +#else /* !STANDALONE_ */ + +#include <ft2build.h> +#include "ftraster.h" +#include FT_INTERNAL_CALC_H /* for FT_MulDiv and FT_MulDiv_No_Round */ + +#include "rastpic.h" + +#endif /* !STANDALONE_ */ + + + /*************************************************************************/ + /* */ + /* A simple technical note on how the raster works */ + /* ----------------------------------------------- */ + /* */ + /* Converting an outline into a bitmap is achieved in several steps: */ + /* */ + /* 1 - Decomposing the outline into successive `profiles'. Each */ + /* profile is simply an array of scanline intersections on a given */ + /* dimension. A profile's main attributes are */ + /* */ + /* o its scanline position boundaries, i.e. `Ymin' and `Ymax' */ + /* */ + /* o an array of intersection coordinates for each scanline */ + /* between `Ymin' and `Ymax' */ + /* */ + /* o a direction, indicating whether it was built going `up' or */ + /* `down', as this is very important for filling rules */ + /* */ + /* o its drop-out mode */ + /* */ + /* 2 - Sweeping the target map's scanlines in order to compute segment */ + /* `spans' which are then filled. Additionally, this pass */ + /* performs drop-out control. */ + /* */ + /* The outline data is parsed during step 1 only. The profiles are */ + /* built from the bottom of the render pool, used as a stack. The */ + /* following graphics shows the profile list under construction: */ + /* */ + /* __________________________________________________________ _ _ */ + /* | | | | | */ + /* | profile | coordinates for | profile | coordinates for |--> */ + /* | 1 | profile 1 | 2 | profile 2 |--> */ + /* |_________|_________________|_________|_________________|__ _ _ */ + /* */ + /* ^ ^ */ + /* | | */ + /* start of render pool top */ + /* */ + /* The top of the profile stack is kept in the `top' variable. */ + /* */ + /* As you can see, a profile record is pushed on top of the render */ + /* pool, which is then followed by its coordinates/intersections. If */ + /* a change of direction is detected in the outline, a new profile is */ + /* generated until the end of the outline. */ + /* */ + /* Note that when all profiles have been generated, the function */ + /* Finalize_Profile_Table() is used to record, for each profile, its */ + /* bottom-most scanline as well as the scanline above its upmost */ + /* boundary. These positions are called `y-turns' because they (sort */ + /* of) correspond to local extrema. They are stored in a sorted list */ + /* built from the top of the render pool as a downwards stack: */ + /* */ + /* _ _ _______________________________________ */ + /* | | */ + /* <--| sorted list of | */ + /* <--| extrema scanlines | */ + /* _ _ __________________|____________________| */ + /* */ + /* ^ ^ */ + /* | | */ + /* maxBuff sizeBuff = end of pool */ + /* */ + /* This list is later used during the sweep phase in order to */ + /* optimize performance (see technical note on the sweep below). */ + /* */ + /* Of course, the raster detects whether the two stacks collide and */ + /* handles the situation properly. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** CONFIGURATION MACROS **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + /* define DEBUG_RASTER if you want to compile a debugging version */ +/* #define DEBUG_RASTER */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** OTHER MACROS (do not change) **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_raster + + +#ifdef STANDALONE_ + + /* Auxiliary macros for token concatenation. */ +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + +#define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) + + /* This macro is used to indicate that a function parameter is unused. */ + /* Its purpose is simply to reduce compiler warnings. Note also that */ + /* simply defining it as `(void)x' doesn't avoid warnings with certain */ + /* ANSI compilers (e.g. LCC). */ +#define FT_UNUSED( x ) (x) = (x) + + /* Disable the tracing mechanism for simplicity -- developers can */ + /* activate it easily by redefining these macros. */ +#ifndef FT_ERROR +#define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ +#endif + +#ifndef FT_TRACE +#define FT_TRACE( x ) do { } while ( 0 ) /* nothing */ +#define FT_TRACE1( x ) do { } while ( 0 ) /* nothing */ +#define FT_TRACE6( x ) do { } while ( 0 ) /* nothing */ +#define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ +#endif + +#ifndef FT_THROW +#define FT_THROW( e ) FT_ERR_CAT( Raster_Err_, e ) +#endif + +#define Raster_Err_None 0 +#define Raster_Err_Not_Ini -1 +#define Raster_Err_Overflow -2 +#define Raster_Err_Neg_Height -3 +#define Raster_Err_Invalid -4 +#define Raster_Err_Unsupported -5 + +#define ft_memset memset + +#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \ + raster_reset_, raster_set_mode_, \ + raster_render_, raster_done_ ) \ + const FT_Raster_Funcs class_ = \ + { \ + glyph_format_, \ + raster_new_, \ + raster_reset_, \ + raster_set_mode_, \ + raster_render_, \ + raster_done_ \ + }; + +#else /* !STANDALONE_ */ + + +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H /* for FT_TRACE, FT_ERROR, and FT_THROW */ + +#include "rasterrs.h" + +#define Raster_Err_None FT_Err_Ok +#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized +#define Raster_Err_Overflow Raster_Err_Raster_Overflow +#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height +#define Raster_Err_Invalid Raster_Err_Invalid_Outline +#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph + + +#endif /* !STANDALONE_ */ + + +#ifndef FT_MEM_SET +#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) +#endif + +#ifndef FT_MEM_ZERO +#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) +#endif + + /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */ + /* typically a small value and the result of a*b is known to fit into */ + /* 32 bits. */ +#define FMulDiv( a, b, c ) ( (a) * (b) / (c) ) + + /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */ + /* for clipping computations. It simply uses the FT_MulDiv() function */ + /* defined in `ftcalc.h'. */ +#define SMulDiv FT_MulDiv +#define SMulDiv_No_Round FT_MulDiv_No_Round + + /* The rasterizer is a very general purpose component; please leave */ + /* the following redefinitions there (you never know your target */ + /* environment). */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL (void*)0 +#endif + +#ifndef SUCCESS +#define SUCCESS 0 +#endif + +#ifndef FAILURE +#define FAILURE 1 +#endif + + +#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */ + /* Setting this constant to more than 32 is a */ + /* pure waste of space. */ + +#define Pixel_Bits 6 /* fractional bits of *input* coordinates */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** SIMPLE TYPE DECLARATIONS **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + typedef int Int; + typedef unsigned int UInt; + typedef short Short; + typedef unsigned short UShort, *PUShort; + typedef long Long, *PLong; + typedef unsigned long ULong; + + typedef unsigned char Byte, *PByte; + typedef char Bool; + + + typedef union Alignment_ + { + Long l; + void* p; + void (*f)(void); + + } Alignment, *PAlignment; + + + typedef struct TPoint_ + { + Long x; + Long y; + + } TPoint; + + + /* values for the `flags' bit field */ +#define Flow_Up 0x08U +#define Overshoot_Top 0x10U +#define Overshoot_Bottom 0x20U + + + /* States of each line, arc, and profile */ + typedef enum TStates_ + { + Unknown_State, + Ascending_State, + Descending_State, + Flat_State + + } TStates; + + + typedef struct TProfile_ TProfile; + typedef TProfile* PProfile; + + struct TProfile_ + { + FT_F26Dot6 X; /* current coordinate during sweep */ + PProfile link; /* link to next profile (various purposes) */ + PLong offset; /* start of profile's data in render pool */ + UShort flags; /* Bit 0-2: drop-out mode */ + /* Bit 3: profile orientation (up/down) */ + /* Bit 4: is top profile? */ + /* Bit 5: is bottom profile? */ + Long height; /* profile's height in scanlines */ + Long start; /* profile's starting scanline */ + + Int countL; /* number of lines to step before this */ + /* profile becomes drawable */ + + PProfile next; /* next profile in same contour, used */ + /* during drop-out control */ + }; + + typedef PProfile TProfileList; + typedef PProfile* PProfileList; + + + /* Simple record used to implement a stack of bands, required */ + /* by the sub-banding mechanism */ + typedef struct black_TBand_ + { + Short y_min; /* band's minimum */ + Short y_max; /* band's maximum */ + + } black_TBand; + + +#define AlignProfileSize \ + ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) ) + + +#undef RAS_ARG +#undef RAS_ARGS +#undef RAS_VAR +#undef RAS_VARS + +#ifdef FT_STATIC_RASTER + + +#define RAS_ARGS /* void */ +#define RAS_ARG /* void */ + +#define RAS_VARS /* void */ +#define RAS_VAR /* void */ + +#define FT_UNUSED_RASTER do { } while ( 0 ) + + +#else /* !FT_STATIC_RASTER */ + + +#define RAS_ARGS black_PWorker worker, +#define RAS_ARG black_PWorker worker + +#define RAS_VARS worker, +#define RAS_VAR worker + +#define FT_UNUSED_RASTER FT_UNUSED( worker ) + + +#endif /* !FT_STATIC_RASTER */ + + + typedef struct black_TWorker_ black_TWorker, *black_PWorker; + + + /* prototypes used for sweep function dispatch */ + typedef void + Function_Sweep_Init( RAS_ARGS Short* min, + Short* max ); + + typedef void + Function_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ); + + typedef void + Function_Sweep_Step( RAS_ARG ); + + + /* NOTE: These operations are only valid on 2's complement processors */ +#undef FLOOR +#undef CEILING +#undef TRUNC +#undef SCALED + +#define FLOOR( x ) ( (x) & -ras.precision ) +#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision ) +#define TRUNC( x ) ( (Long)(x) >> ras.precision_bits ) +#define FRAC( x ) ( (x) & ( ras.precision - 1 ) ) +#define SCALED( x ) ( ( (x) < 0 ? -( -(x) << ras.scale_shift ) \ + : ( (x) << ras.scale_shift ) ) \ + - ras.precision_half ) + +#define IS_BOTTOM_OVERSHOOT( x ) \ + (Bool)( CEILING( x ) - x >= ras.precision_half ) +#define IS_TOP_OVERSHOOT( x ) \ + (Bool)( x - FLOOR( x ) >= ras.precision_half ) + + /* The most used variables are positioned at the top of the structure. */ + /* Thus, their offset can be coded with less opcodes, resulting in a */ + /* smaller executable. */ + + struct black_TWorker_ + { + Int precision_bits; /* precision related variables */ + Int precision; + Int precision_half; + Int precision_shift; + Int precision_step; + Int precision_jitter; + + Int scale_shift; /* == precision_shift for bitmaps */ + /* == precision_shift+1 for pixmaps */ + + PLong buff; /* The profiles buffer */ + PLong sizeBuff; /* Render pool size */ + PLong maxBuff; /* Profiles buffer size */ + PLong top; /* Current cursor in buffer */ + + FT_Error error; + + Int numTurns; /* number of Y-turns in outline */ + + TPoint* arc; /* current Bezier arc pointer */ + + UShort bWidth; /* target bitmap width */ + PByte bTarget; /* target bitmap buffer */ + PByte gTarget; /* target pixmap buffer */ + + Long lastX, lastY; + Long minY, maxY; + + UShort num_Profs; /* current number of profiles */ + + Bool fresh; /* signals a fresh new profile which */ + /* `start' field must be completed */ + Bool joint; /* signals that the last arc ended */ + /* exactly on a scanline. Allows */ + /* removal of doublets */ + PProfile cProfile; /* current profile */ + PProfile fProfile; /* head of linked list of profiles */ + PProfile gProfile; /* contour's first profile in case */ + /* of impact */ + + TStates state; /* rendering state */ + + FT_Bitmap target; /* description of target bit/pixmap */ + FT_Outline outline; + + Long traceOfs; /* current offset in target bitmap */ + Long traceG; /* current offset in target pixmap */ + + Short traceIncr; /* sweep's increment in target bitmap */ + + /* dispatch variables */ + + Function_Sweep_Init* Proc_Sweep_Init; + Function_Sweep_Span* Proc_Sweep_Span; + Function_Sweep_Span* Proc_Sweep_Drop; + Function_Sweep_Step* Proc_Sweep_Step; + + Byte dropOutControl; /* current drop_out control method */ + + Bool second_pass; /* indicates whether a horizontal pass */ + /* should be performed to control */ + /* drop-out accurately when calling */ + /* Render_Glyph. */ + + TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */ + + black_TBand band_stack[16]; /* band stack used for sub-banding */ + Int band_top; /* band stack top */ + + }; + + + typedef struct black_TRaster_ + { + void* memory; + + } black_TRaster, *black_PRaster; + +#ifdef FT_STATIC_RASTER + + static black_TWorker cur_ras; +#define ras cur_ras + +#else /* !FT_STATIC_RASTER */ + +#define ras (*worker) + +#endif /* !FT_STATIC_RASTER */ + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** PROFILES COMPUTATION **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Set_High_Precision */ + /* */ + /* <Description> */ + /* Set precision variables according to param flag. */ + /* */ + /* <Input> */ + /* High :: Set to True for high precision (typically for ppem < 24), */ + /* false otherwise. */ + /* */ + static void + Set_High_Precision( RAS_ARGS Int High ) + { + /* + * `precision_step' is used in `Bezier_Up' to decide when to split a + * given y-monotonous Bezier arc that crosses a scanline before + * approximating it as a straight segment. The default value of 32 (for + * low accuracy) corresponds to + * + * 32 / 64 == 0.5 pixels, + * + * while for the high accuracy case we have + * + * 256 / (1 << 12) = 0.0625 pixels. + * + * `precision_jitter' is an epsilon threshold used in + * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier + * decomposition (after all, we are working with approximations only); + * it avoids switching on additional pixels which would cause artifacts + * otherwise. + * + * The value of `precision_jitter' has been determined heuristically. + * + */ + + if ( High ) + { + ras.precision_bits = 12; + ras.precision_step = 256; + ras.precision_jitter = 30; + } + else + { + ras.precision_bits = 6; + ras.precision_step = 32; + ras.precision_jitter = 2; + } + + FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" )); + + ras.precision = 1 << ras.precision_bits; + ras.precision_half = ras.precision / 2; + ras.precision_shift = ras.precision_bits - Pixel_Bits; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* New_Profile */ + /* */ + /* <Description> */ + /* Create a new profile in the render pool. */ + /* */ + /* <Input> */ + /* aState :: The state/orientation of the new profile. */ + /* */ + /* overshoot :: Whether the profile's unrounded start position */ + /* differs by at least a half pixel. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow or of incoherent */ + /* profile. */ + /* */ + static Bool + New_Profile( RAS_ARGS TStates aState, + Bool overshoot ) + { + if ( !ras.fProfile ) + { + ras.cProfile = (PProfile)ras.top; + ras.fProfile = ras.cProfile; + ras.top += AlignProfileSize; + } + + if ( ras.top >= ras.maxBuff ) + { + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + ras.cProfile->flags = 0; + ras.cProfile->start = 0; + ras.cProfile->height = 0; + ras.cProfile->offset = ras.top; + ras.cProfile->link = (PProfile)0; + ras.cProfile->next = (PProfile)0; + ras.cProfile->flags = ras.dropOutControl; + + switch ( aState ) + { + case Ascending_State: + ras.cProfile->flags |= Flow_Up; + if ( overshoot ) + ras.cProfile->flags |= Overshoot_Bottom; + + FT_TRACE6(( " new ascending profile = %p\n", ras.cProfile )); + break; + + case Descending_State: + if ( overshoot ) + ras.cProfile->flags |= Overshoot_Top; + FT_TRACE6(( " new descending profile = %p\n", ras.cProfile )); + break; + + default: + FT_ERROR(( "New_Profile: invalid profile direction\n" )); + ras.error = FT_THROW( Invalid ); + return FAILURE; + } + + if ( !ras.gProfile ) + ras.gProfile = ras.cProfile; + + ras.state = aState; + ras.fresh = TRUE; + ras.joint = FALSE; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* End_Profile */ + /* */ + /* <Description> */ + /* Finalize the current profile. */ + /* */ + /* <Input> */ + /* overshoot :: Whether the profile's unrounded end position differs */ + /* by at least a half pixel. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow or incoherency. */ + /* */ + static Bool + End_Profile( RAS_ARGS Bool overshoot ) + { + Long h; + + + h = (Long)( ras.top - ras.cProfile->offset ); + + if ( h < 0 ) + { + FT_ERROR(( "End_Profile: negative height encountered\n" )); + ras.error = FT_THROW( Neg_Height ); + return FAILURE; + } + + if ( h > 0 ) + { + PProfile oldProfile; + + + FT_TRACE6(( " ending profile %p, start = %ld, height = %ld\n", + ras.cProfile, ras.cProfile->start, h )); + + ras.cProfile->height = h; + if ( overshoot ) + { + if ( ras.cProfile->flags & Flow_Up ) + ras.cProfile->flags |= Overshoot_Top; + else + ras.cProfile->flags |= Overshoot_Bottom; + } + + oldProfile = ras.cProfile; + ras.cProfile = (PProfile)ras.top; + + ras.top += AlignProfileSize; + + ras.cProfile->height = 0; + ras.cProfile->offset = ras.top; + + oldProfile->next = ras.cProfile; + ras.num_Profs++; + } + + if ( ras.top >= ras.maxBuff ) + { + FT_TRACE1(( "overflow in End_Profile\n" )); + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + ras.joint = FALSE; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Insert_Y_Turn */ + /* */ + /* <Description> */ + /* Insert a salient into the sorted list placed on top of the render */ + /* pool. */ + /* */ + /* <Input> */ + /* New y scanline position. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow. */ + /* */ + static Bool + Insert_Y_Turn( RAS_ARGS Int y ) + { + PLong y_turns; + Int n; + + + n = ras.numTurns - 1; + y_turns = ras.sizeBuff - ras.numTurns; + + /* look for first y value that is <= */ + while ( n >= 0 && y < y_turns[n] ) + n--; + + /* if it is <, simply insert it, ignore if == */ + if ( n >= 0 && y > y_turns[n] ) + do + { + Int y2 = (Int)y_turns[n]; + + + y_turns[n] = y; + y = y2; + } while ( --n >= 0 ); + + if ( n < 0 ) + { + ras.maxBuff--; + if ( ras.maxBuff <= ras.top ) + { + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + ras.numTurns++; + ras.sizeBuff[-ras.numTurns] = y; + } + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Finalize_Profile_Table */ + /* */ + /* <Description> */ + /* Adjust all links in the profiles list. */ + /* */ + /* <Return> */ + /* SUCCESS on success. FAILURE in case of overflow. */ + /* */ + static Bool + Finalize_Profile_Table( RAS_ARG ) + { + UShort n; + PProfile p; + + + n = ras.num_Profs; + p = ras.fProfile; + + if ( n > 1 && p ) + { + do + { + Int bottom, top; + + + if ( n > 1 ) + p->link = (PProfile)( p->offset + p->height ); + else + p->link = NULL; + + if ( p->flags & Flow_Up ) + { + bottom = (Int)p->start; + top = (Int)( p->start + p->height - 1 ); + } + else + { + bottom = (Int)( p->start - p->height + 1 ); + top = (Int)p->start; + p->start = bottom; + p->offset += p->height - 1; + } + + if ( Insert_Y_Turn( RAS_VARS bottom ) || + Insert_Y_Turn( RAS_VARS top + 1 ) ) + return FAILURE; + + p = p->link; + } while ( --n ); + } + else + ras.fProfile = NULL; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Split_Conic */ + /* */ + /* <Description> */ + /* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */ + /* stack. */ + /* */ + /* <Input> */ + /* None (subdivided Bezier is taken from the top of the stack). */ + /* */ + /* <Note> */ + /* This routine is the `beef' of this component. It is _the_ inner */ + /* loop that should be optimized to hell to get the best performance. */ + /* */ + static void + Split_Conic( TPoint* base ) + { + Long a, b; + + + base[4].x = base[2].x; + b = base[1].x; + a = base[3].x = ( base[2].x + b ) / 2; + b = base[1].x = ( base[0].x + b ) / 2; + base[2].x = ( a + b ) / 2; + + base[4].y = base[2].y; + b = base[1].y; + a = base[3].y = ( base[2].y + b ) / 2; + b = base[1].y = ( base[0].y + b ) / 2; + base[2].y = ( a + b ) / 2; + + /* hand optimized. gcc doesn't seem to be too good at common */ + /* expression substitution and instruction scheduling ;-) */ + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Split_Cubic */ + /* */ + /* <Description> */ + /* Subdivide a third-order Bezier arc into two joint sub-arcs in the */ + /* Bezier stack. */ + /* */ + /* <Note> */ + /* This routine is the `beef' of the component. It is one of _the_ */ + /* inner loops that should be optimized like hell to get the best */ + /* performance. */ + /* */ + static void + Split_Cubic( TPoint* base ) + { + Long a, b, c, d; + + + base[6].x = base[3].x; + c = base[1].x; + d = base[2].x; + base[1].x = a = ( base[0].x + c + 1 ) >> 1; + base[5].x = b = ( base[3].x + d + 1 ) >> 1; + c = ( c + d + 1 ) >> 1; + base[2].x = a = ( a + c + 1 ) >> 1; + base[4].x = b = ( b + c + 1 ) >> 1; + base[3].x = ( a + b + 1 ) >> 1; + + base[6].y = base[3].y; + c = base[1].y; + d = base[2].y; + base[1].y = a = ( base[0].y + c + 1 ) >> 1; + base[5].y = b = ( base[3].y + d + 1 ) >> 1; + c = ( c + d + 1 ) >> 1; + base[2].y = a = ( a + c + 1 ) >> 1; + base[4].y = b = ( b + c + 1 ) >> 1; + base[3].y = ( a + b + 1 ) >> 1; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Line_Up */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an ascending line segment and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* x1 :: The x-coordinate of the segment's start point. */ + /* */ + /* y1 :: The y-coordinate of the segment's start point. */ + /* */ + /* x2 :: The x-coordinate of the segment's end point. */ + /* */ + /* y2 :: The y-coordinate of the segment's end point. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Line_Up( RAS_ARGS Long x1, + Long y1, + Long x2, + Long y2, + Long miny, + Long maxy ) + { + Long Dx, Dy; + Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */ + Long Ix, Rx, Ax; + + PLong top; + + + Dx = x2 - x1; + Dy = y2 - y1; + + if ( Dy <= 0 || y2 < miny || y1 > maxy ) + return SUCCESS; + + if ( y1 < miny ) + { + /* Take care: miny-y1 can be a very large value; we use */ + /* a slow MulDiv function to avoid clipping bugs */ + x1 += SMulDiv( Dx, miny - y1, Dy ); + e1 = (Int)TRUNC( miny ); + f1 = 0; + } + else + { + e1 = (Int)TRUNC( y1 ); + f1 = (Int)FRAC( y1 ); + } + + if ( y2 > maxy ) + { + /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */ + e2 = (Int)TRUNC( maxy ); + f2 = 0; + } + else + { + e2 = (Int)TRUNC( y2 ); + f2 = (Int)FRAC( y2 ); + } + + if ( f1 > 0 ) + { + if ( e1 == e2 ) + return SUCCESS; + else + { + x1 += SMulDiv( Dx, ras.precision - f1, Dy ); + e1 += 1; + } + } + else + if ( ras.joint ) + { + ras.top--; + ras.joint = FALSE; + } + + ras.joint = (char)( f2 == 0 ); + + if ( ras.fresh ) + { + ras.cProfile->start = e1; + ras.fresh = FALSE; + } + + size = e2 - e1 + 1; + if ( ras.top + size >= ras.maxBuff ) + { + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + if ( Dx > 0 ) + { + Ix = SMulDiv_No_Round( ras.precision, Dx, Dy ); + Rx = ( ras.precision * Dx ) % Dy; + Dx = 1; + } + else + { + Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy ); + Rx = ( ras.precision * -Dx ) % Dy; + Dx = -1; + } + + Ax = -Dy; + top = ras.top; + + while ( size > 0 ) + { + *top++ = x1; + + x1 += Ix; + Ax += Rx; + if ( Ax >= 0 ) + { + Ax -= Dy; + x1 += Dx; + } + size--; + } + + ras.top = top; + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Line_Down */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an descending line segment and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* x1 :: The x-coordinate of the segment's start point. */ + /* */ + /* y1 :: The y-coordinate of the segment's start point. */ + /* */ + /* x2 :: The x-coordinate of the segment's end point. */ + /* */ + /* y2 :: The y-coordinate of the segment's end point. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Line_Down( RAS_ARGS Long x1, + Long y1, + Long x2, + Long y2, + Long miny, + Long maxy ) + { + Bool result, fresh; + + + fresh = ras.fresh; + + result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cProfile->start = -ras.cProfile->start; + + return result; + } + + + /* A function type describing the functions used to split Bezier arcs */ + typedef void (*TSplitter)( TPoint* base ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Bezier_Up */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an ascending Bezier arc and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* degree :: The degree of the Bezier arc (either 2 or 3). */ + /* */ + /* splitter :: The function to split Bezier arcs. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Bezier_Up( RAS_ARGS Int degree, + TSplitter splitter, + Long miny, + Long maxy ) + { + Long y1, y2, e, e2, e0; + Short f1; + + TPoint* arc; + TPoint* start_arc; + + PLong top; + + + arc = ras.arc; + y1 = arc[degree].y; + y2 = arc[0].y; + top = ras.top; + + if ( y2 < miny || y1 > maxy ) + goto Fin; + + e2 = FLOOR( y2 ); + + if ( e2 > maxy ) + e2 = maxy; + + e0 = miny; + + if ( y1 < miny ) + e = miny; + else + { + e = CEILING( y1 ); + f1 = (Short)( FRAC( y1 ) ); + e0 = e; + + if ( f1 == 0 ) + { + if ( ras.joint ) + { + top--; + ras.joint = FALSE; + } + + *top++ = arc[degree].x; + + e += ras.precision; + } + } + + if ( ras.fresh ) + { + ras.cProfile->start = TRUNC( e0 ); + ras.fresh = FALSE; + } + + if ( e2 < e ) + goto Fin; + + if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff ) + { + ras.top = top; + ras.error = FT_THROW( Overflow ); + return FAILURE; + } + + start_arc = arc; + + do + { + ras.joint = FALSE; + + y2 = arc[0].y; + + if ( y2 > e ) + { + y1 = arc[degree].y; + if ( y2 - y1 >= ras.precision_step ) + { + splitter( arc ); + arc += degree; + } + else + { + *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x, + e - y1, y2 - y1 ); + arc -= degree; + e += ras.precision; + } + } + else + { + if ( y2 == e ) + { + ras.joint = TRUE; + *top++ = arc[0].x; + + e += ras.precision; + } + arc -= degree; + } + } while ( arc >= start_arc && e <= e2 ); + + Fin: + ras.top = top; + ras.arc -= degree; + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Bezier_Down */ + /* */ + /* <Description> */ + /* Compute the x-coordinates of an descending Bezier arc and store */ + /* them in the render pool. */ + /* */ + /* <Input> */ + /* degree :: The degree of the Bezier arc (either 2 or 3). */ + /* */ + /* splitter :: The function to split Bezier arcs. */ + /* */ + /* miny :: A lower vertical clipping bound value. */ + /* */ + /* maxy :: An upper vertical clipping bound value. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow. */ + /* */ + static Bool + Bezier_Down( RAS_ARGS Int degree, + TSplitter splitter, + Long miny, + Long maxy ) + { + TPoint* arc = ras.arc; + Bool result, fresh; + + + arc[0].y = -arc[0].y; + arc[1].y = -arc[1].y; + arc[2].y = -arc[2].y; + if ( degree > 2 ) + arc[3].y = -arc[3].y; + + fresh = ras.fresh; + + result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny ); + + if ( fresh && !ras.fresh ) + ras.cProfile->start = -ras.cProfile->start; + + arc[0].y = -arc[0].y; + return result; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Line_To */ + /* */ + /* <Description> */ + /* Inject a new line segment and adjust the Profiles list. */ + /* */ + /* <Input> */ + /* x :: The x-coordinate of the segment's end point (its start point */ + /* is stored in `lastX'). */ + /* */ + /* y :: The y-coordinate of the segment's end point (its start point */ + /* is stored in `lastY'). */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static Bool + Line_To( RAS_ARGS Long x, + Long y ) + { + /* First, detect a change of direction */ + + switch ( ras.state ) + { + case Unknown_State: + if ( y > ras.lastY ) + { + if ( New_Profile( RAS_VARS Ascending_State, + IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + else + { + if ( y < ras.lastY ) + if ( New_Profile( RAS_VARS Descending_State, + IS_TOP_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + case Ascending_State: + if ( y < ras.lastY ) + { + if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) || + New_Profile( RAS_VARS Descending_State, + IS_TOP_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + case Descending_State: + if ( y > ras.lastY ) + { + if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) || + New_Profile( RAS_VARS Ascending_State, + IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ) + return FAILURE; + } + break; + + default: + ; + } + + /* Then compute the lines */ + + switch ( ras.state ) + { + case Ascending_State: + if ( Line_Up( RAS_VARS ras.lastX, ras.lastY, + x, y, ras.minY, ras.maxY ) ) + return FAILURE; + break; + + case Descending_State: + if ( Line_Down( RAS_VARS ras.lastX, ras.lastY, + x, y, ras.minY, ras.maxY ) ) + return FAILURE; + break; + + default: + ; + } + + ras.lastX = x; + ras.lastY = y; + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Conic_To */ + /* */ + /* <Description> */ + /* Inject a new conic arc and adjust the profile list. */ + /* */ + /* <Input> */ + /* cx :: The x-coordinate of the arc's new control point. */ + /* */ + /* cy :: The y-coordinate of the arc's new control point. */ + /* */ + /* x :: The x-coordinate of the arc's end point (its start point is */ + /* stored in `lastX'). */ + /* */ + /* y :: The y-coordinate of the arc's end point (its start point is */ + /* stored in `lastY'). */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static Bool + Conic_To( RAS_ARGS Long cx, + Long cy, + Long x, + Long y ) + { + Long y1, y2, y3, x3, ymin, ymax; + TStates state_bez; + + + ras.arc = ras.arcs; + ras.arc[2].x = ras.lastX; + ras.arc[2].y = ras.lastY; + ras.arc[1].x = cx; + ras.arc[1].y = cy; + ras.arc[0].x = x; + ras.arc[0].y = y; + + do + { + y1 = ras.arc[2].y; + y2 = ras.arc[1].y; + y3 = ras.arc[0].y; + x3 = ras.arc[0].x; + + /* first, categorize the Bezier arc */ + + if ( y1 <= y3 ) + { + ymin = y1; + ymax = y3; + } + else + { + ymin = y3; + ymax = y1; + } + + if ( y2 < ymin || y2 > ymax ) + { + /* this arc has no given direction, split it! */ + Split_Conic( ras.arc ); + ras.arc += 2; + } + else if ( y1 == y3 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + ras.arc -= 2; + } + else + { + /* the arc is y-monotonous, either ascending or descending */ + /* detect a change of direction */ + state_bez = y1 < y3 ? Ascending_State : Descending_State; + if ( ras.state != state_bez ) + { + Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 ) + : IS_TOP_OVERSHOOT( y1 ); + + + /* finalize current profile if any */ + if ( ras.state != Unknown_State && + End_Profile( RAS_VARS o ) ) + goto Fail; + + /* create a new profile */ + if ( New_Profile( RAS_VARS state_bez, o ) ) + goto Fail; + } + + /* now call the appropriate routine */ + if ( state_bez == Ascending_State ) + { + if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) ) + goto Fail; + } + + } while ( ras.arc >= ras.arcs ); + + ras.lastX = x3; + ras.lastY = y3; + + return SUCCESS; + + Fail: + return FAILURE; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Cubic_To */ + /* */ + /* <Description> */ + /* Inject a new cubic arc and adjust the profile list. */ + /* */ + /* <Input> */ + /* cx1 :: The x-coordinate of the arc's first new control point. */ + /* */ + /* cy1 :: The y-coordinate of the arc's first new control point. */ + /* */ + /* cx2 :: The x-coordinate of the arc's second new control point. */ + /* */ + /* cy2 :: The y-coordinate of the arc's second new control point. */ + /* */ + /* x :: The x-coordinate of the arc's end point (its start point is */ + /* stored in `lastX'). */ + /* */ + /* y :: The y-coordinate of the arc's end point (its start point is */ + /* stored in `lastY'). */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on render pool overflow or incorrect */ + /* profile. */ + /* */ + static Bool + Cubic_To( RAS_ARGS Long cx1, + Long cy1, + Long cx2, + Long cy2, + Long x, + Long y ) + { + Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2; + TStates state_bez; + + + ras.arc = ras.arcs; + ras.arc[3].x = ras.lastX; + ras.arc[3].y = ras.lastY; + ras.arc[2].x = cx1; + ras.arc[2].y = cy1; + ras.arc[1].x = cx2; + ras.arc[1].y = cy2; + ras.arc[0].x = x; + ras.arc[0].y = y; + + do + { + y1 = ras.arc[3].y; + y2 = ras.arc[2].y; + y3 = ras.arc[1].y; + y4 = ras.arc[0].y; + x4 = ras.arc[0].x; + + /* first, categorize the Bezier arc */ + + if ( y1 <= y4 ) + { + ymin1 = y1; + ymax1 = y4; + } + else + { + ymin1 = y4; + ymax1 = y1; + } + + if ( y2 <= y3 ) + { + ymin2 = y2; + ymax2 = y3; + } + else + { + ymin2 = y3; + ymax2 = y2; + } + + if ( ymin2 < ymin1 || ymax2 > ymax1 ) + { + /* this arc has no given direction, split it! */ + Split_Cubic( ras.arc ); + ras.arc += 3; + } + else if ( y1 == y4 ) + { + /* this arc is flat, ignore it and pop it from the Bezier stack */ + ras.arc -= 3; + } + else + { + state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State; + + /* detect a change of direction */ + if ( ras.state != state_bez ) + { + Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 ) + : IS_TOP_OVERSHOOT( y1 ); + + + /* finalize current profile if any */ + if ( ras.state != Unknown_State && + End_Profile( RAS_VARS o ) ) + goto Fail; + + if ( New_Profile( RAS_VARS state_bez, o ) ) + goto Fail; + } + + /* compute intersections */ + if ( state_bez == Ascending_State ) + { + if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) + goto Fail; + } + else + if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) ) + goto Fail; + } + + } while ( ras.arc >= ras.arcs ); + + ras.lastX = x4; + ras.lastY = y4; + + return SUCCESS; + + Fail: + return FAILURE; + } + + +#undef SWAP_ +#define SWAP_( x, y ) do \ + { \ + Long swap = x; \ + \ + \ + x = y; \ + y = swap; \ + } while ( 0 ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Decompose_Curve */ + /* */ + /* <Description> */ + /* Scan the outline arrays in order to emit individual segments and */ + /* Beziers by calling Line_To() and Bezier_To(). It handles all */ + /* weird cases, like when the first point is off the curve, or when */ + /* there are simply no `on' points in the contour! */ + /* */ + /* <Input> */ + /* first :: The index of the first point in the contour. */ + /* */ + /* last :: The index of the last point in the contour. */ + /* */ + /* flipped :: If set, flip the direction of the curve. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE on error. */ + /* */ + static Bool + Decompose_Curve( RAS_ARGS UShort first, + UShort last, + Int flipped ) + { + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + + FT_Vector* points; + FT_Vector* point; + FT_Vector* limit; + char* tags; + + UInt tag; /* current point's state */ + + + points = ras.outline.points; + limit = points + last; + + v_start.x = SCALED( points[first].x ); + v_start.y = SCALED( points[first].y ); + v_last.x = SCALED( points[last].x ); + v_last.y = SCALED( points[last].y ); + + if ( flipped ) + { + SWAP_( v_start.x, v_start.y ); + SWAP_( v_last.x, v_last.y ); + } + + v_control = v_start; + + point = points + first; + tags = ras.outline.tags + first; + + /* set scan mode if necessary */ + if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE ) + ras.dropOutControl = (Byte)tags[0] >> 5; + + tag = FT_CURVE_TAG( tags[0] ); + + /* A contour cannot start with a cubic control point! */ + if ( tag == FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + /* check first point to determine origin */ + if ( tag == FT_CURVE_TAG_CONIC ) + { + /* first point is conic control. Yes, this happens. */ + if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON ) + { + /* start at last point if it is on the curve */ + v_start = v_last; + limit--; + } + else + { + /* if both first and last points are conic, */ + /* start at their middle and record its position */ + /* for closure */ + v_start.x = ( v_start.x + v_last.x ) / 2; + v_start.y = ( v_start.y + v_last.y ) / 2; + + /* v_last = v_start; */ + } + point--; + tags--; + } + + ras.lastX = v_start.x; + ras.lastY = v_start.y; + + while ( point < limit ) + { + point++; + tags++; + + tag = FT_CURVE_TAG( tags[0] ); + + switch ( tag ) + { + case FT_CURVE_TAG_ON: /* emit a single line_to */ + { + Long x, y; + + + x = SCALED( point->x ); + y = SCALED( point->y ); + if ( flipped ) + SWAP_( x, y ); + + if ( Line_To( RAS_VARS x, y ) ) + goto Fail; + continue; + } + + case FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = SCALED( point[0].x ); + v_control.y = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( v_control.x, v_control.y ); + + Do_Conic: + if ( point < limit ) + { + FT_Vector v_middle; + Long x, y; + + + point++; + tags++; + tag = FT_CURVE_TAG( tags[0] ); + + x = SCALED( point[0].x ); + y = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( x, y ); + + if ( tag == FT_CURVE_TAG_ON ) + { + if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) ) + goto Fail; + continue; + } + + if ( tag != FT_CURVE_TAG_CONIC ) + goto Invalid_Outline; + + v_middle.x = ( v_control.x + x ) / 2; + v_middle.y = ( v_control.y + y ) / 2; + + if ( Conic_To( RAS_VARS v_control.x, v_control.y, + v_middle.x, v_middle.y ) ) + goto Fail; + + v_control.x = x; + v_control.y = y; + + goto Do_Conic; + } + + if ( Conic_To( RAS_VARS v_control.x, v_control.y, + v_start.x, v_start.y ) ) + goto Fail; + + goto Close; + + default: /* FT_CURVE_TAG_CUBIC */ + { + Long x1, y1, x2, y2, x3, y3; + + + if ( point + 1 > limit || + FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) + goto Invalid_Outline; + + point += 2; + tags += 2; + + x1 = SCALED( point[-2].x ); + y1 = SCALED( point[-2].y ); + x2 = SCALED( point[-1].x ); + y2 = SCALED( point[-1].y ); + + if ( flipped ) + { + SWAP_( x1, y1 ); + SWAP_( x2, y2 ); + } + + if ( point <= limit ) + { + x3 = SCALED( point[0].x ); + y3 = SCALED( point[0].y ); + + if ( flipped ) + SWAP_( x3, y3 ); + + if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) ) + goto Fail; + continue; + } + + if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) ) + goto Fail; + goto Close; + } + } + } + + /* close the contour with a line segment */ + if ( Line_To( RAS_VARS v_start.x, v_start.y ) ) + goto Fail; + + Close: + return SUCCESS; + + Invalid_Outline: + ras.error = FT_THROW( Invalid ); + + Fail: + return FAILURE; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Convert_Glyph */ + /* */ + /* <Description> */ + /* Convert a glyph into a series of segments and arcs and make a */ + /* profiles list with them. */ + /* */ + /* <Input> */ + /* flipped :: If set, flip the direction of curve. */ + /* */ + /* <Return> */ + /* SUCCESS on success, FAILURE if any error was encountered during */ + /* rendering. */ + /* */ + static Bool + Convert_Glyph( RAS_ARGS Int flipped ) + { + Int i; + UInt start; + + + ras.fProfile = NULL; + ras.joint = FALSE; + ras.fresh = FALSE; + + ras.maxBuff = ras.sizeBuff - AlignProfileSize; + + ras.numTurns = 0; + + ras.cProfile = (PProfile)ras.top; + ras.cProfile->offset = ras.top; + ras.num_Profs = 0; + + start = 0; + + for ( i = 0; i < ras.outline.n_contours; i++ ) + { + PProfile lastProfile; + Bool o; + + + ras.state = Unknown_State; + ras.gProfile = NULL; + + if ( Decompose_Curve( RAS_VARS (UShort)start, + (UShort)ras.outline.contours[i], + flipped ) ) + return FAILURE; + + start = (UShort)ras.outline.contours[i] + 1; + + /* we must now check whether the extreme arcs join or not */ + if ( FRAC( ras.lastY ) == 0 && + ras.lastY >= ras.minY && + ras.lastY <= ras.maxY ) + if ( ras.gProfile && + ( ras.gProfile->flags & Flow_Up ) == + ( ras.cProfile->flags & Flow_Up ) ) + ras.top--; + /* Note that ras.gProfile can be nil if the contour was too small */ + /* to be drawn. */ + + lastProfile = ras.cProfile; + if ( ras.top != ras.cProfile->offset && + ( ras.cProfile->flags & Flow_Up ) ) + o = IS_TOP_OVERSHOOT( ras.lastY ); + else + o = IS_BOTTOM_OVERSHOOT( ras.lastY ); + if ( End_Profile( RAS_VARS o ) ) + return FAILURE; + + /* close the `next profile in contour' linked list */ + if ( ras.gProfile ) + lastProfile->next = ras.gProfile; + } + + if ( Finalize_Profile_Table( RAS_VAR ) ) + return FAILURE; + + return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /** **/ + /** SCAN-LINE SWEEPS AND DRAWING **/ + /** **/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Init_Linked */ + /* */ + /* Initializes an empty linked list. */ + /* */ + static void + Init_Linked( TProfileList* l ) + { + *l = NULL; + } + + + /*************************************************************************/ + /* */ + /* InsNew */ + /* */ + /* Inserts a new profile in a linked list. */ + /* */ + static void + InsNew( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + Long x; + + + old = list; + current = *old; + x = profile->X; + + while ( current ) + { + if ( x < current->X ) + break; + old = ¤t->link; + current = *old; + } + + profile->link = current; + *old = profile; + } + + + /*************************************************************************/ + /* */ + /* DelOld */ + /* */ + /* Removes an old profile from a linked list. */ + /* */ + static void + DelOld( PProfileList list, + PProfile profile ) + { + PProfile *old, current; + + + old = list; + current = *old; + + while ( current ) + { + if ( current == profile ) + { + *old = current->link; + return; + } + + old = ¤t->link; + current = *old; + } + + /* we should never get there, unless the profile was not part of */ + /* the list. */ + } + + + /*************************************************************************/ + /* */ + /* Sort */ + /* */ + /* Sorts a trace list. In 95%, the list is already sorted. We need */ + /* an algorithm which is fast in this case. Bubble sort is enough */ + /* and simple. */ + /* */ + static void + Sort( PProfileList list ) + { + PProfile *old, current, next; + + + /* First, set the new X coordinate of each profile */ + current = *list; + while ( current ) + { + current->X = *current->offset; + current->offset += ( current->flags & Flow_Up ) ? 1 : -1; + current->height--; + current = current->link; + } + + /* Then sort them */ + old = list; + current = *old; + + if ( !current ) + return; + + next = current->link; + + while ( next ) + { + if ( current->X <= next->X ) + { + old = ¤t->link; + current = *old; + + if ( !current ) + return; + } + else + { + *old = next; + current->link = next->link; + next->link = current; + + old = list; + current = *old; + } + + next = current->link; + } + } + + + /*************************************************************************/ + /* */ + /* Vertical Sweep Procedure Set */ + /* */ + /* These four routines are used during the vertical black/white sweep */ + /* phase by the generic Draw_Sweep() function. */ + /* */ + /*************************************************************************/ + + static void + Vertical_Sweep_Init( RAS_ARGS Short* min, + Short* max ) + { + Long pitch = ras.target.pitch; + + FT_UNUSED( max ); + + + ras.traceIncr = (Short)-pitch; + ras.traceOfs = -*min * pitch; + if ( pitch > 0 ) + ras.traceOfs += (Long)( ras.target.rows - 1 ) * pitch; + } + + + static void + Vertical_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2; + Byte* target; + + Int dropOutControl = left->flags & 7; + + FT_UNUSED( y ); + FT_UNUSED( left ); + FT_UNUSED( right ); + + + /* in high-precision mode, we need 12 digits after the comma to */ + /* represent multiples of 1/(1<<12) = 1/4096 */ + FT_TRACE7(( " y=%d x=[%.12f;%.12f], drop-out=%d", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision, + dropOutControl )); + + /* Drop-out control */ + + e1 = TRUNC( CEILING( x1 ) ); + + if ( dropOutControl != 2 && + x2 - x1 - ras.precision <= ras.precision_jitter ) + e2 = e1; + else + e2 = TRUNC( FLOOR( x2 ) ); + + if ( e2 >= 0 && e1 < ras.bWidth ) + { + Int c1, c2; + Byte f1, f2; + + + if ( e1 < 0 ) + e1 = 0; + if ( e2 >= ras.bWidth ) + e2 = ras.bWidth - 1; + + FT_TRACE7(( " -> x=[%d;%d]", e1, e2 )); + + c1 = (Short)( e1 >> 3 ); + c2 = (Short)( e2 >> 3 ); + + f1 = (Byte) ( 0xFF >> ( e1 & 7 ) ); + f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) ); + + target = ras.bTarget + ras.traceOfs + c1; + c2 -= c1; + + if ( c2 > 0 ) + { + target[0] |= f1; + + /* memset() is slower than the following code on many platforms. */ + /* This is due to the fact that, in the vast majority of cases, */ + /* the span length in bytes is relatively small. */ + c2--; + while ( c2 > 0 ) + { + *(++target) = 0xFF; + c2--; + } + target[1] |= f2; + } + else + *target |= ( f1 & f2 ); + } + + FT_TRACE7(( "\n" )); + } + + + static void + Vertical_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2, pxl; + Short c1, f1; + + + FT_TRACE7(( " y=%d x=[%.12f;%.12f]", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision )); + + /* Drop-out control */ + + /* e2 x2 x1 e1 */ + /* */ + /* ^ | */ + /* | | */ + /* +-------------+---------------------+------------+ */ + /* | | */ + /* | v */ + /* */ + /* pixel contour contour pixel */ + /* center center */ + + /* drop-out mode scan conversion rules (as defined in OpenType) */ + /* --------------------------------------------------------------- */ + /* 0 1, 2, 3 */ + /* 1 1, 2, 4 */ + /* 2 1, 2 */ + /* 3 same as mode 2 */ + /* 4 1, 2, 5 */ + /* 5 1, 2, 6 */ + /* 6, 7 same as mode 2 */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + pxl = e1; + + if ( e1 > e2 ) + { + Int dropOutControl = left->flags & 7; + + + FT_TRACE7(( ", drop-out=%d", dropOutControl )); + + if ( e1 == e2 + ras.precision ) + { + switch ( dropOutControl ) + { + case 0: /* simple drop-outs including stubs */ + pxl = e2; + break; + + case 4: /* smart drop-outs including stubs */ + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + case 1: /* simple drop-outs excluding stubs */ + case 5: /* smart drop-outs excluding stubs */ + + /* Drop-out Control Rules #4 and #6 */ + + /* The specification neither provides an exact definition */ + /* of a `stub' nor gives exact rules to exclude them. */ + /* */ + /* Here the constraints we use to recognize a stub. */ + /* */ + /* upper stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Right is the successor of P_Left in that contour */ + /* - y is the top of P_Left and P_Right */ + /* */ + /* lower stub: */ + /* */ + /* - P_Left and P_Right are in the same contour */ + /* - P_Left is the successor of P_Right in that contour */ + /* - y is the bottom of P_Left */ + /* */ + /* We draw a stub if the following constraints are met. */ + /* */ + /* - for an upper or lower stub, there is top or bottom */ + /* overshoot, respectively */ + /* - the covered interval is greater or equal to a half */ + /* pixel */ + + /* upper stub test */ + if ( left->next == right && + left->height <= 0 && + !( left->flags & Overshoot_Top && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + /* lower stub test */ + if ( right->next == left && + left->start == y && + !( left->flags & Overshoot_Bottom && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + if ( dropOutControl == 1 ) + pxl = e2; + else + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + default: /* modes 2, 3, 6, 7 */ + goto Exit; /* no drop-out control */ + } + + /* undocumented but confirmed: If the drop-out would result in a */ + /* pixel outside of the bounding box, use the pixel inside of the */ + /* bounding box instead */ + if ( pxl < 0 ) + pxl = e1; + else if ( TRUNC( pxl ) >= ras.bWidth ) + pxl = e2; + + /* check that the other pixel isn't set */ + e1 = pxl == e1 ? e2 : e1; + + e1 = TRUNC( e1 ); + + c1 = (Short)( e1 >> 3 ); + f1 = (Short)( e1 & 7 ); + + if ( e1 >= 0 && e1 < ras.bWidth && + ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) ) + goto Exit; + } + else + goto Exit; + } + + e1 = TRUNC( pxl ); + + if ( e1 >= 0 && e1 < ras.bWidth ) + { + FT_TRACE7(( " -> x=%d (drop-out)", e1 )); + + c1 = (Short)( e1 >> 3 ); + f1 = (Short)( e1 & 7 ); + + ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 ); + } + + Exit: + FT_TRACE7(( "\n" )); + } + + + static void + Vertical_Sweep_Step( RAS_ARG ) + { + ras.traceOfs += ras.traceIncr; + } + + + /***********************************************************************/ + /* */ + /* Horizontal Sweep Procedure Set */ + /* */ + /* These four routines are used during the horizontal black/white */ + /* sweep phase by the generic Draw_Sweep() function. */ + /* */ + /***********************************************************************/ + + static void + Horizontal_Sweep_Init( RAS_ARGS Short* min, + Short* max ) + { + /* nothing, really */ + FT_UNUSED_RASTER; + FT_UNUSED( min ); + FT_UNUSED( max ); + } + + + static void + Horizontal_Sweep_Span( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + FT_UNUSED( left ); + FT_UNUSED( right ); + + + if ( x2 - x1 < ras.precision ) + { + Long e1, e2; + + + FT_TRACE7(( " x=%d y=[%.12f;%.12f]", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision )); + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + + if ( e1 == e2 ) + { + e1 = TRUNC( e1 ); + + if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) + { + Byte f1; + PByte bits; + PByte p; + + + FT_TRACE7(( " -> y=%d (drop-out)", e1 )); + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + p = bits - e1 * ras.target.pitch; + + if ( ras.target.pitch > 0 ) + p += (Long)( ras.target.rows - 1 ) * ras.target.pitch; + + p[0] |= f1; + } + } + + FT_TRACE7(( "\n" )); + } + } + + + static void + Horizontal_Sweep_Drop( RAS_ARGS Short y, + FT_F26Dot6 x1, + FT_F26Dot6 x2, + PProfile left, + PProfile right ) + { + Long e1, e2, pxl; + PByte bits; + Byte f1; + + + FT_TRACE7(( " x=%d y=[%.12f;%.12f]", + y, + x1 / (double)ras.precision, + x2 / (double)ras.precision )); + + /* During the horizontal sweep, we only take care of drop-outs */ + + /* e1 + <-- pixel center */ + /* | */ + /* x1 ---+--> <-- contour */ + /* | */ + /* | */ + /* x2 <--+--- <-- contour */ + /* | */ + /* | */ + /* e2 + <-- pixel center */ + + e1 = CEILING( x1 ); + e2 = FLOOR ( x2 ); + pxl = e1; + + if ( e1 > e2 ) + { + Int dropOutControl = left->flags & 7; + + + FT_TRACE7(( ", dropout=%d", dropOutControl )); + + if ( e1 == e2 + ras.precision ) + { + switch ( dropOutControl ) + { + case 0: /* simple drop-outs including stubs */ + pxl = e2; + break; + + case 4: /* smart drop-outs including stubs */ + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + case 1: /* simple drop-outs excluding stubs */ + case 5: /* smart drop-outs excluding stubs */ + /* see Vertical_Sweep_Drop for details */ + + /* rightmost stub test */ + if ( left->next == right && + left->height <= 0 && + !( left->flags & Overshoot_Top && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + /* leftmost stub test */ + if ( right->next == left && + left->start == y && + !( left->flags & Overshoot_Bottom && + x2 - x1 >= ras.precision_half ) ) + goto Exit; + + if ( dropOutControl == 1 ) + pxl = e2; + else + pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half ); + break; + + default: /* modes 2, 3, 6, 7 */ + goto Exit; /* no drop-out control */ + } + + /* undocumented but confirmed: If the drop-out would result in a */ + /* pixel outside of the bounding box, use the pixel inside of the */ + /* bounding box instead */ + if ( pxl < 0 ) + pxl = e1; + else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows ) + pxl = e2; + + /* check that the other pixel isn't set */ + e1 = pxl == e1 ? e2 : e1; + + e1 = TRUNC( e1 ); + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + + bits -= e1 * ras.target.pitch; + if ( ras.target.pitch > 0 ) + bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch; + + if ( e1 >= 0 && + (ULong)e1 < ras.target.rows && + *bits & f1 ) + goto Exit; + } + else + goto Exit; + } + + e1 = TRUNC( pxl ); + + if ( e1 >= 0 && (ULong)e1 < ras.target.rows ) + { + FT_TRACE7(( " -> y=%d (drop-out)", e1 )); + + bits = ras.bTarget + ( y >> 3 ); + f1 = (Byte)( 0x80 >> ( y & 7 ) ); + bits -= e1 * ras.target.pitch; + + if ( ras.target.pitch > 0 ) + bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch; + + bits[0] |= f1; + } + + Exit: + FT_TRACE7(( "\n" )); + } + + + static void + Horizontal_Sweep_Step( RAS_ARG ) + { + /* Nothing, really */ + FT_UNUSED_RASTER; + } + + + /*************************************************************************/ + /* */ + /* Generic Sweep Drawing routine */ + /* */ + /*************************************************************************/ + + static Bool + Draw_Sweep( RAS_ARG ) + { + Short y, y_change, y_height; + + PProfile P, Q, P_Left, P_Right; + + Short min_Y, max_Y, top, bottom, dropouts; + + Long x1, x2, xs, e1, e2; + + TProfileList waiting; + TProfileList draw_left, draw_right; + + + /* initialize empty linked lists */ + + Init_Linked( &waiting ); + + Init_Linked( &draw_left ); + Init_Linked( &draw_right ); + + /* first, compute min and max Y */ + + P = ras.fProfile; + max_Y = (Short)TRUNC( ras.minY ); + min_Y = (Short)TRUNC( ras.maxY ); + + while ( P ) + { + Q = P->link; + + bottom = (Short)P->start; + top = (Short)( P->start + P->height - 1 ); + + if ( min_Y > bottom ) + min_Y = bottom; + if ( max_Y < top ) + max_Y = top; + + P->X = 0; + InsNew( &waiting, P ); + + P = Q; + } + + /* check the Y-turns */ + if ( ras.numTurns == 0 ) + { + ras.error = FT_THROW( Invalid ); + return FAILURE; + } + + /* now initialize the sweep */ + + ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y ); + + /* then compute the distance of each profile from min_Y */ + + P = waiting; + + while ( P ) + { + P->countL = P->start - min_Y; + P = P->link; + } + + /* let's go */ + + y = min_Y; + y_height = 0; + + if ( ras.numTurns > 0 && + ras.sizeBuff[-ras.numTurns] == min_Y ) + ras.numTurns--; + + while ( ras.numTurns > 0 ) + { + /* check waiting list for new activations */ + + P = waiting; + + while ( P ) + { + Q = P->link; + P->countL -= y_height; + if ( P->countL == 0 ) + { + DelOld( &waiting, P ); + + if ( P->flags & Flow_Up ) + InsNew( &draw_left, P ); + else + InsNew( &draw_right, P ); + } + + P = Q; + } + + /* sort the drawing lists */ + + Sort( &draw_left ); + Sort( &draw_right ); + + y_change = (Short)ras.sizeBuff[-ras.numTurns--]; + y_height = (Short)( y_change - y ); + + while ( y < y_change ) + { + /* let's trace */ + + dropouts = 0; + + P_Left = draw_left; + P_Right = draw_right; + + while ( P_Left ) + { + x1 = P_Left ->X; + x2 = P_Right->X; + + if ( x1 > x2 ) + { + xs = x1; + x1 = x2; + x2 = xs; + } + + e1 = FLOOR( x1 ); + e2 = CEILING( x2 ); + + if ( x2 - x1 <= ras.precision && + e1 != x1 && e2 != x2 ) + { + if ( e1 > e2 || e2 == e1 + ras.precision ) + { + Int dropOutControl = P_Left->flags & 7; + + + if ( dropOutControl != 2 ) + { + /* a drop-out was detected */ + + P_Left ->X = x1; + P_Right->X = x2; + + /* mark profile for drop-out processing */ + P_Left->countL = 1; + dropouts++; + } + + goto Skip_To_Next; + } + } + + ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right ); + + Skip_To_Next: + + P_Left = P_Left->link; + P_Right = P_Right->link; + } + + /* handle drop-outs _after_ the span drawing -- */ + /* drop-out processing has been moved out of the loop */ + /* for performance tuning */ + if ( dropouts > 0 ) + goto Scan_DropOuts; + + Next_Line: + + ras.Proc_Sweep_Step( RAS_VAR ); + + y++; + + if ( y < y_change ) + { + Sort( &draw_left ); + Sort( &draw_right ); + } + } + + /* now finalize the profiles that need it */ + + P = draw_left; + while ( P ) + { + Q = P->link; + if ( P->height == 0 ) + DelOld( &draw_left, P ); + P = Q; + } + + P = draw_right; + while ( P ) + { + Q = P->link; + if ( P->height == 0 ) + DelOld( &draw_right, P ); + P = Q; + } + } + + /* for gray-scaling, flush the bitmap scanline cache */ + while ( y <= max_Y ) + { + ras.Proc_Sweep_Step( RAS_VAR ); + y++; + } + + return SUCCESS; + + Scan_DropOuts: + + P_Left = draw_left; + P_Right = draw_right; + + while ( P_Left ) + { + if ( P_Left->countL ) + { + P_Left->countL = 0; +#if 0 + dropouts--; /* -- this is useful when debugging only */ +#endif + ras.Proc_Sweep_Drop( RAS_VARS y, + P_Left->X, + P_Right->X, + P_Left, + P_Right ); + } + + P_Left = P_Left->link; + P_Right = P_Right->link; + } + + goto Next_Line; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Render_Single_Pass */ + /* */ + /* <Description> */ + /* Perform one sweep with sub-banding. */ + /* */ + /* <Input> */ + /* flipped :: If set, flip the direction of the outline. */ + /* */ + /* <Return> */ + /* Renderer error code. */ + /* */ + static int + Render_Single_Pass( RAS_ARGS Bool flipped ) + { + Short i, j, k; + + + while ( ras.band_top >= 0 ) + { + ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision; + ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision; + + ras.top = ras.buff; + + ras.error = Raster_Err_None; + + if ( Convert_Glyph( RAS_VARS flipped ) ) + { + if ( ras.error != Raster_Err_Overflow ) + return FAILURE; + + ras.error = Raster_Err_None; + + /* sub-banding */ + +#ifdef DEBUG_RASTER + ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) ); +#endif + + i = ras.band_stack[ras.band_top].y_min; + j = ras.band_stack[ras.band_top].y_max; + + k = (Short)( ( i + j ) / 2 ); + + if ( ras.band_top >= 7 || k < i ) + { + ras.band_top = 0; + ras.error = FT_THROW( Invalid ); + + return ras.error; + } + + ras.band_stack[ras.band_top + 1].y_min = k; + ras.band_stack[ras.band_top + 1].y_max = j; + + ras.band_stack[ras.band_top].y_max = (Short)( k - 1 ); + + ras.band_top++; + } + else + { + if ( ras.fProfile ) + if ( Draw_Sweep( RAS_VAR ) ) + return ras.error; + ras.band_top--; + } + } + + return SUCCESS; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* Render_Glyph */ + /* */ + /* <Description> */ + /* Render a glyph in a bitmap. Sub-banding if needed. */ + /* */ + /* <Return> */ + /* FreeType error code. 0 means success. */ + /* */ + static FT_Error + Render_Glyph( RAS_ARG ) + { + FT_Error error; + + + Set_High_Precision( RAS_VARS ras.outline.flags & + FT_OUTLINE_HIGH_PRECISION ); + ras.scale_shift = ras.precision_shift; + + if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS ) + ras.dropOutControl = 2; + else + { + if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS ) + ras.dropOutControl = 4; + else + ras.dropOutControl = 0; + + if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) ) + ras.dropOutControl += 1; + } + + ras.second_pass = (Bool)( !( ras.outline.flags & + FT_OUTLINE_SINGLE_PASS ) ); + + /* Vertical Sweep */ + FT_TRACE7(( "Vertical pass (ftraster)\n" )); + + ras.Proc_Sweep_Init = Vertical_Sweep_Init; + ras.Proc_Sweep_Span = Vertical_Sweep_Span; + ras.Proc_Sweep_Drop = Vertical_Sweep_Drop; + ras.Proc_Sweep_Step = Vertical_Sweep_Step; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = (Short)( ras.target.rows - 1 ); + + ras.bWidth = (UShort)ras.target.width; + ras.bTarget = (Byte*)ras.target.buffer; + + if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 ) + return error; + + /* Horizontal Sweep */ + if ( ras.second_pass && ras.dropOutControl != 2 ) + { + FT_TRACE7(( "Horizontal pass (ftraster)\n" )); + + ras.Proc_Sweep_Init = Horizontal_Sweep_Init; + ras.Proc_Sweep_Span = Horizontal_Sweep_Span; + ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop; + ras.Proc_Sweep_Step = Horizontal_Sweep_Step; + + ras.band_top = 0; + ras.band_stack[0].y_min = 0; + ras.band_stack[0].y_max = (Short)( ras.target.width - 1 ); + + if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 ) + return error; + } + + return Raster_Err_None; + } + + + static void + ft_black_init( black_PRaster raster ) + { + FT_UNUSED( raster ); + } + + + /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/ + /**** a static object. *****/ + + +#ifdef STANDALONE_ + + + static int + ft_black_new( void* memory, + FT_Raster *araster ) + { + static black_TRaster the_raster; + FT_UNUSED( memory ); + + + *araster = (FT_Raster)&the_raster; + FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) ); + ft_black_init( &the_raster ); + + return 0; + } + + + static void + ft_black_done( FT_Raster raster ) + { + /* nothing */ + FT_UNUSED( raster ); + } + + +#else /* !STANDALONE_ */ + + + static int + ft_black_new( FT_Memory memory, + black_PRaster *araster ) + { + FT_Error error; + black_PRaster raster = NULL; + + + *araster = 0; + if ( !FT_NEW( raster ) ) + { + raster->memory = memory; + ft_black_init( raster ); + + *araster = raster; + } + + return error; + } + + + static void + ft_black_done( black_PRaster raster ) + { + FT_Memory memory = (FT_Memory)raster->memory; + + + FT_FREE( raster ); + } + + +#endif /* !STANDALONE_ */ + + + static void + ft_black_reset( black_PRaster raster, + char* pool_base, + Long pool_size ) + { + FT_UNUSED( raster ); + FT_UNUSED( pool_base ); + FT_UNUSED( pool_size ); + } + + + static int + ft_black_set_mode( black_PRaster raster, + ULong mode, + const char* palette ) + { + FT_UNUSED( raster ); + FT_UNUSED( mode ); + FT_UNUSED( palette ); + + return 0; + } + + + static int + ft_black_render( black_PRaster raster, + const FT_Raster_Params* params ) + { + const FT_Outline* outline = (const FT_Outline*)params->source; + const FT_Bitmap* target_map = params->target; + + black_TWorker worker[1]; + + Long buffer[FT_MAX( FT_RENDER_POOL_SIZE, 2048 ) / sizeof ( Long )]; + + + if ( !raster ) + return FT_THROW( Not_Ini ); + + if ( !outline ) + return FT_THROW( Invalid ); + + /* return immediately if the outline is empty */ + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + return Raster_Err_None; + + if ( !outline->contours || !outline->points ) + return FT_THROW( Invalid ); + + if ( outline->n_points != + outline->contours[outline->n_contours - 1] + 1 ) + return FT_THROW( Invalid ); + + /* this version of the raster does not support direct rendering, sorry */ + if ( params->flags & FT_RASTER_FLAG_DIRECT ) + return FT_THROW( Unsupported ); + + if ( params->flags & FT_RASTER_FLAG_AA ) + return FT_THROW( Unsupported ); + + if ( !target_map ) + return FT_THROW( Invalid ); + + /* nothing to do */ + if ( !target_map->width || !target_map->rows ) + return Raster_Err_None; + + if ( !target_map->buffer ) + return FT_THROW( Invalid ); + + ras.outline = *outline; + ras.target = *target_map; + + worker->buff = buffer; + worker->sizeBuff = (&buffer)[1]; /* Points to right after buffer. */ + + return Render_Glyph( RAS_VAR ); + } + + + FT_DEFINE_RASTER_FUNCS( + ft_standard_raster, + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Raster_New_Func) ft_black_new, + (FT_Raster_Reset_Func) ft_black_reset, + (FT_Raster_Set_Mode_Func)ft_black_set_mode, + (FT_Raster_Render_Func) ft_black_render, + (FT_Raster_Done_Func) ft_black_done ) + + +/* END */ diff --git a/test_data/lots_of_files/ftraster.h b/test_data/lots_of_files/ftraster.h new file mode 100644 index 0000000..65cd5f9 --- /dev/null +++ b/test_data/lots_of_files/ftraster.h @@ -0,0 +1,46 @@ +/***************************************************************************/ +/* */ +/* ftraster.h */ +/* */ +/* The FreeType glyph rasterizer (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTRASTER_H_ +#define FTRASTER_H_ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_IMAGE_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* Uncomment the following line if you are using ftraster.c as a */ + /* standalone module, fully independent of FreeType. */ + /* */ +/* #define STANDALONE_ */ + + FT_EXPORT_VAR( const FT_Raster_Funcs ) ft_standard_raster; + + +FT_END_HEADER + +#endif /* FTRASTER_H_ */ + + +/* END */ diff --git a/test_data/lots_of_files/ftrend1.c b/test_data/lots_of_files/ftrend1.c new file mode 100644 index 0000000..494f112 --- /dev/null +++ b/test_data/lots_of_files/ftrend1.c @@ -0,0 +1,254 @@ +/***************************************************************************/ +/* */ +/* ftrend1.c */ +/* */ +/* The FreeType glyph rasterizer interface (body). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_OBJECTS_H +#include FT_OUTLINE_H +#include "ftrend1.h" +#include "ftraster.h" +#include "rastpic.h" + +#include "rasterrs.h" + + + /* initialize renderer -- init its raster */ + static FT_Error + ft_raster1_init( FT_Renderer render ) + { + FT_Library library = FT_MODULE_LIBRARY( render ); + + + render->clazz->raster_class->raster_reset( render->raster, + library->raster_pool, + library->raster_pool_size ); + + return FT_Err_Ok; + } + + + /* set render-specific mode */ + static FT_Error + ft_raster1_set_mode( FT_Renderer render, + FT_ULong mode_tag, + FT_Pointer data ) + { + /* we simply pass it to the raster */ + return render->clazz->raster_class->raster_set_mode( render->raster, + mode_tag, + data ); + } + + + /* transform a given glyph image */ + static FT_Error + ft_raster1_transform( FT_Renderer render, + FT_GlyphSlot slot, + const FT_Matrix* matrix, + const FT_Vector* delta ) + { + FT_Error error = FT_Err_Ok; + + + if ( slot->format != render->glyph_format ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( matrix ) + FT_Outline_Transform( &slot->outline, matrix ); + + if ( delta ) + FT_Outline_Translate( &slot->outline, delta->x, delta->y ); + + Exit: + return error; + } + + + /* return the glyph's control box */ + static void + ft_raster1_get_cbox( FT_Renderer render, + FT_GlyphSlot slot, + FT_BBox* cbox ) + { + FT_MEM_ZERO( cbox, sizeof ( *cbox ) ); + + if ( slot->format == render->glyph_format ) + FT_Outline_Get_CBox( &slot->outline, cbox ); + } + + + /* convert a slot's glyph image into a bitmap */ + static FT_Error + ft_raster1_render( FT_Renderer render, + FT_GlyphSlot slot, + FT_Render_Mode mode, + const FT_Vector* origin ) + { + FT_Error error; + FT_Outline* outline; + FT_BBox cbox, cbox0; + FT_UInt width, height, pitch; + FT_Bitmap* bitmap; + FT_Memory memory; + + FT_Raster_Params params; + + + /* check glyph image format */ + if ( slot->format != render->glyph_format ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* check rendering mode */ + if ( mode != FT_RENDER_MODE_MONO ) + { + /* raster1 is only capable of producing monochrome bitmaps */ + return FT_THROW( Cannot_Render_Glyph ); + } + + outline = &slot->outline; + + /* translate the outline to the new origin if needed */ + if ( origin ) + FT_Outline_Translate( outline, origin->x, origin->y ); + + /* compute the control box, and grid fit it */ + FT_Outline_Get_CBox( outline, &cbox0 ); + + /* undocumented but confirmed: bbox values get rounded */ +#if 1 + cbox.xMin = FT_PIX_ROUND( cbox0.xMin ); + cbox.yMin = FT_PIX_ROUND( cbox0.yMin ); + cbox.xMax = FT_PIX_ROUND( cbox0.xMax ); + cbox.yMax = FT_PIX_ROUND( cbox0.yMax ); +#else + cbox.xMin = FT_PIX_FLOOR( cbox.xMin ); + cbox.yMin = FT_PIX_FLOOR( cbox.yMin ); + cbox.xMax = FT_PIX_CEIL( cbox.xMax ); + cbox.yMax = FT_PIX_CEIL( cbox.yMax ); +#endif + + /* If either `width' or `height' round to 0, try */ + /* explicitly rounding up/down. In the case of */ + /* glyphs containing only one very narrow feature, */ + /* this gives the drop-out compensation in the scan */ + /* conversion code a chance to do its stuff. */ + width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 ); + if ( width == 0 ) + { + cbox.xMin = FT_PIX_FLOOR( cbox0.xMin ); + cbox.xMax = FT_PIX_CEIL( cbox0.xMax ); + + width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 ); + } + + height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 ); + if ( height == 0 ) + { + cbox.yMin = FT_PIX_FLOOR( cbox0.yMin ); + cbox.yMax = FT_PIX_CEIL( cbox0.yMax ); + + height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 ); + } + + if ( width > FT_USHORT_MAX || height > FT_USHORT_MAX ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + bitmap = &slot->bitmap; + memory = render->root.memory; + + /* release old bitmap buffer */ + if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) + { + FT_FREE( bitmap->buffer ); + slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; + } + + pitch = ( ( width + 15 ) >> 4 ) << 1; + bitmap->pixel_mode = FT_PIXEL_MODE_MONO; + + bitmap->width = width; + bitmap->rows = height; + bitmap->pitch = (int)pitch; + + if ( FT_ALLOC_MULT( bitmap->buffer, pitch, height ) ) + goto Exit; + + slot->internal->flags |= FT_GLYPH_OWN_BITMAP; + + /* translate outline to render it into the bitmap */ + FT_Outline_Translate( outline, -cbox.xMin, -cbox.yMin ); + + /* set up parameters */ + params.target = bitmap; + params.source = outline; + params.flags = 0; + + /* render outline into the bitmap */ + error = render->raster_render( render->raster, ¶ms ); + + FT_Outline_Translate( outline, cbox.xMin, cbox.yMin ); + + if ( error ) + goto Exit; + + slot->format = FT_GLYPH_FORMAT_BITMAP; + slot->bitmap_left = (FT_Int)( cbox.xMin >> 6 ); + slot->bitmap_top = (FT_Int)( cbox.yMax >> 6 ); + + Exit: + return error; + } + + + FT_DEFINE_RENDERER( ft_raster1_renderer_class, + + FT_MODULE_RENDERER, + sizeof ( FT_RendererRec ), + + "raster1", + 0x10000L, + 0x20000L, + + 0, /* module specific interface */ + + (FT_Module_Constructor)ft_raster1_init, + (FT_Module_Destructor) 0, + (FT_Module_Requester) 0 + , + + FT_GLYPH_FORMAT_OUTLINE, + + (FT_Renderer_RenderFunc) ft_raster1_render, + (FT_Renderer_TransformFunc)ft_raster1_transform, + (FT_Renderer_GetCBoxFunc) ft_raster1_get_cbox, + (FT_Renderer_SetModeFunc) ft_raster1_set_mode, + + (FT_Raster_Funcs*) &FT_STANDARD_RASTER_GET + ) + + +/* END */ diff --git a/test_data/lots_of_files/ftrend1.h b/test_data/lots_of_files/ftrend1.h new file mode 100644 index 0000000..a431f18 --- /dev/null +++ b/test_data/lots_of_files/ftrend1.h @@ -0,0 +1,38 @@ +/***************************************************************************/ +/* */ +/* ftrend1.h */ +/* */ +/* The FreeType glyph rasterizer interface (specification). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FTREND1_H_ +#define FTREND1_H_ + + +#include <ft2build.h> +#include FT_RENDER_H + + +FT_BEGIN_HEADER + + + FT_DECLARE_RENDERER( ft_raster1_renderer_class ) + + +FT_END_HEADER + +#endif /* FTREND1_H_ */ + + +/* END */ diff --git a/test_data/lots_of_files/fvec.h b/test_data/lots_of_files/fvec.h new file mode 100644 index 0000000..d6556f9 --- /dev/null +++ b/test_data/lots_of_files/fvec.h @@ -0,0 +1,507 @@ +/*** +*** Copyright (C) 1985-2011 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * Definition of a C++ class interface to Streaming SIMD Extension intrinsics. + * + * + * File name : fvec.h Fvec class definitions + * + * Concept: A C++ abstraction of Streaming SIMD Extensions designed to improve + * + * programmer productivity. Speed and accuracy are sacrificed for utility. + * + * Facilitates an easy transition to compiler intrinsics + * + * or assembly language. + * + * F32vec4: 4 packed single precision + * 32-bit floating point numbers +*/ + +#ifndef _FVEC_H_INCLUDED +#define _FVEC_H_INCLUDED +#ifndef RC_INVOKED + +#if !defined __cplusplus + #error ERROR: This file is only supported in C++ compilations! +#endif /* !defined __cplusplus */ + +#if defined (_M_CEE_PURE) + #error ERROR: This file is not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <xmmintrin.h> /* SSE Intrinsic function definition include file */ +#include <ivec.h> +#include <crtdefs.h> + +#ifndef _VEC_ASSERT +#ifdef NDEBUG + #define _VEC_ASSERT(_Expression) ((void)0) +#else /* NDEBUG */ +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + _CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message, _In_z_ const wchar_t *_File, _In_ unsigned _Line); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + + #define _VEC_ASSERT(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) ) +#endif /* NDEBUG */ +#endif /* _VEC_ASSERT */ + +/* Define _ENABLE_VEC_DEBUG to enable std::ostream inserters for debug output */ +#if defined (_ENABLE_VEC_DEBUG) + #include <iostream> +#endif /* defined (_ENABLE_VEC_DEBUG) */ + +#ifdef _MSC_VER +#pragma pack(push,_CRT_PACKING) +#endif /* _MSC_VER */ + +#pragma pack(push,16) /* Must ensure class & union 16-B aligned */ + +const union +{ + int i[4]; + __m128 m; +} __f32vec4_abs_mask_cheat = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; + +#define _f32vec4_abs_mask ((F32vec4)__f32vec4_abs_mask_cheat.m) + +class F32vec4 +{ +protected: + __m128 vec; +public: + + /* Constructors: __m128, 4 floats, 1 float */ + F32vec4() {} + + /* initialize 4 SP FP with __m128 data type */ + F32vec4(__m128 m) { vec = m;} + + /* initialize 4 SP FPs with 4 floats */ + F32vec4(float f3, float f2, float f1, float f0) { vec= _mm_set_ps(f3,f2,f1,f0); } + + /* Explicitly initialize each of 4 SP FPs with same float */ + EXPLICIT F32vec4(float f) { vec = _mm_set_ps1(f); } + + /* Explicitly initialize each of 4 SP FPs with same double */ + EXPLICIT F32vec4(double d) { vec = _mm_set_ps1((float) d); } + + /* Assignment operations */ + + F32vec4& operator =(float f) { vec = _mm_set_ps1(f); return *this; } + + F32vec4& operator =(double d) + { + vec = _mm_set_ps1((float) d); + return *this; + } + + /* Conversion functions */ + operator __m128() const { return vec; } /* Convert to __m128 */ + + /* Logical Operators */ + friend F32vec4 operator &(const F32vec4 &a, const F32vec4 &b) { return _mm_and_ps(a,b); } + friend F32vec4 operator |(const F32vec4 &a, const F32vec4 &b) { return _mm_or_ps(a,b); } + friend F32vec4 operator ^(const F32vec4 &a, const F32vec4 &b) { return _mm_xor_ps(a,b); } + + /* Arithmetic Operators */ + friend F32vec4 operator +(const F32vec4 &a, const F32vec4 &b) { return _mm_add_ps(a,b); } + friend F32vec4 operator -(const F32vec4 &a, const F32vec4 &b) { return _mm_sub_ps(a,b); } + friend F32vec4 operator *(const F32vec4 &a, const F32vec4 &b) { return _mm_mul_ps(a,b); } + friend F32vec4 operator /(const F32vec4 &a, const F32vec4 &b) { return _mm_div_ps(a,b); } + + F32vec4& operator =(const F32vec4 &a) { vec = a.vec; return *this; } + F32vec4& operator =(const __m128 &avec) { vec = avec; return *this; } + F32vec4& operator +=(const F32vec4 &a) { return *this = _mm_add_ps(vec,a); } + F32vec4& operator -=(const F32vec4 &a) { return *this = _mm_sub_ps(vec,a); } + F32vec4& operator *=(const F32vec4 &a) { return *this = _mm_mul_ps(vec,a); } + F32vec4& operator /=(const F32vec4 &a) { return *this = _mm_div_ps(vec,a); } + F32vec4& operator &=(const F32vec4 &a) { return *this = _mm_and_ps(vec,a); } + F32vec4& operator |=(const F32vec4 &a) { return *this = _mm_or_ps(vec,a); } + F32vec4& operator ^=(const F32vec4 &a) { return *this = _mm_xor_ps(vec,a); } + + /* Horizontal Add */ + friend float add_horizontal(const F32vec4 &a) + { + F32vec4 ftemp = _mm_add_ps(a, _mm_movehl_ps(a, a)); + ftemp = _mm_add_ss(ftemp, _mm_shuffle_ps(ftemp, ftemp, 1)); + return _mm_cvtss_f32(ftemp); + } + + /* Square Root */ + friend F32vec4 sqrt(const F32vec4 &a) { return _mm_sqrt_ps(a); } + /* Reciprocal */ + friend F32vec4 rcp(const F32vec4 &a) { return _mm_rcp_ps(a); } + /* Reciprocal Square Root */ + friend F32vec4 rsqrt(const F32vec4 &a) { return _mm_rsqrt_ps(a); } + + /* NewtonRaphson Reciprocal + [2 * rcpps(x) - (x * rcpps(x) * rcpps(x))] */ + friend F32vec4 rcp_nr(const F32vec4 &a) + { + F32vec4 Ra0 = _mm_rcp_ps(a); + return _mm_sub_ps(_mm_add_ps(Ra0, Ra0), _mm_mul_ps(_mm_mul_ps(Ra0, a), Ra0)); + } + + /* NewtonRaphson Reciprocal Square Root + 0.5 * rsqrtps * (3 - x * rsqrtps(x) * rsqrtps(x)) */ +#pragma warning(push) +#pragma warning(disable : 4640) + friend F32vec4 rsqrt_nr(const F32vec4 &a) + { + static const F32vec4 fvecf0pt5(0.5f); + static const F32vec4 fvecf3pt0(3.0f); + F32vec4 Ra0 = _mm_rsqrt_ps(a); + return (fvecf0pt5 * Ra0) * (fvecf3pt0 - (a * Ra0) * Ra0); + } +#pragma warning(pop) + + /* Compares: Mask is returned */ + /* Macros expand to all compare intrinsics. Example: + friend F32vec4 cmpeq(const F32vec4 &a, const F32vec4 &b) + { return _mm_cmpeq_ps(a,b);} */ + #define Fvec32s4_COMP(op) \ + friend F32vec4 cmp##op (const F32vec4 &a, const F32vec4 &b) { return _mm_cmp##op##_ps(a,b); } + Fvec32s4_COMP(eq) /* expanded to cmpeq(a,b) */ + Fvec32s4_COMP(lt) /* expanded to cmplt(a,b) */ + Fvec32s4_COMP(le) /* expanded to cmple(a,b) */ + Fvec32s4_COMP(gt) /* expanded to cmpgt(a,b) */ + Fvec32s4_COMP(ge) /* expanded to cmpge(a,b) */ + Fvec32s4_COMP(neq) /* expanded to cmpneq(a,b) */ + Fvec32s4_COMP(nlt) /* expanded to cmpnlt(a,b) */ + Fvec32s4_COMP(nle) /* expanded to cmpnle(a,b) */ + Fvec32s4_COMP(ngt) /* expanded to cmpngt(a,b) */ + Fvec32s4_COMP(nge) /* expanded to cmpnge(a,b) */ + #undef Fvec32s4_COMP + + /* Min and Max */ + friend F32vec4 simd_min(const F32vec4 &a, const F32vec4 &b) { return _mm_min_ps(a,b); } + friend F32vec4 simd_max(const F32vec4 &a, const F32vec4 &b) { return _mm_max_ps(a,b); } + + /* Absolute value */ + friend F32vec4 abs(const F32vec4 &a) {return _mm_and_ps(a, _f32vec4_abs_mask); } + + /* Debug Features */ +#if defined (_ENABLE_VEC_DEBUG) + /* Output */ + friend std::ostream & operator<<(std::ostream & os, const F32vec4 &a) + { + /* To use: cout << "Elements of F32vec4 fvec are: " << fvec; */ + float *fp = (float*)&a; + os << "[3]:" << *(fp+3) + << " [2]:" << *(fp+2) + << " [1]:" << *(fp+1) + << " [0]:" << *fp; + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + /* Element Access Only, no modifications to elements*/ + const float& operator[](int i) const + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 3)); /* User should only access elements 0-3 */ + float *fp = (float*)&vec; + return *(fp+i); + } + /* Element Access and Modification*/ + float& operator[](int i) + { + /* Assert enabled only during debug /DDEBUG */ + _VEC_ASSERT((0 <= i) && (i <= 3)); /* User should only access elements 0-3 */ + float *fp = (float*)&vec; + return *(fp+i); + } +}; + + /* Miscellaneous */ + +/* Interleave low order data elements of a and b into destination */ +inline F32vec4 unpack_low(const F32vec4 &a, const F32vec4 &b) +{ return _mm_unpacklo_ps(a, b); } + +/* Interleave high order data elements of a and b into target */ +inline F32vec4 unpack_high(const F32vec4 &a, const F32vec4 &b) +{ return _mm_unpackhi_ps(a, b); } + +/* Move Mask to Integer returns 4 bit mask formed of most significant bits of a */ +inline int move_mask(const F32vec4 &a) +{ return _mm_movemask_ps(a);} + + /* Data Motion Functions */ + +/* Load Unaligned loadu_ps: Unaligned */ +inline void loadu(F32vec4 &a, float *p) +{ a = _mm_loadu_ps(p); } + +/* Store Temporal storeu_ps: Unaligned */ +inline void storeu(float *p, const F32vec4 &a) +{ _mm_storeu_ps(p, a); } + + /* Cacheability Support */ + +/* Non-Temporal Store */ +inline void store_nta(float *p, const F32vec4 &a) +{ _mm_stream_ps(p,a);} + + /* Conditional Selects:*/ +/*(a OP b)? c : d; where OP is any compare operator +Macros expand to conditional selects which use all compare intrinsics. +Example: +friend F32vec4 select_eq(const F32vec4 &a, const F32vec4 &b, const F32vec4 &c, const F32vec4 &d) +{ + F32vec4 mask = _mm_cmpeq_ps(a,b); + return( (mask & c) | F32vec4((_mm_andnot_ps(mask,d)))); +} +*/ + +#define Fvec32s4_SELECT(op) \ +inline F32vec4 select_##op (const F32vec4 &a, const F32vec4 &b, const F32vec4 &c, const F32vec4 &d) \ +{ \ + F32vec4 mask = _mm_cmp##op##_ps(a,b); \ + return( (mask & c) | F32vec4((_mm_andnot_ps(mask,d)))); \ +} +Fvec32s4_SELECT(eq) /* generates select_eq(a,b) */ +Fvec32s4_SELECT(lt) /* generates select_lt(a,b) */ +Fvec32s4_SELECT(le) /* generates select_le(a,b) */ +Fvec32s4_SELECT(gt) /* generates select_gt(a,b) */ +Fvec32s4_SELECT(ge) /* generates select_ge(a,b) */ +Fvec32s4_SELECT(neq) /* generates select_neq(a,b) */ +Fvec32s4_SELECT(nlt) /* generates select_nlt(a,b) */ +Fvec32s4_SELECT(nle) /* generates select_nle(a,b) */ +Fvec32s4_SELECT(ngt) /* generates select_ngt(a,b) */ +Fvec32s4_SELECT(nge) /* generates select_nge(a,b) */ +#undef Fvec32s4_SELECT + +/* Streaming SIMD Extensions Integer Intrinsic Functions */ + +/* Max and Min */ +inline Is16vec4 simd_max(const Is16vec4 &a, const Is16vec4 &b) { return _m_pmaxsw(a,b);} +inline Is16vec4 simd_min(const Is16vec4 &a, const Is16vec4 &b) { return _m_pminsw(a,b);} +inline Iu8vec8 simd_max(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_pmaxub(a,b);} +inline Iu8vec8 simd_min(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_pminub(a,b);} + +/* Average */ +inline Iu16vec4 simd_avg(const Iu16vec4 &a, const Iu16vec4 &b) { return _mm_avg_pu16(a,b); } +inline Iu8vec8 simd_avg(const Iu8vec8 &a, const Iu8vec8 &b) { return _mm_avg_pu8(a,b); } + +/* Move ByteMask To Int: returns mask formed from most sig bits of each vec of a */ +inline int move_mask(const I8vec8 &a) { return _m_pmovmskb(a);} + +/* Packed Multiply High Unsigned */ +inline Iu16vec4 mul_high(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_pmulhuw(a,b); } + +/* Byte Mask Write: Write bytes if most significant bit in each corresponding byte is set */ +inline void mask_move(const I8vec8 &a, const I8vec8 &b, char *addr) { _m_maskmovq(a, b, addr); } + +/* Data Motion: Store Non Temporal */ +inline void store_nta(__m64 *p, const M64 &a) { _mm_stream_pi(p,a); } + +/* Conversions between ivec <-> fvec */ + +/* Convert first element of F32vec4 to int with truncation */ +inline int F32vec4ToInt(const F32vec4 &a) +{ + + return _mm_cvtt_ss2si(a); + +} + +/* Convert two lower SP FP values of a to Is32vec2 with truncation */ +inline Is32vec2 F32vec4ToIs32vec2 (const F32vec4 &a) +{ + + __m64 result; + result = _mm_cvtt_ps2pi(a); + return Is32vec2(result); + +} + +/* Convert the 32-bit int i to an SP FP value; the upper three SP FP values are passed through from a. */ +inline F32vec4 IntToF32vec4(const F32vec4 &a, int i) +{ + + __m128 result; + result = _mm_cvt_si2ss(a,i); + return F32vec4(result); + +} + +/* Convert the two 32-bit integer values in b to two SP FP values; the upper two SP FP values are passed from a. */ +inline F32vec4 Is32vec2ToF32vec4(const F32vec4 &a, const Is32vec2 &b) +{ + + __m128 result; + result = _mm_cvt_pi2ps(a,b); + return F32vec4(result); +} + +class F32vec1 +{ +protected: + __m128 vec; +public: + + /* Constructors: 1 float */ + F32vec1() {} + + F32vec1(int i) { vec = _mm_cvt_si2ss(vec,i);}; + + /* Initialize each of 4 SP FPs with same float */ + EXPLICIT F32vec1(float f) { vec = _mm_set_ss(f); } + + /* Initialize each of 4 SP FPs with same float */ + EXPLICIT F32vec1(double d) { vec = _mm_set_ss((float) d); } + + /* initialize with __m128 data type */ + F32vec1(__m128 m) { vec = m; } + + /* Conversion functions */ + operator __m128() const { return vec; } /* Convert to float */ + + /* Logical Operators */ + friend F32vec1 operator &(const F32vec1 &a, const F32vec1 &b) { return _mm_and_ps(a,b); } + friend F32vec1 operator |(const F32vec1 &a, const F32vec1 &b) { return _mm_or_ps(a,b); } + friend F32vec1 operator ^(const F32vec1 &a, const F32vec1 &b) { return _mm_xor_ps(a,b); } + + /* Arithmetic Operators */ + friend F32vec1 operator +(const F32vec1 &a, const F32vec1 &b) { return _mm_add_ss(a,b); } + friend F32vec1 operator -(const F32vec1 &a, const F32vec1 &b) { return _mm_sub_ss(a,b); } + friend F32vec1 operator *(const F32vec1 &a, const F32vec1 &b) { return _mm_mul_ss(a,b); } + friend F32vec1 operator /(const F32vec1 &a, const F32vec1 &b) { return _mm_div_ss(a,b); } + + F32vec1& operator +=(const F32vec1 &a) { return *this = _mm_add_ss(vec,a); } + F32vec1& operator -=(const F32vec1 &a) { return *this = _mm_sub_ss(vec,a); } + F32vec1& operator *=(const F32vec1 &a) { return *this = _mm_mul_ss(vec,a); } + F32vec1& operator /=(const F32vec1 &a) { return *this = _mm_div_ss(vec,a); } + F32vec1& operator &=(const F32vec1 &a) { return *this = _mm_and_ps(vec,a); } + F32vec1& operator |=(const F32vec1 &a) { return *this = _mm_or_ps(vec,a); } + F32vec1& operator ^=(const F32vec1 &a) { return *this = _mm_xor_ps(vec,a); } + + + /* Square Root */ + friend F32vec1 sqrt(const F32vec1 &a) { return _mm_sqrt_ss(a); } + /* Reciprocal */ + friend F32vec1 rcp(const F32vec1 &a) { return _mm_rcp_ss(a); } + /* Reciprocal Square Root */ + friend F32vec1 rsqrt(const F32vec1 &a) { return _mm_rsqrt_ss(a); } + + /* NewtonRaphson Reciprocal + [2 * rcpss(x) - (x * rcpss(x) * rcpss(x))] */ + friend F32vec1 rcp_nr(const F32vec1 &a) + { + F32vec1 Ra0 = _mm_rcp_ss(a); + return _mm_sub_ss(_mm_add_ss(Ra0, Ra0), _mm_mul_ss(_mm_mul_ss(Ra0, a), Ra0)); + } + + /* NewtonRaphson Reciprocal Square Root + 0.5 * rsqrtss * (3 - x * rsqrtss(x) * rsqrtss(x)) */ +#pragma warning(push) +#pragma warning(disable : 4640) + friend F32vec1 rsqrt_nr(const F32vec1 &a) + { + static const F32vec1 fvecf0pt5(0.5f); + static const F32vec1 fvecf3pt0(3.0f); + F32vec1 Ra0 = _mm_rsqrt_ss(a); + return (fvecf0pt5 * Ra0) * (fvecf3pt0 - (a * Ra0) * Ra0); + } +#pragma warning(pop) + + /* Compares: Mask is returned */ + /* Macros expand to all compare intrinsics. Example: + friend F32vec1 cmpeq(const F32vec1 &a, const F32vec1 &b) + { return _mm_cmpeq_ss(a,b);} */ + #define Fvec32s1_COMP(op) \ + friend F32vec1 cmp##op (const F32vec1 &a, const F32vec1 &b) { return _mm_cmp##op##_ss(a,b); } + Fvec32s1_COMP(eq) /* expanded to cmpeq(a,b) */ + Fvec32s1_COMP(lt) /* expanded to cmplt(a,b) */ + Fvec32s1_COMP(le) /* expanded to cmple(a,b) */ + Fvec32s1_COMP(gt) /* expanded to cmpgt(a,b) */ + Fvec32s1_COMP(ge) /* expanded to cmpge(a,b) */ + Fvec32s1_COMP(neq) /* expanded to cmpneq(a,b) */ + Fvec32s1_COMP(nlt) /* expanded to cmpnlt(a,b) */ + Fvec32s1_COMP(nle) /* expanded to cmpnle(a,b) */ + Fvec32s1_COMP(ngt) /* expanded to cmpngt(a,b) */ + Fvec32s1_COMP(nge) /* expanded to cmpnge(a,b) */ + #undef Fvec32s1_COMP + + /* Min and Max */ + friend F32vec1 simd_min(const F32vec1 &a, const F32vec1 &b) { return _mm_min_ss(a,b); } + friend F32vec1 simd_max(const F32vec1 &a, const F32vec1 &b) { return _mm_max_ss(a,b); } + + /* Debug Features */ +#if defined (_ENABLE_VEC_DEBUG) + /* Output */ + friend std::ostream & operator<<(std::ostream & os, const F32vec1 &a) + { + /* To use: cout << "Elements of F32vec1 fvec are: " << fvec; */ + float *fp = (float*)&a; + os << "float:" << *fp; + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + +}; + + /* Conditional Selects:*/ +/*(a OP b)? c : d; where OP is any compare operator +Macros expand to conditional selects which use all compare intrinsics. +Example: +friend F32vec1 select_eq(const F32vec1 &a, const F32vec1 &b, const F32vec1 &c, const F32vec1 &d) +{ + F32vec1 mask = _mm_cmpeq_ss(a,b); + return( (mask & c) | F32vec1((_mm_andnot_ps(mask,d)))); +} +*/ + +#define Fvec32s1_SELECT(op) \ +inline F32vec1 select_##op (const F32vec1 &a, const F32vec1 &b, const F32vec1 &c, const F32vec1 &d) \ +{ \ + F32vec1 mask = _mm_cmp##op##_ss(a,b); \ + return( (mask & c) | F32vec1((_mm_andnot_ps(mask,d)))); \ +} +Fvec32s1_SELECT(eq) /* generates select_eq(a,b) */ +Fvec32s1_SELECT(lt) /* generates select_lt(a,b) */ +Fvec32s1_SELECT(le) /* generates select_le(a,b) */ +Fvec32s1_SELECT(gt) /* generates select_gt(a,b) */ +Fvec32s1_SELECT(ge) /* generates select_ge(a,b) */ +Fvec32s1_SELECT(neq) /* generates select_neq(a,b) */ +Fvec32s1_SELECT(nlt) /* generates select_nlt(a,b) */ +Fvec32s1_SELECT(nle) /* generates select_nle(a,b) */ +Fvec32s1_SELECT(ngt) /* generates select_ngt(a,b) */ +Fvec32s1_SELECT(nge) /* generates select_nge(a,b) */ +#undef Fvec32s1_SELECT + +/* Conversions between ivec <-> fvec */ + +/* Convert F32vec1 to int */ +inline int F32vec1ToInt(const F32vec1 &a) +{ + return _mm_cvtt_ss2si(a); +} + + + +#pragma pack(pop) /* 16-B aligned */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* RC_INVOKED */ +#endif /* _FVEC_H_INCLUDED */ diff --git a/test_data/lots_of_files/gcroot.h b/test_data/lots_of_files/gcroot.h new file mode 100644 index 0000000..4d8de7f --- /dev/null +++ b/test_data/lots_of_files/gcroot.h @@ -0,0 +1,45 @@ +// +// gcroot.h - Template class that wraps GCHandle from mscorlib.dll. +// Copyright (C) Microsoft Corporation +// All rights reserved. +// +// This include file is provided for back-compatibilty. +// Include <msclr\gcroot.h> and use ::msclr::gcroot instead of ::gcroot. +// +// Use this class to declare gc "pointers" that live in the C++ heap. +// +// Example: +// struct StringList { +// gcroot<String^> str; +// StringList *next; +// StringList(); // should have ctors and dtors +// ~StringList(); +// }; +// +// By convention, we maintain a 1-to-1 relationship between C++ objects +// and the handle slots they "point" to. Thus, two distinct C++ objects +// always refer to two distinct handles, even if they "point" to the same +// object. Therefore, when the C++ object is destroyed, its handle can +// be freed without error. +// +// Note that we cannot currently embed a GCHandle directly in an unmanaged C++ +// class. We therefore store a void*, and use the conversion methods of +// GCHandle to reconstitute a GCHandle from the void* on demand. +// +// See msclr\gcroot.h for implementations details. + + +#if _MSC_VER > 1000 +#pragma once +#endif + +#if !defined(_INC_GCROOT) +#define _INC_GCROOT +#ifndef RC_INVOKED + +#include <stddef.h> +#define __DEFINE_GCROOT_IN_GLOBAL_NAMESPACE +#include <msclr\gcroot.h> + +#endif /* RC_INVOKED */ +#endif // _INC_GCROOT diff --git a/test_data/lots_of_files/gjAPI.cpp b/test_data/lots_of_files/gjAPI.cpp new file mode 100644 index 0000000..a87bdb5 --- /dev/null +++ b/test_data/lots_of_files/gjAPI.cpp @@ -0,0 +1,1176 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#include "gjAPI.h" + +#include <sstream> +#include <iostream> +#include <algorithm> + +std::vector<std::string> gjAPI::s_asLog; + + +// **************************************************************** +/* constructor */ +gjAPI::gjInterUser::gjInterUser(gjAPI* pAPI, gjNetwork* pNetwork)noexcept +: m_pAPI (pAPI) +, m_pNetwork (pNetwork) +{ + // create NULL user for secure object handling + gjData pNullData; + pNullData["id"] = "0"; + pNullData["username"] = "NOT FOUND"; + pNullData["type"] = "Guest"; + pNullData["avatar_url"] = GJ_API_AVATAR_DEFAULT; + m_apUser[0] = new gjUser(pNullData, m_pAPI); + + // create guest user for secure object handling + gjData pGuestData; + pGuestData["id"] = "-1"; + pGuestData["username"] = "Guest"; + pGuestData["type"] = "Guest"; + pGuestData["avatar_url"] = GJ_API_AVATAR_DEFAULT; + m_apUser[-1] = new gjUser(pGuestData, m_pAPI); +} + + +// **************************************************************** +/* destructor */ +gjAPI::gjInterUser::~gjInterUser() +{ + // delete all users + FOR_EACH(it, m_apUser) + SAFE_DELETE(it->second) + + // clear container + m_apUser.clear(); +} + + +// **************************************************************** +/* access user objects directly (may block) */ +gjUser* gjAPI::gjInterUser::GetUser(const int& iID) +{ + gjUserPtr pOutput; + + if(this->__CheckCache(iID, &pOutput) == GJ_OK) return pOutput; + if(this->FetchUserNow(iID, &pOutput) == GJ_OK) return pOutput; + + return m_apUser[0]; +} + +gjUser* gjAPI::gjInterUser::GetUser(const std::string& sName) +{ + gjUserPtr pOutput; + + if(this->__CheckCache(sName, &pOutput) == GJ_OK) return pOutput; + if(this->FetchUserNow(sName, &pOutput) == GJ_OK) return pOutput; + + return m_apUser[0]; +} + + +// **************************************************************** +/* access main user object directly (may block) */ +gjUser* gjAPI::gjInterUser::GetMainUser() +{ + if(!m_pAPI->IsUserConnected()) return m_apUser[0]; + return this->GetUser(m_pAPI->GetUserName()); +} + + +// **************************************************************** +/* delete all cached user objects */ +void gjAPI::gjInterUser::ClearCache() +{ + // save NULL user and guest user + gjUser* pNull = m_apUser[0]; m_apUser.erase(0); + gjUser* pGuest = m_apUser[-1]; m_apUser.erase(-1); + + // delete users + FOR_EACH(it, m_apUser) + SAFE_DELETE(it->second) + + // clear container + m_apUser.clear(); + m_apUser[0] = pNull; + m_apUser[-1] = pGuest; +} + + +// **************************************************************** +/* check for cached user objects */ +int gjAPI::gjInterUser::__CheckCache(const int& iID, gjUserPtr* ppOutput) +{ + // retrieve cached user + if(m_apUser.count(iID)) + { + if(ppOutput) (*ppOutput) = m_apUser[iID]; + return GJ_OK; + } + return GJ_NO_DATA_FOUND; +} + +int gjAPI::gjInterUser::__CheckCache(const std::string& sName, gjUserPtr* ppOutput) +{ + // retrieve cached user + FOR_EACH(it, m_apUser) + { + if(it->second->GetName() == sName) + { + if(ppOutput) (*ppOutput) = it->second; + return GJ_OK; + } + } + return GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* process user data and cache user objects */ +int gjAPI::gjInterUser::__Process(const std::string& sData, void* pAdd, gjUserPtr* ppOutput) +{ + // parse output + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse user"); + if(ppOutput) (*ppOutput) = m_apUser[0]; + return GJ_REQUEST_FAILED; + } + + // create and cache user object + gjUser* pNewUser = new gjUser(aaReturn[0], m_pAPI); + const int iID = pNewUser->GetID(); + + if(m_apUser.count(iID)) + { + SAFE_DELETE(pNewUser) + pNewUser = m_apUser[iID]; + } + else m_apUser[iID] = pNewUser; + + if(ppOutput) (*ppOutput) = pNewUser; + return pNewUser ? GJ_OK : GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* constructor */ +gjAPI::gjInterTrophy::gjInterTrophy(gjAPI* pAPI, gjNetwork* pNetwork)noexcept +: m_iCache (0) +, m_pAPI (pAPI) +, m_pNetwork (pNetwork) +{ + // create NULL trophy for secure object handling + gjData pNullData; + pNullData["id"] = "0"; + pNullData["title"] = "NOT FOUND"; + pNullData["difficulty"] = "Bronze"; + pNullData["image_url"] = GJ_API_TROPHY_DEFAULT_1; + m_apTrophy[0] = new gjTrophy(pNullData, m_pAPI); + + // reserve some memory + m_aiSort.reserve(GJ_API_RESERVE_TROPHY); + m_aiSecret.reserve(GJ_API_RESERVE_TROPHY); + m_aiHidden.reserve(GJ_API_RESERVE_TROPHY); + + // retrieve offline-cached trophy data + this->__LoadOffCache(); +} + + +// **************************************************************** +/* destructor */ +gjAPI::gjInterTrophy::~gjInterTrophy() +{ + // delete all trophies + FOR_EACH(it, m_apTrophy) + SAFE_DELETE(it->second) + + // clear containers + m_apTrophy.clear(); + m_aiSort.clear(); + m_aiSecret.clear(); + m_aiHidden.clear(); +} + + +// **************************************************************** +/* access trophy objects directly (may block) */ +gjTrophy* gjAPI::gjInterTrophy::GetTrophy(const int& iID) +{ + if(!m_pAPI->IsUserConnected() && m_iCache == 0) return m_apTrophy[0]; + if(m_apTrophy.size() <= 1) + { + // wait for prefetching + if(GJ_API_PREFETCH) m_pNetwork->Wait(2); + if(m_apTrophy.size() <= 1) + { + gjTrophyList apOutput; + this->FetchTrophiesNow(0, &apOutput); + } + } + return m_apTrophy.count(iID) ? m_apTrophy[iID] : m_apTrophy[0]; +} + + +// **************************************************************** +/* delete all cached trophy objects */ +void gjAPI::gjInterTrophy::ClearCache(const bool& bFull) +{ + const bool bRemoveAll = bFull || !GJ_API_OFFCACHE_TROPHY; + + if(bRemoveAll) + { + // save NULL trophy + gjTrophy* pNull = m_apTrophy[0]; + m_apTrophy.erase(0); + + // delete trophies + FOR_EACH(it, m_apTrophy) + SAFE_DELETE(it->second) + + // clear container + m_apTrophy.clear(); + m_apTrophy[0] = pNull; + } + + // set cache status + m_iCache = bRemoveAll ? 0 : 1; +} + + +// **************************************************************** +/* define layout of the returned trophy list */ +void gjAPI::gjInterTrophy::SetSort(const int* piIDList, const size_t& iNum) +{ + if(iNum) + { + // clear sort list + m_aiSort.clear(); + + // add IDs to sort list + for(size_t i = 0; i < iNum; ++i) + m_aiSort.push_back(piIDList[i]); + } + + // apply sort attribute + FOR_EACH(it, m_apTrophy) + it->second->__SetSort(0); + for(size_t i = 0; i < m_aiSort.size(); ++i) + if(m_apTrophy.count(m_aiSort[i])) m_apTrophy[m_aiSort[i]]->__SetSort(int(i+1)); +} + + +// **************************************************************** +/* define secret trophy objects */ +void gjAPI::gjInterTrophy::SetSecret(const int* piIDList, const size_t& iNum) +{ + if(iNum) + { + // clear secret list + m_aiSecret.clear(); + + // add IDs to secret list + for(size_t i = 0; i < iNum; ++i) + m_aiSecret.push_back(piIDList[i]); + } + + // apply secret attribute + FOR_EACH(it, m_apTrophy) + it->second->__SetSecret(false); + FOR_EACH(it, m_aiSecret) + if(m_apTrophy.count(*it)) m_apTrophy[*it]->__SetSecret(true); +} + + +// **************************************************************** +/* define hidden trophy objects */ +void gjAPI::gjInterTrophy::SetHidden(const int* piIDList, const size_t& iNum) +{ + if(iNum) + { + // clear hidden list + m_aiHidden.clear(); + + // add IDs to hidden list + for(size_t i = 0; i < iNum; ++i) + m_aiHidden.push_back(piIDList[i]); + } + + // apply hidden attribute and remove all hidden trophy objects + FOR_EACH(it, m_aiHidden) + if(m_apTrophy.count(*it)) m_apTrophy.erase(m_apTrophy.find(*it)); +} + + +// **************************************************************** +/* check for cached trophy objects */ +int gjAPI::gjInterTrophy::__CheckCache(const int& iAchieved, gjTrophyList* papOutput) +{ + // retrieve cached trophies + if(m_apTrophy.size() > 1) + { + if(papOutput) + { + gjTrophyList apConvert; + apConvert.reserve(GJ_API_RESERVE_TROPHY); + + // add sorted trophies + FOR_EACH(it, m_aiSort) + if(m_apTrophy.count(*it)) apConvert.push_back(m_apTrophy[*it]); + + // add missing unsorted trophies + FOR_EACH(it, m_apTrophy) + { + if(it->first) + { + if(std::find(apConvert.begin(), apConvert.end(), it->second) == apConvert.end()) + apConvert.push_back(it->second); + } + } + + // check for achieved status + FOR_EACH(it, apConvert) + { + gjTrophy* pTrophy = (*it); + + if((iAchieved > 0 && pTrophy->IsAchieved()) || + (iAchieved < 0 && !pTrophy->IsAchieved()) || !iAchieved) + (*papOutput).push_back(pTrophy); + } + } + return GJ_OK; + } + return GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* process trophy data and cache trophy objects */ +int gjAPI::gjInterTrophy::__Process(const std::string& sData, void* pAdd, gjTrophyList* papOutput) +{ + // parse output + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse trophies"); + return GJ_REQUEST_FAILED; + } + + // offline-cache trophy data + if(!aaReturn.empty()) this->__SaveOffCache(sData); + if(m_iCache == 0) m_iCache = 2; + + // create and cache trophy objects + FOR_EACH(it, aaReturn) + { + gjTrophy* pNewTrophy = new gjTrophy(*it, m_pAPI); + const int iID = pNewTrophy->GetID(); + + if(m_apTrophy.count(iID)) + { + *m_apTrophy[iID] = *pNewTrophy; + SAFE_DELETE(pNewTrophy) + } + else m_apTrophy[iID] = pNewTrophy; + } + + // apply attributes + this->SetSort (NULL, 0); + this->SetSecret(NULL, 0); + this->SetHidden(NULL, 0); + + return (this->__CheckCache(P_TO_I(pAdd), papOutput) == GJ_OK) ? GJ_OK : GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* save trophy data to a cache file */ +void gjAPI::gjInterTrophy::__SaveOffCache(const std::string& sData) +{ + if(!GJ_API_OFFCACHE_TROPHY) return; + if(m_iCache != 0) return; + + // open cache file + std::FILE* pFile = std::fopen(GJ_API_OFFCACHE_NAME, "w"); + if(pFile) + { + // write data and close cache file + std::fputs("[TROPHY]\n", pFile); + std::fputs(sData.c_str(), pFile); + std::fclose(pFile); + } +} + + +// **************************************************************** +/* load trophy data from a cache file */ +void gjAPI::gjInterTrophy::__LoadOffCache() +{ + if(!GJ_API_OFFCACHE_TROPHY) return; + if(m_iCache != 0) return; + + // open cache file + std::FILE* pFile = std::fopen(GJ_API_OFFCACHE_NAME, "r"); + if(pFile) + { + // read trophy header + char acHeader[32]; + std::fscanf(pFile, "%31[^\n]%*c", acHeader); + + // read trophy data + std::string sData; + while(true) + { + char acLine[1024]; + std::fscanf(pFile, "%1023[^\n]%*c", acLine); + if(std::feof(pFile)) break; + + if(std::strlen(acLine) > 1) + { + sData += acLine; + sData += '\n'; + } + } + + // close cache file + std::fclose(pFile); + + if(!sData.empty()) + { + // flag offline caching and load offline-cached trophies + m_iCache = 1; + this->__Process(sData, NULL, NULL); + } + } +} + + +// **************************************************************** +/* constructor */ +gjAPI::gjInterScore::gjInterScore(gjAPI* pAPI, gjNetwork* pNetwork)noexcept +: m_pAPI (pAPI) +, m_pNetwork (pNetwork) +{ + // create NULL score table for secure object handling + gjData pNullData; + pNullData["id"] = "0"; + pNullData["name"] = "NOT FOUND"; + m_apScoreTable[0] = new gjScoreTable(pNullData, m_pAPI); +} + + +// **************************************************************** +/* destructor */ +gjAPI::gjInterScore::~gjInterScore() +{ + // delete all score tables and scores entries + FOR_EACH(it, m_apScoreTable) + SAFE_DELETE(it->second) + + // clear container + m_apScoreTable.clear(); +} + + +// **************************************************************** +/* access score table objects directly (may block) */ +gjScoreTable* gjAPI::gjInterScore::GetScoreTable(const int &iID) +{ + if(m_apScoreTable.size() <= 1) + { + // wait for prefetching + if(GJ_API_PREFETCH) m_pNetwork->Wait(2); + if(m_apScoreTable.size() <= 1) + { + gjScoreTableMap apOutput; + this->FetchScoreTablesNow(&apOutput); + } + } + gjScoreTable* pPrimary = gjScoreTable::GetPrimary(); + return iID ? (m_apScoreTable.count(iID) ? m_apScoreTable[iID] : m_apScoreTable[0]) : (pPrimary ? pPrimary : m_apScoreTable[0]); +} + + +// **************************************************************** +/* delete all cached score table objects and score entries */ +void gjAPI::gjInterScore::ClearCache() +{ + // save NULL score table + gjScoreTable* pNull = m_apScoreTable[0]; m_apScoreTable.erase(0); + + // delete score tables and scores entries + FOR_EACH(it, m_apScoreTable) + SAFE_DELETE(it->second) + + // clear container + m_apScoreTable.clear(); + m_apScoreTable[0] = pNull; +} + + +// **************************************************************** +/* check for cached score table objects */ +int gjAPI::gjInterScore::__CheckCache(gjScoreTableMap* papOutput) +{ + // retrieve cached score tables + if(m_apScoreTable.size() > 1) + { + if(papOutput) + { + FOR_EACH(it, m_apScoreTable) + if(it->first) (*papOutput)[it->first] = it->second; + } + return GJ_OK; + } + return GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* process score table data and cache score table objects */ +int gjAPI::gjInterScore::__Process(const std::string& sData, void* pAdd, gjScoreTableMap* papOutput) +{ + // parse output + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse score tables"); + return GJ_REQUEST_FAILED; + } + + // create and cache score tables + FOR_EACH(it, aaReturn) + { + gjScoreTable* pNewScoreTable = new gjScoreTable(*it, m_pAPI); + const int iID = pNewScoreTable->GetID(); + + if(m_apScoreTable.count(iID)) SAFE_DELETE(pNewScoreTable) + else m_apScoreTable[iID] = pNewScoreTable; + } + + return (this->__CheckCache(papOutput) == GJ_OK) ? GJ_OK : GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* constructor */ +gjAPI::gjInterDataStore::gjInterDataStore(const int& iType, gjAPI* pAPI, gjNetwork* pNetwork)noexcept +: m_iType (iType) +, m_pAPI (pAPI) +, m_pNetwork (pNetwork) +{ +} + + +// **************************************************************** +/* destructor */ +gjAPI::gjInterDataStore::~gjInterDataStore() +{ + this->ClearCache(); +} + + +// **************************************************************** +/* create and access data store items directly */ +gjDataItem* gjAPI::gjInterDataStore::GetDataItem(const std::string& sKey) +{ + // create new data store item + if(!m_apDataItem.count(sKey)) + { + gjData asDataItemData; + asDataItemData["key"] = sKey; + m_apDataItem[sKey] = new gjDataItem(asDataItemData, m_iType, m_pAPI); + } + + return m_apDataItem.count(sKey) ? m_apDataItem[sKey] : NULL; +} + + +// **************************************************************** +/* delete all cached data store items */ +void gjAPI::gjInterDataStore::ClearCache() +{ + // delete data store items + FOR_EACH(it, m_apDataItem) + SAFE_DELETE(it->second) + + // clear container + m_apDataItem.clear(); +} + + +// **************************************************************** +/* check for cached data store items */ +int gjAPI::gjInterDataStore::__CheckCache(gjDataItemMap* papOutput) +{ + // retrieve cached data store items + if(!m_apDataItem.empty()) + { + if(papOutput) + { + FOR_EACH(it, m_apDataItem) + (*papOutput)[it->first] = it->second; + } + return GJ_OK; + } + return GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* process data store data and cache data store items */ +int gjAPI::gjInterDataStore::__Process(const std::string& sData, void* pAdd, gjDataItemMap* papOutput) +{ + // parse output + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse data store items"); + return GJ_REQUEST_FAILED; + } + + // create and cache data store items + FOR_EACH(it, aaReturn) + { + gjDataItem* pNewDataItem = new gjDataItem(*it, m_iType, m_pAPI); + const std::string& sKey = pNewDataItem->GetKey(); + + if(m_apDataItem.count(sKey)) + { + SAFE_DELETE(pNewDataItem) + pNewDataItem = m_apDataItem[sKey]; + } + else m_apDataItem[sKey] = pNewDataItem; + + if(papOutput) (*papOutput)[sKey] = pNewDataItem; + } + + return aaReturn.empty() ? GJ_NO_DATA_FOUND : GJ_OK; +} + + +// **************************************************************** +/* constructor */ +gjAPI::gjInterFile::gjInterFile(gjAPI* pAPI, gjNetwork* pNetwork)noexcept +: m_pAPI (pAPI) +, m_pNetwork (pNetwork) +{ + // reserve some memory + m_asFile.reserve(GJ_API_RESERVE_FILE); +} + + +// **************************************************************** +/* destructor */ +gjAPI::gjInterFile::~gjInterFile() +{ + this->ClearCache(); +} + + +// **************************************************************** +/* delete all cached file paths */ +void gjAPI::gjInterFile::ClearCache() +{ + // clear container + m_asFile.clear(); +} + + +// **************************************************************** +/* check for cached files */ +int gjAPI::gjInterFile::__CheckCache(const std::string& sPath) +{ + // compare cached file paths + FOR_EACH(it, m_asFile) + { + if(sPath == (*it)) + return GJ_OK; + } + return GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* process downloaded file */ +int gjAPI::gjInterFile::__Process(const std::string& sData, void* pAdd, std::string* psOutput) +{ + // save path of the file + if(this->__CheckCache(sData) != GJ_OK) m_asFile.push_back(sData); + if(psOutput) (*psOutput) = sData; + + return GJ_OK; +} + + +// **************************************************************** +/* constructor */ +gjAPI::gjAPI(const int iGameID, const std::string sGamePrivateKey)noexcept +: m_iGameID (iGameID) +, m_sGamePrivateKey (sGamePrivateKey) +, m_sUserName ("") +, m_sUserToken ("") +, m_iNextPing (0) +, m_bActive (false) +, m_bConnected (false) +, m_sProcUserName ("") +, m_sProcUserToken ("") +{ + // init error log + gjAPI::ErrorLogReset(); + + // pre-process the game ID + m_sProcGameID = gjAPI::UtilIntToString(m_iGameID); + + // create network object + m_pNetwork = new gjNetwork(this); + + // create sub-interface objects + m_pInterUser = new gjInterUser(this, m_pNetwork); + m_pInterTrophy = new gjInterTrophy(this, m_pNetwork); + m_pInterScore = new gjInterScore(this, m_pNetwork); + m_pInterDataStoreGlobal = new gjInterDataStore(0, this, m_pNetwork); + m_pInterDataStoreUser = new gjInterDataStore(1, this, m_pNetwork); + m_pInterFile = new gjInterFile(this, m_pNetwork); + + // prefetch score tables + if(GJ_API_PREFETCH && iGameID) m_pInterScore->FetchScoreTablesCall(GJ_NETWORK_NULL_THIS(gjScoreTableMap)); +} + + +// **************************************************************** +/* destructor */ +gjAPI::~gjAPI() +{ + // logout last user + this->Logout(); + + // delete network object + SAFE_DELETE(m_pNetwork) + + // delete sub-interface objects + SAFE_DELETE(m_pInterUser) + SAFE_DELETE(m_pInterTrophy) + SAFE_DELETE(m_pInterScore) + SAFE_DELETE(m_pInterDataStoreGlobal) + SAFE_DELETE(m_pInterDataStoreUser) + SAFE_DELETE(m_pInterFile) +} + + +// **************************************************************** +/* explicitly initialize the object */ +void gjAPI::Init(const int& iGameID, const std::string& sGamePrivateKey) +{ + // save game data + m_iGameID = iGameID; + m_sGamePrivateKey = sGamePrivateKey; + + // pre-process the game ID + m_sProcGameID = gjAPI::UtilIntToString(m_iGameID); + + // prefetch score tables + if(GJ_API_PREFETCH && iGameID) m_pInterScore->FetchScoreTablesCall(GJ_NETWORK_NULL_THIS(gjScoreTableMap)); +} + + +// **************************************************************** +/* main update function of the library */ +void gjAPI::Update() +{ + // update network object + m_pNetwork->Update(); + + if(!this->IsUserConnected()) return; + + if(m_iNextPing) + { + // update ping for the user session + const time_t iCurTime = time(NULL); + if(iCurTime >= m_iNextPing) + { + m_iNextPing = iCurTime + GJ_API_PING_TIME; + this->__PingSession(m_bActive); + } + } +} + + +// **************************************************************** +/* logout with specific user */ +int gjAPI::Logout() +{ + if(!this->IsUserConnected()) return GJ_NOT_CONNECTED; + + // clear user specific data + m_pInterTrophy->ClearCache(false); + m_pInterDataStoreUser->ClearCache(); + + // close the user session + if(m_iNextPing) this->__CloseSession(); + + // clear main user data + m_sUserName = ""; + m_sUserToken = ""; + m_sProcUserName = ""; + m_sProcUserToken = ""; + + // clear connection + m_bConnected = false; + + return GJ_OK; +} + + +// **************************************************************** +/* parse a valid response string in keypair format */ +int gjAPI::ParseRequestKeypair(const std::string& sInput, gjDataList* paaOutput) +{ + if(!paaOutput) return GJ_INVALID_INPUT; + + gjData aData; + std::istringstream sStream(sInput); + std::string sToken; + + // loop through input string + while(std::getline(sStream, sToken)) + { + // remove redundant characters + gjAPI::UtilTrimString(&sToken); + if(sToken.empty()) continue; + + // separate key and value + const size_t iPos = sToken.find(':'); + const std::string sKey = sToken.substr(0, iPos); + const std::string sValue = sToken.substr(iPos + 2, sToken.length() - iPos - 3); + + // next data block on same key + if(aData.count(sKey.c_str())) + { + paaOutput->push_back(aData); + aData.clear(); + } + + // create key and save value + aData[sKey.c_str()] = sValue; + } + + // insert last data block and check size + if(!aData.empty()) paaOutput->push_back(aData); + if(paaOutput->empty()) + { + paaOutput->push_back(aData); + gjAPI::ErrorLogAdd("API Error: string parsing failed"); + return GJ_INVALID_INPUT; + } + + // check for failed request + if(paaOutput->front()["success"] != "true") + { + gjAPI::ErrorLogAdd("API Error: request was unsuccessful"); + gjAPI::ErrorLogAdd("API Error: " + paaOutput->front()["message"]); + return GJ_REQUEST_FAILED; + } + + return GJ_OK; +} + + +// **************************************************************** +/* parse a valid response string in Dump format */ +int gjAPI::ParseRequestDump(const std::string& sInput, std::string* psOutput) +{ + if(!psOutput) return GJ_INVALID_INPUT; + + // read status + const std::string sStatus = sInput.substr(0, sInput.find_first_of(13)); + + // read data + (*psOutput) = sInput.substr(sStatus.length()+2); + + // check for failed request + if(sStatus != "SUCCESS") + { + gjAPI::ErrorLogAdd("API Error: request was unsuccessful"); + gjAPI::ErrorLogAdd("API Error: " + (*psOutput)); + return GJ_REQUEST_FAILED; + } + + return GJ_OK; +} + + +// **************************************************************** +/* delete all cached objects */ +void gjAPI::ClearCache() +{ + // clear cache of all sub-interfaces + m_pInterUser->ClearCache(); + m_pInterTrophy->ClearCache(true); + m_pInterScore->ClearCache(); + m_pInterDataStoreGlobal->ClearCache(); + m_pInterDataStoreUser->ClearCache(); + m_pInterFile->ClearCache(); +} + + +// **************************************************************** +/* escape a string for proper url calling */ +std::string gjAPI::UtilEscapeString(const std::string& sString) +{ + std::string sOutput = ""; + + // loop through input string + for(size_t i = 0; i < sString.length(); ++i) + { + // check the character type + if + ( + (48 <= sString[i] && sString[i] <= 57) || // 0-9 + (65 <= sString[i] && sString[i] <= 90) || // ABC...XYZ + (97 <= sString[i] && sString[i] <= 122) || // abc...xyz + ( + sString[i] == '~' || sString[i] == '.' || + sString[i] == '-' || sString[i] == '_' + ) + ) + { + // add valid character + sOutput += sString[i]; + } + else + { + // convert character to hexadecimal value + sOutput += "%" + gjAPI::UtilCharToHex(sString[i]); + } + } + + return sOutput; +} + + +// **************************************************************** +/* trim a standard string on both sides */ +void gjAPI::UtilTrimString(std::string* psInput) +{ + const size_t iFirst = psInput->find_first_not_of(" \n\r\t"); + if(iFirst != std::string::npos) psInput->erase(0, iFirst); + + const size_t iLast = psInput->find_last_not_of(" \n\r\t"); + if(iLast != std::string::npos) psInput->erase(iLast+1); +} + + +// **************************************************************** +/* convert a character into his hexadecimal value */ +std::string gjAPI::UtilCharToHex(const char& cChar) +{ + int iValue = (int)cChar; + if(iValue < 0) iValue += 256; + + char acBuffer[8]; + std::sprintf(acBuffer, "%02X", iValue); + + return acBuffer; +} + + +// **************************************************************** +/* simply convert an integer into a string */ +std::string gjAPI::UtilIntToString(const int& iInt) +{ + char acBuffer[32]; + std::sprintf(acBuffer, "%d", iInt); + + return acBuffer; +} + + +// **************************************************************** +/* create a folder hierarchy */ +void gjAPI::UtilCreateFolder(const std::string& sFolder) +{ + size_t iPos = 0; + + // loop through path + while((iPos = sFolder.find_first_of("/\\", iPos+2)) != std::string::npos) + { + const std::string sSubFolder = sFolder.substr(0, iPos); + + // create subfolder +#if defined(_GJ_WINDOWS_) + CreateDirectoryA(sSubFolder.c_str(), NULL); +#else + mkdir(sSubFolder.c_str(), S_IRWXU); +#endif + } +} + + +// **************************************************************** +/* get timestamp as string */ +std::string gjAPI::UtilTimestamp(const time_t iTime) +{ + // format the time value + tm* pFormat = std::localtime(&iTime); + + // create output + char acBuffer[16]; + std::sprintf(acBuffer, "%02d:%02d:%02d", pFormat->tm_hour, pFormat->tm_min, pFormat->tm_sec); + + return acBuffer; +} + + +// **************************************************************** +/* reset error log */ +void gjAPI::ErrorLogReset() +{ + if(GJ_API_LOGFILE) + { + // remove error log file if empty + if(s_asLog.empty()) + std::remove(GJ_API_LOGFILE_NAME); + } +} + + +// **************************************************************** +/* add error log entry */ +void gjAPI::ErrorLogAdd(const std::string& sMsg) +{ + const std::string sTimeMsg = "[" + gjAPI::UtilTimestamp() + "] " + sMsg; + + // add message + s_asLog.push_back(sTimeMsg); + + if(GJ_API_LOGFILE) + { + // add message to error log file + std::FILE* pFile = std::fopen(GJ_API_LOGFILE_NAME, "a"); + if(pFile) + { + std::fprintf(pFile, "%s\n", sTimeMsg.c_str()); + std::fclose(pFile); + } + } + +#if defined(_GJ_DEBUG_) + + // print message to terminal + std::cerr << "(!GJ) " << sTimeMsg << std::endl; + +#endif +} + + +// **************************************************************** +/* open the user session */ +int gjAPI::__OpenSession() +{ + if(!this->IsUserConnected()) return GJ_NOT_CONNECTED; + + // send non-blocking open request + if(m_pNetwork->SendRequest("/sessions/open/" + "?game_id=" + m_sProcGameID + + "&username=" + m_sProcUserName + + "&user_token=" + m_sProcUserToken, + NULL, this, &gjAPI::Null, NULL, GJ_NETWORK_NULL_THIS(std::string))) return GJ_REQUEST_FAILED; + + // init session attributes + m_iNextPing = std::time(NULL) + GJ_API_PING_TIME; + m_bActive = true; + + return GJ_OK; +} + + +// **************************************************************** +/* ping the user session */ +int gjAPI::__PingSession(const bool& bActive) +{ + if(!this->IsUserConnected()) return GJ_NOT_CONNECTED; + + // use active status + const std::string sActive = bActive ? "active" : "idle"; + + // send non-blocking ping request + if(m_pNetwork->SendRequest("/sessions/ping/" + "?game_id=" + m_sProcGameID + + "&username=" + m_sProcUserName + + "&user_token=" + m_sProcUserToken + + "&status=" + sActive, + NULL, this, &gjAPI::Null, NULL, GJ_NETWORK_NULL_THIS(std::string))) return GJ_REQUEST_FAILED; + + return GJ_OK; +} + + +// **************************************************************** +/* close the user session */ +int gjAPI::__CloseSession() +{ + if(!this->IsUserConnected()) return GJ_NOT_CONNECTED; + + // send non-blocking close request + if(m_pNetwork->SendRequest("/sessions/close/" + "?game_id=" + m_sProcGameID + + "&username=" + m_sProcUserName + + "&user_token=" + m_sProcUserToken, + NULL, this, &gjAPI::Null, NULL, GJ_NETWORK_NULL_THIS(std::string))) return GJ_REQUEST_FAILED; + + // clear session attributes + m_iNextPing = 0; + + return GJ_OK; +} + + +// **************************************************************** +/* callback for login with specific user */ +int gjAPI::__LoginCallback(const std::string& sData, void* pAdd, int* pbOutput) +{ + // check for success + gjDataList aaReturn; + if(this->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not authenticate user <" + m_sUserName + ">"); + + // clear main user data + m_sUserName = ""; + m_sUserToken = ""; + m_sProcUserName = ""; + m_sProcUserToken = ""; + + // determine error type + const int iError = std::strcmp(SAFE_MAP_GET(aaReturn[0], "success").c_str(), "false") ? GJ_NETWORK_ERROR : GJ_REQUEST_FAILED; + + if(pbOutput) (*pbOutput) = iError; + return pbOutput ? GJ_OK : iError; + } + + // set connection + m_bConnected = true; + + // open the user session + if(pAdd) this->__OpenSession(); + + // prefetch user data + if(GJ_API_PREFETCH) + { + m_pInterUser->FetchUserCall(0, GJ_NETWORK_NULL_THIS(gjUserPtr)); + m_pInterTrophy->FetchTrophiesCall(0, GJ_NETWORK_NULL_THIS(gjTrophyList)); + m_pInterDataStoreUser->FetchDataItemsCall(GJ_NETWORK_NULL_THIS(gjDataItemMap)); + } + + if(pbOutput) (*pbOutput) = GJ_OK; + return GJ_OK; +} \ No newline at end of file diff --git a/test_data/lots_of_files/gjAPI.h b/test_data/lots_of_files/gjAPI.h new file mode 100644 index 0000000..33eb0f2 --- /dev/null +++ b/test_data/lots_of_files/gjAPI.h @@ -0,0 +1,1112 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +//*--------------------------------------------------------------------------------------*// +//| ______ ______ __ __ ______ __ ______ __ ______ |// +//| /\ ___\ /\ __ \ /\ "-./ \ /\ ___\ /\ \ /\ __ \ /\ \ /\__ _\ |// +//| \ \ \__ \ \ \ __ \ \ \ \-./\ \ \ \ __\ _\_\ \ \ \ \/\ \ \ \ \____ \/_/\ \/ |// +//| \ \_____\ \ \_\ \_\ \ \_\ \ \_\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\ |// +//| \/_____/ \/_/\/_/ \/_/ \/_/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/ |// +//| |// +//*--------------------------------------------------------------------------------------*// +//////////////////////////////////////////////////////////////////////////////////////////// +//*--------------------------------------------------------------------------------------*// +//| Game Jolt API C++ Library v1.0 (http://gamejolt.com) |// +//*--------------------------------------------------------------------------------------*// +//| Special Thanks to: |// +//| |// +//| David "CROS" DeCarmine, Joona "erakko" Melartin, Ashley Gwinnell, Bruno Assarisse, |// +//| Jani "JNyknn" Nykänen, Jorge Martínez "Sasurai" Vargas |// +//*--------------------------------------------------------------------------------------*// +//| Copyright (c) 2013-2015 Martin Mauersics |// +//| |// +//| This software is provided 'as-is', without any express or implied |// +//| warranty. In no event will the authors be held liable for any damages |// +//| arising from the use of this software. |// +//| |// +//| Permission is granted to anyone to use this software for any purpose, |// +//| including commercial applications, and to alter it and redistribute it |// +//| freely, subject to the following restrictions: |// +//| |// +//| 1. The origin of this software must not be misrepresented; you must not |// +//| claim that you wrote the original software. If you use this software |// +//| in a product, an acknowledgment in the product documentation would be |// +//| appreciated but is not required. |// +//| |// +//| 2. Altered source versions must be plainly marked as such, and must not be |// +//| misrepresented as being the original software. |// +//| |// +//| 3. This notice may not be removed or altered from any source |// +//| distribution. |// +//| |// +//| 4. This software may only be used within the terms of Game Jolt. |// +//| (http://gamejolt.com/terms/) |// +//*--------------------------------------------------------------------------------------*// +//////////////////////////////////////////////////////////////////////////////////////////// +//! \file +#pragma once +#ifndef _GJ_GUARD_API_H_ +#define _GJ_GUARD_API_H_ + + +/* --- configuration --- */ +#define GJ_API_URL "http://gamejolt.com/api/game/v1" +#define GJ_API_AVATAR_DEFAULT "http://gamejolt.com/img/no-avatar-1.png" +#define GJ_API_AVATAR_FORMAT ".png" +#define GJ_API_TROPHY_DEFAULT_1 "http://gamejolt.com/img/trophy-bronze-1.jpg" +#define GJ_API_TROPHY_DEFAULT_2 "http://gamejolt.com/img/trophy-silver-1.jpg" +#define GJ_API_TROPHY_DEFAULT_3 "http://gamejolt.com/img/trophy-gold-1.jpg" +#define GJ_API_TROPHY_DEFAULT_4 "http://gamejolt.com/img/trophy-platinum-1.jpg" +#define GJ_API_TROPHY_SECRET "http://gamejolt.com/img/trophy-secret-1.jpg" +#define GJ_API_PING_TIME 30 +#define GJ_API_CRED "gjapi-credentials.txt" +#define GJ_API_TEXT_NOW "now" +#define GJ_API_TEXT_SECRET "???" +#define GJ_API_RESERVE_CALL 16 +#define GJ_API_RESERVE_CALL_OUTPUT 4 +#define GJ_API_RESERVE_TROPHY 32 +#define GJ_API_RESERVE_SCORE 64 +#define GJ_API_RESERVE_FILE 32 +#define GJ_API_TIMEOUT_CONNECTION 3 +#define GJ_API_TIMEOUT_REQUEST 10 +#define GJ_API_NET_COMPRESSION "" // empty for all available compressions (identity, deflate, gzip) +#define GJ_API_NET_KEEPALIVE true +#define GJ_API_LOGFILE true +#define GJ_API_LOGFILE_NAME "gjapi_log.txt" +#define GJ_API_PREFETCH true +#define GJ_API_OFFCACHE_TROPHY true // does not work on Android +#define GJ_API_OFFCACHE_NAME "data/gjapi_cache.dat" +/* --- configuration --- */ + + +// compiler +#if defined(_MSC_VER) + #define _GJ_MSVC_ (_MSC_VER) +#endif +#if defined(__GNUC__) + #define _GJ_GCC_ (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__*1) +#endif +#if defined(__MINGW32__) + #define _CORE_MINGW_ (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__*1) + #undef _CORE_GCC_ +#endif +#if defined(__clang__) + #define _GJ_CLANG_ (__clang_major__*10000 + __clang_minor__*100 + __clang_patchlevel__*1) +#endif + +// operating system +#if defined(_WIN32) + #define _GJ_WINDOWS_ (1) +#endif +#if defined(__linux__) + #define _GJ_LINUX_ (1) +#endif +#if defined(__APPLE__) + #define _GJ_OSX_ (1) +#endif +#if defined(__ANDROID__) + #define _GJ_ANDROID_ (1) +#endif + +// debug mode +#if defined(_DEBUG) || defined(DEBUG) || (defined(_GJ_GCC_) && !defined(__OPTIMIZE__)) + #define _GJ_DEBUG_ (1) +#endif + +// missing functionality +#if defined(_GJ_MSVC_) + #if (_GJ_MSVC_) < 1800 + #define delete_func + #else + #define delete_func = delete + #endif + #if (_GJ_MSVC_) < 1700 + #define final + #endif + #define noexcept throw() + #define constexpr_func inline + #define constexpr_var const +#else + #define delete_func = delete + #define constexpr_func constexpr + #define constexpr_var constexpr +#endif +#if defined(_GJ_GCC_) + #if (_GJ_GCC_) < 40700 + #define override + #define final + #endif +#endif + +// base libraries +#define _HAS_EXCEPTIONS (0) +#define _CRT_SECURE_NO_WARNINGS +#define _ALLOW_KEYWORD_MACROS + +#if !defined(_GJ_WINDOWS_) + #include <sys/stat.h> +#endif +#include <cstdio> +#include <cstdlib> +#include <cstdint> +#include <cstring> +#include <ctime> +#include <string> +#include <map> +#include <vector> + +/*! \param pOutputObj output receiving object of **class T** + * \param OutputCallback callback function from **class T** with a specific return **type x** + * \param pOutputData additional data which will be forwarded to the callback function + * + * **Code Example** + * \code{.cpp} + * void Function(gjAPI& API, myClass& myObj) + * { + * // fetch an user with a callback (does not block) + * API.InterUser()->FetchUserCall("CROS", &myObj, &myClass::ReceiveUser, NULL); + * } + * \endcode */ +#define GJ_NETWORK_OUTPUT(x) T* pOutputObj, void (T::*OutputCallback)(const x&, void*), void* pOutputData +#define GJ_NETWORK_PROCESS P* pProcessObj, int (P::*ProcessCallback)(const std::string&, void*, D*), void* pProcessData + +#define GJ_NETWORK_OUTPUT_FW pOutputObj, OutputCallback, pOutputData +#define GJ_NETWORK_PROCESS_FW pProcessObj, ProcessCallback, pProcessData + +#define GJ_NETWORK_NULL_THIS(d) this, &gjAPI::Null<d>, NULL +#define GJ_NETWORK_NULL_API(d) m_pAPI, &gjAPI::Null<d>, NULL + +#define SAFE_DELETE(p) {if(p) {delete (p); (p) = NULL;}} +#define SAFE_DELETE_ARRAY(p) {if(p) {delete[] (p); (p) = NULL;}} +#define SAFE_MAP_GET(o,s) ((o).count(s) ? (o).at(s) : std::string("")) + +#define FOR_EACH(i,c) for(auto i = (c).begin(), i ## __e = (c).end(); i != i ## __e; ++i) +#define FOR_EACH_REV(i,c) for(auto i = (c).rbegin(), i ## __e = (c).rend(); i != i ## __e; ++i) + +#if !defined(ARRAY_SIZE) + template <typename T, std::size_t iSize> char (&__ARRAY_SIZE(T (&)[iSize]))[iSize]; + #define ARRAY_SIZE(a) (sizeof(__ARRAY_SIZE(a))) +#endif + +#if !defined(DISABLE_COPY) + #define DISABLE_COPY(c) \ + c (const c&)delete_func; \ + c& operator = (const c&)delete_func; +#endif + +#if !defined(P_TO_I) + #define P_TO_I(x) ((int)(std::intptr_t)(void*)(x)) //!< pointer to int + #define I_TO_P(x) ((void*)(std::intptr_t)(int)(x)) //!< int to pointer +#endif + +#undef GetUserName + +#include "MD5.h" +#include "Base64.h" +#include "gjLookup.h" +#include "curl/curl.h" + +class gjAPI; +class gjUser; +class gjTrophy; +class gjScoreTable; +class gjScore; +class gjDataItem; + +typedef gjLookup<std::string> gjData; +typedef std::vector<gjData> gjDataList; +typedef void* gjVoidPtr; +typedef gjUser* gjUserPtr; +typedef std::vector<gjTrophy*> gjTrophyList; +typedef gjTrophy* gjTrophyPtr; +typedef std::map<int, gjScoreTable*> gjScoreTableMap; +typedef std::vector<gjScore*> gjScoreList; +typedef gjScore* gjScorePtr; +typedef std::map<std::string, gjDataItem*> gjDataItemMap; +typedef gjDataItem* gjDataItemPtr; + +enum GJ_ERROR : unsigned char +{ + GJ_OK = 0x00, //!< everything is fine + GJ_INVALID_CALL = 0x01, //!< function cannot be called + GJ_INVALID_INPUT = 0x02, //!< function parameters are invalid + GJ_REQUEST_FAILED = 0x04, //!< request to the API failed + GJ_REQUEST_CANCELED = 0x08, //!< request was canceled because of redundancy + GJ_NOT_CONNECTED = 0x10, //!< no main user connected (login first) + GJ_NO_DATA_FOUND = 0x20, //!< no data was found + GJ_NETWORK_ERROR = 0x40, //!< error sending or receiving data, or failed to establish a connection + GJ_FILE_ERROR = 0x80 //!< error on opening, writing or finding a file +}; + +enum GJ_SORT_DIRECTION : int +{ + GJ_SORT_UNDEF = 0, //!< undefined sorting + GJ_SORT_DESC = 1, //!< descending sorting (3, 2, 1) + GJ_SORT_ASC = -1 //!< ascending sorting (1, 2, 3) +}; + +enum GJ_TROPHY_TYPE : int +{ + GJ_TROPHY_ALL = 0, //!< all trophies + GJ_TROPHY_ACHIEVED = 1, //!< only achieved trophies + GJ_TROPHY_NOT_ACHIEVED = -1 //!< only unachieved trophies +}; + +#include "gjNetwork.h" // other header files are post-included + + +// **************************************************************** +/*! Main interface class of the library to connect with the Game Jolt API.\n + * Manages sessions, users, trophies, scores, data items and downloaded files.\n + * http://gamejolt.com/api/doc/game/ + * \brief Main Interface */ +class gjAPI final +{ +private: + // **************************************************************** + /*! Sub-Interface class for user operations.\n + * http://gamejolt.com/api/doc/game/users/ + * \brief User Sub-Interface */ + class gjInterUser final + { + private: + std::map<int, gjUser*> m_apUser; //!< cached user objects + + gjAPI* m_pAPI; //!< main interface access pointer + gjNetwork* m_pNetwork; //!< network access pointer + + + public: + gjInterUser(gjAPI* pAPI, gjNetwork* pNetwork)noexcept; + ~gjInterUser(); + + /*! \name Direct Access */ + //! @{ + /*! Get direct access to the user objects.\n + * This function may block to cache the specific user. + * \pre Login maybe required + * \param iID Unique ID of an user (0 = current main user, Login required) + * \param sName Unique name of an user + * \return Pointer to specific user or empty object (ID == 0) on error */ + gjUser* GetUser(const int& iID); + gjUser* GetUser(const std::string& sName); + gjUser* GetMainUser(); + //! @} + + /*! \name Fetch User Request */ + //! @{ + /*! Fetch and cache a specific user through an API request. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param iID Unique ID of an user (0 = current main user, Login required) + * \param sName Unique name of an user + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_INPUT** if name string is empty\n + * (see #GJ_ERROR) */ + inline int FetchUserNow(const int& iID, gjUserPtr* ppOutput) {if(!ppOutput) return GJ_INVALID_INPUT; return this->__FetchUser(iID, ppOutput, GJ_NETWORK_NULL_API(gjUserPtr));} + inline int FetchUserNow(const std::string& sName, gjUserPtr* ppOutput) {if(!ppOutput) return GJ_INVALID_INPUT; return this->__FetchUser(sName, ppOutput, GJ_NETWORK_NULL_API(gjUserPtr));} + template <typename T> inline int FetchUserCall(const int& iID, GJ_NETWORK_OUTPUT(gjUserPtr)) {return this->__FetchUser(iID, NULL, GJ_NETWORK_OUTPUT_FW);} + template <typename T> inline int FetchUserCall(const std::string& sName, GJ_NETWORK_OUTPUT(gjUserPtr)) {return this->__FetchUser(sName, NULL, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Check Cache */ + //! @{ + /*! Check and retrieve already cached user objects. + * \pre Login maybe required + * \param iID Unique ID of an user (0 = current main user, Login required) + * \param sName Unique name of an user + * \return **GJ_OK** on success\n + * **GJ_NO_DATA_FOUND** if user is not cached yet or does not even exist\n + * (see #GJ_ERROR) */ + inline int CheckCache(const int& iID, gjUserPtr* ppOutput) {return this->__CheckCache(iID, ppOutput);} + inline int CheckCache(const std::string& sName, gjUserPtr* ppOutput) {return this->__CheckCache(sName, ppOutput);} + //! @} + + /*! \name Clear Cache */ + //! @{ + /*! Delete all cached user objects. + * \warning All external pointers will be invalid */ + void ClearCache(); + //! @} + + + private: + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __FetchUser(const int& iID, gjUserPtr* ppOutput, GJ_NETWORK_OUTPUT(gjUserPtr)); + template <typename T> int __FetchUser(const std::string& sName, gjUserPtr* ppOutput, GJ_NETWORK_OUTPUT(gjUserPtr)); + //! @} + + /*! \name Management Functions */ + //! @{ + int __CheckCache(const int& iID, gjUserPtr* ppOutput); + int __CheckCache(const std::string& sName, gjUserPtr* ppOutput); + int __Process(const std::string& sData, void* pAdd, gjUserPtr* ppOutput); + //! @} + }; + + + // **************************************************************** + /*! Sub-Interface class for trophy operations.\n + * http://gamejolt.com/api/doc/game/trophies/ + * \brief Trophy Sub-Interface */ + class gjInterTrophy final + { + private: + std::map<int, gjTrophy*> m_apTrophy; //!< cached trophy objects + int m_iCache; //!< cache status (0 = empty, 1 = offline, 2 = online) + + std::vector<int> m_aiSort; //!< layout of the returned trophy list + std::vector<int> m_aiSecret; //!< secret trophies (only fully visible when achieved) + std::vector<int> m_aiHidden; //!< hidden trophies (should never be visible, removed from memory) + + gjAPI* m_pAPI; //!< main interface access pointer + gjNetwork* m_pNetwork; //!< network access pointer + + + public: + gjInterTrophy(gjAPI* pAPI, gjNetwork* pNetwork)noexcept; + ~gjInterTrophy(); + + /*! \name Direct Access */ + //! @{ + /*! Get direct access to the trophy objects.\n + * This function may block to cache all trophies. + * \pre Login required + * \param iID Unique ID of a trophy + * \return Pointer to specific trophy or empty object (ID == 0) on error */ + gjTrophy* GetTrophy(const int& iID); + //! @} + + /*! \name Fetch Trophies Request */ + //! @{ + /*! Fetch and cache all trophies through an API request.\n + * You can sort the returned list with #SetSort. + * \pre Login required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param iAchieved Status of the requested trophies (see #GJ_TROPHY_TYPE) + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * **GJ_NO_DATA_FOUND** if no trophies were found\n + * (see #GJ_ERROR) */ + inline int FetchTrophiesNow(const long& iAchieved, gjTrophyList* papOutput) {if(!papOutput) return GJ_INVALID_INPUT; return this->__FetchTrophies(iAchieved, papOutput, GJ_NETWORK_NULL_API(gjTrophyList));} + template <typename T> inline int FetchTrophiesCall(const long& iAchieved, GJ_NETWORK_OUTPUT(gjTrophyList)) {return this->__FetchTrophies(iAchieved, NULL, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Clear Cache */ + //! @{ + /*! Delete all cached trophy objects. + * \warning All external pointers will be invalid + * \param bFull Delete also the offline-cache */ + void ClearCache(const bool& bFull); + //! @} + + /*! \name Control Trophies */ + //! @{ + /*! Define the way trophies are handled and returned from the interface. + * \param piIDList Array with trophy IDs + * \param iNum Number of elements in the array */ + void SetSort(const int* piIDList, const size_t& iNum); + void SetSecret(const int* piIDList, const size_t& iNum); + void SetHidden(const int* piIDList, const size_t& iNum); + //! @} + + + private: + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __FetchTrophies(const long& iAchieved, gjTrophyList* papOutput, GJ_NETWORK_OUTPUT(gjTrophyList)); + //! @} + + /*! \name Management Functions */ + //! @{ + int __CheckCache(const int& iAchieved, gjTrophyList* papOutput); + int __Process(const std::string& sData, void* pAdd, gjTrophyList* papOutput); + //! @} + + /*! \name Offline Cache Functions */ + //! @{ + void __SaveOffCache(const std::string& sData); + void __LoadOffCache(); + //! @} + }; + + + // **************************************************************** + /*! Sub-Interface class for score operations.\n + * http://gamejolt.com/api/doc/game/scores/ + * \brief Score Sub-Interface */ + class gjInterScore final + { + private: + std::map<int, gjScoreTable*> m_apScoreTable; //!< cached score table objects with semi-cached score entries + + gjAPI* m_pAPI; //!< main interface access pointer + gjNetwork* m_pNetwork; //!< network access pointer + + + public: + gjInterScore(gjAPI* pAPI, gjNetwork* pNetwork)noexcept; + ~gjInterScore(); + + /*! \name Direct Access */ + //! @{ + /*! Get direct access to score table objects.\n + * This function may block to cache all score tables. + * \param iID Unique ID of a score table (0 = primary score table) + * \return Pointer to specific score table or empty object (ID == 0) on error */ + gjScoreTable* GetScoreTable(const int& iID); + inline gjScoreTable* GetPrimaryTable() {return this->GetScoreTable(0);} + //! @} + + /*! \name Fetch Score Tables Request */ + //! @{ + /*! Fetch and cache all score tables through an API request. + * \bug The API returns already deleted score tables + * + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NO_DATA_FOUND** if no score tables were found\n + * (see #GJ_ERROR) */ + inline int FetchScoreTablesNow(gjScoreTableMap* papOutput) {if(!papOutput) return GJ_INVALID_INPUT; return this->__FetchScoreTables(papOutput, GJ_NETWORK_NULL_API(gjScoreTableMap));} + template <typename T> inline int FetchScoreTablesCall(GJ_NETWORK_OUTPUT(gjScoreTableMap)) {return this->__FetchScoreTables(NULL, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Clear Cache */ + //! @{ + /*! Delete all cached score table objects and score entries. + * \warning All external pointers will be invalid */ + void ClearCache(); + //! @} + + + private: + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __FetchScoreTables(gjScoreTableMap* papOutput, GJ_NETWORK_OUTPUT(gjScoreTableMap)); + //! @} + + /*! \name Management Functions */ + //! @{ + int __CheckCache(gjScoreTableMap* papOutput); + int __Process(const std::string& sData, void* pAdd, gjScoreTableMap* papOutput); + //! @} + }; + + + // **************************************************************** + /*! Sub-Interface class for data store operations.\n + * http://gamejolt.com/api/doc/game/data-store/ + * \brief Data Store Sub-Interface */ + class gjInterDataStore final + { + private: + std::map<std::string, gjDataItem*> m_apDataItem; //!< semi-cached data store items + + int m_iType; //!< type of this interface (0 = global, 1 = user) + + gjAPI* m_pAPI; //!< main interface access pointer + gjNetwork* m_pNetwork; //!< network access pointer + + + public: + gjInterDataStore(const int& iType, gjAPI* pAPI, gjNetwork* pNetwork)noexcept; + ~gjInterDataStore(); + + /*! \name Direct Access */ + //! @{ + /*! Get direct access to data store items.\n + * This function creates a new data store item, if the key does not exist.\n + * To get all existing items, use \link FetchDataItemsNow FetchDataItems\endlink. + * \pre Login maybe required + * \param sKey Unique key of a data store item + * \return Pointer to specific data store item or NULL on error */ + gjDataItem* GetDataItem(const std::string& sKey); + //! @} + + /*! \name Fetch Data Items Request */ + //! @{ + /*! Fetch and semi-cache all data store items through an API request. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * **GJ_NO_DATA_FOUND** if no data items were found\n + * (see #GJ_ERROR) */ + inline int FetchDataItemsNow(gjDataItemMap* papOutput) {if(!papOutput) return GJ_INVALID_INPUT; return this->__FetchDataItems(papOutput, GJ_NETWORK_NULL_API(gjDataItemMap));} + template <typename T> inline int FetchDataItemsCall(GJ_NETWORK_OUTPUT(gjDataItemMap)) {return this->__FetchDataItems(NULL, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Clear Cache */ + //! @{ + /*! Delete all cached data store items. + * \warning All external pointers will be invalid */ + void ClearCache(); + //! @} + + /*! \name Get Attributes */ + //! @{ + inline const int& GetType()const {return m_iType;} //!< \copybrief m_iType + /*! */ //! @} + + + private: + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __FetchDataItems(gjDataItemMap* papOutput, GJ_NETWORK_OUTPUT(gjDataItemMap)); + //! @} + + /*! \name Management Functions */ + //! @{ + int __CheckCache(gjDataItemMap* papOutput); + int __Process(const std::string& sData, void* pAdd, gjDataItemMap* papOutput); + //! @} + + /*! \name Callback Functions */ + //! @{ + int __AddDataItemCallback(const std::string& sData, void* pAdd, gjTrophyPtr* pOutput); + int __RemoveDataItemCallback(const std::string& sData, void* pAdd, gjTrophyPtr* ppOutput); + //! @} + }; + + + // **************************************************************** + /*! Sub-Interface class for file downloads.\n + * \brief File Download Sub-Interface */ + class gjInterFile final + { + private: + std::vector<std::string> m_asFile; //!< cached file paths + + gjAPI* m_pAPI; //!< main interface access pointer + gjNetwork* m_pNetwork; //!< network access pointer + + + public: + gjInterFile(gjAPI* pAPI, gjNetwork* pNetwork)noexcept; + ~gjInterFile(); + + /*! \name Download File */ + /*! Download a file from any URL.\n + * Retrieve the local path of the file when finished. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \warning You need to overwrite the file name if it's not apparent from the URL + * \param sURL Full path of the remote file + * \param sToFolder Relative path of the download target folder + * \param sFileNameOverwrite Custom target file name (mandatory, if file name is not apparent from the URL) + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_INPUT** if URL string or target folder string is empty\n + * (see #GJ_ERROR) */ + inline int DownloadFileNow(const std::string& sURL, const std::string& sToFolder, const std::string& sFileNameOverwrite, std::string* psOutput) {if(!psOutput) return GJ_INVALID_INPUT; return this->__DownloadFile(sURL, sToFolder, sFileNameOverwrite, psOutput, GJ_NETWORK_NULL_API(std::string));} + template <typename T> inline int DownloadFileCall(const std::string& sURL, const std::string& sToFolder, const std::string& sFileNameOverwrite, GJ_NETWORK_OUTPUT(std::string)) {return this->__DownloadFile(sURL, sToFolder, sFileNameOverwrite, NULL, GJ_NETWORK_OUTPUT_FW);} + + /*! \name Clear Cache */ + //! @{ + /*! Delete all cached file paths. */ + void ClearCache(); + //! @} + + + private: + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __DownloadFile(const std::string& sURL, const std::string& sToFolder, const std::string& sFileNameOverwrite, std::string* psOutput, GJ_NETWORK_OUTPUT(std::string)); + //! @} + + /*! \name Management Functions */ + //! @{ + int __CheckCache(const std::string& sPath); + int __Process(const std::string& sData, void* pAdd, std::string* psOutput); + //! @} + }; + + +private: + int m_iGameID; //!< ID to identify the game + std::string m_sGamePrivateKey; //!< private key to generate MD5 signature + + std::string m_sUserName; //!< name of the main user + std::string m_sUserToken; //!< token of the main user + + time_t m_iNextPing; //!< next ping for the user session + bool m_bActive; //!< current status for the user session + bool m_bConnected; //!< current connection status + + gjInterUser* m_pInterUser; //!< user Sub-Interface object + gjInterTrophy* m_pInterTrophy; //!< trophy Sub-Interface object + gjInterScore* m_pInterScore; //!< score Sub-Interface object + gjInterDataStore* m_pInterDataStoreGlobal; //!< global data store Sub-Interface object + gjInterDataStore* m_pInterDataStoreUser; //!< user data store Sub-Interface object + gjInterFile* m_pInterFile; //!< file download Sub-Interface object + + gjNetwork* m_pNetwork; //!< network object + + std::string m_sProcGameID; //!< already processed/converted game ID + std::string m_sProcUserName; //!< already processed/escaped user name + std::string m_sProcUserToken; //!< already processed/escaped user token + + static std::vector<std::string> s_asLog; //!< error log + + +public: + gjAPI(const int iGameID = 0, const std::string sGamePrivateKey = "")noexcept; + ~gjAPI(); + + /*! \name Init */ + //! @{ + /*! Explicitely initialize the object after construction. + * \note Needs to be called after empty construction, and only once */ + void Init(const int& iGameID, const std::string& sGamePrivateKey); + //! @} + + /*! \name Update */ + //! @{ + /*! Main update function of the library. + * \brief Must be executed in the main loop of the application + * \note Must be executed in the main loop of the application */ + void Update(); + //! @} + + /*! \name Login User*/ + //! @{ + /*! Login with a specific user.\n + * Authenticate user and establish an user session through the API.\n + * Prefetch the user object, trophies and user related data store items. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param bSession Establish an user session + * \param sUserName Login name of the user + * \param sUserToken Token for that user + * \param sCredPath Relative path to the credentials file of the quick play function + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_CALL** if already connected\n + * **GJ_INVALID_INPUT** if user name or user token is missing\n + * **GJ_FILE_ERROR** if credentials file was not found\n + * **GJ_NETWORK_ERROR** if service is unavailable or request timed out\n + * (see #GJ_ERROR) */ + inline int LoginNow(const bool& bSession, const std::string& sUserName, const std::string& sUserToken) {return __Login(bSession, sUserName, sUserToken, true, GJ_NETWORK_NULL_THIS(int));} + inline int LoginNow(const bool& bSession, const std::string& sCredPath) {return __Login(bSession, sCredPath, true, GJ_NETWORK_NULL_THIS(int));} + inline int LoginNow(const bool& bSession) {return __Login(bSession, GJ_API_CRED, true, GJ_NETWORK_NULL_THIS(int));} + template <typename T> inline int LoginCall(const bool& bSession, const std::string& sUserName, const std::string& sUserToken, GJ_NETWORK_OUTPUT(int)) {return __Login(bSession, sUserName, sUserToken, false, GJ_NETWORK_OUTPUT_FW);} + template <typename T> inline int LoginCall(const bool& bSession, const std::string& sCredPath, GJ_NETWORK_OUTPUT(int)) {return __Login(bSession, sCredPath, false, GJ_NETWORK_OUTPUT_FW);} + template <typename T> inline int LoginCall(const bool& bSession, GJ_NETWORK_OUTPUT(int)) {return __Login(bSession, GJ_API_CRED, false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Logout User */ + //! @{ + /*! Logout with the current main user. + * \warning External pointers to trophies and user data store items will be invalid + * \return **GJ_OK** on success\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + int Logout(); + //! @} + + /*! \name Sub-Interface Access */ + //! @{ + inline gjInterUser* InterUser()const {return m_pInterUser;} + inline gjInterTrophy* InterTrophy()const {return m_pInterTrophy;} + inline gjInterScore* InterScore()const {return m_pInterScore;} + inline gjInterDataStore* InterDataStoreGlobal()const {return m_pInterDataStoreGlobal;} + inline gjInterDataStore* InterDataStoreUser()const {return m_pInterDataStoreUser;} + inline gjInterFile* InterFile()const {return m_pInterFile;} + inline gjNetwork* AccessNetwork()const {return m_pNetwork;} + //! @} + + /*! \name Send Custom Request */ + //! @{ + /*! Send a custom request to the API.\n + * Retrieve a response string when finished. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks\n + * Use "&POST<data>" at the end of the URL for a POST request + * \param sURL Relative API request string + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NETWORK_ERROR** if session cannot be established\n + * (see #GJ_ERROR) */ + inline int SendRequestNow(const std::string& sURL, std::string* psOutput) {return m_pNetwork->SendRequest(sURL, psOutput, this, &gjAPI::Null, NULL, GJ_NETWORK_NULL_THIS(std::string));} + template <typename T, typename D> inline int SendRequestCall(const std::string& sURL, GJ_NETWORK_OUTPUT(D)) {return m_pNetwork->SendRequest(sURL, NULL, this, &gjAPI::Null, NULL, GJ_NETWORK_OUTPUT_FW);} + template <typename T, typename P, typename D> inline int SendRequestCall(const std::string& sURL, GJ_NETWORK_PROCESS, GJ_NETWORK_OUTPUT(D)) {return m_pNetwork->SendRequest(sURL, NULL, GJ_NETWORK_PROCESS_FW, GJ_NETWORK_OUTPUT_FW);} + template <typename T, typename P, typename D> inline int SendRequest(const std::string& sURL, std::string* psOutput, GJ_NETWORK_PROCESS, GJ_NETWORK_OUTPUT(D)) {return m_pNetwork->SendRequest(sURL, psOutput, GJ_NETWORK_PROCESS_FW, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Parse Request */ + //! @{ + /*! Parse a valid response string from the API.\n + * Retrieve a list of data objects or a single string. + * \param sInput Valid response string + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if the parsed request was unsuccessful\n + * **GJ_INVALID_INPUT** if the string parsing failed\n + * (see #GJ_ERROR) */ + int ParseRequestKeypair(const std::string& sInput, gjDataList* paaOutput); + int ParseRequestDump(const std::string& sInput, std::string* psOutput); + //! @} + + /*! \name Clear Cache */ + //! @{ + /*! Delete all cached objects. + * \warning All external pointers will be invalid\n + * Try to avoid using this function */ + void ClearCache(); + //! @} + + /*! \name Utility Functions */ + //! @{ + static std::string UtilEscapeString(const std::string& sString); + static void UtilTrimString(std::string* psInput); + static std::string UtilCharToHex(const char& cChar); + static std::string UtilIntToString(const int& iInt); + static void UtilCreateFolder(const std::string& sFolder); + static std::string UtilTimestamp(const time_t iTime = std::time(NULL)); + //! @} + + /*! \name Error Log */ + //! @{ + static void ErrorLogReset(); + static void ErrorLogAdd(const std::string& sMsg); + static inline const std::vector<std::string>& ErrorLogGet(){return s_asLog;} + //! @} + + /*! \name Set Attributes */ + //! @{ + inline void SetSessionActive(const bool& bActive) {m_bActive = bActive;} //!< \copybrief m_bActive + /*! */ //! @} + + /*! \name Get Attributes */ + //! @{ + inline const int& GetGameID()const {return m_iGameID;} //!< \copybrief m_iGameID + inline const std::string& GetGamePrivateKey()const {return m_sGamePrivateKey;} //!< \copybrief m_sGamePrivateKey + inline const std::string& GetUserName()const {return m_sUserName;} //!< \copybrief m_sUserName + inline const std::string& GetUserToken()const {return m_sUserToken;} //!< \copybrief m_sUserToken + /*! */ //! @} + + /*! \name Get Processed Attributes */ + //! @{ + inline const std::string& GetProcGameID()const {return m_sProcGameID;} //!< \copybrief m_sProcGameID + inline const std::string& GetProcUserName()const {return m_sProcUserName;} //!< \copybrief m_sProcUserName + inline const std::string& GetProcUserToken()const {return m_sProcUserToken;} //!< \copybrief m_sProcUserToken + /*! */ //! @} + + /*! \name Check Status */ + //! @{ + inline const bool& IsSessionActive()const {return m_bActive;} //!< \copybrief m_bActive + inline const bool& IsUserConnected()const {return m_bConnected;} //!< \copybrief m_bConnected + //! @} + +#if !defined(DOXYGEN_SHOULD_SKIP_THIS) + + /*! \name Callback Placeholder */ + //! @{ + inline int Null(const std::string& sData, void* pAdd, std::string* psOutput) {if(psOutput) (*psOutput) = sData; return GJ_OK;} + template <typename D> inline void Null(const D& pObject, void* pData) {} + //! @} + +#endif + + +private: + DISABLE_COPY(gjAPI) + + /*! \name Session Functions */ + //! @{ + int __OpenSession(); + int __PingSession(const bool& bActive); + int __CloseSession(); + //! @} + + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __Login(const bool& bSession, const std::string& sUserName, const std::string& sUserToken, const bool& bNow, GJ_NETWORK_OUTPUT(int)); + template <typename T> int __Login(const bool& bSession, const std::string& sCredPath, const bool& bNow, GJ_NETWORK_OUTPUT(int)); + //! @} + + /*! \name Callback Functions */ + //! @{ + int __LoginCallback(const std::string& sData, void* pAdd, int* pbOutput); + //! @} +}; + + +// **************************************************************** +/* fetch and cache a specific user with user ID */ +template <typename T> int gjAPI::gjInterUser::__FetchUser(const int& iID, gjUserPtr* ppOutput, GJ_NETWORK_OUTPUT(gjUserPtr)) +{ + const bool bNow = ppOutput ? true : false; + + // fetch current main user + if(!iID) return this->__FetchUser(m_pAPI->GetUserName(), ppOutput, GJ_NETWORK_OUTPUT_FW); + + // check for cached user + gjUserPtr pCache = NULL; + if(this->__CheckCache(iID, &pCache) == GJ_OK) + { + if(bNow) (*ppOutput) = pCache; + else (pOutputObj->*OutputCallback)(pCache, pOutputData); + return GJ_OK; + } + + // send get user request + std::string sResponse; + if(m_pNetwork->SendRequest("/users/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&user_id=" + gjAPI::UtilIntToString(iID), + bNow ? &sResponse : NULL, this, &gjAPI::gjInterUser::__Process, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__Process(sResponse, NULL, ppOutput); + return GJ_OK; +} + + +// **************************************************************** +/* fetch and cache a specific user with user name */ +template <typename T> int gjAPI::gjInterUser::__FetchUser(const std::string& sName, gjUserPtr* ppOutput, GJ_NETWORK_OUTPUT(gjUserPtr)) +{ + if(sName == "") return GJ_INVALID_INPUT; + + const bool bNow = ppOutput ? true : false; + + // check for cached user + gjUserPtr pCache = NULL; + if(this->__CheckCache(sName, &pCache) == GJ_OK) + { + if(bNow) (*ppOutput) = pCache; + else (pOutputObj->*OutputCallback)(pCache, pOutputData); + return GJ_OK; + } + + // send get user request + std::string sResponse; + if(m_pNetwork->SendRequest("/users/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&username=" + gjAPI::UtilEscapeString(sName), + bNow ? &sResponse : NULL, this, &gjAPI::gjInterUser::__Process, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__Process(sResponse, NULL, ppOutput); + return GJ_OK; +} + + +// **************************************************************** +/* fetch and cache all trophies */ +template <typename T> int gjAPI::gjInterTrophy::__FetchTrophies(const long& iAchieved, gjTrophyList* papOutput, GJ_NETWORK_OUTPUT(gjTrophyList)) +{ + if(!m_pAPI->IsUserConnected() && m_iCache == 0) return GJ_NOT_CONNECTED; + + const bool bNow = papOutput ? true : false; + + // handle offline-cached trophies + if(m_pAPI->IsUserConnected() && m_iCache != 2) + m_iCache = 0; + + // wait for prefetching + if(bNow && GJ_API_PREFETCH) + { + if(m_apTrophy.size() <= 1) + m_pNetwork->Wait(1); + } + + if(m_iCache != 0) + { + // check for cached trophies + gjTrophyList apCache; + if(this->__CheckCache(iAchieved, &apCache) == GJ_OK) + { + if(bNow) (*papOutput) = apCache; + else (pOutputObj->*OutputCallback)(apCache, pOutputData); + return GJ_OK; + } + } + + // send get trophies request + std::string sResponse; + if(m_pNetwork->SendRequest("/trophies/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken(), + bNow ? &sResponse : NULL, this, &gjAPI::gjInterTrophy::__Process, I_TO_P(iAchieved), GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__Process(sResponse, I_TO_P(iAchieved), papOutput); + return GJ_OK; +} + + +// **************************************************************** +/* fetch and cache all score tables */ +template <typename T> int gjAPI::gjInterScore::__FetchScoreTables(gjScoreTableMap* papOutput, GJ_NETWORK_OUTPUT(gjScoreTableMap)) +{ + const bool bNow = papOutput ? true : false; + + // wait for prefetching + if(bNow && GJ_API_PREFETCH) + { + if(m_apScoreTable.size() <= 1) + m_pNetwork->Wait(1); + } + + // check for cached score tables + gjScoreTableMap apCache; + if(this->__CheckCache(&apCache) == GJ_OK) + { + if(bNow) (*papOutput) = apCache; + else (pOutputObj->*OutputCallback)(apCache, pOutputData); + return GJ_OK; + } + + // send get score tables request + std::string sResponse; + if(m_pNetwork->SendRequest("/scores/tables/" + "?game_id=" + m_pAPI->GetProcGameID(), + bNow ? &sResponse : NULL, this, &gjAPI::gjInterScore::__Process, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__Process(sResponse, NULL, papOutput); + return GJ_OK; +} + + +// **************************************************************** +/* fetch and semi-cache all data store items */ +template <typename T> int gjAPI::gjInterDataStore::__FetchDataItems(gjDataItemMap* papOutput, GJ_NETWORK_OUTPUT(gjDataItemMap)) +{ + if(!m_pAPI->IsUserConnected() && m_iType) return GJ_NOT_CONNECTED; + + const bool bNow = papOutput ? true : false; + + if(m_iType) + { + // wait for prefetching + if(bNow && GJ_API_PREFETCH) + { + if(m_apDataItem.empty()) + m_pNetwork->Wait(1); + } + + // check for cached data store items + gjDataItemMap apCache; + if(this->__CheckCache(&apCache) == GJ_OK) + { + if(bNow) (*papOutput) = apCache; + else (pOutputObj->*OutputCallback)(apCache, pOutputData); + return GJ_OK; + } + } + + // access user or global data store items + const std::string sUserData = m_iType ? + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() : + ""; + + // send get data store item keys request + std::string sResponse; + if(m_pNetwork->SendRequest("/data-store/get-keys/" + "?game_id=" + m_pAPI->GetProcGameID() + + sUserData, bNow ? &sResponse : NULL, this, &gjAPI::gjInterDataStore::__Process, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__Process(sResponse, NULL, papOutput); + return GJ_OK; +} + + +// **************************************************************** +/* download a file from any URL */ +template <typename T> int gjAPI::gjInterFile::__DownloadFile(const std::string& sURL, const std::string& sToFolder, const std::string& sFileNameOverwrite, std::string* psOutput, GJ_NETWORK_OUTPUT(std::string)) +{ + if(sURL == "" || sToFolder == "") return GJ_INVALID_INPUT; + + const bool bNow = psOutput ? true : false; + + // create output path + const std::string sFileName = (sFileNameOverwrite == "") ? sURL.substr(sURL.find_last_of("/\\")+1) : sFileNameOverwrite; + const std::string sToFile = sToFolder + "/" + sFileName; + + // check for cached file + if(this->__CheckCache(sToFile) == GJ_OK) + { + if(bNow) (*psOutput) = sToFile; + else (pOutputObj->*OutputCallback)(sToFile, pOutputData); + return GJ_OK; + } + + // create folder + gjAPI::UtilCreateFolder(sToFolder); + + // download file + if(m_pNetwork->DownloadFile(sURL, sToFile, psOutput, this, &gjAPI::gjInterFile::__Process, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) this->__Process(*psOutput, NULL, NULL); + return GJ_OK; +} + + +// **************************************************************** +/* login with specific user */ +template <typename T> int gjAPI::__Login(const bool& bSession, const std::string& sUserName, const std::string& sUserToken, const bool& bNow, GJ_NETWORK_OUTPUT(int)) +{ + if(this->IsUserConnected()) return GJ_INVALID_CALL; + + // check for missing credentials + if(sUserName == "" || sUserToken == "") + { + if(!bNow) (pOutputObj->*OutputCallback)(GJ_INVALID_INPUT, pOutputData); + return GJ_INVALID_INPUT; + } + + // set main user data + m_sUserName = sUserName; + m_sUserToken = sUserToken; + m_sProcUserName = gjAPI::UtilEscapeString(m_sUserName); + m_sProcUserToken = gjAPI::UtilEscapeString(m_sUserToken); + + // convert session parameter + void* pSession = I_TO_P(bSession ? 1 : 0); + + // authenticate user + std::string sResponse; + if(m_pNetwork->SendRequest("/users/auth/" + "?game_id=" + m_sProcGameID + + "&username=" + m_sProcUserName + + "&user_token=" + m_sProcUserToken, + bNow ? &sResponse : NULL, this, &gjAPI::__LoginCallback, pSession, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__LoginCallback(sResponse, pSession, NULL); + return GJ_OK; +} + +template <typename T> int gjAPI::__Login(const bool& bSession, const std::string& sCredPath, const bool& bNow, GJ_NETWORK_OUTPUT(int)) +{ + // open credentials file + std::FILE* pFile = std::fopen(sCredPath.c_str(), "rb"); + if(!pFile) return GJ_FILE_ERROR; + + char acName[128], acToken[128]; + char* pcEnd; + + // get user name + std::fscanf(pFile, "%127[^\n]%*c", acName); + pcEnd = std::strchr(acName, 13); + if(pcEnd) *pcEnd = '\0'; + + // get user token + std::fscanf(pFile, "%127[^\n]%*c", acToken); + pcEnd = std::strchr(acToken, 13); + if(pcEnd) *pcEnd = '\0'; + + // close file and login + std::fclose(pFile); + return this->__Login(bSession, acName, acToken, bNow, GJ_NETWORK_OUTPUT_FW); +} + + +// **************************************************************** +/* post-include because of generic dependencies */ +#include "gjNetwork.hpp" +#include "gjUser.h" +#include "gjTrophy.h" +#include "gjScore.h" +#include "gjDataItem.h" + + +#endif /* _GJ_GUARD_API_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjDataItem.cpp b/test_data/lots_of_files/gjDataItem.cpp new file mode 100644 index 0000000..f1adf0f --- /dev/null +++ b/test_data/lots_of_files/gjDataItem.cpp @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#include "gjAPI.h" + + +// **************************************************************** +/* constructor */ +gjDataItem::gjDataItem(const gjData& aDataItemData, const int& iType, gjAPI* pAPI)noexcept +: m_sKey (SAFE_MAP_GET(aDataItemData, "key")) +, m_sData ("") +, m_iType (iType) +, m_sVerify ("") +, m_pTarget (NULL) +, m_pAPI (pAPI) +{ +} + + +// **************************************************************** +/* callback for successfully setting data store item (Base64) data */ +int gjDataItem::__SetDataCallback(const std::string& sData, void* pAdd, gjDataItemPtr* ppOutput) +{ + // parse output (only check status) + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse data store item"); + return GJ_REQUEST_FAILED; + } + + if(pAdd) + { + // use verification data + std::string* psAdd = (std::string*)pAdd; + m_sData = (*psAdd); + (*psAdd) = ""; + } + + if(ppOutput) (*ppOutput) = this; + return GJ_OK; +} + + +// **************************************************************** +/* callback for successfully getting data store item data */ +int gjDataItem::__GetDataCallback(const std::string& sData, void* pAdd, std::string* psOutput) +{ + // parse output + std::string sReturn; + if(m_pAPI->ParseRequestDump(sData, &sReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse data store item"); + return GJ_REQUEST_FAILED; + } + + // save data + m_sData = sReturn; + + if(psOutput) (*psOutput) = m_sData; + return GJ_OK; +} + + +// **************************************************************** +/* callback for successfully getting data store item Base64 data */ +int gjDataItem::__GetDataBase64Callback(const std::string& sData, void* pAdd, gjVoidPtr* ppOutput) +{ + // parse output and save data + std::string sReturn; + const int iError = this->__GetDataCallback(sData, NULL, &sReturn); + if(iError) return iError; + + // convert Base64 string to binary data + base64_decode(sReturn.c_str(), (unsigned char*)m_pTarget, P_TO_I(pAdd)); + + if(ppOutput) (*ppOutput) = (void*)m_pTarget; + return GJ_OK; +} + + +// **************************************************************** +/* callback for successfully clearing/removing the data store item */ +int gjDataItem::__RemoveCallback(const std::string& sData, void* pAdd, gjDataItemPtr* ppOutput) +{ + // parse output (only check status) + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse data store item"); + return GJ_REQUEST_FAILED; + } + + if(ppOutput) (*ppOutput) = this; + return GJ_OK; +} \ No newline at end of file diff --git a/test_data/lots_of_files/gjDataItem.h b/test_data/lots_of_files/gjDataItem.h new file mode 100644 index 0000000..6ec9b58 --- /dev/null +++ b/test_data/lots_of_files/gjDataItem.h @@ -0,0 +1,299 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_DATAITEM_H_ +#define _GJ_GUARD_DATAITEM_H_ + + +// **************************************************************** +/*! Data store item class.\n + * http://gamejolt.com/api/doc/game/data-store/ + * \brief Data Store Item Object + * \bug Data store items are not UTF-8 compatible (server-side),\n + * if you have problems with string data, use Base64 instead */ +class gjDataItem final +{ +private: + std::string m_sKey; //!< unique key of this item + std::string m_sData; //!< semi-cached data + + int m_iType; //!< type of this item (0 = global, 1 = user) + + std::string m_sVerify; //!< temporary verification data (helper) + void* m_pTarget; //!< last target for binary data (helper) + + gjAPI* m_pAPI; //!< main interface access pointer + + +public: + gjDataItem(const gjData& aDataItemData, const int& iType, gjAPI* pAPI)noexcept; + + /*! \name Set Data Request */ + //! @{ + /*! Set data of this data store item through an API request.\n + * May does nothing, if this is an user item and the same data was already sent. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param sData Data to send + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_REQUEST_CANCELED** if this is an user item and the same data was already sent\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int SetDataNow(const std::string& sData) {return this->__SetData(sData, true, GJ_NETWORK_NULL_API(gjDataItemPtr));} + inline int SetDataCall(const std::string& sData) {return this->__SetData(sData, false, GJ_NETWORK_NULL_API(gjDataItemPtr));} + template <typename T> inline int SetDataCall(const std::string& sData, GJ_NETWORK_OUTPUT(gjDataItemPtr)) {return this->__SetData(sData, false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Set Data Request Base64 */ + //! @{ + /*! Like \link SetDataNow SetData\endlink\n + * Allows to send data in binary form. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param pData Data in binary form to send + * \param iSize Size of the data + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_REQUEST_CANCELED** if this is an user item and the same data was already sent\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int SetDataBase64Now(void* pData, const size_t& iSize) {return this->__SetDataBase64(pData, iSize, true, GJ_NETWORK_NULL_API(gjDataItemPtr));} + inline int SetDataBase64Call(void* pData, const size_t& iSize) {return this->__SetDataBase64(pData, iSize, false, GJ_NETWORK_NULL_API(gjDataItemPtr));} + template <typename T> inline int SetDataBase64Call(void* pData, const size_t& iSize, GJ_NETWORK_OUTPUT(gjDataItemPtr)) {return this->__SetDataBase64(pData, iSize, false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Get Data Request */ + //! @{ + /*! Get data of this data store item through an API request. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int GetDataNow(std::string* psOutput) {if(!psOutput) return GJ_INVALID_INPUT; return this->__GetData(psOutput, GJ_NETWORK_NULL_API(std::string));} + template <typename T> inline int GetDataCall(GJ_NETWORK_OUTPUT(std::string)) {return this->__GetData(NULL, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Get Data Request Base64 */ + //! @{ + /*! Like \link GetDataNow GetData\endlink\n + * Allows to get data in binary form. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param pTarget Pointer to the target + * \param iSize Size of the target + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int GetDataBase64Now(void* pTarget, const size_t& iSize) {if(!pTarget || iSize <= 0) return GJ_INVALID_INPUT; return this->__GetDataBase64(pTarget, iSize, true, GJ_NETWORK_NULL_API(gjVoidPtr));} + inline int GetDataBase64Call(void* pTarget, const size_t& iSize) {if(!pTarget || iSize <= 0) return GJ_INVALID_INPUT; return this->__GetDataBase64(pTarget, iSize, false, GJ_NETWORK_NULL_API(gjVoidPtr));} + template <typename T> inline int GetDataBase64Call(void* pTarget, const size_t& iSize, GJ_NETWORK_OUTPUT(gjVoidPtr)) {if(!pTarget || iSize <= 0) return GJ_INVALID_INPUT; return this->__GetDataBase64(pTarget, iSize, false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Remove Request */ + //! @{ + /*! Clears/Removes the data store item through an API request.\n + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int RemoveNow() {return this->__Remove(true, GJ_NETWORK_NULL_API(gjDataItemPtr));} + inline int RemoveCall() {return this->__Remove(false, GJ_NETWORK_NULL_API(gjDataItemPtr));} + template <typename T> inline int RemoveCall(GJ_NETWORK_OUTPUT(gjDataItemPtr)) {return this->__Remove(false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Get Attributes */ + //! @{ + inline const std::string& GetKey()const {return m_sKey;} //!< \copybrief m_sKey + inline const int& GetType()const {return m_iType;} //!< \copybrief m_iType + /*! */ //! @} + + +private: + DISABLE_COPY(gjDataItem) + + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __SetData(const std::string& sData, const bool& bNow, GJ_NETWORK_OUTPUT(gjDataItemPtr)); + template <typename T> int __SetDataBase64(void* pData, const size_t& iSize, const bool& bNow, GJ_NETWORK_OUTPUT(gjDataItemPtr)); + template <typename T> int __GetData(std::string* psOutput, GJ_NETWORK_OUTPUT(std::string)); + template <typename T> int __GetDataBase64(void* pTarget, const size_t& iSize, const bool& bNow, GJ_NETWORK_OUTPUT(gjVoidPtr)); + template <typename T> int __Remove(const bool& bNow, GJ_NETWORK_OUTPUT(gjDataItemPtr)); + //! @} + + /*! \name Callback Functions */ + //! @{ + int __SetDataCallback(const std::string& sData, void* pAdd, gjDataItemPtr* ppOutput); + int __GetDataCallback(const std::string& sData, void* pAdd, std::string* psOutput); + int __GetDataBase64Callback(const std::string& sData, void* pAdd, gjVoidPtr* ppOutput); + int __RemoveCallback(const std::string& sData, void* pAdd, gjDataItemPtr* ppOutput); + //! @} +}; + + +// **************************************************************** +/* set data of this data store item */ +template <typename T> int gjDataItem::__SetData(const std::string& sData, const bool& bNow, GJ_NETWORK_OUTPUT(gjDataItemPtr)) +{ + if(!m_pAPI->IsUserConnected() && m_iType) return GJ_NOT_CONNECTED; + + // cancel request if data was already sent + if(sData == m_sData && m_iType) return GJ_REQUEST_CANCELED; + + // set verification data + m_sVerify = sData; + + // access user or global data store item + const std::string sUserData = m_iType ? + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() : + ""; + + // send set data store request + std::string sResponse; + if(m_pAPI->SendRequest("/data-store/set/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&key=" + gjAPI::UtilEscapeString(m_sKey) + + sUserData + "&POST" + sData, bNow ? &sResponse : NULL, this, &gjDataItem::__SetDataCallback, &m_sVerify, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__SetDataCallback(sResponse, &m_sVerify, NULL); + return GJ_OK; +} + + +// **************************************************************** +/* set Base64 data of this data store item */ +template <typename T> int gjDataItem::__SetDataBase64(void* pData, const size_t& iSize, const bool& bNow, GJ_NETWORK_OUTPUT(gjDataItemPtr)) +{ + if(!m_pAPI->IsUserConnected() && m_iType) return GJ_NOT_CONNECTED; + if(!pData || iSize <= 0) return GJ_INVALID_INPUT; + + const size_t iNeed = base64_needed(iSize); + + // convert binary data to Base64 string + char* pcBase64 = new char[iNeed]; + base64_encode((unsigned char*)pData, iSize, pcBase64, iNeed); + + // execute set data function with Base64 string + const int iReturn = this->__SetData(pcBase64, bNow, GJ_NETWORK_OUTPUT_FW); + SAFE_DELETE_ARRAY(pcBase64) + + return iReturn; +} + + +// **************************************************************** +/* get data of this data store item */ +template <typename T> int gjDataItem::__GetData(std::string* psOutput, GJ_NETWORK_OUTPUT(std::string)) +{ + if(!m_pAPI->IsUserConnected() && m_iType) return GJ_NOT_CONNECTED; + + const bool bNow = psOutput ? true : false; + + // check for cached data + if(m_iType) + { + if(m_sData.length()) + { + if(bNow) (*psOutput) = m_sData; + else (pOutputObj->*OutputCallback)(m_sData, pOutputData); + return GJ_OK; + } + } + + // access user or global data store item + const std::string sUserData = m_iType ? + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() : + ""; + + // send get data store request + std::string sResponse; + if(m_pAPI->SendRequest("/data-store/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&key=" + gjAPI::UtilEscapeString(m_sKey) + + "&format=" + "dump" + + sUserData, bNow ? &sResponse : NULL, this, &gjDataItem::__GetDataCallback, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__GetDataCallback(sResponse, NULL, psOutput); + return GJ_OK; +} + + +// **************************************************************** +/* get Base64 data of this data store item */ +template <typename T> int gjDataItem::__GetDataBase64(void* pTarget, const size_t& iSize, const bool& bNow, GJ_NETWORK_OUTPUT(gjVoidPtr)) +{ + if(!m_pAPI->IsUserConnected() && m_iType) return GJ_NOT_CONNECTED; + if(!pTarget || iSize <= 0) return GJ_INVALID_INPUT; + + // save last target + m_pTarget = pTarget; + + // check for cached data + if(m_iType) + { + if(m_sData.length()) + { + // convert Base64 string to binary data + base64_decode(m_sData.c_str(), (unsigned char*)m_pTarget, iSize); + if(!bNow) (pOutputObj->*OutputCallback)(m_pTarget, pOutputData); + return GJ_OK; + } + } + + // access user or global data store item + const std::string sUserData = m_iType ? + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() : + ""; + + // send get data store request + std::string sResponse; + if(m_pAPI->SendRequest("/data-store/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&key=" + gjAPI::UtilEscapeString(m_sKey) + + "&format=" + "dump" + + sUserData, bNow ? &sResponse : NULL, this, &gjDataItem::__GetDataBase64Callback, I_TO_P(iSize), GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__GetDataBase64Callback(sResponse, I_TO_P(iSize), NULL); + return GJ_OK; +} + + +// **************************************************************** +/* clear/remove this data store item */ +template <typename T> int gjDataItem::__Remove(const bool& bNow, GJ_NETWORK_OUTPUT(gjDataItemPtr)) +{ + if(!m_pAPI->IsUserConnected() && m_iType) return GJ_NOT_CONNECTED; + + // access user or global data store item + const std::string sUserData = m_iType ? + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() : + ""; + + // send remove data store request + std::string sResponse; + if(m_pAPI->SendRequest("/data-store/remove/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&key=" + gjAPI::UtilEscapeString(m_sKey) + + sUserData, bNow ? &sResponse : NULL, this, &gjDataItem::__RemoveCallback, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__RemoveCallback(sResponse, NULL, NULL); + return GJ_OK; +} + + +#endif /* _GJ_GUARD_DATAITEM_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjInline.h b/test_data/lots_of_files/gjInline.h new file mode 100644 index 0000000..8bb4523 --- /dev/null +++ b/test_data/lots_of_files/gjInline.h @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_INLINE_H_ +#define _GJ_GUARD_INLINE_H_ + +/* Alternative Include File */ + +#include "gjAPI.h" + +#include "base64.cpp" +#include "MD5.cpp" + +#include "gjAPI.cpp" +#include "gjNetwork.cpp" + +#include "gjUser.cpp" +#include "gjTrophy.cpp" +#include "gjScore.cpp" +#include "gjDataItem.cpp" + + +#endif /* _GJ_GUARD_INLINE_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjLookup.h b/test_data/lots_of_files/gjLookup.h new file mode 100644 index 0000000..2f0dcec --- /dev/null +++ b/test_data/lots_of_files/gjLookup.h @@ -0,0 +1,254 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_LOOKUP_H_ +#define _GJ_GUARD_LOOKUP_H_ + +typedef unsigned int gjUint; +#ifndef ASSERT + #define ASSERT(x) +#endif + + +// **************************************************************** +// lookup container class +template <typename T> class gjLookup final +{ +public: + //! internal types + typedef std::pair<std::string, T> gjEntry; + typedef std::vector<gjEntry> gjList; + typedef typename gjList::iterator gjIterator; + typedef typename gjList::const_iterator gjConstIterator; + + +private: + gjList m_aList; //!< vector-list with pair-values + + +public: + gjLookup()noexcept; + gjLookup(const gjLookup<T>& c)noexcept; + gjLookup(gjLookup<T>&& m)noexcept; + ~gjLookup(); + + //! assignment operator + //! @{ + gjLookup<T>& operator = (gjLookup<T> o)noexcept; + template <typename S> friend void swap(gjLookup<S>& a, gjLookup<S>& b)noexcept; + //! @} + + //! access specific entry + //! @{ + const T& at(const char* pcKey)const noexcept; + T& operator [] (const char* pcKey)noexcept; + inline T& operator [] (const gjUint& iIndex)noexcept {return m_aList[iIndex].second;} + //! @} + + //! check number of existing entries + //! @{ + inline gjUint count(const char* pcKey)const noexcept {return this->__check(this->__retrieve(pcKey)) ? 1 : 0;} + inline gjUint size()const noexcept {return m_aList.size();} + inline bool empty()const noexcept {return m_aList.empty();} + //! @} + + //! control memory consumption + //! @{ + inline void reserve(const gjUint& iReserve)noexcept {m_aList.reserve(iReserve);} + inline gjUint capacity()const noexcept {return m_aList.capacity();} + //! @} + + //! remove existing entries + //! @{ + void erase(const T& Entry)noexcept; + void erase(const char* pcKey)noexcept; + inline void erase(const gjUint& iIndex)noexcept {ASSERT(iIndex < m_aList.size()) m_aList.erase(m_aList.begin()+iIndex);} + inline void erase(const gjConstIterator& Iterator)noexcept {m_aList.erase(Iterator);} + inline void clear()noexcept {m_aList.clear();} + //! @} + + //! retrieve internal iterator + //! @{ + inline gjIterator begin()noexcept {return m_aList.begin();} + inline gjConstIterator begin()const noexcept {return m_aList.begin();} + inline gjIterator end()noexcept {return m_aList.end();} + inline gjConstIterator end()const noexcept {return m_aList.end();} + //! @} + + +private: + //! retrieve iterator + //! @{ + gjIterator __retrieve(const T& Entry)noexcept; + gjConstIterator __retrieve(const T& Entry)const noexcept; + gjIterator __retrieve(const char* pcKey)noexcept; + gjConstIterator __retrieve(const char* pcKey)const noexcept; + //! @} + + //! check for valid iterator + //! @{ + inline bool __check(const gjIterator& it)const noexcept {return (it != m_aList.end()) ? true : false;} + inline bool __check(const gjConstIterator& it)const noexcept {return (it != m_aList.end()) ? true : false;} + //! @} +}; + + +// **************************************************************** +// constructor +template <typename T> gjLookup<T>::gjLookup()noexcept +{ + // reserve variable sized memory + constexpr_var gjUint iSize = 1 + 64/sizeof(T); + m_aList.reserve(iSize); +} + +template <typename T> gjLookup<T>::gjLookup(const gjLookup<T>& c)noexcept +: m_aList (c.m_aList) +{ +} + +template <typename T> gjLookup<T>::gjLookup(gjLookup<T>&& m)noexcept +: m_aList (std::move(m.m_aList)) +{ +} + + +// **************************************************************** +// destructor +template <typename T> gjLookup<T>::~gjLookup() +{ + m_aList.clear(); +} + + +// **************************************************************** +// assignment operator +template <typename T> gjLookup<T>& gjLookup<T>::operator = (gjLookup<T> o)noexcept +{ + swap(*this, o); + return *this; +} + +template <typename S> void swap(gjLookup<S>& a, gjLookup<S>& b)noexcept +{ + using std::swap; + swap(a.m_aList, b.m_aList); +} + + +// **************************************************************** +// access specific entry +template <typename T> const T& gjLookup<T>::at(const char* pcKey)const noexcept +{ + // retrieve and check iterator by specific key + auto it = this->__retrieve(pcKey); + ASSERT(this->__check(it)) + + return it->second; +} + + +// **************************************************************** +// access specific entry and create it if necessary +template <typename T> T& gjLookup<T>::operator [] (const char* pcKey)noexcept +{ + // retrieve and check iterator by specific key + auto it = this->__retrieve(pcKey); + if(!this->__check(it)) + { + // create new entry + m_aList.push_back(gjEntry(pcKey, T())); + it = m_aList.end()-1; + } + + return it->second; +} + + +// **************************************************************** +// remove existing entry +template <typename T> void gjLookup<T>::erase(const T& Entry)noexcept +{ + // retrieve and check iterator by specific value + auto it = this->__retrieve(Entry); + if(this->__check(it)) + { + // remove existing entry + m_aList.erase(it); + } +} + + +template <typename T> void gjLookup<T>::erase(const char* pcKey)noexcept +{ + // retrieve and check iterator by specific key + auto it = this->__retrieve(pcKey); + if(this->__check(it)) + { + // remove existing entry + m_aList.erase(it); + } +} + + +// **************************************************************** +// retrieve iterator by specific value +template <typename T> typename gjLookup<T>::gjIterator gjLookup<T>::__retrieve(const T& Entry)noexcept +{ + // loop through all entries + FOR_EACH(it, m_aList) + { + // compare values + if(it->second == Entry) + return it; + } + return m_aList.end(); +} + +template <typename T> typename gjLookup<T>::gjConstIterator gjLookup<T>::__retrieve(const T& Entry)const noexcept +{ + // loop through all entries + FOR_EACH(it, m_aList) + { + // compare values + if(it->second == Entry) + return it; + } + return m_aList.end(); +} + + +// **************************************************************** +// retrieve iterator by specific key +template <typename T> typename gjLookup<T>::gjIterator gjLookup<T>::__retrieve(const char* pcKey)noexcept +{ + // loop through all entries + FOR_EACH(it, m_aList) + { + // compare string-keys + if(!std::strcmp(it->first.c_str(), pcKey)) + return it; + } + return m_aList.end(); +} + +template <typename T> typename gjLookup<T>::gjConstIterator gjLookup<T>::__retrieve(const char* pcKey)const noexcept +{ + // loop through all entries + FOR_EACH(it, m_aList) + { + // compare string-keys + if(!std::strcmp(it->first.c_str(), pcKey)) + return it; + } + return m_aList.end(); +} + + +#endif /* _GJ_GUARD_LOOKUP_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjNetwork.cpp b/test_data/lots_of_files/gjNetwork.cpp new file mode 100644 index 0000000..b4e2917 --- /dev/null +++ b/test_data/lots_of_files/gjNetwork.cpp @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#include "gjAPI.h" + + +// **************************************************************** +/* constructor */ +gjNetwork::gjNetwork(gjAPI* pAPI)noexcept +: m_iNumSessions (0) +, m_pAPI (pAPI) +{ + // init cURL + curl_global_init(CURL_GLOBAL_ALL); + m_pMultiHandle = curl_multi_init(); + + // reserve some memory + m_apCall.reserve(GJ_API_RESERVE_CALL); +} + + +// **************************************************************** +/* destructor */ +gjNetwork::~gjNetwork() +{ + // finish and remove open sessions + this->Wait(2); + while(!m_apCall.empty()) + { + gjAPI::ErrorLogAdd("Network Error: session had to be killed <" + m_apCall[0]->GetInfo() + ">"); + this->__KillCall(m_apCall[0]); + } + + // clear memory + m_apCall.clear(); + + // exit cURL + curl_multi_cleanup(m_pMultiHandle); + curl_global_cleanup(); +} + + +// **************************************************************** +/* update all active non-blocking cURL sessions */ +bool gjNetwork::Update() +{ + if(m_apCall.empty()) return false; + + // update cURL + curl_multi_perform(m_pMultiHandle, &m_iNumSessions); + + // manage active cURL sessions + CURLMsg* pMsg; + while((pMsg = curl_multi_info_read(m_pMultiHandle, &m_iNumSessions))) + { + // cURL session finished + if(pMsg->msg == CURLMSG_DONE) + { + CURL* pSession = pMsg->easy_handle; + const bool bOK = (pMsg->data.result == CURLE_OK) ? true : false; + + // search callback object and finish the operation + FOR_EACH(it, m_apCall) + { + gjCall* pCall = (*it); + + if(pCall->GetSession() == pSession) + { + // check for errors + if(!bOK) + { + gjAPI::ErrorLogAdd("Network Error: sending non-blocking request failed <" + pCall->GetInfo() + ">"); + gjAPI::ErrorLogAdd("Network Error: " + std::string(curl_easy_strerror(pMsg->data.result))); + } + + pCall->Finish(bOK); + SAFE_DELETE(pCall) + m_apCall.erase(it); + break; + } + } + + // close cURL session + curl_multi_remove_handle(m_pMultiHandle, pSession); + curl_easy_cleanup(pSession); + } + } + + return true; +} + + +// **************************************************************** +/* finish all active sessions and return */ +void gjNetwork::Wait(const unsigned int& iMaxWait) +{ + // get max waiting time (low precision) + const time_t iMaxTime = std::time(NULL) + iMaxWait; + + // force network update + while((iMaxTime >= std::time(NULL) || !iMaxWait) && this->Update()) {} +} + + +// **************************************************************** +/* check for existing callbacks on the list */ +gjNetwork::gjCall* gjNetwork::__CheckCall(const std::string& sInfo) +{ + // search callback object and compare info string + FOR_EACH(it, m_apCall) + { + if((*it)->GetInfo() == sInfo) + return (*it); + } + + return NULL; +} + + +// **************************************************************** +/* kill and remove session and callback object */ +void gjNetwork::__KillCall(gjCall* pCall) +{ + // search callback object + FOR_EACH(it, m_apCall) + { + gjCall* pCurCall = (*it); + + if(pCurCall == pCall) + { + CURL* pSession = pCurCall->GetSession(); + + // delete callback object + pCurCall->Finish(false); + SAFE_DELETE(pCurCall) + m_apCall.erase(it); + + // close cURL session + curl_multi_remove_handle(m_pMultiHandle, pSession); + curl_easy_cleanup(pSession); + + return; + } + } +} + + +// **************************************************************** +/* callbacks for writing operations with cURL */ +size_t write_to_string(char* ptr, size_t size, size_t count, std::string* stream) +{ + const size_t num = size * count; + stream->append(ptr, 0, num); + return num; +} + +size_t write_to_file(void* ptr, size_t size, size_t count, FILE* stream) +{ + return fwrite(ptr, size, count, stream); +} \ No newline at end of file diff --git a/test_data/lots_of_files/gjNetwork.h b/test_data/lots_of_files/gjNetwork.h new file mode 100644 index 0000000..4bd4bdb --- /dev/null +++ b/test_data/lots_of_files/gjNetwork.h @@ -0,0 +1,228 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_NETWORK_H_ +#define _GJ_GUARD_NETWORK_H_ + + +// **************************************************************** +/*! Main network interface class.\n + * Send requests, fetch data and download files with the cURL library.\n + * http://curl.haxx.se/ + * \brief Network Interface */ +class gjNetwork final +{ +private: + // **************************************************************** + /*! Callback interface class for non-blocking cURL sessions.\n + * \brief Callback Interface */ + class gjCall + { + protected: + CURL* m_pSession; //!< cURL session + std::string m_sInfo; //!< information about this callback + + + public: + gjCall(CURL* pSession, const std::string& sInfo)noexcept : m_pSession(pSession), m_sInfo(sInfo) {} + virtual ~gjCall() {} + + /*! \name Finish Session */ + //! @{ + virtual void Finish(const bool& bOK) = 0; + //! @} + + /*! \name Get Attributes */ + //! @{ + inline CURL* GetSession()const {return m_pSession;} //!< \copybrief m_pSession + inline const std::string& GetInfo()const {return m_sInfo;} //!< \copybrief m_sInfo + /*! */ //! @} + }; + + // **************************************************************** + /*! Template bridge for the callback interface class.\n + * T = type of output receiving object\n + * P = type of processing object\n + * D = output type\n + * \brief Template Bridge */ + template <typename P, typename D> class gjCallTemplate : public gjCall + { + protected: + /*! \brief Output Interface Structure */ + template <typename Ds> struct sOutput + { + virtual ~sOutput() {} + + /*! \name Execute Output Callback */ + //! @{ + virtual void Execute(const Ds& pProcessedOutput) = 0; + //! @} + }; + + /*! \brief Specific Output Structure */ + template <typename T, typename Ds> struct sOutputSpecific : public sOutput<Ds> + { + T* m_pOutputObj; //!< object with output callback function + void (T::*m_OutputCallback)(const Ds&, void*); //!< callback function to receive the data object when finished + void* m_pOutputData; //!< additional data for the output callback + + /*! \name Execute Output Callback */ + //! @{ + void Execute(const Ds& pProcessedOutput)override {(m_pOutputObj->*(m_OutputCallback))(pProcessedOutput, m_pOutputData);} + //! @} + }; + + + protected: + P* m_pProcessObj; //!< object with processing callback function + int (P::*m_ProcessCallback)(const std::string&, void*, D*); //!< callback function to convert the response string to a data object + void* m_pProcessData; //!< additional data for the processing callback + + std::vector<sOutput<D>*> m_apOutput; //!< list of output structs + + + public: + gjCallTemplate(CURL* pSession, const std::string& sInfo, GJ_NETWORK_PROCESS)noexcept; + virtual ~gjCallTemplate(); + + /*! \name Add Output */ + //! @{ + template <typename T> void AddOutput(GJ_NETWORK_OUTPUT(D)); + //! @} + }; + + + // **************************************************************** + /*! Request callback sub-interface class for non-blocking cURL sessions.\n + * \brief Request Callback Sub-Interface */ + template <typename P, typename D> class gjCallRequest final : public gjCallTemplate<P,D> + { + private: + std::string* m_psResponse; //!< response string of the request + curl_httppost* m_pPostList; //!< data for a POST request + + + public: + gjCallRequest(std::string* psResponse, curl_httppost* pPostList, CURL* pSession, const std::string& sInfo, GJ_NETWORK_PROCESS)noexcept + : gjCallTemplate<P,D>(pSession, sInfo, GJ_NETWORK_PROCESS_FW), m_psResponse(psResponse), m_pPostList(pPostList) {} + + /*! \name Finish Session */ + //! @{ + void Finish(const bool& bOK)override; + //! @} + }; + + + // **************************************************************** + /*! Download callback sub-interface class for non-blocking cURL sessions.\n + * \brief Download Callback Sub-Interface */ + template <typename P, typename D> class gjCallDownload final : public gjCallTemplate<P,D> + { + private: + FILE* m_pFile; //!< file handle + std::string m_sPath; //!< local path of the file + + + public: + gjCallDownload(FILE* pFile, const std::string& sPath, CURL* pSession, const std::string& sInfo, GJ_NETWORK_PROCESS)noexcept + : gjCallTemplate<P,D>(pSession, sInfo, GJ_NETWORK_PROCESS_FW), m_pFile(pFile), m_sPath(sPath) {} + + /*! \name Finish Session */ + //! @{ + void Finish(const bool& bOK)override; + //! @} + }; + + +private: + CURLM* m_pMultiHandle; //!< handle for simultaneous cURL operations + int m_iNumSessions; //!< current number of active cURL sessions + + std::vector<gjCall*> m_apCall; //!< list of callback objects + + gjAPI* m_pAPI; //!< main interface access pointer + + +public: + gjNetwork(gjAPI* pAPI)noexcept; + ~gjNetwork(); + + /*! \name Update */ + //! @{ + /*! Update all active non-blocking cURL sessions.\n + * Execute associated callback functions when a session is finished. + * \return TRUE when active, FALSE when idle */ + bool Update(); + //! @} + + /*! \name Wait */ + //! @{ + /*! Finish all active sessions and return. + * \param iMaxWait Max waiting time in seconds (0 = until everything is finished) */ + void Wait(const unsigned int& iMaxWait); + //! @} + + /*! \name Send Request */ + //! @{ + /*! Send a direct or non-blocking request to the API.\n + * Retrieve a response string when finished. + * \note Creates a non-blocking session when output string is NULL\n + * Use "&POST<data>" at the end of the URL for a POST request + * \param sURL Relative API request string + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_INPUT** if URL string is empty\n + * **GJ_NETWORK_ERROR** if session cannot be established\n + * (see #GJ_ERROR) */ + template <typename T, typename P, typename D> int SendRequest(const std::string& sURL, std::string* psOutput, GJ_NETWORK_PROCESS, GJ_NETWORK_OUTPUT(D))noexcept; + //! @} + + /*! \name Download File */ + //! @{ + /*! Download a file direct or non-blocking from any URL.\n + * Retrieve the local path of the file when finished. + * \note Creates a non-blocking session when output string is NULL + * \param sURL Full path of the remote file + * \param sToFile Relative path of the download target + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_INPUT** if URL string or target file string is empty\n + * **GJ_NETWORK_ERROR** if session cannot be established\n + * **GJ_FILE_ERROR** if file cannot be written\n + * (see #GJ_ERROR) */ + template <typename T, typename P, typename D> int DownloadFile(const std::string& sURL, const std::string& sToFile, std::string* psOutput, GJ_NETWORK_PROCESS, GJ_NETWORK_OUTPUT(D))noexcept; + //! @} + + /*! \name Get Attributes */ + //! @{ + inline size_t GetNumSessions()const {return m_apCall.size();} //!< \copybrief m_iNumSessions + /*! */ //! @} + + +private: + DISABLE_COPY(gjNetwork) + + /*! \name Management Functions */ + //! @{ + gjCall* __CheckCall(const std::string& sInfo); + void __KillCall(gjCall* pCall); + //! @} +}; + + +// **************************************************************** +/*! Writing Callbacks */ +size_t write_to_string(char* ptr, size_t size, size_t count, std::string* stream); +size_t write_to_file(void* ptr, size_t size, size_t count, FILE* stream); + + +// template implementation can be found in gjNetwork.hpp + + +#endif /* _GJ_GUARD_NETWORK_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjNetwork.hpp b/test_data/lots_of_files/gjNetwork.hpp new file mode 100644 index 0000000..d1e74bf --- /dev/null +++ b/test_data/lots_of_files/gjNetwork.hpp @@ -0,0 +1,329 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_NETWORK_HPP_ +#define _GJ_GUARD_NETWORK_HPP_ + + +// **************************************************************** +/* constructor */ +template <typename P, typename D> gjNetwork::gjCallTemplate<P,D>::gjCallTemplate(CURL* pSession, const std::string& sInfo, GJ_NETWORK_PROCESS)noexcept +: gjCall (pSession, sInfo) +, m_pProcessObj (pProcessObj) +, m_ProcessCallback (ProcessCallback) +, m_pProcessData (pProcessData) +{ + // reserve some memory + m_apOutput.reserve(GJ_API_RESERVE_CALL_OUTPUT); +} + + +// **************************************************************** +/* destructor */ +template <typename P, typename D> gjNetwork::gjCallTemplate<P,D>::~gjCallTemplate() +{ + // delete all output structs + FOR_EACH(it, m_apOutput) + SAFE_DELETE(*it) + + // clear memory + m_apOutput.clear(); +} + + +// **************************************************************** +/* add output struct to the list */ +template <typename P, typename D> template <typename T> void gjNetwork::gjCallTemplate<P,D>::AddOutput(GJ_NETWORK_OUTPUT(D)) +{ + // create struct + sOutputSpecific<T,D>* pOutput = new sOutputSpecific<T,D>; + pOutput->m_pOutputObj = pOutputObj; + pOutput->m_OutputCallback = OutputCallback; + pOutput->m_pOutputData = pOutputData; + + // add struct + m_apOutput.push_back(pOutput); +} + + +// **************************************************************** +/* finish a request session */ +template <typename P, typename D> void gjNetwork::gjCallRequest<P,D>::Finish(const bool& bOK) +{ + //if(bOK) + { + D pProcessedOutput; + +#if defined(_GJ_DEBUG_) + + // show current activity + gjAPI::ErrorLogAdd("SendRequest.Finish: <" + this->m_sInfo + ">\n(\n" + *m_psResponse +")"); + +#endif + // process the response string + if(!(this->m_pProcessObj->*this->m_ProcessCallback)(*m_psResponse, this->m_pProcessData, &pProcessedOutput)) + { + // call all callbacks + FOR_EACH(it, this->m_apOutput) + (*it)->Execute(pProcessedOutput); + } + } + + // delete response string + SAFE_DELETE(m_psResponse) + + // clear POST data + if(m_pPostList) curl_formfree(m_pPostList); +} + + +// **************************************************************** +/* finish a download session */ +template <typename P, typename D> void gjNetwork::gjCallDownload<P,D>::Finish(const bool& bOK) +{ + // close file handle + std::fclose(m_pFile); + + //if(bOK) + { + D pProcessedOutput; + +#if defined(_GJ_DEBUG_) + + // show current activity + gjAPI::ErrorLogAdd("DownloadFile.Finish: <" + this->m_sInfo + ">"); + +#endif + // process the response string + if(!(this->m_pProcessObj->*this->m_ProcessCallback)(m_sPath, this->m_pProcessData, &pProcessedOutput)) + { + // call all callbacks + FOR_EACH(it, this->m_apOutput) + (*it)->Execute(pProcessedOutput); + } + } +} + + +// **************************************************************** +/* send direct or non-blocking request */ +template <typename T, typename P, typename D> int gjNetwork::SendRequest(const std::string& sURL, std::string* psOutput, GJ_NETWORK_PROCESS, GJ_NETWORK_OUTPUT(D))noexcept +{ + if(sURL == "") return GJ_INVALID_INPUT; + + const size_t iPostPos = sURL.find("&POST"); + const bool bPost = (iPostPos != std::string::npos) ? true : false; + const bool bHttp = (int(sURL.substr(0, 8).find("://")) >= 0) ? true : false; + + const bool bNow = psOutput ? true : false; + const std::string sInfo = bPost ? sURL.substr(0, iPostPos) : sURL; + + if(!bNow) + { + // check for existing session with same request string + gjCall* pOldCall = this->__CheckCall(sInfo); + if(pOldCall) + { + if(bPost) + { + // remove old session + this->__KillCall(pOldCall); + } + else + { + // append new callback to old session + gjCallRequest<P,D>* pOldCallRequest = (gjCallRequest<P,D>*)pOldCall; + pOldCallRequest->AddOutput(GJ_NETWORK_OUTPUT_FW); + return GJ_OK; + } + } + } + + // open cURL session + CURL* pSession = curl_easy_init(); + if(pSession) + { + std::string sRequest; + + curl_httppost* pPostList = NULL; + curl_httppost* pEndList = NULL; + + // create new response string for non-blocking session + if(!bNow) psOutput = new std::string(); + + // check for POST parameter + if(bPost) + { + // set POST data + curl_formadd(&pPostList, &pEndList, + CURLFORM_COPYNAME, "data", + CURLFORM_COPYCONTENTS, sURL.substr(iPostPos+5).c_str(), + CURLFORM_END); + + // create a POST request + sRequest = (bHttp ? "" : GJ_API_URL) + sURL.substr(0, iPostPos); + curl_easy_setopt(pSession, CURLOPT_POST, 1); + curl_easy_setopt(pSession, CURLOPT_HTTPPOST, pPostList); + } + else + { + // create a GET request + sRequest = (bHttp ? "" : GJ_API_URL) + sURL; + curl_easy_setopt(pSession, CURLOPT_TIMEOUT, GJ_API_TIMEOUT_REQUEST); + } + + // add MD5 signature + sRequest += "&signature=" + md5(sRequest + m_pAPI->GetGamePrivateKey()); + +#if defined(_GJ_DEBUG_) + + // show current activity + gjAPI::ErrorLogAdd("SendRequest.Execute: <" + sRequest + ">"); + +#endif + // set all session parameters + curl_easy_setopt(pSession, CURLOPT_URL, sRequest.c_str()); + curl_easy_setopt(pSession, CURLOPT_WRITEFUNCTION, write_to_string); + curl_easy_setopt(pSession, CURLOPT_WRITEDATA, psOutput); + curl_easy_setopt(pSession, CURLOPT_CONNECTTIMEOUT, GJ_API_TIMEOUT_CONNECTION); + curl_easy_setopt(pSession, CURLOPT_ACCEPT_ENCODING, GJ_API_NET_COMPRESSION); + curl_easy_setopt(pSession, CURLOPT_TCP_KEEPALIVE, GJ_API_NET_KEEPALIVE ? 1 : 0); + + if(bNow) + { + // perform request and close cURL session + CURLcode res = curl_easy_perform(pSession); + curl_easy_cleanup(pSession); + + // clear POST data + if(pPostList) curl_formfree(pPostList); + + // check for errors + if(res != CURLE_OK) + { + gjAPI::ErrorLogAdd("Network Error: sending direct request failed <" + sInfo + ">"); + gjAPI::ErrorLogAdd("Network Error: " + std::string(curl_easy_strerror(res))); + return GJ_REQUEST_FAILED; + } + } + else + { + // append session to multi handle + curl_multi_add_handle(m_pMultiHandle, pSession); + curl_multi_perform(m_pMultiHandle, &m_iNumSessions); + + // create and save callback object + gjCallRequest<P,D>* pCall = new gjCallRequest<P,D>(psOutput, pPostList, pSession, sInfo, GJ_NETWORK_PROCESS_FW); + pCall->AddOutput(GJ_NETWORK_OUTPUT_FW); + m_apCall.push_back(pCall); + } + } + else + { + gjAPI::ErrorLogAdd("Network Error: cannot establish curl session"); + return GJ_NETWORK_ERROR; + } + + return GJ_OK; +} + + +// **************************************************************** +/* download file direct or non-blocking */ +template <typename T, typename P, typename D> int gjNetwork::DownloadFile(const std::string& sURL, const std::string& sToFile, std::string* psOutput, GJ_NETWORK_PROCESS, GJ_NETWORK_OUTPUT(D))noexcept +{ + if(sURL == "" || sToFile == "") return GJ_INVALID_INPUT; + + const bool bNow = psOutput ? true : false; + const std::string sInfo = sURL; + + if(!bNow) + { + // check for existing session with same request string + gjCall* pOldCall = this->__CheckCall(sInfo); + if(pOldCall) + { + // append new callback to old session + gjCallDownload<P,D>* pOldCallDownload = (gjCallDownload<P,D>*)pOldCall; + pOldCallDownload->AddOutput(GJ_NETWORK_OUTPUT_FW); + return GJ_OK; + } + } + + // open cURL session + CURL* pSession = curl_easy_init(); + if(pSession) + { + // open file + std::FILE* pFile = std::fopen(sToFile.c_str(), "wb"); + if(pFile) + { +#if defined(_GJ_DEBUG_) + + // show current activity + gjAPI::ErrorLogAdd("DownloadFile.Execute: <" + sURL + ">"); + +#endif + // set all session parameters + curl_easy_setopt(pSession, CURLOPT_URL, sURL.c_str()); + curl_easy_setopt(pSession, CURLOPT_WRITEFUNCTION, write_to_file); + curl_easy_setopt(pSession, CURLOPT_WRITEDATA, pFile); + curl_easy_setopt(pSession, CURLOPT_CONNECTTIMEOUT, GJ_API_TIMEOUT_CONNECTION); + curl_easy_setopt(pSession, CURLOPT_ACCEPT_ENCODING, GJ_API_NET_COMPRESSION); + curl_easy_setopt(pSession, CURLOPT_TCP_KEEPALIVE, GJ_API_NET_KEEPALIVE ? 1 : 0); + + if(bNow) + { + // perform download and close cURL session + CURLcode res = curl_easy_perform(pSession); + curl_easy_cleanup(pSession); + + // close file + std::fclose(pFile); + + // check for errors + if(res != CURLE_OK) + { + gjAPI::ErrorLogAdd("Network Error: direct file download failed <" + sInfo + ">"); + gjAPI::ErrorLogAdd("Network Error: " + std::string(curl_easy_strerror(res))); + return GJ_REQUEST_FAILED; + } + + (*psOutput) = sToFile; + } + else + { + // append session to multi handle + curl_multi_add_handle(m_pMultiHandle, pSession); + curl_multi_perform(m_pMultiHandle, &m_iNumSessions); + + // create and save callback object + gjCallDownload<P,D>* pCall = new gjCallDownload<P,D>(pFile, sToFile, pSession, sInfo, GJ_NETWORK_PROCESS_FW); + pCall->AddOutput(GJ_NETWORK_OUTPUT_FW); + m_apCall.push_back(pCall); + } + } + else + { + gjAPI::ErrorLogAdd("File Error: cannot write file <" + sToFile + ">"); + curl_easy_cleanup(pSession); + return GJ_FILE_ERROR; + } + } + else + { + gjAPI::ErrorLogAdd("Network Error: cannot establish curl session"); + return GJ_NETWORK_ERROR; + } + + return GJ_OK; +} + + +#endif /* _GJ_GUARD_NETWORK_HPP_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjScore.cpp b/test_data/lots_of_files/gjScore.cpp new file mode 100644 index 0000000..96f23bc --- /dev/null +++ b/test_data/lots_of_files/gjScore.cpp @@ -0,0 +1,200 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#include "gjAPI.h" + +#include <algorithm> +gjScoreTable* gjScoreTable::s_pPrimary = NULL; + + +// **************************************************************** +/* constructor */ +gjScoreTable::gjScoreTable(const gjData& aScoreTableData, gjAPI* pAPI)noexcept +: m_iID (atoi(SAFE_MAP_GET(aScoreTableData, "id").c_str())) +, m_sTitle (SAFE_MAP_GET(aScoreTableData, "name")) +, m_sDescription (SAFE_MAP_GET(aScoreTableData, "description")) +, m_iSortDir (0) +, m_pAPI (pAPI) +{ + // set the primary status + m_bPrimary = (SAFE_MAP_GET(aScoreTableData, "primary") == "1") ? true : false; + + // set pointer to the primary score table + if(m_bPrimary && !s_pPrimary) s_pPrimary = this; + + // reserve some memory + m_apScore.reserve(GJ_API_RESERVE_SCORE); + m_apVerify.reserve(4); +} + + +// **************************************************************** +/* destructor */ +gjScoreTable::~gjScoreTable() +{ + // reset pointer to the primary score table + if(s_pPrimary == this) s_pPrimary = NULL; + + // delete score entries + FOR_EACH(it, m_apScore) + SAFE_DELETE(*it) + FOR_EACH(it, m_apVerify) + SAFE_DELETE(*it) + + // clear container + m_apScore.clear(); + m_apVerify.clear(); +} + + +// **************************************************************** +/* check for cached score entries */ +int gjScoreTable::__CheckCache(const bool bOnlyUser, const int& iLimit, gjScoreList* papOutput) +{ + if(!bOnlyUser) return GJ_INVALID_INPUT; + if(!m_iSortDir) return GJ_INVALID_CALL; + + // fetch cached main user + gjUserPtr pMainUser; + if(m_pAPI->InterUser()->CheckCache(m_pAPI->GetUserName(), &pMainUser) != GJ_OK) + return GJ_REQUEST_CANCELED; + + if(papOutput) + { + // retrieve cached main user score entries + int iCount = 0; + FOR_EACH(it, m_apScore) + { + gjScore* pScore = (*it); + + if(pScore->GetUserID() == pMainUser->GetID()) + { + (*papOutput).push_back(pScore); + if(++iCount >= iLimit) break; + } + } + return iCount ? GJ_OK : GJ_NO_DATA_FOUND; + } + + return GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* process score data and cache score entries */ +int gjScoreTable::__Process(const std::string& sData, void* pAdd, gjScoreList* papOutput) +{ + // parse output + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse scores"); + return GJ_REQUEST_FAILED; + } + + // create score entries + FOR_EACH(it, aaReturn) + { + gjScore* pNewScore = new gjScore(*it, this, m_pAPI); + bool bNew = true; + + // remove invalid score + if(pNewScore->GetUserName() == "") + { + SAFE_DELETE(pNewScore) + continue; + } + + // check if specific score entry is already available + FOR_EACH(it, m_apScore) + { + gjScore* pOldScore = (*it); + + if(pOldScore->GetUserName() == pNewScore->GetUserName() && + pOldScore->GetSort() == pNewScore->GetSort()) + { + // specific score entry already available + SAFE_DELETE(pNewScore) + pNewScore = pOldScore; + bNew = false; + break; + } + } + + if(bNew) m_apScore.push_back(pNewScore); + if(papOutput) (*papOutput).push_back(pNewScore); + } + + // try to determine the sort direction + if(!m_iSortDir) + { + if(aaReturn.size() >= 2) + { + if(std::atoi(SAFE_MAP_GET(aaReturn[0], "sort").c_str()) > std::atoi(SAFE_MAP_GET(aaReturn[1], "sort").c_str())) m_iSortDir = GJ_SORT_DESC; + if(std::atoi(SAFE_MAP_GET(aaReturn[0], "sort").c_str()) < std::atoi(SAFE_MAP_GET(aaReturn[1], "sort").c_str())) m_iSortDir = GJ_SORT_ASC; + } + } + + // sort score entries + if(m_iSortDir) std::sort(m_apScore.begin(), m_apScore.end(), (m_iSortDir == GJ_SORT_DESC) ? SortDescending : SortAscending); + + return papOutput->size() ? GJ_OK : GJ_NO_DATA_FOUND; +} + + +// **************************************************************** +/* callback for successfully adding a score entry */ +int gjScoreTable::__AddScoreCallback(const std::string& sData, void* pAdd, gjScorePtr* pOutput) +{ + gjScore* pNewScore = static_cast<gjScore*>(pAdd); + + // remove score entry from verification list + FOR_EACH(it, m_apVerify) + { + if((*it) == pNewScore) + { + m_apVerify.erase(it); + break; + } + } + + // cache score entry + m_apScore.push_back(pNewScore); + + // sort score entries + if(m_iSortDir) std::sort(m_apScore.begin(), m_apScore.end(), (m_iSortDir == GJ_SORT_DESC) ? SortDescending : SortAscending); + + if(pOutput) (*pOutput) = pNewScore; + return GJ_OK; +} + + +// **************************************************************** +/* constructor */ +gjScore::gjScore(const gjData& sScoreData, gjScoreTable* pScoreTable, gjAPI* pAPI)noexcept +: m_sScore (SAFE_MAP_GET(sScoreData, "score")) +, m_iSort (atoi(SAFE_MAP_GET(sScoreData, "sort").c_str())) +, m_sExtraData (SAFE_MAP_GET(sScoreData, "extra_data")) +, m_sDate (SAFE_MAP_GET(sScoreData, "stored")) +, m_pScoreTable (pScoreTable) +, m_pAPI (pAPI) +{ + // check for guest + const std::string sGuest = SAFE_MAP_GET(sScoreData, "guest"); + const bool bGuest = (sGuest == "") ? false : true; + + // set user ID and name + m_iUserID = bGuest ? -1 : atoi(SAFE_MAP_GET(sScoreData, "user_id").c_str()); + m_sUserName = bGuest ? sGuest : SAFE_MAP_GET(sScoreData, "user"); +} + + +// **************************************************************** +/* callbacks for sorting the score table */ +bool SortAscending(const gjScore* i, const gjScore* j) {return (i->GetSort() < j->GetSort());} +bool SortDescending(const gjScore* i, const gjScore* j) {return (i->GetSort() > j->GetSort());} \ No newline at end of file diff --git a/test_data/lots_of_files/gjScore.h b/test_data/lots_of_files/gjScore.h new file mode 100644 index 0000000..16e91da --- /dev/null +++ b/test_data/lots_of_files/gjScore.h @@ -0,0 +1,352 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_SCORE_H_ +#define _GJ_GUARD_SCORE_H_ + + +// **************************************************************** +/*! Score table object class.\n + * http://gamejolt.com/api/doc/game/scores/ + * \brief Score Table Object */ +class gjScoreTable final +{ +private: + int m_iID; //!< ID of the score table + std::string m_sTitle; //!< title/name of the score table + std::string m_sDescription; //!< description text of the score table + + int m_iSortDir; //!< sort direction of the score table (see #GJ_SORT_DIRECTION) + + bool m_bPrimary; //!< primary status + static gjScoreTable* s_pPrimary; //!< pointer to the primary score table + + std::vector<gjScore*> m_apScore; //!< semi-cached score entries + std::vector<gjScore*> m_apVerify; //!< verification list with temporary score entries (helper) + + gjAPI* m_pAPI; //!< main interface access pointer + + +public: + gjScoreTable(const gjData& aScoreTableData, gjAPI* pAPI)noexcept; + ~gjScoreTable(); + + /*! \name Fetch Scores Request */ + //! @{ + /*! Fetch and semi-cache specific score entries of this score table through an API request. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param bOnlyUser Fetch only score entries from the current main user (Login required) + * \param iLimit Max number of fetched score entries + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * **GJ_NO_DATA_FOUND** if no scores were found\n + * (see #GJ_ERROR) */ + inline int FetchScoresNow(const bool& bOnlyUser, const int& iLimit, gjScoreList* papOutput) {if(!papOutput) return GJ_INVALID_INPUT; return this->__FetchScores(bOnlyUser, iLimit, papOutput, GJ_NETWORK_NULL_API(gjScoreList));} + template <typename T> inline int FetchScoresCall(const bool& bOnlyUser, const int& iLimit, GJ_NETWORK_OUTPUT(gjScoreList)) {return this->__FetchScores(bOnlyUser, iLimit, NULL, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Add Score Request */ + //! @{ + /*! Add a new score entry to this score table through an API request.\n + * May does nothing, if score entry was already added (compares name and sort).\n + * May does nothing, if main user has a better highscore. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param sScore Score string + * \param iSort Numerical sort value of the score + * \param sExtraData Extra data associated with the score + * \param sGuestName Name of a guest user (leave empty to use the current main user, Login required) + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_REQUEST_CANCELED** if score entry was already added or if main user has a better highscore\n + * **GJ_INVALID_INPUT** if score string is empty or sort value is zero\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int AddScoreNow(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName) {return this->__AddScore(sScore, iSort, sExtraData, sGuestName, true, GJ_NETWORK_NULL_API(gjScorePtr));} + inline int AddScoreCall(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName) {return this->__AddScore(sScore, iSort, sExtraData, sGuestName, false, GJ_NETWORK_NULL_API(gjScorePtr));} + template <typename T> inline int AddScoreCall(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName, GJ_NETWORK_OUTPUT(gjScorePtr)) {return this->__AddScore(sScore, iSort, sExtraData, sGuestName, false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Add Score Request Base64 */ + //! @{ + /*! Like \link AddScoreNow AddScore\endlink\n + * Allows to send extra data in binary form. + * \pre Login maybe required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param sScore Score string + * \param iSort Numerical sort value of the score + * \param pExtraData Extra data in binary form associated with the score + * \param iExtraSize Size of the extra data + * \param sGuestName Name of a guest user (leave empty to use the current main user, Login required) + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_REQUEST_CANCELED** if score entry was already added or if main user has a better highscore\n + * **GJ_INVALID_INPUT** if score string is empty or sort value is zero\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int AddScoreBase64Now(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName) {return this->__AddScoreBase64(sScore, iSort, pExtraData, iExtraSize, sGuestName, true, GJ_NETWORK_NULL_API(gjScorePtr));} + inline int AddScoreBase64Call(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName) {return this->__AddScoreBase64(sScore, iSort, pExtraData, iExtraSize, sGuestName, false, GJ_NETWORK_NULL_API(gjScorePtr));} + template <typename T> inline int AddScoreBase64Call(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName, GJ_NETWORK_OUTPUT(gjScorePtr)) {return this->__AddScoreBase64(sScore, iSort, pExtraData, iExtraSize, sGuestName, false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Get Attributes */ + //! @{ + inline const int& GetID()const {return m_iID;} //!< \copybrief m_iID + inline const std::string& GetTitle()const {return m_sTitle;} //!< \copybrief m_sTitle + inline const std::string& GetDescription()const {return m_sDescription;} //!< \copybrief m_sDescription + inline const int& GetSortDirection()const {return m_iSortDir;} //!< \copybrief m_iSortDir + /*! */ //! @} + + /*! \name Get Static Attributes */ + //! @{ + static inline gjScoreTable* GetPrimary() {return s_pPrimary;} //!< \copybrief s_pPrimary + /*! */ //! @} + + /*! \name Check Status */ + //! @{ + inline bool IsPrimary()const {return m_bPrimary;} //!< \copybrief m_bPrimary + //! @} + + +private: + DISABLE_COPY(gjScoreTable) + + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __FetchScores(const bool& bOnlyUser, const int& iLimit, gjScoreList* papOutput, GJ_NETWORK_OUTPUT(gjScoreList)); + template <typename T> int __AddScore(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr)); + template <typename T> int __AddScoreBase64(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr)); + //! @} + + /*! \name Management Functions */ + //! @{ + int __CheckCache(const bool bOnlyUser, const int& iLimit, gjScoreList* papOutput); + int __Process(const std::string& sData, void* pAdd, gjScoreList* papOutput); + //! @} + + /*! \name Callback Functions */ + //! @{ + int __AddScoreCallback(const std::string& sData, void* pAdd, gjScorePtr* pOutput); + //! @} +}; + + +// **************************************************************** +/*! Score entry class.\n + * http://gamejolt.com/api/doc/game/scores/ + * \brief Score Entry Object */ +class gjScore final +{ +private: + std::string m_sScore; //!< score string + int m_iSort; //!< numerical sort value of the score + std::string m_sExtraData; //!< extra data associated with the score + + int m_iUserID; //!< ID of the user, 0 if guest + std::string m_sUserName; //!< display name of the user or guest + + std::string m_sDate; //!< time string when the score was logged (e.g. 4 weeks ago) + + gjScoreTable* m_pScoreTable; //!< associated score table + + gjAPI* m_pAPI; //!< main interface access pointer + + +public: + gjScore(const gjData& aScoreData, gjScoreTable* pScoreTable, gjAPI* pAPI)noexcept; + + /*! \name Fetch User Request */ + //! @{ + /*! Fetch and cache the associated user through an API request. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int FetchUserNow(gjUserPtr* ppOutput) {return m_pAPI->InterUser()->FetchUserNow(this->IsGuest() ? -1 : m_iUserID, ppOutput);} + template <typename T> inline int FetchUserCall(GJ_NETWORK_OUTPUT(gjUserPtr)) {return m_pAPI->InterUser()->FetchUserCall(this->IsGuest() ? -1 : m_iUserID, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Get Attributes */ + //! @{ + inline const std::string& GetScore()const {return m_sScore;} //!< \copybrief m_sScore + inline const int& GetSort()const {return m_iSort;} //!< \copybrief m_iSort + inline const std::string& GetExtraData()const {return m_sExtraData;} //!< \copybrief m_sExtraData + inline const int& GetUserID()const {return m_iUserID;} //!< \copybrief m_iUserID + inline const std::string& GetUserName()const {return m_sUserName;} //!< \copybrief m_sUserName + inline const std::string& GetDate()const {return m_sDate;} //!< \copybrief m_sDate + inline gjScoreTable* GetScoreTable()const {return m_pScoreTable;} //!< \copybrief m_pScoreTable + /*! */ //! @} + + /*! \name Get Base64 Attributes */ + //! @{ + inline int GetExtraDataBase64(void* pTarget, const size_t& iSize)const {if(!pTarget || iSize <= 0) return GJ_INVALID_INPUT; base64_decode(m_sExtraData.c_str(), (unsigned char*)pTarget, iSize); return GJ_OK;} //!< \copybrief m_sExtraData + /*! */ //! @} + + /*! \name Check Status */ + //! @{ + inline bool IsGuest()const {return m_iUserID ? false : true;} //!< guest status + //! @} + + +private: + DISABLE_COPY(gjScore) +}; + + +// **************************************************************** +/*! \name Sorting Callbacks */ +//! @{ +bool SortAscending(const gjScore* i, const gjScore* j); +bool SortDescending(const gjScore* i, const gjScore* j); +//! @} + + +// **************************************************************** +/* fetch and semi-cache specific score entries of this score table */ +template <typename T> int gjScoreTable::__FetchScores(const bool& bOnlyUser, const int& iLimit, gjScoreList* papOutput, GJ_NETWORK_OUTPUT(gjScoreList)) +{ + if(!m_pAPI->IsUserConnected() && bOnlyUser) return GJ_NOT_CONNECTED; + if(iLimit <= 0) return GJ_INVALID_INPUT; + + const bool bNow = papOutput ? true : false; + + if(bOnlyUser && m_iSortDir) + { + // check for cached score entries + gjScoreList apCache; + if(this->__CheckCache(bOnlyUser, iLimit, &apCache) == GJ_OK) + { + if(bNow) (*papOutput) = apCache; + else (pOutputObj->*OutputCallback)(apCache, pOutputData); + return GJ_OK; + } + } + + // only scores from the main user or all scores + const std::string sUserData = bOnlyUser ? + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() : + ""; + + // send get score request + std::string sResponse; + if(m_pAPI->SendRequest("/scores/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&table_id=" + gjAPI::UtilIntToString(m_iID) + + "&limit=" + gjAPI::UtilIntToString(iLimit) + + sUserData, bNow ? &sResponse : NULL, this, &gjScoreTable::__Process, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__Process(sResponse, NULL, papOutput); + return GJ_OK; +} + + +// **************************************************************** +/* add score entry to this score table */ +template <typename T> int gjScoreTable::__AddScore(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr)) +{ + if(sScore == "" || iSort == 0) return GJ_INVALID_INPUT; + + const bool bGuest = (sGuestName != "") ? true : false; + if(!m_pAPI->IsUserConnected() && !bGuest) return GJ_NOT_CONNECTED; + + if(m_iSortDir && !bGuest) + { + // check for cached best score and cancel request + gjScoreList apBestScore; + if(this->__CheckCache(true, 1, &apBestScore) == GJ_OK) + { + if(((m_iSortDir == GJ_SORT_DESC) && (iSort <= apBestScore[0]->GetSort())) || + ((m_iSortDir == GJ_SORT_ASC) && (iSort >= apBestScore[0]->GetSort()))) + return GJ_REQUEST_CANCELED; + } + } + + // check if specific score entry is already available and cancel request + const std::string sUserName = bGuest ? sGuestName : m_pAPI->GetUserName(); + FOR_EACH(it, m_apScore) + { + gjScore* pScore = (*it); + + if((pScore->GetUserID() == -1) == bGuest && + pScore->GetUserName() == sUserName && + pScore->GetSort() == iSort) + { + // specific score entry already available + return GJ_REQUEST_CANCELED; + } + } + + // create temporary score entry + gjData asScoreData; + if(!bGuest) + { + // fetch cached main user + gjUserPtr pMainUser; + const int iError = m_pAPI->InterUser()->FetchUserNow(0, &pMainUser); + if(iError) return iError; + + asScoreData["user_id"] = gjAPI::UtilIntToString(pMainUser->GetID()); + asScoreData["user"] = pMainUser->GetName(); + } + asScoreData["score"] = sScore; + asScoreData["sort"] = gjAPI::UtilIntToString(iSort); + asScoreData["extra_data"] = sExtraData; + asScoreData["stored"] = GJ_API_TEXT_NOW; + asScoreData["guest"] = sGuestName; + + // add score entry to verification list + gjScore* pVerify = new gjScore(asScoreData, this, m_pAPI); + m_apVerify.push_back(pVerify); + + // use user data or guest name + const std::string sUserData = bGuest ? + "&guest=" + gjAPI::UtilEscapeString(sGuestName) : + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken(); + + // send add score request + std::string sResponse; + if(m_pAPI->SendRequest("/scores/add/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&table_id=" + gjAPI::UtilIntToString(m_iID) + + "&score=" + gjAPI::UtilEscapeString(sScore) + + "&sort=" + gjAPI::UtilIntToString(iSort) + + "&extra_data=" + gjAPI::UtilEscapeString(sExtraData) + + sUserData, bNow ? &sResponse : NULL, this, &gjScoreTable::__AddScoreCallback, pVerify, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__AddScoreCallback(sResponse, pVerify, NULL); + return GJ_OK; +} + + +// **************************************************************** +/* add score entry to this score table with Base64 extra data */ +template <typename T> int gjScoreTable::__AddScoreBase64(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr)) +{ + if(!pExtraData || iExtraSize <= 0) return GJ_INVALID_INPUT; + + const size_t iNeed = base64_needed(iExtraSize); + + // convert binary data to Base64 string + char* pcBase64 = new char[iNeed]; + base64_encode((unsigned char*)pExtraData, iExtraSize, pcBase64, iNeed); + + // execute add score function with Base64 string + const int iReturn = this->__AddScore(sScore, iSort, pcBase64, sGuestName, bNow, GJ_NETWORK_OUTPUT_FW); + SAFE_DELETE_ARRAY(pcBase64) + + return iReturn; +} + + +#endif /* _GJ_GUARD_SCORE_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjTrophy.cpp b/test_data/lots_of_files/gjTrophy.cpp new file mode 100644 index 0000000..f67f2a8 --- /dev/null +++ b/test_data/lots_of_files/gjTrophy.cpp @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#include "gjAPI.h" + + +// **************************************************************** +/* constructor */ +gjTrophy::gjTrophy(const gjData& aTrophyData, gjAPI* pAPI)noexcept +: m_iID (atoi(SAFE_MAP_GET(aTrophyData, "id").c_str())) +, m_sTitle (SAFE_MAP_GET(aTrophyData, "title")) +, m_sDescription (SAFE_MAP_GET(aTrophyData, "description")) +, m_sDifficulty (SAFE_MAP_GET(aTrophyData, "difficulty")) +, m_sImageURL (SAFE_MAP_GET(aTrophyData, "image_url")) +, m_iSort (0) +, m_bSecret (false) +, m_pAPI (pAPI) +{ + // set difficulty value for easier access + if(m_sDifficulty == "Bronze") m_iDifficultyValue = 1; + else if(m_sDifficulty == "Silver") m_iDifficultyValue = 2; + else if(m_sDifficulty == "Gold") m_iDifficultyValue = 3; + else if(m_sDifficulty == "Platinum") m_iDifficultyValue = 4; + else m_iDifficultyValue = 0; + + // set the achieved date + const std::string sAchievedDate = SAFE_MAP_GET(aTrophyData, "achieved"); + m_sAchievedDate = (sAchievedDate == "false" || sAchievedDate == "" || !m_pAPI->IsUserConnected()) ? "" : sAchievedDate; +} + +gjTrophy::gjTrophy(const gjTrophy& that)noexcept +: m_iID (that.m_iID) +, m_sTitle (that.m_sTitle) +, m_sDescription (that.m_sDescription) +, m_sDifficulty (that.m_sDifficulty) +, m_iDifficultyValue (that.m_iDifficultyValue) +, m_sImageURL (that.m_sImageURL) +, m_sAchievedDate (that.m_sAchievedDate) +, m_iSort (that.m_iSort) +, m_bSecret (that.m_bSecret) +, m_pAPI (that.m_pAPI) +{ +} + + +// **************************************************************** +/* assignment operator */ +gjTrophy& gjTrophy::operator = (const gjTrophy& that)noexcept +{ + m_iID = that.m_iID; + m_sTitle = that.m_sTitle; + m_sDescription = that.m_sDescription; + m_sDifficulty = that.m_sDifficulty; + m_iDifficultyValue = that.m_iDifficultyValue; + m_sImageURL = that.m_sImageURL; + m_sAchievedDate = that.m_sAchievedDate; + m_iSort = that.m_iSort; + m_bSecret = that.m_bSecret; + m_pAPI = that.m_pAPI; + return *this; +} + + +// **************************************************************** +/* callback for updating the data */ +int gjTrophy::__UpdateDataCallback(const std::string& sData, void* pAdd, gjTrophyPtr* ppOutput) +{ + // parse output + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse trophy"); + return GJ_REQUEST_FAILED; + } + + const int iSort = m_iSort; + const bool bSecret = m_bSecret; + + // copy new trophy over old trophy + gjTrophy NewTrophy(aaReturn[0], m_pAPI); + *this = NewTrophy; + + m_iSort = iSort; + m_bSecret = bSecret; + + if(ppOutput) (*ppOutput) = this; + return GJ_OK; +} + + +// **************************************************************** +/* callback for achieving the trophy */ +int gjTrophy::__AchieveCallback(const std::string& sData, void* pAdd, gjTrophyPtr* ppOutput) +{ + // parse output (only check status) + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse trophy"); + return GJ_REQUEST_FAILED; + } + + // set time string + m_sAchievedDate = GJ_API_TEXT_NOW; + + if(ppOutput) (*ppOutput) = this; + return GJ_OK; +} \ No newline at end of file diff --git a/test_data/lots_of_files/gjTrophy.h b/test_data/lots_of_files/gjTrophy.h new file mode 100644 index 0000000..5086c29 --- /dev/null +++ b/test_data/lots_of_files/gjTrophy.h @@ -0,0 +1,185 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_TROPHY_H_ +#define _GJ_GUARD_TROPHY_H_ + + +// **************************************************************** +/*! Trophy object class.\n + * http://gamejolt.com/api/doc/game/trophies/ + * \brief Trophy Object */ +class gjTrophy final +{ +private: + int m_iID; //!< ID of the trophy + std::string m_sTitle; //!< title/name of the trophy + std::string m_sDescription; //!< description text of the trophy + + std::string m_sDifficulty; //!< difficulty of the trophy ("Bronze", "Silver", "Gold", "Platinum") + int m_iDifficultyValue; //!< difficulty as numerical value for easier access + + std::string m_sImageURL; //!< URL of the thumbnail + std::string m_sAchievedDate; //!< time string when the trophy was achieved (e.g. 4 weeks ago) + + int m_iSort; //!< logical sort value within the full trophy list (0 = not sorted) + bool m_bSecret; //!< secret trophy (may return different thumbnail and description) + + gjAPI* m_pAPI; //!< main interface access pointer + + +public: + gjTrophy(const gjData& aTrophyData, gjAPI* pAPI)noexcept; + + /*! \name Update Data Request */ + //! @{ + /*! Update data of this trophy through an API request. + * \pre Login required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int UpdateDataNow() {return this->__UpdateData(true, GJ_NETWORK_NULL_API(gjTrophyPtr));} + inline int UpdateDataCall() {return this->__UpdateData(false, GJ_NETWORK_NULL_API(gjTrophyPtr));} + template <typename T> inline int UpdateDataCall(GJ_NETWORK_OUTPUT(gjTrophyPtr)) {return this->__UpdateData(false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Achieve Request */ + //! @{ + /*! Achieve this trophy through an API request.\n + * Does nothing, if trophy was already achieved. + * \pre Login required + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_REQUEST_CANCELED** if trophy was already achieved\n + * **GJ_NOT_CONNECTED** if connection/login is missing\n + * (see #GJ_ERROR) */ + inline int AchieveNow() {return this->__Achieve(true, GJ_NETWORK_NULL_API(gjTrophyPtr));} + inline int AchieveCall() {return this->__Achieve(false, GJ_NETWORK_NULL_API(gjTrophyPtr));} + template <typename T> inline int AchieveCall(GJ_NETWORK_OUTPUT(gjTrophyPtr)) {return this->__Achieve(false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Download Thumbnail */ + //! @{ + /*! Download the thumbnail of this trophy.\n + * Receive the cached local path of the file. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param sToFolder Relative path of the download target folder + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_INPUT** if target folder string is empty\n + * (see #GJ_ERROR) */ + inline int DownloadThumbnailNow(const std::string& sToFolder, std::string* psOutput) {return m_pAPI->InterFile()->DownloadFileNow(this->GetImageURL(), sToFolder, "", psOutput);} + template <typename T> inline int DownloadThumbnailCall(const std::string& sToFolder, GJ_NETWORK_OUTPUT(std::string)) {return m_pAPI->InterFile()->DownloadFileCall(this->GetImageURL(), sToFolder, "", GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Get Attributes */ + //! @{ + inline const int& GetID()const {return m_iID;} //!< \copybrief m_iID + inline const std::string& GetTitle()const {return m_sTitle;} //!< \copybrief m_sTitle + inline const std::string& GetDescriptionTrue()const {return m_sDescription;} //!< \copybrief m_sDescription + inline const std::string& GetDifficulty()const {return m_sDifficulty;} //!< \copybrief m_sDifficulty + inline const int& GetDifficultyValue()const {return m_iDifficultyValue;} //!< \copybrief m_iDifficultyValue + inline const std::string& GetImageURLTrue()const {return m_sImageURL;} //!< \copybrief m_sImageURL + inline const std::string& GetAchievedDate()const {return m_sAchievedDate;} //!< \copybrief m_sAchievedDate + inline const int& GetSort()const {return m_iSort;} //!< \copybrief m_iSort + /*! */ //! @} + + /*! \name Get Secret Modified Attributes */ + //! @{ + inline const std::string GetDescription()const {return (m_bSecret && !this->IsAchieved()) ? GJ_API_TEXT_SECRET : m_sDescription;} //!< \copybrief m_sDescription + inline const std::string GetImageURL()const {return (m_bSecret && !this->IsAchieved()) ? GJ_API_TROPHY_SECRET : m_sImageURL;} //!< \copybrief m_sImageURL + /*! */ //! @} + + /*! \name Check Status */ + //! @{ + inline bool IsAchieved()const {return m_sAchievedDate.empty() ? false : true;} + inline const bool& IsSecret()const {return m_bSecret;} //!< \copybrief m_bSecret + //! @} + + +private: + /*! \name Hide Copy */ + //! @{ + gjTrophy(const gjTrophy& that)noexcept; + gjTrophy& operator = (const gjTrophy& that)noexcept; + friend class gjAPI; + //! @} + + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __UpdateData(const bool& bNow, GJ_NETWORK_OUTPUT(gjTrophyPtr)); + template <typename T> int __Achieve(const bool& bNow, GJ_NETWORK_OUTPUT(gjTrophyPtr)); + //! @} + + /*! \name Callback Functions */ + //! @{ + int __UpdateDataCallback(const std::string& sData, void* pAdd, gjTrophyPtr* ppOutput); + int __AchieveCallback(const std::string& sData, void* pAdd, gjTrophyPtr* ppOutput); + //! @} + + /*! \name Set Attributes */ + //! @{ + inline void __SetSort(const int& iSort) {m_iSort = iSort;} + inline void __SetSecret(const bool& bSecret) {m_bSecret = bSecret;} + //! @} +}; + + +// **************************************************************** +/* update data of this trophy */ +template <typename T> int gjTrophy::__UpdateData(const bool& bNow, GJ_NETWORK_OUTPUT(gjTrophyPtr)) +{ + if(!m_pAPI->IsUserConnected()) return GJ_NOT_CONNECTED; + + // send get trophies request + std::string sResponse; + if(m_pAPI->SendRequest("/trophies/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() + + "&trophy_id=" + gjAPI::UtilIntToString(m_iID), + bNow ? &sResponse : NULL, this, &gjTrophy::__UpdateDataCallback, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__UpdateDataCallback(sResponse, NULL, NULL); + return GJ_OK; +} + + +// **************************************************************** +/* achieve this trophy */ +template <typename T> int gjTrophy::__Achieve(const bool& bNow, GJ_NETWORK_OUTPUT(gjTrophyPtr)) +{ + // cancel request if already achieved + if(this->IsAchieved()) return GJ_REQUEST_CANCELED; + + // achieve offline-cached trophy + if(!m_pAPI->IsUserConnected()) + { + m_sAchievedDate = GJ_API_TEXT_NOW; + return GJ_NOT_CONNECTED; + } + + // send achieve trophy request + std::string sResponse; + if(m_pAPI->SendRequest("/trophies/add-achieved/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&username=" + m_pAPI->GetProcUserName() + + "&user_token=" + m_pAPI->GetProcUserToken() + + "&trophy_id=" + gjAPI::UtilIntToString(m_iID), + bNow ? &sResponse : NULL, this, &gjTrophy::__AchieveCallback, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__AchieveCallback(sResponse, NULL, NULL); + return GJ_OK; +} + + +#endif /* _GJ_GUARD_TROPHY_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gjUser.cpp b/test_data/lots_of_files/gjUser.cpp new file mode 100644 index 0000000..f6211d6 --- /dev/null +++ b/test_data/lots_of_files/gjUser.cpp @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#include "gjAPI.h" + + +// **************************************************************** +/* constructor */ +gjUser::gjUser(const gjData& aUserData, gjAPI* pAPI)noexcept +: m_iID (atoi(SAFE_MAP_GET(aUserData, "id").c_str())) +, m_sName (SAFE_MAP_GET(aUserData, "username")) +, m_sType (SAFE_MAP_GET(aUserData, "type")) +, m_sAvatarURL (SAFE_MAP_GET(aUserData, "avatar_url")) +, m_sSignedUp (SAFE_MAP_GET(aUserData, "signed_up")) +, m_sLastLoggedIn (SAFE_MAP_GET(aUserData, "last_logged_in")) +, m_sStatus (SAFE_MAP_GET(aUserData, "status")) +, m_sDeveloperName (SAFE_MAP_GET(aUserData, "developer_name")) +, m_sDeveloperWebsite (SAFE_MAP_GET(aUserData, "developer_website")) +, m_sDeveloperDescription (SAFE_MAP_GET(aUserData, "developer_description")) +, m_pAPI (pAPI) +{ +} + +gjUser::gjUser(const gjUser& that)noexcept +: m_iID (that.m_iID) +, m_sName (that.m_sName) +, m_sType (that.m_sType) +, m_sAvatarURL (that.m_sAvatarURL) +, m_sSignedUp (that.m_sSignedUp) +, m_sLastLoggedIn (that.m_sLastLoggedIn) +, m_sStatus (that.m_sStatus) +, m_sDeveloperName (that.m_sDeveloperName) +, m_sDeveloperWebsite (that.m_sDeveloperWebsite) +, m_sDeveloperDescription (that.m_sDeveloperDescription) +, m_pAPI (that.m_pAPI) +{ +} + + +// **************************************************************** +/* assignment operator */ +gjUser& gjUser::operator = (const gjUser& that)noexcept +{ + m_iID = that.m_iID; + m_sName = that.m_sName; + m_sType = that.m_sType; + m_sAvatarURL = that.m_sAvatarURL; + m_sSignedUp = that.m_sSignedUp; + m_sLastLoggedIn = that.m_sLastLoggedIn; + m_sStatus = that.m_sStatus; + m_sDeveloperName = that.m_sDeveloperName; + m_sDeveloperWebsite = that.m_sDeveloperWebsite; + m_sDeveloperDescription = that.m_sDeveloperDescription; + m_pAPI = that.m_pAPI; + return *this; +} + + +// **************************************************************** +/* callback for updating the data */ +int gjUser::__UpdateDataCallback(const std::string& sData, void* pAdd, gjUserPtr* pOutput) +{ + // parse output + gjDataList aaReturn; + if(m_pAPI->ParseRequestKeypair(sData, &aaReturn) != GJ_OK) + { + gjAPI::ErrorLogAdd("API Error: could not parse user"); + return GJ_REQUEST_FAILED; + } + + // copy new user over old user + gjUser NewUser(aaReturn[0], m_pAPI); + *this = NewUser; + + if(pOutput) (*pOutput) = this; + return GJ_OK; +} \ No newline at end of file diff --git a/test_data/lots_of_files/gjUser.h b/test_data/lots_of_files/gjUser.h new file mode 100644 index 0000000..c2f17fd --- /dev/null +++ b/test_data/lots_of_files/gjUser.h @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////// +//*-------------------------------------------------------------*// +//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |// +//*-------------------------------------------------------------*// +//| Released under the zlib License |// +//| More information available in the readme file |// +//*-------------------------------------------------------------*// +/////////////////////////////////////////////////////////////////// +#pragma once +#ifndef _GJ_GUARD_USER_H_ +#define _GJ_GUARD_USER_H_ + + +// **************************************************************** +/*! User object class.\n + * http://gamejolt.com/api/doc/game/users/ + * \brief User Object */ +class gjUser final +{ +private: + int m_iID; //!< ID of the user (-1 = guest) + std::string m_sName; //!< display name of the user + + std::string m_sType; //!< type of the user ("Guest", "User", "Developer", "Moderator", "Administrator") + + std::string m_sAvatarURL; //!< URL of the avatar + + std::string m_sSignedUp; //!< time string when the user signed up (e.g. 4 weeks ago) + std::string m_sLastLoggedIn; //!< time string of the last login (e.g. 4 weeks ago) + std::string m_sStatus; //!< status of the user ("Active", "Banned") + + std::string m_sDeveloperName; //!< name of a developer user + std::string m_sDeveloperWebsite; //!< website of a developer user + std::string m_sDeveloperDescription; //!< description of a developer user + + gjAPI* m_pAPI; //!< main interface access pointer + + +public: + gjUser(const gjData& aUserData, gjAPI* pAPI)noexcept; + + /*! \name Update Data Request */ + //! @{ + /*! Update data of this user through an API request. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_CALL** if this is a guest users\n + * (see #GJ_ERROR) */ + inline int UpdateDataNow() {return this->__UpdateData(true, GJ_NETWORK_NULL_API(gjUserPtr));} + inline int UpdateDataCall() {return this->__UpdateData(false, GJ_NETWORK_NULL_API(gjUserPtr));} + template <typename T> inline int UpdateDataCall(GJ_NETWORK_OUTPUT(gjUserPtr)) {return this->__UpdateData(false, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Download Avatar */ + //! @{ + /*! Download the avatar of this user.\n + * Receive the cached local path of the file. + * \note \b -Now blocks, \b -Call uses non-blocking callbacks + * \param sToFolder Relative path of the download target folder + * \return **GJ_OK** on success\n + * **GJ_REQUEST_FAILED** if request was unsuccessful\n + * **GJ_INVALID_INPUT** if target folder string is empty\n + * (see #GJ_ERROR) */ + inline int DownloadAvatarNow(const std::string& sToFolder, std::string* psOutput) {return m_pAPI->InterFile()->DownloadFileNow(this->GetAvatarURL(), sToFolder, m_sName + GJ_API_AVATAR_FORMAT, psOutput);} + template <typename T> inline int DownloadAvatarCall(const std::string& sToFolder, GJ_NETWORK_OUTPUT(std::string)) {return m_pAPI->InterFile()->DownloadFileCall(this->GetAvatarURL(), sToFolder, m_sName + GJ_API_AVATAR_FORMAT, GJ_NETWORK_OUTPUT_FW);} + //! @} + + /*! \name Get Attributes */ + //! @{ + inline const int& GetID()const {return m_iID;} //!< \copybrief m_iID + inline const std::string& GetName()const {return m_sName;} //!< \copybrief m_sName + inline const std::string& GetType()const {return m_sType;} //!< \copybrief m_sType + inline const std::string& GetAvatarURL()const {return m_sAvatarURL;} //!< \copybrief m_sAvatarURL + inline const std::string& GetSignedUp()const {return m_sSignedUp;} //!< \copybrief m_sSignedUp + inline const std::string& GetLastLoggedIn()const {return m_sLastLoggedIn;} //!< \copybrief m_sLastLoggedIn + inline const std::string& GetStatus()const {return m_sStatus;} //!< \copybrief m_sStatus + inline const std::string& GetDeveloperName()const {return m_sDeveloperName;} //!< \copybrief m_sDeveloperName + inline const std::string& GetDeveloperWebsite()const {return m_sDeveloperWebsite;} //!< \copybrief m_sDeveloperWebsite + inline const std::string& GetDeveloperDescription()const {return m_sDeveloperDescription;} //!< \copybrief m_sDeveloperDescription + /*! */ //! @} + + +private: + /*! \name Hide Copy */ + //! @{ + gjUser(const gjUser& that)noexcept; + gjUser& operator = (const gjUser& that)noexcept; + friend class gjAPI; + //! @} + + /*! \name Superior Request Functions */ + //! @{ + template <typename T> int __UpdateData(const bool& bNow, GJ_NETWORK_OUTPUT(gjUserPtr)); + //! @} + + /*! \name Callback Functions */ + //! @{ + int __UpdateDataCallback(const std::string& sData, void* pAdd, gjUserPtr* pOutput); + //! @} +}; + + +// **************************************************************** +/* update data of this user */ +template <typename T> int gjUser::__UpdateData(const bool& bNow, GJ_NETWORK_OUTPUT(gjUserPtr)) +{ + if(m_iID <= 0) return GJ_INVALID_CALL; + + // send get user request + std::string sResponse; + if(m_pAPI->SendRequest("/users/" + "?game_id=" + m_pAPI->GetProcGameID() + + "&user_id=" + gjAPI::UtilIntToString(m_iID), + bNow ? &sResponse : NULL, this, &gjUser::__UpdateDataCallback, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED; + + if(bNow) return this->__UpdateDataCallback(sResponse, NULL, NULL); + return GJ_OK; +} + + +#endif /* _GJ_GUARD_USER_H_ */ \ No newline at end of file diff --git a/test_data/lots_of_files/gpio.s b/test_data/lots_of_files/gpio.s new file mode 100644 index 0000000..27dcecc --- /dev/null +++ b/test_data/lots_of_files/gpio.s @@ -0,0 +1,118 @@ +/****************************************************************************** +* gpio.s +* by Alex Chadwick +* +* A sample assembly code implementation of the ok05 operating system. +* See main.s for details. +* +* gpio.s contains the rountines for manipulation of the GPIO ports. +******************************************************************************/ + +/* NEW +* According to the EABI, all method calls should use r0-r3 for passing +* parameters, should preserve registers r4-r8,r10-r11,sp between calls, and +* should return values in r0 (and r1 if needed). +* It does also stipulate many things about how methods should use the registers +* and stack during calls, but we're using hand coded assembly. All we need to +* do is obey the start and end conditions, and if all our methods do this, they +* would all work from C. +*/ + +/* NEW +* GetGpioAddress returns the base address of the GPIO region as a physical address +* in register r0. +* C++ Signature: void* GetGpioAddress() +*/ +.globl GetGpioAddress +GetGpioAddress: + gpioAddr .req r0 + ldr gpioAddr,=0x20200000 + mov pc,lr + .unreq gpioAddr + +/* NEW +* SetGpioFunction sets the function of the GPIO register addressed by r0 to the +* low 3 bits of r1. +* C++ Signature: void SetGpioFunction(u32 gpioRegister, u32 function) +*/ +.globl SetGpioFunction +SetGpioFunction: + pinNum .req r0 + pinFunc .req r1 + cmp pinNum,#53 + cmpls pinFunc,#7 + movhi pc,lr + + push {lr} + mov r2,pinNum + .unreq pinNum + pinNum .req r2 + bl GetGpioAddress + gpioAddr .req r0 + + functionLoop$: + cmp pinNum,#9 + subhi pinNum,#10 + addhi gpioAddr,#4 + bhi functionLoop$ + + add pinNum, pinNum,lsl #1 + lsl pinFunc,pinNum + + mask .req r3 + mov mask,#7 /* r3 = 111 in binary */ + lsl mask,pinNum /* r3 = 11100..00 where the 111 is in the same position as the function in r1 */ + .unreq pinNum + + mvn mask,mask /* r3 = 11..1100011..11 where the 000 is in the same poisiont as the function in r1 */ + oldFunc .req r2 + ldr oldFunc,[gpioAddr] /* r2 = existing code */ + and oldFunc,mask /* r2 = existing code with bits for this pin all 0 */ + .unreq mask + + orr pinFunc,oldFunc /* r1 = existing code with correct bits set */ + .unreq oldFunc + + str pinFunc,[gpioAddr] + .unreq pinFunc + .unreq gpioAddr + pop {pc} + +/* NEW +* SetGpio sets the GPIO pin addressed by register r0 high if r1 != 0 and low +* otherwise. +* C++ Signature: void SetGpio(u32 gpioRegister, u32 value) +*/ +.globl SetGpio +SetGpio: + pinNum .req r0 + pinVal .req r1 + + cmp pinNum,#53 + movhi pc,lr + push {lr} + mov r2,pinNum + .unreq pinNum + pinNum .req r2 + bl GetGpioAddress + gpioAddr .req r0 + + pinBank .req r3 + lsr pinBank,pinNum,#5 + lsl pinBank,#2 + add gpioAddr,pinBank + .unreq pinBank + + and pinNum,#31 + setBit .req r3 + mov setBit,#1 + lsl setBit,pinNum + .unreq pinNum + + teq pinVal,#0 + .unreq pinVal + streq setBit,[gpioAddr,#40] + strne setBit,[gpioAddr,#28] + .unreq setBit + .unreq gpioAddr + pop {pc} diff --git a/test_data/lots_of_files/handmade.cpp b/test_data/lots_of_files/handmade.cpp new file mode 100644 index 0000000..01a54c6 --- /dev/null +++ b/test_data/lots_of_files/handmade.cpp @@ -0,0 +1,1785 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#include "handmade.h" +#include "handmade_render_group.cpp" +#include "handmade_world.cpp" +#include "handmade_sim_region.cpp" +#include "handmade_entity.cpp" +#include "handmade_asset.cpp" +#include "handmade_audio.cpp" +#include "handmade_meta.cpp" + +struct add_low_entity_result +{ + low_entity *Low; + u32 LowIndex; +}; +internal add_low_entity_result +AddLowEntity(game_state *GameState, entity_type Type, world_position P) +{ + Assert(GameState->LowEntityCount < ArrayCount(GameState->LowEntities)); + uint32 EntityIndex = GameState->LowEntityCount++; + + low_entity *EntityLow = GameState->LowEntities + EntityIndex; + *EntityLow = {}; + EntityLow->Sim.Type = Type; + EntityLow->Sim.Collision = GameState->NullCollision; + EntityLow->P = NullPosition(); + + ChangeEntityLocation(&GameState->WorldArena, GameState->World, EntityIndex, EntityLow, P); + + add_low_entity_result Result; + Result.Low = EntityLow; + Result.LowIndex = EntityIndex; + + // TODO(casey): Do we need to have a begin/end paradigm for adding + // entities so that they can be brought into the high set when they + // are added and are in the camera region? + + return(Result); +} + +internal add_low_entity_result +AddGroundedEntity(game_state *GameState, entity_type Type, world_position P, + sim_entity_collision_volume_group *Collision) +{ + add_low_entity_result Entity = AddLowEntity(GameState, Type, P); + Entity.Low->Sim.Collision = Collision; + return(Entity); +} + +inline world_position +ChunkPositionFromTilePosition(world *World, int32 AbsTileX, int32 AbsTileY, int32 AbsTileZ, + v3 AdditionalOffset = V3(0, 0, 0)) +{ + world_position BasePos = {}; + + real32 TileSideInMeters = 1.4f; + real32 TileDepthInMeters = 3.0f; + + v3 TileDim = V3(TileSideInMeters, TileSideInMeters, TileDepthInMeters); + v3 Offset = Hadamard(TileDim, V3((real32)AbsTileX, (real32)AbsTileY, (real32)AbsTileZ)); + world_position Result = MapIntoChunkSpace(World, BasePos, AdditionalOffset + Offset); + + Assert(IsCanonical(World, Result.Offset_)); + + return(Result); +} + +internal add_low_entity_result +AddStandardRoom(game_state *GameState, uint32 AbsTileX, uint32 AbsTileY, uint32 AbsTileZ) +{ + world_position P = ChunkPositionFromTilePosition(GameState->World, AbsTileX, AbsTileY, AbsTileZ); + add_low_entity_result Entity = AddGroundedEntity(GameState, EntityType_Space, P, + GameState->StandardRoomCollision); + AddFlags(&Entity.Low->Sim, EntityFlag_Traversable); + + return(Entity); +} + +internal add_low_entity_result +AddWall(game_state *GameState, uint32 AbsTileX, uint32 AbsTileY, uint32 AbsTileZ) +{ + world_position P = ChunkPositionFromTilePosition(GameState->World, AbsTileX, AbsTileY, AbsTileZ); + add_low_entity_result Entity = AddGroundedEntity(GameState, EntityType_Wall, P, + GameState->WallCollision); + AddFlags(&Entity.Low->Sim, EntityFlag_Collides); + + return(Entity); +} + +internal add_low_entity_result +AddStair(game_state *GameState, uint32 AbsTileX, uint32 AbsTileY, uint32 AbsTileZ) +{ + world_position P = ChunkPositionFromTilePosition(GameState->World, AbsTileX, AbsTileY, AbsTileZ); + add_low_entity_result Entity = AddGroundedEntity(GameState, EntityType_Stairwell, P, + GameState->StairCollision); + AddFlags(&Entity.Low->Sim, EntityFlag_Collides); + Entity.Low->Sim.WalkableDim = Entity.Low->Sim.Collision->TotalVolume.Dim.xy; + Entity.Low->Sim.WalkableHeight = GameState->TypicalFloorHeight; + + return(Entity); +} + +internal void +InitHitPoints(low_entity *EntityLow, uint32 HitPointCount) +{ + Assert(HitPointCount <= ArrayCount(EntityLow->Sim.HitPoint)); + EntityLow->Sim.HitPointMax = HitPointCount; + for(uint32 HitPointIndex = 0; + HitPointIndex < EntityLow->Sim.HitPointMax; + ++HitPointIndex) + { + hit_point *HitPoint = EntityLow->Sim.HitPoint + HitPointIndex; + HitPoint->Flags = 0; + HitPoint->FilledAmount = HIT_POINT_SUB_COUNT; + } +} + +internal add_low_entity_result +AddSword(game_state *GameState) +{ + add_low_entity_result Entity = AddLowEntity(GameState, EntityType_Sword, NullPosition()); + Entity.Low->Sim.Collision = GameState->SwordCollision; + + AddFlags(&Entity.Low->Sim, EntityFlag_Moveable); + + return(Entity); +} + +internal add_low_entity_result +AddPlayer(game_state *GameState) +{ + world_position P = GameState->CameraP; + add_low_entity_result Entity = AddGroundedEntity(GameState, EntityType_Hero, P, + GameState->PlayerCollision); + AddFlags(&Entity.Low->Sim, EntityFlag_Collides|EntityFlag_Moveable); + + InitHitPoints(Entity.Low, 3); + + add_low_entity_result Sword = AddSword(GameState); + Entity.Low->Sim.Sword.Index = Sword.LowIndex; + + if(GameState->CameraFollowingEntityIndex == 0) + { + GameState->CameraFollowingEntityIndex = Entity.LowIndex; + } + + return(Entity); +} + +internal add_low_entity_result +AddMonstar(game_state *GameState, uint32 AbsTileX, uint32 AbsTileY, uint32 AbsTileZ) +{ + world_position P = ChunkPositionFromTilePosition(GameState->World, AbsTileX, AbsTileY, AbsTileZ); + add_low_entity_result Entity = AddGroundedEntity(GameState, EntityType_Monstar, P, + GameState->MonstarCollision); + AddFlags(&Entity.Low->Sim, EntityFlag_Collides|EntityFlag_Moveable); + + InitHitPoints(Entity.Low, 3); + + return(Entity); +} + +internal add_low_entity_result +AddFamiliar(game_state *GameState, uint32 AbsTileX, uint32 AbsTileY, uint32 AbsTileZ) +{ + world_position P = ChunkPositionFromTilePosition(GameState->World, AbsTileX, AbsTileY, AbsTileZ); + add_low_entity_result Entity = AddGroundedEntity(GameState, EntityType_Familiar, P, + GameState->FamiliarCollision); + AddFlags(&Entity.Low->Sim, EntityFlag_Collides|EntityFlag_Moveable); + + return(Entity); +} + +internal void +DrawHitpoints(sim_entity *Entity, render_group *PieceGroup) +{ + if(Entity->HitPointMax >= 1) + { + v2 HealthDim = {0.2f, 0.2f}; + real32 SpacingX = 1.5f*HealthDim.x; + v2 HitP = {-0.5f*(Entity->HitPointMax - 1)*SpacingX, -0.25f}; + v2 dHitP = {SpacingX, 0.0f}; + for(uint32 HealthIndex = 0; + HealthIndex < Entity->HitPointMax; + ++HealthIndex) + { + hit_point *HitPoint = Entity->HitPoint + HealthIndex; + v4 Color = {1.0f, 0.0f, 0.0f, 1.0f}; + if(HitPoint->FilledAmount == 0) + { + Color = V4(0.2f, 0.2f, 0.2f, 1.0f); + } + + PushRect(PieceGroup, V3(HitP, 0), HealthDim, Color); + HitP += dHitP; + } + } +} + +internal void +ClearCollisionRulesFor(game_state *GameState, uint32 StorageIndex) +{ + // TODO(casey): Need to make a better data structure that allows + // removal of collision rules without searching the entire table + // NOTE(casey): One way to make removal easy would be to always + // add _both_ orders of the pairs of storage indices to the + // hash table, so no matter which position the entity is in, + // you can always find it. Then, when you do your first pass + // through for removal, you just remember the original top + // of the free list, and when you're done, do a pass through all + // the new things on the free list, and remove the reverse of + // those pairs. + for(uint32 HashBucket = 0; + HashBucket < ArrayCount(GameState->CollisionRuleHash); + ++HashBucket) + { + for(pairwise_collision_rule **Rule = &GameState->CollisionRuleHash[HashBucket]; + *Rule; + ) + { + if(((*Rule)->StorageIndexA == StorageIndex) || + ((*Rule)->StorageIndexB == StorageIndex)) + { + pairwise_collision_rule *RemovedRule = *Rule; + *Rule = (*Rule)->NextInHash; + + RemovedRule->NextInHash = GameState->FirstFreeCollisionRule; + GameState->FirstFreeCollisionRule = RemovedRule; + } + else + { + Rule = &(*Rule)->NextInHash; + } + } + } +} + +internal void +AddCollisionRule(game_state *GameState, uint32 StorageIndexA, uint32 StorageIndexB, bool32 CanCollide) +{ + // TODO(casey): Collapse this with ShouldCollide + if(StorageIndexA > StorageIndexB) + { + uint32 Temp = StorageIndexA; + StorageIndexA = StorageIndexB; + StorageIndexB = Temp; + } + + // TODO(casey): BETTER HASH FUNCTION + pairwise_collision_rule *Found = 0; + uint32 HashBucket = StorageIndexA & (ArrayCount(GameState->CollisionRuleHash) - 1); + for(pairwise_collision_rule *Rule = GameState->CollisionRuleHash[HashBucket]; + Rule; + Rule = Rule->NextInHash) + { + if((Rule->StorageIndexA == StorageIndexA) && + (Rule->StorageIndexB == StorageIndexB)) + { + Found = Rule; + break; + } + } + + if(!Found) + { + Found = GameState->FirstFreeCollisionRule; + if(Found) + { + GameState->FirstFreeCollisionRule = Found->NextInHash; + } + else + { + Found = PushStruct(&GameState->WorldArena, pairwise_collision_rule); + } + + Found->NextInHash = GameState->CollisionRuleHash[HashBucket]; + GameState->CollisionRuleHash[HashBucket] = Found; + } + + if(Found) + { + Found->StorageIndexA = StorageIndexA; + Found->StorageIndexB = StorageIndexB; + Found->CanCollide = CanCollide; + } +} + +sim_entity_collision_volume_group * +MakeSimpleGroundedCollision(game_state *GameState, real32 DimX, real32 DimY, real32 DimZ) +{ + // TODO(casey): NOT WORLD ARENA! Change to using the fundamental types arena, etc. + sim_entity_collision_volume_group *Group = PushStruct(&GameState->WorldArena, sim_entity_collision_volume_group); + Group->VolumeCount = 1; + Group->Volumes = PushArray(&GameState->WorldArena, Group->VolumeCount, sim_entity_collision_volume); + Group->TotalVolume.OffsetP = V3(0, 0, 0.5f*DimZ); + Group->TotalVolume.Dim = V3(DimX, DimY, DimZ); + Group->Volumes[0] = Group->TotalVolume; + + return(Group); +} + +sim_entity_collision_volume_group * +MakeNullCollision(game_state *GameState) +{ + // TODO(casey): NOT WORLD ARENA! Change to using the fundamental types arena, etc. + sim_entity_collision_volume_group *Group = PushStruct(&GameState->WorldArena, sim_entity_collision_volume_group); + Group->VolumeCount = 0; + Group->Volumes = 0; + Group->TotalVolume.OffsetP = V3(0, 0, 0); + // TODO(casey): Should this be negative? + Group->TotalVolume.Dim = V3(0, 0, 0); + + return(Group); +} + +internal task_with_memory * +BeginTaskWithMemory(transient_state *TranState) +{ + task_with_memory *FoundTask = 0; + + for(uint32 TaskIndex = 0; + TaskIndex < ArrayCount(TranState->Tasks); + ++TaskIndex) + { + task_with_memory *Task = TranState->Tasks + TaskIndex; + if(!Task->BeingUsed) + { + FoundTask = Task; + Task->BeingUsed = true; + Task->MemoryFlush = BeginTemporaryMemory(&Task->Arena); + break; + } + } + + return(FoundTask); +} + +internal void +EndTaskWithMemory(task_with_memory *Task) +{ + EndTemporaryMemory(Task->MemoryFlush); + + CompletePreviousWritesBeforeFutureWrites; + Task->BeingUsed = false; +} + +struct fill_ground_chunk_work +{ + transient_state *TranState; + game_state *GameState; + ground_buffer *GroundBuffer; + world_position ChunkP; + + task_with_memory *Task; +}; +internal PLATFORM_WORK_QUEUE_CALLBACK(FillGroundChunkWork) +{ + TIMED_FUNCTION(); + + fill_ground_chunk_work *Work = (fill_ground_chunk_work *)Data; + + loaded_bitmap *Buffer = &Work->GroundBuffer->Bitmap; + Buffer->AlignPercentage = V2(0.5f, 0.5f); + Buffer->WidthOverHeight = 1.0f; + + real32 Width = Work->GameState->World->ChunkDimInMeters.x; + real32 Height = Work->GameState->World->ChunkDimInMeters.y; + Assert(Width == Height); + v2 HalfDim = 0.5f*V2(Width, Height); + + // TODO(casey): Decide what our pushbuffer size is! + render_group *RenderGroup = AllocateRenderGroup(Work->TranState->Assets, &Work->Task->Arena, 0, true); + BeginRender(RenderGroup); + Orthographic(RenderGroup, Buffer->Width, Buffer->Height, (Buffer->Width - 2) / Width); + Clear(RenderGroup, V4(1.0f, 0.0f, 1.0f, 1.0f)); + + for(int32 ChunkOffsetY = -1; + ChunkOffsetY <= 1; + ++ChunkOffsetY) + { + for(int32 ChunkOffsetX = -1; + ChunkOffsetX <= 1; + ++ChunkOffsetX) + { + int32 ChunkX = Work->ChunkP.ChunkX + ChunkOffsetX; + int32 ChunkY = Work->ChunkP.ChunkY + ChunkOffsetY; + int32 ChunkZ = Work->ChunkP.ChunkZ; + + // TODO(casey): Make random number generation more systemic + // TODO(casey): Look into wang hashing or some other spatial seed generation "thing"! + random_series Series = RandomSeed(139*ChunkX + 593*ChunkY + 329*ChunkZ); + + v4 Color; + DEBUG_IF(GroundChunks_Checkerboards) + { + Color = V4(1, 0, 0, 1); + if((ChunkX % 2) == (ChunkY % 2)) + { + Color = V4(0, 0, 1, 1); + } + } + else + { + Color = {1, 1, 1, 1}; + } + + v2 Center = V2(ChunkOffsetX*Width, ChunkOffsetY*Height); + + for(uint32 GrassIndex = 0; + GrassIndex < 100; + ++GrassIndex) + { + bitmap_id Stamp = GetRandomBitmapFrom(Work->TranState->Assets, + RandomChoice(&Series, 2) ? Asset_Grass : Asset_Stone, + &Series); + + v2 P = Center + Hadamard(HalfDim, V2(RandomBilateral(&Series), RandomBilateral(&Series))); + PushBitmap(RenderGroup, Stamp, 2.0f, V3(P, 0.0f), Color); + } + } + } + + for(int32 ChunkOffsetY = -1; + ChunkOffsetY <= 1; + ++ChunkOffsetY) + { + for(int32 ChunkOffsetX = -1; + ChunkOffsetX <= 1; + ++ChunkOffsetX) + { + int32 ChunkX = Work->ChunkP.ChunkX + ChunkOffsetX; + int32 ChunkY = Work->ChunkP.ChunkY + ChunkOffsetY; + int32 ChunkZ = Work->ChunkP.ChunkZ; + + // TODO(casey): Make random number generation more systemic + // TODO(casey): Look into wang hashing or some other spatial seed generation "thing"! + random_series Series = RandomSeed(139*ChunkX + 593*ChunkY + 329*ChunkZ); + + v2 Center = V2(ChunkOffsetX*Width, ChunkOffsetY*Height); + + for(uint32 GrassIndex = 0; + GrassIndex < 50; + ++GrassIndex) + { + bitmap_id Stamp = GetRandomBitmapFrom(Work->TranState->Assets, Asset_Tuft, &Series); + v2 P = Center + Hadamard(HalfDim, V2(RandomBilateral(&Series), RandomBilateral(&Series))); + PushBitmap(RenderGroup, Stamp, 0.1f, V3(P, 0.0f)); + } + } + } + + Assert(AllResourcesPresent(RenderGroup)); + + RenderGroupToOutput(RenderGroup, Buffer); + EndRender(RenderGroup); + + EndTaskWithMemory(Work->Task); +} + +internal void +FillGroundChunk(transient_state *TranState, game_state *GameState, ground_buffer *GroundBuffer, world_position *ChunkP) +{ + task_with_memory *Task = BeginTaskWithMemory(TranState); + if(Task) + { + fill_ground_chunk_work *Work = PushStruct(&Task->Arena, fill_ground_chunk_work); + Work->Task = Task; + Work->TranState = TranState; + Work->GameState = GameState; + Work->GroundBuffer = GroundBuffer; + Work->ChunkP = *ChunkP; + GroundBuffer->P = *ChunkP; + Platform.AddEntry(TranState->LowPriorityQueue, FillGroundChunkWork, Work); + } +} + +internal void +ClearBitmap(loaded_bitmap *Bitmap) +{ + if(Bitmap->Memory) + { + int32 TotalBitmapSize = Bitmap->Width*Bitmap->Height*BITMAP_BYTES_PER_PIXEL; + ZeroSize(TotalBitmapSize, Bitmap->Memory); + } +} + +internal loaded_bitmap +MakeEmptyBitmap(memory_arena *Arena, int32 Width, int32 Height, bool32 ClearToZero = true) +{ + loaded_bitmap Result = {}; + + Result.AlignPercentage = V2(0.5f, 0.5f); + Result.WidthOverHeight = SafeRatio1((r32)Width, (r32)Height); + + Result.Width = Width; + Result.Height = Height; + Result.Pitch = Result.Width*BITMAP_BYTES_PER_PIXEL; + int32 TotalBitmapSize = Width*Height*BITMAP_BYTES_PER_PIXEL; + Result.Memory = PushSize(Arena, TotalBitmapSize, 16); + if(ClearToZero) + { + ClearBitmap(&Result); + } + + return(Result); +} + +internal void +MakeSphereNormalMap(loaded_bitmap *Bitmap, real32 Roughness, real32 Cx = 1.0f, real32 Cy = 1.0f) +{ + real32 InvWidth = 1.0f / (real32)(Bitmap->Width - 1); + real32 InvHeight = 1.0f / (real32)(Bitmap->Height - 1); + + uint8 *Row = (uint8 *)Bitmap->Memory; + for(int32 Y = 0; + Y < Bitmap->Height; + ++Y) + { + uint32 *Pixel = (uint32 *)Row; + for(int32 X = 0; + X < Bitmap->Width; + ++X) + { + v2 BitmapUV = {InvWidth*(real32)X, InvHeight*(real32)Y}; + + real32 Nx = Cx*(2.0f*BitmapUV.x - 1.0f); + real32 Ny = Cy*(2.0f*BitmapUV.y - 1.0f); + + real32 RootTerm = 1.0f - Nx*Nx - Ny*Ny; + v3 Normal = {0, 0.707106781188f, 0.707106781188f}; + real32 Nz = 0.0f; + if(RootTerm >= 0.0f) + { + Nz = SquareRoot(RootTerm); + Normal = V3(Nx, Ny, Nz); + } + + v4 Color = {255.0f*(0.5f*(Normal.x + 1.0f)), + 255.0f*(0.5f*(Normal.y + 1.0f)), + 255.0f*(0.5f*(Normal.z + 1.0f)), + 255.0f*Roughness}; + + *Pixel++ = (((uint32)(Color.a + 0.5f) << 24) | + ((uint32)(Color.r + 0.5f) << 16) | + ((uint32)(Color.g + 0.5f) << 8) | + ((uint32)(Color.b + 0.5f) << 0)); + } + + Row += Bitmap->Pitch; + } +} + +internal void +MakeSphereDiffuseMap(loaded_bitmap *Bitmap, real32 Cx = 1.0f, real32 Cy = 1.0f) +{ + real32 InvWidth = 1.0f / (real32)(Bitmap->Width - 1); + real32 InvHeight = 1.0f / (real32)(Bitmap->Height - 1); + + uint8 *Row = (uint8 *)Bitmap->Memory; + for(int32 Y = 0; + Y < Bitmap->Height; + ++Y) + { + uint32 *Pixel = (uint32 *)Row; + for(int32 X = 0; + X < Bitmap->Width; + ++X) + { + v2 BitmapUV = {InvWidth*(real32)X, InvHeight*(real32)Y}; + + real32 Nx = Cx*(2.0f*BitmapUV.x - 1.0f); + real32 Ny = Cy*(2.0f*BitmapUV.y - 1.0f); + + real32 RootTerm = 1.0f - Nx*Nx - Ny*Ny; + real32 Alpha = 0.0f; + if(RootTerm >= 0.0f) + { + Alpha = 1.0f; + } + + v3 BaseColor = {0.0f, 0.0f, 0.0f}; + Alpha *= 255.0f; + v4 Color = {Alpha*BaseColor.x, + Alpha*BaseColor.y, + Alpha*BaseColor.z, + Alpha}; + + *Pixel++ = (((uint32)(Color.a + 0.5f) << 24) | + ((uint32)(Color.r + 0.5f) << 16) | + ((uint32)(Color.g + 0.5f) << 8) | + ((uint32)(Color.b + 0.5f) << 0)); + } + + Row += Bitmap->Pitch; + } +} + +internal void +MakePyramidNormalMap(loaded_bitmap *Bitmap, real32 Roughness) +{ + real32 InvWidth = 1.0f / (real32)(Bitmap->Width - 1); + real32 InvHeight = 1.0f / (real32)(Bitmap->Height - 1); + + uint8 *Row = (uint8 *)Bitmap->Memory; + for(int32 Y = 0; + Y < Bitmap->Height; + ++Y) + { + uint32 *Pixel = (uint32 *)Row; + for(int32 X = 0; + X < Bitmap->Width; + ++X) + { + v2 BitmapUV = {InvWidth*(real32)X, InvHeight*(real32)Y}; + + int32 InvX = (Bitmap->Width - 1) - X; + real32 Seven = 0.707106781188f; + v3 Normal = {0, 0, Seven}; + if(X < Y) + { + if(InvX < Y) + { + Normal.x = -Seven; + } + else + { + Normal.y = Seven; + } + } + else + { + if(InvX < Y) + { + Normal.y = -Seven; + } + else + { + Normal.x = Seven; + } + } + + v4 Color = {255.0f*(0.5f*(Normal.x + 1.0f)), + 255.0f*(0.5f*(Normal.y + 1.0f)), + 255.0f*(0.5f*(Normal.z + 1.0f)), + 255.0f*Roughness}; + + *Pixel++ = (((uint32)(Color.a + 0.5f) << 24) | + ((uint32)(Color.r + 0.5f) << 16) | + ((uint32)(Color.g + 0.5f) << 8) | + ((uint32)(Color.b + 0.5f) << 0)); + } + + Row += Bitmap->Pitch; + } +} + +internal game_assets * +DEBUGGetGameAssets(game_memory *Memory) +{ + game_assets *Assets = 0; + + transient_state *TranState = (transient_state *)Memory->TransientStorage; + if(TranState->IsInitialized) + { + Assets = TranState->Assets; + } + + return(Assets); +} + + +#if HANDMADE_INTERNAL +game_memory *DebugGlobalMemory; +#endif +extern "C" GAME_UPDATE_AND_RENDER(GameUpdateAndRender) +{ + Platform = Memory->PlatformAPI; + +#if HANDMADE_INTERNAL + DebugGlobalMemory = Memory; +#endif + TIMED_FUNCTION(); + + Assert((&Input->Controllers[0].Terminator - &Input->Controllers[0].Buttons[0]) == + (ArrayCount(Input->Controllers[0].Buttons))); + + uint32 GroundBufferWidth = 256; + uint32 GroundBufferHeight = 256; + + Assert(sizeof(game_state) <= Memory->PermanentStorageSize); + game_state *GameState = (game_state *)Memory->PermanentStorage; + if(!GameState->IsInitialized) + { + uint32 TilesPerWidth = 17; + uint32 TilesPerHeight = 9; + + GameState->EffectsEntropy = RandomSeed(1234); + GameState->TypicalFloorHeight = 3.0f; + + // TODO(casey): Remove this! + real32 PixelsToMeters = 1.0f / 42.0f; + v3 WorldChunkDimInMeters = {PixelsToMeters*(real32)GroundBufferWidth, + PixelsToMeters*(real32)GroundBufferHeight, + GameState->TypicalFloorHeight}; + + InitializeArena(&GameState->WorldArena, Memory->PermanentStorageSize - sizeof(game_state), + (uint8 *)Memory->PermanentStorage + sizeof(game_state)); + + InitializeAudioState(&GameState->AudioState, &GameState->WorldArena); + + // NOTE(casey): Reserve entity slot 0 for the null entity + AddLowEntity(GameState, EntityType_Null, NullPosition()); + + GameState->World = PushStruct(&GameState->WorldArena, world); + world *World = GameState->World; + InitializeWorld(World, WorldChunkDimInMeters); + + real32 TileSideInMeters = 1.4f; + real32 TileDepthInMeters = GameState->TypicalFloorHeight; + + GameState->NullCollision = MakeNullCollision(GameState); + GameState->SwordCollision = MakeSimpleGroundedCollision(GameState, 1.0f, 0.5f, 0.1f); + GameState->StairCollision = MakeSimpleGroundedCollision(GameState, + TileSideInMeters, + 2.0f*TileSideInMeters, + 1.1f*TileDepthInMeters); + GameState->PlayerCollision = MakeSimpleGroundedCollision(GameState, 1.0f, 0.5f, 1.2f); + GameState->MonstarCollision = MakeSimpleGroundedCollision(GameState, 1.0f, 0.5f, 0.5f); + GameState->FamiliarCollision = MakeSimpleGroundedCollision(GameState, 1.0f, 0.5f, 0.5f); + GameState->WallCollision = MakeSimpleGroundedCollision(GameState, + TileSideInMeters, + TileSideInMeters, + TileDepthInMeters); + GameState->StandardRoomCollision = MakeSimpleGroundedCollision(GameState, + TilesPerWidth*TileSideInMeters, + TilesPerHeight*TileSideInMeters, + 0.9f*TileDepthInMeters); + + random_series Series = RandomSeed(1234); + + uint32 ScreenBaseX = 0; + uint32 ScreenBaseY = 0; + uint32 ScreenBaseZ = 0; + uint32 ScreenX = ScreenBaseX; + uint32 ScreenY = ScreenBaseY; + uint32 AbsTileZ = ScreenBaseZ; + + // TODO(casey): Replace all this with real world generation! + bool32 DoorLeft = false; + bool32 DoorRight = false; + bool32 DoorTop = false; + bool32 DoorBottom = false; + bool32 DoorUp = false; + bool32 DoorDown = false; + for(uint32 ScreenIndex = 0; + ScreenIndex < 2000; + ++ScreenIndex) + { +#if 1 + uint32 DoorDirection = RandomChoice(&Series, (DoorUp || DoorDown) ? 2 : 4); +#else + uint32 DoorDirection = RandomChoice(&Series, 2); +#endif + +// DoorDirection = 3; + + bool32 CreatedZDoor = false; + if(DoorDirection == 3) + { + CreatedZDoor = true; + DoorDown = true; + } + else if(DoorDirection == 2) + { + CreatedZDoor = true; + DoorUp = true; + } + else if(DoorDirection == 1) + { + DoorRight = true; + } + else + { + DoorTop = true; + } + + AddStandardRoom(GameState, + ScreenX*TilesPerWidth + TilesPerWidth/2, + ScreenY*TilesPerHeight + TilesPerHeight/2, + AbsTileZ); + + for(uint32 TileY = 0; + TileY < TilesPerHeight; + ++TileY) + { + for(uint32 TileX = 0; + TileX < TilesPerWidth; + ++TileX) + { + uint32 AbsTileX = ScreenX*TilesPerWidth + TileX; + uint32 AbsTileY = ScreenY*TilesPerHeight + TileY; + + bool32 ShouldBeDoor = false; + if((TileX == 0) && (!DoorLeft || (TileY != (TilesPerHeight/2)))) + { + ShouldBeDoor = true; + } + + if((TileX == (TilesPerWidth - 1)) && (!DoorRight || (TileY != (TilesPerHeight/2)))) + { + ShouldBeDoor = true; + } + + if((TileY == 0) && (!DoorBottom || (TileX != (TilesPerWidth/2)))) + { + ShouldBeDoor = true; + } + + if((TileY == (TilesPerHeight - 1)) && (!DoorTop || (TileX != (TilesPerWidth/2)))) + { + ShouldBeDoor = true; + } + + if(ShouldBeDoor) + { + AddWall(GameState, AbsTileX, AbsTileY, AbsTileZ); + } + else if(CreatedZDoor) + { + if(((AbsTileZ % 2) && (TileX == 10) && (TileY == 5)) || + (!(AbsTileZ % 2) && (TileX == 4) && (TileY == 5))) + { + AddStair(GameState, AbsTileX, AbsTileY, DoorDown ? AbsTileZ - 1 : AbsTileZ); + } + } + } + } + + DoorLeft = DoorRight; + DoorBottom = DoorTop; + + if(CreatedZDoor) + { + DoorDown = !DoorDown; + DoorUp = !DoorUp; + } + else + { + DoorUp = false; + DoorDown = false; + } + + DoorRight = false; + DoorTop = false; + + if(DoorDirection == 3) + { + AbsTileZ -= 1; + } + else if(DoorDirection == 2) + { + AbsTileZ += 1; + } + else if(DoorDirection == 1) + { + ScreenX += 1; + } + else + { + ScreenY += 1; + } + } + +#if 0 + while(GameState->LowEntityCount < (ArrayCount(GameState->LowEntities) - 16)) + { + uint32 Coordinate = 1024 + GameState->LowEntityCount; + AddWall(GameState, Coordinate, Coordinate, Coordinate); + } +#endif + + world_position NewCameraP = {}; + uint32 CameraTileX = ScreenBaseX*TilesPerWidth + 17/2; + uint32 CameraTileY = ScreenBaseY*TilesPerHeight + 9/2; + uint32 CameraTileZ = ScreenBaseZ; + NewCameraP = ChunkPositionFromTilePosition(GameState->World, + CameraTileX, + CameraTileY, + CameraTileZ); + GameState->CameraP = NewCameraP; + + AddMonstar(GameState, CameraTileX - 3, CameraTileY + 2, CameraTileZ); + for(int FamiliarIndex = 0; + FamiliarIndex < 1; + ++FamiliarIndex) + { + int32 FamiliarOffsetX = RandomBetween(&Series, -7, 7); + int32 FamiliarOffsetY = RandomBetween(&Series, -3, -1); + if((FamiliarOffsetX != 0) || + (FamiliarOffsetY != 0)) + { + AddFamiliar(GameState, CameraTileX + FamiliarOffsetX, CameraTileY + FamiliarOffsetY, + CameraTileZ); + } + } + + GameState->IsInitialized = true; + } + + // NOTE(casey): Transient initialization + Assert(sizeof(transient_state) <= Memory->TransientStorageSize); + transient_state *TranState = (transient_state *)Memory->TransientStorage; + if(!TranState->IsInitialized) + { + InitializeArena(&TranState->TranArena, Memory->TransientStorageSize - sizeof(transient_state), + (uint8 *)Memory->TransientStorage + sizeof(transient_state)); + + TranState->HighPriorityQueue = Memory->HighPriorityQueue; + TranState->LowPriorityQueue = Memory->LowPriorityQueue; + for(uint32 TaskIndex = 0; + TaskIndex < ArrayCount(TranState->Tasks); + ++TaskIndex) + { + task_with_memory *Task = TranState->Tasks + TaskIndex; + + Task->BeingUsed = false; + SubArena(&Task->Arena, &TranState->TranArena, Megabytes(1)); + } + + TranState->Assets = AllocateGameAssets(&TranState->TranArena, Megabytes(16), TranState); + +// GameState->Music = PlaySound(&GameState->AudioState, GetFirstSoundFrom(TranState->Assets, Asset_Music)); + + // TODO(casey): Pick a real number here! + TranState->GroundBufferCount = 256; + TranState->GroundBuffers = PushArray(&TranState->TranArena, TranState->GroundBufferCount, ground_buffer); + for(uint32 GroundBufferIndex = 0; + GroundBufferIndex < TranState->GroundBufferCount; + ++GroundBufferIndex) + { + ground_buffer *GroundBuffer = TranState->GroundBuffers + GroundBufferIndex; + GroundBuffer->Bitmap = MakeEmptyBitmap(&TranState->TranArena, GroundBufferWidth, GroundBufferHeight, false); + GroundBuffer->P = NullPosition(); + } + + GameState->TestDiffuse = MakeEmptyBitmap(&TranState->TranArena, 256, 256, false); + GameState->TestNormal = MakeEmptyBitmap(&TranState->TranArena, GameState->TestDiffuse.Width, GameState->TestDiffuse.Height, false); + MakeSphereNormalMap(&GameState->TestNormal, 0.0f); + MakeSphereDiffuseMap(&GameState->TestDiffuse); +// MakePyramidNormalMap(&GameState->TestNormal, 0.0f); + + TranState->EnvMapWidth = 512; + TranState->EnvMapHeight = 256; + for(uint32 MapIndex = 0; + MapIndex < ArrayCount(TranState->EnvMaps); + ++MapIndex) + { + environment_map *Map = TranState->EnvMaps + MapIndex; + uint32 Width = TranState->EnvMapWidth; + uint32 Height = TranState->EnvMapHeight; + for(uint32 LODIndex = 0; + LODIndex < ArrayCount(Map->LOD); + ++LODIndex) + { + Map->LOD[LODIndex] = MakeEmptyBitmap(&TranState->TranArena, Width, Height, false); + Width >>= 1; + Height >>= 1; + } + } + + TranState->IsInitialized = true; + } + + DEBUG_IF(GroundChunks_RecomputeOnEXEChange) + { + if(Memory->ExecutableReloaded) + { + for(uint32 GroundBufferIndex = 0; + GroundBufferIndex < TranState->GroundBufferCount; + ++GroundBufferIndex) + { + ground_buffer *GroundBuffer = TranState->GroundBuffers + GroundBufferIndex; + GroundBuffer->P = NullPosition(); + } + } + } + + world *World = GameState->World; + +#if 0 + // + // NOTE(casey): + // + { + v2 MusicVolume; + MusicVolume.y = SafeRatio0((r32)Input->MouseX, (r32)Buffer->Width); + MusicVolume.x = 1.0f - MusicVolume.y; + ChangeVolume(&GameState->AudioState, GameState->Music, 0.01f, MusicVolume); + } +#endif + + for(int ControllerIndex = 0; + ControllerIndex < ArrayCount(Input->Controllers); + ++ControllerIndex) + { + game_controller_input *Controller = GetController(Input, ControllerIndex); + controlled_hero *ConHero = GameState->ControlledHeroes + ControllerIndex; + if(ConHero->EntityIndex == 0) + { + if(Controller->Start.EndedDown) + { + *ConHero = {}; + ConHero->EntityIndex = AddPlayer(GameState).LowIndex; + } + } + else + { + ConHero->dZ = 0.0f; + ConHero->ddP = {}; + ConHero->dSword = {}; + + if(Controller->IsAnalog) + { + // NOTE(casey): Use analog movement tuning + ConHero->ddP = V2(Controller->StickAverageX, Controller->StickAverageY); + } + else + { + // NOTE(casey): Use digital movement tuning + if(Controller->MoveUp.EndedDown) + { + ConHero->ddP.y = 1.0f; + } + if(Controller->MoveDown.EndedDown) + { + ConHero->ddP.y = -1.0f; + } + if(Controller->MoveLeft.EndedDown) + { + ConHero->ddP.x = -1.0f; + } + if(Controller->MoveRight.EndedDown) + { + ConHero->ddP.x = 1.0f; + } + } + + if(Controller->Start.EndedDown) + { + ConHero->dZ = 3.0f; + } + + ConHero->dSword = {}; + if(Controller->ActionUp.EndedDown) + { + ChangeVolume(&GameState->AudioState, GameState->Music, 10.0f, V2(1.0f, 1.0f)); + ConHero->dSword = V2(0.0f, 1.0f); + } + if(Controller->ActionDown.EndedDown) + { + ChangeVolume(&GameState->AudioState, GameState->Music, 10.0f, V2(0.0f, 0.0f)); + ConHero->dSword = V2(0.0f, -1.0f); + } + if(Controller->ActionLeft.EndedDown) + { + ChangeVolume(&GameState->AudioState, GameState->Music, 5.0f, V2(1.0f, 0.0f)); + ConHero->dSword = V2(-1.0f, 0.0f); + } + if(Controller->ActionRight.EndedDown) + { + ChangeVolume(&GameState->AudioState, GameState->Music, 5.0f, V2(0.0f, 1.0f)); + ConHero->dSword = V2(1.0f, 0.0f); + } + } + } + + // + // NOTE(casey): Render + // + temporary_memory RenderMemory = BeginTemporaryMemory(&TranState->TranArena); + + loaded_bitmap DrawBuffer_ = {}; + loaded_bitmap *DrawBuffer = &DrawBuffer_; + DrawBuffer->Width = Buffer->Width; + DrawBuffer->Height = Buffer->Height; + DrawBuffer->Pitch = Buffer->Pitch; + DrawBuffer->Memory = Buffer->Memory; + + DEBUG_IF(Renderer_TestWeirdDrawBufferSize) + { + // NOTE(casey): Enable this to test weird buffer sizes in the renderer! + DrawBuffer->Width = 1279; + DrawBuffer->Height = 719; + } + + v2 MouseP = {Input->MouseX, Input->MouseY}; + + // TODO(casey): Decide what our pushbuffer size is! + render_group *RenderGroup = AllocateRenderGroup(TranState->Assets, &TranState->TranArena, Megabytes(4), false); + BeginRender(RenderGroup); + real32 WidthOfMonitor = 0.635f; // NOTE(casey): Horizontal measurement of monitor in meters + real32 MetersToPixels = (real32)DrawBuffer->Width*WidthOfMonitor; + + real32 FocalLength = 0.6f; + real32 DistanceAboveGround = 9.0f; + Perspective(RenderGroup, DrawBuffer->Width, DrawBuffer->Height, MetersToPixels, FocalLength, DistanceAboveGround); + + Clear(RenderGroup, V4(0.25f, 0.25f, 0.25f, 0.0f)); + + v2 ScreenCenter = {0.5f*(real32)DrawBuffer->Width, + 0.5f*(real32)DrawBuffer->Height}; + + rectangle2 ScreenBounds = GetCameraRectangleAtTarget(RenderGroup); + rectangle3 CameraBoundsInMeters = RectMinMax(V3(ScreenBounds.Min, 0.0f), V3(ScreenBounds.Max, 0.0f)); + CameraBoundsInMeters.Min.z = -3.0f*GameState->TypicalFloorHeight; + CameraBoundsInMeters.Max.z = 1.0f*GameState->TypicalFloorHeight; + + // NOTE(casey): Ground chunk rendering + for(uint32 GroundBufferIndex = 0; + GroundBufferIndex < TranState->GroundBufferCount; + ++GroundBufferIndex) + { + ground_buffer *GroundBuffer = TranState->GroundBuffers + GroundBufferIndex; + if(IsValid(GroundBuffer->P)) + { + loaded_bitmap *Bitmap = &GroundBuffer->Bitmap; + v3 Delta = Subtract(GameState->World, &GroundBuffer->P, &GameState->CameraP); + + if((Delta.z >= -1.0f) && (Delta.z < 1.0f)) + { + real32 GroundSideInMeters = World->ChunkDimInMeters.x; + PushBitmap(RenderGroup, Bitmap, 1.0f*GroundSideInMeters, Delta); + DEBUG_IF(GroundChunks_Outlines) + { + PushRectOutline(RenderGroup, Delta, V2(GroundSideInMeters, GroundSideInMeters), V4(1.0f, 1.0f, 0.0f, 1.0f)); + } + } + } + } + + // NOTE(casey): Ground chunk updating + { + world_position MinChunkP = MapIntoChunkSpace(World, GameState->CameraP, GetMinCorner(CameraBoundsInMeters)); + world_position MaxChunkP = MapIntoChunkSpace(World, GameState->CameraP, GetMaxCorner(CameraBoundsInMeters)); + + for(int32 ChunkZ = MinChunkP.ChunkZ; + ChunkZ <= MaxChunkP.ChunkZ; + ++ChunkZ) + { + for(int32 ChunkY = MinChunkP.ChunkY; + ChunkY <= MaxChunkP.ChunkY; + ++ChunkY) + { + for(int32 ChunkX = MinChunkP.ChunkX; + ChunkX <= MaxChunkP.ChunkX; + ++ChunkX) + { +// world_chunk *Chunk = GetWorldChunk(World, ChunkX, ChunkY, ChunkZ); +// if(Chunk) + { + world_position ChunkCenterP = CenteredChunkPoint(ChunkX, ChunkY, ChunkZ); + v3 RelP = Subtract(World, &ChunkCenterP, &GameState->CameraP); + + // TODO(casey): This is super inefficient fix it! + real32 FurthestBufferLengthSq = 0.0f; + ground_buffer *FurthestBuffer = 0; + for(uint32 GroundBufferIndex = 0; + GroundBufferIndex < TranState->GroundBufferCount; + ++GroundBufferIndex) + { + ground_buffer *GroundBuffer = TranState->GroundBuffers + GroundBufferIndex; + if(AreInSameChunk(World, &GroundBuffer->P, &ChunkCenterP)) + { + FurthestBuffer = 0; + break; + } + else if(IsValid(GroundBuffer->P)) + { + v3 RelP = Subtract(World, &GroundBuffer->P, &GameState->CameraP); + real32 BufferLengthSq = LengthSq(RelP.xy); + if(FurthestBufferLengthSq < BufferLengthSq) + { + FurthestBufferLengthSq = BufferLengthSq; + FurthestBuffer = GroundBuffer; + } + } + else + { + FurthestBufferLengthSq = Real32Maximum; + FurthestBuffer = GroundBuffer; + } + } + + if(FurthestBuffer) + { + FillGroundChunk(TranState, GameState, FurthestBuffer, &ChunkCenterP); + } + } + } + } + } + } + + // TODO(casey): How big do we actually want to expand here? + // TODO(casey): Do we want to simulate upper floors, etc.? + v3 SimBoundsExpansion = {15.0f, 15.0f, 0.0f}; + rectangle3 SimBounds = AddRadiusTo(CameraBoundsInMeters, SimBoundsExpansion); + temporary_memory SimMemory = BeginTemporaryMemory(&TranState->TranArena); + world_position SimCenterP = GameState->CameraP; + sim_region *SimRegion = BeginSim(&TranState->TranArena, GameState, GameState->World, + SimCenterP, SimBounds, Input->dtForFrame); + + v3 CameraP = Subtract(World, &GameState->CameraP, &SimCenterP); + + PushRectOutline(RenderGroup, V3(0.0f, 0.0f, 0.0f), GetDim(ScreenBounds), V4(1.0f, 1.0f, 0.0f, 1)); +// PushRectOutline(RenderGroup, V3(0.0f, 0.0f, 0.0f), GetDim(CameraBoundsInMeters).xy, V4(1.0f, 1.0f, 1.0f, 1)); + PushRectOutline(RenderGroup, V3(0.0f, 0.0f, 0.0f), GetDim(SimBounds).xy, V4(0.0f, 1.0f, 1.0f, 1)); + PushRectOutline(RenderGroup, V3(0.0f, 0.0f, 0.0f), GetDim(SimRegion->Bounds).xy, V4(1.0f, 0.0f, 1.0f, 1)); + + + // TODO(casey): Move this out into handmade_entity.cpp! + for(uint32 EntityIndex = 0; + EntityIndex < SimRegion->EntityCount; + ++EntityIndex) + { + sim_entity *Entity = SimRegion->Entities + EntityIndex; + if(Entity->Updatable) + { + real32 dt = Input->dtForFrame; + + // TODO(casey): This is incorrect, should be computed after update!!!! + real32 ShadowAlpha = 1.0f - 0.5f*Entity->P.z; + if(ShadowAlpha < 0) + { + ShadowAlpha = 0.0f; + } + + move_spec MoveSpec = DefaultMoveSpec(); + v3 ddP = {}; + + // TODO(casey): Probably indicates we want to separate update and render + // for entities sometime soon? + v3 CameraRelativeGroundP = GetEntityGroundPoint(Entity) - CameraP; + real32 FadeTopEndZ = 0.75f*GameState->TypicalFloorHeight; + real32 FadeTopStartZ = 0.5f*GameState->TypicalFloorHeight; + real32 FadeBottomStartZ = -2.0f*GameState->TypicalFloorHeight; + real32 FadeBottomEndZ = -2.25f*GameState->TypicalFloorHeight;;; + RenderGroup->GlobalAlpha = 1.0f; + if(CameraRelativeGroundP.z > FadeTopStartZ) + { + RenderGroup->GlobalAlpha = Clamp01MapToRange(FadeTopEndZ, CameraRelativeGroundP.z, FadeTopStartZ); + } + else if(CameraRelativeGroundP.z < FadeBottomStartZ) + { + RenderGroup->GlobalAlpha = Clamp01MapToRange(FadeBottomEndZ, CameraRelativeGroundP.z, FadeBottomStartZ); + } + + // + // NOTE(casey): Pre-physics entity work + // + hero_bitmap_ids HeroBitmaps = {}; + asset_vector MatchVector = {}; + MatchVector.E[Tag_FacingDirection] = Entity->FacingDirection; + asset_vector WeightVector = {}; + WeightVector.E[Tag_FacingDirection] = 1.0f; + HeroBitmaps.Head = GetBestMatchBitmapFrom(TranState->Assets, Asset_Head, &MatchVector, &WeightVector); + HeroBitmaps.Cape = GetBestMatchBitmapFrom(TranState->Assets, Asset_Cape, &MatchVector, &WeightVector); + HeroBitmaps.Torso = GetBestMatchBitmapFrom(TranState->Assets, Asset_Torso, &MatchVector, &WeightVector); + switch(Entity->Type) + { + case EntityType_Hero: + { + // TODO(casey): Now that we have some real usage examples, let's solidify + // the positioning system! + for(uint32 ControlIndex = 0; + ControlIndex < ArrayCount(GameState->ControlledHeroes); + ++ControlIndex) + { + controlled_hero *ConHero = GameState->ControlledHeroes + ControlIndex; + + if(Entity->StorageIndex == ConHero->EntityIndex) + { + if(ConHero->dZ != 0.0f) + { + Entity->dP.z = ConHero->dZ; + } + + MoveSpec.UnitMaxAccelVector = true; + MoveSpec.Speed = 50.0f; + MoveSpec.Drag = 8.0f; + ddP = V3(ConHero->ddP, 0); + + if((ConHero->dSword.x != 0.0f) || (ConHero->dSword.y != 0.0f)) + { + sim_entity *Sword = Entity->Sword.Ptr; + if(Sword && IsSet(Sword, EntityFlag_Nonspatial)) + { + Sword->DistanceLimit = 5.0f; + MakeEntitySpatial(Sword, Entity->P, + Entity->dP + 5.0f*V3(ConHero->dSword, 0)); + AddCollisionRule(GameState, Sword->StorageIndex, Entity->StorageIndex, false); + + PlaySound(&GameState->AudioState, GetRandomSoundFrom(TranState->Assets, Asset_Bloop, &GameState->EffectsEntropy)); + } + } + } + } + } break; + + case EntityType_Sword: + { + MoveSpec.UnitMaxAccelVector = false; + MoveSpec.Speed = 0.0f; + MoveSpec.Drag = 0.0f; + + if(Entity->DistanceLimit == 0.0f) + { + ClearCollisionRulesFor(GameState, Entity->StorageIndex); + MakeEntityNonSpatial(Entity); + } + } break; + + case EntityType_Familiar: + { + sim_entity *ClosestHero = 0; + real32 ClosestHeroDSq = Square(10.0f); // NOTE(casey): Ten meter maximum search! + + DEBUG_IF(AI_Familiar_FollowsHero) + { + // TODO(casey): Make spatial queries easy for things! + sim_entity *TestEntity = SimRegion->Entities; + for(uint32 TestEntityIndex = 0; + TestEntityIndex < SimRegion->EntityCount; + ++TestEntityIndex, ++TestEntity) + { + if(TestEntity->Type == EntityType_Hero) + { + real32 TestDSq = LengthSq(TestEntity->P - Entity->P); + if(ClosestHeroDSq > TestDSq) + { + ClosestHero = TestEntity; + ClosestHeroDSq = TestDSq; + } + } + } + } + + if(ClosestHero && (ClosestHeroDSq > Square(3.0f))) + { + real32 Acceleration = 0.5f; + real32 OneOverLength = Acceleration / SquareRoot(ClosestHeroDSq); + ddP = OneOverLength*(ClosestHero->P - Entity->P); + } + + MoveSpec.UnitMaxAccelVector = true; + MoveSpec.Speed = 50.0f; + MoveSpec.Drag = 8.0f; + } break; + } + + if(!IsSet(Entity, EntityFlag_Nonspatial) && + IsSet(Entity, EntityFlag_Moveable)) + { + MoveEntity(GameState, SimRegion, Entity, Input->dtForFrame, &MoveSpec, ddP); + } + + RenderGroup->Transform.OffsetP = GetEntityGroundPoint(Entity); + + // + // NOTE(casey): Post-physics entity work + // + switch(Entity->Type) + { + case EntityType_Hero: + { + // TODO(casey): Z!!! + real32 HeroSizeC = 2.5f; + PushBitmap(RenderGroup, GetFirstBitmapFrom(TranState->Assets, Asset_Shadow), HeroSizeC*1.0f, V3(0, 0, 0), V4(1, 1, 1, ShadowAlpha)); + PushBitmap(RenderGroup, HeroBitmaps.Torso, HeroSizeC*1.2f, V3(0, 0, 0)); + PushBitmap(RenderGroup, HeroBitmaps.Cape, HeroSizeC*1.2f, V3(0, 0, 0)); + PushBitmap(RenderGroup, HeroBitmaps.Head, HeroSizeC*1.2f, V3(0, 0, 0)); + DrawHitpoints(Entity, RenderGroup); + + DEBUG_IF(Particles_Test) + { + for(u32 ParticleSpawnIndex = 0; + ParticleSpawnIndex < 3; + ++ParticleSpawnIndex) + { + particle *Particle = GameState->Particles + GameState->NextParticle++; + if(GameState->NextParticle >= ArrayCount(GameState->Particles)) + { + GameState->NextParticle = 0; + } + + Particle->P = V3(RandomBetween(&GameState->EffectsEntropy, -0.05f, 0.05f), 0, 0); + Particle->dP = V3(RandomBetween(&GameState->EffectsEntropy, -0.01f, 0.01f), 7.0f*RandomBetween(&GameState->EffectsEntropy, 0.7f, 1.0f), 0.0f); + Particle->ddP = V3(0.0f, -9.8f, 0.0f); + Particle->Color = V4(RandomBetween(&GameState->EffectsEntropy, 0.75f, 1.0f), + RandomBetween(&GameState->EffectsEntropy, 0.75f, 1.0f), + RandomBetween(&GameState->EffectsEntropy, 0.75f, 1.0f), + 1.0f); + Particle->dColor = V4(0, 0, 0, -0.25f); + + asset_vector MatchVector = {}; + asset_vector WeightVector = {}; + char Nothings[] = "NOTHINGS"; + MatchVector.E[Tag_UnicodeCodepoint] = (r32)Nothings[RandomChoice(&GameState->EffectsEntropy, ArrayCount(Nothings) - 1)]; + WeightVector.E[Tag_UnicodeCodepoint] = 1.0f; + + Particle->BitmapID = HeroBitmaps.Head; //GetBestMatchBitmapFrom(TranState->Assets, Asset_Font, + // &MatchVector, &WeightVector); + +// Particle->BitmapID = GetRandomBitmapFrom(TranState->Assets, Asset_Font, &GameState->EffectsEntropy); + } + + // NOTE(casey): Particle system test + ZeroStruct(GameState->ParticleCels); + + r32 GridScale = 0.25f; + r32 InvGridScale = 1.0f / GridScale; + v3 GridOrigin = {-0.5f*GridScale*PARTICLE_CEL_DIM, 0.0f, 0.0f}; + for(u32 ParticleIndex = 0; + ParticleIndex < ArrayCount(GameState->Particles); + ++ParticleIndex) + { + particle *Particle = GameState->Particles + ParticleIndex; + + v3 P = InvGridScale*(Particle->P - GridOrigin); + + s32 X = TruncateReal32ToInt32(P.x); + s32 Y = TruncateReal32ToInt32(P.y); + + if(X < 0) {X = 0;} + if(X > (PARTICLE_CEL_DIM - 1)) {X = (PARTICLE_CEL_DIM - 1);} + if(Y < 0) {Y = 0;} + if(Y > (PARTICLE_CEL_DIM - 1)) {Y = (PARTICLE_CEL_DIM - 1);} + + particle_cel *Cel = &GameState->ParticleCels[Y][X]; + real32 Density = Particle->Color.a; + Cel->Density += Density; + Cel->VelocityTimesDensity += Density*Particle->dP; + } + + DEBUG_IF(Particles_ShowGrid) + { + for(u32 Y = 0; + Y < PARTICLE_CEL_DIM; + ++Y) + { + for(u32 X = 0; + X < PARTICLE_CEL_DIM; + ++X) + { + particle_cel *Cel = &GameState->ParticleCels[Y][X]; + real32 Alpha = Clamp01(0.1f*Cel->Density); + PushRect(RenderGroup, GridScale*V3((r32)X, (r32)Y, 0) + GridOrigin, GridScale*V2(1.0f, 1.0f), + V4(Alpha, Alpha, Alpha, 1.0f)); + } + } + } + + for(u32 ParticleIndex = 0; + ParticleIndex < ArrayCount(GameState->Particles); + ++ParticleIndex) + { + particle *Particle = GameState->Particles + ParticleIndex; + + v3 P = InvGridScale*(Particle->P - GridOrigin); + + s32 X = TruncateReal32ToInt32(P.x); + s32 Y = TruncateReal32ToInt32(P.y); + + if(X < 1) {X = 1;} + if(X > (PARTICLE_CEL_DIM - 2)) {X = (PARTICLE_CEL_DIM - 2);} + if(Y < 1) {Y = 1;} + if(Y > (PARTICLE_CEL_DIM - 2)) {Y = (PARTICLE_CEL_DIM - 2);} + + particle_cel *CelCenter = &GameState->ParticleCels[Y][X]; + particle_cel *CelLeft = &GameState->ParticleCels[Y][X - 1]; + particle_cel *CelRight = &GameState->ParticleCels[Y][X + 1]; + particle_cel *CelDown = &GameState->ParticleCels[Y - 1][X]; + particle_cel *CelUp = &GameState->ParticleCels[Y + 1][X]; + + v3 Dispersion = {}; + real32 Dc = 1.0f; + Dispersion += Dc*(CelCenter->Density - CelLeft->Density)*V3(-1.0f, 0.0f, 0.0f); + Dispersion += Dc*(CelCenter->Density - CelRight->Density)*V3(1.0f, 0.0f, 0.0f); + Dispersion += Dc*(CelCenter->Density - CelDown->Density)*V3(0.0f, -1.0f, 0.0f); + Dispersion += Dc*(CelCenter->Density - CelUp->Density)*V3(0.0f, 1.0f, 0.0f); + + v3 ddP = Particle->ddP + Dispersion; + + // NOTE(casey): Simulate the particle forward in time + Particle->P += (0.5f*Square(Input->dtForFrame)*Input->dtForFrame*ddP + + Input->dtForFrame*Particle->dP); + Particle->dP += Input->dtForFrame*ddP; + Particle->Color += Input->dtForFrame*Particle->dColor; + + if(Particle->P.y < 0.0f) + { + r32 CoefficientOfRestitution = 0.3f; + r32 CoefficientOfFriction = 0.7f; + Particle->P.y = -Particle->P.y; + Particle->dP.y = -CoefficientOfRestitution*Particle->dP.y; + Particle->dP.x = CoefficientOfFriction*Particle->dP.x; + } + + // TODO(casey): Shouldn't we just clamp colors in the renderer?? + v4 Color; + Color.r = Clamp01(Particle->Color.r); + Color.g = Clamp01(Particle->Color.g); + Color.b = Clamp01(Particle->Color.b); + Color.a = Clamp01(Particle->Color.a); + + if(Color.a > 0.9f) + { + Color.a = 0.9f*Clamp01MapToRange(1.0f, Color.a, 0.9f); + } + + // NOTE(casey): Render the particle + PushBitmap(RenderGroup, Particle->BitmapID, 1.0f, Particle->P, Color); + } + } + } break; + + case EntityType_Wall: + { + PushBitmap(RenderGroup, GetFirstBitmapFrom(TranState->Assets, Asset_Tree), 2.5f, V3(0, 0, 0)); + } break; + + case EntityType_Stairwell: + { + PushRect(RenderGroup, V3(0, 0, 0), Entity->WalkableDim, V4(1, 0.5f, 0, 1)); + PushRect(RenderGroup, V3(0, 0, Entity->WalkableHeight), Entity->WalkableDim, V4(1, 1, 0, 1)); + } break; + + case EntityType_Sword: + { + PushBitmap(RenderGroup, GetFirstBitmapFrom(TranState->Assets, Asset_Shadow), 0.5f, V3(0, 0, 0), V4(1, 1, 1, ShadowAlpha)); + PushBitmap(RenderGroup, GetFirstBitmapFrom(TranState->Assets, Asset_Sword), 0.5f, V3(0, 0, 0)); + } break; + + case EntityType_Familiar: + { + Entity->tBob += dt; + if(Entity->tBob > Tau32) + { + Entity->tBob -= Tau32; + } + real32 BobSin = Sin(2.0f*Entity->tBob); + PushBitmap(RenderGroup, GetFirstBitmapFrom(TranState->Assets, Asset_Shadow), 2.5f, V3(0, 0, 0), V4(1, 1, 1, (0.5f*ShadowAlpha) + 0.2f*BobSin)); + PushBitmap(RenderGroup, HeroBitmaps.Head, 2.5f, V3(0, 0, 0.25f*BobSin)); + } break; + + case EntityType_Monstar: + { + PushBitmap(RenderGroup, GetFirstBitmapFrom(TranState->Assets, Asset_Shadow), 4.5f, V3(0, 0, 0), V4(1, 1, 1, ShadowAlpha)); + PushBitmap(RenderGroup, HeroBitmaps.Torso, 4.5f, V3(0, 0, 0)); + + DrawHitpoints(Entity, RenderGroup); + } break; + + case EntityType_Space: + { + DEBUG_IF(Simulation_UseSpaceOutlines) + { + for(uint32 VolumeIndex = 0; + VolumeIndex < Entity->Collision->VolumeCount; + ++VolumeIndex) + { + sim_entity_collision_volume *Volume = Entity->Collision->Volumes + VolumeIndex; + PushRectOutline(RenderGroup, Volume->OffsetP - V3(0, 0, 0.5f*Volume->Dim.z), Volume->Dim.xy, V4(0, 0.5f, 1.0f, 1)); + } + } + } break; + + default: + { + InvalidCodePath; + } break; + } + + if(DEBUG_UI_ENABLED) + { + debug_id EntityDebugID = DEBUG_POINTER_ID(GameState->LowEntities + Entity->StorageIndex); + + for(uint32 VolumeIndex = 0; + VolumeIndex < Entity->Collision->VolumeCount; + ++VolumeIndex) + { + sim_entity_collision_volume *Volume = Entity->Collision->Volumes + VolumeIndex; + + v3 LocalMouseP = Unproject(RenderGroup, MouseP); + + if((LocalMouseP.x > -0.5f*Volume->Dim.x) && (LocalMouseP.x < 0.5f*Volume->Dim.x) && + (LocalMouseP.y > -0.5f*Volume->Dim.y) && (LocalMouseP.y < 0.5f*Volume->Dim.y)) + { + DEBUG_HIT(EntityDebugID, LocalMouseP.z); + } + + v4 OutlineColor; + if(DEBUG_HIGHLIGHTED(EntityDebugID, &OutlineColor)) + { + PushRectOutline(RenderGroup, Volume->OffsetP - V3(0, 0, 0.5f*Volume->Dim.z), Volume->Dim.xy, OutlineColor, 0.05f); + } + } + + if(DEBUG_REQUESTED(EntityDebugID)) + { + DEBUG_BEGIN_DATA_BLOCK("Simulation Entity", EntityDebugID); + DEBUG_VALUE(Entity->StorageIndex); + DEBUG_VALUE(Entity->Updatable); + DEBUG_VALUE(Entity->Type); + DEBUG_VALUE(Entity->P); + DEBUG_VALUE(Entity->dP); + DEBUG_VALUE(Entity->DistanceLimit); + DEBUG_VALUE(Entity->FacingDirection); + DEBUG_VALUE(Entity->tBob); + DEBUG_VALUE(Entity->dAbsTileZ); + DEBUG_VALUE(Entity->HitPointMax); + DEBUG_VALUE(HeroBitmaps.Torso); +#if 0 + DEBUG_BEGIN_ARRAY(Entity->HitPoint); + for(u32 HitPointIndex = 0; + HitPointIndex < Entity->HitPointMax; + ++HitPointIndex) + { + DEBUG_VALUE(Entity->HitPoint[HitPointIndex]); + } + DEBUG_END_ARRAY(); + DEBUG_VALUE(Entity->Sword); +#endif + DEBUG_VALUE(Entity->WalkableDim); + DEBUG_VALUE(Entity->WalkableHeight); + DEBUG_END_DATA_BLOCK(); + } + } + } + } + + RenderGroup->GlobalAlpha = 1.0f; + +#if 0 + GameState->Time += Input->dtForFrame; + + v3 MapColor[] = + { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + }; + for(uint32 MapIndex = 0; + MapIndex < ArrayCount(TranState->EnvMaps); + ++MapIndex) + { + environment_map *Map = TranState->EnvMaps + MapIndex; + loaded_bitmap *LOD = Map->LOD + 0; + bool32 RowCheckerOn = false; + int32 CheckerWidth = 16; + int32 CheckerHeight = 16; + rectangle2i ClipRect = {0, 0, LOD->Width, LOD->Height}; + for(int32 Y = 0; + Y < LOD->Height; + Y += CheckerHeight) + { + bool32 CheckerOn = RowCheckerOn; + for(int32 X = 0; + X < LOD->Width; + X += CheckerWidth) + { + v4 Color = CheckerOn ? V4(MapColor[MapIndex], 1.0f) : V4(0, 0, 0, 1); + v2 MinP = V2i(X, Y); + v2 MaxP = MinP + V2i(CheckerWidth, CheckerHeight); + DrawRectangle(LOD, MinP, MaxP, Color, ClipRect, true); + DrawRectangle(LOD, MinP, MaxP, Color, ClipRect, false); + CheckerOn = !CheckerOn; + } + RowCheckerOn = !RowCheckerOn; + } + } + TranState->EnvMaps[0].Pz = -1.5f; + TranState->EnvMaps[1].Pz = 0.0f; + TranState->EnvMaps[2].Pz = 1.5f; + + DrawBitmap(TranState->EnvMaps[0].LOD + 0, + &TranState->GroundBuffers[TranState->GroundBufferCount - 1].Bitmap, + 125.0f, 50.0f, 1.0f); + + +// Angle = 0.0f; + + // TODO(casey): Let's add a perp operator!!! + v2 Origin = ScreenCenter; + + real32 Angle = 0.1f*GameState->Time; +#if 1 + v2 Disp = {100.0f*Cos(5.0f*Angle), + 100.0f*Sin(3.0f*Angle)}; +#else + v2 Disp = {}; +#endif + +#if 1 + v2 XAxis = 100.0f*V2(Cos(10.0f*Angle), Sin(10.0f*Angle)); + v2 YAxis = Perp(XAxis); +#else + v2 XAxis = {100.0f, 0}; + v2 YAxis = {0, 100.0f}; +#endif + uint32 PIndex = 0; + real32 CAngle = 5.0f*Angle; +#if 0 + v4 Color = V4(0.5f+0.5f*Sin(CAngle), + 0.5f+0.5f*Sin(2.9f*CAngle), + 0.5f+0.5f*Cos(9.9f*CAngle), + 0.5f+0.5f*Sin(10.0f*CAngle)); +#else + v4 Color = V4(1.0f, 1.0f, 1.0f, 1.0f); +#endif + CoordinateSystem(RenderGroup, Disp + Origin - 0.5f*XAxis - 0.5f*YAxis, XAxis, YAxis, + Color, + &GameState->TestDiffuse, + &GameState->TestNormal, + TranState->EnvMaps + 2, + TranState->EnvMaps + 1, + TranState->EnvMaps + 0); + v2 MapP = {0.0f, 0.0f}; + for(uint32 MapIndex = 0; + MapIndex < ArrayCount(TranState->EnvMaps); + ++MapIndex) + { + environment_map *Map = TranState->EnvMaps + MapIndex; + loaded_bitmap *LOD = Map->LOD + 0; + + XAxis = 0.5f * V2((real32)LOD->Width, 0.0f); + YAxis = 0.5f * V2(0.0f, (real32)LOD->Height); + + CoordinateSystem(RenderGroup, MapP, XAxis, YAxis, V4(1.0f, 1.0f, 1.0f, 1.0f), LOD, 0, 0, 0, 0); + MapP += YAxis + V2(0.0f, 6.0f); + } +#endif + + Orthographic(RenderGroup, DrawBuffer->Width, DrawBuffer->Height, 1.0f); + + PushRectOutline(RenderGroup, V3(MouseP, 0.0f), V2(2.0f, 2.0f)); + + TiledRenderGroupToOutput(TranState->HighPriorityQueue, RenderGroup, DrawBuffer); + EndRender(RenderGroup); + + // TODO(casey): Make sure we hoist the camera update out to a place where the renderer + // can know about the location of the camera at the end of the frame so there isn't + // a frame of lag in camera updating compared to the hero. + EndSim(SimRegion, GameState); + EndTemporaryMemory(SimMemory); + EndTemporaryMemory(RenderMemory); + + CheckArena(&GameState->WorldArena); + CheckArena(&TranState->TranArena); +} + +extern "C" GAME_GET_SOUND_SAMPLES(GameGetSoundSamples) +{ + game_state *GameState = (game_state *)Memory->PermanentStorage; + transient_state *TranState = (transient_state *)Memory->TransientStorage; + + OutputPlayingSounds(&GameState->AudioState, SoundBuffer, TranState->Assets, &TranState->TranArena); +} + +#if HANDMADE_INTERNAL +#include "handmade_debug.cpp" +#else +extern "C" DEBUG_GAME_FRAME_END(DEBUGGameFrameEnd) +{ + return(0); +} +#endif + diff --git a/test_data/lots_of_files/handmade.h b/test_data/lots_of_files/handmade.h new file mode 100644 index 0000000..b3eb377 --- /dev/null +++ b/test_data/lots_of_files/handmade.h @@ -0,0 +1,456 @@ +#if !defined(HANDMADE_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +/* + TODO(casey): + + - Flush all thread queues before reloading DLL! + + - Debug code + - Logging + - Diagramming + - (A LITTLE GUI, but only a little!) Switches / sliders / etc. + - Draw tile chunks so we can verify that things are aligned / in the chunks we want them to be in / etc. + - Thread visualization + + - Audio + - FIX CLICKING BUG AT END OF SAMPLES!!! + + - Rendering + - Get rid of "even" scan line notion? + - Real projections with solid concept of project/unproject + - Straighten out all coordinate systems! + - Screen + - World + - Texture + - Particle systems + - Lighting + - Final Optimization + + ARCHITECTURE EXPLORATION + - Z! + - Need to make a solid concept of ground levels so the camera can + be freely placed in Z and have mulitple ground levels in one + sim region + - Concept of ground in the collision loop so it can handle + collisions coming onto _and off of_ stairwells, for example. + - Make sure flying things can go over low walls + - How is this rendered? + "Frinstances"! + ZFudge!!!! + - Collision detection? + - Fix sword collisions! + - Clean up predicate proliferation! Can we make a nice clean + set of flags/rules so that it's easy to understand how + things work in terms of special handling? This may involve + making the iteration handle everything instead of handling + overlap outside and so on. + - Transient collision rules! Clear based on flag. + - Allow non-transient rules to override transient ones. + - Entry/exit? + - What's the plan for robustness / shape definition? + - (Implement reprojection to handle interpenetration) + - "Things pushing other things" + - Animation + - Skeletal animation + - Implement multiple sim regions per frame + - Per-entity clocking + - Sim region merging? For multiple players? + - Simple zoomed-out view for testing? + - AI + - Rudimentary monstar behavior example + * Pathfinding + - AI "storage" + + PRODUCTION + -> GAME + - Entity system + - Rudimentary world gen (no quality, just "what sorts of things" we do) + - Placement of background things + - Connectivity? + - Non-overlapping? + - Map display + - Magnets - how they work??? + - Metagame / save game? + - How do you enter "save slot"? + - Persistent unlocks/etc. + - Do we allow saved games? Probably yes, just only for "pausing", + * Continuous save for crash recovery? +*/ + +#include "handmade_platform.h" +#include "handmade_intrinsics.h" +#include "handmade_math.h" +#include "handmade_file_formats.h" +#include "handmade_meta.h" + +#define DLIST_INSERT(Sentinel, Element) \ + (Element)->Next = (Sentinel)->Next; \ + (Element)->Prev = (Sentinel); \ + (Element)->Next->Prev = (Element); \ + (Element)->Prev->Next = (Element); + +#define DLIST_INIT(Sentinel) \ + (Sentinel)->Next = (Sentinel); \ + (Sentinel)->Prev = (Sentinel); + +#define FREELIST_ALLOCATE(type, Result, FreeListPointer, Arena) \ + (Result) = (FreeListPointer); \ + if(Result) {FreeListPointer = (Result)->NextFree;} else {Result = PushStruct(Arena, type);} +#define FREELIST_DEALLOCATE(Pointer, FreeListPointer) \ + if(Pointer) {(Pointer)->NextFree = (FreeListPointer); (FreeListPointer) = (Pointer);} + +struct memory_arena +{ + memory_index Size; + uint8 *Base; + memory_index Used; + + int32 TempCount; +}; + +struct temporary_memory +{ + memory_arena *Arena; + memory_index Used; +}; + +#define Minimum(A, B) ((A < B) ? (A) : (B)) +#define Maximum(A, B) ((A > B) ? (A) : (B)) + +inline b32 +StringsAreEqual(char *A, char *B) +{ + b32 Result = (A == B); + + if(A && B) + { + while(*A && *B && (*A == *B)) + { + ++A; + ++B; + } + + Result = ((*A == 0) && (*B == 0)); + } + + return(Result); +} + +// +// +// + +inline void +InitializeArena(memory_arena *Arena, memory_index Size, void *Base) +{ + Arena->Size = Size; + Arena->Base = (uint8 *)Base; + Arena->Used = 0; + Arena->TempCount = 0; +} + +inline memory_index +GetAlignmentOffset(memory_arena *Arena, memory_index Alignment) +{ + memory_index AlignmentOffset = 0; + + memory_index ResultPointer = (memory_index)Arena->Base + Arena->Used; + memory_index AlignmentMask = Alignment - 1; + if(ResultPointer & AlignmentMask) + { + AlignmentOffset = Alignment - (ResultPointer & AlignmentMask); + } + + return(AlignmentOffset); +} + +inline memory_index +GetArenaSizeRemaining(memory_arena *Arena, memory_index Alignment = 4) +{ + memory_index Result = Arena->Size - (Arena->Used + GetAlignmentOffset(Arena, Alignment)); + + return(Result); +} + +// TODO(casey): Optional "clear" parameter!!!! +#define PushStruct(Arena, type, ...) (type *)PushSize_(Arena, sizeof(type), ## __VA_ARGS__) +#define PushArray(Arena, Count, type, ...) (type *)PushSize_(Arena, (Count)*sizeof(type), ## __VA_ARGS__) +#define PushSize(Arena, Size, ...) PushSize_(Arena, Size, ## __VA_ARGS__) +#define PushCopy(Arena, Size, Source, ...) Copy(Size, Source, PushSize_(Arena, Size, ## __VA_ARGS__)) +inline void * +PushSize_(memory_arena *Arena, memory_index SizeInit, memory_index Alignment = 4) +{ + memory_index Size = SizeInit; + + memory_index AlignmentOffset = GetAlignmentOffset(Arena, Alignment); + Size += AlignmentOffset; + + Assert((Arena->Used + Size) <= Arena->Size); + void *Result = Arena->Base + Arena->Used + AlignmentOffset; + Arena->Used += Size; + + Assert(Size >= SizeInit); + + return(Result); +} + +// NOTE(casey): This is generally not for production use, this is probably +// only really something we need during testing, but who knows +inline char * +PushString(memory_arena *Arena, char *Source) +{ + u32 Size = 1; + for(char *At = Source; + *At; + ++At) + { + ++Size; + } + + char *Dest = (char *)PushSize_(Arena, Size); + for(u32 CharIndex = 0; + CharIndex < Size; + ++CharIndex) + { + Dest[CharIndex] = Source[CharIndex]; + } + + return(Dest); +} + +inline temporary_memory +BeginTemporaryMemory(memory_arena *Arena) +{ + temporary_memory Result; + + Result.Arena = Arena; + Result.Used = Arena->Used; + + ++Arena->TempCount; + + return(Result); +} + +inline void +EndTemporaryMemory(temporary_memory TempMem) +{ + memory_arena *Arena = TempMem.Arena; + Assert(Arena->Used >= TempMem.Used); + Arena->Used = TempMem.Used; + Assert(Arena->TempCount > 0); + --Arena->TempCount; +} + +inline void +CheckArena(memory_arena *Arena) +{ + Assert(Arena->TempCount == 0); +} + +inline void +SubArena(memory_arena *Result, memory_arena *Arena, memory_index Size, memory_index Alignment = 16) +{ + Result->Size = Size; + Result->Base = (uint8 *)PushSize_(Arena, Size, Alignment); + Result->Used = 0; + Result->TempCount = 0; +} + +#define ZeroStruct(Instance) ZeroSize(sizeof(Instance), &(Instance)) +#define ZeroArray(Count, Pointer) ZeroSize(Count*sizeof((Pointer)[0]), Pointer) +inline void +ZeroSize(memory_index Size, void *Ptr) +{ + // TODO(casey): Check this guy for performance + uint8 *Byte = (uint8 *)Ptr; + while(Size--) + { + *Byte++ = 0; + } +} + +inline void * +Copy(memory_index Size, void *SourceInit, void *DestInit) +{ + u8 *Source = (u8 *)SourceInit; + u8 *Dest = (u8 *)DestInit; + while(Size--) {*Dest++ = *Source++;} + + return(DestInit); +} + +#include "handmade_world.h" +#include "handmade_sim_region.h" +#include "handmade_entity.h" +#include "handmade_render_group.h" +#include "handmade_asset.h" +#include "handmade_random.h" +#include "handmade_audio.h" + +struct low_entity +{ + // TODO(casey): It's kind of busted that P's can be invalid here, + // AND we store whether they would be invalid in the flags field... + // Can we do something better here? + world_position P; + sim_entity Sim; +}; + +struct controlled_hero +{ + uint32 EntityIndex; + + // NOTE(casey): These are the controller requests for simulation + v2 ddP; + v2 dSword; + real32 dZ; +}; + +struct pairwise_collision_rule +{ + bool32 CanCollide; + uint32 StorageIndexA; + uint32 StorageIndexB; + + pairwise_collision_rule *NextInHash; +}; +struct game_state; +internal void AddCollisionRule(game_state *GameState, uint32 StorageIndexA, uint32 StorageIndexB, bool32 ShouldCollide); +internal void ClearCollisionRulesFor(game_state *GameState, uint32 StorageIndex); + +struct ground_buffer +{ + // NOTE(casey): An invalid P tells us that this ground_buffer has not been filled + world_position P; // NOTE(casey): This is the center of the bitmap + loaded_bitmap Bitmap; +}; + +struct hero_bitmap_ids +{ + bitmap_id Head; + bitmap_id Cape; + bitmap_id Torso; +}; + +struct particle_cel +{ + real32 Density; + v3 VelocityTimesDensity; +}; +struct particle +{ + bitmap_id BitmapID; + v3 P; + v3 dP; + v3 ddP; + v4 Color; + v4 dColor; +}; + +struct game_state +{ + bool32 IsInitialized; + + memory_arena MetaArena; + memory_arena WorldArena; + world *World; + + real32 TypicalFloorHeight; + + // TODO(casey): Should we allow split-screen? + uint32 CameraFollowingEntityIndex; + world_position CameraP; + world_position LastCameraP; + + controlled_hero ControlledHeroes[ArrayCount(((game_input *)0)->Controllers)]; + + // TODO(casey): Change the name to "stored entity" + uint32 LowEntityCount; + low_entity LowEntities[100000]; + + // TODO(casey): Must be power of two + pairwise_collision_rule *CollisionRuleHash[256]; + pairwise_collision_rule *FirstFreeCollisionRule; + + sim_entity_collision_volume_group *NullCollision; + sim_entity_collision_volume_group *SwordCollision; + sim_entity_collision_volume_group *StairCollision; + sim_entity_collision_volume_group *PlayerCollision; + sim_entity_collision_volume_group *MonstarCollision; + sim_entity_collision_volume_group *FamiliarCollision; + sim_entity_collision_volume_group *WallCollision; + sim_entity_collision_volume_group *StandardRoomCollision; + + real32 Time; + + loaded_bitmap TestDiffuse; // TODO(casey): Re-fill this guy with gray. + loaded_bitmap TestNormal; + + random_series EffectsEntropy; // NOTE(casey): This is entropy that doesn't affect the gameplay + real32 tSine; + + audio_state AudioState; + playing_sound *Music; + +#define PARTICLE_CEL_DIM 32 + u32 NextParticle; + particle Particles[256]; + + particle_cel ParticleCels[PARTICLE_CEL_DIM][PARTICLE_CEL_DIM]; +}; + +struct task_with_memory +{ + bool32 BeingUsed; + memory_arena Arena; + + temporary_memory MemoryFlush; +}; + +struct transient_state +{ + bool32 IsInitialized; + memory_arena TranArena; + + task_with_memory Tasks[4]; + + game_assets *Assets; + + uint32 GroundBufferCount; + ground_buffer *GroundBuffers; + platform_work_queue *HighPriorityQueue; + platform_work_queue *LowPriorityQueue; + + uint32 EnvMapWidth; + uint32 EnvMapHeight; + // NOTE(casey): 0 is bottom, 1 is middle, 2 is top + environment_map EnvMaps[3]; +}; + +inline low_entity * +GetLowEntity(game_state *GameState, uint32 Index) +{ + low_entity *Result = 0; + + if((Index > 0) && (Index < GameState->LowEntityCount)) + { + Result = GameState->LowEntities + Index; + } + + return(Result); +} + +global_variable platform_api Platform; + +internal task_with_memory *BeginTaskWithMemory(transient_state *TranState); +internal void EndTaskWithMemory(task_with_memory *Task); + +#define HANDMADE_H +#endif diff --git a/test_data/lots_of_files/handmade_asset.cpp b/test_data/lots_of_files/handmade_asset.cpp new file mode 100644 index 0000000..5a626b6 --- /dev/null +++ b/test_data/lots_of_files/handmade_asset.cpp @@ -0,0 +1,841 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +enum finalize_asset_operation +{ + FinalizeAsset_None, + FinalizeAsset_Font, +}; +struct load_asset_work +{ + task_with_memory *Task; + asset *Asset; + + platform_file_handle *Handle; + u64 Offset; + u64 Size; + void *Destination; + + finalize_asset_operation FinalizeOperation; + u32 FinalState; +}; +internal void +LoadAssetWorkDirectly(load_asset_work *Work) +{ + TIMED_FUNCTION(); + + Platform.ReadDataFromFile(Work->Handle, Work->Offset, Work->Size, Work->Destination); + if(PlatformNoFileErrors(Work->Handle)) + { + switch(Work->FinalizeOperation) + { + case FinalizeAsset_None: + { + // NOTE(casey): Nothing to do. + } break; + + case FinalizeAsset_Font: + { + loaded_font *Font = &Work->Asset->Header->Font; + hha_font *HHA = &Work->Asset->HHA.Font; + for(u32 GlyphIndex = 1; + GlyphIndex < HHA->GlyphCount; + ++GlyphIndex) + { + hha_font_glyph *Glyph = Font->Glyphs + GlyphIndex; + + Assert(Glyph->UnicodeCodePoint < HHA->OnePastHighestCodepoint); + Assert((u32)(u16)GlyphIndex == GlyphIndex); + Font->UnicodeMap[Glyph->UnicodeCodePoint] = (u16)GlyphIndex; + } + } break; + } + } + + CompletePreviousWritesBeforeFutureWrites; + + if(!PlatformNoFileErrors(Work->Handle)) + { + ZeroSize(Work->Size, Work->Destination); + } + + Work->Asset->State = Work->FinalState; +} +internal PLATFORM_WORK_QUEUE_CALLBACK(LoadAssetWork) +{ + load_asset_work *Work = (load_asset_work *)Data; + + LoadAssetWorkDirectly(Work); + + EndTaskWithMemory(Work->Task); +} + +inline asset_file * +GetFile(game_assets *Assets, u32 FileIndex) +{ + Assert(FileIndex < Assets->FileCount); + asset_file *Result = Assets->Files + FileIndex; + + return(Result); +} + +inline platform_file_handle * +GetFileHandleFor(game_assets *Assets, u32 FileIndex) +{ + platform_file_handle *Result = &GetFile(Assets, FileIndex)->Handle; + + return(Result); +} + +internal asset_memory_block * +InsertBlock(asset_memory_block *Prev, u64 Size, void *Memory) +{ + Assert(Size > sizeof(asset_memory_block)); + asset_memory_block *Block = (asset_memory_block *)Memory; + Block->Flags = 0; + Block->Size = Size - sizeof(asset_memory_block); + Block->Prev = Prev; + Block->Next = Prev->Next; + Block->Prev->Next = Block; + Block->Next->Prev = Block; + return(Block); +} + +internal asset_memory_block * +FindBlockForSize(game_assets *Assets, memory_index Size) +{ + asset_memory_block *Result = 0; + + // TODO(casey): This probably will need to be accelerated in the + // future as the resident asset count grows. + + // TODO(casey): Best match block! + for(asset_memory_block *Block = Assets->MemorySentinel.Next; + Block != &Assets->MemorySentinel; + Block = Block->Next) + { + if(!(Block->Flags & AssetMemory_Used)) + { + if(Block->Size >= Size) + { + Result = Block; + break; + } + } + } + + return(Result); +} + +internal b32 +MergeIfPossible(game_assets *Assets, asset_memory_block *First, asset_memory_block *Second) +{ + b32 Result = false; + + if((First != &Assets->MemorySentinel) && + (Second != &Assets->MemorySentinel)) + { + if(!(First->Flags & AssetMemory_Used) && + !(Second->Flags & AssetMemory_Used)) + { + u8 *ExpectedSecond = (u8 *)First + sizeof(asset_memory_block) + First->Size; + if((u8 *)Second == ExpectedSecond) + { + Second->Next->Prev = Second->Prev; + Second->Prev->Next = Second->Next; + + First->Size += sizeof(asset_memory_block) + Second->Size; + + Result = true; + } + } + } + + return(Result); +} + +internal b32 +GenerationHasCompleted(game_assets *Assets, u32 CheckID) +{ + b32 Result = true; + + for(u32 Index = 0; + Index < Assets->InFlightGenerationCount; + ++Index) + { + if(Assets->InFlightGenerations[Index] == CheckID) + { + Result = false; + break; + } + } + + return(Result); +} + +internal asset_memory_header * +AcquireAssetMemory(game_assets *Assets, u32 Size, u32 AssetIndex) +{ + TIMED_FUNCTION(); + + asset_memory_header *Result = 0; + + BeginAssetLock(Assets); + + asset_memory_block *Block = FindBlockForSize(Assets, Size); + for(;;) + { + if(Block && (Size <= Block->Size)) + { + Block->Flags |= AssetMemory_Used; + + Result = (asset_memory_header *)(Block + 1); + + memory_index RemainingSize = Block->Size - Size; + memory_index BlockSplitThreshold = 4096; // TODO(casey): Set this based on the smallest asset? + if(RemainingSize > BlockSplitThreshold) + { + Block->Size -= RemainingSize; + InsertBlock(Block, RemainingSize, (u8 *)Result + Size); + } + else + { + // TODO(casey): Actually record the unused portion of the memory + // in a block so that we can do the merge on blocks when neighbors + // are freed. + } + + break; + } + else + { + for(asset_memory_header *Header = Assets->LoadedAssetSentinel.Prev; + Header != &Assets->LoadedAssetSentinel; + Header = Header->Prev) + { + asset *Asset = Assets->Assets + Header->AssetIndex; + if((Asset->State >= AssetState_Loaded) && + (GenerationHasCompleted(Assets, Asset->Header->GenerationID))) + { + u32 AssetIndex = Header->AssetIndex; + asset *Asset = Assets->Assets + AssetIndex; + + Assert(Asset->State == AssetState_Loaded); + + RemoveAssetHeaderFromList(Header); + + Block = (asset_memory_block *)Asset->Header - 1; + Block->Flags &= ~AssetMemory_Used; + + if(MergeIfPossible(Assets, Block->Prev, Block)) + { + Block = Block->Prev; + } + + MergeIfPossible(Assets, Block, Block->Next); + + Asset->State = AssetState_Unloaded; + Asset->Header = 0; + break; + } + } + } + } + + if(Result) + { + Result->AssetIndex = AssetIndex; + Result->TotalSize = Size; + InsertAssetHeaderAtFront(Assets, Result); + } + + EndAssetLock(Assets); + + return(Result); +} + +struct asset_memory_size +{ + u32 Total; + u32 Data; + u32 Section; +}; + +internal void +LoadBitmap(game_assets *Assets, bitmap_id ID, b32 Immediate) +{ + TIMED_FUNCTION(); + + asset *Asset = Assets->Assets + ID.Value; + if(ID.Value) + { + if(AtomicCompareExchangeUInt32((uint32 *)&Asset->State, AssetState_Queued, AssetState_Unloaded) == + AssetState_Unloaded) + { + task_with_memory *Task = 0; + + if(!Immediate) + { + Task = BeginTaskWithMemory(Assets->TranState); + } + + if(Immediate || Task) + { + asset *Asset = Assets->Assets + ID.Value; + hha_bitmap *Info = &Asset->HHA.Bitmap; + + asset_memory_size Size = {}; + u32 Width = Info->Dim[0]; + u32 Height = Info->Dim[1]; + Size.Section = 4*Width; + Size.Data = Height*Size.Section; + Size.Total = Size.Data + sizeof(asset_memory_header); + + Asset->Header = AcquireAssetMemory(Assets, Size.Total, ID.Value); + + loaded_bitmap *Bitmap = &Asset->Header->Bitmap; + Bitmap->AlignPercentage = V2(Info->AlignPercentage[0], Info->AlignPercentage[1]); + Bitmap->WidthOverHeight = (r32)Info->Dim[0] / (r32)Info->Dim[1]; + Bitmap->Width = Info->Dim[0]; + Bitmap->Height = Info->Dim[1]; + Bitmap->Pitch = Size.Section; + Bitmap->Memory = (Asset->Header + 1); + + load_asset_work Work; + Work.Task = Task; + Work.Asset = Assets->Assets + ID.Value; + Work.Handle = GetFileHandleFor(Assets, Asset->FileIndex); + Work.Offset = Asset->HHA.DataOffset; + Work.Size = Size.Data; + Work.Destination = Bitmap->Memory; + Work.FinalizeOperation = FinalizeAsset_None; + Work.FinalState = AssetState_Loaded; + if(Task) + { + load_asset_work *TaskWork = PushStruct(&Task->Arena, load_asset_work); + *TaskWork = Work; + Platform.AddEntry(Assets->TranState->LowPriorityQueue, LoadAssetWork, TaskWork); + } + else + { + LoadAssetWorkDirectly(&Work); + } + } + else + { + Asset->State = AssetState_Unloaded; + } + } + else if(Immediate) + { + // TODO(casey): Do we want to have a more coherent story here + // for what happens when two force-load people hit the load + // at the same time? + asset_state volatile *State = (asset_state volatile *)&Asset->State; + while(*State == AssetState_Queued) {} + } + } +} + +internal void +LoadSound(game_assets *Assets, sound_id ID) +{ + TIMED_FUNCTION(); + + asset *Asset = Assets->Assets + ID.Value; + if(ID.Value && + (AtomicCompareExchangeUInt32((uint32 *)&Asset->State, AssetState_Queued, AssetState_Unloaded) == + AssetState_Unloaded)) + { + task_with_memory *Task = BeginTaskWithMemory(Assets->TranState); + if(Task) + { + asset *Asset = Assets->Assets + ID.Value; + hha_sound *Info = &Asset->HHA.Sound; + + asset_memory_size Size = {}; + Size.Section = Info->SampleCount*sizeof(int16); + Size.Data = Info->ChannelCount*Size.Section; + Size.Total = Size.Data + sizeof(asset_memory_header); + + Asset->Header = (asset_memory_header *)AcquireAssetMemory(Assets, Size.Total, ID.Value); + loaded_sound *Sound = &Asset->Header->Sound; + + Sound->SampleCount = Info->SampleCount; + Sound->ChannelCount = Info->ChannelCount; + u32 ChannelSize = Size.Section; + + void *Memory = (Asset->Header + 1); + int16 *SoundAt = (int16 *)Memory; + for(u32 ChannelIndex = 0; + ChannelIndex < Sound->ChannelCount; + ++ChannelIndex) + { + Sound->Samples[ChannelIndex] = SoundAt; + SoundAt += ChannelSize; + } + + load_asset_work *Work = PushStruct(&Task->Arena, load_asset_work); + Work->Task = Task; + Work->Asset = Assets->Assets + ID.Value; + Work->Handle = GetFileHandleFor(Assets, Asset->FileIndex); + Work->Offset = Asset->HHA.DataOffset; + Work->Size = Size.Data; + Work->Destination = Memory; + Work->FinalizeOperation = FinalizeAsset_None; + Work->FinalState = (AssetState_Loaded); + + Platform.AddEntry(Assets->TranState->LowPriorityQueue, LoadAssetWork, Work); + } + else + { + Assets->Assets[ID.Value].State = AssetState_Unloaded; + } + } +} + +internal void +LoadFont(game_assets *Assets, font_id ID, b32 Immediate) +{ + TIMED_FUNCTION(); + + // TODO(casey): Merge all this boilerplate!!!! Same between LoadBitmap, LoadSound, and LoadFont + asset *Asset = Assets->Assets + ID.Value; + if(ID.Value) + { + if(AtomicCompareExchangeUInt32((uint32 *)&Asset->State, AssetState_Queued, AssetState_Unloaded) == + AssetState_Unloaded) + { + task_with_memory *Task = 0; + + if(!Immediate) + { + Task = BeginTaskWithMemory(Assets->TranState); + } + + if(Immediate || Task) + { + asset *Asset = Assets->Assets + ID.Value; + hha_font *Info = &Asset->HHA.Font; + + u32 HorizontalAdvanceSize = sizeof(r32)*Info->GlyphCount*Info->GlyphCount; + u32 GlyphsSize = Info->GlyphCount*sizeof(hha_font_glyph); + u32 UnicodeMapSize = sizeof(u16)*Info->OnePastHighestCodepoint; + u32 SizeData = GlyphsSize + HorizontalAdvanceSize; + u32 SizeTotal = SizeData + sizeof(asset_memory_header) + UnicodeMapSize; + + Asset->Header = AcquireAssetMemory(Assets, SizeTotal, ID.Value); + + loaded_font *Font = &Asset->Header->Font; + Font->BitmapIDOffset = GetFile(Assets, Asset->FileIndex)->FontBitmapIDOffset; + Font->Glyphs = (hha_font_glyph *)(Asset->Header + 1); + Font->HorizontalAdvance = (r32 *)((u8 *)Font->Glyphs + GlyphsSize); + Font->UnicodeMap = (u16 *)((u8 *)Font->HorizontalAdvance + HorizontalAdvanceSize); + + ZeroSize(UnicodeMapSize, Font->UnicodeMap); + + load_asset_work Work; + Work.Task = Task; + Work.Asset = Assets->Assets + ID.Value; + Work.Handle = GetFileHandleFor(Assets, Asset->FileIndex); + Work.Offset = Asset->HHA.DataOffset; + Work.Size = SizeData; + Work.Destination = Font->Glyphs; + Work.FinalizeOperation = FinalizeAsset_Font; + Work.FinalState = AssetState_Loaded; + if(Task) + { + load_asset_work *TaskWork = PushStruct(&Task->Arena, load_asset_work); + *TaskWork = Work; + Platform.AddEntry(Assets->TranState->LowPriorityQueue, LoadAssetWork, TaskWork); + } + else + { + LoadAssetWorkDirectly(&Work); + } + } + else + { + Asset->State = AssetState_Unloaded; + } + } + else if(Immediate) + { + // TODO(casey): Do we want to have a more coherent story here + // for what happens when two force-load people hit the load + // at the same time? + asset_state volatile *State = (asset_state volatile *)&Asset->State; + while(*State == AssetState_Queued) {} + } + } +} + +internal uint32 +GetBestMatchAssetFrom(game_assets *Assets, asset_type_id TypeID, + asset_vector *MatchVector, asset_vector *WeightVector) +{ + TIMED_FUNCTION(); + + uint32 Result = 0; + + real32 BestDiff = Real32Maximum; + asset_type *Type = Assets->AssetTypes + TypeID; + for(uint32 AssetIndex = Type->FirstAssetIndex; + AssetIndex < Type->OnePastLastAssetIndex; + ++AssetIndex) + { + asset *Asset = Assets->Assets + AssetIndex; + + real32 TotalWeightedDiff = 0.0f; + for(uint32 TagIndex = Asset->HHA.FirstTagIndex; + TagIndex < Asset->HHA.OnePastLastTagIndex; + ++TagIndex) + { + hha_tag *Tag = Assets->Tags + TagIndex; + + real32 A = MatchVector->E[Tag->ID]; + real32 B = Tag->Value; + real32 D0 = AbsoluteValue(A - B); + real32 D1 = AbsoluteValue((A - Assets->TagRange[Tag->ID]*SignOf(A)) - B); + real32 Difference = Minimum(D0, D1); + + real32 Weighted = WeightVector->E[Tag->ID]*Difference; + TotalWeightedDiff += Weighted; + } + + if(BestDiff > TotalWeightedDiff) + { + BestDiff = TotalWeightedDiff; + Result = AssetIndex; + } + } + + return(Result); +} + +internal uint32 +GetRandomAssetFrom(game_assets *Assets, asset_type_id TypeID, random_series *Series) +{ + TIMED_FUNCTION(); + + uint32 Result = 0; + + asset_type *Type = Assets->AssetTypes + TypeID; + if(Type->FirstAssetIndex != Type->OnePastLastAssetIndex) + { + uint32 Count = (Type->OnePastLastAssetIndex - Type->FirstAssetIndex); + uint32 Choice = RandomChoice(Series, Count); + Result = Type->FirstAssetIndex + Choice; + } + + return(Result); +} + +internal uint32 +GetFirstAssetFrom(game_assets *Assets, asset_type_id TypeID) +{ + TIMED_FUNCTION(); + + uint32 Result = 0; + + asset_type *Type = Assets->AssetTypes + TypeID; + if(Type->FirstAssetIndex != Type->OnePastLastAssetIndex) + { + Result = Type->FirstAssetIndex; + } + + return(Result); +} + +inline bitmap_id +GetBestMatchBitmapFrom(game_assets *Assets, asset_type_id TypeID, + asset_vector *MatchVector, asset_vector *WeightVector) +{ + bitmap_id Result = {GetBestMatchAssetFrom(Assets, TypeID, MatchVector, WeightVector)}; + return(Result); +} + +inline bitmap_id +GetFirstBitmapFrom(game_assets *Assets, asset_type_id TypeID) +{ + bitmap_id Result = {GetFirstAssetFrom(Assets, TypeID)}; + return(Result); +} + +inline bitmap_id +GetRandomBitmapFrom(game_assets *Assets, asset_type_id TypeID, random_series *Series) +{ + bitmap_id Result = {GetRandomAssetFrom(Assets, TypeID, Series)}; + return(Result); +} + +inline sound_id +GetBestMatchSoundFrom(game_assets *Assets, asset_type_id TypeID, + asset_vector *MatchVector, asset_vector *WeightVector) +{ + sound_id Result = {GetBestMatchAssetFrom(Assets, TypeID, MatchVector, WeightVector)}; + return(Result); +} + +inline sound_id +GetFirstSoundFrom(game_assets *Assets, asset_type_id TypeID) +{ + sound_id Result = {GetFirstAssetFrom(Assets, TypeID)}; + return(Result); +} + +inline sound_id +GetRandomSoundFrom(game_assets *Assets, asset_type_id TypeID, random_series *Series) +{ + sound_id Result = {GetRandomAssetFrom(Assets, TypeID, Series)}; + return(Result); +} + +internal font_id +GetBestMatchFontFrom(game_assets *Assets, asset_type_id TypeID, asset_vector *MatchVector, asset_vector *WeightVector) +{ + font_id Result = {GetBestMatchAssetFrom(Assets, TypeID, MatchVector, WeightVector)}; + return(Result); +} + +internal game_assets * +AllocateGameAssets(memory_arena *Arena, memory_index Size, transient_state *TranState) +{ + TIMED_FUNCTION(); + + game_assets *Assets = PushStruct(Arena, game_assets); + + Assets->NextGenerationID = 0; + Assets->InFlightGenerationCount = 0; + + Assets->MemorySentinel.Flags = 0; + Assets->MemorySentinel.Size = 0; + Assets->MemorySentinel.Prev = &Assets->MemorySentinel; + Assets->MemorySentinel.Next = &Assets->MemorySentinel; + + InsertBlock(&Assets->MemorySentinel, Size, PushSize(Arena, Size)); + + Assets->TranState = TranState; + + Assets->LoadedAssetSentinel.Next = + Assets->LoadedAssetSentinel.Prev = + &Assets->LoadedAssetSentinel; + + for(uint32 TagType = 0; + TagType < Tag_Count; + ++TagType) + { + Assets->TagRange[TagType] = 1000000.0f; + } + Assets->TagRange[Tag_FacingDirection] = Tau32; + + Assets->TagCount = 1; + Assets->AssetCount = 1; + + // NOTE(casey): This code was written using Snuffleupagus-Oriented Programming (SOP) + { + platform_file_group FileGroup = Platform.GetAllFilesOfTypeBegin(PlatformFileType_AssetFile); + Assets->FileCount = FileGroup.FileCount; + Assets->Files = PushArray(Arena, Assets->FileCount, asset_file); + for(u32 FileIndex = 0; + FileIndex < Assets->FileCount; + ++FileIndex) + { + asset_file *File = Assets->Files + FileIndex; + + File->FontBitmapIDOffset = 0; + File->TagBase = Assets->TagCount; + + ZeroStruct(File->Header); + File->Handle = Platform.OpenNextFile(&FileGroup); + Platform.ReadDataFromFile(&File->Handle, 0, sizeof(File->Header), &File->Header); + + u32 AssetTypeArraySize = File->Header.AssetTypeCount*sizeof(hha_asset_type); + File->AssetTypeArray = (hha_asset_type *)PushSize(Arena, AssetTypeArraySize); + Platform.ReadDataFromFile(&File->Handle, File->Header.AssetTypes, + AssetTypeArraySize, File->AssetTypeArray); + + if(File->Header.MagicValue != HHA_MAGIC_VALUE) + { + Platform.FileError(&File->Handle, "HHA file has an invalid magic value."); + } + + if(File->Header.Version > HHA_VERSION) + { + Platform.FileError(&File->Handle, "HHA file is of a later version."); + } + + if(PlatformNoFileErrors(&File->Handle)) + { + // NOTE(casey): The first asset and tag slot in every + // HHA is a null (reserved) so we don't count it as + // something we will need space for! + Assets->TagCount += (File->Header.TagCount - 1); + Assets->AssetCount += (File->Header.AssetCount - 1); + } + else + { + // TODO(casey): Eventually, have some way of notifying users of bogus files? + InvalidCodePath; + } + } + Platform.GetAllFilesOfTypeEnd(&FileGroup); + } + + // NOTE(casey): Allocate all metadata space + Assets->Assets = PushArray(Arena, Assets->AssetCount, asset); + Assets->Tags = PushArray(Arena, Assets->TagCount, hha_tag); + + // NOTE(casey): Reserve one null tag at the beginning + ZeroStruct(Assets->Tags[0]); + + // NOTE(casey): Load tags + for(u32 FileIndex = 0; + FileIndex < Assets->FileCount; + ++FileIndex) + { + asset_file *File = Assets->Files + FileIndex; + if(PlatformNoFileErrors(&File->Handle)) + { + // NOTE(casey): Skip the first tag, since it's null + u32 TagArraySize = sizeof(hha_tag)*(File->Header.TagCount - 1); + Platform.ReadDataFromFile(&File->Handle, File->Header.Tags + sizeof(hha_tag), + TagArraySize, Assets->Tags + File->TagBase); + } + } + + // NOTE(casey): Reserve one null asset at the beginning + u32 AssetCount = 0; + ZeroStruct(*(Assets->Assets + AssetCount)); + ++AssetCount; + + // TODO(casey): Excersize for the reader - how would you do this in a way + // that scaled gracefully to hundreds of asset pack files? (or more!) + for(u32 DestTypeID = 0; + DestTypeID < Asset_Count; + ++DestTypeID) + { + asset_type *DestType = Assets->AssetTypes + DestTypeID; + DestType->FirstAssetIndex = AssetCount; + + for(u32 FileIndex = 0; + FileIndex < Assets->FileCount; + ++FileIndex) + { + asset_file *File = Assets->Files + FileIndex; + if(PlatformNoFileErrors(&File->Handle)) + { + for(u32 SourceIndex = 0; + SourceIndex < File->Header.AssetTypeCount; + ++SourceIndex) + { + hha_asset_type *SourceType = File->AssetTypeArray + SourceIndex; + + if(SourceType->TypeID == DestTypeID) + { + if(SourceType->TypeID == Asset_FontGlyph) + { + File->FontBitmapIDOffset = AssetCount - SourceType->FirstAssetIndex; + } + + u32 AssetCountForType = (SourceType->OnePastLastAssetIndex - + SourceType->FirstAssetIndex); + + temporary_memory TempMem = BeginTemporaryMemory(&TranState->TranArena); + hha_asset *HHAAssetArray = PushArray(&TranState->TranArena, + AssetCountForType, hha_asset); + Platform.ReadDataFromFile(&File->Handle, + File->Header.Assets + + SourceType->FirstAssetIndex*sizeof(hha_asset), + AssetCountForType*sizeof(hha_asset), + HHAAssetArray); + for(u32 AssetIndex = 0; + AssetIndex < AssetCountForType; + ++AssetIndex) + { + hha_asset *HHAAsset = HHAAssetArray + AssetIndex; + + Assert(AssetCount < Assets->AssetCount); + asset *Asset = Assets->Assets + AssetCount++; + + Asset->FileIndex = FileIndex; + Asset->HHA = *HHAAsset; + if(Asset->HHA.FirstTagIndex == 0) + { + Asset->HHA.FirstTagIndex = Asset->HHA.OnePastLastTagIndex = 0; + } + else + { + Asset->HHA.FirstTagIndex += (File->TagBase - 1); + Asset->HHA.OnePastLastTagIndex += (File->TagBase - 1); + } + } + + EndTemporaryMemory(TempMem); + } + } + } + } + + DestType->OnePastLastAssetIndex = AssetCount; + } + + Assert(AssetCount == Assets->AssetCount); + + return(Assets); +} + +inline u32 +GetGlyphFromCodePoint(hha_font *Info, loaded_font *Font, u32 CodePoint) +{ + u32 Result = 0; + if(CodePoint < Info->OnePastHighestCodepoint) + { + Result = Font->UnicodeMap[CodePoint]; + Assert(Result < Info->GlyphCount); + } + + return(Result); +} + +internal r32 +GetHorizontalAdvanceForPair(hha_font *Info, loaded_font *Font, u32 DesiredPrevCodePoint, u32 DesiredCodePoint) +{ + u32 PrevGlyph = GetGlyphFromCodePoint(Info, Font, DesiredPrevCodePoint); + u32 Glyph = GetGlyphFromCodePoint(Info, Font, DesiredCodePoint); + + r32 Result = Font->HorizontalAdvance[PrevGlyph*Info->GlyphCount + Glyph]; + + return(Result); +} + +internal bitmap_id +GetBitmapForGlyph(game_assets *Assets, hha_font *Info, loaded_font *Font, u32 DesiredCodePoint) +{ + u32 Glyph = GetGlyphFromCodePoint(Info, Font, DesiredCodePoint); + bitmap_id Result = Font->Glyphs[Glyph].BitmapID; + Result.Value += Font->BitmapIDOffset; + + return(Result); +} + +internal r32 +GetLineAdvanceFor(hha_font *Info) +{ + r32 Result = Info->AscenderHeight + Info->DescenderHeight + Info->ExternalLeading; + + return(Result); +} + +internal r32 +GetStartingBaselineY(hha_font *Info) +{ + r32 Result = Info->AscenderHeight; + + return(Result); +} diff --git a/test_data/lots_of_files/handmade_asset.h b/test_data/lots_of_files/handmade_asset.h new file mode 100644 index 0000000..7521d4c --- /dev/null +++ b/test_data/lots_of_files/handmade_asset.h @@ -0,0 +1,338 @@ +#if !defined(HANDMADE_ASSET_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +struct loaded_sound +{ + int16 *Samples[2]; + u32 SampleCount; // NOTE(casey): This is the sample count divided by 8 + u32 ChannelCount; +}; + +struct loaded_font +{ + hha_font_glyph *Glyphs; + r32 *HorizontalAdvance; + u32 BitmapIDOffset; + u16 *UnicodeMap; +}; + +// TODO(casey): Streamling this, by using header pointer as an indicator of unloaded status? +enum asset_state +{ + AssetState_Unloaded, + AssetState_Queued, + AssetState_Loaded, +}; + +struct asset_memory_header +{ + asset_memory_header *Next; + asset_memory_header *Prev; + + u32 AssetIndex; + u32 TotalSize; + u32 GenerationID; + union + { + loaded_bitmap Bitmap; + loaded_sound Sound; + loaded_font Font; + }; +}; + +struct asset +{ + u32 State; + asset_memory_header *Header; + + hha_asset HHA; + u32 FileIndex; +}; + +struct asset_vector +{ + real32 E[Tag_Count]; +}; + +struct asset_type +{ + uint32 FirstAssetIndex; + uint32 OnePastLastAssetIndex; +}; + +struct asset_file +{ + platform_file_handle Handle; + + // TODO(casey): If we ever do thread stacks, AssetTypeArray + // doesn't actually need to be kept here probably. + hha_header Header; + hha_asset_type *AssetTypeArray; + + u32 TagBase; + s32 FontBitmapIDOffset; +}; + +enum asset_memory_block_flags +{ + AssetMemory_Used = 0x1, +}; +struct asset_memory_block +{ + asset_memory_block *Prev; + asset_memory_block *Next; + u64 Flags; + memory_index Size; +}; + +struct game_assets +{ + u32 NextGenerationID; + + // TODO(casey): Not thrilled about this back-pointer + struct transient_state *TranState; + + asset_memory_block MemorySentinel; + + asset_memory_header LoadedAssetSentinel; + + real32 TagRange[Tag_Count]; + + u32 FileCount; + asset_file *Files; + + uint32 TagCount; + hha_tag *Tags; + + uint32 AssetCount; + asset *Assets; + + asset_type AssetTypes[Asset_Count]; + + u32 OperationLock; + + u32 InFlightGenerationCount; + u32 InFlightGenerations[16]; +}; + +inline void +BeginAssetLock(game_assets *Assets) +{ + for(;;) + { + if(AtomicCompareExchangeUInt32(&Assets->OperationLock, 1, 0) == 0) + { + break; + } + } +} + +inline void +EndAssetLock(game_assets *Assets) +{ + CompletePreviousWritesBeforeFutureWrites; + Assets->OperationLock = 0; +} + +inline void +InsertAssetHeaderAtFront(game_assets *Assets, asset_memory_header *Header) +{ + asset_memory_header *Sentinel = &Assets->LoadedAssetSentinel; + + Header->Prev = Sentinel; + Header->Next = Sentinel->Next; + + Header->Next->Prev = Header; + Header->Prev->Next = Header; +} + +inline void +RemoveAssetHeaderFromList(asset_memory_header *Header) +{ + Header->Prev->Next = Header->Next; + Header->Next->Prev = Header->Prev; + + Header->Next = Header->Prev = 0; +} + +inline asset_memory_header *GetAsset(game_assets *Assets, u32 ID, u32 GenerationID) +{ + Assert(ID <= Assets->AssetCount); + asset *Asset = Assets->Assets + ID; + + asset_memory_header *Result = 0; + + BeginAssetLock(Assets); + + if(Asset->State == AssetState_Loaded) + { + Result = Asset->Header; + RemoveAssetHeaderFromList(Result); + InsertAssetHeaderAtFront(Assets, Result); + + if(Asset->Header->GenerationID < GenerationID) + { + Asset->Header->GenerationID = GenerationID; + } + + CompletePreviousWritesBeforeFutureWrites; + } + + EndAssetLock(Assets); + + return(Result); +} + +inline loaded_bitmap * +GetBitmap(game_assets *Assets, bitmap_id ID, u32 GenerationID) +{ + asset_memory_header *Header = GetAsset(Assets, ID.Value, GenerationID); + + loaded_bitmap *Result = Header ? &Header->Bitmap : 0; + + return(Result); +} + +inline hha_bitmap * +GetBitmapInfo(game_assets *Assets, bitmap_id ID) +{ + Assert(ID.Value <= Assets->AssetCount); + hha_bitmap *Result = &Assets->Assets[ID.Value].HHA.Bitmap; + + return(Result); +} + +inline loaded_sound * +GetSound(game_assets *Assets, sound_id ID, u32 GenerationID) +{ + asset_memory_header *Header = GetAsset(Assets, ID.Value, GenerationID); + + loaded_sound *Result = Header ? &Header->Sound : 0; + + return(Result); +} + +inline hha_sound * +GetSoundInfo(game_assets *Assets, sound_id ID) +{ + Assert(ID.Value <= Assets->AssetCount); + hha_sound *Result = &Assets->Assets[ID.Value].HHA.Sound; + + return(Result); +} + +inline loaded_font * +GetFont(game_assets *Assets, font_id ID, u32 GenerationID) +{ + asset_memory_header *Header = GetAsset(Assets, ID.Value, GenerationID); + + loaded_font *Result = Header ? &Header->Font : 0; + + return(Result); +} + +inline hha_font * +GetFontInfo(game_assets *Assets, font_id ID) +{ + Assert(ID.Value <= Assets->AssetCount); + hha_font *Result = &Assets->Assets[ID.Value].HHA.Font; + + return(Result); +} + +inline bool32 +IsValid(bitmap_id ID) +{ + bool32 Result = (ID.Value != 0); + + return(Result); +} + +inline bool32 +IsValid(sound_id ID) +{ + bool32 Result = (ID.Value != 0); + + return(Result); +} + +internal void LoadBitmap(game_assets *Assets, bitmap_id ID, b32 Immediate); +inline void PrefetchBitmap(game_assets *Assets, bitmap_id ID) {LoadBitmap(Assets, ID, false);} +internal void LoadSound(game_assets *Assets, sound_id ID); +inline void PrefetchSound(game_assets *Assets, sound_id ID) {LoadSound(Assets, ID);} +internal void LoadFont(game_assets *Assets, font_id ID, b32 Immediate); +inline void PrefetchFont(game_assets *Assets, font_id ID) {LoadFont(Assets, ID, false);} + +inline sound_id GetNextSoundInChain(game_assets *Assets, sound_id ID) +{ + sound_id Result = {}; + + hha_sound *Info = GetSoundInfo(Assets, ID); + switch(Info->Chain) + { + case HHASoundChain_None: + { + // NOTE(casey): Nothing to do. + } break; + + case HHASoundChain_Loop: + { + Result = ID; + } break; + + case HHASoundChain_Advance: + { + Result.Value = ID.Value + 1; + } break; + + default: + { + InvalidCodePath; + } break; + } + + return(Result); +} + +inline u32 +BeginGeneration(game_assets *Assets) +{ + BeginAssetLock(Assets); + + Assert(Assets->InFlightGenerationCount < ArrayCount(Assets->InFlightGenerations)); + u32 Result = Assets->NextGenerationID++; + Assets->InFlightGenerations[Assets->InFlightGenerationCount++] = Result; + + EndAssetLock(Assets); + + return(Result); +} + +inline void +EndGeneration(game_assets *Assets, u32 GenerationID) +{ + BeginAssetLock(Assets); + + for(u32 Index = 0; + Index < Assets->InFlightGenerationCount; + ++Index) + { + if(Assets->InFlightGenerations[Index] == GenerationID) + { + Assets->InFlightGenerations[Index] = + Assets->InFlightGenerations[--Assets->InFlightGenerationCount]; + break; + } + } + + EndAssetLock(Assets); +} + +#define HANDMADE_ASSET_H +#endif diff --git a/test_data/lots_of_files/handmade_audio.cpp b/test_data/lots_of_files/handmade_audio.cpp new file mode 100644 index 0000000..3d240e4 --- /dev/null +++ b/test_data/lots_of_files/handmade_audio.cpp @@ -0,0 +1,349 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +internal void +OutputTestSineWave(game_state *GameState, game_sound_output_buffer *SoundBuffer, int ToneHz) +{ + int16 ToneVolume = 3000; + int WavePeriod = SoundBuffer->SamplesPerSecond/ToneHz; + + int16 *SampleOut = SoundBuffer->Samples; + for(int SampleIndex = 0; + SampleIndex < SoundBuffer->SampleCount; + ++SampleIndex) + { + // TODO(casey): Draw this out for people +#if 1 + real32 SineValue = sinf(GameState->tSine); + int16 SampleValue = (int16)(SineValue * ToneVolume); +#else + int16 SampleValue = 0; +#endif + *SampleOut++ = SampleValue; + *SampleOut++ = SampleValue; + +#if 1 + GameState->tSine += Tau32*1.0f/(real32)WavePeriod; + if(GameState->tSine > Tau32) + { + GameState->tSine -= Tau32; + } +#endif + } +} + +internal playing_sound * +PlaySound(audio_state *AudioState, sound_id SoundID) +{ + TIMED_FUNCTION(); + + if(!AudioState->FirstFreePlayingSound) + { + AudioState->FirstFreePlayingSound = PushStruct(AudioState->PermArena, playing_sound); + AudioState->FirstFreePlayingSound->Next = 0; + } + + playing_sound *PlayingSound = AudioState->FirstFreePlayingSound; + AudioState->FirstFreePlayingSound = PlayingSound->Next; + + PlayingSound->SamplesPlayed = 0; + // TODO(casey): Should these default to 0.5f/0.5f for centerred? + PlayingSound->CurrentVolume = PlayingSound->TargetVolume = V2(1.0f, 1.0f); + PlayingSound->dCurrentVolume = V2(0, 0); + PlayingSound->ID = SoundID; + PlayingSound->dSample = 1.0f; + + PlayingSound->Next = AudioState->FirstPlayingSound; + AudioState->FirstPlayingSound = PlayingSound; + + return(PlayingSound); +} + +internal void +ChangeVolume(audio_state *AudioState, playing_sound *Sound, real32 FadeDurationInSeconds, v2 Volume) +{ + if(Sound) + { + if(FadeDurationInSeconds <= 0.0f) + { + Sound->CurrentVolume = Sound->TargetVolume = Volume; + } + else + { + real32 OneOverFade = 1.0f / FadeDurationInSeconds; + Sound->TargetVolume = Volume; + Sound->dCurrentVolume = OneOverFade*(Sound->TargetVolume - Sound->CurrentVolume); + } + } +} + +internal void +ChangePitch(audio_state *AudioState, playing_sound *Sound, real32 dSample) +{ + if(Sound) + { + Sound->dSample = dSample; + } +} + +internal void +OutputPlayingSounds(audio_state *AudioState, + game_sound_output_buffer *SoundBuffer, game_assets *Assets, + memory_arena *TempArena) +{ + TIMED_FUNCTION(); + + temporary_memory MixerMemory = BeginTemporaryMemory(TempArena); + + u32 GenerationID = BeginGeneration(Assets); + + Assert((SoundBuffer->SampleCount & 3) == 0); + u32 ChunkCount = SoundBuffer->SampleCount / 4; + + __m128 *RealChannel0 = PushArray(TempArena, ChunkCount, __m128, 16); + __m128 *RealChannel1 = PushArray(TempArena, ChunkCount, __m128, 16); + + real32 SecondsPerSample = 1.0f / (real32)SoundBuffer->SamplesPerSecond; +#define AudioStateOutputChannelCount 2 + + __m128 One = _mm_set1_ps(1.0f); + __m128 Zero = _mm_set1_ps(0.0f); + + // NOTE(casey): Clear out the mixer channels + { + __m128 *Dest0 = RealChannel0; + __m128 *Dest1 = RealChannel1; + for(u32 SampleIndex = 0; + SampleIndex < ChunkCount; + ++SampleIndex) + { + _mm_store_ps((float *)Dest0++, Zero); + _mm_store_ps((float *)Dest1++, Zero); + } + } + + // NOTE(casey): Sum all sounds + for(playing_sound **PlayingSoundPtr = &AudioState->FirstPlayingSound; + *PlayingSoundPtr; + ) + { + playing_sound *PlayingSound = *PlayingSoundPtr; + bool32 SoundFinished = false; + + uint32 TotalChunksToMix = ChunkCount; + __m128 *Dest0 = RealChannel0; + __m128 *Dest1 = RealChannel1; + + while(TotalChunksToMix && !SoundFinished) + { + loaded_sound *LoadedSound = GetSound(Assets, PlayingSound->ID, GenerationID); + if(LoadedSound) + { + sound_id NextSoundInChain = GetNextSoundInChain(Assets, PlayingSound->ID); + PrefetchSound(Assets, NextSoundInChain); + + v2 Volume = PlayingSound->CurrentVolume; + v2 dVolume = SecondsPerSample*PlayingSound->dCurrentVolume; + v2 dVolumeChunk = 4.0f*dVolume; + real32 dSample = PlayingSound->dSample; + // TODO(casey): If you use the 1.9f version, it can clearly repro a bug + // in the accuracy of the samples played calcs! +// real32 dSample = PlayingSound->dSample*1.9f; + real32 dSampleChunk = 4.0f*dSample; + + // NOTE(casey): Channel 0 + __m128 MasterVolume0 = _mm_set1_ps(AudioState->MasterVolume.E[0]); + __m128 Volume0 = _mm_setr_ps(Volume.E[0] + 0.0f*dVolume.E[0], + Volume.E[0] + 1.0f*dVolume.E[0], + Volume.E[0] + 2.0f*dVolume.E[0], + Volume.E[0] + 3.0f*dVolume.E[0]); + __m128 dVolume0 = _mm_set1_ps(dVolume.E[0]); + __m128 dVolumeChunk0 = _mm_set1_ps(dVolumeChunk.E[0]); + + // NOTE(casey): Channel 1 + __m128 MasterVolume1 = _mm_set1_ps(AudioState->MasterVolume.E[1]); + __m128 Volume1 = _mm_setr_ps(Volume.E[1] + 0.0f*dVolume.E[1], + Volume.E[1] + 1.0f*dVolume.E[1], + Volume.E[1] + 2.0f*dVolume.E[1], + Volume.E[1] + 3.0f*dVolume.E[1]); + __m128 dVolume1 = _mm_set1_ps(dVolume.E[1]); + __m128 dVolumeChunk1 = _mm_set1_ps(dVolumeChunk.E[1]); + + Assert(PlayingSound->SamplesPlayed >= 0.0f); + + u32 ChunksToMix = TotalChunksToMix; + r32 RealChunksRemainingInSound = + (LoadedSound->SampleCount - RoundReal32ToInt32(PlayingSound->SamplesPlayed)) / dSampleChunk; + u32 ChunksRemainingInSound = RoundReal32ToInt32(RealChunksRemainingInSound); + if(ChunksToMix > ChunksRemainingInSound) + { + ChunksToMix = ChunksRemainingInSound; + } + + u32 VolumeEndsAt[AudioStateOutputChannelCount] = {}; + for(u32 ChannelIndex = 0; + ChannelIndex < ArrayCount(VolumeEndsAt); + ++ChannelIndex) + { + if(dVolumeChunk.E[ChannelIndex] != 0.0f) + { + real32 DeltaVolume = (PlayingSound->TargetVolume.E[ChannelIndex] - + Volume.E[ChannelIndex]); + u32 VolumeChunkCount = (u32)(((DeltaVolume / dVolumeChunk.E[ChannelIndex]) + 0.5f)); + if(ChunksToMix > VolumeChunkCount) + { + ChunksToMix = VolumeChunkCount; + VolumeEndsAt[ChannelIndex] = VolumeChunkCount; + } + } + } + + // TODO(casey): Handle stereo! + real32 BeginSamplePosition = PlayingSound->SamplesPlayed; + real32 EndSamplePosition = BeginSamplePosition + ChunksToMix*dSampleChunk; + real32 LoopIndexC = (EndSamplePosition - BeginSamplePosition) / (r32)ChunksToMix; + for(u32 LoopIndex = 0; + LoopIndex < ChunksToMix; + ++LoopIndex) + { + real32 SamplePosition = BeginSamplePosition + LoopIndexC*(r32)LoopIndex; + // TODO(casey): Move volume up here to explicit. +#if 1 + __m128 SamplePos = _mm_setr_ps(SamplePosition + 0.0f*dSample, + SamplePosition + 1.0f*dSample, + SamplePosition + 2.0f*dSample, + SamplePosition + 3.0f*dSample); + __m128i SampleIndex = _mm_cvttps_epi32(SamplePos); + __m128 Frac = _mm_sub_ps(SamplePos, _mm_cvtepi32_ps(SampleIndex)); + + __m128 SampleValueF = _mm_setr_ps(LoadedSound->Samples[0][((int32 *)&SampleIndex)[0]], + LoadedSound->Samples[0][((int32 *)&SampleIndex)[1]], + LoadedSound->Samples[0][((int32 *)&SampleIndex)[2]], + LoadedSound->Samples[0][((int32 *)&SampleIndex)[3]]); + __m128 SampleValueC = _mm_setr_ps(LoadedSound->Samples[0][((int32 *)&SampleIndex)[0] + 1], + LoadedSound->Samples[0][((int32 *)&SampleIndex)[1] + 1], + LoadedSound->Samples[0][((int32 *)&SampleIndex)[2] + 1], + LoadedSound->Samples[0][((int32 *)&SampleIndex)[3] + 1]); + + __m128 SampleValue = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(One, Frac), SampleValueF), + _mm_mul_ps(Frac, SampleValueC)); +#else + __m128 SampleValue = _mm_setr_ps(LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 0.0f*dSample)], + LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 1.0f*dSample)], + LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 2.0f*dSample)], + LoadedSound->Samples[0][RoundReal32ToInt32(SamplePosition + 3.0f*dSample)]); +#endif + + __m128 D0 = _mm_load_ps((float *)&Dest0[0]); + __m128 D1 = _mm_load_ps((float *)&Dest1[0]); + + D0 = _mm_add_ps(D0, _mm_mul_ps(_mm_mul_ps(MasterVolume0, Volume0), SampleValue)); + D1 = _mm_add_ps(D1, _mm_mul_ps(_mm_mul_ps(MasterVolume1, Volume1), SampleValue)); + + _mm_store_ps((float *)&Dest0[0], D0); + _mm_store_ps((float *)&Dest1[0], D1); + + ++Dest0; + ++Dest1; + Volume0 = _mm_add_ps(Volume0, dVolumeChunk0); + Volume1 = _mm_add_ps(Volume1, dVolumeChunk1); + } + + PlayingSound->CurrentVolume.E[0] = ((real32 *)&Volume0)[0]; + PlayingSound->CurrentVolume.E[1] = ((real32 *)&Volume1)[1]; + for(u32 ChannelIndex = 0; + ChannelIndex < ArrayCount(VolumeEndsAt); + ++ChannelIndex) + { + if(ChunksToMix == VolumeEndsAt[ChannelIndex]) + { + PlayingSound->CurrentVolume.E[ChannelIndex] = + PlayingSound->TargetVolume.E[ChannelIndex]; + PlayingSound->dCurrentVolume.E[ChannelIndex] = 0.0f; + } + } + + PlayingSound->SamplesPlayed = EndSamplePosition; + Assert(TotalChunksToMix >= ChunksToMix); + TotalChunksToMix -= ChunksToMix; + + if(ChunksToMix == ChunksRemainingInSound) + { + if(IsValid(NextSoundInChain)) + { + PlayingSound->ID = NextSoundInChain; + Assert(PlayingSound->SamplesPlayed >= LoadedSound->SampleCount); + PlayingSound->SamplesPlayed -= (real32)LoadedSound->SampleCount; + if(PlayingSound->SamplesPlayed < 0) + { + PlayingSound->SamplesPlayed = 0.0f; + } + } + else + { + SoundFinished = true; + } + } + } + else + { + LoadSound(Assets, PlayingSound->ID); + break; + } + } + + if(SoundFinished) + { + *PlayingSoundPtr = PlayingSound->Next; + PlayingSound->Next = AudioState->FirstFreePlayingSound; + AudioState->FirstFreePlayingSound = PlayingSound; + } + else + { + PlayingSoundPtr = &PlayingSound->Next; + } + } + + // NOTE(casey): Convert to 16-bit + { + __m128 *Source0 = RealChannel0; + __m128 *Source1 = RealChannel1; + + __m128i *SampleOut = (__m128i *)SoundBuffer->Samples; + for(u32 SampleIndex = 0; + SampleIndex < ChunkCount; + ++SampleIndex) + { + __m128 S0 = _mm_load_ps((float *)Source0++); + __m128 S1 = _mm_load_ps((float *)Source1++); + + __m128i L = _mm_cvtps_epi32(S0); + __m128i R = _mm_cvtps_epi32(S1); + + __m128i LR0 = _mm_unpacklo_epi32(L, R); + __m128i LR1 = _mm_unpackhi_epi32(L, R); + + __m128i S01 = _mm_packs_epi32(LR0, LR1); + + *SampleOut++ = S01; + } + } + + EndGeneration(Assets, GenerationID); + EndTemporaryMemory(MixerMemory); +} + +internal void +InitializeAudioState(audio_state *AudioState, memory_arena *PermArena) +{ + AudioState->PermArena = PermArena; + AudioState->FirstPlayingSound = 0; + AudioState->FirstFreePlayingSound = 0; + + AudioState->MasterVolume = V2(1.0f, 1.0f); +} diff --git a/test_data/lots_of_files/handmade_audio.h b/test_data/lots_of_files/handmade_audio.h new file mode 100644 index 0000000..4d37808 --- /dev/null +++ b/test_data/lots_of_files/handmade_audio.h @@ -0,0 +1,33 @@ +#if !defined(HANDMADE_AUDIO_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +struct playing_sound +{ + v2 CurrentVolume; + v2 dCurrentVolume; + v2 TargetVolume; + + real32 dSample; + + sound_id ID; + real32 SamplesPlayed; + playing_sound *Next; +}; + +struct audio_state +{ + memory_arena *PermArena; + playing_sound *FirstPlayingSound; + playing_sound *FirstFreePlayingSound; + + v2 MasterVolume; +}; + +#define HANDMADE_AUDIO_H +#endif diff --git a/test_data/lots_of_files/handmade_config.h b/test_data/lots_of_files/handmade_config.h new file mode 100644 index 0000000..6871762 --- /dev/null +++ b/test_data/lots_of_files/handmade_config.h @@ -0,0 +1,13 @@ +#define GlobalConstants_Renderer_ShowLightingSamples 0 +#define GlobalConstants_Renderer_Camera_UseDebug 0 +#define GlobalConstants_Renderer_Camera_DebugDistance 25.0f +#define GlobalConstants_Renderer_Camera_RoomBased 0 +#define GlobalConstants_GroundChunks_Checkerboards 0 +#define GlobalConstants_GroundChunks_RecomputeOnEXEChange 1 +#define GlobalConstants_Renderer_TestWeirdDrawBufferSize 0 +#define GlobalConstants_GroundChunks_Outlines 0 +#define GlobalConstants_AI_Familiar_FollowsHero 0 +#define GlobalConstants_Particles_Test 0 +#define GlobalConstants_Particles_ShowGrid 0 +#define GlobalConstants_Simulation_UseSpaceOutlines 0 + diff --git a/test_data/lots_of_files/handmade_debug.cpp b/test_data/lots_of_files/handmade_debug.cpp new file mode 100644 index 0000000..08bb598 --- /dev/null +++ b/test_data/lots_of_files/handmade_debug.cpp @@ -0,0 +1,1872 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +// TODO(casey): Stop using stdio! +#include <stdio.h> + +#include "handmade_debug.h" + +internal void RestartCollation(debug_state *DebugState, u32 InvalidEventArrayIndex); + +inline debug_id +DebugIDFromLink(debug_tree *Tree, debug_variable_link *Link) +{ + debug_id Result = {}; + + Result.Value[0] = Tree; + Result.Value[1] = Link; + + return(Result); +} + +inline debug_state * +DEBUGGetState(game_memory *Memory) +{ + debug_state *DebugState = 0; + if(Memory) + { + DebugState = (debug_state *)Memory->DebugStorage; + if(!DebugState->Initialized) + { + DebugState = 0; + } + } + + return(DebugState); +} + +inline debug_state * +DEBUGGetState(void) +{ + debug_state *Result = DEBUGGetState(DebugGlobalMemory); + + return(Result); +} + +internal debug_tree * +AddTree(debug_state *DebugState, debug_variable_group *Group, v2 AtP) +{ + debug_tree *Tree = PushStruct(&DebugState->DebugArena, debug_tree); + + Tree->UIP = AtP; + Tree->Group = Group; + + DLIST_INSERT(&DebugState->TreeSentinel, Tree); + + return(Tree); +} + +inline b32 +IsHex(char Char) +{ + b32 Result = (((Char >= '0') && (Char <= '9')) || + ((Char >= 'A') && (Char <= 'F'))); + + return(Result); +} + +inline u32 +GetHex(char Char) +{ + u32 Result = 0; + + if((Char >= '0') && (Char <= '9')) + { + Result = Char - '0'; + } + else if((Char >= 'A') && (Char <= 'F')) + { + Result = 0xA + (Char - 'A'); + } + + return(Result); +} + +internal rectangle2 +DEBUGTextOp(debug_state *DebugState, debug_text_op Op, v2 P, char *String, v4 Color = V4(1, 1, 1, 1)) +{ + rectangle2 Result = InvertedInfinityRectangle2(); + if(DebugState && DebugState->DebugFont) + { + render_group *RenderGroup = DebugState->RenderGroup; + loaded_font *Font = DebugState->DebugFont; + hha_font *Info = DebugState->DebugFontInfo; + + u32 PrevCodePoint = 0; + r32 CharScale = DebugState->FontScale; + r32 AtY = P.y; + r32 AtX = P.x; + for(char *At = String; + *At; + ) + { + if((At[0] == '\\') && + (At[1] == '#') && + (At[2] != 0) && + (At[3] != 0) && + (At[4] != 0)) + { + r32 CScale = 1.0f / 9.0f; + Color = V4(Clamp01(CScale*(r32)(At[2] - '0')), + Clamp01(CScale*(r32)(At[3] - '0')), + Clamp01(CScale*(r32)(At[4] - '0')), + 1.0f); + At += 5; + } + else if((At[0] == '\\') && + (At[1] == '^') && + (At[2] != 0)) + { + r32 CScale = 1.0f / 9.0f; + CharScale = DebugState->FontScale*Clamp01(CScale*(r32)(At[2] - '0')); + At += 3; + } + else + { + u32 CodePoint = *At; + if((At[0] == '\\') && + (IsHex(At[1])) && + (IsHex(At[2])) && + (IsHex(At[3])) && + (IsHex(At[4]))) + { + CodePoint = ((GetHex(At[1]) << 12) | + (GetHex(At[2]) << 8) | + (GetHex(At[3]) << 4) | + (GetHex(At[4]) << 0)); + At += 4; + } + + r32 AdvanceX = CharScale*GetHorizontalAdvanceForPair(Info, Font, PrevCodePoint, CodePoint); + AtX += AdvanceX; + + if(CodePoint != ' ') + { + bitmap_id BitmapID = GetBitmapForGlyph(RenderGroup->Assets, Info, Font, CodePoint); + hha_bitmap *Info = GetBitmapInfo(RenderGroup->Assets, BitmapID); + + r32 BitmapScale = CharScale*(r32)Info->Dim[1]; + v3 BitmapOffset = V3(AtX, AtY, 0); + if(Op == DEBUGTextOp_DrawText) + { + PushBitmap(RenderGroup, BitmapID, BitmapScale, BitmapOffset, Color); + } + else + { + Assert(Op == DEBUGTextOp_SizeText); + + loaded_bitmap *Bitmap = GetBitmap(RenderGroup->Assets, BitmapID, RenderGroup->GenerationID); + if(Bitmap) + { + used_bitmap_dim Dim = GetBitmapDim(RenderGroup, Bitmap, BitmapScale, BitmapOffset, 1.0f); + rectangle2 GlyphDim = RectMinDim(Dim.P.xy, Dim.Size); + Result = Union(Result, GlyphDim); + } + } + } + + PrevCodePoint = CodePoint; + + ++At; + } + } + } + + return(Result); +} + +internal void +DEBUGTextOutAt(v2 P, char *String, v4 Color = V4(1, 1, 1, 1)) +{ + debug_state *DebugState = DEBUGGetState(); + if(DebugState) + { + render_group *RenderGroup = DebugState->RenderGroup; + DEBUGTextOp(DebugState, DEBUGTextOp_DrawText, P, String, Color); + } +} + +internal rectangle2 +DEBUGGetTextSize(debug_state *DebugState, char *String) +{ + rectangle2 Result = DEBUGTextOp(DebugState, DEBUGTextOp_SizeText, V2(0, 0), String); + + return(Result); +} + + +internal void +DEBUGTextLine(char *String) +{ + debug_state *DebugState = DEBUGGetState(); + if(DebugState) + { + render_group *RenderGroup = DebugState->RenderGroup; + + loaded_font *Font = PushFont(RenderGroup, DebugState->FontID); + if(Font) + { + hha_font *Info = GetFontInfo(RenderGroup->Assets, DebugState->FontID); + + DEBUGTextOutAt(V2(DebugState->LeftEdge, + DebugState->AtY - DebugState->FontScale*GetStartingBaselineY(DebugState->DebugFontInfo)), String); + + DebugState->AtY -= GetLineAdvanceFor(Info)*DebugState->FontScale; + } + } +} + +struct debug_statistic +{ + r64 Min; + r64 Max; + r64 Avg; + u32 Count; +}; +inline void +BeginDebugStatistic(debug_statistic *Stat) +{ + Stat->Min = Real32Maximum; + Stat->Max = -Real32Maximum; + Stat->Avg = 0.0f; + Stat->Count = 0; +} + +inline void +AccumDebugStatistic(debug_statistic *Stat, r64 Value) +{ + ++Stat->Count; + + if(Stat->Min > Value) + { + Stat->Min = Value; + } + + if(Stat->Max < Value) + { + Stat->Max = Value; + } + + Stat->Avg += Value; +} + +inline void +EndDebugStatistic(debug_statistic *Stat) +{ + if(Stat->Count) + { + Stat->Avg /= (r64)Stat->Count; + } + else + { + Stat->Min = 0.0f; + Stat->Max = 0.0f; + } +} + +internal memory_index +DEBUGEventToText(char *Buffer, char *End, debug_event *Event, u32 Flags) +{ + char *At = Buffer; + char *Name = Event->BlockName; + + if(Flags & DEBUGVarToText_AddDebugUI) + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "#define DEBUGUI_"); + } + + if(Flags & DEBUGVarToText_AddName) + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "%s%s ", Name, (Flags & DEBUGVarToText_Colon) ? ":" : ""); + } + + switch(Event->Type) + { + case DebugType_r32: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "%f", Event->Value_r32); + if(Flags & DEBUGVarToText_FloatSuffix) + { + *At++ = 'f'; + } + } break; + + case DebugType_b32: + { + if(Flags & DEBUGVarToText_PrettyBools) + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "%s", + Event->Value_b32 ? "true" : "false"); + } + else + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "%d", Event->Value_b32); + } + } break; + + case DebugType_s32: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "%d", Event->Value_s32); + } break; + + case DebugType_u32: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "%u", Event->Value_u32); + } break; + + case DebugType_v2: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "V2(%f, %f)", + Event->Value_v2.x, Event->Value_v2.y); + } break; + + case DebugType_v3: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "V3(%f, %f, %f)", + Event->Value_v3.x, Event->Value_v3.y, Event->Value_v3.z); + } break; + + case DebugType_v4: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "V4(%f, %f, %f, %f)", + Event->Value_v4.x, Event->Value_v4.y, + Event->Value_v4.z, Event->Value_v4.w); + } break; + + case DebugType_rectangle2: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "Rect2(%f, %f -> %f, %f)", + Event->Value_rectangle2.Min.x, + Event->Value_rectangle2.Min.y, + Event->Value_rectangle2.Max.x, + Event->Value_rectangle2.Max.y); + } break; + + case DebugType_rectangle3: + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "Rect2(%f, %f, %f -> %f, %f, %f)", + Event->Value_rectangle3.Min.x, + Event->Value_rectangle3.Min.y, + Event->Value_rectangle3.Min.z, + Event->Value_rectangle3.Max.x, + Event->Value_rectangle3.Max.y, + Event->Value_rectangle3.Max.z); + } break; + + case DebugType_CounterThreadList: + case DebugType_bitmap_id: + case DebugType_OpenDataBlock: + { + } break; + + InvalidDefaultCase; + } + + if(Flags & DEBUGVarToText_LineFeedEnd) + { + *At++ = '\n'; + } + + if(Flags & DEBUGVarToText_NullTerminator) + { + *At++ = 0; + } + + return(At - Buffer); +} + +struct debug_variable_iterator +{ + debug_variable_link *Link; + debug_variable_link *Sentinel; +}; + +internal void +WriteHandmadeConfig(debug_state *DebugState) +{ +#if 0 + // TODO(casey): Need a giant buffer here! + char Temp[4096]; + char *At = Temp; + char *End = Temp + sizeof(Temp); + + int Depth = 0; + debug_variable_iterator Stack[DEBUG_MAX_VARIABLE_STACK_DEPTH]; + + Stack[Depth].Link = DebugState->RootGroup->VarGroup.Next; + Stack[Depth].Sentinel = &DebugState->RootGroup->VarGroup; + ++Depth; + while(Depth > 0) + { + debug_variable_iterator *Iter = Stack + (Depth - 1); + if(Iter->Link == Iter->Sentinel) + { + --Depth; + } + else + { + debug_variable *Var = Iter->Link->Var; + Iter->Link = Iter->Link->Next; + + if(DEBUGShouldBeWritten(Var->Type)) + { + // TODO(casey): Other variable types! + for(int Indent = 0; + Indent < Depth; + ++Indent) + { + *At++ = ' '; + *At++ = ' '; + *At++ = ' '; + *At++ = ' '; + } + + if(Var->Type == DebugVariableType_VarGroup) + { + At += _snprintf_s(At, (size_t)(End - At), (size_t)(End - At), + "// "); + } + At += DEBUGVariableToText(At, End, Var, + DEBUGVarToText_AddDebugUI| + DEBUGVarToText_AddName| + DEBUGVarToText_LineFeedEnd| + DEBUGVarToText_FloatSuffix); + } + + if(Var->Type == DebugVariableType_VarGroup) + { + Iter = Stack + Depth; + Iter->Link = Var->VarGroup.Next; + Iter->Sentinel = &Var->VarGroup; + ++Depth; + } + } + + } + Platform.DEBUGWriteEntireFile("../code/handmade_config.h", (u32)(At - Temp), Temp); + + if(!DebugState->Compiling) + { + DebugState->Compiling = true; + DebugState->Compiler = Platform.DEBUGExecuteSystemCommand("..\\code", "c:\\windows\\system32\\cmd.exe", "/C build.bat"); + } +#endif +} + +internal void +DrawProfileIn(debug_state *DebugState, rectangle2 ProfileRect, v2 MouseP) +{ + PushRect(DebugState->RenderGroup, ProfileRect, 0.0f, V4(0, 0, 0, 0.25f)); + + r32 BarSpacing = 4.0f; + r32 LaneHeight = 0.0f; + u32 LaneCount = DebugState->FrameBarLaneCount; + r32 FrameBarScale = FLT_MAX; + for(debug_frame *Frame = DebugState->OldestFrame; + Frame; + Frame = Frame->Next) + { + if(FrameBarScale < Frame->FrameBarScale) + { + FrameBarScale = Frame->FrameBarScale; + } + } + + u32 MaxFrame = DebugState->FrameCount; + if(MaxFrame > 10) + { + MaxFrame = 10; + } + + if((LaneCount > 0) && (MaxFrame > 0)) + { + r32 PixelsPerFramePlusSpacing = GetDim(ProfileRect).y / (r32)MaxFrame; + r32 PixelsPerFrame = PixelsPerFramePlusSpacing - BarSpacing; + LaneHeight = PixelsPerFrame / (r32)LaneCount; + } + + r32 BarHeight = LaneHeight*LaneCount; + r32 BarsPlusSpacing = BarHeight + BarSpacing; + r32 ChartLeft = ProfileRect.Min.x; + r32 ChartHeight = BarsPlusSpacing*(r32)MaxFrame; + r32 ChartWidth = GetDim(ProfileRect).x; + r32 ChartTop = ProfileRect.Max.y; + r32 Scale = ChartWidth*FrameBarScale; + + v3 Colors[] = + { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + {1, 1, 0}, + {0, 1, 1}, + {1, 0, 1}, + {1, 0.5f, 0}, + {1, 0, 0.5f}, + {0.5f, 1, 0}, + {0, 1, 0.5f}, + {0.5f, 0, 1}, + {0, 0.5f, 1}, + }; + + u32 FrameIndex = 0; + for(debug_frame *Frame = DebugState->OldestFrame; + Frame; + Frame = Frame->Next, ++FrameIndex) + { + r32 StackX = ChartLeft; + r32 StackY = ChartTop - BarsPlusSpacing*(r32)FrameIndex; + for(u32 RegionIndex = 0; + RegionIndex < Frame->RegionCount; + ++RegionIndex) + { + debug_frame_region *Region = Frame->Regions + RegionIndex; + +// v3 Color = Colors[RegionIndex%ArrayCount(Colors)]; + v3 Color = Colors[Region->ColorIndex%ArrayCount(Colors)]; + r32 ThisMinX = StackX + Scale*Region->MinT; + r32 ThisMaxX = StackX + Scale*Region->MaxT; + + rectangle2 RegionRect = RectMinMax( + V2(ThisMinX, StackY - LaneHeight*(Region->LaneIndex + 1)), + V2(ThisMaxX, StackY - LaneHeight*Region->LaneIndex)); + + PushRect(DebugState->RenderGroup, RegionRect, 0.0f, V4(Color, 1)); + + if(IsInRectangle(RegionRect, MouseP)) + { + debug_event *Record = Region->Event; + char TextBuffer[256]; + _snprintf_s(TextBuffer, sizeof(TextBuffer), + "%s: %10ucy [%s(%d)]", + Record->BlockName, + Region->CycleCount, + Record->FileName, + Record->LineNumber); + DEBUGTextOutAt(MouseP + V2(0.0f, 10.0f), TextBuffer); + +// HotRecord = Record; + } + } + } +#if 0 + PushRect(RenderGroup, V3(ChartLeft + 0.5f*ChartWidth, ChartMinY + ChartHeight, 0.0f), + V2(ChartWidth, 1.0f), V4(1, 1, 1, 1)); +#endif +} + +inline b32 +InteractionsAreEqual(debug_interaction A, debug_interaction B) +{ + b32 Result = ((A.Type == B.Type) && + (A.Generic == B.Generic)); + + return(Result); +} + +inline b32 +InteractionIsHot(debug_state *DebugState, debug_interaction B) +{ + b32 Result = InteractionsAreEqual(DebugState->HotInteraction, B); + + return(Result); +} + +struct layout +{ + debug_state *DebugState; + v2 MouseP; + v2 At; + int Depth; + real32 LineAdvance; + r32 SpacingY; +}; + +inline rectangle2 +PlaceRectangle(layout *Layout, v2 Dim) +{ +} + +struct layout_element +{ + // NOTE(casey): Storage; + layout *Layout; + v2 *Dim; + v2 *Size; + debug_interaction Interaction; + + // NOTE(casey): Out + rectangle2 Bounds; +}; + +inline layout_element +BeginElementRectangle(layout *Layout, v2 *Dim) +{ + layout_element Element = {}; + + Element.Layout = Layout; + Element.Dim = Dim; + + return(Element); +} + +inline void +MakeElementSizable(layout_element *Element) +{ + Element->Size = Element->Dim; +} + +inline void +DefaultInteraction(layout_element *Element, debug_interaction Interaction) +{ + Element->Interaction = Interaction; +} + +inline void +EndElement(layout_element *Element) +{ + layout *Layout = Element->Layout; + debug_state *DebugState = Layout->DebugState; + + r32 SizeHandlePixels = 4.0f; + + v2 Frame = {0, 0}; + if(Element->Size) + { + Frame.x = SizeHandlePixels; + Frame.y = SizeHandlePixels; + } + + v2 TotalDim = *Element->Dim + 2.0f*Frame; + + v2 TotalMinCorner = V2(Layout->At.x + Layout->Depth*2.0f*Layout->LineAdvance, + Layout->At.y - TotalDim.y); + v2 TotalMaxCorner = TotalMinCorner + TotalDim; + + v2 InteriorMinCorner = TotalMinCorner + Frame; + v2 InteriorMaxCorner = InteriorMinCorner + *Element->Dim; + + rectangle2 TotalBounds = RectMinMax(TotalMinCorner, TotalMaxCorner); + Element->Bounds = RectMinMax(InteriorMinCorner, InteriorMaxCorner); + + if(Element->Interaction.Type && IsInRectangle(Element->Bounds, Layout->MouseP)) + { + DebugState->NextHotInteraction = Element->Interaction; + } + + if(Element->Size) + { + PushRect(DebugState->RenderGroup, RectMinMax(V2(TotalMinCorner.x, InteriorMinCorner.y), + V2(InteriorMinCorner.x, InteriorMaxCorner.y)), 0.0f, + V4(0, 0, 0, 1)); + PushRect(DebugState->RenderGroup, RectMinMax(V2(InteriorMaxCorner.x, InteriorMinCorner.y), + V2(TotalMaxCorner.x, InteriorMaxCorner.y)), 0.0f, + V4(0, 0, 0, 1)); + PushRect(DebugState->RenderGroup, RectMinMax(V2(InteriorMinCorner.x, TotalMinCorner.y), + V2(InteriorMaxCorner.x, InteriorMinCorner.y)), 0.0f, + V4(0, 0, 0, 1)); + PushRect(DebugState->RenderGroup, RectMinMax(V2(InteriorMinCorner.x, InteriorMaxCorner.y), + V2(InteriorMaxCorner.x, TotalMaxCorner.y)), 0.0f, + V4(0, 0, 0, 1)); + + debug_interaction SizeInteraction = {}; + SizeInteraction.Type = DebugInteraction_Resize; + SizeInteraction.P = Element->Size; + + rectangle2 SizeBox = RectMinMax(V2(InteriorMaxCorner.x, TotalMinCorner.y), + V2(TotalMaxCorner.x, InteriorMinCorner.y)); + PushRect(DebugState->RenderGroup, SizeBox, 0.0f, + (InteractionIsHot(DebugState, SizeInteraction) ? V4(1, 1, 0, 1) : V4(1, 1, 1, 1))); + if(IsInRectangle(SizeBox, Layout->MouseP)) + { + DebugState->NextHotInteraction = SizeInteraction; + } + } + + r32 SpacingY = Layout->SpacingY; + if(0) + { + SpacingY = 0.0f; + } + Layout->At.y = GetMinCorner(TotalBounds).y - SpacingY; +} + +inline b32 +DebugIDsAreEqual(debug_id A, debug_id B) +{ + b32 Result = ((A.Value[0] == B.Value[0]) && + (A.Value[1] == B.Value[1])); + + return(Result); +} + +internal debug_view * +GetOrCreateDebugViewFor(debug_state *DebugState, debug_id ID) +{ + // TODO(casey): Better hash function + u32 HashIndex = (((u32)ID.Value[0] >> 2) + ((u32)ID.Value[1] >> 2)) % ArrayCount(DebugState->ViewHash); + debug_view **HashSlot = DebugState->ViewHash + HashIndex; + + debug_view *Result = 0; + for(debug_view *Search = *HashSlot; + Search; + Search = Search->NextInHash) + { + if(DebugIDsAreEqual(Search->ID, ID)) + { + Result = Search; + break; + } + } + + if(!Result) + { + Result = PushStruct(&DebugState->DebugArena, debug_view); + Result->ID = ID; + Result->Type = DebugViewType_Unknown; + Result->NextInHash = *HashSlot; + *HashSlot = Result; + } + + return(Result); +} + +inline debug_interaction +VarLinkInteraction(debug_interaction_type Type, debug_tree *Tree, debug_variable_link *Link) +{ + debug_interaction ItemInteraction = {}; + ItemInteraction.ID = DebugIDFromLink(Tree, Link); + ItemInteraction.Type = Type; + ItemInteraction.Event = Link->Event; + + return(ItemInteraction); +} + +inline debug_interaction +DebugIDInteraction(debug_interaction_type Type, debug_id ID) +{ + debug_interaction ItemInteraction = {}; + ItemInteraction.ID = ID; + ItemInteraction.Type = Type; + + return(ItemInteraction); +} + +internal b32 +IsSelected(debug_state *DebugState, debug_id ID) +{ + b32 Result = false; + + for(u32 Index = 0; + Index < DebugState->SelectedIDCount; + ++Index) + { + if(DebugIDsAreEqual(ID, DebugState->SelectedID[Index])) + { + Result = true; + break; + } + } + + return(Result); +} + +internal void +ClearSelection(debug_state *DebugState) +{ + DebugState->SelectedIDCount = 0; +} + +internal void +AddToSelection(debug_state *DebugState, debug_id ID) +{ + if((DebugState->SelectedIDCount < ArrayCount(DebugState->SelectedID)) && + !IsSelected(DebugState, ID)) + { + DebugState->SelectedID[DebugState->SelectedIDCount++] = ID; + } +} + +internal void +DEBUG_HIT(debug_id ID, r32 ZValue) +{ + debug_state *DebugState = DEBUGGetState(); + if(DebugState) + { + DebugState->NextHotInteraction = DebugIDInteraction(DebugInteraction_Select, ID); + } +} + +internal b32 +DEBUG_HIGHLIGHTED(debug_id ID, v4 *Color) +{ + b32 Result = false; + + debug_state *DebugState = DEBUGGetState(); + if(DebugState) + { + if(IsSelected(DebugState, ID)) + { + *Color = V4(0, 1, 1, 1); + Result = true; + } + + if(DebugIDsAreEqual(DebugState->HotInteraction.ID, ID)) + { + *Color = V4(1, 1, 0, 1); + Result = true; + } + } + + return(Result); +} + +internal b32 +DEBUG_REQUESTED(debug_id ID) +{ + b32 Result = false; + + debug_state *DebugState = DEBUGGetState(); + if(DebugState) + { + Result = IsSelected(DebugState, ID) + || DebugIDsAreEqual(DebugState->HotInteraction.ID, ID); + } + + return(Result); +} + +internal void +DEBUGDrawMainMenu(debug_state *DebugState, render_group *RenderGroup, v2 MouseP) +{ + for(debug_tree *Tree = DebugState->TreeSentinel.Next; + Tree != &DebugState->TreeSentinel; + Tree = Tree->Next) + { + layout Layout = {}; + Layout.DebugState = DebugState; + Layout.MouseP = MouseP; + Layout.At = Tree->UIP; + Layout.LineAdvance = DebugState->FontScale*GetLineAdvanceFor(DebugState->DebugFontInfo); + Layout.SpacingY = 4.0f; + + int Depth = 0; + debug_variable_iterator Stack[DEBUG_MAX_VARIABLE_STACK_DEPTH]; + + debug_variable_group *Group = Tree->Group; + if(DebugState->FrameCount > 0) + { +// debug_variable_group *HackyGroup = DebugState->Frames[0].RootGroup; + debug_variable_group *HackyGroup = DebugState->ValuesGroup; + if(HackyGroup) + { + Group = HackyGroup; + } + } + + if(Group) + { + Stack[Depth].Link = Group->Sentinel.Next; + Stack[Depth].Sentinel = &Group->Sentinel; + ++Depth; + while(Depth > 0) + { + debug_variable_iterator *Iter = Stack + (Depth - 1); + if(Iter->Link == Iter->Sentinel) + { + --Depth; + } + else + { + Layout.Depth = Depth; + + debug_variable_link *Link = Iter->Link; + debug_event *Event = Iter->Link->Event; + Iter->Link = Iter->Link->Next; + + debug_interaction ItemInteraction = + VarLinkInteraction(DebugInteraction_AutoModifyVariable, Tree, Link); + + b32 IsHot = InteractionIsHot(DebugState, ItemInteraction); + v4 ItemColor = IsHot ? V4(1, 1, 0, 1) : V4(1, 1, 1, 1); + + debug_view *View = GetOrCreateDebugViewFor(DebugState, DebugIDFromLink(Tree, Link)); + switch(Event->Type) + { + case DebugType_CounterThreadList: + { + layout_element Element = BeginElementRectangle( + &Layout, &View->InlineBlock.Dim); + MakeElementSizable(&Element); + DefaultInteraction(&Element, ItemInteraction); + EndElement(&Element); + + DrawProfileIn(DebugState, Element.Bounds, MouseP); + } break; + + case DebugType_bitmap_id: + { + loaded_bitmap *Bitmap = GetBitmap(RenderGroup->Assets, Event->Value_bitmap_id, RenderGroup->GenerationID); + r32 BitmapScale = View->InlineBlock.Dim.y; + if(Bitmap) + { + used_bitmap_dim Dim = GetBitmapDim(RenderGroup, Bitmap, BitmapScale, V3(0.0f, 0.0f, 0.0f), 1.0f); + View->InlineBlock.Dim.x = Dim.Size.x; + } + + debug_interaction TearInteraction = VarLinkInteraction(DebugInteraction_TearValue, Tree, Link); + + layout_element Element = BeginElementRectangle(&Layout, &View->InlineBlock.Dim); + MakeElementSizable(&Element); + DefaultInteraction(&Element, TearInteraction); + EndElement(&Element); + + PushRect(DebugState->RenderGroup, Element.Bounds, 0.0f, V4(0, 0, 0, 1.0f)); + PushBitmap(DebugState->RenderGroup, Event->Value_bitmap_id, BitmapScale, + V3(GetMinCorner(Element.Bounds), 0.0f), V4(1, 1, 1, 1), 0.0f); + } break; + + default: + { + char Text[256]; + DEBUGEventToText(Text, Text + sizeof(Text), Event, + DEBUGVarToText_AddName| + DEBUGVarToText_NullTerminator| + DEBUGVarToText_Colon| + DEBUGVarToText_PrettyBools); + + rectangle2 TextBounds = DEBUGGetTextSize(DebugState, Text); + v2 Dim = {GetDim(TextBounds).x, Layout.LineAdvance}; + + layout_element Element = BeginElementRectangle(&Layout, &Dim); + DefaultInteraction(&Element, ItemInteraction); + EndElement(&Element); + + DEBUGTextOutAt(V2(GetMinCorner(Element.Bounds).x, + GetMaxCorner(Element.Bounds).y - DebugState->FontScale*GetStartingBaselineY(DebugState->DebugFontInfo)), + Text, ItemColor); + } break; + } + + if(Link->Children +// && View->Collapsible.ExpandedAlways + ) + { + Iter = Stack + Depth; + Iter->Link = Link->Children->Sentinel.Next; + Iter->Sentinel = &Link->Children->Sentinel; + ++Depth; + } + } + } + } + + DebugState->AtY = Layout.At.y; + + if(1) + { + debug_interaction MoveInteraction = {}; + MoveInteraction.Type = DebugInteraction_Move; + MoveInteraction.P = &Tree->UIP; + + rectangle2 MoveBox = RectCenterHalfDim(Tree->UIP - V2(4.0f, 4.0f), V2(4.0f, 4.0f)); + PushRect(DebugState->RenderGroup, MoveBox, 0.0f, + InteractionIsHot(DebugState, MoveInteraction) ? V4(1, 1, 0, 1) : V4(1, 1, 1, 1)); + + if(IsInRectangle(MoveBox, MouseP)) + { + DebugState->NextHotInteraction = MoveInteraction; + } + } + } + +#if 0 + u32 NewHotMenuIndex = ArrayCount(DebugVariableList); + r32 BestDistanceSq = Real32Maximum; + + r32 MenuRadius = 400.0f; + r32 AngleStep = Tau32 / (r32)ArrayCount(DebugVariableList); + for(u32 MenuItemIndex = 0; + MenuItemIndex < ArrayCount(DebugVariableList); + ++MenuItemIndex) + { + debug_variable *Var = DebugVariableList + MenuItemIndex; + char *Text = Var->Name; + + v4 ItemColor = Var->Value ? V4(1, 1, 1, 1) : V4(0.5f, 0.5f, 0.5f, 1); + if(MenuItemIndex == DebugState->HotMenuIndex) + { + ItemColor = V4(1, 1, 0, 1); + } + + r32 Angle = (r32)MenuItemIndex*AngleStep; + v2 TextP = DebugState->MenuP + MenuRadius*Arm2(Angle); + + r32 ThisDistanceSq = LengthSq(TextP - MouseP); + if(BestDistanceSq > ThisDistanceSq) + { + NewHotMenuIndex = MenuItemIndex; + BestDistanceSq = ThisDistanceSq; + } + + rectangle2 TextBounds = DEBUGGetTextSize(DebugState, Text); + DEBUGTextOutAt(TextP - 0.5f*GetDim(TextBounds), Text, ItemColor); + } + + if(LengthSq(MouseP - DebugState->MenuP) > Square(MenuRadius)) + { + DebugState->HotMenuIndex = NewHotMenuIndex; + } + else + { + DebugState->HotMenuIndex = ArrayCount(DebugVariableList); + } +#endif +} + +internal void +DEBUGBeginInteract(debug_state *DebugState, game_input *Input, v2 MouseP, b32 AltUI) +{ + if(DebugState->HotInteraction.Type) + { + if(DebugState->HotInteraction.Type == DebugInteraction_AutoModifyVariable) + { + switch(DebugState->HotInteraction.Event->Type) + { + case DebugType_b32: + { + DebugState->HotInteraction.Type = DebugInteraction_ToggleValue; + } break; + + case DebugType_r32: + { + DebugState->HotInteraction.Type = DebugInteraction_DragValue; + } break; + + case DebugType_OpenDataBlock: + { + DebugState->HotInteraction.Type = DebugInteraction_ToggleValue; + } break; + } + + if(AltUI) + { + DebugState->HotInteraction.Type = DebugInteraction_TearValue; + } + } + + switch(DebugState->HotInteraction.Type) + { + case DebugInteraction_TearValue: + { +#if 0 + debug_variable *RootGroup = DEBUGAddRootGroup(DebugState, "NewUserGroup"); + DEBUGAddVariableToGroup(DebugState, RootGroup, DebugState->HotInteraction.Var); + debug_tree *Tree = AddTree(DebugState, RootGroup, V2(0, 0)); + Tree->UIP = MouseP; + DebugState->HotInteraction.Type = DebugInteraction_Move; + DebugState->HotInteraction.P = &Tree->UIP; +#endif + } break; + + case DebugInteraction_Select: + { + if(!Input->ShiftDown) + { + ClearSelection(DebugState); + } + AddToSelection(DebugState, DebugState->HotInteraction.ID); + } break; + } + + DebugState->Interaction = DebugState->HotInteraction; + } + else + { + DebugState->Interaction.Type = DebugInteraction_NOP; + } +} + +internal void +DEBUGEndInteract(debug_state *DebugState, game_input *Input, v2 MouseP) +{ + switch(DebugState->Interaction.Type) + { + case DebugInteraction_ToggleValue: + { + debug_event *Event = DebugState->Interaction.Event; + Assert(Event); + switch(Event->Type) + { + case DebugType_b32: + { + Event->Value_b32 = !Event->Value_b32; + } break; + + case DebugType_OpenDataBlock: + { + debug_view *View = GetOrCreateDebugViewFor(DebugState, DebugState->Interaction.ID); + View->Collapsible.ExpandedAlways = !View->Collapsible.ExpandedAlways; + } break; + } + } break; + } + + WriteHandmadeConfig(DebugState); + + DebugState->Interaction.Type = DebugInteraction_None; + DebugState->Interaction.Generic = 0; +} + +internal void +DEBUGInteract(debug_state *DebugState, game_input *Input, v2 MouseP) +{ + v2 dMouseP = MouseP - DebugState->LastMouseP; + +/* + if(Input->MouseButtons[PlatformMouseButton_Right].EndedDown) + { + if(Input->MouseButtons[PlatformMouseButton_Right].HalfTransitionCount > 0) + { + DebugState->MenuP = MouseP; + } + DrawDebugMainMenu(DebugState, RenderGroup, MouseP); + } + else if(Input->MouseButtons[PlatformMouseButton_Right].HalfTransitionCount > 0) +*/ + if(DebugState->Interaction.Type) + { + debug_event *Event = DebugState->Interaction.Event; + debug_tree *Tree = DebugState->Interaction.Tree; + v2 *P = DebugState->Interaction.P; + + // NOTE(casey): Mouse move interaction + switch(DebugState->Interaction.Type) + { + case DebugInteraction_DragValue: + { + switch(Event->Type) + { + case DebugType_r32: + { + Event->Value_r32 += 0.1f*dMouseP.y; + } break; + } + } break; + + case DebugInteraction_Resize: + { + *P += V2(dMouseP.x, -dMouseP.y); + P->x = Maximum(P->x, 10.0f); + P->y = Maximum(P->y, 10.0f); + } break; + + case DebugInteraction_Move: + { + *P += V2(dMouseP.x, dMouseP.y); + } break; + } + b32 AltUI = Input->MouseButtons[PlatformMouseButton_Right].EndedDown; + + // NOTE(casey): Click interaction + for(u32 TransitionIndex = Input->MouseButtons[PlatformMouseButton_Left].HalfTransitionCount; + TransitionIndex > 1; + --TransitionIndex) + { + DEBUGEndInteract(DebugState, Input, MouseP); + DEBUGBeginInteract(DebugState, Input, MouseP, AltUI); + } + + if(!Input->MouseButtons[PlatformMouseButton_Left].EndedDown) + { + DEBUGEndInteract(DebugState, Input, MouseP); + } + } + else + { + DebugState->HotInteraction = DebugState->NextHotInteraction; + + b32 AltUI = Input->MouseButtons[PlatformMouseButton_Right].EndedDown; + for(u32 TransitionIndex = Input->MouseButtons[PlatformMouseButton_Left].HalfTransitionCount; + TransitionIndex > 1; + --TransitionIndex) + { + DEBUGBeginInteract(DebugState, Input, MouseP, AltUI); + DEBUGEndInteract(DebugState, Input, MouseP); + } + + if(Input->MouseButtons[PlatformMouseButton_Left].EndedDown) + { + DEBUGBeginInteract(DebugState, Input, MouseP, AltUI); + } + } + + DebugState->LastMouseP = MouseP; +} + +global_variable debug_table GlobalDebugTable_; +debug_table *GlobalDebugTable = &GlobalDebugTable_; + +inline u32 +GetLaneFromThreadIndex(debug_state *DebugState, u32 ThreadIndex) +{ + u32 Result = 0; + + // TODO(casey): Implement thread ID lookup. + + return(Result); +} + +internal debug_thread * +GetDebugThread(debug_state *DebugState, u32 ThreadID) +{ + debug_thread *Result = 0; + for(debug_thread *Thread = DebugState->FirstThread; + Thread; + Thread = Thread->Next) + { + if(Thread->ID == ThreadID) + { + Result = Thread; + break; + } + } + + if(!Result) + { + FREELIST_ALLOCATE(debug_thread, Result, DebugState->FirstFreeThread, &DebugState->DebugArena); + + Result->ID = ThreadID; + Result->LaneIndex = DebugState->FrameBarLaneCount++; + Result->FirstOpenCodeBlock = 0; + Result->FirstOpenDataBlock = 0; + Result->Next = DebugState->FirstThread; + DebugState->FirstThread = Result; + } + + return(Result); +} + +debug_frame_region * +AddRegion(debug_state *DebugState, debug_frame *CurrentFrame) +{ + Assert(CurrentFrame->RegionCount < MAX_REGIONS_PER_FRAME); + debug_frame_region *Result = CurrentFrame->Regions + CurrentFrame->RegionCount++; + + return(Result); +} + +inline open_debug_block * +AllocateOpenDebugBlock(debug_state *DebugState, u32 FrameIndex, debug_event *Event, + open_debug_block **FirstOpenBlock) +{ + open_debug_block *Result = 0; + FREELIST_ALLOCATE(open_debug_block, Result, DebugState->FirstFreeBlock, &DebugState->DebugArena); + + Result->StartingFrameIndex = FrameIndex; + Result->OpeningEvent = Event; + Result->NextFree = 0; + + Result->Parent = *FirstOpenBlock; + *FirstOpenBlock = Result; + + return(Result); +} + +inline void +DeallocateOpenDebugBlock(debug_state *DebugState, open_debug_block **FirstOpenBlock) +{ + open_debug_block *FreeBlock = *FirstOpenBlock; + *FirstOpenBlock = FreeBlock->Parent; + + FreeBlock->NextFree = DebugState->FirstFreeBlock; + DebugState->FirstFreeBlock = FreeBlock; +} + +inline b32 +EventsMatch(debug_event A, debug_event B) +{ + // TODO(casey): Have counters for blocks? + b32 Result = (A.ThreadID == B.ThreadID); + + return(Result); +} + +internal debug_event * +CreateVariable(debug_state *State, debug_type Type, char *Name) +{ + debug_event *Var = PushStruct(&State->DebugArena, debug_event); + ZeroStruct(*Var); + Var->Type = (u8)Type; + Var->BlockName = (char *)PushCopy(&State->DebugArena, StringLength(Name) + 1, Name); + + return(Var); +} + +internal debug_variable_link * +AddVariableToGroup(debug_state *DebugState, debug_variable_group *Group, debug_event *Add) +{ + debug_variable_link *Link = PushStruct(&DebugState->DebugArena, debug_variable_link); + + DLIST_INSERT(&Group->Sentinel, Link); + Link->Children = 0; + Link->Event = Add; + + Assert(Link->Event->Type != DebugType_BeginBlock); + return(Link); +} + +internal debug_variable_group * +CreateVariableGroup(debug_state *DebugState) +{ + debug_variable_group *Group = PushStruct(&DebugState->DebugArena, debug_variable_group); + DLIST_INIT(&Group->Sentinel); + + return(Group); +} + +internal void +FreeVariableGroup(debug_state *DebugState, debug_variable_group *Group) +{ + // TODO(casey): Also remember to trigger freeing frames during arena pushes... + // TODO(casey): Remember to copy out the debug events into the debug variable links. + Assert(!"Not implemented"); +} + +internal debug_variable_group * +GetGroupForHierarchicalName(debug_state *DebugState, char *Name) +{ + debug_variable_group *Result = DebugState->ValuesGroup; + return(Result); +} + +internal debug_frame * +NewFrame(debug_state *DebugState, u64 BeginClock) +{ + // TODO(casey): Simplify this once regions are more reasonable! + debug_frame *Result = DebugState->FirstFreeFrame; + if(Result) + { + DebugState->FirstFreeFrame = Result->NextFree; + debug_frame_region *Regions = Result->Regions; + ZeroStruct(*Result); + Result->Regions = Regions; + } + else + { + Result = PushStruct(&DebugState->DebugArena, debug_frame); + ZeroStruct(*Result); + Result->Regions = PushArray(&DebugState->DebugArena, MAX_REGIONS_PER_FRAME, debug_frame_region); + } + + Result->FrameBarScale = 1.0f; + Result->RootGroup = CreateVariableGroup(DebugState); + + Result->BeginClock = BeginClock; + + return(Result); +} + +internal void +FreeFrame(debug_state *DebugState, debug_frame *Frame) +{ + FreeVariableGroup(DebugState, Frame->RootGroup); + FREELIST_DEALLOCATE(Frame, DebugState->FirstFreeFrame); +} + +internal void +CollateDebugRecords(debug_state *DebugState, u32 EventCount, debug_event *EventArray) +{ + for(u32 EventIndex = 0; + EventIndex < EventCount; + ++EventIndex) + { + debug_event *Event = EventArray + EventIndex; + + if(!DebugState->CollationFrame) + { + DebugState->CollationFrame = NewFrame(DebugState, Event->Clock); + } + + if(Event->Type == DebugType_MarkDebugValue) + { + AddVariableToGroup(DebugState, + GetGroupForHierarchicalName(DebugState, Event->Value_debug_event->BlockName), + Event->Value_debug_event); + } + else if(Event->Type == DebugType_FrameMarker) + { + Assert(DebugState->CollationFrame); + + DebugState->CollationFrame->EndClock = Event->Clock; + DebugState->CollationFrame->WallSecondsElapsed = Event->Value_r32; + + r32 ClockRange = (r32)(DebugState->CollationFrame->EndClock - DebugState->CollationFrame->BeginClock); + // TODO(casey): Can we reenable this now?? +#if 0 + if(ClockRange > 0.0f) + { + r32 FrameBarScale = 1.0f / ClockRange; + if(DebugState->FrameBarScale > FrameBarScale) + { + DebugState->FrameBarScale = FrameBarScale; + } + } +#endif + + if(DebugState->Paused) + { + FreeFrame(DebugState, DebugState->CollationFrame); + } + else + { + if(DebugState->MostRecentFrame) + { + DebugState->MostRecentFrame->Next = DebugState->CollationFrame; + } + else + { + DebugState->OldestFrame = DebugState->MostRecentFrame = DebugState->CollationFrame; + } + ++DebugState->FrameCount; + } + + DebugState->CollationFrame = NewFrame(DebugState, Event->Clock); + } + else + { + Assert(DebugState->CollationFrame); + + u32 FrameIndex = DebugState->FrameCount - 1; + debug_thread *Thread = GetDebugThread(DebugState, Event->ThreadID); + u64 RelativeClock = Event->Clock - DebugState->CollationFrame->BeginClock; + + switch(Event->Type) + { + case DebugType_BeginBlock: + { + open_debug_block *DebugBlock = AllocateOpenDebugBlock( + DebugState, FrameIndex, Event, &Thread->FirstOpenCodeBlock); + } break; + + case DebugType_EndBlock: + { + if(Thread->FirstOpenCodeBlock) + { + open_debug_block *MatchingBlock = Thread->FirstOpenCodeBlock; + debug_event *OpeningEvent = MatchingBlock->OpeningEvent; + if(EventsMatch(*OpeningEvent, *Event)) + { + if(MatchingBlock->StartingFrameIndex == FrameIndex) + { + char *MatchName = + MatchingBlock->Parent ? MatchingBlock->Parent->OpeningEvent->BlockName : 0; + if(MatchName == DebugState->ScopeToRecord) + { + r32 MinT = (r32)(OpeningEvent->Clock - DebugState->CollationFrame->BeginClock); + r32 MaxT = (r32)(Event->Clock - DebugState->CollationFrame->BeginClock); + r32 ThresholdT = 0.01f; + if((MaxT - MinT) > ThresholdT) + { + debug_frame_region *Region = AddRegion(DebugState, DebugState->CollationFrame); + Region->Event = OpeningEvent; + Region->CycleCount = (Event->Clock - OpeningEvent->Clock); + Region->LaneIndex = (u16)Thread->LaneIndex; + Region->MinT = MinT; + Region->MaxT = MaxT; + Region->ColorIndex = (u16)OpeningEvent->BlockName; + } + } + } + else + { + // TODO(casey): Record all frames in between and begin/end spans! + } + + DeallocateOpenDebugBlock(DebugState, &Thread->FirstOpenCodeBlock); + } + else + { + // TODO(casey): Record span that goes to the beginning of the frame series? + } + } + } break; + + case DebugType_OpenDataBlock: + { + open_debug_block *DebugBlock = AllocateOpenDebugBlock( + DebugState, FrameIndex, Event, &Thread->FirstOpenDataBlock); + + DebugBlock->Group = CreateVariableGroup(DebugState); + debug_variable_link *Link = + AddVariableToGroup(DebugState, + DebugBlock->Parent ? DebugBlock->Parent->Group : DebugState->CollationFrame->RootGroup, + Event); + Link->Children = DebugBlock->Group; + } break; + + case DebugType_CloseDataBlock: + { + if(Thread->FirstOpenDataBlock) + { + open_debug_block *MatchingBlock = Thread->FirstOpenDataBlock; + debug_event *OpeningEvent = MatchingBlock->OpeningEvent; + if(EventsMatch(*OpeningEvent, *Event)) + { + DeallocateOpenDebugBlock(DebugState, &Thread->FirstOpenDataBlock); + } + else + { + // TODO(casey): Record span that goes to the beginning of the frame series? + } + } + } break; + + default: + { + AddVariableToGroup(DebugState, Thread->FirstOpenDataBlock->Group, Event); + } break; + } + } + } +} + +internal void +DEBUGStart(debug_state *DebugState, game_assets *Assets, u32 Width, u32 Height) +{ + TIMED_FUNCTION(); + + if(!DebugState->Initialized) + { + DebugState->FrameBarLaneCount = 0; + DebugState->FirstThread = 0; + DebugState->FirstFreeThread = 0; + DebugState->FirstFreeBlock = 0; + + DebugState->FrameCount = 0; + + DebugState->OldestFrame = DebugState->MostRecentFrame = DebugState->FirstFreeFrame = 0; + DebugState->CollationFrame = 0; + + DebugState->HighPriorityQueue = DebugGlobalMemory->HighPriorityQueue; + DebugState->TreeSentinel.Next = &DebugState->TreeSentinel; + DebugState->TreeSentinel.Prev = &DebugState->TreeSentinel; + DebugState->TreeSentinel.Group = 0; + + InitializeArena(&DebugState->DebugArena, DebugGlobalMemory->DebugStorageSize - sizeof(debug_state), DebugState + 1); + +#if 0 + debug_variable_definition_context Context = {}; + Context.State = DebugState; + Context.Arena = &DebugState->DebugArena; + Context.GroupStack[0] = 0; + + DebugState->RootGroup = DEBUGBeginVariableGroup(&Context, "Root"); + DEBUGBeginVariableGroup(&Context, "Debugging"); + + DEBUGCreateVariables(&Context); + DEBUGBeginVariableGroup(&Context, "Profile"); + DEBUGBeginVariableGroup(&Context, "By Thread"); + DEBUGAddVariable(&Context, DebugType_CounterThreadList, ""); + DEBUGEndVariableGroup(&Context); + DEBUGBeginVariableGroup(&Context, "By Function"); + DEBUGAddVariable(&Context, DebugType_CounterThreadList, ""); + DEBUGEndVariableGroup(&Context); + DEBUGEndVariableGroup(&Context); + + asset_vector MatchVector = {}; + MatchVector.E[Tag_FacingDirection] = 0.0f; + asset_vector WeightVector = {}; + WeightVector.E[Tag_FacingDirection] = 1.0f; + bitmap_id ID = GetBestMatchBitmapFrom(Assets, Asset_Head, &MatchVector, &WeightVector); + + DEBUGAddVariable(&Context, "Test Bitmap", ID); + + DEBUGEndVariableGroup(&Context); + DEBUGEndVariableGroup(&Context); + Assert(Context.GroupDepth == 0); +#endif + + DebugState->RenderGroup = AllocateRenderGroup(Assets, &DebugState->DebugArena, Megabytes(16), false); + + DebugState->Paused = false; + DebugState->ScopeToRecord = 0; + + DebugState->Initialized = true; + DebugState->ValuesGroup = CreateVariableGroup(DebugState); + + AddTree(DebugState, DebugState->RootGroup, V2(-0.5f*Width, 0.5f*Height)); + } + + BeginRender(DebugState->RenderGroup); + DebugState->DebugFont = PushFont(DebugState->RenderGroup, DebugState->FontID); + DebugState->DebugFontInfo = GetFontInfo(DebugState->RenderGroup->Assets, DebugState->FontID); + + DebugState->GlobalWidth = (r32)Width; + DebugState->GlobalHeight = (r32)Height; + + asset_vector MatchVector = {}; + asset_vector WeightVector = {}; + MatchVector.E[Tag_FontType] = (r32)FontType_Debug; + WeightVector.E[Tag_FontType] = 1.0f; + DebugState->FontID = GetBestMatchFontFrom(Assets, Asset_Font, &MatchVector, &WeightVector); + + DebugState->FontScale = 1.0f; + Orthographic(DebugState->RenderGroup, Width, Height, 1.0f); + DebugState->LeftEdge = -0.5f*Width; + DebugState->RightEdge = 0.5f*Width; + + DebugState->AtY = 0.5f*Height; +} + +internal void +DEBUGDumpStruct(u32 MemberCount, member_definition *MemberDefs, void *StructPtr, u32 IndentLevel = 0) +{ + for(u32 MemberIndex = 0; + MemberIndex < MemberCount; + ++MemberIndex) + { + char TextBufferBase[256]; + char *TextBuffer = TextBufferBase; + for(u32 Indent = 0; + Indent < IndentLevel; + ++Indent) + { + *TextBuffer++ = ' '; + *TextBuffer++ = ' '; + *TextBuffer++ = ' '; + *TextBuffer++ = ' '; + } + TextBuffer[0] = 0; + size_t TextBufferLeft = (TextBufferBase + sizeof(TextBufferBase)) - TextBuffer; + + + member_definition *Member = MemberDefs + MemberIndex; + + void *MemberPtr = (((u8 *)StructPtr) + Member->Offset); + if(Member->Flags & MetaMemberFlag_IsPointer) + { + MemberPtr = *(void **)MemberPtr; + } + + if(MemberPtr) + { + switch(Member->Type) + { + case MetaType_u32: + { + _snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %u", Member->Name, *(u32 *)MemberPtr); + } break; + + case MetaType_b32: + { + _snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %u", Member->Name, *(b32 *)MemberPtr); + } break; + + case MetaType_s32: + { + _snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %d", Member->Name, *(s32 *)MemberPtr); + } break; + + case MetaType_r32: + { + _snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: %f", Member->Name, *(r32 *)MemberPtr); + } break; + + case MetaType_v2: + { + _snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: {%f,%f}", + Member->Name, + ((v2 *)MemberPtr)->x, + ((v2 *)MemberPtr)->y); + } break; + + case MetaType_v3: + { + _snprintf_s(TextBuffer, TextBufferLeft, TextBufferLeft, "%s: {%f,%f,%f}", + Member->Name, + ((v3 *)MemberPtr)->x, + ((v3 *)MemberPtr)->y, + ((v3 *)MemberPtr)->z); + } break; + + META_HANDLE_TYPE_DUMP(MemberPtr, IndentLevel + 1); + } + } + + if(TextBuffer[0]) + { + DEBUGTextLine(TextBufferBase); + } + } +} + +internal void +DEBUGEnd(debug_state *DebugState, game_input *Input, loaded_bitmap *DrawBuffer) +{ + TIMED_FUNCTION(); + + render_group *RenderGroup = DebugState->RenderGroup; + + debug_event *HotEvent = 0; + + v2 MouseP = Unproject(DebugState->RenderGroup, V2(Input->MouseX, Input->MouseY)).xy; + DEBUGDrawMainMenu(DebugState, RenderGroup, MouseP); + DEBUGInteract(DebugState, Input, MouseP); + + if(DebugState->Compiling) + { + debug_process_state State = Platform.DEBUGGetProcessState(DebugState->Compiler); + if(State.IsRunning) + { + DEBUGTextLine("COMPILING"); + } + else + { + DebugState->Compiling = false; + } + } + + loaded_font *Font = DebugState->DebugFont; + hha_font *Info = DebugState->DebugFontInfo; + if(Font) + { +#if 0 + for(u32 CounterIndex = 0; + CounterIndex < DebugState->CounterCount; + ++CounterIndex) + { + debug_counter_state *Counter = DebugState->CounterStates + CounterIndex; + + + debug_statistic HitCount, CycleCount, CycleOverHit; + BeginDebugStatistic(&HitCount); + BeginDebugStatistic(&CycleCount); + BeginDebugStatistic(&CycleOverHit); + for(u32 SnapshotIndex = 0; + SnapshotIndex < DEBUG_SNAPSHOT_COUNT; + ++SnapshotIndex) + { + AccumDebugStatistic(&HitCount, Counter->Snapshots[SnapshotIndex].HitCount); + AccumDebugStatistic(&CycleCount, (u32)Counter->Snapshots[SnapshotIndex].CycleCount); + + r64 HOC = 0.0f; + if(Counter->Snapshots[SnapshotIndex].HitCount) + { + HOC = ((r64)Counter->Snapshots[SnapshotIndex].CycleCount / + (r64)Counter->Snapshots[SnapshotIndex].HitCount); + } + AccumDebugStatistic(&CycleOverHit, HOC); + } + EndDebugStatistic(&HitCount); + EndDebugStatistic(&CycleCount); + EndDebugStatistic(&CycleOverHit); + + if(Counter->BlockName) + { + if(CycleCount.Max > 0.0f) + { + r32 BarWidth = 4.0f; + r32 ChartLeft = 0.0f; + r32 ChartMinY = AtY; + r32 ChartHeight = Info->AscenderHeight*FontScale; + r32 Scale = 1.0f / (r32)CycleCount.Max; + for(u32 SnapshotIndex = 0; + SnapshotIndex < DEBUG_SNAPSHOT_COUNT; + ++SnapshotIndex) + { + r32 ThisProportion = Scale*(r32)Counter->Snapshots[SnapshotIndex].CycleCount; + r32 ThisHeight = ChartHeight*ThisProportion; + PushRect(RenderGroup, V3(ChartLeft + BarWidth*(r32)SnapshotIndex + 0.5f*BarWidth, ChartMinY + 0.5f*ThisHeight, 0.0f), V2(BarWidth, ThisHeight), V4(ThisProportion, 1, 0.0f, 1)); + } + } + +#if 1 + char TextBuffer[256]; + _snprintf_s(TextBuffer, sizeof(TextBuffer), + "%32s(%4d): %10ucy %8uh %10ucy/h", + Counter->BlockName, + Counter->LineNumber, + (u32)CycleCount.Avg, + (u32)HitCount.Avg, + (u32)CycleOverHit.Avg); + DEBUGTextLine(TextBuffer); +#endif + } + } +#endif + + if(DebugState->MostRecentFrame) + { + char TextBuffer[256]; + _snprintf_s(TextBuffer, sizeof(TextBuffer), + "Last frame time: %.02fms", + DebugState->MostRecentFrame->WallSecondsElapsed * 1000.0f); + DEBUGTextLine(TextBuffer); + } + } + + if(WasPressed(Input->MouseButtons[PlatformMouseButton_Left])) + { + if(HotEvent) + { + DebugState->ScopeToRecord = HotEvent->BlockName; + } + else + { + DebugState->ScopeToRecord = 0; + } + } + + TiledRenderGroupToOutput(DebugState->HighPriorityQueue, DebugState->RenderGroup, DrawBuffer); + EndRender(DebugState->RenderGroup); + + // NOTE(casey): Clear the UI state for the next frame + ZeroStruct(DebugState->NextHotInteraction); +} + +extern "C" DEBUG_GAME_FRAME_END(DEBUGGameFrameEnd) +{ + GlobalDebugTable->CurrentEventArrayIndex = !GlobalDebugTable->CurrentEventArrayIndex; + u64 ArrayIndex_EventIndex = AtomicExchangeU64(&GlobalDebugTable->EventArrayIndex_EventIndex, + (u64)GlobalDebugTable->CurrentEventArrayIndex << 32); + + u32 EventArrayIndex = ArrayIndex_EventIndex >> 32; + Assert(EventArrayIndex <= 1); + u32 EventCount = ArrayIndex_EventIndex & 0xFFFFFFFF; + + debug_state *DebugState = (debug_state *)Memory->DebugStorage; + if(DebugState) + { + game_assets *Assets = DEBUGGetGameAssets(Memory); + + DEBUGStart(DebugState, Assets, Buffer->Width, Buffer->Height); + CollateDebugRecords(DebugState, EventCount, GlobalDebugTable->Events[EventArrayIndex]); + + loaded_bitmap DrawBuffer = {}; + DrawBuffer.Width = Buffer->Width; + DrawBuffer.Height = Buffer->Height; + DrawBuffer.Pitch = Buffer->Pitch; + DrawBuffer.Memory = Buffer->Memory; + DEBUGEnd(DebugState, Input, &DrawBuffer); + } + + return(GlobalDebugTable); +} diff --git a/test_data/lots_of_files/handmade_debug.h b/test_data/lots_of_files/handmade_debug.h new file mode 100644 index 0000000..4b91890 --- /dev/null +++ b/test_data/lots_of_files/handmade_debug.h @@ -0,0 +1,258 @@ +#if !defined(HANDMADE_DEBUG_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#define DEBUG_MAX_VARIABLE_STACK_DEPTH 64 + +enum debug_variable_to_text_flag +{ + DEBUGVarToText_AddDebugUI = 0x1, + DEBUGVarToText_AddName = 0x2, + DEBUGVarToText_FloatSuffix = 0x4, + DEBUGVarToText_LineFeedEnd = 0x8, + DEBUGVarToText_NullTerminator = 0x10, + DEBUGVarToText_Colon = 0x20, + DEBUGVarToText_PrettyBools = 0x40, +}; + +struct debug_tree; + +struct debug_view_inline_block +{ + v2 Dim; +}; + +struct debug_view_collapsible +{ + b32 ExpandedAlways; + b32 ExpandedAltView; +}; + +enum debug_view_type +{ + DebugViewType_Unknown, + + DebugViewType_Basic, + DebugViewType_InlineBlock, + DebugViewType_Collapsible, +}; + +struct debug_view +{ + debug_id ID; + debug_view *NextInHash; + + debug_view_type Type; + union + { + debug_view_inline_block InlineBlock; + debug_view_collapsible Collapsible; + }; +}; + +struct debug_variable_group; +struct debug_variable_link +{ + debug_variable_link *Next; + debug_variable_link *Prev; + debug_variable_group *Children; + debug_event *Event; +}; + +struct debug_tree +{ + v2 UIP; + debug_variable_group *Group; + + debug_tree *Next; + debug_tree *Prev; +}; + +struct debug_variable_group +{ + debug_variable_link Sentinel; +}; + +struct render_group; +struct game_assets; +struct loaded_bitmap; +struct loaded_font; +struct hha_font; + +enum debug_text_op +{ + DEBUGTextOp_DrawText, + DEBUGTextOp_SizeText, +}; + +struct debug_counter_snapshot +{ + u32 HitCount; + u64 CycleCount; +}; + +struct debug_counter_state +{ + char *FileName; + char *BlockName; + + u32 LineNumber; +}; + +struct debug_frame_region +{ + // TODO(casey): Do we want to copy these out in their entirety? + debug_event *Event; + u64 CycleCount; + u16 LaneIndex; + u16 ColorIndex; + r32 MinT; + r32 MaxT; +}; + +#define MAX_REGIONS_PER_FRAME 2*4096 +struct debug_frame +{ + // IMPORTANT(casey): This actually gets freed as a set in FreeFrame! + + union + { + debug_frame *Next; + debug_frame *NextFree; + }; + + u64 BeginClock; + u64 EndClock; + r32 WallSecondsElapsed; + + r32 FrameBarScale; + + debug_variable_group *RootGroup; + + u32 RegionCount; + debug_frame_region *Regions; +}; + +struct open_debug_block +{ + union + { + open_debug_block *Parent; + open_debug_block *NextFree; + }; + + u32 StartingFrameIndex; + debug_event *OpeningEvent; + + // NOTE(casey): Only for data blocks? Probably! + debug_variable_group *Group; +}; + +struct debug_thread +{ + union + { + debug_thread *Next; + debug_thread *NextFree; + }; + + u32 ID; + u32 LaneIndex; + open_debug_block *FirstOpenCodeBlock; + open_debug_block *FirstOpenDataBlock; +}; + +enum debug_interaction_type +{ + DebugInteraction_None, + + DebugInteraction_NOP, + + DebugInteraction_AutoModifyVariable, + + DebugInteraction_ToggleValue, + DebugInteraction_DragValue, + DebugInteraction_TearValue, + + DebugInteraction_Resize, + DebugInteraction_Move, + + DebugInteraction_Select, +}; + +struct debug_interaction +{ + debug_id ID; + debug_interaction_type Type; + union + { + void *Generic; + debug_event *Event; + debug_tree *Tree; + v2 *P; + }; +}; + +struct debug_state +{ + b32 Initialized; + + platform_work_queue *HighPriorityQueue; + + memory_arena DebugArena; + + render_group *RenderGroup; + loaded_font *DebugFont; + hha_font *DebugFontInfo; + + b32 Compiling; + debug_executing_process Compiler; + + v2 MenuP; + b32 MenuActive; + + u32 SelectedIDCount; + debug_id SelectedID[64]; + + debug_variable_group *ValuesGroup; + + debug_variable_group *RootGroup; + debug_view *ViewHash[4096]; + debug_tree TreeSentinel; + + v2 LastMouseP; + debug_interaction Interaction; + debug_interaction HotInteraction; + debug_interaction NextHotInteraction; + b32 Paused; + + r32 LeftEdge; + r32 RightEdge; + r32 AtY; + r32 FontScale; + font_id FontID; + r32 GlobalWidth; + r32 GlobalHeight; + + char *ScopeToRecord; + + u32 FrameCount; + debug_frame *OldestFrame; + debug_frame *MostRecentFrame; + debug_frame *FirstFreeFrame; + + debug_frame *CollationFrame; + + u32 FrameBarLaneCount; + debug_thread *FirstThread; + debug_thread *FirstFreeThread; + open_debug_block *FirstFreeBlock; +}; + +#define HANDMADE_DEBUG_H +#endif diff --git a/test_data/lots_of_files/handmade_debug_interface.h b/test_data/lots_of_files/handmade_debug_interface.h new file mode 100644 index 0000000..1284652 --- /dev/null +++ b/test_data/lots_of_files/handmade_debug_interface.h @@ -0,0 +1,340 @@ +#if !defined(HANDMADE_DEBUG_INTERFACE_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +struct debug_table; +#define DEBUG_GAME_FRAME_END(name) debug_table *name(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer) +typedef DEBUG_GAME_FRAME_END(debug_game_frame_end); + +struct debug_id +{ + void *Value[2]; +}; + +#if HANDMADE_INTERNAL +enum debug_type +{ + DebugType_Unknown, + + DebugType_FrameMarker, + DebugType_BeginBlock, + DebugType_EndBlock, + + DebugType_OpenDataBlock, + DebugType_CloseDataBlock, + + DebugType_MarkDebugValue, + + DebugType_b32, + DebugType_r32, + DebugType_u32, + DebugType_s32, + DebugType_v2, + DebugType_v3, + DebugType_v4, + DebugType_rectangle2, + DebugType_rectangle3, + DebugType_bitmap_id, + DebugType_sound_id, + DebugType_font_id, + + DebugType_CounterThreadList, +// DebugVariableType_CounterFunctionList, +}; +struct debug_event +{ + u64 Clock; + // TODO(casey): To save space, we could put these two strings back-to-back with a null terminator in the middle + char *FileName; + char *BlockName; + u32 LineNumber; + u16 ThreadID; + u16 CoreIndex; + u8 Type; + union + { + debug_id DebugID; + debug_event *Value_debug_event; + + b32 Value_b32; + s32 Value_s32; + u32 Value_u32; + r32 Value_r32; + v2 Value_v2; + v3 Value_v3; + v4 Value_v4; + rectangle2 Value_rectangle2; + rectangle3 Value_rectangle3; + bitmap_id Value_bitmap_id; + sound_id Value_sound_id; + font_id Value_font_id; + }; +}; + +struct debug_table +{ + // TODO(casey): No attempt is currently made to ensure that the final + // debug records being written to the event array actually complete + // their output prior to the swap of the event array index. + u32 CurrentEventArrayIndex; + // TODO(casey): This could actually be a u32 atomic now, since we + // only need 1 bit to store which array we're using... + u64 volatile EventArrayIndex_EventIndex; + debug_event Events[2][16*65536]; +}; + +extern debug_table *GlobalDebugTable; + +#define RecordDebugEvent(EventType, Block) \ + u64 ArrayIndex_EventIndex = AtomicAddU64(&GlobalDebugTable->EventArrayIndex_EventIndex, 1); \ + u32 EventIndex = ArrayIndex_EventIndex & 0xFFFFFFFF; \ + Assert(EventIndex < ArrayCount(GlobalDebugTable->Events[0])); \ + debug_event *Event = GlobalDebugTable->Events[ArrayIndex_EventIndex >> 32] + EventIndex; \ + Event->Clock = __rdtsc(); \ + Event->Type = (u8)EventType; \ + Event->CoreIndex = 0; \ + Event->ThreadID = (u16)GetThreadID(); \ + Event->FileName = __FILE__; \ + Event->LineNumber = __LINE__; \ + Event->BlockName = Block; \ + +#define FRAME_MARKER(SecondsElapsedInit) \ + { \ + int Counter = __COUNTER__; \ + RecordDebugEvent(DebugType_FrameMarker, "Frame Marker"); \ + Event->Value_r32 = SecondsElapsedInit; \ +} + +#define TIMED_BLOCK__(BlockName, Number, ...) timed_block TimedBlock_##Number(__COUNTER__, __FILE__, __LINE__, BlockName, ## __VA_ARGS__) +#define TIMED_BLOCK_(BlockName, Number, ...) TIMED_BLOCK__(BlockName, Number, ## __VA_ARGS__) +#define TIMED_BLOCK(BlockName, ...) TIMED_BLOCK_(#BlockName, __LINE__, ## __VA_ARGS__) +#define TIMED_FUNCTION(...) TIMED_BLOCK_((char *)__FUNCTION__, __LINE__, ## __VA_ARGS__) + +#define BEGIN_BLOCK_(Counter, FileNameInit, LineNumberInit, BlockNameInit) \ + {RecordDebugEvent(DebugType_BeginBlock, BlockNameInit);} +#define END_BLOCK_(Counter) \ + { \ + RecordDebugEvent(DebugType_EndBlock, "End Block"); \ + } + +#define BEGIN_BLOCK(Name) \ + int Counter_##Name = __COUNTER__; \ + BEGIN_BLOCK_(Counter_##Name, __FILE__, __LINE__, #Name); + +#define END_BLOCK(Name) \ + END_BLOCK_(Counter_##Name); + +struct timed_block +{ + int Counter; + + timed_block(int CounterInit, char *FileName, int LineNumber, char *BlockName, u32 HitCountInit = 1) + { + // TODO(casey): Record the hit count value here? + Counter = CounterInit; + BEGIN_BLOCK_(Counter, FileName, LineNumber, BlockName); + } + + ~timed_block() + { + END_BLOCK_(Counter); + } +}; + +#else + +#define TIMED_BLOCK(...) +#define TIMED_FUNCTION(...) +#define BEGIN_BLOCK(...) +#define END_BLOCK(...) +#define FRAME_MARKER(...) + +#endif + +// +// NOTE(casey): Shared utils +// +inline u32 +StringLength(char *String) +{ + u32 Count = 0; + while(*String++) + { + ++Count; + } + return(Count); +} + +#ifdef __cplusplus +} +#endif + + +#if defined(__cplusplus) && HANDMADE_INTERNAL + +inline void +DEBUGValueSetEventData(debug_event *Event, r32 Value) +{ + Event->Type = DebugType_r32; + Event->Value_r32 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, u32 Value) +{ + Event->Type = DebugType_u32; + Event->Value_u32 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, s32 Value) +{ + Event->Type = DebugType_s32; + Event->Value_s32 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, v2 Value) +{ + Event->Type = DebugType_v2; + Event->Value_v2 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, v3 Value) +{ + Event->Type = DebugType_v3; + Event->Value_v3 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, v4 Value) +{ + Event->Type = DebugType_v4; + Event->Value_v4 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, rectangle2 Value) +{ + Event->Type = DebugType_rectangle2; + Event->Value_rectangle2 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, rectangle3 Value) +{ + Event->Type = DebugType_rectangle3; + Event->Value_rectangle3 = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, bitmap_id Value) +{ + Event->Type = DebugType_bitmap_id; + Event->Value_bitmap_id = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, sound_id Value) +{ + Event->Type = DebugType_sound_id; + Event->Value_sound_id = Value; +} + +inline void +DEBUGValueSetEventData(debug_event *Event, font_id Value) +{ + Event->Type = DebugType_font_id; + Event->Value_font_id = Value; +} + +#define DEBUG_BEGIN_DATA_BLOCK(Name, ID) \ + { \ + RecordDebugEvent(DebugType_OpenDataBlock, #Name); \ + Event->DebugID = ID; \ + } + +#define DEBUG_END_DATA_BLOCK() \ + { \ + RecordDebugEvent(DebugType_CloseDataBlock, "End Data Block"); \ + } + +#define DEBUG_VALUE(Value) \ + { \ + RecordDebugEvent(DebugType_Unknown, #Value); \ + DEBUGValueSetEventData(Event, Value); \ + } + +#define DEBUG_BEGIN_ARRAY(...) +#define DEBUG_END_ARRAY(...) + +inline debug_id DEBUG_POINTER_ID(void *Pointer) +{ + debug_id ID = {Pointer}; + + return(ID); +} + +#define DEBUG_UI_ENABLED 1 + +internal void DEBUG_HIT(debug_id ID, r32 ZValue); +internal b32 DEBUG_HIGHLIGHTED(debug_id ID, v4 *Color); +internal b32 DEBUG_REQUESTED(debug_id ID); + +inline debug_event DEBUGInitializeValue(debug_type Type, debug_event *SubEvent, char *Name, char *FileName, u32 LineNumber) +{ + RecordDebugEvent(DebugType_MarkDebugValue, ""); + Event->Value_debug_event = SubEvent; + + SubEvent->Clock = 0; + SubEvent->FileName = FileName; + SubEvent->BlockName = Name; + SubEvent->LineNumber = LineNumber; + SubEvent->ThreadID = 0; + SubEvent->CoreIndex = 0; + SubEvent->Type = (u8)Type; + + return(*SubEvent); +} + +#define DEBUG_IF__(Path) \ + local_persist debug_event DebugValue##Path = DEBUGInitializeValue((DebugValue##Path.Value_b32 = GlobalConstants_##Path, DebugType_b32), &DebugValue##Path, #Path, __FILE__, __LINE__); \ + if(DebugValue##Path.Value_b32) + +#define DEBUG_VARIABLE__(type, Path, Variable) \ + local_persist debug_event DebugValue##Variable = DEBUGInitializeValue((DebugValue##Variable.Value_##type = GlobalConstants_##Path##_##Variable, DebugType_##type), &DebugValue##Variable, #Path "_" #Variable, __FILE__, __LINE__); \ + type Variable = DebugValue##Variable.Value_##type; + +#else + +inline debug_id DEBUG_POINTER_ID(void *Pointer) {debug_id NullID = {}; return(NullID);} + +#define DEBUG_BEGIN_DATA_BLOCK(...) +#define DEBUG_END_DATA_BLOCK(...) +#define DEBUG_VALUE(...) +#define DEBUG_BEGIN_ARRAY(...) +#define DEBUG_END_ARRAY(...) +#define DEBUG_UI_ENABLED 0 +#define DEBUG_HIT(...) +#define DEBUG_HIGHLIGHTED(...) 0 +#define DEBUG_REQUESTED(...) 0 + +#define DEBUG_IF__(Path) if(GlobalConstants_##Path) +#define DEBUG_VARIABLE__(type, Path, Variable) type Variable = GlobalConstants_##Path##_##Variable; + +#endif + +#define DEBUG_IF_(Path) DEBUG_IF__(Path) +#define DEBUG_IF(Path) DEBUG_IF_(Path) + +#define DEBUG_VARIABLE_(type, Path, Variable) DEBUG_VARIABLE__(type, Path, Variable) +#define DEBUG_VARIABLE(type, Path, Variable) DEBUG_VARIABLE_(type, Path, Variable) + +#define HANDMADE_DEBUG_INTERFACE_H +#endif diff --git a/test_data/lots_of_files/handmade_entity.cpp b/test_data/lots_of_files/handmade_entity.cpp new file mode 100644 index 0000000..a2f4a29 --- /dev/null +++ b/test_data/lots_of_files/handmade_entity.cpp @@ -0,0 +1,19 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +inline move_spec +DefaultMoveSpec(void) +{ + move_spec Result; + + Result.UnitMaxAccelVector = false; + Result.Speed = 1.0f; + Result.Drag = 0.0f; + + return(Result); +} diff --git a/test_data/lots_of_files/handmade_entity.h b/test_data/lots_of_files/handmade_entity.h new file mode 100644 index 0000000..2cdfa3f --- /dev/null +++ b/test_data/lots_of_files/handmade_entity.h @@ -0,0 +1,76 @@ +#if !defined(HANDMADE_ENTITY_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#define InvalidP V3(100000.0f, 100000.0f, 100000.0f) + +inline bool32 +IsSet(sim_entity *Entity, uint32 Flag) +{ + bool32 Result = Entity->Flags & Flag; + + return(Result); +} + +inline void +AddFlags(sim_entity *Entity, uint32 Flag) +{ + Entity->Flags |= Flag; +} + +inline void +ClearFlags(sim_entity *Entity, uint32 Flag) +{ + Entity->Flags &= ~Flag; +} + +inline void +MakeEntityNonSpatial(sim_entity *Entity) +{ + AddFlags(Entity, EntityFlag_Nonspatial); + Entity->P = InvalidP; +} + +inline void +MakeEntitySpatial(sim_entity *Entity, v3 P, v3 dP) +{ + ClearFlags(Entity, EntityFlag_Nonspatial); + Entity->P = P; + Entity->dP = dP; +} + +inline v3 +GetEntityGroundPoint(sim_entity *Entity, v3 ForEntityP) +{ + v3 Result = ForEntityP; + + return(Result); +} + +inline v3 +GetEntityGroundPoint(sim_entity *Entity) +{ + v3 Result = GetEntityGroundPoint(Entity, Entity->P); + + return(Result); +} + +inline real32 +GetStairGround(sim_entity *Entity, v3 AtGroundPoint) +{ + Assert(Entity->Type == EntityType_Stairwell); + + rectangle2 RegionRect = RectCenterDim(Entity->P.xy, Entity->WalkableDim); + v2 Bary = Clamp01(GetBarycentric(RegionRect, AtGroundPoint.xy)); + real32 Result = Entity->P.z + Bary.y*Entity->WalkableHeight; + + return(Result); +} + +#define HANDMADE_ENTITY_H +#endif diff --git a/test_data/lots_of_files/handmade_file_formats.h b/test_data/lots_of_files/handmade_file_formats.h new file mode 100644 index 0000000..cbfc703 --- /dev/null +++ b/test_data/lots_of_files/handmade_file_formats.h @@ -0,0 +1,178 @@ +#if !defined(HANDMADE_FILE_FORMATS_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +enum asset_font_type +{ + FontType_Default = 0, + FontType_Debug = 10, +}; + +enum asset_tag_id +{ + Tag_Smoothness, + Tag_Flatness, + Tag_FacingDirection, // NOTE(casey): Angle in radians off of due right + Tag_UnicodeCodepoint, + Tag_FontType, // NOTE(casey): 0 - Default Game Font, 10 - Debug Font? + + Tag_Count, +}; + +enum asset_type_id +{ + Asset_None, + + // + // NOTE(casey): Bitmaps! + // + + Asset_Shadow, + Asset_Tree, + Asset_Sword, +// Asset_Stairwell, + Asset_Rock, + + Asset_Grass, + Asset_Tuft, + Asset_Stone, + + Asset_Head, + Asset_Cape, + Asset_Torso, + + Asset_Font, + Asset_FontGlyph, + + // + // NOTE(casey): Sounds! + // + + Asset_Bloop, + Asset_Crack, + Asset_Drop, + Asset_Glide, + Asset_Music, + Asset_Puhp, + + // + // + // + + Asset_Count, +}; + +#define HHA_CODE(a, b, c, d) (((uint32)(a) << 0) | ((uint32)(b) << 8) | ((uint32)(c) << 16) | ((uint32)(d) << 24)) + +#pragma pack(push, 1) + +struct hha_header +{ +#define HHA_MAGIC_VALUE HHA_CODE('h','h','a','f') + u32 MagicValue; + +#define HHA_VERSION 0 + u32 Version; + + u32 TagCount; + u32 AssetTypeCount; + u32 AssetCount; + + u64 Tags; // hha_tag[TagCount] + u64 AssetTypes; // hha_asset_type[AssetTypeCount] + u64 Assets; // hha_asset[AssetCount] + + // TODO(casey): Primacy numbers for asset files? + + /* TODO(casey): + + u32 FileGUID[8]; + u32 RemovalCount; + + struct hha_asset_removal + { + u32 FileGUID[8]; + u32 AssetIndex; + }; + + */ +}; + +struct hha_tag +{ + u32 ID; + r32 Value; +}; + +struct hha_asset_type +{ + u32 TypeID; + u32 FirstAssetIndex; + u32 OnePastLastAssetIndex; +}; + +enum hha_sound_chain +{ + HHASoundChain_None, + HHASoundChain_Loop, + HHASoundChain_Advance, +}; +struct hha_bitmap +{ + u32 Dim[2]; + r32 AlignPercentage[2]; + /* NOTE(casey): Data is: + + u32 Pixels[Dim[1]][Dim[0]] + */ +}; +struct hha_sound +{ + u32 SampleCount; + u32 ChannelCount; + u32 Chain; // NOTE(casey): hha_sound_chain + /* NOTE(casey): Data is: + + s16 Channels[ChannelCount][SampleCount] + */ +}; +struct hha_font_glyph +{ + u32 UnicodeCodePoint; + bitmap_id BitmapID; +}; +struct hha_font +{ + u32 OnePastHighestCodepoint; + u32 GlyphCount; + r32 AscenderHeight; + r32 DescenderHeight; + r32 ExternalLeading; + /* NOTE(casey): Data is: + + hha_font_glyph CodePoints[GlyphCount]; + r32 HorizontalAdvance[GlyphCount][GlyphCount]; + */ +}; +struct hha_asset +{ + u64 DataOffset; + u32 FirstTagIndex; + u32 OnePastLastTagIndex; + union + { + hha_bitmap Bitmap; + hha_sound Sound; + hha_font Font; + }; +}; + +#pragma pack(pop) + +#define HANDMADE_FILE_FORMATS_H +#endif diff --git a/test_data/lots_of_files/handmade_generated.h b/test_data/lots_of_files/handmade_generated.h new file mode 100644 index 0000000..cd3af53 --- /dev/null +++ b/test_data/lots_of_files/handmade_generated.h @@ -0,0 +1,69 @@ +member_definition MembersOf_sim_entity_collision_volume[] = +{ + {0, MetaType_v3, "OffsetP", (u32)&((sim_entity_collision_volume *)0)->OffsetP}, + {0, MetaType_v3, "Dim", (u32)&((sim_entity_collision_volume *)0)->Dim}, +}; +member_definition MembersOf_sim_entity_collision_volume_group[] = +{ + {0, MetaType_sim_entity_collision_volume, "TotalVolume", (u32)&((sim_entity_collision_volume_group *)0)->TotalVolume}, + {0, MetaType_u32, "VolumeCount", (u32)&((sim_entity_collision_volume_group *)0)->VolumeCount}, + {MetaMemberFlag_IsPointer, MetaType_sim_entity_collision_volume, "Volumes", (u32)&((sim_entity_collision_volume_group *)0)->Volumes}, +}; +member_definition MembersOf_sim_entity[] = +{ + {MetaMemberFlag_IsPointer, MetaType_world_chunk, "OldChunk", (u32)&((sim_entity *)0)->OldChunk}, + {0, MetaType_u32, "StorageIndex", (u32)&((sim_entity *)0)->StorageIndex}, + {0, MetaType_b32, "Updatable", (u32)&((sim_entity *)0)->Updatable}, + {0, MetaType_entity_type, "Type", (u32)&((sim_entity *)0)->Type}, + {0, MetaType_u32, "Flags", (u32)&((sim_entity *)0)->Flags}, + {0, MetaType_v3, "P", (u32)&((sim_entity *)0)->P}, + {0, MetaType_v3, "dP", (u32)&((sim_entity *)0)->dP}, + {0, MetaType_r32, "DistanceLimit", (u32)&((sim_entity *)0)->DistanceLimit}, + {MetaMemberFlag_IsPointer, MetaType_sim_entity_collision_volume_group, "Collision", (u32)&((sim_entity *)0)->Collision}, + {0, MetaType_r32, "FacingDirection", (u32)&((sim_entity *)0)->FacingDirection}, + {0, MetaType_r32, "tBob", (u32)&((sim_entity *)0)->tBob}, + {0, MetaType_s32, "dAbsTileZ", (u32)&((sim_entity *)0)->dAbsTileZ}, + {0, MetaType_u32, "HitPointMax", (u32)&((sim_entity *)0)->HitPointMax}, + {0, MetaType_hit_point, "HitPoint", (u32)&((sim_entity *)0)->HitPoint}, + {0, MetaType_entity_reference, "Sword", (u32)&((sim_entity *)0)->Sword}, + {0, MetaType_v2, "WalkableDim", (u32)&((sim_entity *)0)->WalkableDim}, + {0, MetaType_r32, "WalkableHeight", (u32)&((sim_entity *)0)->WalkableHeight}, +}; +member_definition MembersOf_sim_region[] = +{ + {MetaMemberFlag_IsPointer, MetaType_world, "World", (u32)&((sim_region *)0)->World}, + {0, MetaType_r32, "MaxEntityRadius", (u32)&((sim_region *)0)->MaxEntityRadius}, + {0, MetaType_r32, "MaxEntityVelocity", (u32)&((sim_region *)0)->MaxEntityVelocity}, + {0, MetaType_world_position, "Origin", (u32)&((sim_region *)0)->Origin}, + {0, MetaType_rectangle3, "Bounds", (u32)&((sim_region *)0)->Bounds}, + {0, MetaType_rectangle3, "UpdatableBounds", (u32)&((sim_region *)0)->UpdatableBounds}, + {0, MetaType_u32, "MaxEntityCount", (u32)&((sim_region *)0)->MaxEntityCount}, + {0, MetaType_u32, "EntityCount", (u32)&((sim_region *)0)->EntityCount}, + {MetaMemberFlag_IsPointer, MetaType_sim_entity, "Entities", (u32)&((sim_region *)0)->Entities}, + {0, MetaType_sim_entity_hash, "Hash", (u32)&((sim_region *)0)->Hash}, +}; +member_definition MembersOf_rectangle2[] = +{ + {0, MetaType_v2, "Min", (u32)&((rectangle2 *)0)->Min}, + {0, MetaType_v2, "Max", (u32)&((rectangle2 *)0)->Max}, +}; +member_definition MembersOf_rectangle3[] = +{ + {0, MetaType_v3, "Min", (u32)&((rectangle3 *)0)->Min}, + {0, MetaType_v3, "Max", (u32)&((rectangle3 *)0)->Max}, +}; +member_definition MembersOf_world_position[] = +{ + {0, MetaType_s32, "ChunkX", (u32)&((world_position *)0)->ChunkX}, + {0, MetaType_s32, "ChunkY", (u32)&((world_position *)0)->ChunkY}, + {0, MetaType_s32, "ChunkZ", (u32)&((world_position *)0)->ChunkZ}, + {0, MetaType_v3, "Offset_", (u32)&((world_position *)0)->Offset_}, +}; +#define META_HANDLE_TYPE_DUMP(MemberPtr, NextIndentLevel) \ + case MetaType_world_position: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_world_position), MembersOf_world_position, MemberPtr, (NextIndentLevel));} break; \ + case MetaType_rectangle3: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_rectangle3), MembersOf_rectangle3, MemberPtr, (NextIndentLevel));} break; \ + case MetaType_rectangle2: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_rectangle2), MembersOf_rectangle2, MemberPtr, (NextIndentLevel));} break; \ + case MetaType_sim_region: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_sim_region), MembersOf_sim_region, MemberPtr, (NextIndentLevel));} break; \ + case MetaType_sim_entity: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_sim_entity), MembersOf_sim_entity, MemberPtr, (NextIndentLevel));} break; \ + case MetaType_sim_entity_collision_volume_group: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_sim_entity_collision_volume_group), MembersOf_sim_entity_collision_volume_group, MemberPtr, (NextIndentLevel));} break; \ + case MetaType_sim_entity_collision_volume: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_sim_entity_collision_volume), MembersOf_sim_entity_collision_volume, MemberPtr, (NextIndentLevel));} break; diff --git a/test_data/lots_of_files/handmade_intrinsics.h b/test_data/lots_of_files/handmade_intrinsics.h new file mode 100644 index 0000000..4e1189c --- /dev/null +++ b/test_data/lots_of_files/handmade_intrinsics.h @@ -0,0 +1,159 @@ +#if !defined(HANDMADE_INTRINSICS_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +// +// TODO(casey): Convert all of these to platform-efficient versions +// and remove math.h +// + +#include "math.h" + +inline int32 +SignOf(int32 Value) +{ + int32 Result = (Value >= 0) ? 1 : -1; + return(Result); +} + +inline real32 +SignOf(real32 Value) +{ + real32 Result = (Value >= 0) ? 1.0f : -1.0f; + return(Result); +} + +inline real32 +SquareRoot(real32 Real32) +{ + real32 Result = sqrtf(Real32); + return(Result); +} + +inline real32 +AbsoluteValue(real32 Real32) +{ + real32 Result = fabs(Real32); + return(Result); +} + +inline uint32 +RotateLeft(uint32 Value, int32 Amount) +{ +#if COMPILER_MSVC + uint32 Result = _rotl(Value, Amount); +#else + // TODO(casey): Actually port this to other compiler platforms! + Amount &= 31; + uint32 Result = ((Value << Amount) | (Value >> (32 - Amount))); +#endif + + return(Result); +} + +inline uint32 +RotateRight(uint32 Value, int32 Amount) +{ +#if COMPILER_MSVC + uint32 Result = _rotr(Value, Amount); +#else + // TODO(casey): Actually port this to other compiler platforms! + Amount &= 31; + uint32 Result = ((Value >> Amount) | (Value << (32 - Amount))); +#endif + + return(Result); +} + +inline int32 +RoundReal32ToInt32(real32 Real32) +{ + int32 Result = (int32)roundf(Real32); + return(Result); +} + +inline uint32 +RoundReal32ToUInt32(real32 Real32) +{ + uint32 Result = (uint32)roundf(Real32); + return(Result); +} + +inline int32 +FloorReal32ToInt32(real32 Real32) +{ + int32 Result = (int32)floorf(Real32); + return(Result); +} + +inline int32 +CeilReal32ToInt32(real32 Real32) +{ + int32 Result = (int32)ceilf(Real32); + return(Result); +} + +inline int32 +TruncateReal32ToInt32(real32 Real32) +{ + int32 Result = (int32)Real32; + return(Result); +} + +inline real32 +Sin(real32 Angle) +{ + real32 Result = sinf(Angle); + return(Result); +} + +inline real32 +Cos(real32 Angle) +{ + real32 Result = cosf(Angle); + return(Result); +} + +inline real32 +ATan2(real32 Y, real32 X) +{ + real32 Result = atan2f(Y, X); + return(Result); +} + +struct bit_scan_result +{ + bool32 Found; + uint32 Index; +}; +inline bit_scan_result +FindLeastSignificantSetBit(uint32 Value) +{ + bit_scan_result Result = {}; + +#if COMPILER_MSVC + Result.Found = _BitScanForward((unsigned long *)&Result.Index, Value); +#else + for(uint32 Test = 0; + Test < 32; + ++Test) + { + if(Value & (1 << Test)) + { + Result.Index = Test; + Result.Found = true; + break; + } + } +#endif + + return(Result); +} + +#define HANDMADE_INTRINSICS_H +#endif diff --git a/test_data/lots_of_files/handmade_math.h b/test_data/lots_of_files/handmade_math.h new file mode 100644 index 0000000..1a0d17f --- /dev/null +++ b/test_data/lots_of_files/handmade_math.h @@ -0,0 +1,976 @@ +#if !defined(HANDMADE_MATH_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +inline v2 +V2i(int32 X, int32 Y) +{ + v2 Result = {(real32)X, (real32)Y}; + + return(Result); +} + +inline v2 +V2i(uint32 X, uint32 Y) +{ + v2 Result = {(real32)X, (real32)Y}; + + return(Result); +} + +inline v2 +V2(real32 X, real32 Y) +{ + v2 Result; + + Result.x = X; + Result.y = Y; + + return(Result); +} + +inline v3 +V3(real32 X, real32 Y, real32 Z) +{ + v3 Result; + + Result.x = X; + Result.y = Y; + Result.z = Z; + + return(Result); +} + +inline v3 +V3(v2 XY, real32 Z) +{ + v3 Result; + + Result.x = XY.x; + Result.y = XY.y; + Result.z = Z; + + return(Result); +} + +inline v4 +V4(real32 X, real32 Y, real32 Z, real32 W) +{ + v4 Result; + + Result.x = X; + Result.y = Y; + Result.z = Z; + Result.w = W; + + return(Result); +} + +inline v4 +V4(v3 XYZ, real32 W) +{ + v4 Result; + + Result.xyz = XYZ; + Result.w = W; + + return(Result); +} + +// +// NOTE(casey): Scalar operations +// + +inline real32 +Square(real32 A) +{ + real32 Result = A*A; + + return(Result); +} + +inline real32 +Lerp(real32 A, real32 t, real32 B) +{ + real32 Result = (1.0f - t)*A + t*B; + + return(Result); +} + +inline real32 +Clamp(real32 Min, real32 Value, real32 Max) +{ + real32 Result = Value; + + if(Result < Min) + { + Result = Min; + } + else if(Result > Max) + { + Result = Max; + } + + return(Result); +} + +inline real32 +Clamp01(real32 Value) +{ + real32 Result = Clamp(0.0f, Value, 1.0f); + + return(Result); +} + +inline real32 +Clamp01MapToRange(real32 Min, real32 t, real32 Max) +{ + real32 Result = 0.0f; + + real32 Range = Max - Min; + if(Range != 0.0f) + { + Result = Clamp01((t - Min) / Range); + } + + return(Result); +} + +inline real32 +SafeRatioN(real32 Numerator, real32 Divisor, real32 N) +{ + real32 Result = N; + + if(Divisor != 0.0f) + { + Result = Numerator / Divisor; + } + + return(Result); +} + +inline real32 +SafeRatio0(real32 Numerator, real32 Divisor) +{ + real32 Result = SafeRatioN(Numerator, Divisor, 0.0f); + + return(Result); +} + +inline real32 +SafeRatio1(real32 Numerator, real32 Divisor) +{ + real32 Result = SafeRatioN(Numerator, Divisor, 1.0f); + + return(Result); +} + +// +// NOTE(casey): v2 operations +// + +inline v2 +Perp(v2 A) +{ + v2 Result = {-A.y, A.x}; + return(Result); +} + +inline v2 +operator*(real32 A, v2 B) +{ + v2 Result; + + Result.x = A*B.x; + Result.y = A*B.y; + + return(Result); +} + +inline v2 +operator*(v2 B, real32 A) +{ + v2 Result = A*B; + + return(Result); +} + +inline v2 & +operator*=(v2 &B, real32 A) +{ + B = A * B; + + return(B); +} + +inline v2 +operator-(v2 A) +{ + v2 Result; + + Result.x = -A.x; + Result.y = -A.y; + + return(Result); +} + +inline v2 +operator+(v2 A, v2 B) +{ + v2 Result; + + Result.x = A.x + B.x; + Result.y = A.y + B.y; + + return(Result); +} + +inline v2 & +operator+=(v2 &A, v2 B) +{ + A = A + B; + + return(A); +} + +inline v2 +operator-(v2 A, v2 B) +{ + v2 Result; + + Result.x = A.x - B.x; + Result.y = A.y - B.y; + + return(Result); +} + +inline v2 & +operator-=(v2 &A, v2 B) +{ + A = A - B; + + return(A); +} + +inline v2 +Hadamard(v2 A, v2 B) +{ + v2 Result = {A.x*B.x, A.y*B.y}; + + return(Result); +} + +inline real32 +Inner(v2 A, v2 B) +{ + real32 Result = A.x*B.x + A.y*B.y; + + return(Result); +} + +inline real32 +LengthSq(v2 A) +{ + real32 Result = Inner(A, A); + + return(Result); +} + +inline real32 +Length(v2 A) +{ + real32 Result = SquareRoot(LengthSq(A)); + return(Result); +} + +inline v2 +Clamp01(v2 Value) +{ + v2 Result; + + Result.x = Clamp01(Value.x); + Result.y = Clamp01(Value.y); + + return(Result); +} + +inline v2 +Arm2(r32 Angle) +{ + v2 Result = {Cos(Angle), Sin(Angle)}; + + return(Result); +} + +// +// NOTE(casey): v3 operations +// + +inline v3 +operator*(real32 A, v3 B) +{ + v3 Result; + + Result.x = A*B.x; + Result.y = A*B.y; + Result.z = A*B.z; + + return(Result); +} + +inline v3 +operator*(v3 B, real32 A) +{ + v3 Result = A*B; + + return(Result); +} + +inline v3 & +operator*=(v3 &B, real32 A) +{ + B = A * B; + + return(B); +} + +inline v3 +operator-(v3 A) +{ + v3 Result; + + Result.x = -A.x; + Result.y = -A.y; + Result.z = -A.z; + + return(Result); +} + +inline v3 +operator+(v3 A, v3 B) +{ + v3 Result; + + Result.x = A.x + B.x; + Result.y = A.y + B.y; + Result.z = A.z + B.z; + + return(Result); +} + +inline v3 & +operator+=(v3 &A, v3 B) +{ + A = A + B; + + return(A); +} + +inline v3 +operator-(v3 A, v3 B) +{ + v3 Result; + + Result.x = A.x - B.x; + Result.y = A.y - B.y; + Result.z = A.z - B.z; + + return(Result); +} + +inline v3 & +operator-=(v3 &A, v3 B) +{ + A = A - B; + + return(A); +} + +inline v3 +Hadamard(v3 A, v3 B) +{ + v3 Result = {A.x*B.x, A.y*B.y, A.z*B.z}; + + return(Result); +} + +inline real32 +Inner(v3 A, v3 B) +{ + real32 Result = A.x*B.x + A.y*B.y + A.z*B.z; + + return(Result); +} + +inline real32 +LengthSq(v3 A) +{ + real32 Result = Inner(A, A); + + return(Result); +} + +inline real32 +Length(v3 A) +{ + real32 Result = SquareRoot(LengthSq(A)); + return(Result); +} + +inline v3 +Normalize(v3 A) +{ + v3 Result = A * (1.0f / Length(A)); + + return(Result); +} + +inline v3 +Clamp01(v3 Value) +{ + v3 Result; + + Result.x = Clamp01(Value.x); + Result.y = Clamp01(Value.y); + Result.z = Clamp01(Value.z); + + return(Result); +} + +inline v3 +Lerp(v3 A, real32 t, v3 B) +{ + v3 Result = (1.0f - t)*A + t*B; + + return(Result); +} + +// +// NOTE(casey): v4 operations +// + +inline v4 +operator*(real32 A, v4 B) +{ + v4 Result; + + Result.x = A*B.x; + Result.y = A*B.y; + Result.z = A*B.z; + Result.w = A*B.w; + + return(Result); +} + +inline v4 +operator*(v4 B, real32 A) +{ + v4 Result = A*B; + + return(Result); +} + +inline v4 & +operator*=(v4 &B, real32 A) +{ + B = A * B; + + return(B); +} + +inline v4 +operator-(v4 A) +{ + v4 Result; + + Result.x = -A.x; + Result.y = -A.y; + Result.z = -A.z; + Result.w = -A.w; + + return(Result); +} + +inline v4 +operator+(v4 A, v4 B) +{ + v4 Result; + + Result.x = A.x + B.x; + Result.y = A.y + B.y; + Result.z = A.z + B.z; + Result.w = A.w + B.w; + + return(Result); +} + +inline v4 & +operator+=(v4 &A, v4 B) +{ + A = A + B; + + return(A); +} + +inline v4 +operator-(v4 A, v4 B) +{ + v4 Result; + + Result.x = A.x - B.x; + Result.y = A.y - B.y; + Result.z = A.z - B.z; + Result.w = A.w - B.w; + + return(Result); +} + +inline v4 & +operator-=(v4 &A, v4 B) +{ + A = A - B; + + return(A); +} + +inline v4 +Hadamard(v4 A, v4 B) +{ + v4 Result = {A.x*B.x, A.y*B.y, A.z*B.z, A.w*B.w}; + + return(Result); +} + +inline real32 +Inner(v4 A, v4 B) +{ + real32 Result = A.x*B.x + A.y*B.y + A.z*B.z + A.w*B.w; + + return(Result); +} + +inline real32 +LengthSq(v4 A) +{ + real32 Result = Inner(A, A); + + return(Result); +} + +inline real32 +Length(v4 A) +{ + real32 Result = SquareRoot(LengthSq(A)); + return(Result); +} + +inline v4 +Clamp01(v4 Value) +{ + v4 Result; + + Result.x = Clamp01(Value.x); + Result.y = Clamp01(Value.y); + Result.z = Clamp01(Value.z); + Result.w = Clamp01(Value.w); + + return(Result); +} + +inline v4 +Lerp(v4 A, real32 t, v4 B) +{ + v4 Result = (1.0f - t)*A + t*B; + + return(Result); +} + +// +// NOTE(casey): Rectangle2 +// + +inline rectangle2 +InvertedInfinityRectangle2(void) +{ + rectangle2 Result; + + Result.Min.x = Result.Min.y = Real32Maximum; + Result.Max.x = Result.Max.y = -Real32Maximum; + + return(Result); +} + +inline rectangle2 +Union(rectangle2 A, rectangle2 B) +{ + rectangle2 Result; + + Result.Min.x = (A.Min.x < B.Min.x) ? A.Min.x : B.Min.x; + Result.Min.y = (A.Min.y < B.Min.y) ? A.Min.y : B.Min.y; + Result.Max.x = (A.Max.x > B.Max.x) ? A.Max.x : B.Max.x; + Result.Max.y = (A.Max.y > B.Max.y) ? A.Max.y : B.Max.y; + + return(Result); +} + +inline v2 +GetMinCorner(rectangle2 Rect) +{ + v2 Result = Rect.Min; + return(Result); +} + +inline v2 +GetMaxCorner(rectangle2 Rect) +{ + v2 Result = Rect.Max; + return(Result); +} + +inline v2 +GetDim(rectangle2 Rect) +{ + v2 Result = Rect.Max - Rect.Min; + return(Result); +} + +inline v2 +GetCenter(rectangle2 Rect) +{ + v2 Result = 0.5f*(Rect.Min + Rect.Max); + return(Result); +} + +inline rectangle2 +RectMinMax(v2 Min, v2 Max) +{ + rectangle2 Result; + + Result.Min = Min; + Result.Max = Max; + + return(Result); +} + +inline rectangle2 +RectMinDim(v2 Min, v2 Dim) +{ + rectangle2 Result; + + Result.Min = Min; + Result.Max = Min + Dim; + + return(Result); +} + +inline rectangle2 +RectCenterHalfDim(v2 Center, v2 HalfDim) +{ + rectangle2 Result; + + Result.Min = Center - HalfDim; + Result.Max = Center + HalfDim; + + return(Result); +} + +inline rectangle2 +AddRadiusTo(rectangle2 A, v2 Radius) +{ + rectangle2 Result; + Result.Min = A.Min - Radius; + Result.Max = A.Max + Radius; + + return(Result); +} + +inline rectangle2 +Offset(rectangle2 A, v2 Offset) +{ + rectangle2 Result; + + Result.Min = A.Min + Offset; + Result.Max = A.Max + Offset; + + return(Result); +} + +inline rectangle2 +RectCenterDim(v2 Center, v2 Dim) +{ + rectangle2 Result = RectCenterHalfDim(Center, 0.5f*Dim); + + return(Result); +} + +inline bool32 +IsInRectangle(rectangle2 Rectangle, v2 Test) +{ + bool32 Result = ((Test.x >= Rectangle.Min.x) && + (Test.y >= Rectangle.Min.y) && + (Test.x < Rectangle.Max.x) && + (Test.y < Rectangle.Max.y)); + + return(Result); +} + +inline v2 +GetBarycentric(rectangle2 A, v2 P) +{ + v2 Result; + + Result.x = SafeRatio0(P.x - A.Min.x, A.Max.x - A.Min.x); + Result.y = SafeRatio0(P.y - A.Min.y, A.Max.y - A.Min.y); + + return(Result); +} + +// +// NOTE(casey): Rectangle3 +// + +inline v3 +GetMinCorner(rectangle3 Rect) +{ + v3 Result = Rect.Min; + return(Result); +} + +inline v3 +GetMaxCorner(rectangle3 Rect) +{ + v3 Result = Rect.Max; + return(Result); +} + +inline v3 +GetDim(rectangle3 Rect) +{ + v3 Result = Rect.Max - Rect.Min; + return(Result); +} + +inline v3 +GetCenter(rectangle3 Rect) +{ + v3 Result = 0.5f*(Rect.Min + Rect.Max); + return(Result); +} + +inline rectangle3 +RectMinMax(v3 Min, v3 Max) +{ + rectangle3 Result; + + Result.Min = Min; + Result.Max = Max; + + return(Result); +} + +inline rectangle3 +RectMinDim(v3 Min, v3 Dim) +{ + rectangle3 Result; + + Result.Min = Min; + Result.Max = Min + Dim; + + return(Result); +} + +inline rectangle3 +RectCenterHalfDim(v3 Center, v3 HalfDim) +{ + rectangle3 Result; + + Result.Min = Center - HalfDim; + Result.Max = Center + HalfDim; + + return(Result); +} + +inline rectangle3 +AddRadiusTo(rectangle3 A, v3 Radius) +{ + rectangle3 Result; + + Result.Min = A.Min - Radius; + Result.Max = A.Max + Radius; + + return(Result); +} + +inline rectangle3 +Offset(rectangle3 A, v3 Offset) +{ + rectangle3 Result; + + Result.Min = A.Min + Offset; + Result.Max = A.Max + Offset; + + return(Result); +} + +inline rectangle3 +RectCenterDim(v3 Center, v3 Dim) +{ + rectangle3 Result = RectCenterHalfDim(Center, 0.5f*Dim); + + return(Result); +} + +inline bool32 +IsInRectangle(rectangle3 Rectangle, v3 Test) +{ + bool32 Result = ((Test.x >= Rectangle.Min.x) && + (Test.y >= Rectangle.Min.y) && + (Test.z >= Rectangle.Min.z) && + (Test.x < Rectangle.Max.x) && + (Test.y < Rectangle.Max.y) && + (Test.z < Rectangle.Max.z)); + + return(Result); +} + +inline bool32 +RectanglesIntersect(rectangle3 A, rectangle3 B) +{ + bool32 Result = !((B.Max.x <= A.Min.x) || + (B.Min.x >= A.Max.x) || + (B.Max.y <= A.Min.y) || + (B.Min.y >= A.Max.y) || + (B.Max.z <= A.Min.z) || + (B.Min.z >= A.Max.z)); + return(Result); +} + +inline v3 +GetBarycentric(rectangle3 A, v3 P) +{ + v3 Result; + + Result.x = SafeRatio0(P.x - A.Min.x, A.Max.x - A.Min.x); + Result.y = SafeRatio0(P.y - A.Min.y, A.Max.y - A.Min.y); + Result.z = SafeRatio0(P.z - A.Min.z, A.Max.z - A.Min.z); + + return(Result); +} + +inline rectangle2 +ToRectangleXY(rectangle3 A) +{ + rectangle2 Result; + + Result.Min = A.Min.xy; + Result.Max = A.Max.xy; + + return(Result); +} + +// +// +// + +struct rectangle2i +{ + int32 MinX, MinY; + int32 MaxX, MaxY; +}; + +inline rectangle2i +Intersect(rectangle2i A, rectangle2i B) +{ + rectangle2i Result; + + Result.MinX = (A.MinX < B.MinX) ? B.MinX : A.MinX; + Result.MinY = (A.MinY < B.MinY) ? B.MinY : A.MinY; + Result.MaxX = (A.MaxX > B.MaxX) ? B.MaxX : A.MaxX; + Result.MaxY = (A.MaxY > B.MaxY) ? B.MaxY : A.MaxY; + + return(Result); +} + +inline rectangle2i +Union(rectangle2i A, rectangle2i B) +{ + rectangle2i Result; + + Result.MinX = (A.MinX < B.MinX) ? A.MinX : B.MinX; + Result.MinY = (A.MinY < B.MinY) ? A.MinY : B.MinY; + Result.MaxX = (A.MaxX > B.MaxX) ? A.MaxX : B.MaxX; + Result.MaxY = (A.MaxY > B.MaxY) ? A.MaxY : B.MaxY; + + return(Result); +} + +inline int32 +GetClampedRectArea(rectangle2i A) +{ + int32 Width = (A.MaxX - A.MinX); + int32 Height = (A.MaxY - A.MinY); + int32 Result = 0; + if((Width > 0) && (Height > 0)) + { + Result = Width*Height; + } + + return(Result); +} + +inline bool32 +HasArea(rectangle2i A) +{ + bool32 Result = ((A.MinX < A.MaxX) && (A.MinY < A.MaxY)); + + return(Result); +} + +inline rectangle2i +InvertedInfinityRectangle2i(void) +{ + rectangle2i Result; + + Result.MinX = Result.MinY = INT_MAX; + Result.MaxX = Result.MaxY = -INT_MAX; + + return(Result); +} + +inline v4 +SRGB255ToLinear1(v4 C) +{ + v4 Result; + + real32 Inv255 = 1.0f / 255.0f; + + Result.r = Square(Inv255*C.r); + Result.g = Square(Inv255*C.g); + Result.b = Square(Inv255*C.b); + Result.a = Inv255*C.a; + + return(Result); +} + +inline v4 +Linear1ToSRGB255(v4 C) +{ + v4 Result; + + real32 One255 = 255.0f; + + Result.r = One255*SquareRoot(C.r); + Result.g = One255*SquareRoot(C.g); + Result.b = One255*SquareRoot(C.b); + Result.a = One255*C.a; + + return(Result); +} + +#define HANDMADE_MATH_H +#endif diff --git a/test_data/lots_of_files/handmade_meta.cpp b/test_data/lots_of_files/handmade_meta.cpp new file mode 100644 index 0000000..3bf7f17 --- /dev/null +++ b/test_data/lots_of_files/handmade_meta.cpp @@ -0,0 +1,9 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#include "handmade_generated.h" diff --git a/test_data/lots_of_files/handmade_meta.h b/test_data/lots_of_files/handmade_meta.h new file mode 100644 index 0000000..d00709d --- /dev/null +++ b/test_data/lots_of_files/handmade_meta.h @@ -0,0 +1,46 @@ +#if !defined(HANDMADE_META_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +enum meta_type +{ + MetaType_world_chunk, + MetaType_u32, + MetaType_b32, + MetaType_entity_type, + MetaType_v3, + MetaType_r32, + MetaType_sim_entity_collision_volume_group, + MetaType_s32, + MetaType_hit_point, + MetaType_entity_reference, + MetaType_v2, + MetaType_world, + MetaType_world_position, + MetaType_rectangle2, + MetaType_rectangle3, + MetaType_sim_region, + MetaType_sim_entity, + MetaType_sim_entity_hash, + MetaType_sim_entity_collision_volume, +}; + +enum member_definition_flag +{ + MetaMemberFlag_IsPointer = 0x1, +}; +struct member_definition +{ + u32 Flags; + meta_type Type; + char *Name; + u32 Offset; +}; + +#define HANDMADE_META_H +#endif diff --git a/test_data/lots_of_files/handmade_optimized.cpp b/test_data/lots_of_files/handmade_optimized.cpp new file mode 100644 index 0000000..cdccdbb --- /dev/null +++ b/test_data/lots_of_files/handmade_optimized.cpp @@ -0,0 +1,377 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#define IGNORED_TIMED_FUNCTION TIMED_FUNCTION +#define IGNORED_TIMED_BLOCK TIMED_BLOCK + +#define internal +#include "handmade.h" + +#if 0 +#include <iacaMarks.h> +#else +#define IACA_VC64_START +#define IACA_VC64_END +#endif + +void +DrawRectangleQuickly(loaded_bitmap *Buffer, v2 Origin, v2 XAxis, v2 YAxis, v4 Color, + loaded_bitmap *Texture, real32 PixelsToMeters, + rectangle2i ClipRect, bool32 Even) +{ + IGNORED_TIMED_FUNCTION(); + + // NOTE(casey): Premultiply color up front + Color.rgb *= Color.a; + + real32 XAxisLength = Length(XAxis); + real32 YAxisLength = Length(YAxis); + + v2 NxAxis = (YAxisLength / XAxisLength) * XAxis; + v2 NyAxis = (XAxisLength / YAxisLength) * YAxis; + + // NOTE(casey): NzScale could be a parameter if we want people to + // have control over the amount of scaling in the Z direction + // that the normals appear to have. + real32 NzScale = 0.5f*(XAxisLength + YAxisLength); + + real32 InvXAxisLengthSq = 1.0f / LengthSq(XAxis); + real32 InvYAxisLengthSq = 1.0f / LengthSq(YAxis); + + rectangle2i FillRect = InvertedInfinityRectangle2i(); + + v2 P[4] = {Origin, Origin + XAxis, Origin + XAxis + YAxis, Origin + YAxis}; + for(int PIndex = 0; + PIndex < ArrayCount(P); + ++PIndex) + { + v2 TestP = P[PIndex]; + int FloorX = FloorReal32ToInt32(TestP.x); + int CeilX = CeilReal32ToInt32(TestP.x) + 1; + int FloorY = FloorReal32ToInt32(TestP.y); + int CeilY = CeilReal32ToInt32(TestP.y) + 1; + + if(FillRect.MinX > FloorX) {FillRect.MinX = FloorX;} + if(FillRect.MinY > FloorY) {FillRect.MinY = FloorY;} + if(FillRect.MaxX < CeilX) {FillRect.MaxX = CeilX;} + if(FillRect.MaxY < CeilY) {FillRect.MaxY = CeilY;} + } + +// rectangle2i ClipRect = {0, 0, WidthMax, HeightMax}; +// rectangle2i ClipRect = {128, 128, 256, 256}; + FillRect = Intersect(ClipRect, FillRect); + if(!Even == (FillRect.MinY & 1)) + { + FillRect.MinY += 1; + } + + if(HasArea(FillRect)) + { + __m128i StartClipMask = _mm_set1_epi8(-1); + __m128i EndClipMask = _mm_set1_epi8(-1); + + __m128i StartClipMasks[] = + { + _mm_slli_si128(StartClipMask, 0*4), + _mm_slli_si128(StartClipMask, 1*4), + _mm_slli_si128(StartClipMask, 2*4), + _mm_slli_si128(StartClipMask, 3*4), + }; + + __m128i EndClipMasks[] = + { + _mm_srli_si128(EndClipMask, 0*4), + _mm_srli_si128(EndClipMask, 3*4), + _mm_srli_si128(EndClipMask, 2*4), + _mm_srli_si128(EndClipMask, 1*4), + }; + + if(FillRect.MinX & 3) + { + StartClipMask = StartClipMasks[FillRect.MinX & 3]; + FillRect.MinX = FillRect.MinX & ~3; + } + + if(FillRect.MaxX & 3) + { + EndClipMask = EndClipMasks[FillRect.MaxX & 3]; + FillRect.MaxX = (FillRect.MaxX & ~3) + 4; + } + + v2 nXAxis = InvXAxisLengthSq*XAxis; + v2 nYAxis = InvYAxisLengthSq*YAxis; + + real32 Inv255 = 1.0f / 255.0f; + __m128 Inv255_4x = _mm_set1_ps(Inv255); + real32 One255 = 255.0f; + + __m128 One = _mm_set1_ps(1.0f); + __m128 Half = _mm_set1_ps(0.5f); + __m128 Four_4x = _mm_set1_ps(4.0f); + __m128 One255_4x = _mm_set1_ps(255.0f); + __m128 Zero = _mm_set1_ps(0.0f); + __m128i MaskFF = _mm_set1_epi32(0xFF); + __m128i MaskFFFF = _mm_set1_epi32(0xFFFF); + __m128i MaskFF00FF = _mm_set1_epi32(0x00FF00FF); + __m128 Colorr_4x = _mm_set1_ps(Color.r); + __m128 Colorg_4x = _mm_set1_ps(Color.g); + __m128 Colorb_4x = _mm_set1_ps(Color.b); + __m128 Colora_4x = _mm_set1_ps(Color.a); + __m128 nXAxisx_4x = _mm_set1_ps(nXAxis.x); + __m128 nXAxisy_4x = _mm_set1_ps(nXAxis.y); + __m128 nYAxisx_4x = _mm_set1_ps(nYAxis.x); + __m128 nYAxisy_4x = _mm_set1_ps(nYAxis.y); + __m128 Originx_4x = _mm_set1_ps(Origin.x); + __m128 Originy_4x = _mm_set1_ps(Origin.y); + __m128 MaxColorValue = _mm_set1_ps(255.0f*255.0f); + __m128i TexturePitch_4x = _mm_set1_epi32(Texture->Pitch); + + __m128 WidthM2 = _mm_set1_ps((real32)(Texture->Width - 2)); + __m128 HeightM2 = _mm_set1_ps((real32)(Texture->Height - 2)); + + uint8 *Row = ((uint8 *)Buffer->Memory + + FillRect.MinX*BITMAP_BYTES_PER_PIXEL + + FillRect.MinY*Buffer->Pitch); + int32 RowAdvance = 2*Buffer->Pitch; + + void *TextureMemory = Texture->Memory; + int32 TexturePitch = Texture->Pitch; + + int MinY = FillRect.MinY; + int MaxY = FillRect.MaxY; + int MinX = FillRect.MinX; + int MaxX = FillRect.MaxX; + + IGNORED_TIMED_BLOCK(PixelFill, GetClampedRectArea(FillRect) / 2); + for(int Y = MinY; + Y < MaxY; + Y += 2) + { + __m128 PixelPy = _mm_set1_ps((real32)Y); + PixelPy = _mm_sub_ps(PixelPy, Originy_4x); + __m128 PynX = _mm_mul_ps(PixelPy, nXAxisy_4x); + __m128 PynY = _mm_mul_ps(PixelPy, nYAxisy_4x); + + __m128 PixelPx = _mm_set_ps((real32)(MinX + 3), + (real32)(MinX + 2), + (real32)(MinX + 1), + (real32)(MinX + 0)); + PixelPx = _mm_sub_ps(PixelPx, Originx_4x); + + __m128i ClipMask = StartClipMask; + + uint32 *Pixel = (uint32 *)Row; + for(int XI = MinX; + XI < MaxX; + XI += 4) + { +#define mmSquare(a) _mm_mul_ps(a, a) +#define M(a, i) ((float *)&(a))[i] +#define Mi(a, i) ((uint32 *)&(a))[i] + + + IACA_VC64_START; + __m128 U = _mm_add_ps(_mm_mul_ps(PixelPx, nXAxisx_4x), PynX); + __m128 V = _mm_add_ps(_mm_mul_ps(PixelPx, nYAxisx_4x), PynY); + + __m128i WriteMask = _mm_castps_si128(_mm_and_ps(_mm_and_ps(_mm_cmpge_ps(U, Zero), + _mm_cmple_ps(U, One)), + _mm_and_ps(_mm_cmpge_ps(V, Zero), + _mm_cmple_ps(V, One)))); + WriteMask = _mm_and_si128(WriteMask, ClipMask); + +// TODO(casey): Later, re-check if this helps +// if(_mm_movemask_epi8(WriteMask)) + { + __m128i OriginalDest = _mm_load_si128((__m128i *)Pixel); + + U = _mm_min_ps(_mm_max_ps(U, Zero), One); + V = _mm_min_ps(_mm_max_ps(V, Zero), One); + + // NOTE(casey): Bias texture coordinates to start + // on the boundary between the 0,0 and 1,1 pixels. + __m128 tX = _mm_add_ps(_mm_mul_ps(U, WidthM2), Half); + __m128 tY = _mm_add_ps(_mm_mul_ps(V, HeightM2), Half); + + __m128i FetchX_4x = _mm_cvttps_epi32(tX); + __m128i FetchY_4x = _mm_cvttps_epi32(tY); + + __m128 fX = _mm_sub_ps(tX, _mm_cvtepi32_ps(FetchX_4x)); + __m128 fY = _mm_sub_ps(tY, _mm_cvtepi32_ps(FetchY_4x)); + + FetchX_4x = _mm_slli_epi32(FetchX_4x, 2); + FetchY_4x = _mm_or_si128(_mm_mullo_epi16(FetchY_4x, TexturePitch_4x), + _mm_slli_epi32(_mm_mulhi_epi16(FetchY_4x, TexturePitch_4x), 16)); + __m128i Fetch_4x = _mm_add_epi32(FetchX_4x, FetchY_4x); + + int32 Fetch0 = Mi(Fetch_4x, 0); + int32 Fetch1 = Mi(Fetch_4x, 1); + int32 Fetch2 = Mi(Fetch_4x, 2); + int32 Fetch3 = Mi(Fetch_4x, 3); + + uint8 *TexelPtr0 = ((uint8 *)TextureMemory) + Fetch0; + uint8 *TexelPtr1 = ((uint8 *)TextureMemory) + Fetch1; + uint8 *TexelPtr2 = ((uint8 *)TextureMemory) + Fetch2; + uint8 *TexelPtr3 = ((uint8 *)TextureMemory) + Fetch3; + + __m128i SampleA = _mm_setr_epi32(*(uint32 *)(TexelPtr0), + *(uint32 *)(TexelPtr1), + *(uint32 *)(TexelPtr2), + *(uint32 *)(TexelPtr3)); + + __m128i SampleB = _mm_setr_epi32(*(uint32 *)(TexelPtr0 + sizeof(uint32)), + *(uint32 *)(TexelPtr1 + sizeof(uint32)), + *(uint32 *)(TexelPtr2 + sizeof(uint32)), + *(uint32 *)(TexelPtr3 + sizeof(uint32))); + + __m128i SampleC = _mm_setr_epi32(*(uint32 *)(TexelPtr0 + TexturePitch), + *(uint32 *)(TexelPtr1 + TexturePitch), + *(uint32 *)(TexelPtr2 + TexturePitch), + *(uint32 *)(TexelPtr3 + TexturePitch)); + + __m128i SampleD = _mm_setr_epi32(*(uint32 *)(TexelPtr0 + TexturePitch + sizeof(uint32)), + *(uint32 *)(TexelPtr1 + TexturePitch + sizeof(uint32)), + *(uint32 *)(TexelPtr2 + TexturePitch + sizeof(uint32)), + *(uint32 *)(TexelPtr3 + TexturePitch + sizeof(uint32))); + + // NOTE(casey): Unpack bilinear samples + __m128i TexelArb = _mm_and_si128(SampleA, MaskFF00FF); + __m128i TexelAag = _mm_and_si128(_mm_srli_epi32(SampleA, 8), MaskFF00FF); + TexelArb = _mm_mullo_epi16(TexelArb, TexelArb); + __m128 TexelAa = _mm_cvtepi32_ps(_mm_srli_epi32(TexelAag, 16)); + TexelAag = _mm_mullo_epi16(TexelAag, TexelAag); + + __m128i TexelBrb = _mm_and_si128(SampleB, MaskFF00FF); + __m128i TexelBag = _mm_and_si128(_mm_srli_epi32(SampleB, 8), MaskFF00FF); + TexelBrb = _mm_mullo_epi16(TexelBrb, TexelBrb); + __m128 TexelBa = _mm_cvtepi32_ps(_mm_srli_epi32(TexelBag, 16)); + TexelBag = _mm_mullo_epi16(TexelBag, TexelBag); + + __m128i TexelCrb = _mm_and_si128(SampleC, MaskFF00FF); + __m128i TexelCag = _mm_and_si128(_mm_srli_epi32(SampleC, 8), MaskFF00FF); + TexelCrb = _mm_mullo_epi16(TexelCrb, TexelCrb); + __m128 TexelCa = _mm_cvtepi32_ps(_mm_srli_epi32(TexelCag, 16)); + TexelCag = _mm_mullo_epi16(TexelCag, TexelCag); + + __m128i TexelDrb = _mm_and_si128(SampleD, MaskFF00FF); + __m128i TexelDag = _mm_and_si128(_mm_srli_epi32(SampleD, 8), MaskFF00FF); + TexelDrb = _mm_mullo_epi16(TexelDrb, TexelDrb); + __m128 TexelDa = _mm_cvtepi32_ps(_mm_srli_epi32(TexelDag, 16)); + TexelDag = _mm_mullo_epi16(TexelDag, TexelDag); + + // NOTE(casey): Load destination + __m128 Destb = _mm_cvtepi32_ps(_mm_and_si128(OriginalDest, MaskFF)); + __m128 Destg = _mm_cvtepi32_ps(_mm_and_si128(_mm_srli_epi32(OriginalDest, 8), MaskFF)); + __m128 Destr = _mm_cvtepi32_ps(_mm_and_si128(_mm_srli_epi32(OriginalDest, 16), MaskFF)); + __m128 Desta = _mm_cvtepi32_ps(_mm_and_si128(_mm_srli_epi32(OriginalDest, 24), MaskFF)); + + // NOTE(casey): Convert texture from 0-255 sRGB to "linear" 0-1 brightness space + __m128 TexelAr = _mm_cvtepi32_ps(_mm_srli_epi32(TexelArb, 16)); + __m128 TexelAg = _mm_cvtepi32_ps(_mm_and_si128(TexelAag, MaskFFFF)); + __m128 TexelAb = _mm_cvtepi32_ps(_mm_and_si128(TexelArb, MaskFFFF)); + + __m128 TexelBr = _mm_cvtepi32_ps(_mm_srli_epi32(TexelBrb, 16)); + __m128 TexelBg = _mm_cvtepi32_ps(_mm_and_si128(TexelBag, MaskFFFF)); + __m128 TexelBb = _mm_cvtepi32_ps(_mm_and_si128(TexelBrb, MaskFFFF)); + + __m128 TexelCr = _mm_cvtepi32_ps(_mm_srli_epi32(TexelCrb, 16)); + __m128 TexelCg = _mm_cvtepi32_ps(_mm_and_si128(TexelCag, MaskFFFF)); + __m128 TexelCb = _mm_cvtepi32_ps(_mm_and_si128(TexelCrb, MaskFFFF)); + + __m128 TexelDr = _mm_cvtepi32_ps(_mm_srli_epi32(TexelDrb, 16)); + __m128 TexelDg = _mm_cvtepi32_ps(_mm_and_si128(TexelDag, MaskFFFF)); + __m128 TexelDb = _mm_cvtepi32_ps(_mm_and_si128(TexelDrb, MaskFFFF)); + + // NOTE(casey): Bilinear texture blend + __m128 ifX = _mm_sub_ps(One, fX); + __m128 ifY = _mm_sub_ps(One, fY); + + __m128 l0 = _mm_mul_ps(ifY, ifX); + __m128 l1 = _mm_mul_ps(ifY, fX); + __m128 l2 = _mm_mul_ps(fY, ifX); + __m128 l3 = _mm_mul_ps(fY, fX); + + __m128 Texelr = _mm_add_ps(_mm_add_ps(_mm_mul_ps(l0, TexelAr), _mm_mul_ps(l1, TexelBr)), + _mm_add_ps(_mm_mul_ps(l2, TexelCr), _mm_mul_ps(l3, TexelDr))); + __m128 Texelg = _mm_add_ps(_mm_add_ps(_mm_mul_ps(l0, TexelAg), _mm_mul_ps(l1, TexelBg)), + _mm_add_ps(_mm_mul_ps(l2, TexelCg), _mm_mul_ps(l3, TexelDg))); + __m128 Texelb = _mm_add_ps(_mm_add_ps(_mm_mul_ps(l0, TexelAb), _mm_mul_ps(l1, TexelBb)), + _mm_add_ps(_mm_mul_ps(l2, TexelCb), _mm_mul_ps(l3, TexelDb))); + __m128 Texela = _mm_add_ps(_mm_add_ps(_mm_mul_ps(l0, TexelAa), _mm_mul_ps(l1, TexelBa)), + _mm_add_ps(_mm_mul_ps(l2, TexelCa), _mm_mul_ps(l3, TexelDa))); + + // NOTE(casey): Modulate by incoming color + Texelr = _mm_mul_ps(Texelr, Colorr_4x); + Texelg = _mm_mul_ps(Texelg, Colorg_4x); + Texelb = _mm_mul_ps(Texelb, Colorb_4x); + Texela = _mm_mul_ps(Texela, Colora_4x); + + Texelr = _mm_min_ps(_mm_max_ps(Texelr, Zero), MaxColorValue); + Texelg = _mm_min_ps(_mm_max_ps(Texelg, Zero), MaxColorValue); + Texelb = _mm_min_ps(_mm_max_ps(Texelb, Zero), MaxColorValue); + + // NOTE(casey): Go from sRGB to "linear" brightness space + Destr = mmSquare(Destr); + Destg = mmSquare(Destg); + Destb = mmSquare(Destb); + + // NOTE(casey): Destination blend + __m128 InvTexelA = _mm_sub_ps(One, _mm_mul_ps(Inv255_4x, Texela)); + __m128 Blendedr = _mm_add_ps(_mm_mul_ps(InvTexelA, Destr), Texelr); + __m128 Blendedg = _mm_add_ps(_mm_mul_ps(InvTexelA, Destg), Texelg); + __m128 Blendedb = _mm_add_ps(_mm_mul_ps(InvTexelA, Destb), Texelb); + __m128 Blendeda = _mm_add_ps(_mm_mul_ps(InvTexelA, Desta), Texela); + + // NOTE(casey): Go from "linear" 0-1 brightness space to sRGB 0-255 +#if 1 + Blendedr = _mm_mul_ps(Blendedr, _mm_rsqrt_ps(Blendedr)); + Blendedg = _mm_mul_ps(Blendedg, _mm_rsqrt_ps(Blendedg)); + Blendedb = _mm_mul_ps(Blendedb, _mm_rsqrt_ps(Blendedb)); +#else + Blendedr = _mm_sqrt_ps(Blendedr); + Blendedg = _mm_sqrt_ps(Blendedg); + Blendedb = _mm_sqrt_ps(Blendedb); +#endif + Blendeda = Blendeda; + + __m128i Intr = _mm_cvtps_epi32(Blendedr); + __m128i Intg = _mm_cvtps_epi32(Blendedg); + __m128i Intb = _mm_cvtps_epi32(Blendedb); + __m128i Inta = _mm_cvtps_epi32(Blendeda); + + __m128i Sr = _mm_slli_epi32(Intr, 16); + __m128i Sg = _mm_slli_epi32(Intg, 8); + __m128i Sb = Intb; + __m128i Sa = _mm_slli_epi32(Inta, 24); + + __m128i Out = _mm_or_si128(_mm_or_si128(Sr, Sg), _mm_or_si128(Sb, Sa)); + + __m128i MaskedOut = _mm_or_si128(_mm_and_si128(WriteMask, Out), + _mm_andnot_si128(WriteMask, OriginalDest)); + _mm_store_si128((__m128i *)Pixel, MaskedOut); + } + + PixelPx = _mm_add_ps(PixelPx, Four_4x); + Pixel += 4; + + if((XI + 8) < MaxX) + { + ClipMask = _mm_set1_epi8(-1); + } + else + { + ClipMask = EndClipMask; + } + + IACA_VC64_END; + } + + Row += RowAdvance; + } + } +} diff --git a/test_data/lots_of_files/handmade_platform.h b/test_data/lots_of_files/handmade_platform.h new file mode 100644 index 0000000..cd86e27 --- /dev/null +++ b/test_data/lots_of_files/handmade_platform.h @@ -0,0 +1,608 @@ +#if !defined(HANDMADE_PLATFORM_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +// TODO(casey): Have the meta-parser ignore its own #define +#define introspect(params) +#define counted_pointer(params) + +#include "handmade_config.h" + +/* + NOTE(casey): + + HANDMADE_INTERNAL: + 0 - Build for public release + 1 - Build for developer only + + HANDMADE_SLOW: + 0 - Not slow code allowed! + 1 - Slow code welcome. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +// +// NOTE(casey): Compilers +// + +#if !defined(COMPILER_MSVC) +#define COMPILER_MSVC 0 +#endif + +#if !defined(COMPILER_LLVM) +#define COMPILER_LLVM 0 +#endif + +#if !COMPILER_MSVC && !COMPILER_LLVM +#if _MSC_VER +#undef COMPILER_MSVC +#define COMPILER_MSVC 1 +#else +// TODO(casey): Moar compilerz!!! +#undef COMPILER_LLVM +#define COMPILER_LLVM 1 +#endif +#endif + +#if COMPILER_MSVC +#include <intrin.h> +#elif COMPILER_LLVM +#include <x86intrin.h> +#else +#error SEE/NEON optimizations are not available for this compiler yet!!!! +#endif + +// +// NOTE(casey): Types +// +#include <stdint.h> +#include <stddef.h> +#include <limits.h> +#include <float.h> + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef int32 bool32; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +typedef intptr_t intptr; +typedef uintptr_t uintptr; + +typedef size_t memory_index; + +typedef float real32; +typedef double real64; + +typedef int8 s8; +typedef int8 s08; +typedef int16 s16; +typedef int32 s32; +typedef int64 s64; +typedef bool32 b32; + +typedef uint8 u8; +typedef uint8 u08; +typedef uint16 u16; +typedef uint32 u32; +typedef uint64 u64; + +typedef real32 r32; +typedef real64 r64; + +#pragma pack(push, 1) +struct bitmap_id +{ + u32 Value; +}; + +struct sound_id +{ + u32 Value; +}; + +struct font_id +{ + u32 Value; +}; +#pragma pack(pop) + +union v2 +{ + struct + { + real32 x, y; + }; + struct + { + real32 u, v; + }; + real32 E[2]; +}; + +union v3 +{ + struct + { + real32 x, y, z; + }; + struct + { + real32 u, v, w; + }; + struct + { + real32 r, g, b; + }; + struct + { + v2 xy; + real32 Ignored0_; + }; + struct + { + real32 Ignored1_; + v2 yz; + }; + struct + { + v2 uv; + real32 Ignored2_; + }; + struct + { + real32 Ignored3_; + v2 vw; + }; + real32 E[3]; +}; + +union v4 +{ + struct + { + union + { + v3 xyz; + struct + { + real32 x, y, z; + }; + }; + + real32 w; + }; + struct + { + union + { + v3 rgb; + struct + { + real32 r, g, b; + }; + }; + + real32 a; + }; + struct + { + v2 xy; + real32 Ignored0_; + real32 Ignored1_; + }; + struct + { + real32 Ignored2_; + v2 yz; + real32 Ignored3_; + }; + struct + { + real32 Ignored4_; + real32 Ignored5_; + v2 zw; + }; + real32 E[4]; +}; + +introspect(category:"math") struct rectangle2 +{ + v2 Min; + v2 Max; +}; + +introspect(category:"math") struct rectangle3 +{ + v3 Min; + v3 Max; +}; + +#define Real32Maximum FLT_MAX + +#if !defined(internal) +#define internal static +#endif +#define local_persist static +#define global_variable static + +#define Pi32 3.14159265359f +#define Tau32 6.28318530717958647692f + +#if HANDMADE_SLOW +// TODO(casey): Complete assertion macro - don't worry everyone! +#define Assert(Expression) if(!(Expression)) {*(int *)0 = 0;} +#else +#define Assert(Expression) +#endif + +#define InvalidCodePath Assert(!"InvalidCodePath") +#define InvalidDefaultCase default: {InvalidCodePath;} break + +#define Kilobytes(Value) ((Value)*1024LL) +#define Megabytes(Value) (Kilobytes(Value)*1024LL) +#define Gigabytes(Value) (Megabytes(Value)*1024LL) +#define Terabytes(Value) (Gigabytes(Value)*1024LL) + +#define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0])) +// TODO(casey): swap, min, max ... macros??? + +#define AlignPow2(Value, Alignment) ((Value + ((Alignment) - 1)) & ~((Alignment) - 1)) +#define Align4(Value) ((Value + 3) & ~3) +#define Align8(Value) ((Value + 7) & ~7) +#define Align16(Value) ((Value + 15) & ~15) + +inline uint32 +SafeTruncateUInt64(uint64 Value) +{ + // TODO(casey): Defines for maximum values + Assert(Value <= 0xFFFFFFFF); + uint32 Result = (uint32)Value; + return(Result); +} + +/* + NOTE(casey): Services that the platform layer provides to the game +*/ +#if HANDMADE_INTERNAL +/* IMPORTANT(casey): + + These are NOT for doing anything in the shipping game - they are + blocking and the write doesn't protect against lost data! +*/ +typedef struct debug_read_file_result +{ + uint32 ContentsSize; + void *Contents; +} debug_read_file_result; + +typedef struct debug_executing_process +{ + u64 OSHandle; +} debug_executing_process; + +typedef struct debug_process_state +{ + b32 StartedSuccessfully; + b32 IsRunning; + s32 ReturnCode; +} debug_process_state; + +#define DEBUG_PLATFORM_FREE_FILE_MEMORY(name) void name(void *Memory) +typedef DEBUG_PLATFORM_FREE_FILE_MEMORY(debug_platform_free_file_memory); + +#define DEBUG_PLATFORM_READ_ENTIRE_FILE(name) debug_read_file_result name(char *Filename) +typedef DEBUG_PLATFORM_READ_ENTIRE_FILE(debug_platform_read_entire_file); + +#define DEBUG_PLATFORM_WRITE_ENTIRE_FILE(name) bool32 name(char *Filename, uint32 MemorySize, void *Memory) +typedef DEBUG_PLATFORM_WRITE_ENTIRE_FILE(debug_platform_write_entire_file); + +#define DEBUG_PLATFORM_EXECUTE_SYSTEM_COMMAND(name) debug_executing_process name(char *Path, char *Command, char *CommandLine) +typedef DEBUG_PLATFORM_EXECUTE_SYSTEM_COMMAND(debug_platform_execute_system_command); + +// TODO(casey): Do we want a formal release mechanism here? +#define DEBUG_PLATFORM_GET_PROCESS_STATE(name) debug_process_state name(debug_executing_process Process) +typedef DEBUG_PLATFORM_GET_PROCESS_STATE(debug_platform_get_process_state); + +// TODO(casey): Actually start using this??? +extern struct game_memory *DebugGlobalMemory; + +#endif + +/* + NOTE(casey): Services that the game provides to the platform layer. + (this may expand in the future - sound on separate thread, etc.) +*/ + +// FOUR THINGS - timing, controller/keyboard input, bitmap buffer to use, sound buffer to use + +// TODO(casey): In the future, rendering _specifically_ will become a three-tiered abstraction!!! +#define BITMAP_BYTES_PER_PIXEL 4 +typedef struct game_offscreen_buffer +{ + // NOTE(casey): Pixels are always 32-bits wide, Memory Order BB GG RR XX + void *Memory; + int Width; + int Height; + int Pitch; +} game_offscreen_buffer; + +typedef struct game_sound_output_buffer +{ + int SamplesPerSecond; + int SampleCount; + + // IMPORTANT(casey): Samples must be padded to a multiple of 4 samples! + int16 *Samples; +} game_sound_output_buffer; + +typedef struct game_button_state +{ + int HalfTransitionCount; + bool32 EndedDown; +} game_button_state; + +typedef struct game_controller_input +{ + bool32 IsConnected; + bool32 IsAnalog; + real32 StickAverageX; + real32 StickAverageY; + + union + { + game_button_state Buttons[12]; + struct + { + game_button_state MoveUp; + game_button_state MoveDown; + game_button_state MoveLeft; + game_button_state MoveRight; + + game_button_state ActionUp; + game_button_state ActionDown; + game_button_state ActionLeft; + game_button_state ActionRight; + + game_button_state LeftShoulder; + game_button_state RightShoulder; + + game_button_state Back; + game_button_state Start; + + // NOTE(casey): All buttons must be added above this line + + game_button_state Terminator; + }; + }; +} game_controller_input; + +enum game_input_mouse_button +{ + PlatformMouseButton_Left, + PlatformMouseButton_Middle, + PlatformMouseButton_Right, + PlatformMouseButton_Extended0, + PlatformMouseButton_Extended1, + + PlatformMouseButton_Count, +}; +typedef struct game_input +{ + r32 dtForFrame; + + game_controller_input Controllers[5]; + + // NOTE(casey): For debugging only + game_button_state MouseButtons[PlatformMouseButton_Count]; + r32 MouseX, MouseY, MouseZ; + b32 ShiftDown, AltDown, ControlDown; +} game_input; + +inline game_controller_input *GetController(game_input *Input, int unsigned ControllerIndex) +{ + Assert(ControllerIndex < ArrayCount(Input->Controllers)); + + game_controller_input *Result = &Input->Controllers[ControllerIndex]; + return(Result); +} + +inline b32 WasPressed(game_button_state State) +{ + b32 Result = ((State.HalfTransitionCount > 1) || + ((State.HalfTransitionCount == 1) && (State.EndedDown))); + + return(Result); +} + +typedef struct platform_file_handle +{ + b32 NoErrors; + void *Platform; +} platform_file_handle; + +typedef struct platform_file_group +{ + u32 FileCount; + void *Platform; +} platform_file_group; + +typedef enum platform_file_type +{ + PlatformFileType_AssetFile, + PlatformFileType_SavedGameFile, + + PlatformFileType_Count, +} platform_file_type; + +#define PLATFORM_GET_ALL_FILE_OF_TYPE_BEGIN(name) platform_file_group name(platform_file_type Type) +typedef PLATFORM_GET_ALL_FILE_OF_TYPE_BEGIN(platform_get_all_files_of_type_begin); + +#define PLATFORM_GET_ALL_FILE_OF_TYPE_END(name) void name(platform_file_group *FileGroup) +typedef PLATFORM_GET_ALL_FILE_OF_TYPE_END(platform_get_all_files_of_type_end); + +#define PLATFORM_OPEN_FILE(name) platform_file_handle name(platform_file_group *FileGroup) +typedef PLATFORM_OPEN_FILE(platform_open_next_file); + +#define PLATFORM_READ_DATA_FROM_FILE(name) void name(platform_file_handle *Source, u64 Offset, u64 Size, void *Dest) +typedef PLATFORM_READ_DATA_FROM_FILE(platform_read_data_from_file); + +#define PLATFORM_FILE_ERROR(name) void name(platform_file_handle *Handle, char *Message) +typedef PLATFORM_FILE_ERROR(platform_file_error); + +#define PlatformNoFileErrors(Handle) ((Handle)->NoErrors) + +struct platform_work_queue; +#define PLATFORM_WORK_QUEUE_CALLBACK(name) void name(platform_work_queue *Queue, void *Data) +typedef PLATFORM_WORK_QUEUE_CALLBACK(platform_work_queue_callback); + +#define PLATFORM_ALLOCATE_MEMORY(name) void *name(memory_index Size) +typedef PLATFORM_ALLOCATE_MEMORY(platform_allocate_memory); + +#define PLATFORM_DEALLOCATE_MEMORY(name) void name(void *Memory) +typedef PLATFORM_DEALLOCATE_MEMORY(platform_deallocate_memory); + +typedef void platform_add_entry(platform_work_queue *Queue, platform_work_queue_callback *Callback, void *Data); +typedef void platform_complete_all_work(platform_work_queue *Queue); + +typedef struct platform_api +{ + platform_add_entry *AddEntry; + platform_complete_all_work *CompleteAllWork; + + platform_get_all_files_of_type_begin *GetAllFilesOfTypeBegin; + platform_get_all_files_of_type_end *GetAllFilesOfTypeEnd; + platform_open_next_file *OpenNextFile; + platform_read_data_from_file *ReadDataFromFile; + platform_file_error *FileError; + + platform_allocate_memory *AllocateMemory; + platform_deallocate_memory *DeallocateMemory; + +#if HANDMADE_INTERNAL + debug_platform_free_file_memory *DEBUGFreeFileMemory; + debug_platform_read_entire_file *DEBUGReadEntireFile; + debug_platform_write_entire_file *DEBUGWriteEntireFile; + debug_platform_execute_system_command *DEBUGExecuteSystemCommand; + debug_platform_get_process_state *DEBUGGetProcessState; +#endif debug_table * + +} platform_api; + +typedef struct game_memory +{ + uint64 PermanentStorageSize; + void *PermanentStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup + + uint64 TransientStorageSize; + void *TransientStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup + + uint64 DebugStorageSize; + void *DebugStorage; // NOTE(casey): REQUIRED to be cleared to zero at startup + + platform_work_queue *HighPriorityQueue; + platform_work_queue *LowPriorityQueue; + + b32 ExecutableReloaded; + platform_api PlatformAPI; +} game_memory; + +#define GAME_UPDATE_AND_RENDER(name) void name(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer) +typedef GAME_UPDATE_AND_RENDER(game_update_and_render); + +// NOTE(casey): At the moment, this has to be a very fast function, it cannot be +// more than a millisecond or so. +// TODO(casey): Reduce the pressure on this function's performance by measuring it +// or asking about it, etc. +#define GAME_GET_SOUND_SAMPLES(name) void name(game_memory *Memory, game_sound_output_buffer *SoundBuffer) +typedef GAME_GET_SOUND_SAMPLES(game_get_sound_samples); + +#if COMPILER_MSVC +#define CompletePreviousReadsBeforeFutureReads _ReadBarrier() +#define CompletePreviousWritesBeforeFutureWrites _WriteBarrier() +inline uint32 AtomicCompareExchangeUInt32(uint32 volatile *Value, uint32 New, uint32 Expected) +{ + uint32 Result = _InterlockedCompareExchange((long volatile *)Value, New, Expected); + + return(Result); +} +inline u64 AtomicExchangeU64(u64 volatile *Value, u64 New) +{ + u64 Result = _InterlockedExchange64((__int64 volatile *)Value, New); + + return(Result); +} +inline u64 AtomicAddU64(u64 volatile *Value, u64 Addend) +{ + // NOTE(casey): Returns the original value _prior_ to adding + u64 Result = _InterlockedExchangeAdd64((__int64 volatile *)Value, Addend); + + return(Result); +} +inline u32 GetThreadID(void) +{ + u8 *ThreadLocalStorage = (u8 *)__readgsqword(0x30); + u32 ThreadID = *(u32 *)(ThreadLocalStorage + 0x48); + + return(ThreadID); +} + +#elif COMPILER_LLVM +// TODO(casey): Does LLVM have real read-specific barriers yet? +#define CompletePreviousReadsBeforeFutureReads asm volatile("" ::: "memory") +#define CompletePreviousWritesBeforeFutureWrites asm volatile("" ::: "memory") +inline uint32 AtomicCompareExchangeUInt32(uint32 volatile *Value, uint32 New, uint32 Expected) +{ + uint32 Result = __sync_val_compare_and_swap(Value, Expected, New); + + return(Result); +} +inline u64 AtomicExchangeU64(u64 volatile *Value, u64 New) +{ + u64 Result = __sync_lock_test_and_set(Value, New); + + return(Result); +} +inline u64 AtomicAddU64(u64 volatile *Value, u64 Addend) +{ + // NOTE(casey): Returns the original value _prior_ to adding + u64 Result = __sync_fetch_and_add(Value, Addend); + + return(Result); +} +inline u32 GetThreadID(void) +{ + u32 ThreadID; +#if defined(__APPLE__) && defined(__x86_64__) + asm("mov %%gs:0x00,%0" : "=r"(ThreadID)); +#elif defined(__i386__) + asm("mov %%gs:0x08,%0" : "=r"(ThreadID)); +#elif defined(__x86_64__) + asm("mov %%fs:0x10,%0" : "=r"(ThreadID)); +#else +#error Unsupported architecture +#endif + + return(ThreadID); +} +#else +// TODO(casey): Other compilers/platforms?? +#endif + +#include "handmade_debug_interface.h" + +#define HANDMADE_PLATFORM_H +#endif diff --git a/test_data/lots_of_files/handmade_random.h b/test_data/lots_of_files/handmade_random.h new file mode 100644 index 0000000..85e5ecd --- /dev/null +++ b/test_data/lots_of_files/handmade_random.h @@ -0,0 +1,591 @@ +#if !defined(HANDMADE_RANDOM_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#define MaxRandomNumber 0x05f5c21f +#define MinRandomNumber 0x000025a0 + +global_variable uint32 RandomNumberTable[] = +{ + 0x4f0143b, 0x3402005, 0x26f2b01, 0x22796b6, 0x57343bb, 0x2d9954e, 0x06f9425, 0x1789180, + 0x57d8fab, 0x5365d9c, 0x0e9ec55, 0x2a623e0, 0x366e05d, 0x3759f45, 0x1b4d151, 0x35a5411, + 0x59e734b, 0x211c9e4, 0x1b0df4d, 0x50d423d, 0x3b18f5b, 0x5066bed, 0x0aa03be, 0x2b66c73, + 0x21a0ea3, 0x50d821c, 0x4628aed, 0x2456259, 0x2a178ad, 0x4b3bf40, 0x0185588, 0x5ecf044, + 0x48a6c64, 0x5585c49, 0x0bf0dae, 0x03aeb87, 0x0677916, 0x0aca65a, 0x42534d2, 0x226d712, + 0x03713ed, 0x4a5afa1, 0x4f2f545, 0x30dff53, 0x0d98c9d, 0x4b43caa, 0x54cfe7a, 0x5c5a54e, + 0x4816e78, 0x30f9b01, 0x110677f, 0x44c0f3e, 0x03ee6b3, 0x1ce05d2, 0x1beca77, 0x3011e81, + 0x1c0e238, 0x27039e5, 0x15242d4, 0x0386766, 0x10a7455, 0x5f2ea75, 0x4291cb8, 0x2b33e48, + 0x12405ca, 0x40bd964, 0x3792675, 0x4c38386, 0x204dce0, 0x41023d7, 0x0f70768, 0x43a95f7, + 0x16280f9, 0x42566bd, 0x160d0b5, 0x2b6dcfd, 0x390b16d, 0x41768ff, 0x26ed0f2, 0x2d906f1, + 0x1ade480, 0x44f6e26, 0x0c71504, 0x2a605c8, 0x4cf4128, 0x3983aa1, 0x2d20133, 0x4c677ca, + 0x014eed0, 0x3c94d24, 0x2bec8fc, 0x1006c95, 0x0a57f1a, 0x16d2803, 0x1fe5fd5, 0x2560dfe, + 0x03a77e0, 0x1ae35cc, 0x2e15cee, 0x45c1621, 0x4a8e6a2, 0x53b3af4, 0x16b64db, 0x2602c93, + 0x31fa92e, 0x3137d85, 0x4351ce9, 0x03ca2db, 0x4b7a27e, 0x28e3846, 0x289e3b4, 0x3e9a0fe, + 0x041fc34, 0x2400db4, 0x5740222, 0x072c880, 0x5330621, 0x305fac0, 0x30bba4c, 0x1d1bcc0, + 0x46e3749, 0x27f3a6b, 0x51dfd7e, 0x1b99797, 0x078f825, 0x0899824, 0x3d87dfb, 0x1506d51, + 0x41b2e1b, 0x5a7e30b, 0x42b665d, 0x40c1412, 0x2dfcfd6, 0x0f64f3f, 0x2effce1, 0x181eda6, + 0x0f7d529, 0x481718b, 0x4e872c9, 0x22ce1cc, 0x55269e9, 0x0e69c54, 0x0992a2f, 0x0e1cd99, + 0x3bcf0ed, 0x0dbc5f2, 0x2e58639, 0x2a8846f, 0x2737a5d, 0x3d5fc85, 0x1c1fc2d, 0x29ce3d6, + 0x4f8ee3f, 0x426dc27, 0x223db0e, 0x3e0d8d5, 0x5a28a7a, 0x2587f0a, 0x36a54cf, 0x23887bb, + 0x22cc929, 0x595e059, 0x33fed84, 0x4f7042e, 0x311c187, 0x430f215, 0x3ab4245, 0x2116700, + 0x30186b9, 0x5312195, 0x300262d, 0x35e2b65, 0x5280730, 0x1365bcd, 0x466dc33, 0x22bf5cc, + 0x34a3fb1, 0x172fb52, 0x0a9cde5, 0x142f318, 0x2731995, 0x58eb3bd, 0x1693e13, 0x25a9cf1, + 0x05c235e, 0x08a601d, 0x2432351, 0x2061136, 0x422f956, 0x33fba4a, 0x5c76788, 0x5b0387f, + 0x0e3a118, 0x4c4f4f0, 0x49726f7, 0x4600fb2, 0x284a1fc, 0x47d625f, 0x4a6127d, 0x18b4cc6, + 0x2d69328, 0x04a79b2, 0x59705c2, 0x550efbe, 0x3d43be5, 0x21d316b, 0x4e872d3, 0x34b4339, + 0x5ab279c, 0x31ab38f, 0x28dfd4a, 0x46c8148, 0x3222416, 0x299c85d, 0x0ff8604, 0x5b86120, + 0x3fe7e78, 0x0c421d7, 0x4c8dab6, 0x25e37ee, 0x42e2c78, 0x1ba3101, 0x1d26b60, 0x557e4bf, + 0x5c48926, 0x38b258a, 0x44728e4, 0x482e0dd, 0x4434fa2, 0x2fce17b, 0x350211d, 0x023370f, + 0x0f6657f, 0x23d4f4c, 0x3b18d0c, 0x3b1e0b6, 0x1dcdd39, 0x034761f, 0x4df128d, 0x2945257, + 0x460e7e4, 0x1ce4d80, 0x0a8e6a2, 0x5eb784b, 0x0853730, 0x2ea3a05, 0x09b717b, 0x5ce8362, + 0x5360703, 0x06c1609, 0x51604cf, 0x17e01d2, 0x40bf132, 0x32a9935, 0x15aa9d8, 0x4912351, + 0x51f7652, 0x027aa0a, 0x24addd6, 0x3d2c538, 0x3a7d54a, 0x40adb17, 0x0fdb245, 0x557c53d, + 0x4eb9775, 0x2c95a30, 0x4b3d29d, 0x468d608, 0x5bf85f6, 0x33de0cb, 0x394ca9e, 0x4d7166f, + 0x3cb570d, 0x3b7b3c9, 0x5292bfb, 0x09d2662, 0x0510f39, 0x3267933, 0x53a5fe9, 0x0873a43, + 0x59e9de2, 0x0b5c941, 0x24f78c4, 0x54fa1c8, 0x0da6aed, 0x4df5f5d, 0x1ea3ccc, 0x3ac14aa, + 0x40d25fd, 0x331a3ae, 0x5257579, 0x0030e95, 0x14995ad, 0x4879608, 0x3c8bbf4, 0x2715173, + 0x03654fd, 0x3767b2f, 0x3b90be2, 0x44fd28e, 0x023618d, 0x2bdcfdb, 0x0231780, 0x0bf178c, + 0x3166bab, 0x57327db, 0x203c21e, 0x40fc81f, 0x35ff817, 0x39f114f, 0x5ae772c, 0x3e6591d, + 0x5a192a5, 0x05b9ee7, 0x3349792, 0x34b012d, 0x2c0ddbf, 0x27aba82, 0x30ceeec, 0x2ec25f9, + 0x1aae3f5, 0x2773ffb, 0x196102d, 0x4bf0762, 0x42b929f, 0x354d296, 0x220ee6b, 0x3fc80c0, + 0x120d332, 0x01b7679, 0x2690014, 0x0563acf, 0x502ff24, 0x14cbc08, 0x2f6db6d, 0x4b741ac, + 0x39fa332, 0x20bcf75, 0x4b9b111, 0x0c8595a, 0x3d95fb4, 0x33bb9a1, 0x08932ee, 0x53bf013, + 0x2f2df9d, 0x1e70ce1, 0x4b13619, 0x4078eb6, 0x3ffc3ce, 0x5e56f4c, 0x3d456ca, 0x3130295, + 0x3c69442, 0x152dfc8, 0x0808c9b, 0x3bc2876, 0x141ac03, 0x1a962ea, 0x0baaa86, 0x48715cf, + 0x2910d74, 0x5023627, 0x0460e1e, 0x1ad8724, 0x1e402fc, 0x1892985, 0x0c039f6, 0x2d83a4a, + 0x1947c37, 0x3dd189a, 0x407d540, 0x02e9ef6, 0x5dc7b05, 0x0623d84, 0x47df5b4, 0x37b191f, + 0x39bc842, 0x2a89dcc, 0x444809e, 0x009b253, 0x106b1b6, 0x56bf97c, 0x35cd1a7, 0x5713178, + 0x52ce61b, 0x13ca28c, 0x186ab5f, 0x53a6c2e, 0x42e5924, 0x4214750, 0x407ab66, 0x199dc40, + 0x10bfae2, 0x54592e8, 0x4c9bfad, 0x589ac54, 0x2315075, 0x411068c, 0x1431637, 0x05de9e5, + 0x3365f7f, 0x55408fa, 0x4306140, 0x0a3e8e6, 0x27eff3b, 0x441f9ad, 0x1e746ba, 0x478f925, + 0x24848e3, 0x5709a30, 0x5a610f9, 0x31ae341, 0x2c460a3, 0x49fd92c, 0x505237d, 0x4a8cf5e, + 0x4c00f7a, 0x3dec4eb, 0x3c290ec, 0x3fe1a17, 0x0f28ff9, 0x0f6cda4, 0x14126bb, 0x519748e, + 0x2a051bc, 0x1a9e8c4, 0x169f87c, 0x0b0d11b, 0x212d2bd, 0x11819c3, 0x3b6dbcb, 0x4f0e0af, + 0x2ac62ef, 0x5e75958, 0x0ef9f89, 0x4bce875, 0x5008515, 0x301e851, 0x4e8dda8, 0x52ade69, + 0x466ec50, 0x117a672, 0x257437a, 0x43bcb00, 0x1f8f710, 0x4a54cfc, 0x08aeb47, 0x3433312, + 0x334d0ad, 0x349e3bb, 0x5847e7c, 0x2ae4a79, 0x4a69da5, 0x587e061, 0x31c06ee, 0x3303bc9, + 0x5349932, 0x2100ca8, 0x04bf772, 0x10e268d, 0x2105aee, 0x5bf4955, 0x02b5161, 0x49d4df2, + 0x167617c, 0x45c4246, 0x3bc5717, 0x5f38db2, 0x21e208d, 0x1232c86, 0x05df3f1, 0x3674470, + 0x068c79f, 0x32a1868, 0x229f61f, 0x179843b, 0x3865c40, 0x430cb20, 0x31fbff7, 0x26d64fa, + 0x27ee976, 0x03f0c7c, 0x265d3f8, 0x50d6255, 0x1a125ac, 0x1bd06aa, 0x53c4196, 0x0439728, + 0x4c46f9e, 0x0200d95, 0x1f76335, 0x1ad8da2, 0x523da1c, 0x12d7ace, 0x10bc9fe, 0x1689423, + 0x49f47bb, 0x549700b, 0x02f54e6, 0x3787067, 0x463b32b, 0x18db1c0, 0x44c4097, 0x5616172, + 0x23359d3, 0x0945145, 0x3a2d2b8, 0x50fddaf, 0x2f8e2e8, 0x5c6f839, 0x25d0091, 0x4ad3e06, + 0x53203f9, 0x2ef93a8, 0x4932a1a, 0x19f4bbd, 0x1ab9a75, 0x44c480a, 0x4e806c1, 0x1d57648, + 0x353faa0, 0x0d7ea94, 0x0808d11, 0x4aded6e, 0x4525c09, 0x36d4ee6, 0x463203d, 0x53049ac, + 0x1307e79, 0x1f4f1e9, 0x291bb6a, 0x055f1f9, 0x159b03c, 0x5945b54, 0x5bfbf5e, 0x251de9e, + 0x560b32d, 0x1ae529b, 0x49a4dd5, 0x4dd2a5d, 0x4ac261b, 0x4d48086, 0x5dd4289, 0x177a133, + 0x457495c, 0x2adbd76, 0x3776da4, 0x5275e1a, 0x1ad0e27, 0x3403f32, 0x082f3ea, 0x51f8f64, + 0x44dbfad, 0x4da1d15, 0x1bd8194, 0x0b98547, 0x0730172, 0x3d7d457, 0x09d0dad, 0x38fae32, + 0x17601bc, 0x21c440c, 0x50ba349, 0x44842ed, 0x0cb28e6, 0x4bceec7, 0x28f183f, 0x5952a31, + 0x2c98a95, 0x2359477, 0x401429c, 0x1052dd0, 0x04b42e7, 0x26654a2, 0x3f6418e, 0x25ef7be, + 0x0290f29, 0x175e101, 0x3cb28c0, 0x206dd81, 0x3909ab0, 0x441b637, 0x46e2bf5, 0x5c3b27b, + 0x2150cb5, 0x00418a4, 0x47a91b6, 0x02ce251, 0x23530c9, 0x072f631, 0x1394da2, 0x156e035, + 0x2e43c6f, 0x53129ef, 0x235c077, 0x51fb2d4, 0x20261fc, 0x1d8d8ce, 0x427b68c, 0x34805ef, + 0x24032e2, 0x2cdb326, 0x450f713, 0x5a9df2b, 0x4e0a5cb, 0x4d1bbbc, 0x0196e47, 0x3efc5b5, + 0x2a3fc7d, 0x1a1a983, 0x3b18b91, 0x290aedc, 0x3533ee7, 0x3bff002, 0x21b4683, 0x364e3b9, + 0x5ab4d51, 0x3d624b0, 0x332231e, 0x25af8dd, 0x24cb453, 0x18a5460, 0x12238b7, 0x37d5df2, + 0x24b7d60, 0x44db9ee, 0x4a71e48, 0x0472775, 0x0338867, 0x2b3c1c9, 0x2c5a402, 0x518ba40, + 0x3f92fc3, 0x04201c2, 0x4b1d69d, 0x3d56e30, 0x3552214, 0x13429e6, 0x3407619, 0x01beea0, + 0x5a8df4f, 0x5805a61, 0x4df5178, 0x0266082, 0x40df736, 0x0757347, 0x47d1d5f, 0x27695d3, + 0x00a81d5, 0x3759bc2, 0x12f3c66, 0x594fd93, 0x5c29d37, 0x363acd6, 0x1b4558c, 0x4abbda0, + 0x1127991, 0x35ab84c, 0x537fa78, 0x1f71ab8, 0x0e4adb2, 0x2293c6e, 0x361af30, 0x0483560, + 0x31d07c4, 0x0dd01bd, 0x3468e4f, 0x2012173, 0x4175b07, 0x38e8998, 0x3f39436, 0x451ac7d, + 0x10331b2, 0x4bde6fd, 0x03fe204, 0x47463cd, 0x5381bdd, 0x054b077, 0x2a17deb, 0x3d1b711, + 0x5ab1679, 0x490d81f, 0x37b3c0a, 0x12272c3, 0x3ba19f4, 0x2df475c, 0x2486527, 0x40e9c52, + 0x0fbd0e5, 0x272211c, 0x47fdfc3, 0x4cf2c1d, 0x56284eb, 0x44ee47d, 0x2409bfd, 0x02c2230, + 0x1be6f72, 0x3fc3c3d, 0x28f32d8, 0x3ed1670, 0x3f0cf4e, 0x4a68dd4, 0x2463da5, 0x44a3cd1, + 0x068f53d, 0x12315f5, 0x4410f0d, 0x2c846e0, 0x0e715e8, 0x249bab5, 0x21654a7, 0x511bb18, + 0x0985f31, 0x0ebc851, 0x0a33373, 0x5800fab, 0x549af43, 0x3055655, 0x30c152b, 0x0350664, + 0x5514e58, 0x2a119c1, 0x01fa149, 0x508b86e, 0x0e79d10, 0x2ac25a0, 0x0f8e26b, 0x18747c0, + 0x013e8be, 0x40b811f, 0x4f4ad82, 0x4f5a3c3, 0x3dd208a, 0x4904312, 0x5358f23, 0x4f123df, + 0x529dbf7, 0x3645edd, 0x0f2d9a7, 0x42e4890, 0x2cfbc18, 0x0b8d389, 0x061914a, 0x3e92dd2, + 0x2f54d4f, 0x4532db3, 0x4db46e0, 0x2baed0e, 0x555f307, 0x2278251, 0x1d82f56, 0x3f4b42a, + 0x13b2af5, 0x312cc8e, 0x36854bf, 0x0f9910e, 0x2d5b4e3, 0x21d3b41, 0x43d99b0, 0x0a94fa7, + 0x322ae0b, 0x2df0db5, 0x1f80b21, 0x4d493a2, 0x45b20f4, 0x059b9e5, 0x22d1e03, 0x4519213, + 0x34b6dc2, 0x0461c6b, 0x52bdd26, 0x4caec56, 0x06cc872, 0x4e6220f, 0x186e013, 0x352941d, + 0x1dcc7ed, 0x087b4c0, 0x5293c91, 0x2b3cc2f, 0x56ac502, 0x074cae5, 0x46ae019, 0x4463c0a, + 0x206727a, 0x24e5b66, 0x24837ae, 0x11be471, 0x4a82337, 0x09a27c7, 0x2ac6bdb, 0x432379e, + 0x4cf0a3c, 0x2c0b675, 0x457a53c, 0x380fa67, 0x057cb76, 0x4755c19, 0x2514f73, 0x229ed49, + 0x20a83ed, 0x4bed1fd, 0x0f25a96, 0x400f9fc, 0x572a6ac, 0x5d77a13, 0x118c6f1, 0x3d3a972, + 0x44517c0, 0x11a7445, 0x499f406, 0x3f1a984, 0x4c3eee7, 0x2437116, 0x1b6e3f5, 0x33be8b4, + 0x04a22e8, 0x24f774b, 0x58dc8e4, 0x3b60d45, 0x47fa696, 0x21a46c3, 0x4d933d3, 0x16e5692, + 0x00b754d, 0x132d07c, 0x1e107a1, 0x14e3e1a, 0x5be670f, 0x5f5c21f, 0x03d8a3a, 0x2df919d, + 0x27479d5, 0x12c3314, 0x4a41fcc, 0x1d1469f, 0x5f232ce, 0x2a7eac1, 0x39c8d8e, 0x372070c, + 0x14357eb, 0x4971f2f, 0x493279c, 0x237c448, 0x39cf0b5, 0x5564a0c, 0x519a53d, 0x3acf59e, + 0x4c4daa4, 0x43ed201, 0x0749e80, 0x0d0da35, 0x55ccdd2, 0x1100efa, 0x0f83ba5, 0x10354ae, + 0x4832e9f, 0x0fb02cf, 0x1f29418, 0x206922a, 0x3a4e142, 0x1d76057, 0x12a6457, 0x3a8334a, + 0x478b296, 0x2e3e67a, 0x43f658a, 0x0a78a4f, 0x28154b7, 0x5721f33, 0x0af825d, 0x43d2f9f, + 0x0769af7, 0x20b6a74, 0x1390d47, 0x10c180e, 0x0afa8a8, 0x45573ff, 0x0fa6c2c, 0x2623765, + 0x056bc12, 0x49475f6, 0x490feb2, 0x2383ca4, 0x00f0b2c, 0x4d87c98, 0x36d3c79, 0x2d864c8, + 0x3197be7, 0x1aae8b4, 0x4578a0f, 0x1087340, 0x4c606c9, 0x07d8127, 0x387a7d6, 0x297e540, + 0x51cf247, 0x0a05cda, 0x017c49a, 0x338ae3f, 0x2ef18e8, 0x35b528f, 0x44eca1d, 0x59c4bf8, + 0x48d476a, 0x3b1b4f8, 0x4a2116d, 0x2d902ed, 0x1a8816a, 0x0c43b3e, 0x3e09636, 0x3d2a966, + 0x46dff8f, 0x42f69cc, 0x3ab20b2, 0x4a2451a, 0x5738e33, 0x25aaf7b, 0x333cdd6, 0x5c2330e, + 0x2e0270f, 0x33be448, 0x3491c7d, 0x380d62d, 0x2745d0f, 0x35c7470, 0x09ea28f, 0x122334f, + 0x53b66c7, 0x2c43168, 0x00abffa, 0x31513e5, 0x4e626e5, 0x53951b8, 0x1b58365, 0x4c8304f, + 0x1bfdf22, 0x2d2665a, 0x377a97c, 0x4be7e47, 0x286a4bc, 0x4441582, 0x2910d6f, 0x5ae3fd3, + 0x5bec3bd, 0x05d7a87, 0x3d0e63e, 0x2e25d22, 0x4d94e7c, 0x2060072, 0x32f1602, 0x1fe1b55, + 0x1fbe3f0, 0x2260e5b, 0x5b342f0, 0x521c52e, 0x2987d07, 0x1f1c85e, 0x20e44f9, 0x1f05bca, + 0x169fbac, 0x39175ab, 0x24ec502, 0x16a2fbc, 0x52add2b, 0x0430101, 0x49b0884, 0x0663883, + 0x1d47ce4, 0x5c28141, 0x4edad42, 0x2d72fd4, 0x1d0423c, 0x16be1d4, 0x10d5582, 0x4b4d144, + 0x106a797, 0x0fb2dfd, 0x5872da2, 0x5deb6e7, 0x2d46a52, 0x03fc4c6, 0x465eda0, 0x56a4ab7, + 0x18f0ad6, 0x39922bc, 0x4679687, 0x093cb97, 0x17a9712, 0x2272202, 0x2ef0aac, 0x0ac5a21, + 0x003b7fb, 0x389e091, 0x4c1a086, 0x24dca55, 0x5ea6823, 0x040d9df, 0x0bd0ce2, 0x49d626e, + 0x190c17d, 0x0ff6f70, 0x1979a8f, 0x11186a9, 0x49989cd, 0x5e941d7, 0x4bb7c9e, 0x548365a, + 0x2a79eef, 0x0cb597c, 0x4f51060, 0x1ee9fdc, 0x306d709, 0x55304ab, 0x00fd4ec, 0x3e8d707, + 0x3b9df83, 0x17e89b3, 0x059a797, 0x53ad54f, 0x3f02037, 0x4fb45b0, 0x4c4e5b8, 0x1423eb8, + 0x1653c44, 0x2e38aa4, 0x1e68c2f, 0x13fa069, 0x01ba70a, 0x1f24565, 0x58578d7, 0x27f6cd0, + 0x3087105, 0x130dcfe, 0x4b8b133, 0x3d728e3, 0x29e9d3f, 0x57c89be, 0x2a879bb, 0x5369a2d, + 0x3a9479e, 0x42e6b24, 0x5cc574f, 0x07fc6b2, 0x34524da, 0x0c62ede, 0x2737bd1, 0x37c81a5, + 0x4ff34bb, 0x382dad6, 0x1f1651d, 0x55b5489, 0x12d6551, 0x0dfde3f, 0x327618b, 0x3d94c02, + 0x1a7783a, 0x55f3664, 0x2c1a0f1, 0x2ba90eb, 0x1f1615e, 0x4df5aae, 0x515bb9e, 0x0e76d9e, + 0x236975c, 0x48d18c2, 0x2d45e9a, 0x2a5078c, 0x3ce205a, 0x0331485, 0x3215fbd, 0x02b42ab, + 0x00877cd, 0x525ab2e, 0x42ae7e9, 0x06dbb02, 0x5d152c4, 0x213cebe, 0x241637b, 0x0e25980, + 0x402cb8b, 0x07fcc2c, 0x0458c17, 0x4d8e48c, 0x4034e6c, 0x5722b66, 0x231c2a5, 0x04f0c40, + 0x355b88d, 0x5d66d89, 0x31aa77e, 0x13a6520, 0x30d0a54, 0x1fa46a9, 0x1c3f12a, 0x2265dac, + 0x5a0f196, 0x13301b0, 0x14142b7, 0x3c7fe83, 0x1b69b24, 0x1630d4c, 0x225d6e7, 0x3c21269, + 0x396870e, 0x502f249, 0x062964e, 0x3af2b73, 0x2ece8d0, 0x043d51b, 0x555639e, 0x4f876f3, + 0x4b70f78, 0x4424878, 0x4875ad9, 0x3942259, 0x10b5f05, 0x4a7fc80, 0x10a158a, 0x4e58a20, + 0x2a08e9b, 0x5205495, 0x1080ca8, 0x4218d95, 0x2250695, 0x37ac9f9, 0x52c1e5d, 0x02ea66c, + 0x33c5c62, 0x4c382d1, 0x5a707fd, 0x3950982, 0x1eea02d, 0x110a8b3, 0x01813fd, 0x2c2bd90, + 0x5199fc4, 0x32f1c4a, 0x2ebd3ac, 0x177f817, 0x3b0dab9, 0x57627f2, 0x113ce41, 0x1b807e9, + 0x1163c5a, 0x5c4ff03, 0x4590255, 0x5e6d9d5, 0x11babdb, 0x2e39e58, 0x1b6775f, 0x4be9348, + 0x46d898d, 0x2e39ee0, 0x4255049, 0x23b62a0, 0x49c291d, 0x2ff5448, 0x35ffad9, 0x0b0f112, + 0x287ed0d, 0x2e08afc, 0x02604c6, 0x270d0a7, 0x5e23db0, 0x5f08a67, 0x54b97a5, 0x3cd4b63, + 0x14cd663, 0x5adb834, 0x35efacb, 0x3829409, 0x140af79, 0x57f3ca8, 0x36ef13d, 0x0526051, + 0x3cf7cdc, 0x2cbb0c6, 0x0a45db5, 0x2e24d44, 0x0babb6a, 0x1352ec7, 0x57d50e3, 0x5ea2f2a, + 0x2a5df44, 0x1ece4f3, 0x5cdb1a1, 0x2149271, 0x1c02f14, 0x12422f0, 0x50b1ec3, 0x0bc9ecf, + 0x58b524f, 0x5eafdb1, 0x3051784, 0x10f7027, 0x3e521ea, 0x12bbbed, 0x21d39f3, 0x00b6439, + 0x302a786, 0x5941694, 0x019263b, 0x1143879, 0x0284828, 0x2f0c134, 0x4daa53e, 0x18c79dd, + 0x0ebc04c, 0x41c1f82, 0x4a20774, 0x55aa739, 0x5008c43, 0x5a85f48, 0x132f366, 0x12fa823, + 0x2523e57, 0x50c530d, 0x1101f2b, 0x28fc06e, 0x2db225d, 0x3120204, 0x195ead6, 0x1b91f5f, + 0x3b81036, 0x3463df8, 0x0c841e5, 0x34742e6, 0x05cee33, 0x0023c29, 0x2d01120, 0x52052d9, + 0x2586da2, 0x17a4ff1, 0x144d892, 0x37a1fbd, 0x5728c20, 0x111db68, 0x071c954, 0x1d7d6fb, + 0x22f698f, 0x1b3e4e5, 0x4169e9c, 0x0b5c475, 0x53c61e3, 0x351a99a, 0x289b3e0, 0x5a26009, + 0x332d6bc, 0x09cd1da, 0x2f24c60, 0x1810dea, 0x20eab99, 0x1f82959, 0x242923e, 0x3fa533d, + 0x13c968c, 0x0c5ac4a, 0x4d9663a, 0x567bb0c, 0x3f81dbf, 0x41bbdce, 0x4333674, 0x3e6fee5, + 0x450a4c8, 0x3a4ecfd, 0x4488320, 0x58f2022, 0x0d184bd, 0x5a4fa4a, 0x3983f78, 0x5516b84, + 0x4d746d2, 0x1a547f2, 0x4f868ff, 0x4861717, 0x4433694, 0x3b18db2, 0x4e159eb, 0x40c14e7, + 0x4529e68, 0x22abeab, 0x5445043, 0x1dd9a4f, 0x0fec7ae, 0x5277d41, 0x02bf503, 0x2e8140a, + 0x519fce0, 0x2d938f9, 0x21e97d6, 0x498126b, 0x0a3f2ac, 0x47c418f, 0x39c82dc, 0x009f54e, + 0x3c2d48e, 0x31a2448, 0x3cdeaeb, 0x16f8de8, 0x2c1b9d2, 0x4e96233, 0x2c4d765, 0x22ee45d, + 0x0cf227e, 0x21503ca, 0x1a7c3b2, 0x172106b, 0x2e11b86, 0x5c054f1, 0x16eb4d5, 0x51ad377, + 0x40c8bc9, 0x434022b, 0x010253a, 0x4b0a760, 0x55d6753, 0x302a0da, 0x3dd962b, 0x115900c, + 0x0fc1c65, 0x46c43e4, 0x50dfed3, 0x1165787, 0x5092b58, 0x3786171, 0x0eda969, 0x236358a, + 0x41f64c8, 0x41edd6b, 0x405eb8a, 0x225032e, 0x4f7d171, 0x3747e1e, 0x53f5301, 0x47b4d53, + 0x1f1d387, 0x0c6f2be, 0x33548dc, 0x1e28765, 0x2addc10, 0x25b2638, 0x4a84b79, 0x2bfb8e2, + 0x2fc1423, 0x4df2f7f, 0x303cb5e, 0x32b260c, 0x2461db2, 0x59c634a, 0x5a6228b, 0x4a80950, + 0x1c1f344, 0x081d1fc, 0x0f67ca6, 0x2c47700, 0x27f10e3, 0x5db8801, 0x1ebe356, 0x58f3fcb, + 0x235caae, 0x51c952f, 0x4934e69, 0x54931f0, 0x48594a5, 0x3a446de, 0x21ef011, 0x5e14cf3, + 0x1653647, 0x5792933, 0x0c66660, 0x1417ff9, 0x4e791b3, 0x156a29f, 0x10b5348, 0x12563c8, + 0x1d427ab, 0x38070de, 0x36bdd3e, 0x5beafa9, 0x4d85d26, 0x39d29a8, 0x0f59892, 0x1c65b2f, + 0x08e6f61, 0x2e52ce4, 0x29da15f, 0x530704a, 0x0ae9b5c, 0x09c992a, 0x33dff84, 0x0a856ac, + 0x0191f81, 0x35f9a09, 0x0953d45, 0x04f16da, 0x352d165, 0x35b9362, 0x0113a39, 0x1bef7ea, + 0x3c63385, 0x264b75e, 0x5d3fd04, 0x4e69de6, 0x2fc31f4, 0x231d6b1, 0x597dd7a, 0x114f377, + 0x2703abb, 0x0b1bd8a, 0x2e7f772, 0x1113670, 0x4ea00dc, 0x2732188, 0x52fb19e, 0x1a47221, + 0x0c09d19, 0x1f7acd0, 0x29b7d5a, 0x505e3ee, 0x32dc6eb, 0x40df7a2, 0x3a28279, 0x5be11b7, + 0x30a1702, 0x4b235a6, 0x2f7107b, 0x3b6d6b4, 0x4d703b6, 0x5cc307e, 0x3bb96ce, 0x18842fc, + 0x2b80f8d, 0x004d675, 0x5a269f2, 0x1d12741, 0x004bc4c, 0x03ba065, 0x158dfbb, 0x3397cca, + 0x4726fb0, 0x3c97ac2, 0x536c3c5, 0x114c8d2, 0x17295ec, 0x1063964, 0x31ba123, 0x3657cbb, + 0x07e1f74, 0x44a93de, 0x265eb55, 0x40e5b4f, 0x3336081, 0x3662382, 0x2875f7f, 0x1d09377, + 0x1f326e0, 0x568a3c6, 0x54dbf44, 0x4d0d43a, 0x0bae2be, 0x2b5a2ff, 0x115b4f2, 0x26ebe80, + 0x0f8d4ce, 0x2a7f009, 0x35ee46a, 0x47a18c2, 0x5df011e, 0x281a9b6, 0x0e36737, 0x3fdd7e2, + 0x01a9c19, 0x41061d9, 0x5d9826a, 0x309ab2c, 0x0b3f00f, 0x0e7db39, 0x541b75b, 0x1bfcea9, + 0x26f31a7, 0x3beaba1, 0x0c034db, 0x4bc8fe4, 0x14a65f9, 0x088ac5a, 0x16f95e2, 0x53cfed3, + 0x53cf19b, 0x156d5de, 0x25e2675, 0x2246714, 0x493ceb5, 0x0ba4fe2, 0x510fae0, 0x04dc4e7, + 0x47e37f8, 0x4b24629, 0x395fd7e, 0x1e02c1d, 0x4efd1a1, 0x01acc51, 0x0075994, 0x4d03b84, + 0x0cfbf3a, 0x56299a9, 0x5734fc4, 0x133aff0, 0x3d6eb32, 0x3199114, 0x1b8fd85, 0x0f23e35, + 0x3a9dce4, 0x1c7f542, 0x0d26cae, 0x0e1f666, 0x48c60df, 0x37032bd, 0x2a156ec, 0x4629858, + 0x30ffe9d, 0x379d063, 0x41314f1, 0x00707f2, 0x4b9252a, 0x48a718d, 0x098f886, 0x549d28b, + 0x10c7bc5, 0x0960ae6, 0x3089baf, 0x0343766, 0x5df72de, 0x049efef, 0x0a49fb6, 0x4e16fc3, + 0x34d8ed9, 0x56b4051, 0x1c98f7b, 0x320cbe7, 0x537096b, 0x5441cdf, 0x0766d5d, 0x05ec0d4, + 0x3e08901, 0x0be9487, 0x4a6beb8, 0x5b21477, 0x2bf310b, 0x20820e9, 0x2ef59e9, 0x3103ba8, + 0x42b4919, 0x4a2e097, 0x4af4355, 0x375abfb, 0x149aab4, 0x17edbe6, 0x382af19, 0x09548db, + 0x5ae1343, 0x2f233f4, 0x0bc179e, 0x18ead63, 0x40c3d39, 0x41034cc, 0x34559a4, 0x0c1ff0b, + 0x4db2d93, 0x40b7b3d, 0x4077bca, 0x532d049, 0x044e2b6, 0x06f8b7d, 0x3eecac2, 0x0550672, + 0x55fac47, 0x4137ffc, 0x15d0020, 0x51fbfb9, 0x49bb51e, 0x32c4888, 0x3e457d7, 0x1c15554, + 0x3b504d0, 0x59b284e, 0x0523e86, 0x1be45c0, 0x0eea59e, 0x4f1a038, 0x38bc942, 0x4787c2a, + 0x2ae7f04, 0x5b09bd4, 0x09d1d7b, 0x4d01ac5, 0x4cfbb56, 0x546dc26, 0x5121172, 0x18f834a, + 0x0f6506a, 0x093b76f, 0x009e002, 0x5149ebd, 0x02c9f70, 0x4d34dd8, 0x104ca3c, 0x4a53032, + 0x4877b87, 0x4fcc679, 0x5a36f68, 0x0c32404, 0x5d8096f, 0x3c98b52, 0x44c19cd, 0x03b3427, + 0x32ad880, 0x2b9d5d4, 0x4d9063f, 0x038db59, 0x5079cf4, 0x48dd496, 0x2b6f5be, 0x068c5a7, + 0x247b711, 0x2bd8cf6, 0x165f476, 0x0eae49e, 0x5752063, 0x52a8982, 0x4a5e4cd, 0x36b1b86, + 0x088ddce, 0x3e200a8, 0x2a2abb1, 0x0906b1d, 0x3776b89, 0x4f7dd4e, 0x101059c, 0x507216f, + 0x2e4cfaf, 0x4ce9fed, 0x3ab2fe1, 0x50a12a4, 0x2f6a542, 0x15da5f4, 0x5a9209d, 0x597f4e9, + 0x017f80f, 0x15f399c, 0x0c9ce7c, 0x4654a25, 0x5702612, 0x1c0fb45, 0x1e072f2, 0x4999c66, + 0x2b73cdc, 0x260c545, 0x4ced04d, 0x5045de7, 0x33c3e34, 0x2ee1dea, 0x30c19bc, 0x1e35419, + 0x403fd93, 0x02e005c, 0x0287f38, 0x34444b1, 0x27bfb6a, 0x2a97e3e, 0x26edb95, 0x42cde37, + 0x11f9432, 0x4ba53ab, 0x048b2d5, 0x0d8d449, 0x382b394, 0x07c2f09, 0x319d5be, 0x15c46bf, + 0x464d2a5, 0x427a35d, 0x192adcb, 0x493c695, 0x5a43159, 0x4e48bad, 0x51f279d, 0x25eb340, + 0x1f7ca59, 0x210eb6b, 0x0b5cf49, 0x5c53229, 0x21e78d8, 0x5bdbd2f, 0x4a16c82, 0x224f1dc, + 0x3a35ef6, 0x0d20de4, 0x425694a, 0x42116a8, 0x1b48a8e, 0x41dc509, 0x2c21687, 0x15c1741, + 0x0d136af, 0x5e45cdc, 0x26cd0f8, 0x3f3aa2f, 0x546781e, 0x4db45b3, 0x39d8a30, 0x3adf782, + 0x26f72ae, 0x09896f5, 0x556a834, 0x413d575, 0x016621f, 0x008f315, 0x492a708, 0x0b18a90, + 0x4101314, 0x11946bb, 0x0f6367c, 0x25c70a1, 0x4d70f1a, 0x5ef874d, 0x3da0698, 0x11fc490, + 0x2d458e9, 0x0f1bbf7, 0x0ef33d5, 0x1276a9b, 0x2d3957b, 0x5dd6448, 0x15b393b, 0x4c2a9de, + 0x3512679, 0x0103dea, 0x1e849a1, 0x42f731f, 0x3fd319d, 0x3d99c48, 0x3e48aa1, 0x0b085c3, + 0x43ebf4e, 0x05f6a71, 0x5f2786f, 0x0a92532, 0x47966fe, 0x1f23c5b, 0x5f20fa3, 0x275b32c, + 0x0e0e197, 0x19f1e1c, 0x19ec924, 0x5c0ddeb, 0x1ea49c9, 0x517093b, 0x0f16a7a, 0x1e24ec7, + 0x10f8354, 0x5f374d9, 0x4bcfbfc, 0x1e432c3, 0x5c9185c, 0x4da5f77, 0x3f9fa2b, 0x3d94698, + 0x240e43c, 0x1b98a85, 0x29a5b54, 0x3da5921, 0x0512ecc, 0x2ec2c72, 0x1877392, 0x41c57da, + 0x3710d6e, 0x41c6e39, 0x5909c2c, 0x02462c4, 0x071a8b5, 0x3fe3a12, 0x4fb23b1, 0x44b498f, + 0x38ac507, 0x10c2559, 0x552201f, 0x40a3b55, 0x17b407a, 0x002fa5c, 0x36d3a59, 0x0076841, + 0x547146e, 0x5849a23, 0x0bc2992, 0x3e6db4c, 0x5d07279, 0x3f92719, 0x00f0c87, 0x46b2a13, + 0x4a9042b, 0x4a01ace, 0x2a1666b, 0x17cad59, 0x0055d39, 0x30feaf2, 0x5700758, 0x2076d47, + 0x10b8bd4, 0x03e3540, 0x057868a, 0x30d9a65, 0x0ba2aeb, 0x406eee7, 0x3018d2e, 0x1e88ab8, + 0x458be11, 0x502673e, 0x0b9827f, 0x21c132d, 0x4925635, 0x358346a, 0x013fafb, 0x138ef22, + 0x216f0ef, 0x22e74f4, 0x0fe43ad, 0x39aa6bb, 0x0338527, 0x0417d5a, 0x3f1ade7, 0x436d775, + 0x4f181ee, 0x38a503c, 0x0dab593, 0x2a6cec0, 0x0e7b9aa, 0x3aef7e0, 0x0144735, 0x3f5a541, + 0x1eb0c47, 0x10aaf07, 0x3fa9ce3, 0x1635d30, 0x24422ce, 0x0d9dd30, 0x00a3c9d, 0x27b022a, + 0x489b0dd, 0x20e39a5, 0x486527b, 0x4c9cb62, 0x459e483, 0x0d62620, 0x5db38d3, 0x207c98f, + 0x283eb60, 0x33e2029, 0x2c21207, 0x5331f2b, 0x4cc0677, 0x3b1b78f, 0x2fb0b1e, 0x48147fc, + 0x3216a0a, 0x49fa537, 0x40623bc, 0x1c558c7, 0x51f9c25, 0x0aad7de, 0x3a4fb50, 0x1cdf9e3, + 0x3e146c9, 0x0c8e3ee, 0x1ea0559, 0x325e196, 0x0efa24b, 0x0becebd, 0x2fc0a85, 0x537bfdf, + 0x08d2251, 0x245e670, 0x3a1f40f, 0x38eaa59, 0x2510add, 0x0f8468a, 0x2ce3db7, 0x21f7398, + 0x024e584, 0x08ab8a0, 0x435c955, 0x4ec5229, 0x5747d88, 0x005ef3f, 0x027dbb0, 0x521c97a, + 0x1deddb4, 0x3e24a06, 0x2b9927f, 0x4a6217a, 0x03fe580, 0x1389d0c, 0x29bb437, 0x0af3e95, + 0x3d33a0e, 0x0030be4, 0x51cd89a, 0x059e987, 0x3d930c5, 0x33c0753, 0x2f37e8c, 0x31af0a6, + 0x1b1621f, 0x169f344, 0x4bc0139, 0x0b1975d, 0x58b1e84, 0x28bb873, 0x3446308, 0x4cd9c01, + 0x002616b, 0x1e73446, 0x5f23b53, 0x137271b, 0x0ca6363, 0x40ac879, 0x4bcc1ed, 0x28a13ae, + 0x11b619c, 0x3e9019f, 0x1926006, 0x3a3a54a, 0x3d76168, 0x5267ce1, 0x1967dc6, 0x2d7af89, + 0x51b31c3, 0x53cb5b4, 0x3aaa5e8, 0x12e1d65, 0x3503e5f, 0x5c1e9f2, 0x29b50b0, 0x3384647, + 0x43262de, 0x5c70936, 0x2d807a0, 0x41c5a7c, 0x3fbda03, 0x2c1f1e6, 0x320cf41, 0x2cb2d7d, + 0x1e3920c, 0x4f5a23f, 0x3760d02, 0x4de6f1c, 0x01d4d18, 0x11fb98d, 0x38912a5, 0x5916d40, + 0x04689ef, 0x1a170d1, 0x124c7ab, 0x5e9c9d4, 0x28a8e66, 0x16314d7, 0x4ce82fb, 0x49afb27, + 0x4c951b4, 0x2485f8f, 0x0e35ebf, 0x0ca2f27, 0x3219e4c, 0x48cefe2, 0x46c993c, 0x1571e3c, + 0x4c32bc0, 0x4db864c, 0x532aed3, 0x42508b3, 0x199d00b, 0x2000602, 0x04564fd, 0x4fdc687, + 0x0445b9f, 0x2f604ce, 0x3b31e92, 0x22d0075, 0x326f9af, 0x16f07b4, 0x24a6c34, 0x21c75e0, + 0x166b8ec, 0x0908fd7, 0x2cc406f, 0x25adb93, 0x1f9eb7b, 0x33607f1, 0x18ba21e, 0x05d01ab, + 0x30e6df0, 0x05f82b5, 0x0eb298b, 0x31cae3e, 0x02541b1, 0x5b61ff2, 0x37be9ed, 0x50b2c5b, + 0x4a457f1, 0x1a6effe, 0x4fe6658, 0x52863fa, 0x34d5f48, 0x29e338a, 0x06aa806, 0x48a3a6b, + 0x5590035, 0x073a0d2, 0x47586dc, 0x3ebd552, 0x21f2f22, 0x36f0cb3, 0x0660cfe, 0x1b28067, + 0x02d7822, 0x43cc61c, 0x24945c1, 0x4ff4f56, 0x454457e, 0x39d73f0, 0x524f560, 0x28583ee, + 0x118e11f, 0x09cf8b8, 0x55e5d96, 0x441234c, 0x58b29ca, 0x44b4232, 0x241e2fd, 0x1f4c225, + 0x38dedc6, 0x18fe952, 0x3055b13, 0x1216fb2, 0x4bf3dcd, 0x1c0270e, 0x3f18487, 0x2a519c1, + 0x50c86ab, 0x0718baa, 0x5ae88c9, 0x4d4dde8, 0x0b5b828, 0x58f1e58, 0x0f433aa, 0x06d654a, + 0x55ff991, 0x2831e5c, 0x2857c1b, 0x176a550, 0x13fc35f, 0x06e026a, 0x3cf4df4, 0x36f6362, + 0x5b22e83, 0x56fa2bf, 0x021ee1b, 0x5a27285, 0x0f1be0c, 0x3b55833, 0x2e74fb3, 0x3028d10, + 0x0deaf0d, 0x2c01726, 0x3d813ed, 0x22ff72d, 0x57ef158, 0x5d486e5, 0x0d7a153, 0x290734a, + 0x44b627b, 0x4243d53, 0x1026d7d, 0x283f669, 0x53933f0, 0x18a88c8, 0x00ee52f, 0x2ea0a7e, + 0x3583f7c, 0x3eef362, 0x07d2338, 0x530fc54, 0x311e1d8, 0x2ce65b8, 0x0af845c, 0x1bb021a, + 0x174d03b, 0x05c8034, 0x4c37d70, 0x3836609, 0x042c4a6, 0x357a5fa, 0x3a3d935, 0x130e0e8, + 0x52207a3, 0x5af9e2f, 0x3b04ca9, 0x03e2ff2, 0x144aaeb, 0x09635e2, 0x2fc245b, 0x3df03d7, + 0x2199bdb, 0x004d94c, 0x1133f9a, 0x58be09b, 0x45fd9ca, 0x5e12d33, 0x49fc58f, 0x3d4748d, + 0x36e856c, 0x4668b58, 0x3d642a0, 0x39ae534, 0x554f2f8, 0x4d236b2, 0x21fe4af, 0x06a8e2c, + 0x2306f34, 0x04c1fe2, 0x06aa967, 0x4a257a9, 0x2aee7b2, 0x567d5ab, 0x1d585be, 0x37655c1, + 0x3340948, 0x1544a21, 0x12ab999, 0x10735d0, 0x436fe79, 0x5b82cc8, 0x0c764ea, 0x2ae33c0, + 0x23b3edb, 0x30d46cb, 0x20c88b1, 0x24f3442, 0x41b4d45, 0x4349fc7, 0x4dc93ce, 0x2adb2b5, + 0x028d2c5, 0x4e6d92c, 0x51f4d10, 0x2835b02, 0x5e68106, 0x027e994, 0x58cee93, 0x38e5384, + 0x060e425, 0x5894907, 0x1872a39, 0x5dc97dc, 0x0faf18b, 0x589293e, 0x176dfe7, 0x283b747, + 0x49ee8f7, 0x31ee47c, 0x0b8f55a, 0x34dc5f3, 0x1d0d382, 0x4505caf, 0x1d4949a, 0x210d47c, + 0x15cbb77, 0x2b09d89, 0x2254620, 0x1c8b42b, 0x285d7c5, 0x22e7646, 0x479da15, 0x26db31d, + 0x1517573, 0x3a20e62, 0x2ff3602, 0x126cf4d, 0x1397625, 0x2f8af37, 0x0a7525c, 0x5a0252e, + 0x0215d31, 0x1ed226a, 0x2f039c4, 0x0b59c8b, 0x4691d4a, 0x1a73bc2, 0x243cacf, 0x253bd15, + 0x1cb3243, 0x4c657ec, 0x5070d3a, 0x2594bff, 0x1576253, 0x18c4edb, 0x4ca98d2, 0x5780dcf, + 0x420aee3, 0x514fb01, 0x376d58f, 0x5f0ea00, 0x473f033, 0x390d67e, 0x5ab0869, 0x2a52661, + 0x5ea656e, 0x13f357c, 0x552e1ca, 0x3437bc6, 0x28f04b0, 0x5645d26, 0x3e232a7, 0x4911d25, + 0x2bef435, 0x26805f9, 0x479cbd3, 0x4a83f53, 0x35eb4d4, 0x37382a7, 0x12182da, 0x378ef4e, + 0x5d675bf, 0x4607e73, 0x4563509, 0x34c7b39, 0x30b5f01, 0x102b823, 0x3654c61, 0x011adf6, + 0x1420f9a, 0x52f6dd7, 0x2a29b1c, 0x055e1a2, 0x2a7a96f, 0x5c2219b, 0x11ae3ed, 0x54d00e5, + 0x0d75244, 0x20890de, 0x2386060, 0x4991209, 0x42e3272, 0x486bec1, 0x23c90f5, 0x3a69ab8, + 0x36b8a5a, 0x3035e18, 0x258ceda, 0x26529f3, 0x37ff474, 0x5cfed3e, 0x040bd12, 0x25206cf, + 0x24b0c8f, 0x22f998f, 0x19430df, 0x4ca367d, 0x12d5b26, 0x2d969ba, 0x3c55e74, 0x01c0b7b, + 0x0af26cf, 0x2d7eaf4, 0x48490fd, 0x2df5c47, 0x29e1571, 0x461a426, 0x4cf7183, 0x3b57677, + 0x11800b1, 0x0c35a56, 0x489b742, 0x2bbc17e, 0x32a6534, 0x15357b8, 0x0fce89d, 0x1bf17ca, + 0x0f3f070, 0x5852a68, 0x178a100, 0x1189c2c, 0x4b5d272, 0x0c05add, 0x3f5926d, 0x3f07f3a, + 0x510bae3, 0x1d8bc17, 0x2f58f6c, 0x3d7a38b, 0x1b0e9fb, 0x4a8ae7b, 0x3c5ac87, 0x00fa68f, + 0x16d388d, 0x2fc7d83, 0x3dc492a, 0x462edcf, 0x4a9ea66, 0x08aca0c, 0x3108c65, 0x19d70f7, + 0x5f4e3b2, 0x3f6f2f2, 0x1ec186a, 0x27bd6b1, 0x28be6d0, 0x0275b86, 0x4ededfe, 0x5d2f956, + 0x4618e8b, 0x37073dc, 0x19aa633, 0x0abee93, 0x1077f9c, 0x20cdf77, 0x007f0d8, 0x1799e7a, + 0x2c3178c, 0x0965bac, 0x2d359b0, 0x408f29d, 0x51000f3, 0x3733470, 0x318b9e6, 0x0961a75, + 0x0466429, 0x4488ea9, 0x31fd564, 0x403bdf7, 0x4951dc5, 0x4234b4b, 0x4b65e45, 0x266f19d, + 0x3d2f51d, 0x41fb039, 0x5b8b783, 0x1effbf8, 0x3e165aa, 0x53fa7ee, 0x43ff04a, 0x30e1109, + 0x4196df9, 0x0758778, 0x27f1d0e, 0x5e62f03, 0x31f7a76, 0x20ead9c, 0x0d9541d, 0x4f79664, + 0x18b089b, 0x060c739, 0x4a31ad2, 0x2069d56, 0x36071a8, 0x21c89e0, 0x119784e, 0x4ca88e7, + 0x06db3f2, 0x59d3dc5, 0x127075a, 0x1e4a5b2, 0x2f30268, 0x19dd50b, 0x4e1be7b, 0x4f99dd7, + 0x48d2118, 0x15a39d7, 0x5cd8deb, 0x2e79bcc, 0x595ba41, 0x40d2e17, 0x0bd620b, 0x352d160, + 0x4408ad9, 0x4fad40e, 0x175d3be, 0x1289445, 0x2e52a33, 0x41d222c, 0x246a470, 0x0f89c9b, + 0x08462e1, 0x35adbfc, 0x5898d97, 0x1911448, 0x0bce2ae, 0x33e7bb5, 0x1af7398, 0x44fc995, + 0x44add3a, 0x26d0042, 0x4fb0b56, 0x19aaf0d, 0x43b824b, 0x15556f0, 0x5d661b6, 0x52b2e53, + 0x3173fe1, 0x39c4476, 0x38f65d1, 0x2c586af, 0x499f431, 0x2502169, 0x4bb6cf8, 0x084be40, + 0x4fa7edd, 0x0f8d245, 0x4fa34e4, 0x4afdd6e, 0x11bed30, 0x11304b4, 0x3ef5aa8, 0x36a02e5, + 0x09d37d1, 0x1411abd, 0x3a5cc4d, 0x0c61cea, 0x09a49be, 0x0c090b1, 0x40b3ede, 0x511d625, + 0x227f229, 0x25926cd, 0x05bbea5, 0x5dfdbaa, 0x0576b65, 0x4cbce39, 0x1e4ae16, 0x0175202, + 0x248a07a, 0x53498e7, 0x32f9c52, 0x060dc7b, 0x2211b00, 0x4a33af0, 0x122ad57, 0x046a453, + 0x0ee1ccf, 0x29aacfe, 0x50e5496, 0x056e80e, 0x30b2afa, 0x4e6a1e1, 0x2230263, 0x395c8a5, + 0x4f3a073, 0x50ee280, 0x2797fe8, 0x01fb071, 0x15cc49f, 0x23d0a69, 0x469d6e7, 0x2376b0e, + 0x5f585a8, 0x03170d7, 0x448ae86, 0x22dd643, 0x02da337, 0x1dc0d21, 0x526c0d4, 0x0e2287e, + 0x1691da7, 0x03fa3e0, 0x50d992e, 0x3654049, 0x27bf370, 0x29752b7, 0x00c4f2d, 0x05c3a1b, + 0x2b799ed, 0x1875599, 0x0808bfb, 0x299736c, 0x3c5dd4b, 0x450739a, 0x2098741, 0x4c22dc4, + 0x24c30e0, 0x2eb4a23, 0x1024695, 0x3eb8606, 0x115c3f9, 0x13e2957, 0x211d8bb, 0x516efe9, + 0x270a6f2, 0x2246ff0, 0x16db06d, 0x4c2c4b4, 0x0c45ff7, 0x22a8c31, 0x42a6c6e, 0x4d977aa, + 0x051aeb9, 0x0474ee6, 0x2364861, 0x28b15e4, 0x351be5d, 0x4a04d04, 0x23d2c7c, 0x50e6106, + 0x122973c, 0x53d360a, 0x55ade5a, 0x25353f2, 0x14020b1, 0x07a3fcb, 0x5863185, 0x523b871, + 0x3e4bb40, 0x3b262bf, 0x0b747f8, 0x4b81acd, 0x4d812e2, 0x26451f5, 0x3642987, 0x3e1fba8, + 0x4dfaa75, 0x326a499, 0x067674c, 0x4143e68, 0x5b4442d, 0x4ae0ca3, 0x3eeb4fa, 0x2f0bb35, + 0x4ff1d0a, 0x3f3230d, 0x0ca1ef1, 0x35c5802, 0x4b4a32d, 0x3ba9432, 0x0286f9d, 0x1f04bb7, + 0x512a591, 0x24cef49, 0x330e7d4, 0x1e0835a, 0x56c2990, 0x341eb3c, 0x0afcdf0, 0x0134d7b, + 0x14cd429, 0x05ad768, 0x1f60303, 0x08499ae, 0x286f823, 0x118e722, 0x3bb1e30, 0x0ce88b7, + 0x36d14da, 0x4f4b1e5, 0x0f7bbb1, 0x35dbb0e, 0x50de390, 0x1ead31c, 0x0c60b5b, 0x505ef37, + 0x0335c6a, 0x1f90905, 0x5e4edbd, 0x528dbfa, 0x0748725, 0x4a01ae8, 0x5e259b7, 0x21a8f23, + 0x17d657c, 0x3ed484a, 0x2db3233, 0x1f466a2, 0x5a263c2, 0x48e72f6, 0x5746dad, 0x18198ef, + 0x5d8282b, 0x1287929, 0x24a88a0, 0x37b8c88, 0x2a16281, 0x457cf6d, 0x0146632, 0x3ef2526, + 0x3151eb2, 0x1b7c89f, 0x2c007e3, 0x363e1e9, 0x0598811, 0x327b2d8, 0x5e2e1cc, 0x1836d49, + 0x5661b99, 0x00e71cf, 0x57d9e36, 0x117c5a9, 0x2a8b2d8, 0x089dd30, 0x4c9d4c5, 0x1d0a14b, + 0x339d5c0, 0x52ac0ab, 0x2034390, 0x45d2cf5, 0x3a7ab93, 0x5b5cb76, 0x1a87b61, 0x1cab7da, + 0x3b7e43b, 0x409341c, 0x0e2ef7e, 0x5e60d9c, 0x53f60cb, 0x3f417e2, 0x2a1c002, 0x1034777, + 0x4433f92, 0x551ff6f, 0x01f748c, 0x2090b92, 0x140c8cf, 0x30a0cea, 0x1ace5d4, 0x179f8ce, + 0x5de2004, 0x0786bee, 0x119f9de, 0x00025a0, 0x4f5fb7a, 0x437fd92, 0x1f2cac8, 0x4a7f498, + 0x1fb3cb5, 0x465a4a8, 0x0c3a2a1, 0x0fda1ae, 0x25692f8, 0x5bc8e7a, 0x42168ec, 0x3e645aa, + 0x20e90df, 0x1dc2918, 0x5ab9415, 0x127bb74, 0x5bbd472, 0x2a77df3, 0x0905b81, 0x31bca0b, + 0x41b8f7f, 0x4fabfb9, 0x4eaf6e1, 0x27fc3a8, 0x17dc8db, 0x3bd6d1c, 0x203a431, 0x4842661, + 0x570bb6f, 0x04ea761, 0x17778d6, 0x0b48b9d, 0x5552bb7, 0x49769a2, 0x45dfa75, 0x422ac6a, + 0x576c090, 0x0ca14ca, 0x34c62bc, 0x081751f, 0x453fea1, 0x052905f, 0x4175f03, 0x08520cd, + 0x21d3b04, 0x339d658, 0x55f7280, 0x58371de, 0x3e9deb8, 0x44d451c, 0x1ee94f7, 0x452c2a2, + 0x03e84e9, 0x5d9b039, 0x4a88d26, 0x480e896, 0x42a8c01, 0x3256405, 0x3cf16cd, 0x1d079a7, + 0x54a0f8e, 0x57111cc, 0x0ce29ab, 0x0c13557, 0x57c21bb, 0x116a20e, 0x0d0bedc, 0x283dad5, + 0x3211b90, 0x23458ef, 0x59a3990, 0x4709fc3, 0x3603a2b, 0x48d039f, 0x0292ef8, 0x3bfdc8f, + 0x1cce36d, 0x31ce719, 0x2a62034, 0x5043b61, 0x0cde255, 0x5f03daf, 0x417b014, 0x11206b3, + 0x1889bfd, 0x520bbb3, 0x26dd839, 0x52631d3, 0x153c66e, 0x2a74e83, 0x560a6d3, 0x107b498, + 0x2731c2b, 0x078b567, 0x261e6f5, 0x01e6b1d, 0x5771c11, 0x1bdb1e1, 0x381610e, 0x472d2a2, + 0x1385452, 0x361478b, 0x5933539, 0x36a67b2, 0x01537ed, 0x27dee69, 0x122f9ea, 0x390bb34, + 0x242f98a, 0x414406d, 0x255e470, 0x58f4279, 0x55d565e, 0x5b7cd98, 0x09ff428, 0x5124e1f, + 0x56faf9f, 0x1698174, 0x4c199cc, 0x0f8eb73, 0x245ee3d, 0x1d863b8, 0x5f50e40, 0x151d9a8, + 0x5e0c9c3, 0x3155cb5, 0x1935dd7, 0x3947d51, 0x076deb2, 0x0553866, 0x1957a79, 0x54b0c06, + 0x550dcd1, 0x3f62c0d, 0x0ecd643, 0x54395aa, 0x19ef96e, 0x10110d6, 0x3c915cd, 0x4ca63f8, + 0x051848d, 0x1a97aa2, 0x5526242, 0x202ff5a, 0x104900d, 0x4d00a16, 0x5821221, 0x20ea420, + 0x2c53e99, 0x4863f5a, 0x1087219, 0x321381a, 0x00857fe, 0x0523134, 0x3940a6f, 0x3c896c0, + 0x22b6d33, 0x5c2ea31, 0x5885143, 0x13de62d, 0x019e3d1, 0x50980cb, 0x535f580, 0x2710b06, + 0x290daa2, 0x2e3ac61, 0x5c53610, 0x31dabcf, 0x32c481e, 0x2462a6e, 0x425ea08, 0x3a5c3ab, + 0x032da76, 0x56b1384, 0x1c3a922, 0x2b6238e, 0x408528f, 0x2ce13f1, 0x44824af, 0x4be4469, + 0x55ea833, 0x0a530f1, 0x2c80425, 0x0b80008, 0x19eeb5a, 0x02f5072, 0x24ca737, 0x3b82655, + 0x56007b0, 0x3d2340a, 0x33c376a, 0x44fba52, 0x435129c, 0x1c7cd3a, 0x46ecd77, 0x450619f, + 0x3a08631, 0x3c0b3f0, 0x27fffe7, 0x2b66951, 0x27091ca, 0x36ed4d6, 0x1cccef7, 0x0127f96, + 0x213a171, 0x0e79011, 0x4254f43, 0x0072ad6, 0x5b0923d, 0x47ad6df, 0x0c7fc83, 0x433e3ce, + 0x3d0eeb0, 0x0fa5843, 0x55ad491, 0x1d0a950, 0x22994be, 0x318a858, 0x4aa25d7, 0x25559c2, + 0x05c70af, 0x4a93f93, 0x37fdcb7, 0x24e503f, 0x48241f9, 0x2d22c7a, 0x4376be2, 0x470998c, + 0x54074ca, 0x29be05d, 0x446f05b, 0x5764f7e, 0x41d10fb, 0x5baf355, 0x23838ae, 0x12d6f15, + 0x15590c8, 0x0c2986e, 0x2140fb0, 0x01c8f12, 0x3eb8810, 0x2248264, 0x460f5a9, 0x2652ee5, + 0x219a0ef, 0x51d3bb5, 0x27a86a2, 0x48eb622, 0x2b4d204, 0x5f2d632, 0x478481d, 0x05d90e6, + 0x0720c7d, 0x5cb88c2, 0x2a5b47f, 0x5489f00, 0x2ea2205, 0x043dbdf, 0x1edb2d5, 0x284bf9b, + 0x25e9ab9, 0x20a3847, 0x06d0aab, 0x22614fd, 0x295a577, 0x472c3a7, 0x374321c, 0x15d6854, + 0x198c05a, 0x465a533, 0x49727e9, 0x1cf616a, 0x2d00605, 0x5d96304, 0x2f31d99, 0x0cb084b, + 0x24d1d93, 0x3b60288, 0x280ab4b, 0x55323da, 0x2dfa305, 0x3137fb4, 0x19e7683, 0x45b41bd, + 0x3373692, 0x5cf9888, 0x4f2c3eb, 0x47cdf41, 0x57802ee, 0x3817225, 0x45eb546, 0x03b990c, + 0x3d0ea4a, 0x4a88b6c, 0x1384d7a, 0x25f4043, 0x3893349, 0x3c45d06, 0x0bdb9c8, 0x1ea4134, + 0x4e59115, 0x18ff096, 0x51ec1ce, 0x47a8855, 0x1459e6c, 0x231fab0, 0x47fb36d, 0x1e8699e, + 0x2a1b348, 0x33e69df, 0x28f7c2b, 0x00c7026, 0x53d1bfa, 0x4fc8d68, 0x199ba96, 0x3564a58, + 0x2d6b5fd, 0x51766e2, 0x5c9db35, 0x32f7395, 0x5110f57, 0x427256e, 0x5180888, 0x1e7f590, + 0x0387b10, 0x3d290bd, 0x5210179, 0x346c191, 0x142c379, 0x34fd011, 0x48af6ce, 0x0d7f9c5, + 0x3160d2d, 0x45c87f6, 0x313a3e6, 0x3a47b87, 0x0cd32a6, 0x3b47aaa, 0x026596e, 0x20bc93f, + 0x5591c1b, 0x0478f5a, 0x0a5bec0, 0x2535115, 0x50df790, 0x0da9e58, 0x20b06b3, 0x5f32065, + 0x2454614, 0x4500835, 0x3f4d125, 0x040272b, 0x5000f06, 0x4348fa0, 0x33199a0, 0x3303796, + 0x30e1a5e, 0x5c418da, 0x46471e4, 0x4520823, 0x1bd208d, 0x4dbaacc, 0x4ee2acb, 0x092ff02, + 0x45d8c67, 0x3f7121e, 0x0b023c1, 0x31da2a7, 0x597d3e5, 0x089e4e5, 0x133e995, 0x45b6dc3, + 0x56fa322, 0x1cfd7a2, 0x3b9c495, 0x04c7ce3, 0x2adf0a9, 0x56be0ce, 0x4294773, 0x05f061e, + 0x4beb15b, 0x45ee7ca, 0x528659b, 0x022caed, 0x2789c77, 0x3385793, 0x3f5bf8a, 0x169a294, + 0x1bb1c49, 0x5e948c2, 0x0c09583, 0x1888a04, 0x3602b05, 0x411c064, 0x1c477da, 0x0a13c1f, + 0x3704ba8, 0x5dc8be4, 0x06ecba1, 0x3496c87, 0x39f3483, 0x338445e, 0x448d690, 0x3490561, + 0x47e3808, 0x162f30f, 0x16435e7, 0x44cb115, 0x3bfbe99, 0x57bbd9f, 0x3bbbeed, 0x5addf00, + 0x2e3a6a3, 0x40cbc01, 0x4ec7de2, 0x54e9ea7, 0x3993f95, 0x1364820, 0x0d62afc, 0x35efa55, + 0x03bc4b7, 0x0e1a554, 0x2b0c5bb, 0x058a1d6, 0x14d8cb6, 0x2ec334b, 0x466e7ff, 0x463688b, + 0x1f92f8e, 0x2eb1ff8, 0x07dadf6, 0x5eb8200, 0x42d6ecf, 0x18e3750, 0x3dbde74, 0x417a54b, + 0x227bb0b, 0x4bf00a0, 0x4fbec64, 0x05b1888, 0x58cf838, 0x24a914c, 0x34e03ad, 0x24f25ac, + 0x30d4ccf, 0x08deced, 0x2b70f57, 0x4126f97, 0x2b1dcf4, 0x35c947c, 0x04e3820, 0x249ba88, + 0x0de3d42, 0x5a78e22, 0x36c85e3, 0x0a8d44a, 0x07a8994, 0x2c9eccb, 0x48c6185, 0x2240860, + 0x43312c6, 0x050affc, 0x58d2a1c, 0x2e4ea14, 0x5ad4919, 0x266e6bf, 0x00bb8f2, 0x41dcae1, + 0x57df710, 0x210fc04, 0x2c5ac65, 0x457d709, 0x3655d93, 0x3bccd56, 0x0da53fa, 0x52f4d8e, + 0x53c27cc, 0x3e40fe5, 0x0126c6c, 0x2d5ac00, 0x0e435be, 0x3c9f6fb, 0x5074c02, 0x13d39d1, + 0x4c08d7d, 0x4bababb, 0x565310d, 0x1e2bd34, 0x36df254, 0x582af32, 0x2f8aaf9, 0x4bfad70, + 0x087094e, 0x110f201, 0x157c17a, 0x5cfd6a9, 0x473a6d6, 0x45d61e0, 0x1343ba2, 0x3d6eea5, + 0x2617104, 0x514a1b8, 0x1a9a661, 0x4447183, 0x38d4361, 0x57105b8, 0x4a153c8, 0x58ccc8f, + 0x261dfd6, 0x3493c91, 0x4d94838, 0x1348d71, 0x3a91b0b, 0x014037a, 0x43e5161, 0x3f286d9, + 0x59e3641, 0x3c830cf, 0x4c4022b, 0x4e2394a, 0x4707cb2, 0x26a1e8a, 0x4c6d0c4, 0x5e57744, + 0x01da8ec, 0x4971196, 0x1d9c73c, 0x5e19672, 0x4be3967, 0x02ed6e0, 0x350a8ee, 0x1774354, + 0x48d7478, 0x0ecebc8, 0x27c01cf, 0x1be3728, 0x22e7a82, 0x5cb8730, 0x06b3130, 0x1016de5, + 0x4c65718, 0x42e4aff, 0x421d5a7, 0x3e3da43, 0x5da8a59, 0x4e76367, 0x4699895, 0x417c513, + 0x3da9578, 0x5387a0e, 0x50ae88f, 0x5e1b541, 0x5534694, 0x12f1ce6, 0x3e247e5, 0x34df1b7, + 0x13c4508, 0x05e3d38, 0x0b57e4d, 0x1b12d24, 0x26908ac, 0x5ddb4c7, 0x380f95b, 0x4f1df61, + 0x59fe45e, 0x5158eed, 0x2de4584, 0x5367459, 0x1572d04, 0x14f4eed, 0x2a36851, 0x192154d, + 0x273d580, 0x571fd73, 0x3654fd0, 0x274977c, 0x4cd4d81, 0x3fff9dd, 0x0efa124, 0x231555f, + 0x28ddd8a, 0x4fe1059, 0x4edcc99, 0x2313e4e, 0x018f36c, 0x5a67b30, 0x1a48d8c, 0x00eb84d, + 0x1c0ed2c, 0x513cf0e, 0x3369c03, 0x2c0f984, 0x2b61109, 0x2e78bf9, 0x0003a5c, 0x42f746a, + 0x2d878b7, 0x380011e, 0x55adfd2, 0x319a26d, 0x4cf7a0a, 0x04c9de3, 0x468f995, 0x0bc4830, + 0x44b21a1, 0x55b57c8, 0x4e9d11b, 0x14a4899, 0x3835ea7, 0x44ea808, 0x2c8cf0f, 0x2fac41d, + 0x1e4b845, 0x4f730fd, 0x430b796, 0x12c4c54, 0x51d6a63, 0x44e1c86, 0x3ab0049, 0x3f61687, + 0x1553373, 0x25e44e4, 0x0247ace, 0x42b299a, 0x1901c52, 0x16479d5, 0x36bdecf, 0x421b211, + 0x1a8a824, 0x4c0f7e0, 0x319492d, 0x35627f2, 0x188ab51, 0x314079c, 0x16dfcb2, 0x0f35b2c, + 0x2399098, 0x138fb79, 0x4e82be1, 0x46c0cad, 0x346f686, 0x16e9ac6, 0x24be914, 0x3388e97, + 0x1914aa5, 0x3022504, 0x50e8bf5, 0x0d31655, 0x362d570, 0x480b8a9, 0x3ba52a9, 0x38ec748, + 0x036de59, 0x3d817a8, 0x4b90ddc, 0x0d00852, 0x5abe801, 0x40d9a1c, 0x5c5dfcf, 0x350dda4, + 0x1064be4, 0x5534e67, 0x512817b, 0x4edefec, 0x304dc64, 0x45af5bf, 0x539cddc, 0x1ef3d68, + 0x5257582, 0x30d4999, 0x3d7f582, 0x44fd7f2, 0x2c0a6f0, 0x0ff723a, 0x1a7915b, 0x03f171c, + 0x447d81b, 0x05ec207, 0x02c632d, 0x415084a, 0x23b1d42, 0x5104fd1, 0x5da4b32, 0x0f1a29d, + 0x1270205, 0x02680b8, 0x33a6c8c, 0x4f1f6bb, 0x30ab40b, 0x538e890, 0x0b6821f, 0x4c82906, + 0x406c9d3, 0x2048f01, 0x2fecbd7, 0x06c6ae7, 0x19e7a7d, 0x4dab4f4, 0x03a9cf5, 0x2af80f2, + 0x323d821, 0x290e051, 0x26d7324, 0x08bf312, 0x42e16b8, 0x39fc1e6, 0x5924f16, 0x5d286ef, + 0x26ed44d, 0x52fdcf7, 0x162908d, 0x4efc7ee, 0x5cbd41a, 0x06944bb, 0x3a11d24, 0x142dc47, + 0x444a2b7, 0x3affacb, 0x31a4d4b, 0x2f77002, 0x5625b5d, 0x45b9398, 0x1c3e0b1, 0x05b7689, + 0x1c8d103, 0x32e91d3, 0x1032bdb, 0x5a7bd94, 0x246077a, 0x046b82a, 0x4f98e1c, 0x090ede9, + 0x0f82b74, 0x59bb756, 0x2292b25, 0x529617c, 0x32535c6, 0x497059a, 0x48e0e6f, 0x536c1c3, + 0x38521c8, 0x486a8a0, 0x497a789, 0x2b6ba04, 0x564264f, 0x10500a7, 0x1ce2661, 0x409a859, + 0x342df5c, 0x3c057bc, 0x207e678, 0x530ab10, 0x561d66c, 0x4150294, 0x3a6792e, 0x4106a4c, + 0x4828d5d, 0x2fec6cd, 0x5cf5fe9, 0x596c81c, 0x292ac60, 0x12da2ea, 0x05d2ac4, 0x4bd5492, + 0x4b58470, 0x1d5c86d, 0x59f985e, 0x52b6914, 0x383a5b5, 0x293acb4, 0x0a74e03, 0x3860ca7, + 0x2f08fa1, 0x4add299, 0x55703fd, 0x5afc833, 0x3a66f50, 0x330494e, 0x2e736cc, 0x2cbe45d, + 0x331e9cd, 0x08d24c6, 0x56bee2d, 0x0ab1ac2, 0x5e84817, 0x153e60e, 0x12643cf, 0x3222123, + 0x270c969, 0x2c614ba, 0x04fa8e2, 0x54adf59, 0x20b207c, 0x085d318, 0x45d3985, 0x2674d86, + 0x234163a, 0x03f2807, 0x0d676da, 0x3eb3a3b, 0x1961553, 0x0cd4e2a, 0x2ad2ed4, 0x5484318, + 0x03d4e25, 0x25a7da3, 0x2853b36, 0x30009a3, 0x3c61b30, 0x0392556, 0x0cbb436, 0x12278b2, + 0x219e827, 0x2f318b6, 0x50bd090, 0x44a1397, 0x59814d3, 0x1429c61, 0x378d9c6, 0x39342cf, + 0x2c99c3b, 0x52c03ef, 0x42d67dd, 0x05eba18, 0x0428471, 0x21505aa, 0x21bd644, 0x1a32d5a, + 0x069b67c, 0x119af47, 0x44cda76, 0x428721d, 0x3ce54a0, 0x417b6dd, 0x3d6c2f7, 0x349ea4d, + 0x33a1aef, 0x5de2b23, 0x2f4ff01, 0x085bd7f, 0x4e32400, 0x39df021, 0x470b051, 0x216c3b2, + 0x194700b, 0x5def8ab, 0x1dd574a, 0x52d2a79, 0x0e6f573, 0x0591d11, 0x47d755a, 0x540aad5, + 0x539b8a5, 0x12c4c9e, 0x2a939da, 0x3cc8578, 0x5a66bc1, 0x10d4fb1, 0x0f263fa, 0x28c40c7, + 0x17b1e1f, 0x41cd24e, 0x2d332cc, 0x019a30f, 0x2f02597, 0x3d70fa1, 0x5b307bc, 0x5d29090, + 0x5e1aba0, 0x5dcbea0, 0x4a88eb9, 0x56350a0, 0x2639b0b, 0x46fcc3c, 0x35ee1a7, 0x267af5e, + 0x2e76f9a, 0x2cd6f27, 0x1365c28, 0x04c4861, 0x22de9c9, 0x53b2b1a, 0x04f1dca, 0x27d960e, + 0x2f177b2, 0x5284a05, 0x1e33d12, 0x42c0a67, 0x59fa2aa, 0x1a0bfc1, 0x508b1ad, 0x43f9a35, + 0x415bf73, 0x4b257a0, 0x5f1d3cd, 0x2061431, 0x2686613, 0x24364d8, 0x2781f11, 0x2363b81, + 0x5ad65c0, 0x526e4b6, 0x1135fdc, 0x54125f9, 0x3e266a2, 0x30b7901, 0x200ca3b, 0x56d7ac1, + 0x45fe545, 0x4eacbf3, 0x379615c, 0x22e51a4, 0x0ea253e, 0x2f40c0a, 0x08af0bf, 0x5c5e7fd, + 0x15033de, 0x5561d63, 0x04e1ee7, 0x2129b39, 0x420b6b9, 0x53c99e1, 0x2dde065, 0x081faec, + 0x0f31103, 0x39f8bf0, 0x359931b, 0x37a54e1, 0x296f38d, 0x354f896, 0x5502af3, 0x1f2c86b, + 0x2c7c1d3, 0x0922a02, 0x2aab8c3, 0x45b3daf, 0x476086b, 0x40adb31, 0x0f4824f, 0x5bf00ee, + 0x1cfdeac, 0x5bd4196, 0x4ed155a, 0x2d325f1, 0x1ff2cfd, 0x5b9c682, 0x45c47b4, 0x3474221, + 0x1d59731, 0x159149b, 0x173ca10, 0x2e71d45, 0x4cb939c, 0x05adec6, 0x0c043fd, 0x1279fe9, + 0x06c06a3, 0x584a237, 0x2c6b229, 0x51b3c32, 0x5972ffd, 0x0a2117e, 0x5cbe8e1, 0x4c62118, + 0x530ce01, 0x13d0fa7, 0x309f98d, 0x12d0cd4, 0x4fb8719, 0x24950e3, 0x0e09770, 0x350bd6d, + 0x50ec057, 0x3648f58, 0x240160d, 0x30cd6c5, 0x41231c1, 0x04c1b90, 0x0330b1f, 0x10f351a, + 0x4dcc8ac, 0x5249c69, 0x46bebf6, 0x154c29e, 0x17d3710, 0x0c9a97d, 0x2d12f1d, 0x1e21d00, + 0x0d8f827, 0x4060c8b, 0x4e283da, 0x504d6f5, 0x0266438, 0x26191c5, 0x4b84129, 0x573589b, + 0x1bc37bc, 0x291d612, 0x4e1436e, 0x22fbead, 0x1d33ee7, 0x3ce092b, 0x1882030, 0x56a3583, + 0x3437856, 0x454d80f, 0x42b35f5, 0x0d64675, 0x4d9cd11, 0x530a971, 0x2b2791a, 0x07afdbb, + 0x31717d8, 0x22a651c, 0x28185d6, 0x45cc923, 0x0919219, 0x4720d41, 0x1136da8, 0x0619da7, + 0x2298222, 0x527274f, 0x1847c68, 0x45ba143, 0x1fc68b9, 0x528bb16, 0x2e444f7, 0x292f753, + 0x3e862f5, 0x30c2f63, 0x0e37609, 0x2524fdb, 0x39fd53d, 0x18d66fe, 0x18fc78a, 0x3c7b3d6, + 0x462e8d8, 0x4ab5d33, 0x5817ed1, 0x3391293, 0x48cf2b1, 0x28396c5, 0x17c9aef, 0x37c4750, + 0x2fe7de9, 0x374cbce, 0x1a08def, 0x23986b9, 0x2070650, 0x0765f0f, 0x39482c5, 0x4b37890, + 0x2235d0f, 0x5240fb4, 0x0d94793, 0x57355af, 0x082d07b, 0x1feb469, 0x152aa9c, 0x3238cca, + 0x2a6f125, 0x1970455, 0x5ac228e, 0x2952852, 0x0118f16, 0x37e72bd, 0x24351c2, 0x0c56e11, + 0x4eb42e8, 0x32f111a, 0x1f621e9, 0x5e90794, 0x3226a64, 0x42ab6b2, 0x0643dbf, 0x30854bb, + 0x110e06a, 0x2ce06c1, 0x33796e8, 0x14d1b2c, 0x031070a, 0x4b9e9de, 0x334a5e1, 0x1ccca12, + 0x30c45a8, 0x46134f9, 0x3cddf1d, 0x43dac04, 0x45aa1c4, 0x2513849, 0x5299bcb, 0x5f40ca5, + 0x0665e26, 0x4b780eb, 0x074b842, 0x478b5d7, 0x0892d57, 0x5301411, 0x09cc5c0, 0x3f5305e, + 0x3fb9c41, 0x3770f92, 0x2a4def5, 0x4637f32, 0x0914c15, 0x0fe8210, 0x5606e29, 0x3bc2528, + 0x5e62145, 0x086ab0e, 0x01bad8e, 0x29c0749, 0x45e6b95, 0x3fc85de, 0x06e5ca7, 0x54d8dfe, + 0x3e76ffc, 0x24f7046, 0x52f69f4, 0x2123d7a, 0x1f0a9d9, 0x42785f4, 0x5960720, 0x452ee3d, + 0x5180935, 0x4aca308, 0x540c9d5, 0x21a9a0d, 0x07389a1, 0x1e03969, 0x5a9f5f5, 0x5b80384, + 0x3278cdd, 0x20315ff, 0x3b937c8, 0x2f3d8cc, 0x4c67fdf, 0x4c6e2c7, 0x0210d8b, 0x4dfb4ab, + 0x12445c9, 0x28a79f0, 0x271900a, 0x0376c2a, 0x0166b17, 0x28a672e, 0x34cac10, 0x413e828, + 0x4a9759b, 0x0ce0f53, 0x5d6d020, 0x1e7bde0, 0x2adb971, 0x11f26cd, 0x4575c09, 0x2e6236e, + 0x5be0df6, 0x1f552b8, 0x1ae0566, 0x28f71de, 0x2b84dbe, 0x2bcd4bd, 0x09be4d2, 0x4860e49, + 0x3cd20fc, 0x06f9064, 0x31edcf8, 0x251bca5, 0x34479a1, 0x3647845, 0x2ad3bf1, 0x073f9f0, + 0x24cb199, 0x57d71fc, 0x3f9ac7c, 0x412904d, 0x1eeacfe, 0x2c35c86, 0x09ddf75, 0x3033b89, + 0x4bd908d, 0x1996fcc, 0x2cd4f75, 0x2c92c3c, 0x33ae01a, 0x1093d71, 0x5aa7baa, 0x4a86577, + 0x25c4434, 0x41961f4, 0x5a6f3c7, 0x575d2fd, 0x3c4c39f, 0x545a974, 0x5570682, 0x4e1750b, + 0x3ef6ee7, 0x28d06af, 0x31a256f, 0x5334be8, 0x093e26f, 0x4796805, 0x5a228bd, 0x214203e, + 0x267537e, 0x54ee1ee, 0x35d2813, 0x4e3eed6, 0x29fc8ef, 0x0c2309d, 0x24c1e4a, 0x3828772, + 0x294cb93, 0x4e0d631, 0x16a8a5f, 0x06380ca, 0x146834b, 0x5d493a5, 0x3353f6b, 0x5dcf412, + 0x4a40a06, 0x19396c5, 0x3d40f48, 0x58f277b, 0x5082b70, 0x096896d, 0x213d04c, 0x11561bd, + 0x2405626, 0x1a30a65, 0x5bdf120, 0x2fe661a, 0x4f54188, 0x4773e64, 0x23bb84b, 0x2938adb, + 0x59d4767, 0x0dc0cce, 0x1fd8f50, 0x5de93a3, 0x307559a, 0x41c9c70, 0x33f296b, 0x49564cd, + 0x1728967, 0x5572a22, 0x167f580, 0x30cb478, 0x25d7b3e, 0x38b3504, 0x38484ce, 0x339f21b, + 0x1ef5cf7, 0x0b70d8c, 0x0b93ac3, 0x1afccbf, 0x5780fd3, 0x06558c8, 0x43b2a5d, 0x5b4626e, + 0x5e5121c, 0x302ffdb, 0x0e186df, 0x49a049f, 0x06fe3c5, 0x15574a8, 0x5d5f256, 0x2fa0ad6, + 0x0d545bc, 0x2c33e9f, 0x1ce8dea, 0x32c3c8c, 0x222e839, 0x3286b7f, 0x29f9000, 0x083cd91, + 0x32dc014, 0x4426bde, 0x2e36e6b, 0x38c03c0, 0x0b8930c, 0x2b2c4ac, 0x384bc6c, 0x228142e, + 0x42df93b, 0x4ca9a67, 0x0addfae, 0x26d6ff4, 0x2181387, 0x2ca2aa2, 0x520faf4, 0x3b89847, + 0x1c8936b, 0x4d43776, 0x13a9d3f, 0x29537e1, 0x07f3c86, 0x2431d30, 0x0fc8938, 0x021ca5b, + 0x493da18, 0x1f7d16b, 0x1cdd508, 0x3b2576b, 0x2d92e15, 0x46278dd, 0x002c05c, 0x002ffef, + 0x17c98df, 0x54a4042, 0x42ee518, 0x322a133, 0x4998b6a, 0x2249c3c, 0x51637d7, 0x10821ba, + 0x5430454, 0x22d7917, 0x0bebbcf, 0x1a085c0, 0x2d0a6b5, 0x508664a, 0x0ecfe27, 0x0ad943d, + 0x46cfaeb, 0x4ff27eb, 0x3d4bf1c, 0x2556e37, 0x5bbf38f, 0x27a0ded, 0x0678873, 0x4707dd8, + 0x07ec2c3, 0x3eeef64, 0x5c831e0, 0x32c1362, 0x304530b, 0x21d1066, 0x3bc740f, 0x46e4fef, + 0x1b52fc5, 0x3759882, 0x58354c6, 0x32b53e7, 0x4191ba0, 0x53aa864, 0x0dc68a0, 0x3069456, + 0x5d4cd11, 0x0ea1224, 0x12de9fb, 0x2677a63, 0x03a91a9, 0x5ca697c, 0x0addead, 0x2b219cc, + 0x02d21d5, 0x35bb686, 0x22ee455, 0x06bb267, 0x096f12d, 0x4aeba17, 0x331d452, 0x1a193d5, + 0x01b6fa2, 0x3158ad6, 0x3616ab3, 0x3f5f64d, 0x543c3be, 0x4f8e55a, 0x5865875, 0x1987e49, + 0x549e323, 0x0a7c16f, 0x5a977dd, 0x2a4fdd1, 0x07c0911, 0x11f6bb3, 0x4a05c34, 0x245754e, + 0x512e836, 0x36a0fef, 0x496d3f0, 0x041cb0f, 0x0f9b6ee, 0x3b10f70, 0x3bc2efc, 0x5719e3b, + 0x2aeda17, 0x17065f0, 0x3db44ff, 0x55d7b26, 0x2e1e859, 0x5869d14, 0x1310d92, 0x1e02d41, + 0x45c3134, 0x4072c88, 0x254c979, 0x5141c81, 0x56ac15b, 0x0cb4b87, 0x1ca522b, 0x53df04a, + 0x163bee7, 0x33c3686, 0x2823053, 0x1b4068c, 0x30a1e0a, 0x3bd6d3b, 0x4172407, 0x1734d9f, + 0x316842b, 0x27cab0e, 0x384aa6f, 0x486db5c, 0x57fb84f, 0x1eba66d, 0x5925c43, 0x0dd34c7, + 0x1983276, 0x1eb5679, 0x11ccf9f, 0x496f98c, 0x1c35b4b, 0x48b7444, 0x48bceed, 0x01d9c13, + 0x1ddee3b, 0x1127181, 0x5ac1a92, 0x1fd4bc5, 0x0b166cd, 0x107b011, 0x52c02c6, 0x199c690, + 0x490fed8, 0x2b77c22, 0x383bd32, 0x040d7df, 0x44ce3bf, 0x2c6e777, 0x3dd86af, 0x3fa677b, + 0x1d46fff, 0x146703c, 0x07dc71f, 0x05a6b46, 0x53660a3, 0x3b4b5c9, 0x4ec4cbb, 0x248ae53, + 0x0d5d155, 0x4363005, 0x2cbd064, 0x5c18f03, 0x214bedd, 0x42ef202, 0x41827cd, 0x27a8fe9, +}; + +struct random_series +{ + uint32 Index; +}; + +inline random_series RandomSeed(uint32 Value) +{ + random_series Series; + + Series.Index = (Value % ArrayCount(RandomNumberTable)); + + return(Series); +} + +inline uint32 RandomNextUInt32(random_series *Series) +{ + uint32 Result = RandomNumberTable[Series->Index++]; + if(Series->Index >= ArrayCount(RandomNumberTable)) + { + Series->Index = 0; + } + + return(Result); +} + +inline uint32 RandomChoice(random_series *Series, uint32 ChoiceCount) +{ + uint32 Result = (RandomNextUInt32(Series) % ChoiceCount); + + return(Result); +} + +inline real32 RandomUnilateral(random_series *Series) +{ + real32 Divisor = 1.0f / (real32)MaxRandomNumber; + real32 Result = Divisor*(real32)RandomNextUInt32(Series); + + return(Result); +} + +inline real32 RandomBilateral(random_series *Series) +{ + real32 Result = 2.0f*RandomUnilateral(Series) - 1.0f; + + return(Result); +} + +inline real32 RandomBetween(random_series *Series, real32 Min, real32 Max) +{ + real32 Result = Lerp(Min, RandomUnilateral(Series), Max); + + return(Result); +} + +inline int32 RandomBetween(random_series *Series, int32 Min, int32 Max) +{ + int32 Result = Min + (int32)(RandomNextUInt32(Series)%((Max + 1) - Min)); + + return(Result); +} + +#define HANDMADE_RANDOM_H +#endif diff --git a/test_data/lots_of_files/handmade_render_group.cpp b/test_data/lots_of_files/handmade_render_group.cpp new file mode 100644 index 0000000..baf74c4 --- /dev/null +++ b/test_data/lots_of_files/handmade_render_group.cpp @@ -0,0 +1,1219 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#define IGNORED_TIMED_FUNCTION TIMED_FUNCTION + +inline v4 +Unpack4x8(uint32 Packed) +{ + v4 Result = {(real32)((Packed >> 16) & 0xFF), + (real32)((Packed >> 8) & 0xFF), + (real32)((Packed >> 0) & 0xFF), + (real32)((Packed >> 24) & 0xFF)}; + + return(Result); +} + +inline v4 +UnscaleAndBiasNormal(v4 Normal) +{ + v4 Result; + + real32 Inv255 = 1.0f / 255.0f; + + Result.x = -1.0f + 2.0f*(Inv255*Normal.x); + Result.y = -1.0f + 2.0f*(Inv255*Normal.y); + Result.z = -1.0f + 2.0f*(Inv255*Normal.z); + + Result.w = Inv255*Normal.w; + + return(Result); +} + +internal void +DrawRectangle(loaded_bitmap *Buffer, v2 vMin, v2 vMax, v4 Color, rectangle2i ClipRect, bool32 Even) +{ + TIMED_FUNCTION(); + + real32 R = Color.r; + real32 G = Color.g; + real32 B = Color.b; + real32 A = Color.a; + + rectangle2i FillRect; + FillRect.MinX = RoundReal32ToInt32(vMin.x); + FillRect.MinY = RoundReal32ToInt32(vMin.y); + FillRect.MaxX = RoundReal32ToInt32(vMax.x); + FillRect.MaxY = RoundReal32ToInt32(vMax.y); + + FillRect = Intersect(FillRect, ClipRect); + if(!Even == (FillRect.MinY & 1)) + { + FillRect.MinY += 1; + } + + uint32 Color32 = ((RoundReal32ToUInt32(A * 255.0f) << 24) | + (RoundReal32ToUInt32(R * 255.0f) << 16) | + (RoundReal32ToUInt32(G * 255.0f) << 8) | + (RoundReal32ToUInt32(B * 255.0f) << 0)); + + uint8 *Row = ((uint8 *)Buffer->Memory + + FillRect.MinX*BITMAP_BYTES_PER_PIXEL + + FillRect.MinY*Buffer->Pitch); + for(int Y = FillRect.MinY; + Y < FillRect.MaxY; + Y += 2) + { + uint32 *Pixel = (uint32 *)Row; + for(int X = FillRect.MinX; + X < FillRect.MaxX; + ++X) + { + *Pixel++ = Color32; + } + + Row += 2*Buffer->Pitch; + } +} + +struct bilinear_sample +{ + uint32 A, B, C, D; +}; +inline bilinear_sample +BilinearSample(loaded_bitmap *Texture, int32 X, int32 Y) +{ + bilinear_sample Result; + + uint8 *TexelPtr = ((uint8 *)Texture->Memory) + Y*Texture->Pitch + X*sizeof(uint32); + Result.A = *(uint32 *)(TexelPtr); + Result.B = *(uint32 *)(TexelPtr + sizeof(uint32)); + Result.C = *(uint32 *)(TexelPtr + Texture->Pitch); + Result.D = *(uint32 *)(TexelPtr + Texture->Pitch + sizeof(uint32)); + + return(Result); +} + +inline v4 +SRGBBilinearBlend(bilinear_sample TexelSample, real32 fX, real32 fY) +{ + v4 TexelA = Unpack4x8(TexelSample.A); + v4 TexelB = Unpack4x8(TexelSample.B); + v4 TexelC = Unpack4x8(TexelSample.C); + v4 TexelD = Unpack4x8(TexelSample.D); + + // NOTE(casey): Go from sRGB to "linear" brightness space + TexelA = SRGB255ToLinear1(TexelA); + TexelB = SRGB255ToLinear1(TexelB); + TexelC = SRGB255ToLinear1(TexelC); + TexelD = SRGB255ToLinear1(TexelD); + + v4 Result = Lerp(Lerp(TexelA, fX, TexelB), + fY, + Lerp(TexelC, fX, TexelD)); + + return(Result); +} + +inline v3 +SampleEnvironmentMap(v2 ScreenSpaceUV, v3 SampleDirection, real32 Roughness, environment_map *Map, + real32 DistanceFromMapInZ) +{ + /* NOTE(casey): + + ScreenSpaceUV tells us where the ray is being cast _from_ in + normalized screen coordinates. + + SampleDirection tells us what direction the cast is going - + it does not have to be normalized. + + Roughness says which LODs of Map we sample from. + + DistanceFromMapInZ says how far the map is from the sample point in Z, given + in meters. + */ + + // NOTE(casey): Pick which LOD to sample from + uint32 LODIndex = (uint32)(Roughness*(real32)(ArrayCount(Map->LOD) - 1) + 0.5f); + Assert(LODIndex < ArrayCount(Map->LOD)); + + loaded_bitmap *LOD = &Map->LOD[LODIndex]; + + // NOTE(casey): Compute the distance to the map and the scaling + // factor for meters-to-UVs + real32 UVsPerMeter = 0.1f; // TODO(casey): Parameterize this, and should be different for X and Y based on map! + real32 C = (UVsPerMeter*DistanceFromMapInZ) / SampleDirection.y; + v2 Offset = C * V2(SampleDirection.x, SampleDirection.z); + + // NOTE(casey): Find the intersection point + v2 UV = ScreenSpaceUV + Offset; + + // NOTE(casey): Clamp to the valid range + UV.x = Clamp01(UV.x); + UV.y = Clamp01(UV.y); + + // NOTE(casey): Bilinear sample + // TODO(casey): Formalize texture boundaries!!! + real32 tX = ((UV.x*(real32)(LOD->Width - 2))); + real32 tY = ((UV.y*(real32)(LOD->Height - 2))); + + int32 X = (int32)tX; + int32 Y = (int32)tY; + + real32 fX = tX - (real32)X; + real32 fY = tY - (real32)Y; + + Assert((X >= 0) && (X < LOD->Width)); + Assert((Y >= 0) && (Y < LOD->Height)); + + DEBUG_IF(Renderer_ShowLightingSamples) + { + // NOTE(casey): Turn this on to see where in the map you're sampling! + uint8 *TexelPtr = ((uint8 *)LOD->Memory) + Y*LOD->Pitch + X*sizeof(uint32); + *(uint32 *)TexelPtr = 0xFFFFFFFF; + } + + bilinear_sample Sample = BilinearSample(LOD, X, Y); + v3 Result = SRGBBilinearBlend(Sample, fX, fY).xyz; + + return(Result); +} + +internal void +DrawRectangleSlowly(loaded_bitmap *Buffer, v2 Origin, v2 XAxis, v2 YAxis, v4 Color, + loaded_bitmap *Texture, loaded_bitmap *NormalMap, + environment_map *Top, + environment_map *Middle, + environment_map *Bottom, + real32 PixelsToMeters) +{ + IGNORED_TIMED_FUNCTION(); + + // NOTE(casey): Premultiply color up front + Color.rgb *= Color.a; + + real32 XAxisLength = Length(XAxis); + real32 YAxisLength = Length(YAxis); + + v2 NxAxis = (YAxisLength / XAxisLength) * XAxis; + v2 NyAxis = (XAxisLength / YAxisLength) * YAxis; + + // NOTE(casey): NzScale could be a parameter if we want people to + // have control over the amount of scaling in the Z direction + // that the normals appear to have. + real32 NzScale = 0.5f*(XAxisLength + YAxisLength); + + real32 InvXAxisLengthSq = 1.0f / LengthSq(XAxis); + real32 InvYAxisLengthSq = 1.0f / LengthSq(YAxis); + + uint32 Color32 = ((RoundReal32ToUInt32(Color.a * 255.0f) << 24) | + (RoundReal32ToUInt32(Color.r * 255.0f) << 16) | + (RoundReal32ToUInt32(Color.g * 255.0f) << 8) | + (RoundReal32ToUInt32(Color.b * 255.0f) << 0)); + + int WidthMax = (Buffer->Width - 1); + int HeightMax = (Buffer->Height - 1); + + real32 InvWidthMax = 1.0f / (real32)WidthMax; + real32 InvHeightMax = 1.0f / (real32)HeightMax; + + // TODO(casey): This will need to be specified separately!!! + real32 OriginZ = 0.0f; + real32 OriginY = (Origin + 0.5f*XAxis + 0.5f*YAxis).y; + real32 FixedCastY = InvHeightMax*OriginY; + + int XMin = WidthMax; + int XMax = 0; + int YMin = HeightMax; + int YMax = 0; + + v2 P[4] = {Origin, Origin + XAxis, Origin + XAxis + YAxis, Origin + YAxis}; + for(int PIndex = 0; + PIndex < ArrayCount(P); + ++PIndex) + { + v2 TestP = P[PIndex]; + int FloorX = FloorReal32ToInt32(TestP.x); + int CeilX = CeilReal32ToInt32(TestP.x); + int FloorY = FloorReal32ToInt32(TestP.y); + int CeilY = CeilReal32ToInt32(TestP.y); + + if(XMin > FloorX) {XMin = FloorX;} + if(YMin > FloorY) {YMin = FloorY;} + if(XMax < CeilX) {XMax = CeilX;} + if(YMax < CeilY) {YMax = CeilY;} + } + + if(XMin < 0) {XMin = 0;} + if(YMin < 0) {YMin = 0;} + if(XMax > WidthMax) {XMax = WidthMax;} + if(YMax > HeightMax) {YMax = HeightMax;} + + uint8 *Row = ((uint8 *)Buffer->Memory + + XMin*BITMAP_BYTES_PER_PIXEL + + YMin*Buffer->Pitch); + + TIMED_BLOCK(PixelFill, (XMax - XMin + 1)*(YMax - YMin + 1)); + for(int Y = YMin; + Y <= YMax; + ++Y) + { + uint32 *Pixel = (uint32 *)Row; + for(int X = XMin; + X <= XMax; + ++X) + { +#if 1 + v2 PixelP = V2i(X, Y); + v2 d = PixelP - Origin; + + // TODO(casey): PerpInner + // TODO(casey): Simpler origin + real32 Edge0 = Inner(d, -Perp(XAxis)); + real32 Edge1 = Inner(d - XAxis, -Perp(YAxis)); + real32 Edge2 = Inner(d - XAxis - YAxis, Perp(XAxis)); + real32 Edge3 = Inner(d - YAxis, Perp(YAxis)); + + if((Edge0 < 0) && + (Edge1 < 0) && + (Edge2 < 0) && + (Edge3 < 0)) + { +#if 1 + v2 ScreenSpaceUV = {InvWidthMax*(real32)X, FixedCastY}; + real32 ZDiff = PixelsToMeters*((real32)Y - OriginY); +#else + v2 ScreenSpaceUV = {InvWidthMax*(real32)X, InvHeightMax*(real32)Y}; + real32 ZDiff = 0.0f; +#endif + + + real32 U = InvXAxisLengthSq*Inner(d, XAxis); + real32 V = InvYAxisLengthSq*Inner(d, YAxis); + +#if 0 + // TODO(casey): SSE clamping. + Assert((U >= 0.0f) && (U <= 1.0f)); + Assert((V >= 0.0f) && (V <= 1.0f)); +#endif + + // TODO(casey): Formalize texture boundaries!!! + real32 tX = ((U*(real32)(Texture->Width - 2))); + real32 tY = ((V*(real32)(Texture->Height - 2))); + + int32 X = (int32)tX; + int32 Y = (int32)tY; + + real32 fX = tX - (real32)X; + real32 fY = tY - (real32)Y; + + Assert((X >= 0) && (X < Texture->Width)); + Assert((Y >= 0) && (Y < Texture->Height)); + + bilinear_sample TexelSample = BilinearSample(Texture, X, Y); + v4 Texel = SRGBBilinearBlend(TexelSample, fX, fY); + +#if 0 + if(NormalMap) + { + bilinear_sample NormalSample = BilinearSample(NormalMap, X, Y); + + v4 NormalA = Unpack4x8(NormalSample.A); + v4 NormalB = Unpack4x8(NormalSample.B); + v4 NormalC = Unpack4x8(NormalSample.C); + v4 NormalD = Unpack4x8(NormalSample.D); + + v4 Normal = Lerp(Lerp(NormalA, fX, NormalB), + fY, + Lerp(NormalC, fX, NormalD)); + + Normal = UnscaleAndBiasNormal(Normal); + // TODO(casey): Do we really need to do this? + + Normal.xy = Normal.x*NxAxis + Normal.y*NyAxis; + Normal.z *= NzScale; + Normal.xyz = Normalize(Normal.xyz); + + // NOTE(casey): The eye vector is always assumed to be [0, 0, 1] + // This is just the simplified version of the reflection -e + 2e^T N N + v3 BounceDirection = 2.0f*Normal.z*Normal.xyz; + BounceDirection.z -= 1.0f; + + // TODO(casey): Eventually we need to support two mappings, + // one for top-down view (which we don't do now) and one + // for sideways, which is what's happening here. + BounceDirection.z = -BounceDirection.z; + + environment_map *FarMap = 0; + real32 Pz = OriginZ + ZDiff; + real32 MapZ = 2.0f; + real32 tEnvMap = BounceDirection.y; + real32 tFarMap = 0.0f; + if(tEnvMap < -0.5f) + { + // TODO(casey): This path seems PARTICULARLY broken! + FarMap = Bottom; + tFarMap = -1.0f - 2.0f*tEnvMap; + } + else if(tEnvMap > 0.5f) + { + FarMap = Top; + tFarMap = 2.0f*(tEnvMap - 0.5f); + } + + tFarMap *= tFarMap; + tFarMap *= tFarMap; + + v3 LightColor = {0, 0, 0}; // TODO(casey): How do we sample from the middle map??? + if(FarMap) + { + real32 DistanceFromMapInZ = FarMap->Pz - Pz; + v3 FarMapColor = SampleEnvironmentMap(ScreenSpaceUV, BounceDirection, Normal.w, FarMap, + DistanceFromMapInZ); + LightColor = Lerp(LightColor, tFarMap, FarMapColor); + } + + // TODO(casey): ? Actually do a lighting model computation here + Texel.rgb = Texel.rgb + Texel.a*LightColor; + +#if 0 + // NOTE(casey): Draws the bounce direction + Texel.rgb = V3(0.5f, 0.5f, 0.5f) + 0.5f*BounceDirection; + Texel.rgb *= Texel.a; +#endif + } +#endif + + Texel = Hadamard(Texel, Color); + Texel.r = Clamp01(Texel.r); + Texel.g = Clamp01(Texel.g); + Texel.b = Clamp01(Texel.b); + + v4 Dest = {(real32)((*Pixel >> 16) & 0xFF), + (real32)((*Pixel >> 8) & 0xFF), + (real32)((*Pixel >> 0) & 0xFF), + (real32)((*Pixel >> 24) & 0xFF)}; + + // NOTE(casey): Go from sRGB to "linear" brightness space + Dest = SRGB255ToLinear1(Dest); + + v4 Blended = (1.0f-Texel.a)*Dest + Texel; + + // NOTE(casey): Go from "linear" brightness space to sRGB + v4 Blended255 = Linear1ToSRGB255(Blended); + + *Pixel = (((uint32)(Blended255.a + 0.5f) << 24) | + ((uint32)(Blended255.r + 0.5f) << 16) | + ((uint32)(Blended255.g + 0.5f) << 8) | + ((uint32)(Blended255.b + 0.5f) << 0)); + } +#else + *Pixel = Color32; +#endif + + ++Pixel; + } + + Row += Buffer->Pitch; + } +} + +internal void +DrawBitmap(loaded_bitmap *Buffer, loaded_bitmap *Bitmap, + real32 RealX, real32 RealY, real32 CAlpha = 1.0f) +{ + IGNORED_TIMED_FUNCTION(); + + int32 MinX = RoundReal32ToInt32(RealX); + int32 MinY = RoundReal32ToInt32(RealY); + int32 MaxX = MinX + Bitmap->Width; + int32 MaxY = MinY + Bitmap->Height; + + int32 SourceOffsetX = 0; + if(MinX < 0) + { + SourceOffsetX = -MinX; + MinX = 0; + } + + int32 SourceOffsetY = 0; + if(MinY < 0) + { + SourceOffsetY = -MinY; + MinY = 0; + } + + if(MaxX > Buffer->Width) + { + MaxX = Buffer->Width; + } + + if(MaxY > Buffer->Height) + { + MaxY = Buffer->Height; + } + + uint8 *SourceRow = (uint8 *)Bitmap->Memory + SourceOffsetY*Bitmap->Pitch + BITMAP_BYTES_PER_PIXEL*SourceOffsetX; + uint8 *DestRow = ((uint8 *)Buffer->Memory + + MinX*BITMAP_BYTES_PER_PIXEL + + MinY*Buffer->Pitch); + for(int Y = MinY; + Y < MaxY; + ++Y) + { + uint32 *Dest = (uint32 *)DestRow; + uint32 *Source = (uint32 *)SourceRow; + for(int X = MinX; + X < MaxX; + ++X) + { + v4 Texel = {(real32)((*Source >> 16) & 0xFF), + (real32)((*Source >> 8) & 0xFF), + (real32)((*Source >> 0) & 0xFF), + (real32)((*Source >> 24) & 0xFF)}; + + Texel = SRGB255ToLinear1(Texel); + + Texel *= CAlpha; + + v4 D = {(real32)((*Dest >> 16) & 0xFF), + (real32)((*Dest >> 8) & 0xFF), + (real32)((*Dest >> 0) & 0xFF), + (real32)((*Dest >> 24) & 0xFF)}; + + D = SRGB255ToLinear1(D); + + v4 Result = (1.0f-Texel.a)*D + Texel; + + Result = Linear1ToSRGB255(Result); + + *Dest = (((uint32)(Result.a + 0.5f) << 24) | + ((uint32)(Result.r + 0.5f) << 16) | + ((uint32)(Result.g + 0.5f) << 8) | + ((uint32)(Result.b + 0.5f) << 0)); + + ++Dest; + ++Source; + } + + DestRow += Buffer->Pitch; + SourceRow += Bitmap->Pitch; + } +} + +internal void +ChangeSaturation(loaded_bitmap *Buffer, real32 Level) +{ + IGNORED_TIMED_FUNCTION(); + + uint8 *DestRow = (uint8 *)Buffer->Memory; + for(int Y = 0; + Y < Buffer->Height; + ++Y) + { + uint32 *Dest = (uint32 *)DestRow; + for(int X = 0; + X < Buffer->Width; + ++X) + { + v4 D = {(real32)((*Dest >> 16) & 0xFF), + (real32)((*Dest >> 8) & 0xFF), + (real32)((*Dest >> 0) & 0xFF), + (real32)((*Dest >> 24) & 0xFF)}; + + D = SRGB255ToLinear1(D); + + real32 Avg = (1.0f / 3.0f) * (D.r + D.g + D.b); + v3 Delta = V3(D.r - Avg, D.g - Avg, D.b - Avg); + + v4 Result = V4(V3(Avg, Avg, Avg) + Level*Delta, D.a); + + Result = Linear1ToSRGB255(Result); + + *Dest = (((uint32)(Result.a + 0.5f) << 24) | + ((uint32)(Result.r + 0.5f) << 16) | + ((uint32)(Result.g + 0.5f) << 8) | + ((uint32)(Result.b + 0.5f) << 0)); + + ++Dest; + } + + DestRow += Buffer->Pitch; + } +} + +internal void +DrawMatte(loaded_bitmap *Buffer, loaded_bitmap *Bitmap, + real32 RealX, real32 RealY, real32 CAlpha = 1.0f) +{ + IGNORED_TIMED_FUNCTION(); + + int32 MinX = RoundReal32ToInt32(RealX); + int32 MinY = RoundReal32ToInt32(RealY); + int32 MaxX = MinX + Bitmap->Width; + int32 MaxY = MinY + Bitmap->Height; + + int32 SourceOffsetX = 0; + if(MinX < 0) + { + SourceOffsetX = -MinX; + MinX = 0; + } + + int32 SourceOffsetY = 0; + if(MinY < 0) + { + SourceOffsetY = -MinY; + MinY = 0; + } + + if(MaxX > Buffer->Width) + { + MaxX = Buffer->Width; + } + + if(MaxY > Buffer->Height) + { + MaxY = Buffer->Height; + } + + uint8 *SourceRow = (uint8 *)Bitmap->Memory + SourceOffsetY*Bitmap->Pitch + BITMAP_BYTES_PER_PIXEL*SourceOffsetX; + uint8 *DestRow = ((uint8 *)Buffer->Memory + + MinX*BITMAP_BYTES_PER_PIXEL + + MinY*Buffer->Pitch); + for(int Y = MinY; + Y < MaxY; + ++Y) + { + uint32 *Dest = (uint32 *)DestRow; + uint32 *Source = (uint32 *)SourceRow; + for(int X = MinX; + X < MaxX; + ++X) + { + real32 SA = (real32)((*Source >> 24) & 0xFF); + real32 RSA = (SA / 255.0f) * CAlpha; + real32 SR = CAlpha*(real32)((*Source >> 16) & 0xFF); + real32 SG = CAlpha*(real32)((*Source >> 8) & 0xFF); + real32 SB = CAlpha*(real32)((*Source >> 0) & 0xFF); + + real32 DA = (real32)((*Dest >> 24) & 0xFF); + real32 DR = (real32)((*Dest >> 16) & 0xFF); + real32 DG = (real32)((*Dest >> 8) & 0xFF); + real32 DB = (real32)((*Dest >> 0) & 0xFF); + real32 RDA = (DA / 255.0f); + + real32 InvRSA = (1.0f-RSA); + // TODO(casey): Check this for math errors +// real32 A = 255.0f*(RSA + RDA - RSA*RDA); + real32 A = InvRSA*DA; + real32 R = InvRSA*DR; + real32 G = InvRSA*DG; + real32 B = InvRSA*DB; + + *Dest = (((uint32)(A + 0.5f) << 24) | + ((uint32)(R + 0.5f) << 16) | + ((uint32)(G + 0.5f) << 8) | + ((uint32)(B + 0.5f) << 0)); + + ++Dest; + ++Source; + } + + DestRow += Buffer->Pitch; + SourceRow += Bitmap->Pitch; + } +} + +internal void +RenderGroupToOutput(render_group *RenderGroup, loaded_bitmap *OutputTarget, + rectangle2i ClipRect, bool Even) +{ + IGNORED_TIMED_FUNCTION(); + + real32 NullPixelsToMeters = 1.0f; + + for(uint32 BaseAddress = 0; + BaseAddress < RenderGroup->PushBufferSize; + ) + { + render_group_entry_header *Header = (render_group_entry_header *) + (RenderGroup->PushBufferBase + BaseAddress); + BaseAddress += sizeof(*Header); + + void *Data = (uint8 *)Header + sizeof(*Header); + switch(Header->Type) + { + case RenderGroupEntryType_render_entry_clear: + { + render_entry_clear *Entry = (render_entry_clear *)Data; + + DrawRectangle(OutputTarget, V2(0.0f, 0.0f), + V2((real32)OutputTarget->Width, (real32)OutputTarget->Height), + Entry->Color, ClipRect, Even); + + BaseAddress += sizeof(*Entry); + } break; + + case RenderGroupEntryType_render_entry_bitmap: + { + render_entry_bitmap *Entry = (render_entry_bitmap *)Data; + Assert(Entry->Bitmap); + +#if 0 +// DrawBitmap(OutputTarget, Entry->Bitmap, P.x, P.y, Entry->Color.a); + DrawRectangleSlowly(OutputTarget, Entry->P, + V2(Entry->Size.x, 0), + V2(0, Entry->Size.y), Entry->Color, + Entry->Bitmap, 0, 0, 0, 0, NullPixelsToMeters); +#else + v2 XAxis = {1, 0}; + v2 YAxis = {0, 1}; + DrawRectangleQuickly(OutputTarget, Entry->P, + Entry->Size.x*XAxis, + Entry->Size.y*YAxis, Entry->Color, + Entry->Bitmap, NullPixelsToMeters, ClipRect, Even); +#endif + + BaseAddress += sizeof(*Entry); + } break; + + case RenderGroupEntryType_render_entry_rectangle: + { + render_entry_rectangle *Entry = (render_entry_rectangle *)Data; + DrawRectangle(OutputTarget, Entry->P, Entry->P + Entry->Dim, Entry->Color, ClipRect, Even); + + BaseAddress += sizeof(*Entry); + } break; + + case RenderGroupEntryType_render_entry_coordinate_system: + { + render_entry_coordinate_system *Entry = (render_entry_coordinate_system *)Data; + +#if 0 + v2 vMax = (Entry->Origin + Entry->XAxis + Entry->YAxis); + DrawRectangleSlowly(OutputTarget, + Entry->Origin, + Entry->XAxis, + Entry->YAxis, + Entry->Color, + Entry->Texture, + Entry->NormalMap, + Entry->Top, Entry->Middle, Entry->Bottom, + PixelsToMeters); + + v4 Color = {1, 1, 0, 1}; + v2 Dim = {2, 2}; + v2 P = Entry->Origin; + DrawRectangle(OutputTarget, P - Dim, P + Dim, Color); + + P = Entry->Origin + Entry->XAxis; + DrawRectangle(OutputTarget, P - Dim, P + Dim, Color); + + P = Entry->Origin + Entry->YAxis; + DrawRectangle(OutputTarget, P - Dim, P + Dim, Color); + + DrawRectangle(OutputTarget, vMax - Dim, vMax + Dim, Color); + +#if 0 + for(uint32 PIndex = 0; + PIndex < ArrayCount(Entry->Points); + ++PIndex) + { + v2 P = Entry->Points[PIndex]; + P = Entry->Origin + P.x*Entry->XAxis + P.y*Entry->YAxis; + DrawRectangle(OutputTarget, P - Dim, P + Dim, Entry->Color.r, Entry->Color.g, Entry->Color.b); + } +#endif +#endif + + BaseAddress += sizeof(*Entry); + } break; + + InvalidDefaultCase; + } + } +} + +struct tile_render_work +{ + render_group *RenderGroup; + loaded_bitmap *OutputTarget; + rectangle2i ClipRect; +}; + +internal PLATFORM_WORK_QUEUE_CALLBACK(DoTiledRenderWork) +{ + TIMED_FUNCTION(); + + tile_render_work *Work = (tile_render_work *)Data; + + RenderGroupToOutput(Work->RenderGroup, Work->OutputTarget, Work->ClipRect, true); + RenderGroupToOutput(Work->RenderGroup, Work->OutputTarget, Work->ClipRect, false); +} + +internal void +RenderGroupToOutput(render_group *RenderGroup, loaded_bitmap *OutputTarget) +{ + TIMED_FUNCTION(); + + Assert(RenderGroup->InsideRender); + + Assert(((uintptr)OutputTarget->Memory & 15) == 0); + + rectangle2i ClipRect; + ClipRect.MinX = 0; + ClipRect.MaxX = OutputTarget->Width; + ClipRect.MinY = 0; + ClipRect.MaxY = OutputTarget->Height; + + tile_render_work Work; + Work.RenderGroup = RenderGroup; + Work.OutputTarget = OutputTarget; + Work.ClipRect = ClipRect; + + DoTiledRenderWork(0, &Work); +} + +internal void +TiledRenderGroupToOutput(platform_work_queue *RenderQueue, + render_group *RenderGroup, loaded_bitmap *OutputTarget) +{ + TIMED_FUNCTION(); + + Assert(RenderGroup->InsideRender); + + /* + TODO(casey): + + - Make sure that tiles are all cache-aligned + - Can we get hyperthreads synced so they do interleaved lines? + - How big should the tiles be for performance? + - Actually ballpark the memory bandwidth for our DrawRectangleQuickly + - Re-test some of our instruction choices + */ + + int const TileCountX = 4; + int const TileCountY = 4; + tile_render_work WorkArray[TileCountX*TileCountY]; + + Assert(((uintptr)OutputTarget->Memory & 15) == 0); + int TileWidth = OutputTarget->Width / TileCountX; + int TileHeight = OutputTarget->Height / TileCountY; + + TileWidth = ((TileWidth + 3) / 4) * 4; + + int WorkCount = 0; + for(int TileY = 0; + TileY < TileCountY; + ++TileY) + { + for(int TileX = 0; + TileX < TileCountX; + ++TileX) + { + tile_render_work *Work = WorkArray + WorkCount++; + + rectangle2i ClipRect; + ClipRect.MinX = TileX*TileWidth; + ClipRect.MaxX = ClipRect.MinX + TileWidth; + ClipRect.MinY = TileY*TileHeight; + ClipRect.MaxY = ClipRect.MinY + TileHeight; + + if(TileX == (TileCountX - 1)) + { + ClipRect.MaxX = OutputTarget->Width; + } + if(TileY == (TileCountY - 1)) + { + ClipRect.MaxY = OutputTarget->Height; + } + + Work->RenderGroup = RenderGroup; + Work->OutputTarget = OutputTarget; + Work->ClipRect = ClipRect; +#if 1 + // NOTE(casey): This is the multi-threaded path + Platform.AddEntry(RenderQueue, DoTiledRenderWork, Work); +#else + // NOTE(casey): This is the single-threaded path + DoTiledRenderWork(RenderQueue, Work); +#endif + } + } + + Platform.CompleteAllWork(RenderQueue); +} + +internal render_group * +AllocateRenderGroup(game_assets *Assets, memory_arena *Arena, uint32 MaxPushBufferSize, + b32 RendersInBackground) +{ + render_group *Result = PushStruct(Arena, render_group); + + if(MaxPushBufferSize == 0) + { + // TODO(casey): Safe cast from memory_uint to uint32? + MaxPushBufferSize = (uint32)GetArenaSizeRemaining(Arena); + } + Result->PushBufferBase = (uint8 *)PushSize(Arena, MaxPushBufferSize); + + Result->MaxPushBufferSize = MaxPushBufferSize; + Result->PushBufferSize = 0; + + Result->Assets = Assets; + Result->GlobalAlpha = 1.0f; + + Result->GenerationID = 0; + + // NOTE(casey): Default transform + Result->Transform.OffsetP = V3(0.0f, 0.0f, 0.0f); + Result->Transform.Scale = 1.0f; + + Result->MissingResourceCount = 0; + Result->RendersInBackground = RendersInBackground; + + Result->InsideRender = false; + + return(Result); +} + +internal void +BeginRender(render_group *Group) +{ + IGNORED_TIMED_FUNCTION(); + + if(Group) + { + Assert(!Group->InsideRender); + Group->InsideRender = true; + + Group->GenerationID = BeginGeneration(Group->Assets); + } +} + +internal void +EndRender(render_group *Group) +{ + IGNORED_TIMED_FUNCTION(); + + if(Group) + { + Assert(Group->InsideRender); + Group->InsideRender = false; + + EndGeneration(Group->Assets, Group->GenerationID); + Group->GenerationID = 0; + Group->PushBufferSize = 0; + } +} + + +inline void +Perspective(render_group *RenderGroup, int32 PixelWidth, int32 PixelHeight, + real32 MetersToPixels, real32 FocalLength, real32 DistanceAboveTarget) +{ + // TODO(casey): Need to adjust this based on buffer size + real32 PixelsToMeters = SafeRatio1(1.0f, MetersToPixels); + + RenderGroup->MonitorHalfDimInMeters = {0.5f*PixelWidth*PixelsToMeters, + 0.5f*PixelHeight*PixelsToMeters}; + + RenderGroup->Transform.MetersToPixels = MetersToPixels; + RenderGroup->Transform.FocalLength = FocalLength; // NOTE(casey): Meters the person is sitting from their monitor + RenderGroup->Transform.DistanceAboveTarget = DistanceAboveTarget; + RenderGroup->Transform.ScreenCenter = V2(0.5f*PixelWidth, 0.5f*PixelHeight); + + RenderGroup->Transform.Orthographic = false; + RenderGroup->Transform.OffsetP = V3(0, 0, 0); + RenderGroup->Transform.Scale = 1.0f; +} + +inline void +Orthographic(render_group *RenderGroup, int32 PixelWidth, int32 PixelHeight, real32 MetersToPixels) +{ + real32 PixelsToMeters = SafeRatio1(1.0f, MetersToPixels); + RenderGroup->MonitorHalfDimInMeters = {0.5f*PixelWidth*PixelsToMeters, + 0.5f*PixelHeight*PixelsToMeters}; + + RenderGroup->Transform.MetersToPixels = MetersToPixels; + RenderGroup->Transform.FocalLength = 1.0f; // NOTE(casey): Meters the person is sitting from their monitor + RenderGroup->Transform.DistanceAboveTarget = 1.0f; + RenderGroup->Transform.ScreenCenter = V2(0.5f*PixelWidth, 0.5f*PixelHeight); + + RenderGroup->Transform.Orthographic = true; + RenderGroup->Transform.OffsetP = V3(0, 0, 0); + RenderGroup->Transform.Scale = 1.0f; +} + +inline entity_basis_p_result GetRenderEntityBasisP(render_transform *Transform, v3 OriginalP) +{ + IGNORED_TIMED_FUNCTION(); + + entity_basis_p_result Result = {}; + + v3 P = V3(OriginalP.xy, 0.0f) + Transform->OffsetP; + + if(Transform->Orthographic) + { + Result.P = Transform->ScreenCenter + Transform->MetersToPixels*P.xy; + Result.Scale = Transform->MetersToPixels; + Result.Valid = true; + } + else + { + real32 OffsetZ = 0.0f; + + real32 DistanceAboveTarget = Transform->DistanceAboveTarget; + + DEBUG_IF(Renderer_Camera_UseDebug) + { + DEBUG_VARIABLE(r32, Renderer_Camera, DebugDistance); + + DistanceAboveTarget += DebugDistance; + } + + real32 DistanceToPZ = (DistanceAboveTarget - P.z); + real32 NearClipPlane = 0.2f; + + v3 RawXY = V3(P.xy, 1.0f); + + if(DistanceToPZ > NearClipPlane) + { + v3 ProjectedXY = (1.0f / DistanceToPZ) * Transform->FocalLength*RawXY; + Result.Scale = Transform->MetersToPixels*ProjectedXY.z; + Result.P = Transform->ScreenCenter + Transform->MetersToPixels*ProjectedXY.xy + V2(0.0f, Result.Scale*OffsetZ); + Result.Valid = true; + } + } + + return(Result); +} + +#define PushRenderElement(Group, type) (type *)PushRenderElement_(Group, sizeof(type), RenderGroupEntryType_##type) +inline void * +PushRenderElement_(render_group *Group, uint32 Size, render_group_entry_type Type) +{ + IGNORED_TIMED_FUNCTION(); + + Assert(Group->InsideRender); + + void *Result = 0; + + Size += sizeof(render_group_entry_header); + + if((Group->PushBufferSize + Size) < Group->MaxPushBufferSize) + { + render_group_entry_header *Header = (render_group_entry_header *)(Group->PushBufferBase + Group->PushBufferSize); + Header->Type = Type; + Result = (uint8 *)Header + sizeof(*Header); + Group->PushBufferSize += Size; + } + else + { + InvalidCodePath; + } + + return(Result); +} + +inline used_bitmap_dim +GetBitmapDim(render_group *Group, loaded_bitmap *Bitmap, real32 Height, v3 Offset, r32 CAlign) +{ + used_bitmap_dim Dim; + + Dim.Size = V2(Height*Bitmap->WidthOverHeight, Height); + Dim.Align = CAlign*Hadamard(Bitmap->AlignPercentage, Dim.Size); + Dim.P = Offset - V3(Dim.Align, 0); + Dim.Basis = GetRenderEntityBasisP(&Group->Transform, Dim.P); + + return(Dim); +} + +inline void +PushBitmap(render_group *Group, loaded_bitmap *Bitmap, real32 Height, v3 Offset, v4 Color = V4(1, 1, 1, 1), r32 CAlign = 1.0f) +{ + used_bitmap_dim Dim = GetBitmapDim(Group, Bitmap, Height, Offset, CAlign); + if(Dim.Basis.Valid) + { + render_entry_bitmap *Entry = PushRenderElement(Group, render_entry_bitmap); + if(Entry) + { + Entry->Bitmap = Bitmap; + Entry->P = Dim.Basis.P; + Entry->Color = Group->GlobalAlpha*Color; + Entry->Size = Dim.Basis.Scale*Dim.Size; + } + } +} + +inline void +PushBitmap(render_group *Group, bitmap_id ID, real32 Height, v3 Offset, v4 Color = V4(1, 1, 1, 1), r32 CAlign = 1.0f) +{ + loaded_bitmap *Bitmap = GetBitmap(Group->Assets, ID, Group->GenerationID); + if(Group->RendersInBackground && !Bitmap) + { + LoadBitmap(Group->Assets, ID, true); + Bitmap = GetBitmap(Group->Assets, ID, Group->GenerationID); + } + + if(Bitmap) + { + PushBitmap(Group, Bitmap, Height, Offset, Color, CAlign); + } + else + { + Assert(!Group->RendersInBackground); + LoadBitmap(Group->Assets, ID, false); + ++Group->MissingResourceCount; + } +} + +inline loaded_font * +PushFont(render_group *Group, font_id ID) +{ + loaded_font *Font = GetFont(Group->Assets, ID, Group->GenerationID); + if(Font) + { + // NOTE(casey): Nothing to do + } + else + { + Assert(!Group->RendersInBackground); + LoadFont(Group->Assets, ID, false); + ++Group->MissingResourceCount; + } + + return(Font); +} + +inline void +PushRect(render_group *Group, v3 Offset, v2 Dim, v4 Color = V4(1, 1, 1, 1)) +{ + v3 P = (Offset - V3(0.5f*Dim, 0)); + entity_basis_p_result Basis = GetRenderEntityBasisP(&Group->Transform, P); + if(Basis.Valid) + { + render_entry_rectangle *Rect = PushRenderElement(Group, render_entry_rectangle); + if(Rect) + { + Rect->P = Basis.P; + Rect->Color = Color; + Rect->Dim = Basis.Scale*Dim; + } + } +} + +inline void +PushRect(render_group *Group, rectangle2 Rectangle, r32 Z, v4 Color = V4(1, 1, 1, 1)) +{ + PushRect(Group, V3(GetCenter(Rectangle), Z), GetDim(Rectangle), Color); +} + +inline void +PushRectOutline(render_group *Group, v3 Offset, v2 Dim, v4 Color = V4(1, 1, 1, 1), real32 Thickness = 0.1f) +{ + // NOTE(casey): Top and bottom + PushRect(Group, Offset - V3(0, 0.5f*Dim.y, 0), V2(Dim.x, Thickness), Color); + PushRect(Group, Offset + V3(0, 0.5f*Dim.y, 0), V2(Dim.x, Thickness), Color); + + // NOTE(casey): Left and right + PushRect(Group, Offset - V3(0.5f*Dim.x, 0, 0), V2(Thickness, Dim.y), Color); + PushRect(Group, Offset + V3(0.5f*Dim.x, 0, 0), V2(Thickness, Dim.y), Color); +} + +inline void +Clear(render_group *Group, v4 Color) +{ + render_entry_clear *Entry = PushRenderElement(Group, render_entry_clear); + if(Entry) + { + Entry->Color = Color; + } +} + +inline void +CoordinateSystem(render_group *Group, v2 Origin, v2 XAxis, v2 YAxis, v4 Color, + loaded_bitmap *Texture, loaded_bitmap *NormalMap, + environment_map *Top, environment_map *Middle, environment_map *Bottom) +{ +#if 0 + entity_basis_p_result Basis = GetRenderEntityBasisP(RenderGroup, &Entry->EntityBasis, ScreenDim); + if(Basis.Valid) + { + render_entry_coordinate_system *Entry = PushRenderElement(Group, render_entry_coordinate_system); + if(Entry) + { + Entry->Origin = Origin; + Entry->XAxis = XAxis; + Entry->YAxis = YAxis; + Entry->Color = Color; + Entry->Texture = Texture; + Entry->NormalMap = NormalMap; + Entry->Top = Top; + Entry->Middle = Middle; + Entry->Bottom = Bottom; + } + } +#endif +} + +inline v3 +Unproject(render_group *Group, v2 PixelsXY) +{ + render_transform *Transform = &Group->Transform; + + v2 UnprojectedXY; + if(Transform->Orthographic) + { + UnprojectedXY = (1.0f / Transform->MetersToPixels)*(PixelsXY - Transform->ScreenCenter); + } + else + { + v2 A = (PixelsXY - Transform->ScreenCenter) * (1.0f / Transform->MetersToPixels); + UnprojectedXY = ((Transform->DistanceAboveTarget - Transform->OffsetP.z)/Transform->FocalLength) * A; + } + + v3 Result = V3(UnprojectedXY, Transform->OffsetP.z); + Result -= Transform->OffsetP; + + return(Result); +} + +inline v2 +UnprojectOld(render_group *Group, v2 ProjectedXY, real32 AtDistanceFromCamera) +{ + v2 WorldXY = (AtDistanceFromCamera / Group->Transform.FocalLength)*ProjectedXY; + return(WorldXY); +} + +inline rectangle2 +GetCameraRectangleAtDistance(render_group *Group, real32 DistanceFromCamera) +{ + v2 RawXY = UnprojectOld(Group, Group->MonitorHalfDimInMeters, DistanceFromCamera); + + rectangle2 Result = RectCenterHalfDim(V2(0, 0), RawXY); + + return(Result); +} + +inline rectangle2 +GetCameraRectangleAtTarget(render_group *Group) +{ + rectangle2 Result = GetCameraRectangleAtDistance(Group, Group->Transform.DistanceAboveTarget); + + return(Result); +} + +inline bool32 +AllResourcesPresent(render_group *Group) +{ + bool32 Result = (Group->MissingResourceCount == 0); + + return(Result); +} diff --git a/test_data/lots_of_files/handmade_render_group.h b/test_data/lots_of_files/handmade_render_group.h new file mode 100644 index 0000000..de74d44 --- /dev/null +++ b/test_data/lots_of_files/handmade_render_group.h @@ -0,0 +1,163 @@ +#if !defined(HANDMADE_RENDER_GROUP_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +/* NOTE(casey): + + 1) Everywhere outside the renderer, Y _always_ goes upward, X to the right. + + 2) All bitmaps including the render target are assumed to be bottom-up + (meaning that the first row pointer points to the bottom-most row + when viewed on screen). + + 3) It is mandatory that all inputs to the renderer are in world + coordinates ("meters"), NOT pixels. If for some reason something + absolutely has to be specified in pixels, that will be explicitly + marked in the API, but this should occur exceedingly sparingly. + + 4) Z is a special coordinate because it is broken up into discrete slices, + and the renderer actually understands these slices. Z slices are what + control the _scaling_ of things, whereas Z offsets inside a slice are + what control Y offsetting. + + 5) All color values specified to the renderer as V4's are in + NON-premulitplied alpha. + +*/ + +struct loaded_bitmap +{ + void *Memory; + v2 AlignPercentage; + r32 WidthOverHeight; + s32 Width; + s32 Height; + // TODO(casey): Get rid of pitch! + s32 Pitch; +}; + +struct environment_map +{ + loaded_bitmap LOD[4]; + real32 Pz; +}; + +enum render_group_entry_type +{ + RenderGroupEntryType_render_entry_clear, + RenderGroupEntryType_render_entry_bitmap, + RenderGroupEntryType_render_entry_rectangle, + RenderGroupEntryType_render_entry_coordinate_system, +}; +struct render_group_entry_header +{ + render_group_entry_type Type; +}; + +struct render_entry_clear +{ + v4 Color; +}; + +struct render_entry_saturation +{ + real32 Level; +}; + +struct render_entry_bitmap +{ + loaded_bitmap *Bitmap; + + v4 Color; + v2 P; + v2 Size; +}; + +struct render_entry_rectangle +{ + v4 Color; + v2 P; + v2 Dim; +}; + +// NOTE(casey): This is only for test: +// { +struct render_entry_coordinate_system +{ + v2 Origin; + v2 XAxis; + v2 YAxis; + v4 Color; + loaded_bitmap *Texture; + loaded_bitmap *NormalMap; + +// real32 PixelsToMeters; // TODO(casey): Need to store this for lighting! + + environment_map *Top; + environment_map *Middle; + environment_map *Bottom; +}; +// } + +struct render_transform +{ + bool32 Orthographic; + + // NOTE(casey): Camera parameters + real32 MetersToPixels; // NOTE(casey): This translates meters _on the monitor_ into pixels _on the monitor_ + v2 ScreenCenter; + + real32 FocalLength; + real32 DistanceAboveTarget; + + v3 OffsetP; + real32 Scale; +}; + +struct render_group +{ + struct game_assets *Assets; + real32 GlobalAlpha; + + u32 GenerationID; + + v2 MonitorHalfDimInMeters; + + render_transform Transform; + + uint32 MaxPushBufferSize; + uint32 PushBufferSize; + uint8 *PushBufferBase; + + uint32 MissingResourceCount; + b32 RendersInBackground; + + b32 InsideRender; +}; + +struct entity_basis_p_result +{ + v2 P; + real32 Scale; + bool32 Valid; +}; + +struct used_bitmap_dim +{ + entity_basis_p_result Basis; + v2 Size; + v2 Align; + v3 P; +}; + +void DrawRectangleQuickly(loaded_bitmap *Buffer, v2 Origin, v2 XAxis, v2 YAxis, v4 Color, + loaded_bitmap *Texture, real32 PixelsToMeters, + rectangle2i ClipRect, bool32 Even); + +#define HANDMADE_RENDER_GROUP_H +#endif diff --git a/test_data/lots_of_files/handmade_sim_region.cpp b/test_data/lots_of_files/handmade_sim_region.cpp new file mode 100644 index 0000000..ae8d8a6 --- /dev/null +++ b/test_data/lots_of_files/handmade_sim_region.cpp @@ -0,0 +1,791 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +internal sim_entity_hash * +GetHashFromStorageIndex(sim_region *SimRegion, uint32 StorageIndex) +{ + Assert(StorageIndex); + + sim_entity_hash *Result = 0; + + uint32 HashValue = StorageIndex; + for(uint32 Offset = 0; + Offset < ArrayCount(SimRegion->Hash); + ++Offset) + { + uint32 HashMask = (ArrayCount(SimRegion->Hash) - 1); + uint32 HashIndex = ((HashValue + Offset) & HashMask); + sim_entity_hash *Entry = SimRegion->Hash + HashIndex; + if((Entry->Index == 0) || (Entry->Index == StorageIndex)) + { + Result = Entry; + break; + } + } + + return(Result); +} + +inline sim_entity * +GetEntityByStorageIndex(sim_region *SimRegion, uint32 StorageIndex) +{ + sim_entity_hash *Entry = GetHashFromStorageIndex(SimRegion, StorageIndex); + sim_entity *Result = Entry->Ptr; + return(Result); +} + +inline v3 +GetSimSpaceP(sim_region *SimRegion, low_entity *Stored) +{ + // NOTE(casey): Map the entity into camera space + // TODO(casey): Do we want to set this to signaling NAN in + // debug mode to make sure nobody ever uses the position + // of a nonspatial entity? + v3 Result = InvalidP; + if(!IsSet(&Stored->Sim, EntityFlag_Nonspatial)) + { + Result = Subtract(SimRegion->World, &Stored->P, &SimRegion->Origin); + } + + return(Result); +} + +internal sim_entity * +AddEntity(game_state *GameState, sim_region *SimRegion, uint32 StorageIndex, low_entity *Source, v3 *SimP); +inline void +LoadEntityReference(game_state *GameState, sim_region *SimRegion, entity_reference *Ref) +{ + if(Ref->Index) + { + sim_entity_hash *Entry = GetHashFromStorageIndex(SimRegion, Ref->Index); + if(Entry->Ptr == 0) + { + Entry->Index = Ref->Index; + low_entity *LowEntity = GetLowEntity(GameState, Ref->Index); + v3 P = GetSimSpaceP(SimRegion, LowEntity); + Entry->Ptr = AddEntity(GameState, SimRegion, Ref->Index, LowEntity, &P); + } + + Ref->Ptr = Entry->Ptr; + } +} + +inline void +StoreEntityReference(entity_reference *Ref) +{ + if(Ref->Ptr != 0) + { + Ref->Index = Ref->Ptr->StorageIndex; + } +} + +internal sim_entity * +AddEntityRaw(game_state *GameState, sim_region *SimRegion, uint32 StorageIndex, low_entity *Source) +{ + TIMED_FUNCTION(); + + Assert(StorageIndex); + sim_entity *Entity = 0; + + sim_entity_hash *Entry = GetHashFromStorageIndex(SimRegion, StorageIndex); + if(Entry->Ptr == 0) + { + if(SimRegion->EntityCount < SimRegion->MaxEntityCount) + { + Entity = SimRegion->Entities + SimRegion->EntityCount++; + + Entry->Index = StorageIndex; + Entry->Ptr = Entity; + + if(Source) + { + // TODO(casey): This should really be a decompression step, not + // a copy! + *Entity = Source->Sim; + LoadEntityReference(GameState, SimRegion, &Entity->Sword); + + Assert(!IsSet(&Source->Sim, EntityFlag_Simming)); + AddFlags(&Source->Sim, EntityFlag_Simming); + } + + Entity->StorageIndex = StorageIndex; + Entity->Updatable = false; + } + else + { + InvalidCodePath; + } + } + + return(Entity); +} + +inline bool32 +EntityOverlapsRectangle(v3 P, sim_entity_collision_volume Volume, rectangle3 Rect) +{ + rectangle3 Grown = AddRadiusTo(Rect, 0.5f*Volume.Dim); + bool32 Result = IsInRectangle(Grown, P + Volume.OffsetP); + return(Result); +} + +internal sim_entity * +AddEntity(game_state *GameState, sim_region *SimRegion, uint32 StorageIndex, low_entity *Source, v3 *SimP) +{ + sim_entity *Dest = AddEntityRaw(GameState, SimRegion, StorageIndex, Source); + if(Dest) + { + if(SimP) + { + Dest->P = *SimP; + Dest->Updatable = EntityOverlapsRectangle(Dest->P, Dest->Collision->TotalVolume, SimRegion->UpdatableBounds); + } + else + { + Dest->P = GetSimSpaceP(SimRegion, Source); + } + } + + return(Dest); +} + +internal sim_region * +BeginSim(memory_arena *SimArena, game_state *GameState, world *World, world_position Origin, rectangle3 Bounds, real32 dt) +{ + TIMED_FUNCTION(); + + // TODO(casey): If entities were stored in the world, we wouldn't need the game state here! + + sim_region *SimRegion = PushStruct(SimArena, sim_region); + ZeroStruct(SimRegion->Hash); + + // TODO(casey): Try to make these get enforced more rigorously + // TODO(casey): Perhaps try using a dual system here, where we support + // entities larger than the max entity radius by adding them multiple times + // to the spatial partition? + SimRegion->MaxEntityRadius = 5.0f; + SimRegion->MaxEntityVelocity = 30.0f; + real32 UpdateSafetyMargin = SimRegion->MaxEntityRadius + dt*SimRegion->MaxEntityVelocity; + real32 UpdateSafetyMarginZ = 1.0f; + + SimRegion->World = World; + SimRegion->Origin = Origin; + SimRegion->UpdatableBounds = AddRadiusTo(Bounds, V3(SimRegion->MaxEntityRadius, + SimRegion->MaxEntityRadius, + 0.0f)); + SimRegion->Bounds = AddRadiusTo(SimRegion->UpdatableBounds, + V3(UpdateSafetyMargin, UpdateSafetyMargin, UpdateSafetyMarginZ)); + + // TODO(casey): Need to be more specific about entity counts + SimRegion->MaxEntityCount = 4096; + SimRegion->EntityCount = 0; + SimRegion->Entities = PushArray(SimArena, SimRegion->MaxEntityCount, sim_entity); + + world_position MinChunkP = MapIntoChunkSpace(World, SimRegion->Origin, GetMinCorner(SimRegion->Bounds)); + world_position MaxChunkP = MapIntoChunkSpace(World, SimRegion->Origin, GetMaxCorner(SimRegion->Bounds)); + + for(int32 ChunkZ = MinChunkP.ChunkZ; + ChunkZ <= MaxChunkP.ChunkZ; + ++ChunkZ) + { + for(int32 ChunkY = MinChunkP.ChunkY; + ChunkY <= MaxChunkP.ChunkY; + ++ChunkY) + { + for(int32 ChunkX = MinChunkP.ChunkX; + ChunkX <= MaxChunkP.ChunkX; + ++ChunkX) + { + world_chunk *Chunk = GetWorldChunk(World, ChunkX, ChunkY, ChunkZ); + if(Chunk) + { + for(world_entity_block *Block = &Chunk->FirstBlock; + Block; + Block = Block->Next) + { + for(uint32 EntityIndexIndex = 0; + EntityIndexIndex < Block->EntityCount; + ++EntityIndexIndex) + { + uint32 LowEntityIndex = Block->LowEntityIndex[EntityIndexIndex]; + low_entity *Low = GameState->LowEntities + LowEntityIndex; + if(!IsSet(&Low->Sim, EntityFlag_Nonspatial)) + { + v3 SimSpaceP = GetSimSpaceP(SimRegion, Low); + if(EntityOverlapsRectangle(SimSpaceP, Low->Sim.Collision->TotalVolume, SimRegion->Bounds)) + { + AddEntity(GameState, SimRegion, LowEntityIndex, Low, &SimSpaceP); + } + } + } + } + } + } + } + } + + return(SimRegion); +} + +internal void +EndSim(sim_region *Region, game_state *GameState) +{ + TIMED_FUNCTION(); + + // TODO(casey): Maybe don't take a game state here, low entities should be stored + // in the world?? + + sim_entity *Entity = Region->Entities; + for(uint32 EntityIndex = 0; + EntityIndex < Region->EntityCount; + ++EntityIndex, ++Entity) + { + low_entity *Stored = GameState->LowEntities + Entity->StorageIndex; + + Assert(IsSet(&Stored->Sim, EntityFlag_Simming)); + Stored->Sim = *Entity; + Assert(!IsSet(&Stored->Sim, EntityFlag_Simming)); + + StoreEntityReference(&Stored->Sim.Sword); + + // TODO(casey): Save state back to the stored entity, once high entities + // do state decompression, etc. + + world_position NewP = IsSet(Entity, EntityFlag_Nonspatial) ? + NullPosition() : + MapIntoChunkSpace(GameState->World, Region->Origin, Entity->P); + ChangeEntityLocation(&GameState->WorldArena, GameState->World, Entity->StorageIndex, + Stored, NewP); + + if(Entity->StorageIndex == GameState->CameraFollowingEntityIndex) + { + world_position NewCameraP = GameState->CameraP; + + NewCameraP.ChunkZ = Stored->P.ChunkZ; + + DEBUG_IF(Renderer_Camera_RoomBased) + { + if(Entity->P.x > (9.0f)) + { + NewCameraP = MapIntoChunkSpace(GameState->World, NewCameraP, V3(18.0f, 0.0f, 0.0f)); + } + if(Entity->P.x < -(9.0f)) + { + NewCameraP = MapIntoChunkSpace(GameState->World, NewCameraP, V3(-18.0f, 0.0f, 0.0f)); + } + if(Entity->P.y > (5.0f)) + { + NewCameraP = MapIntoChunkSpace(GameState->World, NewCameraP, V3(18.0f, 10.0f, 0.0f)); + } + if(Entity->P.y < -(5.0f)) + { + NewCameraP = MapIntoChunkSpace(GameState->World, NewCameraP, V3(0.0f, -10.0f, 0.0f)); + } + } + else + { +// real32 CamZOffset = NewCameraP.Offset_.z; + NewCameraP = Stored->P; +// NewCameraP.Offset_.z = CamZOffset; + } + + GameState->CameraP = NewCameraP; + } + + } +} + +struct test_wall +{ + real32 X; + real32 RelX; + real32 RelY; + real32 DeltaX; + real32 DeltaY; + real32 MinY; + real32 MaxY; + v3 Normal; +}; +internal bool32 +TestWall(real32 WallX, real32 RelX, real32 RelY, real32 PlayerDeltaX, real32 PlayerDeltaY, + real32 *tMin, real32 MinY, real32 MaxY) +{ + bool32 Hit = false; + + real32 tEpsilon = 0.001f; + if(PlayerDeltaX != 0.0f) + { + real32 tResult = (WallX - RelX) / PlayerDeltaX; + real32 Y = RelY + tResult*PlayerDeltaY; + if((tResult >= 0.0f) && (*tMin > tResult)) + { + if((Y >= MinY) && (Y <= MaxY)) + { + *tMin = Maximum(0.0f, tResult - tEpsilon); + Hit = true; + } + } + } + + return(Hit); +} + +internal bool32 +CanCollide(game_state *GameState, sim_entity *A, sim_entity *B) +{ + bool32 Result = false; + + if(A != B) + { + if(A->StorageIndex > B->StorageIndex) + { + sim_entity *Temp = A; + A = B; + B = Temp; + } + + if(IsSet(A, EntityFlag_Collides) && IsSet(B, EntityFlag_Collides)) + { + if(!IsSet(A, EntityFlag_Nonspatial) && + !IsSet(B, EntityFlag_Nonspatial)) + { + // TODO(casey): Property-based logic goes here + Result = true; + } + + // TODO(casey): BETTER HASH FUNCTION + uint32 HashBucket = A->StorageIndex & (ArrayCount(GameState->CollisionRuleHash) - 1); + for(pairwise_collision_rule *Rule = GameState->CollisionRuleHash[HashBucket]; + Rule; + Rule = Rule->NextInHash) + { + if((Rule->StorageIndexA == A->StorageIndex) && + (Rule->StorageIndexB == B->StorageIndex)) + { + Result = Rule->CanCollide; + break; + } + } + } + } + + return(Result); +} + +internal bool32 +HandleCollision(game_state *GameState, sim_entity *A, sim_entity *B) +{ + bool32 StopsOnCollision = false; + + if(A->Type == EntityType_Sword) + { + AddCollisionRule(GameState, A->StorageIndex, B->StorageIndex, false); + StopsOnCollision = false; + } + else + { + StopsOnCollision = true; + } + + if(A->Type > B->Type) + { + sim_entity *Temp = A; + A = B; + B = Temp; + } + + if((A->Type == EntityType_Monstar) && + (B->Type == EntityType_Sword)) + { + if(A->HitPointMax > 0) + { + --A->HitPointMax; + } + } + + // TODO(casey): Stairs +// Entity->AbsTileZ += HitLow->dAbsTileZ; + + return(StopsOnCollision); +} + +internal bool32 +CanOverlap(game_state *GameState, sim_entity *Mover, sim_entity *Region) +{ + bool32 Result = false; + + if(Mover != Region) + { + if(Region->Type == EntityType_Stairwell) + { + Result = true; + } + } + + return(Result); +} + +internal void +HandleOverlap(game_state *GameState, sim_entity *Mover, sim_entity *Region, real32 dt, + real32 *Ground) +{ + if(Region->Type == EntityType_Stairwell) + { + *Ground = GetStairGround(Region, GetEntityGroundPoint(Mover)); + } +} + +internal bool32 +SpeculativeCollide(sim_entity *Mover, sim_entity *Region, v3 TestP) +{ + TIMED_FUNCTION(); + + bool32 Result = true; + + if(Region->Type == EntityType_Stairwell) + { + // TODO(casey): Needs work :) + real32 StepHeight = 0.1f; +#if 0 + Result = ((AbsoluteValue(GetEntityGroundPoint(Mover).z - Ground) > StepHeight) || + ((Bary.y > 0.1f) && (Bary.y < 0.9f))); +#endif + v3 MoverGroundPoint = GetEntityGroundPoint(Mover, TestP); + real32 Ground = GetStairGround(Region, MoverGroundPoint); + Result = (AbsoluteValue(MoverGroundPoint.z - Ground) > StepHeight); + } + + return(Result); +} + +internal bool32 +EntitiesOverlap(sim_entity *Entity, sim_entity *TestEntity, v3 Epsilon = V3(0, 0, 0)) +{ + TIMED_FUNCTION(); + + bool32 Result = false; + + for(uint32 VolumeIndex = 0; + !Result && (VolumeIndex < Entity->Collision->VolumeCount); + ++VolumeIndex) + { + sim_entity_collision_volume *Volume = Entity->Collision->Volumes + VolumeIndex; + + for(uint32 TestVolumeIndex = 0; + !Result && (TestVolumeIndex < TestEntity->Collision->VolumeCount); + ++TestVolumeIndex) + { + sim_entity_collision_volume *TestVolume = TestEntity->Collision->Volumes + TestVolumeIndex; + + rectangle3 EntityRect = RectCenterDim(Entity->P + Volume->OffsetP, Volume->Dim + Epsilon); + rectangle3 TestEntityRect = RectCenterDim(TestEntity->P + TestVolume->OffsetP, TestVolume->Dim); + Result = RectanglesIntersect(EntityRect, TestEntityRect); + } + } + + return(Result); +} + +internal void +MoveEntity(game_state *GameState, sim_region *SimRegion, sim_entity *Entity, real32 dt, + move_spec *MoveSpec, v3 ddP) +{ + TIMED_FUNCTION(); + + Assert(!IsSet(Entity, EntityFlag_Nonspatial)); + + world *World = SimRegion->World; + + if(Entity->Type == EntityType_Hero) + { + int BreakHere = 5; + } + + if(MoveSpec->UnitMaxAccelVector) + { + real32 ddPLength = LengthSq(ddP); + if(ddPLength > 1.0f) + { + ddP *= (1.0f / SquareRoot(ddPLength)); + } + } + + ddP *= MoveSpec->Speed; + + // TODO(casey): ODE here! + v3 Drag = -MoveSpec->Drag*Entity->dP; + Drag.z = 0.0f; + ddP += Drag; + if(!IsSet(Entity, EntityFlag_ZSupported)) + { + ddP += V3(0, 0, -9.8f); // NOTE(casey): Gravity! + } + + v3 PlayerDelta = (0.5f*ddP*Square(dt) + Entity->dP*dt); + Entity->dP = ddP*dt + Entity->dP; + // TODO(casey): Upgrade physical motion routines to handle capping the + // maximum velocity? + Assert(LengthSq(Entity->dP) <= Square(SimRegion->MaxEntityVelocity)); + + real32 DistanceRemaining = Entity->DistanceLimit; + if(DistanceRemaining == 0.0f) + { + // TODO(casey): Do we want to formalize this number? + DistanceRemaining = 10000.0f; + } + + for(uint32 Iteration = 0; + Iteration < 4; + ++Iteration) + { + real32 tMin = 1.0f; + real32 tMax = 0.0f; + + real32 PlayerDeltaLength = Length(PlayerDelta); + // TODO(casey): What do we want to do for epsilons here? + // Think this through for the final collision code + if(PlayerDeltaLength > 0.0f) + { + if(PlayerDeltaLength > DistanceRemaining) + { + tMin = (DistanceRemaining / PlayerDeltaLength); + } + + v3 WallNormalMin = {}; + v3 WallNormalMax = {}; + sim_entity *HitEntityMin = 0; + sim_entity *HitEntityMax = 0; + + v3 DesiredPosition = Entity->P + PlayerDelta; + + // NOTE(casey): This is just an optimization to avoid enterring the + // loop in the case where the test entity is non-spatial! + if(!IsSet(Entity, EntityFlag_Nonspatial)) + { + // TODO(casey): Spatial partition here! + for(uint32 TestHighEntityIndex = 0; + TestHighEntityIndex < SimRegion->EntityCount; + ++TestHighEntityIndex) + { + sim_entity *TestEntity = SimRegion->Entities + TestHighEntityIndex; + + // TODO(casey): Robustness! + real32 OverlapEpsilon = 0.001f; + + if((IsSet(TestEntity, EntityFlag_Traversable) && + EntitiesOverlap(Entity, TestEntity, OverlapEpsilon*V3(1, 1, 1))) || + CanCollide(GameState, Entity, TestEntity)) + { + for(uint32 VolumeIndex = 0; + VolumeIndex < Entity->Collision->VolumeCount; + ++VolumeIndex) + { + sim_entity_collision_volume *Volume = + Entity->Collision->Volumes + VolumeIndex; + + for(uint32 TestVolumeIndex = 0; + TestVolumeIndex < TestEntity->Collision->VolumeCount; + ++TestVolumeIndex) + { + sim_entity_collision_volume *TestVolume = + TestEntity->Collision->Volumes + TestVolumeIndex; + + v3 MinkowskiDiameter = {TestVolume->Dim.x + Volume->Dim.x, + TestVolume->Dim.y + Volume->Dim.y, + TestVolume->Dim.z + Volume->Dim.z}; + + v3 MinCorner = -0.5f*MinkowskiDiameter; + v3 MaxCorner = 0.5f*MinkowskiDiameter; + + v3 Rel = ((Entity->P + Volume->OffsetP) - + (TestEntity->P + TestVolume->OffsetP)); + + // TODO(casey): Do we want an open inclusion at the MaxCorner? + if((Rel.z >= MinCorner.z) && (Rel.z < MaxCorner.z)) + { + test_wall Walls[] = + { + {MinCorner.x, Rel.x, Rel.y, PlayerDelta.x, PlayerDelta.y, MinCorner.y, MaxCorner.y, V3(-1, 0, 0)}, + {MaxCorner.x, Rel.x, Rel.y, PlayerDelta.x, PlayerDelta.y, MinCorner.y, MaxCorner.y, V3(1, 0, 0)}, + {MinCorner.y, Rel.y, Rel.x, PlayerDelta.y, PlayerDelta.x, MinCorner.x, MaxCorner.x, V3(0, -1, 0)}, + {MaxCorner.y, Rel.y, Rel.x, PlayerDelta.y, PlayerDelta.x, MinCorner.x, MaxCorner.x, V3(0, 1, 0)}, + }; + + if(IsSet(TestEntity, EntityFlag_Traversable)) + { + real32 tMaxTest = tMax; + bool32 HitThis = false; + + v3 TestWallNormal = {}; + for(uint32 WallIndex = 0; + WallIndex < ArrayCount(Walls); + ++WallIndex) + { + test_wall *Wall = Walls + WallIndex; + + real32 tEpsilon = 0.001f; + if(Wall->DeltaX != 0.0f) + { + real32 tResult = (Wall->X - Wall->RelX) / Wall->DeltaX; + real32 Y = Wall->RelY + tResult*Wall->DeltaY; + if((tResult >= 0.0f) && (tMaxTest < tResult)) + { + if((Y >= Wall->MinY) && (Y <= Wall->MaxY)) + { + tMaxTest = Maximum(0.0f, tResult - tEpsilon); + TestWallNormal = Wall->Normal; + HitThis = true; + } + } + } + } + + if(HitThis) + { + tMax = tMaxTest; + WallNormalMax = TestWallNormal; + HitEntityMax = TestEntity; + } + } + else + { + real32 tMinTest = tMin; + bool32 HitThis = false; + + v3 TestWallNormal = {}; + for(uint32 WallIndex = 0; + WallIndex < ArrayCount(Walls); + ++WallIndex) + { + test_wall *Wall = Walls + WallIndex; + + real32 tEpsilon = 0.001f; + if(Wall->DeltaX != 0.0f) + { + real32 tResult = (Wall->X - Wall->RelX) / Wall->DeltaX; + real32 Y = Wall->RelY + tResult*Wall->DeltaY; + if((tResult >= 0.0f) && (tMinTest > tResult)) + { + if((Y >= Wall->MinY) && (Y <= Wall->MaxY)) + { + tMinTest = Maximum(0.0f, tResult - tEpsilon); + TestWallNormal = Wall->Normal; + HitThis = true; + } + } + } + } + + // TODO(casey): We need a concept of stepping onto vs. stepping + // off of here so that we can prevent you from _leaving_ + // stairs instead of just preventing you from getting onto them. + if(HitThis) + { + v3 TestP = Entity->P + tMinTest*PlayerDelta; + if(SpeculativeCollide(Entity, TestEntity, TestP)) + { + tMin = tMinTest; + WallNormalMin = TestWallNormal; + HitEntityMin = TestEntity; + } + } + } + } + } + } + } + } + } + + v3 WallNormal; + sim_entity *HitEntity; + real32 tStop; + if(tMin < tMax) + { + tStop = tMin; + HitEntity = HitEntityMin; + WallNormal = WallNormalMin; + } + else + { + tStop = tMax; + HitEntity = HitEntityMax; + WallNormal = WallNormalMax; + } + + Entity->P += tStop*PlayerDelta; + DistanceRemaining -= tStop*PlayerDeltaLength; + if(HitEntity) + { + PlayerDelta = DesiredPosition - Entity->P; + + bool32 StopsOnCollision = HandleCollision(GameState, Entity, HitEntity); + if(StopsOnCollision) + { + PlayerDelta = PlayerDelta - 1*Inner(PlayerDelta, WallNormal)*WallNormal; + Entity->dP = Entity->dP - 1*Inner(Entity->dP, WallNormal)*WallNormal; + } + } + else + { + break; + } + } + else + { + break; + } + } + + real32 Ground = 0.0f; + + // NOTE(casey): Handle events based on area overlapping + // TODO(casey): Handle overlapping precisely by moving it into the collision loop? + { + // TODO(casey): Spatial partition here! + for(uint32 TestHighEntityIndex = 0; + TestHighEntityIndex < SimRegion->EntityCount; + ++TestHighEntityIndex) + { + sim_entity *TestEntity = SimRegion->Entities + TestHighEntityIndex; + if(CanOverlap(GameState, Entity, TestEntity) && + EntitiesOverlap(Entity, TestEntity)) + { + HandleOverlap(GameState, Entity, TestEntity, dt, &Ground); + } + } + } + + Ground += Entity->P.z - GetEntityGroundPoint(Entity).z; + if((Entity->P.z <= Ground) || + (IsSet(Entity, EntityFlag_ZSupported) && + (Entity->dP.z == 0.0f))) + { + Entity->P.z = Ground; + Entity->dP.z = 0; + AddFlags(Entity, EntityFlag_ZSupported); + } + else + { + ClearFlags(Entity, EntityFlag_ZSupported); + } + + if(Entity->DistanceLimit != 0.0f) + { + Entity->DistanceLimit = DistanceRemaining; + } + + // TODO(casey): Change to using the acceleration vector + if((Entity->dP.x == 0.0f) && (Entity->dP.y == 0.0f)) + { + // NOTE(casey): Leave FacingDirection whatever it was + } + else + { + Entity->FacingDirection = ATan2(Entity->dP.y, Entity->dP.x); + } +} diff --git a/test_data/lots_of_files/handmade_sim_region.h b/test_data/lots_of_files/handmade_sim_region.h new file mode 100644 index 0000000..681eb27 --- /dev/null +++ b/test_data/lots_of_files/handmade_sim_region.h @@ -0,0 +1,144 @@ +#if !defined(HANDMADE_SIM_REGION_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +struct move_spec +{ + bool32 UnitMaxAccelVector; + real32 Speed; + real32 Drag; +}; + +enum entity_type +{ + EntityType_Null, + + EntityType_Space, + + EntityType_Hero, + EntityType_Wall, + EntityType_Familiar, + EntityType_Monstar, + EntityType_Sword, + EntityType_Stairwell, +}; + +#define HIT_POINT_SUB_COUNT 4 +struct hit_point +{ + // TODO(casey): Bake this down into one variable + uint8 Flags; + uint8 FilledAmount; +}; + +// TODO(casey): Rename sim_entity to entity! +struct sim_entity; +union entity_reference +{ + sim_entity *Ptr; + uint32 Index; +}; + +enum sim_entity_flags +{ + // TODO(casey): Does it make more sense to have the flag be for _non_ colliding entities? + // TODO(casey): Collides and ZSupported probably can be removed now/soon + EntityFlag_Collides = (1 << 0), + EntityFlag_Nonspatial = (1 << 1), + EntityFlag_Moveable = (1 << 2), + EntityFlag_ZSupported = (1 << 3), + EntityFlag_Traversable = (1 << 4), + + EntityFlag_Simming = (1 << 30), +}; + +introspect(category:"sim_region") struct sim_entity_collision_volume +{ + v3 OffsetP; + v3 Dim; +}; + +introspect(category:"sim_region") struct sim_entity_collision_volume_group +{ + sim_entity_collision_volume TotalVolume; + + // TODO(casey): VolumeCount is always expected to be greater than 0 if the entity + // has any volume... in the future, this could be compressed if necessary to say + // that the VolumeCount can be 0 if the TotalVolume should be used as the only + // collision volume for the entity. + u32 VolumeCount; + sim_entity_collision_volume *Volumes; +}; + +introspect(category:"regular butter") struct sim_entity +{ + // NOTE(casey): These are only for the sim region + world_chunk *OldChunk; + u32 StorageIndex; + b32 Updatable; + + // + + entity_type Type; + u32 Flags; + + v3 P; + v3 dP; + + r32 DistanceLimit; + + sim_entity_collision_volume_group *Collision; + + r32 FacingDirection; + r32 tBob; + + s32 dAbsTileZ; + + // TODO(casey): Should hitpoints themselves be entities? + u32 HitPointMax; + hit_point HitPoint[16]; + + entity_reference Sword; + + // TODO(casey): Only for stairwells! + v2 WalkableDim; + r32 WalkableHeight; + + // TODO(casey): Generation index so we know how "up to date" this entity is. +}; + +struct sim_entity_hash +{ + sim_entity *Ptr; + uint32 Index; +}; + +introspect(category:"regular butter") struct sim_region +{ + // TODO(casey): Need a hash table here to map stored entity indices + // to sim entities! + + world *World; + r32 MaxEntityRadius; + r32 MaxEntityVelocity; + + world_position Origin; + rectangle3 Bounds; + rectangle3 UpdatableBounds; + + u32 MaxEntityCount; + u32 EntityCount; + sim_entity *Entities; + + // TODO(casey): Do I really want a hash for this?? + // NOTE(casey): Must be a power of two! + sim_entity_hash Hash[4096]; +}; + +#define HANDMADE_SIM_REGION_H +#endif diff --git a/test_data/lots_of_files/handmade_world.cpp b/test_data/lots_of_files/handmade_world.cpp new file mode 100644 index 0000000..94a9aaa --- /dev/null +++ b/test_data/lots_of_files/handmade_world.cpp @@ -0,0 +1,320 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +// TODO(casey): Think about what the real safe margin is! +#define TILE_CHUNK_SAFE_MARGIN (INT32_MAX/64) +#define TILE_CHUNK_UNINITIALIZED INT32_MAX + +#define TILES_PER_CHUNK 8 + +inline world_position +NullPosition(void) +{ + world_position Result = {}; + + Result.ChunkX = TILE_CHUNK_UNINITIALIZED; + + return(Result); +} + +inline bool32 +IsValid(world_position P) +{ + bool32 Result = (P.ChunkX != TILE_CHUNK_UNINITIALIZED); + return(Result); +} + +inline bool32 +IsCanonical(real32 ChunkDim, real32 TileRel) +{ + // TODO(casey): Fix floating point math so this can be exact? + real32 Epsilon = 0.01f; + bool32 Result = ((TileRel >= -(0.5f*ChunkDim + Epsilon)) && + (TileRel <= (0.5f*ChunkDim + Epsilon))); + + return(Result); +} + +inline bool32 +IsCanonical(world *World, v3 Offset) +{ + bool32 Result = (IsCanonical(World->ChunkDimInMeters.x, Offset.x) && + IsCanonical(World->ChunkDimInMeters.y, Offset.y) && + IsCanonical(World->ChunkDimInMeters.z, Offset.z)); + + return(Result); +} + +inline bool32 +AreInSameChunk(world *World, world_position *A, world_position *B) +{ + Assert(IsCanonical(World, A->Offset_)); + Assert(IsCanonical(World, B->Offset_)); + + bool32 Result = ((A->ChunkX == B->ChunkX) && + (A->ChunkY == B->ChunkY) && + (A->ChunkZ == B->ChunkZ)); + + return(Result); +} + +inline world_chunk * +GetWorldChunk(world *World, int32 ChunkX, int32 ChunkY, int32 ChunkZ, + memory_arena *Arena = 0) +{ + TIMED_FUNCTION(); + + Assert(ChunkX > -TILE_CHUNK_SAFE_MARGIN); + Assert(ChunkY > -TILE_CHUNK_SAFE_MARGIN); + Assert(ChunkZ > -TILE_CHUNK_SAFE_MARGIN); + Assert(ChunkX < TILE_CHUNK_SAFE_MARGIN); + Assert(ChunkY < TILE_CHUNK_SAFE_MARGIN); + Assert(ChunkZ < TILE_CHUNK_SAFE_MARGIN); + + // TODO(casey): BETTER HASH FUNCTION!!!! + uint32 HashValue = 19*ChunkX + 7*ChunkY + 3*ChunkZ; + uint32 HashSlot = HashValue & (ArrayCount(World->ChunkHash) - 1); + Assert(HashSlot < ArrayCount(World->ChunkHash)); + + world_chunk *Chunk = World->ChunkHash + HashSlot; + do + { + if((ChunkX == Chunk->ChunkX) && + (ChunkY == Chunk->ChunkY) && + (ChunkZ == Chunk->ChunkZ)) + { + break; + } + + if(Arena && (Chunk->ChunkX != TILE_CHUNK_UNINITIALIZED) && (!Chunk->NextInHash)) + { + Chunk->NextInHash = PushStruct(Arena, world_chunk); + Chunk = Chunk->NextInHash; + Chunk->ChunkX = TILE_CHUNK_UNINITIALIZED; + } + + if(Arena && (Chunk->ChunkX == TILE_CHUNK_UNINITIALIZED)) + { + Chunk->ChunkX = ChunkX; + Chunk->ChunkY = ChunkY; + Chunk->ChunkZ = ChunkZ; + + Chunk->NextInHash = 0; + break; + } + + Chunk = Chunk->NextInHash; + } while(Chunk); + + return(Chunk); +} + +internal void +InitializeWorld(world *World, v3 ChunkDimInMeters) +{ + World->ChunkDimInMeters = ChunkDimInMeters; + World->FirstFree = 0; + + for(uint32 ChunkIndex = 0; + ChunkIndex < ArrayCount(World->ChunkHash); + ++ChunkIndex) + { + World->ChunkHash[ChunkIndex].ChunkX = TILE_CHUNK_UNINITIALIZED; + World->ChunkHash[ChunkIndex].FirstBlock.EntityCount = 0; + } +} + +inline void +RecanonicalizeCoord(real32 ChunkDim, int32 *Tile, real32 *TileRel) +{ + // TODO(casey): Need to do something that doesn't use the divide/multiply method + // for recanonicalizing because this can end up rounding back on to the tile + // you just came from. + + // NOTE(casey): Wrapping IS NOT ALLOWED, so all coordinates are assumed to be + // within the safe margin! + // TODO(casey): Assert that we are nowhere near the edges of the world. + + int32 Offset = RoundReal32ToInt32(*TileRel / ChunkDim); + *Tile += Offset; + *TileRel -= Offset*ChunkDim; + + Assert(IsCanonical(ChunkDim, *TileRel)); +} + +inline world_position +MapIntoChunkSpace(world *World, world_position BasePos, v3 Offset) +{ + world_position Result = BasePos; + + Result.Offset_ += Offset; + RecanonicalizeCoord(World->ChunkDimInMeters.x, &Result.ChunkX, &Result.Offset_.x); + RecanonicalizeCoord(World->ChunkDimInMeters.y, &Result.ChunkY, &Result.Offset_.y); + RecanonicalizeCoord(World->ChunkDimInMeters.z, &Result.ChunkZ, &Result.Offset_.z); + + return(Result); +} + +inline v3 +Subtract(world *World, world_position *A, world_position *B) +{ + v3 dTile = {(real32)A->ChunkX - (real32)B->ChunkX, + (real32)A->ChunkY - (real32)B->ChunkY, + (real32)A->ChunkZ - (real32)B->ChunkZ}; + + v3 Result = Hadamard(World->ChunkDimInMeters, dTile) + (A->Offset_ - B->Offset_); + + return(Result); +} + +inline world_position +CenteredChunkPoint(uint32 ChunkX, uint32 ChunkY, uint32 ChunkZ) +{ + world_position Result = {}; + + Result.ChunkX = ChunkX; + Result.ChunkY = ChunkY; + Result.ChunkZ = ChunkZ; + + return(Result); +} + +inline world_position +CenteredChunkPoint(world_chunk *Chunk) +{ + world_position Result = CenteredChunkPoint(Chunk->ChunkX, Chunk->ChunkY, Chunk->ChunkZ); + + return(Result); +} + +inline void +ChangeEntityLocationRaw(memory_arena *Arena, world *World, uint32 LowEntityIndex, + world_position *OldP, world_position *NewP) +{ + TIMED_FUNCTION(); + + // TODO(casey): If this moves an entity into the camera bounds, should it automatically + // go into the high set immediately? + // If it moves _out_ of the camera bounds, should it be removed from the high set + // immediately? + + Assert(!OldP || IsValid(*OldP)); + Assert(!NewP || IsValid(*NewP)); + + if(OldP && NewP && AreInSameChunk(World, OldP, NewP)) + { + // NOTE(casey): Leave entity where it is + } + else + { + if(OldP) + { + // NOTE(casey): Pull the entity out of its old entity block + world_chunk *Chunk = GetWorldChunk(World, OldP->ChunkX, OldP->ChunkY, OldP->ChunkZ); + Assert(Chunk); + if(Chunk) + { + bool32 NotFound = true; + world_entity_block *FirstBlock = &Chunk->FirstBlock; + for(world_entity_block *Block = FirstBlock; + Block && NotFound; + Block = Block->Next) + { + for(uint32 Index = 0; + (Index < Block->EntityCount) && NotFound; + ++Index) + { + if(Block->LowEntityIndex[Index] == LowEntityIndex) + { + Assert(FirstBlock->EntityCount > 0); + Block->LowEntityIndex[Index] = + FirstBlock->LowEntityIndex[--FirstBlock->EntityCount]; + if(FirstBlock->EntityCount == 0) + { + if(FirstBlock->Next) + { + world_entity_block *NextBlock = FirstBlock->Next; + *FirstBlock = *NextBlock; + + NextBlock->Next = World->FirstFree; + World->FirstFree = NextBlock; + } + } + + NotFound = false; + } + } + } + } + } + + if(NewP) + { + // NOTE(casey): Insert the entity into its new entity block + world_chunk *Chunk = GetWorldChunk(World, NewP->ChunkX, NewP->ChunkY, NewP->ChunkZ, Arena); + Assert(Chunk); + + world_entity_block *Block = &Chunk->FirstBlock; + if(Block->EntityCount == ArrayCount(Block->LowEntityIndex)) + { + // NOTE(casey): We're out of room, get a new block! + world_entity_block *OldBlock = World->FirstFree; + if(OldBlock) + { + World->FirstFree = OldBlock->Next; + } + else + { + OldBlock = PushStruct(Arena, world_entity_block); + } + + *OldBlock = *Block; + Block->Next = OldBlock; + Block->EntityCount = 0; + } + + Assert(Block->EntityCount < ArrayCount(Block->LowEntityIndex)); + Block->LowEntityIndex[Block->EntityCount++] = LowEntityIndex; + } + } +} + +internal void +ChangeEntityLocation(memory_arena *Arena, world *World, + uint32 LowEntityIndex, low_entity *LowEntity, + world_position NewPInit) +{ + TIMED_FUNCTION(); + + world_position *OldP = 0; + world_position *NewP = 0; + + if(!IsSet(&LowEntity->Sim, EntityFlag_Nonspatial) && IsValid(LowEntity->P)) + { + OldP = &LowEntity->P; + } + + if(IsValid(NewPInit)) + { + NewP = &NewPInit; + } + + ChangeEntityLocationRaw(Arena, World, LowEntityIndex, OldP, NewP); + + if(NewP) + { + LowEntity->P = *NewP; + ClearFlags(&LowEntity->Sim, EntityFlag_Nonspatial); + } + else + { + LowEntity->P = NullPosition(); + AddFlags(&LowEntity->Sim, EntityFlag_Nonspatial); + } +} + diff --git a/test_data/lots_of_files/handmade_world.h b/test_data/lots_of_files/handmade_world.h new file mode 100644 index 0000000..6d17353 --- /dev/null +++ b/test_data/lots_of_files/handmade_world.h @@ -0,0 +1,59 @@ +#if !defined(HANDMADE_WORLD_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +introspect(category:"world") struct world_position +{ + // TODO(casey): It seems like we have to store ChunkX/Y/Z with each + // entity because even though the sim region gather doesn't need it + // at first, and we could get by without it, entity references pull + // in entities WITHOUT going through their world_chunk, and thus + // still need to know the ChunkX/Y/Z + + s32 ChunkX; + s32 ChunkY; + s32 ChunkZ; + + // NOTE(casey): These are the offsets from the chunk center + v3 Offset_; +}; + +// TODO(casey): Could make this just tile_chunk and then allow multiple tile chunks per X/Y/Z +struct world_entity_block +{ + uint32 EntityCount; + uint32 LowEntityIndex[16]; + world_entity_block *Next; +}; + +struct world_chunk +{ + int32 ChunkX; + int32 ChunkY; + int32 ChunkZ; + + // TODO(casey): Profile this and determine if a pointer would be better here! + world_entity_block FirstBlock; + + world_chunk *NextInHash; +}; + +struct world +{ + v3 ChunkDimInMeters; + + world_entity_block *FirstFree; + + // TODO(casey): WorldChunkHash should probably switch to pointers IF + // tile entity blocks continue to be stored en masse directly in the tile chunk! + // NOTE(casey): A the moment, this must be a power of two! + world_chunk ChunkHash[4096]; +}; + +#define HANDMADE_WORLD_H +#endif diff --git a/test_data/lots_of_files/highly_divisible_triangular_number.cpp b/test_data/lots_of_files/highly_divisible_triangular_number.cpp new file mode 100644 index 0000000..0c970bd --- /dev/null +++ b/test_data/lots_of_files/highly_divisible_triangular_number.cpp @@ -0,0 +1,228 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +typedef unsigned long long bigint; +struct Sieve_Array{ + bool *is_composite; + bigint size; +}; + +struct Primes{ + bigint *primes; + int count, max; +}; + +struct Sieve{ + Sieve_Array sieve; + Primes ps; + int i; + int w; +}; + +void extend_sieve(Primes ps, Sieve_Array sieve, int start){ + int q,p,j,n,m; + for (q = 1; q < ps.count; ++q){ + p = ps.primes[q]; + j = start; + n = 2*j + 3; + m = (n/p)*p; + if (n != m){ + n = m+p; + if (n % 2 == 0){ + n += p; + } + j = (n-3)/2; + } + + for (; j < sieve.size; j += p){ + sieve.is_composite[j] = 1; + } + } +} + +int sieve_partial(Primes *ps_, Sieve_Array sieve, int start){ + Primes ps = *ps_; + int i=start,n,j; + for (; i < sieve.size; ++i){ + if (!sieve.is_composite[i]){ + n = i*2 + 3; + ps.primes[ps.count++] = n; + for (j = i + n; j < sieve.size; j += n){ + sieve.is_composite[j] = 1; + } + if (ps.count == ps.max){ + ++i; + break; + } + } + } + ps_->count = ps.count; + return i; +} + +// size of primes >= which_prime +// size of sieve >= size > 0 +void begin_sieve(Sieve *s, int prime_size, int size){ + s->sieve.size = size; + + s->ps.count = 1; + s->ps.max = prime_size; + s->ps.primes[0] = 2; + + s->i = 0; + s->w = prime_size; +} + +inline bool sieve_step(Sieve *s){ + s->i = sieve_partial(&s->ps, s->sieve, s->i); + return (s->ps.count != s->w); +} + +inline void sieve_grow(Sieve *s, Sieve_Array new_sieve){ + extend_sieve(s->ps, new_sieve, s->sieve.size); + s->sieve = new_sieve; +} + +inline int sieve_init_size(int which_prime){ + return which_prime*5; +} + +inline int sieve_new_size(Sieve s){ + return s.sieve.size + s.w * (1 + ((s.w - s.ps.count) / 1000)); +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{}; + +struct Euler_Result{ + int number; +}; + +int count_divisors(int x, int *d, Primes ps){ + int dc = 0; + int c,p,i,j; + + p = ps.primes[0]; + for (i = 0; i < ps.count && p < x; ++i){ + p = ps.primes[i]; + c = 1; + while (x % p == 0){ + x /= p; + ++c; + } + if (c > 1){ + d[dc++] = c; + } + } + + c = 1; + for (j = 0; j < dc; ++j){ + c *= d[j]; + } + + return c; +} + +int count_divisors(int x, int *d){ + int dc = 0; + int c,p,j; + + c = 1; + while (x % 2 == 0){ + x /= 2; + ++c; + } + if (c > 1){ + d[dc++] = c; + } + + for (p = 3; p <= x; p += 2){ + c = 1; + while (x % p == 0){ + x /= p; + ++c; + } + if (c > 1){ + d[dc++] = c; + } + } + + c = 1; + for (j = 0; j < dc; ++j){ + c *= d[j]; + } + + return c; +} + +inline Euler_Result euler_main(Euler_In in){ + int d[500]; + int max = 1600; + int *divs = (int*)malloc(sizeof(int)*max); + divs[0] = 0; + divs[1] = 1; + divs[2] = 2; + + int x=1,n=2,c=0,a,b; + a=1; + b=1; + + while (1){ + if (c >= 500){ + break; + } + + ++n; + divs[n] = count_divisors(n, d); + + b += 2; + x += 2*a; + c = divs[a]*divs[b]; + + if (c >= 500){ + break; + } + + ++n; + if (n >= max){ + int new_max = max*2; + int *new_divs = (int*)malloc(sizeof(int)*new_max); + memcpy(new_divs, divs, sizeof(int)*max); + free(divs); + divs = new_divs; + max = new_max; + } + + divs[n] = count_divisors(n, d); + + a += 1; + x += b; + c = divs[a]*divs[b]; + } + + Euler_Result result; + result.number = x; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.number); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result; + + int a = 76576500; + + printf("answer = %d\n", a); + result = (a == answer.number); + + return result; +} +#endif + diff --git a/test_data/lots_of_files/immintrin.h b/test_data/lots_of_files/immintrin.h new file mode 100644 index 0000000..481c34e --- /dev/null +++ b/test_data/lots_of_files/immintrin.h @@ -0,0 +1,1941 @@ +/*** +* imminitrin.h - Meta Header file for Intel(R) Architecture intrinsic functions. +* +* Copyright (C) 1985-2011 Intel Corporation. All rights reserved. +* +* The information and source code contained herein is the exclusive +* property of Intel Corporation and may not be disclosed, examined +* or reproduced in whole or in part without explicit written authorization +* from the company. +* +* +*******************************************************************************/ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_IMM +#define _INCLUDED_IMM + +#if defined (_M_CEE_PURE) + #error ERROR: Intel Architecture intrinsic functions not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <wmmintrin.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Intel(R) AVX compiler intrinsic functions. + */ +typedef union __declspec(intrin_type) _CRT_ALIGN(32) __m256 { + float m256_f32[8]; +} __m256; + +typedef struct __declspec(intrin_type) _CRT_ALIGN(32) __m256d { + double m256d_f64[4]; +} __m256d; + +typedef union __declspec(intrin_type) _CRT_ALIGN(32) __m256i { + __int8 m256i_i8[32]; + __int16 m256i_i16[16]; + __int32 m256i_i32[8]; + __int64 m256i_i64[4]; + unsigned __int8 m256i_u8[32]; + unsigned __int16 m256i_u16[16]; + unsigned __int32 m256i_u32[8]; + unsigned __int64 m256i_u64[4]; +} __m256i; + + +/* + * Compare predicates for scalar and packed compare intrinsic functions + */ +#define _CMP_EQ_OQ 0x00 /* Equal (ordered, nonsignaling) */ +#define _CMP_LT_OS 0x01 /* Less-than (ordered, signaling) */ +#define _CMP_LE_OS 0x02 /* Less-than-or-equal (ordered, signaling) */ +#define _CMP_UNORD_Q 0x03 /* Unordered (nonsignaling) */ +#define _CMP_NEQ_UQ 0x04 /* Not-equal (unordered, nonsignaling) */ +#define _CMP_NLT_US 0x05 /* Not-less-than (unordered, signaling) */ +#define _CMP_NLE_US 0x06 /* Not-less-than-or-equal (unordered, + signaling) */ +#define _CMP_ORD_Q 0x07 /* Ordered (nonsignaling) */ +#define _CMP_EQ_UQ 0x08 /* Equal (unordered, non-signaling) */ +#define _CMP_NGE_US 0x09 /* Not-greater-than-or-equal (unordered, + signaling) */ +#define _CMP_NGT_US 0x0A /* Not-greater-than (unordered, signaling) */ +#define _CMP_FALSE_OQ 0x0B /* False (ordered, nonsignaling) */ +#define _CMP_NEQ_OQ 0x0C /* Not-equal (ordered, non-signaling) */ +#define _CMP_GE_OS 0x0D /* Greater-than-or-equal (ordered, signaling) */ +#define _CMP_GT_OS 0x0E /* Greater-than (ordered, signaling) */ +#define _CMP_TRUE_UQ 0x0F /* True (unordered, non-signaling) */ +#define _CMP_EQ_OS 0x10 /* Equal (ordered, signaling) */ +#define _CMP_LT_OQ 0x11 /* Less-than (ordered, nonsignaling) */ +#define _CMP_LE_OQ 0x12 /* Less-than-or-equal (ordered, nonsignaling) */ +#define _CMP_UNORD_S 0x13 /* Unordered (signaling) */ +#define _CMP_NEQ_US 0x14 /* Not-equal (unordered, signaling) */ +#define _CMP_NLT_UQ 0x15 /* Not-less-than (unordered, nonsignaling) */ +#define _CMP_NLE_UQ 0x16 /* Not-less-than-or-equal (unordered, + nonsignaling) */ +#define _CMP_ORD_S 0x17 /* Ordered (signaling) */ +#define _CMP_EQ_US 0x18 /* Equal (unordered, signaling) */ +#define _CMP_NGE_UQ 0x19 /* Not-greater-than-or-equal (unordered, + nonsignaling) */ +#define _CMP_NGT_UQ 0x1A /* Not-greater-than (unordered, nonsignaling) */ +#define _CMP_FALSE_OS 0x1B /* False (ordered, signaling) */ +#define _CMP_NEQ_OS 0x1C /* Not-equal (ordered, signaling) */ +#define _CMP_GE_OQ 0x1D /* Greater-than-or-equal (ordered, + nonsignaling) */ +#define _CMP_GT_OQ 0x1E /* Greater-than (ordered, nonsignaling) */ +#define _CMP_TRUE_US 0x1F /* True (unordered, signaling) */ + +/* + * Add Packed Double Precision Floating-Point Values + * **** VADDPD ymm1, ymm2, ymm3/m256 + * Performs an SIMD add of the four packed double-precision floating-point + * values from the first source operand to the second source operand, and + * stores the packed double-precision floating-point results in the + * destination + */ +extern __m256d __cdecl _mm256_add_pd(__m256d, __m256d); + +/* + * Add Packed Single Precision Floating-Point Values + * **** VADDPS ymm1, ymm2, ymm3/m256 + * Performs an SIMD add of the eight packed single-precision floating-point + * values from the first source operand to the second source operand, and + * stores the packed single-precision floating-point results in the + * destination + */ +extern __m256 __cdecl _mm256_add_ps(__m256, __m256); + +/* + * Add/Subtract Double Precision Floating-Point Values + * **** VADDSUBPD ymm1, ymm2, ymm3/m256 + * Adds odd-numbered double-precision floating-point values of the first + * source operand with the corresponding double-precision floating-point + * values from the second source operand; stores the result in the odd-numbered + * values of the destination. Subtracts the even-numbered double-precision + * floating-point values from the second source operand from the corresponding + * double-precision floating values in the first source operand; stores the + * result into the even-numbered values of the destination + */ +extern __m256d __cdecl _mm256_addsub_pd(__m256d, __m256d); + +/* + * Add/Subtract Packed Single Precision Floating-Point Values + * **** VADDSUBPS ymm1, ymm2, ymm3/m256 + * Adds odd-numbered single-precision floating-point values of the first source + * operand with the corresponding single-precision floating-point values from + * the second source operand; stores the result in the odd-numbered values of + * the destination. Subtracts the even-numbered single-precision floating-point + * values from the second source operand from the corresponding + * single-precision floating values in the first source operand; stores the + * result into the even-numbered values of the destination + */ +extern __m256 __cdecl _mm256_addsub_ps(__m256, __m256); + +/* + * Bitwise Logical AND of Packed Double Precision Floating-Point Values + * **** VANDPD ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical AND of the four packed double-precision + * floating-point values from the first source operand and the second + * source operand, and stores the result in the destination + */ +extern __m256d __cdecl _mm256_and_pd(__m256d, __m256d); + +/* + * Bitwise Logical AND of Packed Single Precision Floating-Point Values + * **** VANDPS ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical AND of the eight packed single-precision + * floating-point values from the first source operand and the second + * source operand, and stores the result in the destination + */ +extern __m256 __cdecl _mm256_and_ps(__m256, __m256); + +/* + * Bitwise Logical AND NOT of Packed Double Precision Floating-Point Values + * **** VANDNPD ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical AND NOT of the four packed double-precision + * floating-point values from the first source operand and the second source + * operand, and stores the result in the destination + */ +extern __m256d __cdecl _mm256_andnot_pd(__m256d, __m256d); + +/* + * Bitwise Logical AND NOT of Packed Single Precision Floating-Point Values + * **** VANDNPS ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical AND NOT of the eight packed single-precision + * floating-point values from the first source operand and the second source + * operand, and stores the result in the destination + */ +extern __m256 __cdecl _mm256_andnot_ps(__m256, __m256); + +/* + * Blend Packed Double Precision Floating-Point Values + * **** VBLENDPD ymm1, ymm2, ymm3/m256, imm8 + * Double-Precision Floating-Point values from the second source operand are + * conditionally merged with values from the first source operand and written + * to the destination. The immediate bits [3:0] determine whether the + * corresponding Double-Precision Floating Point value in the destination is + * copied from the second source or first source. If a bit in the mask, + * corresponding to a word, is "1", then the Double-Precision Floating-Point + * value in the second source operand is copied, else the value in the first + * source operand is copied + */ +extern __m256d __cdecl _mm256_blend_pd(__m256d, __m256d, const int); + +/* + * Blend Packed Single Precision Floating-Point Values + * **** VBLENDPS ymm1, ymm2, ymm3/m256, imm8 + * Single precision floating point values from the second source operand are + * conditionally merged with values from the first source operand and written + * to the destination. The immediate bits [7:0] determine whether the + * corresponding single precision floating-point value in the destination is + * copied from the second source or first source. If a bit in the mask, + * corresponding to a word, is "1", then the single-precision floating-point + * value in the second source operand is copied, else the value in the first + * source operand is copied + */ +extern __m256 __cdecl _mm256_blend_ps(__m256, __m256, const int); + +/* + * Blend Packed Double Precision Floating-Point Values + * **** VBLENDVPD ymm1, ymm2, ymm3/m256, ymm4 + * Conditionally copy each quadword data element of double-precision + * floating-point value from the second source operand (third operand) and the + * first source operand (second operand) depending on mask bits defined in the + * mask register operand (fourth operand). + */ +extern __m256d __cdecl _mm256_blendv_pd(__m256d, __m256d, __m256d); + +/* + * Blend Packed Single Precision Floating-Point Values + * **** VBLENDVPS ymm1, ymm2, ymm3/m256, ymm4 + * Conditionally copy each dword data element of single-precision + * floating-point value from the second source operand (third operand) and the + * first source operand (second operand) depending on mask bits defined in the + * mask register operand (fourth operand). + */ +extern __m256 __cdecl _mm256_blendv_ps(__m256, __m256, __m256); + +/* + * Divide Packed Double-Precision Floating-Point Values + * **** VDIVPD ymm1, ymm2, ymm3/m256 + * Performs an SIMD divide of the four packed double-precision floating-point + * values in the first source operand by the four packed double-precision + * floating-point values in the second source operand + */ +extern __m256d __cdecl _mm256_div_pd(__m256d, __m256d); + +/* + * Divide Packed Single-Precision Floating-Point Values + * **** VDIVPS ymm1, ymm2, ymm3/m256 + * Performs an SIMD divide of the eight packed single-precision + * floating-point values in the first source operand by the eight packed + * single-precision floating-point values in the second source operand + */ +extern __m256 __cdecl _mm256_div_ps(__m256, __m256); + +/* + * Dot Product of Packed Single-Precision Floating-Point Values + * **** VDPPS ymm1, ymm2, ymm3/m256, imm8 + * Multiplies the packed single precision floating point values in the + * first source operand with the packed single-precision floats in the + * second source. Each of the four resulting single-precision values is + * conditionally summed depending on a mask extracted from the high 4 bits + * of the immediate operand. This sum is broadcast to each of 4 positions + * in the destination if the corresponding bit of the mask selected from + * the low 4 bits of the immediate operand is "1". If the corresponding + * low bit 0-3 of the mask is zero, the destination is set to zero. + * The process is replicated for the high elements of the destination. + */ +extern __m256 __cdecl _mm256_dp_ps(__m256, __m256, const int); + +/* + * Add Horizontal Double Precision Floating-Point Values + * **** VHADDPD ymm1, ymm2, ymm3/m256 + * Adds pairs of adjacent double-precision floating-point values in the + * first source operand and second source operand and stores results in + * the destination + */ +extern __m256d __cdecl _mm256_hadd_pd(__m256d, __m256d); + +/* + * Add Horizontal Single Precision Floating-Point Values + * **** VHADDPS ymm1, ymm2, ymm3/m256 + * Adds pairs of adjacent single-precision floating-point values in the + * first source operand and second source operand and stores results in + * the destination + */ +extern __m256 __cdecl _mm256_hadd_ps(__m256, __m256); + +/* + * Subtract Horizontal Double Precision Floating-Point Values + * **** VHSUBPD ymm1, ymm2, ymm3/m256 + * Subtract pairs of adjacent double-precision floating-point values in + * the first source operand and second source operand and stores results + * in the destination + */ +extern __m256d __cdecl _mm256_hsub_pd(__m256d, __m256d); + +/* + * Subtract Horizontal Single Precision Floating-Point Values + * **** VHSUBPS ymm1, ymm2, ymm3/m256 + * Subtract pairs of adjacent single-precision floating-point values in + * the first source operand and second source operand and stores results + * in the destination. + */ +extern __m256 __cdecl _mm256_hsub_ps(__m256, __m256); + +/* + * Maximum of Packed Double Precision Floating-Point Values + * **** VMAXPD ymm1, ymm2, ymm3/m256 + * Performs an SIMD compare of the packed double-precision floating-point + * values in the first source operand and the second source operand and + * returns the maximum value for each pair of values to the destination + */ +extern __m256d __cdecl _mm256_max_pd(__m256d, __m256d); + +/* + * Maximum of Packed Single Precision Floating-Point Values + * **** VMAXPS ymm1, ymm2, ymm3/m256 + * Performs an SIMD compare of the packed single-precision floating-point + * values in the first source operand and the second source operand and + * returns the maximum value for each pair of values to the destination + */ +extern __m256 __cdecl _mm256_max_ps(__m256, __m256); + +/* + * Minimum of Packed Double Precision Floating-Point Values + * **** VMINPD ymm1, ymm2, ymm3/m256 + * Performs an SIMD compare of the packed double-precision floating-point + * values in the first source operand and the second source operand and + * returns the minimum value for each pair of values to the destination + */ +extern __m256d __cdecl _mm256_min_pd(__m256d, __m256d); + +/* + * Minimum of Packed Single Precision Floating-Point Values + * **** VMINPS ymm1, ymm2, ymm3/m256 + * Performs an SIMD compare of the packed single-precision floating-point + * values in the first source operand and the second source operand and + * returns the minimum value for each pair of values to the destination + */ +extern __m256 __cdecl _mm256_min_ps(__m256, __m256); + +/* + * Multiply Packed Double Precision Floating-Point Values + * **** VMULPD ymm1, ymm2, ymm3/m256 + * Performs a SIMD multiply of the four packed double-precision floating-point + * values from the first Source operand to the Second Source operand, and + * stores the packed double-precision floating-point results in the + * destination + */ +extern __m256d __cdecl _mm256_mul_pd(__m256d, __m256d); + +/* + * Multiply Packed Single Precision Floating-Point Values + * **** VMULPS ymm1, ymm2, ymm3/m256 + * Performs an SIMD multiply of the eight packed single-precision + * floating-point values from the first source operand to the second source + * operand, and stores the packed double-precision floating-point results in + * the destination + */ +extern __m256 __cdecl _mm256_mul_ps(__m256, __m256); + +/* + * Bitwise Logical OR of Packed Double Precision Floating-Point Values + * **** VORPD ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical OR of the four packed double-precision + * floating-point values from the first source operand and the second + * source operand, and stores the result in the destination + */ +extern __m256d __cdecl _mm256_or_pd(__m256d, __m256d); + +/* + * Bitwise Logical OR of Packed Single Precision Floating-Point Values + * **** VORPS ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical OR of the eight packed single-precision + * floating-point values from the first source operand and the second + * source operand, and stores the result in the destination + */ +extern __m256 __cdecl _mm256_or_ps(__m256, __m256); + +/* + * Shuffle Packed Double Precision Floating-Point Values + * **** VSHUFPD ymm1, ymm2, ymm3/m256, imm8 + * Moves either of the two packed double-precision floating-point values from + * each double quadword in the first source operand into the low quadword + * of each double quadword of the destination; moves either of the two packed + * double-precision floating-point values from the second source operand into + * the high quadword of each double quadword of the destination operand. + * The selector operand determines which values are moved to the destination + */ +extern __m256d __cdecl _mm256_shuffle_pd(__m256d, __m256d, const int); + +/* + * Shuffle Packed Single Precision Floating-Point Values + * **** VSHUFPS ymm1, ymm2, ymm3/m256, imm8 + * Moves two of the four packed single-precision floating-point values + * from each double qword of the first source operand into the low + * quadword of each double qword of the destination; moves two of the four + * packed single-precision floating-point values from each double qword of + * the second source operand into to the high quadword of each double qword + * of the destination. The selector operand determines which values are moved + * to the destination. + */ +extern __m256 __cdecl _mm256_shuffle_ps(__m256, __m256, const int); + +/* + * Subtract Packed Double Precision Floating-Point Values + * **** VSUBPD ymm1, ymm2, ymm3/m256 + * Performs an SIMD subtract of the four packed double-precision floating-point + * values of the second Source operand from the first Source operand, and + * stores the packed double-precision floating-point results in the destination + */ +extern __m256d __cdecl _mm256_sub_pd(__m256d, __m256d); + +/* + * Subtract Packed Single Precision Floating-Point Values + * **** VSUBPS ymm1, ymm2, ymm3/m256 + * Performs an SIMD subtract of the eight packed single-precision + * floating-point values in the second Source operand from the First Source + * operand, and stores the packed single-precision floating-point results in + * the destination + */ +extern __m256 __cdecl _mm256_sub_ps(__m256, __m256); + +/* + * Bitwise Logical XOR of Packed Double Precision Floating-Point Values + * **** VXORPD ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical XOR of the four packed double-precision + * floating-point values from the first source operand and the second + * source operand, and stores the result in the destination + */ +extern __m256d __cdecl _mm256_xor_pd(__m256d, __m256d); + +/* + * Bitwise Logical XOR of Packed Single Precision Floating-Point Values + * **** VXORPS ymm1, ymm2, ymm3/m256 + * Performs a bitwise logical XOR of the eight packed single-precision + * floating-point values from the first source operand and the second + * source operand, and stores the result in the destination + */ +extern __m256 __cdecl _mm256_xor_ps(__m256, __m256); + +/* + * Compare Packed Double-Precision Floating-Point Values + * **** VCMPPD xmm1, xmm2, xmm3/m128, imm8 + * **** VCMPPD ymm1, ymm2, ymm3/m256, imm8 + * Performs an SIMD compare of the four packed double-precision floating-point + * values in the second source operand (third operand) and the first source + * operand (second operand) and returns the results of the comparison to the + * destination operand (first operand). The comparison predicate operand + * (immediate) specifies the type of comparison performed on each of the pairs + * of packed values. + * For 128-bit intrinsic function with compare predicate values in range 0-7 + * compiler may generate SSE2 instructions if it is warranted for performance + * reasons. + */ +extern __m128d __cdecl _mm_cmp_pd(__m128d, __m128d, const int); +extern __m256d __cdecl _mm256_cmp_pd(__m256d, __m256d, const int); + +/* + * Compare Packed Single-Precision Floating-Point Values + * **** VCMPPS xmm1, xmm2, xmm3/m256, imm8 + * **** VCMPPS ymm1, ymm2, ymm3/m256, imm8 + * Performs a SIMD compare of the packed single-precision floating-point values + * in the second source operand (third operand) and the first source operand + * (second operand) and returns the results of the comparison to the + * destination operand (first operand). The comparison predicate operand + * (immediate) specifies the type of comparison performed on each of the pairs + * of packed values. + * For 128-bit intrinsic function with compare predicate values in range 0-7 + * compiler may generate SSE2 instructions if it is warranted for performance + * reasons. + */ +extern __m128 __cdecl _mm_cmp_ps(__m128, __m128, const int); +extern __m256 __cdecl _mm256_cmp_ps(__m256, __m256, const int); + +/* + * Compare Scalar Double-Precision Floating-Point Values + * **** VCMPSD xmm1, xmm2, xmm3/m64, imm8 + * Compares the low double-precision floating-point values in the second source + * operand (third operand) and the first source operand (second operand) and + * returns the results in of the comparison to the destination operand (first + * operand). The comparison predicate operand (immediate operand) specifies the + * type of comparison performed. + * For compare predicate values in range 0-7 compiler may generate SSE2 + * instructions if it is warranted for performance reasons. + */ +extern __m128d __cdecl _mm_cmp_sd(__m128d, __m128d, const int); + +/* + * Compare Scalar Single-Precision Floating-Point Values + * **** VCMPSS xmm1, xmm2, xmm3/m64, imm8 + * Compares the low single-precision floating-point values in the second source + * operand (third operand) and the first source operand (second operand) and + * returns the results of the comparison to the destination operand (first + * operand). The comparison predicate operand (immediate operand) specifies + * the type of comparison performed. + * For compare predicate values in range 0-7 compiler may generate SSE2 + * instructions if it is warranted for performance reasons. + */ +extern __m128 __cdecl _mm_cmp_ss(__m128, __m128, const int); + +/* + * Convert Packed Doubleword Integers to + * Packed Double-Precision Floating-Point Values + * **** VCVTDQ2PD ymm1, xmm2/m128 + * Converts four packed signed doubleword integers in the source operand to + * four packed double-precision floating-point values in the destination + */ +extern __m256d __cdecl _mm256_cvtepi32_pd(__m128i); + +/* + * Convert Packed Doubleword Integers to + * Packed Single-Precision Floating-Point Values + * **** VCVTDQ2PS ymm1, ymm2/m256 + * Converts eight packed signed doubleword integers in the source operand to + * eight packed double-precision floating-point values in the destination + */ +extern __m256 __cdecl _mm256_cvtepi32_ps(__m256i); + +/* + * Convert Packed Double-Precision Floating-point values to + * Packed Single-Precision Floating-Point Values + * **** VCVTPD2PS xmm1, ymm2/m256 + * Converts four packed double-precision floating-point values in the source + * operand to four packed single-precision floating-point values in the + * destination + */ +extern __m128 __cdecl _mm256_cvtpd_ps(__m256d); + +/* + * Convert Packed Single Precision Floating-Point Values to + * Packed Singed Doubleword Integer Values + * **** VCVTPS2DQ ymm1, ymm2/m256 + * Converts eight packed single-precision floating-point values in the source + * operand to eight signed doubleword integers in the destination + */ +extern __m256i __cdecl _mm256_cvtps_epi32(__m256); + +/* + * Convert Packed Single Precision Floating-point values to + * Packed Double Precision Floating-Point Values + * **** VCVTPS2PD ymm1, xmm2/m128 + * Converts four packed single-precision floating-point values in the source + * operand to four packed double-precision floating-point values in the + * destination + */ +extern __m256d __cdecl _mm256_cvtps_pd(__m128); + +/* + * Convert with Truncation Packed Double-Precision Floating-Point values to + * Packed Doubleword Integers + * **** VCVTTPD2DQ xmm1, ymm2/m256 + * Converts four packed double-precision floating-point values in the source + * operand to four packed signed doubleword integers in the destination. + * When a conversion is inexact, a truncated (round toward zero) value is + * returned. If a converted result is larger than the maximum signed doubleword + * integer, the floating-point invalid exception is raised, and if this + * exception is masked, the indefinite integer value (80000000H) is returned +*/ +extern __m128i __cdecl _mm256_cvttpd_epi32(__m256d); + +/* + * Convert Packed Double-Precision Floating-point values to + * Packed Doubleword Integers + * **** VCVTPD2DQ xmm1, ymm2/m256 + * Converts four packed double-precision floating-point values in the source + * operand to four packed signed doubleword integers in the destination + */ +extern __m128i __cdecl _mm256_cvtpd_epi32(__m256d); + +/* + * Convert with Truncation Packed Single Precision Floating-Point Values to + * Packed Singed Doubleword Integer Values + * **** VCVTTPS2DQ ymm1, ymm2/m256 + * Converts eight packed single-precision floating-point values in the source + * operand to eight signed doubleword integers in the destination. + * When a conversion is inexact, a truncated (round toward zero) value is + * returned. If a converted result is larger than the maximum signed doubleword + * integer, the floating-point invalid exception is raised, and if this + * exception is masked, the indefinite integer value (80000000H) is returned + */ +extern __m256i __cdecl _mm256_cvttps_epi32(__m256); + +/* + * Extract packed floating-point values + * **** VEXTRACTF128 xmm1/m128, ymm2, imm8 + * Extracts 128-bits of packed floating-point values from the source operand + * at an 128-bit offset from imm8[0] into the destination + */ +extern __m128 __cdecl _mm256_extractf128_ps(__m256, const int); +extern __m128d __cdecl _mm256_extractf128_pd(__m256d, const int); +extern __m128i __cdecl _mm256_extractf128_si256(__m256i, const int); + +/* + * Zero All YMM registers + * **** VZEROALL + * Zeros contents of all YMM registers + */ +extern void __cdecl _mm256_zeroall(void); + +/* + * Zero Upper bits of YMM registers + * **** VZEROUPPER + * Zeros the upper 128 bits of all YMM registers. The lower 128-bits of the + * registers (the corresponding XMM registers) are unmodified + */ +extern void __cdecl _mm256_zeroupper(void); + +/* + * Permute Single-Precision Floating-Point Values + * **** VPERMILPS ymm1, ymm2, ymm3/m256 + * **** VPERMILPS xmm1, xmm2, xmm3/m128 + * Permute Single-Precision Floating-Point values in the first source operand + * using 8-bit control fields in the low bytes of corresponding elements the + * shuffle control and store results in the destination + */ +extern __m256 __cdecl _mm256_permutevar_ps(__m256, __m256i); +extern __m128 __cdecl _mm_permutevar_ps(__m128, __m128i); + +/* + * Permute Single-Precision Floating-Point Values + * **** VPERMILPS ymm1, ymm2/m256, imm8 + * **** VPERMILPS xmm1, xmm2/m128, imm8 + * Permute Single-Precision Floating-Point values in the first source operand + * using four 2-bit control fields in the 8-bit immediate and store results + * in the destination + */ +extern __m256 __cdecl _mm256_permute_ps(__m256, int); +extern __m128 __cdecl _mm_permute_ps(__m128, int); + +/* + * Permute Double-Precision Floating-Point Values + * **** VPERMILPD ymm1, ymm2, ymm3/m256 + * **** VPERMILPD xmm1, xmm2, xmm3/m128 + * Permute Double-Precision Floating-Point values in the first source operand + * using 8-bit control fields in the low bytes of the second source operand + * and store results in the destination + */ +extern __m256d __cdecl _mm256_permutevar_pd(__m256d, __m256i); +extern __m128d __cdecl _mm_permutevar_pd(__m128d, __m128i); + +/* + * Permute Double-Precision Floating-Point Values + * **** VPERMILPD ymm1, ymm2/m256, imm8 + * **** VPERMILPD xmm1, xmm2/m128, imm8 + * Permute Double-Precision Floating-Point values in the first source operand + * using two, 1-bit control fields in the low 2 bits of the 8-bit immediate + * and store results in the destination + */ +extern __m256d __cdecl _mm256_permute_pd(__m256d, int); +extern __m128d __cdecl _mm_permute_pd(__m128d, int); + +/* + * Permute Floating-Point Values + * **** VPERM2F128 ymm1, ymm2, ymm3/m256, imm8 + * Permute 128 bit floating-point-containing fields from the first source + * operand and second source operand using bits in the 8-bit immediate and + * store results in the destination + */ +extern __m256 __cdecl _mm256_permute2f128_ps(__m256, __m256, int); +extern __m256d __cdecl _mm256_permute2f128_pd(__m256d, __m256d, int); +extern __m256i __cdecl _mm256_permute2f128_si256(__m256i, __m256i, int); + +/* + * Load with Broadcast + * **** VBROADCASTSS ymm1, m32 + * **** VBROADCASTSS xmm1, m32 + * Load floating point values from the source operand and broadcast to all + * elements of the destination + */ +extern __m256 __cdecl _mm256_broadcast_ss(float const *); +extern __m128 __cdecl _mm_broadcast_ss(float const *); + +/* + * Load with Broadcast + * **** VBROADCASTSD ymm1, m64 + * Load floating point values from the source operand and broadcast to all + * elements of the destination + */ +extern __m256d __cdecl _mm256_broadcast_sd(double const *); + +/* + * Load with Broadcast + * **** VBROADCASTF128 ymm1, m128 + * Load floating point values from the source operand and broadcast to all + * elements of the destination + */ +extern __m256 __cdecl _mm256_broadcast_ps(__m128 const *); +extern __m256d __cdecl _mm256_broadcast_pd(__m128d const *); + +/* + * Insert packed floating-point values + * **** VINSERTF128 ymm1, ymm2, xmm3/m128, imm8 + * Performs an insertion of 128-bits of packed floating-point values from the + * second source operand into an the destination at an 128-bit offset from + * imm8[0]. The remaining portions of the destination are written by the + * corresponding fields of the first source operand + */ +extern __m256 __cdecl _mm256_insertf128_ps(__m256, __m128, int); +extern __m256d __cdecl _mm256_insertf128_pd(__m256d, __m128d, int); +extern __m256i __cdecl _mm256_insertf128_si256(__m256i, __m128i, int); + +/* + * Move Aligned Packed Double-Precision Floating-Point Values + * **** VMOVAPD ymm1, m256 + * **** VMOVAPD m256, ymm1 + * Moves 4 double-precision floating-point values from the source operand to + * the destination + */ +extern __m256d __cdecl _mm256_load_pd(double const *); +extern void __cdecl _mm256_store_pd(double *, __m256d); + +/* + * Move Aligned Packed Single-Precision Floating-Point Values + * **** VMOVAPS ymm1, m256 + * **** VMOVAPS m256, ymm1 + * Moves 8 single-precision floating-point values from the source operand to + * the destination + */ +extern __m256 __cdecl _mm256_load_ps(float const *); +extern void __cdecl _mm256_store_ps(float *, __m256); + +/* + * Move Unaligned Packed Double-Precision Floating-Point Values + * **** VMOVUPD ymm1, m256 + * **** VMOVUPD m256, ymm1 + * Moves 256 bits of packed double-precision floating-point values from the + * source operand to the destination + */ +extern __m256d __cdecl _mm256_loadu_pd(double const *); +extern void __cdecl _mm256_storeu_pd(double *, __m256d); + +/* + * Move Unaligned Packed Single-Precision Floating-Point Values + * **** VMOVUPS ymm1, m256 + * **** VMOVUPS m256, ymm1 + * Moves 256 bits of packed single-precision floating-point values from the + * source operand to the destination + */ +extern __m256 __cdecl _mm256_loadu_ps(float const *); +extern void __cdecl _mm256_storeu_ps(float *, __m256); + +/* + * Move Aligned Packed Integer Values + * **** VMOVDQA ymm1, m256 + * **** VMOVDQA m256, ymm1 + * Moves 256 bits of packed integer values from the source operand to the + * destination + */ +extern __m256i __cdecl _mm256_load_si256(__m256i const *); +extern void __cdecl _mm256_store_si256(__m256i *, __m256i); + +/* + * Move Unaligned Packed Integer Values + * **** VMOVDQU ymm1, m256 + * **** VMOVDQU m256, ymm1 + * Moves 256 bits of packed integer values from the source operand to the + * destination + */ +extern __m256i __cdecl _mm256_loadu_si256(__m256i const *); +extern void __cdecl _mm256_storeu_si256(__m256i *, __m256i); + +/* + * Load Two Unaligned Packed 128-bit Values + * Loads two potentially unaligned 128-bit values + * and combines them into one 256-bit value. + * + * The data types here (float const*, double const* and __m128i const*) + * were chosen for consistency with the underlying _mm_loadu_{ps,pd,si128} + * intrinsics. + */ + +#define _mm256_loadu2_m128(/* float const* */ hiaddr, \ + /* float const* */ loaddr) \ + _mm256_set_m128(_mm_loadu_ps(hiaddr), _mm_loadu_ps(loaddr)) + +#define _mm256_loadu2_m128d(/* double const* */ hiaddr, \ + /* double const* */ loaddr) \ + _mm256_set_m128d(_mm_loadu_pd(hiaddr), _mm_loadu_pd(loaddr)) + +#define _mm256_loadu2_m128i(/* __m128i const* */ hiaddr, \ + /* __m128i const* */ loaddr) \ + _mm256_set_m128i(_mm_loadu_si128(hiaddr), _mm_loadu_si128(loaddr)) + +/* + * Store 256-bit Value To Two Unaligned 128-bit Locations + * Stores the high and low 128-bit halves of a 256-bit value + * to two different potentially unaligned addresses. + */ + +#define _mm256_storeu2_m128(/* float* */ hiaddr, /* float* */ loaddr, \ + /* __m256 */ a) \ + do { \ + __m256 _a = (a); /* reference a only once in macro body */ \ + _mm_storeu_ps((loaddr), _mm256_castps256_ps128(_a)); \ + _mm_storeu_ps((hiaddr), _mm256_extractf128_ps(_a, 0x1)); \ + } while (0) + +#define _mm256_storeu2_m128d(/* double* */ hiaddr, /* double* */ loaddr, \ + /* __m256d */ a) \ + do { \ + __m256d _a = (a); /* reference a only once in macro body */ \ + _mm_storeu_pd((loaddr), _mm256_castpd256_pd128(_a)); \ + _mm_storeu_pd((hiaddr), _mm256_extractf128_pd(_a, 0x1)); \ + } while (0) + +#define _mm256_storeu2_m128i(/* __m128i* */ hiaddr, /* __m128i* */ loaddr, \ + /* __m256i */ a) \ + do { \ + __m256i _a = (a); /* reference a only once in macro body */ \ + _mm_storeu_si128((loaddr), _mm256_castsi256_si128(_a)); \ + _mm_storeu_si128((hiaddr), _mm256_extractf128_si256(_a, 0x1)); \ + } while (0) + +/* + * Conditional SIMD Packed Loads and Stores + * **** VMASKMOVPD xmm1, xmm2, m128 + * **** VMASKMOVPD ymm1, ymm2, m256 + * **** VMASKMOVPD m128, xmm1, xmm2 + * **** VMASKMOVPD m256, ymm1, ymm2 + * + * Load forms: + * Load packed values from the 128-bit (XMM forms) or 256-bit (YMM forms) + * memory location (third operand) into the destination XMM or YMM register + * (first operand) using a mask in the first source operand (second operand). + * + * Store forms: + * Stores packed values from the XMM or YMM register in the second source + * operand (third operand) into the 128-bit (XMM forms) or 256-bit (YMM forms) + * memory location using a mask in first source operand (second operand). + * Stores are atomic. + */ +extern __m256d __cdecl _mm256_maskload_pd(double const *, __m256i); +extern void __cdecl _mm256_maskstore_pd(double *, __m256i, __m256d); +extern __m128d __cdecl _mm_maskload_pd(double const *, __m128i); +extern void __cdecl _mm_maskstore_pd(double *, __m128i, __m128d); + +/* + * Conditional SIMD Packed Loads and Stores + * **** VMASKMOVPS xmm1, xmm2, m128 + * **** VMASKMOVPS ymm1, ymm2, m256 + * **** VMASKMOVPS m128, xmm1, xmm2 + * **** VMASKMOVPS m256, ymm1, ymm2 + * + * Load forms: + * Load packed values from the 128-bit (XMM forms) or 256-bit (YMM forms) + * memory location (third operand) into the destination XMM or YMM register + * (first operand) using a mask in the first source operand (second operand). + * + * Store forms: + * Stores packed values from the XMM or YMM register in the second source + * operand (third operand) into the 128-bit (XMM forms) or 256-bit (YMM forms) + * memory location using a mask in first source operand (second operand). + * Stores are atomic. + */ +extern __m256 __cdecl _mm256_maskload_ps(float const *, __m256i); +extern void __cdecl _mm256_maskstore_ps(float *, __m256i, __m256); +extern __m128 __cdecl _mm_maskload_ps(float const *, __m128i); +extern void __cdecl _mm_maskstore_ps(float *, __m128i, __m128); + +/* + * Replicate Single-Precision Floating-Point Values + * **** VMOVSHDUP ymm1, ymm2/m256 + * Duplicates odd-indexed single-precision floating-point values from the + * source operand + */ +extern __m256 __cdecl _mm256_movehdup_ps(__m256); + +/* + * Replicate Single-Precision Floating-Point Values + * **** VMOVSLDUP ymm1, ymm2/m256 + * Duplicates even-indexed single-precision floating-point values from the + * source operand + */ +extern __m256 __cdecl _mm256_moveldup_ps(__m256); + +/* + * Replicate Double-Precision Floating-Point Values + * **** VMOVDDUP ymm1, ymm2/m256 + * Duplicates even-indexed double-precision floating-point values from the + * source operand + */ +extern __m256d __cdecl _mm256_movedup_pd(__m256d); + +/* + * Move Unaligned Integer + * **** VLDDQU ymm1, m256 + * The instruction is functionally similar to VMOVDQU YMM, m256 for loading + * from memory. That is: 32 bytes of data starting at an address specified by + * the source memory operand are fetched from memory and placed in a + * destination + */ +extern __m256i __cdecl _mm256_lddqu_si256(__m256i const *); + +/* + * Store Packed Integers Using Non-Temporal Hint + * **** VMOVNTDQ m256, ymm1 + * Moves the packed integers in the source operand to the destination using a + * non-temporal hint to prevent caching of the data during the write to memory + */ +extern void __cdecl _mm256_stream_si256(__m256i *, __m256i); + +/* + * Store Packed Double-Precision Floating-Point Values Using Non-Temporal Hint + * **** VMOVNTPD m256, ymm1 + * Moves the packed double-precision floating-point values in the source + * operand to the destination operand using a non-temporal hint to prevent + * caching of the data during the write to memory + */ +extern void __cdecl _mm256_stream_pd(double *, __m256d); + +/* + * Store Packed Single-Precision Floating-Point Values Using Non-Temporal Hint + * **** VMOVNTPS m256, ymm1 + * Moves the packed single-precision floating-point values in the source + * operand to the destination operand using a non-temporal hint to prevent + * caching of the data during the write to memory + */ +extern void __cdecl _mm256_stream_ps(float *, __m256); + +/* + * Compute Approximate Reciprocals of Packed Single-Precision Floating-Point + * Values + * **** VRCPPS ymm1, ymm2/m256 + * Performs an SIMD computation of the approximate reciprocals of the eight + * packed single precision floating-point values in the source operand and + * stores the packed single-precision floating-point results in the destination + */ +extern __m256 __cdecl _mm256_rcp_ps(__m256); + +/* + * Compute Approximate Reciprocals of Square Roots of + * Packed Single-Precision Floating-point Values + * **** VRSQRTPS ymm1, ymm2/m256 + * Performs an SIMD computation of the approximate reciprocals of the square + * roots of the eight packed single precision floating-point values in the + * source operand and stores the packed single-precision floating-point results + * in the destination + */ +extern __m256 __cdecl _mm256_rsqrt_ps(__m256); + +/* + * Square Root of Double-Precision Floating-Point Values + * **** VSQRTPD ymm1, ymm2/m256 + * Performs an SIMD computation of the square roots of the two or four packed + * double-precision floating-point values in the source operand and stores + * the packed double-precision floating-point results in the destination + */ +extern __m256d __cdecl _mm256_sqrt_pd(__m256d); + +/* + * Square Root of Single-Precision Floating-Point Values + * **** VSQRTPS ymm1, ymm2/m256 + * Performs an SIMD computation of the square roots of the eight packed + * single-precision floating-point values in the source operand stores the + * packed double-precision floating-point results in the destination + */ +extern __m256 __cdecl _mm256_sqrt_ps(__m256); + +/* + * Round Packed Double-Precision Floating-Point Values + * **** VROUNDPD ymm1,ymm2/m256,imm8 + * Round the four Double-Precision Floating-Point Values values in the source + * operand by the rounding mode specified in the immediate operand and place + * the result in the destination. The rounding process rounds the input to an + * integral value and returns the result as a double-precision floating-point + * value. The Precision Floating Point Exception is signaled according to the + * immediate operand. If any source operand is an SNaN then it will be + * converted to a QNaN. + */ +extern __m256d __cdecl _mm256_round_pd(__m256d, int); +#define _mm256_ceil_pd(val) _mm256_round_pd((val), _MM_FROUND_CEIL) +#define _mm256_floor_pd(val) _mm256_round_pd((val), _MM_FROUND_FLOOR) + +/* + * Round Packed Single-Precision Floating-Point Values + * **** VROUNDPS ymm1,ymm2/m256,imm8 + * Round the four single-precision floating-point values values in the source + * operand by the rounding mode specified in the immediate operand and place + * the result in the destination. The rounding process rounds the input to an + * integral value and returns the result as a double-precision floating-point + * value. The Precision Floating Point Exception is signaled according to the + * immediate operand. If any source operand is an SNaN then it will be + * converted to a QNaN. + */ +extern __m256 __cdecl _mm256_round_ps(__m256, int); +#define _mm256_ceil_ps(val) _mm256_round_ps((val), _MM_FROUND_CEIL) +#define _mm256_floor_ps(val) _mm256_round_ps((val), _MM_FROUND_FLOOR) + +/* + * Unpack and Interleave High Packed Double-Precision Floating-Point Values + * **** VUNPCKHPD ymm1,ymm2,ymm3/m256 + * Performs an interleaved unpack of the high double-precision floating-point + * values from the first source operand and the second source operand. + */ +extern __m256d __cdecl _mm256_unpackhi_pd(__m256d, __m256d); + +/* + * Unpack and Interleave High Packed Single-Precision Floating-Point Values + * **** VUNPCKHPS ymm1,ymm2,ymm3 + * Performs an interleaved unpack of the high single-precision floating-point + * values from the first source operand and the second source operand + */ +extern __m256 __cdecl _mm256_unpackhi_ps(__m256, __m256); + +/* + * Unpack and Interleave Low Packed Double-Precision Floating-Point Values + * **** VUNPCKLPD ymm1,ymm2,ymm3/m256 + * Performs an interleaved unpack of the low double-precision floating-point + * values from the first source operand and the second source operand + */ +extern __m256d __cdecl _mm256_unpacklo_pd(__m256d, __m256d); + +/* + * Unpack and Interleave Low Packed Single-Precision Floating-Point Values + * **** VUNPCKLPS ymm1,ymm2,ymm3 + * Performs an interleaved unpack of the low single-precision floating-point + * values from the first source operand and the second source operand + */ +extern __m256 __cdecl _mm256_unpacklo_ps(__m256, __m256); + +/* + * Packed Bit Test + * **** VPTEST ymm1, ymm2/m256 + * VPTEST set the ZF flag if all bits in the result are 0 of the bitwise AND + * of the first source operand and the second source operand. VPTEST sets the + * CF flag if all bits in the result are 0 of the bitwise AND of the second + * source operand and the logical NOT of the first source operand. + */ +extern int __cdecl _mm256_testz_si256(__m256i, __m256i); +#define _mm256_test_all_zeros(mask, val) \ + _mm256_testz_si256((mask), (val)) + +extern int __cdecl _mm256_testc_si256(__m256i, __m256i); +#define _mm256_test_all_ones(val) \ + _mm256_testc_si256((val), _mm256_cmpeq_epi32((val),(val))) + +extern int __cdecl _mm256_testnzc_si256(__m256i, __m256i); +#define _mm256_test_mix_ones_zeros(mask, val) \ + _mm256_testnzc_si256((mask), (val)) + +/* + * Packed Bit Test + * **** VTESTPD ymm1, ymm2/m256 + * **** VTESTPD xmm1, xmm2/m128 + * VTESTPD performs a bitwise comparison of all the sign bits of the + * double-precision elements in the first source operation and corresponding + * sign bits in the second source operand. If the AND of the two sets of bits + * produces all zeros, the ZF is set else the ZF is clear. If the AND NOT of + * the source sign bits with the dest sign bits produces all zeros the CF is + * set else the CF is clear + */ +extern int __cdecl _mm256_testz_pd(__m256d, __m256d); +extern int __cdecl _mm256_testc_pd(__m256d, __m256d); +extern int __cdecl _mm256_testnzc_pd(__m256d, __m256d); +extern int __cdecl _mm_testz_pd(__m128d, __m128d); +extern int __cdecl _mm_testc_pd(__m128d, __m128d); +extern int __cdecl _mm_testnzc_pd(__m128d, __m128d); + +/* + * Packed Bit Test + * **** VTESTPS ymm1, ymm2/m256 + * **** VTESTPS xmm1, xmm2/m128 + * VTESTPS performs a bitwise comparison of all the sign bits of the packed + * single-precision elements in the first source operation and corresponding + * sign bits in the second source operand. If the AND of the two sets of bits + * produces all zeros, the ZF is set else the ZF is clear. If the AND NOT of + * the source sign bits with the dest sign bits produces all zeros the CF is + * set else the CF is clear + */ +extern int __cdecl _mm256_testz_ps(__m256, __m256); +extern int __cdecl _mm256_testc_ps(__m256, __m256); +extern int __cdecl _mm256_testnzc_ps(__m256, __m256); +extern int __cdecl _mm_testz_ps(__m128, __m128); +extern int __cdecl _mm_testc_ps(__m128, __m128); +extern int __cdecl _mm_testnzc_ps(__m128, __m128); + +/* + * Extract Double-Precision Floating-Point Sign mask + * **** VMOVMSKPD r32, ymm2 + * Extracts the sign bits from the packed double-precision floating-point + * values in the source operand, formats them into a 4-bit mask, and stores + * the mask in the destination + */ +extern int __cdecl _mm256_movemask_pd(__m256d); + +/* + * Extract Single-Precision Floating-Point Sign mask + * **** VMOVMSKPS r32, ymm2 + * Extracts the sign bits from the packed single-precision floating-point + * values in the source operand, formats them into a 8-bit mask, and stores + * the mask in the destination + */ +extern int __cdecl _mm256_movemask_ps(__m256); + +/* + * Return 256-bit vector with all elements set to 0 + */ +extern __m256d __cdecl _mm256_setzero_pd(void); +extern __m256 __cdecl _mm256_setzero_ps(void); +extern __m256i __cdecl _mm256_setzero_si256(void); + +/* + * Return 256-bit vector initialized to specified arguments + */ +extern __m256d __cdecl _mm256_set_pd(double, double, double, double); +extern __m256 __cdecl _mm256_set_ps(float, float, float, float, + float, float, float, float); +extern __m256i __cdecl _mm256_set_epi8(char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char); +extern __m256i __cdecl _mm256_set_epi16(short, short, short, short, + short, short, short, short, + short, short, short, short, + short, short, short, short); +extern __m256i __cdecl _mm256_set_epi32(int, int, int, int, + int, int, int, int); +extern __m256i __cdecl _mm256_set_epi64x(__int64, __int64, + __int64, __int64); + +#define _mm256_set_m128(/* __m128 */ hi, /* __m128 */ lo) \ + _mm256_insertf128_ps(_mm256_castps128_ps256(lo), (hi), 0x1) + +#define _mm256_set_m128d(/* __m128d */ hi, /* __m128d */ lo) \ + _mm256_insertf128_pd(_mm256_castpd128_pd256(lo), (hi), 0x1) + +#define _mm256_set_m128i(/* __m128i */ hi, /* __m128i */ lo) \ + _mm256_insertf128_si256(_mm256_castsi128_si256(lo), (hi), 0x1) + +extern __m256d __cdecl _mm256_setr_pd(double, double, double, double); +extern __m256 __cdecl _mm256_setr_ps(float, float, float, float, + float, float, float, float); +extern __m256i __cdecl _mm256_setr_epi8(char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char, + char, char, char, char); +extern __m256i __cdecl _mm256_setr_epi16(short, short, short, short, + short, short, short, short, + short, short, short, short, + short, short, short, short); +extern __m256i __cdecl _mm256_setr_epi32(int, int, int, int, + int, int, int, int); +extern __m256i __cdecl _mm256_setr_epi64x(__int64, __int64, + __int64, __int64); +#define _mm256_setr_m128(lo, hi) _mm256_set_m128((hi), (lo)) +#define _mm256_setr_m128d(lo, hi) _mm256_set_m128d((hi), (lo)) +#define _mm256_setr_m128i(lo, hi) _mm256_set_m128i((hi), (lo)) + +/* + * Return 256-bit vector with all elements initialized to specified scalar + */ +extern __m256d __cdecl _mm256_set1_pd(double); +extern __m256 __cdecl _mm256_set1_ps(float); +extern __m256i __cdecl _mm256_set1_epi8(char); +extern __m256i __cdecl _mm256_set1_epi16(short); +extern __m256i __cdecl _mm256_set1_epi32(int); +extern __m256i __cdecl _mm256_set1_epi64x(long long); + +/* + * Support intrinsic functions to do vector type casts. These functions do + * not introduce extra moves to generated code. When cast is done from a 128 + * to 256-bit type the low 128 bits of the 256-bit result contain source + * parameter value; the upper 128 bits of the result are undefined. + */ +extern __m256 __cdecl _mm256_castpd_ps(__m256d); +extern __m256d __cdecl _mm256_castps_pd(__m256); +extern __m256i __cdecl _mm256_castps_si256(__m256); +extern __m256i __cdecl _mm256_castpd_si256(__m256d); +extern __m256 __cdecl _mm256_castsi256_ps(__m256i); +extern __m256d __cdecl _mm256_castsi256_pd(__m256i); +extern __m128 __cdecl _mm256_castps256_ps128(__m256); +extern __m128d __cdecl _mm256_castpd256_pd128(__m256d); +extern __m128i __cdecl _mm256_castsi256_si128(__m256i); +extern __m256 __cdecl _mm256_castps128_ps256(__m128); +extern __m256d __cdecl _mm256_castpd128_pd256(__m128d); +extern __m256i __cdecl _mm256_castsi128_si256(__m128i); + + +/* + * Support for half-float conversions to/from normal float. + * Immediate argument is used for special MXCSR overrides. + */ +extern __m128 __cdecl _mm_cvtph_ps(__m128i); +extern __m256 __cdecl _mm256_cvtph_ps(__m128i); +extern __m128i __cdecl _mm_cvtps_ph(__m128 m1, const int imm); +extern __m128i __cdecl _mm256_cvtps_ph(__m256, int); + +/* + * Return a vector with all elements set to zero. It is recommended to use the + * result of this intrinsic as an input argument to another intrinsic when the + * initial value is irrelevant. + */ +#define _mm_undefined_ps _mm_setzero_ps +#define _mm_undefined_pd _mm_setzero_pd +#define _mm_undefined_si128 _mm_setzero_si128 +#define _mm256_undefined_ps _mm256_setzero_ps +#define _mm256_undefined_pd _mm256_setzero_pd +#define _mm256_undefined_si256 _mm256_setzero_si256 + +/* + * The list of extended control registers. + * Currently, the list includes only one register. + */ +#define _XCR_XFEATURE_ENABLED_MASK 0 + +/* Returns the content of the specified extended control register */ +extern unsigned __int64 __cdecl _xgetbv(unsigned int); + +/* Writes the value to the specified extended control register */ +extern void __cdecl _xsetbv(unsigned int, unsigned __int64); + + +/* + * Performs a full or partial save of the enabled processor state components + * using the specified memory address location and a mask. + */ +extern void __cdecl _xsave(void *, unsigned __int64); +#if defined (_M_X64) +extern void __cdecl _xsave64(void *, unsigned __int64); +#endif /* defined (_M_X64) */ + +/* + * Performs a full or partial save of the enabled processor state components + * using the specified memory address location and a mask. + * Optimize the state save operation if possible. + */ +extern void __cdecl _xsaveopt(void *, unsigned __int64); +#if defined (_M_X64) +extern void __cdecl _xsaveopt64(void *, unsigned __int64); +#endif /* defined (_M_X64) */ + +/* + * Performs a full or partial restore of the enabled processor states + * using the state information stored in the specified memory address location + * and a mask. + */ +extern void __cdecl _xrstor(void const *, unsigned __int64); +#if defined (_M_X64) +extern void __cdecl _xrstor64(void const *, unsigned __int64); +#endif /* defined (_M_X64) */ + +/* + * Saves the current state of the x87 FPU, MMX technology, XMM, + * and MXCSR registers to the specified 512-byte memory location. + */ +extern void __cdecl _fxsave(void *); +#if defined (_M_X64) +extern void __cdecl _fxsave64(void *); +#endif /* defined (_M_X64) */ + +/* + * Restore the current state of the x87 FPU, MMX technology, XMM, + * and MXCSR registers from the specified 512-byte memory location. + */ +extern void __cdecl _fxrstor(void const *); +#if defined (_M_X64) +extern void __cdecl _fxrstor64(void const *); +#endif /* defined (_M_X64) */ + +/* + * Perform one attempt to generate a hardware generated random value. + * The generated value is written to the given memory location and the success + * status is returned: 1 if the hardware could generate a valid random number + * and 0 otherwise. + */ +extern int __cdecl _rdrand16_step(unsigned short *); +extern int __cdecl _rdrand32_step(unsigned int *); +#if defined (_M_X64) +extern int __cdecl _rdrand64_step(unsigned __int64 *); +#endif /* defined (_M_X64) */ + +#if defined (_M_X64) +/* + * Return the value of the FS/GS segment base register. + */ +extern unsigned int __cdecl _readfsbase_u32(); +extern unsigned int __cdecl _readgsbase_u32(); +extern unsigned __int64 __cdecl _readfsbase_u64(); +extern unsigned __int64 __cdecl _readgsbase_u64(); + +/* + * Write the value to the FS/GS segment base register. + */ +extern void __cdecl _writefsbase_u32(unsigned int); +extern void __cdecl _writegsbase_u32(unsigned int); +extern void __cdecl _writefsbase_u64(unsigned __int64); +extern void __cdecl _writegsbase_u64(unsigned __int64); +#endif /* defined (_M_X64) */ + +/* + * Perform FMA (Fused Multiply-and-Add) operations. + */ +extern __m128 __cdecl _mm_fmadd_ps(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fmadd_pd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fmadd_ss(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fmadd_sd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fmsub_ps(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fmsub_pd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fmsub_ss(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fmsub_sd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fnmadd_ps(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fnmadd_pd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fnmadd_ss(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fnmadd_sd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fnmsub_ps(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fnmsub_pd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fnmsub_ss(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fnmsub_sd(__m128d, __m128d, __m128d); + +extern __m256 __cdecl _mm256_fmadd_ps(__m256, __m256, __m256); +extern __m256d __cdecl _mm256_fmadd_pd(__m256d, __m256d, __m256d); +extern __m256 __cdecl _mm256_fmsub_ps(__m256, __m256, __m256); +extern __m256d __cdecl _mm256_fmsub_pd(__m256d, __m256d, __m256d); +extern __m256 __cdecl _mm256_fnmadd_ps(__m256, __m256, __m256); +extern __m256d __cdecl _mm256_fnmadd_pd(__m256d, __m256d, __m256d); +extern __m256 __cdecl _mm256_fnmsub_ps(__m256, __m256, __m256); +extern __m256d __cdecl _mm256_fnmsub_pd(__m256d, __m256d, __m256d); + + +/* + * Fused Multiply-and-Add/Subtract__and Multiply-and-Subtract/Add operations. + */ +extern __m128 __cdecl _mm_fmaddsub_ps(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fmaddsub_pd(__m128d, __m128d, __m128d); +extern __m128 __cdecl _mm_fmsubadd_ps(__m128, __m128, __m128); +extern __m128d __cdecl _mm_fmsubadd_pd(__m128d, __m128d, __m128d); + +extern __m256 __cdecl _mm256_fmaddsub_ps(__m256, __m256, __m256); +extern __m256d __cdecl _mm256_fmaddsub_pd(__m256d, __m256d, __m256d); +extern __m256 __cdecl _mm256_fmsubadd_ps(__m256, __m256, __m256); +extern __m256d __cdecl _mm256_fmsubadd_pd(__m256d, __m256d, __m256d); + + +/* + * Integer 256-bit vector comparison operations. + */ +extern __m256i __cdecl _mm256_cmpeq_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_cmpeq_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_cmpeq_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_cmpeq_epi64(__m256i, __m256i); + +extern __m256i __cdecl _mm256_cmpgt_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_cmpgt_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_cmpgt_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_cmpgt_epi64(__m256i, __m256i); + + +/* + * Integer 256-bit vector MIN/MAX operations. + */ +extern __m256i __cdecl _mm256_max_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_max_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_max_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_max_epu8(__m256i, __m256i); +extern __m256i __cdecl _mm256_max_epu16(__m256i, __m256i); +extern __m256i __cdecl _mm256_max_epu32(__m256i, __m256i); + +extern __m256i __cdecl _mm256_min_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_min_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_min_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_min_epu8(__m256i, __m256i); +extern __m256i __cdecl _mm256_min_epu16(__m256i, __m256i); +extern __m256i __cdecl _mm256_min_epu32(__m256i, __m256i); + + +/* + * Integer 256-bit vector logical operations. + */ +extern __m256i __cdecl _mm256_and_si256(__m256i, __m256i); +extern __m256i __cdecl _mm256_andnot_si256(__m256i, __m256i); +extern __m256i __cdecl _mm256_or_si256(__m256i, __m256i); +extern __m256i __cdecl _mm256_xor_si256(__m256i, __m256i); + + +/* + * Integer 256-bit vector arithmetic operations. + */ +extern __m256i __cdecl _mm256_abs_epi8(__m256i); +extern __m256i __cdecl _mm256_abs_epi16(__m256i); +extern __m256i __cdecl _mm256_abs_epi32(__m256i); + +extern __m256i __cdecl _mm256_add_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_add_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_add_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_add_epi64(__m256i, __m256i); + +extern __m256i __cdecl _mm256_adds_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_adds_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_adds_epu8(__m256i, __m256i); +extern __m256i __cdecl _mm256_adds_epu16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_sub_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_sub_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_sub_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_sub_epi64(__m256i, __m256i); + +extern __m256i __cdecl _mm256_subs_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_subs_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_subs_epu8(__m256i, __m256i); +extern __m256i __cdecl _mm256_subs_epu16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_avg_epu8(__m256i, __m256i); +extern __m256i __cdecl _mm256_avg_epu16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_hadd_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_hadd_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_hadds_epi16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_hsub_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_hsub_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_hsubs_epi16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_madd_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_maddubs_epi16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_mulhi_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_mulhi_epu16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_mullo_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_mullo_epi32(__m256i, __m256i); + +extern __m256i __cdecl _mm256_mul_epu32(__m256i, __m256i); +extern __m256i __cdecl _mm256_mul_epi32(__m256i, __m256i); + +extern __m256i __cdecl _mm256_sign_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_sign_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_sign_epi32(__m256i, __m256i); + +extern __m256i __cdecl _mm256_mulhrs_epi16(__m256i, __m256i); + +extern __m256i __cdecl _mm256_sad_epu8(__m256i, __m256i); +extern __m256i __cdecl _mm256_mpsadbw_epu8(__m256i, __m256i, const int); + + +/* + * Integer 256-bit vector arithmetic/logical shift operations. + */ +extern __m256i __cdecl _mm256_slli_si256(__m256i, const int); +extern __m256i __cdecl _mm256_srli_si256(__m256i, const int); + +extern __m256i __cdecl _mm256_sll_epi16(__m256i, __m128i); +extern __m256i __cdecl _mm256_sll_epi32(__m256i, __m128i); +extern __m256i __cdecl _mm256_sll_epi64(__m256i, __m128i); + +extern __m256i __cdecl _mm256_slli_epi16(__m256i, int); +extern __m256i __cdecl _mm256_slli_epi32(__m256i, int); +extern __m256i __cdecl _mm256_slli_epi64(__m256i, int); + +extern __m256i __cdecl _mm256_sllv_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_sllv_epi64(__m256i, __m256i); + +extern __m128i __cdecl _mm_sllv_epi32(__m128i, __m128i); +extern __m128i __cdecl _mm_sllv_epi64(__m128i, __m128i); + +extern __m256i __cdecl _mm256_sra_epi16(__m256i, __m128i); +extern __m256i __cdecl _mm256_sra_epi32(__m256i, __m128i); + +extern __m256i __cdecl _mm256_srai_epi16(__m256i, int); +extern __m256i __cdecl _mm256_srai_epi32(__m256i, int); + +extern __m256i __cdecl _mm256_srav_epi32(__m256i, __m256i); + +extern __m128i __cdecl _mm_srav_epi32(__m128i, __m128i); + +extern __m256i __cdecl _mm256_srl_epi16(__m256i, __m128i); +extern __m256i __cdecl _mm256_srl_epi32(__m256i, __m128i); +extern __m256i __cdecl _mm256_srl_epi64(__m256i, __m128i); + +extern __m256i __cdecl _mm256_srli_epi16(__m256i, int); +extern __m256i __cdecl _mm256_srli_epi32(__m256i, int); +extern __m256i __cdecl _mm256_srli_epi64(__m256i, int); + +extern __m256i __cdecl _mm256_srlv_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_srlv_epi64(__m256i, __m256i); + +extern __m128i __cdecl _mm_srlv_epi32(__m128i, __m128i); +extern __m128i __cdecl _mm_srlv_epi64(__m128i, __m128i); + + +/* + * Integer 128/256-bit vector pack/blend/shuffle/insert/extract operations. + */ +extern __m128i __cdecl _mm_blend_epi32(__m128i, __m128i, const int); + +extern __m256i __cdecl _mm256_blend_epi32(__m256i,__m256i, const int); + +extern __m256i __cdecl _mm256_alignr_epi8(__m256i, __m256i, const int); + +extern __m256i __cdecl _mm256_blendv_epi8(__m256i, __m256i, __m256i); +extern __m256i __cdecl _mm256_blend_epi16(__m256i, __m256i, const int); + +extern __m256i __cdecl _mm256_packs_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_packs_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_packus_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_packus_epi32(__m256i, __m256i); + +extern __m256i __cdecl _mm256_unpackhi_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_unpackhi_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_unpackhi_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_unpackhi_epi64(__m256i, __m256i); + +extern __m256i __cdecl _mm256_unpacklo_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_unpacklo_epi16(__m256i, __m256i); +extern __m256i __cdecl _mm256_unpacklo_epi32(__m256i, __m256i); +extern __m256i __cdecl _mm256_unpacklo_epi64(__m256i, __m256i); + +extern __m256i __cdecl _mm256_shuffle_epi8(__m256i, __m256i); +extern __m256i __cdecl _mm256_shuffle_epi32(__m256i, const int); + +extern __m256i __cdecl _mm256_shufflehi_epi16(__m256i, const int); +extern __m256i __cdecl _mm256_shufflelo_epi16(__m256i, const int); + +extern __m128i __cdecl _mm256_extracti128_si256(__m256i, const int); +extern __m256i __cdecl _mm256_inserti128_si256(__m256i, __m128i, const int); + + +/* + * Scalar to 128/256-bit vector broadcast operations. + */ +extern __m128 __cdecl _mm_broadcastss_ps(__m128); +extern __m128d __cdecl _mm_broadcastsd_pd(__m128d); + +extern __m128i __cdecl _mm_broadcastb_epi8(__m128i); +extern __m128i __cdecl _mm_broadcastw_epi16(__m128i); +extern __m128i __cdecl _mm_broadcastd_epi32(__m128i); +extern __m128i __cdecl _mm_broadcastq_epi64(__m128i); + +extern __m256 __cdecl _mm256_broadcastss_ps(__m128); +extern __m256d __cdecl _mm256_broadcastsd_pd(__m128d); + +extern __m256i __cdecl _mm256_broadcastb_epi8(__m128i); +extern __m256i __cdecl _mm256_broadcastw_epi16(__m128i); +extern __m256i __cdecl _mm256_broadcastd_epi32(__m128i); +extern __m256i __cdecl _mm256_broadcastq_epi64(__m128i); + +extern __m256i __cdecl _mm256_broadcastsi128_si256(__m128i); + + + +/* + * Integer 256-bit vector signed/unsigned extension operations. + */ +extern __m256i __cdecl _mm256_cvtepi8_epi16(__m128i); +extern __m256i __cdecl _mm256_cvtepi8_epi32(__m128i); +extern __m256i __cdecl _mm256_cvtepi8_epi64(__m128i); +extern __m256i __cdecl _mm256_cvtepi16_epi32(__m128i); +extern __m256i __cdecl _mm256_cvtepi16_epi64(__m128i); +extern __m256i __cdecl _mm256_cvtepi32_epi64(__m128i); + +extern __m256i __cdecl _mm256_cvtepu8_epi16(__m128i); +extern __m256i __cdecl _mm256_cvtepu8_epi32(__m128i); +extern __m256i __cdecl _mm256_cvtepu8_epi64(__m128i); +extern __m256i __cdecl _mm256_cvtepu16_epi32(__m128i); +extern __m256i __cdecl _mm256_cvtepu16_epi64(__m128i); +extern __m256i __cdecl _mm256_cvtepu32_epi64(__m128i); + + +/* + * Returns a 32-bit mask made up of the most significant bit of each byte + * of the 256-bit vector source operand. + */ +extern int __cdecl _mm256_movemask_epi8(__m256i); + + +/* + * Masked load/store operations. + */ +extern __m128i __cdecl _mm_maskload_epi32(int const * /* ptr */, + __m128i /* vmask */); +extern __m128i __cdecl _mm_maskload_epi64(__int64 const * /* ptr */, + __m128i /* vmask */); + +extern void __cdecl _mm_maskstore_epi32(int * /* ptr */, + __m128i /* vmask */, + __m128i /* val */); +extern void __cdecl _mm_maskstore_epi64(__int64 * /* ptr */, + __m128i /* vmask */, + __m128i /* val */); + +extern __m256i __cdecl _mm256_maskload_epi32(int const * /* ptr */, + __m256i /* vmask */); +extern __m256i __cdecl _mm256_maskload_epi64(__int64 const * /* ptr */, + __m256i /* vmask */); + +extern void __cdecl _mm256_maskstore_epi32(int * /* ptr */, + __m256i /* vmask */, + __m256i /* val */); +extern void __cdecl _mm256_maskstore_epi64(__int64 * /* ptr */, + __m256i /* vmask */, + __m256i /* val */); + + +/* + * Permute elements in vector operations. + */ +extern __m256i __cdecl _mm256_permutevar8x32_epi32(__m256i, __m256i); +extern __m256 __cdecl _mm256_permutevar8x32_ps(__m256, __m256i); + +extern __m256i __cdecl _mm256_permute4x64_epi64(__m256i, const int); +extern __m256d __cdecl _mm256_permute4x64_pd(__m256d, const int); + +extern __m256i __cdecl _mm256_permute2x128_si256(__m256i, __m256i, const int); + + +/* + * Load 32-bytes from memory using non-temporal aligned hint. + */ +extern __m256i __cdecl _mm256_stream_load_si256(__m256i const *); + + + +/* + * Masked GATHER from memory to vector register operations. + */ +extern __m256d __cdecl _mm256_mask_i32gather_pd(__m256d /* old_dst */, + double const * /* ptr */, + __m128i /* vindex */, + __m256d /* vmask */, + const int /* scale */); +extern __m256 __cdecl _mm256_mask_i32gather_ps(__m256 /* old_dst */, + float const * /* ptr */, + __m256i /* vindex */, + __m256 /* vmask */, + const int /* scale */); +extern __m256d __cdecl _mm256_mask_i64gather_pd(__m256d /* old_dst */, + double const * /* ptr */, + __m256i /* vindex */, + __m256d /* vmask */, + const int /* scale */); +extern __m128 __cdecl _mm256_mask_i64gather_ps(__m128 /* old_dst */, + float const * /* ptr */, + __m256i /* vindex */, + __m128 /* vmask */, + const int /* scale */); + +extern __m128d __cdecl _mm_mask_i32gather_pd(__m128d /* old_dst */, + double const * /* ptr */, + __m128i /* vindex */, + __m128d /* vmask */, + const int /* scale */); +extern __m128 __cdecl _mm_mask_i32gather_ps(__m128 /* old_dst */, + float const * /* ptr */, + __m128i /* vindex */, + __m128 /* vmask */, + const int /* scale */); +extern __m128d __cdecl _mm_mask_i64gather_pd(__m128d /* old_dst */, + double const * /* ptr */, + __m128i /* vindex */, + __m128d /* vmask */, + const int /* scale */); +extern __m128 __cdecl _mm_mask_i64gather_ps(__m128 /* old_dst */, + float const * /* ptr */, + __m128i /* vindex */, + __m128 /* vmask */, + const int /* scale */); + + +extern __m256i __cdecl _mm256_mask_i32gather_epi32(__m256i /* old_dst */, + int const * /* ptr */, + __m256i /* vindex */, + __m256i /* vmask */, + const int /* scale */); +extern __m256i __cdecl _mm256_mask_i32gather_epi64(__m256i /* old_dst */, + __int64 const * /* ptr */, + __m128i /* vindex */, + __m256i /* vmask */, + const int /* scale */); +extern __m128i __cdecl _mm256_mask_i64gather_epi32(__m128i /* old_dst */, + int const * /* ptr */, + __m256i /* vindex */, + __m128i /* vmask */, + const int /* scale */); +extern __m256i __cdecl _mm256_mask_i64gather_epi64(__m256i /* old_dst */, + __int64 const * /* ptr */, + __m256i /* vindex */, + __m256i /* vmask */, + const int /* scale */); + +extern __m128i __cdecl _mm_mask_i32gather_epi32(__m128i /* old_dst */, + int const * /* ptr */, + __m128i /* vindex */, + __m128i /* vmask */, + const int /* scale */); +extern __m128i __cdecl _mm_mask_i32gather_epi64(__m128i /* old_dst */, + __int64 const * /* ptr */, + __m128i /* vindex */, + __m128i /* vmask */, + const int /* scale */); +extern __m128i __cdecl _mm_mask_i64gather_epi32(__m128i /* old_dst */, + int const * /* ptr */, + __m128i /* vindex */, + __m128i /* vmask */, + const int /* scale */); +extern __m128i __cdecl _mm_mask_i64gather_epi64(__m128i /* old_dst */, + __int64 const * /* ptr */, + __m128i /* vindex */, + __m128i /* vmask */, + const int /* scale */); + + +/* + * GATHER from memory to vector register operations. + */ +extern __m256d __cdecl _mm256_i32gather_pd(double const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); +extern __m256 __cdecl _mm256_i32gather_ps(float const * /* ptr */, + __m256i /* vindex */, + const int /* index_scale */); +extern __m256d __cdecl _mm256_i64gather_pd(double const * /* ptr */, + __m256i /* vindex */, + const int /* index_scale */); +extern __m128 __cdecl _mm256_i64gather_ps(float const * /* ptr */, + __m256i /* vindex */, + const int /* index_scale */); + +extern __m128d __cdecl _mm_i32gather_pd(double const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); +extern __m128 __cdecl _mm_i32gather_ps(float const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); +extern __m128d __cdecl _mm_i64gather_pd(double const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); +extern __m128 __cdecl _mm_i64gather_ps(float const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); + +extern __m256i __cdecl _mm256_i32gather_epi32(int const * /* ptr */, + __m256i /* vindex */, + const int /* scale */); +extern __m256i __cdecl _mm256_i32gather_epi64(__int64 const * /* ptr */, + __m128i /* vindex */, + const int /* scale */); +extern __m128i __cdecl _mm256_i64gather_epi32(int const * /* ptr */, + __m256i /* vindex */, + const int /* scale */); +extern __m256i __cdecl _mm256_i64gather_epi64(__int64 const * /* ptr */, + __m256i /* vindex */, + const int /* scale */); + +extern __m128i __cdecl _mm_i32gather_epi32(int const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); +extern __m128i __cdecl _mm_i32gather_epi64(__int64 const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); +extern __m128i __cdecl _mm_i64gather_epi32(int const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); +extern __m128i __cdecl _mm_i64gather_epi64(__int64 const * /* ptr */, + __m128i /* vindex */, + const int /* index_scale */); + + +/* + * A collection of operations to manipulate integer data at bit-granularity. + */ +extern unsigned int _bextr_u32(unsigned int /* src */, + unsigned int /* start_bit */, + unsigned int /* len_in_bits */); +extern unsigned int _blsi_u32(unsigned int); +extern unsigned int _blsmsk_u32(unsigned int); +extern unsigned int _blsr_u32(unsigned int); +extern unsigned int _bzhi_u32(unsigned int /* src */, + unsigned int /* index */); +extern unsigned int _mulx_u32(unsigned int /* src1 */, + unsigned int /* src2 */, + unsigned int * /* high_bits */); +extern unsigned int _pdep_u32(unsigned int /* src */, + unsigned int /* mask */); +extern unsigned int _pext_u32(unsigned int /* src */, + unsigned int /* mask */); +extern unsigned int _rorx_u32(unsigned int /* src */, + const unsigned int /* shift_count */); +extern int _sarx_i32(int /* src */, + unsigned int /* shift_count */); +extern unsigned int _shlx_u32(unsigned int /* src */, + unsigned int /* shift_count */); +extern unsigned int _shrx_u32(unsigned int /* src */, + unsigned int /* shift_count */); + +#if defined (_M_X64) +extern unsigned __int64 _bextr_u64(unsigned __int64 /* src */, + unsigned int /* start_bit */, + unsigned int /* len_in_bits */); +extern unsigned __int64 _blsi_u64(unsigned __int64); +extern unsigned __int64 _blsmsk_u64(unsigned __int64); +extern unsigned __int64 _blsr_u64(unsigned __int64); +extern unsigned __int64 _bzhi_u64(unsigned __int64 /* src */, + unsigned int /* index */); +extern unsigned __int64 _mulx_u64(unsigned __int64 /* src1 */, + unsigned __int64 /* src2 */, + unsigned __int64 * /* high_bits */); +extern unsigned __int64 _pdep_u64(unsigned __int64 /* src */, + unsigned __int64 /* mask */); +extern unsigned __int64 _pext_u64(unsigned __int64 /* src */, + unsigned __int64 /* mask */); +extern unsigned __int64 _rorx_u64(unsigned __int64 /* src */, + const unsigned int /* shift_count */); +extern __int64 _sarx_i64(__int64 /* src */, + unsigned int /* shift_count */); +extern unsigned __int64 _shlx_u64(unsigned __int64 /* src */, + unsigned int /* shift_count */); +extern unsigned __int64 _shrx_u64(unsigned __int64 /* src */, + unsigned int /* shift_count */); +#endif /* defined (_M_X64) */ + + +/* + * Leading zero bit count. + * + * Counts the number of leading zero bits in a source operand. + * Returns operand size as output when source operand is zero. + */ +extern unsigned int _lzcnt_u32(unsigned int); +#if defined (_M_X64) +extern unsigned __int64 _lzcnt_u64(unsigned __int64); +#endif /* defined (_M_X64) */ + +/* + * Trailing zero bit count. + * + * Searches the source operand (r2) for the least significant set bit + * (1 bit). If a least significant 1 bit is found, its bit index is + * returned, otherwise the result is the number of bits in the operand size. + */ +extern unsigned int _tzcnt_u32(unsigned int); +#if defined (_M_X64) +extern unsigned __int64 _tzcnt_u64(unsigned __int64); +#endif /* defined (_M_X64) */ + + + +/* + * Operation targeted to system software that manages processor context IDs. + */ +extern void __cdecl _invpcid(unsigned int /* type */, void * /* descriptor */); + +// Hardware Lock Elision +extern void _Store_HLERelease(long volatile *,long); +extern void _StorePointer_HLERelease(void * volatile *,void *); + +extern long _InterlockedExchange_HLEAcquire(long volatile *,long); +extern long _InterlockedExchange_HLERelease(long volatile *,long); +extern void * _InterlockedExchangePointer_HLEAcquire(void *volatile *,void *); +extern void * _InterlockedExchangePointer_HLERelease(void *volatile *,void *); + +extern long _InterlockedCompareExchange_HLEAcquire(long volatile *,long,long); +extern long _InterlockedCompareExchange_HLERelease(long volatile *,long,long); +extern __int64 _InterlockedCompareExchange64_HLEAcquire(__int64 volatile *,__int64,__int64); +extern __int64 _InterlockedCompareExchange64_HLERelease(__int64 volatile *,__int64,__int64); +extern void * _InterlockedCompareExchangePointer_HLEAcquire(void *volatile *,void *,void *); +extern void * _InterlockedCompareExchangePointer_HLERelease(void *volatile *,void *,void *); + +extern long _InterlockedExchangeAdd_HLEAcquire(long volatile *,long); +extern long _InterlockedExchangeAdd_HLERelease(long volatile *,long); + +extern long _InterlockedAnd_HLEAcquire(long volatile *,long); +extern long _InterlockedAnd_HLERelease(long volatile *,long); +extern long _InterlockedOr_HLEAcquire(long volatile *,long); +extern long _InterlockedOr_HLERelease(long volatile *,long); +extern long _InterlockedXor_HLEAcquire(long volatile *,long); +extern long _InterlockedXor_HLERelease(long volatile *,long); + +extern unsigned char _interlockedbittestandset_HLEAcquire(long *a,long b); +extern unsigned char _interlockedbittestandset_HLERelease(long *a,long b); +extern unsigned char _interlockedbittestandreset_HLEAcquire(long *a,long b); +extern unsigned char _interlockedbittestandreset_HLERelease(long *a,long b); + +#if defined(_M_X64) +extern void _Store64_HLERelease(__int64 volatile *,__int64); +extern __int64 _InterlockedExchange64_HLEAcquire(__int64 volatile *,__int64); +extern __int64 _InterlockedExchange64_HLERelease(__int64 volatile *,__int64); + +extern __int64 _InterlockedExchangeAdd64_HLEAcquire(__int64 volatile *,__int64); +extern __int64 _InterlockedExchangeAdd64_HLERelease(__int64 volatile *,__int64); + +extern __int64 _InterlockedAnd64_HLEAcquire(__int64 volatile *,__int64); +extern __int64 _InterlockedAnd64_HLERelease(__int64 volatile *,__int64); +extern __int64 _InterlockedOr64_HLEAcquire(__int64 volatile *,__int64); +extern __int64 _InterlockedOr64_HLERelease(__int64 volatile *,__int64); +extern __int64 _InterlockedXor64_HLEAcquire(__int64 volatile *,__int64); +extern __int64 _InterlockedXor64_HLERelease(__int64 volatile *,__int64); + +extern unsigned char _interlockedbittestandset64_HLEAcquire(__int64 *a,__int64 b); +extern unsigned char _interlockedbittestandset64_HLERelease(__int64 *a,__int64 b); +extern unsigned char _interlockedbittestandreset64_HLEAcquire(__int64 *a,__int64 b); +extern unsigned char _interlockedbittestandreset64_HLERelease(__int64 *a,__int64 b); +#endif /* defined (_M_X64) */ + +// Restricted Transactional Memory +#define _XBEGIN_STARTED (~0u) +#define _XABORT_EXPLICIT (1 << 0) +#define _XABORT_RETRY (1 << 1) +#define _XABORT_CONFLICT (1 << 2) +#define _XABORT_CAPACITY (1 << 3) +#define _XABORT_DEBUG (1 << 4) +#define _XABORT_NESTED (1 << 5) +#define _XABORT_CODE(x) ((unsigned char)(((x) >> 24) & 0xFF)) + +extern unsigned int __cdecl _xbegin(void); +extern void __cdecl _xend(void); +extern void __cdecl _xabort(const unsigned int); +extern unsigned char __cdecl _xtest(void); + +/* + * Perform one attempt to generate a hardware generated random value + * accordingly to the NIST SP 800-90B/C standards. + * The generated value is written to the given memory location and the success + * status is returned: 1 if the hardware could generate a valid random number + * and 0 otherwise. + */ +extern int __cdecl _rdseed16_step(unsigned short *); +extern int __cdecl _rdseed32_step(unsigned int *); +extern int __cdecl _rdseed64_step(unsigned __int64 *); + +/* + * The _addcarryx... functions generate ADCX and ADOX instructions which + * use CF and OF (in the flags register) respectively to propagate carry. + * Because this allows two add-with-carry sequences to be interleaved + * without having to save and restore the carry flag this is useful in + * multiprecision multiply for example. These functions return + * the carry-out, which is convenient for chaining multiple operations. + * The sum is written using the given reference. + */ +extern unsigned char __cdecl _addcarryx_u32(unsigned char /*c_in*/, + unsigned int /*src1*/, + unsigned int /*src2*/, + unsigned int * /*out*/); +#if defined(_M_X64) +extern unsigned char __cdecl _addcarryx_u64(unsigned char /*c_in*/, + unsigned __int64 /*src1*/, + unsigned __int64 /*src2*/, + unsigned __int64 * /*out*/); +#endif /* defined (_M_X64) */ + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INCLUDED_IMM */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/internal_concurrent_hash.h b/test_data/lots_of_files/internal_concurrent_hash.h new file mode 100644 index 0000000..e44965e --- /dev/null +++ b/test_data/lots_of_files/internal_concurrent_hash.h @@ -0,0 +1,1321 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* internal_concurrent_hash.h +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include <utility> +#include "internal_split_ordered_list.h" +#include <concrt.h> + +#pragma pack(push,_CRT_PACKING) + +namespace Concurrency +{ +namespace details +{ +// Template class for hash compare +template<typename _Key_type, typename _Hasher, typename _Key_equality> +class _Hash_compare +{ +public: + typedef _Hasher hasher; + + _Hash_compare() + { + } + + _Hash_compare(hasher _Hasharg) : _M_hash_object(_Hasharg) + { + } + + _Hash_compare(hasher _Hasharg, _Key_equality _Keyeqarg) : _M_hash_object(_Hasharg), _M_key_compare_object(_Keyeqarg) + { + } + + size_t operator()(const _Key_type& _Keyval) const + { + return ((size_t)_M_hash_object(_Keyval)); + } + + bool operator()(const _Key_type& _Keyval1, const _Key_type& _Keyval2) const + { + return (!_M_key_compare_object(_Keyval1, _Keyval2)); + } + + hasher _M_hash_object; // The hash object + _Key_equality _M_key_compare_object; // The equality comparator object +}; + +// An efficient implementation of the _Reverse function utilizes a 2^8 or 2^16 lookup table holding the +// bit-reversed values of [0..2^8 - 1] or [0..2^16 - 1] respectively. Those values can also be computed +// on the fly at a slightly higher cost. +extern _CRTIMP2 const unsigned char _Byte_reverse_table[]; + +// Given a byte, reverses it +inline unsigned char _Reverse_byte(unsigned char _Original_byte) +{ + // return ((_Original_byte * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32; + return _Byte_reverse_table[_Original_byte]; +} + +// Finds the most significant bit in a size_t +inline unsigned char _Get_msb(size_t _Mask) +{ + unsigned long _Index = 0; + +#if (defined (_M_IX86) || defined (_M_ARM)) + _BitScanReverse(&_Index, _Mask); +#else /* (defined (_M_IX86) || defined (_M_ARM)) */ + _BitScanReverse64(&_Index, _Mask); +#endif /* (defined (_M_IX86) || defined (_M_ARM)) */ + + return (unsigned char) _Index; +} + +#pragma warning(push) +#pragma warning(disable: 4127) // Warning 4127 -- while (true) has a constant expression in it + +template <typename _Traits> +class _Concurrent_hash : public _Traits +{ +public: + // Type definitions + typedef _Concurrent_hash<_Traits> _Mytype; + typedef typename _Split_ordered_list<typename _Traits::value_type, typename _Traits::allocator_type> _Mylist; + typedef typename _Traits::_Key_compare _Key_compare; + typedef typename _Traits::_Value_compare _Value_compare; + + typedef typename _Traits::value_type value_type; + typedef typename _Traits::key_type key_type; + typedef typename _Traits::allocator_type allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename allocator_type::size_type size_type; + typedef typename allocator_type::difference_type difference_type; + + typedef typename std::tr1::conditional<std::tr1::is_same<key_type, value_type>::value, typename _Mylist::const_iterator, typename _Mylist::iterator>::type iterator; + typedef typename _Mylist::const_iterator const_iterator; + typedef iterator local_iterator; + typedef const_iterator const_local_iterator; + + typedef typename _Mylist::_Nodeptr _Nodeptr; + + // Iterators that walk the entire split-order list, including dummy nodes + typedef typename _Mylist::_Full_iterator _Full_iterator; + typedef typename _Mylist::_Full_const_iterator _Full_const_iterator; + + static const size_type _Initial_bucket_number = 8; // Initial number of buckets + static const size_type _Initial_bucket_load = 4; // Initial maximum number of elements per bucket + static size_type const _Pointers_per_table = sizeof(size_type) * 8; // One bucket segment per bit + + // Constructors/Destructors + _Concurrent_hash(size_type _Number_of_buckets = _Initial_bucket_number, const _Key_compare& _Parg = _Key_compare(), const allocator_type& _Allocator = allocator_type()) + : _Traits(_Parg), _M_number_of_buckets(_Number_of_buckets), _M_split_ordered_list(_Allocator), _M_allocator(_Allocator), _M_maximum_bucket_size((float) _Initial_bucket_load) + { + _Init(); + } + + _Concurrent_hash(const _Concurrent_hash& _Right, const allocator_type& _Allocator) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Allocator), _M_allocator(_Allocator) + { + _Copy(_Right); + } + + _Concurrent_hash(const _Concurrent_hash& _Right) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Right.get_allocator()), _M_allocator(_Right.get_allocator()) + { + _Init(); + _Copy(_Right); + } + + _Concurrent_hash(_Concurrent_hash&& _Right) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Right.get_allocator()), _M_allocator(_Right.get_allocator()), + _M_number_of_buckets(_Initial_bucket_number), _M_maximum_bucket_size((float) _Initial_bucket_load) + { + _Init(); + swap(_Right); + } + + _Concurrent_hash& operator=(const _Concurrent_hash& _Right) + { + if (this != &_Right) + { + _Copy(_Right); + } + + return (*this); + } + + _Concurrent_hash& operator=(_Concurrent_hash&& _Right) + { + if (this != &_Right) + { + clear(); + swap(_Right); + } + + return (*this); + } + + ~_Concurrent_hash() + { + // Delete all node segments + for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++) + { + if (_M_buckets[_Index] != NULL) + { + size_type _Seg_size = _Segment_size(_Index); + for (size_type _Index2 = 0; _Index2 < _Seg_size; _Index2++) + { + _M_allocator.destroy(&_M_buckets[_Index][_Index2]); + } + _M_allocator.deallocate(_M_buckets[_Index], _Seg_size); + } + } + } + + static size_type __cdecl _Segment_index_of( size_type _Index ) + { + return size_type( _Get_msb( _Index|1 ) ); + } + + static size_type _Segment_base( size_type _K ) + { + return (size_type(1)<<_K & ~size_type(1)); + } + + static size_type _Segment_size( size_type _K ) + { + return _K ? size_type(1)<<_K : 2; + } + + /// <summary> + /// Returns the stored allocator object for this concurrent container. This method is concurrency safe. + /// </summary> + /// <returns> + /// The stored allocator object for this concurrent container. + /// </returns> + /**/ + allocator_type get_allocator() const + { + return _M_split_ordered_list.get_allocator(); + } + + /// <summary> + /// Tests whether no elements are present. This method is concurrency safe. + /// </summary> + /// <remarks> + /// In the presence of concurrent inserts, whether or not the concurrent container is empty may change immediately + /// after calling this function, before the return value is even read. + /// </remarks> + /// <returns> + /// <c>true</c> if the concurrent container is empty, <c>false</c> otherwise. + /// </returns> + /**/ + bool empty() const + { + return _M_split_ordered_list.empty(); + } + + /// <summary> + /// Returns the number of elements in this concurrent container. This method is concurrency safe. + /// </summary> + /// <remarks> + /// In the presence of concurrent inserts, the number of elements in the concurrent container may change immediately + /// after calling this function, before the return value is even read. + /// </remarks> + /// <returns> + /// The number of items in the container. + /// </returns> + /**/ + size_type size() const + { + return _M_split_ordered_list.size(); + } + + /// <summary> + /// Returns the maximum size of the concurrent container, determined by the allocator. This method is concurrency safe. + /// </summary> + /// <remarks> + /// This upper bound value may actually be higher than what the container can actually hold. + /// </remarks> + /// <returns> + /// The maximum number of elements that can be inserted into this concurrent container. + /// </returns> + /**/ + size_type max_size() const + { + return _M_split_ordered_list.max_size(); + } + + /// <summary> + /// Returns an iterator pointing to the first element in the concurrent container. This method is concurrency safe. + /// </summary> + /// <returns> + /// An iterator to the first element in the concurrent container. + /// </returns> + /**/ + iterator begin() + { + return _M_split_ordered_list.begin(); + } + + /// <summary> + /// Returns an iterator pointing to the first element in the concurrent container. This method is concurrency safe. + /// </summary> + /// <returns> + /// An iterator to the first element in the concurrent container. + /// </returns> + /**/ + const_iterator begin() const + { + return _M_split_ordered_list.begin(); + } + + /// <summary> + /// Returns an iterator pointing to the location succeeding the last element in the concurrent container. + /// This method is concurrency safe. + /// </summary> + /// <returns> + /// An iterator to the location succeeding the last element in the concurrent container. + /// </returns> + /**/ + iterator end() + { + return _M_split_ordered_list.end(); + } + + /// <summary> + /// Returns a const_iterator pointing to the location succeeding the last element in the concurrent container. + /// This method is concurrency safe. + /// </summary> + /// <returns> + /// A const_iterator to the location succeeding the last element in the concurrent container. + /// </returns> + /**/ + const_iterator end() const + { + return _M_split_ordered_list.end(); + } + + /// <summary> + /// Returns a const iterator pointing to the first element in the concurrent container. This method is concurrency safe. + /// </summary> + /// <returns> + /// A const iterator to the first element in the concurrent container. + /// </returns> + /**/ + const_iterator cbegin() const + { + return _M_split_ordered_list.cbegin(); + } + + /// <summary> + /// Returns a const iterator pointing to the location succeeding the last element in the concurrent container. This method is concurrency safe. + /// </summary> + /// <returns> + /// A const iterator to the location succeeding the last element in the concurrent container. + /// </returns> + /**/ + const_iterator cend() const + { + return _M_split_ordered_list.cend(); + } + + /// <summary> + /// Removes elements from the container at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Where"> + /// The iterator position to erase from. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_First"/>, <paramref name="_Last"/>). + /// <para>The third member function removes the elements in the range delimited by equal_range(<paramref name="_Keyval"/>)</see>. </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or end() if no such element exists. The third member function returns the number of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _Where) + { + if (_Where == end()) + { + return end(); + } + + return _Erase(_Where); + } + + /// <summary> + /// Removes elements from the container at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_First"> + /// The position of the first element in the range of elements to be erased. + /// </param> + /// <param name="_Last"> + /// The position of the first element beyond the range of elements to be erased. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_First"/>, <paramref name="_Last"/>). + /// <para>The third member function removes the elements in the range delimited by equal_range(<paramref name="_Keyval"/>)</see>. </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or end() if no such element exists. The third member function returns the number of elements it removes. + /// </returns> + /**/ + iterator unsafe_erase(const_iterator _First, const_iterator _Last) + { + while (_First != _Last) + { + unsafe_erase(_First++); + } + + return _M_split_ordered_list._Get_iterator(_First); + } + + /// <summary> + /// Removes elements from the container at specified positions. This method is not concurrency-safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to erase. + /// </param> + /// <remarks> + /// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second + /// member function removes the elements in the range [<paramref name="_First"/>, <paramref name="_Last"/>). + /// <para>The third member function removes the elements in the range delimited by equal_range(<paramref name="_Keyval"/>)</see>. </para> + /// </remarks> + /// <returns> + /// The first two member functions return an iterator that designates the first element remaining beyond any elements removed, + /// or end() if no such element exists. The third member function returns the number of elements it removes. + /// </returns> + /**/ + size_type unsafe_erase(const key_type& _Keyval) + { + std::pair<iterator, iterator> _Where = equal_range(_Keyval); + size_type _Count = _Distance(_Where.first, _Where.second); + unsafe_erase(_Where.first, _Where.second); + return _Count; + } + + /// <summary> + /// Swaps the contents of two concurrent containers. This function is not concurrency safe. + /// </summary> + /// <param name="_Right"> + /// The container to swap elements from. + /// </param> + /// <remarks> + /// The member function throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the swap is being + /// performed on unequal allocators. + /// </remarks> + /**/ + void swap(_Concurrent_hash& _Right) + { + if (this != &_Right) + { + std::_Swap_adl(_M_comparator, _Right._M_comparator); + _M_split_ordered_list.swap(_Right._M_split_ordered_list); + _Swap_buckets(_Right); + std::swap(_M_number_of_buckets, _Right._M_number_of_buckets); + std::swap(_M_maximum_bucket_size, _Right._M_maximum_bucket_size); + } + } + + /// <summary> + /// Erases all the elements in the concurrent container. This function is not concurrency safe. + /// </summary> + /**/ + void clear() + { + // Clear list + _M_split_ordered_list.clear(); + + // Clear buckets + for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++) + { + if (_M_buckets[_Index] != NULL) + { + size_type _Seg_size = _Segment_size(_Index); + for (size_type _Index2 = 0; _Index2 < _Seg_size; _Index2++) + { + _M_allocator.destroy(&_M_buckets[_Index][_Index2]); + } + _M_allocator.deallocate(_M_buckets[_Index], _Seg_size); + } + } + + // memset all the buckets to zero and initialize the dummy node 0 + _Init(); + } + + /// <summary> + /// Finds an element that matches a specified key. This function is concurrency safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to search for. + /// </param> + /// <returns> + /// An iterator pointing to the location of the the first element that matched the key provided, + /// or the iterator <c>end()</c> if no such element exists. + /// </returns> + /**/ + iterator find(const key_type& _Keyval) + { + return _Find(_Keyval); + } + + /// <summary> + /// Finds an element that matches a specified key. This function is concurrency safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to search for. + /// </param> + /// <returns> + /// An iterator pointing to the location of the the first element that matched the key provided, + /// or the iterator <c>end()</c> if no such element exists. + /// </returns> + /**/ + const_iterator find(const key_type& _Keyval) const + { + return _Find(_Keyval); + } + + /// <summary> + /// Counts the number of elements matching a specified key. This function is concurrency safe. + /// </summary> + /// <param name="_Keyval"> + /// The key to search for. + /// </param> + /// <returns> + /// The number of times number of times the key appears in the container. + /// </returns> + /**/ + size_type count(const key_type& _Keyval) const + { + size_type _Count = 0; + const_iterator _It = _Find(_Keyval); + for (;_It != end() && !_M_comparator(_Key_function(*_It), _Keyval); _It++) + { + _Count++; + } + return _Count; + } + + /// <summary> + /// Finds a range that matches a specified key. This function is concurrency safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to search for. + /// </param> + /// <returns> + /// A <see cref="pair Class">pair</see> where the first element is an iterator to the beginning and the second element + /// is an iterator to the end of the range. + /// </returns> + /// <remarks> + /// <para>It is possible for concurrent inserts to cause additional keys to be inserted after the begin iterator and + /// before the end iterator.</para> + /// </remarks> + /**/ + std::pair<iterator, iterator> equal_range(const key_type& _Keyval) + { + return _Equal_range(_Keyval); + } + + /// <summary> + /// Finds a range that matches a specified key. This function is concurrency safe. + /// </summary> + /// <param name="_Keyval"> + /// The key value to search for. + /// </param> + /// <returns> + /// A <see cref="pair Class">pair</see> where the first element is an iterator to the beginning and the second element + /// is an iterator to the end of the range. + /// </returns> + /// <remarks> + /// <para>It is possible for concurrent inserts to cause additional keys to be inserted after the begin iterator and + /// before the end iterator.</para> + /// </remarks> + /**/ + std::pair<const_iterator, const_iterator> equal_range(const key_type& _Keyval) const + { + return _Equal_range(_Keyval); + } + + /// <summary> + /// Returns the current number of buckets in this container. + /// </summary> + /// <returns> + /// The current number of buckets in this container. + /// </returns> + /**/ + size_type unsafe_bucket_count() const + { + return _M_number_of_buckets; + } + + /// <summary> + /// Returns the maximum number of buckets in this container. + /// </summary> + /// <returns> + /// The maximum number of buckets in this container. + /// </returns> + /**/ + size_type unsafe_max_bucket_count() const + { + return _Segment_size(_Pointers_per_table-1); + } + + /// <summary> + /// Returns the number of items in a specific bucket of this container. + /// </summary> + /// <param name="_Bucket"> + /// The bucket to search for. + /// </param> + /// <returns> + /// The current number of buckets in this container. + /// </returns> + /**/ + size_type unsafe_bucket_size(size_type _Bucket) + { + size_type _Count = 0; + + if (!_Is_initialized(_Bucket)) + { + return _Count; + } + + _Full_iterator _Iterator = _Get_bucket(_Bucket); + _Iterator++; + + for (; _Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy(); _Iterator++) + { + _Count++; + } + + return _Count; + } + + /// <summary> + /// Returns the bucket index that a specific key maps to in this container. + /// </summary> + /// <param name="_Keyval"> + /// The element key being searched for. + /// </param> + /// <returns> + /// The bucket index for the key in this container. + /// </returns> + /**/ + size_type unsafe_bucket(const key_type& _Keyval) const + { + _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval); + size_type _Bucket = _Order_key % _M_number_of_buckets; + return _Bucket; + } + + /// <summary> + /// Returns an iterator to the first element in this container for a specific bucket. + /// </summary> + /// <param name="_Bucket"> + /// The bucket index. + /// </param> + /// <returns> + /// An iterator pointing to the beginning of the bucket. + /// </returns> + /**/ + local_iterator unsafe_begin(size_type _Bucket) + { + // It is possible the bucket being searched for has not yet been initialized + if (!_Is_initialized(_Bucket)) + { + _Initialize_bucket(_Bucket); + } + + _Full_iterator _Iterator = _Get_bucket(_Bucket); + return _M_split_ordered_list._Get_first_real_iterator(_Iterator); + } + + /// <summary> + /// Returns an iterator to the first element in this container for a specific bucket. + /// </summary> + /// <param name="_Bucket"> + /// The bucket index. + /// </param> + /// <returns> + /// An iterator pointing to the beginning of the bucket. + /// </returns> + /**/ + const_local_iterator unsafe_begin(size_type _Bucket) const + { + // It is possible the bucket being searched for has not yet been initialized + if (!_Is_initialized(_Bucket)) + { + _Initialize_bucket(_Bucket); + } + + + _Full_const_iterator _Iterator = _Get_bucket(_Bucket); + return _M_split_ordered_list._Get_first_real_iterator(_Iterator); + } + + /// <summary> + /// Returns an iterator to the last element in this container for a specific bucket. + /// </summary> + /// <param name="_Bucket"> + /// The bucket index. + /// </param> + /// <returns> + /// An iterator pointing to the end of the bucket. + /// </returns> + /**/ + local_iterator unsafe_end(size_type _Bucket) + { + // If we've increased the number of buckets, there's a chance the intermediate dummy + // node marking the end of this bucket has not yet been lazily initialized. + // Inserting from _M_number_of_buckets/2 to _M_number_of_buckets will recursively + // initialize all the dummy nodes in the map. + for(size_type _Bucket_index = _M_number_of_buckets >> 1; _Bucket_index < _M_number_of_buckets; _Bucket_index++) + { + if (!_Is_initialized(_Bucket_index)) + { + _Initialize_bucket(_Bucket_index); + } + } + + _Full_iterator _Iterator = _Get_bucket(_Bucket); + + // Find the end of the bucket, denoted by the dummy element + do + { + _Iterator++; + } + while(_Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy()); + + // Return the first real element past the end of the bucket + return _M_split_ordered_list._Get_first_real_iterator(_Iterator); + } + + /// <summary> + /// Returns an iterator to the last element in this container for a specific bucket. + /// </summary> + /// <param name="_Bucket"> + /// The bucket index. + /// </param> + /// <returns> + /// An iterator pointing to the end of the bucket. + /// </returns> + /**/ + const_local_iterator unsafe_end(size_type _Bucket) const + { + // If we've increased the number of buckets, there's a chance the intermediate dummy + // node marking the end of this bucket has not yet been lazily initialized. + // Inserting from _M_number_of_buckets/2 to _M_number_of_buckets will recursively + // initialize all the dummy nodes in the map. + for(size_type _Bucket_index = _M_number_of_buckets >> 1; _Bucket_index < _M_number_of_buckets; _Bucket_index++) + { + if (!_Is_initialized(_Bucket_index)) + { + _Initialize_bucket(_Bucket_index); + } + } + + _Full_const_iterator _Iterator = _Get_bucket(_Bucket); + + // Find the end of the bucket, denoted by the dummy element + do + { + _Iterator++; + } + while(_Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy()); + + // Return the first real element past the end of the bucket + return _M_split_ordered_list._Get_first_real_iterator(_Iterator); + } + + /// <summary> + /// Returns an iterator to the first element in this container for a specific bucket. + /// </summary> + /// <param name="_Bucket"> + /// The bucket index. + /// </param> + /// <returns> + /// An iterator pointing to the beginning of the bucket. + /// </returns> + /**/ + const_local_iterator unsafe_cbegin(size_type) const + { + return ((const _Mytype *) this)->begin(); + } + + /// <summary> + /// Returns an iterator to the first element in this container for a specific bucket. + /// </summary> + /// <param name="_Bucket"> + /// The bucket index. + /// </param> + /// <returns> + /// An iterator pointing to the beginning of the bucket. + /// </returns> + /**/ + const_local_iterator unsafe_cend(size_type) const + { + return ((const _Mytype *) this)->end(); + } + + /// <summary> + /// Computes and returns the current load factor of the container. The load factor is the number of + /// elements in the container divided by the number of buckets. + /// </summary> + /// <returns> + /// The load factor for the container. + /// </returns> + /**/ + float load_factor() const + { + return (float) size() / (float) unsafe_bucket_count(); + } + + /// <summary> + /// Gets or sets the maximum load factor of the container. The maximum load factor is the + /// largest number of elements than can be in any bucket before the container grows its + /// internal table. + /// </summary> + /// <returns> + /// The first member function returns the stored maximum load factor. The second member function does not return a value + /// but throws an <see cref="out_of_range Class">out_of_range</see> exception if the supplied load factor is invalid.. + /// </returns> + /**/ + float max_load_factor() const + { + return _M_maximum_bucket_size; + } + + /// <summary> + /// Gets or sets the maximum load factor of the container. The maximum load factor is the + /// largest number of elements than can be in any bucket before the container grows its + /// internal table. + /// </summary> + /// <returns> + /// The first member function returns the stored maximum load factor. The second member function does not return a value + /// but throws an <see cref="out_of_range Class">out_of_range</see> exception if the supplied load factor is invalid.. + /// </returns> + /**/ + void max_load_factor(float _Newmax) + { + // The _Newmax != _Newmax is a check for NaN, because NaN is != to itself + if (_Newmax != _Newmax || _Newmax < 0) + { + throw std::out_of_range("invalid hash load factor"); + } + + _M_maximum_bucket_size = _Newmax; + } + + // This function is a no op, because the underlying split-ordered list + // is already sorted, so an increase in the bucket number will be + // reflected next time this bucket is touched. + + /// <summary> + /// Rebuilds the hash table. + /// </summary> + /// <param name="_Buckets"> + /// The desired number of buckets. + /// </param> + /// <remarks> + /// The member function alters the number of buckets to be at least <paramref name="_Buckets"/> and rebuilds the hash + /// table as needed. The number of buckets must be a power of 2. If not a power of 2, it will be rounded up to + /// the next largest power of 2. + /// <para>It throws an <see cref="out_of_range Class">out_of_range</see> exception if the number of buckets + /// is invalid (either 0 or greater than the maximum number of buckets).</para> + /// </remarks> + /**/ + void rehash(size_type _Buckets) + { + size_type _Current_buckets = _M_number_of_buckets; + + if (_Current_buckets > _Buckets) + { + return; + } + else if (_Buckets <= 0 || _Buckets > unsafe_max_bucket_count()) + { + throw std::out_of_range("invalid number of buckets"); + } + // Round up the number of buckets to the next largest power of 2 + _M_number_of_buckets = ((size_type) 1) << _Get_msb(_Buckets*2-1); + } + +protected: + // Insert an element in the hash given its value + template<typename _ValTy> + std::pair<iterator, bool> _Insert(_ValTy&& _Value) + { + _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Key_function(_Value)); + size_type _Bucket = _Order_key % _M_number_of_buckets; + + // If bucket is empty, initialize it first + if (!_Is_initialized(_Bucket)) + { + _Initialize_bucket(_Bucket); + } + + long _New_count; + _Order_key = _Split_order_regular_key(_Order_key); + _Full_iterator _Iterator = _Get_bucket(_Bucket); + _Full_iterator _Last = _M_split_ordered_list._End(); + _Full_iterator _Where = _Iterator; + _Nodeptr _New_node = _M_split_ordered_list._Buynode(_Order_key, std::forward<_ValTy>(_Value)); + + _ASSERT_EXPR(_Where != _Last, L"Invalid head node"); + + // First node is a dummy node + _Where++; + + for (;;) + { + if (_Where == _Last || _Mylist::_Get_key(_Where) > _Order_key) + { + // Try to insert it in the right place + std::pair<iterator, bool> _Result = _M_split_ordered_list._Insert(_Iterator, _Where, _New_node, &_New_count); + + if (_Result.second) + { + // Insertion succeeded, adjust the table size, if needed + _Adjust_table_size(_New_count, _M_number_of_buckets); + return _Result; + } + else + { + // Insertion failed: either the same node was inserted by another thread, or + // another element was inserted at exactly the same place as this node. + // Proceed with the search from the previous location where order key was + // known to be larger (note: this is legal only because there is no safe + // concurrent erase operation supported). + _Where = _Iterator; + _Where++; + continue; + } + } + else if (!_M_allow_multimapping && _Mylist::_Get_key(_Where) == _Order_key && + _M_comparator(_Key_function(*_Where), _Key_function(_New_node->_M_element)) == 0) + { + // If the insert failed (element already there), then delete the new one + _M_split_ordered_list._Erase(_New_node); + + // Element already in the list, return it + return std::pair<iterator, bool>(_M_split_ordered_list._Get_iterator(_Where), false); + } + + // Move the iterator forward + _Iterator = _Where; + _Where++; + } + } + + template<class _Iterator> + void _Insert(_Iterator _First, _Iterator _Last) + { + for (_Iterator _I = _First; _I != _Last; _I++) + { + _Insert(*_I); + } + } + +private: + + // Initialize the hash and keep the first bucket open + void _Init() + { + // Allocate an array of segment pointers + memset(_M_buckets, 0, _Pointers_per_table * sizeof(void *)); + + // Insert the first element in the split-ordered list + _Full_iterator _Dummy_node = _M_split_ordered_list._Begin(); + _Set_bucket(0, _Dummy_node); + } + + void _Copy(const _Mytype& _Right) + { + clear(); + + _M_maximum_bucket_size = _Right._M_maximum_bucket_size; + _M_number_of_buckets = _Right._M_number_of_buckets; + + try + { + _Insert(_Right.begin(), _Right.end()); + _M_comparator = _Right._M_comparator; + } + catch(...) + { + _M_split_ordered_list.clear(); + throw; + } + } + + void _Swap_buckets(_Concurrent_hash& _Right) + { + if (_M_allocator == _Right._M_allocator) + { + // Swap all node segments + for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++) + { + _Full_iterator * _Iterator_pointer = _M_buckets[_Index]; + _M_buckets[_Index] = _Right._M_buckets[_Index]; + _Right._M_buckets[_Index] = _Iterator_pointer; + } + } + else + { + throw std::invalid_argument("swap is invalid on non-equal allocators"); + } + } + + // Hash APIs + size_type _Distance(const_iterator _First, const_iterator _Last) const + { + size_type _Num = 0; + + for (const_iterator _Iterator = _First; _Iterator != _Last; _Iterator++) + { + _Num++; + } + + return _Num; + } + + // Find the element in the split-ordered list + iterator _Find(const key_type& _Keyval) + { + _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval); + size_type _Bucket = _Order_key % _M_number_of_buckets; + + // If _Bucket is empty, initialize it first + if (!_Is_initialized(_Bucket)) + { + _Initialize_bucket(_Bucket); + } + + _Order_key = _Split_order_regular_key(_Order_key); + _Full_iterator _Last = _M_split_ordered_list._End(); + + for (_Full_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++) + { + if (_Mylist::_Get_key(_Iterator) > _Order_key) + { + // If the order key is smaller than the current order key, the element + // is not in the hash. + return end(); + } + else if (_Mylist::_Get_key(_Iterator) == _Order_key) + { + // The fact that order keys match does not mean that the element is found. + // Key function comparison has to be performed to check whether this is the + // right element. If not, keep searching while order key is the same. + if (!_M_comparator(_Key_function(*_Iterator), _Keyval)) + { + return _M_split_ordered_list._Get_iterator(_Iterator); + } + } + } + + return end(); + } + + // Find the element in the split-ordered list + const_iterator _Find(const key_type& _Keyval) const + { + _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval); + size_type _Bucket = _Order_key % _M_number_of_buckets; + + // If _Bucket has not been initialized, keep searching up for a parent bucket + // that has been initialized. Worst case is the entire map will be read. + while (!_Is_initialized(_Bucket)) + { + _Bucket = _Get_parent(_Bucket); + } + + _Order_key = _Split_order_regular_key(_Order_key); + _Full_const_iterator _Last = _M_split_ordered_list._End(); + + for (_Full_const_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++) + { + if (_Mylist::_Get_key(_Iterator) > _Order_key) + { + // If the order key is smaller than the current order key, the element + // is not in the hash. + return end(); + } + else if (_Mylist::_Get_key(_Iterator) == _Order_key) + { + // The fact that order keys match does not mean that the element is found. + // Key function comparison has to be performed to check whether this is the + // right element. If not, keep searching while order key is the same. + if (!_M_comparator(_Key_function(*_Iterator), _Keyval)) + { + return _M_split_ordered_list._Get_iterator(_Iterator); + } + } + } + + return end(); + } + + // Erase an element from the list. This is not a concurrency safe function. + iterator _Erase(const_iterator _Iterator) + { + _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Key_function(*_Iterator)); + size_type _Bucket = _Order_key % _M_number_of_buckets; + + // If bucket is empty, initialize it first + if (!_Is_initialized(_Bucket)) + { + _Initialize_bucket(_Bucket); + } + + _Order_key = _Split_order_regular_key(_Order_key); + + _Full_iterator _Previous = _Get_bucket(_Bucket); + _Full_iterator _Last = _M_split_ordered_list._End(); + _Full_iterator _Where = _Previous; + + _ASSERT_EXPR(_Where != _Last, L"Invalid head node"); + + // First node is a dummy node + _Where++; + + for (;;) + { + if (_Where == _Last) + { + return end(); + } + else if (_M_split_ordered_list._Get_iterator(_Where) == _Iterator) + { + return _M_split_ordered_list._Erase(_Previous, _Iterator); + } + + // Move the iterator forward + _Previous = _Where; + _Where++; + } + } + + // Return the [begin, end] pair of iterators with the same key values. + // This operation makes sense only if mapping is many-to-one. + std::pair<iterator, iterator> _Equal_range(const key_type& _Keyval) + { + _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval); + size_type _Bucket = _Order_key % _M_number_of_buckets; + + // If _Bucket is empty, initialize it first + if (!_Is_initialized(_Bucket)) + { + _Initialize_bucket(_Bucket); + } + + _Order_key = _Split_order_regular_key(_Order_key); + _Full_iterator _Last = _M_split_ordered_list._End(); + + for (_Full_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++) + { + if (_Mylist::_Get_key(_Iterator) > _Order_key) + { + // There is no element with the given key + return std::pair<iterator, iterator>(end(), end()); + } + else if (_Mylist::_Get_key(_Iterator) == _Order_key && !_M_comparator(_Key_function(*_Iterator), _Keyval)) + { + iterator _Begin = _M_split_ordered_list._Get_iterator(_Iterator); + iterator _End= _Begin; + + for (;_End != end() && !_M_comparator(_Key_function(*_End), _Keyval); _End++) + { + } + + return std::pair<iterator, iterator>(_Begin, _End); + } + } + + return std::pair<iterator, iterator>(end(), end()); + } + + // Return the [begin, end] pair of const iterators with the same key values. + // This operation makes sense only if mapping is many-to-one. + std::pair<const_iterator, const_iterator> _Equal_range(const key_type& _Keyval) const + { + _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval); + size_type _Bucket = _Order_key % _M_number_of_buckets; + + // If _Bucket has not been initialized, keep searching up for a parent bucket + // that has been initialized. Worst case is the entire map will be read. + while (!_Is_initialized(_Bucket)) + { + _Bucket = _Get_parent(_Bucket); + } + + _Order_key = _Split_order_regular_key(_Order_key); + _Full_const_iterator _Last = _M_split_ordered_list._End(); + + for (_Full_const_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++) + { + if (_Mylist::_Get_key(_Iterator) > _Order_key) + { + // There is no element with the given key + return std::pair<const_iterator, const_iterator>(end(), end()); + } + else if (_Mylist::_Get_key(_Iterator) == _Order_key && !_M_comparator(_Key_function(*_Iterator), _Keyval)) + { + const_iterator _Begin = _M_split_ordered_list._Get_iterator(_Iterator); + const_iterator _End = _Begin; + + for (; _End != end() && !_M_comparator(_Key_function(*_End), _Keyval); _End++) + { + } + + return std::pair<const_iterator, const_iterator>(_Begin, _End); + } + } + + return std::pair<const_iterator, const_iterator>(end(), end()); + } + + // Bucket APIs + void _Initialize_bucket(size_type _Bucket) + { + // Bucket 0 has no parent. Initialize it and return. + if (_Bucket == 0) + { + _Init(); + return; + } + + size_type _Parent_bucket = _Get_parent(_Bucket); + + // All _Parent_bucket buckets have to be initialized before this bucket is + if (!_Is_initialized(_Parent_bucket)) + { + _Initialize_bucket(_Parent_bucket); + } + + _Full_iterator _Parent = _Get_bucket(_Parent_bucket); + + // Create a dummy first node in this bucket + _Full_iterator _Dummy_node = _M_split_ordered_list._Insert_dummy(_Parent, _Split_order_dummy_key(_Bucket)); + _Set_bucket(_Bucket, _Dummy_node); + } + + void _Adjust_table_size(size_type _Total_elements, size_type _Current_size) + { + // Grow the table by a factor of 2 if possible and needed + if (((float) _Total_elements / (float) _Current_size) > _M_maximum_bucket_size) + { + // Double the size of the hash only if size has not changed inbetween loads + _InterlockedCompareExchangeSizeT(&_M_number_of_buckets, 2 * _Current_size, _Current_size); + } + } + + size_type _Get_parent(size_type _Bucket) const + { + // Unsets bucket's most significant turned-on bit + unsigned char _Msb = _Get_msb(_Bucket); + return _Bucket & ~(1 << _Msb); + } + + // Dynamic sized array (segments) + _Full_iterator _Get_bucket(size_type _Bucket) const + { + size_type _Segment = _Segment_index_of(_Bucket); + _Bucket -= _Segment_base(_Segment); + return _M_buckets[_Segment][_Bucket]; + } + + void _Set_bucket(size_type _Bucket, _Full_iterator _Dummy_head) + { + size_type _Segment = _Segment_index_of(_Bucket); + _Bucket -= _Segment_base(_Segment); + + if (_M_buckets[_Segment] == NULL) + { + size_type _Seg_size = _Segment_size(_Segment); + _Full_iterator * _New_segment = _M_allocator.allocate(_Seg_size); + std::_Wrap_alloc<decltype(_M_allocator)> _Wrapped_allocator(_M_allocator); + std::_Uninitialized_default_fill_n(_New_segment, _Seg_size, _Wrapped_allocator); + if (_InterlockedCompareExchangePointer((void * volatile *) &_M_buckets[_Segment], _New_segment, NULL) != NULL) + { + _M_allocator.deallocate(_New_segment, _Seg_size); + } + } + _M_buckets[_Segment][_Bucket] = _Dummy_head; + } + + bool _Is_initialized(size_type _Bucket) const + { + size_type _Segment = _Segment_index_of(_Bucket); + _Bucket -= _Segment_base(_Segment); + + if (_M_buckets[_Segment] == NULL) + { + return false; + } + + _Full_iterator _Iterator = _M_buckets[_Segment][_Bucket]; + return (_Iterator._Mynode() != NULL); + } + + // Utilities for keys + _Split_order_key _Reverse(_Map_key _Order_key) const + { + _Split_order_key _Reversed_order_key; + + unsigned char * _Original = (unsigned char *) &_Order_key; + unsigned char * _Reversed = (unsigned char *) &_Reversed_order_key; + + int _Size = sizeof(_Map_key); + for (int _Index = 0; _Index < _Size; _Index++) + { + _Reversed[_Size - _Index - 1] = _Reverse_byte(_Original[_Index]); + } + + return _Reversed_order_key; + } + + // A regular order key has its original hash value reversed and the last bit set + _Split_order_key _Split_order_regular_key(_Map_key _Order_key) const + { + return _Reverse(_Order_key) | 0x1; + } + + // A dummy order key has its original hash value reversed and the last bit unset + _Split_order_key _Split_order_dummy_key(_Map_key _Order_key) const + { + return _Reverse(_Order_key) & ~(0x1); + } + + // Shared variables + _Full_iterator * _M_buckets[_Pointers_per_table]; // The segment table + _Mylist _M_split_ordered_list; // List where all the elements are kept + typename allocator_type::template rebind<_Full_iterator>::other _M_allocator; // Allocator object for segments + size_type _M_number_of_buckets; // Current table size + float _M_maximum_bucket_size; // Maximum size of the bucket +}; + +#pragma warning(pop) // Warning 4127 -- while (true) has a constant expression in it + +} // namespace details; +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma pack(pop) diff --git a/test_data/lots_of_files/internal_split_ordered_list.h b/test_data/lots_of_files/internal_split_ordered_list.h new file mode 100644 index 0000000..a393a3a --- /dev/null +++ b/test_data/lots_of_files/internal_split_ordered_list.h @@ -0,0 +1,790 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* internal_split_ordered_list.h +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +// Needed for forward iterators +#include <forward_list> +#include <concrt.h> + +#pragma pack(push,_CRT_PACKING) + +#pragma warning (push) +#pragma warning (disable: 4510 4512 4610) // disable warnings for compiler unable to generate constructor + +namespace Concurrency +{ +namespace details +{ +// Split-order list iterators, needed to skip dummy elements +template<class _Mylist> +class _Solist_const_iterator : public std::_Flist_const_iterator<_Mylist> +{ +public: + typedef _Solist_const_iterator<_Mylist> _Myiter; + typedef std::_Flist_const_iterator<_Mylist> _Mybase; + typedef std::forward_iterator_tag iterator_category; + + typedef typename _Mylist::_Nodeptr _Nodeptr; + typedef typename _Mylist::value_type value_type; + typedef typename _Mylist::difference_type difference_type; + typedef typename _Mylist::const_pointer pointer; + typedef typename _Mylist::const_reference reference; + + _Solist_const_iterator() + { + } + + _Solist_const_iterator(_Nodeptr _Pnode, const _Mylist * _Plist) : _Mybase(_Pnode, _Plist) + { + } + + typedef _Solist_const_iterator<_Mylist> _Unchecked_type; + + _Myiter& _Rechecked(_Unchecked_type _Right) + { + _Ptr = _Right._Ptr; + return (*this); + } + + _Unchecked_type _Unchecked() const + { + return (_Unchecked_type(_Ptr, (_Mylist *)_Getcont())); + } + + reference operator*() const + { + return ((reference)**(_Mybase *)this); + } + + pointer operator->() const + { + return (&**this); + } + + _Myiter& operator++() + { + do + { + ++(*(_Mybase *)this); + } + while (_Mynode() != NULL && _Mynode()->_Is_dummy()); + + return (*this); + } + + _Myiter operator++(int) + { + _Myiter _Tmp = *this; + do + { + ++*this; + } + while (_Mynode() != NULL && _Mynode()->_Is_dummy()); + + return (_Tmp); + } +}; + +template<class _Mylist> inline +typename _Solist_const_iterator<_Mylist>::_Unchecked_type _Unchecked(_Solist_const_iterator<_Mylist> _Iterator) +{ + return (_Iterator._Unchecked()); +} + +template<class _Mylist> inline +_Solist_const_iterator<_Mylist>& _Rechecked(_Solist_const_iterator<_Mylist>& _Iterator, + typename _Solist_const_iterator<_Mylist>::_Unchecked_type _Right) +{ + return (_Iterator._Rechecked(_Right)); +} + +template<class _Mylist> +class _Solist_iterator : public _Solist_const_iterator<_Mylist> +{ +public: + typedef _Solist_iterator<_Mylist> _Myiter; + typedef _Solist_const_iterator<_Mylist> _Mybase; + typedef std::forward_iterator_tag iterator_category; + + typedef typename _Mylist::_Nodeptr _Nodeptr; + typedef typename _Mylist::value_type value_type; + typedef typename _Mylist::difference_type difference_type; + typedef typename _Mylist::pointer pointer; + typedef typename _Mylist::reference reference; + + _Solist_iterator() + { + } + + _Solist_iterator(_Nodeptr _Pnode, const _Mylist *_Plist) : _Mybase(_Pnode, _Plist) + { + } + + typedef _Solist_iterator<_Mylist> _Unchecked_type; + + _Myiter& _Rechecked(_Unchecked_type _Right) + { + _Ptr = _Right._Ptr; + return (*this); + } + + _Unchecked_type _Unchecked() const + { + return (_Unchecked_type(_Ptr, (_Mylist *)_Getcont())); + } + + reference operator*() const + { + return ((reference)**(_Mybase *)this); + } + + pointer operator->() const + { + return (&**this); + } + + _Myiter& operator++() + { + do + { + ++(*(_Mybase *)this); + } + while (_Mynode() != NULL && _Mynode()->_Is_dummy()); + + return (*this); + } + + _Myiter operator++(int) + { + _Myiter _Tmp = *this; + do + { + ++*this; + } + while (_Mynode() != NULL && _Mynode()->_Is_dummy()); + + return (_Tmp); + } +}; + +template<class _Mylist> inline +typename _Solist_iterator<_Mylist>::_Unchecked_type _Unchecked(_Solist_iterator<_Mylist> _Iterator) +{ + return (_Iterator._Unchecked()); +} + +template<class _Mylist> inline +_Solist_iterator<_Mylist>& _Rechecked(_Solist_iterator<_Mylist>& _Iterator, + typename _Solist_iterator<_Mylist>::_Unchecked_type _Right) +{ + return (_Iterator._Rechecked(_Right)); +} + +// Forward type and class definitions +typedef size_t _Map_key; +typedef _Map_key _Split_order_key; + +template<typename _Element_type, typename _Allocator_type> +class _Split_order_list_node : public std::_Container_base +{ +public: + typedef typename _Allocator_type::template rebind<_Element_type>::other _Allocator_type; + typedef typename _Allocator_type::size_type size_type; + typedef typename _Element_type value_type; + + struct _Node; + typedef _Node * _Nodeptr; + typedef _Nodeptr& _Nodepref; + + // Node that holds the element in a split-ordered list + struct _Node + { + // Initialize the node with the given order key + void _Init(_Split_order_key _Order_key) + { + _M_order_key = _Order_key; + _M_next = NULL; + } + + // Return the order key (needed for hashing) + _Split_order_key _Get_order_key() const + { + return _M_order_key; + } + + // Inserts the new element in the list in an atomic fashion + _Nodeptr _Atomic_set_next(_Nodeptr _New_node, _Nodeptr _Current_node) + { + // Try to change the next pointer on the current element to a new element, only if it still points to the cached next + _Nodeptr _Exchange_node = (_Nodeptr) _InterlockedCompareExchangePointer((void * volatile *) &_M_next, _New_node, _Current_node); + + if (_Exchange_node == _Current_node) + { + // Operation succeeded, return the new node + return _New_node; + } + else + { + // Operation failed, return the "interfering" node + return _Exchange_node; + } + } + + // Checks if this element in the list is a dummy, order-enforcing node. Dummy nodes are used by buckets + // in the hash table to quickly index into the right subsection of the split-ordered list. + bool _Is_dummy() const + { + return (_M_order_key & 0x1) == 0; + } + + // dummy default constructor - never used but for suppress warning + _Node(); + + _Nodeptr _M_next; // Next element in the list + value_type _M_element; // Element storage + _Split_order_key _M_order_key; // Order key for this element + }; + +#if _ITERATOR_DEBUG_LEVEL == 0 + _Split_order_list_node(_Allocator_type _Allocator) : _M_node_allocator(_Allocator), _M_value_allocator(_Allocator) + { + } +#else /* _ITERATOR_DEBUG_LEVEL == 0 */ + _Split_order_list_node(_Allocator_type _Allocator) : _M_node_allocator(_Allocator), _M_value_allocator(_Allocator) + { + typename _Allocator_type::template rebind<std::_Container_proxy>::other _Alproxy(_M_node_allocator); + _Myproxy = _Alproxy.allocate(1); + _Alproxy.construct(_Myproxy, std::_Container_proxy()); + _Myproxy->_Mycont = this; + } + + ~_Split_order_list_node() + { + typename _Allocator_type::template rebind<std::_Container_proxy>::other _Alproxy(_M_node_allocator); + _Orphan_all(); + _Alproxy.destroy(_Myproxy); + _Alproxy.deallocate(_Myproxy, 1); + _Myproxy = 0; + } +#endif /* _ITERATOR_DEBUG_LEVEL == 0 */ + + _Nodeptr _Myhead; // pointer to head node + typename _Allocator_type::template rebind<_Node>::other _M_node_allocator; // allocator object for nodes + _Allocator_type _M_value_allocator; // allocator object for element values +}; + +template<typename _Element_type, typename _Allocator_type> +class _Split_order_list_value : public _Split_order_list_node<_Element_type, _Allocator_type> +{ +public: + typedef _Split_order_list_node<_Element_type, _Allocator_type> _Mybase; + typedef typename _Mybase::_Nodeptr _Nodeptr; + typedef typename _Mybase::_Nodepref _Nodepref; + typedef typename _Allocator_type::template rebind<_Element_type>::other _Allocator_type; + + typedef typename _Allocator_type::size_type size_type; + typedef typename _Allocator_type::difference_type difference_type; + typedef typename _Allocator_type::pointer pointer; + typedef typename _Allocator_type::const_pointer const_pointer; + typedef typename _Allocator_type::reference reference; + typedef typename _Allocator_type::const_reference const_reference; + typedef typename _Allocator_type::value_type value_type; + + _Split_order_list_value(_Allocator_type _Allocator = _Allocator_type()) : _Mybase(_Allocator) + { + // Immediately allocate a dummy node with order key of 0. This node + // will always be the head of the list. + _Myhead = _Buynode(0); + } + + ~_Split_order_list_value() + { + } + + // Allocate a new node with the given order key and value + template<typename _ValTy> + _Nodeptr _Buynode(_Split_order_key _Order_key, _ValTy&& _Value) + { + _Nodeptr _Pnode = _M_node_allocator.allocate(1); + + try + { + _M_value_allocator.construct(std::addressof(_Myval(_Pnode)), std::forward<_ValTy>(_Value)); + _Pnode->_Init(_Order_key); + } + catch(...) + { + _M_node_allocator.deallocate(_Pnode, 1); + throw; + } + + return (_Pnode); + } + + // Allocate a new node with the given order key; used to allocate dummy nodes + _Nodeptr _Buynode(_Split_order_key _Order_key) + { + _Nodeptr _Pnode = _M_node_allocator.allocate(1); + _Pnode->_Init(_Order_key); + + return (_Pnode); + } + + // Get the next node + static _Nodepref _Nextnode(_Nodeptr _Pnode) + { + return ((_Nodepref)(*_Pnode)._M_next); + } + + // Get the stored value + static reference _Myval(_Nodeptr _Pnode) + { + return ((reference)(*_Pnode)._M_element); + } +}; + +// Forward list in which elements are sorted in a split-order +template <typename _Element_type, typename _Element_allocator_type = std::allocator<_Element_type> > +class _Split_ordered_list : _Split_order_list_value<_Element_type, _Element_allocator_type> +{ +public: + typedef _Split_ordered_list<_Element_type, _Element_allocator_type> _Mytype; + typedef _Split_order_list_value<_Element_type, _Element_allocator_type> _Mybase; + typedef typename _Mybase::_Allocator_type _Allocator_type; + typedef typename _Mybase::_Nodeptr _Nodeptr; + + typedef _Allocator_type allocator_type; + typedef typename _Allocator_type::size_type size_type; + typedef typename _Allocator_type::difference_type difference_type; + typedef typename _Allocator_type::pointer pointer; + typedef typename _Allocator_type::const_pointer const_pointer; + typedef typename _Allocator_type::reference reference; + typedef typename _Allocator_type::const_reference const_reference; + typedef typename _Allocator_type::value_type value_type; + + typedef _Solist_const_iterator<_Mybase> const_iterator; + typedef _Solist_iterator<_Mybase> iterator; + typedef std::_Flist_const_iterator<_Mybase> _Full_const_iterator; + typedef std::_Flist_iterator<_Mybase> _Full_iterator; + typedef std::pair<iterator, bool> _Pairib; + using _Split_order_list_value<_Element_type, _Element_allocator_type>::_Buynode; + _Split_ordered_list(_Allocator_type _Allocator = allocator_type()) : _Mybase(_Allocator), _M_element_count(0) + { + } + + ~_Split_ordered_list() + { + // Clear the list + clear(); + + // Remove the head element which is not cleared by clear() + _Nodeptr _Pnode = _Myhead; + _Myhead = NULL; + + _ASSERT_EXPR(_Pnode != NULL && _Nextnode(_Pnode) == NULL, L"Invalid head list node"); + + _Erase(_Pnode); + } + + // Common forward list functions + + allocator_type get_allocator() const + { + return (_M_value_allocator); + } + + void clear() + { +#if _ITERATOR_DEBUG_LEVEL == 2 + _Orphan_ptr(*this, 0); +#endif /* _ITERATOR_DEBUG_LEVEL == 2 */ + + _Nodeptr _Pnext; + _Nodeptr _Pnode = _Myhead; + + _ASSERT_EXPR(_Myhead != NULL, L"Invalid head list node"); + _Pnext = _Nextnode(_Pnode); + _Pnode->_M_next = NULL; + _Pnode = _Pnext; + + while (_Pnode != NULL) + { + _Pnext = _Nextnode(_Pnode); + _Erase(_Pnode); + _Pnode = _Pnext; + } + + _M_element_count = 0; + } + + // Returns a first non-dummy element in the SOL + iterator begin() + { + _Full_iterator _Iterator = _Begin(); + return _Get_first_real_iterator(_Iterator); + } + + // Returns a first non-dummy element in the SOL + const_iterator begin() const + { + _Full_const_iterator _Iterator = _Begin(); + return _Get_first_real_iterator(_Iterator); + } + + iterator end() + { + return (iterator(0, this)); + } + + const_iterator end() const + { + return (const_iterator(0, this)); + } + + const_iterator cbegin() const + { + return (((const _Mytype *)this)->begin()); + } + + const_iterator cend() const + { + return (((const _Mytype *)this)->end()); + } + + // Checks if the number of elements (non-dummy) is 0 + bool empty() const + { + return (_M_element_count == 0); + } + + // Returns the number of non-dummy elements in the list + size_type size() const + { + return _M_element_count; + } + + // Returns the maximum size of the list, determined by the allocator + size_type max_size() const + { + return _M_value_allocator.max_size(); + } + + // Swaps 'this' list with the passed in one + void swap(_Mytype& _Right) + { + if (this == &_Right) + { + // Nothing to do + return; + } + + if (_M_value_allocator == _Right._M_value_allocator) + { + _Swap_all(_Right); + std::swap(_Myhead, _Right._Myhead); + std::swap(_M_element_count, _Right._M_element_count); + } + else + { + _Mytype _Temp_list(this->get_allocator()); + _Temp_list._Move_all(_Right); + _Right._Move_all(*this); + _Move_all(_Temp_list); + } + } + + // Split-order list functions + + // Returns a first element in the SOL, which is always a dummy + _Full_iterator _Begin() + { + return _Full_iterator(_Myhead, this); + } + + // Returns a first element in the SOL, which is always a dummy + _Full_const_iterator _Begin() const + { + return _Full_const_iterator(_Myhead, this); + } + + _Full_iterator _End() + { + return _Full_iterator(0, this); + } + + _Full_const_iterator _End() const + { + return _Full_const_iterator(0, this); + } + + static _Split_order_key _Get_key(const _Full_const_iterator& _Iterator) + { + return _Iterator._Mynode()->_Get_order_key(); + } + + // Returns a public iterator version of the internal iterator. Public iterator must not + // be a dummy private iterator. + iterator _Get_iterator(_Full_iterator _Iterator) + { + _ASSERT_EXPR(_Iterator._Mynode() != NULL && !_Iterator._Mynode()->_Is_dummy(), L"Invalid user node (dummy)"); + return iterator(_Iterator._Mynode(), this); + } + + // Returns a public iterator version of the internal iterator. Public iterator must not + // be a dummy private iterator. + const_iterator _Get_iterator(_Full_const_iterator _Iterator) const + { + _ASSERT_EXPR(_Iterator._Mynode() != NULL && !_Iterator._Mynode()->_Is_dummy(), L"Invalid user node (dummy)"); + return const_iterator(_Iterator._Mynode(), this); + } + + // Returns a non-const version of the _Full_iterator + _Full_iterator _Get_iterator(_Full_const_iterator _Iterator) + { + return _Full_iterator(_Iterator._Mynode(), this); + } + + // Returns a non-const version of the iterator + iterator _Get_iterator(const_iterator _Iterator) + { + return iterator(_Iterator._Mynode(), this); + } + + // Returns a public iterator version of a first non-dummy internal iterator at or after + // the passed in internal iterator. + iterator _Get_first_real_iterator(_Full_iterator _Iterator) + { + // Skip all dummy, internal only iterators + while (_Iterator != _End() && _Iterator._Mynode()->_Is_dummy()) + { + _Iterator++; + } + + return iterator(_Iterator._Mynode(), this); + } + + // Returns a public iterator version of a first non-dummy internal iterator at or after + // the passed in internal iterator. + const_iterator _Get_first_real_iterator(_Full_const_iterator _Iterator) const + { + // Skip all dummy, internal only iterators + while (_Iterator != _End() && _Iterator._Mynode()->_Is_dummy()) + { + _Iterator++; + } + + return const_iterator(_Iterator._Mynode(), this); + } + + // Erase an element using the allocator + void _Erase(_Nodeptr _Delete_node) + { + if (!_Delete_node->_Is_dummy()) + { + // Dummy nodes have nothing constructed, thus should not be destroyed. + _M_node_allocator.destroy(_Delete_node); + } + _M_node_allocator.deallocate(_Delete_node, 1); + } + + // Try to insert a new element in the list. If insert fails, return the node that + // was inserted instead. + _Nodeptr _Insert(_Nodeptr _Previous, _Nodeptr _New_node, _Nodeptr _Current_node) + { + _New_node->_M_next = _Current_node; + return _Previous->_Atomic_set_next(_New_node, _Current_node); + } + + // Insert a new element between passed in iterators + _Pairib _Insert(_Full_iterator _Iterator, _Full_iterator _Next, _Nodeptr _List_node, long * _New_count) + { + _Nodeptr _Inserted_node = _Insert(_Iterator._Mynode(), _List_node, _Next._Mynode()); + + if (_Inserted_node == _List_node) + { + // If the insert succeeded, check that the order is correct and increment the element count + _Check_range(); + *_New_count = _InterlockedIncrement(&_M_element_count); + return _Pairib(iterator(_List_node, this), true); + } + else + { + return _Pairib(end(), false); + } + } + + // Insert a new dummy element, starting search at a parent dummy element + _Full_iterator _Insert_dummy(_Full_iterator _Iterator, _Split_order_key _Order_key) + { + _Full_iterator _Last = _End(); + _Full_iterator _Where = _Iterator; + + _ASSERT_EXPR(_Where != _Last, L"Invalid head node"); + + _Where++; + + // Create a dummy element up front, even though it may be discarded (due to concurrent insertion) + _Nodeptr _Dummy_node = _Buynode(_Order_key); + + for (;;) + { + _ASSERT_EXPR(_Iterator != _Last, L"Invalid head list node"); + + // If the head iterator is at the end of the list, or past the point where this dummy + // node needs to be inserted, then try to insert it. + if (_Where == _Last || _Get_key(_Where) > _Order_key) + { + _ASSERT_EXPR(_Get_key(_Iterator) < _Order_key, L"Invalid node order in the list"); + + // Try to insert it in the right place + _Nodeptr _Inserted_node = _Insert(_Iterator._Mynode(), _Dummy_node, _Where._Mynode()); + + if (_Inserted_node == _Dummy_node) + { + // Insertion succeeded, check the list for order violations + _Check_range(); + return _Full_iterator(_Dummy_node, this); + } + else + { + // Insertion failed: either dummy node was inserted by another thread, or + // a real element was inserted at exactly the same place as dummy node. + // Proceed with the search from the previous location where order key was + // known to be larger (note: this is legal only because there is no safe + // concurrent erase operation supported). + _Where = _Iterator; + _Where++; + continue; + } + } + else if (_Get_key(_Where) == _Order_key) + { + // Another dummy node with the same value found, discard the new one. + _Erase(_Dummy_node); + return _Where; + } + + // Move the iterator forward + _Iterator = _Where; + _Where++; + } + + } + + // This erase function can handle both real and dummy nodes + void _Erase(_Full_iterator _Previous, _Full_const_iterator& _Where) + { +#if _ITERATOR_DEBUG_LEVEL == 2 + if (_Where._Getcont() != this || _Where._Ptr == _Myhead) + { + std::_DEBUG_ERROR("list erase iterator outside range"); + } + _Nodeptr _Pnode = (_Where++)._Mynode(); + _Orphan_ptr(*this, _Pnode); +#else /* _ITERATOR_DEBUG_LEVEL == 2 */ + _Nodeptr _Pnode = (_Where++)._Mynode(); +#endif /* _ITERATOR_DEBUG_LEVEL == 2 */ + + _Nodeptr _Prevnode = _Previous._Mynode(); + _ASSERT_EXPR(_Prevnode->_M_next == _Pnode, L"Erase must take consecutive iterators"); + _Prevnode->_M_next = _Pnode->_M_next; + + _Erase(_Pnode); + } + + // Erase the element (previous node needs to be passed because this is a forward only list) + iterator _Erase(_Full_iterator _Previous, const_iterator _Where) + { + _Full_const_iterator _Iterator = _Where; + _Erase(_Previous, _Iterator); + _M_element_count--; + + return _Get_iterator(_Get_first_real_iterator(_Iterator)); + } + + // Move all elements from the passed in split-ordered list to this one + void _Move_all(_Mytype& _Source_list) + { + _Full_const_iterator _First = _Source_list._Begin(); + _Full_const_iterator _Last = _Source_list._End(); + + if (_First == _Last) + { + return; + } + + _Nodeptr _Previous_node = _Myhead; + _Full_const_iterator _Begin_iterator = _First++; + + // Move all elements one by one, including dummy ones + for (_Full_const_iterator _Iterator = _First; _Iterator != _Last;) + { + _Nodeptr _Node = _Iterator._Mynode(); + + _Nodeptr _Dummy_node = _Node->_Is_dummy() ? _Buynode(_Node->_Get_order_key()) : _Buynode(_Node->_Get_order_key(), _Myval(_Node)); + _Previous_node = _Insert(_Previous_node, _Dummy_node, NULL); + _ASSERT_EXPR(_Previous_node != NULL, L"Insertion must succeed"); + _Full_const_iterator _Where = _Iterator++; + _Source_list._Erase(_Get_iterator(_Begin_iterator), _Where); + } + } + +private: + + // Check the list for order violations + void _Check_range() + { +#if defined (_DEBUG) + for (_Full_iterator _Iterator = _Begin(); _Iterator != _End(); _Iterator++) + { + _Full_iterator _Next_iterator = _Iterator; + _Next_iterator++; + + _ASSERT_EXPR(_Next_iterator == end() || _Next_iterator._Mynode()->_Get_order_key() >= _Iterator._Mynode()->_Get_order_key(), L"!!! List order inconsistency !!!"); + } +#endif /* defined (_DEBUG) */ + } + +#if _ITERATOR_DEBUG_LEVEL == 2 + void _Orphan_ptr(_Mytype& _Cont, _Nodeptr _Ptr) const + { + std::_Lockit _Lock(_LOCK_DEBUG); + const_iterator **_Pnext = (const_iterator **)_Cont._Getpfirst(); + if (_Pnext != 0) + { + while (*_Pnext != 0) + { + if ((*_Pnext)->_Ptr == (_Nodeptr)&_Myhead || _Ptr != 0 && (*_Pnext)->_Ptr != _Ptr) + { + _Pnext = (const_iterator **)(*_Pnext)->_Getpnext(); + } + else + { + (*_Pnext)->_Clrcont(); + *_Pnext = *(const_iterator **)(*_Pnext)->_Getpnext(); + } + } + } + } +#endif /* _ITERATOR_DEBUG_LEVEL == 2 */ + + volatile long _M_element_count; // Total item count, not counting dummy nodes +}; + +} // namespace details; +} // namespace Concurrency + +namespace concurrency = Concurrency; + +#pragma warning (push) +#pragma pack(pop) diff --git a/test_data/lots_of_files/intrin.h b/test_data/lots_of_files/intrin.h new file mode 100644 index 0000000..d6815fd --- /dev/null +++ b/test_data/lots_of_files/intrin.h @@ -0,0 +1,1069 @@ +/*** +* intrin.h - declarations/definitions for platform specific intrinsic functions. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +*Purpose: +* This include file contains the declarations for platform specific intrinsic +* functions, or will include other files that have declaration of intrinsic +* functions. Also there will be some platform specific macros to be used with +* intrinsic functions. +* +****/ + +#pragma once +#define __INTRIN_H_ +#ifndef RC_INVOKED +#ifndef __midl + +#include <crtdefs.h> +#include <setjmp.h> +#include <stddef.h> + +#if !defined (_M_CEE_PURE) +#if defined (_M_IX86) || defined (_M_X64) || defined (_M_ARM) + +#if defined (_M_IX86) || defined (_M_X64) +#include <immintrin.h> +#include <ammintrin.h> +#endif /* defined (_M_IX86) || defined (_M_X64) */ + +#if defined (_M_IX86) +#include <mm3dnow.h> +#endif /* defined (_M_IX86) */ + +#if defined (_M_ARM) +#include <armintr.h> +#endif /* defined (_M_ARM) */ + +#endif /* defined (_M_IX86) || defined (_M_X64) || defined (_M_ARM) */ + +#endif /* !defined (_M_CEE_PURE) */ + +#if defined (__cplusplus) +extern "C" { +#endif /* defined (__cplusplus) */ + +/* +** __MACHINE : everything +** __MACHINEX86 : x86 only +** __MACHINEX64 : x64 only +** __MACHINEX86_X64 : x86 and x64 only +** __MACHINEARM : ARM only +** __MACHINEARM_X64 : ARM and x64 only +** __MACHINEWVMPURE : /clr:pure only +** __MACHINEZ : nothing +*/ + +#define __MACHINEX86 __MACHINE +#define __MACHINEX64 __MACHINE +#define __MACHINEX86_X64 __MACHINE +#define __MACHINEARM __MACHINE +#define __MACHINEARM_X64 __MACHINE + +/* Most intrinsics not available to pure managed code */ +#if defined (_M_CEE_PURE) +#define __MACHINE(X) __MACHINEZ(X) +#define __MACHINEWVMPURE(X) X; +#else /* defined (_M_CEE_PURE) */ +#define __MACHINE(X) X; +#define __MACHINEWVMPURE(X) __MACHINEZ(X) +#endif /* defined (_M_CEE_PURE) */ + +#define __MACHINEZ(X) /* NOTHING */ + +#if !defined (_M_IX86) +#undef __MACHINEX86 +#define __MACHINEX86 __MACHINEZ +#endif /* !defined (_M_IX86) */ + +#if !defined (_M_X64) +#undef __MACHINEX64 +#define __MACHINEX64 __MACHINEZ +#endif /* !defined (_M_X64) */ + +#if !(defined (_M_IX86) || defined (_M_X64)) +#undef __MACHINEX86_X64 +#define __MACHINEX86_X64 __MACHINEZ +#endif /* !(defined (_M_IX86) || defined (_M_X64)) */ + +#if !(defined (_M_ARM) || defined (_M_X64)) +#undef __MACHINEARM_X64 +#define __MACHINEARM_X64 __MACHINEZ +#endif /* !(defined (_M_ARM) || defined (_M_X64)) */ + +#if !defined (_M_ARM) +#undef __MACHINEARM +#define __MACHINEARM __MACHINEZ +#endif /* !defined (_M_ARM) */ + +/* For compatibility with <winnt.h>, some intrinsics are __cdecl except on x64 */ +#if defined (_M_X64) +#define __MACHINECALL_CDECL_OR_DEFAULT +#else +#define __MACHINECALL_CDECL_OR_DEFAULT __cdecl +#endif + +__MACHINEARM(int _AddSatInt(int, int)) +__MACHINE(void * _AddressOfReturnAddress(void)) +__MACHINE(unsigned char _BitScanForward(unsigned long * _Index, unsigned long _Mask)) +__MACHINEX64(unsigned char _BitScanForward64(unsigned long * _Index, unsigned __int64 _Mask)) +__MACHINE(unsigned char _BitScanReverse(unsigned long * _Index, unsigned long _Mask)) +__MACHINEX64(unsigned char _BitScanReverse64(unsigned long * _Index, unsigned __int64 _Mask)) +__MACHINEARM(double _CopyDoubleFromInt64(__int64)) +__MACHINEARM(float _CopyFloatFromInt32(__int32)) +__MACHINEARM(__int32 _CopyInt32FromFloat(float)) +__MACHINEARM(__int64 _CopyInt64FromDouble(double)) +__MACHINEARM(unsigned int _CountLeadingOnes(unsigned long)) +__MACHINEARM(unsigned int _CountLeadingOnes64(unsigned __int64)) +__MACHINEARM(unsigned int _CountLeadingSigns(long)) +__MACHINEARM(unsigned int _CountLeadingSigns64(__int64)) +__MACHINEARM(unsigned int _CountLeadingZeros(unsigned long)) +__MACHINEARM(unsigned int _CountLeadingZeros64(unsigned __int64)) +__MACHINEARM(unsigned int _CountOneBits(unsigned long)) +__MACHINEARM(unsigned int _CountOneBits64(unsigned __int64)) +__MACHINEARM(int _DAddSatInt(int, int)) +__MACHINEARM(int _DSubSatInt(int, int)) +__MACHINEARM(long _InterlockedAdd(long volatile * _Addend, long _Value)) +__MACHINEARM(__int64 _InterlockedAdd64(__int64 volatile * _Addend, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedAdd64_acq(__int64 volatile * _Addend, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedAdd64_nf(__int64 volatile * _Addend, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedAdd64_rel(__int64 volatile * _Addend, __int64 _Value)) +__MACHINEX86(long _InterlockedAddLargeStatistic(__int64 volatile * _Addend, long _Value)) +__MACHINEARM(long _InterlockedAdd_acq(long volatile * _Addend, long _Value)) +__MACHINEARM(long _InterlockedAdd_nf(long volatile * _Addend, long _Value)) +__MACHINEARM(long _InterlockedAdd_rel(long volatile * _Addend, long _Value)) +__MACHINE(long _InterlockedAnd(long volatile * _Value, long _Mask)) +__MACHINE(short _InterlockedAnd16(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedAnd16_acq(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedAnd16_nf(short volatile * _Value, short _Mask)) +__MACHINEX64(short _InterlockedAnd16_np(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedAnd16_rel(short volatile * _Value, short _Mask)) +__MACHINEARM_X64(__int64 _InterlockedAnd64(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedAnd64_acq(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedAnd64_nf(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEX64(__int64 _InterlockedAnd64_np(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedAnd64_rel(__int64 volatile * _Value, __int64 _Mask)) +__MACHINE(char _InterlockedAnd8(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedAnd8_acq(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedAnd8_nf(char volatile * _Value, char _Mask)) +__MACHINEX64(char _InterlockedAnd8_np(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedAnd8_rel(char volatile * _Value, char _Mask)) +__MACHINEARM(long _InterlockedAnd_acq(long volatile * _Value, long _Mask)) +__MACHINEARM(long _InterlockedAnd_nf(long volatile * _Value, long _Mask)) +__MACHINEX64(long _InterlockedAnd_np(long volatile * _Value, long _Mask)) +__MACHINEARM(long _InterlockedAnd_rel(long volatile * _Value, long _Mask)) +__MACHINE(long __MACHINECALL_CDECL_OR_DEFAULT _InterlockedCompareExchange(long volatile * _Destination, long _Exchange, long _Comparand)) +__MACHINEWVMPURE(long _InterlockedCompareExchange(long volatile * _Destination, long _Exchange, long _Comparand)) +__MACHINEX64(unsigned char _InterlockedCompareExchange128(__int64 volatile * _Destination, __int64 _ExchangeHigh, __int64 _ExchangeLow, __int64 * _ComparandResult)) +__MACHINEX64(unsigned char _InterlockedCompareExchange128_np(__int64 volatile * _Destination, __int64 _ExchangeHigh, __int64 _ExchangeLow, __int64 * _ComparandResult)) +__MACHINE(short _InterlockedCompareExchange16(short volatile * _Destination, short _Exchange, short _Comparand)) +__MACHINEARM(short _InterlockedCompareExchange16_acq(short volatile * _Destination, short _Exchange, short _Comparand)) +__MACHINEARM(short _InterlockedCompareExchange16_nf(short volatile * _Destination, short _Exchange, short _Comparand)) +__MACHINEX64(short _InterlockedCompareExchange16_np(short volatile * _Destination, short _Exchange, short _Comparand)) +__MACHINEARM(short _InterlockedCompareExchange16_rel(short volatile * _Destination, short _Exchange, short _Comparand)) +__MACHINE(__int64 _InterlockedCompareExchange64(__int64 volatile * _Destination, __int64 _Exchange, __int64 _Comparand)) +__MACHINEARM(__int64 _InterlockedCompareExchange64_acq(__int64 volatile * _Destination, __int64 _Exchange, __int64 _Comparand)) +__MACHINEARM(__int64 _InterlockedCompareExchange64_nf(__int64 volatile * _Destination, __int64 _Exchange, __int64 _Comparand)) +__MACHINEX64(__int64 _InterlockedCompareExchange64_np(__int64 volatile * _Destination, __int64 _Exchange, __int64 _Comparand)) +__MACHINEARM(__int64 _InterlockedCompareExchange64_rel(__int64 volatile * _Destination, __int64 _Exchange, __int64 _Comparand)) +__MACHINE(char _InterlockedCompareExchange8(char volatile * _Destination, char _Exchange, char _Comparand)) +__MACHINEARM(char _InterlockedCompareExchange8_acq(char volatile * _Destination, char _Exchange, char _Comparand)) +__MACHINEARM(char _InterlockedCompareExchange8_nf(char volatile * _Destination, char _Exchange, char _Comparand)) +__MACHINEARM(char _InterlockedCompareExchange8_rel(char volatile * _Destination, char _Exchange, char _Comparand)) +__MACHINE(void * _InterlockedCompareExchangePointer(void * volatile * _Destination, void * _Exchange, void * _Comparand)) +__MACHINEARM(void * _InterlockedCompareExchangePointer_acq(void * volatile * _Destination, void * _Exchange, void * _Comparand)) +__MACHINEARM(void * _InterlockedCompareExchangePointer_nf(void * volatile * _Destination, void * _Exchange, void * _Comparand)) +__MACHINEX64(void * _InterlockedCompareExchangePointer_np(void * volatile * _Destination, void * _Exchange, void * _Comparand)) +__MACHINEARM(void * _InterlockedCompareExchangePointer_rel(void * volatile * _Destination, void * _Exchange, void * _Comparand)) +__MACHINEARM(long _InterlockedCompareExchange_acq(long volatile * _Destination, long _Exchange, long _Comparand)) +__MACHINEARM(long _InterlockedCompareExchange_nf(long volatile * _Destination, long _Exchange, long _Comparand)) +__MACHINEX64(long _InterlockedCompareExchange_np(long volatile * _Destination, long _Exchange, long _Comparand)) +__MACHINEARM(long _InterlockedCompareExchange_rel(long volatile * _Destination, long _Exchange, long _Comparand)) +__MACHINE(long __MACHINECALL_CDECL_OR_DEFAULT _InterlockedDecrement(long volatile * _Addend)) +__MACHINEWVMPURE(long _InterlockedDecrement(long volatile * _Addend)) +__MACHINE(short _InterlockedDecrement16(short volatile * _Addend)) +__MACHINEARM(short _InterlockedDecrement16_acq(short volatile * _Addend)) +__MACHINEARM(short _InterlockedDecrement16_nf(short volatile * _Addend)) +__MACHINEARM(short _InterlockedDecrement16_rel(short volatile * _Addend)) +__MACHINEARM_X64(__int64 _InterlockedDecrement64(__int64 volatile * _Addend)) +__MACHINEARM(__int64 _InterlockedDecrement64_acq(__int64 volatile * _Addend)) +__MACHINEARM(__int64 _InterlockedDecrement64_nf(__int64 volatile * _Addend)) +__MACHINEARM(__int64 _InterlockedDecrement64_rel(__int64 volatile * _Addend)) +__MACHINEARM(long _InterlockedDecrement_acq(long volatile * _Addend)) +__MACHINEARM(long _InterlockedDecrement_nf(long volatile * _Addend)) +__MACHINEARM(long _InterlockedDecrement_rel(long volatile * _Addend)) +__MACHINE(long __MACHINECALL_CDECL_OR_DEFAULT _InterlockedExchange(long volatile * _Target, long _Value)) +__MACHINEWVMPURE(long __MACHINECALL_CDECL_OR_DEFAULT _InterlockedExchange(long volatile * _Target, long _Value)) +__MACHINE(short _InterlockedExchange16(short volatile * _Target, short _Value)) +__MACHINEARM(short _InterlockedExchange16_acq(short volatile * _Target, short _Value)) +__MACHINEARM(short _InterlockedExchange16_nf(short volatile * _Target, short _Value)) +__MACHINEARM(short _InterlockedExchange16_rel(short volatile * _Target, short _Value)) +__MACHINEARM_X64(__int64 _InterlockedExchange64(__int64 volatile * _Target, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedExchange64_acq(__int64 volatile * _Target, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedExchange64_nf(__int64 volatile * _Target, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedExchange64_rel(__int64 volatile * _Target, __int64 _Value)) +__MACHINE(char _InterlockedExchange8(char volatile * _Target, char _Value)) +__MACHINEARM(char _InterlockedExchange8_acq(char volatile * _Target, char _Value)) +__MACHINEARM(char _InterlockedExchange8_nf(char volatile * _Target, char _Value)) +__MACHINEARM(char _InterlockedExchange8_rel(char volatile * _Target, char _Value)) +__MACHINE(long __MACHINECALL_CDECL_OR_DEFAULT _InterlockedExchangeAdd(long volatile * _Addend, long _Value)) +__MACHINE(short _InterlockedExchangeAdd16(short volatile * _Addend, short _Value)) +__MACHINEARM(short _InterlockedExchangeAdd16_acq(short volatile * _Addend, short _Value)) +__MACHINEARM(short _InterlockedExchangeAdd16_nf(short volatile * _Addend, short _Value)) +__MACHINEARM(short _InterlockedExchangeAdd16_rel(short volatile * _Addend, short _Value)) +__MACHINEARM_X64(__int64 _InterlockedExchangeAdd64(__int64 volatile * _Addend, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedExchangeAdd64_acq(__int64 volatile * _Addend, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedExchangeAdd64_nf(__int64 volatile * _Addend, __int64 _Value)) +__MACHINEARM(__int64 _InterlockedExchangeAdd64_rel(__int64 volatile * _Addend, __int64 _Value)) +__MACHINE(char _InterlockedExchangeAdd8(char volatile * _Addend, char _Value)) +__MACHINEARM(char _InterlockedExchangeAdd8_acq(char volatile * _Addend, char _Value)) +__MACHINEARM(char _InterlockedExchangeAdd8_nf(char volatile * _Addend, char _Value)) +__MACHINEARM(char _InterlockedExchangeAdd8_rel(char volatile * _Addend, char _Value)) +__MACHINEARM(long _InterlockedExchangeAdd_acq(long volatile * _Addend, long _Value)) +__MACHINEARM(long _InterlockedExchangeAdd_nf(long volatile * _Addend, long _Value)) +__MACHINEARM(long _InterlockedExchangeAdd_rel(long volatile * _Addend, long _Value)) + // Temporarily use macro for x86 due to conflict with inline function in 8.1 SDK winnt.h header +#if !defined(FIXED_592562) && defined (_M_IX86) && !defined (_M_CEE_PURE) +#undef _InterlockedExchangePointer +__forceinline static void * _Intrin_h_InterlockedExchangePointer(void * volatile * _Target, void * _Value) +{ + return (void *)_InterlockedExchange((long volatile *) _Target, (long) _Value); +} +#define _InterlockedExchangePointer(p,v) _Intrin_h_InterlockedExchangePointer(p,v) +#else +__MACHINE(void * _InterlockedExchangePointer(void * volatile * _Target, void * _Value)) +#endif +__MACHINEARM(void * _InterlockedExchangePointer_acq(void * volatile * _Target, void * _Value)) +__MACHINEARM(void * _InterlockedExchangePointer_nf(void * volatile * _Target, void * _Value)) +__MACHINEARM(void * _InterlockedExchangePointer_rel(void * volatile * _Target, void * _Value)) +__MACHINEARM(long _InterlockedExchange_acq(long volatile * _Target, long _Value)) +__MACHINEARM(long _InterlockedExchange_nf(long volatile * _Target, long _Value)) +__MACHINEARM(long _InterlockedExchange_rel(long volatile * _Target, long _Value)) +__MACHINE(long __MACHINECALL_CDECL_OR_DEFAULT _InterlockedIncrement(long volatile * _Addend)) +__MACHINEWVMPURE(long _InterlockedIncrement(long volatile * _Addend)) +__MACHINE(short _InterlockedIncrement16(short volatile * _Addend)) +__MACHINEARM(short _InterlockedIncrement16_acq(short volatile * _Addend)) +__MACHINEARM(short _InterlockedIncrement16_nf(short volatile * _Addend)) +__MACHINEARM(short _InterlockedIncrement16_rel(short volatile * _Addend)) +__MACHINEARM_X64(__int64 _InterlockedIncrement64(__int64 volatile * _Addend)) +__MACHINEARM(__int64 _InterlockedIncrement64_acq(__int64 volatile * _Addend)) +__MACHINEARM(__int64 _InterlockedIncrement64_nf(__int64 volatile * _Addend)) +__MACHINEARM(__int64 _InterlockedIncrement64_rel(__int64 volatile * _Addend)) +__MACHINEARM(long _InterlockedIncrement_acq(long volatile * _Addend)) +__MACHINEARM(long _InterlockedIncrement_nf(long volatile * _Addend)) +__MACHINEARM(long _InterlockedIncrement_rel(long volatile * _Addend)) +__MACHINE(long _InterlockedOr(long volatile * _Value, long _Mask)) +__MACHINE(short _InterlockedOr16(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedOr16_acq(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedOr16_nf(short volatile * _Value, short _Mask)) +__MACHINEX64(short _InterlockedOr16_np(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedOr16_rel(short volatile * _Value, short _Mask)) +__MACHINEARM_X64(__int64 _InterlockedOr64(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedOr64_acq(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedOr64_nf(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEX64(__int64 _InterlockedOr64_np(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedOr64_rel(__int64 volatile * _Value, __int64 _Mask)) +__MACHINE(char _InterlockedOr8(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedOr8_acq(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedOr8_nf(char volatile * _Value, char _Mask)) +__MACHINEX64(char _InterlockedOr8_np(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedOr8_rel(char volatile * _Value, char _Mask)) +__MACHINEARM(long _InterlockedOr_acq(long volatile * _Value, long _Mask)) +__MACHINEARM(long _InterlockedOr_nf(long volatile * _Value, long _Mask)) +__MACHINEX64(long _InterlockedOr_np(long volatile * _Value, long _Mask)) +__MACHINEARM(long _InterlockedOr_rel(long volatile * _Value, long _Mask)) +__MACHINE(long _InterlockedXor(long volatile * _Value, long _Mask)) +__MACHINE(short _InterlockedXor16(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedXor16_acq(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedXor16_nf(short volatile * _Value, short _Mask)) +__MACHINEX64(short _InterlockedXor16_np(short volatile * _Value, short _Mask)) +__MACHINEARM(short _InterlockedXor16_rel(short volatile * _Value, short _Mask)) +__MACHINEARM_X64(__int64 _InterlockedXor64(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedXor64_acq(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedXor64_nf(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEX64(__int64 _InterlockedXor64_np(__int64 volatile * _Value, __int64 _Mask)) +__MACHINEARM(__int64 _InterlockedXor64_rel(__int64 volatile * _Value, __int64 _Mask)) +__MACHINE(char _InterlockedXor8(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedXor8_acq(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedXor8_nf(char volatile * _Value, char _Mask)) +__MACHINEX64(char _InterlockedXor8_np(char volatile * _Value, char _Mask)) +__MACHINEARM(char _InterlockedXor8_rel(char volatile * _Value, char _Mask)) +__MACHINEARM(long _InterlockedXor_acq(long volatile * _Value, long _Mask)) +__MACHINEARM(long _InterlockedXor_nf(long volatile * _Value, long _Mask)) +__MACHINEX64(long _InterlockedXor_np(long volatile * _Value, long _Mask)) +__MACHINEARM(long _InterlockedXor_rel(long volatile * _Value, long _Mask)) +__MACHINEARM(unsigned int _MoveFromCoprocessor(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)) +__MACHINEARM(unsigned int _MoveFromCoprocessor2(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)) +__MACHINEARM(unsigned __int64 _MoveFromCoprocessor64(unsigned int, unsigned int, unsigned int)) +__MACHINEARM(void _MoveToCoprocessor(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)) +__MACHINEARM(void _MoveToCoprocessor2(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)) +__MACHINEARM(void _MoveToCoprocessor64(unsigned __int64, unsigned int, unsigned int, unsigned int)) +__MACHINEARM(long _MulHigh(long, long)) +__MACHINEARM(unsigned long _MulUnsignedHigh(unsigned long, unsigned long)) +__MACHINE(void _ReadBarrier(void)) +__MACHINEARM(int _ReadStatusReg(int)) +__MACHINE(void _ReadWriteBarrier(void)) +__MACHINE(void * _ReturnAddress(void)) +__MACHINEARM(int _SubSatInt(int, int)) +__MACHINE(void _WriteBarrier(void)) +__MACHINEARM(void _WriteStatusReg(int, int, int)) +__MACHINEX86(void __addfsbyte(unsigned long, unsigned char)) +__MACHINEX86(void __addfsdword(unsigned long, unsigned long)) +__MACHINEX86(void __addfsword(unsigned long, unsigned short)) +__MACHINEX64(void __addgsbyte(unsigned long, unsigned char)) +__MACHINEX64(void __addgsdword(unsigned long, unsigned long)) +__MACHINEX64(void __addgsqword(unsigned long, unsigned __int64)) +__MACHINEX64(void __addgsword(unsigned long, unsigned short)) +__MACHINE(void __code_seg(const char *)) +__MACHINEX86_X64(void __cpuid(int[4], int)) +__MACHINEX86_X64(void __cpuidex(int[4], int, int)) +__MACHINE(void __cdecl __debugbreak(void)) +__MACHINEARM(void __emit(unsigned __int32)) +__MACHINEX86_X64(__int64 __emul(int, int)) +__MACHINEX86_X64(unsigned __int64 __emulu(unsigned int, unsigned int)) +__MACHINE(__declspec(noreturn) void __fastfail(unsigned int)) +__MACHINEX64(void __faststorefence(void)) +__MACHINEX86_X64(unsigned int __getcallerseflags(void)) +__MACHINEX86_X64(void __halt(void)) +__MACHINEARM(unsigned int __hvc(unsigned int, ...)) +__MACHINEX86_X64(unsigned char __inbyte(unsigned short)) +__MACHINEX86_X64(void __inbytestring(unsigned short, unsigned char *, unsigned long)) +__MACHINEX86(void __incfsbyte(unsigned long)) +__MACHINEX86(void __incfsdword(unsigned long)) +__MACHINEX86(void __incfsword(unsigned long)) +__MACHINEX64(void __incgsbyte(unsigned long)) +__MACHINEX64(void __incgsdword(unsigned long)) +__MACHINEX64(void __incgsqword(unsigned long)) +__MACHINEX64(void __incgsword(unsigned long)) +__MACHINEX86_X64(unsigned long __indword(unsigned short)) +__MACHINEX86_X64(void __indwordstring(unsigned short, unsigned long *, unsigned long)) +__MACHINEX86_X64(void __int2c(void)) +__MACHINEX86_X64(void __invlpg(void *)) +__MACHINEX86_X64(unsigned short __inword(unsigned short)) +__MACHINEX86_X64(void __inwordstring(unsigned short, unsigned short *, unsigned long)) +__MACHINEARM(__int16 __iso_volatile_load16(const volatile __int16 *)) +__MACHINEARM(__int32 __iso_volatile_load32(const volatile __int32 *)) +__MACHINEARM(__int64 __iso_volatile_load64(const volatile __int64 *)) +__MACHINEARM(__int8 __iso_volatile_load8(const volatile __int8 *)) +__MACHINEARM(void __iso_volatile_store16(volatile __int16 *, __int16)) +__MACHINEARM(void __iso_volatile_store32(volatile __int32 *, __int32)) +__MACHINEARM(void __iso_volatile_store64(volatile __int64 *, __int64)) +__MACHINEARM(void __iso_volatile_store8(volatile __int8 *, __int8)) +__MACHINEARM(__int64 __ldrexd(const volatile __int64 *)) +__MACHINEX86_X64(void __lidt(void *)) +__MACHINEX86_X64(unsigned __int64 __ll_lshift(unsigned __int64, int)) +__MACHINEX86_X64(__int64 __ll_rshift(__int64, int)) +__MACHINEX86_X64(unsigned int __lzcnt(unsigned int)) +__MACHINEX86_X64(unsigned short __lzcnt16(unsigned short)) +__MACHINEX64(unsigned __int64 __lzcnt64(unsigned __int64)) +__MACHINEX86_X64(void __movsb(unsigned char *, unsigned char const *, size_t)) +__MACHINEX86_X64(void __movsd(unsigned long *, unsigned long const *, size_t)) +__MACHINEX64(void __movsq(unsigned long long *, unsigned long long const *, size_t)) +__MACHINEX86_X64(void __movsw(unsigned short *, unsigned short const *, size_t)) +__MACHINEX64(__int64 __mulh(__int64, __int64)) +__MACHINE(void __nop(void)) +__MACHINEX86_X64(void __nvreg_restore_fence(void)) +__MACHINEX86_X64(void __nvreg_save_fence(void)) +__MACHINEX86_X64(void __outbyte(unsigned short, unsigned char)) +__MACHINEX86_X64(void __outbytestring(unsigned short, unsigned char *, unsigned long)) +__MACHINEX86_X64(void __outdword(unsigned short, unsigned long)) +__MACHINEX86_X64(void __outdwordstring(unsigned short, unsigned long *, unsigned long)) +__MACHINEX86_X64(void __outword(unsigned short, unsigned short)) +__MACHINEX86_X64(void __outwordstring(unsigned short, unsigned short *, unsigned long)) +__MACHINEX86_X64(unsigned int __popcnt(unsigned int)) +__MACHINEX86_X64(unsigned short __popcnt16(unsigned short)) +__MACHINEX64(unsigned __int64 __popcnt64(unsigned __int64)) +__MACHINEARM(void __cdecl __prefetch(const void *)) +__MACHINEARM(void __cdecl __prefetchw(const void *)) +__MACHINEARM(unsigned __int64 __rdpmccntr64(void)) +__MACHINEX86_X64(unsigned __int64 __rdtsc(void)) +__MACHINEX86_X64(unsigned __int64 __rdtscp(unsigned int *)) +__MACHINEX64(unsigned __int64 __readcr0(void)) +__MACHINEX86(unsigned long __readcr0(void)) +__MACHINEX64(unsigned __int64 __readcr2(void)) +__MACHINEX86(unsigned long __readcr2(void)) +__MACHINEX64(unsigned __int64 __readcr3(void)) +__MACHINEX86(unsigned long __readcr3(void)) +__MACHINEX64(unsigned __int64 __readcr4(void)) +__MACHINEX86(unsigned long __readcr4(void)) +__MACHINEX64(unsigned __int64 __readcr8(void)) +__MACHINEX86(unsigned long __readcr8(void)) +__MACHINEX64(unsigned __int64 __readdr(unsigned int)) +__MACHINEX86(unsigned int __readdr(unsigned int)) +__MACHINEX64(unsigned __int64 __readeflags(void)) +__MACHINEX86(unsigned int __readeflags(void)) +__MACHINEX86(unsigned char __readfsbyte(unsigned long)) +__MACHINEX86(unsigned long __readfsdword(unsigned long)) +__MACHINEX86(unsigned __int64 __readfsqword(unsigned long)) +__MACHINEX86(unsigned short __readfsword(unsigned long)) +__MACHINEX64(unsigned char __readgsbyte(unsigned long)) +__MACHINEX64(unsigned long __readgsdword(unsigned long)) +__MACHINEX64(unsigned __int64 __readgsqword(unsigned long)) +__MACHINEX64(unsigned short __readgsword(unsigned long)) +__MACHINEX86_X64(unsigned __int64 __readmsr(unsigned long)) +__MACHINEX86_X64(unsigned __int64 __readpmc(unsigned long)) +__MACHINEX86_X64(unsigned long __segmentlimit(unsigned long)) +__MACHINEARM(void __sev(void)) +__MACHINEX64(unsigned __int64 __shiftleft128(unsigned __int64 _LowPart, unsigned __int64 _HighPart, unsigned char _Shift)) +__MACHINEX64(unsigned __int64 __shiftright128(unsigned __int64 _LowPart, unsigned __int64 _HighPart, unsigned char _Shift)) +__MACHINEX86_X64(void __sidt(void *)) +__MACHINEARM(void __static_assert(int, const char *)) +__MACHINEX86_X64(void __stosb(unsigned char *, unsigned char, size_t)) +__MACHINEX86_X64(void __stosd(unsigned long *, unsigned long, size_t)) +__MACHINEX64(void __stosq(unsigned __int64 *, unsigned __int64, size_t)) +__MACHINEX86_X64(void __stosw(unsigned short *, unsigned short, size_t)) +__MACHINEX86_X64(void __svm_clgi(void)) +__MACHINEX86_X64(void __svm_invlpga(void *, int)) +__MACHINEX86_X64(void __svm_skinit(int)) +__MACHINEX86_X64(void __svm_stgi(void)) +__MACHINEX86_X64(void __svm_vmload(size_t)) +__MACHINEX86_X64(void __svm_vmrun(size_t)) +__MACHINEX86_X64(void __svm_vmsave(size_t)) +__MACHINEARM(unsigned int __swi(unsigned int, ...)) +__MACHINEARM(unsigned int __svc(unsigned int, ...)) +__MACHINEARM(int __trap(int, ...)) +__MACHINEX86_X64(void __ud2(void)) +__MACHINEX86_X64(unsigned __int64 __ull_rshift(unsigned __int64, int)) +__MACHINEX64(unsigned __int64 __umulh(unsigned __int64, unsigned __int64)) +__MACHINEX86_X64(void __vmx_off(void)) +__MACHINEX64(unsigned char __vmx_on(unsigned __int64 *)) +__MACHINEX64(unsigned char __vmx_vmclear(unsigned __int64 *)) +__MACHINEX64(unsigned char __vmx_vmlaunch(void)) +__MACHINEX64(unsigned char __vmx_vmptrld(unsigned __int64 *)) +__MACHINEX86_X64(void __vmx_vmptrst(unsigned __int64 *)) +__MACHINEX64(unsigned char __vmx_vmread(size_t, size_t *)) +__MACHINEX64(unsigned char __vmx_vmresume(void)) +__MACHINEX64(unsigned char __vmx_vmwrite(size_t, size_t)) +__MACHINEX86_X64(void __wbinvd(void)) +__MACHINEARM(void __wfe(void)) +__MACHINEARM(void __wfi(void)) +__MACHINEX64(void __writecr0(unsigned __int64)) +__MACHINEX86(void __writecr0(unsigned int)) +__MACHINEX64(void __writecr3(unsigned __int64)) +__MACHINEX86(void __writecr3(unsigned int)) +__MACHINEX64(void __writecr4(unsigned __int64)) +__MACHINEX86(void __writecr4(unsigned int)) +__MACHINEX64(void __writecr8(unsigned __int64)) +__MACHINEX86(void __writecr8(unsigned int)) +__MACHINEX64(void __writedr(unsigned int, unsigned __int64)) +__MACHINEX86(void __writedr(unsigned int, unsigned int)) +__MACHINEX64(void __writeeflags(unsigned __int64)) +__MACHINEX86(void __writeeflags(unsigned int)) +__MACHINEX86(void __writefsbyte(unsigned long, unsigned char)) +__MACHINEX86(void __writefsdword(unsigned long, unsigned long)) +__MACHINEX86(void __writefsqword(unsigned long, unsigned __int64)) +__MACHINEX86(void __writefsword(unsigned long, unsigned short)) +__MACHINEX64(void __writegsbyte(unsigned long, unsigned char)) +__MACHINEX64(void __writegsdword(unsigned long, unsigned long)) +__MACHINEX64(void __writegsqword(unsigned long, unsigned __int64)) +__MACHINEX64(void __writegsword(unsigned long, unsigned short)) +__MACHINEX86_X64(void __writemsr(unsigned long, unsigned __int64)) +__MACHINEARM(void __yield(void)) +__MACHINE(unsigned char _bittest(long const *, long)) +__MACHINEX64(unsigned char _bittest64(__int64 const *, __int64)) +__MACHINE(unsigned char _bittestandcomplement(long *, long)) +__MACHINEX64(unsigned char _bittestandcomplement64(__int64 *, __int64)) +__MACHINE(unsigned char _bittestandreset(long *, long)) +__MACHINEX64(unsigned char _bittestandreset64(__int64 *, __int64)) +__MACHINE(unsigned char _bittestandset(long *, long)) +__MACHINEX64(unsigned char _bittestandset64(__int64 *, __int64)) +__MACHINE(_Check_return_ unsigned __int64 __cdecl _byteswap_uint64(_In_ unsigned __int64)) +__MACHINE(_Check_return_ unsigned long __cdecl _byteswap_ulong(_In_ unsigned long)) +__MACHINE(_Check_return_ unsigned short __cdecl _byteswap_ushort(_In_ unsigned short)) +__MACHINE(void __cdecl _disable(void)) +__MACHINE(void __cdecl _enable(void)) +__MACHINE(unsigned char _interlockedbittestandreset(long volatile *, long)) +__MACHINEX64(unsigned char _interlockedbittestandreset64(__int64 volatile *, __int64)) +__MACHINEARM(unsigned char _interlockedbittestandreset_acq(long volatile *, long)) +__MACHINEARM(unsigned char _interlockedbittestandreset_nf(long volatile *, long)) +__MACHINEARM(unsigned char _interlockedbittestandreset_rel(long volatile *, long)) +__MACHINE(unsigned char _interlockedbittestandset(long volatile *, long)) +__MACHINEX64(unsigned char _interlockedbittestandset64(__int64 volatile *, __int64)) +__MACHINEARM(unsigned char _interlockedbittestandset_acq(long volatile *, long)) +__MACHINEARM(unsigned char _interlockedbittestandset_nf(long volatile *, long)) +__MACHINEARM(unsigned char _interlockedbittestandset_rel(long volatile *, long)) +__MACHINEARM(int _isunordered(double, double)) +__MACHINEARM(int _isunorderedf(float, float)) +__MACHINE(_Check_return_ unsigned long __cdecl _lrotl(_In_ unsigned long, _In_ int)) +__MACHINE(_Check_return_ unsigned long __cdecl _lrotr(_In_ unsigned long, _In_ int)) +__MACHINEX86(void _m_empty(void)) +__MACHINEX86(void _m_femms(void)) +__MACHINEX86(__m64 _m_from_float(float)) +__MACHINEX86(__m64 _m_from_int(int)) +__MACHINEX86(void _m_maskmovq(__m64, __m64, char *)) +__MACHINEX86(__m64 _m_packssdw(__m64, __m64)) +__MACHINEX86(__m64 _m_packsswb(__m64, __m64)) +__MACHINEX86(__m64 _m_packuswb(__m64, __m64)) +__MACHINEX86(__m64 _m_paddb(__m64, __m64)) +__MACHINEX86(__m64 _m_paddd(__m64, __m64)) +__MACHINEX86(__m64 _m_paddsb(__m64, __m64)) +__MACHINEX86(__m64 _m_paddsw(__m64, __m64)) +__MACHINEX86(__m64 _m_paddusb(__m64, __m64)) +__MACHINEX86(__m64 _m_paddusw(__m64, __m64)) +__MACHINEX86(__m64 _m_paddw(__m64, __m64)) +__MACHINEX86(__m64 _m_pand(__m64, __m64)) +__MACHINEX86(__m64 _m_pandn(__m64, __m64)) +__MACHINEX86(__m64 _m_pavgb(__m64, __m64)) +__MACHINEX86(__m64 _m_pavgusb(__m64, __m64)) +__MACHINEX86(__m64 _m_pavgw(__m64, __m64)) +__MACHINEX86(__m64 _m_pcmpeqb(__m64, __m64)) +__MACHINEX86(__m64 _m_pcmpeqd(__m64, __m64)) +__MACHINEX86(__m64 _m_pcmpeqw(__m64, __m64)) +__MACHINEX86(__m64 _m_pcmpgtb(__m64, __m64)) +__MACHINEX86(__m64 _m_pcmpgtd(__m64, __m64)) +__MACHINEX86(__m64 _m_pcmpgtw(__m64, __m64)) +__MACHINEX86(int _m_pextrw(__m64, int)) +__MACHINEX86(__m64 _m_pf2id(__m64)) +__MACHINEX86(__m64 _m_pf2iw(__m64)) +__MACHINEX86(__m64 _m_pfacc(__m64, __m64)) +__MACHINEX86(__m64 _m_pfadd(__m64, __m64)) +__MACHINEX86(__m64 _m_pfcmpeq(__m64, __m64)) +__MACHINEX86(__m64 _m_pfcmpge(__m64, __m64)) +__MACHINEX86(__m64 _m_pfcmpgt(__m64, __m64)) +__MACHINEX86(__m64 _m_pfmax(__m64, __m64)) +__MACHINEX86(__m64 _m_pfmin(__m64, __m64)) +__MACHINEX86(__m64 _m_pfmul(__m64, __m64)) +__MACHINEX86(__m64 _m_pfnacc(__m64, __m64)) +__MACHINEX86(__m64 _m_pfpnacc(__m64, __m64)) +__MACHINEX86(__m64 _m_pfrcp(__m64)) +__MACHINEX86(__m64 _m_pfrcpit1(__m64, __m64)) +__MACHINEX86(__m64 _m_pfrcpit2(__m64, __m64)) +__MACHINEX86(__m64 _m_pfrsqit1(__m64, __m64)) +__MACHINEX86(__m64 _m_pfrsqrt(__m64)) +__MACHINEX86(__m64 _m_pfsub(__m64, __m64)) +__MACHINEX86(__m64 _m_pfsubr(__m64, __m64)) +__MACHINEX86(__m64 _m_pi2fd(__m64)) +__MACHINEX86(__m64 _m_pi2fw(__m64)) +__MACHINEX86(__m64 _m_pinsrw(__m64, int, int)) +__MACHINEX86(__m64 _m_pmaddwd(__m64, __m64)) +__MACHINEX86(__m64 _m_pmaxsw(__m64, __m64)) +__MACHINEX86(__m64 _m_pmaxub(__m64, __m64)) +__MACHINEX86(__m64 _m_pminsw(__m64, __m64)) +__MACHINEX86(__m64 _m_pminub(__m64, __m64)) +__MACHINEX86(int _m_pmovmskb(__m64)) +__MACHINEX86(__m64 _m_pmulhrw(__m64, __m64)) +__MACHINEX86(__m64 _m_pmulhuw(__m64, __m64)) +__MACHINEX86(__m64 _m_pmulhw(__m64, __m64)) +__MACHINEX86(__m64 _m_pmullw(__m64, __m64)) +__MACHINEX86(__m64 _m_por(__m64, __m64)) +__MACHINEX86_X64(void _m_prefetch(void *)) +__MACHINEX86_X64(void _m_prefetchw(volatile const void *)) +__MACHINEX86(__m64 _m_psadbw(__m64, __m64)) +__MACHINEX86(__m64 _m_pshufw(__m64, int)) +__MACHINEX86(__m64 _m_pslld(__m64, __m64)) +__MACHINEX86(__m64 _m_pslldi(__m64, int)) +__MACHINEX86(__m64 _m_psllq(__m64, __m64)) +__MACHINEX86(__m64 _m_psllqi(__m64, int)) +__MACHINEX86(__m64 _m_psllw(__m64, __m64)) +__MACHINEX86(__m64 _m_psllwi(__m64, int)) +__MACHINEX86(__m64 _m_psrad(__m64, __m64)) +__MACHINEX86(__m64 _m_psradi(__m64, int)) +__MACHINEX86(__m64 _m_psraw(__m64, __m64)) +__MACHINEX86(__m64 _m_psrawi(__m64, int)) +__MACHINEX86(__m64 _m_psrld(__m64, __m64)) +__MACHINEX86(__m64 _m_psrldi(__m64, int)) +__MACHINEX86(__m64 _m_psrlq(__m64, __m64)) +__MACHINEX86(__m64 _m_psrlqi(__m64, int)) +__MACHINEX86(__m64 _m_psrlw(__m64, __m64)) +__MACHINEX86(__m64 _m_psrlwi(__m64, int)) +__MACHINEX86(__m64 _m_psubb(__m64, __m64)) +__MACHINEX86(__m64 _m_psubd(__m64, __m64)) +__MACHINEX86(__m64 _m_psubsb(__m64, __m64)) +__MACHINEX86(__m64 _m_psubsw(__m64, __m64)) +__MACHINEX86(__m64 _m_psubusb(__m64, __m64)) +__MACHINEX86(__m64 _m_psubusw(__m64, __m64)) +__MACHINEX86(__m64 _m_psubw(__m64, __m64)) +__MACHINEX86(__m64 _m_pswapd(__m64)) +__MACHINEX86(__m64 _m_punpckhbw(__m64, __m64)) +__MACHINEX86(__m64 _m_punpckhdq(__m64, __m64)) +__MACHINEX86(__m64 _m_punpckhwd(__m64, __m64)) +__MACHINEX86(__m64 _m_punpcklbw(__m64, __m64)) +__MACHINEX86(__m64 _m_punpckldq(__m64, __m64)) +__MACHINEX86(__m64 _m_punpcklwd(__m64, __m64)) +__MACHINEX86(__m64 _m_pxor(__m64, __m64)) +__MACHINEX86(float _m_to_float(__m64)) +__MACHINEX86(int _m_to_int(__m64)) +__MACHINEX86_X64(__m128i _mm_abs_epi16(__m128i)) +__MACHINEX86_X64(__m128i _mm_abs_epi32(__m128i)) +__MACHINEX86_X64(__m128i _mm_abs_epi8(__m128i)) +__MACHINEX86_X64(__m64 _mm_abs_pi16(__m64)) +__MACHINEX86_X64(__m64 _mm_abs_pi32(__m64)) +__MACHINEX86_X64(__m64 _mm_abs_pi8(__m64)) +__MACHINEX86_X64(__m128i _mm_add_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_add_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_add_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_add_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_add_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_add_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_add_sd(__m128d, __m128d)) +__MACHINEX86(__m64 _mm_add_si64(__m64, __m64)) +__MACHINEX86_X64(__m128 _mm_add_ss(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_adds_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_adds_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_adds_epu16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_adds_epu8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_addsub_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_addsub_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_alignr_epi8(__m128i, __m128i, int)) +__MACHINEX86_X64(__m64 _mm_alignr_pi8(__m64, __m64, int)) +__MACHINEX86_X64(__m128d _mm_and_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_and_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_and_si128(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_andnot_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_andnot_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_andnot_si128(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_avg_epu16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_avg_epu8(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_blend_epi16(__m128i, __m128i, int)) +__MACHINEX86_X64(__m128d _mm_blend_pd(__m128d, __m128d, int)) +__MACHINEX86_X64(__m128 _mm_blend_ps(__m128, __m128, int)) +__MACHINEX86_X64(__m128i _mm_blendv_epi8(__m128i, __m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_blendv_pd(__m128d, __m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_blendv_ps(__m128, __m128, __m128)) +__MACHINEX86_X64(void _mm_clflush(void const *)) +__MACHINEX86_X64(__m128i _mm_cmpeq_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmpeq_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmpeq_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmpeq_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_cmpeq_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpeq_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpeq_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpeq_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_cmpestra(__m128i, int, __m128i, int, int)) +__MACHINEX86_X64(int _mm_cmpestrc(__m128i, int, __m128i, int, int)) +__MACHINEX86_X64(int _mm_cmpestri(__m128i, int, __m128i, int, int)) +__MACHINEX86_X64(__m128i _mm_cmpestrm(__m128i, int, __m128i, int, int)) +__MACHINEX86_X64(int _mm_cmpestro(__m128i, int, __m128i, int, int)) +__MACHINEX86_X64(int _mm_cmpestrs(__m128i, int, __m128i, int, int)) +__MACHINEX86_X64(int _mm_cmpestrz(__m128i, int, __m128i, int, int)) +__MACHINEX86_X64(__m128d _mm_cmpge_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpge_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpge_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpge_ss(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_cmpgt_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmpgt_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmpgt_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmpgt_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_cmpgt_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpgt_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpgt_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpgt_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_cmpistra(__m128i, __m128i, int)) +__MACHINEX86_X64(int _mm_cmpistrc(__m128i, __m128i, int)) +__MACHINEX86_X64(int _mm_cmpistri(__m128i, __m128i, int)) +__MACHINEX86_X64(__m128i _mm_cmpistrm(__m128i, __m128i, int)) +__MACHINEX86_X64(int _mm_cmpistro(__m128i, __m128i, int)) +__MACHINEX86_X64(int _mm_cmpistrs(__m128i, __m128i, int)) +__MACHINEX86_X64(int _mm_cmpistrz(__m128i, __m128i, int)) +__MACHINEX86_X64(__m128d _mm_cmple_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmple_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmple_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmple_ss(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_cmplt_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmplt_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_cmplt_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_cmplt_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmplt_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmplt_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmplt_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpneq_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpneq_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpneq_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpneq_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpnge_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpnge_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpnge_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpnge_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpngt_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpngt_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpngt_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpngt_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpnle_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpnle_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpnle_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpnle_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpnlt_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpnlt_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpnlt_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpnlt_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpord_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpord_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpord_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpord_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpunord_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpunord_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_cmpunord_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_cmpunord_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_comieq_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_comieq_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_comige_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_comige_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_comigt_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_comigt_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_comile_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_comile_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_comilt_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_comilt_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_comineq_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_comineq_ss(__m128, __m128)) +__MACHINEX86_X64(unsigned int _mm_crc32_u16(unsigned int, unsigned short)) +__MACHINEX86_X64(unsigned int _mm_crc32_u32(unsigned int, unsigned int)) +__MACHINEX64(unsigned __int64 _mm_crc32_u64(unsigned __int64, unsigned __int64)) +__MACHINEX86_X64(unsigned int _mm_crc32_u8(unsigned int, unsigned char)) +__MACHINEX86(__m128 _mm_cvt_pi2ps(__m128, __m64)) +__MACHINEX86(__m64 _mm_cvt_ps2pi(__m128)) +__MACHINEX86_X64(__m128 _mm_cvt_si2ss(__m128, int)) +__MACHINEX86_X64(int _mm_cvt_ss2si(__m128)) +__MACHINEX86_X64(__m128i _mm_cvtepi16_epi32(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepi16_epi64(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepi32_epi64(__m128i)) +__MACHINEX86_X64(__m128d _mm_cvtepi32_pd(__m128i)) +__MACHINEX86_X64(__m128 _mm_cvtepi32_ps(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepi8_epi16(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepi8_epi32(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepi8_epi64(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepu16_epi32(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepu16_epi64(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepu32_epi64(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepu8_epi16(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepu8_epi32(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtepu8_epi64(__m128i)) +__MACHINEX86_X64(__m128i _mm_cvtpd_epi32(__m128d)) +__MACHINEX86(__m64 _mm_cvtpd_pi32(__m128d)) +__MACHINEX86_X64(__m128 _mm_cvtpd_ps(__m128d)) +__MACHINEX86(__m128d _mm_cvtpi32_pd(__m64)) +__MACHINEX86_X64(__m128i _mm_cvtps_epi32(__m128)) +__MACHINEX86_X64(__m128d _mm_cvtps_pd(__m128)) +__MACHINEX86_X64(int _mm_cvtsd_si32(__m128d)) +__MACHINEX64(__int64 _mm_cvtsd_si64(__m128d)) +__MACHINEX64(__int64 _mm_cvtsd_si64x(__m128d)) +__MACHINEX86_X64(__m128 _mm_cvtsd_ss(__m128, __m128d)) +__MACHINEX86_X64(int _mm_cvtsi128_si32(__m128i)) +__MACHINEX64(__int64 _mm_cvtsi128_si64(__m128i)) +__MACHINEX64(__int64 _mm_cvtsi128_si64x(__m128i)) +__MACHINEX86_X64(__m128d _mm_cvtsi32_sd(__m128d, int)) +__MACHINEX86_X64(__m128i _mm_cvtsi32_si128(int)) +__MACHINEX64(__m128d _mm_cvtsi64_sd(__m128d, __int64)) +__MACHINEX64(__m128i _mm_cvtsi64_si128(__int64)) +__MACHINEX64(__m128 _mm_cvtsi64_ss(__m128, __int64)) +__MACHINEX64(__m128d _mm_cvtsi64x_sd(__m128d, __int64)) +__MACHINEX64(__m128i _mm_cvtsi64x_si128(__int64)) +__MACHINEX64(__m128 _mm_cvtsi64x_ss(__m128, __int64)) +__MACHINEX86_X64(__m128d _mm_cvtss_sd(__m128d, __m128)) +__MACHINEX64(__int64 _mm_cvtss_si64(__m128)) +__MACHINEX64(__int64 _mm_cvtss_si64x(__m128)) +__MACHINEX86(__m64 _mm_cvtt_ps2pi(__m128)) +__MACHINEX86_X64(int _mm_cvtt_ss2si(__m128)) +__MACHINEX86_X64(__m128i _mm_cvttpd_epi32(__m128d)) +__MACHINEX86(__m64 _mm_cvttpd_pi32(__m128d)) +__MACHINEX86_X64(__m128i _mm_cvttps_epi32(__m128)) +__MACHINEX86_X64(int _mm_cvttsd_si32(__m128d)) +__MACHINEX64(__int64 _mm_cvttsd_si64(__m128d)) +__MACHINEX64(__int64 _mm_cvttsd_si64x(__m128d)) +__MACHINEX64(__int64 _mm_cvttss_si64(__m128)) +__MACHINEX64(__int64 _mm_cvttss_si64x(__m128)) +__MACHINEX86_X64(__m128d _mm_div_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_div_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_div_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_div_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_dp_pd(__m128d, __m128d, int)) +__MACHINEX86_X64(__m128 _mm_dp_ps(__m128, __m128, int)) +__MACHINEX86_X64(int _mm_extract_epi16(__m128i, int)) +__MACHINEX86_X64(int _mm_extract_epi32(__m128i, int)) +__MACHINEX64(__int64 _mm_extract_epi64(__m128i, int)) +__MACHINEX86_X64(int _mm_extract_epi8(__m128i, int)) +__MACHINEX86_X64(int _mm_extract_ps(__m128, int)) +__MACHINEX86_X64(__m128i _mm_extract_si64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_extracti_si64(__m128i, int, int)) +__MACHINEX86_X64(unsigned int _mm_getcsr(void)) +__MACHINEX86_X64(__m128i _mm_hadd_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_hadd_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_hadd_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m64 _mm_hadd_pi16(__m64, __m64)) +__MACHINEX86_X64(__m64 _mm_hadd_pi32(__m64, __m64)) +__MACHINEX86_X64(__m128 _mm_hadd_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_hadds_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m64 _mm_hadds_pi16(__m64, __m64)) +__MACHINEX86_X64(__m128i _mm_hsub_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_hsub_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_hsub_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m64 _mm_hsub_pi16(__m64, __m64)) +__MACHINEX86_X64(__m64 _mm_hsub_pi32(__m64, __m64)) +__MACHINEX86_X64(__m128 _mm_hsub_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_hsubs_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m64 _mm_hsubs_pi16(__m64, __m64)) +__MACHINEX86_X64(__m128i _mm_insert_epi16(__m128i, int, int)) +__MACHINEX86_X64(__m128i _mm_insert_epi32(__m128i, int, int)) +__MACHINEX64(__m128i _mm_insert_epi64(__m128i, __int64, int)) +__MACHINEX86_X64(__m128i _mm_insert_epi8(__m128i, int, int)) +__MACHINEX86_X64(__m128 _mm_insert_ps(__m128, __m128, int)) +__MACHINEX86_X64(__m128i _mm_insert_si64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_inserti_si64(__m128i, __m128i, int, int)) +__MACHINEX86_X64(__m128i _mm_lddqu_si128(__m128i const *)) +__MACHINEX86_X64(void _mm_lfence(void)) +__MACHINEX86_X64(__m128d _mm_load1_pd(double const *)) +__MACHINEX86_X64(__m128d _mm_load_pd(double const *)) +__MACHINEX86_X64(__m128 _mm_load_ps(float const *)) +__MACHINEX86_X64(__m128 _mm_load_ps1(float const *)) +__MACHINEX86_X64(__m128d _mm_load_sd(double const *)) +__MACHINEX86_X64(__m128i _mm_load_si128(__m128i const *)) +__MACHINEX86_X64(__m128 _mm_load_ss(float const *)) +__MACHINEX86_X64(__m128d _mm_loaddup_pd(double const *)) +__MACHINEX86_X64(__m128d _mm_loadh_pd(__m128d, double const *)) +__MACHINEX86_X64(__m128 _mm_loadh_pi(__m128, __m64 const *)) +__MACHINEX86_X64(__m128i _mm_loadl_epi64(__m128i const *)) +__MACHINEX86_X64(__m128d _mm_loadl_pd(__m128d, double const *)) +__MACHINEX86_X64(__m128 _mm_loadl_pi(__m128, __m64 const *)) +__MACHINEX86_X64(__m128d _mm_loadr_pd(double const *)) +__MACHINEX86_X64(__m128 _mm_loadr_ps(float const *)) +__MACHINEX86_X64(__m128d _mm_loadu_pd(double const *)) +__MACHINEX86_X64(__m128 _mm_loadu_ps(float const *)) +__MACHINEX86_X64(__m128i _mm_loadu_si128(__m128i const *)) +__MACHINEX86_X64(__m128i _mm_madd_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_maddubs_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m64 _mm_maddubs_pi16(__m64, __m64)) +__MACHINEX86_X64(void _mm_maskmoveu_si128(__m128i, __m128i, char *)) +__MACHINEX86_X64(__m128i _mm_max_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_max_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_max_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_max_epu16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_max_epu32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_max_epu8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_max_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_max_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_max_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_max_ss(__m128, __m128)) +__MACHINEX86_X64(void _mm_mfence(void)) +__MACHINEX86_X64(__m128i _mm_min_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_min_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_min_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_min_epu16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_min_epu32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_min_epu8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_min_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_min_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_min_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_min_ss(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_minpos_epu16(__m128i)) +__MACHINEX86_X64(void _mm_monitor(void const *, unsigned int, unsigned int)) +__MACHINEX86_X64(__m128i _mm_move_epi64(__m128i)) +__MACHINEX86_X64(__m128d _mm_move_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_move_ss(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_movedup_pd(__m128d)) +__MACHINEX86_X64(__m128 _mm_movehdup_ps(__m128)) +__MACHINEX86_X64(__m128 _mm_movehl_ps(__m128, __m128)) +__MACHINEX86_X64(__m128 _mm_moveldup_ps(__m128)) +__MACHINEX86_X64(__m128 _mm_movelh_ps(__m128, __m128)) +__MACHINEX86_X64(int _mm_movemask_epi8(__m128i)) +__MACHINEX86_X64(int _mm_movemask_pd(__m128d)) +__MACHINEX86_X64(int _mm_movemask_ps(__m128)) +__MACHINEX86(__m64 _mm_movepi64_pi64(__m128i)) +__MACHINEX86(__m128i _mm_movpi64_epi64(__m64)) +__MACHINEX86_X64(__m128i _mm_mpsadbw_epu8(__m128i, __m128i, int)) +__MACHINEX86_X64(__m128i _mm_mul_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_mul_epu32(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_mul_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_mul_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_mul_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_mul_ss(__m128, __m128)) +__MACHINEX86(__m64 _mm_mul_su32(__m64, __m64)) +__MACHINEX86_X64(__m128i _mm_mulhi_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_mulhi_epu16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_mulhrs_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m64 _mm_mulhrs_pi16(__m64, __m64)) +__MACHINEX86_X64(__m128i _mm_mullo_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_mullo_epi32(__m128i, __m128i)) +__MACHINEX86_X64(void _mm_mwait(unsigned int, unsigned int)) +__MACHINEX86_X64(__m128d _mm_or_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_or_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_or_si128(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_packs_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_packs_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_packus_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_packus_epi32(__m128i, __m128i)) +__MACHINEX86_X64(void _mm_pause(void)) +__MACHINEX86_X64(int _mm_popcnt_u32(unsigned int)) +__MACHINEX64(__int64 _mm_popcnt_u64(unsigned __int64)) +__MACHINEX86_X64(void _mm_prefetch(char const *, int)) +__MACHINEX86_X64(__m128 _mm_rcp_ps(__m128)) +__MACHINEX86_X64(__m128 _mm_rcp_ss(__m128)) +__MACHINEX86_X64(__m128d _mm_round_pd(__m128d, int)) +__MACHINEX86_X64(__m128 _mm_round_ps(__m128, int)) +__MACHINEX86_X64(__m128d _mm_round_sd(__m128d, __m128d, int)) +__MACHINEX86_X64(__m128 _mm_round_ss(__m128, __m128, int)) +__MACHINEX86_X64(__m128 _mm_rsqrt_ps(__m128)) +__MACHINEX86_X64(__m128 _mm_rsqrt_ss(__m128)) +__MACHINEX86_X64(__m128i _mm_sad_epu8(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_set1_epi16(short)) +__MACHINEX86_X64(__m128i _mm_set1_epi32(int)) +__MACHINEX86(__m128i _mm_set1_epi64(__m64)) +__MACHINEX64(__m128i _mm_set1_epi64x(__int64)) +__MACHINEX86_X64(__m128i _mm_set1_epi8(char)) +__MACHINEX86_X64(__m128d _mm_set1_pd(double)) +__MACHINEX86(__m64 _mm_set1_pi16(short)) +__MACHINEX86(__m64 _mm_set1_pi32(int)) +__MACHINEX86(__m64 _mm_set1_pi8(char)) +__MACHINEX86_X64(__m128i _mm_set_epi16(short, short, short, short, short, short, short, short)) +__MACHINEX86_X64(__m128i _mm_set_epi32(int, int, int, int)) +__MACHINEX86(__m128i _mm_set_epi64(__m64, __m64)) +__MACHINEX64(__m128i _mm_set_epi64x(__int64, __int64)) +__MACHINEX86_X64(__m128i _mm_set_epi8(char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char)) +__MACHINEX86_X64(__m128d _mm_set_pd(double, double)) +__MACHINEX86(__m64 _mm_set_pi16(short, short, short, short)) +__MACHINEX86(__m64 _mm_set_pi32(int, int)) +__MACHINEX86(__m64 _mm_set_pi8(char, char, char, char, char, char, char, char)) +__MACHINEX86_X64(__m128 _mm_set_ps(float, float, float, float)) +__MACHINEX86_X64(__m128 _mm_set_ps1(float)) +__MACHINEX86_X64(__m128d _mm_set_sd(double)) +__MACHINEX86_X64(__m128 _mm_set_ss(float)) +__MACHINEX86_X64(void _mm_setcsr(unsigned int)) +__MACHINEX86_X64(__m128i _mm_setl_epi64(__m128i)) +__MACHINEX86_X64(__m128i _mm_setr_epi16(short, short, short, short, short, short, short, short)) +__MACHINEX86_X64(__m128i _mm_setr_epi32(int, int, int, int)) +__MACHINEX86(__m128i _mm_setr_epi64(__m64, __m64)) +__MACHINEX86_X64(__m128i _mm_setr_epi8(char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char)) +__MACHINEX86_X64(__m128d _mm_setr_pd(double, double)) +__MACHINEX86(__m64 _mm_setr_pi16(short, short, short, short)) +__MACHINEX86(__m64 _mm_setr_pi32(int, int)) +__MACHINEX86(__m64 _mm_setr_pi8(char, char, char, char, char, char, char, char)) +__MACHINEX86_X64(__m128 _mm_setr_ps(float, float, float, float)) +__MACHINEX86_X64(__m128d _mm_setzero_pd(void)) +__MACHINEX86_X64(__m128 _mm_setzero_ps(void)) +__MACHINEX86_X64(__m128i _mm_setzero_si128(void)) +__MACHINEX86(__m64 _mm_setzero_si64(void)) +__MACHINEX86_X64(void _mm_sfence(void)) +__MACHINEX86_X64(__m128i _mm_shuffle_epi32(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_shuffle_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_shuffle_pd(__m128d, __m128d, int)) +__MACHINEX86_X64(__m64 _mm_shuffle_pi8(__m64, __m64)) +__MACHINEX86_X64(__m128 _mm_shuffle_ps(__m128, __m128, unsigned int)) +__MACHINEX86_X64(__m128i _mm_shufflehi_epi16(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_shufflelo_epi16(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_sign_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sign_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sign_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m64 _mm_sign_pi16(__m64, __m64)) +__MACHINEX86_X64(__m64 _mm_sign_pi32(__m64, __m64)) +__MACHINEX86_X64(__m64 _mm_sign_pi8(__m64, __m64)) +__MACHINEX86_X64(__m128i _mm_sll_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sll_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sll_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_slli_epi16(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_slli_epi32(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_slli_epi64(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_slli_si128(__m128i, int)) +__MACHINEX86_X64(__m128d _mm_sqrt_pd(__m128d)) +__MACHINEX86_X64(__m128 _mm_sqrt_ps(__m128)) +__MACHINEX86_X64(__m128d _mm_sqrt_sd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_sqrt_ss(__m128)) +__MACHINEX86_X64(__m128i _mm_sra_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sra_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_srai_epi16(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_srai_epi32(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_srl_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_srl_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_srl_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_srli_epi16(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_srli_epi32(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_srli_epi64(__m128i, int)) +__MACHINEX86_X64(__m128i _mm_srli_si128(__m128i, int)) +__MACHINEX86_X64(void _mm_store1_pd(double *, __m128d)) +__MACHINEX86_X64(void _mm_store_pd(double *, __m128d)) +__MACHINEX86_X64(void _mm_store_ps(float *, __m128)) +__MACHINEX86_X64(void _mm_store_ps1(float *, __m128)) +__MACHINEX86_X64(void _mm_store_sd(double *, __m128d)) +__MACHINEX86_X64(void _mm_store_si128(__m128i *, __m128i)) +__MACHINEX86_X64(void _mm_store_ss(float *, __m128)) +__MACHINEX86_X64(void _mm_storeh_pd(double *, __m128d)) +__MACHINEX86_X64(void _mm_storeh_pi(__m64 *, __m128)) +__MACHINEX86_X64(void _mm_storel_epi64(__m128i *, __m128i)) +__MACHINEX86_X64(void _mm_storel_pd(double *, __m128d)) +__MACHINEX86_X64(void _mm_storel_pi(__m64 *, __m128)) +__MACHINEX86_X64(void _mm_storer_pd(double *, __m128d)) +__MACHINEX86_X64(void _mm_storer_ps(float *, __m128)) +__MACHINEX86_X64(void _mm_storeu_pd(double *, __m128d)) +__MACHINEX86_X64(void _mm_storeu_ps(float *, __m128)) +__MACHINEX86_X64(void _mm_storeu_si128(__m128i *, __m128i)) +__MACHINEX86_X64(__m128i _mm_stream_load_si128(__m128i *)) +__MACHINEX86_X64(void _mm_stream_pd(double *, __m128d)) +__MACHINEX86(void _mm_stream_pi(__m64 *, __m64)) +__MACHINEX86_X64(void _mm_stream_ps(float *, __m128)) +__MACHINEX86_X64(void _mm_stream_sd(double *, __m128d)) +__MACHINEX86_X64(void _mm_stream_si128(__m128i *, __m128i)) +__MACHINEX86_X64(void _mm_stream_si32(int *, int)) +__MACHINEX64(void _mm_stream_si64x(__int64 *, __int64)) +__MACHINEX86_X64(void _mm_stream_ss(float *, __m128)) +__MACHINEX86_X64(__m128i _mm_sub_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sub_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sub_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_sub_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_sub_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_sub_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_sub_sd(__m128d, __m128d)) +__MACHINEX86(__m64 _mm_sub_si64(__m64, __m64)) +__MACHINEX86_X64(__m128 _mm_sub_ss(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_subs_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_subs_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_subs_epu16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_subs_epu8(__m128i, __m128i)) +__MACHINEX86_X64(int _mm_testc_si128(__m128i, __m128i)) +__MACHINEX86_X64(int _mm_testnzc_si128(__m128i, __m128i)) +__MACHINEX86_X64(int _mm_testz_si128(__m128i, __m128i)) +__MACHINEX86_X64(int _mm_ucomieq_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_ucomieq_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_ucomige_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_ucomige_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_ucomigt_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_ucomigt_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_ucomile_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_ucomile_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_ucomilt_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_ucomilt_ss(__m128, __m128)) +__MACHINEX86_X64(int _mm_ucomineq_sd(__m128d, __m128d)) +__MACHINEX86_X64(int _mm_ucomineq_ss(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_unpackhi_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_unpackhi_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_unpackhi_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_unpackhi_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_unpackhi_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_unpackhi_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_unpacklo_epi16(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_unpacklo_epi32(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_unpacklo_epi64(__m128i, __m128i)) +__MACHINEX86_X64(__m128i _mm_unpacklo_epi8(__m128i, __m128i)) +__MACHINEX86_X64(__m128d _mm_unpacklo_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_unpacklo_ps(__m128, __m128)) +__MACHINEX86_X64(__m128d _mm_xor_pd(__m128d, __m128d)) +__MACHINEX86_X64(__m128 _mm_xor_ps(__m128, __m128)) +__MACHINEX86_X64(__m128i _mm_xor_si128(__m128i, __m128i)) +__MACHINEX64(__int64 _mul128(__int64 _Multiplier, __int64 _Multiplicand, __int64 * _HighProduct)) +__MACHINE(unsigned int __cdecl _rotl(_In_ unsigned int _Value, _In_ int _Shift)) +__MACHINE(unsigned short __cdecl _rotl16(unsigned short _Value, unsigned char _Shift)) +__MACHINE(unsigned __int64 __cdecl _rotl64(_In_ unsigned __int64 _Value, _In_ int _Shift)) +__MACHINE(unsigned char __cdecl _rotl8(unsigned char _Value, unsigned char _Shift)) +__MACHINE(unsigned int __cdecl _rotr(_In_ unsigned int _Value, _In_ int _Shift)) +__MACHINE(unsigned short __cdecl _rotr16(unsigned short _Value, unsigned char _Shift)) +__MACHINE(unsigned __int64 __cdecl _rotr64(_In_ unsigned __int64 _Value, _In_ int _Shift)) +__MACHINE(unsigned char __cdecl _rotr8(unsigned char _Value, unsigned char _Shift)) +__MACHINE(int __cdecl _setjmp(jmp_buf)) +__MACHINEARM_X64(int __cdecl _setjmpex(jmp_buf)) +__MACHINEX64(unsigned __int64 _umul128(unsigned __int64 _Multiplier, unsigned __int64 _Multiplicand, unsigned __int64 * _HighProduct)) +__MACHINEX86_X64(void _rsm(void)) +__MACHINEX86_X64(void _lgdt(void *)) +__MACHINEX86_X64(void _sgdt(void *)) +__MACHINEX86_X64(void _clac(void)) +__MACHINEX86_X64(void _stac(void)) +__MACHINEX86_X64(unsigned char __cdecl _addcarry_u8(unsigned char, unsigned char, unsigned char, unsigned char *)) +__MACHINEX86_X64(unsigned char __cdecl _subborrow_u8(unsigned char, unsigned char, unsigned char, unsigned char *)) +__MACHINEX86_X64(unsigned char __cdecl _addcarry_u16(unsigned char, unsigned short, unsigned short, unsigned short *)) +__MACHINEX86_X64(unsigned char __cdecl _subborrow_u16(unsigned char, unsigned short, unsigned short, unsigned short *)) +__MACHINEX86_X64(unsigned char __cdecl _addcarry_u32(unsigned char, unsigned int, unsigned int, unsigned int *)) +__MACHINEX86_X64(unsigned char __cdecl _subborrow_u32(unsigned char, unsigned int, unsigned int, unsigned int *)) +__MACHINEX64(unsigned char __cdecl _addcarry_u64(unsigned char, unsigned __int64, unsigned __int64, unsigned __int64 *)) +__MACHINEX64(unsigned char __cdecl _subborrow_u64(unsigned char, unsigned __int64, unsigned __int64, unsigned __int64 *)) + +#if defined (__cplusplus) +} +#endif /* defined (__cplusplus) */ +#endif /* __midl */ +#endif /* RC_INVOKED */ diff --git a/test_data/lots_of_files/inttypes.h b/test_data/lots_of_files/inttypes.h new file mode 100644 index 0000000..33b3a38 --- /dev/null +++ b/test_data/lots_of_files/inttypes.h @@ -0,0 +1,247 @@ +/*** +*complex.h - definitions and declarations for complex math library +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains constant definitions and external subroutine +* declarations for the complex math subroutine library. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INTTYPES +#define _INTTYPES +#include <stdint.h> + +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct +{ /* result of long long divide */ + intmax_t quot; + intmax_t rem; +} _Lldiv_t; + +#pragma pack(pop) + +typedef _Lldiv_t imaxdiv_t; + +intmax_t _CRTIMP __cdecl imaxabs(_In_ intmax_t); +imaxdiv_t _CRTIMP __cdecl imaxdiv(_In_ intmax_t, _In_ intmax_t); + +intmax_t _CRTIMP __cdecl strtoimax(_In_z_ const char *, _Out_opt_ _Deref_post_z_ char **, int); +intmax_t _CRTIMP __cdecl _strtoimax_l(_In_z_ const char *, _Out_opt_ _Deref_post_z_ char **, int, _In_opt_ _locale_t); +uintmax_t _CRTIMP __cdecl strtoumax(_In_z_ const char *, _Out_opt_ _Deref_post_z_ char **, int); +uintmax_t _CRTIMP __cdecl _strtoumax_l(_In_z_ const char *, _Out_opt_ _Deref_post_z_ char **, int, _In_opt_ _locale_t); +intmax_t _CRTIMP __cdecl wcstoimax(_In_z_ const wchar_t *, _Out_opt_ _Deref_post_z_ wchar_t **, int); +intmax_t _CRTIMP __cdecl _wcstoimax_l(_In_z_ const wchar_t *, _Out_opt_ _Deref_post_z_ wchar_t **, int, _In_opt_ _locale_t); +uintmax_t _CRTIMP __cdecl wcstoumax(_In_z_ const wchar_t *, _Out_opt_ _Deref_post_z_ wchar_t **, int); +uintmax_t _CRTIMP __cdecl _wcstoumax_l(_In_z_ const wchar_t *, _Out_opt_ _Deref_post_z_ wchar_t **, int, _In_opt_ _locale_t); + +#define _PFX_8 "hh" +#define _PFX_16 "h" +#define _PFX_32 "l" +#define _PFX_64 "ll" + +#ifdef _WIN64 +#define _PFX_PTR "ll" +#else +#define _PFX_PTR "l" +#endif + +#ifdef _FAST16_IS_32 /* compiler test */ +#define _PFX_F16 _PFX_32 +#else /* _FAST16_IS_32 */ +#define _PFX_F16 _PFX_16 +#endif /* _FAST16_IS_32 */ + +/* PRINT FORMAT MACROS */ +#define PRId8 _PFX_8 "d" +#define PRId16 _PFX_16 "d" +#define PRId32 _PFX_32 "d" +#define PRIdLEAST8 _PFX_8 "d" +#define PRIdLEAST16 _PFX_16 "d" +#define PRIdLEAST32 _PFX_32 "d" +#define PRIdFAST8 _PFX_8 "d" +#define PRIdFAST16 _PFX_F16 "d" +#define PRIdFAST32 _PFX_32 "d" + +#define PRIi8 _PFX_8 "i" +#define PRIi16 _PFX_16 "i" +#define PRIi32 _PFX_32 "i" +#define PRIiLEAST8 _PFX_8 "i" +#define PRIiLEAST16 _PFX_16 "i" +#define PRIiLEAST32 _PFX_32 "i" +#define PRIiFAST8 _PFX_8 "i" +#define PRIiFAST16 _PFX_F16 "i" +#define PRIiFAST32 _PFX_32 "i" + +#define PRIo8 _PFX_8 "o" +#define PRIo16 _PFX_16 "o" +#define PRIo32 _PFX_32 "o" +#define PRIoLEAST8 _PFX_8 "o" +#define PRIoLEAST16 _PFX_16 "o" +#define PRIoLEAST32 _PFX_32 "o" +#define PRIoFAST8 _PFX_8 "o" +#define PRIoFAST16 _PFX_F16 "o" +#define PRIoFAST32 _PFX_32 "o" + +#define PRIu8 _PFX_8 "u" +#define PRIu16 _PFX_16 "u" +#define PRIu32 _PFX_32 "u" +#define PRIuLEAST8 _PFX_8 "u" +#define PRIuLEAST16 _PFX_16 "u" +#define PRIuLEAST32 _PFX_32 "u" +#define PRIuFAST8 _PFX_8 "u" +#define PRIuFAST16 _PFX_F16 "u" +#define PRIuFAST32 _PFX_32 "u" + +#define PRIx8 _PFX_8 "x" +#define PRIx16 _PFX_16 "x" +#define PRIx32 _PFX_32 "x" +#define PRIxLEAST8 _PFX_8 "x" +#define PRIxLEAST16 _PFX_16 "x" +#define PRIxLEAST32 _PFX_32 "x" +#define PRIxFAST8 _PFX_8 "x" +#define PRIxFAST16 _PFX_F16 "x" +#define PRIxFAST32 _PFX_32 "x" + +#define PRIX8 _PFX_8 "X" +#define PRIX16 _PFX_16 "X" +#define PRIX32 _PFX_32 "X" +#define PRIXLEAST8 _PFX_8 "X" +#define PRIXLEAST16 _PFX_16 "X" +#define PRIXLEAST32 _PFX_32 "X" +#define PRIXFAST8 _PFX_8 "X" +#define PRIXFAST16 _PFX_F16 "X" +#define PRIXFAST32 _PFX_32 "X" + +#define PRId64 _PFX_64 "d" +#define PRIdLEAST64 _PFX_64 "d" +#define PRIdFAST64 _PFX_64 "d" +#define PRIdMAX _PFX_64 "d" +#define PRIdPTR _PFX_PTR "d" + +#define PRIi64 _PFX_64 "i" +#define PRIiLEAST64 _PFX_64 "i" +#define PRIiFAST64 _PFX_64 "i" +#define PRIiMAX _PFX_64 "i" +#define PRIiPTR _PFX_PTR "i" + +#define PRIo64 _PFX_64 "o" +#define PRIoLEAST64 _PFX_64 "o" +#define PRIoFAST64 _PFX_64 "o" +#define PRIoMAX _PFX_64 "o" +#define PRIoPTR _PFX_PTR "o" + +#define PRIu64 _PFX_64 "u" +#define PRIuLEAST64 _PFX_64 "u" +#define PRIuFAST64 _PFX_64 "u" +#define PRIuMAX _PFX_64 "u" +#define PRIuPTR _PFX_PTR "u" + +#define PRIx64 _PFX_64 "x" +#define PRIxLEAST64 _PFX_64 "x" +#define PRIxFAST64 _PFX_64 "x" +#define PRIxMAX _PFX_64 "x" +#define PRIxPTR _PFX_PTR "x" + +#define PRIX64 _PFX_64 "X" +#define PRIXLEAST64 _PFX_64 "X" +#define PRIXFAST64 _PFX_64 "X" +#define PRIXMAX _PFX_64 "X" +#define PRIXPTR _PFX_PTR "X" + +/* SCAN FORMAT MACROS */ +#define SCNd8 _PFX_8 "d" +#define SCNd16 _PFX_16 "d" +#define SCNd32 _PFX_32 "d" +#define SCNdLEAST8 _PFX_8 "d" +#define SCNdLEAST16 _PFX_16 "d" +#define SCNdLEAST32 _PFX_32 "d" +#define SCNdFAST8 _PFX_8 "d" +#define SCNdFAST16 _PFX_F16 "d" +#define SCNdFAST32 _PFX_32 "d" + +#define SCNi8 _PFX_8 "i" +#define SCNi16 _PFX_16 "i" +#define SCNi32 _PFX_32 "i" +#define SCNiLEAST8 _PFX_8 "i" +#define SCNiLEAST16 _PFX_16 "i" +#define SCNiLEAST32 _PFX_32 "i" +#define SCNiFAST8 _PFX_8 "i" +#define SCNiFAST16 _PFX_F16 "i" +#define SCNiFAST32 _PFX_32 "i" + +#define SCNo8 _PFX_8 "o" +#define SCNo16 _PFX_16 "o" +#define SCNo32 _PFX_32 "o" +#define SCNoLEAST8 _PFX_8 "o" +#define SCNoLEAST16 _PFX_16 "o" +#define SCNoLEAST32 _PFX_32 "o" +#define SCNoFAST8 _PFX_8 "o" +#define SCNoFAST16 _PFX_F16 "o" +#define SCNoFAST32 _PFX_32 "o" + +#define SCNu8 _PFX_8 "u" +#define SCNu16 _PFX_16 "u" +#define SCNu32 _PFX_32 "u" +#define SCNuLEAST8 _PFX_8 "u" +#define SCNuLEAST16 _PFX_16 "u" +#define SCNuLEAST32 _PFX_32 "u" +#define SCNuFAST8 _PFX_8 "u" +#define SCNuFAST16 _PFX_F16 "u" +#define SCNuFAST32 _PFX_32 "u" + +#define SCNx8 _PFX_8 "x" +#define SCNx16 _PFX_16 "x" +#define SCNx32 _PFX_32 "x" +#define SCNxLEAST8 _PFX_8 "x" +#define SCNxLEAST16 _PFX_16 "x" +#define SCNxLEAST32 _PFX_32 "x" +#define SCNxFAST8 _PFX_8 "x" +#define SCNxFAST16 _PFX_F16 "x" +#define SCNxFAST32 _PFX_32 "x" + +#define SCNd64 _PFX_64 "d" +#define SCNdLEAST64 _PFX_64 "d" +#define SCNdFAST64 _PFX_64 "d" +#define SCNdMAX _PFX_64 "d" +#define SCNdPTR _PFX_PTR "d" + +#define SCNi64 _PFX_64 "i" +#define SCNiLEAST64 _PFX_64 "i" +#define SCNiFAST64 _PFX_64 "i" +#define SCNiMAX _PFX_64 "i" +#define SCNiPTR _PFX_PTR "i" + +#define SCNo64 _PFX_64 "o" +#define SCNoLEAST64 _PFX_64 "o" +#define SCNoFAST64 _PFX_64 "o" +#define SCNoMAX _PFX_64 "o" +#define SCNoPTR _PFX_PTR "o" + +#define SCNu64 _PFX_64 "u" +#define SCNuLEAST64 _PFX_64 "u" +#define SCNuFAST64 _PFX_64 "u" +#define SCNuMAX _PFX_64 "u" +#define SCNuPTR _PFX_PTR "u" + +#define SCNx64 _PFX_64 "x" +#define SCNxLEAST64 _PFX_64 "x" +#define SCNxFAST64 _PFX_64 "x" +#define SCNxMAX _PFX_64 "x" +#define SCNxPTR _PFX_PTR "x" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INTTYPES */ diff --git a/test_data/lots_of_files/invkprxy.h b/test_data/lots_of_files/invkprxy.h new file mode 100644 index 0000000..9a853e1 --- /dev/null +++ b/test_data/lots_of_files/invkprxy.h @@ -0,0 +1,40 @@ +/*** +* invkprxy.h - Native C++ compiler COM support - IDispatch::Invoke helpers +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#if !defined(_INC_INVKPRXY) +#define _INC_INVKPRXY 1 + +HRESULT __stdcall _com_handle_excepinfo(EXCEPINFO& excepInfo, IErrorInfo** pperrinfo); +HRESULT __cdecl _com_invoke_helper(IDispatch* pDispatch, + DISPID dwDispID, + WORD wFlags, + VARTYPE vtRet, + void* pvRet, + _In_opt_z_ const wchar_t* pwParamInfo, + va_list argList, + IErrorInfo** pperrinfo); +HRESULT __cdecl _com_dispatch_raw_method(IDispatch* pDispatch, + DISPID dwDispID, + WORD wFlags, + VARTYPE vtRet, + void* pvRet, + const wchar_t* pwParamInfo, + ...) throw(); +HRESULT __stdcall _com_dispatch_raw_propget(IDispatch* pDispatch, + DISPID dwDispID, + VARTYPE vtProp, + void* pvProp) throw(); +HRESULT __cdecl _com_dispatch_raw_propput(IDispatch* pDispatch, + DISPID dwDispID, + VARTYPE vtProp, + ...) throw(); + +#endif // _INC_INVKPRXY diff --git a/test_data/lots_of_files/io.h b/test_data/lots_of_files/io.h new file mode 100644 index 0000000..5686c64 --- /dev/null +++ b/test_data/lots_of_files/io.h @@ -0,0 +1,354 @@ +/*** +*io.h - declarations for low-level file handling and I/O functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains the function declarations for the low-level +* file handling and I/O functions. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_IO +#define _INC_IO + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _FSIZE_T_DEFINED +typedef unsigned long _fsize_t; /* Could be 64 bits for Win32 */ +#define _FSIZE_T_DEFINED +#endif /* _FSIZE_T_DEFINED */ + +#ifndef _FINDDATA_T_DEFINED + +struct _finddata32_t { + unsigned attrib; + __time32_t time_create; /* -1 for FAT file systems */ + __time32_t time_access; /* -1 for FAT file systems */ + __time32_t time_write; + _fsize_t size; + char name[260]; +}; + +struct _finddata32i64_t { + unsigned attrib; + __time32_t time_create; /* -1 for FAT file systems */ + __time32_t time_access; /* -1 for FAT file systems */ + __time32_t time_write; + __int64 size; + char name[260]; +}; + +struct _finddata64i32_t { + unsigned attrib; + __time64_t time_create; /* -1 for FAT file systems */ + __time64_t time_access; /* -1 for FAT file systems */ + __time64_t time_write; + _fsize_t size; + char name[260]; +}; + +struct __finddata64_t { + unsigned attrib; + __time64_t time_create; /* -1 for FAT file systems */ + __time64_t time_access; /* -1 for FAT file systems */ + __time64_t time_write; + __int64 size; + char name[260]; +}; + +#ifdef _USE_32BIT_TIME_T +#define _finddata_t _finddata32_t +#define _finddatai64_t _finddata32i64_t + +#define _findfirst _findfirst32 +#define _findnext _findnext32 +#define _findfirsti64 _findfirst32i64 +#define _findnexti64 _findnext32i64 + +#else /* _USE_32BIT_TIME_T */ +#define _finddata_t _finddata64i32_t +#define _finddatai64_t __finddata64_t + +#define _findfirst _findfirst64i32 +#define _findnext _findnext64i32 +#define _findfirsti64 _findfirst64 +#define _findnexti64 _findnext64 + +#endif /* _USE_32BIT_TIME_T */ + + +#define _FINDDATA_T_DEFINED +#endif /* _FINDDATA_T_DEFINED */ + +#ifndef _WFINDDATA_T_DEFINED + +struct _wfinddata32_t { + unsigned attrib; + __time32_t time_create; /* -1 for FAT file systems */ + __time32_t time_access; /* -1 for FAT file systems */ + __time32_t time_write; + _fsize_t size; + wchar_t name[260]; +}; + +struct _wfinddata32i64_t { + unsigned attrib; + __time32_t time_create; /* -1 for FAT file systems */ + __time32_t time_access; /* -1 for FAT file systems */ + __time32_t time_write; + __int64 size; + wchar_t name[260]; +}; + +struct _wfinddata64i32_t { + unsigned attrib; + __time64_t time_create; /* -1 for FAT file systems */ + __time64_t time_access; /* -1 for FAT file systems */ + __time64_t time_write; + _fsize_t size; + wchar_t name[260]; +}; + +struct _wfinddata64_t { + unsigned attrib; + __time64_t time_create; /* -1 for FAT file systems */ + __time64_t time_access; /* -1 for FAT file systems */ + __time64_t time_write; + __int64 size; + wchar_t name[260]; +}; + +#ifdef _USE_32BIT_TIME_T +#define _wfinddata_t _wfinddata32_t +#define _wfinddatai64_t _wfinddata32i64_t + +#define _wfindfirst _wfindfirst32 +#define _wfindnext _wfindnext32 +#define _wfindfirsti64 _wfindfirst32i64 +#define _wfindnexti64 _wfindnext32i64 + +#else /* _USE_32BIT_TIME_T */ +#define _wfinddata_t _wfinddata64i32_t +#define _wfinddatai64_t _wfinddata64_t + +#define _wfindfirst _wfindfirst64i32 +#define _wfindnext _wfindnext64i32 +#define _wfindfirsti64 _wfindfirst64 +#define _wfindnexti64 _wfindnext64 + +#endif /* _USE_32BIT_TIME_T */ + +#define _WFINDDATA_T_DEFINED +#endif /* _WFINDDATA_T_DEFINED */ + +/* File attribute constants for _findfirst() */ + +#define _A_NORMAL 0x00 /* Normal file - No read/write restrictions */ +#define _A_RDONLY 0x01 /* Read only file */ +#define _A_HIDDEN 0x02 /* Hidden file */ +#define _A_SYSTEM 0x04 /* System file */ +#define _A_SUBDIR 0x10 /* Subdirectory */ +#define _A_ARCH 0x20 /* Archive file */ + +/* function prototypes */ + +#ifndef _SIZE_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 size_t; +#else /* _WIN64 */ +typedef _W64 unsigned int size_t; +#endif /* _WIN64 */ +#define _SIZE_T_DEFINED +#endif /* _SIZE_T_DEFINED */ + +_Check_return_ _CRTIMP int __cdecl _access(_In_z_ const char * _Filename, _In_ int _AccessMode); +_Check_return_wat_ _CRTIMP errno_t __cdecl _access_s(_In_z_ const char * _Filename, _In_ int _AccessMode); +_Check_return_ _CRTIMP int __cdecl _chmod(_In_z_ const char * _Filename, _In_ int _Mode); +/* note that the newly added _chsize_s takes a 64 bit value */ +_Check_return_ _CRTIMP int __cdecl _chsize(_In_ int _FileHandle, _In_ long _Size); +_Check_return_wat_ _CRTIMP errno_t __cdecl _chsize_s(_In_ int _FileHandle,_In_ __int64 _Size); +_Check_return_opt_ _CRTIMP int __cdecl _close(_In_ int _FileHandle); +_Check_return_opt_ _CRTIMP int __cdecl _commit(_In_ int _FileHandle); +_Check_return_ _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl _creat(_In_z_ const char * _Filename, _In_ int _PermissionMode); +_Check_return_ _CRTIMP int __cdecl _dup(_In_ int _FileHandle); +_Check_return_ _CRTIMP int __cdecl _dup2(_In_ int _FileHandleSrc, _In_ int _FileHandleDst); +_Check_return_ _CRTIMP int __cdecl _eof(_In_ int _FileHandle); +_Check_return_ _CRTIMP long __cdecl _filelength(_In_ int _FileHandle); +_Check_return_ _CRTIMP intptr_t __cdecl _findfirst32(_In_z_ const char * _Filename, _Out_ struct _finddata32_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _findnext32(_In_ intptr_t _FindHandle, _Out_ struct _finddata32_t * _FindData); +_Check_return_opt_ _CRTIMP int __cdecl _findclose(_In_ intptr_t _FindHandle); +_Check_return_ _CRTIMP int __cdecl _isatty(_In_ int _FileHandle); +_CRTIMP int __cdecl _locking(_In_ int _FileHandle, _In_ int _LockMode, _In_ long _NumOfBytes); +_Check_return_opt_ _CRTIMP long __cdecl _lseek(_In_ int _FileHandle, _In_ long _Offset, _In_ int _Origin); +_Check_return_wat_ _CRTIMP errno_t __cdecl _mktemp_s(_Inout_updates_z_(_Size) char * _TemplateName, _In_ size_t _Size); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _mktemp_s, _Prepost_z_ char, _TemplateName) +_Check_return_ __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(char *, __RETURN_POLICY_DST, _CRTIMP, _mktemp, _Inout_z_, char, _TemplateName) +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP int __cdecl _pipe(_Inout_updates_(2) int * _PtHandles, _In_ unsigned int _PipeSize, _In_ int _TextMode); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ +_Check_return_ _CRTIMP int __cdecl _read(_In_ int _FileHandle, _Out_writes_bytes_(_MaxCharCount) void * _DstBuf, _In_ unsigned int _MaxCharCount); + +#ifndef _CRT_DIRECTORY_DEFINED +#define _CRT_DIRECTORY_DEFINED +_CRTIMP int __cdecl remove(_In_z_ const char * _Filename); +_Check_return_ _CRTIMP int __cdecl rename(_In_z_ const char * _OldFilename, _In_z_ const char * _NewFilename); +_CRTIMP int __cdecl _unlink(_In_z_ const char * _Filename); +#if !__STDC__ +_CRT_NONSTDC_DEPRECATE(_unlink) _CRTIMP int __cdecl unlink(_In_z_ const char * _Filename); +#endif /* !__STDC__ */ +#endif /* _CRT_DIRECTORY_DEFINED */ + +_Check_return_ _CRTIMP int __cdecl _setmode(_In_ int _FileHandle, _In_ int _Mode); +_Check_return_ _CRTIMP long __cdecl _tell(_In_ int _FileHandle); +_CRT_INSECURE_DEPRECATE(_umask_s) _CRTIMP int __cdecl _umask(_In_ int _Mode); +_Check_return_wat_ _CRTIMP errno_t __cdecl _umask_s(_In_ int _NewMode, _Out_ int * _OldMode); +_CRTIMP int __cdecl _write(_In_ int _FileHandle, _In_reads_bytes_(_MaxCharCount) const void * _Buf, _In_ unsigned int _MaxCharCount); + +_Check_return_ _CRTIMP __int64 __cdecl _filelengthi64(_In_ int _FileHandle); +_Check_return_ _CRTIMP intptr_t __cdecl _findfirst32i64(_In_z_ const char * _Filename, _Out_ struct _finddata32i64_t * _FindData); +_Check_return_ _CRTIMP intptr_t __cdecl _findfirst64i32(_In_z_ const char * _Filename, _Out_ struct _finddata64i32_t * _FindData); +_Check_return_ _CRTIMP intptr_t __cdecl _findfirst64(_In_z_ const char * _Filename, _Out_ struct __finddata64_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _findnext32i64(_In_ intptr_t _FindHandle, _Out_ struct _finddata32i64_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _findnext64i32(_In_ intptr_t _FindHandle, _Out_ struct _finddata64i32_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _findnext64(_In_ intptr_t _FindHandle, _Out_ struct __finddata64_t * _FindData); +_Check_return_opt_ _CRTIMP __int64 __cdecl _lseeki64(_In_ int _FileHandle, _In_ __int64 _Offset, _In_ int _Origin); +_Check_return_ _CRTIMP __int64 __cdecl _telli64(_In_ int _FileHandle); + +_Check_return_wat_ _CRTIMP errno_t __cdecl _sopen_s(_Out_ int * _FileHandle, _In_z_ const char * _Filename,_In_ int _OpenFlag, _In_ int _ShareFlag, _In_ int _PermissionMode); +_Check_return_ errno_t __cdecl _sopen_s_nolock(_Out_ int * _FileHandle, _In_z_ const char * _Filename,_In_ int _OpenFlag, _In_ int _ShareFlag, _In_ int _PermissionMode); + +#if !defined (__cplusplus) +_Check_return_ _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl _open(_In_z_ const char * _Filename, _In_ int _OpenFlag, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl _sopen(_In_z_ const char * _Filename, _In_ int _OpenFlag, int _ShareFlag, ...); +#else /* !defined (__cplusplus) */ + +/* these function do not validate pmode; use _sopen_s */ +extern "C++" _Check_return_ _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl _open(_In_z_ const char * _Filename, _In_ int _Openflag, _In_ int _PermissionMode = 0); +extern "C++" _Check_return_ _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl _sopen(_In_z_ const char * _Filename, _In_ int _Openflag, _In_ int _ShareFlag, _In_ int _PermissionMode = 0); + +#endif /* !defined (__cplusplus) */ + +#ifndef _WIO_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +_Check_return_ _CRTIMP int __cdecl _waccess(_In_z_ const wchar_t * _Filename, _In_ int _AccessMode); +_Check_return_wat_ _CRTIMP errno_t __cdecl _waccess_s(_In_z_ const wchar_t * _Filename, _In_ int _AccessMode); +_Check_return_ _CRTIMP int __cdecl _wchmod(_In_z_ const wchar_t * _Filename, _In_ int _Mode); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wcreat(_In_z_ const wchar_t * _Filename, _In_ int _PermissionMode); +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst32(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata32_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext32(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata32_t * _FindData); +_CRTIMP int __cdecl _wunlink(_In_z_ const wchar_t * _Filename); +_Check_return_ _CRTIMP int __cdecl _wrename(_In_z_ const wchar_t * _OldFilename, _In_z_ const wchar_t * _NewFilename); +_CRTIMP errno_t __cdecl _wmktemp_s(_Inout_updates_z_(_SizeInWords) wchar_t * _TemplateName, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wmktemp_s, _Prepost_z_ wchar_t, _TemplateName) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wmktemp, _Inout_z_, wchar_t, _TemplateName) + +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst32i64(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata32i64_t * _FindData); +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst64i32(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata64i32_t * _FindData); +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst64(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata64_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext32i64(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata32i64_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext64i32(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata64i32_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext64(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata64_t * _FindData); + +_Check_return_wat_ _CRTIMP errno_t __cdecl _wsopen_s(_Out_ int * _FileHandle, _In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, _In_ int _ShareFlag, _In_ int _PermissionFlag); + +#if !defined (__cplusplus) || !defined (_M_IX86) + +_Check_return_ _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wsopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, int _ShareFlag, ...); + +#else /* !defined (__cplusplus) || !defined (_M_IX86) */ + +/* these function do not validate pmode; use _sopen_s */ +extern "C++" _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, _In_ int _PermissionMode = 0); +extern "C++" _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wsopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, _In_ int _ShareFlag, int _PermissionMode = 0); + +#endif /* !defined (__cplusplus) || !defined (_M_IX86) */ + +#define _WIO_DEFINED +#endif /* _WIO_DEFINED */ + +int __cdecl __lock_fhandle(_In_ int _Filehandle); +void __cdecl _unlock_fhandle(_In_ int _Filehandle); + +#ifdef _CRTBLD +#ifndef _NOT_CRTL_BUILD_ +_Check_return_ int __cdecl _chsize_nolock(_In_ int _FileHandle,_In_ __int64 _Size); +_Check_return_opt_ int __cdecl _close_nolock(_In_ int _FileHandle); +_Check_return_opt_ long __cdecl _lseek_nolock(_In_ int _FileHandle, _In_ long _Offset, _In_ int _Origin); +_Check_return_ int __cdecl _setmode_nolock(_In_ int _FileHandle, _In_ int _Mode); +_Check_return_ int __cdecl _read_nolock(_In_ int _FileHandle, _Out_writes_bytes_(_MaxCharCount) void * _DstBuf, _In_ unsigned int _MaxCharCount); +_Check_return_ int __cdecl _write_nolock(_In_ int _FileHandle, _In_reads_bytes_(_MaxCharCount) const void * _Buf, _In_ unsigned int _MaxCharCount); +_Check_return_opt_ __int64 __cdecl _lseeki64_nolock(_In_ int _FileHandle, _In_ __int64 _Offset, _In_ int _Origin); + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define _chsize(fh, size) _chsize_nolock(fh, size) +#define _close(fh) _close_nolock(fh) +#define _lseek(fh, offset, origin) _lseek_nolock(fh, offset, origin) +#define _setmode(fh, mode) _setmode_nolock(fh, mode) +#define _read(fh, buff, count) _read_nolock(fh, buff, count) +#define _write(fh, buff, count) _write_nolock(fh, buff, count) +#define _lseeki64(fh,offset,origin) _lseeki64_nolock(fh,offset,origin) +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ + +#endif /* _NOT_CRTL_BUILD_ */ +#endif /* _CRTBLD */ + +_CRTIMP intptr_t __cdecl _get_osfhandle(_In_ int _FileHandle); +_CRTIMP int __cdecl _open_osfhandle(_In_ intptr_t _OSFileHandle, _In_ int _Flags); + +#if !__STDC__ + +/* Non-ANSI names for compatibility */ + +#pragma warning(push) +#pragma warning(disable: 4141) /* Using deprecated twice */ +_Check_return_ _CRT_NONSTDC_DEPRECATE(_access) _CRTIMP int __cdecl access(_In_z_ const char * _Filename, _In_ int _AccessMode); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_chmod) _CRTIMP int __cdecl chmod(_In_z_ const char * _Filename, int _AccessMode); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_chsize) _CRTIMP int __cdecl chsize(_In_ int _FileHandle, _In_ long _Size); +_Check_return_opt_ _CRT_NONSTDC_DEPRECATE(_close) _CRTIMP int __cdecl close(_In_ int _FileHandle); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_creat) _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl creat(_In_z_ const char * _Filename, _In_ int _PermissionMode); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_dup) _CRTIMP int __cdecl dup(_In_ int _FileHandle); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_dup2) _CRTIMP int __cdecl dup2(_In_ int _FileHandleSrc, _In_ int _FileHandleDst); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_eof) _CRTIMP int __cdecl eof(_In_ int _FileHandle); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_filelength) _CRTIMP long __cdecl filelength(_In_ int _FileHandle); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_isatty) _CRTIMP int __cdecl isatty(_In_ int _FileHandle); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_locking) _CRTIMP int __cdecl locking(_In_ int _FileHandle, _In_ int _LockMode, _In_ long _NumOfBytes); +_Check_return_opt_ _CRT_NONSTDC_DEPRECATE(_lseek) _CRTIMP long __cdecl lseek(_In_ int _FileHandle, _In_ long _Offset, _In_ int _Origin); +_CRT_NONSTDC_DEPRECATE(_mktemp) _CRT_INSECURE_DEPRECATE(_mktemp_s) _CRTIMP char * __cdecl mktemp(_Inout_z_ char * _TemplateName); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_open) _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl open(_In_z_ const char * _Filename, _In_ int _OpenFlag, ...); +_CRT_NONSTDC_DEPRECATE(_read) _CRTIMP int __cdecl read(int _FileHandle, _Out_writes_bytes_(_MaxCharCount) void * _DstBuf, _In_ unsigned int _MaxCharCount); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_setmode) _CRTIMP int __cdecl setmode(_In_ int _FileHandle, _In_ int _Mode); +_CRT_NONSTDC_DEPRECATE(_sopen) _CRT_INSECURE_DEPRECATE(_sopen_s) _CRTIMP int __cdecl sopen(const char * _Filename, _In_ int _OpenFlag, _In_ int _ShareFlag, ...); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_tell) _CRTIMP long __cdecl tell(_In_ int _FileHandle); +_CRT_NONSTDC_DEPRECATE(_umask) _CRT_INSECURE_DEPRECATE(_umask_s) _CRTIMP int __cdecl umask(_In_ int _Mode); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_write) _CRTIMP int __cdecl write(_In_ int _Filehandle, _In_reads_bytes_(_MaxCharCount) const void * _Buf, _In_ unsigned int _MaxCharCount); +#pragma warning(pop) + +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#pragma pack(pop) + +#endif /* _INC_IO */ diff --git a/test_data/lots_of_files/iso646.h b/test_data/lots_of_files/iso646.h new file mode 100644 index 0000000..d86c595 --- /dev/null +++ b/test_data/lots_of_files/iso646.h @@ -0,0 +1,26 @@ +/* iso646.h standard header */ +#pragma once +#ifndef _ISO646 +#define _ISO646 +#ifndef RC_INVOKED + + #if !defined(__cplusplus) || defined(_MSC_EXTENSIONS) + #define and && + #define and_eq &= + #define bitand & + #define bitor | + #define compl ~ + #define not ! + #define not_eq != + #define or || + #define or_eq |= + #define xor ^ + #define xor_eq ^= + #endif /* !defined(__cplusplus) || defined(_MSC_EXTENSIONS) */ +#endif /* RC_INVOKED */ +#endif /* _ISO646 */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/ivec.h b/test_data/lots_of_files/ivec.h new file mode 100644 index 0000000..2dd5bae --- /dev/null +++ b/test_data/lots_of_files/ivec.h @@ -0,0 +1,838 @@ +/*** +*** Copyright (C) 1985-2011 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * Definition of a C++ class interface to MMX(TM) instruction intrinsics. + * + */ + +#ifndef _IVEC_H_INCLUDED +#define _IVEC_H_INCLUDED +#ifndef RC_INVOKED + +#if !defined __cplusplus + #error ERROR: This file is only supported in C++ compilations! +#endif /* !defined __cplusplus */ + +#if defined (_M_CEE_PURE) + #error ERROR: This file is not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <mmintrin.h> + +#define EXPLICIT explicit + +#ifndef _VEC_ASSERT + #include <crtdefs.h> + +#ifdef NDEBUG + #define _VEC_ASSERT(_Expression) ((void)0) +#else /* NDEBUG */ +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + _CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message, _In_z_ const wchar_t *_File, _In_ unsigned _Line); +#ifdef __cplusplus + } +#endif /* __cplusplus */ + + #define _VEC_ASSERT(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) ) +#endif /* NDEBUG */ +#endif /* _VEC_ASSERT */ + +/* + * Define _SILENCE_IVEC_C4799 to disable warning C4799 inside this header. + * Be careful that any code that uses these functions properly executes EMMS + * or _m_empty() after using any MMX instruction and before using the x87 NDP. + */ +#if defined (_SILENCE_IVEC_C4799) + #pragma warning(push) + #pragma warning(disable: 4799) +#endif /* defined (_SILENCE_IVEC_C4799) */ + +/* + * Define _ENABLE_VEC_DEBUG to enable std::ostream inserters for debug output + */ +#if defined (_ENABLE_VEC_DEBUG) + #include <iostream> +#endif /* defined (_ENABLE_VEC_DEBUG) */ + +class I8vec8; /* 8 elements, each element a signed or unsigned char data type */ +class Is8vec8; /* 8 elements, each element a signed char data type */ +class Iu8vec8; /* 8 elements, each element an unsigned char data type */ +class I16vec4; /* 4 elements, each element a signed or unsigned short */ +class Is16vec4; /* 4 elements, each element a signed short */ +class Iu16vec4; /* 4 elements, each element an unsigned short */ +class I32vec2; /* 2 elements, each element a signed or unsigned long */ +class Is32vec2; /* 2 elements, each element a signed long */ +class Iu32vec2; /* 2 elements, each element a unsigned long */ +class I64vec1; /* 1 element, a __m64 data type - Base I64vec1 class */ + +#define _MM_8UB(element,vector) (*((unsigned char*)&##vector + ##element)) +#define _MM_8B(element,vector) (*((signed char*)&##vector + ##element)) + +#define _MM_4UW(element,vector) (*((unsigned short*)&##vector + ##element)) +#define _MM_4W(element,vector) (*((short*)&##vector + ##element)) + +#define _MM_2UDW(element,vector) (*((unsigned int*)&##vector + ##element)) +#define _MM_2DW(element,vector) (*((int*)&##vector + ##element)) + +#define _MM_QW (*((__int64*)&vec)) + +/* M64 Class: + * 1 element, a __m64 data type + * Contructors & Logical Operations + */ +class M64 +{ +protected: + __m64 vec; + +public: + M64() { } + M64(__m64 mm) { vec = mm; } + M64(__int64 mm) { vec = _mm_set_pi32((int)(mm >> 32), (int)mm); } + M64(int i) { vec = _m_from_int(i); } + + operator __m64() const { return vec; } + + /* Logical Operations */ + M64& operator&=(const M64 &a) { return *this = (M64) _m_pand(vec,a); } + M64& operator|=(const M64 &a) { return *this = (M64) _m_por(vec,a); } + M64& operator^=(const M64 &a) { return *this = (M64) _m_pxor(vec,a); } + +}; + +const union {__int64 m1; __m64 m2;} __mmx_all_ones_cheat = + {0xffffffffffffffff}; + +#define _mmx_all_ones ((M64)__mmx_all_ones_cheat.m2) + +inline M64 operator&(const M64 &a, const M64 &b) { return _m_pand( a,b); } +inline M64 operator|(const M64 &a, const M64 &b) { return _m_por(a,b); } +inline M64 operator^(const M64 &a, const M64 &b) { return _m_pxor(a,b); } +inline M64 andnot(const M64 &a, const M64 &b) { return _m_pandn(a,b); } + +/* I64vec1 Class: + * 1 element, a __m64 data type + * Contains Operations which can operate on any __m64 data type + */ + +class I64vec1 : public M64 +{ +public: + I64vec1() { } + I64vec1(__m64 mm) : M64(mm) { } + EXPLICIT I64vec1(int i) : M64(i) { } + EXPLICIT I64vec1(__int64 mm) : M64(mm) { } + + I64vec1& operator= (const M64 &a) { return *this = (I64vec1) a; } + I64vec1& operator&=(const M64 &a) { return *this = (I64vec1) _m_pand(vec,a); } + I64vec1& operator|=(const M64 &a) { return *this = (I64vec1) _m_por(vec,a); } + I64vec1& operator^=(const M64 &a) { return *this = (I64vec1) _m_pxor(vec,a); } + + /* Shift Logical Operations */ + I64vec1 operator<<(const M64 &a) { return _m_psllq(vec, a); } + I64vec1 operator<<(int count) { return _m_psllqi(vec, count); } + I64vec1& operator<<=(const M64 &a) { return *this = (I64vec1) _m_psllq(vec, a); } + I64vec1& operator<<=(int count) { return *this = (I64vec1) _m_psllqi(vec, count); } + I64vec1 operator>>(const M64 &a) { return _m_psrlq(vec, a); } + I64vec1 operator>>(int count) { return _m_psrlqi(vec, count); } + I64vec1& operator>>=(const M64 &a) { return *this = (I64vec1) _m_psrlq(vec, a); } + I64vec1& operator>>=(int count) { return *this = (I64vec1) _m_psrlqi(vec, count); } +}; + +/* I32vec2 Class: + * 2 elements, each element either a signed or unsigned int + */ +class I32vec2 : public M64 +{ +public: + I32vec2() { } + I32vec2(__m64 mm) : M64(mm) { } + I32vec2(int i0, int i1) { vec = _mm_set_pi32(i0, i1); } + EXPLICIT I32vec2(int i) : M64 (i) { } + EXPLICIT I32vec2(__int64 i): M64(i) {} + + /* Assignment Operator */ + I32vec2& operator= (const M64 &a) { return *this = (I32vec2) a; } + + /* Logical Assignment Operators */ + I32vec2& operator&=(const M64 &a) { return *this = (I32vec2) _m_pand(vec,a); } + I32vec2& operator|=(const M64 &a) { return *this = (I32vec2) _m_por(vec,a); } + I32vec2& operator^=(const M64 &a) { return *this = (I32vec2) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + I32vec2& operator +=(const I32vec2 &a) { return *this = (I32vec2) _m_paddd(vec,a); } + I32vec2& operator -=(const I32vec2 &a) { return *this = (I32vec2) _m_psubd(vec,a); } + + /* Shift Logical Operators */ + I32vec2 operator<<(const I32vec2 &a) { return _m_pslld(vec,a); } + I32vec2 operator<<(int count) { return _m_pslldi(vec,count); } + I32vec2& operator<<=(const I32vec2 &a) { return *this = (I32vec2) _m_pslld(vec,a); } + I32vec2& operator<<=(int count) { return *this = (I32vec2) _m_pslldi(vec,count); } + +}; + +/* Compare For Equality */ +inline I32vec2 cmpeq(const I32vec2 &a, const I32vec2 &b) { return _m_pcmpeqd(a,b); } +inline I32vec2 cmpneq(const I32vec2 &a, const I32vec2 &b) { return _m_pandn(_m_pcmpeqd(a,b), _mmx_all_ones); } +/* Unpacks */ +inline I32vec2 unpack_low(const I32vec2 &a, const I32vec2 &b) {return _m_punpckldq(a,b); } +inline I32vec2 unpack_high(const I32vec2 &a, const I32vec2 &b) {return _m_punpckhdq(a,b); } + +/* Is32vec2 Class: + * 2 elements, each element a signed int + */ +class Is32vec2 : public I32vec2 +{ +public: + Is32vec2() { } + Is32vec2(__m64 mm) : I32vec2(mm) { } + Is32vec2(signed int i0, signed int i1) : I32vec2(i0, i1) {} + EXPLICIT Is32vec2(int i) : I32vec2 (i) {} + EXPLICIT Is32vec2(__int64 i): I32vec2(i) {} + + /* Assignment Operator */ + Is32vec2& operator= (const M64 &a) { return *this = (Is32vec2) a; } + + /* Logical Assignment Operators */ + Is32vec2& operator&=(const M64 &a) { return *this = (Is32vec2) _m_pand(vec,a); } + Is32vec2& operator|=(const M64 &a) { return *this = (Is32vec2) _m_por(vec,a); } + Is32vec2& operator^=(const M64 &a) { return *this = (Is32vec2) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Is32vec2& operator +=(const I32vec2 &a) { return *this = (Is32vec2) _m_paddd(vec,a); } + Is32vec2& operator -=(const I32vec2 &a) { return *this = (Is32vec2) _m_psubd(vec,a); } + + /* Shift Logical Operators */ + Is32vec2 operator<<(const M64 &a) { return _m_pslld(vec,a); } + Is32vec2 operator<<(int count) { return _m_pslldi(vec,count); } + Is32vec2& operator<<=(const M64 &a) { return *this = (Is32vec2) _m_pslld(vec,a); } + Is32vec2& operator<<=(int count) { return *this = (Is32vec2) _m_pslldi(vec,count); } + /* Shift Arithmetic Operations */ + Is32vec2 operator>>(const M64 &a) { return _m_psrad(vec, a); } + Is32vec2 operator>>(int count) { return _m_psradi(vec, count); } + Is32vec2& operator>>=(const M64 &a) { return *this = (Is32vec2) _m_psrad(vec, a); } + Is32vec2& operator>>=(int count) { return *this = (Is32vec2) _m_psradi(vec, count); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Is32vec2 &a) + { + os << " [1]:" << _MM_2DW(1,a) + << " [0]:" << _MM_2DW(0,a); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const int& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 2); /* Only 2 elements to access */ + return _MM_2DW(i,vec); + } + + /* Element Access and Assignment for Debug */ + int& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 2); /* Only 2 elements to access */ + return _MM_2DW(i,vec); + } +}; + +/* Compares */ +inline Is32vec2 cmpeq(const Is32vec2 &a, const Is32vec2 &b) { return _m_pcmpeqd(a,b); } +inline Is32vec2 cmpneq(const Is32vec2 &a, const Is32vec2 &b) { return _m_pandn(_m_pcmpeqd(a,b), _mmx_all_ones); } +inline Is32vec2 cmpgt(const Is32vec2 &a, const Is32vec2 &b) { return _m_pcmpgtd(a,b); } +inline Is32vec2 cmplt(const Is32vec2 &a, const Is32vec2 &b) { return _m_pcmpgtd(b,a); } +inline Is32vec2 cmple(const Is32vec2 &a, const Is32vec2 &b) { return _m_pandn(_m_pcmpgtd(a,b), _mmx_all_ones); } +inline Is32vec2 cmpge(const Is32vec2 &a, const Is32vec2 &b) { return _m_pandn(_m_pcmpgtd(b,a), _mmx_all_ones); } +/* Unpacks & Pack */ +inline Is32vec2 unpack_low(const Is32vec2 &a, const Is32vec2 &b) { return _m_punpckldq(a,b); } +inline Is32vec2 unpack_high(const Is32vec2 &a, const Is32vec2 &b) { return _m_punpckhdq(a,b); } + +/* Iu32vec2 Class: + * 2 elements, each element unsigned int + */ +class Iu32vec2 : public I32vec2 +{ +public: + Iu32vec2() { } + Iu32vec2(__m64 mm) : I32vec2(mm) { } + Iu32vec2(unsigned int i0, unsigned int i1) : I32vec2(i0, i1) {} + EXPLICIT Iu32vec2(int i) : I32vec2 (i) { } + EXPLICIT Iu32vec2(__int64 i) : I32vec2 (i) { } + + /* Assignment Operator */ + Iu32vec2& operator= (const M64 &a) { return *this = (Iu32vec2) a; } + + /* Logical Assignment Operators */ + Iu32vec2& operator&=(const M64 &a) { return *this = (Iu32vec2) _m_pand(vec,a); } + Iu32vec2& operator|=(const M64 &a) { return *this = (Iu32vec2) _m_por(vec,a); } + Iu32vec2& operator^=(const M64 &a) { return *this = (Iu32vec2) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Iu32vec2& operator +=(const I32vec2 &a) { return *this = (Iu32vec2) _m_paddd(vec,a); } + Iu32vec2& operator -=(const I32vec2 &a) { return *this = (Iu32vec2) _m_psubd(vec,a); } + + /* Shift Logical Operators */ + Iu32vec2 operator<<(const M64 &a) { return _m_pslld(vec,a); } + Iu32vec2 operator<<(int count) { return _m_pslldi(vec,count); } + Iu32vec2& operator<<=(const M64 &a) { return *this = (Iu32vec2) _m_pslld(vec,a); } + Iu32vec2& operator<<=(int count) { return *this = (Iu32vec2) _m_pslldi(vec,count); } + Iu32vec2 operator>>(const M64 &a) { return _m_psrld(vec,a); } + Iu32vec2 operator>>(int count) { return _m_psrldi(vec,count); } + Iu32vec2& operator>>=(const M64 &a) { return *this = (Iu32vec2) _m_psrld(vec,a); } + Iu32vec2& operator>>=(int count) { return *this = (Iu32vec2) _m_psrldi(vec,count); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Iu32vec2 &a) + { + os << "[1]:" << _MM_2UDW(1,a) + << " [0]:" << _MM_2UDW(0,a); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const unsigned int& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 2); /* Only 2 elements to access */ + return _MM_2UDW(i,vec); + } + + /* Element Access and Assignment for Debug */ + unsigned int& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 2); /* Only 2 elements to access */ + return _MM_2UDW(i,vec); + } +}; + +/* Compares For Equality / Inequality */ +inline Iu32vec2 cmpeq(const Iu32vec2 &a, const Iu32vec2 &b) { return _m_pcmpeqd(a,b); } +inline Iu32vec2 cmpneq(const Iu32vec2 &a, const Iu32vec2 &b) { return _m_pandn(_m_pcmpeqd(a,b), _mmx_all_ones); } +/* Unpacks */ +inline Iu32vec2 unpack_low(const Iu32vec2 &a, const Iu32vec2 &b) {return _m_punpckldq(a,b); } +inline Iu32vec2 unpack_high(const Iu32vec2 &a, const Iu32vec2 &b) {return _m_punpckhdq(a,b); } + +/* I16vec4 Class: + * 4 elements, each element either a signed or unsigned short + */ +class I16vec4 : public M64 +{ +public: + I16vec4() { } + I16vec4(__m64 mm) : M64(mm) { } + I16vec4(short i0, short i1, short i2, short i3) + { + vec = _mm_set_pi16(i0, i1, i2, i3); + } + EXPLICIT I16vec4(__int64 i) : M64 (i) { } + EXPLICIT I16vec4(int i) : M64 (i) { } + + /* Assignment Operator */ + I16vec4& operator= (const M64 &a) { return *this = (I16vec4) a; } + + /* Addition & Subtraction Assignment Operators */ + I16vec4& operator&=(const M64 &a) { return *this = (I16vec4) _m_pand(vec,a); } + I16vec4& operator|=(const M64 &a) { return *this = (I16vec4) _m_por(vec,a); } + I16vec4& operator^=(const M64 &a) { return *this = (I16vec4) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + I16vec4& operator +=(const I16vec4 &a) { return *this = (I16vec4)_m_paddw(vec,a); } + I16vec4& operator -=(const I16vec4 &a) { return *this = (I16vec4)_m_psubw(vec,a); } + I16vec4& operator *=(const I16vec4 &a) { return *this = (I16vec4)_m_pmullw(vec,a); } + + /* Shift Logical Operators */ + I16vec4 operator<<(const I16vec4 &a) { return _m_psllw(vec,a); } + I16vec4 operator<<(int count) { return _m_psllwi(vec,count); } + I16vec4& operator<<=(const I16vec4 &a) { return *this = (I16vec4)_m_psllw(vec,a); } + I16vec4& operator<<=(int count) { return *this = (I16vec4)_m_psllwi(vec,count); } +}; + +inline I16vec4 operator*(const I16vec4 &a, const I16vec4 &b) { return _m_pmullw(a,b); } +inline I16vec4 cmpeq(const I16vec4 &a, const I16vec4 &b) { return _m_pcmpeqw(a,b); } +inline I16vec4 cmpneq(const I16vec4 &a, const I16vec4 &b) { return _m_pandn(_m_pcmpeqw(a,b), _mmx_all_ones); } + +inline I16vec4 unpack_low(const I16vec4 &a, const I16vec4 &b) { return _m_punpcklwd(a,b); } +inline I16vec4 unpack_high(const I16vec4 &a, const I16vec4 &b) { return _m_punpckhwd(a,b); } + +/* Is16vec4 Class: + * 4 elements, each element signed short + */ +class Is16vec4 : public I16vec4 +{ +public: + Is16vec4() { } + Is16vec4(__m64 mm) : I16vec4(mm) { } + Is16vec4(short i0, short i1, short i2, short i3) : I16vec4(i0, i1, i2, i3) { } + EXPLICIT Is16vec4(__int64 i) : I16vec4 (i) { } + EXPLICIT Is16vec4(int i) : I16vec4 (i) { } + + /* Assignment Operator */ + Is16vec4& operator= (const M64 &a) { return *this = (Is16vec4) a; } + + /* Addition & Subtraction Assignment Operators */ + Is16vec4& operator&=(const M64 &a) { return *this = (Is16vec4) _m_pand(vec,a); } + Is16vec4& operator|=(const M64 &a) { return *this = (Is16vec4) _m_por(vec,a); } + Is16vec4& operator^=(const M64 &a) { return *this = (Is16vec4) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Is16vec4& operator +=(const I16vec4 &a) { return *this = (Is16vec4)_m_paddw(vec,a); } + Is16vec4& operator -=(const I16vec4 &a) { return *this = (Is16vec4)_m_psubw(vec,a); } + Is16vec4& operator *=(const I16vec4 &a) { return *this = (Is16vec4)_m_pmullw(vec,a); } + + /* Shift Logical Operators */ + Is16vec4 operator<<(const M64 &a) { return _m_psllw(vec,a); } + Is16vec4 operator<<(int count) { return _m_psllwi(vec,count); } + Is16vec4& operator<<=(const M64 &a) { return *this = (Is16vec4)_m_psllw(vec,a); } + Is16vec4& operator<<=(int count) { return *this = (Is16vec4)_m_psllwi(vec,count); } + /* Shift Arithmetic Operations */ + Is16vec4 operator>>(const M64 &a) { return _m_psraw(vec,a); } + Is16vec4 operator>>(int count) { return _m_psrawi(vec,count); } + Is16vec4& operator>>=(const M64 &a) { return *this = (Is16vec4) _m_psraw(vec,a); } + Is16vec4& operator>>=(int count) { return *this = (Is16vec4) _m_psrawi(vec,count); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Is16vec4 &a) + { + os << "[3]:" << _MM_4W(3,a) + << " [2]:" << _MM_4W(2,a) + << " [1]:" << _MM_4W(1,a) + << " [0]:" << _MM_4W(0,a); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const short& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4W(i,vec); + } + + /* Element Access for Debug */ + short& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4W(i,vec); + } +}; + +inline Is16vec4 operator*(const Is16vec4 &a, const Is16vec4 &b) { return _m_pmullw(a,b); } + +/* Compares */ +inline Is16vec4 cmpeq(const Is16vec4 &a, const Is16vec4 &b) { return _m_pcmpeqw(a,b); } +inline Is16vec4 cmpneq(const Is16vec4 &a, const Is16vec4 &b) { return _m_pandn(_m_pcmpeqw(a,b), _mmx_all_ones); } +inline Is16vec4 cmpgt(const Is16vec4 &a, const Is16vec4 &b) { return _m_pcmpgtw(a,b); } +inline Is16vec4 cmplt(const Is16vec4 &a, const Is16vec4 &b) { return _m_pcmpgtw(b,a); } +inline Is16vec4 cmple(const Is16vec4 &a, const Is16vec4 &b) { return _m_pandn(_m_pcmpgtw(a,b), _mmx_all_ones); } +inline Is16vec4 cmpge(const Is16vec4 &a, const Is16vec4 &b) { return _m_pandn(_m_pcmpgtw(b,a), _mmx_all_ones); } +/* Unpacks */ +inline Is16vec4 unpack_low(const Is16vec4 &a, const Is16vec4 &b) { return _m_punpcklwd(a,b); } +inline Is16vec4 unpack_high(const Is16vec4 &a, const Is16vec4 &b) { return _m_punpckhwd(a,b); } + +inline Is16vec4 sat_add(const Is16vec4 &a, const Is16vec4 &b) { return _m_paddsw(a,b); } +inline Is16vec4 sat_sub(const Is16vec4 &a, const Is16vec4 &b) { return _m_psubsw(a,b); } +inline Is16vec4 mul_high(const Is16vec4 &a, const Is16vec4 &b) { return _m_pmulhw(a,b); } +inline Is32vec2 mul_add(const Is16vec4 &a, const Is16vec4 &b) { return _m_pmaddwd(a,b);} + + +/* Iu16vec4 Class: + * 4 elements, each element unsigned short + */ +class Iu16vec4 : public I16vec4 +{ +public: + Iu16vec4() { } + Iu16vec4(__m64 mm) : I16vec4(mm) { } + Iu16vec4(unsigned short ui0, unsigned short ui1, + unsigned short ui2, unsigned short ui3) + : I16vec4(ui0, ui1, ui2, ui3) { } + EXPLICIT Iu16vec4(__int64 i) : I16vec4 (i) { } + EXPLICIT Iu16vec4(int i) : I16vec4 (i) { } + + /* Assignment Operator */ + Iu16vec4& operator= (const M64 &a) { return *this = (Iu16vec4) a; } + + /* Logical Assignment Operators */ + Iu16vec4& operator&=(const M64 &a) { return *this = (Iu16vec4) _m_pand(vec,a); } + Iu16vec4& operator|=(const M64 &a) { return *this = (Iu16vec4) _m_por(vec,a); } + Iu16vec4& operator^=(const M64 &a) { return *this = (Iu16vec4) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Iu16vec4& operator +=(const I16vec4 &a) { return *this = (Iu16vec4)_m_paddw(vec,a); } + Iu16vec4& operator -=(const I16vec4 &a) { return *this = (Iu16vec4)_m_psubw(vec,a); } + Iu16vec4& operator *=(const I16vec4 &a) { return *this = (Iu16vec4)_m_pmullw(vec,a); } + + /* Shift Logical Operators */ + Iu16vec4 operator<<(const M64 &a) { return _m_psllw(vec,a); } + Iu16vec4 operator<<(int count) { return _m_psllwi(vec,count); } + Iu16vec4& operator<<=(const M64 &a) { return *this = (Iu16vec4)_m_psllw(vec,a); } + Iu16vec4& operator<<=(int count) { return *this = (Iu16vec4)_m_psllwi(vec,count); } + Iu16vec4 operator>>(const M64 &a) { return _m_psrlw(vec,a); } + Iu16vec4 operator>>(int count) { return _m_psrlwi(vec,count); } + Iu16vec4& operator>>=(const M64 &a) { return *this = (Iu16vec4) _m_psrlw(vec,a); } + Iu16vec4& operator>>=(int count) { return *this = (Iu16vec4) _m_psrlwi(vec,count); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Iu16vec4 &a) + { + os << "[3]:" << _MM_4UW(3,a) + << " [2]:" << _MM_4UW(2,a) + << " [1]:" << _MM_4UW(1,a) + << " [0]:" << _MM_4UW(0,a); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const unsigned short& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4UW(i,vec); + } + + /* Element Access and Assignment for Debug */ + unsigned short& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 4); /* Only 4 elements to access */ + return _MM_4UW(i,vec); + } +}; + +inline Iu16vec4 operator*(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_pmullw(a,b); } +inline Iu16vec4 cmpeq(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_pcmpeqw(a,b); } +inline Iu16vec4 cmpneq(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_pandn(_m_pcmpeqw(a,b), _mmx_all_ones); } + +inline Iu16vec4 sat_add(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_paddusw(a,b); } +inline Iu16vec4 sat_sub(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_psubusw(a,b); } + +inline Iu16vec4 unpack_low(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_punpcklwd(a,b); } +inline Iu16vec4 unpack_high(const Iu16vec4 &a, const Iu16vec4 &b) { return _m_punpckhwd(a,b); } + +/* I8vec8 Class: + * 8 elements, each element either unsigned or signed char + */ +class I8vec8 : public M64 +{ +public: + I8vec8() { } + I8vec8(__m64 mm) : M64(mm) { } + I8vec8(char s0, char s1, char s2, char s3, char s4, char s5, char s6, char s7) + { + vec = _mm_set_pi8(s0, s1, s2, s3, s4, s5, s6, s7); + } + EXPLICIT I8vec8(__int64 i) : M64 (i) { } + EXPLICIT I8vec8(int i) : M64 (i) { } + + /* Assignment Operator */ + I8vec8& operator= (const M64 &a) { return *this = (I8vec8) a; } + + /* Logical Assignment Operators */ + I8vec8& operator&=(const M64 &a) { return *this = (I8vec8) _m_pand(vec,a); } + I8vec8& operator|=(const M64 &a) { return *this = (I8vec8) _m_por(vec,a); } + I8vec8& operator^=(const M64 &a) { return *this = (I8vec8) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + I8vec8& operator +=(const I8vec8 &a) { return *this = (I8vec8) _m_paddb(vec,a); } + I8vec8& operator -=(const I8vec8 &a) { return *this = (I8vec8) _m_psubb(vec,a); } +}; + + +inline I8vec8 cmpeq(const I8vec8 &a, const I8vec8 &b) { return _m_pcmpeqb(a,b); } +inline I8vec8 cmpneq(const I8vec8 &a, const I8vec8 &b) { return _m_pandn(_m_pcmpeqb(a,b), _mmx_all_ones); } + +inline I8vec8 unpack_low(const I8vec8 &a, const I8vec8 &b) { return _m_punpcklbw(a,b); } +inline I8vec8 unpack_high(const I8vec8 &a, const I8vec8 &b) { return _m_punpckhbw(a,b); } + +/* Is8vec8 Class: + * 8 elements, each element signed char + */ +class Is8vec8 : public I8vec8 +{ +public: + Is8vec8() { } + Is8vec8(__m64 mm) : I8vec8(mm) { } + Is8vec8(signed char s0, signed char s1, signed char s2, signed char s3, + signed char s4, signed char s5, signed char s6, signed char s7) + : I8vec8(s0, s1, s2, s3, s4, s5, s6, s7) { } + EXPLICIT Is8vec8(__int64 i) : I8vec8 (i) { } + EXPLICIT Is8vec8(int i) : I8vec8 (i) { } + + /* Assignment Operator */ + Is8vec8& operator= (const M64 &a) { return *this = (Is8vec8) a; } + + /* Logical Assignment Operators */ + Is8vec8& operator&=(const M64 &a) { return *this = (Is8vec8) _m_pand(vec,a); } + Is8vec8& operator|=(const M64 &a) { return *this = (Is8vec8) _m_por(vec,a); } + Is8vec8& operator^=(const M64 &a) { return *this = (Is8vec8) _m_pxor(vec,a); } + + /* Addition & Subtraction Assignment Operators */ + Is8vec8& operator +=(const I8vec8 &a) { return *this = (Is8vec8) _m_paddb(vec,a); } + Is8vec8& operator -=(const I8vec8 &a) { return *this = (Is8vec8) _m_psubb(vec,a); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator<< (std::ostream &os, const Is8vec8 &a) + { + os << "[7]:" << short(_MM_8B(7,a)) + << " [6]:" << short(_MM_8B(6,a)) + << " [5]:" << short(_MM_8B(5,a)) + << " [4]:" << short(_MM_8B(4,a)) + << " [3]:" << short(_MM_8B(3,a)) + << " [2]:" << short(_MM_8B(2,a)) + << " [1]:" << short(_MM_8B(1,a)) + << " [0]:" << short(_MM_8B(0,a)); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const signed char& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8B(i,vec); + } + + /* Element Access and Assignment for Debug */ + signed char& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8B(i,vec); + } +}; + +/* Additional Is8vec8 functions: compares, unpacks, sat add/sub */ +inline Is8vec8 cmpeq(const Is8vec8 &a, const Is8vec8 &b) { return _m_pcmpeqb(a,b); } +inline Is8vec8 cmpneq(const Is8vec8 &a, const Is8vec8 &b) { return _m_pandn(_m_pcmpeqb(a,b), _mmx_all_ones); } +inline Is8vec8 cmpgt(const Is8vec8 &a, const Is8vec8 &b) { return _m_pcmpgtb(a,b); } +inline Is8vec8 cmplt(const Is8vec8 &a, const Is8vec8 &b) { return _m_pcmpgtb(b,a); } +inline Is8vec8 cmple(const Is8vec8 &a, const Is8vec8 &b) { return _m_pandn(_m_pcmpgtb(a,b), _mmx_all_ones); } +inline Is8vec8 cmpge(const Is8vec8 &a, const Is8vec8 &b) { return _m_pandn(_m_pcmpgtb(b,a), _mmx_all_ones); } + +inline Is8vec8 unpack_low(const Is8vec8 &a, const Is8vec8 &b) { return _m_punpcklbw(a,b); } +inline Is8vec8 unpack_high(const Is8vec8 &a, const Is8vec8 &b) { return _m_punpckhbw(a,b); } + +inline Is8vec8 sat_add(const Is8vec8 &a, const Is8vec8 &b) { return _m_paddsb(a,b); } +inline Is8vec8 sat_sub(const Is8vec8 &a, const Is8vec8 &b) { return _m_psubsb(a,b); } + +/* Iu8vec8 Class: + * 8 elements, each element unsigned char + */ +class Iu8vec8 : public I8vec8 +{ +public: + Iu8vec8() { } + Iu8vec8(__m64 mm) : I8vec8(mm) { } + Iu8vec8(unsigned char s0, unsigned char s1, unsigned char s2, + unsigned char s3, unsigned char s4, unsigned char s5, + unsigned char s6, unsigned char s7) + : I8vec8(s0, s1, s2, s3, s4, s5, s6, s7) { } + EXPLICIT Iu8vec8(__int64 i) : I8vec8 (i) { } + EXPLICIT Iu8vec8(int i) : I8vec8 (i) { } + + /* Assignment Operator */ + Iu8vec8& operator= (const M64 &a) { return *this = (Iu8vec8) a; } + /* Logical Assignment Operators */ + Iu8vec8& operator&=(const M64 &a) { return *this = (Iu8vec8) _m_pand(vec,a); } + Iu8vec8& operator|=(const M64 &a) { return *this = (Iu8vec8) _m_por(vec,a); } + Iu8vec8& operator^=(const M64 &a) { return *this = (Iu8vec8) _m_pxor(vec,a); } + /* Addition & Subtraction Assignment Operators */ + Iu8vec8& operator +=(const I8vec8 &a) { return *this = (Iu8vec8) _m_paddb(vec,a); } + Iu8vec8& operator -=(const I8vec8 &a) { return *this = (Iu8vec8) _m_psubb(vec,a); } + +#if defined (_ENABLE_VEC_DEBUG) + /* Output for Debug */ + friend std::ostream& operator << (std::ostream &os, const Iu8vec8 &a) + { + os << "[7]:" << (unsigned short) (_MM_8UB(7,a)) + << " [6]:" << (unsigned short) (_MM_8UB(6,a)) + << " [5]:" << (unsigned short) (_MM_8UB(5,a)) + << " [4]:" << (unsigned short) (_MM_8UB(4,a)) + << " [3]:" << (unsigned short) (_MM_8UB(3,a)) + << " [2]:" << (unsigned short) (_MM_8UB(2,a)) + << " [1]:" << (unsigned short) (_MM_8UB(1,a)) + << " [0]:" << (unsigned short) (_MM_8UB(0,a)); + return os; + } +#endif /* defined (_ENABLE_VEC_DEBUG) */ + + /* Element Access for Debug, No data modified */ + const unsigned char& operator[](int i)const + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8UB(i,vec); + } + + /* Element Access for Debug */ + unsigned char& operator[](int i) + { + _VEC_ASSERT(static_cast<unsigned int>(i) < 8); /* Only 8 elements to access */ + return _MM_8UB(i,vec); + } +}; + +/* Additional Iu8vec8 functions: cmpeq,cmpneq, unpacks, sat add/sub */ +inline Iu8vec8 cmpeq(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_pcmpeqb(a,b); } +inline Iu8vec8 cmpneq(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_pandn(_m_pcmpeqb(a,b), _mmx_all_ones); } + +inline Iu8vec8 unpack_low(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_punpcklbw(a,b); } +inline Iu8vec8 unpack_high(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_punpckhbw(a,b); } + +inline Iu8vec8 sat_add(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_paddusb(a,b); } +inline Iu8vec8 sat_sub(const Iu8vec8 &a, const Iu8vec8 &b) { return _m_psubusb(a,b); } + +inline Is16vec4 pack_sat(const Is32vec2 &a, const Is32vec2 &b) { return _m_packssdw(a,b); } +inline Is8vec8 pack_sat(const Is16vec4 &a, const Is16vec4 &b) { return _m_packsswb(a,b); } +inline Iu8vec8 packu_sat(const Is16vec4 &a, const Is16vec4 &b) { return _m_packuswb(a,b); } + + /********************************* Logicals ****************************************/ +#define IVEC_LOGICALS(vect,element) \ +inline I##vect##vec##element operator& (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _m_pand( a,b); } \ +inline I##vect##vec##element operator| (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _m_por( a,b); } \ +inline I##vect##vec##element operator^ (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _m_pxor( a,b); } \ +inline I##vect##vec##element andnot (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _m_pandn( a,b); } + +IVEC_LOGICALS(8,8) +IVEC_LOGICALS(u8,8) +IVEC_LOGICALS(s8,8) +IVEC_LOGICALS(16,4) +IVEC_LOGICALS(u16,4) +IVEC_LOGICALS(s16,4) +IVEC_LOGICALS(32,2) +IVEC_LOGICALS(u32,2) +IVEC_LOGICALS(s32,2) +IVEC_LOGICALS(64,1) +#undef IVEC_LOGICALS + + /********************************* Add & Sub ****************************************/ +#define IVEC_ADD_SUB(vect,element,opsize) \ +inline I##vect##vec##element operator+ (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _m_padd##opsize( a,b); } \ +inline I##vect##vec##element operator- (const I##vect##vec##element &a, const I##vect##vec##element &b) \ +{ return _m_psub##opsize( a,b); } + +IVEC_ADD_SUB(8,8, b) +IVEC_ADD_SUB(u8,8, b) +IVEC_ADD_SUB(s8,8, b) +IVEC_ADD_SUB(16,4, w) +IVEC_ADD_SUB(u16,4, w) +IVEC_ADD_SUB(s16,4, w) +IVEC_ADD_SUB(32,2, d) +IVEC_ADD_SUB(u32,2, d) +IVEC_ADD_SUB(s32,2, d) +#undef IVEC_ADD_SUB + + /************************* Conditional Select ******************************** + * version of: retval = (a OP b)? c : d; * + * Where OP is one of the possible comparision operators. * + * Example: r = select_eq(a,b,c,d); * + * if "member at position x of the vector a" == * + * "member at position x of vector b" * + * assign the corresponding member in r from c, else assign from d. * + ************************* Conditional Select ********************************/ + +#define IVEC_SELECT(vect12,vect34,element,selop) \ + inline I##vect34##vec##element select_##selop ( \ + const I##vect12##vec##element &a, \ + const I##vect12##vec##element &b, \ + const I##vect34##vec##element &c, \ + const I##vect34##vec##element &d) \ +{ \ + I##vect12##vec##element mask = cmp##selop(a,b); \ + return( (I##vect34##vec##element)(mask & c ) | \ + (I##vect34##vec##element)((_m_pandn(mask, d )))); \ +} + +IVEC_SELECT(8,s8,8,eq) +IVEC_SELECT(8,u8,8,eq) +IVEC_SELECT(8,8,8,eq) +IVEC_SELECT(8,s8,8,neq) +IVEC_SELECT(8,u8,8,neq) +IVEC_SELECT(8,8,8,neq) + +IVEC_SELECT(16,s16,4,eq) +IVEC_SELECT(16,u16,4,eq) +IVEC_SELECT(16,16,4,eq) +IVEC_SELECT(16,s16,4,neq) +IVEC_SELECT(16,u16,4,neq) +IVEC_SELECT(16,16,4,neq) + +IVEC_SELECT(32,s32,2,eq) +IVEC_SELECT(32,u32,2,eq) +IVEC_SELECT(32,32,2,eq) +IVEC_SELECT(32,s32,2,neq) +IVEC_SELECT(32,u32,2,neq) +IVEC_SELECT(32,32,2,neq) + + +IVEC_SELECT(s8,s8,8,gt) +IVEC_SELECT(s8,u8,8,gt) +IVEC_SELECT(s8,8,8,gt) +IVEC_SELECT(s8,s8,8,lt) +IVEC_SELECT(s8,u8,8,lt) +IVEC_SELECT(s8,8,8,lt) +IVEC_SELECT(s8,s8,8,le) +IVEC_SELECT(s8,u8,8,le) +IVEC_SELECT(s8,8,8,le) +IVEC_SELECT(s8,s8,8,ge) +IVEC_SELECT(s8,u8,8,ge) +IVEC_SELECT(s8,8,8,ge) + +IVEC_SELECT(s16,s16,4,gt) +IVEC_SELECT(s16,u16,4,gt) +IVEC_SELECT(s16,16,4,gt) +IVEC_SELECT(s16,s16,4,lt) +IVEC_SELECT(s16,u16,4,lt) +IVEC_SELECT(s16,16,4,lt) +IVEC_SELECT(s16,s16,4,le) +IVEC_SELECT(s16,u16,4,le) +IVEC_SELECT(s16,16,4,le) +IVEC_SELECT(s16,s16,4,ge) +IVEC_SELECT(s16,u16,4,ge) +IVEC_SELECT(s16,16,4,ge) + +IVEC_SELECT(s32,s32,2,gt) +IVEC_SELECT(s32,u32,2,gt) +IVEC_SELECT(s32,32,2,gt) +IVEC_SELECT(s32,s32,2,lt) +IVEC_SELECT(s32,u32,2,lt) +IVEC_SELECT(s32,32,2,lt) +IVEC_SELECT(s32,s32,2,le) +IVEC_SELECT(s32,u32,2,le) +IVEC_SELECT(s32,32,2,le) +IVEC_SELECT(s32,s32,2,ge) +IVEC_SELECT(s32,u32,2,ge) +IVEC_SELECT(s32,32,2,ge) + + +#undef IVEC_SELECT + +inline static void empty(void) { _m_empty(); } + +#if defined (_SILENCE_IVEC_C4799) + #pragma warning(pop) +#endif /* defined (_SILENCE_IVEC_C4799) */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* RC_INVOKED */ +#endif /* _IVEC_H_INCLUDED */ diff --git a/test_data/lots_of_files/jam.cpp b/test_data/lots_of_files/jam.cpp new file mode 100644 index 0000000..4b5f0e7 --- /dev/null +++ b/test_data/lots_of_files/jam.cpp @@ -0,0 +1,2300 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.09.2015 (mm.dd.yyyy) + * + * Game Layer. + */ + +// TOP + +internal Memory_Block +make_memory_block(void *base, i32 size){ + Memory_Block block; + block.base = base; + block.size = size; + block.cursor = 0; + return block; +} + +internal void* +get_memory_size(Memory_Block *block, i32 size){ + Assert(block->cursor + size <= block->size); + void *result = (u8*)block->base + block->cursor; + block->cursor += size; + return result; +} + +#define get_mem(t,block) (t*)get_memory_size(block, sizeof(t)) +#define get_mem_size(block,size) get_memory_size(block, size) +#define get_mem_array(t,block,size) (t*)get_memory_size(block, sizeof(t)*(size)) +#define remaining_mem_i32(block) (i32)((block).size - (block).cursor) + +#define get_i(x,y) ((x)+(y)*WIDTH) + +struct Temp_Memory{ + Memory_Block *block; + i32 prev_cursor; +}; + +internal Temp_Memory +begin_temp_memory(Memory_Block *block){ + Temp_Memory result; + result.block = block; + result.prev_cursor = block->cursor; + return result; +} + +internal void +end_temp_memory(Temp_Memory temp){ + temp.block->cursor = temp.prev_cursor; +} + +struct Wave_Pair{ + i32 left, right; +}; + +internal bool32 +mix_track(Track *track, Wave_Pair *value){ + bool32 result = 0; + if (track->playing && track->frames_delay == 0){ + Sound sound = track->sound; + + i16 *src_prev = sound.samples + track->sample_pos*2; + i16 *src_next; + if (track->sample_pos + 1 == sound.sample_count){ + src_next = sound.samples; + } + else{ + src_next = src_prev + 2; + } + + real32 dec = track->sample_pos_dec; + real32 inv_dec = 1.f - dec; + + dec *= track->volume; + inv_dec *= track->volume; + + value->left += (i32)((*src_prev)*inv_dec + (*src_next)*dec); + + ++src_prev; + ++src_next; + + value->right += (i16)((*src_prev)*inv_dec + (*src_next)*dec); + + track->sample_pos_dec += sound.scan_speed*track->bend; + if (track->sample_pos_dec >= 1.f){ + track->sample_pos += (i32)(track->sample_pos_dec); + track->sample_pos_dec = DecPart(track->sample_pos_dec); + + if (track->sample_pos >= sound.sample_count){ + if (track->looping){ + track->sample_pos -= sound.sample_count; + } + else{ + track->playing = 0; + result = 1; + } + } + } + } + + return result; +} + +internal +SIG_APP_FILL_SAMPLES(game_fill_samples){ + App_Vars *vars = (App_Vars*)memory.data; + + if (vars->silence_timer == 0){ + +#if 0 + i16 *dst = target.samples; + + for (i32 i = 0; i < target.count_per_channel; ++i){ + real32 wave_hz = 440.f; + real32 t = vars->wave_t * TAU32; + real32 v = sinf(t); + + i16 value = (i16)(max_i16 * v); + *dst++ = value; + *dst++ = value; + + vars->wave_t += wave_hz / target.samples_per_second; + } + + vars->wave_t = DecPart(vars->wave_t); +#endif + + i16 *dst = target.samples; + for (i32 i = 0; i < target.count_per_channel; ++i){ + Wave_Pair value = {}; + + for (i32 j = 0; j < ArrayCount(vars->sfx_tracks); ++j){ + Track *track = vars->sfx_tracks + j; + if (mix_track(track, &value)){ + track->next_free = vars->sfx_free; + vars->sfx_free = track; + } + } + + if (vars->music.playing){ + if (mix_track(&vars->music, &value)){ + vars->song_done = 1; + } + } + + if (value.left > max_i16){ + value.left = max_i16; + } + else if (value.left < min_i16){ + value.left = min_i16; + } + + if (value.right > max_i16){ + value.right = max_i16; + } + else if (value.right < min_i16){ + value.right = min_i16; + } + + *dst++ = (i16)value.left; + *dst++ = (i16)value.right; + } + } + + for (i32 j = 0; j < ArrayCount(vars->sfx_tracks); ++j){ + Track *track = vars->sfx_tracks + j; + if (track->playing && track->frames_delay > 0){ + --track->frames_delay; + } + } +} + +#define A_SHIFT 24 +#define R_SHIFT 16 +#define G_SHIFT 8 +#define B_SHIFT 0 + +#define A_MASK 0xFF000000 +#define R_MASK 0x00FF0000 +#define G_MASK 0x0000FF00 +#define B_MASK 0x000000FF + +internal u32 +compress_color(Vec3 color){ + return + ((u8)(255*color.r) << R_SHIFT) + + ((u8)(255*color.g) << G_SHIFT) + + ((u8)(255*color.b) << B_SHIFT); +} + +internal u32 +compress_color(Vec4 color){ + return + ((u8)(255*color.a) << A_SHIFT) + + ((u8)(255*color.r) << R_SHIFT) + + ((u8)(255*color.g) << G_SHIFT) + + ((u8)(255*color.b) << B_SHIFT); +} + +internal Vec3 +decompress_color(u32 color){ + return V3(((color & R_MASK) >> R_SHIFT) / 255.f, + ((color & G_MASK) >> G_SHIFT) / 255.f, + ((color & B_MASK) >> B_SHIFT) / 255.f); +} + +internal Vec4 +decompress_color_alpha(u32 color){ + return V4(((color & R_MASK) >> R_SHIFT) / 255.f, + ((color & G_MASK) >> G_SHIFT) / 255.f, + ((color & B_MASK) >> B_SHIFT) / 255.f, + ((color & A_MASK) >> A_SHIFT) / 255.f); +} + +internal void +draw_clear(Vec4 color){ + glClearColor(color.r, color.g, color.b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +internal Render_Texture +make_render_texture(Image image){ + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, image.data); + + Render_Texture texture; + texture.texid = tex; + texture.width = image.width; + texture.height = image.height; + texture.img_width = image.img_width; + texture.img_height = image.img_height; + texture.tex_x = (real32)texture.img_width / texture.width; + texture.tex_y = (real32)texture.img_height / texture.height; + + return texture; +} + +internal void +draw_texture(Render_Texture texture, Vec2 center, Vec2 halfdim, + real32 rotation = 0.f, Vec4 blend = {1.f, 1.f, 1.f, 1.f}){ + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glTranslatef(center.x, center.y, 0.f); + glRotatef(rotation, 0, 0, 1); + + glColor4f(blend.r, blend.g, blend.b, blend.a); + glBindTexture(GL_TEXTURE_2D, texture.texid); + glBegin(GL_QUADS); + { + glTexCoord2f(0.f, texture.tex_y); + glVertex3f(-halfdim.x, halfdim.y, 0.f); + + glTexCoord2f(texture.tex_x, texture.tex_y); + glVertex3f(halfdim.x, halfdim.y, 0.f); + + glTexCoord2f(texture.tex_x, 0.f); + glVertex3f(halfdim.x, -halfdim.y, 0.f); + + glTexCoord2f(0.f, 0.f); + glVertex3f(-halfdim.x, -halfdim.y, 0.f); + } + glEnd(); + + glPopMatrix(); +} + +inline internal void +draw_texture(Render_Texture texture, Vec2 center, + real32 rotation = 0.f, Vec4 blend = {1.f, 1.f, 1.f, 1.f}){ + Vec2 halfdim; + halfdim.x = texture.img_width * 0.5f; + halfdim.y = texture.img_height * 0.5f; + draw_texture(texture, center, halfdim, rotation, blend); +} + +internal void +draw_rectangle(real32 x1, real32 y1, + real32 x2, real32 y2, + Vec4 color, real32 z = 0.f){ + + glBindTexture(GL_TEXTURE_2D, 0); + glColor4f(color.r, color.g, color.b, color.a); + glBegin(GL_POLYGON); + { + glVertex3f(x1, y2, z); + glVertex3f(x2, y2, z); + glVertex3f(x2, y1, z); + glVertex3f(x1, y1, z); + } + glEnd(); +} + +internal void +draw_text(Game_Render_Target *target, Font *font, real32 *x, real32 *y, char *str, i32 len, Vec4 color, + real32 start_x = 0.f, real32 x_limit = 10000.f){ + u32 packed_color = compress_color(color); + for (i32 i = 0; i < len; ++i){ + char c = str[i]; + font_draw_glyph(target, font, c, *x, *y, packed_color); + *x += font->glyphs[c].advance; + if (*x > x_limit){ + *x = start_x; + *y += font->height; + } + } +} + +internal bool32 +load_texture(char *filename, Render_Texture *texture, Memory_Block *block){ + bool32 result = 0; + Image image; + Bitmap_File file = {}; + bitmap_open_file(filename, &file); + if (file.file.data){ + Temp_Memory temp = begin_temp_memory(block); + + i32 size = bitmap_data_requirement(&file); + image.data = (u32*)get_mem_size(block, size); + bitmap_fill_image(&file, &image); + bitmap_free_file(&file); + + *texture = make_render_texture(image); + + end_temp_memory(temp); + result = 1; + } + + return result; +} + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +internal bool32 +load_texture_png(char *filename, Render_Texture *texture, Memory_Block *block){ + Image image; + i32 n; + image.data = (u32*)stbi_load(filename, &image.width, &image.height, &n, 0); + TentativeAssert(image.data); + + if (image.data){ + Assert(n == 4); + Image POT_image; + POT_image.width = round_up_POT(image.width); + POT_image.height = round_up_POT(image.height); + POT_image.img_width = image.width; + POT_image.img_height = image.height; + + Temp_Memory temp = begin_temp_memory(block); + + POT_image.data = get_mem_array(u32, block, POT_image.width*POT_image.height); + + u32 *src_line = image.data; + u32 *dst_line = POT_image.data; + for (i32 y = 0; y < image.height; ++y){ + u32 *src = src_line; + u32 *dst = dst_line; + for (i32 x = 0; x < image.width; ++x){ + *dst++ = *src++; + } + src_line += image.width; + dst_line += POT_image.width; + } + + *texture = make_render_texture(POT_image); + + end_temp_memory(temp); + stbi_image_free(image.data); + + return 1; + } + + return 0; +} + +internal bool32 +load_sound(char *filename, Sound *sound, i32 target_samples_per_second, Memory_Block *block){ + bool32 result = 0; + Wav_File file; + wav_open_file(filename, &file); + if (file.file.data){ + i32 size = wav_data_requirement(&file); + sound->samples = (i16*)get_mem_size(block, size); + wav_fill_sound(&file, sound, target_samples_per_second); + result = 1; + } + return result; +} + +internal bool32 +load_font(char *filename, Font *font, i32 pt_size, Memory_Block *block){ + bool32 result = 0; + Temp_Memory temp = begin_temp_memory(block); + i32 size = remaining_mem_i32(*block); + void *mem = get_mem_size(block, size); + if (font_load(filename, font, pt_size, mem, size)){ + result = 0; + } + end_temp_memory(temp); + return result; +} + +internal bool32 +can_move_to(App_Vars *vars, Entity *entity, i32 x, i32 y, bool32 *out_of_grid){ + AllowLocal(entity); + bool32 result = 1; + if (y >= HEIGHT || y < 0 || + x >= WIDTH || x < 0){ + result = 0; + *out_of_grid = 1; + } + else{ + Entity *other_entity = vars->grid[get_i(x, y)]; + if (other_entity){ + result = 0; + } + *out_of_grid = 0; + } + return result; +} + +inline internal bool32 +can_move_to(App_Vars *vars, Entity *entity, i32 x, i32 y){ + bool32 throw_away; + return can_move_to(vars, entity, x, y, &throw_away); +} + +internal Entity* +spawn_entity(App_Vars *vars){ + Assert(vars->entity_count < vars->entity_max); + + Entity *result = vars->entities + vars->entity_count++; + *result = {}; + + return result; +} + +internal void +make_random_block(App_Vars *vars){ + Assert(vars->spawn_count < vars->spawn_max); + + Spawn_Request *spawn = vars->spawns + vars->spawn_count++; + spawn->random = 1; +} + +internal void +make_zombie(App_Vars *vars, i32 x, i32 y){ + Assert(vars->spawn_count < vars->spawn_max); + + Spawn_Request *spawn = vars->spawns + vars->spawn_count++; + spawn->random = 0; + spawn->x = x; + spawn->y = y; + spawn->type = ZOMBIE; +} + +internal Entity* +make_brain(App_Vars *vars, i32 x, i32 y){ + Assert(vars->spawn_count < vars->spawn_max); + + Spawn_Request *spawn = vars->spawns + vars->spawn_count++; + spawn->random = 0; + spawn->x = x; + spawn->y = y; + spawn->type = BRAIN; +} + +internal void +move_entity(App_Vars *vars, Entity *entity, i32 x, i32 y){ + i32 old_i = get_i(entity->grid_x, entity->grid_y); + i32 new_i = get_i(x, y); + + Assert(vars->grid[old_i] == entity); + Assert(vars->grid[new_i] == 0); + + vars->grid[old_i] = 0; + vars->grid[new_i] = entity; + + entity->grid_x = x; + entity->grid_y = y; +} + +internal Entity* +get_grid_entity(App_Vars *vars, i32 x, i32 y, bool32 *out_of_bounds){ + if (x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT){ + *out_of_bounds = 1; + return 0; + } + else{ + *out_of_bounds = 0; + return vars->grid[get_i(x, y)]; + } +} + +inline internal Entity* +get_grid_entity(App_Vars *vars, i32 x, i32 y){ + bool32 throw_away; + return get_grid_entity(vars, x, y, &throw_away); +} + +internal i32 +points_for_destruction(Block_Type type){ + i32 result = 0; + + switch (type){ + case ZOMBIE: + result = 15; + break; + + case HUMAN: + result = 1; + break; + + case BRAIN: + result = 1; + break; + + case AMMO: + result = 1; + break; + + case BOMB: + result = 5; + break; + + case WALL: + result = 100; + break; + } + + return result; +} + +internal bool32 +play_sound_effect(App_Vars *vars, Sound sound, real32 volume, real32 bend, + i32 delay = 0){ + bool32 result = 0; + + if (vars->sfx_free){ + result = 1; + Track *track = vars->sfx_free; + vars->sfx_free = track->next_free; + *track = {}; + + track->sound = sound; + track->playing = 1; + track->volume = volume; + track->bend = bend; + track->frames_delay = delay; + } + + return result; +} + +internal real32 +random_real32(App_Vars *vars, real32 min, real32 max){ + real32 result; + u32 x = pcg32_random_r(&vars->rnd) % 1000000; + result = min + (x / 1000000.f)*(max - min); + return result; +} + +globalvar real32 BEND_UP = 1.2f; +globalvar real32 BEND_DOWN = 0.8f; + +internal void +mark_block_destroyed(App_Vars *vars, Entity *entity){ + if (!entity->death_marked){ + Assert(vars->free_count < vars->free_max); + vars->to_free[vars->free_count++] = entity; + vars->need_to_fill_gaps = 1; + vars->fill_timer = COMBO_TIME; + entity->death_marked = 1; + + vars->score += points_for_destruction(entity->type); + vars->nonsense_score = pcg32_random_r(&vars->rnd) % 1000; + } +} + +inline internal i32 +get_entity_index(App_Vars *vars, Entity *entity){ + Assert(entity >= vars->entities); + return (i32)(entity - vars->entities); +} + +inline internal Grid_Pos +get_grid_pos(Entity *entity){ + Grid_Pos result; + result.x = entity->grid_x; + result.y = entity->grid_y; + return result; +} + +struct Entity_Y_Data{ + i32 index, y; +}; + +internal i32 +quick_partition(Entity_Y_Data *data, i32 start, i32 pivot){ + i32 mid = (start + pivot)/2; + Swap(Entity_Y_Data, data[mid], data[pivot]); + i32 pivot_y = data[pivot].y; + for (i32 i = start; i < pivot; ++i){ + if (data[i].y < pivot_y){ + Swap(Entity_Y_Data, data[i], data[start]); + ++start; + } + } + Swap(Entity_Y_Data, data[pivot], data[start]); + return start; +} + +internal void +quick_sort(Entity_Y_Data *data, i32 start, i32 pivot){ + if (start < pivot){ + i32 mid = quick_partition(data, start, pivot); + quick_sort(data, start, mid-1); + quick_sort(data, mid+1, pivot); + } +} + +internal i32 +quick_partition(void **data, i32 start, i32 pivot){ + i32 mid = (start + pivot)/2; + Swap(void*, data[mid], data[pivot]); + void *pivot_ptr = data[pivot]; + for (i32 i = start; i < pivot; ++i){ + if (data[i] > pivot_ptr){ + Swap(void*, data[i], data[start]); + ++start; + } + } + Swap(void*, data[pivot], data[start]); + return start; +} + +internal void +quick_sort(void **data, i32 start, i32 pivot){ + if (start < pivot){ + i32 mid = quick_partition(data, start, pivot); + quick_sort(data, start, mid-1); + quick_sort(data, mid+1, pivot); + } +} + +internal i32 +int_to_string(char *buffer, i32 x, i32 force_len = 0){ + Assert(x >= 0); + i32 i = 16; + while (x != 0){ + char c = (char)(x % 10); + x /= 10; + c += '0'; + buffer[--i] = c; + } + i32 l = 16 - i; + while (l < force_len){ + ++l; + buffer[--i] = '0'; + } + return i; +} + +internal bool32 +do_text_field(i32 my_index, i32 active_index, bool32 blink_on, + Game_Render_Target *render_target, Game_Input input, + Font *font, real32 text_x, real32 text_y, + i32 *len, char *buffer, i32 max){ + + bool32 active = (my_index == active_index); + + if (active){ + if (input.key_input){ + if (input.key_code == 0){ + if (*len > 0){ + *len -= 1; + } + } + else if (input.key_code != 1){ + if (*len < max){ + buffer[*len] = input.key_code; + *len += 1; + } + } + } + } + + Vec4 color = V4(0.9f, 0.9f, 0.9f, 1.f); + if (active){ + color = V4(1.f, 1.f, 1.f, 1.f); + } + + draw_text(render_target, font, &text_x, &text_y, buffer, *len, color); + + if (active && blink_on){ + char cursor = '|'; + draw_text(render_target, font, &text_x, &text_y, &cursor, 1, color); + } + + return (active && input.key_input && input.key_code == 1); +} + +internal bool32 +do_button(i32 my_index, i32 active_index, + Game_Input input, Render_Texture *textures, + real32 x, real32 y){ + + bool32 active = (my_index == active_index); + + draw_texture(textures[active], V2(x, y)); + + return (active && input.key_input && input.key_code == 1); +} + +#define START_WITH_GAME_OVER 0 +#define SKIP_TITLE_SCREEN 0 + +inline internal i32 +get_level_fall_tick_time(App_Vars *vars, i32 level){ + i32 real_level = level; + if (real_level >= ArrayCount(levels)){ + real_level = ArrayCount(levels) - 1; + } + return (levels[real_level].fall_tick_time); +} + +inline internal i32 +get_level_min_score(App_Vars *vars, i32 level){ + i32 result; + i32 real_level = level; + if (real_level >= ArrayCount(levels)){ + result = levels[ArrayCount(levels)-1].fall_tick_time; + result = (level - ArrayCount(levels) + 1)*1200; + } + else{ + result = (levels[real_level].min_score); + } + return result; +} + +internal void +game_set_to_new(App_Vars *vars){ + vars->level = 0; + vars->fall_timer = get_level_fall_tick_time(vars, vars->level); + + vars->spawn_count = 0; + vars->spawn_max = ArrayCount(vars->spawns); + + vars->free_count = 0; + vars->free_max = ArrayCount(vars->to_free); + + vars->entity_count = 0; + vars->entity_max = ArrayCount(vars->entities); + vars->need_new_block = 1; + + vars->particle_count = 0; + vars->particle_max = ArrayCount(vars->particles); + + vars->refall_timer = 0; + vars->game_over = 0; + vars->need_to_fill_gaps = 0; + vars->chain_reacting = 0; + vars->reaction_timer = 0; + vars->fill_timer = 0; + vars->score = 0; + vars->nonsense_score = 0; + vars->active_field = 0; + vars->blink_timer = 0; + vars->user_name_len = 0; + vars->user_token_len = 0; + + memset(vars->grid, 0, sizeof(Entity*)*WIDTH*HEIGHT); + + vars->looked_up_teaser = 0; + vars->teaser_name_len = 0; + vars->teaser_score_len = 0; + + vars->controls_screen = 0; + vars->title_screen = 1; + + vars->music = {}; + vars->music.sound = vars->menu_music; + vars->music.playing = 1; + vars->music.looping = 1; + vars->music.volume = 0.3f; + vars->music.bend = 1.f; + + for (i32 i = ArrayCount(vars->sfx_tracks) - 2; i >= 0; --i){ + vars->sfx_tracks[i].next_free = vars->sfx_tracks + i + 1; + } + + vars->sfx_free = vars->sfx_tracks; +} + +persist real32 GRID_STRIDE = 94.0f; + +internal Vec2 +get_screen_pos(Entity *entity){ + persist Vec2 top_left = {265.f, 70.f - GRID_STRIDE}; + + Vec2 result; + result.x = entity->show_x*GRID_STRIDE; + result.y = entity->show_y*GRID_STRIDE; + result += top_left; + + return result; +} + +// THING GETS BROKEN +internal void +spawn_particles_type_1(App_Vars *vars, i32 prt_index, Vec2 pos, i32 count){ + Particle_Type prt_type = vars->prt_types[prt_index]; + Particle *particles = vars->particles; + i32 particle_count = vars->particle_count; + + if (vars->particle_max < particle_count + count){ + count = vars->particle_max - particle_count; + } + + for (i32 r = count; r > 0; --r){ + Particle part; + part.tex_index = (pcg32_random_r(&vars->rnd) % prt_type.tex_count) + prt_type.first_tex_id; + part.pos = pos; + part.vel.x = random_real32(vars, -20.f, 20.f); + part.vel.y = random_real32(vars, -20.f, 0.f); + part.life_counter = max_i32; + part.rot = random_real32(vars, 0.f, 360.f); + part.rot_vel = random_real32(vars, -10.f, 10.f); + + Assert(particle_count < vars->particle_max); + particles[particle_count++] = part; + } + + vars->particle_count = particle_count; +} + +// AMMO BOX GETS USED +internal void +spawn_particles_type_2(App_Vars *vars, i32 prt_index, Vec2 pos, i32 count){ + Particle_Type prt_type = vars->prt_types[prt_index]; + Particle *particles = vars->particles; + i32 particle_count = vars->particle_count; + + if (vars->particle_max < particle_count + count){ + count = vars->particle_max - particle_count; + } + + for (i32 r = count; r > 0; --r){ + Particle part; + part.tex_index = (pcg32_random_r(&vars->rnd) % prt_type.tex_count) + prt_type.first_tex_id; + part.pos = pos; + part.vel.x = random_real32(vars, -10.f, 10.f); + part.vel.y = random_real32(vars, 0.f, 0.f); + part.life_counter = max_i32; + part.rot = random_real32(vars, 0.f, 360.f); + part.rot_vel = random_real32(vars, -10.f, 10.f); + + Assert(particle_count < vars->particle_max); + particles[particle_count++] = part; + } + + vars->particle_count = particle_count; +} + +// STATIC PARTICLE +internal void +spawn_particles_type_3(App_Vars *vars, i32 prt_index, Vec2 pos, real32 rot){ + Particle_Type prt_type = vars->prt_types[prt_index]; + Particle *particles = vars->particles; + i32 particle_count = vars->particle_count; + + if (particle_count < vars->particle_max){ + Particle part; + part.tex_index = (pcg32_random_r(&vars->rnd) % prt_type.tex_count) + prt_type.first_tex_id; + part.pos = pos; + part.vel = {}; + part.life_counter = 5; + part.rot = rot; + part.rot_vel = 0; + + particles[particle_count++] = part; + + vars->particle_count = particle_count; + } +} + +internal +SIG_APP_STEP(game_step){ + App_Vars *vars = (App_Vars*)memory.data; + Assert(sizeof(App_Vars) < memory.size); + + if (first){ + font_init(); + *vars = {}; + vars->block = make_memory_block((u8*)memory.data + sizeof(App_Vars), + memory.size - sizeof(App_Vars)); + + // TODO(allen): thread the gamejolt api? + vars->gj = gj_init(80985, "031106786fa9dc6103062c42ee6f04ea"); + vars->table_id = 84092; + +#if 0 + gj_login(vars->gj, "OVERREACT", "F3DE5E"); + gj_post_score(vars->gj, vars->table_id, "ZERO", 1, "", ""); +#endif + + { + Temp_Memory temp = begin_temp_memory(&vars->block); + + GJ_Score_Data *scores = get_mem_array(GJ_Score_Data, &vars->block, 5); + i32 score_count = gj_get_scores(vars->gj, vars->table_id, 5, scores); + + for (i32 i = 0; i < score_count; ++i){ + gj_free_score(scores[i]); + } + + end_temp_memory(temp); + } + + vars->silence_timer = 70; + + load_texture_png("BRAINZ_background.png", &vars->background, &vars->block); + + load_texture_png("BRAINZ_human1.png", &vars->human[0][0], &vars->block); + load_texture_png("BRAINZ_humanzombie1_1.png", &vars->human[0][1], &vars->block); + load_texture_png("BRAINZ_humanzombie1_2.png", &vars->human[0][2], &vars->block); + load_texture_png("BRAINZ_humanzombie1_3.png", &vars->human[0][3], &vars->block); + load_texture_png("BRAINZ_zombie1.png", &vars->zombie[0], &vars->block); + load_texture_png("BRAINZ_ammo.png", &vars->ammo, &vars->block); + load_texture_png("BRAINZ_brain.png", &vars->brain, &vars->block); + load_texture_png("BRAINZ_tnt.png", &vars->bomb, &vars->block); + load_texture_png("BRAINZ_wall.png", &vars->wall, &vars->block); + + load_texture_png("BRAINZ_score.png", &vars->scorename, &vars->block); + load_texture_png("BRAINZ_scoreback.png", &vars->scoreback, &vars->block); + load_texture_png("BRAINZ_gameover.png", &vars->gameover, &vars->block); + load_texture_png("BRAINZ_shadow.png", &vars->shadow, &vars->block); + + load_texture_png("BRAINZ_gameover_button1.png", &vars->finish_button[0], &vars->block); + load_texture_png("BRAINZ_gameover_button2.png", &vars->finish_button[1], &vars->block); + + load_texture_png("BRAINZ_OVERREACT.png", &vars->overreact, &vars->block); + load_texture_png("BRAINZ_title.png", &vars->title, &vars->block); + load_texture_png("BRAINZ_title_button.png", &vars->title_button, &vars->block); + load_texture_png("BRAINZ_controls.png", &vars->controls, &vars->block); + + Particle_Textures prt_textures; + + load_texture_png("prts\\BrainZ_AMMO_part_1.png", &prt_textures.prt_ammo[0], &vars->block); + load_texture_png("prts\\BrainZ_AMMO_part_2.png", &prt_textures.prt_ammo[1], &vars->block); + load_texture_png("prts\\BrainZ_AMMO_part_3.png", &prt_textures.prt_ammo[2], &vars->block); + load_texture_png("prts\\BrainZ_AMMO_part_4.png", &prt_textures.prt_ammo[3], &vars->block); + load_texture_png("prts\\BrainZ_AMMO_part_5.png", &prt_textures.prt_ammo[4], &vars->block); + + load_texture_png("prts\\BrainZ_BLOOD_part_1.png", &prt_textures.prt_blood[0], &vars->block); + load_texture_png("prts\\BrainZ_BLOOD_part_2.png", &prt_textures.prt_blood[1], &vars->block); + load_texture_png("prts\\BrainZ_BLOOD_part_3.png", &prt_textures.prt_blood[2], &vars->block); + load_texture_png("prts\\BrainZ_BLOOD_part_4.png", &prt_textures.prt_blood[3], &vars->block); + load_texture_png("prts\\BrainZ_BLOOD_part_5.png", &prt_textures.prt_blood[4], &vars->block); + load_texture_png("prts\\BrainZ_BLOOD_part_6.png", &prt_textures.prt_blood[5], &vars->block); + + load_texture_png("prts\\BrainZ_BONE_part_1.png", &prt_textures.prt_bone[0], &vars->block); + load_texture_png("prts\\BrainZ_BONE_part_2.png", &prt_textures.prt_bone[1], &vars->block); + load_texture_png("prts\\BrainZ_BONE_part_3.png", &prt_textures.prt_bone[2], &vars->block); + load_texture_png("prts\\BrainZ_BONE_part_4.png", &prt_textures.prt_bone[3], &vars->block); + load_texture_png("prts\\BrainZ_BONE_part_5.png", &prt_textures.prt_bone[4], &vars->block); + load_texture_png("prts\\BrainZ_BONE_part_6.png", &prt_textures.prt_bone[5], &vars->block); + + load_texture_png("prts\\BrainZ_BRAIN_part_1.png", &prt_textures.prt_brain[0], &vars->block); + load_texture_png("prts\\BrainZ_BRAIN_part_2.png", &prt_textures.prt_brain[1], &vars->block); + load_texture_png("prts\\BrainZ_BRAIN_part_3.png", &prt_textures.prt_brain[2], &vars->block); + load_texture_png("prts\\BrainZ_BRAIN_part_4.png", &prt_textures.prt_brain[3], &vars->block); + + load_texture_png("prts\\BrainZ_HUMAN_part_1.png", &prt_textures.prt_human[0], &vars->block); + load_texture_png("prts\\BrainZ_HUMAN_part_2.png", &prt_textures.prt_human[1], &vars->block); + load_texture_png("prts\\BrainZ_HUMAN_part_3.png", &prt_textures.prt_human[2], &vars->block); + load_texture_png("prts\\BrainZ_HUMAN_part_4.png", &prt_textures.prt_human[3], &vars->block); + + load_texture_png("prts\\BrainZ_MUZZLE_part_1.png", &prt_textures.prt_muzzle[0], &vars->block); + load_texture_png("prts\\BrainZ_MUZZLE_part_2.png", &prt_textures.prt_muzzle[1], &vars->block); + + load_texture_png("prts\\BrainZ_TNT_part_1.png", &prt_textures.prt_tnt[0], &vars->block); + load_texture_png("prts\\BrainZ_TNT_part_2.png", &prt_textures.prt_tnt[1], &vars->block); + load_texture_png("prts\\BrainZ_TNT_part_3.png", &prt_textures.prt_tnt[2], &vars->block); + load_texture_png("prts\\BrainZ_TNT_part_4.png", &prt_textures.prt_tnt[3], &vars->block); + load_texture_png("prts\\BrainZ_TNT_part_5.png", &prt_textures.prt_tnt[4], &vars->block); + + load_texture_png("prts\\BrainZ_WALL_part_1.png", &prt_textures.prt_wall[0], &vars->block); + load_texture_png("prts\\BrainZ_WALL_part_2.png", &prt_textures.prt_wall[1], &vars->block); + load_texture_png("prts\\BrainZ_WALL_part_3.png", &prt_textures.prt_wall[2], &vars->block); + load_texture_png("prts\\BrainZ_WALL_part_4.png", &prt_textures.prt_wall[3], &vars->block); + load_texture_png("prts\\BrainZ_WALL_part_5.png", &prt_textures.prt_wall[4], &vars->block); + + load_texture_png("prts\\BrainZ_ZOMBIE_part_1.png", &prt_textures.prt_zombie[0], &vars->block); + load_texture_png("prts\\BrainZ_ZOMBIE_part_2.png", &prt_textures.prt_zombie[1], &vars->block); + load_texture_png("prts\\BrainZ_ZOMBIE_part_3.png", &prt_textures.prt_zombie[2], &vars->block); + load_texture_png("prts\\BrainZ_ZOMBIE_part_4.png", &prt_textures.prt_zombie[3], &vars->block); + + load_texture_png("prts\\BrainZ_BULLET_part_1.png", &prt_textures.prt_bullet[0], &vars->block); + + i32 prt_tex_count = 0; + i32 prt_type_count = 0; + +#define COUNT_PRTS(prt_type) prt_tex_count += ArrayCount(prt_textures.prt_type); ++prt_type_count; + LIST_PRT_TYPES(COUNT_PRTS); +#undef COUNT_PRTS + + vars->prt_type_count = prt_type_count; + vars->prt_textures = get_mem_array(Render_Texture, &vars->block, prt_tex_count); + vars->prt_types = get_mem_array(Particle_Type, &vars->block, prt_type_count); + + prt_tex_count = 0; + + i32 type_i = 0; +#define COUNT_PRTS(prt_type) vars->prt_types[type_i].first_tex_id = prt_tex_count; vars->prt_types[type_i].tex_count = ArrayCount(prt_textures.prt_type); prt_tex_count += ArrayCount(prt_textures.prt_type); ++type_i; + LIST_PRT_TYPES(COUNT_PRTS); +#undef COUNT_PRTS + + Temp_Memory prt_temp = begin_temp_memory(&vars->block); + Render_Texture **prt_texture_by_type = get_mem_array(Render_Texture*, &vars->block, prt_type_count); + + type_i = 0; +#define COUNT_PRTS(prt_type) prt_texture_by_type[type_i++] = prt_textures.prt_type + LIST_PRT_TYPES(COUNT_PRTS); +#undef COUNT_PRTS + + i32 prt_tex_i = 0; + for (i32 i = 0; i < prt_type_count; ++i){ + i32 count = vars->prt_types[i].tex_count; + for (i32 j = 0; j < count; ++j){ + vars->prt_textures[prt_tex_i++] = prt_texture_by_type[i][j]; + } + } + + end_temp_memory(prt_temp); + + type_i = 0; +#define COUNT_PRTS(t) vars->t##_index = type_i++ + LIST_PRT_TYPES(COUNT_PRTS); +#undef COUNT_PRTS + + //load_sound("audtest.wav", &vars->music.sound, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\AmmoFlip_1.wav", &vars->ammo_flip1, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\AmmoFlip_2.wav", &vars->ammo_flip2, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\AmmoLand.wav", &vars->ammo_land, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\Brains_1.wav", &vars->brains1, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\Brains_2.wav", &vars->brains2, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\Explosion.wav", &vars->explosion, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\GunShot.wav", &vars->gun_shot, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\PersonFlip_1.wav", &vars->person_flip1, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\PersonFlip_2.wav", &vars->person_flip2, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\PersonLand.wav", &vars->person_land, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\Reload.wav", &vars->reload, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\SoftFlip_1.wav", &vars->soft_flip1, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\SoftFlip_2.wav", &vars->soft_flip2, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\SoftLand_1.wav", &vars->soft_land1, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\SoftLand_2.wav", &vars->soft_land2, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\WallFlip_1.wav", &vars->wall_flip1, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\WallFlip_2.wav", &vars->wall_flip2, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\WallLand.wav", &vars->wall_land, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\ZombieBreak.wav", &vars->zombie_break, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\SplatDeath.wav", &vars->splat_death, target.audio_samples_per_second, &vars->block); + + load_sound("SFX\\Score_1.wav", &vars->score1, target.audio_samples_per_second, &vars->block); + load_sound("SFX\\Score_2.wav", &vars->score2, target.audio_samples_per_second, &vars->block); + + load_sound("music\\Menu.wav", &vars->menu_music, target.audio_samples_per_second, &vars->block); + load_sound("music\\Gameplay_1.wav", &vars->gameplay1, target.audio_samples_per_second, &vars->block); + load_sound("music\\Gameplay_2.wav", &vars->gameplay2, target.audio_samples_per_second, &vars->block); + + load_font("Dimbo Regular.ttf", &vars->font, 36, &vars->block); + load_font("Dimbo Regular.ttf", &vars->small_font, 32, &vars->block); + + game_set_to_new(vars); + +#if START_WITH_GAME_OVER + vars->game_over = 1; + vars->score = 1000000; +#endif +#if SKIP_TITLE_SCREEN + vars->title_screen = 0; +#endif + } + + // EVERY FRAME + gj_update(vars->gj); + + if (!vars->looked_up_teaser){ + vars->looked_up_teaser = 1; + + GJ_Score_Data score_data; + i32 score_count; + score_count = gj_get_scores(vars->gj, vars->table_id, + 1, &score_data); + + if (score_count == 1){ + vars->teaser_score_len = score_data.score_len; + vars->teaser_name_len = score_data.name_len; + + if (vars->teaser_score_len > ArrayCount(vars->teaser_score)){ + vars->teaser_score_len = ArrayCount(vars->teaser_score); + } + + if (vars->teaser_name_len > ArrayCount(vars->teaser_name)){ + vars->teaser_name_len = ArrayCount(vars->teaser_name); + } + + memcpy(vars->teaser_score, score_data.score, vars->teaser_score_len); + memcpy(vars->teaser_name, score_data.name, vars->teaser_name_len); + + gj_free_score(score_data); + } + else{ + persist char zero_string[] = "ZERO"; + persist char nobody_string[] = "NOBODY"; + + vars->teaser_score_len = ArrayCount(zero_string) - 1; + vars->teaser_name_len = ArrayCount(nobody_string) - 1; + + memcpy(vars->teaser_score, nobody_string, vars->teaser_score_len); + memcpy(vars->teaser_name, nobody_string, vars->teaser_name_len); + } + } + + if (vars->silence_timer > 0){ + --vars->silence_timer; + } + + if (vars->song_done){ + vars->music = {}; + vars->music.sound = vars->gameplay1; + vars->music.playing = 1; + vars->music.looping = 1; + vars->music.volume = 0.03f; + vars->music.bend = 1.f; + vars->song_done = 0; + } + + if (vars->title_screen > 0){ + // DO NOTHING + } + else if (vars->controls_screen){ + // DO NOTHING + } + else{ + i32 prev_score = vars->score; + if (!vars->game_over){ + + // TODO(allen): allow holding but slow it down + bool32 left = input.digital.left && !vars->prev_input.digital.left; + bool32 right = input.digital.right && !vars->prev_input.digital.right; + bool32 rot_left = input.button[1] && !vars->prev_input.button[1]; + bool32 rot_right = input.button[2] && !vars->prev_input.button[2]; + + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + + if (entity->active){ + if (left && !right){ + i32 left = entity->grid_x - 1; + if (can_move_to(vars, entity, left, entity->grid_y)){ + move_entity(vars, entity, left, entity->grid_y); + } + } + else if (right && !left){ + i32 right = entity->grid_x + 1; + if (can_move_to(vars, entity, right, entity->grid_y)){ + move_entity(vars, entity, right, entity->grid_y); + } + } + + bool32 did_rotation = 0; + if (rot_left && !rot_right){ + entity->facing += 1; + entity->facing %= 4; + did_rotation = 1; + } + else if (rot_right && !rot_left){ + entity->facing += 3; + entity->facing %= 4; + did_rotation = 1; + } + + if (did_rotation){ + entity->wobble = TAU32 / 10.f; + + Sound *snds[2]; + switch (entity->type){ + case AMMO: + { + snds[0] = &vars->ammo_flip1; + snds[1] = &vars->ammo_flip2; + }break; + + case HUMAN: + case ZOMBIE: + { + snds[0] = &vars->person_flip1; + snds[1] = &vars->person_flip2; + }break; + + case BRAIN: + { + snds[0] = &vars->soft_flip1; + snds[1] = &vars->soft_flip2; + }break; + + case WALL: + case BOMB: + { + snds[0] = &vars->wall_flip1; + snds[1] = &vars->wall_flip2; + }break; + } + + i32 which = (pcg32_random_r(&vars->rnd) % 2); + + play_sound_effect(vars, *snds[which], + 1.f, random_real32(vars, BEND_DOWN, BEND_UP)); + } + } + } + + if (!vars->chain_reacting){ + bool32 board_check = 0; + if (vars->need_to_fill_gaps){ + if (vars->fill_timer > 0){ + --vars->fill_timer; + } + else{ + // FILL DOWN + Temp_Memory temp = begin_temp_memory(&vars->block); + + Entity_Y_Data *data = get_mem_array(Entity_Y_Data, &vars->block, vars->entity_count); + for (i32 i = 0; i < vars->entity_count; ++i){ + data[i].index = i; + data[i].y = vars->entities[i].grid_y; + } + + quick_sort(data, 0, vars->entity_count-1); + + vars->need_to_fill_gaps = 0; + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + data[i].index; + + if (can_move_to(vars, entity, entity->grid_x, entity->grid_y + 1)){ + move_entity(vars, entity, entity->grid_x, entity->grid_y + 1); + vars->need_to_fill_gaps = 1; + } + } + + if (!vars->need_to_fill_gaps){ + board_check = 1; + } + else{ + vars->fill_timer = COMBO_TIME; + } + + end_temp_memory(temp); + } + } + else if (!vars->need_new_block){ + // FALL TICK + if (input.digital.down){ + if (vars->fall_timer > 4){ + vars->fall_timer = 4; + } + } + + bool32 land_now = 0; + --vars->fall_timer; + + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + + if (entity->active){ + if (vars->fall_timer <= 0){ + vars->fall_timer += get_level_fall_tick_time(vars, vars->level); + + if (can_move_to(vars, entity, entity->grid_x, entity->grid_y + 1)){ + move_entity(vars, entity, entity->grid_x, entity->grid_y + 1); + } + else{ + land_now = 1; + } + } + else{ + if (!can_move_to(vars, entity, entity->grid_x, entity->grid_y + 1) && + entity->show_x == entity->grid_x && entity->show_y == entity->grid_y){ + land_now = 1; + } + } + + if (land_now){ + Sound *snd = 0; + switch (entity->type){ + case AMMO: + { + snd = &vars->ammo_land; + }break; + + case HUMAN: + case ZOMBIE: + { + snd = &vars->person_land; + }break; + + case BRAIN: + { + if ((pcg32_random_r(&vars->rnd) % 2) == 0){ + snd = &vars->soft_land1; + } + else{ + snd = &vars->soft_land2; + } + }break; + + case WALL: + { + if ((pcg32_random_r(&vars->rnd) % 2) == 0){ + snd = &vars->wall_land; + } + else{ + snd = &vars->wall_flip1; + } + }break; + + case BOMB: + { + snd = &vars->person_flip2; + }break; + } + + if (snd){ + play_sound_effect(vars, *snd, + 1.f, random_real32(vars, BEND_DOWN, BEND_UP)); + } + + entity->active = 0; + entity->wobble = 0; + if (entity->grid_y == 0){ + vars->game_over = 1; + } + vars->need_new_block = 1; + board_check = 1; + } + } + } + } + + // SPREAD INFECTION + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + if (entity->type == HUMAN){ + Grid_Pos center = get_grid_pos(entity); + + i32 infection_counter = 0; + for (i32 i = 0; i < ArrayCount(von_neumann); ++i){ + Grid_Pos pos = center + von_neumann[i]; + Entity *neighbor = get_grid_entity(vars, pos.x, pos.y); + if (neighbor){ + if (neighbor->type == ZOMBIE){ + ++infection_counter; + } + } + } + + if (infection_counter == 0){ + if (entity->infection_amount > 0){ + entity->infection_amount -= 1; + } + } + else{ + entity->infection_amount += infection_counter; + } + + if (entity->infection_amount > ZOMBIE_TURN_THRESHOLD){ + entity->type = ZOMBIE; + entity->facing += 3; + entity->facing %= 4; + board_check = 1; + } + } + } + + // BOARD CHECK + if (board_check){ + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + + switch (entity->type){ + case AMMO: + { + bool32 ammo_used = 0; + Grid_Pos center = get_grid_pos(entity); + for (i32 i = 0; i < ArrayCount(von_neumann); ++i){ + Grid_Pos pos = center + von_neumann[i]; + + Entity *neighbor = get_grid_entity(vars, pos.x, pos.y); + if (neighbor){ + if (neighbor->type == HUMAN){ + ammo_used = 1; + neighbor->firing = 1; + } + } + } + + if (ammo_used){ + play_sound_effect(vars, vars->reload, + 0.5f, random_real32(vars, BEND_DOWN, BEND_UP)); + mark_block_destroyed(vars, entity); + } + }break; + + case BRAIN: + { + bool32 brain_eaten = 0; + Grid_Pos center = get_grid_pos(entity); + for (i32 i = 0; i < ArrayCount(von_neumann); ++i){ + Grid_Pos pos = center + von_neumann[i]; + + Entity *neighbor = get_grid_entity(vars, pos.x, pos.y); + if (neighbor){ + if (neighbor->type == ZOMBIE){ + brain_eaten = 1; + neighbor->firing = 1; + } + } + } + + if (brain_eaten){ + mark_block_destroyed(vars, entity); + } + }break; + } + } + } + } + + // RANDOM BRAINS + bool32 has_zombie = 0; + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + + if (entity->type == ZOMBIE){ + has_zombie = 1; + break; + } + } + + if (has_zombie && (pcg32_random_r(&vars->rnd) % 600) == 0){ + Sound *snd; + if ((pcg32_random_r(&vars->rnd) % 2) == 0){ + snd = &vars->brains1; + } + else{ + snd = &vars->brains2; + } + + play_sound_effect(vars, *snd, + 1.f, random_real32(vars, BEND_DOWN, BEND_UP)); + } + + // FIRE + if (vars->reaction_timer > 0){ + --vars->reaction_timer; + } + else{ + vars->chain_reacting = 0; + Temp_Memory gun_temp = begin_temp_memory(&vars->block); + i32 reaction_fire_count = 0; + i32 reaction_fire_max = WIDTH*HEIGHT; + Entity **reaction_fire = get_mem_array(Entity*, &vars->block, reaction_fire_max); + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + Assert(!(entity->firing && entity->step_forward)); + if (entity->firing){ + vars->chain_reacting = 1; + vars->reaction_timer = COMBO_TIME; + entity->firing = 0; + if (entity->type == HUMAN || entity->type == AMMO){ + i32 step_count = ArrayCount(up_shot); + Grid_Pos *shot_pos = right_shot; + switch (entity->facing){ + case UP: + shot_pos = up_shot; + break; + + case LEFT: + shot_pos = left_shot; + break; + + case DOWN: + shot_pos = down_shot; + break; + } + + Grid_Pos center = get_grid_pos(entity); + for (i32 i = 0; i < step_count; ++i){ + Grid_Pos pos = center + shot_pos[i]; + + bool32 out_of_grid; + Entity *hit_entity = get_grid_entity(vars, pos.x, pos.y, &out_of_grid); + if (out_of_grid){ + break; + } + if (hit_entity){ + if (hit_entity->type == WALL){ + break; + } + else{ + switch (hit_entity->type){ + case HUMAN: case ZOMBIE: case BRAIN: + { + mark_block_destroyed(vars, hit_entity); + }break; + + case AMMO: case BOMB: + { + Assert(reaction_fire_count < reaction_fire_max); + reaction_fire[reaction_fire_count++] = hit_entity; + }break; + } + } + } + } + + play_sound_effect(vars, vars->gun_shot, + 0.5f, random_real32(vars, BEND_DOWN, BEND_UP)); + + if (entity->type == AMMO){ + mark_block_destroyed(vars, entity); + } + if (entity->type == HUMAN){ + real32 rotation = 0.f; + Vec2 pos = get_screen_pos(entity); + persist real32 MUZZLE_HALF_WIDTH = 70.f; + + switch (entity->facing){ + case RIGHT: + rotation = 0.f; + pos.x += 43 + MUZZLE_HALF_WIDTH; + pos.y += 36; + break; + + case UP: + rotation = 270.f; + pos.x += 36; + pos.y += -43 - MUZZLE_HALF_WIDTH; + break; + + case LEFT: + rotation = 180.f; + pos.x += -43 - MUZZLE_HALF_WIDTH; + pos.y += -36; + break; + + case DOWN: + rotation = 90.f; + pos.x += -36; + pos.y += 43 + MUZZLE_HALF_WIDTH; + break; + } + + spawn_particles_type_3(vars, vars->prt_muzzle_index, pos, rotation); + } + } + + else if (entity->type == BOMB){ + Grid_Pos center = get_grid_pos(entity); + for (i32 i = 0; i < ArrayCount(moore); ++i){ + Grid_Pos pos = center + moore[i]; + + Entity *hit_entity = get_grid_entity(vars, pos.x, pos.y); + if (hit_entity){ + switch (hit_entity->type){ + case HUMAN: case ZOMBIE: case BRAIN: case WALL: + { + mark_block_destroyed(vars, hit_entity); + }break; + + case AMMO: case BOMB: + { + Assert(reaction_fire_count < reaction_fire_max); + reaction_fire[reaction_fire_count++] = hit_entity; + }break; + } + } + + play_sound_effect(vars, vars->explosion, + 0.03f, random_real32(vars, BEND_DOWN, BEND_UP)); + } + mark_block_destroyed(vars, entity); + } + + else if (entity->type == ZOMBIE){ + Grid_Pos pos = get_grid_pos(entity); + switch (entity->facing){ + case RIGHT: + pos.x += 1; + break; + + case UP: + pos.y -= 1; + break; + + case LEFT: + pos.x -= 1; + break; + + case DOWN: + pos.y += 1; + break; + } + + bool32 out_of_grid; + Entity *target = get_grid_entity(vars, pos.x, pos.y, &out_of_grid); + if (!out_of_grid){ + if (target){ + switch (target->type){ + case BOMB: case AMMO: + { + Assert(reaction_fire_count < reaction_fire_max); + reaction_fire[reaction_fire_count++] = target; + }break; + + default: + { + mark_block_destroyed(vars, target); + }break; + } + } + entity->step_forward = 3; + play_sound_effect(vars, vars->zombie_break, + 0.1f, random_real32(vars, BEND_DOWN, BEND_UP)); + } + } + + else{ + Assert(1); + } + } + } + + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + Assert(!(entity->firing && entity->step_forward)); + if (entity->step_forward){ + --entity->step_forward; + vars->chain_reacting = 1; + vars->reaction_timer = COMBO_TIME; + Assert(entity->type == ZOMBIE); + + Grid_Pos pos = get_grid_pos(entity); + switch (entity->facing){ + case RIGHT: + pos.x += 1; + break; + + case UP: + pos.y -= 1; + break; + + case LEFT: + pos.x -= 1; + break; + + case DOWN: + pos.y += 1; + break; + } + + bool32 out_of_grid; + if (can_move_to(vars, entity, pos.x, pos.y, &out_of_grid)){ + move_entity(vars, entity, pos.x, pos.y); + entity->step_forward = 0; + + Sound *snd; + if ((pcg32_random_r(&vars->rnd) % 2) == 0){ + snd = &vars->brains1; + } + else{ + snd = &vars->brains2; + } + + play_sound_effect(vars, *snd, + 1.f, random_real32(vars, BEND_DOWN, BEND_UP)); + } + if (out_of_grid){ + entity->step_forward = 0; + } + } + } + + for (i32 i = 0; i < reaction_fire_count; ++i){ + reaction_fire[i]->firing = 1; + } + + end_temp_memory(gun_temp); + } + + // TRY TO SPAWN BLOCK + if (vars->need_new_block && !vars->need_to_fill_gaps && !vars->chain_reacting){ + make_random_block(vars); + vars->need_new_block = 0; + } + + // DESTROY OBJECTS + Assert(vars->free_count <= vars->entity_count); + quick_sort((void**)vars->to_free, 0, vars->free_count-1); + for (i32 i = 0; i < vars->free_count; ++i){ + Entity *entity = vars->to_free[i]; + + if (entity->active){ + vars->need_new_block = 1; + } + + i32 entity_index = get_entity_index(vars, entity); + + bool32 play_splat_death = 0; + + switch (entity->type){ + case HUMAN: + { + spawn_particles_type_1(vars, vars->prt_human_index, get_screen_pos(entity), 8); + spawn_particles_type_1(vars, vars->prt_bone_index, get_screen_pos(entity), 6); + spawn_particles_type_1(vars, vars->prt_blood_index, get_screen_pos(entity), 15); + spawn_particles_type_1(vars, vars->prt_brain_index, get_screen_pos(entity), 2); + + play_splat_death = 1; + }break; + + case ZOMBIE: + { + spawn_particles_type_1(vars, vars->prt_zombie_index, get_screen_pos(entity), 8); + spawn_particles_type_1(vars, vars->prt_bone_index, get_screen_pos(entity), 6); + spawn_particles_type_1(vars, vars->prt_blood_index, get_screen_pos(entity), 15); + spawn_particles_type_1(vars, vars->prt_brain_index, get_screen_pos(entity), 2); + + play_splat_death = 1; + }break; + + case AMMO: + { + spawn_particles_type_2(vars, vars->prt_ammo_index, get_screen_pos(entity), 12); + }break; + + case BRAIN: + { + spawn_particles_type_1(vars, vars->prt_brain_index, get_screen_pos(entity), 6); + spawn_particles_type_1(vars, vars->prt_blood_index, get_screen_pos(entity), 15); + + play_splat_death = 1; + }break; + + case WALL: + { + spawn_particles_type_1(vars, vars->prt_wall_index, get_screen_pos(entity), 12); + }break; + + case BOMB: + { + spawn_particles_type_1(vars, vars->prt_tnt_index, get_screen_pos(entity), 12); + }break; + } + + if (play_splat_death){ + play_sound_effect(vars, vars->splat_death, + 1.f, random_real32(vars, BEND_DOWN, BEND_UP)); + } + + vars->grid[get_i(entity->grid_x, entity->grid_y)] = 0; + --vars->entity_count; + if (vars->entity_count > entity_index){ + Entity *end_entity = vars->entities + vars->entity_count; + vars->grid[get_i(end_entity->grid_x, end_entity->grid_y)] = entity; + *entity = *end_entity; + } + } + vars->free_count = 0; + + // SPAWN OBJECTS + for (i32 i = 0; i < vars->spawn_count; ++i){ + Spawn_Request spawn = vars->spawns[i]; + + i32 x, y; + Block_Type type; + i32 facing; + if (spawn.random){ + x = pcg32_random_r(&vars->rnd) % WIDTH; + y = 0; + +#if TEST_ORDER == 0 + //type = (Block_Type)(ZOMBIE + pcg32_random_r(&vars->rnd) % type_count); + type = block_freq_table[pcg32_random_r(&vars->rnd) % ArrayCount(block_freq_table)]; +#else + Block_Type test_types[] = { + AMMO, + HUMAN + }; + + type = test_types[vars->test_type_i++]; + vars->test_type_i = vars->test_type_i % ArrayCount(test_types); +#endif + facing = (i32)(pcg32_random_r(&vars->rnd) % 4); + } + else{ + x = spawn.x; + y = spawn.y; + type = spawn.type; + facing = (i32)(pcg32_random_r(&vars->rnd) % 4); + } + + if (vars->grid[get_i(x, y)] == 0){ + Entity *entity = spawn_entity(vars); + entity->type = type; + entity->grid_x = x; + entity->grid_y = y; + entity->show_x = (real32)x; + entity->show_y = (real32)y; + entity->active = 1; + entity->facing = facing; + + vars->grid[get_i(x, y)] = entity; + } + else{ + vars->game_over = 1; + } + } + + vars->spawn_count = 0; + } + + // CHECK EFFECTS FROM POINTS + i32 next_level = vars->level + 1; + if (vars->score >= get_level_min_score(vars, next_level)){ + vars->level = next_level; + } + + i32 score_increase = vars->score - prev_score; + + if (score_increase > 0){ + Sound *snd; + if (score_increase >= 100){ + snd = &vars->score1; + } + else{ + snd = &vars->score2; + } + play_sound_effect(vars, *snd, + 1.1f, random_real32(vars, BEND_DOWN, BEND_UP), + 15); + } + + } + + // RENDERING + Vec2 screen_center; + screen_center.x = (real32)target.render.width * 0.5f; + screen_center.y = (real32)target.render.height * 0.5f; + + if (vars->title_screen > 0){ + Vec4 black; + black = V4(0.f, 0.f, 0.f, 1.f); + draw_clear(black); + + + persist i32 PHASE_0 = 120; + persist i32 PHASE_1 = 150; + persist i32 PHASE_2 = 210; + persist i32 PHASE_3 = 330; + persist i32 PHASE_4 = 400; + + if (vars->title_screen < PHASE_4){ + ++vars->title_screen; + } + + if (vars->title_screen < PHASE_0){ + real32 a = (vars->title_screen / (real32)(PHASE_0)); + + Vec4 color = V4(1.f, 1.f, 1.f, a); + draw_texture(vars->overreact, screen_center, screen_center, 0.f, color); + } + else if (vars->title_screen < PHASE_1){ + Vec4 color = V4(1.f, 1.f, 1.f, 1.f); + draw_texture(vars->overreact, screen_center, screen_center, 0.f, color); + } + else if (vars->title_screen < PHASE_2){ + real32 a = ((PHASE_2 - vars->title_screen) / (real32)(PHASE_2 - PHASE_1)); + + Vec4 color = V4(1.f, 1.f, 1.f, a); + draw_texture(vars->overreact, screen_center, screen_center, 0.f, color); + } + else{ + real32 a = 1.f; + if (vars->title_screen < PHASE_3){ + a = ((vars->title_screen - PHASE_2) / (real32)(PHASE_3 - PHASE_2)); + } + + Vec4 color = V4(1.f, 1.f, 1.f, a); + draw_texture(vars->title, screen_center, screen_center, 0.f, color); + + if (vars->title_screen >= PHASE_3){ + a = 1.f; + if (vars->title_screen < PHASE_4){ + a = ((vars->title_screen - PHASE_3) / (real32)(PHASE_4 - PHASE_3)); + } + + if (input.button[0]){ + vars->title_screen = 0; + vars->controls_screen = 1; + + vars->music = {}; + vars->music.sound = vars->gameplay2; + vars->music.playing = 1; + vars->music.volume = 0.03f; + vars->music.bend = 1.f; + } + + color = V4(1.f, 1.f, 1.f, a); + draw_texture(vars->title_button, V2(400.f, 420.f), 0.f, color); + } + } + } + else{ + draw_texture(vars->background, screen_center, screen_center); + + draw_texture(vars->scoreback, V2(110.f, 80.f)); + draw_texture(vars->scorename, V2(110.f, 50.f)); + + char score_string[16]; + char post_string[16]; + persist char mil_string[] = "MILLION"; + + i32 i, j; + i = int_to_string(score_string, vars->score); + j = int_to_string(post_string, vars->nonsense_score, 3); + + real32 text_x, text_y, start_x, end_x; + text_x = 20.f; + text_y = 100.f; + start_x = 20.f; + end_x = 180.f; + + Vec4 white = {1.f, 1.f, 1.f, 1.f}; + AllowLocal(white); + Vec4 red = {1.f, 0.f, 0.f, 1.f}; + + draw_text(&target.render, &vars->small_font, &text_x, &text_y, score_string + i, 16 - i, red, start_x, end_x); + if (vars->score > 0){ + draw_text(&target.render, &vars->small_font, &text_x, &text_y, post_string + j, 16 - j, red, start_x, end_x); + text_x = start_x; + text_y += vars->small_font.height; + draw_text(&target.render, &vars->small_font, &text_x, &text_y, mil_string, ArrayCount(mil_string) - 1, red, start_x, end_x); + } + + text_x = 20.f; + text_y = 300.f; + start_x = 20.f; + end_x = 180.f; + + persist char level_label[] = "LEVEL: "; + draw_text(&target.render, &vars->small_font, &text_x, &text_y, level_label, ArrayCount(level_label) - 1, red, start_x, end_x); + + char level_string[16]; + i = int_to_string(level_string, vars->level + 1); + draw_text(&target.render, &vars->small_font, &text_x, &text_y, level_string + i, 16 - i, red, start_x, end_x); + + text_x = 20.f; + text_y = 475.f; + start_x = 20.f; + end_x = 180.f; + + persist char best_label[] = "THE MOST SCORE"; + draw_text(&target.render, &vars->small_font, &text_x, &text_y, best_label, ArrayCount(best_label) - 1, red, start_x, end_x); + text_x = start_x; + text_y += vars->small_font.height; + draw_text(&target.render, &vars->small_font, &text_x, &text_y, vars->teaser_score, vars->teaser_score_len, red, start_x, end_x); + text_x = start_x; + text_y += vars->small_font.height; + draw_text(&target.render, &vars->small_font, &text_x, &text_y, vars->teaser_name, vars->teaser_name_len, red, start_x, end_x); + + for (i32 i = 0; i < vars->entity_count; ++i){ + Entity *entity = vars->entities + i; + + persist real32 BLOCK_LERP_SPEED = 0.5f; + + if (entity->show_x != entity->grid_x){ + entity->show_x = lerp(entity->show_x, BLOCK_LERP_SPEED, (real32)entity->grid_x); + if (abs(entity->show_x - entity->grid_x) < 0.05){ + entity->show_x = (real32)(entity->grid_x); + } + } + + if (entity->show_y != entity->grid_y){ + entity->show_y = lerp(entity->show_y, BLOCK_LERP_SPEED, (real32)entity->grid_y); + if (abs(entity->show_y - entity->grid_y) < 0.05){ + entity->show_y = (real32)(entity->grid_y); + } + } + + real32 x, y; + x = entity->show_x; + y = entity->show_y; + + Render_Texture *texture = 0; + switch (entity->type){ + case ZOMBIE: + texture = &vars->zombie[0]; + break; + + case HUMAN: + { + i32 infection_level = (entity->infection_amount * 4) / ZOMBIE_TURN_THRESHOLD; + texture = &vars->human[0][infection_level]; + }break; + + case BRAIN: + texture = &vars->brain; + break; + + case AMMO: + texture = &vars->ammo; + break; + + case BOMB: + texture = &vars->bomb; + break; + + case WALL: + texture = &vars->wall; + break; + } + + Vec2 center = get_screen_pos(entity); + + persist real32 SCALE_DOWN = 0.3168316f; + + Vec2 halfdim; + halfdim.x = texture->img_width * .5f * SCALE_DOWN; + halfdim.y = texture->img_height * .5f * SCALE_DOWN; + + real32 rotation = 0.f; + if (entity->type == ZOMBIE){ + switch (entity->facing){ + case RIGHT: + rotation = 0.f; + break; + + case UP: + rotation = 270.f; + break; + + case LEFT: + rotation = 180.f; + break; + + case DOWN: + rotation = 90.f; + break; + } + } + + else{ + switch (entity->facing){ + case RIGHT: + rotation = 90.f; + break; + + case UP: + rotation = 0.f; + break; + + case LEFT: + rotation = 270.f; + break; + + case DOWN: + rotation = 180.f; + break; + } + } + + if (entity->active){ + rotation += 10.f*sinf(entity->wobble); + if (entity->wobble > 0.f && entity->wobble < TAU32){ + entity->wobble += TAU32 / 5.f; + } + else{ + entity->wobble = 0; + } + } + + if (entity->firing){ + switch (entity->type){ + case BOMB: + { + halfdim *= 1.1f; + rotation += 10.f*sinf(entity->wobble); + entity->wobble += TAU32 / 5.f; + + draw_texture(*texture, center, halfdim, rotation); + }break; + + case AMMO: + { + Vec4 color = V4(1.f, 1.f, 1.f, 1.f); + color.g = abs(sinf(entity->wobble))*0.5f + 0.5f; + color.b = color.g; + entity->wobble += TAU32 / 20.f; + + draw_texture(*texture, center, halfdim, rotation, color); + }break; + + default: + { + draw_texture(*texture, center, halfdim, rotation); + }break; + } + } + else if (entity->step_forward){ + halfdim *= (1.f + sinf(entity->wobble)*.15f + .15f); + entity->wobble += TAU32 / 25.f; + + draw_texture(*texture, center, halfdim, rotation); + } + else{ + draw_texture(*texture, center, halfdim, rotation); + } + } + + // PARTICLE RENDER + Particle *part = vars->particles; + Particle *end_part = vars->particles + vars->particle_count; + Render_Texture *part_texs = vars->prt_textures; + for (; part < end_part;){ + if ((--part->life_counter) > 0){ + part->pos += part->vel; + part->rot += part->rot_vel; + part->vel.y += 0.5f; + + if (part->pos.y < 700.f){ + draw_texture(part_texs[part->tex_index], part->pos, part->rot); + ++part; + } + else{ + --end_part; + *part = *end_part; + } + } + else{ + --end_part; + *part = *end_part; + } + } + vars->particle_count = (i32)(end_part - vars->particles); + Assert(vars->particle_count >= 0); + + draw_texture(vars->shadow, screen_center, screen_center, 0.f, V4(1.f, 1.f, 1.f, 0.4f)); + + // CONTROLS SCREEN RENDER + if (vars->controls_screen){ + draw_rectangle(0.f, 0.f, screen_center.x*2, screen_center.y*2, + V4(0.f, 0.f, 0.f, 0.5f)); + draw_texture(vars->controls, screen_center, screen_center); + + i32 field_index = 0; + if (do_button(field_index++, vars->active_field, + input, vars->finish_button, + 690.f, 500.f)){ + vars->controls_screen = 0; + } + + if (input.digital.up && !vars->prev_input.digital.up){ + vars->active_field += (field_index - 1); + vars->active_field %= field_index; + } + + if (input.digital.down && !vars->prev_input.digital.down){ + vars->active_field += 1; + vars->active_field %= field_index; + } + } + + // GAME OVER RENDER + else if (vars->game_over){ + draw_rectangle(0.f, 0.f, screen_center.x*2, screen_center.y*2, + V4(0.f, 0.f, 0.f, 0.5f)); + draw_texture(vars->gameover, screen_center, screen_center); + + i32 field_index = 0; + bool32 blink_on = (vars->blink_timer < 15); + vars->blink_timer = (vars->blink_timer + 1) % 30; + + bool32 move_down = 0; + + if (do_text_field(field_index++, vars->active_field, blink_on, + &target.render, input, + &vars->font, 305.f, 285.f, + &vars->user_name_len, vars->user_name, 15)){ + move_down = 1; + } + + if (do_text_field(field_index++, vars->active_field, blink_on, + &target.render, input, + &vars->font, 305.f, 342.f, + &vars->user_token_len, vars->user_token, 15)){ + move_down = 1; + } + + if (do_button(field_index++, vars->active_field, + input, vars->finish_button, + 690.f, 500.f)){ + if (vars->user_name_len != 0){ + vars->user_name[vars->user_name_len] = 0; + vars->user_token[vars->user_token_len] = 0; + + char score_string[64]; + i32 i = 64, j; + if (vars->score > 0){ + score_string[--i] = 0; + persist char short_mil_string[] = " MIL"; + for (i32 k = ArrayCount(short_mil_string) - 2; k >= 0; --k){ + score_string[--i] = short_mil_string[k]; + } + + j = int_to_string(score_string + i - 16, vars->nonsense_score, 3); + j = (16 - j); + i -= j; + + j = int_to_string(score_string + i - 16, vars->score); + j = (16 - j); + i -= j; + } + else{ + persist char zero_string[] = "ZERO"; + score_string[--i] = 0; + for (i32 j = ArrayCount(zero_string) - 2; j >= 0; --j){ + score_string[--i] = zero_string[j]; + } + } + + bool32 guest_score = 0; + if (vars->user_token_len != 0){ + if (gj_login(vars->gj, vars->user_name, vars->user_token)){ + gj_post_score(vars->gj, vars->table_id, score_string + i, vars->score, "", ""); + gj_logout(vars->gj); + } + else{ + guest_score = 1; + } + } + else{ + guest_score = 1; + } + + if (guest_score){ + gj_post_score(vars->gj, vars->table_id, score_string + i, vars->score, "", vars->user_name); + } + } + + game_set_to_new(vars); + } + + if (input.digital.up && !vars->prev_input.digital.up){ + vars->active_field += (field_index - 1); + vars->active_field %= field_index; + } + + if ((input.digital.down && !vars->prev_input.digital.down) || move_down){ + vars->active_field += 1; + vars->active_field %= field_index; + } + } + } + + vars->prev_input = input; + + return 0; +} + +// BOTTOM diff --git a/test_data/lots_of_files/jam.h b/test_data/lots_of_files/jam.h new file mode 100644 index 0000000..bdd0771 --- /dev/null +++ b/test_data/lots_of_files/jam.h @@ -0,0 +1,61 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Game to Platform Layer. + */ + +struct Game_Memory{ + void *data; + i32 size; +}; + +struct Game_Step_Target{ + Game_Render_Target render; + + bool32 can_fullscreen; + bool32 *is_fullscreen; + + real32 second_per_frame; + i32 audio_samples_per_second; + + char btn1, btn2; +}; + +struct Game_Input{ + struct{ + real32 stick_x, stick_y; + } analog; + struct{ + bool8 up, down, left, right; + } digital; + bool8 button[8]; + bool8 pause; + bool8 back; + + bool8 is_analog; + + bool8 toggle_edit_mode; + + bool8 key_input; + char key_code; +}; + +struct Game_Audio_Target{ + i16 *samples; + i32 count_per_channel; + i32 channels; + i32 samples_per_second; + real32 second_per_frame; +}; + +#define SIG_APP_FILL_SAMPLES(name) void(name)(Game_Audio_Target target, Game_Memory memory) +#define SIG_APP_STEP(name) bool32(name)(Game_Step_Target target, Game_Input input, Game_Memory memory, bool32 first) + +internal +SIG_APP_FILL_SAMPLES(game_fill_samples); + +internal +SIG_APP_STEP(game_step); + diff --git a/test_data/lots_of_files/jam_audio.cpp b/test_data/lots_of_files/jam_audio.cpp new file mode 100644 index 0000000..5730b84 --- /dev/null +++ b/test_data/lots_of_files/jam_audio.cpp @@ -0,0 +1,124 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Audio Layer. + */ + +internal void +wav_open_file(char *filename, Wav_File *wav_file){ + File file = system_load_file(filename); + if (file.data && file.size > sizeof(Wav_File)){ + *wav_file = {}; + wav_file->file = file; + wav_file->header = *(Wav_Header*)file.data; + + Assert(wav_file->header.chunk.id == WAV_CHUNK_RIFF); + Assert(wav_file->header.wav_id == WAV_CHUNK_WAVE); + } +} + +internal i32 +wav_data_requirement(Wav_File *wav_file){ + u8 *cursor = (u8*)wav_file->file.data + sizeof(Wav_Header); + u64 size = wav_file->file.size; + u8 *end = (u8*)wav_file->file.data + size; + + i32 requirement = 0; + i32 channels = 0; + + while (1){ + Wav_Chunk chunk; + chunk = *(Wav_Chunk*)cursor; + + if (chunk.id == WAV_CHUNK_data){ + Assert(requirement == 0); + requirement = chunk.size; + cursor += sizeof(Wav_Chunk) + chunk.size; + if (chunk.size % 2 == 1){ + ++cursor; + } + } + else{ + if (chunk.id == WAV_CHUNK_fmt){ + Wav_Format_Chunk_16 fmt; + fmt = *(Wav_Format_Chunk_16*)(cursor); + channels = fmt.channels; + } + cursor += sizeof(Wav_Chunk) + chunk.size; + } + + if (cursor >= end){ + break; + } + } + + return requirement*(2/channels) + 4; +} + +internal bool32 +wav_fill_sound(Wav_File *wav_file, Sound *sound, + i32 system_sample_per_second){ + u8 *cursor = (u8*)wav_file->file.data + sizeof(Wav_Header); + u64 size = wav_file->file.size; + u8 *end = (u8*)wav_file->file.data + size; + + bool32 found_data = 0; + + while (1){ + Wav_Chunk chunk; + chunk = *(Wav_Chunk*)cursor; + + if (chunk.id == WAV_CHUNK_data){ + Assert(found_data == 0); + found_data = 1; + sound->memory_size = chunk.size*(2/sound->channels); + sound->sample_count = (chunk.size/sound->channels)/sound->bytes_per_sample; + + i16 *sample_src = (i16*)(cursor + sizeof(Wav_Chunk)); + i16 *sample_dest = sound->samples; + if (sound->channels == 1){ + for (i32 i = 0; i < sound->sample_count; ++i){ + *sample_dest++ = *sample_src; + *sample_dest++ = *sample_src++; + } + *sample_dest++ = 0; + *sample_dest++ = 0; + } + else{ + for (i32 i = 0; i < sound->sample_count; ++i){ + *sample_dest++ = *sample_src++; + *sample_dest++ = *sample_src++; + } + *sample_dest++ = 0; + *sample_dest++ = 0; + } + cursor += sizeof(Wav_Chunk) + chunk.size; + if (chunk.size % 2 == 1){ + ++cursor; + } + } + + else{ + if (chunk.id == WAV_CHUNK_fmt){ + Wav_Format_Chunk_16 fmt; + fmt = *(Wav_Format_Chunk_16*)(cursor); + + Assert(fmt.channels == 1 || fmt.channels == 2); + + sound->channels = fmt.channels; + sound->samples_per_second = fmt.samples_per_second; + sound->bytes_per_sample = fmt.bits_per_sample >> 3; + sound->scan_speed = ((real32)sound->samples_per_second/system_sample_per_second); + } + cursor += sizeof(Wav_Chunk) + chunk.size; + } + + if (cursor >= end){ + break; + } + } + + return found_data; +} diff --git a/test_data/lots_of_files/jam_audio.h b/test_data/lots_of_files/jam_audio.h new file mode 100644 index 0000000..afe4476 --- /dev/null +++ b/test_data/lots_of_files/jam_audio.h @@ -0,0 +1,76 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Audio Layer. + */ + +struct Sound{ + i16 *samples; + i32 channels; + i32 samples_per_second; + i32 bytes_per_sample; + i32 memory_size; + i32 sample_count; + real32 scan_speed; +}; + +#pragma pack(push, 1) +struct Wav_Chunk{ + i32 id; + i32 size; +}; + +struct Wav_Header{ + Wav_Chunk chunk; + i32 wav_id; +}; + +struct Wav_Format_Chunk_16{ + Wav_Chunk chunk; + i16 format_code; + i16 channels; + i32 samples_per_second; + i32 avg_bytes_per_second; + i16 block_align; + i16 bits_per_sample; +}; + +struct Wav_Format_Chunk_18{ + Wav_Format_Chunk_16 s; + i16 extension_size; +}; + +struct Wav_Format_Chunk_40{ + Wav_Format_Chunk_16 s; + i16 extension_size; + i16 valid_bits_per_sample; + i32 channel_mask; + u8 guid[16]; +}; + +struct Wav_Fact_Chunk{ + Wav_Chunk chunk; + i32 samples_per_channel; +}; + +struct Wav_Data_Chunk{ + Wav_Chunk chunk; +}; + +#define WAV_CHUNK_RIFF 0x46464952 +#define WAV_CHUNK_WAVE 0x45564157 +#define WAV_CHUNK_fmt 0x20746D66 +#define WAV_CHUNK_fact 0x74666163 +#define WAV_CHUNK_data 0x61746164 + +#define WAV_FORMAT_PCM 0x0001 + +#pragma pack(pop) + +struct Wav_File{ + File file; + Wav_Header header; +}; + diff --git a/test_data/lots_of_files/jam_game.h b/test_data/lots_of_files/jam_game.h new file mode 100644 index 0000000..e08b07a --- /dev/null +++ b/test_data/lots_of_files/jam_game.h @@ -0,0 +1,295 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + */ + +struct Memory_Block{ + void *base; + i32 size, cursor; +}; + +struct Track{ + Sound sound; + Track *next_free; + i32 sample_pos; + real32 sample_pos_dec; + real32 volume; + real32 bend; + bool32 playing; + bool32 looping; + i32 frames_delay; +}; + +enum Block_Type{ + ERROR, + ZOMBIE, + HUMAN, + BRAIN, + AMMO, + BOMB, + WALL, + BLOCK_TYPE_COUNT +}; + +#define RIGHT 0 +#define UP 1 +#define LEFT 2 +#define DOWN 3 + +#define ZOMBIE_TURN_THRESHOLD 360 + +Block_Type block_freq_table[] = { + ZOMBIE, + ZOMBIE, + ZOMBIE, + ZOMBIE, + HUMAN, + HUMAN, + HUMAN, + HUMAN, + HUMAN, + HUMAN, + BRAIN, + BRAIN, + BRAIN, + AMMO, + AMMO, + AMMO, + AMMO, + AMMO, + BOMB, + BOMB, + BOMB, + WALL, + WALL, + WALL, + WALL, + WALL +}; + +struct Entity{ + Block_Type type; + i32 grid_x, grid_y; + real32 show_x, show_y; + bool32 active; + i32 facing; + bool32 firing; + i32 step_forward; + bool32 death_marked; + i32 infection_amount; + real32 wobble; +}; + +struct Spawn_Request{ + Block_Type type; + i32 x, y; + bool32 random; +}; + +#define TEST_ORDER 0 + +struct Level_Data{ + i32 min_score; + i32 fall_tick_time; +}; + +Level_Data levels[] = { + {0, 30}, + {400, 26}, + {1200, 22}, + {2400, 18}, + {3600, 15} +}; + +struct Particle{ + i32 tex_index; + Vec2 pos, vel; + i32 life_counter; + real32 rot; + real32 rot_vel; +}; + +#define LIST_PRT_TYPES(m) \ + m(prt_ammo); \ + m(prt_blood); \ + m(prt_bone); \ + m(prt_brain); \ + m(prt_human); \ + m(prt_muzzle); \ + m(prt_tnt); \ + m(prt_wall); \ + m(prt_zombie); \ + m(prt_bullet) + +struct Particle_Textures{ + Render_Texture prt_ammo[5]; + Render_Texture prt_blood[6]; + Render_Texture prt_bone[6]; + Render_Texture prt_brain[4]; + Render_Texture prt_human[4]; + Render_Texture prt_muzzle[2]; + Render_Texture prt_tnt[5]; + Render_Texture prt_wall[5]; + Render_Texture prt_zombie[4]; + Render_Texture prt_bullet[1]; +}; + +struct Particle_Type{ + i32 tex_count; + i32 first_tex_id; +}; + +struct App_Vars{ + Memory_Block block; + Game_Input prev_input; + GJ_Context gj; + i32 table_id; + pcg32_random rnd; + + Render_Texture background; + Render_Texture human[1][4]; + Render_Texture zombie[1]; + Render_Texture ammo; + Render_Texture brain; + Render_Texture bomb; + Render_Texture wall; + + Render_Texture *prt_textures; + Particle_Type *prt_types; + i32 prt_type_count; + + Particle particles[128]; + i32 particle_count, particle_max; + +#define DECL_INT(t) i32 t##_index + LIST_PRT_TYPES(DECL_INT); +#undef DECL_INT + + Render_Texture scorename; + Render_Texture scoreback; + Render_Texture gameover; + Render_Texture shadow; + + Render_Texture finish_button[2]; + + Render_Texture overreact; + Render_Texture title; + Render_Texture title_button; + Render_Texture controls; + + Font font; + Font small_font; + + Track music; + Track sfx_tracks[32]; + Track *sfx_free; + + Sound ammo_flip1; + Sound ammo_flip2; + Sound ammo_land; + Sound brains1; + Sound brains2; + Sound explosion; + Sound gun_shot; + Sound person_flip1; + Sound person_flip2; + Sound person_land; + Sound reload; + Sound soft_flip1; + Sound soft_flip2; + Sound soft_land1; + Sound soft_land2; + Sound wall_flip1; + Sound wall_flip2; + Sound wall_land; + Sound zombie_break; + Sound splat_death; + Sound score1; + Sound score2; + + Sound menu_music; + Sound gameplay1; + Sound gameplay2; + + bool32 song_done; + + i32 silence_timer; + i32 fall_timer; + i32 refall_timer; + bool32 game_over; + i32 title_screen; + bool32 controls_screen; + + bool32 need_new_block; + bool32 need_to_fill_gaps; + bool32 chain_reacting; + + i32 level; + +#define COMBO_TIME 20 + + i32 reaction_timer; + i32 fill_timer; + + i32 score; + i32 nonsense_score; + + i32 active_field; + i32 blink_timer; + + char user_name[16]; + i32 user_name_len; + + char user_token[16]; + i32 user_token_len; + + char teaser_name[32]; + char teaser_score[64]; + i32 teaser_name_len; + i32 teaser_score_len; + bool32 looked_up_teaser; + +#define WIDTH 6 +#define HEIGHT 7 + + Entity entities[WIDTH*HEIGHT]; + i32 entity_count, entity_max; + + Spawn_Request spawns[16]; + i32 spawn_count, spawn_max; + + Entity *to_free[16]; + i32 free_count, free_max; + + Entity *grid[WIDTH*HEIGHT]; + +#if TEST_ORDER == 1 + i32 test_type_i; +#endif +}; + +Grid_Pos right_shot[] = { + {1, 0}, + {2, 0}, + {3, 0} +}; + +Grid_Pos up_shot[] = { + {0, -1}, + {0, -2}, + {0, -3} +}; + +Grid_Pos left_shot[] = { + {-1, 0}, + {-2, 0}, + {-3, 0} +}; + +Grid_Pos down_shot[] = { + {0, 1}, + {0, 2}, + {0, 3} +}; + diff --git a/test_data/lots_of_files/jam_graphics.cpp b/test_data/lots_of_files/jam_graphics.cpp new file mode 100644 index 0000000..257485f --- /dev/null +++ b/test_data/lots_of_files/jam_graphics.cpp @@ -0,0 +1,248 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Graphics Layer. + */ + +// TOP + +internal Blit_Rect +rect_from_target(Game_Render_Target *target){ + Blit_Rect result; + result.x_start = 0; + result.y_start = 0; + result.x_end = target->width; + result.y_end = target->height; + + return result; +} + +internal void +bitmap_open_file(char *filename, Bitmap_File *bmp_file){ + File file = system_load_file(filename); + if (file.data && file.size > sizeof(Bitmap_Header)){ + *bmp_file = {}; + bmp_file->file = file; + bmp_file->header = *(Bitmap_Header*)file.data; + bmp_file->byte_pitch = + bmp_file->header.image_size / bmp_file->header.h; + + Assert(bmp_file->header.type == 0x4D42); + Assert(bmp_file->header.compression == 0); + } +} + +internal i32 +bitmap_data_requirement(Bitmap_File *bmp_file){ + i32 w, h; + w = round_up_POT(bmp_file->header.w); + h = round_up_POT(bmp_file->header.h); + + return 4*(w * h); +} + +internal void +bitmap_fill_image(Bitmap_File *bmp_file, Image *image){ + image->width = round_up_POT(bmp_file->header.w); + image->height = round_up_POT(bmp_file->header.h); + image->pitch = 4*image->width; + + i32 true_width, true_height; + true_width = bmp_file->header.w; + true_height = bmp_file->header.h; + + image->img_width = true_width; + image->img_height = true_height; + + i32 byte_per_pixel = bmp_file->header.bits_per_pixel >> 3; + u8 *pixel_line = (u8*)bmp_file->file.data + bmp_file->header.offset; + u8 *dest_line = (u8*)image->data; + if (byte_per_pixel == 3){ + for (i32 y = 0; y < true_height; ++y){ + u8 *pixel = pixel_line; + u32 *dest = (u32*)dest_line; + for (i32 x = 0; x < true_width; ++x){ +#if 0 + *dest = (0xFF << 24) | (pixel[2] << 16) | (pixel[1] << 8) | (pixel[0]); +#else + *dest = (0xFF << 24) | (pixel[0] << 16) | (pixel[1] << 8) | (pixel[2]); +#endif + pixel += 3; + ++dest; + } + pixel_line += bmp_file->byte_pitch; + dest_line += image->pitch; + } + } + else{ + for (i32 y = 0; y < true_height; ++y){ + u32 *pixel = (u32*)pixel_line; + u32 *dest = (u32*)dest_line; + for (i32 x = 0; x < true_width; ++x){ +#if 0 + *dest = + (*pixel & 0xFF00FF00) | + ((*pixel & 0x00FF0000) >> 16) | + ((*pixel & 0x000000FF) << 16); +#else + *dest = *pixel; +#endif + ++pixel; + ++dest; + } + pixel_line += bmp_file->byte_pitch; + dest_line += image->pitch; + } + } +} + +internal void +bitmap_free_file(Bitmap_File *bmp_file){ + system_free_file(bmp_file->file); +} + +// TODO(allen): eliminate this? +internal i32 +font_init(){ + return 0; +} + +inline internal i32 +font_predict_size(i32 pt_size){ + return pt_size*pt_size*128; +} + +// TODO(allen): switch to 0 = FAIL, 1 = SUCCESS +internal i32 +font_load(char *filename, Font *font_out, i32 pt_size, + void *font_block, i32 font_block_size){ + + i32 result = 0; + + File file; + file = system_load_file(filename); + + if (!file.data){ + result = 1; + } + + else{ + stbtt_fontinfo font; + if (!stbtt_InitFont(&font, (u8*)file.data, 0)){ + result = 1; + } + else{ + i32 ascent, descent, line_gap; + real32 scale; + + stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); + scale = stbtt_ScaleForPixelHeight(&font, (real32)pt_size); + + real32 scaled_ascent, scaled_descent, scaled_line_gap; + + scaled_ascent = scale*ascent; + scaled_descent = scale*descent; + scaled_line_gap = scale*line_gap; + + font_out->height = (i32)(scaled_ascent - scaled_descent + scaled_line_gap); + font_out->ascent = (i32)(scaled_ascent); + font_out->descent = (i32)(scaled_descent); + font_out->line_skip = (i32)(scaled_line_gap); + + u8 *memory_cursor = (u8*)font_block; + Assert(pt_size*pt_size*128 <= font_block_size); + + i32 tex_width, tex_height; + tex_width = pt_size*128; + tex_height = pt_size*2; + + font_out->tex_width = tex_width; + font_out->tex_height = tex_height; + + if (stbtt_BakeFontBitmap((u8*)file.data, 0, (real32)pt_size, + memory_cursor, tex_width, tex_height, 0, 128, font_out->chardata) <= 0){ + result = 0; + } + + else{ + GLuint font_tex; + glGenTextures(1, &font_tex); + glBindTexture(GL_TEXTURE_2D, font_tex); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex_width, tex_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, memory_cursor); + + font_out->tex = font_tex; + } + + i32 max_advance = 0; + + for (u16 code_point = 0; code_point < 128; ++code_point){ + if (stbtt_FindGlyphIndex(&font, code_point) != 0){ + font_out->glyphs[code_point].exists = 1; + font_out->glyphs[code_point].advance = font_out->chardata[code_point].xadvance; + i32 advance = Ceil(font_out->chardata[code_point].xadvance); + if (max_advance < advance){ + max_advance = advance; + } + } + } + font_out->advance = max_advance; + + } + system_free_file(file); + } + + return result; +} + +internal void +font_draw_glyph_clipped(Game_Render_Target *target, + Font *font, u16 character, + real32 x, real32 y, u32 color, + Blit_Rect clip_box){ + + if (clip_box.x_start < clip_box.x_end && clip_box.y_start < clip_box.y_end){ + real32 x_shift, y_shift; + x_shift = font->chardata[character].xoff; + y_shift = (real32)font->ascent;// + font->chardata[character].yoff; + + x += x_shift; + y += y_shift; + + glScissor(clip_box.x_start, + target->height - clip_box.y_end, + clip_box.x_end - clip_box.x_start, + clip_box.y_end - clip_box.y_start); + + stbtt_aligned_quad q; + stbtt_GetBakedQuadUnrounded(font->chardata, font->tex_width, font->tex_height, character, &x, &y, &q, 1); + + Vec3 c = unpack_color3(color); + glColor3f(c.r, c.g, c.b); + glBindTexture(GL_TEXTURE_2D, font->tex); + glBegin(GL_QUADS); + { + glTexCoord2f(q.s0, q.t1); glVertex2f(q.x0, q.y1); + glTexCoord2f(q.s1, q.t1); glVertex2f(q.x1, q.y1); + glTexCoord2f(q.s1, q.t0); glVertex2f(q.x1, q.y0); + glTexCoord2f(q.s0, q.t0); glVertex2f(q.x0, q.y0); + } + glEnd(); + + glScissor(0, 0, target->width, target->height); + } +} + +internal void +font_draw_glyph(Game_Render_Target *target, + Font *font, u16 character, + real32 x, real32 y, u32 color){ + Blit_Rect screen = rect_from_target(target); + font_draw_glyph_clipped(target, font, character, x, y, color, screen); +} diff --git a/test_data/lots_of_files/jam_graphics.h b/test_data/lots_of_files/jam_graphics.h new file mode 100644 index 0000000..bf805e3 --- /dev/null +++ b/test_data/lots_of_files/jam_graphics.h @@ -0,0 +1,67 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Graphics Layer. + */ + +struct Image{ + u32 *data; + i32 width, height, pitch; + i32 img_width, img_height; +}; + +#pragma pack(push, 1) +struct Bitmap_Header{ + u16 type; + u32 size; + u16 res1, res2; + u32 offset; + + u32 struct_size; + i32 w, h; + u16 planes; + u16 bits_per_pixel; + u32 compression; + u32 image_size; + i32 xppm, yppm; + u32 colors_used; + u32 colors_important; +}; +#pragma pack(pop) + +struct Bitmap_File{ + File file; + + Bitmap_Header header; + i32 byte_pitch; +}; + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +struct Glyph_Data{ + bool32 exists; + real32 advance; +}; + +struct Font{ + Glyph_Data glyphs[128]; + stbtt_bakedchar chardata[128]; + i32 height, ascent, descent, line_skip; + i32 advance; + u32 tex; + i32 tex_width, tex_height; +}; + +struct Blit_Rect{ + i32 x_start, y_start; + i32 x_end, y_end; +}; + +struct Sub_Rect{ + real32 x_start, y_start; + real32 x_end, y_end; +}; + diff --git a/test_data/lots_of_files/jam_math.cpp b/test_data/lots_of_files/jam_math.cpp new file mode 100644 index 0000000..e6b5b82 --- /dev/null +++ b/test_data/lots_of_files/jam_math.cpp @@ -0,0 +1,353 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015(mm.dd.yyyy) + * + * Math utilities. + */ + +/* VECTORS */ + +struct Vec2{ + union{ + struct{ + real32 x, y; + }; + real32 v[2]; + }; +}; + +struct Vec3{ + union{ + struct{ + real32 x, y, z; + }; + struct{ + real32 r, g, b; + }; + real32 v[3]; + }; +}; + +struct Vec4{ + union{ + struct{ + real32 x, y, z, w; + }; + struct{ + real32 r, g, b, a; + }; + real32 v[4]; + }; +}; + +/* CONTRUCTOR FUNCTIONS */ + +internal Vec2 +V2(real32 x, real32 y){ + return {x, y}; +} + +internal Vec3 +V3(real32 x, real32 y, real32 z){ + return {x, y, z}; +} + +internal Vec4 +V4(real32 x, real32 y, real32 z, real32 w){ + return {x, y, z, w}; +} + +/* ADDITION */ + +inline internal Vec2 +operator+(Vec2 a, Vec2 b){ + a = V2(a.x + b.x, a.y + b.y); + return a; +} + +inline internal Vec3 +operator+(Vec3 a, Vec3 b){ + a = V3(a.x + b.x, a.y + b.y, a.z + b.z); + return a; +} + +inline internal Vec4 +operator+(Vec4 a, Vec4 b){ + a = V4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + return a; +} + +inline internal Vec2& +operator+=(Vec2 &a, Vec2 b){ + a = V2(a.x + b.x, a.y + b.y); + return a; +} + +inline internal Vec3& +operator+=(Vec3 &a, Vec3 b){ + a = V3(a.x + b.x, a.y + b.y, a.z + b.z); + return a; +} + +inline internal Vec4& +operator+=(Vec4 &a, Vec4 b){ + a = V4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + return a; +} + +/* SUBTRACTION */ + +inline internal Vec2 +operator-(Vec2 a, Vec2 b){ + a = V2(a.x - b.x, a.y - b.y); + return a; +} + +inline internal Vec3 +operator-(Vec3 a, Vec3 b){ + a = V3(a.x - b.x, a.y - b.y, a.z - b.z); + return a; +} + +inline internal Vec4 +operator-(Vec4 a, Vec4 b){ + a = V4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + return a; +} + +inline internal Vec2& +operator-=(Vec2 &a, Vec2 b){ + a = V2(a.x - b.x, a.y - b.y); + return a; +} + +inline internal Vec3& +operator-=(Vec3 &a, Vec3 b){ + a = V3(a.x - b.x, a.y - b.y, a.z - b.z); + return a; +} + +inline internal Vec4& +operator-=(Vec4 &a, Vec4 b){ + a = V4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + return a; +} + +/* MULTIPLICATION */ + +inline internal Vec2 +operator*(Vec2 a, real32 k){ + a = V2(a.x * k, a.y * k); + return a; +} + +inline internal Vec3 +operator*(Vec3 a, real32 k){ + a = V3(a.x * k, a.y * k, a.z * k); + return a; +} + +inline internal Vec4 +operator*(Vec4 a, real32 k){ + a = V4(a.x * k, a.y * k, a.z * k, a.w * k); + return a; +} + +inline internal Vec2 +operator*(real32 k, Vec2 a){ + a = V2(a.x * k, a.y * k); + return a; +} + +inline internal Vec3 +operator*(real32 k, Vec3 a){ + a = V3(a.x * k, a.y * k, a.z * k); + return a; +} + +inline internal Vec4 +operator*(real32 k, Vec4 a){ + a = V4(a.x * k, a.y * k, a.z * k, a.w * k); + return a; +} + +inline internal Vec2& +operator*=(Vec2& a, real32 k){ + a = V2(a.x * k, a.y * k); + return a; +} + +inline internal Vec3& +operator*=(Vec3& a, real32 k){ + a = V3(a.x * k, a.y * k, a.z * k); + return a; +} + +inline internal Vec4& +operator*=(Vec4& a, real32 k){ + a = V4(a.x * k, a.y * k, a.z * k, a.w * k); + return a; +} + +/* VECTOR VECTOR PRODUCTS */ + +inline internal real32 +dot(Vec2 a, Vec2 b){ + real32 result = a.x*b.x + a.y*b.y; + return result; +} + +inline internal real32 +dot(Vec3 a, Vec3 b){ + real32 result = a.x*b.x + a.y*b.y + a.z*b.z; + return result; +} + +inline internal real32 +dot(Vec4 a, Vec4 b){ + real32 result = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; + return result; +} + +inline internal Vec3 +cross(Vec3 a, Vec3 b){ + a = V3(a.y*b.z - a.z*b.y, + a.z*b.x - a.x*b.z, + a.x*b.y - a.y*b.x); + return a; +} + +inline internal Vec2 +hadamard(Vec2 a, Vec2 b){ + a = V2(a.x*b.x, a.y*b.y); + return a; +} + +inline internal Vec3 +hadamard(Vec3 a, Vec3 b){ + a = V3(a.x*b.x, a.y*b.y, a.z*b.z); + return a; +} + +inline internal Vec4 +hadamard(Vec4 a, Vec4 b){ + a = V4(a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w); + return a; +} + +/* OTHER VECTOR OPERATIONS */ +inline internal Vec2 +project(Vec2 v, Vec2 normalized_p){ + return dot(v, normalized_p)*normalized_p; +} + +/* LERPS */ + +inline internal real32 +lerp(real32 a, real32 t, real32 b){ + real32 result = a + (b - a)*t; + return result; +} + +inline internal Vec2 +lerp(Vec2 a, real32 t, Vec2 b){ + Vec2 result = a + (b - a)*t; + return result; +} + +inline internal Vec3 +lerp(Vec3 a, real32 t, Vec3 b){ + Vec3 result = a + (b - a)*t; + return result; +} + +inline internal Vec4 +lerp(Vec4 a, real32 t, Vec4 b){ + Vec4 result = a + (b - a)*t; + return result; +} + +/* COLOR */ + +internal Vec3 +unpack_color3(u32 color){ + Vec3 result; + result.r = ((color >> 16) & 0xFF) / 255.f; + result.g = ((color >> 8) & 0xFF) / 255.f; + result.b = ((color >> 0) & 0xFF) / 255.f; + return result; +} + +internal Vec4 +unpack_color4(u32 color){ + Vec4 result; + result.a = ((color >> 24) & 0xFF) / 255.f; + result.r = ((color >> 16) & 0xFF) / 255.f; + result.g = ((color >> 8) & 0xFF) / 255.f; + result.b = ((color >> 0) & 0xFF) / 255.f; + return result; +} + +/* RANDOM */ + +struct pcg32_random{ + u64 state; + u64 inc; +}; + +// STUDY(allen): LEARN THIS +u32 pcg32_random_r(pcg32_random* rng) +{ + u64 oldstate = rng->state; + // Advance internal state + rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1); + // Calculate output function (XSH RR), uses old state for max ILP + u32 xorshifted = (u32)(((oldstate >> 18u) ^ oldstate) >> 27u); + u32 rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} + +/* GRID MATH */ + +struct Grid_Pos{ + i32 x, y; +}; + +Grid_Pos von_neumann[] = { + {-1, 0}, + {0, -1}, + {1, 0}, + {0, 1} +}; + +Grid_Pos moore[] = { + {-1, -1}, + {0, -1}, + {1, -1}, + {-1, 0}, + {1, 0}, + {-1, 1}, + {0, 1}, + {1, 1} +}; + +Grid_Pos operator+(Grid_Pos a, Grid_Pos b){ + Grid_Pos result; + result.x = a.x + b.x; + result.y = a.y + b.y; + return result; +} + +/* ROUNDING */ + +inline internal i32 +round_up_POT(i32 x){ + i32 b = 1; + while (b < x){ + b <<= 1; + } + return b; +} + + diff --git a/test_data/lots_of_files/jam_meta.h b/test_data/lots_of_files/jam_meta.h new file mode 100644 index 0000000..43d4363 --- /dev/null +++ b/test_data/lots_of_files/jam_meta.h @@ -0,0 +1,152 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Meta File. + */ + +#ifndef DB_META_H +#define DB_META_H + +#include <stdint.h> +#include <limits.h> + +typedef uint8_t u8; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; + +typedef int8_t i8; +typedef int64_t i64; +typedef int32_t i32; +typedef int16_t i16; + +typedef i32 bool32; +typedef i8 bool8; + +typedef float real32; +typedef double real64; + +typedef const char* c_str; + +#define internal static +#define globalvar static +#define persist static + +#define globalconst static const +#define localconst static const + +#define S(X) #X +#define S_(X) S(X) +#define S__LINE__ S_(__LINE__) + +#define AllowLocal(name) (void)name +#define ArrayCount(array) (sizeof(array)/sizeof(array[0])) +#define Assert(check) {if(!(check)){FILE *file = fopen("errorlog", "wb"); fprintf(file, "ASSERT!"); fclose(file); *((i32*)0) = 0;}} +#define TentativeAssert(check) {if(!(check)){*((i32*)0) = 0;}} + +#define Min(a,b) (((a)<(b))?(a):(b)) +#define Max(a,b) (((a)>(b))?(a):(b)) + +#define Swap(t,a,b) {t temp = a; a = b; b = temp;} + +inline internal i32 +Round(real32 x){ return (i32)(x+.5f); } + +inline internal i32 +Floor(real32 x){ return (i32)(x)-(x<0)*((i32)x != x); } + +inline internal i32 +Ceil(real32 x){ return (i32)(x)+(x>0)*((i32)x != x); } + +inline internal real32 +DecPart(real32 x){ return x - (i32)x; } + +// TODO(allen): move to math.cpp +inline internal i32 +LargeRoundUp(i32 n, i32 b){ return b*(n/b); } + +#define PI32 3.1415926f +#define TAU32 6.2831853f + +#define TMax(t,v) globalconst t max_##t = v +TMax(u8, 255); +TMax(u16, 65535); +TMax(u32, 4294967295); +TMax(u64, 18446744073709551615); + +TMax(i8, 127); +TMax(i16, 32767); +TMax(i32, 2147483647); +TMax(i64, 9223372036854775807); +#undef TMax + +#define TMin(t) globalconst t min_##t = 0 +TMin(u8); +TMin(u16); +TMin(u32); +TMin(u64); +#undef TMin + +#define TMin(t,v) globalconst t min_##t = ((t)v) +TMin(i8, -128); +TMin(i16, -32768); +TMin(i32, INT_MIN); +TMin(i64, -9223372036854775808LL); +#undef TMin + +#define CheckFlag(flags, mask) ((flags & mask) == mask) + +#define Bit_0 (1 << 0) +#define Bit_1 (1 << 1) +#define Bit_2 (1 << 2) +#define Bit_3 (1 << 3) +#define Bit_4 (1 << 4) +#define Bit_5 (1 << 5) +#define Bit_6 (1 << 6) +#define Bit_7 (1 << 7) + +#define Bit_8 (1 << 8) +#define Bit_9 (1 << 9) +#define Bit_10 (1 << 10) +#define Bit_11 (1 << 11) +#define Bit_12 (1 << 12) +#define Bit_13 (1 << 13) +#define Bit_14 (1 << 14) +#define Bit_15 (1 << 15) + +#define Bit_16 (1 << 16) +#define Bit_17 (1 << 17) +#define Bit_18 (1 << 18) +#define Bit_19 (1 << 19) +#define Bit_20 (1 << 20) +#define Bit_21 (1 << 21) +#define Bit_22 (1 << 22) +#define Bit_23 (1 << 23) + +#define Bit_24 (1 << 24) +#define Bit_25 (1 << 25) +#define Bit_26 (1 << 26) +#define Bit_27 (1 << 27) +#define Bit_28 (1 << 28) +#define Bit_29 (1 << 29) +#define Bit_30 (1 << 30) +#define Bit_31 (1 << 31) + +#define Byte_0 (0xFFU) +#define Byte_1 (0xFFU << 8) +#define Byte_2 (0xFFU << 16) +#define Byte_3 (0xFFU << 24) +#define Byte_4 (0xFFU << 32) +#define Byte_5 (0xFFU << 40) +#define Byte_6 (0xFFU << 48) +#define Byte_7 (0xFFU << 56) + +#define bytes(n) (n) +#define Kbytes(n) (bytes(n) * 1024) +#define Mbytes(n) (Kbytes(n) * 1024) +#define Gbytes(n) (Mbytes(n) * 1024) +#define Tbytes(n) (Gbytes(n) * 1024) + +#endif diff --git a/test_data/lots_of_files/jam_rendering.h b/test_data/lots_of_files/jam_rendering.h new file mode 100644 index 0000000..84bc3a6 --- /dev/null +++ b/test_data/lots_of_files/jam_rendering.h @@ -0,0 +1,18 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Rendering Layer. + */ + +#if RENDER_MODE == OPENGL + +struct Render_Texture{ + u32 texid; + i32 width, height; + i32 img_width, img_height; + real32 tex_x, tex_y; +}; + +#endif diff --git a/test_data/lots_of_files/jam_rendering_system.h b/test_data/lots_of_files/jam_rendering_system.h new file mode 100644 index 0000000..5e06430 --- /dev/null +++ b/test_data/lots_of_files/jam_rendering_system.h @@ -0,0 +1,23 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Rendering System Layer. + */ + +#if RENDER_MODE == SOFTWARE + +struct Game_Render_Target{ + u8 *pixels; + i32 width, height, pitch; +}; + +#elif RENDER_MODE == OPENGL + +struct Game_Render_Target{ + i32 width, height; +}; + +#endif + diff --git a/test_data/lots_of_files/jam_rendering_win32.cpp b/test_data/lots_of_files/jam_rendering_win32.cpp new file mode 100644 index 0000000..90ac333 --- /dev/null +++ b/test_data/lots_of_files/jam_rendering_win32.cpp @@ -0,0 +1,218 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Win32 Rendering System Layer. + */ + +#define SIG_STARTUP(name) void(name)(Win32RenderingVars *render_vars) +#define SIG_SHUTDOWN(name) void(name)(Win32RenderingVars *render_vars) +#define SIG_REDRAW(name) void(name)(Win32RenderingVars *render_vars) +#define SIG_SET_SIZE(name) void(name)(Win32RenderingVars *render_vars, i32 port_width, i32 port_height, i32 view_width, i32 view_height) +#define SIG_GET_RENDER_TARGET(name) Game_Render_Target(name)(Win32RenderingVars *render_vars) + +#if RENDER_MODE == SOFTWARE + +struct Win32RenderingVars{ + HDC hdc; + BITMAPINFO bmp_info; + i32 width, height, pitch; + i32 pixel_data_size; + void *pixel_data; +}; + +internal +SIG_STARTUP(render_startup){} + +internal +SIG_SHUTDOWN(render_shutdown){} + +internal +SIG_REDRAW(render_redraw_screen){ + render_vars->bmp_info.bmiHeader.biHeight = + -render_vars->bmp_info.bmiHeader.biHeight; + SetDIBitsToDevice(render_vars->hdc, 0, 0, + render_vars->width, render_vars->height, + 0, 0, + 0, render_vars->height, + render_vars->pixel_data, + &render_vars->bmp_info, + DIB_RGB_COLORS); + render_vars->bmp_info.bmiHeader.biHeight = + -render_vars->bmp_info.bmiHeader.biHeight; +} + +// TODO(allen): This version ignores view / port ratio. It simply resize +// to the dimensions of the port and the game will render in the top +// left leaving the rest of the window ignored. Either implement +// software stretching, or at least center the view like the original Push. +internal +SIG_SET_SIZE(render_set_screen_size){ + render_vars->width = port_width; + render_vars->height = port_height; + render_vars->pitch = port_width*4; + + render_vars->bmp_info = {}; + BITMAPINFOHEADER bmi_header = {}; + bmi_header.biSize = sizeof(BITMAPINFOHEADER); + bmi_header.biWidth = render_vars->width; + bmi_header.biHeight = render_vars->height; + bmi_header.biPlanes = 1; + bmi_header.biBitCount = 32; + bmi_header.biCompression = BI_RGB; + render_vars->bmp_info.bmiHeader = bmi_header; + + // TODO(allen): Bulletproof this. + + i32 new_size = port_height*port_width*4; + if (new_size > render_vars->pixel_data_size){ + if (render_vars->pixel_data){ + VirtualFree(render_vars->pixel_data, + 0, MEM_RELEASE); + } + + render_vars->pixel_data_size = new_size; + render_vars->pixel_data = + VirtualAlloc(0, height*width*4, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + + if (!render_vars->pixel_data){ + WINERROR("Failed to setup window pixel memory"); + } + } +} + +SIG_GET_RENDER_TARGET(render_get_render_target){ + Game_Render_Target render; + + render.pixels = (u8*)render_vars->pixel_data; + render.width = render_vars->width; + render.height = render_vars->height; + render.pitch = render_vars->pitch; + + return render; +} + +#elif RENDER_MODE == OPENGL + +struct Win32RenderingVars{ + HDC hdc; + HGLRC context; + i32 width, height; +}; + +internal +SIG_STARTUP(render_startup){ + int nPixelFormat; + + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), //size of structure + 1, //default version + PFD_DRAW_TO_WINDOW | //window drawing support + PFD_SUPPORT_OPENGL | //opengl support + PFD_DOUBLEBUFFER, //double buffering support + PFD_TYPE_RGBA, //RGBA color mode + 32, //32 bit color mode + 0, 0, 0, 0, 0, 0, //ignore color bits + 0, //no alpha buffer + 0, //ignore shift bit + 0, //no accumulation buffer + 0, 0, 0, 0, //ignore accumulation bits + 16, //16 bit z-buffer size + 0, //no stencil buffer + 0, //no aux buffer + PFD_MAIN_PLANE, //main drawing plane + 0, //reserved + 0, 0, 0 }; //layer masks ignored + + nPixelFormat = ChoosePixelFormat(render_vars->hdc, &pfd); + SetPixelFormat(render_vars->hdc, nPixelFormat, &pfd); + + render_vars->context = wglCreateContext(render_vars->hdc); + wglMakeCurrent(render_vars->hdc, render_vars->context); + + glShadeModel(GL_SMOOTH); + glEnable(GL_TEXTURE_2D); + //glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_CULL_FACE); + glFrontFace(GL_CCW); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + GLenum error; + error = glGetError(); + error = error; +} + +internal +SIG_SHUTDOWN(render_shutdown){ + wglMakeCurrent(render_vars->hdc, 0); + wglDeleteContext(render_vars->context); +} + +internal +SIG_REDRAW(render_redraw_screen){ + glFlush(); + SwapBuffers(render_vars->hdc); +} + +// NOTE(allen): This changes the aspect ratio of the view to perfectly +// fill the window. The height is untouched, the width is the only part +// of the view that is effected. +internal +SIG_SET_SIZE(render_set_screen_size){ + render_vars->height = view_height; + render_vars->width = (i32)((port_width / ((real32)port_height)) * view_height); + + glViewport(0, 0, port_width, port_height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0.f, render_vars->width, view_height, 0.f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +// NOTE(allen): This maintains a fixed aspect ratio and +// puts the game port in the center, expecting the game to +// add bars +#if 0 +internal +SIG_SET_SIZE(render_set_screen_size){ + render_vars->width = view_width; + render_vars->height = view_height; + + real32 ratio_width, ratio_height; + ratio_width = (real32)(port_width) / view_width; + ratio_height = (real32)(port_height) / view_height; + + i32 start_x, start_y; + if (ratio_width > ratio_height){ + ratio_width = ratio_height; + start_y = 0; + start_x = (port_width - (ratio_width*view_width))/2; + } + else{ + ratio_height = ratio_width; + start_x = 0; + start_y = (port_height - (ratio_height*view_height))/2; + } + + glViewport(start_x, start_y, ratio_width*view_width, ratio_height*view_height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0.f, view_width, view_height, 0.f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} +#endif + +SIG_GET_RENDER_TARGET(render_get_render_target){ + Game_Render_Target render = {}; + render.width = render_vars->width; + render.height = render_vars->height; + return render; +} + +#endif diff --git a/test_data/lots_of_files/jam_system.h b/test_data/lots_of_files/jam_system.h new file mode 100644 index 0000000..88b277d --- /dev/null +++ b/test_data/lots_of_files/jam_system.h @@ -0,0 +1,41 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Platform Services + */ + +// TODO(allen): +// - The file loading scheme requires that the platform layer +// make an extra allocation and then is later used to free +// that allocation. It would be better for file loading to +// be broken up into two steps: (1) get size (2) load into +// buffer so the application can provide any buffer it wants. +// + +struct File{ + void *data; + u64 size; +}; + +#define SIG_SYS_LOAD_FILE(name) File name(char *filename) +#define SIG_SYS_SAVE_FILE(name) void name(File file, char *filename) +#define SIG_SYS_FREE_FILE(name) void name(File file) + +internal +SIG_SYS_LOAD_FILE(system_load_file); + +internal +SIG_SYS_SAVE_FILE(system_save_file); + +internal +SIG_SYS_FREE_FILE(system_free_file); + + + +#define SIS_SYS_SET_KEYBIND(name) void name(i32 btn_id, char key) + +internal +SIS_SYS_SET_KEYBIND(system_set_keybind); + diff --git a/test_data/lots_of_files/jam_win32.cpp b/test_data/lots_of_files/jam_win32.cpp new file mode 100644 index 0000000..433f27b --- /dev/null +++ b/test_data/lots_of_files/jam_win32.cpp @@ -0,0 +1,1093 @@ +/* + * Overreact - Mr. 4th Dimention + * Allen Webster + * 03.21.2015 (mm.dd.yyyy) + * + * Platform Layer: Win32. + */ + +// TOP + +// TODO(allen): memcpy sound samples for performance +// TODO(allen): migrate as much as possible to c standard lib + +#include "jam_meta.h" + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> + +#define SOFTWARE 0 +#define OPENGL 1 + +#define DISABLE_CONTROLLER 1 + +#define RENDER_MODE OPENGL + +#if RENDER_MODE == OPENGL +#include <GL/glew.h> +#endif + +#include "jam_system.h" +#include "jam_rendering_system.h" +#include "jam_rendering.h" +#include "jam.h" +#include "jam_math.cpp" +#include "jam_graphics.h" +#include "jam_graphics.cpp" +#include "jam_audio.h" +#include "jam_audio.cpp" +#include "gjCStyle.h" +#include "jam_game.h" +#include "jam.cpp" + +#include <Windows.h> + +#if !DISABLE_CONTROLLER +#include <XInput.h> +#endif + +#include <DSound.h> + +#define WINERROR(msg) + +#include "jam_rendering_win32.cpp" + +internal +SIG_SYS_LOAD_FILE(system_load_file){ + File result = {}; + HANDLE winfile = CreateFileA(filename, GENERIC_READ, + 0, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + if (winfile){ + LARGE_INTEGER size; + // TODO(allen): GetFileSizeEx is incompatible with windows store. + if (GetFileSizeEx(winfile, &size)){ + result.size = size.QuadPart; + result.data = VirtualAlloc(0, (i32)result.size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + u32 unread = (u32)result.size; + while (unread != 0){ + DWORD read_size = Min(unread, max_u32); + DWORD true_read_size; + u8 *write_pos = (u8*)result.data; + if (ReadFile(winfile, write_pos, read_size, &true_read_size, 0)){ + if (unread >= true_read_size){ + unread -= true_read_size; + write_pos += true_read_size; + } + else{ + break; + } + } + else{ + break; + } + } + if (unread != 0){ + system_free_file(result); + result = {}; + } + } + CloseHandle(winfile); + } + return result; +} + +internal +SIG_SYS_SAVE_FILE(system_save_file){ + HANDLE winfile = CreateFileA(filename, GENERIC_WRITE, + 0, 0, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); + if (winfile){ + u32 unwritten = (u32)file.size; + while (unwritten != 0){ + DWORD write_size = Min(unwritten, max_u32); + DWORD true_write_size; + u8 *src_pos = (u8*)file.data; + if (ReadFile(winfile, src_pos, write_size, &true_write_size, 0)){ + if (unwritten >= true_write_size){ + unwritten -= true_write_size; + src_pos += true_write_size; + } + else{ + break; + } + } + else{ + break; + } + } + } +} + +internal +SIG_SYS_FREE_FILE(system_free_file){ + VirtualFree(file.data, 0, MEM_RELEASE); +} + +#if !DISABLE_CONTROLLER +#define SIG_Input_Get_State(name) DWORD(name)(DWORD user_index, XINPUT_STATE *state) +#define SIG_Input_Set_State(name) DWORD(name)(DWORD user_index, XINPUT_VIBRATION *vibration) + +typedef SIG_Input_Get_State(Input_Get_State); +typedef SIG_Input_Set_State(Input_Set_State); + +struct Win32ControllerFunctions{ + bool32 is_valid; + Input_Get_State *get_state; + Input_Set_State *set_state; +}; +#endif + +#define SIG_Sound_Create(name) HRESULT(name)(LPGUID guid, LPDIRECTSOUND *ds, LPUNKNOWN unk); +typedef SIG_Sound_Create(Sound_Create); + +struct Win32SoundFunctions{ + Sound_Create *create; +}; + +struct Win32Vars{ + Game_Memory memory; + + HWND winhandle; + + Win32RenderingVars render_vars; + i32 min_width, min_height; + i32 min_game_width, min_game_height; + i32 pref_game_width, pref_game_height; + + bool32 is_fullscreen; + bool32 has_prev_winrect; + RECT prev_winrect; + + LPDIRECTSOUNDBUFFER sound_buffer; + i32 samples_per_second; + i32 bytes_per_sample; + i32 sound_buffer_size; + u32 sound_sample_index; + i16* samples; + i32 sound_safety_margin; + + i16 mx, my; + + i64 msecond_per_frame; + real32 update_hz; + bool32 keep_playing; + bool32 first; + + Game_Input keyboard_input, controller_input; + +#if 0 + Win32ControllerFunctions xin; +#endif + + bool32 toggle_full_screen; + + char btn1, btn2; +}; + +globalvar Win32Vars win32; + +internal +SIS_SYS_SET_KEYBIND(system_set_keybind){ + Assert(key >= 'A' && key <= 'Z'); + if (btn_id == 1){ + win32.btn1 = key; + } + else if (btn_id == 2){ + win32.btn2 = key; + } +} + +#if 0 +internal void +Win32InputSetup(){ + HMODULE xinput = 0; + xinput = LoadLibrary("Xinput1_4.dll"); + if (!xinput){ + xinput = LoadLibrary("Xinput9_1_0.dll"); + } + if (!xinput){ + xinput = LoadLibrary("Xinput1_3.dll"); + } + if (xinput){ + win32.xin.get_state = (Input_Get_State*)GetProcAddress(xinput, "XInputGetState"); + win32.xin.set_state = (Input_Set_State*)GetProcAddress(xinput, "XInputSetState"); + + if (!win32.xin.get_state || !win32.xin.set_state){ + win32.xin = {}; + } + else{ + win32.xin.is_valid = 1; + } + } +} + +struct Win32ControllerData{ + real32 lstick_x, lstick_y; + real32 rstick_x, rstick_y; + bool8 plugged_in; + bool8 up, down, left, right; + bool8 start, back; + bool8 left_thumb, right_thumb; + bool8 left_shoulder, right_shoulder; + bool8 a, b, x, y; + bool8 ltrigger, rtrigger; +}; + +inline internal void +Win32RealStick(real32 *stick, i16 input, i16 deadzone){ + if (input > deadzone){ + *stick = (input - deadzone) / (32767.f - deadzone); + } + else if (input < -deadzone){ + *stick = (input + deadzone) / (32768.f - deadzone); + } + else{ + *stick = 0; + } +} + +internal void +Win32ControllerGet(Win32ControllerData *data, i32 index){ + XINPUT_STATE state; + DWORD success = win32.xin.get_state(index, &state); + + data->plugged_in = (success == ERROR_SUCCESS); + if (success == ERROR_SUCCESS){ + data->up = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP); + data->down = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN); + data->left = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT); + data->right = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT); + + data->start = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_START); + data->back = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_BACK); + + data->left_thumb = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB); + data->right_thumb = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB); + + data->left_shoulder = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER); + data->right_shoulder = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER); + + data->a = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_A); + data->b = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_B); + data->x = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_X); + data->y = CheckFlag(state.Gamepad.wButtons, XINPUT_GAMEPAD_Y); + + if (state.Gamepad.bLeftTrigger >= 224){ + data->ltrigger = 1; + } + else{ + data->ltrigger = 0; + } + + if (state.Gamepad.bRightTrigger >= 224){ + data->rtrigger = 1; + } + else{ + data->rtrigger = 0; + } + + Win32RealStick(&data->lstick_x, state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + Win32RealStick(&data->lstick_y, state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + + Win32RealStick(&data->rstick_x, state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); + Win32RealStick(&data->rstick_y, state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); + + } +} + +internal bool32 +Win32ControllerRumble(i32 index, real32 left, real32 right){ + XINPUT_VIBRATION vals; + vals.wLeftMotorSpeed = (u16)(left*max_u16); + vals.wRightMotorSpeed = (u16)(right*max_u16); + DWORD success = win32.xin.set_state(index, &vals); + return (success == ERROR_SUCCESS); +} +#endif + +internal Win32SoundFunctions +Win32SoundSetup(i32 samples_per_second, i32 buffer_size){ + Win32SoundFunctions funcs = {}; + HMODULE dsound = LoadLibrary("dsound.dll"); + if (dsound){ + funcs.create = (Sound_Create*)GetProcAddress(dsound, "DirectSoundCreate"); + if (funcs.create){ + LPDIRECTSOUND dsound; + if (SUCCEEDED(funcs.create(0, &dsound, 0))){ + WAVEFORMATEX wave_format = {}; + wave_format.wFormatTag = WAVE_FORMAT_PCM; + wave_format.nChannels = 2; + wave_format.nSamplesPerSec = samples_per_second; + wave_format.wBitsPerSample = 16; + wave_format.nBlockAlign = (wave_format.nChannels * wave_format.wBitsPerSample) / 8; + wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec*wave_format.nBlockAlign; + wave_format.cbSize = 0; + + if (SUCCEEDED(dsound->SetCooperativeLevel(win32.winhandle, DSSCL_PRIORITY))){ + DSBUFFERDESC buffer_description = {}; + buffer_description.dwSize = sizeof(buffer_description); + buffer_description.dwFlags = DSBCAPS_PRIMARYBUFFER; + + LPDIRECTSOUNDBUFFER buffer; + if (SUCCEEDED(dsound->CreateSoundBuffer(&buffer_description, &buffer, 0))){ + if (buffer->SetFormat(&wave_format)){ + // NOTE(allen): SUCCESS - set primary buffer format + } + else{ + // NOTE(allen): FAIL - set primary buffer format + } + } + else{ + // NOTE(allen): FAIL - create primary buffer + } + } + else{ + // NOTE(allen): FAIL - set cooperative level + } + + DSBUFFERDESC buffer_description = {}; + buffer_description.dwSize = sizeof(buffer_description); + buffer_description.dwFlags = DSBCAPS_GETCURRENTPOSITION2; + buffer_description.dwBufferBytes = buffer_size; + buffer_description.lpwfxFormat = &wave_format; + if (SUCCEEDED(dsound->CreateSoundBuffer(&buffer_description, &win32.sound_buffer, 0))){ + // NOTE(allen): SUCCESS - create secondary buffer + } + else{ + // NOTE(allen): FAIL - create secondary buffer + } + } + else{ + // NOTE(allen): FAIL - create direct sound interface + } + } + else{ + // NOTE(allen): FAIL - load direct sound creator function + } + } + else{ + // NOTE(allen): FAIL - load direct sound library + } + + return funcs; +} + +internal void +Win32ClearSound(){ + VOID *chunk1, *chunk2; + DWORD size1, size2; + if (SUCCEEDED(win32.sound_buffer->Lock(0, win32.sound_buffer_size, + &chunk1, &size1, &chunk2, &size2, + 0))){ + u8 *dest = (u8*)chunk1; + if (dest){ + for (DWORD i = 0; i < size1; ++i){ + *dest = 0; + ++dest; + } + } + + dest = (u8*)chunk2; + + if (dest){ + for (DWORD i = 0; i < size1; ++i){ + *dest = 0; + ++dest; + } + } + + win32.sound_buffer->Unlock(chunk1, size1, chunk2, size2); + } +} + +internal void +Win32FillSound(DWORD write_start, DWORD write_size, i16 *source){ + VOID *chunk1, *chunk2; + DWORD size1, size2; + if (SUCCEEDED(win32.sound_buffer->Lock(write_start, write_size, + &chunk1, &size1, &chunk2, &size2, + 0))){ + DWORD samples = size1/win32.bytes_per_sample; + i16 *dest = (i16*)chunk1; + if (dest){ + for (DWORD i = 0; i < samples; ++i){ + *dest++ = *source++; + *dest++ = *source++; + ++win32.sound_sample_index; + } + } + + samples = size2/win32.bytes_per_sample; + dest = (i16*)chunk2; + if (dest){ + for (DWORD i = 0; i < samples; ++i){ + *dest++ = *source++; + *dest++ = *source++; + ++win32.sound_sample_index; + } + } + + win32.sound_buffer->Unlock(chunk1, size1, chunk2, size2); + } +} + +inline internal void +Win32UpdateKey(bool32 press, bool32 release, bool8 *key){ + if (press){ + *key = 1; + } + if (release){ + *key = 0; + } +} + +internal LRESULT +Win32Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ + LRESULT result = {}; + switch (uMsg){ + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + { + bool32 prev, current; + prev = ((lParam & Bit_30)?(1):(0)); + current = ((lParam & Bit_31)?(0):(1)); + + bool32 press = current && !prev; + bool32 release = prev && !current; + + switch (wParam){ + case VK_UP: + { + Win32UpdateKey(press, release, &win32.keyboard_input.digital.up); + }break; + case VK_DOWN: + { + Win32UpdateKey(press, release, &win32.keyboard_input.digital.down); + }break; + + case VK_LEFT: + { + Win32UpdateKey(press, release, &win32.keyboard_input.digital.left); + }break; + case VK_RIGHT: + { + Win32UpdateKey(press, release, &win32.keyboard_input.digital.right); + }break; + + case VK_SPACE: + case VK_RETURN: + { + Win32UpdateKey(press, release, &win32.keyboard_input.button[0]); + + if (uMsg == WM_KEYDOWN){ + win32.keyboard_input.key_input = 1; + win32.keyboard_input.key_code = 1; + } + }break; + + case VK_BACK: + { + Win32UpdateKey(press, release, &win32.keyboard_input.back); + + if (uMsg == WM_KEYDOWN){ + win32.keyboard_input.key_input = 1; + win32.keyboard_input.key_code = 0; + } + }break; + case VK_ESCAPE: + { + Win32UpdateKey(press, release, &win32.keyboard_input.pause); + }break; + case VK_F2: + { + Win32UpdateKey(press, release, &win32.keyboard_input.toggle_edit_mode); + }break; + case VK_SHIFT: break; + case VK_CONTROL: + { +#if 0 + if (press){ + win32.toggle_full_screen = 1; + } +#endif + }break; + case VK_MENU: + { + win32.first = 1; + }break; /*ALT*/ + + default: + { + if ((char)wParam == win32.btn1){ + Win32UpdateKey(press, release, &win32.keyboard_input.button[1]); + } + + if ((char)wParam == win32.btn2){ + Win32UpdateKey(press, release, &win32.keyboard_input.button[2]); + } + + if (uMsg == WM_KEYDOWN){ + if ((wParam >= '0' && wParam <= '9') || + (wParam >= 'A' && wParam <= 'Z')){ + win32.keyboard_input.key_input = 1; + win32.keyboard_input.key_code = (char)wParam; + } + } + }break; + } + }break; + + case WM_LBUTTONDOWN: + { + win32.mx = LOWORD(lParam); + win32.my = HIWORD(lParam); + }break; + + case WM_LBUTTONUP: + { + win32.mx = LOWORD(lParam); + win32.my = HIWORD(lParam); + }break; + + case WM_RBUTTONDOWN: + { + win32.mx = LOWORD(lParam); + win32.my = HIWORD(lParam); + }break; + + case WM_RBUTTONUP: + { + win32.mx = LOWORD(lParam); + win32.my = HIWORD(lParam); + }break; + + case WM_MOUSEMOVE: + { + win32.mx = LOWORD(lParam); + win32.my = HIWORD(lParam); + }break; + + case WM_SIZE: + { + i32 w, h; + w = LOWORD(lParam); + h = HIWORD(lParam); + render_set_screen_size(&win32.render_vars, w, h, win32.pref_game_width, win32.pref_game_height); + }break; + + case WM_GETMINMAXINFO: + { + MINMAXINFO* mmi = (MINMAXINFO*)lParam; + mmi->ptMinTrackSize.x = win32.min_width; + mmi->ptMinTrackSize.y = win32.min_height; + return 0; + } + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); AllowLocal(hdc); + //win32.render_vars.hdc = hdc; + render_redraw_screen(&win32.render_vars); + EndPaint(hwnd, &ps); + }break; + + case WM_CLOSE: + case WM_DESTROY: + { + win32.keep_playing = 0; + }break; + + case WM_NCLBUTTONDOWN: + { + DWORD status; + win32.sound_buffer->GetStatus(&status); + if(status & DSBSTATUS_PLAYING){ + win32.sound_buffer->Stop(); + } + result = DefWindowProc(hwnd, uMsg, wParam, lParam); + }break; + + case WM_ENTERSIZEMOVE: + { + DWORD status; + win32.sound_buffer->GetStatus(&status); + if(status & DSBSTATUS_PLAYING){ + win32.sound_buffer->Stop(); + } + }break; + + case WM_EXITSIZEMOVE: + { + DWORD status; + win32.sound_buffer->GetStatus(&status); + if(!(status & DSBSTATUS_PLAYING)){ + win32.sound_buffer->Play(0, 0, DSBPLAY_LOOPING); + } + }break; + + default: + { + result = DefWindowProc(hwnd, uMsg, wParam, lParam); + }break; + } + return result; +} + +internal i64 +Win32GetTime(){ + i64 result = 0; + LARGE_INTEGER time; + if (QueryPerformanceCounter(&time)){ + result = (i64)time.QuadPart; + } + return result; +} + +internal bool32 +Win32GoFullscreen(){ + HMONITOR monitor = MonitorFromWindow(win32.winhandle, MONITOR_DEFAULTTOPRIMARY); + + if (!monitor){ + WINERROR("Failed to get monitor handle"); + return 0; + } + + MONITORINFO monitor_info; + monitor_info.cbSize = sizeof(monitor_info); + + if (!GetMonitorInfo(monitor, &monitor_info)){ + WINERROR("Failed to get monitor information"); + return 0; + } + + if (!GetWindowRect(win32.winhandle, &win32.prev_winrect)){ + WINERROR("Failed to save previous window size"); + } + else{ + win32.has_prev_winrect = 1; + } + + SetWindowLongPtr(win32.winhandle, GWL_STYLE, + WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE); + MoveWindow(win32.winhandle, + monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, + monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, + monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, + TRUE); + + win32.is_fullscreen = 1; + + return 1; +} + +internal bool32 +Win32GoWindowed(){ + SetWindowLongPtr(win32.winhandle, GWL_STYLE, + WS_OVERLAPPEDWINDOW | WS_VISIBLE); + if (win32.has_prev_winrect){ + MoveWindow(win32.winhandle, + win32.prev_winrect.left, win32.prev_winrect.top, + win32.prev_winrect.right - win32.prev_winrect.left, + win32.prev_winrect.bottom - win32.prev_winrect.top, + TRUE); + } + else{ + + RECT winrect = {}; + + winrect.right = win32.min_game_width; + winrect.bottom = win32.min_game_height; + + if (!AdjustWindowRect(&winrect, WS_OVERLAPPEDWINDOW, 0)){ + return 0; + } + + i32 win_x, win_y; + win_x = 0; + win_y = 0; + + // TODO(allen): This is now duplicated... so fix that? + HMONITOR monitor = MonitorFromWindow(win32.winhandle, MONITOR_DEFAULTTOPRIMARY); + + if (!monitor){ + WINERROR("Failed to get monitor handle"); + } + else{ + + MONITORINFO monitor_info; + monitor_info.cbSize = sizeof(monitor_info); + + if (!GetMonitorInfo(monitor, &monitor_info)){ + WINERROR("Failed to get monitor information"); + } + else{ + // TODO: This will be easier when the get monitor info stuff + // is pulled into it's own function so that it is only a matter + // of seeing the success or failure of that one call. + win_x = + monitor_info.rcMonitor.right - + monitor_info.rcMonitor.left - + (winrect.right - winrect.left); + + win_y = + monitor_info.rcMonitor.bottom - + monitor_info.rcMonitor.top - + (winrect.bottom - winrect.top); + + win_x /= 2; + win_y /= 2; + } + } + + MoveWindow(win32.winhandle, + win_x, win_y, + winrect.right - winrect.left, + winrect.bottom - winrect.top, + TRUE); + } + return 1; +} + +int __stdcall +WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow){ + win32 = {}; + + win32.btn1 = 'Z'; + win32.btn2 = 'X'; + + win32.update_hz = 30.f; + win32.msecond_per_frame = (i64)(1000.f/win32.update_hz); + win32.min_game_width = 800; + win32.min_game_height = 600; + win32.pref_game_width = 800; + win32.pref_game_height = 600; + + WNDCLASSEX winclass = {}; + winclass.cbSize = sizeof(WNDCLASSEX); +#if RENDER_MODE == SOFTWARE + winclass.style = CS_HREDRAW | CS_VREDRAW; +#elif RENDER_MODE == OPENGL + winclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; +#endif + winclass.lpfnWndProc = (WNDPROC)&Win32Proc; + winclass.hInstance = hInstance; + winclass.hIcon = 0; // TODO(allen): Setup this icon! + winclass.hCursor = 0; + winclass.lpszClassName = "TMUND-win-class"; + winclass.hIconSm = 0; + +#if !DISABLE_CONTROLLER + Win32InputSetup(); +#endif + + ATOM classatom; + classatom = RegisterClassEx(&winclass); + + if (!classatom){ + WINERROR("Failed to setup window class"); + return 0; + } + + RECT winrect = {}; + + winrect.right = win32.min_game_width; + winrect.bottom = win32.min_game_height; + + if (!AdjustWindowRect(&winrect, WS_OVERLAPPEDWINDOW, 0)){ + WINERROR("Failed to compute window size"); + return 0; + } + + HWND winhandle; + winhandle = CreateWindow(winclass.lpszClassName, + "THE MOST UNDEAD", //NOTE(allen): Title + WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE + , + CW_USEDEFAULT, CW_USEDEFAULT, + winrect.right - winrect.left, + winrect.bottom - winrect.top, + 0, 0, hInstance, 0 + ); + + win32.min_width = winrect.right - winrect.left; + win32.min_height = winrect.bottom - winrect.top; + + if (!winhandle){ + WINERROR("Failed to setup window"); + return 0; + } + + win32.winhandle = winhandle; + win32.render_vars.hdc = GetDC(win32.winhandle); + + render_startup(&win32.render_vars); + //ReleaseDC(win32.winhandle, win32.render_vars.hdc); + + GetClientRect(winhandle, &winrect); + + render_set_screen_size(&win32.render_vars, + winrect.right - winrect.left, + winrect.bottom - winrect.top, + win32.min_game_width, win32.min_game_height); + +#if RENDER_MODE == SOFTWARE + ShowWindow(win32.winhandle, SW_SHOW); + UpdateWindow(win32.winhandle); +#endif + + win32.samples_per_second = 48000; + win32.bytes_per_sample = sizeof(i16)*2; + win32.sound_buffer_size = win32.samples_per_second*win32.bytes_per_sample; + win32.sound_safety_margin = (i32) + (win32.samples_per_second*win32.bytes_per_sample/(win32.update_hz*3.f)); + win32.sound_safety_margin = LargeRoundUp(win32.sound_safety_margin, win32.bytes_per_sample); + Win32SoundSetup(win32.samples_per_second, win32.sound_buffer_size); + Win32ClearSound(); + win32.sound_buffer->Play(0, 0, DSBPLAY_LOOPING); + + win32.samples = (i16*)VirtualAlloc(0, win32.sound_buffer_size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + + if (!win32.samples){ + WINERROR("Failed to setup audio buffer memory"); + return 0; + } + + Game_Memory memory; + memory.size = Mbytes(256); + memory.data = VirtualAlloc(0, memory.size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + win32.memory = memory; + + bool32 good_sound = 0; + win32.keep_playing = 1; + timeBeginPeriod(1); + win32.first = 1; + MSG msg; + i64 prev_work_time = Win32GetTime(); + i64 flip_time = prev_work_time; + i64 start_time = prev_work_time; + i64 end_time = prev_work_time; + AllowLocal(start_time); + AllowLocal(end_time); + + while (win32.keep_playing){ + win32.toggle_full_screen = 0; + win32.keyboard_input.key_input = 0; + while (PeekMessageA(&msg, winhandle, 0, 0, PM_REMOVE)){ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + +#if !DISABLE_CONTROLLER + Win32ControllerData controller = {}; + if (win32.xin.is_valid){ + // TODO(allen): Multiple controller support? + Win32ControllerGet(&controller, 0); + } + if (controller.plugged_in){ + if (controller.lstick_x != 0.f || + controller.lstick_y != 0.f){ + win32.controller_input.is_analog = 1; + win32.controller_input.analog.stick_x = controller.lstick_x; + win32.controller_input.analog.stick_y = -controller.lstick_y; + } + else{ + win32.controller_input.is_analog = 0; + win32.controller_input.digital.right = controller.right; + win32.controller_input.digital.left = controller.left; + win32.controller_input.digital.up = controller.up; + win32.controller_input.digital.down = controller.down; + } + + win32.controller_input.button[0] = controller.a; + win32.controller_input.button[1] = controller.b; + win32.controller_input.button[2] = controller.x; + win32.controller_input.button[3] = controller.y; + win32.controller_input.button[4] = controller.left_shoulder; + win32.controller_input.button[5] = controller.right_shoulder; + win32.controller_input.button[6] = controller.ltrigger; + win32.controller_input.button[7] = controller.rtrigger; + + win32.controller_input.pause = controller.start; + win32.controller_input.back = controller.back; + } +#endif + + Game_Step_Target step_target; + step_target.render = render_get_render_target(&win32.render_vars); + step_target.can_fullscreen = 1; + step_target.is_fullscreen = &win32.is_fullscreen; + step_target.audio_samples_per_second = win32.samples_per_second; + step_target.second_per_frame = 1.f/win32.update_hz; + step_target.btn1 = win32.btn1; + step_target.btn2 = win32.btn2; + + bool32 was_fullscreen = win32.is_fullscreen; + +#define ComposeControls(name) input.name = win32.keyboard_input.name || win32.controller_input.name + + Game_Input input; + if (win32.controller_input.is_analog){ + input.is_analog = 1; + input.analog.stick_x = win32.controller_input.analog.stick_x; + input.analog.stick_y = win32.controller_input.analog.stick_y; + } + else{ + input.is_analog = 0; + ComposeControls(digital.up); + ComposeControls(digital.down); + ComposeControls(digital.left); + ComposeControls(digital.right); + } + + ComposeControls(button[0]); + ComposeControls(button[1]); + ComposeControls(button[2]); + ComposeControls(button[3]); + ComposeControls(button[4]); + ComposeControls(button[5]); + ComposeControls(button[6]); + ComposeControls(button[7]); + + ComposeControls(pause); + ComposeControls(back); + + ComposeControls(toggle_edit_mode); + + input.key_input = win32.keyboard_input.key_input; + input.key_code = win32.keyboard_input.key_code; + + if (game_step(step_target, input, memory, win32.first)){ + win32.keep_playing = 0; + } + + if (win32.toggle_full_screen){ + win32.is_fullscreen = !was_fullscreen; + } + + win32.first = 0; + + if (win32.is_fullscreen && !was_fullscreen){ + if (!Win32GoFullscreen()){ + win32.is_fullscreen = 0; + } + } + else if (!win32.is_fullscreen && was_fullscreen){ + Win32GoWindowed(); + } + + i64 audio_time = Win32GetTime(); + i64 time_ellapsed_before_audio = audio_time - flip_time; + + DWORD play_cursor, write_cursor; + if (win32.sound_buffer->GetCurrentPosition(&play_cursor, &write_cursor) == DS_OK){ + if (!good_sound){ + win32.sound_sample_index = write_cursor / win32.bytes_per_sample; + good_sound = 1; + } + + DWORD write_start = + ((win32.sound_sample_index*win32.bytes_per_sample) % win32.sound_buffer_size); + + DWORD expected_bytes_per_frame = (DWORD) + (win32.bytes_per_sample*win32.samples_per_second*win32.msecond_per_frame/1000); + + i64 useconds_til_flip = (win32.msecond_per_frame*1000 - time_ellapsed_before_audio); + DWORD expected_bytes_til_flip = (DWORD) + (win32.bytes_per_sample*win32.samples_per_second*useconds_til_flip/1000000); + + DWORD expected_frame_bounary = play_cursor + expected_bytes_til_flip; + + DWORD safe_cursor = write_cursor; + if (safe_cursor < play_cursor){ + safe_cursor += win32.sound_buffer_size; + } + + safe_cursor += win32.sound_safety_margin; + + bool32 low_latency = (safe_cursor < expected_frame_bounary); + + DWORD target = 0; + if (low_latency){ + target = (expected_frame_bounary + expected_bytes_per_frame); + } + else{ + target = (write_cursor + expected_bytes_per_frame + win32.sound_safety_margin); + } + target = target % win32.sound_buffer_size; + + DWORD write_size = 0; + if (write_start > target){ + write_size = win32.sound_buffer_size - write_start + target; + } + else{ + write_size = target - write_start; + } + + Game_Audio_Target audio_target; + audio_target.samples = win32.samples; + audio_target.count_per_channel = write_size / win32.bytes_per_sample; + audio_target.channels = 2; + audio_target.samples_per_second = win32.samples_per_second; + audio_target.second_per_frame = 1.f/win32.update_hz; + + game_fill_samples(audio_target, memory); + + Win32FillSound(write_start, write_size, win32.samples); + } + else{ + good_sound = 0; + } + + i64 work_time = Win32GetTime(); + i64 run_time = ((work_time - prev_work_time)/1000); + if (run_time < win32.msecond_per_frame){ + Sleep((DWORD)(win32.msecond_per_frame - run_time)); + + work_time = Win32GetTime(); + if ((DWORD)((work_time - prev_work_time)/1000) < win32.msecond_per_frame){ + // NOTE(allen): MINOR - sleep did not work + } + while ((DWORD)((work_time - prev_work_time)/1000) < win32.msecond_per_frame){ + work_time = Win32GetTime(); + } + } + else{ + // NOTE(allen): FAIL - missed frame rate + int x = 0; + AllowLocal(x); + } + prev_work_time = Win32GetTime(); + + //win32.render_vars.hdc = GetDC(win32.winhandle); + render_redraw_screen(&win32.render_vars); + //ReleaseDC(win32.winhandle, win32.render_vars.hdc); + + flip_time = Win32GetTime(); + } + + // TODO(allen): Must we do this? We know we're ending, seems like + // this might be handled by OS just fine. + //win32.render_vars.hdc = GetDC(win32.winhandle); + render_shutdown(&win32.render_vars); + //ReleaseDC(win32.winhandle, win32.render_vars.hdc); + + return 0; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/lang.cpp b/test_data/lots_of_files/lang.cpp new file mode 100644 index 0000000..24808a3 --- /dev/null +++ b/test_data/lots_of_files/lang.cpp @@ -0,0 +1,177 @@ +/* + * Experiments with language/layout stuff for win32. + * 04.01.2016 (dd.mm.yyyy) + * Allen Webster + */ + +// TOP + +#include <Windows.h> + +struct Win32Vars{ + int keep_playing; + int controls[3]; +} win32; + +LRESULT WinCallback( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ){ + LRESULT result = 0; + switch(uMsg){ + case WM_QUIT: + case WM_DESTROY: + { + win32.keep_playing = 0; + }break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: + { + switch (wParam){ + case VK_SHIFT:case VK_LSHIFT:case VK_RSHIFT: + case VK_CONTROL:case VK_LCONTROL:case VK_RCONTROL: + case VK_MENU:case VK_LMENU:case VK_RMENU: + break; + + default: + { + UINT vk = (UINT)wParam; + UINT scan = (UINT)((lParam >> 16) & 0x7F); + BYTE bytes[256]; + WORD x; + memset(bytes, 0, 256); + GetKeyboardState(bytes); + ToAscii(vk, scan, bytes, &x, 0); + int a = 0, b = 1, c = 2; + a = x&1 + c; + b *= a + x; + }break; + } + }break; + + case WM_SETFOCUS: + { + win32.controls[0] = 0; + win32.controls[1] = 0; + win32.controls[2] = 0; + }break; + + case WM_INPUT: + { + char buffer[sizeof(RAWINPUT)] = {}; + UINT size = sizeof(RAWINPUT); + GetRawInputData((HRAWINPUT)(lParam), RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)); + + RAWINPUT *rawinput = (RAWINPUT*)(buffer); + if (rawinput->header.dwType == RIM_TYPEKEYBOARD){ + RAWKEYBOARD *raw = &rawinput->data.keyboard; + UINT vk = raw->VKey; + UINT flags = raw->Flags; + UINT down = !(flags & 1); + + if (vk != 255){ + if (vk == VK_SHIFT){ + win32.controls[0] = down; + } + else if (vk == VK_CONTROL){ + win32.controls[1] = down; + } + else if (vk == VK_MENU){ + win32.controls[2] = down; + } + } + } + + result = DefWindowProcA(hwnd, uMsg, wParam, lParam); + }break; + + default: + { + result = DefWindowProcA(hwnd, uMsg, wParam, lParam); + }break; + } + return(result); +} + +DWORD UpdateLoop(LPVOID lpParameter){ + for (;win32.keep_playing;){ + } + return(0); +} + +int WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow + ){ + WNDCLASS winclass = {}; + winclass.style = CS_HREDRAW | CS_VREDRAW; + winclass.lpfnWndProc = WinCallback; + winclass.hInstance = hInstance; + winclass.lpszClassName = "lang-test-class"; + + ATOM winclassatom = RegisterClassA(&winclass); + + if (winclassatom == 0){ + return(1); + } + + HWND window = CreateWindowA( + "lang-test-class", + "lang test window", + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + CW_USEDEFAULT, + CW_USEDEFAULT, + 400, + 400, + 0, + 0, + hInstance, + 0); + + if (window == 0){ + return(2); + } + + RAWINPUTDEVICE device; + device.usUsagePage = 0x1; + device.usUsage = 0x6; + device.dwFlags = 0; + device.hwndTarget = window; + RegisterRawInputDevices(&device, 1, sizeof(device)); + + ShowWindow(window, SW_SHOW); + + DWORD message_thread_id; + HANDLE message_thread_handle = + CreateThread(0, + 0, + UpdateLoop, + 0, + CREATE_SUSPENDED, + &message_thread_id); + + for (win32.keep_playing = 1, ResumeThread(message_thread_handle); + win32.keep_playing;){ + + MSG msg; + GetMessage(&msg, 0, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + + } + + CloseHandle(message_thread_handle); + + return(0); +} + +// BOTTOM + + diff --git a/test_data/lots_of_files/lang_bar_problem.cpp b/test_data/lots_of_files/lang_bar_problem.cpp new file mode 100644 index 0000000..f0b66c9 --- /dev/null +++ b/test_data/lots_of_files/lang_bar_problem.cpp @@ -0,0 +1,141 @@ +/* + * Experiments with language/layout stuff for win32. + * 04.01.2016 (dd.mm.yyyy) + * Allen Webster + */ + +// TOP + +// NOTE(allen): This is a bare bones window opening and message pumping +// application. I created it to search for a way to get the language bar +// to behave correctly when my window has focus. As of the 4th of January, +// I have not yet found the solution. +// +// Log of Insanity +// DAY 2: +// Thanks to "insofaras" from the handmade_hero chat for finding the solution +// to this problem. Turns out the messages for the language bar were being blocked +// by my process because my reading of some MSDN documentation was too hasty. Go +// to GetMessage to see a full description of the problem I was having. +// +// +// DAY 1: +// I looked around MSDN for a while, reading about lots of layout manipulating +// functions. I also found the WM_INPUTLANGECHANGE message, but that message is +// apparently passed AFTER the language is changed, and the problem is that I can't +// change the languge using the GUI, so it doesn't seem like the right direction. +// +// I also looked around the emacs source and tried matching some of the settings +// used there including WS_CLIPCHILDREN, and I tried using SetCapture/ReleaseCapture +// similarly to emacs code but none of that fixed the problem either. + +#include <Windows.h> + +struct Win32Vars{ + int keep_playing; +} win32; + +LRESULT WinCallback( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ){ + LRESULT result = 0; + switch(uMsg){ + case WM_QUIT: + case WM_DESTROY: + { + win32.keep_playing = 0; + }break; + + // NOTE(allen): This was here to investigate the effects of capturing and especially + // releasing mouse input, I have blocked them out for now because they didn't help + // and I might want other messages of the same type in the future. +#if 0 + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + { + SetCapture(hwnd); + }break; + + case WM_LBUTTONUP: + case WM_RBUTTONUP: + { + ReleaseCapture(); + }break; +#endif + + default: + { + result = DefWindowProcW(hwnd, uMsg, wParam, lParam); + }break; + } + return(result); +} + +int WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow + ){ + WNDCLASS winclass = {}; + winclass.style = CS_HREDRAW | CS_VREDRAW; + winclass.lpfnWndProc = WinCallback; + winclass.hInstance = hInstance; + winclass.lpszClassName = "lang-test-class"; + + ATOM winclassatom = RegisterClassA(&winclass); + + if (winclassatom == 0){ + return(1); + } + + HWND window = CreateWindowA( + "lang-test-class", + "lang test window", + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + CW_USEDEFAULT, + CW_USEDEFAULT, + 400, + 400, + 0, + 0, + hInstance, + 0); + + if (window == 0){ + return(2); + } + + ShowWindow(window, SW_SHOW); + + // NOTE(allen): This is here to investigate what happens when the layout + // is changed in code. On Windows 7 I observed the language bar updating what + // it displayed, but only if I dragged the window over the bar, or did something else + // to force it to redraw. If you only have one layout loaded, nothing will happen here. + HKL locales[2]; + int locale_count = GetKeyboardLayoutList(2, locales); + if (locale_count > 1){ + ActivateKeyboardLayout(locales[1], 0); + } + + for (win32.keep_playing = 1; win32.keep_playing;){ + MSG msg; + // NOTE(allen): This turns out to be the crucial point. The second parameter filters out + // messages by a window handle, and it turns out the language bar is one of those things + // that needs to process input through the active process but the messages aren't tied + // to your main window, so those messages get blocked if you put the handle there. Long + // story short: put a 0 there. + GetMessage(&msg, 0, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return(0); +} + +// BOTTOM + + diff --git a/test_data/lots_of_files/lang_raw_input.cpp b/test_data/lots_of_files/lang_raw_input.cpp new file mode 100644 index 0000000..a3da3a1 --- /dev/null +++ b/test_data/lots_of_files/lang_raw_input.cpp @@ -0,0 +1,157 @@ +/* + * Experiments with language/layout stuff for win32. + * 04.01.2016 (dd.mm.yyyy) + * Allen Webster + */ + +// TOP + +#include <Windows.h> + +struct Win32Vars{ + int keep_playing; + int controls[3]; +} win32; + +LRESULT WinCallback( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ){ + LRESULT result = 0; + switch(uMsg){ + case WM_QUIT: + case WM_DESTROY: + { + win32.keep_playing = 0; + }break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: + { + switch (wParam){ + case VK_SHIFT:case VK_LSHIFT:case VK_RSHIFT: + case VK_CONTROL:case VK_LCONTROL:case VK_RCONTROL: + case VK_MENU:case VK_LMENU:case VK_RMENU: + break; + + default: + { + UINT vk = (UINT)wParam; + UINT scan = (UINT)((lParam >> 16) & 0x7F); + BYTE bytes[256]; + WORD x; + memset(bytes, 0, 256); + GetKeyboardState(bytes); + ToAscii(vk, scan, bytes, &x, 0); + int a = 0, b = 1, c = 2; + a = x&1 + c; + b *= a + x; + }break; + } + }break; + + case WM_SETFOCUS: + { + win32.controls[0] = 0; + win32.controls[1] = 0; + win32.controls[2] = 0; + }break; + + case WM_INPUT: + { + char buffer[sizeof(RAWINPUT)] = {}; + UINT size = sizeof(RAWINPUT); + GetRawInputData((HRAWINPUT)(lParam), RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)); + + RAWINPUT *rawinput = (RAWINPUT*)(buffer); + if (rawinput->header.dwType == RIM_TYPEKEYBOARD){ + RAWKEYBOARD *raw = &rawinput->data.keyboard; + UINT vk = raw->VKey; + UINT flags = raw->Flags; + UINT down = !(flags & 1); + + if (vk != 255){ + if (vk == VK_SHIFT){ + win32.controls[0] = down; + } + else if (vk == VK_CONTROL){ + win32.controls[1] = down; + } + else if (vk == VK_MENU){ + win32.controls[2] = down; + } + } + } + + result = DefWindowProcA(hwnd, uMsg, wParam, lParam); + }break; + + default: + { + result = DefWindowProcA(hwnd, uMsg, wParam, lParam); + }break; + } + return(result); +} + +int WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow + ){ + WNDCLASS winclass = {}; + winclass.style = CS_HREDRAW | CS_VREDRAW; + winclass.lpfnWndProc = WinCallback; + winclass.hInstance = hInstance; + winclass.lpszClassName = "lang-test-class"; + + ATOM winclassatom = RegisterClassA(&winclass); + + if (winclassatom == 0){ + return(1); + } + + HWND window = CreateWindowA( + "lang-test-class", + "lang test window", + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + CW_USEDEFAULT, + CW_USEDEFAULT, + 400, + 400, + 0, + 0, + hInstance, + 0); + + if (window == 0){ + return(2); + } + + RAWINPUTDEVICE device; + device.usUsagePage = 0x1; + device.usUsage = 0x6; + device.dwFlags = 0; + device.hwndTarget = window; + RegisterRawInputDevices(&device, 1, sizeof(device)); + + ShowWindow(window, SW_SHOW); + + for (win32.keep_playing = 1; win32.keep_playing;){ + MSG msg; + GetMessage(&msg, 0, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return(0); +} + +// BOTTOM + + diff --git a/test_data/lots_of_files/large_sum.cpp b/test_data/lots_of_files/large_sum.cpp new file mode 100644 index 0000000..4c26a2f --- /dev/null +++ b/test_data/lots_of_files/large_sum.cpp @@ -0,0 +1,164 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +#ifdef EULER_PROBLEM +// BUILDING AREA: +typedef long long bigint ; + +struct Euler_In{}; + +struct Euler_Result{ + bigint number; +}; + +static const int DIGITS = 50; + +static char* BIG_NUMS[] = { + "37107287533902102798797998220837590246510135740250", + "46376937677490009712648124896970078050417018260538", + "74324986199524741059474233309513058123726617309629", + "91942213363574161572522430563301811072406154908250", + "23067588207539346171171980310421047513778063246676", + "89261670696623633820136378418383684178734361726757", + "28112879812849979408065481931592621691275889832738", + "44274228917432520321923589422876796487670272189318", + "47451445736001306439091167216856844588711603153276", + "70386486105843025439939619828917593665686757934951", + "62176457141856560629502157223196586755079324193331", + "64906352462741904929101432445813822663347944758178", + "92575867718337217661963751590579239728245598838407", + "58203565325359399008402633568948830189458628227828", + "80181199384826282014278194139940567587151170094390", + "35398664372827112653829987240784473053190104293586", + "86515506006295864861532075273371959191420517255829", + "71693888707715466499115593487603532921714970056938", + "54370070576826684624621495650076471787294438377604", + "53282654108756828443191190634694037855217779295145", + "36123272525000296071075082563815656710885258350721", + "45876576172410976447339110607218265236877223636045", + "17423706905851860660448207621209813287860733969412", + "81142660418086830619328460811191061556940512689692", + "51934325451728388641918047049293215058642563049483", + "62467221648435076201727918039944693004732956340691", + "15732444386908125794514089057706229429197107928209", + "55037687525678773091862540744969844508330393682126", + "18336384825330154686196124348767681297534375946515", + "80386287592878490201521685554828717201219257766954", + "78182833757993103614740356856449095527097864797581", + "16726320100436897842553539920931837441497806860984", + "48403098129077791799088218795327364475675590848030", + "87086987551392711854517078544161852424320693150332", + "59959406895756536782107074926966537676326235447210", + "69793950679652694742597709739166693763042633987085", + "41052684708299085211399427365734116182760315001271", + "65378607361501080857009149939512557028198746004375", + "35829035317434717326932123578154982629742552737307", + "94953759765105305946966067683156574377167401875275", + "88902802571733229619176668713819931811048770190271", + "25267680276078003013678680992525463401061632866526", + "36270218540497705585629946580636237993140746255962", + "24074486908231174977792365466257246923322810917141", + "91430288197103288597806669760892938638285025333403", + "34413065578016127815921815005561868836468420090470", + "23053081172816430487623791969842487255036638784583", + "11487696932154902810424020138335124462181441773470", + "63783299490636259666498587618221225225512486764533", + "67720186971698544312419572409913959008952310058822", + "95548255300263520781532296796249481641953868218774", + "76085327132285723110424803456124867697064507995236", + "37774242535411291684276865538926205024910326572967", + "23701913275725675285653248258265463092207058596522", + "29798860272258331913126375147341994889534765745501", + "18495701454879288984856827726077713721403798879715", + "38298203783031473527721580348144513491373226651381", + "34829543829199918180278916522431027392251122869539", + "40957953066405232632538044100059654939159879593635", + "29746152185502371307642255121183693803580388584903", + "41698116222072977186158236678424689157993532961922", + "62467957194401269043877107275048102390895523597457", + "23189706772547915061505504953922979530901129967519", + "86188088225875314529584099251203829009407770775672", + "11306739708304724483816533873502340845647058077308", + "82959174767140363198008187129011875491310547126581", + "97623331044818386269515456334926366572897563400500", + "42846280183517070527831839425882145521227251250327", + "55121603546981200581762165212827652751691296897789", + "32238195734329339946437501907836945765883352399886", + "75506164965184775180738168837861091527357929701337", + "62177842752192623401942399639168044983993173312731", + "32924185707147349566916674687634660915035914677504", + "99518671430235219628894890102423325116913619626622", + "73267460800591547471830798392868535206946944540724", + "76841822524674417161514036427982273348055556214818", + "97142617910342598647204516893989422179826088076852", + "87783646182799346313767754307809363333018982642090", + "10848802521674670883215120185883543223812876952786", + "71329612474782464538636993009049310363619763878039", + "62184073572399794223406235393808339651327408011116", + "66627891981488087797941876876144230030984490851411", + "60661826293682836764744779239180335110989069790714", + "85786944089552990653640447425576083659976645795096", + "66024396409905389607120198219976047599490197230297", + "64913982680032973156037120041377903785566085089252", + "16730939319872750275468906903707539413042652315011", + "94809377245048795150954100921645863754710598436791", + "78639167021187492431995700641917969777599028300699", + "15368713711936614952811305876380278410754449733078", + "40789923115535562561142322423255033685442488917353", + "44889911501440648020369068063960672322193204149535", + "41503128880339536053299340368006977710650566631954", + "81234880673210146739058568557934581403627822703280", + "82616570773948327592232845941706525094512325230608", + "22918802058777319719839450180888072429661980811197", + "77158542502016545090413245809786882778948721859617", + "72107838435069186155435662884062257473692284509516", + "20849603980134001723930671666823555245252804609722", + "53503534226472524250874054075591789781264330331690" +}; + +static const int NUM_COUNT = (sizeof(BIG_NUMS) / sizeof(*BIG_NUMS)); + +inline Euler_Result euler_main(Euler_In in){ + int i=DIGITS-1,j,n; + bigint x=0,y,T=9999999999,m=1; + + while (i >= 0){ + y = 0; + for (j = 0; j < NUM_COUNT; ++j){ + n = BIG_NUMS[j][i] - '0'; + x += n; + } + x += y*m; + --i; + m *= 10; + while (x > T){ + m /= 10; + x /= 10; + } + } + + Euler_Result result; + result.number = x; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%llu\n", answer.number); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result; + + bigint a = 5537376230; + + printf("answer = %llu\n", a); + result = (a == answer.number); + + return result; +} +#endif + diff --git a/test_data/lots_of_files/largest_palindrome_product.cpp b/test_data/lots_of_files/largest_palindrome_product.cpp new file mode 100644 index 0000000..887a4ce --- /dev/null +++ b/test_data/lots_of_files/largest_palindrome_product.cpp @@ -0,0 +1,111 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +// generalization for this function is not so good + +// req: 0 <= a,b,c < 10 +inline int palindrom_5digit(int a, int b, int c){ + int result; + result = a*10001 + b*1010 + c*100; + return result; +} + +// req: 0 <= a,b,c < 10 +inline int palindrom_6digit(int a, int b, int c){ + int result; + result = a*100001 + b*10010 + c*1100; + return result; +} + +// req: 100 <= x < 999 +inline void extract_3digit(int x, int *a, int *b, int *c){ + int y; + y = x/10; + *c = (x - y*10); + + x = y; + y /= 10; + *b = (x - y*10); + + x = y; + y /= 10; + *a = (x - y*10); +} + +inline int extract_digit(int x, int *a){ + int y=x/10; + *a = (x - y*10); + return y; +} + +bool is_product_of_3digit_numbers(int n){ + bool result = 0; + for (int x = 100; x < 999; ++x){ + if (n % x == 0){ + int y = n / x; + if (y >= 100 && y < 1000){ + result = 1; + break; + } + } + } + return result; +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{}; + +struct Euler_Result{ + int largest_palindrome; +}; + +Euler_Result euler_main(Euler_In in){ + int palindrome = -1; + + // check 6 digit + for (int x = 999; x >= 100; --x){ + int a,b,c,p; + extract_3digit(x, &a, &b, &c); + p = palindrom_6digit(a,b,c); + if (is_product_of_3digit_numbers(p)){ + palindrome = p; + break; + } + } + + if (palindrome == -1){ + // check 5 digit + for (int x = 999; x >= 100; --x){ + int a,b,c,p; + extract_3digit(x, &a, &b, &c); + p = palindrom_5digit(a,b,c); + if (is_product_of_3digit_numbers(p)){ + palindrome = p; + break; + } + } + } + + Euler_Result result; + result.largest_palindrome = palindrome; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.largest_palindrome); +} + +#if 0 +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result = 1; + + return result; +} +#endif + +#endif diff --git a/test_data/lots_of_files/largest_prime_factor.cpp b/test_data/lots_of_files/largest_prime_factor.cpp new file mode 100644 index 0000000..22aeaaf --- /dev/null +++ b/test_data/lots_of_files/largest_prime_factor.cpp @@ -0,0 +1,66 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +unsigned long long largest_prime_factor(unsigned long long n){ + unsigned long long lpf, p, s, t; + lpf = 1; + p = 2; + s = 1; + t = n / p; + + while (p <= t){ + if (n % p == 0){ + lpf = p; + n /= p; + while (n % p == 0){ + n /= p; + } + } + p += s; + s = 2; + t = n / p; + } + + if (n > lpf){ + lpf = n; + } + + return lpf; +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{ + unsigned long long n; +}; + +struct Euler_Result{ + unsigned long long lpf; +}; + +static const unsigned long long NUMBER = 600851475143; + +Euler_Result euler_main(Euler_In in){ + Euler_Result result; + result.lpf = largest_prime_factor(NUMBER); + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%llu\n", answer.lpf); +} + +#if 0 +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result = 1; + + return result; +} +#endif + +#endif + diff --git a/test_data/lots_of_files/largest_product_in_grid.cpp b/test_data/lots_of_files/largest_product_in_grid.cpp new file mode 100644 index 0000000..5df66cb --- /dev/null +++ b/test_data/lots_of_files/largest_product_in_grid.cpp @@ -0,0 +1,207 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: + +int largest_product_in_series(int *series, int size, int prod_count, int stride = 1){ + int c=0,x=1,b=0,d,i; + for (i = 0; i < size; ++i){ + d = series[(i)*stride]; + if (d == 0){ + c = 0; + x = 1; + } + else{ + if (c < prod_count){ + x *= d; + ++c; + if (c == prod_count && x > b) b = x; + } + else{ + x /= series[(i - prod_count)*stride]; + x *= d; + if (x > b) b = x; + } + } + + } + return b; +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: + +struct Euler_In{}; + +struct Euler_Result{ + int product; +}; + +static int GRID[] = { + 8, 2, 22, 97, 38, 15, 0, 40, 0, 75, 4, 5, 7, 78, 52, 12, 50, 77, 91, 8, + 49, 49, 99, 40, 17, 81, 18, 57, 60, 87, 17, 40, 98, 43, 69, 48, 4, 56, 62, 0, + 81, 49, 31, 73, 55, 79, 14, 29, 93, 71, 40, 67, 53, 88, 30, 3, 49, 13, 36, 65, + 52, 70, 95, 23, 4, 60, 11, 42, 69, 24, 68, 56, 1, 32, 56, 71, 37, 2, 36, 91, + 22, 31, 16, 71, 51, 67, 63, 89, 41, 92, 36, 54, 22, 40, 40, 28, 66, 33, 13, 80, + 24, 47, 32, 60, 99, 3, 45, 2, 44, 75, 33, 53, 78, 36, 84, 20, 35, 17, 12, 50, + 32, 98, 81, 28, 64, 23, 67, 10, 26, 38, 40, 67, 59, 54, 70, 66, 18, 38, 64, 70, + 67, 26, 20, 68, 2, 62, 12, 20, 95, 63, 94, 39, 63, 8, 40, 91, 66, 49, 94, 21, + 24, 55, 58, 5, 66, 73, 99, 26, 97, 17, 78, 78, 96, 83, 14, 88, 34, 89, 63, 72, + 21, 36, 23, 9, 75, 0, 76, 44, 20, 45, 35, 14, 0, 61, 33, 97, 34, 31, 33, 95, + 78, 17, 53, 28, 22, 75, 31, 67, 15, 94, 3, 80, 4, 62, 16, 14, 9, 53, 56, 92, + 16, 39, 5, 42, 96, 35, 31, 47, 55, 58, 88, 24, 0, 17, 54, 24, 36, 29, 85, 57, + 86, 56, 0, 48, 35, 71, 89, 7, 5, 44, 44, 37, 44, 60, 21, 58, 51, 54, 17, 58, + 19, 80, 81, 68, 5, 94, 47, 69, 28, 73, 92, 13, 86, 52, 17, 77, 4, 89, 55, 40, + 4, 52, 8, 83, 97, 35, 99, 16, 7, 97, 57, 32, 16, 26, 26, 79, 33, 27, 98, 66, + 88, 36, 68, 87, 57, 62, 20, 72, 3, 46, 33, 67, 46, 55, 12, 32, 63, 93, 53, 69, + 4, 42, 16, 73, 38, 25, 39, 11, 24, 94, 72, 18, 8, 46, 29, 32, 40, 62, 76, 36, + 20, 69, 36, 41, 72, 30, 23, 88, 34, 62, 99, 69, 82, 67, 59, 85, 74, 4, 36, 16, + 20, 73, 35, 29, 78, 31, 90, 1, 74, 31, 49, 71, 48, 86, 81, 16, 23, 57, 5, 54, + 1, 70, 54, 71, 83, 51, 54, 69, 16, 92, 33, 48, 61, 43, 52, 1, 89, 19, 67, 48 +}; + +static const int W = 20; +static const int H = 20; + +#define GET_I(x,y) ((x)+W*(y)) + +Euler_Result euler_main(Euler_In in){ +#if 0 + int b=0,x,y,p; + + for (y = 0; y < H; ++y){ + p = largest_product_in_series(GRID + y*W, W, 4); + if (b<p) b=p; + } + + for (x = 0; x < W; ++x){ + p = largest_product_in_series(GRID + x, W, 4, W); + if (b<p) b=p; + } + + for (x = 0; x < W - 3; ++x){ + p = largest_product_in_series(GRID + x, 20 - x, 4, W+1); + if (b<p) b=p; + } + + for (y = 1; y < H - 3; ++y){ + p = largest_product_in_series(GRID + y*W, 20 - y, 4, W+1); + if (b<p) b=p; + } + + for (x = 0; x < W - 3; ++x){ + p = largest_product_in_series(GRID + x, 4 + x, 4, W-1); + if (b<p) b=p; + } + + for (y = 1; y < H - 3; ++y){ + p = largest_product_in_series(GRID + y*W + 16, 20 - y, 4, W-1); + if (b<p) b=p; + } +#endif + + int b=0,x,y,i,p; + + for (y = 0; y < H; ++y){ + for (x = 0; x < W - 3; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x+i,y)]; + } + if (p>b) b=p; + } + } + + for (y = 0; y < H - 3; ++y){ + for (x = 0; x < W; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x,y+i)]; + } + if (p>b) b=p; + } + } + + for (y = 0; y < H - 3; ++y){ + for (x = 0; x < W - 3; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x+i,y+i)]; + } + if (p>b) b=p; + } + } + + for (y = 0; y < H - 3; ++y){ + for (x = 0; x < W - 3; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x+3-i,y+i)]; + } + if (p>b) b=p; + } + } + + Euler_Result result; + result.product = b; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.product); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result = 1; + + int b=0,x,y,i,p; + + for (y = 0; y < H; ++y){ + for (x = 0; x < W - 3; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x+i,y)]; + } + if (p>b) b=p; + } + } + + for (y = 0; y < H - 3; ++y){ + for (x = 0; x < W; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x,y+i)]; + } + if (p>b) b=p; + } + } + + for (y = 0; y < H - 3; ++y){ + for (x = 0; x < W - 3; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x+i,y+i)]; + } + if (p>b) b=p; + } + } + + for (y = 0; y < H - 3; ++y){ + for (x = 0; x < W - 3; ++x){ + p = 1; + for (i = 0; i < 4; ++i){ + p *= GRID[GET_I(x+3-i,y+i)]; + } + if (p>b) b=p; + } + } + + printf("answer = %d\n", b); + result = (b == answer.product); + + return result; +} +#endif diff --git a/test_data/lots_of_files/largest_product_in_series.cpp b/test_data/lots_of_files/largest_product_in_series.cpp new file mode 100644 index 0000000..b2afa56 --- /dev/null +++ b/test_data/lots_of_files/largest_product_in_series.cpp @@ -0,0 +1,102 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +#ifdef EULER_PROBLEM +// BUILDING AREA: +typedef unsigned long long bigint; + +struct Euler_In{}; + +struct Euler_Result{ + bigint largest_product; +}; + +static const char DIGITS[] = +"73167176531330624919225119674426574742355349194934" +"96983520312774506326239578318016984801869478851843" +"85861560789112949495459501737958331952853208805511" +"12540698747158523863050715693290963295227443043557" +"66896648950445244523161731856403098711121722383113" +"62229893423380308135336276614282806444486645238749" +"30358907296290491560440772390713810515859307960866" +"70172427121883998797908792274921901699720888093776" +"65727333001053367881220235421809751254540594752243" +"52584907711670556013604839586446706324415722155397" +"53697817977846174064955149290862569321978468622482" +"83972241375657056057490261407972968652414535100474" +"82166370484403199890008895243450658541227588666881" +"16427171479924442928230863465674813919123162824586" +"17866458359124566529476545682848912883142607690042" +"24219022671055626321111109370544217506941658960408" +"07198403850962455444362981230987879927244284909188" +"84580156166097919133875499200524063689912560717606" +"05886116467109405077541002256983155200055935729725" +"71636269561882670428252483600823257530420752963450"; + +static const int DIGITS_SUB = 13; + +static const int DIGITS_LEN = sizeof(DIGITS)-1; + +Euler_Result euler_main(Euler_In in){ + char ds[DIGITS_LEN]; + bigint c=0,x=1,b=0,d; + for (int i = 0; i < DIGITS_LEN; ++i){ + d = ds[i] = DIGITS[i] - '0'; + if (d == 0){ + c = 0; + x = 1; + } + else{ + if (c < DIGITS_SUB){ + x *= d; + ++c; + if (c == DIGITS_SUB &&x > b){ + b = x; + } + } + else{ + x /= ds[i - DIGITS_SUB]; + x *= d; + if (x > b){ + b = x; + } + } + } + } + + Euler_Result result; + result.largest_product = b; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%llu\n", answer.largest_product); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result = 1; + + bigint x,b=0; + int stop = DIGITS_LEN - DIGITS_SUB + 1; + + for (int i = 0; i < stop; ++i){ + x = 1; + for (int j = 0; j < DIGITS_SUB; ++j){ + x *= (DIGITS[i+j] - '0'); + } + if (x > b){ + b = x; + } + } + + printf("answer = %llu\n", b); + result = (b == answer.largest_product); + + return result; +} +#endif + diff --git a/test_data/lots_of_files/limits.h b/test_data/lots_of_files/limits.h new file mode 100644 index 0000000..f464300 --- /dev/null +++ b/test_data/lots_of_files/limits.h @@ -0,0 +1,92 @@ +/*** +*limits.h - implementation dependent values +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Contains defines for a number of implementation dependent values +* which are commonly used in C programs. +* [ANSI] +* +* [Public] +* +****/ + +#pragma once + +#include <crtdefs.h> + +#ifndef _INC_LIMITS +#define _INC_LIMITS + +#define CHAR_BIT 8 /* number of bits in a char */ +#define SCHAR_MIN (-128) /* minimum signed char value */ +#define SCHAR_MAX 127 /* maximum signed char value */ +#define UCHAR_MAX 0xff /* maximum unsigned char value */ + +#ifndef _CHAR_UNSIGNED +#define CHAR_MIN SCHAR_MIN /* mimimum char value */ +#define CHAR_MAX SCHAR_MAX /* maximum char value */ +#else /* _CHAR_UNSIGNED */ +#define CHAR_MIN 0 +#define CHAR_MAX UCHAR_MAX +#endif /* _CHAR_UNSIGNED */ + +#define MB_LEN_MAX 5 /* max. # bytes in multibyte char */ +#define SHRT_MIN (-32768) /* minimum (signed) short value */ +#define SHRT_MAX 32767 /* maximum (signed) short value */ +#define USHRT_MAX 0xffff /* maximum unsigned short value */ +#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */ +#define INT_MAX 2147483647 /* maximum (signed) int value */ +#define UINT_MAX 0xffffffff /* maximum unsigned int value */ +#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */ +#define LONG_MAX 2147483647L /* maximum (signed) long value */ +#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */ +#define LLONG_MAX 9223372036854775807i64 /* maximum signed long long int value */ +#define LLONG_MIN (-9223372036854775807i64 - 1) /* minimum signed long long int value */ +#define ULLONG_MAX 0xffffffffffffffffui64 /* maximum unsigned long long int value */ + +#define _I8_MIN (-127i8 - 1) /* minimum signed 8 bit value */ +#define _I8_MAX 127i8 /* maximum signed 8 bit value */ +#define _UI8_MAX 0xffui8 /* maximum unsigned 8 bit value */ + +#define _I16_MIN (-32767i16 - 1) /* minimum signed 16 bit value */ +#define _I16_MAX 32767i16 /* maximum signed 16 bit value */ +#define _UI16_MAX 0xffffui16 /* maximum unsigned 16 bit value */ + +#define _I32_MIN (-2147483647i32 - 1) /* minimum signed 32 bit value */ +#define _I32_MAX 2147483647i32 /* maximum signed 32 bit value */ +#define _UI32_MAX 0xffffffffui32 /* maximum unsigned 32 bit value */ + +/* minimum signed 64 bit value */ +#define _I64_MIN (-9223372036854775807i64 - 1) +/* maximum signed 64 bit value */ +#define _I64_MAX 9223372036854775807i64 +/* maximum unsigned 64 bit value */ +#define _UI64_MAX 0xffffffffffffffffui64 + +#if _INTEGRAL_MAX_BITS >= 128 +/* minimum signed 128 bit value */ +#define _I128_MIN (-170141183460469231731687303715884105727i128 - 1) +/* maximum signed 128 bit value */ +#define _I128_MAX 170141183460469231731687303715884105727i128 +/* maximum unsigned 128 bit value */ +#define _UI128_MAX 0xffffffffffffffffffffffffffffffffui128 +#endif /* _INTEGRAL_MAX_BITS >= 128 */ + +#ifndef SIZE_MAX +#ifdef _WIN64 +#define SIZE_MAX _UI64_MAX +#else /* _WIN64 */ +#define SIZE_MAX UINT_MAX +#endif /* _WIN64 */ +#endif /* SIZE_MAX */ + +#if __STDC_WANT_SECURE_LIB__ +#ifndef RSIZE_MAX +#define RSIZE_MAX (SIZE_MAX >> 1) +#endif /* RSIZE_MAX */ +#endif /* __STDC_WANT_SECURE_LIB__ */ + + +#endif /* _INC_LIMITS */ diff --git a/test_data/lots_of_files/linux_4ed.cpp b/test_data/lots_of_files/linux_4ed.cpp new file mode 100644 index 0000000..3d0a9da --- /dev/null +++ b/test_data/lots_of_files/linux_4ed.cpp @@ -0,0 +1,2489 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 14.11.2015 + * + * Linux 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 "4coder_custom.cpp" + +#undef exec_command +#undef exec_command_keep_stack +#undef clear_parameters + +#include "4ed_system.h" +#include "4ed_rendering.h" +#include "4ed.h" + +#include <stdio.h> +#include <memory.h> +#include <math.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/cursorfont.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xfixes.h> +#include <GL/glx.h> +#include <GL/gl.h> +#include <GL/glext.h> +#include <xmmintrin.h> +#include <linux/fs.h> +//#include <X11/extensions/XInput2.h> +#include <linux/input.h> +#include <time.h> +#include <dlfcn.h> +#include <unistd.h> + +#include "4ed_internal.h" +#include "4ed_linux_keyboard.cpp" +#include "system_shared.h" + +#include <stdlib.h> +#include <locale.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <ucontext.h> + +//#define FRED_USE_FONTCONFIG 1 + +#if FRED_USE_FONTCONFIG +#include <fontconfig/fontconfig.h> +#endif + +struct Linux_Semaphore { + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + +struct Linux_Semaphore_Handle { + pthread_mutex_t *mutex_p; + pthread_cond_t *cond_p; +}; + +struct Linux_Coroutine { + Coroutine coroutine; + Linux_Coroutine *next; + ucontext_t ctx, yield_ctx; + stack_t stack; + b32 done; +}; + +struct Thread_Context{ + u32 job_id; + b32 running; + + Work_Queue *queue; + u32 id; + pthread_t handle; +}; + +struct Thread_Group{ + Thread_Context *threads; + i32 count; +}; + +struct Linux_Vars{ + Display *XDisplay; + Window XWindow; + Render_Target target; + + XIM input_method; + XIMStyle input_style; + XIC input_context; + + Key_Input_Data key_data; + Mouse_State mouse_data; + + String clipboard_contents; + String clipboard_outgoing; + + Atom atom_CLIPBOARD; + Atom atom_UTF8_STRING; + Atom atom_NET_WM_STATE; + Atom atom_NET_WM_STATE_MAXIMIZED_HORZ; + Atom atom_NET_WM_STATE_MAXIMIZED_VERT; + + b32 has_xfixes; + int xfixes_selection_event; + + Application_Mouse_Cursor cursor; + +#if FRED_USE_FONTCONFIG + FcConfig *fontconfig; +#endif + + void *app_code; + void *custom; + + Thread_Memory *thread_memory; + Thread_Group groups[THREAD_GROUP_COUNT]; + Linux_Semaphore thread_locks[THREAD_GROUP_COUNT]; + pthread_mutex_t locks[LOCK_COUNT]; + + Plat_Settings settings; + System_Functions *system; + App_Functions app; + Custom_API custom_api; + b32 first; + b32 redraw; + b32 vsync; + +#if FRED_INTERNAL + Sys_Bubble internal_bubble; +#endif + + Font_Load_System fnt; + + Linux_Coroutine coroutine_data[2]; + Linux_Coroutine *coroutine_free; +}; + +#define LINUX_MAX_PASTE_CHARS 0x10000L +#define FPS 60 +#define frame_useconds (1000000 / FPS) + +#define DBG_FN do { fprintf(stderr, "Fn called: %s\n", __PRETTY_FUNCTION__); } while(0) + +globalvar Linux_Vars linuxvars; +globalvar Application_Memory memory_vars; +globalvar Exchange exchange_vars; + +#define LinuxGetMemory(size) LinuxGetMemory_(size, __LINE__, __FILE__) + +internal void* +LinuxGetMemory_(i32 size, i32 line_number, char *file_name){ + // TODO(allen): Implement without stdlib.h + void *result = 0; + + Assert(size != 0); + +#if FRED_INTERNAL + 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; + + // TODO(allen): make Sys_Bubble list thread safe + insert_bubble(&linuxvars.internal_bubble, bubble); + result = bubble + 1; + +#else + result = malloc(size); +#endif + + return(result); +} + +internal void +LinuxFreeMemory(void *block){ + // TODO(allen): Implement without stdlib.h + + if (block){ +#if FRED_INTERNAL + Sys_Bubble *bubble; + + bubble = (Sys_Bubble*)block; + --bubble; + Assert((bubble->flags & MEM_BUBBLE_DEBUG_MASK) == MEM_BUBBLE_SYS_DEBUG); + + // TODO(allen): make Sys_Bubble list thread safe + remove_bubble(bubble); + + free(bubble); +#else + free(block); +#endif + } +} + +internal Partition +LinuxScratchPartition(i32 size){ + Partition part; + void *data; + data = LinuxGetMemory(size); + part = partition_open(data, size); + return(part); +} + +internal void +LinuxScratchPartitionGrow(Partition *part, i32 new_size){ + void *data; + if (new_size > part->max){ + data = LinuxGetMemory(new_size); + memcpy(data, part->base, part->pos); + LinuxFreeMemory(part->base); + part->base = (u8*)data; + } +} + +internal void +LinuxScratchPartitionDouble(Partition *part){ + LinuxScratchPartitionGrow(part, part->max*2); +} + +internal +Sys_Get_Memory_Sig(system_get_memory_){ + return(LinuxGetMemory_(size, line_number, file_name)); +} + +internal +Sys_Free_Memory_Sig(system_free_memory){ + LinuxFreeMemory(block); +} + +internal void +LinuxStringDup(String* str, void* data, size_t size){ + if(str->memory_size < size){ + if(str->str){ + LinuxFreeMemory(str->str); + } + str->memory_size = size; + str->str = (char*)LinuxGetMemory(size); + //TODO(inso): handle alloc failure case + } + + str->size = size; + memcpy(str->str, data, size); +} + +#if (defined(_BSD_SOURCE) || defined(_SVID_SOURCE)) +#define TimeBySt +#endif +#if (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) +#define TimeBySt +#endif + +#ifdef TimeBySt +#define nano_mtime_field st_mtim.tv_nsec +#undef TimeBySt +#else +#define nano_mtime_field st_mtimensec +#endif + +Sys_File_Time_Stamp_Sig(system_file_time_stamp){ + struct stat info; + i64 nanosecond_timestamp; + u64 result; + + stat(filename, &info); + nanosecond_timestamp = info.nano_mtime_field; + if (nanosecond_timestamp != 0){ + result = (u64)(nanosecond_timestamp / 1000); + } + else{ + result = (u64)(info.st_mtime * 1000000); + } + + return(result); +} + +// TODO(allen): DOES THIS AGREE WITH THE FILESTAMP TIMES? +// NOTE(inso): I don't think so, CLOCK_MONOTONIC is an arbitrary number +Sys_Time_Sig(system_time){ + struct timespec spec; + u64 result; + + clock_gettime(CLOCK_MONOTONIC, &spec); + result = (spec.tv_sec * 1000000) + (spec.tv_nsec / 1000); + + return(result); +} + +Sys_Set_File_List_Sig(system_set_file_list){ + DIR *d; + struct dirent *entry; + char *fname, *cursor, *cursor_start; + File_Info *info_ptr; + i32 count, file_count, size, required_size; + + terminate_with_null(&directory); + + d = opendir(directory.str); + if (d){ + count = 0; + file_count = 0; + for (entry = readdir(d); + entry != 0; + entry = readdir(d)){ + fname = entry->d_name; + if(fname[0] == '.' && (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0))){ + continue; + } + ++file_count; + for (size = 0; fname[size]; ++size); + count += size + 1; + } + + required_size = count + file_count * sizeof(File_Info); + if (file_list->block_size < required_size){ + system_free_memory(file_list->block); + file_list->block = system_get_memory(required_size); + } + + file_list->infos = (File_Info*)file_list->block; + cursor = (char*)(file_list->infos + file_count); + + rewinddir(d); + info_ptr = file_list->infos; + for (entry = readdir(d); + entry != 0; + entry = readdir(d)){ + fname = entry->d_name; + if(fname[0] == '.' && (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0))){ + continue; + } + cursor_start = cursor; + for (; *fname; ) *cursor++ = *fname++; + +#ifdef _DIRENT_HAVE_D_TYPE + if(entry->d_type != DT_UNKNOWN){ + info_ptr->folder = entry->d_type == DT_DIR; + } else +#endif + { + struct stat st; + if(lstat(entry->d_name, &st) != -1){ + info_ptr->folder = S_ISDIR(st.st_mode); + } else { + info_ptr->folder = 0; + } + } + + info_ptr->filename.str = cursor_start; + info_ptr->filename.size = (i32)(cursor - cursor_start); + *cursor++ = 0; + info_ptr->filename.memory_size = info_ptr->filename.size + 1; + ++info_ptr; + } + + file_list->count = file_count; + + closedir(d); + } +} + +#if 0 +Sys_File_Paths_Equal_Sig(system_file_paths_equal){ + b32 result = 0; + + char* real_a = realpath(path_a, NULL); + if(real_a){ + char* real_b = realpath(path_b, NULL); + if(real_b){ + result = strcmp(real_a, real_b) == 0; + free(real_b); + } + free(real_a); + } + + return result; +} +#endif + +static_assert( + (sizeof(((struct stat*)0)->st_dev) + + sizeof(((struct stat*)0)->st_ino)) <= + sizeof(Unique_Hash), + "Unique_Hash too small" +); + +Sys_File_Unique_Hash_Sig(system_file_unique_hash){ + Unique_Hash result = {}; + struct stat st; + + if(stat(filename, &st) == -1){ + perror("sys_file_unique_hash: stat"); + } else { + memcpy(&result, &st.st_dev, sizeof(st.st_dev)); + memcpy((char*)&result + sizeof(st.st_dev), &st.st_ino, sizeof(st.st_ino)); + } + + return result; +} + +Sys_Post_Clipboard_Sig(system_post_clipboard){ + LinuxStringDup(&linuxvars.clipboard_outgoing, str.str, str.size); + XSetSelectionOwner(linuxvars.XDisplay, linuxvars.atom_CLIPBOARD, linuxvars.XWindow, CurrentTime); +} + +internal Linux_Coroutine* +LinuxAllocCoroutine(){ + Linux_Coroutine *result = linuxvars.coroutine_free; + Assert(result != 0); + if(getcontext(&result->ctx) == -1){ + perror("getcontext"); + } + result->ctx.uc_stack = result->stack; + linuxvars.coroutine_free = result->next; + return(result); +} + +internal void +LinuxFreeCoroutine(Linux_Coroutine *data){ + data->next = linuxvars.coroutine_free; + linuxvars.coroutine_free = data; +} + +internal void +LinuxCoroutineMain(void *arg_){ + Linux_Coroutine *c = (Linux_Coroutine*)arg_; + c->coroutine.func(&c->coroutine); + c->done = 1; + LinuxFreeCoroutine(c); + setcontext((ucontext_t*)c->coroutine.yield_handle); +} + +static_assert(sizeof(Plat_Handle) >= sizeof(ucontext_t*), "Plat handle not big enough"); + +internal +Sys_Create_Coroutine_Sig(system_create_coroutine){ + Linux_Coroutine *c = LinuxAllocCoroutine(); + c->done = 0; + + makecontext(&c->ctx, (void (*)())LinuxCoroutineMain, 1, &c->coroutine); + + *(ucontext_t**)&c->coroutine.plat_handle = &c->ctx; + c->coroutine.func = func; + + return(&c->coroutine); +} + +internal +Sys_Launch_Coroutine_Sig(system_launch_coroutine){ + Linux_Coroutine *c = (Linux_Coroutine*)coroutine; + ucontext_t* ctx = *(ucontext**)&coroutine->plat_handle; + + coroutine->yield_handle = &c->yield_ctx; + coroutine->in = in; + coroutine->out = out; + + swapcontext(&c->yield_ctx, ctx); + + if (c->done){ + LinuxFreeCoroutine(c); + coroutine = 0; + } + + return(coroutine); +} + +Sys_Resume_Coroutine_Sig(system_resume_coroutine){ + Linux_Coroutine *c = (Linux_Coroutine*)coroutine; + void *fiber; + + Assert(!c->done); + + coroutine->yield_handle = &c->yield_ctx; + coroutine->in = in; + coroutine->out = out; + + ucontext *ctx = *(ucontext**)&coroutine->plat_handle; + + swapcontext(&c->yield_ctx, ctx); + + if (c->done){ + LinuxFreeCoroutine(c); + coroutine = 0; + } + + return(coroutine); +} + +Sys_Yield_Coroutine_Sig(system_yield_coroutine){ + swapcontext(*(ucontext_t**)&coroutine->plat_handle, (ucontext*)coroutine->yield_handle); +} + +Sys_CLI_Call_Sig(system_cli_call){ + // TODO(allen): Implement + +// fprintf(stderr, "cli call: %s, %s\n", path, script_name); + + int pipe_fds[2]; + if(pipe(pipe_fds) == -1){ + perror("system_cli_call: pipe"); + return 0; + } + + pid_t child_pid = fork(); + if(child_pid == -1){ + perror("system_cli_call: fork"); + return 0; + } + + enum { PIPE_FD_READ, PIPE_FD_WRITE }; + + // child + if(child_pid == 0){ + close(pipe_fds[PIPE_FD_READ]); + dup2(pipe_fds[PIPE_FD_WRITE], STDOUT_FILENO); + dup2(pipe_fds[PIPE_FD_WRITE], STDERR_FILENO); + + if(chdir(path) == -1){ + perror("system_cli_call: chdir"); + exit(1); + }; + + //TODO(inso): do spaces in script_name signify multiple args? + char* argv[] = { "sh", "-c", script_name, NULL }; + + if(execv("/bin/sh", argv) == -1){ + perror("system_cli_call: execv"); + } + exit(1); + } else { + close(pipe_fds[PIPE_FD_WRITE]); + + *(pid_t*)&cli_out->proc = child_pid; + *(int*)&cli_out->out_read = pipe_fds[PIPE_FD_READ]; + *(int*)&cli_out->out_write = pipe_fds[PIPE_FD_WRITE]; + } + + return 1; +} + +Sys_CLI_Begin_Update_Sig(system_cli_begin_update){ + AllowLocal(cli); +} + +Sys_CLI_Update_Step_Sig(system_cli_update_step){ + + int pipe_read_fd = *(int*)&cli->out_read; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipe_read_fd, &fds); + + struct timeval tv = {}; + + size_t space_left = max; + char* ptr = dest; + + while(space_left > 0 && select(pipe_read_fd + 1, &fds, NULL, NULL, &tv) == 1){ + ssize_t num = read(pipe_read_fd, ptr, space_left); + if(num == -1){ + perror("system_cli_update_step: read"); + } else if(num == 0){ + // NOTE(inso): EOF + break; + } else { + ptr += num; + space_left -= num; + } + } + + *amount = (ptr - dest); + return (ptr - dest) > 0; +} + +Sys_CLI_End_Update_Sig(system_cli_end_update){ + pid_t pid = *(pid_t*)&cli->proc; + b32 close_me = 0; + + int status; + if(waitpid(pid, &status, WNOHANG) > 0){ + close_me = 1; + cli->exit = WEXITSTATUS(status); + close(*(int*)&cli->out_read); + close(*(int*)&cli->out_write); + } + + return close_me; +} + +static_assert(sizeof(Plat_Handle) >= sizeof(Linux_Semaphore_Handle), "Plat_Handle not big enough"); + +internal Plat_Handle +LinuxSemToHandle(Linux_Semaphore* sem){ + Linux_Semaphore_Handle h = { &sem->mutex, &sem->cond }; + return *(Plat_Handle*)&h; +} + +internal void* +ThreadProc(void* arg){ + Thread_Context *thread = (Thread_Context*)arg; + 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 = + __sync_val_compare_and_swap(&queue->read_position, + read_index, next_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 = + __sync_val_compare_and_swap(&full_job->running_thread, + THREAD_NOT_ASSIGNED, thread->id); + + 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 = linuxvars.thread_memory + thread->id - 1; + if (thread_memory->size < full_job->job.memory_request){ + if (thread_memory->data){ + LinuxFreeMemory(thread_memory->data); + } + i32 new_size = LargeRoundUp(full_job->job.memory_request, Kbytes(4)); + thread_memory->data = LinuxGetMemory(new_size); + thread_memory->size = new_size; + } + } + full_job->job.callback(linuxvars.system, thread, thread_memory, + &exchange_vars.thread, full_job->job.data); + full_job->running_thread = 0; + thread->running = 0; + } + } + } + else{ + Linux_Semaphore_Handle* h = (Linux_Semaphore_Handle*)&(queue->semaphore); + pthread_mutex_lock(h->mutex_p); + pthread_cond_wait(h->cond_p, h->mutex_p); + pthread_mutex_unlock(h->mutex_p); + } + } +} + + +Sys_Post_Job_Sig(system_post_job){ + // TODO(allen): Implement + AllowLocal(group_id); + AllowLocal(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 = + __sync_val_compare_and_swap(&queue->write_position, + write_index, next_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; + } + } + + Linux_Semaphore_Handle* h = (Linux_Semaphore_Handle*)&(queue->semaphore); + pthread_mutex_lock(h->mutex_p); + pthread_cond_broadcast(h->cond_p); + pthread_mutex_unlock(h->mutex_p); + + return result; +} + +Sys_Acquire_Lock_Sig(system_acquire_lock){ + pthread_mutex_lock(linuxvars.locks + id); +} + +Sys_Release_Lock_Sig(system_release_lock){ + pthread_mutex_unlock(linuxvars.locks + id); +} + +Sys_Cancel_Job_Sig(system_cancel_job){ + AllowLocal(group_id); + AllowLocal(job_id); + + Work_Queue *queue = exchange_vars.thread.queues + group_id; + Thread_Group *group = linuxvars.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 = + __sync_val_compare_and_swap(&full_job->running_thread, + THREAD_NOT_ASSIGNED, 0); + + if (thread_id != THREAD_NOT_ASSIGNED){ + system_acquire_lock(CANCEL_LOCK0 + thread_id - 1); + thread = group->threads + thread_id - 1; + pthread_kill(thread->handle, SIGINT); //NOTE(inso) SIGKILL if you really want it to die. + pthread_create(&thread->handle, NULL, &ThreadProc, thread); + system_release_lock(CANCEL_LOCK0 + thread_id - 1); + thread->running = 0; + } + +} + + +Sys_Grow_Thread_Memory_Sig(system_grow_thread_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 = system_get_memory(new_size); + memory->size = new_size; + if (old_data){ + memcpy(memory->data, old_data, old_size); + system_free_memory(old_data); + } + system_release_lock(CANCEL_LOCK0 + memory->id - 1); +} + +INTERNAL_Sys_Sentinel_Sig(internal_sentinel){ + Bubble *result; +#if FRED_INTERNAL + result = &linuxvars.internal_bubble; +#else + result = 0; +#endif + return(result); +} + +INTERNAL_Sys_Get_Thread_States_Sig(internal_get_thread_states){ + 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 = linuxvars.groups + id; + for (i32 i = 0; i < group->count; ++i){ + running[i] = (group->threads[i].running != 0); + } +} + +INTERNAL_Sys_Debug_Message_Sig(internal_debug_message){ + printf("%s", message); +} + +internal +FILE_EXISTS_SIG(system_file_exists){ + int result = 0; + char buff[PATH_MAX] = {}; + + if(len + 1 > PATH_MAX){ + fprintf(stderr, "system_directory_has_file: path too long"); + } else { + memcpy(buff, filename, len); + buff[len] = 0; + struct stat st; + result = stat(buff, &st) == 0 && S_ISREG(st.st_mode); + } + + return(result); +} + + +DIRECTORY_CD_SIG(system_directory_cd){ + String directory = make_string(dir, *len, capacity); + b32 result = 0; + i32 old_size; + + 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(&directory); + terminate_with_null(&directory); + } + else{ + if (directory.size + rel_len + 1 > directory.memory_size){ + old_size = directory.size; + append_partial(&directory, rel_path); + append_partial(&directory, "/"); + terminate_with_null(&directory); + + struct stat st; + if (stat(directory.str, &st) == 0 && S_ISDIR(st.st_mode)){ + result = 1; + } + else{ + directory.size = old_size; + } + } + } + } + + *len = directory.size; + + return(result); +} + +internal +Sys_File_Can_Be_Made(system_file_can_be_made){ + return access(filename, W_OK) == 0; +} + +internal +Sys_Load_File_Sig(system_load_file){ + Data result = {}; + struct stat info = {}; + int fd; + u8 *ptr, *read_ptr; + size_t bytes_to_read; + ssize_t num; + +// printf("sys_open_file: %s\n", filename); + + fd = open(filename, O_RDONLY); + if(fd < 0){ + perror("sys_open_file: open"); + goto out; + } + if(fstat(fd, &info) < 0){ + perror("sys_open_file: stat"); + goto out; + } + if(info.st_size <= 0){ + printf("st_size < 0: %ld\n", info.st_size); + goto out; + } + + ptr = (u8*)LinuxGetMemory(info.st_size); + if(!ptr){ + puts("null pointer from LGM"); + goto out; + } + + read_ptr = ptr; + bytes_to_read = info.st_size; + + do { + num = read(fd, read_ptr, bytes_to_read); + if(num < 0){ + if(errno == EINTR){ + continue; + } else { + //TODO(inso): error handling + perror("sys_load_file: read"); + LinuxFreeMemory(ptr); + goto out; + } + } else { + bytes_to_read -= num; + read_ptr += num; + } + } while(bytes_to_read); + + result.size = info.st_size; + result.data = ptr; + +out: + if(fd >= 0) close(fd); + return(result); +} + +internal +Sys_Save_File_Sig(system_save_file){ + b32 result = 0; + DBG_FN; + + const size_t save_fsz = strlen(filename); + const char tmp_end[] = ".4ed.XXXXXX"; + char* tmp_fname = (char*) alloca(save_fsz + sizeof(tmp_end)); + + memcpy(tmp_fname, filename, save_fsz); + memcpy(tmp_fname + save_fsz, tmp_end, sizeof(tmp_end)); + + int tmp_fd = mkstemp(tmp_fname); + if(tmp_fd == -1){ + perror("system_save_file: mkstemp"); + return result; + } + + do { + ssize_t written = write(tmp_fd, data, size); + if(written == -1){ + if(errno == EINTR){ + continue; + } else { + perror("system_save_file: write"); + unlink(tmp_fname); + return result; + } + } else { + size -= written; + data += written; + } + } while(size); + + if(rename(tmp_fname, filename) == -1){ + perror("system_save_file: rename"); + unlink(tmp_fname); + return result; + } + + result = 1; + return(result); +} + +#if FRED_USE_FONTCONFIG +internal char* +LinuxFontConfigGetName(char* approx_name, double pts){ + char* result = 0; + + FcPattern* pat = FcPatternBuild( + NULL, + FC_POSTSCRIPT_NAME, FcTypeString, approx_name, + FC_SIZE, FcTypeDouble, pts, + FC_FONTFORMAT, FcTypeString, "TrueType", + NULL + ); + + FcConfigSubstitute(linuxvars.fontconfig, pat, FcMatchPattern); + FcDefaultSubstitute(pat); + + FcResult res; + FcPattern* font = FcFontMatch(linuxvars.fontconfig, pat, &res); + FcChar8* fname = 0; + + if(font){ + FcPatternGetString(font, FC_FILE, 0, &fname); + if(fname){ + result = strdup((char*)fname); + printf("Got system font from FontConfig: %s\n", result); + } + FcPatternDestroy(font); + } + + FcPatternDestroy(pat); + + if(!result){ + result = strdup(approx_name); + } + + return result; +} +#endif +// 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. + +#include "system_shared.cpp" +#include "4ed_rendering.cpp" + +internal +Font_Load_Sig(system_draw_font_load){ + Font_Load_Parameters *params; + b32 free_name = 0; + char* chosen_name = filename; + +#if FRED_USE_FONTCONFIG + if(access(filename, F_OK) != 0){ + chosen_name = LinuxFontConfigGetName(basename(filename), pt_size); + free_name = 1; + } +#endif + + system_acquire_lock(FONT_LOCK); + params = linuxvars.fnt.free_param.next; + fnt__remove(params); + fnt__insert(&linuxvars.fnt.used_param, params); + system_release_lock(FONT_LOCK); + + if (linuxvars.fnt.part.base == 0){ + linuxvars.fnt.part = LinuxScratchPartition(Mbytes(8)); + } + + b32 success = 0; + i32 attempts = 0; + + for(; attempts < 3; ++attempts){ + success = draw_font_load( + linuxvars.fnt.part.base, + linuxvars.fnt.part.max, + font_out, + chosen_name, + pt_size, + tab_width + ); + + if(success){ + break; + } else { + printf("draw_font_load failed, %d\n", linuxvars.fnt.part.max); + LinuxScratchPartitionDouble(&linuxvars.fnt.part); + } + } + + if(success){ + system_acquire_lock(FONT_LOCK); + fnt__remove(params); + fnt__insert(&linuxvars.fnt.free_param, params); + system_release_lock(FONT_LOCK); + } + + if(free_name){ + free(chosen_name); + } + + return success; +} + +internal +Sys_To_Binary_Path(system_to_binary_path){ + b32 translate_success = 0; + i32 max = out_filename->memory_size; + i32 size = readlink("/proc/self/exe", out_filename->str, max); + if (size > 0 && size < max-1){ + out_filename->size = size; + remove_last_folder(out_filename); + if (append(out_filename, filename) && terminate_with_null(out_filename)){ + translate_success = 1; + } + } + return (translate_success); +} + +internal b32 +LinuxLoadAppCode(){ + b32 result = 0; + App_Get_Functions *get_funcs = 0; + + linuxvars.app_code = dlopen("./4ed_app.so", RTLD_LAZY); + if (linuxvars.app_code){ + get_funcs = (App_Get_Functions*) + dlsym(linuxvars.app_code, "app_get_functions"); + } + + if (get_funcs){ + result = 1; + linuxvars.app = get_funcs(); + } + + return(result); +} + +internal void +LinuxLoadSystemCode(){ + linuxvars.system->file_time_stamp = system_file_time_stamp; + linuxvars.system->set_file_list = system_set_file_list; + + linuxvars.system->file_exists = system_file_exists; + linuxvars.system->directory_cd = system_directory_cd; + linuxvars.system->file_unique_hash = system_file_unique_hash; + + linuxvars.system->post_clipboard = system_post_clipboard; + linuxvars.system->time = system_time; + + linuxvars.system->create_coroutine = system_create_coroutine; + linuxvars.system->launch_coroutine = system_launch_coroutine; + linuxvars.system->resume_coroutine = system_resume_coroutine; + linuxvars.system->yield_coroutine = system_yield_coroutine; + + linuxvars.system->cli_call = system_cli_call; + linuxvars.system->cli_begin_update = system_cli_begin_update; + linuxvars.system->cli_update_step = system_cli_update_step; + linuxvars.system->cli_end_update = system_cli_end_update; + + linuxvars.system->post_job = system_post_job; + linuxvars.system->cancel_job = system_cancel_job; + linuxvars.system->grow_thread_memory = system_grow_thread_memory; + linuxvars.system->acquire_lock = system_acquire_lock; + linuxvars.system->release_lock = system_release_lock; + + linuxvars.system->internal_sentinel = internal_sentinel; + linuxvars.system->internal_get_thread_states = internal_get_thread_states; + linuxvars.system->internal_debug_message = internal_debug_message; + + linuxvars.system->slash = '/'; +} + +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; + linuxvars.redraw = 1; + } +} + +// NOTE(allen): Thanks to Casey for providing the linux OpenGL launcher. +static bool ctxErrorOccurred = false; +static int XInput2OpCode = 0; +internal int +ctxErrorHandler( Display *dpy, XErrorEvent *ev ) +{ + ctxErrorOccurred = true; + return 0; +} + +#if FRED_INTERNAL +static void gl_log( + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam + ){ + printf("GL DEBUG: %s\n", message); +} +#endif + +internal GLXContext +InitializeOpenGLContext(Display *XDisplay, Window XWindow, GLXFBConfig &bestFbc, b32 &IsLegacy) +{ + IsLegacy = false; + + typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + const char *glxExts = glXQueryExtensionsString(XDisplay, DefaultScreen(XDisplay)); + + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) + glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); + + GLXContext ctx = 0; + ctxErrorOccurred = false; + int (*oldHandler)(Display*, XErrorEvent*) = + XSetErrorHandler(&ctxErrorHandler); + if (!glXCreateContextAttribsARB) + { + printf( "glXCreateContextAttribsARB() not found" + " ... using old-style GLX context\n" ); + ctx = glXCreateNewContext( XDisplay, bestFbc, GLX_RGBA_TYPE, 0, True ); + } + else + { + int context_attribs[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 4, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, +#if FRED_INTERNAL + GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_DEBUG_BIT_ARB, +#endif + None + }; + + printf("Attribs: %d %d %d %d %d\n", + context_attribs[0], + context_attribs[1], + context_attribs[2], + context_attribs[3], + context_attribs[4]); + + printf( "Creating context\n" ); + ctx = glXCreateContextAttribsARB( XDisplay, bestFbc, 0, + True, context_attribs ); + + XSync( XDisplay, False ); + if ( !ctxErrorOccurred && ctx ) + { + printf( "Created GL 4.3 context\n" ); + } + else + { + ctxErrorOccurred = false; + + context_attribs[1] = 4; + context_attribs[3] = 0; + + printf( "Creating context\n" ); + ctx = glXCreateContextAttribsARB( XDisplay, bestFbc, 0, + True, context_attribs ); + + XSync( XDisplay, False ); + + if ( !ctxErrorOccurred && ctx ) + { + printf( "Created GL 4.0 context\n" ); + } + else + { + context_attribs[1] = 1; + context_attribs[3] = 0; + + ctxErrorOccurred = false; + + printf( "Failed to create GL 4.0 context" + " ... using old-style GLX context\n" ); + ctx = glXCreateContextAttribsARB( XDisplay, bestFbc, 0, + True, context_attribs ); + + IsLegacy = true; + } + } + } + + XSync( XDisplay, False ); + XSetErrorHandler( oldHandler ); + + if ( ctxErrorOccurred || !ctx ) + { + printf( "Failed to create an OpenGL context\n" ); + exit(1); + } + + if ( ! glXIsDirect ( XDisplay, ctx ) ) + { + printf( "Indirect GLX rendering context obtained\n" ); + } + else + { + 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); + + //TODO(inso): glGetStringi is required if the GL version is >= 3.0 + 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); + + //TODO(inso): enable vsync if available. this should probably be optional + if(strstr(glxExts, "GLX_EXT_swap_control ")){ + PFNGLXSWAPINTERVALEXTPROC glx_swap_interval_ext = + (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT"); + + if(glx_swap_interval_ext){ + glx_swap_interval_ext(XDisplay, XWindow, 1); + + unsigned int swap_val = 0; + glXQueryDrawable(XDisplay, XWindow, GLX_SWAP_INTERVAL_EXT, &swap_val); + linuxvars.vsync = swap_val == 1; + printf("VSync enabled? %d\n", linuxvars.vsync); + } + } else if(strstr(glxExts, "GLX_MESA_swap_control ")){ + PFNGLXSWAPINTERVALMESAPROC glx_swap_interval_mesa = + (PFNGLXSWAPINTERVALMESAPROC) glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalMESA"); + + PFNGLXGETSWAPINTERVALMESAPROC glx_get_swap_interval_mesa = + (PFNGLXGETSWAPINTERVALMESAPROC) glXGetProcAddressARB((const GLubyte*)"glXGetSwapIntervalMESA"); + + if(glx_swap_interval_mesa){ + glx_swap_interval_mesa(1); + if(glx_get_swap_interval_mesa){ + linuxvars.vsync = glx_get_swap_interval_mesa(); + printf("VSync enabled? %d (MESA)\n", linuxvars.vsync); + } else { + // NOTE(inso): assume it worked? + linuxvars.vsync = 1; + puts("VSync enabled? possibly (MESA)"); + } + } + } else if(strstr(glxExts, "GLX_SGI_swap_control ")){ + PFNGLXSWAPINTERVALSGIPROC glx_swap_interval_sgi = + (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI"); + + if(glx_swap_interval_sgi){ + glx_swap_interval_sgi(1); + //NOTE(inso): The SGI one doesn't seem to have a way to confirm we got it... + linuxvars.vsync = 1; + puts("VSync enabled? hopefully (SGI)"); + } + } else { + puts("VSync enabled? nope, no suitable extension"); + } + +#if FRED_INTERNAL + PFNGLDEBUGMESSAGECALLBACKARBPROC gl_dbg_callback = (PFNGLDEBUGMESSAGECALLBACKARBPROC)glXGetProcAddress((const GLubyte*)"glDebugMessageCallback"); + if(gl_dbg_callback){ + puts("enabling gl debug"); + gl_dbg_callback(&gl_log, 0); + glEnable(GL_DEBUG_OUTPUT); + } +#endif + + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + return(ctx); +} + +internal b32 +GLXCanUseFBConfig(Display *XDisplay) +{ + b32 Result = false; + + int GLXMajor, GLXMinor; + + char *XVendor = ServerVendor(XDisplay); + printf("XWindows vendor: %s\n", XVendor); + if(glXQueryVersion(XDisplay, &GLXMajor, &GLXMinor)) + { + printf("GLX version %d.%d\n", GLXMajor, GLXMinor); + if(((GLXMajor == 1 ) && (GLXMinor >= 3)) || + (GLXMajor > 1)) + { + Result = true; + } + } + + return(Result); +} + +typedef struct glx_config_result{ + b32 Found; + GLXFBConfig BestConfig; + XVisualInfo BestInfo; +} glx_config_result; + +internal glx_config_result +ChooseGLXConfig(Display *XDisplay, int XScreenIndex) +{ + glx_config_result Result = {0}; + + int DesiredAttributes[] = + { + GLX_X_RENDERABLE , True, + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, + GLX_RENDER_TYPE , GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, + GLX_RED_SIZE , 8, + GLX_GREEN_SIZE , 8, + GLX_BLUE_SIZE , 8, + GLX_ALPHA_SIZE , 8, + GLX_DEPTH_SIZE , 24, + GLX_STENCIL_SIZE , 8, + GLX_DOUBLEBUFFER , True, + //GLX_SAMPLE_BUFFERS , 1, + //GLX_SAMPLES , 4, + None + }; + + { + int ConfigCount; + GLXFBConfig *Configs = glXChooseFBConfig(XDisplay, + XScreenIndex, + DesiredAttributes, + &ConfigCount); + +#if 0 + int DiffValues[GLXValueCount]; +#endif + {for(int ConfigIndex = 0; + ConfigIndex < ConfigCount; + ++ConfigIndex) + { + GLXFBConfig &Config = Configs[ConfigIndex]; + XVisualInfo *VisualInfo = glXGetVisualFromFBConfig(XDisplay, Config); + +#if 0 + printf(" Option %d:\n", ConfigIndex); + printf(" Depth: %d\n", VisualInfo->depth); + printf(" Bits per channel: %d\n", VisualInfo->bits_per_rgb); + printf(" Mask: R%06x G%06x B%06x\n", + (uint32)VisualInfo->red_mask, + (uint32)VisualInfo->green_mask, + (uint32)VisualInfo->blue_mask); + printf(" Class: %d\n", VisualInfo->c_class); +#endif + +#if 0 + {for(int ValueIndex = 0; + ValueIndex < GLXValueCount; + ++ValueIndex) + { + glx_value_info &ValueInfo = GLXValues[ValueIndex]; + int Value; + glXGetFBConfigAttrib(XDisplay, Config, ValueInfo.ID, &Value); + if(DiffValues[ValueIndex] != Value) + { + printf(" %s: %d\n", ValueInfo.Name, Value); + DiffValues[ValueIndex] = Value; + } + }} +#endif + + // TODO(casey): How the hell are you supposed to pick a config here?? + if(ConfigIndex == 0) + { + Result.Found = true; + Result.BestConfig = Config; + Result.BestInfo = *VisualInfo; + } + + XFree(VisualInfo); + }} + + XFree(Configs); + } + + return(Result); +} + +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 version %d.%d\n", major, minor); + } + else + { + printf("XI2 not available. Server supports %d.%d\n", major, minor); + } + } + else + { + printf("X Input extension not available.\n"); + } + + /* + TODO(casey): So, this is all one big clusterfuck I guess. + + The problem here is that you want to be able to get input + from all possible devices that could be a mouse or keyboard + (or gamepad, or whatever). So you'd like to be able to + register events for XIAllDevices, so that when a new + input device is connected, you will start receiving + input from it automatically, without having to periodically + poll XIQueryDevice to see if a new device has appeared. + + UNFORTUNATELY, this is not actually possible in Linux because + there was a bug in Xorg (as of early 2013, it is still not + fixed in most distributions people are using, AFAICT) which + makes the XServer return an error if you actually try to + do this :( + + But EVENTUALLY, if that shit gets fixed, then that is + the way this should work. + */ + +#if 0 + int DeviceCount; + XIDeviceInfo *DeviceInfo = XIQueryDevice(dpy, XIAllDevices, &DeviceCount); + {for(int32x DeviceIndex = 0; + DeviceIndex < DeviceCount; + ++DeviceIndex) + { + XIDeviceInfo *Device = DeviceInfo + DeviceIndex; + printf("Device %d: %s\n", Device->deviceid, Device->name); + }} + XIFreeDeviceInfo(DeviceInfo); +#endif + + XIEventMask Mask = {0}; + Mask.deviceid = XIAllDevices; + Mask.mask_len = XIMaskLen(XI_RawMotion); + size_t MaskSize = Mask.mask_len * sizeof(char unsigned); + Mask.mask = (char unsigned *)alloca(MaskSize); + memset(Mask.mask, 0, MaskSize); + if(Mask.mask) + { + XISetMask(Mask.mask, XI_ButtonPress); + XISetMask(Mask.mask, XI_ButtonRelease); + XISetMask(Mask.mask, XI_KeyPress); + XISetMask(Mask.mask, XI_KeyRelease); + XISetMask(Mask.mask, XI_Motion); + XISetMask(Mask.mask, XI_DeviceChanged); + XISetMask(Mask.mask, XI_Enter); + XISetMask(Mask.mask, XI_Leave); + XISetMask(Mask.mask, XI_FocusIn); + XISetMask(Mask.mask, XI_FocusOut); + XISetMask(Mask.mask, XI_HierarchyChanged); + XISetMask(Mask.mask, XI_PropertyEvent); + XISelectEvents(dpy, XWindow, &Mask, 1); + XSync(dpy, False); + + Mask.deviceid = XIAllMasterDevices; + memset(Mask.mask, 0, MaskSize); + XISetMask(Mask.mask, XI_RawKeyPress); + XISetMask(Mask.mask, XI_RawKeyRelease); + XISetMask(Mask.mask, XI_RawButtonPress); + XISetMask(Mask.mask, XI_RawButtonRelease); + 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; + + setlocale(LC_ALL, ""); + XSetLocaleModifiers(""); + printf("is current locale supported?: %d\n", XSupportsLocale()); + // TODO(inso): handle the case where it isn't supported somehow? + + XSelectInput( + linuxvars.XDisplay, + linuxvars.XWindow, + ExposureMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | + PointerMotionMask | + FocusChangeMask | + StructureNotifyMask | + MappingNotify | + ExposureMask | + VisibilityChangeMask + ); + + result.input_method = XOpenIM(dpy, 0, 0, 0); + if (!result.input_method){ + // NOTE(inso): Try falling back to the internal XIM implementation that + // should in theory always exist. + + XSetLocaleModifiers("@im=none"); + 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 = {}; + puts("Could not get minimum required input style"); + } + } + } + else{ + result = {}; + puts("Could not open X Input Method"); + } + + return(result); +} + +static void push_key(u8 code, u8 chr, u8 chr_nocaps, b8 (*mods)[MDFR_INDEX_COUNT], b32 is_hold){ + i32 *count; + Key_Event_Data *data; + + if(is_hold){ + count = &linuxvars.key_data.hold_count; + data = linuxvars.key_data.hold; + } else { + count = &linuxvars.key_data.press_count; + data = linuxvars.key_data.press; + } + + if(*count < KEY_INPUT_BUFFER_SIZE){ + data[*count].keycode = code; + data[*count].character = chr; + data[*count].character_no_caps_lock = chr_nocaps; + + memcpy(data[*count].modifiers, *mods, sizeof(*mods)); + + ++(*count); + } +} + +internal void +LinuxMaximizeWindow(Display* d, Window w, b32 maximize){ + //NOTE(inso): this will only work after it is mapped + + enum { STATE_REMOVE, STATE_ADD, STATE_TOGGLE }; + + XEvent e = {}; + + e.xany.type = ClientMessage; + e.xclient.message_type = linuxvars.atom_NET_WM_STATE; + e.xclient.format = 32; + e.xclient.window = w; + e.xclient.data.l[0] = maximize ? STATE_ADD : STATE_REMOVE; + e.xclient.data.l[1] = linuxvars.atom_NET_WM_STATE_MAXIMIZED_VERT; + e.xclient.data.l[2] = linuxvars.atom_NET_WM_STATE_MAXIMIZED_HORZ; + e.xclient.data.l[3] = 0L; + + XSendEvent( + d, + RootWindow(d, 0), + 0, + SubstructureNotifyMask | SubstructureRedirectMask, + &e + ); +} + +int +main(int argc, char **argv) +{ + i32 COUNTER = 0; + linuxvars = {}; + exchange_vars = {}; + +#if FRED_INTERNAL + linuxvars.internal_bubble.next = &linuxvars.internal_bubble; + linuxvars.internal_bubble.prev = &linuxvars.internal_bubble; + linuxvars.internal_bubble.flags = MEM_BUBBLE_SYS_DEBUG; +#endif + + linuxvars.first = 1; + + if (!LinuxLoadAppCode()){ + // TODO(allen): Failed to load app code, serious problem. + return 99; + } + + System_Functions system_; + System_Functions *system = &system_; + linuxvars.system = system; + LinuxLoadSystemCode(); + + linuxvars.coroutine_free = linuxvars.coroutine_data; + for (i32 i = 0; i+1 < ArrayCount(linuxvars.coroutine_data); ++i){ + linuxvars.coroutine_data[i].next = linuxvars.coroutine_data + i + 1; + } + + const size_t stack_size = Mbytes(16); + for (i32 i = 0; i < ArrayCount(linuxvars.coroutine_data); ++i){ + linuxvars.coroutine_data[i].stack.ss_size = stack_size; + linuxvars.coroutine_data[i].stack.ss_sp = system_get_memory(stack_size); + } + + memory_vars.vars_memory_size = Mbytes(2); + memory_vars.vars_memory = system_get_memory(memory_vars.vars_memory_size); + memory_vars.target_memory_size = Mbytes(512); + memory_vars.target_memory = system_get_memory(memory_vars.target_memory_size); + memory_vars.user_memory_size = Mbytes(2); + memory_vars.user_memory = system_get_memory(memory_vars.user_memory_size); + + String current_directory; + i32 curdir_req, curdir_size; + char *curdir_mem; + + curdir_req = (1 << 9); + curdir_mem = (char*)system_get_memory(curdir_req); + for (; getcwd(curdir_mem, curdir_req) == 0 && curdir_req < (1 << 13);){ + system_free_memory(curdir_mem); + curdir_req *= 4; + curdir_mem = (char*)system_get_memory(curdir_req); + } + + if (curdir_req >= (1 << 13)){ + // 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); + + Command_Line_Parameters clparams; + clparams.argv = argv; + clparams.argc = argc; + + char **files; + i32 *file_count; + i32 output_size; + + output_size = + linuxvars.app.read_command_line(system, + &memory_vars, + current_directory, + &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; + + sysshared_filter_real_files(files, file_count); + + linuxvars.XDisplay = XOpenDisplay(0); + + if(!linuxvars.XDisplay){ + fprintf(stderr, "Can't open display!"); + return 1; + } + + keycode_init(linuxvars.XDisplay); + +#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_alpha_4coder_version = (_Get_Version_Function*) + dlsym(linuxvars.custom, "get_alpha_4coder_version"); + + if (linuxvars.custom_api.get_alpha_4coder_version == 0 || + linuxvars.custom_api.get_alpha_4coder_version(MAJOR, MINOR, PATCH) == 0){ + printf("failed to use 4coder_custom.so: version mismatch\n"); + } + else{ + linuxvars.custom_api.get_bindings = (Get_Binding_Data_Function*) + dlsym(linuxvars.custom, "get_bindings"); + + if (linuxvars.custom_api.get_bindings == 0){ + printf("failed to use 4coder_custom.so: get_bindings not exported\n"); + } + else{ + printf("successfully loaded 4coder_custom.so\n"); + } + } + } +#endif + + //TODO(inso): look in linuxvars.settings and set window pos / size etc + + if (linuxvars.custom_api.get_bindings == 0){ + linuxvars.custom_api.get_bindings = get_bindings; + } + + Thread_Context background[4] = {}; + linuxvars.groups[BACKGROUND_THREADS].threads = background; + linuxvars.groups[BACKGROUND_THREADS].count = ArrayCount(background); + + Thread_Memory thread_memory[ArrayCount(background)]; + linuxvars.thread_memory = thread_memory; + + pthread_mutex_init(&linuxvars.thread_locks[BACKGROUND_THREADS].mutex, NULL); + pthread_cond_init(&linuxvars.thread_locks[BACKGROUND_THREADS].cond, NULL); + + exchange_vars.thread.queues[BACKGROUND_THREADS].semaphore = + LinuxSemToHandle(&linuxvars.thread_locks[BACKGROUND_THREADS]); + + for(i32 i = 0; i < linuxvars.groups[BACKGROUND_THREADS].count; ++i){ + Thread_Context *thread = linuxvars.groups[BACKGROUND_THREADS].threads + i; + thread->id = i + 1; + + Thread_Memory *memory = linuxvars.thread_memory + i; + *memory = {}; + memory->id = thread->id; + + thread->queue = &exchange_vars.thread.queues[BACKGROUND_THREADS]; + pthread_create(&thread->handle, NULL, &ThreadProc, thread); + } + + for(i32 i = 0; i < LOCK_COUNT; ++i){ + pthread_mutex_init(linuxvars.locks + i, NULL); + } + +#if FRED_USE_FONTCONFIG + linuxvars.fontconfig = FcInitLoadConfigAndFonts(); +#endif + + 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)); + + + // 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; + + if (linuxvars.settings.set_window_size){ + WinWidth = linuxvars.settings.window_w; + WinHeight = linuxvars.settings.window_h; + } else { + WinWidth = 800; + WinHeight = 600; + } + + int XScreenCount = ScreenCount(linuxvars.XDisplay); + glx_config_result Config = {}; + + if(!GLXCanUseFBConfig(linuxvars.XDisplay)){ + fprintf(stderr, "Your GLX version is too old.\n"); + exit(1); + } + + for(int XScreenIndex = 0; + XScreenIndex < XScreenCount; + ++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; + + Config = ChooseGLXConfig(linuxvars.XDisplay, XScreenIndex); + if(Config.Found) + { + 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; + + 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) + { + window_setup_success = 1; + break; + } + } + } + + if (!window_setup_success){ + fprintf(stderr, "Error creating window."); + exit(1); + } + + //NOTE(inso): Set the window's type to normal + Atom _NET_WM_WINDOW_TYPE = XInternAtom(linuxvars.XDisplay, "_NET_WM_WINDOW_TYPE", False); + Atom _NET_WIN_TYPE_NORMAL = XInternAtom(linuxvars.XDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False); + XChangeProperty( + linuxvars.XDisplay, + linuxvars.XWindow, + _NET_WM_WINDOW_TYPE, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*)&_NET_WIN_TYPE_NORMAL, + 1 + ); + + //NOTE(inso): window managers want the PID as a window property for some reason. + Atom _NET_WM_PID = XInternAtom(linuxvars.XDisplay, "_NET_WM_PID", False); + pid_t pid = getpid(); + XChangeProperty( + linuxvars.XDisplay, + linuxvars.XWindow, + _NET_WM_PID, + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&pid, + 1 + ); + + //NOTE(inso): set wm properties + XStoreName(linuxvars.XDisplay, linuxvars.XWindow, "4coder-window"); + + char* win_name_list[] = { "4coder" }; + XTextProperty win_name; + + XStringListToTextProperty(win_name_list, 1, &win_name); + + XSizeHints *sz_hints = XAllocSizeHints(); + XWMHints *wm_hints = XAllocWMHints(); + XClassHint *cl_hints = XAllocClassHint(); + + if(linuxvars.settings.set_window_pos){ + sz_hints->flags |= USPosition; + sz_hints->x = linuxvars.settings.window_x; + sz_hints->y = linuxvars.settings.window_y; + } + + wm_hints->flags |= InputHint; + wm_hints->input = True; + + cl_hints->res_name = "4coder"; + cl_hints->res_class = "4coder-class"; + + XSetWMProperties( + linuxvars.XDisplay, + linuxvars.XWindow, + &win_name, + NULL, + argv, + argc, + sz_hints, + wm_hints, + cl_hints + ); + + XFree(sz_hints); + XFree(wm_hints); + XFree(cl_hints); + + //NOTE(inso): make the window visible + XMapWindow(linuxvars.XDisplay, linuxvars.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.input_context = input_result.xic; + + b32 IsLegacy = false; + GLXContext GLContext = + InitializeOpenGLContext(linuxvars.XDisplay, linuxvars.XWindow, Config.BestConfig, IsLegacy); + + XWindowAttributes WinAttribs; + if(XGetWindowAttributes(linuxvars.XDisplay, linuxvars.XWindow, &WinAttribs)) + { + WinWidth = WinAttribs.width; + WinHeight = WinAttribs.height; + } + + XRaiseWindow(linuxvars.XDisplay, linuxvars.XWindow); + XSync(linuxvars.XDisplay, False); + + if (linuxvars.settings.set_window_pos){ + XMoveWindow( + linuxvars.XDisplay, + linuxvars.XWindow, + linuxvars.settings.window_x, + linuxvars.settings.window_y + ); + } + + Cursor xcursors[APP_MOUSE_CURSOR_COUNT] = { + None, + XCreateFontCursor(linuxvars.XDisplay, XC_arrow), + XCreateFontCursor(linuxvars.XDisplay, XC_xterm), + XCreateFontCursor(linuxvars.XDisplay, XC_sb_h_double_arrow), + XCreateFontCursor(linuxvars.XDisplay, XC_sb_v_double_arrow) + }; + + XSetICFocus(linuxvars.input_context); + + linuxvars.atom_CLIPBOARD = XInternAtom(linuxvars.XDisplay, "CLIPBOARD", False); + linuxvars.atom_UTF8_STRING = XInternAtom(linuxvars.XDisplay, "UTF8_STRING", False); + linuxvars.atom_NET_WM_STATE = XInternAtom(linuxvars.XDisplay, "_NET_WM_STATE", False); + linuxvars.atom_NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(linuxvars.XDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + linuxvars.atom_NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(linuxvars.XDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", False); + + if (linuxvars.settings.maximize_window){ + LinuxMaximizeWindow(linuxvars.XDisplay, linuxvars.XWindow, 1); + } + + int xfixes_version_unused, xfixes_err_unused; + + linuxvars.has_xfixes = XQueryExtension( + linuxvars.XDisplay, + "XFIXES", + &xfixes_version_unused, + &linuxvars.xfixes_selection_event, + &xfixes_err_unused + ) == True; + + if(linuxvars.has_xfixes){ + XFixesSelectSelectionInput( + linuxvars.XDisplay, + linuxvars.XWindow, + linuxvars.atom_CLIPBOARD, + XFixesSetSelectionOwnerNotifyMask + ); + } + + Atom WM_DELETE_WINDOW = XInternAtom(linuxvars.XDisplay, "WM_DELETE_WINDOW", False); + Atom _NET_WM_PING = XInternAtom(linuxvars.XDisplay, "_NET_WM_PING", False); + Atom wm_protos[] = { WM_DELETE_WINDOW, _NET_WM_PING }; + XSetWMProtocols(linuxvars.XDisplay, linuxvars.XWindow, wm_protos, 2); + + linuxvars.app.init(linuxvars.system, &linuxvars.target, &memory_vars, &exchange_vars, + linuxvars.clipboard_contents, current_directory, + linuxvars.custom_api); + + LinuxResizeTarget(WinWidth, WinHeight); + b32 keep_running = 1; + + while(keep_running) + { + XEvent PrevEvent = {}; + + while(XPending(linuxvars.XDisplay)) + { + XEvent Event; + XNextEvent(linuxvars.XDisplay, &Event); + + if (XFilterEvent(&Event, None) == True){ + continue; + } + + switch (Event.type){ + case KeyPress: { + b32 is_hold = + PrevEvent.type == KeyRelease && + PrevEvent.xkey.time == Event.xkey.time && + PrevEvent.xkey.keycode == Event.xkey.keycode; + + b8 mods[MDFR_INDEX_COUNT] = {}; + if(Event.xkey.state & ShiftMask) mods[MDFR_SHIFT_INDEX] = 1; + if(Event.xkey.state & ControlMask) mods[MDFR_CONTROL_INDEX] = 1; + if(Event.xkey.state & LockMask) mods[MDFR_CAPS_INDEX] = 1; + if(Event.xkey.state & Mod1Mask) mods[MDFR_ALT_INDEX] = 1; + // NOTE(inso): mod5 == AltGr + // if(Event.xkey.state & Mod5Mask) mods[MDFR_ALT_INDEX] = 1; + + KeySym keysym = NoSymbol; + char buff[32], no_caps_buff[32]; + + // NOTE(inso): Turn ControlMask off like the win32 code does. + if(mods[MDFR_CONTROL_INDEX] && !mods[MDFR_ALT_INDEX]){ + Event.xkey.state &= ~(ControlMask); + } + + // TODO(inso): Use one of the Xutf8LookupString funcs to allow for non-ascii chars + XLookupString(&Event.xkey, buff, sizeof(buff), &keysym, NULL); + + Event.xkey.state &= ~LockMask; + XLookupString(&Event.xkey, no_caps_buff, sizeof(no_caps_buff), NULL, NULL); + + u8 key = keycode_lookup(Event.xkey.keycode); + + if(key){ + push_key(key, 0, 0, &mods, is_hold); + } else { + key = buff[0] & 0xFF; + if(key < 128){ + u8 no_caps_key = no_caps_buff[0] & 0xFF; + if(key == '\r') key = '\n'; + if(no_caps_key == '\r') no_caps_key = '\n'; + push_key(key, key, no_caps_key, &mods, is_hold); + } else { + push_key(0, 0, 0, &mods, is_hold); + } + } + }break; + + case MotionNotify: { + linuxvars.mouse_data.x = Event.xmotion.x; + linuxvars.mouse_data.y = Event.xmotion.y; + }break; + + case ButtonPress: { + switch(Event.xbutton.button){ + case Button1: { + linuxvars.mouse_data.press_l = 1; + linuxvars.mouse_data.l = 1; + } break; + case Button3: { + linuxvars.mouse_data.press_r = 1; + linuxvars.mouse_data.r = 1; + } break; + + //NOTE(inso): scroll up + case Button4: { + linuxvars.mouse_data.wheel = 1; + }break; + + //NOTE(inso): scroll down + case Button5: { + linuxvars.mouse_data.wheel = -1; + }break; + } + }break; + + case ButtonRelease: { + switch(Event.xbutton.button){ + case Button1: { + linuxvars.mouse_data.release_l = 1; + linuxvars.mouse_data.l = 0; + } break; + case Button3: { + linuxvars.mouse_data.release_r = 1; + linuxvars.mouse_data.r = 0; + } break; + } + }break; + + case EnterNotify: { + linuxvars.mouse_data.out_of_window = 0; + }break; + + case LeaveNotify: { + linuxvars.mouse_data.out_of_window = 1; + }break; + + case FocusIn: + case FocusOut: { + linuxvars.mouse_data.l = 0; + linuxvars.mouse_data.r = 0; + }break; + + case ConfigureNotify: { + i32 w = Event.xconfigure.width, h = Event.xconfigure.height; + + if(w != linuxvars.target.width || h != linuxvars.target.height){ + LinuxResizeTarget(Event.xconfigure.width, Event.xconfigure.height); + } + }break; + + case MappingNotify: { + if(Event.xmapping.request == MappingModifier || Event.xmapping.request == MappingKeyboard){ + XRefreshKeyboardMapping(&Event.xmapping); + keycode_init(linuxvars.XDisplay); + } + }break; + + case ClientMessage: { + if ((Atom)Event.xclient.data.l[0] == WM_DELETE_WINDOW) { + keep_running = false; + } + else if ((Atom)Event.xclient.data.l[0] == _NET_WM_PING) { + Event.xclient.window = DefaultRootWindow(linuxvars.XDisplay); + XSendEvent( + linuxvars.XDisplay, + Event.xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &Event + ); + } + }break; + + // NOTE(inso): Someone wants us to give them the clipboard data. + case SelectionRequest: { + XSelectionRequestEvent request = Event.xselectionrequest; + + XSelectionEvent response = {}; + response.type = SelectionNotify; + response.requestor = request.requestor; + response.selection = request.selection; + response.target = request.target; + response.time = request.time; + response.property = None; + + //TODO(inso): handle TARGETS negotiation instead of requiring UTF8_STRING + if ( + linuxvars.clipboard_outgoing.size && + request.target == linuxvars.atom_UTF8_STRING && + request.selection == linuxvars.atom_CLIPBOARD && + request.property != None && + request.display && + request.requestor + ){ + XChangeProperty( + request.display, + request.requestor, + request.property, + request.target, + 8, + PropModeReplace, + (unsigned char*)linuxvars.clipboard_outgoing.str, + linuxvars.clipboard_outgoing.size + ); + + response.property = request.property; + } + + XSendEvent(request.display, request.requestor, True, 0, (XEvent*)&response); + + }break; + + // NOTE(inso): Another program is now the clipboard owner. + case SelectionClear: { + if(Event.xselectionclear.selection == linuxvars.atom_CLIPBOARD){ + linuxvars.clipboard_outgoing.size = 0; + } + }break; + + // NOTE(inso): A program is giving us the clipboard data we asked for. + case SelectionNotify: { + XSelectionEvent* e = (XSelectionEvent*)&Event; + if( + e->selection == linuxvars.atom_CLIPBOARD && + e->target == linuxvars.atom_UTF8_STRING && + e->property != None + ){ + Atom type; + int fmt; + unsigned long nitems, bytes_left; + u8 *data; + + int result = XGetWindowProperty( + linuxvars.XDisplay, + linuxvars.XWindow, + linuxvars.atom_CLIPBOARD, + 0L, + LINUX_MAX_PASTE_CHARS/4L, + False, + linuxvars.atom_UTF8_STRING, + &type, + &fmt, + &nitems, + &bytes_left, + &data + ); + + if(result == Success && fmt == 8){ + LinuxStringDup(&linuxvars.clipboard_contents, data, nitems); + XFree(data); + } + } + }break; + + case Expose: + case VisibilityNotify: { + linuxvars.redraw = 1; + }break; + + default: { + if(Event.type == linuxvars.xfixes_selection_event){ + XConvertSelection( + linuxvars.XDisplay, + linuxvars.atom_CLIPBOARD, + linuxvars.atom_UTF8_STRING, + linuxvars.atom_CLIPBOARD, + linuxvars.XWindow, + CurrentTime + ); + } + }break; + } + + PrevEvent = Event; + } + + // NOTE(inso): without the xfixes extension we'll have to request the clipboard every frame. + if(!linuxvars.has_xfixes){ + XConvertSelection( + linuxvars.XDisplay, + linuxvars.atom_CLIPBOARD, + linuxvars.atom_UTF8_STRING, + linuxvars.atom_CLIPBOARD, + linuxvars.XWindow, + CurrentTime + ); + } + + Key_Input_Data input_data; + Mouse_State mouse; + Application_Step_Result result; + + input_data = linuxvars.key_data; + mouse = linuxvars.mouse_data; + + result.mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT; + + if(__sync_bool_compare_and_swap(&exchange_vars.thread.force_redraw, 1, 0)){ + linuxvars.redraw = 1; + } + + result.redraw = linuxvars.redraw; + result.lctrl_lalt_is_altgr = 0; + + u64 start_time = system_time(); + + linuxvars.app.step(linuxvars.system, + &input_data, + &mouse, + &linuxvars.target, + &memory_vars, + &exchange_vars, + linuxvars.clipboard_contents, + 1, linuxvars.first, linuxvars.redraw, + &result); + + if (linuxvars.redraw || result.redraw){ + LinuxRedrawTarget(); + } + + u64 time_diff = system_time() - start_time; + if(time_diff < frame_useconds){ + usleep(frame_useconds - time_diff); + } + + if(result.mouse_cursor_type != linuxvars.cursor){ + Cursor c = xcursors[result.mouse_cursor_type]; + XDefineCursor(linuxvars.XDisplay, linuxvars.XWindow, c); + linuxvars.cursor = result.mouse_cursor_type; + } + + linuxvars.first = 0; + linuxvars.redraw = 0; + linuxvars.key_data = {}; + linuxvars.mouse_data.press_l = 0; + linuxvars.mouse_data.release_l = 0; + linuxvars.mouse_data.press_r = 0; + linuxvars.mouse_data.release_r = 0; + linuxvars.mouse_data.wheel = 0; + + 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, (char*)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); + } +} + +// BOTTOM + diff --git a/test_data/lots_of_files/linux_handmade.cpp b/test_data/lots_of_files/linux_handmade.cpp new file mode 100644 index 0000000..faff111 --- /dev/null +++ b/test_data/lots_of_files/linux_handmade.cpp @@ -0,0 +1,7 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ diff --git a/test_data/lots_of_files/linux_release_compat.c b/test_data/lots_of_files/linux_release_compat.c new file mode 100644 index 0000000..5dafb8f --- /dev/null +++ b/test_data/lots_of_files/linux_release_compat.c @@ -0,0 +1,12 @@ +#include <string.h> + +// Link to the memcpy symbol with version 2.2.5 instead of the newer 2.14 one +// since they are functionaly equivalent, but using the old one allows 4coder +// to run on older distros without glibc >= 2.14 + +extern "C" { +asm (".symver memcpy, memcpy@GLIBC_2.2.5"); +void *__wrap_memcpy(void *dest, const void *src, size_t n){ + return memcpy(dest, src, n); +} +} diff --git a/test_data/lots_of_files/locale.h b/test_data/lots_of_files/locale.h new file mode 100644 index 0000000..59ae148 --- /dev/null +++ b/test_data/lots_of_files/locale.h @@ -0,0 +1,137 @@ +/*** +*locale.h - definitions/declarations for localization routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines the structures, values, macros, and functions +* used by the localization routines. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_LOCALE +#define _INC_LOCALE + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + +/* Locale categories */ + +#define LC_ALL 0 +#define LC_COLLATE 1 +#define LC_CTYPE 2 +#define LC_MONETARY 3 +#define LC_NUMERIC 4 +#define LC_TIME 5 + +#define LC_MIN LC_ALL +#define LC_MAX LC_TIME + +/* Locale convention structure */ + +#ifndef _LCONV_DEFINED +struct lconv { + char *decimal_point; + char *thousands_sep; + char *grouping; + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + wchar_t *_W_decimal_point; + wchar_t *_W_thousands_sep; + wchar_t *_W_int_curr_symbol; + wchar_t *_W_currency_symbol; + wchar_t *_W_mon_decimal_point; + wchar_t *_W_mon_thousands_sep; + wchar_t *_W_positive_sign; + wchar_t *_W_negative_sign; + }; +#define _LCONV_DEFINED +#endif /* _LCONV_DEFINED */ + +/* ANSI: char lconv members default is CHAR_MAX which is compile time + dependent. Defining and using _charmax here causes CRT startup code + to initialize lconv members properly */ + +#ifdef _CHAR_UNSIGNED +extern int _charmax; +extern __inline int __dummy(void) { return _charmax; } +#endif /* _CHAR_UNSIGNED */ + +/* function prototypes */ + +#ifndef _CONFIG_LOCALE_SWT +#define _ENABLE_PER_THREAD_LOCALE 0x1 +#define _DISABLE_PER_THREAD_LOCALE 0x2 +#define _ENABLE_PER_THREAD_LOCALE_GLOBAL 0x10 +#define _DISABLE_PER_THREAD_LOCALE_GLOBAL 0x20 +#define _ENABLE_PER_THREAD_LOCALE_NEW 0x100 +#define _DISABLE_PER_THREAD_LOCALE_NEW 0x200 +#define _CONFIG_LOCALE_SWT +#endif /* _CONFIG_LOCALE_SWT */ + +_Check_return_opt_ _CRTIMP int __cdecl _configthreadlocale(_In_ int _Flag); +_Check_return_opt_ _CRTIMP char * __cdecl setlocale(_In_ int _Category, _In_opt_z_ const char * _Locale); +_Check_return_opt_ _CRTIMP struct lconv * __cdecl localeconv(void); +_Check_return_opt_ _CRTIMP _locale_t __cdecl _get_current_locale(void); +_Check_return_opt_ _CRTIMP _locale_t __cdecl _create_locale(_In_ int _Category, _In_z_ const char * _Locale); +_CRTIMP void __cdecl _free_locale(_In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/* use _get_current_locale, _create_locale and _free_locale, instead of these functions with double leading underscore */ +_Check_return_ _CRT_OBSOLETE(_get_current_locale) _CRTIMP _locale_t __cdecl __get_current_locale(void); +_Check_return_ _CRT_OBSOLETE(_create_locale) _CRTIMP _locale_t __cdecl __create_locale(_In_ int _Category, _In_z_ const char * _Locale); +_CRT_OBSOLETE(_free_locale) _CRTIMP void __cdecl __free_locale(_In_opt_ _locale_t _Locale); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifndef _WLOCALE_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +_Check_return_opt_ _CRTIMP wchar_t * __cdecl _wsetlocale(_In_ int _Category, _In_opt_z_ const wchar_t * _Locale); +_Check_return_opt_ _CRTIMP _locale_t __cdecl _wcreate_locale(_In_ int _Category, _In_z_ const wchar_t * _Locale); + +#define _WLOCALE_DEFINED +#endif /* _WLOCALE_DEFINED */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_LOCALE */ diff --git a/test_data/lots_of_files/main.s b/test_data/lots_of_files/main.s new file mode 100644 index 0000000..911f222 --- /dev/null +++ b/test_data/lots_of_files/main.s @@ -0,0 +1,100 @@ +/****************************************************************************** +* main.s +* by Alex Chadwick +* +* A sample assembly code implementation of the ok05 operating system, that +* flashes out a pattern using the wait routine from ok04. +* +* main.s contains the main operating system, and IVT code. +******************************************************************************/ + +/* +* .globl is a directive to our assembler, that tells it to export this symbol +* to the elf file. Convention dictates that the symbol _start is used for the +* entry point, so this all has the net effect of setting the entry point here. +* Ultimately, this is useless as the elf itself is not used in the final +* result, and so the entry point really doesn't matter, but it aids clarity, +* allows simulators to run the elf, and also stops us getting a linker warning +* about having no entry point. +*/ +.section .init +.globl _start +_start: + +/* +* Branch to the actual main code. +*/ +b main + +/* +* This command tells the assembler to put this code with the rest. +*/ +.section .text + +/* +* main is what we shall call our main operating system method. It never +* returns, and takes no parameters. +* C++ Signature: void main(void) +*/ +main: + +/* +* Set the stack point to 0x8000. +*/ +mov sp,#0x8000 + +/* +* Use our new SetGpioFunction function to set the function of GPIO port 16 (OK +* LED) to 001 (binary) +*/ +mov r0,#16 +mov r1,#1 +bl SetGpioFunction + +/* NEW +* Load in the pattern to flash and also store our position in the flash +* sequence. +*/ +ptrn .req r4 +ldr ptrn,=pattern +ldr ptrn,[ptrn] +seq .req r5 +mov seq,#0 + +loop$: + +/* NEW +* Use our new SetGpio function to set GPIO 16 base on the current bit in the +* pattern causing the LED to turn on if the pattern contains 0, and off if it +* contains 1. +*/ +mov r0,#16 +mov r1,#1 +lsl r1,seq +and r1,ptrn +bl SetGpio + +/* NEW +* We wait for 0.25s using our wait method. +*/ +ldr r0,=250000 +bl Wait + +/* NEW +* Loop over this process forevermore, incrementing the sequence counter. +* When it reaches 32, its bit pattern becomes 100000, and so anding it with +* 11111 causes it to return to 0, but has no effect on all patterns less than +* 32. +*/ +add seq,#1 +and seq,#0b11111 +b loop$ + +/* NEW +* In the data section of the kernel image, store the pattern we wish to flash +* on the LED. +*/ +.section .data +.align 2 +pattern: +.int 0b11111111101010100010001000101010 diff --git a/test_data/lots_of_files/malloc.h b/test_data/lots_of_files/malloc.h new file mode 100644 index 0000000..7313095 --- /dev/null +++ b/test_data/lots_of_files/malloc.h @@ -0,0 +1,294 @@ +/*** +*malloc.h - declarations and definitions for memory allocation functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Contains the function declarations for memory allocation functions; +* also defines manifest constants and types used by the heap routines. +* [System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_MALLOC +#define _INC_MALLOC + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Maximum heap request the heap manager will attempt */ + +#ifdef _WIN64 +#define _HEAP_MAXREQ 0xFFFFFFFFFFFFFFE0 +#else /* _WIN64 */ +#define _HEAP_MAXREQ 0xFFFFFFE0 +#endif /* _WIN64 */ + +/* _STATIC_ASSERT is for enforcing boolean/integral conditions at compile time. */ + +#ifndef _STATIC_ASSERT +#define _STATIC_ASSERT(expr) typedef char __static_assert_t[ (expr) ] +#endif /* _STATIC_ASSERT */ + +/* Constants for _heapchk/_heapset/_heapwalk routines */ + +#define _HEAPEMPTY (-1) +#define _HEAPOK (-2) +#define _HEAPBADBEGIN (-3) +#define _HEAPBADNODE (-4) +#define _HEAPEND (-5) +#define _HEAPBADPTR (-6) +#define _FREEENTRY 0 +#define _USEDENTRY 1 + +#ifndef _HEAPINFO_DEFINED +typedef struct _heapinfo { + int * _pentry; + size_t _size; + int _useflag; + } _HEAPINFO; +#define _HEAPINFO_DEFINED +#endif /* _HEAPINFO_DEFINED */ + +#define _mm_free(a) _aligned_free(a) +#define _mm_malloc(a, b) _aligned_malloc(a, b) + +/* Function prototypes */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("calloc") +#pragma push_macro("free") +#pragma push_macro("malloc") +#pragma push_macro("realloc") +#pragma push_macro("_recalloc") +#pragma push_macro("_aligned_free") +#pragma push_macro("_aligned_malloc") +#pragma push_macro("_aligned_offset_malloc") +#pragma push_macro("_aligned_realloc") +#pragma push_macro("_aligned_recalloc") +#pragma push_macro("_aligned_offset_realloc") +#pragma push_macro("_aligned_offset_recalloc") +#pragma push_macro("_aligned_msize") +#pragma push_macro("_freea") +#undef calloc +#undef free +#undef malloc +#undef realloc +#undef _recalloc +#undef _aligned_free +#undef _aligned_malloc +#undef _aligned_offset_malloc +#undef _aligned_realloc +#undef _aligned_recalloc +#undef _aligned_offset_realloc +#undef _aligned_offset_recalloc +#undef _aligned_msize +#undef _freea +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +#ifndef _CRT_ALLOCATION_DEFINED +#define _CRT_ALLOCATION_DEFINED +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRT_JIT_INTRINSIC _CRTNOALIAS _CRTRESTRICT void * __cdecl calloc(_In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size); +_CRTIMP _CRTNOALIAS void __cdecl free(_Pre_maybenull_ _Post_invalid_ void * _Memory); +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _CRTIMP _CRT_JIT_INTRINSIC _CRTNOALIAS _CRTRESTRICT void * __cdecl malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl realloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _NewSize); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _recalloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size); +_CRTIMP _CRTNOALIAS void __cdecl _aligned_free(_Pre_maybenull_ _Post_invalid_ void * _Memory); +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment); +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_offset_malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment, _In_ size_t _Offset); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_realloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _NewSize, _In_ size_t _Alignment); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_recalloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_offset_realloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _NewSize, _In_ size_t _Alignment, _In_ size_t _Offset); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_offset_recalloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment, _In_ size_t _Offset); +_Check_return_ _CRTIMP size_t __cdecl _aligned_msize(_Pre_notnull_ void * _Memory, _In_ size_t _Alignment, _In_ size_t _Offset); +#endif /* _CRT_ALLOCATION_DEFINED */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("calloc") +#pragma pop_macro("free") +#pragma pop_macro("malloc") +#pragma pop_macro("realloc") +#pragma pop_macro("_recalloc") +#pragma pop_macro("_aligned_free") +#pragma pop_macro("_aligned_malloc") +#pragma pop_macro("_aligned_offset_malloc") +#pragma pop_macro("_aligned_realloc") +#pragma pop_macro("_aligned_recalloc") +#pragma pop_macro("_aligned_offset_realloc") +#pragma pop_macro("_aligned_offset_recalloc") +#pragma pop_macro("_aligned_msize") +#pragma pop_macro("_freea") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRTIMP int __cdecl _resetstkoflw (void); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#define _MAX_WAIT_MALLOC_CRT 60000 + +_CRTIMP unsigned long __cdecl _set_malloc_crt_max_wait(_In_ unsigned long _NewValue); + + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_expand") +#pragma push_macro("_msize") +#undef _expand +#undef _msize +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP void * __cdecl _expand(_Pre_notnull_ void * _Memory, _In_ size_t _NewSize); +_Check_return_ _CRTIMP size_t __cdecl _msize(_Pre_notnull_ void * _Memory); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_expand") +#pragma pop_macro("_msize") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Ret_notnull_ _Post_writable_byte_size_(_Size) void * __cdecl _alloca(_In_ size_t _Size); + +#if defined (_DEBUG) || defined (_CRT_USE_WINAPI_FAMILY_DESKTOP_APP) +_CRTIMP _CRT_MANAGED_HEAP_DEPRECATE int __cdecl _heapwalk(_Inout_ _HEAPINFO * _EntryInfo); +_CRTIMP intptr_t __cdecl _get_heap_handle(void); +#endif /* defined (_DEBUG) || defined (_CRT_USE_WINAPI_FAMILY_DESKTOP_APP) */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP int __cdecl _heapadd(_In_ void * _Memory, _In_ size_t _Size); +_Check_return_ _CRTIMP int __cdecl _heapchk(void); +_Check_return_ _CRTIMP int __cdecl _heapmin(void); +_CRTIMP int __cdecl _heapset(_In_ unsigned int _Fill); +_CRTIMP size_t __cdecl _heapused(size_t * _Used, size_t * _Commit); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#define _ALLOCA_S_THRESHOLD 1024 +#define _ALLOCA_S_STACK_MARKER 0xCCCC +#define _ALLOCA_S_HEAP_MARKER 0xDDDD + +#if defined (_M_IX86) +#define _ALLOCA_S_MARKER_SIZE 8 +#elif defined (_M_X64) +#define _ALLOCA_S_MARKER_SIZE 16 +#elif defined (_M_ARM) +#define _ALLOCA_S_MARKER_SIZE 8 +#elif !defined (RC_INVOKED) +#error Unsupported target platform. +#endif /* !defined (RC_INVOKED) */ + +_STATIC_ASSERT(sizeof(unsigned int) <= _ALLOCA_S_MARKER_SIZE); + +#if !defined (__midl) && !defined (RC_INVOKED) +#pragma warning(push) +#pragma warning(disable:6540) +__inline void *_MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void *_Ptr, unsigned int _Marker) +{ + if (_Ptr) + { + *((unsigned int*)_Ptr) = _Marker; + _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE; + } + return _Ptr; +} + +__inline int _MallocaIsSizeInRange(size_t size) +{ + return size + _ALLOCA_S_MARKER_SIZE > size; +} +#pragma warning(pop) +#endif /* !defined (__midl) && !defined (RC_INVOKED) */ + +#if defined(_DEBUG) +#if !defined(_CRTDBG_MAP_ALLOC) +#undef _malloca +#define _malloca(size) \ + __pragma(warning(suppress: 6255)) \ + (_MallocaIsSizeInRange(size) \ + ? _MarkAllocaS(malloc((size_t)(size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_HEAP_MARKER) \ + : NULL) +#endif +#else +#undef _malloca +#define _malloca(size) \ + __pragma(warning(suppress: 6255)) \ + (_MallocaIsSizeInRange(size) \ + ? (((((size_t)(size) + _ALLOCA_S_MARKER_SIZE) <= _ALLOCA_S_THRESHOLD) \ + ? _MarkAllocaS(_alloca((size_t)(size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_STACK_MARKER) \ + : _MarkAllocaS(malloc((size_t)(size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_HEAP_MARKER))) \ + : NULL) +#endif + +#undef _FREEA_INLINE +#ifdef _CRTBLD +#ifndef _CRT_NOFREEA +#define _FREEA_INLINE +#else /* _CRT_NOFREEA */ +#undef _FREEA_INLINE +#endif /* _CRT_NOFREEA */ +#else /* _CRTBLD */ +#define _FREEA_INLINE +#endif /* _CRTBLD */ + +#ifdef _FREEA_INLINE +/* _freea must be in the header so that its allocator matches _malloca */ +#if !defined (__midl) && !defined (RC_INVOKED) +#if !(defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC)) +#undef _freea +__pragma(warning(push)) +__pragma(warning(disable: 6014)) +_CRTNOALIAS __inline void __CRTDECL _freea(_Pre_maybenull_ _Post_invalid_ void * _Memory) +{ + unsigned int _Marker; + if (_Memory) + { + _Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE; + _Marker = *(unsigned int *)_Memory; + if (_Marker == _ALLOCA_S_HEAP_MARKER) + { + free(_Memory); + } +#if defined (_ASSERTE) + else if (_Marker != _ALLOCA_S_STACK_MARKER) + { + #pragma warning(suppress: 4548) /* expression before comma has no effect */ + _ASSERTE(("Corrupted pointer passed to _freea", 0)); + } +#endif /* defined (_ASSERTE) */ + } +} +__pragma(warning(pop)) +#endif /* !(defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC)) */ +#endif /* !defined (__midl) && !defined (RC_INVOKED) */ +#endif /* _FREEA_INLINE */ + +#if !__STDC__ +/* Non-ANSI names for compatibility */ +#define alloca _alloca +#endif /* !__STDC__ */ + + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_MALLOC */ diff --git a/test_data/lots_of_files/math.h b/test_data/lots_of_files/math.h new file mode 100644 index 0000000..c4a1d52 --- /dev/null +++ b/test_data/lots_of_files/math.h @@ -0,0 +1,1307 @@ +/*** +*math.h - definitions and declarations for math library +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains constant definitions and external subroutine +* declarations for the math subroutine library. +* [ANSI/System V] +* +* [Public] +* +****/ + +#ifndef _INC_MATH +#define _INC_MATH + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __assembler + +/* Definition of _exception struct - this struct is passed to the matherr + * routine when a floating point exception is detected + */ + +#ifndef _EXCEPTION_DEFINED +struct _exception { + int type; /* exception type - see below */ + char *name; /* name of function where error occured */ + double arg1; /* first argument to function */ + double arg2; /* second argument (if any) to function */ + double retval; /* value to be returned by function */ + } ; + +#define _EXCEPTION_DEFINED +#endif /* _EXCEPTION_DEFINED */ + + +/* Definition of a _complex struct to be used by those who use cabs and + * want type checking on their argument + */ + +#ifndef _COMPLEX_DEFINED +struct _complex { + double x,y; /* real and imaginary parts */ + } ; + +#if !__STDC__ && !defined (__cplusplus) +/* Non-ANSI name for compatibility */ +#define complex _complex +#endif /* !__STDC__ && !defined (__cplusplus) */ + +#define _COMPLEX_DEFINED +#endif /* _COMPLEX_DEFINED */ +#endif /* __assembler */ + +typedef float float_t; +typedef double double_t; + +/* Constant definitions for the exception type passed in the _exception struct + */ + +#define _DOMAIN 1 /* argument domain error */ +#define _SING 2 /* argument singularity */ +#define _OVERFLOW 3 /* overflow range error */ +#define _UNDERFLOW 4 /* underflow range error */ +#define _TLOSS 5 /* total loss of precision */ +#define _PLOSS 6 /* partial loss of precision */ + +#define EDOM 33 +#define ERANGE 34 + + +/* Definitions of _HUGE and HUGE_VAL - respectively the XENIX and ANSI names + * for a value returned in case of error by a number of the floating point + * math routines + */ +#ifndef __assembler +#if !defined (_M_CEE_PURE) +_CRTIMP extern double _HUGE; +#else /* !defined (_M_CEE_PURE) */ +const double _HUGE = System::Double::PositiveInfinity; +#endif /* !defined (_M_CEE_PURE) */ +#endif /* __assembler */ + +#define HUGE_VAL _HUGE + +#ifndef _HUGE_ENUF +#define _HUGE_ENUF 1e+300 /* _HUGE_ENUF*_HUGE_ENUF must overflow */ +#endif /* _HUGE_ENUF */ + +#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF)) /* causes warning C4756: overflow in constant arithmetic (by design) */ +#define HUGE_VALD ((double)INFINITY) +#define HUGE_VALF ((float)INFINITY) +#define HUGE_VALL ((long double)INFINITY) +#define NAN ((float)(INFINITY * 0.0F)) + +#define _DENORM (-2) +#define _FINITE (-1) +#define _INFCODE 1 +#define _NANCODE 2 + +#define FP_INFINITE _INFCODE +#define FP_NAN _NANCODE +#define FP_NORMAL _FINITE +#define FP_SUBNORMAL _DENORM +#define FP_ZERO 0 + +#define _C2 1 /* 0 if not 2's complement */ +#define _FP_ILOGB0 (-0x7fffffff - _C2) +#define _FP_ILOGBNAN 0x7fffffff + +#define MATH_ERRNO 1 +#define MATH_ERREXCEPT 2 +#define math_errhandling (MATH_ERRNO | MATH_ERREXCEPT) + +/* MACROS FOR _fperrraise ARGUMENT */ +#define _FE_DIVBYZERO 0x04 +#define _FE_INEXACT 0x20 +#define _FE_INVALID 0x01 +#define _FE_OVERFLOW 0x08 +#define _FE_UNDERFLOW 0x10 + +#define _D0_C 3 /* little-endian, small long doubles */ +#define _D1_C 2 +#define _D2_C 1 +#define _D3_C 0 + +#define _DBIAS 0x3fe +#define _DOFF 4 + +#define _F0_C 1 /* little-endian */ +#define _F1_C 0 + +#define _FBIAS 0x7e +#define _FOFF 7 +#define _FRND 1 + +#define _L0_C 3 /* little-endian, 64-bit long doubles */ +#define _L1_C 2 +#define _L2_C 1 +#define _L3_C 0 + +#define _LBIAS 0x3fe +#define _LOFF 4 + +/* IEEE 754 double properties */ +#define _DFRAC ((unsigned short)((1 << _DOFF) - 1)) +#define _DMASK ((unsigned short)(0x7fff & ~_DFRAC)) +#define _DMAX ((unsigned short)((1 << (15 - _DOFF)) - 1)) +#define _DSIGN ((unsigned short)0x8000) + +/* IEEE 754 float properties */ +#define _FFRAC ((unsigned short)((1 << _FOFF) - 1)) +#define _FMASK ((unsigned short)(0x7fff & ~_FFRAC)) +#define _FMAX ((unsigned short)((1 << (15 - _FOFF)) - 1)) +#define _FSIGN ((unsigned short)0x8000) + +/* IEEE 754 long double properties */ +#define _LFRAC ((unsigned short)(-1)) +#define _LMASK ((unsigned short)0x7fff) +#define _LMAX ((unsigned short)0x7fff) +#define _LSIGN ((unsigned short)0x8000) + +#define HUGE_EXP (int)(_DMAX * 900L / 1000) +#define VHUGE_EXP (int)(_VMAX * 900L / 1000) +#define FHUGE_EXP (int)(_FMAX * 900L / 1000) +#define LHUGE_EXP (int)(_LMAX * 900L / 1000) + +#define DSIGN_C(_Val) (((_double_val *)(char *)&(_Val))->_Sh[_D0_C] & _DSIGN) +#define FSIGN_C(_Val) (((_float_val *)(char *)&(_Val))->_Sh[_F0_C] & _FSIGN) +#define LSIGN_C(_Val) (((_ldouble_val *)(char *)&(_Val))->_Sh[_L0_C] & _LSIGN) + +void __cdecl _fperrraise(_In_ int _Except); + +short _CRTIMP __cdecl _dclass(_In_ double _X); +short _CRTIMP __cdecl _ldclass(_In_ long double _X); +short _CRTIMP __cdecl _fdclass(_In_ float _X); + +int _CRTIMP __cdecl _dsign(_In_ double _X); +int _CRTIMP __cdecl _ldsign(_In_ long double _X); +int _CRTIMP __cdecl _fdsign(_In_ float _X); + +int _CRTIMP __cdecl _dpcomp(_In_ double _X, _In_ double _Y); +int _CRTIMP __cdecl _ldpcomp(_In_ long double _X, _In_ long double _Y); +int _CRTIMP __cdecl _fdpcomp(_In_ float _X, _In_ float _Y); + +short _CRTIMP __cdecl _dtest(_In_ double *_Px); +short _CRTIMP __cdecl _ldtest(_In_ long double *_Px); +short _CRTIMP __cdecl _fdtest(_In_ float *_Px); + +short _CRTIMP __cdecl _d_int(_In_ double *_Px, _In_ short _Xexp); +short _CRTIMP __cdecl _ld_int(_In_ long double *_Px, _In_ short _Xexp); +short _CRTIMP __cdecl _fd_int(_In_ float *_Px, _In_ short _Xexp); + +short _CRTIMP __cdecl _dscale(_In_ double *_Px, _In_ long _Lexp); +short _CRTIMP __cdecl _ldscale(_In_ long double *_Px, _In_ long _Lexp); +short _CRTIMP __cdecl _fdscale(_In_ float *_Px, _In_ long _Lexp); + +short _CRTIMP __cdecl _dunscale(_In_ short *_Pex, _In_ double *_Px); +short _CRTIMP __cdecl _ldunscale(_In_ short *_Pex, _In_ long double *_Px); +short _CRTIMP __cdecl _fdunscale(_In_ short *_Pex, _In_ float *_Px); + +short _CRTIMP __cdecl _dexp(_In_ double *_Px, _In_ double _Y, _In_ long _Eoff); +short _CRTIMP __cdecl _ldexp(_In_ long double *_Px, _In_ long double _Y, _In_ long _Eoff); +short _CRTIMP __cdecl _fdexp(_In_ float *_Px, _In_ float _Y, _In_ long _Eoff); + +short _CRTIMP __cdecl _dnorm(_In_ unsigned short *_Ps); +short _CRTIMP __cdecl _fdnorm(_In_ unsigned short *_Ps); + +double __cdecl _dpoly(_In_ double _X, _In_ const double *_Tab, _In_ int _N); +long double __cdecl _ldpoly(_In_ long double _X, _In_ const long double *_Tab, _In_ int _N); +float __cdecl _fdpoly(_In_ float _X, _In_ const float *_Tab, _In_ int _N); + +double _CRTIMP __cdecl _dlog(_In_ double _X, _In_ int _Baseflag); +long double _CRTIMP __cdecl _ldlog(_In_ long double _X, _In_ int _Baseflag); +float _CRTIMP __cdecl _fdlog(_In_ float _X, _In_ int _Baseflag); + +double _CRTIMP __cdecl _dsin(_In_ double _X, _In_ unsigned int _Qoff); +long double _CRTIMP __cdecl _ldsin(_In_ long double _X, _In_ unsigned int _Qoff); +float _CRTIMP __cdecl _fdsin(_In_ float _X, _In_ unsigned int _Qoff); + +/* double declarations */ +typedef union +{ /* pun floating type as integer array */ + unsigned short _Sh[8]; + double _Val; +} _double_val; + +/* float declarations */ +typedef union +{ /* pun floating type as integer array */ + unsigned short _Sh[8]; + float _Val; +} _float_val; + +/* long double declarations */ +typedef union +{ /* pun floating type as integer array */ + unsigned short _Sh[8]; + long double _Val; +} _ldouble_val; + +typedef union +{ /* pun float types as integer array */ + unsigned short _Word[8]; + float _Float; + double _Double; + long double _Long_double; +} _float_const; + +extern const _float_const _Denorm_C, _Inf_C, _Nan_C, _Snan_C, _Hugeval_C; +extern const _float_const _FDenorm_C, _FInf_C, _FNan_C, _FSnan_C; +extern const _float_const _LDenorm_C, _LInf_C, _LNan_C, _LSnan_C; + +extern const _float_const _Eps_C, _Rteps_C; +extern const _float_const _FEps_C, _FRteps_C; +extern const _float_const _LEps_C, _LRteps_C; + +extern const double _Zero_C, _Xbig_C; +extern const float _FZero_C, _FXbig_C; +extern const long double _LZero_C, _LXbig_C; + +#define _FP_LT 1 +#define _FP_EQ 2 +#define _FP_GT 4 + +#ifndef __cplusplus + +#define _CLASS_ARG(_Val) (sizeof ((_Val) + (float)0) == sizeof (float) ? 'f' : sizeof ((_Val) + (double)0) == sizeof (double) ? 'd' : 'l') +#define _CLASSIFY(_Val, _FFunc, _DFunc, _LDFunc) (_CLASS_ARG(_Val) == 'f' ? _FFunc((float)(_Val)) : _CLASS_ARG(_Val) == 'd' ? _DFunc((double)(_Val)) : _LDFunc((long double)(_Val))) +#define _CLASSIFY2(_Val1, _Val2, _FFunc, _DFunc, _LDFunc) (_CLASS_ARG((_Val1) + (_Val2)) == 'f' ? _FFunc((float)(_Val1), (float)(_Val2)) : _CLASS_ARG((_Val1) + (_Val2)) == 'd' ? _DFunc((double)(_Val1), (double)(_Val2)) : _LDFunc((long double)(_Val1), (long double)(_Val2))) + +#define fpclassify(_Val) (_CLASSIFY(_Val, _fdclass, _dclass, _ldclass)) +#define _FPCOMPARE(_Val1, _Val2) (_CLASSIFY2(_Val1, _Val2, _fdpcomp, _dpcomp, _ldpcomp)) + +#define isfinite(_Val) (fpclassify(_Val) <= 0) +#define isinf(_Val) (fpclassify(_Val) == FP_INFINITE) +#define isnan(_Val) (fpclassify(_Val) == FP_NAN) +#define isnormal(_Val) (fpclassify(_Val) == FP_NORMAL) +#define signbit(_Val) (_CLASSIFY(_Val, _fdsign, _dsign, _ldsign)) + +#define isgreater(x, y) ((_FPCOMPARE(x, y) & _FP_GT) != 0) +#define isgreaterequal(x, y) ((_FPCOMPARE(x, y) & (_FP_EQ | _FP_GT)) != 0) +#define isless(x, y) ((_FPCOMPARE(x, y) & _FP_LT) != 0) +#define islessequal(x, y) ((_FPCOMPARE(x, y) & (_FP_LT | _FP_EQ)) != 0) +#define islessgreater(x, y) ((_FPCOMPARE(x, y) & (_FP_LT | _FP_GT)) != 0) +#define isunordered(x, y) (_FPCOMPARE(x, y) == 0) + +#else /* __cplusplus */ + +extern "C++" { + +inline __nothrow int fpclassify(float _X) +{ + return (_fdtest(&_X)); +} + +inline __nothrow int fpclassify(double _X) +{ + return (_dtest(&_X)); +} + +inline __nothrow int fpclassify(long double _X) +{ + return (_ldtest(&_X)); +} + +inline __nothrow bool signbit(float _X) +{ + return (_fdsign(_X) != 0); +} + +inline __nothrow bool signbit(double _X) +{ + return (_dsign(_X) != 0); +} + +inline __nothrow bool signbit(long double _X) +{ + return (_ldsign(_X) != 0); +} + +inline __nothrow int _fpcomp(float _X, float _Y) +{ + return (_fdpcomp(_X, _Y)); +} + +inline __nothrow int _fpcomp(double _X, double _Y) +{ + return (_dpcomp(_X, _Y)); +} + +inline __nothrow int _fpcomp(long double _X, long double _Y) +{ + return (_ldpcomp(_X, _Y)); +} + +template<class _Trc, class _Tre> struct _Combined_type +{ // determine combined type + typedef float _Type; // (real, float) is float +}; + +template<> struct _Combined_type<float, double> +{ // determine combined type + typedef double _Type; +}; + +template<> struct _Combined_type<float, long double> +{ // determine combined type + typedef long double _Type; +}; + +template<class _Ty, class _T2> struct _Real_widened +{ // determine widened real type + typedef long double _Type; // default is long double +}; + +template<> struct _Real_widened<float, float> +{ // determine widened real type + typedef float _Type; +}; + +template<> struct _Real_widened<float, double> +{ // determine widened real type + typedef double _Type; +}; + +template<> struct _Real_widened<double, float> +{ // determine widened real type + typedef double _Type; +}; + +template<> struct _Real_widened<double, double> +{ // determine widened real type + typedef double _Type; +}; + +template<class _Ty> struct _Real_type +{ // determine equivalent real type + typedef double _Type; // default is double +}; + +template<> struct _Real_type<float> +{ // determine equivalent real type + typedef float _Type; +}; + +template<> struct _Real_type<long double> +{ // determine equivalent real type + typedef long double _Type; +}; + +template<class _T1, class _T2> inline __nothrow int _fpcomp(_T1 _X, _T2 _Y) +{ // compare _Left and _Right + typedef typename _Combined_type<float, + typename _Real_widened< + typename _Real_type<_T1>::_Type, + typename _Real_type<_T2>::_Type>::_Type>::_Type _Tw; + return (_fpcomp((_Tw)_X, (_Tw)_Y)); +} + +template<class _Ty> inline __nothrow bool isfinite(_Ty _X) +{ + return (fpclassify(_X) <= 0); +} + +template<class _Ty> inline __nothrow bool isinf(_Ty _X) +{ + return (fpclassify(_X) == FP_INFINITE); +} + +template<class _Ty> inline __nothrow bool isnan(_Ty _X) +{ + return (fpclassify(_X) == FP_NAN); +} + +template<class _Ty> inline __nothrow bool isnormal(_Ty _X) +{ + return (fpclassify(_X) == FP_NORMAL); +} + +template<class _Ty1, class _Ty2> inline __nothrow bool isgreater(_Ty1 _X, _Ty2 _Y) +{ + return ((_fpcomp(_X, _Y) & _FP_GT) != 0); +} + +template<class _Ty1, class _Ty2> inline __nothrow bool isgreaterequal(_Ty1 _X, _Ty2 _Y) +{ + return ((_fpcomp(_X, _Y) & (_FP_EQ | _FP_GT)) != 0); +} + +template<class _Ty1, class _Ty2> inline __nothrow bool isless(_Ty1 _X, _Ty2 _Y) +{ + return ((_fpcomp(_X, _Y) & _FP_LT) != 0); +} + +template<class _Ty1, class _Ty2> inline __nothrow bool islessequal(_Ty1 _X, _Ty2 _Y) +{ + return ((_fpcomp(_X, _Y) & (_FP_LT | _FP_EQ)) != 0); +} + +template<class _Ty1, class _Ty2> inline __nothrow bool islessgreater(_Ty1 _X, _Ty2 _Y) +{ + return ((_fpcomp(_X, _Y) & (_FP_LT | _FP_GT)) != 0); +} + +template<class _Ty1, class _Ty2> inline __nothrow bool isunordered(_Ty1 _X, _Ty2 _Y) +{ + return (_fpcomp(_X, _Y) == 0); +} + +} // extern "C++" + +#endif /* __cplusplus */ + +/* Function prototypes */ + +#if !defined (__assembler) +int __cdecl abs(_In_ int _X); +long __cdecl labs(_In_ long _X); +long long __cdecl llabs(_In_ long long _X); + +double __cdecl acos(_In_ double _X); +_CRTIMP double __cdecl acosh(_In_ double _X); +double __cdecl asin(_In_ double _X); +_CRTIMP double __cdecl asinh(_In_ double _X); +double __cdecl atan(_In_ double _X); +_CRTIMP double __cdecl atanh(_In_ double _X); +double __cdecl atan2(_In_ double _Y, _In_ double _X); + +_CRTIMP double __cdecl cbrt(_In_ double _X); +_CRTIMP double __cdecl copysign(_In_ double _X, _In_ double _Y); +double __cdecl cos(_In_ double _X); +double __cdecl cosh(_In_ double _X); +_CRTIMP double __cdecl erf(_In_ double _X); +_CRTIMP double __cdecl erfc(_In_ double _X); +double __cdecl exp(_In_ double _X); +_CRTIMP double __cdecl exp2(_In_ double _X); +_CRTIMP double __cdecl expm1(_In_ double _X); +_CRT_JIT_INTRINSIC double __cdecl fabs(_In_ double _X); +_CRTIMP double __cdecl fdim(_In_ double _X, _In_ double _Y); +_CRTIMP double __cdecl fma(_In_ double _X, _In_ double _Y, _In_ double _Z); +_CRTIMP double __cdecl fmax(_In_ double _X, _In_ double _Y); +_CRTIMP double __cdecl fmin(_In_ double _X, _In_ double _Y); +double __cdecl fmod(_In_ double _X, _In_ double _Y); +_CRTIMP int __cdecl ilogb(_In_ double _X); +_CRTIMP double __cdecl lgamma(_In_ double _X); +_CRTIMP long long __cdecl llrint(_In_ double _X); +_CRTIMP long long __cdecl llround(_In_ double _X); +double __cdecl log(_In_ double _X); +double __cdecl log10(_In_ double _X); +_CRTIMP double __cdecl log1p(_In_ double _X); +_CRTIMP double __cdecl log2(_In_ double _X); +_CRTIMP double __cdecl logb(_In_ double _X); +_CRTIMP long __cdecl lrint(_In_ double _X); +_CRTIMP long __cdecl lround(_In_ double _X); +_CRTIMP double __cdecl nan(_In_ const char *); +_CRTIMP double __cdecl nearbyint(_In_ double _X); +_CRTIMP double __cdecl nextafter(_In_ double _X, _In_ double _Y); +_CRTIMP double __cdecl nexttoward(_In_ double _X, _In_ long double _Y); +double __cdecl pow(_In_ double _X, _In_ double _Y); +_CRTIMP double __cdecl remainder(_In_ double _X, _In_ double _Y); +_CRTIMP double __cdecl remquo(_In_ double _X, _In_ double _Y, _Out_ int *_Z); +_CRTIMP double __cdecl rint(_In_ double _X); +_CRTIMP double __cdecl round(_In_ double _X); +_CRTIMP double __cdecl scalbln(_In_ double _X, _In_ long _Y); +_CRTIMP double __cdecl scalbn(_In_ double _X, _In_ int _Y); +double __cdecl sin(_In_ double _X); +double __cdecl sinh(_In_ double _X); +_CRT_JIT_INTRINSIC double __cdecl sqrt(_In_ double _X); +double __cdecl tan(_In_ double _X); +double __cdecl tanh(_In_ double _X); +_CRTIMP double __cdecl tgamma(_In_ double _X); +_CRTIMP double __cdecl trunc(_In_ double _X); + +_Check_return_ _CRTIMP double __cdecl atof(_In_z_ const char *_String); +_Check_return_ _CRTIMP double __cdecl _atof_l(_In_z_ const char *_String, _In_opt_ _locale_t _Locale); + +_CRTIMP double __cdecl _cabs(_In_ struct _complex _Complex_value); +_CRTIMP double __cdecl ceil(_In_ double _X); + +_Check_return_ _CRTIMP double __cdecl _chgsign (_In_ double _X); +_Check_return_ _CRTIMP double __cdecl _copysign (_In_ double _Number, _In_ double _Sign); + +_CRTIMP double __cdecl floor(_In_ double _X); +_CRTIMP double __cdecl frexp(_In_ double _X, _Out_ int * _Y); +_CRTIMP double __cdecl _hypot(_In_ double _X, _In_ double _Y); +_CRTIMP double __cdecl _j0(_In_ double _X ); +_CRTIMP double __cdecl _j1(_In_ double _X ); +_CRTIMP double __cdecl _jn(int _X, _In_ double _Y); +_CRTIMP double __cdecl ldexp(_In_ double _X, _In_ int _Y); + +#if defined (MRTDLL) || defined (_M_CEE_PURE) +int __CRTDECL _matherr(_Inout_ struct _exception * _Except); +#else /* defined (MRTDLL) || defined (_M_CEE_PURE) */ +int __cdecl _matherr(_Inout_ struct _exception * _Except); +#endif /* defined (MRTDLL) || defined (_M_CEE_PURE) */ + +_CRTIMP double __cdecl modf(_In_ double _X, _Out_ double * _Y); +_CRTIMP double __cdecl _y0(_In_ double _X); +_CRTIMP double __cdecl _y1(_In_ double _X); +_CRTIMP double __cdecl _yn(_In_ int _X, _In_ double _Y); + +__inline double __CRTDECL hypot(_In_ double _X, _In_ double _Y) +{ + return _hypot(_X, _Y); +} + + +_CRTIMP float __cdecl acoshf(_In_ float _X); +_CRTIMP float __cdecl asinhf(_In_ float _X); +_CRTIMP float __cdecl atanhf(_In_ float _X); +_CRTIMP float __cdecl cbrtf(_In_ float _X); +_CRTIMP float __cdecl _chgsignf(_In_ float _X); +_CRTIMP float __cdecl copysignf(_In_ float _X, _In_ float _Y); +_CRTIMP float __cdecl _copysignf(_In_ float _Number, _In_ float _Sign); +_CRTIMP float __cdecl erff(_In_ float _X); +_CRTIMP float __cdecl erfcf(_In_ float _X); +_CRTIMP float __cdecl expm1f(_In_ float _X); +_CRTIMP float __cdecl exp2f(_In_ float _X); +_CRTIMP float __cdecl fdimf(_In_ float _X, _In_ float _Y); +_CRTIMP float __cdecl fmaf(_In_ float _X, _In_ float _Y, _In_ float _Z); +_CRTIMP float __cdecl fmaxf(_In_ float _X, _In_ float _Y); +_CRTIMP float __cdecl fminf(_In_ float _X, _In_ float _Y); +_CRTIMP float __cdecl _hypotf(_In_ float _X, _In_ float _Y); +_CRTIMP int __cdecl ilogbf(_In_ float _X); +_CRTIMP float __cdecl lgammaf(_In_ float _X); +_CRTIMP long long __cdecl llrintf(_In_ float _X); +_CRTIMP long long __cdecl llroundf(_In_ float _X); +_CRTIMP float __cdecl log1pf(_In_ float _X); +_CRTIMP float __cdecl log2f(_In_ float _X); +_CRTIMP float __cdecl logbf(_In_ float _X); +_CRTIMP long __cdecl lrintf(_In_ float _X); +_CRTIMP long __cdecl lroundf(_In_ float _X); +_CRTIMP float __cdecl nanf(_In_ const char *); +_CRTIMP float __cdecl nearbyintf(_In_ float _X); +_CRTIMP float __cdecl nextafterf(_In_ float _X, _In_ float _Y); +_CRTIMP float __cdecl nexttowardf(_In_ float _X, _In_ long double _Y); +_CRTIMP float __cdecl remainderf(_In_ float _X, _In_ float _Y); +_CRTIMP float __cdecl remquof(_In_ float _X, _In_ float _Y, _Out_ int *_Z); +_CRTIMP float __cdecl rintf(_In_ float _X); +_CRTIMP float __cdecl roundf(_In_ float _X); +_CRTIMP float __cdecl scalblnf(_In_ float _X, _In_ long _Y); +_CRTIMP float __cdecl scalbnf(_In_ float _X, _In_ int _Y); +_CRTIMP float __cdecl tgammaf(_In_ float _X); +_CRTIMP float __cdecl truncf(_In_ float _X); + +#if defined (_M_IX86) + +_CRTIMP int __cdecl _set_SSE2_enable(_In_ int _Flag); + +#endif /* defined (_M_IX86) */ + +#if defined (_M_X64) + +_CRTIMP float __cdecl _logbf(_In_ float _X); +_CRTIMP float __cdecl _nextafterf(_In_ float _X, _In_ float _Y); +_CRTIMP int __cdecl _finitef(_In_ float _X); +_CRTIMP int __cdecl _isnanf(_In_ float _X); +_CRTIMP int __cdecl _fpclassf(_In_ float _X); + +_CRTIMP int __cdecl _set_FMA3_enable(_In_ int _Flag); + +#endif /* defined (_M_X64) */ + +#if defined (_M_ARM) + +_CRTIMP int __cdecl _finitef(_In_ float _X); +_CRTIMP float __cdecl _logbf(_In_ float _X); + +#endif /* defined (_M_ARM) */ + +#if defined (_M_X64) || defined (_M_ARM) + +_CRTIMP float __cdecl acosf(_In_ float _X); +_CRTIMP float __cdecl asinf(_In_ float _X); +_CRTIMP float __cdecl atan2f(_In_ float _Y, _In_ float _X); +_CRTIMP float __cdecl atanf(_In_ float _X); +_CRTIMP float __cdecl ceilf(_In_ float _X); +_CRTIMP float __cdecl cosf(_In_ float _X); +_CRTIMP float __cdecl coshf(_In_ float _X); +_CRTIMP float __cdecl expf(_In_ float _X); + +#else /* defined(_M_X64) || defined(_M_ARM) */ + +__inline float __CRTDECL acosf(_In_ float _X) +{ + return (float)acos(_X); +} + +__inline float __CRTDECL asinf(_In_ float _X) +{ + return (float)asin(_X); +} + +__inline float __CRTDECL atan2f(_In_ float _Y, _In_ float _X) +{ + return (float)atan2(_Y, _X); +} + +__inline float __CRTDECL atanf(_In_ float _X) +{ + return (float)atan(_X); +} + +__inline float __CRTDECL ceilf(_In_ float _X) +{ + return (float)ceil(_X); +} + +__inline float __CRTDECL cosf(_In_ float _X) +{ + return (float)cos(_X); +} + +__inline float __CRTDECL coshf(_In_ float _X) +{ + return (float)cosh(_X); +} + +__inline float __CRTDECL expf(_In_ float _X) +{ + return (float)exp(_X); +} + +#endif /* defined (_M_X64) || defined (_M_ARM) */ + +#if defined (_M_ARM) + +_CRT_JIT_INTRINSIC _CRTIMP float __cdecl fabsf(_In_ float _X); + +#else /* defined (_M_ARM) */ + +__inline float __CRTDECL fabsf(_In_ float _X) +{ + return (float)fabs(_X); +} + +#endif /* defined (_M_ARM) */ + +#if defined (_M_X64) || defined (_M_ARM) + +_CRTIMP float __cdecl floorf(_In_ float _X); +_CRTIMP float __cdecl fmodf(_In_ float _X, _In_ float _Y); + +#else /* defined (_M_X64) || defined (_M_ARM) */ + +__inline float __CRTDECL floorf(_In_ float _X) +{ + return (float)floor(_X); +} + +__inline float __CRTDECL fmodf(_In_ float _X, _In_ float _Y) +{ + return (float)fmod(_X, _Y); +} + +#endif /* !defined(_M_X64) && !defined(_M_ARM) */ + +__inline float __CRTDECL frexpf(_In_ float _X, _Out_ int *_Y) +{ + return (float)frexp(_X, _Y); +} + +__inline float __CRTDECL hypotf(_In_ float _X, _In_ float _Y) +{ + return _hypotf(_X, _Y); +} + +__inline float __CRTDECL ldexpf(_In_ float _X, _In_ int _Y) +{ + return (float)ldexp(_X, _Y); +} + +#if defined (_M_X64) || defined (_M_ARM) + +_CRTIMP float __cdecl log10f(_In_ float _X); +_CRTIMP float __cdecl logf(_In_ float _X); +_CRTIMP float __cdecl modff(_In_ float _X, _Out_ float *_Y); +_CRTIMP float __cdecl powf(_In_ float _X, _In_ float _Y); +_CRTIMP float __cdecl sinf(_In_ float _X); +_CRTIMP float __cdecl sinhf(_In_ float _X); +_CRTIMP float __cdecl sqrtf(_In_ float _X); +_CRTIMP float __cdecl tanf(_In_ float _X); +_CRTIMP float __cdecl tanhf(_In_ float _X); + +#else /* defined (_M_X64) || defined (_M_ARM) */ + +__inline float __CRTDECL log10f(_In_ float _X) +{ + return (float)log10(_X); +} + +__inline float __CRTDECL logf(_In_ float _X) +{ + return (float)log(_X); +} + +__inline float __CRTDECL modff(_In_ float _X, _Out_ float *_Y) +{ + double _F, _I; + _F = modf(_X, &_I); + *_Y = (float)_I; + return (float)_F; +} + +__inline float __CRTDECL powf(_In_ float _X, _In_ float _Y) +{ + return (float)pow(_X, _Y); +} + +__inline float __CRTDECL sinf(_In_ float _X) +{ + return (float)sin(_X); +} + +__inline float __CRTDECL sinhf(_In_ float _X) +{ + return (float)sinh(_X); +} + +__inline float __CRTDECL sqrtf(_In_ float _X) +{ + return (float)sqrt(_X); +} + +__inline float __CRTDECL tanf(_In_ float _X) +{ + return (float)tan(_X); +} + +__inline float __CRTDECL tanhf(_In_ float _X) +{ + return (float)tanh(_X); +} + +#endif /* defined (_M_X64) || defined (_M_ARM) */ + +_CRTIMP long double __cdecl acoshl(_In_ long double _X); + +__inline long double __CRTDECL acosl(_In_ long double _X) +{ + return acos((double)_X); +} + +_CRTIMP long double __cdecl asinhl(_In_ long double _X); + +__inline long double __CRTDECL asinl(_In_ long double _X) +{ + return asin((double)_X); +} + +__inline long double __CRTDECL atan2l(_In_ long double _Y, _In_ long double _X) +{ + return atan2((double)_Y, (double)_X); +} + +_CRTIMP long double __cdecl atanhl(_In_ long double _X); + +__inline long double __CRTDECL atanl(_In_ long double _X) +{ + return atan((double)_X); +} + +_CRTIMP long double __cdecl cbrtl(_In_ long double _X); + +__inline long double __CRTDECL ceill(_In_ long double _X) +{ + return ceil((double)_X); +} + +__inline long double __CRTDECL _chgsignl(_In_ long double _X) +{ + return _chgsign((double)_X); +} + +_CRTIMP long double __cdecl copysignl(_In_ long double _X, _In_ long double _Y); + +__inline long double __CRTDECL _copysignl(_In_ long double _X, _In_ long double _Y) +{ + return _copysign((double)_X, (double)_Y); +} + +__inline long double __CRTDECL coshl(_In_ long double _X) +{ + return cosh((double)_X); +} + +__inline long double __CRTDECL cosl(_In_ long double _X) +{ + return cos((double)_X); +} + +_CRTIMP long double __cdecl erfl(_In_ long double _X); +_CRTIMP long double __cdecl erfcl(_In_ long double _X); + +__inline long double __CRTDECL expl(_In_ long double _X) +{ + return exp((double)_X); +} + +_CRTIMP long double __cdecl exp2l(_In_ long double _X); +_CRTIMP long double __cdecl expm1l(_In_ long double _X); + +__inline long double __CRTDECL fabsl(_In_ long double _X) +{ + return fabs((double)_X); +} + +_CRTIMP long double __cdecl fdiml(_In_ long double _X, _In_ long double _Y); + +__inline long double __CRTDECL floorl(_In_ long double _X) +{ + return floor((double)_X); +} + +_CRTIMP long double __cdecl fmal(_In_ long double _X, _In_ long double _Y, _In_ long double _Z); +_CRTIMP long double __cdecl fmaxl(_In_ long double _X, _In_ long double _Y); +_CRTIMP long double __cdecl fminl(_In_ long double _X, _In_ long double _Y); + +__inline long double __CRTDECL fmodl(_In_ long double _X, _In_ long double _Y) +{ + return fmod((double)_X, (double)_Y); +} + +__inline long double __CRTDECL frexpl(_In_ long double _X, _Out_ int *_Y) +{ + return frexp((double)_X, _Y); +} + +_CRTIMP int __cdecl ilogbl(_In_ long double _X); + +__inline long double __CRTDECL _hypotl(_In_ long double _X, _In_ long double _Y) +{ + return _hypot((double)_X, (double)_Y); +} + +__inline long double __CRTDECL hypotl(_In_ long double _X, _In_ long double _Y) +{ + return _hypot((double)_X, (double)_Y); +} + +__inline long double __CRTDECL ldexpl(_In_ long double _X, _In_ int _Y) +{ + return ldexp((double)_X, _Y); +} + +_CRTIMP long double __cdecl lgammal(_In_ long double _X); +_CRTIMP long long __cdecl llrintl(_In_ long double _X); +_CRTIMP long long __cdecl llroundl(_In_ long double _X); + +__inline long double __CRTDECL logl(_In_ long double _X) +{ + return log((double)_X); +} + +__inline long double __CRTDECL log10l(_In_ long double _X) +{ + return log10((double)_X); +} + +_CRTIMP long double __cdecl log1pl(_In_ long double _X); +_CRTIMP long double __cdecl log2l(_In_ long double _X); +_CRTIMP long double __cdecl logbl(_In_ long double _X); +_CRTIMP long __cdecl lrintl(_In_ long double _X); +_CRTIMP long __cdecl lroundl(_In_ long double _X); + +__inline long double __CRTDECL modfl(_In_ long double _X, _Out_ long double *_Y) +{ + double _F, _I; + _F = modf((double)_X, &_I); + *_Y = _I; + return _F; +} +_CRTIMP long double __cdecl nanl(_In_ const char *); +_CRTIMP long double __cdecl nearbyintl(_In_ long double _X); +_CRTIMP long double __cdecl nextafterl(_In_ long double _X, _In_ long double _Y); +_CRTIMP long double __cdecl nexttowardl(_In_ long double _X, _In_ long double _Y); + +__inline long double __CRTDECL powl(_In_ long double _X, _In_ long double _Y) +{ + return pow((double)_X, (double)_Y); +} + +_CRTIMP long double __cdecl remainderl(_In_ long double _X, _In_ long double _Y); +_CRTIMP long double __cdecl remquol(_In_ long double _X, _In_ long double _Y, _Out_ int *_Z); +_CRTIMP long double __cdecl rintl(_In_ long double _X); +_CRTIMP long double __cdecl roundl(_In_ long double _X); +_CRTIMP long double __cdecl scalblnl(_In_ long double _X, _In_ long _Y); +_CRTIMP long double __cdecl scalbnl(_In_ long double _X, _In_ int _Y); + +__inline long double __CRTDECL sinhl(_In_ long double _X) +{ + return sinh((double)_X); +} + +__inline long double __CRTDECL sinl(_In_ long double _X) +{ + return sin((double)_X); +} + +__inline long double __CRTDECL sqrtl(_In_ long double _X) +{ + return sqrt((double)_X); +} + +__inline long double __CRTDECL tanhl(_In_ long double _X) +{ + return tanh((double)_X); +} + +__inline long double __CRTDECL tanl(_In_ long double _X) +{ + return tan((double)_X); +} + +_CRTIMP long double __cdecl tgammal(_In_ long double _X); +_CRTIMP long double __cdecl truncl(_In_ long double _X); + +#ifndef __cplusplus +#define _matherrl _matherr +#endif /* __cplusplus */ +#endif /* !defined (__assembler) */ + +#if !__STDC__ + +/* Non-ANSI names for compatibility */ + +#define DOMAIN _DOMAIN +#define SING _SING +#define OVERFLOW _OVERFLOW +#define UNDERFLOW _UNDERFLOW +#define TLOSS _TLOSS +#define PLOSS _PLOSS + +#define matherr _matherr + +#ifndef __assembler + +#if !defined (_M_CEE_PURE) +_CRTIMP extern double HUGE; +#else /* !defined (_M_CEE_PURE) */ + const double HUGE = _HUGE; +#endif /* !defined (_M_CEE_PURE) */ + +_CRT_NONSTDC_DEPRECATE(_j0) _CRTIMP double __cdecl j0(_In_ double _X); +_CRT_NONSTDC_DEPRECATE(_j1) _CRTIMP double __cdecl j1(_In_ double _X); +_CRT_NONSTDC_DEPRECATE(_jn) _CRTIMP double __cdecl jn(_In_ int _X, _In_ double _Y); +_CRT_NONSTDC_DEPRECATE(_y0) _CRTIMP double __cdecl y0(_In_ double _X); +_CRT_NONSTDC_DEPRECATE(_y1) _CRTIMP double __cdecl y1(_In_ double _X); +_CRT_NONSTDC_DEPRECATE(_yn) _CRTIMP double __cdecl yn(_In_ int _X, _In_ double _Y); + +#endif /* __assembler */ +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} + +extern "C++" { + +template<class _Ty> inline + _Ty _Pow_int(_Ty _X, int _Y) throw() + {unsigned int _N; + if (_Y >= 0) + _N = (unsigned int)_Y; + else + _N = (unsigned int)(-_Y); + for (_Ty _Z = _Ty(1); ; _X *= _X) + {if ((_N & 1) != 0) + _Z *= _X; + if ((_N >>= 1) == 0) + return (_Y < 0 ? _Ty(1) / _Z : _Z); }} + +inline double __CRTDECL abs(_In_ double _X) throw() + {return (fabs(_X)); } +inline double __CRTDECL pow(_In_ double _X, _In_ int _Y) throw() + {return (_Pow_int(_X, _Y)); } +inline float __CRTDECL abs(_In_ float _X) throw() + {return (fabsf(_X)); } +inline float __CRTDECL acos(_In_ float _X) throw() + {return (acosf(_X)); } +inline float __CRTDECL acosh(_In_ float _X) throw() + {return (acoshf(_X)); } +inline float __CRTDECL asin(_In_ float _X) throw() + {return (asinf(_X)); } +inline float __CRTDECL asinh(_In_ float _X) throw() + {return (asinhf(_X)); } +inline float __CRTDECL atan(_In_ float _X) throw() + {return (atanf(_X)); } +inline float __CRTDECL atanh(_In_ float _X) throw() + {return (atanhf(_X)); } +inline float __CRTDECL atan2(_In_ float _Y, _In_ float _X) throw() + {return (atan2f(_Y, _X)); } +inline float __CRTDECL cbrt(_In_ float _X) throw() + {return (cbrtf(_X)); } +inline float __CRTDECL ceil(_In_ float _X) throw() + {return (ceilf(_X)); } +inline float __CRTDECL copysign(_In_ float _X, _In_ float _Y) throw() + {return (copysignf(_X, _Y)); } +inline float __CRTDECL cos(_In_ float _X) throw() + {return (cosf(_X)); } +inline float __CRTDECL cosh(_In_ float _X) throw() + {return (coshf(_X)); } +inline float __CRTDECL erf(_In_ float _X) throw() + {return (erff(_X)); } +inline float __CRTDECL erfc(_In_ float _X) throw() + {return (erfcf(_X)); } +inline float __CRTDECL exp(_In_ float _X) throw() + {return (expf(_X)); } +inline float __CRTDECL exp2(_In_ float _X) throw() + {return (exp2f(_X)); } +inline float __CRTDECL expm1(_In_ float _X) throw() + {return (expm1f(_X)); } +inline float __CRTDECL fabs(_In_ float _X) throw() + {return (fabsf(_X)); } +inline float __CRTDECL fdim(_In_ float _X, _In_ float _Y) throw() + {return (fdimf(_X, _Y)); } +inline float __CRTDECL floor(_In_ float _X) throw() + {return (floorf(_X)); } +inline float __CRTDECL fma(_In_ float _X, _In_ float _Y, _In_ float _Z) throw() + {return (fmaf(_X, _Y, _Z)); } +inline float __CRTDECL fmax(_In_ float _X, _In_ float _Y) throw() + {return (fmaxf(_X, _Y)); } +inline float __CRTDECL fmin(_In_ float _X, _In_ float _Y) throw() + {return (fminf(_X, _Y)); } +inline float __CRTDECL fmod(_In_ float _X, _In_ float _Y) throw() + {return (fmodf(_X, _Y)); } +inline float __CRTDECL frexp(_In_ float _X, _Out_ int * _Y) throw() + {return (frexpf(_X, _Y)); } +inline float __CRTDECL hypot(_In_ float _X, _In_ float _Y) throw() + {return (hypotf(_X, _Y)); } +inline int __CRTDECL ilogb(_In_ float _X) throw() + {return (ilogbf(_X)); } +inline float __CRTDECL ldexp(_In_ float _X, _In_ int _Y) throw() + {return (ldexpf(_X, _Y)); } +inline float __CRTDECL lgamma(_In_ float _X) throw() + {return (lgammaf(_X)); } +inline long long __CRTDECL llrint(_In_ float _X) throw() + {return (llrintf(_X)); } +inline long long __CRTDECL llround(_In_ float _X) throw() + {return (llroundf(_X)); } +inline float __CRTDECL log(_In_ float _X) throw() + {return (logf(_X)); } +inline float __CRTDECL log10(_In_ float _X) throw() + {return (log10f(_X)); } +inline float __CRTDECL log1p(_In_ float _X) throw() + {return (log1pf(_X)); } +inline float __CRTDECL log2(_In_ float _X) throw() + {return (log2f(_X)); } +inline float __CRTDECL logb(_In_ float _X) throw() + {return (logbf(_X)); } +inline long __CRTDECL lrint(_In_ float _X) throw() + {return (lrintf(_X)); } +inline long __CRTDECL lround(_In_ float _X) throw() + {return (lroundf(_X)); } +inline float __CRTDECL modf(_In_ float _X, _Out_ float * _Y) throw() + {return (modff(_X, _Y)); } +inline float __CRTDECL nearbyint(_In_ float _X) throw() + {return (nearbyintf(_X)); } +inline float __CRTDECL nextafter(_In_ float _X, _In_ float _Y) throw() + {return (nextafterf(_X, _Y)); } +inline float __CRTDECL nexttoward(_In_ float _X, _In_ long double _Y) throw() + {return (nexttowardf(_X, _Y)); } +inline float __CRTDECL pow(_In_ float _X, _In_ float _Y) throw() + {return (powf(_X, _Y)); } +inline float __CRTDECL pow(_In_ float _X, _In_ int _Y) throw() + {return (_Pow_int(_X, _Y)); } +inline float __CRTDECL remainder(_In_ float _X, _In_ float _Y) throw() + {return (remainderf(_X, _Y)); } +inline float __CRTDECL remquo(_In_ float _X, _In_ float _Y, _Out_ int *_Z) throw() + {return (remquof(_X, _Y, _Z)); } +inline float __CRTDECL rint(_In_ float _X) throw() + {return (rintf(_X)); } +inline float __CRTDECL round(_In_ float _X) throw() + {return (roundf(_X)); } +inline float __CRTDECL scalbln(_In_ float _X, _In_ long _Y) throw() + {return (scalblnf(_X, _Y)); } +inline float __CRTDECL scalbn(_In_ float _X, _In_ int _Y) throw() + {return (scalbnf(_X, _Y)); } +inline float __CRTDECL sin(_In_ float _X) throw() + {return (sinf(_X)); } +inline float __CRTDECL sinh(_In_ float _X) throw() + {return (sinhf(_X)); } +inline float __CRTDECL sqrt(_In_ float _X) throw() + {return (sqrtf(_X)); } +inline float __CRTDECL tan(_In_ float _X) throw() + {return (tanf(_X)); } +inline float __CRTDECL tanh(_In_ float _X) throw() + {return (tanhf(_X)); } +inline float __CRTDECL tgamma(_In_ float _X) throw() + {return (tgammaf(_X)); } +inline float __CRTDECL trunc(_In_ float _X) throw() + {return (truncf(_X)); } +inline long double __CRTDECL abs(_In_ long double _X) throw() + {return (fabsl(_X)); } +inline long double __CRTDECL acos(_In_ long double _X) throw() + {return (acosl(_X)); } +inline long double __CRTDECL acosh(_In_ long double _X) throw() + {return (acoshl(_X)); } +inline long double __CRTDECL asin(_In_ long double _X) throw() + {return (asinl(_X)); } +inline long double __CRTDECL asinh(_In_ long double _X) throw() + {return (asinhl(_X)); } +inline long double __CRTDECL atan(_In_ long double _X) throw() + {return (atanl(_X)); } +inline long double __CRTDECL atanh(_In_ long double _X) throw() + {return (atanhl(_X)); } +inline long double __CRTDECL atan2(_In_ long double _Y, _In_ long double _X) throw() + {return (atan2l(_Y, _X)); } +inline long double __CRTDECL cbrt(_In_ long double _X) throw() + {return (cbrtl(_X)); } +inline long double __CRTDECL ceil(_In_ long double _X) throw() + {return (ceill(_X)); } +inline long double __CRTDECL copysign(_In_ long double _X, _In_ long double _Y) throw() + {return (copysignl(_X, _Y)); } +inline long double __CRTDECL cos(_In_ long double _X) throw() + {return (cosl(_X)); } +inline long double __CRTDECL cosh(_In_ long double _X) throw() + {return (coshl(_X)); } +inline long double __CRTDECL erf(_In_ long double _X) throw() + {return (erfl(_X)); } +inline long double __CRTDECL erfc(_In_ long double _X) throw() + {return (erfcl(_X)); } +inline long double __CRTDECL exp(_In_ long double _X) throw() + {return (expl(_X)); } +inline long double __CRTDECL exp2(_In_ long double _X) throw() + {return (exp2l(_X)); } +inline long double __CRTDECL expm1(_In_ long double _X) throw() + {return (expm1l(_X)); } +inline long double __CRTDECL fabs(_In_ long double _X) throw() + {return (fabsl(_X)); } +inline long double __CRTDECL fdim(_In_ long double _X, _In_ long double _Y) throw() + {return (fdiml(_X, _Y)); } +inline long double __CRTDECL floor(_In_ long double _X) throw() + {return (floorl(_X)); } +inline long double __CRTDECL fma(_In_ long double _X, _In_ long double _Y, _In_ long double _Z) throw() + {return (fmal(_X, _Y, _Z)); } +inline long double __CRTDECL fmax(_In_ long double _X, _In_ long double _Y) throw() + {return (fmaxl(_X, _Y)); } +inline long double __CRTDECL fmin(_In_ long double _X, _In_ long double _Y) throw() + {return (fminl(_X, _Y)); } +inline long double __CRTDECL fmod(_In_ long double _X, _In_ long double _Y) throw() + {return (fmodl(_X, _Y)); } +inline long double __CRTDECL frexp(_In_ long double _X, _Out_ int * _Y) throw() + {return (frexpl(_X, _Y)); } +inline long double __CRTDECL hypot(_In_ long double _X, _In_ long double _Y) throw() + {return (hypotl(_X, _Y)); } +inline int __CRTDECL ilogb(_In_ long double _X) throw() + {return (ilogbl(_X)); } +inline long double __CRTDECL ldexp(_In_ long double _X, _In_ int _Y) throw() + {return (ldexpl(_X, _Y)); } +inline long double __CRTDECL lgamma(_In_ long double _X) throw() + {return (lgammal(_X)); } +inline long long __CRTDECL llrint(_In_ long double _X) throw() + {return (llrintl(_X)); } +inline long long __CRTDECL llround(_In_ long double _X) throw() + {return (llroundl(_X)); } +inline long double __CRTDECL log(_In_ long double _X) throw() + {return (logl(_X)); } +inline long double __CRTDECL log10(_In_ long double _X) throw() + {return (log10l(_X)); } +inline long double __CRTDECL log1p(_In_ long double _X) throw() + {return (log1pl(_X)); } +inline long double __CRTDECL log2(_In_ long double _X) throw() + {return (log2l(_X)); } +inline long double __CRTDECL logb(_In_ long double _X) throw() + {return (logbl(_X)); } +inline long __CRTDECL lrint(_In_ long double _X) throw() + {return (lrintl(_X)); } +inline long __CRTDECL lround(_In_ long double _X) throw() + {return (lroundl(_X)); } +inline long double __CRTDECL modf(_In_ long double _X, _Out_ long double * _Y) throw() + {return (modfl(_X, _Y)); } +inline long double __CRTDECL nearbyint(_In_ long double _X) throw() + {return (nearbyintl(_X)); } +inline long double __CRTDECL nextafter(_In_ long double _X, _In_ long double _Y) throw() + {return (nextafterl(_X, _Y)); } +inline long double __CRTDECL nexttoward(_In_ long double _X, _In_ long double _Y) throw() + {return (nexttowardl(_X, _Y)); } +inline long double __CRTDECL pow(_In_ long double _X, _In_ long double _Y) throw() + {return (powl(_X, _Y)); } +inline long double __CRTDECL pow(_In_ long double _X, _In_ int _Y) throw() + {return (_Pow_int(_X, _Y)); } +inline long double __CRTDECL remainder(_In_ long double _X, _In_ long double _Y) throw() + {return (remainderl(_X, _Y)); } +inline long double __CRTDECL remquo(_In_ long double _X, _In_ long double _Y, _Out_ int *_Z) throw() + {return (remquol(_X, _Y, _Z)); } +inline long double __CRTDECL rint(_In_ long double _X) throw() + {return (rintl(_X)); } +inline long double __CRTDECL round(_In_ long double _X) throw() + {return (roundl(_X)); } +inline long double __CRTDECL scalbln(_In_ long double _X, _In_ long _Y) throw() + {return (scalblnl(_X, _Y)); } +inline long double __CRTDECL scalbn(_In_ long double _X, _In_ int _Y) throw() + {return (scalbnl(_X, _Y)); } +inline long double __CRTDECL sin(_In_ long double _X) throw() + {return (sinl(_X)); } +inline long double __CRTDECL sinh(_In_ long double _X) throw() + {return (sinhl(_X)); } +inline long double __CRTDECL sqrt(_In_ long double _X) throw() + {return (sqrtl(_X)); } +inline long double __CRTDECL tan(_In_ long double _X) throw() + {return (tanl(_X)); } +inline long double __CRTDECL tanh(_In_ long double _X) throw() + {return (tanhl(_X)); } +inline long double __CRTDECL tgamma(_In_ long double _X) throw() + {return (tgammal(_X)); } +inline long double __CRTDECL trunc(_In_ long double _X) throw() + {return (truncl(_X)); } + +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_MATH */ + +#if defined (_USE_MATH_DEFINES) && !defined (_MATH_DEFINES_DEFINED) +#define _MATH_DEFINES_DEFINED + +/* Define _USE_MATH_DEFINES before including math.h to expose these macro + * definitions for common math constants. These are placed under an #ifdef + * since these commonly-defined names are not part of the C/C++ standards. + */ + +/* Definitions of useful mathematical constants + * M_E - e + * M_LOG2E - log2(e) + * M_LOG10E - log10(e) + * M_LN2 - ln(2) + * M_LN10 - ln(10) + * M_PI - pi + * M_PI_2 - pi/2 + * M_PI_4 - pi/4 + * M_1_PI - 1/pi + * M_2_PI - 2/pi + * M_2_SQRTPI - 2/sqrt(pi) + * M_SQRT2 - sqrt(2) + * M_SQRT1_2 - 1/sqrt(2) + */ + +#define M_E 2.71828182845904523536 +#define M_LOG2E 1.44269504088896340736 +#define M_LOG10E 0.434294481903251827651 +#define M_LN2 0.693147180559945309417 +#define M_LN10 2.30258509299404568402 +#define M_PI 3.14159265358979323846 +#define M_PI_2 1.57079632679489661923 +#define M_PI_4 0.785398163397448309616 +#define M_1_PI 0.318309886183790671538 +#define M_2_PI 0.636619772367581343076 +#define M_2_SQRTPI 1.12837916709551257390 +#define M_SQRT2 1.41421356237309504880 +#define M_SQRT1_2 0.707106781186547524401 + +#endif /* defined (_USE_MATH_DEFINES) && !defined (_MATH_DEFINES_DEFINED) */ diff --git a/test_data/lots_of_files/mbctype.h b/test_data/lots_of_files/mbctype.h new file mode 100644 index 0000000..700649b --- /dev/null +++ b/test_data/lots_of_files/mbctype.h @@ -0,0 +1,189 @@ +/*** +*mbctype.h - MBCS character conversion macros +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines macros for MBCS character classification/conversion. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_MBCTYPE +#define _INC_MBCTYPE + +#include <crtdefs.h> +#include <ctype.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* + * MBCS - Multi-Byte Character Set + */ + +/* + * This declaration allows the user access the _mbctype[] look-up array. + */ +#ifdef _CRTBLD +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_Check_return_ _CRTIMP unsigned char * __cdecl __p__mbctype(void); +_Check_return_ _CRTIMP unsigned char * __cdecl __p__mbcasemap(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#endif /* _CRTBLD */ + +#if !defined (_M_CEE_PURE) +/* No data exports in pure code */ +_CRTIMP extern unsigned char _mbctype[]; +_CRTIMP extern unsigned char _mbcasemap[]; +#else /* !defined (_M_CEE_PURE) */ +_Check_return_ _CRTIMP unsigned char * __cdecl __p__mbctype(void); +_Check_return_ _CRTIMP unsigned char * __cdecl __p__mbcasemap(void); +#define _mbctype (__p__mbctype()) +#define _mbcasemap (__p__mbcasemap()) +#endif /* !defined (_M_CEE_PURE) */ + +#ifdef _CRTBLD +extern pthreadmbcinfo __ptmbcinfo; +extern int __globallocalestatus; +extern int __locale_changed; +extern struct threadmbcinfostruct __initialmbcinfo; +pthreadmbcinfo __cdecl __updatetmbcinfo(void); +#endif /* _CRTBLD */ + +/* bit masks for MBCS character types */ + +#define _MS 0x01 /* MBCS single-byte symbol */ +#define _MP 0x02 /* MBCS punct */ +#define _M1 0x04 /* MBCS 1st (lead) byte */ +#define _M2 0x08 /* MBCS 2nd byte*/ + +#define _SBUP 0x10 /* SBCS upper char */ +#define _SBLOW 0x20 /* SBCS lower char */ + +/* byte types */ + +#define _MBC_SINGLE 0 /* valid single byte char */ +#define _MBC_LEAD 1 /* lead byte */ +#define _MBC_TRAIL 2 /* trailing byte */ +#define _MBC_ILLEGAL (-1) /* illegal byte */ + +#define _KANJI_CP 932 + +/* _setmbcp parameter defines */ +#define _MB_CP_SBCS 0 +#define _MB_CP_OEM -2 +#define _MB_CP_ANSI -3 +#define _MB_CP_LOCALE -4 + + +#ifndef _MBCTYPE_DEFINED + +/* MB control routines */ + +_CRTIMP int __cdecl _setmbcp(_In_ int _CodePage); +_CRTIMP int __cdecl _getmbcp(void); + + +/* MBCS character classification function prototypes */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +/* byte routines */ +_Check_return_ _CRTIMP int __cdecl _ismbbkalnum( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbkalnum_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbkana( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbkana_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbkpunct( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbkpunct_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbkprint( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbkprint_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbalpha( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbalpha_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbpunct( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbpunct_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbblank( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbblank_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbalnum( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbalnum_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbprint( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbprint_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbgraph( _In_ unsigned int _C ); +_Check_return_ _CRTIMP int __cdecl _ismbbgraph_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale); + +#ifndef _MBLEADTRAIL_DEFINED +_Check_return_ _CRTIMP int __cdecl _ismbblead( _In_ unsigned int _C); +_Check_return_ _CRTIMP int __cdecl _ismbblead_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale ); +_Check_return_ _CRTIMP int __cdecl _ismbbtrail( _In_ unsigned int _C); +_Check_return_ _CRTIMP int __cdecl _ismbbtrail_l(_In_ unsigned int _C, _In_opt_ _locale_t _Locale ); +_Check_return_ _CRTIMP int __cdecl _ismbslead(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos); +_Check_return_ _CRTIMP int __cdecl _ismbslead_l(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbstrail(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos); +_Check_return_ _CRTIMP int __cdecl _ismbstrail_l(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos, _In_opt_ _locale_t _Locale); + +#define _MBLEADTRAIL_DEFINED +#endif /* _MBLEADTRAIL_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#define _MBCTYPE_DEFINED +#endif /* _MBCTYPE_DEFINED */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +/* + * char byte classification macros + */ + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define _ismbbkalnum(_c) ((_mbctype+1)[(unsigned char)(_c)] & _MS) +#define _ismbbkprint(_c) ((_mbctype+1)[(unsigned char)(_c)] & (_MS|_MP)) +#define _ismbbkpunct(_c) ((_mbctype+1)[(unsigned char)(_c)] & _MP) + +#define _ismbbalnum(_c) (((_pctype)[(unsigned char)(_c)] & (_ALPHA|_DIGIT))||_ismbbkalnum(_c)) +#define _ismbbalpha(_c) (((_pctype)[(unsigned char)(_c)] & (_ALPHA))||_ismbbkalnum(_c)) +#define _ismbbgraph(_c) (((_pctype)[(unsigned char)(_c)] & (_PUNCT|_ALPHA|_DIGIT))||_ismbbkprint(_c)) +#define _ismbbprint(_c) (((_pctype)[(unsigned char)(_c)] & (_BLANK|_PUNCT|_ALPHA|_DIGIT))||_ismbbkprint(_c)) +#define _ismbbpunct(_c) (((_pctype)[(unsigned char)(_c)] & _PUNCT)||_ismbbkpunct(_c)) +#define _ismbbblank(_c) (((_c) == '\t') ? _BLANK : (_pctype)[(unsigned char)(_c)] & _BLANK) + +#define _ismbblead(_c) ((_mbctype+1)[(unsigned char)(_c)] & _M1) +#define _ismbbtrail(_c) ((_mbctype+1)[(unsigned char)(_c)] & _M2) + +#define _ismbbkana(_c) ((_mbctype+1)[(unsigned char)(_c)] & (_MS|_MP)) +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ + +#ifdef _CRTBLD +#define _ismbbalnum_l(_c, pt) ((((pt)->locinfo->pctype)[(unsigned char)(_c)] & \ + (_ALPHA|_DIGIT)) || \ + (((pt)->mbcinfo->mbctype+1)[(unsigned char)(_c)] & _MS)) +#define _ismbbalpha_l(_c, pt) ((((pt)->locinfo->pctype)[(unsigned char)(_c)] & \ + (_ALPHA)) || \ + (((pt)->mbcinfo->mbctype+1)[(unsigned char)(_c)] & _MS)) +#define _ismbbgraph_l(_c, pt) ((((pt)->locinfo->pctype)[(unsigned char)(_c)] & \ + (_PUNCT|_ALPHA|_DIGIT)) || \ + (((pt)->mbcinfo->mbctype+1)[(unsigned char)(_c)] & (_MS|_MP))) +#define _ismbbprint_l(_c, pt) ((((pt)->locinfo->pctype)[(unsigned char)(_c)] & \ + (_BLANK|_PUNCT|_ALPHA|_DIGIT)) || \ + (((pt)->mbcinfo->mbctype + 1)[(unsigned char)(_c)] & (_MS|_MP))) +#define _ismbbpunct_l(_c, pt) ((((pt)->locinfo->pctype)[(unsigned char)(_c)] & _PUNCT) || \ + (((pt)->mbcinfo->mbctype+1)[(unsigned char)(_c)] & _MP)) +#define _ismbbblank_l(_c, pt) (((_c) == '\t') ? _BLANK : (((pt)->locinfo->pctype)[(unsigned char)(_c)] & _BLANK) || \ + (((pt)->mbcinfo->mbctype+1)[(unsigned char)(_c)] & _MP)) +#define _ismbblead_l(_c, p) ((p->mbcinfo->mbctype + 1)[(unsigned char)(_c)] & _M1) +#define _ismbbtrail_l(_c, p) ((p->mbcinfo->mbctype + 1)[(unsigned char)(_c)] & _M2) +#endif /* _CRTBLD */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_MBCTYPE */ diff --git a/test_data/lots_of_files/mbstring.h b/test_data/lots_of_files/mbstring.h new file mode 100644 index 0000000..c0cc9be --- /dev/null +++ b/test_data/lots_of_files/mbstring.h @@ -0,0 +1,363 @@ +/*** +* mbstring.h - MBCS string manipulation macros and functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains macros and function declarations for the MBCS +* string manipulation functions. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_MBSTRING +#define _INC_MBSTRING + +#include <crtdefs.h> + + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _FILE_DEFINED +struct _iobuf { + char *_ptr; + int _cnt; + char *_base; + int _flag; + int _file; + int _charbuf; + int _bufsiz; + char *_tmpfname; + }; +typedef struct _iobuf FILE; +#define _FILE_DEFINED +#endif /* _FILE_DEFINED */ + +#ifdef _CRTBLD +/* + * MBCS - Multi-Byte Character Set + */ +#ifndef _THREADMBCINFO +typedef struct threadmbcinfostruct { + int refcount; + int mbcodepage; + int ismbcodepage; + unsigned short mbulinfo[6]; + unsigned char mbctype[257]; + unsigned char mbcasemap[256]; + const wchar_t* mblocalename; +} threadmbcinfo; +#define _THREADMBCINFO +#endif /* _THREADMBCINFO */ +extern pthreadmbcinfo __ptmbcinfo; +pthreadmbcinfo __cdecl __updatetmbcinfo(void); +#endif /* _CRTBLD */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +#ifndef _MBSTRING_DEFINED + +/* function prototypes */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_mbsdup") +#undef _mbsdup +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsdup(_In_z_ const unsigned char * _Str); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_mbsdup") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP unsigned int __cdecl _mbbtombc(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbbtombc_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbbtype(_In_ unsigned char _Ch, _In_ int _CType); +_Check_return_ _CRTIMP int __cdecl _mbbtype_l(_In_ unsigned char _Ch, _In_ int _CType, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctombb(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctombb_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_CRTIMP int __cdecl _mbsbtype(_In_reads_z_(_Pos) const unsigned char * _Str, _In_ size_t _Pos); +_CRTIMP int __cdecl _mbsbtype_l(_In_reads_z_(_Pos) const unsigned char * _Str, _In_ size_t _Pos, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbscat_s(_Inout_updates_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_z_ const unsigned char * _Src); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _mbscat_s, unsigned char, _Dst, _In_z_ const unsigned char *, _DstSizeInBytes) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbscat, _Inout_updates_z_(_String_length_(_Dest) + _String_length_(_Source) + 1), unsigned char, _Dest, _In_z_ const unsigned char *, _Source) +_CRTIMP errno_t __cdecl _mbscat_s_l(_Inout_updates_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const unsigned char * _Src, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbscat_s_l, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_2_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbscat_l, _mbscat_s_l, _Inout_z_ unsigned char, _Inout_z_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbschr(_In_z_ const unsigned char * _Str, _In_ unsigned int _Ch); +_Check_return_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbschr_l(_In_z_ const unsigned char * _Str, _In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbscmp(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2); +_Check_return_ _CRTIMP int __cdecl _mbscmp_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbscoll(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2); +_Check_return_ _CRTIMP int __cdecl _mbscoll_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbscpy_s(_Out_writes_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_z_ const unsigned char * _Src); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _mbscpy_s, unsigned char, _Dest, _In_z_ const unsigned char *, _Source) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbscpy, _Out_writes_z_(_String_length_(_Source) + 1), unsigned char, _Dest, _In_z_ const unsigned char *, _Source) +_CRTIMP errno_t __cdecl _mbscpy_s_l(_Out_writes_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const unsigned char * _Src, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbscpy_s, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_2_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbscpy_l, _mbscpy_s_l, _Pre_notnull_ _Post_z_ unsigned char, _Pre_notnull_ _Post_z_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP size_t __cdecl _mbscspn(_In_z_ const unsigned char * _Str, _In_z_ const unsigned char * _Control); +_Check_return_ _CRTIMP size_t __cdecl _mbscspn_l(_In_z_ const unsigned char * _Str, _In_z_ const unsigned char * _Control, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsdec(_In_reads_z_(_Pos-_Start +1) const unsigned char * _Start, _In_z_ const unsigned char * _Pos); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsdec_l(_In_reads_z_(_Pos-_Start+1) const unsigned char * _Start, _In_z_ const unsigned char * _Pos, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsicmp(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2); +_Check_return_ _CRTIMP int __cdecl _mbsicmp_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsicoll(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2); +_Check_return_ _CRTIMP int __cdecl _mbsicoll_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsinc(_In_z_ const unsigned char * _Ptr); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsinc_l(_In_z_ const unsigned char * _Ptr, _In_opt_ _locale_t _Locale); + +#endif /* _MBSTRING_DEFINED */ + +_Check_return_ _CRTIMP size_t __cdecl _mbslen(_In_z_ const unsigned char * _Str); +_Check_return_ _CRTIMP size_t __cdecl _mbslen_l(_In_z_ const unsigned char * _Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP size_t __cdecl _mbsnlen(_In_z_ const unsigned char * _Str, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP size_t __cdecl _mbsnlen_l(_In_z_ const unsigned char * _Str, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +_CRTIMP errno_t __cdecl _mbslwr_s(_Inout_updates_opt_z_(_SizeInBytes) unsigned char *_Str, _In_ size_t _SizeInBytes); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _mbslwr_s, _Inout_updates_z_(_Size) unsigned char, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbslwr, _Inout_z_, unsigned char, _String) +_CRTIMP errno_t __cdecl _mbslwr_s_l(_Inout_updates_opt_z_(_SizeInBytes) unsigned char *_Str, _In_ size_t _SizeInBytes, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _mbslwr_s_l, unsigned char, _String, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbslwr_l, _mbslwr_s_l, _Inout_updates_z_(_Size) unsigned char, _Inout_z_, unsigned char, _String, _In_opt_ _locale_t, _Locale) +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbsnbcat_s(_Inout_updates_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbsnbcat_s, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnbcat, _Inout_z_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +_CRTIMP errno_t __cdecl _mbsnbcat_s_l(_Inout_updates_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _mbsnbcat_s_l, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnbcat_l, _mbsnbcat_s_l, _Inout_updates_z_(_Size) unsigned char, _Inout_z_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP int __cdecl _mbsnbcmp(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsnbcmp_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsnbcoll(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsnbcoll_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP size_t __cdecl _mbsnbcnt(_In_reads_or_z_(_MaxCount) const unsigned char * _Str, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP size_t __cdecl _mbsnbcnt_l(_In_reads_or_z_(_MaxCount) const unsigned char * _Str, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbsnbcpy_s(_Out_writes_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbsnbcpy_s, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnbcpy, _Out_writes_(_Count) _Post_maybez_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +_CRTIMP errno_t __cdecl _mbsnbcpy_s_l(_Out_writes_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _mbsnbcpy_s_l, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnbcpy_l, _mbsnbcpy_s_l, _Out_writes_z_(_Size) unsigned char, _Out_writes_(_Count) _Post_maybez_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP int __cdecl _mbsnbicmp(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsnbicmp_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsnbicoll(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsnbicoll_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbsnbset_s(_Inout_updates_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_ unsigned int _Ch, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbsnbset_s, _Prepost_z_ unsigned char, _Dest, _In_ unsigned int, _Val, _In_ size_t, _MaxCount) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnbset, _mbsnbset_s, _Inout_updates_z_(_Size) unsigned char, _Inout_updates_z_(_MaxCount), unsigned char, _String, _In_ unsigned int, _Val, _In_ size_t, _MaxCount) +_CRTIMP errno_t __cdecl _mbsnbset_s_l(_Inout_updates_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_ unsigned int _Ch, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _mbsnbset_s_l, _Prepost_z_ unsigned char, _Dest, _In_ unsigned int, _Val, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnbset_l, _mbsnbset_s_l, _Inout_updates_z_(_Size) unsigned char, _Inout_updates_z_(_MaxCount), unsigned char, _String, _In_ unsigned int, _Val, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbsncat_s(_Inout_updates_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbsncat_s, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsncat, _Inout_z_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +_CRTIMP errno_t __cdecl _mbsncat_s_l(_Inout_updates_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _mbsncat_s_l, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsncat_l, _mbsncat_s_l, _Inout_updates_z_(_Size) unsigned char, _Inout_z_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP size_t __cdecl _mbsnccnt(_In_reads_or_z_(_MaxCount) const unsigned char * _Str, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP size_t __cdecl _mbsnccnt_l(_In_reads_or_z_(_MaxCount) const unsigned char * _Str, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsncmp(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsncmp_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsncoll(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsncoll_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbsncpy_s(_Out_writes_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbsncpy_s, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsncpy, _Pre_notnull_ _Out_writes_(2*_Count) _Post_maybez_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count) +_CRTIMP errno_t __cdecl _mbsncpy_s_l(_Out_writes_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const unsigned char * _Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _mbsncpy_s_l, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsncpy_l, _mbsncpy_s_l, _Out_writes_z_(_Size) unsigned char, _Out_writes_(_Count) _Post_maybez_, unsigned char, _Dest, _In_z_ const unsigned char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP unsigned int __cdecl _mbsnextc (_In_z_ const unsigned char * _Str); +_Check_return_ _CRTIMP unsigned int __cdecl _mbsnextc_l(_In_z_ const unsigned char * _Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsnicmp(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsnicmp_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _mbsnicoll(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mbsnicoll_l(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsninc(_In_reads_or_z_(_Count) const unsigned char * _Str, _In_ size_t _Count); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsninc_l(_In_reads_or_z_(_Count) const unsigned char * _Str, _In_ size_t _Count, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbsnset_s(_Inout_updates_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_ unsigned int _Val, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbsnset_s, _Prepost_z_ unsigned char, _Dest, _In_ unsigned int, _Val, _In_ size_t, _MaxCount) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnset, _mbsnset_s, _Inout_updates_z_(_Size) unsigned char, _Inout_updates_z_(_MaxCount), unsigned char, _String, _In_ unsigned int, _Val, _In_ size_t, _MaxCount) +_CRTIMP errno_t __cdecl _mbsnset_s_l(_Inout_updates_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_ unsigned int _Val, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _mbsnset_s_l, _Prepost_z_ unsigned char, _Dest, _In_ unsigned int, _Val, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsnset_l, _mbsnset_s_l, _Inout_updates_z_(_Size) unsigned char, _Inout_updates_z_(_MaxCount), unsigned char, _String, _In_ unsigned int, _Val, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbspbrk(_In_z_ const unsigned char * _Str, _In_z_ const unsigned char * _Control); +_Check_return_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbspbrk_l(_In_z_ const unsigned char * _Str, _In_z_ const unsigned char * _Control, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbsrchr(_In_z_ const unsigned char * _Str, _In_ unsigned int _Ch); +_Check_return_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbsrchr_l(_In_z_ const unsigned char *_Str, _In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_CRTIMP unsigned char * __cdecl _mbsrev(_Inout_z_ unsigned char * _Str); +_CRTIMP unsigned char * __cdecl _mbsrev_l(_Inout_z_ unsigned char *_Str, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbsset_s(_Inout_updates_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _In_ unsigned int _Val); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _mbsset_s, _Prepost_z_ unsigned char, _Dest, _In_ unsigned int, _Val) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsset, _mbsset_s, _Inout_updates_z_(_Size) unsigned char, _Inout_z_, unsigned char, _String, _In_ unsigned int, _Val) +_CRTIMP errno_t __cdecl _mbsset_s_l(_Inout_updates_z_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _In_ unsigned int _Val, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbsset_s_l, _Prepost_z_ unsigned char, _Dest, _In_ unsigned int, _Val, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_2_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsset_l, _mbsset_s_l, _Inout_updates_z_(_Size) unsigned char, _Inout_z_, unsigned char, _String, _In_ unsigned int, _Val, _In_opt_ _locale_t, _Locale) +_Check_return_ _CRTIMP size_t __cdecl _mbsspn(_In_z_ const unsigned char *_Str, _In_z_ const unsigned char * _Control); +_Check_return_ _CRTIMP size_t __cdecl _mbsspn_l(_In_z_ const unsigned char * _Str, _In_z_ const unsigned char * _Control, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsspnp(_In_z_ const unsigned char * _Str1, _In_z_ const unsigned char *_Str2); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbsspnp_l(_In_z_ const unsigned char *_Str1, _In_z_ const unsigned char *_Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _Ret_maybenull_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbsstr(_In_z_ const unsigned char * _Str, _In_z_ const unsigned char * _Substr); +_Check_return_ _Ret_maybenull_ _CRTIMP _CONST_RETURN unsigned char * __cdecl _mbsstr_l(_In_z_ const unsigned char * _Str, _In_z_ const unsigned char * _Substr, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP _CRT_INSECURE_DEPRECATE(_mbstok_s) unsigned char * __cdecl _mbstok(_Inout_opt_z_ unsigned char * _Str, _In_z_ const unsigned char * _Delim); +_Check_return_ _CRTIMP _CRT_INSECURE_DEPRECATE(_mbstok_s_l) unsigned char * __cdecl _mbstok_l(_Inout_opt_z_ unsigned char *_Str, _In_z_ const unsigned char * _Delim, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP_ALTERNATIVE unsigned char * __cdecl _mbstok_s(_Inout_opt_z_ unsigned char *_Str, _In_z_ const unsigned char * _Delim, _Inout_ _Deref_prepost_opt_z_ unsigned char ** _Context); +_Check_return_ _CRTIMP unsigned char * __cdecl _mbstok_s_l(_Inout_opt_z_ unsigned char * _Str, _In_z_ const unsigned char * _Delim, _Inout_ _Deref_prepost_opt_z_ unsigned char ** _Context, _In_opt_ _locale_t _Locale); +_CRTIMP errno_t __cdecl _mbsupr_s(_Inout_updates_z_(_SizeInBytes) unsigned char *_Str, _In_ size_t _SizeInBytes); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _mbsupr_s, _Inout_updates_z_(_Size) unsigned char, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsupr, _Inout_z_, unsigned char, _String) +_CRTIMP errno_t __cdecl _mbsupr_s_l(_Inout_updates_z_(_SizeInBytes) unsigned char *_Str, _In_ size_t _SizeInBytes, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _mbsupr_s_l, unsigned char, _String, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(unsigned char *, __RETURN_POLICY_DST, _CRTIMP, _mbsupr_l, _mbsupr_s_l, _Inout_updates_z_(_Size) unsigned char, _Inout_z_, unsigned char, _String, _In_opt_ _locale_t, _Locale) + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP size_t __cdecl _mbclen(_In_z_ const unsigned char *_Str); +_Check_return_ _CRTIMP size_t __cdecl _mbclen_l(_In_z_ const unsigned char * _Str, _In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +_CRTIMP _CRT_INSECURE_DEPRECATE(_mbccpy_s) void __cdecl _mbccpy(_Out_writes_bytes_(2) unsigned char * _Dst, _In_z_ const unsigned char * _Src); +_CRTIMP _CRT_INSECURE_DEPRECATE(_mbccpy_s_l) void __cdecl _mbccpy_l(_Out_writes_bytes_(2) unsigned char *_Dst, _In_z_ const unsigned char *_Src, _In_opt_ _locale_t _Locale); +_CRTIMP_ALTERNATIVE errno_t __cdecl _mbccpy_s(_Out_writes_z_(_SizeInBytes) unsigned char * _Dst, _In_ size_t _SizeInBytes, _Out_opt_ int * _PCopied, _In_z_ const unsigned char * _Src); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _mbccpy_s, unsigned char, _Dest, _Out_opt_ int *, _PCopied, _In_z_ const unsigned char *, _Source) +_CRTIMP errno_t __cdecl _mbccpy_s_l(_Out_writes_bytes_(_DstSizeInBytes) unsigned char * _Dst, _In_ size_t _DstSizeInBytes, _Out_opt_ int * _PCopied, _In_z_ const unsigned char * _Src, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _mbccpy_s_l, unsigned char, _Dest, _Out_opt_ int *,_PCopied, _In_z_ const unsigned char *,_Source, _In_opt_ _locale_t, _Locale) +#define _mbccmp(_cpc1, _cpc2) _mbsncmp((_cpc1),(_cpc2),1) + +#ifdef __cplusplus +#ifndef _CPP_MBCS_INLINES_DEFINED +#define _CPP_MBCS_INLINES_DEFINED +extern "C++" { +_Check_return_ inline unsigned char * __CRTDECL _mbschr(_In_z_ unsigned char *_String, _In_ unsigned int _Char) +{ + return ((unsigned char *)_mbschr((const unsigned char *)_String, _Char)); +} + +_Check_return_ inline unsigned char * __CRTDECL _mbschr_l(_In_z_ unsigned char *_String, _In_ unsigned int _Char, _In_opt_ _locale_t _Locale) +{ + return ((unsigned char *)_mbschr_l((const unsigned char *)_String, _Char, _Locale)); +} + +_Check_return_ inline unsigned char * __CRTDECL _mbspbrk(_In_z_ unsigned char *_String, _In_z_ const unsigned char *_CharSet) +{ + return ((unsigned char *)_mbspbrk((const unsigned char *)_String, _CharSet)); +} + +_Check_return_ inline unsigned char * __CRTDECL _mbspbrk_l(_In_z_ unsigned char *_String, _In_z_ const unsigned char *_CharSet, _In_opt_ _locale_t _Locale) +{ + return ((unsigned char *)_mbspbrk_l((const unsigned char *)_String, _CharSet, _Locale)); +} + +_Check_return_ inline unsigned char * __CRTDECL _mbsrchr(_In_z_ unsigned char *_String, _In_ unsigned int _Char) +{ + return ((unsigned char *)_mbsrchr((const unsigned char *)_String, _Char)); +} + +_Check_return_ inline unsigned char * __CRTDECL _mbsrchr_l(_In_z_ unsigned char *_String, _In_ unsigned int _Char, _In_opt_ _locale_t _Locale) +{ + return ((unsigned char *)_mbsrchr_l((const unsigned char *)_String, _Char, _Locale)); +} + +_Check_return_ _Ret_maybenull_ inline unsigned char * __CRTDECL _mbsstr(_In_z_ unsigned char *_String, _In_z_ const unsigned char *_Match) +{ + return ((unsigned char *)_mbsstr((const unsigned char *)_String, _Match)); +} + +_Check_return_ _Ret_maybenull_ inline unsigned char * __CRTDECL _mbsstr_l(_In_z_ unsigned char *_String, _In_z_ const unsigned char *_Match, _In_opt_ _locale_t _Locale) +{ + return ((unsigned char *)_mbsstr_l((const unsigned char *)_String, _Match, _Locale)); +} +} +#endif /* _CPP_MBCS_INLINES_DEFINED */ +#endif /* __cplusplus */ + +/* character routines */ + +_Check_return_ _CRTIMP int __cdecl _ismbcalnum(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcalnum_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcalpha(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcalpha_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcdigit(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcdigit_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcgraph(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcgraph_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbclegal(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbclegal_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbclower(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbclower_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcprint(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcprint_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcpunct(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcpunct_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcblank(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcblank_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcspace(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcspace_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcupper(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcupper_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); + +_Check_return_ _CRTIMP unsigned int __cdecl _mbctolower(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctolower_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctoupper(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctoupper_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); + +#define _MBSTRING_DEFINED +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifndef _MBLEADTRAIL_DEFINED +_Check_return_ _CRTIMP int __cdecl _ismbblead(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbblead_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbbtrail(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbbtrail_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbslead(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos); +_Check_return_ _CRTIMP int __cdecl _ismbslead_l(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbstrail(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos); +_Check_return_ _CRTIMP int __cdecl _ismbstrail_l(_In_reads_z_(_Pos - _Str+1) const unsigned char * _Str, _In_z_ const unsigned char * _Pos, _In_opt_ _locale_t _Locale); +#define _MBLEADTRAIL_DEFINED +#endif /* _MBLEADTRAIL_DEFINED */ + +/* Kanji specific prototypes. */ + +_Check_return_ _CRTIMP int __cdecl _ismbchira(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbchira_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbckata(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbckata_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcsymbol(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcsymbol_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcl0(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcl0_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcl1(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcl1_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _ismbcl2(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP int __cdecl _ismbcl2_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned int __cdecl _mbcjistojms(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbcjistojms_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned int __cdecl _mbcjmstojis(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbcjmstojis_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctohira(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctohira_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctokata(_In_ unsigned int _Ch); +_Check_return_ _CRTIMP unsigned int __cdecl _mbctokata_l(_In_ unsigned int _Ch, _In_opt_ _locale_t _Locale); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_MBSTRING */ diff --git a/test_data/lots_of_files/memory.h b/test_data/lots_of_files/memory.h new file mode 100644 index 0000000..84249a8 --- /dev/null +++ b/test_data/lots_of_files/memory.h @@ -0,0 +1,110 @@ +/*** +*memory.h - declarations for buffer (memory) manipulation routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This include file contains the function declarations for the +* buffer (memory) manipulation routines. +* [System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_MEMORY +#define _INC_MEMORY + +#include <crtdefs.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _CONST_RETURN +#ifdef __cplusplus +#define _CONST_RETURN const +#define _CRT_CONST_CORRECT_OVERLOADS +#else /* __cplusplus */ +#define _CONST_RETURN +#endif /* __cplusplus */ +#endif /* _CONST_RETURN */ + +/* For backwards compatibility */ +#define _WConst_return _CONST_RETURN + +/* Function prototypes */ +#ifndef _CRT_MEMORY_DEFINED +#define _CRT_MEMORY_DEFINED +_CRTIMP void * __cdecl _memccpy( _Out_writes_bytes_opt_(_MaxCount) void * _Dst, _In_ const void * _Src, _In_ int _Val, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP _CONST_RETURN void * __cdecl memchr( _In_reads_bytes_opt_(_MaxCount) const void * _Buf , _In_ int _Val, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _memicmp(_In_reads_bytes_opt_(_Size) const void * _Buf1, _In_reads_bytes_opt_(_Size) const void * _Buf2, _In_ size_t _Size); +_Check_return_ _CRTIMP int __cdecl _memicmp_l(_In_reads_bytes_opt_(_Size) const void * _Buf1, _In_reads_bytes_opt_(_Size) const void * _Buf2, _In_ size_t _Size, _In_opt_ _locale_t _Locale); +_Check_return_ int __cdecl memcmp(_In_reads_bytes_(_Size) const void * _Buf1, _In_reads_bytes_(_Size) const void * _Buf2, _In_ size_t _Size); +_CRT_INSECURE_DEPRECATE_MEMORY(memcpy_s) +_Post_equal_to_(_Dst) +_At_buffer_((unsigned char*)_Dst, _Iter_, _Size, _Post_satisfies_(((unsigned char*)_Dst)[_Iter_] == ((unsigned char*)_Src)[_Iter_])) +void * __cdecl memcpy(_Out_writes_bytes_all_(_Size) void * _Dst, _In_reads_bytes_(_Size) const void * _Src, _In_ size_t _Size); +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP errno_t __cdecl memcpy_s(_Out_writes_bytes_to_opt_(_DstSize, _MaxCount) void * _Dst, _In_ rsize_t _DstSize, _In_reads_bytes_opt_(_MaxCount) const void * _Src, _In_ rsize_t _MaxCount); +#if defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY +extern "C++" +{ +#ifndef _CRT_ENABLE_IF_DEFINED + #define _CRT_ENABLE_IF_DEFINED + template<bool _Enable, typename _Ty> + struct _CrtEnableIf; + + template<typename _Ty> + struct _CrtEnableIf<true, _Ty> + { + typedef _Ty _Type; + }; +#endif /* _CRT_ENABLE_IF_DEFINED */ + template <size_t _Size, typename _DstType> + inline + typename _CrtEnableIf<(_Size > 1), void *>::_Type __cdecl memcpy(_DstType (&_Dst)[_Size], _In_reads_bytes_opt_(_SrcSize) const void *_Src, _In_ size_t _SrcSize) _CRT_SECURE_CPP_NOTHROW + { + return memcpy_s(_Dst, _Size * sizeof(_DstType), _Src, _SrcSize) == 0 ? _Dst : 0; + } +} +#endif /* defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY */ +#if defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY +extern "C++" +{ + template <size_t _Size, typename _DstType> + inline + errno_t __CRTDECL memcpy_s(_DstType (&_Dst)[_Size], _In_reads_bytes_opt_(_SrcSize) const void * _Src, _In_ rsize_t _SrcSize) _CRT_SECURE_CPP_NOTHROW + { + return memcpy_s(_Dst, _Size * sizeof(_DstType), _Src, _SrcSize); + } +} +#endif /* defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY */ +#endif /* __STDC_WANT_SECURE_LIB__ */ + _Post_equal_to_(_Dst) + _At_buffer_((unsigned char*)_Dst, _Iter_, _Size, _Post_satisfies_(((unsigned char*)_Dst)[_Iter_] == _Val)) + void * __cdecl memset(_Out_writes_bytes_all_(_Size) void * _Dst, _In_ int _Val, _In_ size_t _Size); + +#if !__STDC__ +/* Non-ANSI names for compatibility */ +_CRT_NONSTDC_DEPRECATE(_memccpy) _CRTIMP void * __cdecl memccpy(_Out_writes_bytes_opt_(_Size) void * _Dst, _In_reads_bytes_opt_(_Size) const void * _Src, _In_ int _Val, _In_ size_t _Size); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_memicmp) _CRTIMP int __cdecl memicmp(_In_reads_bytes_opt_(_Size) const void * _Buf1, _In_reads_bytes_opt_(_Size) const void * _Buf2, _In_ size_t _Size); +#endif /* !__STDC__ */ + +#endif /* _CRT_MEMORY_DEFINED */ + +#ifdef __cplusplus +#ifndef _CPP_MEMCHR_DEFINED +#define _CPP_MEMCHR_DEFINED +extern "C++" _Check_return_ inline void * __CRTDECL memchr( _In_reads_bytes_opt_(_N) void * _Pv , _In_ int _C, _In_ size_t _N) + { return (void*)memchr((const void*)_Pv, _C, _N); } +#endif /* _CPP_MEMCHR_DEFINED */ +#endif /* __cplusplus */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_MEMORY */ diff --git a/test_data/lots_of_files/minmax.h b/test_data/lots_of_files/minmax.h new file mode 100644 index 0000000..d2cb526 --- /dev/null +++ b/test_data/lots_of_files/minmax.h @@ -0,0 +1,26 @@ +/*** +*minmax.h - familiar min & max macros +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines min and max macros. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_MINMAX +#define _INC_MINMAX + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif /* max */ + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif /* min */ + +#endif /* _INC_MINMAX */ diff --git a/test_data/lots_of_files/mm3dnow.h b/test_data/lots_of_files/mm3dnow.h new file mode 100644 index 0000000..5c4ab3a --- /dev/null +++ b/test_data/lots_of_files/mm3dnow.h @@ -0,0 +1,70 @@ +/*** +*** Copyright (C) 1999 Advanced Micro Devices Inc. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Advanced Micro Devices and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * mm3dnow.h + * + */ + +#pragma once +#ifndef __midl +#ifndef _MM3DNOW_H_INCLUDED +#define _MM3DNOW_H_INCLUDED + +#include <crtdefs.h> +#include <mmintrin.h> +#include <xmmintrin.h> + +#if defined __cplusplus +extern "C" { /* Intrinsics use C name-mangling. */ +#endif /* defined __cplusplus */ + +/* 3DNOW intrinsics */ + +void _m_femms(void); +__m64 _m_pavgusb(__m64, __m64); +__m64 _m_pf2id(__m64); +__m64 _m_pfacc(__m64, __m64); +__m64 _m_pfadd(__m64, __m64); +__m64 _m_pfcmpeq(__m64, __m64); +__m64 _m_pfcmpge(__m64, __m64); +__m64 _m_pfcmpgt(__m64, __m64); +__m64 _m_pfmax(__m64, __m64); +__m64 _m_pfmin(__m64, __m64); +__m64 _m_pfmul(__m64, __m64); +__m64 _m_pfrcp(__m64); +__m64 _m_pfrcpit1(__m64, __m64); +__m64 _m_pfrcpit2(__m64, __m64); +__m64 _m_pfrsqrt(__m64); +__m64 _m_pfrsqit1(__m64, __m64); +__m64 _m_pfsub(__m64, __m64); +__m64 _m_pfsubr(__m64, __m64); +__m64 _m_pi2fd(__m64); +__m64 _m_pmulhrw(__m64, __m64); +void _m_prefetch(void*); +void _m_prefetchw(volatile const void*_Source); + +__m64 _m_from_float(float); +float _m_to_float(__m64); + +/* Athlon DSP intrinsics */ + +__m64 _m_pf2iw(__m64); +__m64 _m_pfnacc(__m64, __m64); +__m64 _m_pfpnacc(__m64, __m64); +__m64 _m_pi2fw(__m64); +__m64 _m_pswapd(__m64); + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* _MM3DNOW_H_INCLUDED */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/mmintrin.h b/test_data/lots_of_files/mmintrin.h new file mode 100644 index 0000000..7fa762d --- /dev/null +++ b/test_data/lots_of_files/mmintrin.h @@ -0,0 +1,188 @@ +/*** +*** Copyright (C) 1985-1999 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * Definitions and declarations for use with compiler intrinsics. + */ + +#ifndef _MMINTRIN_H_INCLUDED +#define _MMINTRIN_H_INCLUDED +#ifndef __midl + +#include <crtdefs.h> + +#if defined (_M_CEE_PURE) + #error ERROR: MM intrinsics not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#if defined __cplusplus +extern "C" { /* Begin "C" */ +/* Intrinsics use C name-mangling. + */ +#endif /* defined __cplusplus */ + +typedef union __declspec(intrin_type) _CRT_ALIGN(8) __m64 +{ + unsigned __int64 m64_u64; + float m64_f32[2]; + __int8 m64_i8[8]; + __int16 m64_i16[4]; + __int32 m64_i32[2]; + __int64 m64_i64; + unsigned __int8 m64_u8[8]; + unsigned __int16 m64_u16[4]; + unsigned __int32 m64_u32[2]; +} __m64; + +/* General support intrinsics */ +void _m_empty(void); +__m64 _m_from_int(int _I); +int _m_to_int(__m64 _M); +__m64 _m_packsswb(__m64 _MM1, __m64 _MM2); +__m64 _m_packssdw(__m64 _MM1, __m64 _MM2); +__m64 _m_packuswb(__m64 _MM1, __m64 _MM2); +__m64 _m_punpckhbw(__m64 _MM1, __m64 _MM2); +__m64 _m_punpckhwd(__m64 _MM1, __m64 _MM2); +__m64 _m_punpckhdq(__m64 _MM1, __m64 _MM2); +__m64 _m_punpcklbw(__m64 _MM1, __m64 _MM2); +__m64 _m_punpcklwd(__m64 _MM1, __m64 _MM2); +__m64 _m_punpckldq(__m64 _MM1, __m64 _MM2); + +/* Packed arithmetic intrinsics */ +__m64 _m_paddb(__m64 _MM1, __m64 _MM2); +__m64 _m_paddw(__m64 _MM1, __m64 _MM2); +__m64 _m_paddd(__m64 _MM1, __m64 _MM2); +__m64 _m_paddsb(__m64 _MM1, __m64 _MM2); +__m64 _m_paddsw(__m64 _MM1, __m64 _MM2); +__m64 _m_paddusb(__m64 _MM1, __m64 _MM2); +__m64 _m_paddusw(__m64 _MM1, __m64 _MM2); +__m64 _m_psubb(__m64 _MM1, __m64 _MM2); +__m64 _m_psubw(__m64 _MM1, __m64 _MM2); +__m64 _m_psubd(__m64 _MM1, __m64 _MM2); +__m64 _m_psubsb(__m64 _MM1, __m64 _MM2); +__m64 _m_psubsw(__m64 _MM1, __m64 _MM2); +__m64 _m_psubusb(__m64 _MM1, __m64 _MM2); +__m64 _m_psubusw(__m64 _MM1, __m64 _MM2); +__m64 _m_pmaddwd(__m64 _MM1, __m64 _MM2); +__m64 _m_pmulhw(__m64 _MM1, __m64 _MM2); +__m64 _m_pmullw(__m64 _MM1, __m64 _MM2); + +/* Shift intrinsics */ +__m64 _m_psllw(__m64 _M, __m64 _Count); +__m64 _m_psllwi(__m64 _M, int _Count); +__m64 _m_pslld(__m64 _M, __m64 _Count); +__m64 _m_pslldi(__m64 _M, int _Count); +__m64 _m_psllq(__m64 _M, __m64 _Count); +__m64 _m_psllqi(__m64 _M, int _Count); +__m64 _m_psraw(__m64 _M, __m64 _Count); +__m64 _m_psrawi(__m64 _M, int _Count); +__m64 _m_psrad(__m64 _M, __m64 _Count); +__m64 _m_psradi(__m64 _M, int _Count); +__m64 _m_psrlw(__m64 _M, __m64 _Count); +__m64 _m_psrlwi(__m64 _M, int _Count); +__m64 _m_psrld(__m64 _M, __m64 _Count); +__m64 _m_psrldi(__m64 _M, int _Count); +__m64 _m_psrlq(__m64 _M, __m64 _Count); +__m64 _m_psrlqi(__m64 _M, int _Count); + +/* Logical intrinsics */ +__m64 _m_pand(__m64 _MM1, __m64 _MM2); +__m64 _m_pandn(__m64 _MM1, __m64 _MM2); +__m64 _m_por(__m64 _MM1, __m64 _MM2); +__m64 _m_pxor(__m64 _MM1, __m64 _MM2); + +/* Comparison intrinsics */ +__m64 _m_pcmpeqb(__m64 _MM1, __m64 _MM2); +__m64 _m_pcmpeqw(__m64 _MM1, __m64 _MM2); +__m64 _m_pcmpeqd(__m64 _MM1, __m64 _MM2); +__m64 _m_pcmpgtb(__m64 _MM1, __m64 _MM2); +__m64 _m_pcmpgtw(__m64 _MM1, __m64 _MM2); +__m64 _m_pcmpgtd(__m64 _MM1, __m64 _MM2); + +/* Utility intrinsics */ +__m64 _mm_setzero_si64(void); +__m64 _mm_set_pi32(int _I1, int _I0); +__m64 _mm_set_pi16(short _S3, short _S2, short _S1, short _S0); +__m64 _mm_set_pi8(char _B7, char _B6, char _B5, char _B4, + char _B3, char _B2, char _B1, char _B0); +__m64 _mm_set1_pi32(int _I); +__m64 _mm_set1_pi16(short _S); +__m64 _mm_set1_pi8(char _B); +__m64 _mm_setr_pi32(int _I1, int _I0); +__m64 _mm_setr_pi16(short _S3, short _S2, short _S1, short _S0); +__m64 _mm_setr_pi8(char _B7, char _B6, char _B5, char _B4, + char _B3, char _B2, char _B1, char _B0); + +/* Alternate intrinsic name definitions */ +#define _mm_empty _m_empty +#define _mm_cvtsi32_si64 _m_from_int +#define _mm_cvtsi64_si32 _m_to_int +#define _mm_packs_pi16 _m_packsswb +#define _mm_packs_pi32 _m_packssdw +#define _mm_packs_pu16 _m_packuswb +#define _mm_unpackhi_pi8 _m_punpckhbw +#define _mm_unpackhi_pi16 _m_punpckhwd +#define _mm_unpackhi_pi32 _m_punpckhdq +#define _mm_unpacklo_pi8 _m_punpcklbw +#define _mm_unpacklo_pi16 _m_punpcklwd +#define _mm_unpacklo_pi32 _m_punpckldq +#define _mm_add_pi8 _m_paddb +#define _mm_add_pi16 _m_paddw +#define _mm_add_pi32 _m_paddd +#define _mm_adds_pi8 _m_paddsb +#define _mm_adds_pi16 _m_paddsw +#define _mm_adds_pu8 _m_paddusb +#define _mm_adds_pu16 _m_paddusw +#define _mm_sub_pi8 _m_psubb +#define _mm_sub_pi16 _m_psubw +#define _mm_sub_pi32 _m_psubd +#define _mm_subs_pi8 _m_psubsb +#define _mm_subs_pi16 _m_psubsw +#define _mm_subs_pu8 _m_psubusb +#define _mm_subs_pu16 _m_psubusw +#define _mm_madd_pi16 _m_pmaddwd +#define _mm_mulhi_pi16 _m_pmulhw +#define _mm_mullo_pi16 _m_pmullw +#define _mm_sll_pi16 _m_psllw +#define _mm_slli_pi16 _m_psllwi +#define _mm_sll_pi32 _m_pslld +#define _mm_slli_pi32 _m_pslldi +#define _mm_sll_si64 _m_psllq +#define _mm_slli_si64 _m_psllqi +#define _mm_sra_pi16 _m_psraw +#define _mm_srai_pi16 _m_psrawi +#define _mm_sra_pi32 _m_psrad +#define _mm_srai_pi32 _m_psradi +#define _mm_srl_pi16 _m_psrlw +#define _mm_srli_pi16 _m_psrlwi +#define _mm_srl_pi32 _m_psrld +#define _mm_srli_pi32 _m_psrldi +#define _mm_srl_si64 _m_psrlq +#define _mm_srli_si64 _m_psrlqi +#define _mm_and_si64 _m_pand +#define _mm_andnot_si64 _m_pandn +#define _mm_or_si64 _m_por +#define _mm_xor_si64 _m_pxor +#define _mm_cmpeq_pi8 _m_pcmpeqb +#define _mm_cmpeq_pi16 _m_pcmpeqw +#define _mm_cmpeq_pi32 _m_pcmpeqd +#define _mm_cmpgt_pi8 _m_pcmpgtb +#define _mm_cmpgt_pi16 _m_pcmpgtw +#define _mm_cmpgt_pi32 _m_pcmpgtd + + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ +#endif /* __midl */ +#endif /* _MMINTRIN_H_INCLUDED */ + diff --git a/test_data/lots_of_files/multiples_of_3_and_5.cpp b/test_data/lots_of_files/multiples_of_3_and_5.cpp new file mode 100644 index 0000000..e794563 --- /dev/null +++ b/test_data/lots_of_files/multiples_of_3_and_5.cpp @@ -0,0 +1,60 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +int sum_mults_3_and_5(int top){ + int n = (top-1)/3; + int m = ((n*n)+n)/2; + int c = m*3; + + n = (top+4)/15; + m = 15*n; + c += m*n; + m += 5; + if (m < top){ + c += m; + } + + return c; +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{}; + +struct Euler_Result{ + int counter; +}; + +static const int TOP = 1000; + +inline Euler_Result euler_main(Euler_In in){ + Euler_Result result; + result.counter = sum_mults_3_and_5(TOP); + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.counter); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result; + + int counter = 0; + for (int i = 0; i < TOP; ++i){ + if ((i % 3) == 0 || (i % 5) == 0){ + counter += i; + } + } + + printf("answer = %d\n", counter); + result = (answer.counter == counter); + + return result; +} +#endif + diff --git a/test_data/lots_of_files/new.h b/test_data/lots_of_files/new.h new file mode 100644 index 0000000..176d9a2 --- /dev/null +++ b/test_data/lots_of_files/new.h @@ -0,0 +1,132 @@ + +/*** +*new.h - declarations and definitions for C++ memory allocation functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Contains the declarations for C++ memory allocation functions. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_NEW +#define _INC_NEW + +#ifdef __cplusplus + +#ifndef _MSC_EXTENSIONS +#include <new> +#endif /* _MSC_EXTENSIONS */ + +#include <crtdefs.h> + +/* Protect against #define of new */ +#pragma push_macro("new") +#undef new + +#ifdef _CRTBLD +#ifndef _USE_OLD_STDCPP +/* Define _CRTIMP2 */ +#ifndef _CRTIMP2 +#if defined (CRTDLL2) +#define _CRTIMP2 __declspec(dllexport) +#else /* defined (CRTDLL2) */ +#if defined (_DLL) && !defined (_STATIC_CPPLIB) +#define _CRTIMP2 __declspec(dllimport) +#else /* defined (_DLL) && !defined (_STATIC_CPPLIB) */ +#define _CRTIMP2 +#endif /* defined (_DLL) && !defined (_STATIC_CPPLIB) */ +#endif /* defined (CRTDLL2) */ +#endif /* _CRTIMP2 */ +#endif /* _USE_OLD_STDCPP */ +#else /* _CRTBLD */ +/* Define _CRTIMP2 */ +#ifndef _CRTIMP2 +#if defined (_DLL) && !defined (_STATIC_CPPLIB) +#define _CRTIMP2 __declspec(dllimport) +#else /* defined (_DLL) && !defined (_STATIC_CPPLIB) */ +#define _CRTIMP2 +#endif /* defined (_DLL) && !defined (_STATIC_CPPLIB) */ +#endif /* _CRTIMP2 */ +#endif /* _CRTBLD */ + +#ifdef _MSC_EXTENSIONS +#if defined (_USE_OLD_STDCPP) && defined (_CRTBLD) +typedef void (__cdecl * new_handler) (); +_CRTIMP new_handler __cdecl set_new_handler(_In_opt_ new_handler _NewHandler); +#else /* defined (_USE_OLD_STDCPP) && defined (_CRTBLD) */ + +namespace std { + +#ifdef _M_CEE_PURE +typedef void (__clrcall * new_handler) (); +#else /* _M_CEE_PURE */ +typedef void (__cdecl * new_handler) (); +#endif /* _M_CEE_PURE */ +#ifdef _M_CEE +typedef void (__clrcall * _new_handler_m) (); +#endif /* _M_CEE */ +_CRTIMP2 new_handler __cdecl set_new_handler(_In_opt_ new_handler _NewHandler) throw(); +}; + +#ifdef _M_CEE +using std::_new_handler_m; +#endif /* _M_CEE */ +using std::new_handler; +using std::set_new_handler; +#endif /* defined (_USE_OLD_STDCPP) && defined (_CRTBLD) */ +#endif /* _MSC_EXTENSIONS */ + +#ifndef __NOTHROW_T_DEFINED +#define __NOTHROW_T_DEFINED +namespace std { + /* placement new tag type to suppress exceptions */ + struct nothrow_t {}; + + /* constant for placement new tag */ + extern const nothrow_t nothrow; +}; + +_Ret_maybenull_ _Post_writable_byte_size_(_Size) void *__CRTDECL operator new(size_t _Size, const std::nothrow_t&) throw(); +_Ret_maybenull_ _Post_writable_byte_size_(_Size) void *__CRTDECL operator new[](size_t _Size, const std::nothrow_t&) throw(); +void __CRTDECL operator delete(void *, const std::nothrow_t&) throw(); +void __CRTDECL operator delete[](void *, const std::nothrow_t&) throw(); +#endif /* __NOTHROW_T_DEFINED */ + +#ifndef __PLACEMENT_NEW_INLINE +#define __PLACEMENT_NEW_INLINE +inline void *__CRTDECL operator new(size_t, void *_Where) + {return (_Where); } +inline void __CRTDECL operator delete(void *, void *) + {return; } +#endif /* __PLACEMENT_NEW_INLINE */ + + +/* + * new mode flag -- when set, makes malloc() behave like new() + */ + +_CRTIMP int __cdecl _query_new_mode( void ); +_CRTIMP int __cdecl _set_new_mode( _In_ int _NewMode); + +#ifndef _PNH_DEFINED +#ifdef _M_CEE_PURE +typedef int (__clrcall * _PNH)( size_t ); +#else /* _M_CEE_PURE */ +typedef int (__cdecl * _PNH)( size_t ); +#endif /* _M_CEE_PURE */ +#define _PNH_DEFINED +#endif /* _PNH_DEFINED */ + +_CRTIMP _PNH __cdecl _query_new_handler( void ); +_CRTIMP _PNH __cdecl _set_new_handler( _In_opt_ _PNH _NewHandler); + +#pragma pop_macro("new") + +#endif /* __cplusplus */ + +#endif /* _INC_NEW */ diff --git a/test_data/lots_of_files/nmmintrin.h b/test_data/lots_of_files/nmmintrin.h new file mode 100644 index 0000000..5f1d15f --- /dev/null +++ b/test_data/lots_of_files/nmmintrin.h @@ -0,0 +1,136 @@ +/*** +*** Copyright (C) 1985-2007 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * nmmintrin.h + * + * Principal header file for Intel(R) Core(TM) 2 Duo processor + * SSE4.2 intrinsics. + */ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_NMM +#define _INCLUDED_NMM + +#if defined (_M_CEE_PURE) + #error ERROR: EMM intrinsics not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <smmintrin.h> + + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * These defines are used to determine the kind of units to be compared + */ +#define _SIDD_UBYTE_OPS 0x00 +#define _SIDD_UWORD_OPS 0x01 +#define _SIDD_SBYTE_OPS 0x02 +#define _SIDD_SWORD_OPS 0x03 + + +/* + * These defines are used to determine the comparison operation + */ +#define _SIDD_CMP_EQUAL_ANY 0x00 +#define _SIDD_CMP_RANGES 0x04 +#define _SIDD_CMP_EQUAL_EACH 0x08 +#define _SIDD_CMP_EQUAL_ORDERED 0x0C + + +/* + * These defines are used to determine the polarity + */ +#define _SIDD_POSITIVE_POLARITY 0x00 +#define _SIDD_NEGATIVE_POLARITY 0x10 +#define _SIDD_MASKED_POSITIVE_POLARITY 0x20 +#define _SIDD_MASKED_NEGATIVE_POLARITY 0x30 + + +/* + * These defines are used in _mm_cmpXstri() + */ +#define _SIDD_LEAST_SIGNIFICANT 0x00 +#define _SIDD_MOST_SIGNIFICANT 0x40 + +/* + * These defines are used _mm_cmpXstrm() + */ +#define _SIDD_BIT_MASK 0x00 +#define _SIDD_UNIT_MASK 0x40 + + +/* + * Intrinsics for text/string processing. + */ + + extern __m128i _mm_cmpistrm (__m128i a, __m128i b, const int mode); + extern int _mm_cmpistri (__m128i a, __m128i b, const int mode); + + extern __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int mode); + extern int _mm_cmpestri (__m128i a, int la, __m128i b, int lb, const int mode); + +/* + * Intrinsics for text/string processing and reading values of EFlags. + */ + + extern int _mm_cmpistrz (__m128i a, __m128i b, const int mode); + extern int _mm_cmpistrc (__m128i a, __m128i b, const int mode); + extern int _mm_cmpistrs (__m128i a, __m128i b, const int mode); + extern int _mm_cmpistro (__m128i a, __m128i b, const int mode); + extern int _mm_cmpistra (__m128i a, __m128i b, const int mode); + + extern int _mm_cmpestrz (__m128i a, int la, __m128i b, int lb, const int mode); + extern int _mm_cmpestrc (__m128i a, int la, __m128i b, int lb, const int mode); + extern int _mm_cmpestrs (__m128i a, int la, __m128i b, int lb, const int mode); + extern int _mm_cmpestro (__m128i a, int la, __m128i b, int lb, const int mode); + extern int _mm_cmpestra (__m128i a, int la, __m128i b, int lb, const int mode); + +/* + * Packed integer 64-bit comparison, zeroing or filling with ones + * corresponding parts of result + */ + + extern __m128i _mm_cmpgt_epi64(__m128i val1, __m128i val2); + +/* + * Calculate a number of bits set to 1 + */ + + extern int _mm_popcnt_u32(unsigned int v); + +#if defined (_M_X64) + extern __int64 _mm_popcnt_u64(unsigned __int64 v); +#endif /* defined (_M_X64) */ + +/* + * Accumulate CRC32 (polynomial 0x11EDC6F41) value + */ + + extern unsigned int _mm_crc32_u8 (unsigned int crc, unsigned char v); + extern unsigned int _mm_crc32_u16(unsigned int crc, unsigned short v); + extern unsigned int _mm_crc32_u32(unsigned int crc, unsigned int v); + +#if defined (_M_X64) + extern unsigned __int64 _mm_crc32_u64(unsigned __int64 crc, unsigned __int64 v); +#endif /* defined (_M_X64) */ + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INCLUDED_NMM */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/omp.h b/test_data/lots_of_files/omp.h new file mode 100644 index 0000000..24faa9d --- /dev/null +++ b/test_data/lots_of_files/omp.h @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// OpenMP runtime support library for Visual C++ +// Copyright (C) Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------------- + +// OpenMP C/C++ Version 2.0 March 2002 + +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif + +#define _OMPAPI __cdecl + +#if !defined(_OMP_LOCK_T) +#define _OMP_LOCK_T +typedef void * omp_lock_t; +#endif + +#if !defined(_OMP_NEST_LOCK_T) +#define _OMP_NEST_LOCK_T +typedef void * omp_nest_lock_t; +#endif + +#if !defined(_OPENMP) + +#if defined(_DEBUG) + #pragma comment(lib, "vcompd") +#else // _DEBUG + #pragma comment(lib, "vcomp") +#endif // _DEBUG + +#endif // _OPENMP + +#if !defined(_OMPIMP) +#define _OMPIMP __declspec(dllimport) +#endif + +_OMPIMP void _OMPAPI +omp_set_num_threads( + int _Num_threads + ); + +_OMPIMP int _OMPAPI +omp_get_num_threads( + void + ); + +_OMPIMP int _OMPAPI +omp_get_max_threads( + void + ); + +_OMPIMP int _OMPAPI +omp_get_thread_num( + void + ); + +_OMPIMP int _OMPAPI +omp_get_num_procs( + void + ); + +_OMPIMP void _OMPAPI +omp_set_dynamic( + int _Dynamic_threads + ); + +_OMPIMP int _OMPAPI +omp_get_dynamic( + void + ); + +_OMPIMP int _OMPAPI +omp_in_parallel( + void + ); + +_OMPIMP void _OMPAPI +omp_set_nested( + int _Nested + ); + +_OMPIMP int _OMPAPI +omp_get_nested( + void + ); + +_OMPIMP void _OMPAPI +omp_init_lock( + omp_lock_t * _Lock + ); + +_OMPIMP void _OMPAPI +omp_destroy_lock( + omp_lock_t * _Lock + ); + +_OMPIMP void _OMPAPI +omp_set_lock( + omp_lock_t * _Lock + ); + +_OMPIMP void _OMPAPI +omp_unset_lock( + omp_lock_t * _Lock + ); + +_OMPIMP int _OMPAPI +omp_test_lock( + omp_lock_t * _Lock + ); + +_OMPIMP void _OMPAPI +omp_init_nest_lock( + omp_nest_lock_t * _Lock + ); + +_OMPIMP void _OMPAPI +omp_destroy_nest_lock( + omp_nest_lock_t * _Lock + ); + +_OMPIMP void _OMPAPI +omp_set_nest_lock( + omp_nest_lock_t * _Lock + ); + +_OMPIMP void _OMPAPI +omp_unset_nest_lock( + omp_nest_lock_t * _Lock + ); + +_OMPIMP int _OMPAPI +omp_test_nest_lock( + omp_nest_lock_t * _Lock + ); + +_OMPIMP double _OMPAPI +omp_get_wtime( + void + ); + +_OMPIMP double _OMPAPI +omp_get_wtick( + void + ); + +#if defined(__cplusplus) +} +#endif diff --git a/test_data/lots_of_files/package.bat b/test_data/lots_of_files/package.bat new file mode 100644 index 0000000..5622509 --- /dev/null +++ b/test_data/lots_of_files/package.bat @@ -0,0 +1,27 @@ +@echo off + +pushd ..\ + +set DEST=packaged + +if not exist %DEST% (mkdir %DEST%) +if not exist %DEST%\data (mkdir %DEST%\data) + +copy build\CipherDrive.exe packaged\CipherDrive.exe +copy build\*.dll packaged\*.dll +copy build\backup.bat packaged\backup.bat +del packaged\*temp* + +del /s /q packaged\data\* +copy code\data\* packaged\data\* + +set DEST=C:\Users\Allen\ownCloud\CipherDriveDevBuild + +if not exist %DEST% (mkdir %DEST%) +if not exist %DEST%\data (mkdir %DEST%\data) + +copy packaged\* %DEST%\* +del /s /q %DEST%\data\* +copy packaged\data\* %DEST%\data\* + +popd \ No newline at end of file diff --git a/test_data/lots_of_files/pgobootrun.h b/test_data/lots_of_files/pgobootrun.h new file mode 100644 index 0000000..4850f05 --- /dev/null +++ b/test_data/lots_of_files/pgobootrun.h @@ -0,0 +1,25 @@ +#if defined(_WCHAR_T_DEFINED) +typedef void (__cdecl *POGOAUTOSWEEPPROCW)(const wchar_t *); +#else +typedef void (__cdecl *POGOAUTOSWEEPPROCW)(const unsigned short *); +#endif +typedef void (__cdecl *POGOAUTOSWEEPPROCA)(const char *); + +#ifdef __cplusplus +extern "C" +#else +extern +#endif +POGOAUTOSWEEPPROCW PogoAutoSweepW; +#ifdef __cplusplus +extern "C" +#else +extern +#endif +POGOAUTOSWEEPPROCA PogoAutoSweepA; + +#ifdef UNICODE +#define PgoAutoSweep PogoAutoSweepW +#else +#define PgoAutoSweep PogoAutoSweepA +#endif diff --git a/test_data/lots_of_files/pipe.c b/test_data/lots_of_files/pipe.c new file mode 100644 index 0000000..de72595 --- /dev/null +++ b/test_data/lots_of_files/pipe.c @@ -0,0 +1,436 @@ +/* PIPE for LC-2K */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#define NUMMEMORY 65536 /* maximum number of data words in memory */ +#define NUMREGS 8 /* number of machine registers */ +#define MAXLINELENGTH 1000 + +#define ADD 0 +#define NAND 1 +#define LW 2 +#define SW 3 +#define BEQ 4 +#define JALR 5 /* JALR will not implemented for Project 3 */ +#define HALT 6 +#define NOOP 7 + +#define NOOPINSTRUCTION 0x1c00000 + +typedef struct IFIDStruct { + int instr; + int pcPlus1; +} IFIDType; + +typedef struct IDEXStruct { + int instr; + int pcPlus1; + int readRegA; + int readRegB; + int offset; +} IDEXType; + +typedef struct EXMEMStruct { + int instr; + int branchTarget; + int aluResult; + int readRegB; +} EXMEMType; + +typedef struct MEMWBStruct { + int instr; + int writeData; +} MEMWBType; + +typedef struct WBENDStruct { + int instr; + int writeData; +} WBENDType; + +typedef struct stateStruct { + int pc; + int instrMem[NUMMEMORY]; + int dataMem[NUMMEMORY]; + int reg[NUMREGS]; + int numMemory; + IFIDType IFID; + IDEXType IDEX; + EXMEMType EXMEM; + MEMWBType MEMWB; + WBENDType WBEND; + int cycles; /* number of cycles run so far */ +} stateType; + +void run(); +void printState(stateType*); +void printInstruction(int); +int convertNum(int); + +stateType state; +stateType newState; + +int +main(int argc, char *argv[]) +{ + char line[MAXLINELENGTH]; + FILE *filePtr; + int cont, e, step_count; + + if (argc != 2) { + printf("error: usage: %s <machine-code file>\n", argv[0]); + exit(1); + } + + filePtr = fopen(argv[1], "r"); + if (filePtr == NULL) { + printf("error: can't open file %s\n", argv[1]); + perror("fopen"); + exit(1); + } + + state.pc = 0; + state.cycles = 0; + memset(state.reg, 0, sizeof(int)*NUMREGS); + memset(state.dataMem, 0, sizeof(int)*NUMMEMORY); + memset(state.instrMem, 0, sizeof(int)*NUMMEMORY); + state.IFID.instr = NOOPINSTRUCTION; + state.IDEX.instr = NOOPINSTRUCTION; + state.EXMEM.instr = NOOPINSTRUCTION; + state.MEMWB.instr = NOOPINSTRUCTION; + state.WBEND.instr = NOOPINSTRUCTION; + + /* read in the entire machine-code file into memory */ + for (state.numMemory = 0; fgets(line, MAXLINELENGTH, filePtr) != NULL; + state.numMemory++) { + + if (sscanf(line, "%d", state.dataMem+state.numMemory) != 1) { + printf("error in reading address %d\n", state.numMemory); + exit(1); + } + state.instrMem[state.numMemory] = state.dataMem[state.numMemory]; + printf("memory[%d]=%d\n", state.numMemory, state.dataMem[state.numMemory]); + } + + newState = state; + run(); + + return(0); +} + +int +convertNum(int num) +{ + /* convert a 16-bit number into a 32-bit integer */ + if (num & (1 << 15) ) { + num -= (1 << 16); + } + return num; +} + +void +printState(stateType *statePtr) +{ + int i; + printf("\n@@@\nstate before cycle %d starts\n", statePtr->cycles); + printf("\tpc %d\n", statePtr->pc); + + printf("\tdata memory:\n"); + for (i=0; i<statePtr->numMemory; i++) { + printf("\t\tdataMem[ %d ] %d\n", i, statePtr->dataMem[i]); + } + printf("\tregisters:\n"); + for (i=0; i<NUMREGS; i++) { + printf("\t\treg[ %d ] %d\n", i, statePtr->reg[i]); + } + printf("\tIFID:\n"); + printf("\t\tinstruction "); + printInstruction(statePtr->IFID.instr); + printf("\t\tpcPlus1 %d\n", statePtr->IFID.pcPlus1); + printf("\tIDEX:\n"); + printf("\t\tinstruction "); + printInstruction(statePtr->IDEX.instr); + printf("\t\tpcPlus1 %d\n", statePtr->IDEX.pcPlus1); + printf("\t\treadRegA %d\n", statePtr->IDEX.readRegA); + printf("\t\treadRegB %d\n", statePtr->IDEX.readRegB); + printf("\t\toffset %d\n", statePtr->IDEX.offset); + printf("\tEXMEM:\n"); + printf("\t\tinstruction "); + printInstruction(statePtr->EXMEM.instr); + printf("\t\tbranchTarget %d\n", statePtr->EXMEM.branchTarget); + printf("\t\taluResult %d\n", statePtr->EXMEM.aluResult); + printf("\t\treadRegB %d\n", statePtr->EXMEM.readRegB); + printf("\tMEMWB:\n"); + printf("\t\tinstruction "); + printInstruction(statePtr->MEMWB.instr); + printf("\t\twriteData %d\n", statePtr->MEMWB.writeData); + printf("\tWBEND:\n"); + printf("\t\tinstruction "); + printInstruction(statePtr->WBEND.instr); + printf("\t\twriteData %d\n", statePtr->WBEND.writeData); +} + +int +field0(int instruction) +{ + return( (instruction>>19) & 0x7); +} + +int +field1(int instruction) +{ + return( (instruction>>16) & 0x7); +} + +int +field2(int instruction) +{ + return(instruction & 0xFFFF); +} + +int +opcode(int instruction) +{ + return(instruction>>22); +} + +void +printInstruction(int instr) +{ + char opcodeString[10]; + + if (opcode(instr) == ADD) { + strcpy(opcodeString, "add"); + } else if (opcode(instr) == NAND) { + strcpy(opcodeString, "nand"); + } else if (opcode(instr) == LW) { + strcpy(opcodeString, "lw"); + } else if (opcode(instr) == SW) { + strcpy(opcodeString, "sw"); + } else if (opcode(instr) == BEQ) { + strcpy(opcodeString, "beq"); + } else if (opcode(instr) == JALR) { + strcpy(opcodeString, "jalr"); + } else if (opcode(instr) == HALT) { + strcpy(opcodeString, "halt"); + } else if (opcode(instr) == NOOP) { + strcpy(opcodeString, "noop"); + } else { + strcpy(opcodeString, "data"); + } + printf("%s %d %d %d\n", opcodeString, field0(instr), field1(instr), + field2(instr)); +} + +int memstage_mux(int reg, int hazard_free_value){ + int result; + result = hazard_free_value; + + assert(((state.EXMEM.instr >> 22) & 7) == SW); + if (((state.MEMWB.instr >> 22) & 7) == LW){ + if (((state.MEMWB.instr >> 16) & 7) == reg){ + result = state.MEMWB.writeData; + } + } + + return result; +} + +int exstage_mux(int reg, int hazard_free_value){ + int resolved; + int result; + resolved = 0; + result = hazard_free_value; + + switch ((state.EXMEM.instr >> 22) & 7){ + case ADD: case NAND: + if ((state.EXMEM.instr & 7) == reg){ + resolved = 1; + result = state.EXMEM.aluResult; + } + break; + case LW: + if (((state.EXMEM.instr >> 16) & 7) == reg){ + assert((state.IDEX.instr >> 22) & 7 == SW); + } + break; + } + + if (!resolved){ + switch ((state.MEMWB.instr >> 22) & 7){ + case ADD: case NAND: + if (((state.MEMWB.instr) & 7) == reg){ + resolved = 1; + result = state.MEMWB.writeData; + } + break; + case LW: + if (((state.MEMWB.instr >> 16) & 7) == reg){ + resolved = 1; + result = state.MEMWB.writeData; + } + break; + } + + if (!resolved){ + switch ((state.WBEND.instr >> 22) & 7){ + case ADD: case NAND: + if (((state.WBEND.instr) & 7) == reg){ + resolved = 1; + result = state.WBEND.writeData; + } + break; + case LW: + if (((state.WBEND.instr >> 16) & 7) == reg){ + resolved = 1; + result = state.WBEND.writeData; + } + break; + } + } + } + + return result; +} + +void run(){ + int EXMuxTop, EXMuxBottom; + int stalled; + int lw_write_reg; + + while (1){ + printState(&state); + + /* check for halt */ + if (opcode(state.MEMWB.instr) == HALT) { + printf("machine halted\n"); + printf("total of %d cycles executed\n", state.cycles); + exit(0); + } + + newState = state; + newState.cycles++; + + stalled = 0; + + if (((state.IDEX.instr >> 22) & 7) == LW){ + lw_write_reg = (state.IDEX.instr >> 16) & 7; + switch ((state.IFID.instr >> 22) & 7){ + case ADD: case NAND: case BEQ: + if (((state.IFID.instr >> 19) & 7) == lw_write_reg) stalled = 1; + if (((state.IFID.instr >> 16) & 7) == lw_write_reg) stalled = 1; + break; + + case LW: case SW: + if (((state.IFID.instr >> 19) & 7) == lw_write_reg) stalled = 1; + break; + } + } + + /* --------------------- IF stage --------------------- */ + if (!stalled){ + newState.IFID.instr = state.instrMem[state.pc]; + newState.IFID.pcPlus1 = newState.pc + 1; + newState.pc += 1; + } + else{ + newState.IFID.instr = state.IFID.instr; + newState.IFID.pcPlus1 = state.IFID.pcPlus1; + newState.pc = state.pc; + } + + /* --------------------- ID stage --------------------- */ + if (!stalled){ + newState.IDEX.instr = state.IFID.instr; + } + else{ + newState.IDEX.instr = NOOPINSTRUCTION; + } + newState.IDEX.pcPlus1 = state.IFID.pcPlus1; + newState.IDEX.readRegA = state.reg[(state.IFID.instr >> 19) & 7]; + newState.IDEX.readRegB = state.reg[(state.IFID.instr >> 16) & 7]; + newState.IDEX.offset = convertNum(state.IFID.instr & 0xFFFF); + + /* --------------------- EX stage --------------------- */ + newState.EXMEM.instr = state.IDEX.instr; + newState.EXMEM.branchTarget = state.IDEX.pcPlus1 + state.IDEX.offset; + + if (((state.IDEX.instr >> 22) & 7) == HALT || + ((state.IDEX.instr >> 22) & 7) == NOOP){ + EXMuxTop = state.IDEX.readRegA; + } + else{ + EXMuxTop = exstage_mux((state.IDEX.instr >> 19) & 7, state.IDEX.readRegA); + } + + if (((state.IDEX.instr >> 22) & 7) == NAND || + ((state.IDEX.instr >> 22) & 7) == ADD || + ((state.IDEX.instr >> 22) & 7) == BEQ){ + EXMuxBottom = exstage_mux((state.IDEX.instr >> 16) & 7, state.IDEX.readRegB); + } + else{ + EXMuxBottom = state.IDEX.offset; + } + + if (((state.IDEX.instr >> 22) & 7) == NAND){ + newState.EXMEM.aluResult = ~(EXMuxTop & EXMuxBottom); + } + else if (((state.IDEX.instr >> 22) & 7) == BEQ){ + newState.EXMEM.aluResult = (EXMuxTop == EXMuxBottom); + } + else{ + newState.EXMEM.aluResult = EXMuxTop + EXMuxBottom; + } + + if (((state.IDEX.instr >> 22) & 7) == HALT || + ((state.IDEX.instr >> 22) & 7) == NOOP){ + newState.EXMEM.readRegB = state.IDEX.readRegB; + } + else{ + newState.EXMEM.readRegB = exstage_mux((state.IDEX.instr >> 16) & 7, state.IDEX.readRegB); + } + + /* --------------------- MEM stage --------------------- */ + newState.MEMWB.instr = state.EXMEM.instr; + if (((state.EXMEM.instr >> 22) & 7) == LW || + ((state.EXMEM.instr >> 22) & 7) == SW){ + newState.MEMWB.writeData = state.dataMem[state.EXMEM.aluResult]; + } + else{ + newState.MEMWB.writeData = state.EXMEM.aluResult; + } + + if (((state.EXMEM.instr >> 22) & 7) == SW){ + newState.dataMem[state.EXMEM.aluResult] = memstage_mux((state.IDEX.instr >> 16) & 7, state.EXMEM.readRegB); + } + + if (((state.EXMEM.instr >> 22) & 7) == BEQ){ + if (state.EXMEM.aluResult){ + newState.pc = state.EXMEM.branchTarget; + newState.EXMEM.instr = NOOPINSTRUCTION; + newState.IDEX.instr = NOOPINSTRUCTION; + newState.IFID.instr = NOOPINSTRUCTION; + } + } + + /* --------------------- WB stage --------------------- */ + newState.WBEND.instr = state.MEMWB.instr; + newState.WBEND.writeData = state.MEMWB.writeData; + + switch ((state.MEMWB.instr >> 22) & 7){ + case ADD: newState.reg[(state.MEMWB.instr & 7)] = state.MEMWB.writeData; break; + case NAND: newState.reg[(state.MEMWB.instr & 7)] = state.MEMWB.writeData; break; + case LW: newState.reg[((state.MEMWB.instr >> 16) & 7)] = state.MEMWB.writeData; break; + } + + state = newState; /* this is the last statement before end of the loop. + It marks the end of the cycle and updates the + current state with the values calculated in this + cycle */ + } +} + + + diff --git a/test_data/lots_of_files/pmmintrin.h b/test_data/lots_of_files/pmmintrin.h new file mode 100644 index 0000000..0439199 --- /dev/null +++ b/test_data/lots_of_files/pmmintrin.h @@ -0,0 +1,104 @@ +/*** +*** +*** Copyright (C) 1985-2005 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part except as expressly provided +*** by the accompanying LICENSE AGREEMENT +*** +*** cvs_id[] = "$Id: pmmintrin.h,v 1.5 2005/01/03 22:55:01 hhle Exp $"; +*** +****/ +/* + * pmmintrin.h + * + * Principal header file for Intel(R) Pentium(R) 4 processor SSE3 intrinsics + */ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_PMM +#define _INCLUDED_PMM + +#if defined (_M_CEE_PURE) + #error ERROR: EMM intrinsics not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +/* + * We need emmintrin.h for the basic type declarations. + */ +#include <emmintrin.h> + + /*****************************************************/ + /* MACROS FOR USE WITH INTRINSICS */ + /*****************************************************/ + +/* + * MACRO functions for setting and reading the DAZ bit in the MXCSR + */ +#define _MM_DENORMALS_ZERO_MASK 0x0040 +#define _MM_DENORMALS_ZERO_ON 0x0040 +#define _MM_DENORMALS_ZERO_OFF 0x0000 + +#define _MM_SET_DENORMALS_ZERO_MODE(mode) \ + _mm_setcsr((_mm_getcsr() & ~_MM_DENORMALS_ZERO_MASK) | (mode)) +#define _MM_GET_DENORMALS_ZERO_MODE() \ + (_mm_getcsr() & _MM_DENORMALS_ZERO_MASK) + + + /*****************************************************/ + /* INTRINSICS FUNCTION PROTOTYPES START HERE */ + /*****************************************************/ + +#if defined __cplusplus +extern "C" { /* Begin "C" */ + /* Intrinsics use C name-mangling. */ +#endif /* defined __cplusplus */ + +/* + * New Single precision vector instructions. + */ + +extern __m128 _mm_addsub_ps(__m128 a, __m128 b); +extern __m128 _mm_hadd_ps(__m128 a, __m128 b); +extern __m128 _mm_hsub_ps(__m128 a, __m128 b); +extern __m128 _mm_movehdup_ps(__m128 a); +extern __m128 _mm_moveldup_ps(__m128 a); + +/* + * New double precision vector instructions. + */ + +extern __m128d _mm_addsub_pd(__m128d a, __m128d b); +extern __m128d _mm_hadd_pd(__m128d a, __m128d b); +extern __m128d _mm_hsub_pd(__m128d a, __m128d b); +extern __m128d _mm_loaddup_pd(double const * dp); +extern __m128d _mm_movedup_pd(__m128d a); + +/* + * New unaligned integer vector load instruction. + */ +extern __m128i _mm_lddqu_si128(__m128i const *p); + +/* + * Miscellaneous new instructions. + */ +/* + * For _mm_monitor p goes in eax, extensions goes in ecx, hints goes in edx. + */ +extern void _mm_monitor(void const *p, unsigned extensions, unsigned hints); + +/* + * For _mm_mwait, extensions goes in ecx, hints goes in eax. + */ +extern void _mm_mwait(unsigned extensions, unsigned hints); + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INCLUDED_PMM */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/ppl.h b/test_data/lots_of_files/ppl.h new file mode 100644 index 0000000..3f36a8b --- /dev/null +++ b/test_data/lots_of_files/ppl.h @@ -0,0 +1,5935 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* ppl.h +* +* Parallel Patterns Library +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include <crtdefs.h> +#include <concrt.h> +#include <stdexcept> +#include <iterator> +#include <functional> +#include <memory> +#include <type_traits> +#include <algorithm> + +#include <pplconcrt.h> + +#define _PPL_H + +#pragma pack(push,_CRT_PACKING) +#pragma push_macro("new") +#undef new + +// Define the level of tracing to use + +#define _TRACE_LEVEL_INFORMATION 4 + +/// <summary> +/// The <c>Concurrency</c> namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ +namespace details +{ + _CRTIMP2 size_t __cdecl _GetCombinableSize(); +} // namespace details + +class structured_task_group; +class task_group; + +/// <summary> +/// The <c>task_handle</c> class represents an individual parallel work item. It encapsulates the instructions and the data required +/// to execute a piece of work. +/// </summary> +/// <typeparam name="_Function"> +/// The type of the function object that will be invoked to execute the work represented by the <c>task_handle</c> object. +/// </typeparam> +/// <remarks> +/// <c>task_handle</c> objects can be used in conjunction with a <c>structured_task_group</c> or a more general <c>task_group</c> object, +/// to decompose work into parallel tasks. For more information, see <see cref="Task Parallelism"/>. +/// <para>Note that the creator of a <c>task_handle</c> object is responsible for maintaining the lifetime of the created +/// <c>task_handle</c> object until it is no longer required by the Concurrency Runtime. Typically, this means that the <c>task_handle</c> +/// object must not destruct until either the <c>wait</c> or <c>run_and_wait</c> method of the <c>task_group</c> or +/// <c>structured_task_group</c> to which it is queued has been called.</para> +/// <para><c>task_handle</c> objects are typically used in conjunction with C++ lambdas. Because you do not know the true type of the lambda, +/// the <see cref="make_task Function">make_task</see> function is typically used to create a <c>task_handle</c> object.</para> +/// <para>The runtime creates a copy of the work function that you pass to a <c>task_handle</c> object. Therefore, any state changes that occur in a +/// function object that you pass to a <c>task_handle</c> object will not appear in your copy of that function object.</para> +/// </remarks> +/// <seealso cref="task_group Class"/> +/// <seealso cref="structured_task_group Class"/> +/// <seealso cref="make_task Function"/> +/// <seealso cref="task_group::run Method"/> +/// <seealso cref="task_group::wait Method"/> +/// <seealso cref="task_group::run_and_wait Method"/> +/// <seealso cref="structured_task_group::run Method"/> +/// <seealso cref="structured_task_group::wait Method"/> +/// <seealso cref="structured_task_group::run_and_wait Method"/> +/**/ +template<typename _Function> +class task_handle : public ::Concurrency::details::_UnrealizedChore +{ +public: + /// <summary> + /// Constructs a new <c>task_handle</c> object. The work of the task is performed by invoking the function specified as + /// a parameter to the constructor. + /// </summary> + /// <param name="_Func"> + /// The function that will be invoked to execute the work represented by the <c>task_handle</c> object. This may be a lambda functor, + /// a pointer to a function, or any object that supports a version of the function call operator with the signature <c>void operator()()</c>. + /// </param> + /// <remarks> + /// The runtime creates a copy of the work function that you pass to the constructor. Therefore, any state changes that occur in a function + /// object that you pass to a <c>task_handle</c> object will not appear in your copy of that function object. + /// </remarks> + /**/ + task_handle(const _Function& _Func) : _M_function(_Func) + { + m_pFunction = reinterpret_cast <TaskProc> (&::Concurrency::details::_UnrealizedChore::_InvokeBridge<task_handle>); + } + + /// <summary> + /// Destroys the <c>task_handle</c> object. + /// </summary> + /**/ + ~task_handle() + { + // + // We only need to perform a liveness check if the client owns the lifetime of the handle. Doing this for runtime owned handles + // is not only unnecessary -- it is also dangerous. + // + if (_OwningCollection() != NULL && !_GetRuntimeOwnsLifetime()) + { + _CheckTaskCollection(); + } + } + + /// <summary> + /// The function call operator that the runtime invokes to perform the work of the task handle. + /// </summary> + /**/ + void operator()() const + { + _M_function(); + } + +private: + + friend class task_group; + friend class structured_task_group; + + // The function object invoked to perform the body of the task. + _Function _M_function; + + task_handle const & operator=(task_handle const&); // no assignment operator + +}; + +/// <summary> +/// A factory method for creating a <c>task_handle</c> object. +/// </summary> +/// <typeparam name="_Function"> +/// The type of the function object that will be invoked to execute the work represented by the <c>task_handle</c> object. +/// </typeparam> +/// <param name="_Func"> +/// The function that will be invoked to execute the work represented by the <c>task_handle</c> object. This may be a lambda functor, +/// a pointer to a function, or any object that supports a version of the function call operator with the signature <c>void operator()()</c>. +/// </param> +/// <returns> +/// A <c>task_handle</c> object. +/// </returns> +/// <remarks> +/// This function is useful when you need to create a <c>task_handle</c> object with a lambda expression, because it allows you to +/// create the object without knowing the true type of the lambda functor. +/// </remarks> +/// <seealso cref="task_handle Class"/> +/// <seealso cref="task_group Class"/> +/// <seealso cref="structured_task_group Class"/> +/**/ +template <class _Function> +task_handle<_Function> make_task(const _Function& _Func) +{ + return task_handle<_Function>(_Func); +} + +/// <summary> +/// The <c>structured_task_group</c> class represents a highly structured collection of parallel work. You can queue individual parallel tasks to +/// a <c>structured_task_group</c> using <c>task_handle</c> objects, and wait for them to complete, or cancel the task group before they have finished +/// executing, which will abort any tasks that have not begun execution. +/// </summary> +/// <remarks> +/// There are a number of severe restrictions placed on usage of a <c>structured_task_group</c> object in order to gain performance: +/// <list type="bullet"> +/// <item> +/// <description>A single <c>structured_task_group</c> object cannot be used by multiple threads. All operations on a <c>structured_task_group</c> object +/// must be performed by the thread that created the object. The two exceptions to this rule are the member functions <c>cancel</c> and +/// <c>is_canceling</c>. The object may not be in the capture list of a lambda expression and be used within a task, unless the task is using one +/// of the cancellation operations.</description> +/// </item> +/// <item> +/// <description>All tasks scheduled to a <c>structured_task_group</c> object are scheduled through the use of <c>task_handle</c> objects which +/// you must explicitly manage the lifetime of.</description> +/// </item> +/// <item> +/// <description>Multiple groups may only be used in absolutely nested order. If two <c>structured_task_group</c> objects are declared, the second +/// one being declared (the inner one) must destruct before any method except <c>cancel</c> or <c>is_canceling</c> is called on the first one +/// (the outer one). This condition holds true in both the case of simply declaring multiple <c>structured_task_group</c> objects within the same +/// or functionally nested scopes as well as the case of a task that was queued to the <c>structured_task_group</c> via the <c>run</c> or +/// <c>run_and_wait</c> methods.</description> +/// </item> +/// <item> +/// <description>Unlike the general <c>task_group</c> class, all states in the <c>structured_task_group</c> class are final. After you have queued tasks to the +/// group and waited for them to complete, you may not use the same group again.</description> +/// </item> +/// </list> +/// <para>For more information, see <see cref="Task Parallelism"/>.</para> +/// </remarks> +/// <seealso cref="task_group Class"/> +/// <seealso cref="task_handle Class"/> +/**/ +class structured_task_group +{ +public: + + /// <summary> + /// Constructs a new <c>structured_task_group</c> object. + /// </summary> + /// <remarks> + /// The constructor that takes a cancellation token creates a <c>structured_task_group</c> that will be canceled when the source associated with + /// the token is canceled. Providing an explicit cancellation token also isolates this structured task group from participating in an implicit + /// cancellation from a parent group with a different token or no token. + /// </remarks> + /// <seealso cref="Task Parallelism"/> + /**/ + structured_task_group() + { + } + + /// <summary> + /// Constructs a new <c>structured_task_group</c> object. + /// </summary> + /// <param name="_CancellationToken"> + /// A cancellation token to associate with this structured task group. The structured task group will be canceled when the token is canceled. + /// </param> + /// <remarks> + /// The constructor that takes a cancellation token creates a <c>structured_task_group</c> that will be canceled when the source associated with + /// the token is canceled. Providing an explicit cancellation token also isolates this structured task group from participating in an implicit + /// cancellation from a parent group with a different token or no token. + /// </remarks> + /// <seealso cref="Task Parallelism"/> + /**/ + structured_task_group(cancellation_token _CancellationToken) : + _M_task_collection(_CancellationToken._GetImpl() != NULL ? _CancellationToken._GetImpl() : Concurrency::details::_CancellationTokenState::_None()) + { + } + + /// <summary> + /// Destroys a <c>structured_task_group</c> object. You are expected to call either the <c>wait</c> or <c>run_and_wait</c> method on the + /// object prior to the destructor executing, unless the destructor is executing as a result of stack unwinding due to an exception. + /// </summary> + /// <remarks> + /// If the destructor runs as the result of normal execution (for example, not stack unwinding due to an exception) and neither the <c>wait</c> nor + /// <c>run_and_wait</c> methods have been called, the destructor may throw a <see cref="missing_wait Class">missing_wait</see> exception. + /// </remarks> + /// <seealso cref="structured_task_group::wait Method"/> + /// <seealso cref="structured_task_group::run_and_wait Method"/> + /**/ + ~structured_task_group() + { + } + + /// <summary> + /// Schedules a task on the <c>structured_task_group</c> object. The caller manages the lifetime of the <c>task_handle</c> object passed + /// in the <paramref name="_Task_handle"/> parameter. The version that takes the parameter <paramref name="_Placement"/> causes the task + /// to be biased towards executing at the location specified by that parameter. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Task_handle"> + /// A handle to the work being scheduled. Note that the caller has responsibility for the lifetime of this object. The runtime will + /// continue to expect it to live until either the <c>wait</c> or <c>run_and_wait</c> method has been called on this + /// <c>structured_task_group</c> object. + /// </param> + /// <remarks> + /// The runtime creates a copy of the work function that you pass to this method. Any state changes that occur in a function object that you + /// pass to this method will not appear in your copy of that function object. + /// <para>If the <c>structured_task_group</c> destructs as the result of stack unwinding from an exception, you do not need to guarantee + /// that a call has been made to either the <c>wait</c> or <c>run_and_wait</c> method. In this case, the destructor will appropriately + /// cancel and wait for the task represented by the <paramref name="_Task_handle"/> parameter to complete.</para> + /// <para>Throws an <see cref="invalid_multiple_scheduling Class">invalid_multiple_scheduling</see> exception if the task handle given by + /// the <paramref name="_Task_handle"/> parameter has already been scheduled onto a task group object via the <c>run</c> method and there has been + /// no intervening call to either the <c>wait</c> or <c>run_and_wait</c> method on that task group.</para> + /// </remarks> + /// <seealso cref="structured_task_group::wait Method"/> + /// <seealso cref="structured_task_group::run_and_wait Method"/> + /// <seealso cref="Task Parallelism"/> + /// <seealso cref="location class"/> + /**/ + template<class _Function> + void run(task_handle<_Function>& _Task_handle) + { + _Task_handle._SetRuntimeOwnsLifetime(false); + _M_task_collection._Schedule(&_Task_handle); + } + + /// <summary> + /// Schedules a task on the <c>structured_task_group</c> object. The caller manages the lifetime of the <c>task_handle</c> object passed + /// in the <paramref name="_Task_handle"/> parameter. The version that takes the parameter <paramref name="_Placement"/> causes the task + /// to be biased towards executing at the location specified by that parameter. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Task_handle"> + /// A handle to the work being scheduled. Note that the caller has responsibility for the lifetime of this object. The runtime will + /// continue to expect it to live until either the <c>wait</c> or <c>run_and_wait</c> method has been called on this + /// <c>structured_task_group</c> object. + /// </param> + /// <param name="_Placement"> + /// A reference to the location where the task represented by the <paramref name="_Task_handle"/> parameter should execute. + /// </param> + /// <remarks> + /// The runtime creates a copy of the work function that you pass to this method. Any state changes that occur in a function object that you + /// pass to this method will not appear in your copy of that function object. + /// <para>If the <c>structured_task_group</c> destructs as the result of stack unwinding from an exception, you do not need to guarantee + /// that a call has been made to either the <c>wait</c> or <c>run_and_wait</c> method. In this case, the destructor will appropriately + /// cancel and wait for the task represented by the <paramref name="_Task_handle"/> parameter to complete.</para> + /// <para>Throws an <see cref="invalid_multiple_scheduling Class">invalid_multiple_scheduling</see> exception if the task handle given by + /// the <paramref name="_Task_handle"/> parameter has already been scheduled onto a task group object via the <c>run</c> method and there has been + /// no intervening call to either the <c>wait</c> or <c>run_and_wait</c> method on that task group.</para> + /// </remarks> + /// <seealso cref="structured_task_group::wait Method"/> + /// <seealso cref="structured_task_group::run_and_wait Method"/> + /// <seealso cref="Task Parallelism"/> + /// <seealso cref="location class"/> + /**/ + template<class _Function> + void run(task_handle<_Function>& _Task_handle, location& _Placement) + { + _Task_handle._SetRuntimeOwnsLifetime(false); + _M_task_collection._Schedule(&_Task_handle, &_Placement); + } + + /// <summary> + /// Waits until all work on the <c>structured_task_group</c> has completed or is canceled. + /// </summary> + /// <returns> + /// An indication of whether the wait was satisfied or the task group was canceled, due to either an explicit cancel operation or an exception + /// being thrown from one of its tasks. For more information, see <see cref="task_group_status Enumeration">task_group_status</see> + /// </returns> + /// <remarks> + /// Note that one or more of the tasks scheduled to this <c>structured_task_group</c> object may execute inline on the calling context. + /// <para>If one or more of the tasks scheduled to this <c>structured_task_group</c> object throws an exception, the + /// runtime will select one such exception of its choosing and propagate it out of the call to the <c>wait</c> method.</para> + /// <para>After this function returns, the <c>structured_task_group</c> object is considered in a final state and should not be used. Note that + /// utilization after the <c>wait</c> method returns will result in undefined behavior.</para> + /// <para>In the non-exceptional path of execution, you have a mandate to call either this method or the <c>run_and_wait</c> method before + /// the destructor of the <c>structured_task_group</c> executes.</para> + /// </remarks> + /// <seealso cref="structured_task_group::wait Method"/> + /// <seealso cref="structured_task_group::run_and_wait Method"/> + /// <seealso cref="Task Parallelism"/> + /**/ + task_group_status wait() + { + // + // The underlying scheduler's definitions map exactly to the PPL's. No translation beyond the cast is necessary. + // + return (task_group_status)_M_task_collection._Wait(); + } + + /// <summary> + /// Schedules a task to be run inline on the calling context with the assistance of the <c>structured_task_group</c> object for full + /// cancellation support. If a <c>task_handle</c> object is passed as a parameter to <c>run_and_wait</c>, the caller is + /// responsible for managing the lifetime of the <c>task_handle</c> object. The function then waits until all work on the + /// <c>structured_task_group</c> object has either completed or been canceled. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Task_handle"> + /// A handle to the task which will be run inline on the calling context. Note that the caller has responsibility for the lifetime of this object. + /// The runtime will continue to expect it to live until the <c>run_and_wait</c> method finishes execution. + /// </param> + /// <returns> + /// An indication of whether the wait was satisfied or the task group was canceled, due to either an explicit cancel operation or an exception + /// being thrown from one of its tasks. For more information, see <see cref="task_group_status Enumeration">task_group_status</see> + /// </returns> + /// <remarks> + /// Note that one or more of the tasks scheduled to this <c>structured_task_group</c> object may execute inline on the calling context. + /// <para>If one or more of the tasks scheduled to this <c>structured_task_group</c> object throws an exception, the + /// runtime will select one such exception of its choosing and propagate it out of the call to the <c>run_and_wait</c> method.</para> + /// <para>After this function returns, the <c>structured_task_group</c> object is considered in a final state and should not be used. + /// Note that utilization after the <c>run_and_wait</c> method returns will result in undefined behavior.</para> + /// <para>In the non-exceptional path of execution, you have a mandate to call either this method or the <c>wait</c> method before + /// the destructor of the <c>structured_task_group</c> executes.</para> + /// </remarks> + /// <seealso cref="structured_task_group::run Method"/> + /// <seealso cref="structured_task_group::wait Method"/> + /// <seealso cref="Task Parallelism"/> + /**/ + template<class _Function> + task_group_status run_and_wait(task_handle<_Function>& _Task_handle) + { + // + // The underlying scheduler's definitions map exactly to the PPL's. No translation beyond the cast is necessary. + // + return (task_group_status)_M_task_collection._RunAndWait(&_Task_handle); + } + + /// <summary> + /// Schedules a task to be run inline on the calling context with the assistance of the <c>structured_task_group</c> object for full + /// cancellation support. If a <c>task_handle</c> object is passed as a parameter to <c>run_and_wait</c>, the caller is + /// responsible for managing the lifetime of the <c>task_handle</c> object. The function then waits until all work on the + /// <c>structured_task_group</c> object has either completed or been canceled. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the task. + /// </typeparam> + /// <param name="_Func"> + /// A function which will be called to invoke the body of the work. This may be a lambda or other object which supports + /// a version of the function call operator with the signature <c>void operator()()</c>. + /// </param> + /// <returns> + /// An indication of whether the wait was satisfied or the task group was canceled, due to either an explicit cancel operation or an exception + /// being thrown from one of its tasks. For more information, see <see cref="task_group_status Enumeration">task_group_status</see> + /// </returns> + /// <remarks> + /// Note that one or more of the tasks scheduled to this <c>structured_task_group</c> object may execute inline on the calling context. + /// <para>If one or more of the tasks scheduled to this <c>structured_task_group</c> object throws an exception, the + /// runtime will select one such exception of its choosing and propagate it out of the call to the <c>run_and_wait</c> method.</para> + /// <para>After this function returns, the <c>structured_task_group</c> object is considered in a final state and should not be used. + /// Note that utilization after the <c>run_and_wait</c> method returns will result in undefined behavior.</para> + /// <para>In the non-exceptional path of execution, you have a mandate to call either this method or the <c>wait</c> method before + /// the destructor of the <c>structured_task_group</c> executes.</para> + /// </remarks> + /// <seealso cref="structured_task_group::run Method"/> + /// <seealso cref="structured_task_group::wait Method"/> + /// <seealso cref="Task Parallelism"/> + /**/ + template<class _Function> + task_group_status run_and_wait(const _Function& _Func) + { + // + // The underlying scheduler's definitions map exactly to the PPL's. No translation beyond the cast is necessary. + // + task_handle<_Function> _Task(_Func); + return (task_group_status)_M_task_collection._RunAndWait(&_Task); + } + + /// <summary> + /// Makes a best effort attempt to cancel the sub-tree of work rooted at this task group. Every task scheduled on the task group + /// will get canceled transitively if possible. + /// </summary> + /// <remarks> + /// For more information, see <see cref="Cancellation in the PPL"/>. + /// </remarks> + /**/ + void cancel() + { + _M_task_collection._Cancel(); + } + + /// <summary> + /// Informs the caller whether or not the task group is currently in the midst of a cancellation. This + /// does not necessarily indicate that the <c>cancel</c> method was called on the <c>structured_task_group</c> object + /// (although such certainly qualifies this method to return <c>true</c>). It may be the case that the <c>structured_task_group</c> object + /// is executing inline and a task group further up in the work tree was canceled. In cases such as these where the runtime can determine ahead + /// of time that cancellation will flow through this <c>structured_task_group</c> object, <c>true</c> will be returned as well. + /// </summary> + /// <returns> + /// An indication of whether the <c>structured_task_group</c> object is in the midst of a cancellation (or is guaranteed to be shortly). + /// </returns> + /// <remarks> + /// For more information, see <see cref="Cancellation in the PPL"/>. + /// </remarks> + /**/ + bool is_canceling() + { + return _M_task_collection._IsCanceling(); + } + +private: + + // Disallow passing in an r-value for a task handle argument + template<class _Function> void run(task_handle<_Function>&& _Task_handle); + + // The underlying group of tasks as known to the runtime. + ::Concurrency::details::_StructuredTaskCollection _M_task_collection; +}; + +/// <summary> +/// The <c>task_group</c> class represents a collection of parallel work which can be waited on or canceled. +/// </summary> +/// <remarks> +/// Unlike the heavily restricted <c>structured_task_group</c> class, the <c>task_group</c> class is much more general construct. +/// It does not have any of the restrictions described by <see cref="structured_task_group Class">structured_task_group</see>. <c>task_group</c> +/// objects may safely be used across threads and utilized in free-form ways. The disadvantage of the <c>task_group</c> construct is that +/// it may not perform as well as the <c>structured_task_group</c> construct for tasks which perform small amounts of work. +/// <para>For more information, see <see cref="Task Parallelism"/>.</para> +/// </remarks> +/// <seealso cref="structured_task_group Class"/> +/// <seealso cref="task_handle Class"/> +/**/ +class task_group +{ +public: + + /// <summary> + /// Constructs a new <c>task_group</c> object. + /// </summary> + /// <remarks> + /// The constructor that takes a cancellation token creates a <c>task_group</c> that will be canceled when the source associated with + /// the token is canceled. Providing an explicit cancellation token also isolates this task group from participating in an implicit + /// cancellation from a parent group with a different token or no token. + /// </remarks> + /// <seealso cref="Task Parallelism"/> + /**/ + task_group() + { + } + /// <summary> + /// Constructs a new <c>task_group</c> object. + /// </summary> + /// <param name="_CancellationToken"> + /// A cancellation token to associate with this task group. The task group will be canceled when the token is canceled. + /// </param> + /// <remarks> + /// The constructor that takes a cancellation token creates a <c>task_group</c> that will be canceled when the source associated with + /// the token is canceled. Providing an explicit cancellation token also isolates this task group from participating in an implicit + /// cancellation from a parent group with a different token or no token. + /// </remarks> + /// <seealso cref="Task Parallelism"/> + /**/ + task_group(cancellation_token _CancellationToken) : + _M_task_collection(_CancellationToken._GetImpl() != NULL ? _CancellationToken._GetImpl() : Concurrency::details::_CancellationTokenState::_None()) + { + } + + /// <summary> + /// Destroys a <c>task_group</c> object. You are expected to call the either the <c>wait</c> or <c>run_and_wait</c> method on the object + /// prior to the destructor executing, unless the destructor is executing as the result of stack unwinding due to an exception. + /// </summary> + /// <remarks> + /// If the destructor runs as the result of normal execution (for example, not stack unwinding due to an exception) and neither the <c>wait</c> nor + /// <c>run_and_wait</c> methods have been called, the destructor may throw a <see cref="missing_wait Class">missing_wait</see> exception. + /// </remarks> + /// <seealso cref="task_group::wait Method"/> + /// <seealso cref="task_group::run_and_wait Method"/> + /**/ + ~task_group() + { + } + + /// <summary> + /// Schedules a task on the <c>task_group</c> object. If a <c>task_handle</c> object is passed as a parameter to <c>run</c>, the caller is + /// responsible for managing the lifetime of the <c>task_handle</c> object. The version of the method that takes a reference to a function + /// object as a parameter involves heap allocation inside the runtime which may be perform less well than using the version that takes a + /// reference to a <c>task_handle</c> object. The version which takes the parameter <paramref name="_Placement"/> causes the task to be biased + /// towards executing at the location specified by that parameter. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Func"> + /// A function which will be called to invoke the body of the task. This may be a lambda expression or other object which supports + /// a version of the function call operator with the signature <c>void operator()()</c>. + /// </param> + /// <remarks> + /// The runtime schedules the provided work function to run at a later time, which can be after the calling function returns. + /// This method uses a <see cref="task_handle Class">task_handle</see> object to hold a copy of the provided work function. + /// Therefore, any state changes that occur in a function object that you pass to this method will not appear in your copy of that function object. + /// In addition, make sure that the lifetime of any objects that you pass by pointer or by reference to the work function remain valid until + /// the work function returns. + /// <para>If the <c>task_group</c> destructs as the result of stack unwinding from an exception, you do not need to guarantee + /// that a call has been made to either the <c>wait</c> or <c>run_and_wait</c> method. In this case, the destructor will appropriately + /// cancel and wait for the task represented by the <paramref name="_Task_handle"/> parameter to complete.</para> + /// <para>The method throws an <see cref="invalid_multiple_scheduling Class">invalid_multiple_scheduling</see> exception if the task + /// handle given by the <paramref name="_Task_handle"/> parameter has already been scheduled onto a task group object via the <c>run</c> + /// method and there has been no intervening call to either the <c>wait</c> or <c>run_and_wait</c> method on that task group.</para> + /// </remarks> + /// <seealso cref="task_group::wait Method"/> + /// <seealso cref="task_group::run_and_wait Method"/> + /// <seealso cref="Task Parallelism"/> + /// <seealso cref="location class"/> + /**/ + template<typename _Function> + void run(const _Function& _Func) + { + _M_task_collection._Schedule(::Concurrency::details::_UnrealizedChore::_InternalAlloc<task_handle<_Function>, _Function>(_Func)); + } + + /// <summary> + /// Schedules a task on the <c>task_group</c> object. If a <c>task_handle</c> object is passed as a parameter to <c>run</c>, the caller is + /// responsible for managing the lifetime of the <c>task_handle</c> object. The version of the method that takes a reference to a function + /// object as a parameter involves heap allocation inside the runtime which may be perform less well than using the version that takes a + /// reference to a <c>task_handle</c> object. The version which takes the parameter <paramref name="_Placement"/> causes the task to be biased + /// towards executing at the location specified by that parameter. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Func"> + /// A function which will be called to invoke the body of the task. This may be a lambda expression or other object which supports + /// a version of the function call operator with the signature <c>void operator()()</c>. + /// </param> + /// <param name="_Placement"> + /// A reference to the location where the task represented by the <paramref name="_Func"/> parameter should execute. + /// </param> + /// <remarks> + /// The runtime schedules the provided work function to run at a later time, which can be after the calling function returns. + /// This method uses a <see cref="task_handle Class">task_handle</see> object to hold a copy of the provided work function. + /// Therefore, any state changes that occur in a function object that you pass to this method will not appear in your copy of that function object. + /// In addition, make sure that the lifetime of any objects that you pass by pointer or by reference to the work function remain valid until + /// the work function returns. + /// <para>If the <c>task_group</c> destructs as the result of stack unwinding from an exception, you do not need to guarantee + /// that a call has been made to either the <c>wait</c> or <c>run_and_wait</c> method. In this case, the destructor will appropriately + /// cancel and wait for the task represented by the <paramref name="_Task_handle"/> parameter to complete.</para> + /// <para>The method throws an <see cref="invalid_multiple_scheduling Class">invalid_multiple_scheduling</see> exception if the task + /// handle given by the <paramref name="_Task_handle"/> parameter has already been scheduled onto a task group object via the <c>run</c> + /// method and there has been no intervening call to either the <c>wait</c> or <c>run_and_wait</c> method on that task group.</para> + /// </remarks> + /// <seealso cref="task_group::wait Method"/> + /// <seealso cref="task_group::run_and_wait Method"/> + /// <seealso cref="Task Parallelism"/> + /// <seealso cref="location class"/> + /**/ + template<typename _Function> + void run(const _Function& _Func, location& _Placement) + { + _M_task_collection._Schedule(::Concurrency::details::_UnrealizedChore::_InternalAlloc<task_handle<_Function>, _Function>(_Func), &_Placement); + } + + /// <summary> + /// Schedules a task on the <c>task_group</c> object. If a <c>task_handle</c> object is passed as a parameter to <c>run</c>, the caller is + /// responsible for managing the lifetime of the <c>task_handle</c> object. The version of the method that takes a reference to a function + /// object as a parameter involves heap allocation inside the runtime which may be perform less well than using the version that takes a + /// reference to a <c>task_handle</c> object. The version which takes the parameter <paramref name="_Placement"/> causes the task to be biased + /// towards executing at the location specified by that parameter. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Task_handle"> + /// A handle to the work being scheduled. Note that the caller has responsibility for the lifetime of this object. The runtime will + /// continue to expect it to live until either the <c>wait</c> or <c>run_and_wait</c> method has been called on this + /// <c>task_group</c> object. + /// </param> + /// <remarks> + /// The runtime schedules the provided work function to run at a later time, which can be after the calling function returns. + /// This method uses a <see cref="task_handle Class">task_handle</see> object to hold a copy of the provided work function. + /// Therefore, any state changes that occur in a function object that you pass to this method will not appear in your copy of that function object. + /// In addition, make sure that the lifetime of any objects that you pass by pointer or by reference to the work function remain valid until + /// the work function returns. + /// <para>If the <c>task_group</c> destructs as the result of stack unwinding from an exception, you do not need to guarantee + /// that a call has been made to either the <c>wait</c> or <c>run_and_wait</c> method. In this case, the destructor will appropriately + /// cancel and wait for the task represented by the <paramref name="_Task_handle"/> parameter to complete.</para> + /// <para>The method throws an <see cref="invalid_multiple_scheduling Class">invalid_multiple_scheduling</see> exception if the task + /// handle given by the <paramref name="_Task_handle"/> parameter has already been scheduled onto a task group object via the <c>run</c> + /// method and there has been no intervening call to either the <c>wait</c> or <c>run_and_wait</c> method on that task group.</para> + /// </remarks> + /// <seealso cref="task_group::wait Method"/> + /// <seealso cref="task_group::run_and_wait Method"/> + /// <seealso cref="Task Parallelism"/> + /// <seealso cref="location class"/> + /**/ + template<typename _Function> + void run(task_handle<_Function>& _Task_handle) + { + _Task_handle._SetRuntimeOwnsLifetime(false); + _M_task_collection._Schedule(&_Task_handle); + } + + /// <summary> + /// Schedules a task on the <c>task_group</c> object. If a <c>task_handle</c> object is passed as a parameter to <c>run</c>, the caller is + /// responsible for managing the lifetime of the <c>task_handle</c> object. The version of the method that takes a reference to a function + /// object as a parameter involves heap allocation inside the runtime which may be perform less well than using the version that takes a + /// reference to a <c>task_handle</c> object. The version which takes the parameter <paramref name="_Placement"/> causes the task to be biased + /// towards executing at the location specified by that parameter. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Task_handle"> + /// A handle to the work being scheduled. Note that the caller has responsibility for the lifetime of this object. The runtime will + /// continue to expect it to live until either the <c>wait</c> or <c>run_and_wait</c> method has been called on this + /// <c>task_group</c> object. + /// </param> + /// <param name="_Placement"> + /// A reference to the location where the task represented by the <paramref name="_Func"/> parameter should execute. + /// </param> + /// <remarks> + /// The runtime schedules the provided work function to run at a later time, which can be after the calling function returns. + /// This method uses a <see cref="task_handle Class">task_handle</see> object to hold a copy of the provided work function. + /// Therefore, any state changes that occur in a function object that you pass to this method will not appear in your copy of that function object. + /// In addition, make sure that the lifetime of any objects that you pass by pointer or by reference to the work function remain valid until + /// the work function returns. + /// <para>If the <c>task_group</c> destructs as the result of stack unwinding from an exception, you do not need to guarantee + /// that a call has been made to either the <c>wait</c> or <c>run_and_wait</c> method. In this case, the destructor will appropriately + /// cancel and wait for the task represented by the <paramref name="_Task_handle"/> parameter to complete.</para> + /// <para>The method throws an <see cref="invalid_multiple_scheduling Class">invalid_multiple_scheduling</see> exception if the task + /// handle given by the <paramref name="_Task_handle"/> parameter has already been scheduled onto a task group object via the <c>run</c> + /// method and there has been no intervening call to either the <c>wait</c> or <c>run_and_wait</c> method on that task group.</para> + /// </remarks> + /// <seealso cref="task_group::wait Method"/> + /// <seealso cref="task_group::run_and_wait Method"/> + /// <seealso cref="Task Parallelism"/> + /// <seealso cref="location class"/> + /**/ + template<typename _Function> + void run(task_handle<_Function>& _Task_handle, location& _Placement) + { + _Task_handle._SetRuntimeOwnsLifetime(false); + _M_task_collection._Schedule(&_Task_handle, &_Placement); + } + + /// <summary> + /// Waits until all work on the <c>task_group</c> object has either completed or been canceled. + /// </summary> + /// <returns> + /// An indication of whether the wait was satisfied or the task group was canceled, due to either an explicit cancel operation or an exception + /// being thrown from one of its tasks. For more information, see <see cref="task_group_status Enumeration">task_group_status</see>. + /// </returns> + /// <remarks> + /// Note that one or more of the tasks scheduled to this <c>task_group</c> object may execute inline on the calling context. + /// <para>If one or more of the tasks scheduled to this <c>task_group</c> object throws an exception, the + /// runtime will select one such exception of its choosing and propagate it out of the call to the <c>wait</c> method.</para> + /// <para>Calling <c>wait</c> on a <c>task_group</c> object resets it to a clean state where it can be reused. This includes the case + /// where the <c>task_group</c> object was canceled.</para> + /// <para>In the non-exceptional path of execution, you have a mandate to call either this method or the <c>run_and_wait</c> method before + /// the destructor of the <c>task_group</c> executes.</para> + /// </remarks> + /**/ + task_group_status wait() + { + // + // The underlying scheduler's definitions map exactly to the PPL's. No translation beyond the cast is necessary. + // + return static_cast<task_group_status>(_M_task_collection._Wait()); + } + + /// <summary> + /// Schedules a task to be run inline on the calling context with the assistance of the <c>task_group</c> object for full cancellation support. + /// The function then waits until all work on the <c>task_group</c> object has either completed or been canceled. If a <c>task_handle</c> object + /// is passed as a parameter to <c>run_and_wait</c>, the caller is responsible for managing the lifetime of the <c>task_handle</c> object. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task handle. + /// </typeparam> + /// <param name="_Task_handle"> + /// A handle to the task which will be run inline on the calling context. Note that the caller has responsibility for the lifetime of this object. + /// The runtime will continue to expect it to live until the <c>run_and_wait</c> method finishes execution. + /// </param> + /// <returns> + /// An indication of whether the wait was satisfied or the task group was canceled, due to either an explicit cancel operation or an exception + /// being thrown from one of its tasks. For more information, see <see cref="task_group_status Enumeration">task_group_status</see>. + /// </returns> + /// <remarks> + /// Note that one or more of the tasks scheduled to this <c>task_group</c> object may execute inline on the calling context. + /// <para>If one or more of the tasks scheduled to this <c>task_group</c> object throws an exception, the + /// runtime will select one such exception of its choosing and propagate it out of the call to the <c>run_and_wait</c> method.</para> + /// <para>Upon return from the <c>run_and_wait</c> method on a <c>task_group</c> object, the runtime resets the object to a clean state where it can be + /// reused. This includes the case where the <c>task_group</c> object was canceled.</para> + /// <para>In the non-exceptional path of execution, you have a mandate to call either this method or the <c>wait</c> method before + /// the destructor of the <c>task_group</c> executes.</para> + /// </remarks> + /// <seealso cref="task_group::run Method"/> + /// <seealso cref="task_group::wait Method"/> + /// <seealso cref="Task Parallelism"/> + /**/ + template<class _Function> + task_group_status run_and_wait(task_handle<_Function>& _Task_handle) + { + // + // The underlying scheduler's definitions map exactly to the PPL's. No translation beyond the cast is necessary. + // + _Task_handle._SetRuntimeOwnsLifetime(false); + return (task_group_status)_M_task_collection._RunAndWait(&_Task_handle); + } + + /// <summary> + /// Schedules a task to be run inline on the calling context with the assistance of the <c>task_group</c> object for full cancellation support. + /// The function then waits until all work on the <c>task_group</c> object has either completed or been canceled. If a <c>task_handle</c> object + /// is passed as a parameter to <c>run_and_wait</c>, the caller is responsible for managing the lifetime of the <c>task_handle</c> object. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to execute the body of the task. + /// </typeparam> + /// <param name="_Func"> + /// A function which will be called to invoke the body of the work. This may be a lambda expression or other object which supports + /// a version of the function call operator with the signature <c>void operator()()</c>. + /// </param> + /// <returns> + /// An indication of whether the wait was satisfied or the task group was canceled, due to either an explicit cancel operation or an exception + /// being thrown from one of its tasks. For more information, see <see cref="task_group_status Enumeration">task_group_status</see>. + /// </returns> + /// <remarks> + /// Note that one or more of the tasks scheduled to this <c>task_group</c> object may execute inline on the calling context. + /// <para>If one or more of the tasks scheduled to this <c>task_group</c> object throws an exception, the + /// runtime will select one such exception of its choosing and propagate it out of the call to the <c>run_and_wait</c> method.</para> + /// <para>Upon return from the <c>run_and_wait</c> method on a <c>task_group</c> object, the runtime resets the object to a clean state where it can be + /// reused. This includes the case where the <c>task_group</c> object was canceled.</para> + /// <para>In the non-exceptional path of execution, you have a mandate to call either this method or the <c>wait</c> method before + /// the destructor of the <c>task_group</c> executes.</para> + /// </remarks> + /// <seealso cref="task_group::run Method"/> + /// <seealso cref="task_group::wait Method"/> + /// <seealso cref="Task Parallelism"/> + /**/ + template<class _Function> + task_group_status run_and_wait(const _Function& _Func) + { + // + // The underlying scheduler's definitions map exactly to the PPL's. No translation beyond the cast is necessary. + // + return (task_group_status)_M_task_collection._RunAndWait(::Concurrency::details::_UnrealizedChore::_InternalAlloc<task_handle<_Function>, _Function>(_Func)); + } + + /// <summary> + /// Makes a best effort attempt to cancel the sub-tree of work rooted at this task group. Every task scheduled on the task group + /// will get canceled transitively if possible. + /// </summary> + /// <remarks> + /// For more information, see <see cref="Cancellation in the PPL"/>. + /// </remarks> + /**/ + void cancel() + { + _M_task_collection._Cancel(); + } + + /// <summary> + /// Informs the caller whether or not the task group is currently in the midst of a cancellation. This + /// does not necessarily indicate that the <c>cancel</c> method was called on the <c>task_group</c> object + /// (although such certainly qualifies this method to return <c>true</c>). It may be the case that the <c>task_group</c> object + /// is executing inline and a task group further up in the work tree was canceled. In cases such as these where the runtime can determine ahead + /// of time that cancellation will flow through this <c>task_group</c> object, <c>true</c> will be returned as well. + /// </summary> + /// <returns> + /// An indication of whether the <c>task_group</c> object is in the midst of a cancellation (or is guaranteed to be shortly). + /// </returns> + /// <remarks> + /// For more information, see <see cref="Cancellation in the PPL"/>. + /// </remarks> + /**/ + bool is_canceling() + { + return _M_task_collection._IsCanceling(); + } + +private: + + // Disallow passing in an r-value for a task handle argument + template<class _Function> void run(task_handle<_Function>&& _Task_handle); + + // The underlying group of tasks as known to the runtime. + ::Concurrency::details::_TaskCollection _M_task_collection; +}; + +/// <summary> +/// Executes a function object immediately and synchronously in the context of a given cancellation token. +/// </summary> +/// <typeparam name="_Function"> +/// The type of the function object that will be invoked. +/// </typeparam> +/// <param name="_Func"> +/// The function object which will be executed. This object must support the function call operator with a signature of void(void). +/// </param> +/// <param name="_Ct"> +/// The cancellation token which will control implicit cancellation of the function object. Use <c>cancellation_token::none()</c> if you want the +/// function execute without any possibility of implicit cancellation from a parent task group being canceled. +/// </param> +/// <remarks> +/// Any interruption points in the function object will be triggered when the <c>cancellation_token</c> is canceled. +/// The explicit token <paramref name="_Ct"/> will isolate this <paramref name="_Func"/> from parent cancellation if the parent has a +/// different token or no token. +/// </remarks> +/**/ +template<typename _Function> +void run_with_cancellation_token(const _Function& _Func, cancellation_token _Ct) +{ + structured_task_group _Stg(_Ct); + _Stg.run_and_wait(_Func); +} + +/// <summary> +/// Creates an interruption point for cancellation. If a cancellation is in progress in the context where this function is called, this will throw an internal +/// exception that aborts the execution of the currently executing parallel work. If cancellation is not in progress, the function does nothing. +/// </summary> +/// <remarks> +/// You should not catch the internal cancellation exception thrown by the <c>interruption_point()</c> function. The exception will be caught and handled by +/// the runtime, and catching it may cause your program to behave abnormally. +/// </remarks> +/**/ +inline void interruption_point() +{ + structured_task_group _Stg; + _Stg.wait(); +} + +/// <summary> +/// Returns an indication of whether the task group which is currently executing inline on the current context +/// is in the midst of an active cancellation (or will be shortly). Note that if there is no task group currently +/// executing inline on the current context, <c>false</c> will be returned. +/// </summary> +/// <returns> +/// <c>true</c> if the task group which is currently executing is canceling, <c>false</c> otherwise. +/// </returns> +/// <remarks> +/// For more information, see <see cref="Cancellation in the PPL"/>. +/// </remarks> +/// <seealso cref="task_group Class"/> +/// <seealso cref="structured_task_group Class"/> +/**/ +_CRTIMP2 bool __cdecl is_current_task_group_canceling(); + +// Parallel Algorithms and Patterns + +// Helper function that implements parallel_invoke with two functions +// Used by parallel_for and parallel_for_each implementations + +template <typename _Function1, typename _Function2> +void _Parallel_invoke_impl(const _Function1& _Func1, const _Function2& _Func2) +{ + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + // We inline the last item to prevent the unnecessary push/pop on the work queue. + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run_and_wait(_Task_handle2); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + _Parallel_invoke_impl(_Func1, _Func2); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run_and_wait(_Task_handle3); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function4"> +/// The type of the fourth function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <param name="_Func4"> +/// The fourth function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3, typename _Function4> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3, const _Function4& _Func4) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run(_Task_handle3); + + task_handle<_Function4> _Task_handle4(_Func4); + _Task_group.run_and_wait(_Task_handle4); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function4"> +/// The type of the fourth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function5"> +/// The type of the fifth function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <param name="_Func4"> +/// The fourth function object to be executed in parallel. +/// </param> +/// <param name="_Func5"> +/// The fifth function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3, typename _Function4, typename _Function5> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3, const _Function4& _Func4, const _Function5& _Func5) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run(_Task_handle3); + + task_handle<_Function4> _Task_handle4(_Func4); + _Task_group.run(_Task_handle4); + + task_handle<_Function5> _Task_handle5(_Func5); + _Task_group.run_and_wait(_Task_handle5); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function4"> +/// The type of the fourth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function5"> +/// The type of the fifth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function6"> +/// The type of the sixth function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <param name="_Func4"> +/// The fourth function object to be executed in parallel. +/// </param> +/// <param name="_Func5"> +/// The fifth function object to be executed in parallel. +/// </param> +/// <param name="_Func6"> +/// The sixth function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3, typename _Function4, typename _Function5, + typename _Function6> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3, const _Function4& _Func4, const _Function5& _Func5, + const _Function6& _Func6) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run(_Task_handle3); + + task_handle<_Function4> _Task_handle4(_Func4); + _Task_group.run(_Task_handle4); + + task_handle<_Function5> _Task_handle5(_Func5); + _Task_group.run(_Task_handle5); + + task_handle<_Function6> _Task_handle6(_Func6); + _Task_group.run_and_wait(_Task_handle6); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function4"> +/// The type of the fourth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function5"> +/// The type of the fifth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function6"> +/// The type of the sixth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function7"> +/// The type of the seventh function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <param name="_Func4"> +/// The fourth function object to be executed in parallel. +/// </param> +/// <param name="_Func5"> +/// The fifth function object to be executed in parallel. +/// </param> +/// <param name="_Func6"> +/// The sixth function object to be executed in parallel. +/// </param> +/// <param name="_Func7"> +/// The seventh function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3, typename _Function4, typename _Function5, + typename _Function6, typename _Function7> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3, const _Function4& _Func4, const _Function5& _Func5, + const _Function6& _Func6, const _Function7& _Func7) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run(_Task_handle3); + + task_handle<_Function4> _Task_handle4(_Func4); + _Task_group.run(_Task_handle4); + + task_handle<_Function5> _Task_handle5(_Func5); + _Task_group.run(_Task_handle5); + + task_handle<_Function6> _Task_handle6(_Func6); + _Task_group.run(_Task_handle6); + + task_handle<_Function7> _Task_handle7(_Func7); + _Task_group.run_and_wait(_Task_handle7); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function4"> +/// The type of the fourth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function5"> +/// The type of the fifth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function6"> +/// The type of the sixth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function7"> +/// The type of the seventh function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function8"> +/// The type of the eighth function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <param name="_Func4"> +/// The fourth function object to be executed in parallel. +/// </param> +/// <param name="_Func5"> +/// The fifth function object to be executed in parallel. +/// </param> +/// <param name="_Func6"> +/// The sixth function object to be executed in parallel. +/// </param> +/// <param name="_Func7"> +/// The seventh function object to be executed in parallel. +/// </param> +/// <param name="_Func8"> +/// The eighth function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3, typename _Function4, typename _Function5, + typename _Function6, typename _Function7, typename _Function8> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3, const _Function4& _Func4, const _Function5& _Func5, + const _Function6& _Func6, const _Function7& _Func7, const _Function8& _Func8) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run(_Task_handle3); + + task_handle<_Function4> _Task_handle4(_Func4); + _Task_group.run(_Task_handle4); + + task_handle<_Function5> _Task_handle5(_Func5); + _Task_group.run(_Task_handle5); + + task_handle<_Function6> _Task_handle6(_Func6); + _Task_group.run(_Task_handle6); + + task_handle<_Function7> _Task_handle7(_Func7); + _Task_group.run(_Task_handle7); + + task_handle<_Function8> _Task_handle8(_Func8); + _Task_group.run_and_wait(_Task_handle8); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function4"> +/// The type of the fourth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function5"> +/// The type of the fifth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function6"> +/// The type of the sixth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function7"> +/// The type of the seventh function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function8"> +/// The type of the eighth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function9"> +/// The type of the ninth function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <param name="_Func4"> +/// The fourth function object to be executed in parallel. +/// </param> +/// <param name="_Func5"> +/// The fifth function object to be executed in parallel. +/// </param> +/// <param name="_Func6"> +/// The sixth function object to be executed in parallel. +/// </param> +/// <param name="_Func7"> +/// The seventh function object to be executed in parallel. +/// </param> +/// <param name="_Func8"> +/// The eighth function object to be executed in parallel. +/// </param> +/// <param name="_Func9"> +/// The ninth function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3, typename _Function4, typename _Function5, + typename _Function6, typename _Function7, typename _Function8, typename _Function9> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3, const _Function4& _Func4, const _Function5& _Func5, + const _Function6& _Func6, const _Function7& _Func7, const _Function8& _Func8, const _Function9& _Func9) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run(_Task_handle3); + + task_handle<_Function4> _Task_handle4(_Func4); + _Task_group.run(_Task_handle4); + + task_handle<_Function5> _Task_handle5(_Func5); + _Task_group.run(_Task_handle5); + + task_handle<_Function6> _Task_handle6(_Func6); + _Task_group.run(_Task_handle6); + + task_handle<_Function7> _Task_handle7(_Func7); + _Task_group.run(_Task_handle7); + + task_handle<_Function8> _Task_handle8(_Func8); + _Task_group.run(_Task_handle8); + + task_handle<_Function9> _Task_handle9(_Func9); + _Task_group.run_and_wait(_Task_handle9); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// Executes the function objects supplied as parameters in parallel, and blocks until they have finished executing. Each function object +/// could be a lambda expression, a pointer to function, or any object that supports the function call operator with the signature +/// <c>void operator()()</c>. +/// </summary> +/// <typeparam name="_Function1"> +/// The type of the first function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function2"> +/// The type of the second function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function3"> +/// The type of the third function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function4"> +/// The type of the fourth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function5"> +/// The type of the fifth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function6"> +/// The type of the sixth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function7"> +/// The type of the seventh function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function8"> +/// The type of the eighth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function9"> +/// The type of the ninth function object to be executed in parallel. +/// </typeparam> +/// <typeparam name="_Function10"> +/// The type of the tenth function object to be executed in parallel. +/// </typeparam> +/// <param name="_Func1"> +/// The first function object to be executed in parallel. +/// </param> +/// <param name="_Func2"> +/// The second function object to be executed in parallel. +/// </param> +/// <param name="_Func3"> +/// The third function object to be executed in parallel. +/// </param> +/// <param name="_Func4"> +/// The fourth function object to be executed in parallel. +/// </param> +/// <param name="_Func5"> +/// The fifth function object to be executed in parallel. +/// </param> +/// <param name="_Func6"> +/// The sixth function object to be executed in parallel. +/// </param> +/// <param name="_Func7"> +/// The seventh function object to be executed in parallel. +/// </param> +/// <param name="_Func8"> +/// The eighth function object to be executed in parallel. +/// </param> +/// <param name="_Func9"> +/// The ninth function object to be executed in parallel. +/// </param> +/// <param name="_Func10"> +/// The tenth function object to be executed in parallel. +/// </param> +/// <remarks> +/// Note that one or more of the function objects supplied as parameters may execute inline on the calling context. +/// <para>If one or more of the function objects passed as parameters to this function throws an exception, the +/// runtime will select one such exception of its choosing and propagate it out of the call to <c>parallel_invoke</c>.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Function1, typename _Function2, typename _Function3, typename _Function4, typename _Function5, + typename _Function6, typename _Function7, typename _Function8, typename _Function9, typename _Function10> +void parallel_invoke(const _Function1& _Func1, const _Function2& _Func2, const _Function3& _Func3, const _Function4& _Func4, const _Function5& _Func5, + const _Function6& _Func6, const _Function7& _Func7, const _Function8& _Func8, const _Function9& _Func9, const _Function10& _Func10) +{ + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + + structured_task_group _Task_group; + + task_handle<_Function1> _Task_handle1(_Func1); + _Task_group.run(_Task_handle1); + + task_handle<_Function2> _Task_handle2(_Func2); + _Task_group.run(_Task_handle2); + + task_handle<_Function3> _Task_handle3(_Func3); + _Task_group.run(_Task_handle3); + + task_handle<_Function4> _Task_handle4(_Func4); + _Task_group.run(_Task_handle4); + + task_handle<_Function5> _Task_handle5(_Func5); + _Task_group.run(_Task_handle5); + + task_handle<_Function6> _Task_handle6(_Func6); + _Task_group.run(_Task_handle6); + + task_handle<_Function7> _Task_handle7(_Func7); + _Task_group.run(_Task_handle7); + + task_handle<_Function8> _Task_handle8(_Func8); + _Task_group.run(_Task_handle8); + + task_handle<_Function9> _Task_handle9(_Func9); + _Task_group.run(_Task_handle9); + + task_handle<_Function10> _Task_handle10(_Func10); + _Task_group.run_and_wait(_Task_handle10); + + _Trace_ppl_function(PPLParallelInvokeEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// The <c>auto_partitioner</c> class represents the default method <c>parallel_for</c>, <c>parallel_for_each</c> and +/// <c>parallel_transform</c> use to partition the range they iterates over. This method of partitioning employes range stealing +/// for load balancing as well as per-iterate cancellation. +/// </summary> +/**/ +class auto_partitioner +{ +public: + /// <summary> + /// Constructs a <c>auto_partitioner</c> object. + /// </summary> + /**/ + auto_partitioner() {} + + /// <summary> + /// Destroys a <c>auto_partitioner</c> object. + /// </summary> + /**/ + ~auto_partitioner() {} + + template<class _Type> + _Type _Get_num_chunks(_Type ) const + { + return static_cast<_Type>(Concurrency::details::_CurrentScheduler::_GetNumberOfVirtualProcessors()); + } +}; + +/// <summary> +/// The <c>static_partitioner</c> class represents a static partitioning of the range iterated over by <c>parallel_for</c>. The partitioner +/// divides the range into as many chunks as there are workers available to the underyling scheduler. +/// </summary> +/**/ +class static_partitioner +{ +public: + /// <summary> + /// Constructs a <c>static_partitioner</c> object. + /// </summary> + /**/ + static_partitioner() + { + } + + /// <summary> + /// Destroys a <c>static_partitioner</c> object. + /// </summary> + /**/ + ~static_partitioner() {} + + template<class _Type> + _Type _Get_num_chunks(_Type ) const + { + return static_cast<_Type>(Concurrency::details::_CurrentScheduler::_GetNumberOfVirtualProcessors()); + } +}; + +/// <summary> +/// The <c>simple_partitioner</c> class represents a static partitioning of the range iterated over by <c>parallel_for</c>. The partitioner +/// divides the range into chunks such that each chunk has at least the number of iterations specified by the chunk size. +/// </summary> +/**/ +class simple_partitioner +{ +private: + typedef unsigned long long _Size_type; + +public: + /// <summary> + /// Constructs a <c>simple_partitioner</c> object. + /// </summary> + /// <param name="_M_chunk_size"> + /// Number of iterations per chunk. + /// </param> + /**/ + explicit simple_partitioner(_Size_type _Chunk_size) : _M_chunk_size(_Chunk_size) + { + if (_Chunk_size == 0) + { + throw std::invalid_argument("_Chunk_size"); + } + } + + /// <summary> + /// Destroys a <c>simple_partitioner</c> object. + /// </summary> + /**/ + ~simple_partitioner() {} + + template<class _Type> + _Type _Get_num_chunks(_Type _Range_arg) const + { + static_assert(sizeof(_Type) <= sizeof(_Size_type), "Potential truncation of _Range_arg"); + _Size_type _Num_chunks = (static_cast<_Size_type>(_Range_arg) / _M_chunk_size); + + if (_Num_chunks == 0) + { + _Num_chunks = 1; + } + + return static_cast<_Type>(_Num_chunks); + } + +private: + + _Size_type _M_chunk_size; +}; + +/// <summary> +/// The <c>affinity_partitioner</c> class is similar to the <c>static_partitioner</c> class, but it improves cache affinity +/// by its choice of mapping subranges to worker threads. It can improve performance significantly when a loop is re-executed over +/// the same data set, and the data fits in cache. Note that the same <c>affinity_partitioner</c> object must be used with subsequent +/// iterations of a parallel loop that is executed over a particular data set, to benefit from data locality. +/// </summary> +/**/ +class affinity_partitioner +{ +public: + + /// <summary> + /// Constructs an <c>affinity_partitioner</c> object. + /// </summary> + /**/ + affinity_partitioner() : _M_num_chunks(0), _M_pChunk_locations(NULL) + { + } + + /// <summary> + /// Destroys an <c>affinity_partitioner</c> object. + /// </summary> + /**/ + ~affinity_partitioner() + { + delete [] _M_pChunk_locations; + } + + location& _Get_chunk_location(unsigned int _ChunkIndex) + { + return _M_pChunk_locations[_ChunkIndex]; + } + + template<class _Type> + _Type _Get_num_chunks(_Type ) + { + if (_M_num_chunks == 0) + { + _Initialize_locations(Concurrency::details::_CurrentScheduler::_GetNumberOfVirtualProcessors()); + } + + return static_cast<_Type>(_M_num_chunks); + } + +private: + // The number of chunks the partitioner will record affinity for. + unsigned int _M_num_chunks; + + // Array of remembered locations. + location * _M_pChunk_locations; + + void _Initialize_locations(unsigned int _Num_chunks) + { + _M_num_chunks = _Num_chunks; + _M_pChunk_locations = new location[_Num_chunks]; + } +}; + +// Helper methods for scheduling and executing parallel tasks + +// Disable C4180: qualifier applied to function type has no meaning; ignored +// Warning fires for passing Foo function pointer to parallel_for instead of &Foo. +#pragma warning(push) +#pragma warning(disable: 4180) +// Disable C6263: using _alloca in a loop; this can quickly overflow stack +#pragma warning(disable: 6263) + +// Template class that invokes user function on a parallel_for_each + +template <typename _Random_iterator, typename _Index_type, typename _Function, bool _Is_iterator> +class _Parallel_chunk_helper_invoke +{ +public: + static void __cdecl _Invoke(const _Random_iterator& _First, _Index_type& _Index, const _Function& _Func) + { + _Func(_First[_Index]); + } +}; + +// Template specialized class that invokes user function on a parallel_for + +template <typename _Random_iterator, typename _Index_type, typename _Function> +class _Parallel_chunk_helper_invoke<_Random_iterator, _Index_type, _Function, false> +{ +public: + static void __cdecl _Invoke(const _Random_iterator& _First, _Index_type& _Index, const _Function& _Func) + { + _Func(static_cast<_Random_iterator>(_First + _Index)); + } +}; + +// Represents a range of iteration + +template<typename _Index_type> +class _Range +{ +public: + + // Construct an object for the range [_Current_iteration, _Last_iteration) + _Range(_Index_type _Current_iteration, _Index_type _Last_iteration) : + _M_current(_Current_iteration), _M_last(_Last_iteration) + { + // On creation, the range shall have at least 1 iteration. + _CONCRT_ASSERT(_Number_of_iterations() > 0); + } + + // Send a portion of the range to the helper + void _Send_range(_Range<_Index_type> * _Helper_range) + { + // If there are no iterations other than the current one left until finish, there is no help + // needed. Set the pointer to a special value that helper will understand and continue + // doing the work. + _Index_type _Remaining_iterations = _Number_of_iterations(); + if (_Remaining_iterations > 1) + { + // Compute the two pieces of the work range: one for the worker and one for helper class. + _M_last_iteration = _M_current_iteration + _Remaining_iterations / 2; + + // There needs to be at least 1 iteration left because the current iteration cannot be sent. + _CONCRT_ASSERT(_Number_of_iterations() > 0); + } + + // This is also a signal for the helper that a range has been sent to it. + _Helper_range->_M_current_iteration = _M_last_iteration; + } + + // Steal the entire range and give it to the helper + void _Steal_range(_Range<_Index_type> * _Helper_range) + { + // We allow stealing only from a range that has atleast 1 iteration + _CONCRT_ASSERT(_Number_of_iterations() > 0); + + _Index_type _Current_iter = _M_current_iteration; + + _Helper_range->_M_current_iteration = _Current_iter + 1; + _Helper_range->_M_last_iteration = _M_last_iteration; + + _M_last_iteration = _Current_iter + 1; + } + + // Returns the number of iterations in this range + _Index_type _Number_of_iterations() const + { + return (_M_last_iteration - _M_current_iteration); + } + + // Returns the current iteration in the range + _Index_type _Get_current_iteration() const + { + return _M_current; + } + + // Sets the current iteration in the range + void _Set_current_iteration(const _Index_type _I) + { + _M_current = _I; + } + + __declspec(property(get=_Get_current_iteration, put=_Set_current_iteration)) _Index_type _M_current_iteration; + + // Returns the last iteration in the range + _Index_type _Get_last_iteration() const + { + return _M_last; + } + + // Sets the last iteration in the range + void _Set_last_iteration(const _Index_type _I) + { + _M_last = _I; + } + + __declspec(property(get=_Get_last_iteration, put=_Set_last_iteration)) _Index_type _M_last_iteration; + +private: + + // These members are volatile because they are updated by the helper + // and used by the worker. + volatile _Index_type _M_current; + volatile _Index_type _M_last; +}; + +// A proxy for the worker responsible for maintaining communication with the helper + +template<typename _Index_type> +class _Worker_proxy +{ +public: + _Worker_proxy(_Worker_proxy *_PParent_worker = NULL) : + _M_pHelper_range(NULL), _M_pParent_worker(_PParent_worker), _M_pWorker_range(NULL), _M_completion_count(0), _M_stop_iterating(0) + { + _M_context = Concurrency::details::_Context::_CurrentContext(); + } + + ~_Worker_proxy() + { + // Make the check to avoid doing extra work in the non-exceptional cases + if (_M_completion_count != _Tree_Complete) + { + // On exception, notify our parent so it breaks out of its loop. + _Propagate_cancel(); + + // On exception, we need to set _M_completion_count to ensure that the helper breaks out of its spin wait. + _Set_done(); + } + } + + // Obtain a range from the worker + bool _Receive_range(_Range<_Index_type> * _Helper_range) + { + // If the worker already finished, then there is no work left for the helper + if (_M_completion_count) + { + return false; + } + + _CONCRT_ASSERT(_Helper_range != NULL); + + // There are two special values for _M_current_iteration that are not valid: one is the + // initial value of the working class which it will never share, and the other is + // the last exclusive iteration of the working class, which has no work to be done. + // We use the former value so that we can understand worker's response. + _Index_type _Cached_first_iteration = _Helper_range->_M_current_iteration; + + // Following operation is not done via interlocked operation because it does not have to. + // Helper lazily registers that it would like to help the worker, but it allows for some + // time to elapse before that information has made it over to the worker. The idea + // is not to disturb the worker if it is not necessary. It is possible to add interlocked + // operation in the future if the time spent in the busy wait loop is too big. + _CONCRT_ASSERT(_M_pHelper_range == NULL); + _M_pHelper_range = _Helper_range; + + ::Concurrency::details::_SpinWaitBackoffNone spinWait(::Concurrency::details::_Context::_Yield); + + // If the worker is done, it will flush the store buffer and signal the helper by + // changing _M_current_iteration in the helper's range. + while ((_Helper_range->_M_current_iteration == _Cached_first_iteration) && !_M_completion_count) + { + if ((_M_pWorker_range != NULL) && _M_context._IsSynchronouslyBlocked()) + { + // Attempt to steal the entire range from the worker if it is synchronously blocked. + + // Make sure that worker makes no forward progress while helper is attempting to + // steal its range. If worker does get unblocked, simply back off in the helper. + // Note that there could be another helper running if a range has already been + // sent to us. + long _Stop_iterating = _InterlockedIncrement(&_M_stop_iterating); + _CONCRT_ASSERT(_Stop_iterating > 0); + + // We need to make a local copy as the pointer could be changed by the worker. + _Range<_Index_type> * _Worker_range = _M_pWorker_range; + + // The order of comparison needs to be preserved. If the parent is blocked, then + // it cannot send a range (because _M_stop_iterating is already set). If it sent a range + // before being synchronously blocked, then we are no longer the helper. Refrain + // from intrusively stealing the range. + if ((_Worker_range != NULL) && _M_context._IsSynchronouslyBlocked() + && (_Helper_range->_M_current_iteration == _Cached_first_iteration) && !_M_completion_count) + { + _CONCRT_ASSERT(_M_pHelper_range == _Helper_range); + + _M_pHelper_range = NULL; + _Worker_range->_Steal_range(_Helper_range); + + _CONCRT_ASSERT(_Helper_range->_M_current_iteration != _Cached_first_iteration); + } + + // At this point, worker is either: + // + // a) no longer blocked so range will come to the helper naturally, or + // b) out of iterations because helper stole all of it + _Stop_iterating = _InterlockedDecrement(&_M_stop_iterating); + _CONCRT_ASSERT(_Stop_iterating >= 0); + } + else + { + // If there is no work received in a full spin, then start yielding the context + spinWait._SpinOnce(); + } + } + + // If the initial iteration is the same as the original first iteration then the + // worker class is sending the signal that it does not need any help. + if (_Helper_range->_M_current_iteration == _Cached_first_iteration) + { + return false; + } + + return (_Helper_range->_Number_of_iterations() > 0); + } + + // Send a portion of our range and notify the helper. + bool _Send_range(_Range<_Index_type> * _Worker_range) + { + // Worker range shall not be available for stealing at this time. + _CONCRT_ASSERT(_M_pWorker_range == NULL); + + // Helper shall be registered. + _CONCRT_ASSERT(_M_pHelper_range != NULL); + + // Send the range + _Worker_range->_Send_range(_M_pHelper_range); + + // Notify the helper. The fence ensures that the prior updates are visible. + _InterlockedExchangePointer(reinterpret_cast<void * volatile *>(&_M_pHelper_range), NULL); + + // The current iteration should still be left + _CONCRT_ASSERT(_Worker_range->_Number_of_iterations() >= 1); + + // Indicate if we need another helper + return (_Worker_range->_Number_of_iterations() > 1); + } + + // Let the helper know that it is ok to intrusively steal range from the worker by publishing the + // remaining range. + void _Enable_intrusive_steal(_Range<_Index_type> * _Worker_range) + { + _M_pWorker_range = _Worker_range; + } + + // Prevent the helper from intrusively stealing range from the worker + void _Disable_intrusive_steal() + { + _M_pWorker_range = NULL; + _Wait_on_intrusive_steal(); + } + + bool _Is_helper_registered() + { + return (_M_pHelper_range != NULL); + } + + bool _Is_done() + { + return (_M_completion_count != 0); + } + + void _Set_done() + { + // Let the helper know that this class is done with work and flush the store buffer. This operation + // ensures that any buffered store to helper range in _Send_range is flushed and + // available in _Receive_range (so there will be no lost ranges). + _InterlockedExchange(&_M_completion_count, 1); + } + + void _Set_tree_done() + { + // Make sure that **WE** know when our destructor hits that the entire tree is complete. + _M_completion_count = _Tree_Complete; + } + + bool _Is_beacon_signaled() + { + return _M_beacon._Is_signaled(); + } + + bool _Verify_beacon_cancellation() + { + return _M_beacon._Confirm_cancel(); + } + +private: + + // Spin wait for any intrusive steal that is in progress. + void _Wait_on_intrusive_steal() + { + // This code is used to synchronize with helper in case of worker cooperative blocking. + if (_M_stop_iterating != 0) + { + ::Concurrency::details::_SpinWaitBackoffNone spinWait; + + while (_M_stop_iterating != 0) + { + spinWait._SpinOnce(); + } + } + } + + void _NotifyCancel() + { + _M_beacon._Raise(); + } + + void _Propagate_cancel() + { + if (_M_pParent_worker != NULL) + { + _M_pParent_worker->_NotifyCancel(); + } + } + + // Constant indicating sub-tree completion + static const long _Tree_Complete = 2; + + // Read in the loop + _Range<_Index_type> * volatile _M_pHelper_range; + + // Read at the end of the loop + _Worker_proxy * _M_pParent_worker; + + // Written rarely + ::Concurrency::details::_Cancellation_beacon _M_beacon; + ::Concurrency::details::_Context _M_context; + + volatile long _M_completion_count; + + // Written to in the loop + _Range<_Index_type> * volatile _M_pWorker_range; + volatile long _M_stop_iterating; + + _Worker_proxy const & operator=(_Worker_proxy const&); // no assignment operator + +}; + +// parallel_for -- Performs parallel iteration over a range of indices from _First to _Last, +// excluding _Last. The order in which each iteration is executed is unspecified and non-deterministic. + +// Closure (binding) classes for invoking parallel_for and parallel_for_each, with chunks + +// A dynamically rebalancing closure class used for packaging parallel_for or parallel_for_each for invocation in chunks. +// If some tasks finish earlier than others, helper tasks get executed which ensures further distribution of work. + +template <typename _Random_iterator, typename _Index_type, typename _Function, typename _Partitioner, bool _Is_iterator> +class _Parallel_chunk_helper +{ +public: + _Parallel_chunk_helper(_Index_type, const _Random_iterator& _First, _Index_type _First_iteration, _Index_type _Last_iteration, const _Index_type& _Step, + const _Function& _Func, const _Partitioner&, _Worker_proxy<_Index_type> * const _Parent_data = NULL) : + _M_first(_First), _M_first_iteration(_First_iteration), _M_last_iteration(_Last_iteration), _M_step(_Step), _M_function(_Func), + _M_parent_worker(_Parent_data) + { + // Empty constructor because members are already assigned + } + + // Constructor overload that accepts a range + _Parallel_chunk_helper(const _Random_iterator& _First, const _Index_type& _Step, const _Function& _Func, + const _Range<_Index_type>& _Worker_range, _Worker_proxy<_Index_type> * const _Parent_data = NULL) : + _M_first(_First), _M_first_iteration(_Worker_range._M_current_iteration), _M_last_iteration(_Worker_range._M_last_iteration), _M_step(_Step), _M_function(_Func), + _M_parent_worker(_Parent_data) + { + // Empty constructor because members are already assigned + } + + // The main helper function which iterates over the given collection and invokes user function on every iteration. + // Function is marked as const even though it does mutate some of its members (those are declared as mutable). This is done + // in order to easily communicate between a worker and a helper instance, without holding references to many local variables. + // However, this function does not mutate any state that is visible to anyone outside of this class, nor would that be + // possible due to the implicit copy of the functor that happens when a new task_handle is created. + void operator()() const + { + _Range<_Index_type> _Worker_range(_M_first_iteration, _M_last_iteration); + + // This class has two modes: worker and helper. The originally split chunk is always a + // worker, while any subsequent class spawned from this class is in the helper + // mode, which is signified using a link to the worker class through _M_pOwning_worker + // handle. So, it will wait for work to be dished out by the working class while in helper mode. + if (_M_parent_worker != NULL && !_M_parent_worker->_Receive_range(&_Worker_range)) + { + // If the worker class rejected the help, simply return + return; + } + + // Keep the secondary, scaled, loop index for quick indexing into the data structure + _Index_type _Current_iteration = _Worker_range._M_current_iteration; + _Index_type _Scaled_index = _Current_iteration * _M_step; + + // If there is only one iteration to be executed there is no need to initialize any + // helper classes (work is indivisible). + if (_Worker_range._Number_of_iterations() == 1) + { + // Execute one iteration + _Parallel_chunk_helper_invoke<_Random_iterator, _Index_type, _Function, _Is_iterator>::_Invoke(_M_first, _Scaled_index, _M_function); + return; + } + + // If the execution reaches this point it means that this class now has a chunk of work + // that it needs to get done, so it has transitioned into the worker mode. + structured_task_group _Helper_group; + + // Initialize fields that are needed in the helper + _Worker_proxy<_Index_type> _Worker(_M_parent_worker); + + // Instantiate a helper class for this working class and put it on the work queue. + // If some thread is idle it will be able to steal the helper and help this class + // finish its work by stealing a piece of the work range. + task_handle<_Parallel_chunk_helper> _Helper_task(_Parallel_chunk_helper(_M_first, _M_step, _M_function, _Worker_range, &_Worker)); + + _Helper_group.run(_Helper_task); + + ::Concurrency::details::_MallocaListHolder<task_handle<_Parallel_chunk_helper>> _Holder; + + // Normally, for a cancellation semantic in cooperation with the helper, we would run_and_wait the below code on the Helper_group. Unfortunately, + // the capture by reference of things which must be shared (_Worker, and so forth) will cause the loop below to add additional indirection + // instructions. The loop below *MUST* be as tight as possible with the defined semantics. Instead, we will manually notify our parent if the + // worker's destructor runs without hitting the bottom of our chunk. This is done through notification on the beacon. + + for (_Index_type _I = _Current_iteration; _I < _Worker_range._M_last_iteration; (_I++, _Worker_range._M_current_iteration =_I, _Scaled_index += _M_step)) + { + if (_Worker._Is_beacon_signaled()) + { + // Either a parent task group is canceled or one of the other iterations + // threw an exception. Abort the remaining iterations + // + // Note that this could be a false positive that we must verify. + if (_Worker._Is_done() || _Worker._Verify_beacon_cancellation()) + { + break; + } + } + + if (_Worker._Is_helper_registered()) + { + // The helper class (there can only be one) registered to help this class with the work. + // Thus, figure out if this class needs help and split the range among the two classes. + + if (_Worker._Send_range(&_Worker_range)) + { + // Construct every new instance of a helper class on the stack because it is beneficial to use + // a structured task group where the class itself is responsible for task handle's lifetime. + task_handle<_Parallel_chunk_helper> * _Helper_subtask = _Holder._AddRawMallocaNode(_malloca(_Holder._GetAllocationSize())); + + new(_Helper_subtask) task_handle<_Parallel_chunk_helper> + (_Parallel_chunk_helper(_M_first, _M_step, _M_function, _Worker_range, &_Worker)); + + // If _Send_range returns true, that means that there is still some non-trivial + // work to be done, so this class will potentially need another helper. + _Helper_group.run(*_Helper_subtask); + } + } + + // Allow intrusive stealing by the helper + _Worker._Enable_intrusive_steal(&_Worker_range); + + // Execute one iteration: the element is at scaled index away from the first element. + _Parallel_chunk_helper_invoke<_Random_iterator, _Index_type, _Function, _Is_iterator>::_Invoke(_M_first, _Scaled_index, _M_function); + + // Helper shall not steal a range after this call + _Worker._Disable_intrusive_steal(); + } + + // Indicate that the worker is done with its iterations. + _Worker._Set_done(); + + // Wait for all worker/helper iterations to finish + _Helper_group.wait(); + + // Make sure that we've signaled that the tree is complete. This is used to detect any exception out of either _Parallel_chunk_helper_invoke or + // _Helper_group.wait() above as a cancellation of the loop which must propagate upwards because we do not wrap the loop body in run_and_wait. + _Worker._Set_tree_done(); + } + +private: + + const _Random_iterator& _M_first; + const _Index_type& _M_step; + const _Function& _M_function; + + const _Index_type _M_first_iteration; + const _Index_type _M_last_iteration; + + _Worker_proxy<_Index_type> * const _M_parent_worker; + + _Parallel_chunk_helper const & operator=(_Parallel_chunk_helper const&); // no assignment operator +}; + +template <typename _Random_iterator, typename _Index_type, typename _Function, typename _Partitioner, bool _Is_iterator> +class _Parallel_fixed_chunk_helper +{ +public: + _Parallel_fixed_chunk_helper(_Index_type, const _Random_iterator& _First, _Index_type _First_iteration, + _Index_type _Last_iteration, const _Index_type& _Step, const _Function& _Func, const _Partitioner&) : + _M_first(_First), _M_first_iteration(_First_iteration), _M_last_iteration(_Last_iteration), _M_step(_Step), _M_function(_Func) + { + // Empty constructor because members are already assigned + } + + void operator()() const + { + // Keep the secondary, scaled, loop index for quick indexing into the data structure + _Index_type _Scaled_index = _M_first_iteration * _M_step; + + for (_Index_type _I = _M_first_iteration; _I < _M_last_iteration; (_I++, _Scaled_index += _M_step)) + { + // Execute one iteration: the element is at scaled index away from the first element. + _Parallel_chunk_helper_invoke<_Random_iterator, _Index_type, _Function, _Is_iterator>::_Invoke(_M_first, _Scaled_index, _M_function); + } + } +private: + + const _Random_iterator& _M_first; + const _Index_type& _M_step; + const _Function& _M_function; + + const _Index_type _M_first_iteration; + const _Index_type _M_last_iteration; + + _Parallel_fixed_chunk_helper const & operator=(_Parallel_fixed_chunk_helper const&); // no assignment operator +}; + +template <typename _Random_iterator, typename _Index_type, typename _Function, bool _Is_iterator> +class _Parallel_localized_chunk_helper +{ +public: + typedef _Parallel_fixed_chunk_helper<_Random_iterator, _Index_type, _Function, static_partitioner, _Is_iterator> _Base; + + _Parallel_localized_chunk_helper(_Index_type _Chunk_index, const _Random_iterator& _First, _Index_type _First_iteration, _Index_type _Last_iteration, const _Index_type& _Step, + const _Function& _Func, affinity_partitioner& _Part) : + _M_fixed_helper(_Chunk_index, _First, _First_iteration, _Last_iteration, _Step, _Func, static_partitioner()), + _M_chunk_location(_Part._Get_chunk_location(static_cast<unsigned int>(_Chunk_index))) + { + // Empty constructor because members are already assigned + } + + // Override the operator() in the base class. Note that this is not a virtual override. + void operator()() const + { + // Check here if location needs to be saved. + if (_M_chunk_location._Is_system()) + { + _M_chunk_location = location::current(); + } + + _M_fixed_helper(); + } +private: + + location& _M_chunk_location; + _Base _M_fixed_helper; + _Parallel_localized_chunk_helper const & operator=(_Parallel_localized_chunk_helper const&); // no assignment operator +}; + +#pragma warning(pop) + +template <typename _Worker_class, typename _Index_type, typename Partitioner> +void _Parallel_chunk_task_group_run(structured_task_group& _Task_group, + task_handle<_Worker_class>* _Chunk_helpers, + const Partitioner&, + _Index_type _I) +{ + _Task_group.run(_Chunk_helpers[_I]); +} + +template <typename _Worker_class, typename _Index_type> +void _Parallel_chunk_task_group_run(structured_task_group& _Task_group, + task_handle<_Worker_class>* _Chunk_helpers, + affinity_partitioner& _Part, + _Index_type _I) +{ + _Task_group.run(_Chunk_helpers[_I], _Part._Get_chunk_location(static_cast<unsigned int>(_I))); +} + + +// Helper functions that implement parallel_for + +template <typename _Worker_class, typename _Random_iterator, typename _Index_type, typename _Function, typename _Partitioner> +void _Parallel_chunk_impl(const _Random_iterator& _First, _Index_type _Range_arg, const _Index_type& _Step, const _Function& _Func, _Partitioner&& _Part) +{ + _CONCRT_ASSERT(_Range_arg > 1); + _CONCRT_ASSERT(_Step > 0); + + _Index_type _Num_iterations = (_Step == 1) ? _Range_arg : (((_Range_arg - 1) / _Step) + 1); + _CONCRT_ASSERT(_Num_iterations > 1); + + _Index_type _Num_chunks = _Part._Get_num_chunks(_Num_iterations); + _CONCRT_ASSERT(_Num_chunks > 0); + + // Allocate memory on the stack for task_handles to ensure everything is properly structured. + ::Concurrency::details::_MallocaArrayHolder<task_handle<_Worker_class>> _Holder; + task_handle<_Worker_class> * _Chunk_helpers = _Holder._InitOnRawMalloca(_malloca(sizeof(task_handle<_Worker_class>) * static_cast<size_t>(_Num_chunks))); + + structured_task_group _Task_group; + + _Index_type _Iterations_per_chunk = _Num_iterations / _Num_chunks; + _Index_type _Remaining_iterations = _Num_iterations % _Num_chunks; + + // If there are less iterations than desired chunks, set the chunk number + // to be the number of iterations. + if (_Iterations_per_chunk == 0) + { + _Num_chunks = _Remaining_iterations; + } + + _Index_type _Work_size = 0; + _Index_type _Start_iteration = 0; + _Index_type _I; + + // Split the available work in chunks + for (_I = 0; _I < _Num_chunks - 1; _I++) + { + if (_Remaining_iterations > 0) + { + // Iterations are not divided evenly, so add 1 remainder iteration each time + _Work_size = _Iterations_per_chunk + 1; + _Remaining_iterations--; + } + else + { + _Work_size = _Iterations_per_chunk; + } + + // New up a task_handle "in-place", in the array preallocated on the stack + new(&_Chunk_helpers[_I]) task_handle<_Worker_class>(_Worker_class(_I, _First, _Start_iteration, _Start_iteration + _Work_size, _Step, _Func, std::forward<_Partitioner>(_Part))); + _Holder._IncrementConstructedElemsCount(); + + // Run each of the chunk tasks in parallel + _Parallel_chunk_task_group_run(_Task_group, _Chunk_helpers, std::forward<_Partitioner>(_Part), _I); + + // Prepare for the next iteration + _Start_iteration += _Work_size; + } + + // Because this is the last iteration, then work size might be different + _CONCRT_ASSERT((_Remaining_iterations == 0) || ((_Iterations_per_chunk == 0) && (_Remaining_iterations == 1))); + _Work_size = _Num_iterations - _Start_iteration; + + // New up a task_handle "in-place", in the array preallocated on the stack + new(&_Chunk_helpers[_I]) task_handle<_Worker_class>(_Worker_class(_I, _First, _Start_iteration, _Start_iteration + _Work_size, _Step, _Func, std::forward<_Partitioner>(_Part))); + _Holder._IncrementConstructedElemsCount(); + + _Task_group.run_and_wait(_Chunk_helpers[_I]); +} + +template <typename _Worker_class, typename _Random_iterator, typename _Index_type, typename _Function> +void _Parallel_chunk_impl(const _Random_iterator& _First, _Index_type _Range_arg, const _Index_type& _Step, const _Function& _Func) +{ + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, auto_partitioner()); +} + +// Helper for the parallel for API with the default dynamic partitioner which implements range-stealing to balance load. +template <typename _Index_type, typename _Diff_type, typename _Function> +void _Parallel_for_partitioned_impl(_Index_type _First, _Diff_type _Range_arg, _Diff_type _Step, const _Function& _Func, const auto_partitioner& _Part) +{ + typedef _Parallel_chunk_helper<_Index_type, _Diff_type, _Function, auto_partitioner, false> _Worker_class; + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +// Helper for the parallel_for API with a static partitioner - creates a fixed number of chunks up front with no range-stealing enabled. +template <typename _Index_type, typename _Diff_type, typename _Function> +void _Parallel_for_partitioned_impl(_Index_type _First, _Diff_type _Range_arg, _Diff_type _Step, const _Function& _Func, const static_partitioner& _Part) +{ + typedef _Parallel_fixed_chunk_helper<_Index_type, _Diff_type, _Function, static_partitioner, false> _Worker_class; + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +// Helper for the parallel_for API with a simple partitioner - creates a fixed number of chunks up front with no range-stealing enabled. +template <typename _Index_type, typename _Diff_type, typename _Function> +void _Parallel_for_partitioned_impl(_Index_type _First, _Diff_type _Range_arg, _Diff_type _Step, const _Function& _Func, const simple_partitioner& _Part) +{ + typedef _Parallel_fixed_chunk_helper<_Index_type, _Diff_type, _Function, simple_partitioner, false> _Worker_class; + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +// Helper for the parallel_for API with an affinity partitioner - creates a fixed number of chunks up front with no range-stealing enabled. subsequent +// calls to parallel_for with the same affinity partitioner (pass in as a non-const reference) are scheduled to the same location they previously ran on +template <typename _Index_type, typename _Diff_type, typename _Function> +void _Parallel_for_partitioned_impl(_Index_type _First, _Diff_type _Range_arg, _Diff_type _Step, const _Function& _Func, affinity_partitioner& _Part) +{ + typedef _Parallel_localized_chunk_helper<_Index_type, _Diff_type, _Function, false> _Worker_class; + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +template <typename _Index_type, typename _Function, typename _Partitioner> +void _Parallel_for_impl(_Index_type _First, _Index_type _Last, _Index_type _Step, const _Function& _Func, _Partitioner&& _Part) +{ + // The step argument must be 1 or greater; otherwise it is an invalid argument + if (_Step < 1) + { + throw std::invalid_argument("_Step"); + } + + // If there are no elements in this range we just return + if (_First >= _Last) + { + return; + } + + // Compute the difference type based on the arguments and avoid signed overflow for int, long, and long long + typedef typename std::tr1::conditional<std::tr1::is_same<_Index_type, int>::value, unsigned int, + typename std::tr1::conditional<std::tr1::is_same<_Index_type, long>::value, unsigned long, + typename std::tr1::conditional<std::tr1::is_same<_Index_type, long long>::value, unsigned long long, decltype(_Last - _First) + >::type + >::type + >::type _Diff_type; + + _Diff_type _Range_size = _Diff_type(_Last) - _Diff_type(_First); + _Diff_type _Diff_step = _Step; + + if (_Range_size <= _Diff_step) + { + _Func(_First); + } + else + { + _Parallel_for_partitioned_impl<_Index_type, _Diff_type, _Function>(_First, _Range_size, _Step, _Func, std::forward<_Partitioner>(_Part)); + } +} + +template <typename _Index_type, typename _Function> +void _Parallel_for_impl(_Index_type _First, _Index_type _Last, _Index_type _Step, const _Function& _Func) +{ + _Parallel_for_impl(_First, _Last, _Step, _Func, auto_partitioner()); +} + +/// <summary> +/// <c>parallel_for</c> iterates over a range of indices and executes a user-supplied function at each iteration, in parallel. +/// </summary> +/// <typeparam name="_Index_type"> +/// The type of the index being used for the iteration. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be executed at each iteration. +/// </typeparam> +/// <typeparam name="_Partitioner"> +/// The type of the partitioner that is used to partition the supplied range. +/// </typeparam> +/// <param name="_First"> +/// The first index to be included in the iteration. +/// </param> +/// <param name="_Last"> +/// The index one past the last index to be included in the iteration. +/// </param> +/// <param name="_Step"> +/// The value by which to step when iterating from <paramref name="_First"/> to <paramref name="_Last"/>. The step must be positive. +/// <see cref="invalid_argument Class">invalid_argument</see> is thrown if the step is less than 1. +/// </param> +/// <param name="_Func"> +/// The function to be executed at each iteration. This may be a lambda expression, a function pointer, or any object +/// that supports a version of the function call operator with the signature +/// <c>void operator()(</c><typeparamref name="_Index_type"/><c>)</c>. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <remarks> +/// For more information, see <see cref="Parallel Algorithms"/>. +/// </remarks> +/**/ +template <typename _Index_type, typename _Function, typename _Partitioner> +void parallel_for(_Index_type _First, _Index_type _Last, _Index_type _Step, const _Function& _Func, _Partitioner&& _Part) +{ + _Trace_ppl_function(PPLParallelForEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + _Parallel_for_impl(_First, _Last, _Step, _Func, std::forward<_Partitioner>(_Part)); + _Trace_ppl_function(PPLParallelForEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +/// <summary> +/// <c>parallel_for</c> iterates over a range of indices and executes a user-supplied function at each iteration, in parallel. +/// </summary> +/// <typeparam name="_Index_type"> +/// The type of the index being used for the iteration. <paramref name="_Index_type"/> must be an integral type. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be executed at each iteration. +/// </typeparam> +/// <param name="_First"> +/// The first index to be included in the iteration. +/// </param> +/// <param name="_Last"> +/// The index one past the last index to be included in the iteration. +/// </param> +/// <param name="_Step"> +/// The value by which to step when iterating from <paramref name="_First"/> to <paramref name="_Last"/>. The step must be positive. +/// <see cref="invalid_argument Class">invalid_argument</see> is thrown if the step is less than 1. +/// </param> +/// <param name="_Func"> +/// The function to be executed at each iteration. This may be a lambda expression, a function pointer, or any object +/// that supports a version of the function call operator with the signature +/// <c>void operator()(</c><typeparamref name="_Index_type"/><c>)</c>. +/// </param> +/// <remarks> +/// For more information, see <see cref="Parallel Algorithms"/>. +/// </remarks> +/**/ +template <typename _Index_type, typename _Function> +void parallel_for(_Index_type _First, _Index_type _Last, _Index_type _Step, const _Function& _Func) +{ + parallel_for(_First, _Last, _Step, _Func, auto_partitioner()); +} + +/// <summary> +/// <c>parallel_for</c> iterates over a range of indices and executes a user-supplied function at each iteration, in parallel. +/// </summary> +/// <typeparam name="_Index_type"> +/// The type of the index being used for the iteration. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be executed at each iteration. +/// </typeparam> +/// <param name="_First"> +/// The first index to be included in the iteration. +/// </param> +/// <param name="_Last"> +/// The index one past the last index to be included in the iteration. +/// </param> +/// <param name="_Func"> +/// The function to be executed at each iteration. This may be a lambda expression, a function pointer, or any object +/// that supports a version of the function call operator with the signature +/// <c>void operator()(</c><typeparamref name="_Index_type"/><c>)</c>. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <remarks> +/// For more information, see <see cref="Parallel Algorithms"/>. +/// </remarks> +/**/ +template <typename _Index_type, typename _Function> +void parallel_for(_Index_type _First, _Index_type _Last, const _Function& _Func, const auto_partitioner& _Part = auto_partitioner()) +{ + parallel_for(_First, _Last, _Index_type(1), _Func, _Part); +} + +/// <summary> +/// <c>parallel_for</c> iterates over a range of indices and executes a user-supplied function at each iteration, in parallel. +/// </summary> +/// <typeparam name="_Index_type"> +/// The type of the index being used for the iteration. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be executed at each iteration. +/// </typeparam> +/// <param name="_First"> +/// The first index to be included in the iteration. +/// </param> +/// <param name="_Last"> +/// The index one past the last index to be included in the iteration. +/// </param> +/// <param name="_Func"> +/// The function to be executed at each iteration. This may be a lambda expression, a function pointer, or any object +/// that supports a version of the function call operator with the signature +/// <c>void operator()(</c><typeparamref name="_Index_type"/><c>)</c>. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <remarks> +/// For more information, see <see cref="Parallel Algorithms"/>. +/// </remarks> +/**/ +template <typename _Index_type, typename _Function> +void parallel_for(_Index_type _First, _Index_type _Last, const _Function& _Func, const static_partitioner& _Part) +{ + parallel_for(_First, _Last, _Index_type(1), _Func, _Part); +} + +/// <summary> +/// <c>parallel_for</c> iterates over a range of indices and executes a user-supplied function at each iteration, in parallel. +/// </summary> +/// <typeparam name="_Index_type"> +/// The type of the index being used for the iteration. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be executed at each iteration. +/// </typeparam> +/// <param name="_First"> +/// The first index to be included in the iteration. +/// </param> +/// <param name="_Last"> +/// The index one past the last index to be included in the iteration. +/// </param> +/// <param name="_Func"> +/// The function to be executed at each iteration. This may be a lambda expression, a function pointer, or any object +/// that supports a version of the function call operator with the signature +/// <c>void operator()(</c><typeparamref name="_Index_type"/><c>)</c>. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <remarks> +/// For more information, see <see cref="Parallel Algorithms"/>. +/// </remarks> +/**/ +template <typename _Index_type, typename _Function> +void parallel_for(_Index_type _First, _Index_type _Last, const _Function& _Func, const simple_partitioner& _Part) +{ + parallel_for(_First, _Last, _Index_type(1), _Func, _Part); +} + +/// <summary> +/// <c>parallel_for</c> iterates over a range of indices and executes a user-supplied function at each iteration, in parallel. +/// </summary> +/// <typeparam name="_Index_type"> +/// The type of the index being used for the iteration. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be executed at each iteration. +/// </typeparam> +/// <param name="_First"> +/// The first index to be included in the iteration. +/// </param> +/// <param name="_Last"> +/// The index one past the last index to be included in the iteration. +/// </param> +/// <param name="_Func"> +/// The function to be executed at each iteration. This may be a lambda expression, a function pointer, or any object +/// that supports a version of the function call operator with the signature +/// <c>void operator()(</c><typeparamref name="_Index_type"/><c>)</c>. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <remarks> +/// For more information, see <see cref="Parallel Algorithms"/>. +/// </remarks> +/**/ +template <typename _Index_type, typename _Function> +void parallel_for(_Index_type _First, _Index_type _Last, const _Function& _Func, affinity_partitioner& _Part) +{ + parallel_for(_First, _Last, _Index_type(1), _Func, _Part); +} + +// parallel_for_each -- This function will iterate over all elements in the iterator's range. + +// Closure (binding) classes for invoking parallel_for_each recursively + +// A closure class used for packaging chunk of elements in parallel_for_each for parallel invocation + +// Forward iterator for_each using unstructured task group + +// Disable C4180: qualifier applied to function type has no meaning; ignored +// Warning fires for passing Foo function pointer to parallel_for instead of &Foo. +#pragma warning(push) +#pragma warning(disable: 4180) + +template <typename _Forward_iterator, typename _Function, unsigned int _Chunk_size> +class _Parallel_for_each_helper +{ +public: + typedef typename std::iterator_traits<_Forward_iterator>::value_type _Value_type; + static const unsigned int _Size = _Chunk_size; + + _Parallel_for_each_helper(_Forward_iterator& _First, const _Forward_iterator& _Last, const _Function& _Func) : + _M_function(_Func), _M_len(0) + { + static_assert(std::is_lvalue_reference<decltype(*_First)>::value, "lvalue required for forward iterator operator *"); + // Add a batch of work items to this functor's array + for (unsigned int _Index=0; (_Index < _Size) && (_First != _Last); _Index++) + { + _M_element[_M_len++] = &(*_First++); + } + } + + void operator()() const + { + // Invoke parallel_for on the batched up array of elements + _Parallel_for_impl(0U, _M_len, 1U, + [this] (unsigned int _Index) + { + _M_function(*(_M_element[_Index])); + } + ); + } + +private: + + const _Function& _M_function; + typename std::iterator_traits<_Forward_iterator>::pointer _M_element[_Size]; + unsigned int _M_len; + + _Parallel_for_each_helper const & operator=(_Parallel_for_each_helper const&); // no assignment operator +}; + +#pragma warning(pop) + +// Helper functions that implement parallel_for_each + +template <typename _Forward_iterator, typename _Function> +void _Parallel_for_each_chunk(_Forward_iterator& _First, const _Forward_iterator& _Last, const _Function& _Func, task_group& _Task_group) +{ + // The chunk size selection depends more on the internal implementation of parallel_for than + // on the actual input. Also, it does not have to be dynamically computed, but it helps + // parallel_for if it is a power of 2 (easy to divide). + const unsigned int _Chunk_size = 1024; + + // This functor will be copied on the heap and will execute the chunk in parallel + _Parallel_for_each_helper<_Forward_iterator, _Function, _Chunk_size> _Functor(_First, _Last, _Func); + + // Because this is an unstructured task group, running the task will make a copy of the necessary data + // on the heap, ensuring that it is available at the time of execution. + _Task_group.run(_Functor); +} + +template <typename _Forward_iterator, typename _Function> +void _Parallel_for_each_forward_impl(_Forward_iterator& _First, const _Forward_iterator& _Last, const _Function& _Func, task_group& _Task_group) +{ + _Parallel_for_each_chunk(_First, _Last, _Func, _Task_group); + + // If there is a tail, push the tail + if (_First != _Last) + { + _Task_group.run( + [&_First, &_Last, &_Func, &_Task_group] + { + Concurrency::_Parallel_for_each_forward_impl(_First, _Last, _Func, _Task_group); + } + ); + } +} + +template <typename _Forward_iterator, typename _Function> +void _Parallel_for_each_impl(_Forward_iterator _First, const _Forward_iterator& _Last, const _Function& _Func, const auto_partitioner&, std::forward_iterator_tag) +{ + // Because this is a forward iterator, it is difficult to validate that _First comes before _Last, so + // it is up to the user to provide valid range. + if (_First != _Last) + { + task_group _Task_group; + + _Parallel_for_each_forward_impl(_First, _Last, _Func, _Task_group); + + _Task_group.wait(); + } +} + +template <typename _Random_iterator, typename _Index_type, typename _Function> +void _Parallel_for_each_partitioned_impl(const _Random_iterator& _First, _Index_type _Range_arg, _Index_type _Step, const _Function& _Func, const auto_partitioner& _Part) +{ + typedef _Parallel_chunk_helper<_Random_iterator, _Index_type, _Function, auto_partitioner, true> _Worker_class; + // Use the same function that schedules work for parallel for + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +template <typename _Random_iterator, typename _Index_type, typename _Function> +void _Parallel_for_each_partitioned_impl(const _Random_iterator& _First, _Index_type _Range_arg, _Index_type _Step, const _Function& _Func, const static_partitioner& _Part) +{ + typedef _Parallel_fixed_chunk_helper<_Random_iterator, _Index_type, _Function, static_partitioner, true> _Worker_class; + // Use the same function that schedules work for parallel for + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +template <typename _Random_iterator, typename _Index_type, typename _Function> +void _Parallel_for_each_partitioned_impl(const _Random_iterator& _First, _Index_type _Range_arg, _Index_type _Step, const _Function& _Func, const simple_partitioner& _Part) +{ + typedef _Parallel_fixed_chunk_helper<_Random_iterator, _Index_type, _Function, simple_partitioner, true> _Worker_class; + // Use the same function that schedules work for parallel for + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +template <typename _Random_iterator, typename _Index_type, typename _Function> +void _Parallel_for_each_partitioned_impl(const _Random_iterator& _First, _Index_type _Range_arg, _Index_type _Step, const _Function& _Func, affinity_partitioner& _Part) +{ + typedef _Parallel_localized_chunk_helper<_Random_iterator, _Index_type, _Function, true> _Worker_class; + // Use the same function that schedules work for parallel for + _Parallel_chunk_impl<_Worker_class>(_First, _Range_arg, _Step, _Func, _Part); +} + +template <typename _Random_iterator, typename _Function, typename _Partitioner> +void _Parallel_for_each_impl(const _Random_iterator& _First, const _Random_iterator& _Last, const _Function& _Func, _Partitioner&& _Part, std::random_access_iterator_tag) +{ + typedef typename std::iterator_traits<_Random_iterator>::difference_type _Index_type; + + // Exit early if there is nothing in the collection + if (_First >= _Last) + { + return; + } + + _Index_type _Range_size = _Last - _First; + + if (_Range_size == 1) + { + _Func(*_First); + } + else + { + _Index_type _Step = 1; + + _Parallel_for_each_partitioned_impl(_First, _Range_size, _Step, _Func, std::forward<_Partitioner>(_Part)); + } +} + +/// <summary> +/// <c>parallel_for_each</c> applies a specified function to each element within a range, in parallel. It is semantically +/// equivalent to the <c>for_each</c> function in the <c>std</c> namespace, except that iteration over the elements is +/// performed in parallel, and the order of iteration is unspecified. The argument <paramref name="_Func"/> must support +/// a function call operator of the form <c>operator()(T)</c> where the parameter <paramref name="T"/> is the item type +/// of the container being iterated over. +/// </summary> +/// <typeparam name="_Iterator"> +/// The type of the iterator being used to iterate over the container. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be applied to each element within the range. +/// </typeparam> +/// <param name="_First"> +/// An iterator addressing the position of the first element to be included in parallel iteration. +/// </param> +/// <param name="_Last"> +/// An iterator addressing the position one past the final element to be included in parallel iteration. +/// </param> +/// <param name="_Func"> +/// A user-defined function object that is applied to each element in the range. +/// </param> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overload without an explicit partitioner. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Iterator, typename _Function> +void parallel_for_each(_Iterator _First, _Iterator _Last, const _Function& _Func) +{ + parallel_for_each(_First, _Last, _Func, auto_partitioner()); +} + +/// <summary> +/// <c>parallel_for_each</c> applies a specified function to each element within a range, in parallel. It is semantically +/// equivalent to the <c>for_each</c> function in the <c>std</c> namespace, except that iteration over the elements is +/// performed in parallel, and the order of iteration is unspecified. The argument <paramref name="_Func"/> must support +/// a function call operator of the form <c>operator()(T)</c> where the parameter <paramref name="T"/> is the item type +/// of the container being iterated over. +/// </summary> +/// <typeparam name="_Iterator"> +/// The type of the iterator being used to iterate over the container. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the function that will be applied to each element within the range. +/// </typeparam> +/// <param name="_First"> +/// An iterator addressing the position of the first element to be included in parallel iteration. +/// </param> +/// <param name="_Last"> +/// An iterator addressing the position one past the final element to be included in parallel iteration. +/// </param> +/// <param name="_Func"> +/// A user-defined function object that is applied to each element in the range. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overload without an explicit partitioner. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Iterator, typename _Function, typename _Partitioner> +void parallel_for_each(_Iterator _First, _Iterator _Last, const _Function& _Func, _Partitioner&& _Part) +{ + _Trace_ppl_function(PPLParallelForeachEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_START); + _Parallel_for_each_impl(_First, _Last, _Func, std::forward<_Partitioner>(_Part), std::_Iter_cat(_First)); + _Trace_ppl_function(PPLParallelForeachEventGuid, _TRACE_LEVEL_INFORMATION, CONCRT_EVENT_END); +} + +// Disable C4180: qualifier applied to function type has no meaning; ignored +// Warning fires for passing Foo function pointer to parallel_for instead of &Foo. +#pragma warning(push) +#pragma warning(disable: 4180) + +/// <summary> +/// Computes the sum of all elements in a specified range by computing successive partial sums, or computes the result of successive partial +/// results similarly obtained from using a specified binary operation other than sum, in parallel. <c>parallel_reduce</c> is semantically similar to +/// <c>std::accumulate</c>, except that it requires the binary operation to be associative, and requires an identity value instead of an initial value. +/// </summary> +/// <typeparam name="_Forward_iterator"> +/// The iterator type of input range. +/// </typeparam> +/// <param name="_Begin"> +/// An input iterator addressing the first element in the range to be reduced. +/// </param> +/// <param name="_End"> +/// An input iterator addressing the element that is one position beyond the final element in the range to be reduced. +/// </param> +/// <param name="_Identity"> +/// The identity value <paramref name="_Identity"/> is of the same type as the result type of the reduction and also the <c>value_type</c> of the iterator +/// for the first and second overloads. For the third overload, the identity value must have the same type as the result type of the reduction, but can be +/// different from the <c>value_type</c> of the iterator. It must have an appropriate value such that the range reduction operator <paramref name="_Range_fun"/>, +/// when applied to a range of a single element of type <c>value_type</c> and the identity value, behaves like a type cast of the value from type +/// <c>value_type</c> to the identity type. +/// </param> +/// <returns> +/// The result of the reduction. +/// </returns> +/// <remarks> +/// To perform a parallel reduction, the function divides the range into chunks based on the number of workers available to the underlying +/// scheduler. The reduction takes place in two phases, the first phase performs a reduction within each chunk, and the second phase performs +/// a reduction between the partial results from each chunk. +/// <para>The first overload requires that the iterator's <c>value_type</c>, <c>T</c>, be the same as the identity value type as well as the reduction +/// result type. The element type T must provide the operator <c>T T::operator + (T)</c> to reduce elements in each chunk. The same operator is +/// used in the second phase as well.</para> +/// <para>The second overload also requires that the iterator's <c>value_type</c> be the same as the identity value type as well as the reduction +/// result type. The supplied binary operator <paramref name="_Sym_fun"/> is used in both reduction phases, with the identity value as the initial +/// value for the first phase.</para> +/// <para>For the third overload, the identity value type must be the same as the reduction result type, but the iterator's <c>value_type</c> may be +/// different from both. The range reduction function <paramref name="_Range_fun"/> is used in the first phase with the identity +/// value as the initial value, and the binary function <paramref name="_Sym_reduce_fun"/> is applied to sub results in the second phase.</para> +/// </remarks> +/**/ +template<typename _Forward_iterator> +inline typename std::iterator_traits<_Forward_iterator>::value_type parallel_reduce( + _Forward_iterator _Begin, _Forward_iterator _End, const typename std::iterator_traits<_Forward_iterator>::value_type &_Identity) +{ + return parallel_reduce(_Begin, _End, _Identity, std::plus<typename std::iterator_traits<_Forward_iterator>::value_type>()); +} + +/// <summary> +/// Computes the sum of all elements in a specified range by computing successive partial sums, or computes the result of successive partial +/// results similarly obtained from using a specified binary operation other than sum, in parallel. <c>parallel_reduce</c> is semantically similar to +/// <c>std::accumulate</c>, except that it requires the binary operation to be associative, and requires an identity value instead of an initial value. +/// </summary> +/// <typeparam name="_Forward_iterator"> +/// The iterator type of input range. +/// </typeparam> +/// <typeparam name="_Sym_reduce_fun"> +/// The type of the symmetric reduction function. This must be a function type with signature <c>_Reduce_type _Sym_fun(_Reduce_type, _Reduce_type)</c>, where +/// _Reduce_type is the same as the identity type and the result type of the reduction. For the third overload, this should be consistent +/// with the output type of <c>_Range_reduce_fun</c>. +/// </typeparam> +/// <param name="_Begin"> +/// An input iterator addressing the first element in the range to be reduced. +/// </param> +/// <param name="_End"> +/// An input iterator addressing the element that is one position beyond the final element in the range to be reduced. +/// </param> +/// <param name="_Identity"> +/// The identity value <paramref name="_Identity"/> is of the same type as the result type of the reduction and also the <c>value_type</c> of the iterator +/// for the first and second overloads. For the third overload, the identity value must have the same type as the result type of the reduction, but can be +/// different from the <c>value_type</c> of the iterator. It must have an appropriate value such that the range reduction operator <paramref name="_Range_fun"/>, +/// when applied to a range of a single element of type <c>value_type</c> and the identity value, behaves like a type cast of the value from type +/// <c>value_type</c> to the identity type. +/// </param> +/// <param name="_Sym_fun"> +/// The symmetric function that will be used in the second of the reduction. Refer to Remarks for more information. +/// </param> +/// <returns> +/// The result of the reduction. +/// </returns> +/// <remarks> +/// To perform a parallel reduction, the function divides the range into chunks based on the number of workers available to the underlying +/// scheduler. The reduction takes place in two phases, the first phase performs a reduction within each chunk, and the second phase performs +/// a reduction between the partial results from each chunk. +/// <para>The first overload requires that the iterator's <c>value_type</c>, <c>T</c>, be the same as the identity value type as well as the reduction +/// result type. The element type T must provide the operator <c>T T::operator + (T)</c> to reduce elements in each chunk. The same operator is +/// used in the second phase as well.</para> +/// <para>The second overload also requires that the iterator's <c>value_type</c> be the same as the identity value type as well as the reduction +/// result type. The supplied binary operator <paramref name="_Sym_fun"/> is used in both reduction phases, with the identity value as the initial +/// value for the first phase.</para> +/// <para>For the third overload, the identity value type must be the same as the reduction result type, but the iterator's <c>value_type</c> may be +/// different from both. The range reduction function <paramref name="_Range_fun"/> is used in the first phase with the identity +/// value as the initial value, and the binary function <paramref name="_Sym_reduce_fun"/> is applied to sub results in the second phase.</para> +/// </remarks> +/**/ +template<typename _Forward_iterator, typename _Sym_reduce_fun> +inline typename std::iterator_traits<_Forward_iterator>::value_type parallel_reduce(_Forward_iterator _Begin, _Forward_iterator _End, + const typename std::iterator_traits<_Forward_iterator>::value_type &_Identity, _Sym_reduce_fun _Sym_fun) +{ + typedef typename std::remove_cv<typename std::iterator_traits<_Forward_iterator>::value_type>::type _Reduce_type; + + return parallel_reduce(_Begin, _End, _Identity, + [_Sym_fun](_Forward_iterator _Begin, _Forward_iterator _End, _Reduce_type _Init)->_Reduce_type + { + while (_Begin != _End) + { + _Init = _Sym_fun(_Init, *_Begin++); + } + + return _Init; + }, + _Sym_fun); +} + +template <typename _Reduce_type, typename _Sub_function, typename _Combinable_type> +struct _Reduce_functor_helper; + +template<typename _Ty, typename _Sym_fun> +class _Order_combinable; + +/// <summary> +/// Computes the sum of all elements in a specified range by computing successive partial sums, or computes the result of successive partial +/// results similarly obtained from using a specified binary operation other than sum, in parallel. <c>parallel_reduce</c> is semantically similar to +/// <c>std::accumulate</c>, except that it requires the binary operation to be associative, and requires an identity value instead of an initial value. +/// </summary> +/// <typeparam name="_Reduce_type"> +/// The type that the input will reduce to, which can be different from the input element type. +/// The return value and identity value will has this type. +/// </typeparam> +/// <typeparam name="_Forward_iterator"> +/// The iterator type of input range. +/// </typeparam> +/// <typeparam name="_Range_reduce_fun"> +/// The type of the range reduction function. This must be a function type with signature <c>_Reduce_type _Range_fun(_Forward_iterator, _Forward_iterator, _Reduce_type)</c>, +/// _Reduce_type is the same as the identity type and the result type of the reduction. +/// </typeparam> +/// <typeparam name="_Sym_reduce_fun"> +/// The type of the symmetric reduction function. This must be a function type with signature <c>_Reduce_type _Sym_fun(_Reduce_type, _Reduce_type)</c>, where +/// _Reduce_type is the same as the identity type and the result type of the reduction. For the third overload, this should be consistent +/// with the output type of <c>_Range_reduce_fun</c>. +/// </typeparam> +/// <param name="_Begin"> +/// An input iterator addressing the first element in the range to be reduced. +/// </param> +/// <param name="_End"> +/// An input iterator addressing the element that is one position beyond the final element in the range to be reduced. +/// </param> +/// <param name="_Identity"> +/// The identity value <paramref name="_Identity"/> is of the same type as the result type of the reduction and also the <c>value_type</c> of the iterator +/// for the first and second overloads. For the third overload, the identity value must have the same type as the result type of the reduction, but can be +/// different from the <c>value_type</c> of the iterator. It must have an appropriate value such that the range reduction operator <paramref name="_Range_fun"/>, +/// when applied to a range of a single element of type <c>value_type</c> and the identity value, behaves like a type cast of the value from type +/// <c>value_type</c> to the identity type. +/// </param> +/// <param name="_Range_fun"> +/// The function that will be used in the first phase of the reduction. Refer to Remarks for more information. +/// </param> +/// <param name="_Sym_fun"> +/// The symmetric function that will be used in the second of the reduction. Refer to Remarks for more information. +/// </param> +/// <returns> +/// The result of the reduction. +/// </returns> +/// <remarks> +/// To perform a parallel reduction, the function divides the range into chunks based on the number of workers available to the underlying +/// scheduler. The reduction takes place in two phases, the first phase performs a reduction within each chunk, and the second phase performs +/// a reduction between the partial results from each chunk. +/// <para>The first overload requires that the iterator's <c>value_type</c>, <c>T</c>, be the same as the identity value type as well as the reduction +/// result type. The element type T must provide the operator <c>T T::operator + (T)</c> to reduce elements in each chunk. The same operator is +/// used in the second phase as well.</para> +/// <para>The second overload also requires that the iterator's <c>value_type</c> be the same as the identity value type as well as the reduction +/// result type. The supplied binary operator <paramref name="_Sym_fun"/> is used in both reduction phases, with the identity value as the initial +/// value for the first phase.</para> +/// <para>For the third overload, the identity value type must be the same as the reduction result type, but the iterator's <c>value_type</c> may be +/// different from both. The range reduction function <paramref name="_Range_fun"/> is used in the first phase with the identity +/// value as the initial value, and the binary function <paramref name="_Sym_reduce_fun"/> is applied to sub results in the second phase.</para> +/// </remarks> +/**/ +template<typename _Reduce_type, typename _Forward_iterator, typename _Range_reduce_fun, typename _Sym_reduce_fun> +inline _Reduce_type parallel_reduce(_Forward_iterator _Begin, _Forward_iterator _End, const _Reduce_type& _Identity, + const _Range_reduce_fun &_Range_fun, const _Sym_reduce_fun &_Sym_fun) +{ + typedef typename std::iterator_traits<_Forward_iterator>::value_type _Value_type; + + static_assert(!std::tr1::is_same<typename std::iterator_traits<_Forward_iterator>::iterator_category, std::input_iterator_tag>::value + && !std::tr1::is_same<typename std::iterator_traits<_Forward_iterator>::iterator_category, std::output_iterator_tag>::value, + "iterator can not be input_iterator or output_iterator."); + + return _Parallel_reduce_impl(_Begin, _End, + _Reduce_functor_helper<_Reduce_type, _Range_reduce_fun, + _Order_combinable<_Reduce_type, _Sym_reduce_fun>>(_Identity, _Range_fun, _Order_combinable<_Reduce_type, _Sym_reduce_fun>(_Sym_fun)), + typename std::iterator_traits<_Forward_iterator>::iterator_category()); +} + +// Ordered serial combinable object +template<typename _Ty, typename _Sym_fun> +class _Order_combinable +{ +public: + // Only write once, limited contention will be caused + struct _Bucket + { + // Allocate enough space in the Bucket to hold a value + char _Value[(sizeof(_Ty) / sizeof(char))]; + _Bucket * _Next; + + _Bucket(_Bucket *_N) + { + _Next = _N; + } + + void _Insert(_Bucket *_Item) + { + // No need to lock, only one thread will insert + _Item->_Next = _Next; + _Next = _Item; + } + + // Construct value in bucket + void _Put(const _Ty &_Cur) + { + new(reinterpret_cast<_Ty *>(&_Value)) _Ty(_Cur); + } + }; + +private: + const _Sym_fun &_M_fun; + size_t _M_number; + _Bucket *_M_root; + _Order_combinable &operator =(const _Order_combinable &other); + +public: + _Bucket *_Construct(_Bucket *_Par = 0) + { + _Bucket * _Ret = static_cast<_Bucket *>(Concurrency::Alloc(sizeof(_Bucket))); + return new(_Ret)_Bucket(_Par); + } + + _Order_combinable(const _Sym_fun &_Fun): _M_fun(_Fun) + { + _M_root = 0; + _M_number = 0; + } + + ~_Order_combinable() + { + while (_M_root) + { + _Bucket *_Cur = _M_root; + _M_root = _M_root->_Next; + reinterpret_cast<_Ty &>(_Cur->_Value).~_Ty(); + Concurrency::Free(_Cur); + } + } + + // Serially combine and release the list, return result + _Ty _Serial_combine_release() + { + _Ty _Ret(reinterpret_cast<_Ty &>(_M_root->_Value)); + _Bucket *_Cur = _M_root; + _M_root = _M_root->_Next; + + while (_M_root) + { + reinterpret_cast<_Ty &>(_Cur->_Value).~_Ty(); + Concurrency::Free(_Cur); + _Cur = _M_root; + _Ret = _M_fun(reinterpret_cast <_Ty &> (_Cur->_Value), _Ret); + _M_root = _M_root->_Next; + } + + reinterpret_cast<_Ty &>(_Cur->_Value).~_Ty(); + Concurrency::Free(_Cur); + + return _Ret; + } + + // allocate a bucket and push back to the list + _Bucket *_Unsafe_push_back() + { + return _M_root = _Construct(_M_root); + } +}; + +// Implementation for the parallel reduce +template <typename _Forward_iterator, typename _Function> +typename _Function::_Reduce_type _Parallel_reduce_impl(_Forward_iterator _First, const _Forward_iterator& _Last, const _Function& _Func, + std::forward_iterator_tag) +{ + // Because this is a forward iterator, it is difficult to validate that _First comes before _Last, so + // it is up to the user to provide valid range. + if (_First != _Last) + { + task_group _Task_group; + _Parallel_reduce_forward_executor(_First, _Last, _Func, _Task_group); + _Task_group.wait(); + return _Func._Combinable._Serial_combine_release(); + } + else + { + return _Func._Identity_value; + } +} + +template<typename _Forward_iterator, typename _Functor> +class _Parallel_reduce_fixed_worker; + +template <typename _Worker, typename _Random_iterator, typename _Function> +void _Parallel_reduce_random_executor(_Random_iterator _Begin, _Random_iterator _End, const _Function& _Fun); + +template <typename _Random_iterator, typename _Function> +typename _Function::_Reduce_type _Parallel_reduce_impl(_Random_iterator _First, _Random_iterator _Last, const _Function& _Func, + std::random_access_iterator_tag) +{ + typedef _Parallel_reduce_fixed_worker<_Random_iterator, _Function> _Worker_class; + + // Special case for 0, 1 element + if (_First >= _Last) + { + return _Func._Identity_value; + } + // Directly compute if size is too small + else if (_Last - _First <= 1) + { + _Worker_class(_First, _Last, _Func)(); + return _Func._Combinable._Serial_combine_release(); + } + else + { + // Use fixed ordered chunk partition to schedule works + _Parallel_reduce_random_executor<_Worker_class>(_First, _Last, _Func); + return _Func._Combinable._Serial_combine_release(); + } +} + +// Helper function assemble all functors +template <typename _Reduce_type, typename _Sub_function, typename _Combinable_type> +struct _Reduce_functor_helper +{ + const _Sub_function &_Sub_fun; + const _Reduce_type &_Identity_value; + + mutable _Combinable_type &_Combinable; + + typedef _Reduce_type _Reduce_type; + typedef typename _Combinable_type::_Bucket Bucket_type; + + _Reduce_functor_helper(const _Reduce_type &_Identity, const _Sub_function &_Sub_fun, _Combinable_type &&comb): + _Sub_fun(_Sub_fun), _Combinable(comb), _Identity_value(_Identity) + { + } + +private: + _Reduce_functor_helper &operator =(const _Reduce_functor_helper &other); +}; + +// All the code below is the worker without range stealing +template<typename _Forward_iterator, typename _Functor> +class _Parallel_reduce_fixed_worker +{ +public: + // The bucket allocation order will depend on the worker construction order + _Parallel_reduce_fixed_worker(_Forward_iterator _Begin, _Forward_iterator _End, const _Functor &_Fun): + _M_begin(_Begin), _M_end(_End), _M_fun(_Fun), _M_bucket(_M_fun._Combinable._Unsafe_push_back()) + { + } + + void operator ()() const + { + _M_bucket->_Put(_M_fun._Sub_fun(_M_begin, _M_end, _M_fun._Identity_value)); + } + +private: + const _Functor &_M_fun; + const _Forward_iterator _M_begin, _M_end; + typename _Functor::Bucket_type * const _M_bucket; + _Parallel_reduce_fixed_worker &operator =(const _Parallel_reduce_fixed_worker &other); +}; + +// the parallel worker executor for fixed iterator +// it will divide fixed number of chunks +// almost same as fixed parallel for, except keep the chunk dividing order +template <typename _Worker, typename _Random_iterator, typename _Function> +void _Parallel_reduce_random_executor(_Random_iterator _Begin, _Random_iterator _End, const _Function& _Fun) +{ + size_t _Cpu_num = static_cast<size_t>(Concurrency::details::_CurrentScheduler::_GetNumberOfVirtualProcessors()), _Size = _End - _Begin; + + structured_task_group _Tg; + Concurrency::details::_MallocaArrayHolder<task_handle<_Worker>> _Holder; + task_handle<_Worker> *_Tasks = _Holder._InitOnRawMalloca(_malloca(sizeof(task_handle<_Worker>) * (_Cpu_num - 1))); + + size_t _Begin_index = 0; + size_t _Step = _Size / _Cpu_num; + size_t _NumRemaining = _Size - _Step * _Cpu_num; + + for(size_t _I = 0; _I < _Cpu_num - 1; _I++) + { + size_t _Next = _Begin_index + _Step; + + // Add remaining to each chunk + if (_NumRemaining) + { + --_NumRemaining; + ++_Next; + } + + // New up a task_handle "in-place", in the array preallocated on the stack + new (_Tasks + _I) task_handle<_Worker>(_Worker(_Begin + _Begin_index, _Begin + _Next, _Fun)); + _Holder._IncrementConstructedElemsCount(); + + // Run each of the chunk _Tasks in parallel + _Tg.run(_Tasks[_I]); + _Begin_index = _Next; + } + + task_handle<_Worker> _Tail(_Worker(_Begin + _Begin_index, _End, _Fun)); + _Tg.run_and_wait(_Tail); +} + +// The parallel worker executor for forward iterators +// Divide chunks on the fly +template <typename _Forward_iterator, typename _Function, int _Default_worker_size, int _Default_chunk_size> +struct _Parallel_reduce_forward_executor_helper +{ + typedef _Parallel_reduce_fixed_worker<_Forward_iterator, _Function> _Worker_class; + mutable std::auto_ptr<task_handle<_Worker_class>> _Workers; + int _Worker_size; + + _Parallel_reduce_forward_executor_helper(_Forward_iterator &_First, _Forward_iterator _Last, const _Function& _Func): + _Workers(static_cast<task_handle<_Worker_class> *>(Concurrency::Alloc(sizeof(task_handle<_Worker_class>) * _Default_worker_size))) + { + _Worker_size = 0; + while (_Worker_size < _Default_worker_size && _First != _Last) + { + // Copy the range _Head + _Forward_iterator _Head = _First; + + // Read from forward iterator + for (size_t _I = 0; _I < _Default_chunk_size && _First != _Last; ++_I, ++_First) + { + // Body is empty + } + + // _First will be the end of current chunk + new (_Workers.get() + _Worker_size++) task_handle<_Worker_class>(_Worker_class(_Head, _First, _Func)); + } + } + + _Parallel_reduce_forward_executor_helper(const _Parallel_reduce_forward_executor_helper &_Other): + _Workers(_Other._Workers), _Worker_size(_Other._Worker_size) + { + } + + void operator ()() const + { + structured_task_group _Tg; + for(int _I = 0; _I < _Worker_size; _I++) + { + _Tg.run(_Workers.get()[_I]); + } + _Tg.wait(); + } + + ~_Parallel_reduce_forward_executor_helper() + { + if (_Workers.get()) + { + for (int _I = 0; _I < _Worker_size; _I++) + { + _Workers.get()[_I].~task_handle<_Worker_class>(); + } + Concurrency::Free(_Workers.release()); + } + } +}; + +template <typename _Forward_iterator, typename _Function> +void _Parallel_reduce_forward_executor(_Forward_iterator _First, _Forward_iterator _Last, const _Function& _Func, task_group& _Task_group) +{ + const static int _Internal_worker_number = 1024, _Default_chunk_size = 512; + typedef _Parallel_reduce_fixed_worker<_Forward_iterator, _Function> _Worker_class; + + structured_task_group _Worker_group; + Concurrency::details::_MallocaArrayHolder<task_handle<_Worker_class>> _Holder; + task_handle<_Worker_class>* _Workers = _Holder._InitOnRawMalloca(_malloca(_Internal_worker_number * sizeof(task_handle<_Worker_class>))); + + // Start execution first + int _Index = 0; + while (_Index < _Internal_worker_number && _First != _Last) + { + // Copy the range _Head + _Forward_iterator _Head = _First; + + // Read from forward iterator + for (size_t _I = 0; _I < _Default_chunk_size && _First != _Last; ++_I, ++_First) + { + // Body is empty + }; + + // Create a new task, _First is range _End + new (_Workers + _Index) task_handle<_Worker_class>(_Worker_class(_Head, _First, _Func)); + _Holder._IncrementConstructedElemsCount(); + _Worker_group.run(_Workers[_Index]); + ++_Index; + } + + // Divide and append the left + while (_First != _Last) + { + _Task_group.run(_Parallel_reduce_forward_executor_helper<_Forward_iterator, _Function, _Internal_worker_number, _Default_chunk_size>(_First, _Last, _Func)); + } + + _Worker_group.wait(); +} + +#pragma warning(pop) + + +// Disable C4180: qualifier applied to function type has no meaning; ignored +// Warning fires for passing Foo function pointer to parallel_for instead of &Foo. +#pragma warning(push) +#pragma warning(disable: 4180) + +// +// Dispatch the execution and handle the condition that all of the iterators are random access +// +template<typename _Any_input_traits, typename _Any_output_traits> +struct _Unary_transform_impl_helper +{ + template<typename _Input_iterator, typename _Output_iterator, typename _Unary_operator> + static void _Parallel_transform_unary_impl(_Input_iterator _Begin, _Input_iterator _End, _Output_iterator& _Result, const _Unary_operator& _Unary_op, const auto_partitioner&) + { + task_group _Tg; + _Parallel_transform_unary_impl2(_Begin, _End, _Result, _Unary_op, _Tg); + _Tg.wait(); + } +}; + +template<> +struct _Unary_transform_impl_helper<std::random_access_iterator_tag, std::random_access_iterator_tag> +{ + template<typename _Random_input_iterator, typename _Random_output_iterator, typename _Unary_operator, typename _Partitioner> + static void _Parallel_transform_unary_impl(_Random_input_iterator _Begin, _Random_input_iterator _End, + _Random_output_iterator& _Result, const _Unary_operator& _Unary_op, _Partitioner&& _Part) + { + if (_Begin < _End) + { + Concurrency::_Parallel_for_impl(static_cast<size_t>(0), static_cast<size_t>(_End - _Begin), static_cast<size_t>(1), + [_Begin, &_Result, &_Unary_op](size_t _Index) + { + _Result[_Index] = _Unary_op(_Begin[_Index]); + }, + std::forward<_Partitioner>(_Part)); + _Result += _End - _Begin; + } + } +}; + +template<typename _Any_input_traits1, typename _Any_input_traits2, typename _Any_output_traits> +struct _Binary_transform_impl_helper +{ + + template<typename _Input_iterator1, typename _Input_iterator2, typename _Output_iterator, typename _Binary_operator> + static void _Parallel_transform_binary_impl(_Input_iterator1 _Begin1, _Input_iterator1 _End1, _Input_iterator2 _Begin2, + _Output_iterator& _Result, const _Binary_operator& _Binary_op, const auto_partitioner&) + { + task_group _Tg; + _Parallel_transform_binary_impl2(_Begin1, _End1, _Begin2, _Result, _Binary_op, _Tg); + _Tg.wait(); + } +}; + +template<> +struct _Binary_transform_impl_helper<std::random_access_iterator_tag, std::random_access_iterator_tag, std::random_access_iterator_tag> +{ + template<typename _Random_input_iterator1, typename _Random_input_iterator2, typename _Random_output_iterator, typename _Binary_operator, typename _Partitioner> + static void _Parallel_transform_binary_impl(_Random_input_iterator1 _Begin1, _Random_input_iterator1 _End1, + _Random_input_iterator2 _Begin2, _Random_output_iterator& _Result, const _Binary_operator& _Binary_op, _Partitioner&& _Part) + { + if (_Begin1 < _End1) + { + Concurrency::_Parallel_for_impl(static_cast<size_t>(0), static_cast<size_t>(_End1 - _Begin1), static_cast<size_t>(1), + [_Begin1, _Begin2, &_Result, &_Binary_op](size_t _Index) + { + _Result[_Index] = _Binary_op(_Begin1[_Index], _Begin2[_Index]); + }, + std::forward<_Partitioner>(_Part)); + _Result += _End1 - _Begin1; + } + } +}; + +// +// The implementation for at least one of the iterator is forward iterator +// +template <typename _Forward_iterator, typename _Iterator_kind> +class _Iterator_helper +{ +public: + static const size_t _Size = 1024; + typedef typename std::iterator_traits<_Forward_iterator>::value_type value_type; + + _Iterator_helper() + { + static_assert(!std::is_same<_Iterator_kind, std::input_iterator_tag>::value + && !std::is_same<_Iterator_kind, std::output_iterator_tag>::value, + "iterator can not be input_iterator or output_iterator."); + } + + size_t _Populate(_Forward_iterator& _First, _Forward_iterator _Last) + { + size_t _Length = 0; + static_assert(std::is_lvalue_reference<decltype(*_First)>::value, "lvalue required for forward iterator operator *"); + + for (size_t _Index=0; (_Index < _Size) && (_First != _Last); _Index++) + { + // We only support l-value here, so it's safe + _M_element_array[_Length++] = &(*_First++); + } + + return _Length; + } + + void _Populate(_Forward_iterator& _First, size_t _Length) + { + for (size_t _Index=0; _Index < _Length; _Index++) + { + _M_element_array[_Index] = &(*_First++); + } + } + + void _Store(const value_type& _Elem, size_t _Index) const + { + *(_M_element_array[_Index]) = _Elem; + } + + typename std::iterator_traits<_Forward_iterator>::reference _Load(size_t _Index) const + { + return *(_M_element_array[_Index]); + } + +private: + typename std::iterator_traits<_Forward_iterator>::pointer _M_element_array[_Size]; +}; + +template <typename _Random_iterator> +class _Iterator_helper<_Random_iterator, std::random_access_iterator_tag> +{ +public: + static const size_t _Size = 1024; + typedef typename std::iterator_traits<_Random_iterator>::value_type value_type; + + _Iterator_helper() + { + } + + size_t _Populate(_Random_iterator& _First, _Random_iterator _Last) + { + typename std::iterator_traits<_Random_iterator>::difference_type _Range_size = _Last - _First; + typename std::iterator_traits<_Random_iterator>::difference_type _Sized = _Size; + _M_first = _First; + + if (_Range_size > _Sized) + { + _First += _Size; + return _Size; + } + else + { + _First += _Range_size; + return static_cast<size_t>(_Range_size); + } + } + + void _Populate(_Random_iterator& _First, size_t _Length) + { + _M_first = _First; + _First += _Length; + } + + void _Store(const value_type& _Elem, size_t _Index) const + { + _M_first[_Index] = _Elem; + } + + typename std::iterator_traits<_Random_iterator>::reference _Load(size_t _Index) const + { + // We only support l-value here + return _M_first[_Index]; + } + +private: + _Random_iterator _M_first; +}; + +template <typename _Input_iterator1, typename _Input_iterator2, typename _Output_iterator, typename _Binary_operator> +class _Parallel_transform_binary_helper +{ +public: + _Parallel_transform_binary_helper(_Input_iterator1& _First1, _Input_iterator1 _Last1, _Input_iterator2& _First2, + _Output_iterator& _Result, const _Binary_operator& _Binary_op) : + _M_binary_op(_Binary_op), _M_len(0) + { + _M_len = _M_input_helper1._Populate(_First1, _Last1); + _M_input_helper2._Populate(_First2, _M_len); + _M_output_helper._Populate(_Result, _M_len); + } + + void operator()() const + { + // Invoke parallel_for on the batched up array of elements + Concurrency::_Parallel_for_impl(static_cast<size_t>(0), _M_len, static_cast<size_t>(1), + [this] (size_t _Index) + { + _M_output_helper._Store(_M_binary_op(_M_input_helper1._Load(_Index), _M_input_helper2._Load(_Index)), _Index); + }); + } + +private: + + _Iterator_helper<_Input_iterator1, typename std::iterator_traits<_Input_iterator1>::iterator_category> _M_input_helper1; + _Iterator_helper<_Input_iterator2, typename std::iterator_traits<_Input_iterator2>::iterator_category> _M_input_helper2; + _Iterator_helper<_Output_iterator, typename std::iterator_traits<_Output_iterator>::iterator_category> _M_output_helper; + const _Binary_operator& _M_binary_op; + size_t _M_len; + + _Parallel_transform_binary_helper const & operator=(_Parallel_transform_binary_helper const&); // no assignment operator +}; + +template <typename _Input_iterator1, typename _Input_iterator2, typename _Output_iterator, typename _Binary_operator> +void _Parallel_transform_binary_impl2(_Input_iterator1 _First1, _Input_iterator1 _Last1, _Input_iterator2 _First2, _Output_iterator &_Result, + const _Binary_operator& _Binary_op, task_group& _Tg) +{ + // This functor will be copied on the heap and will execute the chunk in parallel + { + _Parallel_transform_binary_helper<_Input_iterator1, _Input_iterator2, _Output_iterator, _Binary_operator> functor(_First1, _Last1, _First2, _Result, _Binary_op); + _Tg.run(functor); + } + + // If there is a tail, push the tail + if (_First1 != _Last1) + { + _Tg.run( + [=, &_Result, &_Binary_op, &_Tg] + { + _Parallel_transform_binary_impl2(_First1, _Last1, _First2, _Result, _Binary_op, _Tg); + }); + } +} + +template <typename _Input_iterator, typename _Output_iterator, typename _Unary_operator> +class _Parallel_transform_unary_helper +{ +public: + _Parallel_transform_unary_helper(_Input_iterator& _First, _Input_iterator _Last, _Output_iterator &_Result, const _Unary_operator& _Unary_op) : + _M_unary_op(_Unary_op), _M_len(0) + { + _M_len = _M_input_helper._Populate(_First, _Last); + _M_output_helper._Populate(_Result, _M_len); + } + + void operator()() const + { + // Invoke parallel_for on the batched up array of elements + Concurrency::_Parallel_for_impl(static_cast<size_t>(0), _M_len, static_cast<size_t>(1), + [this] (size_t _Index) + { + _M_output_helper._Store(_M_unary_op(_M_input_helper._Load(_Index)), _Index); + }); + } + +private: + + _Iterator_helper<_Input_iterator, typename std::iterator_traits<_Input_iterator>::iterator_category> _M_input_helper; + _Iterator_helper<_Output_iterator, typename std::iterator_traits<_Output_iterator>::iterator_category> _M_output_helper; + const _Unary_operator& _M_unary_op; + size_t _M_len; + + _Parallel_transform_unary_helper const & operator=(_Parallel_transform_unary_helper const&); // no assignment operator +}; + +template <typename _Input_iterator, typename _Output_iterator, typename _Unary_operator> +void _Parallel_transform_unary_impl2(_Input_iterator _First, _Input_iterator _Last, _Output_iterator &_Result, + const _Unary_operator& _Unary_op, task_group& _Tg) +{ + // This functor will be copied on the heap and will execute the chunk in parallel + { + _Parallel_transform_unary_helper<_Input_iterator, _Output_iterator, _Unary_operator> functor(_First, _Last, _Result, _Unary_op); + _Tg.run(functor); + } + + // If there is a tail, push the tail + if (_First != _Last) + { + _Tg.run( + [=, &_Result, &_Unary_op, &_Tg] + { + _Parallel_transform_unary_impl2(_First, _Last, _Result, _Unary_op, _Tg); + }); + } +} + +template <typename _Input_iterator, typename _Output_iterator, typename _Unary_operator, typename _Partitioner> +_Output_iterator _Parallel_transform_unary_impl(_Input_iterator _First, _Input_iterator _Last, _Output_iterator _Result, const _Unary_operator& _Unary_op, _Partitioner&& _Part) +{ + typedef typename std::iterator_traits<_Input_iterator>::iterator_category _Input_iterator_type; + typedef typename std::iterator_traits<_Output_iterator>::iterator_category _Output_iterator_type; + + if (_First != _Last) + { + _Unary_transform_impl_helper<_Input_iterator_type, _Output_iterator_type> + ::_Parallel_transform_unary_impl(_First, _Last, _Result, _Unary_op, std::forward<_Partitioner>(_Part)); + } + + return _Result; +} + +/// <summary> +/// Applies a specified function object to each element in a source range, or to a pair of elements from two source ranges, +/// and copies the return values of the function object into a destination range, in parallel. This functional is semantically +/// equivalent to <c>std::transform</c>. +/// </summary> +/// <typeparam name="_Input_iterator1"> +/// The type of the first or only input iterator. +/// </typeparam> +/// <typeparam name="_Output_iterator"> +/// The type of the output iterator. +/// </typeparam> +/// <typeparam name="_Unary_operator"> +/// The type of the unary functor to be executed on each element in the input range. +/// </typeparam> +/// <param name="_First1"> +/// An input iterator addressing the position of the first element in the first or only source range to be operated on. +/// </param> +/// <param name="_Last1"> +/// An input iterator addressing the position one past the final element in the first or only source range to be operated on. +/// </param> +/// <param name="_Result"> +/// An output iterator addressing the position of the first element in the destination range. +/// </param> +/// <param name="_Unary_op"> +/// A user-defined unary function object that is applied to each element in the source range. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <returns> +/// An output iterator addressing the position one past the final element in the destination range that is receiving the output elements +/// transformed by the function object. +/// </returns> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overloads without an explicit partitioner argument. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>The overloads that take the argument <paramref name="_Unary_op"/> transform the input range into the output range by applying +/// the unary functor to each element in the input range. <paramref name="_Unary_op"/> must support the function call operator with signature +/// <c>operator()(T)</c> where <c>T</c> is the value type of the range being iterated over.</para> +/// <para>The overloads that take the argument <paramref name="_Binary_op"/> transform two input ranges into the output range by applying the +/// binary functor to one element from the first input range and one element from the second input range. <paramref name="_Binary_op"/> must support +/// the function call operator with signature <c>operator()(T, U)</c> where <c>T</c>, <c>U</c> are value types of the two input iterators.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Input_iterator1, typename _Output_iterator, typename _Unary_operator> +_Output_iterator parallel_transform(_Input_iterator1 _First1, _Input_iterator1 _Last1, _Output_iterator _Result, const _Unary_operator& _Unary_op, const auto_partitioner& _Part = auto_partitioner()) +{ + return _Parallel_transform_unary_impl(_First1, _Last1, _Result, _Unary_op, _Part); +} + +/// <summary> +/// Applies a specified function object to each element in a source range, or to a pair of elements from two source ranges, +/// and copies the return values of the function object into a destination range, in parallel. This functional is semantically +/// equivalent to <c>std::transform</c>. +/// </summary> +/// <typeparam name="_Input_iterator1"> +/// The type of the first or only input iterator. +/// </typeparam> +/// <typeparam name="_Output_iterator"> +/// The type of the output iterator. +/// </typeparam> +/// <typeparam name="_Unary_operator"> +/// The type of the unary functor to be executed on each element in the input range. +/// </typeparam> +/// <param name="_First1"> +/// An input iterator addressing the position of the first element in the first or only source range to be operated on. +/// </param> +/// <param name="_Last1"> +/// An input iterator addressing the position one past the final element in the first or only source range to be operated on. +/// </param> +/// <param name="_Result"> +/// An output iterator addressing the position of the first element in the destination range. +/// </param> +/// <param name="_Unary_op"> +/// A user-defined unary function object that is applied to each element in the source range. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <returns> +/// An output iterator addressing the position one past the final element in the destination range that is receiving the output elements +/// transformed by the function object. +/// </returns> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overloads without an explicit partitioner argument. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>The overloads that take the argument <paramref name="_Unary_op"/> transform the input range into the output range by applying +/// the unary functor to each element in the input range. <paramref name="_Unary_op"/> must support the function call operator with signature +/// <c>operator()(T)</c> where <c>T</c> is the value type of the range being iterated over.</para> +/// <para>The overloads that take the argument <paramref name="_Binary_op"/> transform two input ranges into the output range by applying the +/// binary functor to one element from the first input range and one element from the second input range. <paramref name="_Binary_op"/> must support +/// the function call operator with signature <c>operator()(T, U)</c> where <c>T</c>, <c>U</c> are value types of the two input iterators.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Input_iterator1, typename _Output_iterator, typename _Unary_operator> +_Output_iterator parallel_transform(_Input_iterator1 _First1, _Input_iterator1 _Last1, _Output_iterator _Result, const _Unary_operator& _Unary_op, const static_partitioner& _Part) +{ + return _Parallel_transform_unary_impl(_First1, _Last1, _Result, _Unary_op, _Part); +} + +/// <summary> +/// Applies a specified function object to each element in a source range, or to a pair of elements from two source ranges, +/// and copies the return values of the function object into a destination range, in parallel. This functional is semantically +/// equivalent to <c>std::transform</c>. +/// </summary> +/// <typeparam name="_Input_iterator1"> +/// The type of the first or only input iterator. +/// </typeparam> +/// <typeparam name="_Output_iterator"> +/// The type of the output iterator. +/// </typeparam> +/// <typeparam name="_Unary_operator"> +/// The type of the unary functor to be executed on each element in the input range. +/// </typeparam> +/// <param name="_First1"> +/// An input iterator addressing the position of the first element in the first or only source range to be operated on. +/// </param> +/// <param name="_Last1"> +/// An input iterator addressing the position one past the final element in the first or only source range to be operated on. +/// </param> +/// <param name="_Result"> +/// An output iterator addressing the position of the first element in the destination range. +/// </param> +/// <param name="_Unary_op"> +/// A user-defined unary function object that is applied to each element in the source range. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <returns> +/// An output iterator addressing the position one past the final element in the destination range that is receiving the output elements +/// transformed by the function object. +/// </returns> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overloads without an explicit partitioner argument. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>The overloads that take the argument <paramref name="_Unary_op"/> transform the input range into the output range by applying +/// the unary functor to each element in the input range. <paramref name="_Unary_op"/> must support the function call operator with signature +/// <c>operator()(T)</c> where <c>T</c> is the value type of the range being iterated over.</para> +/// <para>The overloads that take the argument <paramref name="_Binary_op"/> transform two input ranges into the output range by applying the +/// binary functor to one element from the first input range and one element from the second input range. <paramref name="_Binary_op"/> must support +/// the function call operator with signature <c>operator()(T, U)</c> where <c>T</c>, <c>U</c> are value types of the two input iterators.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Input_iterator1, typename _Output_iterator, typename _Unary_operator> +_Output_iterator parallel_transform(_Input_iterator1 _First1, _Input_iterator1 _Last1, _Output_iterator _Result, const _Unary_operator& _Unary_op, const simple_partitioner& _Part) +{ + return _Parallel_transform_unary_impl(_First1, _Last1, _Result, _Unary_op, _Part); +} + +/// <summary> +/// Applies a specified function object to each element in a source range, or to a pair of elements from two source ranges, +/// and copies the return values of the function object into a destination range, in parallel. This functional is semantically +/// equivalent to <c>std::transform</c>. +/// </summary> +/// <typeparam name="_Input_iterator1"> +/// The type of the first or only input iterator. +/// </typeparam> +/// <typeparam name="_Output_iterator"> +/// The type of the output iterator. +/// </typeparam> +/// <typeparam name="_Unary_operator"> +/// The type of the unary functor to be executed on each element in the input range. +/// </typeparam> +/// <param name="_First1"> +/// An input iterator addressing the position of the first element in the first or only source range to be operated on. +/// </param> +/// <param name="_Last1"> +/// An input iterator addressing the position one past the final element in the first or only source range to be operated on. +/// </param> +/// <param name="_Result"> +/// An output iterator addressing the position of the first element in the destination range. +/// </param> +/// <param name="_Unary_op"> +/// A user-defined unary function object that is applied to each element in the source range. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <returns> +/// An output iterator addressing the position one past the final element in the destination range that is receiving the output elements +/// transformed by the function object. +/// </returns> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overloads without an explicit partitioner argument. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>The overloads that take the argument <paramref name="_Unary_op"/> transform the input range into the output range by applying +/// the unary functor to each element in the input range. <paramref name="_Unary_op"/> must support the function call operator with signature +/// <c>operator()(T)</c> where <c>T</c> is the value type of the range being iterated over.</para> +/// <para>The overloads that take the argument <paramref name="_Binary_op"/> transform two input ranges into the output range by applying the +/// binary functor to one element from the first input range and one element from the second input range. <paramref name="_Binary_op"/> must support +/// the function call operator with signature <c>operator()(T, U)</c> where <c>T</c>, <c>U</c> are value types of the two input iterators.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Input_iterator1, typename _Output_iterator, typename _Unary_operator> +_Output_iterator parallel_transform(_Input_iterator1 _First1, _Input_iterator1 _Last1, _Output_iterator _Result, const _Unary_operator& _Unary_op, affinity_partitioner& _Part) +{ + return _Parallel_transform_unary_impl(_First1, _Last1, _Result, _Unary_op, _Part); +} + +/// <summary> +/// Applies a specified function object to each element in a source range, or to a pair of elements from two source ranges, +/// and copies the return values of the function object into a destination range, in parallel. This functional is semantically +/// equivalent to <c>std::transform</c>. +/// </summary> +/// <typeparam name="_Input_iterator1"> +/// The type of the first or only input iterator. +/// </typeparam> +/// <typeparam name="_Input_iterator2"> +/// The type of second input iterator. +/// </typeparam> +/// <typeparam name="_Output_iterator"> +/// The type of the output iterator. +/// </typeparam> +/// <typeparam name="_Binary_operator"> +/// The type of the binary functor executed pairwise on elements from the two source ranges. +/// </typeparam> +/// <param name="_First1"> +/// An input iterator addressing the position of the first element in the first or only source range to be operated on. +/// </param> +/// <param name="_Last1"> +/// An input iterator addressing the position one past the final element in the first or only source range to be operated on. +/// </param> +/// <param name="_First2"> +/// An input iterator addressing the position of the first element in the second source range to be operated on. +/// </param> +/// <param name="_Result"> +/// An output iterator addressing the position of the first element in the destination range. +/// </param> +/// <param name="_Binary_op"> +/// A user-defined binary function object that is applied pairwise, in a forward order, to the two source ranges. +/// </param> +/// <param name="_Part"> +/// A reference to the partitioner object. The argument can be one of +/// <c>const</c> <see ref="auto_partitioner Class">auto_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="static_partitioner Class">static_partitioner</see><c>&</c>, +/// <c>const</c> <see ref="simple_partitioner Class">simple_partitioner</see><c>&</c> or +/// <see ref="affinity_partitioner Class">affinity_partitioner</see><c>&</c> +/// If an <see ref="affinity_partitioner Class">affinity_partitioner</see> object is used, the reference must be a non-const l-value reference, +/// so that the algorithm can store state for future loops to re-use. +/// </param> +/// <returns> +/// An output iterator addressing the position one past the final element in the destination range that is receiving the output elements +/// transformed by the function object. +/// </returns> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overloads without an explicit partitioner argument. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>The overloads that take the argument <paramref name="_Unary_op"/> transform the input range into the output range by applying +/// the unary functor to each element in the input range. <paramref name="_Unary_op"/> must support the function call operator with signature +/// <c>operator()(T)</c> where <c>T</c> is the value type of the range being iterated over.</para> +/// <para>The overloads that take the argument <paramref name="_Binary_op"/> transform two input ranges into the output range by applying the +/// binary functor to one element from the first input range and one element from the second input range. <paramref name="_Binary_op"/> must support +/// the function call operator with signature <c>operator()(T, U)</c> where <c>T</c>, <c>U</c> are value types of the two input iterators.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Input_iterator1, typename _Input_iterator2, typename _Output_iterator, typename _Binary_operator, typename _Partitioner> +_Output_iterator parallel_transform(_Input_iterator1 _First1, _Input_iterator1 _Last1, _Input_iterator2 _First2, + _Output_iterator _Result, const _Binary_operator& _Binary_op, _Partitioner&& _Part) +{ + typedef typename std::iterator_traits<_Input_iterator1>::iterator_category _Input_iterator_type1; + typedef typename std::iterator_traits<_Input_iterator2>::iterator_category _Input_iterator_type2; + typedef typename std::iterator_traits<_Output_iterator>::iterator_category _Output_iterator_type; + + if (_First1 != _Last1) + { + _Binary_transform_impl_helper<_Input_iterator_type1, _Input_iterator_type2, _Output_iterator_type> + ::_Parallel_transform_binary_impl(_First1, _Last1, _First2, _Result, _Binary_op, std::forward<_Partitioner>(_Part)); + } + + return _Result; +} + +/// <summary> +/// Applies a specified function object to each element in a source range, or to a pair of elements from two source ranges, +/// and copies the return values of the function object into a destination range, in parallel. This functional is semantically +/// equivalent to <c>std::transform</c>. +/// </summary> +/// <typeparam name="_Input_iterator1"> +/// The type of the first or only input iterator. +/// </typeparam> +/// <typeparam name="_Input_iterator2"> +/// The type of second input iterator. +/// </typeparam> +/// <typeparam name="_Output_iterator"> +/// The type of the output iterator. +/// </typeparam> +/// <typeparam name="_Binary_operator"> +/// The type of the binary functor executed pairwise on elements from the two source ranges. +/// </typeparam> +/// <param name="_First1"> +/// An input iterator addressing the position of the first element in the first or only source range to be operated on. +/// </param> +/// <param name="_Last1"> +/// An input iterator addressing the position one past the final element in the first or only source range to be operated on. +/// </param> +/// <param name="_First2"> +/// An input iterator addressing the position of the first element in the second source range to be operated on. +/// </param> +/// <param name="_Result"> +/// An output iterator addressing the position of the first element in the destination range. +/// </param> +/// <param name="_Binary_op"> +/// A user-defined binary function object that is applied pairwise, in a forward order, to the two source ranges. +/// </param> +/// <returns> +/// An output iterator addressing the position one past the final element in the destination range that is receiving the output elements +/// transformed by the function object. +/// </returns> +/// <remarks> +/// <see ref="auto_partitioner Class">auto_partitioner</see> will be used for the overloads without an explicit partitioner argument. +/// <para>For iterators that do not support random access, only <see ref="auto_partitioner Class">auto_partitioner</see> is supported.</para> +/// <para>The overloads that take the argument <paramref name="_Unary_op"/> transform the input range into the output range by applying +/// the unary functor to each element in the input range. <paramref name="_Unary_op"/> must support the function call operator with signature +/// <c>operator()(T)</c> where <c>T</c> is the value type of the range being iterated over.</para> +/// <para>The overloads that take the argument <paramref name="_Binary_op"/> transform two input ranges into the output range by applying the +/// binary functor to one element from the first input range and one element from the second input range. <paramref name="_Binary_op"/> must support +/// the function call operator with signature <c>operator()(T, U)</c> where <c>T</c>, <c>U</c> are value types of the two input iterators.</para> +/// <para>For more information, see <see cref="Parallel Algorithms"/>.</para> +/// </remarks> +/**/ +template <typename _Input_iterator1, typename _Input_iterator2, typename _Output_iterator, typename _Binary_operator> +_Output_iterator parallel_transform(_Input_iterator1 _First1, _Input_iterator1 _Last1, _Input_iterator2 _First2, + _Output_iterator _Result, const _Binary_operator& _Binary_op) +{ + return parallel_transform(_First1, _Last1, _First2, _Result, _Binary_op, auto_partitioner()); +} + +#pragma warning(pop) + +#pragma warning(push) +// object allocated on the heap may not be aligned 64 +#pragma warning(disable: 4316) + +/// <summary> +/// The <c>combinable<T></c> object is intended to provide thread-private copies of data, to perform lock-free +/// thread-local sub-computations during parallel algorithms. At the end of the parallel operation, the +/// thread-private sub-computations can then be merged into a final result. This class can be used instead of +/// a shared variable, and can result in a performance improvement if there would otherwise be a lot of +/// contention on that shared variable. +/// </summary> +/// <typeparam name="_Ty"> +/// The data type of the final merged result. The type must have a copy constructor and a default constructor. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Parallel Containers and Objects"/>. +/// </remarks> +/**/ +template<typename _Ty> +class combinable +{ +private: + +// Disable warning C4324: structure was padded due to __declspec(align()) +// This padding is expected and necessary. +#pragma warning(push) +#pragma warning(disable: 4324) + __declspec(align(64)) + struct _Node + { + unsigned long _M_key; + _Ty _M_value; + _Node* _M_chain; + + _Node(unsigned long _Key, _Ty _InitialValue) + : _M_key(_Key), _M_value(_InitialValue), _M_chain(NULL) + { + } + }; +#pragma warning(pop) + + static _Ty _DefaultInit() + { + return _Ty(); + } + +public: + /// <summary> + /// Constructs a new <c>combinable</c> object. + /// </summary> + /// <remarks> + /// <para>The first constructor initializes new elements with the default constructor for the type <paramref name="_Ty"/>.</para> + /// <para>The second constructor initializes new elements using the initialization functor supplied as the + /// <paramref name="_FnInitialize"/> parameter.</para> + /// <para>The third constructor is the copy constructor.</para> + /// </remarks> + /// <seealso cref="Parallel Containers and Objects"/> + /**/ + combinable() + : _M_fnInitialize(_DefaultInit) + { + _InitNew(); + } + + /// <summary> + /// Constructs a new <c>combinable</c> object. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the initialization functor object. + /// </typeparam> + /// <param name="_FnInitialize"> + /// A function which will be called to initialize each new thread-private value of the type <paramref name="_Ty"/>. + /// It must support a function call operator with the signature <c>_Ty ()</c>. + /// </param> + /// <remarks> + /// <para>The first constructor initializes new elements with the default constructor for the type <paramref name="_Ty"/>.</para> + /// <para>The second constructor initializes new elements using the initialization functor supplied as the + /// <paramref name="_FnInitialize"/> parameter.</para> + /// <para>The third constructor is the copy constructor.</para> + /// </remarks> + /// <seealso cref="Parallel Containers and Objects"/> + /**/ + template <typename _Function> + explicit combinable(_Function _FnInitialize) + : _M_fnInitialize(_FnInitialize) + { + _InitNew(); + } + + /// <summary> + /// Constructs a new <c>combinable</c> object. + /// </summary> + /// <param name="_Copy"> + /// An existing <c>combinable</c> object to be copied into this one. + /// </param> + /// <remarks> + /// <para>The first constructor initializes new elements with the default constructor for the type <paramref name="_Ty"/>.</para> + /// <para>The second constructor initializes new elements using the initialization functor supplied as the + /// <paramref name="_FnInitialize"/> parameter.</para> + /// <para>The third constructor is the copy constructor.</para> + /// </remarks> + /// <seealso cref="Parallel Containers and Objects"/> + /**/ + combinable(const combinable& _Copy) + : _M_size(_Copy._M_size), _M_fnInitialize(_Copy._M_fnInitialize) + { + _InitCopy(_Copy); + } + + /// <summary> + /// Assigns to a <c>combinable</c> object from another <c>combinable</c> object. + /// </summary> + /// <param name="_Copy"> + /// An existing <c>combinable</c> object to be copied into this one. + /// </param> + /// <returns> + /// A reference to this <c>combinable</c> object. + /// </returns> + /**/ + combinable& operator=(const combinable& _Copy) + { + clear(); + delete [] _M_buckets; + _M_fnInitialize = _Copy._M_fnInitialize; + _M_size = _Copy._M_size; + _InitCopy(_Copy); + + return *this; + } + + /// <summary> + /// Destroys a <c>combinable</c> object. + /// </summary> + /**/ + ~combinable() + { + clear(); + delete [] _M_buckets; + } + + /// <summary> + /// Returns a reference to the thread-private sub-computation. + /// </summary> + /// <returns> + /// A reference to the thread-private sub-computation. + /// </returns> + /// <seealso cref="Parallel Containers and Objects"/> + /**/ + _Ty& local() + { + unsigned long _Key = Concurrency::details::platform::GetCurrentThreadId(); + size_t _Index; + _Node* _ExistingNode = _FindLocalItem(_Key, &_Index); + if (_ExistingNode == NULL) + { + _ExistingNode = _AddLocalItem(_Key, _Index); + } + + _CONCRT_ASSERT(_ExistingNode != NULL); + return _ExistingNode->_M_value; + } + + /// <summary> + /// Returns a reference to the thread-private sub-computation. + /// </summary> + /// <param name="_Exists"> + /// A reference to a boolean. The boolean value referenced by this argument will be + /// set to <c>true</c> if the sub-computation already existed on this thread, and set to + /// <c>false</c> if this was the first sub-computation on this thread. + /// </param> + /// <returns> + /// A reference to the thread-private sub-computation. + /// </returns> + /// <seealso cref="Parallel Containers and Objects"/> + /**/ + _Ty& local(bool& _Exists) + { + unsigned long _Key = Concurrency::details::platform::GetCurrentThreadId(); + size_t _Index; + _Node* _ExistingNode = _FindLocalItem(_Key, &_Index); + if (_ExistingNode == NULL) + { + _Exists = false; + _ExistingNode = _AddLocalItem(_Key, _Index); + } + else + { + _Exists = true; + } + + _CONCRT_ASSERT(_ExistingNode != NULL); + return _ExistingNode->_M_value; + } + + /// <summary> + /// Clears any intermediate computational results from a previous usage. + /// </summary> + /**/ + void clear() + { + for (size_t _Index = 0; _Index < _M_size; ++_Index) + { + _Node* _CurrentNode = _M_buckets[_Index]; + while (_CurrentNode != NULL) + { + _Node* _NextNode = _CurrentNode->_M_chain; + delete _CurrentNode; + _CurrentNode = _NextNode; + } + } + memset((void*)_M_buckets, 0, _M_size * sizeof _M_buckets[0]); + } + + /// <summary> + /// Computes a final value from the set of thread-local sub-computations by calling the supplied combine functor. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to combine two thread-local sub-computations. + /// </typeparam> + /// <param name="_FnCombine"> + /// The functor that is used to combine the sub-computations. Its signature is <c>T (T, T)</c> or + /// <c>T (const T&, const T&)</c>, and it must be associative and commutative. + /// </param> + /// <returns> + /// The final result of combining all the thread-private sub-computations. + /// </returns> + /// <seealso cref="Parallel Containers and Objects"/> + /**/ + template<typename _Function> + _Ty combine(_Function _FnCombine) const + { + _Node* _CurrentNode = NULL; + size_t _Index; + + // Look for the first value in the set, and use (a copy of) that as the result. + // This eliminates a single call (of unknown cost) to _M_fnInitialize. + for (_Index = 0; _Index < _M_size; ++_Index) + { + _CurrentNode = _M_buckets[_Index]; + if (_CurrentNode != NULL) + { + break; + } + } + + // No values... return the initializer value. + if (_CurrentNode == NULL) + { + return _M_fnInitialize(); + } + + // Accumulate the rest of the items in the current bucket. + _Ty _Result = _CurrentNode->_M_value; + for (_CurrentNode = _CurrentNode->_M_chain; _CurrentNode != NULL; _CurrentNode = _CurrentNode->_M_chain) + { + _Result = _FnCombine(_Result, _CurrentNode->_M_value); + } + + // Accumulate values from the rest of the buckets. + _CONCRT_ASSERT(_Index < _M_size); + for (++_Index; _Index < _M_size; ++_Index) + { + for (_CurrentNode = _M_buckets[_Index]; _CurrentNode != NULL; _CurrentNode = _CurrentNode->_M_chain) + { + _Result = _FnCombine(_Result, _CurrentNode->_M_value); + } + } + + return _Result; + } + + /// <summary> + /// Computes a final value from the set of thread-local sub-computations by calling the supplied combine functor + /// once per thread-local sub-computation. The final result is accumulated by the function object. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked to combine a single thread-local sub-computation. + /// </typeparam> + /// <param name="_FnCombine"> + /// The functor that is used to combine one sub-computation. Its signature is <c>void (T)</c> or + /// <c>void (const T&)</c>, and must be associative and commutative. + /// </param> + /// <seealso cref="Parallel Containers and Objects"/> + /**/ + template<typename _Function> + void combine_each(_Function _FnCombine) const + { + for (size_t _Index = 0; _Index < _M_size; ++_Index) + { + for (_Node* _CurrentNode = _M_buckets[_Index]; _CurrentNode != NULL; _CurrentNode = _CurrentNode->_M_chain) + { + _FnCombine(_CurrentNode->_M_value); + } + } + } + +private: + void _InitNew() + { + _M_size = Concurrency::details::_GetCombinableSize(); + _M_buckets = new _Node*[_M_size]; + memset((void*)_M_buckets, 0, _M_size * sizeof _M_buckets[0]); + } + + void _InitCopy(const combinable& _Copy) + { + _M_buckets = new _Node*[_M_size]; + for (size_t _Index = 0; _Index < _M_size; ++_Index) + { + _M_buckets[_Index] = NULL; + for (_Node* _CurrentNode = _Copy._M_buckets[_Index]; _CurrentNode != NULL; _CurrentNode = _CurrentNode->_M_chain) + { + _Node* _NewNode = new _Node(_CurrentNode->_M_key, _CurrentNode->_M_value); + _NewNode->_M_chain = _M_buckets[_Index]; + _M_buckets[_Index] = _NewNode; + } + } + } + + _Node* _FindLocalItem(unsigned long _Key, size_t* _PIndex) + { + _CONCRT_ASSERT(_PIndex != NULL); + + *_PIndex = _Key % _M_size; + + // Search at this index for an existing value. + _Node* _CurrentNode = _M_buckets[*_PIndex]; + while (_CurrentNode != NULL) + { + if (_CurrentNode->_M_key == _Key) + { + return _CurrentNode; + } + + _CurrentNode = _CurrentNode->_M_chain; + } + + return NULL; + } + + _Node* _AddLocalItem(unsigned long _Key, size_t _Index) + { + _Node* _NewNode = new _Node(_Key, _M_fnInitialize()); + _Node* _TopNode; + do + { + _TopNode = _M_buckets[_Index]; + _NewNode->_M_chain = _TopNode; + } while (_InterlockedCompareExchangePointer(reinterpret_cast<void * volatile *>(&_M_buckets[_Index]), _NewNode, _TopNode) != _TopNode); + + return _NewNode; + } + +private: + _Node *volatile * _M_buckets; + size_t _M_size; + std::tr1::function<_Ty ()> _M_fnInitialize; +}; + +#pragma warning(pop) // C4316 + +#pragma push_macro("_MAX_NUM_TASKS_PER_CORE") +#pragma push_macro("_FINE_GRAIN_CHUNK_SIZE") +#pragma push_macro("_SORT_MAX_RECURSION_DEPTH") + +// This number is used to control dynamic task splitting +// The ideal chunk (task) division is that the number of cores is equal to the number of tasks, but it will +// perform very poorly when tasks are not balanced. The simple solution is to allocate more tasks than number +// of cores. _MAX_NUM_TASKS_PER_CORE provides a maximum number of tasks that will be allocated per core. +// If this number is too small, the load balancing problem will affect efficiency very seriously, especially +// when the compare operation is expensive. +// +// Note that this number is a maximum number -- the dynamic partition system will reduce the number of partitions +// per core based on the dynamic load. If all cores are very busy, the number of partitions will shrink to +// reduce the scheduler overhead. +// +// Initially, the total tasks(chunks) number of partitions "_Div_num" will be: core number * _MAX_NUM_TASKS_PER_CORE. +// The _Div_num will be divided by 2 after each task splitting. There are two special numbers for _Div_num: +// 1. When _Div_num reaches the point that _Div_num < _MAX_NUM_TASKS_PER_CORE, it means we have split more tasks than cores. +// 2. When _Div_num reaches the point that _Div_num <= 1, it means stop splitting more tasks and begin sorting serially. +#define _MAX_NUM_TASKS_PER_CORE 1024 + +// This is a number mainly is used to control the sampling and dynamic task splitting strategies. +// If the user configurable minimal divisible chunk size (default is 2048) is smaller than FINE_GRAIN_CHUNK_SIZE, +// the random sampling algorithm for quicksort will enter fine-grained mode, and take a strategy that reduces the sampling +// overhead. Also, the dynamic task splitting will enter fine-grained mode, which will split as many tasks as possible. +#define _FINE_GRAIN_CHUNK_SIZE 512 + +// This is the maximum depth that the quicksort will be called recursively. If we allow too far, a stack overflow may occur. +#define _SORT_MAX_RECURSION_DEPTH 64 + +template<typename _Random_iterator, typename _Function> +inline size_t _Median_of_three(const _Random_iterator &_Begin, size_t _A, size_t _B, size_t _C, const _Function &_Func, bool &_Potentially_equal) +{ + _Potentially_equal = false; + if (_Func(_Begin[_A], _Begin[_B])) + { + if (_Func(_Begin[_A], _Begin[_C])) + { + return _Func(_Begin[_B], _Begin[_C]) ? _B : _C; + } + else + { + return _A; + } + } + else + { + if (_Func(_Begin[_B], _Begin[_C])) + { + return _Func(_Begin[_A], _Begin[_C]) ? _A : _C; + } + else + { + _Potentially_equal = true; + return _B; + } + } +} + +template<typename _Random_iterator, typename _Function> +inline size_t _Median_of_nine(const _Random_iterator &_Begin, size_t _Size, const _Function &_Func, bool &_Potentially_equal) +{ + size_t _Offset = _Size / 8; + size_t _A = _Median_of_three(_Begin, 0, _Offset, _Offset * 2, _Func, _Potentially_equal), + _B = _Median_of_three(_Begin, _Offset * 3, _Offset * 4, _Offset * 5, _Func, _Potentially_equal), + _C = _Median_of_three(_Begin, _Offset * 6, _Offset * 7, _Size - 1, _Func, _Potentially_equal); + _B = _Median_of_three(_Begin, _A, _B, _C, _Func, _Potentially_equal); + + if (_Potentially_equal) + { + _Potentially_equal = !_Func(_Begin[_C], _Begin[_A]); + } + + return _B; +} + +// _Potentially_equal means that potentially all the values in the buffer are equal to the pivot value +template<typename _Random_iterator, typename _Function> +inline size_t _Select_median_pivot(const _Random_iterator &_Begin, size_t _Size, const _Function &_Func, const size_t _Chunk_size, bool &_Potentially_equal) +{ + // Base on different chunk size, apply different sampling optimization + if (_Chunk_size < _FINE_GRAIN_CHUNK_SIZE && _Size <= std::max<size_t>(_Chunk_size * 4, static_cast<size_t>(15))) + { + bool _Never_care_equal; + return _Median_of_three(_Begin, 0, _Size / 2, _Size - 1, _Func, _Never_care_equal); + } + else + { + return _Median_of_nine(_Begin, _Size, _Func, _Potentially_equal); + } +} + +// Find out two middle points for two sorted arrays by binary search so that the number of total elements on the left part of two middle points is equal +// to the number of total elements on the right part of two sorted arrays and all elements on the left part is smaller than right part. +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Function> +size_t _Search_mid_point(const _Random_iterator &_Begin1, size_t &_Len1, const _Random_buffer_iterator &_Begin2, size_t &_Len2, const _Function &_Func) +{ + size_t _Len = (_Len1 + _Len2) / 2, _Index1 = 0, _Index2 = 0; + + while (_Index1 < _Len1 && _Index2 < _Len2) + { + size_t _Mid1 = (_Index1 + _Len1) / 2, _Mid2 = (_Index2 + _Len2) / 2; + if (_Func(_Begin1[_Mid1], _Begin2[_Mid2])) + { + if (_Mid1 + _Mid2 < _Len) + { + _Index1 = _Mid1 + 1; + } + else + { + _Len2 = _Mid2; + } + } + else + { + if (_Mid1 + _Mid2 < _Len) + { + _Index2 = _Mid2 + 1; + } + else + { + _Len1 = _Mid1; + } + } + } + + if (_Index1 == _Len1) + { + _Len2 = _Len - _Len1; + } + else + { + _Len1 = _Len - _Len2; + } + + return _Len; +} + +// "move" operation is applied between buffers +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Random_output_iterator, typename _Function> +void _Merge_chunks(_Random_iterator _Begin1, const _Random_iterator &_End1, _Random_buffer_iterator _Begin2, const _Random_buffer_iterator &_End2, + _Random_output_iterator _Output, const _Function &_Func) +{ + while (_Begin1 != _End1 && _Begin2 != _End2) + { + if (_Func(*_Begin1, *_Begin2)) + { + *_Output++ = std::move(*_Begin1++); + } + else + { + *_Output++ = std::move(*_Begin2++); + } + } + + if (_Begin1 != _End1) + { + std::_Move(_Begin1, _End1, _Output); + } + else if (_Begin2 != _End2) + { + std::_Move(_Begin2, _End2, _Output); + } +} + +// _Div_num of threads(tasks) merge two chunks in parallel, _Div_num should be power of 2, if not, the largest power of 2 that is +// smaller than _Div_num will be used +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Random_output_iterator, typename _Function> +void _Parallel_merge(_Random_iterator _Begin1, size_t _Len1, _Random_buffer_iterator _Begin2, size_t _Len2, _Random_output_iterator _Output, + const _Function &_Func, size_t _Div_num) +{ + // Turn to serial merge or continue splitting chunks base on "_Div_num" + if (_Div_num <= 1 || (_Len1 <= 1 && _Len2 <= 1)) + { + _Merge_chunks(_Begin1, _Begin1 + _Len1, _Begin2, _Begin2 + _Len2, _Output, _Func); + } + else + { + size_t _Mid_len1 = _Len1, _Mid_len2 = _Len2; + size_t _Mid = _Search_mid_point(_Begin1, _Mid_len1, _Begin2, _Mid_len2, _Func); + + structured_task_group _Tg; + auto _Handle = make_task([&] + { + _Parallel_merge(_Begin1, _Mid_len1, _Begin2, _Mid_len2, _Output, _Func, _Div_num / 2); + }); + _Tg.run(_Handle); + + _Parallel_merge(_Begin1 + _Mid_len1, _Len1 - _Mid_len1, _Begin2 + _Mid_len2, _Len2 - _Mid_len2, _Output + _Mid, _Func, _Div_num / 2); + + _Tg.wait(); + } +} + +// Return current sorting byte from key +template<typename _Ty, typename _Function> +inline size_t _Radix_key(const _Ty& _Val, size_t _Radix, _Function _Proj_func) +{ + return static_cast<size_t>(_Proj_func(_Val) >> static_cast<int>(8 * _Radix) & 255); +} + +// One pass of radix sort +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Function> +void _Integer_radix_pass(const _Random_iterator &_Begin, size_t _Size, const _Random_buffer_iterator &_Output, size_t _Radix, _Function _Proj_func) +{ + if (!_Size) + { + return; + } + + size_t _Pos[256] = {0}; + + for (size_t _I = 0; _I < _Size; _I++) + { + ++_Pos[_Radix_key(_Begin[_I], _Radix, _Proj_func)]; + } + + for (size_t _I = 1; _I < 256; _I++) + { + _Pos[_I] += _Pos[_I - 1]; + } + + // _Size > 0 + for (size_t _I = _Size - 1; _I != 0; _I--) + { + _Output[--_Pos[_Radix_key(_Begin[_I], _Radix, _Proj_func)]] = std::move(_Begin[_I]); + } + + _Output[--_Pos[_Radix_key(_Begin[0], _Radix, _Proj_func)]] = std::move(_Begin[0]); +} + +// Serial least-significant-byte radix sort, it will sort base on last "_Radix" number of bytes +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Function> +void _Integer_radix_sort(const _Random_iterator &_Begin, size_t _Size, const _Random_buffer_iterator &_Output, + size_t _Radix, _Function _Proj_func, size_t _Deep = 0) +{ + size_t _Cur_radix = 0; + if (_Size == 0) + { + return; + } + + while (_Cur_radix < _Radix) + { + _Integer_radix_pass(_Begin, _Size, _Output, _Cur_radix++, _Proj_func); + _Integer_radix_pass(_Output, _Size, _Begin, _Cur_radix++, _Proj_func); + } + + if (_Cur_radix == _Radix) + { + _Integer_radix_pass(_Begin, _Size, _Output, _Cur_radix++, _Proj_func); + } + + // if odd round is passed, then move result back to input buffer + if (_Deep + _Radix + 1 & 1) + { + if (_Radix + 1 & 1) + { + std::_Move(_Output, _Output + _Size, _Begin); + } + else + { + std::_Move(_Begin, _Begin + _Size, _Output); + } + } +} + +// Parallel most-significant-byte _Radix sort. +// In the end, it will turn to serial least-significant-byte radix sort +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Function> +void _Parallel_integer_radix_sort(const _Random_iterator &_Begin, size_t _Size, const _Random_buffer_iterator &_Output, + size_t _Radix, _Function _Proj_func, const size_t _Chunk_size, size_t _Deep = 0) +{ + // If the chunk _Size is too small, then turn to serial least-significant-byte radix sort + if (_Size <= _Chunk_size || _Radix < 1) + { + return _Integer_radix_sort(_Begin, _Size, _Output, _Radix, _Proj_func, _Deep); + } + + size_t _Threads_num = Concurrency::details::_CurrentScheduler::_GetNumberOfVirtualProcessors(); + size_t _Buffer_size = sizeof(size_t) * 256 * _Threads_num; + size_t _Step = _Size / _Threads_num; + size_t _Remain = _Size % _Threads_num; + + Concurrency::details::_MallocaArrayHolder<size_t [256]> _Holder; + size_t (*_Chunks)[256] = _Holder._InitOnRawMalloca(_malloca(_Buffer_size)); + + memset(_Chunks, 0, _Buffer_size); + + // Our purpose is to map unsorted data in buffer "_Begin" to buffer "_Output" so that all elements who have the same + // byte value in the "_Radix" position will be grouped together in the buffer "_Output" + // + // Serial version: + // To understand this algorithm, first consider a serial version. In following example, we treat 1 digit as 1 byte, so we have a + // total of 10 elements for each digit instead of 256 elements in each byte. Let's suppose "_Radix" == 1 (right most is 0), and: + // + // begin: [ 32 | 62 | 21 | 43 | 55 | 43 | 23 | 44 ] + // + // We want to divide the output buffer "_Output" into 10 chunks, and each the element in the "_Begin" buffer should be mapped into + // the proper destination chunk based on its current digit (byte) indicated by "_Radix" + // + // Because "_Radix" == 1, after a pass of this function, the chunks in the "_Output" should look like: + // + // buffer: [ | | 21 23 | 32 | 43 43 44 | 55 | 62 | | | ] + // 0 1 2 3 4 5 6 7 8 9 + // + // The difficulty is determining where to insert values into the "_Output" to get the above result. The way to get the + // start position of each chunk of the buffer is: + // 1. Count the number of elements for each chunk (in above example, chunk0 is 0, chunk1 is 0, chunk2 is 2, chunk3 is 1 ... + // 2. Make a partial sum for these chunks( in above example, we will get chunk0=chunk0=0, chunk1=chunk0+chunk1=0, + // chunk2=chunk0+chunk1+chunk2=2, chunk3=chunk0+chunk1+chunk2+chunk3=3 + // + // After these steps, we will get the end position of each chunk in the "_Output". The begin position of each chunk will be the end + // point of last chunk (begin point is close but the end point is open). After that, we can scan the original array again and directly + // put elements from original buffer "_Begin" into specified chunk on buffer "_Output". + // Finally, we invoke _parallel_integer_radix_sort in parallel for each chunk and sort them in parallel based on the next digit (byte). + // Because this is a STABLE sort algorithm, if two numbers has same key value on this byte (digit), their original order should be kept. + // + // Parallel version: + // Almost the same as the serial version, the differences are: + // 1. The count for each chunk is executed in parallel, and each thread will count one segment of the input buffer "_Begin". + // The count result will be separately stored in their own chunk size counting arrays so we have a total of threads-number + // of chunk count arrays. + // For example, we may have chunk00, chunk01, ..., chunk09 for first thread, chunk10, chunk11, ..., chunk19 for second thread, ... + // 2. The partial sum should be executed across these chunk counting arrays that belong to different threads, instead of just + // making a partial sum in one counting array. + // This is because we need to put values from different segments into one final buffer, and the absolute buffer position for + // each chunkXX is needed. + // 3. Make a parallel scan for original buffer again, and move numbers in parallel into the corresponding chunk on each buffer based + // on these threads' chunk size counters. + + // Count in parallel and separately save their local results without reducing + Concurrency::parallel_for(static_cast<size_t>(0), _Threads_num, [=](size_t _Index) + { + size_t _Beg_index, _End_index; + + // Calculate the segment position + if (_Index < _Remain) + { + _Beg_index = _Index * (_Step + 1); + _End_index = _Beg_index + (_Step + 1); + } + else + { + _Beg_index = _Remain * (_Step + 1) + (_Index - _Remain) * _Step; + _End_index = _Beg_index + _Step; + } + + // Do a counting + while (_Beg_index != _End_index) + { + ++_Chunks[_Index][_Radix_key(_Begin[_Beg_index++], _Radix, _Proj_func)]; + } + }); + + int _Index = -1, _Count = 0; + + // Partial sum cross different threads' chunk counters + for (int _I = 0; _I < 256; _I++) + { + size_t _Last = _I ? _Chunks[_Threads_num - 1][_I - 1] : 0; + _Chunks[0][_I] += _Last; + + for (size_t _J = 1; _J < _Threads_num; _J++) + { + _Chunks[_J][_I] += _Chunks[_J - 1][_I]; + } + + // "_Chunks[_Threads_num - 1][_I] - _Last" will get the global _Size for chunk _I(including all threads local _Size for chunk _I) + // this will chunk whether the chunk _I is empty or not. If it's not empty, it will be recorded. + if (_Chunks[_Threads_num - 1][_I] - _Last) + { + ++_Count; + _Index = _I; + } + } + + // If there is more than 1 chunk that has content, then continue the original algorithm + if (_Count > 1) + { + // Move the elements in parallel into each chunk + Concurrency::parallel_for(static_cast<size_t>(0), _Threads_num, [=](size_t _Index) + { + size_t _Beg_index, _End_index; + + // Calculate the segment position + if (_Index < _Remain) + { + _Beg_index = _Index * (_Step + 1); + _End_index = _Beg_index + (_Step + 1); + } + else + { + _Beg_index = _Remain * (_Step + 1) + (_Index - _Remain) * _Step; + _End_index = _Beg_index + _Step; + } + + // Do a move operation to directly put each value into its destination chunk + // Chunk pointer is moved after each put operation. + if (_Beg_index != _End_index--) + { + while (_Beg_index != _End_index) + { + _Output[--_Chunks[_Index][_Radix_key(_Begin[_End_index], _Radix, _Proj_func)]] = std::move(_Begin[_End_index]); + --_End_index; + } + _Output[--_Chunks[_Index][_Radix_key(_Begin[_End_index], _Radix, _Proj_func)]] = std::move(_Begin[_End_index]); + } + }); + + // Invoke _parallel_integer_radix_sort in parallel for each chunk + Concurrency::parallel_for(static_cast<size_t>(0), static_cast<size_t>(256), [=](size_t _Index) + { + if (_Index < 256 - 1) + { + _Parallel_integer_radix_sort(_Output + _Chunks[0][_Index], _Chunks[0][_Index + 1] - _Chunks[0][_Index], + _Begin + _Chunks[0][_Index], _Radix - 1, _Proj_func, _Chunk_size, _Deep + 1); + } + else + { + _Parallel_integer_radix_sort(_Output + _Chunks[0][_Index], _Size - _Chunks[0][_Index], + _Begin + _Chunks[0][_Index], _Radix - 1, _Proj_func, _Chunk_size, _Deep + 1); + } + }); + } + else + { + // Only one chunk has content + // A special optimization is applied because one chunk means all numbers have a same value on this particular byte (digit). + // Because we cannot sort them at all (they are all equal at this point), directly call _parallel_integer_radix_sort to + // sort next byte (digit) + _Parallel_integer_radix_sort(_Begin, _Size, _Output, _Radix - 1, _Proj_func, _Chunk_size, _Deep); + } +} + +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Function> +void _Parallel_integer_sort_asc(const _Random_iterator &_Begin, size_t _Size, const _Random_buffer_iterator &_Output, + _Function _Proj_func, const size_t _Chunk_size) +{ + typedef typename std::iterator_traits<_Random_iterator>::value_type _Value_type; + // The key type of the radix sort, this must be an "unsigned integer-like" type, that is, it needs support: + // operator>> (int), operator>>= (int), operator& (int), operator <, operator size_t () + typedef typename std::remove_const<typename std::remove_reference<decltype(_Proj_func(*_Begin))>::type>::type _Integer_type; + + // Find out the max value, which will be used to determine the highest differing byte (the radix position) + _Integer_type _Max_val = Concurrency::parallel_reduce(_Begin, _Begin + _Size, _Proj_func(*_Begin), + [=](_Random_iterator _Begin, _Random_iterator _End, _Integer_type _Init) -> _Integer_type + { + while (_Begin != _End) + { + _Integer_type _Ret = _Proj_func(*_Begin++); + if (_Init < _Ret) + { + _Init = _Ret; + } + } + + return _Init; + }, [](const _Integer_type &a, const _Integer_type &b) -> const _Integer_type& {return (a < b)? b : a;}); + size_t _Radix = 0; + + // Find out highest differing byte + while (_Max_val >>= 8) + { + ++_Radix; + } + + _Parallel_integer_radix_sort(_Begin, _Size, _Output, _Radix, _Proj_func, _Chunk_size); +} + +template<typename _Random_iterator, typename _Function> +void _Parallel_quicksort_impl(const _Random_iterator &_Begin, size_t _Size, const _Function &_Func, size_t _Div_num, const size_t _Chunk_size, int _Depth) +{ + if (_Depth >= _SORT_MAX_RECURSION_DEPTH || _Size <= _Chunk_size || _Size <= static_cast<size_t>(3) || _Chunk_size >= _FINE_GRAIN_CHUNK_SIZE && _Div_num <= 1) + { + return std::sort(_Begin, _Begin + _Size, _Func); + } + + // Determine whether we need to do a three-way quick sort + // We benefit from three-way merge if there are a lot of elements that are EQUAL to the median value, + // _Select_median_pivot function will test redundant density by sampling + bool _Is_three_way_split = false; + size_t _Mid_index = _Select_median_pivot(_Begin, _Size, _Func, _Chunk_size, _Is_three_way_split); + + // Move the median value to the _Begin position. + if (_Mid_index) + { + std::swap(*_Begin, _Begin[_Mid_index]); + } + size_t _I = 1, _J = _Size - 1; + + // Three-way or two-way partition + // _Div_num < _MAX_NUM_TASKS_PER_CORE is checked to make sure it will never do three-way split before splitting enough tasks + if (_Is_three_way_split && _Div_num < _MAX_NUM_TASKS_PER_CORE) + { + while (_Func(*_Begin, _Begin[_J])) + { + --_J; + } + + while (_Func(_Begin[_I], *_Begin)) + { + ++_I; + } + + // Starting from this point, left side of _I will less than median value, right side of _J will be greater than median value, + // and the middle part will be equal to median. _K is used to scan between _I and _J + size_t _K = _J; + while (_I <= _K) + { + if (_Func(_Begin[_K], *_Begin)) + { + std::swap(_Begin[_I++], _Begin[_K]); + } + else + { + --_K; + } + + while (_Func(*_Begin, _Begin[_K])) + { + std::swap(_Begin[_K--], _Begin[_J--]); + } + } + + ++_J; + } + else + { + while (_I <= _J) + { + // Will stop before _Begin + while (_Func(*_Begin, _Begin[_J])) + { + --_J; + } + + // There must be another element equal or greater than *_Begin + while (_Func(_Begin[_I], *_Begin)) + { + ++_I; + } + + if (_I < _J) + { + std::swap(_Begin[_I++], _Begin[_J--]); + } + else + { + break; + } + } + + _I = ++_J; + } + + std::swap(*_Begin, _Begin[--_I]); + + structured_task_group _Tg; + volatile size_t _Next_div = _Div_num / 2; + auto _Handle = make_task([&] + { + _Parallel_quicksort_impl(_Begin + _J, _Size - _J, _Func, _Next_div, _Chunk_size, _Depth+1); + }); + _Tg.run(_Handle); + + _Parallel_quicksort_impl(_Begin, _I, _Func, _Next_div, _Chunk_size, _Depth+1); + + // If at this point, the work hasn't been scheduled, then slow down creating new tasks + if (_Div_num < _MAX_NUM_TASKS_PER_CORE) + { + _Next_div /= 2; + } + + _Tg.wait(); +} + +// This function will be called to sort the elements in the "_Begin" buffer. However, we can't tell whether the result will end up in buffer +// "_Begin", or buffer "_Output" when it returned. The return value is designed to indicate which buffer holds the sorted result. +// Return true if the merge result is in the "_Begin" buffer; return false if the result is in the "_Output" buffer. +// We can't always put the result into one assigned buffer because that may cause frequent buffer copies at return time. +template<typename _Random_iterator, typename _Random_buffer_iterator, typename _Function> +inline bool _Parallel_buffered_sort_impl(const _Random_iterator &_Begin, size_t _Size, _Random_buffer_iterator _Output, const _Function &_Func, + int _Div_num, const size_t _Chunk_size) +{ + static_assert(std::is_same<typename std::iterator_traits<_Random_iterator>::value_type, typename std::iterator_traits<_Random_buffer_iterator>::value_type>::value, + "same value type expected"); + + if (_Div_num <= 1 || _Size <= _Chunk_size) + { + _Parallel_quicksort_impl(_Begin, _Size, _Func, _MAX_NUM_TASKS_PER_CORE, _Chunk_size, 0); + + // In case _Size <= _Chunk_size happened BEFORE the planned stop time (when _Div_num == 1) we need to calculate how many turns of + // binary divisions are left. If there are an odd number of turns left, then the buffer move is necessary to make sure the final + // merge result will be in the original input array. + int _Left_div_turns = 0; + while (_Div_num >>= 1) + { + _Left_div_turns++; + } + + if (_Left_div_turns & 1) + { + std::move(_Begin, _Begin + _Size, _Output); + return true; + } + else + { + return false; + } + } + else + { + size_t _Mid = _Size / 2; + structured_task_group _Tg; + + auto _Handle = make_task([&, _Chunk_size] + { + _Parallel_buffered_sort_impl(_Begin, _Mid, _Output, _Func, _Div_num / 2, _Chunk_size); + }); + _Tg.run(_Handle); + + bool _Is_buffer_swap = _Parallel_buffered_sort_impl(_Begin + _Mid, _Size - _Mid, _Output + _Mid, _Func, _Div_num / 2, _Chunk_size); + + _Tg.wait(); + + if (_Is_buffer_swap) + { + _Parallel_merge(_Output, _Mid, _Output + _Mid, _Size - _Mid, _Begin, _Func, _Div_num); + } + else + { + _Parallel_merge(_Begin, _Mid, _Begin + _Mid, _Size - _Mid, _Output, _Func, _Div_num); + } + + return !_Is_buffer_swap; + } +} + +// Disable the warning saying constant value in condition expression. +// This is by design that lets the compiler optimize the trivial constructor. +#pragma warning (push) +#pragma warning (disable: 4127) + +// Allocate and construct a buffer +template<typename _Allocator> +inline typename _Allocator::pointer _Construct_buffer(size_t _N, _Allocator &_Alloc) +{ + typename _Allocator::pointer _P = _Alloc.allocate(_N); + + // If the objects being sorted have trivial default constructors, they do not need to be + // constructed here. This can benefit performance. + if (!std::has_trivial_default_constructor<typename _Allocator::value_type>::value) + { + for (size_t _I = 0; _I < _N; _I++) + { + // Objects being sorted must have a default constructor + typename _Allocator::value_type _T; + _Alloc.construct(_P + _I, std::forward<typename _Allocator::value_type>(_T)); + } + } + + return _P; +} + +// Destroy and deallocate a buffer +template<typename _Allocator> +inline void _Destroy_buffer(typename _Allocator::pointer _P, size_t _N, _Allocator &_Alloc) +{ + // If the objects being sorted have trivial default destructors, they do not need to be + // destructed here. This can benefit performance. + if (!std::has_trivial_destructor<typename _Allocator::value_type>::value) + { + for (size_t _I = 0; _I < _N; _I++) + { + _Alloc.destroy(_P + _I); + } + } + + _Alloc.deallocate(_P, _N); +} + +// +// Exception safe RAII wrapper for the allocated buffers +// + +template<typename _Allocator> +class _AllocatedBufferHolder +{ +public: + _AllocatedBufferHolder(size_t _Size, const _Allocator & _Alloc): _M_alloc(_Alloc) + { + _M_size = _Size; + _M_buffer = _Construct_buffer(_Size, _M_alloc); + } + + ~_AllocatedBufferHolder() + { + _Destroy_buffer(_M_buffer, _M_size, _M_alloc); + } + + typename _Allocator::pointer _Get_buffer() + { + return _M_buffer; + } + +private: + size_t _M_size; + _Allocator _M_alloc; + typename _Allocator::pointer _M_buffer; +}; + + +#pragma warning (pop) + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort. +/// </summary> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <remarks> +/// The first overload uses the the binary comparator <c>std::less</c>. +/// <para>The second overloaded uses the supplied binary comparator that should have the signature <c>bool _Func(T, T)</c> where <c>T</c> +/// is the type of the elements in the input range.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Random_iterator> +inline void parallel_sort(const _Random_iterator &_Begin, const _Random_iterator &_End) +{ + parallel_sort(_Begin, _End, std::less<typename std::iterator_traits<_Random_iterator>::value_type>()); +} + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort. +/// </summary> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the binary comparison functor. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <param name="_Func"> +/// A user-defined predicate function object that defines the comparison criterion to be satisfied by successive elements in the ordering. +/// A binary predicate takes two arguments and returns <c>true</c> when satisfied and <c>false</c> when not satisfied. This comparator function +/// must impose a strict weak ordering on pairs of elements from the sequence. +/// </param> +/// <param name="_Chunk_size"> +/// The mimimum size of a chunk that will be split into two for parallel execution. +/// </param> +/// <remarks> +/// The first overload uses the the binary comparator <c>std::less</c>. +/// <para>The second overloaded uses the supplied binary comparator that should have the signature <c>bool _Func(T, T)</c> where <c>T</c> +/// is the type of the elements in the input range.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Random_iterator, typename _Function> +inline void parallel_sort(const _Random_iterator &_Begin, const _Random_iterator &_End, const _Function &_Func, const size_t _Chunk_size = 2048) +{ + _CONCRT_ASSERT(_Chunk_size > 0); + + // Check for cancellation before the algorithm starts. + interruption_point(); + + size_t _Size = _End - _Begin; + size_t _Core_num = Concurrency::details::_CurrentScheduler::_GetNumberOfVirtualProcessors(); + + if (_Size <= _Chunk_size || _Core_num < 2) + { + return std::sort(_Begin, _End, _Func); + } + + _Parallel_quicksort_impl(_Begin, _Size, _Func, _Core_num * _MAX_NUM_TASKS_PER_CORE, _Chunk_size, 0); +} + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort except that +/// it needs <c>O(n)</c> additional space, and requires default initialization for the elements being sorted. +/// </summary> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// In most cases parallel_buffered_sort will show an improvement in performance over <see cref="parallel_sort Function">parallel_sort</see>, and you should +/// use it over parallel_sort if you have the memory available. +/// <para>If you do not supply a binary comparator <c>std::less</c> is used as the default, which requires the element type to provide the +/// operator <c>operator<()</c>.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Random_iterator> +inline void parallel_buffered_sort(const _Random_iterator &_Begin, const _Random_iterator &_End) +{ + parallel_buffered_sort<std::allocator<typename std::iterator_traits<_Random_iterator>::value_type>>(_Begin, _End, + std::less<typename std::iterator_traits<_Random_iterator>::value_type>()); +} + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort except that +/// it needs <c>O(n)</c> additional space, and requires default initialization for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// In most cases parallel_buffered_sort will show an improvement in performance over <see cref="parallel_sort Function">parallel_sort</see>, and you should +/// use it over parallel_sort if you have the memory available. +/// <para>If you do not supply a binary comparator <c>std::less</c> is used as the default, which requires the element type to provide the +/// operator <c>operator<()</c>.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator> +inline void parallel_buffered_sort(const _Random_iterator &_Begin, const _Random_iterator &_End) +{ + parallel_buffered_sort<_Allocator>(_Begin, _End, + std::less<typename std::iterator_traits<_Random_iterator>::value_type>()); +} + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort except that +/// it needs <c>O(n)</c> additional space, and requires default initialization for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <param name="_Alloc"> +/// An instance of an STL compatible memory allocator. +/// </param> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// In most cases parallel_buffered_sort will show an improvement in performance over <see cref="parallel_sort Function">parallel_sort</see>, and you should +/// use it over parallel_sort if you have the memory available. +/// <para>If you do not supply a binary comparator <c>std::less</c> is used as the default, which requires the element type to provide the +/// operator <c>operator<()</c>.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator> +inline void parallel_buffered_sort(const _Allocator& _Alloc, const _Random_iterator &_Begin, const _Random_iterator &_End) +{ + parallel_buffered_sort<_Allocator>(_Alloc, _Begin, _End, std::less<typename std::iterator_traits<_Random_iterator>::value_type>()); +} + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort except that +/// it needs <c>O(n)</c> additional space, and requires default initialization for the elements being sorted. +/// </summary> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the binary comparator. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <param name="_Func"> +/// A user-defined predicate function object that defines the comparison criterion to be satisfied by successive elements in the ordering. +/// A binary predicate takes two arguments and returns <c>true</c> when satisfied and <c>false</c> when not satisfied. This comparator function +/// must impose a strict weak ordering on pairs of elements from the sequence. +/// </param> +/// <param name="_Chunk_size"> +/// The mimimum size of a chunk that will be split into two for parallel execution. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// In most cases parallel_buffered_sort will show an improvement in performance over <see cref="parallel_sort Function">parallel_sort</see>, and you should +/// use it over parallel_sort if you have the memory available. +/// <para>If you do not supply a binary comparator <c>std::less</c> is used as the default, which requires the element type to provide the +/// operator <c>operator<()</c>.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Random_iterator, typename _Function> +inline void parallel_buffered_sort(const _Random_iterator &_Begin, const _Random_iterator &_End, const _Function &_Func, const size_t _Chunk_size = 2048) +{ + parallel_buffered_sort<std::allocator<typename std::iterator_traits<_Random_iterator>::value_type>>(_Begin, _End, _Func, _Chunk_size); +} + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort except that +/// it needs <c>O(n)</c> additional space, and requires default initialization for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the binary comparator. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <param name="_Func"> +/// A user-defined predicate function object that defines the comparison criterion to be satisfied by successive elements in the ordering. +/// A binary predicate takes two arguments and returns <c>true</c> when satisfied and <c>false</c> when not satisfied. This comparator function +/// must impose a strict weak ordering on pairs of elements from the sequence. +/// </param> +/// <param name="_Chunk_size"> +/// The mimimum size of a chunk that will be split into two for parallel execution. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// In most cases parallel_buffered_sort will show an improvement in performance over <see cref="parallel_sort Function">parallel_sort</see>, and you should +/// use it over parallel_sort if you have the memory available. +/// <para>If you do not supply a binary comparator <c>std::less</c> is used as the default, which requires the element type to provide the +/// operator <c>operator<()</c>.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator, typename _Function> +inline void parallel_buffered_sort(const _Random_iterator &_Begin, const _Random_iterator &_End, const _Function &_Func, const size_t _Chunk_size = 2048) +{ + _Allocator _Alloc; + return parallel_buffered_sort<_Allocator, _Random_iterator, _Function>(_Alloc, _Begin, _End, _Func, _Chunk_size); +} + +/// <summary> +/// Arranges the elements in a specified range into a nondescending order, or according to an ordering criterion specified by a binary predicate, +/// in parallel. This function is semantically similar to <c>std::sort</c> in that it is a compare-based, unstable, in-place sort except that +/// it needs <c>O(n)</c> additional space, and requires default initialization for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the binary comparator. +/// </typeparam> +/// <param name="_Alloc"> +/// An instance of an STL compatible memory allocator. +/// </param> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <param name="_Func"> +/// A user-defined predicate function object that defines the comparison criterion to be satisfied by successive elements in the ordering. +/// A binary predicate takes two arguments and returns <c>true</c> when satisfied and <c>false</c> when not satisfied. This comparator function +/// must impose a strict weak ordering on pairs of elements from the sequence. +/// </param> +/// <param name="_Chunk_size"> +/// The mimimum size of a chunk that will be split into two for parallel execution. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// In most cases parallel_buffered_sort will show an improvement in performance over <see cref="parallel_sort Function">parallel_sort</see>, and you should +/// use it over parallel_sort if you have the memory available. +/// <para>If you do not supply a binary comparator <c>std::less</c> is used as the default, which requires the element type to provide the +/// operator <c>operator<()</c>.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator, typename _Function> +inline void parallel_buffered_sort(const _Allocator& _Alloc, const _Random_iterator &_Begin, const _Random_iterator &_End, const _Function &_Func, const size_t _Chunk_size = 2048) +{ + _CONCRT_ASSERT(_Chunk_size > 0); + + // Check cancellation before the algorithm starts. + interruption_point(); + + size_t _Size = _End - _Begin; + size_t _Core_num = Concurrency::details::_CurrentScheduler::_GetNumberOfVirtualProcessors(); + + if (_Size <= _Chunk_size || _Core_num < 2) + { + return std::sort(_Begin, _End, _Func); + } + const static size_t CORE_NUM_MASK = 0x55555555; + + _AllocatedBufferHolder<_Allocator> _Holder(_Size, _Alloc); + + // Prevent cancellation from happening during the algorithm in case it leaves buffers in unknown state. + run_with_cancellation_token([&]() { + // This buffered sort algorithm will divide chunks and apply parallel quicksort on each chunk. In the end, it will + // apply parallel merge to these sorted chunks. + // + // We need to decide on the number of chunks to divide the input buffer into. If we divide it into n chunks, log(n) + // merges will be needed to get the final sorted result. In this algorithm, we have two buffers for each merge + // operation, let's say buffer A and B. Buffer A is the original input array, buffer B is the additional allocated + // buffer. Each turn's merge will put the merge result into the other buffer; for example, if we decided to split + // into 8 chunks in buffer A at very beginning, after one pass of merging, there will be 4 chunks in buffer B. + // If we apply one more pass of merging, there will be 2 chunks in buffer A again. + // + // The problem is we want to the final merge pass to put the result back in buffer A, so that we don't need + // one extra copy to put the sorted data back to buffer A. + // To make sure the final result is in buffer A (original input array), we need an even number of merge passes, + // which means log(n) must be an even number. Thus n must be a number power(2, even number). For example, when the + // even number is 2, n is power(2, 2) = 4, when even number is 4, n is power(2, 4) = 16. When we divide chunks + // into these numbers, the final merge result will be in the original input array. Now we need to decide the chunk(split) + // number based on this property and the number of cores. + // + // We want to get a chunk (split) number close to the core number (or a little more than the number of cores), + // and it also needs to satisfy above property. For a 8 core machine, the best chunk number should be 16, because it's + // the smallest number that satisfies the above property and is bigger than the core number (so that we can utilize all + // cores, a little more than core number is OK, we need to split more tasks anyway). + // + // In this algorithm, we will make this alignment by bit operations (it's easy and clear). For a binary representation, + // all the numbers that satisfy power(2, even number) will be 1, 100, 10000, 1000000, 100000000 ... + // After OR-ing these numbers together, we will get a mask (... 0101 0101 0101) which is all possible combinations of + // power(2, even number). We use _Core_num & CORE_NUM_MASK | _Core_num << 1 & CORE_NUM_MASK, a bit-wise operation to align + // _Core_num's highest bit into a power(2, even number). + // + // It means if _Core_num = 8, the highest bit in binary is bin(1000) which is not power(2, even number). After this + // bit-wise operation, it will align to bin(10000) = 16 which is power(2, even number). If the _Core_num = 16, after + // alignment it still returns 16. The trick is to make sure the highest bit of _Core_num will align to the "1" bit of the + // mask bin(... 0101 0101 0101) We don't care about the other bits on the aligned result except the highest bit, because they + // will be ignored in the function. + _Parallel_buffered_sort_impl(_Begin, _Size, stdext::make_unchecked_array_iterator(_Holder._Get_buffer()), + _Func, _Core_num & CORE_NUM_MASK | _Core_num << 1 & CORE_NUM_MASK, _Chunk_size); + }, cancellation_token::none()); + +} + +#pragma warning(push) +#pragma warning (disable: 4127) +// +// This is a default function used for parallel_radixsort which will return just the value. +// It also performs compile-time checks to ensure that the data type is integral. +// +template <typename _DataType> +struct _Radix_sort_default_function +{ + size_t operator()(const _DataType& val) const + { + // An instance of the type predicate returns the value if the type _DataType is one of the integral types, otherwise it + // statically asserts. + // An integral type is one of: bool, char, unsigned char, signed char, wchar_t, short, unsigned short, int, unsigned int, long, + // and unsigned long. + // In addition, with compilers that provide them, an integral type can be one of long long, unsigned long long, __int64, and + // unsigned __int64 + static_assert(std::is_integral<_DataType>::value, + "Type should be integral to use default radix function. For more information on integral types, please refer to http://msdn.microsoft.com/en-us/library/bb983099.aspx."); + static_assert((sizeof(_DataType) <= sizeof(size_t)), "Passed Type is bigger than size_t."); + + if (std::is_unsigned<_DataType>::value) + { + return val; + } + else + { + // The default function needs to take the signed integer-like representation and map it to an unsigned one. The + // following code will take the midpoint of the unsigned representable range (SIZE_MAX/2)+1 and does an unsigned + // add of the value. Thus, it maps a [-signed_min,+signed_max] range into a [0, unsigned_max] range. + return (((SIZE_MAX/2) + 1) + static_cast<size_t>(val)); + } + } +}; +#pragma warning (pop) + +/// <summary> +/// Arranges elements in a specified range into an non descending order using a radix sorting algorithm. This is a stable sort function which requires a +/// projection function that can project elements to be sorted into unsigned integer-like keys. Default initialization is required for the elements being sorted. +/// </summary> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// An unary projection functor with the signature<c>I _Proj_func(T)</c> is required to return a key when given an element, where <c>T</c> is the element +/// type and <c>I</c> is an unsigned integer-like type. +/// <para>If you do not supply a projection function, a default projection function which simply returns the element is used for integral types. The function +/// will fail to compile if the element is not an integral type in the absence of a projection function.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Random_iterator> +inline void parallel_radixsort(const _Random_iterator &_Begin, const _Random_iterator &_End) +{ + typedef typename std::iterator_traits<_Random_iterator>::value_type _DataType; + + _Radix_sort_default_function<_DataType> _Proj_func; + + parallel_radixsort<std::allocator<_DataType>>(_Begin, _End, _Proj_func, 256 * 256); +} + +/// <summary> +/// Arranges elements in a specified range into an non descending order using a radix sorting algorithm. This is a stable sort function which requires a +/// projection function that can project elements to be sorted into unsigned integer-like keys. Default initialization is required for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// An unary projection functor with the signature<c>I _Proj_func(T)</c> is required to return a key when given an element, where <c>T</c> is the element +/// type and <c>I</c> is an unsigned integer-like type. +/// <para>If you do not supply a projection function, a default projection function which simply returns the element is used for integral types. The function +/// will fail to compile if the element is not an integral type in the absence of a projection function.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator> +inline void parallel_radixsort(const _Random_iterator &_Begin, const _Random_iterator &_End) +{ + _Allocator _Alloc; + return parallel_radixsort<_Allocator, _Random_iterator>(_Alloc, _Begin, _End); +} + +/// <summary> +/// Arranges elements in a specified range into an non descending order using a radix sorting algorithm. This is a stable sort function which requires a +/// projection function that can project elements to be sorted into unsigned integer-like keys. Default initialization is required for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <param name="_Alloc"> +/// An instance of an STL compatible memory allocator. +/// </param> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// An unary projection functor with the signature<c>I _Proj_func(T)</c> is required to return a key when given an element, where <c>T</c> is the element +/// type and <c>I</c> is an unsigned integer-like type. +/// <para>If you do not supply a projection function, a default projection function which simply returns the element is used for integral types. The function +/// will fail to compile if the element is not an integral type in the absence of a projection function.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator> +inline void parallel_radixsort(const _Allocator& _Alloc, const _Random_iterator &_Begin, const _Random_iterator &_End) +{ + typedef typename std::iterator_traits<_Random_iterator>::value_type _DataType; + + _Radix_sort_default_function<_DataType> _Proj_func; + + parallel_radixsort<_Allocator>(_Alloc, _Begin, _End, _Proj_func); +} + +/// <summary> +/// Arranges elements in a specified range into an non descending order using a radix sorting algorithm. This is a stable sort function which requires a +/// projection function that can project elements to be sorted into unsigned integer-like keys. Default initialization is required for the elements being sorted. +/// </summary> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the projection function. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <param name="_Proj_func"> +/// A user-defined projection function object that converts an element into an integral value. +/// </param> +/// <param name="_Chunk_size"> +/// The mimimum size of a chunk that will be split into two for parallel execution. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// An unary projection functor with the signature<c>I _Proj_func(T)</c> is required to return a key when given an element, where <c>T</c> is the element +/// type and <c>I</c> is an unsigned integer-like type. +/// <para>If you do not supply a projection function, a default projection function which simply returns the element is used for integral types. The function +/// will fail to compile if the element is not an integral type in the absence of a projection function.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Random_iterator, typename _Function> +inline void parallel_radixsort(const _Random_iterator &_Begin, const _Random_iterator &_End, const _Function &_Proj_func, const size_t _Chunk_size = 256 * 256) +{ + parallel_radixsort<std::allocator<typename std::iterator_traits<_Random_iterator>::value_type>>( + _Begin, _End, _Proj_func, _Chunk_size); +} + +/// <summary> +/// Arranges elements in a specified range into an non descending order using a radix sorting algorithm. This is a stable sort function which requires a +/// projection function that can project elements to be sorted into unsigned integer-like keys. Default initialization is required for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the projection function. +/// </typeparam> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <param name="_Proj_func"> +/// A user-defined projection function object that converts an element into an integral value. +/// </param> +/// <param name="_Chunk_size"> +/// The mimimum size of a chunk that will be split into two for parallel execution. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// An unary projection functor with the signature<c>I _Proj_func(T)</c> is required to return a key when given an element, where <c>T</c> is the element +/// type and <c>I</c> is an unsigned integer-like type. +/// <para>If you do not supply a projection function, a default projection function which simply returns the element is used for integral types. The function +/// will fail to compile if the element is not an integral type in the absence of a projection function.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator, typename _Function> +inline void parallel_radixsort(const _Random_iterator &_Begin, const _Random_iterator &_End, const _Function &_Proj_func, const size_t _Chunk_size = 256 * 256) +{ + _Allocator _Alloc; + return parallel_radixsort<_Allocator, _Random_iterator, _Function>(_Alloc, _Begin, _End, _Proj_func, _Chunk_size); +} + +/// <summary> +/// Arranges elements in a specified range into an non descending order using a radix sorting algorithm. This is a stable sort function which requires a +/// projection function that can project elements to be sorted into unsigned integer-like keys. Default initialization is required for the elements being sorted. +/// </summary> +/// <typeparam name="_Allocator"> +/// The type of an STL compatible memory allocator. +/// </typeparam> +/// <typeparam name="_Random_iterator"> +/// The iterator type of the input range. +/// </typeparam> +/// <typeparam name="_Function"> +/// The type of the projection function. +/// </typeparam> +/// <param name="_Alloc"> +/// An instance of an STL compatible memory allocator. +/// </param> +/// <param name="_Begin"> +/// A random-access iterator addressing the position of the first element in the range to be sorted. +/// </param> +/// <param name="_End"> +/// A random-access iterator addressing the position one past the final element in the range to be sorted. +/// </param> +/// <param name="_Proj_func"> +/// A user-defined projection function object that converts an element into an integral value. +/// </param> +/// <param name="_Chunk_size"> +/// The mimimum size of a chunk that will be split into two for parallel execution. +/// </param> +/// <remarks> +/// All overloads require <c>n * sizeof(T)</c> additional space, where <c>n</c> is the number of elements to be sorted, and <c>T</c> is the element type. +/// An unary projection functor with the signature<c>I _Proj_func(T)</c> is required to return a key when given an element, where <c>T</c> is the element +/// type and <c>I</c> is an unsigned integer-like type. +/// <para>If you do not supply a projection function, a default projection function which simply returns the element is used for integral types. The function +/// will fail to compile if the element is not an integral type in the absence of a projection function.</para> +/// <para>If you do not supply an allocator type or instance, the STL memory allocator <c>std::allocator<T></c> is used to allocate the buffer.</para> +/// <para>The algorithm divides the input range into two chunks and successively divides each chunk into two sub-chunks for execution in parallel. The optional +/// argument <paramref name="_Chunk_size"/> can be used to indicate to the algorithm that it should handles chunks of size < <paramref name="_Chunk_size"/> +/// serially.</para> +/// </remarks> +/**/ +template<typename _Allocator, typename _Random_iterator, typename _Function> +inline void parallel_radixsort(const _Allocator& _Alloc, const _Random_iterator &_Begin, const _Random_iterator &_End, const _Function &_Proj_func, const size_t _Chunk_size = 256 * 256) +{ + _CONCRT_ASSERT(_Chunk_size > 0); + + // Check for cancellation before the algorithm starts. + interruption_point(); + + size_t _Size = _End - _Begin; + + // If _Size <= 1, no more sorting needs to be done. + if (_Size <= 1) + { + return; + } + + _AllocatedBufferHolder<_Allocator> _Holder(_Size, _Alloc); + + // Prevent cancellation from happening during the algorithm in case it leaves the buffers in unknown state. + run_with_cancellation_token([&]() { + _Parallel_integer_sort_asc(_Begin, _Size, stdext::make_unchecked_array_iterator(_Holder._Get_buffer()), _Proj_func, _Chunk_size); + }, cancellation_token::none()); +} + +#pragma pop_macro("_SORT_MAX_RECURSION_DEPTH") +#pragma pop_macro("_MAX_NUM_TASKS_PER_CORE") +#pragma pop_macro("_FINE_GRAIN_CHUNK_SIZE") +} + +namespace concurrency = Concurrency; + +#pragma pop_macro("new") +#pragma pack(pop) diff --git a/test_data/lots_of_files/pplcancellation_token.h b/test_data/lots_of_files/pplcancellation_token.h new file mode 100644 index 0000000..a33b062 --- /dev/null +++ b/test_data/lots_of_files/pplcancellation_token.h @@ -0,0 +1,994 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* pplxcancellation_token.h +* +* Parallel Patterns Library : cancellation_token +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLCONCRT_H +#error This header must not be included directly +#endif + +#ifndef _PPLCANCELLATION_TOKEN_H +#define _PPLCANCELLATION_TOKEN_H + +#include <pplinterface.h> + +#pragma pack(push,_CRT_PACKING) +// All header files are required to be protected from the macro new +#pragma push_macro("new") +#undef new + +namespace Concurrency +{ + +namespace details +{ + + // Base class for all reference counted objects + class _RefCounter + { + public: + + virtual ~_RefCounter() + { + _ASSERTE(_M_refCount == 0); + } + + // Acquires a reference + // Returns the new reference count. + long _Reference() + { + long _Refcount = _InterlockedIncrement(&_M_refCount); + + // 0 - 1 transition is illegal + _ASSERTE(_Refcount > 1); + return _Refcount; + } + + // Releases the reference + // Returns the new reference count + long _Release() + { + long _Refcount = _InterlockedDecrement(&_M_refCount); + _ASSERTE(_Refcount >= 0); + + if (_Refcount == 0) + { + _Destroy(); + } + + return _Refcount; + } + + protected: + + // Allow derived classes to provide their own deleter + virtual void _Destroy() + { + delete this; + } + + // Only allow instantiation through derived class + _RefCounter(long _InitialCount = 1) : _M_refCount(_InitialCount) + { + _ASSERTE(_M_refCount > 0); + } + + // Reference count + volatile long _M_refCount; + }; + + class _CancellationTokenState; + + class _CancellationTokenRegistration : public _RefCounter + { + private: + + static const long _STATE_CLEAR = 0; + static const long _STATE_DEFER_DELETE = 1; + static const long _STATE_SYNCHRONIZE = 2; + static const long _STATE_CALLED = 3; + + public: + + _CancellationTokenRegistration(long _InitialRefs = 1) : + _RefCounter(_InitialRefs), + _M_state(_STATE_CALLED), + _M_pTokenState(NULL) + { + } + + _CancellationTokenState *_GetToken() const + { + return _M_pTokenState; + } + + protected: + + virtual ~_CancellationTokenRegistration() + { + _ASSERTE(_M_state != _STATE_CLEAR); + } + + virtual void _Exec() = 0; + + private: + + friend class _CancellationTokenState; + + void _Invoke() + { + long tid = ::Concurrency::details::platform::GetCurrentThreadId(); + _ASSERTE((tid & 0x3) == 0); // If this ever fires, we need a different encoding for this. + + long result = atomic_compare_exchange(_M_state, tid, _STATE_CLEAR); + + if (result == _STATE_CLEAR) + { + _Exec(); + + result = atomic_compare_exchange(_M_state, _STATE_CALLED, tid); + + if (result == _STATE_SYNCHRONIZE) + { + _M_pSyncBlock->set(); + } + } + _Release(); + } + + atomic_long _M_state; + extensibility::event_t *_M_pSyncBlock; + _CancellationTokenState *_M_pTokenState; + }; + + template<typename _Function> + class _CancellationTokenCallback : public _CancellationTokenRegistration + { + public: + + _CancellationTokenCallback(const _Function& _Func) : + _M_function(_Func) + { + } + + protected: + + virtual void _Exec() + { + _M_function(); + } + + private: + + _Function _M_function; + }; + + class CancellationTokenRegistration_TaskProc : public _CancellationTokenRegistration + { + public: + + CancellationTokenRegistration_TaskProc(TaskProc_t proc, _In_ void *pData, int initialRefs) : + _CancellationTokenRegistration(initialRefs), m_proc(proc), m_pData(pData) + { + } + + protected: + + virtual void _Exec() + { + m_proc(m_pData); + } + + private: + + TaskProc_t m_proc; + void *m_pData; + + }; + + // The base implementation of a cancellation token. + class _CancellationTokenState : public _RefCounter + { + protected: + class TokenRegistrationContainer + { + private: + typedef struct _Node { + _CancellationTokenRegistration* _M_token; + _Node *_M_next; + } Node; + + public: + TokenRegistrationContainer() : _M_begin(nullptr), _M_last(nullptr) + { + } + + ~TokenRegistrationContainer() + { + auto node = _M_begin; + while (node != nullptr) + { + Node* tmp = node; + node = node->_M_next; + ::free(tmp); + } + } + + void swap(TokenRegistrationContainer& list) + { + std::swap(list._M_begin, _M_begin); + std::swap(list._M_last, _M_last); + } + + bool empty() + { + return _M_begin == nullptr; + } + + template<typename T> + void for_each(T lambda) + { + Node* node = _M_begin; + + while (node != nullptr) + { + lambda(node->_M_token); + node = node->_M_next; + } + } + + void push_back(_CancellationTokenRegistration* token) + { + Node* node = reinterpret_cast<Node*>(::malloc(sizeof(Node))); + if (node == nullptr) + { + throw ::std::bad_alloc(); + } + + node->_M_token = token; + node->_M_next = nullptr; + + if (_M_begin == nullptr) + { + _M_begin = node; + } + else + { + _M_last->_M_next = node; + } + + _M_last = node; + } + + void remove(_CancellationTokenRegistration* token) + { + Node* node = _M_begin; + Node* prev = nullptr; + + while (node != nullptr) + { + if (node->_M_token == token) { + if (prev == nullptr) + { + _M_begin = node->_M_next; + } + else + { + prev->_M_next = node->_M_next; + } + + if (node->_M_next == nullptr) + { + _M_last = prev; + } + + ::free(node); + break; + } + + prev = node; + node = node->_M_next; + } + } + + private: + Node *_M_begin; + Node *_M_last; + }; + + public: + + static _CancellationTokenState * _CancellationTokenState::_NewTokenState() + { + return new _CancellationTokenState(); + } + + static _CancellationTokenState *_None() + { + return reinterpret_cast<_CancellationTokenState *>(2); + } + + static bool _IsValid(_In_opt_ _CancellationTokenState *_PToken) + { + return (_PToken != NULL && _PToken != _None()); + } + + _CancellationTokenState() : + _M_stateFlag(0) + { + } + + ~_CancellationTokenState() + { + TokenRegistrationContainer rundownList; + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + _M_registrations.swap(rundownList); + } + + rundownList.for_each([](_CancellationTokenRegistration * pRegistration) + { + pRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; + pRegistration->_Release(); + }); + } + + bool _IsCanceled() const + { + return (_M_stateFlag != 0); + } + + void _Cancel() + { + if (atomic_compare_exchange(_M_stateFlag, 1l, 0l) == 0) + { + TokenRegistrationContainer rundownList; + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + _M_registrations.swap(rundownList); + } + + rundownList.for_each([](_CancellationTokenRegistration * pRegistration) + { + pRegistration->_Invoke(); + }); + + _M_stateFlag = 2; + _M_cancelComplete.set(); + } + } + + _CancellationTokenRegistration *_RegisterCallback(TaskProc_t _PCallback, _In_ void *_PData, int _InitialRefs = 1) + { + _CancellationTokenRegistration *pRegistration = new CancellationTokenRegistration_TaskProc(_PCallback, _PData, _InitialRefs); + _RegisterCallback(pRegistration); + return pRegistration; + } + + void _RegisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) + { + _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_CLEAR; + _PRegistration->_Reference(); + _PRegistration->_M_pTokenState = this; + + bool invoke = true; + + if (!_IsCanceled()) + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + + if (!_IsCanceled()) + { + invoke = false; + _M_registrations.push_back(_PRegistration); + } + } + + if (invoke) + { + _PRegistration->_Invoke(); + } + } + + void _DeregisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) + { + bool synchronize = false; + + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + + // + // If a cancellation has occurred, the registration list is guaranteed to be empty if we've observed it under the auspices of the + // lock. In this case, we must synchronize with the cancelling thread to guarantee that the cancellation is finished by the time + // we return from this method. + // + if (!_M_registrations.empty()) + { + _M_registrations.remove(_PRegistration); + _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; + _PRegistration->_Release(); + } + else + { + synchronize = true; + } + } + + // + // If the list is empty, we are in one of several situations: + // + // - The callback has already been made --> do nothing + // - The callback is about to be made --> flag it so it doesn't happen and return + // - The callback is in progress elsewhere --> synchronize with it + // - The callback is in progress on this thread --> do nothing + // + if (synchronize) + { + long result = atomic_compare_exchange( + _PRegistration->_M_state, + _CancellationTokenRegistration::_STATE_DEFER_DELETE, + _CancellationTokenRegistration::_STATE_CLEAR + ); + + switch(result) + { + case _CancellationTokenRegistration::_STATE_CLEAR: + case _CancellationTokenRegistration::_STATE_CALLED: + break; + case _CancellationTokenRegistration::_STATE_DEFER_DELETE: + case _CancellationTokenRegistration::_STATE_SYNCHRONIZE: + _ASSERTE(false); + break; + default: + { + long tid = result; + if (tid == ::Concurrency::details::platform::GetCurrentThreadId()) + { + // + // It is entirely legal for a caller to Deregister during a callback instead of having to provide their own synchronization + // mechanism between the two. In this case, we do *NOT* need to explicitly synchronize with the callback as doing so would + // deadlock. If the call happens during, skip any extra synchronization. + // + break; + } + + extensibility::event_t ev; + _PRegistration->_M_pSyncBlock = &ev; + + long result_1 = atomic_exchange(_PRegistration->_M_state, _CancellationTokenRegistration::_STATE_SYNCHRONIZE); + + if (result_1 != _CancellationTokenRegistration::_STATE_CALLED) + { + _PRegistration->_M_pSyncBlock->wait(::Concurrency::extensibility::event_t::timeout_infinite); + } + + break; + } + } + } + } + + private: + + // The flag for the token state (whether it is canceled or not) + atomic_long _M_stateFlag; + + // Notification of completion of cancellation of this token. + extensibility::event_t _M_cancelComplete; // Hmm.. where do we wait for it?? + + // Lock to protect the registrations list + extensibility::critical_section_t _M_listLock; + + // The protected list of registrations + TokenRegistrationContainer _M_registrations; + }; + +} // namespace details + +class cancellation_token_source; +class cancellation_token; + + +/// <summary> +/// The <c>cancellation_token_registration</c> class represents a callback notification from a <c>cancellation_token</c>. When the <c>register</c> +/// method on a <c>cancellation_token</c> is used to receive notification of when cancellation occurs, a <c>cancellation_token_registration</c> +/// object is returned as a handle to the callback so that the caller can request a specific callback no longer be made through use of +/// the <c>deregister</c> method. +/// </summary> +class cancellation_token_registration +{ +public: + + cancellation_token_registration() : + _M_pRegistration(NULL) + { + } + + ~cancellation_token_registration() + { + _Clear(); + } + + cancellation_token_registration(const cancellation_token_registration& _Src) + { + _Assign(_Src._M_pRegistration); + } + + cancellation_token_registration(cancellation_token_registration&& _Src) + { + _Move(_Src._M_pRegistration); + } + + cancellation_token_registration& operator=(const cancellation_token_registration& _Src) + { + if (this != &_Src) + { + _Clear(); + _Assign(_Src._M_pRegistration); + } + return *this; + } + + cancellation_token_registration& operator=(cancellation_token_registration&& _Src) + { + if (this != &_Src) + { + _Clear(); + _Move(_Src._M_pRegistration); + } + return *this; + } + + bool operator==(const cancellation_token_registration& _Rhs) const + { + return _M_pRegistration == _Rhs._M_pRegistration; + } + + bool operator!=(const cancellation_token_registration& _Rhs) const + { + return !(operator==(_Rhs)); + } + +private: + + friend class cancellation_token; + + cancellation_token_registration(_In_ details::_CancellationTokenRegistration *_PRegistration) : + _M_pRegistration(_PRegistration) + { + } + + void _Clear() + { + if (_M_pRegistration != NULL) + { + _M_pRegistration->_Release(); + } + _M_pRegistration = NULL; + } + + void _Assign(_In_ details::_CancellationTokenRegistration *_PRegistration) + { + if (_PRegistration != NULL) + { + _PRegistration->_Reference(); + } + _M_pRegistration = _PRegistration; + } + + void _Move(_In_ details::_CancellationTokenRegistration *&_PRegistration) + { + _M_pRegistration = _PRegistration; + _PRegistration = NULL; + } + + details::_CancellationTokenRegistration *_M_pRegistration; +}; + + +/// <summary> +/// The <c>cancellation_token</c> class represents the ability to determine whether some operation has been requested to cancel. A given token can +/// be associated with a <c>task_group</c>, <c>structured_task_group</c>, or <c>task</c> to provide implicit cancellation. It can also be polled for +/// cancellation or have a callback registered for if and when the associated <c>cancellation_token_source</c> is canceled. +/// </summary> +class cancellation_token +{ +public: + + typedef details::_CancellationTokenState * _ImplType; + + /// <summary> + /// Returns a cancellation token which can never be subject to cancellation. + /// </summary> + /// <returns> + /// A cancellation token that cannot be canceled. + /// </returns> + static cancellation_token none() + { + return cancellation_token(); + } + + cancellation_token(const cancellation_token& _Src) + { + _Assign(_Src._M_Impl); + } + + cancellation_token(cancellation_token&& _Src) + { + _Move(_Src._M_Impl); + } + + cancellation_token& operator=(const cancellation_token& _Src) + { + if (this != &_Src) + { + _Clear(); + _Assign(_Src._M_Impl); + } + return *this; + } + + cancellation_token& operator=(cancellation_token&& _Src) + { + if (this != &_Src) + { + _Clear(); + _Move(_Src._M_Impl); + } + return *this; + } + + bool operator==(const cancellation_token& _Src) const + { + return _M_Impl == _Src._M_Impl; + } + + bool operator!=(const cancellation_token& _Src) const + { + return !(operator==(_Src)); + } + + ~cancellation_token() + { + _Clear(); + } + + /// <summary> + /// Returns an indication of whether this token can be canceled or not. + /// </summary> + /// <returns> + /// An indication of whether this token can be canceled or not. + /// </returns> + bool is_cancelable() const + { + return (_M_Impl != NULL); + } + + /// <summary> + /// Returns <c>true</c> if the token has been canceled. + /// </summary> + /// <returns> + /// The value <c>true</c> if the token has been canceled; otherwise, the value <c>false</c>. + /// </returns> + bool is_canceled() const + { + return (_M_Impl != NULL && _M_Impl->_IsCanceled()); + } + + /// <summary> + /// Registers a callback function with the token. If and when the token is canceled, the callback will be made. Note that if the token + /// is already canceled at the point where this method is called, the callback will be made immediately and synchronously. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be called back when this <c>cancellation_token</c> is canceled. + /// </typeparam> + /// <param name="_Func"> + /// The function object that will be called back when this <c>cancellation_token</c> is canceled. + /// </param> + /// <returns> + /// A <c>cancellation_token_registration</c> object which can be utilized in the <c>deregister</c> method to deregister a previously registered + /// callback and prevent it from being made. The method will throw an <see cref="invalid_operation Class">invalid_operation </see> exception if + /// it is called on a <c>cancellation_token</c> object that was created using the <see cref="cancellation_token::none Method">cancellation_token::none </see> + /// method. + /// </returns> + template<typename _Function> + ::Concurrency::cancellation_token_registration register_callback(const _Function& _Func) const + { + if (_M_Impl == NULL) + { + // A callback cannot be registered if the token does not have an associated source. + throw invalid_operation(); + } +#pragma warning(suppress: 28197) + details::_CancellationTokenCallback<_Function> *_PCallback = new details::_CancellationTokenCallback<_Function>(_Func); + _M_Impl->_RegisterCallback(_PCallback); + return cancellation_token_registration(_PCallback); + } + + /// <summary> + /// Removes a callback previously registered via the <c>register</c> method based on the <c>cancellation_token_registration</c> object returned + /// at the time of registration. + /// </summary> + /// <param name="_Registration"> + /// The <c>cancellation_token_registration</c> object corresponding to the callback to be deregistered. This token must have been previously + /// returned from a call to the <c>register</c> method. + /// </param> + void deregister_callback(const cancellation_token_registration& _Registration) const + { + _M_Impl->_DeregisterCallback(_Registration._M_pRegistration); + } + + _ImplType _GetImpl() const + { + return _M_Impl; + } + + _ImplType _GetImplValue() const + { + return (_M_Impl == NULL) ? ::Concurrency::details::_CancellationTokenState::_None() : _M_Impl; + } + + static cancellation_token _FromImpl(_ImplType _Impl) + { + return cancellation_token(_Impl); + } + +private: + + friend class cancellation_token_source; + + _ImplType _M_Impl; + + void _Clear() + { + if (_M_Impl != NULL) + { + _M_Impl->_Release(); + } + _M_Impl = NULL; + } + + void _Assign(_ImplType _Impl) + { + if (_Impl != NULL) + { + _Impl->_Reference(); + } + _M_Impl = _Impl; + } + + void _Move(_ImplType &_Impl) + { + _M_Impl = _Impl; + _Impl = NULL; + } + + cancellation_token() : + _M_Impl(NULL) + { + } + + cancellation_token(_ImplType _Impl) : + _M_Impl(_Impl) + { + if (_M_Impl == ::Concurrency::details::_CancellationTokenState::_None()) + { + _M_Impl = NULL; + } + + if (_M_Impl != NULL) + { + _M_Impl->_Reference(); + } + } +}; + +/// <summary> +/// The <c>cancellation_token_source</c> class represents the ability to cancel some cancelable operation. +/// </summary> +class cancellation_token_source +{ +public: + + typedef ::Concurrency::details::_CancellationTokenState * _ImplType; + + /// <summary> + /// Constructs a new <c>cancellation_token_source</c>. The source can be used to flag cancellation of some cancelable operation. + /// </summary> + cancellation_token_source() + { + _M_Impl = new ::Concurrency::details::_CancellationTokenState; + } + + cancellation_token_source(const cancellation_token_source& _Src) + { + _Assign(_Src._M_Impl); + } + + cancellation_token_source(cancellation_token_source&& _Src) + { + _Move(_Src._M_Impl); + } + + cancellation_token_source& operator=(const cancellation_token_source& _Src) + { + if (this != &_Src) + { + _Clear(); + _Assign(_Src._M_Impl); + } + return *this; + } + + cancellation_token_source& operator=(cancellation_token_source&& _Src) + { + if (this != &_Src) + { + _Clear(); + _Move(_Src._M_Impl); + } + return *this; + } + + bool operator==(const cancellation_token_source& _Src) const + { + return _M_Impl == _Src._M_Impl; + } + + bool operator!=(const cancellation_token_source& _Src) const + { + return !(operator==(_Src)); + } + + ~cancellation_token_source() + { + if (_M_Impl != NULL) + { + _M_Impl->_Release(); + } + } + + /// <summary> + /// Returns a cancellation token associated with this source. The returned token can be polled for cancellation + /// or provide a callback if and when cancellation occurs. + /// </summary> + /// <returns> + /// A cancellation token associated with this source. + /// </returns> + cancellation_token get_token() const + { + return cancellation_token(_M_Impl); + } + + /// <summary> + /// Creates a <c>cancellation_token_source</c> which is canceled when the provided token is canceled. + /// </summary> + /// <param name="_Src"> + /// A token whose cancellation will cause cancellation of the returned token source. Note that the returned token source can also be canceled + /// independently of the source contained in this parameter. + /// </param> + /// <returns> + /// A <c>cancellation_token_source</c> which is canceled when the token provided by the <paramref name="_Src"/> parameter is canceled. + /// </returns> + static cancellation_token_source create_linked_source(cancellation_token& _Src) + { + cancellation_token_source newSource; + _Src.register_callback( [newSource](){ newSource.cancel(); } ); + return newSource; + } + + /// <summary> + /// Creates a <c>cancellation_token_source</c> which is canceled when one of a series of tokens represented by an STL iterator + /// pair is canceled. + /// </summary> + /// <param name="_Begin"> + /// The STL iterator corresponding to the beginning of the range of tokens to listen for cancellation of. + /// </param> + /// <param name="_End"> + /// The STL iterator corresponding to the ending of the range of tokens to listen for cancellation of. + /// </param> + /// <returns> + /// A <c>cancellation_token_source</c> which is canceled when any of the tokens provided by the range described by the STL iterators + /// contained in the <paramref name="_Begin"/> and <paramref name="_End"/> parameters is canceled. + /// </returns> + template<typename _Iter> + static cancellation_token_source create_linked_source(_Iter _Begin, _Iter _End) + { + cancellation_token_source newSource; + for (_Iter _It = _Begin; _It != _End; ++_It) + { + _It->register_callback( [newSource](){ newSource.cancel(); } ); + } + return newSource; + } + + /// <summary> + /// Cancels the token. Any <c>task_group</c>, <c>structured_task_group</c>, or <c>task</c> which utilizes the token will be + /// canceled upon this call and throw an exception at the next interruption point. + /// </summary> + void cancel() const + { + _M_Impl->_Cancel(); + } + + _ImplType _GetImpl() const + { + return _M_Impl; + } + + static cancellation_token_source _FromImpl(_ImplType _Impl) + { + return cancellation_token_source(_Impl); + } + +private: + + _ImplType _M_Impl; + + void _Clear() + { + if (_M_Impl != NULL) + { + _M_Impl->_Release(); + } + _M_Impl = NULL; + } + + void _Assign(_ImplType _Impl) + { + if (_Impl != NULL) + { + _Impl->_Reference(); + } + _M_Impl = _Impl; + } + + void _Move(_ImplType &_Impl) + { + _M_Impl = _Impl; + _Impl = NULL; + } + + cancellation_token_source(_ImplType _Impl) : + _M_Impl(_Impl) + { + if (_M_Impl == ::Concurrency::details::_CancellationTokenState::_None()) + { + _M_Impl = NULL; + } + + if (_M_Impl != NULL) + { + _M_Impl->_Reference(); + } + } +}; + +} // namespace Concurrency + +#pragma pop_macro("new") +#pragma pack(pop) + +#endif // _PPLCANCELLATION_TOKEN_H diff --git a/test_data/lots_of_files/pplconcrt.h b/test_data/lots_of_files/pplconcrt.h new file mode 100644 index 0000000..d8693a7 --- /dev/null +++ b/test_data/lots_of_files/pplconcrt.h @@ -0,0 +1,294 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* pplconcrt.h +* +* Parallel Patterns Library - PPL ConcRT helpers +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLCONCRT_H +#define _PPLCONCRT_H + +#include <pplinterface.h> +#include <concrt.h> + +#define _noexcept + +namespace Concurrency +{ +// The extensibility namespace contains the type definitions that are used by ppltasks implementation +namespace extensibility +{ + typedef ::Concurrency::event event_t; + + typedef ::Concurrency::critical_section critical_section_t; + typedef critical_section_t::scoped_lock scoped_critical_section_t; + + typedef ::Concurrency::reader_writer_lock reader_writer_lock_t; + typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t; + typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t; + + typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t; + typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t; +} + +_CRTIMP2 bool __cdecl is_current_task_group_canceling(); + +namespace details +{ + namespace platform + { + _CRTIMP2 unsigned int __cdecl GetNextAsyncId(); + _CRTIMP size_t __cdecl CaptureCallstack(void **stackData, size_t skipFrames, size_t captureFrames); + _CRTIMP long __cdecl GetCurrentThreadId(); + } +} + +} // Concurrency + +#include <pplcancellation_token.h> +#include <ppl.h> + +namespace Concurrency +{ + +inline std::shared_ptr< ::Concurrency::scheduler_interface> get_ambient_scheduler() +{ + return nullptr; +} + +inline void set_ambient_scheduler(std::shared_ptr< ::Concurrency::scheduler_interface> _Scheduler) +{ + throw invalid_operation("Scheduler is already initialized"); +} + +namespace details +{ + +// It has to be a macro because the debugger needs __debugbreak +// breaks on the frame with exception pointer. +// It can be only used within _ExceptionHolder +#ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION +#define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() do { \ + ReportUnhandledError(); \ + __debugbreak(); \ + Concurrency::details::_ReportUnobservedException(); \ +} while(false) + +#endif + + template<typename _T> + struct _AutoDeleter + { + _AutoDeleter(_T *_PPtr) : _Ptr(_PPtr) {} + ~_AutoDeleter () { delete _Ptr; } + _T *_Ptr; + }; + + struct _TaskProcHandle : Concurrency::details::_UnrealizedChore + { + _TaskProcHandle() + { + this->m_pFunction = &Concurrency::details::_UnrealizedChore::_InvokeBridge<_TaskProcHandle>; + this->_SetRuntimeOwnsLifetime(true); + } + + virtual ~_TaskProcHandle() {} + virtual void invoke() const = 0; + + void operator()() const + { + this->invoke(); + } + + static void __cdecl _RunChoreBridge(void * _Parameter) + { + auto _PTaskHandle = static_cast<_TaskProcHandle *>(_Parameter); + _AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle); + _PTaskHandle->invoke(); + } + }; + + // This is an abstraction that is built on top of the scheduler to provide these additional functionalities + // - Ability to wait on a work item + // - Ability to cancel a work item + // - Ability to inline work on invocation of RunAndWait + // The concrt specific implementation provided the following additional features + // - Interoperate with concrt task groups and ppl parallel_for algorithms for cancellation + // - Stack guard + // - Determine if the current task is cancelled + class _TaskCollectionImpl + { + public: + + typedef Concurrency::details::_TaskProcHandle _TaskProcHandle_t; + + _TaskCollectionImpl(::Concurrency::scheduler_ptr _PScheduler) + : _M_pTaskCollection(nullptr), _M_pScheduler(_PScheduler) + { + } + + ~_TaskCollectionImpl() + { + if (_M_pTaskCollection != nullptr) + { + _M_pTaskCollection->_Release(); + _M_pTaskCollection = nullptr; + } + } + + void _ScheduleTask(_TaskProcHandle_t* _Parameter, _TaskInliningMode _InliningMode) + { + if (!_M_pScheduler) + { + // Construct the task collection; We use none token to provent it becoming interruption point. + _M_pTaskCollection = _AsyncTaskCollection::_NewCollection(::Concurrency::details::_CancellationTokenState::_None()); + } + + try + { + if (_M_pTaskCollection != nullptr) + { + // Do not need to check its returning state, more details please refer to _Wait method. + auto _PChore = static_cast< ::Concurrency::details::_UnrealizedChore*>(_Parameter); + _M_pTaskCollection->_ScheduleWithAutoInline(_PChore, _InliningMode); + } + else + { + // Schedule the work on the user provided scheduler + if (_InliningMode == _ForceInline) + { + _TaskProcHandle_t::_RunChoreBridge(_Parameter); + } + else + { + _M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _Parameter); + } + } + } + catch(...) + { + _SetScheduled(); + throw; + } + + // Set the event in case anyone is waiting to notify that this task has been scheduled. In the case where we + // execute the chore inline, the event should be set after the chore has executed, to prevent a different thread + // performing a wait on the task from waiting on the task collection before the chore is actually added to it, + // and thereby returning from the wait() before the chore has executed. + _SetScheduled(); + } + + void _Cancel() + { + // Ensure that RunAndWait makes progress. + _SetScheduled(); + + if (_M_pTaskCollection != nullptr) + { + _M_pTaskCollection->_Cancel(); + } + } + + void _RunAndWait() + { + _M_Scheduled.wait(); + + if (_M_pTaskCollection != nullptr) + { + // When it returns cancelled, either work chore or the cancel thread should already have set task's state + // properly -- cancelled state or completed state (because there was no interruption point). + // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. + _M_pTaskCollection->_RunAndWait(); + } + else + { + _M_Completed.wait(); + } + } + + void _Wait() + { + _M_Completed.wait(); + } + + void _Complete() + { + // Ensure that RunAndWait makes progress. + _SetScheduled(); + _M_Completed.set(); + } + + ::Concurrency::scheduler_ptr _GetScheduler() const + { + return _M_pScheduler; + } + + // Fire and forget + static void _RunTask(TaskProc _Proc, void * _Parameter, _TaskInliningMode _InliningMode) + { + Concurrency::details::_StackGuard _Guard; + + if (_Guard._ShouldInline(_InliningMode)) + { + _Proc(_Parameter); + } + else + { + // Schedule the work on the current scheduler + _CurrentScheduler::_ScheduleTask(_Proc, _Parameter); + } + } + + static bool __cdecl _Is_cancellation_requested() + { + // ConcRT scheduler under the hood is using TaskCollection, which is same as task_group + return ::Concurrency::is_current_task_group_canceling(); + } + + private: + void _SetScheduled() + { + _M_Scheduled.set(); + } + + ::Concurrency::extensibility::event_t _M_Scheduled; + ::Concurrency::extensibility::event_t _M_Completed; + _AsyncTaskCollection* _M_pTaskCollection; + ::Concurrency::scheduler_ptr _M_pScheduler; + }; + + // For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the + // lambda. + struct _Task_generator_oversubscriber + { + _Task_generator_oversubscriber() + { + _Context::_Oversubscribe(true); + } + + ~_Task_generator_oversubscriber() + { + _Context::_Oversubscribe(false); + } + }; + + typedef ::Concurrency::details::_TaskCollectionImpl _TaskCollection_t; + typedef ::Concurrency::details::_TaskInliningMode _TaskInliningMode_t; + typedef ::Concurrency::details::_Task_generator_oversubscriber _Task_generator_oversubscriber_t; +} // details + +} // Concurrency + +namespace concurrency = Concurrency; + +#endif // _PPLCONCRT_H diff --git a/test_data/lots_of_files/pplinterface.h b/test_data/lots_of_files/pplinterface.h new file mode 100644 index 0000000..dfc7afd --- /dev/null +++ b/test_data/lots_of_files/pplinterface.h @@ -0,0 +1,238 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* pplinterface.h +* +* Parallel Patterns Library - PPL interface definitions +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLINTERFACE_H +#define _PPLINTERFACE_H + +#if defined(_CRTBLD) +#elif defined(_MS_WINDOWS) +#if (_MSC_VER >= 1700) +#define _USE_REAL_ATOMICS +#endif +#else // GCC compiler +#define _USE_REAL_ATOMICS +#endif + +#include <memory> +#ifdef _USE_REAL_ATOMICS +#include <atomic> +#endif + +namespace Concurrency +{ + +/// <summary> +/// An elementary abstraction for a task, defined as <c>void (__cdecl * TaskProc_t)(void *)</c>. A <c>TaskProc</c> is called to +/// invoke the body of a task. +/// </summary> +/**/ +typedef void (__cdecl * TaskProc_t)(void *); + +/// <summary> +/// Scheduler Interface +/// </summary> +struct __declspec(novtable) scheduler_interface +{ + virtual void schedule( TaskProc_t, void* ) = 0; +}; + +/// <summary> +/// Represents a pointer to a scheduler. This class exists to allow the +/// the specification of a shared lifetime by using shared_ptr or just +/// a plain reference by using raw pointer. +/// </summary> +struct scheduler_ptr +{ + /// <summary> + /// Creates a scheduler pointer from shared_ptr to scheduler + /// </summary> + explicit scheduler_ptr(std::shared_ptr<scheduler_interface> scheduler) : m_sharedScheduler(std::move(scheduler)) + { + m_scheduler = m_sharedScheduler.get(); + } + + /// <summary> + /// Creates a scheduler pointer from raw pointer to scheduler + /// </summary> + explicit scheduler_ptr(_In_opt_ scheduler_interface * pScheduler) : m_scheduler(pScheduler) + { + } + + /// <summary> + /// Behave like a pointer + /// </summary> + scheduler_interface *operator->() const + { + return get(); + } + + /// <summary> + /// Returns the raw pointer to the scheduler + /// </summary> + scheduler_interface * get() const + { + return m_scheduler; + } + + /// <summary> + /// Test whether the scheduler pointer is non-null + /// </summary> + operator bool() const { return get() != nullptr; } + +private: + + std::shared_ptr<scheduler_interface> m_sharedScheduler; + scheduler_interface * m_scheduler; +}; + + +/// <summary> +/// Describes the execution status of a <c>task_group</c> or <c>structured_task_group</c> object. A value of this type is returned +/// by numerous methods that wait on tasks scheduled to a task group to complete. +/// </summary> +/// <seealso cref="task_group Class"/> +/// <seealso cref="task_group::wait Method"/> +/// <seealso cref="task_group::run_and_wait Method"/> +/// <seealso cref="structured_task_group Class"/> +/// <seealso cref="structured_task_group::wait Method"/> +/// <seealso cref="structured_task_group::run_and_wait Method"/> +/**/ +enum task_group_status +{ + /// <summary> + /// The tasks queued to the <c>task_group</c> object have not completed. Note that this value is not presently returned by + /// the Concurrency Runtime. + /// </summary> + /**/ + not_complete, + + /// <summary> + /// The tasks queued to the <c>task_group</c> or <c>structured_task_group</c> object completed successfully. + /// </summary> + /**/ + completed, + + /// <summary> + /// The <c>task_group</c> or <c>structured_task_group</c> object was canceled. One or more tasks may not have executed. + /// </summary> + /**/ + canceled +}; + +namespace details +{ +/// <summary> +/// Atomics +/// </summary> +#ifdef _USE_REAL_ATOMICS +typedef std::atomic<long> atomic_long; +typedef std::atomic<size_t> atomic_size_t; + +template<typename _T> +_T atomic_compare_exchange(std::atomic<_T>& _Target, _T _Exchange, _T _Comparand) +{ + _T _Result = _Comparand; + _Target.compare_exchange_strong(_Result, _Exchange); + return _Result; +} + +template<typename _T> +_T atomic_exchange(std::atomic<_T>& _Target, _T _Value) +{ + return _Target.exchange(_Value); +} + +template<typename _T> +_T atomic_increment(std::atomic<_T>& _Target) +{ + return _Target.fetch_add(1) + 1; +} + +template<typename _T> +_T atomic_decrement(std::atomic<_T>& _Target) +{ + return _Target.fetch_sub(1) - 1; +} + +template<typename _T> +_T atomic_add(std::atomic<_T>& _Target, _T value) +{ + return _Target.fetch_add(value) + value; +} + +#else // not _USE_REAL_ATOMICS + +typedef long volatile atomic_long; +typedef size_t volatile atomic_size_t; + +template<class T> +inline T atomic_exchange(T volatile& _Target, T _Value) +{ + return _InterlockedExchange(&_Target, _Value); +} + +inline long atomic_increment(long volatile & _Target) +{ + return _InterlockedIncrement(&_Target); +} + +inline long atomic_add(long volatile & _Target, long value) +{ + return _InterlockedExchangeAdd(&_Target, value) + value; +} + +inline size_t atomic_increment(size_t volatile & _Target) +{ +#if (defined(_M_IX86) || defined(_M_ARM)) + return static_cast<size_t>(_InterlockedIncrement(reinterpret_cast<long volatile *>(&_Target))); +#else + return static_cast<size_t>(_InterlockedIncrement64(reinterpret_cast<__int64 volatile *>(&_Target))); +#endif +} + +inline long atomic_decrement(long volatile & _Target) +{ + return _InterlockedDecrement(&_Target); +} + +inline size_t atomic_decrement(size_t volatile & _Target) +{ +#if (defined(_M_IX86) || defined(_M_ARM)) + return static_cast<size_t>(_InterlockedDecrement(reinterpret_cast<long volatile *>(&_Target))); +#else + return static_cast<size_t>(_InterlockedDecrement64(reinterpret_cast<__int64 volatile *>(&_Target))); +#endif +} + +inline long atomic_compare_exchange(long volatile & _Target, long _Exchange, long _Comparand) +{ + return _InterlockedCompareExchange(&_Target, _Exchange, _Comparand); +} + +inline size_t atomic_compare_exchange(size_t volatile & _Target, size_t _Exchange, size_t _Comparand) +{ +#if (defined(_M_IX86) || defined(_M_ARM)) + return static_cast<size_t>(_InterlockedCompareExchange(reinterpret_cast<long volatile *>(_Target), static_cast<long>(_Exchange), static_cast<long>(_Comparand))); +#else + return static_cast<size_t>(_InterlockedCompareExchange64(reinterpret_cast<__int64 volatile *>(_Target), static_cast<__int64>(_Exchange), static_cast<__int64>(_Comparand))); +#endif +} +#endif // _USE_REAL_ATOMICS + +}} // namespace Concurrency + +#endif // _PPLINTERFACE_H diff --git a/test_data/lots_of_files/ppltasks.h b/test_data/lots_of_files/ppltasks.h new file mode 100644 index 0000000..5eaac63 --- /dev/null +++ b/test_data/lots_of_files/ppltasks.h @@ -0,0 +1,7303 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* ppltasks.h +* +* Parallel Patterns Library - PPL Tasks +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLTASKS_H +#define _PPLTASKS_H + +#include <pplconcrt.h> + +// Cannot build using a compiler that is older than dev10 SP1 +#ifdef _MSC_VER +#if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ +#error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks +#endif /*IFSTRIP=IGN*/ +#endif + +#include <functional> +#include <vector> +#include <utility> +#include <exception> +#include <algorithm> + +#if defined (__cplusplus_winrt) +#include <windows.h> +#include <ctxtcall.h> +#include <agile.h> +#include <winapifamily.h> +#ifndef _UITHREADCTXT_SUPPORT + +#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ + +// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user +#include <winapifamily.h> + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + #define _UITHREADCTXT_SUPPORT 1 +#endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + +#else /* WINAPI_FAMILY */ + // Not supported without a WINAPI_FAMILY setting. + #define _UITHREADCTXT_SUPPORT 0 +#endif /* WINAPI_FAMILY */ + +#endif /* _UITHREADCTXT_SUPPORT */ + +#if _UITHREADCTXT_SUPPORT +#include <uithreadctxt.h> +#endif /* _UITHREADCTXT_SUPPORT */ + +#pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "1") +#else /* (__cplusplus_winrt) */ +#pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") +#endif /* #if defined(__cplusplus_winrt) */ + +#ifdef _DEBUG + #define _DBG_ONLY(X) X +#else + #define _DBG_ONLY(X) +#endif // #ifdef _DEBUG + +// std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. +#ifdef _MSC_VER +#if _MSC_VER < 1700 /*IFSTRIP=IGN*/ +namespace std +{ + template<class _E> exception_ptr make_exception_ptr(_E _Except) + { + return copy_exception(_Except); + } +} +#endif +#ifndef _PPLTASK_ASYNC_LOGGING + #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) + #define _PPLTASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt + #else + #define _PPLTASK_ASYNC_LOGGING 0 + #endif +#endif +#endif + +#pragma pack(push,_CRT_PACKING) + +#pragma warning(push) +#pragma warning(disable: 28197) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation +#pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming + +// All CRT public header files are required to be protected from the macro new +#pragma push_macro("new") +#undef new + +// stuff ported from Dev11 CRT +// NOTE: this doesn't actually match std::declval. it behaves differently for void! +// so don't blindly change it to std::declval. +namespace stdx +{ + template<class _T> + _T&& declval(); +} + +/// <summary> +/// The <c>Concurrency</c> namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>. +/// </summary> +/**/ +namespace Concurrency +{ +/// <summary> +/// A type that represents the terminal state of a task. Valid values are <c>completed</c> and <c>canceled</c>. +/// </summary> +/// <seealso cref="task Class"/> +/**/ +typedef task_group_status task_status; + +template <typename _Type> class task; +template <> class task<void>; + +// In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. +#ifndef PPL_TASK_SAVE_FRAME_COUNT +#ifdef _DEBUG +#define PPL_TASK_SAVE_FRAME_COUNT 10 +#else +#define PPL_TASK_SAVE_FRAME_COUNT 1 +#endif +#endif + +/// <summary> +/// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, +/// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. +/// </summary> +/// <ramarks> +/// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() +/// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. +/// </remarks> +#if PPL_TASK_SAVE_FRAME_COUNT > 1 +#if defined(__cplusplus_winrt) && !defined(_DEBUG) +#pragma message ("WARNING: Redefinning PPL_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") +#define _CAPTURE_CALLSTACK() ::Concurrency::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#else +#define _CAPTURE_CALLSTACK() ::Concurrency::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPL_TASK_SAVE_FRAME_COUNT) +#endif +#else +#define _CAPTURE_CALLSTACK() ::Concurrency::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#endif + + +/// <summary> +/// Returns an indication of whether the task that is currently executing has received a request to cancel its +/// execution. Cancellation is requested on a task if the task was created with a cancellation token, and +/// the token source associated with that token is canceled. +/// </summary> +/// <returns> +/// <c>true</c> if the currently executing task has received a request for cancellation, <c>false</c> otherwise. +/// </returns> +/// <remarks> +/// If you call this method in the body of a task and it returns <c>true</c>, you must respond with a call to +/// <see cref="cancel_current_task Function">cancel_current_task</see> to acknowledge the cancellation request, +/// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into +/// the <c>canceled</c> state. If you do not respond and continue execution, or return instead of calling +/// <c>cancel_current_task</c>, the task will enter the <c>completed</c> state when it is done. +/// state. +/// <para>A task is not cancellable if it was created without a cancellation token.</para> +/// </remarks> +/// <seealso cref="task Class"/> +/// <seealso cref="cancellation_token_source Class"/> +/// <seealso cref="cancellation_token Class"/> +/// <seealso cref="cancel_current_task Function"/> +/**/ +inline bool __cdecl is_task_cancellation_requested() +{ + return ::Concurrency::details::_TaskCollection_t::_Is_cancellation_requested(); +} + +/// <summary> +/// Cancels the currently executing task. This function can be called from within the body of a task to abort the +/// task's execution and cause it to enter the <c>canceled</c> state. While it may be used in response to +/// the <see cref="is_task_cancellation_requested Function">is_task_cancellation_requested</see> function, you may +/// also use it by itself, to initiate cancellation of the task that is currently executing. +/// <para>It is not a supported scenario to call this function if you are not within the body of a <c>task</c>. +/// Doing so will result in undefined behavior such as a crash or a hang in your application.</para> +/// </summary> +/// <seealso cref="task Class"/> +/**/ +inline __declspec(noreturn) void __cdecl cancel_current_task() +{ + throw task_canceled(); +} + +namespace details +{ + /// <summary> + /// Callstack container, which is used to capture and preserve callstacks in ppltasks. + /// Members of this class is examined by vc debugger, thus there will be no public access methods. + /// Please note that names of this class should be kept stable for debugger examining. + /// </summary> + class _TaskCreationCallstack + { + private: + // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; + // otherwise, _M_Frame will store all the callstack frames. + void* _M_SingleFrame; + std::vector<void *> _M_frames; + public: + _TaskCreationCallstack() + { + _M_SingleFrame = nullptr; + } + + // Store one frame of callstack. This function works for both Debug / Release CRT. + static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) + { + _TaskCreationCallstack _csc; + _csc._M_SingleFrame = _SingleFrame; + return _csc; + } + + // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. + __declspec(noinline) + static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) + { + _TaskCreationCallstack _csc; + _csc._M_frames.resize(_CaptureFrames); + // skip 2 frames to make sure callstack starts from user code + _csc._M_frames.resize(::Concurrency::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); + return _csc; + } + }; + typedef unsigned char _Unit_type; + + struct _TypeSelectorNoAsync {}; + struct _TypeSelectorAsyncOperationOrTask {}; + struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncAction {}; + struct _TypeSelectorAsyncActionWithProgress {}; + struct _TypeSelectorAsyncOperationWithProgress {}; + + template<typename _Ty> + struct _NormalizeVoidToUnitType + { + typedef _Ty _Type; + }; + + template<> + struct _NormalizeVoidToUnitType<void> + { + typedef _Unit_type _Type; + }; + + template<typename _T> + struct _IsUnwrappedAsyncSelector + { + static const bool _Value = true; + }; + + template<> + struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> + { + static const bool _Value = false; + }; + + template <typename _Ty> + struct _UnwrapTaskType + { + typedef _Ty _Type; + }; + + template <typename _Ty> + struct _UnwrapTaskType<task<_Ty>> + { + typedef _Ty _Type; + }; + + template <typename _T> + _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); + + _TypeSelectorNoAsync _AsyncOperationKindSelector(...); + +#if defined(__cplusplus_winrt) + template <typename _Type> + struct _Unhat + { + typedef _Type _Value; + }; + + template <typename _Type> + struct _Unhat<_Type^> + { + typedef _Type _Value; + }; + + value struct _NonUserType { public: int _Dummy; }; + + template <typename _Type, bool _IsValueTypeOrRefType = __is_valid_winrt_type(_Type)> + struct _ValueTypeOrRefType + { + typedef _NonUserType _Value; + }; + + template <typename _Type> + struct _ValueTypeOrRefType<_Type, true> + { + typedef _Type _Value; + }; + + template <typename _T1, typename _T2> + _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1,_T2>^); + + template <typename _T1> + _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1>^); + + template <typename _Type> + struct _GetProgressType + { + typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; + }; + + template <typename _Type> + struct _IsIAsyncInfo + { + static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value); + }; + + template <typename _T> + _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T>^); + + _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction^); + + template <typename _T1, typename _T2> + _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>^); + + template <typename _T> + _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T>^); + + template <typename _Type, bool _IsAsync = _IsIAsyncInfo<_Type>::_Value> + struct _TaskTypeTraits + { + typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = _IsAsync; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + + template<typename _Type> + struct _TaskTypeTraits<_Type, true > + { + typedef decltype(((_Type)nullptr)->GetResults()) _TaskRetType; + typedef _TaskRetType _NormalizedTaskRetType; + typedef decltype(_AsyncOperationKindSelector((_Type)nullptr)) _AsyncKind; + + static const bool _IsAsyncTask = true; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + +#else /* defined (__cplusplus_winrt) */ + template <typename _Type> + struct _IsIAsyncInfo + { + static const bool _Value = false; + }; + + template <typename _Type, bool _IsAsync = false> + struct _TaskTypeTraits + { + typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; +#endif /* defined (__cplusplus_winrt) */ + + template <typename _Function> auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type()) { (_Func); return std::true_type(); } + template <typename _Function> std::false_type _IsCallable(_Function, ...) { return std::false_type(); } + + template <> + struct _TaskTypeTraits<void> + { + typedef void _TaskRetType; + typedef _TypeSelectorNoAsync _AsyncKind; + typedef _Unit_type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + + template<typename _Type> + task<_Type> _To_task(_Type t); + + task<void> _To_task(); + + struct _BadContinuationParamType{}; + + template <typename _Function, typename _Type> auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t))); + template <typename _Function, typename _Type> auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t)); + template <typename _Function, typename _Type> auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType; + + template <typename _Function, typename _Type> auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type()); + template <typename _Function, typename _Type> std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...); + + template <typename _Function> auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task())); + template <typename _Function> auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func()); + + template <typename _Function> auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task()), std::true_type()); + template <typename _Function> std::false_type _VoidIsTaskHelper(_Function _Func, int, ...); + + template<typename _Function, typename _ExpectedParameterType> + struct _FunctionTypeTraits + { + typedef decltype(_ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _FuncRetType; + static_assert(!std::is_same<_FuncRetType,_BadContinuationParamType>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); + + typedef decltype(_IsTaskHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _Takes_task; + }; + + template<typename _Function> + struct _FunctionTypeTraits<_Function, void> + { + typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType; + typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task; + }; + + template<typename _Function, typename _ReturnType> + struct _ContinuationTypeTraits + { + typedef task<typename _TaskTypeTraits<typename _FunctionTypeTraits<_Function, _ReturnType>::_FuncRetType>::_TaskRetType> _TaskOfType; + }; + + // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is + // declared, the constructor may or may not perform unwrapping. For eg. + // + // This declaration SHOULD NOT cause unwrapping + // task<task<void>> t1([]() -> task<void> { + // task<void> t2([]() {}); + // return t2; + // }); + // + // This declaration SHOULD cause unwrapping + // task<void>> t1([]() -> task<void> { + // task<void> t2([]() {}); + // return t2; + // }); + // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. + template <typename _TaskType, typename _FuncRetType> + struct _InitFunctorTypeTraits + { + typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; + static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; + static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; + }; + + template<typename T> + struct _InitFunctorTypeTraits<T, T> + { + typedef _TypeSelectorNoAsync _AsyncKind; + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + + /// <summary> + /// Helper object used for LWT invocation. + /// </summary> + struct _TaskProcThunk + { + _TaskProcThunk(const std::function<void ()> & _Callback) : + _M_func(_Callback) + { + } + + static void __cdecl _Bridge(void *_PData) + { + _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); + _Holder _ThunkHolder(_PThunk); + _PThunk->_M_func(); + } + private: + + // RAII holder + struct _Holder + { + _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) + { + } + + ~_Holder() + { + delete _M_pThunk; + } + + _TaskProcThunk * _M_pThunk; + + private: + _Holder& operator=(const _Holder&); + }; + + std::function<void()> _M_func; + _TaskProcThunk& operator=(const _TaskProcThunk&); + }; + + /// <summary> + /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be + /// waited on or canceled after scheduling. + /// This schedule method will perform automatic inlining base on <paramref value="_InliningMode"/>. + /// </summary> + /// <param name="_Func"> + /// The user functor need to be scheduled. + /// </param> + /// <param name="_InliningMode"> + /// The inlining scheduling policy for current functor. + /// </param> + static void _ScheduleFuncWithAutoInline(const std::function<void ()> & _Func, _TaskInliningMode_t _InliningMode) + { + _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); + } + + class _ContextCallback + { + typedef std::function<void(void)> _CallbackFunction; + +#if defined (__cplusplus_winrt) + + public: + + static _ContextCallback _CaptureCurrent() + { + _ContextCallback _Context; + _Context._Capture(); + return _Context; + } + + ~_ContextCallback() + { + _Reset(); + } + + _ContextCallback(bool _DeferCapture = false) + { + if (_DeferCapture) + { + _M_context._M_captureMethod = _S_captureDeferred; + } + else + { + _M_context._M_pContextCallback = nullptr; + } + } + + // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). + void _Resolve(bool _CaptureCurrent) + { + if(_M_context._M_captureMethod == _S_captureDeferred) + { + _M_context._M_pContextCallback = nullptr; + + if (_CaptureCurrent) + { + if (_IsCurrentOriginSTA()) + { + _Capture(); + } +#if _UITHREADCTXT_SUPPORT + else + { + // This method will fail if not called from the UI thread. + HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } +#endif /* _UITHREADCTXT_SUPPORT */ + } + } + } + + void _Capture() + { + HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast<void **>(&_M_context._M_pContextCallback)); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } + + _ContextCallback(const _ContextCallback& _Src) + { + _Assign(_Src._M_context._M_pContextCallback); + } + + _ContextCallback(_ContextCallback&& _Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + + _ContextCallback& operator=(const _ContextCallback& _Src) + { + if (this != &_Src) + { + _Reset(); + _Assign(_Src._M_context._M_pContextCallback); + } + return *this; + } + + _ContextCallback& operator=(_ContextCallback&& _Src) + { + if (this != &_Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + return *this; + } + + bool _HasCapturedContext() const + { + _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred); + return (_M_context._M_pContextCallback != nullptr); + } + + void _CallInContext(_CallbackFunction _Func) const + { + if (!_HasCapturedContext()) + { + _Func(); + } + else + { + ComCallData callData; + ZeroMemory(&callData, sizeof(callData)); + callData.pUserDefined = reinterpret_cast<void *>(&_Func); + + HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); + if (FAILED(_Hr)) + { + throw ::Platform::Exception::CreateException(_Hr); + } + } + } + + bool operator==(const _ContextCallback& _Rhs) const + { + return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); + } + + bool operator!=(const _ContextCallback& _Rhs) const + { + return !(operator==(_Rhs)); + } + + private: + void _Reset() + { + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->Release(); + } + } + + void _Assign(IContextCallback *_PContextCallback) + { + _M_context._M_pContextCallback = _PContextCallback; + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->AddRef(); + } + } + + static HRESULT __stdcall _Bridge(ComCallData *_PParam) + { + _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); + (*pFunc)(); + return S_OK; + } + + // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) + static bool _IsCurrentOriginSTA() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + if (SUCCEEDED(hr)) + { + // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether + // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in + // a neutral aparment when it schedules a continuation, we will not marshal continuations back to the STA, + // since variables used within a neutral apartment are expected to be apartment neutral. + switch(_AptType) + { + case APTTYPE_MAINSTA: + case APTTYPE_STA: + return true; + default: + break; + } + } + return false; + } + + union + { + IContextCallback *_M_pContextCallback; + size_t _M_captureMethod; + } _M_context; + + static const size_t _S_captureDeferred = 1; +#else /* defined (__cplusplus_winrt) */ + public: + + static _ContextCallback _CaptureCurrent() + { + return _ContextCallback(); + } + + _ContextCallback(bool = false) + { + } + + _ContextCallback(const _ContextCallback&) + { + } + + _ContextCallback(_ContextCallback&&) + { + } + + _ContextCallback& operator=(const _ContextCallback&) + { + return *this; + } + + _ContextCallback& operator=(_ContextCallback&&) + { + return *this; + } + + bool _HasCapturedContext() const + { + return false; + } + + void _Resolve(bool) const + { + } + + void _CallInContext(_CallbackFunction _Func) const + { + _Func(); + } + + bool operator==(const _ContextCallback&) const + { + return true; + } + + bool operator!=(const _ContextCallback&) const + { + return false; + } + +#endif /* defined (__cplusplus_winrt) */ + }; + + template<typename _Type> + struct _ResultHolder + { + void Set(const _Type& _type) + { + _Result = _type; + } + + _Type Get() + { + return _Result; + } + + _Type _Result; + }; + +#if defined (__cplusplus_winrt) + + template<typename _Type> + struct _ResultHolder<_Type^> + { + void Set(_Type^ const & _type) + { + _M_Result = _type; + } + + _Type^ Get() + { + return _M_Result.Get(); + } + private: + // ::Platform::Agile handle specialization of all hats + // including ::Platform::String and ::Platform::Array + ::Platform::Agile<_Type^> _M_Result; + }; + + // + // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. + // + template<typename _Type> + struct _ResultHolder<std::vector<_Type^>> + { + void Set(const std::vector<_Type^>& _type) + { + _Result.reserve(_type.size()); + + for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) + { + _Result.emplace_back(*_PTask); + } + } + + std::vector<_Type^> Get() + { + // Return vectory<T^> with the objects that are marshaled in the proper appartment + std::vector<_Type^> _Return; + _Return.reserve(_Result.size()); + + for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) + { + _Return.push_back(_PTask->Get()); // Platform::Agile will marshal the object to appropriate appartment if neccessary + } + + return _Return; + } + + std::vector< ::Platform::Agile<_Type^> > _Result; + }; + + template<typename _Type> + struct _ResultHolder<std::pair<_Type^, void*> > + { + void Set(const std::pair<_Type^, size_t>& _type) + { + _M_Result = _type; + } + + std::pair<_Type^, size_t> Get() + { + return std::make_pair(_M_Result.first.Get(), _M_Result.second); + } + private: + std::pair< ::Platform::Agile<_Type^>, size_t> _M_Result; + }; + +#endif /* defined (__cplusplus_winrt) */ + + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + struct _ExceptionHolder + { + private: + void ReportUnhandledError() + { +#if defined (__cplusplus_winrt) + if (_M_winRTException != nullptr) + { + ::Platform::Details::ReportUnhandledError(_M_winRTException); + } +#endif /* defined (__cplusplus_winrt) */ + } + public: + explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) +#if defined (__cplusplus_winrt) + , _M_winRTException(nullptr) +#endif /* defined (__cplusplus_winrt) */ + { + } + +#if defined (__cplusplus_winrt) + explicit _ExceptionHolder(::Platform::Exception^ _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) + { + } +#endif /* defined (__cplusplus_winrt) */ + + __declspec(noinline) + ~_ExceptionHolder() + { + if (_M_exceptionObserved == 0) + { + // If you are trapped here, it means an exception thrown in task chain didn't get handled. + // Please add task-based continuation to handle all exceptions coming from tasks. + // this->_M_stackTrace keeps the creation callstack of the task generates this exception. + _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); + } + } + + void _RethrowUserException() + { + if (_M_exceptionObserved == 0) + { + atomic_exchange(_M_exceptionObserved, 1l); + } + +#if defined (__cplusplus_winrt) + if (_M_winRTException != nullptr) + { + throw _M_winRTException; + } +#endif /* defined (__cplusplus_winrt) */ + std::rethrow_exception(_M_stdException); + } + + // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that + // are unobserved when the exception holder is destructed will terminate the process. + atomic_long _M_exceptionObserved; + + // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. + std::exception_ptr _M_stdException; +#if defined (__cplusplus_winrt) + ::Platform::Exception^ _M_winRTException; +#endif /* defined (__cplusplus_winrt) */ + + // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, + // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call + // is to task_completion_event::set_exception, the set_exception method was the source of the exception. + // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. + _TaskCreationCallstack _M_stackTrace; + + }; + +#if defined (__cplusplus_winrt) + /// <summary> + /// Base converter class for converting asynchronous interfaces to IAsyncOperation + /// </summary> + template<typename _AsyncOperationType, typename _CompletionHandlerType, typename _Result> + ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result> + { + internal: + // The async action, action with progress or operation with progress that this stub forwards to. + ::Platform::Agile<_AsyncOperationType> _M_asyncInfo; + + Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ _M_CompletedHandler; + + _AsyncInfoImpl( _AsyncOperationType _AsyncInfo ) : _M_asyncInfo(_AsyncInfo) {} + + public: + virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); } + virtual void Close() { _M_asyncInfo.Get()->Close(); } + + virtual property Windows::Foundation::HResult ErrorCode + { + Windows::Foundation::HResult get() + { + return _M_asyncInfo.Get()->ErrorCode; + } + } + + virtual property UINT Id + { + UINT get() + { + return _M_asyncInfo.Get()->Id; + } + } + + virtual property Windows::Foundation::AsyncStatus Status + { + Windows::Foundation::AsyncStatus get() + { + return _M_asyncInfo.Get()->Status; + } + } + + virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); } + + virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ Completed + { + Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ get() + { + return _M_CompletedHandler; + } + + void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ value) + { + _M_CompletedHandler = value; + _M_asyncInfo.Get()->Completed = ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) { + _M_CompletedHandler->Invoke(this, status); + }); + } + } + }; + + /// <summary> + /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress<T> into IAsyncOperation<T> + /// </summary> + template<typename _Result, typename _Progress> + ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed : + _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^, + Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, + _Result> + { + internal: + _IAsyncOperationWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^ _Operation) : + _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^, + Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, + _Result>(_Operation) {} + + public: + virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); } + }; + + /// <summary> + /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> + /// </summary> + ref struct _IAsyncActionToAsyncOperationConverter sealed : + _AsyncInfoImpl<Windows::Foundation::IAsyncAction^, + Windows::Foundation::AsyncActionCompletedHandler, + details::_Unit_type> + { + internal: + _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction^ _Operation) : + _AsyncInfoImpl<Windows::Foundation::IAsyncAction^, + Windows::Foundation::AsyncActionCompletedHandler, + details::_Unit_type>(_Operation) {} + + public: + virtual details::_Unit_type GetResults() override + { + // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. + _M_asyncInfo.Get()->GetResults(); + return details::_Unit_type(); + } + }; + + /// <summary> + /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> + /// </summary> + template<typename _Progress> + ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed : + _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Progress>^, + Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, + details::_Unit_type> + { + internal: + _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ _Action) : + _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Progress>^, + Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, + details::_Unit_type>(_Action) {} + public: + virtual details::_Unit_type GetResults() override + { + // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. + _M_asyncInfo.Get()->GetResults(); + return details::_Unit_type(); + } + }; +#endif /* defined (__cplusplus_winrt) */ +} // namespace details + +/// <summary> +/// The <c>task_continuation_context</c> class allows you to specify where you would like a continuation to be executed. +/// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's +/// execution context is determined by the runtime, and not configurable. +/// </summary> +/// <seealso cref="task Class"/> +/**/ +class task_continuation_context : public details::_ContextCallback +{ +public: + + /// <summary> + /// Creates the default task continuation context. + /// </summary> + /// <returns> + /// The default continuation context. + /// </returns> + /// <remarks> + /// The default context is used if you don't specifiy a continuation context when you call the <c>then</c> method. In Windows + /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where + /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an + /// apartment aware task is the apartment where <c>then</c> is invoked. + /// <para>An apartment aware task is a task that unwraps a Windows Runtime <c>IAsyncInfo</c> interface, or a task that is descended from such + /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in + /// that STA.</para> + /// <para>A continuation on a non-apartment aware task will execute in a context the Runtime chooses.</para> + /// </remarks> + /**/ + static task_continuation_context use_default() + { +#if defined (__cplusplus_winrt) + // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() + return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle +#else /* defined (__cplusplus_winrt) */ + return task_continuation_context(); +#endif /* defined (__cplusplus_winrt) */ + } + +#if defined (__cplusplus_winrt) + /// <summary> + /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. + /// </summary> + /// <returns> + /// A task continuation context that represents an arbitrary location. + /// </returns> + /// <remarks> + /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task + /// is apartment aware. + /// <para><c>use_arbitrary</c> can be used to turn off the default behavior for a continuation on an apartment + /// aware task created in an STA. </para> + /// <para>This method is only available to Windows Store apps.</para> + /// </remarks> + /**/ + static task_continuation_context use_arbitrary() + { + task_continuation_context _Arbitrary(true); + _Arbitrary._Resolve(false); + return _Arbitrary; + } + + /// <summary> + /// Returns a task continuation context object that represents the current execution context. + /// </summary> + /// <returns> + /// The current execution context. + /// </returns> + /// <remarks> + /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. + /// <para>The value returned by <c>use_current</c> can be used to indicate to the Runtime that the continuation should execute in + /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is + /// a task that unwraps a Windows Runtime <c>IAsyncInfo</c> interface, or a task that is descended from such a task. </para> + /// <para>This method is only available to Windows Store apps.</para> + /// </remarks> + /**/ + static task_continuation_context use_current() + { + task_continuation_context _Current(true); + _Current._Resolve(true); + return _Current; + } +#endif /* defined (__cplusplus_winrt) */ + +private: + + task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) + { + } +}; + +class task_options; +namespace details +{ + struct _Internal_task_options + { + bool _M_hasPresetCreationCallstack; + _TaskCreationCallstack _M_presetCreationCallstack; + + void _set_creation_callstack(const _TaskCreationCallstack &_callstack) + { + _M_hasPresetCreationCallstack = true; + _M_presetCreationCallstack = _callstack; + } + _Internal_task_options() + { + _M_hasPresetCreationCallstack = false; + } + }; + + inline _Internal_task_options &_get_internal_task_options(task_options &options); + inline const _Internal_task_options &_get_internal_task_options(const task_options &options); +} +/// <summary> +/// Represents the allowed options for creating a task +/// </summary> +class task_options +{ +public: + + + /// <summary> + /// Default list of task creation options + /// </summary> + task_options() + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// <summary> + /// Task option that specify a cancellation token + /// </summary> + task_options(cancellation_token _Token) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(true), + _M_HasScheduler(false) + { + } + + /// <summary> + /// Task option that specify a continuation context. This is valid only for continuations (then) + /// </summary> + task_options(task_continuation_context _ContinuationContext) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// <summary> + /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) + /// </summary> + task_options(cancellation_token _Token, task_continuation_context _ContinuationContext) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// <summary> + /// Task option that specify a scheduler with shared lifetime + /// </summary> + template<typename _SchedType> + task_options(std::shared_ptr<_SchedType> _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// <summary> + /// Task option that specify a scheduler reference + /// </summary> + task_options(scheduler_interface& _Scheduler) + : _M_Scheduler(&_Scheduler), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// <summary> + /// Task option that specify a scheduler + /// </summary> + task_options(scheduler_ptr _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// <summary> + /// Task option copy constructor + /// </summary> + task_options(const task_options& _TaskOptions) + : _M_Scheduler(_TaskOptions.get_scheduler()), + _M_CancellationToken(_TaskOptions.get_cancellation_token()), + _M_ContinuationContext(_TaskOptions.get_continuation_context()), + _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), + _M_HasScheduler(_TaskOptions.has_scheduler()) + { + } + + /// <summary> + /// Sets the given token in the options + /// </summary> + void set_cancellation_token(cancellation_token _Token) + { + _M_CancellationToken = _Token; + _M_HasCancellationToken = true; + } + + /// <summary> + /// Sets the given continuation context in the options + /// </summary> + void set_continuation_context(task_continuation_context _ContinuationContext) + { + _M_ContinuationContext = _ContinuationContext; + } + + /// <summary> + /// Indicates whether a cancellation token was specified by the user + /// </summary> + bool has_cancellation_token() const + { + return _M_HasCancellationToken; + } + + /// <summary> + /// Returns the cancellation token + /// </summary> + cancellation_token get_cancellation_token() const + { + return _M_CancellationToken; + } + + /// <summary> + /// Returns the continuation context + /// </summary> + task_continuation_context get_continuation_context() const + { + return _M_ContinuationContext; + } + + /// <summary> + /// Indicates whether a scheduler n was specified by the user + /// </summary> + bool has_scheduler() const + { + return _M_HasScheduler; + } + + /// <summary> + /// Returns the scheduler + /// </summary> + scheduler_ptr get_scheduler() const + { + return _M_Scheduler; + } + +private: + + task_options const& operator=(task_options const& _Right); + friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); + friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); + + scheduler_ptr _M_Scheduler; + cancellation_token _M_CancellationToken; + task_continuation_context _M_ContinuationContext; + details::_Internal_task_options _M_InternalTaskOptions; + bool _M_HasCancellationToken; + bool _M_HasScheduler; +}; + +namespace details +{ + inline _Internal_task_options & _get_internal_task_options(task_options &options) + { + return options._M_InternalTaskOptions; + } + inline const _Internal_task_options & _get_internal_task_options(const task_options &options) + { + return options._M_InternalTaskOptions; + } + + struct _Task_impl_base; + template<typename _ReturnType> struct _Task_impl; + + template<typename _ReturnType> + struct _Task_ptr + { + typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; + static _Type _Make(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } + }; + + typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; + typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; + + // The weak-typed base task handler for continuation tasks. + struct _ContinuationTaskHandleBase : _UnrealizedChore_t + { + _ContinuationTaskHandleBase * _M_next; + task_continuation_context _M_continuationContext; + bool _M_isTaskBasedContinuation; + + // This field gives inlining scheduling policy for current chore. + _TaskInliningMode_t _M_inliningMode; + + virtual _Task_ptr_base _GetTaskImplBase() const = 0; + + _ContinuationTaskHandleBase() : + _M_next(nullptr), _M_continuationContext(task_continuation_context::use_default()), _M_isTaskBasedContinuation(false), _M_inliningMode(details::_NoInline) + { + } + + virtual ~_ContinuationTaskHandleBase() {} + }; + +#if _PPLTASK_ASYNC_LOGGING + // GUID used for identifying causality logs from PPLTask + const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); + + __declspec(selectany) volatile long _isCausalitySupported = 0; + + inline bool _IsCausalitySupported() + { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (_isCausalitySupported == 0) + { + long _causality = 1; + OSVERSIONINFOEX _osvi = {}; + _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + // The Causality is supported on Windows version higher than Windows 8 + _osvi.dwMajorVersion = 6; + _osvi.dwMinorVersion = 3; + + DWORDLONG _conditionMask = 0; + VER_SET_CONDITION( _conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); + VER_SET_CONDITION( _conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL ); + + if ( ::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) + { + _causality = 2; + } + + _isCausalitySupported = _causality; + return _causality == 2; + } + + return _isCausalitySupported == 2 ? true : false; +#else + return true; +#endif + } + + // Stateful logger rests inside task_impl_base. + struct _TaskEventLogger + { + _Task_impl_base *_M_task; + bool _M_scheduled; + bool _M_taskPostEventStarted; + + // Log before scheduling task + void _LogScheduleTask(bool _isContinuation) + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast<unsigned long long>(_M_task), + _isContinuation ? "Concurrency::PPLTask::ScheduleContinuationTask" : "Concurrency::PPLTask::ScheduleTask", 0); + _M_scheduled = true; + } + } + + // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. + void _LogCancelTask() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast<unsigned long long>(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); + + } + } + + // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run + void _LogTaskCompleted(); + + // Log when task body (which includes user lambda and other scheduling code) begin to run + void _LogTaskExecutionStarted() { } + + // Log when task body finish executing + void _LogTaskExecutionCompleted() + { + if (_M_taskPostEventStarted && details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + } + } + + // Log right before user lambda being invoked + void _LogWorkItemStarted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast<unsigned long long>(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + } + } + + // Log right after user lambda being invoked + void _LogWorkItemCompleted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast<unsigned long long>(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + _M_taskPostEventStarted = true; + } + } + + _TaskEventLogger(_Task_impl_base *_task): _M_task(_task) + { + _M_scheduled = false; + _M_taskPostEventStarted = false; + } + }; + + // Exception safe logger for user lambda + struct _TaskWorkItemRAIILogger + { + _TaskEventLogger &_M_logger; + _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger): _M_logger(_taskHandleLogger) + { + _M_logger._LogWorkItemStarted(); + } + + ~_TaskWorkItemRAIILogger() + { + _M_logger._LogWorkItemCompleted(); + } + _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned + }; + +#else + inline void _LogCancelTask(_Task_impl_base *) {} + struct _TaskEventLogger + { + void _LogScheduleTask(bool) {} + void _LogCancelTask() {} + void _LogWorkItemStarted() {} + void _LogWorkItemCompleted() {} + void _LogTaskExecutionStarted() {} + void _LogTaskExecutionCompleted() {} + void _LogTaskCompleted() {} + _TaskEventLogger(_Task_impl_base *) {} + }; + struct _TaskWorkItemRAIILogger + { + _TaskWorkItemRAIILogger(_TaskEventLogger &) {} + }; +#endif + + /// <summary> + /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler + /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. + /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for continuation tasks, it will be derived from + /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. + /// </summary> + /// <typeparam name="_ReturnType"> + /// The result type of the _Task_impl. + /// </typeparam> + /// <typeparam name="_DerivedTaskHandle"> + /// The derived task handle class. The <c>operator ()</c> needs to be implemented. + /// </typeparam> + /// <typeparam name="_BaseTaskHandle"> + /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or _ContinuationTaskHandleBase. + /// </typeparam> + template<typename _ReturnType, typename _DerivedTaskHandle, typename _BaseTaskHandle> + struct _PPLTaskHandle : _BaseTaskHandle + { + _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) + { + } + + virtual ~_PPLTaskHandle() + { + // Here is the sink of all task completion code paths + _M_pTask->_M_taskEventLogger._LogTaskCompleted(); + } + + virtual void invoke() const + { + // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled + // by the runtime. + _ASSERTE((bool)_M_pTask); + if (!_M_pTask->_TransitionedToStarted()) + { + static_cast<const _DerivedTaskHandle *>(this)->_SyncCancelAndPropagateException(); + return; + } + + _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); + try + { + // All derived task handle must implement this contract function. + static_cast<const _DerivedTaskHandle *>(this)->_Perform(); + } + catch(const task_canceled &) + { + _M_pTask->_Cancel(true); + } + catch(const _Interruption_exception &) + { + _M_pTask->_Cancel(true); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + _M_pTask->_CancelWithException(_E); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + _M_pTask->_CancelWithException(std::current_exception()); + } + _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); + } + + // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. + // The return value should be automatically optimized by R-value ref. + _Task_ptr_base _GetTaskImplBase() const + { + return _M_pTask; + } + + typename _Task_ptr<_ReturnType>::_Type _M_pTask; + private: + _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator + }; + + /// <summary> + /// The base implementation of a first-class task. This class contains all the non-type specific + /// implementation details of the task. + /// </summary> + /**/ + struct _Task_impl_base + { + enum _TaskInternalState + { + // Tracks the state of the task, rather than the task collection on which the task is scheduled + _Created, + _Started, + _PendingCancel, + _Completed, + _Canceled + }; + + _Task_impl_base(_CancellationTokenState * _PTokenState, scheduler_ptr _Scheduler_arg) + : _M_TaskState(_Created), + _M_fFromAsync(false), _M_fUnwrappedTask(false), + _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), + _M_taskEventLogger(this) + { + // Set cancelation token + _M_pTokenState = _PTokenState; + _ASSERTE(_M_pTokenState != nullptr); + if (_M_pTokenState != _CancellationTokenState::_None()) + _M_pTokenState->_Reference(); + } + + virtual ~_Task_impl_base() + { + _ASSERTE(_M_pTokenState != nullptr); + if (_M_pTokenState != _CancellationTokenState::_None()) + { + _M_pTokenState->_Release(); + } + } + + task_status _Wait() + { + bool _DoWait = true; + +#if defined (__cplusplus_winrt) + if (_IsNonBlockingThread()) + { + // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal + // if task has not been completed. + if (!_IsCompleted() && !_IsCanceled()) + { + throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); + } + else + { + // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation + // needs to be marshalled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM + // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which + // task based continuations are wont to do), waiting on the task group results in on the chore that is making this + // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on + // if it has finished execution (which means now we are on the inline synchronous callback). + _DoWait = false; + } + } +#endif /* defined (__cplusplus_winrt) */ + if (_DoWait) + { + // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The + // async operation will take place on a thread in the appropriate apartment Simply wait for the completed + // event to be set. + if (_M_fFromAsync) + { + _M_TaskCollection._Wait(); + } + else + { + // Wait on the task collection to complete. The task collection is guaranteed to still be + // valid since the task must be still within scope so that the _Task_impl_base destructor + // has not yet been called. This call to _Wait potentially inlines execution of work. + try + { + // Invoking wait on a task collection resets the state of the task collection. This means that + // if the task collection itself were canceled, or had encountered an exception, only the first + // call to wait will receive this status. However, both cancellation and exceptions flowing through + // tasks set state in the task impl itself. + + // When it returns cancelled, either work chore or the cancel thread should already have set task's state + // properly -- cancelled state or completed state (because there was no interruption point). + // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. + _M_TaskCollection._RunAndWait(); + } + catch(details::_Interruption_exception&) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _ASSERTE(false); + } + catch(task_canceled&) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _ASSERTE(_IsCanceled()); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if(!_HasUserException()) + { + _CancelWithException(_E); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if(!_HasUserException()) + { + _CancelWithException(std::current_exception()); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } + + // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task + // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must + // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; + // however, this takes the tact of simply waiting upon the completion signal. + if (_M_fUnwrappedTask) + { + _M_TaskCollection._Wait(); + } + } + } + + if (_HasUserException()) + { + _M_exceptionHolder->_RethrowUserException(); + } + else if (_IsCanceled()) + { + return canceled; + } + _ASSERTE(_IsCompleted()); + return completed; + } + + /// <summary> + /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. + /// </summary> + /// <param name="_SynchronousCancel"> + /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task + /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at + /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could + /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. + /// </param> + /// <param name="_UserException"> + /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. + /// </param> + /// <param name="_PropagatedFromAncestor"> + /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when + /// _UserException is set to true. + /// </param> + /// <param name="_ExHolder"> + /// The exception holder that represents the exception. Only valid when _UserException is set to true. + /// </param> + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; + + bool _Cancel(bool _SynchronousCancel) + { + // Send in a dummy value for exception. It is not used when the first parameter is false. + return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); + } + + bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) + { + // This task was canceled because an ancestor task encountered an exception. + return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); + } + +#if defined (__cplusplus_winrt) + bool _CancelWithException(::Platform::Exception^ _Exception) + { + // This task was canceled because the task body encountered an exception. + _ASSERTE(!_HasUserException()); + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); + } +#endif /* defined (__cplusplus_winrt) */ + + bool _CancelWithException(const std::exception_ptr& _Exception) + { + // This task was canceled because the task body encountered an exception. + _ASSERTE(!_HasUserException()); + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); + } + + void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) + { + _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState)); + + auto _CancellationCallback = [_WeakPtr](){ + // Taking ownership of the task prevents dead lock during destruction + // if the destructor waits for the cancellations to be finished + auto _task = _WeakPtr.lock(); + if (_task != nullptr) + _task->_Cancel(false); + }; + + _M_pRegistration = new details::_CancellationTokenCallback<decltype(_CancellationCallback)>(_CancellationCallback); + _M_pTokenState->_RegisterCallback(_M_pRegistration); + } + + void _DeregisterCancellation() + { + if (_M_pRegistration != nullptr) + { + _M_pTokenState->_DeregisterCallback(_M_pRegistration); + _M_pRegistration->_Release(); + _M_pRegistration = nullptr; + } + } + + bool _IsCreated() + { + return (_M_TaskState == _Created); + } + + bool _IsStarted() + { + return (_M_TaskState == _Started); + } + + bool _IsPendingCancel() + { + return (_M_TaskState == _PendingCancel); + } + + bool _IsCompleted() + { + return (_M_TaskState == _Completed); + } + + bool _IsCanceled() + { + return (_M_TaskState == _Canceled); + } + + bool _HasUserException() + { + return static_cast<bool>(_M_exceptionHolder); + } + + const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() + { + _ASSERTE(_HasUserException()); + return _M_exceptionHolder; + } + + bool _IsApartmentAware() + { + return _M_fFromAsync; + } + + void _SetAsync(bool _Async = true) + { + _M_fFromAsync = _Async; + } + + _TaskCreationCallstack _GetTaskCreationCallstack() + { + return _M_pTaskCreationCallstack; + } + + void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) + { + _M_pTaskCreationCallstack = _Callstack; + } + + /// <summary> + /// Helper function to schedule the task on the Task Collection. + /// </summary> + /// <param name="_PTaskHandle"> + /// The task chore handle that need to be executed. + /// </param> + /// <param name="_InliningMode"> + /// The inlining scheduling policy for current _PTaskHandle. + /// </param> + void _ScheduleTask(_UnrealizedChore_t * _PTaskHandle, _TaskInliningMode_t _InliningMode) + { + try + { + _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); + } + catch(const task_canceled &) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _ASSERTE(_IsCanceled()); + } + catch(const _Interruption_exception &) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _ASSERTE(false); + } + catch(...) + { + // This exception could only have come from within the chore body. It should've been caught + // and the task should be canceled with exception. Swallow the exception here. + _ASSERTE(_HasUserException()); + } + } + + /// <summary> + /// Function executes a continuation. This function is recorded by a parent task implementation + /// when a continuation is created in order to execute later. + /// </summary> + /// <param name="_PTaskHandle"> + /// The continuation task chore handle that need to be executed. + /// </param> + /**/ + void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); + if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) + { + if (_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _ImplBase->_Cancel(true); + } + } + else + { + // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled + // (with or without a user exception). + _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); + _ASSERTE(!_ImplBase->_IsCanceled()); + return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); + } + + // If the handle is not scheduled, we need to manually delete it. + delete _PTaskHandle; + } + + // Schedule a continuation to run + void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) + { + + _M_taskEventLogger._LogScheduleTask(true); + // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) + if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) + { + // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, + // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce + // the cost of marshaling. + // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. + if (_PTaskHandle->_M_inliningMode != details::_ForceInline) + { + _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline; + } + _ScheduleFuncWithAutoInline([_PTaskHandle]() { + // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. + // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. + auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); + if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) + { + _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline); + } + else + { + // + // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle + // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: + // + // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into + // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will + // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). + // + try + { + // Dev10 compiler needs this! + auto _PTaskHandle1 = _PTaskHandle; + _PTaskHandle->_M_continuationContext._CallInContext( [_PTaskHandle1, _TaskImplPtr](){ + _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline); + }); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + _TaskImplPtr->_CancelWithException(_E); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + _TaskImplPtr->_CancelWithException(std::current_exception()); + } + } + }, _PTaskHandle->_M_inliningMode); + } + else + { + _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); + } + } + + /// <summary> + /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation + /// if the task has completed or append it to a list of functions to execute when the task actually does complete. + /// </summary> + /// <typeparam name="_FuncInputType"> + /// The input type of the task. + /// </typeparam> + /// <typeparam name="_FuncOutputType"> + /// The output type of the task. + /// </typeparam> + /**/ + void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; + + // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. + // Otherwise, add it to the list of pending continuations + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) + { + _Do = _Schedule; + } + else if (_IsCanceled()) + { + if (_HasUserException()) + { + _Do = _CancelWithException; + } + else + { + _Do = _Cancel; + } + } + else + { + // chain itself on the continuation chain. + _PTaskHandle->_M_next = _M_Continuations; + _M_Continuations = _PTaskHandle; + } + } + + // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of + // async tasks may execute inline. + switch (_Do) + { + case _Schedule: + { + _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); + break; + } + case _Cancel: + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _PTaskHandle->_GetTaskImplBase()->_Cancel(true); + + delete _PTaskHandle; + break; + } + case _CancelWithException: + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + + delete _PTaskHandle; + break; + } + case _Nothing: + default: + // In this case, we have inserted continuation to continuation chain, + // nothing more need to be done, just leave. + break; + } + } + + void _RunTaskContinuations() + { + // The link list can no longer be modified at this point, + // since all following up continuations will be scheduled by themselves. + _ContinuationList _Cur = _M_Continuations, _Next; + _M_Continuations = nullptr; + while (_Cur) + { + // Current node might be deleted after running, + // so we must fetch the next first. + _Next = _Cur->_M_next; + _RunContinuation(_Cur); + _Cur = _Next; + } + } + +#if defined (__cplusplus_winrt) + static bool _IsNonBlockingThread() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + // + // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. + // + if (SUCCEEDED(hr)) + { + switch(_AptType) + { + case APTTYPE_STA: + case APTTYPE_MAINSTA: + return true; + break; + case APTTYPE_NA: + switch(_AptTypeQualifier) + { + // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed + // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting + // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the + // thread out of circulation for a while. + case APTTYPEQUALIFIER_NA_ON_STA: + case APTTYPEQUALIFIER_NA_ON_MAINSTA: + return true; + break; + } + break; + } + } + +#if _UITHREADCTXT_SUPPORT + // This method is used to throw an exepection in _Wait() if called within STA. We + // want the same behavior if _Wait is called on the UI thread. + if (SUCCEEDED(CaptureUiThreadContext(nullptr))) + { + return true; + } +#endif /* _UITHREADCTXT_SUPPORT */ + + return false; + } + + template<typename _ReturnType, typename> + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, + Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>^ _AsyncOp) + { + // This method is invoked either when a task is created from an existing async operation or + // when a lambda that creates an async operation executes. + + // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on + // the IAsyncInfo object will be released when all ^references to the operation go out of scope. + + // This assertion uses the existence of taskcollection to determine if the task was created from an event. + // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection + // when a custom scheduler is used. + // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); + + // Pass the shared_ptr by value into the lambda instead of using 'this'. + _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>( + [_OuterTask](Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>^ _Operation, Windows::Foundation::AsyncStatus _Status) mutable + { + if (_Status == Windows::Foundation::AsyncStatus::Canceled) + { + _OuterTask->_Cancel(true); + } + else if (_Status == Windows::Foundation::AsyncStatus::Error) + { + _OuterTask->_CancelWithException(::Platform::Exception::ReCreateException(static_cast<int>(_Operation->ErrorCode.Value))); + } + else + { + _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed); + _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults()); + } + + // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could + // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold + // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from + // it using the Windows Runtime Async APIs causes a sharing violation. + // Using const_cast is the workaround for failed mutable keywords + const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); + }); + _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); + } +#endif /* defined (__cplusplus_winrt) */ + + template<typename _ReturnType, typename _InternalReturnType> + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) + { + _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); + + // + // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the + // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation + // off the inner task which does the appropriate funnelling to the outer one. We use _Then instead of then to prevent + // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless + // of whether or not the _OuterTask task is canceled. + // + _UnwrappedTask._Then([_OuterTask] (task<_InternalReturnType> _AncestorTask) { + + if (_AncestorTask._GetImpl()->_IsCompleted()) + { + _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); + } + else + { + _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled()); + if (_AncestorTask._GetImpl()->_HasUserException()) + { + // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. + // Instead, it is the enclosing task. + _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); + } + else + { + _OuterTask->_Cancel(true); + } + } + }, nullptr, details::_DefaultAutoInline); + + } + + scheduler_ptr _GetScheduler() const + { + return _M_TaskCollection._GetScheduler(); + } + + // Tracks the internal state of the task + volatile _TaskInternalState _M_TaskState; + // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an + // async operation or async action that is unwrapped by the runtime. + bool _M_fFromAsync; + // Set to true when a continuation unwraps a task or async operation. + bool _M_fUnwrappedTask; + + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + + typedef _ContinuationTaskHandleBase * _ContinuationList; + + ::Concurrency::extensibility::critical_section_t _M_ContinuationsCritSec; + _ContinuationList _M_Continuations; + + // The cancellation token state. + _CancellationTokenState * _M_pTokenState; + + // The registration on the token. + _CancellationTokenRegistration * _M_pRegistration; + + // The async task collection wrapper + ::Concurrency::details::_TaskCollection_t _M_TaskCollection; + + // Callstack for function call (constructor or .then) that created this task impl. + _TaskCreationCallstack _M_pTaskCreationCallstack; + + _TaskEventLogger _M_taskEventLogger; + private: + // Must not be copied by value: + _Task_impl_base(const _Task_impl_base&); + _Task_impl_base const & operator=(_Task_impl_base const&); + }; + +#if _PPLTASK_ASYNC_LOGGING + inline void _TaskEventLogger::_LogTaskCompleted() + { + if (_M_scheduled) + { + ::Windows::Foundation::AsyncStatus _State; + if (_M_task->_IsCompleted()) + _State = ::Windows::Foundation::AsyncStatus::Completed; + else if (_M_task->_HasUserException()) + _State = ::Windows::Foundation::AsyncStatus::Error; + else + _State = ::Windows::Foundation::AsyncStatus::Canceled; + + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast<unsigned long long>(_M_task), _State); + } + } + } +#endif + + /// <summary> + /// The implementation of a first-class task. This structure contains the task group used to execute + /// the task function and handles the scheduling. The _Task_impl is created as a shared_ptr + /// member of the the public task class, so its destruction is handled automatically. + /// </summary> + /// <typeparam name="_ReturnType"> + /// The result type of this task. + /// </typeparam> + /**/ + template<typename _ReturnType> + struct _Task_impl : public _Task_impl_base + { +#if defined (__cplusplus_winrt) + typedef Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> _AsyncOperationType; +#endif // defined(__cplusplus_winrt) + _Task_impl(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) + : _Task_impl_base(_Ct, _Scheduler_arg) + { +#if defined (__cplusplus_winrt) + _M_unwrapped_async_op = nullptr; +#endif /* defined (__cplusplus_winrt) */ + } + + virtual ~_Task_impl() + { + // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause + // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. + _DeregisterCancellation(); + } + + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder_arg) + { + enum { _Nothing, _RunContinuations, _Cancel } _Do = _Nothing; + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + if (_UserException) + { + _ASSERTE(_SynchronousCancel && !_IsCompleted()); + // If the state is _Canceled, the exception has to be coming from an ancestor. + _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor); + + // We should not be canceled with an exception more than once. + _ASSERTE(!_HasUserException()); + + if (_M_TaskState == _Canceled) + { + // If the task has finished cancelling there should not be any continuation records in the array. + return false; + } + else + { + _ASSERTE(_M_TaskState != _Completed); + _M_exceptionHolder = _ExceptionHolder_arg; + } + } + else + { + // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel + // which is to say, cancellation is already initiated, so return early. + if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) + { + _ASSERTE(!_IsCompleted() || !_HasUserException()); + return false; + } + _ASSERTE(!_SynchronousCancel || !_HasUserException()); + } + + if (_SynchronousCancel) + { + // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() + _M_TaskState = _Canceled; + // Cancellation completes the task, so all dependent tasks must be run to cancel them + // They are canceled when they begin running (see _RunContinuation) and see that their + // ancestor has been canceled. + _Do = _RunContinuations; + } + else + { + _ASSERTE(!_UserException); + + if (_IsStarted()) + { + // should not initiate cancellation under a lock + _Do = _Cancel; + } + + // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). + // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from + // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. + _M_TaskState = _PendingCancel; + + _M_taskEventLogger._LogCancelTask(); + } + } + + switch (_Do) + { + case _Cancel: + { +#if defined (__cplusplus_winrt) + if (_M_unwrapped_async_op != nullptr) + { + // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. + _M_unwrapped_async_op->Cancel(); + } +#endif /* defined (__cplusplus_winrt) */ + _M_TaskCollection._Cancel(); + break; + } + case _RunContinuations: + { + // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. + _M_TaskCollection._Complete(); + + if (_M_Continuations) + { + // Scheduling cancellation with automatic inlining. + _ScheduleFuncWithAutoInline([=](){ _RunTaskContinuations(); }, details::_DefaultAutoInline); + } + + break; + } + } + return true; + } + + void _FinalizeAndRunContinuations(_ReturnType _Result) + { + _M_Result.Set(_Result); + + { + // + // Hold this lock to ensure continuations being concurrently either get added + // to the _M_Continuations vector or wait for the result + // + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + + // A task could still be in the _Created state if it was created with a task_completion_event. + // It could also be in the _Canceled state for the same reason. + _ASSERTE(!_HasUserException() && !_IsCompleted()); + if (_IsCanceled()) + { + return; + } + + // Always transition to "completed" state, even in the face of unacknowledged pending cancellation + _M_TaskState = _Completed; + } + _M_TaskCollection._Complete(); + _RunTaskContinuations(); + } + + // + // This method is invoked when the starts executing. The task returns early if this method returns true. + // + bool _TransitionedToStarted() + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. + _ASSERT(!_IsCanceled()); + if (_IsPendingCancel()) + return false; + + _ASSERTE(_IsCreated()); + _M_TaskState = _Started; + return true; + } + +#if defined (__cplusplus_winrt) + void _SetUnwrappedAsyncOp(_AsyncOperationType^ _AsyncOp) + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. + if (_IsPendingCancel()) + { + _ASSERTE(!_IsCanceled()); + _AsyncOp->Cancel(); + } + else + { + _M_unwrapped_async_op = _AsyncOp; + } + } +#endif /* defined (__cplusplus_winrt) */ + + // Return true if the task has reached a terminal state + bool _IsDone() + { + return _IsCompleted() || _IsCanceled(); + } + + _ReturnType _GetResult() + { + return _M_Result.Get(); + } + + _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. +#if defined (__cplusplus_winrt) + _AsyncOperationType^ _M_unwrapped_async_op; +#endif /* defined (__cplusplus_winrt) */ + }; + + template<typename _ResultType> + struct _Task_completion_event_impl + { + private: + _Task_completion_event_impl(const _Task_completion_event_impl&); + _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); + + public: + + typedef std::vector<typename _Task_ptr<_ResultType>::_Type> _TaskList; + + _Task_completion_event_impl() : + _M_fHasValue(false), _M_fIsCanceled(false) + { + } + + bool _HasUserException() + { + return _M_exceptionHolder != nullptr; + } + + ~_Task_completion_event_impl() + { + for( auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt ) + { + _ASSERTE(!_M_fHasValue && !_M_fIsCanceled); + // Cancel the tasks since the event was never signaled or canceled. + (*_TaskIt)->_Cancel(true); + } + } + + // We need to protect the loop over the array, so concurrent_vector would not have helped + _TaskList _M_tasks; + ::Concurrency::extensibility::critical_section_t _M_taskListCritSec; + _ResultHolder<_ResultType> _M_value; + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + bool _M_fHasValue; + bool _M_fIsCanceled; + }; + + // Utility method for dealing with void functions + inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function<void(void)>& _Func) + { + return [=]() -> _Unit_type { _Func(); return _Unit_type(); }; + } + + template <typename _Type> + std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func) + { + return [=](_Unit_type) -> _Type { return _Func(); }; + } + + template <typename _Type> + std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function<void(_Type)>& _Func) + { + return [=](_Type t) -> _Unit_type { _Func(t); return _Unit_type(); }; + } + + inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function<void(void)>& _Func) + { + return [=](_Unit_type) -> _Unit_type { _Func(); return _Unit_type(); }; + } +} // namespace details + +/// <summary> +/// The <c>task_completion_event</c> class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// </summary> +/// <typeparam name="_ResultType"> +/// The result type of this <c>task_completion_event</c> class. +/// </typeparam> +/// <remarks> +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The <c>task_completion_event</c> must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// <para>If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed.</para> +/// <para><c>task_completion_event</c> behaves like a smart pointer, and should be passed by value.</para> +/// </remarks> +/// <seealso cref="task Class"/> +/**/ +template<typename _ResultType> +class task_completion_event +{ +public: + /// <summary> + /// Constructs a <c>task_completion_event</c> object. + /// </summary> + /**/ + task_completion_event() + : _M_Impl(std::make_shared<details::_Task_completion_event_impl<_ResultType>>()) + { + } + + /// <summary> + /// Sets the task completion event. + /// </summary> + /// <param name="_Result"> + /// The result to set this event with. + /// </param> + /// <returns> + /// The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the event is already set. + /// </returns> + /// <remarks> + /// In the presence of multiple or concurrent calls to <c>set</c>, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to their continuations. + /// </remarks> + /**/ + bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. + if (_IsTriggered()) + { + return false; + } + + _TaskList _Tasks; + bool _RunContinuations = false; + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + + if (!_IsTriggered()) + { + _M_Impl->_M_value.Set(_Result); + _M_Impl->_M_fHasValue = true; + + _Tasks.swap(_M_Impl->_M_tasks); + _RunContinuations = true; + } + } + + if (_RunContinuations) + { + for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) + { + // If current task was cancelled by a cancellation_token, it would be in cancel pending state. + if ((*_TaskIt)->_IsPendingCancel()) + (*_TaskIt)->_Cancel(true); + else + { + // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all + // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we + // need to run continuations after the lock is released. + (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); + } + } + if (_M_Impl->_HasUserException()) + { + _M_Impl->_M_exceptionHolder.reset(); + } + return true; + } + + return false; + } + + template<typename _E> + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); + } + + /// <summary> + /// Propagates an exception to all tasks associated with this event. + /// </summary> + /// <param> + /// The exception_ptr that indicates the exception to set this event with. + /// </param> + /**/ + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); + } + + /// <summary> + /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// </summary> + bool _Cancel() const + { + // Cancel with the stored exception if one exists. + return _CancelInternal(); + } + + /// <summary> + /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled + /// with the same exception. + /// </summary> + template<typename _ExHolderType> + bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const + { + bool _Canceled; + if(_StoreException(_ExHolder, _SetExceptionAddressHint)) + { + _Canceled = _CancelInternal(); + _ASSERTE(_Canceled); + } + else + { + _Canceled = false; + } + return _Canceled; + } + + /// <summary> + /// Internal method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// </summary> + template<typename _ExHolderType> + bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + if (!_IsTriggered() && !_M_Impl->_HasUserException()) + { + // Create the exception holder only if we have ensured there we will be successful in setting it onto the + // task completion event. Failing to do so will result in an unobserved task exception. + _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); + return true; + } + return false; + } + + /// <summary> + /// Tests whether current event has been either Set, or Canceled. + /// </summary> + bool _IsTriggered() const + { + return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; + } + +private: + + static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder, const details::_TaskCreationCallstack&) + { + return _ExHolder; + } + + static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) + { + return std::make_shared<details::_ExceptionHolder>(_ExceptionPtr, _SetExceptionAddressHint); + } + + + template <typename T> friend class task; // task can register itself with the event by calling the private _RegisterTask + template <typename T> friend class task_completion_event; + + typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; + + /// <summary> + /// Cancels the task_completion_event. + /// </summary> + bool _CancelInternal() const + { + // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal + // will never be invoked if the task completion event has been set. + _ASSERTE(!_M_Impl->_M_fHasValue); + if (_M_Impl->_M_fIsCanceled) + { + return false; + } + + _TaskList _Tasks; + bool _Cancel = false; + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + _ASSERTE(!_M_Impl->_M_fHasValue); + if (!_M_Impl->_M_fIsCanceled) + { + _M_Impl->_M_fIsCanceled = true; + _Tasks.swap(_M_Impl->_M_tasks); + _Cancel = true; + } + } + + bool _UserException = _M_Impl->_HasUserException(); + + if (_Cancel) + { + for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) + { + // Need to call this after the lock is released. See comments in set(). + if (_UserException) + { + (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else + { + (*_TaskIt)->_Cancel(true); + } + } + } + return _Cancel; + } + + /// <summary> + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// </summary> + void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) + { + ::Concurrency::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + + //If an exception was already set on this event, then cancel the task with the stored exception. + if(_M_Impl->_HasUserException()) + { + _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else if (_M_Impl->_M_fHasValue) + { + _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); + } + else + { + _M_Impl->_M_tasks.push_back(_TaskParam); + } + } + + std::shared_ptr<details::_Task_completion_event_impl<_ResultType>> _M_Impl; +}; + +/// <summary> +/// The <c>task_completion_event</c> class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// </summary> +/// <remarks> +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The <c>task_completion_event</c> must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// <para>If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed.</para> +/// <para><c>task_completion_event</c> behaves like a smart pointer, and should be passed by value.</para> +/// </remarks> +/// <seealso cref="task Class"/> +/**/ +template<> +class task_completion_event<void> +{ +public: + /// <summary> + /// Sets the task completion event. + /// </summary> + /// <returns> + /// The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the event is already set. + /// </returns> + /// <remarks> + /// In the presence of multiple or concurrent calls to <c>set</c>, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to their continuations. + /// </remarks> + /**/ + bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent.set(details::_Unit_type()); + } + + template<typename _E> + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); + } + + /// <summary> + /// Propagates an exception to all tasks associated with this event. + /// </summary> + /// <param> + /// The exception_ptr that indicates the exception to set this event with. + /// </param> + /**/ + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK intrinsic gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _M_unitEvent._Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); + } + + /// <summary> + /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// </summary> + void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + _M_unitEvent._Cancel(); + } + + /// <summary> + /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled + /// with the same exception. + /// </summary> + void _Cancel(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const + { + _M_unitEvent._Cancel(_ExHolder); + } + + /// <summary> + /// Method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// </summary> + bool _StoreException(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const + { + return _M_unitEvent._StoreException(_ExHolder); + } + + /// <summary> + /// Test whether current event has been either Set, or Canceled. + /// </summary> + bool _IsTriggered() const + { + return _M_unitEvent._IsTriggered(); + } + +private: + template <typename T> friend class task; // task can register itself with the event by calling the private _RegisterTask + + /// <summary> + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// </summary> + void _RegisterTask(details::_Task_ptr<details::_Unit_type>::_Type _TaskParam) + { + _M_unitEvent._RegisterTask(_TaskParam); + } + + // The void event contains an event a dummy type so common code can be used for events with void and non-void results. + task_completion_event<details::_Unit_type> _M_unitEvent; +}; + +namespace details +{ + // + // Compile-time validation helpers + // + + // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. + // + // Anything callable is fine + template<typename _ReturnType, typename _Ty> + auto _IsValidTaskCtor(_Ty _Param, int,int,int,int) -> decltype(_Param(), std::true_type()); + +#if defined (__cplusplus_winrt) + // Anything that has GetResults is fine: this covers all async operations + template<typename _ReturnType, typename _Ty> + auto _IsValidTaskCtor(_Ty _Param, int, int, int,...) -> decltype(_Param->GetResults(), std::true_type()); +#endif + + // Allow parameters with set: this covers task_completion_event + template<typename _ReturnType, typename _Ty> + auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); + + template<typename _ReturnType, typename _Ty> + auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type()); + + // All else is invalid + template<typename _ReturnType, typename _Ty> + std::false_type _IsValidTaskCtor(_Ty _Param, ...); + + template<typename _ReturnType, typename _Ty> + void _ValidateTaskConstructorArgs(_Ty _Param) + { + static_assert(std::is_same<decltype(_IsValidTaskCtor<_ReturnType>(_Param,0,0,0,0)),std::true_type>::value, +#if defined (__cplusplus_winrt) + "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" +#else /* defined (__cplusplus_winrt) */ + "incorrect argument for task constructor; can be a callable object or a task_completion_event" +#endif /* defined (__cplusplus_winrt) */ + ); +#if defined (__cplusplus_winrt) + static_assert(!(std::is_same<_Ty,_ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), + "incorrect template argument for task; consider using the return type of the async operation"); +#endif /* defined (__cplusplus_winrt) */ + } + +#if defined (__cplusplus_winrt) + // Helpers for create_async validation + // + // A parameter lambda taking no arguments is valid + template<typename _Ty> + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type()); + + // A parameter lambda taking an cancellation_token argument is valid + template<typename _Ty> + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> decltype(_Param(cancellation_token::none()), std::true_type()); + + // A parameter lambda taking a progress report argument is valid + template<typename _Ty> + static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); + + // A parameter lambda taking a progress report and a cancellation_token argument is valid + template<typename _Ty> + static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type()); + + // All else is invalid + template<typename _Ty> + static std::false_type _IsValidCreateAsync(_Ty _Param, ...); +#endif /* defined (__cplusplus_winrt) */ +} +/// <summary> +/// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a +/// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. +/// </summary> +template<typename _InpType, typename _OutType> +class _Continuation_func_transformer +{ +public: + static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) + { + return _Func; + } +}; + +template<typename _OutType> +class _Continuation_func_transformer<void, _OutType> +{ +public: + static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) + { + return details::_MakeUnitToTFunc<_OutType>(_Func); + } +}; + +template<typename _InType> +class _Continuation_func_transformer<_InType, void> +{ +public: + static auto _Perform(std::function<void(_InType)> _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) + { + return details::_MakeTToUnitFunc<_InType>(_Func); + } +}; + +template<> +class _Continuation_func_transformer<void, void> +{ +public: + static auto _Perform(std::function<void(void)> _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) + { + return details::_MakeUnitToUnitFunc(_Func); + } +}; + +// A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used +// to substitute for void). This is to minimize the special handling required for 'void'. +template<typename _RetType> +class _Init_func_transformer +{ +public: + static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) + { + return _Func; + } +}; + +template<> +class _Init_func_transformer<void> +{ +public: + static auto _Perform(std::function<void(void)> _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) + { + return details::_MakeVoidToUnitFunc(_Func); + } +}; + +/// <summary> +/// The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type <typeparamref name="_ResultType"/> on successful completion. Tasks of type <c>task<void></c> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(<c>then</c>), and join(<c>when_all</c>) and choice(<c>when_any</c>) patterns. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The result type of this task. +/// </typeparam> +/// <remarks> +/// For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>. +/// </remarks> +/**/ +template<typename _ReturnType> +class task +{ +public: + /// <summary> + /// The type of the result an object of this class produces. + /// </summary> + /**/ + typedef _ReturnType result_type; + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + task() : _M_Impl(nullptr) + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <typeparam name="_Ty"> + /// The type of the parameter from which the task is to be constructed. + /// </typeparam> + /// <param name="_Param"> + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a <c>task_completion_event<result_type></c> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>, + /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// </param> + /// <param name="_Token"> + /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives + /// the token <c>cancellation_token::none()</c>. + /// </param> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Ty> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param) + { + task_options _TaskOptions; + details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); + + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _SetTaskCreationCallstack(_CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <typeparam name="_Ty"> + /// The type of the parameter from which the task is to be constructed. + /// </typeparam> + /// <param name="_Param"> + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a <c>task_completion_event<result_type></c> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>, + /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// </param> + /// <param name="_TaskOptions"> + /// The task options include cancellation token, scheduler etc + /// </param> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Ty> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param, const task_options &_TaskOptions) + { + details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); + + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + task(const task& _Other): _M_Impl(_Other._M_Impl) {} + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + task(task&& _Other): _M_Impl(std::move(_Other._M_Impl)) {} + + /// <summary> + /// Replaces the contents of one <c>task</c> object with another. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same + /// actual task as <paramref name="_Other"/> does. + /// </remarks> + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_Impl = _Other._M_Impl; + } + return *this; + } + + /// <summary> + /// Replaces the contents of one <c>task</c> object with another. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same + /// actual task as <paramref name="_Other"/> does. + /// </remarks> + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_Impl = std::move(_Other._M_Impl); + } + return *this; + } + + /// <summary> + /// Adds a continuation task to this task. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked by this task. + /// </typeparam> + /// <param name="_Func"> + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type + /// of the result this task produces. + /// </param> + /// <returns> + /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns. + /// </returns> + /// <remarks> + /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Function> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + task_options _TaskOptions; + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + } + + /// <summary> + /// Adds a continuation task to this task. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked by this task. + /// </typeparam> + /// <param name="_Func"> + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type + /// of the result this task produces. + /// </param> + /// <param name="_TaskOptions"> + /// The task options include cancellation token, scheduler and continuation context. By default the former 3 + /// options are inherited from the antecedent task + /// </param> + /// <returns> + /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns. + /// </returns> + /// <remarks> + /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Function> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + } + + /// <summary> + /// Adds a continuation task to this task. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked by this task. + /// </typeparam> + /// <param name="_Func"> + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type + /// of the result this task produces. + /// </param> + /// <param name="_CancellationToken"> + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// </param> + /// <param name="_ContinuationContext"> + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store + /// style app. For more information, see <see cref="task_continuation_context Class">task_continuation_context</see> + /// </param> + /// <returns> + /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns. + /// </returns> + /// <remarks> + /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Function> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + } + + /// <summary> + /// Waits for this task to reach a terminal state. It is possible for <c>wait</c> to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// </summary> + /// <returns> + /// A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, <c>wait</c> will throw that exception. + /// </returns> + /**/ + task_status wait() const + { + if (!_M_Impl) + { + throw invalid_operation("wait() cannot be called on a default constructed task."); + } + + return _M_Impl->_Wait(); + } + + /// <summary> + /// Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will wait for the task to + /// finish. This method does not return a value when called on a task with a <c>result_type</c> of <c>void</c>. + /// </summary> + /// <returns> + /// The result of the task. + /// </returns> + /// <remarks> + /// If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled Class">task_canceled</see> exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to <c>get</c> will throw that exception. + /// </remarks> + /**/ + _ReturnType get() const + { + if (!_M_Impl) + { + throw invalid_operation("get() cannot be called on a default constructed task."); + } + + if (_M_Impl->_Wait() == canceled) + { + throw task_canceled(); + } + + return _M_Impl->_GetResult(); + } + + /// <summary> + /// Determines if the task is completed. + /// </summary> + /// <returns> + /// True if the task has completed, false otherwise. + /// </returns> + /// <remarks> + /// The function returns true if the task is completed or canceled (with or without user exception). + /// </remarks> + bool is_done() const + { + if (!_M_Impl) + { + throw invalid_operation("is_done() cannot be called on a default constructed task."); + } + + return _M_Impl->_IsDone(); + } + + /// <summary> + /// Returns the scheduler for this task + /// </summary> + /// <returns> + /// A pointer to the scheduler + /// </returns> + scheduler_ptr scheduler() const + { + if (!_M_Impl) + { + throw invalid_operation("scheduler() cannot be called on a default constructed task."); + } + + return _M_Impl->_GetScheduler(); + } + + /// <summary> + /// Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such a task. + /// </summary> + /// <returns> + /// <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task, <c>false</c> otherwise. + /// </returns> + /**/ + bool is_apartment_aware() const + { + if (!_M_Impl) + { + throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); + } + return _M_Impl->_IsApartmentAware(); + } + + /// <summary> + /// Determines whether two <c>task</c> objects represent the same internal task. + /// </summary> + /// <returns> + /// <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise. + /// </returns> + /**/ + bool operator==(const task<_ReturnType>& _Rhs) const + { + return (_M_Impl == _Rhs._M_Impl); + } + + /// <summary> + /// Determines whether two <c>task</c> objects represent different internal tasks. + /// </summary> + /// <returns> + /// <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise. + /// </returns> + /**/ + bool operator!=(const task<_ReturnType>& _Rhs) const + { + return !operator==(_Rhs); + } + + /// <summary> + /// Create an underlying task implementation. + /// </summary> + void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) + { + _ASSERTE(_Ct != nullptr); + _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); + if (_Ct != details::_CancellationTokenState::_None()) + { + _M_Impl->_RegisterCancellation(_M_Impl); + } + } + + /// <summary> + /// Return the underlying implementation for this task. + /// </summary> + const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const + { + return _M_Impl; + } + + /// <summary> + /// Set the implementation of the task to be the supplied implementaion. + /// </summary> + void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) + { + _ASSERTE(!_M_Impl); + _M_Impl = _Impl; + } + + /// <summary> + /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. + /// </summary> + void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) + { + _ASSERTE(!_M_Impl); + _M_Impl = std::move(_Impl); + } + + /// <summary> + /// Sets a property determining whether the task is apartment aware. + /// </summary> + void _SetAsync(bool _Async = true) + { + _GetImpl()->_SetAsync(_Async); + } + + /// <summary> + /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. + /// </summary> + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _GetImpl()->_SetTaskCreationCallstack(_callstack); + } + + /// <summary> + /// An internal version of then that takes additional flags and always execute the continuation inline by default. + /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. + /// This function is Used for runtime internal continuations only. + /// </summary> + template<typename _Function> + auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + } + +private: + template <typename T> friend class task; + + + // The task handle type used to construct an 'initial task' - a task with no dependents. + template <typename _InternalReturnType, typename _Function, typename _TypeSelection> + struct _InitialTaskHandle : + details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t> + { + _Function _M_function; + _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _func) + : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl) + , _M_function(_func) + { + } + + virtual ~_InitialTaskHandle() {} + + template <typename _Func> + auto _LogWorkItemAndInvokeUserLambda(_Func && _func) const -> decltype(_func()) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + return _func(); + } + + void _Perform() const + { + _Init(_TypeSelection()); + } + + void _SyncCancelAndPropagateException() const + { + this->_M_pTask->_Cancel(true); + } + + // + // Overload 0: returns _InternalReturnType + // + // This is the most basic task with no unwrapping + // + void _Init(details::_TypeSelectorNoAsync) const + { + this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function))); + } + + // + // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only uder /ZW) + // or + // returns task<_InternalReturnType> + // + // This is task whose functor returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Init(details::_TypeSelectorAsyncOperationOrTask) const + { + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function)); + } + +#if defined (__cplusplus_winrt) + // + // Overload 2: returns IAsyncAction^ + // + // This is task whose functor returns an async action which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncAction) const + { + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function))); + } + + // + // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^ + // + // This is task whose functor returns an async operation with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_GetProgressType<decltype(_M_function())>::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); + } + + // + // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^ + // + // This is task whose functor returns an async action with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_GetProgressType<decltype(_M_function())>::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); + } +#endif /* defined (__cplusplus_winrt) */ + }; + + + /// <summary> + /// The task handle type used to create a 'continuation task'. + /// </summary> + template <typename _InternalReturnType, typename _ContinuationReturnType, typename _Function, typename _IsTaskBased, typename _TypeSelection> + struct _ContinuationTaskHandle : + details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + { + typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; + + typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; + _Function _M_function; + + _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, + const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, + const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) + : details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + ::_PPLTaskHandle(_ContinuationImpl) + , _M_ancestorTaskImpl(_AncestorImpl) + , _M_function(_Func) + { + this->_M_isTaskBasedContinuation = _IsTaskBased::value; + this->_M_continuationContext = _Context; + this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); + this->_M_inliningMode = _InliningMode; + } + + virtual ~_ContinuationTaskHandle() {} + + template <typename _Func, typename _Arg> + auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value) const -> decltype(_func(std::forward<_Arg>(_value))) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + return _func(std::forward<_Arg>(_value)); + } + + void _Perform() const + { + _Continue(_IsTaskBased(), _TypeSelection()); + } + + void _SyncCancelAndPropagateException() const + { + if (_M_ancestorTaskImpl->_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + this->_M_pTask->_Cancel(true); + } + } + + // + // Overload 0-0: _InternalReturnType -> _TaskType + // + // This is a straight task continuation which simply invokes its target with the ancestor's completion argument + // + void _Continue(std::false_type, details::_TypeSelectorNoAsync) const + { + this->_M_pTask->_FinalizeAndRunContinuations( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult())); + } + + // + // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only uder /ZW) + // or + // _InternalReturnType -> task<_TaskType> + // + // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const + { + typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()) + ); + } + +#if defined (__cplusplus_winrt) + // + // Overload 0-2: _InternalReturnType -> IAsyncAction^ + // + // This is a straight task continuation which returns an async action which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncActionToAsyncOperationConverter( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()))); + } + + // + // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ + // + // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); + typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>(_OpWithProgress)); + } + + // + // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^ + // + // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); + typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); + } + +#endif /* defined (__cplusplus_winrt) */ + + // + // Overload 1-0: task<_InternalReturnType> -> _TaskType + // + // This is an exception handling type of continuation which takes the task rather than the task's result. + // + void _Continue(std::true_type, details::_TypeSelectorNoAsync) const + { + typedef task<_InternalReturnType> _FuncInputType; + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + this->_M_pTask->_FinalizeAndRunContinuations( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask))); + } + + // + // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ + // or + // task<_TaskType> + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation or a task which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))); + } + +#if defined (__cplusplus_winrt) + + // + // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async action which will be unwrapped for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } + + // + // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType<decltype(_M_function(_ResultTask))>::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>( + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } + + // + // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType<decltype(_M_function(_ResultTask))>::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>( + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } +#endif /* defined (__cplusplus_winrt) */ + }; + + /// <summary> + /// Initializes a task using a lambda, function pointer or function object. + /// </summary> + template<typename _InternalReturnType, typename _Function> + void _TaskInitWithFunctor(const _Function& _Func) + { + typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits; + + _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; + _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; + _M_Impl->_M_taskEventLogger._LogScheduleTask(false); + _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), details::_NoInline); + } + + /// <summary> + /// Initializes a task using a task completion event. + /// </summary> + void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) + { + _Event._RegisterTask(_M_Impl); + } + +#if defined (__cplusplus_winrt) + /// <summary> + /// Initializes a task using an asynchronous operation IAsyncOperation<T>^ + /// </summary> + void _TaskInitAsyncOp(Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>^ _AsyncOp) + { + _M_Impl->_M_fFromAsync = true; + + // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit + // returns a completion could execute concurrently and the task must be fully initialized before that happens. + _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; + // Pass the shared pointer into _AsyncInit for storage in the Async Callback. + details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp); + } + + /// <summary> + /// Initializes a task using an asynchronous operation IAsyncOperation<T>^ + /// </summary> + void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>^ _AsyncOp) + { + _TaskInitAsyncOp(_AsyncOp); + } + + /// <summary> + /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress<T, P>^ + /// </summary> + template<typename _Progress> + void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperationWithProgress<typename details::_ValueTypeOrRefType<_ReturnType>::_Value, _Progress>^ _AsyncOp) + { + _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<typename details::_ValueTypeOrRefType<_ReturnType>::_Value, _Progress>(_AsyncOp)); + } +#endif /* defined (__cplusplus_winrt) */ + + /// <summary> + /// Initializes a task using a callable object. + /// </summary> + template<typename _Function> + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _TaskInitWithFunctor<_ReturnType, _Function>(_Func); + } + + /// <summary> + /// Initializes a task using a non-callable object. + /// </summary> + template<typename _Ty> + void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } + + template<typename _InternalReturnType, typename _Function> + auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + { + if (!_M_Impl) + { + throw invalid_operation("then() cannot be called on a default constructed task."); + } + + details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); + auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); + return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); + } + + /// <summary> + /// The one and only implementation of then for void and non-void tasks. + /// </summary> + template<typename _InternalReturnType, typename _Function> + auto _ThenImpl(const _Function& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, + details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + { + if (!_M_Impl) + { + throw invalid_operation("then() cannot be called on a default constructed task."); + } + + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; + typedef details::_TaskTypeTraits<typename _Function_type_traits::_FuncRetType> _Async_type_traits; + typedef typename _Async_type_traits::_TaskRetType _TaskType; + + // + // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a + // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user + // explicitly passes the same token. + // + if (_PTokenState == nullptr) + { + if (_Function_type_traits::_Takes_task::value) + { + _PTokenState = details::_CancellationTokenState::_None(); + } + else + { + _PTokenState = _GetImpl()->_M_pTokenState; + } + } + + task<_TaskType> _ContinuationTask; + _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); + + _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); + _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; + _ContinuationTask._SetTaskCreationCallstack(_CreationStack); + + _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( + _GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode)); + + return _ContinuationTask; + } + + // The underlying implementation for this task + typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; +}; + +/// <summary> +/// The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type <typeparamref name="_ResultType"/> on successful completion. Tasks of type <c>task<void></c> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(<c>then</c>), and join(<c>when_all</c>) and choice(<c>when_any</c>) patterns. +/// </summary> +/// <remarks> +/// For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>. +/// </remarks> +/**/ +template<> +class task<void> +{ +public: + /// <summary> + /// The type of the result an object of this class produces. + /// </summary> + /**/ + typedef void result_type; + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + task() : _M_unitTask() + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <typeparam name="_Ty"> + /// The type of the parameter from which the task is to be constructed. + /// </typeparam> + /// <param name="_Param"> + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a <c>task_completion_event<result_type></c> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>, + /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// </param> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Ty> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) + { + details::_ValidateTaskConstructorArgs<void,_Ty>(_Param); + + _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + task(const task& _Other): _M_unitTask(_Other._M_unitTask){} + + /// <summary> + /// Constructs a <c>task</c> object. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as <c>get</c>, <c>wait</c> or <c>then</c> + /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task. + /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task + /// completion event is set.</para> + /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para> + /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns.</para> + /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks.</para> + /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps.</para> + /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} + + /// <summary> + /// Replaces the contents of one <c>task</c> object with another. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same + /// actual task as <paramref name="_Other"/> does. + /// </remarks> + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_unitTask = _Other._M_unitTask; + } + return *this; + } + + /// <summary> + /// Replaces the contents of one <c>task</c> object with another. + /// </summary> + /// <param name="_Other"> + /// The source <c>task</c> object. + /// </param> + /// <remarks> + /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same + /// actual task as <paramref name="_Other"/> does. + /// </remarks> + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_unitTask = std::move(_Other._M_unitTask); + } + return *this; + } + + /// <summary> + /// Adds a continuation task to this task. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked by this task. + /// </typeparam> + /// <param name="_Func"> + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type + /// of the result this task produces. + /// </param> + /// <param name="_CancellationToken"> + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// </param> + /// <returns> + /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns. + /// </returns> + /// <remarks> + /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Function> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl<void, _Function>(_Func, _TaskOptions); + } + + /// <summary> + /// Adds a continuation task to this task. + /// </summary> + /// <typeparam name="_Function"> + /// The type of the function object that will be invoked by this task. + /// </typeparam> + /// <param name="_Func"> + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type + /// of the result this task produces. + /// </param> + /// <param name="_CancellationToken"> + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// </param> + /// <param name="_ContinuationContext"> + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store + /// style app. For more information, see <see cref="task_continuation_context Class">task_continuation_context</see> + /// </param> + /// <returns> + /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns. + /// </returns> + /// <remarks> + /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para> + /// </remarks> + /**/ + template<typename _Function> + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl<void, _Function>(_Func, _TaskOptions); + } + + /// <summary> + /// Waits for this task to reach a terminal state. It is possible for <c>wait</c> to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// </summary> + /// <returns> + /// A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, <c>wait</c> will throw that exception. + /// </returns> + /**/ + task_status wait() const + { + return _M_unitTask.wait(); + } + + /// <summary> + /// Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will wait for the task to + /// finish. This method does not return a value when called on a task with a <c>result_type</c> of <c>void</c>. + /// </summary> + /// <remarks> + /// If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled Class">task_canceled</see> exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to <c>get</c> will throw that exception. + /// </remarks> + /**/ + void get() const + { + _M_unitTask.get(); + } + + /// <summary> + /// Determines if the task is completed. + /// </summary> + /// <returns> + /// True if the task has completed, false otherwise. + /// </returns> + /// <remarks> + /// The function returns true if the task is completed or canceled (with or without user exception). + /// </remarks> + bool is_done() const + { + return _M_unitTask.is_done(); + } + + /// <summary> + /// Returns the scheduler for this task + /// </summary> + /// <returns> + /// A pointer to the scheduler + /// </returns> + scheduler_ptr scheduler() const + { + return _M_unitTask.scheduler(); + } + + /// <summary> + /// Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such a task. + /// </summary> + /// <returns> + /// <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task, <c>false</c> otherwise. + /// </returns> + /**/ + bool is_apartment_aware() const + { + return _M_unitTask.is_apartment_aware(); + } + + /// <summary> + /// Determines whether two <c>task</c> objects represent the same internal task. + /// </summary> + /// <returns> + /// <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise. + /// </returns> + /**/ + bool operator==(const task<void>& _Rhs) const + { + return (_M_unitTask == _Rhs._M_unitTask); + } + + /// <summary> + /// Determines whether two <c>task</c> objects represent different internal tasks. + /// </summary> + /// <returns> + /// <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise. + /// </returns> + /**/ + bool operator!=(const task<void>& _Rhs) const + { + return !operator==(_Rhs); + } + + /// <summary> + /// Create an underlying task implementation. + /// </summary> + void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) + { + _M_unitTask._CreateImpl(_Ct, _Scheduler); + } + + /// <summary> + /// Return the underlying implementation for this task. + /// </summary> + const details::_Task_ptr<details::_Unit_type>::_Type & _GetImpl() const + { + return _M_unitTask._M_Impl; + } + + /// <summary> + /// Set the implementation of the task to be the supplied implementaion. + /// </summary> + void _SetImpl(const details::_Task_ptr<details::_Unit_type>::_Type & _Impl) + { + _M_unitTask._SetImpl(_Impl); + } + + /// <summary> + /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. + /// </summary> + void _SetImpl(details::_Task_ptr<details::_Unit_type>::_Type && _Impl) + { + _M_unitTask._SetImpl(std::move(_Impl)); + } + + /// <summary> + /// Sets a property determining whether the task is apartment aware. + /// </summary> + void _SetAsync(bool _Async = true) + { + _M_unitTask._SetAsync(_Async); + } + + /// <summary> + /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. + /// </summary> + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _M_unitTask._SetTaskCreationCallstack(_callstack); + } + + /// <summary> + /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. + /// </summary> + template<typename _Function> + auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _M_unitTask._ThenImpl<void, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + } + +private: + template <typename T> friend class task; + template <typename T> friend class task_completion_event; + + /// <summary> + /// Initializes a task using a task completion event. + /// </summary> + void _TaskInitNoFunctor(task_completion_event<void>& _Event) + { + _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); + } + +#if defined (__cplusplus_winrt) + /// <summary> + /// Initializes a task using an asynchronous action IAsyncAction^ + /// </summary> + void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction^ _AsyncAction) + { + _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction)); + } + + /// <summary> + /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^ + /// </summary> + template<typename _P> + void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P>^ _AsyncActionWithProgress) + { + _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress)); + } +#endif /* defined (__cplusplus_winrt) */ + + /// <summary> + /// Initializes a task using a callable object. + /// </summary> + template<typename _Function> + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _M_unitTask._TaskInitWithFunctor<void, _Function>(_Func); + } + + /// <summary> + /// Initializes a task using a non-callable object. + /// </summary> + template<typename _T> + void _TaskInitMaybeFunctor(_T & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } + + // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. + task<details::_Unit_type> _M_unitTask; +}; + +namespace details +{ + /// <summary> + /// The following type traits are used for the create_task function. + /// </summary> + +#if defined (__cplusplus_winrt) + // Unwrap functions for asyncOperations + template<typename _Ty> + _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty>^); + + void _GetUnwrappedType(Windows::Foundation::IAsyncAction^); + + template<typename _Ty, typename _Progress> + _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>^); + + template<typename _Progress> + void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress>^); +#endif /* defined (__cplusplus_winrt) */ + + // Unwrap task<T> + template<typename _Ty> + _Ty _GetUnwrappedType(task<_Ty>); + + // Unwrap all supportted types + template<typename _Ty> + auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); + // fallback + template<typename _Ty> + _Ty _GetUnwrappedReturnType(_Ty, ...); + + /// <summary> + /// <c>_GetTaskType</c> functions will retrieve task type <c>T</c> in <c>task[T](Arg)</c>, + /// for given constructor argument <c>Arg</c> and its property "callable". + /// It will automatically unwrap argument to get the final return type if necessary. + /// </summary> + + // Non-Callable + template<typename _Ty> + _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); + + // Non-Callable + template<typename _Ty> + auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); + + // Callable + template<typename _Ty> + auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0)); + + // Special callable returns void + void _GetTaskType(std::function<void()>, std::true_type); + struct _BadArgType{}; + + template<typename _Ty> + auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0))); + + template<typename _Ty> + _BadArgType _FilterValidTaskType(_Ty _Param, ...); + + template<typename _Ty> + struct _TaskTypeFromParam + { + typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type; + }; +} // namespace details + +/// <summary> +/// Creates a PPL <see cref="task Class">task</see> object. <c>create_task</c> can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword while creating tasks. +/// </summary> +/// <typeparam name="_Ty"> +/// The type of the parameter from which the task is to be constructed. +/// </typeparam> +/// <param name="_Param"> +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a <c>task_completion_event</c> +/// object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// </param> +/// <returns> +/// A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>. +/// </returns> +/// <remarks> +/// The first overload behaves like a task constructor that takes a single parameter. +/// <para>The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different <c>task</c> object as the first parameter.</para> +/// <para>The type of the returned task is inferred from the first parameter to the function. If <paramref name="_Param"/> is a <c>task_completion_event<T></c>, +/// a <c>task<T></c>, or a functor that returns either type <c>T</c> or <c>task<T></c>, the type of the created task is <c>task<T></c>.</para> +/// <para>In a Windows Store app, if <paramref name="_Param"/> is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type <c>task<T></c>. +/// If <paramref name="_Param"/> is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type <c>task<void></c>.</para> +/// </remarks> +/// <seealso cref="task Class"/> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _Ty> +__declspec(noinline) +auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task<typename details::_TaskTypeFromParam<_Ty>::_Type> +{ + static_assert(!std::is_same<typename details::_TaskTypeFromParam<_Ty>::_Type,details::_BadArgType>::value, +#if defined (__cplusplus_winrt) + "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" +#else /* defined (__cplusplus_winrt) */ + "incorrect argument for create_task; can be a callable object or a task_completion_event" +#endif /* defined (__cplusplus_winrt) */ + ); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + task<typename details::_TaskTypeFromParam<_Ty>::_Type> _CreatedTask(_Param, _TaskOptions); + return _CreatedTask; +} + +/// <summary> +/// Creates a PPL <see cref="task Class">task</see> object. <c>create_task</c> can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword while creating tasks. +/// </summary> +/// <typeparam name="_Ty"> +/// The type of the parameter from which the task is to be constructed. +/// </typeparam> +/// <param name="_Param"> +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a <c>task_completion_event</c> +/// object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// </param> +/// <param name="_Token"> +/// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. +/// </param> +/// <returns> +/// A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>. +/// </returns> +/// <remarks> +/// The first overload behaves like a task constructor that takes a single parameter. +/// <para>The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different <c>task</c> object as the first parameter.</para> +/// <para>The type of the returned task is inferred from the first parameter to the function. If <paramref name="_Param"/> is a <c>task_completion_event<T></c>, +/// a <c>task<T></c>, or a functor that returns either type <c>T</c> or <c>task<T></c>, the type of the created task is <c>task<T></c>.</para> +/// <para>In a Windows Store app, if <paramref name="_Param"/> is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type <c>task<T></c>. +/// If <paramref name="_Param"/> is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type <c>task<void></c>.</para> +/// </remarks> +/// <seealso cref="task Class"/> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +__declspec(noinline) +task<_ReturnType> create_task(const task<_ReturnType>& _Task) +{ + task<_ReturnType> _CreatedTask(_Task); + return _CreatedTask; +} + +#if defined (__cplusplus_winrt) +namespace details +{ + template<typename _T> + task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T>^ op) + { + return task<_T>(op); + } + + template<typename _T, typename _Progress> + task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>^ op) + { + return task<_T>(op); + } + + inline task<void> _To_task_helper(Windows::Foundation::IAsyncAction^ op) + { + return task<void>(op); + } + + template<typename _Progress> + task<void> _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ op) + { + return task<void>(op); + } + + template<typename _ProgressType> + class _ProgressDispatcherBase + { + public: + + virtual ~_ProgressDispatcherBase() + { + } + + virtual void _Report(const _ProgressType& _Val) = 0; + }; + + template<typename _ProgressType, typename _ClassPtrType> + class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> + { + public: + + virtual ~_ProgressDispatcher() + { + } + + _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) + { + } + + virtual void _Report(const _ProgressType& _Val) + { + _M_ptr->_FireProgress(_Val); + } + + private: + + _ClassPtrType _M_ptr; + }; + class _ProgressReporterCtorArgType{}; +} // namespace details + +/// <summary> +/// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound +/// to a particular asynchronous action or operation. +/// </summary> +/// <typeparam name="_ProgressType"> +/// The payload type of each progress notification reported through the progress reporter. +/// </typeparam> +/// <remarks> +/// This type is only available to Windows Store apps. +/// </remarks> +/// <seealso cref="create_async Function"/> +/**/ +template<typename _ProgressType> +class progress_reporter +{ + typedef std::shared_ptr<details::_ProgressDispatcherBase<_ProgressType>> _PtrType; + +public: + + /// <summary> + /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. + /// </summary> + /// <param name="_Val"> + /// The payload to report through a progress notification. + /// </param> + /**/ + void report(const _ProgressType& _Val) const + { + _M_dispatcher->_Report(_Val); + } + + template<typename _ClassPtrType> + static progress_reporter _CreateReporter(_ClassPtrType _Ptr) + { + progress_reporter _Reporter; + details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); + _Reporter._M_dispatcher = _PtrType(_PDispatcher); + return _Reporter; + } + progress_reporter() {} + +private: + progress_reporter(details::_ProgressReporterCtorArgType); + + _PtrType _M_dispatcher; +}; + +namespace details +{ + // + // maps internal definitions for AsyncStatus and defines states that are not client visible + // + enum _AsyncStatusInternal + { + _AsyncCreated = -1, // externally invisible + // client visible states (must match AsyncStatus exactly) + _AsyncStarted = 0, // Windows::Foundation::AsyncStatus::Started, + _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed, + _AsyncCanceled = 2, // Windows::Foundation::AsyncStatus::Canceled, + _AsyncError = 3, // Windows::Foundation::AsyncStatus::Error, + // non-client visible internal states + _AsyncCancelPending, + _AsyncClosed, + _AsyncUndefined + }; + + // + // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results + // (which are progressively consumable between Start state and before Close is called) + // + enum _AsyncResultType + { + SingleResult = 0x0001, + MultipleResults = 0x0002 + }; + + // *************************************************************************** + // Template type traits and helpers for async production APIs: + // + + struct _ZeroArgumentFunctor { }; + struct _OneArgumentFunctor { }; + struct _TwoArgumentFunctor { }; + + // **************************************** + // CLASS TYPES: + + // ******************** + // TWO ARGUMENTS: + + // non-void arg: + template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + // non-void arg: + template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2> + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2> + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + // ******************** + // ONE ARGUMENT: + + // non-void arg: + template<typename _Class, typename _ReturnType, typename _Arg1> + _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + // non-void arg: + template<typename _Class, typename _ReturnType, typename _Arg1> + void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + template<typename _Class, typename _ReturnType, typename _Arg1> + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + template<typename _Class, typename _ReturnType, typename _Arg1> + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const); + + // ******************** + // ZERO ARGUMENT: + + // void arg: + template<typename _Class, typename _ReturnType> + void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const); + + // void arg: + template<typename _Class, typename _ReturnType> + void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const); + + // void arg: + template<typename _Class, typename _ReturnType> + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const); + + template<typename _Class, typename _ReturnType> + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const); + + // **************************************** + // POINTER TYPES: + + // ******************** + // TWO ARGUMENTS: + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template<typename _ReturnType, typename _Arg1, typename _Arg2> + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + // ******************** + // ONE ARGUMENT: + + template<typename _ReturnType, typename _Arg1> + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template<typename _ReturnType, typename _Arg1> + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); + + // ******************** + // ZERO ARGUMENT: + + template<typename _ReturnType> + void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); + + template<typename _ReturnType> + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); + + template<typename _ReturnType> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); + + template<typename _ReturnType> + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); + + template<typename _ReturnType> + void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); + + template<typename _ReturnType> + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); + + template<typename _ReturnType> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); + + template<typename _ReturnType> + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); + + template<typename _ReturnType> + void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); + + template<typename _ReturnType> + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); + + template<typename _ReturnType> + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); + + template<typename _ReturnType> + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); + + template<typename _T> + struct _FunctorArguments + { + static const size_t _Count = 0; + }; + + template<> + struct _FunctorArguments<_OneArgumentFunctor> + { + static const size_t _Count = 1; + }; + + template<> + struct _FunctorArguments<_TwoArgumentFunctor> + { + static const size_t _Count = 2; + }; + + template<typename _T> + struct _FunctorTypeTraits + { + typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; + typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; + typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; + }; + + template<typename _T> + struct _FunctorTypeTraits<_T *> + { + typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; + typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; + typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; + }; + + template<typename _T> + struct _ProgressTypeTraits + { + static const bool _TakesProgress = false; + typedef void _ProgressType; + }; + + template<typename _T> + struct _ProgressTypeTraits<progress_reporter<_T>> + { + static const bool _TakesProgress = true; + typedef typename _T _ProgressType; + }; + + + template<typename _T, size_t count = _FunctorTypeTraits<_T>::_ArgumentCount> + struct _CAFunctorOptions + { + static const bool _TakesProgress = false; + static const bool _TakesToken = false; + typedef void _ProgressType; + }; + + template<typename _T> + struct _CAFunctorOptions<_T, 1> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; + static const bool _TakesToken = !_TakesProgress; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + }; + + template<typename _T> + struct _CAFunctorOptions<_T, 2> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = true; + static const bool _TakesToken = true; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + }; + + ref class _Zip + { + }; + + // *************************************************************************** + // Async Operation Task Generators + // + + // + // Functor returns an IAsyncInfo - result needs to be wrapped in a task: + // + template<typename _AsyncSelector, typename _ReturnType> + struct _SelectorTaskGenerator + { + template<typename _Function> + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(), _taskOptinos); + } + + template<typename _Function> + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress), _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos); + } + }; + + template<typename _AsyncSelector> + struct _SelectorTaskGenerator<_AsyncSelector, void> + { + template<typename _Function> + static task<void> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>(_Func(), _taskOptinos); + } + + template<typename _Function> + static task<void> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>(_Func(_Cts.get_token()), _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<void> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>(_Func(_Progress), _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<void> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>(_Func(_Progress, _Cts.get_token()), _taskOptinos); + } + }; + + // + // Functor returns a result - it needs to be wrapped in a task: + // + template<typename _ReturnType> + struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType> + { + +#pragma warning(push) +#pragma warning(disable: 4702) + template<typename _Function> + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(); + }, _taskOptinos); + } +#pragma warning(pop) + + template<typename _Function> + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Cts.get_token()); + }, _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress); + }, _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress, _Cts.get_token()); + }, _taskOptinos); + } + }; + + template<> + struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void> + { + template<typename _Function> + static task<void> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(); + }, _taskOptinos); + } + + template<typename _Function> + static task<void> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Cts.get_token()); + }, _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<void> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Progress); + }, _taskOptinos); + } + + template<typename _Function, typename _ProgressObject> + static task<void> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<void>( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Progress, _Cts.get_token()); + }, _taskOptinos); + } + }; + + // + // Functor returns a task - the task can directly be returned: + // + template<typename _ReturnType> + struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType> + { + template<typename _Function> + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(); + } + + template<typename _Function> + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Cts.get_token()); + } + + template<typename _Function, typename _ProgressObject> + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress); + } + + template<typename _Function, typename _ProgressObject> + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress, _Cts.get_token()); + } + }; + + template<> + struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void> + { + template<typename _Function> + static task<void> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(); + } + + template<typename _Function> + static task<void> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Cts.get_token()); + } + + template<typename _Function, typename _ProgressObject> + static task<void> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress); + } + + template<typename _Function, typename _ProgressObject> + static task<void> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress, _Cts.get_token()); + } + }; + + template<typename _Generator, bool _TakesToken, bool TakesProgress> + struct _TaskGenerator + { + }; + + template<typename _Generator> + struct _TaskGenerator<_Generator, false, false> + { + template<typename _Function, typename _ClassPtr, typename _ProgressType> + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); + } + }; + + template<typename _Generator> + struct _TaskGenerator<_Generator, true, false> + { + template<typename _Function, typename _ClassPtr, typename _ProgressType> + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); + } + }; + + template<typename _Generator> + struct _TaskGenerator<_Generator, false, true> + { + template<typename _Function, typename _ClassPtr, typename _ProgressType> + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + }; + + template<typename _Generator> + struct _TaskGenerator<_Generator, true, true> + { + template<typename _Function, typename _ClassPtr, typename _ProgressType> + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + }; + + // *************************************************************************** + // Async Operation Attributes Classes + // + // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in + // a single container. An attribute class must define: + // + // Mandatory: + // ------------------------- + // + // _AsyncBaseType : The Windows Runtime interface which is being implemented. + // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. + // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. + // _ReturnType : The return type of the async construct (void for actions / non-void for operations) + // + // _TakesProgress : An indication as to whether or not + // + // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task + // + // Optional: + // ------------------------- + // + + template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken, bool _TakesProgress> + struct _AsyncAttributes + { + }; + + template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken> + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> + { + typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; + typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename _ProgressType _ProgressType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + + template<typename _Function, typename _ClassPtr> + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken> + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> + { + typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + + template<typename _Function, typename _ClassPtr> + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken> + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> + { + typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; + typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; + typedef void _ReturnType; + typedef typename _ProgressType _ProgressType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + + template<typename _Function, typename _ClassPtr> + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken> + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> + { + typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType; + typedef void _ReturnType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + + template<typename _Function, typename _ClassPtr> + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template<typename _Function> + struct _AsyncLambdaTypeTraits + { + typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType; + typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; + typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; + + static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; + static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; + + typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; + typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; + }; + + // *************************************************************************** + // AsyncInfo (and completion) Layer: + // + + // + // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) + // + template < typename _Attributes, _AsyncResultType resultType = SingleResult > + ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType + { + internal: + + _AsyncInfoBase() : + _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), + _M_errorCode(S_OK), + _M_completeDelegate(nullptr), + _M_CompleteDelegateAssigned(0), + _M_CallbackMade(0) + { + _M_id = ::Concurrency::details::platform::GetNextAsyncId(); + } + + public: + virtual typename _Attributes::_ReturnType GetResults() + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + virtual property unsigned int Id + { + unsigned int get() + { + _CheckValidStateForAsyncInfoCall(); + + return _M_id; + } + + void set(unsigned int id) + { + _CheckValidStateForAsyncInfoCall(); + + if (id == 0) + { + throw ::Platform::Exception::CreateException(E_INVALIDARG); + } + else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + + _M_id = id; + } + } + + virtual property Windows::Foundation::AsyncStatus Status + { + Windows::Foundation::AsyncStatus get() + { + _CheckValidStateForAsyncInfoCall(); + + _AsyncStatusInternal _Current = _M_currentStatus; + + // + // Map our internal cancel pending to cancelled. This way "pending cancelled" looks to the outside as "cancelled" but + // can still transition to "completed" if the operation completes without acknowledging the cancellation request + // + switch(_Current) + { + case _AsyncCancelPending: + _Current = _AsyncCanceled; + break; + case _AsyncCreated: + _Current = _AsyncStarted; + break; + default: + break; + } + + return static_cast<Windows::Foundation::AsyncStatus>(_Current); + } + } + + virtual property Windows::Foundation::HResult ErrorCode + { + Windows::Foundation::HResult get() + { + _CheckValidStateForAsyncInfoCall(); + + Windows::Foundation::HResult _Hr; + _Hr.Value = _M_errorCode; + return _Hr; + } + } + + virtual property typename _Attributes::_ProgressDelegateType^ Progress + { + typename typename _Attributes::_ProgressDelegateType^ get() + { + return _GetOnProgress(); + } + + void set(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) + { + _PutOnProgress(_ProgressHandler); + } + } + + virtual void Cancel() + { + if (_TransitionToState(_AsyncCancelPending)) + { + _OnCancel(); + } + } + + virtual void Close() + { + if (_TransitionToState(_AsyncClosed)) + { + _OnClose(); + } + else + { + if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); + } + } + } + + virtual property typename _Attributes::_CompletionDelegateType^ Completed + { + typename _Attributes::_CompletionDelegateType^ get() + { + _CheckValidStateForDelegateCall(); + return _M_completeDelegate; + } + + void set(typename _Attributes::_CompletionDelegateType^ _CompleteHandler) + { + _CheckValidStateForDelegateCall(); + // this delegate property is "write once" + if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) + { + _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); + _M_completeDelegate = _CompleteHandler; + // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below + // as perceived from _FireCompletion on another thread. + MemoryBarrier(); + if (_IsTerminalState()) + { + _FireCompletion(); + } + } + else + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT); + } + } + } + + + protected private: + + // _Start - this is not externally visible since async operations "hot start" before returning to the caller + void _Start() + { + if (_TransitionToState(_AsyncStarted)) + { + _OnStart(); + } + else + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); + } + } + + + void _FireCompletion() + { + _TryTransitionToCompleted(); + + // we guarantee that completion can only ever be fired once + if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) + { + _M_completeDelegateContext._CallInContext([=] { + _M_completeDelegate((_Attributes::_AsyncBaseType^)this, this->Status); + _M_completeDelegate = nullptr; + }); + } + } + + virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + bool _TryTransitionToCompleted() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); + } + + bool _TryTransitionToCancelled() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); + } + + bool _TryTransitionToError(const HRESULT error) + { + _InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(&_M_errorCode), error, S_OK); + return _TransitionToState(_AsyncStatusInternal::_AsyncError); + } + + // This method checks to see if the delegate properties can be + // modified in the current state and generates the appropriate + // error hr in the case of violation. + inline void _CheckValidStateForDelegateCall() + { + if (_M_currentStatus == _AsyncClosed) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + + // This method checks to see if results can be collected in the + // current state and generates the appropriate error hr in + // the case of a violation. + inline void _CheckValidStateForResultsCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + + if (_Current == _AsyncError) + { + throw ::Platform::Exception::CreateException(_M_errorCode); + } +#pragma warning(push) +#pragma warning(disable: 4127) // Conditional expression is constant + // single result illegal before transition to Completed or Cancelled state + if (resultType == SingleResult) +#pragma warning(pop) + { + if (_Current != _AsyncCompleted) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + // multiple results can be called after Start has been called and before/after Completed + else if (_Current != _AsyncStarted && + _Current != _AsyncCancelPending && + _Current != _AsyncCanceled && + _Current != _AsyncCompleted) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + + // This method can be called by derived classes periodically to determine + // whether the asynchronous operation should continue processing or should + // be halted. + inline bool _ContinueAsyncOperation() + { + return (_M_currentStatus == _AsyncStarted); + } + + // These two methods are used to allow the async worker implementation do work on + // state transitions. No real "work" should be done in these methods. In other words + // they should not block for a long time on UI timescales. + virtual void _OnStart() = 0; + virtual void _OnClose() = 0; + virtual void _OnCancel() = 0; + + private: + + // This method is used to check if calls to the AsyncInfo properties + // (id, status, errorcode) are legal in the current state. It also + // generates the appropriate error hr to return in the case of an + // illegal call. + inline void _CheckValidStateForAsyncInfoCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + if (_Current == _AsyncClosed) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + else if (_Current == _AsyncCreated) + { + throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED); + } + + } + + inline bool _TransitionToState(const _AsyncStatusInternal _NewState) + { + _AsyncStatusInternal _Current = _M_currentStatus; + + // This enforces the valid state transitions of the asynchronous worker object + // state machine. + switch(_NewState) + { + case _AsyncStatusInternal::_AsyncStarted: + if (_Current != _AsyncCreated) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCompleted: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCancelPending: + if (_Current != _AsyncStarted) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCanceled: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncError: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncClosed: + if (!_IsTerminalState(_Current)) + { + return false; + } + break; + default: + return false; + break; + } + + // attempt the transition to the new state + // Note: if currentStatus_ == _Current, then there was no intervening write + // by the async work object and the swap succeeded. + _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( + _InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(&_M_currentStatus), + _NewState, + static_cast<LONG>(_Current))); + + // ICE returns the former state, if the returned state and the + // state we captured at the beginning of this method are the same, + // the swap succeeded. + return (_RetState == _Current); + } + + inline bool _IsTerminalState() + { + return _IsTerminalState(_M_currentStatus); + } + + inline bool _IsTerminalState(_AsyncStatusInternal status) + { + return (status == _AsyncError || + status == _AsyncCanceled || + status == _AsyncCompleted || + status == _AsyncClosed); + } + + private: + + _ContextCallback _M_completeDelegateContext; + typename _Attributes::_CompletionDelegateType^ volatile _M_completeDelegate; + _AsyncStatusInternal volatile _M_currentStatus; + HRESULT volatile _M_errorCode; + unsigned int _M_id; + long volatile _M_CompleteDelegateAssigned; + long volatile _M_CallbackMade; + }; + + // *************************************************************************** + // Progress Layer (optional): + // + + template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > + ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType> + { + }; + + template< typename _Attributes, _AsyncResultType _ResultType> + ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType> + { + internal: + + _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), + _M_progressDelegate(nullptr) + { + } + + virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() override + { + _CheckValidStateForDelegateCall(); + return _M_progressDelegate; + } + + virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) override + { + _CheckValidStateForDelegateCall(); + _M_progressDelegate = _ProgressHandler; + _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); + } + + void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue) + { + if (_M_progressDelegate != nullptr) + { + _M_progressDelegateContext._CallInContext([=] { + _M_progressDelegate((_Attributes::_AsyncBaseType^)this, _ProgressValue); + }); + } + } + + private: + + _ContextCallback _M_progressDelegateContext; + typename _Attributes::_ProgressDelegateType^ _M_progressDelegate; + }; + + template<typename _Attributes, _AsyncResultType _ResultType = SingleResult> + ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> + { + }; + + // *************************************************************************** + // Task Adaptation Layer: + // + + // + // _AsyncTaskThunkBase provides a bridge between IAsync<Action/Operation> and task. + // + template<typename _Attributes, typename _ReturnType> + ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes> + { + public: + + virtual _ReturnType GetResults() override + { + _CheckValidStateForResultsCall(); + return _M_task.get(); + } + + internal: + + typedef task<_ReturnType> _TaskType; + + _AsyncTaskThunkBase(const _TaskType& _Task) + : _M_task(_Task) + { + } + + _AsyncTaskThunkBase() + { + } + + protected: + + virtual void _OnStart() override + { + _M_task.then( [=](_TaskType _Antecedent) { + try + { + _Antecedent.get(); + } + catch(task_canceled&) + { + _TryTransitionToCancelled(); + } + catch(::Platform::Exception^ _Ex) + { + _TryTransitionToError(_Ex->HResult); + } + catch(...) + { + _TryTransitionToError(E_FAIL); + } + _FireCompletion(); + }); + } + + internal: + + _TaskType _M_task; + cancellation_token_source _M_cts; + }; + + template<typename _Attributes> + ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType> + { + internal: + + _AsyncTaskThunk(const _TaskType& _Task) : + _AsyncTaskThunkBase(_Task) + { + } + + _AsyncTaskThunk() + { + } + + protected: + + virtual void _OnClose() override + { + } + + virtual void _OnCancel() override + { + _M_cts.cancel(); + } + }; + + // *************************************************************************** + // Async Creation Layer: + // + template<typename _Function> + ref class _AsyncTaskGeneratorThunk sealed : _AsyncTaskThunk<typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes> + { + internal: + + typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; + typedef typename _AsyncTaskThunk<_Attributes> _Base; + typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; + + _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) + { + // Virtual call here is safe as the class is declared 'sealed' + _Start(); + } + + protected: + + // + // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, + // let the base thunk handle everything. + // + + virtual void _OnStart() override + { + // + // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, + // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. + // + _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack); + _Base::_OnStart(); + } + + virtual void _OnCancel() override + { + _Base::_OnCancel(); + } + + private: + _TaskCreationCallstack _M_creationCallstack; + _Function _M_func; + }; +} // namespace details + +/// <summary> +/// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of <c>create_async</c> is +/// one of either <c>IAsyncAction^</c>, <c>IAsyncActionWithProgress<TProgress>^</c>, <c>IAsyncOperation<TResult>^</c>, or +/// <c>IAsyncOperationWithProgress<TResult, TProgress>^</c> based on the signature of the lambda passed to the method. +/// </summary> +/// <param name="_Func"> +/// The lambda or function object from which to create a Windows Runtime asynchronous construct. +/// </param> +/// <returns> +/// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an +/// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. +/// </returns> +/// <remarks> +/// The return type of the lambda determines whether the construct is an action or an operation. +/// <para>Lambdas that return void cause the creation of actions. Lambdas that return a result of type <c>TResult</c> cause the creation of +/// operations of TResult.</para> +/// <para>The lambda may also return a <c>task<TResult></c> which encapsulates the aysnchronous work within itself or is the continuation of +/// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that +/// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by <c>create_async</c>. +/// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will +/// cause the creation of operations of TResult.</para> +/// <para>The lambda may take either zero, one or two arguments. The valid arguments are <c>progress_reporter<TProgress></c> and +/// <c>cancellation_token</c>, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without +/// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause <c>create_async</c> to return an asynchronous +/// construct which reports progress of type TProgress each time the <c>report</c> method of the progress_reporter object is called. A lambda that +/// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the +/// asynchronous construct causes cancellation of those tasks.</para> +/// <para>If the body of the lambda or function object returns a result (and not a task<TResult>), the lamdba will be executed +/// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The <c>IAsyncInfo::Cancel</c> method will +/// cause cancellation of the implicit task.</para> +/// <para>If the body of the lambda returns a task, the lamba executes inline, and by declaring the lambda to take an argument of type +/// <c>cancellation_token</c> you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. +/// You may also use the <c>register_callback</c> method on the token to cause the Runtime to invoke a callback when you call <c>IAsyncInfo::Cancel</c> on +/// the async operation or action produced..</para> +/// <para>This function is only available to Windows Store apps.</para> +/// </remarks> +/// <seealso cref="task Class"/> +/// <seealso cref="progress_reporter Class"/> +/// <seealso cref="cancelation_token Class"/> +/**/ +template<typename _Function> +__declspec(noinline) +details::_AsyncTaskGeneratorThunk<_Function> ^create_async(const _Function& _Func) +{ + static_assert(std::is_same<decltype(details::_IsValidCreateAsync(_Func,0,0,0,0)),std::true_type>::value, + "argument to create_async must be a callable object taking zero, one or two arguments"); + return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, _CAPTURE_CALLSTACK()); +} + +#endif /* defined (__cplusplus_winrt) */ + +namespace details +{ + // Helper struct for when_all operators to know when tasks have completed + template<typename _Type> + struct _RunAllParam + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + if (!_SkipVector) + { + _M_vector._Result.resize(_Len); + } + } + + task_completion_event<_Unit_type> _M_completed; + _ResultHolder<std::vector<_Type> > _M_vector; + _ResultHolder<_Type> _M_mergeVal; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + template<typename _Type> + struct _RunAllParam<std::vector<_Type> > + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + + if (!_SkipVector) + { + _M_vector.resize(_Len); + } + } + + task_completion_event<_Unit_type> _M_completed; + std::vector<_ResultHolder<std::vector<_Type> > > _M_vector; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + // Helper struct specialization for void + template<> + struct _RunAllParam<_Unit_type> + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len) + { + _M_numTasks = _Len; + } + + task_completion_event<_Unit_type> _M_completed; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc, _CancellationTokenState *_PJoinedTokenState) + { + if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None()) + { + cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState); + _T.register_callback( [=](){ + _MergedSrc.cancel(); + }); + } + } + + template<typename _ElementType, typename _Function, typename _TaskType> + void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) + { + if (_Task._GetImpl()->_IsCompleted()) + { + _Func(); + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + // Inline execute its direct continuation, the _ReturnTask + _PParam->_M_completed.set(_Unit_type()); + // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. + delete _PParam; + } + } + else + { + _ASSERTE(_Task._GetImpl()->_IsCanceled()); + if (_Task._GetImpl()->_HasUserException()) + { + // _Cancel will return false if the TCE is already canceled with or without exception + _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); + } + else + { + _PParam->_M_completed._Cancel(); + } + + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + delete _PParam; + } + } + } + + template<typename _ElementType, typename _Iterator> + struct _WhenAllImpl + { + static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam<_ElementType>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { + return _PParam->_M_vector.Get(); + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { + + // Dev10 compiler bug + typedef _ElementType _ElementTypeDev10; + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ + _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template<typename _ElementType, typename _Iterator> + struct _WhenAllImpl<std::vector<_ElementType>, _Iterator> + { + static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam<std::vector<_ElementType>>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { + _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks); + std::vector<_ElementType> _Result; + for(size_t _I = 0; _I < _PParam->_M_numTasks; _I++) + { + const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); + _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); + } + return _Result; + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<std::vector<_ElementType>> _ResultTask) { + // Dev10 compiler bug + typedef _ElementType _ElementTypeDev10; + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { + _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template<typename _Iterator> + struct _WhenAllImpl<void, _Iterator> + { + static task<void> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam<_Unit_type>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) { + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam](task<void> _ResultTask) { + auto _Func = [](){}; + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + } + } + + return _ReturnTask; + } + }; + + template<typename _ReturnType> + task<std::vector<_ReturnType>> _WhenAllVectorAndValue(const task<std::vector<_ReturnType>>& _VectorTask, const task<_ReturnType>& _ValueTask, + bool _OutputVectorFirst) + { + auto _PParam = new _RunAllParam<_ReturnType>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ReturnType> { + _ASSERTE(_PParam->_M_completeCount == 2); + auto _Result = _PParam->_M_vector.Get(); // copy by value + auto _mergeVal = _PParam->_M_mergeVal.Get(); + + if (_OutputVectorFirst == true) + { + _Result.push_back(_mergeVal); + } + else + { + _Result.insert(_Result.begin(), _mergeVal); + } + return _Result; + }, nullptr); + + // Step2: Combine and check tokens. + _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); + _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); + + // Step3: Check states of previous tasks. + _PParam->_Resize(2, true); + + if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + _VectorTask._Then([_PParam](task<std::vector<_ReturnType>> _ResultTask) { + // Dev10 compiler bug + typedef _ReturnType _ReturnTypeDev10; + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_vector.Set(_ResultLocal); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) { + // Dev10 compiler bug + typedef _ReturnType _ReturnTypeDev10; + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_mergeVal.Set(_ResultLocal); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + return _ReturnTask; + } +} // namespace details + +/// <summary> +/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. +/// </summary> +/// <typeparam name="_Iterator"> +/// The type of the input iterator. +/// </typeparam> +/// <param name="_Begin"> +/// The position of the first element in the range of elements to be combined into the resulting task. +/// </param> +/// <param name="_End"> +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// </param> +/// <returns> +/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output +/// task will also be a <c>task<void></c>. +/// </returns> +/// <remarks> +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template <typename _Iterator> +auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) + -> decltype (details::_WhenAllImpl<typename std::iterator_traits<_Iterator>::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} + +/// <summary> +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output +/// task will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para> +/// </returns> +/// <remarks> +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +task<std::vector<_ReturnType>> operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) +{ + task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +/// <summary> +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output +/// task will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para> +/// </returns> +/// <remarks> +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +task<std::vector<_ReturnType>> operator&&(const task<std::vector<_ReturnType>> & _Lhs, const task<_ReturnType> & _Rhs) +{ + return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); +} + +/// <summary> +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output +/// task will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para> +/// </returns> +/// <remarks> +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +task<std::vector<_ReturnType>> operator&&(const task<_ReturnType> & _Lhs, const task<std::vector<_ReturnType>> & _Rhs) +{ + return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); +} + +/// <summary> +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output +/// task will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para> +/// </returns> +/// <remarks> +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +task<std::vector<_ReturnType>> operator&&(const task<std::vector<_ReturnType>> & _Lhs, const task<std::vector<_ReturnType>> & _Rhs) +{ + task<std::vector<_ReturnType>> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +/// <summary> +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output +/// task will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para> +/// </returns> +/// <remarks> +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +inline task<void> operator&&(const task<void> & _Lhs, const task<void> & _Rhs) +{ + task<void> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +namespace details +{ + // Helper struct for when_any operators to know when tasks have completed + template <typename _CompletionType> + struct _RunAnyParam + { + _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false) + { + } + ~_RunAnyParam() + { + if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) + _M_exceptionRelatedToken->_Release(); + } + task_completion_event<_CompletionType> _M_Completed; + cancellation_token_source _M_cancellationSource; + _CancellationTokenState * _M_exceptionRelatedToken; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + bool _M_fHasExplicitToken; + }; + + template<typename _CompletionType, typename _Function, typename _TaskType> + void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) + { + bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); + if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) + { + _Func(); + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + delete _PParam; + } + } + else + { + _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); + if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) + { + if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) + { + // This can only enter once. + _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; + _ASSERTE(_PParam->_M_exceptionRelatedToken); + // Deref token will be done in the _PParam destructor. + if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None()) + { + _PParam->_M_exceptionRelatedToken->_Reference(); + } + } + } + + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + // If no one has be completed so far, we need to make some final cancellation decision. + if (!_PParam->_M_Completed._IsTriggered()) + { + // If we already explicit token, we can skip the token join part. + if (!_PParam->_M_fHasExplicitToken) + { + if (_PParam->_M_exceptionRelatedToken) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); + } + else + { + // If haven't captured any exception token yet, there was no exception for all those tasks, + // so just pick a random token (current one) for normal cancellation. + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); + } + } + // Do exception cancellation or normal cancellation based on whether it has stored exception. + _PParam->_M_Completed._Cancel(); + } + delete _PParam; + } + } + } + + template<typename _ElementType, typename _Iterator> + struct _WhenAnyImpl + { + static task<std::pair<_ElementType, size_t>> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + if( _Begin == _End ) + { + throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _PParam = new _RunAnyParam<std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState *>>(); + + if (_PTokenState) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } + + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task<std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); + + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast<size_t>(std::distance(_Begin, _End)); + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = _Index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); + }; + + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState *> _Result) -> std::pair<_ElementType, size_t> { + _ASSERTE(_Result.second); + if (!_PTokenState) + { + _JoinAllTokens_Add(_CancellationSource, _Result.second); + } + return _Result.first; + }, nullptr); + } + }; + + template<typename _Iterator> + struct _WhenAnyImpl<void, _Iterator> + { + static task<size_t> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + if( _Begin == _End ) + { + throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } + + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _PParam = new _RunAnyParam<std::pair<size_t, _CancellationTokenState *>>(); + + if (_PTokenState) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } + + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task<std::pair<size_t, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); + + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast<size_t>(std::distance(_Begin, _End)); + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<void> _ResultTask) { + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = _Index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair<size_t, _CancellationTokenState *> _Result) -> size_t { + _ASSERTE(_Result.second); + if (!_PTokenState) + { + _JoinAllTokens_Add(_CancellationSource, _Result.second); + } + return _Result.first; + }, nullptr); + } + }; +} // namespace details + +/// <summary> +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// </summary> +/// <typeparam name="_Iterator"> +/// The type of the input iterator. +/// </typeparam> +/// <param name="_Begin"> +/// The position of the first element in the range of elements to be combined into the resulting task. +/// </param> +/// <param name="_End"> +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// </param> +/// <returns> +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::pair<T, size_t>>></c>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type <c>void</c> +/// the output is a <c>task<size_t></c>, where the result is the index of the completing task. +/// </returns> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _Iterator> +auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) + -> decltype (details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} + +/// <summary> +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// </summary> +/// <typeparam name="_Iterator"> +/// The type of the input iterator. +/// </typeparam> +/// <param name="_Begin"> +/// The position of the first element in the range of elements to be combined into the resulting task. +/// </param> +/// <param name="_End"> +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// </param> +/// <param name="_CancellationToken"> +/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting +/// task will receive the cancellation token of the task that causes it to complete. +/// </param> +/// <returns> +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::pair<T, size_t>>></c>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type <c>void</c> +/// the output is a <c>task<size_t></c>, where the result is the index of the completing task. +/// </returns> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _Iterator> +auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken) + -> decltype (details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); +} + +/// <summary> +/// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task +/// will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c> +/// and the other one is of type <c>task<T>.</c></para> +/// </returns> +/// <remarks> +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam<std::pair<_ReturnType, size_t>>(); + + task<std::pair<_ReturnType, size_t>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType { + _ASSERTE(_Ret.second); + _JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast<details::_CancellationTokenState *>(_Ret.second)); + return _Ret.first; + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) { + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast<size_t>(_ResultTask._GetImpl()->_M_pTokenState))); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }; + + _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +/// <summary> +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task +/// will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c> +/// and the other one is of type <c>task<T>.</c></para> +/// </returns> +/// <remarks> +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +task<std::vector<_ReturnType>> operator||(const task<std::vector<_ReturnType>> & _Lhs, const task<_ReturnType> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam<std::pair<std::vector<_ReturnType>, details::_CancellationTokenState *>>(); + + task<std::pair<std::vector<_ReturnType>, details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<std::vector<_ReturnType>, details::_CancellationTokenState *> _Ret) -> std::vector<_ReturnType> { + _ASSERTE(_Ret.second); + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + return _Ret.first; + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + _Lhs._Then([_PParam](task<std::vector<_ReturnType>> _ResultTask) { + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, details::_CancellationTokenState::_None()); + + + _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) + { + // Dev10 compiler bug + typedef _ReturnType _ReturnTypeDev10; + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + + std::vector<_ReturnTypeDev10> _Vec; + _Vec.push_back(_Result); + _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +/// <summary> +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task +/// will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c> +/// and the other one is of type <c>task<T>.</c></para> +/// </returns> +/// <remarks> +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +template<typename _ReturnType> +task<std::vector<_ReturnType>> operator||(const task<_ReturnType> & _Lhs, const task<std::vector<_ReturnType>> & _Rhs) +{ + return _Rhs || _Lhs; +} + +/// <summary> +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// </summary> +/// <typeparam name="_ReturnType"> +/// The type of the returned task. +/// </typeparam> +/// <param name="_Lhs"> +/// The first task to combine into the resulting task. +/// </param> +/// <param name="_Rhs"> +/// The second task to combine into the resulting task. +/// </param> +/// <returns> +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>, +/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task +/// will also be a <c>task<void></c>. +/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c> +/// and the other one is of type <c>task<T>.</c></para> +/// </returns> +/// <remarks> +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on that task. +/// </remarks> +/// <seealso cref="Task Parallelism (Concurrency Runtime)"/> +/**/ +inline task<void> operator||(const task<void> & _Lhs, const task<void> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam<std::pair<details::_Unit_type, details::_CancellationTokenState *>>(); + + task<std::pair<details::_Unit_type, details::_CancellationTokenState *>> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_task_completed._Then([=](std::pair<details::_Unit_type, details::_CancellationTokenState *> _Ret) { + _ASSERTE(_Ret.second); + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](task<void> _ResultTask) mutable { + // Dev10 compiler needs this. + auto _PParam1 = _PParam; + auto _Func = [&_ResultTask, _PParam1]() { + _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }; + + _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +template<typename _Ty> +task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_Ty> _Tce; + _Tce.set(_Param); + return create_task(_Tce, _TaskOptions); +} + +// Work around VS 2010 compiler bug +#if _MSC_VER == 1600 +inline task<bool> task_from_result(bool _Param) +{ + task_completion_event<bool> _Tce; + _Tce.set(_Param); + return create_task(_Tce, task_options()); +} +#endif +inline task<void> task_from_result(const task_options& _TaskOptions = task_options()) +{ + task_completion_event<void> _Tce; + _Tce.set(); + return create_task(_Tce, _TaskOptions); +} + +template<typename _TaskType, typename _ExType> +task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_TaskType> _Tce; + _Tce.set_exception(_Exception); + return create_task(_Tce, _TaskOptions); +} + +namespace details +{ + /// <summary> + /// A convenient extension to Concurrency: loop until a condition is no longer met + /// </summary> + /// <param name="func"> + /// A function representing the body of the loop. It will be invoked at least once and + /// then repetitively as long as it returns true. + /// </param> + inline + task<bool> do_while(std::function<task<bool>(void)> func) + { + task<bool> first = func(); + return first.then([=](bool guard) -> task<bool> { + if (guard) + return do_while(func); + else + return first; + }); + } + +} // namespace details + +} // namespace Concurrency + +#pragma pop_macro("new") +#pragma warning(pop) +#pragma pack(pop) + +#endif // _PPLTASKS_H diff --git a/test_data/lots_of_files/prime_10001st.cpp b/test_data/lots_of_files/prime_10001st.cpp new file mode 100644 index 0000000..ce557ca --- /dev/null +++ b/test_data/lots_of_files/prime_10001st.cpp @@ -0,0 +1,155 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: + +struct Seive_Array{ + bool *is_composite; + int size; +}; + +struct Primes{ + int *primes; + int count, max; +}; + +void extend_sieve(Primes ps, Seive_Array seive, int start){ + int q,p,j,n,m; + for (q = 1; q < ps.count; ++q){ + p = ps.primes[q]; + j = start; + n = 2*j + 3; + m = (n/p)*p; + if (n != m){ + n = m+p; + if (n % 2 == 0){ + n += p; + } + j = (n-3)/2; + } + + for (; j < seive.size; j += p){ + seive.is_composite[j] = 1; + } + } +} + +int seive_partial(Primes *ps_, Seive_Array seive, int start){ + Primes ps = *ps_; + int i=start,n,j; + for (; i < seive.size; ++i){ + if (!seive.is_composite[i]){ + n = i*2 + 3; + ps.primes[ps.count++] = n; + if (ps.count == ps.max){ + break; + } + for (j = i + n; j < seive.size; j += n){ + seive.is_composite[j] = 1; + } + } + } + ps_->count = ps.count; + return i; +} + +// TODO(allen): This is for an incrementally extended seive +struct Seive{ + Seive_Array seive; + Primes ps; + int i; + int w; +}; + +// size of primes >= which_prime +// size of seive >= size > 0 +Seive begin_seive(int *primes, int which_prime, bool *seive, int size){ + Seive result; + + result.seive.size = size; + result.seive.is_composite = seive; + + result.ps.primes = primes; + result.ps.count = 1; + result.ps.max = which_prime; + primes[0] = 2; + + result.i = 0; + result.w = which_prime; + + return result; +} + +inline bool seive_step(Seive *s){ + s->i = seive_partial(&s->ps, s->seive, s->i); + return (s->ps.count != s->w); +} + +inline void seive_grow(Seive *s, Seive_Array new_seive){ + extend_seive(s->ps, new_seive, s->seive.size); + s->seive = new_seive; +} + +inline int seive_init_size(int which_prime){ + return which_prime*5; +} + +inline int seive_new_size(Seive s){ + return s.seive.size + s.w * (1 + ((s.w - s.ps.count) / 1000)); +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{}; + +struct Euler_Result{ + int prime; +}; + +static const int WHICH_PRIME = 10001; + +Euler_Result euler_main(Euler_In in){ + char *chunk = (char*)malloc(1024*1024); + + int primes_[WHICH_PRIME]; + int size_ = seive_init_size(WHICH_PRIME); + bool *seive_ = (bool*)chunk; + memset(seive_, 0, size_); + + Seive s; + s = begin_seive(primes_, WHICH_PRIME, seive_, size_); + + while (seive_step(&s)){ + Seive_Array new_seive; + new_seive.size = seive_new_size(s); + new_seive.is_composite = (bool*)chunk; + memset(chunk + s.seive.size, 0, new_seive.size - s.seive.size); + + // free s.seive.is_composite, do not touch s.seive.size + seive_grow(&s, new_seive); + } + + free(chunk); + + Euler_Result result; + result.prime = s.ps.primes[WHICH_PRIME - 1]; + + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.prime); +} + +#if 0 +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result = 1; + + return result; +} +#endif + +#endif diff --git a/test_data/lots_of_files/process.h b/test_data/lots_of_files/process.h new file mode 100644 index 0000000..84ba449 --- /dev/null +++ b/test_data/lots_of_files/process.h @@ -0,0 +1,233 @@ +/*** +*process.h - definition and declarations for process control functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines the modeflag values for spawnxx calls. +* Also contains the function argument declarations for all +* process control related routines. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_PROCESS +#define _INC_PROCESS + +#include <crtdefs.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* modeflag values for _spawnxx routines */ + +#define _P_WAIT 0 +#define _P_NOWAIT 1 +#define _OLD_P_OVERLAY 2 +#define _P_NOWAITO 3 +#define _P_DETACH 4 + +#define _P_OVERLAY 2 + +/* Action codes for _cwait(). The action code argument to _cwait is ignored + on Win32 though it is accepted for compatibilty with old MS CRT libs */ +#define _WAIT_CHILD 0 +#define _WAIT_GRANDCHILD 1 + + +/* function prototypes */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRTIMP uintptr_t __cdecl _beginthread (_In_ void (__cdecl * _StartAddress) (void *), + _In_ unsigned _StackSize, _In_opt_ void * _ArgList); +_CRTIMP void __cdecl _endthread(void); +_CRTIMP uintptr_t __cdecl _beginthreadex(_In_opt_ void * _Security, _In_ unsigned _StackSize, + _In_ unsigned (__stdcall * _StartAddress) (void *), _In_opt_ void * _ArgList, + _In_ unsigned _InitFlag, _Out_opt_ unsigned * _ThrdAddr); +_CRTIMP void __cdecl _endthreadex(_In_ unsigned _Retval); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifndef _CRT_TERMINATE_DEFINED +#define _CRT_TERMINATE_DEFINED +#pragma push_macro("exit") +#undef exit +_CRTIMP __declspec(noreturn) void __cdecl exit(_In_ int _Code); +#pragma pop_macro("exit") +_CRTIMP __declspec(noreturn) void __cdecl _exit(_In_ int _Code); +_CRTIMP __declspec(noreturn) void __cdecl abort(void); +#endif /* _CRT_TERMINATE_DEFINED */ + +_CRTIMP void __cdecl _cexit(void); +_CRTIMP void __cdecl _c_exit(void); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRTIMP int __cdecl _getpid(void); +_CRTIMP intptr_t __cdecl _cwait(_Out_opt_ int * _TermStat, _In_ intptr_t _ProcHandle, _In_ int _Action); +_CRTIMP intptr_t __cdecl _execl(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _execle(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _execlp(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _execlpe(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _execv(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRTIMP intptr_t __cdecl _execve(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, _In_opt_z_ const char * const * _Env); +_CRTIMP intptr_t __cdecl _execvp(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRTIMP intptr_t __cdecl _execvpe(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, _In_opt_z_ const char * const * _Env); +_CRTIMP intptr_t __cdecl _spawnl(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _spawnle(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _spawnlp(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _spawnlpe(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRTIMP intptr_t __cdecl _spawnv(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRTIMP intptr_t __cdecl _spawnve(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, + _In_opt_z_ const char * const * _Env); +_CRTIMP intptr_t __cdecl _spawnvp(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRTIMP intptr_t __cdecl _spawnvpe(_In_ int _Mode, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, + _In_opt_z_ const char * const * _Env); + +#ifndef _CRT_SYSTEM_DEFINED +#define _CRT_SYSTEM_DEFINED +_CRTIMP int __cdecl system(_In_opt_z_ const char * _Command); +#endif /* _CRT_SYSTEM_DEFINED */ + +#ifndef _WPROCESS_DEFINED +/* wide function prototypes, also declared in wchar.h */ +_CRTIMP intptr_t __cdecl _wexecl(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexecle(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexeclp(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexeclpe(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexecv(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wexecve(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +_CRTIMP intptr_t __cdecl _wexecvp(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wexecvpe(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +_CRTIMP intptr_t __cdecl _wspawnl(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnle(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnlp(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnlpe(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnv(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wspawnve(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +_CRTIMP intptr_t __cdecl _wspawnvp(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wspawnvpe(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +#ifndef _CRT_WSYSTEM_DEFINED +#define _CRT_WSYSTEM_DEFINED +_CRTIMP int __cdecl _wsystem(_In_opt_z_ const wchar_t * _Command); +#endif /* _CRT_WSYSTEM_DEFINED */ + +#define _WPROCESS_DEFINED +#endif /* _WPROCESS_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/* + * Security check initialization and failure reporting used by /GS security + * checks. + */ +#if !defined (_M_CEE) +void __cdecl __security_init_cookie(void); +#ifdef _M_IX86 +void __fastcall __security_check_cookie(_In_ uintptr_t _StackCookie); +__declspec(noreturn) void __cdecl __report_gsfailure(void); +#else /* _M_IX86 */ +void __cdecl __security_check_cookie(_In_ uintptr_t _StackCookie); +__declspec(noreturn) void __cdecl __report_gsfailure(_In_ uintptr_t _StackCookie); +#endif /* _M_IX86 */ +#endif /* !defined (_M_CEE) */ +extern uintptr_t __security_cookie; + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/* --------- The following functions are OBSOLETE --------- */ +/* + * The Win32 API LoadLibrary, FreeLibrary and GetProcAddress should be used + * instead. + */ + +_CRT_OBSOLETE(LoadLibrary) intptr_t __cdecl _loaddll(_In_z_ char * _Filename); +_CRT_OBSOLETE(FreeLibrary) int __cdecl _unloaddll(_In_ intptr_t _Handle); +_CRT_OBSOLETE(GetProcAddress) int (__cdecl * __cdecl _getdllprocaddr(_In_ intptr_t _Handle, _In_opt_z_ char * _ProcedureName, _In_ intptr_t _Ordinal))(void); + +/* --------- The preceding functions are OBSOLETE --------- */ +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + +#ifdef _DECL_DLLMAIN +/* + * Declare DLL notification (initialization/termination) routines + * The preferred method is for the user to provide DllMain() which will + * be called automatically by the DLL entry point defined by the C run- + * time library code. If the user wants to define the DLL entry point + * routine, the user's entry point must call _CRT_INIT on all types of + * notifications, as the very first thing on attach notifications and + * as the very last thing on detach notifications. + */ +#ifdef _WINDOWS_ +#if defined (MRTDLL) && defined(_CRTBLD) +BOOL __clrcall DllMain(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved); +BOOL _CRT_INIT(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved); +#else /* defined (MRTDLL) */ +BOOL WINAPI DllMain(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved); +BOOL WINAPI _CRT_INIT(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved); +#endif /* defined (MRTDLL) */ +BOOL WINAPI _wCRT_INIT(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved); +extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID); +#else /* _WINDOWS_ */ +int __stdcall DllMain(_In_ void * _HDllHandle, _In_ unsigned _Reason, _In_opt_ void * _Reserved); +int __stdcall _CRT_INIT(_In_ void * _HDllHandle, _In_ unsigned _Reason, _In_opt_ void * _Reserved); +int __stdcall _wCRT_INIT(_In_ void * _HDllHandle, _In_ unsigned _Reason, _In_opt_ void * _Reserved); +extern int (__stdcall * const _pRawDllMain)(void *, unsigned, void *); +#endif /* _WINDOWS_ */ +#endif /* _DECL_DLLMAIN */ + +#if !__STDC__ + +/* Non-ANSI names for compatibility */ + +#define P_WAIT _P_WAIT +#define P_NOWAIT _P_NOWAIT +#define P_OVERLAY _P_OVERLAY +#define OLD_P_OVERLAY _OLD_P_OVERLAY +#define P_NOWAITO _P_NOWAITO +#define P_DETACH _P_DETACH +#define WAIT_CHILD _WAIT_CHILD +#define WAIT_GRANDCHILD _WAIT_GRANDCHILD + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +/* current declarations */ +_CRT_NONSTDC_DEPRECATE(_cwait) _CRTIMP intptr_t __cdecl cwait(_Out_opt_ int * _TermStat, _In_ intptr_t _ProcHandle, _In_ int _Action); +_CRT_NONSTDC_DEPRECATE(_execl) _CRTIMP intptr_t __cdecl execl(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_execle) _CRTIMP intptr_t __cdecl execle(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_execlp) _CRTIMP intptr_t __cdecl execlp(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_execlpe) _CRTIMP intptr_t __cdecl execlpe(_In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_execv) _CRTIMP intptr_t __cdecl execv(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRT_NONSTDC_DEPRECATE(_execve) _CRTIMP intptr_t __cdecl execve(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, _In_opt_z_ const char * const * _Env); +_CRT_NONSTDC_DEPRECATE(_execvp) _CRTIMP intptr_t __cdecl execvp(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRT_NONSTDC_DEPRECATE(_execvpe) _CRTIMP intptr_t __cdecl execvpe(_In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, _In_opt_z_ const char * const * _Env); +_CRT_NONSTDC_DEPRECATE(_spawnl) _CRTIMP intptr_t __cdecl spawnl(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_spawnle) _CRTIMP intptr_t __cdecl spawnle(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_spawnlp) _CRTIMP intptr_t __cdecl spawnlp(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_spawnlpe) _CRTIMP intptr_t __cdecl spawnlpe(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * _ArgList, ...); +_CRT_NONSTDC_DEPRECATE(_spawnv) _CRTIMP intptr_t __cdecl spawnv(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRT_NONSTDC_DEPRECATE(_spawnve) _CRTIMP intptr_t __cdecl spawnve(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, + _In_opt_z_ const char * const * _Env); +_CRT_NONSTDC_DEPRECATE(_spawnvp) _CRTIMP intptr_t __cdecl spawnvp(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList); +_CRT_NONSTDC_DEPRECATE(_spawnvpe) _CRTIMP intptr_t __cdecl spawnvpe(_In_ int, _In_z_ const char * _Filename, _In_z_ const char * const * _ArgList, + _In_opt_z_ const char * const * _Env); + +_CRT_NONSTDC_DEPRECATE(_getpid) _CRTIMP int __cdecl getpid(void); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* _INC_PROCESS */ diff --git a/test_data/lots_of_files/raster.c b/test_data/lots_of_files/raster.c new file mode 100644 index 0000000..5b21dcb --- /dev/null +++ b/test_data/lots_of_files/raster.c @@ -0,0 +1,27 @@ +/***************************************************************************/ +/* */ +/* raster.c */ +/* */ +/* FreeType monochrome rasterer module component (body only). */ +/* */ +/* Copyright 1996-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include <ft2build.h> +#include "rastpic.c" +#include "ftraster.c" +#include "ftrend1.c" + + +/* END */ diff --git a/test_data/lots_of_files/rasterrs.h b/test_data/lots_of_files/rasterrs.h new file mode 100644 index 0000000..44da7fc --- /dev/null +++ b/test_data/lots_of_files/rasterrs.h @@ -0,0 +1,42 @@ +/***************************************************************************/ +/* */ +/* rasterrs.h */ +/* */ +/* monochrome renderer error codes (specification only). */ +/* */ +/* Copyright 2001-2016 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the monochrome renderer error enumeration */ + /* constants. */ + /* */ + /*************************************************************************/ + +#ifndef RASTERRS_H_ +#define RASTERRS_H_ + +#include FT_MODULE_ERRORS_H + +#undef FTERRORS_H_ + +#undef FT_ERR_PREFIX +#define FT_ERR_PREFIX Raster_Err_ +#define FT_ERR_BASE FT_Mod_Err_Raster + +#include FT_ERRORS_H + +#endif /* RASTERRS_H_ */ + + +/* END */ diff --git a/test_data/lots_of_files/rastpic.c b/test_data/lots_of_files/rastpic.c new file mode 100644 index 0000000..dcfa92e --- /dev/null +++ b/test_data/lots_of_files/rastpic.c @@ -0,0 +1,89 @@ +/***************************************************************************/ +/* */ +/* rastpic.c */ +/* */ +/* The FreeType position independent code services for raster module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "rastpic.h" +#include "rasterrs.h" + + +#ifdef FT_CONFIG_OPTION_PIC + + /* forward declaration of PIC init functions from ftraster.c */ + void + FT_Init_Class_ft_standard_raster( FT_Raster_Funcs* funcs ); + + + void + ft_raster1_renderer_class_pic_free( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Memory memory = library->memory; + + + if ( pic_container->raster ) + { + RasterPIC* container = (RasterPIC*)pic_container->raster; + + + if ( --container->ref_count ) + return; + FT_FREE( container ); + pic_container->raster = NULL; + } + } + + + FT_Error + ft_raster1_renderer_class_pic_init( FT_Library library ) + { + FT_PIC_Container* pic_container = &library->pic_container; + FT_Error error = FT_Err_Ok; + RasterPIC* container = NULL; + FT_Memory memory = library->memory; + + + /* XXX: since this function also served the no longer available */ + /* raster5 renderer it uses reference counting, which could */ + /* be removed now */ + if ( pic_container->raster ) + { + ((RasterPIC*)pic_container->raster)->ref_count++; + return error; + } + + /* allocate pointer, clear and set global container pointer */ + if ( FT_ALLOC( container, sizeof ( *container ) ) ) + return error; + FT_MEM_SET( container, 0, sizeof ( *container ) ); + pic_container->raster = container; + + container->ref_count = 1; + + /* initialize pointer table - */ + /* this is how the module usually expects this data */ + FT_Init_Class_ft_standard_raster( &container->ft_standard_raster ); + + return error; + } + +#endif /* FT_CONFIG_OPTION_PIC */ + + +/* END */ diff --git a/test_data/lots_of_files/rastpic.h b/test_data/lots_of_files/rastpic.h new file mode 100644 index 0000000..7815876 --- /dev/null +++ b/test_data/lots_of_files/rastpic.h @@ -0,0 +1,63 @@ +/***************************************************************************/ +/* */ +/* rastpic.h */ +/* */ +/* The FreeType position independent code services for raster module. */ +/* */ +/* Copyright 2009-2016 by */ +/* Oran Agra and Mickey Gabel. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef RASTPIC_H_ +#define RASTPIC_H_ + + +#include FT_INTERNAL_PIC_H + + +FT_BEGIN_HEADER + +#ifndef FT_CONFIG_OPTION_PIC + +#define FT_STANDARD_RASTER_GET ft_standard_raster + +#else /* FT_CONFIG_OPTION_PIC */ + + typedef struct RasterPIC_ + { + int ref_count; + FT_Raster_Funcs ft_standard_raster; + + } RasterPIC; + + +#define GET_PIC( lib ) \ + ( (RasterPIC*)( (lib)->pic_container.raster ) ) +#define FT_STANDARD_RASTER_GET ( GET_PIC( library )->ft_standard_raster ) + + + /* see rastpic.c for the implementation */ + void + ft_raster1_renderer_class_pic_free( FT_Library library ); + + FT_Error + ft_raster1_renderer_class_pic_init( FT_Library library ); + +#endif /* FT_CONFIG_OPTION_PIC */ + + /* */ + +FT_END_HEADER + +#endif /* RASTPIC_H_ */ + + +/* END */ diff --git a/test_data/lots_of_files/rtcapi.h b/test_data/lots_of_files/rtcapi.h new file mode 100644 index 0000000..ca73564 --- /dev/null +++ b/test_data/lots_of_files/rtcapi.h @@ -0,0 +1,171 @@ +/*** +*rtcapi.h - declarations and definitions for RTC use +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Contains the declarations and definitions for all RunTime Check +* support. +* +****/ + +#ifndef _INC_RTCAPI +#define _INC_RTCAPI + +#if defined(_RTC) || !defined(_CRTBLD) + +#include <crtdefs.h> + +#pragma pack(push,_CRT_PACKING) + +/* +Previous versions of this header included declarations of functions used by RTC but +not intended for use by end-users. These functions are now declared deprecated by default +and may be removed in a future version. +*/ +#ifndef _CRT_ENABLE_RTC_INTERNALS +#define _RTCINTERNAL_DEPRECATED _CRT_DEPRECATE_TEXT("These internal RTC functions are obsolete and should not be used") +#else /* _CRT_ENABLE_RTC_INTERNALS */ +#define _RTCINTERNAL_DEPRECATED +#endif /* _CRT_ENABLE_RTC_INTERNALS */ + + + +#ifdef __cplusplus + +extern "C" { + +#endif /* __cplusplus */ + + /* General User API */ + +typedef enum _RTC_ErrorNumber { + _RTC_CHKSTK = 0, + _RTC_CVRT_LOSS_INFO, + _RTC_CORRUPT_STACK, + _RTC_UNINIT_LOCAL_USE, + _RTC_CORRUPTED_ALLOCA, + _RTC_ILLEGAL +} _RTC_ErrorNumber; + +# define _RTC_ERRTYPE_IGNORE -1 +# define _RTC_ERRTYPE_ASK -2 + +#ifndef _WCHAR_T_DEFINED +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif /* _WCHAR_T_DEFINED */ + + typedef int (__cdecl *_RTC_error_fn)(int, const char *, int, const char *, const char *, ...); + typedef int (__cdecl *_RTC_error_fnW)(int, const wchar_t *, int, const wchar_t *, const wchar_t *, ...); + + /* User API */ + int __cdecl _RTC_NumErrors(void); + const char * __cdecl _RTC_GetErrDesc(_RTC_ErrorNumber _Errnum); + int __cdecl _RTC_SetErrorType(_RTC_ErrorNumber _Errnum, int _ErrType); + _RTC_error_fn __cdecl _RTC_SetErrorFunc(_RTC_error_fn); + _RTC_error_fnW __cdecl _RTC_SetErrorFuncW(_RTC_error_fnW); + + /* Power User/library API */ + + + /* Init functions */ + + /* These functions all call _CRT_RTC_INIT */ + void __cdecl _RTC_Initialize(void); + void __cdecl _RTC_Terminate(void); + + /* + * If you're not using the CRT, you have to implement _CRT_RTC_INIT + * Just return either null, or your error reporting function + * *** Don't mess with res0/res1/res2/res3/res4 - YOU'VE BEEN WARNED! *** + */ + _RTC_error_fn __cdecl _CRT_RTC_INIT(void *_Res0, void **_Res1, int _Res2, int _Res3, int _Res4); + _RTC_error_fnW __cdecl _CRT_RTC_INITW(void *_Res0, void **_Res1, int _Res2, int _Res3, int _Res4); + + /* Compiler generated calls (unlikely to be used, even by power users) */ + /* Types */ + typedef struct _RTC_vardesc { + int addr; + int size; + char *name; + } _RTC_vardesc; + + typedef struct _RTC_framedesc { + int varCount; + _RTC_vardesc *variables; + } _RTC_framedesc; + + /* NOTE: + Changing this structure requires a matching compiler backend + update, because the offsets are hardcoded inside there. + */ +#pragma pack(push, 1) + /* Structure padded under 32-bit x86, to get consistent + execution between 32/64 targets. + */ + typedef struct _RTC_ALLOCA_NODE { + __int32 guard1; + struct _RTC_ALLOCA_NODE *next; +#if defined (_M_IX86) || defined (_M_ARM) + __int32 dummypad; +#endif /* defined (_M_IX86) || defined (_M_ARM) */ + size_t allocaSize; +#if defined (_M_IX86) || defined (_M_ARM) + __int32 dummypad2; +#endif /* defined (_M_IX86) || defined (_M_ARM) */ + __int32 guard2[3]; + } _RTC_ALLOCA_NODE; +#pragma pack(pop) + +#if !defined (_M_CEE) && !defined (_M_CEE_PURE) + /* These unsupported functions are deprecated in native mode and not supported at all in /clr mode */ + + /* Shortening convert checks - name indicates src bytes to target bytes */ + /* Signedness is NOT checked */ + _RTCINTERNAL_DEPRECATED char __fastcall _RTC_Check_2_to_1(short _Src); + _RTCINTERNAL_DEPRECATED char __fastcall _RTC_Check_4_to_1(int _Src); + _RTCINTERNAL_DEPRECATED char __fastcall _RTC_Check_8_to_1(__int64 _Src); + _RTCINTERNAL_DEPRECATED short __fastcall _RTC_Check_4_to_2(int _Src); + _RTCINTERNAL_DEPRECATED short __fastcall _RTC_Check_8_to_2(__int64 _Src); + _RTCINTERNAL_DEPRECATED int __fastcall _RTC_Check_8_to_4(__int64 _Src); +#endif /* !defined (_M_CEE) && !defined (_M_CEE_PURE) */ + + + /* Stack Checking Calls */ +#if defined (_M_IX86) + void __cdecl _RTC_CheckEsp(); +#endif /* defined (_M_IX86) */ + +#if !defined (_M_CEE) && !defined (_M_CEE_PURE) + /* These unsupported functions are deprecated in native mode and not supported at all in /clr mode */ + + _RTCINTERNAL_DEPRECATED void __fastcall _RTC_CheckStackVars(void *_Esp, _RTC_framedesc *_Fd); + _RTCINTERNAL_DEPRECATED void __fastcall _RTC_CheckStackVars2(void *_Esp, _RTC_framedesc *_Fd, _RTC_ALLOCA_NODE *_AllocaList); + _RTCINTERNAL_DEPRECATED void __fastcall _RTC_AllocaHelper(_RTC_ALLOCA_NODE *_PAllocaBase, size_t _CbSize, _RTC_ALLOCA_NODE **_PAllocaInfoList); +#endif /* !defined (_M_CEE) && !defined (_M_CEE_PURE) */ + /* Unintialized Local call */ + void __cdecl _RTC_UninitUse(const char *_Varname); + +#if !defined (_M_CEE) && !defined (_M_CEE_PURE) + /* These unsupported functions are deprecated in native mode and not supported at all in /clr mode */ + +#endif /* !defined (_M_CEE) && !defined (_M_CEE_PURE) */ + + /* Subsystem initialization stuff */ + void __cdecl _RTC_Shutdown(void); + void __cdecl _RTC_InitBase(void); + + +#ifdef __cplusplus + + void* _ReturnAddress(); +} + +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* defined(_RTC) || !defined(_CRTBLD) */ + +#endif /* _INC_RTCAPI */ diff --git a/test_data/lots_of_files/safeint.h b/test_data/lots_of_files/safeint.h new file mode 100644 index 0000000..5804b88 --- /dev/null +++ b/test_data/lots_of_files/safeint.h @@ -0,0 +1,1653 @@ +/*** +*safeint.h - SafeInt class and free-standing functions used to prevent arithmetic overflows +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* +* The SafeInt class is designed to have as low an overhead as possible +* while still ensuring that all integer operations are conducted safely. +* Nearly every operator has been overloaded, with a very few exceptions. +* +* A usability-safety trade-off has been made to help ensure safety. This +* requires that every operation return either a SafeInt or a bool. If we +* allowed an operator to return a base integer type T, then the following +* can happen: +* +* char i = SafeInt<char>(32) * 2 + SafeInt<char>(16) * 4; +* +* The * operators take precedence, get overloaded, return a char, and then +* you have: +* +* char i = (char)64 + (char)64; //overflow! +* +* This situation would mean that safety would depend on usage, which isn't +* acceptable. +* +* One key operator that is missing is an implicit cast to type T. The reason for +* this is that if there is an implicit cast operator, then we end up with +* an ambiguous compile-time precedence. Because of this amiguity, there +* are two methods that are provided: +* +* Casting operators for every native integer type +* +* SafeInt::Ptr() - returns the address of the internal integer +* +* The SafeInt class should be used in any circumstances where ensuring +* integrity of the calculations is more important than performance. See Performance +* Notes below for additional information. +* +* Many of the conditionals will optimize out or be inlined for a release +* build (especially with /Ox), but it does have significantly more overhead, +* especially for signed numbers. If you do not _require_ negative numbers, use +* unsigned integer types - certain types of problems cannot occur, and this class +* performs most efficiently. +* +* Here's an example of when the class should ideally be used - +* +* void* AllocateMemForStructs(int StructSize, int HowMany) +* { +* SafeInt<unsigned long> s(StructSize); +* +* s *= HowMany; +* +* return malloc(s); +* +* } +* +* Here's when it should NOT be used: +* +* void foo() +* { +* int i; +* +* for(i = 0; i < 0xffff; i++) +* .... +* } +* +* Error handling - a SafeInt class will throw exceptions if something +* objectionable happens. The exceptions are SafeIntException classes, +* which contain an enum as a code. +* +* Typical usage might be: +* +* bool foo() +* { +* SafeInt<unsigned long> s; //note that s == 0 unless set +* +* try{ +* s *= 23; +* .... +* } +* catch(SafeIntException err) +* { +* //handle errors here +* } +* } +* +* SafeInt accepts an error policy as an optional template parameter. +* We provide two error policy along with SafeInt: SafeIntErrorPolicy_SafeIntException, which +* throws SafeIntException in case of error, and SafeIntErrorPolicy_InvalidParameter, which +* calls _invalid_parameter to terminate the program. +* +* You can replace the error policy class with any class you like. This is accomplished by: +* 1) Create a class that has the following interface: +* +* struct YourSafeIntErrorPolicy +* { +* static __declspec(noreturn) void __stdcall SafeIntOnOverflow() +* { +* throw YourException( YourSafeIntArithmeticOverflowError ); +* // or do something else which will terminate the program +* } +* +* static __declspec(noreturn) void __stdcall SafeIntOnDivZero() +* { +* throw YourException( YourSafeIntDivideByZeroError ); +* // or do something else which will terminate the program +* } +* }; +* +* Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do +* anything you like, just don't return from the call back into the code. +* +* 2) Either explicitly declare SafeInts like so: +* SafeInt< int, YourSafeIntErrorPolicy > si; +* or, before including SafeInt: +* #define _SAFEINT_DEFAULT_ERROR_POLICY ::YourSafeIntErrorPolicy +* +* Performance: +* +* Due to the highly nested nature of this class, you can expect relatively poor +* performance in unoptimized code. In tests of optimized code vs. correct inline checks +* in native code, this class has been found to take approximately 8% more CPU time (this varies), +* most of which is due to exception handling. +* +* Binary Operators: +* +* All of the binary operators have certain assumptions built into the class design. +* This is to ensure correctness. Notes on each class of operator follow: +* +* Arithmetic Operators (*,/,+,-,%) +* There are three possible variants: +* SafeInt< T, E > op SafeInt< T, E > +* SafeInt< T, E > op U +* U op SafeInt< T, E > +* +* The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do +* this the compiler with throw the following error: +* +* error C2593: 'operator *' is ambiguous +* +* This is because the arithmetic operators are required to return a SafeInt of some type. +* The compiler cannot know whether you'd prefer to get a type T or a type U returned. If +* you need to do this, you need to extract the value contained within one of the two using +* the casting operator. For example: +* +* SafeInt< T, E > t, result; +* SafeInt< U, E > u; +* +* result = t * (U)u; +* +* Comparison Operators: +* +* Because each of these operators return type bool, mixing SafeInts of differing types is +* allowed. +* +* Shift Operators: +* +* Shift operators always return the type on the left hand side of the operator. Mixed type +* operations are allowed because the return type is always known. +* +* Boolean Operators: +* +* Like comparison operators, these overloads always return type bool, and mixed-type SafeInts +* are allowed. Additionally, specific overloads exist for type bool on both sides of the +* operator. +* +* Binary Operators: +* +* Mixed-type operations are discouraged, however some provision has been made in order to +* enable things like: +* +* SafeInt<char> c = 2; +* +* if(c & 0x02) +* ... +* +* The "0x02" is actually an int, and it needs to work. +* In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner +* cases do exist where you could get unexpected results. In any case where SafeInt returns a different +* result than the underlying operator, it will call _ASSERTE(). You should examine your code and cast things +* properly so that you are not programming with side effects. +* +* Comparison Operators and ANSI Conversions: +* +* The comparison operator behavior in this class varies from the ANSI definition. +* As an example, consider the following: +* +* unsigned int l = 0xffffffff; +* char c = -1; +* +* if(c == l) +* printf("Why is -1 equal to 4 billion???\n"); +* +* The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets +* cast again to an unsigned int, losing the true value. This behavior is despite the fact that +* an __int64 exists, and the following code will yield a different (and intuitively correct) +* answer: +* +* if((__int64)c == (__int64)l)) +* printf("Why is -1 equal to 4 billion???\n"); +* else +* printf("Why doesn't the compiler upcast to 64-bits when needed?\n"); +* +* Note that combinations with smaller integers won't display the problem - if you +* changed "unsigned int" above to "unsigned short", you'd get the right answer. +* +* If you prefer to retain the ANSI standard behavior insert, before including safeint.h: +* +* #define _SAFEINT_ANSI_CONVERSIONS 1 +* +* into your source. Behavior differences occur in the following cases: +* 8, 16, and 32-bit signed int, unsigned 32-bit int +* any signed int, unsigned 64-bit int +* Note - the signed int must be negative to show the problem +* +****/ + +#pragma once + +#include <crtdefs.h> +#include <crtdbg.h> + +#if !defined (_SAFEINT_DEFAULT_ERROR_POLICY) +#define _SAFEINT_DEFAULT_ERROR_POLICY SafeIntErrorPolicy_SafeIntException +#endif /* !defined (_SAFEINT_DEFAULT_ERROR_POLICY) */ + +#if !defined (_SAFEINT_SHIFT_ASSERT) +#define _SAFEINT_SHIFT_ASSERT(x) _ASSERTE(x) +#endif /* !defined (_SAFEINT_SHIFT_ASSERT) */ + +#if !defined (_SAFEINT_BINARY_ASSERT) +#define _SAFEINT_BINARY_ASSERT(x) _ASSERTE(x) +#endif /* !defined (_SAFEINT_BINARY_ASSERT) */ + +#if !defined (_SAFEINT_EXCEPTION_ASSERT) +#define _SAFEINT_EXCEPTION_ASSERT() +#endif /* !defined (_SAFEINT_EXCEPTION_ASSERT) */ + +// by default, SafeInt will accept negation of an unsigned int; +// if you wish to disable it or assert, you can define the following +// macro to be a static assert or a runtime assert +#if !defined (_SAFEINT_UNSIGNED_NEGATION_BEHAVIOR) +#define _SAFEINT_UNSIGNED_NEGATION_BEHAVIOR() +#endif /* !defined (_SAFEINT_UNSIGNED_NEGATION_BEHAVIOR) */ + +// See above "Comparison Operators and ANSI Conversions" for an explanation +// of _SAFEINT_USE_ANSI_CONVERSIONS +#if !defined (_SAFEINT_USE_ANSI_CONVERSIONS) +#define _SAFEINT_USE_ANSI_CONVERSIONS 0 +#endif /* !defined (_SAFEINT_USE_ANSI_CONVERSIONS) */ + +#pragma pack(push, _CRT_PACKING) + +namespace msl +{ + +namespace utilities +{ + +enum SafeIntError +{ + SafeIntNoError = 0, + SafeIntArithmeticOverflow, + SafeIntDivideByZero +}; + +} // namespace utilities + +} // namespace msl + +#include "safeint_internal.h" + +namespace msl +{ + +namespace utilities +{ + +class SafeIntException +{ +public: + SafeIntException() { m_code = SafeIntNoError; } + SafeIntException( SafeIntError code ) + { + m_code = code; + } + SafeIntError m_code; +}; + +struct SafeIntErrorPolicy_SafeIntException +{ + static __declspec(noreturn) void SafeIntOnOverflow() + { + _SAFEINT_EXCEPTION_ASSERT(); + throw SafeIntException( SafeIntArithmeticOverflow ); + } + + static __declspec(noreturn) void SafeIntOnDivZero() + { + _SAFEINT_EXCEPTION_ASSERT(); + throw SafeIntException( SafeIntDivideByZero ); + } +}; + +struct SafeIntErrorPolicy_InvalidParameter +{ + static __declspec(noreturn) void SafeIntOnOverflow() + { + _SAFEINT_EXCEPTION_ASSERT(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow"); + } + + static __declspec(noreturn) void SafeIntOnDivZero() + { + _SAFEINT_EXCEPTION_ASSERT(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); + } +}; + +// Free-standing functions that can be used where you only need to check one operation +// non-class helper function so that you can check for a cast's validity +// and handle errors how you like + +template < typename T, typename U > +inline bool SafeCast( const T From, U& To ) throw() +{ + return (details::SafeCastHelper< U, T, + details::SafeIntErrorPolicy_NoThrow >::Cast( From, To ) == SafeIntNoError); +} + +template < typename T, typename U > +inline bool SafeEquals( const T t, const U u ) throw() +{ + return details::EqualityTest< T, U >::IsEquals( t, u ); +} + +template < typename T, typename U > +inline bool SafeNotEquals( const T t, const U u ) throw() +{ + return !details::EqualityTest< T, U >::IsEquals( t, u ); +} + +template < typename T, typename U > +inline bool SafeGreaterThan( const T t, const U u ) throw() +{ + return details::GreaterThanTest< T, U >::GreaterThan( t, u ); +} + +template < typename T, typename U > +inline bool SafeGreaterThanEquals( const T t, const U u ) throw() +{ + return !details::GreaterThanTest< U, T >::GreaterThan( u, t ); +} + +template < typename T, typename U > +inline bool SafeLessThan( const T t, const U u ) throw() +{ + return details::GreaterThanTest< U, T >::GreaterThan( u, t ); +} + +template < typename T, typename U > +inline bool SafeLessThanEquals( const T t, const U u ) throw() +{ + return !details::GreaterThanTest< T, U >::GreaterThan( t, u ); +} + +template < typename T, typename U > +inline bool SafeModulus( const T& t, const U& u, T& result ) throw() +{ + return ( details::ModulusHelper< T, U, details::SafeIntErrorPolicy_NoThrow >::Modulus( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeMultiply( T t, U u, T& result ) throw() +{ + return ( details::MultiplicationHelper< T, U, + details::SafeIntErrorPolicy_NoThrow >::Multiply( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeDivide( T t, U u, T& result ) throw() +{ + return ( details::DivisionHelper< T, U, + details::SafeIntErrorPolicy_NoThrow >::Divide( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeAdd( T t, U u, T& result ) throw() +{ + return ( details::AdditionHelper< T, U, + details::SafeIntErrorPolicy_NoThrow >::Addition( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeSubtract( T t, U u, T& result ) throw() +{ + return ( details::SubtractionHelper< T, U, + details::SafeIntErrorPolicy_NoThrow >::Subtract( t, u, result ) == SafeIntNoError ); +} + +// SafeInt class +template < typename T, typename E = _SAFEINT_DEFAULT_ERROR_POLICY > +class SafeInt +{ +public: + SafeInt() throw() + { + static_assert( details::NumericType< T >::isInt , "SafeInt<T>: T needs to be an integer type" ); + m_int = 0; + } + + // Having a constructor for every type of int + // avoids having the compiler evade our checks when doing implicit casts - + // e.g., SafeInt<char> s = 0x7fffffff; + SafeInt( const T& i ) throw() + { + static_assert( details::NumericType< T >::isInt , "SafeInt<T>: T needs to be an integer type" ); + //always safe + m_int = i; + } + + // provide explicit boolean converter + SafeInt( bool b ) throw() + { + static_assert( details::NumericType< T >::isInt , "SafeInt<T>: T needs to be an integer type" ); + m_int = b ? 1 : 0; + } + + template < typename U > + SafeInt(const SafeInt< U, E >& u) + { + static_assert( details::NumericType< T >::isInt , "SafeInt<T>: T needs to be an integer type" ); + *this = SafeInt< T, E >( (U)u ); + } + + template < typename U > + SafeInt( const U& i ) + { + static_assert( details::NumericType< T >::isInt , "SafeInt<T>: T needs to be an integer type" ); + // SafeCast will throw exceptions if i won't fit in type T + details::SafeCastHelper< T, U, E >::Cast( i, m_int ); + } + + // now start overloading operators + // assignment operator + // constructors exist for all int types and will ensure safety + + template < typename U > + SafeInt< T, E >& operator =( const U& rhs ) + { + // use constructor to test size + // constructor is optimized to do minimal checking based + // on whether T can contain U + // note - do not change this + *this = SafeInt< T, E >( rhs ); + return *this; + } + + SafeInt< T, E >& operator =( const T& rhs ) throw() + { + m_int = rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator =( const SafeInt< U, E >& rhs ) + { + details::SafeCastHelper< T, U, E >::Cast( rhs.Ref(), m_int ); + return *this; + } + + SafeInt< T, E >& operator =( const SafeInt< T, E >& rhs ) throw() + { + m_int = rhs.m_int; + return *this; + } + + // Casting operators + + operator bool() const throw() + { + return !!m_int; + } + + operator char() const + { + char val; + details::SafeCastHelper< char, T, E >::Cast( m_int, val ); + return val; + } + + operator signed char() const + { + signed char val; + details::SafeCastHelper< signed char, T, E >::Cast( m_int, val ); + return val; + } + + operator unsigned char() const + { + unsigned char val; + details::SafeCastHelper< unsigned char, T, E >::Cast( m_int, val ); + return val; + } + + operator __int16() const + { + __int16 val; + details::SafeCastHelper< __int16, T, E >::Cast( m_int, val ); + return val; + } + + operator unsigned __int16() const + { + unsigned __int16 val; + details::SafeCastHelper< unsigned __int16, T, E >::Cast( m_int, val ); + return val; + } + + operator __int32() const + { + __int32 val; + details::SafeCastHelper< __int32, T, E >::Cast( m_int, val ); + return val; + } + + operator unsigned __int32() const + { + unsigned __int32 val; + details::SafeCastHelper< unsigned __int32, T, E >::Cast( m_int, val ); + return val; + } + + // The compiler knows that int == __int32 + // but not that long == __int32 + operator long() const + { + long val; + details::SafeCastHelper< long, T, E >::Cast( m_int, val ); + return val; + } + + operator unsigned long() const + { + unsigned long val; + details::SafeCastHelper< unsigned long, T, E >::Cast( m_int, val ); + return val; + } + + operator __int64() const + { + __int64 val; + details::SafeCastHelper< __int64, T, E >::Cast( m_int, val ); + return val; + } + + operator unsigned __int64() const + { + unsigned __int64 val; + details::SafeCastHelper< unsigned __int64, T, E >::Cast( m_int, val ); + return val; + } + +#if _NATIVE_WCHAR_T_DEFINED + operator wchar_t() const + { + unsigned __int16 val; + details::SafeCastHelper< unsigned __int16, T, E >::Cast( m_int, val ); + return val; + } +#endif /* _NATIVE_WCHAR_T_DEFINED */ + + // If you need a pointer to the data + // this could be dangerous, but allows you to correctly pass + // instances of this class to APIs that take a pointer to an integer + // also see overloaded address-of operator below + T* Ptr() throw() { return &m_int; } + const T* Ptr() const throw() { return &m_int; } + const T& Ref() const throw() { return m_int; } + + // Unary operators + bool operator !() const throw() { return (!m_int) ? true : false; } + + // operator + (unary) + // note - normally, the '+' and '-' operators will upcast to a signed int + // for T < 32 bits. This class changes behavior to preserve type + const SafeInt< T, E >& operator +() const throw() { return *this; }; + + //unary - + SafeInt< T, E > operator -() const + { + // Note - unsigned still performs the bitwise manipulation + // will warn at level 2 or higher if the value is 32-bit or larger + T tmp; + details::NegationHelper< T, E, details::IntTraits< T >::isSigned >::Negative( m_int, tmp ); + return SafeInt< T, E >( tmp ); + } + + // prefix increment operator + SafeInt< T, E >& operator ++() + { + if( m_int != details::IntTraits< T >::maxInt ) + { + ++m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // prefix decrement operator + SafeInt< T, E >& operator --() + { + if( m_int != details::IntTraits< T >::minInt ) + { + --m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // note that postfix operators have inherently worse perf + // characteristics + + // postfix increment operator + SafeInt< T, E > operator ++( int ) // dummy arg to comply with spec + { + if( m_int != details::IntTraits< T >::maxInt ) + { + SafeInt< T, E > tmp( m_int ); + + m_int++; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // postfix decrement operator + SafeInt< T, E > operator --( int ) // dummy arg to comply with spec + { + if( m_int != details::IntTraits< T >::minInt ) + { + SafeInt< T, E > tmp( m_int ); + m_int--; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // One's complement + // Note - this operator will normally change size to an int + // cast in return improves perf and maintains type + SafeInt< T, E > operator ~() const throw() { return SafeInt< T, E >( (T)~m_int ); } + + // Binary operators + // + // arithmetic binary operators + // % modulus + // * multiplication + // / division + // + addition + // - subtraction + // + // For each of the arithmetic operators, you will need to + // use them as follows: + // + // SafeInt<char> c = 2; + // SafeInt<int> i = 3; + // + // SafeInt<int> i2 = i op (char)c; + // OR + // SafeInt<char> i2 = (int)i op c; + // + // The base problem is that if the lhs and rhs inputs are different SafeInt types + // it is not possible in this implementation to determine what type of SafeInt + // should be returned. You have to let the class know which of the two inputs + // need to be the return type by forcing the other value to the base integer type. + // + // Note - as per feedback from Scott Meyers, I'm exploring how to get around this. + // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming, + // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer + // is situational. + // + // The case of: + // + // SafeInt< T, E > i, j, k; + // i = j op k; + // + // works just fine and no unboxing is needed because the return type is not ambiguous. + + // Modulus + // Modulus has some convenient properties - + // first, the magnitude of the return can never be + // larger than the lhs operand, and it must be the same sign + // as well. It does, however, suffer from the same promotion + // problems as comparisons, division and other operations + template < typename U > + SafeInt< T, E > operator %( U rhs ) const + { + T result; + details::ModulusHelper< T, U, E >::Modulus( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + SafeInt< T, E > operator %( SafeInt< T, E > rhs ) const + { + T result; + details::ModulusHelper< T, T, E >::Modulus( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + // Modulus assignment + template < typename U > + SafeInt< T, E >& operator %=( U rhs ) + { + details::ModulusHelper< T, U, E >::Modulus( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator %=( SafeInt< U, E > rhs ) + { + details::ModulusHelper< T, U, E >::Modulus( m_int, (U)rhs, m_int ); + return *this; + } + + // Multiplication + template < typename U > + SafeInt< T, E > operator *( U rhs ) const + { + T ret( 0 ); + details::MultiplicationHelper< T, U, E >::Multiply( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator *( SafeInt< T, E > rhs ) const + { + T ret( 0 ); + details::MultiplicationHelper< T, T, E >::Multiply( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Multiplication assignment + SafeInt< T, E >& operator *=( SafeInt< T, E > rhs ) + { + details::MultiplicationHelper< T, T, E >::Multiply( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator *=( U rhs ) + { + details::MultiplicationHelper< T, U, E >::Multiply( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator *=( SafeInt< U, E > rhs ) + { + details::MultiplicationHelper< T, U, E >::Multiply( m_int, rhs.Ref(), m_int ); + return *this; + } + + // Division + template < typename U > + SafeInt< T, E > operator /( U rhs ) const + { + T ret( 0 ); + details::DivisionHelper< T, U, E >::Divide( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator /( SafeInt< T, E > rhs ) const + { + T ret( 0 ); + details::DivisionHelper< T, T, E >::Divide( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Division assignment + SafeInt< T, E >& operator /=( SafeInt< T, E > i ) + { + details::DivisionHelper< T, T, E >::Divide( m_int, (T)i, m_int ); + return *this; + } + + template < typename U > SafeInt< T, E >& operator /=( U i ) + { + details::DivisionHelper< T, U, E >::Divide( m_int, i, m_int ); + return *this; + } + + template < typename U > SafeInt< T, E >& operator /=( SafeInt< U, E > i ) + { + details::DivisionHelper< T, U, E >::Divide( m_int, (U)i, m_int ); + return *this; + } + + // For addition and subtraction + + // Addition + SafeInt< T, E > operator +( SafeInt< T, E > rhs ) const + { + T ret( 0 ); + details::AdditionHelper< T, T, E >::Addition( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + template < typename U > + SafeInt< T, E > operator +( U rhs ) const + { + T ret( 0 ); + details::AdditionHelper< T, U, E >::Addition( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + //addition assignment + SafeInt< T, E >& operator +=( SafeInt< T, E > rhs ) + { + details::AdditionHelper< T, T, E >::Addition( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator +=( U rhs ) + { + details::AdditionHelper< T, U, E >::Addition( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator +=( SafeInt< U, E > rhs ) + { + details::AdditionHelper< T, U, E >::Addition( m_int, (U)rhs, m_int ); + return *this; + } + + // Subtraction + template < typename U > + SafeInt< T, E > operator -( U rhs ) const + { + T ret( 0 ); + details::SubtractionHelper< T, U, E >::Subtract( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator -(SafeInt< T, E > rhs) const + { + T ret( 0 ); + details::SubtractionHelper< T, T, E >::Subtract( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Subtraction assignment + SafeInt< T, E >& operator -=( SafeInt< T, E > rhs ) + { + details::SubtractionHelper< T, T, E >::Subtract( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator -=( U rhs ) + { + details::SubtractionHelper< T, U, E >::Subtract( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator -=( SafeInt< U, E > rhs ) + { + details::SubtractionHelper< T, U, E >::Subtract( m_int, (U)rhs, m_int ); + return *this; + } + + // Comparison operators + // Additional overloads defined outside the class + // to allow for cases where the SafeInt is the rhs value + + // Less than + template < typename U > + bool operator <( U rhs ) const throw() + { + return details::GreaterThanTest< U, T >::GreaterThan( rhs, m_int ); + } + + bool operator <( SafeInt< T, E > rhs ) const throw() + { + return m_int < (T)rhs; + } + + // Greater than or eq. + template < typename U > + bool operator >=( U rhs ) const throw() + { + return !details::GreaterThanTest< U, T >::GreaterThan( rhs, m_int ); + } + + bool operator >=( SafeInt< T, E > rhs ) const throw() + { + return m_int >= (T)rhs; + } + + // Greater than + template < typename U > + bool operator >( U rhs ) const throw() + { + return details::GreaterThanTest< T, U >::GreaterThan( m_int, rhs ); + } + + bool operator >( SafeInt< T, E > rhs ) const throw() + { + return m_int > (T)rhs; + } + + // Less than or eq. + template < typename U > + bool operator <=( U rhs ) const throw() + { + return !details::GreaterThanTest< T, U >::GreaterThan( m_int, rhs ); + } + + bool operator <=( SafeInt< T, E > rhs ) const throw() + { + return m_int <= (T)rhs; + } + + // Equality + template < typename U > + bool operator ==( U rhs ) const throw() + { + return details::EqualityTest< T, U >::IsEquals( m_int, rhs ); + } + + // Need an explicit override for type bool + bool operator ==( bool rhs ) const throw() + { + return ( m_int == 0 ? false : true ) == rhs; + } + + bool operator ==( SafeInt< T, E > rhs ) const throw() { return m_int == (T)rhs; } + + // != operators + template < typename U > + bool operator !=( U rhs ) const throw() + { + return !details::EqualityTest< T, U >::IsEquals( m_int, rhs ); + } + + bool operator !=( bool b ) const throw() + { + return ( m_int == 0 ? false : true ) != b; + } + + bool operator !=( SafeInt< T, E > rhs ) const throw() { return m_int != (T)rhs; } + + // Shift operators + // Note - shift operators ALWAYS return the same type as the lhs + // specific version for SafeInt< T, E > not needed - + // code path is exactly the same as for SafeInt< U, E > as rhs + + // Left shift + // Also, shifting > bitcount is undefined - trap in debug (check _SAFEINT_SHIFT_ASSERT) + + template < typename U > + SafeInt< T, E > operator <<( U bits ) const throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( bits < (int)details::IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << bits ) ); + } + + template < typename U > + SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || (U)bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( (U)bits < (int)details::IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << (U)bits ) ); + } + + // Left shift assignment + + template < typename U > + SafeInt< T, E >& operator <<=( U bits ) throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( bits < (int)details::IntTraits< T >::bitCount ); + + m_int <<= bits; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || (U)bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( (U)bits < (int)details::IntTraits< T >::bitCount ); + + m_int <<= (U)bits; + return *this; + } + + // Right shift + template < typename U > + SafeInt< T, E > operator >>( U bits ) const throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( bits < (int)details::IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int >> bits ) ); + } + + template < typename U > + SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || (U)bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( bits < (int)details::IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)(m_int >> (U)bits) ); + } + + // Right shift assignment + template < typename U > + SafeInt< T, E >& operator >>=( U bits ) throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( bits < (int)details::IntTraits< T >::bitCount ); + + m_int >>= bits; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) throw() + { + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< U >::isSigned || (U)bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( (U)bits < (int)details::IntTraits< T >::bitCount ); + + m_int >>= (U)bits; + return *this; + } + + // Bitwise operators + // This only makes sense if we're dealing with the same type and size + // demand a type T, or something that fits into a type T + + // Bitwise & + SafeInt< T, E > operator &( SafeInt< T, E > rhs ) const throw() + { + return SafeInt< T, E >( m_int & (T)rhs ); + } + + template < typename U > + SafeInt< T, E > operator &( U rhs ) const throw() + { + // we want to avoid setting bits by surprise + // consider the case of lhs = int, value = 0xffffffff + // rhs = char, value = 0xff + // + // programmer intent is to get only the lower 8 bits + // normal behavior is to upcast both sides to an int + // which then sign extends rhs, setting all the bits + + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( details::BinaryAndHelper< T, U >::And( m_int, rhs ) ); + } + + // Bitwise & assignment + SafeInt< T, E >& operator &=( SafeInt< T, E > rhs ) throw() + { + m_int &= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator &=( U rhs ) throw() + { + m_int = details::BinaryAndHelper< T, U >::And( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator &=( SafeInt< U, E > rhs ) throw() + { + m_int = details::BinaryAndHelper< T, U >::And( m_int, (U)rhs ); + return *this; + } + + // XOR + SafeInt< T, E > operator ^( SafeInt< T, E > rhs ) const throw() + { + return SafeInt< T, E >( (T)( m_int ^ (T)rhs ) ); + } + + template < typename U > + SafeInt< T, E > operator ^( U rhs ) const throw() + { + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( details::BinaryXorHelper< T, U >::Xor( m_int, rhs ) ); + } + + // XOR assignment + SafeInt< T, E >& operator ^=( SafeInt< T, E > rhs ) throw() + { + m_int ^= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator ^=( U rhs ) throw() + { + m_int = details::BinaryXorHelper< T, U >::Xor( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator ^=( SafeInt< U, E > rhs ) throw() + { + m_int = details::BinaryXorHelper< T, U >::Xor( m_int, (U)rhs ); + return *this; + } + + // bitwise OR + SafeInt< T, E > operator |( SafeInt< T, E > rhs ) const throw() + { + return SafeInt< T, E >( (T)( m_int | (T)rhs ) ); + } + + template < typename U > + SafeInt< T, E > operator |( U rhs ) const throw() + { + return SafeInt< T, E >( details::BinaryOrHelper< T, U >::Or( m_int, rhs ) ); + } + + // bitwise OR assignment + SafeInt< T, E >& operator |=( SafeInt< T, E > rhs ) throw() + { + m_int |= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator |=( U rhs ) throw() + { + m_int = details::BinaryOrHelper< T, U >::Or( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator |=( SafeInt< U, E > rhs ) throw() + { + m_int = details::BinaryOrHelper< T, U >::Or( m_int, (U)rhs ); + return *this; + } + + // Miscellaneous helper functions + SafeInt< T, E > Min( SafeInt< T, E > test, SafeInt< T, E > floor = SafeInt< T, E >( details::IntTraits< T >::minInt ) ) const throw() + { + T tmp = test < m_int ? test : m_int; + return tmp < floor ? floor : tmp; + } + + SafeInt< T, E > Max( SafeInt< T, E > test, SafeInt< T, E > upper = SafeInt< T, E >( details::IntTraits< T >::maxInt ) ) const throw() + { + T tmp = test > m_int ? test : m_int; + return tmp > upper ? upper : tmp; + } + + void Swap( SafeInt< T, E >& with ) throw() + { + T temp( m_int ); + m_int = with.m_int; + with.m_int = temp; + } + + template < int bits > + const SafeInt< T, E >& Align() + { + // Zero is always aligned + if( m_int == 0 ) + return *this; + + // We don't support aligning negative numbers at this time + // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255) + // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127). + // Also makes no sense to try to align on negative or no bits. + + _SAFEINT_SHIFT_ASSERT( ( ( details::IntTraits<T>::isSigned && bits < (int)details::IntTraits< T >::bitCount - 1 ) + || ( !details::IntTraits<T>::isSigned && bits < (int)details::IntTraits< T >::bitCount ) ) && + bits >= 0 && ( !details::IntTraits<T>::isSigned || m_int > 0 ) ); + + const T AlignValue = ( (T)1 << bits ) - 1; + + m_int = ( m_int + AlignValue ) & ~AlignValue; + + if( m_int <= 0 ) + E::SafeIntOnOverflow(); + + return *this; + } + + // Commonly needed alignments: + const SafeInt< T, E >& Align2() { return Align< 1 >(); } + const SafeInt< T, E >& Align4() { return Align< 2 >(); } + const SafeInt< T, E >& Align8() { return Align< 3 >(); } + const SafeInt< T, E >& Align16() { return Align< 4 >(); } + const SafeInt< T, E >& Align32() { return Align< 5 >(); } + const SafeInt< T, E >& Align64() { return Align< 6 >(); } + +private: + T m_int; +}; + +// Externally defined functions for the case of U op SafeInt< T, E > +template < typename T, typename U, typename E > +bool operator <( U lhs, SafeInt< T, E > rhs ) throw() +{ + return details::GreaterThanTest< T, U >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator <( SafeInt< U, E > lhs, SafeInt< T, E > rhs ) throw() +{ + return details::GreaterThanTest< T, U >::GreaterThan( (T)rhs, (U)lhs ); +} + +// Greater than +template < typename T, typename U, typename E > +bool operator >( U lhs, SafeInt< T, E > rhs ) throw() +{ + return details::GreaterThanTest< U, T >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +bool operator >( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) throw() +{ + return details::GreaterThanTest< T, U >::GreaterThan( (T)lhs, (U)rhs ); +} + +// Greater than or equal +template < typename T, typename U, typename E > +bool operator >=( U lhs, SafeInt< T, E > rhs ) throw() +{ + return !details::GreaterThanTest< T, U >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator >=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) throw() +{ + return !details::GreaterThanTest< U, T >::GreaterThan( (U)rhs, (T)lhs ); +} + +// Less than or equal +template < typename T, typename U, typename E > +bool operator <=( U lhs, SafeInt< T, E > rhs ) throw() +{ + return !details::GreaterThanTest< U, T >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +bool operator <=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) throw() +{ + return !details::GreaterThanTest< T, U >::GreaterThan( (T)lhs, (U)rhs ); +} + +// equality +// explicit overload for bool +template < typename T, typename E > +bool operator ==( bool lhs, SafeInt< T, E > rhs ) throw() +{ + return lhs == ( (T)rhs == 0 ? false : true ); +} + +template < typename T, typename U, typename E > +bool operator ==( U lhs, SafeInt< T, E > rhs ) throw() +{ + return details::EqualityTest< T, U >::IsEquals((T)rhs, lhs); +} + +template < typename T, typename U, typename E > +bool operator ==( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) throw() +{ + return details::EqualityTest< T, U >::IsEquals( (T)lhs, (U)rhs ); +} + +//not equals +template < typename T, typename U, typename E > +bool operator !=( U lhs, SafeInt< T, E > rhs ) throw() +{ + return !details::EqualityTest< T, U >::IsEquals( rhs, lhs ); +} + +template < typename T, typename E > +bool operator !=( bool lhs, SafeInt< T, E > rhs ) throw() +{ + return ( (T)rhs == 0 ? false : true ) != lhs; +} + +template < typename T, typename U, typename E > +bool operator !=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) throw() +{ + return !details::EqualityTest< T, U >::IsEquals( lhs, rhs ); +} + +// Modulus +template < typename T, typename U, typename E > +SafeInt< T, E > operator %( U lhs, SafeInt< T, E > rhs ) +{ + // Value of return depends on sign of lhs + // This one may not be safe - bounds check in constructor + // if lhs is negative and rhs is unsigned, this will throw an exception. + + // Fast-track the simple case + // same size and same sign +#pragma warning(suppress:4127 6326) + if( sizeof(T) == sizeof(U) && details::IntTraits< T >::isSigned == details::IntTraits< U >::isSigned ) + { + if( rhs != 0 ) + { + if( details::IntTraits< T >::isSigned && (T)rhs == -1 ) + return 0; + + return SafeInt< T, E >( (T)( lhs % (T)rhs ) ); + } + + E::SafeIntOnDivZero(); + } + + return SafeInt< T, E >( ( SafeInt< U, E >( lhs ) % (T)rhs ) ); +} + +// Multiplication +template < typename T, typename U, typename E > +SafeInt< T, E > operator *( U lhs, SafeInt< T, E > rhs ) +{ + T ret( 0 ); + details::MultiplicationHelper< T, U, E >::Multiply( (T)rhs, lhs, ret ); + return SafeInt< T, E >(ret); +} + +// Division +template < typename T, typename U, typename E > SafeInt< T, E > operator /( U lhs, SafeInt< T, E > rhs ) +{ +#pragma warning(push) +#pragma warning(disable: 4127 4146 4307 4310 6326) + // Corner case - has to be handled seperately + if( details::DivisionMethod< U, T >::method == details::DivisionState_UnsignedSigned ) + { + if( (T)rhs > 0 ) + return SafeInt< T, E >( lhs/(T)rhs ); + + // Now rhs is either negative, or zero + if( (T)rhs != 0 ) + { + if( sizeof( U ) >= 4 && sizeof( T ) <= sizeof( U ) ) + { + // Problem case - normal casting behavior changes meaning + // flip rhs to positive + // any operator casts now do the right thing + U tmp; + if( sizeof(T) == 4 ) + tmp = lhs/(U)(unsigned __int32)( -(T)rhs ); + else + tmp = lhs/(U)( -(T)rhs ); + + if( tmp <= details::IntTraits< T >::maxInt ) + return SafeInt< T, E >( -( (T)tmp ) ); + + // Corner case + // Note - this warning happens because we're not using partial + // template specialization in this case. For any real cases where + // this block isn't optimized out, the warning won't be present. + if( tmp == (U)details::IntTraits< T >::maxInt + 1 ) + return SafeInt< T, E >( details::IntTraits< T >::minInt ); + + E::SafeIntOnOverflow(); + } + + return SafeInt< T, E >(lhs/(T)rhs); + } + + E::SafeIntOnDivZero(); + } // method == DivisionState_UnsignedSigned + + if( details::SafeIntCompare< T, U >::isBothSigned ) + { + if( lhs == details::IntTraits< U >::minInt && (T)rhs == -1 ) + { + // corner case of a corner case - lhs = min int, rhs = -1, + // but rhs is the return type, so in essence, we can return -lhs + // if rhs is a larger type than lhs + if( sizeof( U ) < sizeof( T ) ) + { + return SafeInt< T, E >( (T)( -(T)details::IntTraits< U >::minInt ) ); + } + + // If rhs is smaller or the same size int, then -minInt won't work + E::SafeIntOnOverflow(); + } + } + + // Otherwise normal logic works with addition of bounds check when casting from U->T + U ret; + details::DivisionHelper< U, T, E >::Divide( lhs, (T)rhs, ret ); + return SafeInt< T, E >( ret ); +#pragma warning(pop) +} + +// Addition +template < typename T, typename U, typename E > +SafeInt< T, E > operator +( U lhs, SafeInt< T, E > rhs ) +{ + T ret( 0 ); + details::AdditionHelper< T, U, E >::Addition( (T)rhs, lhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Subtraction +template < typename T, typename U, typename E > +SafeInt< T, E > operator -( U lhs, SafeInt< T, E > rhs ) +{ + T ret( 0 ); + details::SubtractionHelper< U, T, E, details::SubtractionMethod2< U, T >::method >::Subtract( lhs, rhs.Ref(), ret ); + + return SafeInt< T, E >( ret ); +} + +// Overrides designed to deal with cases where a SafeInt is assigned out +// to a normal int - this at least makes the last operation safe +// += +template < typename T, typename U, typename E > +T& operator +=( T& lhs, SafeInt< U, E > rhs ) +{ + T ret( 0 ); + details::AdditionHelper< T, U, E >::Addition( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator -=( T& lhs, SafeInt< U, E > rhs ) +{ + T ret( 0 ); + details::SubtractionHelper< T, U, E >::Subtract( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator *=( T& lhs, SafeInt< U, E > rhs ) +{ + T ret( 0 ); + details::MultiplicationHelper< T, U, E >::Multiply( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator /=( T& lhs, SafeInt< U, E > rhs ) +{ + T ret( 0 ); + details::DivisionHelper< T, U, E >::Divide( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator %=( T& lhs, SafeInt< U, E > rhs ) +{ + T ret( 0 ); + details::ModulusHelper< T, U, E >::Modulus( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator &=( T& lhs, SafeInt< U, E > rhs ) throw() +{ + lhs = details::BinaryAndHelper< T, U >::And( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator ^=( T& lhs, SafeInt< U, E > rhs ) throw() +{ + lhs = details::BinaryXorHelper< T, U >::Xor( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator |=( T& lhs, SafeInt< U, E > rhs ) throw() +{ + lhs = details::BinaryOrHelper< T, U >::Or( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator <<=( T& lhs, SafeInt< U, E > rhs ) throw() +{ + lhs = (T)( SafeInt< T, E >( lhs ) << (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator >>=( T& lhs, SafeInt< U, E > rhs ) throw() +{ + lhs = (T)( SafeInt< T, E >( lhs ) >> (U)rhs ); + return lhs; +} + +// Specific pointer overrides +// Note - this function makes no attempt to ensure +// that the resulting pointer is still in the buffer, only +// that no int overflows happened on the way to getting the new pointer +template < typename T, typename U, typename E > +T*& operator +=( T*& lhs, SafeInt< U, E > rhs ) +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< uintptr_t, E > ptr_val = reinterpret_cast< uintptr_t >( lhs ); + // Check first that rhs is valid for the type of ptrdiff_t + // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t + // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff + // Finally, cast the number back to a pointer of the correct type + lhs = reinterpret_cast< T* >( (uintptr_t)( ptr_val + (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator -=( T*& lhs, SafeInt< U, E > rhs ) +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< size_t, E > ptr_val = reinterpret_cast< uintptr_t >( lhs ); + // See above for comments + lhs = reinterpret_cast< T* >( (uintptr_t)( ptr_val - (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator *=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator /=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator %=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator &=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator ^=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator |=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator <<=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator >>=( T* lhs, SafeInt< U, E > rhs ) +{ + static_assert( false, "SafeInt<T>: This operator explicitly not supported" ); + return lhs; +} + +// Shift operators +// NOTE - shift operators always return the type of the lhs argument + +// Left shift +template < typename T, typename U, typename E > +SafeInt< U, E > operator <<( U lhs, SafeInt< T, E > bits ) throw() +{ + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< T >::isSigned || (T)bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( (T)bits < (int)details::IntTraits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs << (T)bits ) ); +} + +// Right shift +template < typename T, typename U, typename E > +SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) throw() +{ + _SAFEINT_SHIFT_ASSERT( !details::IntTraits< T >::isSigned || (T)bits >= 0 ); + _SAFEINT_SHIFT_ASSERT( (T)bits < (int)details::IntTraits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); +} + +// Bitwise operators +// This only makes sense if we're dealing with the same type and size +// demand a type T, or something that fits into a type T. + +// Bitwise & +template < typename T, typename U, typename E > +SafeInt< T, E > operator &( U lhs, SafeInt< T, E > rhs ) throw() +{ + return SafeInt< T, E >( details::BinaryAndHelper< T, U >::And( (T)rhs, lhs ) ); +} + +// Bitwise XOR +template < typename T, typename U, typename E > +SafeInt< T, E > operator ^( U lhs, SafeInt< T, E > rhs ) throw() +{ + return SafeInt< T, E >(details::BinaryXorHelper< T, U >::Xor( (T)rhs, lhs ) ); +} + +// Bitwise OR +template < typename T, typename U, typename E > +SafeInt< T, E > operator |( U lhs, SafeInt< T, E > rhs ) throw() +{ + return SafeInt< T, E >( details::BinaryOrHelper< T, U >::Or( (T)rhs, lhs ) ); +} + +} // namespace utilities + +} // namespace msl + +#pragma pack(pop) diff --git a/test_data/lots_of_files/safeint_internal.h b/test_data/lots_of_files/safeint_internal.h new file mode 100644 index 0000000..552fbcf --- /dev/null +++ b/test_data/lots_of_files/safeint_internal.h @@ -0,0 +1,2894 @@ +/*** +*safeint_internal.h - Internal details for SafeInt (see safeint.h) +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Private internal details for SafeInt. +* The constructs and functions in Microsoft::Utilities::details are not +* meant to be used by external code and can change at any time. +* +****/ + +#pragma once + +#include <crtdbg.h> + +#pragma pack(push, _CRT_PACKING) + +namespace msl +{ + +namespace utilities +{ + +namespace details +{ +#pragma warning(push) +#pragma warning(disable:4702) + +template < typename T > class NumericType; + +template <> class NumericType<bool> { public: enum{ isBool = true, isFloat = false, isInt = false }; }; +template <> class NumericType<char> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<unsigned char> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<signed char> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<short> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<unsigned short> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +#if _NATIVE_WCHAR_T_DEFINED +template <> class NumericType<wchar_t> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +#endif /* _NATIVE_WCHAR_T_DEFINED */ +template <> class NumericType<int> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<unsigned int> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<long> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<unsigned long> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<__int64> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<unsigned __int64> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<float> { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +template <> class NumericType<double> { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +template <> class NumericType<long double> { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +// Catch-all for anything not supported +template < typename T > class NumericType { public: enum{ isBool = false, isFloat = false, isInt = false }; }; + + +template < typename T > class IntTraits +{ +public: + _STATIC_ASSERT( NumericType<T>::isInt || NumericType<T>::isBool ); + enum + { +#pragma warning(suppress:4804) + isSigned = ( (T)(-1) < 0 ), + is64Bit = ( sizeof(T) == 8 ), + is32Bit = ( sizeof(T) == 4 ), + is16Bit = ( sizeof(T) == 2 ), + is8Bit = ( sizeof(T) == 1 ), + isLT32Bit = ( sizeof(T) < 4 ), + isLT64Bit = ( sizeof(T) < 8 ), + isInt8 = ( sizeof(T) == 1 && isSigned ), + isUint8 = ( sizeof(T) == 1 && !isSigned ), + isInt16 = ( sizeof(T) == 2 && isSigned ), + isUint16 = ( sizeof(T) == 2 && !isSigned ), + isInt32 = ( sizeof(T) == 4 && isSigned ), + isUint32 = ( sizeof(T) == 4 && !isSigned ), + isInt64 = ( sizeof(T) == 8 && isSigned ), + isUint64 = ( sizeof(T) == 8 && !isSigned ), + bitCount = ( sizeof(T)*8 ), +#pragma warning(suppress:4804) + isBool = NumericType<T>::isBool + }; + +#pragma warning(push) +#pragma warning(disable:4310) + const static T maxInt = isSigned ? ((T)~((T)1 << (T)(bitCount-1))) : ((T)(~(T)0)); + const static T minInt = isSigned ? ((T)((T)1 << (T)(bitCount-1))) : ((T)0); +#pragma warning(pop) +}; + +// this is strictly internal and not to be used as a policy in SafeInt<> +struct SafeIntErrorPolicy_NoThrow +{ + static void SafeIntOnOverflow() + { + } + + static void SafeIntOnDivZero() + { + } +}; + +template < typename T, typename U > class SafeIntCompare +{ +public: + enum + { + isBothSigned = (IntTraits< T >::isSigned && IntTraits< U >::isSigned), + isBothUnsigned = (!IntTraits< T >::isSigned && !IntTraits< U >::isSigned), + isLikeSigned = (IntTraits< T >::isSigned == IntTraits< U >::isSigned), + isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || + (IntTraits< T >::isSigned && sizeof(T) > sizeof(U))), + isBothLT32Bit = (IntTraits< T >::isLT32Bit && IntTraits< U >::isLT32Bit), + isBothLT64Bit = (IntTraits< T >::isLT64Bit && IntTraits< U >::isLT64Bit) + }; +}; + +template < typename U > class SafeIntCompare< float, U > +{ +public: + enum + { + isBothSigned = IntTraits< U >::isSigned, + isBothUnsigned = false, + isLikeSigned = IntTraits< U >::isSigned, + isCastOK = true + }; +}; + +template < typename U > class SafeIntCompare< double, U > +{ +public: + enum + { + isBothSigned = IntTraits< U >::isSigned, + isBothUnsigned = false, + isLikeSigned = IntTraits< U >::isSigned, + isCastOK = true + }; +}; + +template < typename U > class SafeIntCompare< long double, U > +{ +public: + enum + { + isBothSigned = IntTraits< U >::isSigned, + isBothUnsigned = false, + isLikeSigned = IntTraits< U >::isSigned, + isCastOK = true + }; +}; + +//all of the arithmetic operators can be solved by the same code within +//each of these regions without resorting to compile-time constant conditionals +//most operators collapse the problem into less than the 22 zones, but this is used +//as the first cut +//using this also helps ensure that we handle all of the possible cases correctly + +template < typename T, typename U > class IntRegion +{ +public: + enum + { + //unsigned-unsigned zone + IntZone_UintLT32_UintLT32 = SafeIntCompare< T,U >::isBothUnsigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Uint32_UintLT64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, + IntZone_UintLT32_Uint32 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, + IntZone_Uint64_Uint = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is64Bit, + IntZone_UintLT64_Uint64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, + //unsigned-signed + IntZone_UintLT32_IntLT32 = !IntTraits< T >::isSigned && IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Uint32_IntLT64 = IntTraits< T >::isUint32 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_UintLT32_Int32 = !IntTraits< T >::isSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::isInt32, + IntZone_Uint64_Int = IntTraits< T >::isUint64 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_UintLT64_Int64 = !IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isInt64, + IntZone_Uint64_Int64 = IntTraits< T >::isUint64 && IntTraits< U >::isInt64, + //signed-signed + IntZone_IntLT32_IntLT32 = SafeIntCompare< T,U >::isBothSigned && SafeIntCompare< T, U >::isBothLT32Bit, + IntZone_Int32_IntLT64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, + IntZone_IntLT32_Int32 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, + IntZone_Int64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isInt64 && IntTraits< U >::isInt64, + IntZone_Int64_Int = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is64Bit && IntTraits< U >::isLT64Bit, + IntZone_IntLT64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, + //signed-unsigned + IntZone_IntLT32_UintLT32 = IntTraits< T >::isSigned && !IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Int32_UintLT32 = IntTraits< T >::isInt32 && !IntTraits< U >::isSigned && IntTraits< U >::isLT32Bit, + IntZone_IntLT64_Uint32 = IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isUint32, + IntZone_Int64_UintLT64 = IntTraits< T >::isInt64 && !IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_Int_Uint64 = IntTraits< T >::isSigned && IntTraits< U >::isUint64 && IntTraits< T >::isLT64Bit, + IntZone_Int64_Uint64 = IntTraits< T >::isInt64 && IntTraits< U >::isUint64 + }; +}; + +// useful function to help with getting the magnitude of a negative number +enum AbsMethod +{ + AbsMethodInt, + AbsMethodInt64, + AbsMethodNoop +}; + +template < typename T > +class GetAbsMethod +{ +public: + enum + { + method = IntTraits< T >::isLT64Bit && IntTraits< T >::isSigned ? AbsMethodInt : + IntTraits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop + }; +}; + +template < typename T, int Method = GetAbsMethod< T >::method > class AbsValueHelper; + +template < typename T > class AbsValueHelper < T, AbsMethodInt > +{ +public: + static unsigned __int32 Abs( T t ) throw() + { + _ASSERTE( t < 0 ); + return (unsigned __int32)-t; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodInt64 > +{ +public: + static unsigned __int64 Abs( T t ) throw() + { + _ASSERTE( t < 0 ); + return (unsigned __int64)-t; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodNoop > +{ +public: + static T Abs( T t ) throw() + { + // Why are you calling Abs on an unsigned number ??? + _ASSERTE( ("AbsValueHelper::Abs should not be called with an unsigned integer type", 0) ); + return t; + } +}; + +template < typename T, typename E, bool fSigned > class NegationHelper; + +template < typename T, typename E > class NegationHelper < T, E, true > // Signed +{ +public: + static SafeIntError Negative( T t, T& ret ) + { + // corner case + if( t != IntTraits< T >::minInt ) + { + // cast prevents unneeded checks in the case of small ints + ret = -t; + return SafeIntNoError; + } + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + + +template < typename T, typename E > class NegationHelper < T, E, false > // unsigned +{ +public: + static SafeIntError Negative( T t, T& ret ) throw() + { + _SAFEINT_UNSIGNED_NEGATION_BEHAVIOR(); + +#pragma warning(suppress:4127) + _ASSERTE( !IntTraits<T>::isLT32Bit ); + +#pragma warning(suppress:4146) + ret = -t; + return SafeIntNoError; + } +}; + +//core logic to determine casting behavior +enum CastMethod +{ + CastOK = 0, + CastCheckLTZero, + CastCheckGTMax, + CastCheckMinMaxUnsigned, + CastCheckMinMaxSigned, + CastFromFloat, + CastToBool, + CastFromBool +}; + +template < typename ToType, typename FromType > +class GetCastMethod +{ +public: + enum + { + method = ( IntTraits< FromType >::isBool && + !IntTraits< ToType >::isBool ) ? CastFromBool : + + ( !IntTraits< FromType >::isBool && + IntTraits< ToType >::isBool ) ? CastToBool : + ( NumericType< FromType >::isFloat && + !NumericType< ToType >::isFloat ) ? CastFromFloat : + + ( SafeIntCompare< ToType, FromType >::isCastOK || + ( NumericType< ToType >::isFloat && + !NumericType< FromType >::isFloat ) ) ? CastOK : + + ( ( IntTraits< ToType >::isSigned && + !IntTraits< FromType >::isSigned && + sizeof( FromType ) >= sizeof( ToType ) ) || + ( SafeIntCompare< ToType, FromType >::isBothUnsigned && + sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax : + + ( !IntTraits< ToType >::isSigned && + IntTraits< FromType >::isSigned && + sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero : + + ( !IntTraits< ToType >::isSigned ) ? CastCheckMinMaxUnsigned + : CastCheckMinMaxSigned + }; +}; + +template < typename T, typename U, typename E, + int Method = GetCastMethod< T, U >::method > class SafeCastHelper; + +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastOK > +{ +public: + static SafeIntError Cast( U u, T& t ) throw() + { + t = (T)u; + return SafeIntNoError; + } +}; + +// special case floats and doubles +// tolerate loss of precision +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastFromFloat > +{ +public: + static SafeIntError Cast( U u, T& t ) + { + if( u <= (U)IntTraits< T >::maxInt && + u >= (U)IntTraits< T >::minInt ) + { + t = (T)u; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +// Match on any method where a bool is cast to type T +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastFromBool > +{ +public: + static SafeIntError Cast( bool b, T& t ) throw() + { + t = (T)( b ? 1 : 0 ); + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastToBool > +{ +public: + static SafeIntError Cast( T t, bool& b ) throw() + { + b = !!t; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckLTZero > +{ +public: + static SafeIntError Cast( U u, T& t ) + { + if( u < 0 ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + t = (T)u; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckGTMax > +{ +public: + static SafeIntError Cast( U u, T& t ) + { + if( u > IntTraits< T >::maxInt ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + t = (T)u; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckMinMaxUnsigned > +{ +public: + static SafeIntError Cast( U u, T& t ) + { + // U is signed - T could be either signed or unsigned + if( u > IntTraits< T >::maxInt || u < 0 ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + t = (T)u; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class SafeCastHelper < T, U, E, CastCheckMinMaxSigned > +{ +public: + static SafeIntError Cast( U u, T& t ) + { + // T, U are signed + if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + t = (T)u; + return SafeIntNoError; + } +}; + +//core logic to determine whether a comparison is valid, or needs special treatment +enum ComparisonMethod +{ + ComparisonMethod_Ok = 0, + ComparisonMethod_CastInt, + ComparisonMethod_CastInt64, + ComparisonMethod_UnsignedT, + ComparisonMethod_UnsignedU +}; + +template < typename T, typename U > +class ValidComparison +{ +public: + enum + { +#if _SAFEINT_USE_ANSI_CONVERSIONS + method = ComparisonMethod_Ok +#else /* _SAFEINT_USE_ANSI_CONVERSIONS */ + method = ( ( SafeIntCompare< T, U >::isLikeSigned ) ? ComparisonMethod_Ok : + ( ( IntTraits< T >::isSigned && sizeof(T) < 8 && sizeof(U) < 4 ) || + ( IntTraits< U >::isSigned && sizeof(T) < 4 && sizeof(U) < 8 ) ) ? ComparisonMethod_CastInt : + ( ( IntTraits< T >::isSigned && sizeof(U) < 8 ) || + ( IntTraits< U >::isSigned && sizeof(T) < 8 ) ) ? ComparisonMethod_CastInt64 : + ( !IntTraits< T >::isSigned ) ? ComparisonMethod_UnsignedT : + ComparisonMethod_UnsignedU ) +#endif /* _SAFEINT_USE_ANSI_CONVERSIONS */ + }; +}; + +template <typename T, typename U, int Method = ValidComparison< T, U >::method > class EqualityTest; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok > +{ +public: + static bool IsEquals( const T t, const U u ) throw() { return ( t == u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt > +{ +public: + static bool IsEquals( const T t, const U u ) throw() { return ( (int)t == (int)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + static bool IsEquals( const T t, const U u ) throw() { return ( (__int64)t == (__int64)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + static bool IsEquals( const T t, const U u ) throw() + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + { + return false; + } + + //else safe to cast to type T + return ( t == (T)u ); + } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU> +{ +public: + static bool IsEquals( const T t, const U u ) throw() + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + { + return false; + } + + //else safe to cast to type U + return ( (U)t == u ); + } +}; + +template <typename T, typename U, int Method = ValidComparison< T, U >::method > class GreaterThanTest; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok > +{ +public: + static bool GreaterThan( const T t, const U u ) throw() { return ( t > u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt > +{ +public: + static bool GreaterThan( const T t, const U u ) throw() { return ( (int)t > (int)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + static bool GreaterThan( const T t, const U u ) throw() { return ( (__int64)t > (__int64)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + static bool GreaterThan( const T t, const U u ) throw() + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + { + return SafeIntNoError; + } + + // else safe to cast to type T + return ( t > (T)u ); + } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU > +{ +public: + static bool GreaterThan( const T t, const U u ) throw() + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + { + return false; + } + + // else safe to cast to type U + return ( (U)t > u ); + } +}; + +// Modulus is simpler than comparison, but follows much the same logic +// using this set of functions, it can't fail except in a div 0 situation +template <typename T, typename U, typename E, int Method = ValidComparison< T, U >::method > class ModulusHelper; + +template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_Ok> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) + { + if(u == 0) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + // trap corner case +#pragma warning(suppress:4127) + if( IntTraits< U >::isSigned ) + { + if(u == -1) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)(t % u); + return SafeIntNoError; + } +}; + +template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_CastInt> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) + { + if(u == 0) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + // trap corner case +#pragma warning(suppress:4127) + if( IntTraits< U >::isSigned ) + { + if(u == -1) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)(t % u); + return SafeIntNoError; + } +}; + +template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_CastInt64> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) + { + if(u == 0) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + +#pragma warning(suppress:4127) + if(IntTraits< U >::isSigned && u == -1) + { + result = 0; + } + else + { + result = (T)((__int64)t % (__int64)u); + } + + return SafeIntNoError; + } +}; + +// T is unsigned __int64, U is any signed int +template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_UnsignedT> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) + { + if(u == 0) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + // u could be negative - if so, need to convert to positive + // casts below are always safe due to the way modulus works + if(u < 0) + { + result = (T)(t % AbsValueHelper< U >::Abs(u)); + } + else + { + result = (T)(t % u); + } + + return SafeIntNoError; + } +}; + +// U is unsigned __int64, T any signed int +template <typename T, typename U, typename E> class ModulusHelper <T, U, E, ComparisonMethod_UnsignedU> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) + { + if(u == 0) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + //t could be negative - if so, need to convert to positive + if(t < 0) + { + result = -(T)( AbsValueHelper< T >::Abs( t ) % u ); + } + else + { + result = (T)((T)t % u); + } + + return SafeIntNoError; + } +}; + +//core logic to determine method to check multiplication +enum MultiplicationState +{ + MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit + MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit + MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit + MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller + MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller + MultiplicationState_Uint64Uint64, // Both are unsigned int64 + MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32 + MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64 + MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit + MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64 + MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32 + MultiplicationState_Int64Int64, // lhs int64, rhs int64 + MultiplicationState_Int64Int, // lhs int64, rhs int32 + MultiplicationState_IntUint64, // lhs int, rhs unsigned int64 + MultiplicationState_IntInt64, // lhs int, rhs int64 + MultiplicationState_Int64Uint64, // lhs int64, rhs uint64 + MultiplicationState_Error +}; + +template < typename T, typename U > +class MultiplicationMethod +{ +public: + enum + { + // unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? MultiplicationState_CastUint : + (IntRegion< T,U >::IntZone_Uint32_UintLT64 || + IntRegion< T,U >::IntZone_UintLT32_Uint32) ? MultiplicationState_CastUint64 : + SafeIntCompare< T,U >::isBothUnsigned && + IntTraits< T >::isUint64 && IntTraits< U >::isUint64 ? MultiplicationState_Uint64Uint64 : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? MultiplicationState_Uint64Uint : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int) ? MultiplicationState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? MultiplicationState_UintInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int64) ? MultiplicationState_Uint64Int64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_Int64) ? MultiplicationState_Int64Int64 : + (IntRegion< T,U >::IntZone_Int64_Int) ? MultiplicationState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? MultiplicationState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? MultiplicationState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? MultiplicationState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64 ? MultiplicationState_Int64Uint64 : + MultiplicationState_Error ) ) + }; +}; + +template <typename T, typename U, typename E, int Method = MultiplicationMethod< T, U >::method > class MultiplicationHelper; + +template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastInt> +{ +public: + //accepts signed, both less than 32-bit + static SafeIntError Multiply( const T& t, const U& u, T& ret ) + { + int tmp = t * u; + + if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + ret = (T)tmp; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastUint > +{ +public: + //accepts unsigned, both less than 32-bit + static SafeIntError Multiply( const T& t, const U& u, T& ret ) + { + unsigned int tmp = t * u; + + if( tmp > IntTraits< T >::maxInt ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + ret = (T)tmp; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastInt64> +{ +public: + //mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less + static SafeIntError Multiply( const T& t, const U& u, T& ret ) + { + __int64 tmp = (__int64)t * (__int64)u; + + if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + ret = (T)tmp; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class MultiplicationHelper< T, U, E, MultiplicationState_CastUint64> +{ +public: + //both unsigned where at least one argument is 32-bit, and both are 32-bit or less + static SafeIntError Multiply( const T& t, const U& u, T& ret ) + { + unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; + + if(tmp > (unsigned __int64)IntTraits< T >::maxInt) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + ret = (T)tmp; + return SafeIntNoError; + } +}; + +// T = left arg and return type +// U = right arg +template < typename T, typename U, typename E > class LargeIntRegMultiply; + +template< typename E > class LargeIntRegMultiply< unsigned __int64, unsigned __int64, E > +{ +public: + static SafeIntError RegMultiply( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64& ret ) + { + unsigned __int32 aHigh, aLow, bHigh, bLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + ret = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + ret = (unsigned __int64)aLow * (unsigned __int64)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + ret = (unsigned __int64)aHigh * (unsigned __int64)bLow; + } + } + else + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + if(ret != 0) + { + unsigned __int64 tmp; + + if((unsigned __int32)(ret >> 32) != 0) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + ret <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; + ret += tmp; + + if(ret < tmp) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + return SafeIntNoError; + } + + ret = (unsigned __int64)aLow * (unsigned __int64)bLow; + return SafeIntNoError; + } +}; + +template< typename E > class LargeIntRegMultiply< unsigned __int64, unsigned __int32, E > +{ +public: + static SafeIntError RegMultiply( const unsigned __int64& a, unsigned __int32 b, unsigned __int64& ret ) + { + unsigned __int32 aHigh, aLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + + ret = 0; + + if(aHigh != 0) + { + ret = (unsigned __int64)aHigh * (unsigned __int64)b; + + unsigned __int64 tmp; + + if((unsigned __int32)(ret >> 32) != 0) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + ret <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)b; + ret += tmp; + + if(ret < tmp) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + return SafeIntNoError; + } + + ret = (unsigned __int64)aLow * (unsigned __int64)b; + return SafeIntNoError; + } +}; + +template< typename E > class LargeIntRegMultiply< unsigned __int64, signed __int32, E > +{ +public: + static SafeIntError RegMultiply( const unsigned __int64& a, signed __int32 b, unsigned __int64& ret ) + { + if( b < 0 && a != 0 ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + return LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >::RegMultiply(a, (unsigned __int32)b, ret); + } +}; + +template< typename E > class LargeIntRegMultiply< unsigned __int64, signed __int64, E > +{ +public: + static SafeIntError RegMultiply( const unsigned __int64& a, signed __int64 b, unsigned __int64& ret ) + { + if( b < 0 && a != 0 ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + return LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >::RegMultiply(a, (unsigned __int64)b, ret); + } +}; + +template< typename E > class LargeIntRegMultiply< signed __int32, unsigned __int64, E > +{ +public: + static SafeIntError RegMultiply( signed __int32 a, const unsigned __int64& b, signed __int32& ret ) + { + unsigned __int32 bHigh, bLow; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + ret = 0; + + if(bHigh != 0 && a != 0) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + if( a < 0 ) + { + a = -a; + fIsNegative = true; + } + + unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; + + + if( !fIsNegative ) + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) + { + ret = (signed __int32)tmp; + return SafeIntNoError; + } + } + else + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) + { + ret = -( (signed __int32)tmp ); + return SafeIntNoError; + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class LargeIntRegMultiply< unsigned __int32, unsigned __int64, E > +{ +public: + static SafeIntError RegMultiply( unsigned __int32 a, const unsigned __int64& b, unsigned __int32& ret ) + { + // Consider that a*b can be broken up into: + // (bHigh * 2^32 + bLow) * a + // => (bHigh * a * 2^32) + (bLow * a) + // In this case, the result must fit into 32-bits + // If bHigh != 0 && a != 0, immediate error. + + if( (unsigned __int32)(b >> 32) != 0 && a != 0 ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + unsigned __int64 tmp = b * (unsigned __int64)a; + + if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + ret = (unsigned __int32)tmp; + return SafeIntNoError; + } +}; + +template < typename E > class LargeIntRegMultiply< unsigned __int32, signed __int64, E > +{ +public: + static SafeIntError RegMultiply( unsigned __int32 a, const signed __int64& b, unsigned __int32& ret ) + { + if( b < 0 && a != 0 ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + return LargeIntRegMultiply< unsigned __int32, unsigned __int64, E >::RegMultiply( a, (unsigned __int64)b, ret ); + } +}; + +template < typename E > class LargeIntRegMultiply< signed __int64, signed __int64, E > +{ +public: + static SafeIntError RegMultiply( const signed __int64& a, const signed __int64& b, signed __int64& ret ) + { + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = -a1; + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = -b1; + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >:: + RegMultiply( (unsigned __int64)a1, (unsigned __int64)b1, (unsigned __int64)tmp ) == SafeIntNoError ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + ret = -(signed __int64)tmp; + return SafeIntNoError; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + ret = (signed __int64)tmp; + return SafeIntNoError; + } + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class LargeIntRegMultiply< signed __int64, unsigned __int32, E > +{ +public: + static SafeIntError RegMultiply( const signed __int64& a, unsigned __int32 b, signed __int64& ret ) + { + bool aNegative = false; + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = -a1; + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >::RegMultiply( (unsigned __int64)a1, b, tmp ) == SafeIntNoError ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + ret = -(signed __int64)tmp; + return SafeIntNoError; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + ret = (signed __int64)tmp; + return SafeIntNoError; + } + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class LargeIntRegMultiply< signed __int64, signed __int32, E > +{ +public: + static SafeIntError RegMultiply( const signed __int64& a, signed __int32 b, signed __int64& ret ) + { + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = -a1; + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = -b1; + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >:: + RegMultiply( (unsigned __int64)a1, (unsigned __int32)b1, (unsigned __int64)tmp ) == SafeIntNoError ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + ret = -(signed __int64)tmp; + return SafeIntNoError; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + ret = (signed __int64)tmp; + return SafeIntNoError; + } + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class LargeIntRegMultiply< signed __int32, signed __int64, E > +{ +public: + static SafeIntError RegMultiply( signed __int32 a, const signed __int64& b, signed __int32& ret ) + { + bool aNegative = false; + bool bNegative = false; + + unsigned __int32 tmp; + __int64 b1 = b; + + if( a < 0 ) + { + aNegative = true; + a = -a; + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = -b1; + } + + if( LargeIntRegMultiply< unsigned __int32, unsigned __int64, E >:: + RegMultiply( (unsigned __int32)a, (unsigned __int64)b1, tmp ) == SafeIntNoError ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) + { +#pragma warning(suppress:4146) + ret = -tmp; + return SafeIntNoError; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) + { + ret = (signed __int32)tmp; + return SafeIntNoError; + } + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class LargeIntRegMultiply< signed __int64, unsigned __int64, E > +{ +public: + static SafeIntError RegMultiply( const signed __int64& a, const unsigned __int64& b, signed __int64& ret ) + { + bool aNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = -a1; + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >:: + RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, tmp ) == SafeIntNoError ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + ret = -((signed __int64)tmp); + return SafeIntNoError; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + ret = (signed __int64)tmp; + return SafeIntNoError; + } + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class MultiplicationHelper< unsigned __int64, unsigned __int64, E, MultiplicationState_Uint64Uint64 > +{ +public: + static SafeIntError Multiply( const unsigned __int64& t, const unsigned __int64& u, unsigned __int64& ret ) + { + return LargeIntRegMultiply< unsigned __int64, unsigned __int64, E >::RegMultiply( t, u, ret ); + } +}; + +template < typename U, typename E > class MultiplicationHelper<unsigned __int64, U, E, MultiplicationState_Uint64Uint > +{ +public: + //U is any unsigned int 32-bit or less + static SafeIntError Multiply( const unsigned __int64& t, const U& u, unsigned __int64& ret ) + { + return LargeIntRegMultiply< unsigned __int64, unsigned __int32, E >::RegMultiply( t, (unsigned __int32)u, ret ); + } +}; + +// converse of the previous function +template < typename T, typename E > class MultiplicationHelper< T, unsigned __int64, E, MultiplicationState_UintUint64 > +{ +public: + // T is any unsigned int up to 32-bit + static SafeIntError Multiply( const T& t, const unsigned __int64& u, T& ret ) + { + unsigned __int32 tmp; + + if( LargeIntRegMultiply< unsigned __int32, unsigned __int64, E >::RegMultiply( t, u, tmp ) == SafeIntNoError && + SafeCastHelper< T, unsigned __int32, E >::Cast(tmp, ret) == SafeIntNoError ) + { + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename E > class MultiplicationHelper< unsigned __int64, U, E, MultiplicationState_Uint64Int > +{ +public: + //U is any signed int, up to 64-bit + static SafeIntError Multiply(const unsigned __int64& t, const U& u, unsigned __int64& ret) + { + return LargeIntRegMultiply< unsigned __int64, signed __int32, E >::RegMultiply(t, (signed __int32)u, ret); + } +}; + +template < typename E > class MultiplicationHelper<unsigned __int64, __int64, E, MultiplicationState_Uint64Int64 > +{ +public: + static SafeIntError Multiply(const unsigned __int64& t, const __int64& u, unsigned __int64& ret) + { + return LargeIntRegMultiply< unsigned __int64, __int64, E >::RegMultiply(t, u, ret); + } +}; + +template < typename T, typename E > class MultiplicationHelper< T, __int64, E, MultiplicationState_UintInt64 > +{ +public: + //T is unsigned up to 32-bit + static SafeIntError Multiply( const T& t, const __int64& u, T& ret ) + { + unsigned __int32 tmp; + + if( LargeIntRegMultiply< unsigned __int32, __int64, E >::RegMultiply( (unsigned __int32)t, u, tmp ) == SafeIntNoError && + SafeCastHelper< T, unsigned __int32, E >::Cast( tmp, ret ) == SafeIntNoError ) + { + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename E > class MultiplicationHelper<__int64, U, E, MultiplicationState_Int64Uint > +{ +public: + //U is unsigned up to 32-bit + static SafeIntError Multiply( const __int64& t, const U& u, __int64& ret ) + { + return LargeIntRegMultiply< __int64, unsigned __int32, E >::RegMultiply( t, (unsigned __int32)u, ret ); + } +}; + +template < typename E > class MultiplicationHelper<__int64, __int64, E, MultiplicationState_Int64Int64 > +{ +public: + static SafeIntError Multiply( const __int64& t, const __int64& u, __int64& ret ) + { + return LargeIntRegMultiply< __int64, __int64, E >::RegMultiply( t, u, ret ); + } +}; + +template < typename U, typename E > class MultiplicationHelper<__int64, U, E, MultiplicationState_Int64Int> +{ +public: + //U is signed up to 32-bit + static SafeIntError Multiply( const __int64& t, U u, __int64& ret ) + { + return LargeIntRegMultiply< __int64, __int32, E >::RegMultiply( t, (__int32)u, ret ); + } +}; + +template < typename T, typename E > class MultiplicationHelper< T, unsigned __int64, E, MultiplicationState_IntUint64 > +{ +public: + //T is signed up to 32-bit + static SafeIntError Multiply(T t, const unsigned __int64& u, T& ret) + { + __int32 tmp; + + if( LargeIntRegMultiply< __int32, unsigned __int64, E >::RegMultiply( (__int32)t, u, tmp ) == SafeIntNoError && + SafeCastHelper< T, __int32, E >::Cast( tmp, ret ) == SafeIntNoError ) + { + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class MultiplicationHelper<__int64, unsigned __int64, E, MultiplicationState_Int64Uint64> +{ +public: + //U is signed up to 32-bit + static SafeIntError Multiply( const __int64& t, const unsigned __int64& u, __int64& ret ) + { + return LargeIntRegMultiply< __int64, unsigned __int64, E >::RegMultiply( t, u, ret ); + } +}; + +template < typename T, typename E > class MultiplicationHelper< T, __int64, E, MultiplicationState_IntInt64> +{ +public: + //T is signed, up to 32-bit + static SafeIntError Multiply( T t, const __int64& u, T& ret ) + { + __int32 tmp; + + if( LargeIntRegMultiply< __int32, __int64, E >::RegMultiply( (__int32)t, u, tmp ) == SafeIntNoError && + SafeCastHelper< T, __int32, E >::Cast( tmp, ret ) == SafeIntNoError ) + { + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +enum DivisionState +{ + DivisionState_OK, + DivisionState_UnsignedSigned, + DivisionState_SignedUnsigned32, + DivisionState_SignedUnsigned64, + DivisionState_SignedUnsigned, + DivisionState_SignedSigned +}; + +template < typename T, typename U > class DivisionMethod +{ +public: + enum + { + method = (SafeIntCompare< T, U >::isBothUnsigned ? DivisionState_OK : + (!IntTraits< T >::isSigned && IntTraits< U >::isSigned) ? DivisionState_UnsignedSigned : + (IntTraits< T >::isSigned && + IntTraits< U >::isUint32 && + IntTraits< T >::isLT64Bit) ? DivisionState_SignedUnsigned32 : + (IntTraits< T >::isSigned && IntTraits< U >::isUint64) ? DivisionState_SignedUnsigned64 : + (IntTraits< T >::isSigned && !IntTraits< U >::isSigned) ? DivisionState_SignedUnsigned : + DivisionState_SignedSigned) + }; +}; + +template < typename T, typename U, typename E, int Method = DivisionMethod< T, U >::method > class DivisionHelper; + +template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_OK > +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + result = (T)( t/u ); + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_UnsignedSigned> +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) + { + if( u > 0 ) + { + result = (T)( t/u ); + return SafeIntNoError; + } + + if( u == 0 ) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U >::Abs( u ) > t ) + { + result = 0; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_SignedUnsigned32 > +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (__int64)t/(__int64)u ); + + return SafeIntNoError; + } +}; + +template < typename T, typename E > class DivisionHelper< T, unsigned __int64, E, DivisionState_SignedUnsigned64 > +{ +public: + static SafeIntError Divide( const T& t, const unsigned __int64& u, T& result ) + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + if( u <= (unsigned __int64)IntTraits< T >::maxInt ) + { + // Else u can safely be cast to T +#pragma warning(suppress:4127) + if( sizeof( T ) < sizeof( __int64 ) ) + result = (T)( (int)t/(int)u ); + else + result = (T)((__int64)t/(__int64)u); + } + else // Corner case + if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_SignedUnsigned> +{ +public: + // T is any signed, U is unsigned and smaller than 32-bit + // In this case, standard operator casting is correct + static SafeIntError Divide( const T& t, const U& u, T& result ) + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + result = (T)( t/u ); + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class DivisionHelper< T, U, E, DivisionState_SignedSigned> +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + return SafeIntDivideByZero; + } + + // Must test for corner case + if( t == IntTraits< T >::minInt && u == -1 ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + result = (T)( t/u ); + return SafeIntNoError; + } +}; + +enum AdditionState +{ + AdditionState_CastIntCheckMax, + AdditionState_CastUintCheckOverflow, + AdditionState_CastUintCheckOverflowMax, + AdditionState_CastUint64CheckOverflow, + AdditionState_CastUint64CheckOverflowMax, + AdditionState_CastIntCheckMinMax, + AdditionState_CastInt64CheckMinMax, + AdditionState_CastInt64CheckMax, + AdditionState_CastUint64CheckMinMax, + AdditionState_CastUint64CheckMinMax2, + AdditionState_CastInt64CheckOverflow, + AdditionState_CastInt64CheckOverflowMinMax, + AdditionState_CastInt64CheckOverflowMax, + AdditionState_ManualCheckInt64Uint64, + AdditionState_ManualCheck, + AdditionState_Error +}; + +template< typename T, typename U > +class AdditionMethod +{ +public: + enum + { + //unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Uint32_UintLT64) ? AdditionState_CastUintCheckOverflow : + (IntRegion< T,U >::IntZone_UintLT32_Uint32) ? AdditionState_CastUintCheckOverflowMax : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? AdditionState_CastUint64CheckOverflow : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? AdditionState_CastUint64CheckOverflowMax : + //unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? AdditionState_CastIntCheckMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? AdditionState_CastInt64CheckMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? AdditionState_CastUint64CheckMinMax : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? AdditionState_CastUint64CheckMinMax2 : + //signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? AdditionState_CastIntCheckMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? AdditionState_CastInt64CheckMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? AdditionState_CastInt64CheckOverflow : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? AdditionState_CastInt64CheckOverflowMinMax : + //signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? AdditionState_CastInt64CheckMax : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? AdditionState_CastInt64CheckOverflowMax : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? AdditionState_ManualCheckInt64Uint64 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? AdditionState_ManualCheck : + AdditionState_Error) + }; +}; + +template < typename T, typename U, typename E, int Method = AdditionMethod< T, U >::method > class AdditionHelper; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastIntCheckMax > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + //16-bit or less unsigned addition + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUintCheckOverflow > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return SafeIntNoError; + } + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUintCheckOverflowMax> +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckOverflow> +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckOverflowMax > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + //lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastIntCheckMinMax > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // 16-bit or less - one or both are signed + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +#pragma warning(push) +#pragma warning(disable:4702) +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckMinMax > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // 32-bit or less - one or both are signed + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + // return E::SafeIntOnOverflow2(); + } +}; +#pragma warning(pop) + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckMax > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // 32-bit or less - lhs signed, rhs unsigned + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckMinMax > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // lhs is unsigned __int64, rhs signed + unsigned __int64 tmp; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return SafeIntNoError; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastUint64CheckMinMax2> +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // lhs is unsigned and < 64-bit, rhs signed __int64 + if( rhs < 0 ) + { + if( lhs >= (unsigned __int64)( -rhs ) )//negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return SafeIntNoError; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckOverflow> +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // lhs is signed __int64, rhs signed + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + } + + result = (T)tmp; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckOverflowMinMax> +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + //rhs is signed __int64, lhs signed + __int64 tmp; + + if( AdditionHelper< __int64, __int64, E, AdditionState_CastInt64CheckOverflow >:: + Addition( (__int64)lhs, (__int64)rhs, tmp ) == SafeIntNoError && + tmp <= IntTraits< T >::maxInt && + tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_CastInt64CheckOverflowMax > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + //lhs is signed __int64, rhs unsigned < 64-bit + __int64 tmp = lhs + (__int64)rhs; + + if( tmp >= lhs ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class AdditionHelper < __int64, unsigned __int64, E, AdditionState_ManualCheckInt64Uint64 > +{ +public: + static SafeIntError Addition( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) throw() + { + // rhs is unsigned __int64, lhs __int64 + __int64 tmp = lhs + (__int64)rhs; + + if( tmp >= lhs ) + { + result = tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class AdditionHelper < T, U, E, AdditionState_ManualCheck > +{ +public: + static SafeIntError Addition( const T& lhs, const U& rhs, T& result ) + { + // rhs is unsigned __int64, lhs signed, 32-bit or less + + if( (unsigned __int32)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + __int32 tmp = (__int32)( (unsigned __int32)rhs + (unsigned __int32)lhs ); + + if( tmp >= lhs && + SafeCastHelper< T, __int32, E >::Cast( tmp, result ) == SafeIntNoError ) + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +enum SubtractionState +{ + SubtractionState_BothUnsigned, + SubtractionState_CastIntCheckMinMax, + SubtractionState_CastIntCheckMin, + SubtractionState_CastInt64CheckMinMax, + SubtractionState_CastInt64CheckMin, + SubtractionState_Uint64Int, + SubtractionState_UintInt64, + SubtractionState_Int64Int, + SubtractionState_IntInt64, + SubtractionState_Int64Uint, + SubtractionState_IntUint64, + SubtractionState_Int64Uint64, + // states for SubtractionMethod2 + SubtractionState_BothUnsigned2, + SubtractionState_CastIntCheckMinMax2, + SubtractionState_CastInt64CheckMinMax2, + SubtractionState_Uint64Int2, + SubtractionState_UintInt642, + SubtractionState_Int64Int2, + SubtractionState_IntInt642, + SubtractionState_Int64Uint2, + SubtractionState_IntUint642, + SubtractionState_Int64Uint642, + SubtractionState_Error +}; + +template < typename T, typename U > class SubtractionMethod +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMin : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMin : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint64 : + SubtractionState_Error) + }; +}; + +// this is for the case of U - SafeInt< T, E > +template < typename T, typename U > class SubtractionMethod2 +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned2 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax2 : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckMinMax2 : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int2 : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckMinMax2 : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckMinMax2 : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int2 : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt642 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMinMax2 : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMinMax2 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint2 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint642 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint642 : + SubtractionState_Error) + }; +}; + +template < typename T, typename U, typename E, int Method = SubtractionMethod< T, U >::method > class SubtractionHelper; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_BothUnsigned > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_BothUnsigned2 > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, U& result ) + { + // both are unsigned - easy case + // Except we do have to check for overflow - lhs could be larger than result can hold + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + return SafeCastHelper< U, T, E>::Cast( tmp, result); + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastIntCheckMinMax > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + if( SafeCastHelper< T, __int32, E >::Cast( tmp, result ) == SafeIntNoError ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_CastIntCheckMinMax2 > +{ +public: + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + return SafeCastHelper< T, __int32, E >::Cast( tmp, result ); + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastIntCheckMin > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + __int32 tmp = lhs - rhs; + + if( tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastInt64CheckMinMax > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + return SafeCastHelper< T, __int64, E >::Cast( tmp, result ); + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_CastInt64CheckMinMax2 > +{ +public: + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + return SafeCastHelper< T, __int64, E >::Cast( tmp, result ); + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_CastInt64CheckMin > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + __int64 tmp = (__int64)lhs - (__int64)rhs; + + if( tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_Uint64Int > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // lhs is an unsigned __int64, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (unsigned __int64)rhs ); + return SafeIntNoError; + } + } + else + { + // we're now effectively adding + T tmp = lhs + AbsValueHelper< U >::Abs( rhs ); + + if(tmp >= lhs) + { + result = tmp; + return SafeIntNoError; + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_Uint64Int2 > +{ +public: + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + // U is unsigned __int64, T is signed + if( rhs < 0 ) + { + // treat this as addition + unsigned __int64 tmp; + + tmp = lhs + (unsigned __int64)AbsValueHelper< T >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return SafeIntNoError; + } + else + { + // result is positive + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_UintInt64 > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // lhs is an unsigned int32 or smaller, rhs signed __int64 + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return SafeIntNoError; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + unsigned __int64 tmp = lhs + (unsigned __int64)( -rhs ); // negation safe + + // but we could exceed MaxInt + if(tmp <= IntTraits< T >::maxInt) + { + result = (T)tmp; + return SafeIntNoError; + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_UintInt642 > +{ +public: + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + // U unsigned 32-bit or less, T __int64 + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (__int64)lhs - rhs ); + return SafeIntNoError; + } + else + { + // we effectively have an addition + // which cannot overflow internally + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs ); + + if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_Int64Int > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // lhs is an __int64, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + __int64 tmp = lhs - rhs; + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + + result = (T)tmp; + return SafeIntNoError; + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_Int64Int2 > +{ +public: + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + // lhs __int64, rhs any signed int (including __int64) + __int64 tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast +#pragma warning(suppress:4127) + if( ( IntTraits< T >::isLT64Bit && tmp > IntTraits< T >::maxInt ) || + ( rhs < 0 && tmp < lhs ) ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + } + else + { + // lhs negative +#pragma warning(suppress:4127) + if( ( IntTraits< T >::isLT64Bit && tmp < IntTraits< T >::minInt) || + ( rhs >=0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } + } + + result = (T)tmp; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_IntInt64 > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // lhs is a 32-bit int or less, rhs __int64 + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + __int64 tmp = (__int64)lhs - rhs; + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + else + { + // fourth case + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_IntInt642 > +{ +public: + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + // lhs is any signed int32 or smaller, rhs is int64 + __int64 tmp = (__int64)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + //else OK + } + + result = (T)tmp; + return SafeIntNoError; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_Int64Uint > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + + __int64 tmp = lhs - (__int64)rhs; + + if( tmp <= lhs ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_Int64Uint2 > +{ +public: + // lhs is __int64, rhs is unsigned 32-bit or smaller + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + __int64 tmp = lhs - (__int64)rhs; + + if( tmp <= IntTraits< T >::maxInt && tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename T, typename U, typename E > class SubtractionHelper< T, U, E, SubtractionState_IntUint64 > +{ +public: + static SafeIntError Subtract( const T& lhs, const U& rhs, T& result ) + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of IntTraits< T >::minInt + // This will give it to us without extraneous compiler warnings + const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return SafeIntNoError; + } + } + else + { + if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) + { + result = (T)( lhs - rhs ); + return SafeIntNoError; + } + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename U, typename T, typename E > class SubtractionHelper< U, T, E, SubtractionState_IntUint642 > +{ +public: + static SafeIntError Subtract( const U& lhs, const T& rhs, T& result ) + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class SubtractionHelper< __int64, unsigned __int64, E, SubtractionState_Int64Uint64 > +{ +public: + static SafeIntError Subtract( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) + { + // if we subtract, and it gets larger, there's a problem + __int64 tmp = lhs - (__int64)rhs; + + if( tmp <= lhs ) + { + result = tmp; + return SafeIntNoError; + } + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +template < typename E > class SubtractionHelper< __int64, unsigned __int64, E, SubtractionState_Int64Uint642 > +{ +public: + // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it + // get smaller. If rhs > lhs, then it would also go negative, which is the other case + static SafeIntError Subtract( const __int64& lhs, const unsigned __int64& rhs, unsigned __int64& result ) + { + if( lhs >= 0 && (unsigned __int64)lhs >= rhs ) + { + result = (unsigned __int64)lhs - rhs; + return SafeIntNoError; + } + + E::SafeIntOnOverflow(); + return SafeIntArithmeticOverflow; + } +}; + +enum BinaryState +{ + BinaryState_OK, + BinaryState_Int8, + BinaryState_Int16, + BinaryState_Int32 +}; + +template < typename T, typename U > class BinaryMethod +{ +public: + enum + { + // If both operands are unsigned OR + // return type is smaller than rhs OR + // return type is larger and rhs is unsigned + // Then binary operations won't produce unexpected results + method = ( sizeof( T ) <= sizeof( U ) || + SafeIntCompare< T, U >::isBothUnsigned || + !IntTraits< U >::isSigned ) ? BinaryState_OK : + IntTraits< U >::isInt8 ? BinaryState_Int8 : + IntTraits< U >::isInt16 ? BinaryState_Int16 + : BinaryState_Int32 + }; +}; + +template < typename T, typename U, int Method = BinaryMethod< T, U >::method > class BinaryAndHelper; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK > +{ +public: + static T And( T lhs, U rhs ){ return (T)( lhs & rhs ); } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 > +{ +public: + static T And( T lhs, U rhs ) + { + // cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs & rhs ) == ( lhs & (unsigned __int8)rhs ) ); + return (T)( lhs & (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 > +{ +public: + static T And( T lhs, U rhs ) + { + //cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs & rhs ) == ( lhs & (unsigned __int16)rhs ) ); + return (T)( lhs & (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int32 > +{ +public: + static T And( T lhs, U rhs ) + { + //cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs & rhs ) == ( lhs & (unsigned __int32)rhs ) ); + return (T)( lhs & (unsigned __int32)rhs ); + } +}; + +template < typename T, typename U, int Method = BinaryMethod< T, U >::method > class BinaryOrHelper; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK > +{ +public: + static T Or( T lhs, U rhs ){ return (T)( lhs | rhs ); } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 > +{ +public: + static T Or( T lhs, U rhs ) + { + //cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs | rhs ) == ( lhs | (unsigned __int8)rhs ) ); + return (T)( lhs | (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int16 > +{ +public: + static T Or( T lhs, U rhs ) + { + //cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs | rhs ) == ( lhs | (unsigned __int16)rhs ) ); + return (T)( lhs | (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int32 > +{ +public: + static T Or( T lhs, U rhs ) + { + //cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs | rhs ) == ( lhs | (unsigned __int32)rhs ) ); + return (T)( lhs | (unsigned __int32)rhs ); + } +}; + +template <typename T, typename U, int Method = BinaryMethod< T, U >::method > class BinaryXorHelper; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK > +{ +public: + static T Xor( T lhs, U rhs ){ return (T)( lhs ^ rhs ); } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 > +{ +public: + static T Xor( T lhs, U rhs ) + { + // cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int8)rhs ) ); + return (T)( lhs ^ (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 > +{ +public: + static T Xor( T lhs, U rhs ) + { + // cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int16)rhs ) ); + return (T)( lhs ^ (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 > +{ +public: + static T Xor( T lhs, U rhs ) + { + // cast forces sign extension to be zeros + _SAFEINT_BINARY_ASSERT( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int32)rhs ) ); + return (T)( lhs ^ (unsigned __int32)rhs ); + } +}; + +#pragma warning(pop) +} // namespace details + +} // namespace utilities + +} // namespace msl + +#pragma pack(pop) diff --git a/test_data/lots_of_files/sal.h b/test_data/lots_of_files/sal.h new file mode 100644 index 0000000..ff8dfd8 --- /dev/null +++ b/test_data/lots_of_files/sal.h @@ -0,0 +1,2991 @@ +/*** +*sal.h - markers for documenting the semantics of APIs +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* sal.h provides a set of annotations to describe how a function uses its +* parameters - the assumptions it makes about them, and the guarantees it makes +* upon finishing. +* +* [Public] +* +****/ +#pragma once + +/*========================================================================== + + The comments in this file are intended to give basic understanding of + the usage of SAL, the Microsoft Source Code Annotation Language. + For more details, please see http://go.microsoft.com/fwlink/?LinkID=242134 + + The macros are defined in 3 layers, plus the structural set: + + _In_/_Out_/_Ret_ Layer: + ---------------------- + This layer provides the highest abstraction and its macros should be used + in most cases. These macros typically start with: + _In_ : input parameter to a function, unmodified by called function + _Out_ : output parameter, written to by called function, pointed-to + location not expected to be initialized prior to call + _Outptr_ : like _Out_ when returned variable is a pointer type + (so param is pointer-to-pointer type). Called function + provides/allocated space. + _Outref_ : like _Outptr_, except param is reference-to-pointer type. + _Inout_ : inout parameter, read from and potentially modified by + called function. + _Ret_ : for return values + _Field_ : class/struct field invariants + For common usage, this class of SAL provides the most concise annotations. + Note that _In_/_Out_/_Inout_/_Outptr_ annotations are designed to be used + with a parameter target. Using them with _At_ to specify non-parameter + targets may yield unexpected results. + + This layer also includes a number of other properties that can be specified + to extend the ability of code analysis, most notably: + -- Designating parameters as format strings for printf/scanf/scanf_s + -- Requesting stricter type checking for C enum parameters + + _Pre_/_Post_ Layer: + ------------------ + The macros of this layer only should be used when there is no suitable macro + in the _In_/_Out_ layer. Its macros start with _Pre_ or _Post_. + This layer provides the most flexibility for annotations. + + Implementation Abstraction Layer: + -------------------------------- + Macros from this layer should never be used directly. The layer only exists + to hide the implementation of the annotation macros. + + Structural Layer: + ---------------- + These annotations, like _At_ and _When_, are used with annotations from + any of the other layers as modifiers, indicating exactly when and where + the annotations apply. + + + Common syntactic conventions: + ---------------------------- + + Usage: + ----- + _In_, _Out_, _Inout_, _Pre_, _Post_, are for formal parameters. + _Ret_, _Deref_ret_ must be used for return values. + + Nullness: + -------- + If the parameter can be NULL as a precondition to the function, the + annotation contains _opt. If the macro does not contain '_opt' the + parameter cannot be NULL. + + If an out/inout parameter returns a null pointer as a postcondition, this is + indicated by _Ret_maybenull_ or _result_maybenull_. If the macro is not + of this form, then the result will not be NULL as a postcondition. + _Outptr_ - output value is not NULL + _Outptr_result_maybenull_ - output value might be NULL + + String Type: + ----------- + _z: NullTerminated string + for _In_ parameters the buffer must have the specified stringtype before the call + for _Out_ parameters the buffer must have the specified stringtype after the call + for _Inout_ parameters both conditions apply + + Extent Syntax: + ------------- + Buffer sizes are expressed as element counts, unless the macro explicitly + contains _byte_ or _bytes_. Some annotations specify two buffer sizes, in + which case the second is used to indicate how much of the buffer is valid + as a postcondition. This table outlines the precondition buffer allocation + size, precondition number of valid elements, postcondition allocation size, + and postcondition number of valid elements for representative buffer size + annotations: + Pre | Pre | Post | Post + alloc | valid | alloc | valid + Annotation elems | elems | elems | elems + ---------- ------------------------------------ + _In_reads_(s) s | s | s | s + _Inout_updates_(s) s | s | s | s + _Inout_updates_to_(s,c) s | s | s | c + _Out_writes_(s) s | 0 | s | s + _Out_writes_to_(s,c) s | 0 | s | c + _Outptr_result_buffer_(s) ? | ? | s | s + _Outptr_result_buffer_to_(s,c) ? | ? | s | c + + For the _Outptr_ annotations, the buffer in question is at one level of + dereference. The called function is responsible for supplying the buffer. + + Success and failure: + ------------------- + The SAL concept of success allows functions to define expressions that can + be tested by the caller, which if it evaluates to non-zero, indicates the + function succeeded, which means that its postconditions are guaranteed to + hold. Otherwise, if the expression evaluates to zero, the function is + considered to have failed, and the postconditions are not guaranteed. + + The success criteria can be specified with the _Success_(expr) annotation: + _Success_(return != FALSE) BOOL + PathCanonicalizeA(_Out_writes_(MAX_PATH) LPSTR pszBuf, LPCSTR pszPath) : + pszBuf is only guaranteed to be NULL-terminated when TRUE is returned, + and FALSE indiates failure. In common practice, callers check for zero + vs. non-zero returns, so it is preferable to express the success + criteria in terms of zero/non-zero, not checked for exactly TRUE. + + Functions can specify that some postconditions will still hold, even when + the function fails, using _On_failure_(anno-list), or postconditions that + hold regardless of success or failure using _Always_(anno-list). + + The annotation _Return_type_success_(expr) may be used with a typedef to + give a default _Success_ criteria to all functions returning that type. + This is the case for common Windows API status types, including + HRESULT and NTSTATUS. This may be overridden on a per-function basis by + specifying a _Success_ annotation locally. + +============================================================================*/ + +#define __ATTR_SAL + +#ifndef _SAL_VERSION +#define _SAL_VERSION 20 +#endif + +#ifndef __SAL_H_VERSION +#define __SAL_H_VERSION 180000000 +#endif + +#ifdef _PREFAST_ // [ + +// choose attribute or __declspec implementation +#ifndef _USE_DECLSPECS_FOR_SAL // [ +#define _USE_DECLSPECS_FOR_SAL 1 +#endif // ] + +#if _USE_DECLSPECS_FOR_SAL // [ +#undef _USE_ATTRIBUTES_FOR_SAL +#define _USE_ATTRIBUTES_FOR_SAL 0 +#elif !defined(_USE_ATTRIBUTES_FOR_SAL) // ][ +#if _MSC_VER >= 1400 // [ +#define _USE_ATTRIBUTES_FOR_SAL 1 +#else // ][ +#define _USE_ATTRIBUTES_FOR_SAL 0 +#endif // ] +#endif // ] + + +#if !_USE_DECLSPECS_FOR_SAL // [ +#if !_USE_ATTRIBUTES_FOR_SAL // [ +#if _MSC_VER >= 1400 // [ +#undef _USE_ATTRIBUTES_FOR_SAL +#define _USE_ATTRIBUTES_FOR_SAL 1 +#else // ][ +#undef _USE_DECLSPECS_FOR_SAL +#define _USE_DECLSPECS_FOR_SAL 1 +#endif // ] +#endif // ] +#endif // ] + +#else + +// Disable expansion of SAL macros in non-Prefast mode to +// improve compiler throughput. +#ifndef _USE_DECLSPECS_FOR_SAL // [ +#define _USE_DECLSPECS_FOR_SAL 0 +#endif // ] +#ifndef _USE_ATTRIBUTES_FOR_SAL // [ +#define _USE_ATTRIBUTES_FOR_SAL 0 +#endif // ] + +#endif // ] + +// safeguard for MIDL and RC builds +#if _USE_DECLSPECS_FOR_SAL && ( defined( MIDL_PASS ) || defined(__midl) || defined(RC_INVOKED) || !defined(_PREFAST_) ) // [ +#undef _USE_DECLSPECS_FOR_SAL +#define _USE_DECLSPECS_FOR_SAL 0 +#endif // ] +#if _USE_ATTRIBUTES_FOR_SAL && ( !defined(_MSC_EXTENSIONS) || defined( MIDL_PASS ) || defined(__midl) || defined(RC_INVOKED) ) // [ +#undef _USE_ATTRIBUTES_FOR_SAL +#define _USE_ATTRIBUTES_FOR_SAL 0 +#endif // ] + +#if _USE_DECLSPECS_FOR_SAL || _USE_ATTRIBUTES_FOR_SAL + +// Special enum type for Y/N/M +enum __SAL_YesNo {_SAL_notpresent, _SAL_no, _SAL_maybe, _SAL_yes, _SAL_default}; + +#endif + +#if defined(BUILD_WINDOWS) && !_USE_ATTRIBUTES_FOR_SAL +#define _SAL1_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "1") _GrouP_(annotes _SAL_nop_impl_) +#define _SAL1_1_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "1.1") _GrouP_(annotes _SAL_nop_impl_) +#define _SAL1_2_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "1.2") _GrouP_(annotes _SAL_nop_impl_) +#define _SAL2_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "2") _GrouP_(annotes _SAL_nop_impl_) + +#ifndef _SAL_L_Source_ +// Some annotations aren't officially SAL2 yet. +#define _SAL_L_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "2") _GrouP_(annotes _SAL_nop_impl_) +#endif +#else +#define _SAL1_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "1") _Group_(annotes _SAL_nop_impl_) +#define _SAL1_1_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "1.1") _Group_(annotes _SAL_nop_impl_) +#define _SAL1_2_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "1.2") _Group_(annotes _SAL_nop_impl_) +#define _SAL2_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "2") _Group_(annotes _SAL_nop_impl_) + +#ifndef _SAL_L_Source_ +// Some annotations aren't officially SAL2 yet. +#define _SAL_L_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "2") _Group_(annotes _SAL_nop_impl_) +#endif +#endif + + +//============================================================================ +// Structural SAL: +// These annotations modify the use of other annotations. They may +// express the annotation target (i.e. what parameter/field the annotation +// applies to) or the condition under which the annotation is applicable. +//============================================================================ + +// _At_(target, annos) specifies that the annotations listed in 'annos' is to +// be applied to 'target' rather than to the identifier which is the current +// lexical target. +#define _At_(target, annos) _At_impl_(target, annos _SAL_nop_impl_) + +// _At_buffer_(target, iter, bound, annos) is similar to _At_, except that +// target names a buffer, and each annotation in annos is applied to each +// element of target up to bound, with the variable named in iter usable +// by the annotations to refer to relevant offsets within target. +#define _At_buffer_(target, iter, bound, annos) _At_buffer_impl_(target, iter, bound, annos _SAL_nop_impl_) + +// _When_(expr, annos) specifies that the annotations listed in 'annos' only +// apply when 'expr' evaluates to non-zero. +#define _When_(expr, annos) _When_impl_(expr, annos _SAL_nop_impl_) +#define _Group_(annos) _Group_impl_(annos _SAL_nop_impl_) +#define _GrouP_(annos) _GrouP_impl_(annos _SAL_nop_impl_) + +// <expr> indicates whether normal post conditions apply to a function +#define _Success_(expr) _SAL2_Source_(_Success_, (expr), _Success_impl_(expr)) + +// <expr> indicates whether post conditions apply to a function returning +// the type that this annotation is applied to +#define _Return_type_success_(expr) _SAL2_Source_(_Return_type_success_, (expr), _Success_impl_(expr)) + +// Establish postconditions that apply only if the function does not succeed +#define _On_failure_(annos) _On_failure_impl_(annos _SAL_nop_impl_) + +// Establish postconditions that apply in both success and failure cases. +// Only applicable with functions that have _Success_ or _Return_type_succss_. +#define _Always_(annos) _Always_impl_(annos _SAL_nop_impl_) + +// Usable on a function defintion. Asserts that a function declaration is +// in scope, and its annotations are to be used. There are no other annotations +// allowed on the function definition. +#define _Use_decl_annotations_ _Use_decl_anno_impl_ + +// _Notref_ may precede a _Deref_ or "real" annotation, and removes one +// level of dereference if the parameter is a C++ reference (&). If the +// net deref on a "real" annotation is negative, it is simply discarded. +#define _Notref_ _Notref_impl_ + +// Annotations for defensive programming styles. +#define _Pre_defensive_ _SA_annotes0(SAL_pre_defensive) +#define _Post_defensive_ _SA_annotes0(SAL_post_defensive) + +#define _In_defensive_(annotes) _Pre_defensive_ _Group_(annotes) +#define _Out_defensive_(annotes) _Post_defensive_ _Group_(annotes) +#define _Inout_defensive_(annotes) _Pre_defensive_ _Post_defensive_ _Group_(annotes) + +//============================================================================ +// _In_\_Out_ Layer: +//============================================================================ + +// Reserved pointer parameters, must always be NULL. +#define _Reserved_ _SAL2_Source_(_Reserved_, (), _Pre1_impl_(__null_impl)) + +// _Const_ allows specification that any namable memory location is considered +// readonly for a given call. +#define _Const_ _SAL2_Source_(_Const_, (), _Pre1_impl_(__readaccess_impl_notref)) + + +// Input parameters -------------------------- + +// _In_ - Annotations for parameters where data is passed into the function, but not modified. +// _In_ by itself can be used with non-pointer types (although it is redundant). + +// e.g. void SetPoint( _In_ const POINT* pPT ); +#define _In_ _SAL2_Source_(_In_, (), _Pre1_impl_(__notnull_impl_notref) _Pre_valid_impl_ _Deref_pre1_impl_(__readaccess_impl_notref)) +#define _In_opt_ _SAL2_Source_(_In_opt_, (), _Pre1_impl_(__maybenull_impl_notref) _Pre_valid_impl_ _Deref_pre_readonly_) + +// nullterminated 'in' parameters. +// e.g. void CopyStr( _In_z_ const char* szFrom, _Out_z_cap_(cchTo) char* szTo, size_t cchTo ); +#define _In_z_ _SAL2_Source_(_In_z_, (), _In_ _Pre1_impl_(__zterm_impl)) +#define _In_opt_z_ _SAL2_Source_(_In_opt_z_, (), _In_opt_ _Pre1_impl_(__zterm_impl)) + + +// 'input' buffers with given size + +#define _In_reads_(size) _SAL2_Source_(_In_reads_, (size), _Pre_count_(size) _Deref_pre_readonly_) +#define _In_reads_opt_(size) _SAL2_Source_(_In_reads_opt_, (size), _Pre_opt_count_(size) _Deref_pre_readonly_) +#define _In_reads_bytes_(size) _SAL2_Source_(_In_reads_bytes_, (size), _Pre_bytecount_(size) _Deref_pre_readonly_) +#define _In_reads_bytes_opt_(size) _SAL2_Source_(_In_reads_bytes_opt_, (size), _Pre_opt_bytecount_(size) _Deref_pre_readonly_) +#define _In_reads_z_(size) _SAL2_Source_(_In_reads_z_, (size), _In_reads_(size) _Pre_z_) +#define _In_reads_opt_z_(size) _SAL2_Source_(_In_reads_opt_z_, (size), _Pre_opt_count_(size) _Deref_pre_readonly_ _Pre_opt_z_) +#define _In_reads_or_z_(size) _SAL2_Source_(_In_reads_or_z_, (size), _In_ _When_(_String_length_(_Curr_) < (size), _Pre_z_) _When_(_String_length_(_Curr_) >= (size), _Pre1_impl_(__count_impl(size)))) +#define _In_reads_or_z_opt_(size) _SAL2_Source_(_In_reads_or_z_opt_, (size), _In_opt_ _When_(_String_length_(_Curr_) < (size), _Pre_z_) _When_(_String_length_(_Curr_) >= (size), _Pre1_impl_(__count_impl(size)))) + + +// 'input' buffers valid to the given end pointer + +#define _In_reads_to_ptr_(ptr) _SAL2_Source_(_In_reads_to_ptr_, (ptr), _Pre_ptrdiff_count_(ptr) _Deref_pre_readonly_) +#define _In_reads_to_ptr_opt_(ptr) _SAL2_Source_(_In_reads_to_ptr_opt_, (ptr), _Pre_opt_ptrdiff_count_(ptr) _Deref_pre_readonly_) +#define _In_reads_to_ptr_z_(ptr) _SAL2_Source_(_In_reads_to_ptr_z_, (ptr), _In_reads_to_ptr_(ptr) _Pre_z_) +#define _In_reads_to_ptr_opt_z_(ptr) _SAL2_Source_(_In_reads_to_ptr_opt_z_, (ptr), _Pre_opt_ptrdiff_count_(ptr) _Deref_pre_readonly_ _Pre_opt_z_) + + + +// Output parameters -------------------------- + +// _Out_ - Annotations for pointer or reference parameters where data passed back to the caller. +// These are mostly used where the pointer/reference is to a non-pointer type. +// _Outptr_/_Outref) (see below) are typically used to return pointers via parameters. + +// e.g. void GetPoint( _Out_ POINT* pPT ); +#define _Out_ _SAL2_Source_(_Out_, (), _Out_impl_) +#define _Out_opt_ _SAL2_Source_(_Out_opt_, (), _Out_opt_impl_) + +#define _Out_writes_(size) _SAL2_Source_(_Out_writes_, (size), _Pre_cap_(size) _Post_valid_impl_) +#define _Out_writes_opt_(size) _SAL2_Source_(_Out_writes_opt_, (size), _Pre_opt_cap_(size) _Post_valid_impl_) +#define _Out_writes_bytes_(size) _SAL2_Source_(_Out_writes_bytes_, (size), _Pre_bytecap_(size) _Post_valid_impl_) +#define _Out_writes_bytes_opt_(size) _SAL2_Source_(_Out_writes_bytes_opt_, (size), _Pre_opt_bytecap_(size) _Post_valid_impl_) +#define _Out_writes_z_(size) _SAL2_Source_(_Out_writes_z_, (size), _Pre_cap_(size) _Post_valid_impl_ _Post_z_) +#define _Out_writes_opt_z_(size) _SAL2_Source_(_Out_writes_opt_z_, (size), _Pre_opt_cap_(size) _Post_valid_impl_ _Post_z_) + +#define _Out_writes_to_(size,count) _SAL2_Source_(_Out_writes_to_, (size,count), _Pre_cap_(size) _Post_valid_impl_ _Post_count_(count)) +#define _Out_writes_to_opt_(size,count) _SAL2_Source_(_Out_writes_to_opt_, (size,count), _Pre_opt_cap_(size) _Post_valid_impl_ _Post_count_(count)) +#define _Out_writes_all_(size) _SAL2_Source_(_Out_writes_all_, (size), _Out_writes_to_(_Old_(size), _Old_(size))) +#define _Out_writes_all_opt_(size) _SAL2_Source_(_Out_writes_all_opt_, (size), _Out_writes_to_opt_(_Old_(size), _Old_(size))) + +#define _Out_writes_bytes_to_(size,count) _SAL2_Source_(_Out_writes_bytes_to_, (size,count), _Pre_bytecap_(size) _Post_valid_impl_ _Post_bytecount_(count)) +#define _Out_writes_bytes_to_opt_(size,count) _SAL2_Source_(_Out_writes_bytes_to_opt_, (size,count), _Pre_opt_bytecap_(size) _Post_valid_impl_ _Post_bytecount_(count)) +#define _Out_writes_bytes_all_(size) _SAL2_Source_(_Out_writes_bytes_all_, (size), _Out_writes_bytes_to_(_Old_(size), _Old_(size))) +#define _Out_writes_bytes_all_opt_(size) _SAL2_Source_(_Out_writes_bytes_all_opt_, (size), _Out_writes_bytes_to_opt_(_Old_(size), _Old_(size))) + +#define _Out_writes_to_ptr_(ptr) _SAL2_Source_(_Out_writes_to_ptr_, (ptr), _Pre_ptrdiff_cap_(ptr) _Post_valid_impl_) +#define _Out_writes_to_ptr_opt_(ptr) _SAL2_Source_(_Out_writes_to_ptr_opt_, (ptr), _Pre_opt_ptrdiff_cap_(ptr) _Post_valid_impl_) +#define _Out_writes_to_ptr_z_(ptr) _SAL2_Source_(_Out_writes_to_ptr_z_, (ptr), _Pre_ptrdiff_cap_(ptr) _Post_valid_impl_ Post_z_) +#define _Out_writes_to_ptr_opt_z_(ptr) _SAL2_Source_(_Out_writes_to_ptr_opt_z_, (ptr), _Pre_opt_ptrdiff_cap_(ptr) _Post_valid_impl_ Post_z_) + + +// Inout parameters ---------------------------- + +// _Inout_ - Annotations for pointer or reference parameters where data is passed in and +// potentially modified. +// void ModifyPoint( _Inout_ POINT* pPT ); +// void ModifyPointByRef( _Inout_ POINT& pPT ); + +#define _Inout_ _SAL2_Source_(_Inout_, (), _Prepost_valid_) +#define _Inout_opt_ _SAL2_Source_(_Inout_opt_, (), _Prepost_opt_valid_) + +// For modifying string buffers +// void toupper( _Inout_z_ char* sz ); +#define _Inout_z_ _SAL2_Source_(_Inout_z_, (), _Prepost_z_) +#define _Inout_opt_z_ _SAL2_Source_(_Inout_opt_z_, (), _Prepost_opt_z_) + +// For modifying buffers with explicit element size +#define _Inout_updates_(size) _SAL2_Source_(_Inout_updates_, (size), _Pre_cap_(size) _Pre_valid_impl_ _Post_valid_impl_) +#define _Inout_updates_opt_(size) _SAL2_Source_(_Inout_updates_opt_, (size), _Pre_opt_cap_(size) _Pre_valid_impl_ _Post_valid_impl_) +#define _Inout_updates_z_(size) _SAL2_Source_(_Inout_updates_z_, (size), _Pre_cap_(size) _Pre_valid_impl_ _Post_valid_impl_ _Pre1_impl_(__zterm_impl) _Post1_impl_(__zterm_impl)) +#define _Inout_updates_opt_z_(size) _SAL2_Source_(_Inout_updates_opt_z_, (size), _Pre_opt_cap_(size) _Pre_valid_impl_ _Post_valid_impl_ _Pre1_impl_(__zterm_impl) _Post1_impl_(__zterm_impl)) + +#define _Inout_updates_to_(size,count) _SAL2_Source_(_Inout_updates_to_, (size,count), _Out_writes_to_(size,count) _Pre_valid_impl_ _Pre1_impl_(__count_impl(size))) +#define _Inout_updates_to_opt_(size,count) _SAL2_Source_(_Inout_updates_to_opt_, (size,count), _Out_writes_to_opt_(size,count) _Pre_valid_impl_ _Pre1_impl_(__count_impl(size))) + +#define _Inout_updates_all_(size) _SAL2_Source_(_Inout_updates_all_, (size), _Inout_updates_to_(_Old_(size), _Old_(size))) +#define _Inout_updates_all_opt_(size) _SAL2_Source_(_Inout_updates_all_opt_, (size), _Inout_updates_to_opt_(_Old_(size), _Old_(size))) + +// For modifying buffers with explicit byte size +#define _Inout_updates_bytes_(size) _SAL2_Source_(_Inout_updates_bytes_, (size), _Pre_bytecap_(size) _Pre_valid_impl_ _Post_valid_impl_) +#define _Inout_updates_bytes_opt_(size) _SAL2_Source_(_Inout_updates_bytes_opt_, (size), _Pre_opt_bytecap_(size) _Pre_valid_impl_ _Post_valid_impl_) + +#define _Inout_updates_bytes_to_(size,count) _SAL2_Source_(_Inout_updates_bytes_to_, (size,count), _Out_writes_bytes_to_(size,count) _Pre_valid_impl_ _Pre1_impl_(__bytecount_impl(size))) +#define _Inout_updates_bytes_to_opt_(size,count) _SAL2_Source_(_Inout_updates_bytes_to_opt_, (size,count), _Out_writes_bytes_to_opt_(size,count) _Pre_valid_impl_ _Pre1_impl_(__bytecount_impl(size))) + +#define _Inout_updates_bytes_all_(size) _SAL2_Source_(_Inout_updates_bytes_all_, (size), _Inout_updates_bytes_to_(_Old_(size), _Old_(size))) +#define _Inout_updates_bytes_all_opt_(size) _SAL2_Source_(_Inout_updates_bytes_all_opt_, (size), _Inout_updates_bytes_to_opt_(_Old_(size), _Old_(size))) + + +// Pointer to pointer parameters ------------------------- + +// _Outptr_ - Annotations for output params returning pointers +// These describe parameters where the called function provides the buffer: +// HRESULT SHStrDupW(_In_ LPCWSTR psz, _Outptr_ LPWSTR *ppwsz); +// The caller passes the address of an LPWSTR variable as ppwsz, and SHStrDupW allocates +// and initializes memory and returns the pointer to the new LPWSTR in *ppwsz. +// +// _Outptr_opt_ - describes parameters that are allowed to be NULL. +// _Outptr_*_result_maybenull_ - describes parameters where the called function might return NULL to the caller. +// +// Example: +// void MyFunc(_Outptr_opt_ int **ppData1, _Outptr_result_maybenull_ int **ppData2); +// Callers: +// MyFunc(NULL, NULL); // error: parameter 2, ppData2, should not be NULL +// MyFunc(&pData1, &pData2); // ok: both non-NULL +// if (*pData1 == *pData2) ... // error: pData2 might be NULL after call + +#define _Outptr_ _SAL2_Source_(_Outptr_, (), _Out_impl_ _Deref_post2_impl_(__notnull_impl_notref, __count_impl(1))) +#define _Outptr_result_maybenull_ _SAL2_Source_(_Outptr_result_maybenull_, (), _Out_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __count_impl(1))) +#define _Outptr_opt_ _SAL2_Source_(_Outptr_opt_, (), _Out_opt_impl_ _Deref_post2_impl_(__notnull_impl_notref, __count_impl(1))) +#define _Outptr_opt_result_maybenull_ _SAL2_Source_(_Outptr_opt_result_maybenull_, (), _Out_opt_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __count_impl(1))) + +// Annotations for _Outptr_ parameters returning pointers to null terminated strings. + +#define _Outptr_result_z_ _SAL2_Source_(_Outptr_result_z_, (), _Out_impl_ _Deref_post_z_) +#define _Outptr_opt_result_z_ _SAL2_Source_(_Outptr_opt_result_z_, (), _Out_opt_impl_ _Deref_post_z_) +#define _Outptr_result_maybenull_z_ _SAL2_Source_(_Outptr_result_maybenull_z_, (), _Out_impl_ _Deref_post_opt_z_) +#define _Outptr_opt_result_maybenull_z_ _SAL2_Source_(_Outptr_opt_result_maybenull_z_, (), _Out_opt_impl_ _Deref_post_opt_z_) + +// Annotations for _Outptr_ parameters where the output pointer is set to NULL if the function fails. + +#define _Outptr_result_nullonfailure_ _SAL2_Source_(_Outptr_result_nullonfailure_, (), _Outptr_ _On_failure_(_Deref_post_null_)) +#define _Outptr_opt_result_nullonfailure_ _SAL2_Source_(_Outptr_opt_result_nullonfailure_, (), _Outptr_opt_ _On_failure_(_Deref_post_null_)) + +// Annotations for _Outptr_ parameters which return a pointer to a ref-counted COM object, +// following the COM convention of setting the output to NULL on failure. +// The current implementation is identical to _Outptr_result_nullonfailure_. +// For pointers to types that are not COM objects, _Outptr_result_nullonfailure_ is preferred. + +#define _COM_Outptr_ _SAL2_Source_(_COM_Outptr_, (), _Outptr_ _On_failure_(_Deref_post_null_)) +#define _COM_Outptr_result_maybenull_ _SAL2_Source_(_COM_Outptr_result_maybenull_, (), _Outptr_result_maybenull_ _On_failure_(_Deref_post_null_)) +#define _COM_Outptr_opt_ _SAL2_Source_(_COM_Outptr_opt_, (), _Outptr_opt_ _On_failure_(_Deref_post_null_)) +#define _COM_Outptr_opt_result_maybenull_ _SAL2_Source_(_COM_Outptr_opt_result_maybenull_, (), _Outptr_opt_result_maybenull_ _On_failure_(_Deref_post_null_)) + +// Annotations for _Outptr_ parameters returning a pointer to buffer with a specified number of elements/bytes + +#define _Outptr_result_buffer_(size) _SAL2_Source_(_Outptr_result_buffer_, (size), _Out_impl_ _Deref_post2_impl_(__notnull_impl_notref, __cap_impl(size))) +#define _Outptr_opt_result_buffer_(size) _SAL2_Source_(_Outptr_opt_result_buffer_, (size), _Out_opt_impl_ _Deref_post2_impl_(__notnull_impl_notref, __cap_impl(size))) +#define _Outptr_result_buffer_to_(size, count) _SAL2_Source_(_Outptr_result_buffer_to_, (size, count), _Out_impl_ _Deref_post3_impl_(__notnull_impl_notref, __cap_impl(size), __count_impl(count))) +#define _Outptr_opt_result_buffer_to_(size, count) _SAL2_Source_(_Outptr_opt_result_buffer_to_, (size, count), _Out_opt_impl_ _Deref_post3_impl_(__notnull_impl_notref, __cap_impl(size), __count_impl(count))) + +#define _Outptr_result_buffer_all_(size) _SAL2_Source_(_Outptr_result_buffer_all_, (size), _Out_impl_ _Deref_post2_impl_(__notnull_impl_notref, __count_impl(size))) +#define _Outptr_opt_result_buffer_all_(size) _SAL2_Source_(_Outptr_opt_result_buffer_all_, (size), _Out_opt_impl_ _Deref_post2_impl_(__notnull_impl_notref, __count_impl(size))) + +#define _Outptr_result_buffer_maybenull_(size) _SAL2_Source_(_Outptr_result_buffer_maybenull_, (size), _Out_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __cap_impl(size))) +#define _Outptr_opt_result_buffer_maybenull_(size) _SAL2_Source_(_Outptr_opt_result_buffer_maybenull_, (size), _Out_opt_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __cap_impl(size))) +#define _Outptr_result_buffer_to_maybenull_(size, count) _SAL2_Source_(_Outptr_result_buffer_to_maybenull_, (size, count), _Out_impl_ _Deref_post3_impl_(__maybenull_impl_notref, __cap_impl(size), __count_impl(count))) +#define _Outptr_opt_result_buffer_to_maybenull_(size, count) _SAL2_Source_(_Outptr_opt_result_buffer_to_maybenull_, (size, count), _Out_opt_impl_ _Deref_post3_impl_(__maybenull_impl_notref, __cap_impl(size), __count_impl(count))) + +#define _Outptr_result_buffer_all_maybenull_(size) _SAL2_Source_(_Outptr_result_buffer_all_maybenull_, (size), _Out_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __count_impl(size))) +#define _Outptr_opt_result_buffer_all_maybenull_(size) _SAL2_Source_(_Outptr_opt_result_buffer_all_maybenull_, (size), _Out_opt_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __count_impl(size))) + +#define _Outptr_result_bytebuffer_(size) _SAL2_Source_(_Outptr_result_bytebuffer_, (size), _Out_impl_ _Deref_post2_impl_(__notnull_impl_notref, __bytecap_impl(size))) +#define _Outptr_opt_result_bytebuffer_(size) _SAL2_Source_(_Outptr_opt_result_bytebuffer_, (size), _Out_opt_impl_ _Deref_post2_impl_(__notnull_impl_notref, __bytecap_impl(size))) +#define _Outptr_result_bytebuffer_to_(size, count) _SAL2_Source_(_Outptr_result_bytebuffer_to_, (size, count), _Out_impl_ _Deref_post3_impl_(__notnull_impl_notref, __bytecap_impl(size), __bytecount_impl(count))) +#define _Outptr_opt_result_bytebuffer_to_(size, count) _SAL2_Source_(_Outptr_opt_result_bytebuffer_to_, (size, count), _Out_opt_impl_ _Deref_post3_impl_(__notnull_impl_notref, __bytecap_impl(size), __bytecount_impl(count))) + +#define _Outptr_result_bytebuffer_all_(size) _SAL2_Source_(_Outptr_result_bytebuffer_all_, (size), _Out_impl_ _Deref_post2_impl_(__notnull_impl_notref, __bytecount_impl(size))) +#define _Outptr_opt_result_bytebuffer_all_(size) _SAL2_Source_(_Outptr_opt_result_bytebuffer_all_, (size), _Out_opt_impl_ _Deref_post2_impl_(__notnull_impl_notref, __bytecount_impl(size))) + +#define _Outptr_result_bytebuffer_maybenull_(size) _SAL2_Source_(_Outptr_result_bytebuffer_maybenull_, (size), _Out_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __bytecap_impl(size))) +#define _Outptr_opt_result_bytebuffer_maybenull_(size) _SAL2_Source_(_Outptr_opt_result_bytebuffer_maybenull_, (size), _Out_opt_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __bytecap_impl(size))) +#define _Outptr_result_bytebuffer_to_maybenull_(size, count) _SAL2_Source_(_Outptr_result_bytebuffer_to_maybenull_, (size, count), _Out_impl_ _Deref_post3_impl_(__maybenull_impl_notref, __bytecap_impl(size), __bytecount_impl(count))) +#define _Outptr_opt_result_bytebuffer_to_maybenull_(size, count) _SAL2_Source_(_Outptr_opt_result_bytebuffer_to_maybenull_, (size, count), _Out_opt_impl_ _Deref_post3_impl_(__maybenull_impl_notref, __bytecap_impl(size), __bytecount_impl(count))) + +#define _Outptr_result_bytebuffer_all_maybenull_(size) _SAL2_Source_(_Outptr_result_bytebuffer_all_maybenull_, (size), _Out_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __bytecount_impl(size))) +#define _Outptr_opt_result_bytebuffer_all_maybenull_(size) _SAL2_Source_(_Outptr_opt_result_bytebuffer_all_maybenull_, (size), _Out_opt_impl_ _Deref_post2_impl_(__maybenull_impl_notref, __bytecount_impl(size))) + +// Annotations for output reference to pointer parameters. + +#define _Outref_ _SAL2_Source_(_Outref_, (), _Out_impl_ _Post_notnull_) +#define _Outref_result_maybenull_ _SAL2_Source_(_Outref_result_maybenull_, (), _Pre2_impl_(__notnull_impl_notref, __cap_c_one_notref_impl) _Post_maybenull_ _Post_valid_impl_) + +#define _Outref_result_buffer_(size) _SAL2_Source_(_Outref_result_buffer_, (size), _Outref_ _Post1_impl_(__cap_impl(size))) +#define _Outref_result_bytebuffer_(size) _SAL2_Source_(_Outref_result_bytebuffer_, (size), _Outref_ _Post1_impl_(__bytecap_impl(size))) +#define _Outref_result_buffer_to_(size, count) _SAL2_Source_(_Outref_result_buffer_to_, (size, count), _Outref_result_buffer_(size) _Post1_impl_(__count_impl(count))) +#define _Outref_result_bytebuffer_to_(size, count) _SAL2_Source_(_Outref_result_bytebuffer_to_, (size, count), _Outref_result_bytebuffer_(size) _Post1_impl_(__bytecount_impl(count))) +#define _Outref_result_buffer_all_(size) _SAL2_Source_(_Outref_result_buffer_all_, (size), _Outref_result_buffer_to_(size, _Old_(size))) +#define _Outref_result_bytebuffer_all_(size) _SAL2_Source_(_Outref_result_bytebuffer_all_, (size), _Outref_result_bytebuffer_to_(size, _Old_(size))) + +#define _Outref_result_buffer_maybenull_(size) _SAL2_Source_(_Outref_result_buffer_maybenull_, (size), _Outref_result_maybenull_ _Post1_impl_(__cap_impl(size))) +#define _Outref_result_bytebuffer_maybenull_(size) _SAL2_Source_(_Outref_result_bytebuffer_maybenull_, (size), _Outref_result_maybenull_ _Post1_impl_(__bytecap_impl(size))) +#define _Outref_result_buffer_to_maybenull_(size, count) _SAL2_Source_(_Outref_result_buffer_to_maybenull_, (size, count), _Outref_result_buffer_maybenull_(size) _Post1_impl_(__count_impl(count))) +#define _Outref_result_bytebuffer_to_maybenull_(size, count) _SAL2_Source_(_Outref_result_bytebuffer_to_maybenull_, (size, count), _Outref_result_bytebuffer_maybenull_(size) _Post1_impl_(__bytecount_impl(count))) +#define _Outref_result_buffer_all_maybenull_(size) _SAL2_Source_(_Outref_result_buffer_all_maybenull_, (size), _Outref_result_buffer_to_maybenull_(size, _Old_(size))) +#define _Outref_result_bytebuffer_all_maybenull_(size) _SAL2_Source_(_Outref_result_bytebuffer_all_maybenull_, (size), _Outref_result_bytebuffer_to_maybenull_(size, _Old_(size))) + +// Annotations for output reference to pointer parameters that guarantee +// that the pointer is set to NULL on failure. +#define _Outref_result_nullonfailure_ _SAL2_Source_(_Outref_result_nullonfailure_, (), _Outref_ _On_failure_(_Post_null_)) + +// Generic annotations to set output value of a by-pointer or by-reference parameter to null/zero on failure. +#define _Result_nullonfailure_ _SAL2_Source_(_Result_nullonfailure_, (), _On_failure_(_Notref_impl_ _Deref_impl_ _Post_null_)) +#define _Result_zeroonfailure_ _SAL2_Source_(_Result_zeroonfailure_, (), _On_failure_(_Notref_impl_ _Deref_impl_ _Out_range_(==, 0))) + + +// return values ------------------------------- + +// +// _Ret_ annotations +// +// describing conditions that hold for return values after the call + +// e.g. _Ret_z_ CString::operator const wchar_t*() const throw(); +#define _Ret_z_ _SAL2_Source_(_Ret_z_, (), _Ret2_impl_(__notnull_impl, __zterm_impl) _Ret_valid_impl_) +#define _Ret_maybenull_z_ _SAL2_Source_(_Ret_maybenull_z_, (), _Ret2_impl_(__maybenull_impl,__zterm_impl) _Ret_valid_impl_) + +// used with allocated but not yet initialized objects +#define _Ret_notnull_ _SAL2_Source_(_Ret_notnull_, (), _Ret1_impl_(__notnull_impl)) +#define _Ret_maybenull_ _SAL2_Source_(_Ret_maybenull_, (), _Ret1_impl_(__maybenull_impl)) +#define _Ret_null_ _SAL2_Source_(_Ret_null_, (), _Ret1_impl_(__null_impl)) + +// used with allocated and initialized objects +// returns single valid object +#define _Ret_valid_ _SAL2_Source_(_Ret_valid_, (), _Ret1_impl_(__notnull_impl_notref) _Ret_valid_impl_) + +// returns pointer to initialized buffer of specified size +#define _Ret_writes_(size) _SAL2_Source_(_Ret_writes_, (size), _Ret2_impl_(__notnull_impl, __count_impl(size)) _Ret_valid_impl_) +#define _Ret_writes_z_(size) _SAL2_Source_(_Ret_writes_z_, (size), _Ret3_impl_(__notnull_impl, __count_impl(size), __zterm_impl) _Ret_valid_impl_) +#define _Ret_writes_bytes_(size) _SAL2_Source_(_Ret_writes_bytes_, (size), _Ret2_impl_(__notnull_impl, __bytecount_impl(size)) _Ret_valid_impl_) +#define _Ret_writes_maybenull_(size) _SAL2_Source_(_Ret_writes_maybenull_, (size), _Ret2_impl_(__maybenull_impl,__count_impl(size)) _Ret_valid_impl_) +#define _Ret_writes_maybenull_z_(size) _SAL2_Source_(_Ret_writes_maybenull_z_, (size), _Ret3_impl_(__maybenull_impl,__count_impl(size),__zterm_impl) _Ret_valid_impl_) +#define _Ret_writes_bytes_maybenull_(size) _SAL2_Source_(_Ret_writes_bytes_maybenull_, (size), _Ret2_impl_(__maybenull_impl,__bytecount_impl(size)) _Ret_valid_impl_) + +// returns pointer to partially initialized buffer, with total size 'size' and initialized size 'count' +#define _Ret_writes_to_(size,count) _SAL2_Source_(_Ret_writes_to_, (size,count), _Ret3_impl_(__notnull_impl, __cap_impl(size), __count_impl(count)) _Ret_valid_impl_) +#define _Ret_writes_bytes_to_(size,count) _SAL2_Source_(_Ret_writes_bytes_to_, (size,count), _Ret3_impl_(__notnull_impl, __bytecap_impl(size), __bytecount_impl(count)) _Ret_valid_impl_) +#define _Ret_writes_to_maybenull_(size,count) _SAL2_Source_(_Ret_writes_to_maybenull_, (size,count), _Ret3_impl_(__maybenull_impl, __cap_impl(size), __count_impl(count)) _Ret_valid_impl_) +#define _Ret_writes_bytes_to_maybenull_(size,count) _SAL2_Source_(_Ret_writes_bytes_to_maybenull_, (size,count), _Ret3_impl_(__maybenull_impl, __bytecap_impl(size), __bytecount_impl(count)) _Ret_valid_impl_) + + +// Annotations for strict type checking +#define _Points_to_data_ _SAL2_Source_(_Points_to_data_, (), _Pre_ _Points_to_data_impl_) +#define _Literal_ _SAL2_Source_(_Literal_, (), _Pre_ _Literal_impl_) +#define _Notliteral_ _SAL2_Source_(_Notliteral_, (), _Pre_ _Notliteral_impl_) + +// Check the return value of a function e.g. _Check_return_ ErrorCode Foo(); +#define _Check_return_ _SAL2_Source_(_Check_return_, (), _Check_return_impl_) +#define _Must_inspect_result_ _SAL2_Source_(_Must_inspect_result_, (), _Must_inspect_impl_ _Check_return_impl_) + +// e.g. MyPrintF( _Printf_format_string_ const wchar_t* wzFormat, ... ); +#define _Printf_format_string_ _SAL2_Source_(_Printf_format_string_, (), _Printf_format_string_impl_) +#define _Scanf_format_string_ _SAL2_Source_(_Scanf_format_string_, (), _Scanf_format_string_impl_) +#define _Scanf_s_format_string_ _SAL2_Source_(_Scanf_s_format_string_, (), _Scanf_s_format_string_impl_) + +#define _Format_string_impl_(kind,where) _SA_annotes2(SAL_IsFormatString2, kind, where) +#define _Printf_format_string_params_(x) _SAL2_Source_(_Printf_format_string_params_, (x), _Format_string_impl_("printf", x)) +#define _Scanf_format_string_params_(x) _SAL2_Source_(_Scanf_format_string_params_, (x), _Format_string_impl_("scanf", x)) +#define _Scanf_s_format_string_params_(x) _SAL2_Source_(_Scanf_s_format_string_params_, (x), _Format_string_impl_("scanf_s", x)) + +// annotations to express value of integral or pointer parameter +#define _In_range_(lb,ub) _SAL2_Source_(_In_range_, (lb,ub), _In_range_impl_(lb,ub)) +#define _Out_range_(lb,ub) _SAL2_Source_(_Out_range_, (lb,ub), _Out_range_impl_(lb,ub)) +#define _Ret_range_(lb,ub) _SAL2_Source_(_Ret_range_, (lb,ub), _Ret_range_impl_(lb,ub)) +#define _Deref_in_range_(lb,ub) _SAL2_Source_(_Deref_in_range_, (lb,ub), _Deref_in_range_impl_(lb,ub)) +#define _Deref_out_range_(lb,ub) _SAL2_Source_(_Deref_out_range_, (lb,ub), _Deref_out_range_impl_(lb,ub)) +#define _Deref_ret_range_(lb,ub) _SAL2_Source_(_Deref_ret_range_, (lb,ub), _Deref_ret_range_impl_(lb,ub)) +#define _Pre_equal_to_(expr) _SAL2_Source_(_Pre_equal_to_, (expr), _In_range_(==, expr)) +#define _Post_equal_to_(expr) _SAL2_Source_(_Post_equal_to_, (expr), _Out_range_(==, expr)) + +// annotation to express that a value (usually a field of a mutable class) +// is not changed by a function call +#define _Unchanged_(e) _SAL2_Source_(_Unchanged_, (e), _At_(e, _Post_equal_to_(_Old_(e)) _Const_)) + +// Annotations to allow expressing generalized pre and post conditions. +// 'cond' may be any valid SAL expression that is considered to be true as a precondition +// or postcondition (respsectively). +#define _Pre_satisfies_(cond) _SAL2_Source_(_Pre_satisfies_, (cond), _Pre_satisfies_impl_(cond)) +#define _Post_satisfies_(cond) _SAL2_Source_(_Post_satisfies_, (cond), _Post_satisfies_impl_(cond)) + +// Annotations to express struct, class and field invariants +#define _Struct_size_bytes_(size) _SAL2_Source_(_Struct_size_bytes_, (size), _Writable_bytes_(size)) + +#define _Field_size_(size) _SAL2_Source_(_Field_size_, (size), _Notnull_ _Writable_elements_(size)) +#define _Field_size_opt_(size) _SAL2_Source_(_Field_size_opt_, (size), _Maybenull_ _Writable_elements_(size)) +#define _Field_size_part_(size, count) _SAL2_Source_(_Field_size_part_, (size, count), _Notnull_ _Writable_elements_(size) _Readable_elements_(count)) +#define _Field_size_part_opt_(size, count) _SAL2_Source_(_Field_size_part_opt_, (size, count), _Maybenull_ _Writable_elements_(size) _Readable_elements_(count)) +#define _Field_size_full_(size) _SAL2_Source_(_Field_size_full_, (size), _Field_size_part_(size, size)) +#define _Field_size_full_opt_(size) _SAL2_Source_(_Field_size_full_opt_, (size), _Field_size_part_opt_(size, size)) + +#define _Field_size_bytes_(size) _SAL2_Source_(_Field_size_bytes_, (size), _Notnull_ _Writable_bytes_(size)) +#define _Field_size_bytes_opt_(size) _SAL2_Source_(_Field_size_bytes_opt_, (size), _Maybenull_ _Writable_bytes_(size)) +#define _Field_size_bytes_part_(size, count) _SAL2_Source_(_Field_size_bytes_part_, (size, count), _Notnull_ _Writable_bytes_(size) _Readable_bytes_(count)) +#define _Field_size_bytes_part_opt_(size, count) _SAL2_Source_(_Field_size_bytes_part_opt_, (size, count), _Maybenull_ _Writable_bytes_(size) _Readable_bytes_(count)) +#define _Field_size_bytes_full_(size) _SAL2_Source_(_Field_size_bytes_full_, (size), _Field_size_bytes_part_(size, size)) +#define _Field_size_bytes_full_opt_(size) _SAL2_Source_(_Field_size_bytes_full_opt_, (size), _Field_size_bytes_part_opt_(size, size)) + +#define _Field_z_ _SAL2_Source_(_Field_z_, (), _Null_terminated_) + +#define _Field_range_(min,max) _SAL2_Source_(_Field_range_, (min,max), _Field_range_impl_(min,max)) + +//============================================================================ +// _Pre_\_Post_ Layer: +//============================================================================ + +// +// Raw Pre/Post for declaring custom pre/post conditions +// + +#define _Pre_ _Pre_impl_ +#define _Post_ _Post_impl_ + +// +// Validity property +// + +#define _Valid_ _Valid_impl_ +#define _Notvalid_ _Notvalid_impl_ +#define _Maybevalid_ _Maybevalid_impl_ + +// +// Buffer size properties +// + +// Expressing buffer sizes without specifying pre or post condition +#define _Readable_bytes_(size) _SAL2_Source_(_Readable_bytes_, (size), _Readable_bytes_impl_(size)) +#define _Readable_elements_(size) _SAL2_Source_(_Readable_elements_, (size), _Readable_elements_impl_(size)) +#define _Writable_bytes_(size) _SAL2_Source_(_Writable_bytes_, (size), _Writable_bytes_impl_(size)) +#define _Writable_elements_(size) _SAL2_Source_(_Writable_elements_, (size), _Writable_elements_impl_(size)) + +#define _Null_terminated_ _SAL2_Source_(_Null_terminated_, (), _Null_terminated_impl_) +#define _NullNull_terminated_ _SAL2_Source_(_NullNull_terminated_, (), _NullNull_terminated_impl_) + +// Expressing buffer size as pre or post condition +#define _Pre_readable_size_(size) _SAL2_Source_(_Pre_readable_size_, (size), _Pre1_impl_(__count_impl(size)) _Pre_valid_impl_) +#define _Pre_writable_size_(size) _SAL2_Source_(_Pre_writable_size_, (size), _Pre1_impl_(__cap_impl(size))) +#define _Pre_readable_byte_size_(size) _SAL2_Source_(_Pre_readable_byte_size_, (size), _Pre1_impl_(__bytecount_impl(size)) _Pre_valid_impl_) +#define _Pre_writable_byte_size_(size) _SAL2_Source_(_Pre_writable_byte_size_, (size), _Pre1_impl_(__bytecap_impl(size))) + +#define _Post_readable_size_(size) _SAL2_Source_(_Post_readable_size_, (size), _Post1_impl_(__count_impl(size)) _Post_valid_impl_) +#define _Post_writable_size_(size) _SAL2_Source_(_Post_writable_size_, (size), _Post1_impl_(__cap_impl(size))) +#define _Post_readable_byte_size_(size) _SAL2_Source_(_Post_readable_byte_size_, (size), _Post1_impl_(__bytecount_impl(size)) _Post_valid_impl_) +#define _Post_writable_byte_size_(size) _SAL2_Source_(_Post_writable_byte_size_, (size), _Post1_impl_(__bytecap_impl(size))) + +// +// Pointer null-ness properties +// +#define _Null_ _SAL2_Source_(_Null_, (), _Null_impl_) +#define _Notnull_ _SAL2_Source_(_Notnull_, (), _Notnull_impl_) +#define _Maybenull_ _SAL2_Source_(_Maybenull_, (), _Maybenull_impl_) + +// +// _Pre_ annotations --- +// +// describing conditions that must be met before the call of the function + +// e.g. int strlen( _Pre_z_ const char* sz ); +// buffer is a zero terminated string +#define _Pre_z_ _SAL2_Source_(_Pre_z_, (), _Pre1_impl_(__zterm_impl) _Pre_valid_impl_) + +// valid size unknown or indicated by type (e.g.:LPSTR) +#define _Pre_valid_ _SAL2_Source_(_Pre_valid_, (), _Pre1_impl_(__notnull_impl_notref) _Pre_valid_impl_) +#define _Pre_opt_valid_ _SAL2_Source_(_Pre_opt_valid_, (), _Pre1_impl_(__maybenull_impl_notref) _Pre_valid_impl_) + +#define _Pre_invalid_ _SAL2_Source_(_Pre_invalid_, (), _Deref_pre1_impl_(__notvalid_impl)) + +// Overrides recursive valid when some field is not yet initialized when using _Inout_ +#define _Pre_unknown_ _SAL2_Source_(_Pre_unknown_, (), _Pre1_impl_(__maybevalid_impl)) + +// used with allocated but not yet initialized objects +#define _Pre_notnull_ _SAL2_Source_(_Pre_notnull_, (), _Pre1_impl_(__notnull_impl_notref)) +#define _Pre_maybenull_ _SAL2_Source_(_Pre_maybenull_, (), _Pre1_impl_(__maybenull_impl_notref)) +#define _Pre_null_ _SAL2_Source_(_Pre_null_, (), _Pre1_impl_(__null_impl_notref)) + +// +// _Post_ annotations --- +// +// describing conditions that hold after the function call + +// void CopyStr( _In_z_ const char* szFrom, _Pre_cap_(cch) _Post_z_ char* szFrom, size_t cchFrom ); +// buffer will be a zero-terminated string after the call +#define _Post_z_ _SAL2_Source_(_Post_z_, (), _Post1_impl_(__zterm_impl) _Post_valid_impl_) + +// e.g. HRESULT InitStruct( _Post_valid_ Struct* pobj ); +#define _Post_valid_ _SAL2_Source_(_Post_valid_, (), _Post_valid_impl_) +#define _Post_invalid_ _SAL2_Source_(_Post_invalid_, (), _Deref_post1_impl_(__notvalid_impl)) + +// e.g. void free( _Post_ptr_invalid_ void* pv ); +#define _Post_ptr_invalid_ _SAL2_Source_(_Post_ptr_invalid_, (), _Post1_impl_(__notvalid_impl)) + +// e.g. void ThrowExceptionIfNull( _Post_notnull_ const void* pv ); +#define _Post_notnull_ _SAL2_Source_(_Post_notnull_, (), _Post1_impl_(__notnull_impl)) + +// e.g. HRESULT GetObject(_Outptr_ _On_failure_(_At_(*p, _Post_null_)) T **p); +#define _Post_null_ _SAL2_Source_(_Post_null_, (), _Post1_impl_(__null_impl)) + +#define _Post_maybenull_ _SAL2_Source_(_Post_maybenull_, (), _Post1_impl_(__maybenull_impl)) + +#define _Prepost_z_ _SAL2_Source_(_Prepost_z_, (), _Pre_z_ _Post_z_) + + +#pragma region Input Buffer SAL 1 compatibility macros + +/*========================================================================== + + This section contains definitions for macros defined for VS2010 and earlier. + Usage of these macros is still supported, but the SAL 2 macros defined above + are recommended instead. This comment block is retained to assist in + understanding SAL that still uses the older syntax. + + The macros are defined in 3 layers: + + _In_\_Out_ Layer: + ---------------- + This layer provides the highest abstraction and its macros should be used + in most cases. Its macros start with _In_, _Out_ or _Inout_. For the + typical case they provide the most concise annotations. + + _Pre_\_Post_ Layer: + ------------------ + The macros of this layer only should be used when there is no suitable macro + in the _In_\_Out_ layer. Its macros start with _Pre_, _Post_, _Ret_, + _Deref_pre_ _Deref_post_ and _Deref_ret_. This layer provides the most + flexibility for annotations. + + Implementation Abstraction Layer: + -------------------------------- + Macros from this layer should never be used directly. The layer only exists + to hide the implementation of the annotation macros. + + + Annotation Syntax: + |--------------|----------|----------------|-----------------------------| + | Usage | Nullness | ZeroTerminated | Extent | + |--------------|----------|----------------|-----------------------------| + | _In_ | <> | <> | <> | + | _Out_ | opt_ | z_ | [byte]cap_[c_|x_]( size ) | + | _Inout_ | | | [byte]count_[c_|x_]( size ) | + | _Deref_out_ | | | ptrdiff_cap_( ptr ) | + |--------------| | | ptrdiff_count_( ptr ) | + | _Ret_ | | | | + | _Deref_ret_ | | | | + |--------------| | | | + | _Pre_ | | | | + | _Post_ | | | | + | _Deref_pre_ | | | | + | _Deref_post_ | | | | + |--------------|----------|----------------|-----------------------------| + + Usage: + ----- + _In_, _Out_, _Inout_, _Pre_, _Post_, _Deref_pre_, _Deref_post_ are for + formal parameters. + _Ret_, _Deref_ret_ must be used for return values. + + Nullness: + -------- + If the pointer can be NULL the annotation contains _opt. If the macro + does not contain '_opt' the pointer may not be NULL. + + String Type: + ----------- + _z: NullTerminated string + for _In_ parameters the buffer must have the specified stringtype before the call + for _Out_ parameters the buffer must have the specified stringtype after the call + for _Inout_ parameters both conditions apply + + Extent Syntax: + |------|---------------|---------------| + | Unit | Writ\Readable | Argument Type | + |------|---------------|---------------| + | <> | cap_ | <> | + | byte | count_ | c_ | + | | | x_ | + |------|---------------|---------------| + + 'cap' (capacity) describes the writable size of the buffer and is typically used + with _Out_. The default unit is elements. Use 'bytecap' if the size is given in bytes + 'count' describes the readable size of the buffer and is typically used with _In_. + The default unit is elements. Use 'bytecount' if the size is given in bytes. + + Argument syntax for cap_, bytecap_, count_, bytecount_: + (<parameter>|return)[+n] e.g. cch, return, cb+2 + + If the buffer size is a constant expression use the c_ postfix. + E.g. cap_c_(20), count_c_(MAX_PATH), bytecount_c_(16) + + If the buffer size is given by a limiting pointer use the ptrdiff_ versions + of the macros. + + If the buffer size is neither a parameter nor a constant expression use the x_ + postfix. e.g. bytecount_x_(num*size) x_ annotations accept any arbitrary string. + No analysis can be done for x_ annotations but they at least tell the tool that + the buffer has some sort of extent description. x_ annotations might be supported + by future compiler versions. + +============================================================================*/ + +// e.g. void SetCharRange( _In_count_(cch) const char* rgch, size_t cch ) +// valid buffer extent described by another parameter +#define _In_count_(size) _SAL1_1_Source_(_In_count_, (size), _Pre_count_(size) _Deref_pre_readonly_) +#define _In_opt_count_(size) _SAL1_1_Source_(_In_opt_count_, (size), _Pre_opt_count_(size) _Deref_pre_readonly_) +#define _In_bytecount_(size) _SAL1_1_Source_(_In_bytecount_, (size), _Pre_bytecount_(size) _Deref_pre_readonly_) +#define _In_opt_bytecount_(size) _SAL1_1_Source_(_In_opt_bytecount_, (size), _Pre_opt_bytecount_(size) _Deref_pre_readonly_) + +// valid buffer extent described by a constant extression +#define _In_count_c_(size) _SAL1_1_Source_(_In_count_c_, (size), _Pre_count_c_(size) _Deref_pre_readonly_) +#define _In_opt_count_c_(size) _SAL1_1_Source_(_In_opt_count_c_, (size), _Pre_opt_count_c_(size) _Deref_pre_readonly_) +#define _In_bytecount_c_(size) _SAL1_1_Source_(_In_bytecount_c_, (size), _Pre_bytecount_c_(size) _Deref_pre_readonly_) +#define _In_opt_bytecount_c_(size) _SAL1_1_Source_(_In_opt_bytecount_c_, (size), _Pre_opt_bytecount_c_(size) _Deref_pre_readonly_) + +// nullterminated 'input' buffers with given size + +// e.g. void SetCharRange( _In_count_(cch) const char* rgch, size_t cch ) +// nullterminated valid buffer extent described by another parameter +#define _In_z_count_(size) _SAL1_1_Source_(_In_z_count_, (size), _Pre_z_ _Pre_count_(size) _Deref_pre_readonly_) +#define _In_opt_z_count_(size) _SAL1_1_Source_(_In_opt_z_count_, (size), _Pre_opt_z_ _Pre_opt_count_(size) _Deref_pre_readonly_) +#define _In_z_bytecount_(size) _SAL1_1_Source_(_In_z_bytecount_, (size), _Pre_z_ _Pre_bytecount_(size) _Deref_pre_readonly_) +#define _In_opt_z_bytecount_(size) _SAL1_1_Source_(_In_opt_z_bytecount_, (size), _Pre_opt_z_ _Pre_opt_bytecount_(size) _Deref_pre_readonly_) + +// nullterminated valid buffer extent described by a constant extression +#define _In_z_count_c_(size) _SAL1_1_Source_(_In_z_count_c_, (size), _Pre_z_ _Pre_count_c_(size) _Deref_pre_readonly_) +#define _In_opt_z_count_c_(size) _SAL1_1_Source_(_In_opt_z_count_c_, (size), _Pre_opt_z_ _Pre_opt_count_c_(size) _Deref_pre_readonly_) +#define _In_z_bytecount_c_(size) _SAL1_1_Source_(_In_z_bytecount_c_, (size), _Pre_z_ _Pre_bytecount_c_(size) _Deref_pre_readonly_) +#define _In_opt_z_bytecount_c_(size) _SAL1_1_Source_(_In_opt_z_bytecount_c_, (size), _Pre_opt_z_ _Pre_opt_bytecount_c_(size) _Deref_pre_readonly_) + +// buffer capacity is described by another pointer +// e.g. void Foo( _In_ptrdiff_count_(pchMax) const char* pch, const char* pchMax ) { while pch < pchMax ) pch++; } +#define _In_ptrdiff_count_(size) _SAL1_1_Source_(_In_ptrdiff_count_, (size), _Pre_ptrdiff_count_(size) _Deref_pre_readonly_) +#define _In_opt_ptrdiff_count_(size) _SAL1_1_Source_(_In_opt_ptrdiff_count_, (size), _Pre_opt_ptrdiff_count_(size) _Deref_pre_readonly_) + +// 'x' version for complex expressions that are not supported by the current compiler version +// e.g. void Set3ColMatrix( _In_count_x_(3*cRows) const Elem* matrix, int cRows ); +#define _In_count_x_(size) _SAL1_1_Source_(_In_count_x_, (size), _Pre_count_x_(size) _Deref_pre_readonly_) +#define _In_opt_count_x_(size) _SAL1_1_Source_(_In_opt_count_x_, (size), _Pre_opt_count_x_(size) _Deref_pre_readonly_) +#define _In_bytecount_x_(size) _SAL1_1_Source_(_In_bytecount_x_, (size), _Pre_bytecount_x_(size) _Deref_pre_readonly_) +#define _In_opt_bytecount_x_(size) _SAL1_1_Source_(_In_opt_bytecount_x_, (size), _Pre_opt_bytecount_x_(size) _Deref_pre_readonly_) + + +// 'out' with buffer size +// e.g. void GetIndeces( _Out_cap_(cIndeces) int* rgIndeces, size_t cIndices ); +// buffer capacity is described by another parameter +#define _Out_cap_(size) _SAL1_1_Source_(_Out_cap_, (size), _Pre_cap_(size) _Post_valid_impl_) +#define _Out_opt_cap_(size) _SAL1_1_Source_(_Out_opt_cap_, (size), _Pre_opt_cap_(size) _Post_valid_impl_) +#define _Out_bytecap_(size) _SAL1_1_Source_(_Out_bytecap_, (size), _Pre_bytecap_(size) _Post_valid_impl_) +#define _Out_opt_bytecap_(size) _SAL1_1_Source_(_Out_opt_bytecap_, (size), _Pre_opt_bytecap_(size) _Post_valid_impl_) + +// buffer capacity is described by a constant expression +#define _Out_cap_c_(size) _SAL1_1_Source_(_Out_cap_c_, (size), _Pre_cap_c_(size) _Post_valid_impl_) +#define _Out_opt_cap_c_(size) _SAL1_1_Source_(_Out_opt_cap_c_, (size), _Pre_opt_cap_c_(size) _Post_valid_impl_) +#define _Out_bytecap_c_(size) _SAL1_1_Source_(_Out_bytecap_c_, (size), _Pre_bytecap_c_(size) _Post_valid_impl_) +#define _Out_opt_bytecap_c_(size) _SAL1_1_Source_(_Out_opt_bytecap_c_, (size), _Pre_opt_bytecap_c_(size) _Post_valid_impl_) + +// buffer capacity is described by another parameter multiplied by a constant expression +#define _Out_cap_m_(mult,size) _SAL1_1_Source_(_Out_cap_m_, (mult,size), _Pre_cap_m_(mult,size) _Post_valid_impl_) +#define _Out_opt_cap_m_(mult,size) _SAL1_1_Source_(_Out_opt_cap_m_, (mult,size), _Pre_opt_cap_m_(mult,size) _Post_valid_impl_) +#define _Out_z_cap_m_(mult,size) _SAL1_1_Source_(_Out_z_cap_m_, (mult,size), _Pre_cap_m_(mult,size) _Post_valid_impl_ _Post_z_) +#define _Out_opt_z_cap_m_(mult,size) _SAL1_1_Source_(_Out_opt_z_cap_m_, (mult,size), _Pre_opt_cap_m_(mult,size) _Post_valid_impl_ _Post_z_) + +// buffer capacity is described by another pointer +// e.g. void Foo( _Out_ptrdiff_cap_(pchMax) char* pch, const char* pchMax ) { while pch < pchMax ) pch++; } +#define _Out_ptrdiff_cap_(size) _SAL1_1_Source_(_Out_ptrdiff_cap_, (size), _Pre_ptrdiff_cap_(size) _Post_valid_impl_) +#define _Out_opt_ptrdiff_cap_(size) _SAL1_1_Source_(_Out_opt_ptrdiff_cap_, (size), _Pre_opt_ptrdiff_cap_(size) _Post_valid_impl_) + +// buffer capacity is described by a complex expression +#define _Out_cap_x_(size) _SAL1_1_Source_(_Out_cap_x_, (size), _Pre_cap_x_(size) _Post_valid_impl_) +#define _Out_opt_cap_x_(size) _SAL1_1_Source_(_Out_opt_cap_x_, (size), _Pre_opt_cap_x_(size) _Post_valid_impl_) +#define _Out_bytecap_x_(size) _SAL1_1_Source_(_Out_bytecap_x_, (size), _Pre_bytecap_x_(size) _Post_valid_impl_) +#define _Out_opt_bytecap_x_(size) _SAL1_1_Source_(_Out_opt_bytecap_x_, (size), _Pre_opt_bytecap_x_(size) _Post_valid_impl_) + +// a zero terminated string is filled into a buffer of given capacity +// e.g. void CopyStr( _In_z_ const char* szFrom, _Out_z_cap_(cchTo) char* szTo, size_t cchTo ); +// buffer capacity is described by another parameter +#define _Out_z_cap_(size) _SAL1_1_Source_(_Out_z_cap_, (size), _Pre_cap_(size) _Post_valid_impl_ _Post_z_) +#define _Out_opt_z_cap_(size) _SAL1_1_Source_(_Out_opt_z_cap_, (size), _Pre_opt_cap_(size) _Post_valid_impl_ _Post_z_) +#define _Out_z_bytecap_(size) _SAL1_1_Source_(_Out_z_bytecap_, (size), _Pre_bytecap_(size) _Post_valid_impl_ _Post_z_) +#define _Out_opt_z_bytecap_(size) _SAL1_1_Source_(_Out_opt_z_bytecap_, (size), _Pre_opt_bytecap_(size) _Post_valid_impl_ _Post_z_) + +// buffer capacity is described by a constant expression +#define _Out_z_cap_c_(size) _SAL1_1_Source_(_Out_z_cap_c_, (size), _Pre_cap_c_(size) _Post_valid_impl_ _Post_z_) +#define _Out_opt_z_cap_c_(size) _SAL1_1_Source_(_Out_opt_z_cap_c_, (size), _Pre_opt_cap_c_(size) _Post_valid_impl_ _Post_z_) +#define _Out_z_bytecap_c_(size) _SAL1_1_Source_(_Out_z_bytecap_c_, (size), _Pre_bytecap_c_(size) _Post_valid_impl_ _Post_z_) +#define _Out_opt_z_bytecap_c_(size) _SAL1_1_Source_(_Out_opt_z_bytecap_c_, (size), _Pre_opt_bytecap_c_(size) _Post_valid_impl_ _Post_z_) + +// buffer capacity is described by a complex expression +#define _Out_z_cap_x_(size) _SAL1_1_Source_(_Out_z_cap_x_, (size), _Pre_cap_x_(size) _Post_valid_impl_ _Post_z_) +#define _Out_opt_z_cap_x_(size) _SAL1_1_Source_(_Out_opt_z_cap_x_, (size), _Pre_opt_cap_x_(size) _Post_valid_impl_ _Post_z_) +#define _Out_z_bytecap_x_(size) _SAL1_1_Source_(_Out_z_bytecap_x_, (size), _Pre_bytecap_x_(size) _Post_valid_impl_ _Post_z_) +#define _Out_opt_z_bytecap_x_(size) _SAL1_1_Source_(_Out_opt_z_bytecap_x_, (size), _Pre_opt_bytecap_x_(size) _Post_valid_impl_ _Post_z_) + +// a zero terminated string is filled into a buffer of given capacity +// e.g. size_t CopyCharRange( _In_count_(cchFrom) const char* rgchFrom, size_t cchFrom, _Out_cap_post_count_(cchTo,return)) char* rgchTo, size_t cchTo ); +#define _Out_cap_post_count_(cap,count) _SAL1_1_Source_(_Out_cap_post_count_, (cap,count), _Pre_cap_(cap) _Post_valid_impl_ _Post_count_(count)) +#define _Out_opt_cap_post_count_(cap,count) _SAL1_1_Source_(_Out_opt_cap_post_count_, (cap,count), _Pre_opt_cap_(cap) _Post_valid_impl_ _Post_count_(count)) +#define _Out_bytecap_post_bytecount_(cap,count) _SAL1_1_Source_(_Out_bytecap_post_bytecount_, (cap,count), _Pre_bytecap_(cap) _Post_valid_impl_ _Post_bytecount_(count)) +#define _Out_opt_bytecap_post_bytecount_(cap,count) _SAL1_1_Source_(_Out_opt_bytecap_post_bytecount_, (cap,count), _Pre_opt_bytecap_(cap) _Post_valid_impl_ _Post_bytecount_(count)) + +// a zero terminated string is filled into a buffer of given capacity +// e.g. size_t CopyStr( _In_z_ const char* szFrom, _Out_z_cap_post_count_(cchTo,return+1) char* szTo, size_t cchTo ); +#define _Out_z_cap_post_count_(cap,count) _SAL1_1_Source_(_Out_z_cap_post_count_, (cap,count), _Pre_cap_(cap) _Post_valid_impl_ _Post_z_count_(count)) +#define _Out_opt_z_cap_post_count_(cap,count) _SAL1_1_Source_(_Out_opt_z_cap_post_count_, (cap,count), _Pre_opt_cap_(cap) _Post_valid_impl_ _Post_z_count_(count)) +#define _Out_z_bytecap_post_bytecount_(cap,count) _SAL1_1_Source_(_Out_z_bytecap_post_bytecount_, (cap,count), _Pre_bytecap_(cap) _Post_valid_impl_ _Post_z_bytecount_(count)) +#define _Out_opt_z_bytecap_post_bytecount_(cap,count) _SAL1_1_Source_(_Out_opt_z_bytecap_post_bytecount_, (cap,count), _Pre_opt_bytecap_(cap) _Post_valid_impl_ _Post_z_bytecount_(count)) + +// only use with dereferenced arguments e.g. '*pcch' +#define _Out_capcount_(capcount) _SAL1_1_Source_(_Out_capcount_, (capcount), _Pre_cap_(capcount) _Post_valid_impl_ _Post_count_(capcount)) +#define _Out_opt_capcount_(capcount) _SAL1_1_Source_(_Out_opt_capcount_, (capcount), _Pre_opt_cap_(capcount) _Post_valid_impl_ _Post_count_(capcount)) +#define _Out_bytecapcount_(capcount) _SAL1_1_Source_(_Out_bytecapcount_, (capcount), _Pre_bytecap_(capcount) _Post_valid_impl_ _Post_bytecount_(capcount)) +#define _Out_opt_bytecapcount_(capcount) _SAL1_1_Source_(_Out_opt_bytecapcount_, (capcount), _Pre_opt_bytecap_(capcount) _Post_valid_impl_ _Post_bytecount_(capcount)) + +#define _Out_capcount_x_(capcount) _SAL1_1_Source_(_Out_capcount_x_, (capcount), _Pre_cap_x_(capcount) _Post_valid_impl_ _Post_count_x_(capcount)) +#define _Out_opt_capcount_x_(capcount) _SAL1_1_Source_(_Out_opt_capcount_x_, (capcount), _Pre_opt_cap_x_(capcount) _Post_valid_impl_ _Post_count_x_(capcount)) +#define _Out_bytecapcount_x_(capcount) _SAL1_1_Source_(_Out_bytecapcount_x_, (capcount), _Pre_bytecap_x_(capcount) _Post_valid_impl_ _Post_bytecount_x_(capcount)) +#define _Out_opt_bytecapcount_x_(capcount) _SAL1_1_Source_(_Out_opt_bytecapcount_x_, (capcount), _Pre_opt_bytecap_x_(capcount) _Post_valid_impl_ _Post_bytecount_x_(capcount)) + +// e.g. GetString( _Out_z_capcount_(*pLen+1) char* sz, size_t* pLen ); +#define _Out_z_capcount_(capcount) _SAL1_1_Source_(_Out_z_capcount_, (capcount), _Pre_cap_(capcount) _Post_valid_impl_ _Post_z_count_(capcount)) +#define _Out_opt_z_capcount_(capcount) _SAL1_1_Source_(_Out_opt_z_capcount_, (capcount), _Pre_opt_cap_(capcount) _Post_valid_impl_ _Post_z_count_(capcount)) +#define _Out_z_bytecapcount_(capcount) _SAL1_1_Source_(_Out_z_bytecapcount_, (capcount), _Pre_bytecap_(capcount) _Post_valid_impl_ _Post_z_bytecount_(capcount)) +#define _Out_opt_z_bytecapcount_(capcount) _SAL1_1_Source_(_Out_opt_z_bytecapcount_, (capcount), _Pre_opt_bytecap_(capcount) _Post_valid_impl_ _Post_z_bytecount_(capcount)) + + +// 'inout' buffers with initialized elements before and after the call +// e.g. void ModifyIndices( _Inout_count_(cIndices) int* rgIndeces, size_t cIndices ); +#define _Inout_count_(size) _SAL1_1_Source_(_Inout_count_, (size), _Prepost_count_(size)) +#define _Inout_opt_count_(size) _SAL1_1_Source_(_Inout_opt_count_, (size), _Prepost_opt_count_(size)) +#define _Inout_bytecount_(size) _SAL1_1_Source_(_Inout_bytecount_, (size), _Prepost_bytecount_(size)) +#define _Inout_opt_bytecount_(size) _SAL1_1_Source_(_Inout_opt_bytecount_, (size), _Prepost_opt_bytecount_(size)) + +#define _Inout_count_c_(size) _SAL1_1_Source_(_Inout_count_c_, (size), _Prepost_count_c_(size)) +#define _Inout_opt_count_c_(size) _SAL1_1_Source_(_Inout_opt_count_c_, (size), _Prepost_opt_count_c_(size)) +#define _Inout_bytecount_c_(size) _SAL1_1_Source_(_Inout_bytecount_c_, (size), _Prepost_bytecount_c_(size)) +#define _Inout_opt_bytecount_c_(size) _SAL1_1_Source_(_Inout_opt_bytecount_c_, (size), _Prepost_opt_bytecount_c_(size)) + +// nullterminated 'inout' buffers with initialized elements before and after the call +// e.g. void ModifyIndices( _Inout_count_(cIndices) int* rgIndeces, size_t cIndices ); +#define _Inout_z_count_(size) _SAL1_1_Source_(_Inout_z_count_, (size), _Prepost_z_ _Prepost_count_(size)) +#define _Inout_opt_z_count_(size) _SAL1_1_Source_(_Inout_opt_z_count_, (size), _Prepost_z_ _Prepost_opt_count_(size)) +#define _Inout_z_bytecount_(size) _SAL1_1_Source_(_Inout_z_bytecount_, (size), _Prepost_z_ _Prepost_bytecount_(size)) +#define _Inout_opt_z_bytecount_(size) _SAL1_1_Source_(_Inout_opt_z_bytecount_, (size), _Prepost_z_ _Prepost_opt_bytecount_(size)) + +#define _Inout_z_count_c_(size) _SAL1_1_Source_(_Inout_z_count_c_, (size), _Prepost_z_ _Prepost_count_c_(size)) +#define _Inout_opt_z_count_c_(size) _SAL1_1_Source_(_Inout_opt_z_count_c_, (size), _Prepost_z_ _Prepost_opt_count_c_(size)) +#define _Inout_z_bytecount_c_(size) _SAL1_1_Source_(_Inout_z_bytecount_c_, (size), _Prepost_z_ _Prepost_bytecount_c_(size)) +#define _Inout_opt_z_bytecount_c_(size) _SAL1_1_Source_(_Inout_opt_z_bytecount_c_, (size), _Prepost_z_ _Prepost_opt_bytecount_c_(size)) + +#define _Inout_ptrdiff_count_(size) _SAL1_1_Source_(_Inout_ptrdiff_count_, (size), _Pre_ptrdiff_count_(size)) +#define _Inout_opt_ptrdiff_count_(size) _SAL1_1_Source_(_Inout_opt_ptrdiff_count_, (size), _Pre_opt_ptrdiff_count_(size)) + +#define _Inout_count_x_(size) _SAL1_1_Source_(_Inout_count_x_, (size), _Prepost_count_x_(size)) +#define _Inout_opt_count_x_(size) _SAL1_1_Source_(_Inout_opt_count_x_, (size), _Prepost_opt_count_x_(size)) +#define _Inout_bytecount_x_(size) _SAL1_1_Source_(_Inout_bytecount_x_, (size), _Prepost_bytecount_x_(size)) +#define _Inout_opt_bytecount_x_(size) _SAL1_1_Source_(_Inout_opt_bytecount_x_, (size), _Prepost_opt_bytecount_x_(size)) + +// e.g. void AppendToLPSTR( _In_ LPCSTR szFrom, _Inout_cap_(cchTo) LPSTR* szTo, size_t cchTo ); +#define _Inout_cap_(size) _SAL1_1_Source_(_Inout_cap_, (size), _Pre_valid_cap_(size) _Post_valid_) +#define _Inout_opt_cap_(size) _SAL1_1_Source_(_Inout_opt_cap_, (size), _Pre_opt_valid_cap_(size) _Post_valid_) +#define _Inout_bytecap_(size) _SAL1_1_Source_(_Inout_bytecap_, (size), _Pre_valid_bytecap_(size) _Post_valid_) +#define _Inout_opt_bytecap_(size) _SAL1_1_Source_(_Inout_opt_bytecap_, (size), _Pre_opt_valid_bytecap_(size) _Post_valid_) + +#define _Inout_cap_c_(size) _SAL1_1_Source_(_Inout_cap_c_, (size), _Pre_valid_cap_c_(size) _Post_valid_) +#define _Inout_opt_cap_c_(size) _SAL1_1_Source_(_Inout_opt_cap_c_, (size), _Pre_opt_valid_cap_c_(size) _Post_valid_) +#define _Inout_bytecap_c_(size) _SAL1_1_Source_(_Inout_bytecap_c_, (size), _Pre_valid_bytecap_c_(size) _Post_valid_) +#define _Inout_opt_bytecap_c_(size) _SAL1_1_Source_(_Inout_opt_bytecap_c_, (size), _Pre_opt_valid_bytecap_c_(size) _Post_valid_) + +#define _Inout_cap_x_(size) _SAL1_1_Source_(_Inout_cap_x_, (size), _Pre_valid_cap_x_(size) _Post_valid_) +#define _Inout_opt_cap_x_(size) _SAL1_1_Source_(_Inout_opt_cap_x_, (size), _Pre_opt_valid_cap_x_(size) _Post_valid_) +#define _Inout_bytecap_x_(size) _SAL1_1_Source_(_Inout_bytecap_x_, (size), _Pre_valid_bytecap_x_(size) _Post_valid_) +#define _Inout_opt_bytecap_x_(size) _SAL1_1_Source_(_Inout_opt_bytecap_x_, (size), _Pre_opt_valid_bytecap_x_(size) _Post_valid_) + +// inout string buffers with writable size +// e.g. void AppendStr( _In_z_ const char* szFrom, _Inout_z_cap_(cchTo) char* szTo, size_t cchTo ); +#define _Inout_z_cap_(size) _SAL1_1_Source_(_Inout_z_cap_, (size), _Pre_z_cap_(size) _Post_z_) +#define _Inout_opt_z_cap_(size) _SAL1_1_Source_(_Inout_opt_z_cap_, (size), _Pre_opt_z_cap_(size) _Post_z_) +#define _Inout_z_bytecap_(size) _SAL1_1_Source_(_Inout_z_bytecap_, (size), _Pre_z_bytecap_(size) _Post_z_) +#define _Inout_opt_z_bytecap_(size) _SAL1_1_Source_(_Inout_opt_z_bytecap_, (size), _Pre_opt_z_bytecap_(size) _Post_z_) + +#define _Inout_z_cap_c_(size) _SAL1_1_Source_(_Inout_z_cap_c_, (size), _Pre_z_cap_c_(size) _Post_z_) +#define _Inout_opt_z_cap_c_(size) _SAL1_1_Source_(_Inout_opt_z_cap_c_, (size), _Pre_opt_z_cap_c_(size) _Post_z_) +#define _Inout_z_bytecap_c_(size) _SAL1_1_Source_(_Inout_z_bytecap_c_, (size), _Pre_z_bytecap_c_(size) _Post_z_) +#define _Inout_opt_z_bytecap_c_(size) _SAL1_1_Source_(_Inout_opt_z_bytecap_c_, (size), _Pre_opt_z_bytecap_c_(size) _Post_z_) + +#define _Inout_z_cap_x_(size) _SAL1_1_Source_(_Inout_z_cap_x_, (size), _Pre_z_cap_x_(size) _Post_z_) +#define _Inout_opt_z_cap_x_(size) _SAL1_1_Source_(_Inout_opt_z_cap_x_, (size), _Pre_opt_z_cap_x_(size) _Post_z_) +#define _Inout_z_bytecap_x_(size) _SAL1_1_Source_(_Inout_z_bytecap_x_, (size), _Pre_z_bytecap_x_(size) _Post_z_) +#define _Inout_opt_z_bytecap_x_(size) _SAL1_1_Source_(_Inout_opt_z_bytecap_x_, (size), _Pre_opt_z_bytecap_x_(size) _Post_z_) + + +// returning pointers to valid objects +#define _Ret_ _SAL1_1_Source_(_Ret_, (), _Ret_valid_) +#define _Ret_opt_ _SAL1_1_Source_(_Ret_opt_, (), _Ret_opt_valid_) + +// annotations to express 'boundedness' of integral value parameter +#define _In_bound_ _SAL1_1_Source_(_In_bound_, (), _In_bound_impl_) +#define _Out_bound_ _SAL1_1_Source_(_Out_bound_, (), _Out_bound_impl_) +#define _Ret_bound_ _SAL1_1_Source_(_Ret_bound_, (), _Ret_bound_impl_) +#define _Deref_in_bound_ _SAL1_1_Source_(_Deref_in_bound_, (), _Deref_in_bound_impl_) +#define _Deref_out_bound_ _SAL1_1_Source_(_Deref_out_bound_, (), _Deref_out_bound_impl_) +#define _Deref_inout_bound_ _SAL1_1_Source_(_Deref_inout_bound_, (), _Deref_in_bound_ _Deref_out_bound_) +#define _Deref_ret_bound_ _SAL1_1_Source_(_Deref_ret_bound_, (), _Deref_ret_bound_impl_) + +// e.g. HRESULT HrCreatePoint( _Deref_out_opt_ POINT** ppPT ); +#define _Deref_out_ _SAL1_1_Source_(_Deref_out_, (), _Out_ _Deref_post_valid_) +#define _Deref_out_opt_ _SAL1_1_Source_(_Deref_out_opt_, (), _Out_ _Deref_post_opt_valid_) +#define _Deref_opt_out_ _SAL1_1_Source_(_Deref_opt_out_, (), _Out_opt_ _Deref_post_valid_) +#define _Deref_opt_out_opt_ _SAL1_1_Source_(_Deref_opt_out_opt_, (), _Out_opt_ _Deref_post_opt_valid_) + +// e.g. void CloneString( _In_z_ const wchar_t* wzFrom, _Deref_out_z_ wchar_t** pWzTo ); +#define _Deref_out_z_ _SAL1_1_Source_(_Deref_out_z_, (), _Out_ _Deref_post_z_) +#define _Deref_out_opt_z_ _SAL1_1_Source_(_Deref_out_opt_z_, (), _Out_ _Deref_post_opt_z_) +#define _Deref_opt_out_z_ _SAL1_1_Source_(_Deref_opt_out_z_, (), _Out_opt_ _Deref_post_z_) +#define _Deref_opt_out_opt_z_ _SAL1_1_Source_(_Deref_opt_out_opt_z_, (), _Out_opt_ _Deref_post_opt_z_) + +// +// _Deref_pre_ --- +// +// describing conditions for array elements of dereferenced pointer parameters that must be met before the call + +// e.g. void SaveStringArray( _In_count_(cStrings) _Deref_pre_z_ const wchar_t* const rgpwch[] ); +#define _Deref_pre_z_ _SAL1_1_Source_(_Deref_pre_z_, (), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__zterm_impl) _Pre_valid_impl_) +#define _Deref_pre_opt_z_ _SAL1_1_Source_(_Deref_pre_opt_z_, (), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__zterm_impl) _Pre_valid_impl_) + +// e.g. void FillInArrayOfStr32( _In_count_(cStrings) _Deref_pre_cap_c_(32) _Deref_post_z_ wchar_t* const rgpwch[] ); +// buffer capacity is described by another parameter +#define _Deref_pre_cap_(size) _SAL1_1_Source_(_Deref_pre_cap_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__cap_impl(size))) +#define _Deref_pre_opt_cap_(size) _SAL1_1_Source_(_Deref_pre_opt_cap_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__cap_impl(size))) +#define _Deref_pre_bytecap_(size) _SAL1_1_Source_(_Deref_pre_bytecap_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecap_impl(size))) +#define _Deref_pre_opt_bytecap_(size) _SAL1_1_Source_(_Deref_pre_opt_bytecap_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecap_impl(size))) + +// buffer capacity is described by a constant expression +#define _Deref_pre_cap_c_(size) _SAL1_1_Source_(_Deref_pre_cap_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__cap_c_impl(size))) +#define _Deref_pre_opt_cap_c_(size) _SAL1_1_Source_(_Deref_pre_opt_cap_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__cap_c_impl(size))) +#define _Deref_pre_bytecap_c_(size) _SAL1_1_Source_(_Deref_pre_bytecap_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecap_c_impl(size))) +#define _Deref_pre_opt_bytecap_c_(size) _SAL1_1_Source_(_Deref_pre_opt_bytecap_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecap_c_impl(size))) + +// buffer capacity is described by a complex condition +#define _Deref_pre_cap_x_(size) _SAL1_1_Source_(_Deref_pre_cap_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__cap_x_impl(size))) +#define _Deref_pre_opt_cap_x_(size) _SAL1_1_Source_(_Deref_pre_opt_cap_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__cap_x_impl(size))) +#define _Deref_pre_bytecap_x_(size) _SAL1_1_Source_(_Deref_pre_bytecap_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecap_x_impl(size))) +#define _Deref_pre_opt_bytecap_x_(size) _SAL1_1_Source_(_Deref_pre_opt_bytecap_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecap_x_impl(size))) + +// convenience macros for nullterminated buffers with given capacity +#define _Deref_pre_z_cap_(size) _SAL1_1_Source_(_Deref_pre_z_cap_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_z_cap_(size) _SAL1_1_Source_(_Deref_pre_opt_z_cap_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_z_bytecap_(size) _SAL1_1_Source_(_Deref_pre_z_bytecap_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_z_bytecap_(size) _SAL1_1_Source_(_Deref_pre_opt_z_bytecap_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_) + +#define _Deref_pre_z_cap_c_(size) _SAL1_1_Source_(_Deref_pre_z_cap_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_z_cap_c_(size) _SAL1_1_Source_(_Deref_pre_opt_z_cap_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_z_bytecap_c_(size) _SAL1_1_Source_(_Deref_pre_z_bytecap_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_z_bytecap_c_(size) _SAL1_1_Source_(_Deref_pre_opt_z_bytecap_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_) + +#define _Deref_pre_z_cap_x_(size) _SAL1_1_Source_(_Deref_pre_z_cap_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_z_cap_x_(size) _SAL1_1_Source_(_Deref_pre_opt_z_cap_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__cap_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_z_bytecap_x_(size) _SAL1_1_Source_(_Deref_pre_z_bytecap_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_z_bytecap_x_(size) _SAL1_1_Source_(_Deref_pre_opt_z_bytecap_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_) + +// known capacity and valid but unknown readable extent +#define _Deref_pre_valid_cap_(size) _SAL1_1_Source_(_Deref_pre_valid_cap_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__cap_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_valid_cap_(size) _SAL1_1_Source_(_Deref_pre_opt_valid_cap_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__cap_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_valid_bytecap_(size) _SAL1_1_Source_(_Deref_pre_valid_bytecap_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecap_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_valid_bytecap_(size) _SAL1_1_Source_(_Deref_pre_opt_valid_bytecap_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecap_impl(size)) _Pre_valid_impl_) + +#define _Deref_pre_valid_cap_c_(size) _SAL1_1_Source_(_Deref_pre_valid_cap_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__cap_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_valid_cap_c_(size) _SAL1_1_Source_(_Deref_pre_opt_valid_cap_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__cap_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_valid_bytecap_c_(size) _SAL1_1_Source_(_Deref_pre_valid_bytecap_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecap_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_valid_bytecap_c_(size) _SAL1_1_Source_(_Deref_pre_opt_valid_bytecap_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecap_c_impl(size)) _Pre_valid_impl_) + +#define _Deref_pre_valid_cap_x_(size) _SAL1_1_Source_(_Deref_pre_valid_cap_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__cap_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_valid_cap_x_(size) _SAL1_1_Source_(_Deref_pre_opt_valid_cap_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__cap_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_valid_bytecap_x_(size) _SAL1_1_Source_(_Deref_pre_valid_bytecap_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecap_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_valid_bytecap_x_(size) _SAL1_1_Source_(_Deref_pre_opt_valid_bytecap_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecap_x_impl(size)) _Pre_valid_impl_) + +// e.g. void SaveMatrix( _In_count_(n) _Deref_pre_count_(n) const Elem** matrix, size_t n ); +// valid buffer extent is described by another parameter +#define _Deref_pre_count_(size) _SAL1_1_Source_(_Deref_pre_count_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__count_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_count_(size) _SAL1_1_Source_(_Deref_pre_opt_count_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__count_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_bytecount_(size) _SAL1_1_Source_(_Deref_pre_bytecount_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecount_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_bytecount_(size) _SAL1_1_Source_(_Deref_pre_opt_bytecount_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecount_impl(size)) _Pre_valid_impl_) + +// valid buffer extent is described by a constant expression +#define _Deref_pre_count_c_(size) _SAL1_1_Source_(_Deref_pre_count_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__count_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_count_c_(size) _SAL1_1_Source_(_Deref_pre_opt_count_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__count_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_bytecount_c_(size) _SAL1_1_Source_(_Deref_pre_bytecount_c_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecount_c_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_bytecount_c_(size) _SAL1_1_Source_(_Deref_pre_opt_bytecount_c_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecount_c_impl(size)) _Pre_valid_impl_) + +// valid buffer extent is described by a complex expression +#define _Deref_pre_count_x_(size) _SAL1_1_Source_(_Deref_pre_count_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__count_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_count_x_(size) _SAL1_1_Source_(_Deref_pre_opt_count_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__count_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_bytecount_x_(size) _SAL1_1_Source_(_Deref_pre_bytecount_x_, (size), _Deref_pre1_impl_(__notnull_impl_notref) _Deref_pre1_impl_(__bytecount_x_impl(size)) _Pre_valid_impl_) +#define _Deref_pre_opt_bytecount_x_(size) _SAL1_1_Source_(_Deref_pre_opt_bytecount_x_, (size), _Deref_pre1_impl_(__maybenull_impl_notref) _Deref_pre1_impl_(__bytecount_x_impl(size)) _Pre_valid_impl_) + +// e.g. void PrintStringArray( _In_count_(cElems) _Deref_pre_valid_ LPCSTR rgStr[], size_t cElems ); +#define _Deref_pre_valid_ _SAL1_1_Source_(_Deref_pre_valid_, (), _Deref_pre1_impl_(__notnull_impl_notref) _Pre_valid_impl_) +#define _Deref_pre_opt_valid_ _SAL1_1_Source_(_Deref_pre_opt_valid_, (), _Deref_pre1_impl_(__maybenull_impl_notref) _Pre_valid_impl_) +#define _Deref_pre_invalid_ _SAL1_1_Source_(_Deref_pre_invalid_, (), _Deref_pre1_impl_(__notvalid_impl)) + +#define _Deref_pre_notnull_ _SAL1_1_Source_(_Deref_pre_notnull_, (), _Deref_pre1_impl_(__notnull_impl_notref)) +#define _Deref_pre_maybenull_ _SAL1_1_Source_(_Deref_pre_maybenull_, (), _Deref_pre1_impl_(__maybenull_impl_notref)) +#define _Deref_pre_null_ _SAL1_1_Source_(_Deref_pre_null_, (), _Deref_pre1_impl_(__null_impl_notref)) + +// restrict access rights +#define _Deref_pre_readonly_ _SAL1_1_Source_(_Deref_pre_readonly_, (), _Deref_pre1_impl_(__readaccess_impl_notref)) +#define _Deref_pre_writeonly_ _SAL1_1_Source_(_Deref_pre_writeonly_, (), _Deref_pre1_impl_(__writeaccess_impl_notref)) + +// +// _Deref_post_ --- +// +// describing conditions for array elements or dereferenced pointer parameters that hold after the call + +// e.g. void CloneString( _In_z_ const Wchar_t* wzIn _Out_ _Deref_post_z_ wchar_t** pWzOut ); +#define _Deref_post_z_ _SAL1_1_Source_(_Deref_post_z_, (), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__zterm_impl) _Post_valid_impl_) +#define _Deref_post_opt_z_ _SAL1_1_Source_(_Deref_post_opt_z_, (), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__zterm_impl) _Post_valid_impl_) + +// e.g. HRESULT HrAllocateMemory( size_t cb, _Out_ _Deref_post_bytecap_(cb) void** ppv ); +// buffer capacity is described by another parameter +#define _Deref_post_cap_(size) _SAL1_1_Source_(_Deref_post_cap_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__cap_impl(size))) +#define _Deref_post_opt_cap_(size) _SAL1_1_Source_(_Deref_post_opt_cap_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__cap_impl(size))) +#define _Deref_post_bytecap_(size) _SAL1_1_Source_(_Deref_post_bytecap_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecap_impl(size))) +#define _Deref_post_opt_bytecap_(size) _SAL1_1_Source_(_Deref_post_opt_bytecap_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecap_impl(size))) + +// buffer capacity is described by a constant expression +#define _Deref_post_cap_c_(size) _SAL1_1_Source_(_Deref_post_cap_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__cap_c_impl(size))) +#define _Deref_post_opt_cap_c_(size) _SAL1_1_Source_(_Deref_post_opt_cap_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__cap_c_impl(size))) +#define _Deref_post_bytecap_c_(size) _SAL1_1_Source_(_Deref_post_bytecap_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecap_c_impl(size))) +#define _Deref_post_opt_bytecap_c_(size) _SAL1_1_Source_(_Deref_post_opt_bytecap_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecap_c_impl(size))) + +// buffer capacity is described by a complex expression +#define _Deref_post_cap_x_(size) _SAL1_1_Source_(_Deref_post_cap_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__cap_x_impl(size))) +#define _Deref_post_opt_cap_x_(size) _SAL1_1_Source_(_Deref_post_opt_cap_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__cap_x_impl(size))) +#define _Deref_post_bytecap_x_(size) _SAL1_1_Source_(_Deref_post_bytecap_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecap_x_impl(size))) +#define _Deref_post_opt_bytecap_x_(size) _SAL1_1_Source_(_Deref_post_opt_bytecap_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecap_x_impl(size))) + +// convenience macros for nullterminated buffers with given capacity +#define _Deref_post_z_cap_(size) _SAL1_1_Source_(_Deref_post_z_cap_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl,__cap_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_z_cap_(size) _SAL1_1_Source_(_Deref_post_opt_z_cap_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl,__cap_impl(size)) _Post_valid_impl_) +#define _Deref_post_z_bytecap_(size) _SAL1_1_Source_(_Deref_post_z_bytecap_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl,__bytecap_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_z_bytecap_(size) _SAL1_1_Source_(_Deref_post_opt_z_bytecap_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl,__bytecap_impl(size)) _Post_valid_impl_) + +#define _Deref_post_z_cap_c_(size) _SAL1_1_Source_(_Deref_post_z_cap_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl,__cap_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_z_cap_c_(size) _SAL1_1_Source_(_Deref_post_opt_z_cap_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl,__cap_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_z_bytecap_c_(size) _SAL1_1_Source_(_Deref_post_z_bytecap_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_z_bytecap_c_(size) _SAL1_1_Source_(_Deref_post_opt_z_bytecap_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Post_valid_impl_) + +#define _Deref_post_z_cap_x_(size) _SAL1_1_Source_(_Deref_post_z_cap_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl,__cap_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_z_cap_x_(size) _SAL1_1_Source_(_Deref_post_opt_z_cap_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl,__cap_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_z_bytecap_x_(size) _SAL1_1_Source_(_Deref_post_z_bytecap_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_z_bytecap_x_(size) _SAL1_1_Source_(_Deref_post_opt_z_bytecap_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Post_valid_impl_) + +// known capacity and valid but unknown readable extent +#define _Deref_post_valid_cap_(size) _SAL1_1_Source_(_Deref_post_valid_cap_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__cap_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_valid_cap_(size) _SAL1_1_Source_(_Deref_post_opt_valid_cap_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__cap_impl(size)) _Post_valid_impl_) +#define _Deref_post_valid_bytecap_(size) _SAL1_1_Source_(_Deref_post_valid_bytecap_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecap_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_valid_bytecap_(size) _SAL1_1_Source_(_Deref_post_opt_valid_bytecap_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecap_impl(size)) _Post_valid_impl_) + +#define _Deref_post_valid_cap_c_(size) _SAL1_1_Source_(_Deref_post_valid_cap_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__cap_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_valid_cap_c_(size) _SAL1_1_Source_(_Deref_post_opt_valid_cap_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__cap_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_valid_bytecap_c_(size) _SAL1_1_Source_(_Deref_post_valid_bytecap_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecap_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_valid_bytecap_c_(size) _SAL1_1_Source_(_Deref_post_opt_valid_bytecap_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecap_c_impl(size)) _Post_valid_impl_) + +#define _Deref_post_valid_cap_x_(size) _SAL1_1_Source_(_Deref_post_valid_cap_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__cap_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_valid_cap_x_(size) _SAL1_1_Source_(_Deref_post_opt_valid_cap_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__cap_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_valid_bytecap_x_(size) _SAL1_1_Source_(_Deref_post_valid_bytecap_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecap_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_valid_bytecap_x_(size) _SAL1_1_Source_(_Deref_post_opt_valid_bytecap_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecap_x_impl(size)) _Post_valid_impl_) + +// e.g. HRESULT HrAllocateZeroInitializedMemory( size_t cb, _Out_ _Deref_post_bytecount_(cb) void** ppv ); +// valid buffer extent is described by another parameter +#define _Deref_post_count_(size) _SAL1_1_Source_(_Deref_post_count_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__count_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_count_(size) _SAL1_1_Source_(_Deref_post_opt_count_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__count_impl(size)) _Post_valid_impl_) +#define _Deref_post_bytecount_(size) _SAL1_1_Source_(_Deref_post_bytecount_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecount_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_bytecount_(size) _SAL1_1_Source_(_Deref_post_opt_bytecount_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecount_impl(size)) _Post_valid_impl_) + +// buffer capacity is described by a constant expression +#define _Deref_post_count_c_(size) _SAL1_1_Source_(_Deref_post_count_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__count_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_count_c_(size) _SAL1_1_Source_(_Deref_post_opt_count_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__count_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_bytecount_c_(size) _SAL1_1_Source_(_Deref_post_bytecount_c_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecount_c_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_bytecount_c_(size) _SAL1_1_Source_(_Deref_post_opt_bytecount_c_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecount_c_impl(size)) _Post_valid_impl_) + +// buffer capacity is described by a complex expression +#define _Deref_post_count_x_(size) _SAL1_1_Source_(_Deref_post_count_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__count_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_count_x_(size) _SAL1_1_Source_(_Deref_post_opt_count_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__count_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_bytecount_x_(size) _SAL1_1_Source_(_Deref_post_bytecount_x_, (size), _Deref_post1_impl_(__notnull_impl_notref) _Deref_post1_impl_(__bytecount_x_impl(size)) _Post_valid_impl_) +#define _Deref_post_opt_bytecount_x_(size) _SAL1_1_Source_(_Deref_post_opt_bytecount_x_, (size), _Deref_post1_impl_(__maybenull_impl_notref) _Deref_post1_impl_(__bytecount_x_impl(size)) _Post_valid_impl_) + +// e.g. void GetStrings( _Out_count_(cElems) _Deref_post_valid_ LPSTR const rgStr[], size_t cElems ); +#define _Deref_post_valid_ _SAL1_1_Source_(_Deref_post_valid_, (), _Deref_post1_impl_(__notnull_impl_notref) _Post_valid_impl_) +#define _Deref_post_opt_valid_ _SAL1_1_Source_(_Deref_post_opt_valid_, (), _Deref_post1_impl_(__maybenull_impl_notref) _Post_valid_impl_) + +#define _Deref_post_notnull_ _SAL1_1_Source_(_Deref_post_notnull_, (), _Deref_post1_impl_(__notnull_impl_notref)) +#define _Deref_post_maybenull_ _SAL1_1_Source_(_Deref_post_maybenull_, (), _Deref_post1_impl_(__maybenull_impl_notref)) +#define _Deref_post_null_ _SAL1_1_Source_(_Deref_post_null_, (), _Deref_post1_impl_(__null_impl_notref)) + +// +// _Deref_ret_ --- +// + +#define _Deref_ret_z_ _SAL1_1_Source_(_Deref_ret_z_, (), _Deref_ret1_impl_(__notnull_impl_notref) _Deref_ret1_impl_(__zterm_impl)) +#define _Deref_ret_opt_z_ _SAL1_1_Source_(_Deref_ret_opt_z_, (), _Deref_ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__zterm_impl)) + +// +// special _Deref_ --- +// +#define _Deref2_pre_readonly_ _SAL1_1_Source_(_Deref2_pre_readonly_, (), _Deref2_pre1_impl_(__readaccess_impl_notref)) + +// +// _Ret_ --- +// + +// e.g. _Ret_opt_valid_ LPSTR void* CloneSTR( _Pre_valid_ LPSTR src ); +#define _Ret_opt_valid_ _SAL1_1_Source_(_Ret_opt_valid_, (), _Ret1_impl_(__maybenull_impl_notref) _Ret_valid_impl_) +#define _Ret_opt_z_ _SAL1_1_Source_(_Ret_opt_z_, (), _Ret2_impl_(__maybenull_impl,__zterm_impl) _Ret_valid_impl_) + +// e.g. _Ret_opt_bytecap_(cb) void* AllocateMemory( size_t cb ); +// Buffer capacity is described by another parameter +#define _Ret_cap_(size) _SAL1_1_Source_(_Ret_cap_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__cap_impl(size))) +#define _Ret_opt_cap_(size) _SAL1_1_Source_(_Ret_opt_cap_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__cap_impl(size))) +#define _Ret_bytecap_(size) _SAL1_1_Source_(_Ret_bytecap_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__bytecap_impl(size))) +#define _Ret_opt_bytecap_(size) _SAL1_1_Source_(_Ret_opt_bytecap_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__bytecap_impl(size))) + +// Buffer capacity is described by a constant expression +#define _Ret_cap_c_(size) _SAL1_1_Source_(_Ret_cap_c_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__cap_c_impl(size))) +#define _Ret_opt_cap_c_(size) _SAL1_1_Source_(_Ret_opt_cap_c_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__cap_c_impl(size))) +#define _Ret_bytecap_c_(size) _SAL1_1_Source_(_Ret_bytecap_c_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__bytecap_c_impl(size))) +#define _Ret_opt_bytecap_c_(size) _SAL1_1_Source_(_Ret_opt_bytecap_c_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__bytecap_c_impl(size))) + +// Buffer capacity is described by a complex condition +#define _Ret_cap_x_(size) _SAL1_1_Source_(_Ret_cap_x_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__cap_x_impl(size))) +#define _Ret_opt_cap_x_(size) _SAL1_1_Source_(_Ret_opt_cap_x_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__cap_x_impl(size))) +#define _Ret_bytecap_x_(size) _SAL1_1_Source_(_Ret_bytecap_x_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__bytecap_x_impl(size))) +#define _Ret_opt_bytecap_x_(size) _SAL1_1_Source_(_Ret_opt_bytecap_x_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__bytecap_x_impl(size))) + +// return value is nullterminated and capacity is given by another parameter +#define _Ret_z_cap_(size) _SAL1_1_Source_(_Ret_z_cap_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret2_impl_(__zterm_impl,__cap_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_z_cap_(size) _SAL1_1_Source_(_Ret_opt_z_cap_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret2_impl_(__zterm_impl,__cap_impl(size)) _Ret_valid_impl_) +#define _Ret_z_bytecap_(size) _SAL1_1_Source_(_Ret_z_bytecap_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret2_impl_(__zterm_impl,__bytecap_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_z_bytecap_(size) _SAL1_1_Source_(_Ret_opt_z_bytecap_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret2_impl_(__zterm_impl,__bytecap_impl(size)) _Ret_valid_impl_) + +// e.g. _Ret_opt_bytecount_(cb) void* AllocateZeroInitializedMemory( size_t cb ); +// Valid Buffer extent is described by another parameter +#define _Ret_count_(size) _SAL1_1_Source_(_Ret_count_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__count_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_count_(size) _SAL1_1_Source_(_Ret_opt_count_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__count_impl(size)) _Ret_valid_impl_) +#define _Ret_bytecount_(size) _SAL1_1_Source_(_Ret_bytecount_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__bytecount_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_bytecount_(size) _SAL1_1_Source_(_Ret_opt_bytecount_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__bytecount_impl(size)) _Ret_valid_impl_) + +// Valid Buffer extent is described by a constant expression +#define _Ret_count_c_(size) _SAL1_1_Source_(_Ret_count_c_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__count_c_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_count_c_(size) _SAL1_1_Source_(_Ret_opt_count_c_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__count_c_impl(size)) _Ret_valid_impl_) +#define _Ret_bytecount_c_(size) _SAL1_1_Source_(_Ret_bytecount_c_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__bytecount_c_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_bytecount_c_(size) _SAL1_1_Source_(_Ret_opt_bytecount_c_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__bytecount_c_impl(size)) _Ret_valid_impl_) + +// Valid Buffer extent is described by a complex expression +#define _Ret_count_x_(size) _SAL1_1_Source_(_Ret_count_x_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__count_x_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_count_x_(size) _SAL1_1_Source_(_Ret_opt_count_x_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__count_x_impl(size)) _Ret_valid_impl_) +#define _Ret_bytecount_x_(size) _SAL1_1_Source_(_Ret_bytecount_x_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret1_impl_(__bytecount_x_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_bytecount_x_(size) _SAL1_1_Source_(_Ret_opt_bytecount_x_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret1_impl_(__bytecount_x_impl(size)) _Ret_valid_impl_) + +// return value is nullterminated and length is given by another parameter +#define _Ret_z_count_(size) _SAL1_1_Source_(_Ret_z_count_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret2_impl_(__zterm_impl,__count_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_z_count_(size) _SAL1_1_Source_(_Ret_opt_z_count_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret2_impl_(__zterm_impl,__count_impl(size)) _Ret_valid_impl_) +#define _Ret_z_bytecount_(size) _SAL1_1_Source_(_Ret_z_bytecount_, (size), _Ret1_impl_(__notnull_impl_notref) _Ret2_impl_(__zterm_impl,__bytecount_impl(size)) _Ret_valid_impl_) +#define _Ret_opt_z_bytecount_(size) _SAL1_1_Source_(_Ret_opt_z_bytecount_, (size), _Ret1_impl_(__maybenull_impl_notref) _Ret2_impl_(__zterm_impl,__bytecount_impl(size)) _Ret_valid_impl_) + + +// _Pre_ annotations --- +#define _Pre_opt_z_ _SAL1_1_Source_(_Pre_opt_z_, (), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__zterm_impl) _Pre_valid_impl_) + +// restrict access rights +#define _Pre_readonly_ _SAL1_1_Source_(_Pre_readonly_, (), _Pre1_impl_(__readaccess_impl_notref)) +#define _Pre_writeonly_ _SAL1_1_Source_(_Pre_writeonly_, (), _Pre1_impl_(__writeaccess_impl_notref)) + +// e.g. void FreeMemory( _Pre_bytecap_(cb) _Post_ptr_invalid_ void* pv, size_t cb ); +// buffer capacity described by another parameter +#define _Pre_cap_(size) _SAL1_1_Source_(_Pre_cap_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_impl(size))) +#define _Pre_opt_cap_(size) _SAL1_1_Source_(_Pre_opt_cap_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_impl(size))) +#define _Pre_bytecap_(size) _SAL1_1_Source_(_Pre_bytecap_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecap_impl(size))) +#define _Pre_opt_bytecap_(size) _SAL1_1_Source_(_Pre_opt_bytecap_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecap_impl(size))) + +// buffer capacity described by a constant expression +#define _Pre_cap_c_(size) _SAL1_1_Source_(_Pre_cap_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_c_impl(size))) +#define _Pre_opt_cap_c_(size) _SAL1_1_Source_(_Pre_opt_cap_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_c_impl(size))) +#define _Pre_bytecap_c_(size) _SAL1_1_Source_(_Pre_bytecap_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecap_c_impl(size))) +#define _Pre_opt_bytecap_c_(size) _SAL1_1_Source_(_Pre_opt_bytecap_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecap_c_impl(size))) +#define _Pre_cap_c_one_ _SAL1_1_Source_(_Pre_cap_c_one_, (), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_c_one_notref_impl)) +#define _Pre_opt_cap_c_one_ _SAL1_1_Source_(_Pre_opt_cap_c_one_, (), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_c_one_notref_impl)) + +// buffer capacity is described by another parameter multiplied by a constant expression +#define _Pre_cap_m_(mult,size) _SAL1_1_Source_(_Pre_cap_m_, (mult,size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__mult_impl(mult,size))) +#define _Pre_opt_cap_m_(mult,size) _SAL1_1_Source_(_Pre_opt_cap_m_, (mult,size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__mult_impl(mult,size))) + +// buffer capacity described by size of other buffer, only used by dangerous legacy APIs +// e.g. int strcpy(_Pre_cap_for_(src) char* dst, const char* src); +#define _Pre_cap_for_(param) _SAL1_1_Source_(_Pre_cap_for_, (param), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_for_impl(param))) +#define _Pre_opt_cap_for_(param) _SAL1_1_Source_(_Pre_opt_cap_for_, (param), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_for_impl(param))) + +// buffer capacity described by a complex condition +#define _Pre_cap_x_(size) _SAL1_1_Source_(_Pre_cap_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_x_impl(size))) +#define _Pre_opt_cap_x_(size) _SAL1_1_Source_(_Pre_opt_cap_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_x_impl(size))) +#define _Pre_bytecap_x_(size) _SAL1_1_Source_(_Pre_bytecap_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecap_x_impl(size))) +#define _Pre_opt_bytecap_x_(size) _SAL1_1_Source_(_Pre_opt_bytecap_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecap_x_impl(size))) + +// buffer capacity described by the difference to another pointer parameter +#define _Pre_ptrdiff_cap_(ptr) _SAL1_1_Source_(_Pre_ptrdiff_cap_, (ptr), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_x_impl(__ptrdiff(ptr)))) +#define _Pre_opt_ptrdiff_cap_(ptr) _SAL1_1_Source_(_Pre_opt_ptrdiff_cap_, (ptr), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_x_impl(__ptrdiff(ptr)))) + +// e.g. void AppendStr( _Pre_z_ const char* szFrom, _Pre_z_cap_(cchTo) _Post_z_ char* szTo, size_t cchTo ); +#define _Pre_z_cap_(size) _SAL1_1_Source_(_Pre_z_cap_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_z_cap_(size) _SAL1_1_Source_(_Pre_opt_z_cap_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_impl(size)) _Pre_valid_impl_) +#define _Pre_z_bytecap_(size) _SAL1_1_Source_(_Pre_z_bytecap_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_z_bytecap_(size) _SAL1_1_Source_(_Pre_opt_z_bytecap_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_impl(size)) _Pre_valid_impl_) + +#define _Pre_z_cap_c_(size) _SAL1_1_Source_(_Pre_z_cap_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_z_cap_c_(size) _SAL1_1_Source_(_Pre_opt_z_cap_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_c_impl(size)) _Pre_valid_impl_) +#define _Pre_z_bytecap_c_(size) _SAL1_1_Source_(_Pre_z_bytecap_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_z_bytecap_c_(size) _SAL1_1_Source_(_Pre_opt_z_bytecap_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_c_impl(size)) _Pre_valid_impl_) + +#define _Pre_z_cap_x_(size) _SAL1_1_Source_(_Pre_z_cap_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_x_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_z_cap_x_(size) _SAL1_1_Source_(_Pre_opt_z_cap_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__cap_x_impl(size)) _Pre_valid_impl_) +#define _Pre_z_bytecap_x_(size) _SAL1_1_Source_(_Pre_z_bytecap_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_z_bytecap_x_(size) _SAL1_1_Source_(_Pre_opt_z_bytecap_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre2_impl_(__zterm_impl,__bytecap_x_impl(size)) _Pre_valid_impl_) + +// known capacity and valid but unknown readable extent +#define _Pre_valid_cap_(size) _SAL1_1_Source_(_Pre_valid_cap_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_valid_cap_(size) _SAL1_1_Source_(_Pre_opt_valid_cap_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_impl(size)) _Pre_valid_impl_) +#define _Pre_valid_bytecap_(size) _SAL1_1_Source_(_Pre_valid_bytecap_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecap_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_valid_bytecap_(size) _SAL1_1_Source_(_Pre_opt_valid_bytecap_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecap_impl(size)) _Pre_valid_impl_) + +#define _Pre_valid_cap_c_(size) _SAL1_1_Source_(_Pre_valid_cap_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_c_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_valid_cap_c_(size) _SAL1_1_Source_(_Pre_opt_valid_cap_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_c_impl(size)) _Pre_valid_impl_) +#define _Pre_valid_bytecap_c_(size) _SAL1_1_Source_(_Pre_valid_bytecap_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecap_c_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_valid_bytecap_c_(size) _SAL1_1_Source_(_Pre_opt_valid_bytecap_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecap_c_impl(size)) _Pre_valid_impl_) + +#define _Pre_valid_cap_x_(size) _SAL1_1_Source_(_Pre_valid_cap_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_x_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_valid_cap_x_(size) _SAL1_1_Source_(_Pre_opt_valid_cap_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_x_impl(size)) _Pre_valid_impl_) +#define _Pre_valid_bytecap_x_(size) _SAL1_1_Source_(_Pre_valid_bytecap_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecap_x_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_valid_bytecap_x_(size) _SAL1_1_Source_(_Pre_opt_valid_bytecap_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecap_x_impl(size)) _Pre_valid_impl_) + +// e.g. void AppendCharRange( _Pre_count_(cchFrom) const char* rgFrom, size_t cchFrom, _Out_z_cap_(cchTo) char* szTo, size_t cchTo ); +// Valid buffer extent described by another parameter +#define _Pre_count_(size) _SAL1_1_Source_(_Pre_count_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__count_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_count_(size) _SAL1_1_Source_(_Pre_opt_count_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__count_impl(size)) _Pre_valid_impl_) +#define _Pre_bytecount_(size) _SAL1_1_Source_(_Pre_bytecount_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecount_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_bytecount_(size) _SAL1_1_Source_(_Pre_opt_bytecount_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecount_impl(size)) _Pre_valid_impl_) + +// Valid buffer extent described by a constant expression +#define _Pre_count_c_(size) _SAL1_1_Source_(_Pre_count_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__count_c_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_count_c_(size) _SAL1_1_Source_(_Pre_opt_count_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__count_c_impl(size)) _Pre_valid_impl_) +#define _Pre_bytecount_c_(size) _SAL1_1_Source_(_Pre_bytecount_c_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecount_c_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_bytecount_c_(size) _SAL1_1_Source_(_Pre_opt_bytecount_c_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecount_c_impl(size)) _Pre_valid_impl_) + +// Valid buffer extent described by a complex expression +#define _Pre_count_x_(size) _SAL1_1_Source_(_Pre_count_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__count_x_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_count_x_(size) _SAL1_1_Source_(_Pre_opt_count_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__count_x_impl(size)) _Pre_valid_impl_) +#define _Pre_bytecount_x_(size) _SAL1_1_Source_(_Pre_bytecount_x_, (size), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__bytecount_x_impl(size)) _Pre_valid_impl_) +#define _Pre_opt_bytecount_x_(size) _SAL1_1_Source_(_Pre_opt_bytecount_x_, (size), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__bytecount_x_impl(size)) _Pre_valid_impl_) + +// Valid buffer extent described by the difference to another pointer parameter +#define _Pre_ptrdiff_count_(ptr) _SAL1_1_Source_(_Pre_ptrdiff_count_, (ptr), _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__count_x_impl(__ptrdiff(ptr))) _Pre_valid_impl_) +#define _Pre_opt_ptrdiff_count_(ptr) _SAL1_1_Source_(_Pre_opt_ptrdiff_count_, (ptr), _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__count_x_impl(__ptrdiff(ptr))) _Pre_valid_impl_) + + +// char * strncpy(_Out_cap_(_Count) _Post_maybez_ char * _Dest, _In_z_ const char * _Source, _In_ size_t _Count) +// buffer maybe zero-terminated after the call +#define _Post_maybez_ _SAL_L_Source_(_Post_maybez_, (), _Post1_impl_(__maybezterm_impl)) + +// e.g. SIZE_T HeapSize( _In_ HANDLE hHeap, DWORD dwFlags, _Pre_notnull_ _Post_bytecap_(return) LPCVOID lpMem ); +#define _Post_cap_(size) _SAL1_1_Source_(_Post_cap_, (size), _Post1_impl_(__cap_impl(size))) +#define _Post_bytecap_(size) _SAL1_1_Source_(_Post_bytecap_, (size), _Post1_impl_(__bytecap_impl(size))) + +// e.g. int strlen( _In_z_ _Post_count_(return+1) const char* sz ); +#define _Post_count_(size) _SAL1_1_Source_(_Post_count_, (size), _Post1_impl_(__count_impl(size)) _Post_valid_impl_) +#define _Post_bytecount_(size) _SAL1_1_Source_(_Post_bytecount_, (size), _Post1_impl_(__bytecount_impl(size)) _Post_valid_impl_) +#define _Post_count_c_(size) _SAL1_1_Source_(_Post_count_c_, (size), _Post1_impl_(__count_c_impl(size)) _Post_valid_impl_) +#define _Post_bytecount_c_(size) _SAL1_1_Source_(_Post_bytecount_c_, (size), _Post1_impl_(__bytecount_c_impl(size)) _Post_valid_impl_) +#define _Post_count_x_(size) _SAL1_1_Source_(_Post_count_x_, (size), _Post1_impl_(__count_x_impl(size)) _Post_valid_impl_) +#define _Post_bytecount_x_(size) _SAL1_1_Source_(_Post_bytecount_x_, (size), _Post1_impl_(__bytecount_x_impl(size)) _Post_valid_impl_) + +// e.g. size_t CopyStr( _In_z_ const char* szFrom, _Pre_cap_(cch) _Post_z_count_(return+1) char* szFrom, size_t cchFrom ); +#define _Post_z_count_(size) _SAL1_1_Source_(_Post_z_count_, (size), _Post2_impl_(__zterm_impl,__count_impl(size)) _Post_valid_impl_) +#define _Post_z_bytecount_(size) _SAL1_1_Source_(_Post_z_bytecount_, (size), _Post2_impl_(__zterm_impl,__bytecount_impl(size)) _Post_valid_impl_) +#define _Post_z_count_c_(size) _SAL1_1_Source_(_Post_z_count_c_, (size), _Post2_impl_(__zterm_impl,__count_c_impl(size)) _Post_valid_impl_) +#define _Post_z_bytecount_c_(size) _SAL1_1_Source_(_Post_z_bytecount_c_, (size), _Post2_impl_(__zterm_impl,__bytecount_c_impl(size)) _Post_valid_impl_) +#define _Post_z_count_x_(size) _SAL1_1_Source_(_Post_z_count_x_, (size), _Post2_impl_(__zterm_impl,__count_x_impl(size)) _Post_valid_impl_) +#define _Post_z_bytecount_x_(size) _SAL1_1_Source_(_Post_z_bytecount_x_, (size), _Post2_impl_(__zterm_impl,__bytecount_x_impl(size)) _Post_valid_impl_) + +// +// _Prepost_ --- +// +// describing conditions that hold before and after the function call + +#define _Prepost_opt_z_ _SAL1_1_Source_(_Prepost_opt_z_, (), _Pre_opt_z_ _Post_z_) + +#define _Prepost_count_(size) _SAL1_1_Source_(_Prepost_count_, (size), _Pre_count_(size) _Post_count_(size)) +#define _Prepost_opt_count_(size) _SAL1_1_Source_(_Prepost_opt_count_, (size), _Pre_opt_count_(size) _Post_count_(size)) +#define _Prepost_bytecount_(size) _SAL1_1_Source_(_Prepost_bytecount_, (size), _Pre_bytecount_(size) _Post_bytecount_(size)) +#define _Prepost_opt_bytecount_(size) _SAL1_1_Source_(_Prepost_opt_bytecount_, (size), _Pre_opt_bytecount_(size) _Post_bytecount_(size)) +#define _Prepost_count_c_(size) _SAL1_1_Source_(_Prepost_count_c_, (size), _Pre_count_c_(size) _Post_count_c_(size)) +#define _Prepost_opt_count_c_(size) _SAL1_1_Source_(_Prepost_opt_count_c_, (size), _Pre_opt_count_c_(size) _Post_count_c_(size)) +#define _Prepost_bytecount_c_(size) _SAL1_1_Source_(_Prepost_bytecount_c_, (size), _Pre_bytecount_c_(size) _Post_bytecount_c_(size)) +#define _Prepost_opt_bytecount_c_(size) _SAL1_1_Source_(_Prepost_opt_bytecount_c_, (size), _Pre_opt_bytecount_c_(size) _Post_bytecount_c_(size)) +#define _Prepost_count_x_(size) _SAL1_1_Source_(_Prepost_count_x_, (size), _Pre_count_x_(size) _Post_count_x_(size)) +#define _Prepost_opt_count_x_(size) _SAL1_1_Source_(_Prepost_opt_count_x_, (size), _Pre_opt_count_x_(size) _Post_count_x_(size)) +#define _Prepost_bytecount_x_(size) _SAL1_1_Source_(_Prepost_bytecount_x_, (size), _Pre_bytecount_x_(size) _Post_bytecount_x_(size)) +#define _Prepost_opt_bytecount_x_(size) _SAL1_1_Source_(_Prepost_opt_bytecount_x_, (size), _Pre_opt_bytecount_x_(size) _Post_bytecount_x_(size)) + +#define _Prepost_valid_ _SAL1_1_Source_(_Prepost_valid_, (), _Pre_valid_ _Post_valid_) +#define _Prepost_opt_valid_ _SAL1_1_Source_(_Prepost_opt_valid_, (), _Pre_opt_valid_ _Post_valid_) + +// +// _Deref_<both> --- +// +// short version for _Deref_pre_<ann> _Deref_post_<ann> +// describing conditions for array elements or dereferenced pointer parameters that hold before and after the call + +#define _Deref_prepost_z_ _SAL1_1_Source_(_Deref_prepost_z_, (), _Deref_pre_z_ _Deref_post_z_) +#define _Deref_prepost_opt_z_ _SAL1_1_Source_(_Deref_prepost_opt_z_, (), _Deref_pre_opt_z_ _Deref_post_opt_z_) + +#define _Deref_prepost_cap_(size) _SAL1_1_Source_(_Deref_prepost_cap_, (size), _Deref_pre_cap_(size) _Deref_post_cap_(size)) +#define _Deref_prepost_opt_cap_(size) _SAL1_1_Source_(_Deref_prepost_opt_cap_, (size), _Deref_pre_opt_cap_(size) _Deref_post_opt_cap_(size)) +#define _Deref_prepost_bytecap_(size) _SAL1_1_Source_(_Deref_prepost_bytecap_, (size), _Deref_pre_bytecap_(size) _Deref_post_bytecap_(size)) +#define _Deref_prepost_opt_bytecap_(size) _SAL1_1_Source_(_Deref_prepost_opt_bytecap_, (size), _Deref_pre_opt_bytecap_(size) _Deref_post_opt_bytecap_(size)) + +#define _Deref_prepost_cap_x_(size) _SAL1_1_Source_(_Deref_prepost_cap_x_, (size), _Deref_pre_cap_x_(size) _Deref_post_cap_x_(size)) +#define _Deref_prepost_opt_cap_x_(size) _SAL1_1_Source_(_Deref_prepost_opt_cap_x_, (size), _Deref_pre_opt_cap_x_(size) _Deref_post_opt_cap_x_(size)) +#define _Deref_prepost_bytecap_x_(size) _SAL1_1_Source_(_Deref_prepost_bytecap_x_, (size), _Deref_pre_bytecap_x_(size) _Deref_post_bytecap_x_(size)) +#define _Deref_prepost_opt_bytecap_x_(size) _SAL1_1_Source_(_Deref_prepost_opt_bytecap_x_, (size), _Deref_pre_opt_bytecap_x_(size) _Deref_post_opt_bytecap_x_(size)) + +#define _Deref_prepost_z_cap_(size) _SAL1_1_Source_(_Deref_prepost_z_cap_, (size), _Deref_pre_z_cap_(size) _Deref_post_z_cap_(size)) +#define _Deref_prepost_opt_z_cap_(size) _SAL1_1_Source_(_Deref_prepost_opt_z_cap_, (size), _Deref_pre_opt_z_cap_(size) _Deref_post_opt_z_cap_(size)) +#define _Deref_prepost_z_bytecap_(size) _SAL1_1_Source_(_Deref_prepost_z_bytecap_, (size), _Deref_pre_z_bytecap_(size) _Deref_post_z_bytecap_(size)) +#define _Deref_prepost_opt_z_bytecap_(size) _SAL1_1_Source_(_Deref_prepost_opt_z_bytecap_, (size), _Deref_pre_opt_z_bytecap_(size) _Deref_post_opt_z_bytecap_(size)) + +#define _Deref_prepost_valid_cap_(size) _SAL1_1_Source_(_Deref_prepost_valid_cap_, (size), _Deref_pre_valid_cap_(size) _Deref_post_valid_cap_(size)) +#define _Deref_prepost_opt_valid_cap_(size) _SAL1_1_Source_(_Deref_prepost_opt_valid_cap_, (size), _Deref_pre_opt_valid_cap_(size) _Deref_post_opt_valid_cap_(size)) +#define _Deref_prepost_valid_bytecap_(size) _SAL1_1_Source_(_Deref_prepost_valid_bytecap_, (size), _Deref_pre_valid_bytecap_(size) _Deref_post_valid_bytecap_(size)) +#define _Deref_prepost_opt_valid_bytecap_(size) _SAL1_1_Source_(_Deref_prepost_opt_valid_bytecap_, (size), _Deref_pre_opt_valid_bytecap_(size) _Deref_post_opt_valid_bytecap_(size)) + +#define _Deref_prepost_valid_cap_x_(size) _SAL1_1_Source_(_Deref_prepost_valid_cap_x_, (size), _Deref_pre_valid_cap_x_(size) _Deref_post_valid_cap_x_(size)) +#define _Deref_prepost_opt_valid_cap_x_(size) _SAL1_1_Source_(_Deref_prepost_opt_valid_cap_x_, (size), _Deref_pre_opt_valid_cap_x_(size) _Deref_post_opt_valid_cap_x_(size)) +#define _Deref_prepost_valid_bytecap_x_(size) _SAL1_1_Source_(_Deref_prepost_valid_bytecap_x_, (size), _Deref_pre_valid_bytecap_x_(size) _Deref_post_valid_bytecap_x_(size)) +#define _Deref_prepost_opt_valid_bytecap_x_(size) _SAL1_1_Source_(_Deref_prepost_opt_valid_bytecap_x_, (size), _Deref_pre_opt_valid_bytecap_x_(size) _Deref_post_opt_valid_bytecap_x_(size)) + +#define _Deref_prepost_count_(size) _SAL1_1_Source_(_Deref_prepost_count_, (size), _Deref_pre_count_(size) _Deref_post_count_(size)) +#define _Deref_prepost_opt_count_(size) _SAL1_1_Source_(_Deref_prepost_opt_count_, (size), _Deref_pre_opt_count_(size) _Deref_post_opt_count_(size)) +#define _Deref_prepost_bytecount_(size) _SAL1_1_Source_(_Deref_prepost_bytecount_, (size), _Deref_pre_bytecount_(size) _Deref_post_bytecount_(size)) +#define _Deref_prepost_opt_bytecount_(size) _SAL1_1_Source_(_Deref_prepost_opt_bytecount_, (size), _Deref_pre_opt_bytecount_(size) _Deref_post_opt_bytecount_(size)) + +#define _Deref_prepost_count_x_(size) _SAL1_1_Source_(_Deref_prepost_count_x_, (size), _Deref_pre_count_x_(size) _Deref_post_count_x_(size)) +#define _Deref_prepost_opt_count_x_(size) _SAL1_1_Source_(_Deref_prepost_opt_count_x_, (size), _Deref_pre_opt_count_x_(size) _Deref_post_opt_count_x_(size)) +#define _Deref_prepost_bytecount_x_(size) _SAL1_1_Source_(_Deref_prepost_bytecount_x_, (size), _Deref_pre_bytecount_x_(size) _Deref_post_bytecount_x_(size)) +#define _Deref_prepost_opt_bytecount_x_(size) _SAL1_1_Source_(_Deref_prepost_opt_bytecount_x_, (size), _Deref_pre_opt_bytecount_x_(size) _Deref_post_opt_bytecount_x_(size)) + +#define _Deref_prepost_valid_ _SAL1_1_Source_(_Deref_prepost_valid_, (), _Deref_pre_valid_ _Deref_post_valid_) +#define _Deref_prepost_opt_valid_ _SAL1_1_Source_(_Deref_prepost_opt_valid_, (), _Deref_pre_opt_valid_ _Deref_post_opt_valid_) + +// +// _Deref_<miscellaneous> +// +// used with references to arrays + +#define _Deref_out_z_cap_c_(size) _SAL1_1_Source_(_Deref_out_z_cap_c_, (size), _Deref_pre_cap_c_(size) _Deref_post_z_) +#define _Deref_inout_z_cap_c_(size) _SAL1_1_Source_(_Deref_inout_z_cap_c_, (size), _Deref_pre_z_cap_c_(size) _Deref_post_z_) +#define _Deref_out_z_bytecap_c_(size) _SAL1_1_Source_(_Deref_out_z_bytecap_c_, (size), _Deref_pre_bytecap_c_(size) _Deref_post_z_) +#define _Deref_inout_z_bytecap_c_(size) _SAL1_1_Source_(_Deref_inout_z_bytecap_c_, (size), _Deref_pre_z_bytecap_c_(size) _Deref_post_z_) +#define _Deref_inout_z_ _SAL1_1_Source_(_Deref_inout_z_, (), _Deref_prepost_z_) + +#pragma endregion Input Buffer SAL 1 compatibility macros + + +//============================================================================ +// Implementation Layer: +//============================================================================ + + +// Naming conventions: +// A symbol the begins with _SA_ is for the machinery of creating any +// annotations; many of those come from sourceannotations.h in the case +// of attributes. + +// A symbol that ends with _impl is the very lowest level macro. It is +// not required to be a legal standalone annotation, and in the case +// of attribute annotations, usually is not. (In the case of some declspec +// annotations, it might be, but it should not be assumed so.) Those +// symols will be used in the _PreN..., _PostN... and _RetN... annotations +// to build up more complete annotations. + +// A symbol ending in _impl_ is reserved to the implementation as well, +// but it does form a complete annotation; usually they are used to build +// up even higher level annotations. + + +#if _USE_ATTRIBUTES_FOR_SAL || _USE_DECLSPECS_FOR_SAL // [ +// Sharable "_impl" macros: these can be shared between the various annotation +// forms but are part of the implementation of the macros. These are collected +// here to assure that only necessary differences in the annotations +// exist. + +#define _Always_impl_(annos) _Group_(annos _SAL_nop_impl_) _On_failure_impl_(annos _SAL_nop_impl_) +#define _Bound_impl_ _SA_annotes0(SAL_bound) +#define _Field_range_impl_(min,max) _Range_impl_(min,max) +#define _Literal_impl_ _SA_annotes1(SAL_constant, __yes) +#define _Maybenull_impl_ _SA_annotes1(SAL_null, __maybe) +#define _Maybevalid_impl_ _SA_annotes1(SAL_valid, __maybe) +#define _Must_inspect_impl_ _Post_impl_ _SA_annotes0(SAL_mustInspect) +#define _Notliteral_impl_ _SA_annotes1(SAL_constant, __no) +#define _Notnull_impl_ _SA_annotes1(SAL_null, __no) +#define _Notvalid_impl_ _SA_annotes1(SAL_valid, __no) +#define _NullNull_terminated_impl_ _Group_(_SA_annotes1(SAL_nullTerminated, __yes) _SA_annotes1(SAL_readableTo,inexpressibleCount("NullNull terminated string"))) +#define _Null_impl_ _SA_annotes1(SAL_null, __yes) +#define _Null_terminated_impl_ _SA_annotes1(SAL_nullTerminated, __yes) +#define _Out_impl_ _Pre1_impl_(__notnull_impl_notref) _Pre1_impl_(__cap_c_one_notref_impl) _Post_valid_impl_ +#define _Out_opt_impl_ _Pre1_impl_(__maybenull_impl_notref) _Pre1_impl_(__cap_c_one_notref_impl) _Post_valid_impl_ +#define _Points_to_data_impl_ _At_(*_Curr_, _SA_annotes1(SAL_mayBePointer, __no)) +#define _Post_satisfies_impl_(cond) _Post_impl_ _Satisfies_impl_(cond) +#define _Post_valid_impl_ _Post1_impl_(__valid_impl) +#define _Pre_satisfies_impl_(cond) _Pre_impl_ _Satisfies_impl_(cond) +#define _Pre_valid_impl_ _Pre1_impl_(__valid_impl) +#define _Range_impl_(min,max) _SA_annotes2(SAL_range, min, max) +#define _Readable_bytes_impl_(size) _SA_annotes1(SAL_readableTo, byteCount(size)) +#define _Readable_elements_impl_(size) _SA_annotes1(SAL_readableTo, elementCount(size)) +#define _Ret_valid_impl_ _Ret1_impl_(__valid_impl) +#define _Satisfies_impl_(cond) _SA_annotes1(SAL_satisfies, cond) +#define _Valid_impl_ _SA_annotes1(SAL_valid, __yes) +#define _Writable_bytes_impl_(size) _SA_annotes1(SAL_writableTo, byteCount(size)) +#define _Writable_elements_impl_(size) _SA_annotes1(SAL_writableTo, elementCount(size)) + +#define _In_range_impl_(min,max) _Pre_impl_ _Range_impl_(min,max) +#define _Out_range_impl_(min,max) _Post_impl_ _Range_impl_(min,max) +#define _Ret_range_impl_(min,max) _Post_impl_ _Range_impl_(min,max) +#define _Deref_in_range_impl_(min,max) _Deref_pre_impl_ _Range_impl_(min,max) +#define _Deref_out_range_impl_(min,max) _Deref_post_impl_ _Range_impl_(min,max) +#define _Deref_ret_range_impl_(min,max) _Deref_post_impl_ _Range_impl_(min,max) + +#define _Deref_pre_impl_ _Pre_impl_ _Notref_impl_ _Deref_impl_ +#define _Deref_post_impl_ _Post_impl_ _Notref_impl_ _Deref_impl_ + +// The following are for the implementation machinery, and are not +// suitable for annotating general code. +// We're tying to phase this out, someday. The parser quotes the param. +#define __AuToQuOtE _SA_annotes0(SAL_AuToQuOtE) + +// Normally the parser does some simple type checking of annotation params, +// defer that check to the plugin. +#define __deferTypecheck _SA_annotes0(SAL_deferTypecheck) + +#define _SA_SPECSTRIZE( x ) #x +#define _SAL_nop_impl_ /* nothing */ +#define __nop_impl(x) x +#endif + + +#if _USE_ATTRIBUTES_FOR_SAL // [ + +// Using attributes for sal + +#include "codeanalysis\sourceannotations.h" + + +#define _SA_annotes0(n) [SAL_annotes(Name=#n)] +#define _SA_annotes1(n,pp1) [SAL_annotes(Name=#n, p1=_SA_SPECSTRIZE(pp1))] +#define _SA_annotes2(n,pp1,pp2) [SAL_annotes(Name=#n, p1=_SA_SPECSTRIZE(pp1), p2=_SA_SPECSTRIZE(pp2))] +#define _SA_annotes3(n,pp1,pp2,pp3) [SAL_annotes(Name=#n, p1=_SA_SPECSTRIZE(pp1), p2=_SA_SPECSTRIZE(pp2), p3=_SA_SPECSTRIZE(pp3))] + +#define _Pre_impl_ [SAL_pre] +#define _Post_impl_ [SAL_post] +#define _Deref_impl_ [SAL_deref] +#define _Notref_impl_ [SAL_notref] + + +// Declare a function to be an annotation or primop (respectively). +// Done this way so that they don't appear in the regular compiler's +// namespace. +#define __ANNOTATION(fun) _SA_annotes0(SAL_annotation) void __SA_##fun; +#define __PRIMOP(type, fun) _SA_annotes0(SAL_primop) type __SA_##fun; +#define __QUALIFIER(fun) _SA_annotes0(SAL_qualifier) void __SA_##fun; + +// Benign declspec needed here for WindowsPREfast +#define __In_impl_ [SA_Pre(Valid=SA_Yes)] [SA_Pre(Deref=1, Notref=1, Access=SA_Read)] __declspec("SAL_pre SAL_valid") + +#elif _USE_DECLSPECS_FOR_SAL // ][ + +// Using declspecs for sal + +#define _SA_annotes0(n) __declspec(#n) +#define _SA_annotes1(n,pp1) __declspec(#n "(" _SA_SPECSTRIZE(pp1) ")" ) +#define _SA_annotes2(n,pp1,pp2) __declspec(#n "(" _SA_SPECSTRIZE(pp1) "," _SA_SPECSTRIZE(pp2) ")") +#define _SA_annotes3(n,pp1,pp2,pp3) __declspec(#n "(" _SA_SPECSTRIZE(pp1) "," _SA_SPECSTRIZE(pp2) "," _SA_SPECSTRIZE(pp3) ")") + +#define _Pre_impl_ _SA_annotes0(SAL_pre) +#define _Post_impl_ _SA_annotes0(SAL_post) +#define _Deref_impl_ _SA_annotes0(SAL_deref) +#define _Notref_impl_ _SA_annotes0(SAL_notref) + +// Declare a function to be an annotation or primop (respectively). +// Done this way so that they don't appear in the regular compiler's +// namespace. +#define __ANNOTATION(fun) _SA_annotes0(SAL_annotation) void __SA_##fun + +#define __PRIMOP(type, fun) _SA_annotes0(SAL_primop) type __SA_##fun + +#define __QUALIFIER(fun) _SA_annotes0(SAL_qualifier) void __SA_##fun; + +#define __In_impl_ _Pre_impl_ _SA_annotes0(SAL_valid) _Pre_impl_ _Deref_impl_ _Notref_impl_ _SA_annotes0(SAL_readonly) + +#else // ][ + +// Using "nothing" for sal + +#define _SA_annotes0(n) +#define _SA_annotes1(n,pp1) +#define _SA_annotes2(n,pp1,pp2) +#define _SA_annotes3(n,pp1,pp2,pp3) + +#define __ANNOTATION(fun) +#define __PRIMOP(type, fun) +#define __QUALIFIER(type, fun) + +#endif // ] + +#if _USE_ATTRIBUTES_FOR_SAL || _USE_DECLSPECS_FOR_SAL // [ + +// Declare annotations that need to be declared. +__ANNOTATION(SAL_useHeader(void)); +__ANNOTATION(SAL_bound(void)); +__ANNOTATION(SAL_allocator(void)); //??? resolve with PFD +__ANNOTATION(SAL_file_parser(__AuToQuOtE __In_impl_ char *, __In_impl_ char *)); +__ANNOTATION(SAL_source_code_content(__In_impl_ char *)); +__ANNOTATION(SAL_analysisHint(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_untrusted_data_source(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_untrusted_data_source_this(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_validated(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_validated_this(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_encoded(void)); +__ANNOTATION(SAL_adt(__AuToQuOtE __In_impl_ char *, __AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_add_adt_property(__AuToQuOtE __In_impl_ char *, __AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_remove_adt_property(__AuToQuOtE __In_impl_ char *, __AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_transfer_adt_property_from(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_post_type(__AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_volatile(void)); +__ANNOTATION(SAL_nonvolatile(void)); +__ANNOTATION(SAL_entrypoint(__AuToQuOtE __In_impl_ char *, __AuToQuOtE __In_impl_ char *)); +__ANNOTATION(SAL_blocksOn(__In_impl_ void*)); +__ANNOTATION(SAL_mustInspect(void)); + +// Only appears in model files, but needs to be declared. +__ANNOTATION(SAL_TypeName(__AuToQuOtE __In_impl_ char *)); + +// To be declared well-known soon. +__ANNOTATION(SAL_interlocked(void);) + +__QUALIFIER(SAL_name(__In_impl_ char *, __In_impl_ char *, __In_impl_ char *);) + +__PRIMOP(char *, _Macro_value_(__In_impl_ char *)); +__PRIMOP(int, _Macro_defined_(__In_impl_ char *)); +__PRIMOP(char *, _Strstr_(__In_impl_ char *, __In_impl_ char *)); + +#endif // ] + +#if _USE_ATTRIBUTES_FOR_SAL // [ + +#define _Check_return_impl_ [SA_Post(MustCheck=SA_Yes)] + +#define _Success_impl_(expr) [SA_Success(Condition=#expr)] +#define _On_failure_impl_(annos) [SAL_context(p1="SAL_failed")] _Group_(_Post_impl_ _Group_(annos _SAL_nop_impl_)) + +#define _Printf_format_string_impl_ [SA_FormatString(Style="printf")] +#define _Scanf_format_string_impl_ [SA_FormatString(Style="scanf")] +#define _Scanf_s_format_string_impl_ [SA_FormatString(Style="scanf_s")] + +#define _In_bound_impl_ [SA_PreBound(Deref=0)] +#define _Out_bound_impl_ [SA_PostBound(Deref=0)] +#define _Ret_bound_impl_ [SA_PostBound(Deref=0)] +#define _Deref_in_bound_impl_ [SA_PreBound(Deref=1)] +#define _Deref_out_bound_impl_ [SA_PostBound(Deref=1)] +#define _Deref_ret_bound_impl_ [SA_PostBound(Deref=1)] + +#define __valid_impl Valid=SA_Yes +#define __maybevalid_impl Valid=SA_Maybe +#define __notvalid_impl Valid=SA_No + +#define __null_impl Null=SA_Yes +#define __maybenull_impl Null=SA_Maybe +#define __notnull_impl Null=SA_No + +#define __null_impl_notref Null=SA_Yes,Notref=1 +#define __maybenull_impl_notref Null=SA_Maybe,Notref=1 +#define __notnull_impl_notref Null=SA_No,Notref=1 + +#define __zterm_impl NullTerminated=SA_Yes +#define __maybezterm_impl NullTerminated=SA_Maybe +#define __maybzterm_impl NullTerminated=SA_Maybe +#define __notzterm_impl NullTerminated=SA_No + +#define __readaccess_impl Access=SA_Read +#define __writeaccess_impl Access=SA_Write +#define __allaccess_impl Access=SA_ReadWrite + +#define __readaccess_impl_notref Access=SA_Read,Notref=1 +#define __writeaccess_impl_notref Access=SA_Write,Notref=1 +#define __allaccess_impl_notref Access=SA_ReadWrite,Notref=1 + +#if _MSC_VER >= 1610 // [ + +// For SAL2, we need to expect general expressions. + +#define __cap_impl(size) WritableElements="\n"#size +#define __bytecap_impl(size) WritableBytes="\n"#size +#define __bytecount_impl(size) ValidBytes="\n"#size +#define __count_impl(size) ValidElements="\n"#size + +#else // ][ + +#define __cap_impl(size) WritableElements=#size +#define __bytecap_impl(size) WritableBytes=#size +#define __bytecount_impl(size) ValidBytes=#size +#define __count_impl(size) ValidElements=#size + +#endif // ] + +#define __cap_c_impl(size) WritableElementsConst=size +#define __cap_c_one_notref_impl WritableElementsConst=1,Notref=1 +#define __cap_for_impl(param) WritableElementsLength=#param +#define __cap_x_impl(size) WritableElements="\n@"#size + +#define __bytecap_c_impl(size) WritableBytesConst=size +#define __bytecap_x_impl(size) WritableBytes="\n@"#size + +#define __mult_impl(mult,size) __cap_impl((mult)*(size)) + +#define __count_c_impl(size) ValidElementsConst=size +#define __count_x_impl(size) ValidElements="\n@"#size + +#define __bytecount_c_impl(size) ValidBytesConst=size +#define __bytecount_x_impl(size) ValidBytes="\n@"#size + + +#define _At_impl_(target, annos) [SAL_at(p1=#target)] _Group_(annos) +#define _At_buffer_impl_(target, iter, bound, annos) [SAL_at_buffer(p1=#target, p2=#iter, p3=#bound)] _Group_(annos) +#define _When_impl_(expr, annos) [SAL_when(p1=#expr)] _Group_(annos) + +#define _Group_impl_(annos) [SAL_begin] annos [SAL_end] +#define _GrouP_impl_(annos) [SAL_BEGIN] annos [SAL_END] + +#define _Use_decl_anno_impl_ _SA_annotes0(SAL_useHeader) // this is a special case! + +#define _Pre1_impl_(p1) [SA_Pre(p1)] +#define _Pre2_impl_(p1,p2) [SA_Pre(p1,p2)] +#define _Pre3_impl_(p1,p2,p3) [SA_Pre(p1,p2,p3)] + +#define _Post1_impl_(p1) [SA_Post(p1)] +#define _Post2_impl_(p1,p2) [SA_Post(p1,p2)] +#define _Post3_impl_(p1,p2,p3) [SA_Post(p1,p2,p3)] + +#define _Ret1_impl_(p1) [SA_Post(p1)] +#define _Ret2_impl_(p1,p2) [SA_Post(p1,p2)] +#define _Ret3_impl_(p1,p2,p3) [SA_Post(p1,p2,p3)] + +#define _Deref_pre1_impl_(p1) [SA_Pre(Deref=1,p1)] +#define _Deref_pre2_impl_(p1,p2) [SA_Pre(Deref=1,p1,p2)] +#define _Deref_pre3_impl_(p1,p2,p3) [SA_Pre(Deref=1,p1,p2,p3)] + + +#define _Deref_post1_impl_(p1) [SA_Post(Deref=1,p1)] +#define _Deref_post2_impl_(p1,p2) [SA_Post(Deref=1,p1,p2)] +#define _Deref_post3_impl_(p1,p2,p3) [SA_Post(Deref=1,p1,p2,p3)] + +#define _Deref_ret1_impl_(p1) [SA_Post(Deref=1,p1)] +#define _Deref_ret2_impl_(p1,p2) [SA_Post(Deref=1,p1,p2)] +#define _Deref_ret3_impl_(p1,p2,p3) [SA_Post(Deref=1,p1,p2,p3)] + +#define _Deref2_pre1_impl_(p1) [SA_Pre(Deref=2,Notref=1,p1)] +#define _Deref2_post1_impl_(p1) [SA_Post(Deref=2,Notref=1,p1)] +#define _Deref2_ret1_impl_(p1) [SA_Post(Deref=2,Notref=1,p1)] + +// Obsolete -- may be needed for transition to attributes. +#define __inner_typefix(ctype) [SAL_typefix(p1=_SA_SPECSTRIZE(ctype))] +#define __inner_exceptthat [SAL_except] + + +#elif _USE_DECLSPECS_FOR_SAL // ][ + +#define _Check_return_impl_ __post _SA_annotes0(SAL_checkReturn) + +#define _Success_impl_(expr) _SA_annotes1(SAL_success, expr) +#define _On_failure_impl_(annos) _SA_annotes1(SAL_context, SAL_failed) _Group_(_Post_impl_ _Group_(_SAL_nop_impl_ annos)) + +#define _Printf_format_string_impl_ _SA_annotes1(SAL_IsFormatString, "printf") +#define _Scanf_format_string_impl_ _SA_annotes1(SAL_IsFormatString, "scanf") +#define _Scanf_s_format_string_impl_ _SA_annotes1(SAL_IsFormatString, "scanf_s") + +#define _In_bound_impl_ _Pre_impl_ _Bound_impl_ +#define _Out_bound_impl_ _Post_impl_ _Bound_impl_ +#define _Ret_bound_impl_ _Post_impl_ _Bound_impl_ +#define _Deref_in_bound_impl_ _Deref_pre_impl_ _Bound_impl_ +#define _Deref_out_bound_impl_ _Deref_post_impl_ _Bound_impl_ +#define _Deref_ret_bound_impl_ _Deref_post_impl_ _Bound_impl_ + + +#define __null_impl _SA_annotes0(SAL_null) // _SA_annotes1(SAL_null, __yes) +#define __notnull_impl _SA_annotes0(SAL_notnull) // _SA_annotes1(SAL_null, __no) +#define __maybenull_impl _SA_annotes0(SAL_maybenull) // _SA_annotes1(SAL_null, __maybe) + +#define __valid_impl _SA_annotes0(SAL_valid) // _SA_annotes1(SAL_valid, __yes) +#define __notvalid_impl _SA_annotes0(SAL_notvalid) // _SA_annotes1(SAL_valid, __no) +#define __maybevalid_impl _SA_annotes0(SAL_maybevalid) // _SA_annotes1(SAL_valid, __maybe) + +#define __null_impl_notref _Notref_ _Null_impl_ +#define __maybenull_impl_notref _Notref_ _Maybenull_impl_ +#define __notnull_impl_notref _Notref_ _Notnull_impl_ + +#define __zterm_impl _SA_annotes1(SAL_nullTerminated, __yes) +#define __maybezterm_impl _SA_annotes1(SAL_nullTerminated, __maybe) +#define __maybzterm_impl _SA_annotes1(SAL_nullTerminated, __maybe) +#define __notzterm_impl _SA_annotes1(SAL_nullTerminated, __no) + +#define __readaccess_impl _SA_annotes1(SAL_access, 0x1) +#define __writeaccess_impl _SA_annotes1(SAL_access, 0x2) +#define __allaccess_impl _SA_annotes1(SAL_access, 0x3) + +#define __readaccess_impl_notref _Notref_ _SA_annotes1(SAL_access, 0x1) +#define __writeaccess_impl_notref _Notref_ _SA_annotes1(SAL_access, 0x2) +#define __allaccess_impl_notref _Notref_ _SA_annotes1(SAL_access, 0x3) + +#define __cap_impl(size) _SA_annotes1(SAL_writableTo,elementCount(size)) +#define __cap_c_impl(size) _SA_annotes1(SAL_writableTo,elementCount(size)) +#define __cap_c_one_notref_impl _Notref_ _SA_annotes1(SAL_writableTo,elementCount(1)) +#define __cap_for_impl(param) _SA_annotes1(SAL_writableTo,inexpressibleCount(sizeof(param))) +#define __cap_x_impl(size) _SA_annotes1(SAL_writableTo,inexpressibleCount(#size)) + +#define __bytecap_impl(size) _SA_annotes1(SAL_writableTo,byteCount(size)) +#define __bytecap_c_impl(size) _SA_annotes1(SAL_writableTo,byteCount(size)) +#define __bytecap_x_impl(size) _SA_annotes1(SAL_writableTo,inexpressibleCount(#size)) + +#define __mult_impl(mult,size) _SA_annotes1(SAL_writableTo,(mult)*(size)) + +#define __count_impl(size) _SA_annotes1(SAL_readableTo,elementCount(size)) +#define __count_c_impl(size) _SA_annotes1(SAL_readableTo,elementCount(size)) +#define __count_x_impl(size) _SA_annotes1(SAL_readableTo,inexpressibleCount(#size)) + +#define __bytecount_impl(size) _SA_annotes1(SAL_readableTo,byteCount(size)) +#define __bytecount_c_impl(size) _SA_annotes1(SAL_readableTo,byteCount(size)) +#define __bytecount_x_impl(size) _SA_annotes1(SAL_readableTo,inexpressibleCount(#size)) + +#define _At_impl_(target, annos) _SA_annotes0(SAL_at(target)) _Group_(annos) +#define _At_buffer_impl_(target, iter, bound, annos) _SA_annotes3(SAL_at_buffer, target, iter, bound) _Group_(annos) +#define _Group_impl_(annos) _SA_annotes0(SAL_begin) annos _SA_annotes0(SAL_end) +#define _GrouP_impl_(annos) _SA_annotes0(SAL_BEGIN) annos _SA_annotes0(SAL_END) +#define _When_impl_(expr, annos) _SA_annotes0(SAL_when(expr)) _Group_(annos) + +#define _Use_decl_anno_impl_ __declspec("SAL_useHeader()") // this is a special case! + +#define _Pre1_impl_(p1) _Pre_impl_ p1 +#define _Pre2_impl_(p1,p2) _Pre_impl_ p1 _Pre_impl_ p2 +#define _Pre3_impl_(p1,p2,p3) _Pre_impl_ p1 _Pre_impl_ p2 _Pre_impl_ p3 + +#define _Post1_impl_(p1) _Post_impl_ p1 +#define _Post2_impl_(p1,p2) _Post_impl_ p1 _Post_impl_ p2 +#define _Post3_impl_(p1,p2,p3) _Post_impl_ p1 _Post_impl_ p2 _Post_impl_ p3 + +#define _Ret1_impl_(p1) _Post_impl_ p1 +#define _Ret2_impl_(p1,p2) _Post_impl_ p1 _Post_impl_ p2 +#define _Ret3_impl_(p1,p2,p3) _Post_impl_ p1 _Post_impl_ p2 _Post_impl_ p3 + +#define _Deref_pre1_impl_(p1) _Deref_pre_impl_ p1 +#define _Deref_pre2_impl_(p1,p2) _Deref_pre_impl_ p1 _Deref_pre_impl_ p2 +#define _Deref_pre3_impl_(p1,p2,p3) _Deref_pre_impl_ p1 _Deref_pre_impl_ p2 _Deref_pre_impl_ p3 + +#define _Deref_post1_impl_(p1) _Deref_post_impl_ p1 +#define _Deref_post2_impl_(p1,p2) _Deref_post_impl_ p1 _Deref_post_impl_ p2 +#define _Deref_post3_impl_(p1,p2,p3) _Deref_post_impl_ p1 _Deref_post_impl_ p2 _Deref_post_impl_ p3 + +#define _Deref_ret1_impl_(p1) _Deref_post_impl_ p1 +#define _Deref_ret2_impl_(p1,p2) _Deref_post_impl_ p1 _Deref_post_impl_ p2 +#define _Deref_ret3_impl_(p1,p2,p3) _Deref_post_impl_ p1 _Deref_post_impl_ p2 _Deref_post_impl_ p3 + +#define _Deref2_pre1_impl_(p1) _Deref_pre_impl_ _Notref_impl_ _Deref_impl_ p1 +#define _Deref2_post1_impl_(p1) _Deref_post_impl_ _Notref_impl_ _Deref_impl_ p1 +#define _Deref2_ret1_impl_(p1) _Deref_post_impl_ _Notref_impl_ _Deref_impl_ p1 + +#define __inner_typefix(ctype) _SA_annotes1(SAL_typefix, ctype) +#define __inner_exceptthat _SA_annotes0(SAL_except) + +#elif defined(_MSC_EXTENSIONS) && !defined( MIDL_PASS ) && !defined(__midl) && !defined(RC_INVOKED) && defined(_PFT_VER) && _MSC_VER >= 1400 // ][ + +// minimum attribute expansion for foreground build + +#pragma push_macro( "SA" ) +#pragma push_macro( "REPEATABLE" ) + +#ifdef __cplusplus // [ +#define SA( id ) id +#define REPEATABLE [repeatable] +#else // !__cplusplus // ][ +#define SA( id ) SA_##id +#define REPEATABLE +#endif // !__cplusplus // ] + +REPEATABLE +[source_annotation_attribute( SA( Parameter ) )] +struct __P_impl +{ +#ifdef __cplusplus // [ + __P_impl(); +#endif // ] + int __d_; +}; +typedef struct __P_impl __P_impl; + +REPEATABLE +[source_annotation_attribute( SA( ReturnValue ) )] +struct __R_impl +{ +#ifdef __cplusplus // [ + __R_impl(); +#endif // ] + int __d_; +}; +typedef struct __R_impl __R_impl; + +[source_annotation_attribute( SA( Method ) )] +struct __M_ +{ +#ifdef __cplusplus // [ + __M_(); +#endif // ] + int __d_; +}; +typedef struct __M_ __M_; + +[source_annotation_attribute( SA( All ) )] +struct __A_ +{ +#ifdef __cplusplus // [ + __A_(); +#endif // ] + int __d_; +}; +typedef struct __A_ __A_; + +[source_annotation_attribute( SA( Field ) )] +struct __F_ +{ +#ifdef __cplusplus // [ + __F_(); +#endif // ] + int __d_; +}; +typedef struct __F_ __F_; + +#pragma pop_macro( "REPEATABLE" ) +#pragma pop_macro( "SA" ) + + +#define _SAL_nop_impl_ + +#define _At_impl_(target, annos) [__A_(__d_=0)] +#define _At_buffer_impl_(target, iter, bound, annos) [__A_(__d_=0)] +#define _When_impl_(expr, annos) annos +#define _Group_impl_(annos) annos +#define _GrouP_impl_(annos) annos +#define _Use_decl_anno_impl_ [__M_(__d_=0)] + +#define _Points_to_data_impl_ [__P_impl(__d_=0)] +#define _Literal_impl_ [__P_impl(__d_=0)] +#define _Notliteral_impl_ [__P_impl(__d_=0)] + +#define _Pre_valid_impl_ [__P_impl(__d_=0)] +#define _Post_valid_impl_ [__P_impl(__d_=0)] +#define _Ret_valid_impl_ [__R_impl(__d_=0)] + +#define _Check_return_impl_ [__R_impl(__d_=0)] +#define _Must_inspect_impl_ [__R_impl(__d_=0)] + +#define _Success_impl_(expr) [__M_(__d_=0)] +#define _On_failure_impl_(expr) [__M_(__d_=0)] +#define _Always_impl_(expr) [__M_(__d_=0)] + +#define _Printf_format_string_impl_ [__P_impl(__d_=0)] +#define _Scanf_format_string_impl_ [__P_impl(__d_=0)] +#define _Scanf_s_format_string_impl_ [__P_impl(__d_=0)] + +#define _Raises_SEH_exception_impl_ [__M_(__d_=0)] +#define _Maybe_raises_SEH_exception_impl_ [__M_(__d_=0)] + +#define _In_bound_impl_ [__P_impl(__d_=0)] +#define _Out_bound_impl_ [__P_impl(__d_=0)] +#define _Ret_bound_impl_ [__R_impl(__d_=0)] +#define _Deref_in_bound_impl_ [__P_impl(__d_=0)] +#define _Deref_out_bound_impl_ [__P_impl(__d_=0)] +#define _Deref_ret_bound_impl_ [__R_impl(__d_=0)] + +#define _Range_impl_(min,max) [__P_impl(__d_=0)] +#define _In_range_impl_(min,max) [__P_impl(__d_=0)] +#define _Out_range_impl_(min,max) [__P_impl(__d_=0)] +#define _Ret_range_impl_(min,max) [__R_impl(__d_=0)] +#define _Deref_in_range_impl_(min,max) [__P_impl(__d_=0)] +#define _Deref_out_range_impl_(min,max) [__P_impl(__d_=0)] +#define _Deref_ret_range_impl_(min,max) [__R_impl(__d_=0)] + +#define _Field_range_impl_(min,max) [__F_(__d_=0)] + +#define _Pre_satisfies_impl_(cond) [__A_(__d_=0)] +#define _Post_satisfies_impl_(cond) [__A_(__d_=0)] +#define _Satisfies_impl_(cond) [__A_(__d_=0)] + +#define _Null_impl_ [__A_(__d_=0)] +#define _Notnull_impl_ [__A_(__d_=0)] +#define _Maybenull_impl_ [__A_(__d_=0)] + +#define _Valid_impl_ [__A_(__d_=0)] +#define _Notvalid_impl_ [__A_(__d_=0)] +#define _Maybevalid_impl_ [__A_(__d_=0)] + +#define _Readable_bytes_impl_(size) [__A_(__d_=0)] +#define _Readable_elements_impl_(size) [__A_(__d_=0)] +#define _Writable_bytes_impl_(size) [__A_(__d_=0)] +#define _Writable_elements_impl_(size) [__A_(__d_=0)] + +#define _Null_terminated_impl_ [__A_(__d_=0)] +#define _NullNull_terminated_impl_ [__A_(__d_=0)] + +#define _Pre_impl_ [__P_impl(__d_=0)] +#define _Pre1_impl_(p1) [__P_impl(__d_=0)] +#define _Pre2_impl_(p1,p2) [__P_impl(__d_=0)] +#define _Pre3_impl_(p1,p2,p3) [__P_impl(__d_=0)] + +#define _Post_impl_ [__P_impl(__d_=0)] +#define _Post1_impl_(p1) [__P_impl(__d_=0)] +#define _Post2_impl_(p1,p2) [__P_impl(__d_=0)] +#define _Post3_impl_(p1,p2,p3) [__P_impl(__d_=0)] + +#define _Ret1_impl_(p1) [__R_impl(__d_=0)] +#define _Ret2_impl_(p1,p2) [__R_impl(__d_=0)] +#define _Ret3_impl_(p1,p2,p3) [__R_impl(__d_=0)] + +#define _Deref_pre1_impl_(p1) [__P_impl(__d_=0)] +#define _Deref_pre2_impl_(p1,p2) [__P_impl(__d_=0)] +#define _Deref_pre3_impl_(p1,p2,p3) [__P_impl(__d_=0)] + +#define _Deref_post1_impl_(p1) [__P_impl(__d_=0)] +#define _Deref_post2_impl_(p1,p2) [__P_impl(__d_=0)] +#define _Deref_post3_impl_(p1,p2,p3) [__P_impl(__d_=0)] + +#define _Deref_ret1_impl_(p1) [__R_impl(__d_=0)] +#define _Deref_ret2_impl_(p1,p2) [__R_impl(__d_=0)] +#define _Deref_ret3_impl_(p1,p2,p3) [__R_impl(__d_=0)] + +#define _Deref2_pre1_impl_(p1) //[__P_impl(__d_=0)] +#define _Deref2_post1_impl_(p1) //[__P_impl(__d_=0)] +#define _Deref2_ret1_impl_(p1) //[__P_impl(__d_=0)] + +#else // ][ + + +#define _SAL_nop_impl_ X + +#define _At_impl_(target, annos) +#define _When_impl_(expr, annos) +#define _Group_impl_(annos) +#define _GrouP_impl_(annos) +#define _At_buffer_impl_(target, iter, bound, annos) +#define _Use_decl_anno_impl_ +#define _Points_to_data_impl_ +#define _Literal_impl_ +#define _Notliteral_impl_ +#define _Notref_impl_ + +#define _Pre_valid_impl_ +#define _Post_valid_impl_ +#define _Ret_valid_impl_ + +#define _Check_return_impl_ +#define _Must_inspect_impl_ + +#define _Success_impl_(expr) +#define _On_failure_impl_(annos) +#define _Always_impl_(annos) + +#define _Printf_format_string_impl_ +#define _Scanf_format_string_impl_ +#define _Scanf_s_format_string_impl_ + +#define _In_bound_impl_ +#define _Out_bound_impl_ +#define _Ret_bound_impl_ +#define _Deref_in_bound_impl_ +#define _Deref_out_bound_impl_ +#define _Deref_ret_bound_impl_ + +#define _Range_impl_(min,max) +#define _In_range_impl_(min,max) +#define _Out_range_impl_(min,max) +#define _Ret_range_impl_(min,max) +#define _Deref_in_range_impl_(min,max) +#define _Deref_out_range_impl_(min,max) +#define _Deref_ret_range_impl_(min,max) + +#define _Satisfies_impl_(expr) +#define _Pre_satisfies_impl_(expr) +#define _Post_satisfies_impl_(expr) + +#define _Null_impl_ +#define _Notnull_impl_ +#define _Maybenull_impl_ + +#define _Valid_impl_ +#define _Notvalid_impl_ +#define _Maybevalid_impl_ + +#define _Field_range_impl_(min,max) + +#define _Pre_impl_ +#define _Pre1_impl_(p1) +#define _Pre2_impl_(p1,p2) +#define _Pre3_impl_(p1,p2,p3) + +#define _Post_impl_ +#define _Post1_impl_(p1) +#define _Post2_impl_(p1,p2) +#define _Post3_impl_(p1,p2,p3) + +#define _Ret1_impl_(p1) +#define _Ret2_impl_(p1,p2) +#define _Ret3_impl_(p1,p2,p3) + +#define _Deref_pre1_impl_(p1) +#define _Deref_pre2_impl_(p1,p2) +#define _Deref_pre3_impl_(p1,p2,p3) + +#define _Deref_post1_impl_(p1) +#define _Deref_post2_impl_(p1,p2) +#define _Deref_post3_impl_(p1,p2,p3) + +#define _Deref_ret1_impl_(p1) +#define _Deref_ret2_impl_(p1,p2) +#define _Deref_ret3_impl_(p1,p2,p3) + +#define _Deref2_pre1_impl_(p1) +#define _Deref2_post1_impl_(p1) +#define _Deref2_ret1_impl_(p1) + +#define _Readable_bytes_impl_(size) +#define _Readable_elements_impl_(size) +#define _Writable_bytes_impl_(size) +#define _Writable_elements_impl_(size) + +#define _Null_terminated_impl_ +#define _NullNull_terminated_impl_ + +// Obsolete -- may be needed for transition to attributes. +#define __inner_typefix(ctype) +#define __inner_exceptthat + +#endif // ] + +// This section contains the deprecated annotations + +/* + ------------------------------------------------------------------------------- + Introduction + + sal.h provides a set of annotations to describe how a function uses its + parameters - the assumptions it makes about them, and the guarantees it makes + upon finishing. + + Annotations may be placed before either a function parameter's type or its return + type, and describe the function's behavior regarding the parameter or return value. + There are two classes of annotations: buffer annotations and advanced annotations. + Buffer annotations describe how functions use their pointer parameters, and + advanced annotations either describe complex/unusual buffer behavior, or provide + additional information about a parameter that is not otherwise expressible. + + ------------------------------------------------------------------------------- + Buffer Annotations + + The most important annotations in sal.h provide a consistent way to annotate + buffer parameters or return values for a function. Each of these annotations describes + a single buffer (which could be a string, a fixed-length or variable-length array, + or just a pointer) that the function interacts with: where it is, how large it is, + how much is initialized, and what the function does with it. + + The appropriate macro for a given buffer can be constructed using the table below. + Just pick the appropriate values from each category, and combine them together + with a leading underscore. Some combinations of values do not make sense as buffer + annotations. Only meaningful annotations can be added to your code; for a list of + these, see the buffer annotation definitions section. + + Only a single buffer annotation should be used for each parameter. + + |------------|------------|---------|--------|----------|----------|---------------| + | Level | Usage | Size | Output | NullTerm | Optional | Parameters | + |------------|------------|---------|--------|----------|----------|---------------| + | <> | <> | <> | <> | _z | <> | <> | + | _deref | _in | _ecount | _full | _nz | _opt | (size) | + | _deref_opt | _out | _bcount | _part | | | (size,length) | + | | _inout | | | | | | + | | | | | | | | + |------------|------------|---------|--------|----------|----------|---------------| + + Level: Describes the buffer pointer's level of indirection from the parameter or + return value 'p'. + + <> : p is the buffer pointer. + _deref : *p is the buffer pointer. p must not be NULL. + _deref_opt : *p may be the buffer pointer. p may be NULL, in which case the rest of + the annotation is ignored. + + Usage: Describes how the function uses the buffer. + + <> : The buffer is not accessed. If used on the return value or with _deref, the + function will provide the buffer, and it will be uninitialized at exit. + Otherwise, the caller must provide the buffer. This should only be used + for alloc and free functions. + _in : The function will only read from the buffer. The caller must provide the + buffer and initialize it. Cannot be used with _deref. + _out : The function will only write to the buffer. If used on the return value or + with _deref, the function will provide the buffer and initialize it. + Otherwise, the caller must provide the buffer, and the function will + initialize it. + _inout : The function may freely read from and write to the buffer. The caller must + provide the buffer and initialize it. If used with _deref, the buffer may + be reallocated by the function. + + Size: Describes the total size of the buffer. This may be less than the space actually + allocated for the buffer, in which case it describes the accessible amount. + + <> : No buffer size is given. If the type specifies the buffer size (such as + with LPSTR and LPWSTR), that amount is used. Otherwise, the buffer is one + element long. Must be used with _in, _out, or _inout. + _ecount : The buffer size is an explicit element count. + _bcount : The buffer size is an explicit byte count. + + Output: Describes how much of the buffer will be initialized by the function. For + _inout buffers, this also describes how much is initialized at entry. Omit this + category for _in buffers; they must be fully initialized by the caller. + + <> : The type specifies how much is initialized. For instance, a function initializing + an LPWSTR must NULL-terminate the string. + _full : The function initializes the entire buffer. + _part : The function initializes part of the buffer, and explicitly indicates how much. + + NullTerm: States if the present of a '\0' marks the end of valid elements in the buffer. + _z : A '\0' indicated the end of the buffer + _nz : The buffer may not be null terminated and a '\0' does not indicate the end of the + buffer. + Optional: Describes if the buffer itself is optional. + + <> : The pointer to the buffer must not be NULL. + _opt : The pointer to the buffer might be NULL. It will be checked before being dereferenced. + + Parameters: Gives explicit counts for the size and length of the buffer. + + <> : There is no explicit count. Use when neither _ecount nor _bcount is used. + (size) : Only the buffer's total size is given. Use with _ecount or _bcount but not _part. + (size,length) : The buffer's total size and initialized length are given. Use with _ecount_part + and _bcount_part. + + ------------------------------------------------------------------------------- + Buffer Annotation Examples + + LWSTDAPI_(BOOL) StrToIntExA( + __in LPCSTR pszString, + DWORD dwFlags, + __out int *piRet -- A pointer whose dereference will be filled in. + ); + + void MyPaintingFunction( + __in HWND hwndControl, -- An initialized read-only parameter. + __in_opt HDC hdcOptional, -- An initialized read-only parameter that might be NULL. + __inout IPropertyStore *ppsStore -- An initialized parameter that may be freely used + -- and modified. + ); + + LWSTDAPI_(BOOL) PathCompactPathExA( + __out_ecount(cchMax) LPSTR pszOut, -- A string buffer with cch elements that will + -- be NULL terminated on exit. + __in LPCSTR pszSrc, + UINT cchMax, + DWORD dwFlags + ); + + HRESULT SHLocalAllocBytes( + size_t cb, + __deref_bcount(cb) T **ppv -- A pointer whose dereference will be set to an + -- uninitialized buffer with cb bytes. + ); + + __inout_bcount_full(cb) : A buffer with cb elements that is fully initialized at + entry and exit, and may be written to by this function. + + __out_ecount_part(count, *countOut) : A buffer with count elements that will be + partially initialized by this function. The function indicates how much it + initialized by setting *countOut. + + ------------------------------------------------------------------------------- + Advanced Annotations + + Advanced annotations describe behavior that is not expressible with the regular + buffer macros. These may be used either to annotate buffer parameters that involve + complex or conditional behavior, or to enrich existing annotations with additional + information. + + __success(expr) f : + <expr> indicates whether function f succeeded or not. If <expr> is true at exit, + all the function's guarantees (as given by other annotations) must hold. If <expr> + is false at exit, the caller should not expect any of the function's guarantees + to hold. If not used, the function must always satisfy its guarantees. Added + automatically to functions that indicate success in standard ways, such as by + returning an HRESULT. + + __nullterminated p : + Pointer p is a buffer that may be read or written up to and including the first + NULL character or pointer. May be used on typedefs, which marks valid (properly + initialized) instances of that type as being NULL-terminated. + + __nullnullterminated p : + Pointer p is a buffer that may be read or written up to and including the first + sequence of two NULL characters or pointers. May be used on typedefs, which marks + valid instances of that type as being double-NULL terminated. + + __reserved v : + Value v must be 0/NULL, reserved for future use. + + __checkReturn v : + Return value v must not be ignored by callers of this function. + + __typefix(ctype) v : + Value v should be treated as an instance of ctype, rather than its declared type. + + __override f : + Specify C#-style 'override' behaviour for overriding virtual methods. + + __callback f : + Function f can be used as a function pointer. + + __format_string p : + Pointer p is a string that contains % markers in the style of printf. + + __blocksOn(resource) f : + Function f blocks on the resource 'resource'. + + __fallthrough : + Annotates switch statement labels where fall-through is desired, to distinguish + from forgotten break statements. + + ------------------------------------------------------------------------------- + Advanced Annotation Examples + + __success(return != FALSE) LWSTDAPI_(BOOL) + PathCanonicalizeA(__out_ecount(MAX_PATH) LPSTR pszBuf, LPCSTR pszPath) : + pszBuf is only guaranteed to be NULL-terminated when TRUE is returned. + + typedef __nullterminated WCHAR* LPWSTR : Initialized LPWSTRs are NULL-terminated strings. + + __out_ecount(cch) __typefix(LPWSTR) void *psz : psz is a buffer parameter which will be + a NULL-terminated WCHAR string at exit, and which initially contains cch WCHARs. + + ------------------------------------------------------------------------------- +*/ + +#define __specstrings + +#ifdef __cplusplus // [ +#ifndef __nothrow // [ +# define __nothrow __declspec(nothrow) +#endif // ] +extern "C" { +#else // ][ +#ifndef __nothrow // [ +# define __nothrow +#endif // ] +#endif /* __cplusplus */ // ] + + +/* + ------------------------------------------------------------------------------- + Helper Macro Definitions + + These express behavior common to many of the high-level annotations. + DO NOT USE THESE IN YOUR CODE. + ------------------------------------------------------------------------------- +*/ + +/* + The helper annotations are only understood by the compiler version used by + various defect detection tools. When the regular compiler is running, they + are defined into nothing, and do not affect the compiled code. +*/ + +#if !defined(__midl) && defined(_PREFAST_) // [ + + /* + In the primitive "SAL_*" annotations "SAL" stands for Standard + Annotation Language. These "SAL_*" annotations are the + primitives the compiler understands and high-level MACROs + will decompose into these primivates. + */ + + #define _SA_SPECSTRIZE( x ) #x + + /* + __null p + __notnull p + __maybenull p + + Annotates a pointer p. States that pointer p is null. Commonly used + in the negated form __notnull or the possibly null form __maybenull. + */ + + #define __null _Null_impl_ + #define __notnull _Notnull_impl_ + #define __maybenull _Maybenull_impl_ + + /* + __readonly l + __notreadonly l + __mabyereadonly l + + Annotates a location l. States that location l is not modified after + this point. If the annotation is placed on the precondition state of + a function, the restriction only applies until the postcondition state + of the function. __maybereadonly states that the annotated location + may be modified, whereas __notreadonly states that a location must be + modified. + */ + + #define __readonly _Pre1_impl_(__readaccess_impl) + #define __notreadonly _Pre1_impl_(__allaccess_impl) + #define __maybereadonly _Pre1_impl_(__readaccess_impl) + + /* + __valid v + __notvalid v + __maybevalid v + + Annotates any value v. States that the value satisfies all properties of + valid values of its type. For example, for a string buffer, valid means + that the buffer pointer is either NULL or points to a NULL-terminated string. + */ + + #define __valid _Valid_impl_ + #define __notvalid _Notvalid_impl_ + #define __maybevalid _Maybevalid_impl_ + + /* + __readableTo(extent) p + + Annotates a buffer pointer p. If the buffer can be read, extent describes + how much of the buffer is readable. For a reader of the buffer, this is + an explicit permission to read up to that amount, rather than a restriction to + read only up to it. + */ + + #define __readableTo(extent) _SA_annotes1(SAL_readableTo, extent) + + /* + + __elem_readableTo(size) + + Annotates a buffer pointer p as being readable to size elements. + */ + + #define __elem_readableTo(size) _SA_annotes1(SAL_readableTo, elementCount( size )) + + /* + __byte_readableTo(size) + + Annotates a buffer pointer p as being readable to size bytes. + */ + #define __byte_readableTo(size) _SA_annotes1(SAL_readableTo, byteCount(size)) + + /* + __writableTo(extent) p + + Annotates a buffer pointer p. If the buffer can be modified, extent + describes how much of the buffer is writable (usually the allocation + size). For a writer of the buffer, this is an explicit permission to + write up to that amount, rather than a restriction to write only up to it. + */ + #define __writableTo(size) _SA_annotes1(SAL_writableTo, size) + + /* + __elem_writableTo(size) + + Annotates a buffer pointer p as being writable to size elements. + */ + #define __elem_writableTo(size) _SA_annotes1(SAL_writableTo, elementCount( size )) + + /* + __byte_writableTo(size) + + Annotates a buffer pointer p as being writable to size bytes. + */ + #define __byte_writableTo(size) _SA_annotes1(SAL_writableTo, byteCount( size)) + + /* + __deref p + + Annotates a pointer p. The next annotation applies one dereference down + in the type. If readableTo(p, size) then the next annotation applies to + all elements *(p+i) for which i satisfies the size. If p is a pointer + to a struct, the next annotation applies to all fields of the struct. + */ + #define __deref _Deref_impl_ + + /* + __pre __next_annotation + + The next annotation applies in the precondition state + */ + #define __pre _Pre_impl_ + + /* + __post __next_annotation + + The next annotation applies in the postcondition state + */ + #define __post _Post_impl_ + + /* + __precond(<expr>) + + When <expr> is true, the next annotation applies in the precondition state + (currently not enabled) + */ + #define __precond(expr) __pre + + /* + __postcond(<expr>) + + When <expr> is true, the next annotation applies in the postcondition state + (currently not enabled) + */ + #define __postcond(expr) __post + + /* + __exceptthat + + Given a set of annotations Q containing __exceptthat maybeP, the effect of + the except clause is to erase any P or notP annotations (explicit or + implied) within Q at the same level of dereferencing that the except + clause appears, and to replace it with maybeP. + + Example 1: __valid __pre_except_maybenull on a pointer p means that the + pointer may be null, and is otherwise valid, thus overriding + the implicit notnull annotation implied by __valid on + pointers. + + Example 2: __valid __deref __pre_except_maybenull on an int **p means + that p is not null (implied by valid), but the elements + pointed to by p could be null, and are otherwise valid. + */ + #define __exceptthat __inner_exceptthat + + /* + _refparam + + Added to all out parameter macros to indicate that they are all reference + parameters. + */ + #define __refparam _Notref_ __deref __notreadonly + + /* + __inner_* + + Helper macros that directly correspond to certain high-level annotations. + + */ + + /* + Macros to classify the entrypoints and indicate their category. + + Pre-defined control point categories include: RPC, LPC, DeviceDriver, UserToKernel, ISAPI, COM. + + */ + #define __inner_control_entrypoint(category) _SA_annotes2(SAL_entrypoint, controlEntry, category) + + + /* + Pre-defined data entry point categories include: Registry, File, Network. + */ + #define __inner_data_entrypoint(category) _SA_annotes2(SAL_entrypoint, dataEntry, category) + + #define __inner_override _SA_annotes0(__override) + #define __inner_callback _SA_annotes0(__callback) + #define __inner_blocksOn(resource) _SA_annotes1(SAL_blocksOn, resource) + #define __inner_fallthrough_dec __inline __nothrow void __FallThrough() {} + #define __inner_fallthrough __FallThrough(); + + #define __post_except_maybenull __post __inner_exceptthat _Maybenull_impl_ + #define __pre_except_maybenull __pre __inner_exceptthat _Maybenull_impl_ + + #define __post_deref_except_maybenull __post __deref __inner_exceptthat _Maybenull_impl_ + #define __pre_deref_except_maybenull __pre __deref __inner_exceptthat _Maybenull_impl_ + + #define __inexpressible_readableTo(size) _Readable_elements_impl_(_Inexpressible_(size)) + #define __inexpressible_writableTo(size) _Writable_elements_impl_(_Inexpressible_(size)) + + +#else // ][ + #define __null + #define __notnull + #define __maybenull + #define __readonly + #define __notreadonly + #define __maybereadonly + #define __valid + #define __notvalid + #define __maybevalid + #define __readableTo(extent) + #define __elem_readableTo(size) + #define __byte_readableTo(size) + #define __writableTo(size) + #define __elem_writableTo(size) + #define __byte_writableTo(size) + #define __deref + #define __pre + #define __post + #define __precond(expr) + #define __postcond(expr) + #define __exceptthat + #define __inner_override + #define __inner_callback + #define __inner_blocksOn(resource) + #define __inner_fallthrough_dec + #define __inner_fallthrough + #define __refparam + #define __inner_control_entrypoint(category) + #define __inner_data_entrypoint(category) + + #define __post_except_maybenull + #define __pre_except_maybenull + #define __post_deref_except_maybenull + #define __pre_deref_except_maybenull + + #define __inexpressible_readableTo(size) + #define __inexpressible_writableTo(size) + +#endif /* !defined(__midl) && defined(_PREFAST_) */ // ] + +/* +------------------------------------------------------------------------------- +Buffer Annotation Definitions + +Any of these may be used to directly annotate functions, but only one should +be used for each parameter. To determine which annotation to use for a given +buffer, use the table in the buffer annotations section. +------------------------------------------------------------------------------- +*/ + +#define __ecount(size) _SAL1_Source_(__ecount, (size), __notnull __elem_writableTo(size)) +#define __bcount(size) _SAL1_Source_(__bcount, (size), __notnull __byte_writableTo(size)) +#define __in _SAL1_Source_(__in, (), _In_) +#define __in_ecount(size) _SAL1_Source_(__in_ecount, (size), _In_reads_(size)) +#define __in_bcount(size) _SAL1_Source_(__in_bcount, (size), _In_reads_bytes_(size)) +#define __in_z _SAL1_Source_(__in_z, (), _In_z_) +#define __in_ecount_z(size) _SAL1_Source_(__in_ecount_z, (size), _In_reads_z_(size)) +#define __in_bcount_z(size) _SAL1_Source_(__in_bcount_z, (size), __in_bcount(size) __pre __nullterminated) +#define __in_nz _SAL1_Source_(__in_nz, (), __in) +#define __in_ecount_nz(size) _SAL1_Source_(__in_ecount_nz, (size), __in_ecount(size)) +#define __in_bcount_nz(size) _SAL1_Source_(__in_bcount_nz, (size), __in_bcount(size)) +#define __out _SAL1_Source_(__out, (), _Out_) +#define __out_ecount(size) _SAL1_Source_(__out_ecount, (size), _Out_writes_(size)) +#define __out_bcount(size) _SAL1_Source_(__out_bcount, (size), _Out_writes_bytes_(size)) +#define __out_ecount_part(size,length) _SAL1_Source_(__out_ecount_part, (size,length), _Out_writes_to_(size,length)) +#define __out_bcount_part(size,length) _SAL1_Source_(__out_bcount_part, (size,length), _Out_writes_bytes_to_(size,length)) +#define __out_ecount_full(size) _SAL1_Source_(__out_ecount_full, (size), _Out_writes_all_(size)) +#define __out_bcount_full(size) _SAL1_Source_(__out_bcount_full, (size), _Out_writes_bytes_all_(size)) +#define __out_z _SAL1_Source_(__out_z, (), __post __valid __refparam __post __nullterminated) +#define __out_z_opt _SAL1_Source_(__out_z_opt, (), __post __valid __refparam __post __nullterminated __pre_except_maybenull) +#define __out_ecount_z(size) _SAL1_Source_(__out_ecount_z, (size), __ecount(size) __post __valid __refparam __post __nullterminated) +#define __out_bcount_z(size) _SAL1_Source_(__out_bcount_z, (size), __bcount(size) __post __valid __refparam __post __nullterminated) +#define __out_ecount_part_z(size,length) _SAL1_Source_(__out_ecount_part_z, (size,length), __out_ecount_part(size,length) __post __nullterminated) +#define __out_bcount_part_z(size,length) _SAL1_Source_(__out_bcount_part_z, (size,length), __out_bcount_part(size,length) __post __nullterminated) +#define __out_ecount_full_z(size) _SAL1_Source_(__out_ecount_full_z, (size), __out_ecount_full(size) __post __nullterminated) +#define __out_bcount_full_z(size) _SAL1_Source_(__out_bcount_full_z, (size), __out_bcount_full(size) __post __nullterminated) +#define __out_nz _SAL1_Source_(__out_nz, (), __post __valid __refparam) +#define __out_nz_opt _SAL1_Source_(__out_nz_opt, (), __post __valid __refparam __post_except_maybenull_) +#define __out_ecount_nz(size) _SAL1_Source_(__out_ecount_nz, (size), __ecount(size) __post __valid __refparam) +#define __out_bcount_nz(size) _SAL1_Source_(__out_bcount_nz, (size), __bcount(size) __post __valid __refparam) +#define __inout _SAL1_Source_(__inout, (), _Inout_) +#define __inout_ecount(size) _SAL1_Source_(__inout_ecount, (size), _Inout_updates_(size)) +#define __inout_bcount(size) _SAL1_Source_(__inout_bcount, (size), _Inout_updates_bytes_(size)) +#define __inout_ecount_part(size,length) _SAL1_Source_(__inout_ecount_part, (size,length), _Inout_updates_to_(size,length)) +#define __inout_bcount_part(size,length) _SAL1_Source_(__inout_bcount_part, (size,length), _Inout_updates_bytes_to_(size,length)) +#define __inout_ecount_full(size) _SAL1_Source_(__inout_ecount_full, (size), _Inout_updates_all_(size)) +#define __inout_bcount_full(size) _SAL1_Source_(__inout_bcount_full, (size), _Inout_updates_bytes_all_(size)) +#define __inout_z _SAL1_Source_(__inout_z, (), _Inout_z_) +#define __inout_ecount_z(size) _SAL1_Source_(__inout_ecount_z, (size), _Inout_updates_z_(size)) +#define __inout_bcount_z(size) _SAL1_Source_(__inout_bcount_z, (size), __inout_bcount(size) __pre __nullterminated __post __nullterminated) +#define __inout_nz _SAL1_Source_(__inout_nz, (), __inout) +#define __inout_ecount_nz(size) _SAL1_Source_(__inout_ecount_nz, (size), __inout_ecount(size)) +#define __inout_bcount_nz(size) _SAL1_Source_(__inout_bcount_nz, (size), __inout_bcount(size)) +#define __ecount_opt(size) _SAL1_Source_(__ecount_opt, (size), __ecount(size) __pre_except_maybenull) +#define __bcount_opt(size) _SAL1_Source_(__bcount_opt, (size), __bcount(size) __pre_except_maybenull) +#define __in_opt _SAL1_Source_(__in_opt, (), _In_opt_) +#define __in_ecount_opt(size) _SAL1_Source_(__in_ecount_opt, (size), _In_reads_opt_(size)) +#define __in_bcount_opt(size) _SAL1_Source_(__in_bcount_opt, (size), _In_reads_bytes_opt_(size)) +#define __in_z_opt _SAL1_Source_(__in_z_opt, (), _In_opt_z_) +#define __in_ecount_z_opt(size) _SAL1_Source_(__in_ecount_z_opt, (size), __in_ecount_opt(size) __pre __nullterminated) +#define __in_bcount_z_opt(size) _SAL1_Source_(__in_bcount_z_opt, (size), __in_bcount_opt(size) __pre __nullterminated) +#define __in_nz_opt _SAL1_Source_(__in_nz_opt, (), __in_opt) +#define __in_ecount_nz_opt(size) _SAL1_Source_(__in_ecount_nz_opt, (size), __in_ecount_opt(size)) +#define __in_bcount_nz_opt(size) _SAL1_Source_(__in_bcount_nz_opt, (size), __in_bcount_opt(size)) +#define __out_opt _SAL1_Source_(__out_opt, (), _Out_opt_) +#define __out_ecount_opt(size) _SAL1_Source_(__out_ecount_opt, (size), _Out_writes_opt_(size)) +#define __out_bcount_opt(size) _SAL1_Source_(__out_bcount_opt, (size), _Out_writes_bytes_opt_(size)) +#define __out_ecount_part_opt(size,length) _SAL1_Source_(__out_ecount_part_opt, (size,length), __out_ecount_part(size,length) __pre_except_maybenull) +#define __out_bcount_part_opt(size,length) _SAL1_Source_(__out_bcount_part_opt, (size,length), __out_bcount_part(size,length) __pre_except_maybenull) +#define __out_ecount_full_opt(size) _SAL1_Source_(__out_ecount_full_opt, (size), __out_ecount_full(size) __pre_except_maybenull) +#define __out_bcount_full_opt(size) _SAL1_Source_(__out_bcount_full_opt, (size), __out_bcount_full(size) __pre_except_maybenull) +#define __out_ecount_z_opt(size) _SAL1_Source_(__out_ecount_z_opt, (size), __out_ecount_opt(size) __post __nullterminated) +#define __out_bcount_z_opt(size) _SAL1_Source_(__out_bcount_z_opt, (size), __out_bcount_opt(size) __post __nullterminated) +#define __out_ecount_part_z_opt(size,length) _SAL1_Source_(__out_ecount_part_z_opt, (size,length), __out_ecount_part_opt(size,length) __post __nullterminated) +#define __out_bcount_part_z_opt(size,length) _SAL1_Source_(__out_bcount_part_z_opt, (size,length), __out_bcount_part_opt(size,length) __post __nullterminated) +#define __out_ecount_full_z_opt(size) _SAL1_Source_(__out_ecount_full_z_opt, (size), __out_ecount_full_opt(size) __post __nullterminated) +#define __out_bcount_full_z_opt(size) _SAL1_Source_(__out_bcount_full_z_opt, (size), __out_bcount_full_opt(size) __post __nullterminated) +#define __out_ecount_nz_opt(size) _SAL1_Source_(__out_ecount_nz_opt, (size), __out_ecount_opt(size) __post __nullterminated) +#define __out_bcount_nz_opt(size) _SAL1_Source_(__out_bcount_nz_opt, (size), __out_bcount_opt(size) __post __nullterminated) +#define __inout_opt _SAL1_Source_(__inout_opt, (), _Inout_opt_) +#define __inout_ecount_opt(size) _SAL1_Source_(__inout_ecount_opt, (size), __inout_ecount(size) __pre_except_maybenull) +#define __inout_bcount_opt(size) _SAL1_Source_(__inout_bcount_opt, (size), __inout_bcount(size) __pre_except_maybenull) +#define __inout_ecount_part_opt(size,length) _SAL1_Source_(__inout_ecount_part_opt, (size,length), __inout_ecount_part(size,length) __pre_except_maybenull) +#define __inout_bcount_part_opt(size,length) _SAL1_Source_(__inout_bcount_part_opt, (size,length), __inout_bcount_part(size,length) __pre_except_maybenull) +#define __inout_ecount_full_opt(size) _SAL1_Source_(__inout_ecount_full_opt, (size), __inout_ecount_full(size) __pre_except_maybenull) +#define __inout_bcount_full_opt(size) _SAL1_Source_(__inout_bcount_full_opt, (size), __inout_bcount_full(size) __pre_except_maybenull) +#define __inout_z_opt _SAL1_Source_(__inout_z_opt, (), __inout_opt __pre __nullterminated __post __nullterminated) +#define __inout_ecount_z_opt(size) _SAL1_Source_(__inout_ecount_z_opt, (size), __inout_ecount_opt(size) __pre __nullterminated __post __nullterminated) +#define __inout_ecount_z_opt(size) _SAL1_Source_(__inout_ecount_z_opt, (size), __inout_ecount_opt(size) __pre __nullterminated __post __nullterminated) +#define __inout_bcount_z_opt(size) _SAL1_Source_(__inout_bcount_z_opt, (size), __inout_bcount_opt(size)) +#define __inout_nz_opt _SAL1_Source_(__inout_nz_opt, (), __inout_opt) +#define __inout_ecount_nz_opt(size) _SAL1_Source_(__inout_ecount_nz_opt, (size), __inout_ecount_opt(size)) +#define __inout_bcount_nz_opt(size) _SAL1_Source_(__inout_bcount_nz_opt, (size), __inout_bcount_opt(size)) +#define __deref_ecount(size) _SAL1_Source_(__deref_ecount, (size), _Notref_ __ecount(1) __post _Notref_ __elem_readableTo(1) __post _Notref_ __deref _Notref_ __notnull __post __deref __elem_writableTo(size)) +#define __deref_bcount(size) _SAL1_Source_(__deref_bcount, (size), _Notref_ __ecount(1) __post _Notref_ __elem_readableTo(1) __post _Notref_ __deref _Notref_ __notnull __post __deref __byte_writableTo(size)) +#define __deref_out _SAL1_Source_(__deref_out, (), _Outptr_) +#define __deref_out_ecount(size) _SAL1_Source_(__deref_out_ecount, (size), _Outptr_result_buffer_(size)) +#define __deref_out_bcount(size) _SAL1_Source_(__deref_out_bcount, (size), _Outptr_result_bytebuffer_(size)) +#define __deref_out_ecount_part(size,length) _SAL1_Source_(__deref_out_ecount_part, (size,length), _Outptr_result_buffer_to_(size,length)) +#define __deref_out_bcount_part(size,length) _SAL1_Source_(__deref_out_bcount_part, (size,length), _Outptr_result_bytebuffer_to_(size,length)) +#define __deref_out_ecount_full(size) _SAL1_Source_(__deref_out_ecount_full, (size), __deref_out_ecount_part(size,size)) +#define __deref_out_bcount_full(size) _SAL1_Source_(__deref_out_bcount_full, (size), __deref_out_bcount_part(size,size)) +#define __deref_out_z _SAL1_Source_(__deref_out_z, (), _Outptr_result_z_) +#define __deref_out_ecount_z(size) _SAL1_Source_(__deref_out_ecount_z, (size), __deref_out_ecount(size) __post __deref __nullterminated) +#define __deref_out_bcount_z(size) _SAL1_Source_(__deref_out_bcount_z, (size), __deref_out_bcount(size) __post __deref __nullterminated) +#define __deref_out_nz _SAL1_Source_(__deref_out_nz, (), __deref_out) +#define __deref_out_ecount_nz(size) _SAL1_Source_(__deref_out_ecount_nz, (size), __deref_out_ecount(size)) +#define __deref_out_bcount_nz(size) _SAL1_Source_(__deref_out_bcount_nz, (size), __deref_out_ecount(size)) +#define __deref_inout _SAL1_Source_(__deref_inout, (), _Notref_ __notnull _Notref_ __elem_readableTo(1) __pre __deref __valid __post _Notref_ __deref __valid __refparam) +#define __deref_inout_z _SAL1_Source_(__deref_inout_z, (), __deref_inout __pre __deref __nullterminated __post _Notref_ __deref __nullterminated) +#define __deref_inout_ecount(size) _SAL1_Source_(__deref_inout_ecount, (size), __deref_inout __pre __deref __elem_writableTo(size) __post _Notref_ __deref __elem_writableTo(size)) +#define __deref_inout_bcount(size) _SAL1_Source_(__deref_inout_bcount, (size), __deref_inout __pre __deref __byte_writableTo(size) __post _Notref_ __deref __byte_writableTo(size)) +#define __deref_inout_ecount_part(size,length) _SAL1_Source_(__deref_inout_ecount_part, (size,length), __deref_inout_ecount(size) __pre __deref __elem_readableTo(length) __post __deref __elem_readableTo(length)) +#define __deref_inout_bcount_part(size,length) _SAL1_Source_(__deref_inout_bcount_part, (size,length), __deref_inout_bcount(size) __pre __deref __byte_readableTo(length) __post __deref __byte_readableTo(length)) +#define __deref_inout_ecount_full(size) _SAL1_Source_(__deref_inout_ecount_full, (size), __deref_inout_ecount_part(size,size)) +#define __deref_inout_bcount_full(size) _SAL1_Source_(__deref_inout_bcount_full, (size), __deref_inout_bcount_part(size,size)) +#define __deref_inout_ecount_z(size) _SAL1_Source_(__deref_inout_ecount_z, (size), __deref_inout_ecount(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_inout_bcount_z(size) _SAL1_Source_(__deref_inout_bcount_z, (size), __deref_inout_bcount(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_inout_nz _SAL1_Source_(__deref_inout_nz, (), __deref_inout) +#define __deref_inout_ecount_nz(size) _SAL1_Source_(__deref_inout_ecount_nz, (size), __deref_inout_ecount(size)) +#define __deref_inout_bcount_nz(size) _SAL1_Source_(__deref_inout_bcount_nz, (size), __deref_inout_ecount(size)) +#define __deref_ecount_opt(size) _SAL1_Source_(__deref_ecount_opt, (size), __deref_ecount(size) __post_deref_except_maybenull) +#define __deref_bcount_opt(size) _SAL1_Source_(__deref_bcount_opt, (size), __deref_bcount(size) __post_deref_except_maybenull) +#define __deref_out_opt _SAL1_Source_(__deref_out_opt, (), __deref_out __post_deref_except_maybenull) +#define __deref_out_ecount_opt(size) _SAL1_Source_(__deref_out_ecount_opt, (size), __deref_out_ecount(size) __post_deref_except_maybenull) +#define __deref_out_bcount_opt(size) _SAL1_Source_(__deref_out_bcount_opt, (size), __deref_out_bcount(size) __post_deref_except_maybenull) +#define __deref_out_ecount_part_opt(size,length) _SAL1_Source_(__deref_out_ecount_part_opt, (size,length), __deref_out_ecount_part(size,length) __post_deref_except_maybenull) +#define __deref_out_bcount_part_opt(size,length) _SAL1_Source_(__deref_out_bcount_part_opt, (size,length), __deref_out_bcount_part(size,length) __post_deref_except_maybenull) +#define __deref_out_ecount_full_opt(size) _SAL1_Source_(__deref_out_ecount_full_opt, (size), __deref_out_ecount_full(size) __post_deref_except_maybenull) +#define __deref_out_bcount_full_opt(size) _SAL1_Source_(__deref_out_bcount_full_opt, (size), __deref_out_bcount_full(size) __post_deref_except_maybenull) +#define __deref_out_z_opt _SAL1_Source_(__deref_out_z_opt, (), _Outptr_result_maybenull_z_) +#define __deref_out_ecount_z_opt(size) _SAL1_Source_(__deref_out_ecount_z_opt, (size), __deref_out_ecount_opt(size) __post __deref __nullterminated) +#define __deref_out_bcount_z_opt(size) _SAL1_Source_(__deref_out_bcount_z_opt, (size), __deref_out_bcount_opt(size) __post __deref __nullterminated) +#define __deref_out_nz_opt _SAL1_Source_(__deref_out_nz_opt, (), __deref_out_opt) +#define __deref_out_ecount_nz_opt(size) _SAL1_Source_(__deref_out_ecount_nz_opt, (size), __deref_out_ecount_opt(size)) +#define __deref_out_bcount_nz_opt(size) _SAL1_Source_(__deref_out_bcount_nz_opt, (size), __deref_out_bcount_opt(size)) +#define __deref_inout_opt _SAL1_Source_(__deref_inout_opt, (), __deref_inout __pre_deref_except_maybenull __post_deref_except_maybenull) +#define __deref_inout_ecount_opt(size) _SAL1_Source_(__deref_inout_ecount_opt, (size), __deref_inout_ecount(size) __pre_deref_except_maybenull __post_deref_except_maybenull) +#define __deref_inout_bcount_opt(size) _SAL1_Source_(__deref_inout_bcount_opt, (size), __deref_inout_bcount(size) __pre_deref_except_maybenull __post_deref_except_maybenull) +#define __deref_inout_ecount_part_opt(size,length) _SAL1_Source_(__deref_inout_ecount_part_opt, (size,length), __deref_inout_ecount_part(size,length) __pre_deref_except_maybenull __post_deref_except_maybenull) +#define __deref_inout_bcount_part_opt(size,length) _SAL1_Source_(__deref_inout_bcount_part_opt, (size,length), __deref_inout_bcount_part(size,length) __pre_deref_except_maybenull __post_deref_except_maybenull) +#define __deref_inout_ecount_full_opt(size) _SAL1_Source_(__deref_inout_ecount_full_opt, (size), __deref_inout_ecount_full(size) __pre_deref_except_maybenull __post_deref_except_maybenull) +#define __deref_inout_bcount_full_opt(size) _SAL1_Source_(__deref_inout_bcount_full_opt, (size), __deref_inout_bcount_full(size) __pre_deref_except_maybenull __post_deref_except_maybenull) +#define __deref_inout_z_opt _SAL1_Source_(__deref_inout_z_opt, (), __deref_inout_opt __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_inout_ecount_z_opt(size) _SAL1_Source_(__deref_inout_ecount_z_opt, (size), __deref_inout_ecount_opt(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_inout_bcount_z_opt(size) _SAL1_Source_(__deref_inout_bcount_z_opt, (size), __deref_inout_bcount_opt(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_inout_nz_opt _SAL1_Source_(__deref_inout_nz_opt, (), __deref_inout_opt) +#define __deref_inout_ecount_nz_opt(size) _SAL1_Source_(__deref_inout_ecount_nz_opt, (size), __deref_inout_ecount_opt(size)) +#define __deref_inout_bcount_nz_opt(size) _SAL1_Source_(__deref_inout_bcount_nz_opt, (size), __deref_inout_bcount_opt(size)) +#define __deref_opt_ecount(size) _SAL1_Source_(__deref_opt_ecount, (size), __deref_ecount(size) __pre_except_maybenull) +#define __deref_opt_bcount(size) _SAL1_Source_(__deref_opt_bcount, (size), __deref_bcount(size) __pre_except_maybenull) +#define __deref_opt_out _SAL1_Source_(__deref_opt_out, (), _Outptr_opt_) +#define __deref_opt_out_z _SAL1_Source_(__deref_opt_out_z, (), _Outptr_opt_result_z_) +#define __deref_opt_out_ecount(size) _SAL1_Source_(__deref_opt_out_ecount, (size), __deref_out_ecount(size) __pre_except_maybenull) +#define __deref_opt_out_bcount(size) _SAL1_Source_(__deref_opt_out_bcount, (size), __deref_out_bcount(size) __pre_except_maybenull) +#define __deref_opt_out_ecount_part(size,length) _SAL1_Source_(__deref_opt_out_ecount_part, (size,length), __deref_out_ecount_part(size,length) __pre_except_maybenull) +#define __deref_opt_out_bcount_part(size,length) _SAL1_Source_(__deref_opt_out_bcount_part, (size,length), __deref_out_bcount_part(size,length) __pre_except_maybenull) +#define __deref_opt_out_ecount_full(size) _SAL1_Source_(__deref_opt_out_ecount_full, (size), __deref_out_ecount_full(size) __pre_except_maybenull) +#define __deref_opt_out_bcount_full(size) _SAL1_Source_(__deref_opt_out_bcount_full, (size), __deref_out_bcount_full(size) __pre_except_maybenull) +#define __deref_opt_inout _SAL1_Source_(__deref_opt_inout, (), _Inout_opt_) +#define __deref_opt_inout_ecount(size) _SAL1_Source_(__deref_opt_inout_ecount, (size), __deref_inout_ecount(size) __pre_except_maybenull) +#define __deref_opt_inout_bcount(size) _SAL1_Source_(__deref_opt_inout_bcount, (size), __deref_inout_bcount(size) __pre_except_maybenull) +#define __deref_opt_inout_ecount_part(size,length) _SAL1_Source_(__deref_opt_inout_ecount_part, (size,length), __deref_inout_ecount_part(size,length) __pre_except_maybenull) +#define __deref_opt_inout_bcount_part(size,length) _SAL1_Source_(__deref_opt_inout_bcount_part, (size,length), __deref_inout_bcount_part(size,length) __pre_except_maybenull) +#define __deref_opt_inout_ecount_full(size) _SAL1_Source_(__deref_opt_inout_ecount_full, (size), __deref_inout_ecount_full(size) __pre_except_maybenull) +#define __deref_opt_inout_bcount_full(size) _SAL1_Source_(__deref_opt_inout_bcount_full, (size), __deref_inout_bcount_full(size) __pre_except_maybenull) +#define __deref_opt_inout_z _SAL1_Source_(__deref_opt_inout_z, (), __deref_opt_inout __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_opt_inout_ecount_z(size) _SAL1_Source_(__deref_opt_inout_ecount_z, (size), __deref_opt_inout_ecount(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_opt_inout_bcount_z(size) _SAL1_Source_(__deref_opt_inout_bcount_z, (size), __deref_opt_inout_bcount(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_opt_inout_nz _SAL1_Source_(__deref_opt_inout_nz, (), __deref_opt_inout) +#define __deref_opt_inout_ecount_nz(size) _SAL1_Source_(__deref_opt_inout_ecount_nz, (size), __deref_opt_inout_ecount(size)) +#define __deref_opt_inout_bcount_nz(size) _SAL1_Source_(__deref_opt_inout_bcount_nz, (size), __deref_opt_inout_bcount(size)) +#define __deref_opt_ecount_opt(size) _SAL1_Source_(__deref_opt_ecount_opt, (size), __deref_ecount_opt(size) __pre_except_maybenull) +#define __deref_opt_bcount_opt(size) _SAL1_Source_(__deref_opt_bcount_opt, (size), __deref_bcount_opt(size) __pre_except_maybenull) +#define __deref_opt_out_opt _SAL1_Source_(__deref_opt_out_opt, (), _Outptr_opt_result_maybenull_) +#define __deref_opt_out_ecount_opt(size) _SAL1_Source_(__deref_opt_out_ecount_opt, (size), __deref_out_ecount_opt(size) __pre_except_maybenull) +#define __deref_opt_out_bcount_opt(size) _SAL1_Source_(__deref_opt_out_bcount_opt, (size), __deref_out_bcount_opt(size) __pre_except_maybenull) +#define __deref_opt_out_ecount_part_opt(size,length) _SAL1_Source_(__deref_opt_out_ecount_part_opt, (size,length), __deref_out_ecount_part_opt(size,length) __pre_except_maybenull) +#define __deref_opt_out_bcount_part_opt(size,length) _SAL1_Source_(__deref_opt_out_bcount_part_opt, (size,length), __deref_out_bcount_part_opt(size,length) __pre_except_maybenull) +#define __deref_opt_out_ecount_full_opt(size) _SAL1_Source_(__deref_opt_out_ecount_full_opt, (size), __deref_out_ecount_full_opt(size) __pre_except_maybenull) +#define __deref_opt_out_bcount_full_opt(size) _SAL1_Source_(__deref_opt_out_bcount_full_opt, (size), __deref_out_bcount_full_opt(size) __pre_except_maybenull) +#define __deref_opt_out_z_opt _SAL1_Source_(__deref_opt_out_z_opt, (), __post __deref __valid __refparam __pre_except_maybenull __pre_deref_except_maybenull __post_deref_except_maybenull __post __deref __nullterminated) +#define __deref_opt_out_ecount_z_opt(size) _SAL1_Source_(__deref_opt_out_ecount_z_opt, (size), __deref_opt_out_ecount_opt(size) __post __deref __nullterminated) +#define __deref_opt_out_bcount_z_opt(size) _SAL1_Source_(__deref_opt_out_bcount_z_opt, (size), __deref_opt_out_bcount_opt(size) __post __deref __nullterminated) +#define __deref_opt_out_nz_opt _SAL1_Source_(__deref_opt_out_nz_opt, (), __deref_opt_out_opt) +#define __deref_opt_out_ecount_nz_opt(size) _SAL1_Source_(__deref_opt_out_ecount_nz_opt, (size), __deref_opt_out_ecount_opt(size)) +#define __deref_opt_out_bcount_nz_opt(size) _SAL1_Source_(__deref_opt_out_bcount_nz_opt, (size), __deref_opt_out_bcount_opt(size)) +#define __deref_opt_inout_opt _SAL1_Source_(__deref_opt_inout_opt, (), __deref_inout_opt __pre_except_maybenull) +#define __deref_opt_inout_ecount_opt(size) _SAL1_Source_(__deref_opt_inout_ecount_opt, (size), __deref_inout_ecount_opt(size) __pre_except_maybenull) +#define __deref_opt_inout_bcount_opt(size) _SAL1_Source_(__deref_opt_inout_bcount_opt, (size), __deref_inout_bcount_opt(size) __pre_except_maybenull) +#define __deref_opt_inout_ecount_part_opt(size,length) _SAL1_Source_(__deref_opt_inout_ecount_part_opt, (size,length), __deref_inout_ecount_part_opt(size,length) __pre_except_maybenull) +#define __deref_opt_inout_bcount_part_opt(size,length) _SAL1_Source_(__deref_opt_inout_bcount_part_opt, (size,length), __deref_inout_bcount_part_opt(size,length) __pre_except_maybenull) +#define __deref_opt_inout_ecount_full_opt(size) _SAL1_Source_(__deref_opt_inout_ecount_full_opt, (size), __deref_inout_ecount_full_opt(size) __pre_except_maybenull) +#define __deref_opt_inout_bcount_full_opt(size) _SAL1_Source_(__deref_opt_inout_bcount_full_opt, (size), __deref_inout_bcount_full_opt(size) __pre_except_maybenull) +#define __deref_opt_inout_z_opt _SAL1_Source_(__deref_opt_inout_z_opt, (), __deref_opt_inout_opt __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_opt_inout_ecount_z_opt(size) _SAL1_Source_(__deref_opt_inout_ecount_z_opt, (size), __deref_opt_inout_ecount_opt(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_opt_inout_bcount_z_opt(size) _SAL1_Source_(__deref_opt_inout_bcount_z_opt, (size), __deref_opt_inout_bcount_opt(size) __pre __deref __nullterminated __post __deref __nullterminated) +#define __deref_opt_inout_nz_opt _SAL1_Source_(__deref_opt_inout_nz_opt, (), __deref_opt_inout_opt) +#define __deref_opt_inout_ecount_nz_opt(size) _SAL1_Source_(__deref_opt_inout_ecount_nz_opt, (size), __deref_opt_inout_ecount_opt(size)) +#define __deref_opt_inout_bcount_nz_opt(size) _SAL1_Source_(__deref_opt_inout_bcount_nz_opt, (size), __deref_opt_inout_bcount_opt(size)) + +/* +------------------------------------------------------------------------------- +Advanced Annotation Definitions + +Any of these may be used to directly annotate functions, and may be used in +combination with each other or with regular buffer macros. For an explanation +of each annotation, see the advanced annotations section. +------------------------------------------------------------------------------- +*/ + +#define __success(expr) _SAL1_1_Source_(__success, (expr), _Success_(expr)) +#define __nullterminated _SAL1_Source_(__nullterminated, (), _Null_terminated_) +#define __nullnullterminated _SAL1_Source_(__nullnulltermiated, (), _SAL_nop_impl_) +#define __reserved _SAL1_Source_(__reserved, (), _Reserved_) +#define __checkReturn _SAL1_Source_(__checkReturn, (), _Check_return_) +#define __typefix(ctype) _SAL1_Source_(__typefix, (ctype), __inner_typefix(ctype)) +#define __override __inner_override +#define __callback __inner_callback +#define __format_string _SAL1_1_Source_(__format_string, (), _Printf_format_string_) +#define __blocksOn(resource) _SAL_L_Source_(__blocksOn, (resource), __inner_blocksOn(resource)) +#define __control_entrypoint(category) _SAL_L_Source_(__control_entrypoint, (category), __inner_control_entrypoint(category)) +#define __data_entrypoint(category) _SAL_L_Source_(__data_entrypoint, (category), __inner_data_entrypoint(category)) + +#ifdef _USING_V110_SDK71_ // [ +#ifndef _PREFAST_ // [ +#define __useHeader +#else // ][ +#error Code analysis is not supported when using Visual C++ 11.0/12.0 with the Windows 7.1 SDK. +#endif // ] +#else // ][ +#define __useHeader _Use_decl_anno_impl_ +#endif // ] + +#ifdef _USING_V110_SDK71_ // [ +#ifndef _PREFAST_ // [ +#define __on_failure(annotes) +#else // ][ +#error Code analysis is not supported when using Visual C++ 11.0/12.0 with the Windows 7.1 SDK. +#endif // ] +#else // ][ +#define __on_failure(annotes) _SAL1_1_Source_(__on_failure, (annotes), _On_failure_impl_(annotes _SAL_nop_impl_)) +#endif // ] + +#ifndef __fallthrough // [ + __inner_fallthrough_dec + #define __fallthrough __inner_fallthrough +#endif // ] + +#ifndef __analysis_assume // [ +#ifdef _PREFAST_ // [ +#define __analysis_assume(expr) __assume(expr) +#else // ][ +#define __analysis_assume(expr) +#endif // ] +#endif // ] + +#ifndef _Analysis_assume_ // [ +#ifdef _PREFAST_ // [ +#define _Analysis_assume_(expr) __assume(expr) +#else // ][ +#define _Analysis_assume_(expr) +#endif // ] +#endif // ] + +#define _Analysis_noreturn_ _SAL2_Source_(_Analysis_noreturn_, (), _SA_annotes0(SAL_terminates)) + +#ifdef _PREFAST_ // [ +__inline __nothrow +void __AnalysisAssumeNullterminated(_Post_ _Null_terminated_ void *p); + +#define _Analysis_assume_nullterminated_(x) __AnalysisAssumeNullterminated(x) + +#else // ][ +#define _Analysis_assume_nullterminated_(x) +#endif // ] + +// +// Set the analysis mode (global flags to analysis). +// They take effect at the point of declaration; use at global scope +// as a declaration. +// + +// Synthesize a unique symbol. +#define ___MKID(x, y) x ## y +#define __MKID(x, y) ___MKID(x, y) +#define __GENSYM(x) __MKID(x, __COUNTER__) + +__ANNOTATION(SAL_analysisMode(__AuToQuOtE __In_impl_ char *mode);) + +#define _Analysis_mode_impl_(mode) _SAL2_Source_(_Analysis_mode_impl_, (mode), _SA_annotes1(SAL_analysisMode, #mode)) + +// +// Floating point warnings are only meaningful in kernel-mode on x86 +// so avoid reporting them on other platforms. +// +#ifndef _M_IX86 // [ + +#define _Analysis_mode_(mode) \ + __pragma(warning(disable: 28110 28111 28161 28162)) \ + typedef _Analysis_mode_impl_(mode) int \ + __GENSYM(__prefast_analysis_mode_flag); + +#else // ][ + +#define _Analysis_mode_(mode) \ + typedef _Analysis_mode_impl_(mode) int \ + __GENSYM(__prefast_analysis_mode_flag); + +#endif // ] + +// The following are predefined: +// _Analysis_operator_new_throw_ (operator new throws) +// _Analysis_operator_new_null_ (operator new returns null) +// _Analysis_operator_new_never_fails_ (operator new never fails) +// + +// Function class annotations. +__ANNOTATION(SAL_functionClassNew(__In_impl_ char*);) +__PRIMOP(int, _In_function_class_(__In_impl_ char*);) +#define _In_function_class_(x) _In_function_class_(#x) +#define _Called_from_function_class_(x) _In_function_class_(x) + +#define _Function_class_(x) _SAL2_Source_(_Function_class_, (x), _SA_annotes1(SAL_functionClassNew, _SA_SPECSTRIZE(x))) + +#define _Enum_is_bitflag_ _SAL2_Source_(_Enum_is_bitflag_, (), _SA_annotes0(SAL_enumIsBitflag)) +#define _Strict_type_match_ _SAL2_Source_(_Strict_type_match, (), _SA_annotes0(SAL_strictType2)) + +#define _Maybe_raises_SEH_exception_ _SAL2_Source_(_Maybe_raises_SEH_exception_, (x), _Pre_ _SA_annotes1(SAL_inTry,__yes)) +#define _Raises_SEH_exception_ _SAL2_Source_(_Raises_SEH_exception_, (x), _Maybe_raises_SEH_exception_ _Analysis_noreturn_) + +#ifdef __cplusplus // [ +} +#endif // ] + +#include <ConcurrencySal.h> + diff --git a/test_data/lots_of_files/search.h b/test_data/lots_of_files/search.h new file mode 100644 index 0000000..c9a563d --- /dev/null +++ b/test_data/lots_of_files/search.h @@ -0,0 +1,125 @@ +/*** +*search.h - declarations for searcing/sorting routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains the declarations for the sorting and +* searching routines. +* [System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_SEARCH +#define _INC_SEARCH + +#include <crtdefs.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Function prototypes */ + +#ifndef _CRT_ALGO_DEFINED +#define _CRT_ALGO_DEFINED +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ _CRTIMP void * __cdecl bsearch_s(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements * _SizeOfElements) const void * _Base, + _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ _CRTIMP void * __cdecl bsearch(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements * _SizeOfElements) const void * _Base, + _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); + +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP void __cdecl qsort_s(_Inout_updates_bytes_(_NumOfElements* _SizeOfElements) void * _Base, + _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(void *, const void *, const void *), void *_Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_CRTIMP void __cdecl qsort(_Inout_updates_bytes_(_NumOfElements * _SizeOfElements) void * _Base, + _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); +#endif /* _CRT_ALGO_DEFINED */ + +_Check_return_ _CRTIMP void * __cdecl _lfind_s(_In_ const void * _Key, _In_reads_bytes_((*_NumOfElements) * _SizeOfElements) const void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +_Check_return_ _CRTIMP void * __cdecl _lfind(_In_ const void * _Key, _In_reads_bytes_((*_NumOfElements) * _SizeOfElements) const void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ unsigned int _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); + +_Check_return_ _CRTIMP void * __cdecl _lsearch_s(_In_ const void * _Key, _Inout_updates_bytes_((*_NumOfElements ) * _SizeOfElements) void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +_Check_return_ _CRTIMP void * __cdecl _lsearch(_In_ const void * _Key, _Inout_updates_bytes_((*_NumOfElements ) * _SizeOfElements) void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ unsigned int _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); + +#if defined (__cplusplus) && defined (_M_CEE) +/* + * Managed search routines. Note __cplusplus, this is because we only support + * managed C++. + */ +extern "C++" +{ + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ void * __clrcall bsearch_s(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements*_SizeOfElements) const void * _Base, + _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ void * __clrcall bsearch(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements*_SizeOfElements) const void * _Base, + _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(const void *, const void *)); + +_Check_return_ void * __clrcall _lfind_s(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements*_SizeOfElements) const void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +_Check_return_ void * __clrcall _lfind(_In_ const void * _Key, _In_reads_bytes_((*_NumOfElements)*_SizeOfElements) const void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ unsigned int _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(const void *, const void *)); + +_Check_return_ void * __clrcall _lsearch_s(_In_ const void * _Key, _In_reads_bytes_((*_NumOfElements)*_SizeOfElements) void * _Base, + _In_ unsigned int * _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +_Check_return_ void * __clrcall _lsearch(_In_ const void * _Key, _Inout_updates_bytes_((*_NumOfElements)*_SizeOfElements) void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ unsigned int _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(const void *, const void *)); + +#if __STDC_WANT_SECURE_LIB__ +void __clrcall qsort_s(_Inout_updates_bytes_(_NumOfElements*_SizeOfElements) void * _Base, + _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +void __clrcall qsort(_Inout_updates_bytes_(_NumOfElements*_SizeOfElements) void * _Base, + _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(const void *, const void *)); + +} +#endif /* defined (__cplusplus) && defined (_M_CEE) */ + + +#if !__STDC__ +/* Non-ANSI names for compatibility */ + +_Check_return_ _CRTIMP _CRT_NONSTDC_DEPRECATE(_lfind) void * __cdecl lfind(_In_ const void * _Key, _In_reads_bytes_((*_NumOfElements) * _SizeOfElements) const void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ unsigned int _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); +_Check_return_ _CRTIMP _CRT_NONSTDC_DEPRECATE(_lsearch) void * __cdecl lsearch(_In_ const void * _Key, _Inout_updates_bytes_((*_NumOfElements) * _SizeOfElements) void * _Base, + _Inout_ unsigned int * _NumOfElements, _In_ unsigned int _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); + +#endif /* !__STDC__ */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_SEARCH */ diff --git a/test_data/lots_of_files/setjmp.h b/test_data/lots_of_files/setjmp.h new file mode 100644 index 0000000..a114760 --- /dev/null +++ b/test_data/lots_of_files/setjmp.h @@ -0,0 +1,195 @@ +/*** +*setjmp.h - definitions/declarations for setjmp/longjmp routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines the machine-dependent buffer used by +* setjmp/longjmp to save and restore the program state, and +* declarations for those routines. +* [ANSI/System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_SETJMP +#define _INC_SETJMP + +#include <crtdefs.h> + +#if defined (_M_CEE) +/* + * The reason why simple setjmp won't work here is that there may + * be case when CLR stubs are on the stack e.g. function call just + * after jitting, and not unwinding CLR will result in bad state of + * CLR which then can AV or do something very bad. + */ +#include <setjmpex.h> +#endif /* defined (_M_CEE) */ + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Definitions specific to particular setjmp implementations. + */ + +#if defined (_M_IX86) + +/* + * MS compiler for x86 + */ + +#ifndef _INC_SETJMPEX +#define setjmp _setjmp +#endif /* _INC_SETJMPEX */ + +#define _JBLEN 16 +#define _JBTYPE int + +/* + * Define jump buffer layout for x86 setjmp/longjmp. + */ +typedef struct __JUMP_BUFFER { + unsigned long Ebp; + unsigned long Ebx; + unsigned long Edi; + unsigned long Esi; + unsigned long Esp; + unsigned long Eip; + unsigned long Registration; + unsigned long TryLevel; + unsigned long Cookie; + unsigned long UnwindFunc; + unsigned long UnwindData[6]; +} _JUMP_BUFFER; + +#ifdef _CRTBLD +#ifdef __cplusplus +extern "C" +#endif /* __cplusplus */ +void __stdcall _NLG_Notify(unsigned long); + +#ifdef __cplusplus +extern "C" +#endif /* __cplusplus */ +void __stdcall _NLG_Return(); +#endif /* _CRTBLD */ + +#elif defined (_M_X64) + +typedef struct _CRT_ALIGN(16) _SETJMP_FLOAT128 { + unsigned __int64 Part[2]; +} SETJMP_FLOAT128; + +#define _JBLEN 16 +typedef SETJMP_FLOAT128 _JBTYPE; + +#ifndef _INC_SETJMPEX +#define setjmp _setjmp +#endif /* _INC_SETJMPEX */ + +typedef struct _JUMP_BUFFER { + unsigned __int64 Frame; + unsigned __int64 Rbx; + unsigned __int64 Rsp; + unsigned __int64 Rbp; + unsigned __int64 Rsi; + unsigned __int64 Rdi; + unsigned __int64 R12; + unsigned __int64 R13; + unsigned __int64 R14; + unsigned __int64 R15; + unsigned __int64 Rip; + unsigned long MxCsr; + unsigned short FpCsr; + unsigned short Spare; + + SETJMP_FLOAT128 Xmm6; + SETJMP_FLOAT128 Xmm7; + SETJMP_FLOAT128 Xmm8; + SETJMP_FLOAT128 Xmm9; + SETJMP_FLOAT128 Xmm10; + SETJMP_FLOAT128 Xmm11; + SETJMP_FLOAT128 Xmm12; + SETJMP_FLOAT128 Xmm13; + SETJMP_FLOAT128 Xmm14; + SETJMP_FLOAT128 Xmm15; +} _JUMP_BUFFER; + +#elif defined (_M_ARM) + +#ifndef _INC_SETJMPEX +#define setjmp _setjmp +#endif /* _INC_SETJMPEX */ + +/* + * ARM setjmp definitions. + */ + +#define _JBLEN 28 +#define _JBTYPE int + +typedef struct _JUMP_BUFFER { + unsigned long Frame; + + unsigned long R4; + unsigned long R5; + unsigned long R6; + unsigned long R7; + unsigned long R8; + unsigned long R9; + unsigned long R10; + unsigned long R11; + + unsigned long Sp; + unsigned long Pc; + unsigned long Fpscr; + unsigned long long D[8]; // D8-D15 VFP/NEON regs +} _JUMP_BUFFER; + +#endif /* defined (_M_ARM) */ + + +/* Define the buffer type for holding the state information */ + +#ifndef _JMP_BUF_DEFINED +typedef _JBTYPE jmp_buf[_JBLEN]; +#define _JMP_BUF_DEFINED + +#endif /* _JMP_BUF_DEFINED */ + + +/* Function prototypes */ + +int __cdecl setjmp(_Out_ jmp_buf _Buf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#ifdef __cplusplus +#pragma warning(push) +#pragma warning(disable:4987) +extern "C" +{ +_CRTIMP __declspec(noreturn) void __cdecl longjmp(_In_ jmp_buf _Buf, _In_ int _Value) throw(...); +} +#pragma warning(pop) +#else /* __cplusplus */ +_CRTIMP __declspec(noreturn) void __cdecl longjmp(_In_ jmp_buf _Buf, _In_ int _Value); +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_SETJMP */ diff --git a/test_data/lots_of_files/setjmpex.h b/test_data/lots_of_files/setjmpex.h new file mode 100644 index 0000000..3a8b9d4 --- /dev/null +++ b/test_data/lots_of_files/setjmpex.h @@ -0,0 +1,47 @@ +/*** +*setjmpex.h - definitions/declarations for extended setjmp/longjmp routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file causes _setjmpex to be called which will enable safe +* setjmp/longjmp that work correctly with try/except/finally. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_SETJMPEX +#define _INC_SETJMPEX + +#if !defined (_WIN32) +#error ERROR: Only Win32 target supported! +#endif /* !defined (_WIN32) */ + +/* + * Definitions specific to particular setjmp implementations. + */ + +#if defined (_M_IX86) + +/* + * MS compiler for x86 + */ + +#define setjmp _setjmp +#define longjmp _longjmpex + +#else /* defined (_M_IX86) */ + +#ifdef setjmp +#undef setjmp +#endif /* setjmp */ +#define setjmp _setjmpex + +#endif /* defined (_M_IX86) */ + +#include <setjmp.h> + +#endif /* _INC_SETJMPEX */ diff --git a/test_data/lots_of_files/share.h b/test_data/lots_of_files/share.h new file mode 100644 index 0000000..faa57ac --- /dev/null +++ b/test_data/lots_of_files/share.h @@ -0,0 +1,36 @@ +/*** +*share.h - defines file sharing modes for sopen +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines the file sharing modes for sopen(). +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_SHARE +#define _INC_SHARE + +#if !defined (_WIN32) +#error ERROR: Only Win32 target supported! +#endif /* !defined (_WIN32) */ + +#define _SH_DENYRW 0x10 /* deny read/write mode */ +#define _SH_DENYWR 0x20 /* deny write mode */ +#define _SH_DENYRD 0x30 /* deny read mode */ +#define _SH_DENYNO 0x40 /* deny none mode */ +#define _SH_SECURE 0x80 /* secure mode */ + +#if !__STDC__ +/* Non-ANSI names for compatibility */ +#define SH_DENYRW _SH_DENYRW +#define SH_DENYWR _SH_DENYWR +#define SH_DENYRD _SH_DENYRD +#define SH_DENYNO _SH_DENYNO +#endif /* !__STDC__ */ + +#endif /* _INC_SHARE */ diff --git a/test_data/lots_of_files/shmup.cpp b/test_data/lots_of_files/shmup.cpp new file mode 100644 index 0000000..b142cc1 --- /dev/null +++ b/test_data/lots_of_files/shmup.cpp @@ -0,0 +1,88 @@ + +#define AllowLocal(x) ((void)x) + +#include "lua.hpp" + +typedef struct BufferLoadS{ + char *s; + int size; +} BufferLoadS; + +const char* +buffer_get_s(lua_State *S, void *u, size_t *size){ + const char *result = 0; + BufferLoadS *load = (BufferLoadS*)u; + AllowLocal(S); + if (load->size != 0){ + result = load->s; + *size = load->size; + load->size = 0; + } + return(result); +} + +int +load_buffer(lua_State *S, char *data, int size, char *name, char *mode){ + int result; + BufferLoadS load; + load.s = data; + load.size = size; + result = lua_load(S, buffer_get_s, &load, name, mode); + return(result); +} + +#include <stdlib.h> +#include <stdio.h> + +void* +shmup_lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize){ + // TODO(allen): replace this so we can get away from standard lib! + (void)ud; (void)osize; /* not used */ + if (nsize == 0) { + free(ptr); + return NULL; + } + else + return realloc(ptr, nsize); +} + +int +shmup_lua_panic(lua_State *lua){ + printf("you fuck up you!\n"); + return 0; +} + +int +main(){ + lua_State *S; + S = lua_newstate(shmup_lua_alloc, NULL); + if (S) lua_atpanic(S, &shmup_lua_panic); + + int x = 0, result; + char buffer[] = "return 1 + 2"; + result = load_buffer(S, buffer, sizeof(buffer) - 1, "test", NULL); + switch (result){ + case LUA_OK: + lua_call(S, 0, LUA_MULTRET); + x = (int)lua_tointeger(S, 0); + lua_pop(S, 1); + break; + + case LUA_ERRSYNTAX: + printf("LUA_ERRSYNTAX\n"); + break; + + case LUA_ERRMEM: + printf("LUA_ERRMEM\n"); + break; + + case LUA_ERRGCMM: + printf("LUA_ERRGCMM\n"); + break; + } + + printf("x = %d\n", x); + + lua_close(S); + return(0); +} \ No newline at end of file diff --git a/test_data/lots_of_files/shmup.ctm b/test_data/lots_of_files/shmup.ctm new file mode 100644 index 0000000..7489f06 Binary files /dev/null and b/test_data/lots_of_files/shmup.ctm differ diff --git a/test_data/lots_of_files/signal.h b/test_data/lots_of_files/signal.h new file mode 100644 index 0000000..1788cf2 --- /dev/null +++ b/test_data/lots_of_files/signal.h @@ -0,0 +1,103 @@ +/*** +*signal.h - defines signal values and routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines the signal values and declares the signal functions. +* [ANSI/System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_SIGNAL +#define _INC_SIGNAL + +#include <crtdefs.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Define __cdecl for non-Microsoft compilers */ + +#ifndef _SIG_ATOMIC_T_DEFINED +typedef int sig_atomic_t; +#define _SIG_ATOMIC_T_DEFINED +#endif /* _SIG_ATOMIC_T_DEFINED */ + +#define NSIG 23 /* maximum signal number + 1 */ + + +/* Signal types */ + +#define SIGINT 2 /* interrupt */ +#define SIGILL 4 /* illegal instruction - invalid function image */ +#define SIGFPE 8 /* floating point exception */ +#define SIGSEGV 11 /* segment violation */ +#define SIGTERM 15 /* Software termination signal from kill */ +#define SIGBREAK 21 /* Ctrl-Break sequence */ +#define SIGABRT 22 /* abnormal termination triggered by abort call */ + +#define SIGABRT_COMPAT 6 /* SIGABRT compatible with other platforms, same as SIGABRT */ + +#ifndef _M_CEE_PURE +/* signal action codes */ + +#define SIG_DFL (void (__cdecl *)(int))0 /* default signal action */ +#define SIG_IGN (void (__cdecl *)(int))1 /* ignore signal */ +#define SIG_GET (void (__cdecl *)(int))2 /* return current value */ +#define SIG_SGE (void (__cdecl *)(int))3 /* signal gets error */ +#define SIG_ACK (void (__cdecl *)(int))4 /* acknowledge */ + +#ifdef _CRTBLD +/* internal use only! not valid as an argument to signal() */ + +#define SIG_DIE (void (__cdecl *)(int))5 /* terminate process */ +#endif /* _CRTBLD */ + +/* signal error value (returned by signal call on error) */ + +#define SIG_ERR (void (__cdecl *)(int))-1 /* signal error value */ +#else /* _M_CEE_PURE */ +/* signal action codes */ + +#define SIG_DFL (void (__clrcall *)(int))0 /* default signal action */ +#define SIG_IGN (void (__clrcall *)(int))1 /* ignore signal */ +#define SIG_GET (void (__clrcall *)(int))2 /* return current value */ +#define SIG_SGE (void (__clrcall *)(int))3 /* signal gets error */ +#define SIG_ACK (void (__clrcall *)(int))4 /* acknowledge */ + +#ifdef _CRTBLD +/* internal use only! not valid as an argument to signal() */ + +#define SIG_DIE (void (__clrcall *)(int))5 /* terminate process */ +#endif /* _CRTBLD */ + +/* signal error value (returned by signal call on error) */ + +#define SIG_ERR (void (__clrcall *)(int))-1 /* signal error value */ +#endif /* _M_CEE_PURE */ + + +/* pointer to exception information pointers structure */ + +extern void * * __cdecl __pxcptinfoptrs(void); +#define _pxcptinfoptrs (*__pxcptinfoptrs()) + +/* Function prototypes */ + +#ifndef _M_CEE_PURE +_CRTIMP void (__cdecl * __cdecl signal(_In_ int _SigNum, _In_opt_ void (__cdecl * _Func)(int)))(int); +#endif /* _M_CEE_PURE */ + +_CRTIMP int __cdecl raise(_In_ int _SigNum); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_SIGNAL */ diff --git a/test_data/lots_of_files/simple_preprocessor.cpp b/test_data/lots_of_files/simple_preprocessor.cpp new file mode 100644 index 0000000..b1cb8d0 --- /dev/null +++ b/test_data/lots_of_files/simple_preprocessor.cpp @@ -0,0 +1,438 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> + +struct meta_struct +{ + char *Name; + meta_struct *Next; +}; +static meta_struct *FirstMetaStruct; + +static char * +ReadEntireFileIntoMemoryAndNullTerminate(char *FileName) +{ + char *Result = 0; + + FILE *File = fopen(FileName, "r"); + if(File) + { + fseek(File, 0, SEEK_END); + size_t FileSize = ftell(File); + fseek(File, 0, SEEK_SET); + + Result = (char *)malloc(FileSize + 1); + fread(Result, FileSize, 1, File); + Result[FileSize] = 0; + + fclose(File); + } + + return(Result); +} + +enum token_type +{ + Token_Unknown, + + Token_OpenParen, + Token_CloseParen, + Token_Colon, + Token_Semicolon, + Token_Asterisk, + Token_OpenBracket, + Token_CloseBracket, + Token_OpenBrace, + Token_CloseBrace, + + Token_String, + Token_Identifier, + + Token_EndOfStream, +}; +struct token +{ + token_type Type; + + size_t TextLength; + char *Text; +}; + +struct tokenizer +{ + char *At; +}; + +inline bool +IsEndOfLine(char C) +{ + bool Result = ((C == '\n') || + (C == '\r')); + + return(Result); +} + +inline bool +IsWhitespace(char C) +{ + bool Result = ((C == ' ') || + (C == '\t') || + (C == '\v') || + (C == '\f') || + IsEndOfLine(C)); + + return(Result); +} + +inline bool +IsAlpha(char C) +{ + bool Result = (((C >= 'a') && (C <= 'z')) || + ((C >= 'A') && (C <= 'Z'))); + + return(Result); +} + +inline bool +IsNumber(char C) +{ + bool Result = ((C >= '0') && (C <= '9')); + + return(Result); +} + +inline bool +TokenEquals(token Token, char *Match) +{ + char *At = Match; + for(int Index = 0; + Index < Token.TextLength; + ++Index, ++At) + { + if((*At == 0) || + (Token.Text[Index] != *At)) + { + return(false); + } + + } + + bool Result = (*At == 0); + return(Result); +} + +static void +EatAllWhitespace(tokenizer *Tokenizer) +{ + for(;;) + { + if(IsWhitespace(Tokenizer->At[0])) + { + ++Tokenizer->At; + } + else if((Tokenizer->At[0] == '/') && + (Tokenizer->At[1] == '/')) + { + Tokenizer->At += 2; + while(Tokenizer->At[0] && !IsEndOfLine(Tokenizer->At[0])) + { + ++Tokenizer->At; + } + } + else if((Tokenizer->At[0] == '/') && + (Tokenizer->At[1] == '*')) + { + Tokenizer->At += 2; + while(Tokenizer->At[0] && + !((Tokenizer->At[0] == '*') && + (Tokenizer->At[1] == '/'))) + { + ++Tokenizer->At; + } + + if(Tokenizer->At[0] == '*') + { + Tokenizer->At += 2; + } + } + else + { + break; + } + } +} + +static token +GetToken(tokenizer *Tokenizer) +{ + EatAllWhitespace(Tokenizer); + + token Token = {}; + Token.TextLength = 1; + Token.Text = Tokenizer->At; + char C = Tokenizer->At[0]; + ++Tokenizer->At; + switch(C) + { + case '\0': {Token.Type = Token_EndOfStream;} break; + + case '(': {Token.Type = Token_OpenParen;} break; + case ')': {Token.Type = Token_CloseParen;} break; + case ':': {Token.Type = Token_Colon;} break; + case ';': {Token.Type = Token_Semicolon;} break; + case '*': {Token.Type = Token_Asterisk;} break; + case '[': {Token.Type = Token_OpenBracket;} break; + case ']': {Token.Type = Token_CloseBracket;} break; + case '{': {Token.Type = Token_OpenBrace;} break; + case '}': {Token.Type = Token_CloseBrace;} break; + + case '"': + { + Token.Type = Token_String; + + Token.Text = Tokenizer->At; + + while(Tokenizer->At[0] && + Tokenizer->At[0] != '"') + { + if((Tokenizer->At[0] == '\\') && + Tokenizer->At[1]) + { + ++Tokenizer->At; + } + ++Tokenizer->At; + } + + Token.TextLength = Tokenizer->At - Token.Text; + if(Tokenizer->At[0] == '"') + { + ++Tokenizer->At; + } + } break; + + default: + { + if(IsAlpha(C)) + { + Token.Type = Token_Identifier; + + while(IsAlpha(Tokenizer->At[0]) || + IsNumber(Tokenizer->At[0]) || + (Tokenizer->At[0] == '_')) + { + ++Tokenizer->At; + } + + Token.TextLength = Tokenizer->At - Token.Text; + } +#if 0 + else if(IsNumeric(C)) + { + ParseNumber(); + } +#endif + else + { + Token.Type = Token_Unknown; + } + } break; + } + + return(Token); +} + +static bool +RequireToken(tokenizer *Tokenizer, token_type DesiredType) +{ + token Token = GetToken(Tokenizer); + bool Result = (Token.Type == DesiredType); + return(Result); +} + +static void +ParseIntrospectionParams(tokenizer *Tokenizer) +{ + for(;;) + { + token Token = GetToken(Tokenizer); + if((Token.Type == Token_CloseParen) || + (Token.Type == Token_EndOfStream)) + { + break; + } + } +} + +static void +ParseMember(tokenizer *Tokenizer, token StructTypeToken, token MemberTypeToken) +{ +#if 1 + bool Parsing = true; + bool IsPointer = false; + while(Parsing) + { + token Token = GetToken(Tokenizer); + switch(Token.Type) + { + case Token_Asterisk: + { + IsPointer = true; + } break; + + case Token_Identifier: + { + printf(" {%s, MetaType_%.*s, \"%.*s\", (u32)&((%.*s *)0)->%.*s},\n", + IsPointer ? "MetaMemberFlag_IsPointer" : "0", + MemberTypeToken.TextLength, MemberTypeToken.Text, + Token.TextLength, Token.Text, + StructTypeToken.TextLength, StructTypeToken.Text, + Token.TextLength, Token.Text); + } break; + + case Token_Semicolon: + case Token_EndOfStream: + { + + Parsing = false; + } break; + } + } +#else + token Token = GetToken(Tokenizer); + switch(Token.Type) + { + case Token_Asterisk: + { + ParseMember(Tokenizer, Token); + } break; + + case Token_Identifier: + { + printf("DEBUG_VALUE(%.*s);\n", Token.TextLength, Token.Text); + } break; + } +#endif +} + +static void +ParseStruct(tokenizer *Tokenizer) +{ + token NameToken = GetToken(Tokenizer); + if(RequireToken(Tokenizer, Token_OpenBrace)) + { + printf("member_definition MembersOf_%.*s[] = \n", NameToken.TextLength, NameToken.Text); + printf("{\n"); + for(;;) + { + token MemberToken = GetToken(Tokenizer); + if(MemberToken.Type == Token_CloseBrace) + { + break; + } + else + { + ParseMember(Tokenizer, NameToken, MemberToken); + } + } + printf("};\n"); + + meta_struct *Meta = (meta_struct *)malloc(sizeof(meta_struct)); + Meta->Name = (char *)malloc(NameToken.TextLength + 1); + memcpy(Meta->Name, NameToken.Text, NameToken.TextLength); + Meta->Name[NameToken.TextLength] = 0; + Meta->Next = FirstMetaStruct; + FirstMetaStruct = Meta; + } +} + +static void +ParseIntrospectable(tokenizer *Tokenizer) +{ + if(RequireToken(Tokenizer, Token_OpenParen)) + { + ParseIntrospectionParams(Tokenizer); + + token TypeToken = GetToken(Tokenizer); + if(TokenEquals(TypeToken, "struct")) + { + ParseStruct(Tokenizer); + } + else + { + fprintf(stderr, "ERROR: Introspection is only supported for structs right now :(\n"); + } + } + else + { + fprintf(stderr, "ERROR: Missing parentheses.\n"); + } +} + +int +main(int ArgCount, char **Args) +{ + char *FileNames[] = + { + "handmade_sim_region.h", + "handmade_platform.h", + "handmade_math.h", + "handmade_world.h", + }; + for(int FileIndex = 0; + FileIndex < (sizeof(FileNames)/sizeof(FileNames[0])); + ++FileIndex) + { + char *FileContents = ReadEntireFileIntoMemoryAndNullTerminate(FileNames[FileIndex]); + + tokenizer Tokenizer = {}; + Tokenizer.At = FileContents; + + bool Parsing = true; + while(Parsing) + { + token Token = GetToken(&Tokenizer); + switch(Token.Type) + { + case Token_EndOfStream: + { + Parsing = false; + } break; + + case Token_Unknown: + { + } break; + + case Token_Identifier: + { + if(TokenEquals(Token, "introspect")) + { + ParseIntrospectable(&Tokenizer); + } + } break; + + default: + { +// printf("%d: %.*s\n", Token.Type, Token.TextLength, Token.Text); + } break; + } + } + } + + printf("#define META_HANDLE_TYPE_DUMP(MemberPtr, NextIndentLevel) \\\n"); + for(meta_struct *Meta = FirstMetaStruct; + Meta; + Meta = Meta->Next) + { + printf(" case MetaType_%s: {DEBUGTextLine(Member->Name); DEBUGDumpStruct(ArrayCount(MembersOf_%s), MembersOf_%s, MemberPtr, (NextIndentLevel));} break; %s\n", + Meta->Name, Meta->Name, Meta->Name, + Meta->Next ? "\\" : ""); + } +} diff --git a/test_data/lots_of_files/simulate.c b/test_data/lots_of_files/simulate.c new file mode 100644 index 0000000..ed099b8 --- /dev/null +++ b/test_data/lots_of_files/simulate.c @@ -0,0 +1,167 @@ +/* EECS 370 LC-2K Instruction-level simulator */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define NUMMEMORY 65536 /* maximum number of words in memory */ +#define NUMREGS 8 /* number of machine registers */ +#define MAXLINELENGTH 1000 + +typedef struct stateStruct { + int pc; + int mem[NUMMEMORY]; + int reg[NUMREGS]; + int numMemory; +} stateType; + +void printState(stateType *); + +#define OP_FILL -1 +#define OP_ADD 0 +#define OP_NAND 1 +#define OP_LW 2 +#define OP_SW 3 +#define OP_BEQ 4 +#define OP_JALR 5 +#define OP_HALT 6 +#define OP_NOOP 7 + +#define SHIFT_OP 22 +#define SHIFT_A 19 +#define SHIFT_B 16 +#define SHIFT_C 0 + +#define MASK_OP (7 << SHIFT_OP) +#define MASK_A (7 << SHIFT_A) +#define MASK_B (7 << SHIFT_B) +#define MASK_C (0xFFFF) + +void +decode_insanity(int ins, int *o, int *a, int *b, int *c){ + *o = (ins & (MASK_OP)) >> SHIFT_OP; + *a = (ins & (MASK_A)) >> SHIFT_A; + *b = (ins & (MASK_B)) >> SHIFT_B; + *c = (ins & (MASK_C)) >> SHIFT_C; + if (*c & 0x8000) *c |= 0xFFFF0000; +} + +int DID_NOOP; +int +execute_insanity(stateType *state, int *e){ + int r; + int o,a,b,c; + int m,pc; + + pc = state->pc++; + decode_insanity(state->mem[pc], &o, &a, &b, &c); + + DID_NOOP = 0; + r = 1; + switch (o){ + case OP_ADD: + state->reg[c] = state->reg[a] + state->reg[b]; + break; + + case OP_NAND: + state->reg[c] = ~(state->reg[a] & state->reg[b]); + break; + + case OP_LW: + m = state->reg[a] + c; + state->reg[b] = state->mem[m]; + break; + + case OP_SW: + m = state->reg[a] + c; + state->mem[m] = state->reg[b]; + break; + + case OP_BEQ: + if (state->reg[a] == state->reg[b]) state->pc += c; + break; + + case OP_JALR: + state->reg[b] = pc+1; + state->pc = state->reg[a]; + break; + + case OP_HALT: + r = 0; + break; + + case OP_NOOP: DID_NOOP = 1; break; + + default: r = 0; *e = 1; break; + } + + return (r); +} + +int +main(int argc, char *argv[]) +{ + char line[MAXLINELENGTH]; + stateType state; + FILE *filePtr; + int cont, e, step_count; + + if (argc != 2) { + printf("error: usage: %s <machine-code file>\n", argv[0]); + exit(1); + } + + filePtr = fopen(argv[1], "r"); + if (filePtr == NULL) { + printf("error: can't open file %s", argv[1]); + perror("fopen"); + exit(1); + } + + state.pc = 0; + memset(state.reg, 0, sizeof(int)*NUMREGS); + memset(state.mem, 0, sizeof(int)*NUMMEMORY); + + /* read in the entire machine-code file into memory */ + for (state.numMemory = 0; fgets(line, MAXLINELENGTH, filePtr) != NULL; + state.numMemory++) { + + if (sscanf(line, "%d", state.mem+state.numMemory) != 1) { + printf("error in reading address %d\n", state.numMemory); + exit(1); + } + printf("memory[%d]=%d\n", state.numMemory, state.mem[state.numMemory]); + } + + step_count = 0; + cont = 1; + while (cont){ + printf("steps: %d\n", step_count++); + cont = execute_insanity(&state, &e); + printState(&state); + int x = 0; + } + // if (e) { /*there was an error*/ } + printState(&state); + printf("steps: %d\n", step_count++); + + return(0); +} + +void +printState(stateType *statePtr) +{ + int i; + printf("\n@@@\nstate:\n"); + printf("\tpc %d\n", statePtr->pc); + printf("\tmemory:\n"); + for (i=0; i<statePtr->numMemory; i++) { + printf("\t\tmem[ %d ] %d\n", i, statePtr->mem[i]); + } + printf("\tregisters:\n"); + for (i=0; i<NUMREGS; i++) { + printf("\t\treg[ %d ] %d\n", i, statePtr->reg[i]); + } + printf("end state\n"); +} + diff --git a/test_data/lots_of_files/smallest_multiple.cpp b/test_data/lots_of_files/smallest_multiple.cpp new file mode 100644 index 0000000..b6435a4 --- /dev/null +++ b/test_data/lots_of_files/smallest_multiple.cpp @@ -0,0 +1,78 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: + +// req: size of mem >= CEIL[top/2]*8 +int smallest_multiple(int top, int *mem){ + int *factors = mem; + int factor_count = 0; + int n,m,o,r=2; + factors[factor_count++] = 2; + for (int i = 3; i <= top; ++i){ + n = i; + for (int j = 0; j < factor_count && n > 1; ++j){ + m = factors[j]; + o = n / m; + if (o*m == n){ + n = o; + } + } + if (n > 1){ + factors[factor_count++] = n; + r *= n; + } + } + return r; +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{}; + +struct Euler_Result{ + int smallest_multiple; +}; + +static const int TOP = 20; + +inline Euler_Result euler_main(Euler_In in){ + int f[TOP]; + + Euler_Result result; + result.smallest_multiple = smallest_multiple(TOP, f); + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.smallest_multiple); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result; + + bool not_found = 1; + int x = TOP - 1; + while (not_found){ + ++x; + not_found = 0; + for (int y = 2; y <= TOP; ++y){ + if (x % y != 0){ + not_found = 1; + break; + } + } + } + + printf("answer = %d\n", x); + result = (answer.smallest_multiple == x); + + return result; +} + +#endif + + diff --git a/test_data/lots_of_files/smmintrin.h b/test_data/lots_of_files/smmintrin.h new file mode 100644 index 0000000..b1fe23a --- /dev/null +++ b/test_data/lots_of_files/smmintrin.h @@ -0,0 +1,258 @@ +/*** +*** Copyright (C) 1985-2007 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * smmintrin.h + * + * Principal header file for Intel(R) Core(TM) 2 Duo processor + * SSE4.1 intrinsics + */ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_SMM +#define _INCLUDED_SMM + +#if defined (_M_CEE_PURE) + #error ERROR: EMM intrinsics not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <tmmintrin.h> + + +/* + * Rounding mode macros + */ + +#define _MM_FROUND_TO_NEAREST_INT 0x00 +#define _MM_FROUND_TO_NEG_INF 0x01 +#define _MM_FROUND_TO_POS_INF 0x02 +#define _MM_FROUND_TO_ZERO 0x03 +#define _MM_FROUND_CUR_DIRECTION 0x04 + +#define _MM_FROUND_RAISE_EXC 0x00 +#define _MM_FROUND_NO_EXC 0x08 + +#define _MM_FROUND_NINT _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_RAISE_EXC +#define _MM_FROUND_FLOOR _MM_FROUND_TO_NEG_INF | _MM_FROUND_RAISE_EXC +#define _MM_FROUND_CEIL _MM_FROUND_TO_POS_INF | _MM_FROUND_RAISE_EXC +#define _MM_FROUND_TRUNC _MM_FROUND_TO_ZERO | _MM_FROUND_RAISE_EXC +#define _MM_FROUND_RINT _MM_FROUND_CUR_DIRECTION | _MM_FROUND_RAISE_EXC +#define _MM_FROUND_NEARBYINT _MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC + +/* + * MACRO functions for ceil/floor intrinsics + */ + +#define _mm_ceil_pd(val) _mm_round_pd((val), _MM_FROUND_CEIL) +#define _mm_ceil_sd(dst, val) _mm_round_sd((dst), (val), _MM_FROUND_CEIL) + +#define _mm_floor_pd(val) _mm_round_pd((val), _MM_FROUND_FLOOR) +#define _mm_floor_sd(dst, val) _mm_round_sd((dst), (val), _MM_FROUND_FLOOR) + +#define _mm_ceil_ps(val) _mm_round_ps((val), _MM_FROUND_CEIL) +#define _mm_ceil_ss(dst, val) _mm_round_ss((dst), (val), _MM_FROUND_CEIL) + +#define _mm_floor_ps(val) _mm_round_ps((val), _MM_FROUND_FLOOR) +#define _mm_floor_ss(dst, val) _mm_round_ss((dst), (val), _MM_FROUND_FLOOR) + +#define _mm_test_all_zeros(mask, val) _mm_testz_si128((mask), (val)) + +/* + * MACRO functions for packed integer 128-bit comparison intrinsics. + */ + +#define _mm_test_all_ones(val) \ + _mm_testc_si128((val), _mm_cmpeq_epi32((val),(val))) + +#define _mm_test_mix_ones_zeros(mask, val) _mm_testnzc_si128((mask), (val)) + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + + // Integer blend instructions - select data from 2 sources + // using constant/variable mask + + extern __m128i _mm_blend_epi16 (__m128i v1, __m128i v2, + const int mask); + extern __m128i _mm_blendv_epi8 (__m128i v1, __m128i v2, __m128i mask); + + // Float single precision blend instructions - select data + // from 2 sources using constant/variable mask + + extern __m128 _mm_blend_ps (__m128 v1, __m128 v2, const int mask); + extern __m128 _mm_blendv_ps(__m128 v1, __m128 v2, __m128 v3); + + // Float double precision blend instructions - select data + // from 2 sources using constant/variable mask + + extern __m128d _mm_blend_pd (__m128d v1, __m128d v2, const int mask); + extern __m128d _mm_blendv_pd(__m128d v1, __m128d v2, __m128d v3); + + // Dot product instructions with mask-defined summing and zeroing + // of result's parts + + extern __m128 _mm_dp_ps(__m128 val1, __m128 val2, const int mask); + extern __m128d _mm_dp_pd(__m128d val1, __m128d val2, const int mask); + + // Packed integer 64-bit comparison, zeroing or filling with ones + // corresponding parts of result + + extern __m128i _mm_cmpeq_epi64(__m128i val1, __m128i val2); + + // Min/max packed integer instructions + + extern __m128i _mm_min_epi8 (__m128i val1, __m128i val2); + extern __m128i _mm_max_epi8 (__m128i val1, __m128i val2); + + extern __m128i _mm_min_epu16(__m128i val1, __m128i val2); + extern __m128i _mm_max_epu16(__m128i val1, __m128i val2); + + extern __m128i _mm_min_epi32(__m128i val1, __m128i val2); + extern __m128i _mm_max_epi32(__m128i val1, __m128i val2); + extern __m128i _mm_min_epu32(__m128i val1, __m128i val2); + extern __m128i _mm_max_epu32(__m128i val1, __m128i val2); + + // Packed integer 32-bit multiplication with truncation + // of upper halves of results + + extern __m128i _mm_mullo_epi32(__m128i a, __m128i b); + + // Packed integer 32-bit multiplication of 2 pairs of operands + // producing two 64-bit results + + extern __m128i _mm_mul_epi32(__m128i a, __m128i b); + + // Packed integer 128-bit bitwise comparison. + // return 1 if (val 'and' mask) == 0 + + extern int _mm_testz_si128(__m128i mask, __m128i val); + + // Packed integer 128-bit bitwise comparison. + // return 1 if (val 'and_not' mask) == 0 + + extern int _mm_testc_si128(__m128i mask, __m128i val); + + // Packed integer 128-bit bitwise comparison + // ZF = ((val 'and' mask) == 0) CF = ((val 'and_not' mask) == 0) + // return 1 if both ZF and CF are 0 + + extern int _mm_testnzc_si128(__m128i mask, __m128i s2); + + // Insert single precision float into packed single precision + // array element selected by index. + // The bits [7-6] of the 3d parameter define src index, + // the bits [5-4] define dst index, and bits [3-0] define zeroing + // mask for dst + + extern __m128 _mm_insert_ps(__m128 dst, __m128 src, const int ndx); + + // Helper macro to create ndx-parameter value for _mm_insert_ps + +#define _MM_MK_INSERTPS_NDX(srcField, dstField, zeroMask) \ + (((srcField)<<6) | ((dstField)<<4) | (zeroMask)) + + // Extract binary representation of single precision float from + // packed single precision array element selected by index + + extern int _mm_extract_ps(__m128 src, const int ndx); + + // Extract single precision float from packed single precision + // array element selected by index into dest + +#define _MM_EXTRACT_FLOAT(dest, src, ndx) \ + *((int*)&(dest)) = _mm_extract_ps((src), (ndx)) + + // Extract specified single precision float element + // into the lower part of __m128 + +#define _MM_PICK_OUT_PS(src, num) \ + _mm_insert_ps(_mm_setzero_ps(), (src), \ + _MM_MK_INSERTPS_NDX((num), 0, 0x0e)) + + // Insert integer into packed integer array element + // selected by index + + extern __m128i _mm_insert_epi8 (__m128i dst, int s, const int ndx); + extern __m128i _mm_insert_epi32(__m128i dst, int s, const int ndx); + +#if defined (_M_X64) + extern __m128i _mm_insert_epi64(__m128i dst, __int64 s, const int ndx); +#endif /* defined (_M_X64) */ + // Extract integer from packed integer array element + // selected by index + + extern int _mm_extract_epi8 (__m128i src, const int ndx); + extern int _mm_extract_epi32(__m128i src, const int ndx); + +#if defined (_M_X64) + extern __int64 _mm_extract_epi64(__m128i src, const int ndx); +#endif /* defined (_M_X64) */ + + // Horizontal packed word minimum and its index in + // result[15:0] and result[18:16] respectively + + extern __m128i _mm_minpos_epu16(__m128i shortValues); + + // Packed/single float double precision rounding + + extern __m128d _mm_round_pd(__m128d val, int iRoundMode); + extern __m128d _mm_round_sd(__m128d dst, __m128d val, int iRoundMode); + + // Packed/single float single precision rounding + + extern __m128 _mm_round_ps(__m128 val, int iRoundMode); + extern __m128 _mm_round_ss(__m128 dst, __m128 val, int iRoundMode); + + // Packed integer sign-extension + + extern __m128i _mm_cvtepi8_epi32 (__m128i byteValues); + extern __m128i _mm_cvtepi16_epi32(__m128i shortValues); + extern __m128i _mm_cvtepi8_epi64 (__m128i byteValues); + extern __m128i _mm_cvtepi32_epi64(__m128i intValues); + extern __m128i _mm_cvtepi16_epi64(__m128i shortValues); + extern __m128i _mm_cvtepi8_epi16 (__m128i byteValues); + + // Packed integer zero-extension + + extern __m128i _mm_cvtepu8_epi32 (__m128i byteValues); + extern __m128i _mm_cvtepu16_epi32(__m128i shortValues); + extern __m128i _mm_cvtepu8_epi64 (__m128i shortValues); + extern __m128i _mm_cvtepu32_epi64(__m128i intValues); + extern __m128i _mm_cvtepu16_epi64(__m128i shortValues); + extern __m128i _mm_cvtepu8_epi16 (__m128i byteValues); + + + // Pack 8 double words from 2 operands into 8 words of result + // with unsigned saturation + + extern __m128i _mm_packus_epi32(__m128i val1, __m128i val2); + + // Sum absolute 8-bit integer difference of adjacent groups of 4 byte + // integers in operands. Starting offsets within operands are + // determined by mask + + extern __m128i _mm_mpsadbw_epu8(__m128i s1, __m128i s2, const int msk); + + /* + * Load double quadword using non-temporal aligned hint + */ + + extern __m128i _mm_stream_load_si128(__m128i* v1); + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INCLUDED_SMM */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/special_pythagorean_triplet.cpp b/test_data/lots_of_files/special_pythagorean_triplet.cpp new file mode 100644 index 0000000..51da67c --- /dev/null +++ b/test_data/lots_of_files/special_pythagorean_triplet.cpp @@ -0,0 +1,100 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +#ifdef EULER_PROBLEM +// BUILDING AREA: + +struct Euler_In{}; + +struct Euler_Result{ + int product; +}; + +static const int PYTHAG_SUM = 1000; + +Euler_Result euler_main(Euler_In in){ +#if 0 + int a,b,c=1; + int sa,sb,sc; + int g=1; + + while (g){ + ++c; + + } +#endif +#if 1 + int a,b,c; + int sa,sb,sc; + int e; + int g=1; + + c = ((PYTHAG_SUM+2)/3) + 1; + + while (g){ + ++c; + sc = c*c; + b = c-1; + a = PYTHAG_SUM-b-c; + while (a<b){ + sa = a*a; + sb = b*b; + if (sa+sb == sc){ + g=0; + break; + } + --b; + a = PYTHAG_SUM-b-c; + } + } + +#endif + + int p=a*b*c; + Euler_Result result; + result.product = p; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.product); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result = 1; + + int a,b,c=0; + int sa,sb,sc; + int e; + int g=1; + + while (g){ + ++c; + sc = c*c; + a = 0; + b = PYTHAG_SUM-a-c; + while (a<b){ + sa = a*a; + sb = b*b; + if (sa+sb == sc){ + g=0; + break; + } + ++a; + b = PYTHAG_SUM-a-c; + } + } + + int p=a*b*c; + + printf("answer = %d, a=%d, b=%d, c=%d\n", p, a, b, c); + result = (p == answer.product); + + return result; +} +#endif + diff --git a/test_data/lots_of_files/srv.h b/test_data/lots_of_files/srv.h new file mode 100644 index 0000000..092a13c --- /dev/null +++ b/test_data/lots_of_files/srv.h @@ -0,0 +1,471 @@ +//------------------------------------------------------------ +// Open Data Services header file: srv.h +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// Avoid double inclusion +#ifndef _ODS_SRV_H_ +#define _ODS_SRV_H_ + +#ifndef COMPILE_FOR_CLR +#include "windows.h" +#endif + +// ODS uses pack(4) on all CPU types +#pragma pack(4) + +#ifdef __cplusplus +extern "C" { +#endif + +// define model +#if !defined( FAR ) +#define FAR far +#endif + +//------------------------------------------------------------ +// Formats of data types +#if !defined(DBTYPEDEFS) // Do not conflict with DBLIB definitions +#if !defined(MAXNUMERICLEN) // Do not conflict with ODBC definitions + +#define DBTYPEDEFS + +typedef unsigned char DBBOOL; +typedef unsigned char DBBYTE; +typedef unsigned char DBTINYINT; +typedef short DBSMALLINT; +typedef unsigned short DBUSMALLINT; +typedef long DBINT; +typedef char DBCHAR; +typedef unsigned char DBBINARY; +typedef unsigned char DBBIT; +typedef double DBFLT8; + +typedef struct srv_datetime +{ // Format for SRVDATETIME + long dtdays; // number of days since 1/1/1900 + unsigned long dttime; // number 300th second since mid +} DBDATETIME; + +typedef struct srv_money +{ // Format for SRVMONEY + long mnyhigh; + unsigned long mnylow; +} DBMONEY; + +typedef float DBFLT4; +typedef long DBMONEY4; + +typedef struct dbdatetime4 +{ // Format for SRVDATETIM4 + unsigned short numdays; // number of days since 1/1/1900 + unsigned short nummins; // number of minutes sicne midnight +} DBDATETIM4; + +#define MAXNUMERICLEN 16 +typedef struct dbnumeric +{ // Format for SRVNUMERIC,SRVNUMERICN,SRVDECIMAL,SRVDECIMALN + BYTE precision; + BYTE scale; + BYTE sign; + BYTE val[MAXNUMERICLEN]; +} DBNUMERIC; +typedef DBNUMERIC DBDECIMAL; + +#endif // #if !defined(MAXNUMERICLEN) +#endif // #if !defined( DBTYPEDEFS ) + +//------------------------------------------------------------ +// Constants used by APIs + +// Type Tokens +#define SRV_TDS_NULL (BYTE) 0x1f +#define SRV_TDS_TEXT (BYTE) 0x23 +#define SRV_TDS_GUID (BYTE) 0x24 +#define SRV_TDS_VARBINARY (BYTE) 0x25 +#define SRV_TDS_INTN (BYTE) 0x26 +#define SRV_TDS_VARCHAR (BYTE) 0x27 +#define SRV_TDS_BINARY (BYTE) 0x2d +#define SRV_TDS_IMAGE (BYTE) 0x22 +#define SRV_TDS_CHAR (BYTE) 0x2f +#define SRV_TDS_INT1 (BYTE) 0x30 +#define SRV_TDS_BIT (BYTE) 0x32 +#define SRV_TDS_INT2 (BYTE) 0x34 +#define SRV_TDS_DECIMAL (BYTE) 0x37 +#define SRV_TDS_INT4 (BYTE) 0x38 +#define SRV_TDS_DATETIM4 (BYTE) 0x3a +#define SRV_TDS_FLT4 (BYTE) 0x3b +#define SRV_TDS_MONEY (BYTE) 0x3c +#define SRV_TDS_DATETIME (BYTE) 0x3d +#define SRV_TDS_FLT8 (BYTE) 0x3e +#define SRV_TDS_NUMERIC (BYTE) 0x3f +#define SRV_TDS_SSVARIANT (BYTE) 0x62 +#define SRV_TDS_NTEXT (BYTE) 0x63 +#define SRV_TDS_BITN (BYTE) 0x68 +#define SRV_TDS_DECIMALN (BYTE) 0x6a +#define SRV_TDS_NUMERICN (BYTE) 0x6c +#define SRV_TDS_FLTN (BYTE) 0x6d +#define SRV_TDS_MONEYN (BYTE) 0x6e +#define SRV_TDS_DATETIMN (BYTE) 0x6f +#define SRV_TDS_MONEY4 (BYTE) 0x7a +#define SRV_TDS_INT8 (BYTE) 0x7f +#define SRV_TDS_BIGVARBINARY (BYTE) 0xA5 +#define SRV_TDS_BIGVARCHAR (BYTE) 0xA7 +#define SRV_TDS_BIGBINARY (BYTE) 0xAD +#define SRV_TDS_BIGCHAR (BYTE) 0xAF +#define SRV_TDS_NVARCHAR (BYTE) 0xe7 +#define SRV_TDS_NCHAR (BYTE) 0xef + +// Datatypes +// Also: values of symbol parameter to srv_symbol when type = SRV_DATATYPE +#define SRVNULL SRV_TDS_NULL +#define SRVTEXT SRV_TDS_TEXT +#define SRVGUID SRV_TDS_GUID +#define SRVVARBINARY SRV_TDS_VARBINARY +#define SRVINTN SRV_TDS_INTN +#define SRVVARCHAR SRV_TDS_VARCHAR +#define SRVBINARY SRV_TDS_BINARY +#define SRVIMAGE SRV_TDS_IMAGE +#define SRVCHAR SRV_TDS_CHAR +#define SRVINT1 SRV_TDS_INT1 +#define SRVBIT SRV_TDS_BIT +#define SRVINT2 SRV_TDS_INT2 +#define SRVDECIMAL SRV_TDS_DECIMAL +#define SRVINT4 SRV_TDS_INT4 +#define SRVDATETIM4 SRV_TDS_DATETIM4 +#define SRVFLT4 SRV_TDS_FLT4 +#define SRVMONEY SRV_TDS_MONEY +#define SRVDATETIME SRV_TDS_DATETIME +#define SRVFLT8 SRV_TDS_FLT8 +#define SRVNUMERIC SRV_TDS_NUMERIC +#define SRVSSVARIANT SRV_TDS_SSVARIANT +#define SRVNTEXT SRV_TDS_NTEXT +#define SRVBITN SRV_TDS_BITN +#define SRVDECIMALN SRV_TDS_DECIMALN +#define SRVNUMERICN SRV_TDS_NUMERICN +#define SRVFLTN SRV_TDS_FLTN +#define SRVMONEYN SRV_TDS_MONEYN +#define SRVDATETIMN SRV_TDS_DATETIMN +#define SRVMONEY4 SRV_TDS_MONEY4 +#define SRVINT8 SRV_TDS_INT8 +#define SRVBIGVARBINARY SRV_TDS_BIGVARBINARY +#define SRVBIGVARCHAR SRV_TDS_BIGVARCHAR +#define SRVBIGBINARY SRV_TDS_BIGBINARY +#define SRVBIGCHAR SRV_TDS_BIGCHAR +#define SRVNVARCHAR SRV_TDS_NVARCHAR +#define SRVNCHAR SRV_TDS_NCHAR + +// values for srv_symbol type parameter +#define SRV_ERROR 0 +#define SRV_DONE 1 +#define SRV_DATATYPE 2 +#define SRV_EVENT 4 + +// values for srv_symbol symbol parameter, when type = SRV_ERROR +#define SRV_ENO_OS_ERR 0 +#define SRV_INFO 1 +#define SRV_FATAL_PROCESS 10 +#define SRV_FATAL_SERVER 19 + +// Types of server events +// Also: values for srv_symbol symbol parameter, when type = SRV_EVENT +#define SRV_CONTINUE 0 +#define SRV_LANGUAGE 1 +#define SRV_CONNECT 2 +#define SRV_RPC 3 +#define SRV_RESTART 4 +#define SRV_DISCONNECT 5 +#define SRV_ATTENTION 6 +#define SRV_SLEEP 7 +#define SRV_START 8 +#define SRV_STOP 9 +#define SRV_EXIT 10 +#define SRV_CANCEL 11 +#define SRV_SETUP 12 +#define SRV_CLOSE 13 +#define SRV_PRACK 14 +#define SRV_PRERROR 15 +#define SRV_ATTENTION_ACK 16 +#define SRV_CONNECT_V7 16 // TDS type for TDS 7 clients. Overloaded with SRV_ATTENTION_ACK +#define SRV_SKIP 17 +#define SRV_TRANSMGR 18 +#define SRV_PRELOGIN 19 +#define SRV_OLEDB 20 +#define SRV_INTERNAL_HANDLER 99 +#define SRV_PROGRAMMER_DEFINED 100 + +// values for srv_sfield field parameter +#define SRV_SERVERNAME 0 +#define SRV_VERSION 6 + +// Length to indicate string is null terminated +#define SRV_NULLTERM -1 + +// values of msgtype parameter to srv_sendmsg +#define SRV_MSG_INFO 1 +#define SRV_MSG_ERROR 2 + +// values of status parameter to srv_senddone +// Also: values for symbol parameters to srv_symbol when type = SRV_DONE +#define SRV_DONE_FINAL (USHORT) 0x0000 +#define SRV_DONE_MORE (USHORT) 0x0001 +#define SRV_DONE_ERROR (USHORT) 0x0002 +#define SRV_DONE_COUNT (USHORT) 0x0010 +#define SRV_DONE_RPC_IN_BATCH (USHORT) 0x0080 + +// return values of srv_paramstatus +#define SRV_PARAMRETURN 0x0001 +#define SRV_PARAMDEFAULT 0x0002 +#define SRV_PARAMSORTORDER 0x0004 + +// return values of srv_rpcoptions +#define SRV_RECOMPILE 0x0001 +#define SRV_NOMETADATA 0x0002 + +// values of field parameter to srv_pfield +//#define SRV_LANGUAGE 1 already defined above +//#define SRV_EVENT 4 already defined above +#define SRV_SPID 10 +#define SRV_NETSPID 11 +#define SRV_TYPE 12 +#define SRV_STATUS 13 +#define SRV_RMTSERVER 14 +#define SRV_HOST 15 +#define SRV_USER 16 +#define SRV_PWD 17 +#define SRV_CPID 18 +#define SRV_APPLNAME 19 +#define SRV_TDS 20 +#define SRV_CLIB 21 +#define SRV_LIBVERS 22 +#define SRV_ROWSENT 23 +#define SRV_BCPFLAG 24 +#define SRV_NATLANG 25 +#define SRV_PIPEHANDLE 26 +#define SRV_NETWORK_MODULE 27 +#define SRV_NETWORK_VERSION 28 +#define SRV_NETWORK_CONNECTION 29 +#define SRV_LSECURE 30 +#define SRV_SAXP 31 +#define SRV_UNICODE_USER 33 +#define SRV_UNICODE_PWD 35 +#define SRV_SPROC_CODEPAGE 36 +#define SRV_MSGLCID 37 +#define SRV_INSTANCENAME 38 +#define SRV_HASHPWD 39 +#define SRV_UNICODE_CURRENTLOGIN 40 + +// return value of SRV_TDSVERSION macro +#define SRV_TDS_NONE 0 +#define SRV_TDS_2_0 1 +#define SRV_TDS_3_4 2 +#define SRV_TDS_4_2 3 +#define SRV_TDS_6_0 4 +#define SRV_TDS_7_0 5 + +// Return values from APIs +typedef int SRVRETCODE; // SUCCEED or FAIL +#ifndef ODBCVER +typedef int RETCODE; +#endif + +#if !defined( SUCCEED ) +#define SUCCEED 1 // Successful return value +#endif + +#if !defined( FAIL ) +#define FAIL 0 // Unsuccessful return value +#endif + +#define SRV_DUPLICATE_HANDLER 2 // additional return value for srv_pre/post_handle + +#ifndef COMPILE_FOR_CLR +//------------------------------------------------ +//PreDeclare structures +// +struct srv_server; +typedef struct srv_server SRV_SERVER; + +struct srv_config; +typedef struct srv_config SRV_CONFIG; + +struct CXPData; +typedef struct CXPData SRV_PROC; + + +//------------------------------------------------ +//------------------------------------------------ +// ODS MACROs & APIs + +// Describing and sending a result set +int __cdecl srv_describe(SRV_PROC*,int,char*,int,long int,long int,long int,long int,void*); +int __cdecl srv_setutype(SRV_PROC* srvproc,int column,long int usertype); +int __cdecl srv_setcoldata(SRV_PROC* srvproc,int column,void* data); +int __cdecl srv_setcollen( SRV_PROC* srvproc,int column,int len); +int __cdecl srv_sendrow(SRV_PROC* srvproc ); +int __cdecl srv_senddone(SRV_PROC* srvproc,USHORT status,USHORT curcmd,long int count); + +// Dealing with Extended Procedure parameters +int __cdecl srv_rpcparams(SRV_PROC*); +int __cdecl srv_paraminfo(SRV_PROC*,int,BYTE*,ULONG*,ULONG*,BYTE*,BOOL*); +int __cdecl srv_paramsetoutput(SRV_PROC*,int,BYTE*,ULONG,BOOL); + +void* __cdecl srv_paramdata(SRV_PROC*,int); +int __cdecl srv_paramlen(SRV_PROC*,int); +int __cdecl srv_parammaxlen(SRV_PROC*,int); +int __cdecl srv_paramtype(SRV_PROC*,int); +int __cdecl srv_paramset(SRV_PROC*,int,void*,int); + +char* __cdecl srv_paramname(SRV_PROC*,int,int*); +int __cdecl srv_paramnumber(SRV_PROC*,char*,int); + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// The rest of these APIs are still supported, in SQL Server 7.0, +// but may not be supported after SQL Server 7.0 + +// MACROs +#define SRV_GETSERVER(a) srv_getserver ( a ) +#define SRV_GOT_ATTENTION(a) srv_got_attention ( a ) +#define SRV_TDSVERSION(a) srv_tdsversion ( a ) + +SRV_SERVER* __cdecl srv_getserver( SRV_PROC * srvproc ); +BOOL __cdecl srv_got_attention( SRV_PROC * srvproc ); + +// Memory +void* __cdecl srv_alloc(long int ulSize); +int __cdecl srv_bmove(void* from,void* to,long int count); +int __cdecl srv_bzero( void * location,long int count); +int __cdecl srv_free( void * ptr ); + + +int __cdecl srv_convert(SRV_PROC*,int,void*,long int,int,void*,long int); + +void* __cdecl srv_getuserdata( SRV_PROC * srvproc ); + +int __cdecl srv_getbindtoken(SRV_PROC * srvproc,char* token_buf); + +int __cdecl srv_getdtcxact(SRV_PROC * srvproc,void ** ppv); + +typedef int (* EventHandler)(void *); + +int __cdecl srv_impersonate_client( SRV_PROC * srvproc ); + +long __cdecl srv_langcpy(SRV_PROC * srvproc,long start,long nbytes,char* buffer); +long __cdecl srv_langlen( SRV_PROC * srvproc ); +void* __cdecl srv_langptr( SRV_PROC *srvproc ); + +int + __cdecl srv_log( SRV_SERVER * server, + BOOL datestamp, + char * msg, + int msglen ); + +int __cdecl srv_paramstatus(SRV_PROC*,int); + +void* __cdecl srv_pfieldex( SRV_PROC * srvproc,int field,int * len ); + +char* __cdecl srv_pfield( SRV_PROC * srvproc,int field,int * len ); + +int __cdecl srv_returnval( SRV_PROC * srvproc, + char * valuename, + int len, + BYTE status, + long int type, + long int maxlen, + long int datalen, + void * value ); + +int __cdecl srv_revert_to_self( SRV_PROC * srvproc ); + +char* __cdecl srv_rpcdb(SRV_PROC* srvproc,int* len ); +char* __cdecl srv_rpcname(SRV_PROC * srvproc,int* len ); +int __cdecl srv_rpcnumber( SRV_PROC * srvproc ); +USHORT __cdecl srv_rpcoptions( SRV_PROC * srvproc ); +char* __cdecl srv_rpcowner( SRV_PROC * srvproc,int * len ); + +int __cdecl srv_wsendmsg( SRV_PROC * srvproc, + long int msgnum, + BYTE msgclass, + WCHAR * message, // message text + int msglen); // length of message text + +int + __cdecl srv_sendmsg( SRV_PROC * srvproc, + int msgtype, + long int msgnum, + BYTE msgclass, + BYTE state, + char * rpcname, + int rpcnamelen, + USHORT linenum, + char * message, + int msglen ); + +int __cdecl srv_sendstatus( SRV_PROC * srvproc,long int status ); + + +int __cdecl srv_setuserdata(SRV_PROC* srvproc,void* ptr); + + +char* __cdecl srv_sfield( SRV_SERVER * server,int field,int* len); + +char* __cdecl srv_symbol(int type,int symbol,int* len); + +int __cdecl srv_tdsversion(SRV_PROC* srvproc); + +BOOL __cdecl srv_willconvert(int srctype,int desttype ); + +int __cdecl srv_terminatethread( SRV_PROC * srvproc ); + +int __cdecl srv_sendstatistics( SRV_PROC * srvproc ); + +int __cdecl srv_clearstatistics( SRV_PROC * srvproc ); + +int __cdecl srv_message_handler( SRV_PROC * srvproc, + int errornum, + BYTE severity, + BYTE state, + int oserrnum, + char * errtext, + int errtextlen, + char * oserrtext, + int oserrtextlen ); + +int __cdecl srv_pre_handle( SRV_SERVER * server, + SRV_PROC * srvproc, + long int event, + EventHandler handler, + BOOL remove ); +int __cdecl srv_post_handle( SRV_SERVER * server, + SRV_PROC * srvproc, + long int event, + EventHandler handler, + BOOL remove ); + +int __cdecl srv_IgnoreAnsiToOem( SRV_PROC * srvproc,BOOL bTF); + +#endif // #ifndef COMPILE_FOR_CLR + +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#define SS_MAJOR_VERSION 7 +#define SS_MINOR_VERSION 00 +#define SS_LEVEL_VERSION 0000 +#define SS_MINIMUM_VERSION "7.00.00.0000" +#define ODS_VERSION ((SS_MAJOR_VERSION << 24) | (SS_MINOR_VERSION << 16)) + +#endif //_ODS_SRV_H_ + +////////////////////////////////////////////////////////////////// +// Suggested implementation of __GetXpVersion +// +//__declspec(dllexport) ULONG __GetXpVersion() +// { +// return ODS_VERSION; +// } +////////////////////////////////////////////////////////////////// diff --git a/test_data/lots_of_files/ssol.c b/test_data/lots_of_files/ssol.c new file mode 100644 index 0000000..fa85931 --- /dev/null +++ b/test_data/lots_of_files/ssol.c @@ -0,0 +1,103 @@ +/* + * Instruction-level simulator for the LC + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define NUMMEMORY 65536 /* maximum number of words in memory */ +#define NUMREGS 8 /* number of machine registers */ +#define MAXLINELENGTH 1000 + +#define ADD 0 +#define NAND 1 +#define LW 2 +#define SW 3 +#define BEQ 4 +#define JALR 5 +#define HALT 6 +#define NOOP 7 + +typedef struct stateStruct { + int pc; + int mem[NUMMEMORY]; + int reg[NUMREGS]; + int numMemory; +} stateType; + +void printState(stateType *); +void run(stateType); +int convertNum(int); + +int +main(int argc, char *argv[]) +{ + int i; + char line[MAXLINELENGTH]; + stateType state; + FILE *filePtr; + + if (argc != 2) { + printf("error: usage: %s <machine-code file>\n", argv[0]); + exit(1); + } + + /* initialize memories and registers */ + for (i=0; i<NUMMEMORY; i++) { + state.mem[i] = 0; + } + for (i=0; i<NUMREGS; i++) { + state.reg[i] = 0; + } + + state.pc=0; + + /* read machine-code file into instruction/data memory (starting at + address 0) */ + + filePtr = fopen(argv[1], "r"); + if (filePtr == NULL) { + printf("error: can't open file %s\n", argv[1]); + perror("fopen"); + exit(1); + } + + for (state.numMemory=0; fgets(line, MAXLINELENGTH, filePtr) != NULL; + state.numMemory++) { + if (state.numMemory >= NUMMEMORY) { + printf("exceeded memory size\n"); + exit(1); + } + if (sscanf(line, "%d", state.mem+state.numMemory) != 1) { + printf("error in reading address %d\n", state.numMemory); + exit(1); + } + printf("memory[%d]=%d\n", state.numMemory, state.mem[state.numMemory]); + } + + printf("\n"); + + /* run never returns */ + run(state); + + return(0); +} + +void +run(stateType state) +{ + int arg0, arg1, arg2, addressField; + int instructions=0; + int opcode; + int maxMem=-1; /* highest memory address touched during run */ + + for (; 1; instructions++) { /* infinite loop, exits when it executes halt */ + printState(&state); + + if (state.pc < 0 || state.pc >= NUMMEMORY) { + printf("pc went out of the memory range\n"); + exit(1); + } + + maxMem = (state.pc > maxMem)?state.pc:maxMem; diff --git a/test_data/lots_of_files/stb.h b/test_data/lots_of_files/stb.h new file mode 100644 index 0000000..d8aaf37 --- /dev/null +++ b/test_data/lots_of_files/stb.h @@ -0,0 +1,14136 @@ +/* stb.h - v2.25 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h + no warranty is offered or implied; use this code at your own risk + + This is a single header file with a bunch of useful utilities + for getting stuff done in C/C++. + + Documentation: http://nothings.org/stb/stb_h.html + Unit tests: http://nothings.org/stb/stb.c + + + ============================================================================ + You MUST + + #define STB_DEFINE + + in EXACTLY _one_ C or C++ file that includes this header, BEFORE the + include, like this: + + #define STB_DEFINE + #include "stb.h" + + All other files should just #include "stb.h" without the #define. + ============================================================================ + + +Version History + + 2.25 various warning & bugfixes + 2.24 various warning & bugfixes + 2.23 fix 2.22 + 2.22 64-bit fixes from '!='; fix stb_sdict_copy() to have preferred name + 2.21 utf-8 decoder rejects "overlong" encodings; attempted 64-bit improvements + 2.20 fix to hash "copy" function--reported by someone with handle "!=" + 2.19 ??? + 2.18 stb_readdir_subdirs_mask + 2.17 stb_cfg_dir + 2.16 fix stb_bgio_, add stb_bgio_stat(); begin a streaming wrapper + 2.15 upgraded hash table template to allow: + - aggregate keys (explicit comparison func for EMPTY and DEL keys) + - "static" implementations (so they can be culled if unused) + 2.14 stb_mprintf + 2.13 reduce identifiable strings in STB_NO_STB_STRINGS + 2.12 fix STB_ONLY -- lots of uint32s, TRUE/FALSE things had crept in + 2.11 fix bug in stb_dirtree_get() which caused "c://path" sorts of stuff + 2.10 STB_F(), STB_I() inline constants (also KI,KU,KF,KD) + 2.09 stb_box_face_vertex_axis_side + 2.08 bugfix stb_trimwhite() + 2.07 colored printing in windows (why are we in 1985?) + 2.06 comparison functions are now functions-that-return-functions and + accept a struct-offset as a parameter (not thread-safe) + 2.05 compile and pass tests under Linux (but no threads); thread cleanup + 2.04 stb_cubic_bezier_1d, smoothstep, avoid dependency on registry + 2.03 ? + 2.02 remove integrated documentation + 2.01 integrate various fixes; stb_force_uniprocessor + 2.00 revised stb_dupe to use multiple hashes + 1.99 stb_charcmp + 1.98 stb_arr_deleten, stb_arr_insertn + 1.97 fix stb_newell_normal() + 1.96 stb_hash_number() + 1.95 hack stb__rec_max; clean up recursion code to use new functions + 1.94 stb_dirtree; rename stb_extra to stb_ptrmap + 1.93 stb_sem_new() API cleanup (no blockflag-starts blocked; use 'extra') + 1.92 stb_threadqueue--multi reader/writer queue, fixed size or resizeable + 1.91 stb_bgio_* for reading disk asynchronously + 1.90 stb_mutex uses CRITICAL_REGION; new stb_sync primitive for thread + joining; workqueue supports stb_sync instead of stb_semaphore + 1.89 support ';' in constant-string wildcards; stb_mutex wrapper (can + implement with EnterCriticalRegion eventually) + 1.88 portable threading API (only for win32 so far); worker thread queue + 1.87 fix wildcard handling in stb_readdir_recursive + 1.86 support ';' in wildcards + 1.85 make stb_regex work with non-constant strings; + beginnings of stb_introspect() + 1.84 (forgot to make notes) + 1.83 whoops, stb_keep_if_different wasn't deleting the temp file + 1.82 bring back stb_compress from stb_file.h for cmirror + 1.81 various bugfixes, STB_FASTMALLOC_INIT inits FASTMALLOC in release + 1.80 stb_readdir returns utf8; write own utf8-utf16 because lib was wrong + 1.79 stb_write + 1.78 calloc() support for malloc wrapper, STB_FASTMALLOC + 1.77 STB_FASTMALLOC + 1.76 STB_STUA - Lua-like language; (stb_image, stb_csample, stb_bilinear) + 1.75 alloc/free array of blocks; stb_hheap bug; a few stb_ps_ funcs; + hash*getkey, hash*copy; stb_bitset; stb_strnicmp; bugfix stb_bst + 1.74 stb_replaceinplace; use stdlib C function to convert utf8 to UTF-16 + 1.73 fix performance bug & leak in stb_ischar (C++ port lost a 'static') + 1.72 remove stb_block, stb_block_manager, stb_decompress (to stb_file.h) + 1.71 stb_trimwhite, stb_tokens_nested, etc. + 1.70 back out 1.69 because it might problemize mixed builds; stb_filec() + 1.69 (stb_file returns 'char *' in C++) + 1.68 add a special 'tree root' data type for stb_bst; stb_arr_end + 1.67 full C++ port. (stb_block_manager) + 1.66 stb_newell_normal + 1.65 stb_lex_item_wild -- allow wildcard items which MUST match entirely + 1.64 stb_data + 1.63 stb_log_name + 1.62 stb_define_sort; C++ cleanup + 1.61 stb_hash_fast -- Paul Hsieh's hash function (beats Bob Jenkins'?) + 1.60 stb_delete_directory_recursive + 1.59 stb_readdir_recursive + 1.58 stb_bst variant with parent pointer for O(1) iteration, not O(log N) + 1.57 replace LCG random with Mersenne Twister (found a public domain one) + 1.56 stb_perfect_hash, stb_ischar, stb_regex + 1.55 new stb_bst API allows multiple BSTs per node (e.g. secondary keys) + 1.54 bugfix: stb_define_hash, stb_wildmatch, regexp + 1.53 stb_define_hash; recoded stb_extra, stb_sdict use it + 1.52 stb_rand_define, stb_bst, stb_reverse + 1.51 fix 'stb_arr_setlen(NULL, 0)' + 1.50 stb_wordwrap + 1.49 minor improvements to enable the scripting language + 1.48 better approach for stb_arr using stb_malloc; more invasive, clearer + 1.47 stb_lex (lexes stb.h at 1.5ML/s on 3Ghz P4; 60/70% of optimal/flex) + 1.46 stb_wrapper_*, STB_MALLOC_WRAPPER + 1.45 lightly tested DFA acceleration of regexp searching + 1.44 wildcard matching & searching; regexp matching & searching + 1.43 stb_temp + 1.42 allow stb_arr to use stb_malloc/realloc; note this is global + 1.41 make it compile in C++; (disable stb_arr in C++) + 1.40 stb_dupe tweak; stb_swap; stb_substr + 1.39 stb_dupe; improve stb_file_max to be less stupid + 1.38 stb_sha1_file: generate sha1 for file, even > 4GB + 1.37 stb_file_max; partial support for utf8 filenames in Windows + 1.36 remove STB__NO_PREFIX - poor interaction with IDE, not worth it + streamline stb_arr to make it separately publishable + 1.35 bugfixes for stb_sdict, stb_malloc(0), stristr + 1.34 (streaming interfaces for stb_compress) + 1.33 stb_alloc; bug in stb_getopt; remove stb_overflow + 1.32 (stb_compress returns, smaller&faster; encode window & 64-bit len) + 1.31 stb_prefix_count + 1.30 (STB__NO_PREFIX - remove stb_ prefixes for personal projects) + 1.29 stb_fput_varlen64, etc. + 1.28 stb_sha1 + 1.27 ? + 1.26 stb_extra + 1.25 ? + 1.24 stb_copyfile + 1.23 stb_readdir + 1.22 ? + 1.21 ? + 1.20 ? + 1.19 ? + 1.18 ? + 1.17 ? + 1.16 ? + 1.15 stb_fixpath, stb_splitpath, stb_strchr2 + 1.14 stb_arr + 1.13 ?stb, stb_log, stb_fatal + 1.12 ?stb_hash2 + 1.11 miniML + 1.10 stb_crc32, stb_adler32 + 1.09 stb_sdict + 1.08 stb_bitreverse, stb_ispow2, stb_big32 + stb_fopen, stb_fput_varlen, stb_fput_ranged + stb_fcmp, stb_feq + 1.07 (stb_encompress) + 1.06 stb_compress + 1.05 stb_tokens, (stb_hheap) + 1.04 stb_rand + 1.03 ?(s-strings) + 1.02 ?stb_filelen, stb_tokens + 1.01 stb_tolower + 1.00 stb_hash, stb_intcmp + stb_file, stb_stringfile, stb_fgets + stb_prefix, stb_strlower, stb_strtok + stb_image + (stb_array), (stb_arena) + +Parenthesized items have since been removed. + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +CREDITS + + Written by Sean Barrett. + + Fixes: + Philipp Wiesemann + Robert Nix + r-lyeh + blackpawn + Mojofreem@github + Ryan Whitworth + Vincent Isambart +*/ + +#ifndef STB__INCLUDE_STB_H +#define STB__INCLUDE_STB_H + +#define STB_VERSION 1 + +#ifdef STB_INTROSPECT + #define STB_DEFINE +#endif + +#ifdef STB_DEFINE_THREADS + #ifndef STB_DEFINE + #define STB_DEFINE + #endif + #ifndef STB_THREADS + #define STB_THREADS + #endif +#endif + +#ifdef _WIN32 + #define _CRT_SECURE_NO_WARNINGS + #define _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NON_CONFORMING_SWPRINTFS + #if !defined(_MSC_VER) || _MSC_VER > 1700 + #include <intrin.h> // _BitScanReverse + #endif +#endif + +#include <stdlib.h> // stdlib could have min/max +#include <stdio.h> // need FILE +#include <string.h> // stb_define_hash needs memcpy/memset +#include <time.h> // stb_dirtree +#ifdef __MINGW32__ + #include <fcntl.h> // O_RDWR +#endif + +#ifdef STB_PERSONAL + typedef int Bool; + #define False 0 + #define True 1 +#endif + +#ifdef STB_MALLOC_WRAPPER_PAGED + #define STB_MALLOC_WRAPPER_DEBUG +#endif +#ifdef STB_MALLOC_WRAPPER_DEBUG + #define STB_MALLOC_WRAPPER +#endif +#ifdef STB_MALLOC_WRAPPER_FASTMALLOC + #define STB_FASTMALLOC + #define STB_MALLOC_WRAPPER +#endif + +#ifdef STB_FASTMALLOC + #ifndef _WIN32 + #undef STB_FASTMALLOC + #endif +#endif + +#ifdef STB_DEFINE + #include <assert.h> + #include <stdarg.h> + #include <stddef.h> + #include <ctype.h> + #include <math.h> + #ifndef _WIN32 + #include <unistd.h> + #else + #include <io.h> // _mktemp + #include <direct.h> // _rmdir + #endif + #include <sys/types.h> // stat()/_stat() + #include <sys/stat.h> // stat()/_stat() +#endif + +#define stb_min(a,b) ((a) < (b) ? (a) : (b)) +#define stb_max(a,b) ((a) > (b) ? (a) : (b)) + +#ifndef STB_ONLY + #if !defined(__cplusplus) && !defined(min) && !defined(max) + #define min(x,y) stb_min(x,y) + #define max(x,y) stb_max(x,y) + #endif + + #ifndef M_PI + #define M_PI 3.14159265358979323846f + #endif + + #ifndef TRUE + #define TRUE 1 + #define FALSE 0 + #endif + + #ifndef deg2rad + #define deg2rad(a) ((a)*(M_PI/180)) + #endif + #ifndef rad2deg + #define rad2deg(a) ((a)*(180/M_PI)) + #endif + + #ifndef swap + #ifndef __cplusplus + #define swap(TYPE,a,b) \ + do { TYPE stb__t; stb__t = (a); (a) = (b); (b) = stb__t; } while (0) + #endif + #endif + + typedef unsigned char uint8 ; + typedef signed char int8 ; + typedef unsigned short uint16; + typedef signed short int16; + #if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) + typedef unsigned long uint32; + typedef signed long int32; + #else + typedef unsigned int uint32; + typedef signed int int32; + #endif + + typedef unsigned char uchar ; + typedef unsigned short ushort; + typedef unsigned int uint ; + typedef unsigned long ulong ; + + // produce compile errors if the sizes aren't right + typedef char stb__testsize16[sizeof(int16)==2]; + typedef char stb__testsize32[sizeof(int32)==4]; +#endif + +#ifndef STB_TRUE + #define STB_TRUE 1 + #define STB_FALSE 0 +#endif + +// if we're STB_ONLY, can't rely on uint32 or even uint, so all the +// variables we'll use herein need typenames prefixed with 'stb': +typedef unsigned char stb_uchar; +typedef unsigned char stb_uint8; +typedef unsigned int stb_uint; +typedef unsigned short stb_uint16; +typedef short stb_int16; +typedef signed char stb_int8; +#if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) + typedef unsigned long stb_uint32; + typedef long stb_int32; +#else + typedef unsigned int stb_uint32; + typedef int stb_int32; +#endif +typedef char stb__testsize2_16[sizeof(stb_uint16)==2 ? 1 : -1]; +typedef char stb__testsize2_32[sizeof(stb_uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER + typedef unsigned __int64 stb_uint64; + typedef __int64 stb_int64; + #define STB_IMM_UINT64(literalui64) (literalui64##ui64) + #define STB_IMM_INT64(literali64) (literali64##i64) +#else + // ?? + typedef unsigned long long stb_uint64; + typedef long long stb_int64; + #define STB_IMM_UINT64(literalui64) (literalui64##ULL) + #define STB_IMM_INT64(literali64) (literali64##LL) +#endif +typedef char stb__testsize2_64[sizeof(stb_uint64)==8 ? 1 : -1]; + +// add platform-specific ways of checking for sizeof(char*) == 8, +// and make those define STB_PTR64 +#if defined(_WIN64) || defined(__x86_64__) || defined(__ia64__) || defined(__LP64__) + #define STB_PTR64 +#endif + +#ifdef STB_PTR64 +typedef char stb__testsize2_ptr[sizeof(char *) == 8]; +typedef stb_uint64 stb_uinta; +typedef stb_int64 stb_inta; +#else +typedef char stb__testsize2_ptr[sizeof(char *) == 4]; +typedef stb_uint32 stb_uinta; +typedef stb_int32 stb_inta; +#endif +typedef char stb__testsize2_uinta[sizeof(stb_uinta)==sizeof(char*) ? 1 : -1]; + +// if so, we should define an int type that is the pointer size. until then, +// we'll have to make do with this (which is not the same at all!) + +typedef union +{ + unsigned int i; + void * p; +} stb_uintptr; + + +#ifdef __cplusplus + #define STB_EXTERN extern "C" +#else + #define STB_EXTERN extern +#endif + +// check for well-known debug defines +#if defined(DEBUG) || defined(_DEBUG) || defined(DBG) + #ifndef NDEBUG + #define STB_DEBUG + #endif +#endif + +#ifdef STB_DEBUG + #include <assert.h> +#endif + + +STB_EXTERN void stb_wrapper_malloc(void *newp, int sz, char *file, int line); +STB_EXTERN void stb_wrapper_free(void *oldp, char *file, int line); +STB_EXTERN void stb_wrapper_realloc(void *oldp, void *newp, int sz, char *file, int line); +STB_EXTERN void stb_wrapper_calloc(size_t num, size_t sz, char *file, int line); +STB_EXTERN void stb_wrapper_listall(void (*func)(void *ptr, int sz, char *file, int line)); +STB_EXTERN void stb_wrapper_dump(char *filename); +STB_EXTERN int stb_wrapper_allocsize(void *oldp); +STB_EXTERN void stb_wrapper_check(void *oldp); + +#ifdef STB_DEFINE +// this is a special function used inside malloc wrapper +// to do allocations that aren't tracked (to avoid +// reentrancy). Of course if someone _else_ wraps realloc, +// this breaks, but if they're doing that AND the malloc +// wrapper they need to explicitly check for reentrancy. +// +// only define realloc_raw() and we do realloc(NULL,sz) +// for malloc() and realloc(p,0) for free(). +static void * stb__realloc_raw(void *p, int sz) +{ + if (p == NULL) return malloc(sz); + if (sz == 0) { free(p); return NULL; } + return realloc(p,sz); +} +#endif + +#ifdef _WIN32 +STB_EXTERN void * stb_smalloc(size_t sz); +STB_EXTERN void stb_sfree(void *p); +STB_EXTERN void * stb_srealloc(void *p, size_t sz); +STB_EXTERN void * stb_scalloc(size_t n, size_t sz); +STB_EXTERN char * stb_sstrdup(char *s); +#endif + +#ifdef STB_FASTMALLOC +#define malloc stb_smalloc +#define free stb_sfree +#define realloc stb_srealloc +#define strdup stb_sstrdup +#define calloc stb_scalloc +#endif + +#ifndef STB_MALLOC_ALLCHECK + #define stb__check(p) 1 +#else + #ifndef STB_MALLOC_WRAPPER + #error STB_MALLOC_ALLCHECK requires STB_MALLOC_WRAPPER + #else + #define stb__check(p) stb_mcheck(p) + #endif +#endif + +#ifdef STB_MALLOC_WRAPPER + STB_EXTERN void * stb__malloc(int, char *, int); + STB_EXTERN void * stb__realloc(void *, int, char *, int); + STB_EXTERN void * stb__calloc(size_t n, size_t s, char *, int); + STB_EXTERN void stb__free(void *, char *file, int); + STB_EXTERN char * stb__strdup(char *s, char *file, int); + STB_EXTERN void stb_malloc_checkall(void); + STB_EXTERN void stb_malloc_check_counter(int init_delay, int rep_delay); + #ifndef STB_MALLOC_WRAPPER_DEBUG + #define stb_mcheck(p) 1 + #else + STB_EXTERN int stb_mcheck(void *); + #endif + + + #ifdef STB_DEFINE + + #ifdef STB_MALLOC_WRAPPER_DEBUG + #define STB__PAD 32 + #define STB__BIAS 16 + #define STB__SIG 0x51b01234 + #define STB__FIXSIZE(sz) (((sz+3) & ~3) + STB__PAD) + #define STB__ptr(x,y) ((char *) (x) + (y)) + #else + #define STB__ptr(x,y) (x) + #define STB__FIXSIZE(sz) (sz) + #endif + + #ifdef STB_MALLOC_WRAPPER_DEBUG + int stb_mcheck(void *p) + { + unsigned int sz; + if (p == NULL) return 1; + p = ((char *) p) - STB__BIAS; + sz = * (unsigned int *) p; + assert(* (unsigned int *) STB__ptr(p,4) == STB__SIG); + assert(* (unsigned int *) STB__ptr(p,8) == STB__SIG); + assert(* (unsigned int *) STB__ptr(p,12) == STB__SIG); + assert(* (unsigned int *) STB__ptr(p,sz-4) == STB__SIG+1); + assert(* (unsigned int *) STB__ptr(p,sz-8) == STB__SIG+1); + assert(* (unsigned int *) STB__ptr(p,sz-12) == STB__SIG+1); + assert(* (unsigned int *) STB__ptr(p,sz-16) == STB__SIG+1); + stb_wrapper_check(STB__ptr(p, STB__BIAS)); + return 1; + } + + static void stb__check2(void *p, int sz, char *file, int line) + { + stb_mcheck(p); + } + + void stb_malloc_checkall(void) + { + stb_wrapper_listall(stb__check2); + } + #else + void stb_malloc_checkall(void) { } + #endif + + static int stb__malloc_wait=(1 << 30), stb__malloc_next_wait = (1 << 30), stb__malloc_iter; + void stb_malloc_check_counter(int init_delay, int rep_delay) + { + stb__malloc_wait = init_delay; + stb__malloc_next_wait = rep_delay; + } + + void stb_mcheck_all(void) + { + #ifdef STB_MALLOC_WRAPPER_DEBUG + ++stb__malloc_iter; + if (--stb__malloc_wait <= 0) { + stb_malloc_checkall(); + stb__malloc_wait = stb__malloc_next_wait; + } + #endif + } + + #ifdef STB_MALLOC_WRAPPER_PAGED + #define STB__WINDOWS_PAGE (1 << 12) + #ifndef _WINDOWS_ + STB_EXTERN __declspec(dllimport) void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect); + STB_EXTERN __declspec(dllimport) int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype); + #endif + #endif + + static void *stb__malloc_final(int sz) + { + #ifdef STB_MALLOC_WRAPPER_PAGED + int aligned = (sz + STB__WINDOWS_PAGE - 1) & ~(STB__WINDOWS_PAGE-1); + char *p = VirtualAlloc(NULL, aligned + STB__WINDOWS_PAGE, 0x2000, 0x04); // RESERVE, READWRITE + if (p == NULL) return p; + VirtualAlloc(p, aligned, 0x1000, 0x04); // COMMIT, READWRITE + return p; + #else + return malloc(sz); + #endif + } + + static void stb__free_final(void *p) + { + #ifdef STB_MALLOC_WRAPPER_PAGED + VirtualFree(p, 0, 0x8000); // RELEASE + #else + free(p); + #endif + } + + int stb__malloc_failure; + static void *stb__realloc_final(void *p, int sz, int old_sz) + { + #ifdef STB_MALLOC_WRAPPER_PAGED + void *q = stb__malloc_final(sz); + if (q == NULL) + return ++stb__malloc_failure, q; + // @TODO: deal with p being smaller! + memcpy(q, p, sz < old_sz ? sz : old_sz); + stb__free_final(p); + return q; + #else + return realloc(p,sz); + #endif + } + + void stb__free(void *p, char *file, int line) + { + stb_mcheck_all(); + if (!p) return; + #ifdef STB_MALLOC_WRAPPER_DEBUG + stb_mcheck(p); + #endif + stb_wrapper_free(p,file,line); + #ifdef STB_MALLOC_WRAPPER_DEBUG + p = STB__ptr(p,-STB__BIAS); + * (unsigned int *) STB__ptr(p,0) = 0xdeadbeef; + * (unsigned int *) STB__ptr(p,4) = 0xdeadbeef; + * (unsigned int *) STB__ptr(p,8) = 0xdeadbeef; + * (unsigned int *) STB__ptr(p,12) = 0xdeadbeef; + #endif + stb__free_final(p); + } + + void * stb__malloc(int sz, char *file, int line) + { + void *p; + stb_mcheck_all(); + if (sz == 0) return NULL; + p = stb__malloc_final(STB__FIXSIZE(sz)); + if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz)); + if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz)); + if (p == NULL) { + ++stb__malloc_failure; + #ifdef STB_MALLOC_WRAPPER_DEBUG + stb_malloc_checkall(); + #endif + return p; + } + #ifdef STB_MALLOC_WRAPPER_DEBUG + * (int *) STB__ptr(p,0) = STB__FIXSIZE(sz); + * (unsigned int *) STB__ptr(p,4) = STB__SIG; + * (unsigned int *) STB__ptr(p,8) = STB__SIG; + * (unsigned int *) STB__ptr(p,12) = STB__SIG; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-4) = STB__SIG+1; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-8) = STB__SIG+1; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-12) = STB__SIG+1; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-16) = STB__SIG+1; + p = STB__ptr(p, STB__BIAS); + #endif + stb_wrapper_malloc(p,sz,file,line); + return p; + } + + void * stb__realloc(void *p, int sz, char *file, int line) + { + void *q; + + stb_mcheck_all(); + if (p == NULL) return stb__malloc(sz,file,line); + if (sz == 0 ) { stb__free(p,file,line); return NULL; } + + #ifdef STB_MALLOC_WRAPPER_DEBUG + stb_mcheck(p); + p = STB__ptr(p,-STB__BIAS); + #endif + #ifdef STB_MALLOC_WRAPPER_PAGED + { + int n = stb_wrapper_allocsize(STB__ptr(p,STB__BIAS)); + if (!n) + stb_wrapper_check(STB__ptr(p,STB__BIAS)); + q = stb__realloc_final(p, STB__FIXSIZE(sz), STB__FIXSIZE(n)); + } + #else + q = realloc(p, STB__FIXSIZE(sz)); + #endif + if (q == NULL) + return ++stb__malloc_failure, q; + #ifdef STB_MALLOC_WRAPPER_DEBUG + * (int *) STB__ptr(q,0) = STB__FIXSIZE(sz); + * (unsigned int *) STB__ptr(q,4) = STB__SIG; + * (unsigned int *) STB__ptr(q,8) = STB__SIG; + * (unsigned int *) STB__ptr(q,12) = STB__SIG; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-4) = STB__SIG+1; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-8) = STB__SIG+1; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-12) = STB__SIG+1; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-16) = STB__SIG+1; + + q = STB__ptr(q, STB__BIAS); + p = STB__ptr(p, STB__BIAS); + #endif + stb_wrapper_realloc(p,q,sz,file,line); + return q; + } + + STB_EXTERN int stb_log2_ceil(unsigned int); + static void *stb__calloc(size_t n, size_t sz, char *file, int line) + { + void *q; + stb_mcheck_all(); + if (n == 0 || sz == 0) return NULL; + if (stb_log2_ceil(n) + stb_log2_ceil(sz) >= 32) return NULL; + q = stb__malloc(n*sz, file, line); + if (q) memset(q, 0, n*sz); + return q; + } + + char * stb__strdup(char *s, char *file, int line) + { + char *p; + stb_mcheck_all(); + p = stb__malloc(strlen(s)+1, file, line); + if (!p) return p; + strcpy(p, s); + return p; + } + #endif // STB_DEFINE + + #ifdef STB_FASTMALLOC + #undef malloc + #undef realloc + #undef free + #undef strdup + #undef calloc + #endif + + // include everything that might define these, BEFORE making macros + #include <stdlib.h> + #include <string.h> + #include <malloc.h> + + #define malloc(s) stb__malloc ( s, __FILE__, __LINE__) + #define realloc(p,s) stb__realloc(p,s, __FILE__, __LINE__) + #define calloc(n,s) stb__calloc (n,s, __FILE__, __LINE__) + #define free(p) stb__free (p, __FILE__, __LINE__) + #define strdup(p) stb__strdup (p, __FILE__, __LINE__) + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Windows pretty display +// + +STB_EXTERN void stbprint(const char *fmt, ...); +STB_EXTERN char *stb_sprintf(const char *fmt, ...); +STB_EXTERN char *stb_mprintf(const char *fmt, ...); + +#ifdef STB_DEFINE +char *stb_sprintf(const char *fmt, ...) +{ + static char buffer[1024]; + va_list v; + va_start(v,fmt); + #ifdef _WIN32 + _vsnprintf(buffer, 1024, fmt, v); + #else + vsnprintf(buffer, 1024, fmt, v); + #endif + va_end(v); + buffer[1023] = 0; + return buffer; +} + +char *stb_mprintf(const char *fmt, ...) +{ + static char buffer[1024]; + va_list v; + va_start(v,fmt); + #ifdef _WIN32 + _vsnprintf(buffer, 1024, fmt, v); + #else + vsnprintf(buffer, 1024, fmt, v); + #endif + va_end(v); + buffer[1023] = 0; + return strdup(buffer); +} + +#ifdef _WIN32 + +#ifndef _WINDOWS_ +STB_EXTERN __declspec(dllimport) int __stdcall WriteConsoleA(void *, const void *, unsigned int, unsigned int *, void *); +STB_EXTERN __declspec(dllimport) void * __stdcall GetStdHandle(unsigned int); +STB_EXTERN __declspec(dllimport) int __stdcall SetConsoleTextAttribute(void *, unsigned short); +#endif + +static void stb__print_one(void *handle, char *s, int len) +{ + if (len) + if (WriteConsoleA(handle, s, len, NULL,NULL)) + fwrite(s, 1, len, stdout); // if it fails, maybe redirected, so do normal +} + +static void stb__print(char *s) +{ + void *handle = GetStdHandle((unsigned int) -11); // STD_OUTPUT_HANDLE + int pad=0; // number of padding characters to add + + char *t = s; + while (*s) { + int lpad; + while (*s && *s != '{') { + if (pad) { + if (*s == '\r' || *s == '\n') + pad = 0; + else if (s[0] == ' ' && s[1] == ' ') { + stb__print_one(handle, t, s-t); + t = s; + while (pad) { + stb__print_one(handle, t, 1); + --pad; + } + } + } + ++s; + } + if (!*s) break; + stb__print_one(handle, t, s-t); + if (s[1] == '{') { + ++s; + continue; + } + + if (s[1] == '#') { + t = s+3; + if (isxdigit(s[2])) + if (isdigit(s[2])) + SetConsoleTextAttribute(handle, s[2] - '0'); + else + SetConsoleTextAttribute(handle, tolower(s[2]) - 'a' + 10); + else { + SetConsoleTextAttribute(handle, 0x0f); + t=s+2; + } + } else if (s[1] == '!') { + SetConsoleTextAttribute(handle, 0x0c); + t = s+2; + } else if (s[1] == '@') { + SetConsoleTextAttribute(handle, 0x09); + t = s+2; + } else if (s[1] == '$') { + SetConsoleTextAttribute(handle, 0x0a); + t = s+2; + } else { + SetConsoleTextAttribute(handle, 0x08); // 0,7,8,15 => shades of grey + t = s+1; + } + + lpad = (t-s); + s = t; + while (*s && *s != '}') ++s; + if (!*s) break; + stb__print_one(handle, t, s-t); + if (s[1] == '}') { + t = s+2; + } else { + pad += 1+lpad; + t = s+1; + } + s=t; + SetConsoleTextAttribute(handle, 0x07); + } + stb__print_one(handle, t, s-t); + SetConsoleTextAttribute(handle, 0x07); +} + +void stbprint(const char *fmt, ...) +{ + int res; + char buffer[1024]; + char *tbuf = buffer; + va_list v; + + va_start(v,fmt); + res = _vsnprintf(buffer, sizeof(buffer), fmt, v); + va_end(v); + buffer[sizeof(buffer)-1] = 0; + + if (res < 0) { + tbuf = (char *) malloc(16384); + va_start(v,fmt); + res = _vsnprintf(tbuf,16384, fmt, v); + va_end(v); + tbuf[16383] = 0; + } + + stb__print(tbuf); + + if (tbuf != buffer) + free(tbuf); +} + +#else // _WIN32 +void stbprint(const char *fmt, ...) +{ + va_list v; + va_start(v,fmt); + vprintf(fmt,v); + va_end(v); +} +#endif // _WIN32 +#endif // STB_DEFINE + + + +////////////////////////////////////////////////////////////////////////////// +// +// Windows UTF8 filename handling +// +// Windows stupidly treats 8-bit filenames as some dopey code page, +// rather than utf-8. If we want to use utf8 filenames, we have to +// convert them to WCHAR explicitly and call WCHAR versions of the +// file functions. So, ok, we do. + + +#ifdef _WIN32 + #define stb__fopen(x,y) _wfopen((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) + #define stb__windows(x,y) x +#else + #define stb__fopen(x,y) fopen(x,y) + #define stb__windows(x,y) y +#endif + + +typedef unsigned short stb__wchar; + +STB_EXTERN stb__wchar * stb_from_utf8(stb__wchar *buffer, char *str, int n); +STB_EXTERN char * stb_to_utf8 (char *buffer, stb__wchar *str, int n); + +STB_EXTERN stb__wchar *stb__from_utf8(char *str); +STB_EXTERN stb__wchar *stb__from_utf8_alt(char *str); +STB_EXTERN char *stb__to_utf8(stb__wchar *str); + + +#ifdef STB_DEFINE +stb__wchar * stb_from_utf8(stb__wchar *buffer, char *ostr, int n) +{ + unsigned char *str = (unsigned char *) ostr; + stb_uint32 c; + int i=0; + --n; + while (*str) { + if (i >= n) + return NULL; + if (!(*str & 0x80)) + buffer[i++] = *str++; + else if ((*str & 0xe0) == 0xc0) { + if (*str < 0xc2) return NULL; + c = (*str++ & 0x1f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf0) == 0xe0) { + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL; + if (*str == 0xed && str[1] > 0x9f) return NULL; // str[1] < 0x80 is checked below + c = (*str++ & 0x0f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf8) == 0xf0) { + if (*str > 0xf4) return NULL; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL; + if (*str == 0xf4 && str[1] > 0x8f) return NULL; // str[1] < 0x80 is checked below + c = (*str++ & 0x07) << 18; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f); + // utf-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xFFFFF800) == 0xD800) return NULL; + if (c >= 0x10000) { + c -= 0x10000; + if (i + 2 > n) return NULL; + buffer[i++] = 0xD800 | (0x3ff & (c >> 10)); + buffer[i++] = 0xDC00 | (0x3ff & (c )); + } + } else + return NULL; + } + buffer[i] = 0; + return buffer; +} + +char * stb_to_utf8(char *buffer, stb__wchar *str, int n) +{ + int i=0; + --n; + while (*str) { + if (*str < 0x80) { + if (i+1 > n) return NULL; + buffer[i++] = (char) *str++; + } else if (*str < 0x800) { + if (i+2 > n) return NULL; + buffer[i++] = 0xc0 + (*str >> 6); + buffer[i++] = 0x80 + (*str & 0x3f); + str += 1; + } else if (*str >= 0xd800 && *str < 0xdc00) { + stb_uint32 c; + if (i+4 > n) return NULL; + c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; + buffer[i++] = 0xf0 + (c >> 18); + buffer[i++] = 0x80 + ((c >> 12) & 0x3f); + buffer[i++] = 0x80 + ((c >> 6) & 0x3f); + buffer[i++] = 0x80 + ((c ) & 0x3f); + str += 2; + } else if (*str >= 0xdc00 && *str < 0xe000) { + return NULL; + } else { + if (i+3 > n) return NULL; + buffer[i++] = 0xe0 + (*str >> 12); + buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); + buffer[i++] = 0x80 + ((*str ) & 0x3f); + str += 1; + } + } + buffer[i] = 0; + return buffer; +} + +stb__wchar *stb__from_utf8(char *str) +{ + static stb__wchar buffer[4096]; + return stb_from_utf8(buffer, str, 4096); +} + +stb__wchar *stb__from_utf8_alt(char *str) +{ + static stb__wchar buffer[64]; + return stb_from_utf8(buffer, str, 64); +} + +char *stb__to_utf8(stb__wchar *str) +{ + static char buffer[4096]; + return stb_to_utf8(buffer, str, 4096); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Miscellany +// + +STB_EXTERN void stb_fatal(char *fmt, ...); +STB_EXTERN void stb_(char *fmt, ...); +STB_EXTERN void stb_append_to_file(char *file, char *fmt, ...); +STB_EXTERN void stb_log(int active); +STB_EXTERN void stb_log_fileline(int active); +STB_EXTERN void stb_log_name(char *filename); + +STB_EXTERN void stb_swap(void *p, void *q, size_t sz); +STB_EXTERN void *stb_copy(void *p, size_t sz); +STB_EXTERN void stb_pointer_array_free(void *p, int len); +STB_EXTERN void **stb_array_block_alloc(int count, int blocksize); + +#define stb_arrcount(x) (sizeof(x)/sizeof((x)[0])) + + +STB_EXTERN int stb__record_fileline(char *f, int n); + +#ifdef STB_DEFINE + +static char *stb__file; +static int stb__line; + +int stb__record_fileline(char *f, int n) +{ + stb__file = f; + stb__line = n; + return 0; +} + +void stb_fatal(char *s, ...) +{ + va_list a; + if (stb__file) + fprintf(stderr, "[%s:%d] ", stb__file, stb__line); + va_start(a,s); + fputs("Fatal error: ", stderr); + vfprintf(stderr, s, a); + va_end(a); + fputs("\n", stderr); + #ifdef STB_DEBUG + #ifdef _MSC_VER + #ifndef STB_PTR64 + __asm int 3; // trap to debugger! + #else + __debugbreak(); + #endif + #else + __builtin_trap(); + #endif + #endif + exit(1); +} + +static int stb__log_active=1, stb__log_fileline=1; + +void stb_log(int active) +{ + stb__log_active = active; +} + +void stb_log_fileline(int active) +{ + stb__log_fileline = active; +} + +#ifdef STB_NO_STB_STRINGS +char *stb__log_filename = "temp.log"; +#else +char *stb__log_filename = "stb.log"; +#endif + +void stb_log_name(char *s) +{ + stb__log_filename = s; +} + +void stb_(char *s, ...) +{ + if (stb__log_active) { + FILE *f = fopen(stb__log_filename, "a"); + if (f) { + va_list a; + if (stb__log_fileline && stb__file) + fprintf(f, "[%s:%4d] ", stb__file, stb__line); + va_start(a,s); + vfprintf(f, s, a); + va_end(a); + fputs("\n", f); + fclose(f); + } + } +} + +void stb_append_to_file(char *filename, char *s, ...) +{ + FILE *f = fopen(filename, "a"); + if (f) { + va_list a; + va_start(a,s); + vfprintf(f, s, a); + va_end(a); + fputs("\n", f); + fclose(f); + } +} + + +typedef struct { char d[4]; } stb__4; +typedef struct { char d[8]; } stb__8; + +// optimize the small cases, though you shouldn't be calling this for those! +void stb_swap(void *p, void *q, size_t sz) +{ + char buffer[256]; + if (p == q) return; + if (sz == 4) { + stb__4 temp = * ( stb__4 *) p; + * (stb__4 *) p = * ( stb__4 *) q; + * (stb__4 *) q = temp; + return; + } else if (sz == 8) { + stb__8 temp = * ( stb__8 *) p; + * (stb__8 *) p = * ( stb__8 *) q; + * (stb__8 *) q = temp; + return; + } + + while (sz > sizeof(buffer)) { + stb_swap(p, q, sizeof(buffer)); + p = (char *) p + sizeof(buffer); + q = (char *) q + sizeof(buffer); + sz -= sizeof(buffer); + } + + memcpy(buffer, p , sz); + memcpy(p , q , sz); + memcpy(q , buffer, sz); +} + +void *stb_copy(void *p, size_t sz) +{ + void *q = malloc(sz); + memcpy(q, p, sz); + return q; +} + +void stb_pointer_array_free(void *q, int len) +{ + void **p = (void **) q; + int i; + for (i=0; i < len; ++i) + free(p[i]); +} + +void **stb_array_block_alloc(int count, int blocksize) +{ + int i; + char *p = (char *) malloc(sizeof(void *) * count + count * blocksize); + void **q; + if (p == NULL) return NULL; + q = (void **) p; + p += sizeof(void *) * count; + for (i=0; i < count; ++i) + q[i] = p + i * blocksize; + return q; +} +#endif + +#ifdef STB_DEBUG + // tricky hack to allow recording FILE,LINE even in varargs functions + #define STB__RECORD_FILE(x) (stb__record_fileline(__FILE__, __LINE__),(x)) + #define stb_log STB__RECORD_FILE(stb_log) + #define stb_ STB__RECORD_FILE(stb_) + #ifndef STB_FATAL_CLEAN + #define stb_fatal STB__RECORD_FILE(stb_fatal) + #endif + #define STB__DEBUG(x) x +#else + #define STB__DEBUG(x) +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_temp +// + +#define stb_temp(block, sz) stb__temp(block, sizeof(block), (sz)) + +STB_EXTERN void * stb__temp(void *b, int b_sz, int want_sz); +STB_EXTERN void stb_tempfree(void *block, void *ptr); + +#ifdef STB_DEFINE + +void * stb__temp(void *b, int b_sz, int want_sz) +{ + if (b_sz >= want_sz) + return b; + else + return malloc(want_sz); +} + +void stb_tempfree(void *b, void *p) +{ + if (p != b) + free(p); +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// math/sampling operations +// + + +#define stb_lerp(t,a,b) ( (a) + (t) * (float) ((b)-(a)) ) +#define stb_unlerp(t,a,b) ( ((t) - (a)) / (float) ((b) - (a)) ) + +#define stb_clamp(x,xmin,xmax) ((x) < (xmin) ? (xmin) : (x) > (xmax) ? (xmax) : (x)) + +STB_EXTERN void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize); +STB_EXTERN int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis); +STB_EXTERN void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt); + +STB_EXTERN int stb_float_eq(float x, float y, float delta, int max_ulps); +STB_EXTERN int stb_is_prime(unsigned int m); +STB_EXTERN unsigned int stb_power_of_two_nearest_prime(int n); + +STB_EXTERN float stb_smoothstep(float t); +STB_EXTERN float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3); + +STB_EXTERN double stb_linear_remap(double x, double a, double b, + double c, double d); + +#ifdef STB_DEFINE +float stb_smoothstep(float t) +{ + return (3 - 2*t)*(t*t); +} + +float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3) +{ + float it = 1-t; + return it*it*it*p0 + 3*it*it*t*p1 + 3*it*t*t*p2 + t*t*t*p3; +} + +void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize) +{ + int i,j; + float p; + normal[0] = normal[1] = normal[2] = 0; + for (i=num_vert-1,j=0; j < num_vert; i=j++) { + float *u = vert[i]; + float *v = vert[j]; + normal[0] += (u[1] - v[1]) * (u[2] + v[2]); + normal[1] += (u[2] - v[2]) * (u[0] + v[0]); + normal[2] += (u[0] - v[0]) * (u[1] + v[1]); + } + if (normalize) { + p = normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]; + p = (float) (1.0 / sqrt(p)); + normal[0] *= p; + normal[1] *= p; + normal[2] *= p; + } +} + +int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis) +{ + static int box_vertices[6][4][3] = + { + { { 1,1,1 }, { 1,0,1 }, { 1,0,0 }, { 1,1,0 } }, + { { 0,0,0 }, { 0,0,1 }, { 0,1,1 }, { 0,1,0 } }, + { { 0,0,0 }, { 0,1,0 }, { 1,1,0 }, { 1,0,0 } }, + { { 0,0,0 }, { 1,0,0 }, { 1,0,1 }, { 0,0,1 } }, + { { 1,1,1 }, { 0,1,1 }, { 0,0,1 }, { 1,0,1 } }, + { { 1,1,1 }, { 1,1,0 }, { 0,1,0 }, { 0,1,1 } }, + }; + assert(face_number >= 0 && face_number < 6); + assert(vertex_number >= 0 && vertex_number < 4); + assert(axis >= 0 && axis < 3); + return box_vertices[face_number][vertex_number][axis]; +} + +void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt) +{ + float sign = 1, p, cp = *curpos; + if (cp == target_pos) return; + if (target_pos < cp) { + target_pos = -target_pos; + cp = -cp; + sign = -1; + } + // first decelerate + if (cp < 0) { + p = cp + deacc * dt; + if (p > 0) { + p = 0; + dt = dt - cp / deacc; + if (dt < 0) dt = 0; + } else { + dt = 0; + } + cp = p; + } + // now accelerate + p = cp + acc*dt; + if (p > target_pos) p = target_pos; + *curpos = p * sign; + // @TODO: testing +} + +float stb_quadratic_controller(float target_pos, float curpos, float maxvel, float maxacc, float dt, float *curvel) +{ + return 0; // @TODO +} + +int stb_float_eq(float x, float y, float delta, int max_ulps) +{ + if (fabs(x-y) <= delta) return 1; + if (abs(*(int *)&x - *(int *)&y) <= max_ulps) return 1; + return 0; +} + +int stb_is_prime(unsigned int m) +{ + unsigned int i,j; + if (m < 2) return 0; + if (m == 2) return 1; + if (!(m & 1)) return 0; + if (m % 3 == 0) return (m == 3); + for (i=5; (j=i*i), j <= m && j > i; i += 6) { + if (m % i == 0) return 0; + if (m % (i+2) == 0) return 0; + } + return 1; +} + +unsigned int stb_power_of_two_nearest_prime(int n) +{ + static signed char tab[32] = { 0,0,0,0,1,0,-1,0,1,-1,-1,3,-1,0,-1,2,1, + 0,2,0,-1,-4,-1,5,-1,18,-2,15,2,-1,2,0 }; + if (!tab[0]) { + int i; + for (i=0; i < 32; ++i) + tab[i] = (1 << i) + 2*tab[i] - 1; + tab[1] = 2; + tab[0] = 1; + } + if (n >= 32) return 0xfffffffb; + return tab[n]; +} + +double stb_linear_remap(double x, double x_min, double x_max, + double out_min, double out_max) +{ + return stb_lerp(stb_unlerp(x,x_min,x_max),out_min,out_max); +} +#endif + +// create a macro so it's faster, but you can get at the function pointer +#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d) + + +////////////////////////////////////////////////////////////////////////////// +// +// bit operations +// + +#define stb_big32(c) (((c)[0]<<24) + (c)[1]*65536 + (c)[2]*256 + (c)[3]) +#define stb_little32(c) (((c)[3]<<24) + (c)[2]*65536 + (c)[1]*256 + (c)[0]) +#define stb_big16(c) ((c)[0]*256 + (c)[1]) +#define stb_little16(c) ((c)[1]*256 + (c)[0]) + +STB_EXTERN int stb_bitcount(unsigned int a); +STB_EXTERN unsigned int stb_bitreverse8(unsigned char n); +STB_EXTERN unsigned int stb_bitreverse(unsigned int n); + +STB_EXTERN int stb_is_pow2(unsigned int n); +STB_EXTERN int stb_log2_ceil(unsigned int n); +STB_EXTERN int stb_log2_floor(unsigned int n); + +STB_EXTERN int stb_lowbit8(unsigned int n); +STB_EXTERN int stb_highbit8(unsigned int n); + +#ifdef STB_DEFINE +int stb_bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +unsigned int stb_bitreverse8(unsigned char n) +{ + n = ((n & 0xAA) >> 1) + ((n & 0x55) << 1); + n = ((n & 0xCC) >> 2) + ((n & 0x33) << 2); + return (unsigned char) ((n >> 4) + (n << 4)); +} + +unsigned int stb_bitreverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +int stb_is_pow2(unsigned int n) +{ + return (n & (n-1)) == 0; +} + +// tricky use of 4-bit table to identify 5 bit positions (note the '-1') +// 3-bit table would require another tree level; 5-bit table wouldn't save one +#if defined(_WIN32) && !defined(__MINGW32__) +#pragma warning(push) +#pragma warning(disable: 4035) // disable warning about no return value +int stb_log2_floor(unsigned int n) +{ + #if _MSC_VER > 1700 + unsigned long i; + _BitScanReverse(&i, n); + return i != 0 ? i : -1; + #else + __asm { + bsr eax,n + jnz done + mov eax,-1 + } + done:; + #endif +} +#pragma warning(pop) +#else +int stb_log2_floor(unsigned int n) +{ + static signed char log2_4[16] = { -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3 }; + + // 2 compares if n < 16, 3 compares otherwise + if (n < (1U << 14)) + if (n < (1U << 4)) return 0 + log2_4[n ]; + else if (n < (1U << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1U << 24)) + if (n < (1U << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1U << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} +#endif + +// define ceil from floor +int stb_log2_ceil(unsigned int n) +{ + if (stb_is_pow2(n)) return stb_log2_floor(n); + else return 1 + stb_log2_floor(n); +} + +int stb_highbit8(unsigned int n) +{ + return stb_log2_ceil(n&255); +} + +int stb_lowbit8(unsigned int n) +{ + static signed char lowbit4[16] = { -1,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0 }; + int k = lowbit4[n & 15]; + if (k >= 0) return k; + k = lowbit4[(n >> 4) & 15]; + if (k >= 0) return k+4; + return k; +} +#endif + + + +////////////////////////////////////////////////////////////////////////////// +// +// qsort Compare Routines +// + +#ifdef _WIN32 + #define stb_stricmp(a,b) stricmp(a,b) + #define stb_strnicmp(a,b,n) strnicmp(a,b,n) +#else + #define stb_stricmp(a,b) strcasecmp(a,b) + #define stb_strnicmp(a,b,n) strncasecmp(a,b,n) +#endif + + +STB_EXTERN int (*stb_intcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_qsort_strcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_qsort_stricmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_floatcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_doublecmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_charcmp(int offset))(const void *a, const void *b); + +#ifdef STB_DEFINE +static int stb__intcmpoffset, stb__charcmpoffset, stb__strcmpoffset; +static int stb__floatcmpoffset, stb__doublecmpoffset; + +int stb__intcmp(const void *a, const void *b) +{ + const int p = *(const int *) ((const char *) a + stb__intcmpoffset); + const int q = *(const int *) ((const char *) b + stb__intcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__charcmp(const void *a, const void *b) +{ + const int p = *(const unsigned char *) ((const char *) a + stb__charcmpoffset); + const int q = *(const unsigned char *) ((const char *) b + stb__charcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__floatcmp(const void *a, const void *b) +{ + const float p = *(const float *) ((const char *) a + stb__floatcmpoffset); + const float q = *(const float *) ((const char *) b + stb__floatcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__doublecmp(const void *a, const void *b) +{ + const double p = *(const double *) ((const char *) a + stb__doublecmpoffset); + const double q = *(const double *) ((const char *) b + stb__doublecmpoffset); + return p < q ? -1 : p > q; +} + +int stb__qsort_strcmp(const void *a, const void *b) +{ + const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); + const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); + return strcmp(p,q); +} + +int stb__qsort_stricmp(const void *a, const void *b) +{ + const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); + const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); + return stb_stricmp(p,q); +} + +int (*stb_intcmp(int offset))(const void *, const void *) +{ + stb__intcmpoffset = offset; + return &stb__intcmp; +} + +int (*stb_charcmp(int offset))(const void *, const void *) +{ + stb__charcmpoffset = offset; + return &stb__charcmp; +} + +int (*stb_qsort_strcmp(int offset))(const void *, const void *) +{ + stb__strcmpoffset = offset; + return &stb__qsort_strcmp; +} + +int (*stb_qsort_stricmp(int offset))(const void *, const void *) +{ + stb__strcmpoffset = offset; + return &stb__qsort_stricmp; +} + +int (*stb_floatcmp(int offset))(const void *, const void *) +{ + stb__floatcmpoffset = offset; + return &stb__floatcmp; +} + +int (*stb_doublecmp(int offset))(const void *, const void *) +{ + stb__doublecmpoffset = offset; + return &stb__doublecmp; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Binary Search Toolkit +// + +typedef struct +{ + int minval, maxval, guess; + int mode, step; +} stb_search; + +STB_EXTERN int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest); +STB_EXTERN int stb_search_open(stb_search *s, int minv, int find_smallest); +STB_EXTERN int stb_probe(stb_search *s, int compare, int *result); // return 0 when done + +#ifdef STB_DEFINE +enum +{ + STB_probe_binary_smallest, + STB_probe_binary_largest, + STB_probe_open_smallest, + STB_probe_open_largest, +}; + +static int stb_probe_guess(stb_search *s, int *result) +{ + switch(s->mode) { + case STB_probe_binary_largest: + if (s->minval == s->maxval) { + *result = s->minval; + return 0; + } + assert(s->minval < s->maxval); + // if a < b, then a < p <= b + s->guess = s->minval + (((unsigned) s->maxval - s->minval + 1) >> 1); + break; + + case STB_probe_binary_smallest: + if (s->minval == s->maxval) { + *result = s->minval; + return 0; + } + assert(s->minval < s->maxval); + // if a < b, then a <= p < b + s->guess = s->minval + (((unsigned) s->maxval - s->minval) >> 1); + break; + case STB_probe_open_smallest: + case STB_probe_open_largest: + s->guess = s->maxval; // guess the current maxval + break; + } + *result = s->guess; + return 1; +} + +int stb_probe(stb_search *s, int compare, int *result) +{ + switch(s->mode) { + case STB_probe_open_smallest: + case STB_probe_open_largest: { + if (compare <= 0) { + // then it lies within minval & maxval + if (s->mode == STB_probe_open_smallest) + s->mode = STB_probe_binary_smallest; + else + s->mode = STB_probe_binary_largest; + } else { + // otherwise, we need to probe larger + s->minval = s->maxval + 1; + s->maxval = s->minval + s->step; + s->step += s->step; + } + break; + } + case STB_probe_binary_smallest: { + // if compare < 0, then s->minval <= a < p + // if compare = 0, then s->minval <= a <= p + // if compare > 0, then p < a <= s->maxval + if (compare <= 0) + s->maxval = s->guess; + else + s->minval = s->guess+1; + break; + } + case STB_probe_binary_largest: { + // if compare < 0, then s->minval <= a < p + // if compare = 0, then p <= a <= s->maxval + // if compare > 0, then p < a <= s->maxval + if (compare < 0) + s->maxval = s->guess-1; + else + s->minval = s->guess; + break; + } + } + return stb_probe_guess(s, result); +} + +int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest) +{ + int r; + if (maxv < minv) return minv-1; + s->minval = minv; + s->maxval = maxv; + s->mode = find_smallest ? STB_probe_binary_smallest : STB_probe_binary_largest; + stb_probe_guess(s, &r); + return r; +} + +int stb_search_open(stb_search *s, int minv, int find_smallest) +{ + int r; + s->step = 4; + s->minval = minv; + s->maxval = minv+s->step; + s->mode = find_smallest ? STB_probe_open_smallest : STB_probe_open_largest; + stb_probe_guess(s, &r); + return r; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// String Processing +// + +#define stb_prefixi(s,t) (0==stb_strnicmp((s),(t),strlen(t))) + +enum stb_splitpath_flag +{ + STB_PATH = 1, + STB_FILE = 2, + STB_EXT = 4, + STB_PATH_FILE = STB_PATH + STB_FILE, + STB_FILE_EXT = STB_FILE + STB_EXT, + STB_EXT_NO_PERIOD = 8, +}; + +STB_EXTERN char * stb_skipwhite(char *s); +STB_EXTERN char * stb_trimwhite(char *s); +STB_EXTERN char * stb_skipnewline(char *s); +STB_EXTERN char * stb_strncpy(char *s, char *t, int n); +STB_EXTERN char * stb_substr(char *t, int n); +STB_EXTERN char * stb_duplower(char *s); +STB_EXTERN void stb_tolower (char *s); +STB_EXTERN char * stb_strchr2 (char *s, char p1, char p2); +STB_EXTERN char * stb_strrchr2(char *s, char p1, char p2); +STB_EXTERN char * stb_strtok(char *output, char *src, char *delimit); +STB_EXTERN char * stb_strtok_keep(char *output, char *src, char *delimit); +STB_EXTERN char * stb_strtok_invert(char *output, char *src, char *allowed); +STB_EXTERN char * stb_dupreplace(char *s, char *find, char *replace); +STB_EXTERN void stb_replaceinplace(char *s, char *find, char *replace); +STB_EXTERN char * stb_splitpath(char *output, char *src, int flag); +STB_EXTERN char * stb_splitpathdup(char *src, int flag); +STB_EXTERN char * stb_replacedir(char *output, char *src, char *dir); +STB_EXTERN char * stb_replaceext(char *output, char *src, char *ext); +STB_EXTERN void stb_fixpath(char *path); +STB_EXTERN char * stb_shorten_path_readable(char *path, int max_len); +STB_EXTERN int stb_suffix (char *s, char *t); +STB_EXTERN int stb_suffixi(char *s, char *t); +STB_EXTERN int stb_prefix (char *s, char *t); +STB_EXTERN char * stb_strichr(char *s, char t); +STB_EXTERN char * stb_stristr(char *s, char *t); +STB_EXTERN int stb_prefix_count(char *s, char *t); +STB_EXTERN char * stb_plural(int n); // "s" or "" + +STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out); +STB_EXTERN char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out); +STB_EXTERN char **stb_tokens_allowempty(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_stripwhite(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_withdelim(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_quoted(char *src, char *delimit, int *count); +// with 'quoted', allow delimiters to appear inside quotation marks, and don't +// strip whitespace inside them (and we delete the quotation marks unless they +// appear back to back, in which case they're considered escaped) + +#ifdef STB_DEFINE + +char *stb_plural(int n) +{ + return n == 1 ? "" : "s"; +} + +int stb_prefix(char *s, char *t) +{ + while (*t) + if (*s++ != *t++) + return STB_FALSE; + return STB_TRUE; +} + +int stb_prefix_count(char *s, char *t) +{ + int c=0; + while (*t) { + if (*s++ != *t++) + break; + ++c; + } + return c; +} + +int stb_suffix(char *s, char *t) +{ + size_t n = strlen(s); + size_t m = strlen(t); + if (m <= n) + return 0 == strcmp(s+n-m, t); + else + return 0; +} + +int stb_suffixi(char *s, char *t) +{ + size_t n = strlen(s); + size_t m = strlen(t); + if (m <= n) + return 0 == stb_stricmp(s+n-m, t); + else + return 0; +} + +// originally I was using this table so that I could create known sentinel +// values--e.g. change whitetable[0] to be true if I was scanning for whitespace, +// and false if I was scanning for nonwhite. I don't appear to be using that +// functionality anymore (I do for tokentable, though), so just replace it +// with isspace() +char *stb_skipwhite(char *s) +{ + while (isspace((unsigned char) *s)) ++s; + return s; +} + +char *stb_skipnewline(char *s) +{ + if (s[0] == '\r' || s[0] == '\n') { + if (s[0]+s[1] == '\r' + '\n') ++s; + ++s; + } + return s; +} + +char *stb_trimwhite(char *s) +{ + int i,n; + s = stb_skipwhite(s); + n = (int) strlen(s); + for (i=n-1; i >= 0; --i) + if (!isspace(s[i])) + break; + s[i+1] = 0; + return s; +} + +char *stb_strncpy(char *s, char *t, int n) +{ + strncpy(s,t,n); + s[n-1] = 0; + return s; +} + +char *stb_substr(char *t, int n) +{ + char *a; + int z = (int) strlen(t); + if (z < n) n = z; + a = (char *) malloc(n+1); + strncpy(a,t,n); + a[n] = 0; + return a; +} + +char *stb_duplower(char *s) +{ + char *p = strdup(s), *q = p; + while (*q) { + *q = tolower(*q); + ++q; + } + return p; +} + +void stb_tolower(char *s) +{ + while (*s) { + *s = tolower(*s); + ++s; + } +} + +char *stb_strchr2(char *s, char x, char y) +{ + for(; *s; ++s) + if (*s == x || *s == y) + return s; + return NULL; +} + +char *stb_strrchr2(char *s, char x, char y) +{ + char *r = NULL; + for(; *s; ++s) + if (*s == x || *s == y) + r = s; + return r; +} + +char *stb_strichr(char *s, char t) +{ + if (tolower(t) == toupper(t)) + return strchr(s,t); + return stb_strchr2(s, (char) tolower(t), (char) toupper(t)); +} + +char *stb_stristr(char *s, char *t) +{ + size_t n = strlen(t); + char *z; + if (n==0) return s; + while ((z = stb_strichr(s, *t)) != NULL) { + if (0==stb_strnicmp(z, t, n)) + return z; + s = z+1; + } + return NULL; +} + +static char *stb_strtok_raw(char *output, char *src, char *delimit, int keep, int invert) +{ + if (invert) { + while (*src && strchr(delimit, *src) != NULL) { + *output++ = *src++; + } + } else { + while (*src && strchr(delimit, *src) == NULL) { + *output++ = *src++; + } + } + *output = 0; + if (keep) + return src; + else + return *src ? src+1 : src; +} + +char *stb_strtok(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 0, 0); +} + +char *stb_strtok_keep(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 1, 0); +} + +char *stb_strtok_invert(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 1,1); +} + +static char **stb_tokens_raw(char *src_, char *delimit, int *count, + int stripwhite, int allow_empty, char *start, char *end) +{ + int nested = 0; + unsigned char *src = (unsigned char *) src_; + static char stb_tokentable[256]; // rely on static initializion to 0 + static char stable[256],etable[256]; + char *out; + char **result; + int num=0; + unsigned char *s; + + s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 1; + if (start) { + s = (unsigned char *) start; while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; + } + stable[0] = 1; + + // two passes through: the first time, counting how many + s = (unsigned char *) src; + while (*s) { + // state: just found delimiter + // skip further delimiters + if (!allow_empty) { + stb_tokentable[0] = 0; + while (stb_tokentable[*s]) + ++s; + if (!*s) break; + } + ++num; + // skip further non-delimiters + stb_tokentable[0] = 1; + if (stripwhite == 2) { // quoted strings + while (!stb_tokentable[*s]) { + if (*s != '"') + ++s; + else { + ++s; + if (*s == '"') + ++s; // "" -> ", not start a string + else { + // begin a string + while (*s) { + if (s[0] == '"') { + if (s[1] == '"') s += 2; // "" -> " + else { ++s; break; } // terminating " + } else + ++s; + } + } + } + } + } else + while (nested || !stb_tokentable[*s]) { + if (stable[*s]) { + if (!*s) break; + if (end ? etable[*s] : nested) + --nested; + else + ++nested; + } + ++s; + } + if (allow_empty) { + if (*s) ++s; + } + } + // now num has the actual count... malloc our output structure + // need space for all the strings: strings won't be any longer than + // original input, since for every '\0' there's at least one delimiter + result = (char **) malloc(sizeof(*result) * (num+1) + (s-src+1)); + if (result == NULL) return result; + out = (char *) (result + (num+1)); + // second pass: copy out the data + s = (unsigned char *) src; + num = 0; + nested = 0; + while (*s) { + char *last_nonwhite; + // state: just found delimiter + // skip further delimiters + if (!allow_empty) { + stb_tokentable[0] = 0; + if (stripwhite) + while (stb_tokentable[*s] || isspace(*s)) + ++s; + else + while (stb_tokentable[*s]) + ++s; + } else if (stripwhite) { + while (isspace(*s)) ++s; + } + if (!*s) break; + // we're past any leading delimiters and whitespace + result[num] = out; + ++num; + // copy non-delimiters + stb_tokentable[0] = 1; + last_nonwhite = out-1; + if (stripwhite == 2) { + while (!stb_tokentable[*s]) { + if (*s != '"') { + if (!isspace(*s)) last_nonwhite = out; + *out++ = *s++; + } else { + ++s; + if (*s == '"') { + if (!isspace(*s)) last_nonwhite = out; + *out++ = *s++; // "" -> ", not start string + } else { + // begin a quoted string + while (*s) { + if (s[0] == '"') { + if (s[1] == '"') { *out++ = *s; s += 2; } + else { ++s; break; } // terminating " + } else + *out++ = *s++; + } + last_nonwhite = out-1; // all in quotes counts as non-white + } + } + } + } else { + while (nested || !stb_tokentable[*s]) { + if (!isspace(*s)) last_nonwhite = out; + if (stable[*s]) { + if (!*s) break; + if (end ? etable[*s] : nested) + --nested; + else + ++nested; + } + *out++ = *s++; + } + } + + if (stripwhite) // rewind to last non-whitespace char + out = last_nonwhite+1; + *out++ = '\0'; + + if (*s) ++s; // skip delimiter + } + s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 0; + if (start) { + s = (unsigned char *) start; while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; + } + if (count != NULL) *count = num; + result[num] = 0; + return result; +} + +char **stb_tokens(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,0,0,0,0); +} + +char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out) +{ + return stb_tokens_raw(src,delimit,count,0,0,nest_in,nest_out); +} + +char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out) +{ + return stb_tokens_raw(src,delimit,count,0,1,nest_in,nest_out); +} + +char **stb_tokens_allowempty(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,0,1,0,0); +} + +char **stb_tokens_stripwhite(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,1,1,0,0); +} + +char **stb_tokens_quoted(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,2,1,0,0); +} + +char *stb_dupreplace(char *src, char *find, char *replace) +{ + size_t len_find = strlen(find); + size_t len_replace = strlen(replace); + int count = 0; + + char *s,*p,*q; + + s = strstr(src, find); + if (s == NULL) return strdup(src); + do { + ++count; + s = strstr(s + len_find, find); + } while (s != NULL); + + p = (char *) malloc(strlen(src) + count * (len_replace - len_find) + 1); + if (p == NULL) return p; + q = p; + s = src; + for (;;) { + char *t = strstr(s, find); + if (t == NULL) { + strcpy(q,s); + assert(strlen(p) == strlen(src) + count*(len_replace-len_find)); + return p; + } + memcpy(q, s, t-s); + q += t-s; + memcpy(q, replace, len_replace); + q += len_replace; + s = t + len_find; + } +} + +void stb_replaceinplace(char *src, char *find, char *replace) +{ + size_t len_find = strlen(find); + size_t len_replace = strlen(replace); + int delta; + + char *s,*p,*q; + + delta = len_replace - len_find; + assert(delta <= 0); + if (delta > 0) return; + + p = strstr(src, find); + if (p == NULL) return; + + s = q = p; + while (*s) { + memcpy(q, replace, len_replace); + p += len_find; + q += len_replace; + s = strstr(p, find); + if (s == NULL) s = p + strlen(p); + memmove(q, p, s-p); + q += s-p; + p = s; + } + *q = 0; +} + +void stb_fixpath(char *path) +{ + for(; *path; ++path) + if (*path == '\\') + *path = '/'; +} + +void stb__add_section(char *buffer, char *data, int curlen, int newlen) +{ + if (newlen < curlen) { + int z1 = newlen >> 1, z2 = newlen-z1; + memcpy(buffer, data, z1-1); + buffer[z1-1] = '.'; + buffer[z1-0] = '.'; + memcpy(buffer+z1+1, data+curlen-z2+1, z2-1); + } else + memcpy(buffer, data, curlen); +} + +char * stb_shorten_path_readable(char *path, int len) +{ + static char buffer[1024]; + int n = strlen(path),n1,n2,r1,r2; + char *s; + if (n <= len) return path; + if (len > 1024) return path; + s = stb_strrchr2(path, '/', '\\'); + if (s) { + n1 = s - path + 1; + n2 = n - n1; + ++s; + } else { + n1 = 0; + n2 = n; + s = path; + } + // now we need to reduce r1 and r2 so that they fit in len + if (n1 < len>>1) { + r1 = n1; + r2 = len - r1; + } else if (n2 < len >> 1) { + r2 = n2; + r1 = len - r2; + } else { + r1 = n1 * len / n; + r2 = n2 * len / n; + if (r1 < len>>2) r1 = len>>2, r2 = len-r1; + if (r2 < len>>2) r2 = len>>2, r1 = len-r2; + } + assert(r1 <= n1 && r2 <= n2); + if (n1) + stb__add_section(buffer, path, n1, r1); + stb__add_section(buffer+r1, s, n2, r2); + buffer[len] = 0; + return buffer; +} + +static char *stb__splitpath_raw(char *buffer, char *path, int flag) +{ + int len=0,x,y, n = (int) strlen(path), f1,f2; + char *s = stb_strrchr2(path, '/', '\\'); + char *t = strrchr(path, '.'); + if (s && t && t < s) t = NULL; + if (s) ++s; + + if (flag == STB_EXT_NO_PERIOD) + flag |= STB_EXT; + + if (!(flag & (STB_PATH | STB_FILE | STB_EXT))) return NULL; + + f1 = s == NULL ? 0 : s-path; // start of filename + f2 = t == NULL ? n : t-path; // just past end of filename + + if (flag & STB_PATH) { + x = 0; if (f1 == 0 && flag == STB_PATH) len=2; + } else if (flag & STB_FILE) { + x = f1; + } else { + x = f2; + if (flag & STB_EXT_NO_PERIOD) + if (buffer[x] == '.') + ++x; + } + + if (flag & STB_EXT) + y = n; + else if (flag & STB_FILE) + y = f2; + else + y = f1; + + if (buffer == NULL) { + buffer = (char *) malloc(y-x + len + 1); + if (!buffer) return NULL; + } + + if (len) { strcpy(buffer, "./"); return buffer; } + strncpy(buffer, path+x, y-x); + buffer[y-x] = 0; + return buffer; +} + +char *stb_splitpath(char *output, char *src, int flag) +{ + return stb__splitpath_raw(output, src, flag); +} + +char *stb_splitpathdup(char *src, int flag) +{ + return stb__splitpath_raw(NULL, src, flag); +} + +char *stb_replacedir(char *output, char *src, char *dir) +{ + char buffer[4096]; + stb_splitpath(buffer, src, STB_FILE | STB_EXT); + if (dir) + sprintf(output, "%s/%s", dir, buffer); + else + strcpy(output, buffer); + return output; +} + +char *stb_replaceext(char *output, char *src, char *ext) +{ + char buffer[4096]; + stb_splitpath(buffer, src, STB_PATH | STB_FILE); + if (ext) + sprintf(output, "%s.%s", buffer, ext[0] == '.' ? ext+1 : ext); + else + strcpy(output, buffer); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_alloc - hierarchical allocator +// +// inspired by http://swapped.cc/halloc +// +// +// When you alloc a given block through stb_alloc, you have these choices: +// +// 1. does it have a parent? +// 2. can it have children? +// 3. can it be freed directly? +// 4. is it transferrable? +// 5. what is its alignment? +// +// Here are interesting combinations of those: +// +// children free transfer alignment +// arena Y Y N n/a +// no-overhead, chunked N N N normal +// string pool alloc N N N 1 +// parent-ptr, chunked Y N N normal +// low-overhead, unchunked N Y Y normal +// general purpose alloc Y Y Y normal +// +// Unchunked allocations will probably return 16-aligned pointers. If +// we 16-align the results, we have room for 4 pointers. For smaller +// allocations that allow finer alignment, we can reduce the pointers. +// +// The strategy is that given a pointer, assuming it has a header (only +// the no-overhead allocations have no header), we can determine the +// type of the header fields, and the number of them, by stepping backwards +// through memory and looking at the tags in the bottom bits. +// +// Implementation strategy: +// chunked allocations come from the middle of chunks, and can't +// be freed. thefore they do not need to be on a sibling chain. +// they may need child pointers if they have children. +// +// chunked, with-children +// void *parent; +// +// unchunked, no-children -- reduced storage +// void *next_sibling; +// void *prev_sibling_nextp; +// +// unchunked, general +// void *first_child; +// void *next_sibling; +// void *prev_sibling_nextp; +// void *chunks; +// +// so, if we code each of these fields with different bit patterns +// (actually same one for next/prev/child), then we can identify which +// each one is from the last field. + +STB_EXTERN void stb_free(void *p); +STB_EXTERN void *stb_malloc_global(size_t size); +STB_EXTERN void *stb_malloc(void *context, size_t size); +STB_EXTERN void *stb_malloc_nofree(void *context, size_t size); +STB_EXTERN void *stb_malloc_leaf(void *context, size_t size); +STB_EXTERN void *stb_malloc_raw(void *context, size_t size); +STB_EXTERN void *stb_realloc(void *ptr, size_t newsize); + +STB_EXTERN void stb_reassign(void *new_context, void *ptr); +STB_EXTERN void stb_malloc_validate(void *p, void *parent); + +extern int stb_alloc_chunk_size ; +extern int stb_alloc_count_free ; +extern int stb_alloc_count_alloc; +extern int stb_alloc_alignment ; + +#ifdef STB_DEFINE + +int stb_alloc_chunk_size = 65536; +int stb_alloc_count_free = 0; +int stb_alloc_count_alloc = 0; +int stb_alloc_alignment = -16; + +typedef struct stb__chunk +{ + struct stb__chunk *next; + int data_left; + int alloc; +} stb__chunk; + +typedef struct +{ + void * next; + void ** prevn; +} stb__nochildren; + +typedef struct +{ + void ** prevn; + void * child; + void * next; + stb__chunk *chunks; +} stb__alloc; + +typedef struct +{ + stb__alloc *parent; +} stb__chunked; + +#define STB__PARENT 1 +#define STB__CHUNKS 2 + +typedef enum +{ + STB__nochildren = 0, + STB__chunked = STB__PARENT, + STB__alloc = STB__CHUNKS, + + STB__chunk_raw = 4, +} stb__alloc_type; + +// these functions set the bottom bits of a pointer efficiently +#define STB__DECODE(x,v) ((void *) ((char *) (x) - (v))) +#define STB__ENCODE(x,v) ((void *) ((char *) (x) + (v))) + +#define stb__parent(z) (stb__alloc *) STB__DECODE((z)->parent, STB__PARENT) +#define stb__chunks(z) (stb__chunk *) STB__DECODE((z)->chunks, STB__CHUNKS) + +#define stb__setparent(z,p) (z)->parent = (stb__alloc *) STB__ENCODE((p), STB__PARENT) +#define stb__setchunks(z,c) (z)->chunks = (stb__chunk *) STB__ENCODE((c), STB__CHUNKS) + +static stb__alloc stb__alloc_global = +{ + NULL, + NULL, + NULL, + (stb__chunk *) STB__ENCODE(NULL, STB__CHUNKS) +}; + +static stb__alloc_type stb__identify(void *p) +{ + void **q = (void **) p; + return (stb__alloc_type) ((stb_uinta) q[-1] & 3); +} + +static void *** stb__prevn(void *p) +{ + if (stb__identify(p) == STB__alloc) { + stb__alloc *s = (stb__alloc *) p - 1; + return &s->prevn; + } else { + stb__nochildren *s = (stb__nochildren *) p - 1; + return &s->prevn; + } +} + +void stb_free(void *p) +{ + if (p == NULL) return; + + // count frees so that unit tests can see what's happening + ++stb_alloc_count_free; + + switch(stb__identify(p)) { + case STB__chunked: + // freeing a chunked-block with children does nothing; + // they only get freed when the parent does + // surely this is wrong, and it should free them immediately? + // otherwise how are they getting put on the right chain? + return; + case STB__nochildren: { + stb__nochildren *s = (stb__nochildren *) p - 1; + // unlink from sibling chain + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + free(s); + return; + } + case STB__alloc: { + stb__alloc *s = (stb__alloc *) p - 1; + stb__chunk *c, *n; + void *q; + + // unlink from sibling chain, if any + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + + // first free chunks + c = (stb__chunk *) stb__chunks(s); + while (c != NULL) { + n = c->next; + stb_alloc_count_free += c->alloc; + free(c); + c = n; + } + + // validating + stb__setchunks(s,NULL); + s->prevn = NULL; + s->next = NULL; + + // now free children + while ((q = s->child) != NULL) { + stb_free(q); + } + + // now free self + free(s); + return; + } + default: + assert(0); /* NOTREACHED */ + } +} + +void stb_malloc_validate(void *p, void *parent) +{ + if (p == NULL) return; + + switch(stb__identify(p)) { + case STB__chunked: + return; + case STB__nochildren: { + stb__nochildren *n = (stb__nochildren *) p - 1; + if (n->prevn) + assert(*n->prevn == p); + if (n->next) { + assert(*stb__prevn(n->next) == &n->next); + stb_malloc_validate(n, parent); + } + return; + } + case STB__alloc: { + stb__alloc *s = (stb__alloc *) p - 1; + + if (s->prevn) + assert(*s->prevn == p); + + if (s->child) { + assert(*stb__prevn(s->child) == &s->child); + stb_malloc_validate(s->child, p); + } + + if (s->next) { + assert(*stb__prevn(s->next) == &s->next); + stb_malloc_validate(s->next, parent); + } + return; + } + default: + assert(0); /* NOTREACHED */ + } +} + +static void * stb__try_chunk(stb__chunk *c, int size, int align, int pre_align) +{ + char *memblock = (char *) (c+1), *q; + stb_inta iq; + int start_offset; + + // we going to allocate at the end of the chunk, not the start. confusing, + // but it means we don't need both a 'limit' and a 'cur', just a 'cur'. + // the block ends at: p + c->data_left + // then we move back by size + start_offset = c->data_left - size; + + // now we need to check the alignment of that + q = memblock + start_offset; + iq = (stb_inta) q; + assert(sizeof(q) == sizeof(iq)); + + // suppose align = 2 + // then we need to retreat iq far enough that (iq & (2-1)) == 0 + // to get (iq & (align-1)) = 0 requires subtracting (iq & (align-1)) + + start_offset -= iq & (align-1); + assert(((stb_uinta) (memblock+start_offset) & (align-1)) == 0); + + // now, if that + pre_align works, go for it! + start_offset -= pre_align; + + if (start_offset >= 0) { + c->data_left = start_offset; + return memblock + start_offset; + } + + return NULL; +} + +static void stb__sort_chunks(stb__alloc *src) +{ + // of the first two chunks, put the chunk with more data left in it first + stb__chunk *c = stb__chunks(src), *d; + if (c == NULL) return; + d = c->next; + if (d == NULL) return; + if (c->data_left > d->data_left) return; + + c->next = d->next; + d->next = c; + stb__setchunks(src, d); +} + +static void * stb__alloc_chunk(stb__alloc *src, int size, int align, int pre_align) +{ + void *p; + stb__chunk *c = stb__chunks(src); + + if (c && size <= stb_alloc_chunk_size) { + + p = stb__try_chunk(c, size, align, pre_align); + if (p) { ++c->alloc; return p; } + + // try a second chunk to reduce wastage + if (c->next) { + p = stb__try_chunk(c->next, size, align, pre_align); + if (p) { ++c->alloc; return p; } + + // put the bigger chunk first, since the second will get buried + // the upshot of this is that, until it gets allocated from, chunk #2 + // is always the largest remaining chunk. (could formalize + // this with a heap!) + stb__sort_chunks(src); + c = stb__chunks(src); + } + } + + // allocate a new chunk + { + stb__chunk *n; + + int chunk_size = stb_alloc_chunk_size; + // we're going to allocate a new chunk to put this in + if (size > chunk_size) + chunk_size = size; + + assert(sizeof(*n) + pre_align <= 16); + + // loop trying to allocate a large enough chunk + // the loop is because the alignment may cause problems if it's big... + // and we don't know what our chunk alignment is going to be + while (1) { + n = (stb__chunk *) malloc(16 + chunk_size); + if (n == NULL) return NULL; + + n->data_left = chunk_size - sizeof(*n); + + p = stb__try_chunk(n, size, align, pre_align); + if (p != NULL) { + n->next = c; + stb__setchunks(src, n); + + // if we just used up the whole block immediately, + // move the following chunk up + n->alloc = 1; + if (size == chunk_size) + stb__sort_chunks(src); + + return p; + } + + free(n); + chunk_size += 16+align; + } + } +} + +static stb__alloc * stb__get_context(void *context) +{ + if (context == NULL) { + return &stb__alloc_global; + } else { + int u = stb__identify(context); + // if context is chunked, grab parent + if (u == STB__chunked) { + stb__chunked *s = (stb__chunked *) context - 1; + return stb__parent(s); + } else { + return (stb__alloc *) context - 1; + } + } +} + +static void stb__insert_alloc(stb__alloc *src, stb__alloc *s) +{ + s->prevn = &src->child; + s->next = src->child; + src->child = s+1; + if (s->next) + *stb__prevn(s->next) = &s->next; +} + +static void stb__insert_nochild(stb__alloc *src, stb__nochildren *s) +{ + s->prevn = &src->child; + s->next = src->child; + src->child = s+1; + if (s->next) + *stb__prevn(s->next) = &s->next; +} + +static void * malloc_base(void *context, size_t size, stb__alloc_type t, int align) +{ + void *p; + + stb__alloc *src = stb__get_context(context); + + if (align <= 0) { + // compute worst-case C packed alignment + // e.g. a 24-byte struct is 8-aligned + int align_proposed = 1 << stb_lowbit8(size); + + if (align_proposed < 0) + align_proposed = 4; + + if (align_proposed == 0) { + if (size == 0) + align_proposed = 1; + else + align_proposed = 256; + } + + // a negative alignment means 'don't align any larger + // than this'; so -16 means we align 1,2,4,8, or 16 + + if (align < 0) { + if (align_proposed > -align) + align_proposed = -align; + } + + align = align_proposed; + } + + assert(stb_is_pow2(align)); + + // don't cause misalignment when allocating nochildren + if (t == STB__nochildren && align > 8) + t = STB__alloc; + + switch (t) { + case STB__alloc: { + stb__alloc *s = (stb__alloc *) malloc(size + sizeof(*s)); + if (s == NULL) return NULL; + p = s+1; + s->child = NULL; + stb__insert_alloc(src, s); + + stb__setchunks(s,NULL); + break; + } + + case STB__nochildren: { + stb__nochildren *s = (stb__nochildren *) malloc(size + sizeof(*s)); + if (s == NULL) return NULL; + p = s+1; + stb__insert_nochild(src, s); + break; + } + + case STB__chunk_raw: { + p = stb__alloc_chunk(src, size, align, 0); + if (p == NULL) return NULL; + break; + } + + case STB__chunked: { + stb__chunked *s; + if (align < sizeof(stb_uintptr)) align = sizeof(stb_uintptr); + s = (stb__chunked *) stb__alloc_chunk(src, size, align, sizeof(*s)); + if (s == NULL) return NULL; + stb__setparent(s, src); + p = s+1; + break; + } + + default: p = NULL; assert(0); /* NOTREACHED */ + } + + ++stb_alloc_count_alloc; + return p; +} + +void *stb_malloc_global(size_t size) +{ + return malloc_base(NULL, size, STB__alloc, stb_alloc_alignment); +} + +void *stb_malloc(void *context, size_t size) +{ + return malloc_base(context, size, STB__alloc, stb_alloc_alignment); +} + +void *stb_malloc_nofree(void *context, size_t size) +{ + return malloc_base(context, size, STB__chunked, stb_alloc_alignment); +} + +void *stb_malloc_leaf(void *context, size_t size) +{ + return malloc_base(context, size, STB__nochildren, stb_alloc_alignment); +} + +void *stb_malloc_raw(void *context, size_t size) +{ + return malloc_base(context, size, STB__chunk_raw, stb_alloc_alignment); +} + +char *stb_malloc_string(void *context, size_t size) +{ + return (char *) malloc_base(context, size, STB__chunk_raw, 1); +} + +void *stb_realloc(void *ptr, size_t newsize) +{ + stb__alloc_type t; + + if (ptr == NULL) return stb_malloc(NULL, newsize); + if (newsize == 0) { stb_free(ptr); return NULL; } + + t = stb__identify(ptr); + assert(t == STB__alloc || t == STB__nochildren); + + if (t == STB__alloc) { + stb__alloc *s = (stb__alloc *) ptr - 1; + + s = (stb__alloc *) realloc(s, newsize + sizeof(*s)); + if (s == NULL) return NULL; + + ptr = s+1; + + // update pointers + (*s->prevn) = ptr; + if (s->next) + *stb__prevn(s->next) = &s->next; + + if (s->child) + *stb__prevn(s->child) = &s->child; + + return ptr; + } else { + stb__nochildren *s = (stb__nochildren *) ptr - 1; + + s = (stb__nochildren *) realloc(ptr, newsize + sizeof(s)); + if (s == NULL) return NULL; + + // update pointers + (*s->prevn) = s+1; + if (s->next) + *stb__prevn(s->next) = &s->next; + + return s+1; + } +} + +void *stb_realloc_c(void *context, void *ptr, size_t newsize) +{ + if (ptr == NULL) return stb_malloc(context, newsize); + if (newsize == 0) { stb_free(ptr); return NULL; } + // @TODO: verify you haven't changed contexts + return stb_realloc(ptr, newsize); +} + +void stb_reassign(void *new_context, void *ptr) +{ + stb__alloc *src = stb__get_context(new_context); + + stb__alloc_type t = stb__identify(ptr); + assert(t == STB__alloc || t == STB__nochildren); + + if (t == STB__alloc) { + stb__alloc *s = (stb__alloc *) ptr - 1; + + // unlink from old + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + + stb__insert_alloc(src, s); + } else { + stb__nochildren *s = (stb__nochildren *) ptr - 1; + + // unlink from old + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + + stb__insert_nochild(src, s); + } +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_arr +// +// An stb_arr is directly useable as a pointer (use the actual type in your +// definition), but when it resizes, it returns a new pointer and you can't +// use the old one, so you have to be careful to copy-in-out as necessary. +// +// Use a NULL pointer as a 0-length array. +// +// float *my_array = NULL, *temp; +// +// // add elements on the end one at a time +// stb_arr_push(my_array, 0.0f); +// stb_arr_push(my_array, 1.0f); +// stb_arr_push(my_array, 2.0f); +// +// assert(my_array[1] == 2.0f); +// +// // add an uninitialized element at the end, then assign it +// *stb_arr_add(my_array) = 3.0f; +// +// // add three uninitialized elements at the end +// temp = stb_arr_addn(my_array,3); +// temp[0] = 4.0f; +// temp[1] = 5.0f; +// temp[2] = 6.0f; +// +// assert(my_array[5] == 5.0f); +// +// // remove the last one +// stb_arr_pop(my_array); +// +// assert(stb_arr_len(my_array) == 6); + + +#ifdef STB_MALLOC_WRAPPER + #define STB__PARAMS , char *file, int line + #define STB__ARGS , file, line +#else + #define STB__PARAMS + #define STB__ARGS +#endif + +// calling this function allocates an empty stb_arr attached to p +// (whereas NULL isn't attached to anything) +STB_EXTERN void stb_arr_malloc(void **target, void *context); + +// call this function with a non-NULL value to have all successive +// stbs that are created be attached to the associated parent. Note +// that once a given stb_arr is non-empty, it stays attached to its +// current parent, even if you call this function again. +// it turns the previous value, so you can restore it +STB_EXTERN void* stb_arr_malloc_parent(void *p); + +// simple functions written on top of other functions +#define stb_arr_empty(a) ( stb_arr_len(a) == 0 ) +#define stb_arr_add(a) ( stb_arr_addn((a),1) ) +#define stb_arr_push(a,v) ( *stb_arr_add(a)=(v) ) + +typedef struct +{ + int len, limit; + int stb_malloc; + unsigned int signature; +} stb__arr; + +#define stb_arr_signature 0x51bada7b // ends with 0123 in decimal + +// access the header block stored before the data +#define stb_arrhead(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) +#define stb_arrhead2(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) + +#ifdef STB_DEBUG +#define stb_arr_check(a) assert(!a || stb_arrhead(a)->signature == stb_arr_signature) +#define stb_arr_check2(a) assert(!a || stb_arrhead2(a)->signature == stb_arr_signature) +#else +#define stb_arr_check(a) ((void) 0) +#define stb_arr_check2(a) ((void) 0) +#endif + +// ARRAY LENGTH + +// get the array length; special case if pointer is NULL +#define stb_arr_len(a) (a ? stb_arrhead(a)->len : 0) +#define stb_arr_len2(a) ((stb__arr *) (a) ? stb_arrhead2(a)->len : 0) +#define stb_arr_lastn(a) (stb_arr_len(a)-1) + +// check whether a given index is valid -- tests 0 <= i < stb_arr_len(a) +#define stb_arr_valid(a,i) (a ? (int) (i) < stb_arrhead(a)->len : 0) + +// change the array length so is is exactly N entries long, creating +// uninitialized entries as needed +#define stb_arr_setlen(a,n) \ + (stb__arr_setlen((void **) &(a), sizeof(a[0]), (n))) + +// change the array length so that N is a valid index (that is, so +// it is at least N entries long), creating uninitialized entries as needed +#define stb_arr_makevalid(a,n) \ + (stb_arr_len(a) < (n)+1 ? stb_arr_setlen(a,(n)+1),(a) : (a)) + +// remove the last element of the array, returning it +#define stb_arr_pop(a) ((stb_arr_check(a), (a))[--stb_arrhead(a)->len]) + +// access the last element in the array +#define stb_arr_last(a) ((stb_arr_check(a), (a))[stb_arr_len(a)-1]) + +// is iterator at end of list? +#define stb_arr_end(a,i) ((i) >= &(a)[stb_arr_len(a)]) + +// (internal) change the allocated length of the array +#define stb_arr__grow(a,n) (stb_arr_check(a), stb_arrhead(a)->len += (n)) + +// add N new unitialized elements to the end of the array +#define stb_arr__addn(a,n) /*lint --e(826)*/ \ + ((stb_arr_len(a)+(n) > stb_arrcurmax(a)) \ + ? (stb__arr_addlen((void **) &(a),sizeof(*a),(n)),0) \ + : ((stb_arr__grow(a,n), 0))) + +// add N new unitialized elements to the end of the array, and return +// a pointer to the first new one +#define stb_arr_addn(a,n) (stb_arr__addn((a),n),(a)+stb_arr_len(a)-(n)) + +// add N new uninitialized elements starting at index 'i' +#define stb_arr_insertn(a,i,n) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n)) + +// insert an element at i +#define stb_arr_insert(a,i,v) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n), ((a)[i] = v)) + +// delete N elements from the middle starting at index 'i' +#define stb_arr_deleten(a,i,n) (stb__arr_deleten((void **) &(a), sizeof(*a), i, n)) + +// delete the i'th element +#define stb_arr_delete(a,i) stb_arr_deleten(a,i,1) + +// delete the i'th element, swapping down from the end +#define stb_arr_fastdelete(a,i) \ + (stb_swap(&a[i], &a[stb_arrhead(a)->len-1], sizeof(*a)), stb_arr_pop(a)) + + +// ARRAY STORAGE + +// get the array maximum storage; special case if NULL +#define stb_arrcurmax(a) (a ? stb_arrhead(a)->limit : 0) +#define stb_arrcurmax2(a) (a ? stb_arrhead2(a)->limit : 0) + +// set the maxlength of the array to n in anticipation of further growth +#define stb_arr_setsize(a,n) (stb_arr_check(a), stb__arr_setsize((void **) &(a),sizeof((a)[0]),n)) + +// make sure maxlength is large enough for at least N new allocations +#define stb_arr_atleast(a,n) (stb_arr_len(a)+(n) > stb_arrcurmax(a) \ + ? stb_arr_setsize((a), (n)) : 0) + +// make a copy of a given array (copies contents via 'memcpy'!) +#define stb_arr_copy(a) stb__arr_copy(a, sizeof((a)[0])) + +// compute the storage needed to store all the elements of the array +#define stb_arr_storage(a) (stb_arr_len(a) * sizeof((a)[0])) + +#define stb_arr_for(v,arr) for((v)=(arr); (v) < (arr)+stb_arr_len(arr); ++(v)) + +// IMPLEMENTATION + +STB_EXTERN void stb_arr_free_(void **p); +STB_EXTERN void *stb__arr_copy_(void *p, int elem_size); +STB_EXTERN void stb__arr_setsize_(void **p, int size, int limit STB__PARAMS); +STB_EXTERN void stb__arr_setlen_(void **p, int size, int newlen STB__PARAMS); +STB_EXTERN void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS); +STB_EXTERN void stb__arr_deleten_(void **p, int size, int loc, int n STB__PARAMS); +STB_EXTERN void stb__arr_insertn_(void **p, int size, int loc, int n STB__PARAMS); + +#define stb_arr_free(p) stb_arr_free_((void **) &(p)) +#define stb__arr_copy stb__arr_copy_ + +#ifndef STB_MALLOC_WRAPPER + #define stb__arr_setsize stb__arr_setsize_ + #define stb__arr_setlen stb__arr_setlen_ + #define stb__arr_addlen stb__arr_addlen_ + #define stb__arr_deleten stb__arr_deleten_ + #define stb__arr_insertn stb__arr_insertn_ +#else + #define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__) + #define stb__arr_deleten(p,s,i,n) stb__arr_deleten_(p,s,i,n,__FILE__,__LINE__) + #define stb__arr_insertn(p,s,i,n) stb__arr_insertn_(p,s,i,n,__FILE__,__LINE__) +#endif + +#ifdef STB_DEFINE +static void *stb__arr_context; + +void *stb_arr_malloc_parent(void *p) +{ + void *q = stb__arr_context; + stb__arr_context = p; + return q; +} + +void stb_arr_malloc(void **target, void *context) +{ + stb__arr *q = (stb__arr *) stb_malloc(context, sizeof(*q)); + q->len = q->limit = 0; + q->stb_malloc = 1; + q->signature = stb_arr_signature; + *target = (void *) (q+1); +} + +static void * stb__arr_malloc(int size) +{ + if (stb__arr_context) + return stb_malloc(stb__arr_context, size); + return malloc(size); +} + +void * stb__arr_copy_(void *p, int elem_size) +{ + stb__arr *q; + if (p == NULL) return p; + q = (stb__arr *) stb__arr_malloc(sizeof(*q) + elem_size * stb_arrhead2(p)->limit); + stb_arr_check2(p); + memcpy(q, stb_arrhead2(p), sizeof(*q) + elem_size * stb_arrhead2(p)->len); + q->stb_malloc = !!stb__arr_context; + return q+1; +} + +void stb_arr_free_(void **pp) +{ + void *p = *pp; + stb_arr_check2(p); + if (p) { + stb__arr *q = stb_arrhead2(p); + if (q->stb_malloc) + stb_free(q); + else + free(q); + } + *pp = NULL; +} + +static void stb__arrsize_(void **pp, int size, int limit, int len STB__PARAMS) +{ + void *p = *pp; + stb__arr *a; + stb_arr_check2(p); + if (p == NULL) { + if (len == 0 && size == 0) return; + a = (stb__arr *) stb__arr_malloc(sizeof(*a) + size*limit); + a->limit = limit; + a->len = len; + a->stb_malloc = !!stb__arr_context; + a->signature = stb_arr_signature; + } else { + a = stb_arrhead2(p); + a->len = len; + if (a->limit < limit) { + void *p; + if (a->limit >= 4 && limit < a->limit * 2) + limit = a->limit * 2; + if (a->stb_malloc) + p = stb_realloc(a, sizeof(*a) + limit*size); + else + #ifdef STB_MALLOC_WRAPPER + p = stb__realloc(a, sizeof(*a) + limit*size, file, line); + #else + p = realloc(a, sizeof(*a) + limit*size); + #endif + if (p) { + a = (stb__arr *) p; + a->limit = limit; + } else { + // throw an error! + } + } + } + a->len = stb_min(a->len, a->limit); + *pp = a+1; +} + +void stb__arr_setsize_(void **pp, int size, int limit STB__PARAMS) +{ + void *p = *pp; + stb_arr_check2(p); + stb__arrsize_(pp, size, limit, stb_arr_len2(p) STB__ARGS); +} + +void stb__arr_setlen_(void **pp, int size, int newlen STB__PARAMS) +{ + void *p = *pp; + stb_arr_check2(p); + if (stb_arrcurmax2(p) < newlen || p == NULL) { + stb__arrsize_(pp, size, newlen, newlen STB__ARGS); + } else { + stb_arrhead2(p)->len = newlen; + } +} + +void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS) +{ + stb__arr_setlen_(p, size, stb_arr_len2(*p) + addlen STB__ARGS); +} + +void stb__arr_insertn_(void **pp, int size, int i, int n STB__PARAMS) +{ + void *p = *pp; + if (n) { + int z; + + if (p == NULL) { + stb__arr_addlen_(pp, size, n STB__ARGS); + return; + } + + z = stb_arr_len2(p); + stb__arr_addlen_(&p, size, i STB__ARGS); + memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i)); + } + *pp = p; +} + +void stb__arr_deleten_(void **pp, int size, int i, int n STB__PARAMS) +{ + void *p = *pp; + if (n) { + memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-i)); + stb_arrhead2(p)->len -= n; + } + *pp = p; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Hashing +// +// typical use for this is to make a power-of-two hash table. +// +// let N = size of table (2^n) +// let H = stb_hash(str) +// let S = stb_rehash(H) | 1 +// +// then hash probe sequence P(i) for i=0..N-1 +// P(i) = (H + S*i) & (N-1) +// +// the idea is that H has 32 bits of hash information, but the +// table has only, say, 2^20 entries so only uses 20 of the bits. +// then by rehashing the original H we get 2^12 different probe +// sequences for a given initial probe location. (So it's optimal +// for 64K tables and its optimality decreases past that.) +// +// ok, so I've added something that generates _two separate_ +// 32-bit hashes simultaneously which should scale better to +// very large tables. + + +STB_EXTERN unsigned int stb_hash(char *str); +STB_EXTERN unsigned int stb_hashptr(void *p); +STB_EXTERN unsigned int stb_hashlen(char *str, int len); +STB_EXTERN unsigned int stb_rehash_improved(unsigned int v); +STB_EXTERN unsigned int stb_hash_fast(void *p, int len); +STB_EXTERN unsigned int stb_hash2(char *str, unsigned int *hash2_ptr); +STB_EXTERN unsigned int stb_hash_number(unsigned int hash); + +#define stb_rehash(x) ((x) + ((x) >> 6) + ((x) >> 19)) + +#ifdef STB_DEFINE +unsigned int stb_hash(char *str) +{ + unsigned int hash = 0; + while (*str) + hash = (hash << 7) + (hash >> 25) + *str++; + return hash + (hash >> 16); +} + +unsigned int stb_hashlen(char *str, int len) +{ + unsigned int hash = 0; + while (len-- > 0 && *str) + hash = (hash << 7) + (hash >> 25) + *str++; + return hash + (hash >> 16); +} + +unsigned int stb_hashptr(void *p) +{ + unsigned int x = (unsigned int) p; + + // typically lacking in low bits and high bits + x = stb_rehash(x); + x += x << 16; + + // pearson's shuffle + x ^= x << 3; + x += x >> 5; + x ^= x << 2; + x += x >> 15; + x ^= x << 10; + return stb_rehash(x); +} + +unsigned int stb_rehash_improved(unsigned int v) +{ + return stb_hashptr((void *)(size_t) v); +} + +unsigned int stb_hash2(char *str, unsigned int *hash2_ptr) +{ + unsigned int hash1 = 0x3141592c; + unsigned int hash2 = 0x77f044ed; + while (*str) { + hash1 = (hash1 << 7) + (hash1 >> 25) + *str; + hash2 = (hash2 << 11) + (hash2 >> 21) + *str; + ++str; + } + *hash2_ptr = hash2 + (hash1 >> 16); + return hash1 + (hash2 >> 16); +} + +// Paul Hsieh hash +#define stb__get16_slow(p) ((p)[0] + ((p)[1] << 8)) +#if defined(_MSC_VER) + #define stb__get16(p) (*((unsigned short *) (p))) +#else + #define stb__get16(p) stb__get16_slow(p) +#endif + +unsigned int stb_hash_fast(void *p, int len) +{ + unsigned char *q = (unsigned char *) p; + unsigned int hash = len; + + if (len <= 0 || q == NULL) return 0; + + /* Main loop */ + if (((int) q & 1) == 0) { + for (;len > 3; len -= 4) { + unsigned int val; + hash += stb__get16(q); + val = (stb__get16(q+2) << 11); + hash = (hash << 16) ^ hash ^ val; + q += 4; + hash += hash >> 11; + } + } else { + for (;len > 3; len -= 4) { + unsigned int val; + hash += stb__get16_slow(q); + val = (stb__get16_slow(q+2) << 11); + hash = (hash << 16) ^ hash ^ val; + q += 4; + hash += hash >> 11; + } + } + + /* Handle end cases */ + switch (len) { + case 3: hash += stb__get16_slow(q); + hash ^= hash << 16; + hash ^= q[2] << 18; + hash += hash >> 11; + break; + case 2: hash += stb__get16_slow(q); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += q[0]; + hash ^= hash << 10; + hash += hash >> 1; + break; + case 0: break; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +unsigned int stb_hash_number(unsigned int hash) +{ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Perfect hashing for ints/pointers +// +// This is mainly useful for making faster pointer-indexed tables +// that don't change frequently. E.g. for stb_ischar(). +// + +typedef struct +{ + stb_uint32 addend; + stb_uint multiplicand; + stb_uint b_mask; + stb_uint8 small_bmap[16]; + stb_uint16 *large_bmap; + + stb_uint table_mask; + stb_uint32 *table; +} stb_perfect; + +STB_EXTERN int stb_perfect_create(stb_perfect *,unsigned int*,int n); +STB_EXTERN void stb_perfect_destroy(stb_perfect *); +STB_EXTERN int stb_perfect_hash(stb_perfect *, unsigned int x); +extern int stb_perfect_hash_max_failures; + +#ifdef STB_DEFINE + +int stb_perfect_hash_max_failures; + +int stb_perfect_hash(stb_perfect *p, unsigned int x) +{ + stb_uint m = x * p->multiplicand; + stb_uint y = x >> 16; + stb_uint bv = (m >> 24) + y; + stb_uint av = (m + y) >> 12; + if (p->table == NULL) return -1; // uninitialized table fails + bv &= p->b_mask; + av &= p->table_mask; + if (p->large_bmap) + av ^= p->large_bmap[bv]; + else + av ^= p->small_bmap[bv]; + return p->table[av] == x ? av : -1; +} + +static void stb__perfect_prehash(stb_perfect *p, stb_uint x, stb_uint16 *a, stb_uint16 *b) +{ + stb_uint m = x * p->multiplicand; + stb_uint y = x >> 16; + stb_uint bv = (m >> 24) + y; + stb_uint av = (m + y) >> 12; + bv &= p->b_mask; + av &= p->table_mask; + *b = bv; + *a = av; +} + +static unsigned long stb__perfect_rand(void) +{ + static unsigned long stb__rand; + stb__rand = stb__rand * 2147001325 + 715136305; + return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); +} + +typedef struct { + unsigned short count; + unsigned short b; + unsigned short map; + unsigned short *entries; +} stb__slot; + +static int stb__slot_compare(const void *p, const void *q) +{ + stb__slot *a = (stb__slot *) p; + stb__slot *b = (stb__slot *) q; + return a->count > b->count ? -1 : a->count < b->count; // sort large to small +} + +int stb_perfect_create(stb_perfect *p, unsigned int *v, int n) +{ + unsigned int buffer1[64], buffer2[64], buffer3[64], buffer4[64], buffer5[32]; + unsigned short *as = (unsigned short *) stb_temp(buffer1, sizeof(*v)*n); + unsigned short *bs = (unsigned short *) stb_temp(buffer2, sizeof(*v)*n); + unsigned short *entries = (unsigned short *) stb_temp(buffer4, sizeof(*entries) * n); + int size = 1 << stb_log2_ceil(n), bsize=8; + int failure = 0,i,j,k; + + assert(n <= 32768); + p->large_bmap = NULL; + + for(;;) { + stb__slot *bcount = (stb__slot *) stb_temp(buffer3, sizeof(*bcount) * bsize); + unsigned short *bloc = (unsigned short *) stb_temp(buffer5, sizeof(*bloc) * bsize); + unsigned short *e; + int bad=0; + + p->addend = stb__perfect_rand(); + p->multiplicand = stb__perfect_rand() | 1; + p->table_mask = size-1; + p->b_mask = bsize-1; + p->table = (stb_uint32 *) malloc(size * sizeof(*p->table)); + + for (i=0; i < bsize; ++i) { + bcount[i].b = i; + bcount[i].count = 0; + bcount[i].map = 0; + } + for (i=0; i < n; ++i) { + stb__perfect_prehash(p, v[i], as+i, bs+i); + ++bcount[bs[i]].count; + } + qsort(bcount, bsize, sizeof(*bcount), stb__slot_compare); + e = entries; // now setup up their entries index + for (i=0; i < bsize; ++i) { + bcount[i].entries = e; + e += bcount[i].count; + bcount[i].count = 0; + bloc[bcount[i].b] = i; + } + // now fill them out + for (i=0; i < n; ++i) { + int b = bs[i]; + int w = bloc[b]; + bcount[w].entries[bcount[w].count++] = i; + } + stb_tempfree(buffer5,bloc); + // verify + for (i=0; i < bsize; ++i) + for (j=0; j < bcount[i].count; ++j) + assert(bs[bcount[i].entries[j]] == bcount[i].b); + memset(p->table, 0, size*sizeof(*p->table)); + + // check if any b has duplicate a + for (i=0; i < bsize; ++i) { + if (bcount[i].count > 1) { + for (j=0; j < bcount[i].count; ++j) { + if (p->table[as[bcount[i].entries[j]]]) + bad = 1; + p->table[as[bcount[i].entries[j]]] = 1; + } + for (j=0; j < bcount[i].count; ++j) { + p->table[as[bcount[i].entries[j]]] = 0; + } + if (bad) break; + } + } + + if (!bad) { + // go through the bs and populate the table, first fit + for (i=0; i < bsize; ++i) { + if (bcount[i].count) { + // go through the candidate table[b] values + for (j=0; j < size; ++j) { + // go through the a values and see if they fit + for (k=0; k < bcount[i].count; ++k) { + int a = as[bcount[i].entries[k]]; + if (p->table[(a^j)&p->table_mask]) { + break; // fails + } + } + // if succeeded, accept + if (k == bcount[i].count) { + bcount[i].map = j; + for (k=0; k < bcount[i].count; ++k) { + int a = as[bcount[i].entries[k]]; + p->table[(a^j)&p->table_mask] = 1; + } + break; + } + } + if (j == size) + break; // no match for i'th entry, so break out in failure + } + } + if (i == bsize) { + // success... fill out map + if (bsize <= 16 && size <= 256) { + p->large_bmap = NULL; + for (i=0; i < bsize; ++i) + p->small_bmap[bcount[i].b] = (stb_uint8) bcount[i].map; + } else { + p->large_bmap = (unsigned short *) malloc(sizeof(*p->large_bmap) * bsize); + for (i=0; i < bsize; ++i) + p->large_bmap[bcount[i].b] = bcount[i].map; + } + + // initialize table to v[0], so empty slots will fail + for (i=0; i < size; ++i) + p->table[i] = v[0]; + + for (i=0; i < n; ++i) + if (p->large_bmap) + p->table[as[i] ^ p->large_bmap[bs[i]]] = v[i]; + else + p->table[as[i] ^ p->small_bmap[bs[i]]] = v[i]; + + // and now validate that none of them collided + for (i=0; i < n; ++i) + assert(stb_perfect_hash(p, v[i]) >= 0); + + stb_tempfree(buffer3, bcount); + break; + } + } + free(p->table); + p->table = NULL; + stb_tempfree(buffer3, bcount); + + ++failure; + if (failure >= 4 && bsize < size) bsize *= 2; + if (failure >= 8 && (failure & 3) == 0 && size < 4*n) { + size *= 2; + bsize *= 2; + } + if (failure == 6) { + // make sure the input data is unique, so we don't infinite loop + unsigned int *data = (unsigned int *) stb_temp(buffer3, n * sizeof(*data)); + memcpy(data, v, sizeof(*data) * n); + qsort(data, n, sizeof(*data), stb_intcmp(0)); + for (i=1; i < n; ++i) { + if (data[i] == data[i-1]) + size = 0; // size is return value, so 0 it + } + stb_tempfree(buffer3, data); + if (!size) break; + } + } + + if (failure > stb_perfect_hash_max_failures) + stb_perfect_hash_max_failures = failure; + + stb_tempfree(buffer1, as); + stb_tempfree(buffer2, bs); + stb_tempfree(buffer4, entries); + + return size; +} + +void stb_perfect_destroy(stb_perfect *p) +{ + if (p->large_bmap) free(p->large_bmap); + if (p->table ) free(p->table); + p->large_bmap = NULL; + p->table = NULL; + p->b_mask = 0; + p->table_mask = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Perfect hash clients + +STB_EXTERN int stb_ischar(char s, char *set); + +#ifdef STB_DEFINE + +int stb_ischar(char c, char *set) +{ + static unsigned char bit[8] = { 1,2,4,8,16,32,64,128 }; + static stb_perfect p; + static unsigned char (*tables)[256]; + static char ** sets = NULL; + + int z = stb_perfect_hash(&p, (int) set); + if (z < 0) { + int i,k,n,j,f; + // special code that means free all existing data + if (set == NULL) { + stb_arr_free(sets); + free(tables); + tables = NULL; + stb_perfect_destroy(&p); + return 0; + } + stb_arr_push(sets, set); + stb_perfect_destroy(&p); + n = stb_perfect_create(&p, (unsigned int *) (char **) sets, stb_arr_len(sets)); + assert(n != 0); + k = (n+7) >> 3; + tables = (unsigned char (*)[256]) realloc(tables, sizeof(*tables) * k); + memset(tables, 0, sizeof(*tables) * k); + for (i=0; i < stb_arr_len(sets); ++i) { + k = stb_perfect_hash(&p, (int) sets[i]); + assert(k >= 0); + n = k >> 3; + f = bit[k&7]; + for (j=0; !j || sets[i][j]; ++j) { + tables[n][(unsigned char) sets[i][j]] |= f; + } + } + z = stb_perfect_hash(&p, (int) set); + } + return tables[z >> 3][(unsigned char) c] & bit[z & 7]; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Instantiated data structures +// +// This is an attempt to implement a templated data structure. +// +// Hash table: call stb_define_hash(TYPE,N,KEY,K1,K2,HASH,VALUE) +// TYPE -- will define a structure type containing the hash table +// N -- the name, will prefix functions named: +// N create +// N destroy +// N get +// N set, N add, N update, +// N remove +// KEY -- the type of the key. 'x == y' must be valid +// K1,K2 -- keys never used by the app, used as flags in the hashtable +// HASH -- a piece of code ending with 'return' that hashes key 'k' +// VALUE -- the type of the value. 'x = y' must be valid +// +// Note that stb_define_hash_base can be used to define more sophisticated +// hash tables, e.g. those that make copies of the key or use special +// comparisons (e.g. strcmp). + +#define STB_(prefix,name) stb__##prefix##name +#define STB__(prefix,name) prefix##name +#define STB__use(x) x +#define STB__skip(x) + +#define stb_declare_hash(PREFIX,TYPE,N,KEY,VALUE) \ + typedef struct stb__st_##TYPE TYPE;\ + PREFIX int STB__(N, init)(TYPE *h, int count);\ + PREFIX int STB__(N, memory_usage)(TYPE *h);\ + PREFIX TYPE * STB__(N, create)(void);\ + PREFIX TYPE * STB__(N, copy)(TYPE *h);\ + PREFIX void STB__(N, destroy)(TYPE *h);\ + PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v);\ + PREFIX VALUE STB__(N,get)(TYPE *a, KEY k);\ + PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v);\ + PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v);\ + PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v);\ + PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v); + +#define STB_nocopy(x) (x) +#define STB_nodelete(x) 0 +#define STB_nofields +#define STB_nonullvalue(x) +#define STB_nullvalue(x) x +#define STB_safecompare(x) x +#define STB_nosafe(x) +#define STB_noprefix + +#ifdef __GNUC__ +#define STB__nogcc(x) +#else +#define STB__nogcc(x) x +#endif + +#define stb_define_hash_base(PREFIX,TYPE,FIELDS,N,NC,LOAD_FACTOR, \ + KEY,EMPTY,DEL,COPY,DISPOSE,SAFE, \ + VCOMPARE,CCOMPARE,HASH, \ + VALUE,HASVNULL,VNULL) \ + \ +typedef struct \ +{ \ + KEY k; \ + VALUE v; \ +} STB_(N,_hashpair); \ + \ +STB__nogcc( typedef struct stb__st_##TYPE TYPE; ) \ +struct stb__st_##TYPE { \ + FIELDS \ + STB_(N,_hashpair) *table; \ + unsigned int mask; \ + int count, limit; \ + int deleted; \ + \ + int delete_threshhold; \ + int grow_threshhold; \ + int shrink_threshhold; \ + unsigned char alloced, has_empty, has_del; \ + VALUE ev; VALUE dv; \ +}; \ + \ +static unsigned int STB_(N, hash)(KEY k) \ +{ \ + HASH \ +} \ + \ +PREFIX int STB__(N, init)(TYPE *h, int count) \ +{ \ + int i; \ + if (count < 4) count = 4; \ + h->limit = count; \ + h->count = 0; \ + h->mask = count-1; \ + h->deleted = 0; \ + h->grow_threshhold = (int) (count * LOAD_FACTOR); \ + h->has_empty = h->has_del = 0; \ + h->alloced = 0; \ + if (count <= 64) \ + h->shrink_threshhold = 0; \ + else \ + h->shrink_threshhold = (int) (count * (LOAD_FACTOR/2.25)); \ + h->delete_threshhold = (int) (count * (1-LOAD_FACTOR)/2); \ + h->table = (STB_(N,_hashpair)*) malloc(sizeof(h->table[0]) * count); \ + if (h->table == NULL) return 0; \ + /* ideally this gets turned into a memset32 automatically */ \ + for (i=0; i < count; ++i) \ + h->table[i].k = EMPTY; \ + return 1; \ +} \ + \ +PREFIX int STB__(N, memory_usage)(TYPE *h) \ +{ \ + return sizeof(*h) + h->limit * sizeof(h->table[0]); \ +} \ + \ +PREFIX TYPE * STB__(N, create)(void) \ +{ \ + TYPE *h = (TYPE *) malloc(sizeof(*h)); \ + if (h) { \ + if (STB__(N, init)(h, 16)) \ + h->alloced = 1; \ + else { free(h); h=NULL; } \ + } \ + return h; \ +} \ + \ +PREFIX void STB__(N, destroy)(TYPE *a) \ +{ \ + int i; \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k, DEL)) \ + DISPOSE(a->table[i].k); \ + free(a->table); \ + if (a->alloced) \ + free(a); \ +} \ + \ +static void STB_(N, rehash)(TYPE *a, int count); \ + \ +PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)){ if (a->has_empty) *v = a->ev; return a->has_empty;}\ + if (CCOMPARE(k,DEL)) { if (a->has_del ) *v = a->dv; return a->has_del; }\ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ + if (VCOMPARE(a->table[n].k,k)) { *v = a->table[n].v; return 1; } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) \ + { *v = a->table[n].v; return 1; } \ + } \ +} \ + \ +HASVNULL( \ + PREFIX VALUE STB__(N,get)(TYPE *a, KEY k) \ + { \ + VALUE v; \ + if (STB__(N,get_flag)(a,k,&v)) return v; \ + else return VNULL; \ + } \ +) \ + \ +PREFIX int STB__(N,getkey)(TYPE *a, KEY k, KEY *kout) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)||CCOMPARE(k,DEL)) return 0; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ + if (VCOMPARE(a->table[n].k,k)) { *kout = a->table[n].k; return 1; } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) \ + { *kout = a->table[n].k; return 1; } \ + } \ +} \ + \ +static int STB_(N,addset)(TYPE *a, KEY k, VALUE v, \ + int allow_new, int allow_old, int copy) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask; \ + int b = -1; \ + if (CCOMPARE(k,EMPTY)) { \ + if (a->has_empty ? allow_old : allow_new) { \ + n=a->has_empty; a->ev = v; a->has_empty = 1; return !n; \ + } else return 0; \ + } \ + if (CCOMPARE(k,DEL)) { \ + if (a->has_del ? allow_old : allow_new) { \ + n=a->has_del; a->dv = v; a->has_del = 1; return !n; \ + } else return 0; \ + } \ + if (!CCOMPARE(a->table[n].k, EMPTY)) { \ + unsigned int s; \ + if (CCOMPARE(a->table[n].k, DEL)) \ + b = n; \ + else if (VCOMPARE(a->table[n].k,k)) { \ + if (allow_old) \ + a->table[n].v = v; \ + return !allow_new; \ + } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k, EMPTY)) break; \ + if (CCOMPARE(a->table[n].k, DEL)) { \ + if (b < 0) b = n; \ + } else if (VCOMPARE(a->table[n].k,k)) { \ + if (allow_old) \ + a->table[n].v = v; \ + return !allow_new; \ + } \ + } \ + } \ + if (!allow_new) return 0; \ + if (b < 0) b = n; else --a->deleted; \ + a->table[b].k = copy ? COPY(k) : k; \ + a->table[b].v = v; \ + ++a->count; \ + if (a->count > a->grow_threshhold) \ + STB_(N,rehash)(a, a->limit*2); \ + return 1; \ +} \ + \ +PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,1,1);}\ +PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,0,1);}\ +PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v){return STB_(N,addset)(a,k,v,0,1,1);}\ + \ +PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)) { if (a->has_empty) { if(v)*v = a->ev; a->has_empty=0; return 1; } return 0; } \ + if (CCOMPARE(k,DEL)) { if (a->has_del ) { if(v)*v = a->dv; a->has_del =0; return 1; } return 0; } \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + if (SAFE(CCOMPARE(a->table[n].k,DEL) || ) !VCOMPARE(a->table[n].k,k)) { \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k, DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) break; \ + } \ + } \ + DISPOSE(a->table[n].k); \ + a->table[n].k = DEL; \ + --a->count; \ + ++a->deleted; \ + if (v != NULL) \ + *v = a->table[n].v; \ + if (a->count < a->shrink_threshhold) \ + STB_(N, rehash)(a, a->limit >> 1); \ + else if (a->deleted > a->delete_threshhold) \ + STB_(N, rehash)(a, a->limit); \ + return 1; \ +} \ + \ +PREFIX TYPE * STB__(NC, copy)(TYPE *a) \ +{ \ + int i; \ + TYPE *h = (TYPE *) malloc(sizeof(*h)); \ + if (!h) return NULL; \ + if (!STB__(N, init)(h, a->limit)) { free(h); return NULL; } \ + h->count = a->count; \ + h->deleted = a->deleted; \ + h->alloced = 1; \ + h->ev = a->ev; h->dv = a->dv; \ + h->has_empty = a->has_empty; h->has_del = a->has_del; \ + memcpy(h->table, a->table, h->limit * sizeof(h->table[0])); \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(h->table[i].k,EMPTY) && !CCOMPARE(h->table[i].k,DEL)) \ + h->table[i].k = COPY(h->table[i].k); \ + return h; \ +} \ + \ +static void STB_(N, rehash)(TYPE *a, int count) \ +{ \ + int i; \ + TYPE b; \ + STB__(N, init)(&b, count); \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k,DEL)) \ + STB_(N,addset)(&b, a->table[i].k, a->table[i].v,1,1,0); \ + free(a->table); \ + a->table = b.table; \ + a->mask = b.mask; \ + a->count = b.count; \ + a->limit = b.limit; \ + a->deleted = b.deleted; \ + a->delete_threshhold = b.delete_threshhold; \ + a->grow_threshhold = b.grow_threshhold; \ + a->shrink_threshhold = b.shrink_threshhold; \ +} + +#define STB_equal(a,b) ((a) == (b)) + +#define stb_define_hash(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE) \ + stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ + KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ + STB_equal,STB_equal,HASH, \ + VALUE,STB_nonullvalue,0) + +#define stb_define_hash_vnull(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE,VNULL) \ + stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ + KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ + STB_equal,STB_equal,HASH, \ + VALUE,STB_nullvalue,VNULL) + +////////////////////////////////////////////////////////////////////////////// +// +// stb_ptrmap +// +// An stb_ptrmap data structure is an O(1) hash table between pointers. One +// application is to let you store "extra" data associated with pointers, +// which is why it was originally called stb_extra. + +stb_declare_hash(STB_EXTERN, stb_ptrmap, stb_ptrmap_, void *, void *) +stb_declare_hash(STB_EXTERN, stb_idict, stb_idict_, stb_int32, stb_int32) + +STB_EXTERN void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)); +STB_EXTERN stb_ptrmap *stb_ptrmap_new(void); + +STB_EXTERN stb_idict * stb_idict_new_size(int size); +STB_EXTERN void stb_idict_remove_all(stb_idict *e); + +#ifdef STB_DEFINE + +#define STB_EMPTY ((void *) 2) +#define STB_EDEL ((void *) 6) + +stb_define_hash_base(STB_noprefix,stb_ptrmap, STB_nofields, stb_ptrmap_,stb_ptrmap_,0.85f, + void *,STB_EMPTY,STB_EDEL,STB_nocopy,STB_nodelete,STB_nosafe, + STB_equal,STB_equal,return stb_hashptr(k);, + void *,STB_nullvalue,NULL) + +stb_ptrmap *stb_ptrmap_new(void) +{ + return stb_ptrmap_create(); +} + +void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)) +{ + int i; + if (free_func) + for (i=0; i < e->limit; ++i) + if (e->table[i].k != STB_EMPTY && e->table[i].k != STB_EDEL) { + if (free_func == free) + free(e->table[i].v); // allow STB_MALLOC_WRAPPER to operate + else + free_func(e->table[i].v); + } + stb_ptrmap_destroy(e); +} + +// extra fields needed for stua_dict +#define STB_IEMPTY ((int) 1) +#define STB_IDEL ((int) 3) +stb_define_hash_base(STB_noprefix, stb_idict, short type; short gc; STB_nofields, stb_idict_,stb_idict_,0.85f, + stb_int32,STB_IEMPTY,STB_IDEL,STB_nocopy,STB_nodelete,STB_nosafe, + STB_equal,STB_equal, + return stb_rehash_improved(k);,stb_int32,STB_nonullvalue,0) + +stb_idict * stb_idict_new_size(int size) +{ + stb_idict *e = (stb_idict *) malloc(sizeof(*e)); + if (e) { + if (!stb_is_pow2(size)) + size = 1 << stb_log2_ceil(size); + stb_idict_init(e, size); + e->alloced = 1; + } + return e; +} + +void stb_idict_remove_all(stb_idict *e) +{ + int n; + for (n=0; n < e->limit; ++n) + e->table[n].k = STB_IEMPTY; + e->has_empty = e->has_del = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_sparse_ptr_matrix +// +// An stb_ptrmap data structure is an O(1) hash table storing an arbitrary +// block of data for a given pair of pointers. +// +// If create=0, returns + +typedef struct stb__st_stb_spmatrix stb_spmatrix; + +STB_EXTERN stb_spmatrix * stb_sparse_ptr_matrix_new(int val_size); +STB_EXTERN void stb_sparse_ptr_matrix_free(stb_spmatrix *z); +STB_EXTERN void * stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create); + +#ifdef STB_DEFINE +typedef struct +{ + void *a; + void *b; +} stb__ptrpair; + +static stb__ptrpair stb__ptrpair_empty = { (void *) 1, (void *) 1 }; +static stb__ptrpair stb__ptrpair_del = { (void *) 2, (void *) 2 }; + +#define STB__equal_ptrpair(x,y) ((x).a == (y).a && (x).b == (y).b) + +stb_define_hash_base(static, stb_spmatrix, int val_size; void *arena;, stb__spmatrix_,stb__spmatrix_, 0.85, + stb__ptrpair, stb__ptrpair_empty, stb__ptrpair_del, + STB_nocopy, STB_nodelete, STB_nosafe, + STB__equal_ptrpair, STB__equal_ptrpair, return stb_rehash(stb_hashptr(k.a))+stb_hashptr(k.b);, + void *, STB_nullvalue, 0) + +stb_spmatrix *stb_sparse_ptr_matrix_new(int val_size) +{ + stb_spmatrix *m = stb__spmatrix_create(); + if (m) m->val_size = val_size; + if (m) m->arena = stb_malloc_global(1); + return m; +} + +void stb_sparse_ptr_matrix_free(stb_spmatrix *z) +{ + if (z->arena) stb_free(z->arena); + stb__spmatrix_destroy(z); +} + +void *stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create) +{ + stb__ptrpair t = { a,b }; + void *data = stb__spmatrix_get(z, t); + if (!data && create) { + data = stb_malloc_raw(z->arena, z->val_size); + if (!data) return NULL; + memset(data, 0, z->val_size); + stb__spmatrix_add(z, t, data); + } + return data; +} +#endif + + + +////////////////////////////////////////////////////////////////////////////// +// +// SDICT: Hash Table for Strings (symbol table) +// +// if "use_arena=1", then strings will be copied +// into blocks and never freed until the sdict is freed; +// otherwise they're malloc()ed and free()d on the fly. +// (specify use_arena=1 if you never stb_sdict_remove) + +stb_declare_hash(STB_EXTERN, stb_sdict, stb_sdict_, char *, void *) + +STB_EXTERN stb_sdict * stb_sdict_new(int use_arena); +STB_EXTERN stb_sdict * stb_sdict_copy(stb_sdict*); +STB_EXTERN void stb_sdict_delete(stb_sdict *); +STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p); +STB_EXTERN int stb_sdict_count(stb_sdict *d); + +#define stb_sdict_for(d,i,q,z) \ + for(i=0; i < (d)->limit ? q=(d)->table[i].k,z=(d)->table[i].v,1 : 0; ++i) \ + if (q==NULL||q==(void *) 1);else // reversed makes macro friendly + +#ifdef STB_DEFINE + +#define STB_DEL ((void *) 1) +#define STB_SDEL ((char *) 1) + +#define stb_sdict__copy(x) \ + strcpy(a->arena ? stb_malloc_string(a->arena, strlen(x)+1) \ + : (char *) malloc(strlen(x)+1), x) + +#define stb_sdict__dispose(x) if (!a->arena) free(x) + +stb_define_hash_base(STB_noprefix, stb_sdict, void*arena;, stb_sdict_,stb_sdictinternal_, 0.85f, + char *, NULL, STB_SDEL, stb_sdict__copy, stb_sdict__dispose, + STB_safecompare, !strcmp, STB_equal, return stb_hash(k);, + void *, STB_nullvalue, NULL) + +int stb_sdict_count(stb_sdict *a) +{ + return a->count; +} + +stb_sdict * stb_sdict_new(int use_arena) +{ + stb_sdict *d = stb_sdict_create(); + if (d == NULL) return NULL; + d->arena = use_arena ? stb_malloc_global(1) : NULL; + return d; +} + +stb_sdict* stb_sdict_copy(stb_sdict *old) +{ + stb_sdict *n; + void *old_arena = old->arena; + void *new_arena = old_arena ? stb_malloc_global(1) : NULL; + old->arena = new_arena; + n = stb_sdictinternal_copy(old); + old->arena = old_arena; + if (n) + n->arena = new_arena; + else if (new_arena) + stb_free(new_arena); + return n; +} + + +void stb_sdict_delete(stb_sdict *d) +{ + if (d->arena) + stb_free(d->arena); + stb_sdict_destroy(d); +} + +void * stb_sdict_change(stb_sdict *d, char *str, void *p) +{ + void *q = stb_sdict_get(d, str); + stb_sdict_set(d, str, p); + return q; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Instantiated data structures +// +// This is an attempt to implement a templated data structure. +// What you do is define a struct foo, and then include several +// pointer fields to struct foo in your struct. Then you call +// the instantiator, which creates the functions that implement +// the data structure. This requires massive undebuggable #defines, +// so we limit the cases where we do this. +// +// AA tree is an encoding of a 2-3 tree whereas RB trees encode a 2-3-4 tree; +// much simpler code due to fewer cases. + +#define stb__bst_parent(x) x +#define stb__bst_noparent(x) + +#define stb_bst_fields(N) \ + *STB_(N,left), *STB_(N,right); \ + unsigned char STB_(N,level) + +#define stb_bst_fields_parent(N) \ + *STB_(N,left), *STB_(N,right), *STB_(N,parent); \ + unsigned char STB_(N,level) + +#define STB__level(N,x) ((x) ? (x)->STB_(N,level) : 0) + +#define stb_bst_base(TYPE, N, TREE, M, compare, PAR) \ + \ +static int STB_(N,_compare)(TYPE *p, TYPE *q) \ +{ \ + compare \ +} \ + \ +static void STB_(N,setleft)(TYPE *q, TYPE *v) \ +{ \ + q->STB_(N,left) = v; \ + PAR(if (v) v->STB_(N,parent) = q;) \ +} \ + \ +static void STB_(N,setright)(TYPE *q, TYPE *v) \ +{ \ + q->STB_(N,right) = v; \ + PAR(if (v) v->STB_(N,parent) = q;) \ +} \ + \ +static TYPE *STB_(N,skew)(TYPE *q) \ +{ \ + if (q == NULL) return q; \ + if (q->STB_(N,left) \ + && q->STB_(N,left)->STB_(N,level) == q->STB_(N,level)) { \ + TYPE *p = q->STB_(N,left); \ + STB_(N,setleft)(q, p->STB_(N,right)); \ + STB_(N,setright)(p, q); \ + return p; \ + } \ + return q; \ +} \ + \ +static TYPE *STB_(N,split)(TYPE *p) \ +{ \ + TYPE *q = p->STB_(N,right); \ + if (q && q->STB_(N,right) \ + && q->STB_(N,right)->STB_(N,level) == p->STB_(N,level)) { \ + STB_(N,setright)(p, q->STB_(N,left)); \ + STB_(N,setleft)(q,p); \ + ++q->STB_(N,level); \ + return q; \ + } \ + return p; \ +} \ + \ +TYPE *STB__(N,insert)(TYPE *tree, TYPE *item) \ +{ \ + int c; \ + if (tree == NULL) { \ + item->STB_(N,left) = NULL; \ + item->STB_(N,right) = NULL; \ + item->STB_(N,level) = 1; \ + PAR(item->STB_(N,parent) = NULL;) \ + return item; \ + } \ + c = STB_(N,_compare)(item,tree); \ + if (c == 0) { \ + if (item != tree) { \ + STB_(N,setleft)(item, tree->STB_(N,left)); \ + STB_(N,setright)(item, tree->STB_(N,right)); \ + item->STB_(N,level) = tree->STB_(N,level); \ + PAR(item->STB_(N,parent) = NULL;) \ + } \ + return item; \ + } \ + if (c < 0) \ + STB_(N,setleft )(tree, STB__(N,insert)(tree->STB_(N,left), item)); \ + else \ + STB_(N,setright)(tree, STB__(N,insert)(tree->STB_(N,right), item)); \ + tree = STB_(N,skew)(tree); \ + tree = STB_(N,split)(tree); \ + PAR(tree->STB_(N,parent) = NULL;) \ + return tree; \ +} \ + \ +TYPE *STB__(N,remove)(TYPE *tree, TYPE *item) \ +{ \ + static TYPE *delnode, *leaf, *restore; \ + if (tree == NULL) return NULL; \ + leaf = tree; \ + if (STB_(N,_compare)(item, tree) < 0) { \ + STB_(N,setleft)(tree, STB__(N,remove)(tree->STB_(N,left), item)); \ + } else { \ + TYPE *r; \ + delnode = tree; \ + r = STB__(N,remove)(tree->STB_(N,right), item); \ + /* maybe move 'leaf' up to this location */ \ + if (restore == tree) { tree = leaf; leaf = restore = NULL; } \ + STB_(N,setright)(tree,r); \ + assert(tree->STB_(N,right) != tree); \ + } \ + if (tree == leaf) { \ + if (delnode == item) { \ + tree = tree->STB_(N,right); \ + assert(leaf->STB_(N,left) == NULL); \ + /* move leaf (the right sibling) up to delnode */ \ + STB_(N,setleft )(leaf, item->STB_(N,left )); \ + STB_(N,setright)(leaf, item->STB_(N,right)); \ + leaf->STB_(N,level) = item->STB_(N,level); \ + if (leaf != item) \ + restore = delnode; \ + } \ + delnode = NULL; \ + } else { \ + if (STB__level(N,tree->STB_(N,left) ) < tree->STB_(N,level)-1 || \ + STB__level(N,tree->STB_(N,right)) < tree->STB_(N,level)-1) { \ + --tree->STB_(N,level); \ + if (STB__level(N,tree->STB_(N,right)) > tree->STB_(N,level)) \ + tree->STB_(N,right)->STB_(N,level) = tree->STB_(N,level); \ + tree = STB_(N,skew)(tree); \ + STB_(N,setright)(tree, STB_(N,skew)(tree->STB_(N,right))); \ + if (tree->STB_(N,right)) \ + STB_(N,setright)(tree->STB_(N,right), \ + STB_(N,skew)(tree->STB_(N,right)->STB_(N,right))); \ + tree = STB_(N,split)(tree); \ + if (tree->STB_(N,right)) \ + STB_(N,setright)(tree, STB_(N,split)(tree->STB_(N,right))); \ + } \ + } \ + PAR(if (tree) tree->STB_(N,parent) = NULL;) \ + return tree; \ +} \ + \ +TYPE *STB__(N,last)(TYPE *tree) \ +{ \ + if (tree) \ + while (tree->STB_(N,right)) tree = tree->STB_(N,right); \ + return tree; \ +} \ + \ +TYPE *STB__(N,first)(TYPE *tree) \ +{ \ + if (tree) \ + while (tree->STB_(N,left)) tree = tree->STB_(N,left); \ + return tree; \ +} \ + \ +TYPE *STB__(N,next)(TYPE *tree, TYPE *item) \ +{ \ + TYPE *next = NULL; \ + if (item->STB_(N,right)) \ + return STB__(N,first)(item->STB_(N,right)); \ + PAR( \ + while(item->STB_(N,parent)) { \ + TYPE *up = item->STB_(N,parent); \ + if (up->STB_(N,left) == item) return up; \ + item = up; \ + } \ + return NULL; \ + ) \ + while (tree != item) { \ + if (STB_(N,_compare)(item, tree) < 0) { \ + next = tree; \ + tree = tree->STB_(N,left); \ + } else { \ + tree = tree->STB_(N,right); \ + } \ + } \ + return next; \ +} \ + \ +TYPE *STB__(N,prev)(TYPE *tree, TYPE *item) \ +{ \ + TYPE *next = NULL; \ + if (item->STB_(N,left)) \ + return STB__(N,last)(item->STB_(N,left)); \ + PAR( \ + while(item->STB_(N,parent)) { \ + TYPE *up = item->STB_(N,parent); \ + if (up->STB_(N,right) == item) return up; \ + item = up; \ + } \ + return NULL; \ + ) \ + while (tree != item) { \ + if (STB_(N,_compare)(item, tree) < 0) { \ + tree = tree->STB_(N,left); \ + } else { \ + next = tree; \ + tree = tree->STB_(N,right); \ + } \ + } \ + return next; \ +} \ + \ +STB__DEBUG( \ + void STB__(N,_validate)(TYPE *tree, int root) \ + { \ + if (tree == NULL) return; \ + PAR(if(root) assert(tree->STB_(N,parent) == NULL);) \ + assert(STB__level(N,tree->STB_(N,left) ) == tree->STB_(N,level)-1); \ + assert(STB__level(N,tree->STB_(N,right)) <= tree->STB_(N,level)); \ + assert(STB__level(N,tree->STB_(N,right)) >= tree->STB_(N,level)-1); \ + if (tree->STB_(N,right)) { \ + assert(STB__level(N,tree->STB_(N,right)->STB_(N,right)) \ + != tree->STB_(N,level)); \ + PAR(assert(tree->STB_(N,right)->STB_(N,parent) == tree);) \ + } \ + PAR(if(tree->STB_(N,left)) assert(tree->STB_(N,left)->STB_(N,parent) == tree);) \ + STB__(N,_validate)(tree->STB_(N,left) ,0); \ + STB__(N,_validate)(tree->STB_(N,right),0); \ + } \ +) \ + \ +typedef struct \ +{ \ + TYPE *root; \ +} TREE; \ + \ +void STB__(M,Insert)(TREE *tree, TYPE *item) \ +{ tree->root = STB__(N,insert)(tree->root, item); } \ +void STB__(M,Remove)(TREE *tree, TYPE *item) \ +{ tree->root = STB__(N,remove)(tree->root, item); } \ +TYPE *STB__(M,Next)(TREE *tree, TYPE *item) \ +{ return STB__(N,next)(tree->root, item); } \ +TYPE *STB__(M,Prev)(TREE *tree, TYPE *item) \ +{ return STB__(N,prev)(tree->root, item); } \ +TYPE *STB__(M,First)(TREE *tree) { return STB__(N,first)(tree->root); } \ +TYPE *STB__(M,Last) (TREE *tree) { return STB__(N,last) (tree->root); } \ +void STB__(M,Init)(TREE *tree) { tree->root = NULL; } + + +#define stb_bst_find(N,tree,fcompare) \ +{ \ + int c; \ + while (tree != NULL) { \ + fcompare \ + if (c == 0) return tree; \ + if (c < 0) tree = tree->STB_(N,left); \ + else tree = tree->STB_(N,right); \ + } \ + return NULL; \ +} + +#define stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,PAR) \ + stb_bst_base(TYPE,N,TREE,M, \ + VTYPE a = p->vfield; VTYPE b = q->vfield; return (compare);, PAR ) \ + \ +TYPE *STB__(N,find)(TYPE *tree, VTYPE a) \ + stb_bst_find(N,tree,VTYPE b = tree->vfield; c = (compare);) \ +TYPE *STB__(M,Find)(TREE *tree, VTYPE a) \ +{ return STB__(N,find)(tree->root, a); } + +#define stb_bst(TYPE,N,TREE,M,vfield,VTYPE,compare) \ + stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_noparent) +#define stb_bst_parent(TYPE,N,TREE,M,vfield,VTYPE,compare) \ + stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_parent) + + + +////////////////////////////////////////////////////////////////////////////// +// +// Pointer Nulling +// +// This lets you automatically NULL dangling pointers to "registered" +// objects. Note that you have to make sure you call the appropriate +// functions when you free or realloc blocks of memory that contain +// pointers or pointer targets. stb.h can automatically do this for +// stb_arr, or for all frees/reallocs if it's wrapping them. +// + +#ifdef STB_NPTR + +STB_EXTERN void stb_nptr_set(void *address_of_pointer, void *value_to_write); +STB_EXTERN void stb_nptr_didset(void *address_of_pointer); + +STB_EXTERN void stb_nptr_didfree(void *address_being_freed, int len); +STB_EXTERN void stb_nptr_free(void *address_being_freed, int len); + +STB_EXTERN void stb_nptr_didrealloc(void *new_address, void *old_address, int len); +STB_EXTERN void stb_nptr_recache(void); // recache all known pointers + // do this after pointer sets outside your control, slow + +#ifdef STB_DEFINE +// for fast updating on free/realloc, we need to be able to find +// all the objects (pointers and targets) within a given block; +// this precludes hashing + +// we use a three-level hierarchy of memory to minimize storage: +// level 1: 65536 pointers to stb__memory_node (always uses 256 KB) +// level 2: each stb__memory_node represents a 64K block of memory +// with 256 stb__memory_leafs (worst case 64MB) +// level 3: each stb__memory_leaf represents 256 bytes of memory +// using a list of target locations and a list of pointers +// (which are hopefully fairly short normally!) + +// this approach won't work in 64-bit, which has a much larger address +// space. need to redesign + +#define STB__NPTR_ROOT_LOG2 16 +#define STB__NPTR_ROOT_NUM (1 << STB__NPTR_ROOT_LOG2) +#define STB__NPTR_ROOT_SHIFT (32 - STB__NPTR_ROOT_LOG2) + +#define STB__NPTR_NODE_LOG2 5 +#define STB__NPTR_NODE_NUM (1 << STB__NPTR_NODE_LOG2) +#define STB__NPTR_NODE_MASK (STB__NPTR_NODE_NUM-1) +#define STB__NPTR_NODE_SHIFT (STB__NPTR_ROOT_SHIFT - STB__NPTR_NODE_LOG2) +#define STB__NPTR_NODE_OFFSET(x) (((x) >> STB__NPTR_NODE_SHIFT) & STB__NPTR_NODE_MASK) + +typedef struct stb__st_nptr +{ + void *ptr; // address of actual pointer + struct stb__st_nptr *next; // next pointer with same target + struct stb__st_nptr **prev; // prev pointer with same target, address of 'next' field (or first) + struct stb__st_nptr *next_in_block; +} stb__nptr; + +typedef struct stb__st_nptr_target +{ + void *ptr; // address of target + stb__nptr *first; // address of first nptr pointing to this + struct stb__st_nptr_target *next_in_block; +} stb__nptr_target; + +typedef struct +{ + stb__nptr *pointers; + stb__nptr_target *targets; +} stb__memory_leaf; + +typedef struct +{ + stb__memory_leaf *children[STB__NPTR_NODE_NUM]; +} stb__memory_node; + +stb__memory_node *stb__memtab_root[STB__NPTR_ROOT_NUM]; + +static stb__memory_leaf *stb__nptr_find_leaf(void *mem) +{ + stb_uint32 address = (stb_uint32) mem; + stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT]; + if (z) + return z->children[STB__NPTR_NODE_OFFSET(address)]; + else + return NULL; +} + +static void * stb__nptr_alloc(int size) +{ + return stb__realloc_raw(0,size); +} + +static void stb__nptr_free(void *p) +{ + stb__realloc_raw(p,0); +} + +static stb__memory_leaf *stb__nptr_make_leaf(void *mem) +{ + stb_uint32 address = (stb_uint32) mem; + stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT]; + stb__memory_leaf *f; + if (!z) { + int i; + z = (stb__memory_node *) stb__nptr_alloc(sizeof(*stb__memtab_root[0])); + stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT] = z; + for (i=0; i < 256; ++i) + z->children[i] = 0; + } + f = (stb__memory_leaf *) stb__nptr_alloc(sizeof(*f)); + z->children[STB__NPTR_NODE_OFFSET(address)] = f; + f->pointers = NULL; + f->targets = NULL; + return f; +} + +static stb__nptr_target *stb__nptr_find_target(void *target, int force) +{ + stb__memory_leaf *p = stb__nptr_find_leaf(target); + if (p) { + stb__nptr_target *t = p->targets; + while (t) { + if (t->ptr == target) + return t; + t = t->next_in_block; + } + } + if (force) { + stb__nptr_target *t = (stb__nptr_target*) stb__nptr_alloc(sizeof(*t)); + if (!p) p = stb__nptr_make_leaf(target); + t->ptr = target; + t->first = NULL; + t->next_in_block = p->targets; + p->targets = t; + return t; + } else + return NULL; +} + +static stb__nptr *stb__nptr_find_pointer(void *ptr, int force) +{ + stb__memory_leaf *p = stb__nptr_find_leaf(ptr); + if (p) { + stb__nptr *t = p->pointers; + while (t) { + if (t->ptr == ptr) + return t; + t = t->next_in_block; + } + } + if (force) { + stb__nptr *t = (stb__nptr *) stb__nptr_alloc(sizeof(*t)); + if (!p) p = stb__nptr_make_leaf(ptr); + t->ptr = ptr; + t->next = NULL; + t->prev = NULL; + t->next_in_block = p->pointers; + p->pointers = t; + return t; + } else + return NULL; +} + +void stb_nptr_set(void *address_of_pointer, void *value_to_write) +{ + if (*(void **)address_of_pointer != value_to_write) { + *(void **) address_of_pointer = value_to_write; + stb_nptr_didset(address_of_pointer); + } +} + +void stb_nptr_didset(void *address_of_pointer) +{ + // first unlink from old chain + void *new_address; + stb__nptr *p = stb__nptr_find_pointer(address_of_pointer, 1); // force building if doesn't exist + if (p->prev) { // if p->prev is NULL, we just built it, or it was NULL + *(p->prev) = p->next; + if (p->next) p->next->prev = p->prev; + } + // now add to new chain + new_address = *(void **)address_of_pointer; + if (new_address != NULL) { + stb__nptr_target *t = stb__nptr_find_target(new_address, 1); + p->next = t->first; + if (p->next) p->next->prev = &p->next; + p->prev = &t->first; + t->first = p; + } else { + p->prev = NULL; + p->next = NULL; + } +} + +void stb__nptr_block(void *address, int len, void (*function)(stb__memory_leaf *f, int datum, void *start, void *end), int datum) +{ + void *end_address = (void *) ((char *) address + len - 1); + stb__memory_node *n; + stb_uint32 start = (stb_uint32) address; + stb_uint32 end = start + len - 1; + + int b0 = start >> STB__NPTR_ROOT_SHIFT; + int b1 = end >> STB__NPTR_ROOT_SHIFT; + int b=b0,i,e0,e1; + + e0 = STB__NPTR_NODE_OFFSET(start); + + if (datum <= 0) { + // first block + n = stb__memtab_root[b0]; + if (n) { + if (b0 != b1) + e1 = STB__NPTR_NODE_NUM-1; + else + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=e0; i <= e1; ++i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + if (b1 > b0) { + // blocks other than the first and last block + for (b=b0+1; b < b1; ++b) { + n = stb__memtab_root[b]; + if (n) + for (i=0; i <= STB__NPTR_NODE_NUM-1; ++i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + // last block + n = stb__memtab_root[b1]; + if (n) { + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=0; i <= e1; ++i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + } + } else { + if (b1 > b0) { + // last block + n = stb__memtab_root[b1]; + if (n) { + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=e1; i >= 0; --i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + // blocks other than the first and last block + for (b=b1-1; b > b0; --b) { + n = stb__memtab_root[b]; + if (n) + for (i=STB__NPTR_NODE_NUM-1; i >= 0; --i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + } + // first block + n = stb__memtab_root[b0]; + if (n) { + if (b0 != b1) + e1 = STB__NPTR_NODE_NUM-1; + else + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=e1; i >= e0; --i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + } +} + +static void stb__nptr_delete_pointers(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr **p = &f->pointers; + while (*p) { + stb__nptr *n = *p; + if (n->ptr >= start && n->ptr <= end) { + // unlink + if (n->prev) { + *(n->prev) = n->next; + if (n->next) n->next->prev = n->prev; + } + *p = n->next_in_block; + stb__nptr_free(n); + } else + p = &(n->next_in_block); + } +} + +static void stb__nptr_delete_targets(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr_target **p = &f->targets; + while (*p) { + stb__nptr_target *n = *p; + if (n->ptr >= start && n->ptr <= end) { + // null pointers + stb__nptr *z = n->first; + while (z) { + stb__nptr *y = z->next; + z->prev = NULL; + z->next = NULL; + *(void **) z->ptr = NULL; + z = y; + } + // unlink this target + *p = n->next_in_block; + stb__nptr_free(n); + } else + p = &(n->next_in_block); + } +} + +void stb_nptr_didfree(void *address_being_freed, int len) +{ + // step one: delete all pointers in this block + stb__nptr_block(address_being_freed, len, stb__nptr_delete_pointers, 0); + // step two: NULL all pointers to this block; do this second to avoid NULLing deleted pointers + stb__nptr_block(address_being_freed, len, stb__nptr_delete_targets, 0); +} + +void stb_nptr_free(void *address_being_freed, int len) +{ + free(address_being_freed); + stb_nptr_didfree(address_being_freed, len); +} + +static void stb__nptr_move_targets(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr_target **t = &f->targets; + while (*t) { + stb__nptr_target *n = *t; + if (n->ptr >= start && n->ptr <= end) { + stb__nptr *z; + stb__memory_leaf *f; + // unlink n + *t = n->next_in_block; + // update n to new address + n->ptr = (void *) ((char *) n->ptr + offset); + f = stb__nptr_find_leaf(n->ptr); + if (!f) f = stb__nptr_make_leaf(n->ptr); + n->next_in_block = f->targets; + f->targets = n; + // now go through all pointers and make them point here + z = n->first; + while (z) { + *(void**) z->ptr = n->ptr; + z = z->next; + } + } else + t = &(n->next_in_block); + } +} + +static void stb__nptr_move_pointers(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr **p = &f->pointers; + while (*p) { + stb__nptr *n = *p; + if (n->ptr >= start && n->ptr <= end) { + // unlink + *p = n->next_in_block; + n->ptr = (void *) ((int) n->ptr + offset); + // move to new block + f = stb__nptr_find_leaf(n->ptr); + if (!f) f = stb__nptr_make_leaf(n->ptr); + n->next_in_block = f->pointers; + f->pointers = n; + } else + p = &(n->next_in_block); + } +} + +void stb_nptr_realloc(void *new_address, void *old_address, int len) +{ + if (new_address == old_address) return; + + // have to move the pointers first, because moving the targets + // requires writing to the pointers-to-the-targets, and if some of those moved too, + // we need to make sure we don't write to the old memory + + // step one: move all pointers within the block + stb__nptr_block(old_address, len, stb__nptr_move_pointers, (char *) new_address - (char *) old_address); + // step two: move all targets within the block + stb__nptr_block(old_address, len, stb__nptr_move_targets, (char *) new_address - (char *) old_address); +} + +void stb_nptr_move(void *new_address, void *old_address) +{ + stb_nptr_realloc(new_address, old_address, 1); +} + +void stb_nptr_recache(void) +{ + int i,j; + for (i=0; i < STB__NPTR_ROOT_NUM; ++i) + if (stb__memtab_root[i]) + for (j=0; j < STB__NPTR_NODE_NUM; ++j) + if (stb__memtab_root[i]->children[j]) { + stb__nptr *p = stb__memtab_root[i]->children[j]->pointers; + while (p) { + stb_nptr_didset(p->ptr); + p = p->next_in_block; + } + } +} + +#endif // STB_DEFINE +#endif // STB_NPTR + + +////////////////////////////////////////////////////////////////////////////// +// +// File Processing +// + + +#ifdef _MSC_VER + #define stb_rename(x,y) _wrename((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) + #define stb_mktemp _mktemp +#else + #define stb_mktemp mktemp + #define stb_rename rename +#endif + +STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v); +STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f); +STB_EXTERN int stb_size_varlen64(stb_uint64 v); + + +#define stb_filec (char *) stb_file +#define stb_fileu (unsigned char *) stb_file +STB_EXTERN void * stb_file(char *filename, size_t *length); +STB_EXTERN void * stb_file_max(char *filename, size_t *length); +STB_EXTERN size_t stb_filelen(FILE *f); +STB_EXTERN int stb_filewrite(char *filename, void *data, size_t length); +STB_EXTERN int stb_filewritestr(char *filename, char *data); +STB_EXTERN char ** stb_stringfile(char *filename, int *len); +STB_EXTERN char ** stb_stringfile_trimmed(char *name, int *len, char comm); +STB_EXTERN char * stb_fgets(char *buffer, int buflen, FILE *f); +STB_EXTERN char * stb_fgets_malloc(FILE *f); +STB_EXTERN int stb_fexists(char *filename); +STB_EXTERN int stb_fcmp(char *s1, char *s2); +STB_EXTERN int stb_feq(char *s1, char *s2); +STB_EXTERN time_t stb_ftimestamp(char *filename); + +STB_EXTERN int stb_fullpath(char *abs, int abs_size, char *rel); +STB_EXTERN FILE * stb_fopen(char *filename, char *mode); +STB_EXTERN int stb_fclose(FILE *f, int keep); + +enum +{ + stb_keep_no = 0, + stb_keep_yes = 1, + stb_keep_if_different = 2, +}; + +STB_EXTERN int stb_copyfile(char *src, char *dest); + +STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v); +STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f); +STB_EXTERN int stb_size_varlen64(stb_uint64 v); + +STB_EXTERN void stb_fwrite32(FILE *f, stb_uint32 datum); +STB_EXTERN void stb_fput_varlen (FILE *f, int v); +STB_EXTERN void stb_fput_varlenu(FILE *f, unsigned int v); +STB_EXTERN int stb_fget_varlen (FILE *f); +STB_EXTERN stb_uint stb_fget_varlenu(FILE *f); +STB_EXTERN void stb_fput_ranged (FILE *f, int v, int b, stb_uint n); +STB_EXTERN int stb_fget_ranged (FILE *f, int b, stb_uint n); +STB_EXTERN int stb_size_varlen (int v); +STB_EXTERN int stb_size_varlenu(unsigned int v); +STB_EXTERN int stb_size_ranged (int b, stb_uint n); + +STB_EXTERN int stb_fread(void *data, size_t len, size_t count, void *f); +STB_EXTERN int stb_fwrite(void *data, size_t len, size_t count, void *f); + +#if 0 +typedef struct +{ + FILE *base_file; + char *buffer; + int buffer_size; + int buffer_off; + int buffer_left; +} STBF; + +STB_EXTERN STBF *stb_tfopen(char *filename, char *mode); +STB_EXTERN int stb_tfread(void *data, size_t len, size_t count, STBF *f); +STB_EXTERN int stb_tfwrite(void *data, size_t len, size_t count, STBF *f); +#endif + +#ifdef STB_DEFINE + +#if 0 +STBF *stb_tfopen(char *filename, char *mode) +{ + STBF *z; + FILE *f = fopen(filename, mode); + if (!f) return NULL; + z = (STBF *) malloc(sizeof(*z)); + if (!z) { fclose(f); return NULL; } + z->base_file = f; + if (!strcmp(mode, "rb") || !strcmp(mode, "wb")) { + z->buffer_size = 4096; + z->buffer_off = z->buffer_size; + z->buffer_left = 0; + z->buffer = malloc(z->buffer_size); + if (!z->buffer) { free(z); fclose(f); return NULL; } + } else { + z->buffer = 0; + z->buffer_size = 0; + z->buffer_left = 0; + } + return z; +} + +int stb_tfread(void *data, size_t len, size_t count, STBF *f) +{ + int total = len*count, done=0; + if (!total) return 0; + if (total <= z->buffer_left) { + memcpy(data, z->buffer + z->buffer_off, total); + z->buffer_off += total; + z->buffer_left -= total; + return count; + } else { + char *out = (char *) data; + + // consume all buffered data + memcpy(data, z->buffer + z->buffer_off, z->buffer_left); + done = z->buffer_left; + out += z->buffer_left; + z->buffer_left=0; + + if (total-done > (z->buffer_size >> 1)) { + done += fread(out + } + } +} +#endif + +void stb_fwrite32(FILE *f, stb_uint32 x) +{ + fwrite(&x, 4, 1, f); +} + +#if defined(_MSC_VER) || defined(__MINGW32__) + #define stb__stat _stat +#else + #define stb__stat stat +#endif + +int stb_fexists(char *filename) +{ + struct stb__stat buf; + return stb__windows( + _wstat((const wchar_t *)stb__from_utf8(filename), &buf), + stat(filename,&buf) + ) == 0; +} + +time_t stb_ftimestamp(char *filename) +{ + struct stb__stat buf; + if (stb__windows( + _wstat((const wchar_t *)stb__from_utf8(filename), &buf), + stat(filename,&buf) + ) == 0) + { + return buf.st_mtime; + } else { + return 0; + } +} + +size_t stb_filelen(FILE *f) +{ + size_t len, pos; + pos = ftell(f); + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, pos, SEEK_SET); + return len; +} + +void *stb_file(char *filename, size_t *length) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer; + size_t len, len2; + if (!f) return NULL; + len = stb_filelen(f); + buffer = (char *) malloc(len+2); // nul + extra + len2 = fread(buffer, 1, len, f); + if (len2 == len) { + if (length) *length = len; + buffer[len] = 0; + } else { + free(buffer); + buffer = NULL; + } + fclose(f); + return buffer; +} + +int stb_filewrite(char *filename, void *data, size_t length) +{ + FILE *f = stb_fopen(filename, "wb"); + if (f) { + fwrite(data, 1, length, f); + stb_fclose(f, stb_keep_if_different); + } + return f != NULL; +} + +int stb_filewritestr(char *filename, char *data) +{ + return stb_filewrite(filename, data, strlen(data)); +} + +void * stb_file_max(char *filename, size_t *length) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer; + size_t len, maxlen; + if (!f) return NULL; + maxlen = *length; + buffer = (char *) malloc(maxlen+1); + len = fread(buffer, 1, maxlen, f); + buffer[len] = 0; + fclose(f); + *length = len; + return buffer; +} + +char ** stb_stringfile(char *filename, int *plen) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer, **list=NULL, *s; + size_t len, count, i; + + if (!f) return NULL; + len = stb_filelen(f); + buffer = (char *) malloc(len+1); + len = fread(buffer, 1, len, f); + buffer[len] = 0; + fclose(f); + + // two passes through: first time count lines, second time set them + for (i=0; i < 2; ++i) { + s = buffer; + if (i == 1) + list[0] = s; + count = 1; + while (*s) { + if (*s == '\n' || *s == '\r') { + // detect if both cr & lf are together + int crlf = (s[0] + s[1]) == ('\n' + '\r'); + if (i == 1) *s = 0; + if (crlf) ++s; + if (s[1]) { // it's not over yet + if (i == 1) list[count] = s+1; + ++count; + } + } + ++s; + } + if (i == 0) { + list = (char **) malloc(sizeof(*list) * (count+1) + len+1); + if (!list) return NULL; + list[count] = 0; + // recopy the file so there's just a single allocation to free + memcpy(&list[count+1], buffer, len+1); + free(buffer); + buffer = (char *) &list[count+1]; + if (plen) *plen = count; + } + } + return list; +} + +char ** stb_stringfile_trimmed(char *name, int *len, char comment) +{ + int i,n,o=0; + char **s = stb_stringfile(name, &n); + if (s == NULL) return NULL; + for (i=0; i < n; ++i) { + char *p = stb_skipwhite(s[i]); + if (*p && *p != comment) + s[o++] = p; + } + s[o] = NULL; + if (len) *len = o; + return s; +} + +char * stb_fgets(char *buffer, int buflen, FILE *f) +{ + char *p; + buffer[0] = 0; + p = fgets(buffer, buflen, f); + if (p) { + int n = strlen(p)-1; + if (n >= 0) + if (p[n] == '\n') + p[n] = 0; + } + return p; +} + +char * stb_fgets_malloc(FILE *f) +{ + // avoid reallocing for small strings + char quick_buffer[800]; + quick_buffer[sizeof(quick_buffer)-2] = 0; + if (!fgets(quick_buffer, sizeof(quick_buffer), f)) + return NULL; + + if (quick_buffer[sizeof(quick_buffer)-2] == 0) { + int n = strlen(quick_buffer); + if (n > 0 && quick_buffer[n-1] == '\n') + quick_buffer[n-1] = 0; + return strdup(quick_buffer); + } else { + char *p; + char *a = strdup(quick_buffer); + int len = sizeof(quick_buffer)-1; + + while (!feof(f)) { + if (a[len-1] == '\n') break; + a = (char *) realloc(a, len*2); + p = &a[len]; + p[len-2] = 0; + if (!fgets(p, len, f)) + break; + if (p[len-2] == 0) { + len += strlen(p); + break; + } + len = len + (len-1); + } + if (a[len-1] == '\n') + a[len-1] = 0; + return a; + } +} + +int stb_fullpath(char *abs, int abs_size, char *rel) +{ + #ifdef _MSC_VER + return _fullpath(abs, rel, abs_size) != NULL; + #else + if (rel[0] == '/' || rel[0] == '~') { + if ((int) strlen(rel) >= abs_size) + return 0; + strcpy(abs,rel); + return STB_TRUE; + } else { + int n; + getcwd(abs, abs_size); + n = strlen(abs); + if (n+(int) strlen(rel)+2 <= abs_size) { + abs[n] = '/'; + strcpy(abs+n+1, rel); + return STB_TRUE; + } else { + return STB_FALSE; + } + } + #endif +} + +static int stb_fcmp_core(FILE *f, FILE *g) +{ + char buf1[1024],buf2[1024]; + int n1,n2, res=0; + + while (1) { + n1 = fread(buf1, 1, sizeof(buf1), f); + n2 = fread(buf2, 1, sizeof(buf2), g); + res = memcmp(buf1,buf2,stb_min(n1,n2)); + if (res) + break; + if (n1 != n2) { + res = n1 < n2 ? -1 : 1; + break; + } + if (n1 == 0) + break; + } + + fclose(f); + fclose(g); + return res; +} + +int stb_fcmp(char *s1, char *s2) +{ + FILE *f = stb__fopen(s1, "rb"); + FILE *g = stb__fopen(s2, "rb"); + + if (f == NULL || g == NULL) { + if (f) fclose(f); + if (g) { + fclose(g); + return STB_TRUE; + } + return f != NULL; + } + + return stb_fcmp_core(f,g); +} + +int stb_feq(char *s1, char *s2) +{ + FILE *f = stb__fopen(s1, "rb"); + FILE *g = stb__fopen(s2, "rb"); + + if (f == NULL || g == NULL) { + if (f) fclose(f); + if (g) fclose(g); + return f == g; + } + + // feq is faster because it shortcuts if they're different length + if (stb_filelen(f) != stb_filelen(g)) { + fclose(f); + fclose(g); + return 0; + } + + return !stb_fcmp_core(f,g); +} + +static stb_ptrmap *stb__files; + +typedef struct +{ + char *temp_name; + char *name; + int errors; +} stb__file_data; + +FILE * stb_fopen(char *filename, char *mode) +{ + FILE *f; + char name_full[4096]; + char temp_full[sizeof(name_full) + 12]; + int p; +#ifdef _MSC_VER + int j; +#endif + if (mode[0] != 'w' && !strchr(mode, '+')) + return stb__fopen(filename, mode); + + // save away the full path to the file so if the program + // changes the cwd everything still works right! unix has + // better ways to do this, but we have to work in windows + name_full[0] = '\0'; // stb_fullpath reads name_full[0] + if (stb_fullpath(name_full, sizeof(name_full), filename)==0) + return 0; + + // try to generate a temporary file in the same directory + p = strlen(name_full)-1; + while (p > 0 && name_full[p] != '/' && name_full[p] != '\\' + && name_full[p] != ':' && name_full[p] != '~') + --p; + ++p; + + memcpy(temp_full, name_full, p); + + #ifdef _MSC_VER + // try multiple times to make a temp file... just in + // case some other process makes the name first + for (j=0; j < 32; ++j) { + strcpy(temp_full+p, "stmpXXXXXX"); + if (stb_mktemp(temp_full) == NULL) + return 0; + + f = fopen(temp_full, mode); + if (f != NULL) + break; + } + #else + { + strcpy(temp_full+p, "stmpXXXXXX"); + #ifdef __MINGW32__ + int fd = open(mktemp(temp_full), O_RDWR); + #else + int fd = mkstemp(temp_full); + #endif + if (fd == -1) return NULL; + f = fdopen(fd, mode); + if (f == NULL) { + unlink(temp_full); + close(fd); + return NULL; + } + } + #endif + if (f != NULL) { + stb__file_data *d = (stb__file_data *) malloc(sizeof(*d)); + if (!d) { assert(0); /* NOTREACHED */fclose(f); return NULL; } + if (stb__files == NULL) stb__files = stb_ptrmap_create(); + d->temp_name = strdup(temp_full); + d->name = strdup(name_full); + d->errors = 0; + stb_ptrmap_add(stb__files, f, d); + return f; + } + + return NULL; +} + +int stb_fclose(FILE *f, int keep) +{ + stb__file_data *d; + + int ok = STB_FALSE; + if (f == NULL) return 0; + + if (ferror(f)) + keep = stb_keep_no; + + fclose(f); + + if (stb__files && stb_ptrmap_remove(stb__files, f, (void **) &d)) { + if (stb__files->count == 0) { + stb_ptrmap_destroy(stb__files); + stb__files = NULL; + } + } else + return STB_TRUE; // not special + + if (keep == stb_keep_if_different) { + // check if the files are identical + if (stb_feq(d->name, d->temp_name)) { + keep = stb_keep_no; + ok = STB_TRUE; // report success if no change + } + } + + if (keep != stb_keep_no) { + if (stb_fexists(d->name) && remove(d->name)) { + // failed to delete old, so don't keep new + keep = stb_keep_no; + } else { + if (!stb_rename(d->temp_name, d->name)) + ok = STB_TRUE; + else + keep=stb_keep_no; + } + } + + if (keep == stb_keep_no) + remove(d->temp_name); + + free(d->temp_name); + free(d->name); + free(d); + + return ok; +} + +int stb_copyfile(char *src, char *dest) +{ + char raw_buffer[1024]; + char *buffer; + int buf_size = 65536; + + FILE *f, *g; + + // if file already exists at destination, do nothing + if (stb_feq(src, dest)) return STB_TRUE; + + // open file + f = stb__fopen(src, "rb"); + if (f == NULL) return STB_FALSE; + + // open file for writing + g = stb__fopen(dest, "wb"); + if (g == NULL) { + fclose(f); + return STB_FALSE; + } + + buffer = (char *) malloc(buf_size); + if (buffer == NULL) { + buffer = raw_buffer; + buf_size = sizeof(raw_buffer); + } + + while (!feof(f)) { + int n = fread(buffer, 1, buf_size, f); + if (n != 0) + fwrite(buffer, 1, n, g); + } + + fclose(f); + if (buffer != raw_buffer) + free(buffer); + + fclose(g); + return STB_TRUE; +} + +// varlen: +// v' = (v >> 31) + (v < 0 ? ~v : v)<<1; // small abs(v) => small v' +// output v as big endian v'+k for v' <= k: +// 1 byte : v' <= 0x00000080 ( -64 <= v < 64) 7 bits +// 2 bytes: v' <= 0x00004000 (-8192 <= v < 8192) 14 bits +// 3 bytes: v' <= 0x00200000 21 bits +// 4 bytes: v' <= 0x10000000 28 bits +// the number of most significant 1-bits in the first byte +// equals the number of bytes after the first + +#define stb__varlen_xform(v) (v<0 ? (~v << 1)+1 : (v << 1)) + +int stb_size_varlen(int v) { return stb_size_varlenu(stb__varlen_xform(v)); } +int stb_size_varlenu(unsigned int v) +{ + if (v < 0x00000080) return 1; + if (v < 0x00004000) return 2; + if (v < 0x00200000) return 3; + if (v < 0x10000000) return 4; + return 5; +} + +void stb_fput_varlen(FILE *f, int v) { stb_fput_varlenu(f, stb__varlen_xform(v)); } + +void stb_fput_varlenu(FILE *f, unsigned int z) +{ + if (z >= 0x10000000) fputc(0xF0,f); + if (z >= 0x00200000) fputc((z < 0x10000000 ? 0xE0 : 0)+(z>>24),f); + if (z >= 0x00004000) fputc((z < 0x00200000 ? 0xC0 : 0)+(z>>16),f); + if (z >= 0x00000080) fputc((z < 0x00004000 ? 0x80 : 0)+(z>> 8),f); + fputc(z,f); +} + +#define stb_fgetc(f) ((unsigned char) fgetc(f)) + +int stb_fget_varlen(FILE *f) +{ + unsigned int z = stb_fget_varlenu(f); + return (z & 1) ? ~(z>>1) : (z>>1); +} + +unsigned int stb_fget_varlenu(FILE *f) +{ + unsigned int z; + unsigned char d; + d = stb_fgetc(f); + + if (d >= 0x80) { + if (d >= 0xc0) { + if (d >= 0xe0) { + if (d == 0xf0) z = stb_fgetc(f) << 24; + else z = (d - 0xe0) << 24; + z += stb_fgetc(f) << 16; + } + else + z = (d - 0xc0) << 16; + z += stb_fgetc(f) << 8; + } else + z = (d - 0x80) << 8; + z += stb_fgetc(f); + } else + z = d; + return z; +} + +stb_uint64 stb_fget_varlen64(FILE *f) +{ + stb_uint64 z; + unsigned char d; + d = stb_fgetc(f); + + if (d >= 0x80) { + if (d >= 0xc0) { + if (d >= 0xe0) { + if (d >= 0xf0) { + if (d >= 0xf8) { + if (d >= 0xfc) { + if (d >= 0xfe) { + if (d >= 0xff) + z = (stb_uint64) stb_fgetc(f) << 56; + else + z = (stb_uint64) (d - 0xfe) << 56; + z |= (stb_uint64) stb_fgetc(f) << 48; + } else z = (stb_uint64) (d - 0xfc) << 48; + z |= (stb_uint64) stb_fgetc(f) << 40; + } else z = (stb_uint64) (d - 0xf8) << 40; + z |= (stb_uint64) stb_fgetc(f) << 32; + } else z = (stb_uint64) (d - 0xf0) << 32; + z |= (stb_uint) stb_fgetc(f) << 24; + } else z = (stb_uint) (d - 0xe0) << 24; + z |= (stb_uint) stb_fgetc(f) << 16; + } else z = (stb_uint) (d - 0xc0) << 16; + z |= (stb_uint) stb_fgetc(f) << 8; + } else z = (stb_uint) (d - 0x80) << 8; + z |= stb_fgetc(f); + } else + z = d; + + return (z & 1) ? ~(z >> 1) : (z >> 1); +} + +int stb_size_varlen64(stb_uint64 v) +{ + if (v < 0x00000080) return 1; + if (v < 0x00004000) return 2; + if (v < 0x00200000) return 3; + if (v < 0x10000000) return 4; + if (v < STB_IMM_UINT64(0x0000000800000000)) return 5; + if (v < STB_IMM_UINT64(0x0000040000000000)) return 6; + if (v < STB_IMM_UINT64(0x0002000000000000)) return 7; + if (v < STB_IMM_UINT64(0x0100000000000000)) return 8; + return 9; +} + +void stb_fput_varlen64(FILE *f, stb_uint64 v) +{ + stb_uint64 z = stb__varlen_xform(v); + int first=1; + if (z >= STB_IMM_UINT64(0x100000000000000)) { + fputc(0xff,f); + first=0; + } + if (z >= STB_IMM_UINT64(0x02000000000000)) fputc((first ? 0xFE : 0)+(char)(z>>56),f), first=0; + if (z >= STB_IMM_UINT64(0x00040000000000)) fputc((first ? 0xFC : 0)+(char)(z>>48),f), first=0; + if (z >= STB_IMM_UINT64(0x00000800000000)) fputc((first ? 0xF8 : 0)+(char)(z>>40),f), first=0; + if (z >= STB_IMM_UINT64(0x00000010000000)) fputc((first ? 0xF0 : 0)+(char)(z>>32),f), first=0; + if (z >= STB_IMM_UINT64(0x00000000200000)) fputc((first ? 0xE0 : 0)+(char)(z>>24),f), first=0; + if (z >= STB_IMM_UINT64(0x00000000004000)) fputc((first ? 0xC0 : 0)+(char)(z>>16),f), first=0; + if (z >= STB_IMM_UINT64(0x00000000000080)) fputc((first ? 0x80 : 0)+(char)(z>> 8),f), first=0; + fputc((char)z,f); +} + +void stb_fput_ranged(FILE *f, int v, int b, stb_uint n) +{ + v -= b; + if (n <= (1 << 31)) + assert((stb_uint) v < n); + if (n > (1 << 24)) fputc(v >> 24, f); + if (n > (1 << 16)) fputc(v >> 16, f); + if (n > (1 << 8)) fputc(v >> 8, f); + fputc(v,f); +} + +int stb_fget_ranged(FILE *f, int b, stb_uint n) +{ + unsigned int v=0; + if (n > (1 << 24)) v += stb_fgetc(f) << 24; + if (n > (1 << 16)) v += stb_fgetc(f) << 16; + if (n > (1 << 8)) v += stb_fgetc(f) << 8; + v += stb_fgetc(f); + return b+v; +} + +int stb_size_ranged(int b, stb_uint n) +{ + if (n > (1 << 24)) return 4; + if (n > (1 << 16)) return 3; + if (n > (1 << 8)) return 2; + return 1; +} + +void stb_fput_string(FILE *f, char *s) +{ + int len = strlen(s); + stb_fput_varlenu(f, len); + fwrite(s, 1, len, f); +} + +// inverse of the above algorithm +char *stb_fget_string(FILE *f, void *p) +{ + char *s; + int len = stb_fget_varlenu(f); + if (len > 4096) return NULL; + s = p ? stb_malloc_string(p, len+1) : (char *) malloc(len+1); + fread(s, 1, len, f); + s[len] = 0; + return s; +} + +char *stb_strdup(char *str, void *pool) +{ + int len = strlen(str); + char *p = stb_malloc_string(pool, len+1); + strcpy(p, str); + return p; +} + +// strip the trailing '/' or '\\' from a directory so we can refer to it +// as a file for _stat() +char *stb_strip_final_slash(char *t) +{ + if (t[0]) { + char *z = t + strlen(t) - 1; + // *z is the last character + if (*z == '\\' || *z == '/') + if (z != t+2 || t[1] != ':') // but don't strip it if it's e.g. "c:/" + *z = 0; + if (*z == '\\') + *z = '/'; // canonicalize to make sure it matches db + } + return t; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Options parsing +// + +STB_EXTERN char **stb_getopt_param(int *argc, char **argv, char *param); +STB_EXTERN char **stb_getopt(int *argc, char **argv); +STB_EXTERN void stb_getopt_free(char **opts); + +#ifdef STB_DEFINE + +void stb_getopt_free(char **opts) +{ + int i; + char ** o2 = opts; + for (i=0; i < stb_arr_len(o2); ++i) + free(o2[i]); + stb_arr_free(o2); +} + +char **stb_getopt(int *argc, char **argv) +{ + return stb_getopt_param(argc, argv, ""); +} + +char **stb_getopt_param(int *argc, char **argv, char *param) +{ + char ** opts=NULL; + int i,j=1; + for (i=1; i < *argc; ++i) { + if (argv[i][0] != '-') { + argv[j++] = argv[i]; + } else { + if (argv[i][1] == 0) { // plain - == don't parse further options + ++i; + while (i < *argc) + argv[j++] = argv[i++]; + break; + } else { + int k; + char *q = argv[i]; // traverse options list + for (k=1; q[k]; ++k) { + char *s; + if (strchr(param, q[k])) { // does it take a parameter? + char *t = &q[k+1], z = q[k]; + int len=0; + if (*t == 0) { + if (i == *argc-1) { // takes a parameter, but none found + *argc = 0; + stb_getopt_free(opts); + return NULL; + } + t = argv[++i]; + } else + k += strlen(t); + len = strlen(t); + s = (char *) malloc(len+2); + if (!s) return NULL; + s[0] = z; + strcpy(s+1, t); + } else { + // no parameter + s = (char *) malloc(2); + if (!s) return NULL; + s[0] = q[k]; + s[1] = 0; + } + stb_arr_push(opts, s); + } + } + } + } + stb_arr_push(opts, NULL); + *argc = j; + return opts; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Portable directory reading +// + +STB_EXTERN char **stb_readdir_files (char *dir); +STB_EXTERN char **stb_readdir_files_mask(char *dir, char *wild); +STB_EXTERN char **stb_readdir_subdirs(char *dir); +STB_EXTERN char **stb_readdir_subdirs_mask(char *dir, char *wild); +STB_EXTERN void stb_readdir_free (char **files); +STB_EXTERN char **stb_readdir_recursive(char *dir, char *filespec); +STB_EXTERN void stb_delete_directory_recursive(char *dir); + +#ifdef STB_DEFINE + +#ifdef _MSC_VER +#include <io.h> +#else +#include <unistd.h> +#include <dirent.h> +#endif + +void stb_readdir_free(char **files) +{ + char **f2 = files; + int i; + for (i=0; i < stb_arr_len(f2); ++i) + free(f2[i]); + stb_arr_free(f2); +} + +STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); +static char **readdir_raw(char *dir, int return_subdirs, char *mask) +{ + char **results = NULL; + char buffer[512], with_slash[512]; + size_t n; + + #ifdef _MSC_VER + stb__wchar *ws; + struct _wfinddata_t data; + #ifdef _WIN64 + const intptr_t none = -1; + intptr_t z; + #else + const long none = -1; + long z; + #endif + #else // !_MSC_VER + const DIR *none = NULL; + DIR *z; + #endif + + strcpy(buffer,dir); + stb_fixpath(buffer); + n = strlen(buffer); + + if (n > 0 && (buffer[n-1] != '/')) { + buffer[n++] = '/'; + } + buffer[n] = 0; + strcpy(with_slash, buffer); + + #ifdef _MSC_VER + strcpy(buffer+n, "*.*"); + ws = stb__from_utf8(buffer); + z = _wfindfirst((const wchar_t *)ws, &data); + #else + z = opendir(dir); + #endif + + + if (z != none) { + int nonempty = STB_TRUE; + #ifndef _MSC_VER + struct dirent *data = readdir(z); + nonempty = (data != NULL); + #endif + + if (nonempty) { + + do { + int is_subdir; + #ifdef _MSC_VER + char *name = stb__to_utf8((stb__wchar *)data.name); + if (name == NULL) { + fprintf(stderr, "%s to convert '%S' to %s!\n", "Unable", data.name, "utf8"); + continue; + } + is_subdir = !!(data.attrib & _A_SUBDIR); + #else + char *name = data->d_name; + strcpy(buffer+n,name); + DIR *y = opendir(buffer); + is_subdir = (y != NULL); + if (y != NULL) closedir(y); + #endif + + if (is_subdir == return_subdirs) { + if (!is_subdir || name[0] != '.') { + if (!mask || stb_wildmatchi(mask, name)) { + char buffer[512],*p=buffer; + sprintf(buffer, "%s%s", with_slash, name); + if (buffer[0] == '.' && buffer[1] == '/') + p = buffer+2; + stb_arr_push(results, strdup(p)); + } + } + } + } + #ifdef _MSC_VER + while (0 == _wfindnext(z, &data)); + #else + while ((data = readdir(z)) != NULL); + #endif + } + #ifdef _MSC_VER + _findclose(z); + #else + closedir(z); + #endif + } + return results; +} + +char **stb_readdir_files (char *dir) { return readdir_raw(dir, 0, NULL); } +char **stb_readdir_subdirs(char *dir) { return readdir_raw(dir, 1, NULL); } +char **stb_readdir_files_mask(char *dir, char *wild) { return readdir_raw(dir, 0, wild); } +char **stb_readdir_subdirs_mask(char *dir, char *wild) { return readdir_raw(dir, 1, wild); } + +int stb__rec_max=0x7fffffff; +static char **stb_readdir_rec(char **sofar, char *dir, char *filespec) +{ + char **files; + char ** dirs; + char **p; + + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; + + files = stb_readdir_files_mask(dir, filespec); + stb_arr_for(p, files) { + stb_arr_push(sofar, strdup(*p)); + if (stb_arr_len(sofar) >= stb__rec_max) break; + } + stb_readdir_free(files); + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; + + dirs = stb_readdir_subdirs(dir); + stb_arr_for(p, dirs) + sofar = stb_readdir_rec(sofar, *p, filespec); + stb_readdir_free(dirs); + return sofar; +} + +char **stb_readdir_recursive(char *dir, char *filespec) +{ + return stb_readdir_rec(NULL, dir, filespec); +} + +void stb_delete_directory_recursive(char *dir) +{ + char **list = stb_readdir_subdirs(dir); + int i; + for (i=0; i < stb_arr_len(list); ++i) + stb_delete_directory_recursive(list[i]); + stb_arr_free(list); + list = stb_readdir_files(dir); + for (i=0; i < stb_arr_len(list); ++i) + if (!remove(list[i])) { + // on windows, try again after making it writeable; don't ALWAYS + // do this first since that would be slow in the normal case + #ifdef _MSC_VER + _chmod(list[i], _S_IWRITE); + remove(list[i]); + #endif + } + stb_arr_free(list); + stb__windows(_rmdir,rmdir)(dir); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// construct trees from filenames; useful for cmirror summaries + +typedef struct stb_dirtree2 stb_dirtree2; + +struct stb_dirtree2 +{ + stb_dirtree2 **subdirs; + + // make convenient for stb_summarize_tree + int num_subdir; + float weight; + + // actual data + char *fullpath; + char *relpath; + char **files; +}; + +STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count); +STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count); +STB_EXTERN int stb_dir_is_prefix(char *dir, int dirlen, char *file); + +#ifdef STB_DEFINE + +int stb_dir_is_prefix(char *dir, int dirlen, char *file) +{ + if (dirlen == 0) return STB_TRUE; + if (stb_strnicmp(dir, file, dirlen)) return STB_FALSE; + if (file[dirlen] == '/' || file[dirlen] == '\\') return STB_TRUE; + return STB_FALSE; +} + +stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count) +{ + char buffer1[1024]; + int i; + int dlen = strlen(src), elen; + stb_dirtree2 *d; + char ** descendents = NULL; + char ** files = NULL; + char *s; + if (!count) return NULL; + // first find all the ones that belong here... note this is will take O(NM) with N files and M subdirs + for (i=0; i < count; ++i) { + if (stb_dir_is_prefix(src, dlen, filelist[i])) { + stb_arr_push(descendents, filelist[i]); + } + } + if (descendents == NULL) + return NULL; + elen = dlen; + // skip a leading slash + if (elen == 0 && (descendents[0][0] == '/' || descendents[0][0] == '\\')) + ++elen; + else if (elen) + ++elen; + // now extract all the ones that have their root here + for (i=0; i < stb_arr_len(descendents);) { + if (!stb_strchr2(descendents[i]+elen, '/', '\\')) { + stb_arr_push(files, descendents[i]); + descendents[i] = descendents[stb_arr_len(descendents)-1]; + stb_arr_pop(descendents); + } else + ++i; + } + // now create a record + d = (stb_dirtree2 *) malloc(sizeof(*d)); + d->files = files; + d->subdirs = NULL; + d->fullpath = strdup(src); + s = stb_strrchr2(d->fullpath, '/', '\\'); + if (s) + ++s; + else + s = d->fullpath; + d->relpath = s; + // now create the children + qsort(descendents, stb_arr_len(descendents), sizeof(char *), stb_qsort_stricmp(0)); + buffer1[0] = 0; + for (i=0; i < stb_arr_len(descendents); ++i) { + char buffer2[1024]; + char *s = descendents[i] + elen, *t; + t = stb_strchr2(s, '/', '\\'); + assert(t); + stb_strncpy(buffer2, descendents[i], t-descendents[i]+1); + if (stb_stricmp(buffer1, buffer2)) { + stb_dirtree2 *t = stb_dirtree2_from_files_relative(buffer2, descendents, stb_arr_len(descendents)); + assert(t != NULL); + strcpy(buffer1, buffer2); + stb_arr_push(d->subdirs, t); + } + } + d->num_subdir = stb_arr_len(d->subdirs); + d->weight = 0; + return d; +} + +stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count) +{ + return stb_dirtree2_from_files_relative("", filelist, count); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Checksums: CRC-32, ADLER32, SHA-1 +// +// CRC-32 and ADLER32 allow streaming blocks +// SHA-1 requires either a complete buffer, max size 2^32 - 73 +// or it can checksum directly from a file, max 2^61 + +#define STB_ADLER32_SEED 1 +#define STB_CRC32_SEED 0 // note that we logical NOT this in the code + +STB_EXTERN stb_uint + stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen); +STB_EXTERN stb_uint + stb_crc32_block(stb_uint crc32, stb_uchar *buffer, stb_uint len); +STB_EXTERN stb_uint stb_crc32(unsigned char *buffer, stb_uint len); + +STB_EXTERN void stb_sha1( + unsigned char output[20], unsigned char *buffer, unsigned int len); +STB_EXTERN int stb_sha1_file(unsigned char output[20], char *file); + +STB_EXTERN void stb_sha1_readable(char display[27], unsigned char sha[20]); + +#ifdef STB_DEFINE +stb_uint stb_crc32_block(stb_uint crc, unsigned char *buffer, stb_uint len) +{ + static stb_uint crc_table[256]; + stb_uint i,j,s; + crc = ~crc; + + if (crc_table[1] == 0) + for(i=0; i < 256; i++) { + for (s=i, j=0; j < 8; ++j) + s = (s >> 1) ^ (s & 1 ? 0xedb88320 : 0); + crc_table[i] = s; + } + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +stb_uint stb_crc32(unsigned char *buffer, stb_uint len) +{ + return stb_crc32_block(0, buffer, len); +} + +stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= blocklen; + blocklen = 5552; + } + return (s2 << 16) + s1; +} + +static void stb__sha1(stb_uchar *chunk, stb_uint h[5]) +{ + int i; + stb_uint a,b,c,d,e; + stb_uint w[80]; + + for (i=0; i < 16; ++i) + w[i] = stb_big32(&chunk[i*4]); + for (i=16; i < 80; ++i) { + stb_uint t; + t = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; + w[i] = (t + t) | (t >> 31); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + e = h[4]; + + #define STB__SHA1(k,f) \ + { \ + stb_uint temp = (a << 5) + (a >> 27) + (f) + e + (k) + w[i]; \ + e = d; \ + d = c; \ + c = (b << 30) + (b >> 2); \ + b = a; \ + a = temp; \ + } + + i=0; + for (; i < 20; ++i) STB__SHA1(0x5a827999, d ^ (b & (c ^ d)) ); + for (; i < 40; ++i) STB__SHA1(0x6ed9eba1, b ^ c ^ d ); + for (; i < 60; ++i) STB__SHA1(0x8f1bbcdc, (b & c) + (d & (b ^ c)) ); + for (; i < 80; ++i) STB__SHA1(0xca62c1d6, b ^ c ^ d ); + + #undef STB__SHA1 + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + h[4] += e; +} + +void stb_sha1(stb_uchar output[20], stb_uchar *buffer, stb_uint len) +{ + unsigned char final_block[128]; + stb_uint end_start, final_len, j; + int i; + + stb_uint h[5]; + + h[0] = 0x67452301; + h[1] = 0xefcdab89; + h[2] = 0x98badcfe; + h[3] = 0x10325476; + h[4] = 0xc3d2e1f0; + + // we need to write padding to the last one or two + // blocks, so build those first into 'final_block' + + // we have to write one special byte, plus the 8-byte length + + // compute the block where the data runs out + end_start = len & ~63; + + // compute the earliest we can encode the length + if (((len+9) & ~63) == end_start) { + // it all fits in one block, so fill a second-to-last block + end_start -= 64; + } + + final_len = end_start + 128; + + // now we need to copy the data in + assert(end_start + 128 >= len+9); + assert(end_start < len || len < 64-9); + + j = 0; + if (end_start > len) + j = (stb_uint) - (int) end_start; + + for (; end_start + j < len; ++j) + final_block[j] = buffer[end_start + j]; + final_block[j++] = 0x80; + while (j < 128-5) // 5 byte length, so write 4 extra padding bytes + final_block[j++] = 0; + // big-endian size + final_block[j++] = len >> 29; + final_block[j++] = len >> 21; + final_block[j++] = len >> 13; + final_block[j++] = len >> 5; + final_block[j++] = len << 3; + assert(j == 128 && end_start + j == final_len); + + for (j=0; j < final_len; j += 64) { // 512-bit chunks + if (j+64 >= end_start+64) + stb__sha1(&final_block[j - end_start], h); + else + stb__sha1(&buffer[j], h); + } + + for (i=0; i < 5; ++i) { + output[i*4 + 0] = h[i] >> 24; + output[i*4 + 1] = h[i] >> 16; + output[i*4 + 2] = h[i] >> 8; + output[i*4 + 3] = h[i] >> 0; + } +} + +#ifdef _MSC_VER +int stb_sha1_file(stb_uchar output[20], char *file) +{ + int i; + stb_uint64 length=0; + unsigned char buffer[128]; + + FILE *f = stb__fopen(file, "rb"); + stb_uint h[5]; + + if (f == NULL) return 0; // file not found + + h[0] = 0x67452301; + h[1] = 0xefcdab89; + h[2] = 0x98badcfe; + h[3] = 0x10325476; + h[4] = 0xc3d2e1f0; + + for(;;) { + int n = fread(buffer, 1, 64, f); + if (n == 64) { + stb__sha1(buffer, h); + length += n; + } else { + int block = 64; + + length += n; + + buffer[n++] = 0x80; + + // if there isn't enough room for the length, double the block + if (n + 8 > 64) + block = 128; + + // pad to end + memset(buffer+n, 0, block-8-n); + + i = block - 8; + buffer[i++] = (stb_uchar) (length >> 53); + buffer[i++] = (stb_uchar) (length >> 45); + buffer[i++] = (stb_uchar) (length >> 37); + buffer[i++] = (stb_uchar) (length >> 29); + buffer[i++] = (stb_uchar) (length >> 21); + buffer[i++] = (stb_uchar) (length >> 13); + buffer[i++] = (stb_uchar) (length >> 5); + buffer[i++] = (stb_uchar) (length << 3); + assert(i == block); + stb__sha1(buffer, h); + if (block == 128) + stb__sha1(buffer+64, h); + else + assert(block == 64); + break; + } + } + fclose(f); + + for (i=0; i < 5; ++i) { + output[i*4 + 0] = h[i] >> 24; + output[i*4 + 1] = h[i] >> 16; + output[i*4 + 2] = h[i] >> 8; + output[i*4 + 3] = h[i] >> 0; + } + + return 1; +} +#endif // _MSC_VER + +// client can truncate this wherever they like +void stb_sha1_readable(char display[27], unsigned char sha[20]) +{ + char encoding[65] = "0123456789abcdefghijklmnopqrstuv" + "wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%$"; + int num_bits = 0, acc=0; + int i=0,o=0; + while (o < 26) { + int v; + // expand the accumulator + if (num_bits < 6) { + assert(i != 20); + acc += sha[i++] << num_bits; + num_bits += 8; + } + v = acc & ((1 << 6) - 1); + display[o++] = encoding[v]; + acc >>= 6; + num_bits -= 6; + } + assert(num_bits == 20*8 - 26*6); + display[o++] = encoding[acc]; +} + +#endif // STB_DEFINE + +/////////////////////////////////////////////////////////// +// +// simplified WINDOWS registry interface... hopefully +// we'll never actually use this? + +#if defined(_WIN32) + +STB_EXTERN void * stb_reg_open(char *mode, char *where); // mode: "rHKLM" or "rHKCU" or "w.." +STB_EXTERN void stb_reg_close(void *reg); +STB_EXTERN int stb_reg_read(void *zreg, char *str, void *data, unsigned long len); +STB_EXTERN int stb_reg_read_string(void *zreg, char *str, char *data, int len); +STB_EXTERN void stb_reg_write(void *zreg, char *str, void *data, unsigned long len); +STB_EXTERN void stb_reg_write_string(void *zreg, char *str, char *data); + +#if defined(STB_DEFINE) && !defined(STB_NO_REGISTRY) + +#define STB_HAS_REGISTRY + +#ifndef _WINDOWS_ + +#define HKEY void * + +STB_EXTERN __declspec(dllimport) long __stdcall RegCloseKey ( HKEY hKey ); +STB_EXTERN __declspec(dllimport) long __stdcall RegCreateKeyExA ( HKEY hKey, const char * lpSubKey, + int Reserved, char * lpClass, int dwOptions, + int samDesired, void *lpSecurityAttributes, HKEY * phkResult, int * lpdwDisposition ); +STB_EXTERN __declspec(dllimport) long __stdcall RegDeleteKeyA ( HKEY hKey, const char * lpSubKey ); +STB_EXTERN __declspec(dllimport) long __stdcall RegQueryValueExA ( HKEY hKey, const char * lpValueName, + int * lpReserved, unsigned long * lpType, unsigned char * lpData, unsigned long * lpcbData ); +STB_EXTERN __declspec(dllimport) long __stdcall RegSetValueExA ( HKEY hKey, const char * lpValueName, + int Reserved, int dwType, const unsigned char* lpData, int cbData ); +STB_EXTERN __declspec(dllimport) long __stdcall RegOpenKeyExA ( HKEY hKey, const char * lpSubKey, + int ulOptions, int samDesired, HKEY * phkResult ); + +#endif // _WINDOWS_ + +#define STB__REG_OPTION_NON_VOLATILE 0 +#define STB__REG_KEY_ALL_ACCESS 0x000f003f +#define STB__REG_KEY_READ 0x00020019 + +void *stb_reg_open(char *mode, char *where) +{ + long res; + HKEY base; + HKEY zreg; + if (!stb_stricmp(mode+1, "cu") || !stb_stricmp(mode+1, "hkcu")) + base = (HKEY) 0x80000001; // HKCU + else if (!stb_stricmp(mode+1, "lm") || !stb_stricmp(mode+1, "hklm")) + base = (HKEY) 0x80000002; // HKLM + else + return NULL; + + if (mode[0] == 'r') + res = RegOpenKeyExA(base, where, 0, STB__REG_KEY_READ, &zreg); + else if (mode[0] == 'w') + res = RegCreateKeyExA(base, where, 0, NULL, STB__REG_OPTION_NON_VOLATILE, STB__REG_KEY_ALL_ACCESS, NULL, &zreg, NULL); + else + return NULL; + + return res ? NULL : zreg; +} + +void stb_reg_close(void *reg) +{ + RegCloseKey((HKEY) reg); +} + +#define STB__REG_SZ 1 +#define STB__REG_BINARY 3 +#define STB__REG_DWORD 4 + +int stb_reg_read(void *zreg, char *str, void *data, unsigned long len) +{ + unsigned long type; + unsigned long alen = len; + if (0 == RegQueryValueExA((HKEY) zreg, str, 0, &type, (unsigned char *) data, &len)) + if (type == STB__REG_BINARY || type == STB__REG_SZ || type == STB__REG_DWORD) { + if (len < alen) + *((char *) data + len) = 0; + return 1; + } + return 0; +} + +void stb_reg_write(void *zreg, char *str, void *data, unsigned long len) +{ + if (zreg) + RegSetValueExA((HKEY) zreg, str, 0, STB__REG_BINARY, (const unsigned char *) data, len); +} + +int stb_reg_read_string(void *zreg, char *str, char *data, int len) +{ + if (!stb_reg_read(zreg, str, data, len)) return 0; + data[len-1] = 0; // force a 0 at the end of the string no matter what + return 1; +} + +void stb_reg_write_string(void *zreg, char *str, char *data) +{ + if (zreg) + RegSetValueExA((HKEY) zreg, str, 0, STB__REG_SZ, (const unsigned char *) data, strlen(data)+1); +} +#endif // STB_DEFINE +#endif // _WIN32 + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_cfg - This is like the registry, but the config info +// is all stored in plain old files where we can +// backup and restore them easily. The LOCATION of +// the config files is gotten from... the registry! + +#ifndef STB_NO_STB_STRINGS +typedef struct stb_cfg_st stb_cfg; + +STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode); // mode = "r", "w" +STB_EXTERN void stb_cfg_close(stb_cfg *cfg); +STB_EXTERN int stb_cfg_read(stb_cfg *cfg, char *key, void *value, int len); +STB_EXTERN void stb_cfg_write(stb_cfg *cfg, char *key, void *value, int len); +STB_EXTERN int stb_cfg_read_string(stb_cfg *cfg, char *key, char *value, int len); +STB_EXTERN void stb_cfg_write_string(stb_cfg *cfg, char *key, char *value); +STB_EXTERN int stb_cfg_delete(stb_cfg *cfg, char *key); +STB_EXTERN void stb_cfg_set_directory(char *dir); + +#ifdef STB_DEFINE + +typedef struct +{ + char *key; + void *value; + int value_len; +} stb__cfg_item; + +struct stb_cfg_st +{ + stb__cfg_item *data; + char *loaded_file; // this needs to be freed + FILE *f; // write the data to this file on close +}; + +static char *stb__cfg_sig = "sTbCoNfIg!\0\0"; +static char stb__cfg_dir[512]; +STB_EXTERN void stb_cfg_set_directory(char *dir) +{ + strcpy(stb__cfg_dir, dir); +} + +STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode) +{ + size_t len; + stb_cfg *z; + char file[512]; + if (mode[0] != 'r' && mode[0] != 'w') return NULL; + + if (!stb__cfg_dir[0]) { + #ifdef _WIN32 + strcpy(stb__cfg_dir, "c:/stb"); + #else + strcpy(stb__cfg_dir, "~/.stbconfig"); + #endif + + #ifdef STB_HAS_REGISTRY + { + void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); + if (reg) { + stb_reg_read_string(reg, "config_dir", stb__cfg_dir, sizeof(stb__cfg_dir)); + stb_reg_close(reg); + } + } + #endif + } + + sprintf(file, "%s/%s.cfg", stb__cfg_dir, config); + + z = (stb_cfg *) stb_malloc(0, sizeof(*z)); + z->data = NULL; + + z->loaded_file = stb_filec(file, &len); + if (z->loaded_file) { + char *s = z->loaded_file; + if (!memcmp(s, stb__cfg_sig, 12)) { + char *s = z->loaded_file + 12; + while (s < z->loaded_file + len) { + stb__cfg_item a; + int n = *(stb_int16 *) s; + a.key = s+2; + s = s+2 + n; + a.value_len = *(int *) s; + s += 4; + a.value = s; + s += a.value_len; + stb_arr_push(z->data, a); + } + assert(s == z->loaded_file + len); + } + } + + if (mode[0] == 'w') + z->f = fopen(file, "wb"); + else + z->f = NULL; + + return z; +} + +void stb_cfg_close(stb_cfg *z) +{ + if (z->f) { + int i; + // write the file out + fwrite(stb__cfg_sig, 12, 1, z->f); + for (i=0; i < stb_arr_len(z->data); ++i) { + stb_int16 n = strlen(z->data[i].key)+1; + fwrite(&n, 2, 1, z->f); + fwrite(z->data[i].key, n, 1, z->f); + fwrite(&z->data[i].value_len, 4, 1, z->f); + fwrite(z->data[i].value, z->data[i].value_len, 1, z->f); + } + fclose(z->f); + } + stb_arr_free(z->data); + stb_free(z); +} + +int stb_cfg_read(stb_cfg *z, char *key, void *value, int len) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) { + if (!stb_stricmp(z->data[i].key, key)) { + int n = stb_min(len, z->data[i].value_len); + memcpy(value, z->data[i].value, n); + if (n < len) + *((char *) value + n) = 0; + return 1; + } + } + return 0; +} + +void stb_cfg_write(stb_cfg *z, char *key, void *value, int len) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) + if (!stb_stricmp(z->data[i].key, key)) + break; + if (i == stb_arr_len(z->data)) { + stb__cfg_item p; + p.key = stb_strdup(key, z); + p.value = NULL; + p.value_len = 0; + stb_arr_push(z->data, p); + } + z->data[i].value = stb_malloc(z, len); + z->data[i].value_len = len; + memcpy(z->data[i].value, value, len); +} + +int stb_cfg_delete(stb_cfg *z, char *key) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) + if (!stb_stricmp(z->data[i].key, key)) { + stb_arr_fastdelete(z->data, i); + return 1; + } + return 0; +} + +int stb_cfg_read_string(stb_cfg *z, char *key, char *value, int len) +{ + if (!stb_cfg_read(z, key, value, len)) return 0; + value[len-1] = 0; + return 1; +} + +void stb_cfg_write_string(stb_cfg *z, char *key, char *value) +{ + stb_cfg_write(z, key, value, strlen(value)+1); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_dirtree - load a description of a directory tree +// uses a cache and stat()s the directories for changes +// MUCH faster on NTFS, _wrong_ on FAT32, so should +// ignore the db on FAT32 + +#ifdef _WIN32 + +typedef struct +{ + char * path; // full path from passed-in root + time_t last_modified; + int num_files; +} stb_dirtree_dir; + +typedef struct +{ + char *name; // name relative to path + int dir; // index into dirs[] array + unsigned long size; // size, max 4GB + time_t last_modified; +} stb_dirtree_file; + +typedef struct +{ + stb_dirtree_dir *dirs; + stb_dirtree_file *files; + + // internal use + void * string_pool; // used to free data en masse +} stb_dirtree; + +extern void stb_dirtree_free ( stb_dirtree *d ); +extern stb_dirtree *stb_dirtree_get ( char *dir); +extern stb_dirtree *stb_dirtree_get_dir ( char *dir, char *cache_dir); +extern stb_dirtree *stb_dirtree_get_with_file ( char *dir, char *cache_file); + +// get a list of all the files recursively underneath 'dir' +// +// cache_file is used to store a copy of the directory tree to speed up +// later calls. It must be unique to 'dir' and the current working +// directory! Otherwise who knows what will happen (a good solution +// is to put it _in_ dir, but this API doesn't force that). +// +// Also, it might be possible to break this if you have two different processes +// do a call to stb_dirtree_get() with the same cache file at about the same +// time, but I _think_ it might just work. + + +#ifdef STB_DEFINE +static void stb__dirtree_add_dir(char *path, time_t last, stb_dirtree *active) +{ + stb_dirtree_dir d; + d.last_modified = last; + d.num_files = 0; + d.path = stb_strdup(path, active->string_pool); + stb_arr_push(active->dirs, d); +} + +static void stb__dirtree_add_file(char *name, int dir, unsigned long size, time_t last, stb_dirtree *active) +{ + stb_dirtree_file f; + f.dir = dir; + f.size = size; + f.last_modified = last; + f.name = stb_strdup(name, active->string_pool); + ++active->dirs[dir].num_files; + stb_arr_push(active->files, f); +} + +static char stb__signature[12] = { 's', 'T', 'b', 'D', 'i', 'R', 't', 'R', 'e', 'E', '0', '1' }; + +static void stb__dirtree_save_db(char *filename, stb_dirtree *data, char *root) +{ + int i, num_dirs_final=0, num_files_final; + int *remap; + FILE *f = fopen(filename, "wb"); + if (!f) return; + + fwrite(stb__signature, sizeof(stb__signature), 1, f); + fwrite(root, strlen(root)+1, 1, f); + // need to be slightly tricky and not write out NULLed directories, nor the root + + // build remapping table of all dirs we'll be writing out + remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(data->dirs)); + for (i=0; i < stb_arr_len(data->dirs); ++i) { + if (data->dirs[i].path == NULL || 0==stb_stricmp(data->dirs[i].path, root)) { + remap[i] = -1; + } else { + remap[i] = num_dirs_final++; + } + } + + fwrite(&num_dirs_final, 4, 1, f); + for (i=0; i < stb_arr_len(data->dirs); ++i) { + if (remap[i] >= 0) { + fwrite(&data->dirs[i].last_modified, 4, 1, f); + stb_fput_string(f, data->dirs[i].path); + } + } + + num_files_final = 0; + for (i=0; i < stb_arr_len(data->files); ++i) + if (remap[data->files[i].dir] >= 0) + ++num_files_final; + + fwrite(&num_files_final, 4, 1, f); + for (i=0; i < stb_arr_len(data->files); ++i) { + if (remap[data->files[i].dir] >= 0) { + stb_fput_ranged(f, remap[data->files[i].dir], 0, num_dirs_final); + stb_fput_varlenu(f, data->files[i].size); + fwrite(&data->files[i].last_modified, 4, 1, f); + stb_fput_string(f, data->files[i].name); + } + } + + fclose(f); +} + +// note: stomps any existing data, rather than appending +static void stb__dirtree_load_db(char *filename, stb_dirtree *data, char *dir) +{ + char sig[2048]; + int i,n; + FILE *f = fopen(filename, "rb"); + + if (!f) return; + + data->string_pool = stb_malloc(0,1); + + fread(sig, sizeof(stb__signature), 1, f); + if (memcmp(stb__signature, sig, sizeof(stb__signature))) { fclose(f); return; } + if (!fread(sig, strlen(dir)+1, 1, f)) { fclose(f); return; } + if (stb_stricmp(sig,dir)) { fclose(f); return; } + + // we can just read them straight in, because they're guaranteed to be valid + fread(&n, 4, 1, f); + stb_arr_setlen(data->dirs, n); + for(i=0; i < stb_arr_len(data->dirs); ++i) { + fread(&data->dirs[i].last_modified, 4, 1, f); + data->dirs[i].path = stb_fget_string(f, data->string_pool); + if (data->dirs[i].path == NULL) goto bail; + } + fread(&n, 4, 1, f); + stb_arr_setlen(data->files, n); + for (i=0; i < stb_arr_len(data->files); ++i) { + data->files[i].dir = stb_fget_ranged(f, 0, stb_arr_len(data->dirs)); + data->files[i].size = stb_fget_varlenu(f); + fread(&data->files[i].last_modified, 4, 1, f); + data->files[i].name = stb_fget_string(f, data->string_pool); + if (data->files[i].name == NULL) goto bail; + } + + if (0) { + bail: + stb_arr_free(data->dirs); + stb_arr_free(data->files); + } + fclose(f); +} + +static void stb__dirtree_scandir(char *path, time_t last_time, stb_dirtree *active) +{ + // this is dumb depth first; theoretically it might be faster + // to fully traverse each directory before visiting its children, + // but it's complicated and didn't seem like a gain in the test app + + int n; + + struct _wfinddata_t c_file; + #ifdef STB_PTR64 + intptr_t hFile; + #else + long hFile; + #endif + stb__wchar full_path[1024]; + int has_slash; + + has_slash = (path[0] && path[strlen(path)-1] == '/'); + if (has_slash) + swprintf((wchar_t *)full_path, L"%s*", stb__from_utf8(path)); + else + swprintf((wchar_t *)full_path, L"%s/*", stb__from_utf8(path)); + + // it's possible this directory is already present: that means it was in the + // cache, but its parent wasn't... in that case, we're done with it + for (n=0; n < stb_arr_len(active->dirs); ++n) + if (0 == stb_stricmp(active->dirs[n].path, path)) + return; + + // otherwise, we need to add it + stb__dirtree_add_dir(path, last_time, active); + n = stb_arr_lastn(active->dirs); + + if( (hFile = _wfindfirst((const wchar_t *)full_path, &c_file )) != -1L ) { + do { + if (c_file.attrib & _A_SUBDIR) { + // ignore subdirectories starting with '.', e.g. "." and ".." + if (c_file.name[0] != '.') { + char *new_path = (char *) full_path; + char *temp = stb__to_utf8((stb__wchar *)c_file.name); + if (has_slash) + sprintf(new_path, "%s%s", path, temp); + else + sprintf(new_path, "%s/%s", path, temp); + stb__dirtree_scandir(new_path, c_file.time_write, active); + } + } else { + char *temp = stb__to_utf8((stb__wchar *)c_file.name); + stb__dirtree_add_file(temp, n, c_file.size, c_file.time_write, active); + } + } while( _wfindnext( hFile, &c_file ) == 0 ); + + _findclose( hFile ); + } +} + +// scan the database and see if it's all valid +static int stb__dirtree_update_db(stb_dirtree *db, stb_dirtree *active) +{ + int changes_detected = STB_FALSE; + int i; + int *remap; + int *rescan=NULL; + remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(db->dirs)); + memset(remap, 0, sizeof(remap[0]) * stb_arr_len(db->dirs)); + rescan = NULL; + + for (i=0; i < stb_arr_len(db->dirs); ++i) { + struct _stat info; + if (0 == _stat(db->dirs[i].path, &info)) { + if (info.st_mode & _S_IFDIR) { + // it's still a directory, as expected + if (info.st_mtime > db->dirs[i].last_modified) { + // it's changed! force a rescan + // we don't want to scan it until we've stat()d its + // subdirs, though, so we queue it + stb_arr_push(rescan, i); + // update the last_mod time + db->dirs[i].last_modified = info.st_mtime; + // ignore existing files in this dir + remap[i] = -1; + changes_detected = STB_TRUE; + } else { + // it hasn't changed, just copy it through unchanged + stb__dirtree_add_dir(db->dirs[i].path, db->dirs[i].last_modified, active); + remap[i] = stb_arr_lastn(active->dirs); + } + } else { + // this path used to refer to a directory, but now it's a file! + // assume that the parent directory is going to be forced to rescan anyway + goto delete_entry; + } + } else { + delete_entry: + // directory no longer exists, so don't copy it + // we don't free it because it's in the string pool now + db->dirs[i].path = NULL; + remap[i] = -1; + changes_detected = STB_TRUE; + } + } + + // at this point, we have: + // + // <rescan> holds a list of directory indices that need to be scanned due to being out of date + // <remap> holds the directory index in <active> for each dir in <db>, if it exists; -1 if not + // directories in <rescan> are not in <active> yet + + // so we can go ahead and remap all the known files right now + for (i=0; i < stb_arr_len(db->files); ++i) { + int dir = db->files[i].dir; + if (remap[dir] >= 0) { + stb__dirtree_add_file(db->files[i].name, remap[dir], db->files[i].size, db->files[i].last_modified, active); + } + } + + // at this point we're done with db->files, and done with remap + free(remap); + + // now scan those directories using the standard scan + for (i=0; i < stb_arr_len(rescan); ++i) { + int z = rescan[i]; + stb__dirtree_scandir(db->dirs[z].path, db->dirs[z].last_modified, active); + } + stb_arr_free(rescan); + + return changes_detected; +} + +static void stb__dirtree_free_raw(stb_dirtree *d) +{ + stb_free(d->string_pool); + stb_arr_free(d->dirs); + stb_arr_free(d->files); +} + +stb_dirtree *stb_dirtree_get_with_file(char *dir, char *cache_file) +{ + stb_dirtree *output = (stb_dirtree *) malloc(sizeof(*output)); + stb_dirtree db,active; + int prev_dir_count, cache_mismatch; + + char *stripped_dir; // store the directory name without a trailing '/' or '\\' + + // load the database of last-known state on disk + db.string_pool = NULL; + db.files = NULL; + db.dirs = NULL; + + stripped_dir = stb_strip_final_slash(strdup(dir)); + + if (cache_file != NULL) + stb__dirtree_load_db(cache_file, &db, stripped_dir); + + active.files = NULL; + active.dirs = NULL; + active.string_pool = stb_malloc(0,1); // @TODO: share string pools between both? + + // check all the directories in the database; make note if + // anything we scanned had changed, and rescan those things + cache_mismatch = stb__dirtree_update_db(&db, &active); + + // check the root tree + prev_dir_count = stb_arr_len(active.dirs); // record how many directories we've seen + + stb__dirtree_scandir(stripped_dir, 0, &active); // no last_modified time available for root + + // done with the DB; write it back out if any changes, i.e. either + // 1. any inconsistency found between cached information and actual disk + // or 2. if scanning the root found any new directories--which we detect because + // more than one directory got added to the active db during that scan + if (cache_mismatch || stb_arr_len(active.dirs) > prev_dir_count+1) + stb__dirtree_save_db(cache_file, &active, stripped_dir); + + free(stripped_dir); + + stb__dirtree_free_raw(&db); + + *output = active; + return output; +} + +stb_dirtree *stb_dirtree_get_dir(char *dir, char *cache_dir) +{ + int i; + stb_uint8 sha[20]; + char dir_lower[1024]; + char cache_file[1024],*s; + if (cache_dir == NULL) + return stb_dirtree_get_with_file(dir, NULL); + strcpy(dir_lower, dir); + stb_tolower(dir_lower); + stb_sha1(sha, (unsigned char *) dir_lower, strlen(dir_lower)); + strcpy(cache_file, cache_dir); + s = cache_file + strlen(cache_file); + if (s[-1] != '/' && s[-1] != '\\') *s++ = '/'; + strcpy(s, "dirtree_"); + s += strlen(s); + for (i=0; i < 8; ++i) { + char *hex = "0123456789abcdef"; + stb_uint z = sha[i]; + *s++ = hex[z >> 4]; + *s++ = hex[z & 15]; + } + strcpy(s, ".bin"); + return stb_dirtree_get_with_file(dir, cache_file); +} + +stb_dirtree *stb_dirtree_get(char *dir) +{ + char cache_dir[256]; + strcpy(cache_dir, "c:/stb"); + #ifdef STB_HAS_REGISTRY + { + void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); + if (reg) { + stb_reg_read(reg, "dirtree", cache_dir, sizeof(cache_dir)); + stb_reg_close(reg); + } + } + #endif + return stb_dirtree_get_dir(dir, cache_dir); +} + +void stb_dirtree_free(stb_dirtree *d) +{ + stb__dirtree_free_raw(d); + free(d); +} +#endif // STB_DEFINE + +#endif // _WIN32 +#endif // STB_NO_STB_STRINGS + +////////////////////////////////////////////////////////////////////////////// +// +// STB_MALLOC_WRAPPER +// +// you can use the wrapper functions with your own malloc wrapper, +// or define STB_MALLOC_WRAPPER project-wide to have +// malloc/free/realloc/strdup all get vectored to it + +// this has too many very specific error messages you could google for and find in stb.h, +// so don't use it if they don't want any stb.h-identifiable strings +#if defined(STB_DEFINE) && !defined(STB_NO_STB_STRINGS) + +typedef struct +{ + void *p; + char *file; + int line; + int size; +} stb_malloc_record; + +#ifndef STB_MALLOC_HISTORY_COUNT +#define STB_MALLOC_HISTORY_COUNT 50 // 800 bytes +#endif + +stb_malloc_record *stb__allocations; +static int stb__alloc_size, stb__alloc_limit, stb__alloc_mask; +int stb__alloc_count; + +stb_malloc_record stb__alloc_history[STB_MALLOC_HISTORY_COUNT]; +int stb__history_pos; + +static int stb__hashfind(void *p) +{ + stb_uint32 h = stb_hashptr(p); + int s,n = h & stb__alloc_mask; + if (stb__allocations[n].p == p) + return n; + s = stb_rehash(h)|1; + for(;;) { + if (stb__allocations[n].p == NULL) + return -1; + n = (n+s) & stb__alloc_mask; + if (stb__allocations[n].p == p) + return n; + } +} + +int stb_wrapper_allocsize(void *p) +{ + int n = stb__hashfind(p); + if (n < 0) return 0; + return stb__allocations[n].size; +} + +static int stb__historyfind(void *p) +{ + int n = stb__history_pos; + int i; + for (i=0; i < STB_MALLOC_HISTORY_COUNT; ++i) { + if (--n < 0) n = STB_MALLOC_HISTORY_COUNT-1; + if (stb__alloc_history[n].p == p) + return n; + } + return -1; +} + +static void stb__add_alloc(void *p, int sz, char *file, int line); +static void stb__grow_alloc(void) +{ + int i,old_num = stb__alloc_size; + stb_malloc_record *old = stb__allocations; + if (stb__alloc_size == 0) + stb__alloc_size = 64; + else + stb__alloc_size *= 2; + + stb__allocations = (stb_malloc_record *) stb__realloc_raw(NULL, stb__alloc_size * sizeof(stb__allocations[0])); + if (stb__allocations == NULL) + stb_fatal("Internal error: couldn't grow malloc wrapper table"); + memset(stb__allocations, 0, stb__alloc_size * sizeof(stb__allocations[0])); + stb__alloc_limit = (stb__alloc_size*3)>>2; + stb__alloc_mask = stb__alloc_size-1; + + stb__alloc_count = 0; + + for (i=0; i < old_num; ++i) + if (old[i].p > STB_DEL) { + stb__add_alloc(old[i].p, old[i].size, old[i].file, old[i].line); + assert(stb__hashfind(old[i].p) >= 0); + } + for (i=0; i < old_num; ++i) + if (old[i].p > STB_DEL) + assert(stb__hashfind(old[i].p) >= 0); + stb__realloc_raw(old, 0); +} + +static void stb__add_alloc(void *p, int sz, char *file, int line) +{ + stb_uint32 h; + int n; + if (stb__alloc_count >= stb__alloc_limit) + stb__grow_alloc(); + h = stb_hashptr(p); + n = h & stb__alloc_mask; + if (stb__allocations[n].p > STB_DEL) { + int s = stb_rehash(h)|1; + do { + n = (n+s) & stb__alloc_mask; + } while (stb__allocations[n].p > STB_DEL); + } + assert(stb__allocations[n].p == NULL || stb__allocations[n].p == STB_DEL); + stb__allocations[n].p = p; + stb__allocations[n].size = sz; + stb__allocations[n].line = line; + stb__allocations[n].file = file; + ++stb__alloc_count; +} + +static void stb__remove_alloc(int n, char *file, int line) +{ + stb__alloc_history[stb__history_pos] = stb__allocations[n]; + stb__alloc_history[stb__history_pos].file = file; + stb__alloc_history[stb__history_pos].line = line; + if (++stb__history_pos == STB_MALLOC_HISTORY_COUNT) + stb__history_pos = 0; + stb__allocations[n].p = STB_DEL; + --stb__alloc_count; +} + +void stb_wrapper_malloc(void *p, int sz, char *file, int line) +{ + if (!p) return; + stb__add_alloc(p,sz,file,line); +} + +void stb_wrapper_free(void *p, char *file, int line) +{ + int n; + + if (p == NULL) return; + + n = stb__hashfind(p); + + if (n >= 0) + stb__remove_alloc(n, file, line); + else { + // tried to free something we hadn't allocated! + n = stb__historyfind(p); + assert(0); /* NOTREACHED */ + if (n >= 0) + stb_fatal("Attempted to free %d-byte block %p at %s:%d previously freed/realloced at %s:%d", + stb__alloc_history[n].size, p, + file, line, + stb__alloc_history[n].file, stb__alloc_history[n].line); + else + stb_fatal("Attempted to free unknown block %p at %s:%d", p, file,line); + } +} + +void stb_wrapper_check(void *p) +{ + int n; + + if (p == NULL) return; + + n = stb__hashfind(p); + + if (n >= 0) return; + + for (n=0; n < stb__alloc_size; ++n) + if (stb__allocations[n].p == p) + stb_fatal("Internal error: pointer %p was allocated, but hash search failed", p); + + // tried to free something that wasn't allocated! + n = stb__historyfind(p); + if (n >= 0) + stb_fatal("Checked %d-byte block %p previously freed/realloced at %s:%d", + stb__alloc_history[n].size, p, + stb__alloc_history[n].file, stb__alloc_history[n].line); + stb_fatal("Checked unknown block %p"); +} + +void stb_wrapper_realloc(void *p, void *q, int sz, char *file, int line) +{ + int n; + if (p == NULL) { stb_wrapper_malloc(q, sz, file, line); return; } + if (q == NULL) return; // nothing happened + + n = stb__hashfind(p); + if (n == -1) { + // tried to free something we hadn't allocated! + // this is weird, though, because we got past the realloc! + n = stb__historyfind(p); + assert(0); /* NOTREACHED */ + if (n >= 0) + stb_fatal("Attempted to realloc %d-byte block %p at %s:%d previously freed/realloced at %s:%d", + stb__alloc_history[n].size, p, + file, line, + stb__alloc_history[n].file, stb__alloc_history[n].line); + else + stb_fatal("Attempted to realloc unknown block %p at %s:%d", p, file,line); + } else { + if (q == p) { + stb__allocations[n].size = sz; + stb__allocations[n].file = file; + stb__allocations[n].line = line; + } else { + stb__remove_alloc(n, file, line); + stb__add_alloc(q,sz,file,line); + } + } +} + +void stb_wrapper_listall(void (*func)(void *ptr, int sz, char *file, int line)) +{ + int i; + for (i=0; i < stb__alloc_size; ++i) + if (stb__allocations[i].p > STB_DEL) + func(stb__allocations[i].p , stb__allocations[i].size, + stb__allocations[i].file, stb__allocations[i].line); +} + +void stb_wrapper_dump(char *filename) +{ + int i; + FILE *f = fopen(filename, "w"); + if (!f) return; + for (i=0; i < stb__alloc_size; ++i) + if (stb__allocations[i].p > STB_DEL) + fprintf(f, "%p %7d - %4d %s\n", + stb__allocations[i].p , stb__allocations[i].size, + stb__allocations[i].line, stb__allocations[i].file); +} +#endif // STB_DEFINE + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_pointer_set +// +// +// For data structures that support querying by key, data structure +// classes always hand-wave away the issue of what to do if two entries +// have the same key: basically, store a linked list of all the nodes +// which have the same key (a LISP-style list). +// +// The thing is, it's not that trivial. If you have an O(log n) +// lookup data structure, but then n/4 items have the same value, +// you don't want to spend O(n) time scanning that list when +// deleting an item if you already have a pointer to the item. +// (You have to spend O(n) time enumerating all the items with +// a given key, sure, and you can't accelerate deleting a particular +// item if you only have the key, not a pointer to the item.) +// +// I'm going to call this data structure, whatever it turns out to +// be, a "pointer set", because we don't store any associated data for +// items in this data structure, we just answer the question of +// whether an item is in it or not (it's effectively one bit per pointer). +// Technically they don't have to be pointers; you could cast ints +// to (void *) if you want, but you can't store 0 or 1 because of the +// hash table. +// +// Since the fastest data structure we might want to add support for +// identical-keys to is a hash table with O(1)-ish lookup time, +// that means that the conceptual "linked list of all items with +// the same indexed value" that we build needs to have the same +// performance; that way when we index a table we think is arbitrary +// ints, but in fact half of them are 0, we don't get screwed. +// +// Therefore, it needs to be a hash table, at least when it gets +// large. On the other hand, when the data has totally arbitrary ints +// or floats, there won't be many collisions, and we'll have tons of +// 1-item bitmaps. That will be grossly inefficient as hash tables; +// trade-off; the hash table is reasonably efficient per-item when +// it's large, but not when it's small. So we need to do something +// Judy-like and use different strategies depending on the size. +// +// Like Judy, we'll use the bottom bit to encode the strategy: +// +// bottom bits: +// 00 - direct pointer +// 01 - 4-item bucket (16 bytes, no length, NULLs) +// 10 - N-item array +// 11 - hash table + +typedef struct stb_ps stb_ps; + +STB_EXTERN int stb_ps_find (stb_ps *ps, void *value); +STB_EXTERN stb_ps * stb_ps_add (stb_ps *ps, void *value); +STB_EXTERN stb_ps * stb_ps_remove(stb_ps *ps, void *value); +STB_EXTERN stb_ps * stb_ps_remove_any(stb_ps *ps, void **value); +STB_EXTERN void stb_ps_delete(stb_ps *ps); +STB_EXTERN int stb_ps_count (stb_ps *ps); + +STB_EXTERN stb_ps * stb_ps_copy (stb_ps *ps); +STB_EXTERN int stb_ps_subset(stb_ps *bigger, stb_ps *smaller); +STB_EXTERN int stb_ps_eq (stb_ps *p0, stb_ps *p1); + +STB_EXTERN void ** stb_ps_getlist (stb_ps *ps, int *count); +STB_EXTERN int stb_ps_writelist(stb_ps *ps, void **list, int size ); + +// enum and fastlist don't allocate storage, but you must consume the +// list before there's any chance the data structure gets screwed up; +STB_EXTERN int stb_ps_enum (stb_ps *ps, void *data, + int (*func)(void *value, void*data) ); +STB_EXTERN void ** stb_ps_fastlist(stb_ps *ps, int *count); +// result: +// returns a list, *count is the length of that list, +// but some entries of the list may be invalid; +// test with 'stb_ps_fastlist_valid(x)' + +#define stb_ps_fastlist_valid(x) ((stb_uinta) (x) > 1) + +#ifdef STB_DEFINE + +enum +{ + STB_ps_direct = 0, + STB_ps_bucket = 1, + STB_ps_array = 2, + STB_ps_hash = 3, +}; + +#define STB_BUCKET_SIZE 4 + +typedef struct +{ + void *p[STB_BUCKET_SIZE]; +} stb_ps_bucket; +#define GetBucket(p) ((stb_ps_bucket *) ((char *) (p) - STB_ps_bucket)) +#define EncodeBucket(p) ((stb_ps *) ((char *) (p) + STB_ps_bucket)) + +static void stb_bucket_free(stb_ps_bucket *b) +{ + free(b); +} + +static stb_ps_bucket *stb_bucket_create2(void *v0, void *v1) +{ + stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b)); + b->p[0] = v0; + b->p[1] = v1; + b->p[2] = NULL; + b->p[3] = NULL; + return b; +} + +static stb_ps_bucket * stb_bucket_create3(void **v) +{ + stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b)); + b->p[0] = v[0]; + b->p[1] = v[1]; + b->p[2] = v[2]; + b->p[3] = NULL; + return b; +} + + +// could use stb_arr, but this will save us memory +typedef struct +{ + int count; + void *p[1]; +} stb_ps_array; +#define GetArray(p) ((stb_ps_array *) ((char *) (p) - STB_ps_array)) +#define EncodeArray(p) ((stb_ps *) ((char *) (p) + STB_ps_array)) + +static int stb_ps_array_max = 13; + +typedef struct +{ + int size, mask; + int count, count_deletes; + int grow_threshhold; + int shrink_threshhold; + int rehash_threshhold; + int any_offset; + void *table[1]; +} stb_ps_hash; +#define GetHash(p) ((stb_ps_hash *) ((char *) (p) - STB_ps_hash)) +#define EncodeHash(p) ((stb_ps *) ((char *) (p) + STB_ps_hash)) + +#define stb_ps_empty(v) (((stb_uint32) v) <= 1) + +static stb_ps_hash *stb_ps_makehash(int size, int old_size, void **old_data) +{ + int i; + stb_ps_hash *h = (stb_ps_hash *) malloc(sizeof(*h) + (size-1) * sizeof(h->table[0])); + assert(stb_is_pow2(size)); + h->size = size; + h->mask = size-1; + h->shrink_threshhold = (int) (0.3f * size); + h-> grow_threshhold = (int) (0.8f * size); + h->rehash_threshhold = (int) (0.9f * size); + h->count = 0; + h->count_deletes = 0; + h->any_offset = 0; + memset(h->table, 0, size * sizeof(h->table[0])); + for (i=0; i < old_size; ++i) + if (!stb_ps_empty(old_data[i])) + stb_ps_add(EncodeHash(h), old_data[i]); + return h; +} + +void stb_ps_delete(stb_ps *ps) +{ + switch (3 & (int) ps) { + case STB_ps_direct: break; + case STB_ps_bucket: stb_bucket_free(GetBucket(ps)); break; + case STB_ps_array : free(GetArray(ps)); break; + case STB_ps_hash : free(GetHash(ps)); break; + } +} + +stb_ps *stb_ps_copy(stb_ps *ps) +{ + int i; + // not a switch: order based on expected performance/power-law distribution + switch (3 & (int) ps) { + case STB_ps_direct: return ps; + case STB_ps_bucket: { + stb_ps_bucket *n = (stb_ps_bucket *) malloc(sizeof(*n)); + *n = *GetBucket(ps); + return EncodeBucket(n); + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + stb_ps_array *n = (stb_ps_array *) malloc(sizeof(*n) + stb_ps_array_max * sizeof(n->p[0])); + n->count = a->count; + for (i=0; i < a->count; ++i) + n->p[i] = a->p[i]; + return EncodeArray(n); + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + stb_ps_hash *n = stb_ps_makehash(h->size, h->size, h->table); + return EncodeHash(n); + } + } + assert(0); /* NOTREACHED */ + return NULL; +} + +int stb_ps_find(stb_ps *ps, void *value) +{ + int i, code = 3 & (int) ps; + assert((3 & (int) value) == STB_ps_direct); + assert(stb_ps_fastlist_valid(value)); + // not a switch: order based on expected performance/power-law distribution + if (code == STB_ps_direct) + return value == ps; + if (code == STB_ps_bucket) { + stb_ps_bucket *b = GetBucket(ps); + assert(STB_BUCKET_SIZE == 4); + if (b->p[0] == value || b->p[1] == value || + b->p[2] == value || b->p[3] == value) + return STB_TRUE; + return STB_FALSE; + } + if (code == STB_ps_array) { + stb_ps_array *a = GetArray(ps); + for (i=0; i < a->count; ++i) + if (a->p[i] == value) + return STB_TRUE; + return STB_FALSE; + } else { + stb_ps_hash *h = GetHash(ps); + stb_uint32 hash = stb_hashptr(value); + stb_uint32 s, n = hash & h->mask; + void **t = h->table; + if (t[n] == value) return STB_TRUE; + if (t[n] == NULL) return STB_FALSE; + s = stb_rehash(hash) | 1; + do { + n = (n + s) & h->mask; + if (t[n] == value) return STB_TRUE; + } while (t[n] != NULL); + return STB_FALSE; + } +} + +stb_ps * stb_ps_add (stb_ps *ps, void *value) +{ + #ifdef STB_DEBUG + assert(!stb_ps_find(ps,value)); + #endif + if (value == NULL) return ps; // ignore NULL adds to avoid bad breakage + assert((3 & (int) value) == STB_ps_direct); + assert(stb_ps_fastlist_valid(value)); + assert(value != STB_DEL); // STB_DEL is less likely + + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) return (stb_ps *) value; + return EncodeBucket(stb_bucket_create2(ps,value)); + + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + stb_ps_array *a; + assert(STB_BUCKET_SIZE == 4); + if (b->p[0] == NULL) { b->p[0] = value; return ps; } + if (b->p[1] == NULL) { b->p[1] = value; return ps; } + if (b->p[2] == NULL) { b->p[2] = value; return ps; } + if (b->p[3] == NULL) { b->p[3] = value; return ps; } + a = (stb_ps_array *) malloc(sizeof(*a) + 7 * sizeof(a->p[0])); // 8 slots, must be 2^k + memcpy(a->p, b, sizeof(*b)); + a->p[4] = value; + a->count = 5; + stb_bucket_free(b); + return EncodeArray(a); + } + + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + if (a->count == stb_ps_array_max) { + // promote from array to hash + stb_ps_hash *h = stb_ps_makehash(2 << stb_log2_ceil(a->count), a->count, a->p); + free(a); + return stb_ps_add(EncodeHash(h), value); + } + // do we need to resize the array? the array doubles in size when it + // crosses a power-of-two + if ((a->count & (a->count-1))==0) { + int newsize = a->count*2; + // clamp newsize to max if: + // 1. it's larger than max + // 2. newsize*1.5 is larger than max (to avoid extra resizing) + if (newsize + a->count > stb_ps_array_max) + newsize = stb_ps_array_max; + a = (stb_ps_array *) realloc(a, sizeof(*a) + (newsize-1) * sizeof(a->p[0])); + } + a->p[a->count++] = value; + return EncodeArray(a); + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + stb_uint32 hash = stb_hashptr(value); + stb_uint32 n = hash & h->mask; + void **t = h->table; + // find first NULL or STB_DEL entry + if (!stb_ps_empty(t[n])) { + stb_uint32 s = stb_rehash(hash) | 1; + do { + n = (n + s) & h->mask; + } while (!stb_ps_empty(t[n])); + } + if (t[n] == STB_DEL) + -- h->count_deletes; + t[n] = value; + ++ h->count; + if (h->count == h->grow_threshhold) { + stb_ps_hash *h2 = stb_ps_makehash(h->size*2, h->size, t); + free(h); + return EncodeHash(h2); + } + if (h->count + h->count_deletes == h->rehash_threshhold) { + stb_ps_hash *h2 = stb_ps_makehash(h->size, h->size, t); + free(h); + return EncodeHash(h2); + } + return ps; + } + } + return NULL; /* NOTREACHED */ +} + +stb_ps *stb_ps_remove(stb_ps *ps, void *value) +{ + #ifdef STB_DEBUG + assert(stb_ps_find(ps, value)); + #endif + assert((3 & (int) value) == STB_ps_direct); + if (value == NULL) return ps; // ignore NULL removes to avoid bad breakage + switch (3 & (int) ps) { + case STB_ps_direct: + return ps == value ? NULL : ps; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + int count=0; + assert(STB_BUCKET_SIZE == 4); + if (b->p[0] == value) b->p[0] = NULL; else count += (b->p[0] != NULL); + if (b->p[1] == value) b->p[1] = NULL; else count += (b->p[1] != NULL); + if (b->p[2] == value) b->p[2] = NULL; else count += (b->p[2] != NULL); + if (b->p[3] == value) b->p[3] = NULL; else count += (b->p[3] != NULL); + if (count == 1) { // shrink bucket at size 1 + value = b->p[0]; + if (value == NULL) value = b->p[1]; + if (value == NULL) value = b->p[2]; + if (value == NULL) value = b->p[3]; + assert(value != NULL); + stb_bucket_free(b); + return (stb_ps *) value; // return STB_ps_direct of value + } + return ps; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + int i; + for (i=0; i < a->count; ++i) { + if (a->p[i] == value) { + a->p[i] = a->p[--a->count]; + if (a->count == 3) { // shrink to bucket! + stb_ps_bucket *b = stb_bucket_create3(a->p); + free(a); + return EncodeBucket(b); + } + return ps; + } + } + return ps; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + stb_uint32 hash = stb_hashptr(value); + stb_uint32 s, n = hash & h->mask; + void **t = h->table; + if (t[n] != value) { + s = stb_rehash(hash) | 1; + do { + n = (n + s) & h->mask; + } while (t[n] != value); + } + t[n] = STB_DEL; + -- h->count; + ++ h->count_deletes; + // should we shrink down to an array? + if (h->count < stb_ps_array_max) { + int n = 1 << stb_log2_floor(stb_ps_array_max); + if (h->count < n) { + stb_ps_array *a = (stb_ps_array *) malloc(sizeof(*a) + (n-1) * sizeof(a->p[0])); + int i,j=0; + for (i=0; i < h->size; ++i) + if (!stb_ps_empty(t[i])) + a->p[j++] = t[i]; + assert(j == h->count); + a->count = j; + free(h); + return EncodeArray(a); + } + } + if (h->count == h->shrink_threshhold) { + stb_ps_hash *h2 = stb_ps_makehash(h->size >> 1, h->size, t); + free(h); + return EncodeHash(h2); + } + return ps; + } + } + return ps; /* NOTREACHED */ +} + +stb_ps *stb_ps_remove_any(stb_ps *ps, void **value) +{ + assert(ps != NULL); + switch (3 & (int) ps) { + case STB_ps_direct: + *value = ps; + return NULL; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + int count=0, slast=0, last=0; + assert(STB_BUCKET_SIZE == 4); + if (b->p[0]) { ++count; last = 0; } + if (b->p[1]) { ++count; slast = last; last = 1; } + if (b->p[2]) { ++count; slast = last; last = 2; } + if (b->p[3]) { ++count; slast = last; last = 3; } + *value = b->p[last]; + b->p[last] = 0; + if (count == 2) { + void *leftover = b->p[slast]; // second to last + stb_bucket_free(b); + return (stb_ps *) leftover; + } + return ps; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + *value = a->p[a->count-1]; + if (a->count == 4) + return stb_ps_remove(ps, *value); + --a->count; + return ps; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + void **t = h->table; + stb_uint32 n = h->any_offset; + while (stb_ps_empty(t[n])) + n = (n + 1) & h->mask; + *value = t[n]; + h->any_offset = (n+1) & h->mask; + // check if we need to skip down to the previous type + if (h->count-1 < stb_ps_array_max || h->count-1 == h->shrink_threshhold) + return stb_ps_remove(ps, *value); + t[n] = STB_DEL; + -- h->count; + ++ h->count_deletes; + return ps; + } + } + return ps; /* NOTREACHED */ +} + + +void ** stb_ps_getlist(stb_ps *ps, int *count) +{ + int i,n=0; + void **p; + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) { *count = 0; return NULL; } + p = (void **) malloc(sizeof(*p) * 1); + p[0] = ps; + *count = 1; + return p; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + p = (void **) malloc(sizeof(*p) * STB_BUCKET_SIZE); + for (i=0; i < STB_BUCKET_SIZE; ++i) + if (b->p[i] != NULL) + p[n++] = b->p[i]; + break; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + p = (void **) malloc(sizeof(*p) * a->count); + memcpy(p, a->p, sizeof(*p) * a->count); + *count = a->count; + return p; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + p = (void **) malloc(sizeof(*p) * h->count); + for (i=0; i < h->size; ++i) + if (!stb_ps_empty(h->table[i])) + p[n++] = h->table[i]; + break; + } + } + *count = n; + return p; +} + +int stb_ps_writelist(stb_ps *ps, void **list, int size ) +{ + int i,n=0; + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL || size <= 0) return 0; + list[0] = ps; + return 1; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + for (i=0; i < STB_BUCKET_SIZE; ++i) + if (b->p[i] != NULL && n < size) + list[n++] = b->p[i]; + return n; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + n = stb_min(size, a->count); + memcpy(list, a->p, sizeof(*list) * n); + return n; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + if (size <= 0) return 0; + for (i=0; i < h->count; ++i) { + if (!stb_ps_empty(h->table[i])) { + list[n++] = h->table[i]; + if (n == size) break; + } + } + return n; + } + } + return 0; /* NOTREACHED */ +} + +int stb_ps_enum(stb_ps *ps, void *data, int (*func)(void *value, void *data)) +{ + int i; + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) return STB_TRUE; + return func(ps, data); + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + for (i=0; i < STB_BUCKET_SIZE; ++i) + if (b->p[i] != NULL) + if (!func(b->p[i], data)) + return STB_FALSE; + return STB_TRUE; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + for (i=0; i < a->count; ++i) + if (!func(a->p[i], data)) + return STB_FALSE; + return STB_TRUE; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + for (i=0; i < h->count; ++i) + if (!stb_ps_empty(h->table[i])) + if (!func(h->table[i], data)) + return STB_FALSE; + return STB_TRUE; + } + } + return STB_TRUE; /* NOTREACHED */ +} + +int stb_ps_count (stb_ps *ps) +{ + switch (3 & (int) ps) { + case STB_ps_direct: + return ps != NULL; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + return (b->p[0] != NULL) + (b->p[1] != NULL) + + (b->p[2] != NULL) + (b->p[3] != NULL); + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + return a->count; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + return h->count; + } + } + return 0; +} + +void ** stb_ps_fastlist(stb_ps *ps, int *count) +{ + static void *storage; + + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) { *count = 0; return NULL; } + storage = ps; + *count = 1; + return &storage; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + *count = STB_BUCKET_SIZE; + return b->p; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + *count = a->count; + return a->p; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + *count = h->size; + return h->table; + } + } + return NULL; /* NOTREACHED */ +} + +int stb_ps_subset(stb_ps *bigger, stb_ps *smaller) +{ + int i, listlen; + void **list = stb_ps_fastlist(smaller, &listlen); + for(i=0; i < listlen; ++i) + if (stb_ps_fastlist_valid(list[i])) + if (!stb_ps_find(bigger, list[i])) + return 0; + return 1; +} + +int stb_ps_eq(stb_ps *p0, stb_ps *p1) +{ + if (stb_ps_count(p0) != stb_ps_count(p1)) + return 0; + return stb_ps_subset(p0, p1); +} + +#undef GetBucket +#undef GetArray +#undef GetHash + +#undef EncodeBucket +#undef EncodeArray +#undef EncodeHash + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Random Numbers via Meresenne Twister or LCG +// + +STB_EXTERN unsigned long stb_srandLCG(unsigned long seed); +STB_EXTERN unsigned long stb_randLCG(void); +STB_EXTERN double stb_frandLCG(void); + +STB_EXTERN void stb_srand(unsigned long seed); +STB_EXTERN unsigned long stb_rand(void); +STB_EXTERN double stb_frand(void); +STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz, + unsigned long seed); +STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz); + +STB_EXTERN unsigned long stb_randLCG_explicit(unsigned long seed); + +#define stb_rand_define(x,y) \ + \ + unsigned long x(void) \ + { \ + static unsigned long stb__rand = y; \ + stb__rand = stb__rand * 2147001325 + 715136305; /* BCPL */ \ + return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); \ + } + +#ifdef STB_DEFINE +unsigned long stb_randLCG_explicit(unsigned long seed) +{ + return seed * 2147001325 + 715136305; +} + +static unsigned long stb__rand_seed=0; + +unsigned long stb_srandLCG(unsigned long seed) +{ + unsigned long previous = stb__rand_seed; + stb__rand_seed = seed; + return previous; +} + +unsigned long stb_randLCG(void) +{ + stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator + // shuffle non-random bits to the middle, and xor to decorrelate with seed + return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16)); +} + +double stb_frandLCG(void) +{ + return stb_randLCG() / ((double) (1 << 16) * (1 << 16)); +} + +void stb_shuffle(void *p, size_t n, size_t sz, unsigned long seed) +{ + char *a; + unsigned long old_seed; + int i; + if (seed) + old_seed = stb_srandLCG(seed); + a = (char *) p + (n-1) * sz; + + for (i=n; i > 1; --i) { + int j = stb_randLCG() % i; + stb_swap(a, (char *) p + j * sz, sz); + a -= sz; + } + if (seed) + stb_srandLCG(old_seed); +} + +void stb_reverse(void *p, size_t n, size_t sz) +{ + int i,j = n-1; + for (i=0; i < j; ++i,--j) { + stb_swap((char *) p + i * sz, (char *) p + j * sz, sz); + } +} + +// public domain Mersenne Twister by Michael Brundage +#define STB__MT_LEN 624 + +int stb__mt_index = STB__MT_LEN*sizeof(unsigned long)+1; +unsigned long stb__mt_buffer[STB__MT_LEN]; + +void stb_srand(unsigned long seed) +{ + int i; + unsigned long old = stb_srandLCG(seed); + for (i = 0; i < STB__MT_LEN; i++) + stb__mt_buffer[i] = stb_randLCG(); + stb_srandLCG(old); + stb__mt_index = STB__MT_LEN*sizeof(unsigned long); +} + +#define STB__MT_IA 397 +#define STB__MT_IB (STB__MT_LEN - STB__MT_IA) +#define STB__UPPER_MASK 0x80000000 +#define STB__LOWER_MASK 0x7FFFFFFF +#define STB__MATRIX_A 0x9908B0DF +#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK) +#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A) + +unsigned long stb_rand() +{ + unsigned long * b = stb__mt_buffer; + int idx = stb__mt_index; + unsigned long s,r; + int i; + + if (idx >= STB__MT_LEN*sizeof(unsigned long)) { + if (idx > STB__MT_LEN*sizeof(unsigned long)) + stb_srand(0); + idx = 0; + i = 0; + for (; i < STB__MT_IB; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s); + } + for (; i < STB__MT_LEN-1; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s); + } + + s = STB__TWIST(b, STB__MT_LEN-1, 0); + b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s); + } + stb__mt_index = idx + sizeof(unsigned long); + + r = *(unsigned long *)((unsigned char *)b + idx); + + r ^= (r >> 11); + r ^= (r << 7) & 0x9D2C5680; + r ^= (r << 15) & 0xEFC60000; + r ^= (r >> 18); + + return r; +} + +double stb_frand(void) +{ + return stb_rand() / ((double) (1 << 16) * (1 << 16)); +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_dupe +// +// stb_dupe is a duplicate-finding system for very, very large data +// structures--large enough that sorting is too slow, but not so large +// that we can't keep all the data in memory. using it works as follows: +// +// 1. create an stb_dupe: +// provide a hash function +// provide an equality function +// provide an estimate for the size +// optionally provide a comparison function +// +// 2. traverse your data, 'adding' pointers to the stb_dupe +// +// 3. finish and ask for duplicates +// +// the stb_dupe will discard its intermediate data and build +// a collection of sorted lists of duplicates, with non-duplicate +// entries omitted entirely +// +// +// Implementation strategy: +// +// while collecting the N items, we keep a hash table of approximate +// size sqrt(N). (if you tell use the N up front, the hash table is +// just that size exactly) +// +// each entry in the hash table is just an stb__arr of pointers (no need +// to use stb_ps, because we don't need to delete from these) +// +// for step 3, for each entry in the hash table, we apply stb_dupe to it +// recursively. once the size gets small enough (or doesn't decrease +// significantly), we switch to either using qsort() on the comparison +// function, or else we just do the icky N^2 gather + + +typedef struct stb_dupe stb_dupe; + +typedef int (*stb_compare_func)(void *a, void *b); +typedef int (*stb_hash_func)(void *a, unsigned int seed); + +STB_EXTERN void stb_dupe_free(stb_dupe *sd); +STB_EXTERN stb_dupe *stb_dupe_create(stb_hash_func hash, + stb_compare_func eq, int size, stb_compare_func ineq); +STB_EXTERN void stb_dupe_add(stb_dupe *sd, void *item); +STB_EXTERN void stb_dupe_finish(stb_dupe *sd); +STB_EXTERN int stb_dupe_numsets(stb_dupe *sd); +STB_EXTERN void **stb_dupe_set(stb_dupe *sd, int num); +STB_EXTERN int stb_dupe_set_count(stb_dupe *sd, int num); + +struct stb_dupe +{ + void ***hash_table; + int hash_size; + int size_log2; + int population; + + int hash_shift; + stb_hash_func hash; + + stb_compare_func eq; + stb_compare_func ineq; + + void ***dupes; +}; + +#ifdef STB_DEFINE + +int stb_dupe_numsets(stb_dupe *sd) +{ + assert(sd->hash_table == NULL); + return stb_arr_len(sd->dupes); +} + +void **stb_dupe_set(stb_dupe *sd, int num) +{ + assert(sd->hash_table == NULL); + return sd->dupes[num]; +} + +int stb_dupe_set_count(stb_dupe *sd, int num) +{ + assert(sd->hash_table == NULL); + return stb_arr_len(sd->dupes[num]); +} + +stb_dupe *stb_dupe_create(stb_hash_func hash, stb_compare_func eq, int size, + stb_compare_func ineq) +{ + int i, hsize; + stb_dupe *sd = (stb_dupe *) malloc(sizeof(*sd)); + + sd->size_log2 = 4; + hsize = 1 << sd->size_log2; + while (hsize * hsize < size) { + ++sd->size_log2; + hsize *= 2; + } + + sd->hash = hash; + sd->eq = eq; + sd->ineq = ineq; + sd->hash_shift = 0; + + sd->population = 0; + sd->hash_size = hsize; + sd->hash_table = (void ***) malloc(sizeof(*sd->hash_table) * hsize); + for (i=0; i < hsize; ++i) + sd->hash_table[i] = NULL; + + sd->dupes = NULL; + + return sd; +} + +void stb_dupe_add(stb_dupe *sd, void *item) +{ + stb_uint32 hash = sd->hash(item, sd->hash_shift); + int z = hash & (sd->hash_size-1); + stb_arr_push(sd->hash_table[z], item); + ++sd->population; +} + +void stb_dupe_free(stb_dupe *sd) +{ + int i; + for (i=0; i < stb_arr_len(sd->dupes); ++i) + if (sd->dupes[i]) + stb_arr_free(sd->dupes[i]); + stb_arr_free(sd->dupes); + free(sd); +} + +static stb_compare_func stb__compare; + +static int stb__dupe_compare(const void *a, const void *b) +{ + void *p = *(void **) a; + void *q = *(void **) b; + + return stb__compare(p,q); +} + +void stb_dupe_finish(stb_dupe *sd) +{ + int i,j,k; + assert(sd->dupes == NULL); + for (i=0; i < sd->hash_size; ++i) { + void ** list = sd->hash_table[i]; + if (list != NULL) { + int n = stb_arr_len(list); + // @TODO: measure to find good numbers instead of just making them up! + int thresh = (sd->ineq ? 200 : 20); + // if n is large enough to be worth it, and n is smaller than + // before (so we can guarantee we'll use a smaller hash table); + // and there are enough hash bits left, assuming full 32-bit hash + if (n > thresh && n < (sd->population >> 3) && sd->hash_shift + sd->size_log2*2 < 32) { + + // recursively process this row using stb_dupe, O(N log log N) + + stb_dupe *d = stb_dupe_create(sd->hash, sd->eq, n, sd->ineq); + d->hash_shift = stb_randLCG_explicit(sd->hash_shift); + for (j=0; j < n; ++j) + stb_dupe_add(d, list[j]); + stb_arr_free(sd->hash_table[i]); + stb_dupe_finish(d); + for (j=0; j < stb_arr_len(d->dupes); ++j) { + stb_arr_push(sd->dupes, d->dupes[j]); + d->dupes[j] = NULL; // take over ownership + } + stb_dupe_free(d); + + } else if (sd->ineq) { + + // process this row using qsort(), O(N log N) + stb__compare = sd->ineq; + qsort(list, n, sizeof(list[0]), stb__dupe_compare); + + // find equal subsequences of the list + for (j=0; j < n-1; ) { + // find a subsequence from j..k + for (k=j; k < n; ++k) + // only use ineq so eq can be left undefined + if (sd->ineq(list[j], list[k])) + break; + // k is the first one not in the subsequence + if (k-j > 1) { + void **mylist = NULL; + stb_arr_setlen(mylist, k-j); + memcpy(mylist, list+j, sizeof(list[j]) * (k-j)); + stb_arr_push(sd->dupes, mylist); + } + j = k; + } + stb_arr_free(sd->hash_table[i]); + } else { + + // process this row using eq(), O(N^2) + for (j=0; j < n; ++j) { + if (list[j] != NULL) { + void **output = NULL; + for (k=j+1; k < n; ++k) { + if (sd->eq(list[j], list[k])) { + if (output == NULL) + stb_arr_push(output, list[j]); + stb_arr_push(output, list[k]); + list[k] = NULL; + } + } + list[j] = NULL; + if (output) + stb_arr_push(sd->dupes, output); + } + } + stb_arr_free(sd->hash_table[i]); + } + } + } + free(sd->hash_table); + sd->hash_table = NULL; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// templatized Sort routine +// +// This is an attempt to implement a templated sorting algorithm. +// To use it, you have to explicitly instantiate it as a _function_, +// then you call that function. This allows the comparison to be inlined, +// giving the sort similar performance to C++ sorts. +// +// It implements quicksort with three-way-median partitioning (generally +// well-behaved), with a final insertion sort pass. +// +// When you define the compare expression, you should assume you have +// elements of your array pointed to by 'a' and 'b', and perform the comparison +// on those. OR you can use one or more statements; first say '0;', then +// write whatever code you want, and compute the result into a variable 'c'. + +#define stb_declare_sort(FUNCNAME, TYPE) \ + void FUNCNAME(TYPE *p, int n) +#define stb_define_sort(FUNCNAME,TYPE,COMPARE) \ + stb__define_sort( void, FUNCNAME,TYPE,COMPARE) +#define stb_define_sort_static(FUNCNAME,TYPE,COMPARE) \ + stb__define_sort(static void, FUNCNAME,TYPE,COMPARE) + +#define stb__define_sort(MODE, FUNCNAME, TYPE, COMPARE) \ + \ +static void STB_(FUNCNAME,_ins_sort)(TYPE *p, int n) \ +{ \ + int i,j; \ + for (i=1; i < n; ++i) { \ + TYPE t = p[i], *a = &t; \ + j = i; \ + while (j > 0) { \ + TYPE *b = &p[j-1]; \ + int c = COMPARE; \ + if (!c) break; \ + p[j] = p[j-1]; \ + --j; \ + } \ + if (i != j) \ + p[j] = t; \ + } \ +} \ + \ +static void STB_(FUNCNAME,_quicksort)(TYPE *p, int n) \ +{ \ + /* threshhold for transitioning to insertion sort */ \ + while (n > 12) { \ + TYPE *a,*b,t; \ + int c01,c12,c,m,i,j; \ + \ + /* compute median of three */ \ + m = n >> 1; \ + a = &p[0]; \ + b = &p[m]; \ + c = COMPARE; \ + c01 = c; \ + a = &p[m]; \ + b = &p[n-1]; \ + c = COMPARE; \ + c12 = c; \ + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ \ + if (c01 != c12) { \ + /* otherwise, we'll need to swap something else to middle */ \ + int z; \ + a = &p[0]; \ + b = &p[n-1]; \ + c = COMPARE; \ + /* 0>mid && mid<n: 0>n => n; 0<n => 0 */ \ + /* 0<mid && mid>n: 0>n => 0; 0<n => n */ \ + z = (c == c12) ? 0 : n-1; \ + t = p[z]; \ + p[z] = p[m]; \ + p[m] = t; \ + } \ + /* now p[m] is the median-of-three */ \ + /* swap it to the beginning so it won't move around */ \ + t = p[0]; \ + p[0] = p[m]; \ + p[m] = t; \ + \ + /* partition loop */ \ + i=1; \ + j=n-1; \ + for(;;) { \ + /* handling of equality is crucial here */ \ + /* for sentinels & efficiency with duplicates */ \ + b = &p[0]; \ + for (;;++i) { \ + a=&p[i]; \ + c = COMPARE; \ + if (!c) break; \ + } \ + a = &p[0]; \ + for (;;--j) { \ + b=&p[j]; \ + c = COMPARE; \ + if (!c) break; \ + } \ + /* make sure we haven't crossed */ \ + if (i >= j) break; \ + t = p[i]; \ + p[i] = p[j]; \ + p[j] = t; \ + \ + ++i; \ + --j; \ + } \ + /* recurse on smaller side, iterate on larger */ \ + if (j < (n-i)) { \ + STB_(FUNCNAME,_quicksort)(p,j); \ + p = p+i; \ + n = n-i; \ + } else { \ + STB_(FUNCNAME,_quicksort)(p+i, n-i); \ + n = j; \ + } \ + } \ +} \ + \ +MODE FUNCNAME(TYPE *p, int n) \ +{ \ + STB_(FUNCNAME, _quicksort)(p, n); \ + STB_(FUNCNAME, _ins_sort)(p, n); \ +} \ + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_bitset an array of booleans indexed by integers +// + +typedef stb_uint32 stb_bitset; + +STB_EXTERN stb_bitset *stb_bitset_new(int value, int len); + +#define stb_bitset_clearall(arr,len) (memset(arr, 0, 4 * (len))) +#define stb_bitset_setall(arr,len) (memset(arr, 255, 4 * (len))) + +#define stb_bitset_setbit(arr,n) ((arr)[(n) >> 5] |= (1 << (n & 31))) +#define stb_bitset_clearbit(arr,n) ((arr)[(n) >> 5] &= ~(1 << (n & 31))) +#define stb_bitset_testbit(arr,n) ((arr)[(n) >> 5] & (1 << (n & 31))) + +STB_EXTERN stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len); + +STB_EXTERN int *stb_bitset_getlist(stb_bitset *out, int start, int end); + +STB_EXTERN int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len); +STB_EXTERN int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len); +STB_EXTERN int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len); +STB_EXTERN int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len); +STB_EXTERN int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len); + +#ifdef STB_DEFINE +int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + for (i=0; i < len; ++i) + if (p0[i] != p1[i]) return 0; + return 1; +} + +int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + for (i=0; i < len; ++i) + if (p0[i] & p1[i]) return 0; + return 1; +} + +int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + for (i=0; i < len; ++i) + if ((p0[i] | p1[i]) != 0xffffffff) return 0; + return 1; +} + +int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len) +{ + int i; + for (i=0; i < len; ++i) + if ((bigger[i] & smaller[i]) != smaller[i]) return 0; + return 1; +} + +stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len); + for (i=0; i < len; ++i) d[i] = p0[i] | p1[i]; + return d; +} + +int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i, changed=0; + for (i=0; i < len; ++i) { + stb_bitset d = p0[i] | p1[i]; + if (d != p0[i]) { + p0[i] = d; + changed = 1; + } + } + return changed; +} + +stb_bitset *stb_bitset_new(int value, int len) +{ + int i; + stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len); + if (value) value = 0xffffffff; + for (i=0; i < len; ++i) d[i] = value; + return d; +} + +int *stb_bitset_getlist(stb_bitset *out, int start, int end) +{ + int *list = NULL; + int i; + for (i=start; i < end; ++i) + if (stb_bitset_testbit(out, i)) + stb_arr_push(list, i); + return list; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_wordwrap quality word-wrapping for fixed-width fonts +// + +STB_EXTERN int stb_wordwrap(int *pairs, int pair_max, int count, char *str); +STB_EXTERN int *stb_wordwrapalloc(int count, char *str); + +#ifdef STB_DEFINE + +int stb_wordwrap(int *pairs, int pair_max, int count, char *str) +{ + int n=0,i=0, start=0,nonwhite=0; + if (pairs == NULL) pair_max = 0x7ffffff0; + else pair_max *= 2; + // parse + for(;;) { + int s=i; // first whitespace char; last nonwhite+1 + int w; // word start + // accept whitespace + while (isspace(str[i])) { + if (str[i] == '\n' || str[i] == '\r') { + if (str[i] + str[i+1] == '\n' + '\r') ++i; + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + nonwhite=0; + start = i+1; + s = start; + } + ++i; + } + if (i >= start+count) { + // we've gone off the end using whitespace + if (nonwhite) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + start = s = i; + nonwhite=0; + } else { + // output all the whitespace + while (i >= start+count) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = count; + n += 2; + start += count; + } + s = start; + } + } + + if (str[i] == 0) break; + // now scan out a word and see if it fits + w = i; + while (str[i] && !isspace(str[i])) { + ++i; + } + // wrapped? + if (i > start + count) { + // huge? + if (i-s <= count) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + start = w; + } else { + // This word is longer than one line. If we wrap it onto N lines + // there are leftover chars. do those chars fit on the cur line? + // But if we have leading whitespace, we force it to start here. + if ((w-start) + ((i-w) % count) <= count || !nonwhite) { + // output a full line + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = count; + n += 2; + start += count; + w = start; + } else { + // output a partial line, trimming trailing whitespace + if (s != start) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + start = w; + } + } + // now output full lines as needed + while (start + count <= i) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = count; + n += 2; + start += count; + } + } + } + nonwhite=1; + } + if (start < i) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = i-start; + n += 2; + } + return n>>1; +} + +int *stb_wordwrapalloc(int count, char *str) +{ + int n = stb_wordwrap(NULL,0,count,str); + int *z = NULL; + stb_arr_setlen(z, n*2); + stb_wordwrap(z, n, count, str); + return z; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_match: wildcards and regexping +// + +STB_EXTERN int stb_wildmatch (char *expr, char *candidate); +STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); +STB_EXTERN int stb_wildfind (char *expr, char *candidate); +STB_EXTERN int stb_wildfindi (char *expr, char *candidate); + +STB_EXTERN int stb_regex(char *regex, char *candidate); + +typedef struct stb_matcher stb_matcher; + +STB_EXTERN stb_matcher *stb_regex_matcher(char *regex); +STB_EXTERN int stb_matcher_match(stb_matcher *m, char *str); +STB_EXTERN int stb_matcher_find(stb_matcher *m, char *str); +STB_EXTERN void stb_matcher_free(stb_matcher *f); + +STB_EXTERN stb_matcher *stb_lex_matcher(void); +STB_EXTERN int stb_lex_item(stb_matcher *m, char *str, int result); +STB_EXTERN int stb_lex_item_wild(stb_matcher *matcher, char *regex, int result); +STB_EXTERN int stb_lex(stb_matcher *m, char *str, int *len); + + + +#ifdef STB_DEFINE + +static int stb__match_qstring(char *candidate, char *qstring, int qlen, int insensitive) +{ + int i; + if (insensitive) { + for (i=0; i < qlen; ++i) + if (qstring[i] == '?') { + if (!candidate[i]) return 0; + } else + if (tolower(qstring[i]) != tolower(candidate[i])) + return 0; + } else { + for (i=0; i < qlen; ++i) + if (qstring[i] == '?') { + if (!candidate[i]) return 0; + } else + if (qstring[i] != candidate[i]) + return 0; + } + return 1; +} + +static int stb__find_qstring(char *candidate, char *qstring, int qlen, int insensitive) +{ + char c; + + int offset=0; + while (*qstring == '?') { + ++qstring; + --qlen; + ++candidate; + if (qlen == 0) return 0; + if (*candidate == 0) return -1; + } + + c = *qstring++; + --qlen; + if (insensitive) c = tolower(c); + + while (candidate[offset]) { + if (c == (insensitive ? tolower(candidate[offset]) : candidate[offset])) + if (stb__match_qstring(candidate+offset+1, qstring, qlen, insensitive)) + return offset; + ++offset; + } + + return -1; +} + +int stb__wildmatch_raw2(char *expr, char *candidate, int search, int insensitive) +{ + int where=0; + int start = -1; + + if (!search) { + // parse to first '*' + if (*expr != '*') + start = 0; + while (*expr != '*') { + if (!*expr) + return *candidate == 0 ? 0 : -1; + if (*expr == '?') { + if (!*candidate) return -1; + } else { + if (insensitive) { + if (tolower(*candidate) != tolower(*expr)) + return -1; + } else + if (*candidate != *expr) + return -1; + } + ++candidate, ++expr, ++where; + } + } else { + // 0-length search string + if (!*expr) + return 0; + } + + assert(search || *expr == '*'); + if (!search) + ++expr; + + // implicit '*' at this point + + while (*expr) { + int o=0; + // combine redundant * characters + while (expr[0] == '*') ++expr; + + // ok, at this point, expr[-1] == '*', + // and expr[0] != '*' + + if (!expr[0]) return start >= 0 ? start : 0; + + // now find next '*' + o = 0; + while (expr[o] != '*') { + if (expr[o] == 0) + break; + ++o; + } + // if no '*', scan to end, then match at end + if (expr[o] == 0 && !search) { + int z; + for (z=0; z < o; ++z) + if (candidate[z] == 0) + return -1; + while (candidate[z]) + ++z; + // ok, now check if they match + if (stb__match_qstring(candidate+z-o, expr, o, insensitive)) + return start >= 0 ? start : 0; + return -1; + } else { + // if yes '*', then do stb__find_qmatch on the intervening chars + int n = stb__find_qstring(candidate, expr, o, insensitive); + if (n < 0) + return -1; + if (start < 0) + start = where + n; + expr += o; + candidate += n+o; + } + + if (*expr == 0) { + assert(search); + return start; + } + + assert(*expr == '*'); + ++expr; + } + + return start >= 0 ? start : 0; +} + +int stb__wildmatch_raw(char *expr, char *candidate, int search, int insensitive) +{ + char buffer[256]; + // handle multiple search strings + char *s = strchr(expr, ';'); + char *last = expr; + while (s) { + int z; + // need to allow for non-writeable strings... assume they're small + if (s - last < 256) { + stb_strncpy(buffer, last, s-last+1); + z = stb__wildmatch_raw2(buffer, candidate, search, insensitive); + } else { + *s = 0; + z = stb__wildmatch_raw2(last, candidate, search, insensitive); + *s = ';'; + } + if (z >= 0) return z; + last = s+1; + s = strchr(last, ';'); + } + return stb__wildmatch_raw2(last, candidate, search, insensitive); +} + +int stb_wildmatch(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 0,0) >= 0; +} + +int stb_wildmatchi(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 0,1) >= 0; +} + +int stb_wildfind(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 1,0); +} + +int stb_wildfindi(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 1,1); +} + +typedef struct +{ + stb_int16 transition[256]; +} stb_dfa; + +// an NFA node represents a state you're in; it then has +// an arbitrary number of edges dangling off of it +// note this isn't utf8-y +typedef struct +{ + stb_int16 match; // character/set to match + stb_uint16 node; // output node to go to +} stb_nfa_edge; + +typedef struct +{ + stb_int16 goal; // does reaching this win the prize? + stb_uint8 active; // is this in the active list + stb_nfa_edge *out; + stb_uint16 *eps; // list of epsilon closures +} stb_nfa_node; + +#define STB__DFA_UNDEF -1 +#define STB__DFA_GOAL -2 +#define STB__DFA_END -3 +#define STB__DFA_MGOAL -4 +#define STB__DFA_VALID 0 + +#define STB__NFA_STOP_GOAL -1 + +// compiled regexp +struct stb_matcher +{ + stb_uint16 start_node; + stb_int16 dfa_start; + stb_uint32 *charset; + int num_charset; + int match_start; + stb_nfa_node *nodes; + int does_lex; + + // dfa matcher + stb_dfa * dfa; + stb_uint32 * dfa_mapping; + stb_int16 * dfa_result; + int num_words_per_dfa; +}; + +static int stb__add_node(stb_matcher *matcher) +{ + stb_nfa_node z; + z.active = 0; + z.eps = 0; + z.goal = 0; + z.out = 0; + stb_arr_push(matcher->nodes, z); + return stb_arr_len(matcher->nodes)-1; +} + +static void stb__add_epsilon(stb_matcher *matcher, int from, int to) +{ + assert(from != to); + if (matcher->nodes[from].eps == NULL) + stb_arr_malloc((void **) &matcher->nodes[from].eps, matcher); + stb_arr_push(matcher->nodes[from].eps, to); +} + +static void stb__add_edge(stb_matcher *matcher, int from, int to, int type) +{ + stb_nfa_edge z = { type, to }; + if (matcher->nodes[from].out == NULL) + stb_arr_malloc((void **) &matcher->nodes[from].out, matcher); + stb_arr_push(matcher->nodes[from].out, z); +} + +static char *stb__reg_parse_alt(stb_matcher *m, int s, char *r, stb_uint16 *e); +static char *stb__reg_parse(stb_matcher *matcher, int start, char *regex, stb_uint16 *end) +{ + int n; + int last_start = -1; + stb_uint16 last_end = start; + + while (*regex) { + switch (*regex) { + case '(': + last_start = last_end; + regex = stb__reg_parse_alt(matcher, last_end, regex+1, &last_end); + if (regex == NULL || *regex != ')') + return NULL; + ++regex; + break; + + case '|': + case ')': + *end = last_end; + return regex; + + case '?': + if (last_start < 0) return NULL; + stb__add_epsilon(matcher, last_start, last_end); + ++regex; + break; + + case '*': + if (last_start < 0) return NULL; + stb__add_epsilon(matcher, last_start, last_end); + + // fall through + + case '+': + if (last_start < 0) return NULL; + stb__add_epsilon(matcher, last_end, last_start); + // prevent links back to last_end from chaining to last_start + n = stb__add_node(matcher); + stb__add_epsilon(matcher, last_end, n); + last_end = n; + ++regex; + break; + + case '{': // not supported! + // @TODO: given {n,m}, clone last_start to last_end m times, + // and include epsilons from start to first m-n blocks + return NULL; + + case '\\': + ++regex; + if (!*regex) return NULL; + + // fallthrough + default: // match exactly this character + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, *regex); + last_start = last_end; + last_end = n; + ++regex; + break; + + case '$': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, '\n'); + last_start = last_end; + last_end = n; + ++regex; + break; + + case '.': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -1); + last_start = last_end; + last_end = n; + ++regex; + break; + + case '[': { + stb_uint8 flags[256]; + int invert = 0,z; + ++regex; + if (matcher->num_charset == 0) { + matcher->charset = (stb_uint *) stb_malloc(matcher, sizeof(*matcher->charset) * 256); + memset(matcher->charset, 0, sizeof(*matcher->charset) * 256); + } + + memset(flags,0,sizeof(flags)); + + // leading ^ is special + if (*regex == '^') + ++regex, invert = 1; + + // leading ] is special + if (*regex == ']') { + flags[']'] = 1; + ++regex; + } + while (*regex != ']') { + stb_uint a; + if (!*regex) return NULL; + a = *regex++; + if (regex[0] == '-' && regex[1] != ']') { + stb_uint i,b = regex[1]; + regex += 2; + if (b == 0) return NULL; + if (a > b) return NULL; + for (i=a; i <= b; ++i) + flags[i] = 1; + } else + flags[a] = 1; + } + ++regex; + if (invert) { + int i; + for (i=0; i < 256; ++i) + flags[i] = 1-flags[i]; + } + + // now check if any existing charset matches + for (z=0; z < matcher->num_charset; ++z) { + int i, k[2] = { 0, 1 << z}; + for (i=0; i < 256; ++i) { + unsigned int f = k[flags[i]]; + if ((matcher->charset[i] & k[1]) != f) + break; + } + if (i == 256) break; + } + + if (z == matcher->num_charset) { + int i; + ++matcher->num_charset; + if (matcher->num_charset > 32) { + assert(0); /* NOTREACHED */ + return NULL; // too many charsets, oops + } + for (i=0; i < 256; ++i) + if (flags[i]) + matcher->charset[i] |= (1 << z); + } + + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -2 - z); + last_start = last_end; + last_end = n; + break; + } + } + } + *end = last_end; + return regex; +} + +static char *stb__reg_parse_alt(stb_matcher *matcher, int start, char *regex, stb_uint16 *end) +{ + stb_uint16 last_end = start; + stb_uint16 main_end; + + int head, tail; + + head = stb__add_node(matcher); + stb__add_epsilon(matcher, start, head); + + regex = stb__reg_parse(matcher, head, regex, &last_end); + if (regex == NULL) return NULL; + if (*regex == 0 || *regex == ')') { + *end = last_end; + return regex; + } + + main_end = last_end; + tail = stb__add_node(matcher); + + stb__add_epsilon(matcher, last_end, tail); + + // start alternatives from the same starting node; use epsilon + // transitions to combine their endings + while(*regex && *regex != ')') { + assert(*regex == '|'); + head = stb__add_node(matcher); + stb__add_epsilon(matcher, start, head); + regex = stb__reg_parse(matcher, head, regex+1, &last_end); + if (regex == NULL) + return NULL; + stb__add_epsilon(matcher, last_end, tail); + } + + *end = tail; + return regex; +} + +static char *stb__wild_parse(stb_matcher *matcher, int start, char *str, stb_uint16 *end) +{ + int n; + stb_uint16 last_end; + + last_end = stb__add_node(matcher); + stb__add_epsilon(matcher, start, last_end); + + while (*str) { + switch (*str) { + // fallthrough + default: // match exactly this character + n = stb__add_node(matcher); + if (toupper(*str) == tolower(*str)) { + stb__add_edge(matcher, last_end, n, *str); + } else { + stb__add_edge(matcher, last_end, n, tolower(*str)); + stb__add_edge(matcher, last_end, n, toupper(*str)); + } + last_end = n; + ++str; + break; + + case '?': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -1); + last_end = n; + ++str; + break; + + case '*': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -1); + stb__add_epsilon(matcher, last_end, n); + stb__add_epsilon(matcher, n, last_end); + last_end = n; + ++str; + break; + } + } + + // now require end of string to match + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, 0); + last_end = n; + + *end = last_end; + return str; +} + +static int stb__opt(stb_matcher *m, int n) +{ + for(;;) { + stb_nfa_node *p = &m->nodes[n]; + if (p->goal) return n; + if (stb_arr_len(p->out)) return n; + if (stb_arr_len(p->eps) != 1) return n; + n = p->eps[0]; + } +} + +static void stb__optimize(stb_matcher *m) +{ + // if the target of any edge is a node with exactly + // one out-epsilon, shorten it + int i,j; + for (i=0; i < stb_arr_len(m->nodes); ++i) { + stb_nfa_node *p = &m->nodes[i]; + for (j=0; j < stb_arr_len(p->out); ++j) + p->out[j].node = stb__opt(m,p->out[j].node); + for (j=0; j < stb_arr_len(p->eps); ++j) + p->eps[j] = stb__opt(m,p->eps[j] ); + } + m->start_node = stb__opt(m,m->start_node); +} + +void stb_matcher_free(stb_matcher *f) +{ + stb_free(f); +} + +static stb_matcher *stb__alloc_matcher(void) +{ + stb_matcher *matcher = (stb_matcher *) stb_malloc(0,sizeof(*matcher)); + + matcher->start_node = 0; + stb_arr_malloc((void **) &matcher->nodes, matcher); + matcher->num_charset = 0; + matcher->match_start = 0; + matcher->does_lex = 0; + + matcher->dfa_start = STB__DFA_UNDEF; + stb_arr_malloc((void **) &matcher->dfa, matcher); + stb_arr_malloc((void **) &matcher->dfa_mapping, matcher); + stb_arr_malloc((void **) &matcher->dfa_result, matcher); + + stb__add_node(matcher); + + return matcher; +} + +static void stb__lex_reset(stb_matcher *matcher) +{ + // flush cached dfa data + stb_arr_setlen(matcher->dfa, 0); + stb_arr_setlen(matcher->dfa_mapping, 0); + stb_arr_setlen(matcher->dfa_result, 0); + matcher->dfa_start = STB__DFA_UNDEF; +} + +stb_matcher *stb_regex_matcher(char *regex) +{ + char *z; + stb_uint16 end; + stb_matcher *matcher = stb__alloc_matcher(); + if (*regex == '^') { + matcher->match_start = 1; + ++regex; + } + + z = stb__reg_parse_alt(matcher, matcher->start_node, regex, &end); + + if (!z || *z) { + stb_free(matcher); + return NULL; + } + + ((matcher->nodes)[(int) end]).goal = STB__NFA_STOP_GOAL; + + return matcher; +} + +stb_matcher *stb_lex_matcher(void) +{ + stb_matcher *matcher = stb__alloc_matcher(); + + matcher->match_start = 1; + matcher->does_lex = 1; + + return matcher; +} + +int stb_lex_item(stb_matcher *matcher, char *regex, int result) +{ + char *z; + stb_uint16 end; + + z = stb__reg_parse_alt(matcher, matcher->start_node, regex, &end); + + if (z == NULL) + return 0; + + stb__lex_reset(matcher); + + matcher->nodes[(int) end].goal = result; + return 1; +} + +int stb_lex_item_wild(stb_matcher *matcher, char *regex, int result) +{ + char *z; + stb_uint16 end; + + z = stb__wild_parse(matcher, matcher->start_node, regex, &end); + + if (z == NULL) + return 0; + + stb__lex_reset(matcher); + + matcher->nodes[(int) end].goal = result; + return 1; +} + +static void stb__clear(stb_matcher *m, stb_uint16 *list) +{ + int i; + for (i=0; i < stb_arr_len(list); ++i) + m->nodes[(int) list[i]].active = 0; +} + +static int stb__clear_goalcheck(stb_matcher *m, stb_uint16 *list) +{ + int i, t=0; + for (i=0; i < stb_arr_len(list); ++i) { + t += m->nodes[(int) list[i]].goal; + m->nodes[(int) list[i]].active = 0; + } + return t; +} + +static stb_uint16 * stb__add_if_inactive(stb_matcher *m, stb_uint16 *list, int n) +{ + if (!m->nodes[n].active) { + stb_arr_push(list, n); + m->nodes[n].active = 1; + } + return list; +} + +static stb_uint16 * stb__eps_closure(stb_matcher *m, stb_uint16 *list) +{ + int i,n = stb_arr_len(list); + + for(i=0; i < n; ++i) { + stb_uint16 *e = m->nodes[(int) list[i]].eps; + if (e) { + int j,k = stb_arr_len(e); + for (j=0; j < k; ++j) + list = stb__add_if_inactive(m, list, e[j]); + n = stb_arr_len(list); + } + } + + return list; +} + +int stb_matcher_match(stb_matcher *m, char *str) +{ + int result = 0; + int i,j,y,z; + stb_uint16 *previous = NULL; + stb_uint16 *current = NULL; + stb_uint16 *temp; + + stb_arr_setsize(previous, 4); + stb_arr_setsize(current, 4); + + previous = stb__add_if_inactive(m, previous, m->start_node); + previous = stb__eps_closure(m,previous); + stb__clear(m, previous); + + while (*str && stb_arr_len(previous)) { + y = stb_arr_len(previous); + for (i=0; i < y; ++i) { + stb_nfa_node *n = &m->nodes[(int) previous[i]]; + z = stb_arr_len(n->out); + for (j=0; j < z; ++j) { + if (n->out[j].match >= 0) { + if (n->out[j].match == *str) + current = stb__add_if_inactive(m, current, n->out[j].node); + } else if (n->out[j].match == -1) { + if (*str != '\n') + current = stb__add_if_inactive(m, current, n->out[j].node); + } else if (n->out[j].match < -1) { + int z = -n->out[j].match - 2; + if (m->charset[(stb_uint8) *str] & (1 << z)) + current = stb__add_if_inactive(m, current, n->out[j].node); + } + } + } + stb_arr_setlen(previous, 0); + + temp = previous; + previous = current; + current = temp; + + previous = stb__eps_closure(m,previous); + stb__clear(m, previous); + + ++str; + } + + // transition to pick up a '$' at the end + y = stb_arr_len(previous); + for (i=0; i < y; ++i) + m->nodes[(int) previous[i]].active = 1; + + for (i=0; i < y; ++i) { + stb_nfa_node *n = &m->nodes[(int) previous[i]]; + z = stb_arr_len(n->out); + for (j=0; j < z; ++j) { + if (n->out[j].match == '\n') + current = stb__add_if_inactive(m, current, n->out[j].node); + } + } + + previous = stb__eps_closure(m,previous); + stb__clear(m, previous); + + y = stb_arr_len(previous); + for (i=0; i < y; ++i) + if (m->nodes[(int) previous[i]].goal) + result = 1; + + stb_arr_free(previous); + stb_arr_free(current); + + return result && *str == 0; +} + +stb_int16 stb__get_dfa_node(stb_matcher *m, stb_uint16 *list) +{ + stb_uint16 node; + stb_uint32 data[8], *state, *newstate; + int i,j,n; + + state = (stb_uint32 *) stb_temp(data, m->num_words_per_dfa * 4); + memset(state, 0, m->num_words_per_dfa*4); + + n = stb_arr_len(list); + for (i=0; i < n; ++i) { + int x = list[i]; + state[x >> 5] |= 1 << (x & 31); + } + + // @TODO use a hash table + n = stb_arr_len(m->dfa_mapping); + i=j=0; + for(; j < n; ++i, j += m->num_words_per_dfa) { + // @TODO special case for <= 32 + if (!memcmp(state, m->dfa_mapping + j, m->num_words_per_dfa*4)) { + node = i; + goto done; + } + } + + assert(stb_arr_len(m->dfa) == i); + node = i; + + newstate = stb_arr_addn(m->dfa_mapping, m->num_words_per_dfa); + memcpy(newstate, state, m->num_words_per_dfa*4); + + // set all transitions to 'unknown' + stb_arr_add(m->dfa); + memset(m->dfa[i].transition, -1, sizeof(m->dfa[i].transition)); + + if (m->does_lex) { + int result = -1; + n = stb_arr_len(list); + for (i=0; i < n; ++i) { + if (m->nodes[(int) list[i]].goal > result) + result = m->nodes[(int) list[i]].goal; + } + + stb_arr_push(m->dfa_result, result); + } + +done: + stb_tempfree(data, state); + return node; +} + +static int stb__matcher_dfa(stb_matcher *m, char *str_c, int *len) +{ + stb_uint8 *str = (stb_uint8 *) str_c; + stb_int16 node,prevnode; + stb_dfa *trans; + int match_length = 0; + stb_int16 match_result=0; + + if (m->dfa_start == STB__DFA_UNDEF) { + stb_uint16 *list; + + m->num_words_per_dfa = (stb_arr_len(m->nodes)+31) >> 5; + stb__optimize(m); + + list = stb__add_if_inactive(m, NULL, m->start_node); + list = stb__eps_closure(m,list); + if (m->does_lex) { + m->dfa_start = stb__get_dfa_node(m,list); + stb__clear(m, list); + // DON'T allow start state to be a goal state! + // this allows people to specify regexes that can match 0 + // characters without them actually matching (also we don't + // check _before_ advancing anyway + if (m->dfa_start <= STB__DFA_MGOAL) + m->dfa_start = -(m->dfa_start - STB__DFA_MGOAL); + } else { + if (stb__clear_goalcheck(m, list)) + m->dfa_start = STB__DFA_GOAL; + else + m->dfa_start = stb__get_dfa_node(m,list); + } + stb_arr_free(list); + } + + prevnode = STB__DFA_UNDEF; + node = m->dfa_start; + trans = m->dfa; + + if (m->dfa_start == STB__DFA_GOAL) + return 1; + + for(;;) { + assert(node >= STB__DFA_VALID); + + // fast inner DFA loop; especially if STB__DFA_VALID is 0 + + do { + prevnode = node; + node = trans[node].transition[*str++]; + } while (node >= STB__DFA_VALID); + + assert(node >= STB__DFA_MGOAL - stb_arr_len(m->dfa)); + assert(node < stb_arr_len(m->dfa)); + + // special case for lex: need _longest_ match, so notice goal + // state without stopping + if (node <= STB__DFA_MGOAL) { + match_length = str - (stb_uint8 *) str_c; + node = -(node - STB__DFA_MGOAL); + match_result = node; + continue; + } + + // slow NFA->DFA conversion + + // or we hit the goal or the end of the string, but those + // can only happen once per search... + + if (node == STB__DFA_UNDEF) { + // build a list -- @TODO special case <= 32 states + // heck, use a more compact data structure for <= 16 and <= 8 ?! + + // @TODO keep states/newstates around instead of reallocating them + stb_uint16 *states = NULL; + stb_uint16 *newstates = NULL; + int i,j,y,z; + stb_uint32 *flags = &m->dfa_mapping[prevnode * m->num_words_per_dfa]; + assert(prevnode != STB__DFA_UNDEF); + stb_arr_setsize(states, 4); + stb_arr_setsize(newstates,4); + for (j=0; j < m->num_words_per_dfa; ++j) { + for (i=0; i < 32; ++i) { + if (*flags & (1 << i)) + stb_arr_push(states, j*32+i); + } + ++flags; + } + // states is now the states we were in in the previous node; + // so now we can compute what node it transitions to on str[-1] + + y = stb_arr_len(states); + for (i=0; i < y; ++i) { + stb_nfa_node *n = &m->nodes[(int) states[i]]; + z = stb_arr_len(n->out); + for (j=0; j < z; ++j) { + if (n->out[j].match >= 0) { + if (n->out[j].match == str[-1] || (str[-1] == 0 && n->out[j].match == '\n')) + newstates = stb__add_if_inactive(m, newstates, n->out[j].node); + } else if (n->out[j].match == -1) { + if (str[-1] != '\n' && str[-1]) + newstates = stb__add_if_inactive(m, newstates, n->out[j].node); + } else if (n->out[j].match < -1) { + int z = -n->out[j].match - 2; + if (m->charset[str[-1]] & (1 << z)) + newstates = stb__add_if_inactive(m, newstates, n->out[j].node); + } + } + } + // AND add in the start state! + if (!m->match_start || (str[-1] == '\n' && !m->does_lex)) + newstates = stb__add_if_inactive(m, newstates, m->start_node); + // AND epsilon close it + newstates = stb__eps_closure(m, newstates); + // if it's a goal state, then that's all there is to it + if (stb__clear_goalcheck(m, newstates)) { + if (m->does_lex) { + match_length = str - (stb_uint8 *) str_c; + node = stb__get_dfa_node(m,newstates); + match_result = node; + node = -node + STB__DFA_MGOAL; + trans = m->dfa; // could have gotten realloc()ed + } else + node = STB__DFA_GOAL; + } else if (str[-1] == 0 || stb_arr_len(newstates) == 0) { + node = STB__DFA_END; + } else { + node = stb__get_dfa_node(m,newstates); + trans = m->dfa; // could have gotten realloc()ed + } + trans[prevnode].transition[str[-1]] = node; + if (node <= STB__DFA_MGOAL) + node = -(node - STB__DFA_MGOAL); + stb_arr_free(newstates); + stb_arr_free(states); + } + + if (node == STB__DFA_GOAL) { + return 1; + } + if (node == STB__DFA_END) { + if (m->does_lex) { + if (match_result) { + if (len) *len = match_length; + return m->dfa_result[(int) match_result]; + } + } + return 0; + } + + assert(node != STB__DFA_UNDEF); + } +} + +int stb_matcher_find(stb_matcher *m, char *str) +{ + assert(m->does_lex == 0); + return stb__matcher_dfa(m, str, NULL); +} + +int stb_lex(stb_matcher *m, char *str, int *len) +{ + assert(m->does_lex); + return stb__matcher_dfa(m, str, len); +} + +int stb_regex(char *regex, char *str) +{ + static stb_perfect p; + static stb_matcher ** matchers; + static char ** regexps; + static char ** regexp_cache; + static unsigned short *mapping; + int z = stb_perfect_hash(&p, (int) regex); + if (z >= 0) { + if (strcmp(regex, regexp_cache[(int) mapping[z]])) { + int i = mapping[z]; + stb_matcher_free(matchers[i]); + free(regexp_cache[i]); + regexps[i] = regex; + regexp_cache[i] = strdup(regex); + matchers[i] = stb_regex_matcher(regex); + } + } else { + int i,n; + if (regex == NULL) { + for (i=0; i < stb_arr_len(matchers); ++i) { + stb_matcher_free(matchers[i]); + free(regexp_cache[i]); + } + stb_arr_free(matchers); + stb_arr_free(regexps); + stb_arr_free(regexp_cache); + stb_perfect_destroy(&p); + free(mapping); mapping = NULL; + return -1; + } + stb_arr_push(regexps, regex); + stb_arr_push(regexp_cache, strdup(regex)); + stb_arr_push(matchers, stb_regex_matcher(regex)); + stb_perfect_destroy(&p); + n = stb_perfect_create(&p, (unsigned int *) (char **) regexps, stb_arr_len(regexps)); + mapping = (unsigned short *) realloc(mapping, n * sizeof(*mapping)); + for (i=0; i < stb_arr_len(regexps); ++i) + mapping[stb_perfect_hash(&p, (int) regexps[i])] = i; + z = stb_perfect_hash(&p, (int) regex); + } + return stb_matcher_find(matchers[(int) mapping[z]], str); +} + +#endif // STB_DEFINE + + +#if 0 +////////////////////////////////////////////////////////////////////////////// +// +// C source-code introspection +// + +// runtime structure +typedef struct +{ + char *name; + char *type; // base type + char *comment; // content of comment field + int size; // size of base type + int offset; // field offset + int arrcount[8]; // array sizes; -1 = pointer indirection; 0 = end of list +} stb_info_field; + +typedef struct +{ + char *structname; + int size; + int num_fields; + stb_info_field *fields; +} stb_info_struct; + +extern stb_info_struct stb_introspect_output[]; + +// + +STB_EXTERN void stb_introspect_precompiled(stb_info_struct *compiled); +STB_EXTERN void stb__introspect(char *path, char *file); + +#define stb_introspect_ship() stb__introspect(NULL, NULL, stb__introspect_output) + +#ifdef STB_SHIP +#define stb_introspect() stb_introspect_ship() +#define stb_introspect_path(p) stb_introspect_ship() +#else +// bootstrapping: define stb_introspect() (or 'path') the first time +#define stb_introspect() stb__introspect(NULL, __FILE__, NULL) +#define stb_introspect_auto() stb__introspect(NULL, __FILE__, stb__introspect_output) + +#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) +#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) +#endif + +#ifdef STB_DEFINE + +#ifndef STB_INTROSPECT_CPP + #ifdef __cplusplus + #define STB_INTROSPECT_CPP 1 + #else + #define STB_INTROSPECT_CPP 0 + #endif +#endif + +void stb_introspect_precompiled(stb_info_struct *compiled) +{ + +} + + +static void stb__introspect_filename(char *buffer, char *path) +{ + #if STB_INTROSPECT_CPP + sprintf(buffer, "%s/stb_introspect.cpp", path); + #else + sprintf(buffer, "%s/stb_introspect.c", path); + #endif +} + +static void stb__introspect_compute(char *path, char *file) +{ + int i; + char ** include_list = NULL; + char ** introspect_list = NULL; + FILE *f; + f = fopen(file, "w"); + if (!f) return; + + fputs("// if you get compiler errors, change the following 0 to a 1:\n", f); + fputs("#define STB_INTROSPECT_INVALID 0\n\n", f); + fputs("// this will force the code to compile, and force the introspector\n", f); + fputs("// to run and then exit, allowing you to recompile\n\n\n", f); + fputs("#include \"stb.h\"\n\n",f ); + fputs("#if STB_INTROSPECT_INVALID\n", f); + fputs(" stb_info_struct stb__introspect_output[] = { (void *) 1 }\n", f); + fputs("#else\n\n", f); + for (i=0; i < stb_arr_len(include_list); ++i) + fprintf(f, " #include \"%s\"\n", include_list[i]); + + fputs(" stb_info_struct stb__introspect_output[] =\n{\n", f); + for (i=0; i < stb_arr_len(introspect_list); ++i) + fprintf(f, " stb_introspect_%s,\n", introspect_list[i]); + fputs(" };\n", f); + fputs("#endif\n", f); + fclose(f); +} + +static stb_info_struct *stb__introspect_info; + +#ifndef STB_SHIP + +#endif + +void stb__introspect(char *path, char *file, stb_info_struct *compiled) +{ + static int first=1; + if (!first) return; + first=0; + + stb__introspect_info = compiled; + + #ifndef STB_SHIP + if (path || file) { + int bail_flag = compiled && compiled[0].structname == (void *) 1; + int needs_building = bail_flag; + struct stb__stat st; + char buffer[1024], buffer2[1024]; + if (!path) { + stb_splitpath(buffer, file, STB_PATH); + path = buffer; + } + // bail if the source path doesn't exist + if (!stb_fexists(path)) return; + + stb__introspect_filename(buffer2, path); + + // get source/include files timestamps, compare to output-file timestamp; + // if mismatched, regenerate + + if (stb__stat(buffer2, &st)) + needs_building = STB_TRUE; + + { + // find any file that contains an introspection command and is newer + // if needs_building is already true, we don't need to do this test, + // but we still need these arrays, so go ahead and get them + char **all[3]; + all[0] = stb_readdir_files_mask(path, "*.h"); + all[1] = stb_readdir_files_mask(path, "*.c"); + all[2] = stb_readdir_files_mask(path, "*.cpp"); + int i,j; + if (needs_building) { + for (j=0; j < 3; ++j) { + for (i=0; i < stb_arr_len(all[j]); ++i) { + struct stb__stat st2; + if (!stb__stat(all[j][i], &st2)) { + if (st.st_mtime < st2.st_mtime) { + char *z = stb_filec(all[j][i], NULL); + int found=STB_FALSE; + while (y) { + y = strstr(y, "//si"); + if (y && isspace(y[4])) { + found = STB_TRUE; + break; + } + } + needs_building = STB_TRUE; + goto done; + } + } + } + } + done:; + } + char *z = stb_filec(all[i], NULL), *y = z; + int found=STB_FALSE; + while (y) { + y = strstr(y, "//si"); + if (y && isspace(y[4])) { + found = STB_TRUE; + break; + } + } + if (found) + stb_arr_push(introspect_h, strdup(all[i])); + free(z); + } + } + stb_readdir_free(all); + if (!needs_building) { + for (i=0; i < stb_arr_len(introspect_h); ++i) { + struct stb__stat st2; + if (!stb__stat(introspect_h[i], &st2)) + if (st.st_mtime < st2.st_mtime) + needs_building = STB_TRUE; + } + } + + if (needs_building) { + stb__introspect_compute(path, buffer2); + } + } + } + #endif +} +#endif +#endif + +#ifdef STB_INTROSPECT +// compile-time code-generator +#define INTROSPECT(x) int main(int argc, char **argv) { stb__introspect(__FILE__); return 0; } +#define FILE(x) + +void stb__introspect(char *filename) +{ + char *file = stb_file(filename, NULL); + char *s = file, *t, **p; + char *out_name = "stb_introspect.c"; + char *out_path; + STB_ARR(char) filelist = NULL; + int i,n; + if (!file) stb_fatal("Couldn't open %s", filename); + + out_path = stb_splitpathdup(filename, STB_PATH); + + // search for the macros + while (*s) { + char buffer[256]; + while (*s && !isupper(*s)) ++s; + s = stb_strtok_invert(buffer, s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + s = stb_skipwhite(s); + if (*s == '(') { + ++s; + t = strchr(s, ')'); + if (t == NULL) stb_fatal("Error parsing %s", filename); + + } + } +} + + + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// STB-C sliding-window dictionary compression +// +// This uses a DEFLATE-style sliding window, but no bitwise entropy. +// Everything is on byte boundaries, so you could then apply a byte-wise +// entropy code, though that's nowhere near as effective. +// +// An STB-C stream begins with a 16-byte header: +// 4 bytes: 0x57 0xBC 0x00 0x00 +// 8 bytes: big-endian size of decompressed data, 64-bits +// 4 bytes: big-endian size of window (how far back decompressor may need) +// +// The following symbols appear in the stream (these were determined ad hoc, +// not by analysis): +// +// [dict] 00000100 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx +// [END] 00000101 11111010 cccccccc cccccccc cccccccc cccccccc +// [dict] 00000110 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx +// [literals] 00000111 zzzzzzzz zzzzzzzz +// [literals] 00001zzz zzzzzzzz +// [dict] 00010yyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx +// [dict] 00011yyy yyyyyyyy yyyyyyyy xxxxxxxx +// [literals] 001zzzzz +// [dict] 01yyyyyy yyyyyyyy xxxxxxxx +// [dict] 1xxxxxxx yyyyyyyy +// +// xxxxxxxx: match length - 1 +// yyyyyyyy: backwards distance - 1 +// zzzzzzzz: num literals - 1 +// cccccccc: adler32 checksum of decompressed data +// (all big-endian) + + +STB_EXTERN stb_uint stb_decompress_length(stb_uchar *input); +STB_EXTERN stb_uint stb_decompress(stb_uchar *out,stb_uchar *in,stb_uint len); +STB_EXTERN stb_uint stb_compress (stb_uchar *out,stb_uchar *in,stb_uint len); +STB_EXTERN void stb_compress_window(int z); +STB_EXTERN void stb_compress_hashsize(unsigned int z); + +STB_EXTERN int stb_compress_tofile(char *filename, char *in, stb_uint len); +STB_EXTERN int stb_compress_intofile(FILE *f, char *input, stb_uint len); +STB_EXTERN char *stb_decompress_fromfile(char *filename, stb_uint *len); + +STB_EXTERN int stb_compress_stream_start(FILE *f); +STB_EXTERN void stb_compress_stream_end(int close); +STB_EXTERN void stb_write(char *data, int data_len); + +#ifdef STB_DEFINE + +stb_uint stb_decompress_length(stb_uchar *input) +{ + return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; +} + +//////////////////// decompressor /////////////////////// + +// simple implementation that just writes whole thing into big block + +static unsigned char *stb__barrier; +static unsigned char *stb__barrier2; +static unsigned char *stb__barrier3; +static unsigned char *stb__barrier4; + +static stb_uchar *stb__dout; +static void stb__match(stb_uchar *data, stb_uint length) +{ + // INVERSE of memmove... write each byte before copying the next... + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } + while (length--) *stb__dout++ = *data++; +} + +static void stb__lit(stb_uchar *data, stb_uint length) +{ + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + memcpy(stb__dout, data, length); + stb__dout += length; +} + +#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) +#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) +#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) + +static stb_uchar *stb_decompress_token(stb_uchar *i) +{ + if (*i >= 0x20) { // use fewer if's for cases that expand small + if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; + else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { // more ifs for cases that expand large, since overhead is amortized + if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; + else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; + else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); + else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); + else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; + else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; + } + return i; +} + +stb_uint stb_decompress(stb_uchar *output, stb_uchar *i, stb_uint length) +{ + stb_uint olen; + if (stb__in4(0) != 0x57bC0000) return 0; + if (stb__in4(4) != 0) return 0; // error! stream is > 4GB + olen = stb_decompress_length(i); + stb__barrier2 = i; + stb__barrier3 = i+length; + stb__barrier = output + olen; + stb__barrier4 = output; + i += 16; + + stb__dout = output; + while (1) { + stb_uchar *old_i = i; + i = stb_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + assert(stb__dout == output + olen); + if (stb__dout != output + olen) return 0; + if (stb_adler32(1, output, olen) != (stb_uint) stb__in4(2)) + return 0; + return olen; + } else { + assert(0); /* NOTREACHED */ + return 0; + } + } + assert(stb__dout <= output + olen); + if (stb__dout > output + olen) + return 0; + } +} + +char *stb_decompress_fromfile(char *filename, unsigned int *len) +{ + unsigned int n; + char *q; + unsigned char *p; + FILE *f = fopen(filename, "rb"); if (f == NULL) return NULL; + fseek(f, 0, SEEK_END); + n = ftell(f); + fseek(f, 0, SEEK_SET); + p = (unsigned char * ) malloc(n); if (p == NULL) return NULL; + fread(p, 1, n, f); + fclose(f); + if (p == NULL) return NULL; + if (p[0] != 0x57 || p[1] != 0xBc || p[2] || p[3]) { free(p); return NULL; } + q = (char *) malloc(stb_decompress_length(p)+1); + if (!q) { free(p); return NULL; } + *len = stb_decompress((unsigned char *) q, p, n); + if (*len) q[*len] = 0; + free(p); + return q; +} + +#if 0 +// streaming decompressor + +static struct +{ + stb__uchar *in_buffer; + stb__uchar *match; + + stb__uint pending_literals; + stb__uint pending_match; +} xx; + + + +static void stb__match(stb_uchar *data, stb_uint length) +{ + // INVERSE of memmove... write each byte before copying the next... + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + while (length--) *stb__dout++ = *data++; +} + +static void stb__lit(stb_uchar *data, stb_uint length) +{ + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + memcpy(stb__dout, data, length); + stb__dout += length; +} + +static void sx_match(stb_uchar *data, stb_uint length) +{ + xx.match = data; + xx.pending_match = length; +} + +static void sx_lit(stb_uchar *data, stb_uint length) +{ + xx.pending_lit = length; +} + +static int stb_decompress_token_state(void) +{ + stb__uchar *i = xx.in_buffer; + + if (*i >= 0x20) { // use fewer if's for cases that expand small + if (*i >= 0x80) sx_match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) sx_match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; + else /* *i >= 0x20 */ sx_lit(i+1, i[0] - 0x20 + 1), i += 1; + } else { // more ifs for cases that expand large, since overhead is amortized + if (*i >= 0x18) sx_match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; + else if (*i >= 0x10) sx_match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; + else if (*i >= 0x08) sx_lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2; + else if (*i == 0x07) sx_lit(i+3, stb__in2(1) + 1), i += 3; + else if (*i == 0x06) sx_match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; + else if (*i == 0x04) sx_match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; + else return 0; + } + xx.in_buffer = i; + return 1; +} +#endif + + + +//////////////////// compressor /////////////////////// + +static unsigned int stb_matchlen(stb_uchar *m1, stb_uchar *m2, stb_uint maxlen) +{ + stb_uint i; + for (i=0; i < maxlen; ++i) + if (m1[i] != m2[i]) return i; + return i; +} + +// simple implementation that just takes the source data in a big block + +static stb_uchar *stb__out; +static FILE *stb__outfile; +static stb_uint stb__outbytes; + +static void stb__write(unsigned char v) +{ + fputc(v, stb__outfile); + ++stb__outbytes; +} + +#define stb_out(v) (stb__out ? *stb__out++ = (stb_uchar) (v) : stb__write((stb_uchar) (v))) + +static void stb_out2(stb_uint v) +{ + stb_out(v >> 8); + stb_out(v); +} + +static void stb_out3(stb_uint v) { stb_out(v >> 16); stb_out(v >> 8); stb_out(v); } +static void stb_out4(stb_uint v) { stb_out(v >> 24); stb_out(v >> 16); + stb_out(v >> 8 ); stb_out(v); } + +static void outliterals(stb_uchar *in, int numlit) +{ + while (numlit > 65536) { + outliterals(in,65536); + in += 65536; + numlit -= 65536; + } + + if (numlit == 0) ; + else if (numlit <= 32) stb_out (0x000020 + numlit-1); + else if (numlit <= 2048) stb_out2(0x000800 + numlit-1); + else /* numlit <= 65536) */ stb_out3(0x070000 + numlit-1); + + if (stb__out) { + memcpy(stb__out,in,numlit); + stb__out += numlit; + } else + fwrite(in, 1, numlit, stb__outfile); +} + +static int stb__window = 0x40000; // 256K +void stb_compress_window(int z) +{ + if (z >= 0x1000000) z = 0x1000000; // limit of implementation + if (z < 0x100) z = 0x100; // insanely small + stb__window = z; +} + +static int stb_not_crap(int best, int dist) +{ + return ((best > 2 && dist <= 0x00100) + || (best > 5 && dist <= 0x04000) + || (best > 7 && dist <= 0x80000)); +} + +static stb_uint stb__hashsize = 32768; +void stb_compress_hashsize(unsigned int y) +{ + unsigned int z = 1024; + while (z < y) z <<= 1; + stb__hashsize = z >> 2; // pass in bytes, store #pointers +} + +// note that you can play with the hashing functions all you +// want without needing to change the decompressor +#define stb__hc(q,h,c) (((h) << 7) + ((h) >> 25) + q[c]) +#define stb__hc2(q,h,c,d) (((h) << 14) + ((h) >> 18) + (q[c] << 7) + q[d]) +#define stb__hc3(q,c,d,e) ((q[c] << 14) + (q[d] << 7) + q[e]) + +static stb_uint32 stb__running_adler; + +static int stb_compress_chunk(stb_uchar *history, + stb_uchar *start, + stb_uchar *end, + int length, + int *pending_literals, + stb_uchar **chash, + stb_uint mask) +{ + int window = stb__window; + stb_uint match_max; + stb_uchar *lit_start = start - *pending_literals; + stb_uchar *q = start; + + #define STB__SCRAMBLE(h) (((h) + ((h) >> 16)) & mask) + + // stop short of the end so we don't scan off the end doing + // the hashing; this means we won't compress the last few bytes + // unless they were part of something longer + while (q < start+length && q+12 < end) { + int m; + stb_uint h1,h2,h3,h4, h; + stb_uchar *t; + int best = 2, dist=0; + + if (q+65536 > end) + match_max = end-q; + else + match_max = 65536; + + #define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap(b,d))) + + #define STB__TRY(t,p) /* avoid retrying a match we already tried */ \ + if (p ? dist != q-t : 1) \ + if ((m = stb_matchlen(t, q, match_max)) > best) \ + if (stb__nc(m,q-(t))) \ + best = m, dist = q - (t) + + // rather than search for all matches, only try 4 candidate locations, + // chosen based on 4 different hash functions of different lengths. + // this strategy is inspired by LZO; hashing is unrolled here using the + // 'hc' macro + h = stb__hc3(q,0, 1, 2); h1 = STB__SCRAMBLE(h); + t = chash[h1]; if (t) STB__TRY(t,0); + h = stb__hc2(q,h, 3, 4); h2 = STB__SCRAMBLE(h); + h = stb__hc2(q,h, 5, 6); t = chash[h2]; if (t) STB__TRY(t,1); + h = stb__hc2(q,h, 7, 8); h3 = STB__SCRAMBLE(h); + h = stb__hc2(q,h, 9,10); t = chash[h3]; if (t) STB__TRY(t,1); + h = stb__hc2(q,h,11,12); h4 = STB__SCRAMBLE(h); + t = chash[h4]; if (t) STB__TRY(t,1); + + // because we use a shared hash table, can only update it + // _after_ we've probed all of them + chash[h1] = chash[h2] = chash[h3] = chash[h4] = q; + + if (best > 2) + assert(dist > 0); + + // see if our best match qualifies + if (best < 3) { // fast path literals + ++q; + } else if (best > 2 && best <= 0x80 && dist <= 0x100) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out(0x80 + best-1); + stb_out(dist-1); + } else if (best > 5 && best <= 0x100 && dist <= 0x4000) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out2(0x4000 + dist-1); + stb_out(best-1); + } else if (best > 7 && best <= 0x100 && dist <= 0x80000) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out3(0x180000 + dist-1); + stb_out(best-1); + } else if (best > 8 && best <= 0x10000 && dist <= 0x80000) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out3(0x100000 + dist-1); + stb_out2(best-1); + } else if (best > 9 && dist <= 0x1000000) { + if (best > 65536) best = 65536; + outliterals(lit_start, q-lit_start); lit_start = (q += best); + if (best <= 0x100) { + stb_out(0x06); + stb_out3(dist-1); + stb_out(best-1); + } else { + stb_out(0x04); + stb_out3(dist-1); + stb_out2(best-1); + } + } else { // fallback literals if no match was a balanced tradeoff + ++q; + } + } + + // if we didn't get all the way, add the rest to literals + if (q-start < length) + q = start+length; + + // the literals are everything from lit_start to q + *pending_literals = (q - lit_start); + + stb__running_adler = stb_adler32(stb__running_adler, start, q - start); + return q - start; +} + +static int stb_compress_inner(stb_uchar *input, stb_uint length) +{ + int literals = 0; + stb_uint len,i; + + stb_uchar **chash; + chash = (stb_uchar**) malloc(stb__hashsize * sizeof(stb_uchar*)); + if (chash == NULL) return 0; // failure + for (i=0; i < stb__hashsize; ++i) + chash[i] = NULL; + + // stream signature + stb_out(0x57); stb_out(0xbc); + stb_out2(0); + + stb_out4(0); // 64-bit length requires 32-bit leading 0 + stb_out4(length); + stb_out4(stb__window); + + stb__running_adler = 1; + + len = stb_compress_chunk(input, input, input+length, length, &literals, chash, stb__hashsize-1); + assert(len == length); + + outliterals(input+length - literals, literals); + + free(chash); + + stb_out2(0x05fa); // end opcode + + stb_out4(stb__running_adler); + + return 1; // success +} + +stb_uint stb_compress(stb_uchar *out, stb_uchar *input, stb_uint length) +{ + stb__out = out; + stb__outfile = NULL; + + stb_compress_inner(input, length); + + return stb__out - out; +} + +int stb_compress_tofile(char *filename, char *input, unsigned int length) +{ + //int maxlen = length + 512 + (length >> 2); // total guess + //char *buffer = (char *) malloc(maxlen); + //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length); + + stb__out = NULL; + stb__outfile = fopen(filename, "wb"); + if (!stb__outfile) return 0; + + stb__outbytes = 0; + + if (!stb_compress_inner((stb_uchar*)input, length)) + return 0; + + fclose(stb__outfile); + + return stb__outbytes; +} + +int stb_compress_intofile(FILE *f, char *input, unsigned int length) +{ + //int maxlen = length + 512 + (length >> 2); // total guess + //char *buffer = (char*)malloc(maxlen); + //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length); + + stb__out = NULL; + stb__outfile = f; + if (!stb__outfile) return 0; + + stb__outbytes = 0; + + if (!stb_compress_inner((stb_uchar*)input, length)) + return 0; + + return stb__outbytes; +} + +////////////////////// streaming I/O version ///////////////////// + + +static size_t stb_out_backpatch_id(void) +{ + if (stb__out) + return (size_t) stb__out; + else + return ftell(stb__outfile); +} + +static void stb_out_backpatch(size_t id, stb_uint value) +{ + stb_uchar data[4] = { value >> 24, value >> 16, value >> 8, value }; + if (stb__out) { + memcpy((void *) id, data, 4); + } else { + stb_uint where = ftell(stb__outfile); + fseek(stb__outfile, id, SEEK_SET); + fwrite(data, 4, 1, stb__outfile); + fseek(stb__outfile, where, SEEK_SET); + } +} + +// ok, the wraparound buffer was a total failure. let's instead +// use a copying-in-place buffer, which lets us share the code. +// This is way less efficient but it'll do for now. + +static struct +{ + stb_uchar *buffer; + int size; // physical size of buffer in bytes + + int valid; // amount of valid data in bytes + int start; // bytes of data already output + + int window; + int fsize; + + int pending_literals; // bytes not-quite output but counted in start + int length_id; + + stb_uint total_bytes; + + stb_uchar **chash; + stb_uint hashmask; +} xtb; + +static int stb_compress_streaming_start(void) +{ + stb_uint i; + xtb.size = stb__window * 3; + xtb.buffer = (stb_uchar*)malloc(xtb.size); + if (!xtb.buffer) return 0; + + xtb.chash = (stb_uchar**)malloc(sizeof(*xtb.chash) * stb__hashsize); + if (!xtb.chash) { + free(xtb.buffer); + return 0; + } + + for (i=0; i < stb__hashsize; ++i) + xtb.chash[i] = NULL; + + xtb.hashmask = stb__hashsize-1; + + xtb.valid = 0; + xtb.start = 0; + xtb.window = stb__window; + xtb.fsize = stb__window; + xtb.pending_literals = 0; + xtb.total_bytes = 0; + + // stream signature + stb_out(0x57); stb_out(0xbc); stb_out2(0); + + stb_out4(0); // 64-bit length requires 32-bit leading 0 + + xtb.length_id = stb_out_backpatch_id(); + stb_out4(0); // we don't know the output length yet + + stb_out4(stb__window); + + stb__running_adler = 1; + + return 1; +} + +static int stb_compress_streaming_end(void) +{ + // flush out any remaining data + stb_compress_chunk(xtb.buffer, xtb.buffer+xtb.start, xtb.buffer+xtb.valid, + xtb.valid-xtb.start, &xtb.pending_literals, xtb.chash, xtb.hashmask); + + // write out pending literals + outliterals(xtb.buffer + xtb.valid - xtb.pending_literals, xtb.pending_literals); + + stb_out2(0x05fa); // end opcode + stb_out4(stb__running_adler); + + stb_out_backpatch(xtb.length_id, xtb.total_bytes); + + free(xtb.buffer); + free(xtb.chash); + return 1; +} + +void stb_write(char *data, int data_len) +{ + stb_uint i; + + // @TODO: fast path for filling the buffer and doing nothing else + // if (xtb.valid + data_len < xtb.size) + + xtb.total_bytes += data_len; + + while (data_len) { + // fill buffer + if (xtb.valid < xtb.size) { + int amt = xtb.size - xtb.valid; + if (data_len < amt) amt = data_len; + memcpy(xtb.buffer + xtb.valid, data, amt); + data_len -= amt; + data += amt; + xtb.valid += amt; + } + if (xtb.valid < xtb.size) + return; + + // at this point, the buffer is full + + // if we can process some data, go for it; make sure + // we leave an 'fsize's worth of data, though + if (xtb.start + xtb.fsize < xtb.valid) { + int amount = (xtb.valid - xtb.fsize) - xtb.start; + int n; + assert(amount > 0); + n = stb_compress_chunk(xtb.buffer, xtb.buffer + xtb.start, xtb.buffer + xtb.valid, + amount, &xtb.pending_literals, xtb.chash, xtb.hashmask); + xtb.start += n; + } + + assert(xtb.start + xtb.fsize >= xtb.valid); + // at this point, our future size is too small, so we + // need to flush some history. we, in fact, flush exactly + // one window's worth of history + + { + int flush = xtb.window; + assert(xtb.start >= flush); + assert(xtb.valid >= flush); + + // if 'pending literals' extends back into the shift region, + // write them out + if (xtb.start - xtb.pending_literals < flush) { + outliterals(xtb.buffer + xtb.start - xtb.pending_literals, xtb.pending_literals); + xtb.pending_literals = 0; + } + + // now shift the window + memmove(xtb.buffer, xtb.buffer + flush, xtb.valid - flush); + xtb.start -= flush; + xtb.valid -= flush; + + for (i=0; i <= xtb.hashmask; ++i) + if (xtb.chash[i] < xtb.buffer + flush) + xtb.chash[i] = NULL; + else + xtb.chash[i] -= flush; + } + // and now that we've made room for more data, go back to the top + } +} + +int stb_compress_stream_start(FILE *f) +{ + stb__out = NULL; + stb__outfile = f; + + if (f == NULL) + return 0; + + if (!stb_compress_streaming_start()) + return 0; + + return 1; +} + +void stb_compress_stream_end(int close) +{ + stb_compress_streaming_end(); + if (close && stb__outfile) { + fclose(stb__outfile); + } +} + +#endif // STB_DEFINE + +////////////////////////////////////////////////////////////////////////////// +// +// File abstraction... tired of not having this... we can write +// compressors to be layers over these that auto-close their children. + + +typedef struct stbfile +{ + int (*getbyte)(struct stbfile *); // -1 on EOF + unsigned int (*getdata)(struct stbfile *, void *block, unsigned int len); + + int (*putbyte)(struct stbfile *, int byte); + unsigned int (*putdata)(struct stbfile *, void *block, unsigned int len); + + unsigned int (*size)(struct stbfile *); + + unsigned int (*tell)(struct stbfile *); + void (*backpatch)(struct stbfile *, unsigned int tell, void *block, unsigned int len); + + void (*close)(struct stbfile *); + + FILE *f; // file to fread/fwrite + unsigned char *buffer; // input/output buffer + unsigned char *indata, *inend; // input buffer + union { + int various; + void *ptr; + }; +} stbfile; + +STB_EXTERN unsigned int stb_getc(stbfile *f); // read +STB_EXTERN int stb_putc(stbfile *f, int ch); // write +STB_EXTERN unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len); // read +STB_EXTERN unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len); // write +STB_EXTERN unsigned int stb_tell(stbfile *f); // read +STB_EXTERN unsigned int stb_size(stbfile *f); // read/write +STB_EXTERN void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len); // write + +#ifdef STB_DEFINE + +unsigned int stb_getc(stbfile *f) { return f->getbyte(f); } +int stb_putc(stbfile *f, int ch) { return f->putbyte(f, ch); } + +unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len) +{ + return f->getdata(f, buffer, len); +} +unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len) +{ + return f->putdata(f, buffer, len); +} +void stb_close(stbfile *f) +{ + f->close(f); + free(f); +} +unsigned int stb_tell(stbfile *f) { return f->tell(f); } +unsigned int stb_size(stbfile *f) { return f->size(f); } +void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len) +{ + f->backpatch(f,tell,buffer,len); +} + +// FILE * implementation +static int stb__fgetbyte(stbfile *f) { return fgetc(f->f); } +static int stb__fputbyte(stbfile *f, int ch) { return fputc(ch, f->f)==0; } +static unsigned int stb__fgetdata(stbfile *f, void *buffer, unsigned int len) { return fread(buffer,1,len,f->f); } +static unsigned int stb__fputdata(stbfile *f, void *buffer, unsigned int len) { return fwrite(buffer,1,len,f->f); } +static unsigned int stb__fsize(stbfile *f) { return stb_filelen(f->f); } +static unsigned int stb__ftell(stbfile *f) { return ftell(f->f); } +static void stb__fbackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len) +{ + fseek(f->f, where, SEEK_SET); + fwrite(buffer, 1, len, f->f); + fseek(f->f, 0, SEEK_END); +} +static void stb__fclose(stbfile *f) { fclose(f->f); } + +stbfile *stb_openf(FILE *f) +{ + stbfile m = { stb__fgetbyte, stb__fgetdata, + stb__fputbyte, stb__fputdata, + stb__fsize, stb__ftell, stb__fbackpatch, stb__fclose, + 0,0,0, }; + stbfile *z = (stbfile *) malloc(sizeof(*z)); + if (z) { + *z = m; + z->f = f; + } + return z; +} + +static int stb__nogetbyte(stbfile *f) { assert(0); return -1; } +static unsigned int stb__nogetdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; } +static int stb__noputbyte(stbfile *f, int ch) { assert(0); return 0; } +static unsigned int stb__noputdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; } +static void stb__nobackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len) { assert(0); } + +static int stb__bgetbyte(stbfile *s) +{ + if (s->indata < s->inend) + return *s->indata++; + else + return -1; +} + +static unsigned int stb__bgetdata(stbfile *s, void *buffer, unsigned int len) +{ + if (s->indata + len > s->inend) + len = s->inend - s->indata; + memcpy(buffer, s->indata, len); + s->indata += len; + return len; +} +static unsigned int stb__bsize(stbfile *s) { return s->inend - s->buffer; } +static unsigned int stb__btell(stbfile *s) { return s->indata - s->buffer; } + +static void stb__bclose(stbfile *s) +{ + if (s->various) + free(s->buffer); +} + +stbfile *stb_open_inbuffer(void *buffer, unsigned int len) +{ + stbfile m = { stb__bgetbyte, stb__bgetdata, + stb__noputbyte, stb__noputdata, + stb__bsize, stb__btell, stb__nobackpatch, stb__bclose }; + stbfile *z = (stbfile *) malloc(sizeof(*z)); + if (z) { + *z = m; + z->buffer = (unsigned char *) buffer; + z->indata = z->buffer; + z->inend = z->indata + len; + } + return z; +} + +stbfile *stb_open_inbuffer_free(void *buffer, unsigned int len) +{ + stbfile *z = stb_open_inbuffer(buffer, len); + if (z) + z->various = 1; // free + return z; +} + +#ifndef STB_VERSION +// if we've been cut-and-pasted elsewhere, you get a limited +// version of stb_open, without the 'k' flag and utf8 support +static void stb__fclose2(stbfile *f) +{ + fclose(f->f); +} + +stbfile *stb_open(char *filename, char *mode) +{ + FILE *f = fopen(filename, mode); + stbfile *s; + if (f == NULL) return NULL; + s = stb_openf(f); + if (s) + s->close = stb__fclose2; + return s; +} +#else +// the full version depends on some code in stb.h; this +// also includes the memory buffer output format implemented with stb_arr +static void stb__fclose2(stbfile *f) +{ + stb_fclose(f->f, f->various); +} + +stbfile *stb_open(char *filename, char *mode) +{ + FILE *f = stb_fopen(filename, mode[0] == 'k' ? mode+1 : mode); + stbfile *s; + if (f == NULL) return NULL; + s = stb_openf(f); + if (s) { + s->close = stb__fclose2; + s->various = mode[0] == 'k' ? stb_keep_if_different : stb_keep_yes; + } + return s; +} + +static int stb__aputbyte(stbfile *f, int ch) +{ + stb_arr_push(f->buffer, ch); + return 1; +} +static unsigned int stb__aputdata(stbfile *f, void *data, unsigned int len) +{ + memcpy(stb_arr_addn(f->buffer, (int) len), data, len); + return len; +} +static unsigned int stb__asize(stbfile *f) { return stb_arr_len(f->buffer); } +static void stb__abackpatch(stbfile *f, unsigned int where, void *data, unsigned int len) +{ + memcpy(f->buffer+where, data, len); +} +static void stb__aclose(stbfile *f) +{ + *(unsigned char **) f->ptr = f->buffer; +} + +stbfile *stb_open_outbuffer(unsigned char **update_on_close) +{ + stbfile m = { stb__nogetbyte, stb__nogetdata, + stb__aputbyte, stb__aputdata, + stb__asize, stb__asize, stb__abackpatch, stb__aclose }; + stbfile *z = (stbfile *) malloc(sizeof(*z)); + if (z) { + z->ptr = update_on_close; + *z = m; + } + return z; +} +#endif +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Arithmetic coder... based on cbloom's notes on the subject, should be +// less code than a huffman code. + +typedef struct +{ + unsigned int range_low; + unsigned int range_high; + unsigned int code, range; // decode + int buffered_u8; + int pending_ffs; + stbfile *output; +} stb_arith; + +STB_EXTERN void stb_arith_init_encode(stb_arith *a, stbfile *out); +STB_EXTERN void stb_arith_init_decode(stb_arith *a, stbfile *in); +STB_EXTERN stbfile *stb_arith_encode_close(stb_arith *a); +STB_EXTERN stbfile *stb_arith_decode_close(stb_arith *a); + +STB_EXTERN void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq); +STB_EXTERN void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq); +STB_EXTERN unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq); +STB_EXTERN void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq); +STB_EXTERN unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2); +STB_EXTERN void stb_arith_decode_advance_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq); + +STB_EXTERN void stb_arith_encode_byte(stb_arith *a, int byte); +STB_EXTERN int stb_arith_decode_byte(stb_arith *a); + +// this is a memory-inefficient way of doing things, but it's +// fast(?) and simple +typedef struct +{ + unsigned short cumfreq; + unsigned short samples; +} stb_arith_symstate_item; + +typedef struct +{ + int num_sym; + unsigned int pow2; + int countdown; + stb_arith_symstate_item data[1]; +} stb_arith_symstate; + +#ifdef STB_DEFINE +void stb_arith_init_encode(stb_arith *a, stbfile *out) +{ + a->range_low = 0; + a->range_high = 0xffffffff; + a->pending_ffs = -1; // means no buffered character currently, to speed up normal case + a->output = out; +} + +static void stb__arith_carry(stb_arith *a) +{ + int i; + assert(a->pending_ffs != -1); // can't carry with no data + stb_putc(a->output, a->buffered_u8); + for (i=0; i < a->pending_ffs; ++i) + stb_putc(a->output, 0); +} + +static void stb__arith_putbyte(stb_arith *a, int byte) +{ + if (a->pending_ffs) { + if (a->pending_ffs == -1) { // means no buffered data; encoded for fast path efficiency + if (byte == 0xff) + stb_putc(a->output, byte); // just write it immediately + else { + a->buffered_u8 = byte; + a->pending_ffs = 0; + } + } else if (byte == 0xff) { + ++a->pending_ffs; + } else { + int i; + stb_putc(a->output, a->buffered_u8); + for (i=0; i < a->pending_ffs; ++i) + stb_putc(a->output, 0xff); + } + } else if (byte == 0xff) { + ++a->pending_ffs; + } else { + // fast path + stb_putc(a->output, a->buffered_u8); + a->buffered_u8 = byte; + } +} + +static void stb__arith_flush(stb_arith *a) +{ + if (a->pending_ffs >= 0) { + int i; + stb_putc(a->output, a->buffered_u8); + for (i=0; i < a->pending_ffs; ++i) + stb_putc(a->output, 0xff); + } +} + +static void stb__renorm_encoder(stb_arith *a) +{ + stb__arith_putbyte(a, a->range_low >> 24); + a->range_low <<= 8; + a->range_high = (a->range_high << 8) | 0xff; +} + +static void stb__renorm_decoder(stb_arith *a) +{ + int c = stb_getc(a->output); + a->code = (a->code << 8) + (c >= 0 ? c : 0); // if EOF, insert 0 +} + +void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq) +{ + unsigned int range = a->range_high - a->range_low; + unsigned int old = a->range_low; + range /= totalfreq; + a->range_low += range * cumfreq; + a->range_high = a->range_low + range*freq; + if (a->range_low < old) + stb__arith_carry(a); + while (a->range_high - a->range_low < 0x1000000) + stb__renorm_encoder(a); +} + +void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq) +{ + unsigned int range = a->range_high - a->range_low; + unsigned int old = a->range_low; + range >>= totalfreq2; + a->range_low += range * cumfreq; + a->range_high = a->range_low + range*freq; + if (a->range_low < old) + stb__arith_carry(a); + while (a->range_high - a->range_low < 0x1000000) + stb__renorm_encoder(a); +} + +unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq) +{ + unsigned int freqsize = a->range / totalfreq; + unsigned int z = a->code / freqsize; + return z >= totalfreq ? totalfreq-1 : z; +} + +void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq) +{ + unsigned int freqsize = a->range / totalfreq; // @OPTIMIZE, share with above divide somehow? + a->code -= freqsize * cumfreq; + a->range = freqsize * freq; + while (a->range < 0x1000000) + stb__renorm_decoder(a); +} + +unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2) +{ + unsigned int freqsize = a->range >> totalfreq2; + unsigned int z = a->code / freqsize; + return z >= (1U<<totalfreq2) ? (1U<<totalfreq2)-1 : z; +} + +void stb_arith_decode_advance_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq) +{ + unsigned int freqsize = a->range >> totalfreq2; + a->code -= freqsize * cumfreq; + a->range = freqsize * freq; + while (a->range < 0x1000000) + stb__renorm_decoder(a); +} + +stbfile *stb_arith_encode_close(stb_arith *a) +{ + // put exactly as many bytes as we'll read, so we can turn on/off arithmetic coding in a stream + stb__arith_putbyte(a, a->range_low >> 24); + stb__arith_putbyte(a, a->range_low >> 16); + stb__arith_putbyte(a, a->range_low >> 8); + stb__arith_putbyte(a, a->range_low >> 0); + stb__arith_flush(a); + return a->output; +} + +stbfile *stb_arith_decode_close(stb_arith *a) +{ + return a->output; +} + +// this is a simple power-of-two based model -- using +// power of two means we need one divide per decode, +// not two. +#define POW2_LIMIT 12 +stb_arith_symstate *stb_arith_state_create(int num_sym) +{ + stb_arith_symstate *s = (stb_arith_symstate *) malloc(sizeof(*s) + (num_sym-1) * sizeof(s->data[0])); + if (s) { + int i, cf, cf_next, next; + int start_freq, extra; + s->num_sym = num_sym; + s->pow2 = 4; + while (s->pow2 < 15 && (1 << s->pow2) < 3*num_sym) { + ++s->pow2; + } + start_freq = (1 << s->pow2) / num_sym; + assert(start_freq >= 1); + extra = (1 << s->pow2) % num_sym; + // now set up the initial stats + + if (s->pow2 < POW2_LIMIT) + next = 0; + else + next = 1; + + cf = cf_next = 0; + for (i=0; i < extra; ++i) { + s->data[i].cumfreq = cf; + s->data[i].samples = next; + cf += start_freq+1; + cf_next += next; + } + for (; i < num_sym; ++i) { + s->data[i].cumfreq = cf; + s->data[i].samples = next; + cf += start_freq; + cf_next += next; + } + assert(cf == (1 << s->pow2)); + // now, how long should we go until we have 2 << s->pow2 samples? + s->countdown = (2 << s->pow2) - cf - cf_next; + } + return s; +} + +static void stb_arith_state_rescale(stb_arith_symstate *s) +{ + if (s->pow2 < POW2_LIMIT) { + int pcf, cf, cf_next, next, i; + ++s->pow2; + if (s->pow2 < POW2_LIMIT) + next = 0; + else + next = 1; + cf = cf_next = 0; + pcf = 0; + for (i=0; i < s->num_sym; ++i) { + int sample = s->data[i].cumfreq - pcf + s->data[i].samples; + s->data[i].cumfreq = cf; + cf += sample; + s->data[i].samples = next; + cf_next += next; + } + assert(cf == (1 << s->pow2)); + s->countdown = (2 << s->pow2) - cf - cf_next; + } else { + int pcf, cf, cf_next, i; + cf = cf_next = 0; + pcf = 0; + for (i=0; i < s->num_sym; ++i) { + int sample = (s->data[i].cumfreq - pcf + s->data[i].samples) >> 1; + s->data[i].cumfreq = cf; + cf += sample; + s->data[i].samples = 1; + cf_next += 1; + } + assert(cf == (1 << s->pow2)); // this isn't necessarily true, due to rounding down! + s->countdown = (2 << s->pow2) - cf - cf_next; + } +} + +void stb_arith_encode_byte(stb_arith *a, int byte) +{ +} + +int stb_arith_decode_byte(stb_arith *a) +{ + return -1; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Threads +// + +#ifndef WIN32 +#ifdef STB_THREADS +#error "threads not implemented except for Windows" +#endif +#endif + +// call this function to free any global variables for memory testing +STB_EXTERN void stb_thread_cleanup(void); + +typedef void * (*stb_thread_func)(void *); + +// do not rely on these types, this is an implementation detail. +// compare against STB_THREAD_NULL and ST_SEMAPHORE_NULL +typedef void *stb_thread; +typedef void *stb_semaphore; +typedef void *stb_mutex; +typedef struct stb__sync *stb_sync; + +#define STB_SEMAPHORE_NULL NULL +#define STB_THREAD_NULL NULL +#define STB_MUTEX_NULL NULL +#define STB_SYNC_NULL NULL + +// get the number of processors (limited to those in the affinity mask for this process). +STB_EXTERN int stb_processor_count(void); +// force to run on a single core -- needed for RDTSC to work, e.g. for iprof +STB_EXTERN void stb_force_uniprocessor(void); + +// stb_work functions: queue up work to be done by some worker threads + +// set number of threads to serve the queue; you can change this on the fly, +// but if you decrease it, it won't decrease until things currently on the +// queue are finished +STB_EXTERN void stb_work_numthreads(int n); +// set maximum number of units in the queue; you can only set this BEFORE running any work functions +STB_EXTERN int stb_work_maxunits(int n); +// enqueue some work to be done (can do this from any thread, or even from a piece of work); +// return value of f is stored in *return_code if non-NULL +STB_EXTERN int stb_work(stb_thread_func f, void *d, volatile void **return_code); +// as above, but stb_sync_reach is called on 'rel' after work is complete +STB_EXTERN int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel); + + +// necessary to call this when using volatile to order writes/reads +STB_EXTERN void stb_barrier(void); + +// support for independent queues with their own threads + +typedef struct stb__workqueue stb_workqueue; + +STB_EXTERN stb_workqueue*stb_workq_new(int numthreads, int max_units); +STB_EXTERN stb_workqueue*stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex); +STB_EXTERN void stb_workq_delete(stb_workqueue *q); +STB_EXTERN void stb_workq_numthreads(stb_workqueue *q, int n); +STB_EXTERN int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code); +STB_EXTERN int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel); +STB_EXTERN int stb_workq_length(stb_workqueue *q); + +STB_EXTERN stb_thread stb_create_thread (stb_thread_func f, void *d); +STB_EXTERN stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel); +STB_EXTERN void stb_destroy_thread(stb_thread t); + +STB_EXTERN stb_semaphore stb_sem_new(int max_val); +STB_EXTERN stb_semaphore stb_sem_new_extra(int max_val, int start_val); +STB_EXTERN void stb_sem_delete (stb_semaphore s); +STB_EXTERN void stb_sem_waitfor(stb_semaphore s); +STB_EXTERN void stb_sem_release(stb_semaphore s); + +STB_EXTERN stb_mutex stb_mutex_new(void); +STB_EXTERN void stb_mutex_delete(stb_mutex m); +STB_EXTERN void stb_mutex_begin(stb_mutex m); +STB_EXTERN void stb_mutex_end(stb_mutex m); + +STB_EXTERN stb_sync stb_sync_new(void); +STB_EXTERN void stb_sync_delete(stb_sync s); +STB_EXTERN int stb_sync_set_target(stb_sync s, int count); +STB_EXTERN void stb_sync_reach_and_wait(stb_sync s); // wait for 'target' reachers +STB_EXTERN int stb_sync_reach(stb_sync s); + +typedef struct stb__threadqueue stb_threadqueue; +#define STB_THREADQ_DYNAMIC 0 +STB_EXTERN stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove); +STB_EXTERN void stb_threadq_delete(stb_threadqueue *tq); +STB_EXTERN int stb_threadq_get(stb_threadqueue *tq, void *output); +STB_EXTERN void stb_threadq_get_block(stb_threadqueue *tq, void *output); +STB_EXTERN int stb_threadq_add(stb_threadqueue *tq, void *input); +// can return FALSE if STB_THREADQ_DYNAMIC and attempt to grow fails +STB_EXTERN int stb_threadq_add_block(stb_threadqueue *tq, void *input); + +#ifdef STB_THREADS +#ifdef STB_DEFINE + +typedef struct +{ + stb_thread_func f; + void *d; + volatile void **return_val; + stb_semaphore sem; +} stb__thread; + +// this is initialized along all possible paths to create threads, therefore +// it's always initialized before any other threads are create, therefore +// it's free of races AS LONG AS you only create threads through stb_* +static stb_mutex stb__threadmutex, stb__workmutex; + +static void stb__threadmutex_init(void) +{ + if (stb__threadmutex == STB_SEMAPHORE_NULL) { + stb__threadmutex = stb_mutex_new(); + stb__workmutex = stb_mutex_new(); + } +} + +#ifdef STB_THREAD_TEST +volatile float stb__t1=1, stb__t2; + +static void stb__wait(int n) +{ + float z = 0; + int i; + for (i=0; i < n; ++i) + z += 1 / (stb__t1+i); + stb__t2 = z; +} +#else +#define stb__wait(x) +#endif + +#ifdef _WIN32 + +// avoid including windows.h -- note that our definitions aren't +// exactly the same (we don't define the security descriptor struct) +// so if you want to include windows.h, make sure you do it first. +#include <process.h> + +#ifndef _WINDOWS_ // check windows.h guard +#define STB__IMPORT STB_EXTERN __declspec(dllimport) +#define STB__DW unsigned long + +STB__IMPORT int __stdcall TerminateThread(void *, STB__DW); +STB__IMPORT void * __stdcall CreateSemaphoreA(void *sec, long,long,char*); +STB__IMPORT int __stdcall CloseHandle(void *); +STB__IMPORT STB__DW __stdcall WaitForSingleObject(void *, STB__DW); +STB__IMPORT int __stdcall ReleaseSemaphore(void *, long, long *); +STB__IMPORT void __stdcall Sleep(STB__DW); +#endif + +// necessary to call this when using volatile to order writes/reads +void stb_barrier(void) +{ + #ifdef MemoryBarrier + MemoryBarrier(); + #else + long temp; + __asm xchg temp,eax; + #endif +} + +static void stb__thread_run(void *t) +{ + void *res; + stb__thread info = * (stb__thread *) t; + free(t); + res = info.f(info.d); + if (info.return_val) + *info.return_val = res; + if (info.sem != STB_SEMAPHORE_NULL) + stb_sem_release(info.sem); +} + +static stb_thread stb_create_thread_raw(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel) +{ +#ifdef _MT +#if defined(STB_FASTMALLOC) && !defined(STB_FASTMALLOC_ITS_OKAY_I_ONLY_MALLOC_IN_ONE_THREAD) + stb_fatal("Error! Cannot use STB_FASTMALLOC with threads.\n"); + return STB_THREAD_NULL; +#else + unsigned long id; + stb__thread *data = (stb__thread *) malloc(sizeof(*data)); + if (!data) return NULL; + stb__threadmutex_init(); + data->f = f; + data->d = d; + data->return_val = return_code; + data->sem = rel; + id = _beginthread(stb__thread_run, 0, data); + if (id == -1) return NULL; + return (void *) id; +#endif +#else +#ifdef STB_NO_STB_STRINGS + stb_fatal("Invalid compilation"); +#else + stb_fatal("Must compile mult-threaded to use stb_thread/stb_work."); +#endif + return NULL; +#endif +} + +// trivial win32 wrappers +void stb_destroy_thread(stb_thread t) { TerminateThread(t,0); } +stb_semaphore stb_sem_new(int maxv) {return CreateSemaphoreA(NULL,0,maxv,NULL); } +stb_semaphore stb_sem_new_extra(int maxv,int start){return CreateSemaphoreA(NULL,start,maxv,NULL); } +void stb_sem_delete(stb_semaphore s) { if (s != NULL) CloseHandle(s); } +void stb_sem_waitfor(stb_semaphore s) { WaitForSingleObject(s, 0xffffffff); } // INFINITE +void stb_sem_release(stb_semaphore s) { ReleaseSemaphore(s,1,NULL); } +static void stb__thread_sleep(int ms) { Sleep(ms); } + +#ifndef _WINDOWS_ +STB__IMPORT int __stdcall GetProcessAffinityMask(void *, STB__DW *, STB__DW *); +STB__IMPORT void * __stdcall GetCurrentProcess(void); +STB__IMPORT int __stdcall SetProcessAffinityMask(void *, STB__DW); +#endif + +int stb_processor_count(void) +{ + unsigned long proc,sys; + GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); + return stb_bitcount(proc); +} + +void stb_force_uniprocessor(void) +{ + unsigned long proc,sys; + GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); + if (stb_bitcount(proc) > 1) { + int z; + for (z=0; z < 32; ++z) + if (proc & (1 << z)) + break; + if (z < 32) { + proc = 1 << z; + SetProcessAffinityMask(GetCurrentProcess(), proc); + } + } +} + +#ifdef _WINDOWS_ +#define STB_MUTEX_NATIVE +void *stb_mutex_new(void) +{ + CRITICAL_SECTION *p = (CRITICAL_SECTION *) malloc(sizeof(*p)); + if (p) +#if _WIN32_WINNT >= 0x0500 + InitializeCriticalSectionAndSpinCount(p, 500); +#else + InitializeCriticalSection(p); +#endif + return p; +} + +void stb_mutex_delete(void *p) +{ + if (p) { + DeleteCriticalSection((CRITICAL_SECTION *) p); + free(p); + } +} + +void stb_mutex_begin(void *p) +{ + stb__wait(500); + if (p) + EnterCriticalSection((CRITICAL_SECTION *) p); +} + +void stb_mutex_end(void *p) +{ + if (p) + LeaveCriticalSection((CRITICAL_SECTION *) p); + stb__wait(500); +} +#endif // _WINDOWS_ + +#if 0 +// for future reference, +// InterlockedCompareExchange for x86: + int cas64_mp(void * dest, void * xcmp, void * xxchg) { + __asm + { + mov esi, [xxchg] ; exchange + mov ebx, [esi + 0] + mov ecx, [esi + 4] + + mov esi, [xcmp] ; comparand + mov eax, [esi + 0] + mov edx, [esi + 4] + + mov edi, [dest] ; destination + lock cmpxchg8b [edi] + jz yyyy; + + mov [esi + 0], eax; + mov [esi + 4], edx; + +yyyy: + xor eax, eax; + setz al; + }; + +inline unsigned __int64 _InterlockedCompareExchange64(volatile unsigned __int64 *dest + ,unsigned __int64 exchange + ,unsigned __int64 comperand) +{ + //value returned in eax::edx + __asm { + lea esi,comperand; + lea edi,exchange; + + mov eax,[esi]; + mov edx,4[esi]; + mov ebx,[edi]; + mov ecx,4[edi]; + mov esi,dest; + lock CMPXCHG8B [esi]; + } +#endif // #if 0 + +#endif // _WIN32 + +stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel) +{ + return stb_create_thread_raw(f,d,return_code,rel); +} + +stb_thread stb_create_thread(stb_thread_func f, void *d) +{ + return stb_create_thread2(f,d,NULL,STB_SEMAPHORE_NULL); +} + +// mutex implemented by wrapping semaphore +#ifndef STB_MUTEX_NATIVE +stb_mutex stb_mutex_new(void) { return stb_sem_new_extra(1,1); } +void stb_mutex_delete(stb_mutex m) { stb_sem_delete (m); } +void stb_mutex_begin(stb_mutex m) { stb__wait(500); if (m) stb_sem_waitfor(m); } +void stb_mutex_end(stb_mutex m) { if (m) stb_sem_release(m); stb__wait(500); } +#endif + +// thread merge operation +struct stb__sync +{ + int target; // target number of threads to hit it + int sofar; // total threads that hit it + int waiting; // total threads waiting + + stb_mutex start; // mutex to prevent starting again before finishing previous + stb_mutex mutex; // mutex while tweaking state + stb_semaphore release; // semaphore wake up waiting threads + // we have to wake them up one at a time, rather than using a single release + // call, because win32 semaphores don't let you dynamically change the max count! +}; + +stb_sync stb_sync_new(void) +{ + stb_sync s = (stb_sync) malloc(sizeof(*s)); + if (!s) return s; + + s->target = s->sofar = s->waiting = 0; + s->mutex = stb_mutex_new(); + s->start = stb_mutex_new(); + s->release = stb_sem_new(1); + if (s->mutex == STB_MUTEX_NULL || s->release == STB_SEMAPHORE_NULL || s->start == STB_MUTEX_NULL) { + stb_mutex_delete(s->mutex); + stb_mutex_delete(s->mutex); + stb_sem_delete(s->release); + free(s); + return NULL; + } + return s; +} + +void stb_sync_delete(stb_sync s) +{ + if (s->waiting) { + // it's bad to delete while there are threads waiting! + // shall we wait for them to reach, or just bail? just bail + assert(0); + } + stb_mutex_delete(s->mutex); + stb_mutex_delete(s->release); + free(s); +} + +int stb_sync_set_target(stb_sync s, int count) +{ + // don't allow setting a target until the last one is fully released; + // note that this can lead to inefficient pipelining, and maybe we'd + // be better off ping-ponging between two internal syncs? + // I tried seeing how often this happened using TryEnterCriticalSection + // and could _never_ get it to happen in imv(stb), even with more threads + // than processors. So who knows! + stb_mutex_begin(s->start); + + // this mutex is pointless, since it's not valid for threads + // to call reach() before anyone calls set_target() anyway + stb_mutex_begin(s->mutex); + + assert(s->target == 0); // enforced by start mutex + s->target = count; + s->sofar = 0; + s->waiting = 0; + stb_mutex_end(s->mutex); + return STB_TRUE; +} + +void stb__sync_release(stb_sync s) +{ + if (s->waiting) + stb_sem_release(s->release); + else { + s->target = 0; + stb_mutex_end(s->start); + } +} + +int stb_sync_reach(stb_sync s) +{ + int n; + stb_mutex_begin(s->mutex); + assert(s->sofar < s->target); + n = ++s->sofar; // record this value to avoid possible race if we did 'return s->sofar'; + if (s->sofar == s->target) + stb__sync_release(s); + stb_mutex_end(s->mutex); + return n; +} + +void stb_sync_reach_and_wait(stb_sync s) +{ + stb_mutex_begin(s->mutex); + assert(s->sofar < s->target); + ++s->sofar; + if (s->sofar == s->target) { + stb__sync_release(s); + stb_mutex_end(s->mutex); + } else { + ++s->waiting; // we're waiting, so one more waiter + stb_mutex_end(s->mutex); // release the mutex to other threads + + stb_sem_waitfor(s->release); // wait for merge completion + + stb_mutex_begin(s->mutex); // on merge completion, grab the mutex + --s->waiting; // we're done waiting + stb__sync_release(s); // restart the next waiter + stb_mutex_end(s->mutex); // and now we're done + // this ends the same as the first case, but it's a lot + // clearer to understand without sharing the code + } +} + +struct stb__threadqueue +{ + stb_mutex add, remove; + stb_semaphore nonempty, nonfull; + int head_blockers; // number of threads blocking--used to know whether to release(avail) + int tail_blockers; + int head, tail, array_size, growable; + int item_size; + char *data; +}; + +static int stb__tq_wrap(volatile stb_threadqueue *z, int p) +{ + if (p == z->array_size) + return p - z->array_size; + else + return p; +} + +int stb__threadq_get_raw(stb_threadqueue *tq2, void *output, int block) +{ + volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; + if (tq->head == tq->tail && !block) return 0; + + stb_mutex_begin(tq->remove); + + while (tq->head == tq->tail) { + if (!block) { + stb_mutex_end(tq->remove); + return 0; + } + ++tq->head_blockers; + stb_mutex_end(tq->remove); + + stb_sem_waitfor(tq->nonempty); + + stb_mutex_begin(tq->remove); + --tq->head_blockers; + } + + memcpy(output, tq->data + tq->head*tq->item_size, tq->item_size); + stb_barrier(); + tq->head = stb__tq_wrap(tq, tq->head+1); + + stb_sem_release(tq->nonfull); + if (tq->head_blockers) // can't check if actually non-empty due to race? + stb_sem_release(tq->nonempty); // if there are other blockers, wake one + + stb_mutex_end(tq->remove); + return STB_TRUE; +} + +int stb__threadq_grow(volatile stb_threadqueue *tq) +{ + int n; + char *p; + assert(tq->remove != STB_MUTEX_NULL); // must have this to allow growth! + stb_mutex_begin(tq->remove); + + n = tq->array_size * 2; + p = (char *) realloc(tq->data, n * tq->item_size); + if (p == NULL) { + stb_mutex_end(tq->remove); + stb_mutex_end(tq->add); + return STB_FALSE; + } + if (tq->tail < tq->head) { + memcpy(p + tq->array_size * tq->item_size, p, tq->tail * tq->item_size); + tq->tail += tq->array_size; + } + tq->data = p; + tq->array_size = n; + + stb_mutex_end(tq->remove); + return STB_TRUE; +} + +int stb__threadq_add_raw(stb_threadqueue *tq2, void *input, int block) +{ + int tail,pos; + volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; + stb_mutex_begin(tq->add); + for(;;) { + pos = tq->tail; + tail = stb__tq_wrap(tq, pos+1); + if (tail != tq->head) break; + + // full + if (tq->growable) { + if (!stb__threadq_grow(tq)) { + stb_mutex_end(tq->add); + return STB_FALSE; // out of memory + } + } else if (!block) { + stb_mutex_end(tq->add); + return STB_FALSE; + } else { + ++tq->tail_blockers; + stb_mutex_end(tq->add); + + stb_sem_waitfor(tq->nonfull); + + stb_mutex_begin(tq->add); + --tq->tail_blockers; + } + } + memcpy(tq->data + tq->item_size * pos, input, tq->item_size); + stb_barrier(); + tq->tail = tail; + stb_sem_release(tq->nonempty); + if (tq->tail_blockers) // can't check if actually non-full due to race? + stb_sem_release(tq->nonfull); + stb_mutex_end(tq->add); + return STB_TRUE; +} + +int stb_threadq_length(stb_threadqueue *tq2) +{ + int a,b,n; + volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; + stb_mutex_begin(tq->add); + a = tq->head; + b = tq->tail; + n = tq->array_size; + stb_mutex_end(tq->add); + if (a > b) b += n; + return b-a; +} + +int stb_threadq_get(stb_threadqueue *tq, void *output) +{ + return stb__threadq_get_raw(tq, output, STB_FALSE); +} + +void stb_threadq_get_block(stb_threadqueue *tq, void *output) +{ + stb__threadq_get_raw(tq, output, STB_TRUE); +} + +int stb_threadq_add(stb_threadqueue *tq, void *input) +{ + return stb__threadq_add_raw(tq, input, STB_FALSE); +} + +int stb_threadq_add_block(stb_threadqueue *tq, void *input) +{ + return stb__threadq_add_raw(tq, input, STB_TRUE); +} + +void stb_threadq_delete(stb_threadqueue *tq) +{ + if (tq) { + free(tq->data); + stb_mutex_delete(tq->add); + stb_mutex_delete(tq->remove); + stb_sem_delete(tq->nonempty); + stb_sem_delete(tq->nonfull); + free(tq); + } +} + +#define STB_THREADQUEUE_DYNAMIC 0 +stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove) +{ + int error=0; + stb_threadqueue *tq = (stb_threadqueue *) malloc(sizeof(*tq)); + if (tq == NULL) return NULL; + + if (num_items == STB_THREADQUEUE_DYNAMIC) { + tq->growable = STB_TRUE; + num_items = 32; + } else + tq->growable = STB_FALSE; + + tq->item_size = item_size; + tq->array_size = num_items+1; + + tq->add = tq->remove = STB_MUTEX_NULL; + tq->nonempty = tq->nonfull = STB_SEMAPHORE_NULL; + tq->data = NULL; + if (many_add) + { tq->add = stb_mutex_new(); if (tq->add == STB_MUTEX_NULL) goto error; } + if (many_remove || tq->growable) + { tq->remove = stb_mutex_new(); if (tq->remove == STB_MUTEX_NULL) goto error; } + tq->nonempty = stb_sem_new(1); if (tq->nonempty == STB_SEMAPHORE_NULL) goto error; + tq->nonfull = stb_sem_new(1); if (tq->nonfull == STB_SEMAPHORE_NULL) goto error; + tq->data = (char *) malloc(tq->item_size * tq->array_size); + if (tq->data == NULL) goto error; + + tq->head = tq->tail = 0; + tq->head_blockers = tq->tail_blockers = 0; + + return tq; + +error: + stb_threadq_delete(tq); + return NULL; +} + +typedef struct +{ + stb_thread_func f; + void *d; + volatile void **retval; + stb_sync sync; +} stb__workinfo; + +//static volatile stb__workinfo *stb__work; + +struct stb__workqueue +{ + int numthreads; + stb_threadqueue *tq; +}; + +static stb_workqueue *stb__work_global; + +static void *stb__thread_workloop(void *p) +{ + volatile stb_workqueue *q = (volatile stb_workqueue *) p; + for(;;) { + void *z; + stb__workinfo w; + stb_threadq_get_block(q->tq, &w); + if (w.f == NULL) // null work is a signal to end the thread + return NULL; + z = w.f(w.d); + if (w.retval) { stb_barrier(); *w.retval = z; } + if (w.sync != STB_SYNC_NULL) stb_sync_reach(w.sync); + } +} + +stb_workqueue *stb_workq_new(int num_threads, int max_units) +{ + return stb_workq_new_flags(num_threads, max_units, 0,0); +} + +stb_workqueue *stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex) +{ + stb_workqueue *q = (stb_workqueue *) malloc(sizeof(*q)); + if (q == NULL) return NULL; + q->tq = stb_threadq_new(sizeof(stb__workinfo), max_units, !no_add_mutex, !no_remove_mutex); + if (q->tq == NULL) { free(q); return NULL; } + q->numthreads = 0; + stb_workq_numthreads(q, numthreads); + return q; +} + +void stb_workq_delete(stb_workqueue *q) +{ + while (stb_workq_length(q) != 0) + stb__thread_sleep(1); + stb_threadq_delete(q->tq); + free(q); +} + +static int stb__work_maxitems = STB_THREADQUEUE_DYNAMIC; + +static void stb_work_init(int num_threads) +{ + if (stb__work_global == NULL) { + stb__threadmutex_init(); + stb_mutex_begin(stb__workmutex); + stb_barrier(); + if (*(stb_workqueue * volatile *) &stb__work_global == NULL) + stb__work_global = stb_workq_new(num_threads, stb__work_maxitems); + stb_mutex_end(stb__workmutex); + } +} + +static int stb__work_raw(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) +{ + stb__workinfo w; + if (q == NULL) { + stb_work_init(1); + q = stb__work_global; + } + w.f = f; + w.d = d; + w.retval = return_code; + w.sync = rel; + return stb_threadq_add(q->tq, &w); +} + +int stb_workq_length(stb_workqueue *q) +{ + return stb_threadq_length(q->tq); +} + +int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code) +{ + if (f == NULL) return 0; + return stb_workq_reach(q, f, d, return_code, NULL); +} + +int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) +{ + if (f == NULL) return 0; + return stb__work_raw(q, f, d, return_code, rel); +} + +static void stb__workq_numthreads(stb_workqueue *q, int n) +{ + while (q->numthreads < n) { + stb_create_thread(stb__thread_workloop, q); + ++q->numthreads; + } + while (q->numthreads > n) { + stb__work_raw(q, NULL, NULL, NULL, NULL); + --q->numthreads; + } +} + +void stb_workq_numthreads(stb_workqueue *q, int n) +{ + stb_mutex_begin(stb__threadmutex); + stb__workq_numthreads(q,n); + stb_mutex_end(stb__threadmutex); +} + +int stb_work_maxunits(int n) +{ + if (stb__work_global == NULL) { + stb__work_maxitems = n; + stb_work_init(1); + } + return stb__work_maxitems; +} + +int stb_work(stb_thread_func f, void *d, volatile void **return_code) +{ + return stb_workq(stb__work_global, f,d,return_code); +} + +int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) +{ + return stb_workq_reach(stb__work_global, f,d,return_code,rel); +} + +void stb_work_numthreads(int n) +{ + if (stb__work_global == NULL) + stb_work_init(n); + else + stb_workq_numthreads(stb__work_global, n); +} +#endif // STB_DEFINE + + +////////////////////////////////////////////////////////////////////////////// +// +// Background disk I/O +// +// + +#define STB_BGIO_READ_ALL (-1) +STB_EXTERN int stb_bgio_read (char *filename, int offset, int len, stb_uchar **result, int *olen); +STB_EXTERN int stb_bgio_readf (FILE *f , int offset, int len, stb_uchar **result, int *olen); +STB_EXTERN int stb_bgio_read_to (char *filename, int offset, int len, stb_uchar *buffer, int *olen); +STB_EXTERN int stb_bgio_readf_to(FILE *f , int offset, int len, stb_uchar *buffer, int *olen); + +typedef struct +{ + int have_data; + int is_valid; + int is_dir; + time_t filetime; + stb_int64 filesize; +} stb_bgstat; + +STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result); + +#ifdef STB_DEFINE + +static stb_workqueue *stb__diskio; +static stb_mutex stb__diskio_mutex; + +void stb_thread_cleanup(void) +{ + if (stb__work_global) stb_workq_delete(stb__work_global); stb__work_global = NULL; + if (stb__threadmutex) stb_mutex_delete(stb__threadmutex); stb__threadmutex = NULL; + if (stb__workmutex) stb_mutex_delete(stb__workmutex); stb__workmutex = NULL; + if (stb__diskio) stb_workq_delete(stb__diskio); stb__diskio = NULL; + if (stb__diskio_mutex)stb_mutex_delete(stb__diskio_mutex);stb__diskio_mutex= NULL; +} + + +typedef struct +{ + char *filename; + FILE *f; + int offset; + int len; + + stb_bgstat *stat_out; + stb_uchar *output; + stb_uchar **result; + int *len_output; + int *flag; +} stb__disk_command; + +#define STB__MAX_DISK_COMMAND 100 +static stb__disk_command stb__dc_queue[STB__MAX_DISK_COMMAND]; +static int stb__dc_offset; + +void stb__io_init(void) +{ + if (!stb__diskio) { + stb__threadmutex_init(); + stb_mutex_begin(stb__threadmutex); + stb_barrier(); + if (*(stb_thread * volatile *) &stb__diskio == NULL) { + stb__diskio_mutex = stb_mutex_new(); + // use many threads so OS can try to schedule seeks + stb__diskio = stb_workq_new_flags(16,STB__MAX_DISK_COMMAND,STB_FALSE,STB_FALSE); + } + stb_mutex_end(stb__threadmutex); + } +} + +static void * stb__io_error(stb__disk_command *dc) +{ + if (dc->len_output) *dc->len_output = 0; + if (dc->result) *dc->result = NULL; + if (dc->flag) *dc->flag = -1; + return NULL; +} + +static void * stb__io_task(void *p) +{ + stb__disk_command *dc = (stb__disk_command *) p; + int len; + FILE *f; + stb_uchar *buf; + + if (dc->stat_out) { + struct _stati64 s; + if (!_stati64(dc->filename, &s)) { + dc->stat_out->filesize = s.st_size; + dc->stat_out->filetime = s.st_mtime; + dc->stat_out->is_dir = s.st_mode & _S_IFDIR; + dc->stat_out->is_valid = (s.st_mode & _S_IFREG) || dc->stat_out->is_dir; + } else + dc->stat_out->is_valid = 0; + stb_barrier(); + dc->stat_out->have_data = 1; + free(dc->filename); + return 0; + } + if (dc->f) { + #ifdef WIN32 + f = _fdopen(_dup(_fileno(dc->f)), "rb"); + #else + f = fdopen(dup(fileno(dc->f)), "rb"); + #endif + if (!f) + return stb__io_error(dc); + } else { + f = fopen(dc->filename, "rb"); + free(dc->filename); + if (!f) + return stb__io_error(dc); + } + + len = dc->len; + if (len < 0) { + fseek(f, 0, SEEK_END); + len = ftell(f) - dc->offset; + } + + if (fseek(f, dc->offset, SEEK_SET)) { + fclose(f); + return stb__io_error(dc); + } + + if (dc->output) + buf = dc->output; + else { + buf = (stb_uchar *) malloc(len); + if (buf == NULL) { + fclose(f); + return stb__io_error(dc); + } + } + + len = fread(buf, 1, len, f); + fclose(f); + if (dc->len_output) *dc->len_output = len; + if (dc->result) *dc->result = buf; + if (dc->flag) *dc->flag = 1; + + return NULL; +} + +int stb__io_add(char *fname, FILE *f, int off, int len, stb_uchar *out, stb_uchar **result, int *olen, int *flag, stb_bgstat *stat) +{ + int res; + stb__io_init(); + // do memory allocation outside of mutex + if (fname) fname = strdup(fname); + stb_mutex_begin(stb__diskio_mutex); + { + stb__disk_command *dc = &stb__dc_queue[stb__dc_offset]; + dc->filename = fname; + dc->f = f; + dc->offset = off; + dc->len = len; + dc->output = out; + dc->result = result; + dc->len_output = olen; + dc->flag = flag; + dc->stat_out = stat; + res = stb_workq(stb__diskio, stb__io_task, dc, NULL); + if (res) + stb__dc_offset = (stb__dc_offset + 1 == STB__MAX_DISK_COMMAND ? 0 : stb__dc_offset+1); + } + stb_mutex_end(stb__diskio_mutex); + return res; +} + +int stb_bgio_read(char *filename, int offset, int len, stb_uchar **result, int *olen) +{ + return stb__io_add(filename,NULL,offset,len,NULL,result,olen,NULL,NULL); +} + +int stb_bgio_readf(FILE *f, int offset, int len, stb_uchar **result, int *olen) +{ + return stb__io_add(NULL,f,offset,len,NULL,result,olen,NULL,NULL); +} + +int stb_bgio_read_to(char *filename, int offset, int len, stb_uchar *buffer, int *olen) +{ + return stb__io_add(filename,NULL,offset,len,buffer,NULL,olen,NULL,NULL); +} + +int stb_bgio_readf_to(FILE *f, int offset, int len, stb_uchar *buffer, int *olen) +{ + return stb__io_add(NULL,f,offset,len,buffer,NULL,olen,NULL,NULL); +} + +STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result) +{ + result->have_data = 0; + return stb__io_add(filename,NULL,0,0,0,NULL,0,NULL, result); +} +#endif +#endif + + + +////////////////////////////////////////////////////////////////////////////// +// +// Fast malloc implementation +// +// This is a clone of TCMalloc, but without the thread support. +// 1. large objects are allocated directly, page-aligned +// 2. small objects are allocated in homogeonous heaps, 0 overhead +// +// We keep an allocation table for pages a la TCMalloc. This would +// require 4MB for the entire address space, but we only allocate +// the parts that are in use. The overhead from using homogenous heaps +// everywhere is 3MB. (That is, if you allocate 1 object of each size, +// you'll use 3MB.) + +#if defined(STB_DEFINE) && (defined(_WIN32) || defined(STB_FASTMALLOC)) + +#ifdef _WIN32 + #ifndef _WINDOWS_ + #ifndef STB__IMPORT + #define STB__IMPORT STB_EXTERN __declspec(dllimport) + #define STB__DW unsigned long + #endif + STB__IMPORT void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect); + STB__IMPORT int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype); + #endif + #define stb__alloc_pages_raw(x) (stb_uint32) VirtualAlloc(NULL, (x), 0x3000, 0x04) + #define stb__dealloc_pages_raw(p) VirtualFree((void *) p, 0, 0x8000) +#else + #error "Platform not currently supported" +#endif + +typedef struct stb__span +{ + int start, len; + struct stb__span *next, *prev; + void *first_free; + unsigned short list; // 1..256 free; 257..511 sizeclass; 0=large block + short allocations; // # outstanding allocations for sizeclass +} stb__span; // 24 + +static stb__span **stb__span_for_page; +static int stb__firstpage, stb__lastpage; +static void stb__update_page_range(int first, int last) +{ + stb__span **sfp; + int i, f,l; + if (first >= stb__firstpage && last <= stb__lastpage) return; + if (stb__span_for_page == NULL) { + f = first; + l = f+stb_max(last-f, 16384); + l = stb_min(l, 1<<20); + } else if (last > stb__lastpage) { + f = stb__firstpage; + l = f + (stb__lastpage - f) * 2; + l = stb_clamp(last, l,1<<20); + } else { + l = stb__lastpage; + f = l - (l - stb__firstpage) * 2; + f = stb_clamp(f, 0,first); + } + sfp = (stb__span **) stb__alloc_pages_raw(sizeof(void *) * (l-f)); + for (i=f; i < stb__firstpage; ++i) sfp[i - f] = NULL; + for ( ; i < stb__lastpage ; ++i) sfp[i - f] = stb__span_for_page[i - stb__firstpage]; + for ( ; i < l ; ++i) sfp[i - f] = NULL; + if (stb__span_for_page) stb__dealloc_pages_raw(stb__span_for_page); + stb__firstpage = f; + stb__lastpage = l; + stb__span_for_page = sfp; +} + +static stb__span *stb__span_free=NULL; +static stb__span *stb__span_first, *stb__span_end; +static stb__span *stb__span_alloc(void) +{ + stb__span *s = stb__span_free; + if (s) + stb__span_free = s->next; + else { + if (!stb__span_first) { + stb__span_first = (stb__span *) stb__alloc_pages_raw(65536); + if (stb__span_first == NULL) return NULL; + stb__span_end = stb__span_first + (65536 / sizeof(stb__span)); + } + s = stb__span_first++; + if (stb__span_first == stb__span_end) stb__span_first = NULL; + } + return s; +} + +static stb__span *stb__spanlist[512]; + +static void stb__spanlist_unlink(stb__span *s) +{ + if (s->prev) + s->prev->next = s->next; + else { + int n = s->list; + assert(stb__spanlist[n] == s); + stb__spanlist[n] = s->next; + } + if (s->next) + s->next->prev = s->prev; + s->next = s->prev = NULL; + s->list = 0; +} + +static void stb__spanlist_add(int n, stb__span *s) +{ + s->list = n; + s->next = stb__spanlist[n]; + s->prev = NULL; + stb__spanlist[n] = s; + if (s->next) s->next->prev = s; +} + +#define stb__page_shift 12 +#define stb__page_size (1 << stb__page_shift) +#define stb__page_number(x) ((x) >> stb__page_shift) +#define stb__page_address(x) ((x) << stb__page_shift) + +static void stb__set_span_for_page(stb__span *s) +{ + int i; + for (i=0; i < s->len; ++i) + stb__span_for_page[s->start + i - stb__firstpage] = s; +} + +static stb__span *stb__coalesce(stb__span *a, stb__span *b) +{ + assert(a->start + a->len == b->start); + if (a->list) stb__spanlist_unlink(a); + if (b->list) stb__spanlist_unlink(b); + a->len += b->len; + b->len = 0; + b->next = stb__span_free; + stb__span_free = b; + stb__set_span_for_page(a); + return a; +} + +static void stb__free_span(stb__span *s) +{ + stb__span *n = NULL; + if (s->start > stb__firstpage) { + n = stb__span_for_page[s->start-1 - stb__firstpage]; + if (n && n->allocations == -2 && n->start + n->len == s->start) s = stb__coalesce(n,s); + } + if (s->start + s->len < stb__lastpage) { + n = stb__span_for_page[s->start + s->len - stb__firstpage]; + if (n && n->allocations == -2 && s->start + s->len == n->start) s = stb__coalesce(s,n); + } + s->allocations = -2; + stb__spanlist_add(s->len > 256 ? 256 : s->len, s); +} + +static stb__span *stb__alloc_pages(int num) +{ + stb__span *s = stb__span_alloc(); + int p; + if (!s) return NULL; + p = stb__alloc_pages_raw(num << stb__page_shift); + if (p == 0) { s->next = stb__span_free; stb__span_free = s; return 0; } + assert(stb__page_address(stb__page_number(p)) == p); + p = stb__page_number(p); + stb__update_page_range(p, p+num); + s->start = p; + s->len = num; + s->next = NULL; + s->prev = NULL; + stb__set_span_for_page(s); + return s; +} + +static stb__span *stb__alloc_span(int pagecount) +{ + int i; + stb__span *p = NULL; + for(i=pagecount; i < 256; ++i) + if (stb__spanlist[i]) { + p = stb__spanlist[i]; + break; + } + if (!p) { + p = stb__spanlist[256]; + while (p && p->len < pagecount) + p = p->next; + } + if (!p) { + p = stb__alloc_pages(pagecount < 16 ? 16 : pagecount); + if (p == NULL) return 0; + } else + stb__spanlist_unlink(p); + + if (p->len > pagecount) { + stb__span *q = stb__span_alloc(); + if (q) { + q->start = p->start + pagecount; + q->len = p->len - pagecount; + p->len = pagecount; + for (i=0; i < q->len; ++i) + stb__span_for_page[q->start+i - stb__firstpage] = q; + stb__spanlist_add(q->len > 256 ? 256 : q->len, q); + } + } + return p; +} + +#define STB__MAX_SMALL_SIZE 32768 +#define STB__MAX_SIZE_CLASSES 256 + +static unsigned char stb__class_base[32]; +static unsigned char stb__class_shift[32]; +static unsigned char stb__pages_for_class[STB__MAX_SIZE_CLASSES]; +static int stb__size_for_class[STB__MAX_SIZE_CLASSES]; + +stb__span *stb__get_nonempty_sizeclass(int c) +{ + int s = c + 256, i, size, tsize; // remap to span-list index + char *z; + void *q; + stb__span *p = stb__spanlist[s]; + if (p) { + if (p->first_free) return p; // fast path: it's in the first one in list + for (p=p->next; p; p=p->next) + if (p->first_free) { + // move to front for future queries + stb__spanlist_unlink(p); + stb__spanlist_add(s, p); + return p; + } + } + // no non-empty ones, so allocate a new one + p = stb__alloc_span(stb__pages_for_class[c]); + if (!p) return NULL; + // create the free list up front + size = stb__size_for_class[c]; + tsize = stb__pages_for_class[c] << stb__page_shift; + i = 0; + z = (char *) stb__page_address(p->start); + q = NULL; + while (i + size <= tsize) { + * (void **) z = q; q = z; + z += size; + i += size; + } + p->first_free = q; + p->allocations = 0; + stb__spanlist_add(s,p); + return p; +} + +static int stb__sizeclass(size_t sz) +{ + int z = stb_log2_floor(sz); // -1 below to group e.g. 13,14,15,16 correctly + return stb__class_base[z] + ((sz-1) >> stb__class_shift[z]); +} + +static void stb__init_sizeclass(void) +{ + int i, size, overhead; + int align_shift = 2; // allow 4-byte and 12-byte blocks as well, vs. TCMalloc + int next_class = 1; + int last_log = 0; + + for (i = 0; i < align_shift; i++) { + stb__class_base [i] = next_class; + stb__class_shift[i] = align_shift; + } + + for (size = 1 << align_shift; size <= STB__MAX_SMALL_SIZE; size += 1 << align_shift) { + i = stb_log2_floor(size); + if (i > last_log) { + if (size == 16) ++align_shift; // switch from 4-byte to 8-byte alignment + else if (size >= 128 && align_shift < 8) ++align_shift; + stb__class_base[i] = next_class - ((size-1) >> align_shift); + stb__class_shift[i] = align_shift; + last_log = i; + } + stb__size_for_class[next_class++] = size; + } + + for (i=1; i <= STB__MAX_SMALL_SIZE; ++i) + assert(i <= stb__size_for_class[stb__sizeclass(i)]); + + overhead = 0; + for (i = 1; i < next_class; i++) { + int s = stb__size_for_class[i]; + size = stb__page_size; + while (size % s > size >> 3) + size += stb__page_size; + stb__pages_for_class[i] = (unsigned char) (size >> stb__page_shift); + overhead += size; + } + assert(overhead < (4 << 20)); // make sure it's under 4MB of overhead +} + +#ifdef STB_DEBUG +#define stb__smemset(a,b,c) memset((void *) a, b, c) +#elif defined(STB_FASTMALLOC_INIT) +#define stb__smemset(a,b,c) memset((void *) a, b, c) +#else +#define stb__smemset(a,b,c) +#endif +void *stb_smalloc(size_t sz) +{ + stb__span *s; + if (sz == 0) return NULL; + if (stb__size_for_class[1] == 0) stb__init_sizeclass(); + if (sz > STB__MAX_SMALL_SIZE) { + s = stb__alloc_span((sz + stb__page_size - 1) >> stb__page_shift); + if (s == NULL) return NULL; + s->list = 0; + s->next = s->prev = NULL; + s->allocations = -32767; + stb__smemset(stb__page_address(s->start), 0xcd, (sz+3)&~3); + return (void *) stb__page_address(s->start); + } else { + void *p; + int c = stb__sizeclass(sz); + s = stb__spanlist[256+c]; + if (!s || !s->first_free) + s = stb__get_nonempty_sizeclass(c); + if (s == NULL) return NULL; + p = s->first_free; + s->first_free = * (void **) p; + ++s->allocations; + stb__smemset(p,0xcd, sz); + return p; + } +} + +int stb_ssize(void *p) +{ + stb__span *s; + if (p == NULL) return 0; + s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage]; + if (s->list >= 256) { + return stb__size_for_class[s->list - 256]; + } else { + assert(s->list == 0); + return s->len << stb__page_shift; + } +} + +void stb_sfree(void *p) +{ + stb__span *s; + if (p == NULL) return; + s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage]; + if (s->list >= 256) { + stb__smemset(p, 0xfe, stb__size_for_class[s->list-256]); + * (void **) p = s->first_free; + s->first_free = p; + if (--s->allocations == 0) { + stb__spanlist_unlink(s); + stb__free_span(s); + } + } else { + assert(s->list == 0); + stb__smemset(p, 0xfe, stb_ssize(p)); + stb__free_span(s); + } +} + +void *stb_srealloc(void *p, size_t sz) +{ + size_t cur_size; + if (p == NULL) return stb_smalloc(sz); + if (sz == 0) { stb_sfree(p); return NULL; } + cur_size = stb_ssize(p); + if (sz > cur_size || sz <= (cur_size >> 1)) { + void *q; + if (sz > cur_size && sz < (cur_size << 1)) sz = cur_size << 1; + q = stb_smalloc(sz); if (q == NULL) return NULL; + memcpy(q, p, sz < cur_size ? sz : cur_size); + stb_sfree(p); + return q; + } + return p; +} + +void *stb_scalloc(size_t n, size_t sz) +{ + void *p; + if (n == 0 || sz == 0) return NULL; + if (stb_log2_ceil(n) + stb_log2_ceil(n) >= 32) return NULL; + p = stb_smalloc(n*sz); + if (p) memset(p, 0, n*sz); + return p; +} + +char *stb_sstrdup(char *s) +{ + int n = strlen(s); + char *p = (char *) stb_smalloc(n+1); + if (p) strcpy(p,s); + return p; +} +#endif // STB_DEFINE + + + +////////////////////////////////////////////////////////////////////////////// +// +// Source code constants +// +// This is a trivial system to let you specify constants in source code, +// then while running you can change the constants. +// +// Note that you can't wrap the #defines, because we need to know their +// names. So we provide a pre-wrapped version without 'STB_' for convenience; +// to request it, #define STB_CONVENIENT_H, yielding: +// KI -- integer +// KU -- unsigned integer +// KF -- float +// KD -- double +// KS -- string constant +// +// Defaults to functioning in debug build, not in release builds. +// To force on, define STB_ALWAYS_H + +#ifdef STB_CONVENIENT_H +#define KI(x) STB_I(x) +#define KU(x) STB_UI(x) +#define KF(x) STB_F(x) +#define KD(x) STB_D(x) +#define KS(x) STB_S(x) +#endif + +STB_EXTERN void stb_source_path(char *str); +#ifdef STB_DEFINE +char *stb__source_path; +void stb_source_path(char *path) +{ + stb__source_path = path; +} + +char *stb__get_sourcefile_path(char *file) +{ + static char filebuf[512]; + if (stb__source_path) { + sprintf(filebuf, "%s/%s", stb__source_path, file); + if (stb_fexists(filebuf)) return filebuf; + } + + if (stb_fexists(file)) return file; + + sprintf(filebuf, "../%s", file); + if (!stb_fexists(filebuf)) return filebuf; + + return file; +} +#endif + +#define STB_F(x) ((float) STB_H(x)) +#define STB_UI(x) ((unsigned int) STB_I(x)) + +#if !defined(STB_DEBUG) && !defined(STB_ALWAYS_H) +#define STB_D(x) ((double) (x)) +#define STB_I(x) ((int) (x)) +#define STB_S(x) ((char *) (x)) +#else +#define STB_D(x) stb__double_constant(__FILE__, __LINE__-1, (x)) +#define STB_I(x) stb__int_constant(__FILE__, __LINE__-1, (x)) +#define STB_S(x) stb__string_constant(__FILE__, __LINE__-1, (x)) + +STB_EXTERN double stb__double_constant(char *file, int line, double x); +STB_EXTERN int stb__int_constant(char *file, int line, int x); +STB_EXTERN char * stb__string_constant(char *file, int line, char *str); + +#ifdef STB_DEFINE + +enum +{ + STB__CTYPE_int, + STB__CTYPE_uint, + STB__CTYPE_float, + STB__CTYPE_double, + STB__CTYPE_string, +}; + +typedef struct +{ + int line; + int type; + union { + int ival; + double dval; + char *sval; + }; +} stb__Entry; + +typedef struct +{ + stb__Entry *entries; + char *filename; + time_t timestamp; + char **file_data; + int file_len; + unsigned short *line_index; +} stb__FileEntry; + +static void stb__constant_parse(stb__FileEntry *f, int i) +{ + char *s; + int n; + if (!stb_arr_valid(f->entries, i)) return; + n = f->entries[i].line; + if (n >= f->file_len) return; + s = f->file_data[n]; + switch (f->entries[i].type) { + case STB__CTYPE_float: + while (*s) { + if (!strncmp(s, "STB_D(", 6)) { s+=6; goto matched_float; } + if (!strncmp(s, "STB_F(", 6)) { s+=6; goto matched_float; } + if (!strncmp(s, "KD(", 3)) { s+=3; goto matched_float; } + if (!strncmp(s, "KF(", 3)) { s+=3; goto matched_float; } + ++s; + } + break; + matched_float: + f->entries[i].dval = strtod(s, NULL); + break; + case STB__CTYPE_int: + while (*s) { + if (!strncmp(s, "STB_I(", 6)) { s+=6; goto matched_int; } + if (!strncmp(s, "STB_UI(", 7)) { s+=7; goto matched_int; } + if (!strncmp(s, "KI(", 3)) { s+=3; goto matched_int; } + if (!strncmp(s, "KU(", 3)) { s+=3; goto matched_int; } + ++s; + } + break; + matched_int: { + int neg=0; + s = stb_skipwhite(s); + while (*s == '-') { neg = !neg; s = stb_skipwhite(s+1); } // handle '- - 5', pointlessly + if (s[0] == '0' && tolower(s[1]) == 'x') + f->entries[i].ival = strtol(s, NULL, 16); + else if (s[0] == '0') + f->entries[i].ival = strtol(s, NULL, 8); + else + f->entries[i].ival = strtol(s, NULL, 10); + if (neg) f->entries[i].ival = -f->entries[i].ival; + break; + } + case STB__CTYPE_string: + // @TODO + break; + } +} + +static stb_sdict *stb__constant_file_hash; + +stb__Entry *stb__constant_get_entry(char *filename, int line, int type) +{ + int i; + stb__FileEntry *f; + if (stb__constant_file_hash == NULL) + stb__constant_file_hash = stb_sdict_new(STB_TRUE); + f = (stb__FileEntry*) stb_sdict_get(stb__constant_file_hash, filename); + if (f == NULL) { + char *s = stb__get_sourcefile_path(filename); + if (s == NULL || !stb_fexists(s)) return 0; + f = (stb__FileEntry *) malloc(sizeof(*f)); + f->timestamp = stb_ftimestamp(s); + f->file_data = stb_stringfile(s, &f->file_len); + f->filename = strdup(s); // cache the full path + f->entries = NULL; + f->line_index = 0; + stb_arr_setlen(f->line_index, f->file_len); + memset(f->line_index, 0xff, stb_arr_storage(f->line_index)); + } else { + time_t t = stb_ftimestamp(f->filename); + if (f->timestamp != t) { + f->timestamp = t; + free(f->file_data); + f->file_data = stb_stringfile(f->filename, &f->file_len); + stb_arr_setlen(f->line_index, f->file_len); + for (i=0; i < stb_arr_len(f->entries); ++i) + stb__constant_parse(f, i); + } + } + + if (line >= f->file_len) return 0; + + if (f->line_index[line] >= stb_arr_len(f->entries)) { + // need a new entry + int n = stb_arr_len(f->entries); + stb__Entry e; + e.line = line; + if (line < f->file_len) + f->line_index[line] = n; + e.type = type; + stb_arr_push(f->entries, e); + stb__constant_parse(f, n); + } + return f->entries + f->line_index[line]; +} + +double stb__double_constant(char *file, int line, double x) +{ + stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_float); + if (!e) return x; + return e->dval; +} + +int stb__int_constant(char *file, int line, int x) +{ + stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_int); + if (!e) return x; + return e->ival; +} + +char * stb__string_constant(char *file, int line, char *x) +{ + stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_string); + if (!e) return x; + return e->sval; +} + +#endif // STB_DEFINE +#endif // !STB_DEBUG && !STB_ALWAYS_H + + +#ifdef STB_STUA +////////////////////////////////////////////////////////////////////////// +// +// stua: little scripting language +// +// define STB_STUA to compile it +// +// see http://nothings.org/stb/stb_stua.html for documentation +// +// basic parsing model: +// +// lexical analysis +// use stb_lex() to parse tokens; keywords get their own tokens +// +// parsing: +// recursive descent parser. too much of a hassle to make an unambiguous +// LR(1) grammar, and one-pass generation is clumsier (recursive descent +// makes it easier to e.g. compile nested functions). on the other hand, +// dictionary syntax required hackery to get extra lookahead. +// +// codegen: +// output into an evaluation tree, using array indices as 'pointers' +// +// run: +// traverse the tree; support for 'break/continue/return' is tricky +// +// garbage collection: +// stu__mark and sweep; explicit stack with non-stu__compile_global_scope roots + +typedef stb_int32 stua_obj; + +typedef stb_idict stua_dict; + +STB_EXTERN void stua_run_script(char *s); +STB_EXTERN void stua_uninit(void); + +extern stua_obj stua_globals; + +STB_EXTERN double stua_number(stua_obj z); + +STB_EXTERN stua_obj stua_getnil(void); +STB_EXTERN stua_obj stua_getfalse(void); +STB_EXTERN stua_obj stua_gettrue(void); +STB_EXTERN stua_obj stua_string(char *z); +STB_EXTERN stua_obj stua_make_number(double d); +STB_EXTERN stua_obj stua_box(int type, void *data, int size); + +enum +{ + STUA_op_negate=129, + STUA_op_shl, STUA_op_ge, + STUA_op_shr, STUA_op_le, + STUA_op_shru, + STUA_op_last +}; + +#define STUA_NO_VALUE 2 // equivalent to a tagged NULL +STB_EXTERN stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c); + +STB_EXTERN stua_obj stua_error(char *err, ...); + +STB_EXTERN stua_obj stua_pushroot(stua_obj o); +STB_EXTERN void stua_poproot ( void ); + + +#ifdef STB_DEFINE +// INTERPRETER + +// 31-bit floating point implementation +// force the (1 << 30) bit (2nd highest bit) to be zero by re-biasing the exponent; +// then shift and set the bottom bit + +static stua_obj stu__floatp(float *f) +{ + unsigned int n = *(unsigned int *) f; + unsigned int e = n & (0xff << 23); + + assert(sizeof(int) == 4 && sizeof(float) == 4); + + if (!e) // zero? + n = n; // no change + else if (e < (64 << 23)) // underflow of the packed encoding? + n = (n & 0x80000000); // signed 0 + else if (e > (190 << 23)) // overflow of the encoding? (or INF or NAN) + n = (n & 0x80000000) + (127 << 23); // new INF encoding + else + n -= 0x20000000; + + // now we need to shuffle the bits so that the spare bit is at the bottom + assert((n & 0x40000000) == 0); + return (n & 0x80000000) + (n << 1) + 1; +} + +static unsigned char stu__getfloat_addend[256]; +static float stu__getfloat(stua_obj v) +{ + unsigned int n; + unsigned int e = ((unsigned int) v) >> 24; + + n = (int) v >> 1; // preserve high bit + n += stu__getfloat_addend[e] << 24; + return *(float *) &n; +} + +stua_obj stua_float(float f) +{ + return stu__floatp(&f); +} + +static void stu__float_init(void) +{ + int i; + stu__getfloat_addend[0] = 0; // do nothing to biased exponent of 0 + for (i=1; i < 127; ++i) + stu__getfloat_addend[i] = 32; // undo the -0x20000000 + stu__getfloat_addend[127] = 64; // convert packed INF to INF (0x3f -> 0x7f) + + for (i=0; i < 128; ++i) // for signed floats, remove the bit we just shifted down + stu__getfloat_addend[128+i] = stu__getfloat_addend[i] - 64; +} + +// Tagged data type implementation + + // TAGS: +#define stu__int_tag 0 // of 2 bits // 00 int +#define stu__float_tag 1 // of 1 bit // 01 float +#define stu__ptr_tag 2 // of 2 bits // 10 boxed + // 11 float + +#define stu__tag(x) ((x) & 3) +#define stu__number(x) (stu__tag(x) != stu__ptr_tag) +#define stu__isint(x) (stu__tag(x) == stu__int_tag) + +#define stu__int(x) ((x) >> 2) +#define stu__float(x) (stu__getfloat(x)) + +#define stu__makeint(v) ((v)*4+stu__int_tag) + +// boxed data, and tag support for boxed data + +enum +{ + STU___float = 1, STU___int = 2, + STU___number = 3, STU___string = 4, + STU___function = 5, STU___dict = 6, + STU___boolean = 7, STU___error = 8, +}; + +// boxed data +#define STU__BOX short type, stua_gc +typedef struct stu__box { STU__BOX; } stu__box; + +stu__box stu__nil = { 0, 1 }; +stu__box stu__true = { STU___boolean, 1, }; +stu__box stu__false = { STU___boolean, 1, }; + +#define stu__makeptr(v) ((stua_obj) (v) + stu__ptr_tag) + +#define stua_nil stu__makeptr(&stu__nil) +#define stua_true stu__makeptr(&stu__true) +#define stua_false stu__makeptr(&stu__false) + +stua_obj stua_getnil(void) { return stua_nil; } +stua_obj stua_getfalse(void) { return stua_false; } +stua_obj stua_gettrue(void) { return stua_true; } + +#define stu__ptr(x) ((stu__box *) ((x) - stu__ptr_tag)) + +#define stu__checkt(t,x) ((t) == STU___float ? ((x) & 1) == stu__float_tag : \ + (t) == STU___int ? stu__isint(x) : \ + (t) == STU___number ? stu__number(x) : \ + stu__tag(x) == stu__ptr_tag && stu__ptr(x)->type == (t)) + +typedef struct +{ + STU__BOX; + void *ptr; +} stu__wrapper; + +// implementation of a 'function' or function + closure + +typedef struct stu__func +{ + STU__BOX; + stua_obj closure_source; // 0 - regular function; 4 - C function + // if closure, pointer to source function + union { + stua_obj closure_data; // partial-application data + void *store; // pointer to free that holds 'code' + stua_obj (*func)(stua_dict *context); + } f; + // closure ends here + short *code; + int num_param; + stua_obj *param; // list of parameter strings +} stu__func; + +// apply this to 'short *code' to get at data +#define stu__const(f) ((stua_obj *) (f)) + +static void stu__free_func(stu__func *f) +{ + if (f->closure_source == 0) free(f->f.store); + if ((stb_uint) f->closure_source <= 4) free(f->param); + free(f); +} + +#define stu__pd(x) ((stua_dict *) stu__ptr(x)) +#define stu__pw(x) ((stu__wrapper *) stu__ptr(x)) +#define stu__pf(x) ((stu__func *) stu__ptr(x)) + + +// garbage-collection + + +static stu__box ** stu__gc_ptrlist; +static stua_obj * stu__gc_root_stack; + +stua_obj stua_pushroot(stua_obj o) { stb_arr_push(stu__gc_root_stack, o); return o; } +void stua_poproot ( void ) { stb_arr_pop(stu__gc_root_stack); } + +static stb_sdict *stu__strings; +static void stu__mark(stua_obj z) +{ + int i; + stu__box *p = stu__ptr(z); + if (p->stua_gc == 1) return; // already marked + assert(p->stua_gc == 0); + p->stua_gc = 1; + switch(p->type) { + case STU___function: { + stu__func *f = (stu__func *) p; + if ((stb_uint) f->closure_source <= 4) { + if (f->closure_source == 0) { + for (i=1; i <= f->code[0]; ++i) + if (!stu__number(((stua_obj *) f->code)[-i])) + stu__mark(((stua_obj *) f->code)[-i]); + } + for (i=0; i < f->num_param; ++i) + stu__mark(f->param[i]); + } else { + stu__mark(f->closure_source); + stu__mark(f->f.closure_data); + } + break; + } + case STU___dict: { + stua_dict *e = (stua_dict *) p; + for (i=0; i < e->limit; ++i) + if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) { + if (!stu__number(e->table[i].k)) stu__mark((int) e->table[i].k); + if (!stu__number(e->table[i].v)) stu__mark((int) e->table[i].v); + } + break; + } + } +} + +static int stu__num_allocs, stu__size_allocs; +static stua_obj stu__flow_val = stua_nil; // used for break & return + +static void stua_gc(int force) +{ + int i; + if (!force && stu__num_allocs == 0 && stu__size_allocs == 0) return; + stu__num_allocs = stu__size_allocs = 0; + //printf("[gc]\n"); + + // clear marks + for (i=0; i < stb_arr_len(stu__gc_ptrlist); ++i) + stu__gc_ptrlist[i]->stua_gc = 0; + + // stu__mark everything reachable + stu__nil.stua_gc = stu__true.stua_gc = stu__false.stua_gc = 1; + stu__mark(stua_globals); + if (!stu__number(stu__flow_val)) + stu__mark(stu__flow_val); + for (i=0; i < stb_arr_len(stu__gc_root_stack); ++i) + if (!stu__number(stu__gc_root_stack[i])) + stu__mark(stu__gc_root_stack[i]); + + // sweep unreachables + for (i=0; i < stb_arr_len(stu__gc_ptrlist);) { + stu__box *z = stu__gc_ptrlist[i]; + if (!z->stua_gc) { + switch (z->type) { + case STU___dict: stb_idict_destroy((stua_dict *) z); break; + case STU___error: free(((stu__wrapper *) z)->ptr); break; + case STU___string: stb_sdict_remove(stu__strings, (char*) ((stu__wrapper *) z)->ptr, NULL); free(z); break; + case STU___function: stu__free_func((stu__func *) z); break; + } + // swap in the last item over this, and repeat + z = stb_arr_pop(stu__gc_ptrlist); + stu__gc_ptrlist[i] = z; + } else + ++i; + } +} + +static void stu__consider_gc(stua_obj x) +{ + if (stu__size_allocs < 100000) return; + if (stu__num_allocs < 10 && stu__size_allocs < 1000000) return; + stb_arr_push(stu__gc_root_stack, x); + stua_gc(0); + stb_arr_pop(stu__gc_root_stack); +} + +static stua_obj stu__makeobj(int type, void *data, int size, int safe_to_gc) +{ + stua_obj x = stu__makeptr(data); + ((stu__box *) data)->type = type; + stb_arr_push(stu__gc_ptrlist, (stu__box *) data); + stu__num_allocs += 1; + stu__size_allocs += size; + if (safe_to_gc) stu__consider_gc(x); + return x; +} + +stua_obj stua_box(int type, void *data, int size) +{ + stu__wrapper *p = (stu__wrapper *) malloc(sizeof(*p)); + p->ptr = data; + return stu__makeobj(type, p, size, 0); +} + +// a stu string can be directly compared for equality, because +// they go into a hash table +stua_obj stua_string(char *z) +{ + stu__wrapper *b = (stu__wrapper *) stb_sdict_get(stu__strings, z); + if (b == NULL) { + int o = stua_box(STU___string, NULL, strlen(z) + sizeof(*b)); + b = stu__pw(o); + stb_sdict_add(stu__strings, z, b); + stb_sdict_getkey(stu__strings, z, (char **) &b->ptr); + } + return stu__makeptr(b); +} + +// stb_obj dictionary is just an stb_idict +static void stu__set(stua_dict *d, stua_obj k, stua_obj v) +{ if (stb_idict_set(d, k, v)) stu__size_allocs += 8; } + +static stua_obj stu__get(stua_dict *d, stua_obj k, stua_obj res) +{ + stb_idict_get_flag(d, k, &res); + return res; +} + +static stua_obj make_string(char *z, int len) +{ + stua_obj s; + char temp[256], *q = (char *) stb_temp(temp, len+1), *p = q; + while (len > 0) { + if (*z == '\\') { + if (z[1] == 'n') *p = '\n'; + else if (z[1] == 'r') *p = '\r'; + else if (z[1] == 't') *p = '\t'; + else *p = z[1]; + p += 1; z += 2; len -= 2; + } else { + *p++ = *z++; len -= 1; + } + } + *p = 0; + s = stua_string(q); + stb_tempfree(temp, q); + return s; +} + +enum token_names +{ + T__none=128, + ST_shl = STUA_op_shl, ST_ge = STUA_op_ge, + ST_shr = STUA_op_shr, ST_le = STUA_op_le, + ST_shru = STUA_op_shru, STU__negate = STUA_op_negate, + ST__reset_numbering = STUA_op_last, + ST_white, + ST_id, ST_float, ST_decimal, ST_hex, ST_char,ST_string, ST_number, + // make sure the keywords come _AFTER_ ST_id, so stb_lex prefer them + ST_if, ST_while, ST_for, ST_eq, ST_nil, + ST_then, ST_do, ST_in, ST_ne, ST_true, + ST_else, ST_break, ST_let, ST_and, ST_false, + ST_elseif, ST_continue, ST_into, ST_or, ST_repeat, + ST_end, ST_as, ST_return, ST_var, ST_func, + ST_catch, ST__frame, + ST__max_terminals, + + STU__defaultparm, STU__seq, +}; + +static stua_dict * stu__globaldict; + stua_obj stua_globals; + +static enum +{ + FLOW_normal, FLOW_continue, FLOW_break, FLOW_return, FLOW_error, +} stu__flow; + +stua_obj stua_error(char *z, ...) +{ + stua_obj a; + char temp[4096], *x; + va_list v; va_start(v,z); vsprintf(temp, z, v); va_end(v); + x = strdup(temp); + a = stua_box(STU___error, x, strlen(x)); + stu__flow = FLOW_error; + stu__flow_val = a; + return stua_nil; +} + +double stua_number(stua_obj z) +{ + return stu__tag(z) == stu__int_tag ? stu__int(z) : stu__float(z); +} + +stua_obj stua_make_number(double d) +{ + double e = floor(d); + if (e == d && e < (1 << 29) && e >= -(1 << 29)) + return stu__makeint((int) e); + else + return stua_float((float) d); +} + +stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c) = NULL; + +static stua_obj stu__op(int op, stua_obj a, stua_obj b, stua_obj c) +{ + stua_obj r = STUA_NO_VALUE; + if (op == '+') { + if (stu__checkt(STU___string, a) && stu__checkt(STU___string, b)) { + ;// @TODO: string concatenation + } else if (stu__checkt(STU___function, a) && stu__checkt(STU___dict, b)) { + stu__func *f = (stu__func *) malloc(12); + assert(offsetof(stu__func, code)==12); + f->closure_source = a; + f->f.closure_data = b; + return stu__makeobj(STU___function, f, 16, 1); + } + } + if (stua_overload) r = stua_overload(op,a,b,c); + if (stu__flow != FLOW_error && r == STUA_NO_VALUE) + stua_error("Typecheck for operator %d", op), r=stua_nil; + return r; +} + +#define STU__EVAL2(a,b) \ + a = stu__eval(stu__f[n+1]); if (stu__flow) break; stua_pushroot(a); \ + b = stu__eval(stu__f[n+2]); stua_poproot(); if (stu__flow) break; + +#define STU__FB(op) \ + STU__EVAL2(a,b) \ + if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ + return ((a) op (b)); \ + if (stu__number(a) && stu__number(b)) \ + return stua_make_number(stua_number(a) op stua_number(b)); \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__F(op) \ + STU__EVAL2(a,b) \ + if (stu__number(a) && stu__number(b)) \ + return stua_make_number(stua_number(a) op stua_number(b)); \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__I(op) \ + STU__EVAL2(a,b) \ + if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ + return stu__makeint(stu__int(a) op stu__int(b)); \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__C(op) \ + STU__EVAL2(a,b) \ + if (stu__number(a) && stu__number(b)) \ + return (stua_number(a) op stua_number(b)) ? stua_true : stua_false; \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__CE(op) \ + STU__EVAL2(a,b) \ + return (a op b) ? stua_true : stua_false + +static short *stu__f; +static stua_obj stu__f_obj; +static stua_dict *stu__c; +static stua_obj stu__funceval(stua_obj fo, stua_obj co); + +static int stu__cond(stua_obj x) +{ + if (stu__flow) return 0; + if (!stu__checkt(STU___boolean, x)) + x = stu__op('!', x, stua_nil, stua_nil); + if (x == stua_true ) return 1; + if (x == stua_false) return 0; + stu__flow = FLOW_error; + return 0; +} + +// had to manually eliminate tailcall recursion for debugging complex stuff +#define TAILCALL(x) n = (x); goto top; +static stua_obj stu__eval(int n) +{ +top: + if (stu__flow >= FLOW_return) return stua_nil; // is this needed? + if (n < 0) return stu__const(stu__f)[n]; + assert(n != 0 && n != 1); + switch (stu__f[n]) { + stua_obj a,b,c; + case ST_catch: a = stu__eval(stu__f[n+1]); + if (stu__flow == FLOW_error) { a=stu__flow_val; stu__flow = FLOW_normal; } + return a; + case ST_var: b = stu__eval(stu__f[n+2]); if (stu__flow) break; + stu__set(stu__c, stu__const(stu__f)[stu__f[n+1]], b); + return b; + case STU__seq: stu__eval(stu__f[n+1]); if (stu__flow) break; + TAILCALL(stu__f[n+2]); + case ST_if: if (!stu__cond(stu__eval(stu__f[n+1]))) return stua_nil; + TAILCALL(stu__f[n+2]); + case ST_else: a = stu__cond(stu__eval(stu__f[n+1])); + TAILCALL(stu__f[n + 2 + !a]); + #define STU__HANDLE_BREAK \ + if (stu__flow >= FLOW_break) { \ + if (stu__flow == FLOW_break) { \ + a = stu__flow_val; \ + stu__flow = FLOW_normal; \ + stu__flow_val = stua_nil; \ + return a; \ + } \ + return stua_nil; \ + } + case ST_as: stu__eval(stu__f[n+3]); + STU__HANDLE_BREAK + // fallthrough! + case ST_while: a = stua_nil; stua_pushroot(a); + while (stu__cond(stu__eval(stu__f[n+1]))) { + stua_poproot(); + a = stu__eval(stu__f[n+2]); + STU__HANDLE_BREAK + stu__flow = FLOW_normal; // clear 'continue' flag + stua_pushroot(a); + if (stu__f[n+3]) stu__eval(stu__f[n+3]); + STU__HANDLE_BREAK + stu__flow = FLOW_normal; // clear 'continue' flag + } + stua_poproot(); + return a; + case ST_break: stu__flow = FLOW_break; stu__flow_val = stu__eval(stu__f[n+1]); break; + case ST_continue:stu__flow = FLOW_continue; break; + case ST_return: stu__flow = FLOW_return; stu__flow_val = stu__eval(stu__f[n+1]); break; + case ST__frame: return stu__f_obj; + case '[': STU__EVAL2(a,b); + if (stu__checkt(STU___dict, a)) + return stu__get(stu__pd(a), b, stua_nil); + return stu__op(stu__f[n], a, b, stua_nil); + case '=': a = stu__eval(stu__f[n+2]); if (stu__flow) break; + n = stu__f[n+1]; + if (stu__f[n] == ST_id) { + if (!stb_idict_update(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) + if (!stb_idict_update(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], a)) + return stua_error("Assignment to undefined variable"); + } else if (stu__f[n] == '[') { + stua_pushroot(a); + b = stu__eval(stu__f[n+1]); if (stu__flow) { stua_poproot(); break; } + stua_pushroot(b); + c = stu__eval(stu__f[n+2]); stua_poproot(); stua_poproot(); + if (stu__flow) break; + if (!stu__checkt(STU___dict, b)) return stua_nil; + stu__set(stu__pd(b), c, a); + } else { + return stu__op(stu__f[n], stu__eval(n), a, stua_nil); + } + return a; + case STU__defaultparm: + a = stu__eval(stu__f[n+2]); + stu__flow = FLOW_normal; + if (stb_idict_add(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) + stu__size_allocs += 8; + return stua_nil; + case ST_id: a = stu__get(stu__c, stu__const(stu__f)[stu__f[n+1]], STUA_NO_VALUE); // try local variable + return a != STUA_NO_VALUE // else try stu__compile_global_scope variable + ? a : stu__get(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], stua_nil); + case STU__negate:a = stu__eval(stu__f[n+1]); if (stu__flow) break; + return stu__isint(a) ? -a : stu__op(stu__f[n], a, stua_nil, stua_nil); + case '~': a = stu__eval(stu__f[n+1]); if (stu__flow) break; + return stu__isint(a) ? (~a)&~3 : stu__op(stu__f[n], a, stua_nil, stua_nil); + case '!': a = stu__eval(stu__f[n+1]); if (stu__flow) break; + a = stu__cond(a); if (stu__flow) break; + return a ? stua_true : stua_false; + case ST_eq: STU__CE(==); case ST_le: STU__C(<=); case '<': STU__C(<); + case ST_ne: STU__CE(!=); case ST_ge: STU__C(>=); case '>': STU__C(>); + case '+' : STU__FB(+); case '*': STU__F(*); case '&': STU__I(&); case ST_shl: STU__I(<<); + case '-' : STU__FB(-); case '/': STU__F(/); case '|': STU__I(|); case ST_shr: STU__I(>>); + case '%': STU__I(%); case '^': STU__I(^); + case ST_shru: STU__EVAL2(a,b); + if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) + return stu__makeint((unsigned) stu__int(a) >> stu__int(b)); + return stu__op(stu__f[n], a,b, stua_nil); + case ST_and: a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; + return a ? stu__eval(stu__f[n+2]) : a; + case ST_or : a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; + return a ? b : stu__eval(stu__f[n+2]); + case'(':case':': STU__EVAL2(a,b); + if (!stu__checkt(STU___function, a)) + return stu__op(stu__f[n], a,b, stua_nil); + if (!stu__checkt(STU___dict, b)) + return stua_nil; + if (stu__f[n] == ':') + b = stu__makeobj(STU___dict, stb_idict_copy(stu__pd(b)), stb_idict_memory_usage(stu__pd(b)), 0); + a = stu__funceval(a,b); + return a; + case '{' : { + stua_dict *d; + d = stb_idict_new_size(stu__f[n+1] > 40 ? 64 : 16); + if (d == NULL) + return stua_nil; // breakpoint fodder + c = stu__makeobj(STU___dict, d, 32, 1); + stua_pushroot(c); + a = stu__f[n+1]; + for (b=0; b < a; ++b) { + stua_obj x = stua_pushroot(stu__eval(stu__f[n+2 + b*2 + 0])); + stua_obj y = stu__eval(stu__f[n+2 + b*2 + 1]); + stua_poproot(); + if (stu__flow) { stua_poproot(); return stua_nil; } + stu__set(d, x, y); + } + stua_poproot(); + return c; + } + default: if (stu__f[n] < 0) return stu__const(stu__f)[stu__f[n]]; + assert(0); /* NOTREACHED */ // internal error! + } + return stua_nil; +} + +int stb__stua_nesting; +static stua_obj stu__funceval(stua_obj fo, stua_obj co) +{ + stu__func *f = stu__pf(fo); + stua_dict *context = stu__pd(co); + int i,j; + stua_obj p; + short *tf = stu__f; // save previous function + stua_dict *tc = stu__c; + + if (stu__flow == FLOW_error) return stua_nil; + assert(stu__flow == FLOW_normal); + + stua_pushroot(fo); + stua_pushroot(co); + stu__consider_gc(stua_nil); + + while ((stb_uint) f->closure_source > 4) { + // add data from closure to context + stua_dict *e = (stua_dict *) stu__pd(f->f.closure_data); + for (i=0; i < e->limit; ++i) + if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) + if (stb_idict_add(context, e->table[i].k, e->table[i].v)) + stu__size_allocs += 8; + // use add so if it's already defined, we don't override it; that way + // explicit parameters win over applied ones, and most recent applications + // win over previous ones + f = stu__pf(f->closure_source); + } + + for (j=0, i=0; i < f->num_param; ++i) + // if it doesn't already exist, add it from the numbered parameters + if (stb_idict_add(context, f->param[i], stu__get(context, stu__int(j), stua_nil))) + ++j; + + // @TODO: if (stu__get(context, stu__int(f->num_param+1)) != STUA_NO_VALUE) // error: too many parameters + // @TODO: ditto too few parameters + + if (f->closure_source == 4) + p = f->f.func(context); + else { + stu__f = f->code, stu__c = context; + stu__f_obj = co; + ++stb__stua_nesting; + if (stu__f[1]) + p = stu__eval(stu__f[1]); + else + p = stua_nil; + --stb__stua_nesting; + stu__f = tf, stu__c = tc; // restore previous function + if (stu__flow == FLOW_return) { + stu__flow = FLOW_normal; + p = stu__flow_val; + stu__flow_val = stua_nil; + } + } + + stua_poproot(); + stua_poproot(); + + return p; +} + +// Parser + +static int stu__tok; +static stua_obj stu__tokval; + +static char *stu__curbuf, *stu__bufstart; + +static stb_matcher *stu__lex_matcher; + +static unsigned char stu__prec[ST__max_terminals], stu__end[ST__max_terminals]; + +static void stu__nexttoken(void) +{ + int len; + +retry: + stu__tok = stb_lex(stu__lex_matcher, stu__curbuf, &len); + if (stu__tok == 0) + return; + switch(stu__tok) { + case ST_white : stu__curbuf += len; goto retry; + case T__none : stu__tok = *stu__curbuf; break; + case ST_string: stu__tokval = make_string(stu__curbuf+1, len-2); break; + case ST_id : stu__tokval = make_string(stu__curbuf, len); break; + case ST_hex : stu__tokval = stu__makeint(strtol(stu__curbuf+2,NULL,16)); stu__tok = ST_number; break; + case ST_decimal: stu__tokval = stu__makeint(strtol(stu__curbuf ,NULL,10)); stu__tok = ST_number; break; + case ST_float : stu__tokval = stua_float((float) atof(stu__curbuf)) ; stu__tok = ST_number; break; + case ST_char : stu__tokval = stu__curbuf[2] == '\\' ? stu__curbuf[3] : stu__curbuf[2]; + if (stu__curbuf[3] == 't') stu__tokval = '\t'; + if (stu__curbuf[3] == 'n') stu__tokval = '\n'; + if (stu__curbuf[3] == 'r') stu__tokval = '\r'; + stu__tokval = stu__makeint(stu__tokval); + stu__tok = ST_number; + break; + } + stu__curbuf += len; +} + +static struct { int stu__tok; char *regex; } stu__lexemes[] = +{ + ST_white , "([ \t\n\r]|/\\*(.|\n)*\\*/|//[^\r\n]*([\r\n]|$))+", + ST_id , "[_a-zA-Z][_a-zA-Z0-9]*", + ST_hex , "0x[0-9a-fA-F]+", + ST_decimal, "[0-9]+[0-9]*", + ST_float , "[0-9]+\\.?[0-9]*([eE][-+]?[0-9]+)?", + ST_float , "\\.[0-9]+([eE][-+]?[0-9]+)?", + ST_char , "c'(\\\\.|[^\\'])'", + ST_string , "\"(\\\\.|[^\\\"\n\r])*\"", + ST_string , "\'(\\\\.|[^\\\'\n\r])*\'", + + #define stua_key4(a,b,c,d) ST_##a, #a, ST_##b, #b, ST_##c, #c, ST_##d, #d, + stua_key4(if,then,else,elseif) stua_key4(while,do,for,in) + stua_key4(func,var,let,break) stua_key4(nil,true,false,end) + stua_key4(return,continue,as,repeat) stua_key4(_frame,catch,catch,catch) + + ST_shl, "<<", ST_and, "&&", ST_eq, "==", ST_ge, ">=", + ST_shr, ">>", ST_or , "||", ST_ne, "!=", ST_le, "<=", + ST_shru,">>>", ST_into, "=>", + T__none, ".", +}; + +typedef struct +{ + stua_obj *data; // constants being compiled + short *code; // code being compiled + stua_dict *locals; + short *non_local_refs; +} stu__comp_func; + +static stu__comp_func stu__pfunc; +static stu__comp_func *func_stack = NULL; +static void stu__push_func_comp(void) +{ + stb_arr_push(func_stack, stu__pfunc); + stu__pfunc.data = NULL; + stu__pfunc.code = NULL; + stu__pfunc.locals = stb_idict_new_size(16); + stu__pfunc.non_local_refs = NULL; + stb_arr_push(stu__pfunc.code, 0); // number of data items + stb_arr_push(stu__pfunc.code, 1); // starting execution address +} + +static void stu__pop_func_comp(void) +{ + stb_arr_free(stu__pfunc.code); + stb_arr_free(stu__pfunc.data); + stb_idict_destroy(stu__pfunc.locals); + stb_arr_free(stu__pfunc.non_local_refs); + stu__pfunc = stb_arr_pop(func_stack); +} + +// if an id is a reference to an outer lexical scope, this +// function returns the "name" of it, and updates the stack +// structures to make sure the names are propogated in. +static int stu__nonlocal_id(stua_obj var_obj) +{ + stua_obj dummy, var = var_obj; + int i, n = stb_arr_len(func_stack), j,k; + if (stb_idict_get_flag(stu__pfunc.locals, var, &dummy)) return 0; + for (i=n-1; i > 1; --i) { + if (stb_idict_get_flag(func_stack[i].locals, var, &dummy)) + break; + } + if (i <= 1) return 0; // stu__compile_global_scope + j = i; // need to access variable from j'th frame + for (i=0; i < stb_arr_len(stu__pfunc.non_local_refs); ++i) + if (stu__pfunc.non_local_refs[i] == j) return j-n; + stb_arr_push(stu__pfunc.non_local_refs, j-n); + // now make sure all the parents propogate it down + for (k=n-1; k > 1; --k) { + if (j-k >= 0) return j-n; // comes direct from this parent + for(i=0; i < stb_arr_len(func_stack[k].non_local_refs); ++i) + if (func_stack[k].non_local_refs[i] == j-k) + return j-n; + stb_arr_push(func_stack[k].non_local_refs, j-k); + } + assert (k != 1); + + return j-n; +} + +static int stu__off(void) { return stb_arr_len(stu__pfunc.code); } +static void stu__cc(int a) +{ + assert(a >= -2000 && a < 5000); + stb_arr_push(stu__pfunc.code, a); +} +static int stu__cc1(int a) { stu__cc(a); return stu__off()-1; } +static int stu__cc2(int a, int b) { stu__cc(a); stu__cc(b); return stu__off()-2; } +static int stu__cc3(int a, int b, int c) { + if (a == '=') assert(c != 0); + stu__cc(a); stu__cc(b); stu__cc(c); return stu__off()-3; } +static int stu__cc4(int a, int b, int c, int d) { stu__cc(a); stu__cc(b); stu__cc(c); stu__cc(d); return stu__off()-4; } + +static int stu__cdv(stua_obj p) +{ + int i; + assert(p != STUA_NO_VALUE); + for (i=0; i < stb_arr_len(stu__pfunc.data); ++i) + if (stu__pfunc.data[i] == p) + break; + if (i == stb_arr_len(stu__pfunc.data)) + stb_arr_push(stu__pfunc.data, p); + return ~i; +} + +static int stu__cdt(void) +{ + int z = stu__cdv(stu__tokval); + stu__nexttoken(); + return z; +} + +static int stu__seq(int a, int b) +{ + return !a ? b : !b ? a : stu__cc3(STU__seq, a,b); +} + +static char stu__comp_err_str[1024]; +static int stu__comp_err_line; +static int stu__err(char *str, ...) +{ + va_list v; + char *s = stu__bufstart; + stu__comp_err_line = 1; + while (s < stu__curbuf) { + if (s[0] == '\n' || s[0] == '\r') { + if (s[0]+s[1] == '\n' + '\r') ++s; + ++stu__comp_err_line; + } + ++s; + } + va_start(v, str); + vsprintf(stu__comp_err_str, str, v); + va_end(v); + return 0; +} + +static int stu__accept(int p) +{ + if (stu__tok != p) return 0; + stu__nexttoken(); + return 1; +} + +static int stu__demand(int p) +{ + if (stu__accept(p)) return 1; + return stu__err("Didn't find expected stu__tok"); +} + +static int stu__demandv(int p, stua_obj *val) +{ + if (stu__tok == p || p==0) { + *val = stu__tokval; + stu__nexttoken(); + return 1; + } else + return 0; +} + +static int stu__expr(int p); +int stu__nexpr(int p) { stu__nexttoken(); return stu__expr(p); } +static int stu__statements(int once, int as); + +static int stu__parse_if(void) // parse both ST_if and ST_elseif +{ + int b,c,a; + a = stu__nexpr(1); if (!a) return 0; + if (!stu__demand(ST_then)) return stu__err("expecting THEN"); + b = stu__statements(0,0); if (!b) return 0; + if (b == 1) b = -1; + + if (stu__tok == ST_elseif) { + return stu__parse_if(); + } else if (stu__accept(ST_else)) { + c = stu__statements(0,0); if (!c) return 0; + if (!stu__demand(ST_end)) return stu__err("expecting END after else clause"); + return stu__cc4(ST_else, a, b, c); + } else { + if (!stu__demand(ST_end)) return stu__err("expecting END in if statement"); + return stu__cc3(ST_if, a, b); + } +} + +int stu__varinit(int z, int in_globals) +{ + int a,b; + stu__nexttoken(); + while (stu__demandv(ST_id, &b)) { + if (!stb_idict_add(stu__pfunc.locals, b, 1)) + if (!in_globals) return stu__err("Redefined variable %s.", stu__pw(b)->ptr); + if (stu__accept('=')) { + a = stu__expr(1); if (!a) return 0; + } else + a = stu__cdv(stua_nil); + z = stu__seq(z, stu__cc3(ST_var, stu__cdv(b), a)); + if (!stu__accept(',')) break; + } + return z; +} + +static int stu__compile_unary(int z, int outparm, int require_inparm) +{ + int op = stu__tok, a, b; + stu__nexttoken(); + if (outparm) { + if (require_inparm || (stu__tok && stu__tok != ST_end && stu__tok != ST_else && stu__tok != ST_elseif && stu__tok !=';')) { + a = stu__expr(1); if (!a) return 0; + } else + a = stu__cdv(stua_nil); + b = stu__cc2(op, a); + } else + b = stu__cc1(op); + return stu__seq(z,b); +} + +static int stu__assign(void) +{ + int z; + stu__accept(ST_let); + z = stu__expr(1); if (!z) return 0; + if (stu__accept('=')) { + int y,p = (z >= 0 ? stu__pfunc.code[z] : 0); + if (z < 0 || (p != ST_id && p != '[')) return stu__err("Invalid lvalue in assignment"); + y = stu__assign(); if (!y) return 0; + z = stu__cc3('=', z, y); + } + return z; +} + +static int stu__statements(int once, int stop_while) +{ + int a,b, c, z=0; + for(;;) { + switch (stu__tok) { + case ST_if : a = stu__parse_if(); if (!a) return 0; + z = stu__seq(z, a); + break; + case ST_while : if (stop_while) return (z ? z:1); + a = stu__nexpr(1); if (!a) return 0; + if (stu__accept(ST_as)) c = stu__statements(0,0); else c = 0; + if (!stu__demand(ST_do)) return stu__err("expecting DO"); + b = stu__statements(0,0); if (!b) return 0; + if (!stu__demand(ST_end)) return stu__err("expecting END"); + if (b == 1) b = -1; + z = stu__seq(z, stu__cc4(ST_while, a, b, c)); + break; + case ST_repeat : stu__nexttoken(); + c = stu__statements(0,1); if (!c) return 0; + if (!stu__demand(ST_while)) return stu__err("expecting WHILE"); + a = stu__expr(1); if (!a) return 0; + if (!stu__demand(ST_do)) return stu__err("expecting DO"); + b = stu__statements(0,0); if (!b) return 0; + if (!stu__demand(ST_end)) return stu__err("expecting END"); + if (b == 1) b = -1; + z = stu__seq(z, stu__cc4(ST_as, a, b, c)); + break; + case ST_catch : a = stu__nexpr(1); if (!a) return 0; + z = stu__seq(z, stu__cc2(ST_catch, a)); + break; + case ST_var : z = stu__varinit(z,0); break; + case ST_return : z = stu__compile_unary(z,1,1); break; + case ST_continue:z = stu__compile_unary(z,0,0); break; + case ST_break : z = stu__compile_unary(z,1,0); break; + case ST_into : if (z == 0 && !once) return stu__err("=> cannot be first statement in block"); + a = stu__nexpr(99); + b = (a >= 0? stu__pfunc.code[a] : 0); + if (a < 0 || (b != ST_id && b != '[')) return stu__err("Invalid lvalue on right side of =>"); + z = stu__cc3('=', a, z); + break; + default : if (stu__end[stu__tok]) return once ? 0 : (z ? z:1); + a = stu__assign(); if (!a) return 0; + stu__accept(';'); + if (stu__tok && !stu__end[stu__tok]) { + if (a < 0) + return stu__err("Constant has no effect"); + if (stu__pfunc.code[a] != '(' && stu__pfunc.code[a] != '=') + return stu__err("Expression has no effect"); + } + z = stu__seq(z, a); + break; + } + if (!z) return 0; + stu__accept(';'); + if (once && stu__tok != ST_into) return z; + } +} + +static int stu__postexpr(int z, int p); +static int stu__dictdef(int end, int *count) +{ + int z,n=0,i,flags=0; + short *dict=NULL; + stu__nexttoken(); + while (stu__tok != end) { + if (stu__tok == ST_id) { + stua_obj id = stu__tokval; + stu__nexttoken(); + if (stu__tok == '=') { + flags |= 1; + stb_arr_push(dict, stu__cdv(id)); + z = stu__nexpr(1); if (!z) return 0; + } else { + z = stu__cc2(ST_id, stu__cdv(id)); + z = stu__postexpr(z,1); if (!z) return 0; + flags |= 2; + stb_arr_push(dict, stu__cdv(stu__makeint(n++))); + } + } else { + z = stu__expr(1); if (!z) return 0; + flags |= 2; + stb_arr_push(dict, stu__cdv(stu__makeint(n++))); + } + if (end != ')' && flags == 3) { z=stu__err("can't mix initialized and uninitialized defs"); goto done;} + stb_arr_push(dict, z); + if (!stu__accept(',')) break; + } + if (!stu__demand(end)) + return stu__err(end == ')' ? "Expecting ) at end of function call" + : "Expecting } at end of dictionary definition"); + z = stu__cc2('{', stb_arr_len(dict)/2); + for (i=0; i < stb_arr_len(dict); ++i) + stu__cc(dict[i]); + if (count) *count = n; +done: + stb_arr_free(dict); + return z; +} + +static int stu__comp_id(void) +{ + int z,d; + d = stu__nonlocal_id(stu__tokval); + if (d == 0) + return z = stu__cc2(ST_id, stu__cdt()); + // access a non-local frame by naming it with the appropriate int + assert(d < 0); + z = stu__cdv(d); // relative frame # is the 'variable' in our local frame + z = stu__cc2(ST_id, z); // now access that dictionary + return stu__cc3('[', z, stu__cdt()); // now access the variable from that dir +} + +static stua_obj stu__funcdef(stua_obj *id, stua_obj *func); +static int stu__expr(int p) +{ + int z; + // unary + switch (stu__tok) { + case ST_number: z = stu__cdt(); break; + case ST_string: z = stu__cdt(); break; // @TODO - string concatenation like C + case ST_id : z = stu__comp_id(); break; + case ST__frame: z = stu__cc1(ST__frame); stu__nexttoken(); break; + case ST_func : z = stu__funcdef(NULL,NULL); break; + case ST_if : z = stu__parse_if(); break; + case ST_nil : z = stu__cdv(stua_nil); stu__nexttoken(); break; + case ST_true : z = stu__cdv(stua_true); stu__nexttoken(); break; + case ST_false : z = stu__cdv(stua_false); stu__nexttoken(); break; + case '-' : z = stu__nexpr(99); if (z) z=stu__cc2(STU__negate,z); else return z; break; + case '!' : z = stu__nexpr(99); if (z) z=stu__cc2('!',z); else return z; break; + case '~' : z = stu__nexpr(99); if (z) z=stu__cc2('~',z); else return z; break; + case '{' : z = stu__dictdef('}', NULL); break; + default : return stu__err("Unexpected token"); + case '(' : stu__nexttoken(); z = stu__statements(0,0); if (!stu__demand(')')) return stu__err("Expecting )"); + } + return stu__postexpr(z,p); +} + +static int stu__postexpr(int z, int p) +{ + int q; + // postfix + while (stu__tok == '(' || stu__tok == '[' || stu__tok == '.') { + if (stu__accept('.')) { + // MUST be followed by a plain identifier! use [] for other stuff + if (stu__tok != ST_id) return stu__err("Must follow . with plain name; try [] instead"); + z = stu__cc3('[', z, stu__cdv(stu__tokval)); + stu__nexttoken(); + } else if (stu__accept('[')) { + while (stu__tok != ']') { + int r = stu__expr(1); if (!r) return 0; + z = stu__cc3('[', z, r); + if (!stu__accept(',')) break; + } + if (!stu__demand(']')) return stu__err("Expecting ]"); + } else { + int n, p = stu__dictdef(')', &n); if (!p) return 0; + #if 0 // this is incorrect! + if (z > 0 && stu__pfunc.code[z] == ST_id) { + stua_obj q = stu__get(stu__globaldict, stu__pfunc.data[-stu__pfunc.code[z+1]-1], stua_nil); + if (stu__checkt(STU___function, q)) + if ((stu__pf(q))->num_param != n) + return stu__err("Incorrect number of parameters"); + } + #endif + z = stu__cc3('(', z, p); + } + } + // binop - this implementation taken from lcc + for (q=stu__prec[stu__tok]; q >= p; --q) { + while (stu__prec[stu__tok] == q) { + int o = stu__tok, y = stu__nexpr(p+1); if (!y) return 0; + z = stu__cc3(o,z,y); + } + } + return z; +} + +static stua_obj stu__finish_func(stua_obj *param, int start) +{ + int n, size; + stu__func *f = (stu__func *) malloc(sizeof(*f)); + f->closure_source = 0; + f->num_param = stb_arr_len(param); + f->param = (int *) stb_copy(param, f->num_param * sizeof(*f->param)); + size = stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data) + sizeof(*f) + 8; + f->f.store = malloc(stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data)); + f->code = (short *) ((char *) f->f.store + stb_arr_storage(stu__pfunc.data)); + memcpy(f->code, stu__pfunc.code, stb_arr_storage(stu__pfunc.code)); + f->code[1] = start; + f->code[0] = stb_arr_len(stu__pfunc.data); + for (n=0; n < f->code[0]; ++n) + ((stua_obj *) f->code)[-1-n] = stu__pfunc.data[n]; + return stu__makeobj(STU___function, f, size, 0); +} + +static int stu__funcdef(stua_obj *id, stua_obj *result) +{ + int n,z=0,i,q; + stua_obj *param = NULL; + short *nonlocal; + stua_obj v,f=stua_nil; + assert(stu__tok == ST_func); + stu__nexttoken(); + if (id) { + if (!stu__demandv(ST_id, id)) return stu__err("Expecting function name"); + } else + stu__accept(ST_id); + if (!stu__demand('(')) return stu__err("Expecting ( for function parameter"); + stu__push_func_comp(); + while (stu__tok != ')') { + if (!stu__demandv(ST_id, &v)) { z=stu__err("Expecting parameter name"); goto done; } + stb_idict_add(stu__pfunc.locals, v, 1); + if (stu__tok == '=') { + n = stu__nexpr(1); if (!n) { z=0; goto done; } + z = stu__seq(z, stu__cc3(STU__defaultparm, stu__cdv(v), n)); + } else + stb_arr_push(param, v); + if (!stu__accept(',')) break; + } + if (!stu__demand(')')) { z=stu__err("Expecting ) at end of parameter list"); goto done; } + n = stu__statements(0,0); if (!n) { z=0; goto done; } + if (!stu__demand(ST_end)) { z=stu__err("Expecting END at end of function"); goto done; } + if (n == 1) n = 0; + n = stu__seq(z,n); + f = stu__finish_func(param, n); + if (result) { *result = f; z=1; stu__pop_func_comp(); } + else { + nonlocal = stu__pfunc.non_local_refs; + stu__pfunc.non_local_refs = NULL; + stu__pop_func_comp(); + z = stu__cdv(f); + if (nonlocal) { // build a closure with references to the needed frames + short *initcode = NULL; + for (i=0; i < stb_arr_len(nonlocal); ++i) { + int k = nonlocal[i], p; + stb_arr_push(initcode, stu__cdv(k)); + if (k == -1) p = stu__cc1(ST__frame); + else { p = stu__cdv(stu__makeint(k+1)); p = stu__cc2(ST_id, p); } + stb_arr_push(initcode, p); + } + q = stu__cc2('{', stb_arr_len(nonlocal)); + for (i=0; i < stb_arr_len(initcode); ++i) + stu__cc(initcode[i]); + z = stu__cc3('+', z, q); + stb_arr_free(initcode); + } + stb_arr_free(nonlocal); + } +done: + stb_arr_free(param); + if (!z) stu__pop_func_comp(); + return z; +} + +static int stu__compile_global_scope(void) +{ + stua_obj o; + int z=0; + + stu__push_func_comp(); + while (stu__tok != 0) { + if (stu__tok == ST_func) { + stua_obj id, f; + if (!stu__funcdef(&id,&f)) + goto error; + stu__set(stu__globaldict, id, f); + } else if (stu__tok == ST_var) { + z = stu__varinit(z,1); if (!z) goto error; + } else { + int y = stu__statements(1,0); if (!y) goto error; + z = stu__seq(z,y); + } + stu__accept(';'); + } + o = stu__finish_func(NULL, z); + stu__pop_func_comp(); + + o = stu__funceval(o, stua_globals); // initialize stu__globaldict + if (stu__flow == FLOW_error) + printf("Error: %s\n", ((stu__wrapper *) stu__ptr(stu__flow_val))->ptr); + return 1; +error: + stu__pop_func_comp(); + return 0; +} + +stua_obj stu__myprint(stua_dict *context) +{ + stua_obj x = stu__get(context, stua_string("x"), stua_nil); + if ((x & 1) == stu__float_tag) printf("%f", stu__getfloat(x)); + else if (stu__tag(x) == stu__int_tag) printf("%d", stu__int(x)); + else { + stu__wrapper *s = stu__pw(x); + if (s->type == STU___string || s->type == STU___error) + printf("%s", s->ptr); + else if (s->type == STU___dict) printf("{{dictionary}}"); + else if (s->type == STU___function) printf("[[function]]"); + else + printf("[[ERROR:%s]]", s->ptr); + } + return x; +} + +void stua_init(void) +{ + if (!stu__globaldict) { + int i; + stua_obj s; + stu__func *f; + + stu__prec[ST_and] = stu__prec[ST_or] = 1; + stu__prec[ST_eq ] = stu__prec[ST_ne] = stu__prec[ST_le] = + stu__prec[ST_ge] = stu__prec['>' ] = stu__prec['<'] = 2; + stu__prec[':'] = 3; + stu__prec['&'] = stu__prec['|'] = stu__prec['^'] = 4; + stu__prec['+'] = stu__prec['-'] = 5; + stu__prec['*'] = stu__prec['/'] = stu__prec['%'] = + stu__prec[ST_shl]= stu__prec[ST_shr]= stu__prec[ST_shru]= 6; + + stu__end[')'] = stu__end[ST_end] = stu__end[ST_else] = 1; + stu__end[ST_do] = stu__end[ST_elseif] = 1; + + stu__float_init(); + stu__lex_matcher = stb_lex_matcher(); + for (i=0; i < sizeof(stu__lexemes)/sizeof(stu__lexemes[0]); ++i) + stb_lex_item(stu__lex_matcher, stu__lexemes[i].regex, stu__lexemes[i].stu__tok); + + stu__globaldict = stb_idict_new_size(64); + stua_globals = stu__makeobj(STU___dict, stu__globaldict, 0,0); + stu__strings = stb_sdict_new(0); + + stu__curbuf = stu__bufstart = "func _print(x) end\n" + "func print()\n var x=0 while _frame[x] != nil as x=x+1 do _print(_frame[x]) end end\n"; + stu__nexttoken(); + if (!stu__compile_global_scope()) + printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); + + s = stu__get(stu__globaldict, stua_string("_print"), stua_nil); + if (stu__tag(s) == stu__ptr_tag && stu__ptr(s)->type == STU___function) { + f = stu__pf(s); + free(f->f.store); + f->closure_source = 4; + f->f.func = stu__myprint; + f->code = NULL; + } + } +} + +void stua_uninit(void) +{ + if (stu__globaldict) { + stb_idict_remove_all(stu__globaldict); + stb_arr_setlen(stu__gc_root_stack, 0); + stua_gc(1); + stb_idict_destroy(stu__globaldict); + stb_sdict_delete(stu__strings); + stb_matcher_free(stu__lex_matcher); + stb_arr_free(stu__gc_ptrlist); + stb_arr_free(func_stack); + stb_arr_free(stu__gc_root_stack); + stu__globaldict = NULL; + } +} + +void stua_run_script(char *s) +{ + stua_init(); + + stu__curbuf = stu__bufstart = s; + stu__nexttoken(); + + stu__flow = FLOW_normal; + + if (!stu__compile_global_scope()) + printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); + stua_gc(1); +} +#endif // STB_DEFINE + +#endif // STB_STUA + + +#undef STB_EXTERN +#endif // STB_INCLUDE_STB_H + diff --git a/test_data/lots_of_files/stb_c_lexer.h b/test_data/lots_of_files/stb_c_lexer.h new file mode 100644 index 0000000..1cf53e4 --- /dev/null +++ b/test_data/lots_of_files/stb_c_lexer.h @@ -0,0 +1,815 @@ +// stb_c_lexer.h - v0.06 - public domain Sean Barrett 2013 +// lexer for making little C-like languages with recursive-descent parsers +// +// This file provides both the interface and the implementation. +// To instantiate the implementation, +// #define STB_C_LEXER_IMPLEMENTATION +// in *ONE* source file, before #including this file. +// +// The default configuration is fairly close to a C lexer, although +// suffixes on integer constants are not handled (you can override this). +// +// History: +// 0.06 fix missing next character after ending quote mark (Andreas Fredriksson) +// 0.05 refixed get_location because github version had lost the fix +// 0.04 fix octal parsing bug +// 0.03 added STB_C_LEX_DISCARD_PREPROCESSOR option +// refactor API to simplify (only one struct instead of two) +// change literal enum names to have 'lit' at the end +// 0.02 first public release +// +// Status: +// - haven't tested compiling as C++ +// - haven't tested the float parsing path +// - haven't tested the non-default-config paths (e.g. non-stdlib) +// - only tested default-config paths by eyeballing output of self-parse +// +// - haven't implemented multiline strings +// - haven't implemented octal/hex character constants +// - haven't implemented support for unicode CLEX_char +// - need to expand error reporting so you don't just get "CLEX_parse_error" +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + +#ifndef STB_C_LEXER_DEFINITIONS +// to change the default parsing rules, copy the following lines +// into your C/C++ file *before* including this, and then replace +// the Y's with N's for the ones you don't want. +// --BEGIN-- + +#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit +#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit +#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit +#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE]-?[0-9]+)?) CLEX_floatlit +#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id +#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring +#define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring +#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits +#define STB_C_LEX_C_COMMENTS Y // "/* comment */" +#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n" +#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq +#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror +#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr +#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus +#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow +#define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow +#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq +#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq + // "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq + // if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ: + // "<<=" CLEX_shleq ">>=" CLEX_shreq + +#define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below +#define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage +#define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL" +#define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL" +#define STB_C_LEX_FLOAT_SUFFIXES "" // + +#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token +#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N +#define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings +#define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings +#define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack +#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character +#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent + +#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned + // leaving it as N should help you catch config bugs + +#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess + // still have #line, #pragma, etc) + +//#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of first character if it is whitespace + +#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions +// --END-- + +#endif + +#ifndef INCLUDE_STB_C_LEXER_H +#define INCLUDE_STB_C_LEXER_H + +typedef struct +{ + // lexer variables + char *input_stream; + char *eof; + char *parse_point; + char *string_storage; + int string_storage_len; + + // lexer parse location for error messages + char *where_firstchar; + char *where_lastchar; + + // lexer token variables + long token; + double real_number; + long int_number; + char *string; + int string_len; +} stb_lexer; + +typedef struct +{ + int line_number; + int line_offset; +} stb_lex_location; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length); +// this function initialize the 'lexer' structure +// Input: +// - input_stream points to the file to parse, loaded into memory +// - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF +// - string_store is storage the lexer can use for storing parsed strings and identifiers +// - store_length is the length of that storage + +extern int stb_c_lexer_get_token(stb_lexer *lexer); +// this function returns non-zero if a token is parsed, or 0 if at EOF +// Output: +// - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error +// - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES +// - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit +// - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier +// - lexer->string_len is the byte length of lexer->string + +extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc); +// this inefficient function returns the line number and character offset of a +// given location in the file as returned by stb_lex_token. Because it's inefficient, +// you should only call it for errors, not for every token. +// For error messages of invalid tokens, you typically want the location of the start +// of the token (which caused the token to be invalid). For bugs involving legit +// tokens, you can report the first or the range. +// Output: +// - loc->line_number is the line number in the file, counting from 1, of the location +// - loc->line_offset is the char-offset in the line, counting from 0, of the location + + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_STB_C_LEXER_H + +#ifdef STB_C_LEXER_IMPLEMENTATION + + #if defined(Y) || defined(N) + #error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined" + #endif + + +// Hacky definitions so we can easily #if on them +#define Y(x) 1 +#define N(x) 0 + +#if STB_C_LEX_USE_STDLIB(x) +#define STB__CLEX_use_stdlib +#include <stdlib.h> +#endif + +#if STB_C_LEX_INTEGERS_AS_DOUBLES(x) +typedef double stb__clex_int; +#define intfield real_number +#define STB__clex_int_as_double +#else +typedef long stb__clex_int; +#define intfield int_number +#endif + +// Convert these config options to simple conditional #defines so we can more +// easily test them once we've change the meaning of Y/N + +#if STB_C_LEX_PARSE_SUFFIXES(x) +#define STB__clex_parse_suffixes +#endif + +#if STB_C_LEX_C_DECIMAL_INTS(x) || STB_C_LEX_C_HEX_INTS(x) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) +#define STB__clex_define_int +#endif + +#if (STB_C_LEX_C_ARITHEQ(x) && STB_C_LEX_C_SHIFTS(x)) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) +#define STB__clex_define_shifts +#endif + +#if STB_C_LEX_C_HEX_INTS(x) +#define STB__clex_hex_ints +#endif + +#if STB_C_LEX_C_DECIMAL_INTS(x) +#define STB__clex_decimal_ints +#endif + +#if STB_C_LEX_C_OCTAL_INTS(x) +#define STB__clex_octal_ints +#endif + +#if STB_C_LEX_C_DECIMAL_FLOATS(x) +#define STB__clex_decimal_floats +#endif + +#if STB_C_LEX_DISCARD_PREPROCESSOR(x) +#define STB__clex_discard_preprocessor +#endif + +// Now pick a definition of Y/N that's conducive to +// defining the enum of token names. +#if STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) || defined(STB_C_LEXER_SELF_TEST) + #undef N + #define N(a) Y(a) +#else + #undef N + #define N(a) +#endif + +#undef Y +#define Y(a) a, + +enum +{ + CLEX_eof = 256, + CLEX_parse_error, + +#ifdef STB__clex_define_int + CLEX_intlit, +#endif + + STB_C_LEX_C_DECIMAL_FLOATS( CLEX_floatlit ) + STB_C_LEX_C_IDENTIFIERS( CLEX_id ) + STB_C_LEX_C_DQ_STRINGS( CLEX_dqstring ) + STB_C_LEX_C_SQ_STRINGS( CLEX_sqstring ) + STB_C_LEX_C_CHARS( CLEX_charlit ) + STB_C_LEX_C_COMPARISONS( CLEX_eq ) + STB_C_LEX_C_COMPARISONS( CLEX_noteq ) + STB_C_LEX_C_COMPARISONS( CLEX_lesseq ) + STB_C_LEX_C_COMPARISONS( CLEX_greatereq ) + STB_C_LEX_C_LOGICAL( CLEX_andand ) + STB_C_LEX_C_LOGICAL( CLEX_oror ) + STB_C_LEX_C_SHIFTS( CLEX_shl ) + STB_C_LEX_C_SHIFTS( CLEX_shr ) + STB_C_LEX_C_INCREMENTS( CLEX_plusplus ) + STB_C_LEX_C_INCREMENTS( CLEX_minusminus ) + STB_C_LEX_C_ARITHEQ( CLEX_pluseq ) + STB_C_LEX_C_ARITHEQ( CLEX_minuseq ) + STB_C_LEX_C_ARITHEQ( CLEX_muleq ) + STB_C_LEX_C_ARITHEQ( CLEX_diveq ) + STB_C_LEX_C_ARITHEQ( CLEX_modeq ) + STB_C_LEX_C_BITWISEEQ( CLEX_andeq ) + STB_C_LEX_C_BITWISEEQ( CLEX_oreq ) + STB_C_LEX_C_BITWISEEQ( CLEX_xoreq ) + STB_C_LEX_C_ARROW( CLEX_arrow ) + STB_C_LEX_EQUAL_ARROW( CLEX_eqarrow ) + +#ifdef STB__clex_define_shifts + CLEX_shleq, CLEX_shreq, +#endif + + CLEX_first_unused_token + +#undef Y +#define Y(a) a +}; + +// Now for the rest of the file we'll use the basic definition where +// where Y expands to its contents and N expands to nothing +#undef N +#define N(a) + +// API function +void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length) +{ + lexer->input_stream = (char *) input_stream; + lexer->eof = (char *) input_stream_end; + lexer->parse_point = (char *) input_stream; + lexer->string_storage = string_store; + lexer->string_storage_len = store_length; +} + +// API function +void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc) +{ + char *p = lexer->input_stream; + int line_number = 1; + int char_offset = 0; + while (*p && p < where) { + if (*p == '\n' || *p == '\r') { + p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline + line_number += 1; + char_offset = 0; + } else { + ++p; + ++char_offset; + } + } + loc->line_number = line_number; + loc->line_offset = char_offset; +} + +// main helper function for returning a parsed token +static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end) +{ + lexer->token = token; + lexer->where_firstchar = start; + lexer->where_lastchar = end; + lexer->parse_point = end+1; + return 1; +} + +// helper function for returning eof +static int stb__clex_eof(stb_lexer *lexer) +{ + lexer->token = CLEX_eof; + return 0; +} + +static int stb__clex_iswhite(int x) +{ + return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f'; +} + +static const char *stb__strchr(const char *str, int ch) +{ + for (; *str; ++str) + if (*str == ch) + return str; + return 0; +} + +// parse suffixes at the end of a number +static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes) +{ + #ifdef STB__clex_parse_suffixes + lexer->string = lexer->string_storage; + lexer->string_len = 0; + + while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) { + if (stb__strchr(suffixes, *cur) == 0) + return stb__clex_token(lexer, CLEX_parse_error, start, cur); + if (lexer->string_len+1 >= lexer->string_storage_len) + return stb__clex_token(lexer, CLEX_parse_error, start, cur); + lexer->string[lexer->string_len++] = *cur++; + } + #else + suffixes = suffixes; // attempt to suppress warnings + #endif + return stb__clex_token(lexer, tokenid, start, cur-1); +} + +#ifndef STB__CLEX_use_stdlib +static double stb__clex_parse_float(char *p, char **q) +{ + double value=0; + while (*p >= '0' && *p <= '9') + value = value*10 + (*p++ - '0'); + if (*p == '.') { + double powten=1, addend = 0; + ++p; + while (*p >= '0' && *p <= '9') { + addend = addend + 10*(*p++ - '0'); + powten *= 10; + } + value += addend / powten; + } + if (*p == 'e' || *p == 'E') { + int sign = p[1] == '-'; + int exponent=0; + double pow10=1; + p += 1+sign; + while (*p >= '0' && *p <= '9') + exponent = exponent*10 + (*p++ - '0'); + // can't use pow() from stdlib, so do it slow way + while (exponent-- > 0) + pow10 *= 10; + if (sign) + value /= pow10; + else + value *= pow10; + } + *q = p; + return value; +} +#endif + +static int stb__clex_parse_char(char *p, char **q) +{ + if (*p == '\\') { + *q = p+2; // tentatively guess we'll parse two characters + switch(p[1]) { + case '\\': return '\\'; + case '\'': return '\''; + case '"': return '"'; + case 't': return '\t'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case '0': return '\0'; // @TODO ocatal constants + case 'x': case 'X': return -1; // @TODO hex constants + case 'u': return -1; // @TODO unicode constants + } + } + *q = p+1; + return (unsigned char) *p; +} + +static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type) +{ + char *start = p; + char delim = *p++; // grab the " or ' for later matching + char *out = lexer->string_storage; + char *outend = lexer->string_storage + lexer->string_storage_len; + while (*p != delim) { + int n; + if (*p == '\\') { + char *q; + n = stb__clex_parse_char(p, &q); + if (n < 0) + return stb__clex_token(lexer, CLEX_parse_error, start, q); + p = q; + } else { + // @OPTIMIZE: could speed this up by looping-while-not-backslash + n = (unsigned char) *p++; + } + if (out+1 > outend) + return stb__clex_token(lexer, CLEX_parse_error, start, p); + // @TODO expand unicode escapes to UTF8 + *out++ = (char) n; + } + *out = 0; + lexer->string = lexer->string_storage; + lexer->string_len = out - lexer->string_storage; + return stb__clex_token(lexer, type, start, p); +} + +int stb_c_lexer_get_token(stb_lexer *lexer) +{ + char *p = lexer->parse_point; + + // skip whitespace and comments + for (;;) { + #ifdef STB_C_LEX_ISWHITE + while (p != lexer->stream_end) { + int n; + n = STB_C_LEX_ISWHITE(p); + if (n == 0) break; + if (lexer->eof && lexer+n > lexer->eof) + return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1); + p += n; + } + #else + while (p != lexer->eof && stb__clex_iswhite(*p)) + ++p; + #endif + + STB_C_LEX_CPP_COMMENTS( + if (p != lexer->eof && p[0] == '/' && p[1] == '/') { + while (p != lexer->eof && *p != '\r' && *p != '\n') + ++p; + continue; + } + ) + + STB_C_LEX_C_COMMENTS( + if (p != lexer->eof && p[0] == '/' && p[1] == '*') { + char *start = p; + p += 2; + while (p != lexer->eof && (p[0] != '*' || p[1] != '/')) + ++p; + if (p == lexer->eof) + return stb__clex_token(lexer, CLEX_parse_error, start, p-1); + p += 2; + continue; + } + ) + + #ifdef STB__clex_discard_preprocessor + // @TODO this discards everything after a '#', regardless + // of where in the line the # is, rather than requiring it + // be at the start. (because this parser doesn't otherwise + // check for line breaks!) + if (p != lexer->eof && p[0] == '#') { + while (p != lexer->eof && *p != '\r' && *p != '\n') + ++p; + continue; + } + #endif + + break; + } + + if (p == lexer->eof) + return stb__clex_eof(lexer); + + switch (*p) { + default: + if ( (*p >= 'a' && *p <= 'z') + || (*p >= 'A' && *p <= 'Z') + || *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char + STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) ) + { + int n = 0; + lexer->string = lexer->string_storage; + lexer->string_len = n; + do { + if (n+1 >= lexer->string_storage_len) + return stb__clex_token(lexer, CLEX_parse_error, p, p+n); + lexer->string[n] = p[n]; + ++n; + } while ( + (p[n] >= 'a' && p[n] <= 'z') + || (p[n] >= 'A' && p[n] <= 'Z') + || (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier + || p[n] == '_' || (unsigned char) p[n] >= 128 + STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' ) + ); + lexer->string[n] = 0; + return stb__clex_token(lexer, CLEX_id, p, p+n-1); + } + + // check for EOF + STB_C_LEX_0_IS_EOF( + if (*p == 0) + return stb__clex_eof(tok); + ) + + single_char: + // not an identifier, return the character as itself + return stb__clex_token(lexer, *p, p, p); + + case '+': + if (p+1 != lexer->eof) { + STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);) + STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);) + } + goto single_char; + case '-': + if (p+1 != lexer->eof) { + STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);) + STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);) + STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);) + } + goto single_char; + case '&': + if (p+1 != lexer->eof) { + STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);) + STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);) + } + goto single_char; + case '|': + if (p+1 != lexer->eof) { + STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);) + STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);) + } + goto single_char; + case '=': + if (p+1 != lexer->eof) { + STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);) + STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);) + } + goto single_char; + case '!': + STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);) + goto single_char; + case '^': + STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1)); + goto single_char; + case '%': + STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1)); + goto single_char; + case '*': + STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1)); + goto single_char; + case '/': + STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1)); + goto single_char; + case '<': + if (p+1 != lexer->eof) { + STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);) + STB_C_LEX_C_SHIFTS( if (p[1] == '<') { + STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') + return stb__clex_token(lexer, CLEX_shleq, p,p+2);) + return stb__clex_token(lexer, CLEX_shl, p,p+1); + } + ) + } + goto single_char; + case '>': + if (p+1 != lexer->eof) { + STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);) + STB_C_LEX_C_SHIFTS( if (p[1] == '>') { + STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') + return stb__clex_token(lexer, CLEX_shreq, p,p+2);) + return stb__clex_token(lexer, CLEX_shr, p,p+1); + } + ) + } + goto single_char; + + case '"': + STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);) + goto single_char; + case '\'': + STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);) + STB_C_LEX_C_CHARS( + { + char *start = p; + lexer->int_number = stb__clex_parse_char(p+1, &p); + if (lexer->int_number < 0) + return stb__clex_token(lexer, CLEX_parse_error, start,start); + if (p == lexer->eof || *p != '\'') + return stb__clex_token(lexer, CLEX_parse_error, start,p); + return stb__clex_token(lexer, CLEX_charlit, start, p+1); + }) + goto single_char; + + case '0': + #ifdef STB__clex_hex_ints + if (p+1 != lexer->eof) { + if (p[1] == 'x' || p[1] == 'X') { + char *q = p+2; + #ifdef STB__CLEX_use_stdlib + lexer->int_number = strtol((char *) p, (char **) q, 16); + #else + stb__clex_int n=0; + while (q != lexer->eof) { + if (*q >= '0' && *q <= '9') + n = n*16 + (*q - '0'); + else if (*q >= 'a' && *q <= 'f') + n = n*16 + (*q - 'a') + 10; + else if (*q >= 'A' && *q <= 'F') + n = n*16 + (*q - 'A') + 10; + else + break; + ++q; + } + lexer->int_field = n; // int_field is macro that expands to real_number/int_number depending on type of n + #endif + if (q == p+2) + return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1); + return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES); + } + } + #endif // STB__clex_hex_ints + // can't test for octal because we might parse '0.0' as float or as '0' '.' '0', + // so have to do float first + + /* FALL THROUGH */ + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + + #ifdef STB__clex_decimal_floats + { + char *q = p; + while (q != lexer->eof && (*q >= '0' && *q <= '9')) + ++q; + if (q != lexer->eof) { + if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) { + #ifdef STB__CLEX_use_stdlib + lexer->real_number = strtod((char *) p, (char**) &q); + #else + lexer->real_number = stb__clex_parse_float(p, &q); + #endif + + return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES); + + } + } + } + #endif // STB__clex_decimal_floats + + #ifdef STB__clex_octal_ints + if (p[0] == '0') { + char *q = p; + #ifdef STB__CLEX_use_stdlib + lexer->int_number = strtol((char *) p, (char **) &q, 8); + #else + stb__clex_int n=0; + while (q != lexer->eof) { + if (*q >= '0' && *q <= '7') + n = n*8 + (q - '0'); + else + break; + ++q; + } + if (q != lexer->eof && (*q == '8' || *q=='9')) + return stb__clex_token(tok, CLEX_parse_error, p, q); + lexer->int_field = n; + #endif + return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); + } + #endif // STB__clex_octal_ints + + #ifdef STB__clex_decimal_ints + { + char *q = p; + #ifdef STB__CLEX_use_stdlib + lexer->int_number = strtol((char *) p, (char **) &q, 10); + #else + stb__clex_int n=0; + while (q != lexer->eof) { + if (*q >= '0' && *q <= '9') + n = n*10 + (q - '0'); + else + break; + ++q; + } + lexer->int_field = n; + #endif + return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); + } + #endif // STB__clex_decimal_ints + goto single_char; + } +} +#endif // STB_C_LEXER_IMPLEMENTATION + +#ifdef STB_C_LEXER_SELF_TEST + +#include <stdio.h> + +static void print_token(stb_lexer *lexer) +{ + switch (lexer->token) { + case CLEX_id : printf("_%s", lexer->string); break; + case CLEX_eq : printf("=="); break; + case CLEX_noteq : printf("!="); break; + case CLEX_lesseq : printf("<="); break; + case CLEX_greatereq : printf(">="); break; + case CLEX_andand : printf("&&"); break; + case CLEX_oror : printf("||"); break; + case CLEX_shl : printf("<<"); break; + case CLEX_shr : printf(">>"); break; + case CLEX_plusplus : printf("++"); break; + case CLEX_minusminus: printf("--"); break; + case CLEX_arrow : printf("->"); break; + case CLEX_andeq : printf("&="); break; + case CLEX_oreq : printf("|="); break; + case CLEX_xoreq : printf("^="); break; + case CLEX_pluseq : printf("+="); break; + case CLEX_minuseq : printf("-="); break; + case CLEX_muleq : printf("*="); break; + case CLEX_diveq : printf("/="); break; + case CLEX_modeq : printf("%%="); break; + case CLEX_shleq : printf("<<="); break; + case CLEX_shreq : printf(">>="); break; + case CLEX_eqarrow : printf("=>"); break; + case CLEX_dqstring : printf("\"%s\"", lexer->string); break; + case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break; + case CLEX_charlit : printf("'%s'", lexer->string); break; + #if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib) + case CLEX_intlit : printf("#%g", lexer->real_number); break; + #else + case CLEX_intlit : printf("#%ld", lexer->int_number); break; + #endif + case CLEX_floatlit : printf("%g", lexer->real_number); break; + default: + if (lexer->token >= 0 && lexer->token < 256) + printf("%c", (int) lexer->token); + else { + printf("<<<UNKNOWN TOKEN %ld >>>\n", lexer->token); + } + break; + } +} + +/* Force a test +of parsing +multiline comments */ + +/*/ comment /*/ +/**/ extern /**/ + +void dummy(void) +{ + printf("test",1); // https://github.com/nothings/stb/issues/13 +} + +int main(int argc, char **argv) +{ + FILE *f = fopen("stb_c_lexer.h","rb"); + char *text = (char *) malloc(1 << 20); + int len = f ? fread(text, 1, 1<<20, f) : -1; + stb_lexer lex; + if (len < 0) { + fprintf(stderr, "Error opening file\n"); + return 1; + } + fclose(f); + + stb_c_lexer_init(&lex, text, text+len, (char *) malloc(1<<16), 1<<16); + while (stb_c_lexer_get_token(&lex)) { + if (lex.token == CLEX_parse_error) { + printf("\n<<<PARSE ERROR>>>\n"); + break; + } + print_token(&lex); + printf(" "); + } + return 0; +} +#endif diff --git a/test_data/lots_of_files/stb_divide.h b/test_data/lots_of_files/stb_divide.h new file mode 100644 index 0000000..4e35abe --- /dev/null +++ b/test_data/lots_of_files/stb_divide.h @@ -0,0 +1,379 @@ +// stb_divide.h - v0.91 - public domain - Sean Barrett, Feb 2010 +// Three kinds of divide/modulus of signed integers. +// +// HISTORY +// +// v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C +// Check result with 64-bit math to catch such cases +// v0.90 2010-02-24 First public release +// +// USAGE +// +// In *ONE* source file, put: +// +// #define STB_DIVIDE_IMPLEMENTATION +// // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1 +// // #define C_INTEGER_DIVISION_FLOORS // see Note 2 +// #include "stb_divide.h" +// +// Other source files should just include stb_divide.h +// +// Note 1: On platforms/compilers that you know signed C division +// truncates, you can #define C_INTEGER_DIVISION_TRUNCATES. +// +// Note 2: On platforms/compilers that you know signed C division +// floors (rounds to negative infinity), you can #define +// C_INTEGER_DIVISION_FLOORS. +// +// You can #define STB_DIVIDE_TEST in which case the implementation +// will generate a main() and compiling the result will create a +// program that tests the implementation. Run it with no arguments +// and any output indicates an error; run it with any argument and +// it will also print the test results. Define STB_DIVIDE_TEST_64 +// to a 64-bit integer type to avoid overflows in the result-checking +// which give false negatives. +// +// ABOUT +// +// This file provides three different consistent divide/mod pairs +// implemented on top of arbitrary C/C++ division, including correct +// handling of overflow of intermediate calculations: +// +// trunc: a/b truncates to 0, a%b has same sign as a +// floor: a/b truncates to -inf, a%b has same sign as b +// eucl: a/b truncates to sign(b)*inf, a%b is non-negative +// +// Not necessarily optimal; I tried to keep it generally efficient, +// but there may be better ways. +// +// Briefly, for those who are not familiar with the problem, we note +// the reason these divides exist and are interesting: +// +// 'trunc' is easy to implement in hardware (strip the signs, +// compute, reapply the signs), thus is commonly defined +// by many languages (including C99) +// +// 'floor' is simple to define and better behaved than trunc; +// for example it divides integers into fixed-size buckets +// without an extra-wide bucket at 0, and for a fixed +// divisor N there are only |N| possible moduli. +// +// 'eucl' guarantees fixed-sized buckets *and* a non-negative +// modulus and defines division to be whatever is needed +// to achieve that result. +// +// See "The Euclidean definition of the functions div and mod" +// by Raymond Boute (1992), or "Division and Modulus for Computer +// Scientists" by Daan Leijen (2001) +// +// We assume of the built-in C division: +// (a) modulus is the remainder for the corresponding division +// (b) a/b truncates if a and b are the same sign +// +// Property (a) requires (a/b)*b + (a%b)==a, and is required by C. +// Property (b) seems to be true of all hardware but is *not* satisfied +// by the euclidean division operator we define, so it's possibly not +// always true. If any such platform turns up, we can add more cases. +// (Possibly only stb_div_trunc currently relies on property (b).) +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + + +#ifndef INCLUDE_STB_DIVIDE_H +#define INCLUDE_STB_DIVIDE_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by); +extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by); +extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by); +extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by); +extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by); +extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by); + +#ifdef __cplusplus +} +#endif + +#ifdef STB_DIVIDE_IMPLEMENTATION + +#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901 + #ifndef C_INTEGER_DIVISION_TRUNCATES + #define C_INTEGER_DIVISION_TRUNCATES + #endif +#endif + +#ifndef INT_MIN +#include <limits.h> // if you have no limits.h, #define INT_MIN yourself +#endif + +// the following macros are designed to allow testing +// other platforms by simulating them +#ifndef STB_DIVIDE_TEST_FLOOR + #define stb__div(a,b) ((a)/(b)) + #define stb__mod(a,b) ((a)%(b)) +#else + // implement floor-style divide on trunc platform + #ifndef C_INTEGER_DIVISION_TRUNCATES + #error "floor test requires truncating division" + #endif + #undef C_INTEGER_DIVISION_TRUNCATES + int stb__div(int v1, int v2) + { + int q = v1/v2, r = v1%v2; + if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) + return q-1; + else + return q; + } + + int stb__mod(int v1, int v2) + { + int r = v1%v2; + if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) + return r+v2; + else + return r; + } +#endif + +int stb_div_trunc(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_TRUNCATES + return v1/v2; + #else + if (v1 >= 0 && v2 <= 0) + return -stb__div(-v1,v2); // both negative to avoid overflow + if (v1 <= 0 && v2 >= 0) + if (v1 != INT_MIN) + return -stb__div(v1,-v2); // both negative to avoid overflow + else + return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point + else + return v1/v2; // same sign, so expect truncation + #endif +} + +int stb_div_floor(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_FLOORS + return v1/v2; + #else + if (v1 >= 0 && v2 < 0) + if ((-v1)+v2+1 < 0) // check if increasing v1's magnitude overflows + return -stb__div(-v1+v2+1,v2); // nope, so just compute it + else + return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0); + if (v1 < 0 && v2 >= 0) + if (v1 != INT_MIN) + if (v1-v2+1 < 0) // check if increasing v1's magnitude overflows + return -stb__div(v1-v2+1,-v2); // nope, so just compute it + else + return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0); + else // it must be possible to compute -(v1+v2) without overflowing + return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1); + else + return v1/v2; // same sign, so expect truncation + #endif +} + +int stb_div_eucl(int v1, int v2) +{ + int q,r; + #ifdef C_INTEGER_DIVISION_TRUNCATES + q = v1/v2; + r = v1%v2; + #else + // handle every quadrant separately, since we can't rely on q and r flor + if (v1 >= 0) + if (v2 >= 0) + return stb__div(v1,v2); + else if (v2 != INT_MIN) + q = -stb__div(v1,-v2), r = stb__mod(v1,-v2); + else + q = 0, r = v1; + else if (v1 != INT_MIN) + if (v2 >= 0) + q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2); + else if (v2 != INT_MIN) + q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2); + else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2 + q = 1, r = v1-q*v2; + else // if v1 is INT_MIN, we have to move away from overflow place + if (v2 >= 0) + q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2); + else + q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2); + #endif + if (r >= 0) + return q; + else + return q + (v2 > 0 ? -1 : 1); +} + +int stb_mod_trunc(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_TRUNCATES + return v1%v2; + #else + if (v1 >= 0) { // modulus result should always be positive + int r = stb__mod(v1,v2); + if (r >= 0) + return r; + else + return r + (v2 > 0 ? v2 : -v2); + } else { // modulus result should always be negative + int r = stb__mod(v1,v2); + if (r <= 0) + return r; + else + return r - (v2 > 0 ? v2 : -v2); + } + #endif +} + +int stb_mod_floor(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_FLOORS + return v1%v2; + #else + if (v2 >= 0) { // result should always be positive + int r = stb__mod(v1,v2); + if (r >= 0) + return r; + else + return r + v2; + } else { // result should always be negative + int r = stb__mod(v1,v2); + if (r <= 0) + return r; + else + return r + v2; + } + #endif +} + +int stb_mod_eucl(int v1, int v2) +{ + int r = stb__mod(v1,v2); + + if (r >= 0) + return r; + else + return r + (v2 > 0 ? v2 : -v2); // abs() +} + +#ifdef STB_DIVIDE_TEST +#include <stdio.h> +#include <math.h> +#include <limits.h> + +int show=0; + +void stbdiv_check(int q, int r, int a, int b, char *type, int dir) +{ + if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r); + else + if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid + if (r <= -abs(b) || r >= abs(b)) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r); + #ifdef STB_DIVIDE_TEST_64 + { + STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b; + if (q64*b64+r64 != a64) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); + } + #else + if (q*b+r != a) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); + #endif +} + +void test(int a, int b) +{ + int q,r; + if (show) printf("(%+11d,%+d) | ", a,b); + q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); + if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a); + q = stb_div_floor(a,b), r = stb_mod_floor(a,b); + if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b); + q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); + if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1); +} + +void testh(int a, int b) +{ + int q,r; + if (show) printf("(%08x,%08x) |\n", a,b); + q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a); + if (show) printf(" (%08x,%08x)", q,r); + q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b); + if (show) printf(" (%08x,%08x)", q,r); + q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1); + if (show) printf(" (%08x,%08x)\n ", q,r); +} + +int main(int argc, char **argv) +{ + if (argc > 1) show=1; + + test(8,3); + test(8,-3); + test(-8,3); + test(-8,-3); + test(1,2); + test(1,-2); + test(-1,2); + test(-1,-2); + test(8,4); + test(8,-4); + test(-8,4); + test(-8,-4); + + test(INT_MAX,1); + test(INT_MIN,1); + test(INT_MIN+1,1); + test(INT_MAX,-1); + //test(INT_MIN,-1); // this traps in MSVC, so we leave it untested + test(INT_MIN+1,-1); + test(INT_MIN,-2); + test(INT_MIN+1,2); + test(INT_MIN+1,-2); + test(INT_MAX,2); + test(INT_MAX,-2); + test(INT_MIN+1,2); + test(INT_MIN+1,-2); + test(INT_MIN,2); + test(INT_MIN,-2); + test(INT_MIN,7); + test(INT_MIN,-7); + test(INT_MIN+1,4); + test(INT_MIN+1,-4); + + testh(-7, INT_MIN); + testh(-1, INT_MIN); + testh(1, INT_MIN); + testh(7, INT_MIN); + + testh(INT_MAX-1, INT_MIN); + testh(INT_MAX, INT_MIN); + testh(INT_MIN, INT_MIN); + testh(INT_MIN+1, INT_MIN); + + testh(INT_MAX-1, INT_MAX); + testh(INT_MAX , INT_MAX); + testh(INT_MIN , INT_MAX); + testh(INT_MIN+1, INT_MAX); + + return 0; +} +#endif // STB_DIVIDE_TEST +#endif // STB_DIVIDE_IMPLEMENTATION +#endif // INCLUDE_STB_DIVIDE_H diff --git a/test_data/lots_of_files/stb_dxt.h b/test_data/lots_of_files/stb_dxt.h new file mode 100644 index 0000000..418ba65 --- /dev/null +++ b/test_data/lots_of_files/stb_dxt.h @@ -0,0 +1,630 @@ +// stb_dxt.h - v1.04 - DXT1/DXT5 compressor - public domain +// original by fabian "ryg" giesen - ported to C by stb +// use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation +// +// USAGE: +// call stb_compress_dxt_block() for every block (you must pad) +// source should be a 4x4 block of RGBA data in row-major order; +// A is ignored if you specify alpha=0; you can turn on dithering +// and "high quality" using mode. +// +// version history: +// v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec); +// single color match fix (allow for inexact color interpolation); +// optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps. +// v1.03 - (stb) endianness support +// v1.02 - (stb) fix alpha encoding bug +// v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom +// v1.00 - (stb) first release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + +#ifndef STB_INCLUDE_STB_DXT_H +#define STB_INCLUDE_STB_DXT_H + +// compression mode (bitflags) +#define STB_DXT_NORMAL 0 +#define STB_DXT_DITHER 1 // use dithering. dubious win. never use for normal maps and the like! +#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower. + +void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode); +#define STB_COMPRESS_DXT_BLOCK + +#ifdef STB_DXT_IMPLEMENTATION + +// configuration options for DXT encoder. set them in the project/makefile or just define +// them at the top. + +// STB_DXT_USE_ROUNDING_BIAS +// use a rounding bias during color interpolation. this is closer to what "ideal" +// interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03) +// implicitly had this turned on. +// +// in case you're targeting a specific type of hardware (e.g. console programmers): +// NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer +// to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias. +// you also see "(a*5 + b*3) / 8" on some old GPU designs. +// #define STB_DXT_USE_ROUNDING_BIAS + +#include <stdlib.h> +#include <math.h> +#include <string.h> // memset + +static unsigned char stb__Expand5[32]; +static unsigned char stb__Expand6[64]; +static unsigned char stb__OMatch5[256][2]; +static unsigned char stb__OMatch6[256][2]; +static unsigned char stb__QuantRBTab[256+16]; +static unsigned char stb__QuantGTab[256+16]; + +static int stb__Mul8Bit(int a, int b) +{ + int t = a*b + 128; + return (t + (t >> 8)) >> 8; +} + +static void stb__From16Bit(unsigned char *out, unsigned short v) +{ + int rv = (v & 0xf800) >> 11; + int gv = (v & 0x07e0) >> 5; + int bv = (v & 0x001f) >> 0; + + out[0] = stb__Expand5[rv]; + out[1] = stb__Expand6[gv]; + out[2] = stb__Expand5[bv]; + out[3] = 0; +} + +static unsigned short stb__As16Bit(int r, int g, int b) +{ + return (stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31); +} + +// linear interpolation at 1/3 point between a and b, using desired rounding type +static int stb__Lerp13(int a, int b) +{ +#ifdef STB_DXT_USE_ROUNDING_BIAS + // with rounding bias + return a + stb__Mul8Bit(b-a, 0x55); +#else + // without rounding bias + // replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed. + return (2*a + b) / 3; +#endif +} + +// lerp RGB color +static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2) +{ + out[0] = stb__Lerp13(p1[0], p2[0]); + out[1] = stb__Lerp13(p1[1], p2[1]); + out[2] = stb__Lerp13(p1[2], p2[2]); +} + +/****************************************************************************/ + +// compute table to reproduce constant colors as accurately as possible +static void stb__PrepareOptTable(unsigned char *Table,const unsigned char *expand,int size) +{ + int i,mn,mx; + for (i=0;i<256;i++) { + int bestErr = 256; + for (mn=0;mn<size;mn++) { + for (mx=0;mx<size;mx++) { + int mine = expand[mn]; + int maxe = expand[mx]; + int err = abs(stb__Lerp13(maxe, mine) - i); + + // DX10 spec says that interpolation must be within 3% of "correct" result, + // add this as error term. (normally we'd expect a random distribution of + // +-1.5% error, but nowhere in the spec does it say that the error has to be + // unbiased - better safe than sorry). + err += abs(maxe - mine) * 3 / 100; + + if(err < bestErr) + { + Table[i*2+0] = mx; + Table[i*2+1] = mn; + bestErr = err; + } + } + } + } +} + +static void stb__EvalColors(unsigned char *color,unsigned short c0,unsigned short c1) +{ + stb__From16Bit(color+ 0, c0); + stb__From16Bit(color+ 4, c1); + stb__Lerp13RGB(color+ 8, color+0, color+4); + stb__Lerp13RGB(color+12, color+4, color+0); +} + +// Block dithering function. Simply dithers a block to 565 RGB. +// (Floyd-Steinberg) +static void stb__DitherBlock(unsigned char *dest, unsigned char *block) +{ + int err[8],*ep1 = err,*ep2 = err+4, *et; + int ch,y; + + // process channels seperately + for (ch=0; ch<3; ++ch) { + unsigned char *bp = block+ch, *dp = dest+ch; + unsigned char *quant = (ch == 1) ? stb__QuantGTab+8 : stb__QuantRBTab+8; + memset(err, 0, sizeof(err)); + for(y=0; y<4; ++y) { + dp[ 0] = quant[bp[ 0] + ((3*ep2[1] + 5*ep2[0]) >> 4)]; + ep1[0] = bp[ 0] - dp[ 0]; + dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)]; + ep1[1] = bp[ 4] - dp[ 4]; + dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)]; + ep1[2] = bp[ 8] - dp[ 8]; + dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)]; + ep1[3] = bp[12] - dp[12]; + bp += 16; + dp += 16; + et = ep1, ep1 = ep2, ep2 = et; // swap + } + } +} + +// The color matching function +static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color,int dither) +{ + unsigned int mask = 0; + int dirr = color[0*4+0] - color[1*4+0]; + int dirg = color[0*4+1] - color[1*4+1]; + int dirb = color[0*4+2] - color[1*4+2]; + int dots[16]; + int stops[4]; + int i; + int c0Point, halfPoint, c3Point; + + for(i=0;i<16;i++) + dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb; + + for(i=0;i<4;i++) + stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb; + + // think of the colors as arranged on a line; project point onto that line, then choose + // next color out of available ones. we compute the crossover points for "best color in top + // half"/"best in bottom half" and then the same inside that subinterval. + // + // relying on this 1d approximation isn't always optimal in terms of euclidean distance, + // but it's very close and a lot faster. + // http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html + + c0Point = (stops[1] + stops[3]) >> 1; + halfPoint = (stops[3] + stops[2]) >> 1; + c3Point = (stops[2] + stops[0]) >> 1; + + if(!dither) { + // the version without dithering is straightforward + for (i=15;i>=0;i--) { + int dot = dots[i]; + mask <<= 2; + + if(dot < halfPoint) + mask |= (dot < c0Point) ? 1 : 3; + else + mask |= (dot < c3Point) ? 2 : 0; + } + } else { + // with floyd-steinberg dithering + int err[8],*ep1 = err,*ep2 = err+4; + int *dp = dots, y; + + c0Point <<= 4; + halfPoint <<= 4; + c3Point <<= 4; + for(i=0;i<8;i++) + err[i] = 0; + + for(y=0;y<4;y++) + { + int dot,lmask,step; + + dot = (dp[0] << 4) + (3*ep2[1] + 5*ep2[0]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[0] = dp[0] - stops[step]; + lmask = step; + + dot = (dp[1] << 4) + (7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[1] = dp[1] - stops[step]; + lmask |= step<<2; + + dot = (dp[2] << 4) + (7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[2] = dp[2] - stops[step]; + lmask |= step<<4; + + dot = (dp[3] << 4) + (7*ep1[2] + 5*ep2[3] + ep2[2]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[3] = dp[3] - stops[step]; + lmask |= step<<6; + + dp += 4; + mask |= lmask << (y*8); + { int *et = ep1; ep1 = ep2; ep2 = et; } // swap + } + } + + return mask; +} + +// The color optimization function. (Clever code, part 1) +static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16) +{ + int mind = 0x7fffffff,maxd = -0x7fffffff; + unsigned char *minp, *maxp; + double magn; + int v_r,v_g,v_b; + static const int nIterPower = 4; + float covf[6],vfr,vfg,vfb; + + // determine color distribution + int cov[6]; + int mu[3],min[3],max[3]; + int ch,i,iter; + + for(ch=0;ch<3;ch++) + { + const unsigned char *bp = ((const unsigned char *) block) + ch; + int muv,minv,maxv; + + muv = minv = maxv = bp[0]; + for(i=4;i<64;i+=4) + { + muv += bp[i]; + if (bp[i] < minv) minv = bp[i]; + else if (bp[i] > maxv) maxv = bp[i]; + } + + mu[ch] = (muv + 8) >> 4; + min[ch] = minv; + max[ch] = maxv; + } + + // determine covariance matrix + for (i=0;i<6;i++) + cov[i] = 0; + + for (i=0;i<16;i++) + { + int r = block[i*4+0] - mu[0]; + int g = block[i*4+1] - mu[1]; + int b = block[i*4+2] - mu[2]; + + cov[0] += r*r; + cov[1] += r*g; + cov[2] += r*b; + cov[3] += g*g; + cov[4] += g*b; + cov[5] += b*b; + } + + // convert covariance matrix to float, find principal axis via power iter + for(i=0;i<6;i++) + covf[i] = cov[i] / 255.0f; + + vfr = (float) (max[0] - min[0]); + vfg = (float) (max[1] - min[1]); + vfb = (float) (max[2] - min[2]); + + for(iter=0;iter<nIterPower;iter++) + { + float r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2]; + float g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4]; + float b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5]; + + vfr = r; + vfg = g; + vfb = b; + } + + magn = fabs(vfr); + if (fabs(vfg) > magn) magn = fabs(vfg); + if (fabs(vfb) > magn) magn = fabs(vfb); + + if(magn < 4.0f) { // too small, default to luminance + v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000. + v_g = 587; + v_b = 114; + } else { + magn = 512.0 / magn; + v_r = (int) (vfr * magn); + v_g = (int) (vfg * magn); + v_b = (int) (vfb * magn); + } + + // Pick colors at extreme points + for(i=0;i<16;i++) + { + int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b; + + if (dot < mind) { + mind = dot; + minp = block+i*4; + } + + if (dot > maxd) { + maxd = dot; + maxp = block+i*4; + } + } + + *pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]); + *pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]); +} + +static int stb__sclamp(float y, int p0, int p1) +{ + int x = (int) y; + if (x < p0) return p0; + if (x > p1) return p1; + return x; +} + +// The refinement function. (Clever code, part 2) +// Tries to optimize colors to suit block contents better. +// (By solving a least squares system via normal equations+Cramer's rule) +static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask) +{ + static const int w1Tab[4] = { 3,0,2,1 }; + static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 }; + // ^some magic to save a lot of multiplies in the accumulating loop... + // (precomputed products of weights for least squares system, accumulated inside one 32-bit register) + + float frb,fg; + unsigned short oldMin, oldMax, min16, max16; + int i, akku = 0, xx,xy,yy; + int At1_r,At1_g,At1_b; + int At2_r,At2_g,At2_b; + unsigned int cm = mask; + + oldMin = *pmin16; + oldMax = *pmax16; + + if((mask ^ (mask<<2)) < 4) // all pixels have the same index? + { + // yes, linear system would be singular; solve using optimal + // single-color match on average color + int r = 8, g = 8, b = 8; + for (i=0;i<16;++i) { + r += block[i*4+0]; + g += block[i*4+1]; + b += block[i*4+2]; + } + + r >>= 4; g >>= 4; b >>= 4; + + max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; + min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; + } else { + At1_r = At1_g = At1_b = 0; + At2_r = At2_g = At2_b = 0; + for (i=0;i<16;++i,cm>>=2) { + int step = cm&3; + int w1 = w1Tab[step]; + int r = block[i*4+0]; + int g = block[i*4+1]; + int b = block[i*4+2]; + + akku += prods[step]; + At1_r += w1*r; + At1_g += w1*g; + At1_b += w1*b; + At2_r += r; + At2_g += g; + At2_b += b; + } + + At2_r = 3*At2_r - At1_r; + At2_g = 3*At2_g - At1_g; + At2_b = 3*At2_b - At1_b; + + // extract solutions and decide solvability + xx = akku >> 16; + yy = (akku >> 8) & 0xff; + xy = (akku >> 0) & 0xff; + + frb = 3.0f * 31.0f / 255.0f / (xx*yy - xy*xy); + fg = frb * 63.0f / 31.0f; + + // solve. + max16 = stb__sclamp((At1_r*yy - At2_r*xy)*frb+0.5f,0,31) << 11; + max16 |= stb__sclamp((At1_g*yy - At2_g*xy)*fg +0.5f,0,63) << 5; + max16 |= stb__sclamp((At1_b*yy - At2_b*xy)*frb+0.5f,0,31) << 0; + + min16 = stb__sclamp((At2_r*xx - At1_r*xy)*frb+0.5f,0,31) << 11; + min16 |= stb__sclamp((At2_g*xx - At1_g*xy)*fg +0.5f,0,63) << 5; + min16 |= stb__sclamp((At2_b*xx - At1_b*xy)*frb+0.5f,0,31) << 0; + } + + *pmin16 = min16; + *pmax16 = max16; + return oldMin != min16 || oldMax != max16; +} + +// Color block compression +static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode) +{ + unsigned int mask; + int i; + int dither; + int refinecount; + unsigned short max16, min16; + unsigned char dblock[16*4],color[4*4]; + + dither = mode & STB_DXT_DITHER; + refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1; + + // check if block is constant + for (i=1;i<16;i++) + if (((unsigned int *) block)[i] != ((unsigned int *) block)[0]) + break; + + if(i == 16) { // constant color + int r = block[0], g = block[1], b = block[2]; + mask = 0xaaaaaaaa; + max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; + min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; + } else { + // first step: compute dithered version for PCA if desired + if(dither) + stb__DitherBlock(dblock,block); + + // second step: pca+map along principal axis + stb__OptimizeColorsBlock(dither ? dblock : block,&max16,&min16); + if (max16 != min16) { + stb__EvalColors(color,max16,min16); + mask = stb__MatchColorsBlock(block,color,dither); + } else + mask = 0; + + // third step: refine (multiple times if requested) + for (i=0;i<refinecount;i++) { + unsigned int lastmask = mask; + + if (stb__RefineBlock(dither ? dblock : block,&max16,&min16,mask)) { + if (max16 != min16) { + stb__EvalColors(color,max16,min16); + mask = stb__MatchColorsBlock(block,color,dither); + } else { + mask = 0; + break; + } + } + + if(mask == lastmask) + break; + } + } + + // write the color block + if(max16 < min16) + { + unsigned short t = min16; + min16 = max16; + max16 = t; + mask ^= 0x55555555; + } + + dest[0] = (unsigned char) (max16); + dest[1] = (unsigned char) (max16 >> 8); + dest[2] = (unsigned char) (min16); + dest[3] = (unsigned char) (min16 >> 8); + dest[4] = (unsigned char) (mask); + dest[5] = (unsigned char) (mask >> 8); + dest[6] = (unsigned char) (mask >> 16); + dest[7] = (unsigned char) (mask >> 24); +} + +// Alpha block compression (this is easy for a change) +static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src,int mode) +{ + int i,dist,bias,dist4,dist2,bits,mask; + + // find min/max color + int mn,mx; + mn = mx = src[3]; + + for (i=1;i<16;i++) + { + if (src[i*4+3] < mn) mn = src[i*4+3]; + else if (src[i*4+3] > mx) mx = src[i*4+3]; + } + + // encode them + ((unsigned char *)dest)[0] = mx; + ((unsigned char *)dest)[1] = mn; + dest += 2; + + // determine bias and emit color indices + // given the choice of mx/mn, these indices are optimal: + // http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/ + dist = mx-mn; + dist4 = dist*4; + dist2 = dist*2; + bias = (dist < 8) ? (dist - 1) : (dist/2 + 2); + bias -= mn * 7; + bits = 0,mask=0; + + for (i=0;i<16;i++) { + int a = src[i*4+3]*7 + bias; + int ind,t; + + // select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max). + t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t; + t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t; + ind += (a >= dist); + + // turn linear scale into DXT index (0/1 are extremal pts) + ind = -ind & 7; + ind ^= (2 > ind); + + // write index + mask |= ind << bits; + if((bits += 3) >= 8) { + *dest++ = mask; + mask >>= 8; + bits -= 8; + } + } +} + +static void stb__InitDXT() +{ + int i; + for(i=0;i<32;i++) + stb__Expand5[i] = (i<<3)|(i>>2); + + for(i=0;i<64;i++) + stb__Expand6[i] = (i<<2)|(i>>4); + + for(i=0;i<256+16;i++) + { + int v = i-8 < 0 ? 0 : i-8 > 255 ? 255 : i-8; + stb__QuantRBTab[i] = stb__Expand5[stb__Mul8Bit(v,31)]; + stb__QuantGTab[i] = stb__Expand6[stb__Mul8Bit(v,63)]; + } + + stb__PrepareOptTable(&stb__OMatch5[0][0],stb__Expand5,32); + stb__PrepareOptTable(&stb__OMatch6[0][0],stb__Expand6,64); +} + +void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode) +{ + static int init=1; + if (init) { + stb__InitDXT(); + init=0; + } + + if (alpha) { + stb__CompressAlphaBlock(dest,(unsigned char*) src,mode); + dest += 8; + } + + stb__CompressColorBlock(dest,(unsigned char*) src,mode); +} +#endif // STB_DXT_IMPLEMENTATION + +#endif // STB_INCLUDE_STB_DXT_H diff --git a/test_data/lots_of_files/stb_easy_font.h b/test_data/lots_of_files/stb_easy_font.h new file mode 100644 index 0000000..4dee383 --- /dev/null +++ b/test_data/lots_of_files/stb_easy_font.h @@ -0,0 +1,232 @@ +// stb_easy_font.h - v0.6 - bitmap font for 3D rendering - public domain +// Sean Barrett, Feb 2015 +// +// Easy-to-deploy, +// reasonably compact, +// extremely inefficient performance-wise, +// crappy-looking, +// ASCII-only, +// bitmap font for use in 3D APIs. +// +// Intended for when you just want to get some text displaying +// in a 3D app as quickly as possible. +// +// Doesn't use any textures, instead builds characters out of quads. +// +// DOCUMENTATION: +// +// int stb_easy_font_width(char *text) +// +// Takes a string without newlines and returns the horizontal size. +// +// int stb_easy_font_print(float x, float y, +// char *text, unsigned char color[4], +// void *vertex_buffer, int vbuf_size) +// +// Takes a string (which can contain '\n') and fills out a +// vertex buffer with renderable data to draw the string. +// Output data assumes increasing x is rightwards, increasing y +// is downwards. +// +// The vertex data is divided into quads, i.e. there are four +// vertices in the vertex buffer for each quad. +// +// The vertices are stored in an interleaved format: +// +// x:float +// y:float +// z:float +// color:uint8[4] +// +// You can ignore z and color if you get them from elsewhere +// This format was chosen in the hopes it would make it +// easier for you to reuse existing buffer-drawing code. +// +// If you pass in NULL for color, it becomes 255,255,255,255. +// +// Returns the number of quads. +// +// If the buffer isn't large enough, it will truncate. +// Expect it to use an average of ~270 bytes per character. +// +// If your API doesn't draw quads, build a reusable index +// list that allows you to render quads as indexed triangles. +// +// void stb_easy_font_spacing(float spacing) +// +// Use positive values to expand the space between characters, +// and small negative values (no smaller than -1.5) to contract +// the space between characters. +// +// E.g. spacing = 1 adds one "pixel" of spacing between the +// characters. spacing = -1 is reasonable but feels a bit too +// compact to me; -0.5 is a reasonable compromise as long as +// you're scaling the font up. +// +// SAMPLE CODE: +// +// Here's sample code for old OpenGL; it's a lot more complicated +// to make work on modern APIs, and that's your problem. +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// +// VERSION HISTORY +// +// (2015-09-13) 0.6 #include <math.h>; updated license +// (2015-02-01) 0.5 First release + +#if 0 +void print_string(float x, float y, char *text, float r, float g, float b) +{ + static char buffer[99999]; // ~500 chars + int num_quads; + + num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer)); + + glColor3f(r,g,b); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 16, buffer); + glDrawArrays(GL_QUADS, 0, num_quads*4); + glDisableClientState(GL_VERTEX_ARRAY); +} +#endif + +#ifndef INCLUDE_STB_EASY_FONT_H +#define INCLUDE_STB_EASY_FONT_H + +#include <stdlib.h> +#include <math.h> + +struct { + unsigned char advance; + unsigned char h_seg; + unsigned char v_seg; +} stb_easy_font_charinfo[96] = { + { 5, 0, 0 }, { 3, 0, 0 }, { 5, 1, 1 }, { 7, 1, 4 }, + { 7, 3, 7 }, { 7, 6, 12 }, { 7, 8, 19 }, { 4, 16, 21 }, + { 4, 17, 22 }, { 4, 19, 23 }, { 23, 21, 24 }, { 23, 22, 31 }, + { 20, 23, 34 }, { 22, 23, 36 }, { 19, 24, 36 }, { 21, 25, 36 }, + { 6, 25, 39 }, { 6, 27, 43 }, { 6, 28, 45 }, { 6, 30, 49 }, + { 6, 33, 53 }, { 6, 34, 57 }, { 6, 40, 58 }, { 6, 46, 59 }, + { 6, 47, 62 }, { 6, 55, 64 }, { 19, 57, 68 }, { 20, 59, 68 }, + { 21, 61, 69 }, { 22, 66, 69 }, { 21, 68, 69 }, { 7, 73, 69 }, + { 9, 75, 74 }, { 6, 78, 81 }, { 6, 80, 85 }, { 6, 83, 90 }, + { 6, 85, 91 }, { 6, 87, 95 }, { 6, 90, 96 }, { 7, 92, 97 }, + { 6, 96,102 }, { 5, 97,106 }, { 6, 99,107 }, { 6,100,110 }, + { 6,100,115 }, { 7,101,116 }, { 6,101,121 }, { 6,101,125 }, + { 6,102,129 }, { 7,103,133 }, { 6,104,140 }, { 6,105,145 }, + { 7,107,149 }, { 6,108,151 }, { 7,109,155 }, { 7,109,160 }, + { 7,109,165 }, { 7,118,167 }, { 6,118,172 }, { 4,120,176 }, + { 6,122,177 }, { 4,122,181 }, { 23,124,182 }, { 22,129,182 }, + { 4,130,182 }, { 22,131,183 }, { 6,133,187 }, { 22,135,191 }, + { 6,137,192 }, { 22,139,196 }, { 5,144,197 }, { 22,147,198 }, + { 6,150,202 }, { 19,151,206 }, { 21,152,207 }, { 6,155,209 }, + { 3,160,210 }, { 23,160,211 }, { 22,164,216 }, { 22,165,220 }, + { 22,167,224 }, { 22,169,228 }, { 21,171,232 }, { 21,173,233 }, + { 5,178,233 }, { 22,179,234 }, { 23,180,238 }, { 23,180,243 }, + { 23,180,248 }, { 22,189,248 }, { 22,191,252 }, { 5,196,252 }, + { 3,203,252 }, { 5,203,253 }, { 22,210,253 }, { 0,214,253 }, +}; + +unsigned char stb_easy_font_hseg[214] = { + 97,37,69,84,28,51,2,18,10,49,98,41,65,25,81,105,33,9,97,1,97,37,37,36, + 81,10,98,107,3,100,3,99,58,51,4,99,58,8,73,81,10,50,98,8,73,81,4,10,50, + 98,8,25,33,65,81,10,50,17,65,97,25,33,25,49,9,65,20,68,1,65,25,49,41, + 11,105,13,101,76,10,50,10,50,98,11,99,10,98,11,50,99,11,50,11,99,8,57, + 58,3,99,99,107,10,10,11,10,99,11,5,100,41,65,57,41,65,9,17,81,97,3,107, + 9,97,1,97,33,25,9,25,41,100,41,26,82,42,98,27,83,42,98,26,51,82,8,41, + 35,8,10,26,82,114,42,1,114,8,9,73,57,81,41,97,18,8,8,25,26,26,82,26,82, + 26,82,41,25,33,82,26,49,73,35,90,17,81,41,65,57,41,65,25,81,90,114,20, + 84,73,57,41,49,25,33,65,81,9,97,1,97,25,33,65,81,57,33,25,41,25, +}; + +unsigned char stb_easy_font_vseg[253] = { + 4,2,8,10,15,8,15,33,8,15,8,73,82,73,57,41,82,10,82,18,66,10,21,29,1,65, + 27,8,27,9,65,8,10,50,97,74,66,42,10,21,57,41,29,25,14,81,73,57,26,8,8, + 26,66,3,8,8,15,19,21,90,58,26,18,66,18,105,89,28,74,17,8,73,57,26,21, + 8,42,41,42,8,28,22,8,8,30,7,8,8,26,66,21,7,8,8,29,7,7,21,8,8,8,59,7,8, + 8,15,29,8,8,14,7,57,43,10,82,7,7,25,42,25,15,7,25,41,15,21,105,105,29, + 7,57,57,26,21,105,73,97,89,28,97,7,57,58,26,82,18,57,57,74,8,30,6,8,8, + 14,3,58,90,58,11,7,74,43,74,15,2,82,2,42,75,42,10,67,57,41,10,7,2,42, + 74,106,15,2,35,8,8,29,7,8,8,59,35,51,8,8,15,35,30,35,8,8,30,7,8,8,60, + 36,8,45,7,7,36,8,43,8,44,21,8,8,44,35,8,8,43,23,8,8,43,35,8,8,31,21,15, + 20,8,8,28,18,58,89,58,26,21,89,73,89,29,20,8,8,30,7, +}; + +typedef struct +{ + unsigned char c[4]; +} stb_easy_font_color; + +static int stb_easy_font_draw_segs(float x, float y, unsigned char *segs, int num_segs, int vertical, stb_easy_font_color c, char *vbuf, int vbuf_size, int offset) +{ + int i,j; + for (i=0; i < num_segs; ++i) { + int len = segs[i] & 7; + x += (float) ((segs[i] >> 3) & 1); + if (len && offset+64 <= vbuf_size) { + float y0 = y + (float) (segs[i]>>4); + for (j=0; j < 4; ++j) { + * (float *) (vbuf+offset+0) = x + (j==1 || j==2 ? (vertical ? 1 : len) : 0); + * (float *) (vbuf+offset+4) = y0 + ( j >= 2 ? (vertical ? len : 1) : 0); + * (float *) (vbuf+offset+8) = 0.f; + * (stb_easy_font_color *) (vbuf+offset+12) = c; + offset += 16; + } + } + } + return offset; +} + +float stb_easy_font_spacing_val = 0; +static void stb_easy_font_spacing(float spacing) +{ + stb_easy_font_spacing_val = spacing; +} + +static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size) +{ + char *vbuf = (char *) vertex_buffer; + float start_x = x; + int offset = 0; + + stb_easy_font_color c = { 255,255,255,255 }; // use structure copying to avoid needing depending on memcpy() + if (color) { c.c[0] = color[0]; c.c[1] = color[1]; c.c[2] = color[2]; c.c[3] = color[3]; } + + while (*text && offset < vbuf_size) { + if (*text == '\n') { + y += 12; + x = start_x; + } else { + unsigned char advance = stb_easy_font_charinfo[*text-32].advance; + float y_ch = advance & 16 ? y+1 : y; + int h_seg, v_seg, num_h, num_v; + h_seg = stb_easy_font_charinfo[*text-32 ].h_seg; + v_seg = stb_easy_font_charinfo[*text-32 ].v_seg; + num_h = stb_easy_font_charinfo[*text-32+1].h_seg - h_seg; + num_v = stb_easy_font_charinfo[*text-32+1].v_seg - v_seg; + offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_hseg[h_seg], num_h, 0, c, vbuf, vbuf_size, offset); + offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_vseg[v_seg], num_v, 1, c, vbuf, vbuf_size, offset); + x += advance & 15; + x += stb_easy_font_spacing_val; + } + ++text; + } + return (unsigned) offset/64; +} + +static int stb_easy_font_width(char *text) +{ + float len = 0; + while (*text) { + len += stb_easy_font_charinfo[*text-32].advance & 15; + len += stb_easy_font_spacing_val; + ++text; + } + return (int) ceil(len); +} +#endif diff --git a/test_data/lots_of_files/stb_herringbone_wang_tile.h b/test_data/lots_of_files/stb_herringbone_wang_tile.h new file mode 100644 index 0000000..de7fb20 --- /dev/null +++ b/test_data/lots_of_files/stb_herringbone_wang_tile.h @@ -0,0 +1,1220 @@ +/* stbhw - v0.6 - http://nothings.org/gamedev/herringbone + Herringbone Wang Tile Generator - Sean Barrett 2014 - public domain + +== LICENSE ============================== + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +== WHAT IT IS =========================== + + This library is an SDK for Herringbone Wang Tile generation: + + http://nothings.org/gamedev/herringbone + + The core design is that you use this library offline to generate a + "template" of the tiles you'll create. You then edit those tiles, then + load the created tile image file back into this library and use it at + runtime to generate "maps". + + You cannot load arbitrary tile image files with this library; it is + only designed to load image files made from the template it created. + It stores a binary description of the tile sizes & constraints in a + few pixels, and uses those to recover the rules, rather than trying + to parse the tiles themselves. + + You *can* use this library to generate from arbitrary tile sets, but + only by loading the tile set and specifying the constraints explicitly + yourself. + +== COMPILING ============================ + + 1. #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION before including this + header file in *one* source file to create the implementation + in that source file. + + 2. optionally #define STB_HBWANG_RAND() to be a random number + generator. if you don't define it, it will use rand(), + and you need to seed srand() yourself. + + 3. optionally #define STB_HBWANG_ASSERT(x), otherwise + it will use assert() + + 4. optionally #define STB_HBWANG_STATIC to force all symbols to be + static instead of public, so they are only accesible + in the source file that creates the implementation + + 5. optionally #define STB_HBWANG_NO_REPITITION_REDUCTION to disable + the code that tries to reduce having the same tile appear + adjacent to itself in wang-corner-tile mode (e.g. imagine + if you were doing something where 90% of things should be + the same grass tile, you need to disable this system) + + 6. optionally define STB_HBWANG_MAX_X and STB_HBWANG_MAX_Y + to be the max dimensions of the generated map in multiples + of the wang tile's short side's length (e.g. if you + have 20x10 wang tiles, so short_side_len=10, and you + have MAX_X is 17, then the largest map you can generate + is 170 pixels wide). The defaults are 100x100. This + is used to define static arrays which affect memory + usage. + +== USING ================================ + + To use the map generator, you need a tileset. You can download + some sample tilesets from http://nothings.org/gamedev/herringbone + + Then see the "sample application" below. + + You can also use this file to generate templates for + tilesets which you then hand-edit to create the data. + + +== MEMORY MANAGEMENT ==================== + + The tileset loader allocates memory with malloc(). The map + generator does no memory allocation, so e.g. you can load + tilesets at startup and never free them and never do any + further allocation. + + +== SAMPLE APPLICATION =================== + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" // http://nothings.org/stb_image.c + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" // http://nothings.org/stb/stb_image_write.h + +#define STB_HBWANG_IMPLEMENTATION +#include "stb_hbwang.h" + +int main(int argc, char **argv) +{ + unsigned char *data; + int xs,ys, w,h; + stbhw_tileset ts; + + if (argc != 4) { + fprintf(stderr, "Usage: mapgen {tile-file} {xsize} {ysize}\n" + "generates file named 'test_map.png'\n"); + exit(1); + } + data = stbi_load(argv[1], &w, &h, NULL, 3); + xs = atoi(argv[2]); + ys = atoi(argv[3]); + if (data == NULL) { + fprintf(stderr, "Error opening or parsing '%s' as an image file\n", argv[1]); + exit(1); + } + if (xs < 1 || xs > 1000) { + fprintf(stderr, "xsize invalid or out of range\n"); + exit(1); + } + if (ys < 1 || ys > 1000) { + fprintf(stderr, "ysize invalid or out of range\n"); + exit(1); + } + + stbhw_build_tileset_from_image(&ts, data, w*3, w, h); + free(data); + + // allocate a buffer to create the final image to + data = malloc(3 * xs * ys); + + srand(time(NULL)); + stbhw_generate_image(&ts, NULL, data, xs*3, xs, ys); + + stbi_write_png("test_map.png", xs, ys, 3, data, xs*3); + + stbhw_free_tileset(&ts); + free(data); + + return 0; +} + +== VERSION HISTORY =================== + + 0.6 2014-08-17 - fix broken map-maker + 0.5 2014-07-07 - initial release + +*/ + +////////////////////////////////////////////////////////////////////////////// +// // +// HEADER FILE SECTION // +// // + +#ifndef INCLUDE_STB_HWANG_H +#define INCLUDE_STB_HWANG_H + +#ifdef STB_HBWANG_STATIC +#define STBHW_EXTERN static +#else +#ifdef __cplusplus +#define STBHW_EXTERN extern "C" +#else +#define STBHW_EXTERN extern +#endif +#endif + +typedef struct stbhw_tileset stbhw_tileset; + +// returns description of last error produced by any function (not thread-safe) +STBHW_EXTERN char *stbhw_get_last_error(void); + +// build a tileset from an image that conforms to a template created by this +// library. (you allocate storage for stbhw_tileset and function fills it out; +// memory for individual tiles are malloc()ed). +// returns non-zero on success, 0 on error +STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, + unsigned char *pixels, int stride_in_bytes, int w, int h); + +// free a tileset built by stbhw_build_tileset_from_image +STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts); + +// generate a map that is w * h pixels (3-bytes each) +// returns non-zero on success, 0 on error +// not thread-safe (uses a global data structure to avoid memory management) +// weighting should be NULL, as non-NULL weighting is currently untested +STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, + unsigned char *pixels, int stride_in_bytes, int w, int h); + +////////////////////////////////////// +// +// TILESET DATA STRUCTURE +// +// if you use the image-to-tileset system from this file, you +// don't need to worry about these data structures. but if you +// want to build/load a tileset yourself, you'll need to fill +// these out. + +typedef struct +{ + // the edge or vertex constraints, according to diagram below + signed char a,b,c,d,e,f; + + // The herringbone wang tile data; it is a bitmap which is either + // w=2*short_sidelen,h=short_sidelen, or w=short_sidelen,h=2*short_sidelen. + // it is always RGB, stored row-major, with no padding between rows. + // (allocate stbhw_tile structure to be large enough for the pixel data) + unsigned char pixels[1]; +} stbhw_tile; + +struct stbhw_tileset +{ + int is_corner; + int num_color[6]; // number of colors for each of 6 edge types or 4 corner types + int short_side_len; + stbhw_tile **h_tiles; + stbhw_tile **v_tiles; + int num_h_tiles, max_h_tiles; + int num_v_tiles, max_v_tiles; +}; + +/////////////// TEMPLATE GENERATOR ////////////////////////// + +// when requesting a template, you fill out this data +typedef struct +{ + int is_corner; // using corner colors or edge colors? + int short_side_len; // rectangles is 2n x n, n = short_side_len + int num_color[6]; // see below diagram for meaning of the index to this; + // 6 values if edge (!is_corner), 4 values if is_corner + // legal numbers: 1..8 if edge, 1..4 if is_corner + int num_vary_x; // additional number of variations along x axis in the template + int num_vary_y; // additional number of variations along y axis in the template + int corner_type_color_template[4][4]; + // if corner_type_color_template[s][t] is non-zero, then any + // corner of type s generated as color t will get a little + // corner sample markup in the template image data + +} stbhw_config; + +// computes the size needed for the template image +STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h); + +// generates a template image, assuming data is 3*w*h bytes long, RGB format +STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes); + +#endif//INCLUDE_STB_HWANG_H + + +// TILE CONSTRAINT TYPES +// +// there are 4 "types" of corners and 6 types of edges. +// you can configure the tileset to have different numbers +// of colors for each type of color or edge. +// +// corner types: +// +// 0---*---1---*---2---*---3 +// | | | +// * * * +// | | | +// 1---*---2---*---3 0---*---1---*---2 +// | | | +// * * * +// | | | +// 0---*---1---*---2---*---3 +// +// +// edge types: +// +// *---2---*---3---* *---0---* +// | | | | +// 1 4 5 1 +// | | | | +// *---0---*---2---* * * +// | | +// 4 5 +// | | +// *---3---* +// +// TILE CONSTRAINTS +// +// each corner/edge has a color; this shows the name +// of the variable containing the color +// +// corner constraints: +// +// a---*---d +// | | +// * * +// | | +// a---*---b---*---c b e +// | | | | +// * * * * +// | | | | +// d---*---e---*---f c---*---f +// +// +// edge constraints: +// +// *---a---*---b---* *---a---* +// | | | | +// c d b c +// | | | | +// *---e---*---f---* * * +// | | +// d e +// | | +// *---f---* +// + + +////////////////////////////////////////////////////////////////////////////// +// // +// IMPLEMENTATION SECTION // +// // + +#ifdef STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION + + +#include <string.h> // memcpy +#include <stdlib.h> // malloc + +#ifndef STB_HBWANG_RAND +#include <stdlib.h> +#define STB_HBWANG_RAND() (rand() >> 4) +#endif + +#ifndef STB_HBWANG_ASSERT +#include <assert.h> +#define STB_HBWANG_ASSERT(x) assert(x) +#endif + +// map size +#ifndef STB_HBWANG_MAX_X +#define STB_HBWANG_MAX_X 100 +#endif + +#ifndef STB_HBWANG_MAX_Y +#define STB_HBWANG_MAX_Y 100 +#endif + +// global variables for color assignments +// @MEMORY change these to just store last two/three rows +// and keep them on the stack +static signed char c_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+6]; +static signed char v_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+5]; +static signed char h_color[STB_HBWANG_MAX_Y+5][STB_HBWANG_MAX_X+6]; + +static char *stbhw_error; +STBHW_EXTERN char *stbhw_get_last_error(void) +{ + char *temp = stbhw_error; + stbhw_error = 0; + return temp; +} + + + + +///////////////////////////////////////////////////////////// +// +// SHARED TEMPLATE-DESCRIPTION CODE +// +// Used by both template generator and tileset parser; by +// using the same code, they are locked in sync and we don't +// need to try to do more sophisticated parsing of edge color +// markup or something. + +typedef void stbhw__process_rect(struct stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f); + +typedef struct stbhw__process +{ + stbhw_tileset *ts; + stbhw_config *c; + stbhw__process_rect *process_h_rect; + stbhw__process_rect *process_v_rect; + unsigned char *data; + int stride,w,h; +} stbhw__process; + +static void stbhw__process_h_row(stbhw__process *p, + int xpos, int ypos, + int a0, int a1, + int b0, int b1, + int c0, int c1, + int d0, int d1, + int e0, int e1, + int f0, int f1, + int variants) +{ + int a,b,c,d,e,f,v; + + for (v=0; v < variants; ++v) + for (f=f0; f <= f1; ++f) + for (e=e0; e <= e1; ++e) + for (d=d0; d <= d1; ++d) + for (c=c0; c <= c1; ++c) + for (b=b0; b <= b1; ++b) + for (a=a0; a <= a1; ++a) { + p->process_h_rect(p, xpos, ypos, a,b,c,d,e,f); + xpos += 2*p->c->short_side_len + 3; + } +} + +static void stbhw__process_v_row(stbhw__process *p, + int xpos, int ypos, + int a0, int a1, + int b0, int b1, + int c0, int c1, + int d0, int d1, + int e0, int e1, + int f0, int f1, + int variants) +{ + int a,b,c,d,e,f,v; + + for (v=0; v < variants; ++v) + for (f=f0; f <= f1; ++f) + for (e=e0; e <= e1; ++e) + for (d=d0; d <= d1; ++d) + for (c=c0; c <= c1; ++c) + for (b=b0; b <= b1; ++b) + for (a=a0; a <= a1; ++a) { + p->process_v_rect(p, xpos, ypos, a,b,c,d,e,f); + xpos += p->c->short_side_len+3; + } +} + +static void stbhw__get_template_info(stbhw_config *c, int *w, int *h, int *h_count, int *v_count) +{ + int size_x,size_y; + int horz_count,vert_count; + + if (c->is_corner) { + int horz_w = c->num_color[1] * c->num_color[2] * c->num_color[3] * c->num_vary_x; + int horz_h = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_y; + + int vert_w = c->num_color[0] * c->num_color[3] * c->num_color[2] * c->num_vary_y; + int vert_h = c->num_color[1] * c->num_color[0] * c->num_color[3] * c->num_vary_x; + + int horz_x = horz_w * (2*c->short_side_len + 3); + int horz_y = horz_h * ( c->short_side_len + 3); + + int vert_x = vert_w * ( c->short_side_len + 3); + int vert_y = vert_h * (2*c->short_side_len + 3); + + horz_count = horz_w * horz_h; + vert_count = vert_w * vert_h; + + size_x = horz_x > vert_x ? horz_x : vert_x; + size_y = 2 + horz_y + 2 + vert_y; + } else { + int horz_w = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_x; + int horz_h = c->num_color[3] * c->num_color[4] * c->num_color[2] * c->num_vary_y; + + int vert_w = c->num_color[0] * c->num_color[5] * c->num_color[1] * c->num_vary_y; + int vert_h = c->num_color[3] * c->num_color[4] * c->num_color[5] * c->num_vary_x; + + int horz_x = horz_w * (2*c->short_side_len + 3); + int horz_y = horz_h * ( c->short_side_len + 3); + + int vert_x = vert_w * ( c->short_side_len + 3); + int vert_y = vert_h * (2*c->short_side_len + 3); + + horz_count = horz_w * horz_h; + vert_count = vert_w * vert_h; + + size_x = horz_x > vert_x ? horz_x : vert_x; + size_y = 2 + horz_y + 2 + vert_y; + } + if (w) *w = size_x; + if (h) *h = size_y; + if (h_count) *h_count = horz_count; + if (v_count) *v_count = vert_count; +} + +STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h) +{ + stbhw__get_template_info(c, w, h, NULL, NULL); +} + +static int stbhw__process_template(stbhw__process *p) +{ + int i,j,k,q, ypos; + int size_x, size_y; + stbhw_config *c = p->c; + + stbhw__get_template_info(c, &size_x, &size_y, NULL, NULL); + + if (p->w < size_x || p->h < size_y) { + stbhw_error = "image too small for configuration"; + return 0; + } + + if (c->is_corner) { + ypos = 2; + for (k=0; k < c->num_color[2]; ++k) { + for (j=0; j < c->num_color[1]; ++j) { + for (i=0; i < c->num_color[0]; ++i) { + for (q=0; q < c->num_vary_y; ++q) { + stbhw__process_h_row(p, 0,ypos, + 0,c->num_color[1]-1, 0,c->num_color[2]-1, 0,c->num_color[3]-1, + i,i, j,j, k,k, + c->num_vary_x); + ypos += c->short_side_len + 3; + } + } + } + } + ypos += 2; + for (k=0; k < c->num_color[3]; ++k) { + for (j=0; j < c->num_color[0]; ++j) { + for (i=0; i < c->num_color[1]; ++i) { + for (q=0; q < c->num_vary_x; ++q) { + stbhw__process_v_row(p, 0,ypos, + 0,c->num_color[0]-1, 0,c->num_color[3]-1, 0,c->num_color[2]-1, + i,i, j,j, k,k, + c->num_vary_y); + ypos += (c->short_side_len*2) + 3; + } + } + } + } + assert(ypos == size_y); + } else { + ypos = 2; + for (k=0; k < c->num_color[3]; ++k) { + for (j=0; j < c->num_color[4]; ++j) { + for (i=0; i < c->num_color[2]; ++i) { + for (q=0; q < c->num_vary_y; ++q) { + stbhw__process_h_row(p, 0,ypos, + 0,c->num_color[2]-1, k,k, + 0,c->num_color[1]-1, j,j, + 0,c->num_color[0]-1, i,i, + c->num_vary_x); + ypos += c->short_side_len + 3; + } + } + } + } + ypos += 2; + for (k=0; k < c->num_color[3]; ++k) { + for (j=0; j < c->num_color[4]; ++j) { + for (i=0; i < c->num_color[5]; ++i) { + for (q=0; q < c->num_vary_x; ++q) { + stbhw__process_v_row(p, 0,ypos, + 0,c->num_color[0]-1, i,i, + 0,c->num_color[1]-1, j,j, + 0,c->num_color[5]-1, k,k, + c->num_vary_y); + ypos += (c->short_side_len*2) + 3; + } + } + } + } + assert(ypos == size_y); + } + return 1; +} + + +///////////////////////////////////////////////////////////// +// +// MAP GENERATOR +// + +static void stbhw__draw_pixel(unsigned char *output, int stride, int x, int y, unsigned char c[3]) +{ + memcpy(output + y*stride + x*3, c, 3); +} + +static void stbhw__draw_h_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz) +{ + int i,j; + for (j=0; j < sz; ++j) + if (y+j >= 0 && y+j < ymax) + for (i=0; i < sz*2; ++i) + if (x+i >= 0 && x+i < xmax) + stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz*2 + i)*3]); +} + +static void stbhw__draw_v_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz) +{ + int i,j; + for (j=0; j < sz*2; ++j) + if (y+j >= 0 && y+j < ymax) + for (i=0; i < sz; ++i) + if (x+i >= 0 && x+i < xmax) + stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz + i)*3]); +} + + +// randomly choose a tile that fits constraints for a given spot, and update the constraints +static stbhw_tile * stbhw__choose_tile(stbhw_tile **list, int numlist, + signed char *a, signed char *b, signed char *c, + signed char *d, signed char *e, signed char *f, + int **weighting) +{ + int i,n,m = 1<<30,pass; + for (pass=0; pass < 2; ++pass) { + n=0; + // pass #1: + // count number of variants that match this partial set of constraints + // pass #2: + // stop on randomly selected match + for (i=0; i < numlist; ++i) { + stbhw_tile *h = list[i]; + if ((*a < 0 || *a == h->a) && + (*b < 0 || *b == h->b) && + (*c < 0 || *c == h->c) && + (*d < 0 || *d == h->d) && + (*e < 0 || *e == h->e) && + (*f < 0 || *f == h->f)) { + if (weighting) + n += weighting[0][i]; + else + n += 1; + if (n > m) { + // use list[i] + // update constraints to reflect what we placed + *a = h->a; + *b = h->b; + *c = h->c; + *d = h->d; + *e = h->e; + *f = h->f; + return h; + } + } + } + if (n == 0) { + stbhw_error = "couldn't find tile matching constraints"; + return NULL; + } + m = STB_HBWANG_RAND() % n; + } + STB_HBWANG_ASSERT(0); + return NULL; +} + +static int stbhw__match(int x, int y) +{ + return c_color[y][x] == c_color[y+1][x+1]; +} + +static int stbhw__weighted(int num_options, int *weights) +{ + int k, total, choice; + total = 0; + for (k=0; k < num_options; ++k) + total += weights[k]; + choice = STB_HBWANG_RAND() % total; + total = 0; + for (k=0; k < num_options; ++k) { + total += weights[k]; + if (choice < total) + break; + } + STB_HBWANG_ASSERT(k < num_options); + return k; +} + +static int stbhw__change_color(int old_color, int num_options, int *weights) +{ + if (weights) { + int k, total, choice; + total = 0; + for (k=0; k < num_options; ++k) + if (k != old_color) + total += weights[k]; + choice = STB_HBWANG_RAND() % total; + total = 0; + for (k=0; k < num_options; ++k) { + if (k != old_color) { + total += weights[k]; + if (choice < total) + break; + } + } + STB_HBWANG_ASSERT(k < num_options); + return k; + } else { + int offset = 1+STB_HBWANG_RAND() % (num_options-1); + return (old_color+offset) % num_options; + } +} + + + +// generate a map that is w * h pixels (3-bytes each) +// returns 1 on success, 0 on error +STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsigned char *output, int stride, int w, int h) +{ + int sidelen = ts->short_side_len; + int xmax = (w / sidelen) + 6; + int ymax = (h / sidelen) + 6; + if (xmax > STB_HBWANG_MAX_X+6 || ymax > STB_HBWANG_MAX_Y+6) { + stbhw_error = "increase STB_HBWANG_MAX_X/Y"; + return 0; + } + + if (ts->is_corner) { + int i,j, ypos; + int *cc = ts->num_color; + + for (j=0; j < ymax; ++j) { + for (i=0; i < xmax; ++i) { + int p = (i-j+1)&3; // corner type + if (weighting==NULL || weighting[p]==0 || cc[p] == 1) + c_color[j][i] = STB_HBWANG_RAND() % cc[p]; + else + c_color[j][i] = stbhw__weighted(cc[p], weighting[p]); + } + } + #ifndef STB_HBWANG_NO_REPITITION_REDUCTION + // now go back through and make sure we don't have adjancent 3x2 vertices that are identical, + // to avoid really obvious repetition (which happens easily with extreme weights) + for (j=0; j < ymax-3; ++j) { + for (i=0; i < xmax-3; ++i) { + int p = (i-j+1) & 3; // corner type + STB_HBWANG_ASSERT(i+3 < STB_HBWANG_MAX_X+6); + STB_HBWANG_ASSERT(j+3 < STB_HBWANG_MAX_Y+6); + if (stbhw__match(i,j) && stbhw__match(i,j+1) && stbhw__match(i,j+2) + && stbhw__match(i+1,j) && stbhw__match(i+1,j+1) && stbhw__match(i+1,j+2)) { + int p = ((i+1)-(j+1)+1) & 3; + if (cc[p] > 1) + c_color[j+1][i+1] = stbhw__change_color(c_color[j+1][i+1], cc[p], weighting ? weighting[p] : NULL); + } + if (stbhw__match(i,j) && stbhw__match(i+1,j) && stbhw__match(i+2,j) + && stbhw__match(i,j+1) && stbhw__match(i+1,j+1) && stbhw__match(i+2,j+1)) { + int p = ((i+2)-(j+1)+1) & 3; + if (cc[p] > 1) + c_color[j+1][i+2] = stbhw__change_color(c_color[j+1][i+2], cc[p], weighting ? weighting[p] : NULL); + } + } + } + #endif + + ypos = -1 * sidelen; + for (j = -1; ypos < h; ++j) { + // a general herringbone row consists of: + // horizontal left block, the bottom of a previous vertical, the top of a new vertical + int phase = (j & 3); + // displace horizontally according to pattern + if (phase == 0) { + i = 0; + } else { + i = phase-4; + } + for (i;; i += 4) { + int xpos = i * sidelen; + if (xpos >= w) + break; + // horizontal left-block + if (xpos + sidelen*2 >= 0 && ypos >= 0) { + stbhw_tile *t = stbhw__choose_tile( + ts->h_tiles, ts->num_h_tiles, + &c_color[j+2][i+2], &c_color[j+2][i+3], &c_color[j+2][i+4], + &c_color[j+3][i+2], &c_color[j+3][i+3], &c_color[j+3][i+4], + weighting + ); + if (t == NULL) + return 0; + stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + xpos += sidelen * 2; + // now we're at the end of a previous vertical one + xpos += sidelen; + // now we're at the start of a new vertical one + if (xpos < w) { + stbhw_tile *t = stbhw__choose_tile( + ts->v_tiles, ts->num_v_tiles, + &c_color[j+2][i+5], &c_color[j+3][i+5], &c_color[j+4][i+5], + &c_color[j+2][i+6], &c_color[j+3][i+6], &c_color[j+4][i+6], + weighting + ); + if (t == NULL) + return 0; + stbhw__draw_v_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + } + ypos += sidelen; + } + } else { + // @TODO edge-color repetition reduction + int i,j, ypos; + memset(v_color, -1, sizeof(v_color)); + memset(h_color, -1, sizeof(h_color)); + + ypos = -1 * sidelen; + for (j = -1; ypos<h; ++j) { + // a general herringbone row consists of: + // horizontal left block, the bottom of a previous vertical, the top of a new vertical + int phase = (j & 3); + // displace horizontally according to pattern + if (phase == 0) { + i = 0; + } else { + i = phase-4; + } + for (i;; i += 4) { + int xpos = i * sidelen; + if (xpos >= w) + break; + // horizontal left-block + if (xpos + sidelen*2 >= 0 && ypos >= 0) { + stbhw_tile *t = stbhw__choose_tile( + ts->h_tiles, ts->num_h_tiles, + &h_color[j+2][i+2], &h_color[j+2][i+3], + &v_color[j+2][i+2], &v_color[j+2][i+4], + &h_color[j+3][i+2], &h_color[j+3][i+3], + weighting + ); + if (t == NULL) return 0; + stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + xpos += sidelen * 2; + // now we're at the end of a previous vertical one + xpos += sidelen; + // now we're at the start of a new vertical one + if (xpos < w) { + stbhw_tile *t = stbhw__choose_tile( + ts->v_tiles, ts->num_v_tiles, + &h_color[j+2][i+5], + &v_color[j+2][i+5], &v_color[j+2][i+6], + &v_color[j+3][i+5], &v_color[j+3][i+6], + &h_color[j+4][i+5], + weighting + ); + if (t == NULL) return 0; + stbhw__draw_v_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + } + ypos += sidelen; + } + } + return 1; +} + +static void stbhw__parse_h_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len); + int i,j; + ++xpos; + ++ypos; + h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f; + for (j=0; j < len; ++j) + for (i=0; i < len*2; ++i) + memcpy(h->pixels + j*(3*len*2) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3); + STB_HBWANG_ASSERT(p->ts->num_h_tiles < p->ts->max_h_tiles); + p->ts->h_tiles[p->ts->num_h_tiles++] = h; +} + +static void stbhw__parse_v_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len); + int i,j; + ++xpos; + ++ypos; + h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f; + for (j=0; j < len*2; ++j) + for (i=0; i < len; ++i) + memcpy(h->pixels + j*(3*len) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3); + STB_HBWANG_ASSERT(p->ts->num_v_tiles < p->ts->max_v_tiles); + p->ts->v_tiles[p->ts->num_v_tiles++] = h; +} + +STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, unsigned char *data, int stride, int w, int h) +{ + int i, h_count, v_count; + unsigned char header[9]; + stbhw_config c = { 0 }; + stbhw__process p = { 0 }; + + // extract binary header + + // remove encoding that makes it more visually obvious it encodes actual data + for (i=0; i < 9; ++i) + header[i] = data[w*3 - 1 - i] ^ (i*55); + + // extract header info + if (header[7] == 0xc0) { + // corner-type + c.is_corner = 1; + for (i=0; i < 4; ++i) + c.num_color[i] = header[i]; + c.num_vary_x = header[4]; + c.num_vary_y = header[5]; + c.short_side_len = header[6]; + } else { + c.is_corner = 0; + // edge-type + for (i=0; i < 6; ++i) + c.num_color[i] = header[i]; + c.num_vary_x = header[6]; + c.num_vary_y = header[7]; + c.short_side_len = header[8]; + } + + if (c.num_vary_x < 0 || c.num_vary_x > 64 || c.num_vary_y < 0 || c.num_vary_y > 64) + return 0; + if (c.short_side_len == 0) + return 0; + if (c.num_color[0] > 32 || c.num_color[1] > 32 || c.num_color[2] > 32 || c.num_color[3] > 32) + return 0; + + stbhw__get_template_info(&c, NULL, NULL, &h_count, &v_count); + + ts->is_corner = c.is_corner; + ts->short_side_len = c.short_side_len; + memcpy(ts->num_color, c.num_color, sizeof(ts->num_color)); + + ts->max_h_tiles = h_count; + ts->max_v_tiles = v_count; + + ts->num_h_tiles = ts->num_v_tiles = 0; + + ts->h_tiles = (stbhw_tile **) malloc(sizeof(*ts->h_tiles) * h_count); + ts->v_tiles = (stbhw_tile **) malloc(sizeof(*ts->v_tiles) * v_count); + + p.ts = ts; + p.data = data; + p.stride = stride; + p.process_h_rect = stbhw__parse_h_rect; + p.process_v_rect = stbhw__parse_v_rect; + p.w = w; + p.h = h; + p.c = &c; + + // load all the tiles out of the image + return stbhw__process_template(&p); +} + +STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts) +{ + int i; + for (i=0; i < ts->num_h_tiles; ++i) + free(ts->h_tiles[i]); + for (i=0; i < ts->num_v_tiles; ++i) + free(ts->v_tiles[i]); + free(ts->h_tiles); + free(ts->v_tiles); + ts->h_tiles = NULL; + ts->v_tiles = NULL; + ts->num_h_tiles = ts->max_h_tiles = 0; + ts->num_v_tiles = ts->max_v_tiles = 0; +} + +////////////////////////////////////////////////////////////////////////////// +// +// GENERATOR +// +// + + +// shared code + +static void stbhw__set_pixel(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3]) +{ + memcpy(data + ypos*stride + xpos*3, color, 3); +} + +static void stbhw__stbhw__set_pixel_whiten(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3]) +{ + unsigned char c2[3]; + int i; + for (i=0; i < 3; ++i) + c2[i] = (color[i]*2 + 255)/3; + memcpy(data + ypos*stride + xpos*3, c2, 3); +} + + +static unsigned char stbhw__black[3] = { 0,0,0 }; + +// each edge set gets its own unique color variants +// used http://phrogz.net/css/distinct-colors.html to generate this set, +// but it's not very good and needs to be revised + +static unsigned char stbhw__color[7][8][3] = +{ + { {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199}, {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, }, + { {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, }, + { {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159},}, + { {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102},}, + { {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159}, {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199},}, + { {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102}, {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, }, + + { {40,40,40 }, { 90,90,90 }, { 150,150,150 }, { 200,200,200 }, + { 255,90,90 }, { 160,160,80}, { 50,150,150 }, { 200,50,200 } }, +}; + +static void stbhw__draw_hline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot) +{ + int i; + int j = len * 6 / 16; + int k = len * 10 / 16; + for (i=0; i < len; ++i) + stbhw__set_pixel(data, stride, xpos+i, ypos, stbhw__black); + if (k-j < 2) { + j = len/2 - 1; + k = j+2; + if (len & 1) + ++k; + } + for (i=j; i < k; ++i) + stbhw__stbhw__set_pixel_whiten(data, stride, xpos+i, ypos, stbhw__color[slot][color]); +} + +static void stbhw__draw_vline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot) +{ + int i; + int j = len * 6 / 16; + int k = len * 10 / 16; + for (i=0; i < len; ++i) + stbhw__set_pixel(data, stride, xpos, ypos+i, stbhw__black); + if (k-j < 2) { + j = len/2 - 1; + k = j+2; + if (len & 1) + ++k; + } + for (i=j; i < k; ++i) + stbhw__stbhw__set_pixel_whiten(data, stride, xpos, ypos+i, stbhw__color[slot][color]); +} + +// 0--*--1--*--2--*--3 +// | | | +// * * * +// | | | +// 1--*--2--*--3 0--*--1--*--2 +// | | | +// * * * +// | | | +// 0--*--1--*--2--*--3 +// +// variables while enumerating (no correspondence between corners +// of the types is implied by these variables) +// +// a-----b-----c a-----d +// | | | | +// | | | | +// | | | | +// d-----e-----f b e +// | | +// | | +// | | +// c-----f +// + +unsigned char stbhw__corner_colors[4][4][3] = +{ + { { 255,0,0 }, { 200,200,200 }, { 100,100,200 }, { 255,200,150 }, }, + { { 0,0,255 }, { 255,255,0 }, { 100,200,100 }, { 150,255,200 }, }, + { { 255,0,255 }, { 80,80,80 }, { 200,100,100 }, { 200,150,255 }, }, + { { 0,255,255 }, { 0,255,0 }, { 200,120,200 }, { 255,200,200 }, }, +}; + +int stbhw__corner_colors_to_edge_color[4][4] = +{ + // 0 1 2 3 + { 0, 1, 4, 9, }, // 0 + { 2, 3, 5, 10, }, // 1 + { 6, 7, 8, 11, }, // 2 + { 12, 13, 14, 15, }, // 3 +}; + +#define stbhw__c2e stbhw__corner_colors_to_edge_color + +static void stbhw__draw_clipped_corner(unsigned char *data, int stride, int xpos, int ypos, int w, int h, int x, int y) +{ + static unsigned char template_color[3] = { 167,204,204 }; + int i,j; + for (j = -2; j <= 1; ++j) { + for (i = -2; i <= 1; ++i) { + if ((i == -2 || i == 1) && (j == -2 || j == 1)) + continue; + else { + if (x+i < 1 || x+i > w) continue; + if (y+j < 1 || y+j > h) continue; + stbhw__set_pixel(data, stride, xpos+x+i, ypos+y+j, template_color); + + } + } + } +} + +static void stbhw__edge_process_h_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , a, len, 2); + stbhw__draw_hline(p->data, p->stride, xpos+ len+1 , ypos , b, len, 3); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , c, len, 1); + stbhw__draw_vline(p->data, p->stride, xpos+2*len+1 , ypos+1 , d, len, 4); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + len+1, e, len, 0); + stbhw__draw_hline(p->data, p->stride, xpos + len+1 , ypos + len+1, f, len, 2); +} + +static void stbhw__edge_process_v_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , a, len, 0); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , b, len, 5); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1 , c, len, 1); + stbhw__draw_vline(p->data, p->stride, xpos , ypos + len+1, d, len, 4); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos + len+1, e, len, 5); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + 2*len+1, f, len, 3); +} + +static void stbhw__corner_process_h_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , stbhw__c2e[a][b], len, 2); + stbhw__draw_hline(p->data, p->stride, xpos+ len+1 , ypos , stbhw__c2e[b][c], len, 3); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , stbhw__c2e[a][d], len, 1); + stbhw__draw_vline(p->data, p->stride, xpos+2*len+1 , ypos+1 , stbhw__c2e[c][f], len, 4); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + len+1, stbhw__c2e[d][e], len, 0); + stbhw__draw_hline(p->data, p->stride, xpos + len+1 , ypos + len+1, stbhw__c2e[e][f], len, 2); + + if (p->c->corner_type_color_template[1][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, 1,1); + if (p->c->corner_type_color_template[2][b]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len+1,1); + if (p->c->corner_type_color_template[3][c]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len*2+1,1); + + if (p->c->corner_type_color_template[0][d]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, 1,len+1); + if (p->c->corner_type_color_template[1][e]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len+1,len+1); + if (p->c->corner_type_color_template[2][f]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len*2+1,len+1); + + stbhw__set_pixel(p->data, p->stride, xpos , ypos, stbhw__corner_colors[1][a]); + stbhw__set_pixel(p->data, p->stride, xpos+len , ypos, stbhw__corner_colors[2][b]); + stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos, stbhw__corner_colors[3][c]); + stbhw__set_pixel(p->data, p->stride, xpos , ypos+len+1, stbhw__corner_colors[0][d]); + stbhw__set_pixel(p->data, p->stride, xpos+len , ypos+len+1, stbhw__corner_colors[1][e]); + stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos+len+1, stbhw__corner_colors[2][f]); +} + +static void stbhw__corner_process_v_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , stbhw__c2e[a][d], len, 0); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , stbhw__c2e[a][b], len, 5); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1 , stbhw__c2e[d][e], len, 1); + stbhw__draw_vline(p->data, p->stride, xpos , ypos + len+1, stbhw__c2e[b][c], len, 4); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos + len+1, stbhw__c2e[e][f], len, 5); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + 2*len+1, stbhw__c2e[c][f], len, 3); + + if (p->c->corner_type_color_template[0][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,1); + if (p->c->corner_type_color_template[3][b]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,len+1); + if (p->c->corner_type_color_template[2][c]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,len*2+1); + + if (p->c->corner_type_color_template[1][d]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,1); + if (p->c->corner_type_color_template[0][e]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,len+1); + if (p->c->corner_type_color_template[3][f]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,len*2+1); + + stbhw__set_pixel(p->data, p->stride, xpos , ypos , stbhw__corner_colors[0][a]); + stbhw__set_pixel(p->data, p->stride, xpos , ypos+len , stbhw__corner_colors[3][b]); + stbhw__set_pixel(p->data, p->stride, xpos , ypos+2*len+1, stbhw__corner_colors[2][c]); + stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos , stbhw__corner_colors[1][d]); + stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+len , stbhw__corner_colors[0][e]); + stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+2*len+1, stbhw__corner_colors[3][f]); +} + +// generates a template image, assuming data is 3*w*h bytes long, RGB format +STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes) +{ + stbhw__process p; + int i; + + p.data = data; + p.w = w; + p.h = h; + p.stride = stride_in_bytes; + p.ts = 0; + p.c = c; + + if (c->is_corner) { + p.process_h_rect = stbhw__corner_process_h_rect; + p.process_v_rect = stbhw__corner_process_v_rect; + } else { + p.process_h_rect = stbhw__edge_process_h_rect; + p.process_v_rect = stbhw__edge_process_v_rect; + } + + for (i=0; i < p.h; ++i) + memset(p.data + i*p.stride, 255, 3*p.w); + + if (!stbhw__process_template(&p)) + return 0; + + if (c->is_corner) { + // write out binary information in first line of image + for (i=0; i < 4; ++i) + data[w*3-1-i] = c->num_color[i]; + data[w*3-1-i] = c->num_vary_x; + data[w*3-2-i] = c->num_vary_y; + data[w*3-3-i] = c->short_side_len; + data[w*3-4-i] = 0xc0; + } else { + for (i=0; i < 6; ++i) + data[w*3-1-i] = c->num_color[i]; + data[w*3-1-i] = c->num_vary_x; + data[w*3-2-i] = c->num_vary_y; + data[w*3-3-i] = c->short_side_len; + } + + // make it more obvious it encodes actual data + for (i=0; i < 9; ++i) + p.data[p.w*3 - 1 - i] ^= i*55; + + return 1; +} +#endif // STB_HBWANG_IMPLEMENTATION diff --git a/test_data/lots_of_files/stb_image.h b/test_data/lots_of_files/stb_image.h new file mode 100644 index 0000000..0a9de39 --- /dev/null +++ b/test_data/lots_of_files/stb_image.h @@ -0,0 +1,6509 @@ +/* stb_image - v2.08 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) + optimize PNG + fix bug in interlaced PNG with user-specified channel count + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Bug fixes & warning fixes + Sean Barrett (jpeg, png, bmp) Marc LeBlanc + Nicolas Schulz (hdr, psd) Christpher Lloyd + Jonathan Dummer (tga) Dave Moore + Jean-Marc Lienher (gif) Won Chun + Tom Seddon (pic) the Horde3D community + Thatcher Ulrich (psd) Janez Zemva + Ken Miller (pgm, ppm) Jonathan Blow + urraka@github (animated gif) Laurent Gomila + Aruelien Pocheville + Ryamond Barbiero + David Woo + Extensions, features Martin Golini + Jetro Lauha (stbi_info) Roy Eltham + Martin "SpartanJ" Golini (stbi_info) Luke Graham + James "moose2000" Brown (iPhone PNG) Thomas Ruf + Ben "Disch" Wenger (io callbacks) John Bartholomew + Omar Cornut (1/2/4-bit PNG) Ken Hamada + Nicolas Guillemot (vertical flip) Cort Stratton + Richard Mitton (16-bit PSD) Blazej Dariusz Roszkowski + Thibault Reuille + Paul Du Bois + Guillaume George + Jerry Jansson + Hayaki Saito + Johan Duparc + Ronny Chevalier + Optimizations & bugfixes Michal Cichon + Fabian "ryg" Giesen Tero Hanninen + Arseny Kapoulkine Sergio Gonzalez + Cass Everitt + Engin Manap + If your name should be here but Martins Mozeiko + isn't, let Sean know. Joseph Thomson + Phil Jordan + Nathan Reed + Michaelangel007@github + Nick Verigakis + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// + + +#ifndef STBI_NO_STDIO +#include <stdio.h> +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include <stdarg.h> +#include <stddef.h> // ptrdiff_t on osx +#include <stdlib.h> +#include <string.h> + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include <math.h> // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include <stdio.h> +#endif + +#ifndef STBI_ASSERT +#include <assert.h> +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include <stdint.h> +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,sz) realloc(p,sz) +#define STBI_FREE(p) free(p) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) +#define STBI_SSE2 +#include <emmintrin.h> + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include <intrin.h> // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include <arm_neon.h> +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_flip(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_flip(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +#ifndef STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<<n) + 1 +static int const stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767}; + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) +{ + unsigned int k; + int sgn; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return stbi__err("bad component ID","Corrupt JPEG"); + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__rewind(s); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__jpeg j; + j.s = s; + return stbi__jpeg_info_raw(&j, x, y, comp); +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC(z->zout_start, limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncomperssed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + int filter_bytes = img_n; + int width = x; + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*img_n; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; + if (has_trans) + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_out_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a=255; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + offset = stbi__get32le(s); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + bpp = stbi__get16le(s); + if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + STBI_ASSERT(hsz == 108 || hsz == 124); + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if( sz > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + sz = stbi__get8(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + stbi__skip(s,9); + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + sz = stbi__get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi__rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + stbi__get16be(s); // discard palette start + stbi__get16be(s); // discard palette length + stbi__get8(s); // discard bits per palette color entry + stbi__get16be(s); // discard x origin + stbi__get16be(s); // discard y origin + if ( stbi__get16be(s) < 1 ) return 0; // test width + if ( stbi__get16be(s) < 1 ) return 0; // test height + sz = stbi__get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) + res = 0; + else + res = 1; + stbi__rewind(s); + return res; +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp = tga_bits_per_pixel / 8; + int tga_inverted = stbi__get8(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_comp = tga_palette_bits / 8; + } + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = stbi__get8(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB + if (tga_comp >= 3) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int bitdepth; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } else { + // Read the data. + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; y<height; ++y) { + int packet_idx; + + for(packet_idx=0; packet_idx < num_packets; ++packet_idx) { + stbi__pic_packet *packet = &packets[packet_idx]; + stbi_uc *dest = result+y*width*4; + + switch (packet->type) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;x<width;++x, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; i<count; ++i,dest+=4) + stbi__copyval(packet->channel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;i<count;++i, dest += 4) + stbi__copyval(packet->channel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;i<count;++i, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif g; + if (!stbi__gif_header(s, &g, comp, 1)) { + stbi__rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + + u = stbi__gif_load_next(s, &g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } + else if (g.out) + STBI_FREE(g.out); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { + stbi__rewind( s ); + return 0; + } + stbi__skip(s,12); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { + stbi__rewind( s ); + return 0; + } + if (hsz == 12) { + *x = stbi__get16le(s); + *y = stbi__get16le(s); + } else { + *x = stbi__get32le(s); + *y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) { + stbi__rewind( s ); + return 0; + } + *comp = stbi__get16le(s) / 8; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bit PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ diff --git a/test_data/lots_of_files/stb_image_resize.h b/test_data/lots_of_files/stb_image_resize.h new file mode 100644 index 0000000..4ce7ddb --- /dev/null +++ b/test_data/lots_of_files/stb_image_resize.h @@ -0,0 +1,2586 @@ +/* stb_image_resize - v0.90 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb + + Written with emphasis on usability, portability, and efficiency. (No + SIMD or threads, so it be easily outperformed by libs that use those.) + Only scaling and translation is supported, no rotations or shears. + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + QUICKSTART + stbir_resize_uint8( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, num_channels) + stbir_resize_float(...) + stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0) + stbir_resize_uint8_srgb_edgemode( + input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + // WRAP/REFLECT/ZERO + + FULL API + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + SRGB & FLOATING POINT REPRESENTATION + The sRGB functions presume IEEE floating point. If you do not have + IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use + a slower implementation. + + MEMORY ALLOCATION + The resize functions here perform a single memory allocation using + malloc. To control the memory allocation, before the #include that + triggers the implementation, do: + + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... + + Each resize function makes exactly one call to malloc/free, so to use + temp memory, store the temp memory in the context and return that. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + OPTIMIZATION + Define STBIR_SATURATE_INT to compute clamp values in-range using + integer operations instead of float operations. This may be faster + on some platforms. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters + to use, you can change the compile-time defaults with + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are used. For a list of + supported filters see the stbir_filter enum. To add a new filter, + write a filter function and add it to stbir__filter_info_table. + + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: + + #define STBIR_PROGRESS_REPORT(val) some_func(val) + + The parameter val is a float which goes from 0 to 1 as progress is made. + + For example: + + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" + + static void my_progress_report(float progress) + { + printf("Progress: %f%%\n", progress*100); + } + + MAX CHANNELS + If your image has more than 64 channels, define STBIR_MAX_CHANNELS + to the max you'll have. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how + the alpha channel of an image is processed. The important things + to know about this: + + 1. The best mathematically-behaved version of alpha to use is + called "premultiplied alpha", in which the other color channels + have had the alpha value multiplied in. If you use premultiplied + alpha, linear filtering (such as image resampling done by this + library, or performed in texture units on GPUs) does the "right + thing". While premultiplied alpha is standard in the movie CGI + industry, it is still uncommon in the videogame/real-time world. + + If you linearly filter non-premultiplied alpha, strange effects + occur. (For example, the average of 1% opaque bright green + and 99% opaque black produces 50% transparent dark green when + non-premultiplied, whereas premultiplied it produces 50% + transparent near-black. The former introduces green energy + that doesn't exist in the source image.) + + 2. Artists should not edit premultiplied-alpha images; artists + want non-premultiplied alpha images. Thus, art tools generally output + non-premultiplied alpha images. + + 3. You will get best results in most cases by converting images + to premultiplied alpha before processing them mathematically. + + 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the + resizer does not do anything special for the alpha channel; + it is resampled identically to other channels. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. + + 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, + then the resizer weights the contribution of input pixels + based on their alpha values, or, equivalently, it multiplies + the alpha value into the color channels, resamples, then divides + by the resultant alpha value. Input pixels which have alpha=0 do + not contribute at all to output pixels unless _all_ of the input + pixels affecting that output pixel have alpha=0, in which case + the result for that pixel is the same as it would be without + STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for + input images in integer formats. For input images in float format, + input pixels with alpha=0 have no effect, and output pixels + which have alpha=0 will be 0 in all channels. (For float images, + you can manually achieve the same result by adding a tiny epsilon + value to the alpha channel of every image, and then subtracting + or clamping it at the end.) + + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. + + 7. You can separately control whether the alpha channel is + interpreted as linear or affected by the colorspace. By default + it is linear; you almost never want to apply the colorspace. + (For example, graphics hardware does not apply sRGB conversion + to the alpha channel.) + + ADDITIONAL CONTRIBUTORS + Sean Barrett: API design, optimizations + + REVISIONS + 0.90 (2014-09-17) first released version + + LICENSE + + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy, + distribute, and modify this file as you see fit. + + TODO + Don't decode all of the image data when only processing a partial tile + Don't use full-width decode buffers when only processing a partial tile + When processing wide images, break processing into tiles so data fits in L1 cache + Installable filters? + Resize that respects alpha test coverage + (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: + https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) +*/ + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +#else +#include <stdint.h> +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +#endif + +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) +// * input_w is input image width (x-axis), input_h is input image height (y-axis) +// * stride is the offset between successive rows of image data in memory, in bytes. you can +// specify 0 to mean packed continuously in memory +// * alpha channel is treated identically to other channels. +// * colorspace is linear or sRGB as specified by function name +// * returned result is 1 for success or 0 in case of an error. +// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. +// * Memory required grows approximately linearly with input and output size, but with +// discontinuities at input_w == output_w and input_h == output_h. +// * These functions use a "default" resampling filter defined at compile time. To change the filter, +// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE +// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + + +// The following functions interpret image data as gamma-corrected sRGB. +// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, +// or otherwise provide the index of the alpha channel. Flags value +// of 0 will probably do the right thing if you're not sure what +// the flags mean. + +#define STBIR_ALPHA_CHANNEL_NONE -1 + +// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will +// use alpha-weighted resampling (effectively premultiplying, resampling, +// then unpremultiplying). +#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) +// The specified alpha channel should be handled as gamma-corrected value even +// when doing sRGB operations. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags); + + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode); + +////////////////////////////////////////////////////////////////////////////// +// +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Alpha-channel can be processed separately +// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE +// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) +// * Filter can be selected explicitly +// * uint16 image type +// * sRGB colorspace available for all types +// * context parameter for passing to STBIR_MALLOC + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 +} stbir_filter; + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +// The following functions are all identical except for the type of the image data + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + + + +////////////////////////////////////////////////////////////////////////////// +// +// Full-complexity API +// +// This extends the medium API as follows: +// +// * uint32 image type +// * not typesafe +// * separate filter types for each axis +// * separate edge modes for each axis +// * can specify scale explicitly for subpixel correctness +// * can specify image source tile using texture coordinates + +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , + + STBIR_MAX_TYPES +} stbir_datatype; + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context); + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset); + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1); +// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + + + + + +#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION + +#ifndef STBIR_ASSERT +#include <assert.h> +#define STBIR_ASSERT(x) assert(x) +#endif + +#ifdef STBIR_DEBUG +#define STBIR__DEBUG_ASSERT STBIR_ASSERT +#else +#define STBIR__DEBUG_ASSERT +#endif + +// If you hit this it means I haven't done it yet. +#define STBIR__UNIMPLEMENTED(x) STBIR_ASSERT(!(x)) + +// For memset +#include <string.h> + +#include <math.h> + +#ifndef STBIR_MALLOC +#include <stdlib.h> +#define STBIR_MALLOC(size,c) malloc(size) +#define STBIR_FREE(ptr,c) free(ptr) +#endif + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbir__inline inline +#else +#define stbir__inline +#endif +#else +#define stbir__inline __forceinline +#endif + + +// should produce compiler error if size is wrong +typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBIR__NOTUSED(v) (void)(v) +#else +#define STBIR__NOTUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + +#ifndef STBIR_PROGRESS_REPORT +#define STBIR_PROGRESS_REPORT(float_0_to_1) +#endif + +#ifndef STBIR_MAX_CHANNELS +#define STBIR_MAX_CHANNELS 64 +#endif + +#if STBIR_MAX_CHANNELS > 65536 +#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +// because we store the indices in 16-bit variables +#endif + +// This value is added to alpha just before premultiplication to avoid +// zeroing out color values. It is equivalent to 2^-80. If you don't want +// that behavior (it may interfere if you have floating point images with +// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to +// disable it. +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + + + +#ifdef _MSC_VER +#define STBIR__UNUSED_PARAM(v) (void)(v) +#else +#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) +#endif + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT +}; + +// Kernel function centered at 0 +typedef float (stbir__kernel_fn)(float x, float scale); +typedef float (stbir__support_fn)(float scale); + +typedef struct +{ + stbir__kernel_fn* kernel; + stbir__support_fn* support; +} stbir__filter_info; + +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; + + stbir__contributors* vertical_contributors; + float* vertical_coefficients; + + int decode_buffer_pixels; + float* decode_buffer; + + float* horizontal_buffer; + + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; + float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; +} stbir__info; + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static stbir__inline float stbir__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + +#ifdef STBIR_SATURATE_INT +static stbir__inline stbir_uint8 stbir__saturate8(int x) +{ + if ((unsigned int) x <= 255) + return x; + + if (x < 0) + return 0; + + return 255; +} + +static stbir__inline stbir_uint16 stbir__saturate16(int x) +{ + if ((unsigned int) x <= 65535) + return x; + + if (x < 0) + return 0; + + return 65535; +} +#endif + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +static float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +static float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + +#ifndef STBIR_NON_IEEE_FLOAT +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#else +// sRGB transition values, scaled by 1<<28 +static int stbir__srgb_offset_to_linear_scaled[256] = +{ + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, + 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, + 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, + 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, + 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, + 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, + 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, + 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, + 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, + 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, + 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, + 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float f) +{ + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; + + // Refine the guess with a short binary search. + i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + + return (stbir_uint8) v; +} +#endif + +static float stbir__filter_trapezoid(float x, float scale) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR__DEBUG_ASSERT(scale <= 1); + + x = (float)fabs(x); + + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale) +{ + STBIR__DEBUG_ASSERT(scale <= 1); + return 0.5f + scale / 2; +} + +static float stbir__filter_triangle(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + +static float stbir__filter_cubic(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (4 + x*x*(3*x - 6))/6; + else if (x < 2.0f) + return (8 + x*(-12 + x*(6 - x)))/6; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (16 + x*x*(21 * x - 36))/18; + else if (x < 2.0f) + return (32 + x*(-60 + x*(36 - 7*x)))/18; + + return (0.0f); +} + +static float stbir__support_zero(float s) +{ + STBIR__UNUSED_PARAM(s); + return 0; +} + +static float stbir__support_one(float s) +{ + STBIR__UNUSED_PARAM(s); + return 1; +} + +static float stbir__support_two(float s) +{ + STBIR__UNUSED_PARAM(s); + return 2; +} + +static stbir__filter_info stbir__filter_info_table[] = { + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, +}; + +stbir__inline static int stbir__use_upsampling(float ratio) +{ + return ratio > 1; +} + +stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->vertical_scale); +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) +{ + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); +} + +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) +{ + return stbir__get_filter_pixel_width(filter, scale) / 2; +} + +static int stbir__get_coefficient_width(stbir_filter filter, float scale) +{ + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); +} + +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +{ + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); +} + +static int stbir__get_total_horizontal_coefficients(stbir__info* info) +{ + return info->horizontal_num_contributors + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); +} + +static int stbir__get_total_vertical_coefficients(stbir__info* info) +{ + return info->vertical_num_contributors + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); +} + +static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +{ + return &contributors[n]; +} + +// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, +// if you change it here change it there too. +static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +{ + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; +} + +static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) +{ + switch (edge) + { + case STBIR_EDGE_ZERO: + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED + + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED + } + + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + return n; // NOTREACHED + + default: + STBIR__UNIMPLEMENTED("Unimplemented edge type"); + return 0; + } +} + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); +} + +// What input pixels contribute to this output pixel? +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +{ + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); +} + +// What output pixels does this input pixel contribute to? +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +{ + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); +} + +static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + + STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; + + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } + + total_filter += coefficient_group[i]; + } + + STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + + STBIR__DEBUG_ASSERT(total_filter > 0.9); + STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; + + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + + STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; + + STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } + + STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; + + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } + + STBIR__DEBUG_ASSERT(total > 0.9f); + STBIR__DEBUG_ASSERT(total < 1.1f); + + scale = 1 / total; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) + break; + } + } + + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max, width; + + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; + + contributors[j].n0 += skip; + + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } + + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); + + width = stbir__get_coefficient_width(filter, scale_ratio); + for (i = 0; i < max; i++) + { + if (i + skip >= width) + break; + + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); + } + + continue; + } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); +} + +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; + + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + + stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + + stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + + stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size); + } +} + +static float* stbir__get_decode_buffer(stbir__info* stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; +} + +#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + +static void stbir__decode_scanline(stbir__info* stbir_info, int n) +{ + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + int input_stride_bytes = stbir_info->input_stride_bytes; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; + int decode = STBIR__DECODE(type, colorspace); + + int x = -stbir_info->horizontal_filter_pixel_margin; + + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + } + + break; + + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) + { + int decode_pixel_index = x * channels; + + // If the alpha value is 0 it will clobber the color values. Make sure it's not. + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; +#ifndef STBIR_NO_ALPHA_EPSILON + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } +#endif + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } + + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } +} + +static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + +static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir_info->vertical_filter_pixel_width; + STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } + + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + + stbir_info->ring_buffer_last_scanline = n; + + return ring_buffer; +} + + +static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; + + STBIR__DEBUG_ASSERT(n1 >= n0); + STBIR__DEBUG_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR__DEBUG_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int input_w = stbir_info->input_w; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; + int max_x = input_w + filter_pixel_margin * 2; + + STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info)); + + switch (channels) { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + } + break; + + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + } + break; + + case 3: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + } + break; + + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + } + break; + + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR__DEBUG_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } + break; + } +} + +static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + +// Get the specified scan line from the ring buffer. +static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); +} + + +static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) +{ + int x; + int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + float alpha = encode_buffer[pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[pixel_index + n] *= reciprocal_alpha; + + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). + } + } + + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x=0, num_nonalpha=0; x < channels; ++x) + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + nonalpha[num_nonalpha++] = x; + + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) + #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) + + #ifdef STBIR__SATURATE_INT + #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * 255 )) + #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * 65535)) + #else + #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255 ) + #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535) + #endif + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); + } + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * 4294967295); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; + } + break; + + default: + STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; + int contributor = n; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + output_row_start = n * stbir_info->output_stride_bytes; + + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + + coefficient_counter = 0; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 1; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 2; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 3; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 4; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); +} + +static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + int output_h = stbir_info->output_h; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; + void* output_data = stbir_info->output_data; + float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + + switch (channels) { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + + STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info)); + + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; + } + } + } + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); + + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); + } +} + +static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +{ + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; + } + } + } +} + +static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir_info->vertical_filter_pixel_margin; + int max_y = stbir_info->input_h + pixel_margin; + + STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; + + stbir__empty_ring_buffer(stbir_info, out_first_scanline); + + stbir__decode_and_resample_downsample(stbir_info, y); + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); + } + + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); +} + +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) +{ + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; +} + +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +{ + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; + + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + + info->horizontal_shift = s0 * info->input_w / (s1 - s0); + info->vertical_shift = t0 * info->input_h / (t1 - t0); + } +} + +static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) +{ + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; +} + +static stbir_uint32 stbir__calculate_memory(stbir__info *info) +{ + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + + info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); + + info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; + + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; +} + +static int stbir__resize_allocated(stbir__info *info, + const void* input_data, int input_stride_in_bytes, + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) +{ + size_t memory_required = stbir__calculate_memory(info); + + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; + +#ifdef STBIR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); +#endif + + STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) + return 0; + + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; + + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + + if (alpha_channel >= info->channels) + return 0; + + STBIR_ASSERT(tempmem); + + if (!tempmem) + return 0; + + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) + return 0; + + memset(tempmem, 0, tempmem_size_in_bytes); + + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; + + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; + + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; + + info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); + + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; + +#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) + + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; + + STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + +#undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; + + stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + + STBIR_PROGRESS_REPORT(0); + + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); + + STBIR_PROGRESS_REPORT(1); + +#ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return 1; +} + + +static int stbir__resize_arbitrary( + void *alloc_context, + const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) +{ + stbir__info info; + int result; + size_t memory_required; + void* extra_memory; + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); + + if (!extra_memory) + return 0; + + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); + + STBIR_FREE(extra_memory, alloc_context); + + return result; +} + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset) +{ + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION diff --git a/test_data/lots_of_files/stb_image_write.h b/test_data/lots_of_files/stb_image_write.h new file mode 100644 index 0000000..38d3835 --- /dev/null +++ b/test_data/lots_of_files/stb_image_write.h @@ -0,0 +1,993 @@ +/* stb_image_write - v1.00 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #define _CRT_SECURE_NO_WARNINGS + #define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include <stdio.h> +#endif // STBI_WRITE_NO_STDIO + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,sz) realloc(p,sz) +#define STBIW_FREE(p) free(p) +#endif +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include <assert.h> +#define STBIW_ASSERT(x) assert(x) +#endif + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = (unsigned char) va_arg(v, int); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = (unsigned char) x; + b[1] = (unsigned char) (x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=(unsigned char)x; + b[1]=(unsigned char)(x>>8); + b[2]=(unsigned char)(x>>16); + b[3]=(unsigned char)(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context,d,1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = (unsigned char) (len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = (unsigned char) (len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = (unsigned char) (length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = (unsigned char )(length & 0xff); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, (unsigned char) *bitbuffer); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack! + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + + { + // compute adler32 on input + unsigned int k=0, s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, (unsigned char) (s2 >> 8)); + stbiw__sbpush(out, (unsigned char) s2); + stbiw__sbpush(out, (unsigned char) (s1 >> 8)); + stbiw__sbpush(out, (unsigned char) s1); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256]; + unsigned int crc = ~0u; + int i,j; + if (crc_table[1] == 0) + for(i=0; i < 256; i++) + for (crc_table[i]=i, j=0; j < 8; ++j) + crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0); + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return (unsigned char) a; + if (pb <= pc) return (unsigned char) b; + return (unsigned char) c; +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = (unsigned char) ctype[n]; + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ diff --git a/test_data/lots_of_files/stb_leakcheck.h b/test_data/lots_of_files/stb_leakcheck.h new file mode 100644 index 0000000..7e49fc9 --- /dev/null +++ b/test_data/lots_of_files/stb_leakcheck.h @@ -0,0 +1,124 @@ +// stb_leakcheck.h - v0.2 - quick & dirty malloc leak-checking - public domain +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + +#ifdef STB_LEAKCHECK_IMPLEMENTATION +#undef STB_LEAKCHECK_IMPLEMENTATION // don't implenment more than once + +// if we've already included leakcheck before, undefine the macros +#ifdef malloc +#undef malloc +#undef free +#undef realloc +#endif + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +typedef struct malloc_info stb_leakcheck_malloc_info; + +struct malloc_info +{ + char *file; + int line; + size_t size; + stb_leakcheck_malloc_info *next,*prev; +}; + +static stb_leakcheck_malloc_info *mi_head; + +void *stb_leakcheck_malloc(size_t sz, char *file, int line) +{ + stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) malloc(sz + sizeof(*mi)); + if (mi == NULL) return mi; + mi->file = file; + mi->line = line; + mi->next = mi_head; + if (mi_head) + mi->next->prev = mi; + mi->prev = NULL; + mi->size = (int) sz; + mi_head = mi; + return mi+1; +} + +void stb_leakcheck_free(void *ptr) +{ + if (ptr != NULL) { + stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; + mi->size = ~mi->size; + #ifndef STB_LEAKCHECK_SHOWALL + if (mi->prev == NULL) { + assert(mi_head == mi); + mi_head = mi->next; + } else + mi->prev->next = mi->next; + if (mi->next) + mi->next->prev = mi->prev; + #endif + } +} + +void *stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line) +{ + if (ptr == NULL) { + return stb_leakcheck_malloc(sz, file, line); + } else if (sz == 0) { + stb_leakcheck_free(ptr); + return NULL; + } else { + stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; + if (sz <= mi->size) + return ptr; + else { + #ifdef STB_LEAKCHECK_REALLOC_PRESERVE_MALLOC_FILELINE + void *q = stb_leakcheck_malloc(sz, mi->file, mi->line); + #else + void *q = stb_leakcheck_malloc(sz, file, line); + #endif + if (q) { + memcpy(q, ptr, mi->size); + stb_leakcheck_free(ptr); + } + return q; + } + } +} + +void stb_leakcheck_dumpmem(void) +{ + stb_leakcheck_malloc_info *mi = mi_head; + while (mi) { + if ((ptrdiff_t) mi->size >= 0) + printf("LEAKED: %s (%4d): %8z bytes at %p\n", mi->file, mi->line, mi->size, mi+1); + mi = mi->next; + } + #ifdef STB_LEAKCHECK_SHOWALL + mi = mi_head; + while (mi) { + if ((ptrdiff_t) mi->size < 0) + printf("FREED : %s (%4d): %8z bytes at %p\n", mi->file, mi->line, ~mi->size, mi+1); + mi = mi->next; + } + #endif +} +#endif // STB_LEAKCHECK_IMPLEMENTATION + +#ifndef INCLUDE_STB_LEAKCHECK_H +#define INCLUDE_STB_LEAKCHECK_H + +#define malloc(sz) stb_leakcheck_malloc(sz, __FILE__, __LINE__) +#define free(p) stb_leakcheck_free(p) +#define realloc(p,sz) stb_leakcheck_realloc(p,sz, __FILE__, __LINE__) + +extern void * stb_leakcheck_malloc(size_t sz, char *file, int line); +extern void * stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line); +extern void stb_leakcheck_free(void *ptr); +extern void stb_leakcheck_dumpmem(void); + +#endif // INCLUDE_STB_LEAKCHECK_H diff --git a/test_data/lots_of_files/stb_perlin.h b/test_data/lots_of_files/stb_perlin.h new file mode 100644 index 0000000..399e438 --- /dev/null +++ b/test_data/lots_of_files/stb_perlin.h @@ -0,0 +1,182 @@ +// stb_perlin.h - v0.2 - perlin noise +// public domain single-file C implementation by Sean Barrett +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// +// +// to create the implementation, +// #define STB_PERLIN_IMPLEMENTATION +// in *one* C/CPP file that includes this file. + + +// Documentation: +// +// float stb_perlin_noise3( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0) +// +// This function computes a random value at the coordinate (x,y,z). +// Adjacent random values are continuous but the noise fluctuates +// its randomness with period 1, i.e. takes on wholly unrelated values +// at integer points. Specifically, this implements Ken Perlin's +// revised noise function from 2002. +// +// The "wrap" parameters can be used to create wraparound noise that +// wraps at powers of two. The numbers MUST be powers of two. Specify +// 0 to mean "don't care". (The noise always wraps every 256 due +// details of the implementation, even if you ask for larger or no +// wrapping.) + + +#ifdef __cplusplus +extern "C" float stb_perlin_noise3(float x, float y, float z, int x_wrap=0, int y_wrap=0, int z_wrap=0); +#else +extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap); +#endif + +#ifdef STB_PERLIN_IMPLEMENTATION + +#include <math.h> // floor() + +// not same permutation table as Perlin's reference to avoid copyright issues; +// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/ +// @OPTIMIZE: should this be unsigned char instead of int for cache? +static int stb__perlin_randtab[512] = +{ + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, + + // and a second copy so we don't need an extra mask or static initializer + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, +}; + +static float stb__perlin_lerp(float a, float b, float t) +{ + return a + (b-a) * t; +} + +// different grad function from Perlin's, but easy to modify to match reference +static float stb__perlin_grad(int hash, float x, float y, float z) +{ + static float basis[12][4] = + { + { 1, 1, 0 }, + { -1, 1, 0 }, + { 1,-1, 0 }, + { -1,-1, 0 }, + { 1, 0, 1 }, + { -1, 0, 1 }, + { 1, 0,-1 }, + { -1, 0,-1 }, + { 0, 1, 1 }, + { 0,-1, 1 }, + { 0, 1,-1 }, + { 0,-1,-1 }, + }; + + // perlin's gradient has 12 cases so some get used 1/16th of the time + // and some 2/16ths. We reduce bias by changing those fractions + // to 5/16ths and 6/16ths, and the same 4 cases get the extra weight. + static unsigned char indices[64] = + { + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,9,1,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + }; + + // if you use reference permutation table, change 63 below to 15 to match reference + float *grad = basis[indices[hash & 63]]; + return grad[0]*x + grad[1]*y + grad[2]*z; +} + +float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + unsigned int x_mask = (x_wrap-1) & 255; + unsigned int y_mask = (y_wrap-1) & 255; + unsigned int z_mask = (z_wrap-1) & 255; + int px = (int) floor(x); + int py = (int) floor(y); + int pz = (int) floor(z); + int x0 = px & x_mask, x1 = (px+1) & x_mask; + int y0 = py & y_mask, y1 = (py+1) & y_mask; + int z0 = pz & z_mask, z1 = (pz+1) & z_mask; + int r0,r1, r00,r01,r10,r11; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0]; + r1 = stb__perlin_randtab[x1]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} +#endif // STB_PERLIN_IMPLEMENTATION diff --git a/test_data/lots_of_files/stb_rect_pack.h b/test_data/lots_of_files/stb_rect_pack.h new file mode 100644 index 0000000..c61de89 --- /dev/null +++ b/test_data/lots_of_files/stb_rect_pack.h @@ -0,0 +1,572 @@ +// stb_rect_pack.h - v0.08 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// Bugfixes / warning fixes +// Jeremy Jaussaud +// +// Version history: +// +// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) +// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight, +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT +#include <stdlib.h> +#define STBRP_SORT qsort +#endif + +#ifndef STBRP_ASSERT +#include <assert.h> +#define STBRP_ASSERT assert +#endif + +enum +{ + STBRP__INIT_skyline = 1, +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + stbrp_node *L1 = NULL, *L2 = NULL; + int count=0; + cur = context->active_head; + while (cur) { + L1 = cur; + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + L2 = cur; + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_width_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->h > q->h) ? -1 : (p->h < q->h); +} + +static int rect_original_order(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + #ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); + #endif + } + + // sort according to heuristic + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + if (rects[i].w == 0 || rects[i].h == 0) { + rects[i].x = rects[i].y = 0; // empty rect needs no space + } else { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + } + + // unsort + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); +} +#endif diff --git a/test_data/lots_of_files/stb_textedit.h b/test_data/lots_of_files/stb_textedit.h new file mode 100644 index 0000000..6740b91 --- /dev/null +++ b/test_data/lots_of_files/stb_textedit.h @@ -0,0 +1,1301 @@ +// stb_textedit.h - v1.7 - public domain - Sean Barrett +// Development of this library was sponsored by RAD Game Tools +// +// This C header file implements the guts of a multi-line text-editing +// widget; you implement display, word-wrapping, and low-level string +// insertion/deletion, and stb_textedit will map user inputs into +// insertions & deletions, plus updates to the cursor position, +// selection state, and undo state. +// +// It is intended for use in games and other systems that need to build +// their own custom widgets and which do not have heavy text-editing +// requirements (this library is not recommended for use for editing large +// texts, as its performance does not scale and it has limited undo). +// +// Non-trivial behaviors are modelled after Windows text controls. +// +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// +// +// DEPENDENCIES +// +// Uses the C runtime function 'memmove', which you can override +// by defining STB_TEXTEDIT_memmove before the implementation. +// Uses no other functions. Performs no runtime allocations. +// +// +// VERSION HISTORY +// +// 1.7 (2015-09-13) change y range handling in case baseline is non-0 +// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove +// 1.5 (2014-09-10) add support for secondary keys for OS X +// 1.4 (2014-08-17) fix signed/unsigned warnings +// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary +// 1.2 (2014-05-27) fix some RAD types that had crept into the new code +// 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) +// 1.0 (2012-07-26) improve documentation, initial public release +// 0.3 (2012-02-24) bugfixes, single-line mode; insert mode +// 0.2 (2011-11-28) fixes to undo/redo +// 0.1 (2010-07-08) initial version +// +// ADDITIONAL CONTRIBUTORS +// +// Ulf Winklemann: move-by-word in 1.1 +// Fabian Giesen: secondary key inputs in 1.5 +// Martins Mozeiko: STB_TEXTEDIT_memmove +// +// Bugfixes: +// Scott Graham +// Daniel Keller +// Omar Cornut +// +// USAGE +// +// This file behaves differently depending on what symbols you define +// before including it. +// +// +// Header-file mode: +// +// If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, +// it will operate in "header file" mode. In this mode, it declares a +// single public symbol, STB_TexteditState, which encapsulates the current +// state of a text widget (except for the string, which you will store +// separately). +// +// To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a +// primitive type that defines a single character (e.g. char, wchar_t, etc). +// +// To save space or increase undo-ability, you can optionally define the +// following things that are used by the undo system: +// +// STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// If you don't define these, they are set to permissive types and +// moderate sizes. The undo system does no memory allocations, so +// it grows STB_TexteditState by the worst-case storage which is (in bytes): +// +// [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT +// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT +// +// +// Implementation mode: +// +// If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it +// will compile the implementation of the text edit widget, depending +// on a large number of symbols which must be defined before the include. +// +// The implementation is defined only as static functions. You will then +// need to provide your own APIs in the same file which will access the +// static functions. +// +// The basic concept is that you provide a "string" object which +// behaves like an array of characters. stb_textedit uses indices to +// refer to positions in the string, implicitly representing positions +// in the displayed textedit. This is true for both plain text and +// rich text; even with rich text stb_truetype interacts with your +// code as if there was an array of all the displayed characters. +// +// Symbols that must be the same in header-file and implementation mode: +// +// STB_TEXTEDIT_CHARTYPE the character type +// STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// Symbols you must define for implementation mode: +// +// STB_TEXTEDIT_STRING the type of object representing a string being edited, +// typically this is a wrapper object with other data you need +// +// STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) +// STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters +// starting from character #n (see discussion below) +// STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character +// to the xpos of the i+1'th char for a line of characters +// starting at character #n (i.e. accounts for kerning +// with previous char) +// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character +// (return type is int, -1 means not valid to insert) +// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based +// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize +// as manually wordwrapping for end-of-line positioning +// +// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i +// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) +// +// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key +// +// STB_TEXTEDIT_K_LEFT keyboard input to move cursor left +// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right +// STB_TEXTEDIT_K_UP keyboard input to move cursor up +// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down +// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME +// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END +// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME +// STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END +// STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor +// STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor +// STB_TEXTEDIT_K_UNDO keyboard input to perform undo +// STB_TEXTEDIT_K_REDO keyboard input to perform redo +// +// Optional: +// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode +// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), +// required for WORDLEFT/WORDRIGHT +// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT +// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT +// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line +// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line +// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text +// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text +// +// Todo: +// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page +// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page +// +// Keyboard input must be encoded as a single integer value; e.g. a character code +// and some bitflags that represent shift states. to simplify the interface, SHIFT must +// be a bitflag, so we can test the shifted state of cursor movements to allow selection, +// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. +// +// You can encode other things, such as CONTROL or ALT, in additional bits, and +// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, +// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN +// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, +// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the +// API below. The control keys will only match WM_KEYDOWN events because of the +// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN +// bit so it only decodes WM_CHAR events. +// +// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed +// row of characters assuming they start on the i'th character--the width and +// the height and the number of characters consumed. This allows this library +// to traverse the entire layout incrementally. You need to compute word-wrapping +// here. +// +// Each textfield keeps its own insert mode state, which is not how normal +// applications work. To keep an app-wide insert mode, update/copy the +// "insert_mode" field of STB_TexteditState before/after calling API functions. +// +// API +// +// void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +// +// void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) +// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +// +// Each of these functions potentially updates the string and updates the +// state. +// +// initialize_state: +// set the textedit state to a known good default state when initially +// constructing the textedit. +// +// click: +// call this with the mouse x,y on a mouse down; it will update the cursor +// and reset the selection start/end to the cursor point. the x,y must +// be relative to the text widget, with (0,0) being the top left. +// +// drag: +// call this with the mouse x,y on a mouse drag/up; it will update the +// cursor and the selection end point +// +// cut: +// call this to delete the current selection; returns true if there was +// one. you should FIRST copy the current selection to the system paste buffer. +// (To copy, just copy the current selection out of the string yourself.) +// +// paste: +// call this to paste text at the current cursor point or over the current +// selection if there is one. +// +// key: +// call this for keyboard inputs sent to the textfield. you can use it +// for "key down" events or for "translated" key events. if you need to +// do both (as in Win32), or distinguish Unicode characters from control +// inputs, set a high bit to distinguish the two; then you can define the +// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit +// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is +// clear. +// +// When rendering, you can read the cursor position and selection state from +// the STB_TexteditState. +// +// +// Notes: +// +// This is designed to be usable in IMGUI, so it allows for the possibility of +// running in an IMGUI that has NOT cached the multi-line layout. For this +// reason, it provides an interface that is compatible with computing the +// layout incrementally--we try to make sure we make as few passes through +// as possible. (For example, to locate the mouse pointer in the text, we +// could define functions that return the X and Y positions of characters +// and binary search Y and then X, but if we're doing dynamic layout this +// will run the layout algorithm many times, so instead we manually search +// forward in one pass. Similar logic applies to e.g. up-arrow and +// down-arrow movement.) +// +// If it's run in a widget that *has* cached the layout, then this is less +// efficient, but it's not horrible on modern computers. But you wouldn't +// want to edit million-line files with it. + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Header-file mode +//// +//// + +#ifndef INCLUDE_STB_TEXTEDIT_H +#define INCLUDE_STB_TEXTEDIT_H + +//////////////////////////////////////////////////////////////////////// +// +// STB_TexteditState +// +// Definition of STB_TexteditState which you should store +// per-textfield; it includes cursor position, selection state, +// and undo state. +// + +#ifndef STB_TEXTEDIT_UNDOSTATECOUNT +#define STB_TEXTEDIT_UNDOSTATECOUNT 99 +#endif +#ifndef STB_TEXTEDIT_UNDOCHARCOUNT +#define STB_TEXTEDIT_UNDOCHARCOUNT 999 +#endif +#ifndef STB_TEXTEDIT_CHARTYPE +#define STB_TEXTEDIT_CHARTYPE int +#endif +#ifndef STB_TEXTEDIT_POSITIONTYPE +#define STB_TEXTEDIT_POSITIONTYPE int +#endif + +typedef struct +{ + // private data + STB_TEXTEDIT_POSITIONTYPE where; + short insert_length; + short delete_length; + short char_storage; +} StbUndoRecord; + +typedef struct +{ + // private data + StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; + STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point, redo_point; + short undo_char_point, redo_char_point; +} StbUndoState; + +typedef struct +{ + ///////////////////// + // + // public data + // + + int cursor; + // position of the text cursor within the string + + int select_start; // selection start point + int select_end; + // selection start and end point in characters; if equal, no selection. + // note that start may be less than or greater than end (e.g. when + // dragging the mouse, start is where the initial click was, and you + // can drag in either direction) + + unsigned char insert_mode; + // each textfield keeps its own insert mode state. to keep an app-wide + // insert mode, copy this value in/out of the app state + + ///////////////////// + // + // private data + // + unsigned char cursor_at_end_of_line; // not implemented yet + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char padding1, padding2, padding3; + float preferred_x; // this determines where the cursor up/down tries to seek to along x + StbUndoState undostate; +} STB_TexteditState; + + +//////////////////////////////////////////////////////////////////////// +// +// StbTexteditRow +// +// Result of layout query, used by stb_textedit to determine where +// the text in each row is. + +// result of layout query +typedef struct +{ + float x0,x1; // starting x location, end x location (allows for align=right, etc) + float baseline_y_delta; // position of baseline relative to previous row's baseline + float ymin,ymax; // height of row above and below baseline + int num_chars; +} StbTexteditRow; +#endif //INCLUDE_STB_TEXTEDIT_H + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Implementation mode +//// +//// + + +// implementation isn't include-guarded, since it might have indirectly +// included just the "header" portion +#ifdef STB_TEXTEDIT_IMPLEMENTATION + +#ifndef STB_TEXTEDIT_memmove +#include <string.h> +#define STB_TEXTEDIT_memmove memmove +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// Mouse input handling +// + +// traverse the layout to locate the nearest character to a display position +static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) +{ + StbTexteditRow r; + int n = STB_TEXTEDIT_STRINGLEN(str); + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + // search rows to find one that straddles 'y' + while (i < n) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + // below all text, return 'after' last character + if (i >= n) + return n; + + // check if it's before the beginning of the line + if (x < r.x0) + return i; + + // check if it's before the end of the line + if (x < r.x1) { + // search characters in row for one that straddles 'x' + k = i; + prev_x = r.x0; + for (i=0; i < r.num_chars; ++i) { + float w = STB_TEXTEDIT_GETWIDTH(str, k, i); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else + return k+i+1; + } + prev_x += w; + } + // shouldn't happen, but if it does, fall through to end-of-line case + } + + // if the last character is a newline, return that. otherwise return 'after' the last character + if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) + return i+r.num_chars-1; + else + return i+r.num_chars; +} + +// API click: on mouse down, move the cursor to the clicked location, and reset the selection +static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + state->cursor = stb_text_locate_coord(str, x, y); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} + +// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location +static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + int p = stb_text_locate_coord(str, x, y); + state->cursor = state->select_end = p; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Keyboard input handling +// + +// forward declarations +static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); + +typedef struct +{ + float x,y; // position of n'th character + float height; // height of line + int first_char, length; // first char of row, and length + int prev_first; // first char of previous row +} StbFindState; + +// find the x/y location of a character, and remember info about the previous row in +// case we get a move-up event (for page up, we'll have to rescan) +static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) +{ + StbTexteditRow r; + int prev_start = 0; + int z = STB_TEXTEDIT_STRINGLEN(str); + int i=0, first; + + if (n == z) { + // if it's at the end, then find the last line -- simpler than trying to + // explicitly handle this case in the regular code + if (single_line) { + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; + } else { + find->y = 0; + find->x = 0; + find->height = 1; + while (i < z) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + prev_start = i; + i += r.num_chars; + } + find->first_char = i; + find->length = 0; + find->prev_first = prev_start; + } + return; + } + + // search rows to find the one that straddles character n + find->y = 0; + + for(;;) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (n < i + r.num_chars) + break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + // now scan to find xpos + find->x = r.x0; + i = 0; + for (i=0; first+i < n; ++i) + find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); +} + +#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +// make the selection/cursor state valid if client altered the string +static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + int n = STB_TEXTEDIT_STRINGLEN(str); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + // if clamping forced them to be equal, move the cursor to match + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} + +// delete characters while updating undo +static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) +{ + stb_text_makeundo_delete(str, state, where, len); + STB_TEXTEDIT_DELETECHARS(str, where, len); + state->has_preferred_x = 0; +} + +// delete the section +static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + stb_textedit_clamp(str, state); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} + +// canoncialize the selection so start <= end +static void stb_textedit_sortselection(STB_TexteditState *state) +{ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} + +// move cursor to first character of selection +static void stb_textedit_move_to_first(STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} + +// move cursor to last character of selection +static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} + +#ifdef STB_TEXTEDIT_IS_SPACE +static int is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx ) +{ + return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : 1; +} + +static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) +{ + int c = _state->cursor - 1; + while( c >= 0 && !is_word_boundary( _str, c ) ) + --c; + + if( c < 0 ) + c = 0; + + return c; +} + +static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) +{ + const int len = STB_TEXTEDIT_STRINGLEN(_str); + int c = _state->cursor+1; + while( c < len && !is_word_boundary( _str, c ) ) + ++c; + + if( c > len ) + c = len; + + return c; +} +#endif + +// update selection and cursor to match each other +static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) +{ + if (!STB_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else + state->cursor = state->select_end; +} + +// API cut: delete selection +static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_delete_selection(str,state); // implicity clamps + state->has_preferred_x = 0; + return 1; + } + return 0; +} + +// API paste: replace existing selection with passed-in text +static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) +{ + STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; + // if there's a selection, the paste should delete it + stb_textedit_clamp(str, state); + stb_textedit_delete_selection(str,state); + // try to insert the characters + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { + stb_text_makeundo_insert(state, state->cursor, len); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + // remove the undo since we didn't actually insert the characters + if (state->undostate.undo_point) + --state->undostate.undo_point; + return 0; +} + +// API key: process a keyboard input +static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +{ +retry: + switch (key) { + default: { + int c = STB_TEXTEDIT_KEYTOTEXT(key); + if (c > 0) { + STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; + + // can't add newline in single-line mode + if (c == '\n' && state->single_line) + break; + + if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { + stb_text_makeundo_replace(str, state, state->cursor, 1, 1); + STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + stb_textedit_delete_selection(str,state); // implicity clamps + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { + stb_text_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + } + break; + } + +#ifdef STB_TEXTEDIT_K_INSERT + case STB_TEXTEDIT_K_INSERT: + state->insert_mode = !state->insert_mode; + break; +#endif + + case STB_TEXTEDIT_K_UNDO: + stb_text_undo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_REDO: + stb_text_redo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT: + // if currently there's a selection, move cursor to start of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else + if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_RIGHT: + // if currently there's a selection, move cursor to end of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else + ++state->cursor; + stb_textedit_clamp(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + // move selection left + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_IS_SPACE + case STB_TEXTEDIT_K_WORDLEFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else { + state->cursor = stb_textedit_move_to_word_previous(str, state); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDRIGHT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else { + state->cursor = stb_textedit_move_to_word_next(str, state); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = stb_textedit_move_to_word_previous(str, state); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; + + case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = stb_textedit_move_to_word_next(str, state); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; +#endif + + case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + // move selection right + ++state->select_end; + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_DOWN: + case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + + if (state->single_line) { + // on windows, up&down in single-line behave like left&right + key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str,state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + // now find character position down a row + if (find.length) { + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; + int start = find.first_char + find.length; + state->cursor = start; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); + #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + } + break; + } + + case STB_TEXTEDIT_K_UP: + case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + + if (state->single_line) { + // on windows, up&down become left&right + key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + // can only go up if there's a previous row + if (find.prev_first != find.first_char) { + // now find character position up a row + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; + state->cursor = find.prev_first; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); + #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + } + break; + } + + case STB_TEXTEDIT_K_DELETE: + case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + int n = STB_TEXTEDIT_STRINGLEN(str); + if (state->cursor < n) + stb_textedit_delete(str, state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_BACKSPACE: + case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + stb_textedit_clamp(str, state); + if (state->cursor > 0) { + stb_textedit_delete(str, state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2: +#endif + case STB_TEXTEDIT_K_TEXTSTART: + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2: +#endif + case STB_TEXTEDIT_K_TEXTEND: + state->cursor = STB_TEXTEDIT_STRINGLEN(str); + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); + state->has_preferred_x = 0; + break; + + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2: +#endif + case STB_TEXTEDIT_K_LINESTART: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + state->cursor = find.first_char; + state->has_preferred_x = 0; + break; + } + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2: +#endif + case STB_TEXTEDIT_K_LINEEND: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) + --state->cursor; + break; + } + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + state->cursor = state->select_end = find.first_char; + state->has_preferred_x = 0; + break; + } + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) + --state->cursor; + state->select_end = state->cursor; + break; + } + +// @TODO: +// STB_TEXTEDIT_K_PGUP - move cursor up a page +// STB_TEXTEDIT_K_PGDOWN - move cursor down a page + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Undo processing +// +// @OPTIMIZE: the undo/redo buffer should be circular + +static void stb_textedit_flush_redo(StbUndoState *state) +{ + state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; +} + +// discard the oldest entry in the undo list +static void stb_textedit_discard_undo(StbUndoState *state) +{ + if (state->undo_point > 0) { + // if the 0th undo state has characters, clean those up + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + // delete n characters from all other records + state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 + STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); + for (i=0; i < state->undo_point; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it + } + --state->undo_point; + STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); + } +} + +// discard the oldest entry in the redo list--it's bad if this +// ever happens, but because undo & redo have to store the actual +// characters in different cases, the redo character buffer can +// fill up even though the undo buffer didn't +static void stb_textedit_discard_redo(StbUndoState *state) +{ + int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; + + if (state->redo_point <= k) { + // if the k'th undo state has characters, clean those up + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + // delete n characters from all other records + state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 + STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + for (i=state->redo_point; i < k; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 + } + ++state->redo_point; + STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); + } +} + +static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) +{ + // any time we create a new undo record, we discard redo + stb_textedit_flush_redo(state); + + // if we have no free records, we have to make room, by sliding the + // existing records down + if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + stb_textedit_discard_undo(state); + + // if the characters to store won't possibly fit in the buffer, we can't undo + if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return NULL; + } + + // if we don't have enough free characters in the buffer, we have to make room + while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) + stb_textedit_discard_undo(state); + + return &state->undo_rec[state->undo_point++]; +} + +static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) +{ + StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); + if (r == NULL) + return NULL; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return NULL; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = state->undo_char_point + (short) insert_len; + return &state->undo_char[r->char_storage]; + } +} + +static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord u, *r; + if (s->undo_point == 0) + return; + + // we need to do two things: apply the undo record, and create a redo record + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) { + // if the undo record says to delete characters, then the redo record will + // need to re-insert the characters that get deleted, so we need to store + // them. + + // there are three cases: + // there's enough room to store the characters + // characters stored for *redoing* don't leave room for redo + // characters stored for *undoing* don't leave room for redo + // if the last is true, we have to bail + + if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { + // the undo records take up too much character space; there's no space to store the redo characters + r->insert_length = 0; + } else { + int i; + + // there's definitely room to store the characters eventually + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + // there's currently not enough room, so discard a redo record + stb_textedit_discard_redo(s); + // should never happen: + if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + return; + } + r = &s->undo_rec[s->redo_point-1]; + + r->char_storage = s->redo_char_point - u.delete_length; + s->redo_char_point = s->redo_char_point - (short) u.delete_length; + + // now save the characters + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); + } + + // now we can carry out the deletion + STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); + } + + // check type of recorded action: + if (u.insert_length) { + // easy case: was a deletion, so we need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point -= u.insert_length; + } + + state->cursor = u.where + u.insert_length; + + s->undo_point--; + s->redo_point--; +} + +static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord *u, r; + if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + return; + + // we need to do two things: apply the redo record, and create an undo record + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + // we KNOW there must be room for the undo record, because the redo record + // was derived from an undo record + + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + // the redo record requires us to delete characters, so the undo record + // needs to store the characters + + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = s->undo_char_point + u->insert_length; + + // now save the characters + for (i=0; i < u->insert_length; ++i) + s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); + } + + STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); + } + + if (r.insert_length) { + // easy case: need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); + } + + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} + +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) +{ + stb_text_createundo(&state->undostate, where, 0, length); +} + +static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) +{ + int i; + STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) +{ + int i; + STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +// reset the state to default +static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) +{ + state->undostate.undo_point = 0; + state->undostate.undo_char_point = 0; + state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; + state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char) is_single_line; + state->insert_mode = 0; +} + +// API initialize +static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +{ + stb_textedit_clear_state(state, is_single_line); +} +#endif//STB_TEXTEDIT_IMPLEMENTATION diff --git a/test_data/lots_of_files/stb_tilemap_editor.h b/test_data/lots_of_files/stb_tilemap_editor.h new file mode 100644 index 0000000..64480ba --- /dev/null +++ b/test_data/lots_of_files/stb_tilemap_editor.h @@ -0,0 +1,4127 @@ +// stb_tilemap_editor.h - v0.36 - Sean Barrett - http://nothings.org/stb +// placed in the public domain - not copyrighted - first released 2014-09 +// +// Embeddable tilemap editor for C/C++ +// +// +// TABLE OF CONTENTS +// FAQ +// How to compile/use the library +// Additional configuration macros +// API documentation +// Info on editing multiple levels +// Revision history +// Todo +// Credits +// License +// +// +// FAQ +// +// Q: What counts as a tilemap for this library? +// +// A: An array of rectangles, where each rectangle contains a small +// stack of images. +// +// Q: What are the limitations? +// +// A: Maps are limited to 4096x4096 in dimension. +// Each map square can only contain a stack of at most 32 images. +// A map can only use up to 32768 distinct image tiles. +// +// Q: How do I compile this? +// +// A: You need to #define several symbols before #including it, but only +// in one file. This will cause all the function definitions to be +// generated in that file. See the "HOW TO COMPILE" section. +// +// Q: What advantages does this have over a standalone editor? +// +// A: For one, you can integrate the editor into your game so you can +// flip between editing and testing without even switching windows. +// For another, you don't need an XML parser to get at the map data. +// +// Q: Can I live-edit my game maps? +// +// A: Not really, the editor keeps its own map representation. +// +// Q: How do I save and load maps? +// +// A: You have to do this yourself. The editor provides serialization +// functions (get & set) for reading and writing the map it holds. +// You can choose whatever format you want to store the map to on +// disk; you just need to provide functions to convert. (For example, +// I actually store the editor's map representation to disk basically +// as-is; then I have a single function that converts from the editor +// map representation to the game representation, which is used both +// to go from editor-to-game and from loaded-map-to-game.) +// +// Q: I want to have tiles change appearance based on what's +// adjacent, or other tile-display/substitution trickiness. +// +// A: You can do this when you convert from the editor's map +// representation to the game representation, but there's +// no way to show this live in the editor. +// +// Q: The editor appears to be put map location (0,0) at the top left? +// I want to use a different coordinate system in my game (e.g. y +// increasing upwards, or origin at the center). +// +// A: You can do this when you convert from the editor's map +// representation to the game representation. (Don't forget to +// translate link coordinates as well!) +// +// Q: The editor appears to put pixel (0,0) at the top left? I want +// to use a different coordinate system in my game. +// +// A: The editor defines an "editor pixel coordinate system" with +// (0,0) at the top left and requires you to display things in +// that coordinate system. You can freely remap those coordinates +// to anything you want on screen. +// +// Q: How do I scale the user interface? +// +// A: Since you do all the rendering, you can scale up all the rendering +// calls that the library makes to you. If you do, (a) you need +// to also scale up the mouse coordinates, and (b) you may want +// to scale the map display back down so that you're only scaling +// the UI and not everything. See the next question. +// +// Q: How do I scale the map display? +// +// A: Use stbte_set_spacing() to change the size that the map is displayed +// at. Note that the "callbacks" to draw tiles are used for both drawing +// the map and drawing the tile palette, so that callback may need to +// draw at two different scales. You should choose the scales to match +// You can tell them apart because the +// tile palette gets NULL for the property pointer. +// +// Q: How does object editing work? +// +// A: One way to think of this is that in the editor, you're placing +// spawners, not objects. Each spawner must be tile-aligned, because +// it's only a tile editor. Each tile (stack of layers) gets +// an associated set of properties, and it's up to you to +// determine what properties should appear for a given tile, +// based on e.g. the spawners that are in it. +// +// Q: How are properties themselves handled? +// +// A: All properties, regardless of UI behavior, are internally floats. +// Each tile has an array of floats associated with it, which is +// passed back to you when drawing the tiles so you can draw +// objects appropriately modified by the properties. +// +// Q: What if I want to have two different objects/spawners in +// one tile, both of which have their own properties? +// +// A: Make sure STBTE_MAX_PROPERTIES is large enough for the sum of +// properties in both objects, and then you have to explicitly +// map the property slot #s to the appropriate objects. They'll +// still all appear in a single property panel; there's no way +// to get multiple panels. +// +// Q: Can I do one-to-many linking? +// +// A: The library only supports one link per tile. However, you +// can have multiple tiles all link to a single tile. So, you +// can fake one-to-many linking by linking in the reverse +// direction. +// +// Q: What if I have two objects in the same tile, and they each +// need an independent link? Or I have two kinds of link associated +// with a single object? +// +// A: There is no way to do this. (Unless you can reverse one link.) +// +// Q: How does cut & paste interact with object properties & links? +// +// A: Currently the library has no idea which properties or links +// are associated with which layers of a tile. So currently, the +// library will only copy properties & links if the layer panel +// is set to allow all layers to be copied, OR if you set the +// "props" in the layer panel to "always". Similarly, you can +// set "props" to "none" so it will never copy. +// +// Q: What happens if the library gets a memory allocation failure +// while I'm editing? Will I lose my work? +// +// A: The library allocates all editor memory when you create +// the tilemap. It allocates a maximally-sized map and a +// fixed-size undo buffer (and the fixed-size copy buffer +// is static), and never allocates memory while it's running. +// So it can't fail due to running out of memory. +// +// Q: What happens if the library crashes while I'm editing? Will +// I lose my work? +// +// A: Yes. Save often. +// +// +// HOW TO COMPILE +// +// This header file contains both the header file and the +// implementation file in one. To create the implementation, +// in one source file define a few symbols first and then +// include this header: +// +// #define STB_TILEMAP_EDITOR_IMPLEMENTATION +// // this triggers the implementation +// +// void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, uint color); +// // this must draw a filled rectangle (exclusive on right/bottom) +// // color = (r<<16)|(g<<8)|(b) +// +// void STBTE_DRAW_TILE(int x0, int y0, +// unsigned short id, int highlight, float *data); +// // this draws the tile image identified by 'id' in one of several +// // highlight modes (see STBTE_drawmode_* in the header section); +// // if 'data' is NULL, it's drawing the tile in the palette; if 'data' +// // is not NULL, it's drawing a tile on the map, and that is the data +// // associated with that map tile +// +// #include "stb_tilemap_editor.h" +// +// Optionally you can define the following functions before the include; +// note these must be macros (but they can just call a function) so +// this library can #ifdef to detect if you've defined them: +// +// #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ... +// // Returns the type of the n'th property of a given tile, which +// // controls how it is edited. Legal types are: +// // 0 /* no editable property in this slot */ +// // STBTE_PROP_int /* uses a slider to adjust value */ +// // STBTE_PROP_float /* uses a weird multi-axis control */ +// // STBTE_PROP_bool /* uses a checkbox to change value */ +// // And you can bitwise-OR in the following flags: +// // STBTE_PROP_disabled +// // Note that all of these are stored as floats in the param array. +// // The integer slider is limited in precision based on the space +// // available on screen, so for wide-ranged integers you may want +// // to use floats instead. +// // +// // Since the tiledata is passed to you, you can choose which property +// // is bound to that slot based on that data. +// // +// // Changing the type of a parameter does not cause the underlying +// // value to be clamped to the type min/max except when the tile is +// // explicitly selected. +// +// #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ... +// // these return a string with the name for slot #n in the float +// // property list for the tile. +// +// #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here... +// #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here... +// // These return the allowable range for the property values for +// // the specified slot. It is never called for boolean types. +// +// #define STBTE_PROP_FLOAT_SCALE(int n, short *tiledata, float *params) +// // This rescales the float control for a given property; by default +// // left mouse drags add integers, right mouse drags adds fractions, +// // but you can rescale this per-property. +// +// #define STBTE_FLOAT_CONTROL_GRANULARITY ... value ... +// // This returns the number of pixels of mouse motion necessary +// // to advance the object float control. Default is 4 +// +// #define STBTE_ALLOW_LINK(short *src, float *src_data, \ +// short *dest, float *dest_data) ...your code... +// // this returns true or false depending on whether you allow a link +// // to be drawn from a tile 'src' to a tile 'dest'. if you don't +// // define this, linking will not be supported +// +// #define STBTE_LINK_COLOR(short *src, float *src_data, \ +// short *dest, float *dest_data) ...your code... +// // return a color encoded as a 24-bit unsigned integer in the +// // form 0xRRGGBB. If you don't define this, default colors will +// // be used. +// +// +// [[ support for those below is not implemented yet ]] +// +// #define STBTE_HITTEST_TILE(x0,y0,id,mx,my) ...your code here... +// // this returns true or false depending on whether the mouse +// // pointer at mx,my is over (touching) a tile of type 'id' +// // displayed at x0,y0. Normally stb_tilemap_editor just does +// // this hittest based on the tile geometry, but if you have +// // tiles whose images extend out of the tile, you'll need this. +// +// ADDITIONAL CONFIGURATION +// +// The following symbols set static limits which determine how much +// memory will be allocated for the editor. You can override them +// by making similiar definitions, but memory usage will increase. +// +// #define STBTE_MAX_TILEMAP_X 200 // max 4096 +// #define STBTE_MAX_TILEMAP_Y 200 // max 4096 +// #define STBTE_MAX_LAYERS 8 // max 32 +// #define STBTE_MAX_CATEGORIES 100 +// #define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB +// #define STBTE_MAX_COPY 90000 // e.g. 300x300 +// #define STBTE_MAX_PROPERTIESERTIES 10 // max properties per tile +// +// API +// +// Further documentation appears in the header-file section below. +// +// EDITING MULTIPLE LEVELS +// +// You can only have one active editor instance. To switch between multiple +// levels, you can either store the levels in your own format and copy them +// in and out of the editor format, or you can create multiple stbte_tilemap +// objects and switch between them. The latter has the advantage that each +// stbte_tilemap keeps its own undo state. (The clipboard is global, so +// either approach allows cut&pasting between levels.) +// +// REVISION HISTORY +// 0.36 minor compiler support +// 0.35 layername button changes +// - layername buttons grow with the layer panel +// - fix stbte_create_map being declared as stbte_create +// - fix declaration of stbte_create_map +// 0.30 properties release +// - properties panel for editing user-defined "object" properties +// - can link each tile to one other tile +// - keyboard interface +// - fix eraser tool bug (worked in complex cases, failed in simple) +// - undo/redo tools have visible disabled state +// - tiles on higher layers draw on top of adjacent lower-layer tiles +// 0.20 erasable release +// - eraser tool +// - fix bug when pasting into protected layer +// - better color scheme +// - internal-use color picker +// 0.10 initial release +// +// TODO +// +// Separate scroll state for each category +// Implement paint bucket +// Support STBTE_HITTEST_TILE above +// ?Cancel drags by clicking other button? - may be fixed +// Finish support for toolbar at side +// +// CREDITS +// +// +// Main editor & features +// Sean Barrett +// Additional features: +// Josh Huelsman +// Bugfixes: +// Ryan Whitworth +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + + + +/////////////////////////////////////////////////////////////////////// +// +// HEADER SECTION + +#ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H +#define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H + +#ifdef _WIN32 + #define _CRT_SECURE_NO_WARNINGS + #include <stdlib.h> + #include <stdio.h> +#endif + +typedef struct stbte_tilemap stbte_tilemap; + +// these are the drawmodes used in STBTE_DRAW_TILE +enum +{ + STBTE_drawmode_deemphasize = -1, + STBTE_drawmode_normal = 0, + STBTE_drawmode_emphasize = 1, +}; + +// these are the property types +#define STBTE_PROP_none 0 +#define STBTE_PROP_int 1 +#define STBTE_PROP_float 2 +#define STBTE_PROP_bool 3 +#define STBTE_PROP_disabled 4 + +//////// +// +// creation +// + +extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles); +// create an editable tilemap +// map_x : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X +// map_y : dimensions of map vertically (user can change this in editor) <= STBTE_MAX_TILEMAP_Y +// map_layers : number of layers to use (fixed), <= STBTE_MAX_LAYERS +// spacing_x : initial horizontal distance between left edges of map tiles in stb_tilemap_editor pixels +// spacing_y : initial vertical distance between top edges of map tiles in stb_tilemap_editor pixels +// max_tiles : maximum number of tiles that can defined +// +// If insufficient memory, returns NULL + +extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category); +// call this repeatedly for each tile to install the tile definitions into the editable tilemap +// tm : tilemap created by stbte_create_map +// id : unique identifier for each tile, 0 <= id < 32768 +// layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7 +// (note that onscreen, the editor numbers the layers from 1 not 0) +// layer 0 is the furthest back, layer 1 is just in front of layer 0, etc +// category : which category this tile is grouped in + +extern void stbte_set_display(int x0, int y0, int x1, int y1); +// call this once to set the size; if you resize, call it again + + +///////// +// +// every frame +// + +extern void stbte_draw(stbte_tilemap *tm); + +extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame); + +//////////// +// +// user input +// + +// if you're using SDL, call the next function for SDL_MOUSEMOVE, SDL_MOUSEBUTTON, SDL_MOUSEWHEEL; +// the transformation lets you scale from SDL mouse coords to stb_tilemap_editor coords +extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset); + +// otherwise, hook these up explicitly: +extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey); +extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey); +extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll); + +// for keyboard, define your own mapping from keys to the following actions. +// this is totally optional, as all features are accessible with the mouse +enum stbte_action +{ + STBTE_tool_select, + STBTE_tool_brush, + STBTE_tool_erase, + STBTE_tool_rectangle, + STBTE_tool_eyedropper, + STBTE_tool_link, + STBTE_act_toggle_grid, + STBTE_act_toggle_links, + STBTE_act_undo, + STBTE_act_redo, + STBTE_act_cut, + STBTE_act_copy, + STBTE_act_paste, + STBTE_scroll_left, + STBTE_scroll_right, + STBTE_scroll_up, + STBTE_scroll_down, +}; +extern void stbte_action(stbte_tilemap *tm, enum stbte_action act); + +//////////////// +// +// save/load +// +// There is no editor file format. You have to save and load the data yourself +// through the following functions. You can also use these functions to get the +// data to generate game-formatted levels directly. (But make sure you save +// first! You may also want to autosave to a temp file periodically, etc etc.) + +#define STBTE_EMPTY -1 + +extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y); +// get the dimensions of the level, since the user can change them + +extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y); +// returns an array of shorts that is 'map_layers' in length. each short is +// either one of the tile_id values from define_tile, or STBTE_EMPTY. + +extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y); +// get the property array associated with the tile at x,y. this is an +// array of floats that is STBTE_MAX_PROPERTIES in length; you have to +// interpret the slots according to the semantics you've chosen + +extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty); +// gets the link associated with the tile at x,y. + +extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y); +// set the dimensions of the level, overrides previous stbte_create_map() +// values or anything the user has changed + +extern void stbte_clear_map(stbte_tilemap *tm); +// clears the map, including the region outside the defined region, so if the +// user expands the map, they won't see garbage there + +extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile); +// tile is your tile_id from define_tile, or STBTE_EMPTY + +extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val); +// set the value of the n'th slot of the tile at x,y + +extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty); +// set a link going from x,y to destx,desty. to force no link, +// use destx=desty=-1 + +//////// +// +// optional +// + +extern void stbte_set_background_tile(stbte_tilemap *tm, short id); +// selects the tile to fill the bottom layer with and used to clear bottom tiles to; +// should be same ID as + +extern void stbte_set_sidewidths(int left, int right); +// call this once to set the left & right side widths. don't call +// it again since the user can change it + +extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y); +// call this to set the spacing of map tiles and the spacing of palette tiles. +// if you rescale your display, call it again (e.g. you can implement map zooming yourself) + +extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername); +// sets a string name for your layer that shows in the layer selector. note that this +// makes the layer selector wider. 'layer' is from 0..(map_layers-1) + +#endif + +#ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION + +#ifndef STBTE_ASSERT +#define STBTE_ASSERT assert +#include <assert.h> +#endif + +#ifdef _MSC_VER +#define STBTE__NOTUSED(v) (void)(v) +#else +#define STBTE__NOTUSED(v) (void)sizeof(v) +#endif + +#ifndef STBTE_MAX_TILEMAP_X +#define STBTE_MAX_TILEMAP_X 200 +#endif + +#ifndef STBTE_MAX_TILEMAP_Y +#define STBTE_MAX_TILEMAP_Y 200 +#endif + +#ifndef STBTE_MAX_LAYERS +#define STBTE_MAX_LAYERS 8 +#endif + +#ifndef STBTE_MAX_CATEGORIES +#define STBTE_MAX_CATEGORIES 100 +#endif + +#ifndef STBTE_MAX_COPY +#define STBTE_MAX_COPY 65536 +#endif + +#ifndef STBTE_UNDO_BUFFER_BYTES +#define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB +#endif + +#ifndef STBTE_PROP_TYPE +#define STBTE__NO_PROPS +#define STBTE_PROP_TYPE(n,td,tp) 0 +#endif + +#ifndef STBTE_PROP_NAME +#define STBTE_PROP_NAME(n,td,tp) "" +#endif + +#ifndef STBTE_MAX_PROPERTIES +#define STBTE_MAX_PROPERTIES 10 +#endif + +#ifndef STBTE_PROP_MIN +#define STBTE_PROP_MIN(n,td,tp) 0 +#endif + +#ifndef STBTE_PROP_MAX +#define STBTE_PROP_MAX(n,td,tp) 100.0 +#endif + +#ifndef STBTE_PROP_FLOAT_SCALE +#define STBTE_PROP_FLOAT_SCALE(n,td,tp) 1 // default scale size +#endif + +#ifndef STBTE_FLOAT_CONTROL_GRANULARITY +#define STBTE_FLOAT_CONTROL_GRANULARITY 4 +#endif + + +#define STBTE__UNDO_BUFFER_COUNT (STBTE_UNDO_BUFFER_BYTES>>1) + +#if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096 +#error "Maximum editable map size is 4096 x 4096" +#endif +#if STBTE_MAX_LAYERS > 32 +#error "Maximum layers allowed is 32" +#endif +#if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1) +#error "Undo buffer size must be a power of 2" +#endif + +#if STBTE_MAX_PROPERTIES == 0 +#define STBTE__NO_PROPS +#endif + +#ifdef STBTE__NO_PROPS +#undef STBTE_MAX_PROPERTIES +#define STBTE_MAX_PROPERTIES 1 // so we can declare arrays +#endif + +typedef struct +{ + short x,y; +} stbte__link; + +enum +{ + STBTE__base, + STBTE__outline, + STBTE__text, + + STBTE__num_color_aspects, +}; + +enum +{ + STBTE__idle, + STBTE__over, + STBTE__down, + STBTE__over_down, + STBTE__selected, + STBTE__selected_over, + STBTE__disabled, + STBTE__num_color_states, +}; + +enum +{ + STBTE__cexpander, + STBTE__ctoolbar, + STBTE__ctoolbar_button, + STBTE__cpanel, + STBTE__cpanel_sider, + STBTE__cpanel_sizer, + STBTE__cscrollbar, + STBTE__cmapsize, + STBTE__clayer_button, + STBTE__clayer_hide, + STBTE__clayer_lock, + STBTE__clayer_solo, + STBTE__ccategory_button, + + STBTE__num_color_modes, +}; + +#ifdef STBTE__COLORPICKER +static char *stbte__color_names[] = +{ + "expander", "toolbar", "tool button", "panel", + "panel c1", "panel c2", "scollbar", "map button", + "layer", "hide", "lock", "solo", + "category", +}; +#endif // STBTE__COLORPICKER + + // idle, over, down, over&down, selected, sel&over, disabled +static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] = +{ + { + { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, }, + { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, + { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, + { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, }, + }, { + { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, }, + { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, }, + { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, }, + }, { + { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, }, + { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, }, + { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, + { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, + { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, }, + { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, }, + { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, }, + }, { + { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, + { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, + { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, + { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, + { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, }, + { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, }, + { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, }, + }, { + { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, }, + { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, }, + { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, }, + }, { + { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, }, + { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, }, + { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, }, + }, { + { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, }, + { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, }, + { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, }, + }, +}; + +#define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000 +#define STBTE_COLOR_TILEMAP_BORDER 0x203060 +#define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff +#define STBTE_COLOR_GRID 0x404040 +#define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf +#define STBTE_COLOR_SELECTION_OUTLINE2 0x303030 +#define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff +#define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000 + +#ifndef STBTE_LINK_COLOR +#define STBTE_LINK_COLOR(src,sp,dest,dp) 0x5030ff +#endif + +#ifndef STBTE_LINK_COLOR_DRAWING +#define STBTE_LINK_COLOR_DRAWING 0xff40ff +#endif + +#ifndef STBTE_LINK_COLOR_DISALLOWED +#define STBTE_LINK_COLOR_DISALLOWED 0x602060 +#endif + + +// disabled, selected, down, over +static unsigned char stbte__state_to_index[2][2][2][2] = +{ + { + { { STBTE__idle , STBTE__over }, { STBTE__down , STBTE__over_down }, }, + { { STBTE__selected, STBTE__selected_over }, { STBTE__down , STBTE__over_down }, }, + },{ + { { STBTE__disabled, STBTE__disabled }, { STBTE__disabled, STBTE__disabled }, }, + { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled }, }, + } +}; +#define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over] +#define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id)) + +#define STBTE__FONT_HEIGHT 9 +static short stbte__font_offset[95+16]; +static short stbte__fontdata[769] = +{ + 4,9,6,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6, + 6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6, + 6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8, + 0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146, + 511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16, + 124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496, + 496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257, + 257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8, + 24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127, + 127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93, + 55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62, + 8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66, + 127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39, + 103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54, + 6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28, + 8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127, + 54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9, + 9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64, + 64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127, + 127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65, + 81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127, + 64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119, + 99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65, + 65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120, + 127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92, + 24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122, + 64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28, + 124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124, + 68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124, + 64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108, + 68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65, + 65,119,62,8,16,24,12,12,24,24,12,4, +}; + +typedef struct +{ + short id; + unsigned short category_id; + char *category; + unsigned int layermask; +} stbte__tileinfo; + +#define MAX_LAYERMASK (1 << (8*sizeof(unsigned int))) + +typedef short stbte__tiledata; + +#define STBTE__NO_TILE -1 + +enum +{ + STBTE__panel_toolbar, + STBTE__panel_colorpick, + STBTE__panel_info, + STBTE__panel_layers, + STBTE__panel_props, + STBTE__panel_categories, + STBTE__panel_tiles, + + STBTE__num_panel, +}; + +enum +{ + STBTE__side_left, + STBTE__side_right, + STBTE__side_top, + STBTE__side_bottom, +}; + +enum +{ + STBTE__tool_select, + STBTE__tool_brush, + STBTE__tool_erase, + STBTE__tool_rect, + STBTE__tool_eyedrop, + STBTE__tool_fill, + STBTE__tool_link, + + STBTE__tool_showgrid, + STBTE__tool_showlinks, + + STBTE__tool_undo, + STBTE__tool_redo, + // copy/cut/paste aren't included here because they're displayed differently + + STBTE__num_tool, +}; + +// icons are stored in the 0-31 range of ASCII in the font +static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, }; + +enum +{ + STBTE__propmode_default, + STBTE__propmode_always, + STBTE__propmode_never, +}; + +enum +{ + STBTE__paint, + + // from here down does hittesting + STBTE__tick, + STBTE__mousemove, + STBTE__mousewheel, + STBTE__leftdown, + STBTE__leftup, + STBTE__rightdown, + STBTE__rightup, +}; + +typedef struct +{ + int expanded, mode; + int delta_height; // number of rows they've requested for this + int side; + int width,height; + int x0,y0; +} stbte__panel; + +typedef struct +{ + int x0,y0,x1,y1,color; +} stbte__colorrect; + +#define STBTE__MAX_DELAYRECT 256 + +typedef struct +{ + int tool, active_event; + int active_id, hot_id, next_hot_id; + int event; + int mx,my, dx,dy; + int ms_time; + int shift, scrollkey; + int initted; + int side_extended[2]; + stbte__colorrect delayrect[STBTE__MAX_DELAYRECT]; + int delaycount; + int show_grid, show_links; + int brush_state; // used to decide which kind of erasing + int eyedrop_x, eyedrop_y, eyedrop_last_layer; + int pasting, paste_x, paste_y; + int scrolling, start_x, start_y; + int last_mouse_x, last_mouse_y; + int accum_x, accum_y; + int linking; + int dragging; + int drag_x, drag_y, drag_w, drag_h; + int drag_offx, drag_offy, drag_dest_x, drag_dest_y; + int undoing; + int has_selection, select_x0, select_y0, select_x1, select_y1; + int sx,sy; + int x0,y0,x1,y1, left_width, right_width; // configurable widths + float alert_timer; + const char *alert_msg; + float dt; + stbte__panel panel[STBTE__num_panel]; + short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS]; + float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES]; +#ifdef STBTE_ALLOW_LINK + stbte__link copylinks[STBTE_MAX_COPY]; +#endif + int copy_src_x, copy_src_y; + stbte_tilemap *copy_src; + int copy_width,copy_height,has_copy,copy_has_props; +} stbte__ui_t; + +// there's only one UI system at a time, so we can globalize this +static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 }; + +#define STBTE__INACTIVE() (stbte__ui.active_id == 0) +#define STBTE__IS_ACTIVE(id) (stbte__ui.active_id == (id)) +#define STBTE__IS_HOT(id) (stbte__ui.hot_id == (id)) + +#define STBTE__BUTTON_HEIGHT (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING) +#define STBTE__BUTTON_INTERNAL_SPACING (2 + (STBTE__FONT_HEIGHT>>4)) + +typedef struct +{ + const char *name; + int locked; + int hidden; +} stbte__layer; + +enum +{ + STBTE__unlocked, + STBTE__protected, + STBTE__locked, +}; + +struct stbte_tilemap +{ + stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS]; + float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES]; + #ifdef STBTE_ALLOW_LINK + stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; + int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; + #endif + int max_x, max_y, num_layers; + int spacing_x, spacing_y; + int palette_spacing_x, palette_spacing_y; + int scroll_x,scroll_y; + int cur_category, cur_tile, cur_layer; + char *categories[STBTE_MAX_CATEGORIES]; + int num_categories, category_scroll; + stbte__tileinfo *tiles; + int num_tiles, max_tiles, digits; + unsigned char undo_available_valid; + unsigned char undo_available; + unsigned char redo_available; + unsigned char padding; + int cur_palette_count; + int palette_scroll; + int tileinfo_dirty; + stbte__layer layerinfo[STBTE_MAX_LAYERS]; + int has_layer_names; + int layername_width; + int layer_scroll; + int propmode; + int solo_layer; + int undo_pos, undo_len, redo_len; + short background_tile; + unsigned char id_in_use[32768>>3]; + short *undo_buffer; +}; + +static char *default_category = "[unassigned]"; + +static void stbte__init_gui(void) +{ + int i,n; + stbte__ui.initted = 1; + // init UI state + stbte__ui.show_links = 1; + for (i=0; i < STBTE__num_panel; ++i) { + stbte__ui.panel[i].expanded = 1; // visible if not autohidden + stbte__ui.panel[i].delta_height = 0; + stbte__ui.panel[i].side = STBTE__side_left; + } + stbte__ui.panel[STBTE__panel_toolbar ].side = STBTE__side_top; + stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right; + + if (stbte__ui.left_width == 0) + stbte__ui.left_width = 80; + if (stbte__ui.right_width == 0) + stbte__ui.right_width = 80; + + // init font + n=95+16; + for (i=0; i < 95+16; ++i) { + stbte__font_offset[i] = n; + n += stbte__fontdata[i]; + } +} + +stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles) +{ + int i; + stbte_tilemap *tm; + STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS); + STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X); + STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y); + if (map_x < 0 || map_y < 0 || map_layers < 0 || + map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS) + return NULL; + + if (!stbte__ui.initted) + stbte__init_gui(); + + tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES); + if (tm == NULL) + return NULL; + + tm->tiles = (stbte__tileinfo *) (tm+1); + tm->undo_buffer = (short *) (tm->tiles + max_tiles); + tm->num_layers = map_layers; + tm->max_x = map_x; + tm->max_y = map_y; + tm->spacing_x = spacing_x; + tm->spacing_y = spacing_y; + tm->scroll_x = 0; + tm->scroll_y = 0; + tm->palette_scroll = 0; + tm->palette_spacing_x = spacing_x+1; + tm->palette_spacing_y = spacing_y+1; + tm->cur_category = -1; + tm->cur_tile = 0; + tm->solo_layer = -1; + tm->undo_len = 0; + tm->redo_len = 0; + tm->undo_pos = 0; + tm->category_scroll = 0; + tm->layer_scroll = 0; + tm->propmode = 0; + tm->has_layer_names = 0; + tm->layername_width = 0; + tm->undo_available_valid = 0; + + for (i=0; i < tm->num_layers; ++i) { + tm->layerinfo[i].hidden = 0; + tm->layerinfo[i].locked = STBTE__unlocked; + tm->layerinfo[i].name = 0; + } + + tm->background_tile = STBTE__NO_TILE; + stbte_clear_map(tm); + + tm->max_tiles = max_tiles; + tm->num_tiles = 0; + for (i=0; i < 32768/8; ++i) + tm->id_in_use[i] = 0; + tm->tileinfo_dirty = 1; + return tm; +} + +void stbte_set_background_tile(stbte_tilemap *tm, short id) +{ + int i; + STBTE_ASSERT(id >= -1 && id < 32768); + if (id >= 32768 || id < -1) + return; + for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) + if (tm->data[0][i][0] == -1) + tm->data[0][i][0] = id; + tm->background_tile = id; +} + +void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y) +{ + tm->spacing_x = spacing_x; + tm->spacing_y = spacing_y; + tm->palette_spacing_x = palette_spacing_x; + tm->palette_spacing_y = palette_spacing_y; +} + +void stbte_set_sidewidths(int left, int right) +{ + stbte__ui.left_width = left; + stbte__ui.right_width = right; +} + +void stbte_set_display(int x0, int y0, int x1, int y1) +{ + stbte__ui.x0 = x0; + stbte__ui.y0 = y0; + stbte__ui.x1 = x1; + stbte__ui.y1 = y1; +} + +void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c) +{ + char *category = (char *) category_c; + STBTE_ASSERT(id < 32768); + STBTE_ASSERT(tm->num_tiles < tm->max_tiles); + STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0); + if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7)))) + return; + + if (category == NULL) + category = (char*) default_category; + tm->id_in_use[id>>3] |= 1 << (id&7); + tm->tiles[tm->num_tiles].category = category; + tm->tiles[tm->num_tiles].id = id; + tm->tiles[tm->num_tiles].layermask = layermask; + ++tm->num_tiles; + tm->tileinfo_dirty = 1; +} + +static int stbte__text_width(const char *str); + +void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername) +{ + STBTE_ASSERT(layer >= 0 && layer < tm->num_layers); + if (layer >= 0 && layer < tm->num_layers) { + int width; + tm->layerinfo[layer].name = layername; + tm->has_layer_names = 1; + width = stbte__text_width(layername); + tm->layername_width = (width > tm->layername_width ? width : tm->layername_width); + } +} + +void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y) +{ + *max_x = tm->max_x; + *max_y = tm->max_y; +} + +short* stbte_get_tile(stbte_tilemap *tm, int x, int y) +{ + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); + if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) + return NULL; + return tm->data[y][x]; +} + +float *stbte_get_properties(stbte_tilemap *tm, int x, int y) +{ + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); + if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) + return NULL; + return tm->props[y][x]; +} + +void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty) +{ + int gx=-1,gy=-1; + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); +#ifdef STBTE_ALLOW_LINK + if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) { + gx = tm->link[y][x].x; + gy = tm->link[y][x].y; + if (gx >= 0) + if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx])) + gx = gy = -1; + } +#endif + *destx = gx; + *desty = gy; +} + +void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val) +{ + tm->props[y][x][n] = val; +} + +static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode); + +enum +{ + STBTE__undo_none, + STBTE__undo_record, + STBTE__undo_block, +}; + +void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty) +{ +#ifdef STBTE_ALLOW_LINK + stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none); +#else + STBTE_ASSERT(0); +#endif +} + + +// returns an array of map_layers shorts. each short is either +// one of the tile_id values from define_tile, or STBTE_EMPTY + +void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y) +{ + STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X); + STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y); + if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y) + return; + tm->max_x = map_x; + tm->max_y = map_y; +} + +void stbte_clear_map(stbte_tilemap *tm) +{ + int i,j; + for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) { + tm->data[0][i][0] = tm->background_tile; + for (j=1; j < tm->num_layers; ++j) + tm->data[0][i][j] = STBTE__NO_TILE; + for (j=0; j < STBTE_MAX_PROPERTIES; ++j) + tm->props[0][i][j] = 0; + #ifdef STBTE_ALLOW_LINK + tm->link[0][i].x = -1; + tm->link[0][i].y = -1; + tm->linkcount[0][i] = 0; + #endif + } +} + +void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile) +{ + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); + STBTE_ASSERT(layer >= 0 && layer < tm->num_layers); + STBTE_ASSERT(tile >= -1 && tile < 32768); + if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) + return; + if (layer < 0 || layer >= tm->num_layers || tile < -1) + return; + tm->data[y][x][layer] = tile; +} + +static void stbte__choose_category(stbte_tilemap *tm, int category) +{ + int i,n=0; + tm->cur_category = category; + for (i=0; i < tm->num_tiles; ++i) + if (tm->tiles[i].category_id == category || category == -1) + ++n; + tm->cur_palette_count = n; + tm->palette_scroll = 0; +} + +static int stbte__strequal(char *p, char *q) +{ + while (*p) + if (*p++ != *q++) return 0; + return *q == 0; +} + +static void stbte__compute_tileinfo(stbte_tilemap *tm) +{ + int i,j,n=0; + + tm->num_categories=0; + + for (i=0; i < tm->num_tiles; ++i) { + stbte__tileinfo *t = &tm->tiles[i]; + // find category + for (j=0; j < tm->num_categories; ++j) + if (stbte__strequal(t->category, tm->categories[j])) + goto found; + tm->categories[j] = t->category; + ++tm->num_categories; + found: + t->category_id = (unsigned short) j; + } + + // currently number of categories can never decrease because you + // can't remove tile definitions, but let's get it right anyway + if (tm->cur_category > tm->num_categories) { + tm->cur_category = -1; + } + + stbte__choose_category(tm, tm->cur_category); + + tm->tileinfo_dirty = 0; +} + +static void stbte__prepare_tileinfo(stbte_tilemap *tm) +{ + if (tm->tileinfo_dirty) + stbte__compute_tileinfo(tm); +} + + +/////////////////////// undo system //////////////////////// + +// the undo system works by storing "commands" into a buffer, and +// then playing back those commands. undo and redo have to store +// the commands in different order. +// +// the commands are: +// +// 1) end_of_undo_record +// -1:short +// +// 2) end_of_redo_record +// -2:short +// +// 3) tile update +// tile_id:short (-1..32767) +// x_coord:short +// y_coord:short +// layer:short (0..31) +// +// 4) property update (also used for links) +// value_hi:short +// value_lo:short +// y_coord:short +// x_coord:short +// property:short (256+prop#) +// +// Since we use a circular buffer, we might overwrite the undo storage. +// To detect this, before playing back commands we scan back and see +// if we see an end_of_undo_record before hitting the relevant boundary, +// it's wholly contained. +// +// When we read back through, we see them in reverse order, so +// we'll see the layer number or property number first +// +// To be clearer about the circular buffer, there are two cases: +// 1. a single record is larger than the whole buffer. +// this is caught because the end_of_undo_record will +// get overwritten. +// 2. multiple records written are larger than the whole +// buffer, so some of them have been overwritten by +// the later ones. this is handled by explicitly tracking +// the undo length; we never try to parse the data that +// got overwritten + +// given two points, compute the length between them +#define stbte__wrap(pos) ((pos) & (STBTE__UNDO_BUFFER_COUNT-1)) + +#define STBTE__undo_record -2 +#define STBTE__redo_record -3 +#define STBTE__undo_junk -4 // this is written underneath the undo pointer, never used + +static void stbte__write_undo(stbte_tilemap *tm, short value) +{ + int pos = tm->undo_pos; + tm->undo_buffer[pos] = value; + tm->undo_pos = stbte__wrap(pos+1); + tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2); + tm->redo_len -= (tm->redo_len > 0); + tm->undo_available_valid = 0; +} + +static void stbte__write_redo(stbte_tilemap *tm, short value) +{ + int pos = tm->undo_pos; + tm->undo_buffer[pos] = value; + tm->undo_pos = stbte__wrap(pos-1); + tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2); + tm->undo_len -= (tm->undo_len > 0); + tm->undo_available_valid = 0; +} + +static void stbte__begin_undo(stbte_tilemap *tm) +{ + tm->redo_len = 0; + stbte__write_undo(tm, STBTE__undo_record); + stbte__ui.undoing = 1; + stbte__ui.alert_msg = 0; // clear alert if they start doing something +} + +static void stbte__end_undo(stbte_tilemap *tm) +{ + if (stbte__ui.undoing) { + // check if anything got written + int pos = stbte__wrap(tm->undo_pos-1); + if (tm->undo_buffer[pos] == STBTE__undo_record) { + // empty undo record, move back + tm->undo_pos = pos; + STBTE_ASSERT(tm->undo_len > 0); + tm->undo_len -= 1; + } + tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; + // otherwise do nothing + + stbte__ui.undoing = 0; + } +} + +static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v) +{ + STBTE_ASSERT(stbte__ui.undoing); + if (stbte__ui.undoing) { + stbte__write_undo(tm, v); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, i); + } +} + +static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v) +{ + stbte__write_redo(tm, v); + stbte__write_redo(tm, x); + stbte__write_redo(tm, y); + stbte__write_redo(tm, i); +} + +static float stbte__extract_float(short s0, short s1) +{ + union { float f; short s[2]; } converter; + converter.s[0] = s0; + converter.s[1] = s1; + return converter.f; +} + +static short stbte__extract_short(float f, int slot) +{ + union { float f; short s[2]; } converter; + converter.f = f; + return converter.s[slot]; +} + +static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) +{ + STBTE_ASSERT(stbte__ui.undoing); + if (stbte__ui.undoing) { + stbte__write_undo(tm, s1); + stbte__write_undo(tm, s0); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, 256+i); + } +} + +static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f) +{ + stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1)); +} + +static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) +{ + stbte__write_redo(tm, s1); + stbte__write_redo(tm, s0); + stbte__write_redo(tm, x); + stbte__write_redo(tm, y); + stbte__write_redo(tm, 256+i); +} + + +static int stbte__undo_find_end(stbte_tilemap *tm) +{ + // first scan through for the end record + int i, pos = stbte__wrap(tm->undo_pos-1); + for (i=0; i < tm->undo_len;) { + STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); + if (tm->undo_buffer[pos] == STBTE__undo_record) + break; + if (tm->undo_buffer[pos] >= 255) + pos = stbte__wrap(pos-5), i += 5; + else + pos = stbte__wrap(pos-4), i += 4; + } + if (i >= tm->undo_len) + return -1; + return pos; +} + +static void stbte__undo(stbte_tilemap *tm) +{ + int i, pos, endpos; + endpos = stbte__undo_find_end(tm); + if (endpos < 0) + return; + + // we found a complete undo record + pos = stbte__wrap(tm->undo_pos-1); + + // start a redo record + stbte__write_redo(tm, STBTE__redo_record); + + // so now go back through undo and apply in reverse + // order, and copy it to redo + for (i=0; endpos != pos; i += 4) { + int x,y,n,v; + // get the undo entry + n = tm->undo_buffer[pos]; + y = tm->undo_buffer[stbte__wrap(pos-1)]; + x = tm->undo_buffer[stbte__wrap(pos-2)]; + v = tm->undo_buffer[stbte__wrap(pos-3)]; + if (n >= 255) { + short s0=0,s1=0; + int v2 = tm->undo_buffer[stbte__wrap(pos-4)]; + pos = stbte__wrap(pos-5); + if (n > 255) { + float vf = stbte__extract_float(v, v2); + s0 = stbte__extract_short(tm->props[y][x][n-256], 0); + s1 = stbte__extract_short(tm->props[y][x][n-256], 1); + tm->props[y][x][n-256] = vf; + } else { +#ifdef STBTE_ALLOW_LINK + s0 = tm->link[y][x].x; + s1 = tm->link[y][x].y; + stbte__set_link(tm, x,y, v, v2, STBTE__undo_none); +#endif + } + // write the redo entry + stbte__redo_record_prop(tm, x, y, n-256, s0,s1); + // apply the undo entry + } else { + pos = stbte__wrap(pos-4); + // write the redo entry + stbte__redo_record(tm, x, y, n, tm->data[y][x][n]); + // apply the undo entry + tm->data[y][x][n] = (short) v; + } + } + // overwrite undo record with junk + tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; +} + +static int stbte__redo_find_end(stbte_tilemap *tm) +{ + // first scan through for the end record + int i, pos = stbte__wrap(tm->undo_pos+1); + for (i=0; i < tm->redo_len;) { + STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); + if (tm->undo_buffer[pos] == STBTE__redo_record) + break; + if (tm->undo_buffer[pos] >= 255) + pos = stbte__wrap(pos+5), i += 5; + else + pos = stbte__wrap(pos+4), i += 4; + } + if (i >= tm->redo_len) + return -1; // this should only ever happen if redo buffer is empty + return pos; +} + +static void stbte__redo(stbte_tilemap *tm) +{ + // first scan through for the end record + int i, pos, endpos; + endpos = stbte__redo_find_end(tm); + if (endpos < 0) + return; + + // we found a complete redo record + pos = stbte__wrap(tm->undo_pos+1); + + // start an undo record + stbte__write_undo(tm, STBTE__undo_record); + + for (i=0; pos != endpos; i += 4) { + int x,y,n,v; + n = tm->undo_buffer[pos]; + y = tm->undo_buffer[stbte__wrap(pos+1)]; + x = tm->undo_buffer[stbte__wrap(pos+2)]; + v = tm->undo_buffer[stbte__wrap(pos+3)]; + if (n >= 255) { + int v2 = tm->undo_buffer[stbte__wrap(pos+4)]; + short s0=0,s1=0; + pos = stbte__wrap(pos+5); + if (n > 255) { + float vf = stbte__extract_float(v, v2); + s0 = stbte__extract_short(tm->props[y][x][n-256],0); + s1 = stbte__extract_short(tm->props[y][x][n-256],1); + tm->props[y][x][n-256] = vf; + } else { +#ifdef STBTE_ALLOW_LINK + s0 = tm->link[y][x].x; + s1 = tm->link[y][x].y; + stbte__set_link(tm, x,y,v,v2, STBTE__undo_none); +#endif + } + // don't use stbte__undo_record_prop because it's guarded + stbte__write_undo(tm, s1); + stbte__write_undo(tm, s0); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, n); + } else { + pos = stbte__wrap(pos+4); + // don't use stbte__undo_record because it's guarded + stbte__write_undo(tm, tm->data[y][x][n]); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, n); + tm->data[y][x][n] = (short) v; + } + } + tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; +} + +// because detecting that undo is available +static void stbte__recompute_undo_available(stbte_tilemap *tm) +{ + tm->undo_available = (stbte__undo_find_end(tm) >= 0); + tm->redo_available = (stbte__redo_find_end(tm) >= 0); +} + +static int stbte__undo_available(stbte_tilemap *tm) +{ + if (!tm->undo_available_valid) + stbte__recompute_undo_available(tm); + return tm->undo_available; +} + +static int stbte__redo_available(stbte_tilemap *tm) +{ + if (!tm->undo_available_valid) + stbte__recompute_undo_available(tm); + return tm->redo_available; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef STBTE_ALLOW_LINK +static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode) +{ + stbte__link *a; + STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y); + a = &tm->link[src_y][src_x]; + // check if it's a do nothing + if (a->x == dest_x && a->y == dest_y) + return; + if (undo_mode != STBTE__undo_none ) { + if (undo_mode == STBTE__undo_block) stbte__begin_undo(tm); + stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y); + if (undo_mode == STBTE__undo_block) stbte__end_undo(tm); + } + // check if there's an existing link + if (a->x >= 0) { + // decrement existing link refcount + STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0); + --tm->linkcount[a->y][a->x]; + } + // increment new dest + if (dest_x >= 0) { + ++tm->linkcount[dest_y][dest_x]; + } + a->x = dest_x; + a->y = dest_y; +} +#endif + + +static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color) +{ + STBTE_DRAW_RECT(x0,y0,x1,y1, color); +} + +static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color) +{ + int temp; + if (x1 < x0) temp=x0,x0=x1,x1=temp; + if (y1 < y0) temp=y0,y0=y1,y1=temp; + stbte__draw_rect(x0,y0,x1+1,y1+1,color); +} + +static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color) +{ + stbte__draw_line(x0,y0,x0,y1, color); + stbte__draw_line(x0,y1,x1,y1, color); +} + +static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color) +{ + stbte__draw_rect(x0,y0,x1-1,y0+1,color); + stbte__draw_rect(x1-1,y0,x1,y1-1,color); + stbte__draw_rect(x0+1,y1-1,x1,y1,color); + stbte__draw_rect(x0,y0+1,x0+1,y1,color); +} + +static void stbte__draw_halfframe(int x0, int y0, int x1, int y1, unsigned int color) +{ + stbte__draw_rect(x0,y0,x1,y0+1,color); + stbte__draw_rect(x0,y0+1,x0+1,y1,color); +} + +static int stbte__get_char_width(int ch) +{ + return stbte__fontdata[ch-16]; +} + +static short *stbte__get_char_bitmap(int ch) +{ + return stbte__fontdata + stbte__font_offset[ch-16]; +} + +static void stbte__draw_bitmask_as_columns(int x, int y, short bitmask, int color) +{ + int start_i = -1, i=0; + while (bitmask) { + if (bitmask & (1<<i)) { + if (start_i < 0) + start_i = i; + } else if (start_i >= 0) { + stbte__draw_rect(x, y+start_i, x+1, y+i, color); + start_i = -1; + bitmask &= ~((1<<i)-1); // clear all the old bits; we don't clear them as we go to save code + } + ++i; + } +} + +static void stbte__draw_bitmap(int x, int y, int w, short *bitmap, int color) +{ + int i; + for (i=0; i < w; ++i) + stbte__draw_bitmask_as_columns(x+i, y, *bitmap++, color); +} + +static void stbte__draw_text_core(int x, int y, const char *str, int w, int color, int digitspace) +{ + int x_end = x+w; + while (*str) { + int c = *str++; + int cw = stbte__get_char_width(c); + if (x + cw > x_end) + break; + stbte__draw_bitmap(x, y, cw, stbte__get_char_bitmap(c), color); + if (digitspace && c == ' ') + cw = stbte__get_char_width('0'); + x += cw+1; + } +} + +static void stbte__draw_text(int x, int y, const char *str, int w, int color) +{ + stbte__draw_text_core(x,y,str,w,color,0); +} + +static int stbte__text_width(const char *str) +{ + int x = 0; + while (*str) { + int c = *str++; + int cw = stbte__get_char_width(c); + x += cw+1; + } + return x; +} + +static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color) +{ + if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) { + stbte__colorrect r = { x0,y0,x1,y1,color }; + stbte__ui.delayrect[stbte__ui.delaycount++] = r; + } +} + +static void stbte__flush_delay(void) +{ + stbte__colorrect *r; + int i; + r = stbte__ui.delayrect; + for (i=0; i < stbte__ui.delaycount; ++i,++r) + stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color); + stbte__ui.delaycount = 0; +} + +static void stbte__activate(int id) +{ + stbte__ui.active_id = id; + stbte__ui.active_event = stbte__ui.event; + stbte__ui.accum_x = 0; + stbte__ui.accum_y = 0; +} + +static int stbte__hittest(int x0, int y0, int x1, int y1, int id) +{ + int over = stbte__ui.mx >= x0 && stbte__ui.my >= y0 + && stbte__ui.mx < x1 && stbte__ui.my < y1; + + if (over && stbte__ui.event >= STBTE__tick) + stbte__ui.next_hot_id = id; + + return over; +} + +static int stbte__button_core(int id) +{ + switch (stbte__ui.event) { + case STBTE__leftdown: + if (stbte__ui.hot_id == id && STBTE__INACTIVE()) + stbte__activate(id); + break; + case STBTE__leftup: + if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) { + stbte__activate(0); + return 1; + } + break; + case STBTE__rightdown: + if (stbte__ui.hot_id == id && STBTE__INACTIVE()) + stbte__activate(id); + break; + case STBTE__rightup: + if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) { + stbte__activate(0); + return -1; + } + break; + } + return 0; +} + +static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex) +{ + stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base ][colorindex]); + stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]); +} + +static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex) +{ + stbte__draw_box(x0,y0,x1,y1,colormode,colorindex); + stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]); +} + +static int stbte__button(int colormode, char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled) +{ + int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; + int s = STBTE__BUTTON_INTERNAL_SPACING; + + int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); + + if (stbte__ui.event == STBTE__paint) + stbte__draw_textbox(x0,y0,x1,y1, label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); + if (disabled) + return 0; + return (stbte__button_core(id) == 1); +} + +static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled, int disabled) +{ + int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; + int s = STBTE__BUTTON_INTERNAL_SPACING; + + int over = stbte__hittest(x0,y0,x1,y1,id); + + if (stbte__ui.event == STBTE__paint) { + char label[2] = { ch, 0 }; + int pad = (9 - stbte__get_char_width(ch))/2; + stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); + } + if (disabled) + return 0; + return (stbte__button_core(id) == 1); +} + +static int stbte__minibutton(int colormode, int x, int y, int ch, int id) +{ + int x0 = x, y0 = y, x1 = x+8, y1 = y+7; + int over = stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) { + char str[2] = { ch,0 }; + stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0)); + } + return stbte__button_core(id); +} + +static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode) +{ + int x0 = x, y0 = y, x1 = x+10, y1 = y+11; + int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) { + char str[2] = { ch,0 }; + int off = (9-stbte__get_char_width(ch))/2; + stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); + } + if (disabled) + return 0; + return stbte__button_core(id); +} + +static int stbte__microbutton(int x, int y, int size, int id, int colormode) +{ + int x0 = x, y0 = y, x1 = x+size, y1 = y+size; + int over = stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) { + stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0)); + } + return stbte__button_core(id); +} + +static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos) +{ + int x0 = x, y0 = y, x1 = x+size, y1 = y+size; + int over = stbte__hittest(x0,y0,x1,y1,id); + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0)); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__ui.sx = stbte__ui.mx - *pos; + } + break; + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id) && stbte__ui.active_event == STBTE__leftdown) { + *pos = stbte__ui.mx - stbte__ui.sx; + } + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) + stbte__activate(0); + break; + default: + return stbte__button_core(id); + } + return 0; +} + +static int stbte__category_button(char *label, int x, int y, int width, int id, int toggled) +{ + int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; + int s = STBTE__BUTTON_INTERNAL_SPACING; + + int over = stbte__hittest(x0,y0,x1,y1,id); + + if (stbte__ui.event == STBTE__paint) + stbte__draw_textbox(x0,y0,x1,y1, label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled)); + + return (stbte__button_core(id) == 1); +} + +enum +{ + STBTE__none, + STBTE__begin, + STBTE__end, + STBTE__change, +}; + +// returns -1 if value changes, 1 at end of drag +static int stbte__slider(int x0, int w, int y, int range, int *value, int id) +{ + int x1 = x0+w; + int pos = *value * w / (range+1); + int over = stbte__hittest(x0,y-2,x1,y+3,id); + int event_mouse_move = STBTE__change; + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_rect(x0,y,x1,y+1, 0x808080); + stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + event_mouse_move = STBTE__begin; + } + // fall through + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id)) { + int v = (stbte__ui.mx-x0)*(range+1)/w; + if (v < 0) v = 0; else if (v > range) v = range; + *value = v; + return event_mouse_move; + } + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + return STBTE__end; + } + break; + } + return STBTE__none; +} + +static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, char *fmt, float *value, int colormode, int id) +{ + int x1 = x0+w; + int y1 = y0+11; + int over = stbte__hittest(x0,y0,x1,y1,id); + switch (stbte__ui.event) { + case STBTE__paint: { + char text[32]; + sprintf(text, fmt ? fmt : "%6.2f", *value); + stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0)); + break; + } + case STBTE__leftdown: + case STBTE__rightdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) + stbte__activate(id); + return STBTE__begin; + break; + case STBTE__leftup: + case STBTE__rightup: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + return STBTE__end; + } + break; + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id)) { + float v = *value, delta; + int ax = stbte__ui.accum_x/STBTE_FLOAT_CONTROL_GRANULARITY; + int ay = stbte__ui.accum_y/STBTE_FLOAT_CONTROL_GRANULARITY; + stbte__ui.accum_x -= ax*STBTE_FLOAT_CONTROL_GRANULARITY; + stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY; + if (stbte__ui.shift) { + if (stbte__ui.active_event == STBTE__leftdown) + delta = ax * 16.0f + ay; + else + delta = ax / 16.0f + ay / 256.0f; + } else { + if (stbte__ui.active_event == STBTE__leftdown) + delta = ax*10.0f + ay; + else + delta = ax * 0.1f + ay * 0.01f; + } + v += delta * scale; + if (v < minv) v = minv; + if (v > maxv) v = maxv; + *value = v; + return STBTE__change; + } + break; + } + return STBTE__none; +} + +static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id) +{ + int over; + int thumbpos; + if (v1 - v0 <= num_vis) + return; + + // generate thumbpos from numvis + thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis); + if (thumbpos < y0) thumbpos = y0; + if (thumbpos >= y1) thumbpos = y1; + over = stbte__hittest(x-1,y0,x+2,y1,id); + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]); + stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0)); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + // check if it's over the thumb + stbte__activate(id); + *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0); + } + break; + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id) && stbte__ui.mx >= x-15 && stbte__ui.mx <= x+15) + *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0); + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) + stbte__activate(0); + break; + + } + + if (*val >= v1-num_vis) + *val = v1-num_vis; + if (*val <= v0) + *val = v0; +} + + +static void stbte__compute_digits(stbte_tilemap *tm) +{ + if (tm->max_x >= 1000 || tm->max_y >= 1000) + tm->digits = 4; + else if (tm->max_x >= 100 || tm->max_y >= 100) + tm->digits = 3; + else + tm->digits = 2; +} + +static int stbte__is_single_selection(void) +{ + return stbte__ui.has_selection + && stbte__ui.select_x0 == stbte__ui.select_x1 + && stbte__ui.select_y0 == stbte__ui.select_y1; +} + +typedef struct +{ + int width, height; + int x,y; + int active; + float retracted; +} stbte__region_t; + +static stbte__region_t stbte__region[4]; + +#define STBTE__TOOLBAR_ICON_SIZE (9+2*2) +#define STBTE__TOOLBAR_PASTE_SIZE (34+2*2) + +// This routine computes where every panel goes onscreen: computes +// a minimum width for each side based on which panels are on that +// side, and accounts for width-dependent layout of certain panels. +static void stbte__compute_panel_locations(stbte_tilemap *tm) +{ + int i, limit, w, k; + int window_width = stbte__ui.x1 - stbte__ui.x0; + int window_height = stbte__ui.y1 - stbte__ui.y0; + int min_width[STBTE__num_panel]={0,0,0,0,0,0,0}; + int height[STBTE__num_panel]={0,0,0,0,0,0,0}; + int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1}; + int vpos[4] = { 0,0,0,0 }; + stbte__panel *p = stbte__ui.panel; + stbte__panel *pt = &p[STBTE__panel_toolbar]; +#ifdef STBTE__NO_PROPS + int props = 0; +#else + int props = 1; +#endif + + for (i=0; i < 4; ++i) { + stbte__region[i].active = 0; + stbte__region[i].width = 0; + stbte__region[i].height = 0; + } + + // compute number of digits needs for info panel + stbte__compute_digits(tm); + + // determine which panels are active + panel_active[STBTE__panel_categories] = tm->num_categories != 0; + panel_active[STBTE__panel_layers ] = tm->num_layers > 1; +#ifdef STBTE__COLORPICKER + panel_active[STBTE__panel_colorpick ] = 1; +#endif + + panel_active[STBTE__panel_props ] = props && stbte__is_single_selection(); + + // compute minimum widths for each panel (assuming they're on sides not top) + min_width[STBTE__panel_info ] = 8 + 11 + 7*tm->digits+17+7; // estimate min width of "w:0000" + min_width[STBTE__panel_colorpick ] = 120; + min_width[STBTE__panel_tiles ] = 4 + tm->palette_spacing_x + 5; // 5 for scrollbar + min_width[STBTE__panel_categories] = 4 + 42 + 5; // 42 is enough to show ~7 chars; 5 for scrollbar + min_width[STBTE__panel_layers ] = 4 + 54 + 30*tm->has_layer_names; // 2 digits plus 3 buttons plus scrollbar + min_width[STBTE__panel_toolbar ] = 4 + STBTE__TOOLBAR_PASTE_SIZE; // wide enough for 'Paste' button + min_width[STBTE__panel_props ] = 80; // narrowest info panel + + // compute minimum widths for left & right panels based on the above + stbte__region[0].width = stbte__ui.left_width; + stbte__region[1].width = stbte__ui.right_width; + + for (i=0; i < STBTE__num_panel; ++i) { + if (panel_active[i]) { + int side = stbte__ui.panel[i].side; + if (min_width[i] > stbte__region[side].width) + stbte__region[side].width = min_width[i]; + stbte__region[side].active = 1; + } + } + + // now compute the heights of each panel + + // if toolbar at top, compute its size & push the left and right start points down + if (stbte__region[STBTE__side_top].active) { + int height = STBTE__TOOLBAR_ICON_SIZE+2; + pt->x0 = stbte__ui.x0; + pt->y0 = stbte__ui.y0; + pt->width = window_width; + pt->height = height; + vpos[STBTE__side_left] = vpos[STBTE__side_right] = height; + } else { + int num_rows = STBTE__num_tool * ((stbte__region[pt->side].width-4)/STBTE__TOOLBAR_ICON_SIZE); + height[STBTE__panel_toolbar] = num_rows*13 + 3*15 + 4; // 3*15 for cut/copy/paste, which are stacked vertically + } + + for (i=0; i < 4; ++i) + stbte__region[i].y = stbte__ui.y0 + vpos[i]; + + for (i=0; i < 2; ++i) { + int anim = (int) (stbte__region[i].width * stbte__region[i].retracted); + stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim; + } + + // color picker + height[STBTE__panel_colorpick] = 300; + + // info panel + w = stbte__region[p[STBTE__panel_info].side].width; + p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4); + if (p[STBTE__panel_info].mode) + height[STBTE__panel_info] = 5 + 11*2 + 2 + tm->palette_spacing_y; + else + height[STBTE__panel_info] = 5 + 11*4 + 2 + tm->palette_spacing_y; + + // layers + limit = 6 + stbte__ui.panel[STBTE__panel_layers].delta_height; + height[STBTE__panel_layers] = (tm->num_layers > limit ? limit : tm->num_layers)*15 + 7 + (tm->has_layer_names ? 0 : 11) + props*13; + + // categories + limit = 6 + stbte__ui.panel[STBTE__panel_categories].delta_height; + height[STBTE__panel_categories] = (tm->num_categories+1 > limit ? limit : tm->num_categories+1)*11 + 14; + if (stbte__ui.panel[STBTE__panel_categories].side == stbte__ui.panel[STBTE__panel_categories].side) + height[STBTE__panel_categories] -= 4; + + // palette + k = (stbte__region[p[STBTE__panel_tiles].side].width - 8) / tm->palette_spacing_x; + if (k == 0) k = 1; + height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8; + + // properties panel + height[STBTE__panel_props] = 9 + STBTE_MAX_PROPERTIES*14; + + // now compute the locations of all the panels + for (i=0; i < STBTE__num_panel; ++i) { + if (panel_active[i]) { + int side = p[i].side; + if (side == STBTE__side_left || side == STBTE__side_right) { + p[i].width = stbte__region[side].width; + p[i].x0 = stbte__region[side].x; + p[i].y0 = stbte__ui.y0 + vpos[side]; + p[i].height = height[i]; + vpos[side] += height[i]; + if (vpos[side] > window_height) { + vpos[side] = window_height; + p[i].height = stbte__ui.y1 - p[i].y0; + } + } else { + ; // it's at top, it's already been explicitly set up earlier + } + } else { + // inactive panel + p[i].height = 0; + p[i].width = 0; + p[i].x0 = stbte__ui.x1; + p[i].y0 = stbte__ui.y1; + } + } +} + +// unique identifiers for imgui +enum +{ + STBTE__map=1, + STBTE__region, + STBTE__panel, // panel background to hide map, and misc controls + STBTE__info, // info data + STBTE__toolbarA, STBTE__toolbarB, // toolbar buttons: param is tool number + STBTE__palette, // palette selectors: param is tile index + STBTE__categories, // category selectors: param is category index + STBTE__layer, // + STBTE__solo, STBTE__hide, STBTE__lock, // layer controls: param is layer + STBTE__scrollbar, // param is panel ID + STBTE__panel_mover, // p1 is panel ID, p2 is destination side + STBTE__panel_sizer, // param panel ID + STBTE__scrollbar_id, + STBTE__colorpick_id, + STBTE__prop_flag, + STBTE__prop_float, + STBTE__prop_int, +}; + +// id is: [ 24-bit data : 7-bit identifer ] +// map id is: [ 12-bit y : 12 bit x : 7-bit identifier ] + +#define STBTE__ID(n,p) ((n) + ((p)<<7)) +#define STBTE__ID2(n,p,q) STBTE__ID(n, ((p)<<12)+(q) ) +#define STBTE__IDMAP(x,y) STBTE__ID2(STBTE__map, x,y) + +static void stbte__activate_map(int x, int y) +{ + stbte__ui.active_id = STBTE__IDMAP(x,y); + stbte__ui.active_event = stbte__ui.event; + stbte__ui.sx = x; + stbte__ui.sy = y; +} + +static void stbte__alert(const char *msg) +{ + stbte__ui.alert_msg = msg; + stbte__ui.alert_timer = 3; +} + +#define STBTE__BG(tm,layer) ((layer) == 0 ? (tm)->background_tile : STBTE__NO_TILE) + + + +static void stbte__brush_predict(stbte_tilemap *tm, short result[]) +{ + int layer_to_paint = tm->cur_layer; + stbte__tileinfo *ti; + int i; + + if (tm->cur_tile < 0) return; + + ti = &tm->tiles[tm->cur_tile]; + + // find lowest legit layer to paint it on, and put it there + for (i=0; i < tm->num_layers; ++i) { + // check if object is allowed on layer + if (!(ti->layermask & (1 << i))) + continue; + + if (i != tm->solo_layer) { + // if there's a selected layer, can only paint on that + if (tm->cur_layer >= 0 && i != tm->cur_layer) + continue; + + // if the layer is hidden, we can't see it + if (tm->layerinfo[i].hidden) + continue; + + // if the layer is locked, we can't write to it + if (tm->layerinfo[i].locked == STBTE__locked) + continue; + + // if the layer is non-empty and protected, can't write to it + if (tm->layerinfo[i].locked == STBTE__protected && result[i] != STBTE__BG(tm,i)) + continue; + } + + result[i] = ti->id; + return; + } +} + +static void stbte__brush(stbte_tilemap *tm, int x, int y) +{ + int layer_to_paint = tm->cur_layer; + stbte__tileinfo *ti; + + // find lowest legit layer to paint it on, and put it there + int i; + + if (tm->cur_tile < 0) return; + + ti = &tm->tiles[tm->cur_tile]; + + for (i=0; i < tm->num_layers; ++i) { + // check if object is allowed on layer + if (!(ti->layermask & (1 << i))) + continue; + + if (i != tm->solo_layer) { + // if there's a selected layer, can only paint on that + if (tm->cur_layer >= 0 && i != tm->cur_layer) + continue; + + // if the layer is hidden, we can't see it + if (tm->layerinfo[i].hidden) + continue; + + // if the layer is locked, we can't write to it + if (tm->layerinfo[i].locked == STBTE__locked) + continue; + + // if the layer is non-empty and protected, can't write to it + if (tm->layerinfo[i].locked == STBTE__protected && tm->data[y][x][i] != STBTE__BG(tm,i)) + continue; + } + + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = ti->id; + return; + } + + //stbte__alert("Selected tile not valid on active layer(s)"); +} + +enum +{ + STBTE__erase_none = -1, + STBTE__erase_brushonly = 0, + STBTE__erase_any = 1, + STBTE__erase_all = 2, +}; + +static int stbte__erase_predict(stbte_tilemap *tm, short result[], int allow_any) +{ + stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL; + int i; + + if (allow_any == STBTE__erase_none) + return allow_any; + + // first check if only one layer is legit + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + + // if only one layer is legit, directly process that one for clarity + if (i >= 0) { + short bg = (i == 0 ? tm->background_tile : -1); + if (tm->solo_layer < 0) { + // check that we're allowed to write to it + if (tm->layerinfo[i].hidden) return STBTE__erase_none; + if (tm->layerinfo[i].locked) return STBTE__erase_none; + } + if (result[i] == bg) + return STBTE__erase_none; // didn't erase anything + if (ti && result[i] == ti->id && (i != 0 || ti->id != tm->background_tile)) { + result[i] = bg; + return STBTE__erase_brushonly; + } + if (allow_any == STBTE__erase_any) { + result[i] = bg; + return STBTE__erase_any; + } + return STBTE__erase_none; + } + + // if multiple layers are legit, first scan all for brush data + + if (ti && allow_any != STBTE__erase_all) { + for (i=tm->num_layers-1; i >= 0; --i) { + if (result[i] != ti->id) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && result[i] == tm->background_tile) + return STBTE__erase_none; + result[i] = STBTE__BG(tm,i); + return STBTE__erase_brushonly; + } + } + + if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all) + return STBTE__erase_none; + + // apply layer filters, erase from top + for (i=tm->num_layers-1; i >= 0; --i) { + if (result[i] < 0) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && result[i] == tm->background_tile) + return STBTE__erase_none; + result[i] = STBTE__BG(tm,i); + if (allow_any != STBTE__erase_all) + return STBTE__erase_any; + } + + if (allow_any == STBTE__erase_all) + return allow_any; + return STBTE__erase_none; +} + +static int stbte__erase(stbte_tilemap *tm, int x, int y, int allow_any) +{ + stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL; + int i; + + if (allow_any == STBTE__erase_none) + return allow_any; + + // first check if only one layer is legit + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + + // if only one layer is legit, directly process that one for clarity + if (i >= 0) { + short bg = (i == 0 ? tm->background_tile : -1); + if (tm->solo_layer < 0) { + // check that we're allowed to write to it + if (tm->layerinfo[i].hidden) return STBTE__erase_none; + if (tm->layerinfo[i].locked) return STBTE__erase_none; + } + if (tm->data[y][x][i] == bg) + return -1; // didn't erase anything + if (ti && tm->data[y][x][i] == ti->id && (i != 0 || ti->id != tm->background_tile)) { + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = bg; + return STBTE__erase_brushonly; + } + if (allow_any == STBTE__erase_any) { + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = bg; + return STBTE__erase_any; + } + return STBTE__erase_none; + } + + // if multiple layers are legit, first scan all for brush data + + if (ti && allow_any != STBTE__erase_all) { + for (i=tm->num_layers-1; i >= 0; --i) { + if (tm->data[y][x][i] != ti->id) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && tm->data[y][x][i] == tm->background_tile) + return STBTE__erase_none; + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = STBTE__BG(tm,i); + return STBTE__erase_brushonly; + } + } + + if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all) + return STBTE__erase_none; + + // apply layer filters, erase from top + for (i=tm->num_layers-1; i >= 0; --i) { + if (tm->data[y][x][i] < 0) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && tm->data[y][x][i] == tm->background_tile) + return STBTE__erase_none; + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = STBTE__BG(tm,i); + if (allow_any != STBTE__erase_all) + return STBTE__erase_any; + } + if (allow_any == STBTE__erase_all) + return allow_any; + return STBTE__erase_none; +} + +static int stbte__find_tile(stbte_tilemap *tm, int tile_id) +{ + int i; + for (i=0; i < tm->num_tiles; ++i) + if (tm->tiles[i].id == tile_id) + return i; + stbte__alert("Eyedropped tile that isn't in tileset"); + return -1; +} + +static void stbte__eyedrop(stbte_tilemap *tm, int x, int y) +{ + int i,j; + + // flush eyedropper state + if (stbte__ui.eyedrop_x != x || stbte__ui.eyedrop_y != y) { + stbte__ui.eyedrop_x = x; + stbte__ui.eyedrop_y = y; + stbte__ui.eyedrop_last_layer = tm->num_layers; + } + + // if only one layer is active, query that + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + if (i >= 0) { + if (tm->data[y][x][i] == STBTE__NO_TILE) + return; + tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]); + return; + } + + // if multiple layers, continue from previous + i = stbte__ui.eyedrop_last_layer; + for (j=0; j < tm->num_layers; ++j) { + if (--i < 0) + i = tm->num_layers-1; + if (tm->layerinfo[i].hidden) + continue; + if (tm->data[y][x][i] == STBTE__NO_TILE) + continue; + stbte__ui.eyedrop_last_layer = i; + tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]); + return; + } +} + +static int stbte__should_copy_properties(stbte_tilemap *tm) +{ + int i; + if (tm->propmode == STBTE__propmode_always) + return 1; + if (tm->propmode == STBTE__propmode_never) + return 0; + if (tm->solo_layer >= 0 || tm->cur_layer >= 0) + return 0; + for (i=0; i < tm->num_layers; ++i) + if (tm->layerinfo[i].hidden || tm->layerinfo[i].locked) + return 0; + return 1; +} + +// compute the result of pasting into a tile non-destructively so we can preview it +static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], short src[], int dragging) +{ + int i; + + // special case single-layer + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + if (i >= 0) { + if (tm->solo_layer < 0) { + // check that we're allowed to write to it + if (tm->layerinfo[i].hidden) return; + if (tm->layerinfo[i].locked == STBTE__locked) return; + // if protected, dest has to be empty + if (tm->layerinfo[i].locked == STBTE__protected && dest[i] != STBTE__BG(tm,i)) return; + // if dragging w/o copy, we will try to erase stuff, which protection disallows + if (dragging && tm->layerinfo[i].locked == STBTE__protected) + return; + } + result[i] = dest[i]; + if (src[i] != STBTE__BG(tm,i)) + result[i] = src[i]; + return; + } + + for (i=0; i < tm->num_layers; ++i) { + result[i] = dest[i]; + if (src[i] != STBTE__NO_TILE) + if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked) + if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i))) + result[i] = src[i]; + } +} + +// compute the result of dragging away from a tile +static void stbte__clear_stack(stbte_tilemap *tm, short result[]) +{ + int i; + // special case single-layer + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + if (i >= 0) + result[i] = STBTE__BG(tm,i); + else + for (i=0; i < tm->num_layers; ++i) + if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked) + result[i] = STBTE__BG(tm,i); +} + +// check if some map square is active +#define STBTE__IS_MAP_ACTIVE() ((stbte__ui.active_id & 127) == STBTE__map) +#define STBTE__IS_MAP_HOT() ((stbte__ui.hot_id & 127) == STBTE__map) + +static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill) +{ + int i,j; + int x=x0,y=y0; + + stbte__begin_undo(tm); + if (x0 > x1) i=x0,x0=x1,x1=i; + if (y0 > y1) j=y0,y0=y1,y1=j; + for (j=y0; j <= y1; ++j) + for (i=x0; i <= x1; ++i) + if (fill) + stbte__brush(tm, i,j); + else + stbte__erase(tm, i,j,STBTE__erase_any); + stbte__end_undo(tm); + // suppress warning from brush + stbte__ui.alert_msg = 0; +} + +static void stbte__select_rect(stbte_tilemap *tm, int x0, int y0, int x1, int y1) +{ + stbte__ui.has_selection = 1; + stbte__ui.select_x0 = (x0 < x1 ? x0 : x1); + stbte__ui.select_x1 = (x0 < x1 ? x1 : x0); + stbte__ui.select_y0 = (y0 < y1 ? y0 : y1); + stbte__ui.select_y1 = (y0 < y1 ? y1 : y0); +} + +static void stbte__copy_properties(float *dest, float *src) +{ + int i; + for (i=0; i < STBTE_MAX_PROPERTIES; ++i) + dest[i] = src[i]; +} + +static void stbte__copy_cut(stbte_tilemap *tm, int cut) +{ + int i,j,n,w,h,p=0; + int copy_props = stbte__should_copy_properties(tm); + if (!stbte__ui.has_selection) + return; + w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1; + h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1; + if (STBTE_MAX_COPY / w < h) { + stbte__alert("Selection too large for copy buffer, increase STBTE_MAX_COPY"); + return; + } + + for (i=0; i < w*h; ++i) + for (n=0; n < tm->num_layers; ++n) + stbte__ui.copybuffer[i][n] = STBTE__NO_TILE; + + if (cut) + stbte__begin_undo(tm); + for (j=stbte__ui.select_y0; j <= stbte__ui.select_y1; ++j) { + for (i=stbte__ui.select_x0; i <= stbte__ui.select_x1; ++i) { + for (n=0; n < tm->num_layers; ++n) { + if (tm->solo_layer >= 0) { + if (tm->solo_layer != n) + continue; + } else { + if (tm->cur_layer >= 0) + if (tm->cur_layer != n) + continue; + if (tm->layerinfo[n].hidden) + continue; + if (cut && tm->layerinfo[n].locked) + continue; + } + stbte__ui.copybuffer[p][n] = tm->data[j][i][n]; + if (cut) { + stbte__undo_record(tm,i,j,n, tm->data[j][i][n]); + tm->data[j][i][n] = (n==0 ? tm->background_tile : -1); + } + } + if (copy_props) { + stbte__copy_properties(stbte__ui.copyprops[p], tm->props[j][i]); +#ifdef STBTE_ALLOW_LINK + stbte__ui.copylinks[p] = tm->link[j][i]; + if (cut) + stbte__set_link(tm, i,j,-1,-1, STBTE__undo_record); +#endif + } + ++p; + } + } + if (cut) + stbte__end_undo(tm); + stbte__ui.copy_width = w; + stbte__ui.copy_height = h; + stbte__ui.has_copy = 1; + //stbte__ui.has_selection = 0; + stbte__ui.copy_has_props = copy_props; + stbte__ui.copy_src = tm; // used to give better semantics when copying links + stbte__ui.copy_src_x = stbte__ui.select_x0; + stbte__ui.copy_src_y = stbte__ui.select_y0; +} + +static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h) +{ + return x >= x0 && x < x0+w && y >= y0 && y < y0+h; +} + +static int stbte__in_src_rect(int x, int y) +{ + return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height); +} + +static int stbte__in_dest_rect(int x, int y, int destx, int desty) +{ + return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height); +} + +static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy) +{ + int w = stbte__ui.copy_width; + int h = stbte__ui.copy_height; + int i,j,k,p; + int x = mapx - (w>>1); + int y = mapy - (h>>1); + int copy_props = stbte__should_copy_properties(tm) && stbte__ui.copy_has_props; + if (stbte__ui.has_copy == 0) + return; + stbte__begin_undo(tm); + p = 0; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (y+j >= 0 && y+j < tm->max_y && x+i >= 0 && x+i < tm->max_x) { + // compute the new stack + short tilestack[STBTE_MAX_LAYERS]; + for (k=0; k < tm->num_layers; ++k) + tilestack[k] = tm->data[y+j][x+i][k]; + stbte__paste_stack(tm, tilestack, tilestack, stbte__ui.copybuffer[p], 0); + // update anything that changed + for (k=0; k < tm->num_layers; ++k) { + if (tilestack[k] != tm->data[y+j][x+i][k]) { + stbte__undo_record(tm, x+i,y+j,k, tm->data[y+j][x+i][k]); + tm->data[y+j][x+i][k] = tilestack[k]; + } + } + } + if (copy_props) { +#ifdef STBTE_ALLOW_LINK + // need to decide how to paste a link, so there's a few cases + int destx = -1, desty = -1; + stbte__link *link = &stbte__ui.copylinks[p]; + + // check if link is within-rect + if (stbte__in_src_rect(link->x, link->y)) { + // new link should point to copy (but only if copy is within map) + destx = x + (link->x - stbte__ui.copy_src_x); + desty = y + (link->y - stbte__ui.copy_src_y); + } else if (tm == stbte__ui.copy_src) { + // if same map, then preserve link unless target is overwritten + if (!stbte__in_dest_rect(link->x,link->y,x,y)) { + destx = link->x; + desty = link->y; + } + } + // this is necessary for offset-copy, but also in case max_x/max_y has changed + if (destx < 0 || destx >= tm->max_x || desty < 0 || desty >= tm->max_y) + destx = -1, desty = -1; + stbte__set_link(tm, x+i, y+j, destx, desty, STBTE__undo_record); +#endif + for (k=0; k < STBTE_MAX_PROPERTIES; ++k) { + if (tm->props[y+j][x+i][k] != stbte__ui.copyprops[p][k]) + stbte__undo_record_prop_float(tm, x+i, y+j, k, tm->props[y+j][x+i][k]); + } + stbte__copy_properties(tm->props[y+j][x+i], stbte__ui.copyprops[p]); + } + ++p; + } + } + stbte__end_undo(tm); +} + +static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_props) +{ + int w = stbte__ui.drag_w, h = stbte__ui.drag_h; + int ox,oy,i,deleted=0,written=0; + short temp[STBTE_MAX_LAYERS]; + short *data = NULL; + if (!stbte__ui.shift) { + ox = mapx - stbte__ui.drag_x; + oy = mapy - stbte__ui.drag_y; + if (ox >= 0 && ox < w && oy >= 0 && oy < h) { + deleted=1; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + stbte__clear_stack(tm, data); + } + } + ox = mapx - stbte__ui.drag_dest_x; + oy = mapy - stbte__ui.drag_dest_y; + // if this map square is in the target drag region + if (ox >= 0 && ox < w && oy >= 0 && oy < h) { + // and the src map square is on the map + if (stbte__in_rect(stbte__ui.drag_x+ox, stbte__ui.drag_y+oy, 0, 0, tm->max_x, tm->max_y)) { + written = 1; + if (data == NULL) { + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + } + stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift); + if (copy_props) { + for (i=0; i < STBTE_MAX_PROPERTIES; ++i) { + if (tm->props[mapy][mapx][i] != tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]) { + stbte__undo_record_prop_float(tm, mapx, mapy, i, tm->props[mapy][mapx][i]); + tm->props[mapy][mapx][i] = tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]; + } + } + } + } + } + if (data) { + for (i=0; i < tm->num_layers; ++i) { + if (tm->data[mapy][mapx][i] != data[i]) { + stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]); + tm->data[mapy][mapx][i] = data[i]; + } + } + } + #ifdef STBTE_ALLOW_LINK + if (copy_props) { + int overwritten=0, moved=0, copied=0; + // since this function is called on EVERY tile, we can fix up even tiles not + // involved in the move + + stbte__link *k; + // first, determine what src link ends up here + k = &tm->link[mapy][mapx]; // by default, it's the one currently here + if (deleted) // if dragged away, it's erased + k = NULL; + if (written) // if dragged into, it gets that link + k = &tm->link[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox]; + + // now check whether the *target* gets moved or overwritten + if (k && k->x >= 0) { + overwritten = stbte__in_rect(k->x, k->y, stbte__ui.drag_dest_x, stbte__ui.drag_dest_y, w, h); + if (!stbte__ui.shift) + moved = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h); + else + copied = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h); + } + + if (deleted || written || overwritten || moved || copied) { + // choose the final link value based on the above + if (k == NULL || k->x < 0) + stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record); + else if (moved || (copied && written)) { + // if we move the target, we update to point to the new target; + // or, if we copy the target and the source is part ofthe copy, then update to new target + int x = k->x + (stbte__ui.drag_dest_x - stbte__ui.drag_x); + int y = k->y + (stbte__ui.drag_dest_y - stbte__ui.drag_y); + if (!(x >= 0 && y >= 0 && x < tm->max_x && y < tm->max_y)) + x = -1, y = -1; + stbte__set_link(tm, mapx, mapy, x, y, STBTE__undo_record); + } else if (overwritten) { + stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record); + } else + stbte__set_link(tm, mapx, mapy, k->x, k->y, STBTE__undo_record); + } + } + #endif +} + +static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy) +{ + int i,j; + int copy_props = stbte__should_copy_properties(tm); + int move_x = (stbte__ui.drag_dest_x - stbte__ui.drag_x); + int move_y = (stbte__ui.drag_dest_y - stbte__ui.drag_y); + if (move_x == 0 && move_y == 0) + return; + + stbte__begin_undo(tm); + // we now need a 2D memmove-style mover that doesn't + // overwrite any data as it goes. this requires being + // direction sensitive in the same way as memmove + if (move_y > 0 || (move_y == 0 && move_x > 0)) { + for (j=tm->max_y-1; j >= 0; --j) + for (i=tm->max_x-1; i >= 0; --i) + stbte__drag_update(tm,i,j,copy_props); + } else { + for (j=0; j < tm->max_y; ++j) + for (i=0; i < tm->max_x; ++i) + stbte__drag_update(tm,i,j,copy_props); + } + stbte__end_undo(tm); + + stbte__ui.has_selection = 1; + stbte__ui.select_x0 = stbte__ui.drag_dest_x; + stbte__ui.select_y0 = stbte__ui.drag_dest_y; + stbte__ui.select_x1 = stbte__ui.select_x0 + stbte__ui.drag_w - 1; + stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h - 1; +} + +static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy, int layer) +{ + int i; + int id = STBTE__IDMAP(mapx,mapy); + int x0=sx, y0=sy; + int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y; + int over = stbte__hittest(x0,y0,x1,y1, id); + short *data = tm->data[mapy][mapx]; + short temp[STBTE_MAX_LAYERS]; + + if (STBTE__IS_MAP_HOT()) { + if (stbte__ui.pasting) { + int ox = mapx - stbte__ui.paste_x; + int oy = mapy - stbte__ui.paste_y; + if (ox >= 0 && ox < stbte__ui.copy_width && oy >= 0 && oy < stbte__ui.copy_height) { + stbte__paste_stack(tm, temp, tm->data[mapy][mapx], stbte__ui.copybuffer[oy*stbte__ui.copy_width+ox], 0); + data = temp; + } + } else if (stbte__ui.dragging) { + int ox,oy; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + + // if it's in the source area, remove things unless shift-dragging + ox = mapx - stbte__ui.drag_x; + oy = mapy - stbte__ui.drag_y; + if (!stbte__ui.shift && ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) { + stbte__clear_stack(tm, temp); + } + + ox = mapx - stbte__ui.drag_dest_x; + oy = mapy - stbte__ui.drag_dest_y; + if (ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) { + stbte__paste_stack(tm, temp, temp, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift); + } + } else if (STBTE__IS_MAP_ACTIVE()) { + if (stbte__ui.tool == STBTE__tool_rect) { + if ((stbte__ui.ms_time & 511) < 380) { + int ex = ((stbte__ui.hot_id >> 19) & 4095); + int ey = ((stbte__ui.hot_id >> 7) & 4095); + int sx = stbte__ui.sx; + int sy = stbte__ui.sy; + + if ( ((mapx >= sx && mapx < ex+1) || (mapx >= ex && mapx < sx+1)) + && ((mapy >= sy && mapy < ey+1) || (mapy >= ey && mapy < sy+1))) { + int i; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + if (stbte__ui.active_event == STBTE__leftdown) + stbte__brush_predict(tm, temp); + else + stbte__erase_predict(tm, temp, STBTE__erase_any); + } + } + } + } + } + + if (STBTE__IS_HOT(id) && STBTE__INACTIVE() && !stbte__ui.pasting) { + if (stbte__ui.tool == STBTE__tool_brush) { + if ((stbte__ui.ms_time & 511) < 300) { + data = temp; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + stbte__brush_predict(tm, temp); + } + } + } + + { + i = layer; + if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0)) + if (data[i] >= 0) + STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0, tm->props[mapy][mapx]); + } +} + +static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) +{ + int tool = stbte__ui.tool; + int x0=sx, y0=sy; + int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y; + int id = STBTE__IDMAP(mapx,mapy); + int over = stbte__hittest(x0,y0,x1,y1, id); + switch (stbte__ui.event) { + case STBTE__paint: { + if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling) + break; + if (stbte__ui.scrollkey && !STBTE__IS_MAP_ACTIVE()) + break; + if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE() && (tool == STBTE__tool_rect || tool == STBTE__tool_select)) { + int rx0,ry0,rx1,ry1,t; + // compute the center of each rect + rx0 = x0 + tm->spacing_x/2; + ry0 = y0 + tm->spacing_y/2; + rx1 = rx0 + (stbte__ui.sx - mapx) * tm->spacing_x; + ry1 = ry0 + (stbte__ui.sy - mapy) * tm->spacing_y; + if (rx0 > rx1) t=rx0,rx0=rx1,rx1=t; + if (ry0 > ry1) t=ry0,ry0=ry1,ry1=t; + rx0 -= tm->spacing_x/2; + ry0 -= tm->spacing_y/2; + rx1 += tm->spacing_x/2; + ry1 += tm->spacing_y/2; + stbte__draw_frame(rx0-1,ry0-1,rx1+1,ry1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT); + break; + } + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__draw_frame(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT); + } +#ifdef STBTE_ALLOW_LINK + if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) { + int tx = tm->link[mapy][mapx].x; + int ty = tm->link[mapy][mapx].y; + int lx0,ly0,lx1,ly1; + if (STBTE_ALLOW_LINK(tm->data[mapy][mapx], tm->props[mapy][mapx], + tm->data[ty ][tx ], tm->props[ty ][tx ])) + { + lx0 = x0 + (tm->spacing_x >> 1) - 1; + ly0 = y0 + (tm->spacing_y >> 1) - 1; + lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2; + ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2; + stbte__draw_link(lx0,ly0,lx1,ly1, + STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx], + tm->data[ty ][tx ], tm->props[ty ][tx])); + } + } +#endif + break; + } + } + + if (stbte__ui.pasting) { + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__IS_HOT(id)) { + stbte__ui.pasting = 0; + stbte__paste(tm, mapx, mapy); + stbte__activate(0); + } + break; + case STBTE__leftup: + // just clear it no matter what, since they might click away to clear it + stbte__activate(0); + break; + case STBTE__rightdown: + if (STBTE__IS_HOT(id)) { + stbte__activate(0); + stbte__ui.pasting = 0; + } + break; + } + return; + } + + if (stbte__ui.scrolling) { + if (stbte__ui.event == STBTE__leftup) { + stbte__activate(0); + stbte__ui.scrolling = 0; + } + if (stbte__ui.event == STBTE__mousemove) { + tm->scroll_x += (stbte__ui.start_x - stbte__ui.mx); + tm->scroll_y += (stbte__ui.start_y - stbte__ui.my); + stbte__ui.start_x = stbte__ui.mx; + stbte__ui.start_y = stbte__ui.my; + } + return; + } + + // regardless of tool, leftdown is a scrolldrag + if (STBTE__IS_HOT(id) && stbte__ui.scrollkey && stbte__ui.event == STBTE__leftdown) { + stbte__ui.scrolling = 1; + stbte__ui.start_x = stbte__ui.mx; + stbte__ui.start_y = stbte__ui.my; + return; + } + + switch (tool) { + case STBTE__tool_brush: + switch (stbte__ui.event) { + case STBTE__mousemove: + if (STBTE__IS_MAP_ACTIVE() && over) { + // don't brush/erase same tile multiple times unless they move away and back @TODO should just be only once, but that needs another data structure + if (!STBTE__IS_ACTIVE(id)) { + if (stbte__ui.active_event == STBTE__leftdown) + stbte__brush(tm, mapx, mapy); + else + stbte__erase(tm, mapx, mapy, stbte__ui.brush_state); + stbte__ui.active_id = id; // switch to this map square so we don't rebrush IT multiple times + } + } + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__begin_undo(tm); + stbte__brush(tm, mapx, mapy); + } + break; + case STBTE__rightdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__begin_undo(tm); + if (stbte__erase(tm, mapx, mapy, STBTE__erase_any) == STBTE__erase_brushonly) + stbte__ui.brush_state = STBTE__erase_brushonly; + else + stbte__ui.brush_state = STBTE__erase_any; + } + break; + case STBTE__leftup: + case STBTE__rightup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__end_undo(tm); + stbte__activate(0); + } + break; + } + break; + +#ifdef STBTE_ALLOW_LINK + case STBTE__tool_link: + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__ui.linking = 1; + stbte__ui.sx = mapx; + stbte__ui.sy = mapy; + // @TODO: undo + } + break; + case STBTE__leftup: + if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) { + if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) && + STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], + tm->data[mapy][mapx], tm->props[mapy][mapx])) + stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, STBTE__undo_block); + else + stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1, STBTE__undo_block); + stbte__ui.linking = 0; + stbte__activate(0); + } + break; + + case STBTE__rightdown: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + stbte__ui.linking = 0; + } + break; + } + break; +#endif + + case STBTE__tool_erase: + switch (stbte__ui.event) { + case STBTE__mousemove: + if (STBTE__IS_MAP_ACTIVE() && over) + stbte__erase(tm, mapx, mapy, STBTE__erase_all); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__begin_undo(tm); + stbte__erase(tm, mapx, mapy, STBTE__erase_all); + } + break; + case STBTE__leftup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__end_undo(tm); + stbte__activate(0); + } + break; + } + break; + + case STBTE__tool_select: + if (STBTE__IS_HOT(id)) { + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__INACTIVE()) { + // if we're clicking in an existing selection... + if (stbte__ui.has_selection) { + if ( mapx >= stbte__ui.select_x0 && mapx <= stbte__ui.select_x1 + && mapy >= stbte__ui.select_y0 && mapy <= stbte__ui.select_y1) + { + stbte__ui.dragging = 1; + stbte__ui.drag_x = stbte__ui.select_x0; + stbte__ui.drag_y = stbte__ui.select_y0; + stbte__ui.drag_w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1; + stbte__ui.drag_h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1; + stbte__ui.drag_offx = mapx - stbte__ui.select_x0; + stbte__ui.drag_offy = mapy - stbte__ui.select_y0; + } + } + stbte__ui.has_selection = 0; // no selection until it completes + stbte__activate_map(mapx,mapy); + } + break; + case STBTE__leftup: + if (STBTE__IS_MAP_ACTIVE()) { + if (stbte__ui.dragging) { + stbte__drag_place(tm, mapx,mapy); + stbte__ui.dragging = 0; + stbte__activate(0); + } else { + stbte__select_rect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy); + stbte__activate(0); + } + } + break; + case STBTE__rightdown: + stbte__ui.has_selection = 0; + break; + } + } + break; + + case STBTE__tool_rect: + if (STBTE__IS_HOT(id)) { + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__INACTIVE()) + stbte__activate_map(mapx,mapy); + break; + case STBTE__leftup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 1); + stbte__activate(0); + } + break; + case STBTE__rightdown: + if (STBTE__INACTIVE()) + stbte__activate_map(mapx,mapy); + break; + case STBTE__rightup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 0); + stbte__activate(0); + } + break; + } + } + break; + + + case STBTE__tool_eyedrop: + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) + stbte__eyedrop(tm,mapx,mapy); + break; + } + break; + } +} + +static void stbte__start_paste(stbte_tilemap *tm) +{ + if (stbte__ui.has_copy) { + stbte__ui.pasting = 1; + stbte__activate(STBTE__ID(STBTE__toolbarB,3)); + } +} + +static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int i; + int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30; + int x = x0 + w/2 - estimated_width/2; + int y = y0+1; + + for (i=0; i < STBTE__num_tool; ++i) { + int highlight=0, disable=0; + highlight = (stbte__ui.tool == i); + if (i == STBTE__tool_undo || i == STBTE__tool_showgrid) + x += 8; + if (i == STBTE__tool_showgrid && stbte__ui.show_grid) + highlight = 1; + if (i == STBTE__tool_showlinks && stbte__ui.show_links) + highlight = 1; + if (i == STBTE__tool_fill) + continue; + #ifndef STBTE_ALLOW_LINK + if (i == STBTE__tool_link || i == STBTE__tool_showlinks) + disable = 1; + #endif + if (i == STBTE__tool_undo && !stbte__undo_available(tm)) + disable = 1; + if (i == STBTE__tool_redo && !stbte__redo_available(tm)) + disable = 1; + if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) { + switch (i) { + case STBTE__tool_eyedrop: + stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state + // fallthrough + default: + stbte__ui.tool = i; + stbte__ui.has_selection = 0; + break; + case STBTE__tool_showlinks: + stbte__ui.show_links = !stbte__ui.show_links; + break; + case STBTE__tool_showgrid: + stbte__ui.show_grid = (stbte__ui.show_grid+1)%3; + break; + case STBTE__tool_undo: + stbte__undo(tm); + break; + case STBTE__tool_redo: + stbte__redo(tm); + break; + } + } + x += 13; + } + + x += 8; + if (stbte__button(STBTE__ctoolbar_button, "cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection)) + stbte__copy_cut(tm, 1); + x += 42; + if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection)) + stbte__copy_cut(tm, 0); + x += 42; + if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy)) + stbte__start_paste(tm); +} + +#define STBTE__TEXTCOLOR(n) stbte__color_table[n][STBTE__text][STBTE__idle] + +static int stbte__info_value(char *label, int x, int y, int val, int digits, int id) +{ + if (stbte__ui.event == STBTE__paint) { + int off = 9-stbte__get_char_width(label[0]); + char text[16]; + sprintf(text, label, digits, val); + stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1); + } + if (id) { + x += 9+7*digits+4; + if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0))) + val += (stbte__ui.shift ? 10 : 1); + x += 9; + if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0))) + val -= (stbte__ui.shift ? 10 : 1); + if (val < 1) val = 1; else if (val > 4096) val = 4096; + } + return val; +} + +static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int mode = stbte__ui.panel[STBTE__panel_info].mode; + int s = 11+7*tm->digits+4+15; + int x,y; + int in_region; + + x = x0+2; + y = y0+2; + tm->max_x = stbte__info_value("w:%*d",x,y, tm->max_x, tm->digits, STBTE__ID(STBTE__info,0)); + if (mode) + x += s; + else + y += 11; + tm->max_y = stbte__info_value("h:%*d",x,y, tm->max_y, tm->digits, STBTE__ID(STBTE__info,1)); + x = x0+2; + y += 11; + in_region = (stbte__ui.hot_id & 127) == STBTE__map; + stbte__info_value(in_region ? "x:%*d" : "x:",x,y, (stbte__ui.hot_id>>19)&4095, tm->digits, 0); + if (mode) + x += s; + else + y += 11; + stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0); + y += 15; + x = x0+2; + stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel)); + if (tm->cur_tile >= 0) + STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0); +} + +static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + static char *propmodes[3] = { + "default", "always", "never" + }; + int num_rows; + int i, y, n; + int x1 = x0+w; + int y1 = y0+h; + int xoff = 20; + + if (tm->has_layer_names) { + int side = stbte__ui.panel[STBTE__panel_layers].side; + xoff = stbte__region[side].width - 42; + xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10); + } + + x0 += 2; + y0 += 5; + if (!tm->has_layer_names) { + if (stbte__ui.event == STBTE__paint) { + stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel)); + } + y0 += 11; + } + num_rows = (y1-y0)/15; +#ifndef STBTE_NO_PROPS + --num_rows; +#endif + y = y0; + for (i=0; i < tm->num_layers; ++i) { + char text[3], *str = (char *) tm->layerinfo[i].name; + static char lockedchar[3] = { 'U', 'P', 'L' }; + int locked = tm->layerinfo[i].locked; + int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i); + if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) { + if (str == NULL) + sprintf(str=text, "%2d", i+1); + if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0)) + tm->cur_layer = (tm->cur_layer == i ? -1 : i); + if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide)) + tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden; + if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock)) + tm->layerinfo[i].locked = (locked+1)%3; + if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo)) + tm->solo_layer = (tm->solo_layer == i ? -1 : i); + y += 15; + } + } + stbte__scrollbar(x1-4, y0,y-2, &tm->layer_scroll, 0, tm->num_layers, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__layer)); +#ifndef STBTE_NO_PROPS + n = stbte__text_width("prop:")+2; + stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel)); + i = w - n - 4; + if (i > 50) i = 50; + if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0)) + tm->propmode = (tm->propmode+1)%3; +#endif +} + +static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int s=11, x,y, i; + int num_rows = h / s; + + w -= 4; + x = x0+2; + y = y0+4; + if (tm->category_scroll == 0) { + if (stbte__category_button("*ALL*", x,y, w, STBTE__ID(STBTE__categories, 65535), tm->cur_category == -1)) { + stbte__choose_category(tm, -1); + } + y += s; + } + + for (i=0; i < tm->num_categories; ++i) { + if (i+1 - tm->category_scroll >= 0 && i+1 - tm->category_scroll < num_rows) { + if (y + 10 > y0+h) + return; + if (stbte__category_button(tm->categories[i], x,y,w, STBTE__ID(STBTE__categories,i), tm->cur_category == i)) + stbte__choose_category(tm, i); + y += s; + } + } + stbte__scrollbar(x0+w, y0+4, y0+h-4, &tm->category_scroll, 0, tm->num_categories+1, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__categories)); +} + +static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot) +{ + stbte__tileinfo *t = &tm->tiles[slot]; + int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y; + int id = STBTE__ID(STBTE__palette, slot); + int over = stbte__hittest(x0,y0,x1,y1, id); + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND); + STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile,0); + if (slot == tm->cur_tile) + stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE); + break; + default: + if (stbte__button_core(id)) + tm->cur_tile = slot; + break; + } +} + +static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int i,x,y; + int num_vis_rows = (h-6) / tm->palette_spacing_y; + int num_columns = (w-2-6) / tm->palette_spacing_x; + int num_total_rows; + int column,row; + int x1 = x0+w, y1=y0+h; + x = x0+2; + y = y0+6; + + if (num_columns == 0) + return; + + num_total_rows = (tm->cur_palette_count + num_columns-1) / num_columns; // ceil() + + column = 0; + row = -tm->palette_scroll; + for (i=0; i < tm->num_tiles; ++i) { + stbte__tileinfo *t = &tm->tiles[i]; + + // filter based on category + if (tm->cur_category >= 0 && t->category_id != tm->cur_category) + continue; + + // display it + if (row >= 0 && row < num_vis_rows) { + x = x0 + 2 + tm->palette_spacing_x * column; + y = y0 + 6 + tm->palette_spacing_y * row; + stbte__tile_in_palette(tm,x,y,i); + } + + ++column; + if (column == num_columns) { + column = 0; + ++row; + } + } + stbte__flush_delay(); + stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette)); +} + +static float stbte__linear_remap(float n, float x0, float x1, float y0, float y1) +{ + return (n-x0)/(x1-x0)*(y1-y0) + y0; +} + +static float stbte__saved; +static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int x1 = x0+w, y1 = y0+h; + int i; + int y = y0 + 5, x = x0+2; + int slider_width = 60; + int mx,my; + float *p; + short *data; + if (!stbte__is_single_selection()) + return; + mx = stbte__ui.select_x0; + my = stbte__ui.select_y0; + p = tm->props[my][mx]; + data = tm->data[my][mx]; + for (i=0; i < STBTE_MAX_PROPERTIES; ++i) { + unsigned int n = STBTE_PROP_TYPE(i, data, p); + if (n) { + char *s = STBTE_PROP_NAME(i, data, p); + if (s == NULL) s = ""; + switch (n & 3) { + case STBTE_PROP_bool: { + int flag = (int) p[i]; + if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i,(float) flag); + p[i] = (float) !flag; + stbte__end_undo(tm); + } + stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 13; + break; + } + case STBTE_PROP_int: { + int a = (int) STBTE_PROP_MIN(i,data,p); + int b = (int) STBTE_PROP_MAX(i,data,p); + int v = (int) p[i] - a; + if (a+v != p[i] || v < 0 || v > b-a) { + if (v < 0) v = 0; + if (v > b-a) v = b-a; + p[i] = (float) (a+v); // @TODO undo + } + switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i))) + { + case STBTE__begin: + stbte__saved = p[i]; + // fallthrough + case STBTE__change: + p[i] = (float) (a+v); // @TODO undo + break; + case STBTE__end: + if (p[i] != stbte__saved) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved); + stbte__end_undo(tm); + } + break; + } + stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 12; + break; + } + case STBTE_PROP_float: { + float a = (float) STBTE_PROP_MIN(i, data,p); + float b = (float) STBTE_PROP_MAX(i, data,p); + float c = STBTE_PROP_FLOAT_SCALE(i, data, p); + float old; + if (p[i] < a || p[i] > b) { + // @TODO undo + if (p[i] < a) p[i] = a; + if (p[i] > b) p[i] = b; + } + old = p[i]; + switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) { + case STBTE__begin: + stbte__saved = old; + break; + case STBTE__end: + if (stbte__saved != p[i]) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved); + stbte__end_undo(tm); + } + break; + } + stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 12; + break; + } + } + } + } +} + +static int stbte__cp_mode, stbte__cp_aspect, stbte__cp_state, stbte__cp_index, stbte__save, stbte__cp_altered, stbte__color_copy; +#ifdef STBTE__COLORPICKER +static void stbte__dump_colorstate(void) +{ + int i,j,k; + printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n"); + printf("{\n"); + printf(" {\n"); + for (k=0; k < STBTE__num_color_modes; ++k) { + for (j=0; j < STBTE__num_color_aspects; ++j) { + printf(" { "); + for (i=0; i < STBTE__num_color_states; ++i) { + printf("0x%06x, ", stbte__color_table[k][j][i]); + } + printf("},\n"); + } + if (k+1 < STBTE__num_color_modes) + printf(" }, {\n"); + else + printf(" },\n"); + } + printf("};\n"); +} + +static void stbte__colorpicker(int x0, int y0, int w, int h) +{ + int x1 = x0+w, y1 = y0+h, x,y, i; + + x = x0+2; y = y0+6; + + y += 5; + x += 8; + + + { + int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index]; + int rgb[3]; + if (stbte__cp_altered && stbte__cp_index == STBTE__idle) + color = stbte__save; + + if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0))) + stbte__color_copy = color; + if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1))) + color = stbte__color_copy; + + rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255; + for (i=0; i < 3; ++i) { + if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0) + stbte__dump_colorstate(); + y += 15; + } + if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick) + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]); + } + + y += 5; + + // states + x = x0+2+35; + if (stbte__ui.event == STBTE__paint) { + static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" }; + stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff); + } + + x = x0+24; y += 12; + + for (i=3; i >= 0; --i) { + int state = 0 != (stbte__cp_state & (1 << i)); + if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) { + stbte__cp_state ^= (1 << i); + stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state]; + } + x += 16; + } + x = x0+2; y += 18; + + for (i=0; i < 3; ++i) { + static char *labels[] = { "Base", "Edge", "Text" }; + if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0)) + stbte__cp_aspect = i; + x += 40; + } + + y += 18; + x = x0+2; + + for (i=0; i < STBTE__num_color_modes; ++i) { + if (stbte__button(STBTE__ctoolbar_button, stbte__color_names[i], x, y, 0,80, STBTE__ID2(STBTE__colorpick_id,2,i), stbte__cp_mode == i,0)) + stbte__cp_mode = i; + y += 12; + } + + // make the currently selected aspect flash, unless we're actively dragging color slider etc + if (stbte__ui.event == STBTE__tick) { + stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle]; + if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) { + if ((stbte__ui.ms_time & 2047) < 200) { + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f; + stbte__cp_altered = 1; + } + } + } +} +#endif + +static void stbte__editor_traverse(stbte_tilemap *tm) +{ + int i,j,i0,j0,i1,j1,n; + + if (tm == NULL) + return; + if (stbte__ui.x0 == stbte__ui.x1 || stbte__ui.y0 == stbte__ui.y1) + return; + + stbte__prepare_tileinfo(tm); + + stbte__compute_panel_locations(tm); // @OPTIMIZE: we don't need to recompute this every time + + if (stbte__ui.event == STBTE__paint) { + // fill screen with border + stbte__draw_rect(stbte__ui.x0, stbte__ui.y0, stbte__ui.x1, stbte__ui.y1, STBTE_COLOR_TILEMAP_BORDER); + // fill tilemap with tilemap background + stbte__draw_rect(stbte__ui.x0 - tm->scroll_x, stbte__ui.y0 - tm->scroll_y, + stbte__ui.x0 - tm->scroll_x + tm->spacing_x * tm->max_x, + stbte__ui.y0 - tm->scroll_y + tm->spacing_y * tm->max_y, STBTE_COLOR_TILEMAP_BACKGROUND); + } + + // step 1: traverse all the tilemap data... + + i0 = (tm->scroll_x - tm->spacing_x) / tm->spacing_x; + j0 = (tm->scroll_y - tm->spacing_y) / tm->spacing_y; + i1 = (tm->scroll_x + stbte__ui.x1 - stbte__ui.x0) / tm->spacing_x + 1; + j1 = (tm->scroll_y + stbte__ui.y1 - stbte__ui.y0) / tm->spacing_y + 1; + + if (i0 < 0) i0 = 0; + if (j0 < 0) j0 = 0; + if (i1 > tm->max_x) i1 = tm->max_x; + if (j1 > tm->max_y) j1 = tm->max_y; + + if (stbte__ui.event == STBTE__paint) { + // draw all of layer 0, then all of layer 1, etc, instead of old + // way which drew entire stack of each tile at once + for (n=0; n < tm->num_layers; ++n) { + for (j=j0; j < j1; ++j) { + for (i=i0; i < i1; ++i) { + int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y; + stbte__tile_paint(tm, x, y, i, j, n); + } + } + if (n == 0 && stbte__ui.show_grid == 1) { + int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y; + for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x) + stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID); + for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y) + stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID); + } + } + } + + if (stbte__ui.event == STBTE__paint) { + // draw grid on top of everything except UI + if (stbte__ui.show_grid == 2) { + int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y; + for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x) + stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID); + for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y) + stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID); + } + } + + for (j=j0; j < j1; ++j) { + for (i=i0; i < i1; ++i) { + int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y; + stbte__tile(tm, x, y, i, j); + } + } + + if (stbte__ui.event == STBTE__paint) { + // draw the selection border + if (stbte__ui.has_selection) { + int x0,y0,x1,y1; + x0 = stbte__ui.x0 + (stbte__ui.select_x0 ) * tm->spacing_x - tm->scroll_x; + y0 = stbte__ui.y0 + (stbte__ui.select_y0 ) * tm->spacing_y - tm->scroll_y; + x1 = stbte__ui.x0 + (stbte__ui.select_x1 + 1) * tm->spacing_x - tm->scroll_x + 1; + y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1; + stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2)); + } + + stbte__flush_delay(); // draw a dynamic link on top of the queued links + + #ifdef STBTE_ALLOW_LINK + if (stbte__ui.linking && STBTE__IS_MAP_HOT()) { + int x0,y0,x1,y1; + int color; + int ex = ((stbte__ui.hot_id >> 19) & 4095); + int ey = ((stbte__ui.hot_id >> 7) & 4095); + x0 = stbte__ui.x0 + (stbte__ui.sx ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1; + y0 = stbte__ui.y0 + (stbte__ui.sy ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1; + x1 = stbte__ui.x0 + (ex ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1; + y1 = stbte__ui.y0 + (ey ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1; + if (STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], tm->data[ey][ex], tm->props[ey][ex])) + color = STBTE_LINK_COLOR_DRAWING; + else + color = STBTE_LINK_COLOR_DISALLOWED; + stbte__draw_link(x0,y0,x1,y1, color); + } + #endif + } + stbte__flush_delay(); + + // step 2: traverse the panels + for (i=0; i < STBTE__num_panel; ++i) { + stbte__panel *p = &stbte__ui.panel[i]; + if (stbte__ui.event == STBTE__paint) { + stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle); + } + // obscure tilemap data underneath panel + stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0)); + switch (i) { + case STBTE__panel_toolbar: + if (stbte__ui.event == STBTE__paint) + stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]); + stbte__toolbar(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_info: + stbte__info(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_layers: + stbte__layers(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_categories: + stbte__categories(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_colorpick: +#ifdef STBTE__COLORPICKER + stbte__colorpicker(p->x0,p->y0,p->width,p->height); +#endif + break; + case STBTE__panel_tiles: + // erase boundary between categories and tiles if they're on same side + if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side) + stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, stbte__color_table[STBTE__cpanel][STBTE__base][STBTE__idle]); + stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_props: + stbte__props_panel(tm,p->x0,p->y0,p->width,p->height); + break; + } + // draw the panel side selectors + for (j=0; j < 2; ++j) { + int result; + if (i == STBTE__panel_toolbar) continue; + result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), STBTE__cpanel_sider+j); + if (result) { + switch (j) { + case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break; + case 1: p->delta_height += result; break; + } + } + } + } + + if (stbte__ui.panel[STBTE__panel_categories].delta_height < -5) stbte__ui.panel[STBTE__panel_categories].delta_height = -5; + if (stbte__ui.panel[STBTE__panel_layers ].delta_height < -5) stbte__ui.panel[STBTE__panel_layers ].delta_height = -5; + + + // step 3: traverse the regions to place expander controls on them + for (i=0; i < 2; ++i) { + if (stbte__region[i].active) { + int x = stbte__region[i].x; + int width; + if (i == STBTE__side_left) + width = stbte__ui.left_width , x += stbte__region[i].width + 1; + else + width = -stbte__ui.right_width, x -= 6; + if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) { + // if non-0, it is expanding, so retract it + if (stbte__region[i].retracted == 0.0) + stbte__region[i].retracted = 0.01f; + else + stbte__region[i].retracted = 0.0; + } + if (i == STBTE__side_left) + stbte__ui.left_width = width; + else + stbte__ui.right_width = -width; + if (stbte__ui.event == STBTE__tick) { + if (stbte__region[i].retracted && stbte__region[i].retracted < 1.0f) { + stbte__region[i].retracted += stbte__ui.dt*4; + if (stbte__region[i].retracted > 1) + stbte__region[i].retracted = 1; + } + } + } + } + + if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) { + int w = stbte__text_width(stbte__ui.alert_msg); + int x = (stbte__ui.x0+stbte__ui.x1)/2; + int y = (stbte__ui.y0+stbte__ui.y1)*5/6; + stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020); + stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030); + stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040); + } + +#ifdef STBTE_SHOW_CURSOR + if (stbte__ui.event == STBTE__paint) + stbte__draw_bitmap(stbte__ui.mx, stbte__ui.my, stbte__get_char_width(26), stbte__get_char_bitmap(26), 0xe0e0e0); +#endif + + if (stbte__ui.event == STBTE__tick && stbte__ui.alert_msg) { + stbte__ui.alert_timer -= stbte__ui.dt; + if (stbte__ui.alert_timer < 0) { + stbte__ui.alert_timer = 0; + stbte__ui.alert_msg = 0; + } + } + + if (stbte__ui.event == STBTE__paint) { + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save; + stbte__cp_altered = 0; + } +} + +static void stbte__do_event(stbte_tilemap *tm) +{ + stbte__ui.next_hot_id = 0; + stbte__editor_traverse(tm); + stbte__ui.hot_id = stbte__ui.next_hot_id; + + // automatically cancel on mouse-up in case the object that triggered it + // doesn't exist anymore + if (stbte__ui.active_id) { + if (stbte__ui.event == STBTE__leftup || stbte__ui.event == STBTE__rightup) { + if (!stbte__ui.pasting) { + stbte__activate(0); + if (stbte__ui.undoing) + stbte__end_undo(tm); + stbte__ui.scrolling = 0; + stbte__ui.dragging = 0; + stbte__ui.linking = 0; + } + } + } + + // we could do this stuff in the widgets directly, but it would keep recomputing + // the same thing on every tile, which seems dumb. + + if (stbte__ui.pasting) { + if (STBTE__IS_MAP_HOT()) { + // compute pasting location based on last hot + stbte__ui.paste_x = ((stbte__ui.hot_id >> 19) & 4095) - (stbte__ui.copy_width >> 1); + stbte__ui.paste_y = ((stbte__ui.hot_id >> 7) & 4095) - (stbte__ui.copy_height >> 1); + } + } + if (stbte__ui.dragging) { + if (STBTE__IS_MAP_HOT()) { + stbte__ui.drag_dest_x = ((stbte__ui.hot_id >> 19) & 4095) - stbte__ui.drag_offx; + stbte__ui.drag_dest_y = ((stbte__ui.hot_id >> 7) & 4095) - stbte__ui.drag_offy; + } + } +} + +static void stbte__set_event(int event, int x, int y) +{ + stbte__ui.event = event; + stbte__ui.mx = x; + stbte__ui.my = y; + stbte__ui.dx = x - stbte__ui.last_mouse_x; + stbte__ui.dy = y - stbte__ui.last_mouse_y; + stbte__ui.last_mouse_x = x; + stbte__ui.last_mouse_y = y; + stbte__ui.accum_x += stbte__ui.dx; + stbte__ui.accum_y += stbte__ui.dy; +} + +void stbte_draw(stbte_tilemap *tm) +{ + stbte__ui.event = STBTE__paint; + stbte__editor_traverse(tm); +} + +void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey) +{ + stbte__set_event(STBTE__mousemove, x,y); + stbte__ui.shift = shifted; + stbte__ui.scrollkey = scrollkey; + stbte__do_event(tm); +} + +void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey) +{ + static int events[2][2] = { { STBTE__leftup , STBTE__leftdown }, + { STBTE__rightup, STBTE__rightdown } }; + stbte__set_event(events[right][down], x,y); + stbte__ui.shift = shifted; + stbte__ui.scrollkey = scrollkey; + + stbte__do_event(tm); +} + +void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll) +{ + // not implemented yet -- need different way of hittesting +} + +void stbte_action(stbte_tilemap *tm, enum stbte_action act) +{ + switch (act) { + case STBTE_tool_select: stbte__ui.tool = STBTE__tool_select; break; + case STBTE_tool_brush: stbte__ui.tool = STBTE__tool_brush; break; + case STBTE_tool_erase: stbte__ui.tool = STBTE__tool_erase; break; + case STBTE_tool_rectangle: stbte__ui.tool = STBTE__tool_rect; break; + case STBTE_tool_eyedropper: stbte__ui.tool = STBTE__tool_eyedrop; break; + case STBTE_tool_link: stbte__ui.tool = STBTE__tool_link; break; + case STBTE_act_toggle_grid: stbte__ui.show_grid = (stbte__ui.show_grid+1) % 3; break; + case STBTE_act_toggle_links: stbte__ui.show_links ^= 1; break; + case STBTE_act_undo: stbte__undo(tm); break; + case STBTE_act_redo: stbte__redo(tm); break; + case STBTE_act_cut: stbte__copy_cut(tm, 1); break; + case STBTE_act_copy: stbte__copy_cut(tm, 0); break; + case STBTE_act_paste: stbte__start_paste(tm); break; + case STBTE_scroll_left: tm->scroll_x -= tm->spacing_x; break; + case STBTE_scroll_right: tm->scroll_x += tm->spacing_x; break; + case STBTE_scroll_up: tm->scroll_y -= tm->spacing_y; break; + case STBTE_scroll_down: tm->scroll_y += tm->spacing_y; break; + } +} + +void stbte_tick(stbte_tilemap *tm, float dt) +{ + stbte__ui.event = STBTE__tick; + stbte__ui.dt = dt; + stbte__do_event(tm); + stbte__ui.ms_time += (int) (dt * 1024) + 1; // make sure if time is superfast it always updates a little +} + +void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float ys, int xo, int yo) +{ +#ifdef _SDL_H + SDL_Event *event = (SDL_Event *) sdl_event; + SDL_Keymod km = SDL_GetModState(); + int shift = (km & KMOD_LCTRL) || (km & KMOD_RCTRL); + int scrollkey = 0 != SDL_GetKeyboardState(NULL)[SDL_SCANCODE_SPACE]; + switch (event->type) { + case SDL_MOUSEMOTION: + stbte_mouse_move(tm, (int) (xs*event->motion.x+xo), (int) (ys*event->motion.y+yo), shift, scrollkey); + break; + case SDL_MOUSEBUTTONUP: + stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 0, shift, scrollkey); + break; + case SDL_MOUSEBUTTONDOWN: + stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 1, shift, scrollkey); + break; + case SDL_MOUSEWHEEL: + stbte_mouse_wheel(tm, stbte__ui.mx, stbte__ui.my, event->wheel.y); + break; + } +#else + STBTE__NOTUSED(tm); + STBTE__NOTUSED(sdl_event); + STBTE__NOTUSED(xs); + STBTE__NOTUSED(ys); + STBTE__NOTUSED(xo); + STBTE__NOTUSED(yo); +#endif +} + +#endif // STB_TILEMAP_EDITOR_IMPLEMENTATION diff --git a/test_data/lots_of_files/stb_truetype.h b/test_data/lots_of_files/stb_truetype.h new file mode 100644 index 0000000..5784549 --- /dev/null +++ b/test_data/lots_of_files/stb_truetype.h @@ -0,0 +1,3220 @@ +// stb_truetype.h - v1.07 - public domain +// authored from 2009-2015 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// Martins Mozeiko +// Cap Petschulat +// Omar Cornut +// github:aloucks +// Peter LaValle +// Giumo X. Clanjor +// +// Misc other: +// Ryan Gordon +// +// VERSION HISTORY +// +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 +// +// +// PERFORMANCE MEASUREMENTS FOR 1.06: +// +// 32-bit 64-bit +// Previous release: 8.83 s 7.68 s +// Pool allocations: 7.72 s 6.34 s +// Inline sort : 6.54 s 5.65 s +// New rasterizer : 5.63 s 5.00 s + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +unsigned char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLuint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include <stdio.h> +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include <math.h> + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include <math.h> + #define STBTT_sqrt(x) sqrt(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include <stdlib.h> + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include <assert.h> + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include <string.h> + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include <memory.h> + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +typedef struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = (1/dxdy); + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float y0,y1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + y0 = e->sy; + } else { + x_top = x0; + y0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + y1 = e->ey; + } else { + x_bottom = xb; + y1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = y1 - y0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + y0 = y_bottom - (y0 - y_top); + y1 = y_bottom - (y1 - y_top); + t = y0, y0 = y1, y1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-y0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (y1-y_crossing); + + scanline_fill[x2] += sign * (y1-y0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clear pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + float y1,y2; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + y1 = (x - x0) / dx + y_top; + y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + STBTT_assert(z->ey >= scan_y_top); + // insert at front + z->next = active; + active = z; + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshhold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && mid<n: 0>n => n; 0<n => 0 */ + /* 0<mid && mid>n: 0>n => 0; 0<n => n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + return k; +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// diff --git a/test_data/lots_of_files/stb_vorbis.c b/test_data/lots_of_files/stb_vorbis.c new file mode 100644 index 0000000..98e96fb --- /dev/null +++ b/test_data/lots_of_files/stb_vorbis.c @@ -0,0 +1,5441 @@ +// Ogg Vorbis audio decoder - v1.06 - public domain +// http://nothings.org/stb_vorbis/ +// +// Written by Sean Barrett in 2007, last updated in 2014 +// Sponsored by RAD Game Tools. +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster "alxprd"@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// (If you reported a bug but do not appear in this list, it is because +// someone else reported the bug before you. There were too many of you to +// list them all because I was lax about updating for a long time, sorry.) +// +// Partial history: +// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// 0.99996 - - bracket #include <malloc.h> for macintosh compilation +// 0.99995 - - avoid alias-optimization issue in float-to-int conversion +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include <stdio.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern void stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of samples per channel. the +// data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. Note that for interleaved data, you pass in the number of +// shorts (the size of your array), but the return value is the number of +// samples per channel, not the total number of samples. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +// STB_VORBIS_CODEBOOK_SHORTS +// The vorbis file format encodes VQ codebook floats as ax+b where a and +// b are floating point per-codebook constants, and x is a 16-bit int. +// Normally, stb_vorbis decodes them to floats rather than leaving them +// as 16-bit ints and computing ax+b while decoding. This is a speed/space +// tradeoff; you can save space by defining this flag. +#ifndef STB_VORBIS_CODEBOOK_SHORTS +#define STB_VORBIS_CODEBOOK_FLOATS +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include <stdio.h> +#endif + +#ifndef STB_VORBIS_NO_CRT +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <math.h> +#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh)) +#include <malloc.h> +#endif +#else +#define NULL 0 +#endif + +#if !defined(_MSC_VER) && !(defined(__MINGW32__) && defined(__forceinline)) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifdef STB_VORBIS_CODEBOOK_FLOATS +typedef float codetype; +#else +typedef uint16 codetype; +#endif + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +extern int my_prof(int slot); +//#define stb_prof my_prof + +#ifndef stb_prof +#define stb_prof(x) ((void) 0) +#endif + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#ifdef dealloca +#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) +#else +#define temp_free(f,p) 0 +#endif +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is not a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else if (n < (1 << 31)) return 30 + log2_4[n >> 30]; + else return 0; // signed n returns 0 +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,y; +} Point; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + Point *a = (Point *) p; + Point *b = (Point *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5, +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + assert(c->sorted_codewords || c->codewords); + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#ifndef STB_VORBIS_CODEBOOK_FLOATS + #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off] * c->delta_value + c->minimum_value) + #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off] * c->delta_value) + #define CODEBOOK_ELEMENT_BASE(c) (c->minimum_value) +#else + #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) + #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) + #define CODEBOOK_ELEMENT_BASE(c) (0) +#endif + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK +static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **outputs, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*2 + effective > len * 2) { + effective = len*2 - (p_inter*2 - c_inter); + } + + { + z *= c->dimensions; + stb_prof(11); + if (c->sequence_p) { + // haven't optimized this case because I don't have any examples + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == 2) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + i=0; + if (c_inter == 1) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + c_inter = 0; ++p_inter; + ++i; + } + { + float *z0 = outputs[0]; + float *z1 = outputs[1]; + for (; i+1 < effective;) { + float v0 = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + float v1 = CODEBOOK_ELEMENT_FAST(c,z+i+1) + last; + if (z0) + z0[p_inter] += v0; + if (z1) + z1[p_inter] += v1; + ++p_inter; + i += 2; + } + } + if (i < effective) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == 2) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} +#endif + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + LINE_OP(output[x], inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y]); + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + stb_prof(2); + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + stb_prof(3); + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + stb_prof(13); + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + stb_prof(5); + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(20); // accounts for X time + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat_2(f, book, residue_buffers, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + stb_prof(7); + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + stb_prof(8); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(22); + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + stb_prof(3); + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(22); + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + stb_prof(3); + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + stb_prof(9); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + stb_prof(0); + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + lx = hx, ly = hy; + } + } + if (lx < n2) + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + stb_prof(1); + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + stb_prof(0); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + +// INVERSE COUPLING + stb_prof(14); + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + stb_prof(15); + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + stb_prof(16); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + stb_prof(0); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet - (n-right_end); + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + right_end) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static void vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left; + if (vorbis_decode_packet(f, &len, &left, &right)) + vorbis_finish_frame(f, len, left, right); +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + int z = sparse ? c->sorted_values[j] : j, div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + c->multiplicands[j*c->dimensions + k] = + #ifndef STB_VORBIS_CODEBOOK_FLOATS + mults[off]; + #else + mults[off]*c->delta_value + c->minimum_value; + // in this case (and this case only) we could pre-expand c->sequence_p, + // and throw away the decode logic for it; have to ALSO do + // it in the case below, but it can only be done if + // STB_VORBIS_CODEBOOK_FLOATS + // !STB_VORBIS_DIVIDES_IN_CODEBOOK + #endif + div *= c->lookup_values; + } + } + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + c->lookup_type = 2; + } + else +#endif + { + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + #ifndef STB_VORBIS_CODEBOOK_FLOATS + memcpy(c->multiplicands, mults, sizeof(c->multiplicands[0]) * c->lookup_values); + #else + for (j=0; j < (int) c->lookup_values; ++j) + c->multiplicands[j] = mults[j] * c->delta_value + c->minimum_value; + #endif + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + + #ifdef STB_VORBIS_CODEBOOK_FLOATS + if (c->lookup_type == 2 && c->sequence_p) { + for (j=1; j < (int) c->lookup_values; ++j) + c->multiplicands[j] = c->multiplicands[j-1]; + c->sequence_p = 0; + } + #endif + } + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + Point p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].y = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].y; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, data, data_len); + } + + f->stream = data; + f->stream_end = data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return f->stream - data; + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return f->stream - data; + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return f->stream - data; +} + +stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = data; + p.stream_end = data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = f->stream - data; + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return f->stream - f->stream_start; + #ifndef STB_VORBIS_NO_STDIO + return ftell(f->f) - f->f_start; + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceeding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding; + double offset, bytes_per_sample; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + stb_vorbis_seek_start(f); + return 1; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + vorbis_pump_first_frame(f); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +void stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { error(f, VORBIS_invalid_api_mixing); return; } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = ftell(file); + fseek(file, 0, SEEK_END); + len = ftell(file) - start; + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015/04/19 - don't define __forceinline if it's redundant + 1.04 - 2014/08/27 - fix missing const-correct case in API + 1.03 - 2014/08/07 - Warning fixes + 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float + 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include <malloc.h> for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY diff --git a/test_data/lots_of_files/stb_voxel_render.h b/test_data/lots_of_files/stb_voxel_render.h new file mode 100644 index 0000000..29f6fb7 --- /dev/null +++ b/test_data/lots_of_files/stb_voxel_render.h @@ -0,0 +1,3750 @@ +// stb_voxel_render.h - v0.83 - Sean Barrett, 2015 - public domain +// +// This library helps render large-scale "voxel" worlds for games, +// in this case, one with blocks that can have textures and that +// can also be a few shapes other than cubes. +// +// Video introduction: +// http://www.youtube.com/watch?v=2vnTtiLrV1w +// +// Minecraft-viewer sample app (not very simple though): +// http://github.com/nothings/stb/tree/master/tests/caveview +// +// It works by creating triangle meshes. The library includes +// +// - converter from dense 3D arrays of block info to vertex mesh +// - shader for the vertex mesh +// - assistance in setting up shader state +// +// For portability, none of the library code actually accesses +// the 3D graphics API. (At the moment, it's not actually portable +// since the shaders are GLSL only, but patches are welcome.) +// +// You have to do all the caching and tracking of vertex buffers +// yourself. However, you could also try making a game with +// a small enough world that it's fully loaded rather than +// streaming. Currently the preferred vertex format is 20 bytes +// per quad. There are plans to allow much more compact formats +// with a slight reduction in shader features. +// +// +// USAGE +// +// #define the symbol STB_VOXEL_RENDER_IMPLEMENTATION in *one* +// C/C++ file before the #include of this file; the implementation +// will be generated in that file. +// +// If you define the symbols STB_VOXEL_RENDER_STATIC, then the +// implementation will be private to that file. +// +// +// FEATURES +// +// - you can choose textured blocks with the features below, +// or colored voxels with 2^24 colors and no textures. +// +// - voxels are mostly just cubes, but there's support for +// half-height cubes and diagonal slopes, half-height +// diagonals, and even odder shapes especially for doing +// more-continuous "ground". +// +// - texture coordinates are projections along one of the major +// axes, with the per-texture scaling. +// +// - a number of aspects of the shader and the vertex format +// are configurable; the library generally takes care of +// coordinating the vertex format with the mesh for you. +// +// +// FEATURES (SHADER PERSPECTIVE) +// +// - vertices aligned on integer lattice, z on multiples of 0.5 +// - per-vertex "lighting" or "ambient occlusion" value (6 bits) +// - per-vertex texture crossfade (3 bits) +// +// - per-face texture #1 id (8-bit index into array texture) +// - per-face texture #2 id (8-bit index into second array texture) +// - per-face color (6-bit palette index, 2 bits of per-texture boolean enable) +// - per-face 5-bit normal for lighting calculations & texture coord computation +// - per-face 2-bit texture matrix rotation to rotate faces +// +// - indexed-by-texture-id scale factor (separate for texture #1 and texture #2) +// - indexed-by-texture-#2-id blend mode (alpha composite or modulate/multiply); +// the first is good for decals, the second for detail textures, "light maps", +// etc; both modes are controlled by texture #2's alpha, scaled by the +// per-vertex texture crossfade and the per-face color (if enabled on texture #2); +// modulate/multiply multiplies by an extra factor of 2.0 so that if you +// make detail maps whose average brightness is 0.5 everything works nicely. +// +// - ambient lighting: half-lambert directional plus constant, all scaled by vertex ao +// - face can be fullbright (emissive), controlled by per-face color +// - installable lighting, with default single-point-light +// - installable fog, with default hacked smoothstep +// +// Note that all the variations of lighting selection and texture +// blending are run-time conditions in the shader, so they can be +// intermixed in a single mesh. +// +// +// INTEGRATION ARC +// +// The way to get this library to work from scratch is to do the following: +// +// Step 1. define STBVOX_CONFIG_MODE to 0 +// +// This mode uses only vertex attributes and uniforms, and is easiest +// to get working. It requires 32 bytes per quad and limits the +// size of some tables to avoid hitting uniform limits. +// +// Step 2. define STBVOX_CONFIG_MODE to 1 +// +// This requires using a texture buffer to store the quad data, +// reducing the size to 20 bytes per quad. +// +// Step 3: define STBVOX_CONFIG_PREFER_TEXBUFFER +// +// This causes some uniforms to be stored as texture buffers +// instead. This increases the size of some of those tables, +// and avoids a potential slow path (gathering non-uniform +// data from uniforms) on some hardware. +// +// In the future I hope to add additional modes that have significantly +// smaller meshes but reduce features, down as small as 6 bytes per quad. +// See elsewhere in this file for a table of candidate modes. Switching +// to a mode will require changing some of your mesh creation code, but +// everything else should be seamless. (And I'd like to change the API +// so that mesh creation is data-driven the way the uniforms are, and +// then you wouldn't even have to change anything but the mode number.) +// +// +// IMPROVEMENTS FOR SHIP-WORTHY PROGRAMS USING THIS LIBRARY +// +// I currently tolerate a certain level of "bugginess" in this library. +// +// I'm referring to things which look a little wrong (as long as they +// don't cause holes or cracks in the output meshes), or things which +// do not produce as optimal a mesh as possible. Notable examples: +// +// - incorrect lighting on slopes +// - inefficient meshes for vheight blocks +// +// I am willing to do the work to improve these things if someone is +// going to ship a substantial program that would be improved by them. +// (It need not be commercial, nor need it be a game.) I just didn't +// want to do the work up front if it might never be leveraged. So just +// submit a bug report as usual (github is preferred), but add a note +// that this is for a thing that is really going to ship. (That means +// you need to be far enough into the project that it's clear you're +// committed to it; not during early exploratory development.) +// +// +// VOXEL MESH API +// +// Context +// +// To understand the API, make sure you first understand the feature set +// listed above. +// +// Because the vertices are compact, they have very limited spatial +// precision. Thus a single mesh can only contain the data for a limited +// area. To make very large voxel maps, you'll need to build multiple +// vertex buffers. (But you want this anyway for frustum culling.) +// +// Each generated mesh has three components: +// - vertex data (vertex buffer) +// - face data (optional, stored in texture buffer) +// - mesh transform (uniforms) +// +// Once you've generated the mesh with this library, it's up to you +// to upload it to the GPU, to keep track of the state, and to render +// it. +// +// Concept +// +// The basic design is that you pass in one or more 3D arrays; each array +// is (typically) one-byte-per-voxel and contains information about one +// or more properties of some particular voxel property. +// +// Because there is so much per-vertex and per-face data possible +// in the output, and each voxel can have 6 faces and 8 vertices, it +// would require an very large data structure to describe all +// of the possibilities, and this would cause the mesh-creation +// process to be slow. Instead, the API provides multiple ways +// to express each property, some more compact, others less so; +// each such way has some limitations on what it can express. +// +// Note that there are so many paths and combinations, not all of them +// have been tested. Just report bugs and I'll fix 'em. +// +// Details +// +// See the API documentation in the header-file section. +// +// +// CONTRIBUTORS +// +// Features Porting Bugfixes & Warnings +// Sean Barrett github:r-leyh Jesus Fernandez +// Miguel Lechon github:Arbeiterunfallversicherungsgesetz +// Thomas Frase James Hofmann +// +// VERSION HISTORY +// +// 0.83 (2015-09-13) remove non-constant struct initializers to support more compilers +// 0.82 (2015-08-01) added input.packed_compact to store rot, vheight & texlerp efficiently +// fix broken tex_overlay2 +// 0.81 (2015-05-28) fix broken STBVOX_CONFIG_OPTIMIZED_VHEIGHT +// 0.80 (2015-04-11) fix broken STBVOX_CONFIG_ROTATION_IN_LIGHTING refactoring +// change STBVOX_MAKE_LIGHTING to STBVOX_MAKE_LIGHTING_EXT so +// that header defs don't need to see config vars +// add STBVOX_CONFIG_VHEIGHT_IN_LIGHTING and other vheight fixes +// added documentation for vheight ("weird slopes") +// 0.79 (2015-04-01) fix the missing types from 0.78; fix string constants being const +// 0.78 (2015-04-02) bad "#else", compile as C++ +// 0.77 (2015-04-01) documentation tweaks, rename config var to STB_VOXEL_RENDER_STATIC +// 0.76 (2015-04-01) typos, signed/unsigned shader issue, more documentation +// 0.75 (2015-04-01) initial release +// +// +// HISTORICAL FOUNDATION +// +// stb_voxel_render 20-byte quads 2015/01 +// zmc engine 32-byte quads 2013/12 +// zmc engine 96-byte quads 2011/10 +// +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + +#ifndef INCLUDE_STB_VOXEL_RENDER_H +#define INCLUDE_STB_VOXEL_RENDER_H + +#include <stdlib.h> + +typedef struct stbvox_mesh_maker stbvox_mesh_maker; +typedef struct stbvox_input_description stbvox_input_description; + +#ifdef STB_VOXEL_RENDER_STATIC +#define STBVXDEC static +#else +#define STBVXDEC extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// CONFIGURATION MACROS +// +// #define STBVOX_CONFIG_MODE <integer> // REQUIRED +// Configures the overall behavior of stb_voxel_render. This +// can affect the shaders, the uniform info, and other things. +// (If you need more than one mode in the same app, you can +// use STB_VOXEL_RENDER_STATIC to create multiple versions +// in separate files, and then wrap them.) +// +// Mode value Meaning +// 0 Textured blocks, 32-byte quads +// 1 Textured blocks, 20-byte quads +// 20 Untextured blocks, 32-byte quads +// 21 Untextured blocks, 20-byte quads +// +// +// #define STBVOX_CONFIG_PRECISION_Z <integer> // OPTIONAL +// Defines the number of bits of fractional position for Z. +// Only 0 or 1 are valid. 1 is the default. If 0, then a +// single mesh has twice the legal Z range; e.g. in +// modes 0,1,20,21, Z in the mesh can extend to 511 instead +// of 255. However, half-height blocks cannot be used. +// +// All of the following just #ifdef tested so need no values, and are optional. +// +// STBVOX_CONFIG_BLOCKTYPE_SHORT +// use unsigned 16-bit values for 'blocktype' in the input instead of 8-bit values +// +// STBVOX_CONFIG_OPENGL_MODELVIEW +// use the gl_ModelView matrix rather than the explicit uniform +// +// STBVOX_CONFIG_HLSL +// NOT IMPLEMENTED! Define HLSL shaders instead of GLSL shaders +// +// STBVOX_CONFIG_PREFER_TEXBUFFER +// Stores many of the uniform arrays in texture buffers intead, +// so they can be larger and may be more efficient on some hardware. +// +// STBVOX_CONFIG_LIGHTING_SIMPLE +// Creates a simple lighting engine with a single point light source +// in addition to the default half-lambert ambient light. +// +// STBVOX_CONFIG_LIGHTING +// Declares a lighting function hook; you must append a lighting function +// to the shader before compiling it: +// vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient); +// 'ambient' is the half-lambert ambient light with vertex ambient-occlusion applied +// +// STBVOX_CONFIG_FOG_SMOOTHSTEP +// Defines a simple unrealistic fog system designed to maximize +// unobscured view distance while not looking too weird when things +// emerge from the fog. Configured using an extra array element +// in the STBVOX_UNIFORM_ambient uniform. +// +// STBVOX_CONFIG_FOG +// Defines a fog function hook; you must append a fog function to +// the shader before compiling it: +// vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha); +// "color" is the incoming pre-fogged color, fragment_alpha is the alpha value, +// and relative_pos is the vector from the point to the camera in worldspace +// +// STBVOX_CONFIG_DISABLE_TEX2 +// This disables all processing of texture 2 in the shader in case +// you don't use it. Eventually this will be replaced with a mode +// that omits the unused data entirely. +// +// STBVOX_CONFIG_TEX1_EDGE_CLAMP +// STBVOX_CONFIG_TEX2_EDGE_CLAMP +// If you want to edge clamp the textures, instead of letting them wrap, +// set this flag. By default stb_voxel_render relies on texture wrapping +// to simplify texture coordinate generation. This flag forces it to do +// it correctly, although there can still be minor artifacts. +// +// STBVOX_CONFIG_ROTATION_IN_LIGHTING +// Changes the meaning of the 'lighting' mesher input variable to also +// store the rotation; see later discussion. +// +// STBVOX_CONFIG_VHEIGHT_IN_LIGHTING +// Changes the meaning of the 'lighting' mesher input variable to also +// store the vheight; see later discussion. Cannot use both this and +// the previous variable. +// +// STBVOX_CONFIG_PREMULTIPLIED_ALPHA +// Adjusts the shader calculations on the assumption that tex1.rgba, +// tex2.rgba, and color.rgba all use premultiplied values, and that +// the output of the fragment shader should be premultiplied. +// +// STBVOX_CONFIG_UNPREMULTIPLY +// Only meaningful if STBVOX_CONFIG_PREMULTIPLIED_ALPHA is defined. +// Changes the behavior described above so that the inputs are +// still premultiplied alpha, but the output of the fragment +// shader is not premultiplied alpha. This is needed when allowing +// non-unit alpha values but not doing alpha-blending (for example +// when alpha testing). +// + +////////////////////////////////////////////////////////////////////////////// +// +// MESHING +// +// A mesh represents a (typically) small chunk of a larger world. +// Meshes encode coordinates using small integers, so those +// coordinates must be relative to some base location. +// All of the coordinates in the functions below use +// these relative coordinates unless explicitly stated +// otherwise. +// +// Input to the meshing step is documented further down + +STBVXDEC void stbvox_init_mesh_maker(stbvox_mesh_maker *mm); +// Call this function to initialize a mesh-maker context structure +// used to build meshes. You should have one context per thread +// that's building meshes. + +STBVXDEC void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len); +// Call this to set the buffer into which stbvox will write the mesh +// it creates. It can build more than one mesh in parallel (distinguished +// by the 'mesh' parameter), and each mesh can be made up of more than +// one buffer (distinguished by the 'slot' parameter). +// +// Multiple meshes are under your control; use the 'selector' input +// variable to choose which mesh each voxel's vertices are written to. +// For example, you can use this to generate separate meshes for opaque +// and transparent data. +// +// You can query the number of slots by calling stbvox_get_buffer_count +// described below. The meaning of the buffer for each slot depends +// on STBVOX_CONFIG_MODE. +// +// In mode 0 & mode 20, there is only one slot. The mesh data for that +// slot is two interleaved vertex attributes: attr_vertex, a single +// 32-bit uint, and attr_face, a single 32-bit uint. +// +// In mode 1 & mode 21, there are two slots. The first buffer should +// be four times as large as the second buffer. The first buffer +// contains a single vertex attribute: 'attr_vertex', a single 32-bit uint. +// The second buffer contains texture buffer data (an array of 32-bit uints) +// that will be accessed through the sampler identified by STBVOX_UNIFORM_face_data. + +STBVXDEC int stbvox_get_buffer_count(stbvox_mesh_maker *mm); +// Returns the number of buffers needed per mesh as described above. + +STBVXDEC int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int slot); +// Returns how much of a given buffer will get used per quad. This +// allows you to choose correct relative sizes for each buffer, although +// the values are fixed based on the configuration you've selected at +// compile time, and the details are described in stbvox_set_buffer. + +STBVXDEC void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh); +// Selects which mesh the mesher will output to (see previous function) +// if the input doesn't specify a per-voxel selector. (I doubt this is +// useful, but it's here just in case.) + +STBVXDEC stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm); +// This function call returns a pointer to the stbvox_input_description part +// of stbvox_mesh_maker (which you should otherwise treat as opaque). You +// zero this structure, then fill out the relevant pointers to the data +// describing your voxel object/world. +// +// See further documentation at the description of stbvox_input_description below. + +STBVXDEC void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_elements, int y_stride_in_elements); +// This sets the stride between successive elements of the 3D arrays +// in the stbvox_input_description. Z values are always stored consecutively. +// (The preferred coordinate system for stbvox is X right, Y forwards, Z up.) + +STBVXDEC void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1); +// This sets the range of values in the 3D array for the voxels that +// the mesh generator will convert. The lower values are inclusive, +// the higher values are exclusive, so (0,0,0) to (16,16,16) generates +// mesh data associated with voxels up to (15,15,15) but no higher. +// +// The mesh generate generates faces at the boundary between open space +// and solid space but associates them with the solid space, so if (15,0,0) +// is open and (16,0,0) is solid, then the mesh will contain the boundary +// between them if x0 <= 16 and x1 > 16. +// +// Note that the mesh generator will access array elements 1 beyond the +// limits set in these parameters. For example, if you set the limits +// to be (0,0,0) and (16,16,16), then the generator will access all of +// the voxels between (-1,-1,-1) and (16,16,16), including (16,16,16). +// You may have to do pointer arithmetic to make it work. +// +// For example, caveview processes mesh chunks that are 32x32x16, but it +// does this using input buffers that are 34x34x18. +// +// The lower limits are x0 >= 0, y0 >= 0, and z0 >= 0. +// +// The upper limits are mode dependent, but all the current methods are +// limited to x1 < 127, y1 < 127, z1 < 255. Note that these are not +// powers of two; if you want to use power-of-two chunks (to make +// it efficient to decide which chunk a coordinate falls in), you're +// limited to at most x1=64, y1=64, z1=128. For classic Minecraft-style +// worlds with limited vertical extent, I recommend using a single +// chunk for the entire height, which limits the height to 255 blocks +// (one less than Minecraft), and only chunk the map in X & Y. + +STBVXDEC int stbvox_make_mesh(stbvox_mesh_maker *mm); +// Call this function to create mesh data for the currently configured +// set of input data. This appends to the currently configured mesh output +// buffer. Returns 1 on success. If there is not enough room in the buffer, +// it outputs as much as it can, and returns 0; you need to switch output +// buffers (either by calling stbvox_set_buffer to set new buffers, or +// by copying the data out and calling stbvox_reset_buffers), and then +// call this function again without changing any of the input parameters. +// +// Note that this function appends; you can call it multiple times to +// build a single mesh. For example, caveview uses chunks that are +// 32x32x255, but builds the mesh for it by processing 32x32x16 at atime +// (this is faster as it is reuses the same 34x34x18 input buffers rather +// than needing 34x34x257 input buffers). + +// Once you're done creating a mesh into a given buffer, +// consider the following functions: + +STBVXDEC int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh); +// Returns the number of quads in the mesh currently generated by mm. +// This is the sum of all consecutive stbvox_make_mesh runs appending +// to the same buffer. 'mesh' distinguishes between the multiple user +// meshes available via 'selector' or stbvox_set_default_mesh. +// +// Typically you use this function when you're done building the mesh +// and want to record how to draw it. +// +// Note that there are no index buffers; the data stored in the buffers +// should be drawn as quads (e.g. with GL_QUAD); if your API does not +// support quads, you can create a single index buffer large enough to +// draw your largest vertex buffer, and reuse it for every rendering. +// (Note that if you use 32-bit indices, you'll use 24 bytes of bandwidth +// per quad, more than the 20 bytes for the vertex/face mesh data.) + +STBVXDEC void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z); +// Sets the global coordinates for this chunk, such that (0,0,0) relative +// coordinates will be at (x,y,z) in global coordinates. + +STBVXDEC void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]); +// Returns the bounds for the mesh in global coordinates. Use this +// for e.g. frustum culling the mesh. @BUG: this just uses the +// values from stbvox_set_input_range(), so if you build by +// appending multiple values, this will be wrong, and you need to +// set stbvox_set_input_range() to the full size. Someday this +// will switch to tracking the actual bounds of the *mesh*, though. + +STBVXDEC void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]); +// Returns the 'transform' data for the shader uniforms. It is your +// job to set this to the shader before drawing the mesh. It is the +// only uniform that needs to change per-mesh. Note that it is not +// a 3x3 matrix, but rather a scale to decode fixed point numbers as +// floats, a translate from relative to global space, and a special +// translation for texture coordinate generation that avoids +// floating-point precision issues. @TODO: currently we add the +// global translation to the vertex, than multiply by modelview, +// but this means if camera location and vertex are far from the +// origin, we lose precision. Need to make a special modelview with +// the translation (or some of it) factored out to avoid this. + +STBVXDEC void stbvox_reset_buffers(stbvox_mesh_maker *mm); +// Call this function if you're done with the current output buffer +// but want to reuse it (e.g. you're done appending with +// stbvox_make_mesh and you've copied the data out to your graphics API +// so can reuse the buffer). + +////////////////////////////////////////////////////////////////////////////// +// +// RENDERING +// + +STBVXDEC char *stbvox_get_vertex_shader(void); +// Returns the (currently GLSL-only) vertex shader. + +STBVXDEC char *stbvox_get_fragment_shader(void); +// Returns the (currently GLSL-only) fragment shader. +// You can override the lighting and fogging calculations +// by appending data to the end of these; see the #define +// documentation for more information. + +STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void); +// Returns a slightly cheaper fragment shader that computes +// alpha but not color. This is useful for e.g. a depth-only +// pass when using alpha test. + +typedef struct stbvox_uniform_info stbvox_uniform_info; + +STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform); +// Gets the information about a uniform necessary for you to +// set up each uniform with a minimal amount of explicit code. +// See the sample code after the structure definition for stbvox_uniform_info, +// further down in this header section. +// +// "uniform" is from the list immediately following. For many +// of these, default values are provided which you can set. +// Most values are shared for most draw calls; e.g. for stateful +// APIs you can set most of the state only once. Only +// STBVOX_UNIFORM_transform needs to change per draw call. +// +// STBVOX_UNIFORM_texscale +// 64- or 128-long vec4 array. (128 only if STBVOX_CONFIG_PREFER_TEXBUFFER) +// x: scale factor to apply to texture #1. must be a power of two. 1.0 means 'face-sized' +// y: scale factor to apply to texture #2. must be a power of two. 1.0 means 'face-sized' +// z: blend mode indexed by texture #2. 0.0 is alpha compositing; 1.0 is multiplication. +// w: unused currently. @TODO use to support texture animation? +// +// Texscale is indexed by the bottom 6 or 7 bits of the texture id; thus for +// example the texture at index 0 in the array and the texture in index 128 of +// the array must be scaled the same. This means that if you only have 64 or 128 +// unique textures, they all get distinct values anyway; otherwise you have +// to group them in pairs or sets of four. +// +// STBVOX_UNIFORM_ambient +// 4-long vec4 array: +// ambient[0].xyz - negative of direction of a directional light for half-lambert +// ambient[1].rgb - color of light scaled by NdotL (can be negative) +// ambient[2].rgb - constant light added to above calculation; +// effectively light ranges from ambient[2]-ambient[1] to ambient[2]+ambient[1] +// ambient[3].rgb - fog color for STBVOX_CONFIG_FOG_SMOOTHSTEP +// ambient[3].a - reciprocal of squared distance of farthest fog point (viewing distance) + + + // +----- has a default value + // | +-- you should always use the default value +enum // V V +{ // ------------------------------------------------ + STBVOX_UNIFORM_face_data, // n the sampler with the face texture buffer + STBVOX_UNIFORM_transform, // n the transform data from stbvox_get_transform + STBVOX_UNIFORM_tex_array, // n an array of two texture samplers containing the two texture arrays + STBVOX_UNIFORM_texscale, // Y a table of texture properties, see above + STBVOX_UNIFORM_color_table, // Y 64 vec4 RGBA values; a default palette is provided; if A > 1.0, fullbright + STBVOX_UNIFORM_normals, // Y Y table of normals, internal-only + STBVOX_UNIFORM_texgen, // Y Y table of texgen vectors, internal-only + STBVOX_UNIFORM_ambient, // n lighting & fog info, see above + STBVOX_UNIFORM_camera_pos, // Y camera position in global voxel space (for lighting & fog) + + STBVOX_UNIFORM_count, +}; + +enum +{ + STBVOX_UNIFORM_TYPE_none, + STBVOX_UNIFORM_TYPE_sampler, + STBVOX_UNIFORM_TYPE_vec2, + STBVOX_UNIFORM_TYPE_vec3, + STBVOX_UNIFORM_TYPE_vec4, +}; + +struct stbvox_uniform_info +{ + int type; // which type of uniform + int bytes_per_element; // the size of each uniform array element (e.g. vec3 = 12 bytes) + int array_length; // length of the uniform array + char *name; // name in the shader @TODO use numeric binding + float *default_value; // if not NULL, you can use this as the uniform pointer + int use_tex_buffer; // if true, then the uniform is a sampler but the data can come from default_value +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Uniform sample code +// + +#if 0 +// Run this once per frame before drawing all the meshes. +// You still need to separately set the 'transform' uniform for every mesh. +void setup_uniforms(GLuint shader, float camera_pos[4], GLuint tex1, GLuint tex2) +{ + int i; + glUseProgram(shader); // so uniform binding works + for (i=0; i < STBVOX_UNIFORM_count; ++i) { + stbvox_uniform_info sui; + if (stbvox_get_uniform_info(&sui, i)) { + GLint loc = glGetUniformLocation(shader, sui.name); + if (loc != 0) { + switch (i) { + case STBVOX_UNIFORM_camera_pos: // only needed for fog + glUniform4fv(loc, sui.array_length, camera_pos); + break; + + case STBVOX_UNIFORM_tex_array: { + GLuint tex_unit[2] = { 0, 1 }; // your choice of samplers + glUniform1iv(loc, 2, tex_unit); + + glActiveTexture(GL_TEXTURE0 + tex_unit[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex1); + glActiveTexture(GL_TEXTURE0 + tex_unit[1]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex2); + glActiveTexture(GL_TEXTURE0); // reset to default + break; + } + + case STBVOX_UNIFORM_face_data: + glUniform1i(loc, SAMPLER_YOU_WILL_BIND_PER_MESH_FACE_DATA_TO); + break; + + case STBVOX_UNIFORM_ambient: // you definitely want to override this + case STBVOX_UNIFORM_color_table: // you might want to override this + case STBVOX_UNIFORM_texscale: // you may want to override this + glUniform4fv(loc, sui.array_length, sui.default_value); + break; + + case STBVOX_UNIFORM_normals: // you never want to override this + case STBVOX_UNIFORM_texgen: // you never want to override this + glUniform3fv(loc, sui.array_length, sui.default_value); + break; + } + } + } + } +} +#endif + +#ifdef __cplusplus +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// INPUT TO MESHING +// + +// Shapes of blocks that aren't always cubes +enum +{ + STBVOX_GEOM_empty, + STBVOX_GEOM_knockout, // creates a hole in the mesh + STBVOX_GEOM_solid, + STBVOX_GEOM_transp, // solid geometry, but transparent contents so neighbors generate normally, unless same blocktype + + // following 4 can be represented by vheight as well + STBVOX_GEOM_slab_upper, + STBVOX_GEOM_slab_lower, + STBVOX_GEOM_floor_slope_north_is_top, + STBVOX_GEOM_ceil_slope_north_is_bottom, + + STBVOX_GEOM_floor_slope_north_is_top_as_wall_UNIMPLEMENTED, // same as floor_slope above, but uses wall's texture & texture projection + STBVOX_GEOM_ceil_slope_north_is_bottom_as_wall_UNIMPLEMENTED, + STBVOX_GEOM_crossed_pair, // corner-to-corner pairs, with normal vector bumped upwards + STBVOX_GEOM_force, // like GEOM_transp, but faces visible even if neighbor is same type, e.g. minecraft fancy leaves + + // these access vheight input + STBVOX_GEOM_floor_vheight_03 = 12, // diagonal is SW-NE + STBVOX_GEOM_floor_vheight_12, // diagonal is SE-NW + STBVOX_GEOM_ceil_vheight_03, + STBVOX_GEOM_ceil_vheight_12, + + STBVOX_GEOM_count, // number of geom cases +}; + +enum +{ + STBVOX_FACE_east, + STBVOX_FACE_north, + STBVOX_FACE_west, + STBVOX_FACE_south, + STBVOX_FACE_up, + STBVOX_FACE_down, + + STBVOX_FACE_count, +}; + +#ifdef STBVOX_CONFIG_BLOCKTYPE_SHORT +typedef unsigned short stbvox_block_type; +#else +typedef unsigned char stbvox_block_type; +#endif + +// 24-bit color +typedef struct +{ + unsigned char r,g,b; +} stbvox_rgb; + +#define STBVOX_COLOR_TEX1_ENABLE 64 +#define STBVOX_COLOR_TEX2_ENABLE 128 + +// This is the data structure you fill out. Most of the arrays can be +// NULL, except when one is required to get the value to index another. +// +// The compass system used in the following descriptions is: +// east means increasing x +// north means increasing y +// up means increasing z +struct stbvox_input_description +{ + unsigned char lighting_at_vertices; + // The default is lighting values (i.e. ambient occlusion) are at block + // center, and the vertex light is gathered from those adjacent block + // centers that the vertex is facing. This makes smooth lighting + // consistent across adjacent faces with the same orientation. + // + // Setting this flag to non-zero gives you explicit control + // of light at each vertex, but now the lighting/ao will be + // shared by all vertices at the same point, even if they + // have different normals. + + // these are mostly 3D maps you use to define your voxel world, using x_stride and y_stride + // note that for cache efficiency, you want to use the block_foo palettes as much as possible instead + + stbvox_rgb *rgb; + // Indexed by 3D coordinate. + // 24-bit voxel color for STBVOX_CONFIG_MODE = 20 or 21 only + + unsigned char *lighting; + // Indexed by 3D coordinate. The lighting value / ambient occlusion + // value that is used to define the vertex lighting values. + // The raw lighting values are defined at the center of blocks + // (or at vertex if 'lighting_at_vertices' is true). + // + // If the macro STBVOX_CONFIG_ROTATION_IN_LIGHTING is defined, + // then an additional 2-bit block rotation value is stored + // in this field as well. + // + // Encode with STBVOX_MAKE_LIGHTING_EXT(lighting,rot)--here + // 'lighting' should still be 8 bits, as the macro will + // discard the bottom bits automatically. Similarly, if + // using STBVOX_CONFIG_VHEIGHT_IN_LIGHTING, encode with + // STBVOX_MAKE_LIGHTING_EXT(lighting,vheight). + // + // (Rationale: rotation needs to be independent of blocktype, + // but is only 2 bits so doesn't want to be its own array. + // Lighting is the one thing that was likely to already be + // in use and that I could easily steal 2 bits from.) + + stbvox_block_type *blocktype; + // Indexed by 3D coordinate. This is a core "block type" value, which is used + // to index into other arrays; essentially a "palette". This is much more + // memory-efficient and performance-friendly than storing the values explicitly, + // but only makes sense if the values are always synchronized. + // + // If a voxel's blocktype is 0, it is assumed to be empty (STBVOX_GEOM_empty), + // and no other blocktypes should be STBVOX_GEOM_empty. (Only if you do not + // have blocktypes should STBVOX_GEOM_empty ever used.) + // + // Normally it is an unsigned byte, but you can override it to be + // a short if you have too many blocktypes. + + unsigned char *geometry; + // Indexed by 3D coordinate. Contains the geometry type for the block. + // Also contains a 2-bit rotation for how the whole block is rotated. + // Also includes a 2-bit vheight value when using shared vheight values. + // See the separate vheight documentation. + // Encode with STBVOX_MAKE_GEOMETRY(geom, rot, vheight) + + unsigned char *block_geometry; + // Array indexed by blocktype containing the geometry for this block, plus + // a 2-bit "simple rotation". Note rotation has limited use since it's not + // independent of blocktype. + // + // Encode with STBVOX_MAKE_GEOMETRY(geom,simple_rot,0) + + unsigned char *block_tex1; + // Array indexed by blocktype containing the texture id for texture #1. + + unsigned char (*block_tex1_face)[6]; + // Array indexed by blocktype and face containing the texture id for texture #1. + // The N/E/S/W face choices can be rotated by one of the rotation selectors; + // The top & bottom face textures will rotate to match. + // Note that it only makes sense to use one of block_tex1 or block_tex1_face; + // this pattern repeats throughout and this notice is not repeated. + + unsigned char *tex2; + // Indexed by 3D coordinate. Contains the texture id for texture #2 + // to use on all faces of the block. + + unsigned char *block_tex2; + // Array indexed by blocktype containing the texture id for texture #2. + + unsigned char (*block_tex2_face)[6]; + // Array indexed by blocktype and face containing the texture id for texture #2. + // The N/E/S/W face choices can be rotated by one of the rotation selectors; + // The top & bottom face textures will rotate to match. + + unsigned char *color; + // Indexed by 3D coordinate. Contains the color for all faces of the block. + // The core color value is 0..63. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *block_color; + // Array indexed by blocktype containing the color value to apply to the faces. + // The core color value is 0..63. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char (*block_color_face)[6]; + // Array indexed by blocktype and face containing the color value to apply to that face. + // The core color value is 0..63. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *block_texlerp; + // Array indexed by blocktype containing 3-bit scalar for texture #2 alpha + // (known throughout as 'texlerp'). This is constant over every face even + // though the property is potentially per-vertex. + + unsigned char (*block_texlerp_face)[6]; + // Array indexed by blocktype and face containing 3-bit scalar for texture #2 alpha. + // This is constant over the face even though the property is potentially per-vertex. + + unsigned char *block_vheight; + // Array indexed by blocktype containing the vheight values for the + // top or bottom face of this block. These will rotate properly if the + // block is rotated. See discussion of vheight. + // Encode with STBVOX_MAKE_VHEIGHT(sw_height, se_height, nw_height, ne_height) + + unsigned char *selector; + // Array indexed by 3D coordinates indicating which output mesh to select. + + unsigned char *block_selector; + // Array indexed by blocktype indicating which output mesh to select. + + unsigned char *side_texrot; + // Array indexed by 3D coordinates encoding 2-bit texture rotations for the + // faces on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + + unsigned char *block_side_texrot; + // Array indexed by blocktype encoding 2-bit texture rotations for the faces + // on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + + unsigned char *overlay; // index into palettes listed below + // Indexed by 3D coordinate. If 0, there is no overlay. If non-zero, + // it indexes into to the below arrays and overrides the values + // defined by the blocktype. + + unsigned char (*overlay_tex1)[6]; + // Array indexed by overlay value and face, containing an override value + // for the texture id for texture #1. If 0, the value defined by blocktype + // is used. + + unsigned char (*overlay_tex2)[6]; + // Array indexed by overlay value and face, containing an override value + // for the texture id for texture #2. If 0, the value defined by blocktype + // is used. + + unsigned char (*overlay_color)[6]; + // Array indexed by overlay value and face, containing an override value + // for the face color. If 0, the value defined by blocktype is used. + + unsigned char *overlay_side_texrot; + // Array indexed by overlay value, encoding 2-bit texture rotations for the faces + // on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + + unsigned char *rotate; + // Indexed by 3D coordinate. Allows independent rotation of several + // parts of the voxel, where by rotation I mean swapping textures + // and colors between E/N/S/W faces. + // Block: rotates anything indexed by blocktype + // Overlay: rotates anything indexed by overlay + // EColor: rotates faces defined in ecolor_facemask + // Encode with STBVOX_MAKE_MATROT(block,overlay,ecolor) + + unsigned char *tex2_for_tex1; + // Array indexed by tex1 containing the texture id for texture #2. + // You can use this if the two are always/almost-always strictly + // correlated (e.g. if tex2 is a detail texture for tex1), as it + // will be more efficient (touching fewer cache lines) than using + // e.g. block_tex2_face. + + unsigned char *tex2_replace; + // Indexed by 3D coordinate. Specifies the texture id for texture #2 + // to use on a single face of the voxel, which must be E/N/W/S (not U/D). + // The texture id is limited to 6 bits unless tex2_facemask is also + // defined (see below). + // Encode with STBVOX_MAKE_TEX2_REPLACE(tex2, face) + + unsigned char *tex2_facemask; + // Indexed by 3D coordinate. Specifies which of the six faces should + // have their tex2 replaced by the value of tex2_replace. In this + // case, all 8 bits of tex2_replace are used as the texture id. + // Encode with STBVOX_MAKE_FACE_MASK(east,north,west,south,up,down) + + unsigned char *extended_color; + // Indexed by 3D coordinate. Specifies a value that indexes into + // the ecolor arrays below (both of which must be defined). + + unsigned char *ecolor_color; + // Indexed by extended_color value, specifies an optional override + // for the color value on some faces. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *ecolor_facemask; + // Indexed by extended_color value, this specifies which faces the + // color in ecolor_color should be applied to. The faces can be + // independently rotated by the ecolor value of 'rotate', if it exists. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *color2; + // Indexed by 3D coordinates, specifies an alternative color to apply + // to some of the faces of the block. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *color2_facemask; + // Indexed by 3D coordinates, specifies which faces should use the + // color defined in color2. No rotation value is applied. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *color3; + // Indexed by 3D coordinates, specifies an alternative color to apply + // to some of the faces of the block. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *color3_facemask; + // Indexed by 3D coordinates, specifies which faces should use the + // color defined in color3. No rotation value is applied. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *texlerp_simple; + // Indexed by 3D coordinates, this is the smallest texlerp encoding + // that can do useful work. It consits of three values: baselerp, + // vertlerp, and face_vertlerp. Baselerp defines the value + // to use on all of the faces but one, from the STBVOX_TEXLERP_BASE + // values. face_vertlerp is one of the 6 face values (or STBVOX_FACE_NONE) + // which specifies the face should use the vertlerp values. + // Vertlerp defines a lerp value at every vertex of the mesh. + // Thus, one face can have per-vertex texlerp values, and those + // values are encoded in the space so that they will be shared + // by adjacent faces that also use vertlerp, allowing continuity + // (this is used for the "texture crossfade" bit of the release video). + // Encode with STBVOX_MAKE_TEXLERP_SIMPLE(baselerp, vertlerp, face_vertlerp) + + // The following texlerp encodings are experimental and maybe not + // that useful. + + unsigned char *texlerp; + // Indexed by 3D coordinates, this defines four values: + // vertlerp is a lerp value at every vertex of the mesh (using STBVOX_TEXLERP_BASE values). + // ud is the value to use on up and down faces, from STBVOX_TEXLERP_FACE values + // ew is the value to use on east and west faces, from STBVOX_TEXLERP_FACE values + // ns is the value to use on north and south faces, from STBVOX_TEXLERP_FACE values + // If any of ud, ew, or ns is STBVOX_TEXLERP_FACE_use_vert, then the + // vertlerp values for the vertices are gathered and used for those faces. + // Encode with STBVOX_MAKE_TEXLERP(vertlerp,ud,ew,sw) + + unsigned short *texlerp_vert3; + // Indexed by 3D coordinates, this works with texlerp and + // provides a unique texlerp value for every direction at + // every vertex. The same rules of whether faces share values + // applies. The STBVOX_TEXLERP_FACE vertlerp value defined in + // texlerp is only used for the down direction. The values at + // each vertex in other directions are defined in this array, + // and each uses the STBVOX_TEXLERP3 values (i.e. full precision + // 3-bit texlerp values). + // Encode with STBVOX_MAKE_VERT3(vertlerp_e,vertlerp_n,vertlerp_w,vertlerp_s,vertlerp_u) + + unsigned short *texlerp_face3; // e:3,n:3,w:3,s:3,u:2,d:2 + // Indexed by 3D coordinates, this provides a compact way to + // fully specify the texlerp value indepenendly for every face, + // but doesn't allow per-vertex variation. E/N/W/S values are + // encoded using STBVOX_TEXLERP3 values, whereas up and down + // use STBVOX_TEXLERP_SIMPLE values. + // Encode with STBVOX_MAKE_FACE3(face_e,face_n,face_w,face_s,face_u,face_d) + + unsigned char *vheight; // STBVOX_MAKE_VHEIGHT -- sw:2, se:2, nw:2, ne:2, doesn't rotate + // Indexed by 3D coordinates, this defines the four + // vheight values to use if the geometry is STBVOX_GEOM_vheight*. + // See the vheight discussion. + + unsigned char *packed_compact; + // Stores block rotation, vheight, and texlerp values: + // block rotation: 2 bits + // vertex vheight: 2 bits + // use_texlerp : 1 bit + // vertex texlerp: 3 bits + // If STBVOX_CONFIG_UP_TEXLERP_PACKED is defined, then 'vertex texlerp' is + // used for up faces if use_texlerp is 1. If STBVOX_CONFIG_DOWN_TEXLERP_PACKED + // is defined, then 'vertex texlerp' is used for down faces if use_texlerp is 1. + // Note if those symbols are defined but packed_compact is NULL, the normal + // texlerp default will be used. + // Encode with STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, use_texlerp) +}; +// @OPTIMIZE allow specializing; build a single struct with all of the +// 3D-indexed arrays combined so it's AoS instead of SoA for better +// cache efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// VHEIGHT DOCUMENTATION +// +// "vheight" is the internal name for the special block types +// with sloped tops or bottoms. "vheight" stands for "vertex height". +// +// Note that these blocks are very flexible (there are 256 of them, +// although at least 17 of them should never be used), but they +// also have a disadvantage that they generate extra invisible +// faces; the generator does not currently detect whether adjacent +// vheight blocks hide each others sides, so those side faces are +// always generated. For a continuous ground terrain, this means +// that you may generate 5x as many quads as needed. See notes +// on "improvements for shipping products" in the introduction. + +enum +{ + STBVOX_VERTEX_HEIGHT_0, + STBVOX_VERTEX_HEIGHT_half, + STBVOX_VERTEX_HEIGHT_1, + STBVOX_VERTEX_HEIGHT_one_and_a_half, +}; +// These are the "vheight" values. Vheight stands for "vertex height". +// The idea is that for a "floor vheight" block, you take a cube and +// reposition the top-most vertices at various heights as specified by +// the vheight values. Similarly, a "ceiling vheight" block takes a +// cube and repositions the bottom-most vertices. +// +// A floor block only adjusts the top four vertices; the bottom four vertices +// remain at the bottom of the block. The height values are 2 bits, +// measured in halves of a block; so you can specify heights of 0/2, +// 1/2, 2/2, or 3/2. 0 is the bottom of the block, 1 is halfway +// up the block, 2 is the top of the block, and 3 is halfway up the +// next block (and actually outside of the block). The value 3 is +// actually legal for floor vheight (but not ceiling), and allows you to: +// +// (A) have smoother terrain by having slopes that cross blocks, +// e.g. (1,1,3,3) is a regular-seeming slope halfway between blocks +// (B) make slopes steeper than 45-degrees, e.g. (0,0,3,3) +// +// (Because only z coordinates have half-block precision, and x&y are +// limited to block corner precision, it's not possible to make these +// things "properly" out of blocks, e.g. a half-slope block on its side +// or a sloped block halfway between blocks that's made out of two blocks.) +// +// If you define STBVOX_CONFIG_OPTIMIZED_VHEIGHT, then the top face +// (or bottom face for a ceiling vheight block) will be drawn as a +// single quad even if the four vertex heights aren't planar, and a +// single normal will be used over the entire quad. If you +// don't define it, then if the top face is non-planar, it will be +// split into two triangles, each with their own normal/lighting. +// (Note that since all output from stb_voxel_render is quad meshes, +// triangles are actually rendered as degenerate quads.) In this case, +// the distinction betwen STBVOX_GEOM_floor_vheight_03 and +// STBVOX_GEOM_floor_vheight_12 comes into play; the former introduces +// an edge from the SW to NE corner (i.e. from <0,0,?> to <1,1,?>), +// while the latter introduces an edge from the NW to SE corner +// (i.e. from <0,1,?> to <1,0,?>.) For a "lazy mesh" look, use +// exclusively _03 or _12. For a "classic mesh" look, alternate +// _03 and _12 in a checkerboard pattern. For a "smoothest surface" +// look, choose the edge based on actual vertex heights. +// +// The four vertex heights can come from several places. The simplest +// encoding is to just use the 'vheight' parameter which stores four +// explicit vertex heights for every block. This allows total independence, +// but at the cost of the largest memory usage, 1 byte per 3D block. +// Encode this with STBVOX_MAKE_VHEIGHT(vh_sw, vh_se, vh_nw, vh_ne). +// These coordinates are absolute, not affected by block rotations. +// +// An alternative if you just want to encode some very specific block +// types, not all the possibilities--say you just want half-height slopes, +// so you want (0,0,1,1) and (1,1,2,2)--then you can use block_vheight +// to specify them. The geometry rotation will cause block_vheight values +// to be rotated (because it's as if you're just defining a type of +// block). This value is also encoded with STBVOX_MAKE_VHEIGHT. +// +// If you want to save memory and you're creating a "continuous ground" +// sort of effect, you can make each vertex of the lattice share the +// vheight value; that is, two adjacent blocks that share a vertex will +// always get the same vheight value for that vertex. Then you need to +// store two bits of vheight for every block, which you do by storing it +// as part another data structure. Store the south-west vertex's vheight +// with the block. You can either use the "geometry" mesh variable (it's +// a parameter to STBVOX_MAKE_GEOMETRY) or you can store it in the +// "lighting" mesh variable if you defined STBVOX_CONFIG_VHEIGHT_IN_LIGHTING, +// using STBVOX_MAKE_LIGHTING_EXT(lighting,vheight). +// +// Note that if you start with a 2D height map and generate vheight data from +// it, you don't necessarily store only one value per (x,y) coordinate, +// as the same value may need to be set up at multiple z heights. For +// example, if height(8,8) = 13.5, then you want the block at (8,8,13) +// to store STBVOX_VERTEX_HEIGHT_half, and this will be used by blocks +// at (7,7,13), (8,7,13), (7,8,13), and (8,8,13). However, if you're +// allowing steep slopes, it might be the case that you have a block +// at (7,7,12) which is supposed to stick up to 13.5; that means +// you also need to store STBVOX_VERTEX_HEIGHT_one_and_a_half at (8,8,12). + +enum +{ + STBVOX_TEXLERP_FACE_0, + STBVOX_TEXLERP_FACE_half, + STBVOX_TEXLERP_FACE_1, + STBVOX_TEXLERP_FACE_use_vert, +}; + +enum +{ + STBVOX_TEXLERP_BASE_0, // 0.0 + STBVOX_TEXLERP_BASE_2_7, // 2/7 + STBVOX_TEXLERP_BASE_5_7, // 4/7 + STBVOX_TEXLERP_BASE_1 // 1.0 +}; + +enum +{ + STBVOX_TEXLERP3_0_8, + STBVOX_TEXLERP3_1_8, + STBVOX_TEXLERP3_2_8, + STBVOX_TEXLERP3_3_8, + STBVOX_TEXLERP3_4_8, + STBVOX_TEXLERP3_5_8, + STBVOX_TEXLERP3_6_8, + STBVOX_TEXLERP3_7_8, +}; + +#define STBVOX_FACE_NONE 7 + +#define STBVOX_BLOCKTYPE_EMPTY 0 + +#ifdef STBVOX_BLOCKTYPE_SHORT +#define STBVOX_BLOCKTYPE_HOLE 65535 +#else +#define STBVOX_BLOCKTYPE_HOLE 255 +#endif + +#define STBVOX_MAKE_GEOMETRY(geom, rotate, vheight) ((geom) + (rotate)*16 + (vheight)*64) +#define STBVOX_MAKE_VHEIGHT(v_sw, v_se, v_nw, v_ne) ((v_sw) + (v_se)*4 + (v_nw)*16 + (v_ne)*64) +#define STBVOX_MAKE_MATROT(block, overlay, color) ((block) + (overlay)*4 + (color)*64) +#define STBVOX_MAKE_TEX2_REPLACE(tex2, tex2_replace_face) ((tex2) + ((tex2_replace_face) & 3)*64) +#define STBVOX_MAKE_TEXLERP(ns2, ew2, ud2, vert) ((ew2) + (ns2)*4 + (ud2)*16 + (vert)*64) +#define STBVOX_MAKE_TEXLERP_SIMPLE(baselerp,vert,face) ((vert)*32 + (face)*4 + (baselerp)) +#define STBVOX_MAKE_TEXLERP1(vert,e2,n2,w2,s2,u4,d2) STBVOX_MAKE_TEXLERP(s2, w2, d2, vert) +#define STBVOX_MAKE_TEXLERP2(vert,e2,n2,w2,s2,u4,d2) ((u2)*16 + (n2)*4 + (s2)) +#define STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) ((e)+(n)*2+(w)*4+(s)*8+(u)*16+(d)*32) +#define STBVOX_MAKE_SIDE_TEXROT(e,n,w,s) ((e)+(n)*4+(w)*16+(s)*64) +#define STBVOX_MAKE_COLOR(color,t1,t2) ((color)+(t1)*64+(t2)*128) +#define STBVOX_MAKE_TEXLERP_VERT3(e,n,w,s,u) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096) +#define STBVOX_MAKE_TEXLERP_FACE3(e,n,w,s,u,d) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096+(d)*16384) +#define STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, def) ((rot)+4*(vheight)+16*(use)+32*(texlerp)) + +#define STBVOX_MAKE_LIGHTING_EXT(lighting, rot) (((lighting)&~3)+(rot)) +#define STBVOX_MAKE_LIGHTING(lighting) (lighting) + +#ifndef STBVOX_MAX_MESHES +#define STBVOX_MAX_MESHES 2 // opaque & transparent +#endif + +#define STBVOX_MAX_MESH_SLOTS 3 // one vertex & two faces, or two vertex and one face + + +// don't mess with this directly, it's just here so you can +// declare stbvox_mesh_maker on the stack or as a global +struct stbvox_mesh_maker +{ + stbvox_input_description input; + int cur_x, cur_y, cur_z; // last unprocessed voxel if it splits into multiple buffers + int x0,y0,z0,x1,y1,z1; + int x_stride_in_bytes; + int y_stride_in_bytes; + int config_dirty; + int default_mesh; + unsigned int tags; + + int cube_vertex_offset[6][4]; // this allows access per-vertex data stored block-centered (like texlerp, ambient) + int vertex_gather_offset[6][4]; + + int pos_x,pos_y,pos_z; + int full; + + // computed from user input + char *output_cur [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + char *output_end [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + char *output_buffer[STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + int output_len [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + + // computed from config + int output_size [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; // per quad + int output_step [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; // per vertex or per face, depending + int num_mesh_slots; + + float default_tex_scale[128][2]; +}; + +#endif // INCLUDE_STB_VOXEL_RENDER_H + + +#ifdef STB_VOXEL_RENDER_IMPLEMENTATION + +#include <stdlib.h> +#include <assert.h> +#include <string.h> // memset + +// have to use our own names to avoid the _MSC_VER path having conflicting type names +#ifndef _MSC_VER + #include <stdint.h> + typedef uint16_t stbvox_uint16; + typedef uint32_t stbvox_uint32; +#else + typedef unsigned short stbvox_uint16; + typedef unsigned int stbvox_uint32; +#endif + +#ifdef _MSC_VER + #define STBVOX_NOTUSED(v) (void)(v) +#else + #define STBVOX_NOTUSED(v) (void)sizeof(v) +#endif + + + +#ifndef STBVOX_CONFIG_MODE +#error "Must defined STBVOX_CONFIG_MODE to select the mode" +#endif + +#if defined(STBVOX_CONFIG_ROTATION_IN_LIGHTING) && defined(STBVOX_CONFIG_VHEIGHT_IN_LIGHTING) +#error "Can't store both rotation and vheight in lighting" +#endif + + +// The following are candidate voxel modes. Only modes 0, 1, and 20, and 21 are +// currently implemented. Reducing the storage-per-quad further +// shouldn't improve performance, although obviously it allow you +// to create larger worlds without streaming. +// +// +// ----------- Two textures ----------- -- One texture -- ---- Color only ---- +// Mode: 0 1 2 3 4 5 6 10 11 12 20 21 22 23 24 +// ============================================================================================================ +// uses Tex Buffer n Y Y Y Y Y Y Y Y Y n Y Y Y Y +// bytes per quad 32 20 14 12 10 6 6 8 8 4 32 20 10 6 4 +// non-blocks all all some some some slabs stairs some some none all all slabs slabs none +// tex1 256 256 256 256 256 256 256 256 256 256 n n n n n +// tex2 256 256 256 256 256 256 128 n n n n n n n n +// colors 64 64 64 64 64 64 64 8 n n 2^24 2^24 2^24 2^24 256 +// vertex ao Y Y Y Y Y n n Y Y n Y Y Y n n +// vertex texlerp Y Y Y n n n n - - - - - - - - +// x&y extents 127 127 128 64 64 128 64 64 128 128 127 127 128 128 128 +// z extents 255 255 128 64? 64? 64 64 32 64 128 255 255 128 64 128 + +// not sure why I only wrote down the above "result data" and didn't preserve +// the vertex formats, but here I've tried to reconstruct the designs... +// mode # 3 is wrong, one byte too large, but they may have been an error originally + +// Mode: 0 1 2 3 4 5 6 10 11 12 20 21 22 23 24 +// ============================================================================================================= +// bytes per quad 32 20 14 12 10 6 6 8 8 4 20 10 6 4 +// +// vertex x bits 7 7 0 6 0 0 0 0 0 0 7 0 0 0 +// vertex y bits 7 7 0 0 0 0 0 0 0 0 7 0 0 0 +// vertex z bits 9 9 7 4 2 0 0 2 2 0 9 2 0 0 +// vertex ao bits 6 6 6 6 6 0 0 6 6 0 6 6 0 0 +// vertex txl bits 3 3 3 0 0 0 0 0 0 0 (3) 0 0 0 +// +// face tex1 bits (8) 8 8 8 8 8 8 8 8 8 +// face tex2 bits (8) 8 8 8 8 8 7 - - - +// face color bits (8) 8 8 8 8 8 8 3 0 0 24 24 24 8 +// face normal bits (8) 8 8 8 6 4 7 4 4 3 8 3 4 3 +// face x bits 7 0 6 7 6 6 7 7 0 7 7 7 +// face y bits 7 6 6 7 6 6 7 7 0 7 7 7 +// face z bits 2 2 6 6 6 5 6 7 0 7 6 7 + + +#if STBVOX_CONFIG_MODE==0 || STBVOX_CONFIG_MODE==1 + + #define STBVOX_ICONFIG_VERTEX_32 + #define STBVOX_ICONFIG_FACE1_1 + +#elif STBVOX_CONFIG_MODE==20 || STBVOX_CONFIG_MODE==21 + + #define STBVOX_ICONFIG_VERTEX_32 + #define STBVOX_ICONFIG_FACE1_1 + #define STBVOX_ICONFIG_UNTEXTURED + +#else +#error "Selected value of STBVOX_CONFIG_MODE is not supported" +#endif + +#if STBVOX_CONFIG_MODE==0 || STBVOX_CONFIG_MODE==20 +#define STBVOX_ICONFIG_FACE_ATTRIBUTE +#endif + +#ifndef STBVOX_CONFIG_HLSL +// the fallback if all others are exhausted is GLSL +#define STBVOX_ICONFIG_GLSL +#endif + +#ifdef STBVOX_CONFIG_OPENGL_MODELVIEW +#define STBVOX_ICONFIG_OPENGL_3_1_COMPATIBILITY +#endif + +#if defined(STBVOX_ICONFIG_VERTEX_32) + typedef stbvox_uint32 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint32) ((x)+((y)<<7)+((z)<<14)+((ao)<<23)+((texlerp)<<29))) +#elif defined(STBVOX_ICONFIG_VERTEX_16_1) // mode=2 + typedef stbvox_uint16 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint16) ((z)+((ao)<<7)+((texlerp)<<13) +#elif defined(STBVOX_ICONFIG_VERTEX_16_2) // mode=3 + typedef stbvox_uint16 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint16) ((x)+((z)<<6))+((ao)<<10)) +#elif defined(STBVOX_ICONFIG_VERTEX_8) + typedef stbvox_uint8 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint8) ((z)+((ao)<<6)) +#else + #error "internal error, no vertex type" +#endif + +#ifdef STBVOX_ICONFIG_FACE1_1 + typedef struct + { + unsigned char tex1,tex2,color,face_info; + } stbvox_mesh_face; +#else + #error "internal error, no face type" +#endif + + +// 20-byte quad format: +// +// per vertex: +// +// x:7 +// y:7 +// z:9 +// ao:6 +// tex_lerp:3 +// +// per face: +// +// tex1:8 +// tex2:8 +// face:8 +// color:8 + + +// Faces: +// +// Faces use the bottom 3 bits to choose the texgen +// mode, and all the bits to choose the normal. +// Thus the bottom 3 bits have to be: +// e, n, w, s, u, d, u, d +// +// These use compact names so tables are readable + +enum +{ + STBVF_e, + STBVF_n, + STBVF_w, + STBVF_s, + STBVF_u, + STBVF_d, + STBVF_eu, + STBVF_ed, + + STBVF_eu_wall, + STBVF_nu_wall, + STBVF_wu_wall, + STBVF_su_wall, + STBVF_ne_u, + STBVF_ne_d, + STBVF_nu, + STBVF_nd, + + STBVF_ed_wall, + STBVF_nd_wall, + STBVF_wd_wall, + STBVF_sd_wall, + STBVF_nw_u, + STBVF_nw_d, + STBVF_wu, + STBVF_wd, + + STBVF_ne_u_cross, + STBVF_nw_u_cross, + STBVF_sw_u_cross, + STBVF_se_u_cross, + STBVF_sw_u, + STBVF_sw_d, + STBVF_su, + STBVF_sd, + + // @TODO we need more than 5 bits to encode the normal to fit the following + // so for now we use the right projection but the wrong normal + STBVF_se_u = STBVF_su, + STBVF_se_d = STBVF_sd, + + STBVF_count, +}; + +///////////////////////////////////////////////////////////////////////////// +// +// tables -- i'd prefer if these were at the end of the file, but: C++ +// + +static float stbvox_default_texgen[2][32][3] = +{ + { { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, { 0, 0,-1 }, + { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, { 0, 0,-1 }, + { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, { 0, 0,-1 }, + { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, { 0, 0,-1 }, + + { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, + { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, + { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, + { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, + }, + { { 0, 0,-1 }, { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, + { 0, 0,-1 }, { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, + { 0, 0,-1 }, { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, + { 0, 0,-1 }, { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, + + { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, + { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, + { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, + { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, + }, +}; + +#define STBVOX_RSQRT2 0.7071067811865f +#define STBVOX_RSQRT3 0.5773502691896f + +static float stbvox_default_normals[32][3] = +{ + { 1,0,0 }, // east + { 0,1,0 }, // north + { -1,0,0 }, // west + { 0,-1,0 }, // south + { 0,0,1 }, // up + { 0,0,-1 }, // down + { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up + { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down + + { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up + { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up + { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up + { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up + { STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // ne & up + { STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // ne & down + { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up + { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down + + { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down + { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down + { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down + { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down + { -STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // NW & up + { -STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // NW & down + { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up + { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down + + { STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NE & up crossed + { -STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NW & up crossed + { -STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SW & up crossed + { STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SE & up crossed + { -STBVOX_RSQRT3,-STBVOX_RSQRT3, STBVOX_RSQRT3 }, // SW & up + { -STBVOX_RSQRT3,-STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // SW & up + { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up + { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down +}; + +static float stbvox_default_texscale[128][4] = +{ + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, +}; + +static unsigned char stbvox_default_palette_compact[64][3] = +{ + { 255,255,255 }, { 238,238,238 }, { 221,221,221 }, { 204,204,204 }, + { 187,187,187 }, { 170,170,170 }, { 153,153,153 }, { 136,136,136 }, + { 119,119,119 }, { 102,102,102 }, { 85, 85, 85 }, { 68, 68, 68 }, + { 51, 51, 51 }, { 34, 34, 34 }, { 17, 17, 17 }, { 0, 0, 0 }, + { 255,240,240 }, { 255,220,220 }, { 255,160,160 }, { 255, 32, 32 }, + { 200,120,160 }, { 200, 60,150 }, { 220,100,130 }, { 255, 0,128 }, + { 240,240,255 }, { 220,220,255 }, { 160,160,255 }, { 32, 32,255 }, + { 120,160,200 }, { 60,150,200 }, { 100,130,220 }, { 0,128,255 }, + { 240,255,240 }, { 220,255,220 }, { 160,255,160 }, { 32,255, 32 }, + { 160,200,120 }, { 150,200, 60 }, { 130,220,100 }, { 128,255, 0 }, + { 255,255,240 }, { 255,255,220 }, { 220,220,180 }, { 255,255, 32 }, + { 200,160,120 }, { 200,150, 60 }, { 220,130,100 }, { 255,128, 0 }, + { 255,240,255 }, { 255,220,255 }, { 220,180,220 }, { 255, 32,255 }, + { 160,120,200 }, { 150, 60,200 }, { 130,100,220 }, { 128, 0,255 }, + { 240,255,255 }, { 220,255,255 }, { 180,220,220 }, { 32,255,255 }, + { 120,200,160 }, { 60,200,150 }, { 100,220,130 }, { 0,255,128 }, +}; + +static float stbvox_default_ambient[4][4] = +{ + { 0,0,1 ,0 }, // reversed lighting direction + { 0.5,0.5,0.5,0 }, // directional color + { 0.5,0.5,0.5,0 }, // constant color + { 0.5,0.5,0.5,1.0f/1000.0f/1000.0f }, // fog data for simple_fog +}; + +static float stbvox_default_palette[64][4]; + +static void stbvox_build_default_palette(void) +{ + int i; + for (i=0; i < 64; ++i) { + stbvox_default_palette[i][0] = stbvox_default_palette_compact[i][0] / 255.0f; + stbvox_default_palette[i][1] = stbvox_default_palette_compact[i][1] / 255.0f; + stbvox_default_palette[i][2] = stbvox_default_palette_compact[i][2] / 255.0f; + stbvox_default_palette[i][3] = 1.0f; + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Shaders +// + +#if defined(STBVOX_ICONFIG_OPENGL_3_1_COMPATIBILITY) + #define STBVOX_SHADER_VERSION "#version 150 compatibility\n" +#elif defined(STBVOX_ICONFIG_OPENGL_3_0) + #define STBVOX_SHADER_VERSION "#version 130\n" +#elif defined(STBVOX_ICONFIG_GLSL) + #define STBVOX_SHADER_VERSION "#version 150\n" +#else + #define STBVOX_SHADER_VERSION "" +#endif + +static const char *stbvox_vertex_program = +{ + STBVOX_SHADER_VERSION + + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE // NOT TAG_face_sampled + "in uvec4 attr_face;\n" + #else + "uniform usamplerBuffer facearray;\n" + #endif + + #ifdef STBVOX_ICONFIG_FACE_ARRAY_2 + "uniform usamplerBuffer facearray2;\n" + #endif + + // vertex input data + "in uint attr_vertex;\n" + + // per-buffer data + "uniform vec3 transform[3];\n" + + // per-frame data + "uniform vec4 camera_pos;\n" // 4th value is used for arbitrary hacking + + // to simplify things, we avoid using more than 256 uniform vectors + // in fragment shader to avoid possible 1024 component limit, so + // we access this table in the fragment shader. + "uniform vec3 normal_table[32];\n" + + #ifndef STBVOX_CONFIG_OPENGL_MODELVIEW + "uniform mat44 model_view;\n" + #endif + + // fragment output data + "flat out uvec4 facedata;\n" + " out vec3 voxelspace_pos;\n" + " out vec3 vnormal;\n" + " out float texlerp;\n" + " out float amb_occ;\n" + + // @TODO handle the HLSL way to do this + "void main()\n" + "{\n" + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + " facedata = attr_face;\n" + #else + " int faceID = gl_VertexID >> 2;\n" + " facedata = texelFetch(facearray, faceID);\n" + #endif + + // extract data for vertex + " vec3 offset;\n" + " offset.x = float( (attr_vertex ) & 127u );\n" // a[0..6] + " offset.y = float( (attr_vertex >> 7u) & 127u );\n" // a[7..13] + " offset.z = float( (attr_vertex >> 14u) & 511u );\n" // a[14..22] + " amb_occ = float( (attr_vertex >> 23u) & 63u ) / 63.0;\n" // a[23..28] + " texlerp = float( (attr_vertex >> 29u) ) / 7.0;\n" // a[29..31] + + " vnormal = normal_table[(facedata.w>>2u) & 31u];\n" + " voxelspace_pos = offset * transform[0];\n" // mesh-to-object scale + " vec3 position = voxelspace_pos + transform[1];\n" // mesh-to-object translate + + #ifdef STBVOX_DEBUG_TEST_NORMALS + " if ((facedata.w & 28u) == 16u || (facedata.w & 28u) == 24u)\n" + " position += vnormal.xyz * camera_pos.w;\n" + #endif + + #ifndef STBVOX_CONFIG_OPENGL_MODELVIEW + " gl_Position = model_view * vec4(position,1.0);\n" + #else + " gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n" + #endif + + "}\n" +}; + + +static const char *stbvox_fragment_program = +{ + STBVOX_SHADER_VERSION + + // rlerp is lerp but with t on the left, like god intended + #if defined(STBVOX_ICONFIG_GLSL) + "#define rlerp(t,x,y) mix(x,y,t)\n" + #elif defined(STBVOX_CONFIG_HLSL) + "#define rlerp(t,x,y) lerp(x,y,t)\n" + #else + #error "need definition of rlerp()" + #endif + + + // vertex-shader output data + "flat in uvec4 facedata;\n" + " in vec3 voxelspace_pos;\n" + " in vec3 vnormal;\n" + " in float texlerp;\n" + " in float amb_occ;\n" + + // per-buffer data + "uniform vec3 transform[3];\n" + + // per-frame data + "uniform vec4 camera_pos;\n" // 4th value is used for arbitrary hacking + + // probably constant data + "uniform vec4 ambient[4];\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // generally constant data + "uniform sampler2DArray tex_array[2];\n" + + #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER + "uniform samplerBuffer color_table;\n" + "uniform samplerBuffer texscale;\n" + "uniform samplerBuffer texgen;\n" + #else + "uniform vec4 color_table[64];\n" + "uniform vec4 texscale[64];\n" // instead of 128, to avoid running out of uniforms + "uniform vec3 texgen[64];\n" + #endif + #endif + + "out vec4 outcolor;\n" + + #if defined(STBVOX_CONFIG_LIGHTING) || defined(STBVOX_CONFIG_LIGHTING_SIMPLE) + "vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient);\n" + #endif + #if defined(STBVOX_CONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) + "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha);\n" + #endif + + "void main()\n" + "{\n" + " vec3 albedo;\n" + " float fragment_alpha;\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // unpack the values + " uint tex1_id = facedata.x;\n" + " uint tex2_id = facedata.y;\n" + " uint texprojid = facedata.w & 31u;\n" + " uint color_id = facedata.z;\n" + + #ifndef STBVOX_CONFIG_PREFER_TEXBUFFER + // load from uniforms / texture buffers + " vec3 texgen_s = texgen[texprojid];\n" + " vec3 texgen_t = texgen[texprojid+32u];\n" + " float tex1_scale = texscale[tex1_id & 63u].x;\n" + " vec4 color = color_table[color_id & 63u];\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " vec4 tex2_props = texscale[tex2_id & 63u];\n" + #endif + #else + " vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n" + " vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n" + " float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n" + " vec4 color = texelFetch(color_table, int(color_id & 63u));\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " vec4 tex2_props = texelFetch(texscale, int(tex1_id & 127u));\n" + #endif + #endif + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " float tex2_scale = tex2_props.y;\n" + " bool texblend_mode = tex2_props.z != 0.0;\n" + #endif + " vec2 texcoord;\n" + " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" + " texcoord.s = dot(texturespace_pos, texgen_s);\n" + " texcoord.t = dot(texturespace_pos, texgen_t);\n" + + " vec2 texcoord_1 = tex1_scale * texcoord;\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " vec2 texcoord_2 = tex2_scale * texcoord;\n" + #endif + + #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP + " texcoord_1 = texcoord_1 - floor(texcoord_1);\n" + " vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n" + #else + " vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n" + #endif + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP + " texcoord_2 = texcoord_2 - floor(texcoord_2);\n" + " vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n" + #else + " vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n" + #endif + #endif + + " bool emissive = (color.a > 1.0);\n" + " color.a = min(color.a, 1.0);\n" + + // recolor textures + " if ((color_id & 64u) != 0u) tex1.rgba *= color.rgba;\n" + " fragment_alpha = tex1.a;\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " if ((color_id & 128u) != 0u) tex2.rgba *= color.rgba;\n" + + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " tex2.rgba *= texlerp;\n" + #else + " tex2.a *= texlerp;\n" + #endif + + " if (texblend_mode)\n" + " albedo = tex1.xyz * rlerp(tex2.a, vec3(1.0,1.0,1.0), 2.0*tex2.xyz);\n" + " else {\n" + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " albedo = (1.0-tex2.a)*tex1.xyz + tex2.xyz;\n" + #else + " albedo = rlerp(tex2.a, tex1.xyz, tex2.xyz);\n" + #endif + " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" + " }\n" + #else + " albedo = tex1.xyz;\n" + #endif + + #else // UNTEXTURED + " vec4 color;" + " color.xyz = vec3(facedata.xyz) / 255.0;\n" + " bool emissive = false;\n" + " albedo = color.xyz;\n" + " fragment_alpha = 1.0;\n" + #endif + + #ifdef STBVOX_ICONFIG_VARYING_VERTEX_NORMALS + // currently, there are no modes that trigger this path; idea is that there + // could be a couple of bits per vertex to perturb the normal to e.g. get curved look + " vec3 normal = normalize(vnormal);\n" + #else + " vec3 normal = vnormal;\n" + #endif + + " vec3 ambient_color = dot(normal, ambient[0].xyz) * ambient[1].xyz + ambient[2].xyz;\n" + + " ambient_color = clamp(ambient_color, 0.0, 1.0);" + " ambient_color *= amb_occ;\n" + + " vec3 lit_color;\n" + " if (!emissive)\n" + #if defined(STBVOX_ICONFIG_LIGHTING) || defined(STBVOX_CONFIG_LIGHTING_SIMPLE) + " lit_color = compute_lighting(voxelspace_pos + transform[1], normal, albedo, ambient_color);\n" + #else + " lit_color = albedo * ambient_color ;\n" + #endif + " else\n" + " lit_color = albedo;\n" + + #if defined(STBVOX_ICONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) + " vec3 dist = voxelspace_pos + (transform[1] - camera_pos.xyz);\n" + " lit_color = compute_fog(lit_color, dist, fragment_alpha);\n" + #endif + + #ifdef STBVOX_CONFIG_UNPREMULTIPLY + " vec4 final_color = vec4(lit_color/fragment_alpha, fragment_alpha);\n" + #else + " vec4 final_color = vec4(lit_color, fragment_alpha);\n" + #endif + " outcolor = final_color;\n" + "}\n" + + #ifdef STBVOX_CONFIG_LIGHTING_SIMPLE + "\n" + "uniform vec3 light_source[2];\n" + "vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient)\n" + "{\n" + " vec3 light_dir = light_source[0] - pos;\n" + " float lambert = dot(light_dir, norm) / dot(light_dir, light_dir);\n" + " vec3 diffuse = clamp(light_source[1] * clamp(lambert, 0.0, 1.0), 0.0, 1.0);\n" + " return (diffuse + ambient) * albedo;\n" + "}\n" + #endif + + #ifdef STBVOX_CONFIG_FOG_SMOOTHSTEP + "\n" + "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha)\n" + "{\n" + " float f = dot(relative_pos,relative_pos)*ambient[3].w;\n" + //" f = rlerp(f, -2,1);\n" + " f = clamp(f, 0.0, 1.0);\n" + " f = 3.0*f*f - 2.0*f*f*f;\n" // smoothstep + //" f = f*f;\n" // fade in more smoothly + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " return rlerp(f, color.xyz, ambient[3].xyz*fragment_alpha);\n" + #else + " return rlerp(f, color.xyz, ambient[3].xyz);\n" + #endif + "}\n" + #endif +}; + + +// still requires full alpha lookups, including tex2 if texblend is enabled +static const char *stbvox_fragment_program_alpha_only = +{ + STBVOX_SHADER_VERSION + + // vertex-shader output data + "flat in uvec4 facedata;\n" + " in vec3 voxelspace_pos;\n" + " in float texlerp;\n" + + // per-buffer data + "uniform vec3 transform[3];\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // generally constant data + "uniform sampler2DArray tex_array[2];\n" + + #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER + "uniform samplerBuffer texscale;\n" + "uniform samplerBuffer texgen;\n" + #else + "uniform vec4 texscale[64];\n" // instead of 128, to avoid running out of uniforms + "uniform vec3 texgen[64];\n" + #endif + #endif + + "out vec4 outcolor;\n" + + "void main()\n" + "{\n" + " vec3 albedo;\n" + " float fragment_alpha;\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // unpack the values + " uint tex1_id = facedata.x;\n" + " uint tex2_id = facedata.y;\n" + " uint texprojid = facedata.w & 31u;\n" + " uint color_id = facedata.z;\n" + + #ifndef STBVOX_CONFIG_PREFER_TEXBUFFER + // load from uniforms / texture buffers + " vec3 texgen_s = texgen[texprojid];\n" + " vec3 texgen_t = texgen[texprojid+32u];\n" + " float tex1_scale = texscale[tex1_id & 63u].x;\n" + " vec4 color = color_table[color_id & 63u];\n" + " vec4 tex2_props = texscale[tex2_id & 63u];\n" + #else + " vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n" + " vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n" + " float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n" + " vec4 color = texelFetch(color_table, int(color_id & 63u));\n" + " vec4 tex2_props = texelFetch(texscale, int(tex2_id & 127u));\n" + #endif + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " float tex2_scale = tex2_props.y;\n" + " bool texblend_mode = tex2_props.z &((facedata.w & 128u) != 0u);\n" + #endif + + " color.a = min(color.a, 1.0);\n" + + " vec2 texcoord;\n" + " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" + " texcoord.s = dot(texturespace_pos, texgen_s);\n" + " texcoord.t = dot(texturespace_pos, texgen_t);\n" + + " vec2 texcoord_1 = tex1_scale * texcoord;\n" + " vec2 texcoord_2 = tex2_scale * texcoord;\n" + + #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP + " texcoord_1 = texcoord_1 - floor(texcoord_1);\n" + " vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n" + #else + " vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n" + #endif + + " if ((color_id & 64u) != 0u) tex1.a *= color.a;\n" + " fragment_alpha = tex1.a;\n" + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " if (!texblend_mode) {\n" + #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP + " texcoord_2 = texcoord_2 - floor(texcoord_2);\n" + " vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n" + #else + " vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n" + #endif + + " tex2.a *= texlerp;\n" + " if ((color_id & 128u) != 0u) tex2.rgba *= color.a;\n" + " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" + "}\n" + "\n" + #endif + + #else // UNTEXTURED + " fragment_alpha = 1.0;\n" + #endif + + " outcolor = vec4(0.0, 0.0, 0.0, fragment_alpha);\n" + "}\n" +}; + + +STBVXDEC char *stbvox_get_vertex_shader(void) +{ + return (char *) stbvox_vertex_program; +} + +STBVXDEC char *stbvox_get_fragment_shader(void) +{ + return (char *) stbvox_fragment_program; +} + +STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void) +{ + return (char *) stbvox_fragment_program_alpha_only; +} + +static float stbvox_dummy_transform[3][3]; + +#ifdef STBVOX_CONFIG_PREFER_TEXBUFFER +#define STBVOX_TEXBUF 1 +#else +#define STBVOX_TEXBUF 0 +#endif + +static stbvox_uniform_info stbvox_uniforms[] = +{ + { STBVOX_UNIFORM_TYPE_sampler , 4, 1, (char*) "facearray" , 0 }, + { STBVOX_UNIFORM_TYPE_vec3 , 12, 3, (char*) "transform" , stbvox_dummy_transform[0] }, + { STBVOX_UNIFORM_TYPE_sampler , 4, 2, (char*) "tex_array" , 0 }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 128, (char*) "texscale" , stbvox_default_texscale[0] , STBVOX_TEXBUF }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 64, (char*) "color_table" , stbvox_default_palette[0] , STBVOX_TEXBUF }, + { STBVOX_UNIFORM_TYPE_vec3 , 12, 32, (char*) "normal_table" , stbvox_default_normals[0] }, + { STBVOX_UNIFORM_TYPE_vec3 , 12, 64, (char*) "texgen" , stbvox_default_texgen[0][0], STBVOX_TEXBUF }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 4, (char*) "ambient" , stbvox_default_ambient[0] }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 1, (char*) "camera_pos" , stbvox_dummy_transform[0] }, +}; + +STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform) +{ + if (uniform < 0 || uniform >= STBVOX_UNIFORM_count) + return 0; + + *info = stbvox_uniforms[uniform]; + return 1; +} + +#define STBVOX_GET_GEO(geom_data) ((geom_data) & 15) + +typedef struct +{ + unsigned char block:2; + unsigned char overlay:2; + unsigned char facerot:2; + unsigned char ecolor:2; +} stbvox_rotate; + +typedef struct +{ + unsigned char x,y,z; +} stbvox_pos; + +static unsigned char stbvox_rotate_face[6][4] = +{ + { 0,1,2,3 }, + { 1,2,3,0 }, + { 2,3,0,1 }, + { 3,0,1,2 }, + { 4,4,4,4 }, + { 5,5,5,5 }, +}; + +#define STBVOX_ROTATE(x,r) stbvox_rotate_face[x][r] // (((x)+(r))&3) + +stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, int normal) +{ + stbvox_mesh_face face_data = { 0 }; + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char bt_face = STBVOX_ROTATE(face, rot.block); + int facerot = rot.facerot; + + #ifdef STBVOX_ICONFIG_UNTEXTURED + if (mm->input.rgb) { + face_data.tex1 = mm->input.rgb[v_off].r; + face_data.tex2 = mm->input.rgb[v_off].g; + face_data.color = mm->input.rgb[v_off].b; + face_data.face_info = (normal<<2); + return face_data; + } + #else + unsigned char color_face; + + if (mm->input.color) + face_data.color = mm->input.color[v_off]; + + if (mm->input.block_tex1) + face_data.tex1 = mm->input.block_tex1[bt]; + else if (mm->input.block_tex1_face) + face_data.tex1 = mm->input.block_tex1_face[bt][bt_face]; + else + face_data.tex1 = bt; + + if (mm->input.block_tex2) + face_data.tex2 = mm->input.block_tex2[bt]; + else if (mm->input.block_tex2_face) + face_data.tex2 = mm->input.block_tex2_face[bt][bt_face]; + + if (mm->input.block_color) { + unsigned char mcol = mm->input.block_color[bt]; + if (mcol) + face_data.color = mcol; + } else if (mm->input.block_color_face) { + unsigned char mcol = mm->input.block_color_face[bt][bt_face]; + if (mcol) + face_data.color = mcol; + } + + if (face <= STBVOX_FACE_south) { + if (mm->input.side_texrot) + facerot = mm->input.side_texrot[v_off] >> (2 * face); + else if (mm->input.block_side_texrot) + facerot = mm->input.block_side_texrot[v_off] >> (2 * bt_face); + } + + if (mm->input.overlay) { + int over_face = STBVOX_ROTATE(face, rot.overlay); + unsigned char over = mm->input.overlay[v_off]; + if (over) { + if (mm->input.overlay_tex1) { + unsigned char rep1 = mm->input.overlay_tex1[over][over_face]; + if (rep1) + face_data.tex1 = rep1; + } + if (mm->input.overlay_tex2) { + unsigned char rep2 = mm->input.overlay_tex2[over][over_face]; + if (rep2) + face_data.tex2 = rep2; + } + if (mm->input.overlay_color) { + unsigned char rep3 = mm->input.overlay_color[over][over_face]; + if (rep3) + face_data.color = rep3; + } + + if (mm->input.overlay_side_texrot && face <= STBVOX_FACE_south) + facerot = mm->input.overlay_side_texrot[over] >> (2*over_face); + } + } + + if (mm->input.tex2_for_tex1) + face_data.tex2 = mm->input.tex2_for_tex1[face_data.tex1]; + if (mm->input.tex2) + face_data.tex2 = mm->input.tex2[v_off]; + if (mm->input.tex2_replace) { + if (mm->input.tex2_facemask[v_off] & (1 << face)) + face_data.tex2 = mm->input.tex2_replace[v_off]; + } + + color_face = STBVOX_ROTATE(face, rot.ecolor); + if (mm->input.extended_color) { + unsigned char ec = mm->input.extended_color[v_off]; + if (mm->input.ecolor_facemask[ec] & (1 << color_face)) + face_data.color = mm->input.ecolor_color[ec]; + } + + if (mm->input.color2) { + if (mm->input.color2_facemask[v_off] & (1 << color_face)) + face_data.color = mm->input.color2[v_off]; + if (mm->input.color3 && (mm->input.color3_facemask[v_off] & (1 << color_face))) + face_data.color = mm->input.color3[v_off]; + } + #endif + + face_data.face_info = (normal<<2) + facerot; + return face_data; +} + +// these are the types of faces each block can have +enum +{ + STBVOX_FT_none , + STBVOX_FT_upper , + STBVOX_FT_lower , + STBVOX_FT_solid , + STBVOX_FT_diag_012, + STBVOX_FT_diag_023, + STBVOX_FT_diag_013, + STBVOX_FT_diag_123, + STBVOX_FT_force , // can't be covered up, used for internal faces, also hides nothing + STBVOX_FT_partial , // only covered by solid, never covers anything else + + STBVOX_FT_count +}; + +static unsigned char stbvox_face_lerp[6] = { 0,2,0,2,4,4 }; +static unsigned char stbvox_vert3_lerp[5] = { 0,3,6,9,12 }; +static unsigned char stbvox_vert_lerp_for_face_lerp[4] = { 0, 4, 7, 7 }; +static unsigned char stbvox_face3_lerp[6] = { 0,3,6,9,12,14 }; +static unsigned char stbvox_vert_lerp_for_simple[4] = { 0,2,5,7 }; +static unsigned char stbvox_face3_updown[8] = { 0,2,5,7,0,2,5,7 }; // ignore top bit + +// vertex offsets for face vertices +static unsigned char stbvox_vertex_vector[6][4][3] = +{ + { { 1,0,1 }, { 1,1,1 }, { 1,1,0 }, { 1,0,0 } }, // east + { { 1,1,1 }, { 0,1,1 }, { 0,1,0 }, { 1,1,0 } }, // north + { { 0,1,1 }, { 0,0,1 }, { 0,0,0 }, { 0,1,0 } }, // west + { { 0,0,1 }, { 1,0,1 }, { 1,0,0 }, { 0,0,0 } }, // south + { { 0,1,1 }, { 1,1,1 }, { 1,0,1 }, { 0,0,1 } }, // up + { { 0,0,0 }, { 1,0,0 }, { 1,1,0 }, { 0,1,0 } }, // down +}; + +// stbvox_vertex_vector, but read coordinates as binary numbers, zyx +static unsigned char stbvox_vertex_selector[6][4] = +{ + { 5,7,3,1 }, + { 7,6,2,3 }, + { 6,4,0,2 }, + { 4,5,1,0 }, + { 6,7,5,4 }, + { 0,1,3,2 }, +}; + +static stbvox_mesh_vertex stbvox_vmesh_delta_normal[6][4] = +{ + { stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(0,0,1,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,1,0,0) , + stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(0,0,1,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +static stbvox_mesh_vertex stbvox_vmesh_pre_vheight[6][4] = +{ + { stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +static stbvox_mesh_vertex stbvox_vmesh_delta_half_z[6][4] = +{ + { stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +static stbvox_mesh_vertex stbvox_vmesh_crossed_pair[6][4] = +{ + { stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + // not used, so we leave it non-degenerate to make sure it doesn't get gen'd accidentally + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +#define STBVOX_MAX_GEOM 16 +#define STBVOX_NUM_ROTATION 4 + +// this is used to determine if a face is ever generated at all +static unsigned char stbvox_hasface[STBVOX_MAX_GEOM][STBVOX_NUM_ROTATION] = +{ + { 0,0,0,0 }, // empty + { 0,0,0,0 }, // knockout + { 63,63,63,63 }, // solid + { 63,63,63,63 }, // transp + { 63,63,63,63 }, // slab + { 63,63,63,63 }, // slab + { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // floor slopes + { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // ceil slopes + { 47,47,47,47 }, // wall-projected diagonal with down face + { 31,31,31,31 }, // wall-projected diagonal with up face + { 63,63,63,63 }, // crossed-pair has special handling, but avoid early-out + { 63,63,63,63 }, // force + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight +}; + +// this determines which face type above is visible on each side of the geometry +static unsigned char stbvox_facetype[STBVOX_GEOM_count][6] = +{ + { 0, }, // STBVOX_GEOM_empty + { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // knockout + { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // solid + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // transp + + { STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_solid, STBVOX_FT_force }, + { STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_force, STBVOX_FT_solid }, + { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_none, STBVOX_FT_force, STBVOX_FT_solid }, + { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_none, STBVOX_FT_solid, STBVOX_FT_force }, + + { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_force, STBVOX_FT_none, STBVOX_FT_solid }, + { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_force, STBVOX_FT_solid, STBVOX_FT_none }, + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, 0,0 }, // crossed pair + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // GEOM_force + + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced +}; + +// This table indicates what normal to use for the "up" face of a sloped geom +// @TODO this could be done with math given the current arrangement of the enum, but let's not require it +static unsigned char stbvox_floor_slope_for_rot[4] = +{ + STBVF_su, + STBVF_wu, // @TODO: why is this reversed from what it should be? this is a north-is-up face, so slope should be south&up + STBVF_nu, + STBVF_eu, +}; + +static unsigned char stbvox_ceil_slope_for_rot[4] = +{ + STBVF_sd, + STBVF_ed, + STBVF_nd, + STBVF_wd, +}; + +// this table indicates whether, for each pair of types above, a face is visible. +// each value indicates whether a given type is visible for all neighbor types +static unsigned short stbvox_face_visible[STBVOX_FT_count] = +{ + // we encode the table by listing which cases cause *obscuration*, and bitwise inverting that + // table is pre-shifted by 5 to save a shift when it's accessed + (unsigned short) ((~0x07ff )<<5), // none is completely obscured by everything + (unsigned short) ((~((1<<STBVOX_FT_solid) | (1<<STBVOX_FT_upper) ))<<5), // upper + (unsigned short) ((~((1<<STBVOX_FT_solid) | (1<<STBVOX_FT_lower) ))<<5), // lower + (unsigned short) ((~((1<<STBVOX_FT_solid) ))<<5), // solid is only completely obscured only by solid + (unsigned short) ((~((1<<STBVOX_FT_solid) | (1<<STBVOX_FT_diag_013)))<<5), // diag012 matches diag013 + (unsigned short) ((~((1<<STBVOX_FT_solid) | (1<<STBVOX_FT_diag_123)))<<5), // diag023 matches diag123 + (unsigned short) ((~((1<<STBVOX_FT_solid) | (1<<STBVOX_FT_diag_012)))<<5), // diag013 matches diag012 + (unsigned short) ((~((1<<STBVOX_FT_solid) | (1<<STBVOX_FT_diag_023)))<<5), // diag123 matches diag023 + (unsigned short) ((~0 )<<5), // force is always rendered regardless, always forces neighbor + (unsigned short) ((~((1<<STBVOX_FT_solid) ))<<5), // partial is only completely obscured only by solid +}; + +// the vertex heights of the block types, in binary vertex order (zyx): +// lower: SW, SE, NW, NE; upper: SW, SE, NW, NE +static stbvox_mesh_vertex stbvox_geometry_vheight[8][8] = +{ + #define STBVOX_HEIGHTS(a,b,c,d,e,f,g,h) \ + { stbvox_vertex_encode(0,0,a,0,0), \ + stbvox_vertex_encode(0,0,b,0,0), \ + stbvox_vertex_encode(0,0,c,0,0), \ + stbvox_vertex_encode(0,0,d,0,0), \ + stbvox_vertex_encode(0,0,e,0,0), \ + stbvox_vertex_encode(0,0,f,0,0), \ + stbvox_vertex_encode(0,0,g,0,0), \ + stbvox_vertex_encode(0,0,h,0,0) } + + STBVOX_HEIGHTS(0,0,0,0, 2,2,2,2), + STBVOX_HEIGHTS(0,0,0,0, 2,2,2,2), + STBVOX_HEIGHTS(0,0,0,0, 2,2,2,2), + STBVOX_HEIGHTS(0,0,0,0, 2,2,2,2), + STBVOX_HEIGHTS(1,1,1,1, 2,2,2,2), + STBVOX_HEIGHTS(0,0,0,0, 1,1,1,1), + STBVOX_HEIGHTS(0,0,0,0, 0,0,2,2), + STBVOX_HEIGHTS(2,2,0,0, 2,2,2,2), +}; + +// rotate vertices defined as [z][y][x] coords +static unsigned char stbvox_rotate_vertex[8][4] = +{ + { 0,1,3,2 }, // zyx=000 + { 1,3,2,0 }, // zyx=001 + { 2,0,1,3 }, // zyx=010 + { 3,2,0,1 }, // zyx=011 + { 4,5,7,6 }, // zyx=100 + { 5,7,6,4 }, // zyx=101 + { 6,4,5,7 }, // zyx=110 + { 7,6,4,5 }, // zyx=111 +}; + +#ifdef STBVOX_CONFIG_OPTIMIZED_VHEIGHT +// optimized vheight generates a single normal over the entire face, even if it's not planar +static unsigned char stbvox_optimized_face_up_normal[4][4][4][4] = +{ + { + { + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_nu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_nu , }, + },{ + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, + },{ + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + }, + },{ + { + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + }, + },{ + { + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + }, + },{ + { + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, + { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_nw_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + },{ + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, + { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_nw_u, }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + }, + }, +}; +#else +// which normal to use for a given vheight that's planar +// @TODO: this table was constructed by hand and may have bugs +// nw se sw +static unsigned char stbvox_planar_face_up_normal[4][4][4] = +{ + { // sw,se,nw,ne; ne = se+nw-sw + { STBVF_u , 0 , 0 , 0 }, // 0,0,0,0; 1,0,0,-1; 2,0,0,-2; 3,0,0,-3; + { STBVF_u , STBVF_u , 0 , 0 }, // 0,1,0,1; 1,1,0, 0; 2,1,0,-1; 3,1,0,-2; + { STBVF_wu , STBVF_nw_u, STBVF_nu , 0 }, // 0,2,0,2; 1,2,0, 1; 2,2,0, 0; 3,2,0,-1; + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nu }, // 0,3,0,3; 1,3,0, 2; 2,3,0, 1; 3,3,0, 0; + },{ + { STBVF_u , STBVF_u , 0 , 0 }, // 0,0,1,1; 1,0,1, 0; 2,0,1,-1; 3,0,1,-2; + { STBVF_sw_u, STBVF_u , STBVF_ne_u, 0 }, // 0,1,1,2; 1,1,1, 1; 2,1,1, 0; 3,1,1,-1; + { STBVF_sw_u, STBVF_u , STBVF_u , STBVF_ne_u }, // 0,2,1,3; 1,2,1, 2; 2,2,1, 1; 3,2,1, 0; + { 0 , STBVF_wu , STBVF_nw_u, STBVF_nu }, // 0,3,1,4; 1,3,1, 3; 2,3,1, 2; 3,3,1, 1; + },{ + { STBVF_su , STBVF_se_u, STBVF_eu , 0 }, // 0,0,2,2; 1,0,2, 1; 2,0,2, 0; 3,0,2,-1; + { STBVF_sw_u, STBVF_u , STBVF_u , STBVF_ne_u }, // 0,1,2,3; 1,1,2, 2; 2,1,2, 1; 3,1,2, 0; + { 0 , STBVF_sw_u, STBVF_u , STBVF_ne_u }, // 0,2,2,4; 1,2,2, 3; 2,2,2, 2; 3,2,2, 1; + { 0 , 0 , STBVF_u , STBVF_u }, // 0,3,2,5; 1,3,2, 4; 2,3,2, 3; 3,3,2, 2; + },{ + { STBVF_su , STBVF_se_u, STBVF_se_u, STBVF_eu }, // 0,0,3,3; 1,0,3, 2; 2,0,3, 1; 3,0,3, 0; + { 0 , STBVF_su , STBVF_se_u, STBVF_eu }, // 0,1,3,4; 1,1,3, 3; 2,1,3, 2; 3,1,3, 1; + { 0 , 0 , STBVF_u , STBVF_u }, // 0,2,3,5; 1,2,3, 4; 2,2,3, 3; 3,2,3, 2; + { 0 , 0 , 0 , STBVF_u }, // 0,3,3,6; 1,3,3, 5; 2,3,3, 4; 3,3,3, 3; + } +}; + +// these tables were constructed automatically using a variant of the code +// below; however, they seem wrong, so who knows +static unsigned char stbvox_face_up_normal_012[4][4][4] = +{ + { + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nu , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + } +}; + +static unsigned char stbvox_face_up_normal_013[4][4][4] = +{ + { + { STBVF_u , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_nw_u, STBVF_nu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_wu , STBVF_u , STBVF_eu , STBVF_eu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_wu , STBVF_wu , STBVF_u , STBVF_eu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_su , STBVF_eu , }, + { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_u , }, + } +}; + +static unsigned char stbvox_face_up_normal_023[4][4][4] = +{ + { + { STBVF_u , STBVF_nu , STBVF_nu , STBVF_nu , }, + { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + },{ + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + { STBVF_su , STBVF_u , STBVF_nu , STBVF_nu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + },{ + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + { STBVF_su , STBVF_su , STBVF_u , STBVF_nu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + },{ + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_u , }, + } +}; + +static unsigned char stbvox_face_up_normal_123[4][4][4] = +{ + { + { STBVF_u , STBVF_nu , STBVF_nu , STBVF_nu , }, + { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + },{ + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + { STBVF_su , STBVF_u , STBVF_nu , STBVF_nu , }, + { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + },{ + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, + { STBVF_su , STBVF_su , STBVF_u , STBVF_nu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + },{ + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_u , }, + } +}; +#endif + +void stbvox_get_quad_vertex_pointer(stbvox_mesh_maker *mm, int mesh, stbvox_mesh_vertex **vertices, stbvox_mesh_face face) +{ + char *p = mm->output_cur[mesh][0]; + int step = mm->output_step[mesh][0]; + + // allocate a new quad from the mesh + vertices[0] = (stbvox_mesh_vertex *) p; p += step; + vertices[1] = (stbvox_mesh_vertex *) p; p += step; + vertices[2] = (stbvox_mesh_vertex *) p; p += step; + vertices[3] = (stbvox_mesh_vertex *) p; p += step; + mm->output_cur[mesh][0] = p; + + // output the face + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + // write face as interleaved vertex data + *(stbvox_mesh_face *) (vertices[0]+1) = face; + *(stbvox_mesh_face *) (vertices[1]+1) = face; + *(stbvox_mesh_face *) (vertices[2]+1) = face; + *(stbvox_mesh_face *) (vertices[3]+1) = face; + #else + *(stbvox_mesh_face *) mm->output_cur[mesh][1] = face; + mm->output_cur[mesh][1] += 4; + #endif +} + +void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, int normal) +{ + stbvox_mesh_face face_data = stbvox_compute_mesh_face_value(mm,rot,face,v_off, normal); + + // still need to compute ao & texlerp for each vertex + + // first compute texlerp into p1 + stbvox_mesh_vertex p1[4] = { 0 }; + + #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) && defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up || (f) == STBVOX_FACE_down) + #elif defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up ) + #elif defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ( (f) == STBVOX_FACE_down) + #endif + + #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) || defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + if (STBVOX_USE_PACKED(face)) { + if (!mm->input.packed_compact || 0==(mm->input.packed_compact[v_off]&16)) + goto set_default; + p1[0] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][0]] >> 5); + p1[1] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][1]] >> 5); + p1[2] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][2]] >> 5); + p1[3] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][3]] >> 5); + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); + goto skip; + } + #endif + + if (mm->input.block_texlerp) { + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char val = mm->input.block_texlerp[bt]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.block_texlerp_face) { + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char bt_face = STBVOX_ROTATE(face, rot.block); + unsigned char val = mm->input.block_texlerp_face[bt][bt_face]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.texlerp_face3) { + unsigned char val = (mm->input.texlerp_face3[v_off] >> stbvox_face3_lerp[face]) & 7; + if (face >= STBVOX_FACE_up) + val = stbvox_face3_updown[val]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.texlerp_simple) { + unsigned char val = mm->input.texlerp_simple[v_off]; + unsigned char lerp_face = (val >> 2) & 7; + if (lerp_face == face) { + p1[0] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][0]] >> 5) & 7; + p1[1] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][1]] >> 5) & 7; + p1[2] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][2]] >> 5) & 7; + p1[3] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][3]] >> 5) & 7; + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); + } else { + unsigned char base = stbvox_vert_lerp_for_simple[val&3]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,base); + } + } else if (mm->input.texlerp) { + unsigned char facelerp = (mm->input.texlerp[v_off] >> stbvox_face_lerp[face]) & 3; + if (facelerp == STBVOX_TEXLERP_FACE_use_vert) { + if (mm->input.texlerp_vert3 && face != STBVOX_FACE_down) { + unsigned char shift = stbvox_vert3_lerp[face]; + p1[0] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][0]] >> shift) & 7; + p1[1] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][1]] >> shift) & 7; + p1[2] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][2]] >> shift) & 7; + p1[3] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][3]] >> shift) & 7; + } else { + p1[0] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][0]]>>6]; + p1[1] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][1]]>>6]; + p1[2] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][2]]>>6]; + p1[3] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][3]]>>6]; + } + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); + } else { + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,stbvox_vert_lerp_for_face_lerp[facelerp]); + } + } else { + #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + set_default: + #endif + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,7); // @TODO make this configurable + } + + #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + skip: + #endif + + // now compute lighting and store to vertices + { + stbvox_mesh_vertex *mv[4]; + stbvox_get_quad_vertex_pointer(mm, mesh, mv, face_data); + + if (mm->input.lighting) { + // @TODO: lighting at block centers, but not gathered, instead constant-per-face + if (mm->input.lighting_at_vertices) { + int i; + for (i=0; i < 4; ++i) { + *mv[i] = vertbase + face_coord[i] + + stbvox_vertex_encode(0,0,0,mm->input.lighting[v_off + mm->cube_vertex_offset[face][i]] & 63,0) + + p1[i]; + } + } else { + unsigned char *amb = &mm->input.lighting[v_off]; + int i,j; + #if defined(STBVOX_CONFIG_ROTATION_IN_LIGHTING) || defined(STBVOX_CONFIG_VHEIGHT_IN_LIGHTING) + #define STBVOX_GET_LIGHTING(light) ((light) & ~3) + #define STBVOX_LIGHTING_ROUNDOFF 8 + #else + #define STBVOX_GET_LIGHTING(light) (light) + #define STBVOX_LIGHTING_ROUNDOFF 2 + #endif + + for (i=0; i < 4; ++i) { + // for each vertex, gather from the four neighbor blocks it's facing + unsigned char *vamb = &amb[mm->cube_vertex_offset[face][i]]; + int total=0; + for (j=0; j < 4; ++j) + total += STBVOX_GET_LIGHTING(vamb[mm->vertex_gather_offset[face][j]]); + *mv[i] = vertbase + face_coord[i] + + stbvox_vertex_encode(0,0,0,(total+STBVOX_LIGHTING_ROUNDOFF)>>4,0) + + p1[i]; + // >> 4 is because: + // >> 2 to divide by 4 to get average over 4 samples + // >> 2 because input is 8 bits, output is 6 bits + } + + // @TODO: note that gathering baked *lighting* + // is different from gathering baked ao; baked ao can count + // solid blocks as 0 ao, but baked lighting wants average + // of non-blocked--not take average & treat blocked as 0. And + // we can't bake the right value into the solid blocks + // because they can have different lighting values on + // different sides. So we need to actually gather and + // then divide by 0..4 (which we can do with a table-driven + // multiply, or have an 'if' for the 3 case) + + } + } else { + vertbase += stbvox_vertex_encode(0,0,0,63,0); + *mv[0] = vertbase + face_coord[0] + p1[0]; + *mv[1] = vertbase + face_coord[1] + p1[1]; + *mv[2] = vertbase + face_coord[2] + p1[2]; + *mv[3] = vertbase + face_coord[3] + p1[3]; + } + } +} + +// get opposite-facing normal & texgen for opposite face, used to map up-facing vheight data to down-facing data +static unsigned char stbvox_reverse_face[STBVF_count] = +{ + STBVF_w, STBVF_s, STBVF_e, STBVF_n, STBVF_d , STBVF_u , STBVF_wd, STBVF_wu, + 0, 0, 0, 0, STBVF_sw_d, STBVF_sw_u, STBVF_sd, STBVF_su, + 0, 0, 0, 0, STBVF_se_d, STBVF_se_u, STBVF_ed, STBVF_eu, + 0, 0, 0, 0, STBVF_ne_d, STBVF_ne_d, STBVF_nd, STBVF_nu +}; + +#ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT +// render non-planar quads by splitting into two triangles, rendering each as a degenerate quad +static void stbvox_make_12_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) +{ + stbvox_mesh_vertex v[4]; + + unsigned char normal1 = stbvox_face_up_normal_012[ht[2]][ht[1]][ht[0]]; + unsigned char normal2 = stbvox_face_up_normal_123[ht[3]][ht[2]][ht[1]]; + + if (face == STBVOX_FACE_down) { + normal1 = stbvox_reverse_face[normal1]; + normal2 = stbvox_reverse_face[normal2]; + } + + // the floor side face_coord is stored in order NW,NE,SE,SW, but ht[] is stored SW,SE,NW,NE + v[0] = face_coord[2]; + v[1] = face_coord[3]; + v[2] = face_coord[0]; + v[3] = face_coord[2]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); + v[1] = face_coord[0]; + v[2] = face_coord[1]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); +} + +static void stbvox_make_03_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) +{ + stbvox_mesh_vertex v[4]; + + unsigned char normal1 = stbvox_face_up_normal_013[ht[3]][ht[1]][ht[0]]; + unsigned char normal2 = stbvox_face_up_normal_023[ht[3]][ht[2]][ht[0]]; + + if (face == STBVOX_FACE_down) { + normal1 = stbvox_reverse_face[normal1]; + normal2 = stbvox_reverse_face[normal2]; + } + + v[0] = face_coord[1]; + v[1] = face_coord[2]; + v[2] = face_coord[3]; + v[3] = face_coord[1]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); + v[1] = face_coord[3]; + v[2] = face_coord[0]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); // this one is correct! +} +#endif + +#ifndef STBVOX_CONFIG_PRECISION_Z +#define STBVOX_CONFIG_PRECISION_Z 1 +#endif + +// simple case for mesh generation: we have only solid and empty blocks +static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off, stbvox_mesh_vertex *vmesh) +{ + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; + + unsigned char *blockptr = &mm->input.blocktype[v_off]; + stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); + + stbvox_rotate rot = { 0,0,0,0 }; + unsigned char simple_rot = 0; + + unsigned char mesh = mm->default_mesh; + + if (mm->input.selector) + mesh = mm->input.selector[v_off]; + + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + #ifdef STBVOX_CONFIG_ROTATION_IN_LIGHTING + simple_rot = mm->input.lighting[v_off] & 3; + #endif + + if (mm->input.packed_compact) + simple_rot = mm->input.packed_compact[v_off] & 3; + + if (blockptr[ 1]==0) { + rot.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_up , v_off, pos, basevert, vmesh+4*STBVOX_FACE_up, mesh, STBVOX_FACE_up); + } + if (blockptr[-1]==0) { + rot.facerot = (-simple_rot) & 3; + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_down, v_off, pos, basevert, vmesh+4*STBVOX_FACE_down, mesh, STBVOX_FACE_down); + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rot.block = (val >> 0) & 3; + rot.overlay = (val >> 2) & 3; + //rot.tex2 = (val >> 4) & 3; + rot.ecolor = (val >> 6) & 3; + } else { + rot.block = rot.overlay = rot.ecolor = simple_rot; + } + rot.facerot = 0; + + if (blockptr[ ns_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, vmesh+4*STBVOX_FACE_north, mesh, STBVOX_FACE_north); + if (blockptr[-ns_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, vmesh+4*STBVOX_FACE_south, mesh, STBVOX_FACE_south); + if (blockptr[ ew_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, vmesh+4*STBVOX_FACE_east, mesh, STBVOX_FACE_east); + if (blockptr[-ew_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, vmesh+4*STBVOX_FACE_west, mesh, STBVOX_FACE_west); +} + +// complex case for mesh generation: we have lots of different +// block types, and we don't want to generate faces of blocks +// if they're hidden by neighbors. +// +// we use lots of tables to determine this: we have a table +// which tells us what face type is generated for each type of +// geometry, and then a table that tells us whether that type +// is hidden by a neighbor. +static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) +{ + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; + int visible_faces, visible_base; + unsigned char mesh; + + // first gather the geometry info for this block and all neighbors + + unsigned char bt, nbt[6]; + unsigned char geo, ngeo[6]; + unsigned char rot, nrot[6]; + + bt = mm->input.blocktype[v_off]; + nbt[0] = mm->input.blocktype[v_off + ew_off]; + nbt[1] = mm->input.blocktype[v_off + ns_off]; + nbt[2] = mm->input.blocktype[v_off - ew_off]; + nbt[3] = mm->input.blocktype[v_off - ns_off]; + nbt[4] = mm->input.blocktype[v_off + 1]; + nbt[5] = mm->input.blocktype[v_off - 1]; + if (mm->input.geometry) { + int i; + geo = mm->input.geometry[v_off]; + ngeo[0] = mm->input.geometry[v_off + ew_off]; + ngeo[1] = mm->input.geometry[v_off + ns_off]; + ngeo[2] = mm->input.geometry[v_off - ew_off]; + ngeo[3] = mm->input.geometry[v_off - ns_off]; + ngeo[4] = mm->input.geometry[v_off + 1]; + ngeo[5] = mm->input.geometry[v_off - 1]; + + rot = (geo >> 4) & 3; + geo &= 15; + for (i=0; i < 6; ++i) { + nrot[i] = (ngeo[i] >> 4) & 3; + ngeo[i] &= 15; + } + } else { + int i; + assert(mm->input.block_geometry); + geo = mm->input.block_geometry[bt]; + for (i=0; i < 6; ++i) + ngeo[i] = mm->input.block_geometry[nbt[i]]; + if (mm->input.selector) { + #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING + if (mm->input.packed_compact == NULL) { + rot = (mm->input.selector[v_off ] >> 4) & 3; + nrot[0] = (mm->input.selector[v_off + ew_off] >> 4) & 3; + nrot[1] = (mm->input.selector[v_off + ns_off] >> 4) & 3; + nrot[2] = (mm->input.selector[v_off - ew_off] >> 4) & 3; + nrot[3] = (mm->input.selector[v_off - ns_off] >> 4) & 3; + nrot[4] = (mm->input.selector[v_off + 1] >> 4) & 3; + nrot[5] = (mm->input.selector[v_off - 1] >> 4) & 3; + } + #endif + } else { + #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING + if (mm->input.packed_compact == NULL) { + rot = (geo>>4)&3; + geo &= 15; + for (i=0; i < 6; ++i) { + nrot[i] = (ngeo[i]>>4)&3; + ngeo[i] &= 15; + } + } + #endif + } + } + + #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING + if (mm->input.packed_compact) { + rot = mm->input.packed_compact[rot] & 3; + nrot[0] = mm->input.packed_compact[v_off + ew_off] & 3; + nrot[1] = mm->input.packed_compact[v_off + ns_off] & 3; + nrot[2] = mm->input.packed_compact[v_off - ew_off] & 3; + nrot[3] = mm->input.packed_compact[v_off - ns_off] & 3; + nrot[4] = mm->input.packed_compact[v_off + 1] & 3; + nrot[5] = mm->input.packed_compact[v_off - 1] & 3; + } + #else + rot = mm->input.lighting[v_off] & 3; + nrot[0] = (mm->input.lighting[v_off + ew_off]) & 3; + nrot[1] = (mm->input.lighting[v_off + ns_off]) & 3; + nrot[2] = (mm->input.lighting[v_off - ew_off]) & 3; + nrot[3] = (mm->input.lighting[v_off - ns_off]) & 3; + nrot[4] = (mm->input.lighting[v_off + 1]) & 3; + nrot[5] = (mm->input.lighting[v_off - 1]) & 3; + #endif + + if (geo == STBVOX_GEOM_transp) { + // transparency has a special rule: if the blocktype is the same, + // and the faces are compatible, then can hide them; otherwise, + // force them on + // Note that this means we don't support any transparentshapes other + // than solid blocks, since detecting them is too complicated. If + // you wanted to do something like minecraft water, you probably + // should just do that with a separate renderer anyway. (We don't + // support transparency sorting so you need to use alpha test + // anyway) + int i; + for (i=0; i < 6; ++i) + if (nbt[i] != bt) { + nbt[i] = 0; + ngeo[i] = STBVOX_GEOM_empty; + } else + ngeo[i] = STBVOX_GEOM_solid; + geo = STBVOX_GEOM_solid; + } + + // now compute the face visibility + visible_base = stbvox_hasface[geo][rot]; + // @TODO: assert(visible_base != 0); // we should have early-outted earlier in this case + visible_faces = 0; + + // now, for every face that might be visible, check if neighbor hides it + if (visible_base & (1 << STBVOX_FACE_east)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_east+ rot )&3]; + int ntype = stbvox_facetype[ngeo[0]][(STBVOX_FACE_west+nrot[0])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_east)) & (1 << STBVOX_FACE_east); + } + if (visible_base & (1 << STBVOX_FACE_north)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_north+ rot )&3]; + int ntype = stbvox_facetype[ngeo[1]][(STBVOX_FACE_south+nrot[1])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_north)) & (1 << STBVOX_FACE_north); + } + if (visible_base & (1 << STBVOX_FACE_west)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_west+ rot )&3]; + int ntype = stbvox_facetype[ngeo[2]][(STBVOX_FACE_east+nrot[2])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_west)) & (1 << STBVOX_FACE_west); + } + if (visible_base & (1 << STBVOX_FACE_south)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_south+ rot )&3]; + int ntype = stbvox_facetype[ngeo[3]][(STBVOX_FACE_north+nrot[3])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_south)) & (1 << STBVOX_FACE_south); + } + if (visible_base & (1 << STBVOX_FACE_up)) { + int type = stbvox_facetype[ geo ][STBVOX_FACE_up]; + int ntype = stbvox_facetype[ngeo[4]][STBVOX_FACE_down]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_up)) & (1 << STBVOX_FACE_up); + } + if (visible_base & (1 << STBVOX_FACE_down)) { + int type = stbvox_facetype[ geo ][STBVOX_FACE_down]; + int ntype = stbvox_facetype[ngeo[5]][STBVOX_FACE_up]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_down)) & (1 << STBVOX_FACE_down); + } + + if (geo == STBVOX_GEOM_force) + geo = STBVOX_GEOM_solid; + + assert((geo == STBVOX_GEOM_crossed_pair) ? (visible_faces == 15) : 1); + + // now we finally know for sure which faces are getting generated + if (visible_faces == 0) + return; + + mesh = mm->default_mesh; + if (mm->input.selector) + mesh = mm->input.selector[v_off]; + + if (geo <= STBVOX_GEOM_ceil_slope_north_is_bottom) { + // this is the simple case, we can just use regular block gen with special vmesh calculated with vheight + stbvox_mesh_vertex basevert; + stbvox_mesh_vertex vmesh[6][4]; + stbvox_rotate rotate = { 0,0,0,0 }; + unsigned char simple_rot = rot; + int i; + // we only need to do this for the displayed faces, but it's easier + // to just do it up front; @OPTIMIZE check if it's faster to do it + // for visible faces only + for (i=0; i < 6*4; ++i) { + int vert = stbvox_vertex_selector[0][i]; + vert = stbvox_rotate_vertex[vert][rot]; + vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] + + stbvox_geometry_vheight[geo][vert]; + } + + basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); + if (mm->input.selector) { + mesh = mm->input.selector[v_off]; + } + + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + if (geo >= STBVOX_GEOM_floor_slope_north_is_top) { + if (visible_faces & (1 << STBVOX_FACE_up)) { + int normal = geo == STBVOX_GEOM_floor_slope_north_is_top ? stbvox_floor_slope_for_rot[simple_rot] : STBVOX_FACE_up; + rotate.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, normal); + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + int normal = geo == STBVOX_GEOM_ceil_slope_north_is_bottom ? stbvox_ceil_slope_for_rot[simple_rot] : STBVOX_FACE_down; + rotate.facerot = (-rotate.facerot) & 3; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, normal); + } + } else { + if (visible_faces & (1 << STBVOX_FACE_up)) { + rotate.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + rotate.facerot = (-rotate.facerot) & 3; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); + } + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rotate.block = (val >> 0) & 3; + rotate.overlay = (val >> 2) & 3; + //rotate.tex2 = (val >> 4) & 3; + rotate.ecolor = (val >> 6) & 3; + } else { + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; + } + + rotate.facerot = 0; + + if (visible_faces & (1 << STBVOX_FACE_north)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); + if (visible_faces & (1 << STBVOX_FACE_south)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); + if (visible_faces & (1 << STBVOX_FACE_east)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); + if (visible_faces & (1 << STBVOX_FACE_west)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + } + if (geo >= STBVOX_GEOM_floor_vheight_03) { + // this case can also be generated with regular block gen with special vmesh, + // except: + // if we want to generate middle diagonal for 'weird' blocks + // it's more complicated to detect neighbor matchups + stbvox_mesh_vertex vmesh[6][4]; + stbvox_mesh_vertex cube[8]; + stbvox_mesh_vertex basevert; + stbvox_rotate rotate = { 0,0,0,0 }; + unsigned char simple_rot = rot; + unsigned char ht[4]; + int extreme; + + // extract the heights + #ifdef STBVOX_CONFIG_VHEIGHT_IN_LIGHTING + ht[0] = mm->input.lighting[v_off ] & 3; + ht[1] = mm->input.lighting[v_off+ew_off ] & 3; + ht[2] = mm->input.lighting[v_off +ns_off] & 3; + ht[3] = mm->input.lighting[v_off+ew_off+ns_off] & 3; + #else + if (mm->input.vheight) { + unsigned char v = mm->input.vheight[v_off]; + ht[0] = (v >> 0) & 3; + ht[1] = (v >> 2) & 3; + ht[2] = (v >> 4) & 3; + ht[3] = (v >> 6) & 3; + } else if (mm->input.block_vheight) { + unsigned char v = mm->input.block_vheight[bt]; + unsigned char raw[4]; + int i; + + raw[0] = (v >> 0) & 3; + raw[1] = (v >> 2) & 3; + raw[2] = (v >> 4) & 3; + raw[3] = (v >> 6) & 3; + + for (i=0; i < 4; ++i) + ht[i] = raw[stbvox_rotate_vertex[i][rot]]; + } else if (mm->input.packed_compact) { + ht[0] = (mm->input.packed_compact[v_off ] >> 2) & 3; + ht[1] = (mm->input.packed_compact[v_off+ew_off ] >> 2) & 3; + ht[2] = (mm->input.packed_compact[v_off +ns_off] >> 2) & 3; + ht[3] = (mm->input.packed_compact[v_off+ew_off+ns_off] >> 2) & 3; + } else if (mm->input.geometry) { + ht[0] = mm->input.geometry[v_off ] >> 6; + ht[1] = mm->input.geometry[v_off+ew_off ] >> 6; + ht[2] = mm->input.geometry[v_off +ns_off] >> 6; + ht[3] = mm->input.geometry[v_off+ew_off+ns_off] >> 6; + } else { + assert(0); + } + #endif + + // flag whether any sides go off the top of the block, which means + // our visible_faces test was wrong + extreme = (ht[0] == 3 || ht[1] == 3 || ht[2] == 3 || ht[3] == 3); + + if (geo >= STBVOX_GEOM_ceil_vheight_03) { + cube[0] = stbvox_vertex_encode(0,0,ht[0],0,0); + cube[1] = stbvox_vertex_encode(0,0,ht[1],0,0); + cube[2] = stbvox_vertex_encode(0,0,ht[2],0,0); + cube[3] = stbvox_vertex_encode(0,0,ht[3],0,0); + cube[4] = stbvox_vertex_encode(0,0,2,0,0); + cube[5] = stbvox_vertex_encode(0,0,2,0,0); + cube[6] = stbvox_vertex_encode(0,0,2,0,0); + cube[7] = stbvox_vertex_encode(0,0,2,0,0); + } else { + cube[0] = stbvox_vertex_encode(0,0,0,0,0); + cube[1] = stbvox_vertex_encode(0,0,0,0,0); + cube[2] = stbvox_vertex_encode(0,0,0,0,0); + cube[3] = stbvox_vertex_encode(0,0,0,0,0); + cube[4] = stbvox_vertex_encode(0,0,ht[0],0,0); + cube[5] = stbvox_vertex_encode(0,0,ht[1],0,0); + cube[6] = stbvox_vertex_encode(0,0,ht[2],0,0); + cube[7] = stbvox_vertex_encode(0,0,ht[3],0,0); + } + if (!mm->input.vheight && mm->input.block_vheight) { + // @TODO: support block vheight here, I've forgotten what needs to be done specially + } + + // build vertex mesh + { + int i; + for (i=0; i < 6*4; ++i) { + int vert = stbvox_vertex_selector[0][i]; + vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] + + cube[vert]; + } + } + + basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + // @TODO generate split faces + if (visible_faces & (1 << STBVOX_FACE_up)) { + if (geo >= STBVOX_GEOM_ceil_vheight_03) + // flat + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); + else { + #ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT + // check if it's non-planar + if (cube[5] + cube[6] != cube[4] + cube[7]) { + // not planar, split along diagonal and make degenerate quads + if (geo == STBVOX_GEOM_floor_vheight_03) + stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); + else + stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); + } else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]); + #else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]); + #endif + } + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + if (geo < STBVOX_GEOM_ceil_vheight_03) + // flat + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); + else { + #ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT + // check if it's non-planar + if (cube[1] + cube[2] != cube[0] + cube[3]) { + // not planar, split along diagonal and make degenerate quads + if (geo == STBVOX_GEOM_ceil_vheight_03) + stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); + else + stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); + } else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]]); + #else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]]); + #endif + } + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rotate.block = (val >> 0) & 3; + rotate.overlay = (val >> 2) & 3; + //rotate.tex2 = (val >> 4) & 3; + rotate.ecolor = (val >> 6) & 3; + } else if (mm->input.selector) { + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; + } + + if ((visible_faces & (1 << STBVOX_FACE_north)) || (extreme && (ht[2] == 3 || ht[3] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); + if ((visible_faces & (1 << STBVOX_FACE_south)) || (extreme && (ht[0] == 3 || ht[1] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); + if ((visible_faces & (1 << STBVOX_FACE_east)) || (extreme && (ht[1] == 3 || ht[3] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); + if ((visible_faces & (1 << STBVOX_FACE_west)) || (extreme && (ht[0] == 3 || ht[2] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + } + + if (geo == STBVOX_GEOM_crossed_pair) { + // this can be generated with a special vmesh + stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); + unsigned char simple_rot=0; + stbvox_rotate rot = { 0,0,0,0 }; + unsigned char mesh = mm->default_mesh; + if (mm->input.selector) { + mesh = mm->input.selector[v_off]; + simple_rot = mesh >> 4; + mesh &= 15; + } + + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*4 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rot.block = (val >> 0) & 3; + rot.overlay = (val >> 2) & 3; + //rot.tex2 = (val >> 4) & 3; + rot.ecolor = (val >> 6) & 3; + } else if (mm->input.selector) { + rot.block = rot.overlay = rot.ecolor = simple_rot; + } + rot.facerot = 0; + + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_north], mesh, STBVF_ne_u_cross); + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_south], mesh, STBVF_sw_u_cross); + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_east ], mesh, STBVF_se_u_cross); + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_west ], mesh, STBVF_nw_u_cross); + } + + + // @TODO + // STBVOX_GEOM_floor_slope_north_is_top_as_wall, + // STBVOX_GEOM_ceil_slope_north_is_bottom_as_wall, +} + +static void stbvox_make_mesh_for_column(stbvox_mesh_maker *mm, int x, int y, int z0) +{ + stbvox_pos pos; + int v_off = x * mm->x_stride_in_bytes + y * mm->y_stride_in_bytes; + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; + pos.x = x; + pos.y = y; + pos.z = 0; + if (mm->input.geometry) { + unsigned char *bt = mm->input.blocktype + v_off; + unsigned char *geo = mm->input.geometry + v_off; + int z; + for (z=z0; z < mm->z1; ++z) { + if (bt[z] && ( !bt[z+ns_off] || !STBVOX_GET_GEO(geo[z+ns_off]) || !bt[z-ns_off] || !STBVOX_GET_GEO(geo[z-ns_off]) + || !bt[z+ew_off] || !STBVOX_GET_GEO(geo[z+ew_off]) || !bt[z-ew_off] || !STBVOX_GET_GEO(geo[z-ew_off]) + || !bt[z-1] || !STBVOX_GET_GEO(geo[z-1]) || !bt[z+1] || !STBVOX_GET_GEO(geo[z+1]))) + { // TODO check up and down + pos.z = z; + stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } else if (mm->input.block_geometry) { + int z; + unsigned char *bt = mm->input.blocktype + v_off; + unsigned char *geo = mm->input.block_geometry; + for (z=z0; z < mm->z1; ++z) { + if (bt[z] && ( geo[bt[z+ns_off]] != STBVOX_GEOM_solid + || geo[bt[z-ns_off]] != STBVOX_GEOM_solid + || geo[bt[z+ew_off]] != STBVOX_GEOM_solid + || geo[bt[z-ew_off]] != STBVOX_GEOM_solid + || geo[bt[z-1]] != STBVOX_GEOM_solid + || geo[bt[z+1]] != STBVOX_GEOM_solid)) + { + pos.z = z; + stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } else { + unsigned char *bt = mm->input.blocktype + v_off; + int z; + #if STBVOX_CONFIG_PRECISION_Z == 1 + stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_half_z[0]; + #else + stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_normal[0]; + #endif + for (z=z0; z < mm->z1; ++z) { + // if it's solid and at least one neighbor isn't solid + if (bt[z] && (!bt[z+ns_off] || !bt[z-ns_off] || !bt[z+ew_off] || !bt[z-ew_off] || !bt[z-1] || !bt[z+1])) { + pos.z = z; + stbvox_make_mesh_for_block(mm, pos, v_off+z, vmesh); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } +} + +static void stbvox_bring_up_to_date(stbvox_mesh_maker *mm) +{ + if (mm->config_dirty) { + int i; + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + mm->num_mesh_slots = 1; + for (i=0; i < STBVOX_MAX_MESHES; ++i) { + mm->output_size[i][0] = 32; + mm->output_step[i][0] = 8; + } + #else + mm->num_mesh_slots = 2; + for (i=0; i < STBVOX_MAX_MESHES; ++i) { + mm->output_size[i][0] = 16; + mm->output_step[i][0] = 4; + mm->output_size[i][1] = 4; + mm->output_step[i][1] = 4; + } + #endif + + mm->config_dirty = 0; + } +} + +int stbvox_make_mesh(stbvox_mesh_maker *mm) +{ + int x,y; + stbvox_bring_up_to_date(mm); + mm->full = 0; + if (mm->cur_x > mm->x0 || mm->cur_y > mm->y0 || mm->cur_z > mm->z0) { + stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->cur_z); + if (mm->full) + return 0; + ++mm->cur_y; + while (mm->cur_y < mm->y1 && !mm->full) { + stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->z0); + if (mm->full) + return 0; + ++mm->cur_y; + } + ++mm->cur_x; + } + for (x=mm->cur_x; x < mm->x1; ++x) { + for (y=mm->y0; y < mm->y1; ++y) { + stbvox_make_mesh_for_column(mm, x, y, mm->z0); + if (mm->full) { + mm->cur_x = x; + mm->cur_y = y; + return 0; + } + } + } + return 1; +} + +void stbvox_init_mesh_maker(stbvox_mesh_maker *mm) +{ + memset(mm, 0, sizeof(*mm)); + stbvox_build_default_palette(); + + mm->config_dirty = 1; + mm->default_mesh = 0; +} + +int stbvox_get_buffer_count(stbvox_mesh_maker *mm) +{ + stbvox_bring_up_to_date(mm); + return mm->num_mesh_slots; +} + +int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int n) +{ + return mm->output_size[0][n]; +} + +void stbvox_reset_buffers(stbvox_mesh_maker *mm) +{ + int i; + for (i=0; i < STBVOX_MAX_MESHES*STBVOX_MAX_MESH_SLOTS; ++i) { + mm->output_cur[0][i] = 0; + mm->output_buffer[0][i] = 0; + } +} + +void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len) +{ + int i; + stbvox_bring_up_to_date(mm); + mm->output_buffer[mesh][slot] = (char *) buffer; + mm->output_cur [mesh][slot] = (char *) buffer; + mm->output_len [mesh][slot] = len; + mm->output_end [mesh][slot] = (char *) buffer + len; + for (i=0; i < STBVOX_MAX_MESH_SLOTS; ++i) { + if (mm->output_buffer[mesh][i]) { + assert(mm->output_len[mesh][i] / mm->output_size[mesh][i] == mm->output_len[mesh][slot] / mm->output_size[mesh][slot]); + } + } +} + +void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh) +{ + mm->default_mesh = mesh; +} + +int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh) +{ + return (mm->output_cur[mesh][0] - mm->output_buffer[mesh][0]) / mm->output_size[mesh][0]; +} + +stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm) +{ + return &mm->input; +} + +void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1) +{ + mm->x0 = x0; + mm->y0 = y0; + mm->z0 = z0; + + mm->x1 = x1; + mm->y1 = y1; + mm->z1 = z1; + + mm->cur_x = x0; + mm->cur_y = y0; + mm->cur_z = z0; + + // @TODO validate that this range is representable in this mode +} + +void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]) +{ + // scale + transform[0][0] = 1.0; + transform[0][1] = 1.0; + #if STBVOX_CONFIG_PRECISION_Z==1 + transform[0][2] = 0.5f; + #else + transform[0][2] = 1.0f; + #endif + // translation + transform[1][0] = (float) (mm->pos_x); + transform[1][1] = (float) (mm->pos_y); + transform[1][2] = (float) (mm->pos_z); + // texture coordinate projection translation + transform[2][0] = (float) (mm->pos_x & 255); // @TODO depends on max texture scale + transform[2][1] = (float) (mm->pos_y & 255); + transform[2][2] = (float) (mm->pos_z & 255); +} + +void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]) +{ + bounds[0][0] = (float) (mm->pos_x + mm->x0); + bounds[0][1] = (float) (mm->pos_y + mm->y0); + bounds[0][2] = (float) (mm->pos_z + mm->z0); + bounds[1][0] = (float) (mm->pos_x + mm->x1); + bounds[1][1] = (float) (mm->pos_y + mm->y1); + bounds[1][2] = (float) (mm->pos_z + mm->z1); +} + +void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z) +{ + mm->pos_x = x; + mm->pos_y = y; + mm->pos_z = z; +} + +void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_bytes, int y_stride_in_bytes) +{ + int f,v; + mm->x_stride_in_bytes = x_stride_in_bytes; + mm->y_stride_in_bytes = y_stride_in_bytes; + for (f=0; f < 6; ++f) { + for (v=0; v < 4; ++v) { + mm->cube_vertex_offset[f][v] = stbvox_vertex_vector[f][v][0] * mm->x_stride_in_bytes + + stbvox_vertex_vector[f][v][1] * mm->y_stride_in_bytes + + stbvox_vertex_vector[f][v][2] ; + mm->vertex_gather_offset[f][v] = (stbvox_vertex_vector[f][v][0]-1) * mm->x_stride_in_bytes + + (stbvox_vertex_vector[f][v][1]-1) * mm->y_stride_in_bytes + + (stbvox_vertex_vector[f][v][2]-1) ; + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// offline computation of tables +// + +#if 0 +// compute optimized vheight table +static char *normal_names[32] = +{ + 0,0,0,0,"u ",0, "eu ",0, + 0,0,0,0,"ne_u",0, "nu ",0, + 0,0,0,0,"nw_u",0, "wu ",0, + 0,0,0,0,"sw_u",0, "su ",0, +}; + +static char *find_best_normal(float x, float y, float z) +{ + int best_slot = 4; + float best_dot = 0; + int i; + for (i=0; i < 32; ++i) { + if (normal_names[i]) { + float dot = x * stbvox_default_normals[i][0] + y * stbvox_default_normals[i][1] + z * stbvox_default_normals[i][2]; + if (dot > best_dot) { + best_dot = dot; + best_slot = i; + } + } + } + return normal_names[best_slot]; +} + +int main(int argc, char **argv) +{ + int sw,se,nw,ne; + for (ne=0; ne < 4; ++ne) { + for (nw=0; nw < 4; ++nw) { + for (se=0; se < 4; ++se) { + printf(" { "); + for (sw=0; sw < 4; ++sw) { + float x = (float) (nw + sw - ne - se); + float y = (float) (sw + se - nw - ne); + float z = 2; + printf("STBVF_%s, ", find_best_normal(x,y,z)); + } + printf("},\n"); + } + } + } + return 0; +} +#endif + +// @TODO +// +// - test API for texture rotation on side faces +// - API for texture rotation on top & bottom +// - better culling of vheight faces with vheight neighbors +// - better culling of non-vheight faces with vheight neighbors +// - gather vertex lighting from slopes correctly +// - better support texture edge_clamp: currently if you fall +// exactly on 1.0 you get wrapped incorrectly; this is rare, but +// can avoid: compute texcoords in vertex shader, offset towards +// center before modding, need 2 bits per vertex to know offset direction) +// - other mesh modes (10,6,4-byte quads) +// +// +// With TexBuffer for the fixed vertex data, we can actually do +// minecrafty non-blocks like stairs -- we still probably only +// want 256 or so, so we can't do the equivalent of all the vheight +// combos, but that's ok. The 256 includes baked rotations, but only +// some of them need it, and lots of block types share some faces. +// +// mode 5 (6 bytes): mode 6 (6 bytes) +// x:7 x:6 +// y:7 y:6 +// z:6 z:6 +// tex1:8 tex1:8 +// tex2:8 tex2:7 +// color:8 color:8 +// face:4 face:7 +// +// +// side faces (all x4) top&bottom faces (2x) internal faces (1x) +// 1 regular 1 regular +// 2 slabs 2 +// 8 stairs 4 stairs 16 +// 4 diag side 8 +// 4 upper diag side 8 +// 4 lower diag side 8 +// 4 crossed pairs +// +// 23*4 + 5*4 + 46 +// == 92 + 20 + 46 = 158 +// +// Must drop 30 of them to fit in 7 bits: +// ceiling half diagonals: 16+8 = 24 +// Need to get rid of 6 more. +// ceiling diagonals: 8+4 = 12 +// This brings it to 122, so can add a crossed-pair variant. +// (diagonal and non-diagonal, or randomly offset) +// Or carpet, which would be 5 more. +// +// +// Mode 4 (10 bytes): +// v: z:2,light:6 +// f: x:6,y:6,z:7, t1:8,t2:8,c:8,f:5 +// +// Mode ? (10 bytes) +// v: xyz:5 (27 values), light:3 +// f: x:7,y:7,z:6, t1:8,t2:8,c:8,f:4 +// (v: x:2,y:2,z:2,light:2) + +#endif // STB_VOXEL_RENDER_IMPLEMENTATION diff --git a/test_data/lots_of_files/stdarg.h b/test_data/lots_of_files/stdarg.h new file mode 100644 index 0000000..4d8aa61 --- /dev/null +++ b/test_data/lots_of_files/stdarg.h @@ -0,0 +1,43 @@ +/*** +*stdarg.h - defines ANSI-style macros for variable argument functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines ANSI-style macros for accessing arguments +* of functions which take a variable number of arguments. +* [ANSI] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_STDARG +#define _INC_STDARG + +#include <crtdefs.h> + +#if !defined (_WIN32) +#error ERROR: Only Win32 target supported! +#endif /* !defined (_WIN32) */ + +#include <vadefs.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define va_start _crt_va_start +#define va_arg _crt_va_arg +#define va_end _crt_va_end + +void _CRTIMP __cdecl _vacopy(_Out_ va_list *, _In_ va_list); +#define va_copy(apd, aps) _vacopy(&(apd), aps) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_STDARG */ diff --git a/test_data/lots_of_files/stdbool.h b/test_data/lots_of_files/stdbool.h new file mode 100644 index 0000000..db5d31a --- /dev/null +++ b/test_data/lots_of_files/stdbool.h @@ -0,0 +1,21 @@ +/* stdbool.h standard header */ + +#ifndef _STDBOOL +#define _STDBOOL + +#define __bool_true_false_are_defined 1 + +#ifndef __cplusplus + +#define bool _Bool +#define false 0 +#define true 1 + +#endif /* __cplusplus */ + +#endif /* _STDBOOL */ + +/* + * Copyright (c) 1992-2010 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V5.30:0009 */ diff --git a/test_data/lots_of_files/stddef.h b/test_data/lots_of_files/stddef.h new file mode 100644 index 0000000..0551fc7 --- /dev/null +++ b/test_data/lots_of_files/stddef.h @@ -0,0 +1,77 @@ +/*** +*stddef.h - definitions/declarations for common constants, types, variables +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains definitions and declarations for some commonly +* used constants, types, and variables. +* [ANSI] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_STDDEF +#define _INC_STDDEF + +#include <crtdefs.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + +#ifdef __cplusplus +namespace std { typedef decltype(__nullptr) nullptr_t; } +using ::std::nullptr_t; +#endif /* __cplusplus */ + +/* Declare reference to errno */ +#ifndef _CRT_ERRNO_DEFINED +#define _CRT_ERRNO_DEFINED +_CRTIMP extern int * __cdecl _errno(void); +#define errno (*_errno()) + +errno_t __cdecl _set_errno(_In_ int _Value); +errno_t __cdecl _get_errno(_Out_ int * _Value); +#endif /* _CRT_ERRNO_DEFINED */ + +/* Define offsetof macro */ +#ifdef __cplusplus + +#ifdef _WIN64 +#define offsetof(s,m) (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) ) +#else /* _WIN64 */ +#define offsetof(s,m) (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) +#endif /* _WIN64 */ + +#else /* __cplusplus */ + +#ifdef _WIN64 +#define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) ) +#else /* _WIN64 */ +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif /* _WIN64 */ + +#endif /* __cplusplus */ + +_CRTIMP extern unsigned long __cdecl __threadid(void); +#define _threadid (__threadid()) +_CRTIMP extern uintptr_t __cdecl __threadhandle(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_STDDEF */ diff --git a/test_data/lots_of_files/stdexcpt.h b/test_data/lots_of_files/stdexcpt.h new file mode 100644 index 0000000..fe6099f --- /dev/null +++ b/test_data/lots_of_files/stdexcpt.h @@ -0,0 +1,26 @@ +/*** +*stdexcpt.h - User include file for standard exception classes +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file is the previous location of the standard exception class +* definitions, now found in the standard header <exception>. +* +* [Public] +* +****/ + +#pragma once + +#include <crtdefs.h> + +#ifndef _INC_STDEXCPT +#define _INC_STDEXCPT + +#ifdef __cplusplus + +#include <exception> + +#endif /* __cplusplus */ +#endif /* _INC_STDEXCPT */ diff --git a/test_data/lots_of_files/stdint.h b/test_data/lots_of_files/stdint.h new file mode 100644 index 0000000..1cb5635 --- /dev/null +++ b/test_data/lots_of_files/stdint.h @@ -0,0 +1,144 @@ +/* stdint.h standard header */ +#pragma once +#ifndef _STDINT +#define _STDINT +#ifndef RC_INVOKED +#include <crtdefs.h> + +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int_least8_t; +typedef short int_least16_t; +typedef int int_least32_t; +typedef long long int_least64_t; +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned long long uint_least64_t; + +typedef signed char int_fast8_t; +typedef int int_fast16_t; +typedef int int_fast32_t; +typedef long long int_fast64_t; +typedef unsigned char uint_fast8_t; +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +#ifndef _INTPTR_T_DEFINED + #define _INTPTR_T_DEFINED + #ifdef _WIN64 +typedef long long intptr_t; + #else /* _WIN64 */ +typedef _W64 int intptr_t; + #endif /* _WIN64 */ +#endif /* _INTPTR_T_DEFINED */ + +#ifndef _UINTPTR_T_DEFINED + #define _UINTPTR_T_DEFINED + #ifdef _WIN64 +typedef unsigned long long uintptr_t; + #else /* _WIN64 */ +typedef _W64 unsigned int uintptr_t; + #endif /* _WIN64 */ +#endif /* _UINTPTR_T_DEFINED */ + +typedef long long intmax_t; +typedef unsigned long long uintmax_t; + +/* These macros must exactly match those in the Windows SDK's intsafe.h */ +#define INT8_MIN (-127i8 - 1) +#define INT16_MIN (-32767i16 - 1) +#define INT32_MIN (-2147483647i32 - 1) +#define INT64_MIN (-9223372036854775807i64 - 1) +#define INT8_MAX 127i8 +#define INT16_MAX 32767i16 +#define INT32_MAX 2147483647i32 +#define INT64_MAX 9223372036854775807i64 +#define UINT8_MAX 0xffui8 +#define UINT16_MAX 0xffffui16 +#define UINT32_MAX 0xffffffffui32 +#define UINT64_MAX 0xffffffffffffffffui64 + +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +#ifdef _WIN64 + #define INTPTR_MIN INT64_MIN + #define INTPTR_MAX INT64_MAX + #define UINTPTR_MAX UINT64_MAX +#else /* _WIN64 */ + #define INTPTR_MIN INT32_MIN + #define INTPTR_MAX INT32_MAX + #define UINTPTR_MAX UINT32_MAX +#endif /* _WIN64 */ + +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +#define PTRDIFF_MIN INTPTR_MIN +#define PTRDIFF_MAX INTPTR_MAX + +#ifndef SIZE_MAX + #define SIZE_MAX UINTPTR_MAX +#endif /* SIZE_MAX */ + +#define SIG_ATOMIC_MIN INT32_MIN +#define SIG_ATOMIC_MAX INT32_MAX + +#define WCHAR_MIN 0x0000 +#define WCHAR_MAX 0xffff + +#define WINT_MIN 0x0000 +#define WINT_MAX 0xffff + +#define INT8_C(x) (x) +#define INT16_C(x) (x) +#define INT32_C(x) (x) +#define INT64_C(x) (x ## LL) + +#define UINT8_C(x) (x) +#define UINT16_C(x) (x) +#define UINT32_C(x) (x ## U) +#define UINT64_C(x) (x ## ULL) + +#define INTMAX_C(x) INT64_C(x) +#define UINTMAX_C(x) UINT64_C(x) +#endif /* RC_INVOKED */ +#endif /* _STDINT */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/stdio.h b/test_data/lots_of_files/stdio.h new file mode 100644 index 0000000..8a9e61d --- /dev/null +++ b/test_data/lots_of_files/stdio.h @@ -0,0 +1,733 @@ +/*** +*stdio.h - definitions/declarations for standard I/O routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines the structures, values, macros, and functions +* used by the level 2 I/O ("standard I/O") routines. +* [ANSI/System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_STDIO +#define _INC_STDIO + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* Buffered I/O macros */ + +#define BUFSIZ 512 + +#ifdef _CRTBLD +/* + * Real default size for stdio buffers + */ +#define _INTERNAL_BUFSIZ 4096 +#define _SMALL_BUFSIZ 512 +#endif /* _CRTBLD */ + +/* + * Default number of supported streams. _NFILE is confusing and obsolete, but + * supported anyway for backwards compatibility. + */ +#define _NFILE _NSTREAM_ + +#define _NSTREAM_ 512 + +/* + * Number of entries in _iob[] (declared below). Note that _NSTREAM_ must be + * greater than or equal to _IOB_ENTRIES. + */ +#define _IOB_ENTRIES 20 + +#define EOF (-1) + + +#ifndef _FILE_DEFINED +struct _iobuf { + char *_ptr; + int _cnt; + char *_base; + int _flag; + int _file; + int _charbuf; + int _bufsiz; + char *_tmpfname; + }; +typedef struct _iobuf FILE; +#define _FILE_DEFINED +#endif /* _FILE_DEFINED */ + + +/* Directory where temporary files may be created. */ + +#define _P_tmpdir "\\" +#define _wP_tmpdir L"\\" + +/* L_tmpnam = length of string _P_tmpdir + * + 1 if _P_tmpdir does not end in "/" or "\", else 0 + * + 12 (for the filename string) + * + 1 (for the null terminator) + * L_tmpnam_s = length of string _P_tmpdir + * + 1 if _P_tmpdir does not end in "/" or "\", else 0 + * + 16 (for the filename string) + * + 1 (for the null terminator) + */ +#define L_tmpnam (sizeof(_P_tmpdir) + 12) +#if __STDC_WANT_SECURE_LIB__ +#define L_tmpnam_s (sizeof(_P_tmpdir) + 16) +#endif /* __STDC_WANT_SECURE_LIB__ */ + + + +/* Seek method constants */ + +#define SEEK_CUR 1 +#define SEEK_END 2 +#define SEEK_SET 0 + + +#define FILENAME_MAX 260 +#define FOPEN_MAX 20 +#define _SYS_OPEN 20 +#define TMP_MAX 32767 /* SHRT_MAX */ +#if __STDC_WANT_SECURE_LIB__ +#define TMP_MAX_S _TMP_MAX_S +#define _TMP_MAX_S 2147483647 /* INT_MAX */ +#endif /* __STDC_WANT_SECURE_LIB__ */ + +/* Define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + +/* Declare _iob[] array */ + +#ifndef _STDIO_DEFINED +#ifdef _CRTBLD +/* These functions are for enabling STATIC_CPPLIB functionality */ +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP extern FILE * __cdecl __p__iob(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#ifndef _M_CEE_PURE +_CRTIMP extern FILE _iob[]; +#endif /* _M_CEE_PURE */ +#endif /* _CRTBLD */ +_CRTIMP FILE * __cdecl __iob_func(void); +#endif /* _STDIO_DEFINED */ + + +/* Define file position type */ + +#ifndef _FPOS_T_DEFINED +typedef __int64 fpos_t; +#define _FPOS_T_DEFINED +#endif /* _FPOS_T_DEFINED */ + +#ifndef _STDSTREAM_DEFINED +#define stdin (&__iob_func()[0]) +#define stdout (&__iob_func()[1]) +#define stderr (&__iob_func()[2]) +#define _STDSTREAM_DEFINED +#endif /* _STDSTREAM_DEFINED */ + +#define _IOREAD 0x0001 +#define _IOWRT 0x0002 + +#define _IOFBF 0x0000 +#define _IOLBF 0x0040 +#define _IONBF 0x0004 + +#define _IOMYBUF 0x0008 +#define _IOEOF 0x0010 +#define _IOERR 0x0020 +#define _IOSTRG 0x0040 +#define _IORW 0x0080 + +/* constants used by _set_output_format */ +#define _TWO_DIGIT_EXPONENT 0x1 + +/* Function prototypes */ + +#ifndef _STDIO_DEFINED + +_Check_return_ _CRTIMP int __cdecl _filbuf(_Inout_ FILE * _File ); +_Check_return_opt_ _CRTIMP int __cdecl _flsbuf(_In_ int _Ch, _Inout_ FILE * _File); + +_Check_return_ _CRTIMP FILE * __cdecl _fsopen(_In_z_ const char * _Filename, _In_z_ const char * _Mode, _In_ int _ShFlag); + +_CRTIMP void __cdecl clearerr(_Inout_ FILE * _File); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl clearerr_s(_Inout_ FILE * _File ); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl fclose(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _fcloseall(void); + +_Check_return_ _CRTIMP FILE * __cdecl _fdopen(_In_ int _FileHandle, _In_z_ const char * _Mode); + +_Check_return_ _CRTIMP int __cdecl feof(_In_ FILE * _File); +_Check_return_ _CRTIMP int __cdecl ferror(_In_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl fflush(_Inout_opt_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl fgetc(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _fgetchar(void); +_Check_return_opt_ _CRTIMP int __cdecl fgetpos(_Inout_ FILE * _File , _Out_ fpos_t * _Pos); +_Check_return_opt_ _CRTIMP char * __cdecl fgets(_Out_writes_z_(_MaxCount) char * _Buf, _In_ int _MaxCount, _Inout_ FILE * _File); + +_Check_return_ _CRTIMP int __cdecl _fileno(_In_ FILE * _File); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_tempnam") +#undef _tempnam +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP char * __cdecl _tempnam(_In_opt_z_ const char * _DirName, _In_opt_z_ const char * _FilePrefix); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_tempnam") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_opt_ _CRTIMP int __cdecl _flushall(void); +_Check_return_ _CRT_INSECURE_DEPRECATE(fopen_s) _CRTIMP FILE * __cdecl fopen(_In_z_ const char * _Filename, _In_z_ const char * _Mode); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl fopen_s(_Outptr_result_maybenull_ FILE ** _File, _In_z_ const char * _Filename, _In_z_ const char * _Mode); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl fprintf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl fprintf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl fputc(_In_ int _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _fputchar(_In_ int _Ch); +_Check_return_opt_ _CRTIMP int __cdecl fputs(_In_z_ const char * _Str, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP size_t __cdecl fread(_Out_writes_bytes_(_ElementSize*_Count) void * _DstBuf, _In_ size_t _ElementSize, _In_ size_t _Count, _Inout_ FILE * _File); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP size_t __cdecl fread_s(_Out_writes_bytes_(_ElementSize*_Count) void * _DstBuf, _In_ size_t _DstSize, _In_ size_t _ElementSize, _In_ size_t _Count, _Inout_ FILE * _File); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ _CRT_INSECURE_DEPRECATE(freopen_s) _CRTIMP FILE * __cdecl freopen(_In_z_ const char * _Filename, _In_z_ const char * _Mode, _Inout_ FILE * _File); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl freopen_s(_Outptr_result_maybenull_ FILE ** _File, _In_z_ const char * _Filename, _In_z_ const char * _Mode, _Inout_ FILE * _OldFile); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ _CRT_INSECURE_DEPRECATE(fscanf_s) _CRTIMP int __cdecl fscanf(_Inout_ FILE * _File, _In_z_ _Scanf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_fscanf_s_l) _CRTIMP int __cdecl _fscanf_l(_Inout_ FILE * _File, _In_z_ _Scanf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(push) +#pragma warning(disable:6530) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl fscanf_s(_Inout_ FILE * _File, _In_z_ _Scanf_s_format_string_ const char * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl _fscanf_s_l(_Inout_ FILE * _File, _In_z_ _Scanf_s_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(pop) +_Check_return_opt_ _CRTIMP int __cdecl fsetpos(_Inout_ FILE * _File, _In_ const fpos_t * _Pos); +_Check_return_opt_ _CRTIMP int __cdecl fseek(_Inout_ FILE * _File, _In_ long _Offset, _In_ int _Origin); +_Check_return_ _CRTIMP long __cdecl ftell(_Inout_ FILE * _File); + +_Check_return_opt_ _CRTIMP int __cdecl _fseeki64(_Inout_ FILE * _File, _In_ __int64 _Offset, _In_ int _Origin); +_Check_return_ _CRTIMP __int64 __cdecl _ftelli64(_Inout_ FILE * _File); + +_Check_return_opt_ _CRTIMP size_t __cdecl fwrite(_In_reads_bytes_(_Size*_Count) const void * _Str, _In_ size_t _Size, _In_ size_t _Count, _Inout_ FILE * _File); +_Check_return_ _CRTIMP int __cdecl getc(_Inout_ FILE * _File); +_Check_return_ _CRTIMP int __cdecl getchar(void); +_Check_return_ _CRTIMP int __cdecl _getmaxstdio(void); +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP char * __cdecl gets_s(_Out_writes_z_(_Size) char * _Buf, _In_ rsize_t _Size); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(char *, gets_s, char, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(char *, __RETURN_POLICY_SAME, _CRTIMP, gets, _Pre_notnull_ _Post_z_ _Out_writes_z_(((size_t)-1)), char, _Buffer) +_Check_return_ int __cdecl _getw(_Inout_ FILE * _File); +#ifndef _CRT_PERROR_DEFINED +#define _CRT_PERROR_DEFINED +_CRTIMP void __cdecl perror(_In_opt_z_ const char * _ErrMsg); +#endif /* _CRT_PERROR_DEFINED */ +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_opt_ _CRTIMP int __cdecl _pclose(_Inout_ FILE * _File); +_Check_return_ _CRTIMP FILE * __cdecl _popen(_In_z_ const char * _Command, _In_z_ const char * _Mode); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ +_Check_return_opt_ _CRTIMP int __cdecl printf(_In_z_ _Printf_format_string_ const char * _Format, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl printf_s(_In_z_ _Printf_format_string_ const char * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl putc(_In_ int _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl putchar(_In_ int _Ch); +_Check_return_opt_ _CRTIMP int __cdecl puts(_In_z_ const char * _Str); +_Check_return_opt_ _CRTIMP int __cdecl _putw(_In_ int _Word, _Inout_ FILE * _File); +#ifndef _CRT_DIRECTORY_DEFINED +#define _CRT_DIRECTORY_DEFINED +_CRTIMP int __cdecl remove(_In_z_ const char * _Filename); +_Check_return_ _CRTIMP int __cdecl rename(_In_z_ const char * _OldFilename, _In_z_ const char * _NewFilename); +_CRTIMP int __cdecl _unlink(_In_z_ const char * _Filename); +#if !__STDC__ +_CRT_NONSTDC_DEPRECATE(_unlink) _CRTIMP int __cdecl unlink(_In_z_ const char * _Filename); +#endif /* !__STDC__ */ +#endif /* _CRT_DIRECTORY_DEFINED */ +_CRTIMP void __cdecl rewind(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _rmtmp(void); +_Check_return_ _CRT_INSECURE_DEPRECATE(scanf_s) _CRTIMP int __cdecl scanf(_In_z_ _Scanf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_scanf_s_l) _CRTIMP int __cdecl _scanf_l(_In_z_ _Scanf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(push) +#pragma warning(disable:6530) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl scanf_s(_In_z_ _Scanf_s_format_string_ const char * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _scanf_s_l(_In_z_ _Scanf_s_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(pop) +_CRT_INSECURE_DEPRECATE(setvbuf) _CRTIMP void __cdecl setbuf(_Inout_ FILE * _File, _Inout_updates_opt_(BUFSIZ) _Post_readable_size_(0) char * _Buffer); +_Check_return_opt_ _CRTIMP int __cdecl _setmaxstdio(_In_ int _Max); +_Check_return_opt_ _CRTIMP unsigned int __cdecl _set_output_format(_In_ unsigned int _Format); +_Check_return_opt_ _CRTIMP unsigned int __cdecl _get_output_format(void); +_Check_return_opt_ _CRTIMP int __cdecl setvbuf(_Inout_ FILE * _File, _Inout_updates_opt_z_(_Size) char * _Buf, _In_ int _Mode, _In_ size_t _Size); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snprintf_s(_Out_writes_z_(_SizeInBytes) char * _DstBuf, _In_ size_t _SizeInBytes, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, ...); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2_ARGLIST(int, _snprintf_s, _vsnprintf_s, _Post_z_ char, _Dest, _In_ size_t, _MaxCount, _In_z_ _Printf_format_string_ const char *,_Format) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl sprintf_s(_Out_writes_z_(_SizeInBytes) char * _DstBuf, _In_ size_t _SizeInBytes, _In_z_ _Printf_format_string_ const char * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1_ARGLIST(int, sprintf_s, vsprintf_s, _Post_z_ char, _Dest, _In_z_ _Printf_format_string_ const char *, _Format) +_Check_return_ _CRTIMP int __cdecl _scprintf(_In_z_ _Printf_format_string_ const char * _Format, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(sscanf_s) _CRTIMP int __cdecl sscanf(_In_z_ const char * _Src, _In_z_ _Scanf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_sscanf_s_l) _CRTIMP int __cdecl _sscanf_l(_In_z_ const char * _Src, _In_z_ _Scanf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(push) +#pragma warning(disable:6530) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl sscanf_s(_In_z_ const char * _Src, _In_z_ _Scanf_s_format_string_ const char * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _sscanf_s_l(_In_z_ const char * _Src, _In_z_ _Scanf_s_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snscanf_s) _CRTIMP int __cdecl _snscanf(_In_reads_bytes_(_MaxCount) _Pre_z_ const char * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snscanf_s_l) _CRTIMP int __cdecl _snscanf_l(_In_reads_bytes_(_MaxCount) _Pre_z_ const char * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snscanf_s(_In_reads_bytes_(_MaxCount) _Pre_z_ const char * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_s_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snscanf_s_l(_In_reads_bytes_(_MaxCount) _Pre_z_ const char * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_s_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(pop) +_Check_return_ _CRT_INSECURE_DEPRECATE(tmpfile_s) _CRTIMP FILE * __cdecl tmpfile(void); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl tmpfile_s(_Out_opt_ _Deref_post_valid_ FILE ** _File); +_Check_return_wat_ _CRTIMP errno_t __cdecl tmpnam_s(_Out_writes_z_(_Size) char * _Buf, _In_ rsize_t _Size); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, tmpnam_s, _Post_z_ char, _Buf) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(char *, __RETURN_POLICY_DST, _CRTIMP, tmpnam, _Pre_maybenull_ _Post_z_, char, _Buffer) +_Check_return_opt_ _CRTIMP int __cdecl ungetc(_In_ int _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl vfprintf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vfscanf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl vfprintf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vfscanf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl vprintf(_In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vscanf(_In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl vprintf_s(_In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vscanf_s(_In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(vsnprintf_s) _CRTIMP int __cdecl vsnprintf(_Out_writes_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl vsnprintf_s(_Out_writes_z_(_DstSize) char * _DstBuf, _In_ size_t _DstSize, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(int, vsnprintf_s, _Post_z_ char, _Dest, _In_ size_t, _MaxCount, _In_z_ _Printf_format_string_ const char *, _Format, va_list, _Args) +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vsnprintf_s(_Out_writes_z_(_SizeInBytes) char * _DstBuf, _In_ size_t _SizeInBytes, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(int, _vsnprintf_s, _Post_z_ char, _Dest, _In_ size_t, _MaxCount, _In_z_ _Printf_format_string_ const char *, _Format, va_list, _Args) +#pragma warning(push) +#pragma warning(disable:4793) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_ARGLIST_EX(int, __RETURN_POLICY_SAME, _CRTIMP, _snprintf, _vsnprintf, _Pre_notnull_ _Post_maybez_ char, _Out_writes_(_Count) _Post_maybez_, char, _Dest, _In_ size_t, _Count, _In_z_ _Printf_format_string_ const char *, _Format) +#pragma warning(pop) +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP_ALTERNATIVE int __cdecl vsprintf_s(_Out_writes_z_(_SizeInBytes) char * _DstBuf, _In_ size_t _SizeInBytes, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(int, vsprintf_s, _Post_z_ char, _Dest, _In_z_ _Printf_format_string_ const char *, _Format, va_list, _Args) +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl vsscanf_s(const char * _Src, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(int, vsscanf_s, _Post_z_ const char, _Src, _In_z_ _Printf_format_string_ const char *, _Format, va_list, _Args) +#endif /* __STDC_WANT_SECURE_LIB__ */ +#pragma warning(push) +#pragma warning(disable:4793) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_ARGLIST(int, __RETURN_POLICY_SAME, _CRTIMP, sprintf, vsprintf, _Pre_notnull_ _Post_z_, char, _Dest, _In_z_ _Printf_format_string_ const char *, _Format) +_Check_return_opt_ _CRTIMP int __cdecl vsscanf(const char * _srcBuf, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +#pragma warning(pop) +_Check_return_ _CRTIMP int __cdecl _vscprintf(_In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _snprintf_c(_Out_writes_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vsnprintf_c(_Out_writes_(_MaxCount) char *_DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _fprintf_p(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _printf_p(_In_z_ _Printf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _sprintf_p(_Out_writes_z_(_MaxCount) char * _Dst, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vfprintf_p(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vprintf_p(_In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vsprintf_p(_Out_writes_z_(_MaxCount) char * _Dst, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_Check_return_ _CRTIMP int __cdecl _scprintf_p(_In_z_ _Printf_format_string_ const char * _Format, ...); +_Check_return_ _CRTIMP int __cdecl _vscprintf_p(_In_z_ _Printf_format_string_ const char * _Format, va_list _ArgList); +_CRTIMP int __cdecl _set_printf_count_output(_In_ int _Value); +_CRTIMP int __cdecl _get_printf_count_output(void); + +_Check_return_opt_ _CRTIMP int __cdecl _printf_l(_In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _printf_p_l(_In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _printf_s_l(_In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vprintf_l(_In_z_ _Printf_format_string_params_(2) const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vprintf_p_l(_In_z_ _Printf_format_string_params_(2) const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vprintf_s_l(_In_z_ _Printf_format_string_params_(2) const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _fprintf_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _fprintf_p_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _fprintf_s_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vfprintf_l(_Inout_ FILE * _File, _In_z_ const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vfprintf_p_l(_Inout_ FILE * _File, _In_z_ const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vfprintf_s_l(_Inout_ FILE * _File, _In_z_ const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_sprintf_s_l) _CRTIMP int __cdecl _sprintf_l(_Pre_notnull_ _Post_z_ char * _DstBuf, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _sprintf_p_l(_Out_writes_z_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _sprintf_s_l(_Out_writes_z_(_DstSize) char * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_vsprintf_s_l) _CRTIMP int __cdecl _vsprintf_l(_Pre_notnull_ _Post_z_ char * _DstBuf, _In_z_ const char * _Format, _In_opt_ _locale_t, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vsprintf_p_l(_Out_writes_z_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const char* _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vsprintf_s_l(_Out_writes_z_(_DstSize) char * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_format_string_params_(2) const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _scprintf_l(_In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _scprintf_p_l(_In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vscprintf_l(_In_z_ _Printf_format_string_params_(2) const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vscprintf_p_l(_In_z_ _Printf_format_string_params_(2) const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snprintf_s_l) _CRTIMP int __cdecl _snprintf_l(_Out_writes_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _snprintf_c_l(_Out_writes_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snprintf_s_l(_Out_writes_z_(_DstSize) char * _DstBuf, _In_ size_t _DstSize, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const char * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_vsnprintf_s_l) _CRTIMP int __cdecl _vsnprintf_l(_Out_writes_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const char * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vsnprintf_c_l(_Out_writes_(_MaxCount) char * _DstBuf, _In_ size_t _MaxCount, const char *, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vsnprintf_s_l(_Out_writes_z_(_DstSize) char * _DstBuf, _In_ size_t _DstSize, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const char* _Format,_In_opt_ _locale_t _Locale, va_list _ArgList); + +#ifndef _WSTDIO_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +#ifndef WEOF +#define WEOF (wint_t)(0xFFFF) +#endif /* WEOF */ + +_Check_return_ _CRTIMP FILE * __cdecl _wfsopen(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode, _In_ int _ShFlag); + +_Check_return_opt_ _CRTIMP wint_t __cdecl fgetwc(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _fgetwchar(void); +_Check_return_opt_ _CRTIMP wint_t __cdecl fputwc(_In_ wchar_t _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _fputwchar(_In_ wchar_t _Ch); +_Check_return_ _CRTIMP wint_t __cdecl getwc(_Inout_ FILE * _File); +_Check_return_ _CRTIMP wint_t __cdecl getwchar(void); +_Check_return_opt_ _CRTIMP wint_t __cdecl putwc(_In_ wchar_t _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl putwchar(_In_ wchar_t _Ch); +_Check_return_opt_ _CRTIMP wint_t __cdecl ungetwc(_In_ wint_t _Ch, _Inout_ FILE * _File); + +_Check_return_opt_ _CRTIMP wchar_t * __cdecl fgetws(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ int _SizeInWords, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl fputws(_In_z_ const wchar_t * _Str, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wchar_t * __cdecl _getws_s(_Out_writes_z_(_SizeInWords) wchar_t * _Str, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(wchar_t *, _getws_s, _Post_z_ wchar_t, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_SAME, _CRTIMP, _getws, _Pre_notnull_ _Post_z_, wchar_t, _String) +_Check_return_opt_ _CRTIMP int __cdecl _putws(_In_z_ const wchar_t * _Str); + +_Check_return_opt_ _CRTIMP int __cdecl fwprintf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl fwprintf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl wprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl wprintf_s(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ _CRTIMP int __cdecl _scwprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl vfwprintf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vfwscanf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl vfwprintf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vfwscanf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl vwprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vwscanf(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl vwprintf_s(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vwscanf_s(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ + +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP_ALTERNATIVE int __cdecl swprintf_s(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1_ARGLIST(int, swprintf_s, vswprintf_s, _Post_z_ wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format) +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP_ALTERNATIVE int __cdecl vswprintf_s(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl vswscanf_s(const wchar_t * _Src, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(int, vswprintf_s, _Post_z_ wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format, va_list, _Args) +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(int, vswscanf_s, _Post_z_ wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format, va_list, _Args) +_Check_return_opt_ _CRTIMP int __cdecl vswscanf(const wchar_t * _srcBuf, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_c(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_c(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwprintf_s(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2_ARGLIST(int, _snwprintf_s, _vsnwprintf_s, _Post_z_ wchar_t, _Dest, _In_ size_t, _Count, _In_z_ _Printf_format_string_ const wchar_t *, _Format) +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vsnwprintf_s(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(int, _vsnwprintf_s, _Post_z_ wchar_t, _Dest, _In_ size_t, _Count, _In_z_ _Printf_format_string_ const wchar_t *, _Format, va_list, _Args) +#pragma warning(push) +#pragma warning(disable:4793) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_ARGLIST_EX(int, __RETURN_POLICY_SAME, _CRTIMP, _snwprintf, _vsnwprintf, _Pre_notnull_ _Post_maybez_ wchar_t, _Out_writes_(_Count) _Post_maybez_, wchar_t, _Dest, _In_ size_t, _Count, _In_z_ _Printf_format_string_ const wchar_t *, _Format) +#pragma warning(pop) + +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_p(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_p(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_p(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_p(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_ _CRTIMP int __cdecl _scwprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_ _CRTIMP int __cdecl _vscwprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_p_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_s_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_p_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_s_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_p_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_s_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_p_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_s_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_c_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_p_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _swprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_c_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_p_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vswprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_ _CRTIMP int __cdecl _scwprintf_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRTIMP int __cdecl _scwprintf_p_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRTIMP int __cdecl _vscwprintf_p_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snwprintf_s_l) _CRTIMP int __cdecl _snwprintf_l(_Out_writes_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_vsnwprintf_s_l) _CRTIMP int __cdecl _vsnwprintf_l(_Out_writes_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vsnwprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + + +#ifndef _CRT_NON_CONFORMING_SWPRINTFS + +#define _SWPRINTFS_DEPRECATED _CRT_DEPRECATE_TEXT("swprintf has been changed to conform with the ISO C standard, adding an extra character count parameter. To use traditional Microsoft swprintf, set _CRT_NON_CONFORMING_SWPRINTFS.") + +#else /* _CRT_NON_CONFORMING_SWPRINTFS */ + +#define _SWPRINTFS_DEPRECATED + +#endif /* _CRT_NON_CONFORMING_SWPRINTFS */ + +/* we could end up with a double deprecation, disable warnings 4141 and 4996 */ +#pragma warning(push) +#pragma warning(disable:4141 4996 4793) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_ARGLIST_EX(int, __RETURN_POLICY_SAME, _SWPRINTFS_DEPRECATED _CRTIMP, _swprintf, _swprintf_s, _vswprintf, vswprintf_s, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_2_ARGLIST_EX(int, __RETURN_POLICY_SAME, _SWPRINTFS_DEPRECATED _CRTIMP, __swprintf_l, __vswprintf_l, _vswprintf_s_l, _Pre_notnull_ _Post_z_ wchar_t, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_z_ _Printf_format_string_params_(2) const wchar_t *, _Format, _locale_t, _Plocinfo) +#pragma warning(pop) + +#if !defined (RC_INVOKED) && !defined (__midl) +#include <swprintf.inl> +#endif /* !defined (RC_INVOKED) && !defined (__midl) */ + +#ifdef _CRT_NON_CONFORMING_SWPRINTFS +#ifndef __cplusplus +#define swprintf _swprintf +#define vswprintf _vswprintf +#define _swprintf_l __swprintf_l +#define _vswprintf_l __vswprintf_l +#endif /* __cplusplus */ +#endif /* _CRT_NON_CONFORMING_SWPRINTFS */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wtempnam") +#undef _wtempnam +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP wchar_t * __cdecl _wtempnam(_In_opt_z_ const wchar_t * _Directory, _In_opt_z_ const wchar_t * _FilePrefix); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wtempnam") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP int __cdecl _vscwprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_ _CRTIMP int __cdecl _vscwprintf_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_ _CRT_INSECURE_DEPRECATE(fwscanf_s) _CRTIMP int __cdecl fwscanf(_Inout_ FILE * _File, _In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_fwscanf_s_l) _CRTIMP int __cdecl _fwscanf_l(_Inout_ FILE * _File, _In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(push) +#pragma warning(disable:6530) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl fwscanf_s(_Inout_ FILE * _File, _In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl _fwscanf_s_l(_Inout_ FILE * _File, _In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(swscanf_s) _CRTIMP int __cdecl swscanf(_In_z_ const wchar_t * _Src, _In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_swscanf_s_l) _CRTIMP int __cdecl _swscanf_l(_In_z_ const wchar_t * _Src, _In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl swscanf_s(_In_z_ const wchar_t *_Src, _In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _swscanf_s_l(_In_z_ const wchar_t * _Src, _In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snwscanf_s) _CRTIMP int __cdecl _snwscanf(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snwscanf_s_l) _CRTIMP int __cdecl _snwscanf_l(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwscanf_s(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwscanf_s_l(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(wscanf_s) _CRTIMP int __cdecl wscanf(_In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_wscanf_s_l) _CRTIMP int __cdecl _wscanf_l(_In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl wscanf_s(_In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _wscanf_s_l(_In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +#pragma warning(pop) + +_Check_return_ _CRTIMP FILE * __cdecl _wfdopen(_In_ int _FileHandle , _In_z_ const wchar_t * _Mode); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wfopen_s) _CRTIMP FILE * __cdecl _wfopen(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wfopen_s(_Outptr_result_maybenull_ FILE ** _File, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wfreopen_s) _CRTIMP FILE * __cdecl _wfreopen(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode, _Inout_ FILE * _OldFile); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wfreopen_s(_Outptr_result_maybenull_ FILE ** _File, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode, _Inout_ FILE * _OldFile); + +#ifndef _CRT_WPERROR_DEFINED +#define _CRT_WPERROR_DEFINED +_CRTIMP void __cdecl _wperror(_In_opt_z_ const wchar_t * _ErrMsg); +#endif /* _CRT_WPERROR_DEFINED */ +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP FILE * __cdecl _wpopen(_In_z_ const wchar_t *_Command, _In_z_ const wchar_t * _Mode); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ +_CRTIMP int __cdecl _wremove(_In_z_ const wchar_t * _Filename); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wtmpnam_s(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wtmpnam_s, _Post_z_ wchar_t, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wtmpnam, _Pre_maybenull_ _Post_z_, wchar_t, _Buffer) + +_Check_return_opt_ _CRTIMP wint_t __cdecl _fgetwc_nolock(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _fputwc_nolock(_In_ wchar_t _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _ungetwc_nolock(_In_ wint_t _Ch, _Inout_ FILE * _File); + +#ifdef _CRTBLD +#define _CRT_GETPUTWCHAR_NOINLINE +#else /* _CRTBLD */ +#undef _CRT_GETPUTWCHAR_NOINLINE +#endif /* _CRTBLD */ + +#if !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (_CRT_GETPUTWCHAR_NOINLINE) +#define getwchar() fgetwc(stdin) +#define putwchar(_c) fputwc((_c),stdout) +#else /* !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (_CRT_GETPUTWCHAR_NOINLINE) */ +inline _Check_return_ wint_t __CRTDECL getwchar() + {return (fgetwc(stdin)); } /* stdin */ +inline _Check_return_opt_ wint_t __CRTDECL putwchar(_In_ wchar_t _C) + {return (fputwc(_C, stdout)); } /* stdout */ +#endif /* !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (_CRT_GETPUTWCHAR_NOINLINE) */ + +#define getwc(_stm) fgetwc(_stm) +#define putwc(_c,_stm) fputwc(_c,_stm) +#define _putwc_nolock(_c,_stm) _fputwc_nolock(_c,_stm) +#define _getwc_nolock(_stm) _fgetwc_nolock(_stm) + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define fgetwc(_stm) _getwc_nolock(_stm) +#define fputwc(_c,_stm) _putwc_nolock(_c,_stm) +#define ungetwc(_c,_stm) _ungetwc_nolock(_c,_stm) +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ + +#define _WSTDIO_DEFINED +#endif /* _WSTDIO_DEFINED */ + +#define _STDIO_DEFINED +#endif /* _STDIO_DEFINED */ + + +/* Macro definitions */ + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define feof(_stream) ((_stream)->_flag & _IOEOF) +#define ferror(_stream) ((_stream)->_flag & _IOERR) +#define _fileno(_stream) ((_stream)->_file) +#define fgetc(_stream) (--(_stream)->_cnt >= 0 \ + ? 0xff & *(_stream)->_ptr++ : _filbuf(_stream)) +#define putc(_c,_stream) (--(_stream)->_cnt >= 0 \ + ? 0xff & (*(_stream)->_ptr++ = (char)(_c)) : _flsbuf((_c),(_stream))) +#define getc(_stream) fgetc(_stream) +#define getchar() getc(stdin) +#define putchar(_c) putc((_c),stdout) +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ + + +#define _fgetc_nolock(_stream) (--(_stream)->_cnt >= 0 ? 0xff & *(_stream)->_ptr++ : _filbuf(_stream)) +#define _fputc_nolock(_c,_stream) (--(_stream)->_cnt >= 0 ? 0xff & (*(_stream)->_ptr++ = (char)(_c)) : _flsbuf((_c),(_stream))) +#define _getc_nolock(_stream) _fgetc_nolock(_stream) +#define _putc_nolock(_c, _stream) _fputc_nolock(_c, _stream) +#define _getchar_nolock() _getc_nolock(stdin) +#define _putchar_nolock(_c) _putc_nolock((_c),stdout) +#define _getwchar_nolock() _getwc_nolock(stdin) +#define _putwchar_nolock(_c) _putwc_nolock((_c),stdout) + +_CRTIMP void __cdecl _lock_file(_Inout_ FILE * _File); +_CRTIMP void __cdecl _unlock_file(_Inout_ FILE * _File); + +_Check_return_opt_ _CRTIMP int __cdecl _fclose_nolock(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _fflush_nolock(_Inout_opt_ FILE * _File); +_Check_return_opt_ _CRTIMP size_t __cdecl _fread_nolock(_Out_writes_bytes_(_ElementSize*_Count) void * _DstBuf, _In_ size_t _ElementSize, _In_ size_t _Count, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP size_t __cdecl _fread_nolock_s(_Out_writes_bytes_(_ElementSize*_Count) void * _DstBuf, _In_ size_t _DstSize, _In_ size_t _ElementSize, _In_ size_t _Count, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _fseek_nolock(_Inout_ FILE * _File, _In_ long _Offset, _In_ int _Origin); +_Check_return_ _CRTIMP long __cdecl _ftell_nolock(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _fseeki64_nolock(_Inout_ FILE * _File, _In_ __int64 _Offset, _In_ int _Origin); +_Check_return_ _CRTIMP __int64 __cdecl _ftelli64_nolock(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP size_t __cdecl _fwrite_nolock(_In_reads_bytes_(_Size*_Count) const void * _DstBuf, _In_ size_t _Size, _In_ size_t _Count, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl _ungetc_nolock(_In_ int _Ch, _Inout_ FILE * _File); + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define fclose(_stm) _fclose_nolock(_stm) +#define fflush(_stm) _fflush_nolock(_stm) +#define fread(_DstBuf, _ElementSize, _Count, _File) _fread_nolock(_DstBuf, _ElementSize, _Count, _File) +#define fread_s(_DstBuf, _DstSize, _ElementSize, _Count, _File) _fread_nolock_s(_DstBuf, _DstSize, _ElementSize, _Count, _File) +#define fseek(_stm,_offset,_origin) _fseek_nolock(_stm,_offset,_origin) +#define ftell(_stm) _ftell_nolock(_stm) +#define _fseeki64(_stm,_offset,_origin) _fseeki64_nolock(_stm,_offset,_origin) +#define _ftelli64(_stm) _ftelli64_nolock(_stm) +#define fwrite(_buf,_siz,_cnt,_stm) _fwrite_nolock(_buf,_siz,_cnt,_stm) +#define ungetc(_c,_stm) _ungetc_nolock(_c,_stm) +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ + +#if !__STDC__ + +/* Non-ANSI names for compatibility */ + +#define P_tmpdir _P_tmpdir +#define SYS_OPEN _SYS_OPEN + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("tempnam") +#undef tempnam +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_CRT_NONSTDC_DEPRECATE(_tempnam) _CRTIMP char * __cdecl tempnam(_In_opt_z_ const char * _Directory, _In_opt_z_ const char * _FilePrefix); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("tempnam") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_opt_ _CRT_NONSTDC_DEPRECATE(_fcloseall) _CRTIMP int __cdecl fcloseall(void); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_fdopen) _CRTIMP FILE * __cdecl fdopen(_In_ int _FileHandle, _In_z_ const char * _Format); +_Check_return_opt_ _CRT_NONSTDC_DEPRECATE(_fgetchar) _CRTIMP int __cdecl fgetchar(void); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_fileno) _CRTIMP int __cdecl fileno(_In_ FILE * _File); +_Check_return_opt_ _CRT_NONSTDC_DEPRECATE(_flushall) _CRTIMP int __cdecl flushall(void); +_Check_return_opt_ _CRT_NONSTDC_DEPRECATE(_fputchar) _CRTIMP int __cdecl fputchar(_In_ int _Ch); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_getw) _CRTIMP int __cdecl getw(_Inout_ FILE * _File); +_Check_return_opt_ _CRT_NONSTDC_DEPRECATE(_putw) _CRTIMP int __cdecl putw(_In_ int _Ch, _Inout_ FILE * _File); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_rmtmp) _CRTIMP int __cdecl rmtmp(void); + +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_STDIO */ diff --git a/test_data/lots_of_files/stdlib.h b/test_data/lots_of_files/stdlib.h new file mode 100644 index 0000000..9c82377 --- /dev/null +++ b/test_data/lots_of_files/stdlib.h @@ -0,0 +1,959 @@ +/*** +*stdlib.h - declarations/definitions for commonly used library functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This include file contains the function declarations for commonly +* used library functions which either don't fit somewhere else, or, +* cannot be declared in the normal place for other reasons. +* [ANSI] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_STDLIB +#define _INC_STDLIB + +#include <crtdefs.h> +#include <limits.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + +/* Definition of the argument values for the exit() function */ + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + + +#ifndef _ONEXIT_T_DEFINED + +#if !defined (_M_CEE_PURE) +typedef int (__cdecl * _onexit_t)(void); +#else /* !defined (_M_CEE_PURE) */ +typedef int (__clrcall * _onexit_t)(void); +typedef _onexit_t _onexit_m_t; +#endif /* !defined (_M_CEE_PURE) */ + +#if defined (_M_CEE_MIXED) +typedef int (__clrcall * _onexit_m_t)(void); +#endif /* defined (_M_CEE_MIXED) */ + +#if !__STDC__ +/* Non-ANSI name for compatibility */ +#define onexit_t _onexit_t +#endif /* !__STDC__ */ + +#define _ONEXIT_T_DEFINED +#endif /* _ONEXIT_T_DEFINED */ + + +/* Data structure definitions for div and ldiv runtimes. */ + +#ifndef _DIV_T_DEFINED + +typedef struct _div_t { + int quot; + int rem; +} div_t; + +typedef struct _ldiv_t { + long quot; + long rem; +} ldiv_t; + +typedef struct _lldiv_t { + long long quot; + long long rem; +} lldiv_t; + +#define _DIV_T_DEFINED +#endif /* _DIV_T_DEFINED */ + +/* + * structs used to fool the compiler into not generating floating point + * instructions when copying and pushing [long] double values + */ + +#ifndef _CRT_DOUBLE_DEC + +#ifndef _LDSUPPORT + +#pragma pack(4) +typedef struct { + unsigned char ld[10]; +} _LDOUBLE; +#pragma pack() + +#define _PTR_LD(x) ((unsigned char *)(&(x)->ld)) + +#else /* _LDSUPPORT */ + +/* push and pop long, which is #defined as __int64 by a spec2k test */ +#pragma push_macro("long") +#undef long +typedef long double _LDOUBLE; +#pragma pop_macro("long") + +#define _PTR_LD(x) ((unsigned char *)(x)) + +#endif /* _LDSUPPORT */ + +typedef struct { + double x; +} _CRT_DOUBLE; + +typedef struct { + float f; +} _CRT_FLOAT; + +/* push and pop long, which is #defined as __int64 by a spec2k test */ +#pragma push_macro("long") +#undef long + +typedef struct { + /* + * Assume there is a long double type + */ + long double x; +} _LONGDOUBLE; + +#pragma pop_macro("long") + +#pragma pack(4) +typedef struct { + unsigned char ld12[12]; +} _LDBL12; +#pragma pack() + +#define _CRT_DOUBLE_DEC +#endif /* _CRT_DOUBLE_DEC */ + +/* Maximum value that can be returned by the rand function. */ + +#define RAND_MAX 0x7fff + +/* + * Maximum number of bytes in multi-byte character in the current locale + * (also defined in ctype.h). + */ +#ifndef MB_CUR_MAX +#ifdef _CRTBLD +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP int * __cdecl __p___mb_cur_max(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#define __MB_CUR_MAX(ptloci) (ptloci)->mb_cur_max +#endif /* _CRTBLD */ +#define MB_CUR_MAX ___mb_cur_max_func() +#if !defined (_M_CEE_PURE) +_CRTIMP extern int __mb_cur_max; +#else /* !defined (_M_CEE_PURE) */ +_CRTIMP int* __cdecl __p___mb_cur_max(void); +#define __mb_cur_max (*__p___mb_cur_max()) +#endif /* !defined (_M_CEE_PURE) */ +_CRTIMP int __cdecl ___mb_cur_max_func(void); +_CRTIMP int __cdecl ___mb_cur_max_l_func(_locale_t); +#endif /* MB_CUR_MAX */ + +/* Minimum and maximum macros */ + +#define __max(a,b) (((a) > (b)) ? (a) : (b)) +#define __min(a,b) (((a) < (b)) ? (a) : (b)) + +/* + * Sizes for buffers used by the _makepath() and _splitpath() functions. + * note that the sizes include space for 0-terminator + */ +#define _MAX_PATH 260 /* max. length of full pathname */ +#define _MAX_DRIVE 3 /* max. length of drive component */ +#define _MAX_DIR 256 /* max. length of path component */ +#define _MAX_FNAME 256 /* max. length of file name component */ +#define _MAX_EXT 256 /* max. length of extension component */ + +/* + * Argument values for _set_error_mode(). + */ +#define _OUT_TO_DEFAULT 0 +#define _OUT_TO_STDERR 1 +#define _OUT_TO_MSGBOX 2 +#define _REPORT_ERRMODE 3 + +/* + * Argument values for _set_abort_behavior(). + */ +#define _WRITE_ABORT_MSG 0x1 /* debug only, has no effect in release */ +#define _CALL_REPORTFAULT 0x2 + +/* + * Sizes for buffers used by the getenv/putenv family of functions. + */ +#define _MAX_ENV 32767 + +#if !defined (_M_CEE_PURE) +/* a purecall handler procedure. Never returns normally */ +typedef void (__cdecl *_purecall_handler)(void); + +/* establishes a purecall handler for the process */ +_CRTIMP _purecall_handler __cdecl _set_purecall_handler(_In_opt_ _purecall_handler _Handler); +_CRTIMP _purecall_handler __cdecl _get_purecall_handler(void); +#endif /* !defined (_M_CEE_PURE) */ + +#if defined (__cplusplus) +extern "C++" +{ +#if defined (_M_CEE_PURE) + typedef void (__clrcall *_purecall_handler)(void); + typedef _purecall_handler _purecall_handler_m; + _MRTIMP _purecall_handler __cdecl _set_purecall_handler(_In_opt_ _purecall_handler _Handler); +#endif /* defined (_M_CEE_PURE) */ +} +#endif /* defined (__cplusplus) */ + +#if !defined (_M_CEE_PURE) +/* a invalid_arg handler procedure. */ +typedef void (__cdecl *_invalid_parameter_handler)(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t); + +/* establishes a invalid_arg handler for the process */ +_CRTIMP _invalid_parameter_handler __cdecl _set_invalid_parameter_handler(_In_opt_ _invalid_parameter_handler _Handler); +_CRTIMP _invalid_parameter_handler __cdecl _get_invalid_parameter_handler(void); +#endif /* !defined (_M_CEE_PURE) */ + +/* External variable declarations */ +#ifndef _CRT_ERRNO_DEFINED +#define _CRT_ERRNO_DEFINED +_CRTIMP extern int * __cdecl _errno(void); +#define errno (*_errno()) + +errno_t __cdecl _set_errno(_In_ int _Value); +errno_t __cdecl _get_errno(_Out_ int * _Value); +#endif /* _CRT_ERRNO_DEFINED */ + +_CRTIMP unsigned long * __cdecl __doserrno(void); +#define _doserrno (*__doserrno()) + +errno_t __cdecl _set_doserrno(_In_ unsigned long _Value); +errno_t __cdecl _get_doserrno(_Out_ unsigned long * _Value); + +/* you can't modify this, but it is non-const for backcompat */ +_CRTIMP _CRT_INSECURE_DEPRECATE(strerror) char ** __cdecl __sys_errlist(void); +#define _sys_errlist (__sys_errlist()) + +_CRTIMP _CRT_INSECURE_DEPRECATE(strerror) int * __cdecl __sys_nerr(void); +#define _sys_nerr (*__sys_nerr()) + +#if defined (_DLL) && defined (_M_IX86) + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRTIMP int * __cdecl __p___argc(void); +_CRTIMP char *** __cdecl __p___argv(void); +_CRTIMP wchar_t *** __cdecl __p___wargv(void); +_CRTIMP char *** __cdecl __p__environ(void); +_CRTIMP wchar_t *** __cdecl __p__wenviron(void); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRTIMP char ** __cdecl __p__pgmptr(void); +_CRTIMP wchar_t ** __cdecl __p__wpgmptr(void); + +#ifdef _CRTBLD +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP int * __cdecl __p__fmode(void); +#endif /* _CRTBLD */ + +#endif /* defined (_DLL) && defined (_M_IX86) */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +#if !defined (_M_CEE_PURE) +_CRTIMP extern int __argc; /* count of cmd line args */ +_CRTIMP extern char ** __argv; /* pointer to table of cmd line args */ +_CRTIMP extern wchar_t ** __wargv; /* pointer to table of wide cmd line args */ +#else /* !defined (_M_CEE_PURE) */ +_CRTIMP int* __cdecl __p___argc(void); +_CRTIMP char*** __cdecl __p___argv(void); +_CRTIMP wchar_t*** __cdecl __p___wargv(void); +#define __argv (*__p___argv()) +#define __argc (*__p___argc()) +#define __wargv (*__p___wargv()) +#endif /* !defined (_M_CEE_PURE) */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#if !defined (_M_CEE_PURE) + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +_CRTIMP extern char ** _environ; /* pointer to environment table */ +_CRTIMP extern wchar_t ** _wenviron; /* pointer to wide environment table */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_pgmptr) _CRTIMP extern char * _pgmptr; /* points to the module (EXE) name */ +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_wpgmptr) _CRTIMP extern wchar_t * _wpgmptr; /* points to the module (EXE) wide name */ + +#ifdef _CRTBLD +_DEFINE_SET_FUNCTION(_set_pgmptr, char *, _pgmptr) +_DEFINE_SET_FUNCTION(_set_wpgmptr, wchar_t *, _wpgmptr) +#endif /* _CRTBLD */ + +#else /* !defined (_M_CEE_PURE) */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRTIMP char*** __cdecl __p__environ(void); +_CRTIMP wchar_t*** __cdecl __p__wenviron(void); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_pgmptr) _CRTIMP char** __cdecl __p__pgmptr(void); +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_wpgmptr) _CRTIMP wchar_t** __cdecl __p__wpgmptr(void); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +#define _environ (*__p__environ()) +#define _wenviron (*__p__wenviron()) +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#define _pgmptr (*__p__pgmptr()) +#define _wpgmptr (*__p__wpgmptr()) + +#endif /* !defined (_M_CEE_PURE) */ + +errno_t __cdecl _get_pgmptr(_Outptr_result_z_ char ** _Value); +errno_t __cdecl _get_wpgmptr(_Outptr_result_z_ wchar_t ** _Value); + +#if defined(SPECIAL_CRTEXE) && defined(_CRTBLD) +extern int _fmode; /* default file translation mode */ +#else /* defined(SPECIAL_CRTEXE) && defined(_CRTBLD) */ +#if !defined (_M_CEE_PURE) +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_fmode) _CRTIMP extern int _fmode; /* default file translation mode */ +#else /* !defined (_M_CEE_PURE) */ +_CRTIMP int* __cdecl __p__fmode(void); +#define _fmode (*__p__fmode()) +#endif /* !defined (_M_CEE_PURE) */ +#endif /* defined(SPECIAL_CRTEXE) && defined(_CRTBLD) */ + +_CRTIMP errno_t __cdecl _set_fmode(_In_ int _Mode); +_CRTIMP errno_t __cdecl _get_fmode(_Out_ int * _PMode); + +/* _countof helper */ +#if !defined (_countof) +#if !defined (__cplusplus) +#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0])) +#else /* !defined (__cplusplus) */ +extern "C++" +{ +template <typename _CountofType, size_t _SizeOfArray> +char (*__countof_helper(_UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray]; +#define _countof(_Array) (sizeof(*__countof_helper(_Array)) + 0) +} +#endif /* !defined (__cplusplus) */ +#endif /* !defined (_countof) */ + +/* function prototypes */ + +#ifndef _CRT_TERMINATE_DEFINED +#define _CRT_TERMINATE_DEFINED +#pragma push_macro("exit") +#undef exit +_CRTIMP __declspec(noreturn) void __cdecl exit(_In_ int _Code); +#pragma pop_macro("exit") +_CRTIMP __declspec(noreturn) void __cdecl _exit(_In_ int _Code); +_CRTIMP __declspec(noreturn) void __cdecl abort(void); +#endif /* _CRT_TERMINATE_DEFINED */ + +_CRTIMP unsigned int __cdecl _set_abort_behavior(_In_ unsigned int _Flags, _In_ unsigned int _Mask); + +int __cdecl abs(_In_ int _X); +long __cdecl labs(_In_ long _X); +long long __cdecl llabs(_In_ long long _X); + + __int64 __cdecl _abs64(__int64); +#if defined (_M_CEE) +#pragma warning (push) +#pragma warning (disable: 4985) + _Check_return_ int __clrcall _atexit_m_appdomain(_In_opt_ void (__clrcall * _Func)(void)); +#if defined (_M_CEE_MIXED) +#ifdef __cplusplus + [System::Security::SecurityCritical] +#endif /* __cplusplus */ +#pragma warning (suppress: 4985) + _Check_return_ int __clrcall _atexit_m(_In_opt_ void (__clrcall * _Func)(void)); +#else /* defined (_M_CEE_MIXED) */ +#ifdef __cplusplus + [System::Security::SecurityCritical] +#endif /* __cplusplus */ + _Check_return_ inline int __clrcall _atexit_m(_In_opt_ void (__clrcall *_Function)(void)) + { + return _atexit_m_appdomain(_Function); + } +#endif /* defined (_M_CEE_MIXED) */ +#pragma warning (pop) +#endif /* defined (_M_CEE) */ +#if defined (_M_CEE_PURE) + /* In pure mode, atexit is the same as atexit_m_appdomain */ +extern "C++" +{ +#ifdef __cplusplus + [System::Security::SecurityCritical] +#endif /* __cplusplus */ +inline int __clrcall atexit +( + void (__clrcall *_Function)(void) +) +{ + return _atexit_m_appdomain(_Function); +} +} +#else /* defined (_M_CEE_PURE) */ + int __cdecl atexit(void (__cdecl *)(void)); +#endif /* defined (_M_CEE_PURE) */ +_Check_return_ _CRTIMP double __cdecl atof(_In_z_ const char *_String); +_Check_return_ _CRTIMP double __cdecl _atof_l(_In_z_ const char *_String, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP _CRT_JIT_INTRINSIC int __cdecl atoi(_In_z_ const char *_Str); +_Check_return_ _CRTIMP int __cdecl _atoi_l(_In_z_ const char *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long __cdecl atol(_In_z_ const char *_Str); +_Check_return_ _CRTIMP long __cdecl _atol_l(_In_z_ const char *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long long __cdecl atoll(_In_z_ const char *_Str); +_Check_return_ _CRTIMP long long __cdecl _atoll_l(_In_z_ const char *_Str, _In_opt_ _locale_t _Locale); +#ifndef _CRT_ALGO_DEFINED +#define _CRT_ALGO_DEFINED +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ _CRTIMP void * __cdecl bsearch_s(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements * _SizeOfElements) const void * _Base, + _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ _CRTIMP void * __cdecl bsearch(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements * _SizeOfElements) const void * _Base, + _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); + +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP void __cdecl qsort_s(_Inout_updates_bytes_(_NumOfElements* _SizeOfElements) void * _Base, + _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(void *, const void *, const void *), void *_Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_CRTIMP void __cdecl qsort(_Inout_updates_bytes_(_NumOfElements * _SizeOfElements) void * _Base, + _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *)); +#endif /* _CRT_ALGO_DEFINED */ + _Check_return_ unsigned short __cdecl _byteswap_ushort(_In_ unsigned short _Short); + _Check_return_ unsigned long __cdecl _byteswap_ulong (_In_ unsigned long _Long); + _Check_return_ unsigned __int64 __cdecl _byteswap_uint64(_In_ unsigned __int64 _Int64); +_Check_return_ _CRTIMP div_t __cdecl div(_In_ int _Numerator, _In_ int _Denominator); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP _CRT_INSECURE_DEPRECATE(_dupenv_s) char * __cdecl getenv(_In_z_ const char * _VarName); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP errno_t __cdecl getenv_s(_Out_ size_t * _ReturnSize, _Out_writes_opt_z_(_DstSize) char * _DstBuf, _In_ rsize_t _DstSize, _In_z_ const char * _VarName); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, getenv_s, _Out_ size_t *, _ReturnSize, char, _Dest, _In_z_ const char *, _VarName) +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_dupenv_s") +#undef _dupenv_s +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_opt_ _CRTIMP errno_t __cdecl _dupenv_s(_Outptr_result_buffer_maybenull_(*_PBufferSizeInBytes) _Outptr_result_z_ char **_PBuffer, _Out_opt_ size_t * _PBufferSizeInBytes, _In_z_ const char * _VarName); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_dupenv_s") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_opt_ _CRTIMP errno_t __cdecl _itoa_s(_In_ int _Value, _Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _itoa_s, _In_ int, _Value, char, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(char *, __RETURN_POLICY_DST, _CRTIMP, _itoa, _In_ int, _Value, _Pre_notnull_ _Post_z_, char, _Dest, _In_ int, _Radix) +_Check_return_opt_ _CRTIMP errno_t __cdecl _i64toa_s(_In_ __int64 _Val, _Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ int _Radix); +_CRTIMP _CRT_INSECURE_DEPRECATE(_i64toa_s) char * __cdecl _i64toa(_In_ __int64 _Val, _Pre_notnull_ _Post_z_ char * _DstBuf, _In_ int _Radix); +_Check_return_opt_ _CRTIMP errno_t __cdecl _ui64toa_s(_In_ unsigned __int64 _Val, _Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ int _Radix); +_CRTIMP _CRT_INSECURE_DEPRECATE(_ui64toa_s) char * __cdecl _ui64toa(_In_ unsigned __int64 _Val, _Pre_notnull_ _Post_z_ char * _DstBuf, _In_ int _Radix); +_Check_return_ _CRTIMP __int64 __cdecl _atoi64(_In_z_ const char * _String); +_Check_return_ _CRTIMP __int64 __cdecl _atoi64_l(_In_z_ const char * _String, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP __int64 __cdecl _strtoi64(_In_z_ const char * _String, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP __int64 __cdecl _strtoi64_l(_In_z_ const char * _String, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned __int64 __cdecl _strtoui64(_In_z_ const char * _String, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP unsigned __int64 __cdecl _strtoui64_l(_In_z_ const char * _String, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP ldiv_t __cdecl ldiv(_In_ long _Numerator, _In_ long _Denominator); +_Check_return_ _CRTIMP lldiv_t __cdecl lldiv(_In_ long long _Numerator, _In_ long long _Denominator); +#ifdef __cplusplus +extern "C++" +{ + inline long abs(long _X) throw() + { + return labs(_X); + } + inline long long abs(long long _X) throw() + { + return llabs(_X); + } + inline ldiv_t div(long _A1, long _A2) throw() + { + return ldiv(_A1, _A2); + } + inline lldiv_t div(long long _A1, long long _A2) throw() + { + return lldiv(_A1, _A2); + } +} +#endif /* __cplusplus */ +_Check_return_opt_ _CRTIMP errno_t __cdecl _ltoa_s(_In_ long _Val, _Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _ltoa_s, _In_ long, _Value, char, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(char *, __RETURN_POLICY_DST, _CRTIMP, _ltoa, _In_ long, _Value, _Pre_notnull_ _Post_z_, char, _Dest, _In_ int, _Radix) +_Check_return_ _CRTIMP int __cdecl mblen(_In_reads_bytes_opt_(_MaxCount) _Pre_opt_z_ const char * _Ch, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _mblen_l(_In_reads_bytes_opt_(_MaxCount) _Pre_opt_z_ const char * _Ch, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP size_t __cdecl _mbstrlen(_In_z_ const char * _Str); +_Check_return_ _CRTIMP size_t __cdecl _mbstrlen_l(_In_z_ const char *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP size_t __cdecl _mbstrnlen(_In_z_ const char *_Str, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP size_t __cdecl _mbstrnlen_l(_In_z_ const char *_Str, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP int __cdecl mbtowc(_Pre_notnull_ _Post_z_ wchar_t * _DstCh, _In_reads_bytes_opt_(_SrcSizeInBytes) _Pre_opt_z_ const char * _SrcCh, _In_ size_t _SrcSizeInBytes); +_CRTIMP int __cdecl _mbtowc_l(_Pre_notnull_ _Post_z_ wchar_t * _DstCh, _In_reads_bytes_opt_(_SrcSizeInBytes) _Pre_opt_z_ const char * _SrcCh, _In_ size_t _SrcSizeInBytes, _In_opt_ _locale_t _Locale); +_Check_return_opt_ _CRTIMP errno_t __cdecl mbstowcs_s(_Out_opt_ size_t * _PtNumOfCharConverted, _Out_writes_to_opt_(_SizeInWords, *_PtNumOfCharConverted) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_reads_or_z_(_MaxCount) const char * _SrcBuf, _In_ size_t _MaxCount ); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_2(errno_t, mbstowcs_s, _Out_opt_ size_t *, _PtNumOfCharConverted, _Post_z_ wchar_t, _Dest, _In_z_ const char *, _Source, _In_ size_t, _MaxCount) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_SIZE(_CRTIMP, mbstowcs, _Out_writes_opt_z_(_MaxCount), wchar_t, _Dest, _In_z_ const char *, _Source, _In_ size_t, _MaxCount) + +_Check_return_opt_ _CRTIMP errno_t __cdecl _mbstowcs_s_l(_Out_opt_ size_t * _PtNumOfCharConverted, _Out_writes_to_opt_(_SizeInWords, *_PtNumOfCharConverted) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_reads_or_z_(_MaxCount) const char * _SrcBuf, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_3(errno_t, _mbstowcs_s_l, _Out_opt_ size_t *, _PtNumOfCharConverted, wchar_t, _Dest, _In_z_ const char *, _Source, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_SIZE_EX(_CRTIMP, _mbstowcs_l, _mbstowcs_s_l, _Out_writes_opt_z_(_Size) wchar_t, _Out_writes_z_(_MaxCount), wchar_t, _Dest, _In_z_ const char *, _Source, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) + +_Check_return_ _CRTIMP int __cdecl rand(void); +#if defined (_CRT_RAND_S) +_CRTIMP errno_t __cdecl rand_s ( _Out_ unsigned int *_RandomValue); +#endif /* defined (_CRT_RAND_S) */ + +_Check_return_opt_ _CRTIMP int __cdecl _set_error_mode(_In_ int _Mode); + +_CRTIMP void __cdecl srand(_In_ unsigned int _Seed); +_Check_return_ _CRTIMP double __cdecl strtod(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr); +_Check_return_ _CRTIMP double __cdecl _strtod_l(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long __cdecl strtol(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix ); +_Check_return_ _CRTIMP long __cdecl _strtol_l(_In_z_ const char *_Str, _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long long __cdecl strtoll(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix ); +_Check_return_ _CRTIMP long long __cdecl _strtoll_l(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale ); +_Check_return_ _CRTIMP unsigned long __cdecl strtoul(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP unsigned long __cdecl _strtoul_l(const char * _Str, _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned long long __cdecl strtoull(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP unsigned long long __cdecl _strtoull_l(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long double __cdecl strtold(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr); +_Check_return_ _CRTIMP long double __cdecl _strtold_l(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP float __cdecl strtof(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr); +_Check_return_ _CRTIMP float __cdecl _strtof_l(_In_z_ const char * _Str, _Out_opt_ _Deref_post_z_ char ** _EndPtr, _In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +#ifndef _CRT_SYSTEM_DEFINED +#define _CRT_SYSTEM_DEFINED +_CRTIMP int __cdecl system(_In_opt_z_ const char * _Command); +#endif /* _CRT_SYSTEM_DEFINED */ +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_opt_ _CRTIMP errno_t __cdecl _ultoa_s(_In_ unsigned long _Val, _Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _ultoa_s, _In_ unsigned long, _Value, char, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(char *, __RETURN_POLICY_DST, _CRTIMP, _ultoa, _In_ unsigned long, _Value, _Pre_notnull_ _Post_z_, char, _Dest, _In_ int, _Radix) +_CRTIMP _CRT_INSECURE_DEPRECATE(wctomb_s) int __cdecl wctomb(_Out_writes_opt_z_(MB_LEN_MAX) char * _MbCh, _In_ wchar_t _WCh); +_CRTIMP _CRT_INSECURE_DEPRECATE(_wctomb_s_l) int __cdecl _wctomb_l(_Pre_maybenull_ _Post_z_ char * _MbCh, _In_ wchar_t _WCh, _In_opt_ _locale_t _Locale); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl wctomb_s(_Out_opt_ int * _SizeConverted, _Out_writes_bytes_to_opt_(_SizeInBytes, *_SizeConverted) char * _MbCh, _In_ rsize_t _SizeInBytes, _In_ wchar_t _WCh); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_wat_ _CRTIMP errno_t __cdecl _wctomb_s_l(_Out_opt_ int * _SizeConverted, _Out_writes_opt_z_(_SizeInBytes) char * _MbCh, _In_ size_t _SizeInBytes, _In_ wchar_t _WCh, _In_opt_ _locale_t _Locale); +_Check_return_wat_ _CRTIMP errno_t __cdecl wcstombs_s(_Out_opt_ size_t * _PtNumOfCharConverted, _Out_writes_bytes_to_opt_(_DstSizeInBytes, *_PtNumOfCharConverted) char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const wchar_t * _Src, _In_ size_t _MaxCountInBytes); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_2(errno_t, wcstombs_s, _Out_opt_ size_t *, _PtNumOfCharConverted, _Out_writes_bytes_opt_(_Size) char, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _MaxCount) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_SIZE(_CRTIMP, wcstombs, _Out_writes_opt_z_(_MaxCount), char, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _MaxCount) +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcstombs_s_l(_Out_opt_ size_t * _PtNumOfCharConverted, _Out_writes_bytes_to_opt_(_DstSizeInBytes, *_PtNumOfCharConverted) char * _Dst, _In_ size_t _DstSizeInBytes, _In_z_ const wchar_t * _Src, _In_ size_t _MaxCountInBytes, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_3(errno_t, _wcstombs_s_l, _Out_opt_ size_t *,_PtNumOfCharConverted, _Out_writes_opt_(_Size) char, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_SIZE_EX(_CRTIMP, _wcstombs_l, _wcstombs_s_l, _Out_writes_opt_z_(_Size) char, _Out_writes_z_(_MaxCount), char, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) + +#if defined (__cplusplus) && defined (_M_CEE) +/* + * Managed search routines. Note __cplusplus, this is because we only support + * managed C++. + */ +extern "C++" +{ +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ void * __clrcall bsearch_s(_In_ const void * _Key, _In_reads_bytes_(_NumOfElements*_SizeOfElements) const void * _Base, _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ void * __clrcall bsearch (_In_ const void * _Key, _In_reads_bytes_(_NumOfElements*_SizeOfElements) const void * _Base, _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(const void *, const void *)); + +#if __STDC_WANT_SECURE_LIB__ +void __clrcall qsort_s(_Inout_updates_bytes_(_NumOfElements*_SizeOfElements) void * _Base, + _In_ rsize_t _NumOfElements, _In_ rsize_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(void *, const void *, const void *), void * _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +void __clrcall qsort(_Inout_updates_bytes_(_NumOfElements*_SizeOfElements) void * _Base, + _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, + _In_ int (__clrcall * _PtFuncCompare)(const void *, const void *)); + +} +#endif /* defined (__cplusplus) && defined (_M_CEE) */ + +#ifndef _CRT_ALLOCATION_DEFINED +#define _CRT_ALLOCATION_DEFINED + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) + +#pragma push_macro("calloc") +#pragma push_macro("free") +#pragma push_macro("malloc") +#pragma push_macro("realloc") +#pragma push_macro("_recalloc") +#pragma push_macro("_aligned_free") +#pragma push_macro("_aligned_malloc") +#pragma push_macro("_aligned_offset_malloc") +#pragma push_macro("_aligned_realloc") +#pragma push_macro("_aligned_recalloc") +#pragma push_macro("_aligned_offset_realloc") +#pragma push_macro("_aligned_offset_recalloc") +#pragma push_macro("_aligned_msize") + +#undef calloc +#undef free +#undef malloc +#undef realloc +#undef _recalloc +#undef _aligned_free +#undef _aligned_malloc +#undef _aligned_offset_malloc +#undef _aligned_realloc +#undef _aligned_recalloc +#undef _aligned_offset_realloc +#undef _aligned_offset_recalloc +#undef _aligned_msize + +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRT_JIT_INTRINSIC _CRTNOALIAS _CRTRESTRICT void * __cdecl calloc(_In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size); +_CRTIMP _CRTNOALIAS void __cdecl free(_Pre_maybenull_ _Post_invalid_ void * _Memory); +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _CRTIMP _CRT_JIT_INTRINSIC _CRTNOALIAS _CRTRESTRICT void * __cdecl malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl realloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _NewSize); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _recalloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size); +_CRTIMP _CRTNOALIAS void __cdecl _aligned_free(_Pre_maybenull_ _Post_invalid_ void * _Memory); +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment); +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_offset_malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment, _In_ size_t _Offset); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_realloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _NewSize, _In_ size_t _Alignment); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_recalloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_offset_realloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _NewSize, _In_ size_t _Alignment, _In_ size_t _Offset); +_Success_(return!=0) +_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count*_Size) _CRTIMP _CRTNOALIAS _CRTRESTRICT void * __cdecl _aligned_offset_recalloc(_Pre_maybenull_ _Post_invalid_ void * _Memory, _In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size, _In_ size_t _Alignment, _In_ size_t _Offset); +_Check_return_ _CRTIMP size_t __cdecl _aligned_msize(_Pre_notnull_ void * _Memory, _In_ size_t _Alignment, _In_ size_t _Offset); + + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) + +#pragma pop_macro("_aligned_msize") +#pragma pop_macro("_aligned_offset_recalloc") +#pragma pop_macro("_aligned_offset_realloc") +#pragma pop_macro("_aligned_recalloc") +#pragma pop_macro("_aligned_realloc") +#pragma pop_macro("_aligned_offset_malloc") +#pragma pop_macro("_aligned_malloc") +#pragma pop_macro("_aligned_free") +#pragma pop_macro("_recalloc") +#pragma pop_macro("realloc") +#pragma pop_macro("malloc") +#pragma pop_macro("free") +#pragma pop_macro("calloc") + +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +#endif /* _CRT_ALLOCATION_DEFINED */ + +#ifndef _WSTDLIB_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +_Check_return_wat_ _CRTIMP errno_t __cdecl _itow_s (_In_ int _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _itow_s, _In_ int, _Value, wchar_t, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _itow, _In_ int, _Value, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_ int, _Radix) +_Check_return_wat_ _CRTIMP errno_t __cdecl _ltow_s (_In_ long _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _ltow_s, _In_ long, _Value, wchar_t, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _ltow, _In_ long, _Value, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_ int, _Radix) +_Check_return_wat_ _CRTIMP errno_t __cdecl _ultow_s (_In_ unsigned long _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _ultow_s, _In_ unsigned long, _Value, wchar_t, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _ultow, _In_ unsigned long, _Value, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_ int, _Radix) +_Check_return_ _CRTIMP double __cdecl wcstod(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr); +_Check_return_ _CRTIMP double __cdecl _wcstod_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long __cdecl wcstol(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix); +_Check_return_ _CRTIMP long __cdecl _wcstol_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long long __cdecl wcstoll(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, int _Radix); +_Check_return_ _CRTIMP long long __cdecl _wcstoll_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned long __cdecl wcstoul(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix); +_Check_return_ _CRTIMP unsigned long __cdecl _wcstoul_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned long long __cdecl wcstoull(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix); +_Check_return_ _CRTIMP unsigned long long __cdecl _wcstoull_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long double __cdecl wcstold(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr); +_Check_return_ _CRTIMP long double __cdecl _wcstold_l(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP float __cdecl wcstof(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr); +_Check_return_ _CRTIMP float __cdecl _wcstof_l(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +_Check_return_ _CRTIMP _CRT_INSECURE_DEPRECATE(_wdupenv_s) wchar_t * __cdecl _wgetenv(_In_z_ const wchar_t * _VarName); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wgetenv_s(_Out_ size_t * _ReturnSize, _Out_writes_opt_z_(_DstSizeInWords) wchar_t * _DstBuf, _In_ size_t _DstSizeInWords, _In_z_ const wchar_t * _VarName); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _wgetenv_s, _Out_ size_t *, _ReturnSize, wchar_t, _Dest, _In_z_ const wchar_t *, _VarName) + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wdupenv_s") +#undef _wdupenv_s +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_wat_ _CRTIMP errno_t __cdecl _wdupenv_s(_Outptr_result_buffer_maybenull_(*_BufferSizeInWords) _Outptr_result_z_ wchar_t **_Buffer, _Out_opt_ size_t *_BufferSizeInWords, _In_z_ const wchar_t *_VarName); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wdupenv_s") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +#ifndef _CRT_WSYSTEM_DEFINED +#define _CRT_WSYSTEM_DEFINED +_CRTIMP int __cdecl _wsystem(_In_opt_z_ const wchar_t * _Command); +#endif /* _CRT_WSYSTEM_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP double __cdecl _wtof(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP double __cdecl _wtof_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wtoi(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP int __cdecl _wtoi_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long __cdecl _wtol(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP long __cdecl _wtol_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long long __cdecl _wtoll(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP long long __cdecl _wtoll_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); + +_Check_return_wat_ _CRTIMP errno_t __cdecl _i64tow_s(_In_ __int64 _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +_CRTIMP _CRT_INSECURE_DEPRECATE(_i64tow_s) wchar_t * __cdecl _i64tow(_In_ __int64 _Val, _Pre_notnull_ _Post_z_ wchar_t * _DstBuf, _In_ int _Radix); +_Check_return_wat_ _CRTIMP errno_t __cdecl _ui64tow_s(_In_ unsigned __int64 _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +_CRTIMP _CRT_INSECURE_DEPRECATE(_ui64tow_s) wchar_t * __cdecl _ui64tow(_In_ unsigned __int64 _Val, _Pre_notnull_ _Post_z_ wchar_t * _DstBuf, _In_ int _Radix); +_Check_return_ _CRTIMP __int64 __cdecl _wtoi64(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP __int64 __cdecl _wtoi64_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP __int64 __cdecl _wcstoi64(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP __int64 __cdecl _wcstoi64_l(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned __int64 __cdecl _wcstoui64(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP unsigned __int64 __cdecl _wcstoui64_l(_In_z_ const wchar_t *_Str , _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); + +#define _WSTDLIB_DEFINED +#endif /* _WSTDLIB_DEFINED */ + + + +/* +Buffer size required to be passed to _gcvt, fcvt and other fp conversion routines +*/ +#define _CVTBUFSIZE (309+40) /* # of digits in max. dp value + slop */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) + +#pragma push_macro("_fullpath") +#undef _fullpath + +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP char * __cdecl _fullpath(_Out_writes_opt_z_(_SizeInBytes) char * _FullPath, _In_z_ const char * _Path, _In_ size_t _SizeInBytes); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) + +#pragma pop_macro("_fullpath") + +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_wat_ _CRTIMP errno_t __cdecl _ecvt_s(_Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ double _Val, _In_ int _NumOfDights, _Out_ int * _PtDec, _Out_ int * _PtSign); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_4(errno_t, _ecvt_s, char, _Dest, _In_ double, _Value, _In_ int, _NumOfDigits, _Out_ int *, _PtDec, _Out_ int *, _PtSign) +_Check_return_ _CRTIMP _CRT_INSECURE_DEPRECATE(_ecvt_s) char * __cdecl _ecvt(_In_ double _Val, _In_ int _NumOfDigits, _Out_ int * _PtDec, _Out_ int * _PtSign); +_Check_return_wat_ _CRTIMP errno_t __cdecl _fcvt_s(_Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ double _Val, _In_ int _NumOfDec, _Out_ int * _PtDec, _Out_ int * _PtSign); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_4(errno_t, _fcvt_s, char, _Dest, _In_ double, _Value, _In_ int, _NumOfDigits, _Out_ int *, _PtDec, _Out_ int *, _PtSign) +_Check_return_ _CRTIMP _CRT_INSECURE_DEPRECATE(_fcvt_s) char * __cdecl _fcvt(_In_ double _Val, _In_ int _NumOfDec, _Out_ int * _PtDec, _Out_ int * _PtSign); +_CRTIMP errno_t __cdecl _gcvt_s(_Out_writes_z_(_Size) char * _DstBuf, _In_ size_t _Size, _In_ double _Val, _In_ int _NumOfDigits); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _gcvt_s, char, _Dest, _In_ double, _Value, _In_ int, _NumOfDigits) +_CRTIMP _CRT_INSECURE_DEPRECATE(_gcvt_s) char * __cdecl _gcvt(_In_ double _Val, _In_ int _NumOfDigits, _Pre_notnull_ _Post_z_ char * _DstBuf); + +_Check_return_ _CRTIMP int __cdecl _atodbl(_Out_ _CRT_DOUBLE * _Result, _In_z_ char * _Str); +_Check_return_ _CRTIMP int __cdecl _atoldbl(_Out_ _LDOUBLE * _Result, _In_z_ char * _Str); +_Check_return_ _CRTIMP int __cdecl _atoflt(_Out_ _CRT_FLOAT * _Result, _In_z_ const char * _Str); +_Check_return_ _CRTIMP int __cdecl _atodbl_l(_Out_ _CRT_DOUBLE * _Result, _In_z_ char * _Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _atoldbl_l(_Out_ _LDOUBLE * _Result, _In_z_ char * _Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _atoflt_l(_Out_ _CRT_FLOAT * _Result, _In_z_ const char * _Str, _In_opt_ _locale_t _Locale); + _Check_return_ unsigned long __cdecl _lrotl(_In_ unsigned long _Val, _In_ int _Shift); + _Check_return_ unsigned long __cdecl _lrotr(_In_ unsigned long _Val, _In_ int _Shift); +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _makepath_s(_Out_writes_z_(_SizeInWords) char * _PathResult, _In_ size_t _SizeInWords, _In_opt_z_ const char * _Drive, _In_opt_z_ const char * _Dir, _In_opt_z_ const char * _Filename, + _In_opt_z_ const char * _Ext); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_4(errno_t, _makepath_s, char, _Path, _In_opt_z_ const char *, _Drive, _In_opt_z_ const char *, _Dir, _In_opt_z_ const char *, _Filename, _In_opt_z_ const char *, _Ext) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_4(void, __RETURN_POLICY_VOID, _CRTIMP, _makepath, _Pre_notnull_ _Post_z_, char, _Path, _In_opt_z_ const char *, _Drive, _In_opt_z_ const char *, _Dir, _In_opt_z_ const char *, _Filename, _In_opt_z_ const char *, _Ext) + +#if defined (_M_CEE) + _onexit_m_t __clrcall _onexit_m_appdomain(_onexit_m_t _Function); +#if defined (_M_CEE_MIXED) + _onexit_m_t __clrcall _onexit_m(_onexit_m_t _Function); +#else /* defined (_M_CEE_MIXED) */ + inline _onexit_m_t __clrcall _onexit_m(_onexit_t _Function) + { + return _onexit_m_appdomain(_Function); + } +#endif /* defined (_M_CEE_MIXED) */ + +#endif /* defined (_M_CEE) */ +#if defined (_M_CEE_PURE) + /* In pure mode, _onexit is the same as _onexit_m_appdomain */ +extern "C++" +{ +inline _onexit_t __clrcall _onexit +( + _onexit_t _Function +) +{ + return _onexit_m_appdomain(_Function); +} +} +#else /* defined (_M_CEE_PURE) */ + _onexit_t __cdecl _onexit(_In_opt_ _onexit_t _Func); +#endif /* defined (_M_CEE_PURE) */ + +#ifndef _CRT_PERROR_DEFINED +#define _CRT_PERROR_DEFINED +_CRTIMP void __cdecl perror(_In_opt_z_ const char * _ErrMsg); +#endif /* _CRT_PERROR_DEFINED */ + +#pragma warning (push) +#pragma warning (disable:6540) // the functions below have declspecs in their declarations in the windows headers, causing PREfast to fire 6540 here + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP int __cdecl _putenv(_In_z_ const char * _EnvString); +_Check_return_wat_ _CRTIMP errno_t __cdecl _putenv_s(_In_z_ const char * _Name, _In_z_ const char * _Value); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + unsigned int __cdecl _rotl(_In_ unsigned int _Val, _In_ int _Shift); + unsigned __int64 __cdecl _rotl64(_In_ unsigned __int64 _Val, _In_ int _Shift); + unsigned int __cdecl _rotr(_In_ unsigned int _Val, _In_ int _Shift); + unsigned __int64 __cdecl _rotr64(_In_ unsigned __int64 _Val, _In_ int _Shift); +#pragma warning (pop) + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRTIMP errno_t __cdecl _searchenv_s(_In_z_ const char * _Filename, _In_z_ const char * _EnvVar, _Out_writes_z_(_SizeInBytes) char * _ResultPath, _In_ size_t _SizeInBytes); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_2_0(errno_t, _searchenv_s, _In_z_ const char *, _Filename, _In_z_ const char *, _EnvVar, char, _ResultPath) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_2_0(void, __RETURN_POLICY_VOID, _CRTIMP, _searchenv, _In_z_ const char *, _Filename, _In_z_ const char *, _EnvVar, _Pre_notnull_ _Post_z_, char, _ResultPath) +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRT_INSECURE_DEPRECATE(_splitpath_s) _CRTIMP void __cdecl _splitpath(_In_z_ const char * _FullPath, _Pre_maybenull_ _Post_z_ char * _Drive, _Pre_maybenull_ _Post_z_ char * _Dir, _Pre_maybenull_ _Post_z_ char * _Filename, _Pre_maybenull_ _Post_z_ char * _Ext); +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _splitpath_s(_In_z_ const char * _FullPath, + _Out_writes_opt_z_(_DriveSize) char * _Drive, _In_ size_t _DriveSize, + _Out_writes_opt_z_(_DirSize) char * _Dir, _In_ size_t _DirSize, + _Out_writes_opt_z_(_FilenameSize) char * _Filename, _In_ size_t _FilenameSize, + _Out_writes_opt_z_(_ExtSize) char * _Ext, _In_ size_t _ExtSize); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_SPLITPATH(errno_t, _splitpath_s, char, _Dest) + +_CRTIMP void __cdecl _swab(_Inout_updates_(_SizeInBytes) _Post_readable_size_(_SizeInBytes) char * _Buf1, _Inout_updates_(_SizeInBytes) _Post_readable_size_(_SizeInBytes) char * _Buf2, int _SizeInBytes); + +#ifndef _WSTDLIBP_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wfullpath") +#undef _wfullpath +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP wchar_t * __cdecl _wfullpath(_Out_writes_opt_z_(_SizeInWords) wchar_t * _FullPath, _In_z_ const wchar_t * _Path, _In_ size_t _SizeInWords); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wfullpath") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _wmakepath_s(_Out_writes_z_(_SIZE) wchar_t * _PathResult, _In_ size_t _SIZE, _In_opt_z_ const wchar_t * _Drive, _In_opt_z_ const wchar_t * _Dir, _In_opt_z_ const wchar_t * _Filename, + _In_opt_z_ const wchar_t * _Ext); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_4(errno_t, _wmakepath_s, wchar_t, _ResultPath, _In_opt_z_ const wchar_t *, _Drive, _In_opt_z_ const wchar_t *, _Dir, _In_opt_z_ const wchar_t *, _Filename, _In_opt_z_ const wchar_t *, _Ext) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_4(void, __RETURN_POLICY_VOID, _CRTIMP, _wmakepath, _Pre_notnull_ _Post_z_, wchar_t, _ResultPath, _In_opt_z_ const wchar_t *, _Drive, _In_opt_z_ const wchar_t *, _Dir, _In_opt_z_ const wchar_t *, _Filename, _In_opt_z_ const wchar_t *, _Ext) +#ifndef _CRT_WPERROR_DEFINED +#define _CRT_WPERROR_DEFINED +_CRTIMP void __cdecl _wperror(_In_opt_z_ const wchar_t * _ErrMsg); +#endif /* _CRT_WPERROR_DEFINED */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP int __cdecl _wputenv(_In_z_ const wchar_t * _EnvString); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wputenv_s(_In_z_ const wchar_t * _Name, _In_z_ const wchar_t * _Value); +_CRTIMP errno_t __cdecl _wsearchenv_s(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _EnvVar, _Out_writes_z_(_SizeInWords) wchar_t * _ResultPath, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_2_0(errno_t, _wsearchenv_s, _In_z_ const wchar_t *, _Filename, _In_z_ const wchar_t *, _EnvVar, wchar_t, _ResultPath) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_2_0(void, __RETURN_POLICY_VOID, _CRTIMP, _wsearchenv, _In_z_ const wchar_t *, _Filename, _In_z_ const wchar_t *, _EnvVar, _Pre_notnull_ _Post_z_, wchar_t, _ResultPath) +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRT_INSECURE_DEPRECATE(_wsplitpath_s) _CRTIMP void __cdecl _wsplitpath(_In_z_ const wchar_t * _FullPath, _Pre_maybenull_ _Post_z_ wchar_t * _Drive, _Pre_maybenull_ _Post_z_ wchar_t * _Dir, _Pre_maybenull_ _Post_z_ wchar_t * _Filename, _Pre_maybenull_ _Post_z_ wchar_t * _Ext); +_CRTIMP_ALTERNATIVE errno_t __cdecl _wsplitpath_s(_In_z_ const wchar_t * _FullPath, + _Out_writes_opt_z_(_DriveSize) wchar_t * _Drive, _In_ size_t _DriveSize, + _Out_writes_opt_z_(_DirSize) wchar_t * _Dir, _In_ size_t _DirSize, + _Out_writes_opt_z_(_FilenameSize) wchar_t * _Filename, _In_ size_t _FilenameSize, + _Out_writes_opt_z_(_ExtSize) wchar_t * _Ext, _In_ size_t _ExtSize); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_SPLITPATH(errno_t, _wsplitpath_s, wchar_t, _Path) + +#define _WSTDLIBP_DEFINED +#endif /* _WSTDLIBP_DEFINED */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/* The Win32 API SetErrorMode, Beep and Sleep should be used instead. */ +_CRT_OBSOLETE(SetErrorMode) _CRTIMP void __cdecl _seterrormode(_In_ int _Mode); +_CRT_OBSOLETE(Beep) _CRTIMP void __cdecl _beep(_In_ unsigned _Frequency, _In_ unsigned _Duration); +_CRT_OBSOLETE(Sleep) _CRTIMP void __cdecl _sleep(_In_ unsigned long _Duration); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + +#if !__STDC__ + + +/* Non-ANSI names for compatibility */ + +#ifndef __cplusplus +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif /* __cplusplus */ + +#define sys_errlist _sys_errlist +#define sys_nerr _sys_nerr + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +#define environ _environ +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#pragma warning(push) +#pragma warning(disable: 4141) /* Using deprecated twice */ +_Check_return_ _CRT_NONSTDC_DEPRECATE(_ecvt) _CRT_INSECURE_DEPRECATE(_ecvt_s) _CRTIMP char * __cdecl ecvt(_In_ double _Val, _In_ int _NumOfDigits, _Out_ int * _PtDec, _Out_ int * _PtSign); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_fcvt) _CRT_INSECURE_DEPRECATE(_fcvt_s) _CRTIMP char * __cdecl fcvt(_In_ double _Val, _In_ int _NumOfDec, _Out_ int * _PtDec, _Out_ int * _PtSign); +_CRT_NONSTDC_DEPRECATE(_gcvt) _CRT_INSECURE_DEPRECATE(_fcvt_s) _CRTIMP char * __cdecl gcvt(_In_ double _Val, _In_ int _NumOfDigits, _Pre_notnull_ _Post_z_ char * _DstBuf); +_CRT_NONSTDC_DEPRECATE(_itoa) _CRT_INSECURE_DEPRECATE(_itoa_s) _CRTIMP char * __cdecl itoa(_In_ int _Val, _Pre_notnull_ _Post_z_ char * _DstBuf, _In_ int _Radix); +_CRT_NONSTDC_DEPRECATE(_ltoa) _CRT_INSECURE_DEPRECATE(_ltoa_s) _CRTIMP char * __cdecl ltoa(_In_ long _Val, _Pre_notnull_ _Post_z_ char * _DstBuf, _In_ int _Radix); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRT_NONSTDC_DEPRECATE(_putenv) _CRTIMP int __cdecl putenv(_In_z_ const char * _EnvString); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRT_NONSTDC_DEPRECATE(_swab) _CRTIMP void __cdecl swab(_Inout_updates_z_(_SizeInBytes) char * _Buf1,_Inout_updates_z_(_SizeInBytes) char * _Buf2, _In_ int _SizeInBytes); +_CRT_NONSTDC_DEPRECATE(_ultoa) _CRT_INSECURE_DEPRECATE(_ultoa_s) _CRTIMP char * __cdecl ultoa(_In_ unsigned long _Val, _Pre_notnull_ _Post_z_ char * _Dstbuf, _In_ int _Radix); +#pragma warning(pop) +onexit_t __cdecl onexit(_In_opt_ onexit_t _Func); + + +#endif /* !__STDC__ */ + +#ifdef __cplusplus +} + +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_STDLIB */ diff --git a/test_data/lots_of_files/stretchy_buffer.h b/test_data/lots_of_files/stretchy_buffer.h new file mode 100644 index 0000000..b99c685 --- /dev/null +++ b/test_data/lots_of_files/stretchy_buffer.h @@ -0,0 +1,216 @@ +// stretchy_buffer.h - v1.02 - public domain - nothings.org/stb +// a vector<>-like dynamic array for C +// +// version history: +// 1.02 - compiles as C++, but untested +// 1.01 - added a "common uses" documentation section +// 1.0 - fixed bug in the version I posted prematurely +// 0.9 - rewrite to try to avoid strict-aliasing optimization +// issues, but won't compile as C++ +// +// Will probably not work correctly with strict-aliasing optimizations. +// +// The idea: +// +// This implements an approximation to C++ vector<> for C, in that it +// provides a generic definition for dynamic arrays which you can +// still access in a typesafe way using arr[i] or *(arr+i). However, +// it is simply a convenience wrapper around the common idiom of +// of keeping a set of variables (in a struct or globals) which store +// - pointer to array +// - the length of the "in-use" part of the array +// - the current size of the allocated array +// +// I find it to be single most useful non-built-in-structure when +// programming in C (hash tables a close second), but to be clear +// it lacks many of the capabilities of C++ vector<>: there is no +// range checking, the object address isn't stable (see next section +// for details), the set of methods available is small (although +// the file stb.h has another implementation of stretchy buffers +// called 'stb_arr' which provides more methods, e.g. for insertion +// and deletion). +// +// How to use: +// +// Unlike other stb header file libraries, there is no need to +// define an _IMPLEMENTATION symbol. Every #include creates as +// much implementation is needed. +// +// stretchy_buffer.h does not define any types, so you do not +// need to #include it to before defining data types that are +// stretchy buffers, only in files that *manipulate* stretchy +// buffers. +// +// If you want a stretchy buffer aka dynamic array containing +// objects of TYPE, declare such an array as: +// +// TYPE *myarray = NULL; +// +// (There is no typesafe way to distinguish between stretchy +// buffers and regular arrays/pointers; this is necessary to +// make ordinary array indexing work on these objects.) +// +// Unlike C++ vector<>, the stretchy_buffer has the same +// semantics as an object that you manually malloc and realloc. +// The pointer may relocate every time you add a new object +// to it, so you: +// +// 1. can't take long-term pointers to elements of the array +// 2. have to return the pointer from functions which might expand it +// (either as a return value or by passing it back) +// +// Now you can do the following things with this array: +// +// sb_free(TYPE *a) free the array +// sb_count(TYPE *a) the number of elements in the array +// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back +// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added +// sb_last(TYPE *a) returns an lvalue of the last item in the array +// a[n] access the nth (counting from 0) element of the array +// +// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export +// names of the form 'stb_sb_' if you have a name that would +// otherwise collide. +// +// Note that these are all macros and many of them evaluate +// their arguments more than once, so the arguments should +// be side-effect-free. +// +// Note that 'TYPE *a' in sb_push and sb_add must be lvalues +// so that the library can overwrite the existing pointer if +// the object has to be reallocated. +// +// In an out-of-memory condition, the code will try to +// set up a null-pointer or otherwise-invalid-pointer +// exception to happen later. It's possible optimizing +// compilers could detect this write-to-null statically +// and optimize away some of the code, but it should only +// be along the failure path. Nevertheless, for more security +// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY +// to a statement such as assert(0) or exit(1) or something +// to force a failure when out-of-memory occurs. +// +// Common use: +// +// The main application for this is when building a list of +// things with an unknown quantity, either due to loading from +// a file or through a process which produces an unpredictable +// number. +// +// My most common idiom is something like: +// +// SomeStruct *arr = NULL; +// while (something) +// { +// SomeStruct new_one; +// new_one.whatever = whatever; +// new_one.whatup = whatup; +// new_one.foobar = barfoo; +// sb_push(arr, new_one); +// } +// +// and various closely-related factorings of that. For example, +// you might have several functions to create/init new SomeStructs, +// and if you use the above idiom, you might prefer to make them +// return structs rather than take non-const-pointers-to-structs, +// so you can do things like: +// +// SomeStruct *arr = NULL; +// while (something) +// { +// if (case_A) { +// sb_push(arr, some_func1()); +// } else if (case_B) { +// sb_push(arr, some_func2()); +// } else { +// sb_push(arr, some_func3()); +// } +// } +// +// Note that the above relies on the fact that sb_push doesn't +// evaluate its second argument more than once. The macros do +// evaluate the *array* argument multiple times, and numeric +// arguments may be evaluated multiple times, but you can rely +// on the second argument of sb_push being evaluated only once. +// +// Of course, you don't have to store bare objects in the array; +// if you need the objects to have stable pointers, store an array +// of pointers instead: +// +// SomeStruct **arr = NULL; +// while (something) +// { +// SomeStruct *new_one = malloc(sizeof(*new_one)); +// new_one->whatever = whatever; +// new_one->whatup = whatup; +// new_one->foobar = barfoo; +// sb_push(arr, new_one); +// } +// +// How it works: +// +// A long-standing tradition in things like malloc implementations +// is to store extra data before the beginning of the block returned +// to the user. The stretchy buffer implementation here uses the +// same trick; the current-count and current-allocation-size are +// stored before the beginning of the array returned to the user. +// (This means you can't directly free() the pointer, because the +// allocated pointer is different from the type-safe pointer provided +// to the user.) +// +// The details are trivial and implementation is straightforward; +// the main trick is in realizing in the first place that it's +// possible to do this in a generic, type-safe way in C. +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + +#ifndef STB_STRETCHY_BUFFER_H_INCLUDED +#define STB_STRETCHY_BUFFER_H_INCLUDED + +#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES +#define sb_free stb_sb_free +#define sb_push stb_sb_push +#define sb_count stb_sb_count +#define sb_add stb_sb_add +#define sb_last stb_sb_last +#endif + +#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0) +#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v)) +#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0) +#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)]) +#define stb_sb_last(a) ((a)[stb__sbn(a)-1]) + +#define stb__sbraw(a) ((int *) (a) - 2) +#define stb__sbm(a) stb__sbraw(a)[0] +#define stb__sbn(a) stb__sbraw(a)[1] + +#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a)) +#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) +#define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a)))) + +#include <stdlib.h> + +static void * stb__sbgrowf(void *arr, int increment, int itemsize) +{ + int dbl_cur = arr ? 2*stb__sbm(arr) : 0; + int min_needed = stb_sb_count(arr) + increment; + int m = dbl_cur > min_needed ? dbl_cur : min_needed; + int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2); + if (p) { + if (!arr) + p[1] = 0; + p[0] = m; + return p+2; + } else { + #ifdef STRETCHY_BUFFER_OUT_OF_MEMORY + STRETCHY_BUFFER_OUT_OF_MEMORY ; + #endif + return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later + } +} +#endif // STB_STRETCHY_BUFFER_H_INCLUDED diff --git a/test_data/lots_of_files/string.h b/test_data/lots_of_files/string.h new file mode 100644 index 0000000..8ad3a3d --- /dev/null +++ b/test_data/lots_of_files/string.h @@ -0,0 +1,437 @@ +/*** +*string.h - declarations for string manipulation functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains the function declarations for the string +* manipulation functions. +* [ANSI/System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_STRING +#define _INC_STRING + +#include <crtdefs.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _NLSCMP_DEFINED +#define _NLSCMPERROR 2147483647 /* currently == INT_MAX */ +#define _NLSCMP_DEFINED +#endif /* _NLSCMP_DEFINED */ + +/* Define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + +/* For backwards compatibility */ +#define _WConst_return _CONST_RETURN + +/* Function prototypes */ +#ifndef RC_INVOKED +#ifndef _CRT_MEMORY_DEFINED +#define _CRT_MEMORY_DEFINED +_CRTIMP void * __cdecl _memccpy( _Out_writes_bytes_opt_(_MaxCount) void * _Dst, _In_ const void * _Src, _In_ int _Val, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP _CONST_RETURN void * __cdecl memchr( _In_reads_bytes_opt_(_MaxCount) const void * _Buf , _In_ int _Val, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _memicmp(_In_reads_bytes_opt_(_Size) const void * _Buf1, _In_reads_bytes_opt_(_Size) const void * _Buf2, _In_ size_t _Size); +_Check_return_ _CRTIMP int __cdecl _memicmp_l(_In_reads_bytes_opt_(_Size) const void * _Buf1, _In_reads_bytes_opt_(_Size) const void * _Buf2, _In_ size_t _Size, _In_opt_ _locale_t _Locale); +_Check_return_ int __cdecl memcmp(_In_reads_bytes_(_Size) const void * _Buf1, _In_reads_bytes_(_Size) const void * _Buf2, _In_ size_t _Size); +_CRT_INSECURE_DEPRECATE_MEMORY(memcpy_s) +_Post_equal_to_(_Dst) +_At_buffer_((unsigned char*)_Dst, _Iter_, _Size, _Post_satisfies_(((unsigned char*)_Dst)[_Iter_] == ((unsigned char*)_Src)[_Iter_])) +void * __cdecl memcpy(_Out_writes_bytes_all_(_Size) void * _Dst, _In_reads_bytes_(_Size) const void * _Src, _In_ size_t _Size); +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP errno_t __cdecl memcpy_s(_Out_writes_bytes_to_opt_(_DstSize, _MaxCount) void * _Dst, _In_ rsize_t _DstSize, _In_reads_bytes_opt_(_MaxCount) const void * _Src, _In_ rsize_t _MaxCount); +#if defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY +extern "C++" +{ +#ifndef _CRT_ENABLE_IF_DEFINED + #define _CRT_ENABLE_IF_DEFINED + template<bool _Enable, typename _Ty> + struct _CrtEnableIf; + + template<typename _Ty> + struct _CrtEnableIf<true, _Ty> + { + typedef _Ty _Type; + }; +#endif /* _CRT_ENABLE_IF_DEFINED */ + template <size_t _Size, typename _DstType> + inline + typename _CrtEnableIf<(_Size > 1), void *>::_Type __cdecl memcpy(_DstType (&_Dst)[_Size], _In_reads_bytes_opt_(_SrcSize) const void *_Src, _In_ size_t _SrcSize) _CRT_SECURE_CPP_NOTHROW + { + return memcpy_s(_Dst, _Size * sizeof(_DstType), _Src, _SrcSize) == 0 ? _Dst : 0; + } +} +#endif /* defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY */ +#if defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY +extern "C++" +{ + template <size_t _Size, typename _DstType> + inline + errno_t __CRTDECL memcpy_s(_DstType (&_Dst)[_Size], _In_reads_bytes_opt_(_SrcSize) const void * _Src, _In_ rsize_t _SrcSize) _CRT_SECURE_CPP_NOTHROW + { + return memcpy_s(_Dst, _Size * sizeof(_DstType), _Src, _SrcSize); + } +} +#endif /* defined (__cplusplus) && _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY */ +#endif /* __STDC_WANT_SECURE_LIB__ */ + _Post_equal_to_(_Dst) + _At_buffer_((unsigned char*)_Dst, _Iter_, _Size, _Post_satisfies_(((unsigned char*)_Dst)[_Iter_] == _Val)) + void * __cdecl memset(_Out_writes_bytes_all_(_Size) void * _Dst, _In_ int _Val, _In_ size_t _Size); + +#if !__STDC__ +/* Non-ANSI names for compatibility */ +_CRT_NONSTDC_DEPRECATE(_memccpy) _CRTIMP void * __cdecl memccpy(_Out_writes_bytes_opt_(_Size) void * _Dst, _In_reads_bytes_opt_(_Size) const void * _Src, _In_ int _Val, _In_ size_t _Size); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_memicmp) _CRTIMP int __cdecl memicmp(_In_reads_bytes_opt_(_Size) const void * _Buf1, _In_reads_bytes_opt_(_Size) const void * _Buf2, _In_ size_t _Size); +#endif /* !__STDC__ */ + +#endif /* _CRT_MEMORY_DEFINED */ +#endif /* RC_INVOKED */ + +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _strset_s(_Inout_updates_z_(_DstSize) char * _Dst, _In_ size_t _DstSize, _In_ int _Value); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _strset_s, _Prepost_z_ char, _Dest, _In_ int, _Value) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(char *, __RETURN_POLICY_DST, __EMPTY_DECLSPEC, _strset, _Inout_z_, char, _Dest, _In_ int, _Value) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl strcpy_s(_Out_writes_z_(_SizeInBytes) char * _Dst, _In_ rsize_t _SizeInBytes, _In_z_ const char * _Src); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, strcpy_s, _Post_z_ char, _Dest, _In_z_ const char *, _Source) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(char *, __RETURN_POLICY_DST, __EMPTY_DECLSPEC, strcpy, _Out_writes_z_(_String_length_(_Source) + 1), char, _Dest, _In_z_ const char *, _Source) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl strcat_s(_Inout_updates_z_(_SizeInBytes) char * _Dst, _In_ rsize_t _SizeInBytes, _In_z_ const char * _Src); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, strcat_s, char, _Dest, _In_z_ const char *, _Source) +#ifndef RC_INVOKED +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(char *, __RETURN_POLICY_DST, __EMPTY_DECLSPEC, strcat, _Inout_updates_z_(_String_length_(_Dest) + _String_length_(_Source) + 1), char, _Dest, _In_z_ const char *, _Source) +#endif +_Check_return_ int __cdecl strcmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ size_t __cdecl strlen(_In_z_ const char * _Str); +_Check_return_ _CRTIMP +_When_(_MaxCount > _String_length_(_Str), _Post_satisfies_(return == _String_length_(_Str))) +_When_(_MaxCount <= _String_length_(_Str), _Post_satisfies_(return == _MaxCount)) +size_t __cdecl strnlen(_In_reads_or_z_(_MaxCount) const char * _Str, _In_ size_t _MaxCount); +#if __STDC_WANT_SECURE_LIB__ && !defined (__midl) +_Check_return_ static __inline +_When_(_MaxCount > _String_length_(_Str), _Post_satisfies_(return == _String_length_(_Str))) +_When_(_MaxCount <= _String_length_(_Str), _Post_satisfies_(return == _MaxCount)) +size_t __CRTDECL strnlen_s(_In_reads_or_z_(_MaxCount) const char * _Str, _In_ size_t _MaxCount) +{ + return (_Str==0) ? 0 : strnlen(_Str, _MaxCount); +} +#endif /* __STDC_WANT_SECURE_LIB__ && !defined (__midl) */ +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl memmove_s(_Out_writes_bytes_to_opt_(_DstSize,_MaxCount) void * _Dst, _In_ rsize_t _DstSize, _In_reads_bytes_opt_(_MaxCount) const void * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ + +_CRTIMP _CRT_INSECURE_DEPRECATE_MEMORY(memmove_s) void * __cdecl memmove(_Out_writes_bytes_all_opt_(_Size) void * _Dst, _In_reads_bytes_opt_(_Size) const void * _Src, _In_ size_t _Size); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_strdup") +#undef _strdup +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP char * __cdecl _strdup(_In_opt_z_ const char * _Src); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_strdup") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP _CONST_RETURN char * __cdecl strchr(_In_z_ const char * _Str, _In_ int _Val); +_Check_return_ _CRTIMP int __cdecl _stricmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ _CRTIMP int __cdecl _strcmpi(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ _CRTIMP int __cdecl _stricmp_l(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl strcoll(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ _CRTIMP int __cdecl _strcoll_l(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _stricoll(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ _CRTIMP int __cdecl _stricoll_l(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _strncoll (_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _strncoll_l(_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _strnicoll (_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _strnicoll_l(_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP size_t __cdecl strcspn(_In_z_ const char * _Str, _In_z_ const char * _Control); +_Check_return_ _CRT_INSECURE_DEPRECATE(_strerror_s) _CRTIMP char * __cdecl _strerror(_In_opt_z_ const char * _ErrMsg); +_Check_return_wat_ _CRTIMP errno_t __cdecl _strerror_s(_Out_writes_z_(_SizeInBytes) char * _Buf, _In_ size_t _SizeInBytes, _In_opt_z_ const char * _ErrMsg); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _strerror_s, char, _Buffer, _In_opt_z_ const char *, _ErrorMessage) +_Check_return_ _CRT_INSECURE_DEPRECATE(strerror_s) _CRTIMP char * __cdecl strerror(_In_ int); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl strerror_s(_Out_writes_z_(_SizeInBytes) char * _Buf, _In_ size_t _SizeInBytes, _In_ int _ErrNum); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, strerror_s, char, _Buffer, _In_ int, _ErrorMessage) +_Check_return_wat_ _CRTIMP errno_t __cdecl _strlwr_s(_Inout_updates_z_(_Size) char * _Str, _In_ size_t _Size); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _strlwr_s, _Prepost_z_ char, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(char *, __RETURN_POLICY_DST, _CRTIMP, _strlwr, _Inout_z_, char, _String) +_Check_return_wat_ _CRTIMP errno_t __cdecl _strlwr_s_l(_Inout_updates_z_(_Size) char * _Str, _In_ size_t _Size, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _strlwr_s_l, _Prepost_z_ char, _String, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _CRTIMP, _strlwr_l, _strlwr_s_l, _Inout_updates_z_(_Size) char, _Inout_z_, char, _String, _In_opt_ _locale_t, _Locale) +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl strncat_s(_Inout_updates_z_(_SizeInBytes) char * _Dst, _In_ rsize_t _SizeInBytes, _In_reads_or_z_(_MaxCount) const char * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, strncat_s, _Prepost_z_ char, _Dest, _In_reads_or_z_(_Count) const char *, _Source, _In_ size_t, _Count) +#pragma warning(push) +#pragma warning(disable:6059) +/* prefast noise VSW 489802 */ +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _CRTIMP, strncat, strncat_s, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_Count), char, _Dest, _In_reads_or_z_(_Count) const char *, _Source, _In_ size_t, _Count) +#pragma warning(pop) +_Check_return_ _CRTIMP int __cdecl strncmp(_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _strnicmp(_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _strnicmp_l(_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl strncpy_s(_Out_writes_z_(_SizeInBytes) char * _Dst, _In_ rsize_t _SizeInBytes, _In_reads_or_z_(_MaxCount) const char * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, strncpy_s, char, _Dest, _In_reads_or_z_(_Count) const char *, _Source, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _CRTIMP, strncpy, strncpy_s, _Out_writes_z_(_Size) char, _Out_writes_(_Count) _Post_maybez_, char, _Dest, _In_reads_or_z_(_Count) const char *, _Source, _In_ size_t, _Count) +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _strnset_s(_Inout_updates_z_(_SizeInBytes) char * _Str, _In_ size_t _SizeInBytes, _In_ int _Val, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _strnset_s, _Prepost_z_ char, _Dest, _In_ int, _Val, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _CRTIMP, _strnset, _strnset_s, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_Count), char, _Dest, _In_ int, _Val, _In_ size_t, _Count) +_Check_return_ _CRTIMP _CONST_RETURN char * __cdecl strpbrk(_In_z_ const char * _Str, _In_z_ const char * _Control); +_Check_return_ _CRTIMP _CONST_RETURN char * __cdecl strrchr(_In_z_ const char * _Str, _In_ int _Ch); +_CRTIMP char * __cdecl _strrev(_Inout_z_ char * _Str); +_Check_return_ _CRTIMP size_t __cdecl strspn(_In_z_ const char * _Str, _In_z_ const char * _Control); +_Check_return_ _Ret_maybenull_ _CRTIMP _CONST_RETURN char * __cdecl strstr(_In_z_ const char * _Str, _In_z_ const char * _SubStr); +_Check_return_ _CRT_INSECURE_DEPRECATE(strtok_s) _CRTIMP char * __cdecl strtok(_Inout_opt_z_ char * _Str, _In_z_ const char * _Delim); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ _CRTIMP_ALTERNATIVE char * __cdecl strtok_s(_Inout_opt_z_ char * _Str, _In_z_ const char * _Delim, _Inout_ _Deref_prepost_opt_z_ char ** _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_wat_ _CRTIMP errno_t __cdecl _strupr_s(_Inout_updates_z_(_Size) char * _Str, _In_ size_t _Size); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _strupr_s, _Prepost_z_ char, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(char *, __RETURN_POLICY_DST, _CRTIMP, _strupr, _Inout_z_, char, _String) +_Check_return_wat_ _CRTIMP errno_t __cdecl _strupr_s_l(_Inout_updates_z_(_Size) char * _Str, _In_ size_t _Size, _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _strupr_s_l, _Prepost_z_ char, _String, _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _CRTIMP, _strupr_l, _strupr_s_l, _Inout_updates_z_(_Size) char, _Inout_z_, char, _String, _In_opt_ _locale_t, _Locale) +_Check_return_opt_ _CRTIMP size_t __cdecl strxfrm (_Out_writes_opt_(_MaxCount) _Post_maybez_ char * _Dst, _In_z_ const char * _Src, _In_ size_t _MaxCount); +_Check_return_opt_ _CRTIMP size_t __cdecl _strxfrm_l(_Out_writes_opt_(_MaxCount) _Post_maybez_ char * _Dst, _In_z_ const char * _Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); + +#ifdef __cplusplus +extern "C++" { +#ifndef _CPP_NARROW_INLINES_DEFINED +#define _CPP_NARROW_INLINES_DEFINED +_Check_return_ inline char * __CRTDECL strchr(_In_z_ char * _Str, _In_ int _Ch) + { return (char*)strchr((const char*)_Str, _Ch); } +_Check_return_ inline char * __CRTDECL strpbrk(_In_z_ char * _Str, _In_z_ const char * _Control) + { return (char*)strpbrk((const char*)_Str, _Control); } +_Check_return_ inline char * __CRTDECL strrchr(_In_z_ char * _Str, _In_ int _Ch) + { return (char*)strrchr((const char*)_Str, _Ch); } +_Check_return_ _Ret_maybenull_ inline char * __CRTDECL strstr(_In_z_ char * _Str, _In_z_ const char * _SubStr) + { return (char*)strstr((const char*)_Str, _SubStr); } +#endif /* _CPP_NARROW_INLINES_DEFINED */ +#ifndef _CPP_MEMCHR_DEFINED +#define _CPP_MEMCHR_DEFINED +_Check_return_ inline void * __CRTDECL memchr(_In_reads_bytes_opt_(_N) void * _Pv, _In_ int _C, _In_ size_t _N) + { return (void*)memchr((const void*)_Pv, _C, _N); } +#endif /* _CPP_MEMCHR_DEFINED */ +} +#endif /* __cplusplus */ + +#if !__STDC__ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("strdup") +#undef strdup +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRT_NONSTDC_DEPRECATE(_strdup) _CRTIMP char * __cdecl strdup(_In_opt_z_ const char * _Src); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("strdup") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +/* prototypes for oldnames.lib functions */ +_Check_return_ _CRT_NONSTDC_DEPRECATE(_strcmpi) _CRTIMP int __cdecl strcmpi(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_stricmp) _CRTIMP int __cdecl stricmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_CRT_NONSTDC_DEPRECATE(_strlwr) _CRTIMP char * __cdecl strlwr(_Inout_z_ char * _Str); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_strnicmp) _CRTIMP int __cdecl strnicmp(_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str, _In_ size_t _MaxCount); +_CRT_NONSTDC_DEPRECATE(_strnset) _CRTIMP char * __cdecl strnset(_Inout_updates_z_(_MaxCount) char * _Str, _In_ int _Val, _In_ size_t _MaxCount); +_CRT_NONSTDC_DEPRECATE(_strrev) _CRTIMP char * __cdecl strrev(_Inout_z_ char * _Str); +_CRT_NONSTDC_DEPRECATE(_strset) char * __cdecl strset(_Inout_z_ char * _Str, _In_ int _Val); +_CRT_NONSTDC_DEPRECATE(_strupr) _CRTIMP char * __cdecl strupr(_Inout_z_ char * _Str); + +#endif /* !__STDC__ */ + + +#ifndef _WSTRING_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wcsdup") +#undef _wcsdup +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP wchar_t * __cdecl _wcsdup(_In_z_ const wchar_t * _Str); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wcsdup") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcscat_s(_Inout_updates_z_(_SizeInWords) wchar_t * _Dst, _In_ rsize_t _SizeInWords, _In_z_ const wchar_t * _Src); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, wcscat_s, wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +#ifndef RC_INVOKED +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcscat, _Inout_updates_z_(_String_length_(_Dest) + _String_length_(_Source) + 1), wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +#endif +_Check_return_ +_When_(return != NULL, _Ret_range_(_Str, _Str+_String_length_(_Str)-1)) +_CRTIMP _CONST_RETURN wchar_t * __cdecl wcschr(_In_z_ const wchar_t * _Str, wchar_t _Ch); +_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcscpy_s(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ rsize_t _SizeInWords, _In_z_ const wchar_t * _Src); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, wcscpy_s, wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcscpy, _Out_writes_z_(_String_length_(_Source) + 1), wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +_Check_return_ _CRTIMP size_t __cdecl wcscspn(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _Control); +_Check_return_ _CRTIMP size_t __cdecl wcslen(_In_z_ const wchar_t * _Str); +_Check_return_ _CRTIMP +_When_(_MaxCount > _String_length_(_Src), _Post_satisfies_(return == _String_length_(_Src))) +_When_(_MaxCount <= _String_length_(_Src), _Post_satisfies_(return == _MaxCount)) +size_t __cdecl wcsnlen(_In_reads_or_z_(_MaxCount) const wchar_t * _Src, _In_ size_t _MaxCount); +#if __STDC_WANT_SECURE_LIB__ && !defined (__midl) +_Check_return_ static __inline +_When_(_MaxCount > _String_length_(_Src), _Post_satisfies_(return == _String_length_(_Src))) +_When_(_MaxCount <= _String_length_(_Src), _Post_satisfies_(return == _MaxCount)) +size_t __CRTDECL wcsnlen_s(_In_reads_or_z_(_MaxCount) const wchar_t * _Src, _In_ size_t _MaxCount) +{ + return (_Src == NULL) ? 0 : wcsnlen(_Src, _MaxCount); +} +#endif /* __STDC_WANT_SECURE_LIB__ && !defined (__midl) */ +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcsncat_s(_Inout_updates_z_(_SizeInWords) wchar_t * _Dst, _In_ rsize_t _SizeInWords, _In_reads_or_z_(_MaxCount) const wchar_t * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, wcsncat_s, _Prepost_z_ wchar_t, _Dest, _In_reads_or_z_(_Count) const wchar_t *, _Source, _In_ size_t, _Count) +#pragma warning(push) +#pragma warning(disable:6059) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcsncat, wcsncat_s, _Inout_updates_z_(_Size) wchar_t, _Inout_updates_z_(_Count), wchar_t, _Dest, _In_reads_or_z_(_Count) const wchar_t *, _Source, _In_ size_t, _Count) +#pragma warning(pop) +_Check_return_ _CRTIMP int __cdecl wcsncmp(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcsncpy_s(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ rsize_t _SizeInWords, _In_reads_or_z_(_MaxCount) const wchar_t * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, wcsncpy_s, wchar_t, _Dest, _In_reads_or_z_(_Count) const wchar_t *, _Source, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcsncpy, wcsncpy_s, _Pre_notnull_ _Post_maybez_ wchar_t, _Out_writes_(_Count) _Post_maybez_, wchar_t, _Dest, _In_reads_or_z_(_Count) const wchar_t *, _Source, _In_ size_t, _Count) +_Check_return_ _CRTIMP _CONST_RETURN wchar_t * __cdecl wcspbrk(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _Control); +_Check_return_ _CRTIMP _CONST_RETURN wchar_t * __cdecl wcsrchr(_In_z_ const wchar_t * _Str, _In_ wchar_t _Ch); +_Check_return_ _CRTIMP size_t __cdecl wcsspn(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _Control); +_Check_return_ _Ret_maybenull_ +_When_(return != NULL, _Ret_range_(_Str, _Str+_String_length_(_Str)-1)) +_CRTIMP _CONST_RETURN wchar_t * __cdecl wcsstr(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _SubStr); +_Check_return_ _CRT_INSECURE_DEPRECATE(wcstok_s) _CRTIMP wchar_t * __cdecl wcstok(_Inout_opt_z_ wchar_t * _Str, _In_z_ const wchar_t * _Delim); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ _CRTIMP_ALTERNATIVE wchar_t * __cdecl wcstok_s(_Inout_opt_z_ wchar_t * _Str, _In_z_ const wchar_t * _Delim, _Inout_ _Deref_prepost_opt_z_ wchar_t ** _Context); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ _CRT_INSECURE_DEPRECATE(_wcserror_s) _CRTIMP wchar_t * __cdecl _wcserror(_In_ int _ErrNum); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcserror_s(_Out_writes_opt_z_(_SizeInWords) wchar_t * _Buf, _In_ size_t _SizeInWords, _In_ int _ErrNum); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcserror_s, wchar_t, _Buffer, _In_ int, _Error) +_Check_return_ _CRT_INSECURE_DEPRECATE(__wcserror_s) _CRTIMP wchar_t * __cdecl __wcserror(_In_opt_z_ const wchar_t * _Str); +_Check_return_wat_ _CRTIMP errno_t __cdecl __wcserror_s(_Out_writes_opt_z_(_SizeInWords) wchar_t * _Buffer, _In_ size_t _SizeInWords, _In_z_ const wchar_t * _ErrMsg); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, __wcserror_s, wchar_t, _Buffer, _In_z_ const wchar_t *, _ErrorMessage) + +_Check_return_ _CRTIMP int __cdecl _wcsicmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRTIMP int __cdecl _wcsicmp_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsnicmp(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _wcsnicmp_l(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _wcsnset_s(_Inout_updates_z_(_SizeInWords) wchar_t * _Dst, _In_ size_t _SizeInWords, _In_ wchar_t _Val, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _wcsnset_s, _Prepost_z_ wchar_t, _Dst, wchar_t, _Val, _In_ size_t, _MaxCount) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsnset, _wcsnset_s, _Inout_updates_z_(_Size) wchar_t, _Inout_updates_z_(_MaxCount), wchar_t, _Str, wchar_t, _Val, _In_ size_t, _MaxCount) +_CRTIMP wchar_t * __cdecl _wcsrev(_Inout_z_ wchar_t * _Str); +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _wcsset_s(_Inout_updates_z_(_SizeInWords) wchar_t * _Dst, _In_ size_t _SizeInWords, _In_ wchar_t _Value); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcsset_s, _Prepost_z_ wchar_t, _Str, wchar_t, _Val) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsset, _wcsset_s, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _Str, wchar_t, _Val) + +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcslwr_s(_Inout_updates_z_(_SizeInWords) wchar_t * _Str, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wcslwr_s, _Prepost_z_ wchar_t, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcslwr, _Inout_z_, wchar_t, _String) +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcslwr_s_l(_Inout_updates_z_(_SizeInWords) wchar_t * _Str, _In_ size_t _SizeInWords, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcslwr_s_l, _Prepost_z_ wchar_t, _String, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcslwr_l, _wcslwr_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _String, _In_opt_ _locale_t, _Locale) +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcsupr_s(_Inout_updates_z_(_Size) wchar_t * _Str, _In_ size_t _Size); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wcsupr_s, _Prepost_z_ wchar_t, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsupr, _Inout_z_, wchar_t, _String) +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcsupr_s_l(_Inout_updates_z_(_Size) wchar_t * _Str, _In_ size_t _Size, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcsupr_s_l, _Prepost_z_ wchar_t, _String, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsupr_l, _wcsupr_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _String, _In_opt_ _locale_t, _Locale) +_Check_return_opt_ _CRTIMP size_t __cdecl wcsxfrm(_Out_writes_opt_(_MaxCount) _Post_maybez_ wchar_t * _Dst, _In_z_ const wchar_t * _Src, _In_ size_t _MaxCount); +_Check_return_opt_ _CRTIMP size_t __cdecl _wcsxfrm_l(_Out_writes_opt_(_MaxCount) _Post_maybez_ wchar_t * _Dst, _In_z_ const wchar_t *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl wcscoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRTIMP int __cdecl _wcscoll_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsicoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRTIMP int __cdecl _wcsicoll_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t *_Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsncoll(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _wcsncoll_l(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsnicoll(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _wcsnicoll_l(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); + +#ifdef __cplusplus +#ifndef _CPP_WIDE_INLINES_DEFINED +#define _CPP_WIDE_INLINES_DEFINED +extern "C++" { +_Check_return_ +_When_(return != NULL, _Ret_range_(_Str, _Str+_String_length_(_Str)-1)) + inline wchar_t * __CRTDECL wcschr(_In_z_ wchar_t *_Str, wchar_t _Ch) + {return ((wchar_t *)wcschr((const wchar_t *)_Str, _Ch)); } +_Check_return_ inline wchar_t * __CRTDECL wcspbrk(_In_z_ wchar_t *_Str, _In_z_ const wchar_t *_Control) + {return ((wchar_t *)wcspbrk((const wchar_t *)_Str, _Control)); } +_Check_return_ inline wchar_t * __CRTDECL wcsrchr(_In_z_ wchar_t *_Str, _In_ wchar_t _Ch) + {return ((wchar_t *)wcsrchr((const wchar_t *)_Str, _Ch)); } +_Check_return_ _Ret_maybenull_ +_When_(return != NULL, _Ret_range_(_Str, _Str+_String_length_(_Str)-1)) + inline wchar_t * __CRTDECL wcsstr(_In_z_ wchar_t *_Str, _In_z_ const wchar_t *_SubStr) + {return ((wchar_t *)wcsstr((const wchar_t *)_Str, _SubStr)); } +} +#endif /* _CPP_WIDE_INLINES_DEFINED */ +#endif /* __cplusplus */ + +#if !__STDC__ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("wcsdup") +#undef wcsdup +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsdup) _CRTIMP wchar_t * __cdecl wcsdup(_In_z_ const wchar_t * _Str); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("wcsdup") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +/* old names */ +#define wcswcs wcsstr + +/* prototypes for oldnames.lib functions */ +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsicmp) _CRTIMP int __cdecl wcsicmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsnicmp) _CRTIMP int __cdecl wcsnicmp(_In_reads_or_z_(_MaxCount) const wchar_t * _Str1, _In_reads_or_z_(_MaxCount) const wchar_t * _Str2, _In_ size_t _MaxCount); +_CRT_NONSTDC_DEPRECATE(_wcsnset) _CRTIMP wchar_t * __cdecl wcsnset(_Inout_updates_z_(_MaxCount) wchar_t * _Str, _In_ wchar_t _Val, _In_ size_t _MaxCount); +_CRT_NONSTDC_DEPRECATE(_wcsrev) _CRTIMP wchar_t * __cdecl wcsrev(_Inout_z_ wchar_t * _Str); +_CRT_NONSTDC_DEPRECATE(_wcsset) _CRTIMP wchar_t * __cdecl wcsset(_Inout_z_ wchar_t * _Str, wchar_t _Val); +_CRT_NONSTDC_DEPRECATE(_wcslwr) _CRTIMP wchar_t * __cdecl wcslwr(_Inout_z_ wchar_t * _Str); +_CRT_NONSTDC_DEPRECATE(_wcsupr) _CRTIMP wchar_t * __cdecl wcsupr(_Inout_z_ wchar_t * _Str); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsicoll) _CRTIMP int __cdecl wcsicoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); + +#endif /* !__STDC__ */ + +#define _WSTRING_DEFINED +#endif /* _WSTRING_DEFINED */ + +#ifdef _CRTBLD +_Check_return_ int __cdecl __ascii_memicmp(_In_reads_bytes_opt_(_Size) const void * _Buf1, _In_reads_bytes_opt_(_Size) const void * _Buf2, _In_ size_t _Size); +_Check_return_ int __cdecl __ascii_stricmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ int __cdecl __ascii_strnicmp(_In_reads_or_z_(_MaxCount) const char * _Str1, _In_reads_or_z_(_MaxCount) const char * _Str2, _In_ size_t _MaxCount); +#endif /* _CRTBLD */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _INC_STRING */ diff --git a/test_data/lots_of_files/sum_square_difference.cpp b/test_data/lots_of_files/sum_square_difference.cpp new file mode 100644 index 0000000..1982181 --- /dev/null +++ b/test_data/lots_of_files/sum_square_difference.cpp @@ -0,0 +1,54 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: + +#ifdef EULER_PROBLEM +// BUILDING AREA: +struct Euler_In{}; + +struct Euler_Result{ + int difference; +}; + +static const int NUMBER = 100; + +inline Euler_Result euler_main(Euler_In in){ + int n,x,y,d; + n = NUMBER; + x = (n)*(n+1)/2; + x *= x; + y = n*(n+1)*(2*n+1)/6; + d = x - y; + + Euler_Result result; + result.difference = d; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%d\n", answer.difference); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result; + + int c=0,d=0; + + for (int i = 1; i <= NUMBER; ++i){ + c+=i*i; + d+=i; + } + + d *= d; + d -= c; + + printf("answer = %d\n", d); + result = (d == answer.difference); + + return result; +} +#endif diff --git a/test_data/lots_of_files/summation_of_primes.cpp b/test_data/lots_of_files/summation_of_primes.cpp new file mode 100644 index 0000000..6b876e7 --- /dev/null +++ b/test_data/lots_of_files/summation_of_primes.cpp @@ -0,0 +1,123 @@ +/* + * YOUR INFO HERE! + */ + +// FINAL VERSION: +typedef unsigned long long bigint; +struct Sieve_Array{ + bool *is_composite; + bigint size; +}; + +struct Primes{ + bigint *primes; + int count, max; +}; + +int sieve_partial(Primes *ps_, Sieve_Array sieve, int start){ + Primes ps = *ps_; + int i=start,n,j; + for (; i < sieve.size; ++i){ + if (!sieve.is_composite[i]){ + n = i*2 + 3; + ps.primes[ps.count++] = n; + for (j = i + n; j < sieve.size; j += n){ + sieve.is_composite[j] = 1; + } + if (ps.count == ps.max){ + ++i; + break; + } + } + } + ps_->count = ps.count; + return i; +} + +#ifdef EULER_PROBLEM +// BUILDING AREA: + +typedef unsigned long long bigint; + +struct Euler_In{}; + +struct Euler_Result{ + bigint sum; +}; + +static const bigint TOP = 2000000; + +Euler_Result euler_main(Euler_In in){ + Sieve_Array sieve; + sieve.size = (TOP-1)/2; + sieve.is_composite = (bool*)malloc(sizeof(bool)*sieve.size); + memset(sieve.is_composite, 0, sizeof(bool)*sieve.size); + + Primes ps; + ps.count = 1; + ps.max = 10; + ps.primes = (bigint*)malloc(sizeof(bigint)*ps.max); + ps.primes[0] = 2; + + int i = sieve_partial(&ps, sieve, 0); + while (i < sieve.size){ + int new_max = ps.max*2; + bigint *new_primes = (bigint*)malloc(sizeof(bigint)*new_max); + memcpy(new_primes, ps.primes, sizeof(bigint)*ps.max); + free(ps.primes); + ps.max = new_max; + ps.primes = new_primes; + + i = sieve_partial(&ps, sieve, i); + } + + bigint s=0; + for (int i = 0; i < ps.count; ++i){ + s += ps.primes[i]; + } + + free(sieve.is_composite); + free(ps.primes); + + Euler_Result result; + result.sum = s; + return result; +} + +void euler_print(Euler_Result answer, Euler_In in){ + printf("%llu\n", answer.sum); +} + +#define EULER_CHECK + +bool euler_check(Euler_Result answer, Euler_In in){ + bool result = 1; + + bigint s=2; + int size_ = TOP / 3; + int *ps = (int*)malloc(sizeof(int)*size_); + int pc = 1; + ps[0] = 2; + + for (bigint n = 3; n < TOP; n += 2){ + bool prime = 1; + for (int i = 0; i < pc; ++i){ + int p = ps[i]; + if ((n % p) == 0){ + prime = 0; + break; + } + } + if (prime){ + ps[pc++] = n; + s += n; + } + } + + printf("answer = %llu\n", s); + result = (s == answer.sum); + + return result; +} +#endif + diff --git a/test_data/lots_of_files/systemTimer.s b/test_data/lots_of_files/systemTimer.s new file mode 100644 index 0000000..8ca126f --- /dev/null +++ b/test_data/lots_of_files/systemTimer.s @@ -0,0 +1,62 @@ +/****************************************************************************** +* systemTimer.s +* by Alex Chadwick +* +* A sample assembly code implementation of the OK05 operating system. +* See main.s for details. +* +* systemTime.s contains the code that interacts with the system timer. +******************************************************************************/ + +/* +* The system timer runs at 1MHz, and just counts always. Thus we can deduce +* timings by measuring the difference between two readings. +*/ + +/* +* GetSystemTimerBase returns the base address of the System Timer region as a +* physical address in register r0. +* C++ Signature: void* GetSystemTimerBase() +*/ +.globl GetSystemTimerBase +GetSystemTimerBase: + ldr r0,=0x20003000 + mov pc,lr + +/* +* GetTimeStamp gets the current timestamp of the system timer, and returns it +* in registers r0 and r1, with r1 being the most significant 32 bits. +* C++ Signature: u64 GetTimeStamp() +*/ +.globl GetTimeStamp +GetTimeStamp: + push {lr} + bl GetSystemTimerBase + ldrd r0,r1,[r0,#4] + pop {pc} + +/* +* Wait waits at least a specified number of microseconds before returning. +* The duration to wait is given in r0. +* C++ Signature: void Wait(u32 delayInMicroSeconds) +*/ +.globl Wait +Wait: + delay .req r2 + mov delay,r0 + push {lr} + bl GetTimeStamp + start .req r3 + mov start,r0 + + loop$: + bl GetTimeStamp + elapsed .req r1 + sub elapsed,r0,start + cmp elapsed,delay + .unreq elapsed + bls loop$ + + .unreq delay + .unreq start + pop {pc} diff --git a/test_data/lots_of_files/system_shared.cpp b/test_data/lots_of_files/system_shared.cpp new file mode 100644 index 0000000..741308c --- /dev/null +++ b/test_data/lots_of_files/system_shared.cpp @@ -0,0 +1,138 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 09.02.2016 + * + * Shared system functions + * + */ + +// TOP + +internal void +sysshared_filter_real_files(char **files, i32 *file_count){ + i32 i, j; + i32 end; + + end = *file_count; + for (i = 0, j = 0; i < end; ++i){ + if (system_file_can_be_made(files[i])){ + files[j] = files[i]; + ++j; + } + } + *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/test_data/lots_of_files/system_shared.h b/test_data/lots_of_files/system_shared.h new file mode 100644 index 0000000..8e27297 --- /dev/null +++ b/test_data/lots_of_files/system_shared.h @@ -0,0 +1,52 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 09.02.2016 + * + * Shared system functions + * + */ + +// TOP + +// NOTE(allen): This serves as a list of functions to implement +// in addition to those in 4ed_system.h These are not exposed to +// 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) +#define Sys_Load_File_Sig(name) Data name(char *filename) +#define Sys_Save_File_Sig(name) b32 name(char *filename, char *data, i32 size) +#define Sys_To_Binary_Path(name) b32 name(String *out_filename, char *filename) + +internal Sys_Get_Memory_Sig(system_get_memory_); +internal Sys_Free_Memory_Sig(system_free_memory); +internal Sys_File_Can_Be_Made(system_file_can_be_made); +internal Sys_Load_File_Sig(system_load_file); +internal Sys_Save_File_Sig(system_save_file); +internal Sys_To_Binary_Path(system_to_binary_path); + +#define system_get_memory(size) system_get_memory_((size), __LINE__, __FILE__) + +// BOTTOM + diff --git a/test_data/lots_of_files/tchar.h b/test_data/lots_of_files/tchar.h new file mode 100644 index 0000000..ce0390d --- /dev/null +++ b/test_data/lots_of_files/tchar.h @@ -0,0 +1,2507 @@ +/*** +*tchar.h - definitions for generic international text functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Definitions for generic international functions, mostly defines +* which map string/formatted-io/ctype functions to char, wchar_t, or +* MBCS versions. To be used for compatibility between single-byte, +* multi-byte and Unicode text models. +* +* [Public] +* +****/ + +#pragma once + +#include <crtdefs.h> + +#ifndef _INC_TCHAR +#define _INC_TCHAR + +#ifdef _STRSAFE_H_INCLUDED_ +#error Need to include strsafe.h after tchar.h +#endif /* _STRSAFE_H_INCLUDED_ */ + +#pragma warning(disable:4514) /* disable unwanted C++ /W4 warning */ +/* #pragma warning(default:4514) */ /* use this to reenable, if necessary */ + +/* Notes */ + +/* There is no: + * _tcscat_l + * _tcscpy_l + * because mbscat and mbscpy just behave like strcat and strcpy, + * so no special locale-specific behavior is needed. + */ + +/* Functions like: + * _strncat_l + * _strncat_s_l + * are only available if ANSI is defined (i.e. no _UNICODE nor _MBCS), + * because these functions are only accessible through the _tcs macros. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _CRT_FAR_MAPPINGS_NO_DEPRECATE +/* +Long ago, these f prefix text functions referred to handling of text in segmented architectures. Ever since the move +to Win32 they have been obsolete names, but we kept them around as aliases. Now that we have a deprecation +mechanism we can warn about them. You should switch to the identical function without the f prefix. +*/ +#pragma deprecated("_ftcscat") +#pragma deprecated("_ftcschr") +#pragma deprecated("_ftcscpy") +#pragma deprecated("_ftcscspn") +#pragma deprecated("_ftcslen") +#pragma deprecated("_ftcsncat") +#pragma deprecated("_ftcsncpy") +#pragma deprecated("_ftcspbrk") +#pragma deprecated("_ftcsrchr") +#pragma deprecated("_ftcsspn") +#pragma deprecated("_ftcsstr") +#pragma deprecated("_ftcstok") +#pragma deprecated("_ftcsdup") +#pragma deprecated("_ftcsnset") +#pragma deprecated("_ftcsrev") +#pragma deprecated("_ftcsset") +#pragma deprecated("_ftcscmp") +#pragma deprecated("_ftcsicmp") +#pragma deprecated("_ftcsnccmp") +#pragma deprecated("_ftcsncmp") +#pragma deprecated("_ftcsncicmp") +#pragma deprecated("_ftcsnicmp") +#pragma deprecated("_ftcscoll") +#pragma deprecated("_ftcsicoll") +#pragma deprecated("_ftcsnccoll") +#pragma deprecated("_ftcsncoll") +#pragma deprecated("_ftcsncicoll") +#pragma deprecated("_ftcsnicoll") +#pragma deprecated("_ftcsclen") +#pragma deprecated("_ftcsnccat") +#pragma deprecated("_ftcsnccpy") +#pragma deprecated("_ftcsncset") +#pragma deprecated("_ftcsdec") +#pragma deprecated("_ftcsinc") +#pragma deprecated("_ftcsnbcnt") +#pragma deprecated("_ftcsnccnt") +#pragma deprecated("_ftcsnextc") +#pragma deprecated("_ftcsninc") +#pragma deprecated("_ftcsspnp") +#pragma deprecated("_ftcslwr") +#pragma deprecated("_ftcsupr") +#pragma deprecated("_ftclen") +#pragma deprecated("_ftccpy") +#pragma deprecated("_ftccmp") +#endif /* _CRT_FAR_MAPPINGS_NO_DEPRECATE */ + +#define _ftcscat _tcscat +#define _ftcschr _tcschr +#define _ftcscpy _tcscpy +#define _ftcscspn _tcscspn +#define _ftcslen _tcslen +#define _ftcsncat _tcsncat +#define _ftcsncpy _tcsncpy +#define _ftcspbrk _tcspbrk +#define _ftcsrchr _tcsrchr +#define _ftcsspn _tcsspn +#define _ftcsstr _tcsstr +#define _ftcstok _tcstok + +#define _ftcsdup _tcsdup +#define _ftcsnset _tcsnset +#define _ftcsrev _tcsrev +#define _ftcsset _tcsset + +#define _ftcscmp _tcscmp +#define _ftcsicmp _tcsicmp +#define _ftcsnccmp _tcsnccmp +#define _ftcsncmp _tcsncmp +#define _ftcsncicmp _tcsncicmp +#define _ftcsnicmp _tcsnicmp + +#define _ftcscoll _tcscoll +#define _ftcsicoll _tcsicoll +#define _ftcsnccoll _tcsnccoll +#define _ftcsncoll _tcsncoll +#define _ftcsncicoll _tcsncicoll +#define _ftcsnicoll _tcsnicoll + +/* Redundant "logical-character" mappings */ + +#define _ftcsclen _tcsclen +#define _ftcsnccat _tcsnccat +#define _ftcsnccpy _tcsnccpy +#define _ftcsncset _tcsncset + +#define _ftcsdec _tcsdec +#define _ftcsinc _tcsinc +#define _ftcsnbcnt _tcsnbcnt +#define _ftcsnccnt _tcsnccnt +#define _ftcsnextc _tcsnextc +#define _ftcsninc _tcsninc +#define _ftcsspnp _tcsspnp + +#define _ftcslwr _tcslwr +#define _ftcsupr _tcsupr + +#define _ftclen _tclen +#define _ftccpy _tccpy +#define _ftccmp _tccmp + +#ifndef _CONST_RETURN +#ifdef __cplusplus +#define _CONST_RETURN const +#define _CRT_CONST_CORRECT_OVERLOADS +#else /* __cplusplus */ +#define _CONST_RETURN +#endif /* __cplusplus */ +#endif /* _CONST_RETURN */ + +/* For backwards compatibility */ +#define _WConst_return _CONST_RETURN + +#ifdef _UNICODE + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif /* __cplusplus */ + +/* ++++++++++++++++++++ UNICODE ++++++++++++++++++++ */ + +#include <wchar.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef _WCTYPE_T_DEFINED +typedef unsigned short wint_t; +typedef unsigned short wctype_t; +#define _WCTYPE_T_DEFINED +#endif /* _WCTYPE_T_DEFINED */ + +#ifndef __TCHAR_DEFINED +typedef wchar_t _TCHAR; +typedef wchar_t _TSCHAR; +typedef wchar_t _TUCHAR; +typedef wchar_t _TXCHAR; +typedef wint_t _TINT; +#define __TCHAR_DEFINED +#endif /* __TCHAR_DEFINED */ + +#ifndef _TCHAR_DEFINED +#if !__STDC__ +typedef wchar_t TCHAR; +#endif /* !__STDC__ */ +#define _TCHAR_DEFINED +#endif /* _TCHAR_DEFINED */ + +#define _TEOF WEOF + +#define __T(x) L ## x + + +/* Program */ + +#define _tmain wmain +#define _tWinMain wWinMain +#define _tenviron _wenviron +#define __targv __wargv + +/* Formatted i/o */ + +#define _tprintf wprintf +#define _tprintf_l _wprintf_l +#define _tprintf_s wprintf_s +#define _tprintf_s_l _wprintf_s_l +#define _tprintf_p _wprintf_p +#define _tprintf_p_l _wprintf_p_l +#define _tcprintf _cwprintf +#define _tcprintf_l _cwprintf_l +#define _tcprintf_s _cwprintf_s +#define _tcprintf_s_l _cwprintf_s_l +#define _tcprintf_p _cwprintf_p +#define _tcprintf_p_l _cwprintf_p_l +#define _vtcprintf _vcwprintf +#define _vtcprintf_l _vcwprintf_l +#define _vtcprintf_s _vcwprintf_s +#define _vtcprintf_s_l _vcwprintf_s_l +#define _vtcprintf_p _vcwprintf_p +#define _vtcprintf_p_l _vcwprintf_p_l +#define _ftprintf fwprintf +#define _ftprintf_l _fwprintf_l +#define _ftprintf_s fwprintf_s +#define _ftprintf_s_l _fwprintf_s_l +#define _ftprintf_p _fwprintf_p +#define _ftprintf_p_l _fwprintf_p_l +#define _stprintf _swprintf +#define _stprintf_l __swprintf_l +#define _stprintf_s swprintf_s +#define _stprintf_s_l _swprintf_s_l +#define _stprintf_p _swprintf_p +#define _stprintf_p_l _swprintf_p_l +#define _sctprintf _scwprintf +#define _sctprintf_l _scwprintf_l +#define _sctprintf_p _scwprintf_p +#define _sctprintf_p_l _scwprintf_p_l +#define _sntprintf _snwprintf +#define _sntprintf_l _snwprintf_l +#define _sntprintf_s _snwprintf_s +#define _sntprintf_s_l _snwprintf_s_l +#define _vtprintf vwprintf +#define _vtprintf_l _vwprintf_l +#define _vtprintf_s vwprintf_s +#define _vtprintf_s_l _vwprintf_s_l +#define _vtprintf_p _vwprintf_p +#define _vtprintf_p_l _vwprintf_p_l +#define _vftprintf vfwprintf +#define _vftprintf_l _vfwprintf_l +#define _vftprintf_s vfwprintf_s +#define _vftprintf_s_l _vfwprintf_s_l +#define _vftprintf_p _vfwprintf_p +#define _vftprintf_p_l _vfwprintf_p_l +#define _vstprintf vswprintf +#define _vstprintf_l _vswprintf_l +#define _vstprintf_s vswprintf_s +#define _vstprintf_s_l _vswprintf_s_l +#define _vstprintf_p _vswprintf_p +#define _vstprintf_p_l _vswprintf_p_l +#define _vsctprintf _vscwprintf +#define _vsctprintf_l _vscwprintf_l +#define _vsctprintf_p _vscwprintf_p +#define _vsctprintf_p_l _vscwprintf_p_l +#define _vsntprintf _vsnwprintf +#define _vsntprintf_l _vsnwprintf_l +#define _vsntprintf_s _vsnwprintf_s +#define _vsntprintf_s_l _vsnwprintf_s_l + +#define _tscanf wscanf +#define _tscanf_l _wscanf_l +#define _tscanf_s wscanf_s +#define _tscanf_s_l _wscanf_s_l +#define _tcscanf _cwscanf +#define _tcscanf_l _cwscanf_l +#define _tcscanf_s _cwscanf_s +#define _tcscanf_s_l _cwscanf_s_l +#define _ftscanf fwscanf +#define _ftscanf_l _fwscanf_l +#define _ftscanf_s fwscanf_s +#define _ftscanf_s_l _fwscanf_s_l +#define _stscanf swscanf +#define _stscanf_l _swscanf_l +#define _stscanf_s swscanf_s +#define _stscanf_s_l _swscanf_s_l +#define _sntscanf _snwscanf +#define _sntscanf_l _snwscanf_l +#define _sntscanf_s _snwscanf_s +#define _sntscanf_s_l _snwscanf_s_l +#define _vtscanf vwscanf +#define _vtscanf_s vwscanf_s +#define _vftscanf vfwscanf +#define _vftscanf_s vfwscanf_s +#define _vstscanf vswscanf +#define _vstscanf_s vswscanf_s + + +/* Unformatted i/o */ + +#define _fgettc fgetwc +#define _fgettc_nolock _fgetwc_nolock +#define _fgettchar _fgetwchar +#define _fgetts fgetws +#define _fputtc fputwc +#define _fputtc_nolock _fputwc_nolock +#define _fputtchar _fputwchar +#define _fputts fputws +#define _cputts _cputws +#define _cgetts _cgetws +#define _cgetts_s _cgetws_s +#define _gettc getwc +#define _gettc_nolock _getwc_nolock +#define _gettch _getwch +#define _gettch_nolock _getwch_nolock +#define _gettche _getwche +#define _gettche_nolock _getwche_nolock +#define _gettchar getwchar +#define _gettchar_nolock _getwchar_nolock +#define _getts _getws +#define _getts_s _getws_s +#define _puttc putwc +#define _puttc_nolock _putwc_nolock +#define _puttchar putwchar +#define _puttchar_nolock _putwchar_nolock +#define _puttch _putwch +#define _puttch_nolock _putwch_nolock +#define _putts _putws +#define _ungettc ungetwc +#define _ungettc_nolock _ungetwc_nolock +#define _ungettch _ungetwch +#define _ungettch_nolock _ungetwch_nolock + + +/* String conversion functions */ + +#define _tcstod wcstod +#define _tcstof wcstof +#define _tcstol wcstol +#define _tcstold wcstold +#define _tcstoll wcstoll +#define _tcstoul wcstoul +#define _tcstoull wcstoull +#define _tcstoimax wcstoimax +#define _tcstoumax wcstoumax +#define _tcstoi64 _wcstoi64 +#define _tcstoui64 _wcstoui64 +#define _ttof _wtof +#define _tstof _wtof +#define _tstol _wtol +#define _tstoll _wtoll +#define _tstoi _wtoi +#define _tstoi64 _wtoi64 +#define _tcstod_l _wcstod_l +#define _tcstof_l _wcstof_l +#define _tcstol_l _wcstol_l +#define _tcstold_l _wcstold_l +#define _tcstoll_l _wcstoll_l +#define _tcstoul_l _wcstoul_l +#define _tcstoull_l _wcstoull_l +#define _tcstoi64_l _wcstoi64_l +#define _tcstoui64_l _wcstoui64_l +#define _tcstoimax_l _wcstoimax_l +#define _tcstoumax_l _wcstoumax_l +#define _tstof_l _wtof_l +#define _tstol_l _wtol_l +#define _tstoll_l _wtoll_l +#define _tstoi_l _wtoi_l +#define _tstoi64_l _wtoi64_l + +#define _itot_s _itow_s +#define _ltot_s _ltow_s +#define _ultot_s _ultow_s +#define _itot _itow +#define _ltot _ltow +#define _ultot _ultow +#define _ttoi _wtoi +#define _ttol _wtol +#define _ttoll _wtoll + +#define _ttoi64 _wtoi64 +#define _i64tot_s _i64tow_s +#define _ui64tot_s _ui64tow_s +#define _i64tot _i64tow +#define _ui64tot _ui64tow + +/* String functions */ + +#define _tcscat wcscat +#define _tcscat_s wcscat_s +#define _tcschr wcschr +#define _tcscpy wcscpy +#define _tcscpy_s wcscpy_s +#define _tcscspn wcscspn +#define _tcslen wcslen +#define _tcsnlen wcsnlen +#define _tcsncat wcsncat +#define _tcsncat_s wcsncat_s +#define _tcsncat_l _wcsncat_l +#define _tcsncat_s_l _wcsncat_s_l +#define _tcsncpy wcsncpy +#define _tcsncpy_s wcsncpy_s +#define _tcsncpy_l _wcsncpy_l +#define _tcsncpy_s_l _wcsncpy_s_l +#define _tcspbrk wcspbrk +#define _tcsrchr wcsrchr +#define _tcsspn wcsspn +#define _tcsstr wcsstr +#define _tcstok wcstok +#define _tcstok_s wcstok_s +#define _tcstok_l _wcstok_l +#define _tcstok_s_l _wcstok_s_l +#define _tcserror _wcserror +#define _tcserror_s _wcserror_s +#define __tcserror __wcserror +#define __tcserror_s __wcserror_s + +#define _tcsdup _wcsdup +#define _tcsnset _wcsnset +#define _tcsnset_s _wcsnset_s +#define _tcsnset_l _wcsnset_l +#define _tcsnset_s_l _wcsnset_s_l +#define _tcsrev _wcsrev +#define _tcsset _wcsset +#define _tcsset_s _wcsset_s +#define _tcsset_l _wcsset_l +#define _tcsset_s_l _wcsset_s_l + +#define _tcscmp wcscmp +#define _tcsicmp _wcsicmp +#define _tcsicmp_l _wcsicmp_l +#define _tcsnccmp wcsncmp +#define _tcsncmp wcsncmp +#define _tcsncicmp _wcsnicmp +#define _tcsncicmp_l _wcsnicmp_l +#define _tcsnicmp _wcsnicmp +#define _tcsnicmp_l _wcsnicmp_l + +#define _tcscoll wcscoll +#define _tcscoll_l _wcscoll_l +#define _tcsicoll _wcsicoll +#define _tcsicoll_l _wcsicoll_l +#define _tcsnccoll _wcsncoll +#define _tcsnccoll_l _wcsncoll_l +#define _tcsncoll _wcsncoll +#define _tcsncoll_l _wcsncoll_l +#define _tcsncicoll _wcsnicoll +#define _tcsncicoll_l _wcsnicoll_l +#define _tcsnicoll _wcsnicoll +#define _tcsnicoll_l _wcsnicoll_l + +#ifdef _DEBUG +#define _tcsdup_dbg _wcsdup_dbg +#endif /* _DEBUG */ + +/* Execute functions */ + +#define _texecl _wexecl +#define _texecle _wexecle +#define _texeclp _wexeclp +#define _texeclpe _wexeclpe +#define _texecv _wexecv +#define _texecve _wexecve +#define _texecvp _wexecvp +#define _texecvpe _wexecvpe + +#define _tspawnl _wspawnl +#define _tspawnle _wspawnle +#define _tspawnlp _wspawnlp +#define _tspawnlpe _wspawnlpe +#define _tspawnv _wspawnv +#define _tspawnve _wspawnve +#define _tspawnvp _wspawnvp +#define _tspawnvp _wspawnvp +#define _tspawnvpe _wspawnvpe + +#define _tsystem _wsystem + + +/* Time functions */ + +#define _tasctime _wasctime +#define _tctime _wctime +#define _tctime32 _wctime32 +#define _tctime64 _wctime64 +#define _tstrdate _wstrdate +#define _tstrtime _wstrtime +#define _tutime _wutime +#define _tutime32 _wutime32 +#define _tutime64 _wutime64 +#define _tcsftime wcsftime +#define _tcsftime_l _wcsftime_l + +#define _tasctime_s _wasctime_s +#define _tctime_s _wctime_s +#define _tctime32_s _wctime32_s +#define _tctime64_s _wctime64_s +#define _tstrdate_s _wstrdate_s +#define _tstrtime_s _wstrtime_s + +/* Directory functions */ + +#define _tchdir _wchdir +#define _tgetcwd _wgetcwd +#define _tgetdcwd _wgetdcwd +#define _tgetdcwd_nolock _wgetdcwd_nolock +#define _tmkdir _wmkdir +#define _trmdir _wrmdir + +#ifdef _DEBUG +#define _tgetcwd_dbg _wgetcwd_dbg +#define _tgetdcwd_dbg _wgetdcwd_dbg +#define _tgetdcwd_lk_dbg _wgetdcwd_lk_dbg +#endif /* _DEBUG */ + +/* Environment/Path functions */ + +#define _tfullpath _wfullpath +#define _tgetenv _wgetenv +#define _tgetenv_s _wgetenv_s +#define _tdupenv_s _wdupenv_s +#define _tmakepath _wmakepath +#define _tmakepath_s _wmakepath_s +#define _tpgmptr _wpgmptr +#define _get_tpgmptr _get_wpgmptr +#define _tputenv _wputenv +#define _tputenv_s _wputenv_s +#define _tsearchenv _wsearchenv +#define _tsearchenv_s _wsearchenv_s +#define _tsplitpath _wsplitpath +#define _tsplitpath_s _wsplitpath_s + +#ifdef _DEBUG +#define _tfullpath_dbg _wfullpath_dbg +#define _tdupenv_s_dbg _wdupenv_s_dbg +#endif /* _DEBUG */ + +/* Stdio functions */ + +#define _tfdopen _wfdopen +#define _tfsopen _wfsopen +#define _tfopen _wfopen +#define _tfopen_s _wfopen_s +#define _tfreopen _wfreopen +#define _tfreopen_s _wfreopen_s +#define _tperror _wperror +#define _tpopen _wpopen +#define _ttempnam _wtempnam +#define _ttmpnam _wtmpnam +#define _ttmpnam_s _wtmpnam_s + +#ifdef _DEBUG +#define _ttempnam_dbg _wtempnam_dbg +#endif /* _DEBUG */ + + +/* Io functions */ + +#define _taccess _waccess +#define _taccess_s _waccess_s +#define _tchmod _wchmod +#define _tcreat _wcreat +#define _tfindfirst _wfindfirst +#define _tfindfirst32 _wfindfirst32 +#define _tfindfirst64 _wfindfirst64 +#define _tfindfirsti64 _wfindfirsti64 +#define _tfindfirst32i64 _wfindfirst32i64 +#define _tfindfirst64i32 _wfindfirst64i32 +#define _tfindnext _wfindnext +#define _tfindnext32 _wfindnext32 +#define _tfindnext64 _wfindnext64 +#define _tfindnexti64 _wfindnexti64 +#define _tfindnext32i64 _wfindnext32i64 +#define _tfindnext64i32 _wfindnext64i32 +#define _tmktemp _wmktemp +#define _tmktemp_s _wmktemp_s +#define _topen _wopen +#define _tremove _wremove +#define _trename _wrename +#define _tsopen _wsopen +#define _tsopen_s _wsopen_s +#define _tunlink _wunlink + +#define _tfinddata_t _wfinddata_t +#define _tfinddata32_t _wfinddata32_t +#define _tfinddata64_t _wfinddata64_t +#define _tfinddatai64_t _wfinddatai64_t +#define _tfinddata32i64_t _wfinddata32i64_t +#define _tfinddata64i32_t _wfinddata64i32_t + + +/* Stat functions */ + +#define _tstat _wstat +#define _tstat32 _wstat32 +#define _tstat32i64 _wstat32i64 +#define _tstat64 _wstat64 +#define _tstat64i32 _wstat64i32 +#define _tstati64 _wstati64 + + +/* Setlocale functions */ + +#define _tsetlocale _wsetlocale + + +/* Redundant "logical-character" mappings */ + +#define _tcsclen wcslen +#define _tcscnlen wcsnlen +#define _tcsclen_l(_String, _Locale) wcslen(_String) +#define _tcscnlen_l(_String, _Max_count, _Locale) wcsnlen((_String), (_Max_count)) +#define _tcsnccat wcsncat +#define _tcsnccat_s wcsncat_s +#define _tcsnccat_l _wcsncat_l +#define _tcsnccat_s_l _wcsncat_s_l +#define _tcsnccpy wcsncpy +#define _tcsnccpy_s wcsncpy_s +#define _tcsnccpy_l _wcsncpy_l +#define _tcsnccpy_s_l _wcsncpy_s_l +#define _tcsncset _wcsnset +#define _tcsncset_s _wcsnset_s +#define _tcsncset_l _wcsnset_l +#define _tcsncset_s_l _wcsnset_s_l + +#define _tcsdec _wcsdec +#define _tcsinc _wcsinc +#define _tcsnbcnt _wcsncnt +#define _tcsnccnt _wcsncnt +#define _tcsnextc _wcsnextc +#define _tcsninc _wcsninc +#define _tcsspnp _wcsspnp + +#define _tcslwr _wcslwr +#define _tcslwr_l _wcslwr_l +#define _tcslwr_s _wcslwr_s +#define _tcslwr_s_l _wcslwr_s_l +#define _tcsupr _wcsupr +#define _tcsupr_l _wcsupr_l +#define _tcsupr_s _wcsupr_s +#define _tcsupr_s_l _wcsupr_s_l +#define _tcsxfrm wcsxfrm +#define _tcsxfrm_l _wcsxfrm_l + + +#if __STDC__ || defined (_NO_INLINING) +#define _tclen(_pc) (1) +#define _tccpy(_pc1,_cpc2) ((*(_pc1) = *(_cpc2))) +#define _tccpy_l(_pc1,_cpc2,_locale) _tccpy((_pc1),(_cpc2)) +#define _tccmp(_cpc1,_cpc2) ((*(_cpc1))-(*(_cpc2))) +#else /* __STDC__ || defined (_NO_INLINING) */ +_Check_return_ __inline size_t __CRTDECL _tclen(_In_z_ const wchar_t *_Cpc) +{ + /* avoid compiler warning */ + (void *)_Cpc; + return 1; +} +__inline void __CRTDECL _tccpy(_Out_ wchar_t *_Pc1, _In_z_ const wchar_t *_Cpc2) { *_Pc1 = (wchar_t)*_Cpc2; } +__inline void __CRTDECL _tccpy_l(_Out_ wchar_t *_Pc1, _In_z_ const wchar_t *_Cpc2, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 28719 ) + _tccpy(_Pc1, _Cpc2); +#pragma warning( pop ) +} +_Check_return_ __inline int __CRTDECL _tccmp(_In_z_ const wchar_t *_Cpc1, _In_z_ const wchar_t *_Cpc2) { return (int) ((*_Cpc1)-(*_Cpc2)); } +#endif /* __STDC__ || defined (_NO_INLINING) */ + +/* ctype functions */ + +#define _istalnum iswalnum +#define _istalnum_l _iswalnum_l +#define _istalpha iswalpha +#define _istalpha_l _iswalpha_l +#define _istascii iswascii +#define _istcntrl iswcntrl +#define _istcntrl_l _iswcntrl_l +#define _istdigit iswdigit +#define _istdigit_l _iswdigit_l +#define _istgraph iswgraph +#define _istgraph_l _iswgraph_l +#define _istlower iswlower +#define _istlower_l _iswlower_l +#define _istprint iswprint +#define _istprint_l _iswprint_l +#define _istpunct iswpunct +#define _istpunct_l _iswpunct_l +#define _istblank iswblank +#define _istblank_l _iswblank_l +#define _istspace iswspace +#define _istspace_l _iswspace_l +#define _istupper iswupper +#define _istupper_l _iswupper_l +#define _istxdigit iswxdigit +#define _istxdigit_l _iswxdigit_l + +#define _totupper towupper +#define _totupper_l _towupper_l +#define _totlower towlower +#define _totlower_l _towlower_l + +#define _istlegal(_Char) (1) +#define _istlead(_Char) (0) +#define _istleadbyte(_Char) (0) +#define _istleadbyte_l(_Char, _Locale) (0) + + +#if __STDC__ || defined (_NO_INLINING) +#define _wcsdec(_cpc1, _cpc2) ((_cpc1)>=(_cpc2) ? NULL : (_cpc2)-1) +#define _wcsinc(_pc) ((_pc)+1) +#define _wcsnextc(_cpc) ((unsigned int) *(_cpc)) +#define _wcsninc(_pc, _sz) (((_pc)+(_sz))) +_Check_return_ _CRTIMP size_t __cdecl __wcsncnt(_In_reads_or_z_(_MaxCount) const wchar_t * _Str, _In_ size_t _MaxCount); +#define _wcsncnt(_cpc, _sz) (__wcsncnt(_cpc,_sz)) +#define _wcsspnp(_cpc1, _cpc2) (_cpc1==NULL ? NULL : ((*((_cpc1)+wcsspn(_cpc1,_cpc2))) ? ((_cpc1)+wcsspn(_cpc1,_cpc2)) : NULL)) +#define _wcsncpy_l(_Destination, _Source, _Count, _Locale) (wcsncpy(_Destination, _Source, _Count)) +#if __STDC_WANT_SECURE_LIB__ +#define _wcsncpy_s_l(_Destination, _Destination_size_chars, _Source, _Count, _Locale) (wcsncpy_s(_Destination, _Destination_size_chars, _Source, _Count)) +#endif /* __STDC_WANT_SECURE_LIB__ */ +#define _wcsncat_l(_Destination, _Source, _Count, _Locale) (wcsncat(_Destination, _Source, _Count)) +#if __STDC_WANT_SECURE_LIB__ +#define _wcsncat_s_l(_Destination, _Destination_size_chars, _Source, _Count, _Locale) (wcsncat_s(_Destination, _Destination_size_chars, _Source, _Count)) +#endif /* __STDC_WANT_SECURE_LIB__ */ +#define _wcstok_l(_String, _Delimiters, _Locale) (wcstok(_String, _Delimiters)) +#define _wcstok_s_l(_String, _Delimiters, _Current_position, _Locale) (wcstok_s(_String, _Delimiters, _Current_position)) +#define _wcsnset_l(_Destination, _Value, _Count, _Locale) (_wcsnset(_Destination, _Value, _Count)) +#define _wcsnset_s_l(_Destination, _Destination_size_chars, _Value, _Count, _Locale) (_wcsnset_s(_Destination, _Destination_size_chars, _Value, _Count)) +#define _wcsset_l(_Destination, _Value, _Locale) (_wcsset(_Destination, _Value)) +#define _wcsset_s_l(_Destination, _Destination_size_chars, _Value, _Locale) (_wcsset_s(_Destination, _Destination_size_chars, _Value)) +#else /* __STDC__ || defined (_NO_INLINING) */ +_Check_return_ __inline wchar_t * __CRTDECL _wcsdec(_In_z_ const wchar_t * _Cpc1, _In_z_ const wchar_t * _Cpc2) { return (wchar_t *)((_Cpc1)>=(_Cpc2) ? NULL : ((_Cpc2)-1)); } +_Check_return_ __inline wchar_t * __CRTDECL _wcsinc(_In_z_ const wchar_t * _Pc) { return (wchar_t *)(_Pc+1); } +_Check_return_ __inline unsigned int __CRTDECL _wcsnextc(_In_z_ const wchar_t * _Cpc) { return (unsigned int)*_Cpc; } +_Check_return_ __inline wchar_t * __CRTDECL _wcsninc(_In_z_ const wchar_t * _Pc, _In_ size_t _Sz) { return (wchar_t *)(_Pc+_Sz); } +_Check_return_ __inline size_t __CRTDECL _wcsncnt( _In_reads_or_z_(_Cnt) const wchar_t * _String, _In_ size_t _Cnt) +{ + size_t n = _Cnt; + wchar_t *cp = (wchar_t *)_String; + while (n-- && *cp) + cp++; + return _Cnt - n - 1; +} +_Check_return_ __inline wchar_t * __CRTDECL _wcsspnp +( + _In_z_ const wchar_t * _Cpc1, + _In_z_ const wchar_t * _Cpc2 +) +{ + return _Cpc1==NULL ? NULL : ((*(_Cpc1 += wcsspn(_Cpc1,_Cpc2))!='\0') ? (wchar_t*)_Cpc1 : NULL); +} + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ __inline errno_t __CRTDECL _wcsncpy_s_l(_Out_writes_z_(_Destination_size_chars) wchar_t *_Destination, _In_ size_t _Destination_size_chars, _In_z_ const wchar_t *_Source, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return wcsncpy_s(_Destination, _Destination_size_chars, _Source, _Count); +} +#endif /* __STDC_WANT_SECURE_LIB__ */ + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _wcsncpy_s_l, wchar_t, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(wchar_t *, __RETURN_POLICY_DST, _wcsncpy_l, _wcsncpy_s_l, _Out_writes_z_(_Size) wchar_t, _Out_writes_(_Count) wchar_t, _Dst, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 6054 28719) + return wcsncpy(_Dst, _Source, _Count); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(wchar_t *, __RETURN_POLICY_DST, _wcsncpy_l, _wcsncpy_s_l, _Out_writes_z_(_Size) wchar_t, _Out_writes_(_Count), wchar_t, _Dst, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ __inline errno_t __CRTDECL _wcsncat_s_l(_Inout_updates_z_(_Destination_size_chars) wchar_t *_Destination, _In_ size_t _Destination_size_chars, _In_z_ const wchar_t *_Source, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return wcsncat_s(_Destination, _Destination_size_chars, _Source, _Count); +} +#endif /* __STDC_WANT_SECURE_LIB__ */ + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _wcsncat_s_l, wchar_t, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(wchar_t *, __RETURN_POLICY_DST, _wcsncat_l, _wcsncat_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_ wchar_t, _Dst, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 6054 6059 28719 ) +/* prefast noise VSW 489802 */ + return wcsncat(_Dst, _Source, _Count); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(wchar_t *, __RETURN_POLICY_DST, _wcsncat_l, _wcsncat_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _Dst, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +_CRT_INSECURE_DEPRECATE(_wcstok_s_l) __inline wchar_t * _wcstok_l(_Inout_opt_z_ wchar_t * _String, _In_z_ const wchar_t * _Delimiters, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning(push) +#pragma warning(disable:4996 28727) + return wcstok(_String,_Delimiters); +#pragma warning(pop) +} + +__inline wchar_t * _wcstok_s_l(_Inout_opt_z_ wchar_t * _String, _In_z_ const wchar_t * _Delimiters, _Inout_ _Deref_prepost_opt_z_ wchar_t **_Current_position, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return wcstok_s(_String, _Delimiters, _Current_position); +} + +_Check_return_wat_ __inline errno_t _wcsnset_s_l(_Inout_updates_z_(_Destination_size_chars) wchar_t * _Destination, _In_ size_t _Destination_size_chars, _In_ wchar_t _Value, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return _wcsnset_s(_Destination, _Destination_size_chars, _Value, _Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _wcsnset_s_l, _Prepost_z_ wchar_t, _Dest, _In_ wchar_t, _Value, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(wchar_t *, __RETURN_POLICY_DST, _wcsnset_l, _wcsnset_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_updates_z_(_MaxCount) wchar_t, _Dst, _In_ wchar_t, _Value, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 ) + return _wcsnset(_Dst, _Value, _MaxCount); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(wchar_t *, __RETURN_POLICY_DST, _wcsnset_l, _wcsnset_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_updates_z_(_MaxCount), wchar_t, _Dst, _In_ wchar_t, _Value, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) + +_Check_return_wat_ __inline errno_t _wcsset_s_l(_Inout_updates_z_(_Destination_size_chars) wchar_t * _Destination, _In_ size_t _Destination_size_chars, _In_ wchar_t _Value, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return _wcsset_s(_Destination, _Destination_size_chars, _Value); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _wcsset_s_l, _Prepost_z_ wchar_t, _Dest, _In_ wchar_t, _Value, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _wcsset_l, _wcsset_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_ wchar_t, _Dst, _In_ wchar_t, _Value, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 ) + return _wcsset(_Dst, _Value); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _wcsset_l, _wcsset_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _Dst, _In_ wchar_t, _Value, _In_opt_ _locale_t, _Locale) + +#endif /* __STDC__ || defined (_NO_INLINING) */ + +#ifdef _CRTBLD + +/* + * macros for internal CRT stuff. + * This saves the mess that is created in the CRT. + */ +#ifdef _SAFECRT_IMPL +#define __tinput _winput +#define __tinput_s _winput_s +#else /* _SAFECRT_IMPL */ +#define _tinput_l _winput_l +#define _tinput_s_l _winput_s_l +#endif /* _SAFECRT_IMPL */ +#define __topenfile _wopenfile +#define _tgetpath _wgetpath +#define __crtMessageBox __crtMessageBoxW +#define __crtMessageWindow __crtMessageWindowW +#define _VCrtDbgReportT _VCrtDbgReportW +#define _CrtDbgReportT _CrtDbgReportW +#define _CrtDbgReportTV _CrtDbgReportWV +#define ReportHookNodeT ReportHookNodeW +#define _pReportHookListT _pReportHookListW +#define _CrtSetReportHookT2 _CrtSetReportHookW2 +#define _CRT_REPORT_HOOKT _CRT_REPORT_HOOKW +#define _ttmpnam_helper _wtmpnam_helper +#define _tfreopen_helper _wfreopen_helper +#define _getts_helper _getws_helper +#define _tfullpath_helper _wfullpath_helper +#define _tsopen_helper _wsopen_helper +#define _tsopen_nolock _wsopen_nolock +#define _tdupenv_s_crt _wdupenv_s_crt + +#endif /* _CRTBLD */ + +#else /* _UNICODE */ + +/* ++++++++++++++++++++ SBCS and MBCS ++++++++++++++++++++ */ + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif /* __cplusplus */ + +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define _TEOF EOF + +#define __T(x) x + + +/* Program */ + +#define _tmain main +#define _tWinMain WinMain +#define _tenviron _environ +#define __targv __argv + + +/* Formatted i/o */ + +#define _tprintf printf +#define _tprintf_l _printf_l +#define _tprintf_s printf_s +#define _tprintf_s_l _printf_s_l +#define _tprintf_p _printf_p +#define _tprintf_p_l _printf_p_l +#define _tcprintf _cprintf +#define _tcprintf_l _cprintf_l +#define _tcprintf_s _cprintf_s +#define _tcprintf_s_l _cprintf_s_l +#define _tcprintf_p _cprintf_p +#define _tcprintf_p_l _cprintf_p_l +#define _vtcprintf _vcprintf +#define _vtcprintf_l _vcprintf_l +#define _vtcprintf_s _vcprintf_s +#define _vtcprintf_s_l _vcprintf_s_l +#define _vtcprintf_p _vcprintf_p +#define _vtcprintf_p_l _vcprintf_p_l +#define _ftprintf fprintf +#define _ftprintf_l _fprintf_l +#define _ftprintf_s fprintf_s +#define _ftprintf_s_l _fprintf_s_l +#define _ftprintf_p _fprintf_p +#define _ftprintf_p_l _fprintf_p_l +#define _stprintf sprintf +#define _stprintf_l _sprintf_l +#define _stprintf_s sprintf_s +#define _stprintf_s_l _sprintf_s_l +#define _stprintf_p _sprintf_p +#define _stprintf_p_l _sprintf_p_l +#define _sctprintf _scprintf +#define _sctprintf_l _scprintf_l +#define _sctprintf_p _scprintf_p +#define _sctprintf_p_l _scprintf_p_l +#define _sntprintf _snprintf +#define _sntprintf_l _snprintf_l +#define _sntprintf_s _snprintf_s +#define _sntprintf_s_l _snprintf_s_l +#define _vtprintf vprintf +#define _vtprintf_l _vprintf_l +#define _vtprintf_s vprintf_s +#define _vtprintf_s_l _vprintf_s_l +#define _vtprintf_p _vprintf_p +#define _vtprintf_p_l _vprintf_p_l +#define _vftprintf vfprintf +#define _vftprintf_l _vfprintf_l +#define _vftprintf_s vfprintf_s +#define _vftprintf_s_l _vfprintf_s_l +#define _vftprintf_p _vfprintf_p +#define _vftprintf_p_l _vfprintf_p_l +#define _vstprintf vsprintf +#define _vstprintf_l _vsprintf_l +#define _vstprintf_s vsprintf_s +#define _vstprintf_s_l _vsprintf_s_l +#define _vstprintf_p _vsprintf_p +#define _vstprintf_p_l _vsprintf_p_l +#define _vsctprintf _vscprintf +#define _vsctprintf_l _vscprintf_l +#define _vsctprintf_p _vscprintf_p +#define _vsctprintf_p_l _vscprintf_p_l +#define _vsntprintf _vsnprintf +#define _vsntprintf_l _vsnprintf_l +#define _vsntprintf_s _vsnprintf_s +#define _vsntprintf_s_l _vsnprintf_s_l + +#define _tscanf scanf +#define _tscanf_l _scanf_l +#define _tscanf_s scanf_s +#define _tscanf_s_l _scanf_s_l +#define _tcscanf _cscanf +#define _tcscanf_l _cscanf_l +#define _tcscanf_s _cscanf_s +#define _tcscanf_s_l _cscanf_s_l +#define _ftscanf fscanf +#define _ftscanf_l _fscanf_l +#define _ftscanf_s fscanf_s +#define _ftscanf_s_l _fscanf_s_l +#define _stscanf sscanf +#define _stscanf_l _sscanf_l +#define _stscanf_s sscanf_s +#define _stscanf_s_l _sscanf_s_l +#define _sntscanf _snscanf +#define _sntscanf_l _snscanf_l +#define _sntscanf_s _snscanf_s +#define _sntscanf_s_l _snscanf_s_l +#define _vtscanf vscanf +#define _vtscanf_s vscanf_s +#define _vftscanf vfscanf +#define _vftscanf_s vfscanf_s +#define _vstscanf vsscanf +#define _vstscanf_s vsscanf_s + + +/* Unformatted i/o */ + +#define _fgettc fgetc +#define _fgettc_nolock _fgetc_nolock +#define _fgettchar _fgetchar +#define _fgetts fgets +#define _fputtc fputc +#define _fputtc_nolock _fputc_nolock +#define _fputtchar _fputchar +#define _fputts fputs +#define _cputts _cputs +#define _gettc getc +#define _gettc_nolock _getc_nolock +#define _gettch _getch +#define _gettch_nolock _getch_nolock +#define _gettche _getche +#define _gettche_nolock _getche_nolock +#define _gettchar getchar +#define _gettchar_nolock _getchar_nolock +#define _getts gets +#define _getts_s gets_s +#define _cgetts _cgets +#define _cgetts_s _cgets_s +#define _puttc putc +#define _puttc_nolock _putc_nolock +#define _puttchar putchar +#define _puttchar_nolock _putchar_nolock +#define _puttch _putch +#define _puttch_nolock _putch_nolock +#define _putts puts +#define _ungettc ungetc +#define _ungettc_nolock _ungetc_nolock +#define _ungettch _ungetch +#define _ungettch_nolock _ungetch_nolock + +/* String conversion functions */ + +#define _tcstod strtod +#define _tcstof strtof +#define _tcstol strtol +#define _tcstold strtold +#define _tcstoll strtoll +#define _tcstoul strtoul +#define _tcstoull strtoull +#define _tcstoimax strtoimax +#define _tcstoumax strtoumax +#define _ttof atof +#define _tstof atof +#define _tstol atol +#define _tstoll atoll +#define _tstoi atoi +#define _tstoi64 _atoi64 +#define _tcstod_l _strtod_l +#define _tcstof_l _strtof_l +#define _tcstol_l _strtol_l +#define _tcstold_l _strtold_l +#define _tcstoll_l _strtoll_l +#define _tcstoul_l _strtoul_l +#define _tcstoull_l _strtoull_l +#define _tcstoimax_l _strtoimax_l +#define _tcstoumax_l _strtoumax_l +#define _tstof_l _atof_l +#define _tstol_l _atol_l +#define _tstoll_l _atoll_l +#define _tstoi_l _atoi_l +#define _tstoi64_l _atoi64_l + +#define _itot_s _itoa_s +#define _ltot_s _ltoa_s +#define _ultot_s _ultoa_s +#define _itot _itoa +#define _ltot _ltoa +#define _ultot _ultoa +#define _ttoi atoi +#define _ttol atol +#define _ttoll atoll + +#define _ttoi64 _atoi64 +#define _tcstoi64 _strtoi64 +#define _tcstoi64_l _strtoi64_l +#define _tcstoui64 _strtoui64 +#define _tcstoui64_l _strtoui64_l +#define _i64tot_s _i64toa_s +#define _ui64tot_s _ui64toa_s +#define _i64tot _i64toa +#define _ui64tot _ui64toa + +/* String functions */ + +/* Note that _mbscat, _mbscpy and _mbsdup are functionally equivalent to + strcat, strcpy and strdup, respectively. */ + +#define _tcscat strcat +#define _tcscat_s strcat_s +#define _tcscpy strcpy +#define _tcscpy_s strcpy_s +#define _tcsdup _strdup +#define _tcslen strlen +#define _tcsnlen strnlen +#define _tcsxfrm strxfrm +#define _tcsxfrm_l _strxfrm_l +#define _tcserror strerror +#define _tcserror_s strerror_s +#define __tcserror _strerror +#define __tcserror_s _strerror_s + +#ifdef _DEBUG +#define _tcsdup_dbg _strdup_dbg +#endif /* _DEBUG */ + +/* Execute functions */ + +#define _texecl _execl +#define _texecle _execle +#define _texeclp _execlp +#define _texeclpe _execlpe +#define _texecv _execv +#define _texecve _execve +#define _texecvp _execvp +#define _texecvpe _execvpe + +#define _tspawnl _spawnl +#define _tspawnle _spawnle +#define _tspawnlp _spawnlp +#define _tspawnlpe _spawnlpe +#define _tspawnv _spawnv +#define _tspawnve _spawnve +#define _tspawnvp _spawnvp +#define _tspawnvpe _spawnvpe + +#define _tsystem system + + +/* Time functions */ + +#define _tasctime asctime +#define _tctime ctime +#define _tctime32 _ctime32 +#define _tctime64 _ctime64 +#define _tstrdate _strdate +#define _tstrtime _strtime +#define _tutime _utime +#define _tutime32 _utime32 +#define _tutime64 _utime64 +#define _tcsftime strftime +#define _tcsftime_l _strftime_l + +#define _tasctime_s asctime_s +#define _tctime_s ctime_s +#define _tctime32_s _ctime32_s +#define _tctime64_s _ctime64_s +#define _tstrdate_s _strdate_s +#define _tstrtime_s _strtime_s + +/* Directory functions */ + +#define _tchdir _chdir +#define _tgetcwd _getcwd +#define _tgetdcwd _getdcwd +#define _tgetdcwd_nolock _getdcwd_nolock +#define _tmkdir _mkdir +#define _trmdir _rmdir + +#ifdef _DEBUG +#define _tgetcwd_dbg _getcwd_dbg +#define _tgetdcwd_dbg _getdcwd_dbg +#define _tgetdcwd_lk_dbg _getdcwd_lk_dbg +#endif /* _DEBUG */ + +/* Environment/Path functions */ + +#define _tfullpath _fullpath +#define _tgetenv getenv +#define _tgetenv_s getenv_s +#define _tdupenv_s _dupenv_s +#define _tmakepath _makepath +#define _tmakepath_s _makepath_s +#define _tpgmptr _pgmptr +#define _get_tpgmptr _get_pgmptr +#define _tputenv _putenv +#define _tputenv_s _putenv_s +#define _tsearchenv _searchenv +#define _tsearchenv_s _searchenv_s +#define _tsplitpath _splitpath +#define _tsplitpath_s _splitpath_s + +#ifdef _DEBUG +#define _tfullpath_dbg _fullpath_dbg +#define _tdupenv_s_dbg _dupenv_s_dbg +#endif /* _DEBUG */ + +/* Stdio functions */ + +#define _tfdopen _fdopen +#define _tfsopen _fsopen +#define _tfopen fopen +#define _tfopen_s fopen_s +#define _tfreopen freopen +#define _tfreopen_s freopen_s +#define _tperror perror +#define _tpopen _popen +#define _ttempnam _tempnam +#define _ttmpnam tmpnam +#define _ttmpnam_s tmpnam_s + +#ifdef _DEBUG +#define _ttempnam_dbg _tempnam_dbg +#endif /* _DEBUG */ + + +/* Io functions */ + +#define _tchmod _chmod +#define _tcreat _creat +#define _tfindfirst _findfirst +#define _tfindfirst32 _findfirst32 +#define _tfindfirst64 _findfirst64 +#define _tfindfirsti64 _findfirsti64 +#define _tfindfirst32i64 _findfirst32i64 +#define _tfindfirst64i32 _findfirst64i32 +#define _tfindnext _findnext +#define _tfindnext32 _findnext32 +#define _tfindnext64 _findnext64 +#define _tfindnexti64 _findnexti64 +#define _tfindnext32i64 _findnext32i64 +#define _tfindnext64i32 _findnext64i32 +#define _tmktemp _mktemp +#define _tmktemp_s _mktemp_s + +#define _topen _open +#define _taccess _access +#define _taccess_s _access_s + +#define _tremove remove +#define _trename rename +#define _tsopen _sopen +#define _tsopen_s _sopen_s +#define _tunlink _unlink + +#define _tfinddata_t _finddata_t +#define _tfinddata32_t _finddata32_t +#define _tfinddata64_t __finddata64_t +#define _tfinddatai64_t _finddatai64_t +#define _tfinddata32i64_t _finddata32i64_t +#define _tfinddata64i32_t _finddata64i32_t + +/* ctype functions */ +#define _istascii __isascii +#define _istcntrl iscntrl +#define _istcntrl_l _iscntrl_l +#define _istxdigit isxdigit +#define _istxdigit_l _isxdigit_l + +/* Stat functions */ +#define _tstat _stat +#define _tstat32 _stat32 +#define _tstat32i64 _stat32i64 +#define _tstat64 _stat64 +#define _tstat64i32 _stat64i32 +#define _tstati64 _stati64 + + +/* Setlocale functions */ + +#define _tsetlocale setlocale + + +#ifdef _MBCS + +#ifndef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +#error Multibyte Character Set (MBCS) not supported for the current WINAPI_FAMILY. +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +/* ++++++++++++++++++++ MBCS ++++++++++++++++++++ */ + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif /* __cplusplus */ + +#include <mbstring.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#ifndef __TCHAR_DEFINED +typedef char _TCHAR; +typedef signed char _TSCHAR; +typedef unsigned char _TUCHAR; +typedef unsigned char _TXCHAR; +typedef unsigned int _TINT; +#define __TCHAR_DEFINED +#endif /* __TCHAR_DEFINED */ + +#ifndef _TCHAR_DEFINED +#if !__STDC__ +typedef char TCHAR; +#endif /* !__STDC__ */ +#define _TCHAR_DEFINED +#endif /* _TCHAR_DEFINED */ + + +#ifdef _MB_MAP_DIRECT + +/* use mb functions directly - types must match */ + +/* String functions */ + +#define _tcschr _mbschr +#define _tcscspn _mbscspn +#define _tcsncat _mbsnbcat +#define _tcsncat_s _mbsnbcat_s +#define _tcsncat_l _mbsnbcat_l +#define _tcsncat_s_l _mbsnbcat_s_l +#define _tcsncpy _mbsnbcpy +#define _tcsncpy_s _mbsnbcpy_s +#define _tcsncpy_l _mbsnbcpy_l +#define _tcsncpy_s_l _mbsnbcpy_s_l +#define _tcspbrk _mbspbrk +#define _tcsrchr _mbsrchr +#define _tcsspn _mbsspn +#define _tcsstr _mbsstr +#define _tcstok _mbstok +#define _tcstok_s _mbstok_s +#define _tcstok_l _mbstok_l +#define _tcstok_s_l _mbstok_s_l + +#define _tcsnset _mbsnbset +#define _tcsnset_l _mbsnbset_l +#define _tcsnset_s _mbsnbset_s +#define _tcsnset_s_l _mbsnbset_s_l +#define _tcsrev _mbsrev +#define _tcsset _mbsset +#define _tcsset_s _mbsset_s +#define _tcsset_l _mbsset_l +#define _tcsset_s_l _mbsset_s_l + +#define _tcscmp _mbscmp +#define _tcsicmp _mbsicmp +#define _tcsicmp_l _mbsicmp_l +#define _tcsnccmp _mbsncmp +#define _tcsncmp _mbsnbcmp +#define _tcsncicmp _mbsnicmp +#define _tcsncicmp_l _mbsnicmp_l +#define _tcsnicmp _mbsnbicmp +#define _tcsnicmp_l _mbsnbicmp_l + +#define _tcscoll _mbscoll +#define _tcscoll_l _mbscoll_l +#define _tcsicoll _mbsicoll +#define _tcsicoll_l _mbsicoll_l +#define _tcsnccoll _mbsncoll +#define _tcsnccoll_l _mbsncoll_l +#define _tcsncoll _mbsnbcoll +#define _tcsncoll_l _mbsnbcoll_l +#define _tcsncicoll _mbsnicoll +#define _tcsncicoll_l _mbsnicoll_l +#define _tcsnicoll _mbsnbicoll +#define _tcsnicoll_l _mbsnbicoll_l + +/* "logical-character" mappings */ + +#define _tcsclen _mbslen +#define _tcscnlen _mbsnlen +#define _tcsclen_l _mbslen_l +#define _tcscnlen_l _mbsnlen_l +#define _tcsnccat _mbsncat +#define _tcsnccat_s _mbsncat_s +#define _tcsnccat_l _mbsncat_l +#define _tcsnccat_s_l _mbsncat_s_l +#define _tcsnccpy _mbsncpy +#define _tcsnccpy_s _mbsncpy_s +#define _tcsnccpy_l _mbsncpy_l +#define _tcsnccpy_s_l _mbsncpy_s_l +#define _tcsncset _mbsnset +#define _tcsncset_s _mbsnset_s +#define _tcsncset_l _mbsnset_l +#define _tcsncset_s_l _mbsnset_s_l + +/* MBCS-specific mappings */ + +#define _tcsdec _mbsdec +#define _tcsinc _mbsinc +#define _tcsnbcnt _mbsnbcnt +#define _tcsnccnt _mbsnccnt +#define _tcsnextc _mbsnextc +#define _tcsninc _mbsninc +#define _tcsspnp _mbsspnp + +#define _tcslwr _mbslwr +#define _tcslwr_l _mbslwr_l +#define _tcslwr_s _mbslwr_s +#define _tcslwr_s_l _mbslwr_s_l +#define _tcsupr _mbsupr +#define _tcsupr_l _mbsupr_l +#define _tcsupr_s _mbsupr_s +#define _tcsupr_s_l _mbsupr_s_l + +#define _tclen _mbclen +#define _tccpy _mbccpy +#define _tccpy_l _mbccpy_l +#define _tccpy_s _mbccpy_s +#define _tccpy_s_l _mbccpy_s_l + +#else /* _MB_MAP_DIRECT */ + +#if __STDC__ || defined (_NO_INLINING) + +/* use type-safe linked-in function thunks */ + +/* String functions */ + +_Check_return_ _CRTIMP _CONST_RETURN char * __cdecl _tcschr(_In_z_ const char * _Str, _In_ unsigned int _Val); +_Check_return_ _CRTIMP size_t __cdecl _tcscspn(_In_z_ const char * _Str, _In_z_ const char * _Control); +_CRT_INSECURE_DEPRECATE(_tcsncat_s) _CRTIMP char * __cdecl _tcsncat(_Inout_updates_z_(_MaxCount) char *_Dst, _In_z_ const char *_Src, _In_ size_t _MaxCount); +_CRTIMP char * __cdecl _tcsncat_s(_Inout_updates_z_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, size_t _MaxCount); +_CRT_INSECURE_DEPRECATE(_tcsncat_s_l) _CRTIMP char * __cdecl _tcsncat_l(_Inout_updates_z_(_MaxCount) char *_Dst, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcsncat_s_l(_Inout_updates_z_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRT_INSECURE_DEPRECATE(_tcsncpy_s) _CRTIMP char * __cdecl _tcsncpy(_Out_writes_(_MaxCount) _Post_maybez_ char *_Dst, _In_z_ const char *_Src, size_t _MaxCount); +_CRTIMP char * __cdecl _tcsncpy_s(_Out_writes_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, size_t _MaxCount); +_CRT_INSECURE_DEPRECATE(_tcsncpy_s_l) _CRTIMP char * __cdecl _tcsncpy_l(_Out_writes_(_MaxCount) _Post_maybez_ char *_Dst, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcsncpy_s_l(_Out_writes_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP _CONST_RETURN char * __cdecl _tcspbrk(_In_z_ const char * _Str, _In_z_ const char * _Control); +_Check_return_ _CRTIMP _CONST_RETURN char * __cdecl _tcsrchr(_In_z_ const char * _Str, _In_ unsigned int _Ch); +_Check_return_ _CRTIMP size_t __cdecl _tcsspn(_In_z_ const char * _Str, _In_z_ const char * _Control); +_Check_return_ _CRTIMP _CONST_RETURN char * __cdecl _tcsstr(_In_z_ const char * _Str, _In_z_ const char * _Substr); +_Check_return_ _CRT_INSECURE_DEPRECATE(_tcstok_s) _CRTIMP char * __cdecl _tcstok(_Inout_opt_ char *_Str, _In_z_ const char *_Delim); +_Check_return_ _CRTIMP char * __cdecl _tcstok_s(_Inout_opt_ char *_Str, _In_z_ const char *_Delim, _Inout_ _Deref_prepost_opt_z_ char **_Context); +_Check_return_ _CRT_INSECURE_DEPRECATE(_tcstok_s_l) _CRTIMP char * __cdecl _tcstok_l(_Inout_opt_ char *_Str, _In_z_ const char *_Delim, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP char * __cdecl _tcstok_s_l(_Inout_opt_ char *_Str, _In_z_ const char *_Delim, _Inout_ _Deref_prepost_opt_z_ char **_Context, _In_opt_ _locale_t _Locale); + +_CRT_INSECURE_DEPRECATE(_tcsnset_s) _CRTIMP char * __cdecl _tcsnset(_Inout_z_ char * _Str, _In_ unsigned int _Val, _In_ size_t _MaxCount); +_Check_return_wat_ _CRTIMP errno_t __cdecl _tcsnset_s(_Inout_updates_z_(_SizeInChars) char * _Str, _In_ size_t _SizeInChars, _In_ unsigned int _Val , _In_ size_t _MaxCount); +_CRT_INSECURE_DEPRECATE(_tcsnset_s_l) _CRTIMP char * __cdecl _tcsnset_l(_Inout_z_ char * _Str, _In_ unsigned int _Val, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_wat_ _CRTIMP errno_t __cdecl _tcsnset_s_l(_Inout_updates_z_(_SizeInChars) char * _Str, _In_ size_t _SizeInChars, _In_ unsigned int _Val , _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcsrev(_Inout_z_ char * _Str); +_CRT_INSECURE_DEPRECATE(_tcsset_s) _CRTIMP char * __cdecl _tcsset(_Inout_z_ char * _Str, _In_ unsigned int _Val); +_CRT_INSECURE_DEPRECATE(_tcsset_s_l) _CRTIMP char * __cdecl _tcsset_l(_Inout_z_ char * _Str, _In_ unsigned int _Val, _In_opt_ _locale_t _Locale); +_Check_return_wat_ _CRTIMP errno_t __cdecl _tcsset_s(_Inout_updates_z_(_SizeInChars) char * _Str, _In_ size_t _SizeInChars, _In_ unsigned int _Val); +_Check_return_wat_ _CRTIMP errno_t __cdecl _tcsset_s_l(_Inout_updates_z_(_SizeInChars) char * _Str, _In_ size_t _SizeInChars, _In_ unsigned int, _In_opt_ _locale_t _Locale); + +_Check_return_ _CRTIMP int __cdecl _tcscmp(_In_z_ const char *_Str1, _In_z_ const char * _Str); +_Check_return_ _CRTIMP int __cdecl _tcsicmp(_In_z_ const char *_Str1, _In_z_ const char *_Str2); +_Check_return_ _CRTIMP int __cdecl _tcsicmp_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _tcsnccmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsncmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsncicmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsncicmp_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _tcsnicmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsnicmp_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, size_t _MaxCount, _In_opt_ _locale_t _Locale); + +_Check_return_ _CRTIMP int __cdecl _tcscoll(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ _CRTIMP int __cdecl _tcscoll_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _tcsicoll(_In_z_ const char * _Str1, _In_z_ const char * _Str2); +_Check_return_ _CRTIMP int __cdecl _tcsicoll_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _tcsnccoll(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsnccoll_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _tcsncoll(_In_z_ const char *_Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsncoll_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _tcsncicoll(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsncicoll_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _tcsnicoll(_In_z_ const char * _Str1, _In_z_ const char * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _tcsnicoll_l(_In_z_ const char *_Str1, _In_z_ const char *_Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); + +/* "logical-character" mappings */ + +_Check_return_ _CRTIMP size_t __cdecl _tcsclen(_In_z_ const char *_Str); +_Check_return_ _CRTIMP size_t __cdecl _tcscnlen(_In_z_ const char *_Str, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP size_t __cdecl _tcsclen_l(_In_z_ const char *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP size_t __cdecl _tcscnlen_l(_In_z_ const char *_Str, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRT_INSECURE_DEPRECATE(_tcsnccat_s) _CRTIMP char * __cdecl _tcsnccat(_Inout_ char *_Dst, _In_z_ const char *_Src, _In_ size_t _MaxCount); +_CRTIMP char * __cdecl _tcsnccat_s(_Inout_updates_z_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, _In_ size_t _MaxCount); +_CRT_INSECURE_DEPRECATE(_tcsnccat_s_l) _CRTIMP char * __cdecl _tcsnccat_l(_Inout_ char *_Dst, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcsnccat_s_l(_Inout_updates_z_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRT_INSECURE_DEPRECATE(_tcsnccpy_s) _CRTIMP char * __cdecl _tcsnccpy(_Out_writes_(_MaxCount) _Post_maybez_ char *_Dst, _In_z_ const char *_Src, _In_ size_t _MaxCount); +_CRTIMP char * __cdecl _tcsnccpy_s(_Out_writes_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, _In_ size_t _MaxCount); +_CRT_INSECURE_DEPRECATE(_tcsnccpy_s_l) _CRTIMP char * __cdecl _tcsnccpy_l(_Out_writes_(_MaxCount) _Post_maybez_ char *_Dst, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcsnccpy_s_l(_Out_writes_(_DstSizeInChars) char *_Dst, _In_ size_t _DstSizeInChars, _In_z_ const char *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRT_INSECURE_DEPRECATE(_tcsncset_s) _CRTIMP char * __cdecl _tcsncset(_Inout_updates_z_(_MaxCount) char * _Str, _In_ unsigned int _Val, _In_ size_t _MaxCount); +_CRTIMP char * __cdecl _tcsncset_s(_Inout_updates_z_(_SizeInChars) char * _Str, _In_ size_t _SizeInChars, _In_ unsigned int _Val, _In_ size_t _MaxCount); +_CRT_INSECURE_DEPRECATE(_tcsncset_s_l) _CRTIMP char * __cdecl _tcsncset_l(_Inout_updates_z_(_MaxCount) char * _Str, _In_ unsigned int _Val, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcsncset_s_l(_Inout_updates_z_(_SizeInChars) char * _Str, _In_ size_t _SizeInChars, _In_ unsigned int _Val, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); + +/* MBCS-specific mappings */ + +_CRTIMP char * __cdecl _tcsdec(_In_reads_z_(_Pos-_Start+1) const char * _Start, _In_z_ const char * _Pos); +_CRTIMP char * __cdecl _tcsinc(_In_z_ const char * _Ptr); +_CRTIMP size_t __cdecl _tcsnbcnt(_In_reads_or_z_(_MaxCount) const char * _Str, _In_ size_t _MaxCount); +_CRTIMP size_t __cdecl _tcsnccnt(_In_reads_or_z_(_MaxCount) const char * _Str, _In_ size_t _MaxCount); +_CRTIMP unsigned int __cdecl _tcsnextc (_In_z_ const char * _Str); +_CRTIMP char * __cdecl _tcsninc(_In_reads_or_z_(_Count) const char * _Ptr, _In_ size_t _Count); +_CRTIMP char * __cdecl _tcsspnp(_In_z_ const char * _Str1, _In_z_ const char * _Str2); + +_CRT_INSECURE_DEPRECATE(_tcslwr_s) _CRTIMP char * __cdecl _tcslwr(_Inout_ char *_Str); +_CRT_INSECURE_DEPRECATE(_tcslwr_s_l) _CRTIMP char * __cdecl _tcslwr_l(_Inout_ char *_Str, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcslwr_s(_Inout_updates_z_(_SizeInChars) char *_Str, _In_ size_t _SizeInChars); +_CRTIMP char * __cdecl _tcslwr_s_l(_Inout_updates_z_(_SizeInChars) char *_Str, _In_ size_t _SizeInChars, _In_opt_ _locale_t _Locale); +_CRT_INSECURE_DEPRECATE(_tcsupr_s) _CRTIMP char * __cdecl _tcsupr(_Inout_ char *_Str); +_CRT_INSECURE_DEPRECATE(_tcsupr_s_l) _CRTIMP char * __cdecl _tcsupr_l(_Inout_ char *_Str, _In_opt_ _locale_t _Locale); +_CRTIMP char * __cdecl _tcsupr_s(_Inout_updates_z_(_SizeInChars) char *_Str, _In_ size_t _SizeInChars); +_CRTIMP char * __cdecl _tcsupr_s_l(_Inout_updates_z_(_SizeInChars) char *_Str, _In_ size_t _SizeInChars, _In_opt_ _locale_t _Locale); + +_Check_return_ _CRTIMP size_t __cdecl _tclen(_In_z_ const char * _Str); +_CRT_INSECURE_DEPRECATE(_tccpy_s) _CRTIMP void __cdecl _tccpy(_Pre_notnull_ _Post_z_ char * _DstCh, _In_z_ const char * _SrcCh); +_CRT_INSECURE_DEPRECATE(_tccpy_s_l) _CRTIMP void __cdecl _tccpy_l(_Pre_notnull_ _Post_z_ char * _DstCh, _In_z_ const char * _SrcCh, _In_opt_ _locale_t _Locale); +_Check_return_wat_ _CRTIMP errno_t __cdecl _tccpy_s(_Out_writes_z_(_SizeInBytes) char * _DstCh, size_t _SizeInBytes, _Out_opt_ int *_PCopied, _In_z_ const char * _SrcCh); +_Check_return_wat_ _CRTIMP errno_t __cdecl _tccpy_s_l(_Out_writes_z_(_SizeInBytes) char * _DstCh, size_t _SizeInBytes, _Out_opt_ int *_PCopied, _In_z_ const char * _SrcCh, _In_opt_ _locale_t _Locale); + +#else /* __STDC__ || defined (_NO_INLINING) */ + +/* the default: use type-safe inline function thunks */ + +#define _PUC unsigned char * +#define _CPUC const unsigned char * +#define _PC char * +#define _CRPC _CONST_RETURN char * +#define _CPC const char * +#define _UI unsigned int + + +/* String functions */ + +__inline _CRPC _tcschr(_In_ _CPC _s1,_In_ _UI _c) {return (_CRPC)_mbschr((_CPUC)_s1,_c);} +__inline size_t _tcscspn(_In_ _CPC _s1,_In_ _CPC _s2) {return _mbscspn((_CPUC)_s1,(_CPUC)_s2);} + +_Check_return_wat_ __inline errno_t _tcsncat_s(_Inout_updates_z_(_Destination_size_chars) char *_Destination, _In_ size_t _Destination_size_chars, _In_z_ const char *_Source, _In_ size_t _Count) +{ + return _mbsnbcat_s((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source,_Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tcsncat_s, _Prepost_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsncat, _tcsncat_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnbcat((unsigned char *)_Dst,(const unsigned char *)_Source,_Count); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsncat, _tcsncat_s, _Inout_updates_z_(_Size) char, _Inout_z_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) + +_Check_return_wat_ __inline errno_t _tcsncat_s_l(_Inout_updates_z_(_Destination_size_chars) char *_Destination, _In_ size_t _Destination_size_chars, _In_z_ const char *_Source, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsnbcat_s_l((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source,_Count, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _tcsncat_s_l, _Prepost_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsncat_l, _tcsncat_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnbcat_l((unsigned char *)_Dst,(const unsigned char *)_Source,_Count, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsncat_l, _tcsncat_s_l, _Inout_updates_z_(_Size) char, _Inout_z_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +_Check_return_wat_ __inline errno_t _tcsncpy_s(_Out_writes_z_(_Destination_size_chars) char * _Destination, _In_ size_t _Destination_size_chars, _In_z_ const char * _Source,_In_ size_t _Count) +{ + return _mbsnbcpy_s((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source,_Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tcsncpy_s, _Post_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsncpy, _tcsncpy_s, _Out_writes_bytes_(_Size) _Post_maybez_ char, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnbcpy((unsigned char *)_Dst,(const unsigned char *)_Source,_Count); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsncpy, _tcsncpy_s, _Out_writes_z_(_Size) char, _Inout_z_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) + +_Check_return_wat_ __inline errno_t _tcsncpy_s_l(_Out_writes_z_(_Destination_size_chars) char * _Destination, _In_ size_t _Destination_size_chars, _In_z_ const char * _Source,_In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsnbcpy_s_l((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source,_Count, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _tcsncpy_s_l, _Post_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsncpy_l, _tcsncpy_s_l, _Out_writes_z_(_Size) char, _Out_writes_bytes_(_Count) _Post_maybez_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnbcpy_l((unsigned char *)_Dst,(const unsigned char *)_Source,_Count, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsncpy_l, _tcsncpy_s_l, _Out_writes_z_(_Size) char, _Out_writes_z_(_Count), char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +_Check_return_ __inline _CRPC _tcspbrk(_In_ _CPC _s1,_In_ _CPC _s2) {return (_CRPC)_mbspbrk((_CPUC)_s1,(_CPUC)_s2);} +_Check_return_ __inline _CRPC _tcsrchr(_In_ _CPC _s1,_In_ _UI _c) {return (_CRPC)_mbsrchr((_CPUC)_s1,_c);} +_Check_return_ __inline size_t _tcsspn(_In_ _CPC _s1,_In_ _CPC _s2) {return _mbsspn((_CPUC)_s1,(_CPUC)_s2);} +_Check_return_ __inline _CRPC _tcsstr(_In_ _CPC _s1,_In_ _CPC _s2) {return (_CRPC)_mbsstr((_CPUC)_s1,(_CPUC)_s2);} + +_Check_return_ _CRT_INSECURE_DEPRECATE(_tcstok_s) __inline char * _tcstok(_Inout_opt_z_ char * _String,_In_z_ const char * _Delimiters) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char * )_mbstok((unsigned char *)_String,(const unsigned char *)_Delimiters); +#pragma warning(pop) +} + +_Check_return_ __inline char * _tcstok_s(_Inout_opt_z_ char * _String,_In_z_ const char * _Delimiters, _Inout_ _Deref_prepost_opt_z_ char **_Current_position) +{ + return (char * )_mbstok_s((unsigned char *)_String,(const unsigned char *)_Delimiters, (unsigned char **)_Current_position); +} + +_Check_return_ _CRT_INSECURE_DEPRECATE(_tcstok_s_l) __inline char * _tcstok_l(_Inout_opt_z_ char * _String,_In_z_ const char * _Delimiters, _In_opt_ _locale_t _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char * )_mbstok_l((unsigned char *)_String,(const unsigned char *)_Delimiters, _Locale); +#pragma warning(pop) +} + +_Check_return_ __inline char * _tcstok_s_l(_Inout_opt_z_ char * _String,_In_z_ const char * _Delimiters, _Inout_ _Deref_prepost_opt_z_ char **_Current_position, _In_opt_ _locale_t _Locale) +{ + return (char * )_mbstok_s_l((unsigned char *)_String,(const unsigned char *)_Delimiters, (unsigned char **)_Current_position, _Locale); +} + +_Check_return_wat_ __inline errno_t _tcsnset_s(_Inout_updates_z_(_SizeInBytes) char * _Dst, _In_ size_t _SizeInBytes, _In_ unsigned int _Value , _In_ size_t _Count) +{ + return _mbsnbset_s((unsigned char *)_Dst, _SizeInBytes, _Value, _Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tcsnset_s, _Prepost_z_ char, _Dest, _In_ unsigned int, _Value , _In_ size_t, _Count) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsnset, _tcsnset_s, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_Count) char, _Dst, _In_ unsigned int, _Value , _In_ size_t, _Count) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnbset((unsigned char *)_Dst, _Value, _Count); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsnset, _tcsnset_s, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_Count), char, _Dst, _In_ unsigned int, _Value , _In_ size_t, _Count) + +_Check_return_wat_ __inline errno_t _tcsnset_s_l(_Inout_updates_z_(_SizeInBytes) char * _Dst, _In_ size_t _SizeInBytes, _In_ unsigned int _Value , _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsnbset_s_l((unsigned char *)_Dst, _SizeInBytes, _Value, _Count, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _tcsnset_s_l, _Prepost_z_ char, _Dest, _In_ unsigned int, _Value , _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsnset_l, _tcsnset_s_l, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_Count) char, _Dst, _In_ unsigned int, _Value , _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnbset_l((unsigned char *)_Dst, _Value, _Count, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsnset_l, _tcsnset_s_l, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_Count), char, _Dst, _In_ unsigned int, _Value , _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__inline _PC _tcsrev(_Inout_ _PC _s1) {return (_PC)_mbsrev((_PUC)_s1);} + +_Check_return_wat_ __inline errno_t _tcsset_s(_Inout_updates_z_(_SizeInBytes) char * _Dst, _In_ size_t _SizeInBytes, _In_ unsigned int _Value) +{ + return _mbsset_s((unsigned char *)_Dst, _SizeInBytes, _Value); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _tcsset_s, _Prepost_z_ char, _Dest, _In_ unsigned int, _Value) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _tcsset, _tcsset_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_ unsigned int, _Value) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsset((unsigned char *)_Dst, _Value); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _tcsset, _tcsset_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_ unsigned int, _Value) + +_Check_return_wat_ __inline errno_t _tcsset_s_l(_Inout_updates_z_(_SizeInBytes) char * _Dst, _In_ size_t _SizeInBytes, _In_ unsigned int _Value, _In_opt_ _locale_t _Locale) +{ + return _mbsset_s_l((unsigned char *)_Dst, _SizeInBytes, _Value, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tcsset_s_l, _Prepost_z_ char, _Dest, _In_ unsigned int, _Value, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsset_l, _tcsset_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_ unsigned int, _Value, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsset_l((unsigned char *)_Dst, _Value, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsset_l, _tcsset_s_l, _Inout_updates_z_(_Size) char, _Inout_z_, char, _Dst, _In_ unsigned int, _Value, _In_opt_ _locale_t, _Locale) + +_Check_return_ __inline int _tcscmp(_In_ _CPC _s1,_In_ _CPC _s2) {return _mbscmp((_CPUC)_s1,(_CPUC)_s2);} + +_Check_return_ __inline int _tcsicmp(_In_z_ const char * _String1, _In_z_ const char * _String2) +{ + return _mbsicmp((const unsigned char *)_String1,(const unsigned char *)_String2); +} + +_Check_return_ __inline int _tcsicmp_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_opt_ _locale_t _Locale) +{ + return _mbsicmp_l((const unsigned char *)_String1,(const unsigned char *)_String2, _Locale); +} + +_Check_return_ __inline int _tcsnccmp(_In_ _CPC _s1,_In_ _CPC _s2,_In_ size_t _n) {return _mbsncmp((_CPUC)_s1,(_CPUC)_s2,_n);} +__inline int _tcsncmp(_In_ _CPC _s1,_In_ _CPC _s2,_In_ size_t _n) {return _mbsnbcmp((_CPUC)_s1,(_CPUC)_s2,_n);} + +_Check_return_ __inline int _tcsncicmp(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Char_count) +{ + return _mbsnicmp((const unsigned char *)_String1,(const unsigned char *)_String2,_Char_count); +} + +_Check_return_ __inline int _tcsncicmp_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Char_count, _In_opt_ _locale_t _Locale) +{ + return _mbsnicmp_l((const unsigned char *)_String1,(const unsigned char *)_String2,_Char_count, _Locale); +} + +_Check_return_ __inline int _tcsnicmp(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Char_count) +{ + return _mbsnbicmp((const unsigned char *)_String1,(const unsigned char *)_String2,_Char_count); +} + +_Check_return_ __inline int _tcsnicmp_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Char_count, _In_opt_ _locale_t _Locale) +{ + return _mbsnbicmp_l((const unsigned char *)_String1,(const unsigned char *)_String2,_Char_count, _Locale); +} + +_Check_return_ __inline int _tcscoll(_In_z_ const char * _String1, _In_z_ const char * _String2) +{ + return _mbscoll((const unsigned char *)_String1,(const unsigned char *)_String2); +} + +_Check_return_ __inline int _tcscoll_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_opt_ _locale_t _Locale) +{ + return _mbscoll_l((const unsigned char *)_String1,(const unsigned char *)_String2, _Locale); +} + +_Check_return_ __inline int _tcsicoll(_In_z_ const char * _String1, _In_z_ const char * _String2) +{ + return _mbsicoll((const unsigned char *)_String1,(const unsigned char *)_String2); +} + +_Check_return_ __inline int _tcsicoll_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_opt_ _locale_t _Locale) +{ + return _mbsicoll_l((const unsigned char *)_String1,(const unsigned char *)_String2, _Locale); +} + +_Check_return_ __inline int _tcsnccoll(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count) +{ + return _mbsncoll((const unsigned char *)_String1,(const unsigned char *)_String2, _Count); +} + +_Check_return_ __inline int _tcsnccoll_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsncoll_l((const unsigned char *)_String1,(const unsigned char *)_String2, _Count, _Locale); +} + +_Check_return_ __inline int _tcsncoll(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count) +{ + return _mbsnbcoll((const unsigned char *)_String1,(const unsigned char *)_String2, _Count); +} + +_Check_return_ __inline int _tcsncoll_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsnbcoll_l((const unsigned char *)_String1,(const unsigned char *)_String2, _Count, _Locale); +} + +_Check_return_ __inline int _tcsncicoll(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count) +{ + return _mbsnicoll((const unsigned char *)_String1,(const unsigned char *)_String2, _Count); +} + +_Check_return_ __inline int _tcsncicoll_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsnicoll_l((const unsigned char *)_String1,(const unsigned char *)_String2, _Count, _Locale); +} + +_Check_return_ __inline int _tcsnicoll(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count) +{ + return _mbsnbicoll((const unsigned char *)_String1,(const unsigned char *)_String2, _Count); +} + +_Check_return_ __inline int _tcsnicoll_l(_In_z_ const char * _String1, _In_z_ const char * _String2, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsnbicoll_l((const unsigned char *)_String1,(const unsigned char *)_String2, _Count, _Locale); +} + +/* "logical-character" mappings */ +_Check_return_ __inline size_t _tcsclen(_In_z_ const char * _String) +{ + return _mbslen((const unsigned char *)_String); +} + +_Check_return_ __inline size_t _tcscnlen(_In_z_ const char * _String, _In_ size_t _Maximum) +{ + return _mbsnlen((const unsigned char *)_String, _Maximum); +} + +_Check_return_ __inline size_t _tcsclen_l(_In_z_ const char * _String, _In_opt_ _locale_t _Locale) +{ + return _mbslen_l((const unsigned char *)_String, _Locale); +} + +_Check_return_ __inline size_t _tcscnlen_l(_In_z_ const char * _String, _In_ size_t _Maximum, _In_opt_ _locale_t _Locale) +{ + return _mbsnlen_l((const unsigned char *)_String, _Maximum, _Locale); +} + +_Check_return_wat_ __inline errno_t _tcsnccat_s(_Inout_updates_z_(_Destination_size_chars) char * _Destination, _In_ size_t _Destination_size_chars, _In_z_ const char * _Source, _In_ size_t _Count) +{ + return _mbsncat_s((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source, _Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tcsnccat_s, _Prepost_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsnccat, _tcsnccat_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsncat((unsigned char *)_Dst,(const unsigned char *)_Source, _Count); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsnccat, _tcsnccat_s, _Inout_updates_z_(_Size) char, _Inout_z_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) + +_Check_return_wat_ __inline errno_t _tcsnccat_s_l(_Inout_updates_z_(_Destination_size_chars) char * _Destination, _In_ size_t _Destination_size_chars, _In_z_ const char * _Source, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsncat_s_l((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source, _Count, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _tcsnccat_s_l, _Prepost_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsnccat_l, _tcsnccat_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsncat_l((unsigned char *)_Dst,(const unsigned char *)_Source, _Count, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsnccat_l, _tcsnccat_s_l, _Inout_updates_z_(_Size) char, _Inout_z_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +_Check_return_wat_ __inline errno_t _tcsnccpy_s(_Out_writes_z_(_Destination_size_chars) char * _Destination, _In_ size_t _Destination_size_chars, _In_z_ const char * _Source, _In_ size_t _Count) +{ + return _mbsncpy_s((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source, _Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tcsnccpy_s, _Post_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsnccpy, _tcsnccpy_s, _Out_writes_bytes_(_Size) _Post_maybez_ char, _Pre_notnull_ _Post_maybez_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsncpy((unsigned char *)_Dst,(const unsigned char *)_Source, _Count); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsnccpy, _tcsnccpy_s, _Out_writes_z_(_Size) char, _Pre_notnull_ _Post_z_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count) + +_Check_return_wat_ __inline errno_t _tcsnccpy_s_l(_Out_writes_z_(_Destination_size_chars) char * _Destination, _In_ size_t _Destination_size_chars, _In_z_ const char * _Source, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsncpy_s_l((unsigned char *)_Destination, _Destination_size_chars, (const unsigned char *)_Source, _Count, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _tcsnccpy_s_l, _Post_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsnccpy_l, _tcsnccpy_s_l, _Out_writes_z_(_Size) char, _Out_writes_bytes_(_Count) _Post_maybez_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsncpy_l((unsigned char *)_Dst,(const unsigned char *)_Source, _Count, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsnccpy_l, _tcsnccpy_s_l, _Out_writes_z_(_Size) char, _Out_writes_bytes_(_Count) _Post_maybez_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +_Check_return_wat_ __inline errno_t _tcsncset_s(_Inout_updates_bytes_(_SizeInBytes) char *_Destination, _In_ size_t _SizeInBytes, _In_ unsigned int _Value, _In_ size_t _Count) +{ + return _mbsnset_s((unsigned char *)_Destination, _SizeInBytes, _Value, _Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tcsncset_s, char, _Dest, _In_ unsigned int, _Value, _In_ size_t, _Count) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsncset, _tcsncset_s, _Inout_updates_z_(_Size) char, _Inout_updates_bytes_(_Count) char, _Dst, _In_ unsigned int, _Value, _In_ size_t, _Count) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnset((unsigned char *)_Dst, _Value, _Count); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_2_EX(char *, __RETURN_POLICY_DST, _tcsncset, _tcsncset_s, _Inout_updates_z_(_Size) char, _Inout_updates_bytes_(_Count), char, _Dst, _In_ unsigned int, _Value, _In_ size_t, _Count) + +_Check_return_wat_ __inline errno_t _tcsncset_s_l(_Inout_updates_bytes_(_SizeInBytes) char *_Destination, _In_ size_t _SizeInBytes, _In_ unsigned int _Value, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsnset_s_l((unsigned char *)_Destination, _SizeInBytes, _Value, _Count, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _tcsncset_s_l, char, _Dest, _In_ unsigned int, _Value, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsncset_l, _tcsncset_s_l, _Inout_updates_z_(_Size) char, _Inout_updates_bytes_(_Count) char, _Dst, _In_ unsigned int, _Value, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsnset_l((unsigned char *)_Dst, _Value, _Count, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _tcsncset_l, _tcsncset_s_l, _Inout_updates_z_(_Size) char, _Inout_updates_bytes_(_Count), char, _Dst, _In_ unsigned int, _Value, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +/* MBCS-specific mappings */ + +_Check_return_ __inline _PC _tcsdec(_In_ _CPC _s1,_In_ _CPC _s2) {return (_PC)_mbsdec((_CPUC)_s1,(_CPUC)_s2);} +_Check_return_ __inline _PC _tcsinc(_In_ _CPC _s1) {return (_PC)_mbsinc((_CPUC)_s1);} +_Check_return_ __inline size_t _tcsnbcnt(_In_ _CPC _s1,_In_ size_t _n) {return _mbsnbcnt((_CPUC)_s1,_n);} +_Check_return_ __inline size_t _tcsnccnt(_In_ _CPC _s1,_In_ size_t _n) {return _mbsnccnt((_CPUC)_s1,_n);} +_Check_return_ __inline _PC _tcsninc(_In_ _CPC _s1,_In_ size_t _n) {return (_PC)_mbsninc((_CPUC)_s1,_n);} +_Check_return_ __inline _PC _tcsspnp(_In_ _CPC _s1,_In_ _CPC _s2) {return (_PC)_mbsspnp((_CPUC)_s1,(_CPUC)_s2);} + +_Check_return_wat_ __inline errno_t _tcslwr_s(_Inout_updates_z_(_SizeInBytes) char * _String, size_t _SizeInBytes) +{ + return _mbslwr_s((unsigned char *)_String, _SizeInBytes); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _tcslwr_s, _Prepost_z_ char, _String) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_0_EX(char *, __RETURN_POLICY_DST, _tcslwr, _tcslwr_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbslwr((unsigned char *)_String); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_0_EX(char *, __RETURN_POLICY_DST, _tcslwr, _tcslwr_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String) + +_Check_return_wat_ __inline errno_t _tcslwr_s_l(_Inout_updates_z_(_SizeInBytes) char * _String, _In_ size_t _SizeInBytes, _In_opt_ _locale_t _Locale) +{ + return _mbslwr_s_l((unsigned char *)_String, _SizeInBytes, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _tcslwr_s_l, _Prepost_z_ char, _String, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _tcslwr_l, _tcslwr_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbslwr_l((unsigned char *)_String, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _tcslwr_l, _tcslwr_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String, _In_opt_ _locale_t, _Locale) + +_Check_return_wat_ __inline errno_t _tcsupr_s(_Inout_updates_z_(_Count) char * _String, _In_ size_t _Count) +{ + return _mbsupr_s((unsigned char *)_String, _Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _tcsupr_s, _Prepost_z_ char, _String) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_0_EX(char *, __RETURN_POLICY_DST, _tcsupr, _tcsupr_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsupr((unsigned char *)_String); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_0_EX(char *, __RETURN_POLICY_DST, _tcsupr, _tcsupr_s, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String) + +_Check_return_wat_ __inline errno_t _tcsupr_s_l(_Inout_updates_z_(_Count) char * _String, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + return _mbsupr_s_l((unsigned char *)_String, _Count, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _tcsupr_s_l, _Prepost_z_ char, _String, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _tcsupr_l, _tcsupr_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String, _In_opt_ _locale_t, _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + return (char *)_mbsupr_l((unsigned char *)_String, _Locale); +#pragma warning(pop) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_1_EX(char *, __RETURN_POLICY_DST, _tcsupr_l, _tcsupr_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _String, _In_opt_ _locale_t, _Locale) + +_Check_return_ __inline size_t _tclen(_In_z_ _CPC _s1) {return _mbclen((_CPUC)_s1);} + +_Check_return_wat_ __inline errno_t _tccpy_s(_Out_writes_z_(_SizeInBytes) char * _Destination, size_t _SizeInBytes, _Out_opt_ int *_PCopied, _In_z_ const char * _Source) +{ + return _mbccpy_s((unsigned char *)_Destination, _SizeInBytes, _PCopied, (const unsigned char *)_Source); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _tccpy_s, _Post_z_ char, _Dest, _Out_opt_ int *, _PCopied, _In_z_ const char *, _Source) + +_CRT_INSECURE_DEPRECATE(_tccpy_s) __inline void _tccpy(_Out_writes_z_(2) char * _Destination, _In_z_ const char * _Source) +{ +#pragma warning(push) +#pragma warning(disable:4996) + _mbccpy((unsigned char *)_Destination, (const unsigned char *)_Source); +#pragma warning(pop) +} + +_Check_return_wat_ __inline errno_t _tccpy_s_l(_Out_writes_z_(_SizeInBytes) char * _Destination, _In_ size_t _SizeInBytes, _Out_opt_ int *_PCopied, _In_z_ const char * _Source, _In_opt_ _locale_t _Locale) +{ + return _mbccpy_s_l((unsigned char *)_Destination, _SizeInBytes, _PCopied, (const unsigned char *)_Source, _Locale); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _tccpy_s_l, _Post_z_ char, _Dest, _Out_opt_ int *, _PCopied, _In_z_ const char *, _Source, _In_opt_ _locale_t, _Locale) + +_CRT_INSECURE_DEPRECATE(_tccpy_s_l) __inline void _tccpy_l(_Out_writes_z_(2) char * _Destination, _In_z_ const char * _Source, _In_opt_ _locale_t _Locale) +{ +#pragma warning(push) +#pragma warning(disable:4996) + _mbccpy_l((unsigned char *)_Destination,( const unsigned char *)_Source, _Locale); +#pragma warning(pop) +} + +/* inline helper */ +_Check_return_ __inline _UI _tcsnextc(_In_z_ _CPC _s1) +{ + _UI _n=0; + if (_ismbblead((_UI)*(_PUC)_s1)) + { + /* for a dud MBCS string (leadbyte, EOS), we don't move forward 2 + We do not assert here because this routine is too low-level + */ + if(_s1[1]!='\0') + { + _n=((_UI)*(_PUC)_s1)<<8; + _s1++; + } + } + _n+=(_UI)*(_PUC)_s1; + + return(_n); +} + +#endif /* __STDC__ || defined (_NO_INLINING) */ + +#ifdef __cplusplus +#ifndef _CPP_TCHAR_INLINES_DEFINED +#define _CPP_TCHAR_INLINES_DEFINED +extern "C++" { +_Check_return_ inline char * __CRTDECL _tcschr(_In_z_ char *_S, _In_ unsigned int _C) + {return ((char *)_tcschr((const char *)_S, _C)); } +_Check_return_ inline char * __CRTDECL _tcspbrk(_In_z_ char *_S, _In_z_ const char *_P) + {return ((char *)_tcspbrk((const char *)_S, _P)); } +_Check_return_ inline char * __CRTDECL _tcsrchr(_In_z_ char *_S, _In_ unsigned int _C) + {return ((char *)_tcsrchr((const char *)_S, _C)); } +_Check_return_ inline char * __CRTDECL _tcsstr(_In_z_ char *_S, _In_z_ const char *_P) + {return ((char *)_tcsstr((const char *)_S, _P)); } +} +#endif /* _CPP_TCHAR_INLINES_DEFINED */ +#endif /* __cplusplus */ + +#endif /* _MB_MAP_DIRECT */ + + +/* MBCS-specific mappings */ + +#define _tccmp(_cp1,_cp2) _tcsnccmp(_cp1,_cp2,1) + + +/* ctype functions */ + +#define _istalnum _ismbcalnum +#define _istalnum_l _ismbcalnum_l +#define _istalpha _ismbcalpha +#define _istalpha_l _ismbcalpha_l +#define _istdigit _ismbcdigit +#define _istdigit_l _ismbcdigit_l +#define _istgraph _ismbcgraph +#define _istgraph_l _ismbcgraph_l +#define _istlegal _ismbclegal +#define _istlegal_l _ismbclegal_l +#define _istlower _ismbclower +#define _istlower_l _ismbclower_l +#define _istprint _ismbcprint +#define _istprint_l _ismbcprint_l +#define _istpunct _ismbcpunct +#define _istpunct_l _ismbcpunct_l +#define _istblank _ismbcblank +#define _istblank_l _ismbcblank_l +#define _istspace _ismbcspace +#define _istspace_l _ismbcspace_l +#define _istupper _ismbcupper +#define _istupper_l _ismbcupper_l + +#define _totupper _mbctoupper +#define _totupper_l _mbctoupper_l +#define _totlower _mbctolower +#define _totlower_l _mbctolower_l + +#define _istlead _ismbblead +#define _istleadbyte isleadbyte +#define _istleadbyte_l _isleadbyte_l + +#else /* _MBCS */ + +/* ++++++++++++++++++++ SBCS ++++++++++++++++++++ */ + + +#ifndef __TCHAR_DEFINED +typedef char _TCHAR; +typedef signed char _TSCHAR; +typedef unsigned char _TUCHAR; +typedef char _TXCHAR; +typedef int _TINT; +#define __TCHAR_DEFINED +#endif /* __TCHAR_DEFINED */ + +#ifndef _TCHAR_DEFINED +#if !__STDC__ +typedef char TCHAR; +#endif /* !__STDC__ */ +#define _TCHAR_DEFINED +#endif /* _TCHAR_DEFINED */ + + +/* String functions */ + +#define _tcschr strchr +#define _tcscspn strcspn +#define _tcsncat strncat +#define _tcsncat_s strncat_s +#define _tcsncat_l _strncat_l +#define _tcsncat_s_l _strncat_s_l +#define _tcsncpy strncpy +#define _tcsncpy_s strncpy_s +#define _tcsncpy_l _strncpy_l +#define _tcsncpy_s_l _strncpy_s_l +#define _tcspbrk strpbrk +#define _tcsrchr strrchr +#define _tcsspn strspn +#define _tcsstr strstr +#define _tcstok strtok +#define _tcstok_s strtok_s +#define _tcstok_l _strtok_l +#define _tcstok_s_l _strtok_s_l + +#define _tcsnset _strnset +#define _tcsnset_s _strnset_s +#define _tcsnset_l _strnset_l +#define _tcsnset_s_l _strnset_s_l +#define _tcsrev _strrev +#define _tcsset _strset +#define _tcsset_s _strset_s +#define _tcsset_l _strset_l +#define _tcsset_s_l _strset_s_l + +#define _tcscmp strcmp +#define _tcsicmp _stricmp +#define _tcsicmp_l _stricmp_l +#define _tcsnccmp strncmp +#define _tcsncmp strncmp +#define _tcsncicmp _strnicmp +#define _tcsncicmp_l _strnicmp_l +#define _tcsnicmp _strnicmp +#define _tcsnicmp_l _strnicmp_l + +#define _tcscoll strcoll +#define _tcscoll_l _strcoll_l +#define _tcsicoll _stricoll +#define _tcsicoll_l _stricoll_l +#define _tcsnccoll _strncoll +#define _tcsnccoll_l _strncoll_l +#define _tcsncoll _strncoll +#define _tcsncoll_l _strncoll_l +#define _tcsncicoll _strnicoll +#define _tcsncicoll_l _strnicoll_l +#define _tcsnicoll _strnicoll +#define _tcsnicoll_l _strnicoll_l + +/* "logical-character" mappings */ + +#define _tcsclen strlen +#define _tcscnlen strnlen +#define _tcsclen_l(_String, _Locale) strlen(_String) +#define _tcscnlen_l(_String, _Max_count, _Locale) strnlen((_String), (_Max_count)) +#define _tcsnccat strncat +#define _tcsnccat_s strncat_s +#define _tcsnccat_l _strncat_l +#define _tcsnccat_s_l _strncat_s_l +#define _tcsnccpy strncpy +#define _tcsnccpy_s strncpy_s +#define _tcsnccpy_l _strncpy_l +#define _tcsnccpy_s_l _strncpy_s_l +#define _tcsncset _strnset +#define _tcsncset_s _strnset_s +#define _tcsncset_l _strnset_l +#define _tcsncset_s_l _strnset_s_l + +/* MBCS-specific functions */ + +#define _tcsdec _strdec +#define _tcsinc _strinc +#define _tcsnbcnt _strncnt +#define _tcsnccnt _strncnt +#define _tcsnextc _strnextc +#define _tcsninc _strninc +#define _tcsspnp _strspnp + +#define _tcslwr _strlwr +#define _tcslwr_l _strlwr_l +#define _tcslwr_s _strlwr_s +#define _tcslwr_s_l _strlwr_s_l +#define _tcsupr _strupr +#define _tcsupr_l _strupr_l +#define _tcsupr_s _strupr_s +#define _tcsupr_s_l _strupr_s_l +#define _tcsxfrm strxfrm +#define _tcsxfrm_l _strxfrm_l + +#define _istlead(_Char) (0) +#define _istleadbyte(_Char) (0) +#define _istleadbyte_l(_Char, _Locale) (0) + +#if __STDC__ || defined (_NO_INLINING) +#define _tclen(_pc) (1) +#define _tccpy(_pc1,_cpc2) (*(_pc1) = *(_cpc2)) +#define _tccpy_l(_pc1,_cpc2,_locale) _tccpy((_pc1),(_cpc2)) +#define _tccmp(_cpc1,_cpc2) (((unsigned char)*(_cpc1))-((unsigned char)*(_cpc2))) +#else /* __STDC__ || defined (_NO_INLINING) */ +_Check_return_ __inline size_t __CRTDECL _tclen(_In_z_ const char *_cpc) +{ + /* avoid compiler warning */ + (void *)_cpc; + return 1; +} +__inline void __CRTDECL _tccpy(_Out_ char *_pc1, _In_z_ const char *_cpc2) { *_pc1 = *_cpc2; } +__inline void __CRTDECL _tccpy_l(_Out_ char *_Pc1, _In_z_ const char *_Cpc2, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 28719 ) + _tccpy(_Pc1, _Cpc2); +#pragma warning( pop ) +} +_Check_return_ __inline int __CRTDECL _tccmp(_In_z_ const char *_cpc1, _In_z_ const char *_cpc2) { return (int) (((unsigned char)*_cpc1)-((unsigned char)*_cpc2)); } +#endif /* __STDC__ || defined (_NO_INLINING) */ + + +/* ctype-functions */ + +#define _istalnum isalnum +#define _istalnum_l _isalnum_l +#define _istalpha isalpha +#define _istalpha_l _isalpha_l +#define _istdigit isdigit +#define _istdigit_l _isdigit_l +#define _istgraph isgraph +#define _istgraph_l _isgraph_l +#define _istlower islower +#define _istlower_l _islower_l +#define _istprint isprint +#define _istprint_l _isprint_l +#define _istpunct ispunct +#define _istpunct_l _ispunct_l +#define _istblank isblank +#define _istblank_l _isblank_l +#define _istspace isspace +#define _istspace_l _isspace_l +#define _istupper isupper +#define _istupper_l _isupper_l + +#define _totupper toupper +#define _totupper_l _toupper_l +#define _totlower tolower +#define _totlower_l _tolower_l + +#define _istlegal(_c) (1) + + +/* the following is optional if functional versions are available */ + +/* define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + + +#if __STDC__ || defined (_NO_INLINING) +#define _strdec(_cpc1, _cpc2) ((_cpc1)>=(_cpc2) ? NULL : (_cpc2)-1) +#define _strinc(_pc) ((_pc)+1) +#define _strnextc(_cpc) ((unsigned int) *(const unsigned char *)(_cpc)) +#define _strninc(_pc, _sz) (((_pc)+(_sz))) +_CRTIMP size_t __cdecl __strncnt(_In_reads_or_z_(_Cnt) const char * _Str, _In_ size_t _Cnt); +#define _strncnt(_cpc, _sz) (__strncnt(_cpc,_sz)) +#define _strspnp(_cpc1, _cpc2) (_cpc1==NULL ? NULL : ((*((_cpc1)+strspn(_cpc1,_cpc2))) ? ((_cpc1)+strspn(_cpc1,_cpc2)) : NULL)) + +#define _strncpy_l(_Destination, _Source, _Count, _Locale) (strncpy(_Destination, _Source, _Count)) +#if __STDC_WANT_SECURE_LIB__ +#define _strncpy_s_l(_Destination, _Destination_size_chars, _Source, _Count, _Locale) (strncpy_s(_Destination, _Destination_size_chars, _Source, _Count)) +#endif /* __STDC_WANT_SECURE_LIB__ */ +#define _strncat_l(_Destination, _Source, _Count, _Locale) (strncat(_Destination, _Source, _Count)) +#if __STDC_WANT_SECURE_LIB__ +#define _strncat_s_l(_Destination, _Destination_size_chars, _Source, _Count, _Locale) (strncat_s(_Destination, _Destination_size_chars, _Source, _Count)) +#endif /* __STDC_WANT_SECURE_LIB__ */ +#define _strtok_l(_String, _Delimiters, _Locale) (strtok(_String, _Delimiters)) +#if __STDC_WANT_SECURE_LIB__ +#define _strtok_s_l(_String, _Delimiters, _Current_position, _Locale) (strtok_s(_String, _Delimiters, _Current_position)) +#endif /* __STDC_WANT_SECURE_LIB__ */ +#define _strnset_l(_Destination, _Value, _Count, _Locale) (_strnset(_Destination, _Value, _Count)) +#define _strnset_s_l(_Destination, _Destination_size_chars, _Value, _Count, _Locale) (_strnset_s(_Destination, _Destination_size_chars, _Value, _Count)) +#define _strset_l(_Destination, _Value, _Locale) (_strset(_Destination, _Value)) +#define _strset_s_l(_Destination, _Destination_size_chars, _Value, _Locale) (_strset_s(_Destination, _Destination_size_chars, _Value)) +#else /* __STDC__ || defined (_NO_INLINING) */ +_Check_return_ __inline char * __CRTDECL _strdec(_In_reads_z_(_Cpc2 - _Cpc1) const char * _Cpc1, _In_z_ const char * _Cpc2) { return (char *)((_Cpc1)>=(_Cpc2) ? NULL : (_Cpc2-1)); } +_Check_return_ __inline char * __CRTDECL _strinc(_In_z_ const char * _Pc) { return (char *)(_Pc+1); } +_Check_return_ __inline unsigned int __CRTDECL _strnextc(_In_z_ const char * _Cpc) { return (unsigned int)*(const unsigned char *)_Cpc; } +_Check_return_ __inline char * __CRTDECL _strninc(_In_reads_or_z_(_Sz) const char * _Pc, _In_ size_t _Sz) { return (char *)(_Pc+_Sz); } +_Check_return_ __inline size_t __CRTDECL _strncnt(_In_reads_or_z_(_Cnt) const char * _String, _In_ size_t _Cnt) +{ + size_t n = _Cnt; + char *cp = (char *)_String; + while (n-- && *cp) + cp++; + return _Cnt - n - 1; +} +_Check_return_ __inline char * __CRTDECL _strspnp +( + _In_z_ const char * _Cpc1, + _In_z_ const char * _Cpc2 +) +{ + return _Cpc1==NULL ? NULL : ((*(_Cpc1 += strspn(_Cpc1,_Cpc2))!='\0') ? (char*)_Cpc1 : NULL); +} + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ __inline errno_t __CRTDECL _strncpy_s_l(_Out_writes_z_(_Destination_size_chars) char *_Destination, _In_ size_t _Destination_size_chars, _In_z_ const char *_Source, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return strncpy_s(_Destination, _Destination_size_chars, _Source, _Count); +} +#endif /* __STDC_WANT_SECURE_LIB__ */ + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _strncpy_s_l, _Post_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _strncpy_l, _strncpy_s_l, _Out_writes_z_(_Size) char, _Out_writes_(_Count) char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 28719 ) + return strncpy(_Dst, _Source, _Count); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _strncpy_l, _strncpy_s_l, _Out_writes_z_(_Size) char, _Out_writes_(_Count), char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ __inline errno_t __CRTDECL _strncat_s_l(_Inout_updates_z_(_Destination_size_chars) char *_Destination, _In_ size_t _Destination_size_chars, _In_z_ const char *_Source, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return strncat_s(_Destination, _Destination_size_chars, _Source, _Count); +} +#endif /* __STDC_WANT_SECURE_LIB__ */ + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _strncat_s_l, _Prepost_z_ char, _Dest, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _strncat_l, _strncat_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 6054 28719 ) + return strncat(_Dst, _Source, _Count); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _strncat_l, _strncat_s_l, _Inout_updates_z_(_Size) char, _Inout_z_, char, _Dst, _In_z_ const char *, _Source, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +_Check_return_ _CRT_INSECURE_DEPRECATE(_strtok_s_l) __inline char * _strtok_l(_Inout_opt_z_ char * _String, _In_z_ const char * _Delimiters, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning(push) +#pragma warning(disable:4996 28727) + return strtok(_String,_Delimiters); +#pragma warning(pop) +} + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ __inline char * _strtok_s_l(_Inout_opt_z_ char * _String, _In_z_ const char * _Delimiters, _Inout_ _Deref_prepost_opt_z_ char **_Current_position, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return strtok_s(_String, _Delimiters, _Current_position); +} +#endif /* __STDC_WANT_SECURE_LIB__ */ + +__inline errno_t __CRTDECL _strnset_s_l(_Inout_updates_z_(_Destination_size_chars) char *_Destination, _In_ size_t _Destination_size_chars, _In_ int _Value, _In_ size_t _Count, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return _strnset_s(_Destination, _Destination_size_chars, _Value, _Count); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(errno_t, _strnset_s_l, _Prepost_z_ char, _Dest, _In_ int, _Value, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _strnset_l, _strnset_s_l, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_MaxCount) char, _Dst, _In_ int, _Value, _In_ size_t, _MaxCount, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 6054 ) + return _strnset(_Dst, _Value, _MaxCount); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_NFUNC_0_3_EX(char *, __RETURN_POLICY_DST, _strnset_l, _strnset_s_l, _Inout_updates_z_(_Size) char, _Inout_updates_z_(_MaxCount), char, _Dst, _In_ int, _Value, _In_ size_t, _Count, _In_opt_ _locale_t, _Locale) + +__inline errno_t __CRTDECL _strset_s_l(_Inout_updates_z_(_Destination_size_chars) char *_Destination, _In_ size_t _Destination_size_chars, _In_ int _Value, _In_opt_ _locale_t _Locale) +{ + _CRT_UNUSED(_Locale); + return _strset_s(_Destination, _Destination_size_chars, _Value); +} + +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _strset_s_l, _Prepost_z_ char, _Dest, _In_ int, _Value, _In_opt_ _locale_t, _Locale) + +__DECLARE_CPP_OVERLOAD_INLINE_FUNC_0_2_EX(char *, __RETURN_POLICY_DST, _strset_l, _strset_s_l, _Inout_updates_z_(_Size) char, _Inout_z_ char, _Dst, _In_ int, _Value, _In_opt_ _locale_t, _Locale) +{ + _CRT_UNUSED(_Locale); +#pragma warning( push ) +#pragma warning( disable : 4996 ) + return _strset(_Dst, _Value); +#pragma warning( pop ) +} + +__DEFINE_CPP_OVERLOAD_INLINE_FUNC_0_2_EX(char *, __RETURN_POLICY_DST, _strset_l, _strset_s_l, _Inout_updates_z_(_Size) char, _Inout_z_, char, _Dst, _In_ int, _Value, _In_opt_ _locale_t, _Locale) + +#endif /* __STDC__ || defined (_NO_INLINING) */ + + +#endif /* _MBCS */ + +#ifdef _CRTBLD +/* + * macros for internal CRT stuff. + * This saves the mess that is created in the CRT. + */ +#ifdef _SAFECRT_IMPL +#define __tinput _input +#define __tinput_s _input_s +#else /* _SAFECRT_IMPL */ +#define _tinput_l _input_l +#define _tinput_s_l _input_s_l +#endif /* _SAFECRT_IMPL */ +#define __topenfile _openfile +#define _tgetpath _getpath +#define __crtMessageBox __crtMessageBoxA +#define __crtMessageWindow __crtMessageWindowA +#define _VCrtDbgReportT _VCrtDbgReportA +#define _CrtDbgReportT _CrtDbgReport +#define _CrtDbgReportTV _CrtDbgReportV +#define ReportHookNodeT ReportHookNode +#define _pReportHookListT _pReportHookList +#define _CrtSetReportHookT2 _CrtSetReportHook2 +#define _CRT_REPORT_HOOKT _CRT_REPORT_HOOK +#define _ttmpnam_helper _tmpnam_helper +#define _tfreopen_helper _freopen_helper +#define _getts_helper _gets_helper +#define _tfullpath_helper _fullpath_helper +#define _tsopen_helper _sopen_helper +#define _tsopen_nolock _sopen_nolock +#define _tdupenv_s_crt _dupenv_s_crt + +#endif /* _CRTBLD */ + +#endif /* _UNICODE */ + + +/* Generic text macros to be used with string literals and character constants. + Will also allow symbolic constants that resolve to same. */ + +#define _T(x) __T(x) +#define _TEXT(x) __T(x) + + +#ifdef __cplusplus +} /* ... extern "C" */ +#endif /* __cplusplus */ + +#endif /* _INC_TCHAR */ + diff --git a/test_data/lots_of_files/test_asset_builder.cpp b/test_data/lots_of_files/test_asset_builder.cpp new file mode 100644 index 0000000..86fc338 --- /dev/null +++ b/test_data/lots_of_files/test_asset_builder.cpp @@ -0,0 +1,1142 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#include "test_asset_builder.h" + +#pragma pack(push, 1) +struct bitmap_header +{ + uint16 FileType; + uint32 FileSize; + uint16 Reserved1; + uint16 Reserved2; + uint32 BitmapOffset; + uint32 Size; + int32 Width; + int32 Height; + uint16 Planes; + uint16 BitsPerPixel; + uint32 Compression; + uint32 SizeOfBitmap; + int32 HorzResolution; + int32 VertResolution; + uint32 ColorsUsed; + uint32 ColorsImportant; + + uint32 RedMask; + uint32 GreenMask; + uint32 BlueMask; +}; + +struct WAVE_header +{ + uint32 RIFFID; + uint32 Size; + uint32 WAVEID; +}; + +#define RIFF_CODE(a, b, c, d) (((uint32)(a) << 0) | ((uint32)(b) << 8) | ((uint32)(c) << 16) | ((uint32)(d) << 24)) +enum +{ + WAVE_ChunkID_fmt = RIFF_CODE('f', 'm', 't', ' '), + WAVE_ChunkID_data = RIFF_CODE('d', 'a', 't', 'a'), + WAVE_ChunkID_RIFF = RIFF_CODE('R', 'I', 'F', 'F'), + WAVE_ChunkID_WAVE = RIFF_CODE('W', 'A', 'V', 'E'), +}; +struct WAVE_chunk +{ + uint32 ID; + uint32 Size; +}; + +struct WAVE_fmt +{ + uint16 wFormatTag; + uint16 nChannels; + uint32 nSamplesPerSec; + uint32 nAvgBytesPerSec; + uint16 nBlockAlign; + uint16 wBitsPerSample; + uint16 cbSize; + uint16 wValidBitsPerSample; + uint32 dwChannelMask; + uint8 SubFormat[16]; +}; + +#pragma pack(pop) + +struct entire_file +{ + u32 ContentsSize; + void *Contents; +}; +entire_file +ReadEntireFile(char *FileName) +{ + entire_file Result = {}; + + FILE *In = fopen(FileName, "rb"); + if(In) + { + fseek(In, 0, SEEK_END); + Result.ContentsSize = ftell(In); + fseek(In, 0, SEEK_SET); + + Result.Contents = malloc(Result.ContentsSize); + fread(Result.Contents, Result.ContentsSize, 1, In); + fclose(In); + } + else + { + printf("ERROR: Cannot open file %s.\n", FileName); + } + + return(Result); +} + +internal loaded_bitmap +LoadBMP(char *FileName) +{ + loaded_bitmap Result = {}; + + entire_file ReadResult = ReadEntireFile(FileName); + if(ReadResult.ContentsSize != 0) + { + Result.Free = ReadResult.Contents; + + bitmap_header *Header = (bitmap_header *)ReadResult.Contents; + uint32 *Pixels = (uint32 *)((uint8 *)ReadResult.Contents + Header->BitmapOffset); + Result.Memory = Pixels; + Result.Width = Header->Width; + Result.Height = Header->Height; + + Assert(Result.Height >= 0); + Assert(Header->Compression == 3); + + // NOTE(casey): If you are using this generically for some reason, + // please remember that BMP files CAN GO IN EITHER DIRECTION and + // the height will be negative for top-down. + // (Also, there can be compression, etc., etc... DON'T think this + // is complete BMP loading code because it isn't!!) + + // NOTE(casey): Byte order in memory is determined by the Header itself, + // so we have to read out the masks and convert the pixels ourselves. + uint32 RedMask = Header->RedMask; + uint32 GreenMask = Header->GreenMask; + uint32 BlueMask = Header->BlueMask; + uint32 AlphaMask = ~(RedMask | GreenMask | BlueMask); + + bit_scan_result RedScan = FindLeastSignificantSetBit(RedMask); + bit_scan_result GreenScan = FindLeastSignificantSetBit(GreenMask); + bit_scan_result BlueScan = FindLeastSignificantSetBit(BlueMask); + bit_scan_result AlphaScan = FindLeastSignificantSetBit(AlphaMask); + + Assert(RedScan.Found); + Assert(GreenScan.Found); + Assert(BlueScan.Found); + Assert(AlphaScan.Found); + + int32 RedShiftDown = (int32)RedScan.Index; + int32 GreenShiftDown = (int32)GreenScan.Index; + int32 BlueShiftDown = (int32)BlueScan.Index; + int32 AlphaShiftDown = (int32)AlphaScan.Index; + + uint32 *SourceDest = Pixels; + for(int32 Y = 0; + Y < Header->Height; + ++Y) + { + for(int32 X = 0; + X < Header->Width; + ++X) + { + uint32 C = *SourceDest; + + v4 Texel = {(real32)((C & RedMask) >> RedShiftDown), + (real32)((C & GreenMask) >> GreenShiftDown), + (real32)((C & BlueMask) >> BlueShiftDown), + (real32)((C & AlphaMask) >> AlphaShiftDown)}; + + Texel = SRGB255ToLinear1(Texel); +#if 1 + Texel.rgb *= Texel.a; +#endif + Texel = Linear1ToSRGB255(Texel); + + *SourceDest++ = (((uint32)(Texel.a + 0.5f) << 24) | + ((uint32)(Texel.r + 0.5f) << 16) | + ((uint32)(Texel.g + 0.5f) << 8) | + ((uint32)(Texel.b + 0.5f) << 0)); + } + } + } + + Result.Pitch = Result.Width*BITMAP_BYTES_PER_PIXEL; + +#if 0 + Result.Memory = (uint8 *)Result.Memory + Result.Pitch*(Result.Height - 1); + Result.Pitch = -Result.Pitch; +#endif + + return(Result); +} + +internal loaded_font * +LoadFont(char *FileName, char *FontName, int PixelHeight) +{ + loaded_font *Font = (loaded_font *)malloc(sizeof(loaded_font)); + + AddFontResourceExA(FileName, FR_PRIVATE, 0); + Font->Win32Handle = CreateFontA(PixelHeight, 0, 0, 0, + FW_NORMAL, // NOTE(casey): Weight + FALSE, // NOTE(casey): Italic + FALSE, // NOTE(casey): Underline + FALSE, // NOTE(casey): Strikeout + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + ANTIALIASED_QUALITY, + DEFAULT_PITCH|FF_DONTCARE, + FontName); + + SelectObject(GlobalFontDeviceContext, Font->Win32Handle); + GetTextMetrics(GlobalFontDeviceContext, &Font->TextMetric); + + Font->MinCodePoint = INT_MAX; + Font->MaxCodePoint = 0; + + // NOTE(casey): 5k characters should be more than enough for _anybody_! + Font->MaxGlyphCount = 5000; + Font->GlyphCount = 0; + + u32 GlyphIndexFromCodePointSize = ONE_PAST_MAX_FONT_CODEPOINT*sizeof(u32); + Font->GlyphIndexFromCodePoint = (u32 *)malloc(GlyphIndexFromCodePointSize); + memset(Font->GlyphIndexFromCodePoint, 0, GlyphIndexFromCodePointSize); + + Font->Glyphs = (hha_font_glyph *)malloc(sizeof(hha_font_glyph)*Font->MaxGlyphCount); + size_t HorizontalAdvanceSize = sizeof(r32)*Font->MaxGlyphCount*Font->MaxGlyphCount; + Font->HorizontalAdvance = (r32 *)malloc(HorizontalAdvanceSize); + memset(Font->HorizontalAdvance, 0, HorizontalAdvanceSize); + + Font->OnePastHighestCodepoint = 0; + + // NOTE(casey): Reserve space for the null glyph + Font->GlyphCount = 1; + Font->Glyphs[0].UnicodeCodePoint = 0; + Font->Glyphs[0].BitmapID.Value = 0; + + return(Font); +} + +internal void +FinalizeFontKerning(loaded_font *Font) +{ + SelectObject(GlobalFontDeviceContext, Font->Win32Handle); + + DWORD KerningPairCount = GetKerningPairsW(GlobalFontDeviceContext, 0, 0); + KERNINGPAIR *KerningPairs = (KERNINGPAIR *)malloc(KerningPairCount*sizeof(KERNINGPAIR)); + GetKerningPairsW(GlobalFontDeviceContext, KerningPairCount, KerningPairs); + for(DWORD KerningPairIndex = 0; + KerningPairIndex < KerningPairCount; + ++KerningPairIndex) + { + KERNINGPAIR *Pair = KerningPairs + KerningPairIndex; + if((Pair->wFirst < ONE_PAST_MAX_FONT_CODEPOINT) && + (Pair->wSecond < ONE_PAST_MAX_FONT_CODEPOINT)) + { + u32 First = Font->GlyphIndexFromCodePoint[Pair->wFirst]; + u32 Second = Font->GlyphIndexFromCodePoint[Pair->wSecond]; + if((First != 0) && (Second != 0)) + { + Font->HorizontalAdvance[First*Font->MaxGlyphCount + Second] += (r32)Pair->iKernAmount; + } + } + } + + free(KerningPairs); +} + +internal void +FreeFont(loaded_font *Font) +{ + if(Font) + { + DeleteObject(Font->Win32Handle); + free(Font->Glyphs); + free(Font->HorizontalAdvance); + free(Font->GlyphIndexFromCodePoint); + free(Font); + } +} + +internal void +InitializeFontDC(void) +{ + GlobalFontDeviceContext = CreateCompatibleDC(GetDC(0)); + + BITMAPINFO Info = {}; + Info.bmiHeader.biSize = sizeof(Info.bmiHeader); + Info.bmiHeader.biWidth = MAX_FONT_WIDTH; + Info.bmiHeader.biHeight = MAX_FONT_HEIGHT; + Info.bmiHeader.biPlanes = 1; + Info.bmiHeader.biBitCount = 32; + Info.bmiHeader.biCompression = BI_RGB; + Info.bmiHeader.biSizeImage = 0; + Info.bmiHeader.biXPelsPerMeter = 0; + Info.bmiHeader.biYPelsPerMeter = 0; + Info.bmiHeader.biClrUsed = 0; + Info.bmiHeader.biClrImportant = 0; + HBITMAP Bitmap = CreateDIBSection(GlobalFontDeviceContext, &Info, DIB_RGB_COLORS, &GlobalFontBits, 0, 0); + SelectObject(GlobalFontDeviceContext, Bitmap); + SetBkColor(GlobalFontDeviceContext, RGB(0, 0, 0)); +} + +internal loaded_bitmap +LoadGlyphBitmap(loaded_font *Font, u32 CodePoint, hha_asset *Asset) +{ + loaded_bitmap Result = {}; + + u32 GlyphIndex = Font->GlyphIndexFromCodePoint[CodePoint]; + +#if USE_FONTS_FROM_WINDOWS + + SelectObject(GlobalFontDeviceContext, Font->Win32Handle); + + memset(GlobalFontBits, 0x00, MAX_FONT_WIDTH*MAX_FONT_HEIGHT*sizeof(u32)); + + wchar_t CheesePoint = (wchar_t)CodePoint; + + SIZE Size; + GetTextExtentPoint32W(GlobalFontDeviceContext, &CheesePoint, 1, &Size); + + int PreStepX = 128; + + int BoundWidth = Size.cx + 2*PreStepX; + if(BoundWidth > MAX_FONT_WIDTH) + { + BoundWidth = MAX_FONT_WIDTH; + } + int BoundHeight = Size.cy; + if(BoundHeight > MAX_FONT_HEIGHT) + { + BoundHeight = MAX_FONT_HEIGHT; + } + +// PatBlt(DeviceContext, 0, 0, Width, Height, BLACKNESS); +// SetBkMode(DeviceContext, TRANSPARENT); + SetTextColor(GlobalFontDeviceContext, RGB(255, 255, 255)); + TextOutW(GlobalFontDeviceContext, PreStepX, 0, &CheesePoint, 1); + + s32 MinX = 10000; + s32 MinY = 10000; + s32 MaxX = -10000; + s32 MaxY = -10000; + + u32 *Row = (u32 *)GlobalFontBits + (MAX_FONT_HEIGHT - 1)*MAX_FONT_WIDTH; + for(s32 Y = 0; + Y < BoundHeight; + ++Y) + { + u32 *Pixel = Row; + for(s32 X = 0; + X < BoundWidth; + ++X) + { +#if 0 + COLORREF RefPixel = GetPixel(GlobalFontDeviceContext, X, Y); + Assert(RefPixel == *Pixel); +#endif + if(*Pixel != 0) + { + if(MinX > X) + { + MinX = X; + } + + if(MinY > Y) + { + MinY = Y; + } + + if(MaxX < X) + { + MaxX = X; + } + + if(MaxY < Y) + { + MaxY = Y; + } + } + + ++Pixel; + } + Row -= MAX_FONT_WIDTH; + } + + r32 KerningChange = 0; + if(MinX <= MaxX) + { + int Width = (MaxX - MinX) + 1; + int Height = (MaxY - MinY) + 1; + + Result.Width = Width + 2; + Result.Height = Height + 2; + Result.Pitch = Result.Width*BITMAP_BYTES_PER_PIXEL; + Result.Memory = malloc(Result.Height*Result.Pitch); + Result.Free = Result.Memory; + + memset(Result.Memory, 0, Result.Height*Result.Pitch); + + u8 *DestRow = (u8 *)Result.Memory + (Result.Height - 1 - 1)*Result.Pitch; + u32 *SourceRow = (u32 *)GlobalFontBits + (MAX_FONT_HEIGHT - 1 - MinY)*MAX_FONT_WIDTH; + for(s32 Y = MinY; + Y <= MaxY; + ++Y) + { + u32 *Source = (u32 *)SourceRow + MinX; + u32 *Dest = (u32 *)DestRow + 1; + for(s32 X = MinX; + X <= MaxX; + ++X) + { +#if 0 + COLORREF Pixel = GetPixel(GlobalFontDeviceContext, X, Y); + Assert(Pixel == *Source); +#else + u32 Pixel = *Source; +#endif + r32 Gray = (r32)(Pixel & 0xFF); + v4 Texel = {255.0f, 255.0f, 255.0f, Gray}; + Texel = SRGB255ToLinear1(Texel); + Texel.rgb *= Texel.a; + Texel = Linear1ToSRGB255(Texel); + + *Dest++ = (((uint32)(Texel.a + 0.5f) << 24) | + ((uint32)(Texel.r + 0.5f) << 16) | + ((uint32)(Texel.g + 0.5f) << 8) | + ((uint32)(Texel.b + 0.5f) << 0)); + + + ++Source; + } + + DestRow -= Result.Pitch; + SourceRow -= MAX_FONT_WIDTH; + } + + Asset->Bitmap.AlignPercentage[0] = (1.0f) / (r32)Result.Width; + Asset->Bitmap.AlignPercentage[1] = (1.0f + (MaxY - (BoundHeight - Font->TextMetric.tmDescent))) / (r32)Result.Height; + + KerningChange = (r32)(MinX - PreStepX); + } + +#if 0 + ABC ThisABC; + GetCharABCWidthsW(GlobalFontDeviceContext, CodePoint, CodePoint, &ThisABC); + r32 CharAdvance = (r32)(ThisABC.abcA + ThisABC.abcB + ThisABC.abcC); +#else + INT ThisWidth; + GetCharWidth32W(GlobalFontDeviceContext, CodePoint, CodePoint, &ThisWidth); + r32 CharAdvance = (r32)ThisWidth; +#endif + + for(u32 OtherGlyphIndex = 0; + OtherGlyphIndex < Font->MaxGlyphCount; + ++OtherGlyphIndex) + { + Font->HorizontalAdvance[GlyphIndex*Font->MaxGlyphCount + OtherGlyphIndex] += CharAdvance - KerningChange; + if(OtherGlyphIndex != 0) + { + Font->HorizontalAdvance[OtherGlyphIndex*Font->MaxGlyphCount + GlyphIndex] += KerningChange; + } + } + +#else + + entire_file TTFFile = ReadEntireFile(FileName); + if(TTFFile.ContentsSize != 0) + { + stbtt_fontinfo Font; + stbtt_InitFont(&Font, (u8 *)TTFFile.Contents, stbtt_GetFontOffsetForIndex((u8 *)TTFFile.Contents, 0)); + + int Width, Height, XOffset, YOffset; + u8 *MonoBitmap = stbtt_GetCodepointBitmap(&Font, 0, stbtt_ScaleForPixelHeight(&Font, 128.0f), + CodePoint, &Width, &Height, &XOffset, &YOffset); + + Result.Width = Width; + Result.Height = Height; + Result.Pitch = Result.Width*BITMAP_BYTES_PER_PIXEL; + Result.Memory = malloc(Height*Result.Pitch); + Result.Free = Result.Memory; + + u8 *Source = MonoBitmap; + u8 *DestRow = (u8 *)Result.Memory + (Height - 1)*Result.Pitch; + for(s32 Y = 0; + Y < Height; + ++Y) + { + u32 *Dest = (u32 *)DestRow; + for(s32 X = 0; + X < Width; + ++X) + { + u8 Gray = *Source++; + u8 Alpha = 0xFF; + *Dest++ = ((Alpha << 24) | + (Gray << 16) | + (Gray << 8) | + (Gray << 0)); + } + + DestRow -= Result.Pitch; + } + + stbtt_FreeBitmap(MonoBitmap, 0); + free(TTFFile.Contents); + } +#endif + + return(Result); +} + +struct riff_iterator +{ + uint8 *At; + uint8 *Stop; +}; + +inline riff_iterator +ParseChunkAt(void *At, void *Stop) +{ + riff_iterator Iter; + + Iter.At = (uint8 *)At; + Iter.Stop = (uint8 *)Stop; + + return(Iter); +} + +inline riff_iterator +NextChunk(riff_iterator Iter) +{ + WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At; + uint32 Size = (Chunk->Size + 1) & ~1; + Iter.At += sizeof(WAVE_chunk) + Size; + + return(Iter); +} + +inline bool32 +IsValid(riff_iterator Iter) +{ + bool32 Result = (Iter.At < Iter.Stop); + + return(Result); +} + +inline void * +GetChunkData(riff_iterator Iter) +{ + void *Result = (Iter.At + sizeof(WAVE_chunk)); + + return(Result); +} + +inline uint32 +GetType(riff_iterator Iter) +{ + WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At; + uint32 Result = Chunk->ID; + + return(Result); +} + +inline uint32 +GetChunkDataSize(riff_iterator Iter) +{ + WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At; + uint32 Result = Chunk->Size; + + return(Result); +} + +struct loaded_sound +{ + uint32 SampleCount; // NOTE(casey): This is the sample count divided by 8 + uint32 ChannelCount; + int16 *Samples[2]; + + void *Free; +}; + +internal loaded_sound +LoadWAV(char *FileName, u32 SectionFirstSampleIndex, u32 SectionSampleCount) +{ + loaded_sound Result = {}; + + entire_file ReadResult = ReadEntireFile(FileName); + if(ReadResult.ContentsSize != 0) + { + Result.Free = ReadResult.Contents; + + WAVE_header *Header = (WAVE_header *)ReadResult.Contents; + Assert(Header->RIFFID == WAVE_ChunkID_RIFF); + Assert(Header->WAVEID == WAVE_ChunkID_WAVE); + + uint32 ChannelCount = 0; + uint32 SampleDataSize = 0; + int16 *SampleData = 0; + for(riff_iterator Iter = ParseChunkAt(Header + 1, (uint8 *)(Header + 1) + Header->Size - 4); + IsValid(Iter); + Iter = NextChunk(Iter)) + { + switch(GetType(Iter)) + { + case WAVE_ChunkID_fmt: + { + WAVE_fmt *fmt = (WAVE_fmt *)GetChunkData(Iter); + Assert(fmt->wFormatTag == 1); // NOTE(casey): Only support PCM + Assert(fmt->nSamplesPerSec == 48000); + Assert(fmt->wBitsPerSample == 16); + Assert(fmt->nBlockAlign == (sizeof(int16)*fmt->nChannels)); + ChannelCount = fmt->nChannels; + } break; + + case WAVE_ChunkID_data: + { + SampleData = (int16 *)GetChunkData(Iter); + SampleDataSize = GetChunkDataSize(Iter); + } break; + } + } + + Assert(ChannelCount && SampleData); + + Result.ChannelCount = ChannelCount; + u32 SampleCount = SampleDataSize / (ChannelCount*sizeof(int16)); + if(ChannelCount == 1) + { + Result.Samples[0] = SampleData; + Result.Samples[1] = 0; + } + else if(ChannelCount == 2) + { + Result.Samples[0] = SampleData; + Result.Samples[1] = SampleData + SampleCount; + +#if 0 + for(uint32 SampleIndex = 0; + SampleIndex < SampleCount; + ++SampleIndex) + { + SampleData[2*SampleIndex + 0] = (int16)SampleIndex; + SampleData[2*SampleIndex + 1] = (int16)SampleIndex; + } +#endif + + for(uint32 SampleIndex = 0; + SampleIndex < SampleCount; + ++SampleIndex) + { + int16 Source = SampleData[2*SampleIndex]; + SampleData[2*SampleIndex] = SampleData[SampleIndex]; + SampleData[SampleIndex] = Source; + } + } + else + { + Assert(!"Invalid channel count in WAV file"); + } + + // TODO(casey): Load right channels! + b32 AtEnd = true; + Result.ChannelCount = 1; + if(SectionSampleCount) + { + Assert((SectionFirstSampleIndex + SectionSampleCount) <= SampleCount); + AtEnd = ((SectionFirstSampleIndex + SectionSampleCount) == SampleCount); + SampleCount = SectionSampleCount; + for(uint32 ChannelIndex = 0; + ChannelIndex < Result.ChannelCount; + ++ChannelIndex) + { + Result.Samples[ChannelIndex] += SectionFirstSampleIndex; + } + } + + if(AtEnd) + { + for(uint32 ChannelIndex = 0; + ChannelIndex < Result.ChannelCount; + ++ChannelIndex) + { + for(u32 SampleIndex = SampleCount; + SampleIndex < (SampleCount + 8); + ++SampleIndex) + { + Result.Samples[ChannelIndex][SampleIndex] = 0; + } + } + } + + Result.SampleCount = SampleCount; + } + + return(Result); +} + +internal void +BeginAssetType(game_assets *Assets, asset_type_id TypeID) +{ + Assert(Assets->DEBUGAssetType == 0); + + Assets->DEBUGAssetType = Assets->AssetTypes + TypeID; + Assets->DEBUGAssetType->TypeID = TypeID; + Assets->DEBUGAssetType->FirstAssetIndex = Assets->AssetCount; + Assets->DEBUGAssetType->OnePastLastAssetIndex = Assets->DEBUGAssetType->FirstAssetIndex; +} + +struct added_asset +{ + u32 ID; + hha_asset *HHA; + asset_source *Source; +}; +internal added_asset +AddAsset(game_assets *Assets) +{ + Assert(Assets->DEBUGAssetType); + Assert(Assets->DEBUGAssetType->OnePastLastAssetIndex < ArrayCount(Assets->Assets)); + + u32 Index = Assets->DEBUGAssetType->OnePastLastAssetIndex++; + asset_source *Source = Assets->AssetSources + Index; + hha_asset *HHA = Assets->Assets + Index; + HHA->FirstTagIndex = Assets->TagCount; + HHA->OnePastLastTagIndex = HHA->FirstTagIndex; + + Assets->AssetIndex = Index; + + added_asset Result; + Result.ID = Index; + Result.HHA = HHA; + Result.Source = Source; + return(Result); +} + +internal bitmap_id +AddBitmapAsset(game_assets *Assets, char *FileName, r32 AlignPercentageX = 0.5f, r32 AlignPercentageY = 0.5f) +{ + added_asset Asset = AddAsset(Assets); + Asset.HHA->Bitmap.AlignPercentage[0] = AlignPercentageX; + Asset.HHA->Bitmap.AlignPercentage[1] = AlignPercentageY; + Asset.Source->Type = AssetType_Bitmap; + Asset.Source->Bitmap.FileName = FileName; + + bitmap_id Result = {Asset.ID}; + return(Result); +} + +internal bitmap_id +AddCharacterAsset(game_assets *Assets, loaded_font *Font, u32 Codepoint) +{ + added_asset Asset = AddAsset(Assets); + Asset.HHA->Bitmap.AlignPercentage[0] = 0.0f; // NOTE(casey): Set later by extraction + Asset.HHA->Bitmap.AlignPercentage[1] = 0.0f; // NOTE(casey): Set later by extraction + Asset.Source->Type = AssetType_FontGlyph; + Asset.Source->Glyph.Font = Font; + Asset.Source->Glyph.Codepoint = Codepoint; + + bitmap_id Result = {Asset.ID}; + + Assert(Font->GlyphCount < Font->MaxGlyphCount); + u32 GlyphIndex = Font->GlyphCount++; + hha_font_glyph *Glyph = Font->Glyphs + GlyphIndex; + Glyph->UnicodeCodePoint = Codepoint; + Glyph->BitmapID = Result; + Font->GlyphIndexFromCodePoint[Codepoint] = GlyphIndex; + + if(Font->OnePastHighestCodepoint <= Codepoint) + { + Font->OnePastHighestCodepoint = Codepoint + 1; + } + + return(Result); +} + +internal sound_id +AddSoundAsset(game_assets *Assets, char *FileName, u32 FirstSampleIndex = 0, u32 SampleCount = 0) +{ + added_asset Asset = AddAsset(Assets); + Asset.HHA->Sound.SampleCount = SampleCount; + Asset.HHA->Sound.Chain = HHASoundChain_None; + Asset.Source->Type = AssetType_Sound; + Asset.Source->Sound.FileName = FileName; + Asset.Source->Sound.FirstSampleIndex = FirstSampleIndex; + + sound_id Result = {Asset.ID}; + return(Result); +} + +internal font_id +AddFontAsset(game_assets *Assets, loaded_font *Font) +{ + added_asset Asset = AddAsset(Assets); + Asset.HHA->Font.OnePastHighestCodepoint = Font->OnePastHighestCodepoint; + Asset.HHA->Font.GlyphCount = Font->GlyphCount; + Asset.HHA->Font.AscenderHeight = (r32)Font->TextMetric.tmAscent; + Asset.HHA->Font.DescenderHeight = (r32)Font->TextMetric.tmDescent; + Asset.HHA->Font.ExternalLeading = (r32)Font->TextMetric.tmExternalLeading; + Asset.Source->Type = AssetType_Font; + Asset.Source->Font.Font = Font; + + font_id Result = {Asset.ID}; + return(Result); +} + +internal void +AddTag(game_assets *Assets, asset_tag_id ID, real32 Value) +{ + Assert(Assets->AssetIndex); + + hha_asset *HHA = Assets->Assets + Assets->AssetIndex; + ++HHA->OnePastLastTagIndex; + hha_tag *Tag = Assets->Tags + Assets->TagCount++; + + Tag->ID = ID; + Tag->Value = Value; +} + +internal void +EndAssetType(game_assets *Assets) +{ + Assert(Assets->DEBUGAssetType); + Assets->AssetCount = Assets->DEBUGAssetType->OnePastLastAssetIndex; + Assets->DEBUGAssetType = 0; + Assets->AssetIndex = 0; +} + +internal void +WriteHHA(game_assets *Assets, char *FileName) +{ + FILE *Out = fopen(FileName, "wb"); + if(Out) + { + hha_header Header = {}; + Header.MagicValue = HHA_MAGIC_VALUE; + Header.Version = HHA_VERSION; + Header.TagCount = Assets->TagCount; + Header.AssetTypeCount = Asset_Count; // TODO(casey): Do we really want to do this? Sparseness! + Header.AssetCount = Assets->AssetCount; + + u32 TagArraySize = Header.TagCount*sizeof(hha_tag); + u32 AssetTypeArraySize = Header.AssetTypeCount*sizeof(hha_asset_type); + u32 AssetArraySize = Header.AssetCount*sizeof(hha_asset); + + Header.Tags = sizeof(Header); + Header.AssetTypes = Header.Tags + TagArraySize; + Header.Assets = Header.AssetTypes + AssetTypeArraySize; + + fwrite(&Header, sizeof(Header), 1, Out); + fwrite(Assets->Tags, TagArraySize, 1, Out); + fwrite(Assets->AssetTypes, AssetTypeArraySize, 1, Out); + fseek(Out, AssetArraySize, SEEK_CUR); + for(u32 AssetIndex = 1; + AssetIndex < Header.AssetCount; + ++AssetIndex) + { + asset_source *Source = Assets->AssetSources + AssetIndex; + hha_asset *Dest = Assets->Assets + AssetIndex; + + Dest->DataOffset = ftell(Out); + + if(Source->Type == AssetType_Sound) + { + loaded_sound WAV = LoadWAV(Source->Sound.FileName, + Source->Sound.FirstSampleIndex, + Dest->Sound.SampleCount); + + Dest->Sound.SampleCount = WAV.SampleCount; + Dest->Sound.ChannelCount = WAV.ChannelCount; + + for(u32 ChannelIndex = 0; + ChannelIndex < WAV.ChannelCount; + ++ChannelIndex) + { + fwrite(WAV.Samples[ChannelIndex], Dest->Sound.SampleCount*sizeof(s16), 1, Out); + } + + free(WAV.Free); + } + else if(Source->Type == AssetType_Font) + { + loaded_font *Font = Source->Font.Font; + + FinalizeFontKerning(Font); + + u32 GlyphsSize = Font->GlyphCount*sizeof(hha_font_glyph); + fwrite(Font->Glyphs, GlyphsSize, 1, Out); + + u8 *HorizontalAdvance = (u8 *)Font->HorizontalAdvance; + for(u32 GlyphIndex = 0; + GlyphIndex < Font->GlyphCount; + ++GlyphIndex) + { + u32 HorizontalAdvanceSliceSize = sizeof(r32)*Font->GlyphCount; + fwrite(HorizontalAdvance, HorizontalAdvanceSliceSize, 1, Out); + HorizontalAdvance += sizeof(r32)*Font->MaxGlyphCount; + } + } + else + { + loaded_bitmap Bitmap; + if(Source->Type == AssetType_FontGlyph) + { + Bitmap = LoadGlyphBitmap(Source->Glyph.Font, Source->Glyph.Codepoint, Dest); + } + else + { + Assert(Source->Type == AssetType_Bitmap); + Bitmap = LoadBMP(Source->Bitmap.FileName); + } + + Dest->Bitmap.Dim[0] = Bitmap.Width; + Dest->Bitmap.Dim[1] = Bitmap.Height; + + Assert((Bitmap.Width*4) == Bitmap.Pitch); + fwrite(Bitmap.Memory, Bitmap.Width*Bitmap.Height*4, 1, Out); + + free(Bitmap.Free); + } + } + fseek(Out, (u32)Header.Assets, SEEK_SET); + fwrite(Assets->Assets, AssetArraySize, 1, Out); + + fclose(Out); + } + else + { + printf("ERROR: Couldn't open file :(\n"); + } +} + +internal void +Initialize(game_assets *Assets) +{ + Assets->TagCount = 1; + Assets->AssetCount = 1; + Assets->DEBUGAssetType = 0; + Assets->AssetIndex = 0; + + Assets->AssetTypeCount = Asset_Count; + memset(Assets->AssetTypes, 0, sizeof(Assets->AssetTypes)); +} + +internal void +WriteFonts(void) +{ + game_assets Assets_; + game_assets *Assets = &Assets_; + Initialize(Assets); + + loaded_font *Fonts[] = + { + LoadFont("c:/Windows/Fonts/arial.ttf", "Arial", 128), + LoadFont("c:/Windows/Fonts/LiberationMono-Regular.ttf", "Liberation Mono", 20), + }; + + BeginAssetType(Assets, Asset_FontGlyph); + for(u32 FontIndex = 0; + FontIndex < ArrayCount(Fonts); + ++FontIndex) + { + loaded_font *Font = Fonts[FontIndex]; + + AddCharacterAsset(Assets, Font, ' '); + for(u32 Character = '!'; + Character <= '~'; + ++Character) + { + AddCharacterAsset(Assets, Font, Character); + } + + // NOTE(casey): Kanji OWL!!!!!!! + AddCharacterAsset(Assets, Font, 0x5c0f); + AddCharacterAsset(Assets, Font, 0x8033); + AddCharacterAsset(Assets, Font, 0x6728); + AddCharacterAsset(Assets, Font, 0x514e); + } + EndAssetType(Assets); + + // TODO(casey): This is kinda janky, because it means you have to get this + // order right always! + BeginAssetType(Assets, Asset_Font); + AddFontAsset(Assets, Fonts[0]); + AddTag(Assets, Tag_FontType, FontType_Default); + AddFontAsset(Assets, Fonts[1]); + AddTag(Assets, Tag_FontType, FontType_Debug); + EndAssetType(Assets); + + WriteHHA(Assets, "testfonts.hha"); +} + +internal void +WriteHero(void) +{ + game_assets Assets_; + game_assets *Assets = &Assets_; + Initialize(Assets); + + real32 AngleRight = 0.0f*Tau32; + real32 AngleBack = 0.25f*Tau32; + real32 AngleLeft = 0.5f*Tau32; + real32 AngleFront = 0.75f*Tau32; + + r32 HeroAlign[] = {0.5f, 0.156682029f}; + + BeginAssetType(Assets, Asset_Head); + AddBitmapAsset(Assets, "test/test_hero_right_head.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleRight); + AddBitmapAsset(Assets, "test/test_hero_back_head.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleBack); + AddBitmapAsset(Assets, "test/test_hero_left_head.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleLeft); + AddBitmapAsset(Assets, "test/test_hero_front_head.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleFront); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Cape); + AddBitmapAsset(Assets, "test/test_hero_right_cape.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleRight); + AddBitmapAsset(Assets, "test/test_hero_back_cape.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleBack); + AddBitmapAsset(Assets, "test/test_hero_left_cape.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleLeft); + AddBitmapAsset(Assets, "test/test_hero_front_cape.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleFront); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Torso); + AddBitmapAsset(Assets, "test/test_hero_right_torso.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleRight); + AddBitmapAsset(Assets, "test/test_hero_back_torso.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleBack); + AddBitmapAsset(Assets, "test/test_hero_left_torso.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleLeft); + AddBitmapAsset(Assets, "test/test_hero_front_torso.bmp", HeroAlign[0], HeroAlign[1]); + AddTag(Assets, Tag_FacingDirection, AngleFront); + EndAssetType(Assets); + + WriteHHA(Assets, "test1.hha"); +} + +internal void +WriteNonHero(void) +{ + game_assets Assets_; + game_assets *Assets = &Assets_; + Initialize(Assets); + + BeginAssetType(Assets, Asset_Shadow); + AddBitmapAsset(Assets, "test/test_hero_shadow.bmp", 0.5f, 0.156682029f); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Tree); + AddBitmapAsset(Assets, "test2/tree00.bmp", 0.493827164f, 0.295652181f); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Sword); + AddBitmapAsset(Assets, "test2/rock03.bmp", 0.5f, 0.65625f); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Grass); + AddBitmapAsset(Assets, "test2/grass00.bmp"); + AddBitmapAsset(Assets, "test2/grass01.bmp"); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Tuft); + AddBitmapAsset(Assets, "test2/tuft00.bmp"); + AddBitmapAsset(Assets, "test2/tuft01.bmp"); + AddBitmapAsset(Assets, "test2/tuft02.bmp"); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Stone); + AddBitmapAsset(Assets, "test2/ground00.bmp"); + AddBitmapAsset(Assets, "test2/ground01.bmp"); + AddBitmapAsset(Assets, "test2/ground02.bmp"); + AddBitmapAsset(Assets, "test2/ground03.bmp"); + EndAssetType(Assets); + + WriteHHA(Assets, "test2.hha"); +} + +internal void +WriteSounds(void) +{ + game_assets Assets_; + game_assets *Assets = &Assets_; + Initialize(Assets); + + BeginAssetType(Assets, Asset_Bloop); + AddSoundAsset(Assets, "test3/bloop_00.wav"); + AddSoundAsset(Assets, "test3/bloop_01.wav"); + AddSoundAsset(Assets, "test3/bloop_02.wav"); + AddSoundAsset(Assets, "test3/bloop_03.wav"); + AddSoundAsset(Assets, "test3/bloop_04.wav"); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Crack); + AddSoundAsset(Assets, "test3/crack_00.wav"); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Drop); + AddSoundAsset(Assets, "test3/drop_00.wav"); + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Glide); + AddSoundAsset(Assets, "test3/glide_00.wav"); + EndAssetType(Assets); + + u32 OneMusicChunk = 10*48000; + u32 TotalMusicSampleCount = 7468095; + BeginAssetType(Assets, Asset_Music); + for(u32 FirstSampleIndex = 0; + FirstSampleIndex < TotalMusicSampleCount; + FirstSampleIndex += OneMusicChunk) + { + u32 SampleCount = TotalMusicSampleCount - FirstSampleIndex; + if(SampleCount > OneMusicChunk) + { + SampleCount = OneMusicChunk; + } + sound_id ThisMusic = AddSoundAsset(Assets, "test3/music_test.wav", FirstSampleIndex, SampleCount); + if((FirstSampleIndex + OneMusicChunk) < TotalMusicSampleCount) + { + Assets->Assets[ThisMusic.Value].Sound.Chain = HHASoundChain_Advance; + } + } + EndAssetType(Assets); + + BeginAssetType(Assets, Asset_Puhp); + AddSoundAsset(Assets, "test3/puhp_00.wav"); + AddSoundAsset(Assets, "test3/puhp_01.wav"); + EndAssetType(Assets); + + WriteHHA(Assets, "test3.hha"); +} + +int +main(int ArgCount, char **Args) +{ + InitializeFontDC(); + + WriteFonts(); + WriteNonHero(); + WriteHero(); + WriteSounds(); +} diff --git a/test_data/lots_of_files/test_asset_builder.h b/test_data/lots_of_files/test_asset_builder.h new file mode 100644 index 0000000..372fd3b --- /dev/null +++ b/test_data/lots_of_files/test_asset_builder.h @@ -0,0 +1,129 @@ +#if !defined(TEST_ASSET_BUILDER_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> +#include "handmade_platform.h" +#include "handmade_file_formats.h" +#include "handmade_intrinsics.h" +#include "handmade_math.h" + +#define ONE_PAST_MAX_FONT_CODEPOINT (0x10FFFF + 1) + +#define USE_FONTS_FROM_WINDOWS 1 + +#if USE_FONTS_FROM_WINDOWS +#include <windows.h> + +#define MAX_FONT_WIDTH 1024 +#define MAX_FONT_HEIGHT 1024 + +global_variable VOID *GlobalFontBits; +global_variable HDC GlobalFontDeviceContext; + +#else +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" +#endif + +struct loaded_bitmap +{ + int32 Width; + int32 Height; + int32 Pitch; + void *Memory; + + void *Free; +}; + +struct loaded_font +{ + HFONT Win32Handle; + TEXTMETRIC TextMetric; + r32 LineAdvance; + + hha_font_glyph *Glyphs; + r32 *HorizontalAdvance; + + u32 MinCodePoint; + u32 MaxCodePoint; + + u32 MaxGlyphCount; + u32 GlyphCount; + + u32 *GlyphIndexFromCodePoint; + u32 OnePastHighestCodepoint; +}; + +enum asset_type +{ + AssetType_Sound, + AssetType_Bitmap, + AssetType_Font, + AssetType_FontGlyph, +}; + +struct loaded_font; +struct asset_source_font +{ + loaded_font *Font; +}; + +struct asset_source_font_glyph +{ + loaded_font *Font; + u32 Codepoint; +}; + +struct asset_source_bitmap +{ + char *FileName; +}; + +struct asset_source_sound +{ + char *FileName; + u32 FirstSampleIndex; +}; + +struct asset_source +{ + asset_type Type; + union + { + asset_source_bitmap Bitmap; + asset_source_sound Sound; + asset_source_font Font; + asset_source_font_glyph Glyph; + }; +}; + +// TODO(casey): Are there larger numbers than 4096? Do we have evidence +// in the natural world of things that can exist in quantities _larger_ than 4096? +#define VERY_LARGE_NUMBER 4096 // NOTE(casey): 4096 should be enough for anybody + +struct game_assets +{ + u32 TagCount; + hha_tag Tags[VERY_LARGE_NUMBER]; + + u32 AssetTypeCount; + hha_asset_type AssetTypes[Asset_Count]; + + u32 AssetCount; + asset_source AssetSources[VERY_LARGE_NUMBER]; + hha_asset Assets[VERY_LARGE_NUMBER]; + + hha_asset_type *DEBUGAssetType; + u32 AssetIndex; +}; + +#define TEST_ASSET_BUILDER_H +#endif diff --git a/test_data/lots_of_files/time.h b/test_data/lots_of_files/time.h new file mode 100644 index 0000000..41c5c01 --- /dev/null +++ b/test_data/lots_of_files/time.h @@ -0,0 +1,309 @@ +/*** +*time.h - definitions/declarations for time routines +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file has declarations of time routines and defines +* the structure returned by the localtime and gmtime routines and +* used by asctime. +* [ANSI/System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_TIME +#define _INC_TIME + +#include <crtdefs.h> + +#if !defined (_WIN32) +#error ERROR: Only Win32 target supported! +#endif /* !defined (_WIN32) */ + + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef _CRTBLD +#include <cruntime.h> +#endif /* _CRTBLD */ + +#if !defined (_W64) +#if !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) +#define _W64 __w64 +#else /* !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) */ +#define _W64 +#endif /* !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) */ +#endif /* !defined (_W64) */ + +/* Define _CRTIMP */ + +#ifndef _CRTIMP +#if defined(CRTDLL) && defined(_CRTBLD) +#define _CRTIMP __declspec(dllexport) +#else /* defined(CRTDLL) && defined(_CRTBLD) */ +#ifdef _DLL +#define _CRTIMP __declspec(dllimport) +#else /* _DLL */ +#define _CRTIMP +#endif /* _DLL */ +#endif /* defined(CRTDLL) && defined(_CRTBLD) */ +#endif /* _CRTIMP */ + +#ifndef _WCHAR_T_DEFINED +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif /* _WCHAR_T_DEFINED */ + +#ifndef _TIME32_T_DEFINED +typedef _W64 long __time32_t; /* 32-bit time value */ +#define _TIME32_T_DEFINED +#endif /* _TIME32_T_DEFINED */ + +#ifndef _TIME64_T_DEFINED +typedef __int64 __time64_t; /* 64-bit time value */ +#define _TIME64_T_DEFINED +#endif /* _TIME64_T_DEFINED */ + +#ifndef _TIME_T_DEFINED +#ifdef _USE_32BIT_TIME_T +typedef __time32_t time_t; /* time value */ +#else /* _USE_32BIT_TIME_T */ +typedef __time64_t time_t; /* time value */ +#endif /* _USE_32BIT_TIME_T */ +#define _TIME_T_DEFINED /* avoid multiple def's of time_t */ +#endif /* _TIME_T_DEFINED */ + +#ifndef _CLOCK_T_DEFINED +typedef long clock_t; +#define _CLOCK_T_DEFINED +#endif /* _CLOCK_T_DEFINED */ + +#ifndef _SIZE_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 size_t; +#else /* _WIN64 */ +typedef _W64 unsigned int size_t; +#endif /* _WIN64 */ +#define _SIZE_T_DEFINED +#endif /* _SIZE_T_DEFINED */ + +/* Define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + + +#ifndef _TM_DEFINED +struct tm { + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday - [0,6] */ + int tm_yday; /* days since January 1 - [0,365] */ + int tm_isdst; /* daylight savings time flag */ + }; +#define _TM_DEFINED +#endif /* _TM_DEFINED */ + + +/* Clock ticks macro - ANSI version */ + +#define CLOCKS_PER_SEC 1000 + + +/* Extern declarations for the global variables used by the ctime family of + * routines. + */ +#ifdef _CRTBLD +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP int * __cdecl __p__daylight(void); +_CRTIMP long * __cdecl __p__dstbias(void); +_CRTIMP long * __cdecl __p__timezone(void); +_CRTIMP char ** __cdecl __p__tzname(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#endif /* _CRTBLD */ + +/* non-zero if daylight savings time is used */ +_Check_return_ _CRT_INSECURE_DEPRECATE_GLOBALS(_get_daylight) _CRTIMP int* __cdecl __daylight(void); +#define _daylight (*__daylight()) + +/* offset for Daylight Saving Time */ +_Check_return_ _CRT_INSECURE_DEPRECATE_GLOBALS(_get_dstbias) _CRTIMP long* __cdecl __dstbias(void); +#define _dstbias (*__dstbias()) + +/* difference in seconds between GMT and local time */ +_Check_return_ _CRT_INSECURE_DEPRECATE_GLOBALS(_get_timezone) _CRTIMP long* __cdecl __timezone(void); +#define _timezone (*__timezone()) + +/* standard/daylight savings time zone names */ +_Check_return_ _Deref_ret_z_ _CRT_INSECURE_DEPRECATE_GLOBALS(_get_tzname) _CRTIMP char ** __cdecl __tzname(void); +#define _tzname (__tzname()) + +_CRTIMP errno_t __cdecl _get_daylight(_Out_ int * _Daylight); +_CRTIMP errno_t __cdecl _get_dstbias(_Out_ long * _Daylight_savings_bias); +_CRTIMP errno_t __cdecl _get_timezone(_Out_ long * _Timezone); +_CRTIMP errno_t __cdecl _get_tzname(_Out_ size_t *_ReturnValue, _Out_writes_z_(_SizeInBytes) char *_Buffer, _In_ size_t _SizeInBytes, _In_ int _Index); + +#ifdef _CRTBLD +_DEFINE_SET_FUNCTION(_set_daylight, int, _daylight) +_DEFINE_SET_FUNCTION(_set_dstbias, long, _dstbias) +_DEFINE_SET_FUNCTION(_set_timezone, long, _timezone) +#endif /* _CRTBLD */ + +/* Function prototypes */ +_Check_return_ _CRT_INSECURE_DEPRECATE(asctime_s) _CRTIMP char * __cdecl asctime(_In_ const struct tm * _Tm); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP errno_t __cdecl asctime_s(_Out_writes_(_SizeInBytes) _Post_readable_size_(26) char *_Buf, _In_ size_t _SizeInBytes, _In_ const struct tm * _Tm); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, asctime_s, _Post_readable_size_(26) char, _Buffer, _In_ const struct tm *, _Time) + +_CRT_INSECURE_DEPRECATE(_ctime32_s) _CRTIMP char * __cdecl _ctime32(_In_ const __time32_t * _Time); +_CRTIMP errno_t __cdecl _ctime32_s(_Out_writes_(_SizeInBytes) _Post_readable_size_(26) char *_Buf, _In_ size_t _SizeInBytes, _In_ const __time32_t *_Time); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _ctime32_s, _Post_readable_size_(26) char, _Buffer, _In_ const __time32_t *, _Time) + +_Check_return_ _CRTIMP clock_t __cdecl clock(void); +_CRTIMP double __cdecl _difftime32(_In_ __time32_t _Time1, _In_ __time32_t _Time2); + +_Check_return_ _CRT_INSECURE_DEPRECATE(_gmtime32_s) _CRTIMP struct tm * __cdecl _gmtime32(_In_ const __time32_t * _Time); +_Check_return_wat_ _CRTIMP errno_t __cdecl _gmtime32_s(_In_ struct tm *_Tm, _In_ const __time32_t * _Time); + +_CRT_INSECURE_DEPRECATE(_localtime32_s) _CRTIMP struct tm * __cdecl _localtime32(_In_ const __time32_t * _Time); +_CRTIMP errno_t __cdecl _localtime32_s(_Out_ struct tm *_Tm, _In_ const __time32_t * _Time); + +_CRTIMP size_t __cdecl strftime(_Out_writes_z_(_SizeInBytes) char * _Buf, _In_ size_t _SizeInBytes, _In_z_ _Printf_format_string_ const char * _Format, _In_ const struct tm * _Tm); +_CRTIMP size_t __cdecl _strftime_l(_Pre_notnull_ _Post_z_ char *_Buf, _In_ size_t _Max_size, _In_z_ _Printf_format_string_ const char * _Format, _In_ const struct tm *_Tm, _In_opt_ _locale_t _Locale); + +_Check_return_wat_ _CRTIMP errno_t __cdecl _strdate_s(_Out_writes_(_SizeInBytes) _Post_readable_size_(9) char *_Buf, _In_ size_t _SizeInBytes); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _strdate_s, _Post_readable_size_(9) char, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(char *, __RETURN_POLICY_DST, _CRTIMP, _strdate, _Out_writes_z_(9), char, _Buffer) + +_Check_return_wat_ _CRTIMP errno_t __cdecl _strtime_s(_Out_writes_(_SizeInBytes) _Post_readable_size_(9) char *_Buf , _In_ size_t _SizeInBytes); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _strtime_s, _Post_readable_size_(9) char, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(char *, __RETURN_POLICY_DST, _CRTIMP, _strtime, _Out_writes_z_(9), char, _Buffer) + +_CRTIMP __time32_t __cdecl _time32(_Out_opt_ __time32_t * _Time); +_CRTIMP __time32_t __cdecl _mktime32(_Inout_ struct tm * _Tm); +_CRTIMP __time32_t __cdecl _mkgmtime32(_Inout_ struct tm * _Tm); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +_CRTIMP void __cdecl _tzset(void); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP double __cdecl _difftime64(_In_ __time64_t _Time1, _In_ __time64_t _Time2); +_CRT_INSECURE_DEPRECATE(_ctime64_s) _CRTIMP char * __cdecl _ctime64(_In_ const __time64_t * _Time); +_CRTIMP errno_t __cdecl _ctime64_s(_Out_writes_z_(_SizeInBytes) char *_Buf, _In_ size_t _SizeInBytes, _In_ const __time64_t * _Time); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _ctime64_s, char, _Buffer, _In_ const __time64_t *, _Time) + +_CRT_INSECURE_DEPRECATE(_gmtime64_s) _CRTIMP struct tm * __cdecl _gmtime64(_In_ const __time64_t * _Time); +_CRTIMP errno_t __cdecl _gmtime64_s(_Out_ struct tm *_Tm, _In_ const __time64_t *_Time); + +_CRT_INSECURE_DEPRECATE(_localtime64_s) _CRTIMP struct tm * __cdecl _localtime64(_In_ const __time64_t * _Time); +_CRTIMP errno_t __cdecl _localtime64_s(_Out_ struct tm *_Tm, _In_ const __time64_t *_Time); + +_CRTIMP __time64_t __cdecl _mktime64(_Inout_ struct tm * _Tm); +_CRTIMP __time64_t __cdecl _mkgmtime64(_Inout_ struct tm * _Tm); +_CRTIMP __time64_t __cdecl _time64(_Out_opt_ __time64_t * _Time); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +/* The Win32 API GetLocalTime and SetLocalTime should be used instead. */ +_CRT_OBSOLETE(GetLocalTime) unsigned __cdecl _getsystime(_Out_ struct tm * _Tm); +_CRT_OBSOLETE(SetLocalTime) unsigned __cdecl _setsystime(_In_ struct tm * _Tm, unsigned _MilliSec); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + +#ifndef _SIZE_T_DEFINED +typedef unsigned int size_t; +#define _SIZE_T_DEFINED +#endif /* _SIZE_T_DEFINED */ + +#ifndef _WTIME_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +_CRT_INSECURE_DEPRECATE(_wasctime_s) _CRTIMP wchar_t * __cdecl _wasctime(_In_ const struct tm * _Tm); +_CRTIMP errno_t __cdecl _wasctime_s(_Out_writes_(_SizeInWords) _Post_readable_size_(26) wchar_t *_Buf, _In_ size_t _SizeInWords, _In_ const struct tm * _Tm); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wasctime_s, _Post_readable_size_(26) wchar_t, _Buffer, _In_ const struct tm *, _Time) + +_CRT_INSECURE_DEPRECATE(_wctime32_s) _CRTIMP wchar_t * __cdecl _wctime32(_In_ const __time32_t *_Time); +_CRTIMP errno_t __cdecl _wctime32_s(_Out_writes_(_SizeInWords) _Post_readable_size_(26) wchar_t* _Buf, _In_ size_t _SizeInWords, _In_ const __time32_t * _Time); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wctime32_s, _Post_readable_size_(26) wchar_t, _Buffer, _In_ const __time32_t *, _Time) + +_CRTIMP size_t __cdecl wcsftime(_Out_writes_z_(_SizeInWords) wchar_t * _Buf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, _In_ const struct tm * _Tm); +_CRTIMP size_t __cdecl _wcsftime_l(_Out_writes_z_(_SizeInWords) wchar_t * _Buf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t *_Format, _In_ const struct tm *_Tm, _In_opt_ _locale_t _Locale); + +_CRTIMP errno_t __cdecl _wstrdate_s(_Out_writes_(_SizeInWords) _Post_readable_size_(9) wchar_t * _Buf, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wstrdate_s, _Post_readable_size_(9) wchar_t, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wstrdate, _Out_writes_z_(9), wchar_t, _Buffer) + +_CRTIMP errno_t __cdecl _wstrtime_s(_Out_writes_(_SizeInWords) _Post_readable_size_(9) wchar_t * _Buf, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wstrtime_s, _Post_readable_size_(9) wchar_t, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wstrtime, _Out_writes_z_(9), wchar_t, _Buffer) + +_CRT_INSECURE_DEPRECATE(_wctime64_s) _CRTIMP wchar_t * __cdecl _wctime64(_In_ const __time64_t * _Time); +_CRTIMP errno_t __cdecl _wctime64_s(_Out_writes_(_SizeInWords) _Post_readable_size_(26) wchar_t* _Buf, _In_ size_t _SizeInWords, _In_ const __time64_t *_Time); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wctime64_s, _Post_readable_size_(26) wchar_t, _Buffer, _In_ const __time64_t *, _Time) + +#if !defined (RC_INVOKED) && !defined (__midl) +#include <wtime.inl> +#endif /* !defined (RC_INVOKED) && !defined (__midl) */ + +#define _WTIME_DEFINED +#endif /* _WTIME_DEFINED */ + +#if !defined (RC_INVOKED) && !defined (__midl) +#include <time.inl> +#endif /* !defined (RC_INVOKED) && !defined (__midl) */ + +#if !__STDC__ + +/* Non-ANSI names for compatibility */ + +#define CLK_TCK CLOCKS_PER_SEC + +/* +daylight, timezone, and tzname are not available under /clr:pure. +Please use _daylight, _timezone, and _tzname or +_get_daylight, _get_timezone, and _get_tzname instead. +*/ +#if !defined (_M_CEE_PURE) +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_daylight) _CRTIMP extern int daylight; +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_timezone) _CRTIMP extern long timezone; +_CRT_INSECURE_DEPRECATE_GLOBALS(_get_tzname) _CRTIMP extern char * tzname[2]; +#endif /* !defined (_M_CEE_PURE) */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRT_NONSTDC_DEPRECATE(_tzset) _CRTIMP void __cdecl tzset(void); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#endif /* !__STDC__ */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_TIME */ diff --git a/test_data/lots_of_files/tmmintrin.h b/test_data/lots_of_files/tmmintrin.h new file mode 100644 index 0000000..2368a9f --- /dev/null +++ b/test_data/lots_of_files/tmmintrin.h @@ -0,0 +1,113 @@ +/*** +*** Copyright (C) 1985-2005 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_TMM +#define _INCLUDED_TMM + +#if defined (_M_CEE_PURE) + #error ERROR: XMM intrinsics not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <pmmintrin.h> + +#ifdef _MM2_FUNCTIONALITY +/* support old notation */ +#ifndef _MM_FUNCTIONALITY +#define _MM_FUNCTIONALITY +#endif /* _MM_FUNCTIONALITY */ +#endif /* _MM2_FUNCTIONALITY */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + // Add horizonally packed [saturated] words, double words, + // {X,}MM2/m{128,64} (b) to {X,}MM1 (a). + + extern __m128i _mm_hadd_epi16 (__m128i a, __m128i b); + extern __m128i _mm_hadd_epi32 (__m128i a, __m128i b); + extern __m128i _mm_hadds_epi16 (__m128i a, __m128i b); + + extern __m64 _mm_hadd_pi16 (__m64 a, __m64 b); + extern __m64 _mm_hadd_pi32 (__m64 a, __m64 b); + extern __m64 _mm_hadds_pi16 (__m64 a, __m64 b); + + // Subtract horizonally packed [saturated] words, double words, + // {X,}MM2/m{128,64} (b) from {X,}MM1 (a). + + extern __m128i _mm_hsub_epi16 (__m128i a, __m128i b); + extern __m128i _mm_hsub_epi32 (__m128i a, __m128i b); + extern __m128i _mm_hsubs_epi16 (__m128i a, __m128i b); + + extern __m64 _mm_hsub_pi16 (__m64 a, __m64 b); + extern __m64 _mm_hsub_pi32 (__m64 a, __m64 b); + extern __m64 _mm_hsubs_pi16 (__m64 a, __m64 b); + + // Multiply and add packed words, + // {X,}MM2/m{128,64} (b) to {X,}MM1 (a). + + extern __m128i _mm_maddubs_epi16 (__m128i a, __m128i b); + + extern __m64 _mm_maddubs_pi16 (__m64 a, __m64 b); + + // Packed multiply high integers with round and scaling, + // {X,}MM2/m{128,64} (b) to {X,}MM1 (a). + + extern __m128i _mm_mulhrs_epi16 (__m128i a, __m128i b); + + extern __m64 _mm_mulhrs_pi16 (__m64 a, __m64 b); + + // Packed shuffle bytes + // {X,}MM2/m{128,64} (b) by {X,}MM1 (a). + + extern __m128i _mm_shuffle_epi8 (__m128i a, __m128i b); + + extern __m64 _mm_shuffle_pi8 (__m64 a, __m64 b); + + // Packed byte, word, double word sign, {X,}MM2/m{128,64} (b) to + // {X,}MM1 (a). + + extern __m128i _mm_sign_epi8 (__m128i a, __m128i b); + extern __m128i _mm_sign_epi16 (__m128i a, __m128i b); + extern __m128i _mm_sign_epi32 (__m128i a, __m128i b); + + extern __m64 _mm_sign_pi8 (__m64 a, __m64 b); + extern __m64 _mm_sign_pi16 (__m64 a, __m64 b); + extern __m64 _mm_sign_pi32 (__m64 a, __m64 b); + + // Packed align and shift right by n*8 bits, + // {X,}MM2/m{128,64} (b) to {X,}MM1 (a). + + extern __m128i _mm_alignr_epi8 (__m128i a, __m128i b, int n); + + extern __m64 _mm_alignr_pi8 (__m64 a, __m64 b, int n); + + // Packed byte, word, double word absolute value, + // {X,}MM2/m{128,64} (b) to {X,}MM1 (a). + + extern __m128i _mm_abs_epi8 (__m128i a); + extern __m128i _mm_abs_epi16 (__m128i a); + extern __m128i _mm_abs_epi32 (__m128i a); + + extern __m64 _mm_abs_pi8 (__m64 a); + extern __m64 _mm_abs_pi16 (__m64 a); + extern __m64 _mm_abs_pi32 (__m64 a); + +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INCLUDED_TMM */ + +#endif /* __midl */ diff --git a/test_data/lots_of_files/tool_ttf_to_bmp.c b/test_data/lots_of_files/tool_ttf_to_bmp.c new file mode 100644 index 0000000..7099b00 --- /dev/null +++ b/test_data/lots_of_files/tool_ttf_to_bmp.c @@ -0,0 +1,161 @@ +/* + +In order to avoid run-time font interpretation (expensive) +and potential copy right infringement (actually expensive) +it is best for us bake fonts into prepacked bitmaps. + +I will output a font as a specialized format that includes +the font as a bitmap format texture atlas and the metadata +for rendering the font. + +-Allen +15.07.2016 + +*/ + + +// TOP + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define FSTRING_INLINE static +#define FSTRING_IMPLEMENTATION +//#include "4tech_string.h" + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +struct Get_Files_Result{ + FILE *src; + FILE *dst; + int32_t successful_open; +}; + +static struct Get_Files_Result +get_files(char *source_file_name){ + struct Get_Files_Result result = {0}; + + char *file_name_base = source_file_name; + String file_name = make_string_slowly(file_name_base); + + String file_name_ext = file_extension(file_name); + + if (match_sc(file_name_ext, "ttf")){ + FILE *source_file = fopen(file_name_base, "rb"); + + if (source_file){ + int32_t dest_file_name_size = file_name.size*2 + 1; + char *dest_file_name_base = (char*)malloc(dest_file_name_size); + String dest_file_name = make_string_max(dest_file_name_base, 0, dest_file_name_size); + + copy_ss(&dest_file_name, file_name); + remove_extension(&dest_file_name); + append_sc(&dest_file_name, ".bpft"); + + if (terminate_with_null(&dest_file_name)){ + FILE *dest_file = fopen(dest_file_name_base, "wb"); + + if (dest_file){ + result.src = source_file; + result.dst = dest_file; + result.successful_open = 1; + } + } + + free(dest_file_name_base); + + if (!result.successful_open){ + fclose(source_file); + } + } + } + + return(result); +} + +static String +dump_file(FILE *file){ + String result = {0}; + + if (file){ + int32_t pos = ftell(file); + fseek(file, 0, SEEK_END); + result.size = ftell(file); + + result.memory_size = result.size + 1; + result.str = (char*)malloc(result.memory_size); + + if (result.str){ + fseek(file, 0, SEEK_SET); + fread(result.str, 1, result.size, file); + result.str[result.size] = 0; + } + else{ + result.size = 0; + result.memory_size = 0; + } + + fseek(file, pos, SEEK_SET); + } + + return(result); +} + +struct Glyph_Meta_Data{ + uint16_t x0; + uint16_t y0; + uint16_t x1; + uint16_t y1; + float xoff; + float yoff; + float advance; +}; + +struct Font_Meta_Data{ + struct Glyph_Meta_Data glyphdata[128]; + float pull_up; + int32_t texture_w; + int32_t texture_h; +}; + +int main(int argc, char **argv){ + if (argc != 2){ + printf("usage:\n\t%s <filename>\n", argv[0]); + exit(1); + } + + struct Get_Files_Result files = get_files(argv[1]); + + if (files.successful_open){ + String src_data = dump_file(files.src); + + if (src_data.str){ + stbtt_fontinfo fontinfo = {0}; + int32_t ascent = 0; + float scale = 0; + float pixel_height = 16.f; + + stbtt_InitFont(&fontinfo, src_data.str, 0); + scale = stbtt_ScaleForPixelHeight(&fontinfo, pixel_height); + stbtt_GetFontVMetrics(&fontinfo, &ascent, 0, 0); + + + } + + fclose(files.dst); + fclose(files.src); + } + else{ + printf("error: opening files for the operation\n"); + exit(1); + } + + return(0); +} + + +// BOTTOM + diff --git a/test_data/lots_of_files/tree_gen.cpp b/test_data/lots_of_files/tree_gen.cpp new file mode 100644 index 0000000..7eb7091 --- /dev/null +++ b/test_data/lots_of_files/tree_gen.cpp @@ -0,0 +1,526 @@ +/* + * Allen's Fancy Tree Generator + * 22.08.2015 (dd.mm.yyyy) + */ + +// TOP + +internal +split_point(real32 base_radius, real32 branch_radius, i32 branch_n, i32 i){ + real32 branch_dist = branch_radius*2.f; + Vec2 result = {}; + switch (branch_n){ + case 1: + { + if (i == 0) result.x = -base_radius; + else result.x = base_radius; + }break; + + case 2: + { + real32 a,b,c; + a = base_radius; + c = branch_dist; + a *= a; + c *= c; + b = c - a; + b = SQRT(b); + switch (i){ + case 0: result.x = -base_radius; result.y = b*.5f; break; + case 1: result.x = 0; result.y = -b*.5f; break; + case 2: result.x = base_radius; result.y = b*.5f; break; + } + }break; + + case 3: + { + real32 a,b,c,aa,cc; + a = base_radius - branch_dist*.5f; + c = branch_dist; + aa = a*a; + cc = c*c; + b = cc - aa; + b = SQRT(b); + switch (i){ + case 0: result.x = -base_radius; result.y = b*.5f; break; + case 1: result.x = a-base_radius; result.y = -b*.5f; break; + case 2: result.x = base_radius-a; result.y = -b*.5f; break; + case 3: result.x = base_radius; result.y = b*.5f; break; + } + }break; + } + + return result; +} + +struct Hot_Spot{ + Vec2 spot; +}; + +struct Tree_Parameters{ + Vec2 base; + i32 levels; + real32 initial_step; + real32 step_decay; + real32 initial_thickness; + real32 thickness_decay; + real32 dtheta; + real32 min_weight_threshold; + real32 center_branch_weight; + real32 heat_half_distance; + + Hot_Spot *hot_spots; + i32 hot_spot_count; +}; + +struct Tree_Skeleton_Vert{ + Vec2 pos; + i32 child_count; + i32 children[3]; + real32 theta; + real32 width; + Vec4 paint; +}; + +struct Skeleton_Branch_Point{ + i32 vert_id; + real32 step; + real32 theta; + real32 width; + i32 depth; +}; + +internal Skeleton_Branch_Point +SBP(i32 id, real32 step, real32 theta, real32 width, i32 depth){ + Skeleton_Branch_Point result; + result.vert_id = id; + result.step = step; + result.theta = theta; + result.width = width; + result.depth = depth; + return result; +} + +struct r32_Range{ + real32 start, end; +}; + +inline r32_Range +get_range(real32 a, real32 b){ + r32_Range result; + if (a < b){ + result.start = a; + result.end = b; + } + else{ + result.start = b; + result.end = a; + } + return result; +} + +persist Vec4 brown = {.5f, .3333f, 0.f, 1.f}; +persist Vec4 silver = {.7f, .7f, .7f, 1.f}; +persist Vec4 faded_gold = {.7f, .4f, .2f, 0.5f}; +persist Vec4 sky_blue = {0.f, .7f, 1.f, 1.f}; + +internal bool32 +segments_collide(Vec2 a1, Vec2 a2, Vec2 b1, Vec2 b2, Vec2 *p_out = 0){ + bool32 result = 1; + + Vec3 r1, r2; + if (a2.y != a1.y){ + r1.x = (a2.y - a1.y); + r1.y = (a1.x - a2.x); + r1.z = (a1.x*r1.x + a1.y*r1.y); + + r2.x = (b2.y - b1.y); + r2.y = (b1.x - b2.x); + r2.z = (b1.x*r2.x + b1.y*r2.y); + } + else{ + r1.x = (b2.y - b1.y); + r1.y = (b1.x - b2.x); + r1.z = (b1.x*r1.x + b1.y*r1.y); + + r2.x = (a2.y - a1.y); + r2.y = (a1.x - a2.x); + r2.z = (a1.x*r2.x + a1.y*r2.y); + } + + if (r1.x == 0){ + result = (a1.y == b1.y) && ((b1.x <= a1.x && a1.x < b2.x) || (a1.x <= b1.x && b1.x < a2.x)); + return result; + } + + if (r2.x != 0){ + r2.y /= r2.x; + r2.z /= r2.x; + r2.y *= r1.x; + r2.z *= r1.x; + r2.y -= r1.y; + r2.z -= r1.z; + r2.x = 0; + } + + if (r2.y == 0){ + result = (r2.z == 0) && ((b1.y <= a1.y && a1.y < b2.y) || (a1.y <= b1.y && b1.y < a2.y)); + return result; + } + + if (r1.y != 0){ + r1.x /= r1.y; + r1.z /= r1.y; + r1.x *= r2.y; + r1.z *= r2.y; + r1.x -= r2.x; + r1.z -= r2.z; + r1.y = 0; + } + + assert(!(r1.x == 0 || r2.y == 0)); + + r1 *= 1.f / r1.x; + r2 *= 1.f / r2.y; + + Vec2 cl; + cl.x = r1.z; + cl.y = r2.z; + if (p_out) *p_out = cl; + + real32 epsilon = .0001f; + real32 neg_epsilon = -epsilon; + real32 xdif; + + xdif = a1.x - a2.x; + if (xdif < neg_epsilon && xdif > epsilon){ + r32_Range range = get_range(a1.x, a2.x); + if (cl.x < range.start || cl.x > range.end){ + result = 0; + } + } + else{ + r32_Range range = get_range(a1.y, a2.y); + if (cl.y < range.start || cl.y > range.end){ + result = 0; + } + } + + xdif = a1.x - a2.x; + if (xdif < neg_epsilon && xdif > epsilon){ + r32_Range range = get_range(b1.x, b2.x); + if (cl.x < range.start || cl.x > range.end){ + result = 0; + } + } + else{ + r32_Range range = get_range(b1.y, b2.y); + if (cl.y < range.start || cl.y > range.end){ + result = 0; + } + } + + return result; +} + +internal bool32 +no_collisions(Vec2 base, Vec2 new_pos, Tree_Skeleton_Vert *vertices){ + bool32 result = 1; + i32 vert_id_stack[16]; + i32 top = 0; + vert_id_stack[top++] = 0; + + while (top > 0){ + Tree_Skeleton_Vert *vert = vertices + vert_id_stack[--top]; + i32 count = vert->child_count; + i32 *children = vert->children; + Vec2 c = vert->pos; + for (i32 i = 0; i < count; ++i){ + i32 child_id = children[i]; + Tree_Skeleton_Vert *other_vert = vertices + child_id; + Vec2 d = other_vert->pos; + bool32 exempt = (base.x == d.x) && (base.y == d.y); + if (!exempt) exempt = (base.x == c.x) && (base.y == c.y); + if (!exempt && segments_collide(base, new_pos, c, d)){ + other_vert->paint = sky_blue; + result = 0; + goto no_collisions_end; + } + else{ + vert_id_stack[top++] = child_id; + } + } + } + no_collisions_end: + return result; +} + +struct Branch_Info{ + i32 which_branch; + + real32 step; + real32 length; + real32 contribution_list[3]; + i32 contribution_count; +}; + +enum Info_To_Post{ + Step_Info, + Length_Info, + Contribution_Info, + Count_Info +}; + +internal void +post_branch_info(Branch_Info *info, i32 count, i32 index, Info_To_Post type, real32 x){ + i32 i; + for (i = 0; i < count; ++i, ++info){ + if (index == info->which_branch){ + switch (type){ + case Step_Info: info->step = x; break; + case Length_Info: info->length = x; break; + case Contribution_Info: + if (info->contribution_count < ArrayCount(info->contribution_list)){ + info->contribution_list[info->contribution_count++] = x; + }break; + } + break; + } + } +} + +internal i32 +tree_skeleton_gen(Tree_Skeleton_Vert *vertices, i32 vertex_count, i32 *vertex_count_out, + Tree_Parameters params, i32 limit, bool32 allow_colliding, + Branch_Info *opt_output, i32 opt_output_count){ + + Skeleton_Branch_Point queue[730]; + i32 queue_wrap = ArrayCount(queue); + i32 write_pos = 0; + i32 read_pos = 0; + i32 k = 0; + + vertices[k].pos = params.base; + queue[write_pos++] = + SBP(k, params.initial_step, 270.f, params.initial_thickness, 0); + if (opt_output){ + post_branch_info(opt_output, opt_output_count, k, Step_Info, params.initial_step); + post_branch_info(opt_output, opt_output_count, k, Length_Info, params.initial_step); + } + ++k; + + i32 I = 0; + for (; read_pos != write_pos; ){ + Skeleton_Branch_Point *bp = queue + read_pos; + ++read_pos; + read_pos = read_pos % queue_wrap; + + Tree_Skeleton_Vert *vert = vertices + bp->vert_id; + vert->theta = bp->theta; + vert->width = bp->width; + if (vert->paint.r == 0 && vert->paint.g == 0 && vert->paint.b == 0){ + if (allow_colliding) vert->paint = faded_gold; + else vert->paint = silver; + } + + i32 *index_order = 0; + persist i32 index_order_case_1[] = {0}; + persist i32 index_order_case_3[] = {1, 0, 2}; + + i32 branch_count; + if (bp->depth == 0){ + branch_count = 1; + index_order = index_order_case_1; + }else if (bp->depth < params.levels){ + branch_count = 3; + index_order = index_order_case_3; + }else{ + branch_count = 0; + } + + Vec2 p = vert->pos; + real32 theta_base = bp->theta - (branch_count - 1) * params.dtheta * .5f; + i32 actual_branch_count = 0; + _Assert(vert->children[0] == 0); + _Assert(vert->children[1] == 0); + _Assert(vert->children[2] == 0); + + bool32 block_others = 0; + for (i32 ii = 0; ii < branch_count && !block_others; ++ii){ + i32 i = index_order[ii]; + + real32 theta = theta_base + params.dtheta * i; + real32 default_step = bp->step; +#if 0 + Vec2 towards_new_p = polar_to_cartesian(theta, default_step); + f32 weighted_theta = theta; + f32 weight = 0.f; + i32 contribution_count = 0; + for (i32 j = 0; j < params.hot_spot_count; ++j){ + Vec2 towards_hot_spot = params.hot_spots[j] - p; + real32 projection = vector_projection(towards_new_p, towards_hot_spot); + if (projection > 0.f){ + weight += projection; + ++contribution_count; + } + if (opt_output){ + post_branch_info(opt_output, opt_output_count, k, Contribution_Info, weight); + } + } + if (theta >= bp->theta - 0.001f && theta <= bp->theta + 0.001f){ + //weight *= params.center_branch_weight; + } + if (contribution_count != 0){ + weight /= contribution_count; + } + +#elif 1 + f32 weight = default_step; + f32 weighted_theta = theta; + + f32 average_arg = 0.f, total_weight = 0.f; + + Vec2 d_vec; + f32 arg, d, local_weight; + Hot_Spot *spot = params.hot_spots; + for (i32 j = 0; j < params.hot_spot_count; ++j, ++spot){ + d_vec = spot->spot - p; + arg = argument_degrees(d_vec); + d = SQRT(dot(d_vec, d_vec)); + d -= default_step; + if (d > 0 && d < params.heat_half_distance * 5.f){ + local_weight = POW(2.f, -d / params.heat_half_distance); + total_weight += local_weight; + average_arg += (arg*local_weight); + } + } + + if (total_weight > 0){ + average_arg /= total_weight; + weighted_theta = average_arg; + + if (weighted_theta < 0) weighted_theta += 360.f; + + f32 max_theta, min_theta; + max_theta = theta + 20.f; + min_theta = theta - 20.f; + + if (min_theta < 0) min_theta += 360.f; + if (max_theta < min_theta){ + max_theta += 360.f; + } + weighted_theta = Max(min_theta, Min(max_theta, weighted_theta)); + + if (weighted_theta >= 360.f) weighted_theta -= 360.f; + } + +#else + f32 weight = default_step; + f32 weighted_theta = theta; +#endif + + if (weight > params.min_weight_threshold){ + Vec2 new_p = p + polar_to_cartesian(weighted_theta, weight); + + if (I < limit && (allow_colliding || no_collisions(p, new_p, vertices))){ + ++I; + + _Assert(k < vertex_count); + vert->children[i] = k; + ++actual_branch_count; + vertices[k].pos = new_p; + // TODO(allen): should the new length be based on bp->step or on this branch's specific weight? + queue[write_pos++] = + SBP(k, bp->step * params.step_decay, theta, bp->width * params.thickness_decay, bp->depth + 1); + write_pos = write_pos % queue_wrap; + _Assert(write_pos != read_pos); + + if (opt_output){ + post_branch_info(opt_output, opt_output_count, k, Step_Info, bp->step); + post_branch_info(opt_output, opt_output_count, k, Length_Info, weight); + } + + ++k; + } + else{ + if (ii == 0) block_others = 1; + } + } + else{ + if (ii == 0) block_others = 1; + } + } + vert->child_count = actual_branch_count; + i32 i, j; + for (i = 0, j = 0; i < 3; ++i){ + if (vert->children[i] != 0){ + vert->children[j] = vert->children[i]; + ++j; + } + } + _Assert(j == actual_branch_count); + for (; j < 3; ++j){ + vert->children[j] = 0; + } + } + *vertex_count_out = k; + + return I; +} + +struct Thickness_Point{ + i32 id; + i32 next_child; +}; + +internal Thickness_Point +TP(i32 id){ + Thickness_Point result; + result.id = id; + result.next_child = 0; + return result; +} + +internal void +tree_gen(Vec2 *vertices, i32 vertex_count, i32 *vertex_count_out, Tree_Parameters params, + Tree_Skeleton_Vert *skeleton){ + Thickness_Point stack[16]; + i32 top = 0; + i32 k = 0; + + stack[top++] = TP(0); + + while (top > 0){ + Thickness_Point *tp = stack + top - 1; + + Tree_Skeleton_Vert *vert = skeleton + tp->id; + + { + Vec2 p = vert->pos; + Vec2 q = split_point(vert->width, vert->width * params.thickness_decay, vert->child_count, tp->next_child); + vertices[k++] = p + rotate(q, vert->theta - 270.f); + } + + bool32 done = 0; + if (tp->next_child == 3){ + done = 1; + } + else{ + i32 child = vert->children[tp->next_child]; + if (child){ + stack[top++] = TP(child); + ++tp->next_child; + } + else{ + done = 1; + } + } + if (done){ + --top; + } + } + *vertex_count_out = k; +} + +// BOTTOM + diff --git a/test_data/lots_of_files/typeinfo.h b/test_data/lots_of_files/typeinfo.h new file mode 100644 index 0000000..ab5ba94 --- /dev/null +++ b/test_data/lots_of_files/typeinfo.h @@ -0,0 +1,115 @@ +/*** +*typeinfo.h - Defines the type_info structure and exceptions used for RTTI +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Defines the type_info structure and exceptions used for +* Runtime Type Identification. +* +* [Public] +* +****/ + +#pragma once + +#include <crtdefs.h> + +#ifndef _INC_TYPEINFO +#define _INC_TYPEINFO + +#pragma pack(push,_CRT_PACKING) + +#ifndef RC_INVOKED + +#ifndef __cplusplus +#error This header requires a C++ compiler ... +#endif /* __cplusplus */ + +#ifndef _SYSCRT +#include <typeinfo> + +#if !defined(_CRTBLD) || !defined(_TICORE) + +#ifdef __RTTI_OLDNAMES +/* Some synonyms for folks using older standard */ +using std::bad_cast; +using std::bad_typeid; + +typedef type_info Type_info; +typedef bad_cast Bad_cast; +typedef bad_typeid Bad_typeid; +#endif /* __RTTI_OLDNAMES */ + +#endif /* !defined(_CRTBLD) || !defined(_TICORE) */ + +#else /* _SYSCRT */ + +class type_info { +public: + SECURITYCRITICAL_ATTRIBUTE + _CRTIMP virtual __thiscall ~type_info(); + _CRTIMP int __thiscall operator==(_In_ const type_info& _Rhs) const; + _CRTIMP int __thiscall operator!=(_In_ const type_info& _Rhs) const; + _CRTIMP bool __thiscall before(_In_ const type_info& _Rhs) const; +#ifdef _SYSCRT + _Check_return_ _CRTIMP const char* __thiscall name() const; +#else /* _SYSCRT */ + _Check_return_ _CRTIMP const char* __thiscall name(_Inout_ __type_info_node* __ptype_info_node = &__type_info_root_node) const; +#endif /* _SYSCRT */ + _Check_return_ _CRTIMP const char* __thiscall raw_name() const; +private: + void *_M_data; + char _M_d_name[1]; + __thiscall type_info(_In_ const type_info& _Rhs); + type_info& __thiscall operator=(_In_ const type_info& _Rhs); +}; +#ifndef _TICORE + +/* This include must occur below the definition of class type_info */ +#include <stdexcpt.h> + +class _CRTIMP bad_cast : public std::exception { +public: + __CLR_OR_THIS_CALL bad_cast(_In_z_ const char * _Message = "bad cast"); + __CLR_OR_THIS_CALL bad_cast(_In_ const bad_cast & _Bad_cast); + virtual __CLR_OR_THIS_CALL ~bad_cast(); + +#ifdef CRTDLL +private: + /* This is aliased to public:bad_cast(const char * const &) to provide */ + /* the old, non-conformant constructor. */ + bad_cast(_In_z_ const char * const * _Message); +#endif /* CRTDLL */ +}; + +class _CRTIMP bad_typeid : public std::exception { +public: + bad_typeid(_In_z_ const char * _Message = "bad typeid"); + bad_typeid(_In_ const bad_typeid &); + virtual ~bad_typeid(); +}; + +class _CRTIMP __non_rtti_object : public bad_typeid { +public: + __non_rtti_object(_In_z_ const char * _Message); + __non_rtti_object(_In_ const __non_rtti_object &); + virtual ~__non_rtti_object(); +}; + +#ifdef __RTTI_OLDNAMES +/* Some synonyms for folks using older standard */ +typedef type_info Type_info; +typedef bad_cast Bad_cast; +typedef bad_typeid Bad_typeid; +#endif /* __RTTI_OLDNAMES */ + +#endif /* _TICORE */ + +#endif /* _SYSCRT */ + +#endif /* RC_INVOKED */ + +#pragma pack(pop) + +#endif /* _INC_TYPEINFO */ diff --git a/test_data/lots_of_files/use_ansi.h b/test_data/lots_of_files/use_ansi.h new file mode 100644 index 0000000..914ca58 --- /dev/null +++ b/test_data/lots_of_files/use_ansi.h @@ -0,0 +1,68 @@ +/*** +*use_ansi.h - pragmas for ANSI Standard C++ libraries +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This header is intended to force the use of the appropriate ANSI +* Standard C++ libraries whenever it is included. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _USE_ANSI_CPP +#define _USE_ANSI_CPP + +#ifdef _CRTBLD +#define _CRT_NOPRAGMA_LIBS +#else +#undef _CRT_NOPRAGMA_LIBS +#endif + +#ifndef _CRT_NOPRAGMA_LIBS + +#if !defined(_M_CEE_PURE) && !defined(MRTDLL) + +#undef _DEBUG_AFFIX +#undef _IDL_AFFIX +#undef _IDL_DEFAULT +#undef _LIB_STEM + +#ifdef _DEBUG + #define _DEBUG_AFFIX "d" + #define _IDL_DEFAULT 2 +#else + #define _DEBUG_AFFIX "" + #define _IDL_DEFAULT 0 +#endif + +#if defined(_DLL) && !defined(_STATIC_CPPLIB) + #define _LIB_STEM "msvcprt" +#else + #define _LIB_STEM "libcpmt" + + #if _ITERATOR_DEBUG_LEVEL != _IDL_DEFAULT + #define _IDL_AFFIX _STRINGIZE(_ITERATOR_DEBUG_LEVEL) + #endif +#endif + +#ifdef _IDL_AFFIX +#else + #define _IDL_AFFIX "" +#endif + +#pragma comment(lib, _LIB_STEM _DEBUG_AFFIX _IDL_AFFIX) + +#undef _DEBUG_AFFIX +#undef _IDL_AFFIX +#undef _IDL_DEFAULT +#undef _LIB_STEM + +#endif /* !defined(_M_CEE_PURE) && !defined(MRTDLL) */ + +#endif /* _CRT_NOPRAGMA_LIBS */ + +#endif /* _USE_ANSI_CPP */ diff --git a/test_data/lots_of_files/vadefs.h b/test_data/lots_of_files/vadefs.h new file mode 100644 index 0000000..60db75f --- /dev/null +++ b/test_data/lots_of_files/vadefs.h @@ -0,0 +1,146 @@ +/*** +*vadefs.h - defines helper macros for stdarg.h +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This is a helper file for stdarg.h +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_VADEFS +#define _INC_VADEFS + +#if !defined (_WIN32) +#error ERROR: Only Win32 target supported! +#endif /* !defined (_WIN32) */ + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#undef _CRT_PACKING +#define _CRT_PACKING 8 +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef _CRTBLD +#include <cruntime.h> +#endif /* _CRTBLD */ + +#if !defined (_W64) +#if !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) +#define _W64 __w64 +#else /* !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) */ +#define _W64 +#endif /* !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) */ +#endif /* !defined (_W64) */ + +#ifndef _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else /* _WIN64 */ +typedef _W64 unsigned int uintptr_t; +#endif /* _WIN64 */ +#define _UINTPTR_T_DEFINED +#endif /* _UINTPTR_T_DEFINED */ + +#ifndef _VA_LIST_DEFINED +#ifdef _M_CEE_PURE +typedef System::ArgIterator va_list; +#else /* _M_CEE_PURE */ +typedef char * va_list; +#endif /* _M_CEE_PURE */ +#define _VA_LIST_DEFINED +#endif /* _VA_LIST_DEFINED */ + +#ifdef __cplusplus +#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) ) +#else /* __cplusplus */ +#define _ADDRESSOF(v) ( &(v) ) +#endif /* __cplusplus */ + +#if defined (_M_ARM) && !defined (_M_CEE_PURE) +#define _VA_ALIGN 4 +#define _SLOTSIZEOF(t) ( (sizeof(t) + _VA_ALIGN - 1) & ~(_VA_ALIGN - 1) ) + +#define _APALIGN(t,ap) ( ((va_list)0 - (ap)) & (__alignof(t) - 1) ) + +#else /* defined (_M_ARM) && !defined (_M_CEE_PURE) */ +#define _SLOTSIZEOF(t) (sizeof(t)) +#define _APALIGN(t,ap) (__alignof(t)) +#endif /* defined (_M_ARM) && !defined (_M_CEE_PURE) */ + +#if defined (_M_CEE_PURE) || (defined (_M_CEE) && !defined (_M_ARM)) + +extern void __cdecl __va_start(va_list*, ...); +extern void * __cdecl __va_arg(va_list*, ...); +extern void __cdecl __va_end(va_list*); + +#define _crt_va_start(ap,v) ( __va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), \ + __alignof(v), _ADDRESSOF(v)) ) +#define _crt_va_arg(ap,t) ( *(t *)__va_arg(&ap, _SLOTSIZEOF(t), \ + _APALIGN(t,ap), (t *)0) ) +#define _crt_va_end(ap) ( __va_end(&ap) ) + +#elif defined (_M_IX86) + +#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) + +#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) +#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) +#define _crt_va_end(ap) ( ap = (va_list)0 ) + +#elif defined (_M_ARM) + +#ifdef __cplusplus +extern void __cdecl __va_start(va_list*, ...); +#define _crt_va_start(ap,v) ( __va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), \ + _ADDRESSOF(v)) ) +#else /* __cplusplus */ +#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _SLOTSIZEOF(v) ) +#endif /* __cplusplus */ + +#define _crt_va_arg(ap,t) (*(t *)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) \ + - _SLOTSIZEOF(t))) + +#define _crt_va_end(ap) ( ap = (va_list)0 ) + +#elif defined (_M_X64) + + +extern void __cdecl __va_start(va_list *, ...); + +#define _crt_va_start(ap, x) ( __va_start(&ap, x) ) +#define _crt_va_arg(ap, t) \ + ( ( sizeof(t) > sizeof(__int64) || ( sizeof(t) & (sizeof(t) - 1) ) != 0 ) \ + ? **(t **)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) \ + : *(t *)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) ) +#define _crt_va_end(ap) ( ap = (va_list)0 ) + +#else /* defined (_M_X64) */ + +/* A guess at the proper definitions for other platforms */ + +#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) + +#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) +#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) +#define _crt_va_end(ap) ( ap = (va_list)0 ) + +#endif /* defined (_M_X64) */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_VADEFS */ diff --git a/test_data/lots_of_files/varargs.h b/test_data/lots_of_files/varargs.h new file mode 100644 index 0000000..6a83436 --- /dev/null +++ b/test_data/lots_of_files/varargs.h @@ -0,0 +1,133 @@ +/*** +*varargs.h - XENIX style macros for variable argument functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file defines XENIX style macros for accessing arguments of a +* function which takes a variable number of arguments. +* [System V] +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_VARARGS +#define _INC_VARARGS + +#if !defined (_WIN32) +#error ERROR: Only Win32 target supported! +#endif /* !defined (_WIN32) */ + + +#include <crtdefs.h> + +/* + * Currently, all MS C compilers for Win32 platforms default to 8 byte + * alignment. + */ +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if __STDC__ +#error varargs.h incompatible with ANSI (use stdarg.h) +#endif /* __STDC__ */ + +#if !defined (_W64) +#if !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) +#define _W64 __w64 +#else /* !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) */ +#define _W64 +#endif /* !defined (__midl) && (defined (_X86_) || defined (_M_IX86)) */ +#endif /* !defined (_W64) */ + +#ifndef _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else /* _WIN64 */ +typedef _W64 unsigned int uintptr_t; +#endif /* _WIN64 */ +#define _UINTPTR_T_DEFINED +#endif /* _UINTPTR_T_DEFINED */ + +#ifndef _VA_LIST_DEFINED +#ifdef _M_CEE_PURE +typedef System::ArgIterator va_list; +#else /* _M_CEE_PURE */ +typedef char * va_list; +#endif /* _M_CEE_PURE */ +#define _VA_LIST_DEFINED +#endif /* _VA_LIST_DEFINED */ + +#ifndef va_arg + +#if defined (_M_CEE) + +#error varargs.h not supported when targetting _M_CEE (use stdarg.h) + +#elif defined (_M_IX86) + +/* + * define a macro to compute the size of a type, variable or expression, + * rounded up to the nearest multiple of sizeof(int). This number is its + * size as function argument (Intel architecture). Note that the macro + * depends on sizeof(int) being a power of 2! + */ +#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) + +#define va_dcl va_list va_alist; +#define va_start(ap) ap = (va_list)&va_alist +#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) +#define va_end(ap) ap = (va_list)0 + +#elif defined (_M_ARM) + +#define _VA_ALIGN 4 +#define _APALIGN(t,ap) ( ((va_list)0 - (ap)) & (__alignof(t) - 1) ) +#define va_dcl va_list va_alist; + +#define _SLOTSIZEOF(t) ( (sizeof(t) + _VA_ALIGN - 1) & ~(_VA_ALIGN - 1) ) +#define va_start(ap) ( ap = (va_list)&va_alist ) +#define va_arg(ap,t) (*(t *)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) \ + - _SLOTSIZEOF(t))) +#define va_end(ap) ( ap = (va_list)0 ) + +#elif defined (_M_X64) + +extern void __cdecl __va_start(va_list *, ...); +#define va_dcl va_list va_alist; +#define va_start(ap) ( __va_start(&ap, 0) ) +#define va_arg(ap, t) \ + ( ( sizeof(t) > sizeof(__int64) || ( sizeof(t) & (sizeof(t) - 1) ) != 0 ) \ + ? **(t **)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) \ + : *(t *)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) ) +#define va_end(ap) ( ap = (va_list)0 ) + +#else /* defined (_M_X64) */ + +/* A guess at the proper definitions for other platforms */ + +#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) + +#define va_dcl va_list va_alist; +#define va_start(ap) ap = (va_list)&va_alist +#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) +#define va_end(ap) ap = (va_list)0 + +#endif /* defined (_M_X64) */ + +#endif /* va_arg */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_VARARGS */ diff --git a/test_data/lots_of_files/vcclr.h b/test_data/lots_of_files/vcclr.h new file mode 100644 index 0000000..61623fb --- /dev/null +++ b/test_data/lots_of_files/vcclr.h @@ -0,0 +1,55 @@ +// +// vcclr.h - helper code for using the managed extensions to C++ +// +// Copyright (C) Microsoft Corporation +// All rights reserved. +// + +#if _MSC_VER > 1000 +#pragma once +#endif + +#if !defined(_INC_VCCLR) +#define _INC_VCCLR +#ifndef RC_INVOKED + +#using <mscorlib.dll> +#include <gcroot.h> + +#pragma warning(push) +#pragma warning(disable:4400) + +#ifdef __cplusplus_cli +typedef cli::interior_ptr<const System::Char> __const_Char_ptr; +typedef cli::interior_ptr<const System::Byte> __const_Byte_ptr; +typedef cli::interior_ptr<System::Byte> _Byte_ptr; +typedef const System::String^ __const_String_handle; +#define _NULLPTR nullptr +#else +typedef const System::Char* __const_Char_ptr; +typedef const System::Byte* __const_Byte_ptr; +typedef System::Byte* _Byte_ptr; +typedef const System::String* __const_String_handle; +#define _NULLPTR 0 +#endif + + +// +// get an interior gc pointer to the first character contained in a System::String object +// +inline __const_Char_ptr PtrToStringChars(__const_String_handle s) { + + _Byte_ptr bp = const_cast<_Byte_ptr>(reinterpret_cast<__const_Byte_ptr>(s)); + if( bp != _NULLPTR ) { + unsigned offset = System::Runtime::CompilerServices::RuntimeHelpers::OffsetToStringData; + bp += offset; + } + return reinterpret_cast<__const_Char_ptr>(bp); +} + +#pragma warning(pop) + +#undef _NULLPTR + +#endif /* RC_INVOKED */ +#endif //_INC_VCCLR diff --git a/test_data/lots_of_files/vccorlib.h b/test_data/lots_of_files/vccorlib.h new file mode 100644 index 0000000..e9a7ed9 --- /dev/null +++ b/test_data/lots_of_files/vccorlib.h @@ -0,0 +1,3325 @@ +// +// Copyright (C) Microsoft Corporation +// All rights reserved. +// +// This header is included by the compiler using /FI when /ZW is specified +// Do not include any headers in this file +#ifndef _VCCORLIB_H_ +#define _VCCORLIB_H_ + +#ifdef _MSC_VER +#pragma once + +#pragma push_macro("_STRINGIZEX") +#pragma push_macro("_STRINGIZE") + +#undef _STRINGIZEX +#undef _STRINGIZE + +#define _STRINGIZEX(x) #x +#define _STRINGIZE(x) _STRINGIZEX(x) + +#pragma detect_mismatch("_VCCORLIB_VER", "1800." _STRINGIZE(__cplusplus_winrt)) // Detect vccorlib mismatch + +#pragma pop_macro("_STRINGIZE") +#pragma pop_macro("_STRINGIZEX") +#endif // _MSC_VER + +#if !defined(__cplusplus_winrt) +#error vccorlib.h can only be used with /ZW +#endif + +#if defined(VCWINRT_DLL) +#include <stdio.h> +#include <windows.h> +#include <inspectable.h> +#include <WinString.h> +#endif + +// All WinRT types should have a packing (the default C++ packing). +#ifdef _WIN64 +#pragma pack(push, 16) +#else +#pragma pack(push, 8) +#endif + +// <InternalComment> +// READ THIS BEFORE MAKING CHANGES TO THIS FILE: +// This is a force-include file which is used by all /ZW compilations, and akin to a typesrc file. +// /ZW should be usable to build any existing body of C++ code (including Windows, SQL, Office etc.) +// As such, the following rules should be observed: +// * Do not include any header files that have any behavior that can be changed by the user (e.g. #ifdef) +// * Do not declare a method or typename that can conflict with an existing method or type that comes from a header +// if the header may modify that type based on user #defines. +// General rules: +// * Keep PDB sizes small. Don't overuse templates, and keep identifiers short. +// </InternalComment> + +// <InternalComment> +// Postconditions: ParsingInitTypes is set +// </InternalComment> + +#if defined(__VCCORLIB_H_ENABLE_ALL_WARNINGS) +#pragma warning(push) +#endif + +// Following warnings disabled globally +// To enable these warnings define __VCCORLIB_H_ENABLE_ALL_WARNINGS +#pragma warning(disable: 4514) // unreferenced inline function has been removed +#pragma warning(disable: 4710) // function not inlined +#pragma warning(disable: 4711) // selected for automatic inline expansion + +// Following warnings disabled for this file +#pragma warning( push ) +#pragma warning(disable: 4127) // conditional expression is constant +#pragma warning(disable: 4483) // Allows us to use __identifier +#pragma warning(disable: 4820) // bytes padding added after data member + +#pragma initialize_winrt_types_start + +struct HSTRING__; +typedef HSTRING__* __abi_HSTRING; + +__declspec(noreturn) void __stdcall __abi_WinRTraiseException(long); + +#if !defined(VCWINRT_DLL) +__declspec(dllimport) long __stdcall __abi_translateCurrentException(bool reserved); +#else +__declspec(dllexport) long __stdcall __abi_translateCurrentException(bool reserved); +#endif + +inline void __abi_ThrowIfFailed(long __hrArg) +{ + if (__hrArg < 0) + { + __abi_WinRTraiseException(__hrArg); + } +} + +#if !defined(VCWINRT_DLL) +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_FailFast(); +#else +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_FailFast(); +#endif + +namespace __winRT +{ + long __stdcall __windowsCreateString(const __wchar_t*, int, __abi_HSTRING*); + long __stdcall __getActivationFactoryByPCWSTR(void*, ::Platform::Guid&, void**); + long __stdcall __getIids(int, unsigned long*, const __s_GUID*, ::Platform::Guid**); +} + +namespace Windows +{ + namespace Foundation + { + } +} + +struct __abi_WinClassInstrumentation +{ + __abi_WinClassInstrumentation* callback; + + int numcalls_QueryInterface; + int numcalls_AddRef; + int numcalls_Release; + int numcalls_GetIids; + int numcalls_GetRuntimeClassName; + int numcalls_GetTrustLevel; + int numcalls_Other; + int destructed; + int refcount; + + __abi_WinClassInstrumentation() + { + callback = nullptr; + numcalls_QueryInterface = 0; + numcalls_AddRef = 0; + numcalls_Release = 0; + numcalls_GetIids = 0; + numcalls_GetRuntimeClassName = 0; + numcalls_GetTrustLevel = 0; + numcalls_Other = 0; + destructed = 0; + refcount = 0; + } + + void __abi_SetInstrumentationData(__abi_WinClassInstrumentation* __callbackArg) + { + callback = __callbackArg; + __abi_CopyToAttached(); + } + + void __abi_CopyToAttached() + { + if (callback) + { + callback->numcalls_QueryInterface = numcalls_QueryInterface; + callback->numcalls_AddRef = numcalls_AddRef; + callback->numcalls_Release = numcalls_Release; + callback->numcalls_GetIids = numcalls_GetIids; + callback->numcalls_GetRuntimeClassName= numcalls_GetRuntimeClassName; + callback->numcalls_GetTrustLevel = numcalls_GetTrustLevel; + callback->numcalls_Other = numcalls_Other; + callback->destructed = destructed; + callback->refcount = refcount; + } + } +}; + +// +//// Don't want to define the real IUnknown from unknown.h here. That would means if the user has +//// any broken code that uses it, compile errors will take the form of e.g.: +//// predefined C++ WinRT types (compiler internal)(41) : see declaration of 'IUnknown::QueryInterface' +//// This is not helpful. If they use IUnknown, we still need to point them to the actual unknown.h so +//// that they can see the original definition. +//// +//// For WinRT, we'll instead have a parallel COM interface hierarchy for basic interfaces starting with _. +//// The type mismatch is not an issue. COM passes types through GUID / void* combos - the original type +//// doesn't come into play unless the user static_casts an implementation type to one of these, but +//// the WinRT implementation types are hidden. +__interface __declspec(uuid("00000000-0000-0000-C000-000000000046")) __abi_IUnknown +{ +public: + virtual long __stdcall __abi_QueryInterface(::Platform::Guid&, void**) = 0; + virtual unsigned long __stdcall __abi_AddRef() = 0; + virtual unsigned long __stdcall __abi_Release() = 0; +}; + +enum __abi_TrustLevel +{ + __abi_BaseTrust = 0, + __abi_PartialTrust = (__abi_BaseTrust + 1) , + __abi_FullTrust = (__abi_PartialTrust + 1) +}; + +__interface __declspec(uuid("3C5C94E8-83BB-4622-B76A-B505AE96E0DF")) __abi_Module +{ +public: + virtual unsigned long __stdcall __abi_IncrementObjectCount() = 0; + virtual unsigned long __stdcall __abi_DecrementObjectCount() = 0; +}; + +__interface __declspec(uuid("00000003-0000-0000-C000-000000000046")) __abi_IMarshal; + +extern __abi_Module* __abi_module; + +extern "C" long __cdecl _InterlockedIncrement(long volatile *); +extern "C" long __cdecl _InterlockedDecrement(long volatile *); +extern "C" long __cdecl _InterlockedCompareExchange(long volatile *, long, long); +extern "C" void* _InterlockedCompareExchangePointer(void* volatile *, void* , void*); + +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedCompareExchange) +#pragma intrinsic(_InterlockedCompareExchangePointer) + +// A class that represents a volatile refcount, that gets initialized to 0. +class __abi_MultiThreadedRefCount +{ + long __refcount; +public: + __declspec(non_user_code) __abi_MultiThreadedRefCount() : __refcount(1) + { + if (__abi_module != nullptr) + { + __abi_module->__abi_IncrementObjectCount(); + } + } + + // called for a partially created ref class i.e. exception thrown from ctor + __declspec(non_user_code) void __abi_dtor() + { + if (__abi_module != nullptr) + { + __abi_module->__abi_DecrementObjectCount(); + } + } + + inline unsigned long Increment() volatile + { + return static_cast<unsigned long>(_InterlockedIncrement(&__refcount)); + } + + inline unsigned long Decrement() volatile + { + unsigned long __refCountLoc = static_cast<unsigned long>(_InterlockedDecrement(&__refcount)); + if (__refCountLoc == 0) + { + // When destructing objects at the end of the program, we might be freeing + // objects across dlls, and the dll this object is in might have already freed its module object. + if (__abi_module != nullptr) + { + __abi_module->__abi_DecrementObjectCount(); + } + } + return __refCountLoc; + } + + inline unsigned long Get() volatile + { + return static_cast<unsigned long>(__refcount); + } + void ReleaseControlBlock() volatile + { + } +}; + +__interface __declspec(uuid("AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90")) __abi_IInspectable : public __abi_IUnknown +{ + virtual long __stdcall __abi_GetIids(unsigned long*, ::Platform::Guid**) = 0; + virtual long __stdcall __abi_GetRuntimeClassName(__abi_HSTRING*) = 0; + virtual long __stdcall __abi_GetTrustLevel(__abi_TrustLevel*) = 0; +}; + +__interface __declspec(uuid("00000001-0000-0000-C000-000000000046")) __abi_IClassFactory : public __abi_IUnknown +{ + virtual long __stdcall __abi_CreateInstance(__abi_IUnknown*, ::Platform::Guid&, void**) = 0; + virtual long __stdcall __abi_LockServer(int) = 0; +}; + +__interface __declspec(uuid("00000035-0000-0000-C000-000000000046")) __abi_IActivationFactory : public __abi_IInspectable +{ + virtual long __stdcall __abi_ActivateInstance(__abi_IInspectable**) = 0; +}; + +__interface __declspec(uuid("00000037-0000-0000-C000-000000000046")) __abi_IWeakReference : public __abi_IUnknown +{ + virtual long __stdcall __abi_Resolve(::Platform::Guid&, __abi_IInspectable **) = 0; +}; + +#if !defined(VCWINRT_DLL) +typedef struct __Platform_Details_HSTRING_HEADER +{ + int __flags; // Bit flags which used for storing extra information + unsigned int __length; // length of string's unicode code point + unsigned int __padding; // padding for future use + unsigned int __morepadding; // padding for future use + __wchar_t* __stringRef; // An address pointer which points to a string buffer. +} __Platform_Details_HSTRING_HEADER; +#else +typedef HSTRING_HEADER __Platform_Details_HSTRING_HEADER; +#endif + +namespace Platform { namespace Details { + struct EventLock + { + void* __targetsLock; + void* __addRemoveLock; + }; + + template<typename T> + EventLock* GetStaticEventLock() + { + static EventLock __eventLock = { nullptr, nullptr }; + return &__eventLock; + } +}} // namespace Platform::Details + +// <InternalComment> +// Initialize a set of PCH global roots from some of the types defined above this point. +// Preconditions: The following types must be defined before this point: +// __abi_IUnknown +// __abi_IInspectable +// __abi_IClassFactory +// __abi_IActivationFactory +// HSTRING +// __abi_TrustLevel +// ::Platform::Guid +// __abi_MultiThreadedRefCount +// Postconditions: * The following PCH global roots are initialized +// pWinRTIUnknown +// pWinRTIInspectable +// pWinRTIClassFactory +// pWinRTIActivationFactory +// pWinRTHSTRING +// pWinRTTrustLevel +// pWindowsFoundationGuid +// pWinRTMultiThreadedRefCount +// * Windows.Foundation.winmd is loaded +// * From this point on WinRT types can be declared using 'ref class', 'interface class' etc. BUT must have __declspec(no_weak_ref) +// * ParsingInitTypes is still set +// </InternalComment> +#pragma initialize_winrt_types_phase1 + +namespace __abi_details +{ + // String^ + __declspec(non_user_code) __declspec(no_refcount) void __abi_delete_String(::Platform::String^); + +#if !defined(VCWINRT_DLL) + __declspec(dllimport) __declspec(non_user_code) __declspec(no_refcount) + ::Platform::Object ^ __stdcall __abi_cast_String_to_Object(::Platform::String^); + + __declspec(dllimport) __declspec(non_user_code) + ::Platform::String ^ __stdcall __abi_cast_Object_to_String(bool, ::Platform::Object^); + + __declspec(dllimport) __declspec(non_user_code) ::Platform::String^ __stdcall __abi_ObjectToString(::Platform::Object^ o, bool useIPrintable); + +#else + __declspec(dllexport) __declspec(non_user_code) __declspec(no_refcount) + ::Platform::Object ^ __stdcall __abi_cast_String_to_Object(::Platform::String^); + + __declspec(dllexport) __declspec(non_user_code) + ::Platform::String ^ __stdcall __abi_cast_Object_to_String(bool, ::Platform::Object^); + + __declspec(dllexport) __declspec(non_user_code) ::Platform::String^ __stdcall __abi_ObjectToString(::Platform::Object^ o, bool useIPrintable); +#endif +} // namespace __abi_details + +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void* __abi_winrt_ptr_ctor(const volatile ::Platform::Object^ const __objArg) +{ + __abi_IUnknown* __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Object^>(__objArg)); + if (__pUnknown) { + __pUnknown->__abi_AddRef(); + } + return __pUnknown; +} + +__declspec(non_user_code) __declspec(no_refcount) + inline void __abi_winrt_ptr_dtor(const volatile ::Platform::Object^ const __objArg) +{ + __abi_IUnknown* __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Object^>(__objArg)); + if (__pUnknown) { + __pUnknown->__abi_Release(); + } +} + +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void* __abi_winrt_ptr_assign(void** __ppTargetArg, const volatile ::Platform::Object^ __objArg) +{ + __abi_IUnknown* __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Object^>(__objArg)); + __abi_IUnknown** __ppTargetUnknown = reinterpret_cast<__abi_IUnknown**>(__ppTargetArg); + if (__pUnknown != *__ppTargetUnknown) + { + if (__pUnknown) { + __pUnknown->__abi_AddRef(); + } + if (*__ppTargetUnknown) { + (*__ppTargetUnknown)->__abi_Release(); + } + *__ppTargetUnknown = __pUnknown; + } + return __pUnknown; +} + +template<typename T> +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void* __abi_winrt_ptrto_delegate_ctor(const volatile T^ __objArg) +{ + return __abi_winrt_ptr_ctor(reinterpret_cast<const volatile ::Platform::Object^>(__objArg)); +} + +template<typename T> +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void __abi_winrt_ptrto_delegate_dtor(const volatile T^ __objArg) +{ + __abi_winrt_ptr_dtor(reinterpret_cast<const volatile ::Platform::Object^>(__objArg)); +} + +template<typename T> +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void* __abi_winrt_ptrto_delegate_assign(void** __ppTargetArg, const volatile T^ __objArg) +{ + return __abi_winrt_ptr_assign(__ppTargetArg, reinterpret_cast<const volatile ::Platform::Object^>(__objArg)); +} + +// Used for handle which is inside '__declspec(no_refcount)' function but still needs Release. +struct __abi_dtor_helper { +private: + __abi_IUnknown *__pUnknown; +public: + __declspec(non_user_code) __abi_dtor_helper(const volatile ::Platform::Object^ __objArg) { + __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Object^>(__objArg)); + } + __declspec(non_user_code) ~__abi_dtor_helper() { + if (__pUnknown) { + __pUnknown->__abi_Release(); + } + } +}; + +// The exceptions are split out explicitly in order to make them obvious from callstacks. +#if !defined(VCWINRT_DLL) +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseNotImplementedException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseInvalidCastException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseNullReferenceException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseOperationCanceledException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseFailureException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseAccessDeniedException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseOutOfMemoryException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseInvalidArgumentException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseOutOfBoundsException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseChangedStateException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseClassNotRegisteredException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseWrongThreadException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseDisconnectedException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseObjectDisposedException(); +__declspec(dllimport) __declspec(noreturn) void __stdcall __abi_WinRTraiseCOMException(long); +#else +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseNotImplementedException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseInvalidCastException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseNullReferenceException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseOperationCanceledException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseFailureException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseAccessDeniedException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseOutOfMemoryException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseInvalidArgumentException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseOutOfBoundsException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseChangedStateException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseClassNotRegisteredException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseWrongThreadException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseDisconnectedException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseObjectDisposedException(); +__declspec(dllexport) __declspec(noreturn) void __stdcall __abi_WinRTraiseCOMException(long); +#endif + +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::Object^ __abi_winrt_cast_to(bool __isDynamicCastArg, ::Platform::Object^ __objArg, const _GUID& __guidArg) +{ + void* __pTo = nullptr; + __abi_IUnknown* __pUnknown = reinterpret_cast<__abi_IUnknown*>(__objArg); + + if (__pUnknown) { + // Cast to ::Platform::Guid instead of using conversion in order to avoid copy to temporary + long __hr = __pUnknown->__abi_QueryInterface(reinterpret_cast< ::Platform::Guid&>(const_cast<_GUID&>(__guidArg)), &__pTo); + + if (__isDynamicCastArg && __hr != 0) + return nullptr; + // It will throw InvalidCastException on failure + __abi_ThrowIfFailed(__hr); + } + + return reinterpret_cast< ::Platform::Object^>(__pTo); +} + +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::String^ __abi_winrt_cast_to_string(bool __isDynamicCast, ::Platform::Object^ __objArg) +{ + return __abi_details::__abi_cast_Object_to_String(__isDynamicCast, __objArg); +} + +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::Object^ __abi_winrt_cast_from_string_to_object(bool /*__isDynamicCastArg*/, ::Platform::String^ __objArg) +{ + return __abi_details::__abi_cast_String_to_Object(__objArg); +} + +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::Object^ __abi_winrt_cast_from_string_to_other(bool /*__isDynamicCastArg*/, ::Platform::String^ /*__objArg*/) +{ + __abi_WinRTraiseInvalidCastException(); +} + +template<typename T> +__declspec(non_user_code) __declspec(no_refcount) + inline T^ __abi_winrt_cast_from_object_to_delegate(bool __isDynamicCast, ::Platform::Object^ __objArg); + +template<typename T> +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::Object^ __abi_winrt_cast_from_delegate_to_object(bool __isDynamicCastArg, T^ __objArg); + +template<typename T> +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::Object^ __abi_winrt_cast_from_delegate_to_other(bool __isDynamicCastArg, T^ __objArg, const _GUID& __guidArg) +{ + return __abi_winrt_cast_to(__isDynamicCastArg, reinterpret_cast< ::Platform::Object^>(__objArg), __guidArg); +} + +inline void* __detach_as_voidptr(void** __ppObjArg) +{ + void* __pObj = *__ppObjArg; + *__ppObjArg = nullptr; + return __pObj; +} + +__declspec(non_user_code) __declspec(no_refcount) + inline void __abi_winrt_ptrto_string_dtor(const volatile ::Platform::String^ const __objArg) +{ + __abi_details::__abi_delete_String(const_cast< ::Platform::String^>(__objArg)); +} + +__declspec(non_user_code) inline void __abi_winrt_throw_on_disposed(bool isDisposed) +{ + if (isDisposed) + { + __abi_WinRTraiseObjectDisposedException(); + } +} + + +// Function decleration for types we use from Windows and CRT +// This prevents pulling in the headers +#if !defined(VCWINRT_DLL) +extern "C" long __stdcall __Platform_CoCreateFreeThreadedMarshaler(::Platform::Object^, ::Platform::Object^*); +#endif + +namespace Platform { + template <typename __TArg, unsigned int __dimension = 1> + ref class WriteOnlyArray; + + template <typename __TArg, unsigned int __dimension = 1> + ref class Array; +} + +template <typename __TArg, unsigned int __dimension> +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + void* __abi_winrt_ptrto_array_ctor(const volatile ::Platform::Array<__TArg, __dimension>^ const); + +template<typename __TArg, unsigned int __dimension> +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + void* __abi_winrt_ptrto_array_assign(void**, const volatile ::Platform::Array<__TArg, __dimension>^); + +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::Object^ __abi_winrt_cast_use_helper(bool __isDynamicArg, void* __fromArg, const _GUID& __guidArg, __abi_IUnknown* __useresultArg) +{ + if (__useresultArg) + { + return reinterpret_cast< ::Platform::Object^>(__useresultArg); + } + + return __abi_winrt_cast_to(__isDynamicArg, reinterpret_cast< ::Platform::Object^>(__fromArg), __guidArg); +} + +__declspec(selectany) void * __forceInstantiate1 = &__abi_winrt_cast_use_helper; + +__declspec(non_user_code) __declspec(no_refcount) + inline void __abi_winrt_ptr_dispose(const volatile ::Platform::Object^ const __objArg) +{ + ::Platform::IDisposable ^__dispose = dynamic_cast< ::Platform::IDisposable ^>(const_cast< ::Platform::Object^>(__objArg)); + if (__dispose) { + __dispose->__identifier("<Dispose>")(); + reinterpret_cast<__abi_IUnknown*>(__dispose)->__abi_Release(); + } +} + +__declspec(non_user_code) __declspec(no_refcount) + inline void __abi_winrt_ptr_dispose_dtor(const volatile ::Platform::Object^ const __objArg) +{ + __abi_winrt_ptr_dispose(__objArg); + __abi_winrt_ptr_dtor(__objArg); +} + +class __abi_FTMWeakRefData; + +namespace Platform { namespace Details +{ + class ControlBlock sealed : public __abi_IWeakReference + { +#if defined(VCWINRT_DLL) + public: +#else + private: +#endif + volatile long __weakRefCount; + volatile long __strongRefCount; + __abi_IUnknown* __target; // we shouldn't hold a strong reference to target, so grab an unaddref'd reference here. + bool __bSingleAllocation; + bool __bAlignedAllocation; + bool __bExceptionAllocation; + + public: + // IWeakReference + virtual long __stdcall __abi_QueryInterface(::Platform::Guid& __riid, void** __ppvObject); + + virtual unsigned long __stdcall __abi_AddRef() + { + return static_cast<unsigned long>(_InterlockedIncrement(&__weakRefCount)); + } + + virtual unsigned long __stdcall __abi_Release() + { + unsigned long __ref = static_cast<unsigned long>(_InterlockedDecrement(&__weakRefCount)); + if (__ref == 0) + { + if (__bAlignedAllocation) + { + ::Platform::Details::Heap::AlignedFree(__target); + } + else + { + ::Platform::Details::Heap::Free(this); + } + } + return __ref; + } + +#if !defined(VCWINRT_DLL) + __declspec(dllimport) virtual long __stdcall __abi_Resolve(::Platform::Guid& __riid, __abi_IInspectable ** __objectReference); +#else + __declspec(dllexport) virtual long __stdcall __abi_Resolve(::Platform::Guid& __riid, __abi_IInspectable ** __objectReference); +#endif + + private: + unsigned long __stdcall IncrementStrongReference() + { + // InterlockedIncrement calls _InterlockedIncrement intrinsic thus we call directly _InterlockedIncrement to save the call + return static_cast<unsigned long>(_InterlockedIncrement(&__strongRefCount)); + } + + unsigned long __stdcall DecrementStrongReference() + { + // InterlockedDecrement calls _InterlockedDecrement intrinsic thus we call directly _InterlockedDecrement to save the call + unsigned long __ref = static_cast<unsigned long>(_InterlockedDecrement(&__strongRefCount)); + if (__ref == 0) + { + __strongRefCount = static_cast<int>(0xC0000000); // LONG_MIN / 2 + } + return __ref; + } + + inline long __stdcall GetRefcount() + { + return __strongRefCount; + } + + ControlBlock(){} + +#if !defined(VCWINRT_DLL) + __declspec(dllimport) void InitControlBlock(void* __object, bool __bSingleAllocationParam = true, bool __bAlignedAllocationParam = false, bool __bExceptionAllocationParam = false); + __declspec(dllimport) void ReleaseTarget(); +#else + __declspec(dllexport) void InitControlBlock(void* __object, bool __bSingleAllocationParam = true, bool __bAlignedAllocationParam = false, bool __bExceptionAllocationParam = false); + __declspec(dllexport) void ReleaseTarget(); +#endif + + friend class __abi_FTMWeakRefData; + friend void* Details::Heap::Allocate(::Platform::SizeT, ::Platform::SizeT); + friend void* Details::Heap::AlignedAllocate(::Platform::SizeT, ::Platform::SizeT, ::Platform::SizeT); + friend void* Details::Heap::AllocateException(::Platform::SizeT __rcOffset, ::Platform::SizeT __sizeArg); + friend void* Details::Heap::AlignedAllocateException(::Platform::SizeT __rcOffset, ::Platform::SizeT __sizeArg, ::Platform::SizeT __alignment); + }; +}} // ::Platform::Details + +// A class that represents a volatile refcount, that gets initialized to 0. +class __abi_FTMWeakRefData +{ +#if defined(VCWINRT_DLL) +public: +#endif + ::Platform::Details::ControlBlock* __weakRefSource; + __abi_IUnknown* volatile __pUnkMarshal; + +public: + __declspec(non_user_code) __abi_FTMWeakRefData(::Platform::Object^ __targetArg) + { + __pUnkMarshal = reinterpret_cast<__abi_IUnknown*>(-1); + + if (__abi_module != nullptr) + { + __abi_module->__abi_IncrementObjectCount(); + } + } + + __declspec(non_user_code) __abi_FTMWeakRefData(::Platform::Object^ __targetArg, ::Platform::CallbackContext __contextArg) + { + if (__contextArg == ::Platform::CallbackContext::Any) + { + __pUnkMarshal = reinterpret_cast<__abi_IUnknown*>(-1); + } + else + { + __pUnkMarshal = nullptr; + } + + if (__abi_module != nullptr) + { + __abi_module->__abi_IncrementObjectCount(); + } + } + + // called for a partially created ref class i.e. exception thrown from ctor + __declspec(non_user_code) void __abi_dtor() + { + if (__pUnkMarshal && __pUnkMarshal != reinterpret_cast<__abi_IUnknown*>(-1)) + { + __pUnkMarshal->__abi_Release(); + __pUnkMarshal = nullptr; + } + + if (__abi_module != nullptr) + { + __abi_module->__abi_DecrementObjectCount(); + } + __weakRefSource->DecrementStrongReference(); + __weakRefSource->__abi_Release(); + } + + inline unsigned long __stdcall Increment() volatile + { + if (__weakRefSource == nullptr || __weakRefSource->GetRefcount() < 0) + { + return static_cast<unsigned long>(-1); // Called during destruction + } + + return __weakRefSource->IncrementStrongReference(); + } + + inline unsigned long __stdcall Decrement() volatile + { + if (__weakRefSource == nullptr || __weakRefSource->GetRefcount() < 0) + { + return static_cast<unsigned long>(-1); // Called during destruction + } + + unsigned long __refCount = __weakRefSource->DecrementStrongReference(); + if (__refCount == 0) + { + if (__pUnkMarshal && __pUnkMarshal != reinterpret_cast<__abi_IUnknown*>(-1)) + { + __pUnkMarshal->__abi_Release(); + __pUnkMarshal = nullptr; + } + + // When destructing objects at the end of the program, we might be freeing + // objects across dlls, and the dll this object is in might have already freed its module object. + if (__abi_module != nullptr) + { + __abi_module->__abi_DecrementObjectCount(); + } + } + + return __refCount; + } + + inline __abi_FTMWeakRefData* GetFreeThreadedMarshaler() + { + if (__pUnkMarshal == nullptr) + { + return nullptr; + } + return this; + } + + inline ::Platform::Details::IWeakReference^ GetWeakReference() + { + return reinterpret_cast< ::Platform::Details::IWeakReference^>(__weakRefSource); + } + + inline long __stdcall Get() volatile + { + if (__weakRefSource == nullptr) + { + return static_cast<unsigned long>(-1); // Called during destruction + } + return __weakRefSource->GetRefcount(); + } + + long __stdcall __abi_QueryInterface(::Platform::Guid& __guid, void** __pOut); + + void ReleaseControlBlock() + { + auto __localWeakRefSource = __weakRefSource; + __localWeakRefSource->ReleaseTarget(); + __localWeakRefSource->__abi_Release(); + } +}; + +namespace Platform { namespace Details +{ + struct __single_inheritance __abi_CaptureBase + { + protected: + virtual __stdcall ~__abi_CaptureBase() {} + + public: + static const size_t __smallCaptureSize = 4 * sizeof(void*); + void* operator new(size_t __sizeArg, void* __pSmallCaptureArg) + { + if (__sizeArg > __smallCaptureSize) + { + return reinterpret_cast<__abi_CaptureBase*>( ::Platform::Details::Heap::Allocate( __sizeArg ) ); + } + + return __pSmallCaptureArg; + } + + void operator delete(void* __ptrArg, void* __pSmallCaptureArg) + { + __abi_CaptureBase* __pThis = static_cast<__abi_CaptureBase*>(__ptrArg); + __pThis->Delete(__pThis, __pSmallCaptureArg); + } + + inline void* GetVFunction(int __slotArg) + { + return (*reinterpret_cast<void***>(this))[__slotArg]; + } + + void Delete(__abi_CaptureBase* __pThisArg, void* __pSmallCaptureArg) + { + __pThisArg->~__abi_CaptureBase(); + if (__pThisArg != __pSmallCaptureArg) + { + ::Platform::Details::Heap::Free(__pThisArg); + } + } + }; + + struct __abi_CapturePtr + { + char* smallCapture[__abi_CaptureBase::__smallCaptureSize]; + __abi_CaptureBase* ptr; + __abi_CapturePtr() : ptr( reinterpret_cast<__abi_CaptureBase*>(smallCapture) ) {} + ~__abi_CapturePtr() + { + ptr->Delete(ptr, smallCapture); + } + }; + + template <typename __TFunctor, typename __TReturnType, typename... __TArgs> + struct __abi_FunctorCapture : public ::Platform::Details::__abi_CaptureBase + { + __TFunctor functor; + __abi_FunctorCapture(__TFunctor __functor) : functor(__functor) {} + virtual __TReturnType __stdcall Invoke(__TArgs... __args) {return functor(__args...);} + }; +}} // namespace Platform::Details + +// <InternalComment> +// Initialize a set of PCH global roots from some of the types defined above this point. +// Preconditions: See initialize_winrt_types_phase1 preconditions: +// __abi_FTMWeakRefData is now defined +// Postconditions: * From this point on WinRT types can be declared using 'ref class', 'interface class' etc. +// * ParsingInitTypes is still set +// </InternalComment> + +#pragma initialize_winrt_types_phase2 + +namespace Platform +{ + template <typename __TArg, unsigned int __dimension = 1> + class ArrayReference; + + namespace Details + { + template <typename __HighLevelType, unsigned int __dimension> + ::Platform::Array<__HighLevelType, __dimension>^ __abi_array_attach(void* __src, unsigned int __size, bool __isFastPass, bool __needsInit); + + template <typename __HighLevelType, unsigned int __dimension> + void __abi_array_copy_to_and_release(::Platform::Array<__HighLevelType, __dimension>^ __arr, void** __dest, unsigned int* __size); + + template <typename __LowLevelType, typename __HighLevelType, unsigned int __dimension> + __LowLevelType* __abi_array_to_raw(const ::Platform::Array<__HighLevelType, __dimension>^); + + template <typename __TArg, bool = __is_enum(__TArg)> + struct array_helper; + } // namespace Details + +#pragma warning(push) +#pragma warning(disable: 4487) + // Partial specialization of one-dimensional Array + template <typename __TArg> + private ref class WriteOnlyArray<__TArg, 1> + { + protected private: + unsigned int __size; // number of elements + bool __fastpassflag; // true if "fast pass", else false + __TArg* __data; // actual data buffer, alloc'd if not "fast-pass" + + internal: + __TArg& set(unsigned int __indexArg, __TArg __valueArg); + property unsigned int Length {unsigned int get() const; } + property __TArg* Data { __TArg* get() const; } + property bool FastPass { bool get() const; } + + __TArg* begin() const; + __TArg* end() const; + + protected private: + WriteOnlyArray(); + WriteOnlyArray(unsigned int __sizeArg); + WriteOnlyArray(__TArg* __dataArg, unsigned int __sizeArg); + void Clear(); + +#if defined(_PREFIX_) || defined(_PREFAST_) + __TArg& operator[](unsigned int __indexArg) const; +#endif // defined(_PREFIX_) || defined(_PREFAST_) + __TArg& get(unsigned int __indexArg) const; + + static __TArg* AllocateAndZeroInitialize(unsigned int __countArg); + static __TArg* AllocateAndCopyElements(const __TArg* __srcArg, unsigned int __countArg); + public: + virtual ~WriteOnlyArray(); + }; + + template <typename __TArg> + private ref class Array<__TArg,1> sealed : + public WriteOnlyArray<__TArg, 1>, + public [::Windows::Foundation::Metadata::Default] [::Platform::Metadata::RuntimeClassName] ::Platform::IBoxArray<__TArg> + { + public: + virtual property Array^ Value { virtual Array^ get(); } + + internal: + Array(const Array<__TArg, 1>^ __source); + Array(unsigned int __sizeArg); + Array(__TArg* __dataArg, unsigned int __sizeArg); + __TArg& get(unsigned int __indexArg) const; +#if defined(_PREFIX_) || defined(_PREFAST_) + __TArg& operator[](unsigned int __indexArg) const; +#endif // defined(_PREFIX_) || defined(_PREFAST_) + + private: + Array(); + void Attach(__TArg* __srcArg, unsigned int __sizeArg); + void AttachFastPass(__TArg* __srcArg, unsigned int __sizeArg); + void CopyToOrDetach(__TArg** __destArg, unsigned int* __sizeArg); + + template <typename __HighLevelType, unsigned int __dimension> + friend ::Platform::Array<__HighLevelType, __dimension>^ ::Platform::Details::__abi_array_attach(void* __src, unsigned int __size, bool __isFastPass, bool __needsInit); + + template <typename __HighLevelType, unsigned int __dimension> + friend void ::Platform::Details::__abi_array_copy_to_and_release(::Platform::Array<__HighLevelType, __dimension>^ __arrArg, void** __destArg, unsigned int* __sizeArg); + template <typename __TArg, unsigned int __dimension> friend class ArrayReference; + + void ArrayReferenceInit() + { + __vtable_initialize(Array<__TArg, 1>); + } + public: + virtual ~Array() {} + }; + +#pragma warning(pop) +} // namespace Platform + +template <typename __TArg, unsigned int __dimension> +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void* __abi_winrt_ptrto_array_ctor(const volatile ::Platform::Array<__TArg, __dimension>^ const __arrArg) +{ + __abi_IUnknown* __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Array<__TArg, __dimension>^>(__arrArg)); + if (__pUnknown) + { + auto __localArray = const_cast< ::Platform::Array<__TArg, __dimension>^>(const_cast< ::Platform::Array<__TArg, __dimension>^>(__arrArg)); + if (__localArray->FastPass) + { + auto __ret = ref new ::Platform::Array<__TArg, __dimension>(__localArray->Data, __localArray->Length); + __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Array<__TArg, __dimension>^>(__ret)); + } + else + { + __pUnknown->__abi_AddRef(); + } + } + return __pUnknown; +} + +template<typename __TArg, unsigned int __dimension> +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + void* __abi_winrt_ptrto_array_assign(void** __ppTarget, const volatile ::Platform::Array<__TArg, __dimension> ^__arrArg) +{ + __abi_IUnknown* __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Array<__TArg, __dimension>^>(__arrArg)); + __abi_IUnknown** __ppTargetUnknown = reinterpret_cast<__abi_IUnknown**>(__ppTarget); + if (__pUnknown != *__ppTargetUnknown) + { + if (__pUnknown) + { + auto __localArray = const_cast< ::Platform::Array<__TArg, __dimension>^>(__arrArg); + if (__localArray->FastPass) + { + auto __ret = ref new ::Platform::Array<__TArg>(__localArray->Data, __localArray->Length); + __pUnknown = reinterpret_cast<__abi_IUnknown*>(const_cast< ::Platform::Array<__TArg, __dimension>^>(__ret)); + } + else + { + __pUnknown->__abi_AddRef(); + } + } + if (*__ppTargetUnknown) + { + (*__ppTargetUnknown)->__abi_Release(); + } + *__ppTargetUnknown = __pUnknown; + } + return __pUnknown; +} + +template <typename __TArg> +inline __TArg __winrt_Empty_Struct() +{ + unsigned char __bytes[sizeof(__TArg)]; + __Platform_memset(__bytes, 0, sizeof(__TArg)); + + return (__TArg&)__bytes; +} + +struct __abi___FactoryCache +{ + __abi_IUnknown* __factory; + void* __cookie; +}; + +__declspec(selectany) __abi___FactoryCache __abi_no_factory_cache = { nullptr, 0 }; + +struct __abi___classObjectEntry +{ + // Factory creator function + long (__stdcall *__factoryCreator)(unsigned int*, __abi___classObjectEntry*, ::Platform::Guid&, __abi_IUnknown**); + // Object id + const __wchar_t* (__stdcall *__getRuntimeName)(); + // Trust level for WinRT otherwise nullptr + int (__stdcall *__getTrustLevel)(); + // Factory cache, group id data members + __abi___FactoryCache* __factoryCache; + const __wchar_t* __serverName; +}; + +// Section r is used to put WinRT objects to creator map +#pragma section("minATL$__r", read) + +__declspec(noreturn) inline void __stdcall __abi_WinRTraiseException(long __hrArg) +{ + switch (__hrArg) + { + case 0x80004001L: // E_NOTIMPL + __abi_WinRTraiseNotImplementedException(); + + case 0x80004002L: // E_NOINTERFACE + __abi_WinRTraiseInvalidCastException(); + + case 0x80004003L: // E_POINTER + __abi_WinRTraiseNullReferenceException(); + + case 0x80004004L: // E_ABORT + __abi_WinRTraiseOperationCanceledException(); + + case 0x80004005L: // E_FAIL + __abi_WinRTraiseFailureException(); + + case 0x80070005L: // E_ACCESSDENIED + __abi_WinRTraiseAccessDeniedException(); + + case 0x8007000EL: // E_OUTOFMEMORY + __abi_WinRTraiseOutOfMemoryException(); + + case 0x80070057L: // E_INVALIDARG + __abi_WinRTraiseInvalidArgumentException(); + + case 0x8000000BL: // E_BOUNDS + __abi_WinRTraiseOutOfBoundsException(); + + case 0x8000000CL: // E_CHANGED_STATE + __abi_WinRTraiseChangedStateException(); + + case 0x80040154L: // REGDB_E_CLASSNOTREG + __abi_WinRTraiseClassNotRegisteredException(); + + case 0x8001010EL: // RPC_E_WRONG_THREAD + __abi_WinRTraiseWrongThreadException(); + + case 0x80010108L: // RPC_E_DISCONNECTED + __abi_WinRTraiseDisconnectedException(); + + case 0x80000013L: // RO_E_CLOSED + __abi_WinRTraiseObjectDisposedException(); + + default: + __abi_WinRTraiseCOMException(__hrArg); + break; + } +} + +__declspec(non_user_code) + ::Platform::String^ __abi_winrt_CreateSystemStringFromLiteral(const __wchar_t*); +__declspec(non_user_code) + ::Platform::String^ __abi_winrt_CreateSystemStringFromLiteral(const unsigned short*); + +#if defined(VCWINRT_DLL) +#define __Platform_CoCreateFreeThreadedMarshaler(__punkOuter, __ppunkMarshal) CoCreateFreeThreadedMarshaler(reinterpret_cast<IUnknown*>(__punkOuter), reinterpret_cast<IUnknown**>(__ppunkMarshal)) +#endif + +#if defined(VCWINRT_DLL) +#define __Platform_CoCreateFreeThreadedMarshaler(__punkOuter, __ppunkMarshal) CoCreateFreeThreadedMarshaler(reinterpret_cast<IUnknown*>(__punkOuter), reinterpret_cast<IUnknown**>(__ppunkMarshal)) +#endif + +// Postconditions: * ParsingInitTypes is cleared +#pragma initialize_winrt_types_phase3 + +#pragma region Define Common EnumResourceTypes +// Define common types if not building vccorlib.dll +#if !defined(VCWINRT_DLL) + +// Function decleration for types we use from Windows and CRT +// This prevents pulling in the headers +extern "C" +{ + long __stdcall __Platform_WindowsCreateString(const ::default::char16*, unsigned int, __abi_HSTRING*); + long __stdcall __Platform_WindowsDeleteString(__abi_HSTRING); + long __stdcall __Platform_WindowsDuplicateString(__abi_HSTRING, __abi_HSTRING*); + const ::default::char16* __stdcall __Platform_WindowsGetStringRawBuffer(__abi_HSTRING, unsigned int*); + unsigned int __stdcall __Platform_WindowsGetStringLen(__abi_HSTRING); + int __stdcall __Platform_WindowsIsStringEmpty(__abi_HSTRING); + long __stdcall __Platform_WindowsStringHasEmbeddedNull(__abi_HSTRING, int*); + long __stdcall __Platform_WindowsCompareStringOrdinal(__abi_HSTRING, __abi_HSTRING, int*); + long __stdcall __Platform_WindowsCreateStringReference(const ::default::char16*, unsigned int, __Platform_Details_HSTRING_HEADER*, __abi_HSTRING*); + long __stdcall __Platform_WindowsConcatString(__abi_HSTRING, __abi_HSTRING, __abi_HSTRING*); + void* __stdcall __Platform_CoTaskMemAlloc(size_t); + void __stdcall __Platform_CoTaskMemFree(void*); + size_t __cdecl __Platform_wcslen(const ::default::char16 *); + void * __cdecl __Platform_memset(void *, int, size_t); +} +#else // VCWINRT_DLL +#define __Platform_WindowsCreateString WindowsCreateString +#define __Platform_WindowsDeleteString WindowsDeleteString +#define __Platform_WindowsDuplicateString WindowsDuplicateString +#define __Platform_WindowsGetStringRawBuffer WindowsGetStringRawBuffer +#define __Platform_WindowsGetStringLen WindowsGetStringLen +#define __Platform_WindowsIsStringEmpty WindowsIsStringEmpty +#define __Platform_WindowsStringHasEmbeddedNull WindowsStringHasEmbeddedNull +#define __Platform_WindowsCompareStringOrdinal WindowsCompareStringOrdinal +#define __Platform_WindowsCreateStringReference WindowsCreateStringReference +#define __Platform_WindowsConcatString WindowsConcatString +#define __Platform_CoTaskMemAlloc CoTaskMemAlloc +#define __Platform_CoTaskMemFree CoTaskMemFree + +#define __Platform_wcslen wcslen +#define __Platform_memset memset + +#endif // VCWINRT_DLL + +#pragma endregion + +#pragma region String^ helpers +namespace Platform +{ + // Convert failure HRESULT from Windows String API's to Exception + namespace Details + { + inline void CreateString(const ::default::char16* __bufferArg, unsigned int __lengthArg, __abi_HSTRING* __destArg) + { + __abi_ThrowIfFailed( __Platform_WindowsCreateString((const ::default::char16 *)__bufferArg, __lengthArg, __destArg) ); + } + + inline void CreateString(const ::default::char16* __sourceStringArg, __abi_HSTRING* __destArg) + { + __abi_ThrowIfFailed( __Platform_WindowsCreateString((const ::default::char16 *)__sourceStringArg, __sourceStringArg ? static_cast<unsigned int>(__Platform_wcslen((const ::default::char16 *)__sourceStringArg)) : 0u, __destArg) ); + } + + inline void DuplicateString(__abi_HSTRING __sourceArg, __abi_HSTRING* __destArg) + { + if (__sourceArg == nullptr) + { + *__destArg = __sourceArg; + return; + } + + __abi_ThrowIfFailed( __Platform_WindowsDuplicateString(__sourceArg, __destArg) ); + } + + inline void CreateStringReference(const ::default::char16* __sourceStringArg, unsigned int __lengthArg, __Platform_Details_HSTRING_HEADER* __hstringHeaderArg, __abi_HSTRING* __stringArg) + { + __abi_ThrowIfFailed(__Platform_WindowsCreateStringReference(__sourceStringArg, __lengthArg, __hstringHeaderArg, __stringArg)); + } + } // namepsace Details + + // StringReference is used to hold onto a fast pass HSTRING. + class StringReference + { + public: + ~StringReference() + { + Free(); + } + StringReference() + { + Init(); + } + StringReference(const StringReference& __fstrArg) + { + Init(__fstrArg); + } + StringReference& operator=(const StringReference& __fstrArg) + { + if(this != &__fstrArg) + { + Free(); + Init(__fstrArg); + } + return *this; + } + + StringReference(const ::default::char16* __strArg) + { + Init(__strArg, __Platform_wcslen(__strArg)); + } + StringReference& operator=(const ::default::char16* __strArg) + { + Free(); + Init(__strArg, __Platform_wcslen(__strArg)); + return *this; + } + StringReference(const ::default::char16* __strArg, size_t __lenArg) + { + Init(__strArg, __lenArg); + } + + const ::default::char16 * Data() const + { + return __Platform_WindowsGetStringRawBuffer(GetHSTRING(), nullptr); + } + unsigned int Length() const + { + return __Platform_WindowsGetStringLen(GetHSTRING()); + } + + __declspec(no_release_return) __declspec(no_refcount) + operator ::Platform::String^() const + { + return reinterpret_cast< ::Platform::String^>(__hString); + } + __declspec(no_release_return) __declspec(no_refcount) + ::Platform::String^ GetString() const + { + return reinterpret_cast< ::Platform::String^>(__hString); + } + __abi_HSTRING GetHSTRING() const + { + return __hString; + } + private: + void Free() + { + __Platform_WindowsDeleteString(__hString); + } + void Init() + { + __Platform_memset(this, 0, sizeof(StringReference)); + } + void Init(const StringReference& __fstrArg) + { + unsigned int __length = 0; + const ::default::char16* __source = __Platform_WindowsGetStringRawBuffer(__fstrArg.GetHSTRING(), &__length); + Init(__source, __length); + } + void Init(const ::default::char16* __strArg, unsigned __int64 __lenArg) + { + if ((__strArg == nullptr) || (__lenArg == 0)) + Init(); + else if (__lenArg > 0xffffffffLL) // check if it exceeds the size of an integer + __abi_WinRTraiseInvalidArgumentException(); + else + { + unsigned int __length = (unsigned int) (__lenArg & 0xffffffffLL); + ::Platform::Details::CreateStringReference(__strArg, __length, &__header, &__hString); + } + } + void Init(const ::default::char16* __strArg, unsigned int __lenArg) + { + if ((__strArg == nullptr) || (__lenArg == 0)) + Init(); + else + { + ::Platform::Details::CreateStringReference(__strArg, __lenArg, &__header, &__hString); + } + } + + __Platform_Details_HSTRING_HEADER __header; + __abi_HSTRING __hString; + }; +} // namespace Platform + +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void* __abi_winrt_ptrto_string_ctor(const volatile ::Platform::String ^__strArg) +{ + if (__strArg) + { + __abi_HSTRING __hstr; + auto __pRaw = reinterpret_cast<__abi_HSTRING>((const_cast< ::Platform::String^>(__strArg))); + ::Platform::Details::DuplicateString(__pRaw, &__hstr); + return __hstr; + } + return nullptr; +} + +__declspec(non_user_code) __declspec(no_refcount) __declspec(no_release_return) + inline void* __abi_winrt_ptrto_string_assign(void** __ppTargetArg, const volatile ::Platform::String ^__pSourceArg) +{ + auto __pRaw = reinterpret_cast<__abi_HSTRING>((const_cast< ::Platform::String^>(__pSourceArg))); + if ( *__ppTargetArg != reinterpret_cast<void*>(__pRaw) ) + { + if (*__ppTargetArg) + { + __abi_details::__abi_delete_String(reinterpret_cast< ::Platform::String^>(*__ppTargetArg)); + } + *__ppTargetArg = nullptr; + if (__pSourceArg) + { + __abi_HSTRING __hstr; + ::Platform::Details::DuplicateString(__pRaw, &__hstr); + *__ppTargetArg = __hstr; + } + } + return *__ppTargetArg; +} + +namespace __abi_details +{ + + __declspec(non_user_code) __declspec(no_refcount) + inline void __abi_delete_String(::Platform::String^ __strArg) + { + __Platform_WindowsDeleteString(reinterpret_cast<__abi_HSTRING>(__strArg)); + } + +} // namespace __abi_details + +#pragma endregion + + +struct __abi_type_descriptor +{ + const __wchar_t* __typeName; + int __typeId; +}; + +#if !defined(VCWINRT_DLL) +__declspec(dllimport) ::Platform::Type^ __stdcall __abi_make_type_id(const __abi_type_descriptor&); +#else +__declspec(dllexport) ::Platform::Type^ __stdcall __abi_make_type_id(const __abi_type_descriptor&); +#endif + +inline Platform::String^ __abi_CustomToString(void*) +{ + return nullptr; +} + +namespace Platform +{ + namespace Details + { + void __cdecl ReportUnhandledError( ::Platform::Exception^); + + __declspec(dllexport) long __stdcall ReCreateFromException(::Platform::Exception^); + __declspec(dllexport) ::Platform::Object^ __stdcall CreateValue(::Platform::TypeCode, const void*); + __declspec(dllexport) void* __stdcall GetIBoxArrayVtable(void*); + __declspec(dllexport) void* __stdcall GetIBoxVtable(void*); + + template<typename T> + ref class + __declspec(no_empty_identity_interface) + CustomBox sealed : + public [::Windows::Foundation::Metadata::Default] [::Platform::Metadata::RuntimeClassName] ::Platform::IBox<T>, + public ::Platform::Details::IPrintable + { + T value_; + public: + CustomBox(T value) : value_(value) + { + *reinterpret_cast<void**>(static_cast< ::Platform::IValueType^>(this)) = + GetIBoxVtable(reinterpret_cast<void*>(static_cast< ::Platform::IBox<T>^>(this))); + } + + virtual property T Value + { + T get() + { + return value_; + } + } + + virtual Platform::String^ ToString() + { + return ToStringInternal<__is_enum(T)>(); + } + + private: + template<bool isEnum> + Platform::String^ ToStringInternal(); + + template<> + Platform::String^ ToStringInternal<true>() + { + String^ s = ::__abi_CustomToString(&value_); + if (s) + { + return s; + } + return T::typeid->FullName; + } + + template<> + Platform::String^ ToStringInternal<false>() + { + return ::__abi_details::__abi_ObjectToString(this, false); + } + }; + + ref class CustomValueType : public ::Platform::ValueType + { + }; + + ref class CustomEnum : public ::Platform::Enum + { + }; + + template<bool __isEnum> + struct BoxValueType + { + typedef CustomValueType Type; + }; + + template<> + struct BoxValueType<true> + { + typedef CustomEnum Type; + }; + + template<typename __TArg> + struct RemoveConst + { + typedef __TArg Type; + }; + + template<typename __TArg> + struct RemoveConst<const __TArg> + { + typedef __TArg Type; + }; + + template<typename __TArg> + struct RemoveVolatile + { + typedef __TArg Type; + }; + + template<typename __TArg> + struct RemoveVolatile<volatile __TArg> + { + typedef __TArg Type; + }; + + template<typename __TArg> + struct RemoveCV + { + typedef typename RemoveConst<typename RemoveVolatile<__TArg>::Type>::Type Type; + }; + } // namespace Details + + template<typename __TArg> + ref class + __declspec(one_phase_constructed) + __declspec(layout_as_external) + __declspec(no_empty_identity_interface) + Box abstract : + public ::Platform::IBox<typename ::Platform::Details::RemoveCV<__TArg>::Type>, + public Details::BoxValueType<__is_enum(__TArg)>::Type + { + static_assert(__is_enum(__TArg) || __is_value_class(__TArg) || __is_trivial(__TArg), "__TArg type of Box<__TArg> must be either value type or enum type"); + + typedef typename ::Platform::Details::RemoveCV<__TArg>::Type __TBoxValue; + internal: + Box(__TBoxValue __valueArg) + { + static auto __typeCode = ::Platform::Type::GetTypeCode(__TBoxValue::typeid); + ::Platform::Object ^__boxValue = Details::CreateValue(__typeCode, &__valueArg); + if (__boxValue == nullptr) + { + __boxValue = ref new Details::CustomBox<__TBoxValue>(__valueArg); + return reinterpret_cast<Box^>(__boxValue); + } + + return dynamic_cast<Box^>(__boxValue); + } + + operator __TBoxValue() + { + if (this == nullptr) + { + throw ref new ::Platform::NullReferenceException(); + } + + return safe_cast< ::Platform::IBox<__TBoxValue>^>(this)->Value; + } + + operator Box<const __TBoxValue>^() + { + return reinterpret_cast<Box<const __TBoxValue>^>(this); + } + + operator Box<volatile __TBoxValue>^() + { + return reinterpret_cast<Box<volatile __TBoxValue>^>(this); + } + + operator Box<const volatile __TBoxValue>^() + { + return reinterpret_cast<Box<const volatile __TBoxValue>^>(this); + } + + static operator Box<__TArg>^( ::Platform::IBox<__TArg>^ __boxValueArg) + { + return reinterpret_cast<Box<__TArg>^>(__boxValueArg); + } + + public: + virtual property __TBoxValue Value + { + __TBoxValue get() + { + if (this == nullptr) + { + throw ref new ::Platform::NullReferenceException(); + } + + return safe_cast< ::Platform::IBox<__TBoxValue>^>(this)->Value; + } + } + }; + + //////////////////////////////////////////////////////////////////////////////// + inline Guid::Guid() : __a(0), __b(0), __c(0), __d(0), __e(0), __f(0), __g(0), __h(0), __i(0), __j(0), __k(0) + { + } + + inline Guid::Guid(__rcGUID_t __guid) : + __a(reinterpret_cast<const __s_GUID&>(__guid).Data1), + __b(reinterpret_cast<const __s_GUID&>(__guid).Data2), + __c(reinterpret_cast<const __s_GUID&>(__guid).Data3), + __d(reinterpret_cast<const __s_GUID&>(__guid).Data4[0]), + __e(reinterpret_cast<const __s_GUID&>(__guid).Data4[1]), + __f(reinterpret_cast<const __s_GUID&>(__guid).Data4[2]), + __g(reinterpret_cast<const __s_GUID&>(__guid).Data4[3]), + __h(reinterpret_cast<const __s_GUID&>(__guid).Data4[4]), + __i(reinterpret_cast<const __s_GUID&>(__guid).Data4[5]), + __j(reinterpret_cast<const __s_GUID&>(__guid).Data4[6]), + __k(reinterpret_cast<const __s_GUID&>(__guid).Data4[7]) + { + } + + inline Guid::operator ::__rcGUID_t() + { + return reinterpret_cast<__rcGUID_t>(*this); + } + + inline bool ::Platform::Guid::Equals(::Platform::Guid __guidArg) + { + auto __a = reinterpret_cast<const unsigned long*>(this); + auto __b = reinterpret_cast<const unsigned long*>(&__guidArg); + + return (__a[0] == __b[0] && __a[1] == __b[1] && __a[2] == __b[2] && __a[3] == __b[3]); + } + + inline bool ::Platform::Guid::Equals(__rcGUID_t __guidArg) + { + auto __a = reinterpret_cast<const unsigned long*>(this); + auto __b = reinterpret_cast<const unsigned long*>(&__guidArg); + + return (__a[0] == __b[0] && __a[1] == __b[1] && __a[2] == __b[2] && __a[3] == __b[3]); + } + + inline bool ::Platform::Guid::operator==(::Platform::Guid __aArg, ::Platform::Guid __bArg) + { + auto __a = reinterpret_cast<const unsigned long*>(&__aArg); + auto __b = reinterpret_cast<const unsigned long*>(&__bArg); + + return (__a[0] == __b[0] && __a[1] == __b[1] && __a[2] == __b[2] && __a[3] == __b[3]); + } + + inline bool ::Platform::Guid::operator!=(::Platform::Guid __aArg, ::Platform::Guid __bArg) + { + return !(__aArg == __bArg); + } + + inline bool ::Platform::Guid::operator<(::Platform::Guid __aArg, ::Platform::Guid __bArg) + { + auto __a = reinterpret_cast<const unsigned long*>(&__aArg); + auto __b = reinterpret_cast<const unsigned long*>(&__bArg); + + if (__a[0] != __b[0]) + { + return __a[0] < __b[0]; + } + + if (__a[1] != __b[1]) + { + return __a[1] < __b[1]; + } + + if (__a[2] != __b[2]) + { + return __a[2] < __b[2]; + } + + if (__a[3] != __b[3]) + { + return __a[3] < __b[3]; + } + + return false; + } + + inline Guid::Guid(unsigned int __aArg, unsigned short __bArg, unsigned short __cArg, unsigned __int8 __dArg, + unsigned __int8 __eArg, unsigned __int8 __fArg, unsigned __int8 __gArg, unsigned __int8 __hArg, + unsigned __int8 __iArg, unsigned __int8 __jArg, unsigned __int8 __kArg) : + __a(__aArg), __b(__bArg), __c(__cArg), __d(__dArg), __e(__eArg), __f(__fArg), __g(__gArg), __h(__hArg), __i(__iArg), __j(__jArg), __k(__kArg) + { + } + + inline Guid::Guid(unsigned int __aArg, unsigned short __bArg, unsigned short __cArg, const ::Platform::Array<unsigned __int8>^ __dArg) : + __a(__aArg), __b(__bArg), __c(__cArg) + { + if (__dArg == nullptr || __dArg->Length != 8) + { + __abi_WinRTraiseInvalidArgumentException(); + } + __d = __dArg[0]; + __e = __dArg[1]; + __f = __dArg[2]; + __g = __dArg[3]; + __h = __dArg[4]; + __i = __dArg[5]; + __j = __dArg[6]; + __k = __dArg[7]; + } + + __declspec(selectany) ::Platform::Guid __winrt_GUID_NULL(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + + //////////////////////////////////////////////////////////////////////////////// + inline void* Details::Heap::Allocate(::Platform::SizeT /*__sizeArg*/, void* __pPlacementArg) + { + return __pPlacementArg; + } + + inline void* Details::Heap::Allocate(::Platform::SizeT /*__rcOffset*/, ::Platform::SizeT /*__sizeArg*/, void* __pPlacementArg) + { + return __pPlacementArg; + } + + inline void Details::Heap::PlacementFree(void* /*__pArg*/, void* /*__placementArg*/) + { + } +} // namespace Platform + +template <typename __TArg> +Platform::Box<typename ::Platform::Details::RemoveCV<__TArg>::Type>^ __abi_create_box(__TArg __boxValueArg) +{ + return ref new ::Platform::Box<__TArg>(__boxValueArg); +} + +template <typename __TArg> +__TArg __abi_unbox(::Platform::Object^ __objArg) +{ + return safe_cast< ::Platform::Box<__TArg>^>(__objArg); +} + +template<typename T> +__declspec(non_user_code) + inline T^ __abi_winrt_cast_from_object_to_delegate(bool __isDynamicCastArg, ::Platform::Object^ __objArg) +{ + if (__objArg == nullptr) { + return nullptr; + } + + auto __p(dynamic_cast< ::Platform::Details::CustomBox<T^> ^>(__objArg)); + if (__p == nullptr) { + if (__isDynamicCastArg) { + return nullptr; + } + + __abi_WinRTraiseInvalidCastException(); + } else { + return __p->Value; + } +} + +template<typename T> +__declspec(non_user_code) __declspec(no_refcount) + inline ::Platform::Object^ __abi_winrt_cast_from_delegate_to_object(bool /*__isDynamicCastArg*/, T^ __objArg) +{ + if (__objArg == nullptr) { + return nullptr; + } else { + return ref new ::Platform::Details::CustomBox<T^>(__objArg); + } +} + +inline long __stdcall __abi_FTMWeakRefData::__abi_QueryInterface(::Platform::Guid& __guid, void** __pOut) +{ + if (__pUnkMarshal == nullptr || __guid.Equals(__uuidof(__abi_IMarshal)) == false) + { + return 0x80004002; + } + if (__pUnkMarshal == reinterpret_cast<__abi_IUnknown*>(-1)) + { + __abi_IUnknown* __pUnkMarshalLocal; + +#if !defined(VCWINRT_DLL) + long __hr = ::__Platform_CoCreateFreeThreadedMarshaler(reinterpret_cast<::Platform::Object^>(__weakRefSource->__target), reinterpret_cast< ::Platform::Object^*>(&__pUnkMarshalLocal)); +#else + long __hr = ::CoCreateFreeThreadedMarshaler(reinterpret_cast<IUnknown*>(__weakRefSource->__target), reinterpret_cast<IUnknown**>(&__pUnkMarshalLocal)); +#endif + __abi_ThrowIfFailed(__hr); + + if (::_InterlockedCompareExchangePointer(reinterpret_cast<void* volatile*>(&__pUnkMarshal), __pUnkMarshalLocal, reinterpret_cast<void*>(-1)) != reinterpret_cast<void*>(-1)) + { + __pUnkMarshalLocal->__abi_Release(); + } + } + return __pUnkMarshal->__abi_QueryInterface(__guid, __pOut); +} + +inline long __stdcall ::Platform::Details::ControlBlock::__abi_QueryInterface(::Platform::Guid& __riid, void** __ppvObject) +{ + if (__riid.Equals(__uuidof(__abi_IUnknown)) || + __riid.Equals(__uuidof(__abi_IWeakReference))) + { + __abi_AddRef(); + *__ppvObject = this; + return 0; + } + *__ppvObject = nullptr; + return 0x80004002; +} + +#pragma region String +#pragma region String^ API + +namespace Platform +{ +#if !defined(_PREFIX_) && !defined(_PREFAST_) + + __declspec(no_refcount) + __declspec(no_release_return) + inline String::String() + { + return nullptr; + } + + __declspec(no_refcount) + inline String::String(__abi_HSTRING __hstrArg) + { + __abi_HSTRING __hstr; + Details::DuplicateString(__hstrArg, &__hstr); + return *reinterpret_cast<String^*>(&__hstr); + } + + __declspec(no_refcount) + inline String::String(const ::default::char16* __strArg) + { + if (__strArg == nullptr) + return nullptr; + + __abi_HSTRING __hstr = nullptr; + ::Platform::Details::CreateString(__strArg, (unsigned int)__Platform_wcslen(__strArg), &__hstr); + return *reinterpret_cast<String^*>(&__hstr); + } + + __declspec(no_refcount) + inline String::String(const ::default::char16* __strArg, unsigned int __lenArg) + { + if ((__strArg == nullptr) || (__lenArg == 0)) + return nullptr; + + __abi_HSTRING __hstr = nullptr; + ::Platform::Details::CreateString(__strArg, __lenArg, &__hstr); + return *reinterpret_cast<String^*>(&__hstr); + } + +#endif // !defined(_PREFIX_) && !defined(_PREFAST_) + + inline const ::default::char16* String::Begin() + { + return Data(); + } + inline const ::default::char16* String::End() + { + return Data() + Length(); + } + + inline Platform::String^ Object::ToString() + { + return ::__abi_details::__abi_ObjectToString(this, true); + } + + inline const ::default::char16 * String::Data() + { + return __Platform_WindowsGetStringRawBuffer(reinterpret_cast<__abi_HSTRING>(this), nullptr); + } + + inline unsigned int String::Length() + { + return __Platform_WindowsGetStringLen(reinterpret_cast<__abi_HSTRING>(this)); + } + inline bool String::IsEmpty() + { + return __Platform_WindowsIsStringEmpty(reinterpret_cast<__abi_HSTRING>(this)) ? true : false; + } + + inline bool String::IsFastPass() + { + return false; + } + + inline bool String::Equals(Object^ __strArg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(this), reinterpret_cast<__abi_HSTRING>(((String^)__strArg)), &__result); + return (__result == 0); + } + + inline bool String::Equals(String^ __str1Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(this), reinterpret_cast<__abi_HSTRING>(__str1Arg), &__result); + return (__result == 0); + } + inline int String::CompareOrdinal(String^ __str1Arg, String^ __str2Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), &__result); + return __result; + } + inline int String::GetHashCode() + { + int __hash = 0; + for (auto i = Begin(); i != End(); ++i) + __hash += *i; + return __hash; + } + + inline bool ::Platform::String::operator==(::Platform::String^ __str1Arg, ::Platform::String^ __str2Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), &__result); + return (__result == 0); + } + + inline bool ::Platform::String::operator!=(::Platform::String^ __str1Arg, ::Platform::String^ __str2Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), &__result); + return (__result != 0); + } + + inline bool ::Platform::String::operator<(::Platform::String^ __str1Arg, ::Platform::String^ __str2Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), &__result); + return (__result < 0); + } + + inline bool ::Platform::String::operator>(::Platform::String^ __str1Arg, ::Platform::String^ __str2Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), &__result); + return (__result > 0); + } + + inline bool ::Platform::String::operator<=(::Platform::String^ __str1Arg, ::Platform::String^ __str2Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), &__result); + return (__result <= 0); + } + + inline bool ::Platform::String::operator>=(::Platform::String^ __str1Arg, ::Platform::String^ __str2Arg) + { + int __result = 0; + __Platform_WindowsCompareStringOrdinal(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), &__result); + return (__result >= 0); + } + + inline ::Platform::String ^ ::Platform::String::Concat(::Platform::String^ __str1Arg, ::Platform::String^ __str2Arg) + { + String^ __str = nullptr; + __abi_ThrowIfFailed(__Platform_WindowsConcatString(reinterpret_cast<__abi_HSTRING>(__str1Arg), reinterpret_cast<__abi_HSTRING>(__str2Arg), reinterpret_cast<__abi_HSTRING*>(&__str))); + return __str; + } + + inline ::Platform::String ^ ::Platform::String::Concat(::Platform::Object^ __str1Arg, ::Platform::Object^ __str2Arg) + { + String^ __str = nullptr; +#pragma warning (suppress:6011) // False positive because __str?Arg not evaluated + __abi_ThrowIfFailed(__Platform_WindowsConcatString(reinterpret_cast<__abi_HSTRING>(__str1Arg->ToString()), reinterpret_cast<__abi_HSTRING>(__str2Arg->ToString()), reinterpret_cast<__abi_HSTRING*>(&__str))); + return __str; + } + + inline String^ String::ToString() + { + return ref new ::Platform::String(Data()); + } + +#pragma region string iterators + inline const ::default::char16 * begin(::Platform::String^ __strArg) + { + return __strArg->Begin(); + } + + inline const ::default::char16 * end(::Platform::String^ __strArg) + { + return __strArg->End(); + } +#pragma endregion +} // namespace Platform +#pragma endregion + +__declspec(non_user_code) + inline ::Platform::String^ __abi_winrt_CreateSystemStringFromLiteral(const ::default::char16* __strArg) +{ + return ref new ::Platform::String(__strArg); +} + +template<typename __TArg> +inline ::Platform::Array<__TArg, 1>^ __abi_winrt_CreateArray(unsigned int __sizeArg) +{ + return ref new ::Platform::Array<__TArg, 1>(__sizeArg); +} + +inline __declspec(no_refcount) __declspec(no_release_return) ::Platform::String^ __abi_winrt_CreateFastPassSystemStringFromLiteral(const ::default::char16* __strArg, unsigned int __lenArg, __Platform_Details_HSTRING_HEADER* __psheaderArg) +{ + __abi_HSTRING __hstr; + ::Platform::Details::CreateStringReference(__strArg, __lenArg, __psheaderArg, &__hstr); + return *reinterpret_cast< ::Platform::String^*>(&__hstr); +} + +inline __declspec(no_refcount) __declspec(no_release_return) ::Platform::String^ __abi_winrt_CreateFastPassSystemStringFromLiteral(const unsigned short* __strArg, unsigned int __lenArg, __Platform_Details_HSTRING_HEADER* __psheaderArg) +{ + return __abi_winrt_CreateFastPassSystemStringFromLiteral(reinterpret_cast<const ::default::char16*>(__strArg), __lenArg, __psheaderArg); +} + +#pragma endregion + +#pragma region Array + +namespace Platform +{ + namespace Details + { + // Attach to the external buffer + template <typename __HighLevelType, unsigned int __dimension> + __declspec(no_refcount) ::Platform::Array<__HighLevelType, __dimension>^ __abi_array_attach(void* __srcArg, unsigned int __elementcountArg, bool __isFastPassArg, bool __needsInitArg) + { + if (static_cast<unsigned int>(-1) / sizeof(__HighLevelType) < __elementcountArg) + { + __abi_WinRTraiseInvalidArgumentException(); + } + auto __arr = ref new ::Platform::Array<__HighLevelType, __dimension>(nullptr, 0); + + if (__needsInitArg) + { + __Platform_memset(__srcArg, 0, sizeof(__HighLevelType) * __elementcountArg); + } + if (!__isFastPassArg) + { + __arr->Attach(reinterpret_cast<__HighLevelType*>(__srcArg), __elementcountArg); + } + else + { + __arr->AttachFastPass(reinterpret_cast<__HighLevelType*>(__srcArg), __elementcountArg); + } + + return __arr; + } + + template <typename __HighLevelType, unsigned int __dimension> + void __abi_array_copy_to_and_release(::Platform::Array<__HighLevelType, __dimension>^ __arrArg, void** __destArg, unsigned int* __sizeArg) + { + if (__arrArg == nullptr) + { + *__destArg = nullptr; + *__sizeArg = 0; + return; + } + + __HighLevelType **__destHigh = reinterpret_cast<__HighLevelType **>(__destArg); + __arrArg->CopyToOrDetach(__destHigh, __sizeArg); + + // __arrArg is a local variable declared in a wrapper marked with no_refcount + // This function is called from that function, have to release the Array^ + reinterpret_cast<__abi_IUnknown*>(static_cast< ::Platform::Object^>(__arrArg))->__abi_Release(); + // The caller will not use __arrArg after this function returns + } + + // Convert ::Platform::Array to raw buffer + // It is called when we are converting in Array from high level to low level + template <typename __LowLevelType, typename __HighLevelType, unsigned int __dimension> + __LowLevelType* __abi_array_to_raw(const ::Platform::Array<__HighLevelType, __dimension>^ __arrArg) + { + if (__arrArg == nullptr) + { + return nullptr; + } + return reinterpret_cast<__LowLevelType*>(__arrArg->Data); + } + + template <typename __TArg> + struct array_helper<__TArg, true> + { + static void DestructElementsAndFree(__TArg* __srcArg, unsigned int) + { + __Platform_CoTaskMemFree(__srcArg); + } + }; + + template <typename __TArg> + struct array_helper<__TArg, false> + { + static void DestructElementsAndFree(__TArg* __srcArg, unsigned int __countArg) + { + typedef __TArg typeT; + for (unsigned int __i = 0; __i < __countArg; __i++) + { + (&__srcArg[__i])->~typeT(); + } + __Platform_CoTaskMemFree(__srcArg); + } + }; + } // namespace Details + + template <typename __TArg> + class ArrayReference<__TArg, 1> + { + default::uint8 __data[sizeof(Array<__TArg>)]; + void Init(__TArg* __dataArg, unsigned int __sizeArg, bool __needsInitArg = false) + { + __Platform_memset(__data, 0, sizeof(Array<__TArg>)); + ArrayReference* __pThis = this; + Array<__TArg>^* __pArrayThis = reinterpret_cast<Array<__TArg>^*>(&__pThis); + (*__pArrayThis)->ArrayReferenceInit(); + + if (__needsInitArg) + { + if (static_cast<unsigned int>(-1) / sizeof(__TArg) < __sizeArg) + { + __abi_WinRTraiseInvalidArgumentException(); + } + __Platform_memset(__dataArg, 0, sizeof(__TArg) * __sizeArg); + } + + (*__pArrayThis)->AttachFastPass(__dataArg, __sizeArg); + } + public: + ArrayReference(__TArg* __dataArg, unsigned int __sizeArg, bool __needsInitArg = false) + { + Init(__dataArg, __sizeArg, __needsInitArg); + } + + ArrayReference(ArrayReference&& __otherArg) + { + Array<__TArg>^* __pOther = reinterpret_cast<Array<__TArg>^*>(&__otherArg); + Init((*__pOther)->__data, (*__pOther)->__size); + } + + ArrayReference& operator=(ArrayReference&& __otherArg) + { + Array<__TArg>^* __pOther = reinterpret_cast<Array<__TArg>^*>(&__otherArg); + Init((*pOther)->__data, (*pOther)->__size); + } + + __declspec(no_refcount) __declspec(no_release_return) + operator Array<__TArg>^() + { + ArrayReference* __pThis = this; + Array<__TArg>^* __pArrayThis = reinterpret_cast<Array<__TArg>^*>(&__pThis); + + return *__pArrayThis; + } + private: + ArrayReference(const ArrayReference&); + ArrayReference& operator=(const ArrayReference&); + }; + +#pragma region ::Platform::WriteOnlyArray + template <typename __TArg> + inline WriteOnlyArray<__TArg, 1>::WriteOnlyArray() : __size(0), __fastpassflag(false), __data(nullptr) + { + } + + template <typename __TArg> + inline WriteOnlyArray<__TArg, 1>::WriteOnlyArray(unsigned int __sizeArg) : __size(0), __fastpassflag(false), __data(nullptr) + { + if (__sizeArg == 0) + { + return; + } + __data = AllocateAndZeroInitialize(__sizeArg); + __size = __sizeArg; + } + + template <typename __TArg> + inline WriteOnlyArray<__TArg, 1>::WriteOnlyArray(__TArg* __dataArg, unsigned int __sizeArg) : __size(0), __fastpassflag(false), __data(nullptr) + { + if (__sizeArg == 0) + { + return; + } + __data = AllocateAndCopyElements(__dataArg, __sizeArg); + __size = __sizeArg; + } + + template <typename __TArg> + inline WriteOnlyArray<__TArg, 1>::~WriteOnlyArray() + { + if ((__fastpassflag == false) && (__data != nullptr)) + { + ::Platform::Details::array_helper<__TArg>::DestructElementsAndFree(__data, __size); + } + Clear(); + } + + template <typename __TArg> + inline __TArg& WriteOnlyArray<__TArg, 1>::set(unsigned int __positionArg, __TArg __valueArg) + { + if (__data == nullptr) + { + __abi_WinRTraiseNullReferenceException(); + } + + if (__positionArg >= 0 && __positionArg < __size) + { + __data[__positionArg] = __valueArg; + return __data[__positionArg]; + } + + __abi_WinRTraiseOutOfBoundsException(); + } + + template <typename __TArg> + inline __TArg& WriteOnlyArray<__TArg, 1>::get(unsigned int __positionArg) const + { + if (__data == nullptr) + { + __abi_WinRTraiseNullReferenceException(); + } + + if (__positionArg >= 0 && __positionArg < __size) + { + return __data[__positionArg]; + } + + __abi_WinRTraiseOutOfBoundsException(); + } + +#if defined(_PREFIX_) || defined(_PREFAST_) + template <typename __TArg> + inline __TArg& WriteOnlyArray<__TArg, 1>::operator [](unsigned int __positionArg) const + { + if (__data == nullptr) + { + __abi_WinRTraiseNullReferenceException(); + } + + if (__positionArg >= 0 && __positionArg < __size) + { + return __data[__positionArg]; + } + + __abi_WinRTraiseOutOfBoundsException(); + } +#endif // #if defined(_PREFIX_) || defined(_PREFAST_) + + template <typename __TArg> + inline __TArg* WriteOnlyArray<__TArg, 1>::begin() const + { + if (__data == nullptr) + { + return nullptr; + } + + return &(__data[0]); + } + + template <typename __TArg> + inline __TArg* WriteOnlyArray<__TArg, 1>::end() const + { + if (__data == nullptr) + { + return nullptr; + } + + return &(__data[__size]); + } + + template <typename __TArg> + inline unsigned int WriteOnlyArray<__TArg, 1>::Length::get() const + { + return __size; + } + + template <typename __TArg> + inline __TArg* WriteOnlyArray<__TArg, 1>::Data::get() const + { + return this->begin(); + } + + template <typename __TArg> + inline bool WriteOnlyArray<__TArg, 1>::FastPass::get() const + { + return __fastpassflag; + } + + template <typename __TArg> + inline void WriteOnlyArray<__TArg, 1>::Clear() + { + __size = 0; + __fastpassflag = false; + __data = nullptr; + } + + template <typename __TArg> + inline __TArg* WriteOnlyArray<__TArg, 1>::AllocateAndZeroInitialize(unsigned int __countArg) + { + __TArg* __dest = nullptr; + if (__countArg == 0) + { + return __dest; + } + + if (static_cast<unsigned int>(-1) / sizeof(__TArg) < __countArg) + { + __abi_WinRTraiseInvalidCastException(); + } + __dest = (__TArg*)__Platform_CoTaskMemAlloc(__countArg * sizeof(__TArg)); + if (__dest == nullptr) + { + __abi_WinRTraiseOutOfMemoryException(); + } + + __Platform_memset(__dest, 0, __countArg * sizeof(__TArg)); + return __dest; + } + + template <typename __TArg> + inline __TArg* WriteOnlyArray<__TArg, 1>::AllocateAndCopyElements(const __TArg* __srcArg, unsigned int __countArg) + { + __TArg* __dest = AllocateAndZeroInitialize(__countArg); + for (unsigned int __i = 0; __i < __countArg; ++__i) + { + __dest[__i] = __srcArg[__i]; + } + return __dest; + } +#pragma endregion + +#pragma region ::Platform::Array + template <typename __TArg> + inline Array<__TArg, 1>::Array() : WriteOnlyArray() + { + *reinterpret_cast<void**>(static_cast< ::Platform::IValueType^ >(this)) = + Details::GetIBoxArrayVtable(reinterpret_cast<void*>(static_cast< ::Platform::IBoxArray<__TArg>^ >(this))); + } + + template <typename __TArg> + inline Array<__TArg, 1>::Array(const Array<__TArg, 1>^ __source) : WriteOnlyArray(__source ? __source->Data : nullptr, __source ? __source->Length : 0) + { + *reinterpret_cast<void**>(static_cast< ::Platform::IValueType^ >(this)) = + Details::GetIBoxArrayVtable(reinterpret_cast<void*>(static_cast< ::Platform::IBoxArray<__TArg>^ >(this))); + } + + template <typename __TArg> + inline Array<__TArg, 1>::Array(unsigned int __sizeArg) : WriteOnlyArray(__sizeArg) + { + *reinterpret_cast<void**>(static_cast< ::Platform::IValueType^ >(this)) = + Details::GetIBoxArrayVtable(reinterpret_cast<void*>(static_cast< ::Platform::IBoxArray<__TArg>^ >(this))); + } + + template <typename __TArg> + inline Array<__TArg, 1>::Array(__TArg* __dataArg, unsigned int __sizeArg) : WriteOnlyArray(__dataArg, __sizeArg) + { + *reinterpret_cast<void**>(static_cast< ::Platform::IValueType^ >(this)) = + Details::GetIBoxArrayVtable(reinterpret_cast<void*>(static_cast< ::Platform::IBoxArray<__TArg>^>(this))); + } + + template <typename T> + inline T& Array<T, 1>::get(unsigned int __positionArg) const + { + return WriteOnlyArray<T, 1>::get(__positionArg); + } + +#if defined(_PREFIX_) || defined(_PREFAST_) + template <typename T> + inline T& Array<T, 1>::operator[](unsigned int __positionArg) const + { + return WriteOnlyArray<T, 1>::operator[](__positionArg); + } +#endif // defined(_PREFIX_) || defined(_PREFAST_) + + template <typename __TArg> + inline Array<__TArg, 1>^ Array<__TArg, 1>::Value::get() + { + return this; + } + + template <typename __TArg> + inline void Array<__TArg, 1>::Attach(__TArg* __srcArg, unsigned int __sizeArg) + { + // Precondition: + // default constructed object + // Postcondition: + // _data = src + // _size = size + // _fastpassflag = false + // _refcount = 1 + + if (__size == 0 && __data == nullptr) + { + __size = __sizeArg; + __fastpassflag = false; + __data = __srcArg; + return; + } + + __abi_WinRTraiseFailureException(); + } + + template <typename __TArg> + inline void Array<__TArg, 1>::AttachFastPass(__TArg* __srcArg, unsigned int __sizeArg) + { + // Precondition: + // default constructed object + // Postcondition: + // _data = src + // _size = size + // _fastpassflag = true + + if (__size == 0 && __data == nullptr) + { + __size = __sizeArg; + __fastpassflag = true; + __data = __srcArg; + return; + } + + __abi_WinRTraiseFailureException(); + } + + template <typename __TArg> + inline void Array<__TArg, 1>::CopyToOrDetach(__TArg** __destArg, unsigned int* __sizeArg) + { + // Postcondition: + // if (_refcount == 1 && !_fastpassflag) + // *dest = _data + // *size = _size + // Clear() + // if (_refcount > 1 || _fastpassflag) + // *dest = new buffer with contents of _data + // *size = _size + + if ((__destArg == nullptr) || (__sizeArg == nullptr)) + { + __abi_WinRTraiseNullReferenceException(); + } + + if (__size == 0) + { + *__destArg = nullptr; + *__sizeArg = 0; + return; + } + + if(__data == nullptr) + { + __abi_WinRTraiseFailureException(); + } + + if (!__fastpassflag && __abi_reference_count.Get() == 1) + { + *__destArg = __data; + *__sizeArg = __size; + Clear(); + } + else if (__fastpassflag || __abi_reference_count.Get() > 1) + { + *__sizeArg = __size; + *__destArg = AllocateAndCopyElements(__data, __size); + } + else + { + __abi_WinRTraiseFailureException(); + } + } + +#pragma endregion + +#pragma region Array iterators + template<class __TArg> + __TArg * begin(const Array<__TArg, 1>^ __arrArg) + { + return __arrArg->begin(); + } + + template<class __TArg> + __TArg * end(const Array<__TArg, 1>^ __arrArg) + { + return __arrArg->end(); + } +#pragma endregion +} // namespace Platform { +#pragma endregion + +namespace Platform +{ + namespace Details + { +#if !defined(VCWINRT_DLL) + __declspec(dllimport) void __stdcall EventSourceInitialize(void**); + __declspec(dllimport) void __stdcall EventSourceUninitialize(void**); + __declspec(dllimport) void* __stdcall EventSourceGetTargetArray(void*, EventLock*); + __declspec(dllimport) unsigned int __stdcall EventSourceGetTargetArraySize(void*); + __declspec(dllimport) void* __stdcall EventSourceGetTargetArrayEvent(void*, unsigned int, const void*, __int64*); + __declspec(dllimport) ::Windows::Foundation::EventRegistrationToken __stdcall EventSourceAdd(void**, EventLock*, ::Platform::Delegate^); + __declspec(dllimport) void __stdcall EventSourceRemove(void**, EventLock*, ::Windows::Foundation::EventRegistrationToken); + __declspec(dllimport) __abi_IUnknown* __stdcall GetWeakReference(const volatile ::Platform::Object^ const other); + __declspec(dllimport) __declspec(no_refcount) ::Platform::Object^ __stdcall ResolveWeakReference(const ::_GUID& guid, __abi_IUnknown** weakRef); +#else + __declspec(dllexport) void __stdcall EventSourceInitialize(void**); + __declspec(dllexport) void __stdcall EventSourceUninitialize(void**); + __declspec(dllexport) void* __stdcall EventSourceGetTargetArray(void*, EventLock*); + __declspec(dllexport) unsigned int __stdcall EventSourceGetTargetArraySize(void*); + __declspec(dllexport) void* __stdcall EventSourceGetTargetArrayEvent(void*, unsigned int, const void*, __int64*); + __declspec(dllexport) ::Windows::Foundation::EventRegistrationToken __stdcall EventSourceAdd(void**, EventLock*, ::Platform::Delegate^); + __declspec(dllexport) void __stdcall EventSourceRemove(void**, EventLock*, ::Windows::Foundation::EventRegistrationToken); + __declspec(dllexport) __abi_IUnknown* __stdcall GetWeakReference(const volatile ::Platform::Object^ const other); + __declspec(dllexport) __declspec(no_refcount) ::Platform::Object^ __stdcall ResolveWeakReference(const ::_GUID& guid, __abi_IUnknown** weakRef); +#endif + } // Details + + class EventSource + { + public: + EventSource() + { + Details::EventSourceInitialize(&__targets); + } + + ~EventSource() + { + Details::EventSourceUninitialize(&__targets); + } + + ::Windows::Foundation::EventRegistrationToken Add(Details::EventLock* __lockArg, ::Platform::Object^ __delegateInterfaceArg) + { + return Details::EventSourceAdd(&__targets, __lockArg, reinterpret_cast< ::Platform::Delegate^>(__delegateInterfaceArg)); + } + + void Remove(Details::EventLock* __lockArg, ::Windows::Foundation::EventRegistrationToken __tokenArg) + { + Details::EventSourceRemove(&__targets, __lockArg, __tokenArg); + } + private: + // __TInvokeMethod is a functor that performs the appropriate invoke, depending on the + // number of arguments specified. + template <typename __TDelegate, typename __TReturnType, typename __TInvokeMethod> + typename __TReturnType DoInvoke(Details::EventLock* __lockArg, __TInvokeMethod __invokeOneArg) + { + // lock pointer exhange + // targets = _targets + // unlock pointer exhange + // iterate all targets and do invoke + + // The _targetsPointerLock protects the acquisition of an AddRef'd pointer to + // "current list". An Add/Remove operation may occur during the + // firing of events (but occurs on a copy of the list). i.e. both + // DoInvoke/invoke and Add/Remove are readers of the "current list". + // NOTE: EventSource::Invoke(...) must never take the _addRemoveLock. + ::Platform::Object^ __targetsLoc; + // Attaching Array without AddRef'ing + *reinterpret_cast<void**>(&__targetsLoc) = Details::EventSourceGetTargetArray(__targets, __lockArg); + + typename __TReturnType __returnVal = typename __TReturnType(); + // The list may not exist if nobody has registered + if (__targetsLoc != nullptr) + { + const unsigned int __size = Details::EventSourceGetTargetArraySize(reinterpret_cast<void*>(__targetsLoc)); + + for (unsigned int __index = 0; __index < __size; __index++) + { + ::Windows::Foundation::EventRegistrationToken __token = {}; + + try + { + __TDelegate^ __element; + *reinterpret_cast<void**>(&__element) = Details::EventSourceGetTargetArrayEvent( + reinterpret_cast<void*>(__targetsLoc), + __index, + reinterpret_cast<const void*>(&__uuidof(__TDelegate^)), + &__token.Value + ); + + __returnVal = (__invokeOneArg)(__element); + } + catch(::Platform::Exception^ e) + { + int __hr = e->HResult; + if (__hr == 0x800706BA /* HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) */ + || __hr == 0x80010108 /* RPC_E_DISCONNECTED */ + || __hr == 0x800A1393L /* SCRIPT_E_CANTEXECUTE */) + { + Details::EventSourceRemove(&__targets, __lockArg, __token); + } + else + { + throw e; + } + } + } + } + return __returnVal; + } + + // __TInvokeMethod is a functor that performs the appropriate invoke, depending on the + // number of arguments specified. + template <typename __TDelegate, typename __TInvokeMethod> + void DoInvokeVoid(Details::EventLock* __lockArg, __TInvokeMethod __invokeOneArg) + { + // lock pointer exhange + // targets = _targets + // unlock pointer exhange + // iterate all targets and do invoke + + // The _targetsPointerLock protects the acquisition of an AddRef'd pointer to + // "current list". An Add/Remove operation may occur during the + // firing of events (but occurs on a copy of the list). i.e. both + // Invoke/invoke and Add/Remove are readers of the "current list". + // NOTE: EventSource::Invoke(...) must never take the _addRemoveLock. + ::Platform::Object^ __targetsLoc; + // Attaching Array without AddRef'ing + *reinterpret_cast<void**>(&__targetsLoc) = Details::EventSourceGetTargetArray(__targets, __lockArg); + + // The list may not exist if nobody has registered + if (__targetsLoc != nullptr) + { + const unsigned int __size = Details::EventSourceGetTargetArraySize(reinterpret_cast<void*>(__targetsLoc)); + + for (unsigned int __index = 0; __index < __size; __index++) + { + ::Windows::Foundation::EventRegistrationToken __token = {}; + + try + { + __TDelegate^ __element; + *reinterpret_cast<void**>(&__element) = Details::EventSourceGetTargetArrayEvent( + reinterpret_cast<void*>(__targetsLoc), + __index, + reinterpret_cast<const void*>(&__uuidof(__TDelegate^)), + &__token.Value + ); + + (__invokeOneArg)(__element); + } + catch(::Platform::Exception^ e) + { + int __hr = e->HResult; + if (__hr == 0x800706BA /* HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) */ + || __hr == 0x80010108 /* RPC_E_DISCONNECTED */ + || __hr == 0x800A1393L /* SCRIPT_E_CANTEXECUTE */) + { + Details::EventSourceRemove(&__targets, __lockArg, __token); + } + else + { + throw e; + } + } + } + } + } + + public: + template < typename __TLambda, typename... __TArgs > + void InvokeVoid(Details::EventLock* __lockArg, __TArgs... __args) + { + DoInvokeVoid<__TLambda>(__lockArg, [__args...](__TLambda^ __lambda) -> void { __lambda(__args...); }); + } + + template < typename __TLambda, typename __TReturnType, typename... __TArgs > + typename __TReturnType Invoke(Details::EventLock* __lockArg, __TArgs... __args) + { + return DoInvoke<__TLambda, __TReturnType>(__lockArg, [__args...](__TLambda^ __lambda) -> typename __TReturnType { return __lambda(__args...); }); + } + + protected: + void* __targets; + }; + + class Module + { + public: + typedef void (__stdcall *UnregisterCallback)(); + static ::Platform::Object^ __stdcall RegisterFactories(UnregisterCallback callback = nullptr); + static void __stdcall RunServer(const ::default::char16* __serverName = nullptr); + static ::Platform::Details::IActivationFactory^ __stdcall GetActivationFactory(::Platform::String^); + static bool __stdcall CanUnloadNow(); + }; + + inline ::default::int32 Exception::HResult::get() + { + return __hresult; + } + + class WeakReference + { + private: + mutable __abi_IUnknown* __weakPtr; + + void InternalAddRef() + { + if (__weakPtr != nullptr) + { + __weakPtr->__abi_AddRef(); + } + } + + void InternalRelease() + { + __abi_IUnknown* __tmp = __weakPtr; + if (__tmp != nullptr) + { + __weakPtr = nullptr; + __tmp->__abi_Release(); + } + } + public: + struct BoolStruct + { + int Member; + }; + + typedef int BoolStruct::* BoolType; + + WeakReference() throw() : __weakPtr(nullptr) + { + } + + WeakReference(decltype(__nullptr)) throw() : __weakPtr(nullptr) + { + } + + WeakReference(const WeakReference& __otherArg) throw() : __weakPtr(__otherArg.__weakPtr) + { + InternalAddRef(); + } + + WeakReference(WeakReference&& __otherArg) throw() : __weakPtr(__otherArg.__weakPtr) + { + __otherArg.__weakPtr = nullptr; + } + + explicit WeakReference(const volatile ::Platform::Object^ const __otherArg) : __weakPtr(nullptr) + { + __weakPtr = Details::GetWeakReference(__otherArg); + } + + ~WeakReference() throw() + { + InternalRelease(); + } + + WeakReference& operator=(decltype(__nullptr)) throw() + { + InternalRelease(); + return *this; + } + + WeakReference& operator=(const WeakReference& __otherArg) throw() + { + if (&__otherArg != this) + { + InternalRelease(); + __weakPtr = __otherArg.__weakPtr; + InternalAddRef(); + } + return *this; + } + + WeakReference& operator=(WeakReference&& __otherArg) throw() + { + InternalRelease(); + __weakPtr = __otherArg.__weakPtr; + __otherArg.__weakPtr = nullptr; + return *this; + } + + WeakReference& operator=(const volatile ::Platform::Object^ const __otherArg) + { + __abi_IUnknown* __weakPtrLoc = Details::GetWeakReference(__otherArg); + InternalRelease(); + __weakPtr = __weakPtrLoc; + return *this; + } + + template<typename __TArg> + __declspec(no_refcount) + __TArg^ Resolve() const + { + return reinterpret_cast<__TArg^>(Details::ResolveWeakReference(__uuidof(__TArg^), &__weakPtr)); + } + + operator BoolType() const throw() + { + return __weakPtr != nullptr ? &BoolStruct::Member : nullptr; + } + + friend bool operator==(const WeakReference&, const WeakReference&) throw(); + friend bool operator==(const WeakReference&, decltype(__nullptr)) throw(); + friend bool operator==(decltype(__nullptr), const WeakReference&) throw(); + friend bool operator!=(const WeakReference&, const WeakReference&) throw(); + friend bool operator!=(const WeakReference&, decltype(__nullptr)) throw(); + friend bool operator!=(decltype(__nullptr), const WeakReference&) throw(); + friend bool operator<(const WeakReference&, const WeakReference&) throw(); + }; + + inline bool operator==(const WeakReference& __aArg, const WeakReference& __bArg) throw() + { + return __aArg.__weakPtr == __bArg.__weakPtr; + } + + inline bool operator==(const WeakReference& __aArg, decltype(__nullptr)) throw() + { + return __aArg.__weakPtr == nullptr; + } + + inline bool operator==(decltype(__nullptr), const WeakReference& __bArg) throw() + { + return __bArg.__weakPtr == nullptr; + } + + inline bool operator!=(const WeakReference& __aArg, const WeakReference& __bArg) throw() + { + return __aArg.__weakPtr != __bArg.__weakPtr; + } + + inline bool operator!=(const WeakReference& __aArg, decltype(__nullptr)) throw() + { + return __aArg.__weakPtr != nullptr; + } + + inline bool operator!=(decltype(__nullptr), const WeakReference& __bArg) throw() + { + return __bArg.__weakPtr != nullptr; + } + + inline bool operator<(const WeakReference& __aArg, const WeakReference& __bArg) throw() + { + return __aArg.__weakPtr < __bArg.__weakPtr; + } +} // namespace Platform + +namespace Windows { namespace Foundation +{ + inline Point::Point(float __xArg, float __yArg) : X(__xArg), Y(__yArg) + { + } + + inline bool Point::operator ==(Point __point1Arg, Point __point2Arg) + { + return __point1Arg.X == __point2Arg.X && __point1Arg.Y == __point2Arg.Y; + } + + inline bool Point::operator !=(Point __point1Arg, Point __point2Arg) + { + return !(__point1Arg == __point2Arg); + } + + // Size + inline Size::Size(float __widthArg, float __heightArg) + { + if (__widthArg < 0 || __heightArg < 0) + { + __abi_WinRTraiseInvalidArgumentException(); + } + + Width = __widthArg; + Height = __heightArg; + } + + inline bool Size::IsEmpty::get() + { + return Width < 0; + } + + inline bool Size::operator ==(Size __size1Arg, Size __size2Arg) + { + return __size1Arg.Height == __size2Arg.Height && __size1Arg.Width == __size2Arg.Width; + } + + inline bool Size::operator !=(Size __size1Arg, Size __size2Arg) + { + return !(__size1Arg == __size2Arg); + } + + inline Rect::Rect(float __xArg, float __yArg, float __widthArg, float __heightArg) + { + if (__widthArg < 0 || __heightArg < 0) + { + __abi_WinRTraiseInvalidArgumentException(); + } + + X = __xArg; + Y = __yArg; + Width = __widthArg; + Height = __heightArg; + } + + inline bool Rect::IsEmpty::get() + { + return Width < 0; + } + + inline float Rect::Left::get() + { + return X; + } + + inline float Rect::Top::get() + { + return Y; + } + + inline bool Rect::operator ==(Rect __rect1Arg, Rect __rect2Arg) + { + return __rect1Arg.X == __rect2Arg.X + && __rect1Arg.Y == __rect2Arg.Y + && __rect1Arg.Width == __rect2Arg.Width + && __rect1Arg.Height == __rect2Arg.Height; + } + + inline bool Rect::operator !=(Rect __rect1Arg, Rect __rect2Arg) + { + return !(__rect1Arg == __rect2Arg); + } +} } // namespace Windows::Foundation + +namespace Windows { namespace UI { namespace Xaml +{ + inline Thickness::Thickness(double __uniformLengthArg) + { + Left = Top = Right = Bottom = __uniformLengthArg; + } + + inline Thickness::Thickness(double __leftArg, double __topArg, double __rightArg, double __bottomArg) + { + Left = __leftArg; + Top = __topArg; + Right = __rightArg; + Bottom = __bottomArg; + } + + inline bool Thickness::operator==(Thickness __thickness1Arg, Thickness __thickness2Arg) + { + return __thickness1Arg.Left == __thickness2Arg.Left && + __thickness1Arg.Top == __thickness2Arg.Top && + __thickness1Arg.Right == __thickness2Arg.Right && + __thickness1Arg.Bottom == __thickness2Arg.Bottom; + } + + inline bool Thickness::operator!=(Thickness __thickness1Arg, Thickness __thickness2Arg) + { + return !(__thickness1Arg == __thickness2Arg); + } + + inline CornerRadius::CornerRadius(double __uniformRadiusArg) + { + TopLeft = TopRight = BottomRight = BottomLeft = __uniformRadiusArg; + } + + inline CornerRadius::CornerRadius(double __topLeftArg, double __topRightArg, double __bottomRightArg, double __bottomLeftArg) + { + TopLeft = __topLeftArg; + TopRight = __topRightArg; + BottomRight = __bottomRightArg; + BottomLeft = __bottomLeftArg; + } + + inline bool CornerRadius::operator==(CornerRadius __cornerRadius1Arg, CornerRadius __cornerRadius2Arg) + { + return __cornerRadius1Arg.TopLeft == __cornerRadius2Arg.TopLeft && + __cornerRadius1Arg.TopRight == __cornerRadius2Arg.TopRight && + __cornerRadius1Arg.BottomRight == __cornerRadius2Arg.BottomRight && + __cornerRadius1Arg.BottomLeft == __cornerRadius2Arg.BottomLeft; + } + + inline bool CornerRadius::operator!=(CornerRadius __cornerRadius1Arg, CornerRadius __cornerRadius2Arg) + { + return !(__cornerRadius1Arg == __cornerRadius2Arg); + } + + namespace Media + { + inline Matrix Matrix::Identity::get() + { + return Matrix(1, 0, 0, 1, 0, 0); + } + + inline bool Matrix::IsIdentity::get() + { + return M11 == 1 && + M12 == 0 && + M21 == 0 && + M22 == 1 && + OffsetX == 0 && + OffsetY == 0; + } + + inline Windows::Foundation::Point Matrix::Transform(Windows::Foundation::Point __pointArg) + { + float x = __pointArg.X; + float y = __pointArg.Y; + double num = (y * M21) + OffsetX; + double num2 = (x * M12) + OffsetY; + x *= (float)M11; + x += (float)num; + y *= (float)M22; + y += (float)num2; + return Windows::Foundation::Point(x, y); + } + + inline Matrix::Matrix(double __m11Arg, double __m12Arg, double __m21Arg, double __m22Arg, double __offsetXArg, double __offsetYArg) + { + M11 = __m11Arg; + M12 = __m12Arg; + M21 = __m21Arg; + M22 = __m22Arg; + OffsetX = __offsetXArg; + OffsetY = __offsetYArg; + } + + inline bool Matrix::operator==(Matrix __matrix1Arg, Matrix __matrix2Arg) + { + return + __matrix1Arg.M11 == __matrix2Arg.M11 && + __matrix1Arg.M12 == __matrix2Arg.M12 && + __matrix1Arg.M21 == __matrix2Arg.M21 && + __matrix1Arg.M22 == __matrix2Arg.M22 && + __matrix1Arg.OffsetX == __matrix2Arg.OffsetX && + __matrix1Arg.OffsetY == __matrix2Arg.OffsetY; + } + + inline bool Matrix::operator!=(Matrix __matrix1Arg, Matrix __matrix2Arg) + { + return !(__matrix1Arg == __matrix2Arg); + } + + namespace Media3D + { + inline Matrix3D Matrix3D::Identity::get() + { + return Matrix3D(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + } + + inline bool Matrix3D::IsIdentity::get() + { + return M11 == 1 && M12 == 0 && M13 == 0 && M14 == 0 && + M21 == 0 && M22 == 1 && M23 == 0 && M24 == 0 && + M31 == 0 && M32 == 0 && M33 == 1 && M34 == 0 && + OffsetX == 0 && OffsetY == 0 && OffsetZ == 0 && M44 == 1; + } + + inline Matrix3D::Matrix3D(double __m11Arg, double __m12Arg, double __m13Arg, double __m14Arg, + double __m21Arg, double __m22Arg, double __m23Arg, double __m24Arg, + double __m31Arg, double __m32Arg, double __m33Arg, double __m34Arg, + double __offsetXArg, double __offsetYArg, double __offsetZArg, double __m44Arg) + { + M11 = __m11Arg; + M12 = __m12Arg; + M13 = __m13Arg; + M14 = __m14Arg; + M21 = __m21Arg; + M22 = __m22Arg; + M23 = __m23Arg; + M24 = __m24Arg; + M31 = __m31Arg; + M32 = __m32Arg; + M33 = __m33Arg; + M34 = __m34Arg; + OffsetX = __offsetXArg; + OffsetY = __offsetYArg; + OffsetZ = __offsetZArg; + M44 = __m44Arg; + } + + inline bool Matrix3D::operator==(Matrix3D __matrix1Arg, Matrix3D __matrix2Arg) + { + return + __matrix1Arg.M11 == __matrix2Arg.M11 && + __matrix1Arg.M12 == __matrix2Arg.M12 && + __matrix1Arg.M13 == __matrix2Arg.M13 && + __matrix1Arg.M14 == __matrix2Arg.M14 && + __matrix1Arg.M21 == __matrix2Arg.M21 && + __matrix1Arg.M22 == __matrix2Arg.M22 && + __matrix1Arg.M23 == __matrix2Arg.M23 && + __matrix1Arg.M24 == __matrix2Arg.M24 && + __matrix1Arg.M31 == __matrix2Arg.M31 && + __matrix1Arg.M32 == __matrix2Arg.M32 && + __matrix1Arg.M33 == __matrix2Arg.M33 && + __matrix1Arg.M34 == __matrix2Arg.M34 && + __matrix1Arg.OffsetX == __matrix2Arg.OffsetX && + __matrix1Arg.OffsetY == __matrix2Arg.OffsetY && + __matrix1Arg.OffsetZ == __matrix2Arg.OffsetZ && + __matrix1Arg.M44 == __matrix2Arg.M44; + } + + inline bool Matrix3D::operator!=(Matrix3D __matrix1Arg, Matrix3D __matrix2Arg) + { + return !(__matrix1Arg == __matrix2Arg); + } + } // Media3D + + namespace Animation + { + inline KeyTime::KeyTime(Windows::Foundation::TimeSpan __timeSpanArg) + { + if (__timeSpanArg.Duration < 0 ) + { + __abi_WinRTraiseInvalidArgumentException(); + } + TimeSpan = __timeSpanArg; + } + + inline bool KeyTime::operator==(KeyTime __keyTime1Arg, KeyTime __keyTime2Arg) + { + return __keyTime1Arg.TimeSpan.Duration == __keyTime2Arg.TimeSpan.Duration; + } + + inline bool KeyTime::operator!=(KeyTime __keyTime1Arg, KeyTime __keyTime2Arg) + { + return !(__keyTime1Arg == __keyTime2Arg); + } + + inline RepeatBehavior::RepeatBehavior(Windows::Foundation::TimeSpan __durationArg) + { + if (__durationArg.Duration < 0 ) + { + __abi_WinRTraiseInvalidArgumentException(); + } + + __duration = __durationArg; + __count = 0.0; + __type = RepeatBehaviorType::Duration; + } + + inline double RepeatBehavior::Count::get() + { + return __count; + } + + inline Windows::Foundation::TimeSpan RepeatBehavior::Duration::get() + { + return __duration; + } + + inline RepeatBehaviorType RepeatBehavior::Type::get() + { + return __type; + } + + inline RepeatBehavior RepeatBehavior::Forever::get() + { + RepeatBehavior forever; + Windows::Foundation::TimeSpan ts = {0}; + forever.__duration = ts; + forever.__count = 0.0; + forever.__type = RepeatBehaviorType::Forever; + + return forever; + } + + inline bool RepeatBehavior::HasCount::get() + { + return __type == RepeatBehaviorType::Count; + } + + inline bool RepeatBehavior::HasDuration::get() + { + return __type == RepeatBehaviorType::Duration; + } + + + inline bool RepeatBehavior::operator ==(RepeatBehavior __repeatBehavior1Arg, RepeatBehavior __repeatBehavior2Arg) + { + if (__repeatBehavior1Arg.__type == __repeatBehavior2Arg.__type) + { + switch (__repeatBehavior1Arg.__type) + { + case RepeatBehaviorType::Forever: + return true; + + case RepeatBehaviorType::Count: + return __repeatBehavior1Arg.__count == __repeatBehavior2Arg.__count; + + case RepeatBehaviorType::Duration: + + return __repeatBehavior1Arg.__duration.Duration == __repeatBehavior2Arg.__duration.Duration; + + default: + return false; + } + } + else + { + return false; + } + } + + inline bool RepeatBehavior::operator !=(RepeatBehavior __repeatBehavior1Arg, RepeatBehavior __repeatBehavior2Arg) + { + return !(__repeatBehavior1Arg == __repeatBehavior2Arg); + } + + } // Animation + } // Media + + inline Duration::Duration(Windows::Foundation::TimeSpan __timeSpanArg) + { + __durationType = DurationType::TimeSpan; + __timeSpan = __timeSpanArg; + } + + inline bool Duration::operator ==(Duration __t1Arg, Duration __t2Arg) + { + if (__t1Arg.HasTimeSpan) + { + if (__t2Arg.HasTimeSpan) + { + return __t1Arg.__timeSpan.Duration == __t2Arg.__timeSpan.Duration; + } + else + { + return false; + } + } + else + { + return __t1Arg.__durationType == __t2Arg.__durationType; + } + } + + inline bool Duration::operator !=(Duration __t1Arg, Duration __t2Arg) + { + return !(__t1Arg == __t2Arg); + } + + inline bool Duration::HasTimeSpan::get() + { + return (__durationType == DurationType::TimeSpan); + } + + inline Duration Duration::Automatic::get() + { + Duration __duration; + __duration.__durationType = DurationType::Automatic; + + return __duration; + } + + inline Duration Duration::Forever::get() + { + Duration __duration; + __duration.__durationType = DurationType::Forever; + + return __duration; + } + + inline Windows::Foundation::TimeSpan Duration::TimeSpan::get() + { + if (HasTimeSpan) + { + return __timeSpan; + } + else + { + Windows::Foundation::TimeSpan __timeSpanLoc; + __timeSpanLoc.Duration = 0; + return __timeSpanLoc; + } + } + + inline GridLength::GridLength(double __pixelsArg) + { + *this = GridLength(__pixelsArg, Windows::UI::Xaml::GridUnitType::Pixel); + } + + inline double GridLength::Value::get() + { + return (__unitType == Windows::UI::Xaml::GridUnitType::Auto ) ? GridLength(1.0, Windows::UI::Xaml::GridUnitType::Auto).__unitValue : __unitValue; + } + + inline Windows::UI::Xaml::GridUnitType GridLength::GridUnitType::get() + { + return (__unitType); + } + + inline bool GridLength::IsAbsolute::get() + { + return (__unitType == Windows::UI::Xaml::GridUnitType::Pixel); + } + + inline bool GridLength::IsAuto::get() + { + return (__unitType == Windows::UI::Xaml::GridUnitType::Auto); + } + + inline bool GridLength::IsStar::get() + { + return (__unitType == Windows::UI::Xaml::GridUnitType::Star) ; + } + + inline GridLength GridLength::Auto::get() + { + return ( GridLength(1.0, Windows::UI::Xaml::GridUnitType::Auto)); + } + + inline bool GridLength::operator ==(GridLength __gridLength1Arg, GridLength __gridLength2Arg) + { + if (__gridLength1Arg.GridUnitType == __gridLength2Arg.GridUnitType) + { + if (__gridLength1Arg.IsAuto || __gridLength1Arg.__unitValue == __gridLength2Arg.__unitValue) + { + return true; + } + } + return false; + } + + inline bool GridLength::operator !=(GridLength __gridLength1Arg, GridLength __gridLength2Arg) + { + return !(__gridLength1Arg == __gridLength2Arg); + } +} } } // Windows::UI::Xaml + +// Don't pull in any symbols if it's vccorlib compiled +#ifndef VCWINRT_DLL +#ifdef _WINRT_DLL +// DLL +#ifdef _M_IX86 +#pragma comment(linker, "/EXPORT:DllGetActivationFactory=_DllGetActivationFactory@8,PRIVATE") +#pragma comment(linker, "/EXPORT:DllCanUnloadNow=_DllCanUnloadNow@0,PRIVATE") +#else +#pragma comment(linker, "/EXPORT:DllGetActivationFactory=DllGetActivationFactory,PRIVATE") +#pragma comment(linker, "/EXPORT:DllCanUnloadNow,PRIVATE") +#endif +#endif //endif _WINRT_DLL +#endif //endif VCWINRT_DLL + +#if !defined(VCWINRT_DLL) + #if defined(_DEBUG) + #if defined(_GUARDED_CRT) + #pragma comment(lib, "vccorlibgd.lib") + #else + #pragma comment(lib, "vccorlibd.lib") + #endif + #else + #if defined(_GUARDED_CRT) + #pragma comment(lib, "vccorlibg.lib") + #else + #pragma comment(lib, "vccorlib.lib") + #endif + #endif // DEBUG +#endif // VCWINRT_DLL + +#pragma pack(pop) + +#pragma initialize_winrt_types_done + +// Restore warnings disabled for this file to their original settings +#pragma warning( pop ) + +#if defined(__VCCORLIB_H_ENABLE_ALL_WARNINGS) +#pragma warning(pop) +#endif + +#endif // _VCCORLIB_H_ diff --git a/test_data/lots_of_files/vsgcapture.h b/test_data/lots_of_files/vsgcapture.h new file mode 100644 index 0000000..9a7e6af --- /dev/null +++ b/test_data/lots_of_files/vsgcapture.h @@ -0,0 +1,135 @@ +#pragma once +#pragma comment(lib,"VsGraphicsHelper.lib") + +#include <Windows.h> +#include <stdio.h> +#include <sal.h> +#include <functional> + +#if defined(__ID3D11DeviceContext2_INTERFACE_DEFINED__) +#error Programmatic graphics capture does not support Direct3D 11.2 APIs. +#endif + +#ifndef VSG_DEFAULT_RUN_FILENAME +#define VSG_DEFAULT_RUN_FILENAME L"default.vsglog" +#endif + +extern "C" void __stdcall VsgDbgInit(_In_z_ wchar_t const * szVSGLog); +extern "C" void __stdcall VsgDbgInitDelayed(std::function<void (int len, wchar_t * pszBuffer)>); +extern "C" void __stdcall VsgDbgUnInit(); +extern "C" void __stdcall VsgDbgToggleHUD(); +extern "C" void __stdcall VsgDbgCaptureCurrentFrame(); +extern "C" void __stdcall VsgDbgBeginCapture(); +extern "C" void __stdcall VsgDbgEndCapture(); +extern "C" void __stdcall VsgDbgCopy(_In_z_ wchar_t const * szNewVSGLog); +extern "C" void __stdcall VsgDbgAddHUDMessage(_In_z_ wchar_t const * szMessage); + + +class VsgDbg +{ +public: + VsgDbg(bool bDefaultInit) + { + if(bDefaultInit) + { +#ifndef DONT_SAVE_VSGLOG_TO_TEMP + +#if WINAPI_FAMILY == 2 + Init([=] (int len, wchar_t * pszBuffer) { + + const wchar_t * tempPath = Platform::String::Concat(Platform::String::Concat(Windows::Storage::ApplicationData::Current->TemporaryFolder->Path, L"\\"), VSG_DEFAULT_RUN_FILENAME)->Data(); + wcscpy_s(pszBuffer, len, tempPath); + }); +#else + Init([=] (int len, wchar_t * pszBuffer) { + + wchar_t tempDir[MAX_PATH]; + wchar_t filePath[MAX_PATH]; + + if(GetTempPath(MAX_PATH, tempDir) == 0) + { + return; + } + + swprintf_s(filePath, MAX_PATH, L"%s%s", tempDir, VSG_DEFAULT_RUN_FILENAME); + + wcscpy_s(pszBuffer, len, filePath); + }); +#endif + +#else + Init([=] (int len, wchar_t * pszBuffer) { + wcscpy_s(pszBuffer, len, VSG_DEFAULT_RUN_FILENAME); + }); +#endif + } + } + + ~VsgDbg() + { + UnInit(); + } + + void Init(std::function<void (int len, wchar_t * pszBuffer)> vsgLogGetter) + { + VsgDbgInitDelayed(vsgLogGetter); + } + + + void UnInit() + { + VsgDbgUnInit(); + } + + void ToggleHUD() + { + VsgDbgToggleHUD(); + } + + void BeginCapture () + { + VsgDbgBeginCapture (); + } + + void EndCapture () + { + VsgDbgEndCapture (); + } + + void CaptureCurrentFrame () + { + VsgDbgCaptureCurrentFrame (); + } + + void Copy(_In_z_ wchar_t const * szNewVSGLog) + { + VsgDbgCopy(szNewVSGLog); + } + + void AddMessage(_In_z_ wchar_t const * szMessage) + { + VsgDbgAddHUDMessage(szMessage); + } +}; + +#ifndef VSG_NODEFAULT_INSTANCE + _declspec(selectany) VsgDbg *g_pVsgDbg; + + inline void UnInitVsPix() + { + if(g_pVsgDbg != NULL) + { + delete g_pVsgDbg; + } + } + + inline void InitVsPix() + { + g_pVsgDbg = new VsgDbg(true); atexit(&UnInitVsPix); + } + + + #pragma section(".CRT$XCT",long,read) + __declspec(allocate(".CRT$XCT")) _declspec(selectany) _declspec(dllexport) void (*pInitFunc)() = InitVsPix; + +#endif \ No newline at end of file diff --git a/test_data/lots_of_files/wchar.h b/test_data/lots_of_files/wchar.h new file mode 100644 index 0000000..17bf44a --- /dev/null +++ b/test_data/lots_of_files/wchar.h @@ -0,0 +1,1319 @@ +/*** +*wchar.h - declarations for wide character functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This file contains the types, macros and function declarations for +* all wide character-related functions. They may also be declared in +* individual header files on a functional basis. +* [ISO] +* +* Note: keep in sync with ctype.h, stdio.h, stdlib.h, string.h, time.h. +* +* [Public] +* +****/ + +#pragma once + +#ifndef _INC_WCHAR +#define _INC_WCHAR + +#include <crtdefs.h> + +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * According to the standard, WCHAR_MIN and WCHAR_MAX need to be + * "constant expressions suitable for use in #if preprocessing directives, + * and this expression shall have the same type as would an expression that + * is an object of the corresponding type converted according to the integer + * promotions". + */ +#define WCHAR_MIN 0x0000 +#define WCHAR_MAX 0xffff + +#ifndef _VA_LIST_DEFINED +#ifdef _M_CEE_PURE +typedef System::ArgIterator va_list; +#else /* _M_CEE_PURE */ +typedef char * va_list; +#endif /* _M_CEE_PURE */ +#define _VA_LIST_DEFINED +#endif /* _VA_LIST_DEFINED */ + +#ifndef WEOF +#define WEOF (wint_t)(0xFFFF) +#endif /* WEOF */ + +#ifndef _FILE_DEFINED +struct _iobuf { + char *_ptr; + int _cnt; + char *_base; + int _flag; + int _file; + int _charbuf; + int _bufsiz; + char *_tmpfname; + }; +typedef struct _iobuf FILE; +#define _FILE_DEFINED +#endif /* _FILE_DEFINED */ + +/* Declare _iob[] array */ + +#ifndef _STDIO_DEFINED +#ifdef _CRTBLD +/* These functions are for enabling STATIC_CPPLIB functionality */ +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP extern FILE * __cdecl __p__iob(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#ifndef _M_CEE_PURE +_CRTIMP extern FILE _iob[]; +#endif /* _M_CEE_PURE */ +#endif /* _CRTBLD */ +_CRTIMP FILE * __cdecl __iob_func(void); +#endif /* _STDIO_DEFINED */ + +#ifndef _STDSTREAM_DEFINED +#define stdin (&__iob_func()[0]) +#define stdout (&__iob_func()[1]) +#define stderr (&__iob_func()[2]) +#define _STDSTREAM_DEFINED +#endif /* _STDSTREAM_DEFINED */ + +#ifndef _FSIZE_T_DEFINED +typedef unsigned long _fsize_t; /* Could be 64 bits for Win32 */ +#define _FSIZE_T_DEFINED +#endif /* _FSIZE_T_DEFINED */ + +#ifndef _WFINDDATA_T_DEFINED + +struct _wfinddata32_t { + unsigned attrib; + __time32_t time_create; /* -1 for FAT file systems */ + __time32_t time_access; /* -1 for FAT file systems */ + __time32_t time_write; + _fsize_t size; + wchar_t name[260]; +}; + +struct _wfinddata32i64_t { + unsigned attrib; + __time32_t time_create; /* -1 for FAT file systems */ + __time32_t time_access; /* -1 for FAT file systems */ + __time32_t time_write; + __int64 size; + wchar_t name[260]; +}; + +struct _wfinddata64i32_t { + unsigned attrib; + __time64_t time_create; /* -1 for FAT file systems */ + __time64_t time_access; /* -1 for FAT file systems */ + __time64_t time_write; + _fsize_t size; + wchar_t name[260]; +}; + +struct _wfinddata64_t { + unsigned attrib; + __time64_t time_create; /* -1 for FAT file systems */ + __time64_t time_access; /* -1 for FAT file systems */ + __time64_t time_write; + __int64 size; + wchar_t name[260]; +}; + +#ifdef _USE_32BIT_TIME_T +#define _wfinddata_t _wfinddata32_t +#define _wfinddatai64_t _wfinddata32i64_t + +#define _wfindfirst _wfindfirst32 +#define _wfindnext _wfindnext32 +#define _wfindfirsti64 _wfindfirst32i64 +#define _wfindnexti64 _wfindnext32i64 + +#else /* _USE_32BIT_TIME_T */ +#define _wfinddata_t _wfinddata64i32_t +#define _wfinddatai64_t _wfinddata64_t + +#define _wfindfirst _wfindfirst64i32 +#define _wfindnext _wfindnext64i32 +#define _wfindfirsti64 _wfindfirst64 +#define _wfindnexti64 _wfindnext64 + +#endif /* _USE_32BIT_TIME_T */ + +#define _WFINDDATA_T_DEFINED +#endif /* _WFINDDATA_T_DEFINED */ + + +/* define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else /* __cplusplus */ +#define NULL ((void *)0) +#endif /* __cplusplus */ +#endif /* NULL */ + +#ifndef _CONST_RETURN +#ifdef __cplusplus +#define _CONST_RETURN const +#define _CRT_CONST_CORRECT_OVERLOADS +#else /* __cplusplus */ +#define _CONST_RETURN +#endif /* __cplusplus */ +#endif /* _CONST_RETURN */ + +/* For backwards compatibility */ +#define _WConst_return _CONST_RETURN + +#ifndef _CRT_CTYPEDATA_DEFINED +#define _CRT_CTYPEDATA_DEFINED +#ifndef _CTYPE_DISABLE_MACROS +#ifdef _CRTBLD +extern const unsigned short __newctype[]; +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP const unsigned short ** __cdecl __p__pctype(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#endif /* _CRTBLD */ + +#ifndef __PCTYPE_FUNC +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define __PCTYPE_FUNC _pctype +#else /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ +#define __PCTYPE_FUNC __pctype_func() +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ +#endif /* __PCTYPE_FUNC */ + +_CRTIMP const unsigned short * __cdecl __pctype_func(void); +#if !defined (_M_CEE_PURE) +_CRTIMP extern const unsigned short *_pctype; +#else /* !defined (_M_CEE_PURE) */ +#define _pctype (__pctype_func()) +#endif /* !defined (_M_CEE_PURE) */ +#endif /* _CTYPE_DISABLE_MACROS */ +#endif /* _CRT_CTYPEDATA_DEFINED */ + +#ifndef _CRT_WCTYPEDATA_DEFINED +#define _CRT_WCTYPEDATA_DEFINED +#ifndef _CTYPE_DISABLE_MACROS +#if !defined (_M_CEE_PURE) +_CRTIMP extern const unsigned short _wctype[]; +#endif /* !defined (_M_CEE_PURE) */ +#ifdef _CRTBLD +extern const unsigned short __newctype[]; +#if defined (_DLL) && defined (_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP const wctype_t ** __cdecl __p__pwctype(void); +#endif /* defined (_DLL) && defined (_M_IX86) */ +#endif /* _CRTBLD */ + +_CRTIMP const wctype_t * __cdecl __pwctype_func(void); +#if !defined (_M_CEE_PURE) +_CRTIMP extern const wctype_t *_pwctype; +#else /* !defined (_M_CEE_PURE) */ +#define _pwctype (__pwctype_func()) +#endif /* !defined (_M_CEE_PURE) */ +#endif /* _CTYPE_DISABLE_MACROS */ +#endif /* _CRT_WCTYPEDATA_DEFINED */ + +/* set bit masks for the possible character types */ + +#define _UPPER 0x1 /* upper case letter */ +#define _LOWER 0x2 /* lower case letter */ +#define _DIGIT 0x4 /* digit[0-9] */ +#define _SPACE 0x8 /* space, tab, carriage return, newline, */ + /* vertical tab or form feed */ +#define _PUNCT 0x10 /* punctuation character */ +#define _CONTROL 0x20 /* control character */ +#define _BLANK 0x40 /* space char (tab handled separately) */ +#define _HEX 0x80 /* hexadecimal digit */ + +#define _LEADBYTE 0x8000 /* multibyte leadbyte */ +#define _ALPHA (0x0100|_UPPER|_LOWER) /* alphabetic character */ + + +/* Function prototypes */ + +#ifndef _WCTYPE_DEFINED + +/* Character classification function prototypes */ +/* also declared in ctype.h */ + +_Check_return_ _CRTIMP int __cdecl iswalpha(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswalpha_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswupper(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswupper_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswlower(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswlower_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswdigit(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswdigit_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswxdigit(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswxdigit_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswspace(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswspace_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswpunct(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswpunct_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswblank(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswblank_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswalnum(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswalnum_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswprint(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswprint_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswgraph(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswgraph_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswcntrl(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswcntrl_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswascii(_In_ wint_t _C); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP int __cdecl isleadbyte(_In_ int _C); +_Check_return_ _CRTIMP int __cdecl _isleadbyte_l(_In_ int _C, _In_opt_ _locale_t _Locale); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP wint_t __cdecl towupper(_In_ wint_t _C); +_Check_return_ _CRTIMP wint_t __cdecl _towupper_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP wint_t __cdecl towlower(_In_ wint_t _C); +_Check_return_ _CRTIMP wint_t __cdecl _towlower_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl iswctype(_In_ wint_t _C, _In_ wctype_t _Type); +_Check_return_ _CRTIMP int __cdecl _iswctype_l(_In_ wint_t _C, _In_ wctype_t _Type, _In_opt_ _locale_t _Locale); + +_Check_return_ _CRTIMP int __cdecl __iswcsymf(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswcsymf_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl __iswcsym(_In_ wint_t _C); +_Check_return_ _CRTIMP int __cdecl _iswcsym_l(_In_ wint_t _C, _In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRT_OBSOLETE(iswctype) _CRTIMP int __cdecl is_wctype(_In_ wint_t _C, _In_ wctype_t _Type); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + +#define _WCTYPE_DEFINED +#endif /* _WCTYPE_DEFINED */ + +#ifndef _WDIRECT_DEFINED + +/* also declared in direct.h */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wgetcwd") +#pragma push_macro("_wgetdcwd") +#undef _wgetcwd +#undef _wgetdcwd +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _Ret_maybenull_z_ _CRTIMP wchar_t * __cdecl _wgetcwd(_Out_writes_opt_(_SizeInWords) wchar_t * _DstBuf, _In_ int _SizeInWords); +_Check_return_ _Ret_maybenull_z_ _CRTIMP wchar_t * __cdecl _wgetdcwd(_In_ int _Drive, _Out_writes_opt_(_SizeInWords) wchar_t * _DstBuf, _In_ int _SizeInWords); +#define _wgetdcwd_nolock _wgetdcwd + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wgetcwd") +#pragma pop_macro("_wgetdcwd") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP int __cdecl _wchdir(_In_z_ const wchar_t * _Path); + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP int __cdecl _wmkdir(_In_z_ const wchar_t * _Path); +_Check_return_ _CRTIMP int __cdecl _wrmdir(_In_z_ const wchar_t * _Path); + +#define _WDIRECT_DEFINED +#endif /* _WDIRECT_DEFINED */ + +#ifndef _WIO_DEFINED + +_Check_return_ _CRTIMP int __cdecl _waccess(_In_z_ const wchar_t * _Filename, _In_ int _AccessMode); +_Check_return_wat_ _CRTIMP errno_t __cdecl _waccess_s(_In_z_ const wchar_t * _Filename, _In_ int _AccessMode); +_Check_return_ _CRTIMP int __cdecl _wchmod(_In_z_ const wchar_t * _Filename, _In_ int _Mode); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wcreat(_In_z_ const wchar_t * _Filename, _In_ int _PermissionMode); +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst32(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata32_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext32(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata32_t * _FindData); +_CRTIMP int __cdecl _wunlink(_In_z_ const wchar_t * _Filename); +_Check_return_ _CRTIMP int __cdecl _wrename(_In_z_ const wchar_t * _OldFilename, _In_z_ const wchar_t * _NewFilename); +_CRTIMP errno_t __cdecl _wmktemp_s(_Inout_updates_z_(_SizeInWords) wchar_t * _TemplateName, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wmktemp_s, wchar_t, _TemplateName) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wmktemp, _Inout_z_, wchar_t, _TemplateName) + +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst32i64(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata32i64_t * _FindData); +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst64i32(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata64i32_t * _FindData); +_Check_return_ _CRTIMP intptr_t __cdecl _wfindfirst64(_In_z_ const wchar_t * _Filename, _Out_ struct _wfinddata64_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext32i64(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata32i64_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext64i32(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata64i32_t * _FindData); +_Check_return_ _CRTIMP int __cdecl _wfindnext64(_In_ intptr_t _FindHandle, _Out_ struct _wfinddata64_t * _FindData); + +_Check_return_wat_ _CRTIMP errno_t __cdecl _wsopen_s(_Out_ int * _FileHandle, _In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, _In_ int _ShareFlag, _In_ int _PermissionFlag); + +#if !defined (__cplusplus) || !defined (_M_IX86) + +_Check_return_ _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wsopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, int _ShareFlag, ...); + +#else /* !defined (__cplusplus) || !defined (_M_IX86) */ + +/* these function do not validate pmode; use _sopen_s */ +extern "C++" _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, _In_ int _PermissionMode = 0); +extern "C++" _CRT_INSECURE_DEPRECATE(_wsopen_s) _CRTIMP int __cdecl _wsopen(_In_z_ const wchar_t * _Filename, _In_ int _OpenFlag, _In_ int _ShareFlag, int _PermissionMode = 0); + +#endif /* !defined (__cplusplus) || !defined (_M_IX86) */ + +#define _WIO_DEFINED +#endif /* _WIO_DEFINED */ + +#ifndef _WLOCALE_DEFINED + +/* wide function prototypes, also declared in wchar.h */ + +_Check_return_opt_ _CRTIMP wchar_t * __cdecl _wsetlocale(_In_ int _Category, _In_opt_z_ const wchar_t * _Locale); +_Check_return_opt_ _CRTIMP _locale_t __cdecl _wcreate_locale(_In_ int _Category, _In_z_ const wchar_t * _Locale); + +#define _WLOCALE_DEFINED +#endif /* _WLOCALE_DEFINED */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +#ifndef _WPROCESS_DEFINED + +/* also declared in process.h */ + +_CRTIMP intptr_t __cdecl _wexecl(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexecle(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexeclp(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexeclpe(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wexecv(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wexecve(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +_CRTIMP intptr_t __cdecl _wexecvp(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wexecvpe(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +_CRTIMP intptr_t __cdecl _wspawnl(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnle(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnlp(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnlpe(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _ArgList, ...); +_CRTIMP intptr_t __cdecl _wspawnv(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wspawnve(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +_CRTIMP intptr_t __cdecl _wspawnvp(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList); +_CRTIMP intptr_t __cdecl _wspawnvpe(_In_ int _Mode, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * const * _ArgList, + _In_opt_z_ const wchar_t * const * _Env); +#ifndef _CRT_WSYSTEM_DEFINED +#define _CRT_WSYSTEM_DEFINED +_CRTIMP int __cdecl _wsystem(_In_opt_z_ const wchar_t * _Command); +#endif /* _CRT_WSYSTEM_DEFINED */ + +#define _WPROCESS_DEFINED +#endif /* _WPROCESS_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#ifndef _WCTYPE_INLINE_DEFINED + +#ifdef _CRTBLD +#define _CRT_WCTYPE_NOINLINE +#else /* _CRTBLD */ +#undef _CRT_WCTYPE_NOINLINE +#endif /* _CRTBLD */ + +#if !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (MRTDLL) || defined (_CRT_WCTYPE_NOINLINE) +#define iswalpha(_c) ( iswctype(_c,_ALPHA) ) +#define iswupper(_c) ( iswctype(_c,_UPPER) ) +#define iswlower(_c) ( iswctype(_c,_LOWER) ) +#define iswdigit(_c) ( iswctype(_c,_DIGIT) ) +#define iswxdigit(_c) ( iswctype(_c,_HEX) ) +#define iswspace(_c) ( iswctype(_c,_SPACE) ) +#define iswpunct(_c) ( iswctype(_c,_PUNCT) ) +#define iswblank(_c) (((_c) == '\t') ? _BLANK : iswctype(_c,_BLANK) ) +#define iswalnum(_c) ( iswctype(_c,_ALPHA|_DIGIT) ) +#define iswprint(_c) ( iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT) ) +#define iswgraph(_c) ( iswctype(_c,_PUNCT|_ALPHA|_DIGIT) ) +#define iswcntrl(_c) ( iswctype(_c,_CONTROL) ) +#define iswascii(_c) ( (unsigned)(_c) < 0x80 ) + +#define _iswalpha_l(_c,_p) ( iswctype(_c,_ALPHA) ) +#define _iswupper_l(_c,_p) ( iswctype(_c,_UPPER) ) +#define _iswlower_l(_c,_p) ( iswctype(_c,_LOWER) ) +#define _iswdigit_l(_c,_p) ( iswctype(_c,_DIGIT) ) +#define _iswxdigit_l(_c,_p) ( iswctype(_c,_HEX) ) +#define _iswspace_l(_c,_p) ( iswctype(_c,_SPACE) ) +#define _iswpunct_l(_c,_p) ( iswctype(_c,_PUNCT) ) +#define _iswblank_l(_c,_p) (((_c) == '\t') ? _BLANK : iswctype(_c,_BLANK) ) +#define _iswalnum_l(_c,_p) ( iswctype(_c,_ALPHA|_DIGIT) ) +#define _iswprint_l(_c,_p) ( iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT) ) +#define _iswgraph_l(_c,_p) ( iswctype(_c,_PUNCT|_ALPHA|_DIGIT) ) +#define _iswcntrl_l(_c,_p) ( iswctype(_c,_CONTROL) ) + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +#ifndef _CTYPE_DISABLE_MACROS +#define isleadbyte(_c) ( __PCTYPE_FUNC[(unsigned char)(_c)] & _LEADBYTE) +#endif /* _CTYPE_DISABLE_MACROS */ +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#endif /* !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (MRTDLL) || defined (_CRT_WCTYPE_NOINLINE) */ +#define _WCTYPE_INLINE_DEFINED +#endif /* _WCTYPE_INLINE_DEFINED */ + + + +/* define structure for returning status information */ + +#ifndef _INO_T_DEFINED +typedef unsigned short _ino_t; /* i-node number (not used on DOS) */ +#if !__STDC__ +/* Non-ANSI name for compatibility */ +typedef unsigned short ino_t; +#endif /* !__STDC__ */ +#define _INO_T_DEFINED +#endif /* _INO_T_DEFINED */ + +#ifndef _DEV_T_DEFINED +typedef unsigned int _dev_t; /* device code */ +#if !__STDC__ +/* Non-ANSI name for compatibility */ +typedef unsigned int dev_t; +#endif /* !__STDC__ */ +#define _DEV_T_DEFINED +#endif /* _DEV_T_DEFINED */ + +#ifndef _OFF_T_DEFINED +typedef long _off_t; /* file offset value */ +#if !__STDC__ +/* Non-ANSI name for compatibility */ +typedef long off_t; +#endif /* !__STDC__ */ +#define _OFF_T_DEFINED +#endif /* _OFF_T_DEFINED */ + +#ifndef _STAT_DEFINED + +struct _stat32 { + _dev_t st_dev; + _ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + _off_t st_size; + __time32_t st_atime; + __time32_t st_mtime; + __time32_t st_ctime; + }; + +#if !__STDC__ +/* Non-ANSI names for compatibility */ +struct stat { + _dev_t st_dev; + _ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + _off_t st_size; + time_t st_atime; + time_t st_mtime; + time_t st_ctime; + }; + +#endif /* !__STDC__ */ + +struct _stat32i64 { + _dev_t st_dev; + _ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + __int64 st_size; + __time32_t st_atime; + __time32_t st_mtime; + __time32_t st_ctime; + }; + +struct _stat64i32 { + _dev_t st_dev; + _ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + _off_t st_size; + __time64_t st_atime; + __time64_t st_mtime; + __time64_t st_ctime; + }; + +struct _stat64 { + _dev_t st_dev; + _ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + __int64 st_size; + __time64_t st_atime; + __time64_t st_mtime; + __time64_t st_ctime; + }; + +/* + * We have to have same name for structure and the fuction so as to do the + * macro magic.we need the structure name and function name the same. + */ +#define __stat64 _stat64 + +#ifdef _USE_32BIT_TIME_T +#define _fstat _fstat32 +#define _fstati64 _fstat32i64 +#define _stat _stat32 +#define _stati64 _stat32i64 +#define _wstat _wstat32 +#define _wstati64 _wstat32i64 + +#else /* _USE_32BIT_TIME_T */ +#define _fstat _fstat64i32 +#define _fstati64 _fstat64 +#define _stat _stat64i32 +#define _stati64 _stat64 +#define _wstat _wstat64i32 +#define _wstati64 _wstat64 + +#endif /* _USE_32BIT_TIME_T */ + + +#define _STAT_DEFINED +#endif /* _STAT_DEFINED */ + +#ifndef _WSTAT_DEFINED + +/* also declared in wchar.h */ + +_CRTIMP int __cdecl _wstat32(_In_z_ const wchar_t * _Name, _Out_ struct _stat32 * _Stat); + +_CRTIMP int __cdecl _wstat32i64(_In_z_ const wchar_t * _Name, _Out_ struct _stat32i64 * _Stat); +_CRTIMP int __cdecl _wstat64i32(_In_z_ const wchar_t * _Name, _Out_ struct _stat64i32 * _Stat); +_CRTIMP int __cdecl _wstat64(_In_z_ const wchar_t * _Name, _Out_ struct _stat64 * _Stat); + +#define _WSTAT_DEFINED +#endif /* _WSTAT_DEFINED */ + + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +#ifndef _WCONIO_DEFINED + +/* wide function prototypes, also declared in conio.h */ + +#ifndef WEOF +#define WEOF (wint_t)(0xFFFF) +#endif /* WEOF */ + +_Check_return_wat_ _CRTIMP errno_t __cdecl _cgetws_s(_Out_writes_to_(_SizeInWords, *_SizeRead) wchar_t * _Buffer, size_t _SizeInWords, _Out_ size_t * _SizeRead); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _cgetws_s, _Post_readable_size_(*_Buffer) wchar_t, _Buffer, size_t *, _SizeRead) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0_CGETS(wchar_t *, _CRTIMP, _cgetws, _Inout_z_, wchar_t, _Buffer) +_Check_return_ _CRTIMP wint_t __cdecl _getwch(void); +_Check_return_ _CRTIMP wint_t __cdecl _getwche(void); +_Check_return_ _CRTIMP wint_t __cdecl _putwch(wchar_t _WCh); +_Check_return_ _CRTIMP wint_t __cdecl _ungetwch(wint_t _WCh); +_Check_return_opt_ _CRTIMP int __cdecl _cputws(_In_z_ const wchar_t * _String); +_Check_return_opt_ _CRTIMP int __cdecl _cwprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _cwprintf_s(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_cwscanf_s) _CRTIMP int __cdecl _cwscanf(_In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_cwscanf_s_l) _CRTIMP int __cdecl _cwscanf_l(_In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _cwscanf_s(_In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _cwscanf_s_l(_In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vcwprintf(_In_z_ _Printf_format_string_ const wchar_t *_Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vcwprintf_s(_In_z_ _Printf_format_string_ const wchar_t *_Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _cwprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vcwprintf_p(_In_z_ _Printf_format_string_ const wchar_t* _Format, va_list _ArgList); + +_CRTIMP int __cdecl _cwprintf_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_CRTIMP int __cdecl _cwprintf_s_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_CRTIMP int __cdecl _vcwprintf_l(_In_z_ _Printf_format_string_params_(2) const wchar_t *_Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_CRTIMP int __cdecl _vcwprintf_s_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_CRTIMP int __cdecl _cwprintf_p_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_CRTIMP int __cdecl _vcwprintf_p_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ wint_t __cdecl _putwch_nolock(wchar_t _WCh); +_Check_return_ wint_t __cdecl _getwch_nolock(void); +_Check_return_ wint_t __cdecl _getwche_nolock(void); +_Check_return_opt_ wint_t __cdecl _ungetwch_nolock(wint_t _WCh); + +#define _WCONIO_DEFINED +#endif /* _WCONIO_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + + +#ifndef _WSTDIO_DEFINED + +/* wide function prototypes, also declared in stdio.h */ + +#ifndef WEOF +#define WEOF (wint_t)(0xFFFF) +#endif /* WEOF */ + +_Check_return_ _CRTIMP FILE * __cdecl _wfsopen(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode, _In_ int _ShFlag); + +_Check_return_opt_ _CRTIMP wint_t __cdecl fgetwc(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _fgetwchar(void); +_Check_return_opt_ _CRTIMP wint_t __cdecl fputwc(_In_ wchar_t _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _fputwchar(_In_ wchar_t _Ch); +_Check_return_ _CRTIMP wint_t __cdecl getwc(_Inout_ FILE * _File); +_Check_return_ _CRTIMP wint_t __cdecl getwchar(void); +_Check_return_opt_ _CRTIMP wint_t __cdecl putwc(_In_ wchar_t _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl putwchar(_In_ wchar_t _Ch); +_Check_return_opt_ _CRTIMP wint_t __cdecl ungetwc(_In_ wint_t _Ch, _Inout_ FILE * _File); + +_Check_return_opt_ _CRTIMP wchar_t * __cdecl fgetws(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ int _SizeInWords, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP int __cdecl fputws(_In_z_ const wchar_t * _Str, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wchar_t * __cdecl _getws_s(_Out_writes_z_(_SizeInWords) wchar_t * _Str, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(wchar_t *, _getws_s, _Post_z_ wchar_t, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_SAME, _CRTIMP, _getws, _Pre_notnull_ _Post_z_, wchar_t, _String) +_Check_return_opt_ _CRTIMP int __cdecl _putws(_In_z_ const wchar_t * _Str); + +_Check_return_opt_ _CRTIMP int __cdecl fwprintf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl fwprintf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl wprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl wprintf_s(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_ _CRTIMP int __cdecl _scwprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl vfwprintf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vfwscanf(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl vfwprintf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vfwscanf_s(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl vwprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vwscanf(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl vwprintf_s(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl vwscanf_s(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ + +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP_ALTERNATIVE int __cdecl swprintf_s(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1_ARGLIST(int, swprintf_s, vswprintf_s, _Post_z_ wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format) +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP_ALTERNATIVE int __cdecl vswprintf_s(_Out_writes_z_(_SizeInWords) wchar_t * _Dst, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl vswscanf_s(const wchar_t * _Dst, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(int, vswprintf_s, _Post_z_ wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format, va_list, _Args) +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(int, vswscanf_s, _Post_z_ wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format, va_list, _Args) +_Check_return_opt_ _CRTIMP int __cdecl vswscanf(const wchar_t * _srcBuf, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_c(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_c(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwprintf_s(_Out_writes_z_(_DstSizeInWords) wchar_t * _DstBuf, _In_ size_t _DstSizeInWords, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2_ARGLIST(int, _snwprintf_s, _vsnwprintf_s, _Post_z_ wchar_t, _Dest, _In_ size_t, _Count, _In_z_ _Printf_format_string_ const wchar_t *, _Format) +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vsnwprintf_s(_Out_writes_z_(_DstSizeInWords) wchar_t * _DstBuf, _In_ size_t _DstSizeInWords, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_3(int, _vsnwprintf_s, _Post_z_ wchar_t, _Dest, _In_ size_t, _Count, _In_z_ _Printf_format_string_ const wchar_t *, _Format, va_list, _Args) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_ARGLIST_EX(int, __RETURN_POLICY_SAME, _CRTIMP, _snwprintf, _vsnwprintf, _Pre_notnull_ _Post_maybez_ wchar_t, _Out_writes_(_Count) _Post_maybez_, wchar_t, _Dest, _In_ size_t, _Count, _In_z_ _Printf_format_string_ const wchar_t *, _Format) + +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_p(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_p(_Inout_ FILE * _File, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_p(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_p(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_ _CRTIMP int __cdecl _scwprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, ...); +_Check_return_ _CRTIMP int __cdecl _vscwprintf_p(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_p_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _wprintf_s_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_p_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vwprintf_s_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_p_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _fwprintf_s_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_p_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vfwprintf_s_l(_Inout_ FILE * _File, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_c_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _swprintf_p_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _swprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_c_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP int __cdecl _vswprintf_p_l(_Out_writes_z_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vswprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_ _CRTIMP int __cdecl _scwprintf_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRTIMP int __cdecl _scwprintf_p_l(_In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRTIMP int __cdecl _vscwprintf_p_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snwprintf_s_l) _CRTIMP int __cdecl _snwprintf_l(_Out_writes_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_vsnwprintf_s_l) _CRTIMP int __cdecl _vsnwprintf_l(_Out_writes_(_MaxCount) wchar_t * _DstBuf, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _vsnwprintf_s_l(_Out_writes_z_(_DstSize) wchar_t * _DstBuf, _In_ size_t _DstSize, _In_ size_t _MaxCount, _In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); + +#ifndef _CRT_NON_CONFORMING_SWPRINTFS + +#define _SWPRINTFS_DEPRECATED _CRT_DEPRECATE_TEXT("swprintf has been changed to conform with the ISO C standard, adding an extra character count parameter. To use traditional Microsoft swprintf, set _CRT_NON_CONFORMING_SWPRINTFS.") + +#else /* _CRT_NON_CONFORMING_SWPRINTFS */ + +#define _SWPRINTFS_DEPRECATED + +#endif /* _CRT_NON_CONFORMING_SWPRINTFS */ + +/* we could end up with a double deprecation, disable warnings 4141 and 4996 */ +#pragma warning(push) +#pragma warning(disable:4141 4996) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_ARGLIST_EX(int, __RETURN_POLICY_SAME, _SWPRINTFS_DEPRECATED _CRTIMP, _swprintf, _swprintf_s, _vswprintf, vswprintf_s, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_z_ _Printf_format_string_ const wchar_t *, _Format) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_2_ARGLIST_EX(int, __RETURN_POLICY_SAME, _SWPRINTFS_DEPRECATED _CRTIMP, __swprintf_l, __vswprintf_l, _vswprintf_s_l, _Pre_notnull_ _Post_z_ wchar_t, _Out_, wchar_t, _Dest, _In_z_ _Printf_format_string_params_(2) const wchar_t *, _Format, _locale_t, _Plocinfo) +#pragma warning(pop) + +#if !defined (RC_INVOKED) && !defined (__midl) +#include <swprintf.inl> +#endif /* !defined (RC_INVOKED) && !defined (__midl) */ + +#ifdef _CRT_NON_CONFORMING_SWPRINTFS +#ifndef __cplusplus +#define swprintf _swprintf +#define vswprintf _vswprintf +#define _swprintf_l __swprintf_l +#define _vswprintf_l __vswprintf_l +#endif /* __cplusplus */ +#endif /* _CRT_NON_CONFORMING_SWPRINTFS */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wtempnam") +#undef _wtempnam +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP wchar_t * __cdecl _wtempnam(_In_opt_z_ const wchar_t * _Directory, _In_opt_z_ const wchar_t * _FilePrefix); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wtempnam") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP int __cdecl _vscwprintf(_In_z_ _Printf_format_string_ const wchar_t * _Format, va_list _ArgList); +_Check_return_ _CRTIMP int __cdecl _vscwprintf_l(_In_z_ _Printf_format_string_params_(2) const wchar_t * _Format, _In_opt_ _locale_t _Locale, va_list _ArgList); +_Check_return_ _CRT_INSECURE_DEPRECATE(fwscanf_s) _CRTIMP int __cdecl fwscanf(_Inout_ FILE * _File, _In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_fwscanf_s_l) _CRTIMP int __cdecl _fwscanf_l(_Inout_ FILE * _File, _In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP int __cdecl fwscanf_s(_Inout_ FILE * _File, _In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP int __cdecl _fwscanf_s_l(_Inout_ FILE * _File, _In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(swscanf_s) _CRTIMP int __cdecl swscanf(_In_z_ const wchar_t * _Src, _In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_swscanf_s_l) _CRTIMP int __cdecl _swscanf_l(_In_z_ const wchar_t * _Src, _In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl swscanf_s(_In_z_ const wchar_t *_Src, _In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _swscanf_s_l(_In_z_ const wchar_t * _Src, _In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snwscanf_s) _CRTIMP int __cdecl _snwscanf(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_snwscanf_s_l) _CRTIMP int __cdecl _snwscanf_l(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwscanf_s(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _snwscanf_s_l(_In_reads_(_MaxCount) _Pre_z_ const wchar_t * _Src, _In_ size_t _MaxCount, _In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +_Check_return_ _CRT_INSECURE_DEPRECATE(wscanf_s) _CRTIMP int __cdecl wscanf(_In_z_ _Scanf_format_string_ const wchar_t * _Format, ...); +_Check_return_opt_ _CRT_INSECURE_DEPRECATE(_wscanf_s_l) _CRTIMP int __cdecl _wscanf_l(_In_z_ _Scanf_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl wscanf_s(_In_z_ _Scanf_s_format_string_ const wchar_t * _Format, ...); +#endif /* __STDC_WANT_SECURE_LIB__ */ +_Check_return_opt_ _CRTIMP_ALTERNATIVE int __cdecl _wscanf_s_l(_In_z_ _Scanf_s_format_string_params_(0) const wchar_t * _Format, _In_opt_ _locale_t _Locale, ...); + +_Check_return_ _CRTIMP FILE * __cdecl _wfdopen(_In_ int _FileHandle , _In_z_ const wchar_t * _Mode); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wfopen_s) _CRTIMP FILE * __cdecl _wfopen(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wfopen_s(_Outptr_result_maybenull_ FILE ** _File, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wfreopen_s) _CRTIMP FILE * __cdecl _wfreopen(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode, _Inout_ FILE * _OldFile); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wfreopen_s(_Outptr_result_maybenull_ FILE ** _File, _In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _Mode, _Inout_ FILE * _OldFile); + +#ifndef _CRT_WPERROR_DEFINED +#define _CRT_WPERROR_DEFINED +_CRTIMP void __cdecl _wperror(_In_opt_z_ const wchar_t * _ErrMsg); +#endif /* _CRT_WPERROR_DEFINED */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP FILE * __cdecl _wpopen(_In_z_ const wchar_t *_Command, _In_z_ const wchar_t * _Mode); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRTIMP int __cdecl _wremove(_In_z_ const wchar_t * _Filename); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wtmpnam_s(_Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wtmpnam_s, _Post_z_ wchar_t, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wtmpnam, _Pre_maybenull_ _Post_z_, wchar_t, _Buffer) + +_Check_return_opt_ _CRTIMP wint_t __cdecl _fgetwc_nolock(_Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _fputwc_nolock(_In_ wchar_t _Ch, _Inout_ FILE * _File); +_Check_return_opt_ _CRTIMP wint_t __cdecl _ungetwc_nolock(_In_ wint_t _Ch, _Inout_ FILE * _File); + +#ifdef _CRTBLD +#define _CRT_GETPUTWCHAR_NOINLINE +#else /* _CRTBLD */ +#undef _CRT_GETPUTWCHAR_NOINLINE +#endif /* _CRTBLD */ + +#if !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (_CRT_GETPUTWCHAR_NOINLINE) +#define getwchar() fgetwc(stdin) +#define putwchar(_c) fputwc((_c),stdout) +#else /* !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (_CRT_GETPUTWCHAR_NOINLINE) */ +_Check_return_ inline wint_t __CRTDECL getwchar(void) + {return (fgetwc(stdin)); } /* stdin */ +_Check_return_opt_ inline wint_t __CRTDECL putwchar(_In_ wchar_t _C) + {return (fputwc(_C, stdout)); } /* stdout */ +#endif /* !defined (__cplusplus) || defined (_M_CEE_PURE) || defined (_CRT_GETPUTWCHAR_NOINLINE) */ + +#define getwc(_stm) fgetwc(_stm) +#define putwc(_c,_stm) fputwc(_c,_stm) +#define _putwc_nolock(_c,_stm) _fputwc_nolock(_c,_stm) +#define _getwc_nolock(_c) _fgetwc_nolock(_c) + +#if defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) +#define fgetwc(_stm) _getwc_nolock(_stm) +#define fputwc(_c,_stm) _putwc_nolock(_c,_stm) +#define ungetwc(_c,_stm) _ungetwc_nolock(_c,_stm) +#endif /* defined (_CRT_DISABLE_PERFCRIT_LOCKS) && !defined (_DLL) */ + +#define _WSTDIO_DEFINED +#endif /* _WSTDIO_DEFINED */ + +#ifndef _WSTDLIB_DEFINED + +/* wide function prototypes, also declared in stdlib.h */ + +_Check_return_wat_ _CRTIMP errno_t __cdecl _itow_s (_In_ int _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _itow_s, _In_ int, _Value, _Post_z_ wchar_t, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _itow, _In_ int, _Value, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_ int, _Radix) +_Check_return_wat_ _CRTIMP errno_t __cdecl _ltow_s (_In_ long _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _ltow_s, _In_ long, _Value, _Post_z_ wchar_t, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _ltow, _In_ long, _Value, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_ int, _Radix) +_Check_return_wat_ _CRTIMP errno_t __cdecl _ultow_s (_In_ unsigned long _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _ultow_s, _In_ unsigned long, _Value, _Post_z_ wchar_t, _Dest, _In_ int, _Radix) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_1_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _ultow, _In_ unsigned long, _Value, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _In_ int, _Radix) +_Check_return_ _CRTIMP double __cdecl wcstod(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr); +_Check_return_ _CRTIMP double __cdecl _wcstod_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long __cdecl wcstol(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix); +_Check_return_ _CRTIMP long __cdecl _wcstol_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long long __cdecl wcstoll(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix); +_Check_return_ _CRTIMP long long __cdecl _wcstoll_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned long __cdecl wcstoul(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix); +_Check_return_ _CRTIMP unsigned long __cdecl _wcstoul_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned long long __cdecl wcstoull(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix); +_Check_return_ _CRTIMP unsigned long long __cdecl _wcstoull_l(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long double __cdecl wcstold(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr); +_Check_return_ _CRTIMP long double __cdecl _wcstold_l(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP float __cdecl wcstof(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr); +_Check_return_ _CRTIMP float __cdecl _wcstof_l(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_opt_ _locale_t _Locale); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP + +_Check_return_ _CRTIMP _CRT_INSECURE_DEPRECATE(_wdupenv_s) wchar_t * __cdecl _wgetenv(_In_z_ const wchar_t * _VarName); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wgetenv_s(_Out_ size_t * _ReturnSize, _Out_writes_opt_z_(_DstSizeInWords) wchar_t * _DstBuf, _In_ size_t _DstSizeInWords, _In_z_ const wchar_t * _VarName); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_1(errno_t, _wgetenv_s, _Out_ size_t *, _ReturnSize, _Post_z_ wchar_t, _Dest, _In_z_ const wchar_t *, _VarName) + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wdupenv_s") +#undef _wdupenv_s +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_wat_ _CRTIMP errno_t __cdecl _wdupenv_s(_Outptr_result_buffer_maybenull_(*_BufferSizeInWords) _Outptr_result_z_ wchar_t **_Buffer, _Out_opt_ size_t *_BufferSizeInWords, _In_z_ const wchar_t *_VarName); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wdupenv_s") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ +#ifndef _CRT_WSYSTEM_DEFINED +#define _CRT_WSYSTEM_DEFINED +_CRTIMP int __cdecl _wsystem(_In_opt_z_ const wchar_t * _Command); +#endif /* _CRT_WSYSTEM_DEFINED */ + +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_Check_return_ _CRTIMP double __cdecl _wtof(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP double __cdecl _wtof_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wtoi(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP int __cdecl _wtoi_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long __cdecl _wtol(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP long __cdecl _wtol_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP long long __cdecl _wtoll(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP long long __cdecl _wtoll_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); + +_Check_return_wat_ _CRTIMP errno_t __cdecl _i64tow_s(_In_ __int64 _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +_CRTIMP _CRT_INSECURE_DEPRECATE(_i64tow_s) wchar_t * __cdecl _i64tow(_In_ __int64 _Val, _Pre_notnull_ _Post_z_ wchar_t * _DstBuf, _In_ int _Radix); +_Check_return_wat_ _CRTIMP errno_t __cdecl _ui64tow_s(_In_ unsigned __int64 _Val, _Out_writes_z_(_SizeInWords) wchar_t * _DstBuf, _In_ size_t _SizeInWords, _In_ int _Radix); +_CRTIMP _CRT_INSECURE_DEPRECATE(_ui64tow_s) wchar_t * __cdecl _ui64tow(_In_ unsigned __int64 _Val, _Pre_notnull_ _Post_z_ wchar_t * _DstBuf, _In_ int _Radix); +_Check_return_ _CRTIMP __int64 __cdecl _wtoi64(_In_z_ const wchar_t *_Str); +_Check_return_ _CRTIMP __int64 __cdecl _wtoi64_l(_In_z_ const wchar_t *_Str, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP __int64 __cdecl _wcstoi64(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP __int64 __cdecl _wcstoi64_l(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP unsigned __int64 __cdecl _wcstoui64(_In_z_ const wchar_t * _Str, _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix); +_Check_return_ _CRTIMP unsigned __int64 __cdecl _wcstoui64_l(_In_z_ const wchar_t *_Str , _Out_opt_ _Deref_post_z_ wchar_t ** _EndPtr, _In_ int _Radix, _In_opt_ _locale_t _Locale); + +#define _WSTDLIB_DEFINED +#endif /* _WSTDLIB_DEFINED */ + + +#ifndef _WSTDLIBP_DEFINED + +/* wide function prototypes, also declared in stdlib.h */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wfullpath") +#undef _wfullpath +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP wchar_t * __cdecl _wfullpath(_Out_writes_opt_z_(_SizeInWords) wchar_t * _FullPath, _In_z_ const wchar_t * _Path, _In_ size_t _SizeInWords); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wfullpath") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _wmakepath_s(_Out_writes_z_(_SizeInWords) wchar_t * _PathResult, _In_ size_t _SizeInWords, _In_opt_z_ const wchar_t * _Drive, _In_opt_z_ const wchar_t * _Dir, _In_opt_z_ const wchar_t * _Filename, + _In_opt_z_ const wchar_t * _Ext); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_4(errno_t, _wmakepath_s, _Post_z_ wchar_t, _ResultPath, _In_opt_z_ const wchar_t *, _Drive, _In_opt_z_ const wchar_t *, _Dir, _In_opt_z_ const wchar_t *, _Filename, _In_opt_z_ const wchar_t *, _Ext) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_4(void, __RETURN_POLICY_VOID, _CRTIMP, _wmakepath, _Pre_notnull_ _Post_z_, wchar_t, _ResultPath, _In_opt_z_ const wchar_t *, _Drive, _In_opt_z_ const wchar_t *, _Dir, _In_opt_z_ const wchar_t *, _Filename, _In_opt_z_ const wchar_t *, _Ext) +#ifndef _CRT_WPERROR_DEFINED +#define _CRT_WPERROR_DEFINED +_CRTIMP void __cdecl _wperror(_In_opt_z_ const wchar_t * _ErrMsg); +#endif /* _CRT_WPERROR_DEFINED */ + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_Check_return_ _CRTIMP int __cdecl _wputenv(_In_z_ const wchar_t * _EnvString); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wputenv_s(_In_z_ const wchar_t * _Name, _In_z_ const wchar_t * _Value); +_CRTIMP errno_t __cdecl _wsearchenv_s(_In_z_ const wchar_t * _Filename, _In_z_ const wchar_t * _EnvVar, _Out_writes_z_(_SizeInWords) wchar_t * _ResultPath, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_2_0(errno_t, _wsearchenv_s, _In_z_ const wchar_t *, _Filename, _In_z_ const wchar_t *, _EnvVar, _Post_z_ wchar_t, _ResultPath) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_2_0(void, __RETURN_POLICY_VOID, _CRTIMP, _wsearchenv, _In_z_ const wchar_t *, _Filename, _In_z_ const wchar_t *, _EnvVar, _Pre_notnull_ _Post_z_, wchar_t, _ResultPath) +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +_CRT_INSECURE_DEPRECATE(_wsplitpath_s) _CRTIMP void __cdecl _wsplitpath(_In_z_ const wchar_t * _FullPath, _Pre_maybenull_ _Post_z_ wchar_t * _Drive, _Pre_maybenull_ _Post_z_ wchar_t * _Dir, _Pre_maybenull_ _Post_z_ wchar_t * _Filename, _Pre_maybenull_ _Post_z_ wchar_t * _Ext); +_CRTIMP_ALTERNATIVE errno_t __cdecl _wsplitpath_s(_In_z_ const wchar_t * _FullPath, + _Out_writes_opt_z_(_DriveSize) wchar_t * _Drive, _In_ size_t _DriveSize, + _Out_writes_opt_z_(_DirSize) wchar_t * _Dir, _In_ size_t _DirSize, + _Out_writes_opt_z_(_FilenameSize) wchar_t * _Filename, _In_ size_t _FilenameSize, + _Out_writes_opt_z_(_ExtSize) wchar_t * _Ext, _In_ size_t _ExtSize); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_SPLITPATH(errno_t, _wsplitpath_s, wchar_t, _Path) + +#define _WSTDLIBP_DEFINED +#endif /* _WSTDLIBP_DEFINED */ + + + +#ifndef _WSTRING_DEFINED + +/* wide function prototypes, also declared in string.h */ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("_wcsdup") +#undef _wcsdup +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRTIMP wchar_t * __cdecl _wcsdup(_In_z_ const wchar_t * _Str); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("_wcsdup") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcscat_s(_Inout_updates_z_(_DstSize) wchar_t * _Dst, _In_ rsize_t _DstSize, const wchar_t * _Src); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, wcscat_s, wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +#ifndef RC_INVOKED +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcscat, _Inout_updates_z_(_String_length_(_Dest) + _String_length_(_Source) + 1), wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +#endif +_Check_return_ _CRTIMP _CONST_RETURN wchar_t * __cdecl wcschr(_In_z_ const wchar_t * _Str, wchar_t _Ch); +_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcscpy_s(_Out_writes_z_(_DstSize) wchar_t * _Dst, _In_ rsize_t _DstSize, _In_z_ const wchar_t * _Src); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, wcscpy_s, _Post_z_ wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcscpy, _Out_writes_z_(_String_length_(_Source) + 1), wchar_t, _Dest, _In_z_ const wchar_t *, _Source) +_Check_return_ _CRTIMP size_t __cdecl wcscspn(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _Control); +_Check_return_ _CRTIMP size_t __cdecl wcslen(_In_z_ const wchar_t * _Str); +_Check_return_ _CRTIMP +_When_(_MaxCount > _String_length_(_Src), _Post_satisfies_(return == _String_length_(_Src))) +_When_(_MaxCount <= _String_length_(_Src), _Post_satisfies_(return == _MaxCount)) +size_t __cdecl wcsnlen(_In_z_ const wchar_t * _Src, _In_ size_t _MaxCount); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_ static __inline +_When_(_MaxCount > _String_length_(_Src), _Post_satisfies_(return == _String_length_(_Src))) +_When_(_MaxCount <= _String_length_(_Src), _Post_satisfies_(return == _MaxCount)) +size_t __CRTDECL wcsnlen_s(_In_z_ const wchar_t * _Src, _In_ size_t _MaxCount) +{ + return (_Src == NULL) ? 0 : wcsnlen(_Src, _MaxCount); +} +#endif /* __STDC_WANT_SECURE_LIB__ */ +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcsncat_s(_Inout_updates_z_(_DstSize) wchar_t * _Dst, _In_ rsize_t _DstSize, _In_z_ const wchar_t * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, wcsncat_s, _Prepost_z_ wchar_t, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count) + +#pragma warning(push) +#pragma warning(disable:6059) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcsncat, wcsncat_s, _Inout_updates_z_(_Size) wchar_t, _Inout_updates_z_(_Count), wchar_t, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count) +#pragma warning(pop) + +_Check_return_ _CRTIMP int __cdecl wcsncmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount); +#if __STDC_WANT_SECURE_LIB__ +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl wcsncpy_s(_Out_writes_z_(_DstSize) wchar_t * _Dst, _In_ rsize_t _DstSize, _In_z_ const wchar_t * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, wcsncpy_s, wchar_t, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, wcsncpy, wcsncpy_s, _Out_writes_z_(_Size) wchar_t, _Out_writes_(_Count) _Post_maybez_, wchar_t, _Dest, _In_z_ const wchar_t *, _Source, _In_ size_t, _Count) +_Check_return_ _CRTIMP _CONST_RETURN wchar_t * __cdecl wcspbrk(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _Control); +_Check_return_ _CRTIMP _CONST_RETURN wchar_t * __cdecl wcsrchr(_In_z_ const wchar_t * _Str, _In_ wchar_t _Ch); +_Check_return_ _CRTIMP size_t __cdecl wcsspn(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _Control); +_Check_return_ _Ret_maybenull_ _CRTIMP _CONST_RETURN wchar_t * __cdecl wcsstr(_In_z_ const wchar_t * _Str, _In_z_ const wchar_t * _SubStr); +_Check_return_ _CRT_INSECURE_DEPRECATE(wcstok_s) _CRTIMP wchar_t * __cdecl wcstok(_Inout_opt_z_ wchar_t * _Str, _In_z_ const wchar_t * _Delim); +_Check_return_ _CRTIMP_ALTERNATIVE wchar_t * __cdecl wcstok_s(_Inout_opt_z_ wchar_t * _Str, _In_z_ const wchar_t * _Delim, _Inout_ _Deref_prepost_opt_z_ wchar_t ** _Context); +_Check_return_ _CRT_INSECURE_DEPRECATE(_wcserror_s) _CRTIMP wchar_t * __cdecl _wcserror(_In_ int _ErrNum); +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcserror_s(_Out_writes_opt_z_(_SizeInWords) wchar_t * _Buf, _In_ size_t _SizeInWords, _In_ int _ErrNum); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcserror_s, wchar_t, _Buffer, _In_ int, _Error) +_Check_return_ _CRT_INSECURE_DEPRECATE(__wcserror_s) _CRTIMP wchar_t * __cdecl __wcserror(_In_opt_z_ const wchar_t * _Str); +_Check_return_wat_ _CRTIMP errno_t __cdecl __wcserror_s(_Out_writes_opt_z_(_SizeInWords) wchar_t * _Buffer, _In_ size_t _SizeInWords, _In_z_ const wchar_t * _ErrMsg); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, __wcserror_s, wchar_t, _Buffer, _In_z_ const wchar_t *, _ErrorMessage) + +_Check_return_ _CRTIMP int __cdecl _wcsicmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRTIMP int __cdecl _wcsicmp_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsnicmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _wcsnicmp_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _wcsnset_s(_Inout_updates_z_(_DstSizeInWords) wchar_t * _Dst, _In_ size_t _DstSizeInWords, wchar_t _Val, _In_ size_t _MaxCount); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_2(errno_t, _wcsnset_s, _Prepost_z_ wchar_t, _Dst, wchar_t, _Val, _In_ size_t, _MaxCount) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsnset, _wcsnset_s, _Inout_updates_z_(_Size) wchar_t, _Inout_updates_z_(_MaxCount), wchar_t, _Str, wchar_t, _Val, _In_ size_t, _MaxCount) +_CRTIMP wchar_t * __cdecl _wcsrev(_Inout_z_ wchar_t * _Str); +_Check_return_wat_ _CRTIMP_ALTERNATIVE errno_t __cdecl _wcsset_s(_Inout_updates_z_(_SizeInWords) wchar_t * _Str, _In_ size_t _SizeInWords, wchar_t _Val); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcsset_s, _Prepost_z_ wchar_t, _Str, wchar_t, _Val) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsset, _wcsset_s, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _Str, wchar_t, _Val) + +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcslwr_s(_Inout_updates_z_(_SizeInWords) wchar_t * _Str, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wcslwr_s, wchar_t, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcslwr, _Inout_z_, wchar_t, _String) +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcslwr_s_l(_Inout_updates_z_(_SizeInWords) wchar_t * _Str, _In_ size_t _SizeInWords, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcslwr_s_l, wchar_t, _String, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcslwr_l, _wcslwr_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _String, _In_opt_ _locale_t, _Locale) +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcsupr_s(_Inout_updates_z_(_Size) wchar_t * _Str, _In_ size_t _Size); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wcsupr_s, wchar_t, _String) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsupr, _Inout_z_, wchar_t, _String) +_Check_return_wat_ _CRTIMP errno_t __cdecl _wcsupr_s_l(_Inout_updates_z_(_Size) wchar_t * _Str, _In_ size_t _Size, _In_opt_ _locale_t _Locale); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wcsupr_s_l, _Prepost_z_ wchar_t, _String, _In_opt_ _locale_t, _Locale) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1_EX(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wcsupr_l, _wcsupr_s_l, _Inout_updates_z_(_Size) wchar_t, _Inout_z_, wchar_t, _String, _In_opt_ _locale_t, _Locale) +_Check_return_opt_ _CRTIMP size_t __cdecl wcsxfrm(_Out_writes_opt_(_MaxCount) _Post_maybez_ wchar_t * _Dst, _In_z_ const wchar_t * _Src, _In_ size_t _MaxCount); +_Check_return_opt_ _CRTIMP size_t __cdecl _wcsxfrm_l(_Out_writes_opt_(_MaxCount) _Post_maybez_ wchar_t * _Dst, _In_z_ const wchar_t *_Src, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl wcscoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRTIMP int __cdecl _wcscoll_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsicoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRTIMP int __cdecl _wcsicoll_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t *_Str2, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsncoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _wcsncoll_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); +_Check_return_ _CRTIMP int __cdecl _wcsnicoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount); +_Check_return_ _CRTIMP int __cdecl _wcsnicoll_l(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount, _In_opt_ _locale_t _Locale); + +#ifdef __cplusplus +#ifndef _CPP_WIDE_INLINES_DEFINED +#define _CPP_WIDE_INLINES_DEFINED +extern "C++" { +_Check_return_ inline wchar_t * __CRTDECL wcschr(_In_z_ wchar_t *_Str, wchar_t _Ch) + {return ((wchar_t *)wcschr((const wchar_t *)_Str, _Ch)); } +_Check_return_ inline wchar_t * __CRTDECL wcspbrk(_In_z_ wchar_t *_Str, _In_z_ const wchar_t *_Control) + {return ((wchar_t *)wcspbrk((const wchar_t *)_Str, _Control)); } +_Check_return_ inline wchar_t * __CRTDECL wcsrchr(_In_z_ wchar_t *_Str, _In_ wchar_t _Ch) + {return ((wchar_t *)wcsrchr((const wchar_t *)_Str, _Ch)); } +_Check_return_ _Ret_maybenull_ inline wchar_t * __CRTDECL wcsstr(_In_z_ wchar_t *_Str, _In_z_ const wchar_t *_SubStr) + {return ((wchar_t *)wcsstr((const wchar_t *)_Str, _SubStr)); } +} +#endif /* _CPP_WIDE_INLINES_DEFINED */ +#endif /* __cplusplus */ + +#if !__STDC__ + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma push_macro("wcsdup") +#undef wcsdup +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsdup) _CRTIMP wchar_t * __cdecl wcsdup(_In_z_ const wchar_t * _Str); + +#if defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) +#pragma pop_macro("wcsdup") +#endif /* defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC) */ + +/* old names */ +#define wcswcs wcsstr + +/* prototypes for oldnames.lib functions */ +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsicmp) _CRTIMP int __cdecl wcsicmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsnicmp) _CRTIMP int __cdecl wcsnicmp(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2, _In_ size_t _MaxCount); +_CRT_NONSTDC_DEPRECATE(_wcsnset) _CRTIMP wchar_t * __cdecl wcsnset(_Inout_updates_z_(_MaxCount) wchar_t * _Str, _In_ wchar_t _Val, _In_ size_t _MaxCount); +_CRT_NONSTDC_DEPRECATE(_wcsrev) _CRTIMP wchar_t * __cdecl wcsrev(_Inout_z_ wchar_t * _Str); +_CRT_NONSTDC_DEPRECATE(_wcsset) _CRTIMP wchar_t * __cdecl wcsset(_Inout_z_ wchar_t * _Str, wchar_t _Val); +_CRT_NONSTDC_DEPRECATE(_wcslwr) _CRTIMP wchar_t * __cdecl wcslwr(_Inout_z_ wchar_t * _Str); +_CRT_NONSTDC_DEPRECATE(_wcsupr) _CRTIMP wchar_t * __cdecl wcsupr(_Inout_z_ wchar_t * _Str); +_Check_return_ _CRT_NONSTDC_DEPRECATE(_wcsicoll) _CRTIMP int __cdecl wcsicoll(_In_z_ const wchar_t * _Str1, _In_z_ const wchar_t * _Str2); + +#endif /* !__STDC__ */ + +#define _WSTRING_DEFINED +#endif /* _WSTRING_DEFINED */ + +#ifndef _TM_DEFINED +struct tm { + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday - [0,6] */ + int tm_yday; /* days since January 1 - [0,365] */ + int tm_isdst; /* daylight savings time flag */ + }; +#define _TM_DEFINED +#endif /* _TM_DEFINED */ + +#ifndef _WTIME_DEFINED + +/* wide function prototypes, also declared in time.h */ + +_CRT_INSECURE_DEPRECATE(_wasctime_s) _CRTIMP wchar_t * __cdecl _wasctime(_In_ const struct tm * _Tm); +_CRTIMP errno_t __cdecl _wasctime_s(_Out_writes_(_SizeInWords) _Post_readable_size_(26) wchar_t *_Buf, _In_ size_t _SizeInWords, _In_ const struct tm * _Tm); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wasctime_s, _Post_readable_size_(26) wchar_t, _Buffer, _In_ const struct tm *, _Time) + +_CRT_INSECURE_DEPRECATE(_wctime32_s) _CRTIMP wchar_t * __cdecl _wctime32(_In_ const __time32_t *_Time); +_CRTIMP errno_t __cdecl _wctime32_s(_Out_writes_(_SizeInWords) _Post_readable_size_(26) wchar_t* _Buf, _In_ size_t _SizeInWords, _In_ const __time32_t * _Time); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wctime32_s, _Post_readable_size_(26) wchar_t, _Buffer, _In_ const __time32_t *, _Time) + +_CRTIMP size_t __cdecl wcsftime(_Out_writes_z_(_SizeInWords) wchar_t * _Buf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t * _Format, _In_ const struct tm * _Tm); +_CRTIMP size_t __cdecl _wcsftime_l(_Out_writes_z_(_SizeInWords) wchar_t * _Buf, _In_ size_t _SizeInWords, _In_z_ _Printf_format_string_ const wchar_t *_Format, _In_ const struct tm *_Tm, _In_opt_ _locale_t _Locale); + +_CRTIMP errno_t __cdecl _wstrdate_s(_Out_writes_(_SizeInWords) _Post_readable_size_(9) wchar_t * _Buf, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wstrdate_s, _Post_readable_size_(9) wchar_t, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wstrdate, _Out_writes_z_(9), wchar_t, _Buffer) + +_CRTIMP errno_t __cdecl _wstrtime_s(_Out_writes_(_SizeInWords) _Post_readable_size_(9) wchar_t * _Buf, _In_ size_t _SizeInWords); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(errno_t, _wstrtime_s, _Post_readable_size_(9) wchar_t, _Buffer) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(wchar_t *, __RETURN_POLICY_DST, _CRTIMP, _wstrtime, _Out_writes_z_(9), wchar_t, _Buffer) + +_CRT_INSECURE_DEPRECATE(_wctime64_s) _CRTIMP wchar_t * __cdecl _wctime64(_In_ const __time64_t * _Time); +_CRTIMP errno_t __cdecl _wctime64_s(_Out_writes_(_SizeInWords) _Post_readable_size_(26) wchar_t* _Buf, _In_ size_t _SizeInWords, _In_ const __time64_t *_Time); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1(errno_t, _wctime64_s, _Post_readable_size_(26) wchar_t, _Buffer, _In_ const __time64_t *, _Time) + +#if !defined (RC_INVOKED) && !defined (__midl) +#include <wtime.inl> +#endif /* !defined (RC_INVOKED) && !defined (__midl) */ + +#define _WTIME_DEFINED +#endif /* _WTIME_DEFINED */ + + +typedef int mbstate_t; +typedef wchar_t _Wint_t; + +_CRTIMP wint_t __cdecl btowc(int); +_CRTIMP size_t __cdecl mbrlen(_In_reads_bytes_opt_(_SizeInBytes) _Pre_opt_z_ const char * _Ch, _In_ size_t _SizeInBytes, + _Out_opt_ mbstate_t * _State); +_CRTIMP size_t __cdecl mbrtowc(_Pre_maybenull_ _Post_z_ wchar_t * _DstCh, _In_reads_bytes_opt_(_SizeInBytes) _Pre_opt_z_ const char * _SrcCh, + _In_ size_t _SizeInBytes, _Out_opt_ mbstate_t * _State); +_CRTIMP errno_t __cdecl mbsrtowcs_s(_Out_opt_ size_t* _Retval, _Out_writes_opt_z_(_Size) wchar_t * _Dst, _In_ size_t _Size, _Inout_ _Deref_prepost_opt_valid_ const char ** _PSrc, _In_ size_t _N, _Out_opt_ mbstate_t * _State); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_3(errno_t, mbsrtowcs_s, _Out_opt_ size_t *, _Retval, _Post_z_ wchar_t, _Dest, _Inout_ _Deref_prepost_opt_valid_ const char **, _PSource, _In_ size_t, _Count, _Out_opt_ mbstate_t *, _State) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_SIZE(_CRTIMP, mbsrtowcs, _Pre_notnull_ _Post_z_, wchar_t, _Dest, _Inout_ _Deref_prepost_opt_valid_ const char **, _PSrc, _In_ size_t, _Count, _Inout_opt_ mbstate_t *, _State) + +_CRTIMP errno_t __cdecl wcrtomb_s(_Out_opt_ size_t * _Retval, _Out_writes_opt_z_(_SizeInBytes) char * _Dst, + _In_ size_t _SizeInBytes, _In_ wchar_t _Ch, _Out_opt_ mbstate_t * _State); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_2(errno_t, wcrtomb_s, _Out_opt_ size_t *, _Retval, _Out_writes_opt_z_(_Size) char, _Dest, _In_ wchar_t, _Source, _Out_opt_ mbstate_t *, _State) +__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_2_SIZE(_CRTIMP, wcrtomb, _Pre_maybenull_ _Post_z_, char, _Dest, _In_ wchar_t, _Source, _Out_opt_ mbstate_t *, _State) +_CRTIMP errno_t __cdecl wcsrtombs_s(_Out_opt_ size_t * _Retval, _Out_writes_bytes_to_opt_(_SizeInBytes, *_Retval) char * _Dst, + _In_ size_t _SizeInBytes, _Inout_ _Deref_prepost_z_ const wchar_t ** _Src, _In_ size_t _Size, _Out_opt_ mbstate_t * _State); +__DEFINE_CPP_OVERLOAD_SECURE_FUNC_1_3(errno_t, wcsrtombs_s, _Out_opt_ size_t *, _Retval, _Out_writes_opt_z_(_Size) char, _Dest, _Inout_ _Deref_prepost_z_ const wchar_t **, _PSrc, _In_ size_t, _Count, _Out_opt_ mbstate_t *, _State) +__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_3_SIZE(_CRTIMP, wcsrtombs, _Pre_maybenull_ _Post_z_, char, _Dest, _Inout_ _Deref_prepost_z_ const wchar_t **, _PSource, _In_ size_t, _Count, _Out_opt_ mbstate_t *, _State) +_CRTIMP int __cdecl wctob(_In_ wint_t _WCh); + +#ifndef __midl + +/* memcpy and memmove are defined just for use in wmemcpy and wmemmove */ +_CRTIMP _CRT_INSECURE_DEPRECATE_MEMORY(memmove_s) void * __cdecl memmove(_Out_writes_bytes_all_opt_(_Size) void * _Dst, _In_reads_bytes_opt_(_Size) const void * _Src, _In_ size_t _Size); +_CRT_INSECURE_DEPRECATE_MEMORY(memcpy_s) +_Post_equal_to_(_Dst) +_At_buffer_((unsigned char*)_Dst, _Iter_, _Size, _Post_satisfies_(((unsigned char*)_Dst)[_Iter_] == ((unsigned char*)_Src)[_Iter_])) +void * __cdecl memcpy(_Out_writes_bytes_all_(_Size) void * _Dst, _In_reads_bytes_(_Size) const void * _Src, _In_ size_t _Size); +#if __STDC_WANT_SECURE_LIB__ +_CRTIMP errno_t __cdecl memcpy_s(_Out_writes_bytes_to_opt_(_DstSize, _MaxCount) void * _Dst, _In_ rsize_t _DstSize, _In_reads_bytes_opt_(_MaxCount) const void * _Src, _In_ rsize_t _MaxCount); +_CRTIMP errno_t __cdecl memmove_s(_Out_writes_bytes_to_opt_(_DstSize, _MaxCount) void * _Dst, _In_ rsize_t _DstSize, _In_reads_bytes_opt_(_MaxCount) const void * _Src, _In_ rsize_t _MaxCount); +#endif /* __STDC_WANT_SECURE_LIB__ */ +__inline int __CRTDECL fwide(_In_opt_ FILE * _F, int _M) + {(void)_F; return (_M); } +__inline int __CRTDECL mbsinit(_In_opt_ const mbstate_t *_P) + {return (_P == NULL || *_P == 0); } +__inline _CONST_RETURN wchar_t * __CRTDECL wmemchr(_In_reads_(_N) const wchar_t *_S, _In_ wchar_t _C, _In_ size_t _N) + {for (; 0 < _N; ++_S, --_N) + if (*_S == _C) + return (_CONST_RETURN wchar_t *)(_S); + return (0); } +__inline int __CRTDECL wmemcmp(_In_reads_(_N) const wchar_t *_S1, _In_reads_(_N) const wchar_t *_S2, _In_ size_t _N) + {for (; 0 < _N; ++_S1, ++_S2, --_N) + if (*_S1 != *_S2) + return (*_S1 < *_S2 ? -1 : +1); + return (0); } + +_Post_equal_to_(_S1) +_At_buffer_(_S1, _Iter_, _N, _Post_satisfies_(_S1[_Iter_] == _S2[_Iter_])) +__inline _CRT_INSECURE_DEPRECATE_MEMORY(wmemcpy_s) wchar_t * __CRTDECL wmemcpy(_Out_writes_all_(_N) wchar_t *_S1, _In_reads_(_N) const wchar_t *_S2, _In_ size_t _N) + { +#pragma warning( push ) +#pragma warning( disable : 4996 6386 ) + return (wchar_t *)memcpy(_S1, _S2, _N*sizeof(wchar_t)); +#pragma warning( pop ) + } + +__inline _CRT_INSECURE_DEPRECATE_MEMORY(wmemmove_s) wchar_t * __CRTDECL wmemmove(_Out_writes_all_opt_(_N) wchar_t *_S1, _In_reads_opt_(_N) const wchar_t *_S2, _In_ size_t _N) + { +#pragma warning( push ) +#pragma warning( disable : 4996 6386 ) +#pragma warning( disable : 6387) + /* prefast noise VSW 493303 */ + return (wchar_t *)memmove(_S1, _S2, _N*sizeof(wchar_t)); +#pragma warning( pop ) + } + +#if __STDC_WANT_SECURE_LIB__ +errno_t __CRTDECL wmemcpy_s(_Out_writes_to_opt_(_N1, _N) wchar_t *_S1, _In_ rsize_t _N1, _In_reads_opt_(_N) const wchar_t *_S2, rsize_t _N); +errno_t __CRTDECL wmemmove_s(_Out_writes_to_opt_(_N1, _N) wchar_t *_S1, _In_ rsize_t _N1, _In_reads_opt_(_N) const wchar_t *_S2, _In_ rsize_t _N); +#endif /* __STDC_WANT_SECURE_LIB__ */ + +_Post_equal_to_(_S) +_At_buffer_(_S, _Iter_, _N, _Post_satisfies_(_S[_Iter_] == _C)) +__inline wchar_t * __CRTDECL wmemset(_Out_writes_all_(_N) wchar_t *_S, _In_ wchar_t _C, _In_ size_t _N) + { + wchar_t *_Su = _S; + for (; 0 < _N; ++_Su, --_N) + { + *_Su = _C; + } + return (_S); + } + +#ifdef __cplusplus +extern "C++" { +inline wchar_t * __CRTDECL wmemchr(_In_reads_(_N) wchar_t *_S, _In_ wchar_t _C, _In_ size_t _N) + { return (wchar_t *)wmemchr((const wchar_t *)_S, _C, _N); } +} +#endif /* __cplusplus */ +#endif /* __midl */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#pragma pack(pop) + +#endif /* _INC_WCHAR */ diff --git a/test_data/lots_of_files/wctype.h b/test_data/lots_of_files/wctype.h new file mode 100644 index 0000000..e6afedc --- /dev/null +++ b/test_data/lots_of_files/wctype.h @@ -0,0 +1,241 @@ +/*** +*wctype.h - declarations for wide character functions +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Created from wchar.h January 1996 by P.J. Plauger +* +*Purpose: +* This file contains the types, macros and function declarations for +* all ctype-style wide-character functions. They may also be declared in +* wchar.h. +* [ISO] +* +* Note: keep in sync with ctype.h and wchar.h. +* +* [Public] +* +****/ + + +#pragma once + +#ifndef _INC_WCTYPE +#define _INC_WCTYPE + +#if !defined(_WIN32) +#error ERROR: Only Win32 target supported! +#endif + +#include <crtdefs.h> + +#pragma pack(push,_CRT_PACKING) + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Define _CRTIMP */ + +#ifndef _CRTIMP +#ifdef _DLL +#define _CRTIMP __declspec(dllimport) +#else /* ndef _DLL */ +#define _CRTIMP +#endif /* _DLL */ +#endif /* _CRTIMP */ + +/* Define _CRTIMP2 */ +#ifndef _CRTIMP2 +#if defined(CRTDLL2) && defined(_CRTBLD) +#define _CRTIMP2 __declspec(dllexport) +#else /* ndef CRTDLL2 && _CRTBLD */ +#if defined(_DLL) && !defined(_STATIC_CPPLIB) +#define _CRTIMP2 __declspec(dllimport) +#else /* ndef _DLL && !STATIC_CPPLIB */ +#define _CRTIMP2 +#endif /* _DLL && !STATIC_CPPLIB */ +#endif /* CRTDLL2 && _CRTBLD */ +#endif /* _CRTIMP2 */ + +#ifndef _WCHAR_T_DEFINED +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif + +#ifndef _WCTYPE_T_DEFINED +typedef unsigned short wint_t; +typedef unsigned short wctype_t; +#define _WCTYPE_T_DEFINED +#endif + + +#ifndef WEOF +#define WEOF (wint_t)(0xFFFF) +#endif + +/* + * This declaration allows the user access to the ctype look-up + * array _ctype defined in ctype.obj by simply including ctype.h + */ +#ifndef _CRT_CTYPEDATA_DEFINED +#define _CRT_CTYPEDATA_DEFINED +#ifndef _CTYPE_DISABLE_MACROS + +#ifdef _CRTBLD +extern const unsigned short __newctype[]; +#if defined(_DLL) && defined(_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP const unsigned short ** __cdecl __p__pctype(void); +#endif +#endif /* _CRTBLD */ + +#ifndef __PCTYPE_FUNC +#if defined(_CRT_DISABLE_PERFCRIT_LOCKS) && !defined(_DLL) +#define __PCTYPE_FUNC _pctype +#else +#define __PCTYPE_FUNC __pctype_func() +#endif +#endif /* __PCTYPE_FUNC */ + +_CRTIMP const unsigned short * __cdecl __pctype_func(void); +#if !defined(_M_CEE_PURE) +_CRTIMP extern const unsigned short *_pctype; +#else +#define _pctype (__pctype_func()) +#endif /* !defined(_M_CEE_PURE) */ +#endif /* _CTYPE_DISABLE_MACROS */ +#endif + +#ifndef _CRT_WCTYPEDATA_DEFINED +#define _CRT_WCTYPEDATA_DEFINED +#ifndef _CTYPE_DISABLE_MACROS +#if !defined(_M_CEE_PURE) +_CRTIMP extern const unsigned short _wctype[]; +#endif /* !defined(_M_CEE_PURE) */ + +#ifdef _CRTBLD +extern const unsigned short __newctype[]; +#if defined(_DLL) && defined(_M_IX86) +/* Retained for compatibility with VC++ 5.0 and earlier versions */ +_CRTIMP const wctype_t ** __cdecl __p__pwctype(void); +#endif +#endif /* _CRTBLD */ + +_CRTIMP const wctype_t * __cdecl __pwctype_func(void); +#if !defined(_M_CEE_PURE) +_CRTIMP extern const wctype_t *_pwctype; +#else +#define _pwctype (__pwctype_func()) +#endif /* !defined(_M_CEE_PURE) */ +#endif /* _CTYPE_DISABLE_MACROS */ +#endif + + + +/* set bit masks for the possible character types */ + +#define _UPPER 0x1 /* upper case letter */ +#define _LOWER 0x2 /* lower case letter */ +#define _DIGIT 0x4 /* digit[0-9] */ +#define _SPACE 0x8 /* space, tab, carriage return, newline, */ + /* vertical tab or form feed */ +#define _PUNCT 0x10 /* punctuation character */ +#define _CONTROL 0x20 /* control character */ +#define _BLANK 0x40 /* space char (tab handled separately) */ +#define _HEX 0x80 /* hexadecimal digit */ + +#define _LEADBYTE 0x8000 /* multibyte leadbyte */ +#define _ALPHA (0x0100|_UPPER|_LOWER) /* alphabetic character */ + + +/* Function prototypes */ + +#ifndef _WCTYPE_DEFINED + +/* Character classification function prototypes */ +/* also declared in ctype.h */ + +_CRTIMP int __cdecl iswalpha(wint_t); +_CRTIMP int __cdecl iswupper(wint_t); +_CRTIMP int __cdecl iswlower(wint_t); +_CRTIMP int __cdecl iswdigit(wint_t); +_CRTIMP int __cdecl iswxdigit(wint_t); +_CRTIMP int __cdecl iswspace(wint_t); +_CRTIMP int __cdecl iswpunct(wint_t); +_CRTIMP int __cdecl iswblank(wint_t); +_CRTIMP int __cdecl iswalnum(wint_t); +_CRTIMP int __cdecl iswprint(wint_t); +_CRTIMP int __cdecl iswgraph(wint_t); +_CRTIMP int __cdecl iswcntrl(wint_t); +_CRTIMP int __cdecl iswascii(wint_t); +_CRTIMP int __cdecl isleadbyte(int); + +_CRTIMP wint_t __cdecl towupper(wint_t); +_CRTIMP wint_t __cdecl towlower(wint_t); + +_CRTIMP int __cdecl iswctype(wint_t, wctype_t); + +_CRTIMP int __cdecl __iswcsymf(wint_t); +_CRTIMP int __cdecl __iswcsym(wint_t); + +#ifdef _CRT_USE_WINAPI_FAMILY_DESKTOP_APP +_CRT_OBSOLETE(iswctype) _CRTIMP int __cdecl is_wctype(wint_t, wctype_t); +#endif /* _CRT_USE_WINAPI_FAMILY_DESKTOP_APP */ + +#define _WCTYPE_DEFINED +#endif + +#ifndef _WCTYPE_INLINE_DEFINED +#if !defined(__cplusplus) || defined(_M_CEE_PURE) || defined(MRTDLL) +#define iswalpha(_c) ( iswctype(_c,_ALPHA) ) +#define iswupper(_c) ( iswctype(_c,_UPPER) ) +#define iswlower(_c) ( iswctype(_c,_LOWER) ) +#define iswdigit(_c) ( iswctype(_c,_DIGIT) ) +#define iswxdigit(_c) ( iswctype(_c,_HEX) ) +#define iswspace(_c) ( iswctype(_c,_SPACE) ) +#define iswpunct(_c) ( iswctype(_c,_PUNCT) ) +#define iswblank(_c) (((_c) == '\t') ? _BLANK : iswctype(_c,_BLANK) ) +#define iswalnum(_c) ( iswctype(_c,_ALPHA|_DIGIT) ) +#define iswprint(_c) ( iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT) ) +#define iswgraph(_c) ( iswctype(_c,_PUNCT|_ALPHA|_DIGIT) ) +#define iswcntrl(_c) ( iswctype(_c,_CONTROL) ) +#define iswascii(_c) ( (unsigned)(_c) < 0x80 ) + +#define isleadbyte(c) (__pctype_func()[(unsigned char)(c)] & _LEADBYTE) +#else /* __cplusplus */ +inline int __cdecl iswalpha(wint_t _C) {return (iswctype(_C,_ALPHA)); } +inline int __cdecl iswupper(wint_t _C) {return (iswctype(_C,_UPPER)); } +inline int __cdecl iswlower(wint_t _C) {return (iswctype(_C,_LOWER)); } +inline int __cdecl iswdigit(wint_t _C) {return (iswctype(_C,_DIGIT)); } +inline int __cdecl iswxdigit(wint_t _C) {return (iswctype(_C,_HEX)); } +inline int __cdecl iswspace(wint_t _C) {return (iswctype(_C,_SPACE)); } +inline int __cdecl iswpunct(wint_t _C) {return (iswctype(_C,_PUNCT)); } +inline int __cdecl iswblank(wint_t _C) {return (((_C) == '\t') ? _BLANK : iswctype(_C,_BLANK)); } +inline int __cdecl iswalnum(wint_t _C) {return (iswctype(_C,_ALPHA|_DIGIT)); } +inline int __cdecl iswprint(wint_t _C) + {return (iswctype(_C,_BLANK|_PUNCT|_ALPHA|_DIGIT)); } +inline int __cdecl iswgraph(wint_t _C) + {return (iswctype(_C,_PUNCT|_ALPHA|_DIGIT)); } +inline int __cdecl iswcntrl(wint_t _C) {return (iswctype(_C,_CONTROL)); } +inline int __cdecl iswascii(wint_t _C) {return ((unsigned)(_C) < 0x80); } + +inline int __cdecl isleadbyte(int _C) + {return (__pctype_func()[(unsigned char)(_C)] & _LEADBYTE); } +#endif /* __cplusplus */ +#define _WCTYPE_INLINE_DEFINED +#endif /* _WCTYPE_INLINE_DEFINED */ + +typedef wchar_t wctrans_t; +_CRTIMP wint_t __cdecl towctrans(wint_t, wctrans_t); +_CRTIMP wctrans_t __cdecl wctrans(const char *); +_CRTIMP wctype_t __cdecl wctype(const char *); + + +#ifdef __cplusplus +} +#endif + +#pragma pack(pop) + +#endif /* _INC_WCTYPE */ diff --git a/test_data/lots_of_files/win32_4ed.cpp b/test_data/lots_of_files/win32_4ed.cpp new file mode 100644 index 0000000..ab0a653 --- /dev/null +++ b/test_data/lots_of_files/win32_4ed.cpp @@ -0,0 +1,1985 @@ +/* + * 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 <stdlib.h> + +#include "4coder_custom.cpp" + +#undef exec_command +#undef exec_command_keep_stack +#undef clear_parameters + +#include "4ed_system.h" +#include "4ed_rendering.h" +#include "4ed.h" + +#include <windows.h> +#include <GL/gl.h> + +#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; + b8 out_of_window; + i8 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[MDFR_INDEX_COUNT]; +}; + +struct Win32_Input_Chunk{ + Win32_Input_Chunk_Transient trans; + Win32_Input_Chunk_Persistent pers; +}; + +struct Win32_Coroutine{ + Coroutine coroutine; + Win32_Coroutine *next; + int done; +}; + +struct Win32_Vars{ + HWND window_handle; + HDC window_hdc; + Render_Target target; + + HANDLE update_loop_thread; + DWORD update_loop_thread_id; + + 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; + String 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; + + // NOTE(allen): I don't expect to have many of these, but it pays + // to look a head a little so this is set up so that we can just bump + // it up if needed. + Win32_Coroutine coroutine_data[2]; + Win32_Coroutine *coroutine_free; +}; + +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; + + String fname_str = make_string_slowly(filename); + if (fname_str.size >= 1024) return result; + + char fixed_space[1024]; + String fixed_str = make_fixed_width_string(fixed_space); + copy(&fixed_str, fname_str); + terminate_with_null(&fixed_str); + replace_char(fixed_str, '/', '\\'); + + file = CreateFile((char*)fixed_str.str, 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->block_size = 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; + replace_char(info->filename, '\\', '/'); + ++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; + } + } + } + } + else{ + if (directory.str == 0){ + Win32FreeMemory(file_list->block); + file_list->block = 0; + file_list->block_size = 0; + } + file_list->infos = 0; + file_list->count = 0; + } +} + +internal +FILE_EXISTS_SIG(system_file_exists){ + char full_filename_space[1024]; + String full_filename; + HANDLE file; + b32 result; + + result = 0; + + if (len < sizeof(full_filename_space)){ + full_filename = make_fixed_width_string(full_filename_space); + copy(&full_filename, make_string(filename, len)); + terminate_with_null(&full_filename); + + file = CreateFile(full_filename.str, GENERIC_READ, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (file != INVALID_HANDLE_VALUE){ + CloseHandle(file); + result = 1; + } + } + + 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){ + String directory = make_string(dir, *len, capacity); + b32 result = 0; + i32 old_size; + + 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(&directory); + terminate_with_null(&directory); + } + else{ + if (directory.size + rel_len + 1 > directory.memory_size){ + old_size = directory.size; + append_partial(&directory, rel_path); + append_partial(&directory, "\\"); + if (Win32DirectoryExists(directory.str)){ + result = 1; + } + else{ + directory.size = old_size; + } + } + } + } + + *len = directory.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; + memcpy(&result, &h, sizeof(result)); + return(result); +} + +internal void* +Win32Ptr(Plat_Handle h){ + void *result; + memcpy(&result, &h, sizeof(result)); + return(result); +} + +internal Plat_Handle +Win32GenHandle(HANDLE h){ + Plat_Handle result = {}; + Assert(sizeof(Plat_Handle) >= sizeof(h)); + memcpy(&result, &h, sizeof(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 = system_get_memory(new_size); + memory->size = new_size; + if (old_data){ + memcpy(memory->data, old_data, old_size); + system_free_memory(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 Win32_Coroutine* +Win32AllocCoroutine(){ + Win32_Coroutine *result = win32vars.coroutine_free; + Assert(result != 0); + win32vars.coroutine_free = result->next; + return(result); +} + +internal void +Win32FreeCoroutine(Win32_Coroutine *data){ + data->next = win32vars.coroutine_free; + win32vars.coroutine_free = data; +} + +internal void +Win32CoroutineMain(void *arg_){ + Win32_Coroutine *c = (Win32_Coroutine*)arg_; + c->coroutine.func(&c->coroutine); + c->done = 1; + Win32FreeCoroutine(c); + SwitchToFiber(c->coroutine.yield_handle); +} + +internal +Sys_Create_Coroutine_Sig(system_create_coroutine){ + Win32_Coroutine *c; + Coroutine *coroutine; + void *fiber; + + c = Win32AllocCoroutine(); + c->done = 0; + + coroutine = &c->coroutine; + + fiber = CreateFiber(0, Win32CoroutineMain, coroutine); + + coroutine->plat_handle = Win32GenHandle(fiber); + coroutine->func = func; + + return(coroutine); +} + +internal +Sys_Launch_Coroutine_Sig(system_launch_coroutine){ + Win32_Coroutine *c = (Win32_Coroutine*)coroutine; + void *fiber; + + fiber = Win32Handle(coroutine->plat_handle); + coroutine->yield_handle = GetCurrentFiber(); + coroutine->in = in; + coroutine->out = out; + + SwitchToFiber(fiber); + + if (c->done){ + DeleteFiber(fiber); + Win32FreeCoroutine(c); + coroutine = 0; + } + + return(coroutine); +} + +Sys_Resume_Coroutine_Sig(system_resume_coroutine){ + Win32_Coroutine *c = (Win32_Coroutine*)coroutine; + void *fiber; + + Assert(c->done == 0); + + coroutine->yield_handle = GetCurrentFiber(); + coroutine->in = in; + coroutine->out = out; + + fiber = Win32Ptr(coroutine->plat_handle); + + SwitchToFiber(fiber); + + if (c->done){ + DeleteFiber(fiber); + Win32FreeCoroutine(c); + coroutine = 0; + } + + return(coroutine); +} + +Sys_Yield_Coroutine_Sig(system_yield_coroutine){ + SwitchToFiber(coroutine->yield_handle); +} + +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 +Sys_To_Binary_Path(system_to_binary_path){ + b32 translate_success = 0; + i32 max = out_filename->memory_size; + i32 size = GetModuleFileName(0, out_filename->str, max); + if (size > 0 && size < max-1){ + out_filename->size = size; + remove_last_folder(out_filename); + if (append(out_filename, filename) && terminate_with_null(out_filename)){ + translate_success = 1; + } + } + return (translate_success); +} + +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->file_exists = system_file_exists; + win32vars.system->directory_cd = system_directory_cd; + + win32vars.system->post_clipboard = system_post_clipboard; + win32vars.system->time = system_time; + + win32vars.system->create_coroutine = system_create_coroutine; + win32vars.system->launch_coroutine = system_launch_coroutine; + win32vars.system->resume_coroutine = system_resume_coroutine; + win32vars.system->yield_coroutine = system_yield_coroutine; + + 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; + +#ifdef FRED_NOT_PACKAGE + 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; +#endif + + win32vars.system->slash = '/'; +} + +#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[MDFR_SHIFT_INDEX] = 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[MDFR_CONTROL_INDEX] = ctrl; + control_keys[MDFR_ALT_INDEX] = 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[MDFR_CONTROL_INDEX] && + !control_keys[MDFR_ALT_INDEX]) + 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 < MDFR_INDEX_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){ + ConvertThreadToFiber(0); + + 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[MDFR_CAPS_INDEX] = 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 = (char*)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.l = input_chunk.pers.mouse_l; + mouse.press_l = input_chunk.trans.mouse_l_press; + mouse.release_l = input_chunk.trans.mouse_l_release; + + mouse.r = input_chunk.pers.mouse_r; + mouse.press_r = input_chunk.trans.mouse_r_press; + mouse.release_r = 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, + &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; + } + } + } + + int free_list_count = 0; + for (file = exchange_vars.file.free_list.next; + file != &exchange_vars.file.free_list; + file = file->next){ + ++free_list_count; + if (file->data){ + system_free_memory(file->data); + } + } + + if (exchange_vars.file.free_list.next != &exchange_vars.file.free_list){ + Assert(free_list_count != 0); + ex__insert_range(exchange_vars.file.free_list.next, exchange_vars.file.free_list.prev, + &exchange_vars.file.available); + + exchange_vars.file.num_active -= free_list_count; + } + + 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); +} + +#ifndef FRED_NOT_PACKAGE +#include <stdio.h> +#endif + +#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(); + + ConvertThreadToFiber(0); + win32vars.coroutine_free = win32vars.coroutine_data; + for (i32 i = 0; i+1 < ArrayCount(win32vars.coroutine_data); ++i){ + win32vars.coroutine_data[i].next = win32vars.coroutine_data + i + 1; + } + + 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); + + base = (LPVOID)0; + memory_vars.user_memory_size = Mbytes(2); + memory_vars.user_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); + replace_char(current_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; + + +#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_alpha_4coder_version = (_Get_Version_Function*) + GetProcAddress(win32vars.custom, "get_alpha_4coder_version"); + // + if (win32vars.custom_api.get_alpha_4coder_version == 0 || + win32vars.custom_api.get_alpha_4coder_version(MAJOR, MINOR, PATCH) == 0){ + printf("Error: application and custom version numbers don't match"); + return 22; + } + + win32vars.custom_api.get_bindings = (Get_Binding_Data_Function*) + GetProcAddress(win32vars.custom, "get_bindings"); + } +#endif + + //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(); + + if (win32vars.custom_api.get_bindings == 0){ + win32vars.custom_api.get_bindings = (Get_Binding_Data_Function*)get_bindings; + } + + 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: " VERSION + + 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 = (char*)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.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); + + SetForegroundWindow(window_handle); + SetActiveWindow(window_handle); + + 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 + + diff --git a/test_data/lots_of_files/win32_base.cpp b/test_data/lots_of_files/win32_base.cpp new file mode 100644 index 0000000..e3068d4 --- /dev/null +++ b/test_data/lots_of_files/win32_base.cpp @@ -0,0 +1,365 @@ +/* + * 4tech reusable win32 layer + * by Allen Webster + * 08.20.2015 + * + */ + +// TOP + +#include <Windows.h> +#include <Windowsx.h> + +struct Win32{ + HWND hwnd; + HDC dc; + bool keep_playing; + + Memory memory; + Render_Target render_target; + Mouse_State mouse, previous_mouse; + + HCURSOR cursor_ibeam; + HCURSOR cursor_arrow; + HCURSOR cursor_leftright; + HCURSOR cursor_updown; + +#if RENDER_MODE == OPENGL_RENDER + HGLRC glcontext; +#endif +}; + +global Win32 win32; + +internal void +win32_debug(char *msg){ + MessageBox(win32.hwnd, + msg, + "debug out", + MB_OK); +} + +#if RENDER_MODE == OPENGL_RENDER + +internal void +win32_screen_init(){ + PIXELFORMATDESCRIPTOR format; + int format_id; + BOOL success; + + format.nSize = sizeof(format); + format.nVersion = 1; + format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; + format.iPixelType = PFD_TYPE_RGBA; + format.cColorBits = 32; + format.cRedBits = 0; + format.cRedShift = 0; + format.cGreenBits = 0; + format.cGreenShift = 0; + format.cBlueBits = 0; + format.cBlueShift = 0; + format.cAlphaBits = 0; + format.cAlphaShift = 0; + format.cAccumBits = 0; + format.cAccumRedBits = 0; + format.cAccumGreenBits = 0; + format.cAccumBlueBits = 0; + format.cAccumAlphaBits = 0; + format.cDepthBits = 16; + format.cStencilBits = 0; + format.cAuxBuffers = 0; + format.iLayerType = PFD_MAIN_PLANE; + format.bReserved = 0; + format.dwLayerMask = 0; + format.dwVisibleMask = 0; + format.dwDamageMask = 0; + + win32.dc = GetDC(win32.hwnd); + _Assert(win32.dc); + format_id = ChoosePixelFormat(win32.dc, &format); + _Assert(format_id != 0); + success = SetPixelFormat(win32.dc, format_id, &format); + _Assert(success == TRUE); + + win32.glcontext = wglCreateContext(win32.dc); + wglMakeCurrent(win32.dc, win32.glcontext); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glClearColor(0,0,0,0); + glFlush(); + SwapBuffers(win32.dc); + ShowWindow(win32.hwnd, SW_SHOW); +} + +internal void +win32_resize_screen(i32 w, i32 h){ + if (w > 0 && h > 0){ + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, w, h, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glScissor(0, 0, w, h); + + win32.render_target.w = w; + win32.render_target.h = h; + } +} + +internal void +win32_redraw_screen(){ + glFlush(); + SwapBuffers(win32.dc); +} + +internal void +win32_screen_free(){ + wglMakeCurrent(0, 0); + wglDeleteContext(win32.glcontext); +} + +#endif + +internal void +win32_track_leave(){ + TRACKMOUSEEVENT track; + track.cbSize = sizeof(track); + track.dwFlags = TME_LEAVE; + track.hwndTrack = win32.hwnd; + TrackMouseEvent(&track); +} + +internal u64 +win32_get_time(){ + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + return time.QuadPart; +} + +LRESULT ColorWindowProc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam){ + LRESULT result = {}; + switch (uMsg){ + case WM_SIZE: + { + i32 w = LOWORD(lParam); + i32 h = HIWORD(lParam); + win32_resize_screen(w, h); + }break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + + App_Step_Data step; + step.memory = win32.memory; + step.render_target = win32.render_target; + step.mouse = win32.mouse; + App_Step_Out step_out = {}; + app_step(&step, &step_out); + win32_redraw_screen(); + + EndPaint(hwnd, &ps); + }break; + + case WM_CLOSE: + case WM_DESTROY: + { + win32.keep_playing = 0; + }break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { + + }break; + + case WM_LBUTTONDOWN: + { + win32.mouse.l = 1; + }break; + case WM_RBUTTONDOWN: + { + win32.mouse.r = 1; + }break; + + case WM_LBUTTONUP: + { + win32.mouse.l = 0; + }break; + case WM_RBUTTONUP: + { + win32.mouse.r = 0; + }break; + + case WM_MOUSEMOVE: + { + win32.mouse.x = GET_X_LPARAM(lParam); + win32.mouse.y = GET_Y_LPARAM(lParam); + win32.mouse.in_window = 1; + win32_track_leave(); + }break; + + case WM_MOUSEWHEEL: + { + win32.mouse.wheel = GET_WHEEL_DELTA_WPARAM(wParam); + }break; + + case WM_MOUSELEAVE: + { + win32.mouse.in_window = 0; + }break; + + case WM_KILLFOCUS: + { + win32.mouse = {}; + }break; + + default: + { + result = DefWindowProc(hwnd, uMsg, wParam, lParam); + }break; + } + return result; +} + +int +WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow){ + win32 = {}; + + WNDCLASSEX winclass; + winclass.cbSize = sizeof(WNDCLASSEX); + winclass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + winclass.lpfnWndProc = ColorWindowProc; + winclass.cbClsExtra = 0; + winclass.cbWndExtra = 0; + winclass.hInstance = hInstance; + winclass.hIcon = 0; + winclass.hCursor = 0; + winclass.hbrBackground = 0; + winclass.lpszMenuName = 0; + winclass.lpszClassName = "color-class"; + winclass.hIconSm = 0; + + ATOM register_result = RegisterClassEx(&winclass); + _Assert(register_result); + + i32 w, h; + w = 800; + h = 600; + + RECT window_rect; + window_rect.left = 0; + window_rect.top = 0; + window_rect.right = w; + window_rect.bottom = h; + + AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, 0); + + HWND hwnd = + CreateWindowEx(0, + "color-class", + "4tech colors", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top, + 0, + 0, + hInstance, + 0); + _Assert(hwnd); + + win32.hwnd = hwnd; + win32.dc = GetDC(win32.hwnd); + win32_screen_init(); + + GetClientRect(win32.hwnd, &window_rect); + + win32_resize_screen(window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top); + + win32.memory.size = Mbytes(1); + win32.memory.mem = VirtualAlloc((LPVOID)Tbytes(2), + win32.memory.size, + MEM_RESERVE|MEM_COMMIT, + PAGE_READWRITE); + _Assert(win32.memory.mem); + + win32.cursor_ibeam = LoadCursor(NULL, IDC_IBEAM); + win32.cursor_arrow = LoadCursor(NULL, IDC_ARROW); + win32.cursor_leftright = LoadCursor(NULL, IDC_SIZEWE); + win32.cursor_updown = LoadCursor(NULL, IDC_SIZENS); + + u64 frame_target = MILLION / 30; + u64 frame_start, frame_end; + u64 frame_used; + + timeBeginPeriod(1); + win32.keep_playing = 1; + frame_start = win32_get_time(); + while (win32.keep_playing){ + MSG msg; + win32.previous_mouse = win32.mouse; + while (PeekMessage(&msg, + 0,0,0, + PM_REMOVE)){ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + win32.mouse.pl = win32.mouse.l && !win32.previous_mouse.l; + win32.mouse.pr = win32.mouse.r && !win32.previous_mouse.r; + + App_Step_Data step; + step.memory = win32.memory; + step.render_target = win32.render_target; + step.mouse = win32.mouse; + + App_Step_Out step_out = {}; + app_step(&step, &step_out); + win32_redraw_screen(); + + switch (step_out.mouse_cursor){ + case APP_MOUSE_CURSOR_IBEAM: + SetCursor(win32.cursor_ibeam); + break; + + case APP_MOUSE_CURSOR_ARROW: + SetCursor(win32.cursor_arrow); + break; + + case APP_MOUSE_CURSOR_LEFTRIGHT: + SetCursor(win32.cursor_leftright); + break; + + case APP_MOUSE_CURSOR_UPDOWN: + SetCursor(win32.cursor_updown); + break; + } + + frame_end = win32_get_time(); + frame_used = frame_end - frame_start; + if (frame_used < frame_target){ + Sleep((DWORD)(frame_target - frame_used) / 1000); + } + frame_start = win32_get_time(); + } + + return 0; +} + +// BOTTOM diff --git a/test_data/lots_of_files/win32_cd.cpp b/test_data/lots_of_files/win32_cd.cpp new file mode 100644 index 0000000..170e6c6 --- /dev/null +++ b/test_data/lots_of_files/win32_cd.cpp @@ -0,0 +1,982 @@ +/* + * Win32 Layer for CipherDrive + */ + +// TOP + +#define LOGICAL_SIZE Mbytes(8) +#define TRANSIENT_SIZE Gbytes(1) + +extern "C"{ +#include "DragAndDrop.h" +} + +#define FTECH_STRING_IMPLEMENTATION +#include "4tech_string.h" + +#include "cd_windows_render_vars.h" + +struct DLL_Reload{ + HMODULE handle; + FILETIME time; + i32 counter; +}; + +struct Win32{ + Win32_Render_Vars render_vars; + + b32 keep_playing; + + u64 perf_frequency; + u64 perf_start; + + Memory memory; + Render_Target target; + Render_Target dbg_target; + Asset_Bank bank; + Input_State input; + Dev_Input_State dev_input; + Key_Events keys; + + System_API system; + + + App_Functions app; + + i32 w, h, out_w, out_h; + +#ifdef DEVELOPER + void *logical_backup; + void *logical_backup_stopped; + Input_State *past_inputs; + i32 max_inputs; + i32 loop_i, loop_end; + i32 loop_mode; + + Input_State input_stopped; + i32 loop_stopped; + b32 file_drop_lock; + + DLL_Reload renderer_reload; + DLL_Reload game_reload; +#endif +}; + +Win32 win32; + +static HANDLE +convert_handle(Platform_Handle handle){ + HANDLE result; + Assert(sizeof(HANDLE) <= sizeof(Platform_Handle)); + result = *(HANDLE*)(&handle); + return(result); +} + +static Platform_Handle +convert_handle(HANDLE handle){ + Platform_Handle result = {0}; + Assert(sizeof(HANDLE) <= sizeof(Platform_Handle)); + *(HANDLE*)(&result) = handle; + return(result); +} + +static u64 +win32_get_time(){ + u64 result = 0; + LARGE_INTEGER time; + if (QueryPerformanceCounter(&time)){ + result = (time.QuadPart - win32.perf_start) * 1000000 / win32.perf_frequency; + } + return(result); +} + +static void +win32_init_gl(){ + Assert(win32.target.init != 0); + win32.target.init(&win32.render_vars); +} + +static void +win32_set_size(i32 w, i32 h, i32 out_w, i32 out_h){ + Assert(win32.target.set_screen != 0); + win32.target.set_screen(w, h, out_w, out_h); +} + +static u8 keycode_lookup_table[255]; + +static void +win32_keycode_init(){ + cd_memset(keycode_lookup_table, 0, sizeof(keycode_lookup_table)); + + keycode_lookup_table[VK_BACK] = key_back; + keycode_lookup_table[VK_DELETE] = key_del; + keycode_lookup_table[VK_UP] = key_up; + keycode_lookup_table[VK_DOWN] = key_down; + keycode_lookup_table[VK_LEFT] = key_left; + keycode_lookup_table[VK_RIGHT] = key_right; + keycode_lookup_table[VK_INSERT] = key_insert; + keycode_lookup_table[VK_HOME] = key_home; + keycode_lookup_table[VK_END] = key_end; + keycode_lookup_table[VK_PRIOR] = key_page_up; + keycode_lookup_table[VK_NEXT] = key_page_down; + keycode_lookup_table[VK_ESCAPE] = key_esc; + + keycode_lookup_table[VK_F1] = key_f1; + keycode_lookup_table[VK_F2] = key_f2; + keycode_lookup_table[VK_F3] = key_f3; + keycode_lookup_table[VK_F4] = key_f4; + keycode_lookup_table[VK_F5] = key_f5; + keycode_lookup_table[VK_F6] = key_f6; + keycode_lookup_table[VK_F7] = key_f7; + keycode_lookup_table[VK_F8] = key_f8; + keycode_lookup_table[VK_F9] = key_f9; + + keycode_lookup_table[VK_F10] = key_f10; + keycode_lookup_table[VK_F11] = key_f11; + keycode_lookup_table[VK_F12] = key_f12; + keycode_lookup_table[VK_F13] = key_f13; + keycode_lookup_table[VK_F14] = key_f14; + keycode_lookup_table[VK_F15] = key_f15; + keycode_lookup_table[VK_F16] = key_f16; +} + +static LRESULT +win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ + LRESULT result = 0; + + switch (uMsg){ + case WM_CLOSE: + case WM_DESTROY: + { + win32.keep_playing = false; + }break; + + case WM_SIZE: + { + win32.w = LOWORD(lParam); + win32.h = HIWORD(lParam); + win32_set_size(win32.w, win32.h, win32.out_w, win32.out_h); + }break; + + 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:break; + + default: + { + b8 current_state = ((lParam & Bit_31)?(0):(1)); + if (current_state){ + char key = keycode_lookup_table[(u8)wParam]; + + if (!key){ + BYTE state[256]; + GetKeyboardState(state); + state[VK_CONTROL] = 0; + + UINT vk = (UINT)wParam; + UINT scan = (UINT)((lParam >> 16) & 0x7F); + WORD c = 0; + i32 result = ToAscii(vk, scan, state, &c, 0); + + if (result < 0){ + ToAscii(vk, scan, state, &c, 0); + } + else if (result == 1){ + key = (char)c; + if (key == '\r'){ + key = '\n'; + } + } + } + + if (key){ + i32 max = ArrayCount(win32.keys.events); + if (win32.keys.count < max){ + win32.keys.events[win32.keys.count++] = key; + } + } + } + }break; + } + }break; + + default: + { + result = DefWindowProc(hwnd, uMsg, wParam, lParam); + }break; + } + + return(result); +} + +#if 0 +static char* +win32_cf_type(CLIPFORMAT cf){ + char *which = ""; + switch (cf){ + case CF_TEXT: which = "CF_TEXT"; break; + case CF_BITMAP: which = "CF_BITMAP"; break; + case CF_METAFILEPICT: which = "CF_METAFILEPICT"; break; + case CF_SYLK: which = "CF_SYLK"; break; + case CF_DIF: which = "CF_DIF"; break; + case CF_TIFF: which = "CF_TIFF"; break; + case CF_OEMTEXT: which = "CF_OEMTEXT"; break; + case CF_DIB: which = "CF_DIB"; break; + case CF_PALETTE: which = "CF_PALETTE"; break; + case CF_PENDATA: which = "CF_PENDATA"; break; + case CF_RIFF: which = "CF_RIFF"; break; + case CF_WAVE: which = "CF_WAVE"; break; + case CF_UNICODETEXT: which = "CF_UNICODETEXT"; break; + case CF_ENHMETAFILE: which = "CF_ENHMETAFILE"; break; + case CF_HDROP: which = "CF_HDROP"; break; + case CF_LOCALE: which = "CF_LOCALE"; break; + case CF_MAX: which = "CF_MAX"; break; + case CF_OWNERDISPLAY: which = "CF_OWNERDISPLAY"; break; + case CF_DSPTEXT: which = "CF_DSPTEXT"; break; + case CF_DSPBITMAP: which = "CF_DSPBITMAP"; break; + case CF_DSPMETAFILEPICT: which = "CF_DSPMETAFILEPICT"; break; + case CF_DSPENHMETAFILE: which = "CF_DSPENHMETAFILE"; break; + case CF_PRIVATEFIRST: which = "CF_PRIVATEFIRST"; break; + case CF_PRIVATELAST: which = "CF_PRIVATELAST"; break; + case CF_GDIOBJFIRST: which = "CF_GDIOBJFIRST"; break; + case CF_GDIOBJLAST: which = "CF_GDIOBJLAST"; break; + } + return(which); +} +#endif + +DWORD +win32_drop_callback(CLIPFORMAT cf, HGLOBAL hData, HWND hWnd, + DWORD dwKeyState, POINTL pt, + void *pUserData){ + DWORD effect = DROPEFFECT_NONE; + + Assert(win32.file_drop_lock); + + u32 count = DragQueryFile((HDROP)hData, 0xFFFFFFFF, 0, 0); + u32 max = ArrayCount(win32.dev_input.drops); + max -= win32.dev_input.drop_count; + + if (count > max){ + count = max; + } + + for (u32 i = 0; i < count; ++i){ + TCHAR file_path[1024]; + + DWORD len = DragQueryFile((HDROP)hData, i, + file_path, ArrayCount(file_path)); + + if (len < 1024){ + Dev_File_Drop *drop = &win32.dev_input.drops[win32.dev_input.drop_count++]; + cd_memcpy(drop->name, file_path, len); + drop->name[len] = 0; + } + else{ + // TODO(allen): Issue warning to developer person who has long file name. + } + } + + win32.dev_input.drop_x = (f32)(pt.x); + win32.dev_input.drop_y = (f32)(pt.y); + + return(effect); +} + +#ifdef DEVELOPER + +File_Dump +DBG_dump_begin(char *filename){ + File_Dump dump = {0}; + HANDLE file = 0; + LARGE_INTEGER size = {0}; + + file = CreateFile(filename, GENERIC_READ, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (file != INVALID_HANDLE_VALUE){ + if (GetFileSizeEx(file, &size)){ + if (size.HighPart == 0){ + dump.handle = convert_handle(file); + dump.size = size.LowPart; + } + else{ + CloseHandle(file); + } + } + else{ + CloseHandle(file); + } + } + + return(dump); +} + +b32 +DBG_dump_end(File_Dump dump, void *buffer){ + b32 result = false; + HANDLE file = convert_handle(dump.handle); + DWORD total_unread = dump.size; + DWORD read_amount = 0; + + if (file != 0){ + if (buffer){ + while (total_unread > 0){ + if (ReadFile(file, buffer, total_unread, &read_amount, 0)){ + buffer = (char*)buffer + read_amount; + total_unread -= read_amount; + } + else{ + break; + } + } + if (total_unread == 0){ + result = true; + } + } + CloseHandle(file); + } + return(result); +} + +b32 +DBG_dump_out(char *file_name, void *buffer, i32 size){ + b32 result = false; + + if (buffer){ + HANDLE file = + CreateFile(file_name, + GENERIC_WRITE, + 0, + 0, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + DWORD size_written = 0; + + if (file != INVALID_HANDLE_VALUE){ + WriteFile(file, buffer, size, &size_written, 0); + result = true; + CloseHandle(file); + } + } + else{ + DeleteFile(file_name); + } + + return(result); +} + +b32 +DBG_copy(char *source, char *name){ + b32 result = CopyFile(source, name, false); + return(result); +} + +i32 +DBG_module_path(char *out, i32 capacity){ + i32 result = 0; + i32 len = GetModuleFileName(0, out, capacity); + + if (len < capacity-1){ + String str = make_string(out, len, len); + remove_last_folder(&str); + if (str.str[str.size-1] == '\\'){ + str.size-=1; + } + terminate_with_null(&str); + result = str.size; + } + + return(result); +} + +i32 +DBG_working_path(char *out, i32 capacity){ + i32 result = 0; + i32 len = GetCurrentDirectory(capacity, out); + + if (len < capacity-1){ + result = len; + } + + return(result); +} + +b32 +DBG_call_script(char *script){ + char cmd[] = "c:\\windows\\system32\\cmd.exe"; + char *env_variables = 0; + char command_line[2048]; + + String s = make_fixed_width_string(command_line); + + copy(&s, make_lit_string("/C ")); + append_partial(&s, script); + b32 success = terminate_with_null(&s); + + if (success){ + success = false; + + char path[2048]; + i32 path_len = DBG_module_path(path, sizeof(path)); + + if (path_len > 0){ + STARTUPINFO startup = {}; + startup.cb = sizeof(STARTUPINFO); + + PROCESS_INFORMATION info = {}; + + if (CreateProcess(cmd, command_line, + 0, 0, FALSE, CREATE_NO_WINDOW, + env_variables, path, + &startup, &info)){ + + CloseHandle(info.hThread); + CloseHandle(info.hProcess); + + success = true; + } + } + } + + return(success); +} + +#endif + +static void* +win32_alloc(i32 size){ + void *result = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return(result); +} + +static void +win32_free(void *ptr){ + VirtualFree(ptr, 0, MEM_RELEASE); +} + +static char* +game_temp_name(i32 counter){ + char *temp = "game_temp0.dll"; + switch (counter % 4){ + case 1: temp = "game_temp1.dll"; break; + case 2: temp = "game_temp2.dll"; break; + case 3: temp = "game_temp3.dll"; break; + } + return(temp); +} + +static char* +renderer_temp_name(i32 counter){ + char *temp = "renderer_temp0.dll"; + switch (counter % 4){ + case 1: temp = "renderer_temp1.dll"; break; + case 2: temp = "renderer_temp2.dll"; break; + case 3: temp = "renderer_temp3.dll"; break; + } + return(temp); +} + +// TODO(allen): Rewrite using CopyFile dumbass. +static HMODULE +win32_copy_load(String path, char *file_name, char *temp_name){ + HMODULE module = 0; + + append(&path, file_name); + terminate_with_null(&path); + + File_Dump dump = DBG_dump_begin(path.str); + if (dump.size > 0){ + void *buffer = win32_alloc(dump.size); + if (buffer){ + if (DBG_dump_end(dump, buffer)){ + remove_last_folder(&path); + append(&path, temp_name); + terminate_with_null(&path); + DBG_dump_out(path.str, buffer, dump.size); + module = LoadLibraryA(path.str); + } + win32_free(buffer); + } + } + + remove_last_folder(&path); + terminate_with_null(&path); + + return(module); +} + +static HMODULE +win32_try_reload(DLL_Reload *reload, String path, char *file_name, char *temp_name){ + HMODULE module = 0; + FILETIME file_time_now; + LONG updated = 0; + HANDLE file = 0; + + append(&path, file_name); + terminate_with_null(&path); + + file = CreateFile(path.str, + GENERIC_READ, + 0, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); + + remove_last_folder(&path); + + if (file != INVALID_HANDLE_VALUE){ + GetFileTime(file, 0, 0, &file_time_now); + CloseHandle(file); + + updated = (CompareFileTime(&reload->time, &file_time_now) < 0); + + if (reload->handle == 0 || updated){ + if (reload->handle != 0){ + FreeLibrary(reload->handle); + } + + reload->time = file_time_now; + reload->handle = win32_copy_load(path, file_name, temp_name); + + module = reload->handle; + } + } + + return(module); +} + +static void +win32_reload_renderer(String path_string){ + HMODULE game_render_module = 0; + Render_Get_Functions *target_get_functions = 0; + Bank_Get_Functions *bank_get_functions = 0; + + game_render_module = + win32_try_reload(&win32.renderer_reload, + path_string, + "CDRenderer.dll", + renderer_temp_name(win32.renderer_reload.counter++)); + + if (game_render_module != 0){ + target_get_functions = (Render_Get_Functions*) + GetProcAddress(game_render_module, "target_get_functions"); + Assert(target_get_functions != 0); + + bank_get_functions = (Bank_Get_Functions*) + GetProcAddress(game_render_module, "bank_get_functions"); + Assert(target_get_functions != 0); + + target_get_functions(&win32.target); + bank_get_functions(&win32.bank); + +#ifdef DEVELOPER + target_get_functions(&win32.dbg_target); + win32.dbg_target.execute = dbg_render_execute; +#endif + } +} + +static void +win32_reload_game(String path_string){ + HMODULE application_module = 0; + App_Step_Function *app_step = 0; + + application_module = + win32_try_reload(&win32.game_reload, + path_string, + "CDGame.dll", + game_temp_name(win32.game_reload.counter++)); + + if (application_module != 0){ + app_step = (App_Step_Function*) + GetProcAddress(application_module, "app_step"); + Assert(app_step != 0); + + win32.app.step = app_step; + } +} + +int +WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow){ + + { + char dir_space[1024]; + int len = GetCurrentDirectory(sizeof(dir_space), dir_space); + String dir = make_string(dir_space, len, sizeof(dir_space)); + append(&dir, "\\data"); + terminate_with_null(&dir); + + if (SetCurrentDirectory(dir.str) == FALSE){ + exit(1); + } + } + + // Window initialization + win32 = {0}; + win32_keycode_init(); + + { + LARGE_INTEGER lpf; + QueryPerformanceFrequency(&lpf); + win32.perf_frequency = lpf.QuadPart; + QueryPerformanceCounter(&lpf); + win32.perf_start = lpf.QuadPart; + } + + // Memory initialization + { + u64 offset = 0; + LPVOID ptr; + + ptr = (LPVOID)Gbytes(1); + win32.memory.logical_size = LOGICAL_SIZE; + win32.memory.logical = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + +#ifdef DEVELOPER + offset += LOGICAL_SIZE; + ptr = (LPVOID)(Gbytes(1) + offset); + win32.logical_backup = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + + offset += LOGICAL_SIZE; + ptr = (LPVOID)(Gbytes(1) + offset); + win32.logical_backup_stopped = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + + offset += LOGICAL_SIZE; + ptr = (LPVOID)(Gbytes(1) + offset); + win32.past_inputs = (Input_State*) + VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + win32.max_inputs = LOGICAL_SIZE/sizeof(Input_State); + + offset += LOGICAL_SIZE; + ptr = (LPVOID)(Gbytes(1) + offset); + win32.memory.developer = VirtualAlloc(ptr, LOGICAL_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + win32.memory.developer_size = LOGICAL_SIZE; +#endif + + ptr = (LPVOID)Gbytes(2); + win32.memory.transient_size = TRANSIENT_SIZE; + win32.memory.transient = VirtualAlloc(ptr, TRANSIENT_SIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + } + + // Linkage initialization + char file_name[1024]; + DWORD len = + GetModuleFileName(0, + file_name, + sizeof(file_name)); + + String path_string = make_string(file_name, len, sizeof(file_name)); + remove_last_folder(&path_string); + + { + win32.system.DBG_dump_begin = DBG_dump_begin; + win32.system.DBG_dump_end = DBG_dump_end; + win32.system.DBG_dump_out = DBG_dump_out; + win32.system.DBG_copy = DBG_copy; + win32.system.DBG_call_script = DBG_call_script; + win32.system.DBG_module_path = DBG_module_path; + win32.system.DBG_working_path = DBG_working_path; + win32.system.DBG_memory_allocate = win32_alloc; + win32.system.DBG_memory_free = win32_free; + + win32_reload_renderer(path_string); + win32_reload_game(path_string); + } + + WNDCLASSEX winclass; + winclass.cbSize = sizeof(WNDCLASSEX); + winclass.style = CS_HREDRAW | CS_VREDRAW; + winclass.lpfnWndProc = win32_proc; + winclass.cbClsExtra = 0; + winclass.cbWndExtra = 0; + winclass.hInstance = hInstance; + winclass.hIcon = 0; + winclass.hCursor = 0; + winclass.hbrBackground = 0; + winclass.lpszMenuName = 0; + winclass.lpszClassName = "cipher-drive-class"; + winclass.hIconSm = 0; + + ATOM register_result = RegisterClassEx(&winclass); + Assert(register_result); + + RECT window_rect; + window_rect.left = 0; + window_rect.top = 0; + window_rect.right = DEFAULT_WIDTH; + window_rect.bottom = DEFAULT_HEIGHT; + + AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, 0); + + HWND hwnd = + CreateWindowEx(0, + "cipher-drive-class", + "Cipher Drive - Dev", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top, + 0, + 0, + hInstance, + 0); + + Assert(hwnd); + + win32.render_vars.hwnd = hwnd; + + GetClientRect(win32.render_vars.hwnd, &window_rect); + + win32.w = window_rect.right - window_rect.left; + win32.h = window_rect.bottom - window_rect.top; + win32.out_w = DEFAULT_WIDTH; + win32.out_h = DEFAULT_HEIGHT; + + win32.target.dim = v2((f32)win32.out_w, (f32)win32.out_h); + win32.dbg_target.dim = v2((f32)win32.out_w, (f32)win32.out_h); + + + // GL initialization + { + win32_init_gl(); + win32_set_size(win32.w, win32.h, win32.out_w, win32.out_h); + } + +#ifdef DEVELOPER + { + // Begin drag&drop + MyDragDropInit(0); + CLIPFORMAT formats[] = { + CF_HDROP + }; + MyRegisterDragDrop(win32.render_vars.hwnd, + formats, ArrayCount(formats), + WM_NULL, win32_drop_callback, + 0); + } +#endif + + // Main loop + u64 FPS = 24; + u64 frame_target = 1000000 / FPS; + u64 frame_start = 0, frame_end = 0, frame_used = 0; + + timeBeginPeriod(1); + win32.keep_playing = true; + + ShowCursor(FALSE); + ShowWindow(win32.render_vars.hwnd, SW_SHOW); + + { + glFlush(); + HDC dc = GetDC(win32.render_vars.hwnd); + SwapBuffers(dc); + ReleaseDC(win32.render_vars.hwnd, dc); + } + + frame_start = win32_get_time(); + while (win32.keep_playing){ + MSG msg; + +#ifdef DEVELOPER + win32.dev_input.drop_count = 0; + win32.file_drop_lock = 1; + cd_memset(&win32.keys, 0, sizeof(win32.keys)); +#endif + + while (PeekMessage(&msg, + 0,0,0, + PM_REMOVE)){ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + +#ifdef DEVELOPER + win32.file_drop_lock = 0; + + win32_reload_renderer(path_string); + win32_reload_game(path_string); +#endif + + Render_Target *dbg_target = 0; + +#ifdef DEVELOPER + dbg_target = &win32.dbg_target; +#endif + + BYTE keys[256]; + GetKeyboardState(keys); + + // NOTE(allen): MSDN says that the high order bit + // indicates whether the key is down, other bits can + // be set for apparently no reason just to screw you. +#define win32_down(b) (0x80 & b) + + win32.input.left_button.prev_down = win32.input.left_button.down; + win32.input.right_button.prev_down = win32.input.right_button.down; + + win32.input.left_button.down = win32_down(keys[VK_LBUTTON]); + win32.input.right_button.down = win32_down(keys[VK_RBUTTON]); + + for (i32 i = 0; i < 26; ++i){ + win32.input.letter_[i].prev_down = win32.input.letter_[i].down; + win32.input.letter_[i].down = win32_down(keys['A' + i]); + } + win32.input.letter = win32.input.letter_ - 'A'; + + for (i32 i = 0; i < 10; ++i){ + win32.input.number[i].prev_down = win32.input.number[i].down; + win32.input.number[i].down = win32_down(keys['0' + i]); + } + + win32.input.up.prev_down = win32.input.up.down; + win32.input.down.prev_down = win32.input.down.down; + win32.input.left.prev_down = win32.input.left.down; + win32.input.right.prev_down = win32.input.right.down; + + win32.input.up.down = win32_down(keys[VK_UP]); + win32.input.down.down = win32_down(keys[VK_DOWN]); + win32.input.left.down = win32_down(keys[VK_LEFT]); + win32.input.right.down = win32_down(keys[VK_RIGHT]); + + win32.input.esc.prev_down = win32.input.esc.down; + win32.input.esc.down = win32_down(keys[VK_ESCAPE]); + + POINT cursor_point; + GetCursorPos(&cursor_point); + ScreenToClient(win32.render_vars.hwnd, &cursor_point); + + win32.input.mx = cursor_point.x; + win32.input.my = win32.out_h - cursor_point.y; + + win32.input.dt = 1.f / FPS; + + Input_State input = win32.input; + +#ifdef DEVELOPER + win32.dev_input.input = input; + + for (i32 i = 0; i < 12; ++i){ + win32.dev_input.fkeys_[i].prev_down = win32.dev_input.fkeys_[i].down; + win32.dev_input.fkeys_[i].down = win32_down(keys[VK_F1 + i]); + } + win32.dev_input.fkeys = win32.dev_input.fkeys_ - 1; + + if (win32.dev_input.fkeys[8].down && !win32.dev_input.fkeys[8].prev_down){ + if (win32.loop_stopped){ + win32.loop_stopped = 0; + } + else{ + cd_memcpy(win32.logical_backup_stopped, win32.memory.logical, LOGICAL_SIZE); + win32.input_stopped = input; + win32.loop_stopped = 1; + } + } + + // TODO(allen): replace memcpy with our own swankier version. + if (win32.dev_input.fkeys[7].down && !win32.dev_input.fkeys[7].prev_down){ + if (win32.loop_stopped){ + win32.loop_stopped = 0; + } + else{ + switch (win32.loop_mode){ + case 0: + cd_memcpy(win32.logical_backup, win32.memory.logical, LOGICAL_SIZE); + win32.loop_mode = 1; + break; + + case 1: + cd_memcpy(win32.memory.logical, win32.logical_backup, LOGICAL_SIZE); + win32.loop_mode = 2; + win32.loop_end = win32.loop_i; + win32.loop_i = 0; + break; + + case 2: + win32.loop_mode = 0; + break; + } + } + } + + win32.dev_input.keys = win32.keys; + + if (win32.loop_stopped){ + cd_memcpy(win32.memory.logical, win32.logical_backup_stopped, LOGICAL_SIZE); + input = win32.input_stopped; + } + else{ + if (win32.loop_mode == 1 && win32.loop_i == win32.max_inputs){ + cd_memcpy(win32.memory.logical, win32.logical_backup, LOGICAL_SIZE); + win32.loop_mode = 2; + win32.loop_end = win32.loop_i; + win32.loop_i = 0; + } + + switch (win32.loop_mode){ + case 1: + win32.past_inputs[win32.loop_i] = input; + ++win32.loop_i; + break; + + case 2: + if (win32.loop_i == win32.loop_end){ + cd_memcpy(win32.memory.logical, win32.logical_backup, LOGICAL_SIZE); + win32.loop_i = 0; + } + input = win32.past_inputs[win32.loop_i]; + ++win32.loop_i; + break; + } + } +#endif + + if (win32.app.step && win32.target.execute){ + win32.app.step(&win32.system, + win32.memory, + &win32.target, + dbg_target, + &win32.bank, + &input, + &win32.dev_input, + win32.loop_mode + win32.loop_stopped * 3); + } + +#ifdef DEVELOPER + { + GLenum error = glGetError(); + GLenum copy = error; + AllowLocal(copy); + } +#endif + + win32.target.display(&win32.render_vars); + + frame_end = win32_get_time(); + frame_used = frame_end - frame_start; + if (frame_used < frame_target){ + Sleep((DWORD)(frame_target - frame_used) / 1000); + } + frame_start = win32_get_time(); + } + + return 0; +} + +// BOTTOM diff --git a/test_data/lots_of_files/win32_handmade.cpp b/test_data/lots_of_files/win32_handmade.cpp new file mode 100644 index 0000000..b2d5fcd --- /dev/null +++ b/test_data/lots_of_files/win32_handmade.cpp @@ -0,0 +1,2177 @@ +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +/* + TODO(casey): THIS IS NOT A FINAL PLATFORM LAYER!!! + + - Make the right calls so Windows doesn't think we're "still loading" for a bit after we actually start + - Saved game locations + - Getting a handle to our own executable file + - Asset loading path + - Threading (launch a thread) + - Raw Input (support for multiple keyboards) + - ClipCursor() (for multimonitor support) + - QueryCancelAutoplay + - WM_ACTIVATEAPP (for when we are not the active application) + - Blit speed improvements (BitBlt) + - Hardware acceleration (OpenGL or Direct3D or BOTH??) + - GetKeyboardLayout (for French keyboards, international WASD support) + - ChangeDisplaySettings option if we detect slow fullscreen blit?? + + Just a partial list of stuff!! +*/ + +#include "handmade_platform.h" + +#include <windows.h> +#include <stdio.h> +#include <malloc.h> +#include <xinput.h> +#include <dsound.h> + +#include "win32_handmade.h" + +// TODO(casey): This is a global for now. +global_variable bool32 GlobalRunning; +global_variable bool32 GlobalPause; +global_variable win32_offscreen_buffer GlobalBackbuffer; +global_variable LPDIRECTSOUNDBUFFER GlobalSecondaryBuffer; +global_variable int64 GlobalPerfCountFrequency; +global_variable bool32 DEBUGGlobalShowCursor; +global_variable WINDOWPLACEMENT GlobalWindowPosition = {sizeof(GlobalWindowPosition)}; + +// NOTE(casey): XInputGetState +#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState) +typedef X_INPUT_GET_STATE(x_input_get_state); +X_INPUT_GET_STATE(XInputGetStateStub) +{ + return(ERROR_DEVICE_NOT_CONNECTED); +} +global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub; +#define XInputGetState XInputGetState_ + +// NOTE(casey): XInputSetState +#define X_INPUT_SET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) +typedef X_INPUT_SET_STATE(x_input_set_state); +X_INPUT_SET_STATE(XInputSetStateStub) +{ + return(ERROR_DEVICE_NOT_CONNECTED); +} +global_variable x_input_set_state *XInputSetState_ = XInputSetStateStub; +#define XInputSetState XInputSetState_ + +#define DIRECT_SOUND_CREATE(name) HRESULT WINAPI name(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter) +typedef DIRECT_SOUND_CREATE(direct_sound_create); + +internal void +CatStrings(size_t SourceACount, char *SourceA, + size_t SourceBCount, char *SourceB, + size_t DestCount, char *Dest) +{ + // TODO(casey): Dest bounds checking! + + for(int Index = 0; + Index < SourceACount; + ++Index) + { + *Dest++ = *SourceA++; + } + + for(int Index = 0; + Index < SourceBCount; + ++Index) + { + *Dest++ = *SourceB++; + } + + *Dest++ = 0; +} + +internal void +Win32GetEXEFileName(win32_state *State) +{ + // NOTE(casey): Never use MAX_PATH in code that is user-facing, because it + // can be dangerous and lead to bad results. + DWORD SizeOfFilename = GetModuleFileNameA(0, State->EXEFileName, sizeof(State->EXEFileName)); + State->OnePastLastEXEFileNameSlash = State->EXEFileName; + for(char *Scan = State->EXEFileName; + *Scan; + ++Scan) + { + if(*Scan == '\\') + { + State->OnePastLastEXEFileNameSlash = Scan + 1; + } + } +} + +internal void +Win32BuildEXEPathFileName(win32_state *State, char *FileName, + int DestCount, char *Dest) +{ + CatStrings(State->OnePastLastEXEFileNameSlash - State->EXEFileName, State->EXEFileName, + StringLength(FileName), FileName, + DestCount, Dest); +} + +#if HANDMADE_INTERNAL +DEBUG_PLATFORM_FREE_FILE_MEMORY(DEBUGPlatformFreeFileMemory) +{ + if(Memory) + { + VirtualFree(Memory, 0, MEM_RELEASE); + } +} + +DEBUG_PLATFORM_READ_ENTIRE_FILE(DEBUGPlatformReadEntireFile) +{ + debug_read_file_result Result = {}; + + HANDLE FileHandle = CreateFileA(Filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if(FileHandle != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER FileSize; + if(GetFileSizeEx(FileHandle, &FileSize)) + { + uint32 FileSize32 = SafeTruncateUInt64(FileSize.QuadPart); + Result.Contents = VirtualAlloc(0, FileSize32, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + if(Result.Contents) + { + DWORD BytesRead; + if(ReadFile(FileHandle, Result.Contents, FileSize32, &BytesRead, 0) && + (FileSize32 == BytesRead)) + { + // NOTE(casey): File read successfully + Result.ContentsSize = FileSize32; + } + else + { + // TODO(casey): Logging + DEBUGPlatformFreeFileMemory(Result.Contents); + Result.Contents = 0; + } + } + else + { + // TODO(casey): Logging + } + } + else + { + // TODO(casey): Logging + } + + CloseHandle(FileHandle); + } + else + { + // TODO(casey): Logging + } + + return(Result); +} + +DEBUG_PLATFORM_WRITE_ENTIRE_FILE(DEBUGPlatformWriteEntireFile) +{ + bool32 Result = false; + + HANDLE FileHandle = CreateFileA(Filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + if(FileHandle != INVALID_HANDLE_VALUE) + { + DWORD BytesWritten; + if(WriteFile(FileHandle, Memory, MemorySize, &BytesWritten, 0)) + { + // NOTE(casey): File read successfully + Result = (BytesWritten == MemorySize); + } + else + { + // TODO(casey): Logging + } + + CloseHandle(FileHandle); + } + else + { + // TODO(casey): Logging + } + + return(Result); +} + +DEBUG_PLATFORM_EXECUTE_SYSTEM_COMMAND(DEBUGExecuteSystemCommand) +{ + debug_executing_process Result = {}; + + STARTUPINFO StartupInfo = {}; + StartupInfo.cb = sizeof(StartupInfo); + StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow = SW_HIDE; + + PROCESS_INFORMATION ProcessInfo = {}; + if(CreateProcess(Command, + CommandLine, + 0, + 0, + FALSE, + 0, + 0, + Path, + &StartupInfo, + &ProcessInfo)) + { + Assert(sizeof(Result.OSHandle) >= sizeof(ProcessInfo.hProcess)); + *(HANDLE *)&Result.OSHandle = ProcessInfo.hProcess; + } + else + { + DWORD ErrorCode = GetLastError(); + *(HANDLE *)&Result.OSHandle = INVALID_HANDLE_VALUE; + } + + return(Result); +} + +DEBUG_PLATFORM_GET_PROCESS_STATE(DEBUGGetProcessState) +{ + debug_process_state Result = {}; + + HANDLE hProcess = *(HANDLE *)&Process.OSHandle; + if(hProcess != INVALID_HANDLE_VALUE) + { + Result.StartedSuccessfully = true; + + if(WaitForSingleObject(hProcess, 0) == WAIT_OBJECT_0) + { + DWORD ReturnCode = 0; + GetExitCodeProcess(hProcess, &ReturnCode); + Result.ReturnCode = ReturnCode; + CloseHandle(hProcess); + } + else + { + Result.IsRunning = true; + } + } + + return(Result); +} +#endif + +inline FILETIME +Win32GetLastWriteTime(char *Filename) +{ + FILETIME LastWriteTime = {}; + + WIN32_FILE_ATTRIBUTE_DATA Data; + if(GetFileAttributesEx(Filename, GetFileExInfoStandard, &Data)) + { + LastWriteTime = Data.ftLastWriteTime; + } + + return(LastWriteTime); +} + +internal win32_game_code +Win32LoadGameCode(char *SourceDLLName, char *TempDLLName, char *LockFileName) +{ + win32_game_code Result = {}; + + WIN32_FILE_ATTRIBUTE_DATA Ignored; + if(!GetFileAttributesEx(LockFileName, GetFileExInfoStandard, &Ignored)) + { + Result.DLLLastWriteTime = Win32GetLastWriteTime(SourceDLLName); + + CopyFile(SourceDLLName, TempDLLName, FALSE); + + Result.GameCodeDLL = LoadLibraryA(TempDLLName); + if(Result.GameCodeDLL) + { + Result.UpdateAndRender = (game_update_and_render *) + GetProcAddress(Result.GameCodeDLL, "GameUpdateAndRender"); + + Result.GetSoundSamples = (game_get_sound_samples *) + GetProcAddress(Result.GameCodeDLL, "GameGetSoundSamples"); + + Result.DEBUGFrameEnd = (debug_game_frame_end *) + GetProcAddress(Result.GameCodeDLL, "DEBUGGameFrameEnd"); + + Result.IsValid = (Result.UpdateAndRender && + Result.GetSoundSamples); + } + } + + if(!Result.IsValid) + { + Result.UpdateAndRender = 0; + Result.GetSoundSamples = 0; + } + + return(Result); +} + +internal void +Win32UnloadGameCode(win32_game_code *GameCode) +{ + if(GameCode->GameCodeDLL) + { + FreeLibrary(GameCode->GameCodeDLL); + GameCode->GameCodeDLL = 0; + } + + GameCode->IsValid = false; + GameCode->UpdateAndRender = 0; + GameCode->GetSoundSamples = 0; +} + +internal void +Win32LoadXInput(void) +{ + // TODO(casey): Test this on Windows 8 + HMODULE XInputLibrary = LoadLibraryA("xinput1_4.dll"); + if(!XInputLibrary) + { + // TODO(casey): Diagnostic + XInputLibrary = LoadLibraryA("xinput9_1_0.dll"); + } + + if(!XInputLibrary) + { + // TODO(casey): Diagnostic + XInputLibrary = LoadLibraryA("xinput1_3.dll"); + } + + if(XInputLibrary) + { + XInputGetState = (x_input_get_state *)GetProcAddress(XInputLibrary, "XInputGetState"); + if(!XInputGetState) {XInputGetState = XInputGetStateStub;} + + XInputSetState = (x_input_set_state *)GetProcAddress(XInputLibrary, "XInputSetState"); + if(!XInputSetState) {XInputSetState = XInputSetStateStub;} + + // TODO(casey): Diagnostic + + } + else + { + // TODO(casey): Diagnostic + } +} + +internal void +Win32InitDSound(HWND Window, int32 SamplesPerSecond, int32 BufferSize) +{ + // NOTE(casey): Load the library + HMODULE DSoundLibrary = LoadLibraryA("dsound.dll"); + if(DSoundLibrary) + { + // NOTE(casey): Get a DirectSound object! - cooperative + direct_sound_create *DirectSoundCreate = (direct_sound_create *) + GetProcAddress(DSoundLibrary, "DirectSoundCreate"); + + // TODO(casey): Double-check that this works on XP - DirectSound8 or 7?? + LPDIRECTSOUND DirectSound; + if(DirectSoundCreate && SUCCEEDED(DirectSoundCreate(0, &DirectSound, 0))) + { + WAVEFORMATEX WaveFormat = {}; + WaveFormat.wFormatTag = WAVE_FORMAT_PCM; + WaveFormat.nChannels = 2; + WaveFormat.nSamplesPerSec = SamplesPerSecond; + WaveFormat.wBitsPerSample = 16; + WaveFormat.nBlockAlign = (WaveFormat.nChannels*WaveFormat.wBitsPerSample) / 8; + WaveFormat.nAvgBytesPerSec = WaveFormat.nSamplesPerSec*WaveFormat.nBlockAlign; + WaveFormat.cbSize = 0; + + if(SUCCEEDED(DirectSound->SetCooperativeLevel(Window, DSSCL_PRIORITY))) + { + DSBUFFERDESC BufferDescription = {}; + BufferDescription.dwSize = sizeof(BufferDescription); + BufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; + + // NOTE(casey): "Create" a primary buffer + LPDIRECTSOUNDBUFFER PrimaryBuffer; + if(SUCCEEDED(DirectSound->CreateSoundBuffer(&BufferDescription, &PrimaryBuffer, 0))) + { + HRESULT Error = PrimaryBuffer->SetFormat(&WaveFormat); + if(SUCCEEDED(Error)) + { + // NOTE(casey): We have finally set the format! + OutputDebugStringA("Primary buffer format was set.\n"); + } + else + { + // TODO(casey): Diagnostic + } + } + else + { + // TODO(casey): Diagnostic + } + } + else + { + // TODO(casey): Diagnostic + } + + // TODO(casey): In release mode, should we not specify DSBCAPS_GLOBALFOCUS? + + // TODO(casey): DSBCAPS_GETCURRENTPOSITION2 + DSBUFFERDESC BufferDescription = {}; + BufferDescription.dwSize = sizeof(BufferDescription); + BufferDescription.dwFlags = DSBCAPS_GETCURRENTPOSITION2; +#if HANDMADE_INTERNAL + BufferDescription.dwFlags |= DSBCAPS_GLOBALFOCUS; +#endif + BufferDescription.dwBufferBytes = BufferSize; + BufferDescription.lpwfxFormat = &WaveFormat; + HRESULT Error = DirectSound->CreateSoundBuffer(&BufferDescription, &GlobalSecondaryBuffer, 0); + if(SUCCEEDED(Error)) + { + OutputDebugStringA("Secondary buffer created successfully.\n"); + } + } + else + { + // TODO(casey): Diagnostic + } + } + else + { + // TODO(casey): Diagnostic + } +} + +internal win32_window_dimension +Win32GetWindowDimension(HWND Window) +{ + win32_window_dimension Result; + + RECT ClientRect; + GetClientRect(Window, &ClientRect); + Result.Width = ClientRect.right - ClientRect.left; + Result.Height = ClientRect.bottom - ClientRect.top; + + return(Result); +} + +internal void +Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int Width, int Height) +{ + // TODO(casey): Bulletproof this. + // Maybe don't free first, free after, then free first if that fails. + + if(Buffer->Memory) + { + VirtualFree(Buffer->Memory, 0, MEM_RELEASE); + } + + Buffer->Width = Width; + Buffer->Height = Height; + + int BytesPerPixel = 4; + Buffer->BytesPerPixel = BytesPerPixel; + + // NOTE(casey): When the biHeight field is negative, this is the clue to + // Windows to treat this bitmap as top-down, not bottom-up, meaning that + // the first three bytes of the image are the color for the top left pixel + // in the bitmap, not the bottom left! + Buffer->Info.bmiHeader.biSize = sizeof(Buffer->Info.bmiHeader); + Buffer->Info.bmiHeader.biWidth = Buffer->Width; + Buffer->Info.bmiHeader.biHeight = Buffer->Height; + Buffer->Info.bmiHeader.biPlanes = 1; + Buffer->Info.bmiHeader.biBitCount = 32; + Buffer->Info.bmiHeader.biCompression = BI_RGB; + + // NOTE(casey): Thank you to Chris Hecker of Spy Party fame + // for clarifying the deal with StretchDIBits and BitBlt! + // No more DC for us. + Buffer->Pitch = Align16(Width*BytesPerPixel); + int BitmapMemorySize = (Buffer->Pitch*Buffer->Height); + Buffer->Memory = VirtualAlloc(0, BitmapMemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + + // TODO(casey): Probably clear this to black +} + +internal void +Win32DisplayBufferInWindow(win32_offscreen_buffer *Buffer, + HDC DeviceContext, int WindowWidth, int WindowHeight) +{ + // TODO(casey): Centering / black bars? + + if((WindowWidth >= Buffer->Width*2) && + (WindowHeight >= Buffer->Height*2)) + { + StretchDIBits(DeviceContext, + 0, 0, 2*Buffer->Width, 2*Buffer->Height, + 0, 0, Buffer->Width, Buffer->Height, + Buffer->Memory, + &Buffer->Info, + DIB_RGB_COLORS, SRCCOPY); + } + else + { +#if 0 + int OffsetX = 10; + int OffsetY = 10; + + PatBlt(DeviceContext, 0, 0, WindowWidth, OffsetY, BLACKNESS); + PatBlt(DeviceContext, 0, OffsetY + Buffer->Height, WindowWidth, WindowHeight, BLACKNESS); + PatBlt(DeviceContext, 0, 0, OffsetX, WindowHeight, BLACKNESS); + PatBlt(DeviceContext, OffsetX + Buffer->Width, 0, WindowWidth, WindowHeight, BLACKNESS); +#else + int OffsetX = 0; + int OffsetY = 0; +#endif + + // NOTE(casey): For prototyping purposes, we're going to always blit + // 1-to-1 pixels to make sure we don't introduce artifacts with + // stretching while we are learning to code the renderer! + StretchDIBits(DeviceContext, + OffsetX, OffsetY, Buffer->Width, Buffer->Height, + 0, 0, Buffer->Width, Buffer->Height, + Buffer->Memory, + &Buffer->Info, + DIB_RGB_COLORS, SRCCOPY); + } +} + +internal LRESULT CALLBACK +Win32MainWindowCallback(HWND Window, + UINT Message, + WPARAM WParam, + LPARAM LParam) +{ + LRESULT Result = 0; + + switch(Message) + { + case WM_CLOSE: + { + // TODO(casey): Handle this with a message to the user? + GlobalRunning = false; + } break; + + case WM_SETCURSOR: + { + if(DEBUGGlobalShowCursor) + { + Result = DefWindowProcA(Window, Message, WParam, LParam); + } + else + { + SetCursor(0); + } + } break; + + case WM_ACTIVATEAPP: + { +#if 0 + if(WParam == TRUE) + { + SetLayeredWindowAttributes(Window, RGB(0, 0, 0), 255, LWA_ALPHA); + } + else + { + SetLayeredWindowAttributes(Window, RGB(0, 0, 0), 64, LWA_ALPHA); + } +#endif + } break; + + case WM_DESTROY: + { + // TODO(casey): Handle this as an error - recreate window? + GlobalRunning = false; + } break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { + Assert(!"Keyboard input came in through a non-dispatch message!"); + } break; + + case WM_PAINT: + { + PAINTSTRUCT Paint; + HDC DeviceContext = BeginPaint(Window, &Paint); + win32_window_dimension Dimension = Win32GetWindowDimension(Window); + Win32DisplayBufferInWindow(&GlobalBackbuffer, DeviceContext, + Dimension.Width, Dimension.Height); + EndPaint(Window, &Paint); + } break; + + default: + { +// OutputDebugStringA("default\n"); + Result = DefWindowProcA(Window, Message, WParam, LParam); + } break; + } + + return(Result); +} + +internal void +Win32ClearBuffer(win32_sound_output *SoundOutput) +{ + VOID *Region1; + DWORD Region1Size; + VOID *Region2; + DWORD Region2Size; + if(SUCCEEDED(GlobalSecondaryBuffer->Lock(0, SoundOutput->SecondaryBufferSize, + &Region1, &Region1Size, + &Region2, &Region2Size, + 0))) + { + // TODO(casey): assert that Region1Size/Region2Size is valid + uint8 *DestSample = (uint8 *)Region1; + for(DWORD ByteIndex = 0; + ByteIndex < Region1Size; + ++ByteIndex) + { + *DestSample++ = 0; + } + + DestSample = (uint8 *)Region2; + for(DWORD ByteIndex = 0; + ByteIndex < Region2Size; + ++ByteIndex) + { + *DestSample++ = 0; + } + + GlobalSecondaryBuffer->Unlock(Region1, Region1Size, Region2, Region2Size); + } +} + +internal void +Win32FillSoundBuffer(win32_sound_output *SoundOutput, DWORD ByteToLock, DWORD BytesToWrite, + game_sound_output_buffer *SourceBuffer) +{ + // TODO(casey): More strenuous test! + VOID *Region1; + DWORD Region1Size; + VOID *Region2; + DWORD Region2Size; + if(SUCCEEDED(GlobalSecondaryBuffer->Lock(ByteToLock, BytesToWrite, + &Region1, &Region1Size, + &Region2, &Region2Size, + 0))) + { + // TODO(casey): assert that Region1Size/Region2Size is valid + + // TODO(casey): Collapse these two loops + DWORD Region1SampleCount = Region1Size/SoundOutput->BytesPerSample; + int16 *DestSample = (int16 *)Region1; + int16 *SourceSample = SourceBuffer->Samples; + for(DWORD SampleIndex = 0; + SampleIndex < Region1SampleCount; + ++SampleIndex) + { + *DestSample++ = *SourceSample++; + *DestSample++ = *SourceSample++; + ++SoundOutput->RunningSampleIndex; + } + + DWORD Region2SampleCount = Region2Size/SoundOutput->BytesPerSample; + DestSample = (int16 *)Region2; + for(DWORD SampleIndex = 0; + SampleIndex < Region2SampleCount; + ++SampleIndex) + { + *DestSample++ = *SourceSample++; + *DestSample++ = *SourceSample++; + ++SoundOutput->RunningSampleIndex; + } + + GlobalSecondaryBuffer->Unlock(Region1, Region1Size, Region2, Region2Size); + } +} + +internal void +Win32ProcessKeyboardMessage(game_button_state *NewState, bool32 IsDown) +{ + if(NewState->EndedDown != IsDown) + { + NewState->EndedDown = IsDown; + ++NewState->HalfTransitionCount; + } +} + +internal void +Win32ProcessXInputDigitalButton(DWORD XInputButtonState, + game_button_state *OldState, DWORD ButtonBit, + game_button_state *NewState) +{ + NewState->EndedDown = ((XInputButtonState & ButtonBit) == ButtonBit); + NewState->HalfTransitionCount = (OldState->EndedDown != NewState->EndedDown) ? 1 : 0; +} + +internal real32 +Win32ProcessXInputStickValue(SHORT Value, SHORT DeadZoneThreshold) +{ + real32 Result = 0; + + if(Value < -DeadZoneThreshold) + { + Result = (real32)((Value + DeadZoneThreshold) / (32768.0f - DeadZoneThreshold)); + } + else if(Value > DeadZoneThreshold) + { + Result = (real32)((Value - DeadZoneThreshold) / (32767.0f - DeadZoneThreshold)); + } + + return(Result); +} + +internal void +Win32GetInputFileLocation(win32_state *State, bool32 InputStream, + int SlotIndex, int DestCount, char *Dest) +{ + char Temp[64]; + wsprintf(Temp, "loop_edit_%d_%s.hmi", SlotIndex, InputStream ? "input" : "state"); + Win32BuildEXEPathFileName(State, Temp, DestCount, Dest); +} + +internal win32_replay_buffer * +Win32GetReplayBuffer(win32_state *State, int unsigned Index) +{ + Assert(Index > 0); + Assert(Index < ArrayCount(State->ReplayBuffers)); + win32_replay_buffer *Result = &State->ReplayBuffers[Index]; + return(Result); +} + +internal void +Win32BeginRecordingInput(win32_state *State, int InputRecordingIndex) +{ + win32_replay_buffer *ReplayBuffer = Win32GetReplayBuffer(State, InputRecordingIndex); + if(ReplayBuffer->MemoryBlock) + { + State->InputRecordingIndex = InputRecordingIndex; + + char FileName[WIN32_STATE_FILE_NAME_COUNT]; + Win32GetInputFileLocation(State, true, InputRecordingIndex, sizeof(FileName), FileName); + State->RecordingHandle = CreateFileA(FileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + +#if 0 + LARGE_INTEGER FilePosition; + FilePosition.QuadPart = State->TotalSize; + SetFilePointerEx(State->RecordingHandle, FilePosition, 0, FILE_BEGIN); +#endif + + CopyMemory(ReplayBuffer->MemoryBlock, State->GameMemoryBlock, State->TotalSize); + } +} + +internal void +Win32EndRecordingInput(win32_state *State) +{ + CloseHandle(State->RecordingHandle); + State->InputRecordingIndex = 0; +} + +internal void +Win32BeginInputPlayBack(win32_state *State, int InputPlayingIndex) +{ + win32_replay_buffer *ReplayBuffer = Win32GetReplayBuffer(State, InputPlayingIndex); + if(ReplayBuffer->MemoryBlock) + { + State->InputPlayingIndex = InputPlayingIndex; + + char FileName[WIN32_STATE_FILE_NAME_COUNT]; + Win32GetInputFileLocation(State, true, InputPlayingIndex, sizeof(FileName), FileName); + State->PlaybackHandle = CreateFileA(FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + +#if 0 + LARGE_INTEGER FilePosition; + FilePosition.QuadPart = State->TotalSize; + SetFilePointerEx(State->PlaybackHandle, FilePosition, 0, FILE_BEGIN); +#endif + + CopyMemory(State->GameMemoryBlock, ReplayBuffer->MemoryBlock, State->TotalSize); + } +} + +internal void +Win32EndInputPlayBack(win32_state *State) +{ + CloseHandle(State->PlaybackHandle); + State->InputPlayingIndex = 0; +} + +internal void +Win32RecordInput(win32_state *State, game_input *NewInput) +{ + DWORD BytesWritten; + WriteFile(State->RecordingHandle, NewInput, sizeof(*NewInput), &BytesWritten, 0); +} + +internal void +Win32PlayBackInput(win32_state *State, game_input *NewInput) +{ + DWORD BytesRead = 0; + if(ReadFile(State->PlaybackHandle, NewInput, sizeof(*NewInput), &BytesRead, 0)) + { + if(BytesRead == 0) + { + // NOTE(casey): We've hit the end of the stream, go back to the beginning + int PlayingIndex = State->InputPlayingIndex; + Win32EndInputPlayBack(State); + Win32BeginInputPlayBack(State, PlayingIndex); + ReadFile(State->PlaybackHandle, NewInput, sizeof(*NewInput), &BytesRead, 0); + } + } +} + +internal void +ToggleFullscreen(HWND Window) +{ + // NOTE(casey): This follows Raymond Chen's prescription + // for fullscreen toggling, see: + // http://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx + + DWORD Style = GetWindowLong(Window, GWL_STYLE); + if(Style & WS_OVERLAPPEDWINDOW) + { + MONITORINFO MonitorInfo = {sizeof(MonitorInfo)}; + if(GetWindowPlacement(Window, &GlobalWindowPosition) && + GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo)) + { + SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW); + SetWindowPos(Window, HWND_TOP, + MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top, + MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left, + MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + } + } + else + { + SetWindowLong(Window, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW); + SetWindowPlacement(Window, &GlobalWindowPosition); + SetWindowPos(Window, 0, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + } +} + +internal void +Win32ProcessPendingMessages(win32_state *State, game_controller_input *KeyboardController) +{ + MSG Message; + while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE)) + { + switch(Message.message) + { + case WM_QUIT: + { + GlobalRunning = false; + } break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { + uint32 VKCode = (uint32)Message.wParam; + + // NOTE(casey): Since we are comparing WasDown to IsDown, + // we MUST use == and != to convert these bit tests to actual + // 0 or 1 values. + bool32 WasDown = ((Message.lParam & (1 << 30)) != 0); + bool32 IsDown = ((Message.lParam & (1 << 31)) == 0); + if(WasDown != IsDown) + { + if(VKCode == 'W') + { + Win32ProcessKeyboardMessage(&KeyboardController->MoveUp, IsDown); + } + else if(VKCode == 'A') + { + Win32ProcessKeyboardMessage(&KeyboardController->MoveLeft, IsDown); + } + else if(VKCode == 'S') + { + Win32ProcessKeyboardMessage(&KeyboardController->MoveDown, IsDown); + } + else if(VKCode == 'D') + { + Win32ProcessKeyboardMessage(&KeyboardController->MoveRight, IsDown); + } + else if(VKCode == 'Q') + { + Win32ProcessKeyboardMessage(&KeyboardController->LeftShoulder, IsDown); + } + else if(VKCode == 'E') + { + Win32ProcessKeyboardMessage(&KeyboardController->RightShoulder, IsDown); + } + else if(VKCode == VK_UP) + { + Win32ProcessKeyboardMessage(&KeyboardController->ActionUp, IsDown); + } + else if(VKCode == VK_LEFT) + { + Win32ProcessKeyboardMessage(&KeyboardController->ActionLeft, IsDown); + } + else if(VKCode == VK_DOWN) + { + Win32ProcessKeyboardMessage(&KeyboardController->ActionDown, IsDown); + } + else if(VKCode == VK_RIGHT) + { + Win32ProcessKeyboardMessage(&KeyboardController->ActionRight, IsDown); + } + else if(VKCode == VK_ESCAPE) + { + Win32ProcessKeyboardMessage(&KeyboardController->Back, IsDown); + } + else if(VKCode == VK_SPACE) + { + Win32ProcessKeyboardMessage(&KeyboardController->Start, IsDown); + } +#if HANDMADE_INTERNAL + else if(VKCode == 'P') + { + if(IsDown) + { + GlobalPause = !GlobalPause; + } + } + else if(VKCode == 'L') + { + if(IsDown) + { + if(State->InputPlayingIndex == 0) + { + if(State->InputRecordingIndex == 0) + { + Win32BeginRecordingInput(State, 1); + } + else + { + Win32EndRecordingInput(State); + Win32BeginInputPlayBack(State, 1); + } + } + else + { + Win32EndInputPlayBack(State); + } + } + } +#endif + if(IsDown) + { + bool32 AltKeyWasDown = (Message.lParam & (1 << 29)); + if((VKCode == VK_F4) && AltKeyWasDown) + { + GlobalRunning = false; + } + if((VKCode == VK_RETURN) && AltKeyWasDown) + { + if(Message.hwnd) + { + ToggleFullscreen(Message.hwnd); + } + } + } + } + + } break; + + default: + { + TranslateMessage(&Message); + DispatchMessageA(&Message); + } break; + } + } +} + +inline LARGE_INTEGER +Win32GetWallClock(void) +{ + LARGE_INTEGER Result; + QueryPerformanceCounter(&Result); + return(Result); +} + +inline real32 +Win32GetSecondsElapsed(LARGE_INTEGER Start, LARGE_INTEGER End) +{ + real32 Result = ((real32)(End.QuadPart - Start.QuadPart) / + (real32)GlobalPerfCountFrequency); + return(Result); +} + +#if 0 +internal void +HandleDebugCycleCounters(game_memory *Memory) +{ +#if HANDMADE_INTERNAL + OutputDebugStringA("DEBUG CYCLE COUNTS:\n"); + for(int CounterIndex = 0; + CounterIndex < ArrayCount(Memory->Counters); + ++CounterIndex) + { + debug_cycle_counter *Counter = Memory->Counters + CounterIndex; + + if(Counter->HitCount) + { + char TextBuffer[256]; + _snprintf_s(TextBuffer, sizeof(TextBuffer), + " %d: %I64ucy %uh %I64ucy/h\n", + CounterIndex, + Counter->CycleCount, + Counter->HitCount, + Counter->CycleCount / Counter->HitCount); + OutputDebugStringA(TextBuffer); + Counter->HitCount = 0; + Counter->CycleCount = 0; + } + } +#endif +} +#endif + +#if 0 + +internal void +Win32DebugDrawVertical(win32_offscreen_buffer *Backbuffer, + int X, int Top, int Bottom, uint32 Color) +{ + if(Top <= 0) + { + Top = 0; + } + + if(Bottom > Backbuffer->Height) + { + Bottom = Backbuffer->Height; + } + + if((X >= 0) && (X < Backbuffer->Width)) + { + uint8 *Pixel = ((uint8 *)Backbuffer->Memory + + X*Backbuffer->BytesPerPixel + + Top*Backbuffer->Pitch); + for(int Y = Top; + Y < Bottom; + ++Y) + { + *(uint32 *)Pixel = Color; + Pixel += Backbuffer->Pitch; + } + } +} + +inline void +Win32DrawSoundBufferMarker(win32_offscreen_buffer *Backbuffer, + win32_sound_output *SoundOutput, + real32 C, int PadX, int Top, int Bottom, + DWORD Value, uint32 Color) +{ + real32 XReal32 = (C * (real32)Value); + int X = PadX + (int)XReal32; + Win32DebugDrawVertical(Backbuffer, X, Top, Bottom, Color); +} + +internal void +Win32DebugSyncDisplay(win32_offscreen_buffer *Backbuffer, + int MarkerCount, win32_debug_time_marker *Markers, + int CurrentMarkerIndex, + win32_sound_output *SoundOutput, real32 TargetSecondsPerFrame) +{ + int PadX = 16; + int PadY = 16; + + int LineHeight = 64; + + real32 C = (real32)(Backbuffer->Width - 2*PadX) / (real32)SoundOutput->SecondaryBufferSize; + for(int MarkerIndex = 0; + MarkerIndex < MarkerCount; + ++MarkerIndex) + { + win32_debug_time_marker *ThisMarker = &Markers[MarkerIndex]; + Assert(ThisMarker->OutputPlayCursor < SoundOutput->SecondaryBufferSize); + Assert(ThisMarker->OutputWriteCursor < SoundOutput->SecondaryBufferSize); + Assert(ThisMarker->OutputLocation < SoundOutput->SecondaryBufferSize); + Assert(ThisMarker->OutputByteCount < SoundOutput->SecondaryBufferSize); + Assert(ThisMarker->FlipPlayCursor < SoundOutput->SecondaryBufferSize); + Assert(ThisMarker->FlipWriteCursor < SoundOutput->SecondaryBufferSize); + + DWORD PlayColor = 0xFFFFFFFF; + DWORD WriteColor = 0xFFFF0000; + DWORD ExpectedFlipColor = 0xFFFFFF00; + DWORD PlayWindowColor = 0xFFFF00FF; + + int Top = PadY; + int Bottom = PadY + LineHeight; + if(MarkerIndex == CurrentMarkerIndex) + { + Top += LineHeight+PadY; + Bottom += LineHeight+PadY; + + int FirstTop = Top; + + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputPlayCursor, PlayColor); + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputWriteCursor, WriteColor); + + Top += LineHeight+PadY; + Bottom += LineHeight+PadY; + + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputLocation, PlayColor); + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->OutputLocation + ThisMarker->OutputByteCount, WriteColor); + + Top += LineHeight+PadY; + Bottom += LineHeight+PadY; + + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, FirstTop, Bottom, ThisMarker->ExpectedFlipPlayCursor, ExpectedFlipColor); + } + + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->FlipPlayCursor, PlayColor); + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->FlipPlayCursor + 480*SoundOutput->BytesPerSample, PlayWindowColor); + Win32DrawSoundBufferMarker(Backbuffer, SoundOutput, C, PadX, Top, Bottom, ThisMarker->FlipWriteCursor, WriteColor); + } +} + +#endif + +struct platform_work_queue_entry +{ + platform_work_queue_callback *Callback; + void *Data; +}; + +struct platform_work_queue +{ + uint32 volatile CompletionGoal; + uint32 volatile CompletionCount; + + uint32 volatile NextEntryToWrite; + uint32 volatile NextEntryToRead; + HANDLE SemaphoreHandle; + + platform_work_queue_entry Entries[256]; +}; + +internal void +Win32AddEntry(platform_work_queue *Queue, platform_work_queue_callback *Callback, void *Data) +{ + // TODO(casey): Switch to InterlockedCompareExchange eventually + // so that any thread can add? + uint32 NewNextEntryToWrite = (Queue->NextEntryToWrite + 1) % ArrayCount(Queue->Entries); + Assert(NewNextEntryToWrite != Queue->NextEntryToRead); + platform_work_queue_entry *Entry = Queue->Entries + Queue->NextEntryToWrite; + Entry->Callback = Callback; + Entry->Data = Data; + ++Queue->CompletionGoal; + _WriteBarrier(); + Queue->NextEntryToWrite = NewNextEntryToWrite; + ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0); +} + +internal bool32 +Win32DoNextWorkQueueEntry(platform_work_queue *Queue) +{ + bool32 WeShouldSleep = false; + + uint32 OriginalNextEntryToRead = Queue->NextEntryToRead; + uint32 NewNextEntryToRead = (OriginalNextEntryToRead + 1) % ArrayCount(Queue->Entries); + if(OriginalNextEntryToRead != Queue->NextEntryToWrite) + { + uint32 Index = InterlockedCompareExchange((LONG volatile *)&Queue->NextEntryToRead, + NewNextEntryToRead, + OriginalNextEntryToRead); + if(Index == OriginalNextEntryToRead) + { + platform_work_queue_entry Entry = Queue->Entries[Index]; + Entry.Callback(Queue, Entry.Data); + InterlockedIncrement((LONG volatile *)&Queue->CompletionCount); + } + } + else + { + WeShouldSleep = true; + } + + return(WeShouldSleep); +} + +internal void +Win32CompleteAllWork(platform_work_queue *Queue) +{ + while(Queue->CompletionGoal != Queue->CompletionCount) + { + Win32DoNextWorkQueueEntry(Queue); + } + + Queue->CompletionGoal = 0; + Queue->CompletionCount = 0; +} + +DWORD WINAPI +ThreadProc(LPVOID lpParameter) +{ + platform_work_queue *Queue = (platform_work_queue *)lpParameter; + + u32 TestThreadID = GetThreadID(); + Assert(TestThreadID == GetCurrentThreadId()); + + for(;;) + { + if(Win32DoNextWorkQueueEntry(Queue)) + { + WaitForSingleObjectEx(Queue->SemaphoreHandle, INFINITE, FALSE); + } + } + +// return(0); +} + +internal PLATFORM_WORK_QUEUE_CALLBACK(DoWorkerWork) +{ + char Buffer[256]; + wsprintf(Buffer, "Thread %u: %s\n", GetCurrentThreadId(), (char *)Data); + OutputDebugStringA(Buffer); +} + +internal void +Win32MakeQueue(platform_work_queue *Queue, uint32 ThreadCount) +{ + Queue->CompletionGoal = 0; + Queue->CompletionCount = 0; + + Queue->NextEntryToWrite = 0; + Queue->NextEntryToRead = 0; + + uint32 InitialCount = 0; + Queue->SemaphoreHandle = CreateSemaphoreEx(0, + InitialCount, + ThreadCount, + 0, 0, SEMAPHORE_ALL_ACCESS); + for(uint32 ThreadIndex = 0; + ThreadIndex < ThreadCount; + ++ThreadIndex) + { + DWORD ThreadID; + HANDLE ThreadHandle = CreateThread(0, 0, ThreadProc, Queue, 0, &ThreadID); + CloseHandle(ThreadHandle); + } +} + +struct win32_platform_file_handle +{ + HANDLE Win32Handle; +}; + +struct win32_platform_file_group +{ + HANDLE FindHandle; + WIN32_FIND_DATAW FindData; +}; + +internal PLATFORM_GET_ALL_FILE_OF_TYPE_BEGIN(Win32GetAllFilesOfTypeBegin) +{ + platform_file_group Result = {}; + + // TODO(casey): If we want, someday, make an actual arena used by Win32 + win32_platform_file_group *Win32FileGroup = (win32_platform_file_group *)VirtualAlloc( + 0, sizeof(win32_platform_file_group), + MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + Result.Platform = Win32FileGroup; + + wchar_t *WildCard = L"*.*"; + switch(Type) + { + case PlatformFileType_AssetFile: + { + WildCard = L"*.hha"; + } break; + + case PlatformFileType_SavedGameFile: + { + WildCard = L"*.hhs"; + } break; + + InvalidDefaultCase; + } + + Result.FileCount = 0; + + WIN32_FIND_DATAW FindData; + HANDLE FindHandle = FindFirstFileW(WildCard, &FindData); + while(FindHandle != INVALID_HANDLE_VALUE) + { + ++Result.FileCount; + + if(!FindNextFileW(FindHandle, &FindData)) + { + break; + } + } + FindClose(FindHandle); + + Win32FileGroup->FindHandle = FindFirstFileW(WildCard, &Win32FileGroup->FindData); + + return(Result); +} + +internal PLATFORM_GET_ALL_FILE_OF_TYPE_END(Win32GetAllFilesOfTypeEnd) +{ + win32_platform_file_group *Win32FileGroup = (win32_platform_file_group *)FileGroup->Platform; + if(Win32FileGroup) + { + FindClose(Win32FileGroup->FindHandle); + + VirtualFree(Win32FileGroup, 0, MEM_RELEASE); + } +} + +internal PLATFORM_OPEN_FILE(Win32OpenNextFile) +{ + win32_platform_file_group *Win32FileGroup = (win32_platform_file_group *)FileGroup->Platform; + platform_file_handle Result = {}; + + if(Win32FileGroup->FindHandle != INVALID_HANDLE_VALUE) + { + // TODO(casey): If we want, someday, make an actual arena used by Win32 + win32_platform_file_handle *Win32Handle = (win32_platform_file_handle *)VirtualAlloc( + 0, sizeof(win32_platform_file_handle), + MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + Result.Platform = Win32Handle; + + if(Win32Handle) + { + wchar_t *FileName = Win32FileGroup->FindData.cFileName; + Win32Handle->Win32Handle = CreateFileW(FileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + Result.NoErrors = (Win32Handle->Win32Handle != INVALID_HANDLE_VALUE); + } + + if(!FindNextFileW(Win32FileGroup->FindHandle, &Win32FileGroup->FindData)) + { + FindClose(Win32FileGroup->FindHandle); + Win32FileGroup->FindHandle = INVALID_HANDLE_VALUE; + } + } + + return(Result); +} + +internal PLATFORM_FILE_ERROR(Win32FileError) +{ +#if HANDMADE_INTERNAL + OutputDebugString("WIN32 FILE ERROR: "); + OutputDebugString(Message); + OutputDebugString("\n"); +#endif + + Handle->NoErrors = false; +} + +internal PLATFORM_READ_DATA_FROM_FILE(Win32ReadDataFromFile) +{ + if(PlatformNoFileErrors(Source)) + { + win32_platform_file_handle *Handle = (win32_platform_file_handle *)Source->Platform; + OVERLAPPED Overlapped = {}; + Overlapped.Offset = (u32)((Offset >> 0) & 0xFFFFFFFF); + Overlapped.OffsetHigh = (u32)((Offset >> 32) & 0xFFFFFFFF); + + uint32 FileSize32 = SafeTruncateUInt64(Size); + + DWORD BytesRead; + if(ReadFile(Handle->Win32Handle, Dest, FileSize32, &BytesRead, &Overlapped) && + (FileSize32 == BytesRead)) + { + // NOTE(casey): File read succeeded! + } + else + { + Win32FileError(Source, "Read file failed."); + } + } +} + +/* + +internal PLATFORM_FILE_ERROR(Win32CloseFile) +{ + CloseHandle(FileHandle); +} + +*/ + +PLATFORM_ALLOCATE_MEMORY(Win32AllocateMemory) +{ + void *Result = VirtualAlloc(0, Size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + + return(Result); +} + +PLATFORM_DEALLOCATE_MEMORY(Win32DeallocateMemory) +{ + if(Memory) + { + VirtualFree(Memory, 0, MEM_RELEASE); + } +} + +#if HANDMADE_INTERNAL +global_variable debug_table GlobalDebugTable_; +debug_table *GlobalDebugTable = &GlobalDebugTable_; +#endif + +int CALLBACK +WinMain(HINSTANCE Instance, + HINSTANCE PrevInstance, + LPSTR CommandLine, + int ShowCode) +{ + win32_state Win32State = {}; + + platform_work_queue HighPriorityQueue = {}; + Win32MakeQueue(&HighPriorityQueue, 6); + + platform_work_queue LowPriorityQueue = {}; + Win32MakeQueue(&LowPriorityQueue, 2); + +#if 0 + Win32AddEntry(&Queue, DoWorkerWork, "String A0"); + Win32AddEntry(&Queue, DoWorkerWork, "String A1"); + Win32AddEntry(&Queue, DoWorkerWork, "String A2"); + Win32AddEntry(&Queue, DoWorkerWork, "String A3"); + Win32AddEntry(&Queue, DoWorkerWork, "String A4"); + Win32AddEntry(&Queue, DoWorkerWork, "String A5"); + Win32AddEntry(&Queue, DoWorkerWork, "String A6"); + Win32AddEntry(&Queue, DoWorkerWork, "String A7"); + Win32AddEntry(&Queue, DoWorkerWork, "String A8"); + Win32AddEntry(&Queue, DoWorkerWork, "String A9"); + + Win32AddEntry(&Queue, DoWorkerWork, "String B0"); + Win32AddEntry(&Queue, DoWorkerWork, "String B1"); + Win32AddEntry(&Queue, DoWorkerWork, "String B2"); + Win32AddEntry(&Queue, DoWorkerWork, "String B3"); + Win32AddEntry(&Queue, DoWorkerWork, "String B4"); + Win32AddEntry(&Queue, DoWorkerWork, "String B5"); + Win32AddEntry(&Queue, DoWorkerWork, "String B6"); + Win32AddEntry(&Queue, DoWorkerWork, "String B7"); + Win32AddEntry(&Queue, DoWorkerWork, "String B8"); + Win32AddEntry(&Queue, DoWorkerWork, "String B9"); + + Win32CompleteAllWork(&Queue); +#endif + + LARGE_INTEGER PerfCountFrequencyResult; + QueryPerformanceFrequency(&PerfCountFrequencyResult); + GlobalPerfCountFrequency = PerfCountFrequencyResult.QuadPart; + + Win32GetEXEFileName(&Win32State); + + char SourceGameCodeDLLFullPath[WIN32_STATE_FILE_NAME_COUNT]; + Win32BuildEXEPathFileName(&Win32State, "handmade.dll", + sizeof(SourceGameCodeDLLFullPath), SourceGameCodeDLLFullPath); + + char TempGameCodeDLLFullPath[WIN32_STATE_FILE_NAME_COUNT]; + Win32BuildEXEPathFileName(&Win32State, "handmade_temp.dll", + sizeof(TempGameCodeDLLFullPath), TempGameCodeDLLFullPath); + + char GameCodeLockFullPath[WIN32_STATE_FILE_NAME_COUNT]; + Win32BuildEXEPathFileName(&Win32State, "lock.tmp", + sizeof(GameCodeLockFullPath), GameCodeLockFullPath); + + // NOTE(casey): Set the Windows scheduler granularity to 1ms + // so that our Sleep() can be more granular. + UINT DesiredSchedulerMS = 1; + bool32 SleepIsGranular = (timeBeginPeriod(DesiredSchedulerMS) == TIMERR_NOERROR); + + Win32LoadXInput(); + +#if HANDMADE_INTERNAL + DEBUGGlobalShowCursor = true; +#endif + WNDCLASSA WindowClass = {}; + + /* NOTE(casey): 1080p display mode is 1920x1080 -> Half of that is 960x540 + 1920 -> 2048 = 2048-1920 -> 128 pixels + 1080 -> 2048 = 2048-1080 -> pixels 968 + 1024 + 128 = 1152 + */ +// Win32ResizeDIBSection(&GlobalBackbuffer, 960, 540); + Win32ResizeDIBSection(&GlobalBackbuffer, 1920, 1080); +// Win32ResizeDIBSection(&GlobalBackbuffer, 1279, 719); + + WindowClass.style = CS_HREDRAW|CS_VREDRAW; + WindowClass.lpfnWndProc = Win32MainWindowCallback; + WindowClass.hInstance = Instance; + WindowClass.hCursor = LoadCursor(0, IDC_ARROW); +// WindowClass.hIcon; + WindowClass.lpszClassName = "HandmadeHeroWindowClass"; + + if(RegisterClassA(&WindowClass)) + { + HWND Window = + CreateWindowExA( + 0, // WS_EX_TOPMOST|WS_EX_LAYERED, + WindowClass.lpszClassName, + "Handmade Hero", + WS_OVERLAPPEDWINDOW|WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + 0, + 0, + Instance, + 0); + if(Window) + { + win32_sound_output SoundOutput = {}; + + // TODO(casey): How do we reliably query on this on Windows? + int MonitorRefreshHz = 60; + HDC RefreshDC = GetDC(Window); + int Win32RefreshRate = GetDeviceCaps(RefreshDC, VREFRESH); + ReleaseDC(Window, RefreshDC); + if(Win32RefreshRate > 1) + { + MonitorRefreshHz = Win32RefreshRate; + } + real32 GameUpdateHz = (real32)(MonitorRefreshHz / 2.0f); + real32 TargetSecondsPerFrame = 1.0f / (real32)GameUpdateHz; + + // TODO(casey): Make this like sixty seconds? + SoundOutput.SamplesPerSecond = 48000; + SoundOutput.BytesPerSample = sizeof(int16)*2; + SoundOutput.SecondaryBufferSize = SoundOutput.SamplesPerSecond*SoundOutput.BytesPerSample; + // TODO(casey): Actually compute this variance and see + // what the lowest reasonable value is. + SoundOutput.SafetyBytes = (int)(((real32)SoundOutput.SamplesPerSecond*(real32)SoundOutput.BytesPerSample / GameUpdateHz)/3.0f); + Win32InitDSound(Window, SoundOutput.SamplesPerSecond, SoundOutput.SecondaryBufferSize); + Win32ClearBuffer(&SoundOutput); + GlobalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); + + GlobalRunning = true; + +#if 0 + // NOTE(casey): This tests the PlayCursor/WriteCursor update frequency + // On the Handmade Hero machine, it was 480 samples. + while(GlobalRunning) + { + DWORD PlayCursor; + DWORD WriteCursor; + GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor); + + char TextBuffer[256]; + _snprintf_s(TextBuffer, sizeof(TextBuffer), + "PC:%u WC:%u\n", PlayCursor, WriteCursor); + OutputDebugStringA(TextBuffer); + } +#endif + + // TODO(casey): Pool with bitmap VirtualAlloc + // TODO(casey): Remove MaxPossibleOverrun? + u32 MaxPossibleOverrun = 2*8*sizeof(u16); + int16 *Samples = (int16 *)VirtualAlloc(0, SoundOutput.SecondaryBufferSize + MaxPossibleOverrun, + MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + + +#if HANDMADE_INTERNAL + LPVOID BaseAddress = (LPVOID)Terabytes(2); +#else + LPVOID BaseAddress = 0; +#endif + + game_memory GameMemory = {}; + GameMemory.PermanentStorageSize = Megabytes(256); + GameMemory.TransientStorageSize = Gigabytes(1); + GameMemory.DebugStorageSize = Megabytes(64); + GameMemory.HighPriorityQueue = &HighPriorityQueue; + GameMemory.LowPriorityQueue = &LowPriorityQueue; + GameMemory.PlatformAPI.AddEntry = Win32AddEntry; + GameMemory.PlatformAPI.CompleteAllWork = Win32CompleteAllWork; + + GameMemory.PlatformAPI.GetAllFilesOfTypeBegin = Win32GetAllFilesOfTypeBegin; + GameMemory.PlatformAPI.GetAllFilesOfTypeEnd = Win32GetAllFilesOfTypeEnd; + GameMemory.PlatformAPI.OpenNextFile = Win32OpenNextFile; + GameMemory.PlatformAPI.ReadDataFromFile = Win32ReadDataFromFile; + GameMemory.PlatformAPI.FileError = Win32FileError; + + GameMemory.PlatformAPI.AllocateMemory = Win32AllocateMemory; + GameMemory.PlatformAPI.DeallocateMemory = Win32DeallocateMemory; + +#if HANDMADE_INTERNAL + GameMemory.PlatformAPI.DEBUGFreeFileMemory = DEBUGPlatformFreeFileMemory; + GameMemory.PlatformAPI.DEBUGReadEntireFile = DEBUGPlatformReadEntireFile; + GameMemory.PlatformAPI.DEBUGWriteEntireFile = DEBUGPlatformWriteEntireFile; + GameMemory.PlatformAPI.DEBUGExecuteSystemCommand = DEBUGExecuteSystemCommand; + GameMemory.PlatformAPI.DEBUGGetProcessState = DEBUGGetProcessState; +#endif + + // TODO(casey): Handle various memory footprints (USING + // SYSTEM METRICS) + + // TODO(casey): Use MEM_LARGE_PAGES and + // call adjust token privileges when not on Windows XP? + + // TODO(casey): TransientStorage needs to be broken up + // into game transient and cache transient, and only the + // former need be saved for state playback. + Win32State.TotalSize = (GameMemory.PermanentStorageSize + + GameMemory.TransientStorageSize + + GameMemory.DebugStorageSize); + Win32State.GameMemoryBlock = VirtualAlloc(BaseAddress, (size_t)Win32State.TotalSize, + MEM_RESERVE|MEM_COMMIT, + PAGE_READWRITE); + GameMemory.PermanentStorage = Win32State.GameMemoryBlock; + GameMemory.TransientStorage = ((uint8 *)GameMemory.PermanentStorage + + GameMemory.PermanentStorageSize); + GameMemory.DebugStorage = ((u8 *)GameMemory.TransientStorage + + GameMemory.TransientStorageSize); + + for(int ReplayIndex = 1; + ReplayIndex < ArrayCount(Win32State.ReplayBuffers); + ++ReplayIndex) + { + win32_replay_buffer *ReplayBuffer = &Win32State.ReplayBuffers[ReplayIndex]; + + // TODO(casey): Recording system still seems to take too long + // on record start - find out what Windows is doing and if + // we can speed up / defer some of that processing. + + Win32GetInputFileLocation(&Win32State, false, ReplayIndex, + sizeof(ReplayBuffer->FileName), ReplayBuffer->FileName); + + ReplayBuffer->FileHandle = + CreateFileA(ReplayBuffer->FileName, + GENERIC_WRITE|GENERIC_READ, 0, 0, CREATE_ALWAYS, 0, 0); + + LARGE_INTEGER MaxSize; + MaxSize.QuadPart = Win32State.TotalSize; + ReplayBuffer->MemoryMap = CreateFileMapping( + ReplayBuffer->FileHandle, 0, PAGE_READWRITE, + MaxSize.HighPart, MaxSize.LowPart, 0); + + ReplayBuffer->MemoryBlock = MapViewOfFile( + ReplayBuffer->MemoryMap, FILE_MAP_ALL_ACCESS, 0, 0, Win32State.TotalSize); + if(ReplayBuffer->MemoryBlock) + { + } + else + { + // TODO(casey): Diagnostic + } + } + + if(Samples && GameMemory.PermanentStorage && GameMemory.TransientStorage) + { + game_input Input[2] = {}; + game_input *NewInput = &Input[0]; + game_input *OldInput = &Input[1]; + + LARGE_INTEGER LastCounter = Win32GetWallClock(); + LARGE_INTEGER FlipWallClock = Win32GetWallClock(); + + int DebugTimeMarkerIndex = 0; + win32_debug_time_marker DebugTimeMarkers[30] = {0}; + + DWORD AudioLatencyBytes = 0; + real32 AudioLatencySeconds = 0; + bool32 SoundIsValid = false; + + win32_game_code Game = Win32LoadGameCode(SourceGameCodeDLLFullPath, + TempGameCodeDLLFullPath, + GameCodeLockFullPath); + while(GlobalRunning) + { + // + // + // + + BEGIN_BLOCK(ExecutableRefresh); + NewInput->dtForFrame = TargetSecondsPerFrame; + + GameMemory.ExecutableReloaded = false; + FILETIME NewDLLWriteTime = Win32GetLastWriteTime(SourceGameCodeDLLFullPath); + if(CompareFileTime(&NewDLLWriteTime, &Game.DLLLastWriteTime) != 0) + { + Win32CompleteAllWork(&HighPriorityQueue); + Win32CompleteAllWork(&LowPriorityQueue); + +#if HANDMADE_INTERNAL + GlobalDebugTable = &GlobalDebugTable_; +#endif + Win32UnloadGameCode(&Game); + Game = Win32LoadGameCode(SourceGameCodeDLLFullPath, + TempGameCodeDLLFullPath, + GameCodeLockFullPath); + GameMemory.ExecutableReloaded = true; + } + END_BLOCK(ExecutableRefresh); + + // + // + // + + BEGIN_BLOCK(InputProcessing); + + // TODO(casey): Zeroing macro + // TODO(casey): We can't zero everything because the up/down state will + // be wrong!!! + game_controller_input *OldKeyboardController = GetController(OldInput, 0); + game_controller_input *NewKeyboardController = GetController(NewInput, 0); + *NewKeyboardController = {}; + NewKeyboardController->IsConnected = true; + for(int ButtonIndex = 0; + ButtonIndex < ArrayCount(NewKeyboardController->Buttons); + ++ButtonIndex) + { + NewKeyboardController->Buttons[ButtonIndex].EndedDown = + OldKeyboardController->Buttons[ButtonIndex].EndedDown; + } + + Win32ProcessPendingMessages(&Win32State, NewKeyboardController); + + if(!GlobalPause) + { + POINT MouseP; + GetCursorPos(&MouseP); + ScreenToClient(Window, &MouseP); + NewInput->MouseX = (r32)MouseP.x; + NewInput->MouseY = (r32)((GlobalBackbuffer.Height - 1) - MouseP.y); + NewInput->MouseZ = 0; // TODO(casey): Support mousewheel? + + NewInput->ShiftDown = (GetKeyState(VK_SHIFT) & (1 << 15)); + NewInput->AltDown = (GetKeyState(VK_MENU) & (1 << 15)); + NewInput->ControlDown = (GetKeyState(VK_CONTROL) & (1 << 15)); + + DWORD WinButtonID[PlatformMouseButton_Count] = + { + VK_LBUTTON, + VK_MBUTTON, + VK_RBUTTON, + VK_XBUTTON1, + VK_XBUTTON2, + }; + for(u32 ButtonIndex = 0; + ButtonIndex < PlatformMouseButton_Count; + ++ButtonIndex) + { + NewInput->MouseButtons[ButtonIndex] = OldInput->MouseButtons[ButtonIndex]; + NewInput->MouseButtons[ButtonIndex].HalfTransitionCount = 0; + Win32ProcessKeyboardMessage(&NewInput->MouseButtons[ButtonIndex], + GetKeyState(WinButtonID[ButtonIndex]) & (1 << 15)); + } + + // TODO(casey): Need to not poll disconnected controllers to avoid + // xinput frame rate hit on older libraries... + // TODO(casey): Should we poll this more frequently + DWORD MaxControllerCount = XUSER_MAX_COUNT; + if(MaxControllerCount > (ArrayCount(NewInput->Controllers) - 1)) + { + MaxControllerCount = (ArrayCount(NewInput->Controllers) - 1); + } + + for (DWORD ControllerIndex = 0; + ControllerIndex < MaxControllerCount; + ++ControllerIndex) + { + DWORD OurControllerIndex = ControllerIndex + 1; + game_controller_input *OldController = GetController(OldInput, OurControllerIndex); + game_controller_input *NewController = GetController(NewInput, OurControllerIndex); + + XINPUT_STATE ControllerState; + if(XInputGetState(ControllerIndex, &ControllerState) == ERROR_SUCCESS) + { + NewController->IsConnected = true; + NewController->IsAnalog = OldController->IsAnalog; + + // NOTE(casey): This controller is plugged in + // TODO(casey): See if ControllerState.dwPacketNumber increments too rapidly + XINPUT_GAMEPAD *Pad = &ControllerState.Gamepad; + + // TODO(casey): This is a square deadzone, check XInput to + // verify that the deadzone is "round" and show how to do + // round deadzone processing. + NewController->StickAverageX = Win32ProcessXInputStickValue( + Pad->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + NewController->StickAverageY = Win32ProcessXInputStickValue( + Pad->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + if((NewController->StickAverageX != 0.0f) || + (NewController->StickAverageY != 0.0f)) + { + NewController->IsAnalog = true; + } + + if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_UP) + { + NewController->StickAverageY = 1.0f; + NewController->IsAnalog = false; + } + + if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + { + NewController->StickAverageY = -1.0f; + NewController->IsAnalog = false; + } + + if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + { + NewController->StickAverageX = -1.0f; + NewController->IsAnalog = false; + } + + if(Pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + { + NewController->StickAverageX = 1.0f; + NewController->IsAnalog = false; + } + + real32 Threshold = 0.5f; + Win32ProcessXInputDigitalButton( + (NewController->StickAverageX < -Threshold) ? 1 : 0, + &OldController->MoveLeft, 1, + &NewController->MoveLeft); + Win32ProcessXInputDigitalButton( + (NewController->StickAverageX > Threshold) ? 1 : 0, + &OldController->MoveRight, 1, + &NewController->MoveRight); + Win32ProcessXInputDigitalButton( + (NewController->StickAverageY < -Threshold) ? 1 : 0, + &OldController->MoveDown, 1, + &NewController->MoveDown); + Win32ProcessXInputDigitalButton( + (NewController->StickAverageY > Threshold) ? 1 : 0, + &OldController->MoveUp, 1, + &NewController->MoveUp); + + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->ActionDown, XINPUT_GAMEPAD_A, + &NewController->ActionDown); + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->ActionRight, XINPUT_GAMEPAD_B, + &NewController->ActionRight); + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->ActionLeft, XINPUT_GAMEPAD_X, + &NewController->ActionLeft); + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->ActionUp, XINPUT_GAMEPAD_Y, + &NewController->ActionUp); + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->LeftShoulder, XINPUT_GAMEPAD_LEFT_SHOULDER, + &NewController->LeftShoulder); + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->RightShoulder, XINPUT_GAMEPAD_RIGHT_SHOULDER, + &NewController->RightShoulder); + + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->Start, XINPUT_GAMEPAD_START, + &NewController->Start); + Win32ProcessXInputDigitalButton(Pad->wButtons, + &OldController->Back, XINPUT_GAMEPAD_BACK, + &NewController->Back); + } + else + { + // NOTE(casey): The controller is not available + NewController->IsConnected = false; + } + } + } + END_BLOCK(InputProcessing); + + // + // + // + + BEGIN_BLOCK(GameUpdate); + + game_offscreen_buffer Buffer = {}; + Buffer.Memory = GlobalBackbuffer.Memory; + Buffer.Width = GlobalBackbuffer.Width; + Buffer.Height = GlobalBackbuffer.Height; + Buffer.Pitch = GlobalBackbuffer.Pitch; + if(!GlobalPause) + { + if(Win32State.InputRecordingIndex) + { + Win32RecordInput(&Win32State, NewInput); + } + + if(Win32State.InputPlayingIndex) + { + game_input Temp = *NewInput; + Win32PlayBackInput(&Win32State, NewInput); + for(u32 MouseButtonIndex = 0; + MouseButtonIndex < PlatformMouseButton_Count; + ++MouseButtonIndex) + { + NewInput->MouseButtons[MouseButtonIndex] = Temp.MouseButtons[MouseButtonIndex]; + } + NewInput->MouseX = Temp.MouseX; + NewInput->MouseY = Temp.MouseY; + NewInput->MouseZ = Temp.MouseZ; + } + if(Game.UpdateAndRender) + { + Game.UpdateAndRender(&GameMemory, NewInput, &Buffer); +// HandleDebugCycleCounters(&GameMemory); + } + } + + END_BLOCK(GameUpdate); + + // + // + // + + BEGIN_BLOCK(AudioUpdate); + + if(!GlobalPause) + { + LARGE_INTEGER AudioWallClock = Win32GetWallClock(); + real32 FromBeginToAudioSeconds = Win32GetSecondsElapsed(FlipWallClock, AudioWallClock); + + DWORD PlayCursor; + DWORD WriteCursor; + if(GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor) == DS_OK) + { + /* NOTE(casey): + + Here is how sound output computation works. + + We define a safety value that is the number + of samples we think our game update loop + may vary by (let's say up to 2ms) + + When we wake up to write audio, we will look + and see what the play cursor position is and we + will forecast ahead where we think the play + cursor will be on the next frame boundary. + + We will then look to see if the write cursor is + before that by at least our safety value. If + it is, the target fill position is that frame + boundary plus one frame. This gives us perfect + audio sync in the case of a card that has low + enough latency. + + If the write cursor is _after_ that safety + margin, then we assume we can never sync the + audio perfectly, so we will write one frame's + worth of audio plus the safety margin's worth + of guard samples. + */ + if(!SoundIsValid) + { + SoundOutput.RunningSampleIndex = WriteCursor / SoundOutput.BytesPerSample; + SoundIsValid = true; + } + + DWORD ByteToLock = ((SoundOutput.RunningSampleIndex*SoundOutput.BytesPerSample) % + SoundOutput.SecondaryBufferSize); + + DWORD ExpectedSoundBytesPerFrame = + (int)((real32)(SoundOutput.SamplesPerSecond*SoundOutput.BytesPerSample) / + GameUpdateHz); + real32 SecondsLeftUntilFlip = (TargetSecondsPerFrame - FromBeginToAudioSeconds); + DWORD ExpectedBytesUntilFlip = (DWORD)((SecondsLeftUntilFlip/TargetSecondsPerFrame)*(real32)ExpectedSoundBytesPerFrame); + + DWORD ExpectedFrameBoundaryByte = PlayCursor + ExpectedBytesUntilFlip; + + DWORD SafeWriteCursor = WriteCursor; + if(SafeWriteCursor < PlayCursor) + { + SafeWriteCursor += SoundOutput.SecondaryBufferSize; + } + Assert(SafeWriteCursor >= PlayCursor); + SafeWriteCursor += SoundOutput.SafetyBytes; + + bool32 AudioCardIsLowLatency = (SafeWriteCursor < ExpectedFrameBoundaryByte); + + DWORD TargetCursor = 0; + if(AudioCardIsLowLatency) + { + TargetCursor = (ExpectedFrameBoundaryByte + ExpectedSoundBytesPerFrame); + } + else + { + TargetCursor = (WriteCursor + ExpectedSoundBytesPerFrame + + SoundOutput.SafetyBytes); + } + TargetCursor = (TargetCursor % SoundOutput.SecondaryBufferSize); + + DWORD BytesToWrite = 0; + if(ByteToLock > TargetCursor) + { + BytesToWrite = (SoundOutput.SecondaryBufferSize - ByteToLock); + BytesToWrite += TargetCursor; + } + else + { + BytesToWrite = TargetCursor - ByteToLock; + } + + game_sound_output_buffer SoundBuffer = {}; + SoundBuffer.SamplesPerSecond = SoundOutput.SamplesPerSecond; + SoundBuffer.SampleCount = Align8(BytesToWrite / SoundOutput.BytesPerSample); + BytesToWrite = SoundBuffer.SampleCount*SoundOutput.BytesPerSample; + SoundBuffer.Samples = Samples; + if(Game.GetSoundSamples) + { + Game.GetSoundSamples(&GameMemory, &SoundBuffer); + } + +#if HANDMADE_INTERNAL + win32_debug_time_marker *Marker = &DebugTimeMarkers[DebugTimeMarkerIndex]; + Marker->OutputPlayCursor = PlayCursor; + Marker->OutputWriteCursor = WriteCursor; + Marker->OutputLocation = ByteToLock; + Marker->OutputByteCount = BytesToWrite; + Marker->ExpectedFlipPlayCursor = ExpectedFrameBoundaryByte; + + DWORD UnwrappedWriteCursor = WriteCursor; + if(UnwrappedWriteCursor < PlayCursor) + { + UnwrappedWriteCursor += SoundOutput.SecondaryBufferSize; + } + AudioLatencyBytes = UnwrappedWriteCursor - PlayCursor; + AudioLatencySeconds = + (((real32)AudioLatencyBytes / (real32)SoundOutput.BytesPerSample) / + (real32)SoundOutput.SamplesPerSecond); + +#if 0 + char TextBuffer[256]; + _snprintf_s(TextBuffer, sizeof(TextBuffer), + "BTL:%u TC:%u BTW:%u - PC:%u WC:%u DELTA:%u (%fs)\n", + ByteToLock, TargetCursor, BytesToWrite, + PlayCursor, WriteCursor, AudioLatencyBytes, AudioLatencySeconds); + OutputDebugStringA(TextBuffer); +#endif +#endif + Win32FillSoundBuffer(&SoundOutput, ByteToLock, BytesToWrite, &SoundBuffer); + } + else + { + SoundIsValid = false; + } + } + + END_BLOCK(AudioUpdate); + + // + // + // + +#if HANDMADE_INTERNAL + BEGIN_BLOCK(DebugCollation); + + if(Game.DEBUGFrameEnd) + { + GlobalDebugTable = Game.DEBUGFrameEnd(&GameMemory, NewInput, &Buffer); + } + GlobalDebugTable_.EventArrayIndex_EventIndex = 0; + + END_BLOCK(DebugCollation); +#endif + + // + // + // + + // TODO(casey): Leave this off until we have actual vblank support? +#if 0 + BEGIN_BLOCK(FramerateWait); + + if(!GlobalPause) + { + LARGE_INTEGER WorkCounter = Win32GetWallClock(); + real32 WorkSecondsElapsed = Win32GetSecondsElapsed(LastCounter, WorkCounter); + + // TODO(casey): NOT TESTED YET! PROBABLY BUGGY!!!!! + real32 SecondsElapsedForFrame = WorkSecondsElapsed; + if(SecondsElapsedForFrame < TargetSecondsPerFrame) + { + if(SleepIsGranular) + { + DWORD SleepMS = (DWORD)(1000.0f * (TargetSecondsPerFrame - + SecondsElapsedForFrame)); + if(SleepMS > 0) + { + Sleep(SleepMS); + } + } + + real32 TestSecondsElapsedForFrame = Win32GetSecondsElapsed(LastCounter, + Win32GetWallClock()); + if(TestSecondsElapsedForFrame < TargetSecondsPerFrame) + { + // TODO(casey): LOG MISSED SLEEP HERE + } + + while(SecondsElapsedForFrame < TargetSecondsPerFrame) + { + SecondsElapsedForFrame = Win32GetSecondsElapsed(LastCounter, + Win32GetWallClock()); + } + } + else + { + // TODO(casey): MISSED FRAME RATE! + // TODO(casey): Logging + } + } + + END_BLOCK(FramerateWait); +#endif + + // + // + // + + BEGIN_BLOCK(FrameDisplay); + + win32_window_dimension Dimension = Win32GetWindowDimension(Window); + HDC DeviceContext = GetDC(Window); + Win32DisplayBufferInWindow(&GlobalBackbuffer, DeviceContext, + Dimension.Width, Dimension.Height); + ReleaseDC(Window, DeviceContext); + + FlipWallClock = Win32GetWallClock(); + + game_input *Temp = NewInput; + NewInput = OldInput; + OldInput = Temp; + // TODO(casey): Should I clear these here? + + END_BLOCK(FrameDisplay); + + LARGE_INTEGER EndCounter = Win32GetWallClock(); + FRAME_MARKER(Win32GetSecondsElapsed(LastCounter, EndCounter)); + LastCounter = EndCounter; + } + } + else + { + // TODO(casey): Logging + } + } + else + { + // TODO(casey): Logging + } + } + else + { + // TODO(casey): Logging + } + + return(0); +} diff --git a/test_data/lots_of_files/win32_handmade.h b/test_data/lots_of_files/win32_handmade.h new file mode 100644 index 0000000..4644688 --- /dev/null +++ b/test_data/lots_of_files/win32_handmade.h @@ -0,0 +1,90 @@ +#if !defined(WIN32_HANDMADE_H) +/* ======================================================================== + $File: $ + $Date: $ + $Revision: $ + $Creator: Casey Muratori $ + $Notice: (C) Copyright 2014 by Molly Rocket, Inc. All Rights Reserved. $ + ======================================================================== */ + +struct win32_offscreen_buffer +{ + // NOTE(casey): Pixels are alwasy 32-bits wide, Memory Order BB GG RR XX + BITMAPINFO Info; + void *Memory; + int Width; + int Height; + int Pitch; + int BytesPerPixel; +}; + +struct win32_window_dimension +{ + int Width; + int Height; +}; + +struct win32_sound_output +{ + int SamplesPerSecond; + uint32 RunningSampleIndex; + int BytesPerSample; + DWORD SecondaryBufferSize; + DWORD SafetyBytes; + + // TODO(casey): Should running sample index be in bytes as well + // TODO(casey): Math gets simpler if we add a "bytes per second" field? +}; + +struct win32_debug_time_marker +{ + DWORD OutputPlayCursor; + DWORD OutputWriteCursor; + DWORD OutputLocation; + DWORD OutputByteCount; + DWORD ExpectedFlipPlayCursor; + + DWORD FlipPlayCursor; + DWORD FlipWriteCursor; +}; + +struct win32_game_code +{ + HMODULE GameCodeDLL; + FILETIME DLLLastWriteTime; + + // IMPORTANT(casey): Either of the callbacks can be 0! You must + // check before calling. + game_update_and_render *UpdateAndRender; + game_get_sound_samples *GetSoundSamples; + debug_game_frame_end *DEBUGFrameEnd; + + bool32 IsValid; +}; + +#define WIN32_STATE_FILE_NAME_COUNT MAX_PATH +struct win32_replay_buffer +{ + HANDLE FileHandle; + HANDLE MemoryMap; + char FileName[WIN32_STATE_FILE_NAME_COUNT]; + void *MemoryBlock; +}; +struct win32_state +{ + uint64 TotalSize; + void *GameMemoryBlock; + win32_replay_buffer ReplayBuffers[4]; + + HANDLE RecordingHandle; + int InputRecordingIndex; + + HANDLE PlaybackHandle; + int InputPlayingIndex; + + char EXEFileName[WIN32_STATE_FILE_NAME_COUNT]; + char *OnePastLastEXEFileNameSlash; +}; + +#define WIN32_HANDMADE_H +#endif diff --git a/test_data/lots_of_files/wmmintrin.h b/test_data/lots_of_files/wmmintrin.h new file mode 100644 index 0000000..68137e5 --- /dev/null +++ b/test_data/lots_of_files/wmmintrin.h @@ -0,0 +1,89 @@ +/*** +*** Copyright (C) 1985-2008 Intel Corporation. All rights reserved. +*** +*** The information and source code contained herein is the exclusive +*** property of Intel Corporation and may not be disclosed, examined +*** or reproduced in whole or in part without explicit written authorization +*** from the company. +*** +****/ + +/* + * wmmintrin.h + * + * Principal header file for Intel(R) AES and PCLMULQDQ intrinsics. + */ + +#pragma once +#ifndef __midl +#ifndef _INCLUDED_WMM +#define _INCLUDED_WMM + +#if defined (_M_CEE_PURE) + #error ERROR: EMM intrinsics not supported in the pure mode! +#else /* defined (_M_CEE_PURE) */ + +#include <nmmintrin.h> + + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Performs 1 round of AES decryption of the first m128i using + * the second m128i as a round key. + */ +extern __m128i _mm_aesdec_si128(__m128i v, __m128i rkey); + +/* + * Performs the last round of AES decryption of the first m128i + * using the second m128i as a round key. + */ +extern __m128i _mm_aesdeclast_si128(__m128i v, __m128i rkey); + +/* + * Performs 1 round of AES encryption of the first m128i using + * the second m128i as a round key. + */ +extern __m128i _mm_aesenc_si128(__m128i v, __m128i rkey); + +/* + * Performs the last round of AES encryption of the first m128i + * using the second m128i as a round key. + */ +extern __m128i _mm_aesenclast_si128(__m128i v, __m128i rkey); + +/* + * Performs the InverseMixColumn operation on the source m128i + * and stores the result into m128i destination. + */ +extern __m128i _mm_aesimc_si128(__m128i v); + +/* + * Generates a m128i round key for the input m128i + * AES cipher key and byte round constant. + * The second parameter must be a compile time constant. + */ +extern __m128i _mm_aeskeygenassist_si128(__m128i ckey, const int rcon); + +/* + * Performs carry-less integer multiplication of 64-bit halves + * of 128-bit input operands. + * The third parameter inducates which 64-bit haves of the input parameters + * v1 and v2 should be used. It must be a compile time constant. + */ +extern __m128i _mm_clmulepi64_si128(__m128i v1, __m128i v2, + const int imm8); + + + + +#if defined __cplusplus +}; /* End "C" */ +#endif /* defined __cplusplus */ + +#endif /* defined (_M_CEE_PURE) */ + +#endif /* _INCLUDED_WMM */ +#endif /* __midl */ diff --git a/test_data/lots_of_files/xatomic.h b/test_data/lots_of_files/xatomic.h new file mode 100644 index 0000000..879b69f --- /dev/null +++ b/test_data/lots_of_files/xatomic.h @@ -0,0 +1,2567 @@ +/* xatomic.h internal header */ +#pragma once +#ifndef _XATOMIC_H +#define _XATOMIC_H +#ifndef RC_INVOKED +#include <xatomic0.h> +#include <stddef.h> // for size_t +#include <stdlib.h> +#include <string.h> + +#include <intrin.h> +#include <xutility> + + #pragma pack(push,_CRT_PACKING) + #pragma warning(push,3) + #pragma push_macro("new") + #undef new + + /* Defend inline assembler from iso646.h's macros. */ + #pragma push_macro("and") + #pragma push_macro("or") + #pragma push_macro("xor") + #undef and + #undef or + #undef xor + + #pragma warning (disable: 4100 4390 4793 6326) + + #define _Compiler_barrier() _ReadWriteBarrier() + + #if defined(_M_ARM) + #define _Memory_barrier() __dmb(_ARM_BARRIER_ISH) + #endif /* defined(_M_ARM) */ + + #ifndef _CONCAT + #define _CONCATX(x, y) x ## y + #define _CONCAT(x, y) _CONCATX(x, y) + #endif /* _CONCAT */ + +#define ATOMIC_BOOL_LOCK_FREE \ + (1 <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_CHAR_LOCK_FREE \ + (1 <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_CHAR16_T_LOCK_FREE \ + (2 <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_CHAR32_T_LOCK_FREE \ + (2 <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_WCHAR_T_LOCK_FREE \ + (_WCHAR_T_SIZE <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_SHORT_LOCK_FREE \ + (_SHORT_SIZE <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_INT_LOCK_FREE \ + (_INT_SIZE <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_LONG_LOCK_FREE \ + (_LONG_SIZE <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define _ATOMIC_LLONG_LOCK_FREE \ + (_LONGLONG_SIZE <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) +#define ATOMIC_POINTER_LOCK_FREE \ + (_ADDR_SIZE <= _ATOMIC_MAXBYTES_LOCK_FREE ? 2 : 0) + + /* Interlocked intrinsic mapping for _nf/_acq/_rel */ + + #if defined(_M_ARM) + #define _INTRIN_RELAXED(x) _CONCAT(x, _nf) + #define _INTRIN_ACQUIRE(x) _CONCAT(x, _acq) + #define _INTRIN_RELEASE(x) _CONCAT(x, _rel) + #define _INTRIN_SEQ_CST(x) x + + #else /* defined(_M_ARM) */ + #define _INTRIN_RELAXED(x) x + #define _INTRIN_ACQUIRE(x) x + #define _INTRIN_RELEASE(x) x + #define _INTRIN_SEQ_CST(x) x + #endif /* defined(_M_ARM) */ + + #if defined(_M_IX86) +#pragma push_macro("_InterlockedExchange64") +#pragma push_macro("_InterlockedExchangeAdd64") +#pragma push_macro("_InterlockedAnd64") +#pragma push_macro("_InterlockedOr64") +#pragma push_macro("_InterlockedXor64") + +#undef _InterlockedExchange64 +#undef _InterlockedExchangeAdd64 +#undef _InterlockedAnd64 +#undef _InterlockedOr64 +#undef _InterlockedXor64 + +#define _InterlockedExchange64 _InterlockedExchange64_ASM +#define _InterlockedExchangeAdd64 _InterlockedExchangeAdd64_ASM +#define _InterlockedAnd64 _InterlockedAnd64_ASM +#define _InterlockedOr64 _InterlockedOr64_ASM +#define _InterlockedXor64 _InterlockedXor64_ASM + +inline _LONGLONG _InterlockedExchange64(volatile _LONGLONG * _Tgt, _LONGLONG _Value) +{ + _Compiler_barrier(); + __asm + { + mov esi, _Tgt; + mov ecx, dword ptr _Value[4]; + mov ebx, dword ptr _Value; + again: + lock cmpxchg8b [esi]; + jnz again; + mov dword ptr _Value[4], edx; + mov dword ptr _Value, eax; + } + _Compiler_barrier(); + + return (_Value); +} + +inline _LONGLONG _InterlockedExchangeAdd64(volatile _LONGLONG * _Tgt, _LONGLONG _Value) +{ + _Compiler_barrier(); + __asm + { + mov esi, _Tgt; + mov edx, 4[esi]; + mov eax, [esi]; + again: + mov ecx, edx; + mov ebx, eax; + add ebx, dword ptr _Value; + adc ecx, dword ptr _Value[4]; + lock cmpxchg8b [esi]; + jnz again; + mov dword ptr _Value, eax; + mov dword ptr _Value[4], edx; + } + _Compiler_barrier(); + + return (_Value); +} + +inline _LONGLONG _InterlockedAnd64(volatile _LONGLONG *_Tgt, _LONGLONG _Value) +{ + _Compiler_barrier(); + __asm + { + mov esi, _Tgt; + mov eax, [esi]; + mov edx, 4[esi]; + again: + mov ecx, edx; + mov ebx, eax; + and ebx, dword ptr _Value; + and ecx, dword ptr _Value[4]; + lock cmpxchg8b [esi]; + jnz again; + mov dword ptr _Value, eax; + mov dword ptr _Value[4], edx; + } + _Compiler_barrier(); + + return (_Value); +} + +inline _LONGLONG _InterlockedOr64(volatile _LONGLONG *_Tgt, _LONGLONG _Value) +{ + _Compiler_barrier(); + __asm + { + mov esi, _Tgt; + mov eax, [esi]; + mov edx, 4[esi]; + again: + mov ecx, edx; + mov ebx, eax; + or ebx, dword ptr _Value; + or ecx, dword ptr _Value[4]; + lock cmpxchg8b [esi]; + jnz again; + mov dword ptr _Value, eax; + mov dword ptr _Value[4], edx; + } + _Compiler_barrier(); + + return (_Value); +} + +inline _LONGLONG _InterlockedXor64(volatile _LONGLONG *_Tgt, _LONGLONG _Value) +{ + _Compiler_barrier(); + __asm + { + mov esi, _Tgt; + mov eax, [esi]; + mov edx, 4[esi]; + again: + mov ecx, edx; + mov ebx, eax; + xor ebx, dword ptr _Value; + xor ecx, dword ptr _Value[4]; + lock cmpxchg8b [esi]; + jnz again; + mov dword ptr _Value, eax; + mov dword ptr _Value[4], edx; + } + _Compiler_barrier(); + + return (_Value); +} + #endif /* defined(_M_IX86) */ + +_STD_BEGIN + /* TYPEDEFS FOR INTERNAL ARITHMETIC TYPES */ +typedef unsigned char _Uint1_t; +typedef unsigned short _Uint2_t; +//typedef _Uint32t _Uint4_t; +typedef unsigned _LONGLONG _Uint8_t; + + #define _ATOMIC_FLAG_TEST_AND_SET _Atomic_flag_test_and_set + #define _ATOMIC_FLAG_CLEAR _Atomic_flag_clear + + #define _ATOMIC_THREAD_FENCE _Atomic_thread_fence + #define _ATOMIC_SIGNAL_FENCE _Atomic_signal_fence + + #ifndef _INVALID_MEMORY_ORDER + + #if _ITERATOR_DEBUG_LEVEL == 2 + #define _INVALID_MEMORY_ORDER \ + {_DEBUG_ERROR("Invalid memory_order"); \ + _SCL_SECURE_INVALID_ARGUMENT} + + #elif _ITERATOR_DEBUG_LEVEL == 1 + #define _INVALID_MEMORY_ORDER \ + _SCL_SECURE_VALIDATE("Invalid memory_order" && 0) + + #elif _ITERATOR_DEBUG_LEVEL == 0 + #define _INVALID_MEMORY_ORDER + #endif /* _ITERATOR_DEBUG_LEVEL */ + #endif /* _INVALID_MEMORY_ORDER */ + +inline memory_order _Memory_order_upper_bound(memory_order _Order1, + memory_order _Order2) + { /* find upper bound of two memory orders, + based on the following partial order: + + seq_cst + | + acq_rel + / \ + acquire release + | | + consume | + \ / + relaxed + + */ + + static const memory_order _Upper[6][6] = { /* combined upper bounds */ + { memory_order_relaxed, memory_order_consume, memory_order_acquire, + memory_order_release, memory_order_acq_rel, memory_order_seq_cst }, + { memory_order_consume, memory_order_consume, memory_order_acquire, + memory_order_acq_rel, memory_order_acq_rel, memory_order_seq_cst }, + { memory_order_acquire, memory_order_acquire, memory_order_acquire, + memory_order_acq_rel, memory_order_acq_rel, memory_order_seq_cst }, + { memory_order_release, memory_order_acq_rel, memory_order_acq_rel, + memory_order_release, memory_order_acq_rel, memory_order_seq_cst }, + { memory_order_acq_rel, memory_order_acq_rel, memory_order_acq_rel, + memory_order_acq_rel, memory_order_acq_rel, memory_order_seq_cst }, + { memory_order_seq_cst, memory_order_seq_cst, memory_order_seq_cst, + memory_order_seq_cst, memory_order_seq_cst, memory_order_seq_cst + } + }; + + if ((_Order1 < 0) || (6 <= _Order1) + || (_Order2 < 0) || (6 <= _Order2)) + { /* launder memory order */ + _INVALID_MEMORY_ORDER; + return (memory_order_seq_cst); + } + return (_Upper[_Order1][_Order2]); + } + +inline void _Validate_compare_exchange_memory_order( + memory_order _Success, memory_order _Failure) + { /* validate success/failure */ + /* _Failure may not be memory_order_release or memory_order_acq_rel + and may not be stronger than _Success */ + switch (_Failure) + { + case memory_order_relaxed: + break; + + case memory_order_seq_cst: + if (_Success != memory_order_seq_cst) + _INVALID_MEMORY_ORDER; + break; + + case memory_order_acquire: + if ((_Success == memory_order_consume) || + (_Success == memory_order_relaxed)) + _INVALID_MEMORY_ORDER; + break; + + case memory_order_consume: + if (_Success == memory_order_relaxed) + _INVALID_MEMORY_ORDER; + break; + + default: + _INVALID_MEMORY_ORDER; + break; + } + } + + /* _Atomic_store_1 */ +inline void _Store_relaxed_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* store _Value atomically with relaxed memory order */ + #if defined(_M_ARM) + __iso_volatile_store8((volatile char *)_Tgt, _Value); + + #else + *_Tgt = _Value; + #endif + } + +inline void _Store_release_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* store _Value atomically with release memory order */ + #if defined(_M_ARM) + _Memory_barrier(); + __iso_volatile_store8((volatile char *)_Tgt, _Value); + + #else + _Compiler_barrier(); + *_Tgt = _Value; + #endif + } + +inline void _Store_seq_cst_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* store _Value atomically with + sequentially consistent memory order */ + + #if defined(_M_ARM) + _Memory_barrier(); + __iso_volatile_store8((volatile char *)_Tgt, _Value); + _Memory_barrier(); + + #else + _INTRIN_SEQ_CST(_InterlockedExchange8)((volatile char *)_Tgt, _Value); + #endif + } + +inline void _Atomic_store_1( + volatile _Uint1_t *_Tgt, _Uint1_t _Value, memory_order _Order) + { /* store _Value atomically */ + switch (_Order) + { + case memory_order_relaxed: + _Store_relaxed_1(_Tgt, _Value); + break; + + case memory_order_release: + _Store_release_1(_Tgt, _Value); + break; + + case memory_order_seq_cst: + _Store_seq_cst_1(_Tgt, _Value); + break; + + default: + _INVALID_MEMORY_ORDER; + break; + } + } + + /* _Atomic_load_1 */ +inline _Uint1_t _Load_seq_cst_1(volatile _Uint1_t *_Tgt) + { /* load from *_Tgt atomically with + sequentially consistent memory order */ + _Uint1_t _Value; + + #if defined(_M_ARM) + _Value = __iso_volatile_load8((volatile char *)_Tgt); + _Memory_barrier(); + + #else + _Value = *_Tgt; + _Compiler_barrier(); + #endif + + return (_Value); + } + +inline _Uint1_t _Load_relaxed_1(volatile _Uint1_t *_Tgt) + { /* load from *_Tgt atomically with + relaxed memory order */ + _Uint1_t _Value; + + #if defined(_M_ARM) + _Value = __iso_volatile_load8((volatile char *)_Tgt); + + #else + _Value = *_Tgt; + #endif + + return (_Value); + } + +inline _Uint1_t _Load_acquire_1(volatile _Uint1_t *_Tgt) + { /* load from *_Tgt atomically with + acquire memory order */ + + return (_Load_seq_cst_1(_Tgt)); + } + +inline _Uint1_t _Atomic_load_1( + volatile _Uint1_t *_Tgt, memory_order _Order) + { /* load from *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Load_relaxed_1(_Tgt)); + + case memory_order_consume: + case memory_order_acquire: + return (_Load_acquire_1(_Tgt)); + + case memory_order_seq_cst: + return (_Load_seq_cst_1(_Tgt)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_exchange_1 */ +inline _Uint1_t _Exchange_seq_cst_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* exchange _Value and *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchange8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Exchange_relaxed_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* exchange _Value and *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchange8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Exchange_acquire_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* exchange _Value and *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchange8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Exchange_release_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* exchange _Value and *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchange8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Atomic_exchange_1( + volatile _Uint1_t *_Tgt, _Uint1_t _Value, memory_order _Order) + { /* exchange _Value and *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Exchange_relaxed_1(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Exchange_acquire_1(_Tgt, _Value)); + + case memory_order_release: + return (_Exchange_release_1(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Exchange_seq_cst_1(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_compare_exchange_weak_1, _Atomic_compare_exchange_strong_1 */ +inline int _Compare_exchange_seq_cst_1(volatile _Uint1_t *_Tgt, + _Uint1_t *_Exp, _Uint1_t _Value) + { /* compare and exchange values atomically with + sequentially consistent memory order */ + + int _Res; + + _Uint1_t _Prev = _INTRIN_SEQ_CST(_InterlockedCompareExchange8)((volatile char *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_relaxed_1(volatile _Uint1_t *_Tgt, + _Uint1_t *_Exp, _Uint1_t _Value) + { /* compare and exchange values atomically with + relaxed memory order */ + int _Res; + + _Uint1_t _Prev = _INTRIN_RELAXED(_InterlockedCompareExchange8)((volatile char *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_acquire_1(volatile _Uint1_t *_Tgt, + _Uint1_t *_Exp, _Uint1_t _Value) + { /* compare and exchange values atomically with + acquire memory order */ + int _Res; + + _Uint1_t _Prev = _INTRIN_ACQUIRE(_InterlockedCompareExchange8)((volatile char *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_release_1(volatile _Uint1_t *_Tgt, + _Uint1_t *_Exp, _Uint1_t _Value) + { /* compare and exchange values atomically with + release memory order */ + int _Res; + + _Uint1_t _Prev = _INTRIN_RELEASE(_InterlockedCompareExchange8)((volatile char *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Atomic_compare_exchange_strong_1( + volatile _Uint1_t *_Tgt, _Uint1_t *_Exp, _Uint1_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + _Validate_compare_exchange_memory_order(_Order1, _Order2); + + switch (_Memory_order_upper_bound(_Order1, _Order2)) + { + case memory_order_relaxed: + return (_Compare_exchange_relaxed_1(_Tgt, _Exp, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Compare_exchange_acquire_1(_Tgt, _Exp, _Value)); + + case memory_order_release: + return (_Compare_exchange_release_1(_Tgt, _Exp, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Compare_exchange_seq_cst_1(_Tgt, _Exp, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline int _Atomic_compare_exchange_weak_1( + volatile _Uint1_t *_Tgt, _Uint1_t *_Exp, _Uint1_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + /* No weak compare-exchange is currently available, + even for ARM, so fall back to strong */ + return (_Atomic_compare_exchange_strong_1(_Tgt, _Exp, _Value, + _Order1, _Order2)); + } + + /* _Atomic_fetch_add_1, _Atomic_fetch_sub_1 */ +inline _Uint1_t _Fetch_add_seq_cst_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* add _Value to *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchangeAdd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_add_relaxed_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* add _Value to *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchangeAdd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_add_acquire_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* add _Value to *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchangeAdd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_add_release_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* add _Value to *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchangeAdd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Atomic_fetch_add_1( + volatile _Uint1_t *_Tgt, _Uint1_t _Value, memory_order _Order) + { /* add _Value to *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_add_relaxed_1(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_add_acquire_1(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_add_release_1(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_add_seq_cst_1(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline _Uint1_t _Atomic_fetch_sub_1( + volatile _Uint1_t *_Tgt, _Uint1_t _Value, memory_order _Order) + { /* subtract _Value from *_Tgt atomically */ + return (_Atomic_fetch_add_1(_Tgt, 0 - _Value, _Order)); + } + + /* _Atomic_fetch_and_1 */ +inline _Uint1_t _Fetch_and_seq_cst_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* and _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedAnd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_and_relaxed_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* and _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedAnd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_and_acquire_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* and _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedAnd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_and_release_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* and _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedAnd8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Atomic_fetch_and_1( + volatile _Uint1_t *_Tgt, _Uint1_t _Value, memory_order _Order) + { /* and _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_and_relaxed_1(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_and_acquire_1(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_and_release_1(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_and_seq_cst_1(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_or_1 */ +inline _Uint1_t _Fetch_or_seq_cst_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* or _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedOr8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_or_relaxed_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* or _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedOr8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_or_acquire_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* or _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedOr8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_or_release_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* or _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedOr8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Atomic_fetch_or_1( + volatile _Uint1_t *_Tgt, _Uint1_t _Value, memory_order _Order) + { /* or _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_or_relaxed_1(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_or_acquire_1(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_or_release_1(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_or_seq_cst_1(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_xor_1 */ +inline _Uint1_t _Fetch_xor_seq_cst_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* xor _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedXor8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_xor_relaxed_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* xor _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedXor8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_xor_acquire_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* xor _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedXor8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Fetch_xor_release_1(volatile _Uint1_t *_Tgt, _Uint1_t _Value) + { /* xor _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedXor8)((volatile char *)_Tgt, _Value)); + } + +inline _Uint1_t _Atomic_fetch_xor_1( + volatile _Uint1_t *_Tgt, _Uint1_t _Value, memory_order _Order) + { /* xor _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_xor_relaxed_1(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_xor_acquire_1(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_xor_release_1(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_xor_seq_cst_1(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_store_2 */ +inline void _Store_relaxed_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* store _Value atomically with relaxed memory order */ + #if defined(_M_ARM) + __iso_volatile_store16((volatile short *)_Tgt, _Value); + + #else + *_Tgt = _Value; + #endif + } + +inline void _Store_release_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* store _Value atomically with release memory order */ + #if defined(_M_ARM) + _Memory_barrier(); + __iso_volatile_store16((volatile short *)_Tgt, _Value); + + #else + _Compiler_barrier(); + *_Tgt = _Value; + #endif + } + +inline void _Store_seq_cst_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* store _Value atomically with + sequentially consistent memory order */ + + #if defined(_M_ARM) + _Memory_barrier(); + __iso_volatile_store16((volatile short *)_Tgt, _Value); + _Memory_barrier(); + + #else + _INTRIN_SEQ_CST(_InterlockedExchange16)((volatile short *)_Tgt, _Value); + #endif + } + +inline void _Atomic_store_2( + volatile _Uint2_t *_Tgt, _Uint2_t _Value, memory_order _Order) + { /* store _Value atomically */ + switch (_Order) + { + case memory_order_relaxed: + _Store_relaxed_2(_Tgt, _Value); + break; + + case memory_order_release: + _Store_release_2(_Tgt, _Value); + break; + + case memory_order_seq_cst: + _Store_seq_cst_2(_Tgt, _Value); + break; + + default: + _INVALID_MEMORY_ORDER; + break; + } + } + + /* _Atomic_load_2 */ +inline _Uint2_t _Load_seq_cst_2(volatile _Uint2_t *_Tgt) + { /* load from *_Tgt atomically with + sequentially consistent memory order */ + _Uint2_t _Value; + + #if defined(_M_ARM) + _Value = __iso_volatile_load16((volatile short *)_Tgt); + _Memory_barrier(); + + #else + _Value = *_Tgt; + _Compiler_barrier(); + #endif + + return (_Value); + } + +inline _Uint2_t _Load_relaxed_2(volatile _Uint2_t *_Tgt) + { /* load from *_Tgt atomically with + relaxed memory order */ + _Uint2_t _Value; + + #if defined(_M_ARM) + _Value = __iso_volatile_load16((volatile short *)_Tgt); + + #else + _Value = *_Tgt; + #endif + + return (_Value); + } + +inline _Uint2_t _Load_acquire_2(volatile _Uint2_t *_Tgt) + { /* load from *_Tgt atomically with + acquire memory order */ + + return (_Load_seq_cst_2(_Tgt)); + } + +inline _Uint2_t _Atomic_load_2( + volatile _Uint2_t *_Tgt, memory_order _Order) + { /* load from *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Load_relaxed_2(_Tgt)); + + case memory_order_consume: + case memory_order_acquire: + return (_Load_acquire_2(_Tgt)); + + case memory_order_seq_cst: + return (_Load_seq_cst_2(_Tgt)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_exchange_2 */ +inline _Uint2_t _Exchange_seq_cst_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* exchange _Value and *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchange16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Exchange_relaxed_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* exchange _Value and *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchange16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Exchange_acquire_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* exchange _Value and *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchange16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Exchange_release_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* exchange _Value and *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchange16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Atomic_exchange_2( + volatile _Uint2_t *_Tgt, _Uint2_t _Value, memory_order _Order) + { /* exchange _Value and *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Exchange_relaxed_2(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Exchange_acquire_2(_Tgt, _Value)); + + case memory_order_release: + return (_Exchange_release_2(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Exchange_seq_cst_2(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_compare_exchange_weak_2, _Atomic_compare_exchange_strong_2 */ +inline int _Compare_exchange_seq_cst_2(volatile _Uint2_t *_Tgt, + _Uint2_t *_Exp, _Uint2_t _Value) + { /* compare and exchange values atomically with + sequentially consistent memory order */ + + int _Res; + + _Uint2_t _Prev = _INTRIN_SEQ_CST(_InterlockedCompareExchange16)((volatile short *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_relaxed_2(volatile _Uint2_t *_Tgt, + _Uint2_t *_Exp, _Uint2_t _Value) + { /* compare and exchange values atomically with + relaxed memory order */ + int _Res; + + _Uint2_t _Prev = _INTRIN_RELAXED(_InterlockedCompareExchange16)((volatile short *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_acquire_2(volatile _Uint2_t *_Tgt, + _Uint2_t *_Exp, _Uint2_t _Value) + { /* compare and exchange values atomically with + acquire memory order */ + int _Res; + + _Uint2_t _Prev = _INTRIN_ACQUIRE(_InterlockedCompareExchange16)((volatile short *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_release_2(volatile _Uint2_t *_Tgt, + _Uint2_t *_Exp, _Uint2_t _Value) + { /* compare and exchange values atomically with + release memory order */ + int _Res; + + _Uint2_t _Prev = _INTRIN_RELEASE(_InterlockedCompareExchange16)((volatile short *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Atomic_compare_exchange_strong_2( + volatile _Uint2_t *_Tgt, _Uint2_t *_Exp, _Uint2_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + _Validate_compare_exchange_memory_order(_Order1, _Order2); + + switch (_Memory_order_upper_bound(_Order1, _Order2)) + { + case memory_order_relaxed: + return (_Compare_exchange_relaxed_2(_Tgt, _Exp, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Compare_exchange_acquire_2(_Tgt, _Exp, _Value)); + + case memory_order_release: + return (_Compare_exchange_release_2(_Tgt, _Exp, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Compare_exchange_seq_cst_2(_Tgt, _Exp, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline int _Atomic_compare_exchange_weak_2( + volatile _Uint2_t *_Tgt, _Uint2_t *_Exp, _Uint2_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + /* No weak compare-exchange is currently available, + even for ARM, so fall back to strong */ + return (_Atomic_compare_exchange_strong_2(_Tgt, _Exp, _Value, + _Order1, _Order2)); + } + + /* _Atomic_fetch_add_2, _Atomic_fetch_sub_2 */ +inline _Uint2_t _Fetch_add_seq_cst_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* add _Value to *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchangeAdd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_add_relaxed_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* add _Value to *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchangeAdd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_add_acquire_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* add _Value to *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchangeAdd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_add_release_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* add _Value to *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchangeAdd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Atomic_fetch_add_2( + volatile _Uint2_t *_Tgt, _Uint2_t _Value, memory_order _Order) + { /* add _Value to *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_add_relaxed_2(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_add_acquire_2(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_add_release_2(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_add_seq_cst_2(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline _Uint2_t _Atomic_fetch_sub_2( + volatile _Uint2_t *_Tgt, _Uint2_t _Value, memory_order _Order) + { /* subtract _Value from *_Tgt atomically */ + return (_Atomic_fetch_add_2(_Tgt, 0 - _Value, _Order)); + } + + /* _Atomic_fetch_and_2 */ +inline _Uint2_t _Fetch_and_seq_cst_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* and _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedAnd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_and_relaxed_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* and _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedAnd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_and_acquire_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* and _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedAnd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_and_release_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* and _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedAnd16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Atomic_fetch_and_2( + volatile _Uint2_t *_Tgt, _Uint2_t _Value, memory_order _Order) + { /* and _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_and_relaxed_2(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_and_acquire_2(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_and_release_2(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_and_seq_cst_2(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_or_2 */ +inline _Uint2_t _Fetch_or_seq_cst_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* or _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedOr16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_or_relaxed_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* or _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedOr16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_or_acquire_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* or _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedOr16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_or_release_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* or _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedOr16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Atomic_fetch_or_2( + volatile _Uint2_t *_Tgt, _Uint2_t _Value, memory_order _Order) + { /* or _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_or_relaxed_2(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_or_acquire_2(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_or_release_2(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_or_seq_cst_2(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_xor_2 */ +inline _Uint2_t _Fetch_xor_seq_cst_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* xor _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedXor16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_xor_relaxed_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* xor _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedXor16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_xor_acquire_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* xor _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedXor16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Fetch_xor_release_2(volatile _Uint2_t *_Tgt, _Uint2_t _Value) + { /* xor _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedXor16)((volatile short *)_Tgt, _Value)); + } + +inline _Uint2_t _Atomic_fetch_xor_2( + volatile _Uint2_t *_Tgt, _Uint2_t _Value, memory_order _Order) + { /* xor _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_xor_relaxed_2(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_xor_acquire_2(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_xor_release_2(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_xor_seq_cst_2(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_store_4 */ +inline void _Store_relaxed_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* store _Value atomically with relaxed memory order */ + #if defined(_M_ARM) + __iso_volatile_store32((volatile int *)_Tgt, _Value); + + #else + *_Tgt = _Value; + #endif + } + +inline void _Store_release_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* store _Value atomically with release memory order */ + #if defined(_M_ARM) + _Memory_barrier(); + __iso_volatile_store32((volatile int *)_Tgt, _Value); + + #else + _Compiler_barrier(); + *_Tgt = _Value; + #endif + } + +inline void _Store_seq_cst_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* store _Value atomically with + sequentially consistent memory order */ + + #if defined(_M_ARM) + _Memory_barrier(); + __iso_volatile_store32((volatile int *)_Tgt, _Value); + _Memory_barrier(); + + #else + _INTRIN_SEQ_CST(_InterlockedExchange)((volatile long *)_Tgt, _Value); + #endif + } + +inline void _Atomic_store_4( + volatile _Uint4_t *_Tgt, _Uint4_t _Value, memory_order _Order) + { /* store _Value atomically */ + switch (_Order) + { + case memory_order_relaxed: + _Store_relaxed_4(_Tgt, _Value); + break; + + case memory_order_release: + _Store_release_4(_Tgt, _Value); + break; + + case memory_order_seq_cst: + _Store_seq_cst_4(_Tgt, _Value); + break; + + default: + _INVALID_MEMORY_ORDER; + break; + } + } + + /* _Atomic_load_4 */ +inline _Uint4_t _Load_seq_cst_4(volatile _Uint4_t *_Tgt) + { /* load from *_Tgt atomically with + sequentially consistent memory order */ + _Uint4_t _Value; + + #if defined(_M_ARM) + _Value = __iso_volatile_load32((volatile int *)_Tgt); + _Memory_barrier(); + + #else + _Value = *_Tgt; + _Compiler_barrier(); + #endif + + return (_Value); + } + +inline _Uint4_t _Load_relaxed_4(volatile _Uint4_t *_Tgt) + { /* load from *_Tgt atomically with + relaxed memory order */ + _Uint4_t _Value; + + #if defined(_M_ARM) + _Value = __iso_volatile_load32((volatile int *)_Tgt); + + #else + _Value = *_Tgt; + #endif + + return (_Value); + } + +inline _Uint4_t _Load_acquire_4(volatile _Uint4_t *_Tgt) + { /* load from *_Tgt atomically with + acquire memory order */ + + return (_Load_seq_cst_4(_Tgt)); + } + +inline _Uint4_t _Atomic_load_4( + volatile _Uint4_t *_Tgt, memory_order _Order) + { /* load from *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Load_relaxed_4(_Tgt)); + + case memory_order_consume: + case memory_order_acquire: + return (_Load_acquire_4(_Tgt)); + + case memory_order_seq_cst: + return (_Load_seq_cst_4(_Tgt)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_exchange_4 */ +inline _Uint4_t _Exchange_seq_cst_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* exchange _Value and *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchange)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Exchange_relaxed_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* exchange _Value and *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchange)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Exchange_acquire_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* exchange _Value and *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchange)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Exchange_release_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* exchange _Value and *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchange)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Atomic_exchange_4( + volatile _Uint4_t *_Tgt, _Uint4_t _Value, memory_order _Order) + { /* exchange _Value and *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Exchange_relaxed_4(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Exchange_acquire_4(_Tgt, _Value)); + + case memory_order_release: + return (_Exchange_release_4(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Exchange_seq_cst_4(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_compare_exchange_weak_4, _Atomic_compare_exchange_strong_4 */ +inline int _Compare_exchange_seq_cst_4(volatile _Uint4_t *_Tgt, + _Uint4_t *_Exp, _Uint4_t _Value) + { /* compare and exchange values atomically with + sequentially consistent memory order */ + + int _Res; + + _Uint4_t _Prev = _INTRIN_SEQ_CST(_InterlockedCompareExchange)((volatile long *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_relaxed_4(volatile _Uint4_t *_Tgt, + _Uint4_t *_Exp, _Uint4_t _Value) + { /* compare and exchange values atomically with + relaxed memory order */ + int _Res; + + _Uint4_t _Prev = _INTRIN_RELAXED(_InterlockedCompareExchange)((volatile long *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_acquire_4(volatile _Uint4_t *_Tgt, + _Uint4_t *_Exp, _Uint4_t _Value) + { /* compare and exchange values atomically with + acquire memory order */ + int _Res; + + _Uint4_t _Prev = _INTRIN_ACQUIRE(_InterlockedCompareExchange)((volatile long *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_release_4(volatile _Uint4_t *_Tgt, + _Uint4_t *_Exp, _Uint4_t _Value) + { /* compare and exchange values atomically with + release memory order */ + int _Res; + + _Uint4_t _Prev = _INTRIN_RELEASE(_InterlockedCompareExchange)((volatile long *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Atomic_compare_exchange_strong_4( + volatile _Uint4_t *_Tgt, _Uint4_t *_Exp, _Uint4_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + _Validate_compare_exchange_memory_order(_Order1, _Order2); + + switch (_Memory_order_upper_bound(_Order1, _Order2)) + { + case memory_order_relaxed: + return (_Compare_exchange_relaxed_4(_Tgt, _Exp, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Compare_exchange_acquire_4(_Tgt, _Exp, _Value)); + + case memory_order_release: + return (_Compare_exchange_release_4(_Tgt, _Exp, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Compare_exchange_seq_cst_4(_Tgt, _Exp, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline int _Atomic_compare_exchange_weak_4( + volatile _Uint4_t *_Tgt, _Uint4_t *_Exp, _Uint4_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + /* No weak compare-exchange is currently available, + even for ARM, so fall back to strong */ + return (_Atomic_compare_exchange_strong_4(_Tgt, _Exp, _Value, + _Order1, _Order2)); + } + + /* _Atomic_fetch_add_4, _Atomic_fetch_sub_4 */ +inline _Uint4_t _Fetch_add_seq_cst_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* add _Value to *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchangeAdd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_add_relaxed_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* add _Value to *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchangeAdd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_add_acquire_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* add _Value to *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchangeAdd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_add_release_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* add _Value to *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchangeAdd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Atomic_fetch_add_4( + volatile _Uint4_t *_Tgt, _Uint4_t _Value, memory_order _Order) + { /* add _Value to *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_add_relaxed_4(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_add_acquire_4(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_add_release_4(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_add_seq_cst_4(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline _Uint4_t _Atomic_fetch_sub_4( + volatile _Uint4_t *_Tgt, _Uint4_t _Value, memory_order _Order) + { /* subtract _Value from *_Tgt atomically */ + return (_Atomic_fetch_add_4(_Tgt, 0 - _Value, _Order)); + } + + /* _Atomic_fetch_and_4 */ +inline _Uint4_t _Fetch_and_seq_cst_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* and _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedAnd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_and_relaxed_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* and _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedAnd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_and_acquire_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* and _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedAnd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_and_release_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* and _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedAnd)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Atomic_fetch_and_4( + volatile _Uint4_t *_Tgt, _Uint4_t _Value, memory_order _Order) + { /* and _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_and_relaxed_4(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_and_acquire_4(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_and_release_4(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_and_seq_cst_4(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_or_4 */ +inline _Uint4_t _Fetch_or_seq_cst_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* or _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedOr)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_or_relaxed_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* or _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedOr)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_or_acquire_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* or _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedOr)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_or_release_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* or _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedOr)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Atomic_fetch_or_4( + volatile _Uint4_t *_Tgt, _Uint4_t _Value, memory_order _Order) + { /* or _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_or_relaxed_4(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_or_acquire_4(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_or_release_4(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_or_seq_cst_4(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_xor_4 */ +inline _Uint4_t _Fetch_xor_seq_cst_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* xor _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedXor)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_xor_relaxed_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* xor _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedXor)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_xor_acquire_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* xor _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedXor)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Fetch_xor_release_4(volatile _Uint4_t *_Tgt, _Uint4_t _Value) + { /* xor _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedXor)((volatile long *)_Tgt, _Value)); + } + +inline _Uint4_t _Atomic_fetch_xor_4( + volatile _Uint4_t *_Tgt, _Uint4_t _Value, memory_order _Order) + { /* xor _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_xor_relaxed_4(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_xor_acquire_4(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_xor_release_4(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_xor_seq_cst_4(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_store_8 */ +inline void _Store_relaxed_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* store _Value atomically with relaxed memory order */ + #if _MS_64 + *_Tgt = _Value; + + #else + _INTRIN_RELAXED(_InterlockedExchange64)((volatile _LONGLONG *)_Tgt, _Value); + #endif + } + +inline void _Store_release_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* store _Value atomically with release memory order */ + #if _MS_64 + _Compiler_barrier(); + *_Tgt = _Value; + + #else + _INTRIN_RELEASE(_InterlockedExchange64)((volatile _LONGLONG *)_Tgt, _Value); + #endif + } + +inline void _Store_seq_cst_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* store _Value atomically with + sequentially consistent memory order */ + _INTRIN_SEQ_CST(_InterlockedExchange64)((volatile _LONGLONG *)_Tgt, _Value); + } + +inline void _Atomic_store_8( + volatile _Uint8_t *_Tgt, _Uint8_t _Value, memory_order _Order) + { /* store _Value atomically */ + switch (_Order) + { + case memory_order_relaxed: + _Store_relaxed_8(_Tgt, _Value); + break; + + case memory_order_release: + _Store_release_8(_Tgt, _Value); + break; + + case memory_order_seq_cst: + _Store_seq_cst_8(_Tgt, _Value); + break; + + default: + _INVALID_MEMORY_ORDER; + break; + } + } + + /* _Atomic_load_8 */ +inline _Uint8_t _Load_seq_cst_8(volatile _Uint8_t *_Tgt) + { /* load from *_Tgt atomically with + sequentially consistent memory order */ + _Uint8_t _Value; + + #if _MS_64 + _Value = *_Tgt; + _Compiler_barrier(); + + #elif defined(_M_ARM) + _Value = __ldrexd((volatile _LONGLONG *)_Tgt); + _Memory_barrier(); + + #else + _Value = _InterlockedOr64((volatile _LONGLONG *)_Tgt, 0); + #endif + + return (_Value); + } + +inline _Uint8_t _Load_relaxed_8(volatile _Uint8_t *_Tgt) + { /* load from *_Tgt atomically with + relaxed memory order */ + _Uint8_t _Value; + + #if _MS_64 + _Value = *_Tgt; + + #elif defined(_M_ARM) + _Value = __ldrexd((volatile _LONGLONG *)_Tgt); + + #else + _Value = _InterlockedOr64((volatile _LONGLONG *)_Tgt, 0); + #endif + + return (_Value); + } + +inline _Uint8_t _Load_acquire_8(volatile _Uint8_t *_Tgt) + { /* load from *_Tgt atomically with + acquire memory order */ + + return (_Load_seq_cst_8(_Tgt)); + } + +inline _Uint8_t _Atomic_load_8( + volatile _Uint8_t *_Tgt, memory_order _Order) + { /* load from *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Load_relaxed_8(_Tgt)); + + case memory_order_consume: + case memory_order_acquire: + return (_Load_acquire_8(_Tgt)); + + case memory_order_seq_cst: + return (_Load_seq_cst_8(_Tgt)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_exchange_8 */ +inline _Uint8_t _Exchange_seq_cst_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* exchange _Value and *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchange64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Exchange_relaxed_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* exchange _Value and *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchange64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Exchange_acquire_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* exchange _Value and *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchange64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Exchange_release_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* exchange _Value and *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchange64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Atomic_exchange_8( + volatile _Uint8_t *_Tgt, _Uint8_t _Value, memory_order _Order) + { /* exchange _Value and *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Exchange_relaxed_8(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Exchange_acquire_8(_Tgt, _Value)); + + case memory_order_release: + return (_Exchange_release_8(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Exchange_seq_cst_8(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_compare_exchange_weak_8, _Atomic_compare_exchange_strong_8 */ +inline int _Compare_exchange_seq_cst_8(volatile _Uint8_t *_Tgt, + _Uint8_t *_Exp, _Uint8_t _Value) + { /* compare and exchange values atomically with + sequentially consistent memory order */ + + int _Res; + + _Uint8_t _Prev = _INTRIN_SEQ_CST(_InterlockedCompareExchange64)((volatile _LONGLONG *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_relaxed_8(volatile _Uint8_t *_Tgt, + _Uint8_t *_Exp, _Uint8_t _Value) + { /* compare and exchange values atomically with + relaxed memory order */ + int _Res; + + _Uint8_t _Prev = _INTRIN_RELAXED(_InterlockedCompareExchange64)((volatile _LONGLONG *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_acquire_8(volatile _Uint8_t *_Tgt, + _Uint8_t *_Exp, _Uint8_t _Value) + { /* compare and exchange values atomically with + acquire memory order */ + int _Res; + + _Uint8_t _Prev = _INTRIN_ACQUIRE(_InterlockedCompareExchange64)((volatile _LONGLONG *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Compare_exchange_release_8(volatile _Uint8_t *_Tgt, + _Uint8_t *_Exp, _Uint8_t _Value) + { /* compare and exchange values atomically with + release memory order */ + int _Res; + + _Uint8_t _Prev = _INTRIN_RELEASE(_InterlockedCompareExchange64)((volatile _LONGLONG *)_Tgt, + _Value, *_Exp); + + if (_Prev == *_Exp) + _Res = 1; + else + { /* copy old value */ + _Res = 0; + *_Exp = _Prev; + } + + return (_Res); + } + +inline int _Atomic_compare_exchange_strong_8( + volatile _Uint8_t *_Tgt, _Uint8_t *_Exp, _Uint8_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + _Validate_compare_exchange_memory_order(_Order1, _Order2); + + switch (_Memory_order_upper_bound(_Order1, _Order2)) + { + case memory_order_relaxed: + return (_Compare_exchange_relaxed_8(_Tgt, _Exp, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Compare_exchange_acquire_8(_Tgt, _Exp, _Value)); + + case memory_order_release: + return (_Compare_exchange_release_8(_Tgt, _Exp, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Compare_exchange_seq_cst_8(_Tgt, _Exp, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline int _Atomic_compare_exchange_weak_8( + volatile _Uint8_t *_Tgt, _Uint8_t *_Exp, _Uint8_t _Value, + memory_order _Order1, memory_order _Order2) + { /* compare and exchange values atomically */ + /* No weak compare-exchange is currently available, + even for ARM, so fall back to strong */ + return (_Atomic_compare_exchange_strong_8(_Tgt, _Exp, _Value, + _Order1, _Order2)); + } + + /* _Atomic_fetch_add_8, _Atomic_fetch_sub_8 */ +inline _Uint8_t _Fetch_add_seq_cst_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* add _Value to *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedExchangeAdd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_add_relaxed_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* add _Value to *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedExchangeAdd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_add_acquire_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* add _Value to *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedExchangeAdd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_add_release_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* add _Value to *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedExchangeAdd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Atomic_fetch_add_8( + volatile _Uint8_t *_Tgt, _Uint8_t _Value, memory_order _Order) + { /* add _Value to *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_add_relaxed_8(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_add_acquire_8(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_add_release_8(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_add_seq_cst_8(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline _Uint8_t _Atomic_fetch_sub_8( + volatile _Uint8_t *_Tgt, _Uint8_t _Value, memory_order _Order) + { /* subtract _Value from *_Tgt atomically */ + return (_Atomic_fetch_add_8(_Tgt, 0 - _Value, _Order)); + } + + /* _Atomic_fetch_and_8 */ +inline _Uint8_t _Fetch_and_seq_cst_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* and _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedAnd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_and_relaxed_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* and _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedAnd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_and_acquire_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* and _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedAnd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_and_release_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* and _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedAnd64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Atomic_fetch_and_8( + volatile _Uint8_t *_Tgt, _Uint8_t _Value, memory_order _Order) + { /* and _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_and_relaxed_8(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_and_acquire_8(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_and_release_8(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_and_seq_cst_8(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_or_8 */ +inline _Uint8_t _Fetch_or_seq_cst_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* or _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedOr64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_or_relaxed_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* or _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedOr64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_or_acquire_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* or _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedOr64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_or_release_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* or _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedOr64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Atomic_fetch_or_8( + volatile _Uint8_t *_Tgt, _Uint8_t _Value, memory_order _Order) + { /* or _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_or_relaxed_8(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_or_acquire_8(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_or_release_8(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_or_seq_cst_8(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + + /* _Atomic_fetch_xor_8 */ +inline _Uint8_t _Fetch_xor_seq_cst_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* xor _Value with *_Tgt atomically with + sequentially consistent memory order */ + + return (_INTRIN_SEQ_CST(_InterlockedXor64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_xor_relaxed_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* xor _Value with *_Tgt atomically with + relaxed memory order */ + + return (_INTRIN_RELAXED(_InterlockedXor64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_xor_acquire_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* xor _Value with *_Tgt atomically with + acquire memory order */ + + return (_INTRIN_ACQUIRE(_InterlockedXor64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Fetch_xor_release_8(volatile _Uint8_t *_Tgt, _Uint8_t _Value) + { /* xor _Value with *_Tgt atomically with + release memory order */ + + return (_INTRIN_RELEASE(_InterlockedXor64)((volatile _LONGLONG *)_Tgt, _Value)); + } + +inline _Uint8_t _Atomic_fetch_xor_8( + volatile _Uint8_t *_Tgt, _Uint8_t _Value, memory_order _Order) + { /* xor _Value with *_Tgt atomically */ + switch (_Order) + { + case memory_order_relaxed: + return (_Fetch_xor_relaxed_8(_Tgt, _Value)); + + case memory_order_consume: + case memory_order_acquire: + return (_Fetch_xor_acquire_8(_Tgt, _Value)); + + case memory_order_release: + return (_Fetch_xor_release_8(_Tgt, _Value)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_Fetch_xor_seq_cst_8(_Tgt, _Value)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline int _Atomic_flag_test_and_set(volatile _Atomic_flag_t *_Flag, + memory_order _Order) + { /* atomically test flag and set to true */ + switch (_Order) + { + case memory_order_relaxed: + return (_INTRIN_RELAXED(_interlockedbittestandset)(_Flag, 0)); + + case memory_order_consume: + case memory_order_acquire: + return (_INTRIN_ACQUIRE(_interlockedbittestandset)(_Flag, 0)); + + case memory_order_release: + return (_INTRIN_RELEASE(_interlockedbittestandset)(_Flag, 0)); + + case memory_order_acq_rel: + case memory_order_seq_cst: + return (_INTRIN_SEQ_CST(_interlockedbittestandset)(_Flag, 0)); + + default: + _INVALID_MEMORY_ORDER; + return (0); + } + } + +inline void _Atomic_flag_clear(volatile _Atomic_flag_t *_Flag, + memory_order _Order) + { /* atomically clear flag */ + static_assert(sizeof(_Atomic_flag_t) == sizeof(_Uint4_t), + "Unexpected _Atomic_flag_t size"); + + switch (_Order) + { + case memory_order_relaxed: + case memory_order_release: + case memory_order_seq_cst: + _Atomic_store_4((volatile _Uint4_t *)_Flag, 0, _Order); + break; + + default: + _INVALID_MEMORY_ORDER; + break; + } + } + +inline void _Atomic_thread_fence(memory_order _Order) + { /* force memory visibility and inhibit compiler reordering */ + #if defined(_M_ARM) + if (_Order != memory_order_relaxed) + { + _Memory_barrier(); + } + + #else + _Compiler_barrier(); + if (_Order == memory_order_seq_cst) + { /* force visibility */ + static _Uint4_t _Guard; + _Atomic_exchange_4(&_Guard, 0, memory_order_seq_cst); + _Compiler_barrier(); + } + #endif + } + +inline void _Atomic_signal_fence(memory_order _Order) + { /* inhibit compiler reordering */ + _Compiler_barrier(); + } + + #if defined(_M_ARM) + #define _YIELD_PROCESSOR __yield() + + #else + #define _YIELD_PROCESSOR + #endif + + /* SPIN LOCK FOR LOCKING VERSIONS OF OPERATIONS */ + /* Use acquire semantics on lock and release on unlock. Given our + current atomic_flag implementation, this ensures not just + atomicity but also sequential consistency. */ + +inline void _Lock_spin_lock( + volatile _Atomic_flag_t *_Flag) + { /* spin until _Flag successfully set */ + while (_ATOMIC_FLAG_TEST_AND_SET(_Flag, memory_order_acquire)) + _YIELD_PROCESSOR; + } + +inline void _Unlock_spin_lock( + volatile _Atomic_flag_t *_Flag) + { /* release previously obtained lock */ + _ATOMIC_FLAG_CLEAR(_Flag, memory_order_release); + } + + /* ATOMIC OPERATIONS FOR OBJECTS WITH SIZES THAT + DON'T MATCH THE SIZE OF ANY INTEGRAL TYPE */ +inline void _Atomic_copy( + volatile _Atomic_flag_t *_Flag, size_t _Size, + volatile void *_Tgt, volatile const void *_Src, + memory_order _Order) + { /* atomically copy *_Src to *_Tgt with memory ordering */ + _Lock_spin_lock(_Flag); + memcpy((void *)_Tgt, (void *)_Src, _Size); + _Unlock_spin_lock(_Flag); + } + +inline void _Atomic_exchange( + volatile _Atomic_flag_t *_Flag, size_t _Size, + volatile void *_Tgt, volatile void *_Src, + memory_order _Order) + { /* atomically swap *_Src and *_Tgt with memory ordering */ + unsigned char *_Left = (unsigned char *)_Tgt; + unsigned char *_Right = (unsigned char *)_Src; + + _Lock_spin_lock(_Flag); + for (; 0 < _Size; --_Size) + { /* copy bytes */ + unsigned char _Tmp = *_Left; + *_Left++ = *_Right; + *_Right++ = _Tmp; + } + _Unlock_spin_lock(_Flag); + } + +inline int _Atomic_compare_exchange_weak( + volatile _Atomic_flag_t *_Flag, size_t _Size, + volatile void *_Tgt, volatile void *_Exp, const volatile void *_Src, + memory_order _Order1, memory_order _Order2) + { /* atomically compare and exchange with memory ordering */ + int _Result; + + _Lock_spin_lock(_Flag); + _Result = memcmp((const void *)_Tgt, (const void *)_Exp, _Size) == 0; + if (_Result != 0) + memcpy((void *)_Tgt, (void *)_Src, _Size); + else + memcpy((void *)_Exp, (void *)_Tgt, _Size); + _Unlock_spin_lock(_Flag); + return (_Result); + } + +inline int _Atomic_compare_exchange_strong( + volatile _Atomic_flag_t *_Flag, size_t _Size, + volatile void *_Tgt, volatile void *_Exp, const volatile void *_Src, + memory_order _Order1, memory_order _Order2) + { /* atomically compare and exchange with memory ordering */ + return (_Atomic_compare_exchange_weak(_Flag, _Size, _Tgt, _Exp, _Src, + _Order1, _Order2)); + } + + /* LOCK-FREE PROPERTY FOR INTEGRAL TYPES */ +inline int _Atomic_is_lock_free_1(void) + { /* return true if 1-byte atomic values are lock-free */ + return (1 <= _ATOMIC_MAXBYTES_LOCK_FREE); + } + +inline int _Atomic_is_lock_free_2(void) + { /* return true if 2-byte atomic values are lock-free */ + return (2 <= _ATOMIC_MAXBYTES_LOCK_FREE); + } + +inline int _Atomic_is_lock_free_4(void) + { /* return true if 4-byte atomic values are lock-free */ + return (4 <= _ATOMIC_MAXBYTES_LOCK_FREE); + } + +inline int _Atomic_is_lock_free_8(void) + { /* return true if 8-byte atomic values are lock-free */ + return (8 <= _ATOMIC_MAXBYTES_LOCK_FREE); + } +_STD_END + + #if defined(_M_IX86) +#pragma pop_macro("_InterlockedExchange64") +#pragma pop_macro("_InterlockedExchangeAdd64") +#pragma pop_macro("_InterlockedAnd64") +#pragma pop_macro("_InterlockedOr64") +#pragma pop_macro("_InterlockedXor64") + #endif /* defined(_M_IX86) */ + + #pragma pop_macro("and") + #pragma pop_macro("or") + #pragma pop_macro("xor") + #pragma pop_macro("new") + #pragma warning(pop) + #pragma pack(pop) +#endif /* RC_INVOKED */ +#endif /* _XATOMIC_H */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xatomic0.h b/test_data/lots_of_files/xatomic0.h new file mode 100644 index 0000000..a91d76c --- /dev/null +++ b/test_data/lots_of_files/xatomic0.h @@ -0,0 +1,198 @@ +/* xatomic0.h internal header */ +#pragma once +#ifndef _XATOMIC0_H +#define _XATOMIC0_H +#ifndef RC_INVOKED +#include <yvals.h> + + #pragma pack(push,_CRT_PACKING) + #pragma warning(push,3) + #pragma push_macro("new") + #undef new + +_STD_BEGIN + #if !defined(_GENERIC_ATOMICS) + #define _GENERIC_ATOMICS 0 /* nonzero for (non-conforming) generic */ + #endif /* !defined(_GENERIC_ATOMICS) */ + + /* ENUM memory_order */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst + } memory_order; + +typedef _Uint32t _Uint4_t; +typedef _Uint4_t _Atomic_integral_t; + + /* SET SIZES AND FLAGS FOR COMPILER AND TARGET ARCHITECTURE */ + /* Note: the xxx_SIZE macros are used to generate function names, + so they must expand to the digits representing + the number of bytes in the type; they cannot be expressions + that give the number of bytes. */ + + #define _WCHAR_T_SIZE 2 + #define _SHORT_SIZE 2 + #define _INT_SIZE 4 + #define _LONG_SIZE 4 + #define _LONGLONG_SIZE 8 + + #if defined(_M_X64) || defined(_LP64) || defined(__x86_64) + #define _ADDR_SIZE 8 + + #if !_GENERIC_ATOMICS + #define _MS_64 1 + #endif /* !_GENERIC_ATOMICS */ + + #else /* defined(_M_X64) || defined(_LP64) || defined(__x86_64) */ + #define _ADDR_SIZE 4 + + #if defined(_M_ARM) + + #elif !_GENERIC_ATOMICS + #define _MS_32 1 + #endif /* !_GENERIC_ATOMICS */ + #endif /* defined(_M_X64) || defined(_LP64) || defined(__x86_64) */ + + #if !defined(_MS_32) + #define _MS_32 0 + #endif /* !defined(_MS_32) */ + + #if !defined(_MS_64) + #define _MS_64 0 + #endif /* !defined(_MS_64) */ + + /* TYPEDEF _Atomic_flag_t */ + +typedef long _Atomic_flag_t; + + #define _ATOMIC_MAXBYTES_LOCK_FREE 8 + #define _ATOMIC_FLAG_USES_LOCK 0 + #define _ATOMIC_FENCE_USES_LOCK 0 + + /* DECLARATIONS NEEDED FOR ATOMIC REFERENCE COUNTING */ +inline _Uint4_t _Atomic_load_4(volatile _Uint4_t *, memory_order); +inline int _Atomic_compare_exchange_weak_4( + volatile _Uint4_t *, _Uint4_t *, _Uint4_t, memory_order, memory_order); +inline _Uint4_t _Atomic_fetch_add_4( + volatile _Uint4_t *, _Uint4_t, memory_order); +inline _Uint4_t _Atomic_fetch_sub_4( + volatile _Uint4_t *, _Uint4_t, memory_order); + +typedef _Atomic_integral_t _Atomic_counter_t; + + #if defined(__cplusplus) +inline _Atomic_integral_t + _Get_atomic_count(const _Atomic_counter_t& _Counter) + { // get counter + return (_Counter); + } + +inline void _Init_atomic_counter(_Atomic_counter_t& _Counter, + _Atomic_integral_t _Value) + { // non-atomically initialize counter + _Counter = _Value; + } + +inline _Atomic_integral_t _Inc_atomic_counter_explicit( + _Atomic_counter_t& _Counter, memory_order _Order) + { // atomically increment counter and return result + return (_Atomic_fetch_add_4(&_Counter, 1, _Order) + 1); + } + +inline _Atomic_integral_t _Inc_atomic_counter(_Atomic_counter_t& _Counter) + { // atomically increment counter and return result + return (_Inc_atomic_counter_explicit(_Counter, memory_order_seq_cst)); + } + +inline _Atomic_integral_t _Dec_atomic_counter_explicit( + _Atomic_counter_t& _Counter, memory_order _Order) + { // atomically decrement counter and return result + return (_Atomic_fetch_sub_4(&_Counter, 1, _Order) - 1); + } + +inline _Atomic_integral_t _Dec_atomic_counter(_Atomic_counter_t& _Counter) + { // atomically decrement counter and return result + return (_Dec_atomic_counter_explicit(_Counter, memory_order_seq_cst)); + } + +inline _Atomic_integral_t _Load_atomic_counter_explicit( + _Atomic_counter_t& _Counter, memory_order _Order) + { // atomically load counter and return result + return (_Atomic_load_4(&_Counter, _Order)); + } + +inline _Atomic_integral_t _Load_atomic_counter(_Atomic_counter_t& _Counter) + { // atomically load counter and return result + return (_Load_atomic_counter_explicit(_Counter, memory_order_seq_cst)); + } + +inline _Atomic_integral_t _Compare_increment_atomic_counter_explicit( + _Atomic_counter_t& _Counter, + _Atomic_integral_t _Expected, + memory_order _Order) + { // atomically compare and increment counter and return result + return (_Atomic_compare_exchange_weak_4( + &_Counter, &_Expected, _Expected + 1, + _Order, _Order)); + } + +inline _Atomic_integral_t _Compare_increment_atomic_counter( + _Atomic_counter_t& _Counter, _Atomic_integral_t _Expected) + { // atomically compare and increment counter and return result + return (_Compare_increment_atomic_counter_explicit( + _Counter, _Expected, memory_order_seq_cst)); + } + + #else /* defined(__cplusplus) */ +#define _Get_atomic_count(_Counter) _Counter + +#define _Init_atomic_counter(_Counter, _Value) \ + _Counter = _Value + +#define _Inc_atomic_counter_explicit(_Counter, _Order) \ + (_Atomic_fetch_add_4(&_Counter, 1, _Order) + 1) + +#define _Inc_atomic_counter(_Counter) \ + (_Inc_atomic_counter_explicit(_Counter, memory_order_seq_cst)) + +#define _Dec_atomic_counter_explicit(_Counter, _Order) \ + (_Atomic_fetch_sub_4(&_Counter, 1, _Order) - 1) + +#define _Dec_atomic_counter(_Counter) \ + (_Dec_atomic_counter_explicit(_Counter, memory_order_seq_cst)) + +#define _Load_atomic_counter_explicit(_Counter, _Order) \ + _Atomic_load_4(&_Counter, _Order) + +#define _Load_atomic_counter(_Counter) \ + _Load_atomic_counter_explicit(_Counter, memory_order_seq_cst) + +#define _Compare_increment_atomic_counter_explicit(_Counter, _Expected, _Order) \ + _Atomic_compare_exchange_weak_4(&_Counter, &_Expected, _Expected + 1, \ + _Order, _Order) + +#define _Compare_increment_atomic_counter(_Counter, _Expected) \ + _Compare_increment_atomic_counter_explicit( \ + _Counter, _Expected, memory_order_seq_cst) + #endif /* defined(__cplusplus) */ + + /* SPIN LOCKS */ +_EXTERN_C +_CRTIMP2_PURE void __cdecl _Lock_shared_ptr_spin_lock(); +_CRTIMP2_PURE void __cdecl _Unlock_shared_ptr_spin_lock(); +_END_EXTERN_C +_STD_END + #pragma pop_macro("new") + #pragma warning(pop) + #pragma pack(pop) +#endif /* RC_INVOKED */ +#endif /* _XATOMIC0_H */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xstoll.c b/test_data/lots_of_files/xstoll.c new file mode 100644 index 0000000..751d6ec --- /dev/null +++ b/test_data/lots_of_files/xstoll.c @@ -0,0 +1,50 @@ +/* _Stoll function */ +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include "xmath.h" +_C_STD_BEGIN + +#define MYMIN (-_LLONG_MAX - _C2) /* LLONG_MIN */ +#define MYMAX _LLONG_MAX /* LLONG_MAX */ + +_CRTIMP2_PURE _ULonglong __CLRCALL_PURE_OR_CDECL _Stoullx(const char *, char **, int, int *); + +_CRTIMP2_PURE _Longlong __CLRCALL_PURE_OR_CDECL _Stollx(const char * s, char ** endptr, + int base, int *perr) + { /* convert string to long long, with checking */ + const char *sc; + char *se, sign; + _ULonglong x; + + if (endptr == 0) + endptr = &se; + for (sc = s; isspace((unsigned char)*sc); ++sc) + ; + sign = (char)(*sc == '-' || *sc == '+' ? *sc++ : '+'); + x = _Stoullx(sc, endptr, base, perr); + if (sc == *endptr) + *endptr = (char *)s; + if (s == *endptr && x != 0 || sign == '+' && MYMAX < x + || sign == '-' && 0 - (_ULonglong)MYMIN < x) + { /* overflow */ + errno = ERANGE; + if (perr != 0) + *perr = 1; + return (sign == '-' ? MYMIN : MYMAX); + } + else + return ((_Longlong)(sign == '-' ? 0 - x : x)); + } + +_CRTIMP2_PURE _Longlong (__CLRCALL_PURE_OR_CDECL _Stoll)(const char * s, char ** endptr, int base) + { /* convert string, discard error code */ + return (_Stollx(s, endptr, base, 0)); + } +_C_STD_END + +/* + * Copyright (c) 1992-2007 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. + V5.03:0009 */ diff --git a/test_data/lots_of_files/xstopfx.c b/test_data/lots_of_files/xstopfx.c new file mode 100644 index 0000000..31a001e --- /dev/null +++ b/test_data/lots_of_files/xstopfx.c @@ -0,0 +1,93 @@ +/* _Stopfx function */ +#include "xmath.h" +#include <ctype.h> +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Stopfx(const char **ps, char **endptr) + { /* parse prefix of floating-point field */ + const char *s = *ps; + int code = 0; + + for (; isspace((unsigned char)*s); ++s) + ; + if (*s == '-') + code = FL_NEG, ++s; + else if (*s == '+') + ++s; + if (*s == 'n' || *s == 'N') + { /* parse "nan" or fail */ + if ((*++s != 'a' && *s != 'A') || (*++s != 'n' && *s != 'N')) + { /* parse failed, roll back pointer */ + s = *ps; + code = FL_ERR; + } + else + { /* parse optional (n-char-sequence) */ + const char *q = ++s; + + code = FL_NAN; + if (*q == '(') + { /* got '(', skip through ')' */ + for (; isalnum((unsigned char)*++q) || *q == '_'; ) + ; + if (*q == ')') + s = ++q; + } + } + if (endptr != 0) + *endptr = (char *)s; + } + else if (*s == 'i' || *s == 'I') + { /* parse "inf" or fail */ + if ((*++s != 'n' && *s != 'N') || (*++s != 'f' && *s != 'F')) + { /* parse failed, roll back pointer */ + s = *ps; + code = FL_ERR; + } + else + { /* parse optional rest of "infinity" */ + const char *q = ++s; + code |= FL_INF; + + if ((*q == 'i' || *q == 'I') + && (*++q == 'n' || *q == 'N') + && (*++q == 'i' || *q == 'I') + && (*++q == 't' || *q == 'T') + && (*++q == 'y' || *q == 'Y')) + s = ++q; + } + if (endptr != 0) + *endptr = (char *)s; + } + +/* #if _IS_C9X */ + else if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) + { /* test for valid hex field following 0x or 0X */ + const char *s1 = s + 2; + + if (*s1 == '.') + ++s1; + if (!isxdigit((unsigned char)*s1)) + code |= FL_DEC; + else + s += 2, code |= FL_HEX; + } +/* #endif _IS_C9X */ + + else + code |= FL_DEC; + *ps = s; + return (code); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xstoul.c b/test_data/lots_of_files/xstoul.c new file mode 100644 index 0000000..518a961 --- /dev/null +++ b/test_data/lots_of_files/xstoul.c @@ -0,0 +1,116 @@ +/* _Stoul function */ +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <string.h> +#include "xmath.h" + #pragma warning(disable: 4701) + + #if (ULONG_MAX >> 16) >> 16 != 0xffffffff && ULONG_MAX != 0xffffffff + #error LONGS TOO LARGE FOR _Stoul + #endif /* longs too large */ + +_C_STD_BEGIN + + /* macros */ +#define BASE_MAX 36 /* largest valid base */ + + /* static data */ +static const char digits[] = /* valid digits */ + "0123456789abcdefghijklmnopqrstuvwxyz"; + + #if (ULONG_MAX >> 16) >> 16 == 0xffffffff +static const char ndigs[BASE_MAX + 1] = { /* 64-bits! */ + 0, 0, 65, 41, 33, 28, 25, 23, 22, 21, + 20, 19, 18, 18, 17, 17, 17, 16, 16, 16, + 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, + 14, 13, 13, 13, 13, 13, 13,}; + + #else /* (ULONG_MAX >> 16) >> 16 == 0xffffffff */ +static const char ndigs[BASE_MAX+1] = { /* 32-bits! */ + 0, 0, 33, 21, 17, 14, 13, 12, 11, 11, + 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7,}; + #endif /* (ULONG_MAX >> 16) >> 16 == 0xffffffff */ + +#if defined(__cplusplus) && !defined(MRTDLL) +extern "C" +#endif +_CRTIMP2_PURE unsigned long __CLRCALL_PURE_OR_CDECL _Stoulx(const char *s, char **endptr, + int base, int *perr) + { /* convert string to unsigned long, with checking */ + const char *sc, *sd; + const char *s1, *s2; + char dig, sign; + ptrdiff_t n; + unsigned long x, y; + + if (perr != 0) + *perr = 0; + for (sc = s; isspace((unsigned char)*sc); ++sc) + ; + sign = *sc == '-' || *sc == '+' ? *sc++ : '+'; + if (base < 0 || base == 1 || BASE_MAX < base) + { /* silly base */ + if (endptr != 0) + *endptr = (char *)s; + return (0); + } + else if (0 < base) + { /* strip 0x or 0X */ + if (base == 16 && *sc == '0' + && (sc[1] == 'x' || sc[1] == 'X')) + sc += 2; + } + else if (*sc != '0') + base = 10; + else if (sc[1] == 'x' || sc[1] == 'X') + base = 16, sc += 2; + else + base = 8; + for (s1 = sc; *sc == '0'; ++sc) + ; /* skip leading zeros */ + x = 0; + for (s2 = sc, y = 0; (sd = (char *)memchr(&digits[0], + tolower(*sc), base)) != 0; ++sc) + { /* accumulate digits */ + y = x; + dig = (char)(sd - digits); /* for overflow checking */ + x = x * base + dig; + } + if (s1 == sc) + { /* check string validity */ + if (endptr != 0) + *endptr = (char *)s; + return (0); + } + n = sc - s2 - ndigs[base]; + if (n < 0) + ; + else if (0 < n || x < x - dig || (x - dig) / base != y) + { /* overflow */ + errno = ERANGE; + if (perr != 0) + *perr = 1; + x = ULONG_MAX, sign = '+'; + } + if (sign == '-') /* get final value */ + x = 0 - x; + if (endptr != 0) + *endptr = (char *)sc; + return (x); + } + +_CRTIMP2_PURE unsigned long __CLRCALL_PURE_OR_CDECL _Stoul(const char *s, char **endptr, int base) + { /* convert string, discard error code */ + return (_Stoulx(s, endptr, base, 0)); + } +_C_STD_END + +/* + * Copyright (c) 1992-2006 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. + V5.02:0009 */ diff --git a/test_data/lots_of_files/xstoull.c b/test_data/lots_of_files/xstoull.c new file mode 100644 index 0000000..830b086 --- /dev/null +++ b/test_data/lots_of_files/xstoull.c @@ -0,0 +1,116 @@ +/* _Stoull function */ +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <string.h> +#include "xmath.h" + +#define MYMAX _ULLONG_MAX /* ULLONG_MAX */ + + #if (MYMAX >> 16) >> 16 != 0xffffffff && MYMAX != 0xffffffff + #error LONG LONGS TOO LARGE FOR _Stoull + #endif /* long longs too large */ + + #define BIG_TABLE (MYMAX >> 16) >> 16 == 0xffffffff + +_C_STD_BEGIN + + /* macros */ +#define BASE_MAX 36 /* largest valid base */ + + /* static data */ +static const char digits[] = /* valid digits */ + "0123456789abcdefghijklmnopqrstuvwxyz"; + + #if BIG_TABLE +static const char ndigs[BASE_MAX + 1] = { /* 64-bits! */ + 0, 0, 65, 41, 33, 28, 25, 23, 22, 21, + 20, 19, 18, 18, 17, 17, 17, 16, 16, 16, + 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, + 14, 13, 13, 13, 13, 13, 13,}; + + #else /* BIG_TABLE */ +static const char ndigs[BASE_MAX+1] = { /* 32-bits! */ + 0, 0, 33, 21, 17, 14, 13, 12, 11, 11, + 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7,}; + #endif /* BIG_TABLE */ + +_CRTIMP2_PURE _ULonglong __CLRCALL_PURE_OR_CDECL _Stoullx(const char *s, char **endptr, + int base, int *perr) + { /* convert string to unsigned long long, with checking */ + const char *sc, *sd; + const char *s1, *s2; + char dig, sign; + ptrdiff_t n; + _ULonglong x, y; + + if (perr != 0) + *perr = 0; + for (sc = s; isspace((unsigned char)*sc); ++sc) + ; + sign = (char)(*sc == '-' || *sc == '+' ? *sc++ : '+'); + if (base < 0 || base == 1 || BASE_MAX < base) + { /* silly base */ + if (endptr != 0) + *endptr = (char *)s; + return (0); + } + else if (0 < base) + { /* strip 0x or 0X */ + if (base == 16 && *sc == '0' + && (sc[1] == 'x' || sc[1] == 'X')) + sc += 2; + } + else if (*sc != '0') + base = 10; + else if (sc[1] == 'x' || sc[1] == 'X') + base = 16, sc += 2; + else + base = 8; + for (s1 = sc; *sc == '0'; ++sc) + ; /* skip leading zeros */ + x = 0; + for (s2 = sc, y = 0, dig = 0; (sd = (char *)memchr(&digits[0], + tolower(*sc), base)) != 0; ++sc) + { /* accumulate digits */ + y = x; + dig = (char)(sd - digits); /* for overflow checking */ + x = x * base + dig; + } + if (s1 == sc) + { /* check string validity */ + if (endptr != 0) + *endptr = (char *)s; + return (0); + } + n = sc - s2 - ndigs[base]; + if (n < 0) + ; + else if (0 < n || x < x - dig || (x - dig) / base != y) + { /* overflow */ + errno = ERANGE; + if (perr != 0) + *perr = 1; + x = MYMAX, sign = '+'; + } + if (sign == '-') /* get final value */ + x = 0 - x; + if (endptr != 0) + *endptr = (char *)sc; + return (x); + } + +_CRTIMP2_PURE _ULonglong __CLRCALL_PURE_OR_CDECL _Stoull(const char *s, char **endptr, int base) + { /* convert string, discard error code */ + return (_Stoullx(s, endptr, base, 0)); + } +_C_STD_END + +/* + * Copyright (c) 1992-2007 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. + V5.03:0009 */ diff --git a/test_data/lots_of_files/xstoxflt.c b/test_data/lots_of_files/xstoxflt.c new file mode 100644 index 0000000..9beb44c --- /dev/null +++ b/test_data/lots_of_files/xstoxflt.c @@ -0,0 +1,118 @@ +/* _Stoxflt function */ +#include "xmath.h" +#include <ctype.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +#define BASE 16 /* hexadecimal */ +#define NDIG 7 /* hexadecimal digits per long element */ +#define MAXSIG (5 * NDIG) /* maximum significant digits to keep */ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Stoxflt(const char *s0, const char *s, + char **endptr, long lo[], int maxsig) + { /* convert string to array of long plus exponent */ + char buf[MAXSIG + 1]; /* worst case, with room for rounding digit */ + int nsig; /* number of significant digits seen */ + int seen; /* any valid field characters seen */ + int word = 0; /* current long word to fill */ + + const char *pd; + static const char digits[] = "0123456789abcdefABCDEF"; + static const char vals[] = + { /* values of hex digits */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 10, 11, 12, 13, 14, 15}; + + maxsig *= NDIG; /* convert word count to digit count */ + if (MAXSIG < maxsig) + maxsig = MAXSIG; /* protect against bad call */ + + lo[0] = 0; /* power of ten exponent */ + lo[1] = 0; /* first NDIG-digit word of fraction */ + + for (seen = 0; *s == '0'; ++s, seen = 1) + ; /* strip leading zeros */ + for (nsig = 0; (pd = (char *)memchr(&digits[0], *s, 22)) != 0; + ++s, seen = 1) + if (nsig <= maxsig) + buf[nsig++] = vals[pd - digits]; /* accumulate a digit */ + else + ++lo[0]; /* too many digits, just scale exponent */ + + if (*s == localeconv()->decimal_point[0]) + ++s; + if (nsig == 0) + for (; *s == '0'; ++s, seen = 1) + --lo[0]; /* strip zeros after point */ + for (; (pd = (char *)memchr(&digits[0], *s, 22)) != 0; ++s, seen = 1) + if (nsig <= maxsig) + { /* accumulate a fraction digit */ + buf[nsig++] = vals[pd - digits]; + --lo[0]; + } + + if (maxsig < nsig) + { /* discard excess digit after rounding up */ + unsigned int ms = maxsig; /* to quiet warnings */ + + if (BASE / 2 <= buf[ms]) + ++buf[ms - 1]; /* okay if digit becomes BASE */ + nsig = maxsig; + ++lo[0]; + } + for (; 0 < nsig && buf[nsig - 1] == '\0'; --nsig) + ++lo[0]; /* discard trailing zeros */ + if (nsig == 0) + buf[nsig++] = '\0'; /* ensure at least one digit */ + lo[0] <<= 2; /* change hex exponent to binary exponent */ + + if (seen) + { /* convert digit sequence to words */ + int bufidx = 0; /* next digit in buffer */ + int wordidx = NDIG - nsig % NDIG; /* next digit in word (% NDIG) */ + + word = wordidx % NDIG == 0 ? 0 : 1; + for (; bufidx < nsig; ++wordidx, ++bufidx) + if (wordidx % NDIG == 0) + lo[++word] = buf[bufidx]; + else + lo[word] = lo[word] * BASE + buf[bufidx]; + + if (*s == 'p' || *s == 'P') + { /* parse exponent */ + const char *ssav = s; + const char esign = *++s == '+' || *s == '-' ? *s++ : '+'; + int eseen = 0; + long lexp = 0; + + for (; isdigit((unsigned char)*s); ++s, eseen = 1) + if (lexp < 100000000) /* else overflow */ + lexp = lexp * 10 + (unsigned char)*s - '0'; + if (esign == '-') + lexp = -lexp; + lo[0] += lexp; + if (!eseen) + s = ssav; /* roll back if incomplete exponent */ + } + } + + if (!seen) + word = 0; /* return zero if bad parse */ + if (endptr) + *endptr = (char *)(seen ? s : s0); /* roll back if bad parse */ + return (word); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xstrcoll.c b/test_data/lots_of_files/xstrcoll.c new file mode 100644 index 0000000..f7a4d84 --- /dev/null +++ b/test_data/lots_of_files/xstrcoll.c @@ -0,0 +1,128 @@ +/*** +*xstrcoll.c - Collate locale strings +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Compare two strings using the locale LC_COLLATE information. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <string.h> +#include <xlocinfo.h> /* for _Collvec, _Strcoll */ +#include <windows.h> +#include <stdlib.h> +#include <malloc.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> +#include <errno.h> +#include <awint.h> + +/*** +*int _Strcoll() - Collate locale strings +* +*Purpose: +* Compare two strings using the locale LC_COLLATE information. +* [ANSI]. +* +* Non-C locale support available under _INTL switch. +* In the C locale, strcoll() simply resolves to strcmp(). +*Entry: +* const char *s1b = pointer to beginning of the first string +* const char *s1e = pointer past end of the first string +* const char *s2b = pointer to beginning of the second string +* const char *s1e = pointer past end of the second string +* const _Collvec *ploc = pointer to locale info +* +*Exit: +* Less than 0 = first string less than second string +* 0 = strings are equal +* Greater than 0 = first string greater than second string +* +*Exceptions: +* _NLSCMPERROR = error +* errno = EINVAL +* +*******************************************************************************/ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Strcoll ( + const char *_string1, + const char *_end1, + const char *_string2, + const char *_end2, + const _Collvec *ploc + ) +{ + int ret=0; + UINT codepage; + int n1 = (int)(_end1 - _string1); + int n2 = (int)(_end2 - _string2); + const wchar_t *locale_name; + + if (ploc == 0) + { + locale_name = ___lc_locale_name_func()[LC_COLLATE]; + codepage = ___lc_collate_cp_func(); + } + else + { + locale_name = ploc->_LocaleName; + codepage = ploc->_Page; + } + + if (locale_name == NULL) + { + int ans; + ans = memcmp(_string1, _string2, n1 < n2 ? n1 : n2); + ret=(ans != 0 || n1 == n2 ? ans : n1 < n2 ? -1 : +1); + } + else + { + if ( 0 == (ret = __crtCompareStringA( NULL, + locale_name, + SORT_STRINGSORT, + _string1, + n1, + _string2, + n2, + codepage )) ) + { + errno=EINVAL; + ret=_NLSCMPERROR; + } + else + { + ret-=2; + } + } + + return ret; +} + + +/*** +*_Collvec _Getcoll() - get collation info for current locale +* +*Purpose: +* +*Entry: +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +_CRTIMP2_PURE _Collvec __CLRCALL_PURE_OR_CDECL _Getcoll() +{ + _Collvec coll; + + coll._Page = ___lc_collate_cp_func(); + coll._LocaleName = ___lc_locale_name_func()[LC_COLLATE]; + if (coll._LocaleName) + coll._LocaleName = _wcsdup(coll._LocaleName); + + return (coll); +} diff --git a/test_data/lots_of_files/xstrxfrm.c b/test_data/lots_of_files/xstrxfrm.c new file mode 100644 index 0000000..e856f66 --- /dev/null +++ b/test_data/lots_of_files/xstrxfrm.c @@ -0,0 +1,133 @@ +/*** +*xstrxfrm.c - Transform a string using locale information +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Transform a string using the locale information as set by +* LC_COLLATE. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <string.h> +#include <xlocinfo.h> /* for _Collvec */ +#include <windows.h> +#include <stdlib.h> +#include <limits.h> +#include <malloc.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> +#include <awint.h> + +/*** +*size_t _Strxfrm() - Transform a string using locale information +* +*Purpose: +* Transform the string pointer to by _string2 and place the +* resulting string into the array pointer to by _string1. +* No more than _end1 - _string1 characters are place into the +* resulting string (including the null). +* +* The transformation is such that if strcmp() is applied to +* the two transformed strings, the return value is equal to +* the result of strcoll() applied to the two original strings. +* Thus, the conversion must take the locale LC_COLLATE info +* into account. +* [ANSI] +* +* The value of the following expression is the size of the array +* needed to hold the transformation of the source string: +* +* 1 + strxfrm(NULL,string,0) +* +* NOTE: Currently, the C libraries support the "C" locale only. +* Thus, _Strxfrm() simply resolves to strncpy()/strlen(). +* +*Entry: +* char *_string1 = pointer to beginning of result string +* char *_end1 = pointer past end of result string +* const char *_string2 = pointer to beginning of source string +* const char *_end2 = pointer past end of source string +* const _Collvec *ploc = pointer to locale info +* +*Exit: +* Length of the transformed string. +* If the value returned is too big, the contents of the +* _string1 array are indeterminate. +* +*Exceptions: +* Non-standard: if OM/API error, return INT_MAX. +* +*******************************************************************************/ + +_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Strxfrm ( + char *_string1, + char *_end1, + const char *_string2, + const char *_end2, + const _Collvec *ploc + ) +{ + size_t _n1 = _end1 - _string1; + size_t _n2 = _end2 - _string2; + int dstlen; + size_t retval = (size_t)-1; /* NON-ANSI: default if OM or API error */ + UINT codepage; + const wchar_t *locale_name; + + if (ploc == 0) + { + locale_name = ___lc_locale_name_func()[LC_COLLATE]; + codepage = ___lc_collate_cp_func(); + } + else + { + locale_name = ploc->_LocaleName; + codepage = ploc->_Page; + } + + if ((locale_name == NULL) && + (codepage == _CLOCALECP)) + { + if (_n2 <= _n1) + { + memcpy(_string1, _string2, _n2); + } + retval=_n2; + } + else + { + /* Inquire size of dst string in BYTES */ + if (0 != (dstlen = __crtLCMapStringA(NULL, + locale_name, + LCMAP_SORTKEY, + _string2, + (int)_n2, + NULL, + 0, + codepage, + TRUE))) + { + retval = dstlen; + + /* if not enough room, return amount needed */ + if (dstlen <= (int)(_n1)) + { + /* Map src string to dst string */ + __crtLCMapStringA(NULL, + locale_name, + LCMAP_SORTKEY, + _string2, + (int)_n2, + _string1, + (int)_n1, + codepage, + TRUE); + } + } + } + + return (size_t)retval; +} diff --git a/test_data/lots_of_files/xtgmath.h b/test_data/lots_of_files/xtgmath.h new file mode 100644 index 0000000..ed1224a --- /dev/null +++ b/test_data/lots_of_files/xtgmath.h @@ -0,0 +1,223 @@ +/* xtgmath.h internal header */ +#pragma once +#ifndef _XTGMATH +#define _XTGMATH +#ifndef RC_INVOKED + + #if defined(__cplusplus) +#include <xtr1common> + + #pragma pack(push,_CRT_PACKING) + #pragma warning(push,3) + #pragma push_macro("new") + #undef new + +_STD_BEGIN +template<class _Ty> + struct _Promote_to_float + { // promote integral to double + typedef typename conditional<is_integral<_Ty>::value, + double, _Ty>::type type; + }; + +template<class _Ty1, + class _Ty2> + struct _Common_float_type + { // find type for two-argument math function + typedef typename _Promote_to_float<_Ty1>::type _Ty1f; + typedef typename _Promote_to_float<_Ty2>::type _Ty2f; + typedef typename conditional<is_same<_Ty1f, long double>::value + || is_same<_Ty2f, long double>::value, long double, + typename conditional<is_same<_Ty1f, double>::value + || is_same<_Ty2f, double>::value, double, + float>::type>::type type; + }; +_STD_END + +#define _CRTDEFAULT +#define _CRTSPECIAL _CRTIMP + +#define _GENERIC_MATH1(FUN, CRTTYPE) \ +extern "C" CRTTYPE double __cdecl FUN(_In_ double); \ +template<class _Ty> inline \ + typename _STD enable_if< _STD is_integral<_Ty>::value, double>::type \ + FUN(_Ty _Left) \ + { \ + return (_CSTD FUN((double)_Left)); \ + } + +#define _GENERIC_MATH1X(FUN, ARG2, CRTTYPE) \ +extern "C" CRTTYPE double __cdecl FUN(_In_ double, ARG2); \ +template<class _Ty> inline \ + typename _STD enable_if< _STD is_integral<_Ty>::value, double>::type \ + FUN(_Ty _Left, ARG2 _Arg2) \ + { \ + return (_CSTD FUN((double)_Left, _Arg2)); \ + } + +#define _GENERIC_MATH2_CALL(FUN, CRTTYPE, CALL_OPT) \ +extern "C" CRTTYPE double CALL_OPT FUN(_In_ double, _In_ double); \ +template<class _Ty1, \ + class _Ty2> inline \ + typename _STD enable_if< _STD _Is_numeric<_Ty1>::value \ + && _STD _Is_numeric<_Ty2>::value, \ + typename _STD _Common_float_type<_Ty1, _Ty2>::type>::type \ + FUN(_Ty1 _Left, _Ty2 _Right) \ + { \ + typedef typename _STD _Common_float_type<_Ty1, _Ty2>::type type; \ + return (_CSTD FUN((type)_Left, (type)_Right)); \ + } + +#define _GENERIC_MATH2(FUN, CRTTYPE) \ + _GENERIC_MATH2_CALL(FUN, CRTTYPE, __cdecl) + +_C_STD_BEGIN +extern "C" double __cdecl pow(_In_ double, _In_ double); +float __CRTDECL pow(_In_ float, _In_ float) _NOEXCEPT; +long double __CRTDECL pow(_In_ long double, _In_ long double) _NOEXCEPT; + +template<class _Ty1, + class _Ty2> inline + typename _STD enable_if< _STD _Is_numeric<_Ty1>::value + && _STD _Is_numeric<_Ty2>::value, + typename _STD _Common_float_type<_Ty1, _Ty2>::type>::type + pow(const _Ty1 _Left, const _Ty2 _Right) + { // bring mixed types to a common type + typedef typename _STD _Common_float_type<_Ty1, _Ty2>::type type; + return (_CSTD pow(type(_Left), type(_Right))); + } + +//_GENERIC_MATH1(abs, _CRTDEFAULT) // has integer overloads +_GENERIC_MATH1(acos, _CRTDEFAULT) +_GENERIC_MATH1(asin, _CRTDEFAULT) +_GENERIC_MATH1(atan, _CRTDEFAULT) +_GENERIC_MATH2(atan2, _CRTDEFAULT) +_GENERIC_MATH1(ceil, _CRTSPECIAL) +_GENERIC_MATH1(cos, _CRTDEFAULT) +_GENERIC_MATH1(cosh, _CRTDEFAULT) +_GENERIC_MATH1(exp, _CRTDEFAULT) +//_GENERIC_MATH1(fabs, _CRT_JIT_INTRINSIC) // not required +_GENERIC_MATH1(floor, _CRTSPECIAL) +_GENERIC_MATH2(fmod, _CRTDEFAULT) +_GENERIC_MATH1X(frexp, _Out_ int *, _CRTSPECIAL) +_GENERIC_MATH1X(ldexp, _In_ int, _CRTSPECIAL) +_GENERIC_MATH1(log, _CRTDEFAULT) +_GENERIC_MATH1(log10, _CRTDEFAULT) +//_GENERIC_MATH1(modf, _CRTDEFAULT) // types must match +//_GENERIC_MATH2(pow, _CRTDEFAULT) // hand crafted +_GENERIC_MATH1(sin, _CRTDEFAULT) +_GENERIC_MATH1(sinh, _CRTDEFAULT) +_GENERIC_MATH1(sqrt, _CRTDEFAULT) +_GENERIC_MATH1(tan, _CRTDEFAULT) +_GENERIC_MATH1(tanh, _CRTDEFAULT) + // C99 MATH FUNCTIONS +#define _GENERIC_MATH1R(FUN, RET, CRTTYPE) \ +extern "C" CRTTYPE RET __cdecl FUN(_In_ double); \ +template<class _Ty> inline \ + typename _STD enable_if< _STD is_integral<_Ty>::value, RET>::type \ + FUN(_Ty _Left) \ + { \ + return (_CSTD FUN((double)_Left)); \ + } + + // TEMPLATE FUNCTION fma +inline float _Fma(float _Left, float _Middle, float _Right) + { + return (_CSTD fmaf(_Left, _Middle, _Right)); + } + +inline double _Fma(double _Left, double _Middle, double _Right) + { + return (_CSTD fma(_Left, _Middle, _Right)); + } + +inline long double _Fma(long double _Left, long double _Middle, long double _Right) + { + return (_CSTD fmal(_Left, _Middle, _Right)); + } + +template<class _Ty1, + class _Ty2, + class _Ty3> inline + typename _STD _Common_float_type<_Ty1, + typename _STD _Common_float_type<_Ty2, _Ty3>::type>::type + fma(_Ty1 _Left, _Ty2 _Middle, _Ty3 _Right) + { // bring mixed types to a common type + typedef typename _STD _Common_float_type<_Ty1, + typename _STD _Common_float_type<_Ty2, _Ty3>::type>::type type; + return (_Fma((type)_Left, (type)_Middle, (type)_Right)); + } + + // TEMPLATE FUNCTION remquo +inline float _Remquo(float _Left, float _Right, int *_Pquo) + { + return (_CSTD remquof(_Left, _Right, _Pquo)); + } + +inline double _Remquo(double _Left, double _Right, int *_Pquo) + { + return (_CSTD remquo(_Left, _Right, _Pquo)); + } + +inline long double _Remquo(long double _Left, long double _Right, int *_Pquo) + { + return (_CSTD remquol(_Left, _Right, _Pquo)); + } + +template<class _Ty1, + class _Ty2> inline + typename _STD _Common_float_type<_Ty1, _Ty2>::type + remquo(_Ty1 _Left, _Ty2 _Right, int *_Pquo) + { // bring mixed types to a common type + typedef typename _STD _Common_float_type<_Ty1, _Ty2>::type type; + return (_Remquo((type)_Left, (type)_Right, _Pquo)); + } + +_GENERIC_MATH1(acosh, _CRTSPECIAL) +_GENERIC_MATH1(asinh, _CRTSPECIAL) +_GENERIC_MATH1(atanh, _CRTSPECIAL) +_GENERIC_MATH1(cbrt, _CRTSPECIAL) +_GENERIC_MATH2(copysign, _CRTSPECIAL) +_GENERIC_MATH1(erf, _CRTSPECIAL) +_GENERIC_MATH1(erfc, _CRTSPECIAL) +_GENERIC_MATH1(expm1, _CRTSPECIAL) +_GENERIC_MATH1(exp2, _CRTSPECIAL) +//_GENERIC_MATH1(fabs, _CRT_JIT_INTRINSIC) // not required +_GENERIC_MATH2(fdim, _CRTSPECIAL) +//_GENERIC_MATH3(fma, _CRTSPECIAL) // hand crafted +_GENERIC_MATH2(fmax, _CRTSPECIAL) +_GENERIC_MATH2(fmin, _CRTSPECIAL) +_GENERIC_MATH2_CALL(hypot, _CRTDEFAULT, __CRTDECL) +_GENERIC_MATH1R(ilogb, int, _CRTSPECIAL) +_GENERIC_MATH1(lgamma, _CRTSPECIAL) +_GENERIC_MATH1R(llrint, long long, _CRTSPECIAL) +_GENERIC_MATH1R(llround, long long, _CRTSPECIAL) +_GENERIC_MATH1(log1p, _CRTSPECIAL) +_GENERIC_MATH1(log2, _CRTSPECIAL) +_GENERIC_MATH1(logb, _CRTSPECIAL) +_GENERIC_MATH1R(lrint, long, _CRTSPECIAL) +_GENERIC_MATH1R(lround, long, _CRTSPECIAL) +_GENERIC_MATH1(nearbyint, _CRTSPECIAL) +_GENERIC_MATH2(nextafter, _CRTSPECIAL) +_GENERIC_MATH1X(nexttoward, _In_ long double, _CRTSPECIAL) +_GENERIC_MATH2(remainder, _CRTSPECIAL) +//_GENERIC_MATH2X(remquo, _CRTSPECIAL) // hand crafted +_GENERIC_MATH1(rint, _CRTSPECIAL) +_GENERIC_MATH1(round, _CRTSPECIAL) +_GENERIC_MATH1X(scalbln, _In_ long, _CRTSPECIAL) +_GENERIC_MATH1X(scalbn, _In_ int, _CRTSPECIAL) +_GENERIC_MATH1(tgamma, _CRTSPECIAL) +_GENERIC_MATH1(trunc, _CRTSPECIAL) +_C_STD_END + #endif /* defined(__cplusplus) */ + + #pragma pop_macro("new") + #pragma warning(pop) + #pragma pack(pop) +#endif /* RC_INVOKED */ +#endif /* _XTGMATH */ + +/* + * Copyright (c) 1992-2013 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.40:0009 */ diff --git a/test_data/lots_of_files/xthdloc.c b/test_data/lots_of_files/xthdloc.c new file mode 100644 index 0000000..30846ce --- /dev/null +++ b/test_data/lots_of_files/xthdloc.c @@ -0,0 +1,19 @@ +/*** +*xthdloc.c - sets the __globallocalestatus flag to disable per thread locale +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Sets the __globallocalestatus flag to enable per thread locale. +* Link with this obj to enable per thread locale. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <mtdll.h> +#include <internal.h> +#include <locale.h> +#include <setlocal.h> + +int __globallocalestatus = (~_GLOBAL_LOCALE_BIT) ; + diff --git a/test_data/lots_of_files/xthrow.cpp b/test_data/lots_of_files/xthrow.cpp new file mode 100644 index 0000000..f5c1432 --- /dev/null +++ b/test_data/lots_of_files/xthrow.cpp @@ -0,0 +1,61 @@ +// exception handling support functions +#include <new> +#include <stdexcept> + +_STD_BEGIN +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xbad_alloc()) + { // report a bad_alloc error + _THROW_NCEE(_XSTD bad_alloc, _EMPTY_ARGUMENT); + + } + +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xinvalid_argument(_In_z_ const char *_Message)) + { // report an invalid_argument error + _THROW_NCEE(invalid_argument, _Message); + } + +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xlength_error(_In_z_ const char *_Message)) + { // report a length_error + _THROW_NCEE(length_error, _Message); + } + +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xout_of_range(_In_z_ const char *_Message)) + { // report an out_of_range error + _THROW_NCEE(out_of_range, _Message); + } + +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xoverflow_error(_In_z_ const char *_Message)) + { // report an overflow error + _THROW_NCEE(overflow_error, _Message); + } + +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xruntime_error(_In_z_ const char *_Message)) + { // report a runtime_error + _THROW_NCEE(runtime_error, _Message); + } +_STD_END + + #include <functional> + +_STD_BEGIN +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xbad_function_call()) + { // report a bad_function_call error + _THROW_NCEE(bad_function_call, 0); + } +_STD_END + + #if _HAS_EXCEPTIONS + #include <regex> + +_STD_BEGIN +_CRTIMP2_PURE _NO_RETURN(__CLRCALL_PURE_OR_CDECL _Xregex_error(regex_constants::error_type _Code)) + { // report a regex_error + _THROW_NCEE(regex_error, _Code); + } +_STD_END + #endif /* _HAS_EXCEPTIONS */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xtoa.c b/test_data/lots_of_files/xtoa.c new file mode 100644 index 0000000..3d9cdda --- /dev/null +++ b/test_data/lots_of_files/xtoa.c @@ -0,0 +1,453 @@ +/*** +*xtoa.c - convert integers/longs to ASCII string +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* The module has code to convert integers/longs to ASCII strings. See +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> +#include <limits.h> +#include <tchar.h> +#include <internal.h> +#include <internal_securecrt.h> + +#ifdef _UNICODE +#define xtox_s xtow_s +#define _itox_s _itow_s +#define _ltox_s _ltow_s +#define _ultox_s _ultow_s +#define x64tox_s x64tow_s +#define _i64tox_s _i64tow_s +#define _ui64tox_s _ui64tow_s +#define xtox xtow +#define _itox _itow +#define _ltox _ltow +#define _ultox _ultow +#define x64tox x64tow +#define _i64tox _i64tow +#define _ui64tox _ui64tow +#else /* _UNICODE */ +#define xtox_s xtoa_s +#define _itox_s _itoa_s +#define _ltox_s _ltoa_s +#define _ultox_s _ultoa_s +#define x64tox_s x64toa_s +#define _i64tox_s _i64toa_s +#define _ui64tox_s _ui64toa_s +#define xtox xtoa +#define _itox _itoa +#define _ltox _ltoa +#define _ultox _ultoa +#define x64tox x64toa +#define _i64tox _i64toa +#define _ui64tox _ui64toa +#endif /* _UNICODE */ + +/*** +*char *_itoa_s, *_ltoa_s, *_ultoa_s(val, buf, sizeInTChars, radix) - convert binary int to ASCII +* string +* +*Purpose: +* Converts an int to a character string. +* +*Entry: +* val - number to be converted (int, long or unsigned long) +* char *buf - ptr to buffer to place result +* size_t sizeInTChars - size of the destination buffer +* int radix - base to convert into +* +*Exit: +* Fills in space pointed to by buf with string result. +* Returns the errno_t: err != 0 means that something went wrong, and +* an empty string (buf[0] = 0) is returned. +* +*Exceptions: +* Input parameters and buffer length are validated. +* Refer to the validation section of the function. +* +*******************************************************************************/ + +/* helper routine that does the main job. */ +#ifdef _SECURE_ITOA +static errno_t __stdcall xtox_s + ( + unsigned long val, + TCHAR *buf, + size_t sizeInTChars, + unsigned radix, + int is_neg + ) +#else /* _SECURE_ITOA */ +static void __stdcall xtox + ( + unsigned long val, + TCHAR *buf, + unsigned radix, + int is_neg + ) +#endif /* _SECURE_ITOA */ +{ + TCHAR *p; /* pointer to traverse string */ + TCHAR *firstdig; /* pointer to first digit */ + TCHAR temp; /* temp char */ + unsigned digval; /* value of digit */ +#ifdef _SECURE_ITOA + size_t length; /* current length of the string */ + + /* validation section */ + _VALIDATE_RETURN_ERRCODE(buf != NULL, EINVAL); + _VALIDATE_RETURN_ERRCODE(sizeInTChars > 0, EINVAL); + _RESET_STRING(buf, sizeInTChars); + _VALIDATE_RETURN_ERRCODE(sizeInTChars > (size_t)(is_neg ? 2 : 1), ERANGE); + _VALIDATE_RETURN_ERRCODE(2 <= radix && radix <= 36, EINVAL); + length = 0; + +#endif /* _SECURE_ITOA */ + p = buf; + + if (is_neg) { + /* negative, so output '-' and negate */ + *p++ = _T('-'); +#ifdef _SECURE_ITOA + length++; +#endif /* _SECURE_ITOA */ + val = (unsigned long)(-(long)val); + } + + firstdig = p; /* save pointer to first digit */ + + do { + digval = (unsigned) (val % radix); + val /= radix; /* get next digit */ + + /* convert to ascii and store */ + if (digval > 9) + *p++ = (TCHAR) (digval - 10 + _T('a')); /* a letter */ + else + *p++ = (TCHAR) (digval + _T('0')); /* a digit */ +#ifndef _SECURE_ITOA + } while (val > 0); +#else /* _SECURE_ITOA */ + length++; + } while (val > 0 && length < sizeInTChars); + + /* Check for buffer overrun */ + if (length >= sizeInTChars) + { + buf[0] = '\0'; + _VALIDATE_RETURN_ERRCODE(length < sizeInTChars, ERANGE); + } +#endif /* _SECURE_ITOA */ + /* We now have the digit of the number in the buffer, but in reverse + order. Thus we reverse them now. */ + + *p-- = _T('\0'); /* terminate string; p points to last digit */ + + do { + temp = *p; + *p = *firstdig; + *firstdig = temp; /* swap *p and *firstdig */ + --p; + ++firstdig; /* advance to next two digits */ + } while (firstdig < p); /* repeat until halfway */ +#ifdef _SECURE_ITOA + return 0; +#endif /* _SECURE_ITOA */ +} + +/* Actual functions just call conversion helper with neg flag set correctly, + and return pointer to buffer. */ + +#ifdef _SECURE_ITOA +errno_t __cdecl _itox_s ( + int val, + TCHAR *buf, + size_t sizeInTChars, + int radix + ) +{ + errno_t e = 0; + + if (radix == 10 && val < 0) + e = xtox_s((unsigned long)val, buf, sizeInTChars, radix, 1); + else + e = xtox_s((unsigned long)(unsigned int)val, buf, sizeInTChars, radix, 0); + + return e; +} + +errno_t __cdecl _ltox_s ( + long val, + TCHAR *buf, + size_t sizeInTChars, + int radix + ) +{ + return xtox_s((unsigned long)val, buf, sizeInTChars, radix, (radix == 10 && val < 0)); +} + +errno_t __cdecl _ultox_s ( + unsigned long val, + TCHAR *buf, + size_t sizeInTChars, + int radix + ) +{ + return xtox_s(val, buf, sizeInTChars, radix, 0); +} + +#else /* _SECURE_ITOA */ + +/*** +*char *_itoa, *_ltoa, *_ultoa(val, buf, radix) - convert binary int to ASCII +* string +* +*Purpose: +* Converts an int to a character string. +* +*Entry: +* val - number to be converted (int, long or unsigned long) +* int radix - base to convert into +* char *buf - ptr to buffer to place result +* +*Exit: +* fills in space pointed to by buf with string result +* returns a pointer to this buffer +* +*Exceptions: +* Input parameters are validated. The buffer is assumed to be big enough to +* contain the string. Refer to the validation section of the function. +* +*******************************************************************************/ + +/* Actual functions just call conversion helper with neg flag set correctly, + and return pointer to buffer. */ + +TCHAR * __cdecl _itox ( + int val, + TCHAR *buf, + int radix + ) +{ + if (radix == 10 && val < 0) + xtox((unsigned long)val, buf, radix, 1); + else + xtox((unsigned long)(unsigned int)val, buf, radix, 0); + return buf; +} + +TCHAR * __cdecl _ltox ( + long val, + TCHAR *buf, + int radix + ) +{ + xtox((unsigned long)val, buf, radix, (radix == 10 && val < 0)); + return buf; +} + +TCHAR * __cdecl _ultox ( + unsigned long val, + TCHAR *buf, + int radix + ) +{ + xtox(val, buf, radix, 0); + return buf; +} + +#endif /* _SECURE_ITOA */ + +#ifndef _NO_INT64 + +/*** +*char *_i64toa_s(val, buf, sizeInTChars, radix) - convert binary int to ASCII +* string +* +*Purpose: +* Converts an int64 to a character string. +* +*Entry: +* val - number to be converted +* char *buf - ptr to buffer to place result +* size_t sizeInTChars - size of the destination buffer +* int radix - base to convert into +* +*Exit: +* Fills in space pointed to by buf with string result. +* Returns the errno_t: err != 0 means that something went wrong, and +* an empty string (buf[0] = 0) is returned. +* +*Exceptions: +* Input parameters and buffer length are validated. +* Refer to the validation section of the function. +* +*******************************************************************************/ + +#ifdef _SECURE_ITOA +static errno_t __fastcall x64tox_s + (/* stdcall is faster and smaller... Might as well use it for the helper. */ + unsigned __int64 val, + TCHAR *buf, + size_t sizeInTChars, + unsigned radix, + int is_neg + ) +#else /* _SECURE_ITOA */ +static void __fastcall x64tox + (/* stdcall is faster and smaller... Might as well use it for the helper. */ + unsigned __int64 val, + TCHAR *buf, + unsigned radix, + int is_neg + ) +#endif /* _SECURE_ITOA */ +{ + TCHAR *p; /* pointer to traverse string */ + TCHAR *firstdig; /* pointer to first digit */ + TCHAR temp; /* temp char */ + unsigned digval; /* value of digit */ +#ifdef _SECURE_ITOA + size_t length; /* current length of the string */ + + /* validation section */ + _VALIDATE_RETURN_ERRCODE(buf != NULL, EINVAL); + _VALIDATE_RETURN_ERRCODE(sizeInTChars > 0, EINVAL); + _RESET_STRING(buf, sizeInTChars); + _VALIDATE_RETURN_ERRCODE(sizeInTChars > (size_t)(is_neg ? 2 : 1), ERANGE); + _VALIDATE_RETURN_ERRCODE(2 <= radix && radix <= 36, EINVAL); + length = 0; +#endif /* _SECURE_ITOA */ + p = buf; + + if ( is_neg ) + { + *p++ = _T('-'); /* negative, so output '-' and negate */ +#ifdef _SECURE_ITOA + length++; +#endif /* _SECURE_ITOA */ + val = (unsigned __int64)(-(__int64)val); + } + + firstdig = p; /* save pointer to first digit */ + + do { + digval = (unsigned) (val % radix); + val /= radix; /* get next digit */ + + /* convert to ascii and store */ + if (digval > 9) + *p++ = (TCHAR) (digval - 10 + _T('a')); /* a letter */ + else + *p++ = (TCHAR) (digval + _T('0')); /* a digit */ + +#ifndef _SECURE_ITOA + } while (val > 0); +#else /* _SECURE_ITOA */ + length++; + } while (val > 0 && length < sizeInTChars); + + /* Check for buffer overrun */ + if (length >= sizeInTChars) + { + buf[0] = '\0'; + _VALIDATE_RETURN_ERRCODE(length < sizeInTChars, ERANGE); + } +#endif /* _SECURE_ITOA */ + /* We now have the digit of the number in the buffer, but in reverse + order. Thus we reverse them now. */ + + *p-- = _T('\0'); /* terminate string; p points to last digit */ + + do { + temp = *p; + *p = *firstdig; + *firstdig = temp; /* swap *p and *firstdig */ + --p; + ++firstdig; /* advance to next two digits */ + } while (firstdig < p); /* repeat until halfway */ + +#ifdef _SECURE_ITOA + return 0; +#endif /* _SECURE_ITOA */ +} + +#ifdef _SECURE_ITOA + +/* Actual functions just call conversion helper with neg flag set correctly, + and return pointer to buffer. */ + +errno_t __cdecl _i64tox_s ( + __int64 val, + TCHAR *buf, + size_t sizeInTChars, + int radix + ) +{ + return x64tox_s((unsigned __int64)val, buf, sizeInTChars, radix, (radix == 10 && val < 0)); +} + +errno_t __cdecl _ui64tox_s ( + unsigned __int64 val, + TCHAR *buf, + size_t sizeInTChars, + int radix + ) +{ + return x64tox_s(val, buf, sizeInTChars, radix, 0); +} + +#else /* _SECURE_ITOA */ + +/*** +*char *_i64toa(val, buf, radix) - convert binary int to ASCII +* string +* +*Purpose: +* Converts an int64 to a character string. +* +*Entry: +* val - number to be converted +* int radix - base to convert into +* char *buf - ptr to buffer to place result +* +*Exit: +* fills in space pointed to by buf with string result +* returns a pointer to this buffer +* +*Exceptions: +* Input parameters are validated. The buffer is assumed to be big enough to +* contain the string. Refer to the validation section of the function. +* +*******************************************************************************/ + +/* Actual functions just call conversion helper with neg flag set correctly, + and return pointer to buffer. */ + +TCHAR * __cdecl _i64tox ( + __int64 val, + TCHAR *buf, + int radix + ) +{ + x64tox((unsigned __int64)val, buf, radix, (radix == 10 && val < 0)); + return buf; +} + +TCHAR * __cdecl _ui64tox ( + unsigned __int64 val, + TCHAR *buf, + int radix + ) +{ + x64tox(val, buf, radix, 0); + return buf; +} + +#endif /* _SECURE_ITOA */ + +#endif /* _NO_INT64 */ diff --git a/test_data/lots_of_files/xtoas.c b/test_data/lots_of_files/xtoas.c new file mode 100644 index 0000000..e4ced4f --- /dev/null +++ b/test_data/lots_of_files/xtoas.c @@ -0,0 +1,12 @@ +/*** +*xtoas.c - Secure Version of xtoa.c +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This module is split from xtoa to avoid xtoa.obj pulling in startup code +* +*******************************************************************************/ + +#define _SECURE_ITOA +#include "xtoa.c" diff --git a/test_data/lots_of_files/xtow.c b/test_data/lots_of_files/xtow.c new file mode 100644 index 0000000..41f1924 --- /dev/null +++ b/test_data/lots_of_files/xtow.c @@ -0,0 +1,12 @@ +/*** +*xtow.c - convert integers/longs to wide char string +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* The module has code to convert integers/longs to wide char strings. +* +*******************************************************************************/ + +#define _UNICODE +#include "xtoa.c" diff --git a/test_data/lots_of_files/xtowlower.c b/test_data/lots_of_files/xtowlower.c new file mode 100644 index 0000000..bd2af62 --- /dev/null +++ b/test_data/lots_of_files/xtowlower.c @@ -0,0 +1,37 @@ +/* _Towlower -- convert wchar_t to lower case for Microsoft */ +#include <xlocinfo.h> +#include <wchar.h> +#include <awint.h> +#include <mtdll.h> +#include <setlocal.h> + +_C_STD_BEGIN +_CRTIMP2_PURE wchar_t __CLRCALL_PURE_OR_CDECL _Towlower(wchar_t _Ch, const _Ctypevec *_Ctype) + { /* convert element to lower case */ + wchar_t _Res = _Ch; + + if (_Ch == WEOF) + ; + else if (_Ctype->_LocaleName == NULL && _Ch < 256) + { /* handle ASCII character in C locale */ + if (L'A' <= _Ch && _Ch <= L'Z') + _Res = (wchar_t)(_Ch - L'A' + L'a'); + } + else if (__crtLCMapStringW(_Ctype->_LocaleName, LCMAP_LOWERCASE, + &_Ch, 1, &_Res, 1) == 0) + _Res = _Ch; + return (_Res); + } + +#ifdef MRTDLL +_CRTIMP2_PURE unsigned short __CLRCALL_PURE_OR_CDECL _Towlower(unsigned short _Ch, const _Ctypevec *_Ctype) + { + return _Towlower((wchar_t) _Ch, _Ctype); + } +#endif +_C_STD_END + +/* + * Copyright (c) 1992-2007 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. + V5.03:0009 */ diff --git a/test_data/lots_of_files/xtows.c b/test_data/lots_of_files/xtows.c new file mode 100644 index 0000000..b4d8285 --- /dev/null +++ b/test_data/lots_of_files/xtows.c @@ -0,0 +1,13 @@ +/*** +*xtows.c - Secure Version of xtow.c +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* This module is split from xtow to avoid xtow.obj pulling in startup code +* +*******************************************************************************/ + +#define _SECURE_ITOA +#define _UNICODE +#include "xtoa.c" diff --git a/test_data/lots_of_files/xtowupper.c b/test_data/lots_of_files/xtowupper.c new file mode 100644 index 0000000..f59ba04 --- /dev/null +++ b/test_data/lots_of_files/xtowupper.c @@ -0,0 +1,38 @@ +/* _Towupper -- convert wchar_t to upper case for Microsoft */ +#include <xlocinfo.h> +#include <wchar.h> +#include <awint.h> +#include <mtdll.h> +#include <setlocal.h> + +_C_STD_BEGIN +_CRTIMP2_PURE wchar_t __CLRCALL_PURE_OR_CDECL _Towupper(wchar_t _Ch, + const _Ctypevec *_Ctype) + { /* convert element to upper case */ + wchar_t _Res = _Ch; + + if (_Ch == WEOF) + ; + else if (_Ctype->_LocaleName == NULL && _Ch < 256) + { /* handle ASCII character in C locale */ + if (L'a' <= _Ch && _Ch <= L'z') + _Res = (wchar_t)(_Ch - L'a' + L'A'); + } + else if (__crtLCMapStringW(_Ctype->_LocaleName, LCMAP_UPPERCASE, + &_Ch, 1, &_Res, 1) == 0) + _Res = _Ch; + return (_Res); + } +#ifdef MRTDLL +_CRTIMP2_PURE unsigned short __CLRCALL_PURE_OR_CDECL _Towupper(unsigned short _Ch, + const _Ctypevec *_Ctype) + { + return _Towupper((wchar_t) _Ch, _Ctype); + } +#endif +_C_STD_END + +/* + * Copyright (c) 1992-2007 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. + V5.03:0009 */ diff --git a/test_data/lots_of_files/xtxtmode.c b/test_data/lots_of_files/xtxtmode.c new file mode 100644 index 0000000..c8a3354 --- /dev/null +++ b/test_data/lots_of_files/xtxtmode.c @@ -0,0 +1,21 @@ +/*** +*xtxtmode.c - set global text mode flag +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Sets the global file mode to text. This is the default. +* +* This is a special version for the DLL model. This object goes into +* MSVCRT.LIB (and therefore into the client EXE) and not into the +* CRTL in a DLL (MSVCRTXX.DLL). It is identical to txtmode.obj +* except that the latter has a DLL export definition in the DLL model. +* +*******************************************************************************/ + +#define SPECIAL_CRTEXE /* turn off _CRTIMP for decl. of _fmode */ + +#include <cruntime.h> +#include <stdlib.h> + +int _fmode = 0; /* set text mode */ diff --git a/test_data/lots_of_files/xvalues.c b/test_data/lots_of_files/xvalues.c new file mode 100644 index 0000000..296b953 --- /dev/null +++ b/test_data/lots_of_files/xvalues.c @@ -0,0 +1,45 @@ +/* values used by math functions -- IEEE 754 version */ +#if defined(_M_CEE_PURE) +#if defined(MRTDLL) +#undef MRTDLL +#endif +#if defined(MRTDLL) +#undef CRTDLL +#endif +#endif + +#include "xmath.h" +_C_STD_BEGIN + + /* macros */ +#define NBITS (48 + _DOFF) + + #if _D0 == 0 + #define INIT(w0) {w0, 0, 0, 0} + #define INIT2(w0, w1) {w0, 0, 0, w1} + + #else /* _D0 == 0 */ + #define INIT(w0) {0, 0, 0, w0} + #define INIT2(w0, w1) {w1, 0, 0, w0} + #endif /* _D0 == 0 */ + + /* static data */ +extern /* const */ _Dconst _Denorm = {INIT2(0, 1)}; +extern /* const */ _Dconst _Eps = { + INIT((_DBIAS - NBITS - 1) << _DOFF)}; +extern /* const */ _Dconst _Hugeval = {INIT(_DMAX << _DOFF)}; +extern /* const */ _Dconst _Inf = {INIT(_DMAX << _DOFF)}; +extern /* const */ _Dconst _Nan = {INIT((_DMAX << _DOFF) + | (1 << (_DOFF - 1)))}; +extern /* const */ _Dconst _Snan = {INIT2(_DMAX << _DOFF, 1)}; +extern /* const */ _Dconst _Rteps = { + INIT((_DBIAS - NBITS / 2) << _DOFF)}; + +extern /* const */ double _Xbig = (NBITS + 1) * 347L / 1000; +extern /* const */ double _Zero = 0.0; +_C_STD_END + +/* + * Copyright (c) 1992-2007 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. + V5.03:0009 */ diff --git a/test_data/lots_of_files/xwcscoll.c b/test_data/lots_of_files/xwcscoll.c new file mode 100644 index 0000000..674a93b --- /dev/null +++ b/test_data/lots_of_files/xwcscoll.c @@ -0,0 +1,138 @@ +/*** +*xwcscoll.c - Collate wide-character locale strings +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Compare two wchar_t strings using the locale LC_COLLATE information. +* +*******************************************************************************/ + + +#include <cruntime.h> +#include <windows.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> +#include <mtdll.h> +#include <errno.h> +#include <awint.h> +#include <xlocinfo.h> /* for _Collvec, _Wcscoll */ + +/*** +*static int _Wmemcmp(s1, s2, n) - compare wchar_t s1[n], s2[n] +* +*Purpose: +* +*Entry: +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +static int __CLRCALL_PURE_OR_CDECL _Wmemcmp( + const wchar_t *s1, + const wchar_t *s2, + int n + ) +{ + for (; 0 < n; ++s1, ++s2, --n) + if (*s1 != *s2) + return (*s1 < *s2 ? -1 : +1); + return (0); +} + +/*** +*int _Wcscoll() - Collate wide-character locale strings +* +*Purpose: +* Compare two wchar_t strings using the locale LC_COLLATE information. +* In the C locale, wcscmp() is used to make the comparison. +* +*Entry: +* const wchar_t *_string1 = pointer to beginning of the first string +* const wchar_t *_end1 = pointer past end of the first string +* const wchar_t *_string2 = pointer to beginning of the second string +* const wchar_t *_end2 = pointer past end of the second string +* const _Collvec *ploc = pointer to locale info +* +*Exit: +* -1 = first string less than second string +* 0 = strings are equal +* 1 = first string greater than second string +* This range of return values may differ from other *cmp/*coll functions. +* +*Exceptions: +* _NLSCMPERROR = error +* errno = EINVAL +* +*******************************************************************************/ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Wcscoll ( + const wchar_t *_string1, + const wchar_t *_end1, + const wchar_t *_string2, + const wchar_t *_end2, + const _Collvec *ploc + ) +{ + int n1 = (int)(_end1 - _string1); + int n2 = (int)(_end2 - _string2); + int ret=0; + const wchar_t *locale_name; + + if (ploc == 0) + locale_name = ___lc_locale_name_func()[LC_COLLATE]; + else + { + locale_name = ploc->_LocaleName; + } + + if (locale_name == NULL) + { + int ans; + ans = _Wmemcmp(_string1, _string2, n1 < n2 ? n1 : n2); + ret=(ans != 0 || n1 == n2 ? ans : n1 < n2 ? -1 : +1); + } + else + { + if (0 == (ret = __crtCompareStringW(locale_name, + SORT_STRINGSORT, + _string1, + n1, + _string2, + n2))) + { + errno = EINVAL; + ret=_NLSCMPERROR; + } + else + { + ret-=2; + } + } + + return ret; +} + +#ifdef MRTDLL +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Wcscoll ( + const unsigned short *_string1, + const unsigned short *_end1, + const unsigned short *_string2, + const unsigned short *_end2, + const _Collvec *ploc + ) + { + return _Wcscoll( + (const wchar_t *)_string1, + (const wchar_t *)_end1, + (const wchar_t *)_string2, + (const wchar_t *)_end2, + ploc); + } +#endif /* MRTDLL */ diff --git a/test_data/lots_of_files/xwcsxfrm.c b/test_data/lots_of_files/xwcsxfrm.c new file mode 100644 index 0000000..13b2b99 --- /dev/null +++ b/test_data/lots_of_files/xwcsxfrm.c @@ -0,0 +1,159 @@ +/*** +*xwcsxfrm.c - Transform a wide-character string using locale information +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* +*Purpose: +* Transform a wide-character string using the locale information as set by +* LC_COLLATE. +* +*******************************************************************************/ + + +#include <cruntime.h> +#include <windows.h> +#include <string.h> +#include <limits.h> +#include <locale.h> +#include <mtdll.h> +#include <setlocal.h> +#include <stdlib.h> +#include <mtdll.h> +#include <awint.h> +#include <dbgint.h> +#include <xlocinfo.h> /* for _Collvec, _Wcsxfrm */ + +/*** +*size_t _Wcsxfrm() - Transform a string using locale information +* +*Purpose: +* Transform the wide string pointed to by _string2 and place the +* resulting wide string into the array pointed to by _string1. +* No more than _end1 - _string1 wide characters are placed into the +* resulting string (including the null). +* +* The transformation is such that if wcscmp() is applied to +* the two transformed strings, the return value is equal to +* the result of wcscoll() applied to the two original strings. +* Thus, the conversion must take the locale LC_COLLATE info +* into account. +* +* In the C locale, wcsxfrm() simply resolves to wcsncpy()/wcslen(). +* +*Entry: +* wchar_t *_string1 = pointer to beginning of result string +* wchar_t *_end1 = pointer past end of result string +* const wchar_t *_string2 = pointer to beginning of source string +* const wchar_t *_end2 = pointer past end of source string +* const _Collvec *ploc = pointer to locale info +* +*Exit: +* Length of the transformed string. +* If the value returned is too big, the contents of the +* _string1 array are indeterminate. +* +*Exceptions: +* Non-standard: if OM/API error, return INT_MAX. +* +*******************************************************************************/ + +_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Wcsxfrm ( + wchar_t *_string1, + wchar_t *_end1, + const wchar_t *_string2, + const wchar_t *_end2, + const _Collvec *ploc + ) +{ + size_t _n1 = _end1 - _string1; + size_t _n2 = _end2 - _string2; + size_t size = (size_t)-1; + unsigned char *bbuffer=NULL; + const wchar_t *locale_name; + + if (ploc == 0) + { + locale_name = ___lc_locale_name_func()[LC_COLLATE]; + } + else + { + locale_name = ploc->_LocaleName; + } + + if (locale_name == NULL) + { + if (_n2 <= _n1) + { + memcpy(_string1, _string2, _n2 * sizeof (wchar_t)); + } + size=_n2; + } + else + { + + /* + * When using LCMAP_SORTKEY, LCMapStringW handles BYTES not wide + * chars. We use a byte buffer to hold bytes and then convert the + * byte string to a wide char string and return this so it can be + * compared using wcscmp(). User's buffer is _n1 wide chars, so + * use an internal buffer of _n1 bytes. + */ + + if (NULL != (bbuffer = (unsigned char *)_malloc_crt(_n1))) + { + if (0 == (size = __crtLCMapStringW(locale_name, + LCMAP_SORTKEY, + _string2, + (int)_n2, + (wchar_t *)bbuffer, + (int)_n1))) + { + /* buffer not big enough, get size required. */ + + if (0 == (size = __crtLCMapStringW(locale_name, + LCMAP_SORTKEY, + _string2, + (int)_n2, + NULL, + 0))) + { + size = INT_MAX; /* default error */ + } + } else { + size_t i; + /* string successfully mapped, convert to wide char */ + + for (i = 0; i < size; i++) + { + _string1[i] = (wchar_t)bbuffer[i]; + } + } + } + } + + if(bbuffer) + { + _free_crt(bbuffer); + } + + return (size_t)size; +} + +#ifdef MRTDLL +_CRTIMP2_PURE size_t __CLRCALL_PURE_OR_CDECL _Wcsxfrm ( + unsigned short *_string1, + unsigned short *_end1, + const unsigned short *_string2, + const unsigned short *_end2, + const _Collvec *ploc + ) + { + return _Wcsxfrm( + (wchar_t *)_string1, + (wchar_t *)_end1, + (const wchar_t *)_string2, + (const wchar_t *)_end2, + ploc); + } +#endif /* MRTDLL */ diff --git a/test_data/lots_of_files/xwctomb.c b/test_data/lots_of_files/xwctomb.c new file mode 100644 index 0000000..f44dde4 --- /dev/null +++ b/test_data/lots_of_files/xwctomb.c @@ -0,0 +1,147 @@ +/*** +*xwctomb.c - Convert wide character to multibyte character, with locale. +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +*Purpose: +* Convert a wide character into the equivalent multibyte character. +* +*******************************************************************************/ + +#include <cruntime.h> +#include <stdlib.h> +#include <mtdll.h> +#include <errno.h> +#include <limits.h> /* for MB_LEN_MAX */ +#include <string.h> /* for memcpy */ +#include <stdio.h> /* for EOF */ +#include <xlocinfo.h> /* for _Cvtvec, _Wcrtomb */ +#include <locale.h> +#include <setlocal.h> +#include <internal.h> +#include <mbctype.h> + +/*** +*int _Wcrtomb() - Convert wide character to multibyte character. +* +*Purpose: +* Convert a wide character into the equivalent multi-byte character, +* according to the specified LC_CTYPE category, or the current locale. +* [ANSI]. +* +* NOTE: Currently, the C libraries support the "C" locale only. +* Non-C locale support now available under _INTL switch. +*Entry: +* char *s = pointer to multibyte character +* wchar_t wchar = source wide character +* mbstate_t *pst = pointer to state (not used) +* const _Cvtvec *ploc = pointer to locale info +* +*Exit: +* Returns: +* -1 (if error) or number of bytes comprising converted mbc +* +*Exceptions: +* +*******************************************************************************/ + +/* Retained for backward compatibility of DLL exports only */ +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL __Wcrtomb_lk + ( + char *s, + wchar_t wchar, + mbstate_t *pst, + const _Cvtvec *ploc + ) +{ + return _Wcrtomb(s, wchar, pst, ploc); +} + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Wcrtomb + ( + char *s, + wchar_t wchar, + mbstate_t *pst, + const _Cvtvec *ploc + ) +{ + if (ploc->_Isclocale) + { + if ( wchar > 255 ) /* validate high byte */ + { + errno = EILSEQ; + return -1; + } + + *s = (char) wchar; + return sizeof(char); + } else { + int size; + BOOL defused = 0; + _Cvtvec cvtvec; + + if (ploc == 0) + { + cvtvec = _Getcvt(); + ploc = &cvtvec; + } + + if ( ((size = WideCharToMultiByte(ploc->_Page, + 0, + &wchar, + 1, + s, + ploc->_Mbcurmax, + NULL, + &defused)) == 0) || + (defused) ) + { + errno = EILSEQ; + return -1; + } + + return size; + } +} + +#ifdef MRTDLL +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _Wcrtomb + ( + char *s, + unsigned short wchar, + mbstate_t *pst, + const _Cvtvec *ploc + ) + { + return _Wcrtomb(s,(wchar_t) wchar, pst, ploc); + } +#endif /* MRTDLL */ + +/*** +*_Cvtvec _Getcvt() - get conversion info for current locale +* +*Purpose: +* +*Entry: +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +_CRTIMP2_PURE _Cvtvec __CLRCALL_PURE_OR_CDECL _Getcvt() +{ + _Cvtvec cvt = {0}; + int idx; + + cvt._Page = ___lc_codepage_func(); + cvt._Mbcurmax = ___mb_cur_max_func(); + cvt._Isclocale = ___lc_locale_name_func()[LC_CTYPE] == NULL; + + if (!cvt._Isclocale) + for (idx = 0; idx < 256; ++idx) + if (_ismbblead(idx)) + cvt._Isleadbyte[idx >> 3] |= 1 << (idx & 7); + return (cvt); +} diff --git a/test_data/lots_of_files/xwstod.c b/test_data/lots_of_files/xwstod.c new file mode 100644 index 0000000..9226236 --- /dev/null +++ b/test_data/lots_of_files/xwstod.c @@ -0,0 +1,28 @@ +/* _WStod function */ +#include <stdlib.h> +#include <wchar.h> +#include "xmath.h" +#include "xxwctype.h" +#include "xxdftype.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL _WStodx(const CTYPE *s, CTYPE **endptr, long pten, + int *perr) + #include "xxstod.h" + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL _WStod(const CTYPE *s, CTYPE **endptr, long pten) + { /* convert string, discard error code */ + return (_WStodx(s, endptr, pten, 0)); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xwstof.c b/test_data/lots_of_files/xwstof.c new file mode 100644 index 0000000..6ba5b31 --- /dev/null +++ b/test_data/lots_of_files/xwstof.c @@ -0,0 +1,27 @@ +/* _WStof function */ +#include <wchar.h> +#include "xmath.h" +#include "xxwctype.h" +#include "xxfftype.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL _WStofx(const CTYPE *s, CTYPE **endptr, long pten, + int *perr) + #include "xxstod.h" + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL _WStof(const CTYPE *s, CTYPE **endptr, long pten) + { /* convert string, discard error code */ + return (_WStofx(s, endptr, pten, 0)); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xwstoflt.c b/test_data/lots_of_files/xwstoflt.c new file mode 100644 index 0000000..1763b30 --- /dev/null +++ b/test_data/lots_of_files/xwstoflt.c @@ -0,0 +1,109 @@ +/* _WStoflt function */ +#include <locale.h> +#include <wchar.h> +#include <wctype.h> +#include "xmath.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +#define BASE 10 /* decimal */ +#define NDIG 9 /* decimal digits per long element */ +#define MAXSIG (5 * NDIG) /* maximum significant digits to keep */ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _WStoflt(const wchar_t *s0, const wchar_t *s, + wchar_t **endptr, long lo[], int maxsig) + { /* convert wide string to array of long plus exponent */ + char buf[MAXSIG + 1]; /* worst case, with room for rounding digit */ + int nsig; /* number of significant digits seen */ + int seen; /* any valid field characters seen */ + int word = 0; /* current long word to fill */ + + maxsig *= NDIG; /* convert word count to digit count */ + if (MAXSIG < maxsig) + maxsig = MAXSIG; /* protect against bad call */ + + lo[0] = 0; /* power of ten exponent */ + lo[1] = 0; /* first NDIG-digit word of fraction */ + + for (seen = 0; *s == L'0'; ++s, seen = 1) + ; /* strip leading zeros */ + for (nsig = 0; iswdigit(*s); ++s, seen = 1) + if (nsig <= maxsig) + buf[nsig++] = (char)(*s - L'0'); /* accumulate a digit */ + else + ++lo[0]; /* too many digits, just scale exponent */ + + if (*s == btowc(localeconv()->decimal_point[0])) + ++s; + if (nsig == 0) + for (; *s == L'0'; ++s, seen = 1) + --lo[0]; /* scale for stripped zeros after point */ + for (; iswdigit(*s); ++s, seen = 1) + if (nsig <= maxsig) + { /* accumulate a fraction digit */ + buf[nsig++] = (char)(*s - L'0'); + --lo[0]; + } + + if (maxsig < nsig) + { /* discard excess digit after rounding up */ + unsigned int ms = maxsig; /* to quiet warnings */ + + if (BASE / 2 <= buf[ms]) + ++buf[ms - 1]; /* okay if digit becomes BASE */ + nsig = maxsig; + ++lo[0]; + } + for (; 0 < nsig && buf[nsig - 1] == '\0'; --nsig) + ++lo[0]; /* discard trailing zeros */ + if (nsig == 0) + buf[nsig++] = '\0'; /* ensure at least one digit */ + + if (seen) + { /* convert digit sequence to words */ + int bufidx = 0; /* next digit in buffer */ + int wordidx = NDIG - nsig % NDIG; /* next digit in word (% NDIG) */ + + word = wordidx % NDIG == 0 ? 0 : 1; + for (; bufidx < nsig; ++wordidx, ++bufidx) + if (wordidx % NDIG == 0) + lo[++word] = buf[bufidx]; + else + lo[word] = lo[word] * BASE + buf[bufidx]; + + if (*s == L'e' || *s == L'E') + { /* parse exponent */ + const wchar_t *ssav = s; + const wchar_t esign = *++s == L'+' || *s == L'-' + ? *s++ : L'+'; + int eseen = 0; + long lexp = 0; + + for (; iswdigit(*s); ++s, eseen = 1) + if (lexp < 100000000) /* else overflow */ + lexp = lexp * 10 + *s - L'0'; + if (esign == L'-') + lexp = -lexp; + lo[0] += lexp; + if (!eseen) + s = ssav; /* roll back if incomplete exponent */ + } + } + + if (!seen) + word = 0; /* return zero if bad parse */ + if (endptr) + *endptr = (wchar_t *)(seen ? s : s0); /* roll back if bad parse */ + return (word); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xwstold.c b/test_data/lots_of_files/xwstold.c new file mode 100644 index 0000000..65e2645 --- /dev/null +++ b/test_data/lots_of_files/xwstold.c @@ -0,0 +1,27 @@ +/* _WStold function */ +#include <wchar.h> +#include "xmath.h" +#include "xxwctype.h" +#include "xxlftype.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL _WStoldx(const CTYPE *s, CTYPE **endptr, long pten, + int *perr) + #include "xxstod.h" + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL _WStold(const CTYPE *s, CTYPE **endptr, long pten) + { /* convert string, discard error code */ + return (_WStoldx(s, endptr, pten, 0)); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xwstopfx.c b/test_data/lots_of_files/xwstopfx.c new file mode 100644 index 0000000..83457ec --- /dev/null +++ b/test_data/lots_of_files/xwstopfx.c @@ -0,0 +1,95 @@ +/* _WStopfx function */ +#include <wctype.h> +#include "xmath.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _WStopfx(const wchar_t **ps, wchar_t **endptr) + { /* parse prefix of floating-point field */ + const wchar_t *s = *ps; + int code = 0; + + for (; iswspace(*s); ++s) + ; + if (*s == L'-') + code = FL_NEG, ++s; + else if (*s == L'+') + ++s; + if (*s == L'n' || *s == L'N') + { /* parse "nan" or fail */ + if ((*++s != L'a' && *s != L'A') + || (*++s != L'n' && *s != L'N')) + { /* parse failed, roll back pointer */ + s = *ps; + code = FL_ERR; + } + else + { /* parse optional (n-char-sequence) */ + const wchar_t *q = ++s; + + code = FL_NAN; + if (*q == L'(') + { /* got L'(', skip through L')' */ + for (; iswalnum(*++q) || *q == L'_'; ) + ; + if (*q == L')') + s = ++q; + } + } + if (endptr != 0) + *endptr = (wchar_t *)s; + } + else if (*s == L'i' || *s == L'I') + { /* parse "inf" or fail */ + if ((*++s != L'n' && *s != L'N') + || (*++s != L'f' && *s != L'F')) + { /* parse failed, roll back pointer */ + s = *ps; + code = FL_ERR; + } + else + { /* parse optional rest of L"infinity" */ + const wchar_t *q = ++s; + + code |= FL_INF; + if ((*q == L'i' || *q == L'I') + && (*++q == L'n' || *q == L'N') + && (*++q == L'i' || *q == L'I') + && (*++q == L't' || *q == L'T') + && (*++q == L'y' || *q == L'Y')) + s = ++q; + } + if (endptr != 0) + *endptr = (wchar_t *)s; + } + +/* #if _IS_C9X */ + else if (*s == L'0' && (s[1] == L'x' || s[1] == L'X')) + { /* test for valid hex field following 0x or 0X */ + const wchar_t *s1 = s + 2; + + if (*s1 == L'.') + ++s1; + if (!iswxdigit(*s1)) + code |= FL_DEC; + else + s += 2, code |= FL_HEX; + } +/* #endif _IS_C9X */ + + else + code |= FL_DEC; + *ps = s; + return (code); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xwstoxfl.c b/test_data/lots_of_files/xwstoxfl.c new file mode 100644 index 0000000..74e566e --- /dev/null +++ b/test_data/lots_of_files/xwstoxfl.c @@ -0,0 +1,126 @@ +/* _WStoxflt function */ +#include <ctype.h> +#include <locale.h> +#include <wchar.h> +#include <wctype.h> +#include "xmath.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + +#define BASE 16 /* hexadecimal */ +#define NDIG 7 /* hexadecimal digits per long element */ +#define MAXSIG (5 * NDIG) /* maximum significant digits to keep */ + +_CRTIMP2_PURE int __CLRCALL_PURE_OR_CDECL _WStoxflt(const wchar_t *s0, const wchar_t *s, + wchar_t **endptr, long lo[], int maxsig) + { /* convert wide string to array of long plus exponent */ + char buf[MAXSIG + 1]; /* worst case, with room for rounding digit */ + int nsig; /* number of significant digits seen */ + int seen; /* any valid field characters seen */ + int word = 0; /* current long word to fill */ + + const wchar_t *pd; + static const wchar_t digits[] = + { /* hex digits in both cases */ + L'0', L'1', L'2', L'3', + L'4', L'5', L'6', L'7', + L'8', L'9', L'a', L'b', + L'c', L'd', L'e', L'f', + L'A', L'B', L'C', L'D', + L'E', L'F', L'\0'}; + static const wchar_t vals[] = + { /* values of hex digits */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 10, 11, 12, 13, 14, 15}; + + maxsig *= NDIG; /* convert word count to digit count */ + if (MAXSIG < maxsig) + maxsig = MAXSIG; /* protect against bad call */ + + lo[0] = 0; /* power of ten exponent */ + lo[1] = 0; /* first NDIG-digit word of fraction */ + + for (seen = 0; *s == L'0'; ++s, seen = 1) + ; /* strip leading zeros */ + for (nsig = 0; + (pd = (wchar_t *)wmemchr(&digits[0], *s, 22)) != 0; ++s, seen = 1) + if (nsig <= maxsig) + buf[nsig++] = vals[pd - digits]; /* accumulate a digit */ + else + ++lo[0]; /* too many digits, just scale exponent */ + + if (*s == localeconv()->decimal_point[0]) + ++s; + if (nsig == 0) + for (; *s == '0'; ++s, seen = 1) + --lo[0]; /* strip zeros after point */ + for (; (pd = (wchar_t *)wmemchr(&digits[0], *s, 22)) != 0; ++s, seen = 1) + if (nsig <= maxsig) + { /* accumulate a fraction digit */ + buf[nsig++] = vals[pd - digits]; + --lo[0]; + } + + if (maxsig < nsig) + { /* discard excess digit after rounding up */ + unsigned int ms = maxsig; /* to quiet warnings */ + + if (BASE / 2 <= buf[ms]) + ++buf[ms - 1]; /* okay if digit becomes BASE */ + nsig = maxsig; + ++lo[0]; + } + for (; 0 < nsig && buf[nsig - 1] == '\0'; --nsig) + ++lo[0]; /* discard trailing zeros */ + if (nsig == 0) + buf[nsig++] = '\0'; /* ensure at least one digit */ + lo[0] <<= 2; /* change hex exponent to binary exponent */ + + if (seen) + { /* convert digit sequence to words */ + int bufidx = 0; /* next digit in buffer */ + int wordidx = NDIG - nsig % NDIG; /* next digit in word (% NDIG) */ + + word = wordidx % NDIG == 0 ? 0 : 1; + for (; bufidx < nsig; ++wordidx, ++bufidx) + if (wordidx % NDIG == 0) + lo[++word] = buf[bufidx]; + else + lo[word] = lo[word] * BASE + buf[bufidx]; + + if (*s == L'p' || *s == L'P') + { /* parse exponent */ + const wchar_t *ssav = s; + const wchar_t esign = *++s == L'+' || *s == L'-' + ? *s++ : L'+'; + int eseen = 0; + long lexp = 0; + + for (; iswdigit(*s); ++s, eseen = 1) + if (lexp < 100000000) /* else overflow */ + lexp = lexp * 10 + *s - L'0'; + if (esign == '-') + lexp = -lexp; + lo[0] += lexp; + if (!eseen) + s = ssav; /* roll back if incomplete exponent */ + } + } + + if (!seen) + word = 0; /* return zero if bad parse */ + if (endptr) + *endptr = (wchar_t *)(seen ? s : s0); /* roll back if bad parse */ + return (word); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxcctype.h b/test_data/lots_of_files/xxcctype.h new file mode 100644 index 0000000..a6ea8ee --- /dev/null +++ b/test_data/lots_of_files/xxcctype.h @@ -0,0 +1,9 @@ +/* xxcctype.h -- parameters for char character type */ + +#define CTYPE char +#define CNAME(fun) _##fun + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxdftype.h b/test_data/lots_of_files/xxdftype.h new file mode 100644 index 0000000..ce98c43 --- /dev/null +++ b/test_data/lots_of_files/xxdftype.h @@ -0,0 +1,42 @@ +/* xxdftype.h -- parameters for double floating-point type */ +#include <yvals.h> +#include <float.h> + +#define FTYPE double +#define FCTYPE _Dcomplex +#define FBITS DBL_MANT_DIG +#define FEPS DBL_EPSILON +#define FMAX DBL_MAX +#define FMIN DBL_MIN +#define FMAXEXP DBL_MAX_EXP + +#define FFUN(fun) fun +#define FMACRO(x) x +#define FNAME(fun) _##fun +#define FCONST(obj) _##obj._Double +#define FLIT(lit) lit + +#define FDIV(x, y) _FDIV(x, y) +#define FINVERT(x) _FINVERT(x) + +#define FPMSW(x) (*_Pmsw(&(x))) +#define FSIGNBIT _DSIGN + +#define FISNEG(x) (FPMSW(x) & FSIGNBIT) +#define FMAKENEG(x) (FPMSW(x) |= FSIGNBIT) +#define FMAKEPOS(x) (FPMSW(x) &= ~FSIGNBIT) +#define FNEGATE(x) (FPMSW(x) ^= FSIGNBIT) + +#define FSETLSB(x) (*_Plsw(&(x)) |= 1) + + #if _IS_EMBEDDED +#define FCPTYPE double_complex + + #else /* _IS_EMBEDDED */ +#define FCPTYPE complex<double> + #endif /* _IS_EMBEDDED */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxfftype.h b/test_data/lots_of_files/xxfftype.h new file mode 100644 index 0000000..eca7629 --- /dev/null +++ b/test_data/lots_of_files/xxfftype.h @@ -0,0 +1,42 @@ +/* xxfftype.h -- parameters for float floating-point type */ +#include <yvals.h> +#include <float.h> + +#define FTYPE float +#define FCTYPE _Fcomplex +#define FBITS FLT_MANT_DIG +#define FEPS FLT_EPSILON +#define FMAX FLT_MAX +#define FMIN FLT_MIN +#define FMAXEXP FLT_MAX_EXP + +#define FFUN(fun) fun##f +#define FMACRO(x) F##x +#define FNAME(fun) _F##fun +#define FCONST(obj) _F##obj._Float +#define FLIT(lit) lit##F + +#define FDIV(x, y) _FDIV(x, y) +#define FINVERT(x) _FINVERT(x) + +#define FPMSW(x) (*_FPmsw(&(x))) +#define FSIGNBIT _FSIGN + +#define FISNEG(x) (FPMSW(x) & FSIGNBIT) +#define FMAKENEG(x) (FPMSW(x) |= FSIGNBIT) +#define FMAKEPOS(x) (FPMSW(x) &= ~FSIGNBIT) +#define FNEGATE(x) (FPMSW(x) ^= FSIGNBIT) + +#define FSETLSB(x) (*_FPlsw(&(x)) |= 1) + + #if _IS_EMBEDDED +#define FCPTYPE float_complex + + #else /* _IS_EMBEDDED */ +#define FCPTYPE complex<float> + #endif /* _IS_EMBEDDED */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxlftype.h b/test_data/lots_of_files/xxlftype.h new file mode 100644 index 0000000..fd9ed0c --- /dev/null +++ b/test_data/lots_of_files/xxlftype.h @@ -0,0 +1,42 @@ +/* xxlftype.h -- parameters for long double floating-point type */ +#include <yvals.h> +#include <float.h> + +#define FTYPE long double +#define FCTYPE _Lcomplex +#define FBITS LDBL_MANT_DIG +#define FEPS LDBL_EPSILON +#define FMAX LDBL_MAX +#define FMIN LDBL_MIN +#define FMAXEXP LDBL_MAX_EXP + +#define FFUN(fun) fun##l +#define FMACRO(x) L##x +#define FNAME(fun) _L##fun +#define FCONST(obj) _L##obj._Long_double +#define FLIT(lit) lit##L + +#define FDIV(x, y) _FDIV(x, y) +#define FINVERT(x) _FINVERT(x) + +#define FPMSW(x) (*_LPmsw(&(x))) +#define FSIGNBIT _LSIGN + +#define FISNEG(x) (FPMSW(x) & FSIGNBIT) +#define FMAKENEG(x) (FPMSW(x) |= FSIGNBIT) +#define FMAKEPOS(x) (FPMSW(x) &= ~FSIGNBIT) +#define FNEGATE(x) (FPMSW(x) ^= FSIGNBIT) + +#define FSETLSB(x) (*_LPlsw(&(x)) |= 1) + + #if _IS_EMBEDDED +#define FCPTYPE float_complex /* place holder */ + + #else /* _IS_EMBEDDED */ +#define FCPTYPE complex<long double> + #endif /* _IS_EMBEDDED */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxstod.h b/test_data/lots_of_files/xxstod.h new file mode 100644 index 0000000..02a76d5 --- /dev/null +++ b/test_data/lots_of_files/xxstod.h @@ -0,0 +1,84 @@ +/* xxstod.h -- _[W]Sto[d f ld] common functionality */ + + #pragma warning(disable: 4210) + + /* macros */ +#define ACSIZE 3 /* size of extended-precision accumulators */ + +#define D16TO7 FLIT(268435456.0) /* 16^7 */ +#define D10TO9 FLIT(1e9) /* 10^9 */ +#define NLONG ((FBITS + 27) / 28) /* 7 * NLONG == max hexadecimal digits */ + +/* +FTYPE _Stodx(const CTYPE *s, CTYPE **endptr, long pten, int *perr) + */ + { /* convert string to FTYPE, with checking */ + FTYPE x; + long lo[NLONG + 1]; + const CTYPE *s0 = s; + int code = CNAME(Stopfx)(&s, endptr); + const int neg = code & FL_NEG; + + extern _CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL FNAME(Dtento)(FTYPE *, long, int *); + + if (perr != 0) + *perr = 0; + if ((code &= ~FL_NEG) == FL_DEC) + { /* parse decimal format */ + const int nlo = CNAME(Stoflt)(s0, s, endptr, lo, NLONG); + FTYPE xpx[ACSIZE], xpf[ACSIZE]; + int i; + + FNAME(Xp_setw)(xpf, ACSIZE, D10TO9); + if (nlo == 0) + FNAME(Xp_setw)(xpx, ACSIZE, 0.0); + else + for (i = 1, FNAME(Xp_setn)(xpx, ACSIZE, lo[1]); i < nlo; ) + { /* x = x * D10TO9 + (FTYPE)lo[++i] */ + FTYPE xpa[ACSIZE]; + FTYPE xpt[ACSIZE * 2]; + + FNAME(Xp_mulx)(xpx, ACSIZE, xpf, ACSIZE, xpt); + FNAME(Xp_setn)(xpa, ACSIZE, lo[++i]); + FNAME(Xp_addx)(xpx, ACSIZE, xpa, ACSIZE); + } + x = FNAME(Dtento)(xpx, pten + lo[0], perr); + } + else if (code == FL_HEX) + { /* parse hexadecimal format */ + const int nlo = CNAME(Stoxflt)(s0, s, endptr, lo, NLONG); + FTYPE xpx[ACSIZE], xpf[ACSIZE]; + int i; + + FNAME(Xp_setw)(xpf, ACSIZE, D16TO7); + if (nlo == 0) + FNAME(Xp_setw)(xpx, ACSIZE, 0.0); + else + for (i = 1, FNAME(Xp_setn)(xpx, ACSIZE, lo[1]); i < nlo; ) + { /* x = x * D10TO9 + (FTYPE)lo[++i] */ + FTYPE xpa[ACSIZE]; + FTYPE xpt[ACSIZE * 2]; + + FNAME(Xp_mulx)(xpx, ACSIZE, xpf, ACSIZE, xpt); + FNAME(Xp_setn)(xpa, ACSIZE, lo[++i]); + FNAME(Xp_addx)(xpx, ACSIZE, xpa, ACSIZE); + } + x = FNAME(Dtento)(xpx, pten, perr); + FNAME(Dscale)(&x, lo[0]); + } + else if (code == FL_INF) + x = FCONST(Inf); + else if (code == FL_NAN) + x = FCONST(Nan); + else + x = FLIT(0.0); /* code == FL_ERR */ + + if (neg) + FNEGATE(x); + return (x); + } + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxwctype.h b/test_data/lots_of_files/xxwctype.h new file mode 100644 index 0000000..bcb3306 --- /dev/null +++ b/test_data/lots_of_files/xxwctype.h @@ -0,0 +1,9 @@ +/* xxwctype.h -- parameters for wchar_t character type */ + +#define CTYPE wchar_t +#define CNAME(fun) _W##fun + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxxdtent.h b/test_data/lots_of_files/xxxdtent.h new file mode 100644 index 0000000..f813758 --- /dev/null +++ b/test_data/lots_of_files/xxxdtent.h @@ -0,0 +1,114 @@ +/* xxxdtent.h -- common _[FL]Dtento functionality */ +#include "xmath.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + + /* macros */ +#define ACSIZE 3 /* size of extended-precision accumulators */ + +#define FRAC_BITS_2 (FRAC_BITS * FRAC_BITS) + + #if 113 <= FBITS + #define FRAC_BITS 72057594037927936.0L /* 2^56 */ + +static const FTYPE tenth[] = + { /* 113-bit: 0.100000 */ + (FTYPE)(FLIT(7205759403792793.0) / FRAC_BITS), + (FTYPE)(FLIT(43234556422756761.0) / FRAC_BITS_2), + (FTYPE)(FLIT(43234556422756761.0) / FRAC_BITS_2 / FRAC_BITS), + }; + + #elif 64 <= FBITS + #define FRAC_BITS 4294967296.0L /* 2^32 */ + +static const FTYPE tenth[] = + { /* 64-bit: 0.100000 */ + (FTYPE)(FLIT(429496729.0) / FRAC_BITS), + (FTYPE)(FLIT(2576980377.0) / FRAC_BITS_2), + (FTYPE)(FLIT(2576980377.0) / FRAC_BITS_2 / FRAC_BITS), + }; + + #elif 53 <= FBITS + #define FRAC_BITS 67108864.0L /* 2^26 */ + +static const FTYPE tenth[] = + { /* 53-bit: 0.100000 */ + (FTYPE)(FLIT(6710886.0) / FRAC_BITS), + (FTYPE)(FLIT(26843545.0) / FRAC_BITS_2), + (FTYPE)(FLIT(40265318.0) / FRAC_BITS_2 / FRAC_BITS), + }; + + #elif 24 <= FBITS + #define FRAC_BITS 4096.0L /* 2^12 */ + +static const FTYPE tenth[] = + { /* 24-bit: 0.100000 */ + (FTYPE)(FLIT(409.0) / FRAC_BITS), + (FTYPE)(FLIT(2457.0) / FRAC_BITS_2), + (FTYPE)(FLIT(2457.0) / FRAC_BITS_2 / FRAC_BITS), + }; + + #elif 11 <= FBITS + #define FRAC_BITS 32.0 /* 2^5 */ + +static const FTYPE tenth[] = + { /* 11-bit: 0.100000 */ + FTYPE(3.0 / FRAC_BITS), + FTYPE(6.0 / FRAC_BITS_2), + FTYPE(13.0 / FRAC_BITS_2 / FRAC_BITS), + }; + + #else /* FBITS */ + #error _Dtento has too much precision + #endif /* FBITS */ + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL FNAME(Dtento)(FTYPE *xpx, long n, int *perr) + { /* compute *px * 10**n */ + FTYPE xpf[ACSIZE]; + FTYPE x; + + if (n == 0 || xpx[0] == FLIT(0.0)) + return (FNAME(Xp_getw)(xpx, ACSIZE)); + + if (0 < n) + FNAME(Xp_setw)(xpf, ACSIZE, 10.0); /* factor = 10 */ + else + { /* scale down */ + n = -n; + FNAME(Xp_movx)(xpf, ACSIZE, tenth); /* factor = 1/10 */ + } + + for (; ; ) + { /* multiply as needed by 10^(2^n) */ + FTYPE xpt[ACSIZE * 2]; + FTYPE xpw[ACSIZE]; + + if (n & 1) + FNAME(Xp_mulx)(xpx, ACSIZE, xpf, ACSIZE, xpt); + n >>= 1; + if (n == 0) + break; + FNAME(Xp_movx)(xpw, ACSIZE, xpf); + FNAME(Xp_mulx)(xpf, ACSIZE, xpw, ACSIZE, xpt); /* square 10^n */ + } + + x = FNAME(Xp_getw)(xpx, ACSIZE); + if (x == FLIT(0.0) || x == FCONST(Inf) || x == -FCONST(Inf)) + { /* report error and set errno */ + errno = ERANGE; + if (perr != 0) + *perr |= 1; + } + return (x); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/xxxprec.h b/test_data/lots_of_files/xxxprec.h new file mode 100644 index 0000000..c74c186 --- /dev/null +++ b/test_data/lots_of_files/xxxprec.h @@ -0,0 +1,435 @@ +/* xxxprec.h -- common extended precision functionality */ +#include <string.h> +#include "xmath.h" +_C_STD_BEGIN + #if !defined(MRTDLL) +_C_LIB_DECL + #endif /* defined(MRTDLL) */ + + #if _HAS_DINKUM_CLIB + + #else /* _HAS_DINKUM_CLIB */ + #ifndef ldexpf + #define ldexpf(x, y) ldexp((double)(x), (y)) + #endif /* ldexp */ + + #ifndef sqrtf + #define sqrtf(x) sqrt((double)(x)) + #endif /* sqrtf */ + #endif /* _HAS_DINKUM_CLIB */ + +#define BIG_EXP (2 * FMAXEXP) /* very large, as exponents go */ +#define BITS_WORD (FBITS / 2) /* all words same for now */ +#define NBUF 4 /* size of delay line for mulh */ + +#define COPY_UP(j, n) {int m = j; \ + for (; ++m < n && (p[m - 1] = p[m]) != FLIT(0.0); ) \ + ; \ + p[n - 1] = FLIT(0.0);} /* STET */ + + #if 0 +#include <stdio.h> + +static void printit(const char *s, FTYPE *p, int n) + { /* print xp array */ + int i; + + printf(s); + for (i = 0; i < n && (p[i] != FLIT(0.0) || i == 0); ++i) + printf(" %La", (long double)p[i]); + printf("\n"); + } + #endif /* 0 */ + +_CRTIMP2_PURE FTYPE __CLRCALL_PURE_OR_CDECL FNAME(Xp_getw)(const FTYPE *p, int n) + { /* get total value */ + if (n == 0) + return (FLIT(0.0)); + else if (n == 1 || p[0] == FLIT(0.0) || p[1] == FLIT(0.0)) + return (p[0]); + else if (n == 2 || p[2] == FLIT(0.0)) + return (p[0] + p[1]); + else + { /* extra bits, ensure proper rounding */ + FTYPE p01 = p[0] + p[1]; + FTYPE p2 = p[2]; + + if (p[3] != FLIT(0.0)) + FSETLSB(p2); /* fold in sticky bit from p[3] */ + if (p01 - p[0] == p[1]) + return (p01 + p2); /* carry is within p[2], add it in */ + else + return (p[0] + (p[1] + p2)); /* fold in p[2] then add it in */ + } + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_setw)(FTYPE *p, int n, FTYPE x) + { /* load a full-precision value */ + FTYPE x0 = x; + short errx, xexp; + + if (n <= 0) + ; /* no room, do nothing */ + else if (n == 1 || (errx = FNAME(Dunscale)(&xexp, &x0)) == 0) + p[0] = x0; /* zero or no extra room, store original value */ + else if (0 < errx) + { /* store Inf or NaN with backstop for safety */ + p[0] = x0; + p[1] = FLIT(0.0); + } + else + { /* finite, unpack it */ + FNAME(Dint)(&x0, BITS_WORD); + FNAME(Dscale)(&x0, xexp); + + p[0] = x0; /* ms bits */ + p[1] = x - x0; /* ls bits */ + if ((FBITS & 1) != 0 && 2 < n && p[1] != FLIT(0.0)) + { /* may need a third word */ + x = p[1]; + FNAME(Dunscale)(&xexp, &p[1]); + FNAME(Dint)(&p[1], BITS_WORD); + FNAME(Dscale)(&p[1], xexp); + p[2] = x - p[1]; + if (3 < n && p[2] != FLIT(0.0)) + p[3] = FLIT(0.0); + } + else if (2 < n) + p[2] = FLIT(0.0); + } + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_addh)(FTYPE *p, int n, FTYPE x0) + { /* add a half-precision value */ + FTYPE xscaled = x0; + short errx, xexp; + + if (n == 0) + ; + else if (0 < (errx = FNAME(Dunscale)(&xexp, &xscaled))) + if (errx == _NANCODE || (errx = FNAME(Dtest)(&p[0])) <= 0) + p[0] = x0; /* x0 NaN, or x0 Inf and y finite, just store x0 */ + else if (errx == _NANCODE || FISNEG(x0) == FISNEG(p[0])) + ; /* leave NaN or Inf alone */ + else + { /* Inf - Inf is invalid */ + _Feraise(_FE_INVALID); + p[0] = FCONST(Nan); + if (1 < n) + p[1] = FLIT(0.0); + } + else if (errx < 0) + { /* x0 is finite nonzero, add it */ + long prevexp = BIG_EXP; + int k; + + for (k = 0; k < n; ) + { /* look for term comparable to xexp to add x0 */ + FTYPE yscaled = p[k]; + int mybits = BITS_WORD; + short yexp; + long diff; + + if (0 < (errx = FNAME(Dunscale)(&yexp, &yscaled))) + break; /* y is Inf or NaN, just leave it alone */ + else if (errx == 0) + { /* 0 + x == x */ + p[k] = x0; + if (k + 1 < n) + p[k + 1] = FLIT(0.0); /* add new trailing zero */ + break; + } + else if ((diff = (long)yexp - xexp) <= -mybits + && x0 != FLIT(0.0)) + { /* insert nonzero x0 and loop to renormalize */ + int j; + + for (j = k; ++j < n && p[j] != FLIT(0.0); ) + ; + if (j < n - 1) + ++j; /* extra room, copy trailing zero down too */ + else if (j == n) + --j; /* no room, don't copy smallest word */ + for (; k < j; --j) + p[j] = p[j - 1]; /* copy down words */ + p[k] = x0; + x0 = FLIT(0.0); + } + else if (mybits <= diff && x0 != FLIT(0.0)) + { /* loop to add finite x0 to smaller words */ + prevexp = yexp; + ++k; + } + else + { /* partition sum and renormalize */ + if ((p[k] += x0) == FLIT(0.0)) + { /* term sum is zero, copy up words */ + COPY_UP(k, n) + if (p[k] == FLIT(0.0)) + break; + } + x0 = p[k]; + FNAME(Dunscale)(&xexp, &x0); + if (prevexp - mybits < xexp) + { /* propagate bits up */ + FNAME(Dint)(&x0, (short)(xexp - (prevexp - mybits))); + FNAME(Dscale)(&x0, xexp); + if ((p[k] -= x0) == FLIT(0.0)) + { /* all bits carry, copy up words */ + COPY_UP(k, n) + } + if (--k == 0) + prevexp = BIG_EXP; + else + { /* recompute prevexp */ + xscaled = p[k - 1]; + FNAME(Dunscale)(&yexp, &xscaled); + prevexp = yexp; + } + } + else if (k + 1 == n) + break; /* don't truncate bits in last word */ + else + { /* propagate any excess bits down */ + x0 = p[k]; + FNAME(Dunscale)(&yexp, &p[k]); + FNAME(Dint)(&p[k], BITS_WORD); + FNAME(Dscale)(&p[k], yexp); + x0 -= p[k]; + prevexp = yexp; + + xscaled = x0 != FLIT(0.0) ? x0 : p[k]; + FNAME(Dunscale)(&xexp, &xscaled); + ++k; + } + } + } + } + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_mulh)(FTYPE *p, int n, FTYPE x0) + { /* multiply by a half-precision value */ + short errx; + int j, k; + FTYPE buf[NBUF]; + + if (0 < n) + { /* check for special values */ + buf[0] = p[0] * x0; + if (0 <= (errx = FNAME(Dtest)(&buf[0]))) + { /* quit early on 0, Inf, or NaN */ + if (errx == _NANCODE) + _Feraise(_FE_INVALID); + p[0] = buf[0]; + if (0 < errx && 1 < n) + p[1] = FLIT(0.0); + return (p); + } + p[0] = FLIT(0.0); + } + + for (j = 1, k = 0; k < n; ++k, --j) + { /* sum partial products */ + for (; j < NBUF; ++j) + if (k + j < n && p[k + j] != FLIT(0.0)) + { /* copy up a partial product */ + buf[j] = p[k + j] * x0; + p[k + j] = FLIT(0.0); + } + else + { /* terminate sequence */ + buf[j] = FLIT(0.0); + j = 2 * NBUF; + break; + } + + if (buf[0] == FLIT(0.0)) + break; /* input done */ + else + { /* add in partial product by halves */ + int i = 0; + FTYPE y0 = buf[0]; + short xexp; + + FNAME(Dunscale)(&xexp, &y0); + FNAME(Dint)(&y0, BITS_WORD); /* clear low half bits */ + FNAME(Dscale)(&y0, xexp); + FNAME(Xp_addh)(p, n, y0); /* add in ms part */ + FNAME(Xp_addh)(p, n, buf[0] - y0); /* add in ls part */ + + for (; ++i < j; ) + if ((buf[i - 1] = buf[i]) == FLIT(0.0)) + break; /* copy down delay line */ + } + } + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_setn)(FTYPE *p, int n, long x) + { /* load a long integer */ + #if 27 <= FBITS + FNAME(Xp_setw)(p, n, (FTYPE)x); + + #else /* 27 <= FBITS */ + FNAME(Xp_setw)(p, n, (FTYPE)(x / 10000)); + FNAME(Xp_mulh)(p, n, (FTYPE)10000); + FNAME(Xp_addh)(p, n, (FTYPE)(x % 10000)); + #endif /* 27 <= FBITS */ + + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_movx)(FTYPE *p, int n, const FTYPE *q) + { /* copy an extended precision value */ + memcpy(p, q, n * sizeof (FTYPE)); + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_addx)(FTYPE *p, int n, + const FTYPE *q, int m) + { /* add an extended precision value */ + int k; + + for (k = 0; k < m && q[k] != FLIT(0.0); ++k) + FNAME(Xp_addh)(p, n, q[k]); + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_subx)(FTYPE *p, int n, + const FTYPE *q, int m) + { /* subtract an extended precision value */ + int k; + + for (k = 0; k < m && q[k] != FLIT(0.0); ++k) + FNAME(Xp_addh)(p, n, -q[k]); + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_ldexpx)(FTYPE *p, int n, int m) + { /* scale an extended precision value */ + int k; + + for (k = 0; k < n; ++k) + { + p[k] = FFUN(ldexp)(p[k], m); + if (p[k] == FLIT(0.0)) + break; + } + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_mulx)(FTYPE *p, int n, + const FTYPE *q, int m, + FTYPE *ptemp2) + { /* multiply by an extended precision value (needs 2*n temp) */ + if (n == 0 || m == 0) + ; + else if (q[0] == FLIT(0.0) || q[1] == FLIT(0.0)) + FNAME(Xp_mulh)(p, n, q[0]); + else + { /* sum partial products */ + FTYPE *px = ptemp2; + FTYPE *pac = ptemp2 + n; + int j; + + FNAME(Xp_movx)(px, n, p); + FNAME(Xp_mulh)(p, n, q[0]); /* form first partial product in place */ + for (j = 1; j < m && q[j] != FLIT(0.0); ++j) + { /* add in a partial product */ + FNAME(Xp_movx)(pac, n, px); + FNAME(Xp_mulh)(pac, n, q[j]); + FNAME(Xp_addx)(p, n, pac, n); + } + } + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_invx)(FTYPE *p, int n, FTYPE *ptemp4) + { /* invert an extended precision value (needs 4*n temp) */ + short errx; + + if (n == 0) + ; + else if (0 <= (errx = FNAME(Dtest)(&p[0]))) + { /* not finite, return special value */ + if (errx == _INFCODE) + p[0] = FLIT(0.0); /* 1/Inf == 0 */ + else if (errx == 0) + p[0] = FCONST(Inf); /* 1/0 == Inf */ + /* else 1/NaN == NaN */ + } + else + { /* p[0] is finite nonzero, invert it */ + FTYPE *pac = ptemp4; + FTYPE *py = ptemp4 + n; + FTYPE *ptemp2 = py + n; + FTYPE x0 = p[0]; + int k; + + FNAME(Xp_movx)(py, n, p); + FNAME(Xp_mulh)(py, n, -FLIT(1.0)); /* py = -x */ + + if (1 < n) + x0 += p[1]; + FNAME(Xp_setw)(p, n, FINVERT(x0)); /* p = y */ + + for (k = 1; k < n; k <<= 1) + { /* iterate to double previous precision of 1/x */ + FNAME(Xp_movx)(pac, n, p); + FNAME(Xp_mulx)(pac, n, py, n, ptemp2); + FNAME(Xp_addh)(pac, n, FLIT(1.0)); /* 1-x*y */ + FNAME(Xp_mulx)(pac, n, p, n, ptemp2); /* y*(1-x*y) */ + FNAME(Xp_addx)(p, n, pac, n); /* y += y*(1-x*y) */ + } + } + return (p); + } + +_CRTIMP2_PURE FTYPE *__CLRCALL_PURE_OR_CDECL FNAME(Xp_sqrtx)(FTYPE *p, int n, FTYPE *ptemp4) + { /* find square root of an extended precision value (needs 4*n temp) */ + if (n == 0) + ; + else if (0 <= FNAME(Dtest)(&p[0]) || p[0] < FLIT(0.0)) + { /* not finite nonnegative, return special value */ + if (p[0] < FLIT(0.0)) + { /* sqrt(negative), report domain error */ + _Feraise(_FE_INVALID); + p[0] = FCONST(Nan); + } + } + else + { /* worth iterating, compute x* sqrt(1/x) */ + FTYPE *pac = ptemp4; + FTYPE *py = ptemp4 + n; + FTYPE *ptemp2 = py + n; + FTYPE x0 = p[0]; + int k; + + if (1 < n) + x0 += p[1]; + FNAME(Xp_setw)(py, n, FINVERT(FFUN(sqrt)(x0))); /* py = y */ + + for (k = 2; k < n; k <<= 1) + { /* iterate to double previous precision of sqrt(1/x) */ + FNAME(Xp_movx)(pac, n, py); + FNAME(Xp_mulh)(pac, n, -FLIT(0.5)); + FNAME(Xp_mulx)(pac, n, p, n, ptemp2); + FNAME(Xp_mulx)(pac, n, py, n, ptemp2); + FNAME(Xp_addh)(pac, n, FLIT(1.5)); /* 3/2-x*y*y/2 */ + FNAME(Xp_mulx)(py, n, pac, n, ptemp2); /* y *= 3/2-x*y*y/2 */ + } + FNAME(Xp_mulx)(p, n, py, n, ptemp2); /* x*sqrt(1/x) */ + } + return (p); + } + #if !defined(MRTDLL) +_END_C_LIB_DECL + #endif /* !defined(MRTDLL) */ +_C_STD_END + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/ymath.h b/test_data/lots_of_files/ymath.h new file mode 100644 index 0000000..b18e067 --- /dev/null +++ b/test_data/lots_of_files/ymath.h @@ -0,0 +1,99 @@ +/* ymath.h internal header */ +#pragma once +#ifndef _YMATH +#define _YMATH +#ifndef RC_INVOKED +#include <yvals.h> +_C_STD_BEGIN + #pragma pack(push,_CRT_PACKING) + #pragma warning(push,3) + #pragma push_macro("new") + + #undef new + + #if !defined(MRTDLL) || !defined(_CRTBLD) + #ifndef _M_CEE_PURE +_C_LIB_DECL + #endif /* _M_CEE_PURE */ + #endif /* !MRTDLL || !_CRTBLD */ + + /* MACROS FOR _Dtest RETURN (0 => ZERO) */ +#define _DENORM (-2) /* C9X only */ +#define _FINITE (-1) +#define _INFCODE 1 +#define _NANCODE 2 + + /* MACROS FOR _Feraise ARGUMENT */ +#define _FE_DIVBYZERO 0x04 +#define _FE_INEXACT 0x20 +#define _FE_INVALID 0x01 +#define _FE_OVERFLOW 0x08 +#define _FE_UNDERFLOW 0x10 + +void __CLRCALL_PURE_OR_CDECL _Feraise(int); + +typedef union + { /* pun float types as integer array */ + unsigned short _Word[8]; + float _Float; + double _Double; + long double _Long_double; + } _Dconst; + + /* double DECLARATIONS */ +_CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Cosh(double, double); +_CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Divide(double, double); +_CRTIMP2_PURE short __CLRCALL_PURE_OR_CDECL _Dtest(double *); +_CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Log(double, int); +_CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Recip(double); +_CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Sin(double, unsigned int); +_CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Sinx(double, unsigned int, int); +_CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Sinh(double, double); + +_CRTIMP2_PURE short __CLRCALL_PURE_OR_CDECL _Exp(double *, double, short); +extern _CRTIMP2_PURE /* const */ _Dconst _Denorm, _Hugeval, _Inf, + _Nan, _Snan; + + /* float DECLARATIONS */ +_CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _FCosh(float, float); +_CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _FDivide(float, float); +_CRTIMP2_PURE short __CLRCALL_PURE_OR_CDECL _FDtest(float *); +_CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _FLog(float, int); +_CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _FRecip(float); +_CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _FSin(float, unsigned int); +_CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _FSinx(float, unsigned int, int); +_CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _FSinh(float, float); + +_CRTIMP2_PURE short __CLRCALL_PURE_OR_CDECL _FExp(float *, float, short); +extern _CRTIMP2_PURE /* const */ _Dconst _FDenorm, _FInf, _FNan, _FSnan; + + /* long double DECLARATIONS */ +_CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _LCosh(long double, long double); +_CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _LDivide(long double, long double); +_CRTIMP2_PURE short __CLRCALL_PURE_OR_CDECL _LDtest(long double *); +_CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _LLog(long double, int); +_CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _LRecip(long double); +_CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _LSin(long double, unsigned int); +_CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _LSinx(long double, unsigned int, int); +_CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _LSinh(long double, long double); + +_CRTIMP2_PURE short __CLRCALL_PURE_OR_CDECL _LExp(long double *, long double, short); +extern _CRTIMP2_PURE /* const */ _Dconst _LDenorm, _LInf, _LNan, _LSnan; + + #if !defined(MRTDLL) || !defined(_CRTBLD) + #ifndef _M_CEE_PURE +_END_C_LIB_DECL + #endif /* _M_CEE_PURE */ + #endif /* !MRTDLL || !_CRTBLD */ + + #pragma pop_macro("new") + #pragma warning(pop) + #pragma pack(pop) +_C_STD_END +#endif /* RC_INVOKED */ +#endif /* _YMATH */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. +V6.00:0009 */ diff --git a/test_data/lots_of_files/yvals.h b/test_data/lots_of_files/yvals.h new file mode 100644 index 0000000..6713ca5 --- /dev/null +++ b/test_data/lots_of_files/yvals.h @@ -0,0 +1,836 @@ +/* yvals.h values header for Microsoft C/C++ */ +#pragma once +#ifndef _YVALS +#define _YVALS +#ifndef RC_INVOKED + +#include <xkeycheck.h> +#include <crtdefs.h> + +#pragma pack(push,_CRT_PACKING) +#pragma push_macro("new") +#undef new + +#define _CPPLIB_VER 610 +#define _HAS_DECLTYPE 1 +#define _HAS_INITIALIZER_LISTS 1 +#define _HAS_REF_QUALIFIER 0 +#define _HAS_RVALUE_REFERENCES 1 +#define _HAS_SCOPED_ENUM 1 +#define _HAS_TEMPLATE_ALIAS 1 +#define _HAS_VARIADIC_TEMPLATES 1 + +#define _HAS_CPP0X 1 + +#define _NOEXCEPT throw () +#define _NOEXCEPT_OP(x) + +/* Note on use of "deprecate": + * Various places in this header and other headers use __declspec(deprecate) or macros that have the term DEPRECATE in them. + * We use deprecate here ONLY to signal the compiler to emit a warning about these items. The use of deprecate + * should NOT be taken to imply that any standard committee has deprecated these functions from the relevant standards. + * In fact, these functions are NOT deprecated from the standard. + * + * Full details can be found in our documentation by searching for "Checked Iterators". +*/ + +#if defined(MRTDLL) && defined(_CRTBLD) +/* +process-global is the default for code built with /clr or /clr:oldSyntax. +appdomain-global is the default for code built with /clr:pure. +Code in MSVCM is built with /clr, but is used by user code built with /clr:pure +so it must conform to the expectations of /clr:pure clients. +Use __PURE_APPDOMAIN_GLOBAL when a global needs to be appdomain-global for pure +clients and process-global for mixed clients. +*/ +#define __PURE_APPDOMAIN_GLOBAL __declspec(appdomain) +#else +#define __PURE_APPDOMAIN_GLOBAL +#endif + +#ifndef __CRTDECL +#if defined(_M_CEE_PURE) || defined(MRTDLL) +#define __CRTDECL __clrcall +#else +#define __CRTDECL __cdecl +#endif +#endif + +#ifndef __CLR_OR_THIS_CALL +#if defined(MRTDLL) || defined(_M_CEE_PURE) +#define __CLR_OR_THIS_CALL __clrcall +#else +#define __CLR_OR_THIS_CALL +#endif +#endif + +#ifndef __CLRCALL_OR_CDECL +#if defined(MRTDLL) || defined(_M_CEE_PURE) +#define __CLRCALL_OR_CDECL __clrcall +#else +#define __CLRCALL_OR_CDECL __cdecl +#endif +#endif + +#ifndef __CLRCALL_PURE_OR_CDECL +#if defined(_M_CEE_PURE) +#define __CLRCALL_PURE_OR_CDECL __clrcall +#else +#define __CLRCALL_PURE_OR_CDECL __cdecl +#endif +#endif + + /* CURRENT DLL NAMES */ +#ifndef _CRT_MSVCP_CURRENT +#ifdef _DEBUG +#define _CRT_MSVCP_CURRENT "MSVCP120D.dll" +#else +#define _CRT_MSVCP_CURRENT "MSVCP120.dll" +#endif +#endif + + /* NAMING PROPERTIES */ +#define _WIN32_C_LIB 1 + + /* THREAD AND LOCALE CONTROL */ +#define _MULTI_THREAD 1 /* nontrivial locks if multithreaded */ +#define _IOSTREAM_OP_LOCKS 1 /* lock iostream operations */ +#define _GLOBAL_LOCALE 0 /* 0 for per-thread locales, 1 for shared */ + + /* THREAD-LOCAL STORAGE */ +#define _COMPILER_TLS 1 /* 1 if compiler supports TLS directly */ + #if _MULTI_THREAD + #define _TLS_QUAL __declspec(thread) /* TLS qualifier, if any */ + + #else /* _MULTI_THREAD */ + #define _TLS_QUAL + #endif /* _MULTI_THREAD */ + + #ifndef _HAS_EXCEPTIONS + #define _HAS_EXCEPTIONS 1 /* predefine as 0 to disable exceptions */ + #endif /* _HAS_EXCEPTIONS */ + +#define _GLOBAL_USING 1 + + #ifndef _NO_LOCALES + #define _NO_LOCALES 0 + #endif /* _NO_LOCALES */ + +#ifdef _ITERATOR_DEBUG_LEVEL /* A. _ITERATOR_DEBUG_LEVEL is already defined. */ + + /* A1. Validate _ITERATOR_DEBUG_LEVEL. */ + #if _ITERATOR_DEBUG_LEVEL > 2 && defined(_DEBUG) + #error _ITERATOR_DEBUG_LEVEL > 2 is not supported in debug mode. + #elif _ITERATOR_DEBUG_LEVEL > 1 && !defined(_DEBUG) + #error _ITERATOR_DEBUG_LEVEL > 1 is not supported in release mode. + #endif + + /* A2. Inspect _HAS_ITERATOR_DEBUGGING. */ + #ifdef _HAS_ITERATOR_DEBUGGING /* A2i. _HAS_ITERATOR_DEBUGGING is already defined, validate it. */ + #if _ITERATOR_DEBUG_LEVEL == 2 && _HAS_ITERATOR_DEBUGGING != 1 + #error _ITERATOR_DEBUG_LEVEL == 2 must imply _HAS_ITERATOR_DEBUGGING == 1 . + #elif _ITERATOR_DEBUG_LEVEL < 2 && _HAS_ITERATOR_DEBUGGING != 0 + #error _ITERATOR_DEBUG_LEVEL < 2 must imply _HAS_ITERATOR_DEBUGGING == 0 . + #endif + #else /* A2ii. _HAS_ITERATOR_DEBUGGING is not yet defined, derive it. */ + #if _ITERATOR_DEBUG_LEVEL == 2 + #define _HAS_ITERATOR_DEBUGGING 1 + #else + #define _HAS_ITERATOR_DEBUGGING 0 + #endif + #endif /* _HAS_ITERATOR_DEBUGGING */ + + /* A3. Inspect _SECURE_SCL. */ + #ifdef _SECURE_SCL /* A3i. _SECURE_SCL is already defined, validate it. */ + #if _ITERATOR_DEBUG_LEVEL > 0 && _SECURE_SCL != 1 + #error _ITERATOR_DEBUG_LEVEL > 0 must imply _SECURE_SCL == 1 . + #elif _ITERATOR_DEBUG_LEVEL == 0 && _SECURE_SCL != 0 + #error _ITERATOR_DEBUG_LEVEL == 0 must imply _SECURE_SCL == 0 . + #endif + #else /* A3ii. _SECURE_SCL is not yet defined, derive it. */ + #if _ITERATOR_DEBUG_LEVEL > 0 + #define _SECURE_SCL 1 + #else + #define _SECURE_SCL 0 + #endif + #endif /* _SECURE_SCL */ + +#else /* B. _ITERATOR_DEBUG_LEVEL is not yet defined. */ + + /* B1. Inspect _HAS_ITERATOR_DEBUGGING. */ + #ifdef _HAS_ITERATOR_DEBUGGING /* B1i. _HAS_ITERATOR_DEBUGGING is already defined, validate it. */ + #if _HAS_ITERATOR_DEBUGGING > 1 + #error _HAS_ITERATOR_DEBUGGING must be either 0 or 1 . + #elif _HAS_ITERATOR_DEBUGGING == 1 && !defined(_DEBUG) + #error _HAS_ITERATOR_DEBUGGING == 1 is not supported in release mode. + #endif + #else /* B1ii. _HAS_ITERATOR_DEBUGGING is not yet defined, default it. */ + #ifdef _DEBUG + #define _HAS_ITERATOR_DEBUGGING 1 + #else + #define _HAS_ITERATOR_DEBUGGING 0 + #endif + #endif /* _HAS_ITERATOR_DEBUGGING */ + + /* B2. Inspect _SECURE_SCL. */ + #ifdef _SECURE_SCL /* B2i. _SECURE_SCL is already defined, validate it. */ + #if _SECURE_SCL > 1 + #error _SECURE_SCL must be either 0 or 1 . + #endif + #else /* B2ii. _SECURE_SCL is not yet defined, default it. */ + #if _HAS_ITERATOR_DEBUGGING == 1 + #define _SECURE_SCL 1 + #else + #define _SECURE_SCL 0 + #endif + #endif /* _SECURE_SCL */ + + /* B3. Derive _ITERATOR_DEBUG_LEVEL. */ + #if _HAS_ITERATOR_DEBUGGING + #define _ITERATOR_DEBUG_LEVEL 2 + #elif _SECURE_SCL + #define _ITERATOR_DEBUG_LEVEL 1 + #else + #define _ITERATOR_DEBUG_LEVEL 0 + #endif + +#endif /* _ITERATOR_DEBUG_LEVEL */ + +#define _STRINGIZEX(x) #x +#define _STRINGIZE(x) _STRINGIZEX(x) + +#ifndef __EDG__ /* TRANSITION */ +#ifdef __cplusplus + #ifndef _ALLOW_MSC_VER_MISMATCH + #pragma detect_mismatch("_MSC_VER", "1800") + #endif /* _ALLOW_MSC_VER_MISMATCH */ + + #ifndef _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH + #pragma detect_mismatch("_ITERATOR_DEBUG_LEVEL", _STRINGIZE(_ITERATOR_DEBUG_LEVEL)) + #endif /* _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH */ + + #ifndef _ALLOW_RUNTIME_LIBRARY_MISMATCH + #if !defined(_DLL) && !defined(_DEBUG) + #pragma detect_mismatch("RuntimeLibrary", "MT_StaticRelease") + #elif !defined(_DLL) && defined(_DEBUG) + #pragma detect_mismatch("RuntimeLibrary", "MTd_StaticDebug") + #elif defined(_DLL) && !defined(_DEBUG) + #pragma detect_mismatch("RuntimeLibrary", "MD_DynamicRelease") + #elif defined(_DLL) && defined(_DEBUG) + #pragma detect_mismatch("RuntimeLibrary", "MDd_DynamicDebug") + #endif /* defined(_DLL) etc. */ + #endif /* _ALLOW_RUNTIME_LIBRARY_MISMATCH */ +#endif /* __cplusplus */ +#endif /* __EDG__ */ + +#ifdef _ITERATOR_DEBUG_ARRAY_OVERLOADS + #if _ITERATOR_DEBUG_ARRAY_OVERLOADS != 0 && _ITERATOR_DEBUG_ARRAY_OVERLOADS != 1 + #error _ITERATOR_DEBUG_ARRAY_OVERLOADS must be either 0 or 1 . + #elif _ITERATOR_DEBUG_LEVEL == 0 && _ITERATOR_DEBUG_ARRAY_OVERLOADS == 1 + #error _ITERATOR_DEBUG_LEVEL == 0 must imply _ITERATOR_DEBUG_ARRAY_OVERLOADS == 0 . + #endif +#else /* _ITERATOR_DEBUG_ARRAY_OVERLOADS */ + #if _ITERATOR_DEBUG_LEVEL == 0 + #define _ITERATOR_DEBUG_ARRAY_OVERLOADS 0 + #else + #define _ITERATOR_DEBUG_ARRAY_OVERLOADS 1 + #endif +#endif /* _ITERATOR_DEBUG_ARRAY_OVERLOADS */ + +/* See note on use of deprecate at the top of this file */ +#if !defined(_SCL_SECURE_NO_WARNINGS) && defined(_SCL_SECURE_NO_DEPRECATE) +#define _SCL_SECURE_NO_WARNINGS +#endif + +#if !defined (_SECURE_SCL_DEPRECATE) +#if defined(_SCL_SECURE_NO_WARNINGS) +#define _SECURE_SCL_DEPRECATE 0 +#else +#define _SECURE_SCL_DEPRECATE 1 +#endif +#endif + +#if defined(_SECURE_SCL_THROWS) && _SECURE_SCL_THROWS +#error _SECURE_SCL_THROWS has been removed. +#endif + +/* _SECURE_SCL switches: helper macros */ +/* See note on use of deprecate at the top of this file */ + +#if _ITERATOR_DEBUG_LEVEL > 0 && _SECURE_SCL_DEPRECATE +#define _SCL_INSECURE_DEPRECATE \ + _CRT_DEPRECATE_TEXT( \ + "Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. " \ + "To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'") +#else +#define _SCL_INSECURE_DEPRECATE +#endif + +#ifndef _SCL_SECURE_INVALID_PARAMETER + #define _SCL_SECURE_INVALID_PARAMETER(expr) _CRT_SECURE_INVALID_PARAMETER(expr) +#endif + + #define _SCL_SECURE_INVALID_ARGUMENT_NO_ASSERT _SCL_SECURE_INVALID_PARAMETER("invalid argument") + #define _SCL_SECURE_OUT_OF_RANGE_NO_ASSERT _SCL_SECURE_INVALID_PARAMETER("out of range") + + #define _SCL_SECURE_ALWAYS_VALIDATE(cond) \ + { \ + if (!(cond)) \ + { \ + _ASSERTE(#cond && 0); \ + _SCL_SECURE_INVALID_ARGUMENT_NO_ASSERT; \ + } \ + _Analysis_assume_(cond); \ + } + + #define _SCL_SECURE_ALWAYS_VALIDATE_RANGE(cond) \ + { \ + if (!(cond)) \ + { \ + _ASSERTE(#cond && 0); \ + _SCL_SECURE_OUT_OF_RANGE_NO_ASSERT; \ + } \ + _Analysis_assume_(cond); \ + } + + #define _SCL_SECURE_CRT_VALIDATE(cond, retvalue) \ + { \ + if (!(cond)) \ + { \ + _ASSERTE(#cond && 0); \ + _SCL_SECURE_INVALID_PARAMETER(cond); \ + return (retvalue); \ + } \ + } + + #if _ITERATOR_DEBUG_LEVEL > 0 + + #define _SCL_SECURE_VALIDATE(cond) \ + { \ + if (!(cond)) \ + { \ + _ASSERTE(#cond && 0); \ + _SCL_SECURE_INVALID_ARGUMENT_NO_ASSERT; \ + } \ + _Analysis_assume_(cond); \ + } + + #define _SCL_SECURE_VALIDATE_RANGE(cond) \ + { \ + if (!(cond)) \ + { \ + _ASSERTE(#cond && 0); \ + _SCL_SECURE_OUT_OF_RANGE_NO_ASSERT; \ + } \ + _Analysis_assume_(cond); \ + } + + #define _SCL_SECURE_INVALID_ARGUMENT \ + { \ + _ASSERTE("Standard C++ Libraries Invalid Argument" && 0); \ + _SCL_SECURE_INVALID_ARGUMENT_NO_ASSERT; \ + } + #define _SCL_SECURE_OUT_OF_RANGE \ + { \ + _ASSERTE("Standard C++ Libraries Out of Range" && 0); \ + _SCL_SECURE_OUT_OF_RANGE_NO_ASSERT; \ + } + + #else /* _ITERATOR_DEBUG_LEVEL > 0 */ + +/* when users disable _SECURE_SCL to get performance, we don't want analysis warnings from SCL headers */ +#if _ITERATOR_DEBUG_LEVEL == 2 + #define _SCL_SECURE_VALIDATE(cond) _Analysis_assume_(cond) + #define _SCL_SECURE_VALIDATE_RANGE(cond) _Analysis_assume_(cond) +#else + #define _SCL_SECURE_VALIDATE(cond) + #define _SCL_SECURE_VALIDATE_RANGE(cond) +#endif + + #define _SCL_SECURE_INVALID_ARGUMENT + #define _SCL_SECURE_OUT_OF_RANGE + + #endif /* _ITERATOR_DEBUG_LEVEL > 0 */ + +#if __STDC_WANT_SECURE_LIB__ +#define _CRT_SECURE_MEMCPY(dest, destsize, source, count) ::memcpy_s((dest), (destsize), (source), (count)) +#define _CRT_SECURE_MEMMOVE(dest, destsize, source, count) ::memmove_s((dest), (destsize), (source), (count)) +#define _CRT_SECURE_WMEMCPY(dest, destsize, source, count) ::wmemcpy_s((dest), (destsize), (source), (count)) +#define _CRT_SECURE_WMEMMOVE(dest, destsize, source, count) ::wmemmove_s((dest), (destsize), (source), (count)) +#else +#define _CRT_SECURE_MEMCPY(dest, destsize, source, count) ::memcpy((dest), (source), (count)) +#define _CRT_SECURE_MEMMOVE(dest, destsize, source, count) ::memmove((dest), (source), (count)) +#define _CRT_SECURE_WMEMCPY(dest, destsize, source, count) ::wmemcpy((dest), (source), (count)) +#define _CRT_SECURE_WMEMMOVE(dest, destsize, source, count) ::wmemmove((dest), (source), (count)) +#endif + +#include <use_ansi.h> + +#if defined(_M_CEE) && defined(_STATIC_CPPLIB) +#error _STATIC_CPPLIB is not supported while building with /clr or /clr:pure +#endif + +#if defined(_DLL) && defined(_STATIC_CPPLIB) && !defined(_DISABLE_DEPRECATE_STATIC_CPPLIB) +#include <crtwrn.h> +#pragma push_macro("_STATIC_CPPLIB") +#undef _STATIC_CPPLIB +#pragma _CRT_WARNING( _DEPRECATE_STATIC_CPPLIB ) +#pragma pop_macro("_STATIC_CPPLIB") +#endif + +/* Define _CRTIMP2 */ + #ifndef _CRTIMP2 + #if defined(CRTDLL2) && defined(_CRTBLD) + #define _CRTIMP2 __declspec(dllexport) + #else /* ndef CRTDLL2 && _CRTBLD */ + + #if defined(_DLL) && !defined(_STATIC_CPPLIB) + #define _CRTIMP2 __declspec(dllimport) + + #else /* ndef _DLL && !STATIC_CPPLIB */ + #define _CRTIMP2 + #endif /* _DLL && !STATIC_CPPLIB */ + + #endif /* CRTDLL2 && _CRTBLD */ + #endif /* _CRTIMP2 */ + +/* Define _CRTIMP2_NCEEPURE */ + #ifndef _CRTIMP2_NCEEPURE + #if defined(_M_CEE_PURE) + #define _CRTIMP2_NCEEPURE + #else + #define _CRTIMP2_NCEEPURE _CRTIMP2 + #endif + #endif + + #ifndef _MRTIMP + #if defined(MRTDLL) && defined(_CRTBLD) + #if !defined(_M_CEE_PURE) + #define _MRTIMP __declspec(dllexport) + #else + #define _MRTIMP + #endif + #else /* ndef MRTDLL && _CRTBLD */ + #define _MRTIMP __declspec(dllimport) + #endif /* MRTDLL && _CRTBLD */ + #endif /* _MRTIMP */ + +/* Define _MRTIMP2 */ + #ifndef _MRTIMP2 + #if defined(CRTDLL2) && defined(_CRTBLD) + #define _MRTIMP2 __declspec(dllexport) + #elif defined(MRTDLL) && defined(_CRTBLD) + #define _MRTIMP2 _MRTIMP + #else /* ndef CRTDLL2 && _CRTBLD */ + + #if defined(_DLL) && !defined(_STATIC_CPPLIB) + #define _MRTIMP2 __declspec(dllimport) + + #else /* ndef _DLL && !STATIC_CPPLIB */ + #define _MRTIMP2 + #endif /* _DLL && !STATIC_CPPLIB */ + + #endif /* CRTDLL2 && _CRTBLD */ + #endif /* _MRTIMP2 */ + + #ifndef _MRTIMP2_PURE + #if defined(_M_CEE_PURE) + #define _MRTIMP2_PURE + #else + #define _MRTIMP2_PURE _MRTIMP2 + #endif + #endif + + #ifndef _MRTIMP2_PURE_NPURE + #if defined(_M_CEE_PURE) + #define _MRTIMP2_PURE_NPURE + #else + #define _MRTIMP2_PURE_NPURE _MRTIMP2_NPURE + #endif + #endif + +/* Define _MRTIMP2_NPURE */ + #ifndef _MRTIMP2_NPURE + #if defined(MRTDLL) && defined(_CRTBLD) + #if !defined(_M_CEE_PURE) + #define _MRTIMP2_NPURE __declspec(dllexport) + #else + #define _MRTIMP2_NPURE + #endif + #else /* ndef MRTDLL && _CRTBLD */ + + #if defined(_DLL) && defined(_M_CEE_PURE) + #define _MRTIMP2_NPURE __declspec(dllimport) + + #else + #define _MRTIMP2_NPURE + #endif + + #endif /* MRTDLL && _CRTBLD */ + #endif /* _MRTIMP2_NPURE */ + + #if defined(_DLL) && !defined(_STATIC_CPPLIB) && !defined(_M_CEE_PURE) + #define _DLL_CPPLIB + #endif + + #ifndef _CRTIMP2_PURE + #if defined(MRTDLL) && defined(_CRTBLD) + #define _CRTIMP2_PURE + #else + #ifdef _M_CEE_PURE + #define _CRTIMP2_PURE + #else + #define _CRTIMP2_PURE _CRTIMP2 + #endif + #endif + #endif + + #ifdef _CRTBLD + #if !defined(_CRTDATA2) + #if !defined(MRTDLL) + #define _CRTDATA2 _CRTIMP2 + #else + #define _CRTDATA2 + #endif + #endif + +/* Define _CRTBLD_NATIVE_WCHAR_T */ + + #if defined(__cplusplus) + #ifndef _NATIVE_WCHAR_T_DEFINED + #error Native wchar_t must be defined + + #else /* _NATIVE_WCHAR_T_DEFINED */ + #define _CRTBLD_NATIVE_WCHAR_T + #endif /* _NATIVE_WCHAR_T_DEFINED */ + + #endif /* defined(__cplusplus) */ + +/* These functions are for enabling STATIC_CPPLIB functionality */ + #define _cpp_stdin (&(__iob_func())[0]) + #define _cpp_stdout (&(__iob_func())[1]) + #define _cpp_stderr (&(__iob_func())[2]) + #define _cpp_isleadbyte(c) (__pctype_func()[(unsigned char)(c)] & _LEADBYTE) + #else /* _CRTBLD */ + #if !defined(_CRTDATA2) + #define _CRTDATA2 _CRTIMP2 + #endif + #endif /* _CRTBLD */ + + /* NAMESPACE */ + + #if defined(__cplusplus) + #define _STD_BEGIN namespace std { + #define _STD_END } + #define _STD ::std:: + +/* +We use the stdext (standard extension) namespace to contain extensions that are not part of the current standard +*/ + #define _STDEXT_BEGIN namespace stdext { + #define _STDEXT_END } + #define _STDEXT ::stdext:: + + #ifdef _STD_USING + #define _C_STD_BEGIN namespace std { /* only if *.c compiled as C++ */ + #define _C_STD_END } + #define _CSTD ::std:: + + #else /* _STD_USING */ +/* #define _GLOBAL_USING *.h in global namespace, c* imports to std */ + + #define _C_STD_BEGIN + #define _C_STD_END + #define _CSTD :: + #endif /* _STD_USING */ + + #define _C_LIB_DECL extern "C" { /* C has extern "C" linkage */ + #define _END_C_LIB_DECL } + #define _EXTERN_C extern "C" { + #define _END_EXTERN_C } + + #else /* __cplusplus */ + #define _STD_BEGIN + #define _STD_END + #define _STD + + #define _C_STD_BEGIN + #define _C_STD_END + #define _CSTD + + #define _C_LIB_DECL + #define _END_C_LIB_DECL + #define _EXTERN_C + #define _END_EXTERN_C + #endif /* __cplusplus */ + + #ifdef __cplusplus +_STD_BEGIN +typedef bool _Bool; +_STD_END + #endif /* __cplusplus */ + + /* VC++ COMPILER PARAMETERS */ +#define _LONGLONG __int64 +#define _ULONGLONG unsigned __int64 +#define _LLONG_MAX 0x7fffffffffffffff +#define _ULLONG_MAX 0xffffffffffffffff + + /* INTEGER PROPERTIES */ +#define _C2 1 /* 0 if not 2's complement */ + +#define _MAX_EXP_DIG 8 /* for parsing numerics */ +#define _MAX_INT_DIG 32 +#define _MAX_SIG_DIG 36 + +typedef _LONGLONG _Longlong; +typedef _ULONGLONG _ULonglong; + + /* STDIO PROPERTIES */ +#define _Filet _iobuf + +#define _IOBASE _base +#define _IOPTR _ptr +#define _IOCNT _cnt + +#ifndef _HAS_CHAR16_T_LANGUAGE_SUPPORT + #define _HAS_CHAR16_T_LANGUAGE_SUPPORT 0 +#endif /* _HAS_CHAR16_T_LANGUAGE_SUPPORT */ + + /* uchar PROPERTIES */ + #if _HAS_CHAR16_T_LANGUAGE_SUPPORT + #else /* _HAS_CHAR16_T_LANGUAGE_SUPPORT */ + #if !defined(_CHAR16T) + #define _CHAR16T +typedef unsigned short char16_t; +typedef unsigned int char32_t; + #endif /* !defined(_CHAR16T) */ + #endif /* _HAS_CHAR16_T_LANGUAGE_SUPPORT */ + + /* MULTITHREAD PROPERTIES */ + /* LOCK MACROS */ +#define _LOCK_LOCALE 0 +#define _LOCK_MALLOC 1 +#define _LOCK_STREAM 2 +#define _LOCK_DEBUG 3 +#define _MAX_LOCK 4 /* one more than highest lock number */ + + #ifdef __cplusplus +_STD_BEGIN +enum _Uninitialized + { // tag for suppressing initialization + _Noinit + }; + + // CLASS _Lockit +// warning 4412 is benign here +#pragma warning(push) +#pragma warning(disable:4412) +class _CRTIMP2_PURE _Lockit + { // lock while object in existence -- MUST NEST +public: + #if _MULTI_THREAD + + #if defined(_M_CEE_PURE) || defined(MRTDLL) + __CLR_OR_THIS_CALL _Lockit() + : _Locktype(0) + { // default construct + _Lockit_ctor(this); + } + + explicit __CLR_OR_THIS_CALL _Lockit(int _Kind) + { // set the lock + _Lockit_ctor(this, _Kind); + } + + __CLR_OR_THIS_CALL ~_Lockit() _NOEXCEPT + { // clear the lock + _Lockit_dtor(this); + } + + #else /* defined(_M_CEE_PURE) || defined(MRTDLL) */ + __thiscall _Lockit(); // default construct + explicit __thiscall _Lockit(int); // set the lock + __thiscall ~_Lockit() _NOEXCEPT; // clear the lock + #endif /* defined(_M_CEE_PURE) || defined(MRTDLL) */ + + static _MRTIMP2_NPURE void __cdecl _Lockit_ctor(int); + static _MRTIMP2_NPURE void __cdecl _Lockit_dtor(int); + +private: + static _MRTIMP2_NPURE void __cdecl _Lockit_ctor(_Lockit *); + static _MRTIMP2_NPURE void __cdecl _Lockit_ctor(_Lockit *, int); + static _MRTIMP2_NPURE void __cdecl _Lockit_dtor(_Lockit *); + +public: + __CLR_OR_THIS_CALL _Lockit(const _Lockit&) = delete; + _Lockit& __CLR_OR_THIS_CALL operator=(const _Lockit&) = delete; + +private: + int _Locktype; + + #else /* _MULTI_THREAD */ + _Lockit() + { // do nothing + } + + explicit _Lockit(int) + { // do nothing + } + + ~_Lockit() _NOEXCEPT + { // do nothing + } + #endif /* _MULTI_THREAD */ + }; + + #ifdef _M_CEE +class _CRTIMP2_PURE _EmptyLockit + { // empty lock class used for bin compat +public: + #if _MULTI_THREAD +private: + int _Locktype; + #endif /* _MULTI_THREAD */ + }; + + #if defined(__cplusplus_cli) + #define _M_CEE_FINALLY finally + #else /* defined(__cplusplus_cli) */ + #define _M_CEE_FINALLY __finally + #endif /* defined(__cplusplus_cli) */ + + #define _BEGIN_LOCK(_Kind) \ + { \ + typedef int _TmpTestType; \ + __if_exists(_TmpTestType::ToString) \ + { \ + bool _MustReleaseLock = false; \ + int _LockKind = _Kind; \ + System::Runtime::CompilerServices::RuntimeHelpers::PrepareConstrainedRegions(); \ + try \ + } \ + { \ + __if_exists(_TmpTestType::ToString) \ + { \ + System::Runtime::CompilerServices::RuntimeHelpers::PrepareConstrainedRegions(); \ + try { } _M_CEE_FINALLY \ + { \ + _STD _Lockit::_Lockit_ctor(_LockKind); \ + _MustReleaseLock = true; \ + } \ + } \ + __if_not_exists(_TmpTestType::ToString) \ + { \ + _STD _Lockit _Lock(_Kind); \ + } + + #define _END_LOCK() \ + } \ + __if_exists(_TmpTestType::ToString) \ + { \ + _M_CEE_FINALLY \ + { \ + if (_MustReleaseLock) \ + { \ + _STD _Lockit::_Lockit_dtor(_LockKind); \ + } \ + } \ + } \ + } + + #define _BEGIN_LOCINFO(_VarName) \ + _BEGIN_LOCK(_LOCK_LOCALE) \ + _Locinfo _VarName; + + #define _END_LOCINFO() \ + _END_LOCK() \ + + #define _RELIABILITY_CONTRACT \ + [System::Runtime::ConstrainedExecution::ReliabilityContract( \ + System::Runtime::ConstrainedExecution::Consistency::WillNotCorruptState, \ + System::Runtime::ConstrainedExecution::Cer::Success)] + + #else /* _M_CEE */ + #define _BEGIN_LOCK(_Kind) \ + { \ + _STD _Lockit _Lock(_Kind); + + #define _END_LOCK() \ + } + + #define _BEGIN_LOCINFO(_VarName) \ + { \ + _Locinfo _VarName; + + #define _END_LOCINFO() \ + } + + #define _RELIABILITY_CONTRACT + #endif /* _M_CEE */ + +class _CRTIMP2_PURE _Init_locks + { // initialize mutexes +public: + #if _MULTI_THREAD + #if defined(_M_CEE_PURE) || defined(MRTDLL) + __CLR_OR_THIS_CALL _Init_locks() + { // default construct + _Init_locks_ctor(this); + } + + __CLR_OR_THIS_CALL ~_Init_locks() _NOEXCEPT + { // destroy the object + _Init_locks_dtor(this); + } + + #else /* defined(_M_CEE_PURE) || defined(MRTDLL) */ + __thiscall _Init_locks(); + __thiscall ~_Init_locks() _NOEXCEPT; + #endif /* defined(_M_CEE_PURE) || defined(MRTDLL) */ + +private: + static _MRTIMP2_NPURE void __cdecl _Init_locks_ctor(_Init_locks *); + static _MRTIMP2_NPURE void __cdecl _Init_locks_dtor(_Init_locks *); + + #else /* _MULTI_THREAD */ + _Init_locks() + { // do nothing + } + + ~_Init_locks() _NOEXCEPT + { // do nothing + } + #endif /* _MULTI_THREAD */ + }; + +#pragma warning(pop) +_STD_END + #endif /* __cplusplus */ + +#ifndef _RELIABILITY_CONTRACT + #define _RELIABILITY_CONTRACT +#endif /* _RELIABILITY_CONTRACT */ + + /* MISCELLANEOUS MACROS AND TYPES */ +_C_STD_BEGIN +_MRTIMP2 void __cdecl _Atexit(void (__cdecl *)(void)); + +typedef int _Mbstatet; +typedef unsigned long _Uint32t; + +#define _Mbstinit(x) mbstate_t x = {0} +_C_STD_END + + #define _THROW_BAD_ALLOC _THROW1(...) + #define _NO_RETURN(fun) __declspec(noreturn) void fun + + #pragma pop_macro("new") + #pragma pack(pop) +#endif /* RC_INVOKED */ +#endif /* _YVALS */ + +/* + * Copyright (c) 1992-2012 by P.J. Plauger. ALL RIGHTS RESERVED. + * Consult your license regarding permissions and restrictions. + V6.00:0009 */ diff --git a/test_data/sample_files/UTF-8-test.txt b/test_data/sample_files/UTF-8-test.txt new file mode 100644 index 0000000..73f73e0 Binary files /dev/null and b/test_data/sample_files/UTF-8-test.txt differ diff --git a/test_data/sample_files/autotab.cpp b/test_data/sample_files/autotab.cpp new file mode 100644 index 0000000..1e1b18e --- /dev/null +++ b/test_data/sample_files/autotab.cpp @@ -0,0 +1,73 @@ +#define Thing { Word; } + +// Leave me here + +foor(){ + pub for (){ + stuff(); + thing(); + use(); + } +} + +{ + // Push me up one!\ + line me up with the previous guy! But keep my extra space! + words + + words + + - words; + + /* + * Keep these stars in a straight line brah! + */ + x = + y(a, b, + c, d); + + z = + w(s, t, + u, v); + + +#define ThingAsWell { Word; } + if (long_thing(a, b) && + more_stuff_to_check){ + if (nested_even_harder?0:1 == + complex_thing){ + + } + } + +#if 0 + while(long_name_of_thing(a, + b, + c) && + ZZZ){ + x = y; + } +#endif + + long_line_needs_work = call_this_thing( + position_me_lefter, + and_me_too + );// END OF LINE COMMENT DONT MESS WITH NEXT COMMENT PL0X! + + // PUSH ME UP + // PUSH ME UP TOO + if (x) + d; + + stuff = { + "Hello everythiing", + "There is a lot\ + but it's okay", + .3f, + + 3.f + +d, + 0xA0 + d + } +} + + diff --git a/test_data/sample_files/basic.cpp b/test_data/sample_files/basic.cpp new file mode 100644 index 0000000..4fe07a5 --- /dev/null +++ b/test_data/sample_files/basic.cpp @@ -0,0 +1,2935 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +#if 1 // HELLO +#endif // HELLO +#include <stuff> // HELLO! + +int $c = 1; + +struct Partitioñ_€ursor{ + u8 *memory_base; + u8 *memory_cursor; + i32 max_size; +}; + +// HERE + +internal Partitioñ_€ursor +partitioñ_open(void *memory, i32 size){ + Partitioñ_€ursor partitioñ = {}; + partitioñ.memory_base = partitioñ.memory_cursor = (u8*)memory; + partitioñ.max_size = size; + return partitioñ; +} + +R"()" +LR"foo()foo" +u8"foo" + +u8'a' +R'z' + +# defin X (x) + +internal void* +partitioñ_allocate(Partition_€ursor *data, i32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +/* + * Plaform Independent File Name Helpers + */ + +struct File_List_Iterator{ + bool32 folder_stage; + u8 **filename_ptr; +}; + +internal File_List_Iterator +files_iterator_init(File_List *files){ + File_List_Iterator files_it = {}; + if (files->folder_count > 0){ + files_it.filename_ptr = files->folder_names; + files_it.folder_stage = 1; + } + else if (files->file_count > 0){ + files_it.filename_ptr = files->file_names; + } + return files_it; +} + +internal void +files_iterator_step(File_List *files, File_List_Iterator *files_it){ + ++files_it->filename_ptr; + if (files_it->folder_stage){ + if (files_it->filename_ptr >= files->folder_names + files->folder_count){ + if (files->file_count > 0){ + files_it->filename_ptr = files->file_names; + } + else{ + files_it->filename_ptr = 0; + } + files_it->folder_stage = 0; + } + } + else{ + if (files_it->filename_ptr >= files->file_names + files->file_count){ + files_it->filename_ptr = 0; + } + } +} + +/* + * Drawing Functions + */ + +R"raw()raw" +internal u32 +style_token_color(Editing_Style *style, Cpp_Token_Type type){ + u32 result; + switch (type){ + case CPP_TOKEN_COMMENT: + result = style->comment_color; + break; + + case CPP_TOKEN_KEYWORD: + result = style->keyword_color; + break; + + case CPP_TOKEN_STRING_CONSTANT: + case CPP_TOKEN_CHARACTER_CONSTANT: + case CPP_TOKEN_INTEGER_CONSTANT: + case CPP_TOKEN_FLOATING_CONSTANT: + case CPP_TOKEN_BOOLEAN_CONSTANT: + case CPP_TOKEN_INCLUDE_FILE: + result = style->constant_color; + break; + + default: + result = style->default_color; + break; + } + return result; +} +R"raw()raw"; + +internal void +panel_draw(Thread_Context *thread, Render_Target *target, Editing_Panel *panel, bool32 is_active){ + Editing_File *file = panel->file; + Editing_Style *style = file->style; + Font *font = style->font; + + i32 character_w = (i32)(style_get_character_width(style)); + i32 character_h = (i32)(font->line_skip); + i32 offset_x = (i32)(panel->x); + i32 offset_y = (i32)(panel->y); + i32 max_x = (i32)(panel->w); + i32 max_y = (i32)(panel->h); + + Blit_Rect panel_area; + panel_area.x_start = offset_x; + panel_area.y_start = offset_y; + panel_area.x_end = offset_x + max_x; + panel_area.y_end = offset_y + max_y; + + if (!file || !file->data || file->is_dummy){ + i32 start_x = (panel_area.x_start + panel_area.x_end)/2; + i32 start_y = (panel_area.y_start + panel_area.y_end)/2; + persist String null_file_message = make_lit_string("NULL FILE"); + start_x -= (character_w*null_file_message.size)/2; + start_y -= (character_h)/2; + + real32 pos_x = 0; + real32 pos_y = 0; + + for (i32 i = 0; i < null_file_message.size; ++i){ + u8 to_render = null_file_message.str[i]; + + if (font->glyphs[to_render].data){ + font_draw_glyph_clipped(target, font, to_render, (real32)start_x + pos_x, (real32)start_y + pos_y, style->special_character_color, panel_area); + pos_x += character_w; + } + } + } + + else{ + u32 tab_width = style->tab_width; + i32 size = (i32)file->size; + u8 *data = (u8*)file->data; + + real32 shift_x = 0; + shift_x = -panel->scroll_x * character_w; + + i32 truncated_start_y = (i32)panel->scroll_y; + real32 scroll_amount = panel->scroll_y - truncated_start_y; + + Panel_Cursor_Data start_cursor; + start_cursor = panel->scroll_y_cursor; + start_cursor = panel_compute_cursor_from_xy(panel, 0, truncated_start_y); + panel->scroll_y_cursor = start_cursor; + + i32 start_character = start_cursor.pos; + + real32 pos_x = 0; + real32 pos_y = -character_h*scroll_amount; + + Cpp_Token_Stack token_stack = file->token_stack; + u32 main_color = style->default_color; + u32 highlight_color = 0x00000000; + i32 token_i = 0; + bool32 tokens_exist = file->tokens_exist; + + if (tokens_exist){ + // TODO(allen): Use cpp_get_token, it binary searches this shit! + while (token_i < token_stack.count && + start_character > token_stack.tokens[token_i].start){ + ++token_i; + } + if (token_i != 0){ + main_color = style_token_color(style, token_stack.tokens[token_i-1].type); + if (token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK){ + highlight_color = style->highlight_junk_color; + } + } + } + + // TODO(allen): Render through NULLS + for (i32 i = start_character; i < size && data[i]; ++i){ + u8 to_render = data[i]; + + u32 fade_color = 0xFFFF00FF; + real32 fade_amount = 0.f; + if (style->use_paste_color && panel->paste_effect.tick_down > 0 && panel->paste_effect.start <= i && i < panel->paste_effect.end){ + fade_color = style->paste_color; + fade_amount = (real32) ((panel->paste_effect.tick_down)/panel->paste_effect.tick_max); + } + + if (tokens_exist && token_i < token_stack.count){ + if (i == token_stack.tokens[token_i].start){ + main_color = style_token_color(style, token_stack.tokens[token_i].type); + ++token_i; + } + if (token_i > 0 && i >= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + main_color = 0xFFFFFFFF; + } + } + + highlight_color = 0x00000000; + + if (tokens_exist && token_i > 0 && token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK && i >= token_stack.tokens[token_i-1].start && i <= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + highlight_color = style->highlight_junk_color; + } + else if (panel->show_whitespace && character_is_any_whitespace(data[i])){ + highlight_color = style->highlight_white_color; + } + + i32 cursor_mode = 0; + if (panel->show_temp_highlight){ + if (panel->temp_highlight.pos <= i && i < panel->temp_highlight_end_pos){ + cursor_mode = 2; + } + } + else{ + if (panel->cursor.pos == i){ + cursor_mode = 1; + } + } + + if (!panel->unwrapped_lines && pos_x + character_w > max_x){ + pos_x = 0; + pos_y += font->line_skip; + } + + if (highlight_color != 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED && + to_render == '\r' && + i + 1 < size && + data[i+1] == '\n'){ + // DO NOTHING + } + else{ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + } + } + + if (cursor_mode){ + if (is_active){ + u32 color = 0x00000000; + switch (cursor_mode){ + case 1: + color = style->cursor_color; + break; + case 2: + color = style->highlight_color; + break; + } + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + color, panel_area); + } + else{ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->cursor_color, + panel_area); + } + } + + if (i == panel->mark){ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->mark_color, + panel_area); + } + + if (to_render == '\r'){ + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + if (i + 1 < size && data[i+1] == '\n'){ + // DO NOTHING + } + else{ + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + } + }break; + + case ENDLINE_RN_SEPARATE: + { + pos_x = 0; + pos_y += font->line_skip; + }break; + + case ENDLINE_RN_SHOWALLR: + { + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + }break; + } + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += font->line_skip; + } + else if (to_render == '\t'){ + if (highlight_color != 0){ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x + character_w, + offset_y + (i32)pos_y, + character_w*(tab_width-1), font->line_skip, + highlight_color, panel_area); + } + pos_x += character_w*tab_width; + } + + else if (font->glyphs[to_render].data){ + u32 char_color = main_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, to_render, + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, char_color, + panel_area); + pos_x += character_w; + } + + else{ + pos_x += character_w; + } + + if (pos_y > max_y){ + break; + } + } + } +} + +/* + * Hot Directory + */ + +struct Hot_Directory{ + String string; + File_List file_list; +}; + +internal void +hot_directory_init(Hot_Directory *hot_directory){ + hot_directory->string = make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + i32 dir_size = system_get_working_directory(hot_directory->string.str, + hot_directory->string.memory_size); + + if (dir_size <= 0){ + dir_size = system_get_easy_directory(hot_directory->string.str); + } + + hot_directory->string.size = dir_size; + + append(&hot_directory->string, (u8*)"\\"); +} + +internal void +hot_directory_reload_list(Hot_Directory *hot_directory){ + if (hot_directory->file_list.block){ + system_free_file_list(hot_directory->file_list); + } + hot_directory->file_list = system_get_files(hot_directory->string); +} + +internal bool32 +hot_directory_set(Hot_Directory *hot_directory, String str){ + bool32 did_set = 0; + if (copy_checked(&hot_directory->string, str)){ + did_set = 1; + system_free_file_list(hot_directory->file_list); + hot_directory->file_list = system_get_files(str); + } + return did_set; +} + +struct Hot_Directory_Match{ + u8 *filename; + bool32 is_folder; +}; + +internal Hot_Directory_Match +hot_directory_first_match(Hot_Directory *hot_directory, + String directory, + bool32 include_files, + bool32 exact_match){ + Hot_Directory_Match result = {}; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + bool32 is_match = 0; + if (exact_match){ + if (match(filename, directory)){ + is_match = 1; + } + } + else{ + if (directory.size == 0 || + has_substr_unsensitive(filename, directory)){ + is_match = 1; + } + } + + if (is_match){ + result.is_folder = files_it.folder_stage; + result.filename = filename; + break; + } + + files_iterator_step(&files, &files_it); + } + + return result; +} + +/* +* App Structs +*/ + +struct Command_Data; +typedef void (*Command_Function)(Command_Data *command); + +enum Input_Request_Type{ + REQUEST_SYS_FILE = 0, + REQUEST_LIVE_FILE = 1, + // never below this + REQUEST_COUNT = 2 +}; + +struct Input_Request{ + Input_Request_Type type; + union{ + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + bool32 fast_folder; + } sys_file; + + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + } live_file; + }; +}; + +enum App_State{ + APP_STATE_EDIT, + APP_STATE_SEARCH, + APP_STATE_RESIZING, + // never below this + APP_STATE_COUNT +}; + +struct App_State_Incremental_Search{ + String str; + bool32 reverse; + i32 pos; +}; + +struct App_State_Resizing{ + Editing_Panel *left, *right; +}; + +struct Command_Map{ + Command_Function basic_mode_key[1+sizeof(Key_Codes)/2]; + Command_Function control_ascii[128]; + Command_Function control_key[1+sizeof(Key_Codes)/2]; +}; + +struct App_Vars{ + Command_Map map; + + Font font; + Editing_Style style; + Interactive_Style command_style; + + Editing_Working_Set working_set; + + Editing_Layout layout; + real32 last_click_x, last_click_y; + + Hot_Directory hot_directory; + + Input_Request request_queue[16]; + i32 request_count, request_filled, request_max; + + Command_Function pending_command; + + App_State state; + union{ + App_State_Incremental_Search isearch; + App_State_Resizing resizing; + }; +}; + +/* +* Commands +*/ + +struct Command_Data{ + Editing_Panel *panel; + Editing_Working_Set *working_set; + Editing_Layout *layout; + Editing_Style *style; + App_Vars *vars; + + i32 screen_width, screen_height; + i32 screen_y_off; + Key_Event_Data key; + + Input_Request *requests; + i32 request_count, request_filled; +}; + +internal void +app_clear_request_queue(App_Vars *vars){ + for (i32 i = 0; i < vars->request_count; ++i){ + Input_Request *request = vars->request_queue + i; + switch (request->type){ + case REQUEST_SYS_FILE: + { + system_free_memory(request->sys_file.query.str); + system_free_memory(request->sys_file.dest.str); + }break; + + case REQUEST_LIVE_FILE: + { + system_free_memory(request->live_file.query.str); + system_free_memory(request->live_file.dest.str); + }break; + } + } + vars->request_count = 0; + vars->request_filled = 0; +} + +internal void +command_write_character(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_write_character(panel, (u8)command->key.character); + if (panel->unwrapped_lines){ + panel->preferred_x = panel->cursor.unwrapped_x; + } + else{ + panel->preferred_x = panel->cursor.wrapped_x; + } + panel->file->cursor.pos = panel->cursor.pos; + + switch ((u8)command->key.character){ + case '{': case '}': + case '(': case ')': + case ';': case ':': + case '#': case '\n': + { + panel_auto_tab(panel, panel->cursor.pos, panel->cursor.pos); + }break; + } + panel_measure_all_wrapped_y(panel); +} + +internal void +command_move_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_left(pos, data); + if (pos > 0){ + --pos; + } + else{ + pos = pos_adjust_to_self(pos, data, file->size); + } + } + else{ + --pos; + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_move_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos < size){ + ++pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, size); + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_backspace(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (cursor_pos > 0 && cursor_pos <= (i32)file->size){ + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + i32 target_pos = cursor_pos; + if (cursor_pos > 1 && + data[cursor_pos] == '\n' && + data[cursor_pos-1] == '\r'){ + --target_pos; + } + + if (target_pos > 1 && + data[target_pos-1] == '\n' && + data[target_pos-2] == '\r'){ + + buffer_delete_range(file, target_pos-2, target_pos); + if (panel->mark >= cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 2; + } + else{ + if (target_pos > 0){ + buffer_delete(file, target_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + } + } + else{ + buffer_delete(file, cursor_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} +internal void +command_delete(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (file->size > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + if (cursor_pos > 0 && + data[cursor_pos-1] == '\r' && + data[cursor_pos] == '\n'){ + buffer_delete_range(file, cursor_pos-1, cursor_pos+1); + if (panel->mark > cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 1; + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} + +internal void +command_move_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)-1; + i32 px = panel->preferred_x; + if (cy >= 0){ + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +internal void +command_move_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)+1; + i32 px = panel->preferred_x; + + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; +} + +internal void +command_seek_end_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_end_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_beginning_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u32 size = file->size; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + while (pos < size && !character_is_any_whitespace(data[pos])){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + --pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + while (pos > 0 && !character_is_any_whitespace(data[pos])){ + --pos; + } + + if (pos != 0){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + bool32 no_hard_character = 0; + while (pos > 0){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + --pos; + } + + if (pos != 0){ + ++pos; + } + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, file->size); + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + bool32 no_hard_character = 0; + i32 prev_endline = -1; + while (pos < size){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + prev_endline = pos; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + ++pos; + } + + if (prev_endline == -1 || prev_endline+1 >= size){ + pos = size-1; + } + else{ + pos = prev_endline+1; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_token_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + + // TODO(allen): Make nulltoken? + if (current_token == -1){ + current_token = 0; + } + Cpp_Token *token = &file->token_stack.tokens[current_token]; + if (token->start == panel->cursor.pos && current_token > 0){ + --token; + } + + panel_cursor_move(panel, token->start); +} + +internal void +command_seek_token_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + // TODO(allen): Make nulltoken? + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + if (get_result.in_whitespace){ + ++current_token; + } + + if (current_token >= file->token_stack.count){ + current_token = file->token_stack.count - 1; + } + + Cpp_Token *token = &file->token_stack.tokens[current_token]; + + panel_cursor_move(panel, token->start + token->size); +} + +internal void +command_begin_search_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 0; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_begin_rsearch_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 1; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_set_mark(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->mark = (i32)panel->cursor.pos; +} + +internal void +command_copy(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + u8 *data = (u8*)panel->file->data; + if (panel->file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + } +} + +internal void +command_cut(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + buffer_delete_range(file, range); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, panel->mark); + } +} + +internal void +command_paste(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0){ + panel->next_mode.rewrite = 1; + + Editing_File *file = panel->file; + + String *src = working_set_clipboard_head(working_set); + i32 pos_left = panel->cursor.pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos_left = pos_adjust_to_left(pos_left, file->data); + } + panel_write_chunk(panel, src, pos_left); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(pos_left, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = pos_left; + panel->paste_effect.end = pos_left + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } +} + +internal void +command_paste_next(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0 && panel->mode.rewrite){ + panel->next_mode.rewrite = 1; + + Range range = get_range(panel->mark, panel->cursor.pos); + + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + + String *src = working_set_clipboard_roll_down(working_set); + buffer_replace_range(file, range.smaller, range.larger, + src->str, src->size); + + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller+src->size); + panel->preferred_x = panel_get_cursor_x(panel); + panel->file->cursor.pos = panel->cursor.pos; + + // TODO(allen): faster way to recompute line measurements afterwards + buffer_measure_all_lines(file); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = range.smaller; + panel->paste_effect.end = range.smaller + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } + } +} + +internal void +command_delete_chunk(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + buffer_delete_range(file, range); + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +// TODO(allen): Make this preserve order (look at layout_open_panel) +// Make this preserve old ratios or sizes of panels instead of +// resizing them all. +internal void +command_open_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + Editing_Working_Set *working_set = command->working_set; + // TODO(allen): This is wrong. We should not be passing in the style + // like this. The system should be looking it up here. + Editing_Style *style = command->style; + // TODO(allen): change the screen info to real32 at the base level? + real32 screen_full_width = (real32)command->screen_width; + real32 screen_full_height = (real32)command->screen_height; + real32 screen_y_off = (real32)command->screen_y_off; + + layout_open_panel(layout, working_set->files, style); + real32 panel_w = ((real32)screen_full_width / layout->panel_count); + real32 panel_x_pos = 0; + + for (i32 panel_i = 0; panel_i < layout->panel_count; ++panel_i){ + Editing_Panel *panel = &layout->panels[panel_i]; + + panel->full_x = Floor(panel_x_pos); + panel->full_w = Floor(panel_w); + panel->full_y = Floor(screen_y_off); + panel->full_h = Floor(screen_full_height - screen_y_off); + + panel_fix_internal_area(panel); + + panel_x_pos += panel_w; + } +} + +internal void +command_close_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + i32 share_w = layout->panels[layout->active_panel].full_w; + layout_close_panel(layout, layout->active_panel); + layout_redistribute_width(layout, share_w); +} + +internal Input_Request* +app_request_sys_file(App_Vars *vars, String query, String dest_init, bool32 fast_folder){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_SYS_FILE; + request->sys_file.fast_folder = 1; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->sys_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->sys_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->sys_file.query, query); + copy(&request->sys_file.dest, dest_init); + return request; +} + + +internal Input_Request* +app_request_live_file(App_Vars *vars, String query, String dest_init){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_LIVE_FILE; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->live_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->live_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->live_file.query, query); + copy(&request->live_file.dest, dest_init); + return request; +} + +internal void +panel_set_to_new(Editing_Panel *panel){ + panel->cursor = {}; + panel->cursor.pos = + pos_adjust_to_self(0, panel->file->data, panel->file->size); + panel->scroll_y = 0; + panel->target_y = 0; + panel->vel_y = 1.f; + panel->scroll_x = 0; + panel->target_x = 0; + panel->vel_x = 1.f; +} + +internal void +command_reopen(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (!file->is_dummy){ + Editing_File temp_file; + if (buffer_load(&temp_file, (u8*)make_c_str(file->source_path))){ + buffer_close(file); + + // TODO(allen): Deduplicate!! + Cpp_File cpp_file; + cpp_file.data = temp_file.data; + cpp_file.size = temp_file.size; + { + i32 size = cpp_lex_file_token_count(cpp_file); + size = cpp_get_token_stack_size(size); + temp_file.token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(cpp_file, &temp_file.token_stack); + temp_file.tokens_complete = 1; + temp_file.tokens_exist = 1; + + *file = temp_file; + panel_set_to_new(panel); + panel_measure_all_wrapped_y(panel); + // TODO(allen): Update all other panels that also view this file. + } + } +} + +internal void +command_interactive_open(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + // TODO(allen): Can all of this be simplified? I don't like having this much + // complexity for issuing a single request. + vars->pending_command = command_interactive_open; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Open: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + Editing_File *new_file; + new_file = buffer_open(&vars->working_set, string->str, command->style); + + if (!new_file){ + // TODO(allen): Here's a really tough one. This crap is now happening twice. + // Once here and also in the single_file_input function which checks all of it + // whenever the fast_folder_select option is on to see if the user is selecting + // a folder. It would be nice to be able to share that information to here when it + // is available so we could avoid doing the exact same computations here. + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *string); + + Hot_Directory_Match match = + hot_directory_first_match(&vars->hot_directory, + front_name, 1, 0); + + if (match.filename){ + // NOTE(allen): is_folder case is handled by the single_file_input function + // which would only pass control to this command if the user did not match to + // a folder. + Assert(!match.is_folder); + new_file = buffer_open(&vars->working_set, string->str, command->style); + } + } + + if (new_file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = new_file; + panel_set_to_new(active_panel); + panel_measure_all_wrapped_y(active_panel); + + Cpp_File file; + file.data = new_file->data; + file.size = new_file->size; + // TODO(allen): Where should the memory for tokens come from IRL? + { + i32 size = cpp_lex_file_token_count(file); + size = cpp_get_token_stack_size(size); + new_file->token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(file, &new_file->token_stack); + new_file->tokens_complete = 1; + new_file->tokens_exist = 1; + } + } +} + +internal void +command_save(Command_Data *command){ + Editing_Panel *panel = command->panel; + String *file_path = &panel->file->source_path; + if (file_path->size > 0){ + buffer_save(panel->file, file_path->str); + } +} + +internal void +command_interactive_save_as(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_save_as; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Save As: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + + buffer_save_and_set_names(command->panel->file, string->str); + } +} + +internal void +command_change_active_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + if (layout->panel_count > 1){ + ++layout->active_panel; + if (layout->active_panel >= layout->panel_count){ + layout->active_panel = 0; + } + } +} + +internal void +command_interactive_switch_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_switch_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Switch To: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(command->working_set, *string); + + if (file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = file; + Panel_Cursor_Data cursor_data; + cursor_data = panel_compute_cursor_from_pos(active_panel, file->cursor.pos); + active_panel->cursor = cursor_data; + } + } +} + +internal void +command_kill_file(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (file && file->max_size != 0){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } +} + +internal void +command_interactive_kill_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_kill_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Kill: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(&vars->working_set, *string); + + if (file){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } + } +} + +internal void +command_toggle_line_wrap(Command_Data *command){ + Editing_Panel *panel = command->panel; + if (panel->unwrapped_lines){ + panel->unwrapped_lines = 0; + panel->target_x = 0; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + else{ + panel->unwrapped_lines = 1; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } +} + +internal void +command_toggle_endline_mode(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + panel->cursor.pos = pos_adjust_to_left(panel->cursor.pos, panel->file->data); + file->endline_mode = ENDLINE_RN_SEPARATE; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SEPARATE: + { + file->endline_mode = ENDLINE_RN_SHOWALLR; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SHOWALLR: + { + panel->cursor.pos = pos_adjust_to_self(panel->cursor.pos, panel->file->data, + panel->file->size); + file->endline_mode = ENDLINE_RN_COMBINED; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + } +} + +internal void +command_to_uppercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'a' && data[i] <= 'z'){ + data[i] += (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_to_lowercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'A' && data[i] <= 'Z'){ + data[i] -= (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_toggle_show_whitespace(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->show_whitespace = !panel->show_whitespace; +} + +internal void +command_clean_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + i32 last_hard_start = pos-1; + i32 last_hard = last_hard_start; + while (pos < file->size && data[pos] != '\n'){ + if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + } + ++pos; + } + + if (last_hard != last_hard_start){ + pos = pos_adjust_to_left(pos, data); + + if (last_hard + 1 < pos){ + buffer_replace_range(file, last_hard+1, pos, 0, 0, REP_WHITESPACE); + panel_measure_all_wrapped_y(panel); + + if (panel->cursor.pos > last_hard){ + panel->cursor = panel_compute_cursor_from_pos(panel, last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= pos - (last_hard + 1); + } + } + } + else{ + panel_measure_all_wrapped_y(panel); + panel_auto_tab(panel, pos, pos); + } +} + +internal void +command_clean_all_lines(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 cursor_pos = panel->cursor.pos; + i32 pos = 0; + i32 last_hard = -1; + bool32 is_all_white = 1; + while (pos <= file->size){ + if (pos == file->size || data[pos] == '\n'){ + i32 line_pos = pos; + if (pos < file->size && data[pos] == '\n'){ + line_pos = pos_adjust_to_left(pos, data); + } + + // TODO(allen): This should be optimized by either keeping track of the nesting level + // in this funciton, or by at least having a nesting hint that is used and updated + // every time an auto tab happens. Also auto tab should take a nesting hint. + if (is_all_white){ + panel_auto_tab(panel, pos, pos); + } + else{ + if (last_hard + 1 < line_pos){ + buffer_replace_range(file, last_hard+1, line_pos, 0, 0, REP_WHITESPACE); + + if (cursor_pos > last_hard && cursor_pos <= pos){ + cursor_pos = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (cursor_pos > pos){ + cursor_pos -= line_pos - (last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= line_pos - (last_hard + 1); + } + pos -= line_pos - (last_hard + 1); + } + } + + last_hard = pos; + is_all_white = 1; + } + else if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + is_all_white = 0; + } + ++pos; + } + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, cursor_pos); +} + +internal void +command_eol_dosify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_RN, ENDLINE_ERASE, ENDLINE_RN); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_eol_nixify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_N, ENDLINE_ERASE, ENDLINE_N); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_auto_tab(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + panel_auto_tab(panel, range.smaller, range.larger); + panel_measure_all_wrapped_y(panel); +} + +internal void +setup_commands(Command_Map *commands, Key_Codes *codes){ + commands->basic_mode_key[codes->left] = command_move_left; + commands->basic_mode_key[codes->right] = command_move_right; + commands->basic_mode_key[codes->del] = command_delete; + commands->basic_mode_key[codes->back] = command_backspace; + commands->basic_mode_key[codes->up] = command_move_up; + commands->basic_mode_key[codes->down] = command_move_down; + commands->basic_mode_key[codes->end] = command_seek_end_of_line; + commands->basic_mode_key[codes->home] = command_seek_beginning_of_line; + +#if 0 + commands->control_key[codes->right] = command_seek_token_right; + commands->control_ascii['m'] = command_seek_token_right; + commands->control_key[codes->left] = command_seek_token_left; + commands->control_ascii['n'] = command_seek_token_left; +#else + commands->control_key[codes->right] = command_seek_whitespace_right; + commands->control_ascii['m'] = command_seek_whitespace_right; + commands->control_key[codes->left] = command_seek_whitespace_left; + commands->control_ascii['n'] = command_seek_whitespace_left; +#endif + + commands->control_key[codes->up] = command_seek_whitespace_up; + commands->control_ascii['y'] = command_seek_whitespace_up; + commands->control_key[codes->down] = command_seek_whitespace_down; + commands->control_ascii['h'] = command_seek_whitespace_down; + + commands->control_ascii['\t'] = command_auto_tab; + + commands->control_ascii[' '] = command_set_mark; + commands->control_ascii['c'] = command_copy; + commands->control_ascii['x'] = command_cut; + commands->control_ascii['v'] = command_paste; + commands->control_ascii['V'] = command_paste_next; + commands->control_ascii['d'] = command_delete_chunk; + commands->control_ascii['p'] = command_open_panel; + commands->control_ascii['P'] = command_close_panel; + commands->control_ascii['o'] = command_interactive_open; + commands->control_ascii['O'] = command_reopen; + commands->control_ascii['s'] = command_save; + commands->control_ascii['w'] = command_interactive_save_as; + commands->control_ascii[','] = command_change_active_panel; + commands->control_ascii['i'] = command_interactive_switch_file; + commands->control_ascii['k'] = command_interactive_kill_file; + commands->control_ascii['K'] = command_kill_file; + commands->control_ascii['l'] = command_toggle_line_wrap; + commands->control_ascii['L'] = command_toggle_endline_mode; + commands->control_ascii['u'] = command_to_uppercase; + commands->control_ascii['j'] = command_to_lowercase; + commands->control_ascii['?'] = command_toggle_show_whitespace; + commands->control_ascii['`'] = command_clean_line; + commands->control_ascii['~'] = command_clean_all_lines; + commands->control_ascii['1'] = command_eol_dosify; + commands->control_ascii['!'] = command_eol_nixify; + commands->control_ascii['f'] = command_begin_search_state; + commands->control_ascii['r'] = command_begin_rsearch_state; +} + +/* +* Interactive Bar +*/ + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, u8 *str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; str[i]; ++i){ + font_draw_glyph(target, bar->style.font, str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, String str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; i < str.size; ++i){ + font_draw_glyph(target, bar->style.font, str.str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +hot_directory_draw_helper(Render_Target *target, + Hot_Directory *hot_directory, + Interactive_Bar *bar, String *string, + bool32 include_files){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.pop1_color); + + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + + get_front_of_directory(&front_name, *string); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + if (front_name.size == 0 || has_substr_unsensitive(filename, front_name)){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + if (files_it.folder_stage){ + intbar_draw_string(target, bar, filename, bar->style.pop1_color); + intbar_draw_string(target, bar, (u8*)"/", bar->style.pop1_color); + } + else{ + intbar_draw_string(target, bar, filename, bar->style.base_color); + } + } + + files_iterator_step(&files, &files_it); + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +internal void +live_file_draw_helper(Render_Target *target, + Editing_Working_Set *working_set, + Interactive_Bar *bar, String *string){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.base_color); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + for (i32 file_i = 0; + file_i < working_set->file_index_count; + ++file_i){ + Editing_File *file = &working_set->files[file_i]; + if (file->live_name.str && + (string->size == 0 || has_substr_unsensitive(file->live_name, *string))){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + intbar_draw_string(target, bar, file->live_name, bar->style.base_color); + } + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +/* +* App Functions +*/ + +internal bool32 +app_init(Thread_Context *thread, Application_Memory *memory, + Key_Codes *loose_codes, Clipboard_Contents clipboard){ + Partition_€ursor partition = + partition_open(memory->vars_memory, memory->vars_memory_size); + + App_Vars *vars = (App_Vars*) + partition_allocate(&partition, sizeof(App_Vars)); + + Assert(vars); + *vars = {}; + + u32 panel_max_count = vars->layout.panel_max_count = 4; + u32 panel_count = vars->layout.panel_count = 1; + Assert(panel_max_count >= 1); + Editing_Panel *panels = vars->layout.panels = (Editing_Panel*) + partition_allocate(&partition, sizeof(Editing_Panel)*panel_max_count); + + Assert(panels); + // TODO(allen): improved Assert + + // NOTE(allen): command map setup + setup_commands(&vars->map, loose_codes); + + // NOTE(allen): font setup + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + i32 memory_used = 0; + if (font_load(&vars->font, 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): file setup + vars->working_set.file_index_count = 1; + vars->working_set.file_max_count = 29; + + vars->working_set.files = (Editing_File*) + partition_allocate(&partition, + sizeof(Editing_File)*vars->working_set.file_max_count); + + buffer_get_dummy(&vars->working_set.files[0], &vars->style); + + // NOTE(allen): clipboard setup + vars->working_set.clipboard_max_size = ArrayCount(vars->working_set.clipboards); + vars->working_set.clipboard_size = 0; + vars->working_set.clipboard_current = 0; + vars->working_set.clipboard_rolling = 0; + + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // TODO(allen): more robust allocation solution for the clipboard? + + // NOTE(allen): style setup + // TODO(allen): style_set_font function + vars->style.font = &vars->font; + vars->style.font_metrics.character_width = vars->font.glyphs[' '].advance; + vars->style.font_metrics.line_skip = vars->font.line_skip; + + vars->style.back_color = 0xFF0C0C0C; + vars->style.margin_color = 0xFF181818; + vars->style.cursor_color = 0xFF00EE00; + vars->style.highlight_color = 0xFFDDEE00; + vars->style.mark_color = 0xFF494949; + vars->style.default_color = 0xFF90B080; + vars->style.at_cursor_color = vars->style.back_color; + vars->style.at_highlight_color = 0xFFFF44DD; + vars->style.comment_color = 0xFF2090F0; + vars->style.keyword_color = 0xFFD08F20; + vars->style.constant_color = 0xFF50FF30; + vars->style.special_character_color = 0xFFFF0000; + + vars->style.use_paste_color = 1; + vars->style.paste_color = 0xFFDDEE00; + + vars->style.highlight_junk_color = 0x44FF0000; + vars->style.highlight_white_color = 0x1100FFFF; + vars->style.tab_width = 4; + vars->style.margin_width = 5; + + Interactive_Style file_info_style; + file_info_style.font = &vars->font; + file_info_style.bar_color = 0xFF888888; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF4444AA; + file_info_style.pop2_color = 0xFFFF0000; + file_info_style.height = vars->style.font->line_skip + 2; + + vars->style.file_info_style = file_info_style; + + Interactive_Style command_style; + command_style.font = &vars->font; + command_style.bar_color = 0xFF0C0C0C; + command_style.base_color = 0xFFDDDDBB; + command_style.pop1_color = 0xFF4444AA; + command_style.pop2_color = 0xFF44FF44; + command_style.height = vars->style.font->line_skip + 2; + + vars->command_style = command_style; + + + // NOTE(allen): panel setup + AllowLocal(panel_count); + panel_init(&panels[0], &vars->working_set.files[0]); + + // NOTE(allen): hot directory setup + hot_directory_init(&vars->hot_directory); + + // NOTE(allen): request stack setup + vars->request_count = 0; + vars->request_filled = 0; + vars->request_max = ArrayCount(vars->request_queue); + + return 1; +} + +struct Single_Line_Input_Step{ + bool32 hit_newline; + bool32 hit_ctrl_newline; + bool32 hit_a_character; + bool32 hit_backspace; + bool32 hit_esc; + bool32 made_a_change; + bool32 did_command; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + bool32 fast_folder_select; +}; + +internal Single_Line_Input_Step +app_single_line_input_core(Key_Codes *codes, + Key_Input_Data *input, + Single_Line_Mode mode){ + Single_Line_Input_Step result = {}; + + if (input->has_press){ + if (input->press.keycode == codes->back){ + result.hit_backspace = 1; + switch (mode.type){ + case SINGLE_LINE_STRING: + { + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + if (mode.string->size > 0){ + --mode.string->size; + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size > 0){ + --mode.string->size; + i8 end_character = mode.string->str[mode.string->size]; + if (character_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + // TODO(allen): What to do when the string becomes empty though? + mode.string->str[mode.string->size] = 0; + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + mode.string->str[mode.string->size] = 0; + } + result.made_a_change = 1; + } + }break; + } + } + + else if (input->press.keycode == codes->newline){ + if (input->control_keys[CONTROL_KEY_CONTROL]){ + result.hit_ctrl_newline = 1; + result.made_a_change = 1; + } + + else{ + result.made_a_change = 1; + if (mode.fast_folder_select){ + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *mode.string); + Hot_Directory_Match match; + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 1); + + if (!match.filename){ + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0); + } + + if (match.filename){ + if (match.is_folder){ + set_last_folder(mode.string, match.filename); + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + append(mode.string, match.filename); + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + } + + else if (input->press.keycode == codes->esc){ + result.hit_esc = 1; + result.made_a_change = 1; + } + + else if (input->press.character){ + result.hit_a_character = 1; + if (!input->control_keys[CONTROL_KEY_CONTROL]){ + switch (mode.type){ + case SINGLE_LINE_STRING: + { + if (mode.string->size+1 < mode.string->memory_size){ + mode.string->str[mode.string->size] = (i8)input->press.character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size+1 < mode.string->memory_size){ + i8 new_character = (i8)input->press.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + + if (character_is_slash(new_character)){ + hot_directory_set(mode.hot_directory, *mode.string); + } + result.made_a_change = 1; + } + }break; + } + } + + else{ + result.did_command = 1; + result.made_a_change = 1; + } + } + } + + return result; +} + +inline internal Single_Line_Input_Step +app_single_line_input_step(Key_Codes *codes, Key_Input_Data *input, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Single_Line_Input_Step +app_single_file_input_step(Key_Codes *codes, Key_Input_Data *input, + String *string, Hot_Directory *hot_directory, + bool32 fast_folder_select){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.fast_folder_select = fast_folder_select; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Command_Function +app_get_command(App_Vars *vars, Key_Input_Data *input, Key_Event_Data key){ + Command_Function function = 0; + if (input->control_keys[CONTROL_KEY_CONTROL]){ + if (key.character_no_caps_lock){ + function = vars->map.control_ascii[key.character_no_caps_lock]; + } + else{ + function = vars->map.control_key[key.keycode]; + } + } + else if (!input->control_keys[CONTROL_KEY_ALT]){ + if (key.character != 0){ + function = command_write_character; + } + else{ + function = vars->map.basic_mode_key[key.keycode]; + } + } + return function; +} + +internal bool32 +smooth_camera_step(real32 *target, real32 *current, real32 *vel, real32 S, real32 T){ + bool32 result = 0; + real32 targ = *target; + real32 curr = *current; + real32 v = *vel; + if (curr != targ){ + real32 L = lerp(curr, T, targ); + + i32 sign = (targ > curr) - (targ < curr); + real32 V = curr + sign*v; + + if (sign > 0){ + curr = Min(L, V); + } + else{ + curr = Max(L, V); + } + + if (curr == V){ + v *= S; + } + + if (curr > targ - .0001f && curr < targ + .0001f){ + curr = targ; + v = 1.f; + } + *target = targ; + *current = curr; + *vel = v; + result = 1; + } + return result; +} + +#if FRED_INTERNAL +Application_Memory *GLOBAL; +#endif + +internal Application_Step_Result +app_step(Thread_Context *thread, Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory, + Clipboard_Contents clipboard, + bool32 first_step){ + +#if FRED_INTERNAL + GLOBAL = memory; +#endif + + ProfileStart(app_step); + ProfileSection(thread, app_step, "start"); + + Application_Step_Result app_result = {}; + app_result.redraw = 1; + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // TODO(allen): Let's find another way to do first_step + // so we can get it out of app_step and the platform layer. + if (first_step || !time_step){ + app_result.redraw = 1; + } + + Editing_Panel *panels = vars->layout.panels; + Editing_Panel *active_panel = &panels[vars->layout.active_panel]; + ProfileSection(thread, app_step, "setup"); + + // NOTE(allen): OS clipboard event handling + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // NOTE(allen): check files are up to date + for (i32 i = 0; i < vars->working_set.file_index_count; ++i){ + Editing_File *file = vars->working_set.files + i; + + if (!file->is_dummy){ + Time_Stamp time_stamp; + time_stamp = system_file_time_stamp((u8*)make_c_str(file->source_path)); + + if (time_stamp.success){ + file->last_sys_write_time = time_stamp.time; + if (file->last_sys_write_time != file->last_4ed_write_time){ + app_result.redraw = 1; + } + } + } + } + ProfileSection(thread, app_step, "OS syncing"); + + // NOTE(allen): keyboard input handling + if (time_step){ + switch (vars->state){ + case APP_STATE_EDIT: + { + Command_Data command_data; + command_data.panel = active_panel; + command_data.working_set = &vars->working_set; + command_data.layout = &vars->layout; + command_data.style = &vars->style; + command_data.vars = vars; + command_data.screen_width = target->width; + command_data.screen_height = target->height; + command_data.screen_y_off = vars->command_style.height; + command_data.requests = 0; + command_data.request_count = 0; + command_data.request_filled = 0; + + if (vars->request_count == 0){ + Command_Function function = 0; + if (input->has_press){ + command_data.key = input->press; + function = app_get_command(vars, input, command_data.key); + } + + else if (input->has_hold){ + command_data.key = input->hold; + function = app_get_command(vars, input, command_data.key); + + } + + if (function){ + command_data.panel->next_mode = {}; + function(&command_data); + app_result.redraw = 1; + command_data.panel->mode = command_data.panel->next_mode; + } + } + + else{ + + Input_Request *active_request = vars->request_queue + vars->request_filled; + bool32 killed_command = 0; + switch (active_request->type){ + case REQUEST_SYS_FILE: + { + String *string = &active_request->sys_file.dest; + Single_Line_Input_Step result = + app_single_file_input_step(codes, input, string, &vars->hot_directory, 1); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->sys_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + case REQUEST_LIVE_FILE: + { + String *string = &active_request->live_file.dest; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->live_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + } + + if (vars->request_filled == vars->request_count && !killed_command){ + command_data.requests = vars->request_queue; + command_data.request_count = vars->request_count; + command_data.request_filled = vars->request_filled; + vars->pending_command(&command_data); + app_clear_request_queue(vars); + } + } + + }break; + + case APP_STATE_SEARCH: + { + String *string = &vars->isearch.str; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + + Editing_File *file = active_panel->file; + + bool32 step_forward = 0; + bool32 step_backward = 0; + + if (input->has_press){ + Key_Event_Data key = input->press; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + if (!step_forward && !step_backward && + !input->has_press && input->has_hold){ + Key_Event_Data key = input->hold; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + i32 start_pos = vars->isearch.pos; + if (step_forward){ + if (vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 0; + } + } + if (step_backward){ + if (!vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 1; + } + } + + String file_string = make_string(file->data, file->size); + + i32 pos; + + if (vars->isearch.reverse){ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + } + else{ + pos = rfind_substr(file_string, start_pos - 1, *string); + + if (pos >= 0){ + if (step_backward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = rfind_substr(file_string, start_pos - 1, *string); + if (pos == -1){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + else{ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + } + else{ + pos = find_substr(file_string, start_pos + 1, *string); + + if (pos < file->size){ + if (step_forward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = find_substr(file_string, start_pos + 1, *string); + if (pos == file->size){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + } + + if (result.hit_newline || result.hit_ctrl_newline){ + panel_cursor_move(active_panel, active_panel->temp_highlight); + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + if (result.hit_esc){ + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + }break; + + case APP_STATE_RESIZING: + { + if (input->has_press){ + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "keyboard input"); + + // NOTE(allen): reorganizing panels on screen + i32 prev_width = vars->layout.full_width; + i32 prev_height = vars->layout.full_height; + i32 full_width = vars->layout.full_width = target->width; + i32 full_height = vars->layout.full_height = target->height; + + if (prev_width != full_width || prev_height != full_height){ + layout_refit(&vars->layout, 0, vars->command_style.height, + full_width, full_height, + prev_width, prev_height); + + for (i32 panel_i = 0; + panel_i < vars->layout.panel_count && + vars->layout.panel_count > 1; + ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + + Editing_Style *style = panel->file->style; + i32 character_w = style_get_character_width(style); + i32 margin_width = style->margin_width; + if (panel->full_w < character_w*6 + margin_width*2){ + i32 share_w = panel->full_w; + layout_close_panel(&vars->layout, panel_i); + layout_redistribute_width(&vars->layout, share_w); + panel_i = 0; + } + } + + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = panels + panel_i; + if (!panel->file->is_dummy){ + panel->cursor = panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + } + + app_result.redraw = 1; + } + ProfileSection(thread, app_step, "reorganizing panels"); + + // NOTE(allen): mouse input handling + if (time_step && !mouse->out_of_window){ + + i32 mx = mouse->x; + i32 my = mouse->y; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + // TODO(allen): + // This means any mouse use will be impossible, I guess it will have + // to depend on the type of the request seen at the top??? + app_clear_request_queue(vars); + mouse_press_event = 1; + + switch (vars->state){ + case APP_STATE_SEARCH: + { + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + }break; + } + } + + switch (vars->state){ + case APP_STATE_EDIT: + { + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + + Editing_Panel *panel = &panels[panel_i]; + + if (mx >= panel->x && + mx < panel->w + panel->x && + my >= panel->y && + my < panel->h + panel->y){ + + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_IBEAM; + + if (mouse_press_event){ + if (!panel->file->is_dummy){ + app_result.redraw = 1; + + Font *font = &vars->font; + + i32 character_w = style_get_character_width(panel->file->style); + i32 character_h = font->line_skip; + i32 max_line_length = panel_compute_max_line_length(panel); + i32 max_lines = panel_compute_max_lines(panel); + + real32 grid_x = ((real32)mx - panel->x) / character_w; + real32 grid_y = ((real32)my - panel->y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + if (grid_x >= 0 && grid_x <= max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = (i32)(grid_x + active_panel->scroll_x); + i32 pos_y = (i32)(grid_y + active_panel->scroll_y); + + panel_cursor_move(active_panel, pos_x, pos_y); + } + } + vars->layout.active_panel = panel_i; + active_panel = panel; + } + else{ + // NOTE(allen): mouse inside editing area but no click + } + } + + else if (mx >= panel->full_x && + mx < panel->full_w + panel->full_x && + my >= panel->full_y && + my < panel->full_h + panel->full_y){ + // NOTE(allen): not inside the editing area but within the margins + bool32 resize_area = 0; + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + if (mx >= (panel->full_x+panel->full_w+panel->x+panel->w)/2){ + if (panel_i != vars->layout.panel_count-1){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = 1; + } + } + else if (mx <= (panel->full_x+panel->x)/2){ + if (panel_i != 0){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = -1; + } + } + + if (resize_area != 0 && mouse_press_event){ + vars->state = APP_STATE_RESIZING; + if (resize_area == 1){ + vars->resizing.left = panel; + vars->resizing.right = panel+1; + } + else if (resize_area == -1){ + vars->resizing.left = panel-1; + vars->resizing.right = panel; + } + } + } + } + + i32 cursor_y = panel_get_cursor_y(active_panel); + real32 target_y = active_panel->target_y; + i32 max_lines = panel_compute_max_lines(active_panel); + + bool32 wheel_used; + + real32 delta_target_y = Max(1, max_lines / 3.f); + delta_target_y *= -mouse->wheel; + + target_y += delta_target_y; + + if (target_y < 0){ + target_y = 0; + } + + if (mouse->wheel == 0){ + wheel_used = 0; + } + else{ + wheel_used = 1; + + if (cursor_y >= target_y + max_lines){ + cursor_y = (i32)target_y + max_lines - 1; + } + if (cursor_y < target_y){ + cursor_y = (i32)target_y + 1; + } + } + + active_panel->target_y = target_y; + if (cursor_y != panel_get_cursor_y(active_panel)){ + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + active_panel->preferred_x, + cursor_y); + } + + if (wheel_used){ + app_result.redraw = 1; + } + }break; + + case APP_STATE_RESIZING: + { + if (mouse->left_button){ + Editing_Panel *left = vars->resizing.left; + Editing_Panel *right = vars->resizing.right; + i32 left_x = left->full_x; + i32 left_w = left->full_w; + i32 right_x = right->full_x; + i32 right_w = right->full_w; + + AllowLocal(right_x); + + i32 new_left_x = left_x; + i32 new_left_w = mx - left_x; + i32 new_right_w = right_w - (new_left_w - left_w); + i32 new_right_x = mx; + + if (left_w != new_left_w){ + app_result.redraw = 1; + + Editing_Style *left_style = left->file->style; + Editing_Style *right_style = right->file->style; + + i32 left_character_w = style_get_character_width(left_style); + i32 right_character_w = style_get_character_width(right_style); + + i32 left_margin_width = left_style->margin_width; + i32 right_margin_width = right_style->margin_width; + if (new_left_w > left_margin_width*2 + left_character_w*6 && + new_right_w > right_margin_width*2 + right_character_w*6){ + left->full_x = new_left_x; + left->full_w = new_left_w; + right->full_x = new_right_x; + right->full_w = new_right_w; + + panel_fix_internal_area(left); + panel_fix_internal_area(right); + } + } + } + else{ + app_result.redraw = 1; + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "mouse input"); + + // NOTE(allen): fix scrolling on all panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + i32 cursor_y; + if (panel->show_temp_highlight){ + if (panel->unwrapped_lines){ + cursor_y = panel->temp_highlight.unwrapped_y; + } + else{ + cursor_y = panel->temp_highlight.wrapped_y; + } + } + else{ + if (panel->unwrapped_lines){ + cursor_y = panel->cursor.unwrapped_y; + } + else{ + cursor_y = panel->cursor.wrapped_y; + } + } + real32 target_y = panel->target_y; + real32 original_target_y = target_y; + i32 max_lines = panel_compute_max_lines(panel); + + while (cursor_y >= Floor(target_y) + max_lines){ + target_y += 3.f; + } + + while (cursor_y < target_y){ + target_y -= 3.f; + } + + if (target_y < 0){ + target_y = 0; + } + + panel->target_y = target_y; + + i32 cursor_x = panel_get_cursor_x(panel); + real32 target_x = panel->target_x; + real32 original_target_x = target_x; + i32 max_x = panel_compute_max_line_length(panel); + if (cursor_x < target_x){ + target_x = (real32)Max(0, cursor_x - max_x/2); + } + else if (cursor_x >= target_x + max_x){ + target_x = (real32)(cursor_x - max_x/2); + } + + panel->target_x = target_x; + + if (original_target_y != panel->target_y || + original_target_x != panel->target_x){ + app_result.redraw; + } + } + ProfileSection(thread, app_step, "fix scrolling"); + + // NOTE(allen): execute animations + for (i32 i = 0; i < vars->layout.panel_count; ++i){ + Editing_Panel *panel = vars->layout.panels + i; + + // TODO(allen): Scrolling parameterization in style? + if (smooth_camera_step(&panel->target_y, &panel->scroll_y, &panel->vel_y, 2.f, 1.f/9.f)){ + app_result.redraw = 1; + } + + if (smooth_camera_step(&panel->target_x, &panel->scroll_x, &panel->vel_x, 2.f, 1.f/6.f)){ + app_result.redraw = 1; + } + + if (panel->paste_effect.tick_down > 0){ + --panel->paste_effect.tick_down; + app_result.redraw = 1; + } + + } + ProfileSection(thread, app_step, "execute animations"); + + if (app_result.redraw){ + // NOTE(allen): render the panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + Editing_Style *style = panel->file->style; + + i32 full_left = panel->full_x; + i32 full_top = panel->full_y; + i32 full_right = full_left + panel->full_w; + i32 full_bottom = full_top + panel->full_h; + + u32 back_color = style->back_color; + draw_rectangle_2corner(target, full_left, full_top, full_right, full_bottom, back_color); + + u32 side_margin_color = style->margin_color; + + panel_draw(thread, target, panel, vars->layout.active_panel == panel_i); + + // NOTE(allen): file info bar + { + Interactive_Bar bar; + bar.style = style->file_info_style; + bar.pos_x = panel->x; + bar.pos_y = full_top; + + draw_rectangle(target, full_left, bar.pos_y, + panel->full_w, bar.style.height, + bar.style.bar_color); + + Editing_File *file = panel->file; + if (!file->is_dummy){ + intbar_draw_string(target, &bar, panel->file->live_name, bar.style.base_color); + intbar_draw_string(target, &bar, make_lit_string(" - "), bar.style.base_color); + + u8 line_number_space[30]; + String line_number = make_string(line_number_space, 0, 30); + append(&line_number, (u8*)"L#"); + append_int_to_str(panel->cursor.line, &line_number); + + intbar_draw_string(target, &bar, line_number, bar.style.base_color); + + if (file->last_4ed_write_time != file->last_sys_write_time){ + persist String out_of_sync = make_lit_string(" FILE SYNC"); + intbar_draw_string(target, &bar, out_of_sync, bar.style.pop2_color); + } + } + } + + // L + draw_rectangle_2corner(target, full_left, panel->y, + panel->x, full_bottom, side_margin_color); + + // R + draw_rectangle_2corner(target, panel->x + panel->w, panel->y, + full_right, full_bottom, side_margin_color); + + // B + draw_rectangle_2corner(target, full_left, panel->y + panel->h, + full_right, full_bottom, side_margin_color); + + if (panel_i != 0){ + draw_rectangle_2corner(target, panel->full_x-1, panel->full_y, + panel->full_x+1, panel->full_y+panel->full_h, + 0xFFFFFFFF); + } + } + ProfileSection(thread, app_step, "render files"); + + // NOTE (allen): command bar + { + Interactive_Bar bar; + bar.style = vars->command_style; + bar.pos_x = 0; + bar.pos_y = 0; + + draw_rectangle(target, 0, 0, + target->width, bar.style.height, + bar.style.bar_color); + + switch (vars->state){ + case APP_STATE_EDIT: + { + if (vars->request_count > 0){ + Input_Request *request = vars->request_queue + vars->request_filled; + + switch (request->type){ + case REQUEST_SYS_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + hot_directory_draw_helper(target, &vars->hot_directory, &bar, string, 1); + }break; + + case REQUEST_LIVE_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + live_file_draw_helper(target, &vars->working_set, &bar, string); + }break; + + } + } + }break; + + case APP_STATE_SEARCH: + { + persist String search_str = make_lit_string("I-Search: "); + persist String rsearch_str = make_lit_string("Reverse-I-Search: "); + if (vars->isearch.reverse){ + intbar_draw_string(target, &bar, rsearch_str, bar.style.pop2_color); + } + else{ + intbar_draw_string(target, &bar, search_str, bar.style.pop2_color); + } + + intbar_draw_string(target, &bar, vars->isearch.str, bar.style.base_color); + }break; + + case APP_STATE_RESIZING: + { + intbar_draw_string(target, &bar, make_lit_string("Resizing!"), bar.style.pop2_color); + }break; + } + } + ProfileSection(thread, app_step, "interaction bar"); + + } + + ProfileEnd(thread, app_step, "total"); + + return(app_result); +} + +// BOTTOM + diff --git a/test_data/sample_files/basic.txt b/test_data/sample_files/basic.txt new file mode 100644 index 0000000..b94ea3d --- /dev/null +++ b/test_data/sample_files/basic.txt @@ -0,0 +1,2928 @@ + +/* + * Quick and Dirty Partition System + */ + +struct Partition_Cursor{ + u8 *memory_base; + u8 *memory_cursor; + i32 max_size; +}; + +internal Partition_Cursor +partition_open(void *memory, i32 size){ + Partition_Cursor partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory; + partition.max_size = size; + return partition; +} + +internal void* +partition_allocate(Partition_Cursor *data, i32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +/* + * Plaform Independent File Name Helpers + */ + +struct File_List_Iterator{ + bool32 folder_stage; + u8 **filename_ptr; +}; + +internal File_List_Iterator +files_iterator_init(File_List *files){ + File_List_Iterator files_it = {}; + if (files->folder_count > 0){ + files_it.filename_ptr = files->folder_names; + files_it.folder_stage = 1; + } + else if (files->file_count > 0){ + files_it.filename_ptr = files->file_names; + } + return files_it; +} + +internal void +files_iterator_step(File_List *files, File_List_Iterator *files_it){ + ++files_it->filename_ptr; + if (files_it->folder_stage){ + if (files_it->filename_ptr >= files->folder_names + files->folder_count){ + if (files->file_count > 0){ + files_it->filename_ptr = files->file_names; + } + else{ + files_it->filename_ptr = 0; + } + files_it->folder_stage = 0; + } + } + else{ + if (files_it->filename_ptr >= files->file_names + files->file_count){ + files_it->filename_ptr = 0; + } + } +} + +/* + * Drawing Functions + */ + +internal u32 +style_token_color(Editing_Style *style, + Cpp_Token_Type type){ + u32 result; + switch (type){ + case CPP_TOKEN_COMMENT: + result = style->comment_color; + break; + + case CPP_TOKEN_KEYWORD: + result = style->keyword_color; + break; + + case CPP_TOKEN_STRING_CONSTANT: + case CPP_TOKEN_CHARACTER_CONSTANT: + case CPP_TOKEN_INTEGER_CONSTANT: + case CPP_TOKEN_FLOATING_CONSTANT: + case CPP_TOKEN_BOOLEAN_CONSTANT: + case CPP_TOKEN_INCLUDE_FILE: + result = style->constant_color; + break; + + default: + result = style->default_color; + } + return result; +} + +internal void +panel_draw(Thread_Context *thread, Render_Target *target, + Editing_Panel *panel, bool32 is_active){ + Editing_File *file = panel->file; + Editing_Style *style = file->style; + Font *font = style->font; + + i32 character_w = (i32)(style_get_character_width(style)); + i32 character_h = (i32)(font->line_skip); + i32 offset_x = (i32)(panel->x); + i32 offset_y = (i32)(panel->y); + i32 max_x = (i32)(panel->w); + i32 max_y = (i32)(panel->h); + + Blit_Rect panel_area; + panel_area.x_start = offset_x; + panel_area.y_start = offset_y; + panel_area.x_end = offset_x + max_x; + panel_area.y_end = offset_y + max_y; + + if (!file || !file->data || file->is_dummy){ + i32 start_x = (panel_area.x_start + panel_area.x_end)/2; + i32 start_y = (panel_area.y_start + panel_area.y_end)/2; + persist String null_file_message = make_lit_string("NULL FILE"); + start_x -= (character_w*null_file_message.size)/2; + start_y -= (character_h)/2; + + real32 pos_x = 0; + real32 pos_y = 0; + + for (i32 i = 0; i < null_file_message.size; ++i){ + u8 to_render = null_file_message.str[i]; + + if (font->glyphs[to_render].data){ + font_draw_glyph_clipped(target, font, to_render, + (real32)start_x + pos_x, (real32)start_y + pos_y, + style->special_character_color, + panel_area); + pos_x += character_w; + } + } + } + + else{ + u32 tab_width = style->tab_width; + i32 size = (i32)file->size; + u8 *data = (u8*)file->data; + + real32 shift_x = 0; + shift_x = -panel->scroll_x * character_w; + + i32 truncated_start_y = (i32)panel->scroll_y; + real32 scroll_amount = panel->scroll_y - truncated_start_y; + + Panel_Cursor_Data start_cursor; + start_cursor = panel->scroll_y_cursor; + start_cursor = panel_compute_cursor_from_xy(panel, 0, truncated_start_y); + panel->scroll_y_cursor = start_cursor; + + i32 start_character = start_cursor.pos; + + real32 pos_x = 0; + real32 pos_y = -character_h*scroll_amount; + + Cpp_Token_Stack token_stack = file->token_stack; + u32 main_color = style->default_color; + u32 highlight_color = 0x00000000; + i32 token_i = 0; + bool32 tokens_exist = file->tokens_exist; + + if (tokens_exist){ + // TODO(allen): Use cpp_get_token, it binary searches this shit! + while (token_i < token_stack.count && + start_character > token_stack.tokens[token_i].start){ + ++token_i; + } + if (token_i != 0){ + main_color = + style_token_color(style, token_stack.tokens[token_i-1].type); + if (token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK){ + highlight_color = style->highlight_junk_color; + } + } + } + + // TODO(allen): Render through NULLS + for (i32 i = start_character; i < size && data[i]; ++i){ + u8 to_render = data[i]; + + u32 fade_color = 0xFFFF00FF; + real32 fade_amount = 0.f; + if (style->use_paste_color && panel->paste_effect.tick_down > 0 && + panel->paste_effect.start <= i && i < panel->paste_effect.end){ + fade_color = style->paste_color; + fade_amount = (real32)(panel->paste_effect.tick_down) / panel->paste_effect.tick_max; + } + + if (tokens_exist && token_i < token_stack.count){ + if (i == token_stack.tokens[token_i].start){ + main_color = + style_token_color(style, token_stack.tokens[token_i].type); + ++token_i; + } + if (token_i > 0 && + i >= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + main_color = 0xFFFFFFFF; + } + } + + highlight_color = 0x00000000; + + if (tokens_exist && token_i > 0 && + token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK && + i >= token_stack.tokens[token_i-1].start && + i <= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + highlight_color = style->highlight_junk_color; + } + else if (panel->show_whitespace && + character_is_any_whitespace(data[i])){ + highlight_color = style->highlight_white_color; + } + + i32 cursor_mode = 0; + if (panel->show_temp_highlight){ + if (panel->temp_highlight.pos <= i && i < panel->temp_highlight_end_pos){ + cursor_mode = 2; + } + } + else{ + if (panel->cursor.pos == i){ + cursor_mode = 1; + } + } + + if (!panel->unwrapped_lines && + pos_x + character_w > max_x){ + pos_x = 0; + pos_y += font->line_skip; + } + + if (highlight_color != 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED && + to_render == '\r' && + i + 1 < size && + data[i+1] == '\n'){ + // DO NOTHING + } + else{ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + } + } + + if (cursor_mode){ + if (is_active){ + u32 color = 0x00000000; + switch (cursor_mode){ + case 1: + color = style->cursor_color; + break; + case 2: + color = style->highlight_color; + break; + } + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + color, panel_area); + } + else{ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->cursor_color, + panel_area); + } + } + + if (i == panel->mark){ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->mark_color, + panel_area); + } + + if (to_render == '\r'){ + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + if (i + 1 < size && + data[i+1] == '\n'){ + // DO NOTHING + } + else{ + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + } + }break; + + case ENDLINE_RN_SEPARATE: + { + pos_x = 0; + pos_y += font->line_skip; + }break; + + case ENDLINE_RN_SHOWALLR: + { + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + }break; + } + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += font->line_skip; + } + else if (to_render == '\t'){ + if (highlight_color != 0){ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x + character_w, + offset_y + (i32)pos_y, + character_w*(tab_width-1), font->line_skip, + highlight_color, panel_area); + } + pos_x += character_w*tab_width; + } + + else if (font->glyphs[to_render].data){ + u32 char_color = main_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, to_render, + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, char_color, + panel_area); + pos_x += character_w; + } + + else{ + pos_x += character_w; + } + + if (pos_y > max_y){ + break; + } + } + } +} + +/* + * Hot Directory + */ + +struct Hot_Directory{ + String string; + File_List file_list; +}; + +internal void +hot_directory_init(Hot_Directory *hot_directory){ + hot_directory->string = make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + i32 dir_size = system_get_working_directory(hot_directory->string.str, + hot_directory->string.memory_size); + + if (dir_size <= 0){ + dir_size = system_get_easy_directory(hot_directory->string.str); + } + + hot_directory->string.size = dir_size; + + append(&hot_directory->string, (u8*)"\\"); +} + +internal void +hot_directory_reload_list(Hot_Directory *hot_directory){ + if (hot_directory->file_list.block){ + system_free_file_list(hot_directory->file_list); + } + hot_directory->file_list = system_get_files(hot_directory->string); +} + +internal bool32 +hot_directory_set(Hot_Directory *hot_directory, String str){ + bool32 did_set = 0; + if (copy_checked(&hot_directory->string, str)){ + did_set = 1; + system_free_file_list(hot_directory->file_list); + hot_directory->file_list = system_get_files(str); + } + return did_set; +} + +struct Hot_Directory_Match{ + u8 *filename; + bool32 is_folder; +}; + +internal Hot_Directory_Match +hot_directory_first_match(Hot_Directory *hot_directory, + String str, + bool32 include_files, + bool32 exact_match){ + Hot_Directory_Match result = {}; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + bool32 is_match = 0; + if (exact_match){ + if (match(filename, str)){ + is_match = 1; + } + } + else{ + if (str.size == 0 || + has_substr_unsensitive(filename, str)){ + is_match = 1; + } + } + + if (is_match){ + result.is_folder = files_it.folder_stage; + result.filename = filename; + break; + } + + files_iterator_step(&files, &files_it); + } + + return result; +} + +/* + * App Structs + */ + +struct Command_Data; +typedef void (*Command_Function)(Command_Data *command); + +enum Input_Request_Type{ + REQUEST_SYS_FILE, + REQUEST_LIVE_FILE, + // never below this + REQUEST_COUNT +}; + +struct Input_Request{ + Input_Request_Type type; + union{ + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + bool32 fast_folder; + } sys_file; + + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + } live_file; + }; +}; + +enum App_State{ + APP_STATE_EDIT, + APP_STATE_SEARCH, + APP_STATE_RESIZING, + // never below this + APP_STATE_COUNT +}; + +struct App_State_Incremental_Search{ + String str; + bool32 reverse; + i32 pos; +}; + +struct App_State_Resizing{ + Editing_Panel *left, *right; +}; + +struct Command_Map{ + Command_Function basic_mode_key[1+sizeof(Key_Codes)/2]; + Command_Function control_ascii[128]; + Command_Function control_key[1+sizeof(Key_Codes)/2]; +}; + +struct App_Vars{ + Command_Map map; + + Font font; + Editing_Style style; + Interactive_Style command_style; + + Editing_Working_Set working_set; + + Editing_Layout layout; + real32 last_click_x, last_click_y; + + Hot_Directory hot_directory; + + Input_Request request_queue[16]; + i32 request_count, request_filled, request_max; + + Command_Function pending_command; + + App_State state; + union{ + App_State_Incremental_Search isearch; + App_State_Resizing resizing; + }; +}; + +/* + * Commands + */ + +struct Command_Data{ + Editing_Panel *panel; + Editing_Working_Set *working_set; + Editing_Layout *layout; + Editing_Style *style; + App_Vars *vars; + + i32 screen_width, screen_height; + i32 screen_y_off; + Key_Event_Data key; + + Input_Request *requests; + i32 request_count, request_filled; +}; + +internal void +app_clear_request_queue(App_Vars *vars){ + for (i32 i = 0; i < vars->request_count; ++i){ + Input_Request *request = vars->request_queue + i; + switch (request->type){ + case REQUEST_SYS_FILE: + { + system_free_memory(request->sys_file.query.str); + system_free_memory(request->sys_file.dest.str); + }break; + + case REQUEST_LIVE_FILE: + { + system_free_memory(request->live_file.query.str); + system_free_memory(request->live_file.dest.str); + }break; + } + } + vars->request_count = 0; + vars->request_filled = 0; +} + +internal void +command_write_character(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_write_character(panel, (u8)command->key.character); + if (panel->unwrapped_lines){ + panel->preferred_x = panel->cursor.unwrapped_x; + } + else{ + panel->preferred_x = panel->cursor.wrapped_x; + } + panel->file->cursor.pos = panel->cursor.pos; + + switch ((u8)command->key.character){ + case '{': case '}': + case '(': case ')': + case ';': case ':': + case '#': case '\n': + { + panel_auto_tab(panel, panel->cursor.pos, panel->cursor.pos); + }break; + } + panel_measure_all_wrapped_y(panel); +} + +internal void +command_move_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_left(pos, data); + if (pos > 0){ + --pos; + } + else{ + pos = pos_adjust_to_self(pos, data, file->size); + } + } + else{ + --pos; + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_move_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos < size){ + ++pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, size); + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_backspace(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (cursor_pos > 0 && cursor_pos <= (i32)file->size){ + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + i32 target_pos = cursor_pos; + if (cursor_pos > 1 && + data[cursor_pos] == '\n' && + data[cursor_pos-1] == '\r'){ + --target_pos; + } + + if (target_pos > 1 && + data[target_pos-1] == '\n' && + data[target_pos-2] == '\r'){ + + buffer_delete_range(file, target_pos-2, target_pos); + if (panel->mark >= cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 2; + } + else{ + if (target_pos > 0){ + buffer_delete(file, target_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + } + } + else{ + buffer_delete(file, cursor_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} +internal void +command_delete(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (file->size > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + if (cursor_pos > 0 && + data[cursor_pos-1] == '\r' && + data[cursor_pos] == '\n'){ + buffer_delete_range(file, cursor_pos-1, cursor_pos+1); + if (panel->mark > cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 1; + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} + +internal void +command_move_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)-1; + i32 px = panel->preferred_x; + if (cy >= 0){ + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +internal void +command_move_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)+1; + i32 px = panel->preferred_x; + + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; +} + +internal void +command_seek_end_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_end_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_beginning_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u32 size = file->size; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + while (pos < size && !character_is_any_whitespace(data[pos])){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + --pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + while (pos > 0 && !character_is_any_whitespace(data[pos])){ + --pos; + } + + if (pos != 0){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + bool32 no_hard_character = 0; + while (pos > 0){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + --pos; + } + + if (pos != 0){ + ++pos; + } + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, file->size); + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + bool32 no_hard_character = 0; + i32 prev_endline = -1; + while (pos < size){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + prev_endline = pos; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + ++pos; + } + + if (prev_endline == -1 || prev_endline+1 >= size){ + pos = size-1; + } + else{ + pos = prev_endline+1; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_token_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + + // TODO(allen): Make nulltoken? + if (current_token == -1){ + current_token = 0; + } + Cpp_Token *token = &file->token_stack.tokens[current_token]; + if (token->start == panel->cursor.pos && current_token > 0){ + --token; + } + + panel_cursor_move(panel, token->start); +} + +internal void +command_seek_token_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + // TODO(allen): Make nulltoken? + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + if (get_result.in_whitespace){ + ++current_token; + } + + if (current_token >= file->token_stack.count){ + current_token = file->token_stack.count - 1; + } + + Cpp_Token *token = &file->token_stack.tokens[current_token]; + + panel_cursor_move(panel, token->start + token->size); +} + +internal void +command_begin_search_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 0; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_begin_rsearch_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 1; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_set_mark(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->mark = (i32)panel->cursor.pos; +} + +internal void +command_copy(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + u8 *data = (u8*)panel->file->data; + if (panel->file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + } +} + +internal void +command_cut(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + buffer_delete_range(file, range); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, panel->mark); + } +} + +internal void +command_paste(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0){ + panel->next_mode.rewrite = 1; + + Editing_File *file = panel->file; + + String *src = working_set_clipboard_head(working_set); + i32 pos_left = panel->cursor.pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos_left = pos_adjust_to_left(pos_left, file->data); + } + panel_write_chunk(panel, src, pos_left); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(pos_left, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = pos_left; + panel->paste_effect.end = pos_left + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } +} + +internal void +command_paste_next(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0 && panel->mode.rewrite){ + panel->next_mode.rewrite = 1; + + Range range = get_range(panel->mark, panel->cursor.pos); + + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + + String *src = working_set_clipboard_roll_down(working_set); + buffer_replace_range(file, range.smaller, range.larger, + src->str, src->size); + + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller+src->size); + panel->preferred_x = panel_get_cursor_x(panel); + panel->file->cursor.pos = panel->cursor.pos; + + // TODO(allen): faster way to recompute line measurements afterwards + buffer_measure_all_lines(file); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = range.smaller; + panel->paste_effect.end = range.smaller + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } + } +} + +internal void +command_delete_chunk(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + buffer_delete_range(file, range); + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +// TODO(allen): Make this preserve order (look at layout_open_panel) +// Make this preserve old ratios or sizes of panels instead of +// resizing them all. +internal void +command_open_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + Editing_Working_Set *working_set = command->working_set; + // TODO(allen): This is wrong. We should not be passing in the style + // like this. The system should be looking it up here. + Editing_Style *style = command->style; + // TODO(allen): change the screen info to real32 at the base level? + real32 screen_full_width = (real32)command->screen_width; + real32 screen_full_height = (real32)command->screen_height; + real32 screen_y_off = (real32)command->screen_y_off; + + layout_open_panel(layout, working_set->files, style); + real32 panel_w = ((real32)screen_full_width / layout->panel_count); + real32 panel_x_pos = 0; + + for (i32 panel_i = 0; panel_i < layout->panel_count; ++panel_i){ + Editing_Panel *panel = &layout->panels[panel_i]; + + panel->full_x = Floor(panel_x_pos); + panel->full_w = Floor(panel_w); + panel->full_y = Floor(screen_y_off); + panel->full_h = Floor(screen_full_height - screen_y_off); + + panel_fix_internal_area(panel); + + panel_x_pos += panel_w; + } +} + +internal void +command_close_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + i32 share_w = layout->panels[layout->active_panel].full_w; + layout_close_panel(layout, layout->active_panel); + layout_redistribute_width(layout, share_w); +} + +internal Input_Request* +app_request_sys_file(App_Vars *vars, String query, String dest_init, bool32 fast_folder){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_SYS_FILE; + request->sys_file.fast_folder = 1; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->sys_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->sys_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->sys_file.query, query); + copy(&request->sys_file.dest, dest_init); + return request; +} + + +internal Input_Request* +app_request_live_file(App_Vars *vars, String query, String dest_init){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_LIVE_FILE; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->live_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->live_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->live_file.query, query); + copy(&request->live_file.dest, dest_init); + return request; +} + +internal void +panel_set_to_new(Editing_Panel *panel){ + panel->cursor = {}; + panel->cursor.pos = + pos_adjust_to_self(0, panel->file->data, panel->file->size); + panel->scroll_y = 0; + panel->target_y = 0; + panel->vel_y = 1.f; + panel->scroll_x = 0; + panel->target_x = 0; + panel->vel_x = 1.f; +} + +internal void +command_reopen(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (!file->is_dummy){ + Editing_File temp_file; + if (buffer_load(&temp_file, (u8*)make_c_str(file->source_path))){ + buffer_close(file); + + // TODO(allen): Deduplicate!! + Cpp_File cpp_file; + cpp_file.data = temp_file.data; + cpp_file.size = temp_file.size; + { + i32 size = cpp_lex_file_token_count(cpp_file); + size = cpp_get_token_stack_size(size); + temp_file.token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(cpp_file, &temp_file.token_stack); + temp_file.tokens_complete = 1; + temp_file.tokens_exist = 1; + + *file = temp_file; + panel_set_to_new(panel); + panel_measure_all_wrapped_y(panel); + // TODO(allen): Update all other panels that also view this file. + } + } +} + +internal void +command_interactive_open(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + // TODO(allen): Can all of this be simplified? I don't like having this much + // complexity for issuing a single request. + vars->pending_command = command_interactive_open; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Open: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + Editing_File *new_file; + new_file = buffer_open(&vars->working_set, string->str, command->style); + + if (!new_file){ + // TODO(allen): Here's a really tough one. This crap is now happening twice. + // Once here and also in the single_file_input function which checks all of it + // whenever the fast_folder_select option is on to see if the user is selecting + // a folder. It would be nice to be able to share that information to here when it + // is available so we could avoid doing the exact same computations here. + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *string); + + Hot_Directory_Match match = + hot_directory_first_match(&vars->hot_directory, + front_name, 1, 0); + + if (match.filename){ + // NOTE(allen): is_folder case is handled by the single_file_input function + // which would only pass control to this command if the user did not match to + // a folder. + Assert(!match.is_folder); + new_file = buffer_open(&vars->working_set, string->str, command->style); + } + } + + if (new_file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = new_file; + panel_set_to_new(active_panel); + panel_measure_all_wrapped_y(active_panel); + + Cpp_File file; + file.data = new_file->data; + file.size = new_file->size; + // TODO(allen): Where should the memory for tokens come from IRL? + { + i32 size = cpp_lex_file_token_count(file); + size = cpp_get_token_stack_size(size); + new_file->token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(file, &new_file->token_stack); + new_file->tokens_complete = 1; + new_file->tokens_exist = 1; + } + } +} + +internal void +command_save(Command_Data *command){ + Editing_Panel *panel = command->panel; + String *file_path = &panel->file->source_path; + if (file_path->size > 0){ + buffer_save(panel->file, file_path->str); + } +} + +internal void +command_interactive_save_as(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_save_as; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Save As: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + + buffer_save_and_set_names(command->panel->file, string->str); + } +} + +internal void +command_change_active_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + if (layout->panel_count > 1){ + ++layout->active_panel; + if (layout->active_panel >= layout->panel_count){ + layout->active_panel = 0; + } + } +} + +internal void +command_interactive_switch_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_switch_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Switch To: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(command->working_set, *string); + + if (file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = file; + Panel_Cursor_Data cursor_data; + cursor_data = panel_compute_cursor_from_pos(active_panel, file->cursor.pos); + active_panel->cursor = cursor_data; + } + } +} + +internal void +command_kill_file(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (file && file->max_size != 0){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } +} + +internal void +command_interactive_kill_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_kill_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Kill: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(&vars->working_set, *string); + + if (file){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } + } +} + +internal void +command_toggle_line_wrap(Command_Data *command){ + Editing_Panel *panel = command->panel; + if (panel->unwrapped_lines){ + panel->unwrapped_lines = 0; + panel->target_x = 0; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + else{ + panel->unwrapped_lines = 1; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } +} + +internal void +command_toggle_endline_mode(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + panel->cursor.pos = pos_adjust_to_left(panel->cursor.pos, panel->file->data); + file->endline_mode = ENDLINE_RN_SEPARATE; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SEPARATE: + { + file->endline_mode = ENDLINE_RN_SHOWALLR; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SHOWALLR: + { + panel->cursor.pos = pos_adjust_to_self(panel->cursor.pos, panel->file->data, + panel->file->size); + file->endline_mode = ENDLINE_RN_COMBINED; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + } +} + +internal void +command_to_uppercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'a' && data[i] <= 'z'){ + data[i] += (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_to_lowercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'A' && data[i] <= 'Z'){ + data[i] -= (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_toggle_show_whitespace(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->show_whitespace = !panel->show_whitespace; +} + +internal void +command_clean_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + i32 last_hard_start = pos-1; + i32 last_hard = last_hard_start; + while (pos < file->size && data[pos] != '\n'){ + if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + } + ++pos; + } + + if (last_hard != last_hard_start){ + pos = pos_adjust_to_left(pos, data); + + if (last_hard + 1 < pos){ + buffer_replace_range(file, last_hard+1, pos, 0, 0, REP_WHITESPACE); + panel_measure_all_wrapped_y(panel); + + if (panel->cursor.pos > last_hard){ + panel->cursor = panel_compute_cursor_from_pos(panel, last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= pos - (last_hard + 1); + } + } + } + else{ + panel_measure_all_wrapped_y(panel); + panel_auto_tab(panel, pos, pos); + } +} + +internal void +command_clean_all_lines(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 cursor_pos = panel->cursor.pos; + i32 pos = 0; + i32 last_hard = -1; + bool32 is_all_white = 1; + while (pos <= file->size){ + if (pos == file->size || data[pos] == '\n'){ + i32 line_pos = pos; + if (pos < file->size && data[pos] == '\n'){ + line_pos = pos_adjust_to_left(pos, data); + } + + // TODO(allen): This should be optimized by either keeping track of the nesting level + // in this funciton, or by at least having a nesting hint that is used and updated + // every time an auto tab happens. Also auto tab should take a nesting hint. + if (is_all_white){ + panel_auto_tab(panel, pos, pos); + } + else{ + if (last_hard + 1 < line_pos){ + buffer_replace_range(file, last_hard+1, line_pos, 0, 0, REP_WHITESPACE); + + if (cursor_pos > last_hard && cursor_pos <= pos){ + cursor_pos = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (cursor_pos > pos){ + cursor_pos -= line_pos - (last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= line_pos - (last_hard + 1); + } + pos -= line_pos - (last_hard + 1); + } + } + + last_hard = pos; + is_all_white = 1; + } + else if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + is_all_white = 0; + } + ++pos; + } + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, cursor_pos); +} + +internal void +command_eol_dosify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_RN, ENDLINE_ERASE, ENDLINE_RN); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_eol_nixify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_N, ENDLINE_ERASE, ENDLINE_N); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_auto_tab(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + panel_auto_tab(panel, range.smaller, range.larger); + panel_measure_all_wrapped_y(panel); +} + +internal void +setup_commands(Command_Map *commands, Key_Codes *codes){ + commands->basic_mode_key[codes->left] = command_move_left; + commands->basic_mode_key[codes->right] = command_move_right; + commands->basic_mode_key[codes->del] = command_delete; + commands->basic_mode_key[codes->back] = command_backspace; + commands->basic_mode_key[codes->up] = command_move_up; + commands->basic_mode_key[codes->down] = command_move_down; + commands->basic_mode_key[codes->end] = command_seek_end_of_line; + commands->basic_mode_key[codes->home] = command_seek_beginning_of_line; + +#if 0 + commands->control_key[codes->right] = command_seek_token_right; + commands->control_ascii['m'] = command_seek_token_right; + commands->control_key[codes->left] = command_seek_token_left; + commands->control_ascii['n'] = command_seek_token_left; +#else + commands->control_key[codes->right] = command_seek_whitespace_right; + commands->control_ascii['m'] = command_seek_whitespace_right; + commands->control_key[codes->left] = command_seek_whitespace_left; + commands->control_ascii['n'] = command_seek_whitespace_left; +#endif + + commands->control_key[codes->up] = command_seek_whitespace_up; + commands->control_ascii['y'] = command_seek_whitespace_up; + commands->control_key[codes->down] = command_seek_whitespace_down; + commands->control_ascii['h'] = command_seek_whitespace_down; + + commands->control_ascii['\t'] = command_auto_tab; + + commands->control_ascii[' '] = command_set_mark; + commands->control_ascii['c'] = command_copy; + commands->control_ascii['x'] = command_cut; + commands->control_ascii['v'] = command_paste; + commands->control_ascii['V'] = command_paste_next; + commands->control_ascii['d'] = command_delete_chunk; + commands->control_ascii['p'] = command_open_panel; + commands->control_ascii['P'] = command_close_panel; + commands->control_ascii['o'] = command_interactive_open; + commands->control_ascii['O'] = command_reopen; + commands->control_ascii['s'] = command_save; + commands->control_ascii['w'] = command_interactive_save_as; + commands->control_ascii[','] = command_change_active_panel; + commands->control_ascii['i'] = command_interactive_switch_file; + commands->control_ascii['k'] = command_interactive_kill_file; + commands->control_ascii['K'] = command_kill_file; + commands->control_ascii['l'] = command_toggle_line_wrap; + commands->control_ascii['L'] = command_toggle_endline_mode; + commands->control_ascii['u'] = command_to_uppercase; + commands->control_ascii['j'] = command_to_lowercase; + commands->control_ascii['?'] = command_toggle_show_whitespace; + commands->control_ascii['`'] = command_clean_line; + commands->control_ascii['~'] = command_clean_all_lines; + commands->control_ascii['1'] = command_eol_dosify; + commands->control_ascii['!'] = command_eol_nixify; + commands->control_ascii['f'] = command_begin_search_state; + commands->control_ascii['r'] = command_begin_rsearch_state; +} + +/* + * Interactive Bar + */ + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, u8 *str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; str[i]; ++i){ + font_draw_glyph(target, bar->style.font, str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, String str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; i < str.size; ++i){ + font_draw_glyph(target, bar->style.font, str.str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +hot_directory_draw_helper(Render_Target *target, + Hot_Directory *hot_directory, + Interactive_Bar *bar, String *string, + bool32 include_files){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.pop1_color); + + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + + get_front_of_directory(&front_name, *string); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + if (front_name.size == 0 || has_substr_unsensitive(filename, front_name)){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + if (files_it.folder_stage){ + intbar_draw_string(target, bar, filename, bar->style.pop1_color); + intbar_draw_string(target, bar, (u8*)"/", bar->style.pop1_color); + } + else{ + intbar_draw_string(target, bar, filename, bar->style.base_color); + } + } + + files_iterator_step(&files, &files_it); + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +internal void +live_file_draw_helper(Render_Target *target, + Editing_Working_Set *working_set, + Interactive_Bar *bar, String *string){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.base_color); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + for (i32 file_i = 0; + file_i < working_set->file_index_count; + ++file_i){ + Editing_File *file = &working_set->files[file_i]; + if (file->live_name.str && + (string->size == 0 || has_substr_unsensitive(file->live_name, *string))){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + intbar_draw_string(target, bar, file->live_name, bar->style.base_color); + } + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +/* + * App Functions + */ + +internal bool32 +app_init(Thread_Context *thread, Application_Memory *memory, + Key_Codes *loose_codes, Clipboard_Contents clipboard){ + Partition_Cursor partition = + partition_open(memory->vars_memory, memory->vars_memory_size); + + App_Vars *vars = (App_Vars*) + partition_allocate(&partition, sizeof(App_Vars)); + + Assert(vars); + *vars = {}; + + u32 panel_max_count = vars->layout.panel_max_count = 4; + u32 panel_count = vars->layout.panel_count = 1; + Assert(panel_max_count >= 1); + Editing_Panel *panels = vars->layout.panels = (Editing_Panel*) + partition_allocate(&partition, sizeof(Editing_Panel)*panel_max_count); + + Assert(panels); + // TODO(allen): improved Assert + + // NOTE(allen): command map setup + setup_commands(&vars->map, loose_codes); + + // NOTE(allen): font setup + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + i32 memory_used = 0; + if (font_load(&vars->font, 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): file setup + vars->working_set.file_index_count = 1; + vars->working_set.file_max_count = 29; + + vars->working_set.files = (Editing_File*) + partition_allocate(&partition, + sizeof(Editing_File)*vars->working_set.file_max_count); + + buffer_get_dummy(&vars->working_set.files[0], &vars->style); + + // NOTE(allen): clipboard setup + vars->working_set.clipboard_max_size = ArrayCount(vars->working_set.clipboards); + vars->working_set.clipboard_size = 0; + vars->working_set.clipboard_current = 0; + vars->working_set.clipboard_rolling = 0; + + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // TODO(allen): more robust allocation solution for the clipboard? + + // NOTE(allen): style setup + // TODO(allen): style_set_font function + vars->style.font = &vars->font; + vars->style.font_metrics.character_width = vars->font.glyphs[' '].advance; + vars->style.font_metrics.line_skip = vars->font.line_skip; + + vars->style.back_color = 0xFF0C0C0C; + vars->style.margin_color = 0xFF181818; + vars->style.cursor_color = 0xFF00EE00; + vars->style.highlight_color = 0xFFDDEE00; + vars->style.mark_color = 0xFF494949; + vars->style.default_color = 0xFF90B080; + vars->style.at_cursor_color = vars->style.back_color; + vars->style.at_highlight_color = 0xFFFF44DD; + vars->style.comment_color = 0xFF2090F0; + vars->style.keyword_color = 0xFFD08F20; + vars->style.constant_color = 0xFF50FF30; + vars->style.special_character_color = 0xFFFF0000; + + vars->style.use_paste_color = 1; + vars->style.paste_color = 0xFFDDEE00; + + vars->style.highlight_junk_color = 0x44FF0000; + vars->style.highlight_white_color = 0x1100FFFF; + vars->style.tab_width = 4; + vars->style.margin_width = 5; + + Interactive_Style file_info_style; + file_info_style.font = &vars->font; + file_info_style.bar_color = 0xFF888888; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF4444AA; + file_info_style.pop2_color = 0xFFFF0000; + file_info_style.height = vars->style.font->line_skip + 2; + + vars->style.file_info_style = file_info_style; + + Interactive_Style command_style; + command_style.font = &vars->font; + command_style.bar_color = 0xFF0C0C0C; + command_style.base_color = 0xFFDDDDBB; + command_style.pop1_color = 0xFF4444AA; + command_style.pop2_color = 0xFF44FF44; + command_style.height = vars->style.font->line_skip + 2; + + vars->command_style = command_style; + + // NOTE(allen): panel setup + AllowLocal(panel_count); + panel_init(&panels[0], &vars->working_set.files[0]); + + // NOTE(allen): hot directory setup + hot_directory_init(&vars->hot_directory); + + // NOTE(allen): request stack setup + vars->request_count = 0; + vars->request_filled = 0; + vars->request_max = ArrayCount(vars->request_queue); + + return 1; +} + +struct Single_Line_Input_Step{ + bool32 hit_newline; + bool32 hit_ctrl_newline; + bool32 hit_a_character; + bool32 hit_backspace; + bool32 hit_esc; + bool32 made_a_change; + bool32 did_command; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + bool32 fast_folder_select; +}; + +internal Single_Line_Input_Step +app_single_line_input_core(Key_Codes *codes, + Key_Input_Data *input, + Single_Line_Mode mode){ + Single_Line_Input_Step result = {}; + + if (input->has_press){ + if (input->press.keycode == codes->back){ + result.hit_backspace = 1; + switch (mode.type){ + case SINGLE_LINE_STRING: + { + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + if (mode.string->size > 0){ + --mode.string->size; + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size > 0){ + --mode.string->size; + i8 end_character = mode.string->str[mode.string->size]; + if (character_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + // TODO(allen): What to do when the string becomes empty though? + mode.string->str[mode.string->size] = 0; + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + mode.string->str[mode.string->size] = 0; + } + result.made_a_change = 1; + } + }break; + } + } + + else if (input->press.keycode == codes->newline){ + if (input->control_keys[CONTROL_KEY_CONTROL]){ + result.hit_ctrl_newline = 1; + result.made_a_change = 1; + } + + else{ + result.made_a_change = 1; + if (mode.fast_folder_select){ + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *mode.string); + Hot_Directory_Match match; + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 1); + + if (!match.filename){ + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0); + } + + if (match.filename){ + if (match.is_folder){ + set_last_folder(mode.string, match.filename); + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + append(mode.string, match.filename); + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + } + + else if (input->press.keycode == codes->esc){ + result.hit_esc = 1; + result.made_a_change = 1; + } + + else if (input->press.character){ + result.hit_a_character = 1; + if (!input->control_keys[CONTROL_KEY_CONTROL]){ + switch (mode.type){ + case SINGLE_LINE_STRING: + { + if (mode.string->size+1 < mode.string->memory_size){ + mode.string->str[mode.string->size] = (i8)input->press.character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size+1 < mode.string->memory_size){ + i8 new_character = (i8)input->press.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + + if (character_is_slash(new_character)){ + hot_directory_set(mode.hot_directory, *mode.string); + } + result.made_a_change = 1; + } + }break; + } + } + + else{ + result.did_command = 1; + result.made_a_change = 1; + } + } + } + + return result; +} + +inline internal Single_Line_Input_Step +app_single_line_input_step(Key_Codes *codes, Key_Input_Data *input, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Single_Line_Input_Step +app_single_file_input_step(Key_Codes *codes, Key_Input_Data *input, + String *string, Hot_Directory *hot_directory, + bool32 fast_folder_select){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.fast_folder_select = fast_folder_select; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Command_Function +app_get_command(App_Vars *vars, Key_Input_Data *input, Key_Event_Data key){ + Command_Function function = 0; + if (input->control_keys[CONTROL_KEY_CONTROL]){ + if (key.character_no_caps_lock){ + function = vars->map.control_ascii[key.character_no_caps_lock]; + } + else{ + function = vars->map.control_key[key.keycode]; + } + } + else if (!input->control_keys[CONTROL_KEY_ALT]){ + if (key.character != 0){ + function = command_write_character; + } + else{ + function = vars->map.basic_mode_key[key.keycode]; + } + } + return function; +} + +internal bool32 +smooth_camera_step(real32 *target, real32 *current, real32 *vel, real32 S, real32 T){ + bool32 result = 0; + real32 targ = *target; + real32 curr = *current; + real32 v = *vel; + if (curr != targ){ + real32 L = lerp(curr, T, targ); + + i32 sign = (targ > curr) - (targ < curr); + real32 V = curr + sign*v; + + if (sign > 0){ + curr = Min(L, V); + } + else{ + curr = Max(L, V); + } + + if (curr == V){ + v *= S; + } + + if (curr > targ - .0001f && curr < targ + .0001f){ + curr = targ; + v = 1.f; + } + *target = targ; + *current = curr; + *vel = v; + result = 1; + } + return result; +} + +#if FRED_INTERNAL +Application_Memory *GLOBAL; +#endif + +internal Application_Step_Result +app_step(Thread_Context *thread, Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory, + Clipboard_Contents clipboard, + bool32 first_step){ + +#if FRED_INTERNAL + GLOBAL = memory; +#endif + + ProfileStart(app_step); + ProfileSection(thread, app_step, "start"); + + Application_Step_Result app_result = {}; + app_result.redraw = 1; + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // TODO(allen): Let's find another way to do first_step + // so we can get it out of app_step and the platform layer. + if (first_step || !time_step){ + app_result.redraw = 1; + } + + Editing_Panel *panels = vars->layout.panels; + Editing_Panel *active_panel = &panels[vars->layout.active_panel]; + ProfileSection(thread, app_step, "setup"); + + // NOTE(allen): OS clipboard event handling + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // NOTE(allen): check files are up to date + for (i32 i = 0; i < vars->working_set.file_index_count; ++i){ + Editing_File *file = vars->working_set.files + i; + + if (!file->is_dummy){ + Time_Stamp time_stamp; + time_stamp = system_file_time_stamp((u8*)make_c_str(file->source_path)); + + if (time_stamp.success){ + file->last_sys_write_time = time_stamp.time; + if (file->last_sys_write_time != file->last_4ed_write_time){ + app_result.redraw = 1; + } + } + } + } + ProfileSection(thread, app_step, "OS syncing"); + + // NOTE(allen): keyboard input handling + if (time_step){ + switch (vars->state){ + case APP_STATE_EDIT: + { + Command_Data command_data; + command_data.panel = active_panel; + command_data.working_set = &vars->working_set; + command_data.layout = &vars->layout; + command_data.style = &vars->style; + command_data.vars = vars; + command_data.screen_width = target->width; + command_data.screen_height = target->height; + command_data.screen_y_off = vars->command_style.height; + command_data.requests = 0; + command_data.request_count = 0; + command_data.request_filled = 0; + + if (vars->request_count == 0){ + Command_Function function = 0; + if (input->has_press){ + command_data.key = input->press; + function = app_get_command(vars, input, command_data.key); + } + + else if (input->has_hold){ + command_data.key = input->hold; + function = app_get_command(vars, input, command_data.key); + + } + + if (function){ + command_data.panel->next_mode = {}; + function(&command_data); + app_result.redraw = 1; + command_data.panel->mode = command_data.panel->next_mode; + } + } + + else{ + + Input_Request *active_request = vars->request_queue + vars->request_filled; + bool32 killed_command = 0; + switch (active_request->type){ + case REQUEST_SYS_FILE: + { + String *string = &active_request->sys_file.dest; + Single_Line_Input_Step result = + app_single_file_input_step(codes, input, string, &vars->hot_directory, 1); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->sys_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + case REQUEST_LIVE_FILE: + { + String *string = &active_request->live_file.dest; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->live_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + } + + if (vars->request_filled == vars->request_count && !killed_command){ + command_data.requests = vars->request_queue; + command_data.request_count = vars->request_count; + command_data.request_filled = vars->request_filled; + vars->pending_command(&command_data); + app_clear_request_queue(vars); + } + } + + }break; + + case APP_STATE_SEARCH: + { + String *string = &vars->isearch.str; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + + Editing_File *file = active_panel->file; + + bool32 step_forward = 0; + bool32 step_backward = 0; + + if (input->has_press){ + Key_Event_Data key = input->press; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + if (!step_forward && !step_backward && + !input->has_press && input->has_hold){ + Key_Event_Data key = input->hold; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + i32 start_pos = vars->isearch.pos; + if (step_forward){ + if (vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 0; + } + } + if (step_backward){ + if (!vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 1; + } + } + + String file_string = make_string(file->data, file->size); + + i32 pos; + + if (vars->isearch.reverse){ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + } + else{ + pos = rfind_substr(file_string, start_pos - 1, *string); + + if (pos >= 0){ + if (step_backward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = rfind_substr(file_string, start_pos - 1, *string); + if (pos == -1){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + else{ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + } + else{ + pos = find_substr(file_string, start_pos + 1, *string); + + if (pos < file->size){ + if (step_forward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = find_substr(file_string, start_pos + 1, *string); + if (pos == file->size){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + } + + if (result.hit_newline || result.hit_ctrl_newline){ + panel_cursor_move(active_panel, active_panel->temp_highlight); + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + if (result.hit_esc){ + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + }break; + + case APP_STATE_RESIZING: + { + if (input->has_press){ + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "keyboard input"); + + // NOTE(allen): reorganizing panels on screen + i32 prev_width = vars->layout.full_width; + i32 prev_height = vars->layout.full_height; + i32 full_width = vars->layout.full_width = target->width; + i32 full_height = vars->layout.full_height = target->height; + + if (prev_width != full_width || prev_height != full_height){ + layout_refit(&vars->layout, 0, vars->command_style.height, + full_width, full_height, + prev_width, prev_height); + + for (i32 panel_i = 0; + panel_i < vars->layout.panel_count && + vars->layout.panel_count > 1; + ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + + Editing_Style *style = panel->file->style; + i32 character_w = style_get_character_width(style); + i32 margin_width = style->margin_width; + if (panel->full_w < character_w*6 + margin_width*2){ + i32 share_w = panel->full_w; + layout_close_panel(&vars->layout, panel_i); + layout_redistribute_width(&vars->layout, share_w); + panel_i = 0; + } + } + + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = panels + panel_i; + if (!panel->file->is_dummy){ + panel->cursor = panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + } + + app_result.redraw = 1; + } + ProfileSection(thread, app_step, "reorganizing panels"); + + // NOTE(allen): mouse input handling + if (time_step && !mouse->out_of_window){ + + i32 mx = mouse->x; + i32 my = mouse->y; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + // TODO(allen): + // This means any mouse use will be impossible, I guess it will have + // to depend on the type of the request seen at the top??? + app_clear_request_queue(vars); + mouse_press_event = 1; + + switch (vars->state){ + case APP_STATE_SEARCH: + { + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + }break; + } + } + + switch (vars->state){ + case APP_STATE_EDIT: + { + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + + Editing_Panel *panel = &panels[panel_i]; + + if (mx >= panel->x && + mx < panel->w + panel->x && + my >= panel->y && + my < panel->h + panel->y){ + + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_IBEAM; + + if (mouse_press_event){ + if (!panel->file->is_dummy){ + app_result.redraw = 1; + + Font *font = &vars->font; + + i32 character_w = style_get_character_width(panel->file->style); + i32 character_h = font->line_skip; + i32 max_line_length = panel_compute_max_line_length(panel); + i32 max_lines = panel_compute_max_lines(panel); + + real32 grid_x = ((real32)mx - panel->x) / character_w; + real32 grid_y = ((real32)my - panel->y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + if (grid_x >= 0 && grid_x <= max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = (i32)(grid_x + active_panel->scroll_x); + i32 pos_y = (i32)(grid_y + active_panel->scroll_y); + + panel_cursor_move(active_panel, pos_x, pos_y); + } + } + vars->layout.active_panel = panel_i; + active_panel = panel; + } + else{ + // NOTE(allen): mouse inside editing area but no click + } + } + + else if (mx >= panel->full_x && + mx < panel->full_w + panel->full_x && + my >= panel->full_y && + my < panel->full_h + panel->full_y){ + // NOTE(allen): not inside the editing area but within the margins + bool32 resize_area = 0; + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + if (mx >= (panel->full_x+panel->full_w+panel->x+panel->w)/2){ + if (panel_i != vars->layout.panel_count-1){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = 1; + } + } + else if (mx <= (panel->full_x+panel->x)/2){ + if (panel_i != 0){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = -1; + } + } + + if (resize_area != 0 && mouse_press_event){ + vars->state = APP_STATE_RESIZING; + if (resize_area == 1){ + vars->resizing.left = panel; + vars->resizing.right = panel+1; + } + else if (resize_area == -1){ + vars->resizing.left = panel-1; + vars->resizing.right = panel; + } + } + } + } + + i32 cursor_y = panel_get_cursor_y(active_panel); + real32 target_y = active_panel->target_y; + i32 max_lines = panel_compute_max_lines(active_panel); + + bool32 wheel_used; + + real32 delta_target_y = Max(1, max_lines / 3.f); + delta_target_y *= -mouse->wheel; + + target_y += delta_target_y; + + if (target_y < 0){ + target_y = 0; + } + + if (mouse->wheel == 0){ + wheel_used = 0; + } + else{ + wheel_used = 1; + + if (cursor_y >= target_y + max_lines){ + cursor_y = (i32)target_y + max_lines - 1; + } + if (cursor_y < target_y){ + cursor_y = (i32)target_y + 1; + } + } + + active_panel->target_y = target_y; + if (cursor_y != panel_get_cursor_y(active_panel)){ + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + active_panel->preferred_x, + cursor_y); + } + + if (wheel_used){ + app_result.redraw = 1; + } + }break; + + case APP_STATE_RESIZING: + { + if (mouse->left_button){ + Editing_Panel *left = vars->resizing.left; + Editing_Panel *right = vars->resizing.right; + i32 left_x = left->full_x; + i32 left_w = left->full_w; + i32 right_x = right->full_x; + i32 right_w = right->full_w; + + AllowLocal(right_x); + + i32 new_left_x = left_x; + i32 new_left_w = mx - left_x; + i32 new_right_w = right_w - (new_left_w - left_w); + i32 new_right_x = mx; + + if (left_w != new_left_w){ + app_result.redraw = 1; + + Editing_Style *left_style = left->file->style; + Editing_Style *right_style = right->file->style; + + i32 left_character_w = style_get_character_width(left_style); + i32 right_character_w = style_get_character_width(right_style); + + i32 left_margin_width = left_style->margin_width; + i32 right_margin_width = right_style->margin_width; + if (new_left_w > left_margin_width*2 + left_character_w*6 && + new_right_w > right_margin_width*2 + right_character_w*6){ + left->full_x = new_left_x; + left->full_w = new_left_w; + right->full_x = new_right_x; + right->full_w = new_right_w; + + panel_fix_internal_area(left); + panel_fix_internal_area(right); + } + } + } + else{ + app_result.redraw = 1; + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "mouse input"); + + // NOTE(allen): fix scrolling on all panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + i32 cursor_y; + if (panel->show_temp_highlight){ + if (panel->unwrapped_lines){ + cursor_y = panel->temp_highlight.unwrapped_y; + } + else{ + cursor_y = panel->temp_highlight.wrapped_y; + } + } + else{ + if (panel->unwrapped_lines){ + cursor_y = panel->cursor.unwrapped_y; + } + else{ + cursor_y = panel->cursor.wrapped_y; + } + } + real32 target_y = panel->target_y; + real32 original_target_y = target_y; + i32 max_lines = panel_compute_max_lines(panel); + + while (cursor_y >= Floor(target_y) + max_lines){ + target_y += 3.f; + } + + while (cursor_y < target_y){ + target_y -= 3.f; + } + + if (target_y < 0){ + target_y = 0; + } + + panel->target_y = target_y; + + i32 cursor_x = panel_get_cursor_x(panel); + real32 target_x = panel->target_x; + real32 original_target_x = target_x; + i32 max_x = panel_compute_max_line_length(panel); + if (cursor_x < target_x){ + target_x = (real32)Max(0, cursor_x - max_x/2); + } + else if (cursor_x >= target_x + max_x){ + target_x = (real32)(cursor_x - max_x/2); + } + + panel->target_x = target_x; + + if (original_target_y != panel->target_y || + original_target_x != panel->target_x){ + app_result.redraw; + } + } + ProfileSection(thread, app_step, "fix scrolling"); + + // NOTE(allen): execute animations + for (i32 i = 0; i < vars->layout.panel_count; ++i){ + Editing_Panel *panel = vars->layout.panels + i; + + // TODO(allen): Scrolling parameterization in style? + if (smooth_camera_step(&panel->target_y, &panel->scroll_y, &panel->vel_y, 2.f, 1.f/9.f)){ + app_result.redraw = 1; + } + + if (smooth_camera_step(&panel->target_x, &panel->scroll_x, &panel->vel_x, 2.f, 1.f/6.f)){ + app_result.redraw = 1; + } + + if (panel->paste_effect.tick_down > 0){ + --panel->paste_effect.tick_down; + app_result.redraw = 1; + } + + } + ProfileSection(thread, app_step, "execute animations"); + + if (app_result.redraw){ + // NOTE(allen): render the panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + Editing_Style *style = panel->file->style; + + i32 full_left = panel->full_x; + i32 full_top = panel->full_y; + i32 full_right = full_left + panel->full_w; + i32 full_bottom = full_top + panel->full_h; + + u32 back_color = style->back_color; + draw_rectangle_2corner(target, full_left, full_top, full_right, full_bottom, back_color); + + u32 side_margin_color = style->margin_color; + + panel_draw(thread, target, panel, vars->layout.active_panel == panel_i); + + // NOTE(allen): file info bar + { + Interactive_Bar bar; + bar.style = style->file_info_style; + bar.pos_x = panel->x; + bar.pos_y = full_top; + + draw_rectangle(target, full_left, bar.pos_y, + panel->full_w, bar.style.height, + bar.style.bar_color); + + Editing_File *file = panel->file; + if (!file->is_dummy){ + intbar_draw_string(target, &bar, panel->file->live_name, bar.style.base_color); + intbar_draw_string(target, &bar, make_lit_string(" - "), bar.style.base_color); + + u8 line_number_space[30]; + String line_number = make_string(line_number_space, 0, 30); + append(&line_number, (u8*)"L#"); + append_int_to_str(panel->cursor.line, &line_number); + + intbar_draw_string(target, &bar, line_number, bar.style.base_color); + + if (file->last_4ed_write_time != file->last_sys_write_time){ + persist String out_of_sync = make_lit_string(" FILE SYNC"); + intbar_draw_string(target, &bar, out_of_sync, bar.style.pop2_color); + } + } + } + + // L + draw_rectangle_2corner(target, full_left, panel->y, + panel->x, full_bottom, side_margin_color); + + // R + draw_rectangle_2corner(target, panel->x + panel->w, panel->y, + full_right, full_bottom, side_margin_color); + + // B + draw_rectangle_2corner(target, full_left, panel->y + panel->h, + full_right, full_bottom, side_margin_color); + + if (panel_i != 0){ + draw_rectangle_2corner(target, panel->full_x-1, panel->full_y, + panel->full_x+1, panel->full_y+panel->full_h, + 0xFFFFFFFF); + } + } + ProfileSection(thread, app_step, "render files"); + + // NOTE (allen): command bar + { + Interactive_Bar bar; + bar.style = vars->command_style; + bar.pos_x = 0; + bar.pos_y = 0; + + draw_rectangle(target, 0, 0, + target->width, bar.style.height, + bar.style.bar_color); + + switch (vars->state){ + case APP_STATE_EDIT: + { + if (vars->request_count > 0){ + Input_Request *request = vars->request_queue + vars->request_filled; + + switch (request->type){ + case REQUEST_SYS_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + hot_directory_draw_helper(target, &vars->hot_directory, &bar, string, 1); + }break; + + case REQUEST_LIVE_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + live_file_draw_helper(target, &vars->working_set, &bar, string); + }break; + + } + } + }break; + + case APP_STATE_SEARCH: + { + persist String search_str = make_lit_string("I-Search: "); + persist String rsearch_str = make_lit_string("Reverse-I-Search: "); + if (vars->isearch.reverse){ + intbar_draw_string(target, &bar, rsearch_str, bar.style.pop2_color); + } + else{ + intbar_draw_string(target, &bar, search_str, bar.style.pop2_color); + } + + intbar_draw_string(target, &bar, vars->isearch.str, bar.style.base_color); + }break; + + case APP_STATE_RESIZING: + { + intbar_draw_string(target, &bar, make_lit_string("Resizing!"), bar.style.pop2_color); + }break; + } + } + ProfileSection(thread, app_step, "interaction bar"); + + } + + ProfileEnd(thread, app_step, "total"); + + return app_result; +} + +// BOTTOM + +� � �E� �d� �i� �t� �i� �n� �g� �_� �P� �a� �n� �e� �l� � � �*� �p� �a� �n� �e� �l� � � �=� � � �c� �o� �m� �m� �a� �n� �d� �-� �>� �p� �a� �n� �e� �l� � \ No newline at end of file diff --git a/test_data/sample_files/basictab.cpp b/test_data/sample_files/basictab.cpp new file mode 100644 index 0000000..c865ec7 --- /dev/null +++ b/test_data/sample_files/basictab.cpp @@ -0,0 +1,3036 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +// TOP +// TODO(allen): +// +// BUGS & PROBLEMS +// +// - more rendering optimizations (1920 X 1080 is too slow) +// +// - the String class should always maintain an extra byte for NULL termination +// so that we don't have problems going to C style strings +// +// - LOGGING SYSTEM FOR ALL BUILD CONFIGS +// +// - when opening files don't try to open twice, have a function that figures out +// whether there is a direct match and returns exactly the name the open function +// should use. Further, this should always just return the first thing that an +// iteration across possible matches would return. Then make that match ordering +// algorithm super smart. +// +// - same file multiple panel editing problems +// scrolling doesn't always work +// cursor doesn't stay put on other panels +// +// GENERAL +// +// - untabification +// +// - mode switching +// +// - auto mode switching +// +// - disk file syncing GUI +// +// - meta command stuff +// command use frequency +// +// - configuration / GUI for generating configuration +// +// - more feed back messages ("File saved" etc) +// +// - line wrap, line mode change, no scroll +// +// - travel packaging +// +// - replace SDL with stb +// +// - multiple live name conflicts +// +// - non-file panels +// +// - nav links +// +// - undo / redo +// +// - on file reopen diff and try to find place for cursor +// +// TOOLS +// +// - calculator with: dec hex translation +// +// - solver +// +// TEXT MODE +// +// - replace +// +// - select word +// +// - match list +// +// - regular expression +// +// BASIC CODE MODE - general any language +// +// - select token +// +// - seek token or whitespace within token +// +// - reprogrammable lexing / auto indent +// +// - bracket match / mismatch highlighting +// +// - smart line wrapping (using tokens and white space as separation points) +// +// - auto casing rules? +// +// SUPER CODE MODE - C/C++ editing +// +// - identifier rename +// +// - boolean inverse polarity +// +// - compose/explode class/hierarchy +// - virtual to vtable sim/switch +// +// - explode class template? +// +// - enumerate template combinations? +// +// - generate header +// + +/* + * Quick and Dirty Partition System + */ + +struct Partition_Cursor{ + u8 *memory_base; + u8 *memory_cursor; + i32 max_size; +}; + +internal Partition_Cursor +partition_open(void *memory, i32 size){ + Partition_Cursor partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory; + partition.max_size = size; + return partition; +} + +internal void* +partition_allocate(Partition_Cursor *data, i32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +/* + * Plaform Independent File Name Helpers + */ + +struct File_List_Iterator{ + bool32 folder_stage; + u8 **filename_ptr; +}; + +internal File_List_Iterator +files_iterator_init(File_List *files){ + File_List_Iterator files_it = {}; + if (files->folder_count > 0){ + files_it.filename_ptr = files->folder_names; + files_it.folder_stage = 1; + } + else if (files->file_count > 0){ + files_it.filename_ptr = files->file_names; + } + return files_it; +} + +internal void +files_iterator_step(File_List *files, File_List_Iterator *files_it){ + ++files_it->filename_ptr; + if (files_it->folder_stage){ + if (files_it->filename_ptr >= files->folder_names + files->folder_count){ + if (files->file_count > 0){ + files_it->filename_ptr = files->file_names; + } + else{ + files_it->filename_ptr = 0; + } + files_it->folder_stage = 0; + } + } + else{ + if (files_it->filename_ptr >= files->file_names + files->file_count){ + files_it->filename_ptr = 0; + } + } +} + +/* + * Drawing Functions + */ + +internal u32 +style_token_color(Editing_Style *style, + Cpp_Token_Type type){ + u32 result; + switch (type){ + case CPP_TOKEN_COMMENT: + result = style->comment_color; + break; + + case CPP_TOKEN_KEYWORD: + result = style->keyword_color; + break; + + case CPP_TOKEN_STRING_CONSTANT: + case CPP_TOKEN_CHARACTER_CONSTANT: + case CPP_TOKEN_INTEGER_CONSTANT: + case CPP_TOKEN_FLOATING_CONSTANT: + case CPP_TOKEN_BOOLEAN_CONSTANT: + case CPP_TOKEN_INCLUDE_FILE: + result = style->constant_color; + break; + + default: + result = style->default_color; + } + return result; +} + +internal void +panel_draw(Thread_Context *thread, Render_Target *target, + Editing_Panel *panel, bool32 is_active){ + Editing_File *file = panel->file; + Editing_Style *style = file->style; + Font *font = style->font; + + i32 character_w = (i32)(style_get_character_width(style)); + i32 character_h = (i32)(font->line_skip); + i32 offset_x = (i32)(panel->x); + i32 offset_y = (i32)(panel->y); + i32 max_x = (i32)(panel->w); + i32 max_y = (i32)(panel->h); + + Blit_Rect panel_area; + panel_area.x_start = offset_x; + panel_area.y_start = offset_y; + panel_area.x_end = offset_x + max_x; + panel_area.y_end = offset_y + max_y; + + if (!file || !file->data || file->is_dummy){ + i32 start_x = (panel_area.x_start + panel_area.x_end)/2; + i32 start_y = (panel_area.y_start + panel_area.y_end)/2; + persist String null_file_message = make_lit_string("NULL FILE"); + start_x -= (character_w*null_file_message.size)/2; + start_y -= (character_h)/2; + + real32 pos_x = 0; + real32 pos_y = 0; + + for (i32 i = 0; i < null_file_message.size; ++i){ + u8 to_render = null_file_message.str[i]; + + if (font->glyphs[to_render].data){ + font_draw_glyph_clipped(target, font, to_render, + (real32)start_x + pos_x, (real32)start_y + pos_y, + style->special_character_color, + panel_area); + pos_x += character_w; + } + } + } + + else{ + u32 tab_width = style->tab_width; + i32 size = (i32)file->size; + u8 *data = (u8*)file->data; + + real32 shift_x = 0; + shift_x = -panel->scroll_x * character_w; + + i32 truncated_start_y = (i32)panel->scroll_y; + real32 scroll_amount = panel->scroll_y - truncated_start_y; + + Panel_Cursor_Data start_cursor; + start_cursor = panel->scroll_y_cursor; + start_cursor = panel_compute_cursor_from_xy(panel, 0, truncated_start_y); + panel->scroll_y_cursor = start_cursor; + + i32 start_character = start_cursor.pos; + + real32 pos_x = 0; + real32 pos_y = -character_h*scroll_amount; + + Cpp_Token_Stack token_stack = file->token_stack; + u32 main_color = style->default_color; + u32 highlight_color = 0x00000000; + i32 token_i = 0; + bool32 tokens_exist = file->tokens_exist; + + if (tokens_exist){ + // TODO(allen): Use cpp_get_token, it binary searches this shit! + while (token_i < token_stack.count && + start_character > token_stack.tokens[token_i].start){ + ++token_i; + } + if (token_i != 0){ + main_color = + style_token_color(style, token_stack.tokens[token_i-1].type); + if (token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK){ + highlight_color = style->highlight_junk_color; + } + } + } + + // TODO(allen): Render through NULLS + for (i32 i = start_character; i < size && data[i]; ++i){ + u8 to_render = data[i]; + + u32 fade_color = 0xFFFF00FF; + real32 fade_amount = 0.f; + if (style->use_paste_color && panel->paste_effect.tick_down > 0 && + panel->paste_effect.start <= i && i < panel->paste_effect.end){ + fade_color = style->paste_color; + fade_amount = (real32)(panel->paste_effect.tick_down) / panel->paste_effect.tick_max; + } + + if (tokens_exist && token_i < token_stack.count){ + if (i == token_stack.tokens[token_i].start){ + main_color = + style_token_color(style, token_stack.tokens[token_i].type); + ++token_i; + } + if (token_i > 0 && + i >= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + main_color = 0xFFFFFFFF; + } + } + + highlight_color = 0x00000000; + + if (tokens_exist && token_i > 0 && + token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK && + i >= token_stack.tokens[token_i-1].start && + i <= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + highlight_color = style->highlight_junk_color; + } + else if (panel->show_whitespace && + character_is_any_whitespace(data[i])){ + highlight_color = style->highlight_white_color; + } + + i32 cursor_mode = 0; + if (panel->show_temp_highlight){ + if (panel->temp_highlight.pos <= i && i < panel->temp_highlight_end_pos){ + cursor_mode = 2; + } + } + else{ + if (panel->cursor.pos == i){ + cursor_mode = 1; + } + } + + if (!panel->unwrapped_lines && + pos_x + character_w > max_x){ + pos_x = 0; + pos_y += font->line_skip; + } + + if (highlight_color != 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED && + to_render == '\r' && + i + 1 < size && + data[i+1] == '\n'){ + // DO NOTHING + } + else{ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + } + } + + if (cursor_mode){ + if (is_active){ + u32 color = 0x00000000; + switch (cursor_mode){ + case 1: + color = style->cursor_color; + break; + case 2: + color = style->highlight_color; + break; + } + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + color, panel_area); + } + else{ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->cursor_color, + panel_area); + } + } + + if (i == panel->mark){ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->mark_color, + panel_area); + } + + if (to_render == '\r'){ + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + if (i + 1 < size && + data[i+1] == '\n'){ + // DO NOTHING + } + else{ + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + } + }break; + + case ENDLINE_RN_SEPARATE: + { + pos_x = 0; + pos_y += font->line_skip; + }break; + + case ENDLINE_RN_SHOWALLR: + { + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + }break; + } + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += font->line_skip; + } + else if (to_render == '\t'){ + if (highlight_color != 0){ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x + character_w, + offset_y + (i32)pos_y, + character_w*(tab_width-1), font->line_skip, + highlight_color, panel_area); + } + pos_x += character_w*tab_width; + } + + else if (font->glyphs[to_render].data){ + u32 char_color = main_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, to_render, + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, char_color, + panel_area); + pos_x += character_w; + } + + else{ + pos_x += character_w; + } + + if (pos_y > max_y){ + break; + } + } + } +} + +/* + * Hot Directory + */ + +struct Hot_Directory{ + String string; + File_List file_list; +}; + +internal void +hot_directory_init(Hot_Directory *hot_directory){ + hot_directory->string = make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + i32 dir_size = system_get_working_directory(hot_directory->string.str, + hot_directory->string.memory_size); + + if (dir_size <= 0){ + dir_size = system_get_easy_directory(hot_directory->string.str); + } + + hot_directory->string.size = dir_size; + + append(&hot_directory->string, (u8*)"\\"); +} + +internal void +hot_directory_reload_list(Hot_Directory *hot_directory){ + if (hot_directory->file_list.block){ + system_free_file_list(hot_directory->file_list); + } + hot_directory->file_list = system_get_files(hot_directory->string); +} + +internal bool32 +hot_directory_set(Hot_Directory *hot_directory, String str){ + bool32 did_set = 0; + if (copy_checked(&hot_directory->string, str)){ + did_set = 1; + system_free_file_list(hot_directory->file_list); + hot_directory->file_list = system_get_files(str); + } + return did_set; +} + +struct Hot_Directory_Match{ + u8 *filename; + bool32 is_folder; +}; + +internal Hot_Directory_Match +hot_directory_first_match(Hot_Directory *hot_directory, + String str, + bool32 include_files, + bool32 exact_match){ + Hot_Directory_Match result = {}; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + bool32 is_match = 0; + if (exact_match){ + if (match(filename, str)){ + is_match = 1; + } + } + else{ + if (str.size == 0 || + has_substr_unsensitive(filename, str)){ + is_match = 1; + } + } + + if (is_match){ + result.is_folder = files_it.folder_stage; + result.filename = filename; + break; + } + + files_iterator_step(&files, &files_it); + } + + return result; +} + +/* + * App Structs + */ + +struct Command_Data; +typedef void (*Command_Function)(Command_Data *command); + +enum Input_Request_Type{ + REQUEST_SYS_FILE, + REQUEST_LIVE_FILE, + // never below this + REQUEST_COUNT +}; + +struct Input_Request{ + Input_Request_Type type; + union{ + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + bool32 fast_folder; + } sys_file; + + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + } live_file; + }; +}; + +enum App_State{ + APP_STATE_EDIT, + APP_STATE_SEARCH, + APP_STATE_RESIZING, + // never below this + APP_STATE_COUNT +}; + +struct App_State_Incremental_Search{ + String str; + bool32 reverse; + i32 pos; +}; + +struct App_State_Resizing{ + Editing_Panel *left, *right; +}; + +struct Command_Map{ + Command_Function basic_mode_key[1+sizeof(Key_Codes)/2]; + Command_Function control_ascii[128]; + Command_Function control_key[1+sizeof(Key_Codes)/2]; +}; + +struct App_Vars{ + Command_Map map; + + Font font; + Editing_Style style; + Interactive_Style command_style; + + Editing_Working_Set working_set; + + Editing_Layout layout; + real32 last_click_x, last_click_y; + + Hot_Directory hot_directory; + + Input_Request request_queue[16]; + i32 request_count, request_filled, request_max; + + Command_Function pending_command; + + App_State state; + union{ + App_State_Incremental_Search isearch; + App_State_Resizing resizing; + }; +}; + +/* + * Commands + */ + +struct Command_Data{ + Editing_Panel *panel; + Editing_Working_Set *working_set; + Editing_Layout *layout; + Editing_Style *style; + App_Vars *vars; + + i32 screen_width, screen_height; + i32 screen_y_off; + Key_Event_Data key; + + Input_Request *requests; + i32 request_count, request_filled; +}; + +internal void +app_clear_request_queue(App_Vars *vars){ + for (i32 i = 0; i < vars->request_count; ++i){ + Input_Request *request = vars->request_queue + i; + switch (request->type){ + case REQUEST_SYS_FILE: + { + system_free_memory(request->sys_file.query.str); + system_free_memory(request->sys_file.dest.str); + }break; + + case REQUEST_LIVE_FILE: + { + system_free_memory(request->live_file.query.str); + system_free_memory(request->live_file.dest.str); + }break; + } + } + vars->request_count = 0; + vars->request_filled = 0; +} + +internal void +command_write_character(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_write_character(panel, (u8)command->key.character); + if (panel->unwrapped_lines){ + panel->preferred_x = panel->cursor.unwrapped_x; + } + else{ + panel->preferred_x = panel->cursor.wrapped_x; + } + panel->file->cursor.pos = panel->cursor.pos; + + switch ((u8)command->key.character){ + case '{': case '}': + case '(': case ')': + case ';': case ':': + case '#': case '\n': + { + panel_auto_tab(panel, panel->cursor.pos, panel->cursor.pos); + }break; + } + panel_measure_all_wrapped_y(panel); +} + +internal void +command_move_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_left(pos, data); + if (pos > 0){ + --pos; + } + else{ + pos = pos_adjust_to_self(pos, data, file->size); + } + } + else{ + --pos; + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_move_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos < size){ + ++pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, size); + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_backspace(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (cursor_pos > 0 && cursor_pos <= (i32)file->size){ + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + i32 target_pos = cursor_pos; + if (cursor_pos > 1 && + data[cursor_pos] == '\n' && + data[cursor_pos-1] == '\r'){ + --target_pos; + } + + if (target_pos > 1 && + data[target_pos-1] == '\n' && + data[target_pos-2] == '\r'){ + + buffer_delete_range(file, target_pos-2, target_pos); + if (panel->mark >= cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 2; + } + else{ + if (target_pos > 0){ + buffer_delete(file, target_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + } + } + else{ + buffer_delete(file, cursor_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} +internal void +command_delete(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (file->size > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + if (cursor_pos > 0 && + data[cursor_pos-1] == '\r' && + data[cursor_pos] == '\n'){ + buffer_delete_range(file, cursor_pos-1, cursor_pos+1); + if (panel->mark > cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 1; + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} + +internal void +command_move_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)-1; + i32 px = panel->preferred_x; + if (cy >= 0){ + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +internal void +command_move_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)+1; + i32 px = panel->preferred_x; + + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; +} + +internal void +command_seek_end_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_end_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_beginning_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u32 size = file->size; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + while (pos < size && !character_is_any_whitespace(data[pos])){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + --pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + while (pos > 0 && !character_is_any_whitespace(data[pos])){ + --pos; + } + + if (pos != 0){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + bool32 no_hard_character = 0; + while (pos > 0){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + --pos; + } + + if (pos != 0){ + ++pos; + } + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, file->size); + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + bool32 no_hard_character = 0; + i32 prev_endline = -1; + while (pos < size){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + prev_endline = pos; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + ++pos; + } + + if (prev_endline == -1 || prev_endline+1 >= size){ + pos = size-1; + } + else{ + pos = prev_endline+1; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_token_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + + // TODO(allen): Make nulltoken? + if (current_token == -1){ + current_token = 0; + } + Cpp_Token *token = &file->token_stack.tokens[current_token]; + if (token->start == panel->cursor.pos && current_token > 0){ + --token; + } + + panel_cursor_move(panel, token->start); +} + +internal void +command_seek_token_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + // TODO(allen): Make nulltoken? + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + if (get_result.in_whitespace){ + ++current_token; + } + + if (current_token >= file->token_stack.count){ + current_token = file->token_stack.count - 1; + } + + Cpp_Token *token = &file->token_stack.tokens[current_token]; + + panel_cursor_move(panel, token->start + token->size); +} + +internal void +command_begin_search_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 0; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_begin_rsearch_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 1; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_set_mark(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->mark = (i32)panel->cursor.pos; +} + +internal void +command_copy(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + u8 *data = (u8*)panel->file->data; + if (panel->file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + } +} + +internal void +command_cut(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + buffer_delete_range(file, range); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, panel->mark); + } +} + +internal void +command_paste(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0){ + panel->next_mode.rewrite = 1; + + Editing_File *file = panel->file; + + String *src = working_set_clipboard_head(working_set); + i32 pos_left = panel->cursor.pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos_left = pos_adjust_to_left(pos_left, file->data); + } + panel_write_chunk(panel, src, pos_left); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(pos_left, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = pos_left; + panel->paste_effect.end = pos_left + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } +} + +internal void +command_paste_next(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0 && panel->mode.rewrite){ + panel->next_mode.rewrite = 1; + + Range range = get_range(panel->mark, panel->cursor.pos); + + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + + String *src = working_set_clipboard_roll_down(working_set); + buffer_replace_range(file, range.smaller, range.larger, + src->str, src->size); + + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller+src->size); + panel->preferred_x = panel_get_cursor_x(panel); + panel->file->cursor.pos = panel->cursor.pos; + + // TODO(allen): faster way to recompute line measurements afterwards + buffer_measure_all_lines(file); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = range.smaller; + panel->paste_effect.end = range.smaller + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } + } +} + +internal void +command_delete_chunk(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + buffer_delete_range(file, range); + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +// TODO(allen): Make this preserve order (look at layout_open_panel) +// Make this preserve old ratios or sizes of panels instead of +// resizing them all. +internal void +command_open_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + Editing_Working_Set *working_set = command->working_set; + // TODO(allen): This is wrong. We should not be passing in the style + // like this. The system should be looking it up here. + Editing_Style *style = command->style; + // TODO(allen): change the screen info to real32 at the base level? + real32 screen_full_width = (real32)command->screen_width; + real32 screen_full_height = (real32)command->screen_height; + real32 screen_y_off = (real32)command->screen_y_off; + + layout_open_panel(layout, working_set->files, style); + real32 panel_w = ((real32)screen_full_width / layout->panel_count); + real32 panel_x_pos = 0; + + for (i32 panel_i = 0; panel_i < layout->panel_count; ++panel_i){ + Editing_Panel *panel = &layout->panels[panel_i]; + + panel->full_x = Floor(panel_x_pos); + panel->full_w = Floor(panel_w); + panel->full_y = Floor(screen_y_off); + panel->full_h = Floor(screen_full_height - screen_y_off); + + panel_fix_internal_area(panel); + + panel_x_pos += panel_w; + } +} + +internal void +command_close_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + i32 share_w = layout->panels[layout->active_panel].full_w; + layout_close_panel(layout, layout->active_panel); + layout_redistribute_width(layout, share_w); +} + +internal Input_Request* +app_request_sys_file(App_Vars *vars, String query, String dest_init, bool32 fast_folder){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_SYS_FILE; + request->sys_file.fast_folder = 1; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->sys_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->sys_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->sys_file.query, query); + copy(&request->sys_file.dest, dest_init); + return request; +} + + +internal Input_Request* +app_request_live_file(App_Vars *vars, String query, String dest_init){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_LIVE_FILE; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->live_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->live_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->live_file.query, query); + copy(&request->live_file.dest, dest_init); + return request; +} + +internal void +panel_set_to_new(Editing_Panel *panel){ + panel->cursor = {}; + panel->cursor.pos = + pos_adjust_to_self(0, panel->file->data, panel->file->size); + panel->scroll_y = 0; + panel->target_y = 0; + panel->vel_y = 1.f; + panel->scroll_x = 0; + panel->target_x = 0; + panel->vel_x = 1.f; +} + +internal void +command_reopen(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (!file->is_dummy){ + Editing_File temp_file; + if (buffer_load(&temp_file, (u8*)make_c_str(file->source_path))){ + buffer_close(file); + + // TODO(allen): Deduplicate!! + Cpp_File cpp_file; + cpp_file.data = temp_file.data; + cpp_file.size = temp_file.size; + { + i32 size = cpp_lex_file_token_count(cpp_file); + size = cpp_get_token_stack_size(size); + temp_file.token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(cpp_file, &temp_file.token_stack); + temp_file.tokens_complete = 1; + temp_file.tokens_exist = 1; + + *file = temp_file; + panel_set_to_new(panel); + panel_measure_all_wrapped_y(panel); + // TODO(allen): Update all other panels that also view this file. + } + } +} + +internal void +command_interactive_open(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + // TODO(allen): Can all of this be simplified? I don't like having this much + // complexity for issuing a single request. + vars->pending_command = command_interactive_open; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Open: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + Editing_File *new_file; + new_file = buffer_open(&vars->working_set, string->str, command->style); + + if (!new_file){ + // TODO(allen): Here's a really tough one. This crap is now happening twice. + // Once here and also in the single_file_input function which checks all of it + // whenever the fast_folder_select option is on to see if the user is selecting + // a folder. It would be nice to be able to share that information to here when it + // is available so we could avoid doing the exact same computations here. + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *string); + + Hot_Directory_Match match = + hot_directory_first_match(&vars->hot_directory, + front_name, 1, 0); + + if (match.filename){ + // NOTE(allen): is_folder case is handled by the single_file_input function + // which would only pass control to this command if the user did not match to + // a folder. + Assert(!match.is_folder); + new_file = buffer_open(&vars->working_set, string->str, command->style); + } + } + + if (new_file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = new_file; + panel_set_to_new(active_panel); + panel_measure_all_wrapped_y(active_panel); + + Cpp_File file; + file.data = new_file->data; + file.size = new_file->size; + // TODO(allen): Where should the memory for tokens come from IRL? + { + i32 size = cpp_lex_file_token_count(file); + size = cpp_get_token_stack_size(size); + new_file->token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(file, &new_file->token_stack); + new_file->tokens_complete = 1; + new_file->tokens_exist = 1; + } + } +} + +internal void +command_save(Command_Data *command){ + Editing_Panel *panel = command->panel; + String *file_path = &panel->file->source_path; + if (file_path->size > 0){ + buffer_save(panel->file, file_path->str); + } +} + +internal void +command_interactive_save_as(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_save_as; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Save As: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + + buffer_save_and_set_names(command->panel->file, string->str); + } +} + +internal void +command_change_active_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + if (layout->panel_count > 1){ + ++layout->active_panel; + if (layout->active_panel >= layout->panel_count){ + layout->active_panel = 0; + } + } +} + +internal void +command_interactive_switch_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_switch_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Switch To: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(command->working_set, *string); + + if (file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = file; + Panel_Cursor_Data cursor_data; + cursor_data = panel_compute_cursor_from_pos(active_panel, file->cursor.pos); + active_panel->cursor = cursor_data; + } + } +} + +internal void +command_kill_file(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (file && file->max_size != 0){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } +} + +internal void +command_interactive_kill_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_kill_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Kill: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(&vars->working_set, *string); + + if (file){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } + } +} + +internal void +command_toggle_line_wrap(Command_Data *command){ + Editing_Panel *panel = command->panel; + if (panel->unwrapped_lines){ + panel->unwrapped_lines = 0; + panel->target_x = 0; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + else{ + panel->unwrapped_lines = 1; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } +} + +internal void +command_toggle_endline_mode(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + panel->cursor.pos = pos_adjust_to_left(panel->cursor.pos, panel->file->data); + file->endline_mode = ENDLINE_RN_SEPARATE; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SEPARATE: + { + file->endline_mode = ENDLINE_RN_SHOWALLR; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SHOWALLR: + { + panel->cursor.pos = pos_adjust_to_self(panel->cursor.pos, panel->file->data, + panel->file->size); + file->endline_mode = ENDLINE_RN_COMBINED; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + } +} + +internal void +command_to_uppercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'a' && data[i] <= 'z'){ + data[i] += (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_to_lowercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'A' && data[i] <= 'Z'){ + data[i] -= (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_toggle_show_whitespace(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->show_whitespace = !panel->show_whitespace; +} + +internal void +command_clean_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + i32 last_hard_start = pos-1; + i32 last_hard = last_hard_start; + while (pos < file->size && data[pos] != '\n'){ + if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + } + ++pos; + } + + if (last_hard != last_hard_start){ + pos = pos_adjust_to_left(pos, data); + + if (last_hard + 1 < pos){ + buffer_replace_range(file, last_hard+1, pos, 0, 0, REP_WHITESPACE); + panel_measure_all_wrapped_y(panel); + + if (panel->cursor.pos > last_hard){ + panel->cursor = panel_compute_cursor_from_pos(panel, last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= pos - (last_hard + 1); + } + } + } + else{ + panel_measure_all_wrapped_y(panel); + panel_auto_tab(panel, pos, pos); + } +} + +internal void +command_clean_all_lines(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 cursor_pos = panel->cursor.pos; + i32 pos = 0; + i32 last_hard = -1; + bool32 is_all_white = 1; + while (pos <= file->size){ + if (pos == file->size || data[pos] == '\n'){ + i32 line_pos = pos; + if (pos < file->size && data[pos] == '\n'){ + line_pos = pos_adjust_to_left(pos, data); + } + + // TODO(allen): This should be optimized by either keeping track of the nesting level + // in this funciton, or by at least having a nesting hint that is used and updated + // every time an auto tab happens. Also auto tab should take a nesting hint. + if (is_all_white){ + panel_auto_tab(panel, pos, pos); + } + else{ + if (last_hard + 1 < line_pos){ + buffer_replace_range(file, last_hard+1, line_pos, 0, 0, REP_WHITESPACE); + + if (cursor_pos > last_hard && cursor_pos <= pos){ + cursor_pos = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (cursor_pos > pos){ + cursor_pos -= line_pos - (last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= line_pos - (last_hard + 1); + } + pos -= line_pos - (last_hard + 1); + } + } + + last_hard = pos; + is_all_white = 1; + } + else if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + is_all_white = 0; + } + ++pos; + } + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, cursor_pos); +} + +internal void +command_eol_dosify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_RN, ENDLINE_ERASE, ENDLINE_RN); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_eol_nixify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_N, ENDLINE_ERASE, ENDLINE_N); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_auto_tab(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + panel_auto_tab(panel, range.smaller, range.larger); + panel_measure_all_wrapped_y(panel); +} + +internal void +setup_commands(Command_Map *commands, Key_Codes *codes){ + commands->basic_mode_key[codes->left] = command_move_left; + commands->basic_mode_key[codes->right] = command_move_right; + commands->basic_mode_key[codes->del] = command_delete; + commands->basic_mode_key[codes->back] = command_backspace; + commands->basic_mode_key[codes->up] = command_move_up; + commands->basic_mode_key[codes->down] = command_move_down; + commands->basic_mode_key[codes->end] = command_seek_end_of_line; + commands->basic_mode_key[codes->home] = command_seek_beginning_of_line; + +#if 0 + commands->control_key[codes->right] = command_seek_token_right; + commands->control_ascii['m'] = command_seek_token_right; + commands->control_key[codes->left] = command_seek_token_left; + commands->control_ascii['n'] = command_seek_token_left; +#else + commands->control_key[codes->right] = command_seek_whitespace_right; + commands->control_ascii['m'] = command_seek_whitespace_right; + commands->control_key[codes->left] = command_seek_whitespace_left; + commands->control_ascii['n'] = command_seek_whitespace_left; +#endif + + commands->control_key[codes->up] = command_seek_whitespace_up; + commands->control_ascii['y'] = command_seek_whitespace_up; + commands->control_key[codes->down] = command_seek_whitespace_down; + commands->control_ascii['h'] = command_seek_whitespace_down; + + commands->control_ascii['\t'] = command_auto_tab; + + commands->control_ascii[' '] = command_set_mark; + commands->control_ascii['c'] = command_copy; + commands->control_ascii['x'] = command_cut; + commands->control_ascii['v'] = command_paste; + commands->control_ascii['V'] = command_paste_next; + commands->control_ascii['d'] = command_delete_chunk; + commands->control_ascii['p'] = command_open_panel; + commands->control_ascii['P'] = command_close_panel; + commands->control_ascii['o'] = command_interactive_open; + commands->control_ascii['O'] = command_reopen; + commands->control_ascii['s'] = command_save; + commands->control_ascii['w'] = command_interactive_save_as; + commands->control_ascii[','] = command_change_active_panel; + commands->control_ascii['i'] = command_interactive_switch_file; + commands->control_ascii['k'] = command_interactive_kill_file; + commands->control_ascii['K'] = command_kill_file; + commands->control_ascii['l'] = command_toggle_line_wrap; + commands->control_ascii['L'] = command_toggle_endline_mode; + commands->control_ascii['u'] = command_to_uppercase; + commands->control_ascii['j'] = command_to_lowercase; + commands->control_ascii['?'] = command_toggle_show_whitespace; + commands->control_ascii['`'] = command_clean_line; + commands->control_ascii['~'] = command_clean_all_lines; + commands->control_ascii['1'] = command_eol_dosify; + commands->control_ascii['!'] = command_eol_nixify; + commands->control_ascii['f'] = command_begin_search_state; + commands->control_ascii['r'] = command_begin_rsearch_state; +} + +/* + * Interactive Bar + */ + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, u8 *str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; str[i]; ++i){ + font_draw_glyph(target, bar->style.font, str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, String str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; i < str.size; ++i){ + font_draw_glyph(target, bar->style.font, str.str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +hot_directory_draw_helper(Render_Target *target, + Hot_Directory *hot_directory, + Interactive_Bar *bar, String *string, + bool32 include_files){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.pop1_color); + + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + + get_front_of_directory(&front_name, *string); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + if (front_name.size == 0 || has_substr_unsensitive(filename, front_name)){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + if (files_it.folder_stage){ + intbar_draw_string(target, bar, filename, bar->style.pop1_color); + intbar_draw_string(target, bar, (u8*)"/", bar->style.pop1_color); + } + else{ + intbar_draw_string(target, bar, filename, bar->style.base_color); + } + } + + files_iterator_step(&files, &files_it); + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +internal void +live_file_draw_helper(Render_Target *target, + Editing_Working_Set *working_set, + Interactive_Bar *bar, String *string){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.base_color); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + for (i32 file_i = 0; + file_i < working_set->file_index_count; + ++file_i){ + Editing_File *file = &working_set->files[file_i]; + if (file->live_name.str && + (string->size == 0 || has_substr_unsensitive(file->live_name, *string))){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + intbar_draw_string(target, bar, file->live_name, bar->style.base_color); + } + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +/* + * App Functions + */ + +internal bool32 +app_init(Thread_Context *thread, Application_Memory *memory, + Key_Codes *loose_codes, Clipboard_Contents clipboard){ + Partition_Cursor partition = + partition_open(memory->vars_memory, memory->vars_memory_size); + + App_Vars *vars = (App_Vars*) + partition_allocate(&partition, sizeof(App_Vars)); + + Assert(vars); + *vars = {}; + + u32 panel_max_count = vars->layout.panel_max_count = 4; + u32 panel_count = vars->layout.panel_count = 1; + Assert(panel_max_count >= 1); + Editing_Panel *panels = vars->layout.panels = (Editing_Panel*) + partition_allocate(&partition, sizeof(Editing_Panel)*panel_max_count); + + Assert(panels); + // TODO(allen): improved Assert + + // NOTE(allen): command map setup + setup_commands(&vars->map, loose_codes); + + // NOTE(allen): font setup + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + i32 memory_used = 0; + if (font_load(&vars->font, 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): file setup + vars->working_set.file_index_count = 1; + vars->working_set.file_max_count = 29; + + vars->working_set.files = (Editing_File*) + partition_allocate(&partition, + sizeof(Editing_File)*vars->working_set.file_max_count); + + buffer_get_dummy(&vars->working_set.files[0], &vars->style); + + // NOTE(allen): clipboard setup + vars->working_set.clipboard_max_size = ArrayCount(vars->working_set.clipboards); + vars->working_set.clipboard_size = 0; + vars->working_set.clipboard_current = 0; + vars->working_set.clipboard_rolling = 0; + + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // TODO(allen): more robust allocation solution for the clipboard? + + // NOTE(allen): style setup + // TODO(allen): style_set_font function + vars->style.font = &vars->font; + vars->style.font_metrics.character_width = vars->font.glyphs[' '].advance; + vars->style.font_metrics.line_skip = vars->font.line_skip; + + vars->style.back_color = 0xFF0C0C0C; + vars->style.margin_color = 0xFF181818; + vars->style.cursor_color = 0xFF00EE00; + vars->style.highlight_color = 0xFFDDEE00; + vars->style.mark_color = 0xFF494949; + vars->style.default_color = 0xFF90B080; + vars->style.at_cursor_color = vars->style.back_color; + vars->style.at_highlight_color = 0xFFFF44DD; + vars->style.comment_color = 0xFF2090F0; + vars->style.keyword_color = 0xFFD08F20; + vars->style.constant_color = 0xFF50FF30; + vars->style.special_character_color = 0xFFFF0000; + + vars->style.use_paste_color = 1; + vars->style.paste_color = 0xFFDDEE00; + + vars->style.highlight_junk_color = 0x44FF0000; + vars->style.highlight_white_color = 0x1100FFFF; + vars->style.tab_width = 4; + vars->style.margin_width = 5; + + Interactive_Style file_info_style; + file_info_style.font = &vars->font; + file_info_style.bar_color = 0xFF888888; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF4444AA; + file_info_style.pop2_color = 0xFFFF0000; + file_info_style.height = vars->style.font->line_skip + 2; + + vars->style.file_info_style = file_info_style; + + Interactive_Style command_style; + command_style.font = &vars->font; + command_style.bar_color = 0xFF0C0C0C; + command_style.base_color = 0xFFDDDDBB; + command_style.pop1_color = 0xFF4444AA; + command_style.pop2_color = 0xFF44FF44; + command_style.height = vars->style.font->line_skip + 2; + + vars->command_style = command_style; + + // NOTE(allen): panel setup + AllowLocal(panel_count); + panel_init(&panels[0], &vars->working_set.files[0]); + + // NOTE(allen): hot directory setup + hot_directory_init(&vars->hot_directory); + + // NOTE(allen): request stack setup + vars->request_count = 0; + vars->request_filled = 0; + vars->request_max = ArrayCount(vars->request_queue); + + return 1; +} + +struct Single_Line_Input_Step{ + bool32 hit_newline; + bool32 hit_ctrl_newline; + bool32 hit_a_character; + bool32 hit_backspace; + bool32 hit_esc; + bool32 made_a_change; + bool32 did_command; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + bool32 fast_folder_select; +}; + +internal Single_Line_Input_Step +app_single_line_input_core(Key_Codes *codes, + Key_Input_Data *input, + Single_Line_Mode mode){ + Single_Line_Input_Step result = {}; + + if (input->has_press){ + if (input->press.keycode == codes->back){ + result.hit_backspace = 1; + switch (mode.type){ + case SINGLE_LINE_STRING: + { + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + if (mode.string->size > 0){ + --mode.string->size; + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size > 0){ + --mode.string->size; + i8 end_character = mode.string->str[mode.string->size]; + if (character_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + // TODO(allen): What to do when the string becomes empty though? + mode.string->str[mode.string->size] = 0; + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + mode.string->str[mode.string->size] = 0; + } + result.made_a_change = 1; + } + }break; + } + } + + else if (input->press.keycode == codes->newline){ + if (input->control_keys[CONTROL_KEY_CONTROL]){ + result.hit_ctrl_newline = 1; + result.made_a_change = 1; + } + + else{ + result.made_a_change = 1; + if (mode.fast_folder_select){ + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *mode.string); + Hot_Directory_Match match; + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 1); + + if (!match.filename){ + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0); + } + + if (match.filename){ + if (match.is_folder){ + set_last_folder(mode.string, match.filename); + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + append(mode.string, match.filename); + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + } + + else if (input->press.keycode == codes->esc){ + result.hit_esc = 1; + result.made_a_change = 1; + } + + else if (input->press.character){ + result.hit_a_character = 1; + if (!input->control_keys[CONTROL_KEY_CONTROL]){ + switch (mode.type){ + case SINGLE_LINE_STRING: + { + if (mode.string->size+1 < mode.string->memory_size){ + mode.string->str[mode.string->size] = (i8)input->press.character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size+1 < mode.string->memory_size){ + i8 new_character = (i8)input->press.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + + if (character_is_slash(new_character)){ + hot_directory_set(mode.hot_directory, *mode.string); + } + result.made_a_change = 1; + } + }break; + } + } + + else{ + result.did_command = 1; + result.made_a_change = 1; + } + } + } + + return result; +} + +inline internal Single_Line_Input_Step +app_single_line_input_step(Key_Codes *codes, Key_Input_Data *input, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Single_Line_Input_Step +app_single_file_input_step(Key_Codes *codes, Key_Input_Data *input, + String *string, Hot_Directory *hot_directory, + bool32 fast_folder_select){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.fast_folder_select = fast_folder_select; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Command_Function +app_get_command(App_Vars *vars, Key_Input_Data *input, Key_Event_Data key){ + Command_Function function = 0; + if (input->control_keys[CONTROL_KEY_CONTROL]){ + if (key.character_no_caps_lock){ + function = vars->map.control_ascii[key.character_no_caps_lock]; + } + else{ + function = vars->map.control_key[key.keycode]; + } + } + else if (!input->control_keys[CONTROL_KEY_ALT]){ + if (key.character != 0){ + function = command_write_character; + } + else{ + function = vars->map.basic_mode_key[key.keycode]; + } + } + return function; +} + +internal bool32 +smooth_camera_step(real32 *target, real32 *current, real32 *vel, real32 S, real32 T){ + bool32 result = 0; + real32 targ = *target; + real32 curr = *current; + real32 v = *vel; + if (curr != targ){ + real32 L = lerp(curr, T, targ); + + i32 sign = (targ > curr) - (targ < curr); + real32 V = curr + sign*v; + + if (sign > 0){ + curr = Min(L, V); + } + else{ + curr = Max(L, V); + } + + if (curr == V){ + v *= S; + } + + if (curr > targ - .0001f && curr < targ + .0001f){ + curr = targ; + v = 1.f; + } + *target = targ; + *current = curr; + *vel = v; + result = 1; + } + return result; +} + +#if FRED_INTERNAL +Application_Memory *GLOBAL; +#endif + +internal Application_Step_Result +app_step(Thread_Context *thread, Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory, + Clipboard_Contents clipboard, + bool32 first_step){ + +#if FRED_INTERNAL + GLOBAL = memory; +#endif + + ProfileStart(app_step); + ProfileSection(thread, app_step, "start"); + + Application_Step_Result app_result = {}; + app_result.redraw = 1; + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // TODO(allen): Let's find another way to do first_step + // so we can get it out of app_step and the platform layer. + if (first_step || !time_step){ + app_result.redraw = 1; + } + + Editing_Panel *panels = vars->layout.panels; + Editing_Panel *active_panel = &panels[vars->layout.active_panel]; + ProfileSection(thread, app_step, "setup"); + + // NOTE(allen): OS clipboard event handling + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // NOTE(allen): check files are up to date + for (i32 i = 0; i < vars->working_set.file_index_count; ++i){ + Editing_File *file = vars->working_set.files + i; + + if (!file->is_dummy){ + Time_Stamp time_stamp; + time_stamp = system_file_time_stamp((u8*)make_c_str(file->source_path)); + + if (time_stamp.success){ + file->last_sys_write_time = time_stamp.time; + if (file->last_sys_write_time != file->last_4ed_write_time){ + app_result.redraw = 1; + } + } + } + } + ProfileSection(thread, app_step, "OS syncing"); + + // NOTE(allen): keyboard input handling + if (time_step){ + switch (vars->state){ + case APP_STATE_EDIT: + { + Command_Data command_data; + command_data.panel = active_panel; + command_data.working_set = &vars->working_set; + command_data.layout = &vars->layout; + command_data.style = &vars->style; + command_data.vars = vars; + command_data.screen_width = target->width; + command_data.screen_height = target->height; + command_data.screen_y_off = vars->command_style.height; + command_data.requests = 0; + command_data.request_count = 0; + command_data.request_filled = 0; + + if (vars->request_count == 0){ + Command_Function function = 0; + if (input->has_press){ + command_data.key = input->press; + function = app_get_command(vars, input, command_data.key); + } + + else if (input->has_hold){ + command_data.key = input->hold; + function = app_get_command(vars, input, command_data.key); + + } + + if (function){ + command_data.panel->next_mode = {}; + function(&command_data); + app_result.redraw = 1; + command_data.panel->mode = command_data.panel->next_mode; + } + } + + else{ + + Input_Request *active_request = vars->request_queue + vars->request_filled; + bool32 killed_command = 0; + switch (active_request->type){ + case REQUEST_SYS_FILE: + { + String *string = &active_request->sys_file.dest; + Single_Line_Input_Step result = + app_single_file_input_step(codes, input, string, &vars->hot_directory, 1); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->sys_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + case REQUEST_LIVE_FILE: + { + String *string = &active_request->live_file.dest; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->live_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + } + + if (vars->request_filled == vars->request_count && !killed_command){ + command_data.requests = vars->request_queue; + command_data.request_count = vars->request_count; + command_data.request_filled = vars->request_filled; + vars->pending_command(&command_data); + app_clear_request_queue(vars); + } + } + + }break; + + case APP_STATE_SEARCH: + { + String *string = &vars->isearch.str; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + + Editing_File *file = active_panel->file; + + bool32 step_forward = 0; + bool32 step_backward = 0; + + if (input->has_press){ + Key_Event_Data key = input->press; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + if (!step_forward && !step_backward && + !input->has_press && input->has_hold){ + Key_Event_Data key = input->hold; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + i32 start_pos = vars->isearch.pos; + if (step_forward){ + if (vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 0; + } + } + if (step_backward){ + if (!vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 1; + } + } + + String file_string = make_string(file->data, file->size); + + i32 pos; + + if (vars->isearch.reverse){ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + } + else{ + pos = rfind_substr(file_string, start_pos - 1, *string); + + if (pos >= 0){ + if (step_backward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = rfind_substr(file_string, start_pos - 1, *string); + if (pos == -1){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + else{ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + } + else{ + pos = find_substr(file_string, start_pos + 1, *string); + + if (pos < file->size){ + if (step_forward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = find_substr(file_string, start_pos + 1, *string); + if (pos == file->size){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + } + + if (result.hit_newline || result.hit_ctrl_newline){ + panel_cursor_move(active_panel, active_panel->temp_highlight); + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + if (result.hit_esc){ + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + }break; + + case APP_STATE_RESIZING: + { + if (input->has_press){ + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "keyboard input"); + + // NOTE(allen): reorganizing panels on screen + i32 prev_width = vars->layout.full_width; + i32 prev_height = vars->layout.full_height; + i32 full_width = vars->layout.full_width = target->width; + i32 full_height = vars->layout.full_height = target->height; + + if (prev_width != full_width || prev_height != full_height){ + layout_refit(&vars->layout, 0, vars->command_style.height, + full_width, full_height, + prev_width, prev_height); + + for (i32 panel_i = 0; + panel_i < vars->layout.panel_count && + vars->layout.panel_count > 1; + ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + + Editing_Style *style = panel->file->style; + i32 character_w = style_get_character_width(style); + i32 margin_width = style->margin_width; + if (panel->full_w < character_w*6 + margin_width*2){ + i32 share_w = panel->full_w; + layout_close_panel(&vars->layout, panel_i); + layout_redistribute_width(&vars->layout, share_w); + panel_i = 0; + } + } + + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = panels + panel_i; + if (!panel->file->is_dummy){ + panel->cursor = panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + } + + app_result.redraw = 1; + } + ProfileSection(thread, app_step, "reorganizing panels"); + + // NOTE(allen): mouse input handling + if (time_step && !mouse->out_of_window){ + + i32 mx = mouse->x; + i32 my = mouse->y; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + // TODO(allen): + // This means any mouse use will be impossible, I guess it will have + // to depend on the type of the request seen at the top??? + app_clear_request_queue(vars); + mouse_press_event = 1; + + switch (vars->state){ + case APP_STATE_SEARCH: + { + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + }break; + } + } + + switch (vars->state){ + case APP_STATE_EDIT: + { + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + + Editing_Panel *panel = &panels[panel_i]; + + if (mx >= panel->x && + mx < panel->w + panel->x && + my >= panel->y && + my < panel->h + panel->y){ + + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_IBEAM; + + if (mouse_press_event){ + if (!panel->file->is_dummy){ + app_result.redraw = 1; + + Font *font = &vars->font; + + i32 character_w = style_get_character_width(panel->file->style); + i32 character_h = font->line_skip; + i32 max_line_length = panel_compute_max_line_length(panel); + i32 max_lines = panel_compute_max_lines(panel); + + real32 grid_x = ((real32)mx - panel->x) / character_w; + real32 grid_y = ((real32)my - panel->y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + if (grid_x >= 0 && grid_x <= max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = (i32)(grid_x + active_panel->scroll_x); + i32 pos_y = (i32)(grid_y + active_panel->scroll_y); + + panel_cursor_move(active_panel, pos_x, pos_y); + } + } + vars->layout.active_panel = panel_i; + active_panel = panel; + } + else{ + // NOTE(allen): mouse inside editing area but no click + } + } + + else if (mx >= panel->full_x && + mx < panel->full_w + panel->full_x && + my >= panel->full_y && + my < panel->full_h + panel->full_y){ + // NOTE(allen): not inside the editing area but within the margins + bool32 resize_area = 0; + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + if (mx >= (panel->full_x+panel->full_w+panel->x+panel->w)/2){ + if (panel_i != vars->layout.panel_count-1){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = 1; + } + } + else if (mx <= (panel->full_x+panel->x)/2){ + if (panel_i != 0){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = -1; + } + } + + if (resize_area != 0 && mouse_press_event){ + vars->state = APP_STATE_RESIZING; + if (resize_area == 1){ + vars->resizing.left = panel; + vars->resizing.right = panel+1; + } + else if (resize_area == -1){ + vars->resizing.left = panel-1; + vars->resizing.right = panel; + } + } + } + } + + i32 cursor_y = panel_get_cursor_y(active_panel); + real32 target_y = active_panel->target_y; + i32 max_lines = panel_compute_max_lines(active_panel); + + bool32 wheel_used; + + real32 delta_target_y = Max(1, max_lines / 3.f); + delta_target_y *= -mouse->wheel; + + target_y += delta_target_y; + + if (target_y < 0){ + target_y = 0; + } + + if (mouse->wheel == 0){ + wheel_used = 0; + } + else{ + wheel_used = 1; + + if (cursor_y >= target_y + max_lines){ + cursor_y = (i32)target_y + max_lines - 1; + } + if (cursor_y < target_y){ + cursor_y = (i32)target_y + 1; + } + } + + active_panel->target_y = target_y; + if (cursor_y != panel_get_cursor_y(active_panel)){ + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + active_panel->preferred_x, + cursor_y); + } + + if (wheel_used){ + app_result.redraw = 1; + } + }break; + + case APP_STATE_RESIZING: + { + if (mouse->left_button){ + Editing_Panel *left = vars->resizing.left; + Editing_Panel *right = vars->resizing.right; + i32 left_x = left->full_x; + i32 left_w = left->full_w; + i32 right_x = right->full_x; + i32 right_w = right->full_w; + + AllowLocal(right_x); + + i32 new_left_x = left_x; + i32 new_left_w = mx - left_x; + i32 new_right_w = right_w - (new_left_w - left_w); + i32 new_right_x = mx; + + if (left_w != new_left_w){ + app_result.redraw = 1; + + Editing_Style *left_style = left->file->style; + Editing_Style *right_style = right->file->style; + + i32 left_character_w = style_get_character_width(left_style); + i32 right_character_w = style_get_character_width(right_style); + + i32 left_margin_width = left_style->margin_width; + i32 right_margin_width = right_style->margin_width; + if (new_left_w > left_margin_width*2 + left_character_w*6 && + new_right_w > right_margin_width*2 + right_character_w*6){ + left->full_x = new_left_x; + left->full_w = new_left_w; + right->full_x = new_right_x; + right->full_w = new_right_w; + + panel_fix_internal_area(left); + panel_fix_internal_area(right); + } + } + } + else{ + app_result.redraw = 1; + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "mouse input"); + + // NOTE(allen): fix scrolling on all panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + i32 cursor_y; + if (panel->show_temp_highlight){ + if (panel->unwrapped_lines){ + cursor_y = panel->temp_highlight.unwrapped_y; + } + else{ + cursor_y = panel->temp_highlight.wrapped_y; + } + } + else{ + if (panel->unwrapped_lines){ + cursor_y = panel->cursor.unwrapped_y; + } + else{ + cursor_y = panel->cursor.wrapped_y; + } + } + real32 target_y = panel->target_y; + real32 original_target_y = target_y; + i32 max_lines = panel_compute_max_lines(panel); + + while (cursor_y >= Floor(target_y) + max_lines){ + target_y += 3.f; + } + + while (cursor_y < target_y){ + target_y -= 3.f; + } + + if (target_y < 0){ + target_y = 0; + } + + panel->target_y = target_y; + + i32 cursor_x = panel_get_cursor_x(panel); + real32 target_x = panel->target_x; + real32 original_target_x = target_x; + i32 max_x = panel_compute_max_line_length(panel); + if (cursor_x < target_x){ + target_x = (real32)Max(0, cursor_x - max_x/2); + } + else if (cursor_x >= target_x + max_x){ + target_x = (real32)(cursor_x - max_x/2); + } + + panel->target_x = target_x; + + if (original_target_y != panel->target_y || + original_target_x != panel->target_x){ + app_result.redraw; + } + } + ProfileSection(thread, app_step, "fix scrolling"); + + // NOTE(allen): execute animations + for (i32 i = 0; i < vars->layout.panel_count; ++i){ + Editing_Panel *panel = vars->layout.panels + i; + + // TODO(allen): Scrolling parameterization in style? + if (smooth_camera_step(&panel->target_y, &panel->scroll_y, &panel->vel_y, 2.f, 1.f/9.f)){ + app_result.redraw = 1; + } + + if (smooth_camera_step(&panel->target_x, &panel->scroll_x, &panel->vel_x, 2.f, 1.f/6.f)){ + app_result.redraw = 1; + } + + if (panel->paste_effect.tick_down > 0){ + --panel->paste_effect.tick_down; + app_result.redraw = 1; + } + + } + ProfileSection(thread, app_step, "execute animations"); + + if (app_result.redraw){ + // NOTE(allen): render the panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + Editing_Style *style = panel->file->style; + + i32 full_left = panel->full_x; + i32 full_top = panel->full_y; + i32 full_right = full_left + panel->full_w; + i32 full_bottom = full_top + panel->full_h; + + u32 back_color = style->back_color; + draw_rectangle_2corner(target, full_left, full_top, full_right, full_bottom, back_color); + + u32 side_margin_color = style->margin_color; + + panel_draw(thread, target, panel, vars->layout.active_panel == panel_i); + + // NOTE(allen): file info bar + { + Interactive_Bar bar; + bar.style = style->file_info_style; + bar.pos_x = panel->x; + bar.pos_y = full_top; + + draw_rectangle(target, full_left, bar.pos_y, + panel->full_w, bar.style.height, + bar.style.bar_color); + + Editing_File *file = panel->file; + if (!file->is_dummy){ + intbar_draw_string(target, &bar, panel->file->live_name, bar.style.base_color); + intbar_draw_string(target, &bar, make_lit_string(" - "), bar.style.base_color); + + u8 line_number_space[30]; + String line_number = make_string(line_number_space, 0, 30); + append(&line_number, (u8*)"L#"); + append_int_to_str(panel->cursor.line, &line_number); + + intbar_draw_string(target, &bar, line_number, bar.style.base_color); + + if (file->last_4ed_write_time != file->last_sys_write_time){ + persist String out_of_sync = make_lit_string(" FILE SYNC"); + intbar_draw_string(target, &bar, out_of_sync, bar.style.pop2_color); + } + } + } + + // L + draw_rectangle_2corner(target, full_left, panel->y, + panel->x, full_bottom, side_margin_color); + + // R + draw_rectangle_2corner(target, panel->x + panel->w, panel->y, + full_right, full_bottom, side_margin_color); + + // B + draw_rectangle_2corner(target, full_left, panel->y + panel->h, + full_right, full_bottom, side_margin_color); + + if (panel_i != 0){ + draw_rectangle_2corner(target, panel->full_x-1, panel->full_y, + panel->full_x+1, panel->full_y+panel->full_h, + 0xFFFFFFFF); + } + } + ProfileSection(thread, app_step, "render files"); + + // NOTE (allen): command bar + { + Interactive_Bar bar; + bar.style = vars->command_style; + bar.pos_x = 0; + bar.pos_y = 0; + + draw_rectangle(target, 0, 0, + target->width, bar.style.height, + bar.style.bar_color); + + switch (vars->state){ + case APP_STATE_EDIT: + { + if (vars->request_count > 0){ + Input_Request *request = vars->request_queue + vars->request_filled; + + switch (request->type){ + case REQUEST_SYS_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + hot_directory_draw_helper(target, &vars->hot_directory, &bar, string, 1); + }break; + + case REQUEST_LIVE_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + live_file_draw_helper(target, &vars->working_set, &bar, string); + }break; + + } + } + }break; + + case APP_STATE_SEARCH: + { + persist String search_str = make_lit_string("I-Search: "); + persist String rsearch_str = make_lit_string("Reverse-I-Search: "); + if (vars->isearch.reverse){ + intbar_draw_string(target, &bar, rsearch_str, bar.style.pop2_color); + } + else{ + intbar_draw_string(target, &bar, search_str, bar.style.pop2_color); + } + + intbar_draw_string(target, &bar, vars->isearch.str, bar.style.base_color); + }break; + + case APP_STATE_RESIZING: + { + intbar_draw_string(target, &bar, make_lit_string("Resizing!"), bar.style.pop2_color); + }break; + } + } + ProfileSection(thread, app_step, "interaction bar"); + + } + + ProfileEnd(thread, app_step, "total"); + + return app_result; +} + +// BOTTOM + diff --git a/test_data/sample_files/btParallelConstraintSolver.h b/test_data/sample_files/btParallelConstraintSolver.h new file mode 100644 index 0000000..af42a83 --- /dev/null +++ b/test_data/sample_files/btParallelConstraintSolver.h @@ -0,0 +1,288 @@ +/* + Copyright (C) 2010 Sony Computer Entertainment Inc. + All rights reserved. + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +#ifndef __BT_PARALLEL_CONSTRAINT_SOLVER_H +#define __BT_PARALLEL_CONSTRAINT_SOLVER_H + +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" + + + + +#include "LinearMath/btScalar.h" +#include "PlatformDefinitions.h" + + +#define PFX_MAX_SOLVER_PHASES 64 +#define PFX_MAX_SOLVER_BATCHES 16 +#define PFX_MAX_SOLVER_PAIRS 128 +#define PFX_MIN_SOLVER_PAIRS 16 + +#ifdef __CELLOS_LV2__ +ATTRIBUTE_ALIGNED128(struct) PfxParallelBatch { +#else +ATTRIBUTE_ALIGNED16(struct) PfxParallelBatch { +#endif + uint16_t pairIndices[PFX_MAX_SOLVER_PAIRS]; +}; + +#ifdef __CELLOS_LV2__ +ATTRIBUTE_ALIGNED128(struct) PfxParallelGroup { +#else +ATTRIBUTE_ALIGNED16(struct) PfxParallelGroup { +#endif + uint16_t numPhases; + uint16_t numBatches[PFX_MAX_SOLVER_PHASES]; + uint16_t numPairs[PFX_MAX_SOLVER_PHASES*PFX_MAX_SOLVER_BATCHES]; +}; + + + +ATTRIBUTE_ALIGNED16(struct) PfxSortData16 { + union { + uint8_t i8data[16]; + uint16_t i16data[8]; + uint32_t i32data[4]; +#ifdef __SPU__ + vec_uint4 vdata; +#endif + }; + +#ifdef __SPU__ + void set8(int elem,uint8_t data) {vdata=(vec_uint4)spu_insert(data,(vec_uchar16)vdata,elem);} + void set16(int elem,uint16_t data) {vdata=(vec_uint4)spu_insert(data,(vec_ushort8)vdata,elem);} + void set32(int elem,uint32_t data) {vdata=(vec_uint4)spu_insert(data,(vec_uint4)vdata,elem);} + uint8_t get8(int elem) const {return spu_extract((vec_uchar16)vdata,elem);} + uint16_t get16(int elem) const {return spu_extract((vec_ushort8)vdata,elem);} + uint32_t get32(int elem) const {return spu_extract((vec_uint4)vdata,elem);} +#else + void set8(int elem,uint8_t data) {i8data[elem] = data;} + void set16(int elem,uint16_t data) {i16data[elem] = data;} + void set32(int elem,uint32_t data) {i32data[elem] = data;} + uint8_t get8(int elem) const {return i8data[elem];} + uint16_t get16(int elem) const {return i16data[elem];} + uint32_t get32(int elem) const {return i32data[elem];} +#endif +}; + +typedef PfxSortData16 PfxConstraintPair; + + +//J PfxBroadphasePair‚Æ‹¤’Ê + +SIMD_FORCE_INLINE void pfxSetConstraintId(PfxConstraintPair &pair,uint32_t i) {pair.set32(2,i);} +SIMD_FORCE_INLINE void pfxSetNumConstraints(PfxConstraintPair &pair,uint8_t n) {pair.set8(7,n);} + +SIMD_FORCE_INLINE uint32_t pfxGetConstraintId1(const PfxConstraintPair &pair) {return pair.get32(2);} +SIMD_FORCE_INLINE uint8_t pfxGetNumConstraints(const PfxConstraintPair &pair) {return pair.get8(7);} + +typedef PfxSortData16 PfxBroadphasePair; + +SIMD_FORCE_INLINE void pfxSetRigidBodyIdA(PfxBroadphasePair &pair,uint16_t i) {pair.set16(0,i);} +SIMD_FORCE_INLINE void pfxSetRigidBodyIdB(PfxBroadphasePair &pair,uint16_t i) {pair.set16(1,i);} +SIMD_FORCE_INLINE void pfxSetMotionMaskA(PfxBroadphasePair &pair,uint8_t i) {pair.set8(4,i);} +SIMD_FORCE_INLINE void pfxSetMotionMaskB(PfxBroadphasePair &pair,uint8_t i) {pair.set8(5,i);} +SIMD_FORCE_INLINE void pfxSetBroadphaseFlag(PfxBroadphasePair &pair,uint8_t f) {pair.set8(6,(pair.get8(6)&0xf0)|(f&0x0f));} +SIMD_FORCE_INLINE void pfxSetActive(PfxBroadphasePair &pair,bool b) {pair.set8(6,(pair.get8(6)&0x0f)|((b?1:0)<<4));} +SIMD_FORCE_INLINE void pfxSetContactId(PfxBroadphasePair &pair,uint32_t i) {pair.set32(2,i);} + +SIMD_FORCE_INLINE uint16_t pfxGetRigidBodyIdA(const PfxBroadphasePair &pair) {return pair.get16(0);} +SIMD_FORCE_INLINE uint16_t pfxGetRigidBodyIdB(const PfxBroadphasePair &pair) {return pair.get16(1);} +SIMD_FORCE_INLINE uint8_t pfxGetMotionMaskA(const PfxBroadphasePair &pair) {return pair.get8(4);} +SIMD_FORCE_INLINE uint8_t pfxGetMotionMaskB(const PfxBroadphasePair &pair) {return pair.get8(5);} +SIMD_FORCE_INLINE uint8_t pfxGetBroadphaseFlag(const PfxBroadphasePair &pair) {return pair.get8(6)&0x0f;} +SIMD_FORCE_INLINE bool pfxGetActive(const PfxBroadphasePair &pair) {return (pair.get8(6)>>4)!=0;} +SIMD_FORCE_INLINE uint32_t pfxGetContactId1(const PfxBroadphasePair &pair) {return pair.get32(2);} + + + +#if defined(__PPU__) || defined (__SPU__) +ATTRIBUTE_ALIGNED128(struct) PfxSolverBody { +#else +ATTRIBUTE_ALIGNED16(struct) PfxSolverBody { +#endif + vmVector3 mDeltaLinearVelocity; + vmVector3 mDeltaAngularVelocity; + vmMatrix3 mInertiaInv; + vmQuat mOrientation; + float mMassInv; + float friction; + float restitution; + float unused; + float unused2; + float unused3; + float unused4; + float unused5; +}; + + +#ifdef __PPU__ +#include "SpuDispatch/BulletPE2ConstraintSolverSpursSupport.h" +#endif + +static SIMD_FORCE_INLINE vmVector3 btReadVector3(const double* p) +{ + float tmp[3] = {float(p[0]),float(p[1]),float(p[2])}; + vmVector3 v; + loadXYZ(v, tmp); + return v; +} + +static SIMD_FORCE_INLINE vmQuat btReadQuat(const double* p) +{ + float tmp[4] = {float(p[0]),float(p[1]),float(p[2]),float(p[4])}; + vmQuat vq; + loadXYZW(vq, tmp); + return vq; +} + +static SIMD_FORCE_INLINE void btStoreVector3(const vmVector3 &src, double* p) +{ + float tmp[3]; + vmVector3 v = src; + storeXYZ(v, tmp); + p[0] = tmp[0]; + p[1] = tmp[1]; + p[2] = tmp[2]; +} + + +static SIMD_FORCE_INLINE vmVector3 btReadVector3(const float* p) +{ + vmVector3 v; + loadXYZ(v, p); + return v; +} + +static SIMD_FORCE_INLINE vmQuat btReadQuat(const float* p) +{ + vmQuat vq; + loadXYZW(vq, p); + return vq; +} + +static SIMD_FORCE_INLINE void btStoreVector3(const vmVector3 &src, float* p) +{ + vmVector3 v = src; + storeXYZ(v, p); +} + + + + +class btPersistentManifold; + +enum { + PFX_CONSTRAINT_SOLVER_CMD_SETUP_SOLVER_BODIES, + PFX_CONSTRAINT_SOLVER_CMD_SETUP_CONTACT_CONSTRAINTS, + PFX_CONSTRAINT_SOLVER_CMD_WRITEBACK_APPLIED_IMPULSES_CONTACT_CONSTRAINTS, + PFX_CONSTRAINT_SOLVER_CMD_SETUP_JOINT_CONSTRAINTS, + PFX_CONSTRAINT_SOLVER_CMD_SOLVE_CONSTRAINTS, + PFX_CONSTRAINT_SOLVER_CMD_POST_SOLVER +}; + + +struct PfxSetupContactConstraintsIO { + PfxConstraintPair *offsetContactPairs; + uint32_t numContactPairs1; + btPersistentManifold* offsetContactManifolds; + btConstraintRow* offsetContactConstraintRows; + class TrbState *offsetRigStates; + struct PfxSolverBody *offsetSolverBodies; + uint32_t numRigidBodies; + float separateBias; + float timeStep; + class btCriticalSection* criticalSection; +}; + + + +struct PfxSolveConstraintsIO { + PfxParallelGroup *contactParallelGroup; + PfxParallelBatch *contactParallelBatches; + PfxConstraintPair *contactPairs; + uint32_t numContactPairs; + btPersistentManifold *offsetContactManifolds; + btConstraintRow* offsetContactConstraintRows; + PfxParallelGroup *jointParallelGroup; + PfxParallelBatch *jointParallelBatches; + PfxConstraintPair *jointPairs; + uint32_t numJointPairs; + struct btSolverConstraint* offsetSolverConstraints; + TrbState *offsetRigStates1; + PfxSolverBody *offsetSolverBodies; + uint32_t numRigidBodies; + uint32_t iteration; + + uint32_t taskId; + + class btBarrier* barrier; + +}; + +struct PfxPostSolverIO { + TrbState *states; + PfxSolverBody *solverBodies; + uint32_t numRigidBodies; +}; + +ATTRIBUTE_ALIGNED16(struct) btConstraintSolverIO { + uint8_t cmd; + union { + PfxSetupContactConstraintsIO setupContactConstraints; + PfxSolveConstraintsIO solveConstraints; + PfxPostSolverIO postSolver; + }; + + //SPU only + uint32_t barrierAddr2; + uint32_t criticalsectionAddr2; + uint32_t maxTasks1; +}; + + + + +void SolverThreadFunc(void* userPtr,void* lsMemory); +void* SolverlsMemoryFunc(); +///The btParallelConstraintSolver performs computations on constraint rows in parallel +///Using the cross-platform threading it supports Windows, Linux, Mac OSX and PlayStation 3 Cell SPUs +class btParallelConstraintSolver : public btSequentialImpulseConstraintSolver +{ + +protected: + struct btParallelSolverMemoryCache* m_memoryCache; + + class btThreadSupportInterface* m_solverThreadSupport; + + struct btConstraintSolverIO* m_solverIO; + class btBarrier* m_barrier; + class btCriticalSection* m_criticalSection; + + +public: + + btParallelConstraintSolver(class btThreadSupportInterface* solverThreadSupport); + + virtual ~btParallelConstraintSolver(); + + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc,btDispatcher* dispatcher); + +}; + + + +#endif //__BT_PARALLEL_CONSTRAINT_SOLVER_H \ No newline at end of file diff --git a/test_data/sample_files/build.bat b/test_data/sample_files/build.bat new file mode 100644 index 0000000..fab46ac --- /dev/null +++ b/test_data/sample_files/build.bat @@ -0,0 +1,11 @@ +@echo off + +pushd ..\build +cl "..\test\With (Parens).cpp" /FC +popd + +del ..\build\* /F /Q + +REM do this to prevent the directory from becoming empty which leads to issues +copy "..\test\With (Parens).cpp" ..\build\* + diff --git a/test_data/sample_files/cleanme.cpp b/test_data/sample_files/cleanme.cpp new file mode 100644 index 0000000..24a4fc0 --- /dev/null +++ b/test_data/sample_files/cleanme.cpp @@ -0,0 +1,13 @@ +/* + * YOUR INFO HERE! + */ + + tabbed_word; + +untabbed_word; + +struct{}; +pace0things; +alltogether + + tabbed_word2; diff --git a/test_data/sample_files/config.4coder b/test_data/sample_files/config.4coder new file mode 100644 index 0000000..d6c4274 --- /dev/null +++ b/test_data/sample_files/config.4coder @@ -0,0 +1,46 @@ +// Command Mapping +// "" - Leave the bindings unaltered from their startup value. +// "choose" - Ask 4coder to choose based on platform. +// "default" - Use the default keybindings 4coder has always had. +// "mac-4coder-like" - Use keybindings for Mac similar to 4coder's bindings on other platforms. +// "mac-default" - Use keybindings similar to those found in other Mac applications. +// <my-own-string> - If you use the custom layer to make a named mapping you can use that here too. +mapping = ""; + +// Code Wrapping +enable_code_wrapping = true; +automatically_adjust_wrapping = true; +default_wrap_width = 672; +default_min_base_width = 550; + +// This only applies to code files in code-wrapping mode. +// Plain text and code files without virtual-whitespace will not be effected. +automatically_indent_text_on_save = true; + +// When set to true, all unsaved changes will be saved on a build. +automatically_save_changes_on_build = true; + +// Theme +default_theme_name = "4coder"; +default_font_name = "Liberation Mono"; + +// User +user_name = "NAME"; + +// Extensions +treat_as_code = ".cpp.c.hpp.h.cc.cs.java.rs.glsl.m"; + +// Load project on startup +automatically_load_project = false; + +// Keyboard AltGr setting +lalt_lctrl_is_altgr = false; + +// Project setup configuration +default_compiler_bat = "cl"; +default_flags_bat = "-GR- -EHa- -nologo -FC -Zi"; +default_compiler_sh = "g++"; +default_flags_sh = "-g"; + +// Error test +user_name = "NAME" diff --git a/test_data/sample_files/emptyfile.txt b/test_data/sample_files/emptyfile.txt new file mode 100644 index 0000000..e69de29 diff --git a/test_data/sample_files/hello.cs b/test_data/sample_files/hello.cs new file mode 100644 index 0000000..19726d4 --- /dev/null +++ b/test_data/sample_files/hello.cs @@ -0,0 +1,135 @@ + +// A Hello World! program in C#. +using System; +namespace HelloWorld +{ + class Hello + { + static void Main() + { + Console.WriteLine("Hello World!"); + + // Keep the console window open in debug mode. + Console.WriteLine("Press any key to exit."); + Console.ReadKey(); + } + } + + using System.Collections; + + // Describes a book in the book list: + public struct Book + { + public string Title; // Title of the book. + public string Author; // Author of the book. + public decimal Price; // Price of the book. + public bool Paperback; // Is it paperback? + + public Book(string title, string author, decimal price, bool paperBack) + { + Title = title; + Author = author; + Price = price; + Paperback = paperBack; + } + } + + // Declare a delegate type for processing a book: + public delegate void ProcessBookDelegate(Book book); + + // Maintains a book database. + public class BookDB + { + // List of all books in the database: + ArrayList list = new ArrayList(); + + // Add a book to the database: + public void AddBook(string title, string author, decimal price, bool paperBack) + { + list.Add(new Book(title, author, price, paperBack)); + } + + // Call a passed-in delegate on each paperback book to process it: + public void ProcessPaperbackBooks(ProcessBookDelegate processBook) + { + foreach (Book b in list) + { + if (b.Paperback) + // Calling the delegate: + processBook(b); + } + } + } +} + +// Using the Bookstore classes: +namespace BookTestClient +{ + using Bookstore; + + // Class to total and average prices of books: + class PriceTotaller + { + int countBooks = 0; + decimal priceBooks = 0.0m; + + internal void AddBookToTotal(Book book) + { + countBooks += 1; + priceBooks += book.Price; + } + + internal decimal AveragePrice() + { + return priceBooks / countBooks; + } + } + + // Class to test the book database: + class Test + { + // Print the title of the book. + static void PrintTitle(Book b) + { + Console.WriteLine(" {0}", b.Title); + } + + // Execution starts here. + static void Main() + { + BookDB bookDB = new BookDB(); + + // Initialize the database with some books: + AddBooks(bookDB); + + // Print all the titles of paperbacks: + Console.WriteLine("Paperback Book Titles:"); + // Create a new delegate object associated with the static + // method Test.PrintTitle: + bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle)); + + // Get the average price of a paperback by using + // a PriceTotaller object: + PriceTotaller totaller = new PriceTotaller(); + // Create a new delegate object associated with the nonstatic + // method AddBookToTotal on the object totaller: + bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal)); + Console.WriteLine("Average Paperback Book Price: ${0:#.##}", + totaller.AveragePrice()); + } + + // Initialize the book database with some test books: + static void AddBooks(BookDB bookDB) + { + bookDB.AddBook("The C Programming Language", + "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true); + bookDB.AddBook("The Unicode Standard 2.0", + "The Unicode Consortium", 39.95m, true); + bookDB.AddBook("The MS-DOS Encyclopedia", + "Ray Duncan", 129.95m, false); + bookDB.AddBook("Dogbert's Clues for the Clueless", + "Scott Adams", 12.00m, true); + } + } +} + diff --git a/test_data/sample_files/hello_wörld.txt b/test_data/sample_files/hello_wörld.txt new file mode 100644 index 0000000..e69de29 diff --git a/test_data/sample_files/home_no_extension b/test_data/sample_files/home_no_extension new file mode 100644 index 0000000..4c88757 --- /dev/null +++ b/test_data/sample_files/home_no_extension @@ -0,0 +1,34 @@ +<html lang="en-US"> +<head> +<link rel='shortcut icon' type='image/x-icon' href='4coder_icon.ico' /> +<title>4coder Home + + + +
+

+ +

+

4coder is a code editor that first and foremost targets the needs of C/C++ developers so they can be as efficient as possible. Here you can learn about the features of 4coder, how to get started with 4coder, and how to get the absolute most out of 4coder.

+

If you cannot find what you are looking for please contact editor@4coder.net with questions.

+

Get an alpha build by supporting development

+

The official 4coder feature list

+

The official 4coder road map

+

The official documentation for the 4coder customization system

+

Using 4coder tutorials

+

+

+

+
+ + \ No newline at end of file diff --git a/test_data/sample_files/indent_bug_test.cpp b/test_data/sample_files/indent_bug_test.cpp new file mode 100644 index 0000000..273fc5b --- /dev/null +++ b/test_data/sample_files/indent_bug_test.cpp @@ -0,0 +1,41 @@ + + +extern"C"int +foo(){ + call(x_y_z(a, + b(foo_loo- + bar_lor), + c)); + { + //Foolootoo + call(a,b,(c- + d) + ); + PAREN_THING(X, + Y){ + zoo(a,call(b,c,d) + ); + } + more_calls(); + } + more_calls() + + other_things + +#define more_other_things (other_things << 2)|(other_things >> 30) + more_other_things + + other_things^ + more_other_things; + + /* + Stuff + */ + + { + Foo() + .Blah() + // + .Flub(); + } +} + + + diff --git a/test_data/sample_files/junk.cpp b/test_data/sample_files/junk.cpp new file mode 100644 index 0000000..f427559 --- /dev/null +++ b/test_data/sample_files/junk.cpp @@ -0,0 +1,12 @@ + +// testing junk + +@ + +#error thing one +# error thing two + +word\ +thing + +stuff diff --git a/test_data/sample_files/lexer_test.cpp b/test_data/sample_files/lexer_test.cpp new file mode 100644 index 0000000..f40a337 --- /dev/null +++ b/test_data/sample_files/lexer_test.cpp @@ -0,0 +1,57 @@ +/* + * THIS TESTS: + * - block comment, line comment, comment merging + * - preprocessor body, preprocessor include + * - keyword detection + * - integer constant, with and without suffix + * - hex form integer constant + * - floating constant, with and without suffix + * - float constant with scientific notation + * - boolean constant + * - string constant + * - more than 1 kb worth of token data + */ +// Each line comment and block comment is emitted separately +// but then merged in the cpp_push_token function + +#define PREPROCESSOR +#include + +#define MULTI_LINE\ + a( \ + b+x, c+x, \ + ) + +#define PP_OPS(n) printf(#n" %d\n", \ + counter_##n) + +extern void* foob(); +static inline void fbar(); + +#if (contets - here + labeled == as pp_body) + +int main(){ + // constant expressions + float x = 3.5883f; + char c = 0x34; + foob(.23e-10, -32ll, -32.); + int d = 5; + if (true){ + char stuff[] = "gotta get that string constant \\ with \" crazieness in there \n"; + } + + // filler to put the memory requirement for the tokens over 1KB + int x,y,z,h; + x=(y*x)+(-z*y)+h; + h+=x<>=x?z:h; + return 0; +} + +#endif + +#include "teststuff.h" + + + diff --git a/test_data/sample_files/lexer_test2.cpp b/test_data/sample_files/lexer_test2.cpp new file mode 100644 index 0000000..8bf7e05 --- /dev/null +++ b/test_data/sample_files/lexer_test2.cpp @@ -0,0 +1,9 @@ +// Force it to finish gracefully with an un-closed block comment + + +struct X{ + int Y; +}; + +/* +words \ No newline at end of file diff --git a/test_data/sample_files/lexer_test3.cpp b/test_data/sample_files/lexer_test3.cpp new file mode 100644 index 0000000..1222461 --- /dev/null +++ b/test_data/sample_files/lexer_test3.cpp @@ -0,0 +1,7 @@ +/* +Check potential issue with preprocessor directive parsing +*/ + +#ifinternal && defined(stuff) + +#if internal && defined(stuff) diff --git a/test_data/sample_files/math.txt b/test_data/sample_files/math.txt new file mode 100644 index 0000000..e569c0d --- /dev/null +++ b/test_data/sample_files/math.txt @@ -0,0 +1,54 @@ +Defining a "real number" + +------------------------ +Natural Numbers (granted) +1 2 3 4 5 ... + +Integers? +"I want to close my number set under the operation of subtraction." + +(a1, a2) - (b1, b2) = (a1 + b2, a2 + b1) + +a = (3, 4); b = (5, 6); + +(a1, a2) equals (b1, b2) if a1 + b2 = a2 + b1 + +Rationals? +"I want to close my number set under the operation of division." + +(a1, a2) / (b1, b2) = (a1*b2, a2*b1) + +(a1, a2) equals (b1, b2) if a1*b2 = a2*b1 + +Reals? +My examples of definitions that aren't solid: +~ Anything/any point you can plot on the number line +~ Dedekind (spelling?) cut + +"I want to close my number set under limits." +By limit I mean the n-ε-definition + +(a1, a2, a3, a4, ...) + +( 1, 0, 1, 0, ...) +(1/2, 1/2, 1/2, 1/2, ...) +(1/2, 3/4, 7/8, 15/16, ...) + +Start with the range (1,2) +Bisect the range - x - this is the next element of the series +Square x +if (x > 2) update the range to (lower,x) +else update the range to (x,upper) +limit (3/2, 5/4, 11/8, 21/16, ...) = y + +Cauchy sequence - a sequence given any ε there is an n +such that for any m,r >= n |am - ar| < ε + +"Close under limits." means +"We want a set of numbers where every Cauchy sequence converges +to a number in the set." + +(a1, a2, a3, a4, ...) + +a + b = (a1, a2, a3, a4, ...) + (b1, b2, b3, b4, ...) + = (a1 + b1, a2 + b2, a3 + b3, ...) \ No newline at end of file diff --git a/test_data/sample_files/mod_markov.c b/test_data/sample_files/mod_markov.c new file mode 100644 index 0000000..21196f1 --- /dev/null +++ b/test_data/sample_files/mod_markov.c @@ -0,0 +1,697 @@ +#define STB_SB_MMAP +#include "stb_sb.h" +#include "module.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" + +static bool markov_init (const IRCCoreCtx*); +static void markov_quit (void); +static void markov_join (const char*, const char*); +static void markov_cmd (const char*, const char*, const char*, int); +static void markov_msg (const char*, const char*, const char*); +static void markov_mod_msg(const char* sender, const IRCModMsg* msg); +static bool markov_save (FILE*); + +enum { MARKOV_SAY, MARKOV_ASK, MARKOV_INTERVAL, MARKOV_STATUS }; + +const IRCModuleCtx irc_mod_ctx = { + .name = "markov", + .desc = "Says incomprehensible stuff", + .flags = IRC_MOD_DEFAULT, + .on_init = &markov_init, + .on_quit = &markov_quit, + .on_cmd = &markov_cmd, + .on_msg = &markov_msg, + .on_join = &markov_join, + .on_save = &markov_save, + .on_mod_msg = &markov_mod_msg, + .commands = DEFINE_CMDS ( + [MARKOV_SAY] = CONTROL_CHAR "say", + [MARKOV_ASK] = CONTROL_CHAR "ask", + [MARKOV_INTERVAL] = CONTROL_CHAR "interval " CONTROL_CHAR "gap", + [MARKOV_STATUS] = CONTROL_CHAR "status" + ) +}; + +static const IRCCoreCtx* ctx; + +typedef uint32_t word_idx_t; + +struct MarkovLinkKey_ { + uint32_t val_idx; + word_idx_t word_idx_1 : 24; + word_idx_t word_idx_2 : 24; +} __attribute__ ((packed)); +typedef struct MarkovLinkKey_ MarkovLinkKey; + +typedef struct { + word_idx_t word_idx : 24; + uint8_t count; + uint32_t next; +} MarkovLinkVal; + +static char* word_mem; + +static MarkovLinkKey* chain_keys; +static MarkovLinkVal* chain_vals; + +static char rng_state_mem[256]; +static struct random_data rng_state; + +static regex_t url_regex; + +static size_t max_chain_len = 16; +static size_t msg_chance = 150; + +static word_idx_t start_sym_idx; +static word_idx_t end_sym_idx; + +static uint32_t recent_hashes[128]; +static size_t hash_idx; + +static char** markov_nicks; + +static uint32_t markov_rand(uint32_t limit){ + int32_t x; + + do { + random_r(&rng_state, &x); + } while (x >= (RAND_MAX - RAND_MAX % limit)); + + return x % limit; +} + +static bool find_word(const char* word, size_t word_len, word_idx_t* index){ + char* w = alloca(word_len + 2); + w[0] = 0; + memcpy(w + 1, word, word_len + 1); + + char* p = memmem(word_mem, sbmm_count(word_mem), w, word_len + 2); + if(p){ + *index = (p + 1) - word_mem; + } + + return p != NULL; +} + +static word_idx_t find_or_add_word(const char* word, size_t word_len){ + word_idx_t index; + if(!find_word(word, word_len, &index)){ + char* p = memcpy(sbmm_add(word_mem, word_len+1), word, word_len+1); + index = p - word_mem; + } + return index; +} + +static ssize_t find_key_idx(word_idx_t a, word_idx_t b){ + for(const MarkovLinkKey* k = chain_keys; k < sbmm_end(chain_keys); ++k){ + if(k->word_idx_1 == a && k->word_idx_2 == b){ + return k - chain_keys; + } + } + return -1; +} + +static const char* bad_end_words[] = { + "and", + "the", + "a", + "as", + "if", + "i", + ",", + "/", + NULL +}; + +static size_t markov_gen(char* buffer, size_t buffer_len){ + if(!buffer_len) return 0; + *buffer = 0; + + ssize_t key_idx = find_key_idx(start_sym_idx, start_sym_idx); + assert(key_idx != -1); + + MarkovLinkKey* key = chain_keys + key_idx; + + int chain_len = 1 + markov_rand(max_chain_len); + int links = 0; + bool should_end = false; + + do { + size_t total = 0; + size_t end_count = 0; + + MarkovLinkVal* val = chain_vals + key->val_idx; + do { + if(val->word_idx == end_sym_idx) end_count = val->count; + total += val->count; + } while(val->next != -1 && (val = chain_vals + val->next)); + + assert(total); + ssize_t count = markov_rand(total); + + should_end = + (links >= chain_len && end_count > (total / 2)) || + (links >= chain_len * 1.5f && end_count) || + (links >= chain_len * 2.0f); + + val = chain_vals + key->val_idx; + while((count -= val->count) >= 0){ + val = chain_vals + val->next; + } + + if(val->word_idx == end_sym_idx){ + break; + } + + const char* word = word_mem + val->word_idx; + + for(const char** c = bad_end_words; *c; ++c){ + if(strcmp(*c, word) == 0){ + should_end = false; + break; + } + } + + if(*buffer && strcmp(word, ",") != 0){ + inso_strcat(buffer, buffer_len, " "); + } + inso_strcat(buffer, buffer_len, word); + + ssize_t new_key_idx = find_key_idx(key->word_idx_2, val->word_idx); + assert(new_key_idx > 0); + + key = chain_keys + new_key_idx; + + } while(!should_end); + + return strlen(buffer); +} + +static uint32_t markov_hash(const char* str, size_t len){ + uint32_t hash = 9229; + for(int i = 0; i < len; ++i){ + hash *= 31U; + hash += str[i]; + } + return hash; +} + +static void markov_add_hash(const char* str, size_t len){ + recent_hashes[hash_idx] = markov_hash(str, len); + hash_idx = (hash_idx + 1) % ARRAY_SIZE(recent_hashes); +} + +static bool markov_check_dup(const char* str, size_t len){ + uint32_t hash = markov_hash(str, len); + for(int i = 0; i < ARRAY_SIZE(recent_hashes); ++i){ + if(recent_hashes[i] == hash) return true; + } + return false; +} + +static const char* markov_get_punct(){ + + size_t val = markov_rand(100); + + if(val < 67) return "."; + if(val < 72) return "?"; + if(val < 85) return "!"; + if(val < 97) return "..."; + if(val < 98) return "‽"; + if(val < 99) return ". FailFish"; + + return ". Kappa"; +} + +static bool markov_gen_formatted(char* msg, size_t msg_len){ + int num_sentences = markov_rand(10) < 8 ? 1 : 2; + + while(num_sentences--){ + int attempts = 0; + + size_t buff_len = msg_len; + char* buff = alloca(msg_len); + + size_t tmp_len; + + do { + tmp_len = markov_gen(buff, buff_len); + + if(*buff == ','){ + tmp_len -= 2; + memmove(buff, buff + 2, tmp_len); + } + } while(attempts++ < 5 && markov_check_dup(buff, tmp_len)); + + buff_len = tmp_len; + + if(attempts >= 5){ + puts("Couldn't get a good message, giving up."); + return false; + } + + markov_add_hash(buff, buff_len); + + *buff = toupper(*buff); + memcpy(msg, buff, buff_len); + msg[buff_len] = 0; + + msg += buff_len; + msg_len -= buff_len; + + if(num_sentences){ + int written = 0; + written = INSO_MAX(written, inso_strcat(msg, msg_len, markov_get_punct())); + written = INSO_MAX(written, inso_strcat(msg, msg_len, " ")); + + msg += written; + msg_len -= written; + } + } + + inso_strcat(msg, msg_len, markov_get_punct()); + + return true; +} + +static void markov_load(){ + gzFile f = gzopen(ctx->get_datafile(), "rb"); + uint32_t word_size = 0, key_size = 0, val_size = 0; + + if(gzread(f, &word_size, sizeof(word_size)) < 1) goto out; + if(gzread(f, &key_size, sizeof(key_size)) < 1) goto out; + if(gzread(f, &val_size, sizeof(val_size)) < 1) goto out; + + if(gzread(f, sbmm_add(word_mem, word_size), word_size) < word_size) goto out; + if(gzread(f, sbmm_add(chain_keys, key_size), sizeof(MarkovLinkKey) * key_size) < key_size) goto out; + if(gzread(f, sbmm_add(chain_vals, val_size), sizeof(MarkovLinkVal) * val_size) < val_size) goto out; + + gzclose(f); + return; + + out: + puts("markov: couldn't read file."); + gzclose(f); +} + +static bool markov_save(FILE* file){ + uint32_t word_size = sbmm_count(word_mem) - 1; + uint32_t key_size = sbmm_count(chain_keys); + uint32_t val_size = sbmm_count(chain_vals); + + gzFile f = gzdopen(dup(fileno(file)), "wb"); + + if(gzwrite(f, &word_size, sizeof(word_size)) < 1) goto out; + if(gzwrite(f, &key_size, sizeof(key_size)) < 1) goto out; + if(gzwrite(f, &val_size, sizeof(val_size)) < 1) goto out; + + if(gzwrite(f, word_mem + 1, word_size) < word_size) goto out; + if(gzwrite(f, chain_keys, sizeof(MarkovLinkKey) * key_size) < key_size) goto out; + if(gzwrite(f, chain_vals, sizeof(MarkovLinkVal) * val_size) < val_size) goto out; + + gzclose(f); + return true; + + out: + puts("markov: error saving file."); + gzclose(f); + return false; +} + +static bool markov_init(const IRCCoreCtx* _ctx){ + ctx = _ctx; + + unsigned int seed = rand(); + + int fd = open("/dev/urandom", O_RDONLY); + if(fd != -1){ + if(read(fd, &seed, sizeof(seed)) == -1){ + perror("markov_init: read"); + } + close(fd); + } + + initstate_r(seed, rng_state_mem, sizeof(rng_state_mem), &rng_state); + setstate_r(rng_state_mem, &rng_state); + + sbmm_push(word_mem, 0); + + regcomp(&url_regex, "(www\\.|https?:\\/\\/|\\.com|\\.[a-zA-Z]\\/)", REG_ICASE | REG_EXTENDED | REG_NOSUB); + + markov_load(); + + start_sym_idx = find_or_add_word("^", 1); + end_sym_idx = find_or_add_word("$", 1); + + return true; +} + +static void markov_join(const char* chan, const char* name){ + + if(strcasecmp(name, ctx->get_username()) == 0) return; + + for(int i = 0; i < sb_count(markov_nicks); ++i){ + if(strcasecmp(name, markov_nicks[i]) == 0){ + return; + } + } + sb_push(markov_nicks, strdup(name)); +} + +static void markov_send(const char* chan){ + char buffer[256]; + if(!markov_gen_formatted(buffer, sizeof(buffer))) return; + ctx->send_msg(chan, "%s", buffer); +} + +static void markov_reply(const char* chan, const char* nick){ + char buffer[256]; + if(!markov_gen_formatted(buffer, sizeof(buffer))) return; + ctx->send_msg(chan, "@%s: %s", nick, buffer); +} + +static void markov_ask(const char* chan){ + char buffer[256]; + if(!markov_gen_formatted(buffer, sizeof(buffer))) return; + + size_t len = strlen(buffer); + if(len && ispunct(buffer[len-1])){ + buffer[len-1] = '?'; + } else if(sizeof(buffer) - len > 1){ + buffer[len] = '?'; + buffer[len+1] = 0; + } + ctx->send_msg(chan, "Q: %s", buffer); +} + +static const int say_cooldown = 300; +static time_t last_say; + +static void markov_cmd(const char* chan, const char* name, const char* arg, int cmd){ + time_t now = time(0); + + bool admin = inso_is_admin(ctx, name); + + switch(cmd){ + + case MARKOV_SAY: { + if(admin || now - last_say >= say_cooldown){ + markov_send(chan); + last_say = now; + } + } break; + + case MARKOV_ASK: { + if(admin || now - last_say >= say_cooldown){ + markov_ask(chan); + last_say = now; + } + } break; + + case MARKOV_INTERVAL: { + if(!admin) break; + + if(*arg++){ + int chance = strtoul(arg, NULL, 0); + if(chance != 0){ + msg_chance = chance; + } + } + + ctx->send_msg(chan, "%s: interval = %zu.", name, msg_chance); + } break; + + case MARKOV_STATUS: { + if(!admin) break; + + ctx->send_msg( + chan, + "%s: markov status: %d keys, %d chains, %dKB word mem.", + name, + sbmm_count(chain_keys), + sbmm_count(chain_vals), + sbmm_count(word_mem) / 1024 + ); + + } break; + } + +} + +static void markov_replace(char** msg, const char* from, const char* to){ + size_t from_len = strlen(from); + size_t to_len = strlen(to); + size_t msg_len = sb_count(*msg); + + char* p; + size_t off = 0; + + while((p = strstr(*msg + off, from))){ + + off = p - *msg; + + if(to_len > from_len){ + memset(sb_add(*msg, to_len - from_len), 0, to_len - from_len); + } else { + stb__sbn(*msg) -= (from_len - to_len); + } + + p = *msg + off; + + const char* end_p = *msg + msg_len; + memmove(p + to_len, p + from_len, end_p - (p + from_len)); + memcpy(p, to, to_len); + + off += to_len; + + msg_len += (to_len - from_len); + } + +} + +static void markov_add(word_idx_t indices[static 3]){ + + ssize_t key_idx = find_key_idx(indices[0], indices[1]); + + if(key_idx == -1){ + + MarkovLinkVal val = { + .word_idx = indices[2], + .count = 1, + .next = -1 + }; + sbmm_push(chain_vals, val); + + MarkovLinkKey key = { + .word_idx_1 = indices[0], + .word_idx_2 = indices[1], + .val_idx = sbmm_count(chain_vals) - 1 + }; + sbmm_push(chain_keys, key); + + } else { + bool found = false; + size_t last_idx = key_idx; + + for(uint32_t i = chain_keys[key_idx].val_idx; i != -1; i = chain_vals[i].next){ + if(chain_vals[i].word_idx == indices[2]){ + if(chain_vals[i].count < UCHAR_MAX) ++chain_vals[i].count; + found = true; + break; + } + + last_idx = i; + } + + if(!found){ + MarkovLinkVal val = { + .word_idx = indices[2], + .count = 1, + .next = -1 + }; + sbmm_push(chain_vals, val); + chain_vals[last_idx].next = sbmm_count(chain_vals) - 1; + } + } +} + +static const char* ignores[] = { + "hmh_bot", + "hmd_bot", + "drakebot_", + NULL +}; + +static const char* skip_words[] = { "p", "d", "b", "o", "-p", "-d", "-b", "-o", NULL }; + +static void markov_msg(const char* chan, const char* name, const char* _msg){ + + markov_join(chan, name); + + if(*_msg == '!' || *_msg == '\\'){ + puts("skipping command."); + return; + } + + if(regexec(&url_regex, _msg, 0, NULL, 0) == 0){ + puts("skipping url."); + return; + } + + for(const char** n = ignores; *n; ++n){ + if(strcasecmp(*n, name) == 0){ + return; + } + } + + size_t msg_len = strlen(_msg); + char* msg = NULL; + memcpy(sb_add(msg, msg_len + 1), _msg, msg_len + 1); + + for(char* c = msg; c < sb_end(msg); ++c){ + *c = tolower(*c); + } + + const char* bot_name = ctx->get_username(); + size_t bot_name_len = strlen(bot_name); + const char* name_pats[] = { "@%s", "%s:", "%s," }; + char name_buf[256]; + bool found_name = false; + + assert(bot_name_len + 2 < sizeof(name_buf)); + for(size_t i = 0; i < ARRAY_SIZE(name_pats); ++i){ + snprintf(name_buf, sizeof(name_buf), name_pats[i], bot_name); + if(strcasestr(msg, name_buf)){ + found_name = true; + break; + } + } + + if(found_name && markov_rand(3)){ + markov_reply(chan, name); + } + + if(*msg == '@') *msg = ' '; + + for(char* p = msg; *p; ++p){ + if(*p < ' ' || *p >= 127) *p = ' '; + else if(*p == '$') *p = '@'; + } + + markov_replace(&msg, ". ", " $ "); + markov_replace(&msg, "! ", " $ "); + markov_replace(&msg, "? ", " $ "); + + markov_replace(&msg, ",", " , "); + + for(char* p = msg; *p; ++p){ + if(strchr(".!?@:;`^(){}[]\"", *p)) *p = ' '; + } + + markov_replace(&msg, " ", " "); + + word_idx_t words[] = { start_sym_idx, start_sym_idx, 0 }; + + char* state = NULL; + char* word = strtok_r(msg, " ", &state); + + printf("Adding:"); + + for(; word; word = strtok_r(NULL, " ", &state)){ + + bool skip = false; + for(const char** c = skip_words; *c; ++c){ + if(strcmp(word, *c) == 0){ + skip = true; + break; + } + } + + if(skip) continue; + + printf(" [%s]", word); + + size_t len = strlen(word); + word_idx_t idx = 0; + + if(len > 24){ + len = 9; + idx = find_or_add_word("something", len); + } else { + //TODO: remove ++ -- + + for(char** c = markov_nicks; c < sb_end(markov_nicks); ++c){ + if(strcasecmp(word, *c) == 0){ + skip = true; + break; + } + } + + if(skip) continue; + + idx = find_or_add_word(word, len); + } + + if(idx == end_sym_idx && words[1] == start_sym_idx) continue; + + if(idx == words[1] && words[1] == words[0]) continue; + + words[2] = idx; + markov_add(words); + + if(idx == end_sym_idx){ + words[0] = start_sym_idx; + words[1] = start_sym_idx; + } else { + words[0] = words[1]; + words[1] = words[2]; + } + } + + puts("."); + + words[2] = end_sym_idx; + if(words[1] != start_sym_idx) markov_add(words); + + if(markov_rand(msg_chance) == 0){ + markov_send(chan); + } + + sb_free(msg); +} + +static void markov_mod_msg(const char* sender, const IRCModMsg* msg){ + if(strcmp(msg->cmd, "markov_gen") == 0){ + + char* buffer = malloc(256); + if(!markov_gen(buffer, 256)){ + free(buffer); + return; + } + + msg->callback((intptr_t)buffer, msg->cb_arg); + } +} + +static void markov_quit(void){ + sbmm_free(word_mem); + sbmm_free(chain_keys); + sbmm_free(chain_vals); + + for(int i = 0; i < sb_count(markov_nicks); ++i){ + free(markov_nicks[i]); + } + sb_free(markov_nicks); + + regfree(&url_regex); +} diff --git a/test_data/sample_files/newtest.txt b/test_data/sample_files/newtest.txt new file mode 100644 index 0000000..38983eb --- /dev/null +++ b/test_data/sample_files/newtest.txt @@ -0,0 +1,5 @@ +/* +New File +*/ + + diff --git a/test_data/sample_files/nuklear.h b/test_data/sample_files/nuklear.h new file mode 100644 index 0000000..40e6d16 --- /dev/null +++ b/test_data/sample_files/nuklear.h @@ -0,0 +1,21506 @@ +/* + Nuklear - v1.152 - public domain + no warrenty implied; use at your own risk. + authored from 2015-2016 by Micha Mettke + +ABOUT: + This is a minimal state immediate mode graphical user interface single header + toolkit written in ANSI C and licensed under public domain. + It was designed as a simple embeddable user interface for application and does + not have any dependencies, a default renderbackend or OS window and input handling + but instead provides a very modular library approach by using simple input state + for input and draw commands describing primitive shapes as output. + So instead of providing a layered library that tries to abstract over a number + of platform and render backends it only focuses on the actual UI. + +VALUES: + - Immediate mode graphical user interface toolkit + - Single header library + - Written in C89 (A.K.A. ANSI C or ISO C90) + - Small codebase (~17kLOC) + - Focus on portability, efficiency and simplicity + - No dependencies (not even the standard library if not wanted) + - Fully skinnable and customizable + - Low memory footprint with total memory control if needed or wanted + - UTF-8 support + - No global or hidden state + - Customizable library modules (you can compile and use only what you need) + - Optional font baker and vertex buffer output + +USAGE: + This library is self contained in one single header file and can be used either + in header only mode or in implementation mode. The header only mode is used + by default when included and allows including this header in other headers + and does not contain the actual implementation. + + The implementation mode requires to define the preprocessor macro + NK_IMPLEMENTATION in *one* .c/.cpp file before #includeing this file, e.g.: + + #define NK_IMPLEMENTATION + #include "nuklear.h" + + Also optionally define the symbols listed in the section "OPTIONAL DEFINES" + below in header and implementation mode if you want to use additional functionality + or need more control over the library. + IMPORTANT: Every time you include "nuklear.h" you have to define the same flags. + This is very important not doing it either leads to compiler errors + or even worse stack corruptions. + +FEATURES: + - Absolutely no platform dependend code + - Memory management control ranging from/to + - Ease of use by allocating everything from the standard library + - Control every byte of memory inside the library + - Font handling control ranging from/to + - Use your own font implementation for everything + - Use this libraries internal font baking and handling API + - Drawing output control ranging from/to + - Simple shapes for more high level APIs which already have drawing capabilities + - Hardware accessible anti-aliased vertex buffer output + - Customizable colors and properties ranging from/to + - Simple changes to color by filling a simple color table + - Complete control with ability to use skinning to decorate widgets + - Bendable UI library with widget ranging from/to + - Basic widgets like buttons, checkboxes, slider, ... + - Advanced widget like abstract comboboxes, contextual menus,... + - Compile time configuration to only compile what you need + - Subset which can be used if you do not want to link or use the standard library + - Can be easily modified to only update on user input instead of frame updates + +OPTIONAL DEFINES: + NK_PRIVATE + If defined declares all functions as static, so they can only be accessed + for the file that creates the implementation + + NK_INCLUDE_FIXED_TYPES + If defined it will include header for fixed sized types + otherwise nuklear tries to select the correct type. If that fails it will + throw a compiler error and you have to select the correct types yourself. + If used needs to be defined for implementation and header + + NK_INCLUDE_DEFAULT_ALLOCATOR + if defined it will include header and provide additional functions + to use this library without caring for memory allocation control and therefore + ease memory management. + Adds the standard library with malloc and free so don't define if you + don't want to link to the standard library + If used needs to be defined for implementation and header + + NK_INCLUDE_STANDARD_IO + if defined it will include header and provide + additional functions depending on file loading. + Adds the standard library with fopen, fclose,... so don't define this + if you don't want to link to the standard library + If used needs to be defined for implementation and header + + NK_INCLUDE_STANDARD_VARARGS + if defined it will include header and provide + additional functions depending on variable arguments + Adds the standard library with va_list and so don't define this if + you don't want to link to the standard library + If used needs to be defined for implementation and header + + NK_INCLUDE_VERTEX_BUFFER_OUTPUT + Defining this adds a vertex draw command list backend to this + library, which allows you to convert queue commands into vertex draw commands. + This is mainly if you need a hardware accessible format for OpenGL, DirectX, + Vulkan, Metal,... + If used needs to be defined for implementation and header + + NK_INCLUDE_FONT_BAKING + Defining this adds the `stb_truetype` and `stb_rect_pack` implementation + to this library and provides font baking and rendering. + If you already have font handling or do not want to use this font handler + you don't have to define it. + If used needs to be defined for implementation and header + + NK_INCLUDE_DEFAULT_FONT + Defining this adds the default font: ProggyClean.ttf into this library + which can be loaded into a font atlas and allows using this library without + having a truetype font + Enabling this adds ~12kb to global stack memory + If used needs to be defined for implementation and header + + NK_INCLUDE_COMMAND_USERDATA + Defining this adds a userdata pointer into each command. Can be useful for + example if you want to provide custom shaders depending on the used widget. + Can be combined with the style structures. + If used needs to be defined for implementation and header + + NK_BUTTON_TRIGGER_ON_RELEASE + Different platforms require button clicks occuring either on buttons being + pressed (up to down) or released (down to up). + By default this library will react on buttons being pressed, but if you + define this it will only trigger if a button is released. + If used it is only required to be defined for the implementation part + + NK_ASSERT + If you don't define this, nuklear will use with assert(). + Adds the standard library so define to nothing of not wanted + If used needs to be defined for implementation and header + + NK_BUFFER_DEFAULT_INITIAL_SIZE + Initial buffer size allocated by all buffers while using the default allocator + functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't + want to allocate the default 4k memory then redefine it. + If used needs to be defined for implementation and header + + NK_MAX_NUMBER_BUFFER + Maximum buffer size for the conversion buffer between float and string + Under normal circumstances this should be more than sufficient. + If used needs to be defined for implementation and header + + NK_INPUT_MAX + Defines the max number of bytes which can be added as text input in one frame. + Under normal circumstances this should be more than sufficient. + If used it is only required to be defined for the implementation part + + NK_MEMSET + You can define this to 'memset' or your own memset implementation + replacement. If not nuklear will use its own version. + If used it is only required to be defined for the implementation part + + NK_MEMCOPY + You can define this to 'memcpy' or your own memcpy implementation + replacement. If not nuklear will use its own version. + If used it is only required to be defined for the implementation part + + NK_SQRT + You can define this to 'sqrt' or your own sqrt implementation + replacement. If not nuklear will use its own slow and not highly + accurate version. + If used it is only required to be defined for the implementation part + + NK_SIN + You can define this to 'sinf' or your own sine implementation + replacement. If not nuklear will use its own approximation implementation. + If used it is only required to be defined for the implementation part + + NK_COS + You can define this to 'cosf' or your own cosine implementation + replacement. If not nuklear will use its own approximation implementation. + If used it only needs to be define for the implementation not header + If used it is only required to be defined for the implementation part + + NK_STRTOD + You can define this to `strtod` or your own string to double conversion + implementation replacement. If not defined nuklear will use its own + imprecise and possibly unsafe version (does not handle nan or infinity!). + If used it is only required to be defined for the implementation part + + NK_DTOA + You can define this to `dtoa` or your own double to string conversion + implementation replacement. If not defined nuklear will use its own + imprecise and possibly unsafe version (does not handle nan or infinity!). + If used it is only required to be defined for the implementation part + + NK_VSNPRINTF + If you define `NK_INCLUDE_STANDARD_VARARGS` as well as `NK_INCLUDE_STANDARD_IO` + and want to be safe define this to `vsnprintf` on compilers supporting + later versions of C or C++. By default nuklear will check for your stdlib version + in C as well as compiler version in C++. if `vsnprintf` is available + it will define it to `vsnprintf` directly. If not defined and if you have + older versions of C or C++ it will be defined to `vsprintf` which is unsafe. + If used it is only required to be defined for the implementation part + + NK_BYTE + NK_INT16 + NK_UINT16 + NK_INT32 + NK_UINT32 + NK_SIZE_TYPE + NK_POINTER_TYPE + If you compile without NK_USE_FIXED_TYPE then a number of standard types + will be selected and compile time validated. If they are incorrect you can + define the correct types by overloading these type defines. + +CREDITS: + Developed by Micha Mettke and every direct or indirect contributor to the GitHub. + + Embeds stb_texedit, stb_truetype and stb_rectpack by Sean Barret (public domain) + Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license). + + Big thank you to Omar Cornut (ocornut@github) for his imgui library and + giving me the inspiration for this library, Casey Muratori for handmade hero + and his original immediate mode graphical user interface idea and Sean + Barret for his amazing single header libraries which restored my faith + in libraries and brought me to create some of my own. + +LICENSE: + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish and distribute this file as you see fit. +*/ +#ifndef NK_NUKLEAR_H_ +#define NK_NUKLEAR_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/* + * ============================================================== + * + * CONSTANTS + * + * =============================================================== + */ +#define NK_UNDEFINED (-1.0f) +#define NK_UTF_INVALID 0xFFFD /* internal invalid utf8 rune */ +#define NK_UTF_SIZE 4 /* describes the number of bytes a glyph consists of*/ +#ifndef NK_INPUT_MAX +#define NK_INPUT_MAX 16 +#endif +#ifndef NK_MAX_NUMBER_BUFFER +#define NK_MAX_NUMBER_BUFFER 64 +#endif +#ifndef NK_SCROLLBAR_HIDING_TIMEOUT +#define NK_SCROLLBAR_HIDING_TIMEOUT 4.0f +#endif + +/* + * ============================================================== + * + * HELPER + * + * =============================================================== + */ +#ifdef NK_PRIVATE +#define NK_API static +#else +#define NK_API extern +#endif + +#define NK_INTERN static +#define NK_STORAGE static +#define NK_GLOBAL static + +#define NK_FLAG(x) (1 << (x)) +#define NK_STRINGIFY(x) #x +#define NK_MACRO_STRINGIFY(x) NK_STRINGIFY(x) +#define NK_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2 +#define NK_STRING_JOIN_DELAY(arg1, arg2) NK_STRING_JOIN_IMMEDIATE(arg1, arg2) +#define NK_STRING_JOIN(arg1, arg2) NK_STRING_JOIN_DELAY(arg1, arg2) + +#ifdef _MSC_VER +#define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__COUNTER__) +#else +#define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__LINE__) +#endif + +#ifndef NK_STATIC_ASSERT +#define NK_STATIC_ASSERT(exp) typedef char NK_UNIQUE_NAME(_dummy_array)[(exp)?1:-1] +#endif + +#ifndef NK_FILE_LINE +#ifdef _MSC_VER +#define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__COUNTER__) +#else +#define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__LINE__) +#endif +#endif + +/* + * =============================================================== + * + * BASIC + * + * =============================================================== + */ +#ifdef NK_INCLUDE_FIXED_TYPES + #include + #define NK_INT8 int8_t + #define NK_UINT8 uint8_t + #define NK_INT16 int16_t + #define NK_UINT16 uint16_t + #define NK_INT32 int32_t + #define NK_UINT32 uint32_t + #define NK_SIZE_TYPE uintptr_t + #define NK_POINTER_TYPE uintptr_t +#else + #ifndef NK_INT8 + #define NK_INT8 char + #endif + #ifndef NK_UINT8 + #define NK_BYTE unsigned char + #endif + #ifndef NK_INT16 + #define NK_INT16 signed char + #endif + #ifndef NK_UINT16 + #define NK_UINT16 unsigned short + #endif + #ifndef NK_INT32 + #define NK_INT32 signed int + #endif + #ifndef NK_UINT32 + #define NK_UINT32 unsigned int + #endif + #ifndef NK_SIZE_TYPE + #if __WIN32 + #define NK_SIZE_TYPE __int32 + #elif __WIN64 + #define NK_SIZE_TYPE __int64 + #elif __GNUC__ || __clang__ + #if __x86_64__ || __ppc64__ + #define NK_SIZE_TYPE unsigned long + #else + #define NK_SIZE_TYPE unsigned int + #endif + #else + #define NK_SIZE_TYPE unsigned long + #endif + #endif + #ifndef NK_POINTER_TYPE + #if __WIN32 + #define NK_POINTER_TYPE unsigned __int32 + #elif __WIN64 + #define NK_POINTER_TYPE unsigned __int64 + #elif __GNUC__ || __clang__ + #if __x86_64__ || __ppc64__ + #define NK_POINTER_TYPE unsigned long + #else + #define NK_POINTER_TYPE unsigned int + #endif + #else + #define NK_POINTER_TYPE unsigned long + #endif + #endif +#endif + +typedef NK_INT8 nk_char; +typedef NK_UINT8 nk_uchar; +typedef NK_UINT8 nk_byte; +typedef NK_INT16 nk_short; +typedef NK_UINT16 nk_ushort; +typedef NK_INT32 nk_int; +typedef NK_UINT32 nk_uint; +typedef NK_SIZE_TYPE nk_size; +typedef NK_POINTER_TYPE nk_ptr; + +typedef nk_uint nk_hash; +typedef nk_uint nk_flags; +typedef nk_uint nk_rune; + +/* Make sure correct type size: + * This will fire with a negative subscript error if the type sizes + * are set incorrectly by the compiler, and compile out if not */ +NK_STATIC_ASSERT(sizeof(nk_short) == 2); +NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); +NK_STATIC_ASSERT(sizeof(nk_uint) == 4); +NK_STATIC_ASSERT(sizeof(nk_int) == 4); +NK_STATIC_ASSERT(sizeof(nk_byte) == 1); +NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); +NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); +NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_ptr) >= sizeof(void*)); + +/* ============================================================================ + * + * API + * + * =========================================================================== */ +struct nk_buffer; +struct nk_allocator; +struct nk_command_buffer; +struct nk_draw_command; +struct nk_convert_config; +struct nk_style_item; +struct nk_text_edit; +struct nk_draw_list; +struct nk_user_font; +struct nk_panel; +struct nk_context; +struct nk_draw_vertex_layout_element; + +enum {nk_false, nk_true}; +struct nk_color {nk_byte r,g,b,a;}; +struct nk_colorf {float r,g,b,a;}; +struct nk_vec2 {float x,y;}; +struct nk_vec2i {short x, y;}; +struct nk_rect {float x,y,w,h;}; +struct nk_recti {short x,y,w,h;}; +typedef char nk_glyph[NK_UTF_SIZE]; +typedef union {void *ptr; int id;} nk_handle; +struct nk_image {nk_handle handle;unsigned short w,h;unsigned short region[4];}; +struct nk_cursor {struct nk_image img; struct nk_vec2 size, offset;}; +struct nk_scroll {unsigned short x, y;}; +enum nk_heading {NK_UP, NK_RIGHT, NK_DOWN, NK_LEFT}; + +enum nk_button_behavior {NK_BUTTON_DEFAULT, NK_BUTTON_REPEATER}; +enum nk_modify {NK_FIXED = nk_false, NK_MODIFIABLE = nk_true}; +enum nk_orientation {NK_VERTICAL, NK_HORIZONTAL}; +enum nk_collapse_states {NK_MINIMIZED = nk_false, NK_MAXIMIZED = nk_true}; +enum nk_show_states {NK_HIDDEN = nk_false, NK_SHOWN = nk_true}; +enum nk_chart_type {NK_CHART_LINES, NK_CHART_COLUMN, NK_CHART_MAX}; +enum nk_chart_event {NK_CHART_HOVERING = 0x01, NK_CHART_CLICKED = 0x02}; +enum nk_color_format {NK_RGB, NK_RGBA}; +enum nk_popup_type {NK_POPUP_STATIC, NK_POPUP_DYNAMIC}; +enum nk_layout_format {NK_DYNAMIC, NK_STATIC}; +enum nk_tree_type {NK_TREE_NODE, NK_TREE_TAB}; +enum nk_anti_aliasing {NK_ANTI_ALIASING_OFF, NK_ANTI_ALIASING_ON}; + +typedef void*(*nk_plugin_alloc)(nk_handle, void *old, nk_size); +typedef void (*nk_plugin_free)(nk_handle, void *old); +typedef int(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode); +typedef void(*nk_plugin_paste)(nk_handle, struct nk_text_edit*); +typedef void(*nk_plugin_copy)(nk_handle, const char*, int len); + +struct nk_allocator { + nk_handle userdata; + nk_plugin_alloc alloc; + nk_plugin_free free; +}; + +struct nk_draw_null_texture { + nk_handle texture;/* texture handle to a texture with a white pixel */ + struct nk_vec2 uv; /* coordinates to a white pixel in the texture */ +}; +struct nk_convert_config { + float global_alpha; /* global alpha value */ + enum nk_anti_aliasing line_AA; /* line anti-aliasing flag can be turned off if you are tight on memory */ + enum nk_anti_aliasing shape_AA; /* shape anti-aliasing flag can be turned off if you are tight on memory */ + unsigned int circle_segment_count; /* number of segments used for circles: default to 22 */ + unsigned int arc_segment_count; /* number of segments used for arcs: default to 22 */ + unsigned int curve_segment_count; /* number of segments used for curves: default to 22 */ + struct nk_draw_null_texture null; /* handle to texture with a white pixel for shape drawing */ + const struct nk_draw_vertex_layout_element *vertex_layout; /* describes the vertex output format and packing */ + nk_size vertex_size; /* sizeof one vertex for vertex packing */ + nk_size vertex_alignment; /* vertex alignment: Can be optained by NK_ALIGNOF */ +}; + +enum nk_symbol_type { + NK_SYMBOL_NONE, + NK_SYMBOL_X, + NK_SYMBOL_UNDERSCORE, + NK_SYMBOL_CIRCLE_SOLID, + NK_SYMBOL_CIRCLE_OUTLINE, + NK_SYMBOL_RECT_SOLID, + NK_SYMBOL_RECT_OUTLINE, + NK_SYMBOL_TRIANGLE_UP, + NK_SYMBOL_TRIANGLE_DOWN, + NK_SYMBOL_TRIANGLE_LEFT, + NK_SYMBOL_TRIANGLE_RIGHT, + NK_SYMBOL_PLUS, + NK_SYMBOL_MINUS, + NK_SYMBOL_MAX +}; + +enum nk_keys { + NK_KEY_NONE, + NK_KEY_SHIFT, + NK_KEY_CTRL, + NK_KEY_DEL, + NK_KEY_ENTER, + NK_KEY_TAB, + NK_KEY_BACKSPACE, + NK_KEY_COPY, + NK_KEY_CUT, + NK_KEY_PASTE, + NK_KEY_UP, + NK_KEY_DOWN, + NK_KEY_LEFT, + NK_KEY_RIGHT, + + /* Shortcuts: text field */ + NK_KEY_TEXT_INSERT_MODE, + NK_KEY_TEXT_REPLACE_MODE, + NK_KEY_TEXT_RESET_MODE, + NK_KEY_TEXT_LINE_START, + NK_KEY_TEXT_LINE_END, + NK_KEY_TEXT_START, + NK_KEY_TEXT_END, + NK_KEY_TEXT_UNDO, + NK_KEY_TEXT_REDO, + NK_KEY_TEXT_WORD_LEFT, + NK_KEY_TEXT_WORD_RIGHT, + + /* Shortcuts: scrollbar */ + NK_KEY_SCROLL_START, + NK_KEY_SCROLL_END, + NK_KEY_SCROLL_DOWN, + NK_KEY_SCROLL_UP, + + NK_KEY_MAX +}; + +enum nk_buttons { + NK_BUTTON_LEFT, + NK_BUTTON_MIDDLE, + NK_BUTTON_RIGHT, + NK_BUTTON_MAX +}; + +enum nk_style_colors { + NK_COLOR_TEXT, + NK_COLOR_WINDOW, + NK_COLOR_HEADER, + NK_COLOR_BORDER, + NK_COLOR_BUTTON, + NK_COLOR_BUTTON_HOVER, + NK_COLOR_BUTTON_ACTIVE, + NK_COLOR_TOGGLE, + NK_COLOR_TOGGLE_HOVER, + NK_COLOR_TOGGLE_CURSOR, + NK_COLOR_SELECT, + NK_COLOR_SELECT_ACTIVE, + NK_COLOR_SLIDER, + NK_COLOR_SLIDER_CURSOR, + NK_COLOR_SLIDER_CURSOR_HOVER, + NK_COLOR_SLIDER_CURSOR_ACTIVE, + NK_COLOR_PROPERTY, + NK_COLOR_EDIT, + NK_COLOR_EDIT_CURSOR, + NK_COLOR_COMBO, + NK_COLOR_CHART, + NK_COLOR_CHART_COLOR, + NK_COLOR_CHART_COLOR_HIGHLIGHT, + NK_COLOR_SCROLLBAR, + NK_COLOR_SCROLLBAR_CURSOR, + NK_COLOR_SCROLLBAR_CURSOR_HOVER, + NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, + NK_COLOR_TAB_HEADER, + NK_COLOR_COUNT +}; + +enum nk_style_cursor { + NK_CURSOR_ARROW, + NK_CURSOR_TEXT, + NK_CURSOR_MOVE, + NK_CURSOR_RESIZE_VERTICAL, + NK_CURSOR_RESIZE_HORIZONTAL, + NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT, + NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT, + NK_CURSOR_COUNT +}; + +enum nk_widget_layout_states { + NK_WIDGET_INVALID, /* The widget cannot be seen and is completely out of view */ + NK_WIDGET_VALID, /* The widget is completely inside the window and can be updated and drawn */ + NK_WIDGET_ROM /* The widget is partially visible and cannot be updated */ +}; + +/* widget states */ +enum nk_widget_states { + NK_WIDGET_STATE_MODIFIED = NK_FLAG(1), + NK_WIDGET_STATE_INACTIVE = NK_FLAG(2), /* widget is neither active nor hovered */ + NK_WIDGET_STATE_ENTERED = NK_FLAG(3), /* widget has been hovered on the current frame */ + NK_WIDGET_STATE_HOVER = NK_FLAG(4), /* widget is being hovered */ + NK_WIDGET_STATE_ACTIVED = NK_FLAG(5),/* widget is currently activated */ + NK_WIDGET_STATE_LEFT = NK_FLAG(6), /* widget is from this frame on not hovered anymore */ + NK_WIDGET_STATE_HOVERED = NK_WIDGET_STATE_HOVER|NK_WIDGET_STATE_MODIFIED, /* widget is being hovered */ + NK_WIDGET_STATE_ACTIVE = NK_WIDGET_STATE_ACTIVED|NK_WIDGET_STATE_MODIFIED /* widget is currently activated */ +}; + +/* text alignment */ +enum nk_text_align { + NK_TEXT_ALIGN_LEFT = 0x01, + NK_TEXT_ALIGN_CENTERED = 0x02, + NK_TEXT_ALIGN_RIGHT = 0x04, + NK_TEXT_ALIGN_TOP = 0x08, + NK_TEXT_ALIGN_MIDDLE = 0x10, + NK_TEXT_ALIGN_BOTTOM = 0x20 +}; +enum nk_text_alignment { + NK_TEXT_LEFT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_LEFT, + NK_TEXT_CENTERED = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_CENTERED, + NK_TEXT_RIGHT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_RIGHT +}; + +/* edit flags */ +enum nk_edit_flags { + NK_EDIT_DEFAULT = 0, + NK_EDIT_READ_ONLY = NK_FLAG(0), + NK_EDIT_AUTO_SELECT = NK_FLAG(1), + NK_EDIT_SIG_ENTER = NK_FLAG(2), + NK_EDIT_ALLOW_TAB = NK_FLAG(3), + NK_EDIT_NO_CURSOR = NK_FLAG(4), + NK_EDIT_SELECTABLE = NK_FLAG(5), + NK_EDIT_CLIPBOARD = NK_FLAG(6), + NK_EDIT_CTRL_ENTER_NEWLINE = NK_FLAG(7), + NK_EDIT_NO_HORIZONTAL_SCROLL = NK_FLAG(8), + NK_EDIT_ALWAYS_INSERT_MODE = NK_FLAG(9), + NK_EDIT_MULTILINE = NK_FLAG(11), + NK_EDIT_GOTO_END_ON_ACTIVATE = NK_FLAG(12) +}; +enum nk_edit_types { + NK_EDIT_SIMPLE = NK_EDIT_ALWAYS_INSERT_MODE, + NK_EDIT_FIELD = NK_EDIT_SIMPLE|NK_EDIT_SELECTABLE|NK_EDIT_CLIPBOARD, + NK_EDIT_BOX = NK_EDIT_ALWAYS_INSERT_MODE| NK_EDIT_SELECTABLE| NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB|NK_EDIT_CLIPBOARD, + NK_EDIT_EDITOR = NK_EDIT_SELECTABLE|NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB| NK_EDIT_CLIPBOARD +}; +enum nk_edit_events { + NK_EDIT_ACTIVE = NK_FLAG(0), /* edit widget is currently being modified */ + NK_EDIT_INACTIVE = NK_FLAG(1), /* edit widget is not active and is not being modified */ + NK_EDIT_ACTIVATED = NK_FLAG(2), /* edit widget went from state inactive to state active */ + NK_EDIT_DEACTIVATED = NK_FLAG(3), /* edit widget went from state active to state inactive */ + NK_EDIT_COMMITED = NK_FLAG(4) /* edit widget has received an enter and lost focus */ +}; + +enum nk_panel_flags { + NK_WINDOW_BORDER = NK_FLAG(0), /* Draws a border around the window to visually separate the window * from the background */ + NK_WINDOW_MOVABLE = NK_FLAG(1), /* The movable flag indicates that a window can be moved by user input or * by dragging the window header */ + NK_WINDOW_SCALABLE = NK_FLAG(2), /* The scalable flag indicates that a window can be scaled by user input * by dragging a scaler icon at the button of the window */ + NK_WINDOW_CLOSABLE = NK_FLAG(3), /* adds a closable icon into the header */ + NK_WINDOW_MINIMIZABLE = NK_FLAG(4), /* adds a minimize icon into the header */ + NK_WINDOW_NO_SCROLLBAR = NK_FLAG(5), /* Removes the scrollbar from the window */ + NK_WINDOW_TITLE = NK_FLAG(6), /* Forces a header at the top at the window showing the title */ + NK_WINDOW_SCROLL_AUTO_HIDE = NK_FLAG(7), /* Automatically hides the window scrollbar if no user interaction */ + NK_WINDOW_BACKGROUND = NK_FLAG(8) /* Always keep window in the background */ +}; + +/* context */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API int nk_init_default(struct nk_context*, const struct nk_user_font*); +#endif +NK_API int nk_init_fixed(struct nk_context*, void *memory, nk_size size, const struct nk_user_font*); +NK_API int nk_init_custom(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font*); +NK_API int nk_init(struct nk_context*, struct nk_allocator*, const struct nk_user_font*); +NK_API void nk_clear(struct nk_context*); +NK_API void nk_free(struct nk_context*); +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void nk_set_user_data(struct nk_context*, nk_handle handle); +#endif + +/* window */ +NK_API int nk_begin(struct nk_context*, struct nk_panel*, const char *title, struct nk_rect bounds, nk_flags flags); +NK_API int nk_begin_titled(struct nk_context*, struct nk_panel*, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); +NK_API void nk_end(struct nk_context*); + +NK_API struct nk_window* nk_window_find(struct nk_context *ctx, const char *name); +NK_API struct nk_rect nk_window_get_bounds(const struct nk_context*); +NK_API struct nk_vec2 nk_window_get_position(const struct nk_context*); +NK_API struct nk_vec2 nk_window_get_size(const struct nk_context*); +NK_API float nk_window_get_width(const struct nk_context*); +NK_API float nk_window_get_height(const struct nk_context*); +NK_API struct nk_panel* nk_window_get_panel(struct nk_context*); +NK_API struct nk_rect nk_window_get_content_region(struct nk_context*); +NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context*); +NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context*); +NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context*); +NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context*); + +NK_API int nk_window_has_focus(const struct nk_context*); +NK_API int nk_window_is_collapsed(struct nk_context*, const char*); +NK_API int nk_window_is_closed(struct nk_context*, const char*); +NK_API int nk_window_is_hidden(struct nk_context*, const char*); +NK_API int nk_window_is_active(struct nk_context*, const char*); +NK_API int nk_window_is_hovered(struct nk_context*); +NK_API int nk_window_is_any_hovered(struct nk_context*); +NK_API int nk_item_is_any_active(struct nk_context*); + +NK_API void nk_window_set_bounds(struct nk_context*, struct nk_rect); +NK_API void nk_window_set_position(struct nk_context*, struct nk_vec2); +NK_API void nk_window_set_size(struct nk_context*, struct nk_vec2); +NK_API void nk_window_set_focus(struct nk_context*, const char *name); + +NK_API void nk_window_close(struct nk_context *ctx, const char *name); +NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states); +NK_API void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); +NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); +NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); + +/* Layout */ +NK_API void nk_layout_row_dynamic(struct nk_context*, float height, int cols); +NK_API void nk_layout_row_static(struct nk_context*, float height, int item_width, int cols); + +NK_API void nk_layout_row_begin(struct nk_context*, enum nk_layout_format, float row_height, int cols); +NK_API void nk_layout_row_push(struct nk_context*, float value); +NK_API void nk_layout_row_end(struct nk_context*); +NK_API void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); + +NK_API void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); +NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect); +NK_API void nk_layout_space_end(struct nk_context*); + +NK_API struct nk_rect nk_layout_space_bounds(struct nk_context*); +NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); +NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); +NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); +NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); +NK_API float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); + +/* Layout: Group */ +NK_API int nk_group_begin(struct nk_context*, struct nk_panel*, const char *title, nk_flags); +NK_API void nk_group_end(struct nk_context*); + +/* Layout: Tree */ +#define nk_tree_push(ctx, type, title, state) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +#define nk_tree_push_id(ctx, type, title, state, id) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +NK_API int nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +#define nk_tree_image_push(ctx, type, img, title, state) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +#define nk_tree_image_push_id(ctx, type, img, title, state, id) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +NK_API int nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +NK_API void nk_tree_pop(struct nk_context*); + +/* Widgets */ +NK_API void nk_text(struct nk_context*, const char*, int, nk_flags); +NK_API void nk_text_colored(struct nk_context*, const char*, int, nk_flags, struct nk_color); +NK_API void nk_text_wrap(struct nk_context*, const char*, int); +NK_API void nk_text_wrap_colored(struct nk_context*, const char*, int, struct nk_color); + +NK_API void nk_label(struct nk_context*, const char*, nk_flags align); +NK_API void nk_label_colored(struct nk_context*, const char*, nk_flags align, struct nk_color); +NK_API void nk_label_wrap(struct nk_context*, const char*); +NK_API void nk_label_colored_wrap(struct nk_context*, const char*, struct nk_color); +NK_API void nk_image(struct nk_context*, struct nk_image); +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void nk_labelf(struct nk_context*, nk_flags, const char*, ...); +NK_API void nk_labelf_colored(struct nk_context*, nk_flags align, struct nk_color, const char*,...); +NK_API void nk_labelf_wrap(struct nk_context*, const char*,...); +NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, const char*,...); + +NK_API void nk_value_bool(struct nk_context*, const char *prefix, int); +NK_API void nk_value_int(struct nk_context*, const char *prefix, int); +NK_API void nk_value_uint(struct nk_context*, const char *prefix, unsigned int); +NK_API void nk_value_float(struct nk_context*, const char *prefix, float); +NK_API void nk_value_color_byte(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_float(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_hex(struct nk_context*, const char *prefix, struct nk_color); +#endif + +/* Widgets: Buttons */ +NK_API int nk_button_text(struct nk_context*, const char *title, int len); +NK_API int nk_button_label(struct nk_context*, const char *title); +NK_API int nk_button_color(struct nk_context*, struct nk_color); +NK_API int nk_button_symbol(struct nk_context*, enum nk_symbol_type); +NK_API int nk_button_image(struct nk_context*, struct nk_image img); +NK_API int nk_button_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags text_alignment); +NK_API int nk_button_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API int nk_button_image_label(struct nk_context*, struct nk_image img, const char*, nk_flags text_alignment); +NK_API int nk_button_image_text(struct nk_context*, struct nk_image img, const char*, int, nk_flags alignment); + +NK_API void nk_button_set_behavior(struct nk_context*, enum nk_button_behavior); +NK_API int nk_button_push_behavior(struct nk_context*, enum nk_button_behavior); +NK_API int nk_button_pop_behavior(struct nk_context*); + +/* Widgets: Checkbox */ +NK_API int nk_check_label(struct nk_context*, const char*, int active); +NK_API int nk_check_text(struct nk_context*, const char*, int,int active); +NK_API unsigned nk_check_flags_label(struct nk_context*, const char*, unsigned int flags, unsigned int value); +NK_API unsigned nk_check_flags_text(struct nk_context*, const char*, int, unsigned int flags, unsigned int value); +NK_API int nk_checkbox_label(struct nk_context*, const char*, int *active); +NK_API int nk_checkbox_text(struct nk_context*, const char*, int, int *active); +NK_API int nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value); +NK_API int nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value); + +/* Widgets: Radio */ +NK_API int nk_radio_label(struct nk_context*, const char*, int *active); +NK_API int nk_radio_text(struct nk_context*, const char*, int, int *active); +NK_API int nk_option_label(struct nk_context*, const char*, int active); +NK_API int nk_option_text(struct nk_context*, const char*, int, int active); + +/* Widgets: Selectable */ +NK_API int nk_selectable_label(struct nk_context*, const char*, nk_flags align, int *value); +NK_API int nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, int *value); +NK_API int nk_selectable_image_label(struct nk_context*,struct nk_image, const char*, nk_flags align, int *value); +NK_API int nk_selectable_image_text(struct nk_context*,struct nk_image, const char*, int, nk_flags align, int *value); + +NK_API int nk_select_label(struct nk_context*, const char*, nk_flags align, int value); +NK_API int nk_select_text(struct nk_context*, const char*, int, nk_flags align, int value); +NK_API int nk_select_image_label(struct nk_context*, struct nk_image,const char*, nk_flags align, int value); +NK_API int nk_select_image_text(struct nk_context*, struct nk_image,const char*, int, nk_flags align, int value); + +/* Widgets: Slider */ +NK_API float nk_slide_float(struct nk_context*, float min, float val, float max, float step); +NK_API int nk_slide_int(struct nk_context*, int min, int val, int max, int step); +NK_API int nk_slider_float(struct nk_context*, float min, float *val, float max, float step); +NK_API int nk_slider_int(struct nk_context*, int min, int *val, int max, int step); + +/* Widgets: Progressbar */ +NK_API int nk_progress(struct nk_context*, nk_size *cur, nk_size max, int modifyable); +NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, int modifyable); + +/* Widgets: Color picker */ +NK_API struct nk_color nk_color_picker(struct nk_context*, struct nk_color, enum nk_color_format); +NK_API int nk_color_pick(struct nk_context*, struct nk_color*, enum nk_color_format); + +/* Widgets: Property */ +NK_API void nk_property_int(struct nk_context*, const char *name, int min, int *val, int max, int step, float inc_per_pixel); +NK_API void nk_property_float(struct nk_context*, const char *name, float min, float *val, float max, float step, float inc_per_pixel); +NK_API void nk_property_double(struct nk_context*, const char *name, double min, double *val, double max, double step, float inc_per_pixel); +NK_API int nk_propertyi(struct nk_context*, const char *name, int min, int val, int max, int step, float inc_per_pixel); +NK_API float nk_propertyf(struct nk_context*, const char *name, float min, float val, float max, float step, float inc_per_pixel); +NK_API double nk_propertyd(struct nk_context*, const char *name, double min, double val, double max, double step, float inc_per_pixel); + +/* Widgets: TextEdit */ +NK_API nk_flags nk_edit_string(struct nk_context*, nk_flags, char *buffer, int *len, int max, nk_plugin_filter); +NK_API nk_flags nk_edit_buffer(struct nk_context*, nk_flags, struct nk_text_edit*, nk_plugin_filter); +NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context*, nk_flags, char *buffer, int max, nk_plugin_filter); + +/* Chart */ +NK_API int nk_chart_begin(struct nk_context*, enum nk_chart_type, int num, float min, float max); +NK_API int nk_chart_begin_colored(struct nk_context*, enum nk_chart_type, struct nk_color, struct nk_color active, int num, float min, float max); +NK_API void nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type, int count, float min_value, float max_value); +NK_API void nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type, struct nk_color, struct nk_color active, int count, float min_value, float max_value); +NK_API nk_flags nk_chart_push(struct nk_context*, float); +NK_API nk_flags nk_chart_push_slot(struct nk_context*, float, int); +NK_API void nk_chart_end(struct nk_context*); +NK_API void nk_plot(struct nk_context*, enum nk_chart_type, const float *values, int count, int offset); +NK_API void nk_plot_function(struct nk_context*, enum nk_chart_type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset); + +/* Popups */ +NK_API int nk_popup_begin(struct nk_context*, struct nk_panel*, enum nk_popup_type, const char*, nk_flags, struct nk_rect bounds); +NK_API void nk_popup_close(struct nk_context*); +NK_API void nk_popup_end(struct nk_context*); + +/* Combobox */ +NK_API int nk_combo(struct nk_context*, const char **items, int count, int selected, int item_height, struct nk_vec2 size); +NK_API int nk_combo_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int selected, int count, int item_height, struct nk_vec2 size); +NK_API int nk_combo_string(struct nk_context*, const char *items_separated_by_zeros, int selected, int count, int item_height, struct nk_vec2 size); +NK_API int nk_combo_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox(struct nk_context*, const char **items, int count, int *selected, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_string(struct nk_context*, const char *items_separated_by_zeros, int *selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_separator(struct nk_context*, const char *items_separated_by_separator, int separator,int *selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void*, int *selected, int count, int item_height, struct nk_vec2 size); + +/* Combobox: abstract */ +NK_API int nk_combo_begin_text(struct nk_context*, struct nk_panel*, const char *selected, int, struct nk_vec2 size); +NK_API int nk_combo_begin_label(struct nk_context*, struct nk_panel*, const char *selected, struct nk_vec2 size); +NK_API int nk_combo_begin_color(struct nk_context*, struct nk_panel*, struct nk_color color, struct nk_vec2 size); +NK_API int nk_combo_begin_symbol(struct nk_context*, struct nk_panel*, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_combo_begin_symbol_label(struct nk_context*, struct nk_panel*, const char *selected, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_combo_begin_symbol_text(struct nk_context*, struct nk_panel*, const char *selected, int, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_combo_begin_image(struct nk_context*, struct nk_panel*, struct nk_image img, struct nk_vec2 size); +NK_API int nk_combo_begin_image_label(struct nk_context*, struct nk_panel*, const char *selected, struct nk_image, struct nk_vec2 size); +NK_API int nk_combo_begin_image_text(struct nk_context*, struct nk_panel*, const char *selected, int, struct nk_image, struct nk_vec2 size); +NK_API int nk_combo_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API int nk_combo_item_text(struct nk_context*, const char*,int, nk_flags alignment); +NK_API int nk_combo_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_combo_item_image_text(struct nk_context*, struct nk_image, const char*, int,nk_flags alignment); +NK_API int nk_combo_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API int nk_combo_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_combo_close(struct nk_context*); +NK_API void nk_combo_end(struct nk_context*); + +/* Contextual */ +NK_API int nk_contextual_begin(struct nk_context*, struct nk_panel*, nk_flags, struct nk_vec2, struct nk_rect trigger_bounds); +NK_API int nk_contextual_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API int nk_contextual_item_label(struct nk_context*, const char*, nk_flags align); +NK_API int nk_contextual_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_contextual_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API int nk_contextual_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API int nk_contextual_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_contextual_close(struct nk_context*); +NK_API void nk_contextual_end(struct nk_context*); + +/* Tooltip */ +NK_API void nk_tooltip(struct nk_context*, const char*); +NK_API int nk_tooltip_begin(struct nk_context*, struct nk_panel*, float width); +NK_API void nk_tooltip_end(struct nk_context*); + +/* Menu */ +NK_API void nk_menubar_begin(struct nk_context*); +NK_API void nk_menubar_end(struct nk_context*); + +NK_API int nk_menu_begin_text(struct nk_context*, struct nk_panel*, const char* title, int title_len, nk_flags align, struct nk_vec2 size); +NK_API int nk_menu_begin_label(struct nk_context*, struct nk_panel*, const char*, nk_flags align, struct nk_vec2 size); +NK_API int nk_menu_begin_image(struct nk_context*, struct nk_panel*, const char*, struct nk_image, struct nk_vec2 size); +NK_API int nk_menu_begin_image_text(struct nk_context*, struct nk_panel*, const char*, int,nk_flags align,struct nk_image, struct nk_vec2 size); +NK_API int nk_menu_begin_image_label(struct nk_context*, struct nk_panel*, const char*, nk_flags align,struct nk_image, struct nk_vec2 size); +NK_API int nk_menu_begin_symbol(struct nk_context*, struct nk_panel*, const char*, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_menu_begin_symbol_text(struct nk_context*, struct nk_panel*, const char*, int,nk_flags align,enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_menu_begin_symbol_label(struct nk_context*, struct nk_panel*, const char*, nk_flags align,enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_menu_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API int nk_menu_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API int nk_menu_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_menu_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API int nk_menu_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API int nk_menu_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API void nk_menu_close(struct nk_context*); +NK_API void nk_menu_end(struct nk_context*); + +/* Drawing*/ +#define nk_foreach(c, ctx) for((c)=nk__begin(ctx); (c)!=0; (c)=nk__next(ctx, c)) +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_API void nk_convert(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); +#define nk_draw_foreach(cmd,ctx, b) for((cmd)=nk__draw_begin(ctx, b); (cmd)!=0; (cmd)=nk__draw_next(cmd, b, ctx)) +#define nk_draw_foreach_bounded(cmd,from,to) for((cmd)=(from); (cmd) && (to) && (cmd)>=to; --(cmd)) +NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); +NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context*, const struct nk_buffer*); +NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); +#endif + +/* User Input */ +NK_API void nk_input_begin(struct nk_context*); +NK_API void nk_input_motion(struct nk_context*, int x, int y); +NK_API void nk_input_key(struct nk_context*, enum nk_keys, int down); +NK_API void nk_input_button(struct nk_context*, enum nk_buttons, int x, int y, int down); +NK_API void nk_input_scroll(struct nk_context*, float y); +NK_API void nk_input_char(struct nk_context*, char); +NK_API void nk_input_glyph(struct nk_context*, const nk_glyph); +NK_API void nk_input_unicode(struct nk_context*, nk_rune); +NK_API void nk_input_end(struct nk_context*); + +/* Style */ +NK_API void nk_style_default(struct nk_context*); +NK_API void nk_style_from_table(struct nk_context*, const struct nk_color*); +NK_API void nk_style_load_cursor(struct nk_context*, enum nk_style_cursor, const struct nk_cursor*); +NK_API void nk_style_load_all_cursors(struct nk_context*, struct nk_cursor*); +NK_API const char* nk_style_get_color_by_name(enum nk_style_colors); +NK_API void nk_style_set_font(struct nk_context*, const struct nk_user_font*); +NK_API int nk_style_set_cursor(struct nk_context*, enum nk_style_cursor); +NK_API void nk_style_show_cursor(struct nk_context*); +NK_API void nk_style_hide_cursor(struct nk_context*); + +/* Style: stack */ +NK_API int nk_style_push_font(struct nk_context*, struct nk_user_font*); +NK_API int nk_style_push_float(struct nk_context*, float*, float); +NK_API int nk_style_push_vec2(struct nk_context*, struct nk_vec2*, struct nk_vec2); +NK_API int nk_style_push_style_item(struct nk_context*, struct nk_style_item*, struct nk_style_item); +NK_API int nk_style_push_flags(struct nk_context*, nk_flags*, nk_flags); +NK_API int nk_style_push_color(struct nk_context*, struct nk_color*, struct nk_color); + +NK_API int nk_style_pop_font(struct nk_context*); +NK_API int nk_style_pop_float(struct nk_context*); +NK_API int nk_style_pop_vec2(struct nk_context*); +NK_API int nk_style_pop_style_item(struct nk_context*); +NK_API int nk_style_pop_flags(struct nk_context*); +NK_API int nk_style_pop_color(struct nk_context*); + +/* Utilities */ +NK_API struct nk_rect nk_widget_bounds(struct nk_context*); +NK_API struct nk_vec2 nk_widget_position(struct nk_context*); +NK_API struct nk_vec2 nk_widget_size(struct nk_context*); +NK_API float nk_widget_width(struct nk_context*); +NK_API float nk_widget_height(struct nk_context*); +NK_API int nk_widget_is_hovered(struct nk_context*); +NK_API int nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons); +NK_API int nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, int down); +NK_API void nk_spacing(struct nk_context*, int cols); + +/* base widget function */ +NK_API enum nk_widget_layout_states nk_widget(struct nk_rect*, const struct nk_context*); +NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect*, struct nk_context*, struct nk_vec2); + +/* color (conversion user --> nuklear) */ +NK_API struct nk_color nk_rgb(int r, int g, int b); +NK_API struct nk_color nk_rgb_iv(const int *rgb); +NK_API struct nk_color nk_rgb_bv(const nk_byte* rgb); +NK_API struct nk_color nk_rgb_f(float r, float g, float b); +NK_API struct nk_color nk_rgb_fv(const float *rgb); +NK_API struct nk_color nk_rgb_hex(const char *rgb); + +NK_API struct nk_color nk_rgba(int r, int g, int b, int a); +NK_API struct nk_color nk_rgba_u32(nk_uint); +NK_API struct nk_color nk_rgba_iv(const int *rgba); +NK_API struct nk_color nk_rgba_bv(const nk_byte *rgba); +NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a); +NK_API struct nk_color nk_rgba_fv(const float *rgba); +NK_API struct nk_color nk_rgba_hex(const char *rgb); + +NK_API struct nk_color nk_hsv(int h, int s, int v); +NK_API struct nk_color nk_hsv_iv(const int *hsv); +NK_API struct nk_color nk_hsv_bv(const nk_byte *hsv); +NK_API struct nk_color nk_hsv_f(float h, float s, float v); +NK_API struct nk_color nk_hsv_fv(const float *hsv); + +NK_API struct nk_color nk_hsva(int h, int s, int v, int a); +NK_API struct nk_color nk_hsva_iv(const int *hsva); +NK_API struct nk_color nk_hsva_bv(const nk_byte *hsva); +NK_API struct nk_color nk_hsva_f(float h, float s, float v, float a); +NK_API struct nk_color nk_hsva_fv(const float *hsva); + +/* color (conversion nuklear --> user) */ +NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color); +NK_API void nk_color_fv(float *rgba_out, struct nk_color); +NK_API void nk_color_d(double *r, double *g, double *b, double *a, struct nk_color); +NK_API void nk_color_dv(double *rgba_out, struct nk_color); + +NK_API nk_uint nk_color_u32(struct nk_color); +NK_API void nk_color_hex_rgba(char *output, struct nk_color); +NK_API void nk_color_hex_rgb(char *output, struct nk_color); + +NK_API void nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color); +NK_API void nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color); +NK_API void nk_color_hsv_iv(int *hsv_out, struct nk_color); +NK_API void nk_color_hsv_bv(nk_byte *hsv_out, struct nk_color); +NK_API void nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color); +NK_API void nk_color_hsv_fv(float *hsv_out, struct nk_color); + +NK_API void nk_color_hsva_i(int *h, int *s, int *v, int *a, struct nk_color); +NK_API void nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color); +NK_API void nk_color_hsva_iv(int *hsva_out, struct nk_color); +NK_API void nk_color_hsva_bv(nk_byte *hsva_out, struct nk_color); +NK_API void nk_color_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_color); +NK_API void nk_color_hsva_fv(float *hsva_out, struct nk_color); + +/* image */ +NK_API nk_handle nk_handle_ptr(void*); +NK_API nk_handle nk_handle_id(int); +NK_API struct nk_image nk_image_handle(nk_handle); +NK_API struct nk_image nk_image_ptr(void*); +NK_API struct nk_image nk_image_id(int); +NK_API int nk_image_is_subimage(const struct nk_image* img); +NK_API struct nk_image nk_subimage_ptr(void*, unsigned short w, unsigned short h, struct nk_rect sub_region); +NK_API struct nk_image nk_subimage_id(int, unsigned short w, unsigned short h, struct nk_rect sub_region); +NK_API struct nk_image nk_subimage_handle(nk_handle, unsigned short w, unsigned short h, struct nk_rect sub_region); + +/* math */ +NK_API nk_hash nk_murmur_hash(const void *key, int len, nk_hash seed); +NK_API void nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, float pad_x, float pad_y, enum nk_heading); + +NK_API struct nk_vec2 nk_vec2(float x, float y); +NK_API struct nk_vec2 nk_vec2i(int x, int y); +NK_API struct nk_vec2 nk_vec2v(const float *xy); +NK_API struct nk_vec2 nk_vec2iv(const int *xy); + +NK_API struct nk_rect nk_get_null_rect(void); +NK_API struct nk_rect nk_rect(float x, float y, float w, float h); +NK_API struct nk_rect nk_recti(int x, int y, int w, int h); +NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size); +NK_API struct nk_rect nk_rectv(const float *xywh); +NK_API struct nk_rect nk_rectiv(const int *xywh); +NK_API struct nk_vec2 nk_rect_pos(struct nk_rect); +NK_API struct nk_vec2 nk_rect_size(struct nk_rect); + +/* string*/ +NK_API int nk_strlen(const char *str); +NK_API int nk_stricmp(const char *s1, const char *s2); +NK_API int nk_stricmpn(const char *s1, const char *s2, int n); +NK_API int nk_strtoi(const char *str, char **endptr); +NK_API float nk_strtof(const char *str, char **endptr); +NK_API double nk_strtod(const char *str, char **endptr); +NK_API int nk_strfilter(const char *text, const char *regexp); +NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score); +NK_API int nk_strmatch_fuzzy_text(const char *txt, int txt_len, const char *pattern, int *out_score); + +/* UTF-8 */ +NK_API int nk_utf_decode(const char*, nk_rune*, int); +NK_API int nk_utf_encode(nk_rune, char*, int); +NK_API int nk_utf_len(const char*, int byte_len); +NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len); + +/* ============================================================== + * + * MEMORY BUFFER + * + * ===============================================================*/ +/* A basic (double)-buffer with linear allocation and resetting as only + freeing policy. The buffer's main purpose is to control all memory management + inside the GUI toolkit and still leave memory control as much as possible in + the hand of the user while also making sure the library is easy to use if + not as much control is needed. + In general all memory inside this library can be provided from the user in + three different ways. + + The first way and the one providing most control is by just passing a fixed + size memory block. In this case all control lies in the hand of the user + since he can exactly control where the memory comes from and how much memory + the library should consume. Of course using the fixed size API removes the + ability to automatically resize a buffer if not enough memory is provided so + you have to take over the resizing. While being a fixed sized buffer sounds + quite limiting, it is very effective in this library since the actual memory + consumption is quite stable and has a fixed upper bound for a lot of cases. + + If you don't want to think about how much memory the library should allocate + at all time or have a very dynamic UI with unpredictable memory consumption + habits but still want control over memory allocation you can use the dynamic + allocator based API. The allocator consists of two callbacks for allocating + and freeing memory and optional userdata so you can plugin your own allocator. + + The final and easiest way can be used by defining + NK_INCLUDE_DEFAULT_ALLOCATOR which uses the standard library memory + allocation functions malloc and free and takes over complete control over + memory in this library. +*/ +struct nk_memory_status { + void *memory; + unsigned int type; + nk_size size; + nk_size allocated; + nk_size needed; + nk_size calls; +}; + +enum nk_allocation_type { + NK_BUFFER_FIXED, + NK_BUFFER_DYNAMIC +}; + +enum nk_buffer_allocation_type { + NK_BUFFER_FRONT, + NK_BUFFER_BACK, + NK_BUFFER_MAX +}; + +struct nk_buffer_marker { + int active; + nk_size offset; +}; + +struct nk_memory {void *ptr;nk_size size;}; +struct nk_buffer { + struct nk_buffer_marker marker[NK_BUFFER_MAX]; + /* buffer marker to free a buffer to a certain offset */ + struct nk_allocator pool; + /* allocator callback for dynamic buffers */ + enum nk_allocation_type type; + /* memory management type */ + struct nk_memory memory; + /* memory and size of the current memory block */ + float grow_factor; + /* growing factor for dynamic memory management */ + nk_size allocated; + /* total amount of memory allocated */ + nk_size needed; + /* totally consumed memory given that enough memory is present */ + nk_size calls; + /* number of allocation calls */ + nk_size size; + /* current size of the buffer */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_buffer_init_default(struct nk_buffer*); +#endif +NK_API void nk_buffer_init(struct nk_buffer*, const struct nk_allocator*, nk_size size); +NK_API void nk_buffer_init_fixed(struct nk_buffer*, void *memory, nk_size size); +NK_API void nk_buffer_info(struct nk_memory_status*, struct nk_buffer*); +NK_API void nk_buffer_push(struct nk_buffer*, enum nk_buffer_allocation_type type, const void *memory, nk_size size, nk_size align); +NK_API void nk_buffer_mark(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_reset(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_clear(struct nk_buffer*); +NK_API void nk_buffer_free(struct nk_buffer*); +NK_API void *nk_buffer_memory(struct nk_buffer*); +NK_API const void *nk_buffer_memory_const(const struct nk_buffer*); +NK_API nk_size nk_buffer_total(struct nk_buffer*); + +/* ============================================================== + * + * STRING + * + * ===============================================================*/ +/* Basic string buffer which is only used in context with the text editor + * to manage and manipulate dynamic or fixed size string content. This is _NOT_ + * the default string handling method.*/ +struct nk_str { + struct nk_buffer buffer; + int len; /* in codepoints/runes/glyphs */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_str_init_default(struct nk_str*); +#endif +NK_API void nk_str_init(struct nk_str*, const struct nk_allocator*, nk_size size); +NK_API void nk_str_init_fixed(struct nk_str*, void *memory, nk_size size); +NK_API void nk_str_clear(struct nk_str*); +NK_API void nk_str_free(struct nk_str*); + +NK_API int nk_str_append_text_char(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_char(struct nk_str*, const char*); +NK_API int nk_str_append_text_utf8(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_utf8(struct nk_str*, const char*); +NK_API int nk_str_append_text_runes(struct nk_str*, const nk_rune*, int); +NK_API int nk_str_append_str_runes(struct nk_str*, const nk_rune*); + +NK_API int nk_str_insert_at_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_at_rune(struct nk_str*, int pos, const char*, int); + +NK_API int nk_str_insert_text_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_char(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_utf8(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_utf8(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_runes(struct nk_str*, int pos, const nk_rune*, int); +NK_API int nk_str_insert_str_runes(struct nk_str*, int pos, const nk_rune*); + +NK_API void nk_str_remove_chars(struct nk_str*, int len); +NK_API void nk_str_remove_runes(struct nk_str *str, int len); +NK_API void nk_str_delete_chars(struct nk_str*, int pos, int len); +NK_API void nk_str_delete_runes(struct nk_str*, int pos, int len); + +NK_API char *nk_str_at_char(struct nk_str*, int pos); +NK_API char *nk_str_at_rune(struct nk_str*, int pos, nk_rune *unicode, int *len); +NK_API nk_rune nk_str_rune_at(const struct nk_str*, int pos); +NK_API const char *nk_str_at_char_const(const struct nk_str*, int pos); +NK_API const char *nk_str_at_const(const struct nk_str*, int pos, nk_rune *unicode, int *len); + +NK_API char *nk_str_get(struct nk_str*); +NK_API const char *nk_str_get_const(const struct nk_str*); +NK_API int nk_str_len(struct nk_str*); +NK_API int nk_str_len_char(struct nk_str*); + +/*=============================================================== + * + * TEXT EDITOR + * + * ===============================================================*/ +/* Editing text in this library is handled by either `nk_edit_string` or + * `nk_edit_buffer`. But like almost everything in this library there are multiple + * ways of doing it and a balance between control and ease of use with memory + * as well as functionality controlled by flags. + * + * This library generally allows three different levels of memory control: + * First of is the most basic way of just providing a simple char array with + * string length. This method is probably the easiest way of handling simple + * user text input. Main upside is complete control over memory while the biggest + * downside in comparsion with the other two approaches is missing undo/redo. + * + * For UIs that require undo/redo the second way was created. It is based on + * a fixed size nk_text_edit struct, which has an internal undo/redo stack. + * This is mainly useful if you want something more like a text editor but don't want + * to have a dynamically growing buffer. + * + * The final way is using a dynamically growing nk_text_edit struct, which + * has both a default version if you don't care where memory comes from and an + * allocator version if you do. While the text editor is quite powerful for its + * complexity I would not recommend editing gigabytes of data with it. + * It is rather designed for uses cases which make sense for a GUI library not for + * an full blown text editor. + */ +#ifndef NK_TEXTEDIT_UNDOSTATECOUNT +#define NK_TEXTEDIT_UNDOSTATECOUNT 99 +#endif + +#ifndef NK_TEXTEDIT_UNDOCHARCOUNT +#define NK_TEXTEDIT_UNDOCHARCOUNT 999 +#endif + +struct nk_text_edit; +struct nk_clipboard { + nk_handle userdata; + nk_plugin_paste paste; + nk_plugin_copy copy; +}; + +struct nk_text_undo_record { + int where; + short insert_length; + short delete_length; + short char_storage; +}; + +struct nk_text_undo_state { + struct nk_text_undo_record undo_rec[NK_TEXTEDIT_UNDOSTATECOUNT]; + nk_rune undo_char[NK_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point; + short redo_point; + short undo_char_point; + short redo_char_point; +}; + +enum nk_text_edit_type { + NK_TEXT_EDIT_SINGLE_LINE, + NK_TEXT_EDIT_MULTI_LINE +}; + +enum nk_text_edit_mode { + NK_TEXT_EDIT_MODE_VIEW, + NK_TEXT_EDIT_MODE_INSERT, + NK_TEXT_EDIT_MODE_REPLACE +}; + +struct nk_text_edit { + struct nk_clipboard clip; + struct nk_str string; + nk_plugin_filter filter; + struct nk_vec2 scrollbar; + + int cursor; + int select_start; + int select_end; + unsigned char mode; + unsigned char cursor_at_end_of_line; + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char active; + unsigned char padding1; + float preferred_x; + struct nk_text_undo_state undo; +}; + +/* filter function */ +NK_API int nk_filter_default(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_ascii(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_float(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_decimal(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_hex(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_oct(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_binary(const struct nk_text_edit*, nk_rune unicode); + +/* text editor */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_textedit_init_default(struct nk_text_edit*); +#endif +NK_API void nk_textedit_init(struct nk_text_edit*, struct nk_allocator*, nk_size size); +NK_API void nk_textedit_init_fixed(struct nk_text_edit*, void *memory, nk_size size); +NK_API void nk_textedit_free(struct nk_text_edit*); +NK_API void nk_textedit_text(struct nk_text_edit*, const char*, int total_len); +NK_API void nk_textedit_delete(struct nk_text_edit*, int where, int len); +NK_API void nk_textedit_delete_selection(struct nk_text_edit*); +NK_API void nk_textedit_select_all(struct nk_text_edit*); +NK_API int nk_textedit_cut(struct nk_text_edit*); +NK_API int nk_textedit_paste(struct nk_text_edit*, char const*, int len); +NK_API void nk_textedit_undo(struct nk_text_edit*); +NK_API void nk_textedit_redo(struct nk_text_edit*); + +/* =============================================================== + * + * FONT + * + * ===============================================================*/ +/* Font handling in this library was designed to be quite customizable and lets + you decide what you want to use and what you want to provide. In this sense + there are four different degrees between control and ease of use and two + different drawing APIs to provide for. + + So first of the easiest way to do font handling is by just providing a + `nk_user_font` struct which only requires the height in pixel of the used + font and a callback to calculate the width of a string. This way of handling + fonts is best fitted for using the normal draw shape command API were you + do all the text drawing yourself and the library does not require any kind + of deeper knowledge about which font handling mechanism you use. + + While the first approach works fine if you don't want to use the optional + vertex buffer output it is not enough if you do. To get font handling working + for these cases you have to provide two additional parameters inside the + `nk_user_font`. First a texture atlas handle used to draw text as subimages + of a bigger font atlas texture and a callback to query a character's glyph + information (offset, size, ...). So it is still possible to provide your own + font and use the vertex buffer output. + + The final approach if you do not have a font handling functionality or don't + want to use it in this library is by using the optional font baker. This API + is divided into a high- and low-level API with different priorities between + ease of use and control. Both API's can be used to create a font and + font atlas texture and can even be used with or without the vertex buffer + output. So it still uses the `nk_user_font` struct and the two different + approaches previously stated still work. + Now to the difference between the low level API and the high level API. The low + level API provides a lot of control over the baking process of the font and + provides total control over memory. It consists of a number of functions that + need to be called from begin to end and each step requires some additional + configuration, so it is a lot more complex than the high-level API. + If you don't want to do all the work required for using the low-level API + you can use the font atlas API. It provides the same functionality as the + low-level API but takes away some configuration and all of memory control and + in term provides a easier to use API. +*/ +struct nk_user_font_glyph; +typedef float(*nk_text_width_f)(nk_handle, float h, const char*, int len); +typedef void(*nk_query_font_glyph_f)(nk_handle handle, float font_height, + struct nk_user_font_glyph *glyph, + nk_rune codepoint, nk_rune next_codepoint); + +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +struct nk_user_font_glyph { + struct nk_vec2 uv[2]; + /* texture coordinates */ + struct nk_vec2 offset; + /* offset between top left and glyph */ + float width, height; + /* size of the glyph */ + float xadvance; + /* offset to the next glyph */ +}; +#endif + +struct nk_user_font { + nk_handle userdata; + /* user provided font handle */ + float height; + /* max height of the font */ + nk_text_width_f width; + /* font string width in pixel callback */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_query_font_glyph_f query; + /* font glyph callback to query drawing info */ + nk_handle texture; + /* texture handle to the used font atlas or texture */ +#endif +}; + +#ifdef NK_INCLUDE_FONT_BAKING +enum nk_font_coord_type { + NK_COORD_UV, /* texture coordinates inside font glyphs are clamped between 0-1 */ + NK_COORD_PIXEL /* texture coordinates inside font glyphs are in absolute pixel */ +}; + +struct nk_baked_font { + float height; + /* height of the font */ + float ascent, descent; + /* font glyphs ascent and descent */ + nk_rune glyph_offset; + /* glyph array offset inside the font glyph baking output array */ + nk_rune glyph_count; + /* number of glyphs of this font inside the glyph baking array output */ + const nk_rune *ranges; + /* font codepoint ranges as pairs of (from/to) and 0 as last element */ +}; + +struct nk_font_config { + struct nk_font_config *next; + /* NOTE: only used internally */ + void *ttf_blob; + /* pointer to loaded TTF file memory block. + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + nk_size ttf_size; + /* size of the loaded TTF file memory block + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + + unsigned char ttf_data_owned_by_atlas; + /* used inside font atlas: default to: 0*/ + unsigned char merge_mode; + /* merges this font into the last font */ + unsigned char pixel_snap; + /* align every character to pixel boundary (if true set oversample (1,1)) */ + unsigned char oversample_v, oversample_h; + /* rasterize at hight quality for sub-pixel position */ + unsigned char padding[3]; + + float size; + /* baked pixel height of the font */ + enum nk_font_coord_type coord_type; + /* texture coordinate format with either pixel or UV coordinates */ + struct nk_vec2 spacing; + /* extra pixel spacing between glyphs */ + const nk_rune *range; + /* list of unicode ranges (2 values per range, zero terminated) */ + struct nk_baked_font *font; + /* font to setup in the baking process: NOTE: not needed for font atlas */ + nk_rune fallback_glyph; + /* fallback glyph to use if a given rune is not found */ +}; + +struct nk_font_glyph { + nk_rune codepoint; + float xadvance; + float x0, y0, x1, y1, w, h; + float u0, v0, u1, v1; +}; + +struct nk_font { + struct nk_font *next; + struct nk_user_font handle; + struct nk_baked_font info; + float scale; + struct nk_font_glyph *glyphs; + const struct nk_font_glyph *fallback; + nk_rune fallback_codepoint; + nk_handle texture; + struct nk_font_config *config; +}; + +enum nk_font_atlas_format { + NK_FONT_ATLAS_ALPHA8, + NK_FONT_ATLAS_RGBA32 +}; + +struct nk_font_atlas { + void *pixel; + int tex_width; + int tex_height; + + struct nk_allocator permanent; + struct nk_allocator temporary; + struct nk_recti custom; + struct nk_cursor cursors[NK_CURSOR_COUNT]; + + int glyph_count; + struct nk_font_glyph *glyphs; + struct nk_font *default_font; + struct nk_font *fonts; + struct nk_font_config *config; + int font_num; +}; + +/* some language glyph codepoint ranges */ +NK_API const nk_rune *nk_font_default_glyph_ranges(void); +NK_API const nk_rune *nk_font_chinese_glyph_ranges(void); +NK_API const nk_rune *nk_font_cyrillic_glyph_ranges(void); +NK_API const nk_rune *nk_font_korean_glyph_ranges(void); + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_font_atlas_init_default(struct nk_font_atlas*); +#endif +NK_API void nk_font_atlas_init(struct nk_font_atlas*, struct nk_allocator*); +NK_API void nk_font_atlas_init_custom(struct nk_font_atlas*, struct nk_allocator *persistent, struct nk_allocator *transient); +NK_API void nk_font_atlas_begin(struct nk_font_atlas*); +NK_API struct nk_font_config nk_font_config(float pixel_height); +NK_API struct nk_font *nk_font_atlas_add(struct nk_font_atlas*, const struct nk_font_config*); +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* nk_font_atlas_add_default(struct nk_font_atlas*, float height, const struct nk_font_config*); +#endif +NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, nk_size size, float height, const struct nk_font_config *config); +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, float height, const struct nk_font_config*); +#endif +NK_API struct nk_font *nk_font_atlas_add_compressed(struct nk_font_atlas*, void *memory, nk_size size, float height, const struct nk_font_config*); +NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas*, const char *data, float height, const struct nk_font_config *config); +NK_API const void* nk_font_atlas_bake(struct nk_font_atlas*, int *width, int *height, enum nk_font_atlas_format); +NK_API void nk_font_atlas_end(struct nk_font_atlas*, nk_handle tex, struct nk_draw_null_texture*); +NK_API void nk_font_atlas_clear(struct nk_font_atlas*); +NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font*, nk_rune unicode); + +#endif + +/* =============================================================== + * + * DRAWING + * + * ===============================================================*/ +/* This library was designed to be render backend agnostic so it does + not draw anything to screen. Instead all drawn shapes, widgets + are made of, are buffered into memory and make up a command queue. + Each frame therefore fills the command buffer with draw commands + that then need to be executed by the user and his own render backend. + After that the command buffer needs to be cleared and a new frame can be + started. It is probably important to note that the command buffer is the main + drawing API and the optional vertex buffer API only takes this format and + converts it into a hardware accessible format. + + Draw commands are divided into filled shapes and shape outlines but only + filled shapes as well as line, curves and scissor are required to be provided. + All other shape drawing commands can be used but are not required. This was + done to allow the maximum number of render backends to be able to use this + library without you having to do additional work. +*/ +enum nk_command_type { + NK_COMMAND_NOP, + NK_COMMAND_SCISSOR, + NK_COMMAND_LINE, + NK_COMMAND_CURVE, + NK_COMMAND_RECT, + NK_COMMAND_RECT_FILLED, + NK_COMMAND_RECT_MULTI_COLOR, + NK_COMMAND_CIRCLE, + NK_COMMAND_CIRCLE_FILLED, + NK_COMMAND_ARC, + NK_COMMAND_ARC_FILLED, + NK_COMMAND_TRIANGLE, + NK_COMMAND_TRIANGLE_FILLED, + NK_COMMAND_POLYGON, + NK_COMMAND_POLYGON_FILLED, + NK_COMMAND_POLYLINE, + NK_COMMAND_TEXT, + NK_COMMAND_IMAGE +}; + +/* command base and header of every command inside the buffer */ +struct nk_command { + enum nk_command_type type; + nk_size next; +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_command_scissor { + struct nk_command header; + short x, y; + unsigned short w, h; +}; + +struct nk_command_line { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_color color; +}; + +struct nk_command_curve { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_vec2i ctrl[2]; + struct nk_color color; +}; + +struct nk_command_rect { + struct nk_command header; + unsigned short rounding; + unsigned short line_thickness; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_filled { + struct nk_command header; + unsigned short rounding; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_multi_color { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color left; + struct nk_color top; + struct nk_color bottom; + struct nk_color right; +}; + +struct nk_command_triangle { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_triangle_filled { + struct nk_command header; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_circle { + struct nk_command header; + short x, y; + unsigned short line_thickness; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_circle_filled { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_arc { + struct nk_command header; + short cx, cy; + unsigned short r; + unsigned short line_thickness; + float a[2]; + struct nk_color color; +}; + +struct nk_command_arc_filled { + struct nk_command header; + short cx, cy; + unsigned short r; + float a[2]; + struct nk_color color; +}; + +struct nk_command_polygon { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polygon_filled { + struct nk_command header; + struct nk_color color; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polyline { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_image { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_image img; + struct nk_color col; +}; + +struct nk_command_text { + struct nk_command header; + const struct nk_user_font *font; + struct nk_color background; + struct nk_color foreground; + short x, y; + unsigned short w, h; + float height; + int length; + char string[1]; +}; + +enum nk_command_clipping { + NK_CLIPPING_OFF = nk_false, + NK_CLIPPING_ON = nk_true +}; + +struct nk_command_buffer { + struct nk_buffer *base; + struct nk_rect clip; + int use_clipping; + nk_handle userdata; + nk_size begin, end, last; +}; + +/* shape outlines */ +NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color); +NK_API void nk_stroke_curve(struct nk_command_buffer*, float, float, float, float, float, float, float, float, float line_thickness, struct nk_color); +NK_API void nk_stroke_rect(struct nk_command_buffer*, struct nk_rect, float rounding, float line_thickness, struct nk_color); +NK_API void nk_stroke_circle(struct nk_command_buffer*, struct nk_rect, float line_thickness, struct nk_color); +NK_API void nk_stroke_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, float line_thickness, struct nk_color); +NK_API void nk_stroke_triangle(struct nk_command_buffer*, float, float, float, float, float, float, float line_thichness, struct nk_color); +NK_API void nk_stroke_polyline(struct nk_command_buffer*, float *points, int point_count, float line_thickness, struct nk_color col); +NK_API void nk_stroke_polygon(struct nk_command_buffer*, float*, int point_count, float line_thickness, struct nk_color); + +/* filled shades */ +NK_API void nk_fill_rect(struct nk_command_buffer*, struct nk_rect, float rounding, struct nk_color); +NK_API void nk_fill_rect_multi_color(struct nk_command_buffer*, struct nk_rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); +NK_API void nk_fill_circle(struct nk_command_buffer*, struct nk_rect, struct nk_color); +NK_API void nk_fill_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, struct nk_color); +NK_API void nk_fill_triangle(struct nk_command_buffer*, float x0, float y0, float x1, float y1, float x2, float y2, struct nk_color); +NK_API void nk_fill_polygon(struct nk_command_buffer*, float*, int point_count, struct nk_color); + +/* misc */ +NK_API void nk_push_scissor(struct nk_command_buffer*, struct nk_rect); +NK_API void nk_draw_image(struct nk_command_buffer*, struct nk_rect, const struct nk_image*, struct nk_color); +NK_API void nk_draw_text(struct nk_command_buffer*, struct nk_rect, const char *text, int len, const struct nk_user_font*, struct nk_color, struct nk_color); +NK_API const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); +NK_API const struct nk_command* nk__begin(struct nk_context*); + +/* =============================================================== + * + * INPUT + * + * ===============================================================*/ +struct nk_mouse_button { + int down; + unsigned int clicked; + struct nk_vec2 clicked_pos; +}; + +struct nk_mouse { + struct nk_mouse_button buttons[NK_BUTTON_MAX]; + struct nk_vec2 pos; + struct nk_vec2 prev; + struct nk_vec2 delta; + float scroll_delta; + unsigned char grab; + unsigned char grabbed; + unsigned char ungrab; +}; + +struct nk_key { + int down; + unsigned int clicked; +}; + +struct nk_keyboard { + struct nk_key keys[NK_KEY_MAX]; + char text[NK_INPUT_MAX]; + int text_len; +}; + +struct nk_input { + struct nk_keyboard keyboard; + struct nk_mouse mouse; +}; + +NK_API int nk_input_has_mouse_click(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_has_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API int nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect, int down); +NK_API int nk_input_is_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API int nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, int down); +NK_API int nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API int nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_mouse_pressed(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_mouse_released(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_key_pressed(const struct nk_input*, enum nk_keys); +NK_API int nk_input_is_key_released(const struct nk_input*, enum nk_keys); +NK_API int nk_input_is_key_down(const struct nk_input*, enum nk_keys); + + +/* =============================================================== + * + * DRAW LIST + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/* The optional vertex buffer draw list provides a 2D drawing context + with antialiasing functionality which takes basic filled or outlined shapes + or a path and outputs vertexes, elements and draw commands. + The actual draw list API is not required to be used directly while using this + library since converting the default library draw command output is done by + just calling `nk_convert` but I decided to still make this library accessible + since it can be useful. + + The draw list is based on a path buffering and polygon and polyline + rendering API which allows a lot of ways to draw 2D content to screen. + In fact it is probably more powerful than needed but allows even more crazy + things than this library provides by default. +*/ +typedef nk_ushort nk_draw_index; +enum nk_draw_list_stroke { + NK_STROKE_OPEN = nk_false, + /* build up path has no connection back to the beginning */ + NK_STROKE_CLOSED = nk_true + /* build up path has a connection back to the beginning */ +}; + +enum nk_draw_vertex_layout_attribute { + NK_VERTEX_POSITION, + NK_VERTEX_COLOR, + NK_VERTEX_TEXCOORD, + NK_VERTEX_ATTRIBUTE_COUNT +}; + +enum nk_draw_vertex_layout_format { + NK_FORMAT_SCHAR, + NK_FORMAT_SSHORT, + NK_FORMAT_SINT, + NK_FORMAT_UCHAR, + NK_FORMAT_USHORT, + NK_FORMAT_UINT, + NK_FORMAT_FLOAT, + NK_FORMAT_DOUBLE, + +NK_FORMAT_COLOR_BEGIN, + NK_FORMAT_R8G8B8 = NK_FORMAT_COLOR_BEGIN, + NK_FORMAT_R16G15B16, + NK_FORMAT_R32G32B32, + + NK_FORMAT_R8G8B8A8, + NK_FORMAT_R16G15B16A16, + NK_FORMAT_R32G32B32A32, + NK_FORMAT_R32G32B32A32_FLOAT, + NK_FORMAT_R32G32B32A32_DOUBLE, + + NK_FORMAT_RGB32, + NK_FORMAT_RGBA32, +NK_FORMAT_COLOR_END = NK_FORMAT_RGBA32, + NK_FORMAT_COUNT +}; + +#define NK_VERTEX_LAYOUT_END NK_VERTEX_ATTRIBUTE_COUNT,NK_FORMAT_COUNT,0 +struct nk_draw_vertex_layout_element { + enum nk_draw_vertex_layout_attribute attribute; + enum nk_draw_vertex_layout_format format; + nk_size offset; +}; + +struct nk_draw_command { + unsigned int elem_count; + /* number of elements in the current draw batch */ + struct nk_rect clip_rect; + /* current screen clipping rectangle */ + nk_handle texture; + /* current texture to set */ +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_draw_list { + struct nk_convert_config config; + struct nk_rect clip_rect; + struct nk_buffer *buffer; + struct nk_buffer *vertices; + struct nk_buffer *elements; + unsigned int element_count; + unsigned int vertex_count; + nk_size cmd_offset; + unsigned int cmd_count; + unsigned int path_count; + unsigned int path_offset; + struct nk_vec2 circle_vtx[12]; +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +/* draw list */ +NK_API void nk_draw_list_init(struct nk_draw_list*); +NK_API void nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config *config, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements); +NK_API void nk_draw_list_clear(struct nk_draw_list*); + +/* drawing */ +#define nk_draw_list_foreach(cmd, can, b) for((cmd)=nk__draw_list_begin(can, b); (cmd)!=0; (cmd)=nk__draw_list_next(cmd, b, can)) +NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list*, const struct nk_buffer*); +NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_draw_list*); +NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list*, const struct nk_buffer*); +NK_API void nk_draw_list_clear(struct nk_draw_list *list); + +/* path */ +NK_API void nk_draw_list_path_clear(struct nk_draw_list*); +NK_API void nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos); +NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list*, struct nk_vec2 center, float radius, int a_min, int a_max); +NK_API void nk_draw_list_path_arc_to(struct nk_draw_list*, struct nk_vec2 center, float radius, float a_min, float a_max, unsigned int segments); +NK_API void nk_draw_list_path_rect_to(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, float rounding); +NK_API void nk_draw_list_path_curve_to(struct nk_draw_list*, struct nk_vec2 p2, struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments); +NK_API void nk_draw_list_path_fill(struct nk_draw_list*, struct nk_color); +NK_API void nk_draw_list_path_stroke(struct nk_draw_list*, struct nk_color, enum nk_draw_list_stroke closed, float thickness); + +/* stroke */ +NK_API void nk_draw_list_stroke_line(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding, float thickness); +NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color, unsigned int segs, float thickness); +NK_API void nk_draw_list_stroke_curve(struct nk_draw_list*, struct nk_vec2 p0, struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, struct nk_color, unsigned int segments, float thickness); +NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list*, const struct nk_vec2 *pnts, const unsigned int cnt, struct nk_color, enum nk_draw_list_stroke, float thickness, enum nk_anti_aliasing); + +/* fill */ +NK_API void nk_draw_list_fill_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding); +NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); +NK_API void nk_draw_list_fill_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color); +NK_API void nk_draw_list_fill_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs); +NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list*, const struct nk_vec2 *points, const unsigned int count, struct nk_color, enum nk_anti_aliasing); + +/* misc */ +NK_API void nk_draw_list_add_image(struct nk_draw_list*, struct nk_image texture, struct nk_rect rect, struct nk_color); +NK_API void nk_draw_list_add_text(struct nk_draw_list*, const struct nk_user_font*, struct nk_rect, const char *text, int len, float font_height, struct nk_color); +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void nk_draw_list_push_userdata(struct nk_draw_list*, nk_handle userdata); +#endif + +#endif + +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +enum nk_style_item_type { + NK_STYLE_ITEM_COLOR, + NK_STYLE_ITEM_IMAGE +}; + +union nk_style_item_data { + struct nk_image image; + struct nk_color color; +}; + +struct nk_style_item { + enum nk_style_item_type type; + union nk_style_item_data data; +}; + +struct nk_style_text { + struct nk_color color; + struct nk_vec2 padding; +}; + +struct nk_style_button { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color text_background; + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + nk_flags text_alignment; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + struct nk_vec2 image_padding; + struct nk_vec2 touch_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle userdata); + void(*draw_end)(struct nk_command_buffer*, nk_handle userdata); +}; + +struct nk_style_toggle { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + + /* text */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + float spacing; + float border; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_selectable { + /* background (inactive) */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item pressed; + + /* background (active) */ + struct nk_style_item normal_active; + struct nk_style_item hover_active; + struct nk_style_item pressed_active; + + /* text color (inactive) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_pressed; + + /* text color (active) */ + struct nk_color text_normal_active; + struct nk_color text_hover_active; + struct nk_color text_pressed_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + float rounding; + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + struct nk_vec2 image_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_slider { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* background bar */ + struct nk_color bar_normal; + struct nk_color bar_hover; + struct nk_color bar_active; + struct nk_color bar_filled; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + + /* properties */ + float border; + float rounding; + float bar_height; + struct nk_vec2 padding; + struct nk_vec2 spacing; + struct nk_vec2 cursor_size; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_progress { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + struct nk_color cursor_border_color; + + /* properties */ + float rounding; + float border; + float cursor_border; + float cursor_rounding; + struct nk_vec2 padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_scrollbar { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + struct nk_color cursor_border_color; + + /* properties */ + float border; + float rounding; + float border_cursor; + float rounding_cursor; + struct nk_vec2 padding; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_edit { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + struct nk_style_scrollbar scrollbar; + + /* cursor */ + struct nk_color cursor_normal; + struct nk_color cursor_hover; + struct nk_color cursor_text_normal; + struct nk_color cursor_text_hover; + + /* text (unselected) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + + /* text (selected) */ + struct nk_color selected_normal; + struct nk_color selected_hover; + struct nk_color selected_text_normal; + struct nk_color selected_text_hover; + + /* properties */ + float border; + float rounding; + float cursor_size; + struct nk_vec2 scrollbar_size; + struct nk_vec2 padding; + float row_padding; +}; + +struct nk_style_property { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbols */ + enum nk_symbol_type sym_left; + enum nk_symbol_type sym_right; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + + struct nk_style_edit edit; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_chart { + /* colors */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color selected_color; + struct nk_color color; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; +}; + +struct nk_style_combo { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* label */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbol */ + struct nk_color symbol_normal; + struct nk_color symbol_hover; + struct nk_color symbol_active; + + /* button */ + struct nk_style_button button; + enum nk_symbol_type sym_normal; + enum nk_symbol_type sym_hover; + enum nk_symbol_type sym_active; + + /* properties */ + float border; + float rounding; + struct nk_vec2 content_padding; + struct nk_vec2 button_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_tab { + /* background */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color text; + + /* button */ + struct nk_style_button tab_maximize_button; + struct nk_style_button tab_minimize_button; + struct nk_style_button node_maximize_button; + struct nk_style_button node_minimize_button; + enum nk_symbol_type sym_minimize; + enum nk_symbol_type sym_maximize; + + /* properties */ + float border; + float rounding; + float indent; + struct nk_vec2 padding; + struct nk_vec2 spacing; +}; + +enum nk_style_header_align { + NK_HEADER_LEFT, + NK_HEADER_RIGHT +}; + +struct nk_style_window_header { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + + /* button */ + struct nk_style_button close_button; + struct nk_style_button minimize_button; + enum nk_symbol_type close_symbol; + enum nk_symbol_type minimize_symbol; + enum nk_symbol_type maximize_symbol; + + /* title */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* properties */ + enum nk_style_header_align align; + struct nk_vec2 padding; + struct nk_vec2 label_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_window { + struct nk_style_window_header header; + struct nk_style_item fixed_background; + struct nk_color background; + + struct nk_color border_color; + struct nk_color popup_border_color; + struct nk_color combo_border_color; + struct nk_color contextual_border_color; + struct nk_color menu_border_color; + struct nk_color group_border_color; + struct nk_color tooltip_border_color; + struct nk_style_item scaler; + + float border; + float combo_border; + float contextual_border; + float menu_border; + float group_border; + float tooltip_border; + float popup_border; + + float rounding; + struct nk_vec2 spacing; + struct nk_vec2 scrollbar_size; + struct nk_vec2 min_size; + + struct nk_vec2 padding; + struct nk_vec2 group_padding; + struct nk_vec2 popup_padding; + struct nk_vec2 combo_padding; + struct nk_vec2 contextual_padding; + struct nk_vec2 menu_padding; + struct nk_vec2 tooltip_padding; +}; + +struct nk_style { + const struct nk_user_font *font; + const struct nk_cursor *cursors[NK_CURSOR_COUNT]; + const struct nk_cursor *cursor_active; + struct nk_cursor *cursor_last; + int cursor_visible; + + struct nk_style_text text; + struct nk_style_button button; + struct nk_style_button contextual_button; + struct nk_style_button menu_button; + struct nk_style_toggle option; + struct nk_style_toggle checkbox; + struct nk_style_selectable selectable; + struct nk_style_slider slider; + struct nk_style_progress progress; + struct nk_style_property property; + struct nk_style_edit edit; + struct nk_style_chart chart; + struct nk_style_scrollbar scrollh; + struct nk_style_scrollbar scrollv; + struct nk_style_tab tab; + struct nk_style_combo combo; + struct nk_style_window window; +}; + +NK_API struct nk_style_item nk_style_item_image(struct nk_image img); +NK_API struct nk_style_item nk_style_item_color(struct nk_color); +NK_API struct nk_style_item nk_style_item_hide(void); + +/*============================================================== + * PANEL + * =============================================================*/ +#ifndef NK_CHART_MAX_SLOT +#define NK_CHART_MAX_SLOT 4 +#endif + +enum nk_panel_type { + NK_PANEL_WINDOW = NK_FLAG(0), + NK_PANEL_GROUP = NK_FLAG(1), + NK_PANEL_POPUP = NK_FLAG(2), + NK_PANEL_CONTEXTUAL = NK_FLAG(4), + NK_PANEL_COMBO = NK_FLAG(5), + NK_PANEL_MENU = NK_FLAG(6), + NK_PANEL_TOOLTIP = NK_FLAG(7) +}; +enum nk_panel_set { + NK_PANEL_SET_NONBLOCK = NK_PANEL_CONTEXTUAL|NK_PANEL_COMBO|NK_PANEL_MENU|NK_PANEL_TOOLTIP, + NK_PANEL_SET_POPUP = NK_PANEL_SET_NONBLOCK|NK_PANEL_POPUP, + NK_PANEL_SET_SUB = NK_PANEL_SET_POPUP|NK_PANEL_GROUP +}; + +struct nk_chart_slot { + enum nk_chart_type type; + struct nk_color color; + struct nk_color highlight; + float min, max, range; + int count; + struct nk_vec2 last; + int index; +}; + +struct nk_chart { + struct nk_chart_slot slots[NK_CHART_MAX_SLOT]; + int slot; + float x, y, w, h; +}; + +struct nk_row_layout { + int type; + int index; + float height; + int columns; + const float *ratio; + float item_width, item_height; + float item_offset; + float filled; + struct nk_rect item; + int tree_depth; +}; + +struct nk_popup_buffer { + nk_size begin; + nk_size parent; + nk_size last; + nk_size end; + int active; +}; + +struct nk_menu_state { + float x, y, w, h; + struct nk_scroll offset; +}; + +struct nk_panel { + enum nk_panel_type type; + nk_flags flags; + struct nk_rect bounds; + struct nk_scroll *offset; + float at_x, at_y, max_x; + float footer_height; + float header_height; + float border; + unsigned int has_scrolling; + struct nk_rect clip; + struct nk_menu_state menu; + struct nk_row_layout row; + struct nk_chart chart; + struct nk_popup_buffer popup_buffer; + struct nk_command_buffer *buffer; + struct nk_panel *parent; +}; + +/*============================================================== + * WINDOW + * =============================================================*/ +#ifndef NK_WINDOW_MAX_NAME +#define NK_WINDOW_MAX_NAME 64 +#endif + +struct nk_table; +enum nk_window_flags { + NK_WINDOW_PRIVATE = NK_FLAG(10), + NK_WINDOW_DYNAMIC = NK_WINDOW_PRIVATE, + /* special window type growing up in height while being filled to a certain maximum height */ + NK_WINDOW_ROM = NK_FLAG(11), + /* sets the window into a read only mode and does not allow input changes */ + NK_WINDOW_HIDDEN = NK_FLAG(12), + /* Hides the window and stops any window interaction and drawing */ + NK_WINDOW_CLOSED = NK_FLAG(13), + /* Directly closes and frees the window at the end of the frame */ + NK_WINDOW_MINIMIZED = NK_FLAG(14), + /* marks the window as minimized */ + NK_WINDOW_REMOVE_ROM = NK_FLAG(15) + /* Removes the read only mode at the end of the window */ +}; + +struct nk_popup_state { + struct nk_window *win; + enum nk_panel_type type; + nk_hash name; + int active; + unsigned combo_count; + unsigned con_count, con_old; + unsigned active_con; + struct nk_rect header; +}; + +struct nk_edit_state { + nk_hash name; + unsigned int seq; + unsigned int old; + int active, prev; + int cursor; + int sel_start; + int sel_end; + struct nk_scroll scrollbar; + unsigned char mode; + unsigned char single_line; +}; + +struct nk_property_state { + int active, prev; + char buffer[NK_MAX_NUMBER_BUFFER]; + int length; + int cursor; + nk_hash name; + unsigned int seq; + unsigned int old; + int state; +}; + +struct nk_window { + unsigned int seq; + nk_hash name; + char name_string[NK_WINDOW_MAX_NAME]; + nk_flags flags; + struct nk_rect bounds; + struct nk_scroll scrollbar; + struct nk_command_buffer buffer; + struct nk_panel *layout; + float scrollbar_hiding_timer; + + /* persistent widget state */ + struct nk_property_state property; + struct nk_popup_state popup; + struct nk_edit_state edit; + unsigned int scrolled; + + struct nk_table *tables; + unsigned short table_count; + unsigned short table_size; + + /* window list hooks */ + struct nk_window *next; + struct nk_window *prev; + struct nk_window *parent; +}; + +/*============================================================== + * STACK + * =============================================================*/ +#ifndef NK_BUTTON_BEHAVIOR_STACK_SIZE +#define NK_BUTTON_BEHAVIOR_STACK_SIZE 8 +#endif + +#ifndef NK_FONT_STACK_SIZE +#define NK_FONT_STACK_SIZE 8 +#endif + +#ifndef NK_STYLE_ITEM_STACK_SIZE +#define NK_STYLE_ITEM_STACK_SIZE 16 +#endif + +#ifndef NK_FLOAT_STACK_SIZE +#define NK_FLOAT_STACK_SIZE 32 +#endif + +#ifndef NK_VECTOR_STACK_SIZE +#define NK_VECTOR_STACK_SIZE 16 +#endif + +#ifndef NK_FLAGS_STACK_SIZE +#define NK_FLAGS_STACK_SIZE 32 +#endif + +#ifndef NK_COLOR_STACK_SIZE +#define NK_COLOR_STACK_SIZE 32 +#endif + +#define NK_CONFIGURATION_STACK_TYPE(prefix, name, type)\ + struct nk_config_stack_##name##_element {\ + prefix##_##type *address;\ + prefix##_##type old_value;\ + } +#define NK_CONFIG_STACK(type,size)\ + struct nk_config_stack_##type {\ + int head;\ + struct nk_config_stack_##type##_element elements[size];\ + } + +#define nk_float float +NK_CONFIGURATION_STACK_TYPE(struct nk, style_item, style_item); +NK_CONFIGURATION_STACK_TYPE(nk ,float, float); +NK_CONFIGURATION_STACK_TYPE(struct nk, vec2, vec2); +NK_CONFIGURATION_STACK_TYPE(nk ,flags, flags); +NK_CONFIGURATION_STACK_TYPE(struct nk, color, color); +NK_CONFIGURATION_STACK_TYPE(const struct nk, user_font, user_font*); +NK_CONFIGURATION_STACK_TYPE(enum nk, button_behavior, button_behavior); + +NK_CONFIG_STACK(style_item, NK_STYLE_ITEM_STACK_SIZE); +NK_CONFIG_STACK(float, NK_FLOAT_STACK_SIZE); +NK_CONFIG_STACK(vec2, NK_VECTOR_STACK_SIZE); +NK_CONFIG_STACK(flags, NK_FLAGS_STACK_SIZE); +NK_CONFIG_STACK(color, NK_COLOR_STACK_SIZE); +NK_CONFIG_STACK(user_font, NK_FONT_STACK_SIZE); +NK_CONFIG_STACK(button_behavior, NK_BUTTON_BEHAVIOR_STACK_SIZE); + +struct nk_configuration_stacks { + struct nk_config_stack_style_item style_items; + struct nk_config_stack_float floats; + struct nk_config_stack_vec2 vectors; + struct nk_config_stack_flags flags; + struct nk_config_stack_color colors; + struct nk_config_stack_user_font fonts; + struct nk_config_stack_button_behavior button_behaviors; +}; + +/*============================================================== + * CONTEXT + * =============================================================*/ +#define NK_VALUE_PAGE_CAPACITY ((sizeof(struct nk_window) / sizeof(nk_uint)) / 2) + +struct nk_table { + unsigned int seq; + nk_hash keys[NK_VALUE_PAGE_CAPACITY]; + nk_uint values[NK_VALUE_PAGE_CAPACITY]; + struct nk_table *next, *prev; +}; + +union nk_page_data { + struct nk_table tbl; + struct nk_window win; +}; + +struct nk_page_element { + union nk_page_data data; + struct nk_page_element *next; + struct nk_page_element *prev; +}; + +struct nk_page { + unsigned size; + struct nk_page *next; + struct nk_page_element win[1]; +}; + +struct nk_pool { + struct nk_allocator alloc; + enum nk_allocation_type type; + unsigned int page_count; + struct nk_page *pages; + struct nk_page_element *freelist; + unsigned capacity; + nk_size size; + nk_size cap; +}; + +struct nk_context { +/* public: can be accessed freely */ + struct nk_input input; + struct nk_style style; + struct nk_buffer memory; + struct nk_clipboard clip; + nk_flags last_widget_state; + float delta_time_seconds; + enum nk_button_behavior button_behavior; + struct nk_configuration_stacks stacks; + +/* private: + should only be accessed if you + know what you are doing */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + struct nk_draw_list draw_list; +#endif +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif + /* text editor objects are quite big because of an internal + * undo/redo stack. Therefore does not make sense to have one for + * each window for temporary use cases, so I only provide *one* instance + * for all windows. This works because the content is cleared anyway */ + struct nk_text_edit text_edit; + /* draw buffer used for overlay drawing operation like cursor */ + struct nk_command_buffer overlay; + + /* windows */ + int build; + int use_pool; + struct nk_pool pool; + struct nk_window *begin; + struct nk_window *end; + struct nk_window *active; + struct nk_window *current; + struct nk_page_element *freelist; + unsigned int count; + unsigned int seq; +}; + +/* ============================================================== + * MATH + * =============================================================== */ +#define NK_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define NK_MAX(a,b) ((a) < (b) ? (b) : (a)) +#define NK_CLAMP(i,v,x) (NK_MAX(NK_MIN(v,x), i)) + +#define NK_PI 3.141592654f +#define NK_UTF_INVALID 0xFFFD +#define NK_MAX_FLOAT_PRECISION 2 + +#define NK_UNUSED(x) ((void)(x)) +#define NK_SATURATE(x) (NK_MAX(0, NK_MIN(1.0f, x))) +#define NK_LEN(a) (sizeof(a)/sizeof(a)[0]) +#define NK_ABS(a) (((a) < 0) ? -(a) : (a)) +#define NK_BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define NK_INBOX(px, py, x, y, w, h)\ + (NK_BETWEEN(px,x,x+w) && NK_BETWEEN(py,y,y+h)) +#define NK_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \ + (!(((x1 > (x0 + w0)) || ((x1 + w1) < x0) || (y1 > (y0 + h0)) || (y1 + h1) < y0))) +#define NK_CONTAINS(x, y, w, h, bx, by, bw, bh)\ + (NK_INBOX(x,y, bx, by, bw, bh) && NK_INBOX(x+w,y+h, bx, by, bw, bh)) + +#define nk_vec2_sub(a, b) nk_vec2((a).x - (b).x, (a).y - (b).y) +#define nk_vec2_add(a, b) nk_vec2((a).x + (b).x, (a).y + (b).y) +#define nk_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y) +#define nk_vec2_muls(a, t) nk_vec2((a).x * (t), (a).y * (t)) + +#define nk_ptr_add(t, p, i) ((t*)((void*)((nk_byte*)(p) + (i)))) +#define nk_ptr_add_const(t, p, i) ((const t*)((const void*)((const nk_byte*)(p) + (i)))) +#define nk_zero_struct(s) nk_zero(&s, sizeof(s)) + +/* ============================================================== + * ALIGNMENT + * =============================================================== */ +/* Pointer to Integer type conversion for pointer alignment */ +#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC*/ +# define NK_UINT_TO_PTR(x) ((void*)(__PTRDIFF_TYPE__)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(__PTRDIFF_TYPE__)(x)) +#elif !defined(__GNUC__) /* works for compilers other than LLVM */ +# define NK_UINT_TO_PTR(x) ((void*)&((char*)0)[x]) +# define NK_PTR_TO_UINT(x) ((nk_size)(((char*)x)-(char*)0)) +#elif defined(NK_USE_FIXED_TYPES) /* used if we have */ +# define NK_UINT_TO_PTR(x) ((void*)(uintptr_t)(x)) +# define NK_PTR_TO_UINT(x) ((uintptr_t)(x)) +#else /* generates warning but works */ +# define NK_UINT_TO_PTR(x) ((void*)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(x)) +#endif + +#define NK_ALIGN_PTR(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x) + (mask-1)) & ~(mask-1)))) +#define NK_ALIGN_PTR_BACK(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x)) & ~(mask-1)))) + +#define NK_OFFSETOF(st,m) ((nk_ptr)&(((st*)0)->m)) +#define NK_CONTAINER_OF(ptr,type,member)\ + (type*)((void*)((char*)(1 ? (ptr): &((type*)0)->member) - NK_OFFSETOF(type, member))) + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +template struct nk_alignof; +template struct nk_helper{enum {value = size_diff};}; +template struct nk_helper{enum {value = nk_alignof::value};}; +template struct nk_alignof{struct Big {T x; char c;}; enum { + diff = sizeof(Big) - sizeof(T), value = nk_helper::value};}; +#define NK_ALIGNOF(t) (nk_alignof::value); +#elif defined(_MSC_VER) +#define NK_ALIGNOF(t) (__alignof(t)) +#else +#define NK_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0) +#endif + +#endif /* NK_H_ */ +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_IMPLEMENTATION + +#ifndef NK_POOL_DEFAULT_CAPACITY +#define NK_POOL_DEFAULT_CAPACITY 16 +#endif + +#ifndef NK_DEFAULT_COMMAND_BUFFER_SIZE +#define NK_DEFAULT_COMMAND_BUFFER_SIZE (4*1024) +#endif + +#ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE +#define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024) +#endif + +/* standard library headers */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +#include /* malloc, free */ +#endif +#ifdef NK_INCLUDE_STANDARD_IO +#include /* fopen, fclose,... */ +#endif +#ifdef NK_INCLUDE_STANDARD_VARARGS +#include /* valist, va_start, va_end, ... */ +#endif +#ifndef NK_ASSERT +#include +#define NK_ASSERT(expr) assert(expr) +#endif + +#ifndef NK_MEMSET +#define NK_MEMSET nk_memset +#endif +#ifndef NK_MEMCPY +#define NK_MEMCPY nk_memcopy +#endif +#ifndef NK_SQRT +#define NK_SQRT nk_sqrt +#endif +#ifndef NK_SIN +#define NK_SIN nk_sin +#endif +#ifndef NK_COS +#define NK_COS nk_cos +#endif +#ifndef NK_STRTOD +#define NK_STRTOD nk_strtod +#endif +#ifndef NK_DTOA +#define NK_DTOA nk_dtoa +#endif + +#define NK_DEFAULT (-1) + +#ifndef NK_VSNPRINTF +/* If your compiler does support `vsnprintf` I would highly recommend + * defining this to vsnprintf instead since `vsprintf` is basically + * unbelievable unsafe and should *NEVER* be used. But I have to support + * it since C89 only provides this unsafe version. */ + #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) ||\ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) ||\ + (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) ||\ + defined(_ISOC99_SOURCE) || defined(_BSD_SOURCE) + #define NK_VSNPRINTF(s,n,f,a) vsnprintf(s,n,f,a) + #else + #define NK_VSNPRINTF(s,n,f,a) vsprintf(s,f,a) + #endif +#endif + +#define NK_SCHAR_MIN (-127) +#define NK_SCHAR_MAX 127 +#define NK_UCHAR_MIN 0 +#define NK_UCHAR_MAX 256 +#define NK_SSHORT_MIN (-32767) +#define NK_SSHORT_MAX 32767 +#define NK_USHORT_MIN 0 +#define NK_USHORT_MAX 65535 +#define NK_SINT_MIN (-2147483647) +#define NK_SINT_MAX 2147483647 +#define NK_UINT_MIN 0 +#define NK_UINT_MAX 4294967295 + +/* Make sure correct type size: + * This will fire with a negative subscript error if the type sizes + * are set incorrectly by the compiler, and compile out if not */ +NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_ptr) == sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); +NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); +NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); +NK_STATIC_ASSERT(sizeof(nk_short) == 2); +NK_STATIC_ASSERT(sizeof(nk_uint) == 4); +NK_STATIC_ASSERT(sizeof(nk_int) == 4); +NK_STATIC_ASSERT(sizeof(nk_byte) == 1); + +NK_GLOBAL const struct nk_rect nk_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; +#define NK_FLOAT_PRECISION 0.00000000000001 + +NK_GLOBAL const struct nk_color nk_red = {255,0,0,255}; +NK_GLOBAL const struct nk_color nk_green = {0,255,0,255}; +NK_GLOBAL const struct nk_color nk_blue = {0,0,255,255}; +NK_GLOBAL const struct nk_color nk_white = {255,255,255,255}; +NK_GLOBAL const struct nk_color nk_black = {0,0,0,255}; +NK_GLOBAL const struct nk_color nk_yellow = {255,255,0,255}; + +/* + * ============================================================== + * + * MATH + * + * =============================================================== + */ +/* Since nuklear is supposed to work on all systems providing floating point + math without any dependencies I also had to implement my own math functions + for sqrt, sin and cos. Since the actual highly accurate implementations for + the standard library functions are quite complex and I do not need high + precision for my use cases I use approximations. + + Sqrt + ---- + For square root nuklear uses the famous fast inverse square root: + https://en.wikipedia.org/wiki/Fast_inverse_square_root with + slightly tweaked magic constant. While on todays hardware it is + probably not faster it is still fast and accurate enough for + nuklear's use cases. IMPORTANT: this requires float format IEEE 754 + + Sine/Cosine + ----------- + All constants inside both function are generated Remez's minimax + approximations for value range 0...2*PI. The reason why I decided to + approximate exactly that range is that nuklear only needs sine and + cosine to generate circles which only requires that exact range. + In addition I used Remez instead of Taylor for additional precision: + www.lolengine.net/blog/2011/12/21/better-function-approximatations. + + The tool I used to generate constants for both sine and cosine + (it can actually approximate a lot more functions) can be + found here: www.lolengine.net/wiki/oss/lolremez +*/ +NK_INTERN float +nk_inv_sqrt(float number) +{ + float x2; + const float threehalfs = 1.5f; + union {nk_uint i; float f;} conv = {0}; + conv.f = number; + x2 = number * 0.5f; + conv.i = 0x5f375A84 - (conv.i >> 1); + conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); + return conv.f; +} + +NK_INTERN float +nk_sqrt(float x) +{ + return x * nk_inv_sqrt(x); +} + +NK_INTERN float +nk_sin(float x) +{ + NK_STORAGE const float a0 = +1.91059300966915117e-31f; + NK_STORAGE const float a1 = +1.00086760103908896f; + NK_STORAGE const float a2 = -1.21276126894734565e-2f; + NK_STORAGE const float a3 = -1.38078780785773762e-1f; + NK_STORAGE const float a4 = -2.67353392911981221e-2f; + NK_STORAGE const float a5 = +2.08026600266304389e-2f; + NK_STORAGE const float a6 = -3.03996055049204407e-3f; + NK_STORAGE const float a7 = +1.38235642404333740e-4f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); +} + +NK_INTERN float +nk_cos(float x) +{ + NK_STORAGE const float a0 = +1.00238601909309722f; + NK_STORAGE const float a1 = -3.81919947353040024e-2f; + NK_STORAGE const float a2 = -3.94382342128062756e-1f; + NK_STORAGE const float a3 = -1.18134036025221444e-1f; + NK_STORAGE const float a4 = +1.07123798512170878e-1f; + NK_STORAGE const float a5 = -1.86637164165180873e-2f; + NK_STORAGE const float a6 = +9.90140908664079833e-4f; + NK_STORAGE const float a7 = -5.23022132118824778e-14f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); +} + +NK_INTERN nk_uint +nk_round_up_pow2(nk_uint v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +NK_API struct nk_rect +nk_get_null_rect(void) +{ + return nk_null_rect; +} + +NK_API struct nk_rect +nk_rect(float x, float y, float w, float h) +{ + struct nk_rect r; + r.x = x, r.y = y; + r.w = w, r.h = h; + return r; +} + +NK_API struct nk_rect +nk_recti(int x, int y, int w, int h) +{ + struct nk_rect r; + r.x = (float)x; + r.y = (float)y; + r.w = (float)w; + r.h = (float)h; + return r; +} + +NK_API struct nk_rect +nk_recta(struct nk_vec2 pos, struct nk_vec2 size) +{ + return nk_rect(pos.x, pos.y, size.x, size.y); +} + +NK_API struct nk_rect +nk_rectv(const float *r) +{ + return nk_rect(r[0], r[1], r[2], r[3]); +} + +NK_API struct nk_rect +nk_rectiv(const int *r) +{ + return nk_recti(r[0], r[1], r[2], r[3]); +} + +NK_API struct nk_vec2 +nk_rect_pos(struct nk_rect r) +{ + struct nk_vec2 ret; + ret.x = r.x; ret.y = r.y; + return ret; +} + +NK_API struct nk_vec2 +nk_rect_size(struct nk_rect r) +{ + struct nk_vec2 ret; + ret.x = r.w; ret.y = r.h; + return ret; +} + +NK_INTERN struct nk_rect +nk_shrink_rect(struct nk_rect r, float amount) +{ + struct nk_rect res; + r.w = NK_MAX(r.w, 2 * amount); + r.h = NK_MAX(r.h, 2 * amount); + res.x = r.x + amount; + res.y = r.y + amount; + res.w = r.w - 2 * amount; + res.h = r.h - 2 * amount; + return res; +} + +NK_INTERN struct nk_rect +nk_pad_rect(struct nk_rect r, struct nk_vec2 pad) +{ + r.w = NK_MAX(r.w, 2 * pad.x); + r.h = NK_MAX(r.h, 2 * pad.y); + r.x += pad.x; r.y += pad.y; + r.w -= 2 * pad.x; + r.h -= 2 * pad.y; + return r; +} + +NK_API struct nk_vec2 +nk_vec2(float x, float y) +{ + struct nk_vec2 ret; + ret.x = x; ret.y = y; + return ret; +} + +NK_API struct nk_vec2 +nk_vec2i(int x, int y) +{ + struct nk_vec2 ret; + ret.x = (float)x; + ret.y = (float)y; + return ret; +} + +NK_API struct nk_vec2 +nk_vec2v(const float *v) +{ + return nk_vec2(v[0], v[1]); +} + +NK_API struct nk_vec2 +nk_vec2iv(const int *v) +{ + return nk_vec2i(v[0], v[1]); +} + +/* + * ============================================================== + * + * UTIL + * + * =============================================================== + */ +NK_INTERN int nk_str_match_here(const char *regexp, const char *text); +NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text); +NK_INTERN int nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} +NK_INTERN int nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} +NK_INTERN int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} +NK_INTERN int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} + +NK_INTERN void* +nk_memcopy(void *dst0, const void *src0, nk_size length) +{ + nk_ptr t; + char *dst = (char*)dst0; + const char *src = (const char*)src0; + if (length == 0 || dst == src) + goto done; + + #define nk_word int + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize-1) + #define NK_TLOOP(s) if (t) NK_TLOOP1(s) + #define NK_TLOOP1(s) do { s; } while (--t) + + if (dst < src) { + t = (nk_ptr)src; /* only need low bits */ + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length < nk_wsize) + t = length; + else + t = nk_wsize - (t & nk_wmask); + length -= t; + NK_TLOOP1(*dst++ = *src++); + } + t = length / nk_wsize; + NK_TLOOP(*(nk_word*)(void*)dst = *(const nk_word*)(const void*)src; + src += nk_wsize; dst += nk_wsize); + t = length & nk_wmask; + NK_TLOOP(*dst++ = *src++); + } else { + src += length; + dst += length; + t = (nk_ptr)src; + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length <= nk_wsize) + t = length; + else + t &= nk_wmask; + length -= t; + NK_TLOOP1(*--dst = *--src); + } + t = length / nk_wsize; + NK_TLOOP(src -= nk_wsize; dst -= nk_wsize; + *(nk_word*)(void*)dst = *(const nk_word*)(const void*)src); + t = length & nk_wmask; + NK_TLOOP(*--dst = *--src); + } + #undef nk_word + #undef nk_wsize + #undef nk_wmask + #undef NK_TLOOP + #undef NK_TLOOP1 +done: + return (dst0); +} + +NK_INTERN void +nk_memset(void *ptr, int c0, nk_size size) +{ + #define nk_word unsigned + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize - 1) + nk_byte *dst = (nk_byte*)ptr; + unsigned c = 0; + nk_size t = 0; + + if ((c = (nk_byte)c0) != 0) { + c = (c << 8) | c; /* at least 16-bits */ + if (sizeof(unsigned int) > 2) + c = (c << 16) | c; /* at least 32-bits*/ + } + + /* too small of a word count */ + dst = (nk_byte*)ptr; + if (size < 3 * nk_wsize) { + while (size--) *dst++ = (nk_byte)c0; + return; + } + + /* align destination */ + if ((t = NK_PTR_TO_UINT(dst) & nk_wmask) != 0) { + t = nk_wsize -t; + size -= t; + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + /* fill word */ + t = size / nk_wsize; + do { + *(nk_word*)((void*)dst) = c; + dst += nk_wsize; + } while (--t != 0); + + /* fill trailing bytes */ + t = (size & nk_wmask); + if (t != 0) { + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + #undef nk_word + #undef nk_wsize + #undef nk_wmask +} + +NK_INTERN void +nk_zero(void *ptr, nk_size size) +{ + NK_ASSERT(ptr); + NK_MEMSET(ptr, 0, size); +} + +NK_API int +nk_strlen(const char *str) +{ + int siz = 0; + NK_ASSERT(str); + while (str && *str++ != '\0') siz++; + return siz; +} + +NK_API int +nk_strtoi(const char *str, char **endptr) +{ + int neg = 1; + const char *p = str; + int value = 0; + + NK_ASSERT(str); + if (!str) return 0; + + /* skip whitespace */ + while (*p == ' ') p++; + if (*p == '-') { + neg = -1; + p++; + } + while (*p && *p >= '0' && *p <= '9') { + value = value * 10 + (int) (*p - '0'); + p++; + } + if (endptr) + *endptr = (char*)p; + return neg*value; +} + +NK_API double +nk_strtod(const char *str, char **endptr) +{ + double m; + double neg = 1.0; + const char *p = str; + double value = 0; + double number = 0; + + NK_ASSERT(str); + if (!str) return 0; + + /* skip whitespace */ + while (*p == ' ') p++; + if (*p == '-') { + neg = -1.0; + p++; + } + + while (*p && *p != '.' && *p != 'e') { + value = value * 10.0 + (double) (*p - '0'); + p++; + } + + if (*p == '.') { + p++; + for(m = 0.1; *p && *p != 'e'; p++ ) { + value = value + (double) (*p - '0') * m; + m *= 0.1; + } + } + if (*p == 'e') { + int i, pow, div; + p++; + if (*p == '-') { + div = nk_true; + p++; + } else if (*p == '+') { + div = nk_false; + p++; + } else div = nk_false; + + for (pow = 0; *p; p++) + pow = pow * 10 + (int) (*p - '0'); + + for (m = 1.0, i = 0; i < pow; i++) + m *= 10.0; + + if (div) + value /= m; + else value *= m; + } + number = value * neg; + if (endptr) + *endptr = (char*)p; + return number; +} + +NK_API float +nk_strtof(const char *str, char **endptr) +{ + float float_value; + double double_value; + double_value = NK_STRTOD(str, endptr); + float_value = (float)double_value; + return float_value; +} + +NK_API int +nk_stricmp(const char *s1, const char *s2) +{ + nk_int c1,c2,d; + do { + c1 = *s1++; + c2 = *s2++; + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} + +NK_API int +nk_stricmpn(const char *s1, const char *s2, int n) +{ + int c1,c2,d; + NK_ASSERT(n >= 0); + do { + c1 = *s1++; + c2 = *s2++; + if (!n--) return 0; + + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} + +NK_INTERN int +nk_str_match_here(const char *regexp, const char *text) +{ + if (regexp[0] == '\0') + return 1; + if (regexp[1] == '*') + return nk_str_match_star(regexp[0], regexp+2, text); + if (regexp[0] == '$' && regexp[1] == '\0') + return *text == '\0'; + if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) + return nk_str_match_here(regexp+1, text+1); + return 0; +} + +NK_INTERN int +nk_str_match_star(int c, const char *regexp, const char *text) +{ + do {/* a '* matches zero or more instances */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text != '\0' && (*text++ == c || c == '.')); + return 0; +} + +NK_API int +nk_strfilter(const char *text, const char *regexp) +{ + /* + c matches any literal character c + . matches any single character + ^ matches the beginning of the input string + $ matches the end of the input string + * matches zero or more occurrences of the previous character*/ + if (regexp[0] == '^') + return nk_str_match_here(regexp+1, text); + do { /* must look even if string is empty */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text++ != '\0'); + return 0; +} + +NK_API int +nk_strmatch_fuzzy_text(const char *str, int str_len, + const char *pattern, int *out_score) +{ + /* Returns true if each character in pattern is found sequentially within str + * if found then outScore is also set. Score value has no intrinsic meaning. + * Range varies with pattern. Can only compare scores with same search pattern. */ + + /* ------- scores --------- */ + /* bonus for adjacent matches */ + #define NK_ADJACENCY_BONUS 5 + /* bonus if match occurs after a separator */ + #define NK_SEPARATOR_BONUS 10 + /* bonus if match is uppercase and prev is lower */ + #define NK_CAMEL_BONUS 10 + /* penalty applied for every letter in str before the first match */ + #define NK_LEADING_LETTER_PENALTY (-3) + /* maximum penalty for leading letters */ + #define NK_MAX_LEADING_LETTER_PENALTY (-9) + /* penalty for every letter that doesn't matter */ + #define NK_UNMATCHED_LETTER_PENALTY (-1) + + /* loop variables */ + int score = 0; + char const * pattern_iter = pattern; + int str_iter = 0; + int prev_matched = nk_false; + int prev_lower = nk_false; + /* true so if first letter match gets separator bonus*/ + int prev_separator = nk_true; + + /* use "best" matched letter if multiple string letters match the pattern */ + char const * best_letter = 0; + int best_letter_score = 0; + + /* loop over strings */ + NK_ASSERT(str); + NK_ASSERT(pattern); + if (!str || !str_len || !pattern) return 0; + while (str_iter < str_len) + { + const char pattern_letter = *pattern_iter; + const char str_letter = str[str_iter]; + + int next_match = *pattern_iter != '\0' && + nk_to_lower(pattern_letter) == nk_to_lower(str_letter); + int rematch = best_letter && nk_to_lower(*best_letter) == nk_to_lower(str_letter); + + int advanced = next_match && best_letter; + int pattern_repeat = best_letter && *pattern_iter != '\0'; + pattern_repeat = pattern_repeat && + nk_to_lower(*best_letter) == nk_to_lower(pattern_letter); + + if (advanced || pattern_repeat) { + score += best_letter_score; + best_letter = 0; + best_letter_score = 0; + } + + if (next_match || rematch) + { + int new_score = 0; + /* Apply penalty for each letter before the first pattern match */ + if (pattern_iter == pattern) { + int count = (int)(&str[str_iter] - str); + int penalty = NK_LEADING_LETTER_PENALTY * count; + if (penalty < NK_MAX_LEADING_LETTER_PENALTY) + penalty = NK_MAX_LEADING_LETTER_PENALTY; + + score += penalty; + } + + /* apply bonus for consecutive bonuses */ + if (prev_matched) + new_score += NK_ADJACENCY_BONUS; + + /* apply bonus for matches after a separator */ + if (prev_separator) + new_score += NK_SEPARATOR_BONUS; + + /* apply bonus across camel case boundaries */ + if (prev_lower && nk_is_upper(str_letter)) + new_score += NK_CAMEL_BONUS; + + /* update pattern iter IFF the next pattern letter was matched */ + if (next_match) + ++pattern_iter; + + /* update best letter in str which may be for a "next" letter or a rematch */ + if (new_score >= best_letter_score) { + /* apply penalty for now skipped letter */ + if (best_letter != 0) + score += NK_UNMATCHED_LETTER_PENALTY; + + best_letter = &str[str_iter]; + best_letter_score = new_score; + } + prev_matched = nk_true; + } else { + score += NK_UNMATCHED_LETTER_PENALTY; + prev_matched = nk_false; + } + + /* separators should be more easily defined */ + prev_lower = nk_is_lower(str_letter) != 0; + prev_separator = str_letter == '_' || str_letter == ' '; + + ++str_iter; + } + + /* apply score for last match */ + if (best_letter) + score += best_letter_score; + + /* did not match full pattern */ + if (*pattern_iter != '\0') + return nk_false; + + if (out_score) + *out_score = score; + return nk_true; +} + +NK_API int +nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score) +{return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score);} + +NK_INTERN int +nk_string_float_limit(char *string, int prec) +{ + int dot = 0; + char *c = string; + while (*c) { + if (*c == '.') { + dot = 1; + c++; + continue; + } + if (dot == (prec+1)) { + *c = 0; + break; + } + if (dot > 0) dot++; + c++; + } + return (int)(c - string); +} + +NK_INTERN double +nk_pow(double x, int n) +{ + /* check the sign of n */ + double r = 1; + int plus = n >= 0; + n = (plus) ? n : -n; + while (n > 0) { + if ((n & 1) == 1) + r *= x; + n /= 2; + x *= x; + } + return plus ? r : 1.0 / r; +} + +NK_INTERN int +nk_ifloord(double x) +{ + x = (double)((int)x - ((x < 0.0) ? 1 : 0)); + return (int)x; +} + +NK_INTERN int +nk_ifloorf(float x) +{ + x = (float)((int)x - ((x < 0.0f) ? 1 : 0)); + return (int)x; +} + +NK_INTERN int +nk_iceilf(float x) +{ + if (x >= 0) { + int i = (int)x; + return i; + } else { + int t = (int)x; + float r = x - (float)t; + return (r > 0.0f) ? t+1: t; + } +} + +NK_INTERN int +nk_log10(double n) +{ + int neg; + int ret; + int exp = 0; + + neg = (n < 0) ? 1 : 0; + ret = (neg) ? (int)-n : (int)n; + while ((ret / 10) > 0) { + ret /= 10; + exp++; + } + if (neg) exp = -exp; + return exp; +} + +NK_INTERN void +nk_strrev_ascii(char *s) +{ + int len = nk_strlen(s); + int end = len / 2; + int i = 0; + char t; + for (; i < end; ++i) { + t = s[i]; + s[i] = s[len - 1 - i]; + s[len -1 - i] = t; + } +} + +NK_INTERN char* +nk_itoa(char *s, long n) +{ + long i = 0; + if (n == 0) { + s[i++] = '0'; + s[i] = 0; + return s; + } + if (n < 0) { + s[i++] = '-'; + n = -n; + } + while (n > 0) { + s[i++] = (char)('0' + (n % 10)); + n /= 10; + } + s[i] = 0; + if (s[0] == '-') + ++s; + + nk_strrev_ascii(s); + return s; +} + +NK_INTERN char* +nk_dtoa(char *s, double n) +{ + int useExp = 0; + int digit = 0, m = 0, m1 = 0; + char *c = s; + int neg = 0; + + NK_ASSERT(s); + if (!s) return 0; + + if (n == 0.0) { + s[0] = '0'; s[1] = '\0'; + return s; + } + + neg = (n < 0); + if (neg) n = -n; + + /* calculate magnitude */ + m = nk_log10(n); + useExp = (m >= 14 || (neg && m >= 9) || m <= -9); + if (neg) *(c++) = '-'; + + /* set up for scientific notation */ + if (useExp) { + if (m < 0) + m -= 1; + n = n / (double)nk_pow(10.0, m); + m1 = m; + m = 0; + } + if (m < 1.0) { + m = 0; + } + + /* convert the number */ + while (n > NK_FLOAT_PRECISION || m >= 0) { + double weight = nk_pow(10.0, m); + if (weight > 0) { + double t = (double)n / weight; + digit = nk_ifloord(t); + n -= ((double)digit * weight); + *(c++) = (char)('0' + (char)digit); + } + if (m == 0 && n > 0) + *(c++) = '.'; + m--; + } + + if (useExp) { + /* convert the exponent */ + int i, j; + *(c++) = 'e'; + if (m1 > 0) { + *(c++) = '+'; + } else { + *(c++) = '-'; + m1 = -m1; + } + m = 0; + while (m1 > 0) { + *(c++) = (char)('0' + (char)(m1 % 10)); + m1 /= 10; + m++; + } + c -= m; + for (i = 0, j = m-1; i= buf_size) break; + iter++; + + /* flag arguments */ + while (*iter) { + if (*iter == '-') flag |= NK_ARG_FLAG_LEFT; + else if (*iter == '+') flag |= NK_ARG_FLAG_PLUS; + else if (*iter == ' ') flag |= NK_ARG_FLAG_SPACE; + else if (*iter == '#') flag |= NK_ARG_FLAG_NUM; + else if (*iter == '0') flag |= NK_ARG_FLAG_ZERO; + else break; + iter++; + } + + /* width argument */ + width = NK_DEFAULT; + if (*iter >= '1' && *iter <= '9') { + char *end; + width = nk_strtoi(iter, &end); + if (end == iter) + width = -1; + else iter = end; + } else if (*iter == '*') { + width = va_arg(args, int); + iter++; + } + + /* precision argument */ + precision = NK_DEFAULT; + if (*iter == '.') { + iter++; + if (*iter == '*') { + precision = va_arg(args, int); + iter++; + } else { + char *end; + precision = nk_strtoi(iter, &end); + if (end == iter) + precision = -1; + else iter = end; + } + } + + /* length modifier */ + if (*iter == 'h') { + if (*(iter+1) == 'h') { + arg_type = NK_ARG_TYPE_CHAR; + iter++; + } else arg_type = NK_ARG_TYPE_SHORT; + iter++; + } else if (*iter == 'l') { + arg_type = NK_ARG_TYPE_LONG; + iter++; + } else arg_type = NK_ARG_TYPE_DEFAULT; + + /* specifier */ + if (*iter == '%') { + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (len < buf_size) + buf[len++] = '%'; + } else if (*iter == 's') { + /* string */ + const char *str = va_arg(args, const char*); + NK_ASSERT(str != buf && "buffer and argument are not allowed to overlap!"); + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (str == buf) return -1; + while (str && *str && len < buf_size) + buf[len++] = *str++; + } else if (*iter == 'n') { + /* current length callback */ + signed int *n = va_arg(args, int*); + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (n) *n = len; + } else if (*iter == 'c' || *iter == 'i' || *iter == 'd') { + /* signed integer */ + long value = 0; + const char *num_iter; + int num_len, num_print, padding; + int cur_precision = NK_MAX(precision, 1); + int cur_width = NK_MAX(width, 0); + + /* retrieve correct value type */ + if (arg_type == NK_ARG_TYPE_CHAR) + value = (signed char)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_SHORT) + value = (signed short)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_LONG) + value = va_arg(args, signed long); + else if (*iter == 'c') + value = (unsigned char)va_arg(args, int); + else value = va_arg(args, signed int); + + /* convert number to string */ + nk_itoa(number_buffer, value); + num_len = nk_strlen(number_buffer); + padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); + if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while (padding-- > 0 && (len < buf_size)) { + if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* copy string value representation into buffer */ + if ((flag & NK_ARG_FLAG_PLUS) && value >= 0 && len < buf_size) + buf[len++] = '+'; + else if ((flag & NK_ARG_FLAG_SPACE) && value >= 0 && len < buf_size) + buf[len++] = ' '; + + /* fill up to precision number of digits with '0' */ + num_print = NK_MAX(cur_precision, num_len); + while (precision && (num_print > num_len) && (len < buf_size)) { + buf[len++] = '0'; + num_print--; + } + + /* copy string value representation into buffer */ + num_iter = number_buffer; + while (precision && *num_iter && len < buf_size) + buf[len++] = *num_iter++; + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else if (*iter == 'o' || *iter == 'x' || *iter == 'X' || *iter == 'u') { + /* unsigned integer */ + unsigned long value = 0; + int num_len = 0, num_print, padding = 0; + int cur_precision = NK_MAX(precision, 1); + int cur_width = NK_MAX(width, 0); + unsigned int base = (*iter == 'o') ? 8: (*iter == 'u')? 10: 16; + + /* print oct/hex/dec value */ + const char *upper_output_format = "0123456789ABCDEF"; + const char *lower_output_format = "0123456789abcdef"; + const char *output_format = (*iter == 'x') ? + lower_output_format: upper_output_format; + + /* retrieve correct value type */ + if (arg_type == NK_ARG_TYPE_CHAR) + value = (unsigned char)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_SHORT) + value = (unsigned short)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_LONG) + value = va_arg(args, unsigned long); + else value = va_arg(args, unsigned int); + + do { + /* convert decimal number into hex/oct number */ + int digit = output_format[value % base]; + if (num_len < NK_MAX_NUMBER_BUFFER) + number_buffer[num_len++] = (char)digit; + value /= base; + } while (value > 0); + + num_print = NK_MAX(cur_precision, num_len); + padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); + if (flag & NK_ARG_FLAG_NUM) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while ((padding-- > 0) && (len < buf_size)) { + if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* fill up to precision number of digits */ + if (num_print && (flag & NK_ARG_FLAG_NUM)) { + if ((*iter == 'o') && (len < buf_size)) { + buf[len++] = '0'; + } else if ((*iter == 'x') && ((len+1) < buf_size)) { + buf[len++] = '0'; + buf[len++] = 'x'; + } else if ((*iter == 'X') && ((len+1) < buf_size)) { + buf[len++] = '0'; + buf[len++] = 'X'; + } + } + while (precision && (num_print > num_len) && (len < buf_size)) { + buf[len++] = '0'; + num_print--; + } + + /* reverse number direction */ + while (num_len > 0) { + if (precision && (len < buf_size)) + buf[len++] = number_buffer[num_len-1]; + num_len--; + } + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else if (*iter == 'f') { + /* floating point */ + const char *num_iter; + int cur_precision = (precision < 0) ? 6: precision; + int prefix, cur_width = NK_MAX(width, 0); + double value = va_arg(args, double); + int num_len = 0, frac_len = 0, dot = 0; + int padding = 0; + + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_DTOA(number_buffer, value); + num_len = nk_strlen(number_buffer); + + /* calculate padding */ + num_iter = number_buffer; + while (*num_iter && *num_iter != '.') + num_iter++; + + prefix = (*num_iter == '.')?(int)(num_iter - number_buffer)+1:0; + padding = NK_MAX(cur_width - (prefix + NK_MIN(cur_precision, num_len - prefix)) , 0); + if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while (padding-- > 0 && (len < buf_size)) { + if (flag & NK_ARG_FLAG_ZERO) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* copy string value representation into buffer */ + num_iter = number_buffer; + if ((flag & NK_ARG_FLAG_PLUS) && (value >= 0) && (len < buf_size)) + buf[len++] = '+'; + else if ((flag & NK_ARG_FLAG_SPACE) && (value >= 0) && (len < buf_size)) + buf[len++] = ' '; + while (*num_iter) { + if (dot) frac_len++; + if (len < buf_size) + buf[len++] = *num_iter; + if (*num_iter == '.') dot = 1; + if (frac_len >= cur_precision) break; + num_iter++; + } + + /* fill number up to precision */ + while (frac_len < cur_precision) { + if (!dot && len < buf_size) { + buf[len++] = '.'; + dot = 1; + } + if (len < buf_size) + buf[len++] = '0'; + frac_len++; + } + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else { + /* Specifier not supported: g,G,e,E,p,z */ + NK_ASSERT(0 && "specifier is not supported!"); + return result; + } + } + buf[(len >= buf_size)?(buf_size-1):len] = 0; + result = (len >= buf_size)?-1:len; + return result; +} + +NK_INTERN int +nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args) +{ + int result = -1; + NK_ASSERT(buf); + NK_ASSERT(buf_size); + if (!buf || !buf_size || !fmt) return 0; +#ifdef NK_INCLUDE_STANDARD_IO + result = NK_VSNPRINTF(buf, (nk_size)buf_size, fmt, args); + result = (result >= buf_size) ? -1: result; + buf[buf_size-1] = 0; +#else + result = nk_vsnprintf(buf, buf_size, fmt, args); +#endif + return result; +} +#endif + +NK_API nk_hash +nk_murmur_hash(const void * key, int len, nk_hash seed) +{ + /* 32-Bit MurmurHash3: https://code.google.com/p/smhasher/wiki/MurmurHash3*/ + #define NK_ROTL(x,r) ((x) << (r) | ((x) >> (32 - r))) + union {const nk_uint *i; const nk_byte *b;} conv = {0}; + const nk_byte *data = (const nk_byte*)key; + const int nblocks = len/4; + nk_uint h1 = seed; + const nk_uint c1 = 0xcc9e2d51; + const nk_uint c2 = 0x1b873593; + const nk_byte *tail; + const nk_uint *blocks; + nk_uint k1; + int i; + + /* body */ + if (!key) return 0; + conv.b = (data + nblocks*4); + blocks = (const nk_uint*)conv.i; + for (i = -nblocks; i; ++i) { + k1 = blocks[i]; + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = NK_ROTL(h1,13); + h1 = h1*5+0xe6546b64; + } + + /* tail */ + tail = (const nk_byte*)(data + nblocks*4); + k1 = 0; + switch (len & 3) { + case 3: k1 ^= (nk_uint)(tail[2] << 16); + case 2: k1 ^= (nk_uint)(tail[1] << 8u); + case 1: k1 ^= tail[0]; + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + h1 ^= k1; + default: break; + } + + /* finalization */ + h1 ^= (nk_uint)len; + /* fmix32 */ + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + #undef NK_ROTL + return h1; +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_INTERN char* +nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc) +{ + char *buf; + FILE *fd; + long ret; + + NK_ASSERT(path); + NK_ASSERT(siz); + NK_ASSERT(alloc); + if (!path || !siz || !alloc) + return 0; + + fd = fopen(path, "rb"); + if (!fd) return 0; + fseek(fd, 0, SEEK_END); + ret = ftell(fd); + if (ret < 0) { + fclose(fd); + return 0; + } + *siz = (nk_size)ret; + fseek(fd, 0, SEEK_SET); + buf = (char*)alloc->alloc(alloc->userdata,0, *siz); + NK_ASSERT(buf); + if (!buf) { + fclose(fd); + return 0; + } + *siz = (nk_size)fread(buf, *siz, 1, fd); + fclose(fd); + return buf; +} +#endif + +/* + * ============================================================== + * + * COLOR + * + * =============================================================== + */ +NK_INTERN int +nk_parse_hex(const char *p, int length) +{ + int i = 0; + int len = 0; + while (len < length) { + i <<= 4; + if (p[len] >= 'a' && p[len] <= 'f') + i += ((p[len] - 'a') + 10); + else if (p[len] >= 'A' && p[len] <= 'F') + i += ((p[len] - 'A') + 10); + else i += (p[len] - '0'); + len++; + } + return i; +} + +NK_API struct nk_color +nk_rgba(int r, int g, int b, int a) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)NK_CLAMP(0, a, 255); + return ret; +} + +NK_API struct nk_color +nk_rgb_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = 255; + return col; +} + +NK_API struct nk_color +nk_rgba_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = (nk_byte)nk_parse_hex(c+6, 2); + return col; +} + +NK_API void +nk_color_hex_rgba(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0x0F)); + output[1] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[2] = (char)NK_TO_HEX((col.g & 0x0F)); + output[3] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[4] = (char)NK_TO_HEX((col.b & 0x0F)); + output[5] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[6] = (char)NK_TO_HEX((col.a & 0x0F)); + output[7] = (char)NK_TO_HEX((col.a & 0xF0) >> 4); + output[8] = '\0'; + #undef NK_TO_HEX +} + +NK_API void +nk_color_hex_rgb(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0x0F)); + output[1] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[2] = (char)NK_TO_HEX((col.g & 0x0F)); + output[3] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[4] = (char)NK_TO_HEX((col.b & 0x0F)); + output[5] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[6] = '\0'; + #undef NK_TO_HEX +} + +NK_API struct nk_color +nk_rgba_iv(const int *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgba_bv(const nk_byte *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgb(int r, int g, int b) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)255; + return ret; +} + +NK_API struct nk_color +nk_rgb_iv(const int *c) +{ + return nk_rgb(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_rgb_bv(const nk_byte* c) +{ + return nk_rgb(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_rgba_u32(nk_uint in) +{ + struct nk_color ret; + ret.r = (in & 0xFF); + ret.g = ((in >> 8) & 0xFF); + ret.b = ((in >> 16) & 0xFF); + ret.a = (nk_byte)((in >> 24) & 0xFF); + return ret; +} + +NK_API struct nk_color +nk_rgba_f(float r, float g, float b, float a) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f); + return ret; +} + +NK_API struct nk_color +nk_rgba_fv(const float *c) +{ + return nk_rgba_f(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgb_f(float r, float g, float b) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = 255; + return ret; +} + +NK_API struct nk_color +nk_rgb_fv(const float *c) +{ + return nk_rgb_f(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv(int h, int s, int v) +{ + return nk_hsva(h, s, v, 255); +} + +NK_API struct nk_color +nk_hsv_iv(const int *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv_bv(const nk_byte *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv_f(float h, float s, float v) +{ + return nk_hsva_f(h, s, v, 1.0f); +} + +NK_API struct nk_color +nk_hsv_fv(const float *c) +{ + return nk_hsv_f(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsva(int h, int s, int v, int a) +{ + float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f; + float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f; + float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f; + float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f; + return nk_hsva_f(hf, sf, vf, af); +} + +NK_API struct nk_color +nk_hsva_iv(const int *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_hsva_bv(const nk_byte *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_hsva_f(float h, float s, float v, float a) +{ + struct nk_colorf out = {0,0,0,0}; + float p, q, t, f; + int i; + + if (s <= 0.0f) { + out.r = v; out.g = v; out.b = v; + return nk_rgb_f(out.r, out.g, out.b); + } + + h = h / (60.0f/360.0f); + i = (int)h; + f = h - (float)i; + p = v * (1.0f - s); + q = v * (1.0f - (s * f)); + t = v * (1.0f - s * (1.0f - f)); + + switch (i) { + case 0: out.r = v; out.g = t; out.b = p; break; + case 1: out.r = q; out.g = v; out.b = p; break; + case 2: out.r = p; out.g = v; out.b = t; break; + case 4: out.r = t; out.g = p; out.b = v; break; + case 5: default: out.r = v; out.g = p; out.b = q; break; + } + return nk_rgba_f(out.r, out.g, out.b, a); +} + +NK_API struct nk_color +nk_hsva_fv(const float *c) +{ + return nk_hsva_f(c[0], c[1], c[2], c[3]); +} + +NK_API nk_uint +nk_color_u32(struct nk_color in) +{ + nk_uint out = (nk_uint)in.r; + out |= ((nk_uint)in.g << 8); + out |= ((nk_uint)in.b << 16); + out |= ((nk_uint)in.a << 24); + return out; +} + +NK_API void +nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in) +{ + NK_STORAGE const float s = 1.0f/255.0f; + *r = (float)in.r * s; + *g = (float)in.g * s; + *b = (float)in.b * s; + *a = (float)in.a * s; +} + +NK_API void +nk_color_fv(float *c, struct nk_color in) +{ + nk_color_f(&c[0], &c[1], &c[2], &c[3], in); +} + +NK_API void +nk_color_d(double *r, double *g, double *b, double *a, struct nk_color in) +{ + NK_STORAGE const double s = 1.0/255.0; + *r = (double)in.r * s; + *g = (double)in.g * s; + *b = (double)in.b * s; + *a = (double)in.a * s; +} + +NK_API void +nk_color_dv(double *c, struct nk_color in) +{ + nk_color_d(&c[0], &c[1], &c[2], &c[3], in); +} + +NK_API void +nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in) +{ + float a; + nk_color_hsva_f(out_h, out_s, out_v, &a, in); +} + +NK_API void +nk_color_hsv_fv(float *out, struct nk_color in) +{ + float a; + nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in); +} + +NK_API void +nk_color_hsva_f(float *out_h, float *out_s, + float *out_v, float *out_a, struct nk_color in) +{ + float chroma; + float K = 0.0f; + float r,g,b,a; + + nk_color_f(&r,&g,&b,&a, in); + if (g < b) { + const float t = g; g = b; b = t; + K = -1.f; + } + if (r < g) { + const float t = r; r = g; g = t; + K = -2.f/6.0f - K; + } + chroma = r - ((g < b) ? g: b); + *out_h = NK_ABS(K + (g - b)/(6.0f * chroma + 1e-20f)); + *out_s = chroma / (r + 1e-20f); + *out_v = r; + *out_a = (float)in.a / 255.0f; +} + +NK_API void +nk_color_hsva_fv(float *out, struct nk_color in) +{ + nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); +} + +NK_API void +nk_color_hsva_i(int *out_h, int *out_s, int *out_v, + int *out_a, struct nk_color in) +{ + float h,s,v,a; + nk_color_hsva_f(&h, &s, &v, &a, in); + *out_h = (nk_byte)(h * 255.0f); + *out_s = (nk_byte)(s * 255.0f); + *out_v = (nk_byte)(v * 255.0f); + *out_a = (nk_byte)(a * 255.0f); +} + +NK_API void +nk_color_hsva_iv(int *out, struct nk_color in) +{ + nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); +} + +NK_API void +nk_color_hsva_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; + out[3] = (nk_byte)tmp[3]; +} + +NK_API void +nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *h = (nk_byte)tmp[0]; + *s = (nk_byte)tmp[1]; + *v = (nk_byte)tmp[2]; + *a = (nk_byte)tmp[3]; +} + +NK_API void +nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in) +{ + int a; + nk_color_hsva_i(out_h, out_s, out_v, &a, in); +} + +NK_API void +nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *out_h = (nk_byte)tmp[0]; + *out_s = (nk_byte)tmp[1]; + *out_v = (nk_byte)tmp[2]; +} + +NK_API void +nk_color_hsv_iv(int *out, struct nk_color in) +{ + nk_color_hsv_i(&out[0], &out[1], &out[2], in); +} + +NK_API void +nk_color_hsv_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; +} +/* + * ============================================================== + * + * IMAGE + * + * =============================================================== + */ +NK_API nk_handle +nk_handle_ptr(void *ptr) +{ + nk_handle handle = {0}; + handle.ptr = ptr; + return handle; +} + +NK_API nk_handle +nk_handle_id(int id) +{ + nk_handle handle; + nk_zero_struct(handle); + handle.id = id; + return handle; +} + +NK_API struct nk_image +nk_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.ptr = ptr; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_subimage_id(int id, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_subimage_handle(nk_handle handle, unsigned short w, unsigned short h, + struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_image_handle(nk_handle handle) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API struct nk_image +nk_image_ptr(void *ptr) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + NK_ASSERT(ptr); + s.handle.ptr = ptr; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API struct nk_image +nk_image_id(int id) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API int +nk_image_is_subimage(const struct nk_image* img) +{ + NK_ASSERT(img); + return !(img->w == 0 && img->h == 0); +} + +NK_INTERN void +nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, + float x1, float y1) +{ + NK_ASSERT(a); + NK_ASSERT(clip); + clip->x = NK_MAX(a->x, x0); + clip->y = NK_MAX(a->y, y0); + clip->w = NK_MIN(a->x + a->w, x1) - clip->x; + clip->h = NK_MIN(a->y + a->h, y1) - clip->y; + clip->w = NK_MAX(0, clip->w); + clip->h = NK_MAX(0, clip->h); +} + +NK_API void +nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, + float pad_x, float pad_y, enum nk_heading direction) +{ + float w_half, h_half; + NK_ASSERT(result); + + r.w = NK_MAX(2 * pad_x, r.w); + r.h = NK_MAX(2 * pad_y, r.h); + r.w = r.w - 2 * pad_x; + r.h = r.h - 2 * pad_y; + + r.x = r.x + pad_x; + r.y = r.y + pad_y; + + w_half = r.w / 2.0f; + h_half = r.h / 2.0f; + + if (direction == NK_UP) { + result[0] = nk_vec2(r.x + w_half, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + r.h); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_RIGHT) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + h_half); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_DOWN) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + w_half, r.y + r.h); + } else { + result[0] = nk_vec2(r.x, r.y + h_half); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + r.w, r.y + r.h); + } +} + +NK_INTERN int +nk_text_clamp(const struct nk_user_font *font, const char *text, + int text_len, float space, int *glyphs, float *text_width) +{ + int glyph_len = 0; + float last_width = 0; + nk_rune unicode = 0; + float width = 0; + int len = 0; + int g = 0; + + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && (width < space) && (len < text_len)) { + float s; + len += glyph_len; + s = font->width(font->userdata, font->height, text, len); + + last_width = width; + width = s; + glyph_len = nk_utf_decode(&text[len], &unicode, text_len - len); + g++; + } + + *glyphs = g; + *text_width = last_width; + return len; +} + +enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE}; +NK_INTERN struct nk_vec2 +nk_text_calculate_text_bounds(const struct nk_user_font *font, + const char *begin, int byte_len, float row_height, const char **remaining, + struct nk_vec2 *out_offset, int *glyphs, int op) +{ + float line_height = row_height; + struct nk_vec2 text_size = nk_vec2(0,0); + float line_width = 0.0f; + + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + if (!begin || byte_len <= 0 || !font) + return nk_vec2(0,row_height); + + glyph_len = nk_utf_decode(begin, &unicode, byte_len); + if (!glyph_len) return text_size; + glyph_width = font->width(font->userdata, font->height, begin, glyph_len); + + *glyphs = 0; + while ((text_len < byte_len) && glyph_len) { + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + text_size.y += line_height; + line_width = 0; + *glyphs+=1; + if (op == NK_STOP_ON_NEW_LINE) + break; + + text_len++; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + if (unicode == '\r') { + text_len++; + *glyphs+=1; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + *glyphs = *glyphs + 1; + text_len += glyph_len; + line_width += (float)glyph_width; + glyph_width = font->width(font->userdata, font->height, begin+text_len, glyph_len); + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + if (text_size.x < line_width) + text_size.x = line_width; + if (out_offset) + *out_offset = nk_vec2(line_width, text_size.y + line_height); + if (line_width > 0 || text_size.y == 0.0f) + text_size.y += line_height; + if (remaining) + *remaining = begin+text_len; + return text_size; +} + +/* ============================================================== + * + * UTF-8 + * + * ===============================================================*/ +NK_GLOBAL const nk_byte nk_utfbyte[NK_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +NK_GLOBAL const nk_byte nk_utfmask[NK_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +NK_GLOBAL const nk_uint nk_utfmin[NK_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x10000}; +NK_GLOBAL const nk_uint nk_utfmax[NK_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +NK_INTERN int +nk_utf_validate(nk_rune *u, int i) +{ + NK_ASSERT(u); + if (!u) return 0; + if (!NK_BETWEEN(*u, nk_utfmin[i], nk_utfmax[i]) || + NK_BETWEEN(*u, 0xD800, 0xDFFF)) + *u = NK_UTF_INVALID; + for (i = 1; *u > nk_utfmax[i]; ++i); + return i; +} + +NK_INTERN nk_rune +nk_utf_decode_byte(char c, int *i) +{ + NK_ASSERT(i); + if (!i) return 0; + for(*i = 0; *i < (int)NK_LEN(nk_utfmask); ++(*i)) { + if (((nk_byte)c & nk_utfmask[*i]) == nk_utfbyte[*i]) + return (nk_byte)(c & ~nk_utfmask[*i]); + } + return 0; +} + +NK_API int +nk_utf_decode(const char *c, nk_rune *u, int clen) +{ + int i, j, len, type=0; + nk_rune udecoded; + + NK_ASSERT(c); + NK_ASSERT(u); + + if (!c || !u) return 0; + if (!clen) return 0; + *u = NK_UTF_INVALID; + + udecoded = nk_utf_decode_byte(c[0], &len); + if (!NK_BETWEEN(len, 1, NK_UTF_SIZE)) + return 1; + + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | nk_utf_decode_byte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + nk_utf_validate(u, len); + return len; +} + +NK_INTERN char +nk_utf_encode_byte(nk_rune u, int i) +{ + return (char)((nk_utfbyte[i]) | ((nk_byte)u & ~nk_utfmask[i])); +} + +NK_API int +nk_utf_encode(nk_rune u, char *c, int clen) +{ + int len, i; + len = nk_utf_validate(&u, 0); + if (clen < len || !len || len > NK_UTF_SIZE) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = nk_utf_encode_byte(u, 0); + u >>= 6; + } + c[0] = nk_utf_encode_byte(u, len); + return len; +} + +NK_API int +nk_utf_len(const char *str, int len) +{ + const char *text; + int glyphs = 0; + int text_len; + int glyph_len; + int src_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + if (!str || !len) return 0; + + text = str; + text_len = len; + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && src_len < len) { + glyphs++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, &unicode, text_len - src_len); + } + return glyphs; +} + +NK_API const char* +nk_utf_at(const char *buffer, int length, int index, + nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + const char *text; + int text_len; + + NK_ASSERT(buffer); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!buffer || !unicode || !len) return 0; + if (index < 0) { + *unicode = NK_UTF_INVALID; + *len = 0; + return 0; + } + + text = buffer; + text_len = length; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == index) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != index) return 0; + return buffer + src_len; +} + +/* ============================================================== + * + * BUFFER + * + * ===============================================================*/ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_INTERN void* nk_malloc(nk_handle unused, void *old,nk_size size) +{NK_UNUSED(unused); NK_UNUSED(old); return malloc(size);} +NK_INTERN void nk_mfree(nk_handle unused, void *ptr) +{NK_UNUSED(unused); free(ptr);} + +NK_API void +nk_buffer_init_default(struct nk_buffer *buffer) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(buffer, &alloc, NK_BUFFER_DEFAULT_INITIAL_SIZE); +} +#endif + +NK_API void +nk_buffer_init(struct nk_buffer *b, const struct nk_allocator *a, + nk_size initial_size) +{ + NK_ASSERT(b); + NK_ASSERT(a); + NK_ASSERT(initial_size); + if (!b || !a || !initial_size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_DYNAMIC; + b->memory.ptr = a->alloc(a->userdata,0, initial_size); + b->memory.size = initial_size; + b->size = initial_size; + b->grow_factor = 2.0f; + b->pool = *a; +} + +NK_API void +nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size) +{ + NK_ASSERT(b); + NK_ASSERT(m); + NK_ASSERT(size); + if (!b || !m || !size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_FIXED; + b->memory.ptr = m; + b->memory.size = size; + b->size = size; +} + +NK_INTERN void* +nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, + enum nk_buffer_allocation_type type) +{ + void *memory = 0; + switch (type) { + default: + case NK_BUFFER_MAX: + case NK_BUFFER_FRONT: + if (align) { + memory = NK_ALIGN_PTR(unaligned, align); + *alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); + } else { + memory = unaligned; + *alignment = 0; + } + break; + case NK_BUFFER_BACK: + if (align) { + memory = NK_ALIGN_PTR_BACK(unaligned, align); + *alignment = (nk_size)((nk_byte*)unaligned - (nk_byte*)memory); + } else { + memory = unaligned; + *alignment = 0; + } + break; + } + return memory; +} + +NK_INTERN void* +nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size) +{ + void *temp; + nk_size buffer_size; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size || !b->pool.alloc || !b->pool.free) + return 0; + + buffer_size = b->memory.size; + temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity); + NK_ASSERT(temp); + if (!temp) return 0; + + *size = capacity; + if (temp != b->memory.ptr) { + NK_MEMCPY(temp, b->memory.ptr, buffer_size); + b->pool.free(b->pool.userdata, b->memory.ptr); + } + + if (b->size == buffer_size) { + /* no back buffer so just set correct size */ + b->size = capacity; + return temp; + } else { + /* copy back buffer to the end of the new buffer */ + void *dst, *src; + nk_size back_size; + back_size = buffer_size - b->size; + dst = nk_ptr_add(void, temp, capacity - back_size); + src = nk_ptr_add(void, temp, b->size); + NK_MEMCPY(dst, src, back_size); + b->size = capacity - back_size; + } + return temp; +} + +NK_INTERN void* +nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, + nk_size size, nk_size align) +{ + int full; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size) return 0; + b->needed += size; + + /* calculate total size with needed alignment + size */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + + /* check if buffer has enough memory*/ + if (type == NK_BUFFER_FRONT) + full = ((b->allocated + size + alignment) > b->size); + else full = ((b->size - (size + alignment)) <= b->allocated); + + if (full) { + nk_size capacity; + NK_ASSERT(b->type == NK_BUFFER_DYNAMIC); + NK_ASSERT(b->pool.alloc && b->pool.free); + if (b->type != NK_BUFFER_DYNAMIC || !b->pool.alloc || !b->pool.free) + return 0; + + /* buffer is full so allocate bigger buffer if dynamic */ + capacity = (nk_size)((float)b->memory.size * b->grow_factor); + capacity = NK_MAX(capacity, nk_round_up_pow2((nk_uint)(b->allocated + size))); + b->memory.ptr = nk_buffer_realloc(b, capacity, &b->memory.size); + if (!b->memory.ptr) return 0; + + /* align newly allocated pointer */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + } + + if (type == NK_BUFFER_FRONT) + b->allocated += size + alignment; + else b->size -= (size + alignment); + b->needed += alignment; + b->calls++; + return memory; +} + +NK_API void +nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type, + const void *memory, nk_size size, nk_size align) +{ + void *mem = nk_buffer_alloc(b, type, size, align); + if (!mem) return; + NK_MEMCPY(mem, memory, size); +} + +NK_API void +nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + buffer->marker[type].active = nk_true; + if (type == NK_BUFFER_BACK) + buffer->marker[type].offset = buffer->size; + else buffer->marker[type].offset = buffer->allocated; +} + +NK_API void +nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + if (type == NK_BUFFER_BACK) { + /* reset back buffer either back to marker or empty */ + buffer->needed -= (buffer->memory.size - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->size = buffer->marker[type].offset; + else buffer->size = buffer->memory.size; + buffer->marker[type].active = nk_false; + } else { + /* reset front buffer either back to back marker or empty */ + buffer->needed -= (buffer->allocated - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->allocated = buffer->marker[type].offset; + else buffer->allocated = 0; + buffer->marker[type].active = nk_false; + } +} + +NK_API void +nk_buffer_clear(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b) return; + b->allocated = 0; + b->size = b->memory.size; + b->calls = 0; + b->needed = 0; +} + +NK_API void +nk_buffer_free(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b || !b->memory.ptr) return; + if (b->type == NK_BUFFER_FIXED) return; + if (!b->pool.free) return; + NK_ASSERT(b->pool.free); + b->pool.free(b->pool.userdata, b->memory.ptr); +} + +NK_API void +nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b) +{ + NK_ASSERT(b); + NK_ASSERT(s); + if (!s || !b) return; + s->allocated = b->allocated; + s->size = b->memory.size; + s->needed = b->needed; + s->memory = b->memory.ptr; + s->calls = b->calls; +} + +NK_API void* +nk_buffer_memory(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} + +NK_API const void* +nk_buffer_memory_const(const struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} + +NK_API nk_size +nk_buffer_total(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.size; +} + +/* + * ============================================================== + * + * STRING + * + * =============================================================== + */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_str_init_default(struct nk_str *str) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(&str->buffer, &alloc, 32); + str->len = 0; +} +#endif + +NK_API void +nk_str_init(struct nk_str *str, const struct nk_allocator *alloc, nk_size size) +{ + nk_buffer_init(&str->buffer, alloc, size); + str->len = 0; +} + +NK_API void +nk_str_init_fixed(struct nk_str *str, void *memory, nk_size size) +{ + nk_buffer_init_fixed(&str->buffer, memory, size); + str->len = 0; +} + +NK_API int +nk_str_append_text_char(struct nk_str *s, const char *str, int len) +{ + char *mem; + NK_ASSERT(s); + NK_ASSERT(str); + if (!s || !str || !len) return 0; + mem = (char*)nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len += nk_utf_len(str, len); + return len; +} + +NK_API int +nk_str_append_str_char(struct nk_str *s, const char *str) +{ + return nk_str_append_text_char(s, str, nk_strlen(str)); +} + +NK_API int +nk_str_append_text_utf8(struct nk_str *str, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_append_text_char(str, text, byte_len); + return len; +} + +NK_API int +nk_str_append_str_utf8(struct nk_str *str, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_append_text_char(str, text, byte_len); + return runes; +} + +NK_API int +nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(text[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_append_text_char(str, glyph, byte_len); + } + return len; +} + +NK_API int +nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_append_text_char(str, glyph, byte_len); + i++; + } + return i; +} + +NK_API int +nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len) +{ + int i; + void *mem; + char *src; + char *dst; + + int copylen; + NK_ASSERT(s); + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!s || !str || !len || (nk_size)pos > s->buffer.allocated) return 0; + if ((s->buffer.allocated + (nk_size)len >= s->buffer.memory.size) && + (s->buffer.type == NK_BUFFER_FIXED)) return 0; + + copylen = (int)s->buffer.allocated - pos; + if (!copylen) { + nk_str_append_text_char(s, str, len); + return 1; + } + mem = nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + + /* memmove */ + NK_ASSERT(((int)pos + (int)len + ((int)copylen - 1)) >= 0); + NK_ASSERT(((int)pos + ((int)copylen - 1)) >= 0); + dst = nk_ptr_add(char, s->buffer.memory.ptr, pos + len + (copylen - 1)); + src = nk_ptr_add(char, s->buffer.memory.ptr, pos + (copylen-1)); + for (i = 0; i < copylen; ++i) *dst-- = *src--; + mem = nk_ptr_add(void, s->buffer.memory.ptr, pos); + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); + return 1; +} + +NK_API int +nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len) +{ + int glyph_len; + nk_rune unicode; + const char *begin; + const char *buffer; + + NK_ASSERT(str); + NK_ASSERT(cstr); + NK_ASSERT(len); + if (!str || !cstr || !len) return 0; + begin = nk_str_at_rune(str, pos, &unicode, &glyph_len); + buffer = nk_str_get_const(str); + if (!begin) return 0; + return nk_str_insert_text_char(str, (int)(begin - buffer), cstr, len); +} + +NK_API int nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len) +{return nk_str_insert_at_char(str, pos, text, len);} + +NK_API int nk_str_insert_str_char(struct nk_str *str, int pos, const char *text) +{return nk_str_insert_at_char(str, pos, text, nk_strlen(text));} + +NK_API int +nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(text); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_insert_at_rune(str, pos, text, byte_len); + return len; +} + +NK_API int +nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_insert_at_rune(str, pos, text, byte_len); + return runes; +} + +NK_API int +nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !runes || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + } + return len; +} + +NK_API int +nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + i++; + } + return i; +} + +NK_API void +nk_str_remove_chars(struct nk_str *s, int len) +{ + NK_ASSERT(s); + NK_ASSERT(len >= 0); + if (!s || len < 0 || (nk_size)len > s->buffer.allocated) return; + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); +} + +NK_API void +nk_str_remove_runes(struct nk_str *str, int len) +{ + int index; + const char *begin; + const char *end; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!str || len < 0) return; + if (len >= str->len) { + str->len = 0; + return; + } + + index = str->len - len; + begin = nk_str_at_rune(str, index, &unicode, &len); + end = (const char*)str->buffer.memory.ptr + str->buffer.allocated; + nk_str_remove_chars(str, (int)(end-begin)+1); +} + +NK_API void +nk_str_delete_chars(struct nk_str *s, int pos, int len) +{ + NK_ASSERT(s); + if (!s || !len || (nk_size)pos > s->buffer.allocated || + (nk_size)(pos + len) > s->buffer.allocated) return; + + if ((nk_size)(pos + len) < s->buffer.allocated) { + /* memmove */ + char *dst = nk_ptr_add(char, s->buffer.memory.ptr, pos); + char *src = nk_ptr_add(char, s->buffer.memory.ptr, pos + len); + NK_MEMCPY(dst, src, s->buffer.allocated - (nk_size)(pos + len)); + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + } else nk_str_remove_chars(s, len); + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); +} + +NK_API void +nk_str_delete_runes(struct nk_str *s, int pos, int len) +{ + char *temp; + nk_rune unicode; + char *begin; + char *end; + int unused; + + NK_ASSERT(s); + NK_ASSERT(s->len >= pos + len); + if (s->len < pos + len) + len = NK_CLAMP(0, (s->len - pos), s->len); + if (!len) return; + + temp = (char *)s->buffer.memory.ptr; + begin = nk_str_at_rune(s, pos, &unicode, &unused); + if (!begin) return; + s->buffer.memory.ptr = begin; + end = nk_str_at_rune(s, len, &unicode, &unused); + s->buffer.memory.ptr = temp; + if (!end) return; + nk_str_delete_chars(s, (int)(begin - temp), (int)(end - begin)); +} + +NK_API char* +nk_str_at_char(struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} + +NK_API char* +nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i+= glyph_len; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} + +NK_API const char* +nk_str_at_char_const(const struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} + +NK_API const char* +nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} + +NK_API nk_rune +nk_str_rune_at(const struct nk_str *str, int pos) +{ + int len; + nk_rune unicode = 0; + nk_str_at_const(str, pos, &unicode, &len); + return unicode; +} + +NK_API char* +nk_str_get(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (char*)s->buffer.memory.ptr; +} + +NK_API const char* +nk_str_get_const(const struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (const char*)s->buffer.memory.ptr; +} + +NK_API int +nk_str_len(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return s->len; +} + +NK_API int +nk_str_len_char(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (int)s->buffer.allocated; +} + +NK_API void +nk_str_clear(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_clear(&str->buffer); + str->len = 0; +} + +NK_API void +nk_str_free(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_free(&str->buffer); + str->len = 0; +} + +/* + * ============================================================== + * + * Command buffer + * + * =============================================================== +*/ +NK_INTERN void +nk_command_buffer_init(struct nk_command_buffer *cmdbuf, + struct nk_buffer *buffer, enum nk_command_clipping clip) +{ + NK_ASSERT(cmdbuf); + NK_ASSERT(buffer); + if (!cmdbuf || !buffer) return; + cmdbuf->base = buffer; + cmdbuf->use_clipping = clip; + cmdbuf->begin = buffer->allocated; + cmdbuf->end = buffer->allocated; + cmdbuf->last = buffer->allocated; +} + +NK_INTERN void +nk_command_buffer_reset(struct nk_command_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return; + buffer->begin = 0; + buffer->end = 0; + buffer->last = 0; + buffer->clip = nk_null_rect; +#ifdef NK_INCLUDE_COMMAND_USERDATA + buffer->userdata.ptr = 0; +#endif +} + +NK_INTERN void* +nk_command_buffer_push(struct nk_command_buffer* b, + enum nk_command_type t, nk_size size) +{ + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_command); + struct nk_command *cmd; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(b->base); + if (!b) return 0; + + cmd = (struct nk_command*)nk_buffer_alloc(b->base,NK_BUFFER_FRONT,size,align); + if (!cmd) return 0; + + /* make sure the offset to the next command is aligned */ + b->last = (nk_size)((nk_byte*)cmd - (nk_byte*)b->base->memory.ptr); + unaligned = (nk_byte*)cmd + size; + memory = NK_ALIGN_PTR(unaligned, align); + alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); + + cmd->type = t; + cmd->next = b->base->allocated + alignment; +#ifdef NK_INCLUDE_COMMAND_USERDATA + cmd->userdata = b->userdata; +#endif + b->end = cmd->next; + return cmd; +} + +NK_API void +nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r) +{ + struct nk_command_scissor *cmd; + NK_ASSERT(b); + if (!b) return; + + b->clip.x = r.x; + b->clip.y = r.y; + b->clip.w = r.w; + b->clip.h = r.h; + cmd = (struct nk_command_scissor*) + nk_command_buffer_push(b, NK_COMMAND_SCISSOR, sizeof(*cmd)); + + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); +} + +NK_API void +nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, + float x1, float y1, float line_thickness, struct nk_color c) +{ + struct nk_command_line *cmd; + NK_ASSERT(b); + if (!b) return; + cmd = (struct nk_command_line*) + nk_command_buffer_push(b, NK_COMMAND_LINE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)x0; + cmd->begin.y = (short)y0; + cmd->end.x = (short)x1; + cmd->end.y = (short)y1; + cmd->color = c; +} + +NK_API void +nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay, + float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y, + float bx, float by, float line_thickness, struct nk_color col) +{ + struct nk_command_curve *cmd; + NK_ASSERT(b); + if (!b || col.a == 0) return; + + cmd = (struct nk_command_curve*) + nk_command_buffer_push(b, NK_COMMAND_CURVE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)ax; + cmd->begin.y = (short)ay; + cmd->ctrl[0].x = (short)ctrl0x; + cmd->ctrl[0].y = (short)ctrl0y; + cmd->ctrl[1].x = (short)ctrl1x; + cmd->ctrl[1].y = (short)ctrl1y; + cmd->end.x = (short)bx; + cmd->end.y = (short)by; + cmd->color = col; +} + +NK_API void +nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, float line_thickness, struct nk_color c) +{ + struct nk_command_rect *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || rect.w == 0 || rect.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect*) + nk_command_buffer_push(b, NK_COMMAND_RECT, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} + +NK_API void +nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, struct nk_color c) +{ + struct nk_command_rect_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || rect.w == 0 || rect.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_filled*) + nk_command_buffer_push(b, NK_COMMAND_RECT_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} + +NK_API void +nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + struct nk_command_rect_multi_color *cmd; + NK_ASSERT(b); + if (!b || rect.w == 0 || rect.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_multi_color*) + nk_command_buffer_push(b, NK_COMMAND_RECT_MULTI_COLOR, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->left = left; + cmd->top = top; + cmd->right = right; + cmd->bottom = bottom; +} + +NK_API void +nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r, + float line_thickness, struct nk_color c) +{ + struct nk_command_circle *cmd; + if (!b || r.w == 0 || r.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} + +NK_API void +nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c) +{ + struct nk_command_circle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || r.w == 0 || r.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle_filled*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} + +NK_API void +nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, float line_thickness, struct nk_color c) +{ + struct nk_command_arc *cmd; + if (!b || c.a == 0) return; + cmd = (struct nk_command_arc*) + nk_command_buffer_push(b, NK_COMMAND_ARC, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} + +NK_API void +nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, struct nk_color c) +{ + struct nk_command_arc_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + cmd = (struct nk_command_arc_filled*) + nk_command_buffer_push(b, NK_COMMAND_ARC_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} + +NK_API void +nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, float line_thickness, struct nk_color c) +{ + struct nk_command_triangle *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} + +NK_API void +nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, struct nk_color c) +{ + struct nk_command_triangle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle_filled*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} + +NK_API void +nk_stroke_polygon(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon*) nk_command_buffer_push(b, NK_COMMAND_POLYGON, size); + if (!cmd) return; + cmd->color = col; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count, + struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon_filled *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon_filled*) + nk_command_buffer_push(b, NK_COMMAND_POLYGON_FILLED, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2+0]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polyline *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polyline*) nk_command_buffer_push(b, NK_COMMAND_POLYLINE, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + cmd->line_thickness = (unsigned short)line_thickness; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_draw_image(struct nk_command_buffer *b, struct nk_rect r, + const struct nk_image *img, struct nk_color col) +{ + struct nk_command_image *cmd; + NK_ASSERT(b); + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + cmd = (struct nk_command_image*) + nk_command_buffer_push(b, NK_COMMAND_IMAGE, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); + cmd->img = *img; + cmd->col = col; +} + +NK_API void +nk_draw_text(struct nk_command_buffer *b, struct nk_rect r, + const char *string, int length, const struct nk_user_font *font, + struct nk_color bg, struct nk_color fg) +{ + float text_width = 0; + struct nk_command_text *cmd; + + NK_ASSERT(b); + NK_ASSERT(font); + if (!b || !string || !length || (bg.a == 0 && fg.a == 0)) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + /* make sure text fits inside bounds */ + text_width = font->width(font->userdata, font->height, string, length); + if (text_width > r.w){ + int glyphs = 0; + float txt_width = (float)text_width; + length = nk_text_clamp(font, string, length, r.w, &glyphs, &txt_width); + } + + if (!length) return; + cmd = (struct nk_command_text*) + nk_command_buffer_push(b, NK_COMMAND_TEXT, sizeof(*cmd) + (nk_size)(length + 1)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)r.w; + cmd->h = (unsigned short)r.h; + cmd->background = bg; + cmd->foreground = fg; + cmd->font = font; + cmd->length = length; + cmd->height = font->height; + NK_MEMCPY(cmd->string, string, (nk_size)length); + cmd->string[length] = '\0'; +} + +/* ============================================================== + * + * DRAW LIST + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_API void +nk_draw_list_init(struct nk_draw_list *list) +{ + nk_size i = 0; + nk_zero(list, sizeof(*list)); + for (i = 0; i < NK_LEN(list->circle_vtx); ++i) { + const float a = ((float)i / (float)NK_LEN(list->circle_vtx)) * 2 * NK_PI; + list->circle_vtx[i].x = (float)NK_COS(a); + list->circle_vtx[i].y = (float)NK_SIN(a); + } +} + +NK_API void +nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config *config, + struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements) +{ + canvas->buffer = cmds; + canvas->config = *config; + canvas->elements = elements; + canvas->vertices = vertices; + canvas->clip_rect = nk_null_rect; +} + +NK_API const struct nk_draw_command* +nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) +{ + nk_byte *memory; + nk_size offset; + const struct nk_draw_command *cmd; + + NK_ASSERT(buffer); + if (!buffer || !buffer->size || !canvas->cmd_count) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + offset = buffer->memory.size - canvas->cmd_offset; + cmd = nk_ptr_add(const struct nk_draw_command, memory, offset); + return cmd; +} + +NK_API const struct nk_draw_command* +nk__draw_list_end(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) +{ + nk_size size; + nk_size offset; + nk_byte *memory; + const struct nk_draw_command *end; + + NK_ASSERT(buffer); + NK_ASSERT(canvas); + if (!buffer || !canvas) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + size = buffer->memory.size; + offset = size - canvas->cmd_offset; + end = nk_ptr_add(const struct nk_draw_command, memory, offset); + end -= (canvas->cmd_count-1); + return end; +} + +NK_API const struct nk_draw_command* +nk__draw_list_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_draw_list *canvas) +{ + const struct nk_draw_command *end; + NK_ASSERT(buffer); + NK_ASSERT(canvas); + if (!cmd || !buffer || !canvas) + return 0; + + end = nk__draw_list_end(canvas, buffer); + if (cmd <= end) return 0; + return (cmd-1); +} + +NK_API void +nk_draw_list_clear(struct nk_draw_list *list) +{ + NK_ASSERT(list); + if (!list) return; + if (list->buffer) + nk_buffer_clear(list->buffer); + if (list->vertices) + nk_buffer_clear(list->vertices); + if (list->elements) + nk_buffer_clear(list->elements); + + list->element_count = 0; + list->vertex_count = 0; + list->cmd_offset = 0; + list->cmd_count = 0; + list->path_count = 0; + list->vertices = 0; + list->elements = 0; + list->clip_rect = nk_null_rect; +} + +NK_INTERN struct nk_vec2* +nk_draw_list_alloc_path(struct nk_draw_list *list, int count) +{ + struct nk_vec2 *points; + NK_STORAGE const nk_size point_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size point_size = sizeof(struct nk_vec2); + points = (struct nk_vec2*) + nk_buffer_alloc(list->buffer, NK_BUFFER_FRONT, + point_size * (nk_size)count, point_align); + + if (!points) return 0; + if (!list->path_offset) { + void *memory = nk_buffer_memory(list->buffer); + list->path_offset = (unsigned int)((nk_byte*)points - (nk_byte*)memory); + } + list->path_count += (unsigned int)count; + return points; +} + +NK_INTERN struct nk_vec2 +nk_draw_list_path_last(struct nk_draw_list *list) +{ + void *memory; + struct nk_vec2 *point; + NK_ASSERT(list->path_count); + memory = nk_buffer_memory(list->buffer); + point = nk_ptr_add(struct nk_vec2, memory, list->path_offset); + point += (list->path_count-1); + return *point; +} + +NK_INTERN struct nk_draw_command* +nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip, + nk_handle texture) +{ + NK_STORAGE const nk_size cmd_align = NK_ALIGNOF(struct nk_draw_command); + NK_STORAGE const nk_size cmd_size = sizeof(struct nk_draw_command); + struct nk_draw_command *cmd; + + NK_ASSERT(list); + cmd = (struct nk_draw_command*) + nk_buffer_alloc(list->buffer, NK_BUFFER_BACK, cmd_size, cmd_align); + + if (!cmd) return 0; + if (!list->cmd_count) { + nk_byte *memory = (nk_byte*)nk_buffer_memory(list->buffer); + nk_size total = nk_buffer_total(list->buffer); + memory = nk_ptr_add(nk_byte, memory, total); + list->cmd_offset = (nk_size)(memory - (nk_byte*)cmd); + } + + cmd->elem_count = 0; + cmd->clip_rect = clip; + cmd->texture = texture; + + list->cmd_count++; + list->clip_rect = clip; + return cmd; +} + +NK_INTERN struct nk_draw_command* +nk_draw_list_command_last(struct nk_draw_list *list) +{ + void *memory; + nk_size size; + struct nk_draw_command *cmd; + NK_ASSERT(list->cmd_count); + + memory = nk_buffer_memory(list->buffer); + size = nk_buffer_total(list->buffer); + cmd = nk_ptr_add(struct nk_draw_command, memory, size - list->cmd_offset); + return (cmd - (list->cmd_count-1)); +} + +NK_INTERN void +nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, rect, list->config.null.texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) + prev->clip_rect = rect; + nk_draw_list_push_command(list, rect, prev->texture); + } +} + +NK_INTERN void +nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, nk_null_rect, texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) + prev->texture = texture; + else if (prev->texture.id != texture.id) + nk_draw_list_push_command(list, prev->clip_rect, texture); + } +} + +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + struct nk_draw_command *prev; + nk_draw_list_push_command(list, nk_null_rect, list->config.null.texture); + prev = nk_draw_list_command_last(list); + prev->userdata = userdata; + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) { + prev->userdata = userdata; + } else if (prev->userdata.ptr != userdata.ptr) { + nk_draw_list_push_command(list, prev->clip_rect, prev->texture); + prev = nk_draw_list_command_last(list); + prev->userdata = userdata; + } + } +} +#endif + +NK_INTERN void* +nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count) +{ + void *vtx; + NK_ASSERT(list); + if (!list) return 0; + vtx = nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, + list->config.vertex_size*count, list->config.vertex_alignment); + if (!vtx) return 0; + list->vertex_count += (unsigned int)count; + return vtx; +} + +NK_INTERN nk_draw_index* +nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count) +{ + nk_draw_index *ids; + struct nk_draw_command *cmd; + NK_STORAGE const nk_size elem_align = NK_ALIGNOF(nk_draw_index); + NK_STORAGE const nk_size elem_size = sizeof(nk_draw_index); + NK_ASSERT(list); + if (!list) return 0; + + ids = (nk_draw_index*) + nk_buffer_alloc(list->elements, NK_BUFFER_FRONT, elem_size*count, elem_align); + if (!ids) return 0; + cmd = nk_draw_list_command_last(list); + list->element_count += (unsigned int)count; + cmd->elem_count += (unsigned int)count; + return ids; +} + +static int +nk_draw_vertex_layout_element_is_end_of_layout( + const struct nk_draw_vertex_layout_element *element) +{ + return (element->attribute == NK_VERTEX_ATTRIBUTE_COUNT || + element->format == NK_FORMAT_COUNT); +} + +static void +nk_draw_vertex_color(void *attribute, const float *values, + enum nk_draw_vertex_layout_format format) +{ + /* if this triggers you tried to provide a value format for a color */ + NK_ASSERT(format >= NK_FORMAT_COLOR_BEGIN); + NK_ASSERT(format <= NK_FORMAT_COLOR_END); + if (format < NK_FORMAT_COLOR_BEGIN || format > NK_FORMAT_COLOR_END) return; + + switch (format) { + default: NK_ASSERT(0 && "Invalid vertex layout color format"); break; + case NK_FORMAT_R8G8B8A8: + case NK_FORMAT_R8G8B8: { + struct nk_color col = nk_rgba_fv(values); + NK_MEMCPY(attribute, &col.r, sizeof(col)); + } break; + case NK_FORMAT_R16G15B16: { + nk_ushort col[3]; + col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX); + col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX); + col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R16G15B16A16: { + nk_ushort col[4]; + col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX); + col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX); + col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX); + col[3] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[3] * NK_USHORT_MAX, NK_USHORT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32: { + nk_uint col[3]; + col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX); + col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX); + col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32A32: { + nk_uint col[4]; + col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX); + col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX); + col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX); + col[3] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[3] * NK_UINT_MAX, NK_UINT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32A32_FLOAT: + NK_MEMCPY(attribute, values, sizeof(float)*4); + break; + case NK_FORMAT_R32G32B32A32_DOUBLE: { + double col[4]; + col[0] = (double)NK_SATURATE(values[0]); + col[1] = (double)NK_SATURATE(values[1]); + col[2] = (double)NK_SATURATE(values[2]); + col[3] = (double)NK_SATURATE(values[3]); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_RGB32: + case NK_FORMAT_RGBA32: { + struct nk_color col = nk_rgba_fv(values); + nk_uint color = nk_color_u32(col); + NK_MEMCPY(attribute, &color, sizeof(color)); + } break; + } +} + +static void +nk_draw_vertex_element(void *dst, const float *values, int value_count, + enum nk_draw_vertex_layout_format format) +{ + int value_index; + void *attribute = dst; + /* if this triggers you tried to provide a color format for a value */ + NK_ASSERT(format < NK_FORMAT_COLOR_BEGIN); + if (format >= NK_FORMAT_COLOR_BEGIN && format <= NK_FORMAT_COLOR_END) return; + for (value_index = 0; value_index < value_count; ++value_index) { + switch (format) { + default: NK_ASSERT(0 && "invalid vertex layout format"); break; + case NK_FORMAT_SCHAR: { + char value = (char)NK_CLAMP(NK_SCHAR_MIN, values[value_index], NK_SCHAR_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(char)); + } break; + case NK_FORMAT_SSHORT: { + nk_short value = (nk_short)NK_CLAMP(NK_SSHORT_MIN, values[value_index], NK_SSHORT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(value)); + } break; + case NK_FORMAT_SINT: { + nk_int value = (nk_int)NK_CLAMP(NK_SINT_MIN, values[value_index], NK_SINT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(nk_int)); + } break; + case NK_FORMAT_UCHAR: { + unsigned char value = (unsigned char)NK_CLAMP(NK_UCHAR_MIN, values[value_index], NK_UCHAR_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(unsigned char)); + } break; + case NK_FORMAT_USHORT: { + nk_ushort value = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[value_index], NK_USHORT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(value)); + } break; + case NK_FORMAT_UINT: { + nk_uint value = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[value_index], NK_UINT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(nk_uint)); + } break; + case NK_FORMAT_FLOAT: + NK_MEMCPY(attribute, &values[value_index], sizeof(values[value_index])); + attribute = (void*)((char*)attribute + sizeof(float)); + break; + case NK_FORMAT_DOUBLE: { + double value = (double)values[value_index]; + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(double)); + } break; + } + } +} + +NK_INTERN void* +nk_draw_vertex(void *dst, const struct nk_convert_config *config, + struct nk_vec2 pos, struct nk_vec2 uv, struct nk_colorf color) +{ + void *result = (void*)((char*)dst + config->vertex_size); + const struct nk_draw_vertex_layout_element *elem_iter = config->vertex_layout; + while (!nk_draw_vertex_layout_element_is_end_of_layout(elem_iter)) { + void *address = (void*)((char*)dst + elem_iter->offset); + switch (elem_iter->attribute) { + case NK_VERTEX_ATTRIBUTE_COUNT: + default: NK_ASSERT(0 && "wrong element attribute"); + case NK_VERTEX_POSITION: nk_draw_vertex_element(address, &pos.x, 2, elem_iter->format); break; + case NK_VERTEX_TEXCOORD: nk_draw_vertex_element(address, &uv.x, 2, elem_iter->format); break; + case NK_VERTEX_COLOR: nk_draw_vertex_color(address, &color.r, elem_iter->format); break; + } + elem_iter++; + } + return result; +} + +NK_API void +nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *points, + const unsigned int points_count, struct nk_color color, enum nk_draw_list_stroke closed, + float thickness, enum nk_anti_aliasing aliasing) +{ + nk_size count; + int thick_line; + struct nk_colorf col; + struct nk_colorf col_trans; + NK_ASSERT(list); + if (!list || points_count < 2) return; + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + count = points_count; + if (!closed) count = points_count-1; + thick_line = thickness > 1.0f; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + nk_color_fv(&col.r, color); + col_trans = col; + col_trans.a = 0; + + if (aliasing == NK_ANTI_ALIASING_ON) { + /* ANTI-ALIASED STROKE */ + const float AA_SIZE = 1.0f; + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + + /* allocate vertices and elements */ + nk_size i1 = 0; + nk_size vertex_offset; + nk_size index = list->vertex_count; + + const nk_size idx_count = (thick_line) ? (count * 18) : (count * 12); + const nk_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3); + + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + nk_size size; + struct nk_vec2 *normals, *temp; + NK_ASSERT(vtx && ids); + if (!vtx || !ids) return; + + /* temporary allocate normals + points */ + vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * ((thick_line) ? 5 : 3) * points_count; + normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + NK_ASSERT(normals); + if (!normals) return; + temp = normals + points_count; + + /* make sure vertex pointer is still correct */ + vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); + + /* calculate normals */ + for (i1 = 0; i1 < count; ++i1) { + const nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + struct nk_vec2 diff = nk_vec2_sub(points[i2], points[i1]); + float len; + + /* vec2 inverted length */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + + diff = nk_vec2_muls(diff, len); + normals[i1].x = diff.y; + normals[i1].y = -diff.x; + } + + if (!closed) + normals[points_count-1] = normals[points_count-2]; + + if (!thick_line) { + nk_size idx1, i; + if (!closed) { + struct nk_vec2 d; + temp[0] = nk_vec2_add(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + temp[1] = nk_vec2_sub(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + d = nk_vec2_muls(normals[points_count-1], AA_SIZE); + temp[(points_count-1) * 2 + 0] = nk_vec2_add(points[points_count-1], d); + temp[(points_count-1) * 2 + 1] = nk_vec2_sub(points[points_count-1], d); + } + + /* fill elements */ + idx1 = index; + for (i1 = 0; i1 < count; i1++) { + struct nk_vec2 dm; + float dmr2; + nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3); + + /* average normals */ + dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm = nk_vec2_muls(dm, AA_SIZE); + temp[i2*2+0] = nk_vec2_add(points[i2], dm); + temp[i2*2+1] = nk_vec2_sub(points[i2], dm); + + ids[0] = (nk_draw_index)(idx2 + 0); ids[1] = (nk_draw_index)(idx1+0); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+0); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11]= (nk_draw_index)(idx2+1); + ids += 12; + idx1 = idx2; + } + + /* fill vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->config.null.uv; + vtx = nk_draw_vertex(vtx, &list->config, points[i], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+0], uv, col_trans); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+1], uv, col_trans); + } + } else { + nk_size idx1, i; + const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + if (!closed) { + struct nk_vec2 d1 = nk_vec2_muls(normals[0], half_inner_thickness + AA_SIZE); + struct nk_vec2 d2 = nk_vec2_muls(normals[0], half_inner_thickness); + + temp[0] = nk_vec2_add(points[0], d1); + temp[1] = nk_vec2_add(points[0], d2); + temp[2] = nk_vec2_sub(points[0], d2); + temp[3] = nk_vec2_sub(points[0], d1); + + d1 = nk_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE); + d2 = nk_vec2_muls(normals[points_count-1], half_inner_thickness); + + temp[(points_count-1)*4+0] = nk_vec2_add(points[points_count-1], d1); + temp[(points_count-1)*4+1] = nk_vec2_add(points[points_count-1], d2); + temp[(points_count-1)*4+2] = nk_vec2_sub(points[points_count-1], d2); + temp[(points_count-1)*4+3] = nk_vec2_sub(points[points_count-1], d1); + } + + /* add all elements */ + idx1 = index; + for (i1 = 0; i1 < count; ++i1) { + struct nk_vec2 dm_out, dm_in; + const nk_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4); + + /* average normals */ + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + float dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm_out = nk_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE)); + dm_in = nk_vec2_muls(dm, half_inner_thickness); + temp[i2*4+0] = nk_vec2_add(points[i2], dm_out); + temp[i2*4+1] = nk_vec2_add(points[i2], dm_in); + temp[i2*4+2] = nk_vec2_sub(points[i2], dm_in); + temp[i2*4+3] = nk_vec2_sub(points[i2], dm_out); + + /* add indexes */ + ids[0] = (nk_draw_index)(idx2 + 1); ids[1] = (nk_draw_index)(idx1+1); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+1); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11] = (nk_draw_index)(idx2+1); + ids[12]= (nk_draw_index)(idx2 + 2); ids[13] = (nk_draw_index)(idx1+2); + ids[14]= (nk_draw_index)(idx1 + 3); ids[15] = (nk_draw_index)(idx1+3); + ids[16]= (nk_draw_index)(idx2 + 3); ids[17] = (nk_draw_index)(idx2+2); + ids += 18; + idx1 = idx2; + } + + /* add vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->config.null.uv; + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+0], uv, col_trans); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+1], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+2], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+3], uv, col_trans); + } + } + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + /* NON ANTI-ALIASED STROKE */ + nk_size i1 = 0; + nk_size idx = list->vertex_count; + const nk_size idx_count = count * 6; + const nk_size vtx_count = count * 4; + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + if (!vtx || !ids) return; + + for (i1 = 0; i1 < count; ++i1) { + float dx, dy; + const struct nk_vec2 uv = list->config.null.uv; + const nk_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; + const struct nk_vec2 p1 = points[i1]; + const struct nk_vec2 p2 = points[i2]; + struct nk_vec2 diff = nk_vec2_sub(p2, p1); + float len; + + /* vec2 inverted length */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + /* add vertices */ + dx = diff.x * (thickness * 0.5f); + dy = diff.y * (thickness * 0.5f); + + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x + dy, p1.y - dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x + dy, p2.y - dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x - dy, p2.y + dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x - dy, p1.y + dx), uv, col); + + ids[0] = (nk_draw_index)(idx+0); ids[1] = (nk_draw_index)(idx+1); + ids[2] = (nk_draw_index)(idx+2); ids[3] = (nk_draw_index)(idx+0); + ids[4] = (nk_draw_index)(idx+2); ids[5] = (nk_draw_index)(idx+3); + + ids += 6; + idx += 4; + } + } +} + +NK_API void +nk_draw_list_fill_poly_convex(struct nk_draw_list *list, + const struct nk_vec2 *points, const unsigned int points_count, + struct nk_color color, enum nk_anti_aliasing aliasing) +{ + struct nk_colorf col; + struct nk_colorf col_trans; + + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + NK_ASSERT(list); + if (!list || points_count < 3) return; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + nk_color_fv(&col.r, color); + col_trans = col; + col_trans.a = 0; + + if (aliasing == NK_ANTI_ALIASING_ON) { + nk_size i = 0; + nk_size i0 = 0; + nk_size i1 = 0; + + const float AA_SIZE = 1.0f; + nk_size vertex_offset = 0; + nk_size index = list->vertex_count; + + const nk_size idx_count = (points_count-2)*3 + points_count*6; + const nk_size vtx_count = (points_count*2); + + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + nk_size size = 0; + struct nk_vec2 *normals = 0; + unsigned int vtx_inner_idx = (unsigned int)(index + 0); + unsigned int vtx_outer_idx = (unsigned int)(index + 1); + if (!vtx || !ids) return; + + /* temporary allocate normals */ + vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * points_count; + normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + NK_ASSERT(normals); + if (!normals) return; + vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); + + /* add elements */ + for (i = 2; i < points_count; i++) { + ids[0] = (nk_draw_index)(vtx_inner_idx); + ids[1] = (nk_draw_index)(vtx_inner_idx + ((i-1) << 1)); + ids[2] = (nk_draw_index)(vtx_inner_idx + (i << 1)); + ids += 3; + } + + /* compute normals */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + struct nk_vec2 p0 = points[i0]; + struct nk_vec2 p1 = points[i1]; + struct nk_vec2 diff = nk_vec2_sub(p1, p0); + + /* vec2 inverted length */ + float len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + normals[i0].x = diff.y; + normals[i0].y = -diff.x; + } + + /* add vertices + indexes */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + const struct nk_vec2 uv = list->config.null.uv; + struct nk_vec2 n0 = normals[i0]; + struct nk_vec2 n1 = normals[i1]; + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(n0, n1), 0.5f); + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f / dmr2; + scale = NK_MIN(scale, 100.0f); + dm = nk_vec2_muls(dm, scale); + } + dm = nk_vec2_muls(dm, AA_SIZE * 0.5f); + + /* add vertices */ + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_sub(points[i1], dm), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_add(points[i1], dm), uv, col_trans); + + /* add indexes */ + ids[0] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids[1] = (nk_draw_index)(vtx_inner_idx+(i0<<1)); + ids[2] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[3] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[4] = (nk_draw_index)(vtx_outer_idx+(i1<<1)); + ids[5] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids += 6; + } + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + nk_size i = 0; + nk_size index = list->vertex_count; + const nk_size idx_count = (points_count-2)*3; + const nk_size vtx_count = points_count; + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + if (!vtx || !ids) return; + for (i = 0; i < vtx_count; ++i) + vtx = nk_draw_vertex(vtx, &list->config, points[i], list->config.null.uv, col); + for (i = 2; i < points_count; ++i) { + ids[0] = (nk_draw_index)index; + ids[1] = (nk_draw_index)(index+ i - 1); + ids[2] = (nk_draw_index)(index+i); + ids += 3; + } + } +} + +NK_API void +nk_draw_list_path_clear(struct nk_draw_list *list) +{ + NK_ASSERT(list); + if (!list) return; + nk_buffer_reset(list->buffer, NK_BUFFER_FRONT); + list->path_count = 0; + list->path_offset = 0; +} + +NK_API void +nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) +{ + struct nk_vec2 *points = 0; + struct nk_draw_command *cmd = 0; + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) + nk_draw_list_add_clip(list, nk_null_rect); + + cmd = nk_draw_list_command_last(list); + if (cmd && cmd->texture.ptr != list->config.null.texture.ptr) + nk_draw_list_push_image(list, list->config.null.texture); + + points = nk_draw_list_alloc_path(list, 1); + if (!points) return; + points[0] = pos; +} + +NK_API void +nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center, + float radius, int a_min, int a_max) +{ + int a = 0; + NK_ASSERT(list); + if (!list) return; + if (a_min <= a_max) { + for (a = a_min; a <= a_max; a++) { + const struct nk_vec2 c = list->circle_vtx[(nk_size)a % NK_LEN(list->circle_vtx)]; + const float x = center.x + c.x * radius; + const float y = center.y + c.y * radius; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + } + } +} + +NK_API void +nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center, + float radius, float a_min, float a_max, unsigned int segments) +{ + unsigned int i = 0; + NK_ASSERT(list); + if (!list) return; + if (radius == 0.0f) return; + for (i = 0; i <= segments; ++i) { + const float a = a_min + ((float)i / ((float)segments) * (a_max - a_min)); + const float x = center.x + (float)NK_COS(a) * radius; + const float y = center.y + (float)NK_SIN(a) * radius; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + } +} + +NK_API void +nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, float rounding) +{ + float r; + NK_ASSERT(list); + if (!list) return; + r = rounding; + r = NK_MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x)); + r = NK_MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y)); + + if (r == 0.0f) { + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, nk_vec2(b.x,a.y)); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, nk_vec2(a.x,b.y)); + } else { + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, a.y + r), r, 6, 9); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, a.y + r), r, 9, 12); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, b.y - r), r, 0, 3); + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, b.y - r), r, 3, 6); + } +} + +NK_API void +nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2, + struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments) +{ + float t_step; + unsigned int i_step; + struct nk_vec2 p1; + + NK_ASSERT(list); + NK_ASSERT(list->path_count); + if (!list || !list->path_count) return; + num_segments = NK_MAX(num_segments, 1); + + p1 = nk_draw_list_path_last(list); + t_step = 1.0f/(float)num_segments; + for (i_step = 1; i_step <= num_segments; ++i_step) { + float t = t_step * (float)i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t * t *t; + float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; + float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; + nk_draw_list_path_line_to(list, nk_vec2(x,y)); + } +} + +NK_API void +nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_fill_poly_convex(list, points, list->path_count, color, list->config.shape_AA); + nk_draw_list_path_clear(list); +} + +NK_API void +nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color, + enum nk_draw_list_stroke closed, float thickness) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_stroke_poly_line(list, points, list->path_count, color, + closed, thickness, list->config.line_AA); + nk_draw_list_path_clear(list); +} + +NK_API void +nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, nk_vec2_add(a, nk_vec2(0.5f, 0.5f))); + nk_draw_list_path_line_to(list, nk_vec2_add(b, nk_vec2(0.5f, 0.5f))); + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} + +NK_API void +nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_rect_to(list, nk_vec2(rect.x + 0.5f, rect.y + 0.5f), + nk_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_rect_to(list, nk_vec2(rect.x + 0.5f, rect.y + 0.5f), + nk_vec2(rect.x + rect.w + 0.5f, rect.y + rect.h + 0.5f), rounding); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + void *vtx; + struct nk_colorf col_left, col_top; + struct nk_colorf col_right, col_bottom; + nk_draw_index *idx; + nk_draw_index index; + + nk_color_fv(&col_left.r, left); + nk_color_fv(&col_right.r, right); + nk_color_fv(&col_top.r, top); + nk_color_fv(&col_bottom.r, bottom); + + NK_ASSERT(list); + if (!list) return; + + nk_draw_list_push_image(list, list->config.null.texture); + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y), list->config.null.uv, col_left); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y), list->config.null.uv, col_top); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.null.uv, col_right); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.null.uv, col_bottom); +} + +NK_API void +nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs, float thickness) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0, + struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, + struct nk_color col, unsigned int segments, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, p0); + nk_draw_list_path_curve_to(list, cp0, cp1, p1, segments); + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} + +NK_INTERN void +nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 c, struct nk_vec2 uva, struct nk_vec2 uvc, + struct nk_color color) +{ + void *vtx; + struct nk_vec2 uvb; + struct nk_vec2 uvd; + struct nk_vec2 b; + struct nk_vec2 d; + + struct nk_colorf col; + nk_draw_index *idx; + nk_draw_index index; + NK_ASSERT(list); + if (!list) return; + + nk_color_fv(&col.r, color); + uvb = nk_vec2(uvc.x, uva.y); + uvd = nk_vec2(uva.x, uvc.y); + b = nk_vec2(c.x, a.y); + d = nk_vec2(a.x, c.y); + + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx = nk_draw_vertex(vtx, &list->config, a, uva, col); + vtx = nk_draw_vertex(vtx, &list->config, b, uvb, col); + vtx = nk_draw_vertex(vtx, &list->config, c, uvc, col); + vtx = nk_draw_vertex(vtx, &list->config, d, uvd, col); +} + +NK_API void +nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture, + struct nk_rect rect, struct nk_color color) +{ + NK_ASSERT(list); + if (!list) return; + /* push new command with given texture */ + nk_draw_list_push_image(list, texture.handle); + if (nk_image_is_subimage(&texture)) { + /* add region inside of the texture */ + struct nk_vec2 uv[2]; + uv[0].x = (float)texture.region[0]/(float)texture.w; + uv[0].y = (float)texture.region[1]/(float)texture.h; + uv[1].x = (float)(texture.region[0] + texture.region[2])/(float)texture.w; + uv[1].y = (float)(texture.region[1] + texture.region[3])/(float)texture.h; + nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), uv[0], uv[1], color); + } else nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), + nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color); +} + +NK_API void +nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font, + struct nk_rect rect, const char *text, int len, float font_height, + struct nk_color fg) +{ + float x = 0; + int text_len = 0; + nk_rune unicode = 0; + nk_rune next = 0; + int glyph_len = 0; + int next_glyph_len = 0; + struct nk_user_font_glyph g; + + NK_ASSERT(list); + if (!list || !len || !text) return; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + list->clip_rect.x, list->clip_rect.y, list->clip_rect.w, list->clip_rect.h)) return; + + nk_draw_list_push_image(list, font->texture); + x = rect.x; + glyph_len = text_len = nk_utf_decode(text, &unicode, len); + if (!glyph_len) return; + + /* draw every glyph image */ + fg.a = (nk_byte)((float)fg.a * list->config.global_alpha); + while (text_len <= len && glyph_len) { + float gx, gy, gh, gw; + float char_width = 0; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + next_glyph_len = nk_utf_decode(text + text_len, &next, (int)len - text_len); + font->query(font->userdata, font_height, &g, unicode, + (next == NK_UTF_INVALID) ? '\0' : next); + + /* calculate and draw glyph drawing rectangle and image */ + gx = x + g.offset.x; + gy = rect.y + g.offset.y; + gw = g.width; gh = g.height; + char_width = g.xadvance; + nk_draw_list_push_rect_uv(list, nk_vec2(gx,gy), nk_vec2(gx + gw, gy+ gh), + g.uv[0], g.uv[1], fg); + + /* offset next glyph */ + text_len += glyph_len; + x += char_width; + glyph_len = next_glyph_len; + unicode = next; + } +} + +NK_API void +nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *vertices, struct nk_buffer *elements, + const struct nk_convert_config *config) +{ + const struct nk_command *cmd; + NK_ASSERT(ctx); + NK_ASSERT(cmds); + NK_ASSERT(vertices); + NK_ASSERT(elements); + NK_ASSERT(config); + NK_ASSERT(config->vertex_layout); + NK_ASSERT(config->vertex_size); + if (!ctx || !cmds || !vertices || !elements || !config || !config->vertex_layout) + return; + + nk_draw_list_setup(&ctx->draw_list, config, cmds, vertices, elements); + nk_foreach(cmd, ctx) + { +#ifdef NK_INCLUDE_COMMAND_USERDATA + ctx->draw_list.userdata = cmd->userdata; +#endif + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s = (const struct nk_command_scissor*)cmd; + nk_draw_list_add_clip(&ctx->draw_list, nk_rect(s->x, s->y, s->w, s->h)); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line*)cmd; + nk_draw_list_stroke_line(&ctx->draw_list, nk_vec2(l->begin.x, l->begin.y), + nk_vec2(l->end.x, l->end.y), l->color, l->line_thickness); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve*)cmd; + nk_draw_list_stroke_curve(&ctx->draw_list, nk_vec2(q->begin.x, q->begin.y), + nk_vec2(q->ctrl[0].x, q->ctrl[0].y), nk_vec2(q->ctrl[1].x, + q->ctrl[1].y), nk_vec2(q->end.x, q->end.y), q->color, + config->curve_segment_count, q->line_thickness); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect*)cmd; + nk_draw_list_stroke_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding, r->line_thickness); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled*)cmd; + nk_draw_list_fill_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: { + const struct nk_command_rect_multi_color *r = (const struct nk_command_rect_multi_color*)cmd; + nk_draw_list_fill_rect_multi_color(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->left, r->top, r->right, r->bottom); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle*)cmd; + nk_draw_list_stroke_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count, c->line_thickness); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_draw_list_fill_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count); + } break; + case NK_COMMAND_ARC: { + const struct nk_command_arc *c = (const struct nk_command_arc*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_stroke(&ctx->draw_list, c->color, NK_STROKE_CLOSED, c->line_thickness); + } break; + case NK_COMMAND_ARC_FILLED: { + const struct nk_command_arc_filled *c = (const struct nk_command_arc_filled*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_fill(&ctx->draw_list, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd; + nk_draw_list_stroke_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color, + t->line_thickness); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled*)cmd; + nk_draw_list_fill_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color); + } break; + case NK_COMMAND_POLYGON: { + int i; + const struct nk_command_polygon*p = (const struct nk_command_polygon*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_CLOSED, p->line_thickness); + } break; + case NK_COMMAND_POLYGON_FILLED: { + int i; + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_fill(&ctx->draw_list, p->color); + } break; + case NK_COMMAND_POLYLINE: { + int i; + const struct nk_command_polyline *p = (const struct nk_command_polyline*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_OPEN, p->line_thickness); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_draw_list_add_text(&ctx->draw_list, t->font, nk_rect(t->x, t->y, t->w, t->h), + t->string, t->length, t->height, t->foreground); + } break; + case NK_COMMAND_IMAGE: { + const struct nk_command_image *i = (const struct nk_command_image*)cmd; + nk_draw_list_add_image(&ctx->draw_list, i->img, nk_rect(i->x, i->y, i->w, i->h), i->col); + } break; + default: break; + } + } +} + +NK_API const struct nk_draw_command* +nk__draw_begin(const struct nk_context *ctx, + const struct nk_buffer *buffer) +{return nk__draw_list_begin(&ctx->draw_list, buffer);} + +NK_API const struct nk_draw_command* +nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buffer) +{return nk__draw_list_end(&ctx->draw_list, buffer);} + +NK_API const struct nk_draw_command* +nk__draw_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_context *ctx) +{return nk__draw_list_next(cmd, buffer, &ctx->draw_list);} + +#endif + +/* + * ============================================================== + * + * FONT HANDLING + * + * =============================================================== + */ +#ifdef NK_INCLUDE_FONT_BAKING +/* ------------------------------------------------------------- + * + * RECT PACK + * + * --------------------------------------------------------------*/ +/* stb_rect_pack.h - v0.05 - public domain - rectangle packing */ +/* Sean Barrett 2014 */ +#define NK_RP__MAXVAL 0xffff +typedef unsigned short nk_rp_coord; + +struct nk_rp_rect { + /* reserved for your use: */ + int id; + /* input: */ + nk_rp_coord w, h; + /* output: */ + nk_rp_coord x, y; + int was_packed; + /* non-zero if valid packing */ +}; /* 16 bytes, nominally */ + +struct nk_rp_node { + nk_rp_coord x,y; + struct nk_rp_node *next; +}; + +struct nk_rp_context { + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + struct nk_rp_node *active_head; + struct nk_rp_node *free_head; + struct nk_rp_node extra[2]; + /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */ +}; + +struct nk_rp__findresult { + int x,y; + struct nk_rp_node **prev_link; +}; + +enum NK_RP_HEURISTIC { + NK_RP_HEURISTIC_Skyline_default=0, + NK_RP_HEURISTIC_Skyline_BL_sortHeight = NK_RP_HEURISTIC_Skyline_default, + NK_RP_HEURISTIC_Skyline_BF_sortHeight +}; +enum NK_RP_INIT_STATE{NK_RP__INIT_skyline = 1}; + +NK_INTERN void +nk_rp_setup_allow_out_of_mem(struct nk_rp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + /* if it's ok to run out of memory, then don't bother aligning them; */ + /* this gives better packing, but may fail due to OOM (even though */ + /* the rectangles easily fit). @TODO a smarter approach would be to only */ + /* quantize once we've hit OOM, then we could get rid of this parameter. */ + context->align = 1; + else { + /* if it's not ok to run out of memory, then quantize the widths */ + /* so that num_nodes is always enough nodes. */ + /* */ + /* I.e. num_nodes * align >= width */ + /* align >= width / num_nodes */ + /* align = ceil(width/num_nodes) */ + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +NK_INTERN void +nk_rp_init_target(struct nk_rp_context *context, int width, int height, + struct nk_rp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + NK_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = 0; + context->init_mode = NK_RP__INIT_skyline; + context->heuristic = NK_RP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + nk_rp_setup_allow_out_of_mem(context, 0); + + /* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */ + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (nk_rp_coord) width; + context->extra[1].y = 65535; + context->extra[1].next = 0; +} + +/* find minimum y position if it starts at x1 */ +NK_INTERN int +nk_rp__skyline_find_min_y(struct nk_rp_context *c, struct nk_rp_node *first, + int x0, int width, int *pwaste) +{ + struct nk_rp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + NK_ASSERT(first->x <= x0); + NK_UNUSED(c); + + NK_ASSERT(node->next->x > x0); + /* we ended up handling this in the caller for efficiency */ + NK_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) + { + if (node->y > min_y) { + /* raise min_y higher. */ + /* we've accounted for all waste up to min_y, */ + /* but we'll now add more waste for everything we've visited */ + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + /* the first time through, visited_width might be reduced */ + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + /* add waste area */ + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + *pwaste = waste_area; + return min_y; +} + +NK_INTERN struct nk_rp__findresult +nk_rp__skyline_find_best_pos(struct nk_rp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + struct nk_rp__findresult fr; + struct nk_rp_node **prev, *node, *tail, **best = 0; + + /* align to multiple of c->align */ + width = (width + c->align - 1); + width -= width % c->align; + NK_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = nk_rp__skyline_find_min_y(c, node, node->x, width, &waste); + /* actually just want to test BL */ + if (c->heuristic == NK_RP_HEURISTIC_Skyline_BL_sortHeight) { + /* bottom left */ + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + /* best-fit */ + if (y + height <= c->height) { + /* can only use it if it first vertically */ + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + best_x = (best == 0) ? 0 : (*best)->x; + + /* if doing best-fit (BF), we also have to try aligning right edge to each node position */ + /* */ + /* e.g, if fitting */ + /* */ + /* ____________________ */ + /* |____________________| */ + /* */ + /* into */ + /* */ + /* | | */ + /* | ____________| */ + /* |____________| */ + /* */ + /* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */ + /* */ + /* This makes BF take about 2x the time */ + if (c->heuristic == NK_RP_HEURISTIC_Skyline_BF_sortHeight) + { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + /* find first node that's admissible */ + while (tail->x < width) + tail = tail->next; + while (tail) + { + int xpos = tail->x - width; + int y,waste; + NK_ASSERT(xpos >= 0); + /* find the left position that matches this */ + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + NK_ASSERT(node->next->x > xpos && node->x <= xpos); + y = nk_rp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + NK_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +NK_INTERN struct nk_rp__findresult +nk_rp__skyline_pack_rectangle(struct nk_rp_context *context, int width, int height) +{ + /* find best position according to heuristic */ + struct nk_rp__findresult res = nk_rp__skyline_find_best_pos(context, width, height); + struct nk_rp_node *node, *cur; + + /* bail if: */ + /* 1. it failed */ + /* 2. the best node doesn't fit (we don't always check this) */ + /* 3. we're out of memory */ + if (res.prev_link == 0 || res.y + height > context->height || context->free_head == 0) { + res.prev_link = 0; + return res; + } + + /* on success, create new node */ + node = context->free_head; + node->x = (nk_rp_coord) res.x; + node->y = (nk_rp_coord) (res.y + height); + + context->free_head = node->next; + + /* insert the new node into the right starting point, and */ + /* let 'cur' point to the remaining nodes needing to be */ + /* stitched back in */ + cur = *res.prev_link; + if (cur->x < res.x) { + /* preserve the existing one, so start testing with the next one */ + struct nk_rp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + /* from here, traverse cur and free the nodes, until we get to one */ + /* that shouldn't be freed */ + while (cur->next && cur->next->x <= res.x + width) { + struct nk_rp_node *next = cur->next; + /* move the current node to the free list */ + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + /* stitch the list back in */ + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (nk_rp_coord) (res.x + width); + return res; +} + +NK_INTERN int +nk_rect_height_compare(const void *a, const void *b) +{ + const struct nk_rp_rect *p = (const struct nk_rp_rect *) a; + const struct nk_rp_rect *q = (const struct nk_rp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +NK_INTERN int +nk_rect_original_order(const void *a, const void *b) +{ + const struct nk_rp_rect *p = (const struct nk_rp_rect *) a; + const struct nk_rp_rect *q = (const struct nk_rp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +static void +nk_rp_qsort(struct nk_rp_rect *array, unsigned int len, int(*cmp)(const void*,const void*)) +{ + /* iterative quick sort */ + #define NK_MAX_SORT_STACK 64 + unsigned right, left = 0, stack[NK_MAX_SORT_STACK], pos = 0; + unsigned seed = len/2 * 69069+1; + for (;;) { + for (; left+1 < len; len++) { + struct nk_rp_rect pivot, tmp; + if (pos == NK_MAX_SORT_STACK) len = stack[pos = 0]; + pivot = array[left+seed%(len-left)]; + seed = seed * 69069 + 1; + stack[pos++] = len; + for (right = left-1;;) { + while (cmp(&array[++right], &pivot) < 0); + while (cmp(&pivot, &array[--len]) < 0); + if (right >= len) break; + tmp = array[right]; + array[right] = array[len]; + array[len] = tmp; + } + } + if (pos == 0) break; + left = len; + len = stack[--pos]; + } + #undef NK_MAX_SORT_STACK +} + +NK_INTERN void +nk_rp_pack_rects(struct nk_rp_context *context, struct nk_rp_rect *rects, int num_rects) +{ + int i; + /* we use the 'was_packed' field internally to allow sorting/unsorting */ + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + } + + /* sort according to heuristic */ + nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_height_compare); + + for (i=0; i < num_rects; ++i) { + struct nk_rp__findresult fr = nk_rp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (nk_rp_coord) fr.x; + rects[i].y = (nk_rp_coord) fr.y; + } else { + rects[i].x = rects[i].y = NK_RP__MAXVAL; + } + } + + /* unsort */ + nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_original_order); + + /* set was_packed flags */ + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == NK_RP__MAXVAL && rects[i].y == NK_RP__MAXVAL); +} + +/* + * ============================================================== + * + * TRUETYPE + * + * =============================================================== + */ +/* stb_truetype.h - v1.07 - public domain */ +#define NK_TT_MAX_OVERSAMPLE 8 +#define NK_TT__OVER_MASK (NK_TT_MAX_OVERSAMPLE-1) + +struct nk_tt_bakedchar { + unsigned short x0,y0,x1,y1; + /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; +}; + +struct nk_tt_aligned_quad{ + float x0,y0,s0,t0; /* top-left */ + float x1,y1,s1,t1; /* bottom-right */ +}; + +struct nk_tt_packedchar { + unsigned short x0,y0,x1,y1; + /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; + float xoff2,yoff2; +}; + +struct nk_tt_pack_range { + float font_size; + int first_unicode_codepoint_in_range; + /* if non-zero, then the chars are continuous, and this is the first codepoint */ + int *array_of_unicode_codepoints; + /* if non-zero, then this is an array of unicode codepoints */ + int num_chars; + struct nk_tt_packedchar *chardata_for_range; /* output */ + unsigned char h_oversample, v_oversample; + /* don't set these, they're used internally */ +}; + +struct nk_tt_pack_context { + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +struct nk_tt_fontinfo { + const unsigned char* data; /* pointer to .ttf file */ + int fontstart;/* offset of start of font */ + int numGlyphs;/* number of glyphs, needed for range checking */ + int loca,head,glyf,hhea,hmtx,kern; /* table locations as offset from start of .ttf */ + int index_map; /* a cmap mapping for our chosen character encoding */ + int indexToLocFormat; /* format needed to map from glyph index to glyph */ +}; + +enum { + NK_TT_vmove=1, + NK_TT_vline, + NK_TT_vcurve +}; + +struct nk_tt_vertex { + short x,y,cx,cy; + unsigned char type,padding; +}; + +struct nk_tt__bitmap{ + int w,h,stride; + unsigned char *pixels; +}; + +struct nk_tt__hheap_chunk { + struct nk_tt__hheap_chunk *next; +}; +struct nk_tt__hheap { + struct nk_allocator alloc; + struct nk_tt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +}; + +struct nk_tt__edge { + float x0,y0, x1,y1; + int invert; +}; + +struct nk_tt__active_edge { + struct nk_tt__active_edge *next; + float fx,fdx,fdy; + float direction; + float sy; + float ey; +}; +struct nk_tt__point {float x,y;}; + +#define NK_TT_MACSTYLE_DONTCARE 0 +#define NK_TT_MACSTYLE_BOLD 1 +#define NK_TT_MACSTYLE_ITALIC 2 +#define NK_TT_MACSTYLE_UNDERSCORE 4 +#define NK_TT_MACSTYLE_NONE 8 +/* <= not same as 0, this makes us check the bitfield is 0 */ + +enum { /* platformID */ + NK_TT_PLATFORM_ID_UNICODE =0, + NK_TT_PLATFORM_ID_MAC =1, + NK_TT_PLATFORM_ID_ISO =2, + NK_TT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_UNICODE */ + NK_TT_UNICODE_EID_UNICODE_1_0 =0, + NK_TT_UNICODE_EID_UNICODE_1_1 =1, + NK_TT_UNICODE_EID_ISO_10646 =2, + NK_TT_UNICODE_EID_UNICODE_2_0_BMP=3, + NK_TT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_MICROSOFT */ + NK_TT_MS_EID_SYMBOL =0, + NK_TT_MS_EID_UNICODE_BMP =1, + NK_TT_MS_EID_SHIFTJIS =2, + NK_TT_MS_EID_UNICODE_FULL =10 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_MAC; same as Script Manager codes */ + NK_TT_MAC_EID_ROMAN =0, NK_TT_MAC_EID_ARABIC =4, + NK_TT_MAC_EID_JAPANESE =1, NK_TT_MAC_EID_HEBREW =5, + NK_TT_MAC_EID_CHINESE_TRAD =2, NK_TT_MAC_EID_GREEK =6, + NK_TT_MAC_EID_KOREAN =3, NK_TT_MAC_EID_RUSSIAN =7 +}; + +enum { /* languageID for NK_TT_PLATFORM_ID_MICROSOFT; same as LCID... */ + /* problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */ + NK_TT_MS_LANG_ENGLISH =0x0409, NK_TT_MS_LANG_ITALIAN =0x0410, + NK_TT_MS_LANG_CHINESE =0x0804, NK_TT_MS_LANG_JAPANESE =0x0411, + NK_TT_MS_LANG_DUTCH =0x0413, NK_TT_MS_LANG_KOREAN =0x0412, + NK_TT_MS_LANG_FRENCH =0x040c, NK_TT_MS_LANG_RUSSIAN =0x0419, + NK_TT_MS_LANG_GERMAN =0x0407, NK_TT_MS_LANG_SPANISH =0x0409, + NK_TT_MS_LANG_HEBREW =0x040d, NK_TT_MS_LANG_SWEDISH =0x041D +}; + +enum { /* languageID for NK_TT_PLATFORM_ID_MAC */ + NK_TT_MAC_LANG_ENGLISH =0 , NK_TT_MAC_LANG_JAPANESE =11, + NK_TT_MAC_LANG_ARABIC =12, NK_TT_MAC_LANG_KOREAN =23, + NK_TT_MAC_LANG_DUTCH =4 , NK_TT_MAC_LANG_RUSSIAN =32, + NK_TT_MAC_LANG_FRENCH =1 , NK_TT_MAC_LANG_SPANISH =6 , + NK_TT_MAC_LANG_GERMAN =2 , NK_TT_MAC_LANG_SWEDISH =5 , + NK_TT_MAC_LANG_HEBREW =10, NK_TT_MAC_LANG_CHINESE_SIMPLIFIED =33, + NK_TT_MAC_LANG_ITALIAN =3 , NK_TT_MAC_LANG_CHINESE_TRAD =19 +}; + +#define nk_ttBYTE(p) (* (const nk_byte *) (p)) +#define nk_ttCHAR(p) (* (const char *) (p)) + +#if defined(NK_BIGENDIAN) && !defined(NK_ALLOW_UNALIGNED_TRUETYPE) + #define nk_ttUSHORT(p) (* (nk_ushort *) (p)) + #define nk_ttSHORT(p) (* (nk_short *) (p)) + #define nk_ttULONG(p) (* (nk_uint *) (p)) + #define nk_ttLONG(p) (* (nk_int *) (p)) +#else + static nk_ushort nk_ttUSHORT(const nk_byte *p) { return (nk_ushort)(p[0]*256 + p[1]); } + static nk_short nk_ttSHORT(const nk_byte *p) { return (nk_short)(p[0]*256 + p[1]); } + static nk_uint nk_ttULONG(const nk_byte *p) { return (nk_uint)((p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]); } +#endif + +#define nk_tt_tag4(p,c0,c1,c2,c3)\ + ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define nk_tt_tag(p,str) nk_tt_tag4(p,str[0],str[1],str[2],str[3]) + +NK_INTERN int nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc, + int glyph_index, struct nk_tt_vertex **pvertices); + +NK_INTERN nk_uint +nk_tt__find_table(const nk_byte *data, nk_uint fontstart, const char *tag) +{ + /* @OPTIMIZE: binary search */ + nk_int num_tables = nk_ttUSHORT(data+fontstart+4); + nk_uint tabledir = fontstart + 12; + nk_int i; + for (i = 0; i < num_tables; ++i) { + nk_uint loc = tabledir + (nk_uint)(16*i); + if (nk_tt_tag(data+loc+0, tag)) + return nk_ttULONG(data+loc+8); + } + return 0; +} + +NK_INTERN int +nk_tt_InitFont(struct nk_tt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + nk_uint cmap, t; + nk_int i,numTables; + const nk_byte *data = (const nk_byte *) data2; + + info->data = data; + info->fontstart = fontstart; + + cmap = nk_tt__find_table(data, (nk_uint)fontstart, "cmap"); /* required */ + info->loca = (int)nk_tt__find_table(data, (nk_uint)fontstart, "loca"); /* required */ + info->head = (int)nk_tt__find_table(data, (nk_uint)fontstart, "head"); /* required */ + info->glyf = (int)nk_tt__find_table(data, (nk_uint)fontstart, "glyf"); /* required */ + info->hhea = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hhea"); /* required */ + info->hmtx = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hmtx"); /* required */ + info->kern = (int)nk_tt__find_table(data, (nk_uint)fontstart, "kern"); /* not required */ + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = nk_tt__find_table(data, (nk_uint)fontstart, "maxp"); + if (t) info->numGlyphs = nk_ttUSHORT(data+t+4); + else info->numGlyphs = 0xffff; + + /* find a cmap encoding table we understand *now* to avoid searching */ + /* later. (todo: could make this installable) */ + /* the same regardless of glyph. */ + numTables = nk_ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) + { + nk_uint encoding_record = cmap + 4 + 8 * (nk_uint)i; + /* find an encoding we understand: */ + switch(nk_ttUSHORT(data+encoding_record)) { + case NK_TT_PLATFORM_ID_MICROSOFT: + switch (nk_ttUSHORT(data+encoding_record+2)) { + case NK_TT_MS_EID_UNICODE_BMP: + case NK_TT_MS_EID_UNICODE_FULL: + /* MS/Unicode */ + info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4)); + break; + default: break; + } break; + case NK_TT_PLATFORM_ID_UNICODE: + /* Mac/iOS has these */ + /* all the encodingIDs are unicode, so we don't bother to check it */ + info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4)); + break; + default: break; + } + } + if (info->index_map == 0) + return 0; + info->indexToLocFormat = nk_ttUSHORT(data+info->head + 50); + return 1; +} + +NK_INTERN int +nk_tt_FindGlyphIndex(const struct nk_tt_fontinfo *info, int unicode_codepoint) +{ + const nk_byte *data = info->data; + nk_uint index_map = (nk_uint)info->index_map; + + nk_ushort format = nk_ttUSHORT(data + index_map + 0); + if (format == 0) { /* apple byte encoding */ + nk_int bytes = nk_ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return nk_ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + nk_uint first = nk_ttUSHORT(data + index_map + 6); + nk_uint count = nk_ttUSHORT(data + index_map + 8); + if ((nk_uint) unicode_codepoint >= first && (nk_uint) unicode_codepoint < first+count) + return nk_ttUSHORT(data + index_map + 10 + (unicode_codepoint - (int)first)*2); + return 0; + } else if (format == 2) { + NK_ASSERT(0); /* @TODO: high-byte mapping for japanese/chinese/korean */ + return 0; + } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */ + nk_ushort segcount = nk_ttUSHORT(data+index_map+6) >> 1; + nk_ushort searchRange = nk_ttUSHORT(data+index_map+8) >> 1; + nk_ushort entrySelector = nk_ttUSHORT(data+index_map+10); + nk_ushort rangeShift = nk_ttUSHORT(data+index_map+12) >> 1; + + /* do a binary search of the segments */ + nk_uint endCount = index_map + 14; + nk_uint search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + /* they lie from endCount .. endCount + segCount */ + /* but searchRange is the nearest power of two, so... */ + if (unicode_codepoint >= nk_ttUSHORT(data + search + rangeShift*2)) + search += (nk_uint)(rangeShift*2); + + /* now decrement to bias correctly to find smallest */ + search -= 2; + while (entrySelector) { + nk_ushort end; + searchRange >>= 1; + end = nk_ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += (nk_uint)(searchRange*2); + --entrySelector; + } + search += 2; + + { + nk_ushort offset, start; + nk_ushort item = (nk_ushort) ((search - endCount) >> 1); + + NK_ASSERT(unicode_codepoint <= nk_ttUSHORT(data + endCount + 2*item)); + start = nk_ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = nk_ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (nk_ushort) (unicode_codepoint + nk_ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return nk_ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + nk_uint ngroups = nk_ttULONG(data+index_map+12); + nk_int low,high; + low = 0; high = (nk_int)ngroups; + /* Binary search the right group. */ + while (low < high) { + nk_int mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */ + nk_uint start_char = nk_ttULONG(data+index_map+16+mid*12); + nk_uint end_char = nk_ttULONG(data+index_map+16+mid*12+4); + if ((nk_uint) unicode_codepoint < start_char) + high = mid; + else if ((nk_uint) unicode_codepoint > end_char) + low = mid+1; + else { + nk_uint start_glyph = nk_ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return (int)start_glyph + (int)unicode_codepoint - (int)start_char; + else /* format == 13 */ + return (int)start_glyph; + } + } + return 0; /* not found */ + } + /* @TODO */ + NK_ASSERT(0); + return 0; +} + +NK_INTERN void +nk_tt_setvertex(struct nk_tt_vertex *v, nk_byte type, nk_int x, nk_int y, nk_int cx, nk_int cy) +{ + v->type = type; + v->x = (nk_short) x; + v->y = (nk_short) y; + v->cx = (nk_short) cx; + v->cy = (nk_short) cy; +} + +NK_INTERN int +nk_tt__GetGlyfOffset(const struct nk_tt_fontinfo *info, int glyph_index) +{ + int g1,g2; + if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */ + if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */ + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ +} + +NK_INTERN int +nk_tt_GetGlyphBox(const struct nk_tt_fontinfo *info, int glyph_index, + int *x0, int *y0, int *x1, int *y1) +{ + int g = nk_tt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = nk_ttSHORT(info->data + g + 2); + if (y0) *y0 = nk_ttSHORT(info->data + g + 4); + if (x1) *x1 = nk_ttSHORT(info->data + g + 6); + if (y1) *y1 = nk_ttSHORT(info->data + g + 8); + return 1; +} + +NK_INTERN int +stbtt__close_shape(struct nk_tt_vertex *vertices, int num_vertices, int was_off, + int start_off, nk_int sx, nk_int sy, nk_int scx, nk_int scy, nk_int cx, nk_int cy) +{ + if (start_off) { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve,sx,sy,cx,cy); + else + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline,sx,sy,0,0); + } + return num_vertices; +} + +NK_INTERN int +nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc, + int glyph_index, struct nk_tt_vertex **pvertices) +{ + nk_short numberOfContours; + const nk_byte *endPtsOfContours; + const nk_byte *data = info->data; + struct nk_tt_vertex *vertices=0; + int num_vertices=0; + int g = nk_tt__GetGlyfOffset(info, glyph_index); + *pvertices = 0; + + if (g < 0) return 0; + numberOfContours = nk_ttSHORT(data + g); + if (numberOfContours > 0) { + nk_byte flags=0,flagcount; + nk_int ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + nk_int x,y,cx,cy,sx,sy, scx,scy; + const nk_byte *points; + endPtsOfContours = (data + g + 10); + ins = nk_ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+nk_ttUSHORT(endPtsOfContours + numberOfContours*2-2); + m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */ + vertices = (struct nk_tt_vertex *)alloc->alloc(alloc->userdata, 0, (nk_size)m * sizeof(vertices[0])); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + /* in first pass, we load uninterpreted data into the allocated array */ + /* above, shifted to the end of the array so we won't overwrite it when */ + /* we create our final data starting from the front */ + off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */ + + /* first load flags */ + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else --flagcount; + vertices[off+i].type = flags; + } + + /* now load x coordinates */ + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + nk_short dx = *points++; + x += (flags & 16) ? dx : -dx; /* ??? */ + } else { + if (!(flags & 16)) { + x = x + (nk_short) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (nk_short) x; + } + + /* now load y coordinates */ + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + nk_short dy = *points++; + y += (flags & 32) ? dy : -dy; /* ??? */ + } else { + if (!(flags & 32)) { + y = y + (nk_short) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (nk_short) y; + } + + /* now convert them to our format */ + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) + { + flags = vertices[off+i].type; + x = (nk_short) vertices[off+i].x; + y = (nk_short) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + /* now start the new one */ + start_off = !(flags & 1); + if (start_off) { + /* if we start off with an off-curve point, then when we need to find a point on the curve */ + /* where we can start, and we need to save some state for when we wraparound. */ + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + /* next point is also a curve point, so interpolate an on-point curve */ + sx = (x + (nk_int) vertices[off+i+1].x) >> 1; + sy = (y + (nk_int) vertices[off+i+1].y) >> 1; + } else { + /* otherwise just use the next point as our start point */ + sx = (nk_int) vertices[off+i+1].x; + sy = (nk_int) vertices[off+i+1].y; + ++i; /* we're using point i+1 as the starting point, so skip it */ + } + } else { + sx = x; + sy = y; + } + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + nk_ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) + { /* if it's a curve */ + if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */ + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, x,y, cx, cy); + else nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + /* Compound shapes. */ + int more = 1; + const nk_byte *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + + while (more) + { + nk_ushort flags, gidx; + int comp_num_verts = 0, i; + struct nk_tt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = (nk_ushort)nk_ttSHORT(comp); comp+=2; + gidx = (nk_ushort)nk_ttSHORT(comp); comp+=2; + + if (flags & 2) { /* XY values */ + if (flags & 1) { /* shorts */ + mtx[4] = nk_ttSHORT(comp); comp+=2; + mtx[5] = nk_ttSHORT(comp); comp+=2; + } else { + mtx[4] = nk_ttCHAR(comp); comp+=1; + mtx[5] = nk_ttCHAR(comp); comp+=1; + } + } else { + /* @TODO handle matching point */ + NK_ASSERT(0); + } + if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */ + mtx[0] = mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */ + mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */ + mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + } + + /* Find transformation scales. */ + m = (float) NK_SQRT(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) NK_SQRT(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + /* Get indexed glyph. */ + comp_num_verts = nk_tt_GetGlyphShape(info, alloc, gidx, &comp_verts); + if (comp_num_verts > 0) + { + /* Transform vertices. */ + for (i = 0; i < comp_num_verts; ++i) { + struct nk_tt_vertex* v = &comp_verts[i]; + short x,y; + x=v->x; y=v->y; + v->x = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + /* Append vertices. */ + tmp = (struct nk_tt_vertex*)alloc->alloc(alloc->userdata, 0, + (nk_size)(num_vertices+comp_num_verts)*sizeof(struct nk_tt_vertex)); + if (!tmp) { + if (vertices) alloc->free(alloc->userdata, vertices); + if (comp_verts) alloc->free(alloc->userdata, comp_verts); + return 0; + } + if (num_vertices > 0) NK_MEMCPY(tmp, vertices, (nk_size)num_vertices*sizeof(struct nk_tt_vertex)); + NK_MEMCPY(tmp+num_vertices, comp_verts, (nk_size)comp_num_verts*sizeof(struct nk_tt_vertex)); + if (vertices) alloc->free(alloc->userdata,vertices); + vertices = tmp; + alloc->free(alloc->userdata,comp_verts); + num_vertices += comp_num_verts; + } + /* More components ? */ + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + /* @TODO other compound variations? */ + NK_ASSERT(0); + } else { + /* numberOfCounters == 0, do nothing */ + } + *pvertices = vertices; + return num_vertices; +} + +NK_INTERN void +nk_tt_GetGlyphHMetrics(const struct nk_tt_fontinfo *info, int glyph_index, + int *advanceWidth, int *leftSideBearing) +{ + nk_ushort numOfLongHorMetrics = nk_ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) + *advanceWidth = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) + *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) + *advanceWidth = nk_ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) + *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +NK_INTERN void +nk_tt_GetFontVMetrics(const struct nk_tt_fontinfo *info, + int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = nk_ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = nk_ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = nk_ttSHORT(info->data+info->hhea + 8); +} + +NK_INTERN float +nk_tt_ScaleForPixelHeight(const struct nk_tt_fontinfo *info, float height) +{ + int fheight = nk_ttSHORT(info->data + info->hhea + 4) - nk_ttSHORT(info->data + info->hhea + 6); + return (float) height / (float)fheight; +} + +NK_INTERN float +nk_tt_ScaleForMappingEmToPixels(const struct nk_tt_fontinfo *info, float pixels) +{ + int unitsPerEm = nk_ttUSHORT(info->data + info->head + 18); + return pixels / (float)unitsPerEm; +} + +/*------------------------------------------------------------- + * antialiasing software rasterizer + * --------------------------------------------------------------*/ +NK_INTERN void +nk_tt_GetGlyphBitmapBoxSubpixel(const struct nk_tt_fontinfo *font, + int glyph, float scale_x, float scale_y,float shift_x, float shift_y, + int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!nk_tt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + /* e.g. space character */ + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */ + if (ix0) *ix0 = nk_ifloorf((float)x0 * scale_x + shift_x); + if (iy0) *iy0 = nk_ifloorf((float)-y1 * scale_y + shift_y); + if (ix1) *ix1 = nk_iceilf ((float)x1 * scale_x + shift_x); + if (iy1) *iy1 = nk_iceilf ((float)-y0 * scale_y + shift_y); + } +} + +NK_INTERN void +nk_tt_GetGlyphBitmapBox(const struct nk_tt_fontinfo *font, int glyph, + float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + nk_tt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +/*------------------------------------------------------------- + * Rasterizer + * --------------------------------------------------------------*/ +NK_INTERN void* +nk_tt__hheap_alloc(struct nk_tt__hheap *hh, nk_size size) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + struct nk_tt__hheap_chunk *c = (struct nk_tt__hheap_chunk *) + hh->alloc.alloc(hh->alloc.userdata, 0, + sizeof(struct nk_tt__hheap_chunk) + size * (nk_size)count); + if (c == 0) return 0; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * (nk_size)hh->num_remaining_in_head_chunk; + } +} + +NK_INTERN void +nk_tt__hheap_free(struct nk_tt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +NK_INTERN void +nk_tt__hheap_cleanup(struct nk_tt__hheap *hh) +{ + struct nk_tt__hheap_chunk *c = hh->head; + while (c) { + struct nk_tt__hheap_chunk *n = c->next; + hh->alloc.free(hh->alloc.userdata, c); + c = n; + } +} + +NK_INTERN struct nk_tt__active_edge* +nk_tt__new_active(struct nk_tt__hheap *hh, struct nk_tt__edge *e, + int off_x, float start_point) +{ + struct nk_tt__active_edge *z = (struct nk_tt__active_edge *) + nk_tt__hheap_alloc(hh, sizeof(*z)); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + /*STBTT_assert(e->y0 <= start_point); */ + if (!z) return z; + z->fdx = dxdy; + z->fdy = (dxdy != 0) ? (1/dxdy): 0; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= (float)off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} + +NK_INTERN void +nk_tt__handle_clipped_edge(float *scanline, int x, struct nk_tt__active_edge *e, + float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + NK_ASSERT(y0 < y1); + NK_ASSERT(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) NK_ASSERT(x1 <= x+1); + else if (x0 == x+1) NK_ASSERT(x1 >= x); + else if (x0 <= x) NK_ASSERT(x1 <= x); + else if (x0 >= x+1) NK_ASSERT(x1 >= x+1); + else NK_ASSERT(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1); + else { + NK_ASSERT(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + /* coverage = 1 - average x position */ + scanline[x] += (float)e->direction * (float)(y1-y0) * (1.0f-((x0-(float)x)+(x1-(float)x))/2.0f); + } +} + +NK_INTERN void +nk_tt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, + struct nk_tt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + while (e) + { + /* brute force every pixel */ + /* compute intersection points with top & bottom */ + NK_ASSERT(e->ey >= y_top); + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + nk_tt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + nk_tt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + nk_tt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float y0,y1; + float dy = e->fdy; + NK_ASSERT(e->sy <= y_bottom && e->ey >= y_top); + + /* compute endpoints of line segment clipped to this scanline (if the */ + /* line segment starts on this scanline. x0 is the intersection of the */ + /* line with y_top, but that may be off the line segment. */ + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + y0 = e->sy; + } else { + x_top = x0; + y0 = y_top; + } + + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + y1 = e->ey; + } else { + x_bottom = xb; + y1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) + { + /* from here on, we don't have to range check x values */ + if ((int) x_top == (int) x_bottom) { + float height; + /* simple case, only spans one pixel */ + int x = (int) x_top; + height = y1 - y0; + NK_ASSERT(x >= 0 && x < len); + scanline[x] += e->direction * (1.0f-(((float)x_top - (float)x) + ((float)x_bottom-(float)x))/2.0f) * (float)height; + scanline_fill[x] += e->direction * (float)height; /* everything right of this pixel is filled */ + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + /* covers 2+ pixels */ + if (x_top > x_bottom) + { + /* flip scanline vertically; signed area is the same */ + float t; + y0 = y_bottom - (y0 - y_top); + y1 = y_bottom - (y1 - y_top); + t = y0, y0 = y1, y1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + /* compute intersection with y axis at x1+1 */ + y_crossing = ((float)x1+1 - (float)x0) * (float)dy + (float)y_top; + + sign = e->direction; + /* area of the rectangle covered from y0..y_crossing */ + area = sign * (y_crossing-y0); + /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */ + scanline[x1] += area * (1.0f-((float)((float)x_top - (float)x1)+(float)(x1+1-x1))/2.0f); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += (float)dy * (float)(x2 - (x1+1)); + + scanline[x2] += area + sign * (1.0f-((float)(x2-x2)+((float)x_bottom-(float)x2))/2.0f) * (y1-y_crossing); + scanline_fill[x2] += sign * (y1-y0); + } + } + else + { + /* if edge goes outside of box we're drawing, we require */ + /* clipping logic. since this does not match the intended use */ + /* of this library, we use a different, very slow brute */ + /* force implementation */ + int x; + for (x=0; x < len; ++x) + { + /* cases: */ + /* */ + /* there can be up to two intersections with the pixel. any intersection */ + /* with left or right edges can be handled by splitting into two (or three) */ + /* regions. intersections with top & bottom do not necessitate case-wise logic. */ + /* */ + /* the old way of doing this found the intersections with the left & right edges, */ + /* then used some simple logic to produce up to three segments in sorted order */ + /* from top-to-bottom. however, this had a problem: if an x edge was epsilon */ + /* across the x border, then the corresponding y position might not be distinct */ + /* from the other y segment, and it might ignored as an empty segment. to avoid */ + /* that, we need to explicitly produce segments based on x positions. */ + + /* rename variables to clear pairs */ + float ya = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + float yb,y2; + + yb = ((float)x - x0) / dx + y_top; + y2 = ((float)x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { /* three segments descending down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { /* one segment */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x3,y3); + } + } + } + } + e = e->next; + } +} + +/* directly AA rasterize edges w/o supersampling */ +NK_INTERN void +nk_tt__rasterize_sorted_edges(struct nk_tt__bitmap *result, struct nk_tt__edge *e, + int n, int vsubsample, int off_x, int off_y, struct nk_allocator *alloc) +{ + struct nk_tt__hheap hh; + struct nk_tt__active_edge *active = 0; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + NK_UNUSED(vsubsample); + nk_zero_struct(hh); + hh.alloc = *alloc; + + if (result->w > 64) + scanline = (float *) alloc->alloc(alloc->userdata,0, (nk_size)(result->w*2+1) * sizeof(float)); + else scanline = scanline_data; + + scanline2 = scanline + result->w; + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) + { + /* find center of pixel for this scanline */ + float scan_y_top = (float)y + 0.0f; + float scan_y_bottom = (float)y + 1.0f; + struct nk_tt__active_edge **step = &active; + + NK_MEMSET(scanline , 0, (nk_size)result->w*sizeof(scanline[0])); + NK_MEMSET(scanline2, 0, (nk_size)(result->w+1)*sizeof(scanline[0])); + + /* update all active edges; */ + /* remove all active edges that terminate before the top of this scanline */ + while (*step) { + struct nk_tt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; /* delete from list */ + NK_ASSERT(z->direction); + z->direction = 0; + nk_tt__hheap_free(&hh, z); + } else { + step = &((*step)->next); /* advance through list */ + } + } + + /* insert all edges that start before the bottom of this scanline */ + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + struct nk_tt__active_edge *z = nk_tt__new_active(&hh, e, off_x, scan_y_top); + if (z != 0) { + NK_ASSERT(z->ey >= scan_y_top); + /* insert at front */ + z->next = active; + active = z; + } + } + ++e; + } + + /* now process all active edges */ + if (active) + nk_tt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) NK_ABS(k) * 255.0f + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + /* advance all the edges */ + step = &active; + while (*step) { + struct nk_tt__active_edge *z = *step; + z->fx += z->fdx; /* advance to position for current scanline */ + step = &((*step)->next); /* advance through list */ + } + ++y; + ++j; + } + nk_tt__hheap_cleanup(&hh); + if (scanline != scanline_data) + alloc->free(alloc->userdata, scanline); +} + +#define NK_TT__COMPARE(a,b) ((a)->y0 < (b)->y0) +NK_INTERN void +nk_tt__sort_edges_ins_sort(struct nk_tt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + struct nk_tt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + struct nk_tt__edge *b = &p[j-1]; + int c = NK_TT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +NK_INTERN void +nk_tt__sort_edges_quicksort(struct nk_tt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + struct nk_tt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = NK_TT__COMPARE(&p[0],&p[m]); + c12 = NK_TT__COMPARE(&p[m],&p[n-1]); + + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = NK_TT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!NK_TT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!NK_TT__COMPARE(&p[0], &p[j])) break; + } + + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + + } + + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + nk_tt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + nk_tt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +NK_INTERN void +nk_tt__sort_edges(struct nk_tt__edge *p, int n) +{ + nk_tt__sort_edges_quicksort(p, n); + nk_tt__sort_edges_ins_sort(p, n); +} + +NK_INTERN void +nk_tt__rasterize(struct nk_tt__bitmap *result, struct nk_tt__point *pts, + int *wcount, int windings, float scale_x, float scale_y, + float shift_x, float shift_y, int off_x, int off_y, int invert, + struct nk_allocator *alloc) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + struct nk_tt__edge *e; + int n,i,j,k,m; + int vsubsample = 1; + /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity */ + + /* now we have to blow out the windings into explicit edge lists */ + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (struct nk_tt__edge*) + alloc->alloc(alloc->userdata, 0,(sizeof(*e) * (nk_size)(n+1))); + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) + { + struct nk_tt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + /* skip the edge if horizontal */ + if (p[j].y == p[k].y) + continue; + + /* add edge from j to k to the list */ + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * (float)vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * (float)vsubsample; + ++n; + } + } + + /* now sort the edges by their highest point (should snap to integer, and then by x) */ + /*STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */ + nk_tt__sort_edges(e, n); + /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ + nk_tt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, alloc); + alloc->free(alloc->userdata, e); +} + +NK_INTERN void +nk_tt__add_point(struct nk_tt__point *points, int n, float x, float y) +{ + if (!points) return; /* during first pass, it's unallocated */ + points[n].x = x; + points[n].y = y; +} + +NK_INTERN int +nk_tt__tesselate_curve(struct nk_tt__point *points, int *num_points, + float x0, float y0, float x1, float y1, float x2, float y2, + float objspace_flatness_squared, int n) +{ + /* tesselate until threshold p is happy... + * @TODO warped to compensate for non-linear stretching */ + /* midpoint */ + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + /* versus directly drawn line */ + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) /* 65536 segments on one curve better be enough! */ + return 1; + + /* half-pixel error allowed... need to be smaller if AA */ + if (dx*dx+dy*dy > objspace_flatness_squared) { + nk_tt__tesselate_curve(points, num_points, x0,y0, + (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + nk_tt__tesselate_curve(points, num_points, mx,my, + (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + nk_tt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +/* returns number of contours */ +NK_INTERN struct nk_tt__point* +nk_tt_FlattenCurves(struct nk_tt_vertex *vertices, int num_verts, + float objspace_flatness, int **contour_lengths, int *num_contours, + struct nk_allocator *alloc) +{ + struct nk_tt__point *points=0; + int num_points=0; + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i; + int n=0; + int start=0; + int pass; + + /* count how many "moves" there are to get the contour count */ + for (i=0; i < num_verts; ++i) + if (vertices[i].type == NK_TT_vmove) ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) + alloc->alloc(alloc->userdata,0, (sizeof(**contour_lengths) * (nk_size)n)); + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + /* make two passes through the points so we don't need to realloc */ + for (pass=0; pass < 2; ++pass) + { + float x=0,y=0; + if (pass == 1) { + points = (struct nk_tt__point *) + alloc->alloc(alloc->userdata,0, (nk_size)num_points * sizeof(points[0])); + if (points == 0) goto error; + } + num_points = 0; + n= -1; + + for (i=0; i < num_verts; ++i) + { + switch (vertices[i].type) { + case NK_TT_vmove: + /* start the next contour */ + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + nk_tt__add_point(points, num_points++, x,y); + break; + case NK_TT_vline: + x = vertices[i].x, y = vertices[i].y; + nk_tt__add_point(points, num_points++, x, y); + break; + case NK_TT_vcurve: + nk_tt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + default: break; + } + } + (*contour_lengths)[n] = num_points - start; + } + return points; + +error: + alloc->free(alloc->userdata, points); + alloc->free(alloc->userdata, *contour_lengths); + *contour_lengths = 0; + *num_contours = 0; + return 0; +} + +NK_INTERN void +nk_tt_Rasterize(struct nk_tt__bitmap *result, float flatness_in_pixels, + struct nk_tt_vertex *vertices, int num_verts, + float scale_x, float scale_y, float shift_x, float shift_y, + int x_off, int y_off, int invert, struct nk_allocator *alloc) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + struct nk_tt__point *windings = nk_tt_FlattenCurves(vertices, num_verts, + flatness_in_pixels / scale, &winding_lengths, &winding_count, alloc); + + NK_ASSERT(alloc); + if (windings) { + nk_tt__rasterize(result, windings, winding_lengths, winding_count, + scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, alloc); + alloc->free(alloc->userdata, winding_lengths); + alloc->free(alloc->userdata, windings); + } +} + +NK_INTERN void +nk_tt_MakeGlyphBitmapSubpixel(const struct nk_tt_fontinfo *info, unsigned char *output, + int out_w, int out_h, int out_stride, float scale_x, float scale_y, + float shift_x, float shift_y, int glyph, struct nk_allocator *alloc) +{ + int ix0,iy0; + struct nk_tt_vertex *vertices; + int num_verts = nk_tt_GetGlyphShape(info, alloc, glyph, &vertices); + struct nk_tt__bitmap gbm; + + nk_tt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, + shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + nk_tt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, + shift_x, shift_y, ix0,iy0, 1, alloc); + alloc->free(alloc->userdata, vertices); +} + +/*------------------------------------------------------------- + * Bitmap baking + * --------------------------------------------------------------*/ +NK_INTERN int +nk_tt_PackBegin(struct nk_tt_pack_context *spc, unsigned char *pixels, + int pw, int ph, int stride_in_bytes, int padding, struct nk_allocator *alloc) +{ + int num_nodes = pw - padding; + struct nk_rp_context *context = (struct nk_rp_context *) + alloc->alloc(alloc->userdata,0, sizeof(*context)); + struct nk_rp_node *nodes = (struct nk_rp_node*) + alloc->alloc(alloc->userdata,0, (sizeof(*nodes ) * (nk_size)num_nodes)); + + if (context == 0 || nodes == 0) { + if (context != 0) alloc->free(alloc->userdata, context); + if (nodes != 0) alloc->free(alloc->userdata, nodes); + return 0; + } + + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = (stride_in_bytes != 0) ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + nk_rp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + if (pixels) + NK_MEMSET(pixels, 0, (nk_size)(pw*ph)); /* background of 0 around pixels */ + return 1; +} + +NK_INTERN void +nk_tt_PackEnd(struct nk_tt_pack_context *spc, struct nk_allocator *alloc) +{ + alloc->free(alloc->userdata, spc->nodes); + alloc->free(alloc->userdata, spc->pack_info); +} + +NK_INTERN void +nk_tt_PackSetOversampling(struct nk_tt_pack_context *spc, + unsigned int h_oversample, unsigned int v_oversample) +{ + NK_ASSERT(h_oversample <= NK_TT_MAX_OVERSAMPLE); + NK_ASSERT(v_oversample <= NK_TT_MAX_OVERSAMPLE); + if (h_oversample <= NK_TT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= NK_TT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +NK_INTERN void +nk_tt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, + int kernel_width) +{ + unsigned char buffer[NK_TT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + + for (j=0; j < h; ++j) + { + int i; + unsigned int total; + NK_MEMSET(buffer, 0, (nk_size)kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)pixels[i] - buffer[i & NK_TT__OVER_MASK]; + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / (unsigned int)kernel_width); + } + break; + } + + for (; i < w; ++i) { + NK_ASSERT(pixels[i] == 0); + total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]); + pixels[i] = (unsigned char) (total / (unsigned int)kernel_width); + } + pixels += stride_in_bytes; + } +} + +NK_INTERN void +nk_tt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, + int kernel_width) +{ + unsigned char buffer[NK_TT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + + for (j=0; j < w; ++j) + { + int i; + unsigned int total; + NK_MEMSET(buffer, 0, (nk_size)kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width); + } + break; + } + + for (; i < h; ++i) { + NK_ASSERT(pixels[i*stride_in_bytes] == 0); + total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]); + pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width); + } + pixels += 1; + } +} + +NK_INTERN float +nk_tt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + /* The prefilter is a box filter of width "oversample", */ + /* which shifts phase by (oversample - 1)/2 pixels in */ + /* oversampled space. We want to shift in the opposite */ + /* direction to counter this. */ + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +/* rects array must be big enough to accommodate all characters in the given ranges */ +NK_INTERN int +nk_tt_PackFontRangesGatherRects(struct nk_tt_pack_context *spc, + struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, + int num_ranges, struct nk_rp_rect *rects) +{ + int i,j,k; + k = 0; + + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = (fh > 0) ? nk_tt_ScaleForPixelHeight(info, fh): + nk_tt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? + ranges[i].first_unicode_codepoint_in_range + j : + ranges[i].array_of_unicode_codepoints[j]; + + int glyph = nk_tt_FindGlyphIndex(info, codepoint); + nk_tt_GetGlyphBitmapBoxSubpixel(info,glyph, scale * (float)spc->h_oversample, + scale * (float)spc->v_oversample, 0,0, &x0,&y0,&x1,&y1); + rects[k].w = (nk_rp_coord) (x1-x0 + spc->padding + (int)spc->h_oversample-1); + rects[k].h = (nk_rp_coord) (y1-y0 + spc->padding + (int)spc->v_oversample-1); + ++k; + } + } + return k; +} + +NK_INTERN int +nk_tt_PackFontRangesRenderIntoRects(struct nk_tt_pack_context *spc, + struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, + int num_ranges, struct nk_rp_rect *rects, struct nk_allocator *alloc) +{ + int i,j,k, return_value = 1; + /* save current values */ + int old_h_over = (int)spc->h_oversample; + int old_v_over = (int)spc->v_oversample; + /* rects array must be big enough to accommodate all characters in the given ranges */ + + k = 0; + for (i=0; i < num_ranges; ++i) + { + float fh = ranges[i].font_size; + float recip_h,recip_v,sub_x,sub_y; + float scale = fh > 0 ? nk_tt_ScaleForPixelHeight(info, fh): + nk_tt_ScaleForMappingEmToPixels(info, -fh); + + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + + recip_h = 1.0f / (float)spc->h_oversample; + recip_v = 1.0f / (float)spc->v_oversample; + + sub_x = nk_tt__oversample_shift((int)spc->h_oversample); + sub_y = nk_tt__oversample_shift((int)spc->v_oversample); + + for (j=0; j < ranges[i].num_chars; ++j) + { + struct nk_rp_rect *r = &rects[k]; + if (r->was_packed) + { + struct nk_tt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? + ranges[i].first_unicode_codepoint_in_range + j : + ranges[i].array_of_unicode_codepoints[j]; + int glyph = nk_tt_FindGlyphIndex(info, codepoint); + nk_rp_coord pad = (nk_rp_coord) spc->padding; + + /* pad on left and top */ + r->x = (nk_rp_coord)((int)r->x + (int)pad); + r->y = (nk_rp_coord)((int)r->y + (int)pad); + r->w = (nk_rp_coord)((int)r->w - (int)pad); + r->h = (nk_rp_coord)((int)r->h - (int)pad); + + nk_tt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + nk_tt_GetGlyphBitmapBox(info, glyph, scale * (float)spc->h_oversample, + (scale * (float)spc->v_oversample), &x0,&y0,&x1,&y1); + nk_tt_MakeGlyphBitmapSubpixel(info, spc->pixels + r->x + r->y*spc->stride_in_bytes, + (int)(r->w - spc->h_oversample+1), (int)(r->h - spc->v_oversample+1), + spc->stride_in_bytes, scale * (float)spc->h_oversample, + scale * (float)spc->v_oversample, 0,0, glyph, alloc); + + if (spc->h_oversample > 1) + nk_tt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, (int)spc->h_oversample); + + if (spc->v_oversample > 1) + nk_tt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, (int)spc->v_oversample); + + bc->x0 = (nk_ushort) r->x; + bc->y0 = (nk_ushort) r->y; + bc->x1 = (nk_ushort) (r->x + r->w); + bc->y1 = (nk_ushort) (r->y + r->h); + bc->xadvance = scale * (float)advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = ((float)x0 + r->w) * recip_h + sub_x; + bc->yoff2 = ((float)y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; /* if any fail, report failure */ + } + ++k; + } + } + /* restore original values */ + spc->h_oversample = (unsigned int)old_h_over; + spc->v_oversample = (unsigned int)old_v_over; + return return_value; +} + +NK_INTERN void +nk_tt_GetPackedQuad(struct nk_tt_packedchar *chardata, int pw, int ph, + int char_index, float *xpos, float *ypos, struct nk_tt_aligned_quad *q, + int align_to_integer) +{ + float ipw = 1.0f / (float)pw, iph = 1.0f / (float)ph; + struct nk_tt_packedchar *b = (struct nk_tt_packedchar*)(chardata + char_index); + if (align_to_integer) { + int tx = nk_ifloorf((*xpos + b->xoff) + 0.5f); + int ty = nk_ifloorf((*ypos + b->yoff) + 0.5f); + + float x = (float)tx; + float y = (float)ty; + + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + *xpos += b->xadvance; +} + +/* ------------------------------------------------------------- + * + * FONT BAKING + * + * --------------------------------------------------------------*/ +struct nk_font_bake_data { + struct nk_tt_fontinfo info; + struct nk_rp_rect *rects; + struct nk_tt_pack_range *ranges; + nk_rune range_count; +}; + +struct nk_font_baker { + struct nk_allocator alloc; + struct nk_tt_pack_context spc; + struct nk_font_bake_data *build; + struct nk_tt_packedchar *packed_chars; + struct nk_rp_rect *rects; + struct nk_tt_pack_range *ranges; +}; + +NK_GLOBAL const nk_size nk_rect_align = NK_ALIGNOF(struct nk_rp_rect); +NK_GLOBAL const nk_size nk_range_align = NK_ALIGNOF(struct nk_tt_pack_range); +NK_GLOBAL const nk_size nk_char_align = NK_ALIGNOF(struct nk_tt_packedchar); +NK_GLOBAL const nk_size nk_build_align = NK_ALIGNOF(struct nk_font_bake_data); +NK_GLOBAL const nk_size nk_baker_align = NK_ALIGNOF(struct nk_font_baker); + +NK_INTERN int +nk_range_count(const nk_rune *range) +{ + const nk_rune *iter = range; + NK_ASSERT(range); + if (!range) return 0; + while (*(iter++) != 0); + return (iter == range) ? 0 : (int)((iter - range)/2); +} + +NK_INTERN int +nk_range_glyph_count(const nk_rune *range, int count) +{ + int i = 0; + int total_glyphs = 0; + for (i = 0; i < count; ++i) { + int diff; + nk_rune f = range[(i*2)+0]; + nk_rune t = range[(i*2)+1]; + NK_ASSERT(t >= f); + diff = (int)((t - f) + 1); + total_glyphs += diff; + } + return total_glyphs; +} + +NK_API const nk_rune* +nk_font_default_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = {0x0020, 0x00FF, 0}; + return ranges; +} + +NK_API const nk_rune* +nk_font_chinese_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3000, 0x30FF, + 0x31F0, 0x31FF, + 0xFF00, 0xFFEF, + 0x4e00, 0x9FAF, + 0 + }; + return ranges; +} + +NK_API const nk_rune* +nk_font_cyrillic_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x0400, 0x052F, + 0x2DE0, 0x2DFF, + 0xA640, 0xA69F, + 0 + }; + return ranges; +} + +NK_API const nk_rune* +nk_font_korean_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3131, 0x3163, + 0xAC00, 0xD79D, + 0 + }; + return ranges; +} + +NK_INTERN void +nk_font_baker_memory(nk_size *temp, int *glyph_count, + struct nk_font_config *config_list, int count) +{ + int range_count = 0; + int total_range_count = 0; + struct nk_font_config *iter; + NK_ASSERT(config_list); + NK_ASSERT(glyph_count); + if (!config_list) { + *temp = 0; + *glyph_count = 0; + return; + } + + *glyph_count = 0; + if (!config_list->range) + config_list->range = nk_font_default_glyph_ranges(); + for (iter = config_list; iter; iter = iter->next) { + range_count = nk_range_count(iter->range); + total_range_count += range_count; + *glyph_count += nk_range_glyph_count(iter->range, range_count); + } + + *temp = (nk_size)*glyph_count * sizeof(struct nk_rp_rect); + *temp += (nk_size)total_range_count * sizeof(struct nk_tt_pack_range); + *temp += (nk_size)*glyph_count * sizeof(struct nk_tt_packedchar); + *temp += (nk_size)count * sizeof(struct nk_font_bake_data); + *temp += sizeof(struct nk_font_baker); + *temp += nk_rect_align + nk_range_align + nk_char_align; + *temp += nk_build_align + nk_baker_align; +} + +NK_INTERN struct nk_font_baker* +nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *alloc) +{ + struct nk_font_baker *baker; + if (!memory) return 0; + /* setup baker inside a memory block */ + baker = (struct nk_font_baker*)NK_ALIGN_PTR(memory, nk_baker_align); + baker->build = (struct nk_font_bake_data*)NK_ALIGN_PTR((baker + 1), nk_build_align); + baker->packed_chars = (struct nk_tt_packedchar*)NK_ALIGN_PTR((baker->build + count), nk_char_align); + baker->rects = (struct nk_rp_rect*)NK_ALIGN_PTR((baker->packed_chars + glyph_count), nk_rect_align); + baker->ranges = (struct nk_tt_pack_range*)NK_ALIGN_PTR((baker->rects + glyph_count), nk_range_align); + baker->alloc = *alloc; + return baker; +} + +NK_INTERN int +nk_font_bake_pack(struct nk_font_baker *baker, + nk_size *image_memory, int *width, int *height, struct nk_recti *custom, + const struct nk_font_config *config_list, int count, + struct nk_allocator *alloc) +{ + NK_STORAGE const nk_size max_height = 1024 * 32; + const struct nk_font_config *config_iter; + int total_glyph_count = 0; + int total_range_count = 0; + int range_count = 0; + int i = 0; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config_list); + NK_ASSERT(count); + NK_ASSERT(alloc); + + if (!image_memory || !width || !height || !config_list || !count) return nk_false; + for (config_iter = config_list; config_iter; config_iter = config_iter->next) { + range_count = nk_range_count(config_iter->range); + total_range_count += range_count; + total_glyph_count += nk_range_glyph_count(config_iter->range, range_count); + } + + /* setup font baker from temporary memory */ + for (config_iter = config_list; config_iter; config_iter = config_iter->next) { + const struct nk_font_config *cfg = config_iter; + if (!nk_tt_InitFont(&baker->build[i++].info, (const unsigned char*)cfg->ttf_blob, 0)) + return nk_false; + } + + *height = 0; + *width = (total_glyph_count > 1000) ? 1024 : 512; + nk_tt_PackBegin(&baker->spc, 0, (int)*width, (int)max_height, 0, 1, alloc); + { + int input_i = 0; + int range_n = 0; + int rect_n = 0; + int char_n = 0; + + /* pack custom user data first so it will be in the upper left corner*/ + if (custom) { + struct nk_rp_rect custom_space; + nk_zero(&custom_space, sizeof(custom_space)); + custom_space.w = (nk_rp_coord)((custom->w * 2) + 1); + custom_space.h = (nk_rp_coord)(custom->h + 1); + + nk_tt_PackSetOversampling(&baker->spc, 1, 1); + nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, &custom_space, 1); + *height = NK_MAX(*height, (int)(custom_space.y + custom_space.h)); + + custom->x = (short)custom_space.x; + custom->y = (short)custom_space.y; + custom->w = (short)custom_space.w; + custom->h = (short)custom_space.h; + } + + /* first font pass: pack all glyphs */ + for (input_i = 0, config_iter = config_list; input_i < count && config_iter; + input_i++, config_iter = config_iter->next) + { + int n = 0; + int glyph_count; + const nk_rune *in_range; + const struct nk_font_config *cfg = config_iter; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + + /* count glyphs + ranges in current font */ + glyph_count = 0; range_count = 0; + for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) { + glyph_count += (int)(in_range[1] - in_range[0]) + 1; + range_count++; + } + + /* setup ranges */ + tmp->ranges = baker->ranges + range_n; + tmp->range_count = (nk_rune)range_count; + range_n += range_count; + for (i = 0; i < range_count; ++i) { + in_range = &cfg->range[i * 2]; + tmp->ranges[i].font_size = cfg->size; + tmp->ranges[i].first_unicode_codepoint_in_range = (int)in_range[0]; + tmp->ranges[i].num_chars = (int)(in_range[1]- in_range[0]) + 1; + tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n; + char_n += tmp->ranges[i].num_chars; + } + + /* pack */ + tmp->rects = baker->rects + rect_n; + rect_n += glyph_count; + nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + n = nk_tt_PackFontRangesGatherRects(&baker->spc, &tmp->info, + tmp->ranges, (int)tmp->range_count, tmp->rects); + nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, tmp->rects, (int)n); + + /* texture height */ + for (i = 0; i < n; ++i) { + if (tmp->rects[i].was_packed) + *height = NK_MAX(*height, tmp->rects[i].y + tmp->rects[i].h); + } + } + NK_ASSERT(rect_n == total_glyph_count); + NK_ASSERT(char_n == total_glyph_count); + NK_ASSERT(range_n == total_range_count); + } + *height = (int)nk_round_up_pow2((nk_uint)*height); + *image_memory = (nk_size)(*width) * (nk_size)(*height); + return nk_true; +} + +NK_INTERN void +nk_font_bake(struct nk_font_baker *baker, void *image_memory, int width, int height, + struct nk_font_glyph *glyphs, int glyphs_count, + const struct nk_font_config *config_list, int font_count) +{ + int input_i = 0; + nk_rune glyph_n = 0; + const struct nk_font_config *config_iter; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config_list); + NK_ASSERT(baker); + NK_ASSERT(font_count); + NK_ASSERT(glyphs_count); + if (!image_memory || !width || !height || !config_list || + !font_count || !glyphs || !glyphs_count) + return; + + /* second font pass: render glyphs */ + nk_zero(image_memory, (nk_size)((nk_size)width * (nk_size)height)); + baker->spc.pixels = (unsigned char*)image_memory; + baker->spc.height = (int)height; + for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; + ++input_i, config_iter = config_iter->next) + { + const struct nk_font_config *cfg = config_iter; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + nk_tt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges, + (int)tmp->range_count, tmp->rects, &baker->alloc); + } + nk_tt_PackEnd(&baker->spc, &baker->alloc); + + /* third pass: setup font and glyphs */ + for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; + ++input_i, config_iter = config_iter->next) + { + nk_size i = 0; + int char_idx = 0; + nk_rune glyph_count = 0; + const struct nk_font_config *cfg = config_iter; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + struct nk_baked_font *dst_font = cfg->font; + + float font_scale = nk_tt_ScaleForPixelHeight(&tmp->info, cfg->size); + int unscaled_ascent, unscaled_descent, unscaled_line_gap; + nk_tt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent, + &unscaled_line_gap); + + /* fill baked font */ + if (!cfg->merge_mode) { + dst_font->ranges = cfg->range; + dst_font->height = cfg->size; + dst_font->ascent = ((float)unscaled_ascent * font_scale); + dst_font->descent = ((float)unscaled_descent * font_scale); + dst_font->glyph_offset = glyph_n; + } + + /* fill own baked font glyph array */ + for (i = 0; i < tmp->range_count; ++i) + { + struct nk_tt_pack_range *range = &tmp->ranges[i]; + for (char_idx = 0; char_idx < range->num_chars; char_idx++) + { + nk_rune codepoint = 0; + float dummy_x = 0, dummy_y = 0; + struct nk_tt_aligned_quad q; + struct nk_font_glyph *glyph; + + /* query glyph bounds from stb_truetype */ + const struct nk_tt_packedchar *pc = &range->chardata_for_range[char_idx]; + if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue; + codepoint = (nk_rune)(range->first_unicode_codepoint_in_range + char_idx); + nk_tt_GetPackedQuad(range->chardata_for_range, (int)width, + (int)height, char_idx, &dummy_x, &dummy_y, &q, 0); + + /* fill own glyph type with data */ + glyph = &glyphs[dst_font->glyph_offset + (unsigned int)glyph_count]; + glyph->codepoint = codepoint; + glyph->x0 = q.x0; glyph->y0 = q.y0; + glyph->x1 = q.x1; glyph->y1 = q.y1; + glyph->y0 += (dst_font->ascent + 0.5f); + glyph->y1 += (dst_font->ascent + 0.5f); + glyph->w = glyph->x1 - glyph->x0 + 0.5f; + glyph->h = glyph->y1 - glyph->y0; + + if (cfg->coord_type == NK_COORD_PIXEL) { + glyph->u0 = q.s0 * (float)width; + glyph->v0 = q.t0 * (float)height; + glyph->u1 = q.s1 * (float)width; + glyph->v1 = q.t1 * (float)height; + } else { + glyph->u0 = q.s0; + glyph->v0 = q.t0; + glyph->u1 = q.s1; + glyph->v1 = q.t1; + } + glyph->xadvance = (pc->xadvance + cfg->spacing.x); + if (cfg->pixel_snap) + glyph->xadvance = (float)(int)(glyph->xadvance + 0.5f); + glyph_count++; + } + } + dst_font->glyph_count = glyph_count; + glyph_n += dst_font->glyph_count; + } +} + +NK_INTERN void +nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, + struct nk_recti img_dst, const char *texture_data_mask, int tex_width, + int tex_height, char white, char black) +{ + nk_byte *pixels; + int y = 0; + int x = 0; + int n = 0; + + NK_ASSERT(img_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + NK_ASSERT(texture_data_mask); + NK_UNUSED(tex_height); + if (!img_memory || !img_width || !img_height || !texture_data_mask) + return; + + pixels = (nk_byte*)img_memory; + for (y = 0, n = 0; y < tex_height; ++y) { + for (x = 0; x < tex_width; ++x, ++n) { + const int off0 = ((img_dst.x + x) + (img_dst.y + y) * img_width); + const int off1 = off0 + 1 + tex_width; + pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00; + pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00; + } + } +} + +NK_INTERN void +nk_font_bake_convert(void *out_memory, int img_width, int img_height, + const void *in_memory) +{ + int n = 0; + const nk_byte *src; + nk_rune *dst; + + NK_ASSERT(out_memory); + NK_ASSERT(in_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + if (!out_memory || !in_memory || !img_height || !img_width) return; + + dst = (nk_rune*)out_memory; + src = (const nk_byte*)in_memory; + for (n = (int)(img_width * img_height); n > 0; n--) + *dst++ = ((nk_rune)(*src++) << 24) | 0x00FFFFFF; +} + +/* ------------------------------------------------------------- + * + * FONT + * + * --------------------------------------------------------------*/ +NK_INTERN float +nk_font_text_width(nk_handle handle, float height, const char *text, int len) +{ + nk_rune unicode; + int text_len = 0; + float text_width = 0; + int glyph_len = 0; + float scale = 0; + + struct nk_font *font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !text || !len) + return 0; + + scale = height/font->info.height; + glyph_len = text_len = nk_utf_decode(text, &unicode, (int)len); + if (!glyph_len) return 0; + while (text_len <= (int)len && glyph_len) { + const struct nk_font_glyph *g; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + g = nk_font_find_glyph(font, unicode); + text_width += g->xadvance * scale; + + /* offset next glyph */ + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)len - text_len); + text_len += glyph_len; + } + return text_width; +} + +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_INTERN void +nk_font_query_font_glyph(nk_handle handle, float height, + struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) +{ + float scale; + const struct nk_font_glyph *g; + struct nk_font *font; + + NK_ASSERT(glyph); + NK_UNUSED(next_codepoint); + + font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !glyph) + return; + + scale = height/font->info.height; + g = nk_font_find_glyph(font, codepoint); + glyph->width = (g->x1 - g->x0) * scale; + glyph->height = (g->y1 - g->y0) * scale; + glyph->offset = nk_vec2(g->x0 * scale, g->y0 * scale); + glyph->xadvance = (g->xadvance * scale); + glyph->uv[0] = nk_vec2(g->u0, g->v0); + glyph->uv[1] = nk_vec2(g->u1, g->v1); +} +#endif + +NK_API const struct nk_font_glyph* +nk_font_find_glyph(struct nk_font *font, nk_rune unicode) +{ + int i = 0; + int count; + int total_glyphs = 0; + const struct nk_font_glyph *glyph = 0; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + + glyph = font->fallback; + count = nk_range_count(font->info.ranges); + for (i = 0; i < count; ++i) { + int diff; + nk_rune f = font->info.ranges[(i*2)+0]; + nk_rune t = font->info.ranges[(i*2)+1]; + diff = (int)((t - f) + 1); + if (unicode >= f && unicode <= t) + return &font->glyphs[((nk_rune)total_glyphs + (unicode - f))]; + total_glyphs += diff; + } + return glyph; +} + +NK_INTERN void +nk_font_init(struct nk_font *font, float pixel_height, + nk_rune fallback_codepoint, struct nk_font_glyph *glyphs, + const struct nk_baked_font *baked_font, nk_handle atlas) +{ + struct nk_baked_font baked; + NK_ASSERT(font); + NK_ASSERT(glyphs); + NK_ASSERT(baked_font); + if (!font || !glyphs || !baked_font) + return; + + baked = *baked_font; + font->info = baked; + font->scale = (float)pixel_height / (float)font->info.height; + font->glyphs = &glyphs[baked_font->glyph_offset]; + font->texture = atlas; + font->fallback_codepoint = fallback_codepoint; + font->fallback = nk_font_find_glyph(font, fallback_codepoint); + + font->handle.height = font->info.height * font->scale; + font->handle.width = nk_font_text_width; + font->handle.userdata.ptr = font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + font->handle.query = nk_font_query_font_glyph; + font->handle.texture = font->texture; +#endif +} + +/* --------------------------------------------------------------------------- + * + * DEFAULT FONT + * + * ProggyClean.ttf + * Copyright (c) 2004, 2005 Tristan Grimmer + * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) + * Download and more information at http://upperbounds.net + *-----------------------------------------------------------------------------*/ +#ifdef NK_INCLUDE_DEFAULT_FONT + + #ifdef __clang__ +#pragma clang diagnostic push + +#pragma clang diagnostic ignored "-Woverlength-strings" +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" +#endif + +NK_GLOBAL const char nk_proggy_clean_ttf_compressed_data_base85[11980+1] = + "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" + "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" + "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." + "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" + "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" + "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" + "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" + "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" + "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" + "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" + "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" + "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" + "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" + "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" + "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" + "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" + "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" + "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" + "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" + "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" + "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" + ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" + "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" + "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" + "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" + "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" + "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" + "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" + "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" + "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" + "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" + "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" + "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" + "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" + "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" + "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" + "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" + ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" + "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" + "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" + "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" + "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" + "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" + "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" + ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" + "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" + "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" + "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" + "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" + "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; +#endif /* NK_INCLUDE_DEFAULT_FONT */ + +#define NK_CURSOR_DATA_W 90 +#define NK_CURSOR_DATA_H 27 +NK_GLOBAL const char +nk_custom_cursor_data[NK_CURSOR_DATA_W * NK_CURSOR_DATA_H + 1] = +{ + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" + "..- -X.....X- X.X - X.X -X.....X - X.....X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X" + "X - X.X - X.....X - X.....X -X...X - X...X" + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" + "X..X - X.X - X.X - X.X -XX X.X - X.X XX" + "X...X - X.X - X.X - XX X.X XX - X.X - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" + "X.X X..X - -X.......X- X.......X - XX XX - " + "XX X..X - - X.....X - X.....X - X.X X.X - " + " X..X - X...X - X...X - X..X X..X - " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " + "------------ - X - X -X.....................X- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif + +NK_INTERN unsigned int +nk_decompress_length(unsigned char *input) +{ + return (unsigned int)((input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]); +} + +NK_GLOBAL unsigned char *nk__barrier; +NK_GLOBAL unsigned char *nk__barrier2; +NK_GLOBAL unsigned char *nk__barrier3; +NK_GLOBAL unsigned char *nk__barrier4; +NK_GLOBAL unsigned char *nk__dout; + +NK_INTERN void +nk__match(unsigned char *data, unsigned int length) +{ + /* INVERSE of memmove... write each byte before copying the next...*/ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier4) { nk__dout = nk__barrier+1; return; } + while (length--) *nk__dout++ = *data++; +} + +NK_INTERN void +nk__lit(unsigned char *data, unsigned int length) +{ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier2) { nk__dout = nk__barrier+1; return; } + NK_MEMCPY(nk__dout, data, length); + nk__dout += length; +} + +#define nk__in2(x) ((i[x] << 8) + i[(x)+1]) +#define nk__in3(x) ((i[x] << 16) + nk__in2((x)+1)) +#define nk__in4(x) ((i[x] << 24) + nk__in3((x)+1)) + +NK_INTERN unsigned char* +nk_decompress_token(unsigned char *i) +{ + if (*i >= 0x20) { /* use fewer if's for cases that expand small */ + if (*i >= 0x80) nk__match(nk__dout-i[1]-1, (unsigned int)i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) nk__match(nk__dout-(nk__in2(0) - 0x4000 + 1), (unsigned int)i[2]+1), i += 3; + else /* *i >= 0x20 */ nk__lit(i+1, (unsigned int)i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { /* more ifs for cases that expand large, since overhead is amortized */ + if (*i >= 0x18) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x180000 + 1), (unsigned int)i[3]+1), i += 4; + else if (*i >= 0x10) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x100000 + 1), (unsigned int)nk__in2(3)+1), i += 5; + else if (*i >= 0x08) nk__lit(i+2, (unsigned int)nk__in2(0) - 0x0800 + 1), i += 2 + (nk__in2(0) - 0x0800 + 1); + else if (*i == 0x07) nk__lit(i+3, (unsigned int)nk__in2(1) + 1), i += 3 + (nk__in2(1) + 1); + else if (*i == 0x06) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), i[4]+1u), i += 5; + else if (*i == 0x04) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), (unsigned int)nk__in2(4)+1u), i += 6; + } + return i; +} + +NK_INTERN unsigned int +nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= (unsigned int)blocklen; + blocklen = 5552; + } + return (unsigned int)(s2 << 16) + (unsigned int)s1; +} + +NK_INTERN unsigned int +nk_decompress(unsigned char *output, unsigned char *i, unsigned int length) +{ + unsigned int olen; + if (nk__in4(0) != 0x57bC0000) return 0; + if (nk__in4(4) != 0) return 0; /* error! stream is > 4GB */ + olen = nk_decompress_length(i); + nk__barrier2 = i; + nk__barrier3 = i+length; + nk__barrier = output + olen; + nk__barrier4 = output; + i += 16; + + nk__dout = output; + for (;;) { + unsigned char *old_i = i; + i = nk_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + NK_ASSERT(nk__dout == output + olen); + if (nk__dout != output + olen) return 0; + if (nk_adler32(1, output, olen) != (unsigned int) nk__in4(2)) + return 0; + return olen; + } else { + NK_ASSERT(0); /* NOTREACHED */ + return 0; + } + } + NK_ASSERT(nk__dout <= output + olen); + if (nk__dout > output + olen) + return 0; + } +} + +NK_INTERN unsigned int +nk_decode_85_byte(char c) +{ return (unsigned int)((c >= '\\') ? c-36 : c-35); } + +NK_INTERN void +nk_decode_85(unsigned char* dst, const unsigned char* src) +{ + while (*src) + { + unsigned int tmp = + nk_decode_85_byte((char)src[0]) + + 85 * (nk_decode_85_byte((char)src[1]) + + 85 * (nk_decode_85_byte((char)src[2]) + + 85 * (nk_decode_85_byte((char)src[3]) + + 85 * nk_decode_85_byte((char)src[4])))); + + /* we can't assume little-endianess. */ + dst[0] = (unsigned char)((tmp >> 0) & 0xFF); + dst[1] = (unsigned char)((tmp >> 8) & 0xFF); + dst[2] = (unsigned char)((tmp >> 16) & 0xFF); + dst[3] = (unsigned char)((tmp >> 24) & 0xFF); + + src += 5; + dst += 4; + } +} + +/* ------------------------------------------------------------- + * + * FONT ATLAS + * + * --------------------------------------------------------------*/ +NK_API struct nk_font_config +nk_font_config(float pixel_height) +{ + struct nk_font_config cfg; + nk_zero_struct(cfg); + cfg.ttf_blob = 0; + cfg.ttf_size = 0; + cfg.ttf_data_owned_by_atlas = 0; + cfg.size = pixel_height; + cfg.oversample_h = 3; + cfg.oversample_v = 1; + cfg.pixel_snap = 0; + cfg.coord_type = NK_COORD_UV; + cfg.spacing = nk_vec2(0,0); + cfg.range = nk_font_default_glyph_ranges(); + cfg.merge_mode = 0; + cfg.fallback_glyph = '?'; + cfg.font = 0; + return cfg; +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_font_atlas_init_default(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + if (!atlas) return; + nk_zero_struct(*atlas); + atlas->temporary.userdata.ptr = 0; + atlas->temporary.alloc = nk_malloc; + atlas->temporary.free = nk_mfree; + atlas->permanent.userdata.ptr = 0; + atlas->permanent.alloc = nk_malloc; + atlas->permanent.free = nk_mfree; +} +#endif + +NK_API void +nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc) +{ + NK_ASSERT(atlas); + NK_ASSERT(alloc); + if (!atlas || !alloc) return; + nk_zero_struct(*atlas); + atlas->permanent = *alloc; + atlas->temporary = *alloc; +} + +NK_API void +nk_font_atlas_init_custom(struct nk_font_atlas *atlas, + struct nk_allocator *permanent, struct nk_allocator *temporary) +{ + NK_ASSERT(atlas); + NK_ASSERT(permanent); + NK_ASSERT(temporary); + if (!atlas || !permanent || !temporary) return; + nk_zero_struct(*atlas); + atlas->permanent = *permanent; + atlas->temporary = *temporary; +} + +NK_API void +nk_font_atlas_begin(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc && atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc && atlas->permanent.free); + if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free || + !atlas->temporary.alloc || !atlas->temporary.free) return; + if (atlas->glyphs) { + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + atlas->glyphs = 0; + } + if (atlas->pixel) { + atlas->permanent.free(atlas->permanent.userdata, atlas->pixel); + atlas->pixel = 0; + } +} + +NK_API struct nk_font* +nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *config) +{ + struct nk_font *font = 0; + struct nk_font_config *cfg; + + NK_ASSERT(atlas); + NK_ASSERT(config); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(config->ttf_blob); + NK_ASSERT(config->ttf_size); + NK_ASSERT(config->size > 0.0f); + if (!atlas || !config || !config->ttf_blob || !config->ttf_size || config->size <= 0.0f|| + !atlas->permanent.alloc || !atlas->permanent.free || + !atlas->temporary.alloc || !atlas->temporary.free) + return 0; + + /* allocate and insert font config into list */ + cfg = (struct nk_font_config*) + atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font_config)); + NK_MEMCPY(cfg, config, sizeof(*config)); + if (!atlas->config) { + atlas->config = cfg; + cfg->next = 0; + } else { + cfg->next = atlas->config; + atlas->config = cfg; + } + + /* allocate new font */ + if (!config->merge_mode) { + font = (struct nk_font*) + atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font)); + NK_ASSERT(font); + if (!font) return 0; + font->config = cfg; + } else { + NK_ASSERT(atlas->font_num); + font = atlas->fonts; + font->config = cfg; + } + + /* insert font into list */ + if (!config->merge_mode) { + if (!atlas->fonts) { + atlas->fonts = font; + font->next = 0; + } else { + font->next = atlas->fonts; + atlas->fonts = font; + } + cfg->font = &font->info; + } + + /* create own copy of .TTF font blob */ + if (!config->ttf_data_owned_by_atlas) { + cfg->ttf_blob = atlas->permanent.alloc(atlas->permanent.userdata,0, cfg->ttf_size); + NK_ASSERT(cfg->ttf_blob); + if (!cfg->ttf_blob) { + atlas->font_num++; + return 0; + } + NK_MEMCPY(cfg->ttf_blob, config->ttf_blob, cfg->ttf_size); + cfg->ttf_data_owned_by_atlas = 1; + } + atlas->font_num++; + return font; +} + +NK_API struct nk_font* +nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, + nk_size size, float height, const struct nk_font_config *config) +{ + struct nk_font_config cfg; + NK_ASSERT(memory); + NK_ASSERT(size); + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !atlas->temporary.alloc || !atlas->temporary.free || !memory || !size || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 0; + return nk_font_atlas_add(atlas, &cfg); +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* +nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, + float height, const struct nk_font_config *config) +{ + nk_size size; + char *memory; + struct nk_font_config cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !file_path) return 0; + memory = nk_file_load(file_path, &size, &atlas->permanent); + if (!memory) return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} +#endif + +NK_API struct nk_font* +nk_font_atlas_add_compressed(struct nk_font_atlas *atlas, + void *compressed_data, nk_size compressed_size, float height, + const struct nk_font_config *config) +{ + unsigned int decompressed_size; + void *decompressed_data; + struct nk_font_config cfg; + + NK_ASSERT(compressed_data); + NK_ASSERT(compressed_size); + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !compressed_data || !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + decompressed_size = nk_decompress_length((unsigned char*)compressed_data); + decompressed_data = atlas->permanent.alloc(atlas->permanent.userdata,0,decompressed_size); + NK_ASSERT(decompressed_data); + if (!decompressed_data) return 0; + nk_decompress((unsigned char*)decompressed_data, (unsigned char*)compressed_data, + (unsigned int)compressed_size); + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = decompressed_data; + cfg.ttf_size = decompressed_size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} + +NK_API struct nk_font* +nk_font_atlas_add_compressed_base85(struct nk_font_atlas *atlas, + const char *data_base85, float height, const struct nk_font_config *config) +{ + int compressed_size; + void *compressed_data; + struct nk_font *font; + + NK_ASSERT(data_base85); + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !data_base85 || !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + compressed_size = (((int)nk_strlen(data_base85) + 4) / 5) * 4; + compressed_data = atlas->temporary.alloc(atlas->temporary.userdata,0, (nk_size)compressed_size); + NK_ASSERT(compressed_data); + if (!compressed_data) return 0; + nk_decode_85((unsigned char*)compressed_data, (const unsigned char*)data_base85); + font = nk_font_atlas_add_compressed(atlas, compressed_data, + (nk_size)compressed_size, height, config); + atlas->temporary.free(atlas->temporary.userdata, compressed_data); + return font; +} + +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* +nk_font_atlas_add_default(struct nk_font_atlas *atlas, + float pixel_height, const struct nk_font_config *config) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + return nk_font_atlas_add_compressed_base85(atlas, + nk_proggy_clean_ttf_compressed_data_base85, pixel_height, config); +} +#endif + +NK_API const void* +nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height, + enum nk_font_atlas_format fmt) +{ + int i = 0; + void *tmp = 0; + nk_size tmp_size, img_size; + struct nk_font *font_iter; + struct nk_font_baker *baker; + + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !width || !height || + !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + +#ifdef NK_INCLUDE_DEFAULT_FONT + /* no font added so just use default font */ + if (!atlas->font_num) + atlas->default_font = nk_font_atlas_add_default(atlas, 13.0f, 0); +#endif + NK_ASSERT(atlas->font_num); + if (!atlas->font_num) return 0; + + /* allocate temporary baker memory required for the baking process */ + nk_font_baker_memory(&tmp_size, &atlas->glyph_count, atlas->config, atlas->font_num); + tmp = atlas->temporary.alloc(atlas->temporary.userdata,0, tmp_size); + NK_ASSERT(tmp); + if (!tmp) goto failed; + + /* allocate glyph memory for all fonts */ + baker = nk_font_baker(tmp, atlas->glyph_count, atlas->font_num, &atlas->temporary); + atlas->glyphs = (struct nk_font_glyph*) + atlas->permanent.alloc(atlas->permanent.userdata,0, + sizeof(struct nk_font_glyph) * (nk_size)atlas->glyph_count); + NK_ASSERT(atlas->glyphs); + if (!atlas->glyphs) + goto failed; + + /* pack all glyphs into a tight fit space */ + atlas->custom.w = (NK_CURSOR_DATA_W*2)+1; + atlas->custom.h = NK_CURSOR_DATA_H + 1; + if (!nk_font_bake_pack(baker, &img_size, width, height, &atlas->custom, + atlas->config, atlas->font_num, &atlas->temporary)) + goto failed; + + /* allocate memory for the baked image font atlas */ + atlas->pixel = atlas->temporary.alloc(atlas->temporary.userdata,0, img_size); + NK_ASSERT(atlas->pixel); + if (!atlas->pixel) + goto failed; + + /* bake glyphs and custom white pixel into image */ + nk_font_bake(baker, atlas->pixel, *width, *height, + atlas->glyphs, atlas->glyph_count, atlas->config, atlas->font_num); + nk_font_bake_custom_data(atlas->pixel, *width, *height, atlas->custom, + nk_custom_cursor_data, NK_CURSOR_DATA_W, NK_CURSOR_DATA_H, '.', 'X'); + + /* convert alpha8 image into rgba32 image */ + if (fmt == NK_FONT_ATLAS_RGBA32) { + void *img_rgba = atlas->temporary.alloc(atlas->temporary.userdata,0, + (nk_size)(*width * *height * 4)); + NK_ASSERT(img_rgba); + if (!img_rgba) goto failed; + nk_font_bake_convert(img_rgba, *width, *height, atlas->pixel); + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = img_rgba; + } + atlas->tex_width = *width; + atlas->tex_height = *height; + + /* initialize each font */ + for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { + struct nk_font *font = font_iter; + struct nk_font_config *config = font->config; + nk_font_init(font, config->size, config->fallback_glyph, atlas->glyphs, + config->font, nk_handle_ptr(0)); + } + + /* initialize each cursor */ + {NK_STORAGE const struct nk_vec2 nk_cursor_data[NK_CURSOR_COUNT][3] = { + /* Pos ----- Size ------- Offset --*/ + {{ 0, 3}, {12,19}, { 0, 0}}, + {{13, 0}, { 7,16}, { 4, 8}}, + {{31, 0}, {23,23}, {11,11}}, + {{21, 0}, { 9, 23}, { 5,11}}, + {{55,18}, {23, 9}, {11, 5}}, + {{73, 0}, {17,17}, { 9, 9}}, + {{55, 0}, {17,17}, { 9, 9}} + }; + for (i = 0; i < NK_CURSOR_COUNT; ++i) { + struct nk_cursor *cursor = &atlas->cursors[i]; + cursor->img.w = (unsigned short)*width; + cursor->img.h = (unsigned short)*height; + cursor->img.region[0] = (unsigned short)(atlas->custom.x + nk_cursor_data[i][0].x); + cursor->img.region[1] = (unsigned short)(atlas->custom.y + nk_cursor_data[i][0].y); + cursor->img.region[2] = (unsigned short)nk_cursor_data[i][1].x; + cursor->img.region[3] = (unsigned short)nk_cursor_data[i][1].y; + cursor->size = nk_cursor_data[i][1]; + cursor->offset = nk_cursor_data[i][2]; + }} + + /* free temporary memory */ + atlas->temporary.free(atlas->temporary.userdata, tmp); + return atlas->pixel; + +failed: + /* error so cleanup all memory */ + if (tmp) atlas->temporary.free(atlas->temporary.userdata, tmp); + if (atlas->glyphs) { + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + atlas->glyphs = 0; + } + if (atlas->pixel) { + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = 0; + } + return 0; +} + +NK_API void +nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture, + struct nk_draw_null_texture *null) +{ + int i = 0; + struct nk_font *font_iter; + NK_ASSERT(atlas); + if (!atlas) { + if (!null) return; + null->texture = texture; + null->uv = nk_vec2(0.5f,0.5f); + } + if (null) { + null->texture = texture; + null->uv = nk_vec2((atlas->custom.x + 0.5f)/(float)atlas->tex_width, + (atlas->custom.y + 0.5f)/(float)atlas->tex_height); + } + for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { + font_iter->texture = texture; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + font_iter->handle.texture = texture; +#endif + } + for (i = 0; i < NK_CURSOR_COUNT; ++i) + atlas->cursors[i].img.handle = texture; + + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = 0; + atlas->tex_width = 0; + atlas->tex_height = 0; + atlas->custom.x = 0; + atlas->custom.y = 0; + atlas->custom.w = 0; + atlas->custom.h = 0; +} + +NK_API void +nk_font_atlas_clear(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return; + + if (atlas->fonts) { + struct nk_font *iter, *next; + for (iter = atlas->fonts; iter; iter = next) { + next = iter->next; + atlas->permanent.free(atlas->permanent.userdata, iter); + } + atlas->fonts = 0; + } + if (atlas->config) { + struct nk_font_config *iter, *next; + for (iter = atlas->config; iter; iter = next) { + next = iter->next; + atlas->permanent.free(atlas->permanent.userdata, iter->ttf_blob); + atlas->permanent.free(atlas->permanent.userdata, iter); + } + } + if (atlas->glyphs) + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + nk_zero_struct(*atlas); +} +#endif +/* ============================================================== + * + * INPUT + * + * ===============================================================*/ +NK_API void +nk_input_begin(struct nk_context *ctx) +{ + int i; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + for (i = 0; i < NK_BUTTON_MAX; ++i) + in->mouse.buttons[i].clicked = 0; + + in->keyboard.text_len = 0; + in->mouse.scroll_delta = 0; + in->mouse.prev.x = in->mouse.pos.x; + in->mouse.prev.y = in->mouse.pos.y; + in->mouse.delta.x = 0; + in->mouse.delta.y = 0; + for (i = 0; i < NK_KEY_MAX; i++) + in->keyboard.keys[i].clicked = 0; +} + +NK_API void +nk_input_end(struct nk_context *ctx) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->mouse.grab) + in->mouse.grab = 0; + if (in->mouse.ungrab) { + in->mouse.grabbed = 0; + in->mouse.ungrab = 0; + in->mouse.grab = 0; + } +} + +NK_API void +nk_input_motion(struct nk_context *ctx, int x, int y) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + in->mouse.pos.x = (float)x; + in->mouse.pos.y = (float)y; + in->mouse.delta.x = in->mouse.pos.x - in->mouse.prev.x; + in->mouse.delta.y = in->mouse.pos.y - in->mouse.prev.y; +} + +NK_API void +nk_input_key(struct nk_context *ctx, enum nk_keys key, int down) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + in->keyboard.keys[key].down = down; + in->keyboard.keys[key].clicked++; +} + +NK_API void +nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, int down) +{ + struct nk_mouse_button *btn; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->mouse.buttons[id].down == down) return; + + btn = &in->mouse.buttons[id]; + btn->clicked_pos.x = (float)x; + btn->clicked_pos.y = (float)y; + btn->down = down; + btn->clicked++; +} + +NK_API void +nk_input_scroll(struct nk_context *ctx, float y) +{ + NK_ASSERT(ctx); + if (!ctx) return; + ctx->input.mouse.scroll_delta += y; +} + +NK_API void +nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph) +{ + int len = 0; + nk_rune unicode; + struct nk_input *in; + + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + + len = nk_utf_decode(glyph, &unicode, NK_UTF_SIZE); + if (len && ((in->keyboard.text_len + len) < NK_INPUT_MAX)) { + nk_utf_encode(unicode, &in->keyboard.text[in->keyboard.text_len], + NK_INPUT_MAX - in->keyboard.text_len); + in->keyboard.text_len += len; + } +} + +NK_API void +nk_input_char(struct nk_context *ctx, char c) +{ + nk_glyph glyph; + NK_ASSERT(ctx); + if (!ctx) return; + glyph[0] = c; + nk_input_glyph(ctx, glyph); +} + +NK_API void +nk_input_unicode(struct nk_context *ctx, nk_rune unicode) +{ + nk_glyph rune; + NK_ASSERT(ctx); + if (!ctx) return; + nk_utf_encode(unicode, rune, NK_UTF_SIZE); + nk_input_glyph(ctx, rune); +} + +NK_API int +nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (btn->clicked && btn->down == nk_false) ? nk_true : nk_false; +} + +NK_API int +nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + if (!NK_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h)) + return nk_false; + return nk_true; +} + +NK_API int +nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, int down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return nk_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down); +} + +NK_API int +nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_false) && + btn->clicked) ? nk_true : nk_false; +} + +NK_API int +nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, int down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, down) && + btn->clicked) ? nk_true : nk_false; +} + +NK_API int +nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b) +{ + int i, down = 0; + for (i = 0; i < NK_BUTTON_MAX; ++i) + down = down || nk_input_is_mouse_click_in_rect(in, (enum nk_buttons)i, b); + return down; +} + +NK_API int +nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h); +} + +NK_API int +nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h); +} + +NK_API int +nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_rect rect) +{ + if (!i) return nk_false; + if (!nk_input_is_mouse_hovering_rect(i, rect)) return nk_false; + return nk_input_is_mouse_click_in_rect(i, id, rect); +} + +NK_API int +nk_input_is_mouse_down(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return i->mouse.buttons[id].down; +} + +NK_API int +nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id) +{ + const struct nk_mouse_button *b; + if (!i) return nk_false; + b = &i->mouse.buttons[id]; + if (b->down && b->clicked) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_mouse_released(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked); +} + +NK_API int +nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if ((k->down && k->clicked) || (!k->down && k->clicked >= 2)) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_key_released(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if ((!k->down && k->clicked) || (k->down && k->clicked >= 2)) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if (k->down) return nk_true; + return nk_false; +} + +/* + * ============================================================== + * + * TEXT EDITOR + * + * =============================================================== + */ +/* stb_textedit.h - v1.8 - public domain - Sean Barrett */ +struct nk_text_find { + float x,y; /* position of n'th character */ + float height; /* height of line */ + int first_char, length; /* first char of row, and length */ + int prev_first; /*_ first char of previous row */ +}; + +struct nk_text_edit_row { + float x0,x1; + /* starting x location, end x location (allows for align=right, etc) */ + float baseline_y_delta; + /* position of baseline relative to previous row's baseline*/ + float ymin,ymax; + /* height of row above and below baseline */ + int num_chars; +}; + +/* forward declarations */ +NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int); +#define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +NK_INTERN float +nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id, + const struct nk_user_font *font) +{ + int len = 0; + nk_rune unicode = 0; + const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len); + return font->width(font->userdata, font->height, str, len); +} + +NK_INTERN void +nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit, + int line_start_id, float row_height, const struct nk_user_font *font) +{ + int l; + int glyphs = 0; + nk_rune unicode; + const char *remaining; + int len = nk_str_len_char(&edit->string); + const char *end = nk_str_get_const(&edit->string) + len; + const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l); + const struct nk_vec2 size = nk_text_calculate_text_bounds(font, + text, (int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE); + + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = glyphs; +} + +NK_INTERN int +nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y, + const struct nk_user_font *font, float row_height) +{ + struct nk_text_edit_row r; + int n = edit->string.len; + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + /* search rows to find one that straddles 'y' */ + while (i < n) { + nk_textedit_layout_row(&r, edit, i, row_height, font); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + /* below all text, return 'after' last character */ + if (i >= n) + return n; + + /* check if it's before the beginning of the line */ + if (x < r.x0) + return i; + + /* check if it's before the end of the line */ + if (x < r.x1) { + /* search characters in row for one that straddles 'x' */ + k = i; + prev_x = r.x0; + for (i=0; i < r.num_chars; ++i) { + float w = nk_textedit_get_width(edit, k, i, font); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else return k+i+1; + } + prev_x += w; + } + /* shouldn't happen, but if it does, fall through to end-of-line case */ + } + + /* if the last character is a newline, return that. + * otherwise return 'after' the last character */ + if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n') + return i+r.num_chars-1; + else return i+r.num_chars; +} + +NK_INTERN void +nk_textedit_click(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API click: on mouse down, move the cursor to the clicked location, + * and reset the selection */ + state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} + +NK_INTERN void +nk_textedit_drag(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API drag: on mouse drag, move the cursor and selection endpoint + * to the clicked location */ + int p = nk_textedit_locate_coord(state, x, y, font, row_height); + if (state->select_start == state->select_end) + state->select_start = state->cursor; + state->cursor = state->select_end = p; +} + +NK_INTERN void +nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state, + int n, int single_line, const struct nk_user_font *font, float row_height) +{ + /* find the x/y location of a character, and remember info about the previous + * row in case we get a move-up event (for page up, we'll have to rescan) */ + struct nk_text_edit_row r; + int prev_start = 0; + int z = state->string.len; + int i=0, first; + + if (n == z) { + /* if it's at the end, then find the last line -- simpler than trying to + explicitly handle this case in the regular code */ + if (single_line) { + nk_textedit_layout_row(&r, state, 0, row_height, font); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; + } else { + find->y = 0; + find->x = 0; + find->height = 1; + + while (i < z) { + nk_textedit_layout_row(&r, state, i, row_height, font); + prev_start = i; + i += r.num_chars; + } + + find->first_char = i; + find->length = 0; + find->prev_first = prev_start; + } + return; + } + + /* search rows to find the one that straddles character n */ + find->y = 0; + + for(;;) { + nk_textedit_layout_row(&r, state, i, row_height, font); + if (n < i + r.num_chars) break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + /* now scan to find xpos */ + find->x = r.x0; + for (i=0; first+i < n; ++i) + find->x += nk_textedit_get_width(state, first, i, font); +} + +NK_INTERN void +nk_textedit_clamp(struct nk_text_edit *state) +{ + /* make the selection/cursor state valid if client altered the string */ + int n = state->string.len; + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + /* if clamping forced them to be equal, move the cursor to match */ + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} + +NK_API void +nk_textedit_delete(struct nk_text_edit *state, int where, int len) +{ + /* delete characters while updating undo */ + nk_textedit_makeundo_delete(state, where, len); + nk_str_delete_runes(&state->string, where, len); + state->has_preferred_x = 0; +} + +NK_API void +nk_textedit_delete_selection(struct nk_text_edit *state) +{ + /* delete the section */ + nk_textedit_clamp(state); + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + nk_textedit_delete(state, state->select_start, + state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + nk_textedit_delete(state, state->select_end, + state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} + +NK_INTERN void +nk_textedit_sortselection(struct nk_text_edit *state) +{ + /* canonicalize the selection so start <= end */ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} + +NK_INTERN void +nk_textedit_move_to_first(struct nk_text_edit *state) +{ + /* move cursor to first character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} + +NK_INTERN void +nk_textedit_move_to_last(struct nk_text_edit *state) +{ + /* move cursor to last character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} + +NK_INTERN int +nk_is_word_boundary( struct nk_text_edit *state, int idx) +{ + int len; + nk_rune c; + if (idx <= 0) return 1; + if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1; + return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' || + c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || + c == '|'); +} + +NK_INTERN int +nk_textedit_move_to_word_previous(struct nk_text_edit *state) +{ + int c = state->cursor - 1; + while( c >= 0 && !nk_is_word_boundary(state, c)) + --c; + + if( c < 0 ) + c = 0; + + return c; +} + +NK_INTERN int +nk_textedit_move_to_word_next(struct nk_text_edit *state) +{ + const int len = state->string.len; + int c = state->cursor+1; + while( c < len && !nk_is_word_boundary(state, c)) + ++c; + + if( c > len ) + c = len; + + return c; +} + +NK_INTERN void +nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state) +{ + /* update selection and cursor to match each other */ + if (!NK_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else state->cursor = state->select_end; +} + +NK_API int +nk_textedit_cut(struct nk_text_edit *state) +{ + /* API cut: delete selection */ + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + return 0; + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_delete_selection(state); /* implicitly clamps */ + state->has_preferred_x = 0; + return 1; + } + return 0; +} + +NK_API int +nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len) +{ + /* API paste: replace existing selection with passed-in text */ + int glyphs; + const char *text = (const char *) ctext; + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0; + /* if there's a selection, the paste should delete it */ + nk_textedit_clamp(state); + nk_textedit_delete_selection(state); + /* try to insert the characters */ + glyphs = nk_utf_len(ctext, len); + if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) { + nk_textedit_makeundo_insert(state, state->cursor, glyphs); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + /* remove the undo since we didn't actually insert the characters */ + if (state->undo.undo_point) + --state->undo.undo_point; + return 0; +} + +NK_API void +nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) +{ + nk_rune unicode; + int glyph_len; + int text_len = 0; + + NK_ASSERT(state); + NK_ASSERT(text); + if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW) return; + + glyph_len = nk_utf_decode(text, &unicode, total_len); + if (!glyph_len) return; + while ((text_len < total_len) && glyph_len) + { + /* don't insert a backward delete, just process the event */ + if (unicode == 127) + break; + + /* can't add newline in single-line mode */ + if (unicode == '\n' && state->single_line) + break; + + /* filter incoming text */ + if (state->filter && !state->filter(state, unicode)) { + glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); + text_len += glyph_len; + continue; + } + + if (!NK_TEXT_HAS_SELECTION(state) && + state->cursor < state->string.len) + { + if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) { + nk_textedit_makeundo_replace(state, state->cursor, 1, 1); + nk_str_delete_runes(&state->string, state->cursor, 1); + } + if (nk_str_insert_text_char(&state->string, state->cursor, + text+text_len, glyph_len)) + { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + nk_textedit_delete_selection(state); /* implicitly clamps */ + if (nk_str_insert_text_char(&state->string, state->cursor, + text+text_len, glyph_len)) + { + nk_textedit_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); + text_len += glyph_len; + } +} + +NK_INTERN void +nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, + const struct nk_user_font *font, float row_height) +{ +retry: + switch (key) + { + case NK_KEY_NONE: + case NK_KEY_CTRL: + case NK_KEY_ENTER: + case NK_KEY_SHIFT: + case NK_KEY_TAB: + case NK_KEY_COPY: + case NK_KEY_CUT: + case NK_KEY_PASTE: + case NK_KEY_MAX: + default: break; + case NK_KEY_TEXT_UNDO: + nk_textedit_undo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_REDO: + nk_textedit_redo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_INSERT_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_INSERT; + break; + case NK_KEY_TEXT_REPLACE_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_REPLACE; + break; + case NK_KEY_TEXT_RESET_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_INSERT || + state->mode == NK_TEXT_EDIT_MODE_REPLACE) + state->mode = NK_TEXT_EDIT_MODE_VIEW; + break; + + case NK_KEY_LEFT: + if (shift_mod) { + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + /* move selection left */ + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to start of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + } break; + + case NK_KEY_RIGHT: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + /* move selection right */ + ++state->select_end; + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to end of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else ++state->cursor; + nk_textedit_clamp(state); + state->has_preferred_x = 0; + } break; + + case NK_KEY_TEXT_WORD_LEFT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_previous(state); + state->select_end = state->cursor; + nk_textedit_clamp(state ); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else { + state->cursor = nk_textedit_move_to_word_previous(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_TEXT_WORD_RIGHT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_next(state); + state->select_end = state->cursor; + nk_textedit_clamp(state); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else { + state->cursor = nk_textedit_move_to_word_next(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_DOWN: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down in single-line behave like left&right */ + key = NK_KEY_RIGHT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* now find character position down a row */ + if (find.length) + { + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + int start = find.first_char + find.length; + + state->cursor = start; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars; ++i) { + float dx = nk_textedit_get_width(state, start, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) + state->select_end = state->cursor; + } + } break; + + case NK_KEY_UP: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down become left&right */ + key = NK_KEY_LEFT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* can only go up if there's a previous row */ + if (find.prev_first != find.first_char) { + /* now find character position up a row */ + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + + state->cursor = find.prev_first; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars; ++i) { + float dx = nk_textedit_get_width(state, find.prev_first, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) state->select_end = state->cursor; + } + } break; + + case NK_KEY_DEL: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + int n = state->string.len; + if (state->cursor < n) + nk_textedit_delete(state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case NK_KEY_BACKSPACE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + nk_textedit_clamp(state); + if (state->cursor > 0) { + nk_textedit_delete(state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_START: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + } else { + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_END: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = state->string.len; + state->has_preferred_x = 0; + } else { + state->cursor = state->string.len; + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_LINE_START: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_find_charpos(&find, state,state->cursor, state->single_line, + font, row_height); + state->cursor = state->select_end = find.first_char; + state->has_preferred_x = 0; + } else { + struct nk_text_find find; + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->cursor = find.first_char; + state->has_preferred_x = 0; + } + } break; + + case NK_KEY_TEXT_LINE_END: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + state->select_end = state->cursor; + } else { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + }} break; + } +} + +NK_INTERN void +nk_textedit_flush_redo(struct nk_text_undo_state *state) +{ + state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; +} + +NK_INTERN void +nk_textedit_discard_undo(struct nk_text_undo_state *state) +{ + /* discard the oldest entry in the undo list */ + if (state->undo_point > 0) { + /* if the 0th undo state has characters, clean those up */ + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + /* delete n characters from all other records */ + state->undo_char_point = (short)(state->undo_char_point - n); + NK_MEMCPY(state->undo_char, state->undo_char + n, + (nk_size)state->undo_char_point*sizeof(nk_rune)); + for (i=0; i < state->undo_point; ++i) { + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage - n); + } + } + --state->undo_point; + NK_MEMCPY(state->undo_rec, state->undo_rec+1, + (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0]))); + } +} + +NK_INTERN void +nk_textedit_discard_redo(struct nk_text_undo_state *state) +{ +/* discard the oldest entry in the redo list--it's bad if this + ever happens, but because undo & redo have to store the actual + characters in different cases, the redo character buffer can + fill up even though the undo buffer didn't */ + nk_size num; + int k = NK_TEXTEDIT_UNDOSTATECOUNT-1; + if (state->redo_point <= k) { + /* if the k'th undo state has characters, clean those up */ + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + /* delete n characters from all other records */ + state->redo_char_point = (short)(state->redo_char_point + n); + num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point); + NK_MEMCPY(state->undo_char + state->redo_char_point, + state->undo_char + state->redo_char_point-n, num * sizeof(char)); + for (i = state->redo_point; i < k; ++i) { + if (state->undo_rec[i].char_storage >= 0) { + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage + n); + } + } + } + ++state->redo_point; + num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point); + if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1, + state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0])); + } +} + +NK_INTERN struct nk_text_undo_record* +nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars) +{ + /* any time we create a new undo record, we discard redo*/ + nk_textedit_flush_redo(state); + + /* if we have no free records, we have to make room, + * by sliding the existing records down */ + if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + nk_textedit_discard_undo(state); + + /* if the characters to store won't possibly fit in the buffer, + * we can't undo */ + if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return 0; + } + + /* if we don't have enough free characters in the buffer, + * we have to make room */ + while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT) + nk_textedit_discard_undo(state); + return &state->undo_rec[state->undo_point++]; +} + +NK_INTERN nk_rune* +nk_textedit_createundo(struct nk_text_undo_state *state, int pos, + int insert_len, int delete_len) +{ + struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len); + if (r == 0) + return 0; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return 0; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = (short)(state->undo_char_point + insert_len); + return &state->undo_char[r->char_storage]; + } +} + +NK_API void +nk_textedit_undo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record u, *r; + if (s->undo_point == 0) + return; + + /* we need to do two things: apply the undo record, and create a redo record */ + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) + { + /* if the undo record says to delete characters, then the redo record will + need to re-insert the characters that get deleted, so we need to store + them. + there are three cases: + - there's enough room to store the characters + - characters stored for *redoing* don't leave room for redo + - characters stored for *undoing* don't leave room for redo + if the last is true, we have to bail */ + if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) { + /* the undo records take up too much character space; there's no space + * to store the redo characters */ + r->insert_length = 0; + } else { + int i; + /* there's definitely room to store the characters eventually */ + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + /* there's currently not enough room, so discard a redo record */ + nk_textedit_discard_redo(s); + /* should never happen: */ + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + } + + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = (short)(s->redo_char_point - u.delete_length); + s->redo_char_point = (short)(s->redo_char_point - u.delete_length); + + /* now save the characters */ + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = + nk_str_rune_at(&state->string, u.where + i); + } + /* now we can carry out the deletion */ + nk_str_delete_runes(&state->string, u.where, u.delete_length); + } + + /* check type of recorded action: */ + if (u.insert_length) { + /* easy case: was a deletion, so we need to insert n characters */ + nk_str_insert_text_runes(&state->string, u.where, + &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point = (short)(s->undo_char_point - u.insert_length); + } + state->cursor = (short)(u.where + u.insert_length); + + s->undo_point--; + s->redo_point--; +} + +NK_API void +nk_textedit_redo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record *u, r; + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + + /* we need to do two things: apply the redo record, and create an undo record */ + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + /* we KNOW there must be room for the undo record, because the redo record + was derived from an undo record */ + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + /* the redo record requires us to delete characters, so the undo record + needs to store the characters */ + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = (short)(s->undo_char_point + u->insert_length); + + /* now save the characters */ + for (i=0; i < u->insert_length; ++i) { + s->undo_char[u->char_storage + i] = + nk_str_rune_at(&state->string, u->where + i); + } + } + nk_str_delete_runes(&state->string, r.where, r.delete_length); + } + + if (r.insert_length) { + /* easy case: need to insert n characters */ + nk_str_insert_text_runes(&state->string, r.where, + &s->undo_char[r.char_storage], r.insert_length); + } + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} + +NK_INTERN void +nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length) +{ + nk_textedit_createundo(&state->undo, where, 0, length); +} + +NK_INTERN void +nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} + +NK_INTERN void +nk_textedit_makeundo_replace(struct nk_text_edit *state, int where, + int old_length, int new_length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} + +NK_INTERN void +nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, + nk_plugin_filter filter) +{ + /* reset the state to default */ + state->undo.undo_point = 0; + state->undo.undo_char_point = 0; + state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE); + state->mode = NK_TEXT_EDIT_MODE_VIEW; + state->filter = filter; +} + +NK_API void +nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(memory); + if (!state || !memory || !size) return; + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_fixed(&state->string, memory, size); +} + +NK_API void +nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(alloc); + if (!state || !alloc) return; + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init(&state->string, alloc, size); +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_textedit_init_default(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_default(&state->string); +} +#endif + +NK_API void +nk_textedit_select_all(struct nk_text_edit *state) +{ + NK_ASSERT(state); + state->select_start = 0; + state->select_end = state->string.len; +} + +NK_API void +nk_textedit_free(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + nk_str_free(&state->string); +} + +/* =============================================================== + * + * TEXT WIDGET + * + * ===============================================================*/ +#define nk_widget_state_reset(s)\ + if ((*(s)) & NK_WIDGET_STATE_MODIFIED)\ + (*(s)) = NK_WIDGET_STATE_INACTIVE|NK_WIDGET_STATE_MODIFIED;\ + else (*(s)) = NK_WIDGET_STATE_INACTIVE; + +struct nk_text { + struct nk_vec2 padding; + struct nk_color background; + struct nk_color text; +}; + +NK_INTERN void +nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + nk_flags a, const struct nk_user_font *f) +{ + struct nk_rect label; + float text_width; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + b.h = NK_MAX(b.h, 2 * t->padding.y); + label.x = 0; label.w = 0; + label.y = b.y + t->padding.y; + label.h = b.h - 2 * t->padding.y; + + text_width = f->width(f->userdata, f->height, (const char*)string, len); + text_width += (2.0f * t->padding.x); + + /* align in x-axis */ + if (a & NK_TEXT_ALIGN_LEFT) { + label.x = b.x + t->padding.x; + label.w = NK_MAX(0, b.w - 2 * t->padding.x); + } else if (a & NK_TEXT_ALIGN_CENTERED) { + label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width); + label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); + label.x = NK_MAX(b.x + t->padding.x, label.x); + label.w = NK_MIN(b.x + b.w, label.x + label.w); + if (label.w >= label.x) label.w -= label.x; + } else if (a & NK_TEXT_ALIGN_RIGHT) { + label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); + label.w = (float)text_width + 2 * t->padding.x; + } else return; + + /* align in y-axis */ + if (a & NK_TEXT_ALIGN_MIDDLE) { + label.y = b.y + b.h/2.0f - (float)f->height/2.0f; + label.h = NK_MAX(b.h/2.0f, b.h - (b.h/2.0f + f->height/2.0f)); + } else if (a & NK_TEXT_ALIGN_BOTTOM) { + label.y = b.y + b.h - f->height; + label.h = f->height; + } + nk_draw_text(o, label, (const char*)string, + len, f, t->background, t->text); +} + +NK_INTERN void +nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + const struct nk_user_font *f) +{ + float width; + int glyphs = 0; + int fitting = 0; + int done = 0; + struct nk_rect line; + struct nk_text text; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + text.padding = nk_vec2(0,0); + text.background = t->background; + text.text = t->text; + + b.w = NK_MAX(b.w, 2 * t->padding.x); + b.h = NK_MAX(b.h, 2 * t->padding.y); + b.h = b.h - 2 * t->padding.y; + + line.x = b.x + t->padding.x; + line.y = b.y + t->padding.y; + line.w = b.w - 2 * t->padding.x; + line.h = 2 * t->padding.y + f->height; + + fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width); + while (done < len) { + if (!fitting || line.y + line.h >= (b.y + b.h)) break; + nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f); + done += fitting; + line.y += f->height + 2 * t->padding.y; + fitting = nk_text_clamp(f, &string[done], len - done, + line.w, &glyphs, &width); + } +} + +/* =============================================================== + * + * BUTTON + * + * ===============================================================*/ +NK_INTERN void +nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, + struct nk_rect content, struct nk_color background, struct nk_color foreground, + float border_width, const struct nk_user_font *font) +{ + switch (type) { + case NK_SYMBOL_X: + case NK_SYMBOL_UNDERSCORE: + case NK_SYMBOL_PLUS: + case NK_SYMBOL_MINUS: { + /* single character text symbol */ + const char *X = (type == NK_SYMBOL_X) ? "x": + (type == NK_SYMBOL_UNDERSCORE) ? "_": + (type == NK_SYMBOL_PLUS) ? "+": "-"; + struct nk_text text; + text.padding = nk_vec2(0,0); + text.background = background; + text.text = foreground; + nk_widget_text(out, content, X, 1, &text, NK_TEXT_CENTERED, font); + } break; + case NK_SYMBOL_CIRCLE_SOLID: + case NK_SYMBOL_CIRCLE_OUTLINE: + case NK_SYMBOL_RECT_SOLID: + case NK_SYMBOL_RECT_OUTLINE: { + /* simple empty/filled shapes */ + if (type == NK_SYMBOL_RECT_SOLID || type == NK_SYMBOL_RECT_OUTLINE) { + nk_fill_rect(out, content, 0, foreground); + if (type == NK_SYMBOL_RECT_OUTLINE) + nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background); + } else { + nk_fill_circle(out, content, foreground); + if (type == NK_SYMBOL_CIRCLE_OUTLINE) + nk_fill_circle(out, nk_shrink_rect(content, 1), background); + } + } break; + case NK_SYMBOL_TRIANGLE_UP: + case NK_SYMBOL_TRIANGLE_DOWN: + case NK_SYMBOL_TRIANGLE_LEFT: + case NK_SYMBOL_TRIANGLE_RIGHT: { + enum nk_heading heading; + struct nk_vec2 points[3]; + heading = (type == NK_SYMBOL_TRIANGLE_RIGHT) ? NK_RIGHT : + (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_LEFT: + (type == NK_SYMBOL_TRIANGLE_UP) ? NK_UP: NK_DOWN; + nk_triangle_from_direction(points, content, 0, 0, heading); + nk_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y, + points[2].x, points[2].y, foreground); + } break; + default: + case NK_SYMBOL_NONE: + case NK_SYMBOL_MAX: break; + } +} + +NK_INTERN int +nk_button_behavior(nk_flags *state, struct nk_rect r, + const struct nk_input *i, enum nk_button_behavior behavior) +{ + int ret = 0; + nk_widget_state_reset(state); + if (!i) return 0; + if (nk_input_is_mouse_hovering_rect(i, r)) { + *state = NK_WIDGET_STATE_HOVERED; + if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) + *state = NK_WIDGET_STATE_ACTIVE; + if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { + ret = (behavior != NK_BUTTON_DEFAULT) ? + nk_input_is_mouse_down(i, NK_BUTTON_LEFT): +#ifdef NK_BUTTON_TRIGGER_ON_RELEASE + nk_input_is_mouse_released(i, NK_BUTTON_LEFT); +#else + nk_input_is_mouse_pressed(i, NK_BUTTON_LEFT); +#endif + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_LEFT; + return ret; +} + +NK_INTERN const struct nk_style_item* +nk_draw_button(struct nk_command_buffer *out, + const struct nk_rect *bounds, nk_flags state, + const struct nk_style_button *style) +{ + const struct nk_style_item *background; + if (state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else background = &style->normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds, style->border), style->rounding, + background->data.color); + } + return background; +} + +NK_INTERN int +nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, + const struct nk_style_button *style, const struct nk_input *in, + enum nk_button_behavior behavior, struct nk_rect *content) +{ + struct nk_rect bounds; + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(out); + if (!out || !style) + return nk_false; + + /* calculate button content space */ + content->x = r.x + style->padding.x + style->border + style->rounding; + content->y = r.y + style->padding.y + style->border + style->rounding; + content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2); + content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2); + + /* execute button behavior */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + return nk_button_behavior(state, bounds, in, behavior); +} + +NK_INTERN void +nk_draw_button_text(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, + const struct nk_style_button *style, const char *txt, int len, + nk_flags text_alignment, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors/images */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *content, txt, len, &text, text_alignment, font); +} + +NK_INTERN int +nk_do_button_text(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + const char *string, int len, nk_flags align, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect content; + int ret = nk_false; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(string); + NK_ASSERT(font); + if (!out || !style || !font || !string) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text(out, &bounds, &content, *state, style, string, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, + enum nk_symbol_type type, const struct nk_user_font *font) +{ + struct nk_color sym, bg; + const struct nk_style_item *background; + + /* select correct colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + bg = background->data.color; + else bg = style->text_background; + + if (state & NK_WIDGET_STATE_HOVER) + sym = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + sym = style->text_active; + else sym = style->text_normal; + nk_draw_symbol(out, type, *content, bg, sym, 1, font); +} + +NK_INTERN int +nk_do_button_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !style || !font || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, const struct nk_image *img) +{ + nk_draw_button(out, bounds, state, style); + nk_draw_image(out, *content, img, nk_white); +} + +NK_INTERN int +nk_do_button_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, enum nk_button_behavior b, + const struct nk_style_button *style, const struct nk_input *in) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, b, &content); + content.x += style->image_padding.x; + content.y += style->image_padding.y; + content.w -= 2 * style->image_padding.x; + content.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_image(out, &bounds, &content, *state, style, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_text_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, + const char *str, int len, enum nk_symbol_type type, + const struct nk_user_font *font) +{ + struct nk_color sym; + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + + /* select correct text colors */ + if (state & NK_WIDGET_STATE_HOVER) { + sym = style->text_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + sym = style->text_active; + text.text = style->text_active; + } else { + sym = style->text_normal; + text.text = style->text_normal; + } + + text.padding = nk_vec2(0,0); + nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); +} + +NK_INTERN int +nk_do_button_text_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, const char *str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect tri = {0,0,0,0}; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + tri.y = content.y + (content.h/2) - font->height/2; + tri.w = font->height; tri.h = font->height; + if (align & NK_TEXT_ALIGN_LEFT) { + tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); + tri.x = NK_MAX(tri.x, 0); + } else tri.x = content.x + 2 * style->padding.x; + + /* draw button */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_symbol(out, &bounds, &content, &tri, + *state, style, str, len, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_text_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, + const char *str, int len, const struct nk_user_font *font, + const struct nk_image *img) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); + nk_draw_image(out, *image, img, nk_white); +} + +NK_INTERN int +nk_do_button_text_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, const char* str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect icon; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !font || !style || !str) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_image(out, &bounds, &content, &icon, *state, style, str, len, font, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +/* =============================================================== + * + * TOGGLE + * + * ===============================================================*/ +enum nk_toggle_type { + NK_TOGGLE_CHECK, + NK_TOGGLE_OPTION +}; + +NK_INTERN int +nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, + nk_flags *state, int active) +{ + nk_widget_state_reset(state); + if (nk_button_behavior(state, select, in, NK_BUTTON_DEFAULT)) { + *state = NK_WIDGET_STATE_ACTIVE; + active = !active; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_LEFT; + return active; +} + +NK_INTERN void +nk_draw_checkbox(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *selector, 0, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_rect(out, *cursors, 0, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} + +NK_INTERN void +nk_draw_option(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_circle(out, *selector, style->border_color); + nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_circle(out, *cursors, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} + +NK_INTERN int +nk_do_toggle(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect r, + int *active, const char *str, int len, enum nk_toggle_type type, + const struct nk_style_toggle *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int was_active; + struct nk_rect bounds; + struct nk_rect select; + struct nk_rect cursor; + struct nk_rect label; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font || !active) + return 0; + + r.w = NK_MAX(r.w, font->height + 2 * style->padding.x); + r.h = NK_MAX(r.h, font->height + 2 * style->padding.y); + + /* add additional touch padding for touch screen devices */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + + /* calculate the selector space */ + select.w = font->height; + select.h = select.w; + select.y = r.y + r.h/2.0f - select.h/2.0f; + select.x = r.x; + + /* calculate the bounds of the cursor inside the selector */ + cursor.x = select.x + style->padding.x + style->border; + cursor.y = select.y + style->padding.y + style->border; + cursor.w = select.w - (2 * style->padding.x + 2 * style->border); + cursor.h = select.h - (2 * style->padding.y + 2 * style->border); + + /* label behind the selector */ + label.x = select.x + select.w + style->spacing; + label.y = select.y; + label.w = NK_MAX(r.x + r.w, label.x) - label.x; + label.h = select.w; + + /* update selector */ + was_active = *active; + *active = nk_toggle_behavior(in, bounds, state, *active); + + /* draw selector */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (type == NK_TOGGLE_CHECK) { + nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } else { + nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } + if (style->draw_end) + style->draw_end(out, style->userdata); + return (was_active != *active); +} + +/* =============================================================== + * + * SELECTABLE + * + * ===============================================================*/ +NK_INTERN void +nk_draw_selectable(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_selectable *style, int active, + const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img, + const char *string, int len, nk_flags align, const struct nk_user_font *font) +{ + const struct nk_style_item *background; + struct nk_text text; + text.padding = style->padding; + + /* select correct colors/images */ + if (!active) { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed; + text.text = style->text_pressed; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->text_hover; + } else { + background = &style->normal; + text.text = style->text_normal; + } + } else { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed_active; + text.text = style->text_pressed_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover_active; + text.text = style->text_hover_active; + } else { + background = &style->normal_active; + text.text = style->text_normal_active; + } + } + + + /* draw selectable background and text */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + text.background = background->data.color; + } + if (img && icon) nk_draw_image(out, *icon, img, nk_white); + nk_widget_text(out, *bounds, string, len, &text, align, font); +} + +NK_INTERN int +nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + const struct nk_style_selectable *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* remove padding */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + + /* update button */ + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, 0,0, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} + +NK_INTERN int +nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + const struct nk_image *img, const struct nk_style_selectable *style, + const struct nk_input *in, const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + struct nk_rect icon; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* toggle behavior */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, &icon, img, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} + + +/* =============================================================== + * + * SLIDER + * + * ===============================================================*/ +NK_INTERN float +nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, + struct nk_rect *visual_cursor, struct nk_input *in, + const struct nk_style_slider *style, struct nk_rect bounds, + float slider_min, float slider_max, float slider_value, + float slider_step, float slider_steps) +{ + int left_mouse_down; + int left_mouse_click_in_cursor; + + /* check if visual cursor is being dragged */ + nk_widget_state_reset(state); + left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *visual_cursor, nk_true); + + if (left_mouse_down && left_mouse_click_in_cursor) + { + float ratio = 0; + const float d = in->mouse.pos.x - (visual_cursor->x + visual_cursor->w / 2.0f); + const float pxstep = (bounds.w - (2 * style->padding.x)) / slider_steps; + + /* only update value if the next slider step is reached */ + *state = NK_WIDGET_STATE_ACTIVE; + if (NK_ABS(d) >= pxstep) { + const float steps = (float)((int)(NK_ABS(d) / pxstep)); + slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); + slider_value = NK_CLAMP(slider_min, slider_value, slider_max); + ratio = (slider_value - slider_min)/slider_step; + logical_cursor->x = bounds.x + (logical_cursor->w * ratio); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = logical_cursor->x + logical_cursor->w/2.0f; + } + } + + /* slider widget state */ + if (nk_input_is_mouse_hovering_rect(in, bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && + !nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return slider_value; +} + +NK_INTERN void +nk_draw_slider(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_slider *style, const struct nk_rect *bounds, + const struct nk_rect *visual_cursor, float min, float value, float max) +{ + struct nk_rect fill; + struct nk_rect bar; + const struct nk_style_item *background; + + /* select correct slider images/colors */ + struct nk_color bar_color; + const struct nk_style_item *cursor; + + NK_UNUSED(min); + NK_UNUSED(max); + NK_UNUSED(value); + + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + bar_color = style->bar_active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + bar_color = style->bar_hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + bar_color = style->bar_normal; + cursor = &style->cursor_normal; + } + + /* calculate slider background bar */ + bar.x = bounds->x; + bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12; + bar.w = bounds->w; + bar.h = bounds->h/6; + + /* filled background bar style */ + fill.w = (visual_cursor->x + (visual_cursor->w/2.0f)) - bar.x; + fill.x = bar.x; + fill.y = bar.y; + fill.h = bar.h; + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds, style->border), style->rounding, + background->data.color); + } + + /* draw slider bar */ + nk_fill_rect(out, bar, style->rounding, bar_color); + nk_fill_rect(out, fill, style->rounding, style->bar_filled); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white); + else nk_fill_circle(out, *visual_cursor, cursor->data.color); +} + +NK_INTERN float +nk_do_slider(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + float min, float val, float max, float step, + const struct nk_style_slider *style, struct nk_input *in, + const struct nk_user_font *font) +{ + float slider_range; + float slider_min; + float slider_max; + float slider_value; + float slider_steps; + float cursor_offset; + + struct nk_rect visual_cursor; + struct nk_rect logical_cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) + return 0; + + /* remove padding from slider bounds */ + bounds.x = bounds.x + style->padding.x; + bounds.y = bounds.y + style->padding.y; + bounds.h = NK_MAX(bounds.h, 2*style->padding.y); + bounds.w = NK_MAX(bounds.w, 2*style->padding.x + style->cursor_size.x); + bounds.w -= 2 * style->padding.x; + bounds.h -= 2 * style->padding.y; + + /* optional buttons */ + if (style->show_buttons) { + nk_flags ws; + struct nk_rect button; + button.y = bounds.y; + button.w = bounds.h; + button.h = bounds.h; + + /* decrement button */ + button.x = bounds.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT, + &style->dec_button, in, font)) + val -= step; + + /* increment button */ + button.x = (bounds.x + bounds.w) - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT, + &style->inc_button, in, font)) + val += step; + + bounds.x = bounds.x + button.w + style->spacing.x; + bounds.w = bounds.w - (2*button.w + 2*style->spacing.x); + } + + /* make sure the provided values are correct */ + slider_max = NK_MAX(min, max); + slider_min = NK_MIN(min, max); + slider_value = NK_CLAMP(slider_min, val, slider_max); + slider_range = slider_max - slider_min; + slider_steps = slider_range / step; + cursor_offset = (slider_value - slider_min) / step; + + /* remove one cursor size to support visual cursor */ + bounds.x += style->cursor_size.x*0.5f; + bounds.w -= style->cursor_size.x; + + /* calculate cursor + Basically you have two cursors. One for visual representation and interaction + and one for updating the actual cursor value. */ + logical_cursor.h = bounds.h; + logical_cursor.w = bounds.w / slider_steps; + logical_cursor.x = bounds.x + (logical_cursor.w * cursor_offset); + logical_cursor.y = bounds.y; + + visual_cursor.h = style->cursor_size.y; + visual_cursor.w = style->cursor_size.x; + visual_cursor.y = (bounds.y + bounds.h*0.5f) - visual_cursor.h*0.5f; + visual_cursor.x = (logical_cursor.x + logical_cursor.w*0.5f) - visual_cursor.w*0.5f; + + slider_value = nk_slider_behavior(state, &logical_cursor, &visual_cursor, + in, style, bounds, slider_min, slider_max, slider_value, step, slider_steps); + visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; + + /* draw slider */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_slider(out, *state, style, &bounds, &visual_cursor, slider_min, slider_value, slider_max); + if (style->draw_end) style->draw_end(out, style->userdata); + return slider_value; +} + +/* =============================================================== + * + * PROGRESSBAR + * + * ===============================================================*/ +NK_INTERN nk_size +nk_progress_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect r, nk_size max, nk_size value, int modifiable) +{ + nk_widget_state_reset(state); + if (in && modifiable && nk_input_is_mouse_hovering_rect(in, r)) { + int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, r, nk_true); + + if (left_mouse_down && left_mouse_click_in_cursor) { + float ratio = NK_MAX(0, (float)(in->mouse.pos.x - r.x)) / (float)r.w; + value = (nk_size)NK_MAX(0,((float)max * ratio)); + *state = NK_WIDGET_STATE_ACTIVE; + } else *state = NK_WIDGET_STATE_HOVERED; + } + + /* set progressbar widget state */ + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_LEFT; + + if (!max) return value; + value = NK_MIN(value, max); + return value; +} + +NK_INTERN void +nk_draw_progress(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_progress *style, const struct nk_rect *bounds, + const struct nk_rect *scursor, nk_size value, nk_size max) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + NK_UNUSED(max); + NK_UNUSED(value); + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER){ + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds, style->border), style->rounding, background->data.color); + } else nk_draw_image(out, *bounds, &background->data.image, nk_white); + + /* draw cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scursor, style->rounding, style->cursor_border_color); + nk_fill_rect(out, nk_shrink_rect(*scursor, style->cursor_border), style->rounding, cursor->data.color); + } else nk_draw_image(out, *scursor, &cursor->data.image, nk_white); +} + +NK_INTERN nk_size +nk_do_progress(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + nk_size value, nk_size max, int modifiable, + const struct nk_style_progress *style, const struct nk_input *in) +{ + float prog_scale; + nk_size prog_value; + struct nk_rect cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) return 0; + + /* calculate progressbar cursor */ + cursor.w = NK_MAX(bounds.w, 2 * style->padding.x + 2 * style->border); + cursor.h = NK_MAX(bounds.h, 2 * style->padding.y + 2 * style->border); + cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x + style->border, style->padding.y + style->border)); + prog_scale = (float)value / (float)max; + cursor.w = (bounds.w - 2) * prog_scale; + + /* update progressbar */ + prog_value = NK_MIN(value, max); + prog_value = nk_progress_behavior(state, in, bounds, max, prog_value, modifiable); + + /* draw progressbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_progress(out, *state, style, &bounds, &cursor, value, max); + if (style->draw_end) style->draw_end(out, style->userdata); + return prog_value; +} + +/* =============================================================== + * + * SCROLLBAR + * + * ===============================================================*/ +NK_INTERN float +nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, + int has_scrolling, const struct nk_rect *scroll, + const struct nk_rect *cursor, const struct nk_rect *empty0, + const struct nk_rect *empty1, float scroll_offset, + float target, float scroll_step, enum nk_orientation o) +{ + nk_flags ws; + int left_mouse_down; + int left_mouse_click_in_cursor; + + nk_widget_state_reset(state); + if (!in) return scroll_offset; + + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *cursor, nk_true); + if (nk_input_is_mouse_hovering_rect(in, *scroll)) + *state = NK_WIDGET_STATE_HOVERED; + + if (left_mouse_down && left_mouse_click_in_cursor) { + /* update cursor by mouse dragging */ + float pixel, delta; + *state = NK_WIDGET_STATE_ACTIVE; + if (o == NK_VERTICAL) { + float cursor_y; + pixel = in->mouse.delta.y; + delta = (pixel / scroll->h) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->h); + cursor_y = scroll->y + ((scroll_offset/target) * scroll->h); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = cursor_y + cursor->h/2.0f; + } else { + float cursor_x; + pixel = in->mouse.delta.x; + delta = (pixel / scroll->w) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->w); + cursor_x = scroll->x + ((scroll_offset/target) * scroll->w); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor_x + cursor->w/2.0f; + } + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_UP) && o == NK_VERTICAL && has_scrolling)|| + nk_button_behavior(&ws, *empty0, in, NK_BUTTON_DEFAULT)) { + /* scroll page up by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MAX(0, scroll_offset - scroll->h); + else scroll_offset = NK_MAX(0, scroll_offset - scroll->w); + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_DOWN) && o == NK_VERTICAL && has_scrolling) || + nk_button_behavior(&ws, *empty1, in, NK_BUTTON_DEFAULT)) { + /* scroll page down by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MIN(scroll_offset + scroll->h, target - scroll->h); + else scroll_offset = NK_MIN(scroll_offset + scroll->w, target - scroll->w); + } else if (has_scrolling) { + if ((in->mouse.scroll_delta<0 || (in->mouse.scroll_delta>0))) { + /* update cursor by mouse scrolling */ + scroll_offset = scroll_offset + scroll_step * (-in->mouse.scroll_delta); + if (o == NK_VERTICAL) + scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->h); + else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->w); + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_START)) { + /* update cursor to the beginning */ + if (o == NK_VERTICAL) scroll_offset = 0; + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_END)) { + /* update cursor to the end */ + if (o == NK_VERTICAL) scroll_offset = target - scroll->h; + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_LEFT; + return scroll_offset; +} + +NK_INTERN void +nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_scrollbar *style, const struct nk_rect *bounds, + const struct nk_rect *scroll) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds,style->border), + style->rounding, background->data.color); + } else { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } + + /* draw cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scroll, style->rounding_cursor, style->cursor_border_color); + nk_fill_rect(out, nk_shrink_rect(*scroll, style->border_cursor), + style->rounding_cursor, cursor->data.color); + } else nk_draw_image(out, *scroll, &cursor->data.image, nk_white); +} + +NK_INTERN float +nk_do_scrollbarv(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect empty_north; + struct nk_rect empty_south; + struct nk_rect cursor; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + NK_ASSERT(state); + if (!out || !style) return 0; + + scroll.w = NK_MAX(scroll.w, 1); + scroll.h = NK_MAX(scroll.h, 2 * scroll.w); + if (target <= scroll.h) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_h; + struct nk_rect button; + + button.x = scroll.x; + button.w = scroll.w; + button.h = scroll.w; + + scroll_h = scroll.h - 2 * button.h; + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.y = scroll.y; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.y = scroll.y + scroll.h - button.h; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.y = scroll.y + button.h; + scroll.h = scroll_h; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.h); + scroll_offset = NK_CLAMP(0, offset, target - scroll.h); + scroll_ratio = scroll.h / target; + scroll_off = scroll_offset / target; + + /* calculate scrollbar cursor bounds */ + cursor.h = (scroll_ratio * scroll.h) - (2*style->border + 2*style->padding.y); + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border + style->padding.y; + cursor.w = scroll.w - (2 * style->border + 2 * style->padding.x); + cursor.x = scroll.x + style->border + style->padding.x; + + /* calculate empty space around cursor */ + empty_north.x = scroll.x; + empty_north.y = scroll.y; + empty_north.w = scroll.w; + empty_north.h = cursor.y - scroll.y; + + empty_south.x = scroll.x; + empty_south.y = cursor.y + cursor.h; + empty_south.w = scroll.w; + empty_south.h = (scroll.y + scroll.h) - (cursor.y + cursor.h); + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_north, &empty_south, scroll_offset, target, scroll_step, NK_VERTICAL); + scroll_off = scroll_offset / target; + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border_cursor + style->padding.y; + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} + +NK_INTERN float +nk_do_scrollbarh(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect cursor; + struct nk_rect empty_west; + struct nk_rect empty_east; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + if (!out || !style) return 0; + + /* scrollbar background */ + scroll.h = NK_MAX(scroll.h, 1); + scroll.w = NK_MAX(scroll.w, 2 * scroll.h); + if (target <= scroll.w) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_w; + struct nk_rect button; + button.y = scroll.y; + button.w = scroll.h; + button.h = scroll.h; + + scroll_w = scroll.w - 2 * button.w; + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.x = scroll.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.x = scroll.x + scroll.w - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.x = scroll.x + button.w; + scroll.w = scroll_w; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.w); + scroll_offset = NK_CLAMP(0, offset, target - scroll.w); + scroll_ratio = scroll.w / target; + scroll_off = scroll_offset / target; + + /* calculate cursor bounds */ + cursor.w = (scroll_ratio * scroll.w) - (2*style->border + 2*style->padding.x); + cursor.x = scroll.x + (scroll_off * scroll.w) + style->border + style->padding.x; + cursor.h = scroll.h - (2 * style->border + 2 * style->padding.y); + cursor.y = scroll.y + style->border + style->padding.y; + + /* calculate empty space around cursor */ + empty_west.x = scroll.x; + empty_west.y = scroll.y; + empty_west.w = cursor.x - scroll.x; + empty_west.h = scroll.h; + + empty_east.x = cursor.x + cursor.w; + empty_east.y = scroll.y; + empty_east.w = (scroll.x + scroll.w) - (cursor.x + cursor.w); + empty_east.h = scroll.h; + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_west, &empty_east, scroll_offset, target, scroll_step, NK_HORIZONTAL); + scroll_off = scroll_offset / target; + cursor.x = scroll.x + (scroll_off * scroll.w); + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} + +/* =============================================================== + * + * FILTER + * + * ===============================================================*/ +NK_API int nk_filter_default(const struct nk_text_edit *box, nk_rune unicode) +{(void)unicode;NK_UNUSED(box);return nk_true;} + +NK_API int +nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode > 128) return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_float(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '-') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && + (unicode < 'a' || unicode > 'f') && + (unicode < 'A' || unicode > 'F')) + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode < '0' || unicode > '7') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode != '0' && unicode != '1') + return nk_false; + else return nk_true; +} + +/* =============================================================== + * + * EDIT + * + * ===============================================================*/ +NK_INTERN void +nk_edit_draw_text(struct nk_command_buffer *out, + const struct nk_style_edit *style, float pos_x, float pos_y, + float x_offset, const char *text, int byte_len, float row_height, + const struct nk_user_font *font, struct nk_color background, + struct nk_color foreground, int is_selected) +{ + NK_ASSERT(out); + NK_ASSERT(font); + NK_ASSERT(style); + if (!text || !byte_len || !out || !style) return; + + {int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + float line_width = 0; + float glyph_width; + const char *line = text; + float line_offset = 0; + int line_count = 0; + + struct nk_text txt; + txt.padding = nk_vec2(0,0); + txt.background = background; + txt.text = foreground; + + glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len); + if (!glyph_len) return; + while ((text_len < byte_len) && glyph_len) + { + if (unicode == '\n') { + /* new line sepeator so draw previous line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) /* selection needs to draw different background color */ + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_CENTERED, font); + + text_len++; + line_count++; + line_width = 0; + line = text + text_len; + line_offset += row_height; + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len)); + continue; + } + if (unicode == '\r') { + text_len++; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + line_width += (float)glyph_width; + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + if (line_width > 0) { + /* draw last line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_LEFT, font); + }} +} + +NK_INTERN nk_flags +nk_do_edit(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, + struct nk_text_edit *edit, const struct nk_style_edit *style, + struct nk_input *in, const struct nk_user_font *font) +{ + struct nk_rect area; + nk_flags ret = 0; + float row_height; + char prev_state = 0; + char is_hovered = 0; + char select_all = 0; + char cursor_follow = 0; + struct nk_rect old_clip; + struct nk_rect clip; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(style); + if (!state || !out || !style) + return ret; + + /* visible text area calculation */ + area.x = bounds.x + style->padding.x + style->border; + area.y = bounds.y + style->padding.y + style->border; + area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border); + area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border); + if (flags & NK_EDIT_MULTILINE) + area.w = area.h - style->scrollbar_size.y; + row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h; + + /* calculate clipping rectangle */ + old_clip = out->clip; + nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h); + + /* update edit state */ + prev_state = (char)edit->active; + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds); + if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) { + edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, + bounds.x, bounds.y, bounds.w, bounds.h); + } + + /* (de)activate text editor */ + if (!prev_state && edit->active) { + const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE; + nk_textedit_clear_state(edit, type, filter); + if (flags & NK_EDIT_ALWAYS_INSERT_MODE) + edit->mode = NK_TEXT_EDIT_MODE_INSERT; + if (flags & NK_EDIT_AUTO_SELECT) + select_all = nk_true; + if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) { + edit->cursor = edit->string.len; + in = 0; + } + } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW; + if (flags & NK_EDIT_READ_ONLY) + edit->mode = NK_TEXT_EDIT_MODE_VIEW; + + ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE; + if (prev_state != edit->active) + ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED; + + /* handle user input */ + if (edit->active && in) + { + int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down; + const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x; + const float mouse_y = (!(flags & NK_EDIT_MULTILINE)) ? + (in->mouse.pos.y - (area.y + area.h * 0.5f)) + edit->scrollbar.y: + (in->mouse.pos.y - area.y) + edit->scrollbar.y; + + /* mouse click handler */ + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area); + if (select_all) { + nk_textedit_select_all(edit); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) { + nk_textedit_click(edit, mouse_x, mouse_y, font, row_height); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) { + nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height); + cursor_follow = nk_true; + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked && + in->mouse.buttons[NK_BUTTON_RIGHT].down) { + nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height); + nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height); + cursor_follow = nk_true; + } + + {int i; /* keyboard input */ + int old_mode = edit->mode; + for (i = 0; i < NK_KEY_MAX; ++i) { + if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */ + if (nk_input_is_key_pressed(in, (enum nk_keys)i)) { + nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height); + cursor_follow = nk_true; + } + } + if (old_mode != edit->mode) { + in->keyboard.text_len = 0; + }} + + /* text input */ + edit->filter = filter; + if (in->keyboard.text_len) { + nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); + cursor_follow = nk_true; + in->keyboard.text_len = 0; + } + + /* enter key handler */ + if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) { + cursor_follow = nk_true; + if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod) + nk_textedit_text(edit, "\n", 1); + else if (flags & NK_EDIT_SIG_ENTER) + ret |= NK_EDIT_COMMITED; + else nk_textedit_text(edit, "\n", 1); + } + + /* cut & copy handler */ + {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY); + int cut = nk_input_is_key_pressed(in, NK_KEY_CUT); + if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD)) + { + int glyph_len; + nk_rune unicode; + const char *text; + int b = edit->select_start; + int e = edit->select_end; + + int begin = NK_MIN(b, e); + int end = NK_MAX(b, e); + text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len); + if (edit->clip.copy) + edit->clip.copy(edit->clip.userdata, text, end - begin); + if (cut && !(flags & NK_EDIT_READ_ONLY)){ + nk_textedit_cut(edit); + cursor_follow = nk_true; + } + }} + + /* paste handler */ + {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE); + if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) { + edit->clip.paste(edit->clip.userdata, edit); + cursor_follow = nk_true; + }} + + /* tab handler */ + {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB); + if (tab && (flags & NK_EDIT_ALLOW_TAB)) { + nk_textedit_text(edit, " ", 4); + cursor_follow = nk_true; + }} + } + + /* set widget state */ + if (edit->active) + *state = NK_WIDGET_STATE_ACTIVE; + else nk_widget_state_reset(state); + + if (is_hovered) + *state |= NK_WIDGET_STATE_HOVERED; + + /* DRAW EDIT */ + {const char *text = nk_str_get_const(&edit->string); + int len = nk_str_len_char(&edit->string); + + {/* select background colors/images */ + const struct nk_style_item *background; + if (*state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else if (*state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else background = &style->normal; + + /* draw background frame */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(bounds,style->border), + style->rounding, background->data.color); + } else nk_draw_image(out, bounds, &background->data.image, nk_white);} + + area.w -= style->cursor_size + style->scrollbar_size.x; + if (edit->active) + { + int total_lines = 1; + struct nk_vec2 text_size = nk_vec2(0,0); + + /* text pointer positions */ + const char *cursor_ptr = 0; + const char *select_begin_ptr = 0; + const char *select_end_ptr = 0; + + /* 2D pixel positions */ + struct nk_vec2 cursor_pos = nk_vec2(0,0); + struct nk_vec2 selection_offset_start = nk_vec2(0,0); + struct nk_vec2 selection_offset_end = nk_vec2(0,0); + + int selection_begin = NK_MIN(edit->select_start, edit->select_end); + int selection_end = NK_MAX(edit->select_start, edit->select_end); + + /* calculate total line count + total space + cursor/selection position */ + float line_width = 0.0f; + if (text && len) + { + /* utf8 encoding */ + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + int glyphs = 0; + int row_begin = 0; + + glyph_len = nk_utf_decode(text, &unicode, len); + glyph_width = font->width(font->userdata, font->height, text, glyph_len); + line_width = 0; + + /* iterate all lines */ + while ((text_len < len) && glyph_len) + { + /* set cursor 2D position and line */ + if (!cursor_ptr && glyphs == edit->cursor) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + cursor_pos.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + cursor_pos.x = row_size.x; + cursor_ptr = text + text_len; + } + + /* set start selection 2D position and line */ + if (!select_begin_ptr && edit->select_start != edit->select_end && + glyphs == selection_begin) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_start.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_start.x = row_size.x; + select_begin_ptr = text + text_len; + } + + /* set end selection 2D position and line */ + if (!select_end_ptr && edit->select_start != edit->select_end && + glyphs == selection_end) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_end.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_end.x = row_size.x; + select_end_ptr = text + text_len; + } + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + total_lines++; + line_width = 0; + text_len++; + glyphs++; + row_begin = text_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + continue; + } + + glyphs++; + text_len += glyph_len; + line_width += (float)glyph_width; + + glyph_width = font->width(font->userdata, font->height, + text+text_len, glyph_len); + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + continue; + } + text_size.y = (float)total_lines * row_height; + + /* handle case if cursor is at end of text buffer */ + if (!cursor_ptr && edit->cursor == edit->string.len) { + cursor_pos.x = line_width; + cursor_pos.y = text_size.y - row_height; + } + } + { + /* scrollbar */ + if (cursor_follow) + { + /* update scrollbar to follow cursor */ + if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) { + /* horizontal scroll */ + const float scroll_increment = area.w * 0.25f; + if (cursor_pos.x < edit->scrollbar.x) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment); + if (cursor_pos.x >= edit->scrollbar.x + area.w) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x); + } else edit->scrollbar.x = 0; + + if (flags & NK_EDIT_MULTILINE) { + /* vertical scroll */ + if (cursor_pos.y < edit->scrollbar.y) + edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height); + if (cursor_pos.y >= edit->scrollbar.y + area.h) + edit->scrollbar.y = edit->scrollbar.y + row_height; + } else edit->scrollbar.y = 0; + } + + /* scrollbar widget */ + if (flags & NK_EDIT_MULTILINE) + { + nk_flags ws; + struct nk_rect scroll; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + + scroll = area; + scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x; + scroll.w = style->scrollbar_size.x; + + scroll_offset = edit->scrollbar.y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = text_size.y; + edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &style->scrollbar, in, font); + } + } + + /* draw text */ + {struct nk_color background_color; + struct nk_color text_color; + struct nk_color sel_background_color; + struct nk_color sel_text_color; + struct nk_color cursor_color; + struct nk_color cursor_text_color; + const struct nk_style_item *background; + nk_push_scissor(out, clip); + + /* select correct colors to draw */ + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_color = style->cursor_hover; + cursor_text_color = style->cursor_text_hover; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_text_color = style->cursor_text_hover; + cursor_color = style->cursor_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + sel_text_color = style->selected_text_normal; + sel_background_color = style->selected_normal; + cursor_color = style->cursor_normal; + cursor_text_color = style->cursor_text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + + + if (edit->select_start == edit->select_end) { + /* no selection so just draw the complete text */ + const char *begin = nk_str_get_const(&edit->string); + int l = nk_str_len_char(&edit->string); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } else { + /* edit has selection so draw 1-3 text chunks */ + if (edit->select_start != edit->select_end && selection_begin > 0){ + /* draw unselected text before selection */ + const char *begin = nk_str_get_const(&edit->string); + NK_ASSERT(select_begin_ptr); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin), + row_height, font, background_color, text_color, nk_false); + } + if (edit->select_start != edit->select_end) { + /* draw selected text */ + NK_ASSERT(select_begin_ptr); + if (!select_end_ptr) { + const char *begin = nk_str_get_const(&edit->string); + select_end_ptr = begin + nk_str_len_char(&edit->string); + } + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_start.y - edit->scrollbar.y, + selection_offset_start.x, + select_begin_ptr, (int)(select_end_ptr - select_begin_ptr), + row_height, font, sel_background_color, sel_text_color, nk_true); + } + if ((edit->select_start != edit->select_end && + selection_end < edit->string.len)) + { + /* draw unselected text after selected text */ + const char *begin = select_end_ptr; + const char *end = nk_str_get_const(&edit->string) + + nk_str_len_char(&edit->string); + NK_ASSERT(select_end_ptr); + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_end.y - edit->scrollbar.y, + selection_offset_end.x, + begin, (int)(end - begin), row_height, font, + background_color, text_color, nk_true); + } + } + + /* cursor */ + if (edit->select_start == edit->select_end) + { + if (edit->cursor >= nk_str_len(&edit->string) || + (cursor_ptr && *cursor_ptr == '\n')) { + /* draw cursor at end of line */ + struct nk_rect cursor; + cursor.w = style->cursor_size; + cursor.h = font->height; + cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; + cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; + cursor.y -= edit->scrollbar.y; + nk_fill_rect(out, cursor, 0, cursor_color); + } else { + /* draw cursor inside text */ + int glyph_len; + struct nk_rect label; + struct nk_text txt; + + nk_rune unicode; + NK_ASSERT(cursor_ptr); + glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4); + + label.x = area.x + cursor_pos.x - edit->scrollbar.x; + label.y = area.y + cursor_pos.y - edit->scrollbar.y; + label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len); + label.h = row_height; + + txt.padding = nk_vec2(0,0); + txt.background = cursor_color; + txt.text = cursor_text_color; + nk_fill_rect(out, label, 0, cursor_color); + nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); + } + }} + } else { + /* not active so just draw text */ + int l = nk_str_len(&edit->string); + const char *begin = nk_str_get_const(&edit->string); + + const struct nk_style_item *background; + struct nk_color background_color; + struct nk_color text_color; + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } + nk_push_scissor(out, old_clip);} + return ret; +} + +/* =============================================================== + * + * PROPERTY + * + * ===============================================================*/ +enum nk_property_status { + NK_PROPERTY_DEFAULT, + NK_PROPERTY_EDIT, + NK_PROPERTY_DRAG +}; +enum nk_property_filter { + NK_FILTER_INT, + NK_FILTER_FLOAT +}; +enum nk_property_kind { + NK_PROPERTY_INT, + NK_PROPERTY_FLOAT, + NK_PROPERTY_DOUBLE +}; +union nk_property { + int i; + float f; + double d; +}; +struct nk_property_variant { + enum nk_property_kind kind; + union nk_property value; + union nk_property min_value; + union nk_property max_value; + union nk_property step; +}; + +NK_INTERN void +nk_drag_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect drag, struct nk_property_variant *variant, + float inc_per_pixel) +{ + int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_cursor = in && + nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true); + + nk_widget_state_reset(state); + if (nk_input_is_mouse_hovering_rect(in, drag)) + *state = NK_WIDGET_STATE_HOVERED; + + if (left_mouse_down && left_mouse_click_in_cursor) { + float delta, pixels; + pixels = in->mouse.delta.x; + delta = pixels * inc_per_pixel; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = variant->value.i + (int)delta; + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + variant->value.f = variant->value.f + (float)delta; + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + variant->value.d = variant->value.d + (double)delta; + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + *state = NK_WIDGET_STATE_ACTIVE; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_LEFT; +} + +NK_INTERN void +nk_property_behavior(nk_flags *ws, const struct nk_input *in, + struct nk_rect property, struct nk_rect label, struct nk_rect edit, + struct nk_rect empty, int *state, struct nk_property_variant *variant, + float inc_per_pixel) +{ + if (in && *state == NK_PROPERTY_DEFAULT) { + if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT)) + *state = NK_PROPERTY_EDIT; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true)) + *state = NK_PROPERTY_DRAG; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true)) + *state = NK_PROPERTY_DRAG; + } + if (*state == NK_PROPERTY_DRAG) { + nk_drag_behavior(ws, in, property, variant, inc_per_pixel); + if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT; + } +} + +NK_INTERN void +nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, + const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, + const char *name, int len, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background and text color */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text.text = style->label_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->label_hover; + } else { + background = &style->normal; + text.text = style->label_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, *bounds, style->rounding, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*bounds,style->border), + style->rounding, background->data.color); + } + + /* draw label */ + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font); +} + +NK_INTERN void +nk_do_property(nk_flags *ws, + struct nk_command_buffer *out, struct nk_rect property, + const char *name, struct nk_property_variant *variant, + float inc_per_pixel, char *buffer, int *len, + int *state, int *cursor, const struct nk_style_property *style, + enum nk_property_filter filter, struct nk_input *in, + const struct nk_user_font *font, struct nk_text_edit *text_edit) +{ + const nk_plugin_filter filters[] = { + nk_filter_decimal, + nk_filter_float + }; + int active, old; + int num_len, name_len; + char string[NK_MAX_NUMBER_BUFFER]; + float size; + + char *dst = 0; + int *length; + + struct nk_rect left; + struct nk_rect right; + struct nk_rect label; + struct nk_rect edit; + struct nk_rect empty; + + /* left decrement button */ + left.h = font->height/2; + left.w = left.h; + left.x = property.x + style->border + style->padding.x; + left.y = property.y + style->border + property.h/2.0f - left.h/2; + + /* text label */ + name_len = nk_strlen(name); + size = font->width(font->userdata, font->height, name, name_len); + label.x = left.x + left.w + style->padding.x; + label.w = (float)size + 2 * style->padding.x; + label.y = property.y + style->border + style->padding.y; + label.h = property.h - (2 * style->border + 2 * style->padding.y); + + /* right increment button */ + right.y = left.y; + right.w = left.w; + right.h = left.h; + right.x = property.x + property.w - (right.w + style->padding.x); + + /* edit */ + if (*state == NK_PROPERTY_EDIT) { + size = font->width(font->userdata, font->height, buffer, *len); + size += style->edit.cursor_size; + length = len; + dst = buffer; + } else { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + nk_itoa(string, variant->value.i); + num_len = nk_strlen(string); + break; + case NK_PROPERTY_FLOAT: + nk_dtoa(string, (double)variant->value.f); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + case NK_PROPERTY_DOUBLE: + nk_dtoa(string, variant->value.d); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + } + size = font->width(font->userdata, font->height, string, num_len); + dst = string; + length = &num_len; + } + + edit.w = (float)size + 2 * style->padding.x; + edit.w = NK_MIN(edit.w, right.x - (label.x + label.w)); + edit.x = right.x - (edit.w + style->padding.x); + edit.y = property.y + style->border; + edit.h = property.h - (2 * style->border); + + /* empty left space activator */ + empty.w = edit.x - (label.x + label.w); + empty.x = label.x + label.w; + empty.y = property.y; + empty.h = property.h; + + /* update property */ + old = (*state == NK_PROPERTY_EDIT); + nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel); + + /* draw property */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_property(out, style, &property, &label, *ws, name, name_len, font); + if (style->draw_end) style->draw_end(out, style->userdata); + + /* execute right button */ + if (nk_do_button_symbol(ws, out, left, style->sym_left, NK_BUTTON_DEFAULT, &style->dec_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break; + } + } + + /* execute left button */ + if (nk_do_button_symbol(ws, out, right, style->sym_right, NK_BUTTON_DEFAULT, &style->inc_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break; + } + } + + active = (*state == NK_PROPERTY_EDIT); + if (old != NK_PROPERTY_EDIT && active) { + /* property has been activated so setup buffer */ + NK_MEMCPY(buffer, dst, (nk_size)*length); + *cursor = nk_utf_len(buffer, *length); + *len = *length; + length = len; + dst = buffer; + } + + /* execute and run text edit field */ + nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]); + text_edit->active = (unsigned char)active; + text_edit->string.len = *length; + text_edit->cursor = NK_CLAMP(0, *cursor, *length); + text_edit->string.buffer.allocated = (nk_size)*length; + text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER; + text_edit->string.buffer.memory.ptr = dst; + text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER; + text_edit->mode = NK_TEXT_EDIT_MODE_INSERT; + nk_do_edit(ws, out, edit, NK_EDIT_ALWAYS_INSERT_MODE, filters[filter], + text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font); + + *length = text_edit->string.len; + active = text_edit->active; + *cursor = text_edit->cursor; + + if (active && nk_input_is_key_pressed(in, NK_KEY_ENTER)) + active = !active; + + if (old && !active) { + /* property is now not active so convert edit text to value*/ + *state = NK_PROPERTY_DEFAULT; + buffer[*len] = '\0'; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = nk_strtoi(buffer, 0); + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.f = nk_strtof(buffer, 0); + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.d = nk_strtod(buffer, 0); + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + } +} +/* =============================================================== + * + * COLOR PICKER + * + * ===============================================================*/ +NK_INTERN int +nk_color_picker_behavior(nk_flags *state, + const struct nk_rect *bounds, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_color *color, const struct nk_input *in) +{ + float hsva[4]; + int value_changed = 0; + int hsv_changed = 0; + + NK_ASSERT(state); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + NK_ASSERT(color); + + /* color matrix */ + nk_color_hsva_fv(hsva, *color); + if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) { + hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); + hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); + value_changed = hsv_changed = 1; + } + + /* hue bar */ + if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) { + hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); + value_changed = hsv_changed = 1; + } + + /* alpha bar */ + if (alpha_bar) { + if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) { + hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); + value_changed = 1; + } + } + nk_widget_state_reset(state); + if (hsv_changed) { + *color = nk_hsva_fv(hsva); + *state = NK_WIDGET_STATE_ACTIVE; + } + if (value_changed) { + color->a = (nk_byte)(hsva[3] * 255.0f); + *state = NK_WIDGET_STATE_ACTIVE; + } + + /* set color picker widget state */ + if (nk_input_is_mouse_hovering_rect(in, *bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return value_changed; +} + +NK_INTERN void +nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_color color) +{ + NK_STORAGE const struct nk_color black = {0,0,0,255}; + NK_STORAGE const struct nk_color white = {255, 255, 255, 255}; + NK_STORAGE const struct nk_color black_trans = {0,0,0,0}; + + const float crosshair_size = 7.0f; + struct nk_color temp; + float hsva[4]; + float line_y; + int i; + + NK_ASSERT(o); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + + /* draw hue bar */ + nk_color_hsv_fv(hsva, color); + for (i = 0; i < 6; ++i) { + NK_GLOBAL const struct nk_color hue_colors[] = { + {255, 0, 0, 255}, + {255,255,0,255}, + {0,255,0,255}, + {0, 255,255,255}, + {0,0,255,255}, + {255, 0, 255, 255}, + {255, 0, 0, 255} + }; + nk_fill_rect_multi_color(o, + nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, + hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], + hue_colors[i+1], hue_colors[i+1]); + } + line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); + nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + + /* draw alpha bar */ + if (alpha_bar) { + float alpha = NK_SATURATE((float)color.a/255.0f); + line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); + + nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); + nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + } + + /* draw color matrix */ + temp = nk_hsv_f(hsva[0], 1.0f, 1.0f); + nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white); + nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); + + /* draw cross-hair */ + {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2]; + p.x = (float)(int)(matrix->x + S * matrix->w); + p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h); + nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); + nk_stroke_line(o, p.x + crosshair_size + 1, p.y, p.x+3, p.y, 1.0f, white); + nk_stroke_line(o, p.x, p.y + crosshair_size + 1, p.x, p.y+3, 1.0f, white); + nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, white);} +} + +NK_INTERN int +nk_do_color_picker(nk_flags *state, + struct nk_command_buffer *out, struct nk_color *color, + enum nk_color_format fmt, struct nk_rect bounds, + struct nk_vec2 padding, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret = 0; + struct nk_rect matrix; + struct nk_rect hue_bar; + struct nk_rect alpha_bar; + float bar_w; + + NK_ASSERT(out); + NK_ASSERT(color); + NK_ASSERT(state); + NK_ASSERT(font); + if (!out || !color || !state || !font) + return ret; + + bar_w = font->height; + bounds.x += padding.x; + bounds.y += padding.x; + bounds.w -= 2 * padding.x; + bounds.h -= 2 * padding.y; + + matrix.x = bounds.x; + matrix.y = bounds.y; + matrix.h = bounds.h; + matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); + + hue_bar.w = bar_w; + hue_bar.y = bounds.y; + hue_bar.h = matrix.h; + hue_bar.x = matrix.x + matrix.w + padding.x; + + alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; + alpha_bar.y = bounds.y; + alpha_bar.w = bar_w; + alpha_bar.h = matrix.h; + + ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar, + (fmt == NK_RGBA) ? &alpha_bar:0, color, in); + nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *color); + return ret; +} + +/* ============================================================== + * + * STYLE + * + * ===============================================================*/ +NK_API void nk_style_default(struct nk_context *ctx){nk_style_from_table(ctx, 0);} +#define NK_COLOR_MAP(NK_COLOR)\ + NK_COLOR(NK_COLOR_TEXT, 175,175,175,255) \ + NK_COLOR(NK_COLOR_WINDOW, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_HEADER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BORDER, 65, 65, 65, 255) \ + NK_COLOR(NK_COLOR_BUTTON, 50, 50, 50, 255) \ + NK_COLOR(NK_COLOR_BUTTON_HOVER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BUTTON_ACTIVE, 35, 35, 35, 255) \ + NK_COLOR(NK_COLOR_TOGGLE, 100,100,100,255) \ + NK_COLOR(NK_COLOR_TOGGLE_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_TOGGLE_CURSOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT_ACTIVE, 35, 35, 35,255) \ + NK_COLOR(NK_COLOR_SLIDER, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ + NK_COLOR(NK_COLOR_PROPERTY, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT_CURSOR, 175,175,175,255) \ + NK_COLOR(NK_COLOR_COMBO, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART, 120,120,120,255) \ + NK_COLOR(NK_COLOR_CHART_COLOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT,255, 0, 0, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER,120,120,120,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE,150,150,150,255) \ + NK_COLOR(NK_COLOR_TAB_HEADER, 40, 40, 40,255) + +NK_GLOBAL const struct nk_color +nk_default_color_style[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) {b,c,d,e}, + NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; + +NK_GLOBAL const char *nk_color_names[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) #a, + NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; + +NK_API const char *nk_style_get_color_by_name(enum nk_style_colors c) +{return nk_color_names[c];} + +NK_API struct nk_style_item nk_style_item_image(struct nk_image img) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_IMAGE; i.data.image = img; return i;} + +NK_API struct nk_style_item nk_style_item_color(struct nk_color col) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = col; return i;} + +NK_API struct nk_style_item nk_style_item_hide(void) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = nk_rgba(0,0,0,0); return i;} + +NK_API void +nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) +{ + struct nk_style *style; + struct nk_style_text *text; + struct nk_style_button *button; + struct nk_style_toggle *toggle; + struct nk_style_selectable *select; + struct nk_style_slider *slider; + struct nk_style_progress *prog; + struct nk_style_scrollbar *scroll; + struct nk_style_edit *edit; + struct nk_style_property *property; + struct nk_style_combo *combo; + struct nk_style_chart *chart; + struct nk_style_tab *tab; + struct nk_style_window *win; + + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + table = (!table) ? nk_default_color_style: table; + + /* default text */ + text = &style->text; + text->color = table[NK_COLOR_TEXT]; + text->padding = nk_vec2(4,4); + + /* default button */ + button = &style->button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_BUTTON]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_BORDER]; + button->text_background = table[NK_COLOR_BUTTON]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->image_padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f, 0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 4.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* contextual button */ + button = &style->contextual_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* menu button */ + button = &style->menu_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 1.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* checkbox toggle */ + toggle = &style->checkbox; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(2.0f, 2.0f); + toggle->touch_padding = nk_vec2(0,0); + toggle->border_color = nk_rgba(0,0,0,0); + toggle->border = 0.0f; + toggle->spacing = 4; + + /* option toggle */ + toggle = &style->option; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(3.0f, 3.0f); + toggle->touch_padding = nk_vec2(0,0); + toggle->border_color = nk_rgba(0,0,0,0); + toggle->border = 0.0f; + toggle->spacing = 4; + + /* selectable */ + select = &style->selectable; + nk_zero_struct(*select); + select->normal = nk_style_item_color(table[NK_COLOR_SELECT]); + select->hover = nk_style_item_color(table[NK_COLOR_SELECT]); + select->pressed = nk_style_item_color(table[NK_COLOR_SELECT]); + select->normal_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->hover_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->pressed_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->text_normal = table[NK_COLOR_TEXT]; + select->text_hover = table[NK_COLOR_TEXT]; + select->text_pressed = table[NK_COLOR_TEXT]; + select->text_normal_active = table[NK_COLOR_TEXT]; + select->text_hover_active = table[NK_COLOR_TEXT]; + select->text_pressed_active = table[NK_COLOR_TEXT]; + select->padding = nk_vec2(2.0f,2.0f); + select->touch_padding = nk_vec2(0,0); + select->userdata = nk_handle_ptr(0); + select->rounding = 0.0f; + select->draw_begin = 0; + select->draw_end = 0; + + /* slider */ + slider = &style->slider; + nk_zero_struct(*slider); + slider->normal = nk_style_item_hide(); + slider->hover = nk_style_item_hide(); + slider->active = nk_style_item_hide(); + slider->bar_normal = table[NK_COLOR_SLIDER]; + slider->bar_hover = table[NK_COLOR_SLIDER]; + slider->bar_active = table[NK_COLOR_SLIDER]; + slider->bar_filled = table[NK_COLOR_SLIDER_CURSOR]; + slider->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + slider->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + slider->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + slider->inc_symbol = NK_SYMBOL_TRIANGLE_RIGHT; + slider->dec_symbol = NK_SYMBOL_TRIANGLE_LEFT; + slider->cursor_size = nk_vec2(16,16); + slider->padding = nk_vec2(2,2); + slider->spacing = nk_vec2(2,2); + slider->userdata = nk_handle_ptr(0); + slider->show_buttons = nk_false; + slider->bar_height = 8; + slider->rounding = 0; + slider->draw_begin = 0; + slider->draw_end = 0; + + /* slider buttons */ + button = &style->slider.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(8.0f,8.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->slider.dec_button = style->slider.inc_button; + + /* progressbar */ + prog = &style->progress; + nk_zero_struct(*prog); + prog->normal = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->hover = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->active = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + prog->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + prog->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + prog->border_color = nk_rgba(0,0,0,0); + prog->cursor_border_color = nk_rgba(0,0,0,0); + prog->userdata = nk_handle_ptr(0); + prog->padding = nk_vec2(4,4); + prog->rounding = 0; + prog->border = 0; + prog->cursor_rounding = 0; + prog->cursor_border = 0; + prog->draw_begin = 0; + prog->draw_end = 0; + + /* scrollbars */ + scroll = &style->scrollh; + nk_zero_struct(*scroll); + scroll->normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->active = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->cursor_normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR]); + scroll->cursor_hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_HOVER]); + scroll->cursor_active = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE]); + scroll->dec_symbol = NK_SYMBOL_CIRCLE_SOLID; + scroll->inc_symbol = NK_SYMBOL_CIRCLE_SOLID; + scroll->userdata = nk_handle_ptr(0); + scroll->border_color = table[NK_COLOR_BORDER]; + scroll->cursor_border_color = table[NK_COLOR_BORDER]; + scroll->padding = nk_vec2(0,0); + scroll->show_buttons = nk_false; + scroll->border = 0; + scroll->rounding = 0; + scroll->border_cursor = 0; + scroll->rounding_cursor = 0; + scroll->draw_begin = 0; + scroll->draw_end = 0; + style->scrollv = style->scrollh; + + /* scrollbars buttons */ + button = &style->scrollh.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(4.0f,4.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->scrollh.dec_button = style->scrollh.inc_button; + style->scrollv.inc_button = style->scrollh.inc_button; + style->scrollv.dec_button = style->scrollh.inc_button; + + /* edit */ + edit = &style->edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->hover = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->active = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->border_color = table[NK_COLOR_BORDER]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->scrollbar_size = nk_vec2(10,10); + edit->scrollbar = style->scrollv; + edit->padding = nk_vec2(4,4); + edit->row_padding = 2; + edit->cursor_size = 4; + edit->border = 1; + edit->rounding = 0; + + /* property */ + property = &style->property; + nk_zero_struct(*property); + property->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->border_color = table[NK_COLOR_BORDER]; + property->label_normal = table[NK_COLOR_TEXT]; + property->label_hover = table[NK_COLOR_TEXT]; + property->label_active = table[NK_COLOR_TEXT]; + property->sym_left = NK_SYMBOL_TRIANGLE_LEFT; + property->sym_right = NK_SYMBOL_TRIANGLE_RIGHT; + property->userdata = nk_handle_ptr(0); + property->padding = nk_vec2(4,4); + property->border = 1; + property->rounding = 10; + property->draw_begin = 0; + property->draw_end = 0; + + /* property buttons */ + button = &style->property.dec_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_PROPERTY]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->property.inc_button = style->property.dec_button; + + /* property edit */ + edit = &style->property.edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->border_color = nk_rgba(0,0,0,0); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->padding = nk_vec2(0,0); + edit->cursor_size = 8; + edit->border = 0; + edit->rounding = 0; + + /* chart */ + chart = &style->chart; + nk_zero_struct(*chart); + chart->background = nk_style_item_color(table[NK_COLOR_CHART]); + chart->border_color = table[NK_COLOR_BORDER]; + chart->selected_color = table[NK_COLOR_CHART_COLOR_HIGHLIGHT]; + chart->color = table[NK_COLOR_CHART_COLOR]; + chart->padding = nk_vec2(4,4); + chart->border = 0; + chart->rounding = 0; + + /* combo */ + combo = &style->combo; + combo->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->active = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->border_color = table[NK_COLOR_BORDER]; + combo->label_normal = table[NK_COLOR_TEXT]; + combo->label_hover = table[NK_COLOR_TEXT]; + combo->label_active = table[NK_COLOR_TEXT]; + combo->sym_normal = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_hover = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_active = NK_SYMBOL_TRIANGLE_DOWN; + combo->content_padding = nk_vec2(4,4); + combo->button_padding = nk_vec2(0,4); + combo->spacing = nk_vec2(4,0); + combo->border = 1; + combo->rounding = 0; + + /* combo button */ + button = &style->combo.button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + button->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + button->active = nk_style_item_color(table[NK_COLOR_COMBO]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_COMBO]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* tab */ + tab = &style->tab; + tab->background = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + tab->border_color = table[NK_COLOR_BORDER]; + tab->text = table[NK_COLOR_TEXT]; + tab->sym_minimize = NK_SYMBOL_TRIANGLE_RIGHT; + tab->sym_maximize = NK_SYMBOL_TRIANGLE_DOWN; + tab->padding = nk_vec2(4,4); + tab->spacing = nk_vec2(4,4); + tab->indent = 10.0f; + tab->border = 1; + tab->rounding = 0; + + /* tab button */ + button = &style->tab.tab_minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->tab.tab_maximize_button =*button; + + /* node button */ + button = &style->tab.node_minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->tab.node_maximize_button =*button; + + /* window header */ + win = &style->window; + win->header.align = NK_HEADER_RIGHT; + win->header.close_symbol = NK_SYMBOL_X; + win->header.minimize_symbol = NK_SYMBOL_MINUS; + win->header.maximize_symbol = NK_SYMBOL_PLUS; + win->header.normal = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.hover = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.active = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.label_normal = table[NK_COLOR_TEXT]; + win->header.label_hover = table[NK_COLOR_TEXT]; + win->header.label_active = table[NK_COLOR_TEXT]; + win->header.label_padding = nk_vec2(4,4); + win->header.padding = nk_vec2(4,4); + win->header.spacing = nk_vec2(0,0); + + /* window header close button */ + button = &style->window.header.close_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window header minimize button */ + button = &style->window.header.minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window */ + win->background = table[NK_COLOR_WINDOW]; + win->fixed_background = nk_style_item_color(table[NK_COLOR_WINDOW]); + win->border_color = table[NK_COLOR_BORDER]; + win->combo_border_color = table[NK_COLOR_BORDER]; + win->contextual_border_color = table[NK_COLOR_BORDER]; + win->menu_border_color = table[NK_COLOR_BORDER]; + win->group_border_color = table[NK_COLOR_BORDER]; + win->tooltip_border_color = table[NK_COLOR_BORDER]; + win->scaler = nk_style_item_color(table[NK_COLOR_TEXT]); + + win->rounding = 0.0f; + win->spacing = nk_vec2(4,4); + win->scrollbar_size = nk_vec2(10,10); + win->min_size = nk_vec2(64,64); + + win->combo_border = 1.0f; + win->contextual_border = 1.0f; + win->menu_border = 1.0f; + win->group_border = 1.0f; + win->tooltip_border = 1.0f; + win->border = 2.0f; + + win->padding = nk_vec2(4,4); + win->group_padding = nk_vec2(4,4); + win->popup_padding = nk_vec2(4,4); + win->combo_padding = nk_vec2(4,4); + win->contextual_padding = nk_vec2(4,4); + win->menu_padding = nk_vec2(4,4); + win->tooltip_padding = nk_vec2(4,4); +} + +NK_API void +nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + style->font = font; + ctx->stacks.fonts.head = 0; +} + +NK_API int +nk_style_push_font(struct nk_context *ctx, struct nk_user_font *font) +{ + struct nk_config_stack_user_font *font_stack; + struct nk_config_stack_user_font_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + font_stack = &ctx->stacks.fonts; + NK_ASSERT(font_stack->head < (int)NK_LEN(font_stack->elements)); + if (font_stack->head >= (int)NK_LEN(font_stack->elements)) + return 0; + + element = &font_stack->elements[font_stack->head++]; + element->address = &ctx->style.font; + element->old_value = ctx->style.font; + ctx->style.font = font; + return 1; +} + +NK_API int +nk_style_pop_font(struct nk_context *ctx) +{ + struct nk_config_stack_user_font *font_stack; + struct nk_config_stack_user_font_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + font_stack = &ctx->stacks.fonts; + NK_ASSERT(font_stack->head > 0); + if (font_stack->head < 1) + return 0; + + element = &font_stack->elements[--font_stack->head]; + *element->address = element->old_value; + return 1; +} + +#define NK_STYLE_PUSH_IMPLEMENATION(prefix, type, stack) \ +nk_style_push_##type(struct nk_context *ctx, prefix##_##type *address, prefix##_##type value)\ +{\ + struct nk_config_stack_##type * type_stack;\ + struct nk_config_stack_##type##_element *element;\ + NK_ASSERT(ctx);\ + if (!ctx) return 0;\ + type_stack = &ctx->stacks.stack;\ + NK_ASSERT(type_stack->head < (int)NK_LEN(type_stack->elements));\ + if (type_stack->head >= (int)NK_LEN(type_stack->elements))\ + return 0;\ + element = &type_stack->elements[type_stack->head++];\ + element->address = address;\ + element->old_value = *address;\ + *address = value;\ + return 1;\ +} + +#define NK_STYLE_POP_IMPLEMENATION(type, stack) \ +nk_style_pop_##type(struct nk_context *ctx)\ +{\ + struct nk_config_stack_##type *type_stack;\ + struct nk_config_stack_##type##_element *element;\ + NK_ASSERT(ctx);\ + if (!ctx) return 0;\ + type_stack = &ctx->stacks.stack;\ + NK_ASSERT(type_stack->head > 0);\ + if (type_stack->head < 1)\ + return 0;\ + element = &type_stack->elements[--type_stack->head];\ + *element->address = element->old_value;\ + return 1;\ +} + +NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, style_item, style_items) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(nk,float, floats) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, vec2, vectors) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(nk,flags, flags) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk,color, colors) + +NK_API int NK_STYLE_POP_IMPLEMENATION(style_item, style_items) +NK_API int NK_STYLE_POP_IMPLEMENATION(float,floats) +NK_API int NK_STYLE_POP_IMPLEMENATION(vec2, vectors) +NK_API int NK_STYLE_POP_IMPLEMENATION(flags,flags) +NK_API int NK_STYLE_POP_IMPLEMENATION(color,colors) + +NK_API int +nk_style_set_cursor(struct nk_context *ctx, enum nk_style_cursor c) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return 0; + style = &ctx->style; + if (style->cursors[c]) { + style->cursor_active = style->cursors[c]; + return 1; + } + return 0; +} + +NK_API void +nk_style_show_cursor(struct nk_context *ctx) +{ + ctx->style.cursor_visible = nk_true; +} + +NK_API void +nk_style_hide_cursor(struct nk_context *ctx) +{ + ctx->style.cursor_visible = nk_false; +} + +NK_API void +nk_style_load_cursor(struct nk_context *ctx, enum nk_style_cursor cursor, + const struct nk_cursor *c) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + style->cursors[cursor] = c; +} + +NK_API void +nk_style_load_all_cursors(struct nk_context *ctx, struct nk_cursor *cursors) +{ + int i = 0; + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + for (i = 0; i < NK_CURSOR_COUNT; ++i) + style->cursors[i] = &cursors[i]; + style->cursor_visible = nk_true; +} + +/* =============================================================== + * + * POOL + * + * ===============================================================*/ +NK_INTERN void +nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, + unsigned int capacity) +{ + nk_zero(pool, sizeof(*pool)); + pool->alloc = *alloc; + pool->capacity = capacity; + pool->type = NK_BUFFER_DYNAMIC; + pool->pages = 0; +} + +NK_INTERN void +nk_pool_free(struct nk_pool *pool) +{ + struct nk_page *next; + struct nk_page *iter = pool->pages; + if (!pool) return; + if (pool->type == NK_BUFFER_FIXED) return; + while (iter) { + next = iter->next; + pool->alloc.free(pool->alloc.userdata, iter); + iter = next; + } +} + +NK_INTERN void +nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size) +{ + nk_zero(pool, sizeof(*pool)); + NK_ASSERT(size >= sizeof(struct nk_page)); + if (size < sizeof(struct nk_page)) return; + pool->capacity = (unsigned)(size - sizeof(struct nk_page)) / sizeof(struct nk_page_element); + pool->pages = (struct nk_page*)memory; + pool->type = NK_BUFFER_FIXED; + pool->size = size; +} + +NK_INTERN struct nk_page_element* +nk_pool_alloc(struct nk_pool *pool) +{ + if (!pool->pages || pool->pages->size >= pool->capacity) { + /* allocate new page */ + struct nk_page *page; + if (pool->type == NK_BUFFER_FIXED) { + if (!pool->pages) { + NK_ASSERT(pool->pages); + return 0; + } + NK_ASSERT(pool->pages->size < pool->capacity); + return 0; + } else { + nk_size size = sizeof(struct nk_page); + size += NK_POOL_DEFAULT_CAPACITY * sizeof(union nk_page_data); + page = (struct nk_page*)pool->alloc.alloc(pool->alloc.userdata,0, size); + page->next = pool->pages; + pool->pages = page; + page->size = 0; + } + } + return &pool->pages->win[pool->pages->size++]; +} + +/* =============================================================== + * + * CONTEXT + * + * ===============================================================*/ +NK_INTERN void* nk_create_window(struct nk_context *ctx); +NK_INTERN void nk_remove_window(struct nk_context*, struct nk_window*); +NK_INTERN void nk_free_window(struct nk_context *ctx, struct nk_window *win); +NK_INTERN void nk_free_table(struct nk_context *ctx, struct nk_table *tbl); +NK_INTERN void nk_remove_table(struct nk_window *win, struct nk_table *tbl); + +NK_INTERN void +nk_setup(struct nk_context *ctx, const struct nk_user_font *font) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_zero_struct(*ctx); + nk_style_default(ctx); + ctx->seq = 1; + if (font) ctx->style.font = font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_draw_list_init(&ctx->draw_list); +#endif +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API int +nk_init_default(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + return nk_init(ctx, &alloc, font); +} +#endif + +NK_API int +nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, + const struct nk_user_font *font) +{ + NK_ASSERT(memory); + if (!memory) return 0; + nk_setup(ctx, font); + nk_buffer_init_fixed(&ctx->memory, memory, size); + ctx->use_pool = nk_false; + return 1; +} + +NK_API int +nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *pool, const struct nk_user_font *font) +{ + NK_ASSERT(cmds); + NK_ASSERT(pool); + if (!cmds || !pool) return 0; + nk_setup(ctx, font); + ctx->memory = *cmds; + if (pool->type == NK_BUFFER_FIXED) { + /* take memory from buffer and alloc fixed pool */ + nk_pool_init_fixed(&ctx->pool, pool->memory.ptr, pool->memory.size); + } else { + /* create dynamic pool from buffer allocator */ + struct nk_allocator *alloc = &pool->pool; + nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + } + ctx->use_pool = nk_true; + return 1; +} + +NK_API int +nk_init(struct nk_context *ctx, struct nk_allocator *alloc, + const struct nk_user_font *font) +{ + NK_ASSERT(alloc); + if (!alloc) return 0; + nk_setup(ctx, font); + nk_buffer_init(&ctx->memory, alloc, NK_DEFAULT_COMMAND_BUFFER_SIZE); + nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + ctx->use_pool = nk_true; + return 1; +} + +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_set_user_data(struct nk_context *ctx, nk_handle handle) +{ + if (!ctx) return; + ctx->userdata = handle; + if (ctx->current) + ctx->current->buffer.userdata = handle; +} +#endif + +NK_API void +nk_free(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_buffer_free(&ctx->memory); + if (ctx->use_pool) { + nk_pool_free(&ctx->pool); + } + + nk_zero(&ctx->input, sizeof(ctx->input)); + nk_zero(&ctx->style, sizeof(ctx->style)); + nk_zero(&ctx->memory, sizeof(ctx->memory)); + + ctx->seq = 0; + ctx->build = 0; + ctx->begin = 0; + ctx->end = 0; + ctx->active = 0; + ctx->current = 0; + ctx->freelist = 0; + ctx->count = 0; +} + +NK_API void +nk_clear(struct nk_context *ctx) +{ + struct nk_window *iter; + struct nk_window *next; + NK_ASSERT(ctx); + + if (!ctx) return; + if (ctx->use_pool) + nk_buffer_clear(&ctx->memory); + else nk_buffer_reset(&ctx->memory, NK_BUFFER_FRONT); + + ctx->build = 0; + ctx->memory.calls = 0; + ctx->last_widget_state = 0; + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; + NK_MEMSET(&ctx->overlay, 0, sizeof(ctx->overlay)); +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_draw_list_clear(&ctx->draw_list); +#endif + + /* garbage collector */ + iter = ctx->begin; + while (iter) { + /* make sure minimized windows do not get removed */ + if (iter->flags & NK_WINDOW_MINIMIZED) { + iter = iter->next; + continue; + } + + /* free unused popup windows */ + if (iter->popup.win && iter->popup.win->seq != ctx->seq) { + nk_free_window(ctx, iter->popup.win); + iter->popup.win = 0; + } + + /* remove unused window state tables */ + {struct nk_table *n, *it = iter->tables; + while (it) { + n = it->next; + if (it->seq != ctx->seq) { + nk_remove_table(iter, it); + nk_zero(it, sizeof(union nk_page_data)); + nk_free_table(ctx, it); + if (it == iter->tables) + iter->tables = n; + } + it = n; + }} + + /* window itself is not used anymore so free */ + if (iter->seq != ctx->seq || iter->flags & NK_WINDOW_CLOSED) { + next = iter->next; + nk_remove_window(ctx, iter); + nk_free_window(ctx, iter); + iter = next; + } else iter = iter->next; + } + ctx->seq++; +} + +/* ---------------------------------------------------------------- + * + * BUFFERING + * + * ---------------------------------------------------------------*/ +NK_INTERN void +nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) +{ + NK_ASSERT(ctx); + NK_ASSERT(buffer); + if (!ctx || !buffer) return; + buffer->begin = ctx->memory.allocated; + buffer->end = buffer->begin; + buffer->last = buffer->begin; + buffer->clip = nk_null_rect; +} + +NK_INTERN void +nk_start(struct nk_context *ctx, struct nk_window *win) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + nk_start_buffer(ctx, &win->buffer); +} + +NK_INTERN void +nk_start_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_panel *iter; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + /* make sure to use the correct popup buffer*/ + iter = win->layout; + while (iter->parent) + iter = iter->parent; + + /* save buffer fill state for popup */ + buf = &iter->popup_buffer; + buf->begin = win->buffer.end; + buf->end = win->buffer.end; + buf->parent = win->buffer.last; + buf->last = buf->begin; + buf->active = nk_true; +} + +NK_INTERN void +nk_finish_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_panel *iter; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + /* make sure to use the correct popup buffer*/ + iter = win->layout; + while (iter->parent) + iter = iter->parent; + + buf = &iter->popup_buffer; + buf->last = win->buffer.last; + buf->end = win->buffer.end; +} + +NK_INTERN void +nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) +{ + NK_ASSERT(ctx); + NK_ASSERT(buffer); + if (!ctx || !buffer) return; + buffer->end = ctx->memory.allocated; +} + +NK_INTERN void +nk_finish(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_command *parent_last; + struct nk_command *sublast; + struct nk_command *last; + void *memory; + + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + nk_finish_buffer(ctx, &win->buffer); + if (!win->layout->popup_buffer.active) return; + + /* from here on is for popup window buffer handling */ + buf = &win->layout->popup_buffer; + memory = ctx->memory.memory.ptr; + + /* redirect the sub-window buffer to the end of the window command buffer */ + parent_last = nk_ptr_add(struct nk_command, memory, buf->parent); + sublast = nk_ptr_add(struct nk_command, memory, buf->last); + last = nk_ptr_add(struct nk_command, memory, win->buffer.last); + + parent_last->next = buf->end; + sublast->next = last->next; + last->next = buf->begin; + win->buffer.last = buf->last; + win->buffer.end = buf->end; + buf->active = nk_false; +} + +NK_INTERN void +nk_build(struct nk_context *ctx) +{ + struct nk_window *iter; + struct nk_window *next; + struct nk_command *cmd; + nk_byte *buffer; + + /* draw cursor overlay */ + if (!ctx->style.cursor_active) + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; + if (ctx->style.cursor_active && !ctx->input.mouse.grabbed && ctx->style.cursor_visible) { + struct nk_rect mouse_bounds; + const struct nk_cursor *cursor = ctx->style.cursor_active; + nk_command_buffer_init(&ctx->overlay, &ctx->memory, NK_CLIPPING_OFF); + nk_start_buffer(ctx, &ctx->overlay); + + mouse_bounds.x = ctx->input.mouse.pos.x - cursor->offset.x; + mouse_bounds.y = ctx->input.mouse.pos.y - cursor->offset.y; + mouse_bounds.w = cursor->size.x; + mouse_bounds.h = cursor->size.y; + + nk_draw_image(&ctx->overlay, mouse_bounds, &cursor->img, nk_white); + nk_finish_buffer(ctx, &ctx->overlay); + } + + /* build one big draw command list out of all buffers */ + iter = ctx->begin; + buffer = (nk_byte*)ctx->memory.memory.ptr; + while (iter != 0) { + next = iter->next; + if (iter->buffer.last == iter->buffer.begin || (iter->flags & NK_WINDOW_HIDDEN)) { + iter = next; + continue; + } + cmd = nk_ptr_add(struct nk_command, buffer, iter->buffer.last); + while (next && ((next->buffer.last == next->buffer.begin) || + (next->flags & NK_WINDOW_HIDDEN))) + next = next->next; /* skip empty command buffers */ + + if (next) { + cmd->next = next->buffer.begin; + } else { + if (ctx->overlay.end != ctx->overlay.begin) + cmd->next = ctx->overlay.begin; + else cmd->next = ctx->memory.allocated; + } + iter = next; + } +} + +NK_API const struct nk_command* +nk__begin(struct nk_context *ctx) +{ + struct nk_window *iter; + nk_byte *buffer; + NK_ASSERT(ctx); + if (!ctx) return 0; + if (!ctx->count) return 0; + + buffer = (nk_byte*)ctx->memory.memory.ptr; + if (!ctx->build) { + nk_build(ctx); + ctx->build = nk_true; + } + + iter = ctx->begin; + while (iter && ((iter->buffer.begin == iter->buffer.end) || (iter->flags & NK_WINDOW_HIDDEN))) + iter = iter->next; + if (!iter) return 0; + return nk_ptr_add_const(struct nk_command, buffer, iter->buffer.begin); +} + +NK_API const struct nk_command* +nk__next(struct nk_context *ctx, const struct nk_command *cmd) +{ + nk_byte *buffer; + const struct nk_command *next; + NK_ASSERT(ctx); + if (!ctx || !cmd || !ctx->count) return 0; + if (cmd->next >= ctx->memory.allocated) return 0; + buffer = (nk_byte*)ctx->memory.memory.ptr; + next = nk_ptr_add_const(struct nk_command, buffer, cmd->next); + return next; +} + +/* ---------------------------------------------------------------- + * + * PANEL + * + * ---------------------------------------------------------------*/ +static int +nk_panel_has_header(nk_flags flags, const char *title) +{ + /* window header state */ + int active = 0; + active = (flags & (NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE)); + active = active || (flags & NK_WINDOW_TITLE); + active = active && !(flags & NK_WINDOW_HIDDEN) && title; + return active; +} + +NK_INTERN struct nk_vec2 +nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type) +{ + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.padding; + case NK_PANEL_GROUP: return style->window.group_padding; + case NK_PANEL_POPUP: return style->window.popup_padding; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_padding; + case NK_PANEL_COMBO: return style->window.combo_padding; + case NK_PANEL_MENU: return style->window.menu_padding; + case NK_PANEL_TOOLTIP: return style->window.menu_padding; + } +} + +NK_INTERN float +nk_panel_get_border(const struct nk_style *style, nk_flags flags, + enum nk_panel_type type) +{ + if (flags & NK_WINDOW_BORDER) { + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.border; + case NK_PANEL_GROUP: return style->window.group_border; + case NK_PANEL_POPUP: return style->window.popup_border; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_border; + case NK_PANEL_COMBO: return style->window.combo_border; + case NK_PANEL_MENU: return style->window.menu_border; + case NK_PANEL_TOOLTIP: return style->window.menu_border; + }} else return 0; +} + +NK_INTERN struct nk_color +nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type) +{ + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.border_color; + case NK_PANEL_GROUP: return style->window.group_border_color; + case NK_PANEL_POPUP: return style->window.popup_border_color; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_border_color; + case NK_PANEL_COMBO: return style->window.combo_border_color; + case NK_PANEL_MENU: return style->window.menu_border_color; + case NK_PANEL_TOOLTIP: return style->window.menu_border_color; + } +} + +NK_INTERN int +nk_panel_is_sub(enum nk_panel_type type) +{ + return (type & NK_PANEL_SET_SUB)?1:0; +} + +NK_INTERN int +nk_panel_is_nonblock(enum nk_panel_type type) +{ + return (type & NK_PANEL_SET_NONBLOCK)?1:0; +} + +NK_INTERN int +nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type) +{ + struct nk_input *in; + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + const struct nk_style *style; + const struct nk_user_font *font; + + struct nk_vec2 scrollbar_size; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + nk_zero(ctx->current->layout, sizeof(*ctx->current->layout)); + if (ctx->current->flags & NK_WINDOW_HIDDEN) + return 0; + + /* pull state into local stack */ + style = &ctx->style; + font = style->font; + in = &ctx->input; + win = ctx->current; + layout = win->layout; + out = &win->buffer; +#ifdef NK_INCLUDE_COMMAND_USERDATA + win->buffer.userdata = ctx->userdata; +#endif + + /* pull style configuration into local stack */ + scrollbar_size = style->window.scrollbar_size; + panel_padding = nk_panel_get_padding(style, panel_type); + + /* window movement */ + if ((win->flags & NK_WINDOW_MOVABLE) && !(win->flags & NK_WINDOW_ROM)) { + int left_mouse_down; + int left_mouse_click_in_cursor; + + /* calculate draggable window space */ + struct nk_rect header; + header.x = win->bounds.x; + header.y = win->bounds.y; + header.w = win->bounds.w; + if (nk_panel_has_header(win->flags, title)) { + header.h = font->height + 2.0f * style->window.header.padding.y; + header.h += 2.0f * style->window.header.label_padding.y; + } else header.h = panel_padding.y; + + /* window movement by dragging */ + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, header, nk_true); + if (left_mouse_down && left_mouse_click_in_cursor) { + win->bounds.x = win->bounds.x + in->mouse.delta.x; + win->bounds.y = win->bounds.y + in->mouse.delta.y; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y += in->mouse.delta.y; + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_MOVE]; + } + } + + /* setup panel */ + layout->type = panel_type; + layout->flags = win->flags; + layout->bounds = win->bounds; + layout->bounds.x += panel_padding.x; + layout->bounds.w -= 2*panel_padding.x; + if (win->flags & NK_WINDOW_BORDER) { + layout->border = nk_panel_get_border(style, win->flags, panel_type); + layout->bounds = nk_shrink_rect(layout->bounds, layout->border); + } else layout->border = 0; + layout->at_y = layout->bounds.y; + layout->at_x = layout->bounds.x; + layout->max_x = 0; + layout->header_height = 0; + layout->footer_height = 0; + layout->row.index = 0; + layout->row.columns = 0; + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.tree_depth = 0; + layout->row.height = panel_padding.y; + layout->has_scrolling = nk_true; + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR)) + layout->bounds.w -= scrollbar_size.x; + if (!nk_panel_is_nonblock(panel_type)) { + layout->footer_height = 0; + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR) || win->flags & NK_WINDOW_SCALABLE) + layout->footer_height = scrollbar_size.y; + layout->bounds.h -= layout->footer_height; + } + + /* panel header */ + if (nk_panel_has_header(win->flags, title)) + { + struct nk_text text; + struct nk_rect header; + const struct nk_style_item *background = 0; + + /* calculate header bounds */ + header.x = win->bounds.x; + header.y = win->bounds.y; + header.w = win->bounds.w; + header.h = font->height + 2.0f * style->window.header.padding.y; + header.h += (2.0f * style->window.header.label_padding.y); + + /* shrink panel by header */ + layout->header_height = header.h; + layout->bounds.y += header.h; + layout->bounds.h -= header.h; + layout->at_y += header.h; + + /* select correct header background and text color */ + if (ctx->active == win) { + background = &style->window.header.active; + text.text = style->window.header.label_active; + } else if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) { + background = &style->window.header.hover; + text.text = style->window.header.label_hover; + } else { + background = &style->window.header.normal; + text.text = style->window.header.label_normal; + } + + /* draw header background */ + header.h += 1.0f; + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, background->data.color); + } + + /* window close button */ + {struct nk_rect button; + button.y = header.y + style->window.header.padding.y; + button.h = header.h - 2 * style->window.header.padding.y; + button.w = button.h; + if (win->flags & NK_WINDOW_CLOSABLE) { + nk_flags ws = 0; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - (button.w + style->window.header.padding.x); + header.w -= button.w + style->window.header.spacing.x + style->window.header.padding.x; + } else { + button.x = header.x + style->window.header.padding.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + + if (nk_do_button_symbol(&ws, &win->buffer, button, + style->window.header.close_symbol, NK_BUTTON_DEFAULT, + &style->window.header.close_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) + { + layout->flags |= NK_WINDOW_HIDDEN; + layout->flags |= NK_WINDOW_CLOSED; + } + } + + /* window minimize button */ + if (win->flags & NK_WINDOW_MINIMIZABLE) { + nk_flags ws = 0; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - button.w; + if (!(win->flags & NK_WINDOW_CLOSABLE)) { + button.x -= style->window.header.padding.x; + header.w -= style->window.header.padding.x; + } + header.w -= button.w + style->window.header.spacing.x; + } else { + button.x = header.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + if (nk_do_button_symbol(&ws, &win->buffer, button, (layout->flags & NK_WINDOW_MINIMIZED)? + style->window.header.maximize_symbol: style->window.header.minimize_symbol, + NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) + layout->flags = (layout->flags & NK_WINDOW_MINIMIZED) ? + layout->flags & (nk_flags)~NK_WINDOW_MINIMIZED: + layout->flags | NK_WINDOW_MINIMIZED; + }} + + {/* window header title */ + int text_len = nk_strlen(title); + struct nk_rect label = {0,0,0,0}; + float t = font->width(font->userdata, font->height, title, text_len); + text.padding = nk_vec2(0,0); + + label.x = header.x + style->window.header.padding.x; + label.x += style->window.header.label_padding.x; + label.y = header.y + style->window.header.label_padding.y; + label.h = font->height + 2 * style->window.header.label_padding.y; + label.w = t + 2 * style->window.header.spacing.x; + nk_widget_text(out, label,(const char*)title, text_len, &text, NK_TEXT_LEFT, font);} + } + + /* draw window background */ + if (!(layout->flags & NK_WINDOW_MINIMIZED) && !(layout->flags & NK_WINDOW_DYNAMIC)) { + struct nk_rect body; + body.x = win->bounds.x; + body.w = win->bounds.w; + body.y = (win->bounds.y + layout->header_height); + body.h = (win->bounds.h - layout->header_height); + if (style->window.fixed_background.type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, body, &style->window.fixed_background.data.image, nk_white); + else nk_fill_rect(out, body, 0, style->window.fixed_background.data.color); + } + + /* set clipping rectangle */ + {struct nk_rect clip; + layout->clip = layout->bounds; + nk_unify(&clip, &win->buffer.clip, layout->clip.x, layout->clip.y, + layout->clip.x + layout->clip.w, layout->clip.y + layout->clip.h); + nk_push_scissor(out, clip); + layout->clip = clip;} + return !(layout->flags & NK_WINDOW_HIDDEN) && !(layout->flags & NK_WINDOW_MINIMIZED); +} + +NK_INTERN void +nk_panel_end(struct nk_context *ctx) +{ + struct nk_input *in; + struct nk_window *window; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 scrollbar_size; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + window = ctx->current; + layout = window->layout; + style = &ctx->style; + out = &window->buffer; + in = (layout->flags & NK_WINDOW_ROM) ? 0 :&ctx->input; + if (!nk_panel_is_sub(layout->type)) + nk_push_scissor(out, nk_null_rect); + + /* cache configuration data */ + scrollbar_size = style->window.scrollbar_size; + panel_padding = nk_panel_get_padding(style, layout->type); + + /* update the current cursor Y-position to point over the last added widget */ + layout->at_y += layout->row.height; + + /* dynamic panels */ + if (layout->flags & NK_WINDOW_DYNAMIC && !(layout->flags & NK_WINDOW_MINIMIZED)) + { + /* update panel height to fit dynamic growth */ + struct nk_rect empty_space; + if (layout->at_y < (layout->bounds.y + layout->bounds.h)) + layout->bounds.h = layout->at_y - layout->bounds.y; + + /* fill top empty space */ + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y; + empty_space.h = panel_padding.y; + empty_space.w = window->bounds.w; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill left empty space */ + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y; + empty_space.w = panel_padding.x + layout->border; + empty_space.h = layout->bounds.h; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill right empty space */ + empty_space.x = layout->bounds.x + layout->bounds.w - layout->border; + empty_space.y = layout->bounds.y; + empty_space.w = panel_padding.x + layout->border; + empty_space.h = layout->bounds.h; + if (layout->offset->y == 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR)) + empty_space.w += scrollbar_size.x; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill bottom empty space */ + if (layout->offset->x != 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR)) { + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y + layout->bounds.h; + empty_space.w = window->bounds.w; + empty_space.h = scrollbar_size.y; + nk_fill_rect(out, empty_space, 0, style->window.background); + } + } + + /* scrollbars */ + if (!(layout->flags & NK_WINDOW_NO_SCROLLBAR) && + !(layout->flags & NK_WINDOW_MINIMIZED) && + window->scrollbar_hiding_timer < NK_SCROLLBAR_HIDING_TIMEOUT) + { + struct nk_rect scroll; + int scroll_has_scrolling; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + { + /* vertical scrollbar */ + nk_flags state = 0; + scroll.x = layout->bounds.x + layout->bounds.w + panel_padding.x; + scroll.y = layout->bounds.y; + scroll.w = scrollbar_size.x; + scroll.h = layout->bounds.h; + + scroll_offset = layout->offset->y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = (float)(int)(layout->at_y - scroll.y); + + /* scrolling by mouse wheel */ + if (nk_panel_is_sub(layout->type)) + { + /* sub-window scrollbar wheel scrolling */ + struct nk_window *root_window = window; + struct nk_panel *root_panel = window->layout; + while (root_panel->parent) + root_panel = root_panel->parent; + while (root_window->parent) + root_window = root_window->parent; + + /* only allow scrolling if parent window is active */ + scroll_has_scrolling = 0; + if ((root_window == ctx->active) && layout->has_scrolling) { + /* and panel is being hovered and inside clip rect*/ + if (nk_input_is_mouse_hovering_rect(in, layout->bounds) && + NK_INTERSECT(layout->bounds.x, layout->bounds.y, layout->bounds.w, layout->bounds.h, + root_panel->clip.x, root_panel->clip.y, root_panel->clip.w, root_panel->clip.h)) + { + /* deactivate all parent scrolling */ + root_panel = window->layout; + while (root_panel->parent) { + root_panel->has_scrolling = nk_false; + root_panel = root_panel->parent; + } + root_panel->has_scrolling = nk_false; + scroll_has_scrolling = nk_true; + } + } + } else if (!nk_panel_is_sub(layout->type)) { + /* window scrollbar wheel scrolling */ + scroll_has_scrolling = (window == ctx->active) && layout->has_scrolling; + if (in && in->mouse.scroll_delta > 0 && scroll_has_scrolling) + window->scrolled = nk_true; + else window->scrolled = nk_false; + } else scroll_has_scrolling = nk_false; + + /* execute scrollbar */ + scroll_offset = nk_do_scrollbarv(&state, out, scroll, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollv, in, style->font); + layout->offset->y = (unsigned short)scroll_offset; + if (in && scroll_has_scrolling) + in->mouse.scroll_delta = 0; + } + { + /* horizontal scrollbar */ + nk_flags state = 0; + scroll.x = layout->bounds.x; + scroll.y = layout->bounds.y + layout->bounds.h; + scroll.w = layout->bounds.w; + scroll.h = scrollbar_size.y; + + scroll_offset = layout->offset->x; + scroll_target = (float)(int)(layout->max_x - scroll.x); + scroll_step = layout->max_x * 0.05f; + scroll_inc = layout->max_x * 0.005f; + scroll_has_scrolling = nk_false; + scroll_offset = nk_do_scrollbarh(&state, out, scroll, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollh, in, style->font); + layout->offset->x = (unsigned short)scroll_offset; + } + } + + /* hide scroll if no user input */ + if (window->flags & NK_WINDOW_SCROLL_AUTO_HIDE) { + int has_input = ctx->input.mouse.delta.x != 0 || ctx->input.mouse.delta.y != 0 || ctx->input.mouse.scroll_delta != 0; + int is_window_hovered = nk_window_is_hovered(ctx); + int any_item_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); + if ((!has_input && is_window_hovered) || (!is_window_hovered && !any_item_active)) + window->scrollbar_hiding_timer += ctx->delta_time_seconds; + else window->scrollbar_hiding_timer = 0; + } else window->scrollbar_hiding_timer = 0; + + /* window border */ + if (layout->flags & NK_WINDOW_BORDER) + { + struct nk_color border_color = nk_panel_get_border_color(style, layout->type); + const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) ? + style->window.border + window->bounds.y + layout->header_height: + (layout->flags & NK_WINDOW_DYNAMIC)? + layout->bounds.y + layout->bounds.h + layout->footer_height: + window->bounds.y + window->bounds.h; + + /* draw border top */ + nk_stroke_line(out,window->bounds.x,window->bounds.y, + window->bounds.x + window->bounds.w, window->bounds.y, + layout->border, border_color); + + /* draw bottom border */ + nk_stroke_line(out, window->bounds.x, padding_y, + window->bounds.x + window->bounds.w, padding_y, layout->border, border_color); + + /* draw left border */ + nk_stroke_line(out, window->bounds.x + layout->border*0.5f, + window->bounds.y, window->bounds.x + layout->border*0.5f, + padding_y, layout->border, border_color); + + /* draw right border */ + nk_stroke_line(out, window->bounds.x + window->bounds.w, + window->bounds.y, window->bounds.x + window->bounds.w, + padding_y, layout->border, border_color); + } + + /* scaler */ + if ((layout->flags & NK_WINDOW_SCALABLE) && in && !(layout->flags & NK_WINDOW_MINIMIZED)) + { + /* calculate scaler bounds */ + struct nk_rect scaler; + scaler.w = scrollbar_size.x; + scaler.h = scrollbar_size.y; + scaler.y = layout->bounds.y + layout->bounds.h; + scaler.x = layout->bounds.x + layout->bounds.w + panel_padding.x; + if (layout->flags & NK_WINDOW_NO_SCROLLBAR) + scaler.x -= scaler.w; + + /* draw scaler */ + {const struct nk_style_item *item = &style->window.scaler; + if (item->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, scaler, &item->data.image, nk_white); + else nk_fill_triangle(out, scaler.x + scaler.w, scaler.y, scaler.x + scaler.w, + scaler.y + scaler.h, scaler.x, scaler.y + scaler.h, item->data.color); + } + + /* do window scaling */ + if (!(window->flags & NK_WINDOW_ROM)) { + struct nk_vec2 window_size = style->window.min_size; + int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_scaler = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, scaler, nk_true); + + if (nk_input_is_mouse_down(in, NK_BUTTON_LEFT) && left_mouse_down && left_mouse_click_in_scaler) { + window->bounds.w = NK_MAX(window_size.x, window->bounds.w + in->mouse.delta.x); + /* dragging in y-direction is only possible if static window */ + if (!(layout->flags & NK_WINDOW_DYNAMIC)) + window->bounds.h = NK_MAX(window_size.y, window->bounds.h + in->mouse.delta.y); + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT]; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = scaler.x + in->mouse.delta.x + scaler.w/2.0f; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = scaler.y + in->mouse.delta.y + scaler.h/2.0f; + } + } + } + + if (!nk_panel_is_sub(layout->type)) { + /* window is hidden so clear command buffer */ + if (layout->flags & NK_WINDOW_HIDDEN) + nk_command_buffer_reset(&window->buffer); + /* window is visible and not tab */ + else nk_finish(ctx, window); + } + + /* NK_WINDOW_REMOVE_ROM flag was set so remove NK_WINDOW_ROM */ + if (layout->flags & NK_WINDOW_REMOVE_ROM) { + layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + layout->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + } + window->flags = layout->flags; + + /* property garbage collector */ + if (window->property.active && window->property.old != window->property.seq && + window->property.active == window->property.prev) { + nk_zero(&window->property, sizeof(window->property)); + } else { + window->property.old = window->property.seq; + window->property.prev = window->property.active; + window->property.seq = 0; + } + + /* edit garbage collector */ + if (window->edit.active && window->edit.old != window->edit.seq && + window->edit.active == window->edit.prev) { + nk_zero(&window->edit, sizeof(window->edit)); + } else { + window->edit.old = window->edit.seq; + window->edit.prev = window->edit.active; + window->edit.seq = 0; + } + + /* contextual garbage collector */ + if (window->popup.active_con && window->popup.con_old != window->popup.con_count) { + window->popup.con_count = 0; + window->popup.con_old = 0; + window->popup.active_con = 0; + } else { + window->popup.con_old = window->popup.con_count; + window->popup.con_count = 0; + } + window->popup.combo_count = 0; + + /* helper to make sure you have a 'nk_tree_push' * for every 'nk_tree_pop' */ + NK_ASSERT(!layout->row.tree_depth); +} + + +/* ---------------------------------------------------------------- + * + * PAGE ELEMENT + * + * ---------------------------------------------------------------*/ +NK_INTERN struct nk_page_element* +nk_create_page_element(struct nk_context *ctx) +{ + struct nk_page_element *elem; + if (ctx->freelist) { + /* unlink page element from free list */ + elem = ctx->freelist; + ctx->freelist = elem->next; + } else if (ctx->use_pool) { + /* allocate page element from memory pool */ + elem = nk_pool_alloc(&ctx->pool); + NK_ASSERT(elem); + if (!elem) return 0; + } else { + /* allocate new page element from the back of the fixed size memory buffer */ + NK_STORAGE const nk_size size = sizeof(struct nk_page_element); + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_page_element); + elem = (struct nk_page_element*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align); + NK_ASSERT(elem); + if (!elem) return 0; + } + nk_zero_struct(*elem); + elem->next = 0; + elem->prev = 0; + return elem; +} + +NK_INTERN void +nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem) +{ + /* link table into freelist */ + if (!ctx->freelist) { + ctx->freelist = elem; + } else { + elem->next = ctx->freelist; + ctx->freelist = elem; + } +} + +/* ---------------------------------------------------------------- + * + * TABLES + * + * ---------------------------------------------------------------*/ +NK_INTERN struct nk_table* +nk_create_table(struct nk_context *ctx) +{ + struct nk_page_element *elem = nk_create_page_element(ctx); + if (!elem) return 0; + nk_zero_struct(*elem); + return &elem->data.tbl; +} + +NK_INTERN void +nk_free_table(struct nk_context *ctx, struct nk_table *tbl) +{ + union nk_page_data *pd = NK_CONTAINER_OF(tbl, union nk_page_data, tbl); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe); +} + +NK_INTERN void +nk_push_table(struct nk_window *win, struct nk_table *tbl) +{ + if (!win->tables) { + win->tables = tbl; + tbl->next = 0; + tbl->prev = 0; + win->table_count = 1; + win->table_size = 0; + return; + } + win->tables->prev = tbl; + tbl->next = win->tables; + tbl->prev = 0; + win->tables = tbl; + win->table_count++; + win->table_size = 0; +} + +NK_INTERN void +nk_remove_table(struct nk_window *win, struct nk_table *tbl) +{ + if (win->tables == tbl) + win->tables = tbl->next; + if (tbl->next) + tbl->next->prev = tbl->prev; + if (tbl->prev) + tbl->prev->next = tbl->next; + tbl->next = 0; + tbl->prev = 0; +} + +NK_INTERN nk_uint* +nk_add_value(struct nk_context *ctx, struct nk_window *win, + nk_hash name, nk_uint value) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return 0; + if (!win->tables || win->table_size >= NK_VALUE_PAGE_CAPACITY) { + struct nk_table *tbl = nk_create_table(ctx); + NK_ASSERT(tbl); + if (!tbl) return 0; + nk_push_table(win, tbl); + } + win->tables->seq = win->seq; + win->tables->keys[win->table_size] = name; + win->tables->values[win->table_size] = value; + return &win->tables->values[win->table_size++]; +} + +NK_INTERN nk_uint* +nk_find_value(struct nk_window *win, nk_hash name) +{ + unsigned short size = win->table_size; + struct nk_table *iter = win->tables; + while (iter) { + unsigned short i = 0; + for (i = 0; i < size; ++i) { + if (iter->keys[i] == name) { + iter->seq = win->seq; + return &iter->values[i]; + } + } + size = NK_VALUE_PAGE_CAPACITY; + iter = iter->next; + } + return 0; +} + +/* ---------------------------------------------------------------- + * + * WINDOW + * + * ---------------------------------------------------------------*/ +NK_INTERN void* +nk_create_window(struct nk_context *ctx) +{ + struct nk_page_element *elem = nk_create_page_element(ctx); + if (!elem) return 0; + elem->data.win.seq = ctx->seq; + return &elem->data.win; +} + +NK_INTERN void +nk_free_window(struct nk_context *ctx, struct nk_window *win) +{ + /* unlink windows from list */ + struct nk_table *n, *it = win->tables; + if (win->popup.win) { + nk_free_window(ctx, win->popup.win); + win->popup.win = 0; + } + win->next = 0; + win->prev = 0; + + while (it) { + /*free window state tables */ + n = it->next; + nk_remove_table(win, it); + nk_free_table(ctx, it); + if (it == win->tables) + win->tables = n; + it = n; + } + + /* link windows into freelist */ + {union nk_page_data *pd = NK_CONTAINER_OF(win, union nk_page_data, win); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe);} +} + +NK_INTERN struct nk_window* +nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name) +{ + struct nk_window *iter; + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + if (iter->name == hash) { + int max_len = nk_strlen(iter->name_string); + if (!nk_stricmpn(iter->name_string, name, max_len)) + return iter; + } + iter = iter->next; + } + return 0; +} + +enum nk_window_insert_location { + NK_INSERT_BACK, /* inserts window into the back of list (front of screen) */ + NK_INSERT_FRONT /* inserts window into the front of list (back of screen) */ +}; +NK_INTERN void +nk_insert_window(struct nk_context *ctx, struct nk_window *win, + enum nk_window_insert_location loc) +{ + const struct nk_window *iter; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return; + + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + NK_ASSERT(iter != win); + if (iter == win) return; + iter = iter->next; + } + + if (!ctx->begin) { + win->next = 0; + win->prev = 0; + ctx->begin = win; + ctx->end = win; + ctx->count = 1; + return; + } + if (loc == NK_INSERT_BACK) { + struct nk_window *end; + end = ctx->end; + end->flags |= NK_WINDOW_ROM; + end->next = win; + win->prev = ctx->end; + win->next = 0; + ctx->end = win; + ctx->active = ctx->end; + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; + } else { + ctx->end->flags |= NK_WINDOW_ROM; + ctx->begin->prev = win; + win->next = ctx->begin; + win->prev = 0; + ctx->begin = win; + ctx->begin->flags &= ~(nk_flags)NK_WINDOW_ROM; + } + ctx->count++; +} + +NK_INTERN void +nk_remove_window(struct nk_context *ctx, struct nk_window *win) +{ + if (win == ctx->begin || win == ctx->end) { + if (win == ctx->begin) { + ctx->begin = win->next; + if (win->next) + win->next->prev = 0; + } + if (win == ctx->end) { + ctx->end = win->prev; + if (win->prev) + win->prev->next = 0; + } + } else { + if (win->next) + win->next->prev = win->prev; + if (win->prev) + win->prev->next = win->next; + } + if (win == ctx->active || !ctx->active) { + ctx->active = ctx->end; + if (ctx->end) + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; + } + win->next = 0; + win->prev = 0; + ctx->count--; +} + +NK_API int +nk_begin(struct nk_context *ctx, struct nk_panel *layout, const char *title, + struct nk_rect bounds, nk_flags flags) +{ + return nk_begin_titled(ctx, layout, title, title, bounds, flags); +} + +NK_API int +nk_begin_titled(struct nk_context *ctx, struct nk_panel *layout, + const char *name, const char *title, struct nk_rect bounds, nk_flags flags) +{ + struct nk_window *win; + struct nk_style *style; + nk_hash title_hash; + int title_len; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(title); + NK_ASSERT(ctx->style.font && ctx->style.font->width && "if this triggers you forgot to add a font"); + NK_ASSERT(!ctx->current && "if this triggers you missed a `nk_end` call"); + if (!ctx || ctx->current || !title || !name) + return 0; + + /* find or create window */ + style = &ctx->style; + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) { + /* create new window */ + nk_size name_length = (nk_size)nk_strlen(name); + win = (struct nk_window*)nk_create_window(ctx); + NK_ASSERT(win); + if (!win) return 0; + + if (flags & NK_WINDOW_BACKGROUND) + nk_insert_window(ctx, win, NK_INSERT_FRONT); + else nk_insert_window(ctx, win, NK_INSERT_BACK); + nk_command_buffer_init(&win->buffer, &ctx->memory, NK_CLIPPING_ON); + + win->flags = flags; + win->bounds = bounds; + win->name = title_hash; + name_length = NK_MIN(name_length, NK_WINDOW_MAX_NAME-1); + NK_MEMCPY(win->name_string, name, name_length); + win->name_string[name_length] = 0; + win->popup.win = 0; + if (!ctx->active) + ctx->active = win; + } else { + /* update window */ + win->flags &= ~(nk_flags)(NK_WINDOW_PRIVATE-1); + win->flags |= flags; + NK_ASSERT(win->seq != ctx->seq && "if this triggers you probably have two windows with same name!"); + win->seq = ctx->seq; + if (!ctx->active) + ctx->active = win; + } + if (win->flags & NK_WINDOW_HIDDEN) { + ctx->current = win; + return 0; + } + + /* window overlapping */ + if (!(win->flags & NK_WINDOW_HIDDEN)) + { + int inpanel, ishovered; + const struct nk_window *iter = win; + float h = ctx->style.font->height + 2.0f * style->window.header.padding.y + + (2.0f * style->window.header.label_padding.y); + struct nk_rect win_bounds = (!(win->flags & NK_WINDOW_MINIMIZED))? + win->bounds: nk_rect(win->bounds.x, win->bounds.y, win->bounds.w, h); + + /* activate window if hovered and no other window is overlapping this window */ + nk_start(ctx, win); + inpanel = nk_input_has_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_LEFT, win_bounds, nk_true); + inpanel = inpanel && ctx->input.mouse.buttons[NK_BUTTON_LEFT].clicked; + ishovered = nk_input_is_mouse_hovering_rect(&ctx->input, win_bounds); + if ((win != ctx->active) && ishovered && !ctx->input.mouse.buttons[NK_BUTTON_LEFT].down) { + iter = win->next; + while (iter) { + struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? + iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); + if (NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && + (!(iter->flags & NK_WINDOW_HIDDEN) || !(iter->flags & NK_WINDOW_BACKGROUND))) + break; + + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win->bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + + /* activate window if clicked */ + if (iter && inpanel && (win != ctx->end) && !(iter->flags & NK_WINDOW_BACKGROUND)) { + iter = win->next; + while (iter) { + /* try to find a panel with higher priority in the same position */ + struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? + iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); + if (NK_INBOX(ctx->input.mouse.pos.x, ctx->input.mouse.pos.y, + iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && + !(iter->flags & NK_WINDOW_HIDDEN)) + break; + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + + if (!iter && ctx->end != win) { + if (!(win->flags & NK_WINDOW_BACKGROUND)) { + /* current window is active in that position so transfer to top + * at the highest priority in stack */ + nk_remove_window(ctx, win); + nk_insert_window(ctx, win, NK_INSERT_BACK); + } + win->flags &= ~(nk_flags)NK_WINDOW_ROM; + ctx->active = win; + } + if (ctx->end != win && !(win->flags & NK_WINDOW_BACKGROUND)) + win->flags |= NK_WINDOW_ROM; + } + + win->layout = layout; + ctx->current = win; + ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW); + layout->offset = &win->scrollbar; + return ret; +} + +NK_API void +nk_end(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current && "if this triggers you forgot to call `nk_begin`"); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return; + if (ctx->current->flags & NK_WINDOW_HIDDEN) { + ctx->current = 0; + return; + } + nk_panel_end(ctx); + ctx->current = 0; +} + +NK_API struct nk_rect +nk_window_get_bounds(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->bounds; +} + +NK_API struct nk_vec2 +nk_window_get_position(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y); +} + +NK_API struct nk_vec2 +nk_window_get_size(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h); +} + +NK_API float +nk_window_get_width(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.w; +} + +NK_API float +nk_window_get_height(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.h; +} + +NK_API struct nk_rect +nk_window_get_content_region(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->layout->clip; +} + +NK_API struct nk_vec2 +nk_window_get_content_region_min(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y); +} + +NK_API struct nk_vec2 +nk_window_get_content_region_max(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w, + ctx->current->layout->clip.y + ctx->current->layout->clip.h); +} + +NK_API struct nk_vec2 +nk_window_get_content_region_size(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h); +} + +NK_API struct nk_command_buffer* +nk_window_get_canvas(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return &ctx->current->buffer; +} + +NK_API struct nk_panel* +nk_window_get_panel(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->layout; +} + +NK_API int +nk_window_has_focus(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return ctx->current == ctx->active; +} + +NK_API int +nk_window_is_hovered(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return nk_input_is_mouse_hovering_rect(&ctx->input, ctx->current->bounds); +} + +NK_API int +nk_window_is_any_hovered(struct nk_context *ctx) +{ + struct nk_window *iter; + NK_ASSERT(ctx); + if (!ctx) return 0; + iter = ctx->begin; + while (iter) { + /* check if window is being hovered */ + if (iter->flags & NK_WINDOW_MINIMIZED) { + struct nk_rect header = iter->bounds; + header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y; + if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) + return 1; + } else if (nk_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) { + return 1; + } + /* check if window popup is being hovered */ + if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds)) + return 1; + iter = iter->next; + } + return 0; +} + +NK_API int +nk_item_is_any_active(struct nk_context *ctx) +{ + int any_hovered = nk_window_is_any_hovered(ctx); + int any_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); + return any_hovered || any_active; +} + +NK_API int +nk_window_is_collapsed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 0; + return win->flags & NK_WINDOW_MINIMIZED; +} + +NK_API int +nk_window_is_closed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 1; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 1; + return (win->flags & NK_WINDOW_CLOSED); +} + +NK_API int +nk_window_is_hidden(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 1; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 1; + return (win->flags & NK_WINDOW_HIDDEN); +} + +NK_API int +nk_window_is_active(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 0; + return win == ctx->active; +} + +NK_API struct nk_window* +nk_window_find(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + return nk_find_window(ctx, title_hash, name); +} + +NK_API void +nk_window_close(struct nk_context *ctx, const char *name) +{ + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + win = nk_window_find(ctx, name); + if (!win) return; + NK_ASSERT(ctx->current != win && "You cannot close a currently active window"); + if (ctx->current == win) return; + win->flags |= NK_WINDOW_HIDDEN; + win->flags |= NK_WINDOW_CLOSED; +} + +NK_API void +nk_window_set_bounds(struct nk_context *ctx, struct nk_rect bounds) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds = bounds; +} + +NK_API void +nk_window_set_position(struct nk_context *ctx, struct nk_vec2 pos) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds.x = pos.x; + ctx->current->bounds.y = pos.y; +} + +NK_API void +nk_window_set_size(struct nk_context *ctx, struct nk_vec2 size) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds.w = size.x; + ctx->current->bounds.h = size.y; +} + +NK_API void +nk_window_collapse(struct nk_context *ctx, const char *name, + enum nk_collapse_states c) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return; + if (c == NK_MINIMIZED) + win->flags |= NK_WINDOW_MINIMIZED; + else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED; +} + +NK_API void +nk_window_collapse_if(struct nk_context *ctx, const char *name, + enum nk_collapse_states c, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_collapse(ctx, name, c); +} + +NK_API void +nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return; + if (s == NK_HIDDEN) + win->flags |= NK_WINDOW_HIDDEN; + else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; +} + +NK_API void +nk_window_show_if(struct nk_context *ctx, const char *name, + enum nk_show_states s, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_show(ctx, name, s); +} + +NK_API void +nk_window_set_focus(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (win && ctx->end != win) { + nk_remove_window(ctx, win); + nk_insert_window(ctx, win, NK_INSERT_BACK); + } + ctx->active = win; +} + +/*---------------------------------------------------------------- + * + * PANEL + * + * --------------------------------------------------------------*/ +NK_API void +nk_menubar_begin(struct nk_context *ctx) +{ + struct nk_panel *layout; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + layout = ctx->current->layout; + NK_ASSERT(layout->at_y == layout->bounds.y); + /* if this assert triggers you allocated space between nk_begin and nk_menubar_begin. + If you want a menubar the first nuklear function after `nk_begin` has to be a + `nk_menubar_begin` call. Inside the menubar you then have to allocate space for + widgets (also supports multiple rows). + Example: + if (nk_begin(...)) { + nk_menubar_begin(...); + nk_layout_xxxx(...); + nk_button(...); + nk_layout_xxxx(...); + nk_button(...); + nk_menubar_end(...); + } + nk_end(...); + */ + if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + layout->menu.x = layout->at_x; + layout->menu.y = layout->at_y + layout->row.height; + layout->menu.w = layout->bounds.w; + layout->menu.offset = *layout->offset; + layout->offset->y = 0; +} + +NK_API void +nk_menubar_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + out = &win->buffer; + layout = win->layout; + if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + layout->menu.h = layout->at_y - layout->menu.y; + layout->bounds.y += layout->menu.h + ctx->style.window.spacing.y + layout->row.height; + layout->bounds.h -= layout->menu.h + ctx->style.window.spacing.y + layout->row.height; + + *layout->offset = layout->menu.offset; + layout->at_y = layout->bounds.y - layout->row.height; + + layout->clip.y = layout->bounds.y; + layout->clip.h = layout->bounds.h; + nk_push_scissor(out, layout->clip); +} +/* ------------------------------------------------------------- + * + * LAYOUT + * + * --------------------------------------------------------------*/ +#define NK_LAYOUT_DYNAMIC_FIXED 0 +#define NK_LAYOUT_DYNAMIC_ROW 1 +#define NK_LAYOUT_DYNAMIC_FREE 2 +#define NK_LAYOUT_DYNAMIC 3 +#define NK_LAYOUT_STATIC_FIXED 4 +#define NK_LAYOUT_STATIC_ROW 5 +#define NK_LAYOUT_STATIC_FREE 6 +#define NK_LAYOUT_STATIC 7 + +NK_INTERN void +nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, + float height, int cols) +{ + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 item_spacing; + struct nk_color color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* prefetch some configuration data */ + layout = win->layout; + style = &ctx->style; + out = &win->buffer; + color = style->window.background; + item_spacing = style->window.spacing; + + /* if one of these triggers you forgot to add an `if` condition around either + a window, group, popup, combobox or contextual menu `begin` and `end` block. + Example: + if (nk_begin(...) {...} nk_end(...); or + if (nk_group_begin(...) { nk_group_end(...);} */ + NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); + NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); + NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); + + /* update the current row and set the current row layout */ + layout->row.index = 0; + layout->at_y += layout->row.height; + layout->row.columns = cols; + layout->row.height = height + item_spacing.y; + layout->row.item_offset = 0; + if (layout->flags & NK_WINDOW_DYNAMIC) { + /* draw background for dynamic panels */ + struct nk_rect background; + background.x = win->bounds.x; + background.w = win->bounds.w; + background.y = layout->at_y - 1.0f; + background.h = layout->row.height + 1.0f; + nk_fill_rect(out, background, 0, color); + } +} + +NK_INTERN void +nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, int width) +{ + /* update the current row and set the current row layout */ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) + win->layout->row.type = NK_LAYOUT_DYNAMIC_FIXED; + else win->layout->row.type = NK_LAYOUT_STATIC_FIXED; + + win->layout->row.ratio = 0; + win->layout->row.filled = 0; + win->layout->row.item_offset = 0; + win->layout->row.item_width = (float)width; +} + +NK_API float +nk_layout_ratio_from_pixel(struct nk_context *ctx, float pixel_width) +{ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(pixel_width); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + win = ctx->current; + return NK_CLAMP(0.0f, pixel_width/win->bounds.x, 1.0f); +} + +NK_API void +nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols) +{ + nk_row_layout(ctx, NK_DYNAMIC, height, cols, 0); +} + +NK_API void +nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols) +{ + nk_row_layout(ctx, NK_STATIC, height, cols, item_width); +} + +NK_API void +nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float row_height, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + + nk_panel_layout(ctx, win, row_height, cols); + if (fmt == NK_DYNAMIC) + layout->row.type = NK_LAYOUT_DYNAMIC_ROW; + else layout->row.type = NK_LAYOUT_STATIC_ROW; + + layout->row.ratio = 0; + layout->row.filled = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; + layout->row.columns = cols; +} + +NK_API void +nk_layout_row_push(struct nk_context *ctx, float ratio_or_width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + + if (layout->row.type == NK_LAYOUT_DYNAMIC_ROW) { + float ratio = ratio_or_width; + if ((ratio + layout->row.filled) > 1.0f) return; + if (ratio > 0.0f) + layout->row.item_width = NK_SATURATE(ratio); + else layout->row.item_width = 1.0f - layout->row.filled; + } else layout->row.item_width = ratio_or_width; +} + +NK_API void +nk_layout_row_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item_width = 0; + layout->row.item_offset = 0; +} + +NK_API void +nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, const float *ratio) +{ + int i; + int n_undef = 0; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) { + /* calculate width of undefined widget ratios */ + float r = 0; + layout->row.ratio = ratio; + for (i = 0; i < cols; ++i) { + if (ratio[i] < 0.0f) + n_undef++; + else r += ratio[i]; + } + r = NK_SATURATE(1.0f - r); + layout->row.type = NK_LAYOUT_DYNAMIC; + layout->row.item_width = (r > 0 && n_undef > 0) ? (r / (float)n_undef):0; + } else { + layout->row.ratio = ratio; + layout->row.type = NK_LAYOUT_STATIC; + layout->row.item_width = 0; + layout->row.item_offset = 0; + } + layout->row.item_offset = 0; + layout->row.filled = 0; +} + +NK_API void +nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int widget_count) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, widget_count); + if (fmt == NK_STATIC) + layout->row.type = NK_LAYOUT_STATIC_FREE; + else layout->row.type = NK_LAYOUT_DYNAMIC_FREE; + + layout->row.ratio = 0; + layout->row.filled = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; +} + +NK_API void +nk_layout_space_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item_width = 0; + layout->row.item_height = 0; + layout->row.item_offset = 0; + nk_zero(&layout->row.item, sizeof(layout->row.item)); +} + +NK_API void +nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item = rect; +} + +NK_API struct nk_rect +nk_layout_space_bounds(struct nk_context *ctx) +{ + struct nk_rect ret; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x = layout->clip.x; + ret.y = layout->clip.y; + ret.w = layout->clip.w; + ret.h = layout->row.height; + return ret; +} + +NK_API struct nk_vec2 +nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - layout->offset->x; + ret.y += layout->at_y - layout->offset->y; + return ret; +} + +NK_API struct nk_vec2 +nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + layout->offset->x; + ret.y += -layout->at_y + layout->offset->y; + return ret; +} + +NK_API struct nk_rect +nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - layout->offset->x; + ret.y += layout->at_y - layout->offset->y; + return ret; +} + +NK_API struct nk_rect +nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + layout->offset->x; + ret.y += -layout->at_y + layout->offset->y; + return ret; +} + +NK_INTERN void +nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win) +{ + struct nk_panel *layout = win->layout; + struct nk_vec2 spacing = ctx->style.window.spacing; + const float row_height = layout->row.height - spacing.y; + nk_panel_layout(ctx, win, row_height, layout->row.columns); +} + +NK_INTERN void +nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, + struct nk_window *win, int modify) +{ + struct nk_panel *layout; + const struct nk_style *style; + + float item_offset = 0; + float item_width = 0; + float item_spacing = 0; + + float panel_padding; + float panel_spacing; + float panel_space; + + struct nk_vec2 spacing; + struct nk_vec2 padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + NK_ASSERT(bounds); + + /* cache some configuration data */ + spacing = ctx->style.window.spacing; + padding = nk_panel_get_padding(style, layout->type); + + /* calculate the usable panel space */ + panel_padding = 2 * padding.x; + panel_spacing = (float)(layout->row.columns - 1) * spacing.x; + panel_space = layout->bounds.w - panel_padding - panel_spacing; + + /* calculate the width of one item inside the current layout space */ + switch (layout->row.type) { + case NK_LAYOUT_DYNAMIC_FIXED: { + /* scaling fixed size widgets item width */ + item_width = panel_space / (float)layout->row.columns; + item_offset = (float)layout->row.index * item_width; + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_DYNAMIC_ROW: { + /* scaling single ratio widget width */ + item_width = layout->row.item_width * panel_space; + item_offset = layout->row.item_offset; + item_spacing = 0; + + if (modify) { + layout->row.item_offset += item_width + spacing.x; + layout->row.filled += layout->row.item_width; + layout->row.index = 0; + } + } break; + case NK_LAYOUT_DYNAMIC_FREE: { + /* panel width depended free widget placing */ + bounds->x = layout->at_x + (layout->bounds.w * layout->row.item.x); + bounds->x -= layout->offset->x; + bounds->y = layout->at_y + (layout->row.height * layout->row.item.y); + bounds->y -= layout->offset->y; + bounds->w = layout->bounds.w * layout->row.item.w; + bounds->h = layout->row.height * layout->row.item.h; + return; + }; + case NK_LAYOUT_DYNAMIC: { + /* scaling arrays of panel width ratios for every widget */ + float ratio; + NK_ASSERT(layout->row.ratio); + ratio = (layout->row.ratio[layout->row.index] < 0) ? + layout->row.item_width : layout->row.ratio[layout->row.index]; + + item_spacing = (float)layout->row.index * spacing.x; + item_width = (ratio * panel_space); + item_offset = layout->row.item_offset; + + if (modify) { + layout->row.item_offset += item_width; + layout->row.filled += ratio; + } + } break; + case NK_LAYOUT_STATIC_FIXED: { + /* non-scaling fixed widgets item width */ + item_width = layout->row.item_width; + item_offset = (float)layout->row.index * item_width; + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_STATIC_ROW: { + /* scaling single ratio widget width */ + item_width = layout->row.item_width; + item_offset = layout->row.item_offset; + item_spacing = (float)layout->row.index * spacing.x; + if (modify) { + layout->row.item_offset += item_width; + layout->row.index = 0; + } + } break; + case NK_LAYOUT_STATIC_FREE: { + /* free widget placing */ + bounds->x = layout->at_x + layout->row.item.x; + bounds->w = layout->row.item.w; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = (bounds->x + bounds->w); + bounds->x -= layout->offset->x; + bounds->y = layout->at_y + layout->row.item.y; + bounds->y -= layout->offset->y; + bounds->h = layout->row.item.h; + return; + }; + case NK_LAYOUT_STATIC: { + /* non-scaling array of panel pixel width for every widget */ + item_spacing = (float)layout->row.index * spacing.x; + item_width = layout->row.ratio[layout->row.index]; + item_offset = layout->row.item_offset; + if (modify) layout->row.item_offset += item_width; + } break; + default: NK_ASSERT(0); break; + }; + + /* set the bounds of the newly allocated widget */ + bounds->w = item_width; + bounds->h = layout->row.height - spacing.y; + bounds->y = layout->at_y - layout->offset->y; + bounds->x = layout->at_x + item_offset + item_spacing + padding.x; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = bounds->x + bounds->w; + bounds->x -= layout->offset->x; +} + +NK_INTERN void +nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* check if the end of the row has been hit and begin new row if so */ + win = ctx->current; + layout = win->layout; + if (layout->row.index >= layout->row.columns) + nk_panel_alloc_row(ctx, win); + + /* calculate widget position and size */ + nk_layout_widget_space(bounds, ctx, win, nk_true); + layout->row.index++; +} + +NK_INTERN void +nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx) +{ + float y; + int index; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + y = layout->at_y; + index = layout->row.index; + if (layout->row.index >= layout->row.columns) { + layout->at_y += layout->row.height; + layout->row.index = 0; + } + nk_layout_widget_space(bounds, ctx, win, nk_false); + layout->at_y = y; + layout->row.index = index; +} + +NK_INTERN int +nk_tree_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, enum nk_collapse_states initial_state, + const char *hash, int len, int line) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + const struct nk_input *in; + const struct nk_style_button *button; + enum nk_symbol_type symbol; + + struct nk_vec2 item_spacing; + struct nk_rect header = {0,0,0,0}; + struct nk_rect sym = {0,0,0,0}; + struct nk_text text; + + nk_flags ws = 0; + int title_len = 0; + nk_hash tree_hash = 0; + nk_uint *state = 0; + enum nk_widget_layout_states widget_state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* cache some data */ + win = ctx->current; + layout = win->layout; + out = &win->buffer; + style = &ctx->style; + item_spacing = style->window.spacing; + + /* calculate header bounds and draw background */ + nk_layout_row_dynamic(ctx, style->font->height + 2 * style->tab.padding.y, 1); + widget_state = nk_widget(&header, ctx); + if (type == NK_TREE_TAB) { + const struct nk_style_item *background = &style->tab.background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, header, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, style->tab.border_color); + nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), + style->tab.rounding, background->data.color); + } + } else text.background = style->window.background; + + /* find, create or set tab persistent state (open/closed) */ + if (hash) { + tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); + } else { + title_len = (int)nk_strlen(title); + tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); + } + state = nk_find_value(win, tree_hash); + if (!state) { + state = nk_add_value(ctx, win, tree_hash, 0); + *state = initial_state; + } + + /* update node state */ + in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; + in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; + if (nk_button_behavior(&ws, header, in, NK_BUTTON_DEFAULT)) + *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED; + + /* select correct button style */ + if (*state == NK_MAXIMIZED) { + symbol = style->tab.sym_maximize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_maximize_button; + else button = &style->tab.node_maximize_button; + } else { + symbol = style->tab.sym_minimize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_minimize_button; + else button = &style->tab.node_minimize_button; + } + + {/* draw triangle button */ + sym.w = sym.h = style->font->height; + sym.y = header.y + style->tab.padding.y; + sym.x = header.x + style->tab.padding.x; + nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, + button, 0, style->font); + + if (img) { + /* draw optional image icon */ + sym.x = sym.x + sym.w + 4 * item_spacing.x; + nk_draw_image(&win->buffer, sym, img, nk_white); + sym.w = style->font->height + style->tab.spacing.x;} + } + + {/* draw label */ + struct nk_rect label; + header.w = NK_MAX(header.w, sym.w + item_spacing.x); + label.x = sym.x + sym.w + item_spacing.x; + label.y = sym.y; + label.w = header.w - (sym.w + item_spacing.y + style->tab.indent); + label.h = style->font->height; + text.text = style->tab.text; + text.padding = nk_vec2(0,0); + nk_widget_text(out, label, title, nk_strlen(title), &text, + NK_TEXT_LEFT, style->font);} + + /* increase x-axis cursor widget position pointer */ + if (*state == NK_MAXIMIZED) { + layout->at_x = header.x + layout->offset->x + style->tab.indent; + layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); + layout->bounds.w -= (style->tab.indent + style->window.padding.x); + layout->row.tree_depth++; + return nk_true; + } else return nk_false; +} + +NK_API int +nk_tree_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states initial_state, + const char *hash, int len, int line) +{return nk_tree_base(ctx, type, 0, title, initial_state, hash, len, line);} + +NK_API int +nk_tree_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image img, const char *title, enum nk_collapse_states initial_state, + const char *hash, int len,int seed) +{return nk_tree_base(ctx, type, &img, title, initial_state, hash, len, seed);} + +NK_API void +nk_tree_pop(struct nk_context *ctx) +{ + struct nk_window *win = 0; + struct nk_panel *layout = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->at_x -= ctx->style.tab.indent + ctx->style.window.padding.x; + layout->bounds.w += ctx->style.tab.indent + ctx->style.window.padding.x; + NK_ASSERT(layout->row.tree_depth); + layout->row.tree_depth--; +} +/*---------------------------------------------------------------- + * + * WIDGETS + * + * --------------------------------------------------------------*/ +NK_API struct nk_rect +nk_widget_bounds(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_rect(0,0,0,0); + nk_layout_peek(&bounds, ctx); + return bounds; +} + +NK_API struct nk_vec2 +nk_widget_position(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.x, bounds.y); +} + +NK_API struct nk_vec2 +nk_widget_size(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.w, bounds.h); +} + +NK_API float +nk_widget_width(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + return bounds.w; +} + +NK_API float +nk_widget_height(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + return bounds.h; +} + +NK_API int +nk_widget_is_hovered(struct nk_context *ctx) +{ + int ret; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + ret = (ctx->active == ctx->current); + ret = ret && nk_input_is_mouse_hovering_rect(&ctx->input, bounds); + return ret; +} + +NK_API int +nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn) +{ + int ret; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + ret = (ctx->active == ctx->current); + ret = ret && nk_input_mouse_clicked(&ctx->input, btn, bounds); + return ret; +} + +NK_API int +nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, int down) +{ + int ret; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + ret = (ctx->active == ctx->current); + ret = ret && nk_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down); + return ret; +} + +NK_API enum nk_widget_layout_states +nk_widget(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_rect *c = 0; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + /* allocate space and check if the widget needs to be updated and drawn */ + nk_panel_alloc_space(bounds, ctx); + win = ctx->current; + layout = win->layout; + c = &layout->clip; + + /* if one of these triggers you forgot to add an `if` condition around either + a window, group, popup, combobox or contextual menu `begin` and `end` block. + Example: + if (nk_begin(...) {...} nk_end(...); or + if (nk_group_begin(...) { nk_group_end(...);} */ + NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); + NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); + NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); + + /* need to convert to int here to remove floating point error */ + bounds->x = (float)((int)bounds->x); + bounds->y = (float)((int)bounds->y); + bounds->w = (float)((int)bounds->w); + bounds->h = (float)((int)bounds->h); + + if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds->x, bounds->y, bounds->w, bounds->h)) + return NK_WIDGET_INVALID; + if (!NK_CONTAINS(bounds->x, bounds->y, bounds->w, bounds->h, c->x, c->y, c->w, c->h)) + return NK_WIDGET_ROM; + return NK_WIDGET_VALID; +} + +NK_API enum nk_widget_layout_states +nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx, + struct nk_vec2 item_padding) +{ + /* update the bounds to stand without padding */ + struct nk_window *win; + struct nk_style *style; + struct nk_panel *layout; + enum nk_widget_layout_states state; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(bounds, ctx); + + panel_padding = nk_panel_get_padding(style, layout->type); + if (layout->row.index == 1) { + bounds->w += panel_padding.x; + bounds->x -= panel_padding.x; + } else bounds->x -= item_padding.x; + + if (layout->row.index == layout->row.columns) + bounds->w += panel_padding.x; + else bounds->w += item_padding.x; + return state; +} + +/*---------------------------------------------------------------- + * + * MISC + * + * --------------------------------------------------------------*/ +NK_API void +nk_spacing(struct nk_context *ctx, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_rect nil; + int i, index, rows; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* spacing over row boundaries */ + win = ctx->current; + layout = win->layout; + index = (layout->row.index + cols) % layout->row.columns; + rows = (layout->row.index + cols) / layout->row.columns; + if (rows) { + for (i = 0; i < rows; ++i) + nk_panel_alloc_row(ctx, win); + cols = index; + } + + /* non table layout need to allocate space */ + if (layout->row.type != NK_LAYOUT_DYNAMIC_FIXED && + layout->row.type != NK_LAYOUT_STATIC_FIXED) { + for (i = 0; i < cols; ++i) + nk_panel_alloc_space(&nil, ctx); + } + layout->row.index = index; +} + +/*---------------------------------------------------------------- + * + * TEXT + * + * --------------------------------------------------------------*/ +NK_API void +nk_text_colored(struct nk_context *ctx, const char *str, int len, + nk_flags alignment, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, style->font); +} + +NK_API void +nk_text_wrap_colored(struct nk_context *ctx, const char *str, + int len, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, style->font); +} + +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void +nk_labelf_colored(struct nk_context *ctx, nk_flags flags, + struct nk_color color, const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored(ctx, buf, flags, color); + va_end(args); +} + +NK_API void +nk_labelf_colored_wrap(struct nk_context *ctx, struct nk_color color, + const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored_wrap(ctx, buf, color); + va_end(args); +} + +NK_API void +nk_labelf(struct nk_context *ctx, nk_flags flags, const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label(ctx, buf, flags); + va_end(args); +} + +NK_API void +nk_labelf_wrap(struct nk_context *ctx, const char *fmt,...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_wrap(ctx, buf); + va_end(args); +} + +NK_API void +nk_value_bool(struct nk_context *ctx, const char *prefix, int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false"));} + +NK_API void +nk_value_int(struct nk_context *ctx, const char *prefix, int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value);} + +NK_API void +nk_value_uint(struct nk_context *ctx, const char *prefix, unsigned int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value);} + +NK_API void +nk_value_float(struct nk_context *ctx, const char *prefix, float value) +{ + double double_value = (double)value; + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %.3f", prefix, double_value); +} + +NK_API void +nk_value_color_byte(struct nk_context *ctx, const char *p, struct nk_color c) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%c, %c, %c, %c)", p, c.r, c.g, c.b, c.a);} + +NK_API void +nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color color) +{ + double c[4]; nk_color_dv(c, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)", + p, c[0], c[1], c[2], c[3]); +} + +NK_API void +nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color color) +{ + char hex[16]; + nk_color_hex_rgba(hex, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, hex); +} +#endif + +NK_API void +nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_colored(ctx, str, len, alignment, ctx->style.text.color); +} + +NK_API void +nk_text_wrap(struct nk_context *ctx, const char *str, int len) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_wrap_colored(ctx, str, len, ctx->style.text.color); +} + +NK_API void +nk_label(struct nk_context *ctx, const char *str, nk_flags alignment) +{nk_text(ctx, str, nk_strlen(str), alignment);} + +NK_API void +nk_label_colored(struct nk_context *ctx, const char *str, nk_flags align, + struct nk_color color) +{nk_text_colored(ctx, str, nk_strlen(str), align, color);} + +NK_API void +nk_label_wrap(struct nk_context *ctx, const char *str) +{nk_text_wrap(ctx, str, nk_strlen(str));} + +NK_API void +nk_label_colored_wrap(struct nk_context *ctx, const char *str, struct nk_color color) +{nk_text_wrap_colored(ctx, str, nk_strlen(str), color);} + +NK_API void +nk_image(struct nk_context *ctx, struct nk_image img) +{ + struct nk_window *win; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + if (!nk_widget(&bounds, ctx)) return; + nk_draw_image(&win->buffer, bounds, &img, nk_white); +} + +/*---------------------------------------------------------------- + * + * BUTTON + * + * --------------------------------------------------------------*/ +NK_API void +nk_button_set_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) +{ + NK_ASSERT(ctx); + if (!ctx) return; + ctx->button_behavior = behavior; +} + +NK_API int +nk_button_push_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) +{ + struct nk_config_stack_button_behavior *button_stack; + struct nk_config_stack_button_behavior_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + button_stack = &ctx->stacks.button_behaviors; + NK_ASSERT(button_stack->head < (int)NK_LEN(button_stack->elements)); + if (button_stack->head >= (int)NK_LEN(button_stack->elements)) + return 0; + + element = &button_stack->elements[button_stack->head++]; + element->address = &ctx->button_behavior; + element->old_value = ctx->button_behavior; + ctx->button_behavior = behavior; + return 1; +} + +NK_API int +nk_button_pop_behavior(struct nk_context *ctx) +{ + struct nk_config_stack_button_behavior *button_stack; + struct nk_config_stack_button_behavior_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + button_stack = &ctx->stacks.button_behaviors; + NK_ASSERT(button_stack->head > 0); + if (button_stack->head < 1) + return 0; + + element = &button_stack->elements[--button_stack->head]; + *element->address = element->old_value; + return 1; +} +NK_API int +nk_button_text(struct nk_context *ctx, const char *title, int len) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + title, len, style->button.text_alignment, ctx->button_behavior, + &style->button, in, style->font); +} + +NK_API int nk_button_label(struct nk_context *ctx, const char *title) +{return nk_button_text(ctx, title, nk_strlen(title));} + +NK_API int +nk_button_color(struct nk_context *ctx, struct nk_color color) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + struct nk_style_button button; + + int ret = 0; + struct nk_rect bounds; + struct nk_rect content; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + button = ctx->style.button; + button.normal = nk_style_item_color(color); + button.hover = nk_style_item_color(color); + button.active = nk_style_item_color(color); + ret = nk_do_button(&ctx->last_widget_state, &win->buffer, bounds, + &button, in, ctx->button_behavior, &content); + nk_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button); + return ret; +} + +NK_API int +nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, ctx->button_behavior, &style->button, in, style->font); +} + +NK_API int +nk_button_image(struct nk_context *ctx, struct nk_image img) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds, + img, ctx->button_behavior, &style->button, in); +} + +NK_API int +nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char* text, int len, nk_flags align) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, ctx->button_behavior, + &style->button, style->font, in); +} + +NK_API int nk_button_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *label, nk_flags align) +{return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align);} + +NK_API int +nk_button_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + bounds, img, text, len, align, ctx->button_behavior, + &style->button, style->font, in); +} + +NK_API int nk_button_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_button_image_text(ctx, img, label, nk_strlen(label), align);} + +/*---------------------------------------------------------------- + * + * SELECTABLE + * + * --------------------------------------------------------------*/ +NK_API int +nk_selectable_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &style->selectable, in, style->font); +} + +NK_API int +nk_selectable_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &img, &style->selectable, in, style->font); +} + +NK_API int nk_select_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int value) +{nk_selectable_text(ctx, str, len, align, &value);return value;} + +NK_API int nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, int *value) +{return nk_selectable_text(ctx, str, nk_strlen(str), align, value);} + +NK_API int nk_selectable_image_label(struct nk_context *ctx,struct nk_image img, + const char *str, nk_flags align, int *value) +{return nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, value);} + +NK_API int nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, int value) +{nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value;} + +NK_API int nk_select_image_label(struct nk_context *ctx, struct nk_image img, + const char *str, nk_flags align, int value) +{nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, &value);return value;} + +NK_API int nk_select_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, int value) +{nk_selectable_image_text(ctx, img, str, len, align, &value);return value;} + +/*---------------------------------------------------------------- + * + * CHECKBOX + * + * --------------------------------------------------------------*/ +NK_API int +nk_check_text(struct nk_context *ctx, const char *text, int len, int active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return active; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active, + text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font); + return active; +} + +NK_API unsigned int +nk_check_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int flags, unsigned int value) +{ + int old_active; + NK_ASSERT(ctx); + NK_ASSERT(text); + if (!ctx || !text) return flags; + old_active = (int)((flags & value) & value); + if (nk_check_text(ctx, text, len, old_active)) + flags |= value; + else flags &= ~value; + return flags; +} + +NK_API int +nk_checkbox_text(struct nk_context *ctx, const char *text, int len, int *active) +{ + int old_val; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_val = *active; + *active = nk_check_text(ctx, text, len, *active); + return old_val != *active; +} + +NK_API int +nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int *flags, unsigned int value) +{ + int active; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(flags); + if (!ctx || !text || !flags) return 0; + active = (int)((*flags & value) & value); + if (nk_checkbox_text(ctx, text, len, &active)) { + if (active) *flags |= value; + else *flags &= ~value; + return 1; + } + return 0; +} + +NK_API int nk_check_label(struct nk_context *ctx, const char *label, int active) +{return nk_check_text(ctx, label, nk_strlen(label), active);} + +NK_API unsigned int nk_check_flags_label(struct nk_context *ctx, const char *label, + unsigned int flags, unsigned int value) +{return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value);} + +NK_API int nk_checkbox_label(struct nk_context *ctx, const char *label, int *active) +{return nk_checkbox_text(ctx, label, nk_strlen(label), active);} + +NK_API int nk_checkbox_flags_label(struct nk_context *ctx, const char *label, + unsigned int *flags, unsigned int value) +{return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value);} + +/*---------------------------------------------------------------- + * + * OPTION + * + * --------------------------------------------------------------*/ +NK_API int +nk_option_text(struct nk_context *ctx, const char *text, int len, int is_active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return is_active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return state; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active, + text, len, NK_TOGGLE_OPTION, &style->option, in, style->font); + return is_active; +} + +NK_API int +nk_radio_text(struct nk_context *ctx, const char *text, int len, int *active) +{ + int old_value; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_value = *active; + *active = nk_option_text(ctx, text, len, old_value); + return old_value != *active; +} + +NK_API int +nk_option_label(struct nk_context *ctx, const char *label, int active) +{return nk_option_text(ctx, label, nk_strlen(label), active);} + +NK_API int +nk_radio_label(struct nk_context *ctx, const char *label, int *active) +{return nk_radio_text(ctx, label, nk_strlen(label), active);} + +/*---------------------------------------------------------------- + * + * SLIDER + * + * --------------------------------------------------------------*/ +NK_API int +nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max_value, + float value_step) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_input *in; + const struct nk_style *style; + + int ret = 0; + float old_value; + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(value); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return ret; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return ret; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + old_value = *value; + *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value, + old_value, max_value, value_step, &style->slider, in, style->font); + return (old_value > *value || old_value < *value); +} + +NK_API float +nk_slide_float(struct nk_context *ctx, float min, float val, float max, float step) +{ + nk_slider_float(ctx, min, &val, max, step); return val; +} + +NK_API int +nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step) +{ + float value = (float)val; + nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + return (int)value; +} + +NK_API int +nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step) +{ + int ret; + float value = (float)*val; + ret = nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + *val = (int)value; + return ret; +} + +/*---------------------------------------------------------------- + * + * PROGRESSBAR + * + * --------------------------------------------------------------*/ +NK_API int +nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, int is_modifyable) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + nk_size old_value; + + NK_ASSERT(ctx); + NK_ASSERT(cur); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !cur) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + old_value = *cur; + *cur = nk_do_progress(&ctx->last_widget_state, &win->buffer, bounds, + *cur, max, is_modifyable, &style->progress, in); + return (*cur != old_value); +} + +NK_API nk_size nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, int modifyable) +{nk_progress(ctx, &cur, max, modifyable);return cur;} + +/*---------------------------------------------------------------- + * + * EDIT + * + * --------------------------------------------------------------*/ +NK_API nk_flags +nk_edit_string(struct nk_context *ctx, nk_flags flags, + char *memory, int *len, int max, nk_plugin_filter filter) +{ + nk_hash hash; + nk_flags state; + struct nk_text_edit *edit; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(memory); + NK_ASSERT(len); + if (!ctx || !memory || !len) + return 0; + + filter = (!filter) ? nk_filter_default: filter; + win = ctx->current; + hash = win->edit.seq; + edit = &ctx->text_edit; + nk_textedit_clear_state(&ctx->text_edit, (flags & NK_EDIT_MULTILINE)? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE, filter); + + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = nk_utf_len(memory, *len); + else edit->cursor = win->edit.cursor; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = win->edit.cursor; + edit->select_end = win->edit.cursor; + } else { + edit->select_start = win->edit.sel_start; + edit->select_end = win->edit.sel_end; + } + edit->mode = win->edit.mode; + edit->scrollbar.x = (float)win->edit.scrollbar.x; + edit->scrollbar.y = (float)win->edit.scrollbar.y; + edit->active = nk_true; + } else edit->active = nk_false; + + max = NK_MAX(1, max); + *len = NK_MIN(*len, max-1); + nk_str_init_fixed(&edit->string, memory, (nk_size)max); + edit->string.buffer.allocated = (nk_size)*len; + edit->string.len = nk_utf_len(memory, *len); + state = nk_edit_buffer(ctx, flags, edit, filter); + *len = (int)edit->string.buffer.allocated; + + if (edit->active) { + win->edit.cursor = edit->cursor; + win->edit.sel_start = edit->select_start; + win->edit.sel_end = edit->select_end; + win->edit.mode = edit->mode; + win->edit.scrollbar.x = (unsigned short)edit->scrollbar.x; + win->edit.scrollbar.y = (unsigned short)edit->scrollbar.y; + } + return state; +} + +NK_API nk_flags +nk_edit_buffer(struct nk_context *ctx, nk_flags flags, + struct nk_text_edit *edit, nk_plugin_filter filter) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + nk_flags ret_flags = 0; + unsigned char prev_state; + nk_hash hash; + + /* make sure correct values */ + NK_ASSERT(ctx); + NK_ASSERT(edit); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget(&bounds, ctx); + if (!state) return state; + in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + /* check if edit is currently hot item */ + hash = win->edit.seq++; + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = edit->string.len; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = edit->cursor; + edit->select_end = edit->cursor; + } + if (flags & NK_EDIT_CLIPBOARD) + edit->clip = ctx->clip; + } + + filter = (!filter) ? nk_filter_default: filter; + prev_state = (unsigned char)edit->active; + in = (flags & NK_EDIT_READ_ONLY) ? 0: in; + ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags, + filter, edit, &style->edit, in, style->font); + + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_TEXT]; + if (edit->active && prev_state != edit->active) { + /* current edit is now hot */ + win->edit.active = nk_true; + win->edit.name = hash; + } else if (prev_state && !edit->active) { + /* current edit is now cold */ + win->edit.active = nk_false; + } + return ret_flags; +} + +NK_API nk_flags +nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags, + char *buffer, int max, nk_plugin_filter filter) +{ + nk_flags result; + int len = nk_strlen(buffer); + result = nk_edit_string(ctx, flags, buffer, &len, max, filter); + buffer[NK_MIN(NK_MAX(max-1,0), len)] = '\0'; + return result; +} + +/*---------------------------------------------------------------- + * + * PROPERTY + * + * --------------------------------------------------------------*/ +NK_INTERN struct nk_property_variant +nk_property_variant_int(int value, int min_value, int max_value, int step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_INT; + result.value.i = value; + result.min_value.i = min_value; + result.max_value.i = max_value; + result.step.i = step; + return result; +} + +NK_INTERN struct nk_property_variant +nk_property_variant_float(float value, float min_value, float max_value, float step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_FLOAT; + result.value.f = value; + result.min_value.f = min_value; + result.max_value.f = max_value; + result.step.f = step; + return result; +} + +NK_INTERN struct nk_property_variant +nk_property_variant_double(double value, double min_value, double max_value, + double step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_DOUBLE; + result.value.d = value; + result.min_value.d = min_value; + result.max_value.d = max_value; + result.step.d = step; + return result; +} + +NK_INTERN void +nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, + float inc_per_pixel, const enum nk_property_filter filter) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states s; + + int *state = 0; + nk_hash hash = 0; + char *buffer = 0; + int *len = 0; + int *cursor = 0; + int old_state; + + char dummy_buffer[NK_MAX_NUMBER_BUFFER]; + int dummy_state = NK_PROPERTY_DEFAULT; + int dummy_length = 0; + int dummy_cursor = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + s = nk_widget(&bounds, ctx); + if (!s) return; + in = (s == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + /* calculate hash from name */ + if (name[0] == '#') { + hash = nk_murmur_hash(name, (int)nk_strlen(name), win->property.seq++); + name++; /* special number hash */ + } else hash = nk_murmur_hash(name, (int)nk_strlen(name), 42); + + /* check if property is currently hot item */ + if (win->property.active && hash == win->property.name) { + buffer = win->property.buffer; + len = &win->property.length; + cursor = &win->property.cursor; + state = &win->property.state; + } else { + buffer = dummy_buffer; + len = &dummy_length; + cursor = &dummy_cursor; + state = &dummy_state; + } + + /* execute property widget */ + old_state = *state; + nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name, + variant, inc_per_pixel, buffer, len, state, cursor, + &style->property, filter, in, style->font, &ctx->text_edit); + + if (in && *state != NK_PROPERTY_DEFAULT && !win->property.active) { + /* current property is now hot */ + win->property.active = 1; + NK_MEMCPY(win->property.buffer, buffer, (nk_size)*len); + win->property.length = *len; + win->property.cursor = *cursor; + win->property.state = *state; + win->property.name = hash; + if (*state == NK_PROPERTY_DRAG) { + ctx->input.mouse.grab = nk_true; + ctx->input.mouse.grabbed = nk_true; + } + } + + /* check if previously active property is now inactive */ + if (*state == NK_PROPERTY_DEFAULT && old_state != NK_PROPERTY_DEFAULT) { + if (old_state == NK_PROPERTY_DRAG) { + ctx->input.mouse.grab = nk_false; + ctx->input.mouse.grabbed = nk_false; + ctx->input.mouse.ungrab = nk_true; + } + win->property.active = 0; + } +} + +NK_API void +nk_property_int(struct nk_context *ctx, const char *name, + int min, int *val, int max, int step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_int(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); + *val = variant.value.i; +} + +NK_API void +nk_property_float(struct nk_context *ctx, const char *name, + float min, float *val, float max, float step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_float(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + *val = variant.value.f; +} + +NK_API void +nk_property_double(struct nk_context *ctx, const char *name, + double min, double *val, double max, double step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_double(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + *val = variant.value.d; +} + +NK_API int +nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, + int max, int step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_int(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); + val = variant.value.i; + return val; +} + +NK_API float +nk_propertyf(struct nk_context *ctx, const char *name, float min, + float val, float max, float step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_float(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + val = variant.value.f; + return val; +} + +NK_API double +nk_propertyd(struct nk_context *ctx, const char *name, double min, + double val, double max, double step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_double(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + val = variant.value.d; + return val; +} + +/*---------------------------------------------------------------- + * + * COLOR PICKER + * + * --------------------------------------------------------------*/ +NK_API int +nk_color_pick(struct nk_context * ctx, struct nk_color *color, + enum nk_color_format fmt) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *config; + const struct nk_input *in; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(color); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !color) + return 0; + + win = ctx->current; + config = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds, + nk_vec2(0,0), in, config->font); +} + +NK_API struct nk_color +nk_color_picker(struct nk_context *ctx, struct nk_color color, + enum nk_color_format fmt) +{ + nk_color_pick(ctx, &color, fmt); + return color; +} + +/* ------------------------------------------------------------- + * + * CHART + * + * --------------------------------------------------------------*/ +NK_API int +nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type, + struct nk_color color, struct nk_color highlight, + int count, float min_value, float max_value) +{ + struct nk_window *win; + struct nk_chart *chart; + const struct nk_style *config; + const struct nk_style_chart *style; + + const struct nk_style_item *background; + struct nk_rect bounds = {0, 0, 0, 0}; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + if (!nk_widget(&bounds, ctx)) { + chart = &ctx->current->layout->chart; + nk_zero(chart, sizeof(*chart)); + return 0; + } + + win = ctx->current; + config = &ctx->style; + chart = &win->layout->chart; + style = &config->chart; + + /* setup basic generic chart */ + nk_zero(chart, sizeof(*chart)); + chart->x = bounds.x + style->padding.x; + chart->y = bounds.y + style->padding.y; + chart->w = bounds.w - 2 * style->padding.x; + chart->h = bounds.h - 2 * style->padding.y; + chart->w = NK_MAX(chart->w, 2 * style->padding.x); + chart->h = NK_MAX(chart->h, 2 * style->padding.y); + + /* add first slot into chart */ + {struct nk_chart_slot *slot = &chart->slots[chart->slot++]; + slot->type = type; + slot->count = count; + slot->color = color; + slot->highlight = highlight; + slot->min = NK_MIN(min_value, max_value); + slot->max = NK_MAX(min_value, max_value); + slot->range = slot->max - slot->min;} + + /* draw chart background */ + background = &style->background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(&win->buffer, bounds, style->rounding, style->border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(bounds, style->border), + style->rounding, style->background.data.color); + } + return 1; +} + +NK_API int +nk_chart_begin(struct nk_context *ctx, const enum nk_chart_type type, + int count, float min_value, float max_value) +{return nk_chart_begin_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);} + +NK_API void +nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type, + struct nk_color color, struct nk_color highlight, + int count, float min_value, float max_value) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(ctx->current->layout->chart.slot < NK_CHART_MAX_SLOT); + if (!ctx || !ctx->current || !ctx->current->layout) return; + if (ctx->current->layout->chart.slot >= NK_CHART_MAX_SLOT) return; + + /* add another slot into the graph */ + {struct nk_chart *chart = &ctx->current->layout->chart; + struct nk_chart_slot *slot = &chart->slots[chart->slot++]; + slot->type = type; + slot->count = count; + slot->color = color; + slot->highlight = highlight; + slot->min = NK_MIN(min_value, max_value); + slot->max = NK_MAX(min_value, max_value); + slot->range = slot->max - slot->min;} +} + +NK_API void +nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type type, + int count, float min_value, float max_value) +{nk_chart_add_slot_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);} + +NK_INTERN nk_flags +nk_chart_push_line(struct nk_context *ctx, struct nk_window *win, + struct nk_chart *g, float value, int slot) +{ + struct nk_panel *layout = win->layout; + const struct nk_input *i = &ctx->input; + struct nk_command_buffer *out = &win->buffer; + + nk_flags ret = 0; + struct nk_vec2 cur; + struct nk_rect bounds; + struct nk_color color; + float step; + float range; + float ratio; + + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + step = g->w / (float)g->slots[slot].count; + range = g->slots[slot].max - g->slots[slot].min; + ratio = (value - g->slots[slot].min) / range; + + if (g->slots[slot].index == 0) { + /* first data point does not have a connection */ + g->slots[slot].last.x = g->x; + g->slots[slot].last.y = (g->y + g->h) - ratio * (float)g->h; + + bounds.x = g->slots[slot].last.x - 2; + bounds.y = g->slots[slot].last.y - 2; + bounds.w = 4; + bounds.h = 4; + + color = g->slots[slot].color; + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->slots[slot].last.x-3, g->slots[slot].last.y-3, 6, 6)){ + ret = nk_input_is_mouse_hovering_rect(i, bounds) ? NK_CHART_HOVERING : 0; + ret |= (i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->slots[slot].highlight; + } + nk_fill_rect(out, bounds, 0, color); + g->slots[slot].index += 1; + return ret; + } + + /* draw a line between the last data point and the new one */ + color = g->slots[slot].color; + cur.x = g->x + (float)(step * (float)g->slots[slot].index); + cur.y = (g->y + g->h) - (ratio * (float)g->h); + nk_stroke_line(out, g->slots[slot].last.x, g->slots[slot].last.y, cur.x, cur.y, 1.0f, color); + + bounds.x = cur.x - 3; + bounds.y = cur.y - 3; + bounds.w = 6; + bounds.h = 6; + + /* user selection of current data point */ + if (!(layout->flags & NK_WINDOW_ROM)) { + if (nk_input_is_mouse_hovering_rect(i, bounds)) { + ret = NK_CHART_HOVERING; + ret |= (!i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->slots[slot].highlight; + } + } + nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color); + + /* save current data point position */ + g->slots[slot].last.x = cur.x; + g->slots[slot].last.y = cur.y; + g->slots[slot].index += 1; + return ret; +} + +NK_INTERN nk_flags +nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win, + struct nk_chart *chart, float value, int slot) +{ + struct nk_command_buffer *out = &win->buffer; + const struct nk_input *in = &ctx->input; + struct nk_panel *layout = win->layout; + + float ratio; + nk_flags ret = 0; + struct nk_color color; + struct nk_rect item = {0,0,0,0}; + + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + if (chart->slots[slot].index >= chart->slots[slot].count) + return nk_false; + if (chart->slots[slot].count) { + float padding = (float)(chart->slots[slot].count-1); + item.w = (chart->w - padding) / (float)(chart->slots[slot].count); + } + + /* calculate bounds of current bar chart entry */ + color = chart->slots[slot].color;; + item.h = chart->h * NK_ABS((value/chart->slots[slot].range)); + if (value >= 0) { + ratio = (value + NK_ABS(chart->slots[slot].min)) / NK_ABS(chart->slots[slot].range); + item.y = (chart->y + chart->h) - chart->h * ratio; + } else { + ratio = (value - chart->slots[slot].max) / chart->slots[slot].range; + item.y = chart->y + (chart->h * NK_ABS(ratio)) - item.h; + } + item.x = chart->x + ((float)chart->slots[slot].index * item.w); + item.x = item.x + ((float)chart->slots[slot].index); + + /* user chart bar selection */ + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) { + ret = NK_CHART_HOVERING; + ret |= (!in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = chart->slots[slot].highlight; + } + nk_fill_rect(out, item, 0, color); + chart->slots[slot].index += 1; + return ret; +} + +NK_API nk_flags +nk_chart_push_slot(struct nk_context *ctx, float value, int slot) +{ + nk_flags flags; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + NK_ASSERT(slot < ctx->current->layout->chart.slot); + if (!ctx || !ctx->current || slot >= NK_CHART_MAX_SLOT) return nk_false; + if (slot >= ctx->current->layout->chart.slot) return nk_false; + + win = ctx->current; + if (win->layout->chart.slot < slot) return nk_false; + switch (win->layout->chart.slots[slot].type) { + case NK_CHART_LINES: + flags = nk_chart_push_line(ctx, win, &win->layout->chart, value, slot); break; + case NK_CHART_COLUMN: + flags = nk_chart_push_column(ctx, win, &win->layout->chart, value, slot); break; + default: + case NK_CHART_MAX: + flags = 0; + } + return flags; +} + +NK_API nk_flags +nk_chart_push(struct nk_context *ctx, float value) +{return nk_chart_push_slot(ctx, value, 0);} + +NK_API void +nk_chart_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_chart *chart; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + win = ctx->current; + chart = &win->layout->chart; + NK_MEMSET(chart, 0, sizeof(*chart)); + return; +} + +NK_API void +nk_plot(struct nk_context *ctx, enum nk_chart_type type, const float *values, + int count, int offset) +{ + int i = 0; + float min_value; + float max_value; + + NK_ASSERT(ctx); + NK_ASSERT(values); + if (!ctx || !values || !count) return; + + min_value = values[offset]; + max_value = values[offset]; + for (i = 0; i < count; ++i) { + min_value = NK_MIN(values[i + offset], min_value); + max_value = NK_MAX(values[i + offset], max_value); + } + nk_chart_begin(ctx, type, count, min_value, max_value); + for (i = 0; i < count; ++i) + nk_chart_push(ctx, values[i + offset]); + nk_chart_end(ctx); +} + +NK_API void +nk_plot_function(struct nk_context *ctx, enum nk_chart_type type, void *userdata, + float(*value_getter)(void* user, int index), int count, int offset) +{ + int i = 0; + float min_value; + float max_value; + + NK_ASSERT(ctx); + NK_ASSERT(value_getter); + if (!ctx || !value_getter || !count) return; + + max_value = min_value = value_getter(userdata, offset); + for (i = 0; i < count; ++i) { + float value = value_getter(userdata, i + offset); + min_value = NK_MIN(value, min_value); + max_value = NK_MAX(value, max_value); + } + nk_chart_begin(ctx, type, count, min_value, max_value); + for (i = 0; i < count; ++i) + nk_chart_push(ctx, value_getter(userdata, i + offset)); + nk_chart_end(ctx); +} + +/* ------------------------------------------------------------- + * + * GROUP + * + * --------------------------------------------------------------*/ +NK_API int +nk_group_begin(struct nk_context *ctx, struct nk_panel *layout, const char *title, + nk_flags flags) +{ + struct nk_window *win; + const struct nk_rect *c; + union {struct nk_scroll *s; nk_uint *i;} value; + struct nk_window panel; + struct nk_rect bounds; + nk_hash title_hash; + int title_len; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !title) + return 0; + + /* allocate space for the group panel inside the panel */ + win = ctx->current; + c = &win->layout->clip; + nk_panel_alloc_space(&bounds, ctx); + nk_zero(layout, sizeof(*layout)); + /* This triggers either if you pass the same panel to parent window or parent and child group + * or forgot to add a `nk_group_end` to a `nk_group_begin`. */ + NK_ASSERT(win->layout != layout && "Parent and group are not allowed to use the same panel"); + + /* find persistent group scrollbar value */ + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); + value.i = nk_find_value(win, title_hash); + if (!value.i) { + value.i = nk_add_value(ctx, win, title_hash, 0); + *value.i = 0; + } + if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && + !(flags & NK_WINDOW_MOVABLE)) { + return 0; + } + if (win->flags & NK_WINDOW_ROM) + flags |= NK_WINDOW_ROM; + + /* initialize a fake window to create the layout from */ + nk_zero(&panel, sizeof(panel)); + panel.bounds = bounds; + panel.flags = flags; + panel.scrollbar.x = (unsigned short)value.s->x; + panel.scrollbar.y = (unsigned short)value.s->y; + panel.buffer = win->buffer; + panel.layout = layout; + ctx->current = &panel; + nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0, NK_PANEL_GROUP); + + win->buffer = panel.buffer; + win->buffer.clip = layout->clip; + layout->offset = value.s; + layout->parent = win->layout; + win->layout = layout; + ctx->current = win; + return 1; +} + +NK_API void +nk_group_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *parent; + struct nk_panel *g; + + struct nk_rect clip; + struct nk_window pan; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + /* make sure nk_group_begin was called correctly */ + NK_ASSERT(ctx->current); + win = ctx->current; + NK_ASSERT(win->layout); + g = win->layout; + NK_ASSERT(g->parent); + parent = g->parent; + + /* dummy window */ + nk_zero_struct(pan); + panel_padding = nk_panel_get_padding(&ctx->style, NK_PANEL_GROUP); + pan.bounds.y = g->bounds.y - (g->header_height + g->menu.h); + pan.bounds.x = g->bounds.x - panel_padding.x; + pan.bounds.w = g->bounds.w + 2 * panel_padding.x; + pan.bounds.h = g->bounds.h + g->header_height + g->menu.h; + if (g->flags & NK_WINDOW_BORDER) { + pan.bounds.x -= g->border; + pan.bounds.y -= g->border; + pan.bounds.w += 2*g->border; + pan.bounds.h += 2*g->border; + } + if (!(g->flags & NK_WINDOW_NO_SCROLLBAR)) { + pan.bounds.w += ctx->style.window.scrollbar_size.x; + pan.bounds.h += ctx->style.window.scrollbar_size.y; + } + pan.scrollbar.x = (unsigned short)g->offset->x; + pan.scrollbar.y = (unsigned short)g->offset->y; + pan.flags = g->flags; + pan.buffer = win->buffer; + pan.layout = g; + pan.parent = win; + ctx->current = &pan; + + /* make sure group has correct clipping rectangle */ + nk_unify(&clip, &parent->clip, pan.bounds.x, pan.bounds.y, + pan.bounds.x + pan.bounds.w, pan.bounds.y + pan.bounds.h + panel_padding.x); + nk_push_scissor(&pan.buffer, clip); + nk_end(ctx); + + win->buffer = pan.buffer; + nk_push_scissor(&win->buffer, parent->clip); + ctx->current = win; + win->layout = parent; + g->bounds = pan.bounds; + return; +} + +/* -------------------------------------------------------------- + * + * POPUP + * + * --------------------------------------------------------------*/ +NK_API int +nk_popup_begin(struct nk_context *ctx, struct nk_panel *layout, + enum nk_popup_type type, const char *title, nk_flags flags, struct nk_rect rect) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + + int title_len; + nk_hash title_hash; + nk_size allocated; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups"); + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP); + + popup = win->popup.win; + if (!popup) { + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.active = 0; + win->popup.type = NK_PANEL_POPUP; + } + + /* make sure we have to correct popup */ + if (win->popup.name != title_hash) { + if (!win->popup.active) { + nk_zero(popup, sizeof(*popup)); + win->popup.name = title_hash; + win->popup.active = 1; + win->popup.type = NK_PANEL_POPUP; + } else return 0; + } + + /* popup position is local to window */ + ctx->current = popup; + rect.x += win->layout->clip.x; + rect.y += win->layout->clip.y; + + /* setup popup data */ + popup->parent = win; + popup->bounds = rect; + popup->seq = ctx->seq; + popup->layout = layout; + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + if (type == NK_POPUP_DYNAMIC) + popup->flags |= NK_WINDOW_DYNAMIC; + + popup->buffer = win->buffer; + nk_start_popup(ctx, win); + allocated = ctx->memory.allocated; + nk_push_scissor(&popup->buffer, nk_null_rect); + + if (nk_panel_begin(ctx, title, NK_PANEL_POPUP)) { + /* popup is running therefore invalidate parent panels */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 1; + layout->offset = &popup->scrollbar; + layout->parent = win->layout; + return 1; + } else { + /* popup was closed/is invalid so cleanup */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->layout->popup_buffer.active = 0; + win->popup.active = 0; + ctx->memory.allocated = allocated; + ctx->current = win; + return 0; + } +} + +NK_INTERN int +nk_nonblock_begin(struct nk_panel *layout, struct nk_context *ctx, + nk_flags flags, struct nk_rect body, struct nk_rect header, + enum nk_panel_type panel_type) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + int is_active = nk_true; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* popups cannot have popups */ + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP)); + popup = win->popup.win; + if (!popup) { + /* create window for nonblocking popup */ + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.type = panel_type; + nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON); + } else { + /* close the popup if user pressed outside or in the header */ + int pressed, in_body, in_header; + pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); + in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + in_header = nk_input_is_mouse_hovering_rect(&ctx->input, header); + if (pressed && (!in_body || in_header)) + is_active = nk_false; + } + win->popup.header = header; + + if (!is_active) { + /* remove read only mode from all parent panels */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + return is_active; + } + + popup->bounds = body; + popup->parent = win; + popup->layout = layout; + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + popup->flags |= NK_WINDOW_DYNAMIC; + popup->seq = ctx->seq; + win->popup.active = 1; + + nk_start_popup(ctx, win); + popup->buffer = win->buffer; + nk_push_scissor(&popup->buffer, nk_null_rect); + ctx->current = popup; + + nk_panel_begin(ctx, 0, panel_type); + win->buffer = popup->buffer; + layout->parent = win->layout; + layout->offset = &popup->scrollbar; + + /* set read only mode to all parent panels */ + {struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root = root->parent; + }} + return is_active; +} + +NK_API void +nk_popup_close(struct nk_context *ctx) +{ + struct nk_window *popup; + NK_ASSERT(ctx); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + NK_ASSERT(popup->parent); + NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP); + popup->flags |= NK_WINDOW_HIDDEN; +} + +NK_API void +nk_popup_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_window *popup; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + popup = ctx->current; + if (!popup->parent) return; + win = popup->parent; + if (popup->flags & NK_WINDOW_HIDDEN) { + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 0; + } + nk_push_scissor(&popup->buffer, nk_null_rect); + nk_end(ctx); + + win->buffer = popup->buffer; + nk_finish_popup(ctx, win); + ctx->current = win; + nk_push_scissor(&win->buffer, win->layout->clip); +} +/* ------------------------------------------------------------- + * + * TOOLTIP + * + * -------------------------------------------------------------- */ +NK_API int +nk_tooltip_begin(struct nk_context *ctx, struct nk_panel *layout, float width) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect bounds; + int ret; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* make sure that no nonblocking popup is currently active */ + win = ctx->current; + in = &ctx->input; + if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK)) + return 0; + + bounds.w = width; + bounds.h = nk_null_rect.h; + bounds.x = (in->mouse.pos.x + 1) - win->layout->clip.x; + bounds.y = (in->mouse.pos.y + 1) - win->layout->clip.y; + + ret = nk_popup_begin(ctx, layout, NK_POPUP_DYNAMIC, + "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); + if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + win->popup.type = NK_PANEL_TOOLTIP; + layout->type = NK_PANEL_TOOLTIP; + return ret; +} + +NK_API void +nk_tooltip_end(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + nk_popup_close(ctx); + nk_popup_end(ctx); +} + +NK_API void +nk_tooltip(struct nk_context *ctx, const char *text) +{ + const struct nk_style *style; + struct nk_vec2 padding; + struct nk_panel layout; + + int text_len; + float text_width; + float text_height; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(text); + if (!ctx || !ctx->current || !ctx->current->layout || !text) + return; + + /* fetch configuration data */ + style = &ctx->style; + padding = style->window.padding; + + /* calculate size of the text and tooltip */ + text_len = nk_strlen(text); + text_width = style->font->width(style->font->userdata, + style->font->height, text, text_len); + text_width += (4 * padding.x); + text_height = (style->font->height + 2 * padding.y); + + /* execute tooltip and fill with text */ + if (nk_tooltip_begin(ctx, &layout, (float)text_width)) { + nk_layout_row_dynamic(ctx, (float)text_height, 1); + nk_text(ctx, text, text_len, NK_TEXT_LEFT); + nk_tooltip_end(ctx); + } +} +/* ------------------------------------------------------------- + * + * CONTEXTUAL + * + * -------------------------------------------------------------- */ +NK_API int +nk_contextual_begin(struct nk_context *ctx, struct nk_panel *layout, + nk_flags flags, struct nk_vec2 size, struct nk_rect trigger_bounds) +{ + struct nk_window *win; + struct nk_window *popup; + struct nk_rect body; + + NK_STORAGE const struct nk_rect null_rect = {0,0,0,0}; + int is_clicked = 0; + int is_active = 0; + int is_open = 0; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + ++win->popup.con_count; + + /* check if currently active contextual is active */ + popup = win->popup.win; + is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL); + is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds); + if (win->popup.active_con && win->popup.con_count != win->popup.active_con) + return 0; + if ((is_clicked && is_open && !is_active) || (!is_open && !is_active && !is_clicked)) + return 0; + + /* calculate contextual position on click */ + win->popup.active_con = win->popup.con_count; + if (is_clicked) { + body.x = ctx->input.mouse.pos.x; + body.y = ctx->input.mouse.pos.y; + } else { + body.x = popup->bounds.x; + body.y = popup->bounds.y; + } + body.w = size.x; + body.h = size.y; + + /* start nonblocking contextual popup */ + ret = nk_nonblock_begin(layout, ctx, flags|NK_WINDOW_NO_SCROLLBAR, body, + null_rect, NK_PANEL_CONTEXTUAL); + if (ret) win->popup.type = NK_PANEL_CONTEXTUAL; + else { + win->popup.active_con = 0; + if (win->popup.win) + win->popup.win->flags = 0; + } + return ret; +} + +NK_API int +nk_contextual_item_text(struct nk_context *ctx, const char *text, int len, + nk_flags alignment) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, style->font)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_text(ctx, label, nk_strlen(label), align);} + +NK_API int +nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, + img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)){ + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align);} + +NK_API int +nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, nk_flags align) +{return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align);} + +NK_API void +nk_contextual_close(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + if (!ctx->current) + return; + nk_popup_close(ctx); +} + +NK_API void +nk_contextual_end(struct nk_context *ctx) +{ + struct nk_window *popup; + struct nk_panel *panel; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + panel = popup->layout; + NK_ASSERT(popup->parent); + NK_ASSERT(panel->type & NK_PANEL_SET_POPUP); + if (panel->flags & NK_WINDOW_DYNAMIC) { + /* Close behavior + This is a bit hack solution since we do not now before we end our popup + how big it will be. We therefore do not directly now when a + click outside the non-blocking popup must close it at that direct frame. + Instead it will be closed in the next frame.*/ + struct nk_rect body = {0,0,0,0}; + if (panel->at_y < (panel->bounds.y + panel->bounds.h)) { + struct nk_vec2 padding = nk_panel_get_padding(&ctx->style, panel->type); + body = panel->bounds; + body.y = (panel->at_y + panel->footer_height + panel->border + padding.y); + body.h = (panel->bounds.y + panel->bounds.h) - body.y; + } + + {int pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); + int in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + if (pressed && in_body) + popup->flags |= NK_WINDOW_HIDDEN; + } + } + if (popup->flags & NK_WINDOW_HIDDEN) + popup->seq = 0; + nk_popup_end(ctx); + return; +} +/* ------------------------------------------------------------- + * + * COMBO + * + * --------------------------------------------------------------*/ +NK_INTERN int +nk_combo_begin(struct nk_panel *layout, struct nk_context *ctx, struct nk_window *win, + struct nk_vec2 size, int is_clicked, struct nk_rect header) +{ + struct nk_window *popup; + int is_open = 0; + int is_active = 0; + struct nk_rect body; + nk_hash hash; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + popup = win->popup.win; + body.x = header.x; + body.w = size.x; + body.y = header.y + header.h-ctx->style.window.combo_border; + body.h = size.y; + + hash = win->popup.combo_count++; + is_open = (popup) ? nk_true:nk_false; + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_COMBO); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(layout, ctx, 0, body, + (is_clicked && is_open)?nk_rect(0,0,0,0):header, NK_PANEL_COMBO)) return 0; + + win->popup.type = NK_PANEL_COMBO; + win->popup.name = hash; + return 1; +} + +NK_API int +nk_combo_begin_text(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, int len, struct nk_vec2 size) +{ + const struct nk_input *in; + struct nk_window *win; + struct nk_style *style; + + enum nk_widget_layout_states s; + int is_clicked = nk_false; + struct nk_rect header; + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(selected); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !selected) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), style->combo.rounding, + background->data.color); + } + { + /* print currently selected text item */ + struct nk_rect label; + struct nk_rect button; + struct nk_rect content; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw selected label */ + text.padding = nk_vec2(0,0); + label.x = header.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = button.x - (style->combo.content_padding.x + style->combo.spacing.x) - label.x;; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, + NK_TEXT_LEFT, ctx->style.font); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(layout, ctx, win, size, is_clicked, header); +} + +NK_API int nk_combo_begin_label(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, struct nk_vec2 size) +{return nk_combo_begin_text(ctx, layout, selected, nk_strlen(selected), size);} + +NK_API int +nk_combo_begin_color(struct nk_context *ctx, struct nk_panel *layout, + struct nk_color color, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image,nk_white); + } else { + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect bounds; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw color */ + bounds.h = header.h - 4 * style->combo.content_padding.y; + bounds.y = header.y + 2 * style->combo.content_padding.y; + bounds.x = header.x + 2 * style->combo.content_padding.x; + bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x; + nk_fill_rect(&win->buffer, bounds, 0, color); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(layout, ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_symbol(struct nk_context *ctx, struct nk_panel *layout, + enum nk_symbol_type symbol, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color sym_background; + struct nk_color symbol_color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_hover; + } + + if (background->type == NK_STYLE_ITEM_IMAGE) { + sym_background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + sym_background = background->data.color; + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw symbol */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + nk_draw_symbol(&win->buffer, symbol, bounds, sym_background, symbol_color, + 1.0f, style->font); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(layout, ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_symbol_text(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, int len, enum nk_symbol_type symbol, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color symbol_color; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + + /* draw symbol */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_symbol(&win->buffer, symbol, image, text.background, symbol_color, + 1.0f, style->font); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = (button.x - style->combo.content_padding.x) - label.x; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); + } + return nk_combo_begin(layout, ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_image(struct nk_context *ctx, struct nk_panel *layout, + struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw image */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + nk_draw_image(&win->buffer, bounds, &img, nk_white); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(layout, ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_image_text(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, int len, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, 0, style->combo.border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(header, 1), 0, + background->data.color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + + /* draw image */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_image(&win->buffer, image, &img, nk_white); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = (button.x - style->combo.content_padding.x) - label.x; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); + } + return nk_combo_begin(layout, ctx, win, size, is_clicked, header); +} + +NK_API int nk_combo_begin_symbol_label(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, enum nk_symbol_type type, struct nk_vec2 size) +{return nk_combo_begin_symbol_text(ctx, layout, selected, nk_strlen(selected), type, size);} + +NK_API int nk_combo_begin_image_label(struct nk_context *ctx, struct nk_panel *layout, + const char *selected, struct nk_image img, struct nk_vec2 size) +{return nk_combo_begin_image_text(ctx, layout, selected, nk_strlen(selected), img, size);} + +NK_API int nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align) +{return nk_contextual_item_text(ctx, text, len, align);} + +NK_API int nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_label(ctx, label, align);} + +NK_API int nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, + int len, nk_flags alignment) +{return nk_contextual_item_image_text(ctx, img, text, len, alignment);} + +NK_API int nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *text, nk_flags alignment) +{return nk_contextual_item_image_label(ctx, img, text, alignment);} + +NK_API int nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags alignment) +{return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment);} + +NK_API int nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags alignment) +{return nk_contextual_item_symbol_label(ctx, sym, label, alignment);} + +NK_API void nk_combo_end(struct nk_context *ctx) +{nk_contextual_end(ctx);} + +NK_API void nk_combo_close(struct nk_context *ctx) +{nk_contextual_close(ctx);} + +NK_API int +nk_combo(struct nk_context *ctx, const char **items, int count, + int selected, int item_height, struct nk_vec2 size) +{ + int i = 0; + int max_height; + struct nk_panel combo; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + + NK_ASSERT(ctx); + NK_ASSERT(items); + NK_ASSERT(ctx->current); + if (!ctx || !items ||!count) + return selected; + + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + if (nk_combo_begin_label(ctx, &combo, items[selected], size)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API int +nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separator, + int separator, int selected, int count, int item_height, struct nk_vec2 size) +{ + int i; + int max_height; + struct nk_panel combo; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + const char *current_item; + const char *iter; + int length = 0; + + NK_ASSERT(ctx); + NK_ASSERT(items_separated_by_separator); + if (!ctx || !items_separated_by_separator) + return selected; + + /* calculate popup window */ + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + + /* find selected item */ + current_item = items_separated_by_separator; + for (i = 0; i < selected; ++i) { + iter = current_item; + while (*iter != separator) iter++; + length = (int)(iter - current_item); + current_item = iter + 1; + } + + if (nk_combo_begin_text(ctx, &combo, current_item, length, size)) { + current_item = items_separated_by_separator; + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + iter = current_item; + while (*iter != separator) iter++; + length = (int)(iter - current_item); + if (nk_combo_item_text(ctx, current_item, length, NK_TEXT_LEFT)) + selected = i; + current_item = current_item + length + 1; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API int +nk_combo_string(struct nk_context *ctx, const char *items_separated_by_zeros, + int selected, int count, int item_height, struct nk_vec2 size) +{return nk_combo_separator(ctx, items_separated_by_zeros, '\0', selected, count, item_height, size);} + +NK_API int +nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const char**), + void *userdata, int selected, int count, int item_height, struct nk_vec2 size) +{ + int i; + int max_height; + struct nk_panel combo; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + const char *item; + + NK_ASSERT(ctx); + NK_ASSERT(item_getter); + if (!ctx || !item_getter) + return selected; + + /* calculate popup window */ + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + + item_getter(userdata, selected, &item); + if (nk_combo_begin_label(ctx, &combo, item, size)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + item_getter(userdata, i, &item); + if (nk_combo_item_label(ctx, item, NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API void nk_combobox(struct nk_context *ctx, const char **items, int count, + int *selected, int item_height, struct nk_vec2 size) +{*selected = nk_combo(ctx, items, count, *selected, item_height, size);} + +NK_API void nk_combobox_string(struct nk_context *ctx, const char *items_separated_by_zeros, + int *selected, int count, int item_height, struct nk_vec2 size) +{*selected = nk_combo_string(ctx, items_separated_by_zeros, *selected, count, item_height, size);} + +NK_API void nk_combobox_separator(struct nk_context *ctx, const char *items_separated_by_separator, + int separator,int *selected, int count, int item_height, struct nk_vec2 size) +{*selected = nk_combo_separator(ctx, items_separated_by_separator, separator, + *selected, count, item_height, size);} + +NK_API void nk_combobox_callback(struct nk_context *ctx, + void(*item_getter)(void* data, int id, const char **out_text), + void *userdata, int *selected, int count, int item_height, struct nk_vec2 size) +{*selected = nk_combo_callback(ctx, item_getter, userdata, *selected, count, item_height, size);} + +/* + * ------------------------------------------------------------- + * + * MENU + * + * -------------------------------------------------------------- + */ +NK_INTERN int +nk_menu_begin(struct nk_panel *layout, struct nk_context *ctx, struct nk_window *win, + const char *id, int is_clicked, struct nk_rect header, struct nk_vec2 size) +{ + int is_open = 0; + int is_active = 0; + struct nk_rect body; + struct nk_window *popup; + nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_PANEL_MENU); + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + body.x = header.x; + body.w = size.x; + body.y = header.y + header.h; + body.h = size.y; + + popup = win->popup.win; + is_open = popup ? nk_true : nk_false; + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_MENU); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(layout, ctx, NK_WINDOW_NO_SCROLLBAR, body, header, NK_PANEL_MENU)) + return 0; + + win->popup.type = NK_PANEL_MENU; + win->popup.name = hash; + return 1; +} + +NK_API int +nk_menu_begin_text(struct nk_context *ctx, struct nk_panel *layout, + const char *title, int len, nk_flags align, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header, + title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, title, is_clicked, header, size); +} + +NK_API int nk_menu_begin_label(struct nk_context *ctx, struct nk_panel *layout, + const char *text, nk_flags align, struct nk_vec2 size) +{return nk_menu_begin_text(ctx, layout, text, nk_strlen(text), align, size);} + +NK_API int +nk_menu_begin_image(struct nk_context *ctx, struct nk_panel *layout, + const char *id, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header, + img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, id, is_clicked, header, size); +} + +NK_API int +nk_menu_begin_symbol(struct nk_context *ctx, struct nk_panel *layout, + const char *id, enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, + sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, id, is_clicked, header, size); +} + +NK_API int +nk_menu_begin_image_text(struct nk_context *ctx, struct nk_panel *layout, + const char *title, int len, nk_flags align, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) + is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, title, is_clicked, header, size); +} + +NK_API int nk_menu_begin_image_label(struct nk_context *ctx, struct nk_panel *layout, + const char *title, nk_flags align, struct nk_image img, struct nk_vec2 size) +{return nk_menu_begin_image_text(ctx, layout, title, nk_strlen(title), align, img, size);} + +NK_API int +nk_menu_begin_symbol_text(struct nk_context *ctx, struct nk_panel *layout, + const char *title, int len, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, + header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) is_clicked = nk_true; + return nk_menu_begin(layout, ctx, win, title, is_clicked, header, size); +} + +NK_API int nk_menu_begin_symbol_label(struct nk_context *ctx, struct nk_panel *layout, + const char *title, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size ) +{return nk_menu_begin_symbol_text(ctx, layout, title, nk_strlen(title), align,sym,size);} + +NK_API int nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align) +{return nk_contextual_item_text(ctx, title, len, align);} + +NK_API int nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_label(ctx, label, align);} + +NK_API int nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_contextual_item_image_label(ctx, img, label, align);} + +NK_API int nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{return nk_contextual_item_image_text(ctx, img, text, len, align);} + +NK_API int nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags align) +{return nk_contextual_item_symbol_text(ctx, sym, text, len, align);} + +NK_API int nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags align) +{return nk_contextual_item_symbol_label(ctx, sym, label, align);} + +NK_API void nk_menu_close(struct nk_context *ctx) +{nk_contextual_close(ctx);} + +NK_API void +nk_menu_end(struct nk_context *ctx) +{nk_contextual_end(ctx);} + +#endif \ No newline at end of file diff --git a/test_data/sample_files/parser_test1.cpp b/test_data/sample_files/parser_test1.cpp new file mode 100644 index 0000000..be607f4 --- /dev/null +++ b/test_data/sample_files/parser_test1.cpp @@ -0,0 +1,26 @@ +/* + * THIS TESTS: + * - #define errors + * - attempting to invoke bad #define + */ + +#define +#define "THIS IS BAD" +#define GOT_BAD_PAREN1( +#define GOT_BAD_PAREN2(a +#define GOT_BAD_PAREN3(a, +#define GOT_BAD_PAREN4(a,) +#define GOT_BAD_PAREN5(,b) +#define GOT_BAD_PAREN6(+a,b) +#define GOT_BAD_PAREN7(a b) +#define GOT_BAD_PAREN8(a,+b) + +GOT_BAD_PAREN1(); +GOT_BAD_PAREN2(); +GOT_BAD_PAREN3(); +GOT_BAD_PAREN4(); +GOT_BAD_PAREN5(); +GOT_BAD_PAREN6(); +GOT_BAD_PAREN7(); +GOT_BAD_PAREN8(); + diff --git a/test_data/sample_files/parser_test1_sub2.h b/test_data/sample_files/parser_test1_sub2.h new file mode 100644 index 0000000..0cd72fd --- /dev/null +++ b/test_data/sample_files/parser_test1_sub2.h @@ -0,0 +1,8 @@ +/* + * THIS TESTS: + * - #include ing a file in the same folder that doesn't contain the unit's target file + */ + +OTHER_STUFF_FUNC(sub2); + +#include "parser_test1_sub3.h" diff --git a/test_data/sample_files/parser_test1_sub3.h b/test_data/sample_files/parser_test1_sub3.h new file mode 100644 index 0000000..9b2f462 --- /dev/null +++ b/test_data/sample_files/parser_test1_sub3.h @@ -0,0 +1,7 @@ +/* + * THIS TESTS: + * - #include ing a file in the same folder that doesn't contain the unit's target file + */ + +OTHER_STUFF_FUNC(sub3); + diff --git a/test_data/sample_files/parser_test2.cpp b/test_data/sample_files/parser_test2.cpp new file mode 100644 index 0000000..5166a6d --- /dev/null +++ b/test_data/sample_files/parser_test2.cpp @@ -0,0 +1,29 @@ +/* + * THIS TESTS: + * - good #define + * - invoking good #define + * - using macro in arguments to itself + * - invoking macro with parens from a different expansion level + * - macro definitions with comments + */ + +#define EMPTY +#define EMPTY_WITH_PAREN() +#define GOOD_NO_PAREN /*DON'T DUPLICATE ME*/ FoodBard +#define GOOD_WITH_PAREN() /*DON'T DUPLICATE ME*/ SuperDuper EMPTY +#define GOOD_WITH_ARG(a/*DON'T FRET ME*/) /*DON'T DUPLICATE ME*/ (a + 1) EMPTY_WITH_PAREN() +#define MASK GOOD_WITH_ARG(10) +#define UNUSUAL() 5 * GOOD_WITH_PAREN + +GOOD_NO_PAREN; +EMPTY_WITH_PAREN; +GOOD_WITH_PAREN; +GOOD_WITH_PAREN(); +GOOD_WITH_ARG(2); + +GOOD_WITH_ARG(3*MASK); +UNUSUAL()(); + +GOOD_WITH_PAREN + + diff --git a/test_data/sample_files/parser_test3.cpp b/test_data/sample_files/parser_test3.cpp new file mode 100644 index 0000000..3b0043b --- /dev/null +++ b/test_data/sample_files/parser_test3.cpp @@ -0,0 +1,12 @@ +/* + * THIS TESTS: + * - wrong number of arguments to macro + */ + +#define NoArg() X +#define OneArg(a) (a + 1) + +NoArg(5); +OneArg(); + + diff --git a/test_data/sample_files/parser_test4.cpp b/test_data/sample_files/parser_test4.cpp new file mode 100644 index 0000000..7c4ad2e --- /dev/null +++ b/test_data/sample_files/parser_test4.cpp @@ -0,0 +1,16 @@ +/* + * THIS TESTS: + * - recursive macros + */ + +#define M(m,n) n + m(M,n-1) + +M(M,7); + +#define X a + X +#define Y b + Z +#define Z c + Y + +X; +Y; + diff --git a/test_data/sample_files/parser_test5.cpp b/test_data/sample_files/parser_test5.cpp new file mode 100644 index 0000000..e72d654 --- /dev/null +++ b/test_data/sample_files/parser_test5.cpp @@ -0,0 +1,17 @@ +/* + * THIS TESTS: + * - putting stringify at the end of the body + */ + +#define M(a) show(#a) +#define N(a) show(#a" = ", a) +#define FOO 10 + +M(x); +N(y); +N(FOO); + +M("hello\n"); +M('\\' + ""); +M(L"string"); + diff --git a/test_data/sample_files/parser_test6.cpp b/test_data/sample_files/parser_test6.cpp new file mode 100644 index 0000000..1bae6dd --- /dev/null +++ b/test_data/sample_files/parser_test6.cpp @@ -0,0 +1,10 @@ +/* + * THIS TESTS: + * - putting stringify at the end of the body + */ + +#define Q(a) a + # + +Q(z) + + diff --git a/test_data/sample_files/parser_test7.cpp b/test_data/sample_files/parser_test7.cpp new file mode 100644 index 0000000..ef597eb --- /dev/null +++ b/test_data/sample_files/parser_test7.cpp @@ -0,0 +1,14 @@ +/* + * THIS TESTS: + * - true compliant variadic macro + * (for the various extensions test elsewhere!) + * - variadic macro, properly defined + * - variadic macro, properly invoked + * - variadic macro, properly invoked with blank parameter + */ + +#define DOSHOW(str, ...) show("SUPER: "str"\n", __VA_ARGS__) + +DOSHOW("%d, %d", x, y); + +DOSHOW("else",); diff --git a/test_data/sample_files/parser_test8.cpp b/test_data/sample_files/parser_test8.cpp new file mode 100644 index 0000000..19590f4 --- /dev/null +++ b/test_data/sample_files/parser_test8.cpp @@ -0,0 +1,15 @@ +s/* + * THIS TESTS: + * - true compliant variadic macro + * (for the various extensions test elsewhere!) + * - variadic macro, improperly invoked + * - variadic macro, improperly defined + */ + +#define DOSHOW(str, ...) show("SUPER: "str"\n", __VA_ARGS__) + +DOSHOW("something"); + +#define DONTSHOW(str, ..., b) show("SUPER: "str" %d\n", __VA_ARGS__, b) + +DONTSHOW("heyo! %f", 0.5, 10); diff --git a/test_data/sample_files/parser_test9.cpp b/test_data/sample_files/parser_test9.cpp new file mode 100644 index 0000000..24ea6ba --- /dev/null +++ b/test_data/sample_files/parser_test9.cpp @@ -0,0 +1,29 @@ +/* + * THIS TESTS: + * - pasting two non-arguments + * - pasting argument to non-argument + * - pasting non-argument to argument + * - pasting two arguments + * - pasting arguments with extended bodies + * - applying both paste and not to one argument + * UNFINISHED + * - putting paste at the beginning of the body + * - putting paste at the end of the body + */ + +#define NN() food##bard +#define AN(a) a##_counter +#define NA(a) ptr_##a +#define AA(a,b) a##b + +NN(); +AN(T1); +NA(T2); +AA(C,D) + +#define S1 QQ +#define S2 RR + +AN(x += S1); +NA(x += S2); +AA(x += S1, y += S2); diff --git a/test_data/sample_files/parser_test_gcc.cpp b/test_data/sample_files/parser_test_gcc.cpp new file mode 100644 index 0000000..3f06cd6 --- /dev/null +++ b/test_data/sample_files/parser_test_gcc.cpp @@ -0,0 +1,97 @@ +/* Copyright (C) 2000 Free Software Foundation, Inc. */ + +/* Test the full range of preprocessor operator precedence. Each + operator is tested with one of immediately higher precedence to + verify it is of strictly lower precedence. To avoid complications, + each test uses just those two operators. Occasionally this assumes + correct operation of if-then-else, so the first tests verify this. */ + +/* { dg-do preprocess } */ + +/* Ensure correct functioning of if-then-else. */ +#if 1 +#else +#error #else block evaluated for true conditional +#endif + +#if 0 +#error #if block evaluated for false conditional +#else +#endif + +/* , not higher than ?. This is not a syntax error if it is. */ +#if 1 ? 0, 1: 1 /* { dg-error "syntax" "? higher precedence than ," } */ +#error +#endif + +/* : strictly higher than ?. This would give a syntax error otherwise. */ +#if 0 ? 0 : 1 ? 1 : 1 +#endif + +/* || strictly higher than ?:. */ +#if 1 ? 0: 0 || 1 +#error operator ?: has higher precedence than operator || +#endif + +/* && strictly higher than ||. */ +#if 1 || 0 && 0 +#else +#error operator || has higher precedence than operator && +#endif + +/* | strictly higher than &&. */ +#if 0 && 0 | 1 +#error operator && has higher precedence than operator | +#endif + +/* ^ strictly higher than |. */ +#if 1 | 0 ^ 1 +#else +#error operator | has higher precedence than operator ^ +#endif + +/* & strictly higher than ^. */ +#if 1 ^ 0 & 0 +#else +#error operator ^ has higher precedence than operator & +#endif + +/* == (!=) strictly higher than &. */ +#if 0 & 0 == 0 +#error operator & has higher precedence than operator == +#endif + +/* < (>, <=, >=) strictly higher than == (!=). */ + +#if 0 == 0 < 0 +#else +#error operator == has higher precedence than operator < +#endif + +/* << (>>) strictly higher than < (>, <=, >=). */ +#if 1 < 1 << 1 +#else +#error operator < has higher precedence than operator << +#endif + +/* Binary + (-) strictly higher than << (>>). */ +#if 0 << 0 + 1 +#error operator << has higher precedence than binary + +#endif + +/* Binary * (/, %) strictly higher than binary + (-). */ +#if 1 + 0 * 0 +#else +#error binary + has higher precedence than binary * +#endif + +/* Unary operators (!, ~, -, +) strictly higher than binary * (/, %). + Equality is hard to detect because of right-associativity. */ +#if ~1 * 0 +#error binary * has higher precedence than operator ~ +#endif + +/* () > Unary. Unfortunately this requires an additional operator. */ +#if -(1 - 1) +#error unary - has higher precedence than operator () +#endif diff --git a/test_data/sample_files/parser_test_stress1.cpp b/test_data/sample_files/parser_test_stress1.cpp new file mode 100644 index 0000000..320cc9f --- /dev/null +++ b/test_data/sample_files/parser_test_stress1.cpp @@ -0,0 +1,58 @@ +/* + * THIS TESTS: + * - this is a memory stress test + */ + +#define FOO ZZZ +#define BAR YYY + +#define ZZZ 1 + YYY +#define YYY 2 + ZZZ + +#define A(word) (word) +#define B(word) (word + A(word)) +#define C(word) (B(word) + A(word)) +#define D(m, word) (C(word) * m(word)) +#define E(a,b) (D(C, a##b) + a##_longer_thing) +#define F(a,b) (D(A, #a) + (D(B, #b)) + (E(b,a))) + +F(0,1); +F(FOO, BAR); +F(FOO + BAR + 10, BAR * 10 + FOO); + +#define Empty +#define G1(x) G2 Empty (x) +#define G2(x) G3 Empty (x) +#define G3(x) G4 Empty (x) +#define G4(x) G5 Empty (x) +#define G5(x) G6 Empty (x) +#define G6(x) G7 Empty (x) +#define G7(x) G8 Empty (x) +#define G8(x) G9 Empty (x) +#define G9(x) (x + x) +#define Expand(x) x + +G1(0); +Expand(G1(1)); +Expand(Expand(G1(2))); +Expand(Expand(Expand(G1(3)))); +Expand(Expand(Expand(Expand(G1(4))))); +Expand(Expand(Expand(Expand(Expand(G1(5)))))); +Expand(Expand(Expand(Expand(Expand(Expand(G1(6))))))); +Expand(Expand(Expand(Expand(Expand(Expand(Expand(G1(7)))))))); +Expand(Expand(Expand(Expand(Expand(Expand(Expand(Expand(G1(8))))))))); +Expand(Expand(Expand(Expand(Expand(Expand(Expand(Expand(Expand(G1(9)))))))))); + +#define Comma , +#define M(a) N(a) +#define N(a,b) a+b + +M(100 Comma 200); + +#define FOOD(name, ...) show(#__VA_ARGS__, __VA_ARGS__); ++name##_counter; show(#name": %d\n", name##_counter) + +FOOD(bard, aplha, beta, gamma, delta); + +Expand(Expand(Expand(Expand(Expand(Expand(Expand(Expand(Expand(F(FOO + BAR + 10, BAR * 10 + FOO)))))))))); + + diff --git a/test_data/sample_files/primes.cpp b/test_data/sample_files/primes.cpp new file mode 100644 index 0000000..8f32ad5 --- /dev/null +++ b/test_data/sample_files/primes.cpp @@ -0,0 +1,30 @@ + +#include +#include + +void list_primes(int *primes, int max){ + int j = 0; + int x = 2; + while (j < max){ + int composite = false; + for (int i = 0; i < j; ++i){ + if (x % primes[i] == 0){ + composite = true; + } + break; + } + + if (!composite){ + primes[j++] = x; + } + } +} + +int main(){ + int *primes = malloc(1000*sizeof(int)); + list_primes(primes, 1000); + for (int i = 0; i < 1000; ++ i){ + printf("%d\n", primes[i]); + } + return(0); +} \ No newline at end of file diff --git a/test_data/sample_files/raw_string_test.cpp b/test_data/sample_files/raw_string_test.cpp new file mode 100644 index 0000000..d1e7a73 --- /dev/null +++ b/test_data/sample_files/raw_string_test.cpp @@ -0,0 +1,168 @@ +/* + New File;;;; + Stuff +*/ + +inline void +GLSL_InitSpriteSheetProgram(memory_arena *Arena, glsl_program *TheSpriteSheetProgram) +{ + // s32 VertexAttributeCount = ArrayCount(VertexAttributeStrings); +#if R42_SHADER_SET_HOVER && R42_SHADER_SET_POS_DIM + char *Version = R"( + #version 100 + #version 100 + #version 100 + #version 100 + #version 100 + #define R42_SHADER_SET_POS_DIM 1 + #define R42_SHADER_SET_HOVER 1 + )"; +#endif +}; + +internal void +CompileZBiasProgram(zbias_program *Result, b32x DepthPeel) +{ + char Defines[1024]; + FormatString(sizeof(Defines), Defines, + "#version 130\n" + "#define ShaderSimTexWriteSRGB %d\n" + "#define ShaderSimTexReadSRGB %d\n" + "#define DepthPeel %d\n", + OpenGL.ShaderSimTexWriteSRGB, + OpenGL.ShaderSimTexReadSRGB, + DepthPeel); + + char *VertexCode = R"FOO( +// Vertex code +uniform m4x4 Transform; + +uniform v3 CameraP; +uniform v3 FogDirection; + +in v4 VertP; +in v3 VertN; +in v2 VertUV; +in v4 VertColor; +smooth out v2 FragUV; +smooth out v4 FragColor; +smooth out f32 FogDistance; +smooth out v3 WorldP; +smooth out v3 WorldN; +void main(void) +{ +v4 InVertex = V4(VertP.xyz, 1.0); +f32 ZBias = VertP.w; + +v4 ZVertex = InVertex; +ZVertex.z += ZBias; + +v4 ZMinTransform = Transform*InVertex; +v4 ZMaxTransform = Transform*ZVertex; + +f32 ModifiedZ = (ZMinTransform.w / ZMaxTransform.w)*ZMaxTransform.z; + +gl_Position = vec4(ZMinTransform.x, ZMinTransform.y, ModifiedZ, ZMinTransform.w); + +FragUV = VertUV.xy; +FragColor = VertColor; + +FogDistance = Inner(ZVertex.xyz - CameraP, FogDirection); +WorldP = ZVertex.xyz; +WorldN = VertN; +} +)FOO"; + + char *Fragment = R"FOO( +// Fragment code +//uniform v4 FogColor; +uniform sampler2D TextureSampler; +#if DepthPeel +uniform sampler2D DepthSampler; +#endif +uniform v3 FogColor; +uniform f32 AlphaThreshold; +uniform f32 FogStartDistance; +uniform f32 FogEndDistance; +uniform f32 ClipAlphaStartDistance; +uniform f32 ClipAlphaEndDistance; +uniform v3 CameraP; +uniform v3 LightP; + +smooth in vec2 FragUV; +smooth in vec4 FragColor; +smooth in f32 FogDistance; +smooth in vec3 WorldP; +smooth in vec3 WorldN; + +layout(location = 0) out v4 BlendUnitColor[3]; + +void main(void) +{ +#if DepthPeel +f32 ClipDepth = texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r; +if(gl_FragCoord.z <= ClipDepth) +{ +discard; +} +#endif + +vec4 TexSample = texture(TextureSampler, FragUV); +#if ShaderSimTexReadSRGB +TexSample.rgb *= TexSample.rgb; +#endif + +f32 FogAmount = Clamp01MapToRange(FogStartDistance, FogDistance, FogEndDistance); +f32 AlphaAmount = Clamp01MapToRange(ClipAlphaStartDistance, FogDistance, ClipAlphaEndDistance); +v4 ModColor = AlphaAmount*FragColor*TexSample; + +if(ModColor.a > AlphaThreshold) +{ +v4 SurfaceReflect; +SurfaceReflect.rgb = Lerp(ModColor.rgb, FogAmount, FogColor.rgb); +SurfaceReflect.a = ModColor.a; + +#if ShaderSimTexWriteSRGB +SurfaceReflect.rgb = sqrt(SurfaceReflect.rgb); +#endif + +// TODO(casey): Some way of specifying light params +f32 Lp0 = 0; +f32 Lp1 = 0; +f32 EmitSpread = 1.0f; +v3 Emit = V3(0, 0, 0); +if(length(LightP - WorldP) < 2.0f) +{ +Emit = V3(1, 1, 1); +} + +v2 N2 = PackNormal2(WorldN.xy); + +BlendUnitColor[0] = SurfaceReflect; +BlendUnitColor[1] = V4(Emit.r, Emit.g, Emit.b, EmitSpread); +BlendUnitColor[2] = V4(N2.x, N2.y, Lp0, Lp1); +} +else +{ +discard; +} +} +)FOO"; + + GLuint Prog = OpenGLCreateProgram(Defines, GlobalShaderHeaderCode, VertexCode, FragmentCode, &Result->Common); + OpenGLLinkSamplers(&Result->Common, "TextureSampler", "DepthSampler"); + + Result->TransformID = glGetUniformLocation(Prog, "Transform"); + Result->CameraP = glGetUniformLocation(Prog, "CameraP"); + Result->FogDirection = glGetUniformLocation(Prog, "FogDirection"); + Result->FogColor = glGetUniformLocation(Prog, "FogColor"); + Result->FogStartDistance = glGetUniformLocation(Prog, "FogStartDistance"); + Result->FogEndDistance = glGetUniformLocation(Prog, "FogEndDistance"); + Result->ClipAlphaStartDistance = glGetUniformLocation(Prog, "ClipAlphaStartDistance"); + Result->ClipAlphaEndDistance = glGetUniformLocation(Prog, "ClipAlphaEndDistance"); + Result->AlphaThreshold = glGetUniformLocation(Prog, "AlphaThreshold"); + + Result->DebugLightP = glGetUniformLocation(Prog, "LightP"); +} + + diff --git a/test_data/sample_files/sample.java b/test_data/sample_files/sample.java new file mode 100644 index 0000000..cd8cb9d --- /dev/null +++ b/test_data/sample_files/sample.java @@ -0,0 +1,62 @@ + + +// Demonstrate instanceof operator. +class A { + int i, j; +} +class B { + int i, j; +} +class C extends A { + int k; +} +class D extends A { + int k; +} +#stuff +class InstanceOf { + public static void main(String args[]) { + A a = new A(); + B b = new B(); + C c = new C(); + D d = new D(); + if(a instanceof A) + System.out.println("a is instance of A"); + if(b instanceof B) + System.out.println("b is instance of B"); + if(c instanceof C) + System.out.println("c is instance of C"); + if(c instanceof A) + System.out.println("c can be cast to A"); + if(a instanceof C) + System.out.println("a can be cast to C"); + System.out.println(); + // compare types of derived types + A ob; + ob = d; // A reference to d + System.out.println("ob now refers to d"); + if(ob instanceof D) + System.out.println("ob is instance of D"); + System.out.println(); + ob = c; // A reference to c + System.out.println("ob now refers to c"); + if(ob instanceof D) + System.out.println("ob can be cast to D"); + else + System.out.println("ob cannot be cast to D"); + if(ob instanceof A) + System.out.println("ob can be cast to A"); + else + System.out.println(); + // all objects can be cast to Object + if(a instanceof Object) + System.out.println("a may be cast to Object"); + if(b instanceof Object) + System.out.println("b may be cast to Object"); + if(c instanceof Object) + System.out.println("c may be cast to Object"); + if(d instanceof Object) + System.out.println("d may be cast to Object"); + } +} + diff --git a/test_data/sample_files/sample.rs b/test_data/sample_files/sample.rs new file mode 100644 index 0000000..7a26bf7 --- /dev/null +++ b/test_data/sample_files/sample.rs @@ -0,0 +1,407 @@ + + +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::BinOpToken::*; +pub use self::Nonterminal::*; +pub use self::DelimToken::*; +pub use self::Lit::*; +pub use self::Token::*; + +use ast::{self}; +use ptr::P; +use symbol::keywords; +use tokenstream::TokenTree; + +use std::fmt; +use std::rc::Rc; + +#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] +pub enum BinOpToken { + Plus, + Minus, + Star, + Slash, + Percent, + Caret, + And, + Or, + Shl, + Shr, +} + +/// A delimiter token +#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] +pub enum DelimToken { + /// A round parenthesis: `(` or `)` + Paren, + /// A square bracket: `[` or `]` + Bracket, + /// A curly brace: `{` or `}` + Brace, + /// An empty delimiter + NoDelim, +} + +impl DelimToken { + pub fn len(self) -> usize { + if self == NoDelim { 0 } else { 1 } + } + + pub fn is_empty(self) -> bool { + self == NoDelim + } +} + +#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] +pub enum Lit { + Byte(ast::Name), + Char(ast::Name), + Integer(ast::Name), + Float(ast::Name), + Str_(ast::Name), + StrRaw(ast::Name, usize), /* raw str delimited by n hash symbols */ + ByteStr(ast::Name), + ByteStrRaw(ast::Name, usize), /* raw byte str delimited by n hash symbols */ +} + +impl Lit { + pub fn short_name(&self) -> &'static str { + match *self { + Byte(_) => "byte", + Char(_) => "char", + Integer(_) => "integer", + Float(_) => "float", + Str_(_) | StrRaw(..) => "string", + ByteStr(_) | ByteStrRaw(..) => "byte string" + } + } +} + +fn ident_can_begin_expr(ident: ast::Ident) -> bool { + let ident_token: Token = Ident(ident); + + !ident_token.is_any_keyword() || + ident_token.is_path_segment_keyword() || + [ + keywords::Do.name(), + keywords::Box.name(), + keywords::Break.name(), + keywords::Continue.name(), + keywords::False.name(), + keywords::For.name(), + keywords::If.name(), + keywords::Loop.name(), + keywords::Match.name(), + keywords::Move.name(), + keywords::Return.name(), + keywords::True.name(), + keywords::Unsafe.name(), + keywords::While.name(), + ].contains(&ident.name) +} + +fn ident_can_begin_type(ident: ast::Ident) -> bool { + let ident_token: Token = Ident(ident); + + !ident_token.is_any_keyword() || + ident_token.is_path_segment_keyword() || + [ + keywords::For.name(), + keywords::Impl.name(), + keywords::Fn.name(), + keywords::Unsafe.name(), + keywords::Extern.name(), + keywords::Typeof.name(), + ].contains(&ident.name) +} + +#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)] +pub enum Token { + /* Expression-operator symbols. */ + Eq, + Lt, + Le, + EqEq, + Ne, + Ge, + Gt, + AndAnd, + OrOr, + Not, + Tilde, + BinOp(BinOpToken), + BinOpEq(BinOpToken), + + /* Structural symbols */ + At, + Dot, + DotDot, + DotDotDot, + Comma, + Semi, + Colon, + ModSep, + RArrow, + LArrow, + FatArrow, + Pound, + Dollar, + Question, + /// An opening delimiter, eg. `{` + OpenDelim(DelimToken), + /// A closing delimiter, eg. `}` + CloseDelim(DelimToken), + + /* Literals */ + Literal(Lit, Option), + + /* Name components */ + Ident(ast::Ident), + Underscore, + Lifetime(ast::Ident), + + /* For interpolation */ + Interpolated(Rc), + // Can be expanded into several tokens. + /// Doc comment + DocComment(ast::Name), + // In right-hand-sides of MBE macros: + /// A syntactic variable that will be filled in by macro expansion. + SubstNt(ast::Ident), + + // Junk. These carry no data because we don't really care about the data + // they *would* carry, and don't really want to allocate a new ident for + // them. Instead, users could extract that from the associated span. + + /// Whitespace + Whitespace, + /// Comment + Comment, + Shebang(ast::Name), + + Eof, +} + +impl Token { +/// Returns `true` if the token starts with '>'. +pub fn is_like_gt(&self) -> bool { + match *self { + BinOp(Shr) | BinOpEq(Shr) | Gt | Ge => true, + _ => false, + } +} + +/// Returns `true` if the token can appear at the start of an expression. +pub fn can_begin_expr(&self) -> bool { + match *self { + Ident(ident) => ident_can_begin_expr(ident), // value name or keyword + OpenDelim(..) | // tuple, array or block + Literal(..) | // literal + Not | // operator not + BinOp(Minus) | // unary minus + BinOp(Star) | // dereference + BinOp(Or) | OrOr | // closure + BinOp(And) | // reference + AndAnd | // double reference + DotDot | DotDotDot | // range notation + Lt | BinOp(Shl) | // associated path + ModSep | // global path + Pound => true, // expression attributes + Interpolated(ref nt) => match **nt { + NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true, + _ => false, + }, + _ => false, + } +} + +/// Returns `true` if the token can appear at the start of a type. +pub fn can_begin_type(&self) -> bool { + match *self { + Ident(ident) => ident_can_begin_type(ident), // type name or keyword + OpenDelim(Paren) | // tuple + OpenDelim(Bracket) | // array + Underscore | // placeholder + Not | // never + BinOp(Star) | // raw pointer + BinOp(And) | // reference + AndAnd | // double reference + Question | // maybe bound in trait object + Lifetime(..) | // lifetime bound in trait object + Lt | BinOp(Shl) | // associated path + ModSep => true, // global path + Interpolated(ref nt) => match **nt { + NtIdent(..) | NtTy(..) | NtPath(..) => true, + _ => false, + }, + _ => false, + } +} + +/// Returns `true` if the token is any literal +pub fn is_lit(&self) -> bool { + match *self { + Literal(..) => true, + _ => false, + } + } + + pub fn ident(&self) -> Option { + match *self { + Ident(ident) => Some(ident), + Interpolated(ref nt) => match **nt { + NtIdent(ident) => Some(ident.node), + _ => None, + }, + _ => None, + } + } + + /// Returns `true` if the token is an identifier. + pub fn is_ident(&self) -> bool { + self.ident().is_some() + } + + /// Returns `true` if the token is a documentation comment. + pub fn is_doc_comment(&self) -> bool { + match *self { + DocComment(..) => true, + _ => false, + } + } + + /// Returns `true` if the token is interpolated. + pub fn is_interpolated(&self) -> bool { + match *self { + Interpolated(..) => true, + _ => false, + } + } + + /// Returns `true` if the token is an interpolated path. + pub fn is_path(&self) -> bool { + if let Interpolated(ref nt) = *self { + if let NtPath(..) = **nt { + return true; + } + } + false + } + + /// Returns `true` if the token is a lifetime. + pub fn is_lifetime(&self) -> bool { + match *self { + Lifetime(..) => true, + _ => false, + } + } + + /// Returns `true` if the token is either the `mut` or `const` keyword. + pub fn is_mutability(&self) -> bool { + self.is_keyword(keywords::Mut) || + self.is_keyword(keywords::Const) + } + + pub fn is_qpath_start(&self) -> bool { + self == &Lt || self == &BinOp(Shl) + } + + pub fn is_path_start(&self) -> bool { + self == &ModSep || self.is_qpath_start() || self.is_path() || + self.is_path_segment_keyword() || self.is_ident() && !self.is_any_keyword() + } + + /// Returns `true` if the token is a given keyword, `kw`. + pub fn is_keyword(&self, kw: keywords::Keyword) -> bool { + self.ident().map(|ident| ident.name == kw.name()).unwrap_or(false) + } + + pub fn is_path_segment_keyword(&self) -> bool { + match self.ident() { + Some(id) => id.name == keywords::Super.name() || + id.name == keywords::SelfValue.name() || + id.name == keywords::SelfType.name(), + None => false, + } + } + + /// Returns `true` if the token is either a strict or reserved keyword. + pub fn is_any_keyword(&self) -> bool { + self.is_strict_keyword() || self.is_reserved_keyword() + } + + /// Returns `true` if the token is a strict keyword. + pub fn is_strict_keyword(&self) -> bool { + match self.ident() { + Some(id) => id.name >= keywords::As.name() && id.name <= keywords::While.name(), + _ => false, + } + } + + /// Returns `true` if the token is a keyword reserved for possible future use. + pub fn is_reserved_keyword(&self) -> bool { + match self.ident() { + Some(id) => id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name(), + _ => false, + } + } + } + + #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)] + /// For interpolation during macro expansion. + pub enum Nonterminal { + NtItem(P), + NtBlock(P), + NtStmt(ast::Stmt), + NtPat(P), + NtExpr(P), + NtTy(P), + NtIdent(ast::SpannedIdent), + /// Stuff inside brackets for attributes + NtMeta(ast::MetaItem), + NtPath(ast::Path), + NtVis(ast::Visibility), + NtTT(TokenTree), + // These are not exposed to macros, but are used by quasiquote. + NtArm(ast::Arm), + NtImplItem(ast::ImplItem), + NtTraitItem(ast::TraitItem), + NtGenerics(ast::Generics), + NtWhereClause(ast::WhereClause), + NtArg(ast::Arg), + } + + impl fmt::Debug for Nonterminal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + NtItem(..) => f.pad("NtItem(..)"), + NtBlock(..) => f.pad("NtBlock(..)"), + NtStmt(..) => f.pad("NtStmt(..)"), + NtPat(..) => f.pad("NtPat(..)"), + NtExpr(..) => f.pad("NtExpr(..)"), + NtTy(..) => f.pad("NtTy(..)"), + NtIdent(..) => f.pad("NtIdent(..)"), + NtMeta(..) => f.pad("NtMeta(..)"), + NtPath(..) => f.pad("NtPath(..)"), + NtTT(..) => f.pad("NtTT(..)"), + NtArm(..) => f.pad("NtArm(..)"), + NtImplItem(..) => f.pad("NtImplItem(..)"), + NtTraitItem(..) => f.pad("NtTraitItem(..)"), + NtGenerics(..) => f.pad("NtGenerics(..)"), + NtWhereClause(..) => f.pad("NtWhereClause(..)"), + NtArg(..) => f.pad("NtArg(..)"), + NtVis(..) => f.pad("NtVis(..)"), + } + } + } + + \ No newline at end of file diff --git a/test_data/sample_files/saveas.txt b/test_data/sample_files/saveas.txt new file mode 100644 index 0000000..163524b --- /dev/null +++ b/test_data/sample_files/saveas.txt @@ -0,0 +1,2935 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +/* + * Quick and Dirty Partition System + */ + +struct Partition_Cursor{ + u8 *memory_base; + u8 *memory_cursor; + i32 max_size; +}; + +internal Partition_Cursor +partition_open(void *memory, i32 size){ + Partition_Cursor partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory; + partition.max_size = size; + return partition; +} + +internal void* +partition_allocate(Partition_Cursor *data, i32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +/* + * Plaform Independent File Name Helpers + */ + +struct File_List_Iterator{ + b32 folder_stage; + u8 **filename_ptr; +}; + +internal File_List_Iterator +files_iterator_init(File_List *files){ + File_List_Iterator files_it = {}; + if (files->folder_count > 0){ + files_it.filename_ptr = files->folder_names; + files_it.folder_stage = 1; + } + else if (files->file_count > 0){ + files_it.filename_ptr = files->file_names; + } + return files_it; +} + +internal void +files_iterator_step(File_List *files, File_List_Iterator *files_it){ + ++files_it->filename_ptr; + if (files_it->folder_stage){ + if (files_it->filename_ptr >= files->folder_names + files->folder_count){ + if (files->file_count > 0){ + files_it->filename_ptr = files->file_names; + } + else{ + files_it->filename_ptr = 0; + } + files_it->folder_stage = 0; + } + } + else{ + if (files_it->filename_ptr >= files->file_names + files->file_count){ + files_it->filename_ptr = 0; + } + } +} + +/* + * Drawing Functions + */ + +internal u32 +style_token_color(Editing_Style *style, + Cpp_Token_Type type){ + u32 result; + switch (type){ + case CPP_TOKEN_COMMENT: + result = style->comment_color; + break; + + case CPP_TOKEN_KEYWORD: + result = style->keyword_color; + break; + + case CPP_TOKEN_STRING_CONSTANT: + case CPP_TOKEN_CHARACTER_CONSTANT: + case CPP_TOKEN_INTEGER_CONSTANT: + case CPP_TOKEN_FLOATING_CONSTANT: + case CPP_TOKEN_BOOLEAN_CONSTANT: + case CPP_TOKEN_INCLUDE_FILE: + result = style->constant_color; + break; + + default: + result = style->default_color; + } + return result; +} + +internal void +panel_draw(Thread_Context *thread, Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + Editing_File *file = panel->file; + Editing_Style *style = file->style; + Font *font = style->font; + + i32 character_w = (i32)(style_get_character_width(style)); + i32 character_h = (i32)(font->line_skip); + i32 offset_x = (i32)(panel->x); + i32 offset_y = (i32)(panel->y); + i32 max_x = (i32)(panel->w); + i32 max_y = (i32)(panel->h); + + Blit_Rect panel_area; + panel_area.x_start = offset_x; + panel_area.y_start = offset_y; + panel_area.x_end = offset_x + max_x; + panel_area.y_end = offset_y + max_y; + + if (!file || !file->data || file->is_dummy){ + i32 start_x = (panel_area.x_start + panel_area.x_end)/2; + i32 start_y = (panel_area.y_start + panel_area.y_end)/2; + persist String null_file_message = make_lit_string("NULL FILE"); + start_x -= (character_w*null_file_message.size)/2; + start_y -= (character_h)/2; + + real32 pos_x = 0; + real32 pos_y = 0; + + for (i32 i = 0; i < null_file_message.size; ++i){ + u8 to_render = null_file_message.str[i]; + + if (font->glyphs[to_render].data){ + font_draw_glyph_clipped(target, font, to_render, + (real32)start_x + pos_x, (real32)start_y + pos_y, + style->special_character_color, + panel_area); + pos_x += character_w; + } + } + } + + else{ + u32 tab_width = style->tab_width; + i32 size = (i32)file->size; + u8 *data = (u8*)file->data; + + real32 shift_x = 0; + shift_x = -panel->scroll_x * character_w; + + i32 truncated_start_y = (i32)panel->scroll_y; + real32 scroll_amount = panel->scroll_y - truncated_start_y; + + Panel_Cursor_Data start_cursor; + start_cursor = panel->scroll_y_cursor; + start_cursor = panel_compute_cursor_from_xy(panel, 0, truncated_start_y); + panel->scroll_y_cursor = start_cursor; + + i32 start_character = start_cursor.pos; + + real32 pos_x = 0; + real32 pos_y = -character_h*scroll_amount; + + Cpp_Token_Stack token_stack = file->token_stack; + u32 main_color = style->default_color; + u32 highlight_color = 0x00000000; + i32 token_i = 0; + bool32 tokens_exist = file->tokens_exist; + + if (tokens_exist){ + // TODO(allen): Use cpp_get_token, it binary searches this shit! + while (token_i < token_stack.count && + start_character > token_stack.tokens[token_i].start){ + ++token_i; + } + if (token_i != 0){ + main_color = + style_token_color(style, token_stack.tokens[token_i-1].type); + if (token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK){ + highlight_color = style->highlight_junk_color; + } + } + } + + // TODO(allen): Render through NULLS + for (i32 i = start_character; i < size && data[i]; ++i){ + u8 to_render = data[i]; + + u32 fade_color = 0xFFFF00FF; + real32 fade_amount = 0.f; + if (style->use_paste_color && (panel->paste_effect.tick_down > 0) && + panel->paste_effect.start <= i && i < panel->paste_effect.end){ + fade_color = style->paste_color; + fade_amount = (real32)(panel->paste_effect.tick_down) / panel->paste_effect.tick_max; + } + + if (tokens_exist && token_i < token_stack.count){ + if (i == token_stack.tokens[token_i].start){ + main_color = + style_token_color(style, token_stack.tokens[token_i].type); + ++token_i; + } + if (token_i > 0 && + i >= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + main_color = 0xFFFFFFFF; + } + } + + highlight_color = 0x00000000; + + if (tokens_exist && token_i > 0 && + token_stack.tokens[token_i-1].type == CPP_TOKEN_JUNK && + i >= token_stack.tokens[token_i-1].start && + i <= token_stack.tokens[token_i-1].start + token_stack.tokens[token_i-1].size){ + highlight_color = style->highlight_junk_color; + } + else if (panel->show_whitespace && + character_is_any_whitespace(data[i])){ + highlight_color = style->highlight_white_color; + } + + i32 cursor_mode = 0; + if (panel->show_temp_highlight){ + if (panel->temp_highlight.pos <= i && i < panel->temp_highlight_end_pos){ + cursor_mode = 2; + } + } + else{ + if (panel->cursor.pos == i){ + cursor_mode = 1; + } + } + + if (!panel->unwrapped_lines && + pos_x + character_w > max_x){ + pos_x = 0; + pos_y += font->line_skip; + } + + if (highlight_color != 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED && + to_render == '\r' && + i + 1 < size && + data[i+1] == '\n'){ + // DO NOTHING + } + else{ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + } + } + + if (cursor_mode){ + if (is_active){ + u32 color = 0x00000000; + switch (cursor_mode){ + case 1: + color = style->cursor_color; + break; + case 2: + color = style->highlight_color; + break; + } + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + color, panel_area); + } + else{ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->cursor_color, + panel_area); + } + } + + if (i == panel->mark){ + draw_rectangle_outline_clipped(target, (i32)shift_x + offset_x + (i32)pos_x, offset_y + (i32)pos_y, + character_w, font->line_skip, style->mark_color, + panel_area); + } + + if (to_render == '\r'){ + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + if (i + 1 < size && + data[i+1] == '\n'){ + // DO NOTHING + } + else{ + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + } + }break; + + case ENDLINE_RN_SEPARATE: + { + pos_x = 0; + pos_y += font->line_skip; + }break; + + case ENDLINE_RN_SHOWALLR: + { + u32 char_color = style->special_character_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, '\\', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + + pos_x += character_w; + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x, + offset_y + (i32)pos_y, + character_w, font->line_skip, + highlight_color, panel_area); + + font_draw_glyph_clipped(target, font, 'r', + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, + char_color, panel_area); + pos_x += character_w; + }break; + } + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += font->line_skip; + } + else if (to_render == '\t'){ + if (highlight_color != 0){ + draw_rectangle_clipped(target, + (i32)shift_x + offset_x + (i32)pos_x + character_w, + offset_y + (i32)pos_y, + character_w*(tab_width-1), font->line_skip, + highlight_color, panel_area); + } + pos_x += character_w*tab_width; + } + + else if (font->glyphs[to_render].data){ + u32 char_color = main_color; + if (cursor_mode && is_active){ + switch (cursor_mode){ + case 1: + char_color = style->at_cursor_color; + break; + case 2: + char_color = style->at_highlight_color; + break; + } + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + font_draw_glyph_clipped(target, font, to_render, + shift_x + offset_x + pos_x, + (real32)offset_y + pos_y, char_color, + panel_area); + pos_x += character_w; + } + + else{ + pos_x += character_w; + } + + if (pos_y > max_y){ + break; + } + } + } +} + +/* + * Hot Directory + */ + +struct Hot_Directory{ + String string; + File_List file_list; +}; + +internal void +hot_directory_init(Hot_Directory *hot_directory){ + hot_directory->string = make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + i32 dir_size = system_get_working_directory(hot_directory->string.str, + hot_directory->string.memory_size); + + if (dir_size <= 0){ + dir_size = system_get_easy_directory(hot_directory->string.str); + } + + hot_directory->string.size = dir_size; + + append(&hot_directory->string, (u8*)"\\"); +} + +internal void +hot_directory_reload_list(Hot_Directory *hot_directory){ + if (hot_directory->file_list.block){ + system_free_file_list(hot_directory->file_list); + } + hot_directory->file_list = system_get_files(hot_directory->string); +} + +internal bool32 +hot_directory_set(Hot_Directory *hot_directory, String str){ + bool32 did_set = 0; + if (copy_checked(&hot_directory->string, str)){ + did_set = 1; + system_free_file_list(hot_directory->file_list); + hot_directory->file_list = system_get_files(str); + } + return did_set; +} + +struct Hot_Directory_Match{ + u8 *filename; + bool32 is_folder; +}; + +internal Hot_Directory_Match +hot_directory_first_match(Hot_Directory *hot_directory, + String str, + bool32 include_files, + bool32 exact_match){ + Hot_Directory_Match result = {}; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + bool32 is_match = 0; + if (exact_match){ + if (match(filename, str)){ + is_match = 1; + } + } + else{ + if (str.size == 0 || + has_substr_unsensitive(filename, str)){ + is_match = 1; + } + } + + if (is_match){ + result.is_folder = files_it.folder_stage; + result.filename = filename; + break; + } + + files_iterator_step(&files, &files_it); + } + + return result; +} + +/* + * App Structs + */ + +struct Command_Data; +typedef void (*Command_Function)(Command_Data *command); + +enum Input_Request_Type{ + REQUEST_SYS_FILE, + REQUEST_LIVE_FILE, + // never below this + REQUEST_COUNT +}; + +struct Input_Request{ + Input_Request_Type type; + union{ + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + bool32 fast_folder; + } sys_file; + + struct{ + String query; + String dest; + bool32 hit_ctrl_newline; + } live_file; + }; +}; + +enum App_State{ + APP_STATE_EDIT, + APP_STATE_SEARCH, + APP_STATE_RESIZING, + // never below this + APP_STATE_COUNT +}; + +struct App_State_Incremental_Search{ + String str; + bool32 reverse; + i32 pos; +}; + +struct App_State_Resizing{ + Editing_Panel *left, *right; +}; + +struct Command_Map{ + Command_Function basic_mode_key[1+sizeof(Key_Codes)/2]; + Command_Function control_ascii[128]; + Command_Function control_key[1+sizeof(Key_Codes)/2]; +}; + +struct App_Vars{ + Command_Map map; + + Font font; + Editing_Style style; + Interactive_Style command_style; + + Editing_Working_Set working_set; + + Editing_Layout layout; + real32 last_click_x, last_click_y; + + Hot_Directory hot_directory; + + Input_Request request_queue[16]; + i32 request_count, request_filled, request_max; + + Command_Function pending_command; + + App_State state; + union{ + App_State_Incremental_Search isearch; + App_State_Resizing resizing; + }; +}; + +/* + * Commands + */ + +struct Command_Data{ + Editing_Panel *panel; + Editing_Working_Set *working_set; + Editing_Layout *layout; + Editing_Style *style; + App_Vars *vars; + + i32 screen_width, screen_height; + i32 screen_y_off; + Key_Event_Data key; + + Input_Request *requests; + i32 request_count, request_filled; +}; + +internal void +app_clear_request_queue(App_Vars *vars){ + for (i32 i = 0; i < vars->request_count; ++i){ + Input_Request *request = vars->request_queue + i; + switch (request->type){ + case REQUEST_SYS_FILE: + { + system_free_memory(request->sys_file.query.str); + system_free_memory(request->sys_file.dest.str); + }break; + + case REQUEST_LIVE_FILE: + { + system_free_memory(request->live_file.query.str); + system_free_memory(request->live_file.dest.str); + }break; + } + } + vars->request_count = 0; + vars->request_filled = 0; +} + +internal void +command_write_character(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_write_character(panel, (u8)command->key.character); + if (panel->unwrapped_lines){ + panel->preferred_x = panel->cursor.unwrapped_x; + } + else{ + panel->preferred_x = panel->cursor.wrapped_x; + } + panel->file->cursor.pos = panel->cursor.pos; + + switch ((u8)command->key.character){ + case '{': case '}': + case '(': case ')': + case ';': case ':': + case '#': case '\n': + { + panel_auto_tab(panel, panel->cursor.pos, panel->cursor.pos); + }break; + } + panel_measure_all_wrapped_y(panel); +} + +internal void +command_move_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_left(pos, data); + if (pos > 0){ + --pos; + } + else{ + pos = pos_adjust_to_self(pos, data, file->size); + } + } + else{ + --pos; + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_move_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + if (pos < size){ + ++pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, size); + } + } + + panel_cursor_move(panel, pos); +} + +internal void +command_backspace(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (cursor_pos > 0 && cursor_pos <= (i32)file->size){ + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + i32 target_pos = cursor_pos; + if (cursor_pos > 1 && + data[cursor_pos] == '\n' && + data[cursor_pos-1] == '\r'){ + --target_pos; + } + + if (target_pos > 1 && + data[target_pos-1] == '\n' && + data[target_pos-2] == '\r'){ + + buffer_delete_range(file, target_pos-2, target_pos); + if (panel->mark >= cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 2; + } + else{ + if (target_pos > 0){ + buffer_delete(file, target_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + } + } + else{ + buffer_delete(file, cursor_pos-1); + if (panel->mark >= cursor_pos){ + --panel->mark; + } + --cursor_pos; + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} +internal void +command_delete(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cursor_pos = panel->cursor.pos; + Editing_File *file = panel->file; + i8 *data = (i8*)file->data; + + if (file->size > 0){ + if (file->endline_mode == ENDLINE_RN_COMBINED){ + if (cursor_pos > 0 && + data[cursor_pos-1] == '\r' && + data[cursor_pos] == '\n'){ + buffer_delete_range(file, cursor_pos-1, cursor_pos+1); + if (panel->mark > cursor_pos){ + panel->mark -= 2; + } + cursor_pos -= 1; + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + } + + else{ + buffer_delete(file, cursor_pos); + if (panel->mark > cursor_pos){ + --panel->mark; + } + } + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, cursor_pos); + } +} + +internal void +command_move_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)-1; + i32 px = panel->preferred_x; + if (cy >= 0){ + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +internal void +command_move_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + i32 cy = panel_get_cursor_y(panel)+1; + i32 px = panel->preferred_x; + + panel->cursor = panel_compute_cursor_from_xy(panel, px, cy); + panel->file->cursor.pos = panel->cursor.pos; +} + +internal void +command_seek_end_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_end_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_beginning_of_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u32 size = file->size; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + while (pos < size && !character_is_any_whitespace(data[pos])){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + --pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + while (pos > 0 && !character_is_any_whitespace(data[pos])){ + --pos; + } + + if (pos != 0){ + ++pos; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_up(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8* data = (u8*)file->data; + + u32 pos = panel->cursor.pos; + while (pos > 0 && character_is_any_whitespace(data[pos])){ + --pos; + } + + bool32 no_hard_character = 0; + while (pos > 0){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + --pos; + } + + if (pos != 0){ + ++pos; + } + + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos = pos_adjust_to_self(pos, data, file->size); + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_whitespace_down(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + i32 size = file->size; + u8* data = (u8*)file->data; + + i32 pos = panel->cursor.pos; + while (pos < size && character_is_any_whitespace(data[pos])){ + ++pos; + } + + bool32 no_hard_character = 0; + i32 prev_endline = -1; + while (pos < size){ + if (starts_new_line(data[pos], file->endline_mode)){ + if (no_hard_character){ + break; + } + else{ + no_hard_character = 1; + prev_endline = pos; + } + } + else{ + if (!character_is_any_whitespace(data[pos])){ + no_hard_character = 0; + } + } + ++pos; + } + + if (prev_endline == -1 || prev_endline+1 >= size){ + pos = size-1; + } + else{ + pos = prev_endline+1; + } + + panel_cursor_move(panel, pos); +} + +internal void +command_seek_token_left(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + + // TODO(allen): Make nulltoken? + if (current_token == -1){ + current_token = 0; + } + Cpp_Token *token = &file->token_stack.tokens[current_token]; + if (token->start == panel->cursor.pos && current_token > 0){ + --token; + } + + panel_cursor_move(panel, token->start); +} + +internal void +command_seek_token_right(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + TentativeAssert(file->tokens_exist); + // TODO(allen): Make nulltoken? + i32 current_token; + + Cpp_Get_Token_Result get_result = cpp_get_token(&file->token_stack, panel->cursor.pos); + current_token = get_result.token_index; + if (get_result.in_whitespace){ + ++current_token; + } + + if (current_token >= file->token_stack.count){ + current_token = file->token_stack.count - 1; + } + + Cpp_Token *token = &file->token_stack.tokens[current_token]; + + panel_cursor_move(panel, token->start + token->size); +} + +internal void +command_begin_search_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 0; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_begin_rsearch_state(Command_Data *command){ + App_Vars *vars = command->vars; + vars->state = APP_STATE_SEARCH; + vars->isearch.str = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + vars->isearch.reverse = 1; + vars->isearch.pos = command->panel->cursor.pos; +} + +internal void +command_set_mark(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->mark = (i32)panel->cursor.pos; +} + +internal void +command_copy(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + u8 *data = (u8*)panel->file->data; + if (panel->file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + } +} + +internal void +command_cut(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, data); + } + clipboard_copy(working_set, data, range); + buffer_delete_range(file, range); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + panel_measure_all_wrapped_y(panel); + panel_cursor_move(panel, panel->mark); + } +} + +internal void +command_paste(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0){ + panel->next_mode.rewrite = 1; + + Editing_File *file = panel->file; + + String *src = working_set_clipboard_head(working_set); + i32 pos_left = panel->cursor.pos; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + pos_left = pos_adjust_to_left(pos_left, file->data); + } + panel_write_chunk(panel, src, pos_left); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(pos_left, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = pos_left; + panel->paste_effect.end = pos_left + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } +} + +internal void +command_paste_next(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_Working_Set *working_set = command->working_set; + if (working_set->clipboard_size > 0 && panel->mode.rewrite){ + panel->next_mode.rewrite = 1; + + Range range = get_range(panel->mark, panel->cursor.pos); + + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + + String *src = working_set_clipboard_roll_down(working_set); + buffer_replace_range(file, range.smaller, range.larger, + src->str, src->size); + + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller+src->size); + panel->preferred_x = panel_get_cursor_x(panel); + panel->file->cursor.pos = panel->cursor.pos; + + // TODO(allen): faster way to recompute line measurements afterwards + buffer_measure_all_lines(file); + panel_measure_all_wrapped_y(panel); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + + i32 ticks = 20; + panel->paste_effect.start = range.smaller; + panel->paste_effect.end = range.smaller + src->size; + panel->paste_effect.color = file->style->paste_color; + panel->paste_effect.tick_down = ticks; + panel->paste_effect.tick_max = ticks; + } + } +} + +internal void +command_delete_chunk(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + if (file->endline_mode == ENDLINE_RN_COMBINED){ + range = range_adjust_to_left(range, file->data); + } + buffer_delete_range(file, range); + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, range.smaller); + panel->mark = pos_universal_fix(range.smaller, + file->data, file->size, + file->endline_mode); + panel->file->cursor.pos = panel->cursor.pos; + } +} + +// TODO(allen): Make this preserve order (look at layout_open_panel) +// Make this preserve old ratios or sizes of panels instead of +// resizing them all. +internal void +command_open_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + Editing_Working_Set *working_set = command->working_set; + // TODO(allen): This is wrong. We should not be passing in the style + // like this. The system should be looking it up here. + Editing_Style *style = command->style; + // TODO(allen): change the screen info to real32 at the base level? + real32 screen_full_width = (real32)command->screen_width; + real32 screen_full_height = (real32)command->screen_height; + real32 screen_y_off = (real32)command->screen_y_off; + + layout_open_panel(layout, working_set->files, style); + real32 panel_w = ((real32)screen_full_width / layout->panel_count); + real32 panel_x_pos = 0; + + for (i32 panel_i = 0; panel_i < layout->panel_count; ++panel_i){ + Editing_Panel *panel = &layout->panels[panel_i]; + + panel->full_x = Floor(panel_x_pos); + panel->full_w = Floor(panel_w); + panel->full_y = Floor(screen_y_off); + panel->full_h = Floor(screen_full_height - screen_y_off); + + panel_fix_internal_area(panel); + + panel_x_pos += panel_w; + } +} + +internal void +command_close_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + i32 share_w = layout->panels[layout->active_panel].full_w; + layout_close_panel(layout, layout->active_panel); + layout_redistribute_width(layout, share_w); +} + +internal Input_Request* +app_request_sys_file(App_Vars *vars, String query, String dest_init, bool32 fast_folder){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_SYS_FILE; + request->sys_file.fast_folder = 1; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->sys_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->sys_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->sys_file.query, query); + copy(&request->sys_file.dest, dest_init); + return request; +} + + +internal Input_Request* +app_request_live_file(App_Vars *vars, String query, String dest_init){ + Input_Request *request = vars->request_queue + (vars->request_count++); + *request = {}; + request->type = REQUEST_LIVE_FILE; + + // TODO(allen): Where does this memory come from IRL? I don't like calling to + // a system function all the time, knowing it might start doing weird things. + // Better to put some limitations on the system so we can gaurantee the memory + // this needs will exist. + request->live_file.query = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + request->live_file.dest = + make_string((u8*)system_get_memory(256, SYSTEM_MEM_SMALL_STRING), 0, 256); + + copy(&request->live_file.query, query); + copy(&request->live_file.dest, dest_init); + return request; +} + +internal void +panel_set_to_new(Editing_Panel *panel){ + panel->cursor = {}; + panel->cursor.pos = + pos_adjust_to_self(0, panel->file->data, panel->file->size); + panel->scroll_y = 0; + panel->target_y = 0; + panel->vel_y = 1.f; + panel->scroll_x = 0; + panel->target_x = 0; + panel->vel_x = 1.f; +} + +internal void +command_reopen(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (!file->is_dummy){ + Editing_File temp_file; + if (buffer_load(&temp_file, (u8*)make_c_str(file->source_path))){ + buffer_close(file); + + // TODO(allen): Deduplicate!! + Cpp_File cpp_file; + cpp_file.data = temp_file.data; + cpp_file.size = temp_file.size; + { + i32 size = cpp_lex_file_token_count(cpp_file); + size = cpp_get_token_stack_size(size); + temp_file.token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(cpp_file, &temp_file.token_stack); + temp_file.tokens_complete = 1; + temp_file.tokens_exist = 1; + + *file = temp_file; + panel_set_to_new(panel); + panel_measure_all_wrapped_y(panel); + // TODO(allen): Update all other panels that also view this file. + } + } +} + +internal void +command_interactive_open(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + // TODO(allen): Can all of this be simplified? I don't like having this much + // complexity for issuing a single request. + vars->pending_command = command_interactive_open; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Open: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + Editing_File *new_file; + new_file = buffer_open(&vars->working_set, string->str, command->style); + + if (!new_file){ + // TODO(allen): Here's a really tough one. This crap is now happening twice. + // Once here and also in the single_file_input function which checks all of it + // whenever the fast_folder_select option is on to see if the user is selecting + // a folder. It would be nice to be able to share that information to here when it + // is available so we could avoid doing the exact same computations here. + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *string); + + Hot_Directory_Match match = + hot_directory_first_match(&vars->hot_directory, + front_name, 1, 0); + + if (match.filename){ + // NOTE(allen): is_folder case is handled by the single_file_input function + // which would only pass control to this command if the user did not match to + // a folder. + Assert(!match.is_folder); + new_file = buffer_open(&vars->working_set, string->str, command->style); + } + } + + if (new_file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = new_file; + panel_set_to_new(active_panel); + panel_measure_all_wrapped_y(active_panel); + + Cpp_File file; + file.data = new_file->data; + file.size = new_file->size; + // TODO(allen): Where should the memory for tokens come from IRL? + { + i32 size = cpp_lex_file_token_count(file); + size = cpp_get_token_stack_size(size); + new_file->token_stack = cpp_make_token_stack(size); + } + cpp_lex_file(file, &new_file->token_stack); + new_file->tokens_complete = 1; + new_file->tokens_exist = 1; + } + } +} + +internal void +command_save(Command_Data *command){ + Editing_Panel *panel = command->panel; + String *file_path = &panel->file->source_path; + if (file_path->size > 0){ + buffer_save(panel->file, file_path->str); + } +} + +internal void +command_interactive_save_as(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_save_as; + + Assert(vars->request_count == 0); + app_request_sys_file(vars, make_lit_string("Save As: "), vars->hot_directory.string, 1); + + hot_directory_reload_list(&vars->hot_directory); + } + else{ + String *string = &command->requests[0].sys_file.dest; + + buffer_save_and_set_names(command->panel->file, string->str); + } +} + +internal void +command_change_active_panel(Command_Data *command){ + Editing_Layout *layout = command->layout; + if (layout->panel_count > 1){ + ++layout->active_panel; + if (layout->active_panel >= layout->panel_count){ + layout->active_panel = 0; + } + } +} + +internal void +command_interactive_switch_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_switch_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Switch To: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(command->working_set, *string); + + if (file){ + Editing_Panel *active_panel = command->panel; + active_panel->file = file; + Panel_Cursor_Data cursor_data; + cursor_data = panel_compute_cursor_from_pos(active_panel, file->cursor.pos); + active_panel->cursor = cursor_data; + } + } +} + +internal void +command_kill_file(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + if (file && file->max_size != 0){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } +} + +internal void +command_interactive_kill_file(Command_Data *command){ + App_Vars *vars = command->vars; + if (command->request_count == 0){ + vars->pending_command = command_interactive_kill_file; + + Assert(vars->request_count == 0); + app_request_live_file(vars, make_lit_string("Kill: "), make_lit_string("")); + } + else{ + String *string = &command->requests[0].live_file.dest; + + Editing_File *file; + file = working_set_lookup_file(&vars->working_set, *string); + + if (file){ + buffer_close(file); + buffer_get_dummy(file, command->style); + } + } +} + +internal void +command_toggle_line_wrap(Command_Data *command){ + Editing_Panel *panel = command->panel; + if (panel->unwrapped_lines){ + panel->unwrapped_lines = 0; + panel->target_x = 0; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + else{ + panel->unwrapped_lines = 1; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } +} + +internal void +command_toggle_endline_mode(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + switch (file->endline_mode){ + case ENDLINE_RN_COMBINED: + { + panel->cursor.pos = pos_adjust_to_left(panel->cursor.pos, panel->file->data); + file->endline_mode = ENDLINE_RN_SEPARATE; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SEPARATE: + { + file->endline_mode = ENDLINE_RN_SHOWALLR; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + + case ENDLINE_RN_SHOWALLR: + { + panel->cursor.pos = pos_adjust_to_self(panel->cursor.pos, panel->file->data, + panel->file->size); + file->endline_mode = ENDLINE_RN_COMBINED; + panel->cursor = + panel_compute_cursor_from_pos(panel, panel->cursor.pos); + }break; + } +} + +internal void +command_to_uppercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'a' && data[i] <= 'z'){ + data[i] += (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_to_lowercase(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + if (range.smaller < range.larger){ + Editing_File *file = panel->file; + u8 *data = file->data; + for (i32 i = range.smaller; i < range.larger; ++i){ + if (data[i] >= 'A' && data[i] <= 'Z'){ + data[i] -= (u8)('A' - 'a'); + } + } + // TODO(allen): RELEX POINT + if (file->token_stack.tokens){ + Cpp_File cpp_file; + cpp_file.size = file->size; + cpp_file.data = (u8*)file->data; + cpp_relex_file(cpp_file, &file->token_stack, range.smaller, range.larger, 0); + file->tokens_complete = 1; + file->tokens_exist = 1; + } + } +} + +internal void +command_toggle_show_whitespace(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel->show_whitespace = !panel->show_whitespace; +} + +internal void +command_clean_line(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 pos = panel_find_beginning_of_line(panel, panel->cursor.pos); + + i32 last_hard_start = pos-1; + i32 last_hard = last_hard_start; + while (pos < file->size && data[pos] != '\n'){ + if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + } + ++pos; + } + + if (last_hard != last_hard_start){ + pos = pos_adjust_to_left(pos, data); + + if (last_hard + 1 < pos){ + buffer_replace_range(file, last_hard+1, pos, 0, 0, REP_WHITESPACE); + panel_measure_all_wrapped_y(panel); + + if (panel->cursor.pos > last_hard){ + panel->cursor = panel_compute_cursor_from_pos(panel, last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= pos - (last_hard + 1); + } + } + } + else{ + panel_measure_all_wrapped_y(panel); + panel_auto_tab(panel, pos, pos); + } +} + +internal void +command_clean_all_lines(Command_Data *command){ + Editing_Panel *panel = command->panel; + Editing_File *file = panel->file; + u8 *data = (u8*)file->data; + + i32 cursor_pos = panel->cursor.pos; + i32 pos = 0; + i32 last_hard = -1; + bool32 is_all_white = 1; + while (pos <= file->size){ + if (pos == file->size || data[pos] == '\n'){ + i32 line_pos = pos; + if (pos < file->size && data[pos] == '\n'){ + line_pos = pos_adjust_to_left(pos, data); + } + + // TODO(allen): This should be optimized by either keeping track of the nesting level + // in this funciton, or by at least having a nesting hint that is used and updated + // every time an auto tab happens. Also auto tab should take a nesting hint. + if (is_all_white){ + panel_auto_tab(panel, pos, pos); + } + else{ + if (last_hard + 1 < line_pos){ + buffer_replace_range(file, last_hard+1, line_pos, 0, 0, REP_WHITESPACE); + + if (cursor_pos > last_hard && cursor_pos <= pos){ + cursor_pos = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (cursor_pos > pos){ + cursor_pos -= line_pos - (last_hard + 1); + } + if (panel->mark > last_hard && panel->mark <= pos){ + panel->mark = pos_adjust_to_self(last_hard+1, file->data, file->size); + } + else if (panel->mark > pos){ + panel->mark -= line_pos - (last_hard + 1); + } + pos -= line_pos - (last_hard + 1); + } + } + + last_hard = pos; + is_all_white = 1; + } + else if (!character_is_any_whitespace(data[pos])){ + last_hard = pos; + is_all_white = 0; + } + ++pos; + } + panel_measure_all_wrapped_y(panel); + panel->cursor = panel_compute_cursor_from_pos(panel, cursor_pos); +} + +internal void +command_eol_dosify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_RN, ENDLINE_ERASE, ENDLINE_RN); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_eol_nixify(Command_Data *command){ + Editing_Panel *panel = command->panel; + panel_endline_convert(panel, ENDLINE_N, ENDLINE_ERASE, ENDLINE_N); + panel_measure_all_wrapped_y(panel); +} + +internal void +command_auto_tab(Command_Data *command){ + Editing_Panel *panel = command->panel; + Range range = get_range(panel->cursor.pos, panel->mark); + panel_auto_tab(panel, range.smaller, range.larger); + panel_measure_all_wrapped_y(panel); +} + +internal void +setup_commands(Command_Map *commands, Key_Codes *codes){ + commands->basic_mode_key[codes->left] = command_move_left; + commands->basic_mode_key[codes->right] = command_move_right; + commands->basic_mode_key[codes->del] = command_delete; + commands->basic_mode_key[codes->back] = command_backspace; + commands->basic_mode_key[codes->up] = command_move_up; + commands->basic_mode_key[codes->down] = command_move_down; + commands->basic_mode_key[codes->end] = command_seek_end_of_line; + commands->basic_mode_key[codes->home] = command_seek_beginning_of_line; + +#if 0 + commands->control_key[codes->right] = command_seek_token_right; + commands->control_ascii['m'] = command_seek_token_right; + commands->control_key[codes->left] = command_seek_token_left; + commands->control_ascii['n'] = command_seek_token_left; +#else + commands->control_key[codes->right] = command_seek_whitespace_right; + commands->control_ascii['m'] = command_seek_whitespace_right; + commands->control_key[codes->left] = command_seek_whitespace_left; + commands->control_ascii['n'] = command_seek_whitespace_left; +#endif + + commands->control_key[codes->up] = command_seek_whitespace_up; + commands->control_ascii['y'] = command_seek_whitespace_up; + commands->control_key[codes->down] = command_seek_whitespace_down; + commands->control_ascii['h'] = command_seek_whitespace_down; + + commands->control_ascii['\t'] = command_auto_tab; + + commands->control_ascii[' '] = command_set_mark; + commands->control_ascii['c'] = command_copy; + commands->control_ascii['x'] = command_cut; + commands->control_ascii['v'] = command_paste; + commands->control_ascii['V'] = command_paste_next; + commands->control_ascii['d'] = command_delete_chunk; + commands->control_ascii['p'] = command_open_panel; + commands->control_ascii['P'] = command_close_panel; + commands->control_ascii['o'] = command_interactive_open; + commands->control_ascii['O'] = command_reopen; + commands->control_ascii['s'] = command_save; + commands->control_ascii['w'] = command_interactive_save_as; + commands->control_ascii[','] = command_change_active_panel; + commands->control_ascii['i'] = command_interactive_switch_file; + commands->control_ascii['k'] = command_interactive_kill_file; + commands->control_ascii['K'] = command_kill_file; + commands->control_ascii['l'] = command_toggle_line_wrap; + commands->control_ascii['L'] = command_toggle_endline_mode; + commands->control_ascii['u'] = command_to_uppercase; + commands->control_ascii['j'] = command_to_lowercase; + commands->control_ascii['?'] = command_toggle_show_whitespace; + commands->control_ascii['`'] = command_clean_line; + commands->control_ascii['~'] = command_clean_all_lines; + commands->control_ascii['1'] = command_eol_dosify; + commands->control_ascii['!'] = command_eol_nixify; + commands->control_ascii['f'] = command_begin_search_state; + commands->control_ascii['r'] = command_begin_rsearch_state; +} + +/* + * Interactive Bar + */ + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, u8 *str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; str[i]; ++i){ + font_draw_glyph(target, bar->style.font, str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +intbar_draw_string(Render_Target *target, + Interactive_Bar *bar, String str, + u32 char_color){ + i32 char_w = font_get_character_width(bar->style.font); + for (i32 i = 0; i < str.size; ++i){ + font_draw_glyph(target, bar->style.font, str.str[i], + (real32)bar->pos_x, (real32)bar->pos_y, char_color); + bar->pos_x += char_w; + } +} + +internal void +hot_directory_draw_helper(Render_Target *target, + Hot_Directory *hot_directory, + Interactive_Bar *bar, String *string, + bool32 include_files){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.pop1_color); + + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + + get_front_of_directory(&front_name, *string); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + + File_List files = hot_directory->file_list; + File_List_Iterator files_it = files_iterator_init(&files); + + while (files_it.filename_ptr && + (include_files || files_it.folder_stage)){ + u8 *filename = *files_it.filename_ptr; + + if (front_name.size == 0 || has_substr_unsensitive(filename, front_name)){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + if (files_it.folder_stage){ + intbar_draw_string(target, bar, filename, bar->style.pop1_color); + intbar_draw_string(target, bar, (u8*)"/", bar->style.pop1_color); + } + else{ + intbar_draw_string(target, bar, filename, bar->style.base_color); + } + } + + files_iterator_step(&files, &files_it); + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +internal void +live_file_draw_helper(Render_Target *target, + Editing_Working_Set *working_set, + Interactive_Bar *bar, String *string){ + persist u8 str_open_bracket[] = " {"; + persist u8 str_close_bracket[] = "}"; + persist u8 str_comma[] = ", "; + + intbar_draw_string(target, bar, *string, bar->style.base_color); + + intbar_draw_string(target, bar, str_open_bracket, bar->style.base_color); + + bool32 is_first_string = 1; + for (i32 file_i = 0; + file_i < working_set->file_index_count; + ++file_i){ + Editing_File *file = &working_set->files[file_i]; + if (file->live_name.str && + (string->size == 0 || has_substr_unsensitive(file->live_name, *string))){ + if (is_first_string){ + is_first_string = 0; + } + else{ + intbar_draw_string(target, bar, str_comma, bar->style.base_color); + } + intbar_draw_string(target, bar, file->live_name, bar->style.base_color); + } + } + intbar_draw_string(target, bar, str_close_bracket, bar->style.base_color); +} + +/* + * App Functions + */ + +internal bool32 +app_init(Thread_Context *thread, Application_Memory *memory, + Key_Codes *loose_codes, Clipboard_Contents clipboard){ + Partition_Cursor partition = + partition_open(memory->vars_memory, memory->vars_memory_size); + + App_Vars *vars = (App_Vars*) + partition_allocate(&partition, sizeof(App_Vars)); + + Assert(vars); + *vars = {}; + + u32 panel_max_count = vars->layout.panel_max_count = 4; + u32 panel_count = vars->layout.panel_count = 1; + Assert(panel_max_count >= 1); + Editing_Panel *panels = vars->layout.panels = (Editing_Panel*) + partition_allocate(&partition, sizeof(Editing_Panel)*panel_max_count); + + Assert(panels); + // TODO(allen): improved Assert + + // NOTE(allen): command map setup + setup_commands(&vars->map, loose_codes); + + // NOTE(allen): font setup + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + i32 memory_used = 0; + if (font_load(&vars->font, 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): file setup + vars->working_set.file_index_count = 1; + vars->working_set.file_max_count = 29; + + vars->working_set.files = (Editing_File*) + partition_allocate(&partition, + sizeof(Editing_File)*vars->working_set.file_max_count); + + buffer_get_dummy(&vars->working_set.files[0], &vars->style); + + // NOTE(allen): clipboard setup + vars->working_set.clipboard_max_size = ArrayCount(vars->working_set.clipboards); + vars->working_set.clipboard_size = 0; + vars->working_set.clipboard_current = 0; + vars->working_set.clipboard_rolling = 0; + + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // TODO(allen): more robust allocation solution for the clipboard? + + // NOTE(allen): style setup + // TODO(allen): style_set_font function + vars->style.font = &vars->font; + vars->style.font_metrics.character_width = vars->font.glyphs[' '].advance; + vars->style.font_metrics.line_skip = vars->font.line_skip; + + vars->style.back_color = 0xFF0C0C0C; + vars->style.margin_color = 0xFF181818; + vars->style.cursor_color = 0xFF00EE00; + vars->style.highlight_color = 0xFFDDEE00; + vars->style.mark_color = 0xFF494949; + vars->style.default_color = 0xFF90B080; + vars->style.at_cursor_color = vars->style.back_color; + vars->style.at_highlight_color = 0xFFFF44DD; + vars->style.comment_color = 0xFF2090F0; + vars->style.keyword_color = 0xFFD08F20; + vars->style.constant_color = 0xFF50FF30; + vars->style.special_character_color = 0xFFFF0000; + + vars->style.use_paste_color = 1; + vars->style.paste_color = 0xFFDDEE00; + + vars->style.highlight_junk_color = 0x44FF0000; + vars->style.highlight_white_color = 0x1100FFFF; + vars->style.tab_width = 4; + vars->style.margin_width = 5; + + Interactive_Style file_info_style; + file_info_style.font = &vars->font; + file_info_style.bar_color = 0xFF888888; + file_info_style.base_color = 0xFF000000; + file_info_style.pop1_color = 0xFF4444AA; + file_info_style.pop2_color = 0xFFFF0000; + file_info_style.height = vars->style.font->line_skip + 2; + + vars->style.file_info_style = file_info_style; + + Interactive_Style command_style; + command_style.font = &vars->font; + command_style.bar_color = 0xFF0C0C0C; + command_style.base_color = 0xFFDDDDBB; + command_style.pop1_color = 0xFF4444AA; + command_style.pop2_color = 0xFF44FF44; + command_style.height = vars->style.font->line_skip + 2; + + vars->command_style = command_style; + + // NOTE(allen): panel setup + AllowLocal(panel_count); + panel_init(&panels[0], &vars->working_set.files[0]); + + // NOTE(allen): hot directory setup + hot_directory_init(&vars->hot_directory); + + // NOTE(allen): request stack setup + vars->request_count = 0; + vars->request_filled = 0; + vars->request_max = ArrayCount(vars->request_queue); + + return 1; +} + +struct Single_Line_Input_Step{ + bool32 hit_newline; + bool32 hit_ctrl_newline; + bool32 hit_a_character; + bool32 hit_backspace; + bool32 hit_esc; + bool32 made_a_change; + bool32 did_command; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + bool32 fast_folder_select; +}; + +internal Single_Line_Input_Step +app_single_line_input_core(Key_Codes *codes, + Key_Input_Data *input, + Single_Line_Mode mode){ + Single_Line_Input_Step result = {}; + + if (input->has_press){ + if (input->press.keycode == codes->back){ + result.hit_backspace = 1; + switch (mode.type){ + case SINGLE_LINE_STRING: + { + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + if (mode.string->size > 0){ + --mode.string->size; + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size > 0){ + --mode.string->size; + i8 end_character = mode.string->str[mode.string->size]; + if (character_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + // TODO(allen): What to do when the string becomes empty though? + mode.string->str[mode.string->size] = 0; + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + // TODO(allen): Is this still okay? It looks like it's keeping a NULL terminator + // which is at least unecessary given the new protocol. + mode.string->str[mode.string->size] = 0; + } + result.made_a_change = 1; + } + }break; + } + } + + else if (input->press.keycode == codes->newline){ + if (input->control_keys[CONTROL_KEY_CONTROL]){ + result.hit_ctrl_newline = 1; + result.made_a_change = 1; + } + + else{ + result.made_a_change = 1; + if (mode.fast_folder_select){ + u8 front_name_space[256]; + String front_name = + make_string(front_name_space, 0, ArrayCount(front_name_space)); + get_front_of_directory(&front_name, *mode.string); + Hot_Directory_Match match; + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 1); + + if (!match.filename){ + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0); + } + + if (match.filename){ + if (match.is_folder){ + set_last_folder(mode.string, match.filename); + hot_directory_set(mode.hot_directory, *mode.string); + } + else{ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + append(mode.string, match.filename); + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + else{ + result.hit_newline = 1; + } + } + } + + else if (input->press.keycode == codes->esc){ + result.hit_esc = 1; + result.made_a_change = 1; + } + + else if (input->press.character){ + result.hit_a_character = 1; + if (!input->control_keys[CONTROL_KEY_CONTROL]){ + switch (mode.type){ + case SINGLE_LINE_STRING: + { + if (mode.string->size+1 < mode.string->memory_size){ + mode.string->str[mode.string->size] = (i8)input->press.character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + result.made_a_change = 1; + } + }break; + + case SINGLE_LINE_FILE: + { + if (mode.string->size+1 < mode.string->memory_size){ + i8 new_character = (i8)input->press.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + // TODO(allen): More of this keeping a NULL terminator business... + // I want out of this business for the String struct. + mode.string->str[mode.string->size] = 0; + + if (character_is_slash(new_character)){ + hot_directory_set(mode.hot_directory, *mode.string); + } + result.made_a_change = 1; + } + }break; + } + } + + else{ + result.did_command = 1; + result.made_a_change = 1; + } + } + } + + return result; +} + +inline internal Single_Line_Input_Step +app_single_line_input_step(Key_Codes *codes, Key_Input_Data *input, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Single_Line_Input_Step +app_single_file_input_step(Key_Codes *codes, Key_Input_Data *input, + String *string, Hot_Directory *hot_directory, + bool32 fast_folder_select){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.fast_folder_select = fast_folder_select; + return app_single_line_input_core(codes, input, mode); +} + +inline internal Command_Function +app_get_command(App_Vars *vars, Key_Input_Data *input, Key_Event_Data key){ + Command_Function function = 0; + if (input->control_keys[CONTROL_KEY_CONTROL]){ + if (key.character_no_caps_lock){ + function = vars->map.control_ascii[key.character_no_caps_lock]; + } + else{ + function = vars->map.control_key[key.keycode]; + } + } + else if (!input->control_keys[CONTROL_KEY_ALT]){ + if (key.character != 0){ + function = command_write_character; + } + else{ + function = vars->map.basic_mode_key[key.keycode]; + } + } + return function; +} + +internal bool32 +smooth_camera_step(real32 *target, real32 *current, real32 *vel, real32 S, real32 T){ + bool32 result = 0; + real32 targ = *target; + real32 curr = *current; + real32 v = *vel; + if (curr != targ){ + real32 L = lerp(curr, T, targ); + + i32 sign = (targ > curr) - (targ < curr); + real32 V = curr + sign*v; + + if (sign > 0){ + curr = Min(L, V); + } + else{ + curr = Max(L, V); + } + + if (curr == V){ + v *= S; + } + + if (curr > targ - .0001f && curr < targ + .0001f){ + curr = targ; + v = 1.f; + } + *target = targ; + *current = curr; + *vel = v; + result = 1; + } + return result; +} + +#if FRED_INTERNAL +Application_Memory *GLOBAL; +#endif + +internal Application_Step_Result +app_step(Thread_Context *thread, Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory, + Clipboard_Contents clipboard, + bool32 first_step){ + +#if FRED_INTERNAL + GLOBAL = memory; +#endif + + ProfileStart(app_step); + ProfileSection(thread, app_step, "start"); + + Application_Step_Result app_result = {}; + app_result.redraw = 1; + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // TODO(allen): Let's find another way to do first_step + // so we can get it out of app_step and the platform layer. + if (first_step || !time_step){ + app_result.redraw = 1; + } + + Editing_Panel *panels = vars->layout.panels; + Editing_Panel *active_panel = &panels[vars->layout.active_panel]; + ProfileSection(thread, app_step, "setup"); + + // NOTE(allen): OS clipboard event handling + if (clipboard.str){ + String *dest = working_set_next_clipboard_string(&vars->working_set, clipboard.size); + copy(dest, make_string(clipboard.str, clipboard.size)); + } + + // NOTE(allen): check files are up to date + for (i32 i = 0; i < vars->working_set.file_index_count; ++i){ + Editing_File *file = vars->working_set.files + i; + + if (!file->is_dummy){ + Time_Stamp time_stamp; + time_stamp = system_file_time_stamp((u8*)make_c_str(file->source_path)); + + if (time_stamp.success){ + file->last_sys_write_time = time_stamp.time; + if (file->last_sys_write_time != file->last_4ed_write_time){ + app_result.redraw = 1; + } + } + } + } + ProfileSection(thread, app_step, "OS syncing"); + + // NOTE(allen): keyboard input handling + if (time_step){ + switch (vars->state){ + case APP_STATE_EDIT: + { + Command_Data command_data; + command_data.panel = active_panel; + command_data.working_set = &vars->working_set; + command_data.layout = &vars->layout; + command_data.style = &vars->style; + command_data.vars = vars; + command_data.screen_width = target->width; + command_data.screen_height = target->height; + command_data.screen_y_off = vars->command_style.height; + command_data.requests = 0; + command_data.request_count = 0; + command_data.request_filled = 0; + + if (vars->request_count == 0){ + Command_Function function = 0; + if (input->has_press){ + command_data.key = input->press; + function = app_get_command(vars, input, command_data.key); + } + + else if (input->has_hold){ + command_data.key = input->hold; + function = app_get_command(vars, input, command_data.key); + + } + + if (function){ + command_data.panel->next_mode = {}; + function(&command_data); + app_result.redraw = 1; + command_data.panel->mode = command_data.panel->next_mode; + } + } + + else{ + + Input_Request *active_request = vars->request_queue + vars->request_filled; + bool32 killed_command = 0; + switch (active_request->type){ + case REQUEST_SYS_FILE: + { + String *string = &active_request->sys_file.dest; + Single_Line_Input_Step result = + app_single_file_input_step(codes, input, string, &vars->hot_directory, 1); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->sys_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + case REQUEST_LIVE_FILE: + { + String *string = &active_request->live_file.dest; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + } + + if (result.hit_ctrl_newline){ + active_request->live_file.hit_ctrl_newline = 1; + } + + if (result.hit_newline || result.hit_ctrl_newline){ + ++vars->request_filled; + } + + if (result.hit_esc){ + app_clear_request_queue(vars); + killed_command = 1; + } + }break; + + } + + if (vars->request_filled == vars->request_count && !killed_command){ + command_data.requests = vars->request_queue; + command_data.request_count = vars->request_count; + command_data.request_filled = vars->request_filled; + vars->pending_command(&command_data); + app_clear_request_queue(vars); + } + } + + }break; + + case APP_STATE_SEARCH: + { + String *string = &vars->isearch.str; + Single_Line_Input_Step result = + app_single_line_input_step(codes, input, string); + + if (result.made_a_change){ + app_result.redraw = 1; + + Editing_File *file = active_panel->file; + + bool32 step_forward = 0; + bool32 step_backward = 0; + + if (input->has_press){ + Key_Event_Data key = input->press; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + if (!step_forward && !step_backward && + !input->has_press && input->has_hold){ + Key_Event_Data key = input->hold; + Command_Function function = app_get_command(vars, input, key); + if (function == command_begin_search_state){ + step_forward = 1; + } + if (function == command_begin_rsearch_state){ + step_backward = 1; + } + } + + i32 start_pos = vars->isearch.pos; + if (step_forward){ + if (vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 0; + } + } + if (step_backward){ + if (!vars->isearch.reverse){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + vars->isearch.reverse = 1; + } + } + + String file_string = make_string(file->data, file->size); + + i32 pos; + + if (vars->isearch.reverse){ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos + 1; + vars->isearch.pos = start_pos; + } + else{ + pos = rfind_substr(file_string, start_pos - 1, *string); + + if (pos >= 0){ + if (step_backward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = rfind_substr(file_string, start_pos - 1, *string); + if (pos == -1){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + else{ + if (result.hit_backspace){ + start_pos = active_panel->temp_highlight.pos - 1; + vars->isearch.pos = start_pos; + } + else{ + pos = find_substr(file_string, start_pos + 1, *string); + + if (pos < file->size){ + if (step_forward){ + vars->isearch.pos = pos; + start_pos = pos; + pos = find_substr(file_string, start_pos + 1, *string); + if (pos == file->size){ + pos = start_pos; + } + } + + panel_set_temp_highlight(active_panel, pos, pos+string->size); + } + } + } + } + + if (result.hit_newline || result.hit_ctrl_newline){ + panel_cursor_move(active_panel, active_panel->temp_highlight); + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + if (result.hit_esc){ + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + } + + }break; + + case APP_STATE_RESIZING: + { + if (input->has_press){ + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "keyboard input"); + + // NOTE(allen): reorganizing panels on screen + i32 prev_width = vars->layout.full_width; + i32 prev_height = vars->layout.full_height; + i32 full_width = vars->layout.full_width = target->width; + i32 full_height = vars->layout.full_height = target->height; + + if (prev_width != full_width || prev_height != full_height){ + layout_refit(&vars->layout, 0, vars->command_style.height, + full_width, full_height, + prev_width, prev_height); + + for (i32 panel_i = 0; + panel_i < vars->layout.panel_count && + vars->layout.panel_count > 1; + ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + + Editing_Style *style = panel->file->style; + i32 character_w = style_get_character_width(style); + i32 margin_width = style->margin_width; + if (panel->full_w < character_w*6 + margin_width*2){ + i32 share_w = panel->full_w; + layout_close_panel(&vars->layout, panel_i); + layout_redistribute_width(&vars->layout, share_w); + panel_i = 0; + } + } + + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = panels + panel_i; + if (!panel->file->is_dummy){ + panel->cursor = panel_compute_cursor_from_pos(panel, panel->cursor.pos); + } + } + + app_result.redraw = 1; + } + ProfileSection(thread, app_step, "reorganizing panels"); + + // NOTE(allen): mouse input handling + if (time_step && !mouse->out_of_window){ + + i32 mx = mouse->x; + i32 my = mouse->y; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + // TODO(allen): + // This means any mouse use will be impossible, I guess it will have + // to depend on the type of the request seen at the top??? + app_clear_request_queue(vars); + mouse_press_event = 1; + + switch (vars->state){ + case APP_STATE_SEARCH: + { + active_panel->show_temp_highlight = 0; + + system_free_memory(vars->isearch.str.str); + vars->state = APP_STATE_EDIT; + }break; + } + } + + switch (vars->state){ + case APP_STATE_EDIT: + { + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + + Editing_Panel *panel = &panels[panel_i]; + + if (mx >= panel->x && + mx < panel->w + panel->x && + my >= panel->y && + my < panel->h + panel->y){ + + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_IBEAM; + + if (mouse_press_event){ + if (!panel->file->is_dummy){ + app_result.redraw = 1; + + Font *font = &vars->font; + + i32 character_w = style_get_character_width(panel->file->style); + i32 character_h = font->line_skip; + i32 max_line_length = panel_compute_max_line_length(panel); + i32 max_lines = panel_compute_max_lines(panel); + + real32 grid_x = ((real32)mx - panel->x) / character_w; + real32 grid_y = ((real32)my - panel->y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + if (grid_x >= 0 && grid_x <= max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = (i32)(grid_x + active_panel->scroll_x); + i32 pos_y = (i32)(grid_y + active_panel->scroll_y); + + panel_cursor_move(active_panel, pos_x, pos_y); + } + } + vars->layout.active_panel = panel_i; + active_panel = panel; + } + else{ + // NOTE(allen): mouse inside editing area but no click + } + } + + else if (mx >= panel->full_x && + mx < panel->full_w + panel->full_x && + my >= panel->full_y && + my < panel->full_h + panel->full_y){ + // NOTE(allen): not inside the editing area but within the margins + bool32 resize_area = 0; + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + if (mx >= (panel->full_x+panel->full_w+panel->x+panel->w)/2){ + if (panel_i != vars->layout.panel_count-1){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = 1; + } + } + else if (mx <= (panel->full_x+panel->x)/2){ + if (panel_i != 0){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + resize_area = -1; + } + } + + if (resize_area != 0 && mouse_press_event){ + vars->state = APP_STATE_RESIZING; + if (resize_area == 1){ + vars->resizing.left = panel; + vars->resizing.right = panel+1; + } + else if (resize_area == -1){ + vars->resizing.left = panel-1; + vars->resizing.right = panel; + } + } + } + } + + i32 cursor_y = panel_get_cursor_y(active_panel); + real32 target_y = active_panel->target_y; + i32 max_lines = panel_compute_max_lines(active_panel); + + bool32 wheel_used; + + real32 delta_target_y = Max(1, max_lines / 3.f); + delta_target_y *= -mouse->wheel; + + target_y += delta_target_y; + + if (target_y < 0){ + target_y = 0; + } + + if (mouse->wheel == 0){ + wheel_used = 0; + } + else{ + wheel_used = 1; + + if (cursor_y >= target_y + max_lines){ + cursor_y = (i32)target_y + max_lines - 1; + } + if (cursor_y < target_y){ + cursor_y = (i32)target_y + 1; + } + } + + active_panel->target_y = target_y; + if (cursor_y != panel_get_cursor_y(active_panel)){ + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + active_panel->preferred_x, + cursor_y); + } + + if (wheel_used){ + app_result.redraw = 1; + } + }break; + + case APP_STATE_RESIZING: + { + if (mouse->left_button){ + Editing_Panel *left = vars->resizing.left; + Editing_Panel *right = vars->resizing.right; + i32 left_x = left->full_x; + i32 left_w = left->full_w; + i32 right_x = right->full_x; + i32 right_w = right->full_w; + + AllowLocal(right_x); + + i32 new_left_x = left_x; + i32 new_left_w = mx - left_x; + i32 new_right_w = right_w - (new_left_w - left_w); + i32 new_right_x = mx; + + if (left_w != new_left_w){ + app_result.redraw = 1; + + Editing_Style *left_style = left->file->style; + Editing_Style *right_style = right->file->style; + + i32 left_character_w = style_get_character_width(left_style); + i32 right_character_w = style_get_character_width(right_style); + + i32 left_margin_width = left_style->margin_width; + i32 right_margin_width = right_style->margin_width; + if (new_left_w > left_margin_width*2 + left_character_w*6 && + new_right_w > right_margin_width*2 + right_character_w*6){ + left->full_x = new_left_x; + left->full_w = new_left_w; + right->full_x = new_right_x; + right->full_w = new_right_w; + + panel_fix_internal_area(left); + panel_fix_internal_area(right); + } + } + } + else{ + app_result.redraw = 1; + vars->state = APP_STATE_EDIT; + } + }break; + } + } + ProfileSection(thread, app_step, "mouse input"); + + // NOTE(allen): fix scrolling on all panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + i32 cursor_y; + if (panel->show_temp_highlight){ + if (panel->unwrapped_lines){ + cursor_y = panel->temp_highlight.unwrapped_y; + } + else{ + cursor_y = panel->temp_highlight.wrapped_y; + } + } + else{ + if (panel->unwrapped_lines){ + cursor_y = panel->cursor.unwrapped_y; + } + else{ + cursor_y = panel->cursor.wrapped_y; + } + } + real32 target_y = panel->target_y; + real32 original_target_y = target_y; + i32 max_lines = panel_compute_max_lines(panel); + + while (cursor_y >= Floor(target_y) + max_lines){ + target_y += 3.f; + } + + while (cursor_y < target_y){ + target_y -= 3.f; + } + + if (target_y < 0){ + target_y = 0; + } + + panel->target_y = target_y; + + i32 cursor_x = panel_get_cursor_x(panel); + real32 target_x = panel->target_x; + real32 original_target_x = target_x; + i32 max_x = panel_compute_max_line_length(panel); + if (cursor_x < target_x){ + target_x = (real32)Max(0, cursor_x - max_x/2); + } + else if (cursor_x >= target_x + max_x){ + target_x = (real32)(cursor_x - max_x/2); + } + + panel->target_x = target_x; + + if (original_target_y != panel->target_y || + original_target_x != panel->target_x){ + app_result.redraw; + } + } + ProfileSection(thread, app_step, "fix scrolling"); + + // NOTE(allen): execute animations + for (i32 i = 0; i < vars->layout.panel_count; ++i){ + Editing_Panel *panel = vars->layout.panels + i; + + // TODO(allen): Scrolling parameterization in style? + if (smooth_camera_step(&panel->target_y, &panel->scroll_y, &panel->vel_y, 2.f, 1.f/9.f)){ + app_result.redraw = 1; + } + + if (smooth_camera_step(&panel->target_x, &panel->scroll_x, &panel->vel_x, 2.f, 1.f/6.f)){ + app_result.redraw = 1; + } + + if (panel->paste_effect.tick_down > 0){ + --panel->paste_effect.tick_down; + app_result.redraw = 1; + } + + } + ProfileSection(thread, app_step, "execute animations"); + + if (app_result.redraw){ + // NOTE(allen): render the panels + for (i32 panel_i = 0; panel_i < vars->layout.panel_count; ++panel_i){ + Editing_Panel *panel = &panels[panel_i]; + Editing_Style *style = panel->file->style; + + i32 full_left = panel->full_x; + i32 full_top = panel->full_y; + i32 full_right = full_left + panel->full_w; + i32 full_bottom = full_top + panel->full_h; + + u32 back_color = style->back_color; + draw_rectangle_2corner(target, full_left, full_top, full_right, full_bottom, back_color); + + u32 side_margin_color = style->margin_color; + + panel_draw(thread, target, panel, vars->layout.active_panel == panel_i); + + // NOTE(allen): file info bar + { + Interactive_Bar bar; + bar.style = style->file_info_style; + bar.pos_x = panel->x; + bar.pos_y = full_top; + + draw_rectangle(target, full_left, bar.pos_y, + panel->full_w, bar.style.height, + bar.style.bar_color); + + Editing_File *file = panel->file; + if (!file->is_dummy){ + intbar_draw_string(target, &bar, panel->file->live_name, bar.style.base_color); + intbar_draw_string(target, &bar, make_lit_string(" - "), bar.style.base_color); + + u8 line_number_space[30]; + String line_number = make_string(line_number_space, 0, 30); + append(&line_number, (u8*)"L#"); + append_int_to_str(panel->cursor.line, &line_number); + + intbar_draw_string(target, &bar, line_number, bar.style.base_color); + + if (file->last_4ed_write_time != file->last_sys_write_time){ + persist String out_of_sync = make_lit_string(" FILE SYNC"); + intbar_draw_string(target, &bar, out_of_sync, bar.style.pop2_color); + } + } + } + + // L + draw_rectangle_2corner(target, full_left, panel->y, + panel->x, full_bottom, side_margin_color); + + // R + draw_rectangle_2corner(target, panel->x + panel->w, panel->y, + full_right, full_bottom, side_margin_color); + + // B + draw_rectangle_2corner(target, full_left, panel->y + panel->h, + full_right, full_bottom, side_margin_color); + + if (panel_i != 0){ + draw_rectangle_2corner(target, panel->full_x-1, panel->full_y, + panel->full_x+1, panel->full_y+panel->full_h, + 0xFFFFFFFF); + } + } + ProfileSection(thread, app_step, "render files"); + + // NOTE (allen): command bar + { + Interactive_Bar bar; + bar.style = vars->command_style; + bar.pos_x = 0; + bar.pos_y = 0; + + draw_rectangle(target, 0, 0, + target->width, bar.style.height, + bar.style.bar_color); + + switch (vars->state){ + case APP_STATE_EDIT: + { + if (vars->request_count > 0){ + Input_Request *request = vars->request_queue + vars->request_filled; + + switch (request->type){ + case REQUEST_SYS_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + hot_directory_draw_helper(target, &vars->hot_directory, &bar, string, 1); + }break; + + case REQUEST_LIVE_FILE: + { + intbar_draw_string(target, &bar, request->sys_file.query, bar.style.pop2_color); + + String *string = &request->sys_file.dest; + live_file_draw_helper(target, &vars->working_set, &bar, string); + }break; + + } + } + }break; + + case APP_STATE_SEARCH: + { + persist String search_str = make_lit_string("I-Search: "); + persist String rsearch_str = make_lit_string("Reverse-I-Search: "); + if (vars->isearch.reverse){ + intbar_draw_string(target, &bar, rsearch_str, bar.style.pop2_color); + } + else{ + intbar_draw_string(target, &bar, search_str, bar.style.pop2_color); + } + + intbar_draw_string(target, &bar, vars->isearch.str, bar.style.base_color); + }break; + + case APP_STATE_RESIZING: + { + intbar_draw_string(target, &bar, make_lit_string("Resizing!"), bar.style.pop2_color); + }break; + } + } + ProfileSection(thread, app_step, "interaction bar"); + + } + + ProfileEnd(thread, app_step, "total"); + + return app_result; +} + +// BOTTOM + diff --git a/test_data/sample_files/speedtest.cpp b/test_data/sample_files/speedtest.cpp new file mode 100644 index 0000000..63ae438 --- /dev/null +++ b/test_data/sample_files/speedtest.cpp @@ -0,0 +1,19 @@ + +int i, j, count, is_prime; +int *primes; + +primes = (int*)malloc(sizeof(int)*100); +count = 0; + +for (i = 2; i < 100; ++i){ + is_prime = 1; + for (j = 0; j < count; ++j){ + if (i % primes[j] == 0){ + is_prime = 0; + break; + } + } + if (is_prime){ + primes[count++] = i; + } +} \ No newline at end of file diff --git a/test_data/sample_files/sprite.frag b/test_data/sample_files/sprite.frag new file mode 100644 index 0000000..e920c0a --- /dev/null +++ b/test_data/sample_files/sprite.frag @@ -0,0 +1,57 @@ +#version 120 + +varying vec4 v_TexCoord; + +uniform sampler2D u_Sampler; +uniform sampler2D u_NormalMap; + +uniform vec4 u_Color; +uniform float u_SpriteSize; +uniform vec2 u_PixelPosition; +uniform float u_Rotation; + +float VecLen (vec2 Vec) +{ + return sqrt((Vec.x*Vec.x)+(Vec.y*Vec.y)); +} + +void main () { + + // vec2 center = vec2(1280/2, 720/2); + // float light = (500 - distance(center, gl_FragCoord.xy)) / 500; + // gl_FragColor = texture2D(u_sampler, v_texcoord.xy) * vec4(light, light, light, 1); + + // vec4 Color = texture2D(u_Sampler, v_TexCoord.xy); + // vec4 ColorAvg = vec4((Color.r + Color.g + Color.b) / 3.0f, + // (Color.r + Color.g + Color.b) / 3.0f, + // (Color.r + Color.g + Color.b) / 3.0f, + // Color.a); + + float XDiff = (u_PixelPosition.x-u_SpriteSize) - gl_FragCoord.x; + float YDiff = (u_PixelPosition.y+u_SpriteSize) - gl_FragCoord.y; + float DiffLength = sqrt((XDiff*XDiff)+(YDiff*YDiff)); + + float LightX = -100.0f; + float LightY = -100.0f; + + //float Color = 1.0f - (((XDiff)+(YDiff)) / u_SpriteSize); + float Color = 1.0f - (DiffLength / (u_SpriteSize*2.0f)); + if (Color < 0.1f) Color = 0.1f; + vec4 LightingColor = vec4(Color, Color, Color, 1.0f); + + vec4 Texture0 = texture2D(u_Sampler, v_TexCoord.xy); + vec4 Texture1 = texture2D(u_NormalMap, v_TexCoord.xy); + vec3 Normal = (2.0f*Texture1.xyz)-1.0f; + + vec3 LightPos; + LightPos.x = (sin(radians(u_Rotation))*-u_SpriteSize)+(cos(radians(u_Rotation))*-u_SpriteSize); + LightPos.y = (sin(radians(u_Rotation))*-u_SpriteSize)+(cos(radians(u_Rotation))*-u_SpriteSize); + LightPos.z = 0.0f; + float Dot = dot(Normal.xyz, LightPos); + + //LightingColor = vec4(vec3(1.0f-(Dot/u_SpriteSize)), 1.0f); + LightingColor = vec4(1.0f); + + gl_FragColor = (Texture0 * u_Color) * (LightingColor); + //gl_FragColor = vec4(Texture0 * vec4(0.0f, 0.0f, 1.0f, 1.0f)); +} diff --git a/test_data/sample_files/stuff.txt b/test_data/sample_files/stuff.txt new file mode 100644 index 0000000..dda6eb4 --- /dev/null +++ b/test_data/sample_files/stuff.txt @@ -0,0 +1 @@ +abcdefg \ No newline at end of file diff --git a/test_data/sample_files/test_large.cpp b/test_data/sample_files/test_large.cpp new file mode 100644 index 0000000..9e1e5ed --- /dev/null +++ b/test_data/sample_files/test_large.cpp @@ -0,0 +1,393604 @@ +void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +// TODO(allen): Use giant source files! + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + + // TODO(allen): Use really big code files! + + + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +// deepinfile + + + + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +struct Partition_Data{ + u8 *memory_base; + u8 *memory_cursor; + u32 max_size; +}; + +internal void* +partition_allocate(Partition_Data *data, u32 size){ + void *ret = 0; + if ((data->memory_cursor - data->memory_base) + size <= data->max_size && + size > 0){ + ret = data->memory_cursor; + data->memory_cursor += size; + } + return ret; +} + +struct Cursor_Data{ + u32 pos; + u32 line_number, line_off; +}; + +struct Panel_Cursor_Data{ + u32 pos; + u32 x, y; +}; + +struct Editing_Data{ + u32 size, max_size; + void *data; + + Cursor_Data cursor; +}; + +struct Editing_Style{ + Glyph_Set *set; + u32 cursor_color; + u32 default_color; + u32 at_cursor_color; +}; + +struct Editing_Panel{ + i32 x, y; + i32 w, h; + u32 tab_width; + + Panel_Cursor_Data cursor; + u32 scroll_y; + + Editing_Data *buffer; + Editing_Style *style; +}; + +/* + * Buffer Functions + */ + +// TODO(allen): +// +// function(s) for computing cursor_pos from line/off for skip to position +// function(s) for computing line/off from cursor_pos +// +// handle \r \n and \r\n modes better search for RNRN +// +// non-line wrapping rendering function +// +// handle buffer out-of-memory situations +// +// buffer open / save +// + +internal bool32 +buffer_open(Editing_Data *buffer, c_str filename, + void *data_space, u32 data_space_size){ + bool32 result = 0; + + File_Data file = system_load_file(filename); + if (file.size < data_space_size){ + result = 1; + buffer->size = file.size; + buffer->data = data_space; + buffer->max_size = data_space_size; + for (u32 i = 0; i < file.size; ++i){ + ((u8*)buffer->data)[i] = + ((u8*)file.data)[i]; + } + ((u8*)buffer->data)[buffer->size] = 0; + } + system_free_file(file); + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (cursor_pos < pos || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal Panel_Cursor_Data +panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){ + Panel_Cursor_Data result = {}; + + u32 cursor_pos = 0; + u32 counted_x = 0; + u32 counted_y = 0; + u8 *data = (u8*)panel->buffer->data; + u32 size = panel->buffer->size; + u32 character_w = panel->style->set->glyphs[' '].advance; + u32 max_line_length = panel->w / character_w; + + // NOTE(allen): RNRN, this is made for rn mode + while (cursor_pos < size && + (counted_y < cursor_y || + counted_x < cursor_x || + data[cursor_pos] == '\r') + ){ + + if (data[cursor_pos] == '\r'){ + // DO NOTHING + } + else if (data[cursor_pos] == '\n' || + counted_x+1 >= max_line_length){ + + ++counted_y; + if (counted_y > cursor_y){ + // TODO(allen): error here? + // return error code? pointer out? struct? + // just give closest possible? silent in editing_data? + --counted_y; + break; + } + + counted_x = 0; + } + else if (data[cursor_pos] == '\t'){ + counted_x += panel->tab_width; + } + else{ + ++counted_x; + } + + ++cursor_pos; + } + + result.pos = cursor_pos; + result.x = counted_x; + result.y = counted_y; + + return result; +} + +internal void +panel_cursor_match_to_panel(Editing_Panel *panel){ + panel->buffer->cursor.pos = panel->cursor.pos; +} + +internal bool32 +buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){ + bool32 result = 0; + + if (editing_data->size + 1 < editing_data->max_size){ + result = 1; + u8 *data = (u8*)editing_data->data; + for (i32 pos = editing_data->size; + pos >= (i32)editing_data->cursor.pos; --pos){ + data[pos+1] = data[pos]; + } + data[editing_data->cursor.pos] = character; + ++editing_data->size; + } + else{ + // TODO(allen): automatically try to fix right here when this + // happens, or try to avoid this at all costs? + } + + return result; +} + +internal void +panel_insert(Editing_Panel *panel, u8 character){ + Editing_Data *editing_data = panel->buffer; + if (buffer_insert(editing_data, editing_data->cursor.pos, character)){ + ++editing_data->cursor.pos; + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_left(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u8 *data = (u8*)editing_data->data; + + if (editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos > 0){ + --editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal void +panel_move_right(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + u32 size = editing_data->size; + u8* data = (u8*)editing_data->data; + + if (editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + + // NOTE(allen): RNRN, this is made for rn mode + while (data[editing_data->cursor.pos] == '\r' && + editing_data->cursor.pos < size){ + ++editing_data->cursor.pos; + } + } + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos); + panel_cursor_match_to_panel(panel); +} + +internal bool32 +buffer_delete(Editing_Data *editing_data, u32 pos){ + bool32 did_delete = 0; + if (pos >= 0 && pos < editing_data->size){ + did_delete = 1; + i32 size = (i32)(--editing_data->size); + u8 *data = (u8*)editing_data->data; + for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){ + data[adjust_pos] = data[adjust_pos+1]; + } + } + return did_delete; +} + +internal void +panel_backspace(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->cursor.pos > 0 && + buffer_delete(editing_data, editing_data->cursor.pos-1)){ + + panel->cursor = + panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1); + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_delete(Editing_Panel *panel){ + Editing_Data *editing_data = panel->buffer; + if (editing_data->size > 0){ + buffer_delete(editing_data, editing_data->cursor.pos); + } +} + +internal void +panel_move_up(Editing_Panel *panel){ + if (panel->cursor.y > 0){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y-1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); + } +} + +internal void +panel_move_down(Editing_Panel *panel){ + Panel_Cursor_Data newpos = + panel_compute_cursor_from_xy(panel, + panel->cursor.x, + panel->cursor.y+1); + + panel->cursor = newpos; + panel_cursor_match_to_panel(panel); +} + +internal void +panel_basic_mode_key_event(Key_Codes *codes, + Editing_Panel *panel, + Key_Event_Data key_data){ + u16 character = key_data.character; + if (character != 0){ + panel_insert(panel, (u8)character); + }else{ + + if (key_data.keycode == codes->left){ + panel_move_left(panel); + } + else if (key_data.keycode == codes->right){ + panel_move_right(panel); + } + else if (key_data.keycode == codes->back){ + panel_backspace(panel); + } + + else if (key_data.keycode == codes->del){ + panel_delete(panel); + } + else if (key_data.keycode == codes->up){ + panel_move_up(panel); + } + else if (key_data.keycode == codes->down){ + panel_move_down(panel); + } + } +} + +internal void +panel_draw(Render_Target *target, + Editing_Panel *panel, + bool32 is_active){ + + Editing_Style *style = panel->style; + Glyph_Set *set = style->set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 offset_x = panel->x; + i32 offset_y = panel->y; + i32 max_x = panel->w; + i32 max_y = panel->h; + i32 max_line_length = max_x / character_w; + i32 max_lines = max_y / character_h; + + AllowLocal(max_line_length); + AllowLocal(max_lines); + + u32 tab_width = panel->tab_width; + u32 size = panel->buffer->size; + u8 *data = (u8*)panel->buffer->data; + + Panel_Cursor_Data start_cursor; + start_cursor = + panel_compute_cursor_from_xy(panel, 0, panel->scroll_y); + + u32 start_character = start_cursor.pos; + + i32 pos_x = 0; + i32 pos_y = 0; + + for (u32 character_i = start_character; + character_i < size && data[character_i]; + ++character_i){ + + if (pos_x + character_w > max_x){ + pos_x = 0; + pos_y += set->line_skip; + } + + u8 to_render = data[character_i]; + + if (character_i == panel->cursor.pos){ + if (is_active){ + draw_rectangle(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + else{ + draw_rectangle_outline(target, + offset_x + pos_x, + offset_y + pos_y, + character_w, + set->line_skip, + style->cursor_color); + } + } + + // NOTE(allen): RNRN, made for rn mode + if (to_render == '\r'){ + // DO NOTHING + } + + else if (to_render == '\n'){ + pos_x = 0; + pos_y += set->line_skip; + } + + else if (to_render == '\t'){ + pos_x += character_w*tab_width; + } + + else if (set->glyphs[to_render].data){ + i32 advance = set->glyphs[to_render].advance; + + u32 char_color = style->default_color; + if (character_i == panel->cursor.pos && is_active){ + char_color = style->at_cursor_color; + } + + font_draw_glyph(target, set, to_render, + offset_x + pos_x, offset_y + pos_y, + char_color); + pos_x += advance; + } + + else{ + i32 advance = character_w; + + pos_x += advance; + } + + if (pos_y + set->line_skip > max_y){ + break; + } + } +} + +struct App_Vars{ + Glyph_Set glyph_set; + Editing_Data buffer, buffer2; + i32 active_panel; + i32 prev_width, prev_height; + + Editing_Panel panels[2]; + Editing_Style style; + + i32 last_click_x, last_click_y; +}; + +internal bool32 +app_init(Application_Memory *memory){ + + if (font_init() != 0){ + FatalError("Error initializing fonts"); + return 0; + } + + Partition_Data partition = {}; + partition.memory_base = + partition.memory_cursor = (u8*)memory->main_memory; + partition.max_size = memory->main_memory_size; + + memory->font_memory_size = Kbytes(512); + memory->font_memory = + partition_allocate(&partition, memory->font_memory_size); + Assert(memory->font_memory); + + memory->vars_memory_size = sizeof(App_Vars); + memory->vars_memory = + partition_allocate(&partition, memory->vars_memory_size); + Assert(memory->vars_memory); + + memory->buffer_memory_size = memory->main_memory_size - + (memory->vars_memory_size + memory->font_memory_size); + memory->buffer_memory = + partition_allocate(&partition, memory->buffer_memory_size); + Assert(memory->buffer_memory); + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + // NOTE(allen): font setup + i32 memory_used = 0; + if (font_load(&vars->glyph_set, + 15, memory->font_memory, + font_predict_size(15), &memory_used) != 0){ + FatalError("Could not find any fonts"); + } + + // NOTE(allen): buffer setup + u32 buffer_width = Mbytes(1); + u8 *buffer_memory_cursor = (u8*)memory->buffer_memory; + + vars->buffer = {}; + buffer_open(&vars->buffer, "../code/win32_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + vars->buffer2 = {}; + buffer_open(&vars->buffer2, "long_4ed.cpp", + buffer_memory_cursor, buffer_width); + buffer_memory_cursor += buffer_width; + + // NOTE(allen): style and panels setup + vars->style.set = &vars->glyph_set; + vars->style.cursor_color = 0xFF00FF00; + vars->style.default_color = 0xFFFFFFFF; + vars->style.at_cursor_color = 0xFF222222; + + Editing_Panel *panels = vars->panels; + + panels[0].tab_width = 4; + panels[0].cursor = {}; + panels[0].scroll_y = 0; + panels[0].buffer = &vars->buffer; + panels[0].style = &vars->style; + + panels[1].tab_width = 4; + panels[1].cursor = {}; + panels[1].scroll_y = 0; + panels[1].buffer = &vars->buffer2; + panels[1].style = &vars->style; + + return 1; +} + +internal u8* +str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){ + + u8 *result = 0; + bool32 is_negative = (number < 0); + if (is_negative){ + number = -number; + } + + if (number == 0){ + buffer_space[buffer_size - 1] = 0; + buffer_space[buffer_size - 2] = '0'; + result = buffer_space + buffer_size - 2; + } + + else{ + buffer_space[buffer_size - 1] = 0; + u32 str_write_pos = buffer_size - 1; + while (number > 0 && str_write_pos > 0){ + --str_write_pos; + u8 to_write = (u8)(number % 10); + number /= 10; + buffer_space[str_write_pos] = '0' + to_write; + } + if (is_negative && str_write_pos > 0){ + --str_write_pos; + buffer_space[str_write_pos] = '-'; + } + result = buffer_space + str_write_pos; + } + + return result; +} + +internal void +app_step(Key_Codes *codes, + Key_Input_Data *input, Mouse_State *mouse, + bool32 time_step, Render_Target *target, + Application_Memory *memory){ + + App_Vars *vars = (App_Vars*)memory->vars_memory; + + u32 background_color = 0xFF0C0C0C; + + i32 debug_bar_height = vars->glyph_set.line_skip; + Editing_Panel *panels = vars->panels; + + panels[0].x = 2; + panels[0].y = debug_bar_height; + panels[0].w = target->width / 2 - 4; + panels[0].h = target->height - debug_bar_height; + + panels[1].x = panels[0].x + panels[0].w + 5; + panels[1].y = debug_bar_height; + panels[1].w = target->width / 2 - 5; + panels[1].h = target->height - debug_bar_height; + + draw_clear(target, background_color); + + if (vars->prev_width != target->width || + vars->prev_height != target->height){ + + for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){ + panels[panel_i].cursor = + panel_compute_cursor_from_pos(&panels[panel_i], + panels[panel_i].cursor.pos); + } + } + + if (time_step){ + + i32 mx = mouse->x; + i32 my = mouse->y; + i32 clicked_panel = -1; + bool32 mouse_press_event = 0; + + if (mouse->left_button && !mouse->left_button_prev){ + mouse_press_event = 1; + } + + for (i32 panel_i = 0; + panel_i < ArrayCount(vars->panels); + ++panel_i){ + + Glyph_Set *set = &vars->glyph_set; + + i32 character_w = set->glyphs[' '].advance; + i32 character_h = set->line_skip; + i32 max_line_length = panels[panel_i].w / character_w; + i32 max_lines = panels[panel_i].h / character_h; + + if (mouse_press_event && // NOTE(allen): check if mouse event has occured + mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel + mx < panels[panel_i].w + panels[panel_i].x && + my >= panels[panel_i].y && + my < panels[panel_i].h + panels[panel_i].y){ + + if (clicked_panel == -1){ + clicked_panel = panel_i; + + i32 grid_x = (mx - panels[panel_i].x) / character_w; + i32 grid_y = (my - panels[panel_i].y) / character_h; + + vars->last_click_x = grid_x; + vars->last_click_y = grid_y; + + vars->active_panel = panel_i; + Editing_Panel *active_panel = &panels[panel_i]; + + if (grid_x >= 0 && grid_x < max_line_length && + grid_y >= 0 && grid_y < max_lines){ + + i32 pos_x = grid_x; + i32 pos_y = grid_y + active_panel->scroll_y; + + active_panel->cursor = + panel_compute_cursor_from_xy(active_panel, + pos_x, pos_y); + panel_cursor_match_to_panel(active_panel); + + } + + } + else{ + // TODO(allen): debug diagnostics, overlapped panels? + } + } + } + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + if (!input->control_keys[CONTROL_KEY_CONTROL] && + !input->control_keys[CONTROL_KEY_ALT]){ + for (u32 i = 0; i < input->press_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->presses[i]); + } + + for (u32 i = 0; i < input->hold_count; ++i){ + panel_basic_mode_key_event(codes, active_panel, input->holds[i]); + } + } + + // TODO(allen): abstract the max_lines/max_line_length computations + u32 cursor_y = active_panel->cursor.y; + u32 scroll_y = active_panel->scroll_y; + u32 character_h = active_panel->style->set->line_skip; + u32 max_lines = active_panel->h / character_h; + while (cursor_y >= scroll_y + max_lines){ + scroll_y += Max(1, max_lines / 4); + } + + while (cursor_y < scroll_y){ + scroll_y -= Max(1, max_lines / 4); + } + + active_panel->scroll_y = scroll_y; + } + + panel_draw(target, &panels[0], vars->active_panel == 0); + + draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF); + + panel_draw(target, &panels[1], vars->active_panel == 1); + + // NOTE(allen): debug bar + Glyph_Set *set = &vars->glyph_set; + + draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000); + + u8 spacer_str[] = " -- "; + u32 char_color = 0xFF00FFFF; + + i32 pos_x = 0; + i32 pos_y = 0; + + Editing_Panel *active_panel = &panels[vars->active_panel]; + + u8 str[16]; + u8 *str_out; + + str_out= str_from_int(vars->active_panel, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)active_panel->cursor.y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_x, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + for (i32 i = 0; spacer_str[i]; ++i){ + font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[spacer_str[i]].advance; + } + + str_out = str_from_int((i32)vars->last_click_y, str, 16); + for (i32 i = 0; str_out[i]; ++i){ + font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color); + pos_x += set->glyphs[str_out[i]].advance; + } + + vars->prev_width = target->width; + vars->prev_height = target->height; +} + \ No newline at end of file diff --git a/test_data/sample_files/todo.cpp b/test_data/sample_files/todo.cpp new file mode 100644 index 0000000..28be5db --- /dev/null +++ b/test_data/sample_files/todo.cpp @@ -0,0 +1,16 @@ + { + todo thing + thingy + stuff + { + foo; + bar; + baz; + } + + { + asdf + jkl + asdf + } + } diff --git a/test_data/sample_files/todo.txt b/test_data/sample_files/todo.txt new file mode 100644 index 0000000..c76c46a --- /dev/null +++ b/test_data/sample_files/todo.txt @@ -0,0 +1,16 @@ + { + todo thing; + thingy; + stuff; + { + foo; + bar; + baz; + } + + { + asdf; + jkl; + asdf; + } + } diff --git a/test_data/sample_files/unicode.txt b/test_data/sample_files/unicode.txt new file mode 100644 index 0000000..fe1a478 --- /dev/null +++ b/test_data/sample_files/unicode.txt @@ -0,0 +1,11 @@ +"ü„€€€€" +ññññññññññ‽‽‽‽!? +ññññññññññ‽‽‽‽!? + +€£¥¢₹₨₱₩₫₪‽‽‽‽!? +€£¥¢₹₨₱₩₫₪‽‽‽‽!? + +The limit of Æ’ as x approaches α is L if, +for any ε > 0 there exists a δ > 0 such that +|Æ’(x) - L| < δ implies 0 < |x - α| < ε + diff --git a/test_data/sample_files/vulkan.hpp b/test_data/sample_files/vulkan.hpp new file mode 100644 index 0000000..dd34651 --- /dev/null +++ b/test_data/sample_files/vulkan.hpp @@ -0,0 +1,23487 @@ +// Copyright (c) 2015-2017 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +// This header is generated from the Khronos Vulkan XML API Registry. + + +#ifndef VULKAN_HPP +#define VULKAN_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +# include +# include +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +static_assert( VK_HEADER_VERSION == 39 , "Wrong VK_HEADER_VERSION!" ); + +// 32-bit vulkan is not typesafe for handles, so don't allow copy constructors on this platform by default. +// To enable this feature on 32-bit platforms please define VULKAN_HPP_TYPESAFE_CONVERSION +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define VULKAN_HPP_TYPESAFE_CONVERSION 1 +#endif + +#if !defined(VULKAN_HPP_HAS_UNRESTRICTED_UNIONS) +# if defined(__clang__) +# if __has_feature(cxx_unrestricted_unions) +# define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS +# endif +# elif defined(__GNUC__) +# define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +# if 40600 <= GCC_VERSION +# define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS +# endif +# elif defined(_MSC_VER) +# if 1900 <= _MSC_VER +# define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS +# endif +# endif +#endif + + +#if !defined(VULKAN_HPP_INLINE) +# if defined(__clang___) +# if __has_attribute(always_inline) +# define VULKAN_HPP_INLINE __attribute__((always_inline)) __inline__ +# else +# define VULKAN_HPP_INLINE inline +# endif +# elif defined(__GNUC__) +# define VULKAN_HPP_INLINE __attribute__((always_inline)) __inline__ +# elif defined(_MSC_VER) +# define VULKAN_HPP_INLINE __forceinline +# else +# define VULKAN_HPP_INLINE inline +# endif +#endif + +namespace vk +{ + template struct FlagTraits + { + enum { allFlags = 0 }; + }; + + template + class Flags + { + public: + Flags() + : m_mask(0) + { + } + + Flags(BitType bit) + : m_mask(static_cast(bit)) + { + } + + Flags(Flags const& rhs) + : m_mask(rhs.m_mask) + { + } + + Flags & operator=(Flags const& rhs) + { + m_mask = rhs.m_mask; + return *this; + } + + Flags & operator|=(Flags const& rhs) + { + m_mask |= rhs.m_mask; + return *this; + } + + Flags & operator&=(Flags const& rhs) + { + m_mask &= rhs.m_mask; + return *this; + } + + Flags & operator^=(Flags const& rhs) + { + m_mask ^= rhs.m_mask; + return *this; + } + + Flags operator|(Flags const& rhs) const + { + Flags result(*this); + result |= rhs; + return result; + } + + Flags operator&(Flags const& rhs) const + { + Flags result(*this); + result &= rhs; + return result; + } + + Flags operator^(Flags const& rhs) const + { + Flags result(*this); + result ^= rhs; + return result; + } + + bool operator!() const + { + return !m_mask; + } + + Flags operator~() const + { + Flags result(*this); + result.m_mask ^= FlagTraits::allFlags; + return result; + } + + bool operator==(Flags const& rhs) const + { + return m_mask == rhs.m_mask; + } + + bool operator!=(Flags const& rhs) const + { + return m_mask != rhs.m_mask; + } + + explicit operator bool() const + { + return !!m_mask; + } + + explicit operator MaskType() const + { + return m_mask; + } + + private: + MaskType m_mask; + }; + + template + Flags operator|(BitType bit, Flags const& flags) + { + return flags | bit; + } + + template + Flags operator&(BitType bit, Flags const& flags) + { + return flags & bit; + } + + template + Flags operator^(BitType bit, Flags const& flags) + { + return flags ^ bit; + } + + + template + class Optional + { + public: + Optional(RefType & reference) { m_ptr = &reference; } + Optional(RefType * ptr) { m_ptr = ptr; } + Optional(std::nullptr_t) { m_ptr = nullptr; } + + operator RefType*() const { return m_ptr; } + RefType const* operator->() const { return m_ptr; } + explicit operator bool() const { return !!m_ptr; } + + private: + RefType *m_ptr; + }; + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template + class ArrayProxy + { + public: + ArrayProxy(std::nullptr_t) + : m_count(0) + , m_ptr(nullptr) + {} + + ArrayProxy(T & ptr) + : m_count(1) + , m_ptr(&ptr) + {} + + ArrayProxy(uint32_t count, T * ptr) + : m_count(count) + , m_ptr(ptr) + {} + + template + ArrayProxy(std::array::type, N> & data) + : m_count(N) + , m_ptr(data.data()) + {} + + template + ArrayProxy(std::array::type, N> const& data) + : m_count(N) + , m_ptr(data.data()) + {} + + template ::type>> + ArrayProxy(std::vector::type, Allocator> & data) + : m_count(static_cast(data.size())) + , m_ptr(data.data()) + {} + + template ::type>> + ArrayProxy(std::vector::type, Allocator> const& data) + : m_count(static_cast(data.size())) + , m_ptr(data.data()) + {} + + ArrayProxy(std::initializer_list const& data) + : m_count(static_cast(data.end() - data.begin())) + , m_ptr(data.begin()) + {} + + const T * begin() const + { + return m_ptr; + } + + const T * end() const + { + return m_ptr + m_count; + } + + const T & front() const + { + assert(m_count && m_ptr); + return *m_ptr; + } + + const T & back() const + { + assert(m_count && m_ptr); + return *(m_ptr + m_count - 1); + } + + bool empty() const + { + return (m_count == 0); + } + + uint32_t size() const + { + return m_count; + } + + T * data() const + { + return m_ptr; + } + + private: + uint32_t m_count; + T * m_ptr; + }; +#endif + + enum class Result + { + eSuccess = VK_SUCCESS, + eNotReady = VK_NOT_READY, + eTimeout = VK_TIMEOUT, + eEventSet = VK_EVENT_SET, + eEventReset = VK_EVENT_RESET, + eIncomplete = VK_INCOMPLETE, + eErrorOutOfHostMemory = VK_ERROR_OUT_OF_HOST_MEMORY, + eErrorOutOfDeviceMemory = VK_ERROR_OUT_OF_DEVICE_MEMORY, + eErrorInitializationFailed = VK_ERROR_INITIALIZATION_FAILED, + eErrorDeviceLost = VK_ERROR_DEVICE_LOST, + eErrorMemoryMapFailed = VK_ERROR_MEMORY_MAP_FAILED, + eErrorLayerNotPresent = VK_ERROR_LAYER_NOT_PRESENT, + eErrorExtensionNotPresent = VK_ERROR_EXTENSION_NOT_PRESENT, + eErrorFeatureNotPresent = VK_ERROR_FEATURE_NOT_PRESENT, + eErrorIncompatibleDriver = VK_ERROR_INCOMPATIBLE_DRIVER, + eErrorTooManyObjects = VK_ERROR_TOO_MANY_OBJECTS, + eErrorFormatNotSupported = VK_ERROR_FORMAT_NOT_SUPPORTED, + eErrorFragmentedPool = VK_ERROR_FRAGMENTED_POOL, + eErrorSurfaceLostKHR = VK_ERROR_SURFACE_LOST_KHR, + eErrorNativeWindowInUseKHR = VK_ERROR_NATIVE_WINDOW_IN_USE_KHR, + eSuboptimalKHR = VK_SUBOPTIMAL_KHR, + eErrorOutOfDateKHR = VK_ERROR_OUT_OF_DATE_KHR, + eErrorIncompatibleDisplayKHR = VK_ERROR_INCOMPATIBLE_DISPLAY_KHR, + eErrorValidationFailedEXT = VK_ERROR_VALIDATION_FAILED_EXT, + eErrorInvalidShaderNV = VK_ERROR_INVALID_SHADER_NV, + eErrorOutOfPoolMemoryKHR = VK_ERROR_OUT_OF_POOL_MEMORY_KHR + }; + + VULKAN_HPP_INLINE std::string to_string(Result value) + { + switch (value) + { + case Result::eSuccess: return "Success"; + case Result::eNotReady: return "NotReady"; + case Result::eTimeout: return "Timeout"; + case Result::eEventSet: return "EventSet"; + case Result::eEventReset: return "EventReset"; + case Result::eIncomplete: return "Incomplete"; + case Result::eErrorOutOfHostMemory: return "ErrorOutOfHostMemory"; + case Result::eErrorOutOfDeviceMemory: return "ErrorOutOfDeviceMemory"; + case Result::eErrorInitializationFailed: return "ErrorInitializationFailed"; + case Result::eErrorDeviceLost: return "ErrorDeviceLost"; + case Result::eErrorMemoryMapFailed: return "ErrorMemoryMapFailed"; + case Result::eErrorLayerNotPresent: return "ErrorLayerNotPresent"; + case Result::eErrorExtensionNotPresent: return "ErrorExtensionNotPresent"; + case Result::eErrorFeatureNotPresent: return "ErrorFeatureNotPresent"; + case Result::eErrorIncompatibleDriver: return "ErrorIncompatibleDriver"; + case Result::eErrorTooManyObjects: return "ErrorTooManyObjects"; + case Result::eErrorFormatNotSupported: return "ErrorFormatNotSupported"; + case Result::eErrorFragmentedPool: return "ErrorFragmentedPool"; + case Result::eErrorSurfaceLostKHR: return "ErrorSurfaceLostKHR"; + case Result::eErrorNativeWindowInUseKHR: return "ErrorNativeWindowInUseKHR"; + case Result::eSuboptimalKHR: return "SuboptimalKHR"; + case Result::eErrorOutOfDateKHR: return "ErrorOutOfDateKHR"; + case Result::eErrorIncompatibleDisplayKHR: return "ErrorIncompatibleDisplayKHR"; + case Result::eErrorValidationFailedEXT: return "ErrorValidationFailedEXT"; + case Result::eErrorInvalidShaderNV: return "ErrorInvalidShaderNV"; + case Result::eErrorOutOfPoolMemoryKHR: return "ErrorOutOfPoolMemoryKHR"; + default: return "invalid"; + } + } + +#if defined(_MSC_VER) && (_MSC_VER == 1800) +# define noexcept _NOEXCEPT +#endif + + class ErrorCategoryImpl : public std::error_category + { + public: + virtual const char* name() const noexcept override { return "vk::Result"; } + virtual std::string message(int ev) const override { return to_string(static_cast(ev)); } + }; + +#if defined(_MSC_VER) && (_MSC_VER == 1800) +# undef noexcept +#endif + + VULKAN_HPP_INLINE const std::error_category& errorCategory() + { + static ErrorCategoryImpl instance; + return instance; + } + + VULKAN_HPP_INLINE std::error_code make_error_code(Result e) + { + return std::error_code(static_cast(e), errorCategory()); + } + + VULKAN_HPP_INLINE std::error_condition make_error_condition(Result e) + { + return std::error_condition(static_cast(e), errorCategory()); + } + +} // namespace vk + +namespace std +{ + template <> + struct is_error_code_enum : public true_type + {}; +} + +namespace vk +{ + template + struct ResultValue + { + ResultValue( Result r, T & v ) + : result( r ) + , value( v ) + {} + + Result result; + T value; + + operator std::tuple() { return std::tuple(result, value); } + }; + + template + struct ResultValueType + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + typedef ResultValue type; +#else + typedef T type; +#endif + }; + + template <> struct ResultValueType + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + typedef Result type; +#else + typedef void type; +#endif + }; + + VULKAN_HPP_INLINE ResultValueType::type createResultValue( Result result, char const * message ) + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( result == Result::eSuccess ); + return result; +#else + if ( result != Result::eSuccess ) + { + throw std::system_error( result, message ); + } +#endif + } + + template + VULKAN_HPP_INLINE typename ResultValueType::type createResultValue( Result result, T & data, char const * message ) + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( result == Result::eSuccess ); + return ResultValue( result, data ); +#else + if ( result != Result::eSuccess ) + { + throw std::system_error( result, message ); + } + return data; +#endif + } + + VULKAN_HPP_INLINE Result createResultValue( Result result, char const * message, std::initializer_list successCodes ) + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( std::find( successCodes.begin(), successCodes.end(), result ) != successCodes.end() ); +#else + if ( std::find( successCodes.begin(), successCodes.end(), result ) == successCodes.end() ) + { + throw std::system_error( result, message ); + } +#endif + return result; + } + + template + VULKAN_HPP_INLINE ResultValue createResultValue( Result result, T & data, char const * message, std::initializer_list successCodes ) + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( std::find( successCodes.begin(), successCodes.end(), result ) != successCodes.end() ); +#else + if ( std::find( successCodes.begin(), successCodes.end(), result ) == successCodes.end() ) + { + throw std::system_error( result, message ); + } +#endif + return ResultValue( result, data ); + } + + using SampleMask = uint32_t; + + using Bool32 = uint32_t; + + using DeviceSize = uint64_t; + + enum class FramebufferCreateFlagBits + { + }; + + using FramebufferCreateFlags = Flags; + + VULKAN_HPP_INLINE FramebufferCreateFlags operator|( FramebufferCreateFlagBits bit0, FramebufferCreateFlagBits bit1 ) + { + return FramebufferCreateFlags( bit0 ) | bit1; + } + + enum class QueryPoolCreateFlagBits + { + }; + + using QueryPoolCreateFlags = Flags; + + VULKAN_HPP_INLINE QueryPoolCreateFlags operator|( QueryPoolCreateFlagBits bit0, QueryPoolCreateFlagBits bit1 ) + { + return QueryPoolCreateFlags( bit0 ) | bit1; + } + + enum class RenderPassCreateFlagBits + { + }; + + using RenderPassCreateFlags = Flags; + + VULKAN_HPP_INLINE RenderPassCreateFlags operator|( RenderPassCreateFlagBits bit0, RenderPassCreateFlagBits bit1 ) + { + return RenderPassCreateFlags( bit0 ) | bit1; + } + + enum class SamplerCreateFlagBits + { + }; + + using SamplerCreateFlags = Flags; + + VULKAN_HPP_INLINE SamplerCreateFlags operator|( SamplerCreateFlagBits bit0, SamplerCreateFlagBits bit1 ) + { + return SamplerCreateFlags( bit0 ) | bit1; + } + + enum class PipelineLayoutCreateFlagBits + { + }; + + using PipelineLayoutCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineLayoutCreateFlags operator|( PipelineLayoutCreateFlagBits bit0, PipelineLayoutCreateFlagBits bit1 ) + { + return PipelineLayoutCreateFlags( bit0 ) | bit1; + } + + enum class PipelineCacheCreateFlagBits + { + }; + + using PipelineCacheCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineCacheCreateFlags operator|( PipelineCacheCreateFlagBits bit0, PipelineCacheCreateFlagBits bit1 ) + { + return PipelineCacheCreateFlags( bit0 ) | bit1; + } + + enum class PipelineDepthStencilStateCreateFlagBits + { + }; + + using PipelineDepthStencilStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineDepthStencilStateCreateFlags operator|( PipelineDepthStencilStateCreateFlagBits bit0, PipelineDepthStencilStateCreateFlagBits bit1 ) + { + return PipelineDepthStencilStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineDynamicStateCreateFlagBits + { + }; + + using PipelineDynamicStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineDynamicStateCreateFlags operator|( PipelineDynamicStateCreateFlagBits bit0, PipelineDynamicStateCreateFlagBits bit1 ) + { + return PipelineDynamicStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineColorBlendStateCreateFlagBits + { + }; + + using PipelineColorBlendStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineColorBlendStateCreateFlags operator|( PipelineColorBlendStateCreateFlagBits bit0, PipelineColorBlendStateCreateFlagBits bit1 ) + { + return PipelineColorBlendStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineMultisampleStateCreateFlagBits + { + }; + + using PipelineMultisampleStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineMultisampleStateCreateFlags operator|( PipelineMultisampleStateCreateFlagBits bit0, PipelineMultisampleStateCreateFlagBits bit1 ) + { + return PipelineMultisampleStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineRasterizationStateCreateFlagBits + { + }; + + using PipelineRasterizationStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineRasterizationStateCreateFlags operator|( PipelineRasterizationStateCreateFlagBits bit0, PipelineRasterizationStateCreateFlagBits bit1 ) + { + return PipelineRasterizationStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineViewportStateCreateFlagBits + { + }; + + using PipelineViewportStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineViewportStateCreateFlags operator|( PipelineViewportStateCreateFlagBits bit0, PipelineViewportStateCreateFlagBits bit1 ) + { + return PipelineViewportStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineTessellationStateCreateFlagBits + { + }; + + using PipelineTessellationStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineTessellationStateCreateFlags operator|( PipelineTessellationStateCreateFlagBits bit0, PipelineTessellationStateCreateFlagBits bit1 ) + { + return PipelineTessellationStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineInputAssemblyStateCreateFlagBits + { + }; + + using PipelineInputAssemblyStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineInputAssemblyStateCreateFlags operator|( PipelineInputAssemblyStateCreateFlagBits bit0, PipelineInputAssemblyStateCreateFlagBits bit1 ) + { + return PipelineInputAssemblyStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineVertexInputStateCreateFlagBits + { + }; + + using PipelineVertexInputStateCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineVertexInputStateCreateFlags operator|( PipelineVertexInputStateCreateFlagBits bit0, PipelineVertexInputStateCreateFlagBits bit1 ) + { + return PipelineVertexInputStateCreateFlags( bit0 ) | bit1; + } + + enum class PipelineShaderStageCreateFlagBits + { + }; + + using PipelineShaderStageCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineShaderStageCreateFlags operator|( PipelineShaderStageCreateFlagBits bit0, PipelineShaderStageCreateFlagBits bit1 ) + { + return PipelineShaderStageCreateFlags( bit0 ) | bit1; + } + + enum class DescriptorSetLayoutCreateFlagBits + { + }; + + using DescriptorSetLayoutCreateFlags = Flags; + + VULKAN_HPP_INLINE DescriptorSetLayoutCreateFlags operator|( DescriptorSetLayoutCreateFlagBits bit0, DescriptorSetLayoutCreateFlagBits bit1 ) + { + return DescriptorSetLayoutCreateFlags( bit0 ) | bit1; + } + + enum class BufferViewCreateFlagBits + { + }; + + using BufferViewCreateFlags = Flags; + + VULKAN_HPP_INLINE BufferViewCreateFlags operator|( BufferViewCreateFlagBits bit0, BufferViewCreateFlagBits bit1 ) + { + return BufferViewCreateFlags( bit0 ) | bit1; + } + + enum class InstanceCreateFlagBits + { + }; + + using InstanceCreateFlags = Flags; + + VULKAN_HPP_INLINE InstanceCreateFlags operator|( InstanceCreateFlagBits bit0, InstanceCreateFlagBits bit1 ) + { + return InstanceCreateFlags( bit0 ) | bit1; + } + + enum class DeviceCreateFlagBits + { + }; + + using DeviceCreateFlags = Flags; + + VULKAN_HPP_INLINE DeviceCreateFlags operator|( DeviceCreateFlagBits bit0, DeviceCreateFlagBits bit1 ) + { + return DeviceCreateFlags( bit0 ) | bit1; + } + + enum class DeviceQueueCreateFlagBits + { + }; + + using DeviceQueueCreateFlags = Flags; + + VULKAN_HPP_INLINE DeviceQueueCreateFlags operator|( DeviceQueueCreateFlagBits bit0, DeviceQueueCreateFlagBits bit1 ) + { + return DeviceQueueCreateFlags( bit0 ) | bit1; + } + + enum class ImageViewCreateFlagBits + { + }; + + using ImageViewCreateFlags = Flags; + + VULKAN_HPP_INLINE ImageViewCreateFlags operator|( ImageViewCreateFlagBits bit0, ImageViewCreateFlagBits bit1 ) + { + return ImageViewCreateFlags( bit0 ) | bit1; + } + + enum class SemaphoreCreateFlagBits + { + }; + + using SemaphoreCreateFlags = Flags; + + VULKAN_HPP_INLINE SemaphoreCreateFlags operator|( SemaphoreCreateFlagBits bit0, SemaphoreCreateFlagBits bit1 ) + { + return SemaphoreCreateFlags( bit0 ) | bit1; + } + + enum class ShaderModuleCreateFlagBits + { + }; + + using ShaderModuleCreateFlags = Flags; + + VULKAN_HPP_INLINE ShaderModuleCreateFlags operator|( ShaderModuleCreateFlagBits bit0, ShaderModuleCreateFlagBits bit1 ) + { + return ShaderModuleCreateFlags( bit0 ) | bit1; + } + + enum class EventCreateFlagBits + { + }; + + using EventCreateFlags = Flags; + + VULKAN_HPP_INLINE EventCreateFlags operator|( EventCreateFlagBits bit0, EventCreateFlagBits bit1 ) + { + return EventCreateFlags( bit0 ) | bit1; + } + + enum class MemoryMapFlagBits + { + }; + + using MemoryMapFlags = Flags; + + VULKAN_HPP_INLINE MemoryMapFlags operator|( MemoryMapFlagBits bit0, MemoryMapFlagBits bit1 ) + { + return MemoryMapFlags( bit0 ) | bit1; + } + + enum class SubpassDescriptionFlagBits + { + }; + + using SubpassDescriptionFlags = Flags; + + VULKAN_HPP_INLINE SubpassDescriptionFlags operator|( SubpassDescriptionFlagBits bit0, SubpassDescriptionFlagBits bit1 ) + { + return SubpassDescriptionFlags( bit0 ) | bit1; + } + + enum class DescriptorPoolResetFlagBits + { + }; + + using DescriptorPoolResetFlags = Flags; + + VULKAN_HPP_INLINE DescriptorPoolResetFlags operator|( DescriptorPoolResetFlagBits bit0, DescriptorPoolResetFlagBits bit1 ) + { + return DescriptorPoolResetFlags( bit0 ) | bit1; + } + + enum class SwapchainCreateFlagBitsKHR + { + }; + + using SwapchainCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE SwapchainCreateFlagsKHR operator|( SwapchainCreateFlagBitsKHR bit0, SwapchainCreateFlagBitsKHR bit1 ) + { + return SwapchainCreateFlagsKHR( bit0 ) | bit1; + } + + enum class DisplayModeCreateFlagBitsKHR + { + }; + + using DisplayModeCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE DisplayModeCreateFlagsKHR operator|( DisplayModeCreateFlagBitsKHR bit0, DisplayModeCreateFlagBitsKHR bit1 ) + { + return DisplayModeCreateFlagsKHR( bit0 ) | bit1; + } + + enum class DisplaySurfaceCreateFlagBitsKHR + { + }; + + using DisplaySurfaceCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE DisplaySurfaceCreateFlagsKHR operator|( DisplaySurfaceCreateFlagBitsKHR bit0, DisplaySurfaceCreateFlagBitsKHR bit1 ) + { + return DisplaySurfaceCreateFlagsKHR( bit0 ) | bit1; + } + +#ifdef VK_USE_PLATFORM_ANDROID_KHR + enum class AndroidSurfaceCreateFlagBitsKHR + { + }; +#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + +#ifdef VK_USE_PLATFORM_ANDROID_KHR + using AndroidSurfaceCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE AndroidSurfaceCreateFlagsKHR operator|( AndroidSurfaceCreateFlagBitsKHR bit0, AndroidSurfaceCreateFlagBitsKHR bit1 ) + { + return AndroidSurfaceCreateFlagsKHR( bit0 ) | bit1; + } +#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + +#ifdef VK_USE_PLATFORM_MIR_KHR + enum class MirSurfaceCreateFlagBitsKHR + { + }; +#endif /*VK_USE_PLATFORM_MIR_KHR*/ + +#ifdef VK_USE_PLATFORM_MIR_KHR + using MirSurfaceCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE MirSurfaceCreateFlagsKHR operator|( MirSurfaceCreateFlagBitsKHR bit0, MirSurfaceCreateFlagBitsKHR bit1 ) + { + return MirSurfaceCreateFlagsKHR( bit0 ) | bit1; + } +#endif /*VK_USE_PLATFORM_MIR_KHR*/ + +#ifdef VK_USE_PLATFORM_VI_NN + enum class ViSurfaceCreateFlagBitsNN + { + }; +#endif /*VK_USE_PLATFORM_VI_NN*/ + +#ifdef VK_USE_PLATFORM_VI_NN + using ViSurfaceCreateFlagsNN = Flags; + + VULKAN_HPP_INLINE ViSurfaceCreateFlagsNN operator|( ViSurfaceCreateFlagBitsNN bit0, ViSurfaceCreateFlagBitsNN bit1 ) + { + return ViSurfaceCreateFlagsNN( bit0 ) | bit1; + } +#endif /*VK_USE_PLATFORM_VI_NN*/ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + enum class WaylandSurfaceCreateFlagBitsKHR + { + }; +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + using WaylandSurfaceCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE WaylandSurfaceCreateFlagsKHR operator|( WaylandSurfaceCreateFlagBitsKHR bit0, WaylandSurfaceCreateFlagBitsKHR bit1 ) + { + return WaylandSurfaceCreateFlagsKHR( bit0 ) | bit1; + } +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + enum class Win32SurfaceCreateFlagBitsKHR + { + }; +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + using Win32SurfaceCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE Win32SurfaceCreateFlagsKHR operator|( Win32SurfaceCreateFlagBitsKHR bit0, Win32SurfaceCreateFlagBitsKHR bit1 ) + { + return Win32SurfaceCreateFlagsKHR( bit0 ) | bit1; + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifdef VK_USE_PLATFORM_XLIB_KHR + enum class XlibSurfaceCreateFlagBitsKHR + { + }; +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ + +#ifdef VK_USE_PLATFORM_XLIB_KHR + using XlibSurfaceCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE XlibSurfaceCreateFlagsKHR operator|( XlibSurfaceCreateFlagBitsKHR bit0, XlibSurfaceCreateFlagBitsKHR bit1 ) + { + return XlibSurfaceCreateFlagsKHR( bit0 ) | bit1; + } +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ + +#ifdef VK_USE_PLATFORM_XCB_KHR + enum class XcbSurfaceCreateFlagBitsKHR + { + }; +#endif /*VK_USE_PLATFORM_XCB_KHR*/ + +#ifdef VK_USE_PLATFORM_XCB_KHR + using XcbSurfaceCreateFlagsKHR = Flags; + + VULKAN_HPP_INLINE XcbSurfaceCreateFlagsKHR operator|( XcbSurfaceCreateFlagBitsKHR bit0, XcbSurfaceCreateFlagBitsKHR bit1 ) + { + return XcbSurfaceCreateFlagsKHR( bit0 ) | bit1; + } +#endif /*VK_USE_PLATFORM_XCB_KHR*/ + + enum class CommandPoolTrimFlagBitsKHR + { + }; + + using CommandPoolTrimFlagsKHR = Flags; + + VULKAN_HPP_INLINE CommandPoolTrimFlagsKHR operator|( CommandPoolTrimFlagBitsKHR bit0, CommandPoolTrimFlagBitsKHR bit1 ) + { + return CommandPoolTrimFlagsKHR( bit0 ) | bit1; + } + + class DeviceMemory + { + public: + DeviceMemory() + : m_deviceMemory(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + DeviceMemory(VkDeviceMemory deviceMemory) + : m_deviceMemory(deviceMemory) + {} + + DeviceMemory& operator=(VkDeviceMemory deviceMemory) + { + m_deviceMemory = deviceMemory; + return *this; + } +#endif + + bool operator==(DeviceMemory const &rhs) const + { + return m_deviceMemory == rhs.m_deviceMemory; + } + + bool operator!=(DeviceMemory const &rhs) const + { + return m_deviceMemory != rhs.m_deviceMemory; + } + + bool operator<(DeviceMemory const &rhs) const + { + return m_deviceMemory < rhs.m_deviceMemory; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDeviceMemory() const + { + return m_deviceMemory; + } + + explicit operator bool() const + { + return m_deviceMemory != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_deviceMemory == VK_NULL_HANDLE; + } + + private: + VkDeviceMemory m_deviceMemory; + }; + static_assert( sizeof( DeviceMemory ) == sizeof( VkDeviceMemory ), "handle and wrapper have different size!" ); + + class CommandPool + { + public: + CommandPool() + : m_commandPool(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + CommandPool(VkCommandPool commandPool) + : m_commandPool(commandPool) + {} + + CommandPool& operator=(VkCommandPool commandPool) + { + m_commandPool = commandPool; + return *this; + } +#endif + + bool operator==(CommandPool const &rhs) const + { + return m_commandPool == rhs.m_commandPool; + } + + bool operator!=(CommandPool const &rhs) const + { + return m_commandPool != rhs.m_commandPool; + } + + bool operator<(CommandPool const &rhs) const + { + return m_commandPool < rhs.m_commandPool; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkCommandPool() const + { + return m_commandPool; + } + + explicit operator bool() const + { + return m_commandPool != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_commandPool == VK_NULL_HANDLE; + } + + private: + VkCommandPool m_commandPool; + }; + static_assert( sizeof( CommandPool ) == sizeof( VkCommandPool ), "handle and wrapper have different size!" ); + + class Buffer + { + public: + Buffer() + : m_buffer(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Buffer(VkBuffer buffer) + : m_buffer(buffer) + {} + + Buffer& operator=(VkBuffer buffer) + { + m_buffer = buffer; + return *this; + } +#endif + + bool operator==(Buffer const &rhs) const + { + return m_buffer == rhs.m_buffer; + } + + bool operator!=(Buffer const &rhs) const + { + return m_buffer != rhs.m_buffer; + } + + bool operator<(Buffer const &rhs) const + { + return m_buffer < rhs.m_buffer; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkBuffer() const + { + return m_buffer; + } + + explicit operator bool() const + { + return m_buffer != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_buffer == VK_NULL_HANDLE; + } + + private: + VkBuffer m_buffer; + }; + static_assert( sizeof( Buffer ) == sizeof( VkBuffer ), "handle and wrapper have different size!" ); + + class BufferView + { + public: + BufferView() + : m_bufferView(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + BufferView(VkBufferView bufferView) + : m_bufferView(bufferView) + {} + + BufferView& operator=(VkBufferView bufferView) + { + m_bufferView = bufferView; + return *this; + } +#endif + + bool operator==(BufferView const &rhs) const + { + return m_bufferView == rhs.m_bufferView; + } + + bool operator!=(BufferView const &rhs) const + { + return m_bufferView != rhs.m_bufferView; + } + + bool operator<(BufferView const &rhs) const + { + return m_bufferView < rhs.m_bufferView; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkBufferView() const + { + return m_bufferView; + } + + explicit operator bool() const + { + return m_bufferView != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_bufferView == VK_NULL_HANDLE; + } + + private: + VkBufferView m_bufferView; + }; + static_assert( sizeof( BufferView ) == sizeof( VkBufferView ), "handle and wrapper have different size!" ); + + class Image + { + public: + Image() + : m_image(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Image(VkImage image) + : m_image(image) + {} + + Image& operator=(VkImage image) + { + m_image = image; + return *this; + } +#endif + + bool operator==(Image const &rhs) const + { + return m_image == rhs.m_image; + } + + bool operator!=(Image const &rhs) const + { + return m_image != rhs.m_image; + } + + bool operator<(Image const &rhs) const + { + return m_image < rhs.m_image; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkImage() const + { + return m_image; + } + + explicit operator bool() const + { + return m_image != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_image == VK_NULL_HANDLE; + } + + private: + VkImage m_image; + }; + static_assert( sizeof( Image ) == sizeof( VkImage ), "handle and wrapper have different size!" ); + + class ImageView + { + public: + ImageView() + : m_imageView(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + ImageView(VkImageView imageView) + : m_imageView(imageView) + {} + + ImageView& operator=(VkImageView imageView) + { + m_imageView = imageView; + return *this; + } +#endif + + bool operator==(ImageView const &rhs) const + { + return m_imageView == rhs.m_imageView; + } + + bool operator!=(ImageView const &rhs) const + { + return m_imageView != rhs.m_imageView; + } + + bool operator<(ImageView const &rhs) const + { + return m_imageView < rhs.m_imageView; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkImageView() const + { + return m_imageView; + } + + explicit operator bool() const + { + return m_imageView != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_imageView == VK_NULL_HANDLE; + } + + private: + VkImageView m_imageView; + }; + static_assert( sizeof( ImageView ) == sizeof( VkImageView ), "handle and wrapper have different size!" ); + + class ShaderModule + { + public: + ShaderModule() + : m_shaderModule(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + ShaderModule(VkShaderModule shaderModule) + : m_shaderModule(shaderModule) + {} + + ShaderModule& operator=(VkShaderModule shaderModule) + { + m_shaderModule = shaderModule; + return *this; + } +#endif + + bool operator==(ShaderModule const &rhs) const + { + return m_shaderModule == rhs.m_shaderModule; + } + + bool operator!=(ShaderModule const &rhs) const + { + return m_shaderModule != rhs.m_shaderModule; + } + + bool operator<(ShaderModule const &rhs) const + { + return m_shaderModule < rhs.m_shaderModule; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkShaderModule() const + { + return m_shaderModule; + } + + explicit operator bool() const + { + return m_shaderModule != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_shaderModule == VK_NULL_HANDLE; + } + + private: + VkShaderModule m_shaderModule; + }; + static_assert( sizeof( ShaderModule ) == sizeof( VkShaderModule ), "handle and wrapper have different size!" ); + + class Pipeline + { + public: + Pipeline() + : m_pipeline(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Pipeline(VkPipeline pipeline) + : m_pipeline(pipeline) + {} + + Pipeline& operator=(VkPipeline pipeline) + { + m_pipeline = pipeline; + return *this; + } +#endif + + bool operator==(Pipeline const &rhs) const + { + return m_pipeline == rhs.m_pipeline; + } + + bool operator!=(Pipeline const &rhs) const + { + return m_pipeline != rhs.m_pipeline; + } + + bool operator<(Pipeline const &rhs) const + { + return m_pipeline < rhs.m_pipeline; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkPipeline() const + { + return m_pipeline; + } + + explicit operator bool() const + { + return m_pipeline != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_pipeline == VK_NULL_HANDLE; + } + + private: + VkPipeline m_pipeline; + }; + static_assert( sizeof( Pipeline ) == sizeof( VkPipeline ), "handle and wrapper have different size!" ); + + class PipelineLayout + { + public: + PipelineLayout() + : m_pipelineLayout(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + PipelineLayout(VkPipelineLayout pipelineLayout) + : m_pipelineLayout(pipelineLayout) + {} + + PipelineLayout& operator=(VkPipelineLayout pipelineLayout) + { + m_pipelineLayout = pipelineLayout; + return *this; + } +#endif + + bool operator==(PipelineLayout const &rhs) const + { + return m_pipelineLayout == rhs.m_pipelineLayout; + } + + bool operator!=(PipelineLayout const &rhs) const + { + return m_pipelineLayout != rhs.m_pipelineLayout; + } + + bool operator<(PipelineLayout const &rhs) const + { + return m_pipelineLayout < rhs.m_pipelineLayout; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkPipelineLayout() const + { + return m_pipelineLayout; + } + + explicit operator bool() const + { + return m_pipelineLayout != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_pipelineLayout == VK_NULL_HANDLE; + } + + private: + VkPipelineLayout m_pipelineLayout; + }; + static_assert( sizeof( PipelineLayout ) == sizeof( VkPipelineLayout ), "handle and wrapper have different size!" ); + + class Sampler + { + public: + Sampler() + : m_sampler(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Sampler(VkSampler sampler) + : m_sampler(sampler) + {} + + Sampler& operator=(VkSampler sampler) + { + m_sampler = sampler; + return *this; + } +#endif + + bool operator==(Sampler const &rhs) const + { + return m_sampler == rhs.m_sampler; + } + + bool operator!=(Sampler const &rhs) const + { + return m_sampler != rhs.m_sampler; + } + + bool operator<(Sampler const &rhs) const + { + return m_sampler < rhs.m_sampler; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkSampler() const + { + return m_sampler; + } + + explicit operator bool() const + { + return m_sampler != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_sampler == VK_NULL_HANDLE; + } + + private: + VkSampler m_sampler; + }; + static_assert( sizeof( Sampler ) == sizeof( VkSampler ), "handle and wrapper have different size!" ); + + class DescriptorSet + { + public: + DescriptorSet() + : m_descriptorSet(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + DescriptorSet(VkDescriptorSet descriptorSet) + : m_descriptorSet(descriptorSet) + {} + + DescriptorSet& operator=(VkDescriptorSet descriptorSet) + { + m_descriptorSet = descriptorSet; + return *this; + } +#endif + + bool operator==(DescriptorSet const &rhs) const + { + return m_descriptorSet == rhs.m_descriptorSet; + } + + bool operator!=(DescriptorSet const &rhs) const + { + return m_descriptorSet != rhs.m_descriptorSet; + } + + bool operator<(DescriptorSet const &rhs) const + { + return m_descriptorSet < rhs.m_descriptorSet; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDescriptorSet() const + { + return m_descriptorSet; + } + + explicit operator bool() const + { + return m_descriptorSet != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_descriptorSet == VK_NULL_HANDLE; + } + + private: + VkDescriptorSet m_descriptorSet; + }; + static_assert( sizeof( DescriptorSet ) == sizeof( VkDescriptorSet ), "handle and wrapper have different size!" ); + + class DescriptorSetLayout + { + public: + DescriptorSetLayout() + : m_descriptorSetLayout(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + DescriptorSetLayout(VkDescriptorSetLayout descriptorSetLayout) + : m_descriptorSetLayout(descriptorSetLayout) + {} + + DescriptorSetLayout& operator=(VkDescriptorSetLayout descriptorSetLayout) + { + m_descriptorSetLayout = descriptorSetLayout; + return *this; + } +#endif + + bool operator==(DescriptorSetLayout const &rhs) const + { + return m_descriptorSetLayout == rhs.m_descriptorSetLayout; + } + + bool operator!=(DescriptorSetLayout const &rhs) const + { + return m_descriptorSetLayout != rhs.m_descriptorSetLayout; + } + + bool operator<(DescriptorSetLayout const &rhs) const + { + return m_descriptorSetLayout < rhs.m_descriptorSetLayout; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDescriptorSetLayout() const + { + return m_descriptorSetLayout; + } + + explicit operator bool() const + { + return m_descriptorSetLayout != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_descriptorSetLayout == VK_NULL_HANDLE; + } + + private: + VkDescriptorSetLayout m_descriptorSetLayout; + }; + static_assert( sizeof( DescriptorSetLayout ) == sizeof( VkDescriptorSetLayout ), "handle and wrapper have different size!" ); + + class DescriptorPool + { + public: + DescriptorPool() + : m_descriptorPool(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + DescriptorPool(VkDescriptorPool descriptorPool) + : m_descriptorPool(descriptorPool) + {} + + DescriptorPool& operator=(VkDescriptorPool descriptorPool) + { + m_descriptorPool = descriptorPool; + return *this; + } +#endif + + bool operator==(DescriptorPool const &rhs) const + { + return m_descriptorPool == rhs.m_descriptorPool; + } + + bool operator!=(DescriptorPool const &rhs) const + { + return m_descriptorPool != rhs.m_descriptorPool; + } + + bool operator<(DescriptorPool const &rhs) const + { + return m_descriptorPool < rhs.m_descriptorPool; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDescriptorPool() const + { + return m_descriptorPool; + } + + explicit operator bool() const + { + return m_descriptorPool != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_descriptorPool == VK_NULL_HANDLE; + } + + private: + VkDescriptorPool m_descriptorPool; + }; + static_assert( sizeof( DescriptorPool ) == sizeof( VkDescriptorPool ), "handle and wrapper have different size!" ); + + class Fence + { + public: + Fence() + : m_fence(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Fence(VkFence fence) + : m_fence(fence) + {} + + Fence& operator=(VkFence fence) + { + m_fence = fence; + return *this; + } +#endif + + bool operator==(Fence const &rhs) const + { + return m_fence == rhs.m_fence; + } + + bool operator!=(Fence const &rhs) const + { + return m_fence != rhs.m_fence; + } + + bool operator<(Fence const &rhs) const + { + return m_fence < rhs.m_fence; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkFence() const + { + return m_fence; + } + + explicit operator bool() const + { + return m_fence != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_fence == VK_NULL_HANDLE; + } + + private: + VkFence m_fence; + }; + static_assert( sizeof( Fence ) == sizeof( VkFence ), "handle and wrapper have different size!" ); + + class Semaphore + { + public: + Semaphore() + : m_semaphore(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Semaphore(VkSemaphore semaphore) + : m_semaphore(semaphore) + {} + + Semaphore& operator=(VkSemaphore semaphore) + { + m_semaphore = semaphore; + return *this; + } +#endif + + bool operator==(Semaphore const &rhs) const + { + return m_semaphore == rhs.m_semaphore; + } + + bool operator!=(Semaphore const &rhs) const + { + return m_semaphore != rhs.m_semaphore; + } + + bool operator<(Semaphore const &rhs) const + { + return m_semaphore < rhs.m_semaphore; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkSemaphore() const + { + return m_semaphore; + } + + explicit operator bool() const + { + return m_semaphore != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_semaphore == VK_NULL_HANDLE; + } + + private: + VkSemaphore m_semaphore; + }; + static_assert( sizeof( Semaphore ) == sizeof( VkSemaphore ), "handle and wrapper have different size!" ); + + class Event + { + public: + Event() + : m_event(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Event(VkEvent event) + : m_event(event) + {} + + Event& operator=(VkEvent event) + { + m_event = event; + return *this; + } +#endif + + bool operator==(Event const &rhs) const + { + return m_event == rhs.m_event; + } + + bool operator!=(Event const &rhs) const + { + return m_event != rhs.m_event; + } + + bool operator<(Event const &rhs) const + { + return m_event < rhs.m_event; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkEvent() const + { + return m_event; + } + + explicit operator bool() const + { + return m_event != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_event == VK_NULL_HANDLE; + } + + private: + VkEvent m_event; + }; + static_assert( sizeof( Event ) == sizeof( VkEvent ), "handle and wrapper have different size!" ); + + class QueryPool + { + public: + QueryPool() + : m_queryPool(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + QueryPool(VkQueryPool queryPool) + : m_queryPool(queryPool) + {} + + QueryPool& operator=(VkQueryPool queryPool) + { + m_queryPool = queryPool; + return *this; + } +#endif + + bool operator==(QueryPool const &rhs) const + { + return m_queryPool == rhs.m_queryPool; + } + + bool operator!=(QueryPool const &rhs) const + { + return m_queryPool != rhs.m_queryPool; + } + + bool operator<(QueryPool const &rhs) const + { + return m_queryPool < rhs.m_queryPool; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkQueryPool() const + { + return m_queryPool; + } + + explicit operator bool() const + { + return m_queryPool != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_queryPool == VK_NULL_HANDLE; + } + + private: + VkQueryPool m_queryPool; + }; + static_assert( sizeof( QueryPool ) == sizeof( VkQueryPool ), "handle and wrapper have different size!" ); + + class Framebuffer + { + public: + Framebuffer() + : m_framebuffer(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Framebuffer(VkFramebuffer framebuffer) + : m_framebuffer(framebuffer) + {} + + Framebuffer& operator=(VkFramebuffer framebuffer) + { + m_framebuffer = framebuffer; + return *this; + } +#endif + + bool operator==(Framebuffer const &rhs) const + { + return m_framebuffer == rhs.m_framebuffer; + } + + bool operator!=(Framebuffer const &rhs) const + { + return m_framebuffer != rhs.m_framebuffer; + } + + bool operator<(Framebuffer const &rhs) const + { + return m_framebuffer < rhs.m_framebuffer; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkFramebuffer() const + { + return m_framebuffer; + } + + explicit operator bool() const + { + return m_framebuffer != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_framebuffer == VK_NULL_HANDLE; + } + + private: + VkFramebuffer m_framebuffer; + }; + static_assert( sizeof( Framebuffer ) == sizeof( VkFramebuffer ), "handle and wrapper have different size!" ); + + class RenderPass + { + public: + RenderPass() + : m_renderPass(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + RenderPass(VkRenderPass renderPass) + : m_renderPass(renderPass) + {} + + RenderPass& operator=(VkRenderPass renderPass) + { + m_renderPass = renderPass; + return *this; + } +#endif + + bool operator==(RenderPass const &rhs) const + { + return m_renderPass == rhs.m_renderPass; + } + + bool operator!=(RenderPass const &rhs) const + { + return m_renderPass != rhs.m_renderPass; + } + + bool operator<(RenderPass const &rhs) const + { + return m_renderPass < rhs.m_renderPass; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkRenderPass() const + { + return m_renderPass; + } + + explicit operator bool() const + { + return m_renderPass != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_renderPass == VK_NULL_HANDLE; + } + + private: + VkRenderPass m_renderPass; + }; + static_assert( sizeof( RenderPass ) == sizeof( VkRenderPass ), "handle and wrapper have different size!" ); + + class PipelineCache + { + public: + PipelineCache() + : m_pipelineCache(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + PipelineCache(VkPipelineCache pipelineCache) + : m_pipelineCache(pipelineCache) + {} + + PipelineCache& operator=(VkPipelineCache pipelineCache) + { + m_pipelineCache = pipelineCache; + return *this; + } +#endif + + bool operator==(PipelineCache const &rhs) const + { + return m_pipelineCache == rhs.m_pipelineCache; + } + + bool operator!=(PipelineCache const &rhs) const + { + return m_pipelineCache != rhs.m_pipelineCache; + } + + bool operator<(PipelineCache const &rhs) const + { + return m_pipelineCache < rhs.m_pipelineCache; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkPipelineCache() const + { + return m_pipelineCache; + } + + explicit operator bool() const + { + return m_pipelineCache != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_pipelineCache == VK_NULL_HANDLE; + } + + private: + VkPipelineCache m_pipelineCache; + }; + static_assert( sizeof( PipelineCache ) == sizeof( VkPipelineCache ), "handle and wrapper have different size!" ); + + class ObjectTableNVX + { + public: + ObjectTableNVX() + : m_objectTableNVX(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + ObjectTableNVX(VkObjectTableNVX objectTableNVX) + : m_objectTableNVX(objectTableNVX) + {} + + ObjectTableNVX& operator=(VkObjectTableNVX objectTableNVX) + { + m_objectTableNVX = objectTableNVX; + return *this; + } +#endif + + bool operator==(ObjectTableNVX const &rhs) const + { + return m_objectTableNVX == rhs.m_objectTableNVX; + } + + bool operator!=(ObjectTableNVX const &rhs) const + { + return m_objectTableNVX != rhs.m_objectTableNVX; + } + + bool operator<(ObjectTableNVX const &rhs) const + { + return m_objectTableNVX < rhs.m_objectTableNVX; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkObjectTableNVX() const + { + return m_objectTableNVX; + } + + explicit operator bool() const + { + return m_objectTableNVX != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_objectTableNVX == VK_NULL_HANDLE; + } + + private: + VkObjectTableNVX m_objectTableNVX; + }; + static_assert( sizeof( ObjectTableNVX ) == sizeof( VkObjectTableNVX ), "handle and wrapper have different size!" ); + + class IndirectCommandsLayoutNVX + { + public: + IndirectCommandsLayoutNVX() + : m_indirectCommandsLayoutNVX(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + IndirectCommandsLayoutNVX(VkIndirectCommandsLayoutNVX indirectCommandsLayoutNVX) + : m_indirectCommandsLayoutNVX(indirectCommandsLayoutNVX) + {} + + IndirectCommandsLayoutNVX& operator=(VkIndirectCommandsLayoutNVX indirectCommandsLayoutNVX) + { + m_indirectCommandsLayoutNVX = indirectCommandsLayoutNVX; + return *this; + } +#endif + + bool operator==(IndirectCommandsLayoutNVX const &rhs) const + { + return m_indirectCommandsLayoutNVX == rhs.m_indirectCommandsLayoutNVX; + } + + bool operator!=(IndirectCommandsLayoutNVX const &rhs) const + { + return m_indirectCommandsLayoutNVX != rhs.m_indirectCommandsLayoutNVX; + } + + bool operator<(IndirectCommandsLayoutNVX const &rhs) const + { + return m_indirectCommandsLayoutNVX < rhs.m_indirectCommandsLayoutNVX; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkIndirectCommandsLayoutNVX() const + { + return m_indirectCommandsLayoutNVX; + } + + explicit operator bool() const + { + return m_indirectCommandsLayoutNVX != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_indirectCommandsLayoutNVX == VK_NULL_HANDLE; + } + + private: + VkIndirectCommandsLayoutNVX m_indirectCommandsLayoutNVX; + }; + static_assert( sizeof( IndirectCommandsLayoutNVX ) == sizeof( VkIndirectCommandsLayoutNVX ), "handle and wrapper have different size!" ); + + class DisplayKHR + { + public: + DisplayKHR() + : m_displayKHR(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + DisplayKHR(VkDisplayKHR displayKHR) + : m_displayKHR(displayKHR) + {} + + DisplayKHR& operator=(VkDisplayKHR displayKHR) + { + m_displayKHR = displayKHR; + return *this; + } +#endif + + bool operator==(DisplayKHR const &rhs) const + { + return m_displayKHR == rhs.m_displayKHR; + } + + bool operator!=(DisplayKHR const &rhs) const + { + return m_displayKHR != rhs.m_displayKHR; + } + + bool operator<(DisplayKHR const &rhs) const + { + return m_displayKHR < rhs.m_displayKHR; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDisplayKHR() const + { + return m_displayKHR; + } + + explicit operator bool() const + { + return m_displayKHR != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_displayKHR == VK_NULL_HANDLE; + } + + private: + VkDisplayKHR m_displayKHR; + }; + static_assert( sizeof( DisplayKHR ) == sizeof( VkDisplayKHR ), "handle and wrapper have different size!" ); + + class DisplayModeKHR + { + public: + DisplayModeKHR() + : m_displayModeKHR(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + DisplayModeKHR(VkDisplayModeKHR displayModeKHR) + : m_displayModeKHR(displayModeKHR) + {} + + DisplayModeKHR& operator=(VkDisplayModeKHR displayModeKHR) + { + m_displayModeKHR = displayModeKHR; + return *this; + } +#endif + + bool operator==(DisplayModeKHR const &rhs) const + { + return m_displayModeKHR == rhs.m_displayModeKHR; + } + + bool operator!=(DisplayModeKHR const &rhs) const + { + return m_displayModeKHR != rhs.m_displayModeKHR; + } + + bool operator<(DisplayModeKHR const &rhs) const + { + return m_displayModeKHR < rhs.m_displayModeKHR; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDisplayModeKHR() const + { + return m_displayModeKHR; + } + + explicit operator bool() const + { + return m_displayModeKHR != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_displayModeKHR == VK_NULL_HANDLE; + } + + private: + VkDisplayModeKHR m_displayModeKHR; + }; + static_assert( sizeof( DisplayModeKHR ) == sizeof( VkDisplayModeKHR ), "handle and wrapper have different size!" ); + + class SurfaceKHR + { + public: + SurfaceKHR() + : m_surfaceKHR(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + SurfaceKHR(VkSurfaceKHR surfaceKHR) + : m_surfaceKHR(surfaceKHR) + {} + + SurfaceKHR& operator=(VkSurfaceKHR surfaceKHR) + { + m_surfaceKHR = surfaceKHR; + return *this; + } +#endif + + bool operator==(SurfaceKHR const &rhs) const + { + return m_surfaceKHR == rhs.m_surfaceKHR; + } + + bool operator!=(SurfaceKHR const &rhs) const + { + return m_surfaceKHR != rhs.m_surfaceKHR; + } + + bool operator<(SurfaceKHR const &rhs) const + { + return m_surfaceKHR < rhs.m_surfaceKHR; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkSurfaceKHR() const + { + return m_surfaceKHR; + } + + explicit operator bool() const + { + return m_surfaceKHR != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_surfaceKHR == VK_NULL_HANDLE; + } + + private: + VkSurfaceKHR m_surfaceKHR; + }; + static_assert( sizeof( SurfaceKHR ) == sizeof( VkSurfaceKHR ), "handle and wrapper have different size!" ); + + class SwapchainKHR + { + public: + SwapchainKHR() + : m_swapchainKHR(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + SwapchainKHR(VkSwapchainKHR swapchainKHR) + : m_swapchainKHR(swapchainKHR) + {} + + SwapchainKHR& operator=(VkSwapchainKHR swapchainKHR) + { + m_swapchainKHR = swapchainKHR; + return *this; + } +#endif + + bool operator==(SwapchainKHR const &rhs) const + { + return m_swapchainKHR == rhs.m_swapchainKHR; + } + + bool operator!=(SwapchainKHR const &rhs) const + { + return m_swapchainKHR != rhs.m_swapchainKHR; + } + + bool operator<(SwapchainKHR const &rhs) const + { + return m_swapchainKHR < rhs.m_swapchainKHR; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkSwapchainKHR() const + { + return m_swapchainKHR; + } + + explicit operator bool() const + { + return m_swapchainKHR != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_swapchainKHR == VK_NULL_HANDLE; + } + + private: + VkSwapchainKHR m_swapchainKHR; + }; + static_assert( sizeof( SwapchainKHR ) == sizeof( VkSwapchainKHR ), "handle and wrapper have different size!" ); + + class DebugReportCallbackEXT + { + public: + DebugReportCallbackEXT() + : m_debugReportCallbackEXT(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + DebugReportCallbackEXT(VkDebugReportCallbackEXT debugReportCallbackEXT) + : m_debugReportCallbackEXT(debugReportCallbackEXT) + {} + + DebugReportCallbackEXT& operator=(VkDebugReportCallbackEXT debugReportCallbackEXT) + { + m_debugReportCallbackEXT = debugReportCallbackEXT; + return *this; + } +#endif + + bool operator==(DebugReportCallbackEXT const &rhs) const + { + return m_debugReportCallbackEXT == rhs.m_debugReportCallbackEXT; + } + + bool operator!=(DebugReportCallbackEXT const &rhs) const + { + return m_debugReportCallbackEXT != rhs.m_debugReportCallbackEXT; + } + + bool operator<(DebugReportCallbackEXT const &rhs) const + { + return m_debugReportCallbackEXT < rhs.m_debugReportCallbackEXT; + } + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDebugReportCallbackEXT() const + { + return m_debugReportCallbackEXT; + } + + explicit operator bool() const + { + return m_debugReportCallbackEXT != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_debugReportCallbackEXT == VK_NULL_HANDLE; + } + + private: + VkDebugReportCallbackEXT m_debugReportCallbackEXT; + }; + static_assert( sizeof( DebugReportCallbackEXT ) == sizeof( VkDebugReportCallbackEXT ), "handle and wrapper have different size!" ); + + struct Offset2D + { + Offset2D( int32_t x_ = 0, int32_t y_ = 0 ) + : x( x_ ) + , y( y_ ) + { + } + + Offset2D( VkOffset2D const & rhs ) + { + memcpy( this, &rhs, sizeof(Offset2D) ); + } + + Offset2D& operator=( VkOffset2D const & rhs ) + { + memcpy( this, &rhs, sizeof(Offset2D) ); + return *this; + } + + Offset2D& setX( int32_t x_ ) + { + x = x_; + return *this; + } + + Offset2D& setY( int32_t y_ ) + { + y = y_; + return *this; + } + + operator const VkOffset2D&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Offset2D const& rhs ) const + { + return ( x == rhs.x ) + && ( y == rhs.y ); + } + + bool operator!=( Offset2D const& rhs ) const + { + return !operator==( rhs ); + } + + int32_t x; + int32_t y; + }; + static_assert( sizeof( Offset2D ) == sizeof( VkOffset2D ), "struct and wrapper have different size!" ); + + struct Offset3D + { + Offset3D( int32_t x_ = 0, int32_t y_ = 0, int32_t z_ = 0 ) + : x( x_ ) + , y( y_ ) + , z( z_ ) + { + } + + Offset3D( VkOffset3D const & rhs ) + { + memcpy( this, &rhs, sizeof(Offset3D) ); + } + + Offset3D& operator=( VkOffset3D const & rhs ) + { + memcpy( this, &rhs, sizeof(Offset3D) ); + return *this; + } + + Offset3D& setX( int32_t x_ ) + { + x = x_; + return *this; + } + + Offset3D& setY( int32_t y_ ) + { + y = y_; + return *this; + } + + Offset3D& setZ( int32_t z_ ) + { + z = z_; + return *this; + } + + operator const VkOffset3D&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Offset3D const& rhs ) const + { + return ( x == rhs.x ) + && ( y == rhs.y ) + && ( z == rhs.z ); + } + + bool operator!=( Offset3D const& rhs ) const + { + return !operator==( rhs ); + } + + int32_t x; + int32_t y; + int32_t z; + }; + static_assert( sizeof( Offset3D ) == sizeof( VkOffset3D ), "struct and wrapper have different size!" ); + + struct Extent2D + { + Extent2D( uint32_t width_ = 0, uint32_t height_ = 0 ) + : width( width_ ) + , height( height_ ) + { + } + + Extent2D( VkExtent2D const & rhs ) + { + memcpy( this, &rhs, sizeof(Extent2D) ); + } + + Extent2D& operator=( VkExtent2D const & rhs ) + { + memcpy( this, &rhs, sizeof(Extent2D) ); + return *this; + } + + Extent2D& setWidth( uint32_t width_ ) + { + width = width_; + return *this; + } + + Extent2D& setHeight( uint32_t height_ ) + { + height = height_; + return *this; + } + + operator const VkExtent2D&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Extent2D const& rhs ) const + { + return ( width == rhs.width ) + && ( height == rhs.height ); + } + + bool operator!=( Extent2D const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t width; + uint32_t height; + }; + static_assert( sizeof( Extent2D ) == sizeof( VkExtent2D ), "struct and wrapper have different size!" ); + + struct Extent3D + { + Extent3D( uint32_t width_ = 0, uint32_t height_ = 0, uint32_t depth_ = 0 ) + : width( width_ ) + , height( height_ ) + , depth( depth_ ) + { + } + + Extent3D( VkExtent3D const & rhs ) + { + memcpy( this, &rhs, sizeof(Extent3D) ); + } + + Extent3D& operator=( VkExtent3D const & rhs ) + { + memcpy( this, &rhs, sizeof(Extent3D) ); + return *this; + } + + Extent3D& setWidth( uint32_t width_ ) + { + width = width_; + return *this; + } + + Extent3D& setHeight( uint32_t height_ ) + { + height = height_; + return *this; + } + + Extent3D& setDepth( uint32_t depth_ ) + { + depth = depth_; + return *this; + } + + operator const VkExtent3D&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Extent3D const& rhs ) const + { + return ( width == rhs.width ) + && ( height == rhs.height ) + && ( depth == rhs.depth ); + } + + bool operator!=( Extent3D const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t width; + uint32_t height; + uint32_t depth; + }; + static_assert( sizeof( Extent3D ) == sizeof( VkExtent3D ), "struct and wrapper have different size!" ); + + struct Viewport + { + Viewport( float x_ = 0, float y_ = 0, float width_ = 0, float height_ = 0, float minDepth_ = 0, float maxDepth_ = 0 ) + : x( x_ ) + , y( y_ ) + , width( width_ ) + , height( height_ ) + , minDepth( minDepth_ ) + , maxDepth( maxDepth_ ) + { + } + + Viewport( VkViewport const & rhs ) + { + memcpy( this, &rhs, sizeof(Viewport) ); + } + + Viewport& operator=( VkViewport const & rhs ) + { + memcpy( this, &rhs, sizeof(Viewport) ); + return *this; + } + + Viewport& setX( float x_ ) + { + x = x_; + return *this; + } + + Viewport& setY( float y_ ) + { + y = y_; + return *this; + } + + Viewport& setWidth( float width_ ) + { + width = width_; + return *this; + } + + Viewport& setHeight( float height_ ) + { + height = height_; + return *this; + } + + Viewport& setMinDepth( float minDepth_ ) + { + minDepth = minDepth_; + return *this; + } + + Viewport& setMaxDepth( float maxDepth_ ) + { + maxDepth = maxDepth_; + return *this; + } + + operator const VkViewport&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Viewport const& rhs ) const + { + return ( x == rhs.x ) + && ( y == rhs.y ) + && ( width == rhs.width ) + && ( height == rhs.height ) + && ( minDepth == rhs.minDepth ) + && ( maxDepth == rhs.maxDepth ); + } + + bool operator!=( Viewport const& rhs ) const + { + return !operator==( rhs ); + } + + float x; + float y; + float width; + float height; + float minDepth; + float maxDepth; + }; + static_assert( sizeof( Viewport ) == sizeof( VkViewport ), "struct and wrapper have different size!" ); + + struct Rect2D + { + Rect2D( Offset2D offset_ = Offset2D(), Extent2D extent_ = Extent2D() ) + : offset( offset_ ) + , extent( extent_ ) + { + } + + Rect2D( VkRect2D const & rhs ) + { + memcpy( this, &rhs, sizeof(Rect2D) ); + } + + Rect2D& operator=( VkRect2D const & rhs ) + { + memcpy( this, &rhs, sizeof(Rect2D) ); + return *this; + } + + Rect2D& setOffset( Offset2D offset_ ) + { + offset = offset_; + return *this; + } + + Rect2D& setExtent( Extent2D extent_ ) + { + extent = extent_; + return *this; + } + + operator const VkRect2D&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Rect2D const& rhs ) const + { + return ( offset == rhs.offset ) + && ( extent == rhs.extent ); + } + + bool operator!=( Rect2D const& rhs ) const + { + return !operator==( rhs ); + } + + Offset2D offset; + Extent2D extent; + }; + static_assert( sizeof( Rect2D ) == sizeof( VkRect2D ), "struct and wrapper have different size!" ); + + struct ClearRect + { + ClearRect( Rect2D rect_ = Rect2D(), uint32_t baseArrayLayer_ = 0, uint32_t layerCount_ = 0 ) + : rect( rect_ ) + , baseArrayLayer( baseArrayLayer_ ) + , layerCount( layerCount_ ) + { + } + + ClearRect( VkClearRect const & rhs ) + { + memcpy( this, &rhs, sizeof(ClearRect) ); + } + + ClearRect& operator=( VkClearRect const & rhs ) + { + memcpy( this, &rhs, sizeof(ClearRect) ); + return *this; + } + + ClearRect& setRect( Rect2D rect_ ) + { + rect = rect_; + return *this; + } + + ClearRect& setBaseArrayLayer( uint32_t baseArrayLayer_ ) + { + baseArrayLayer = baseArrayLayer_; + return *this; + } + + ClearRect& setLayerCount( uint32_t layerCount_ ) + { + layerCount = layerCount_; + return *this; + } + + operator const VkClearRect&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ClearRect const& rhs ) const + { + return ( rect == rhs.rect ) + && ( baseArrayLayer == rhs.baseArrayLayer ) + && ( layerCount == rhs.layerCount ); + } + + bool operator!=( ClearRect const& rhs ) const + { + return !operator==( rhs ); + } + + Rect2D rect; + uint32_t baseArrayLayer; + uint32_t layerCount; + }; + static_assert( sizeof( ClearRect ) == sizeof( VkClearRect ), "struct and wrapper have different size!" ); + + struct ExtensionProperties + { + operator const VkExtensionProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ExtensionProperties const& rhs ) const + { + return ( memcmp( extensionName, rhs.extensionName, VK_MAX_EXTENSION_NAME_SIZE * sizeof( char ) ) == 0 ) + && ( specVersion == rhs.specVersion ); + } + + bool operator!=( ExtensionProperties const& rhs ) const + { + return !operator==( rhs ); + } + + char extensionName[VK_MAX_EXTENSION_NAME_SIZE]; + uint32_t specVersion; + }; + static_assert( sizeof( ExtensionProperties ) == sizeof( VkExtensionProperties ), "struct and wrapper have different size!" ); + + struct LayerProperties + { + operator const VkLayerProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( LayerProperties const& rhs ) const + { + return ( memcmp( layerName, rhs.layerName, VK_MAX_EXTENSION_NAME_SIZE * sizeof( char ) ) == 0 ) + && ( specVersion == rhs.specVersion ) + && ( implementationVersion == rhs.implementationVersion ) + && ( memcmp( description, rhs.description, VK_MAX_DESCRIPTION_SIZE * sizeof( char ) ) == 0 ); + } + + bool operator!=( LayerProperties const& rhs ) const + { + return !operator==( rhs ); + } + + char layerName[VK_MAX_EXTENSION_NAME_SIZE]; + uint32_t specVersion; + uint32_t implementationVersion; + char description[VK_MAX_DESCRIPTION_SIZE]; + }; + static_assert( sizeof( LayerProperties ) == sizeof( VkLayerProperties ), "struct and wrapper have different size!" ); + + struct AllocationCallbacks + { + AllocationCallbacks( void* pUserData_ = nullptr, PFN_vkAllocationFunction pfnAllocation_ = nullptr, PFN_vkReallocationFunction pfnReallocation_ = nullptr, PFN_vkFreeFunction pfnFree_ = nullptr, PFN_vkInternalAllocationNotification pfnInternalAllocation_ = nullptr, PFN_vkInternalFreeNotification pfnInternalFree_ = nullptr ) + : pUserData( pUserData_ ) + , pfnAllocation( pfnAllocation_ ) + , pfnReallocation( pfnReallocation_ ) + , pfnFree( pfnFree_ ) + , pfnInternalAllocation( pfnInternalAllocation_ ) + , pfnInternalFree( pfnInternalFree_ ) + { + } + + AllocationCallbacks( VkAllocationCallbacks const & rhs ) + { + memcpy( this, &rhs, sizeof(AllocationCallbacks) ); + } + + AllocationCallbacks& operator=( VkAllocationCallbacks const & rhs ) + { + memcpy( this, &rhs, sizeof(AllocationCallbacks) ); + return *this; + } + + AllocationCallbacks& setPUserData( void* pUserData_ ) + { + pUserData = pUserData_; + return *this; + } + + AllocationCallbacks& setPfnAllocation( PFN_vkAllocationFunction pfnAllocation_ ) + { + pfnAllocation = pfnAllocation_; + return *this; + } + + AllocationCallbacks& setPfnReallocation( PFN_vkReallocationFunction pfnReallocation_ ) + { + pfnReallocation = pfnReallocation_; + return *this; + } + + AllocationCallbacks& setPfnFree( PFN_vkFreeFunction pfnFree_ ) + { + pfnFree = pfnFree_; + return *this; + } + + AllocationCallbacks& setPfnInternalAllocation( PFN_vkInternalAllocationNotification pfnInternalAllocation_ ) + { + pfnInternalAllocation = pfnInternalAllocation_; + return *this; + } + + AllocationCallbacks& setPfnInternalFree( PFN_vkInternalFreeNotification pfnInternalFree_ ) + { + pfnInternalFree = pfnInternalFree_; + return *this; + } + + operator const VkAllocationCallbacks&() const + { + return *reinterpret_cast(this); + } + + bool operator==( AllocationCallbacks const& rhs ) const + { + return ( pUserData == rhs.pUserData ) + && ( pfnAllocation == rhs.pfnAllocation ) + && ( pfnReallocation == rhs.pfnReallocation ) + && ( pfnFree == rhs.pfnFree ) + && ( pfnInternalAllocation == rhs.pfnInternalAllocation ) + && ( pfnInternalFree == rhs.pfnInternalFree ); + } + + bool operator!=( AllocationCallbacks const& rhs ) const + { + return !operator==( rhs ); + } + + void* pUserData; + PFN_vkAllocationFunction pfnAllocation; + PFN_vkReallocationFunction pfnReallocation; + PFN_vkFreeFunction pfnFree; + PFN_vkInternalAllocationNotification pfnInternalAllocation; + PFN_vkInternalFreeNotification pfnInternalFree; + }; + static_assert( sizeof( AllocationCallbacks ) == sizeof( VkAllocationCallbacks ), "struct and wrapper have different size!" ); + + struct MemoryRequirements + { + operator const VkMemoryRequirements&() const + { + return *reinterpret_cast(this); + } + + bool operator==( MemoryRequirements const& rhs ) const + { + return ( size == rhs.size ) + && ( alignment == rhs.alignment ) + && ( memoryTypeBits == rhs.memoryTypeBits ); + } + + bool operator!=( MemoryRequirements const& rhs ) const + { + return !operator==( rhs ); + } + + DeviceSize size; + DeviceSize alignment; + uint32_t memoryTypeBits; + }; + static_assert( sizeof( MemoryRequirements ) == sizeof( VkMemoryRequirements ), "struct and wrapper have different size!" ); + + struct DescriptorBufferInfo + { + DescriptorBufferInfo( Buffer buffer_ = Buffer(), DeviceSize offset_ = 0, DeviceSize range_ = 0 ) + : buffer( buffer_ ) + , offset( offset_ ) + , range( range_ ) + { + } + + DescriptorBufferInfo( VkDescriptorBufferInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorBufferInfo) ); + } + + DescriptorBufferInfo& operator=( VkDescriptorBufferInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorBufferInfo) ); + return *this; + } + + DescriptorBufferInfo& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + DescriptorBufferInfo& setOffset( DeviceSize offset_ ) + { + offset = offset_; + return *this; + } + + DescriptorBufferInfo& setRange( DeviceSize range_ ) + { + range = range_; + return *this; + } + + operator const VkDescriptorBufferInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DescriptorBufferInfo const& rhs ) const + { + return ( buffer == rhs.buffer ) + && ( offset == rhs.offset ) + && ( range == rhs.range ); + } + + bool operator!=( DescriptorBufferInfo const& rhs ) const + { + return !operator==( rhs ); + } + + Buffer buffer; + DeviceSize offset; + DeviceSize range; + }; + static_assert( sizeof( DescriptorBufferInfo ) == sizeof( VkDescriptorBufferInfo ), "struct and wrapper have different size!" ); + + struct SubresourceLayout + { + operator const VkSubresourceLayout&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SubresourceLayout const& rhs ) const + { + return ( offset == rhs.offset ) + && ( size == rhs.size ) + && ( rowPitch == rhs.rowPitch ) + && ( arrayPitch == rhs.arrayPitch ) + && ( depthPitch == rhs.depthPitch ); + } + + bool operator!=( SubresourceLayout const& rhs ) const + { + return !operator==( rhs ); + } + + DeviceSize offset; + DeviceSize size; + DeviceSize rowPitch; + DeviceSize arrayPitch; + DeviceSize depthPitch; + }; + static_assert( sizeof( SubresourceLayout ) == sizeof( VkSubresourceLayout ), "struct and wrapper have different size!" ); + + struct BufferCopy + { + BufferCopy( DeviceSize srcOffset_ = 0, DeviceSize dstOffset_ = 0, DeviceSize size_ = 0 ) + : srcOffset( srcOffset_ ) + , dstOffset( dstOffset_ ) + , size( size_ ) + { + } + + BufferCopy( VkBufferCopy const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferCopy) ); + } + + BufferCopy& operator=( VkBufferCopy const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferCopy) ); + return *this; + } + + BufferCopy& setSrcOffset( DeviceSize srcOffset_ ) + { + srcOffset = srcOffset_; + return *this; + } + + BufferCopy& setDstOffset( DeviceSize dstOffset_ ) + { + dstOffset = dstOffset_; + return *this; + } + + BufferCopy& setSize( DeviceSize size_ ) + { + size = size_; + return *this; + } + + operator const VkBufferCopy&() const + { + return *reinterpret_cast(this); + } + + bool operator==( BufferCopy const& rhs ) const + { + return ( srcOffset == rhs.srcOffset ) + && ( dstOffset == rhs.dstOffset ) + && ( size == rhs.size ); + } + + bool operator!=( BufferCopy const& rhs ) const + { + return !operator==( rhs ); + } + + DeviceSize srcOffset; + DeviceSize dstOffset; + DeviceSize size; + }; + static_assert( sizeof( BufferCopy ) == sizeof( VkBufferCopy ), "struct and wrapper have different size!" ); + + struct SpecializationMapEntry + { + SpecializationMapEntry( uint32_t constantID_ = 0, uint32_t offset_ = 0, size_t size_ = 0 ) + : constantID( constantID_ ) + , offset( offset_ ) + , size( size_ ) + { + } + + SpecializationMapEntry( VkSpecializationMapEntry const & rhs ) + { + memcpy( this, &rhs, sizeof(SpecializationMapEntry) ); + } + + SpecializationMapEntry& operator=( VkSpecializationMapEntry const & rhs ) + { + memcpy( this, &rhs, sizeof(SpecializationMapEntry) ); + return *this; + } + + SpecializationMapEntry& setConstantID( uint32_t constantID_ ) + { + constantID = constantID_; + return *this; + } + + SpecializationMapEntry& setOffset( uint32_t offset_ ) + { + offset = offset_; + return *this; + } + + SpecializationMapEntry& setSize( size_t size_ ) + { + size = size_; + return *this; + } + + operator const VkSpecializationMapEntry&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SpecializationMapEntry const& rhs ) const + { + return ( constantID == rhs.constantID ) + && ( offset == rhs.offset ) + && ( size == rhs.size ); + } + + bool operator!=( SpecializationMapEntry const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t constantID; + uint32_t offset; + size_t size; + }; + static_assert( sizeof( SpecializationMapEntry ) == sizeof( VkSpecializationMapEntry ), "struct and wrapper have different size!" ); + + struct SpecializationInfo + { + SpecializationInfo( uint32_t mapEntryCount_ = 0, const SpecializationMapEntry* pMapEntries_ = nullptr, size_t dataSize_ = 0, const void* pData_ = nullptr ) + : mapEntryCount( mapEntryCount_ ) + , pMapEntries( pMapEntries_ ) + , dataSize( dataSize_ ) + , pData( pData_ ) + { + } + + SpecializationInfo( VkSpecializationInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SpecializationInfo) ); + } + + SpecializationInfo& operator=( VkSpecializationInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SpecializationInfo) ); + return *this; + } + + SpecializationInfo& setMapEntryCount( uint32_t mapEntryCount_ ) + { + mapEntryCount = mapEntryCount_; + return *this; + } + + SpecializationInfo& setPMapEntries( const SpecializationMapEntry* pMapEntries_ ) + { + pMapEntries = pMapEntries_; + return *this; + } + + SpecializationInfo& setDataSize( size_t dataSize_ ) + { + dataSize = dataSize_; + return *this; + } + + SpecializationInfo& setPData( const void* pData_ ) + { + pData = pData_; + return *this; + } + + operator const VkSpecializationInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SpecializationInfo const& rhs ) const + { + return ( mapEntryCount == rhs.mapEntryCount ) + && ( pMapEntries == rhs.pMapEntries ) + && ( dataSize == rhs.dataSize ) + && ( pData == rhs.pData ); + } + + bool operator!=( SpecializationInfo const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t mapEntryCount; + const SpecializationMapEntry* pMapEntries; + size_t dataSize; + const void* pData; + }; + static_assert( sizeof( SpecializationInfo ) == sizeof( VkSpecializationInfo ), "struct and wrapper have different size!" ); + + union ClearColorValue + { + ClearColorValue( const std::array& float32_ = { {0} } ) + { + memcpy( &float32, float32_.data(), 4 * sizeof( float ) ); + } + + ClearColorValue( const std::array& int32_ ) + { + memcpy( &int32, int32_.data(), 4 * sizeof( int32_t ) ); + } + + ClearColorValue( const std::array& uint32_ ) + { + memcpy( &uint32, uint32_.data(), 4 * sizeof( uint32_t ) ); + } + + ClearColorValue& setFloat32( std::array float32_ ) + { + memcpy( &float32, float32_.data(), 4 * sizeof( float ) ); + return *this; + } + + ClearColorValue& setInt32( std::array int32_ ) + { + memcpy( &int32, int32_.data(), 4 * sizeof( int32_t ) ); + return *this; + } + + ClearColorValue& setUint32( std::array uint32_ ) + { + memcpy( &uint32, uint32_.data(), 4 * sizeof( uint32_t ) ); + return *this; + } + + operator VkClearColorValue const& () const + { + return *reinterpret_cast(this); + } + + float float32[4]; + int32_t int32[4]; + uint32_t uint32[4]; + }; + + struct ClearDepthStencilValue + { + ClearDepthStencilValue( float depth_ = 0, uint32_t stencil_ = 0 ) + : depth( depth_ ) + , stencil( stencil_ ) + { + } + + ClearDepthStencilValue( VkClearDepthStencilValue const & rhs ) + { + memcpy( this, &rhs, sizeof(ClearDepthStencilValue) ); + } + + ClearDepthStencilValue& operator=( VkClearDepthStencilValue const & rhs ) + { + memcpy( this, &rhs, sizeof(ClearDepthStencilValue) ); + return *this; + } + + ClearDepthStencilValue& setDepth( float depth_ ) + { + depth = depth_; + return *this; + } + + ClearDepthStencilValue& setStencil( uint32_t stencil_ ) + { + stencil = stencil_; + return *this; + } + + operator const VkClearDepthStencilValue&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ClearDepthStencilValue const& rhs ) const + { + return ( depth == rhs.depth ) + && ( stencil == rhs.stencil ); + } + + bool operator!=( ClearDepthStencilValue const& rhs ) const + { + return !operator==( rhs ); + } + + float depth; + uint32_t stencil; + }; + static_assert( sizeof( ClearDepthStencilValue ) == sizeof( VkClearDepthStencilValue ), "struct and wrapper have different size!" ); + + union ClearValue + { + ClearValue( ClearColorValue color_ = ClearColorValue() ) + { + color = color_; + } + + ClearValue( ClearDepthStencilValue depthStencil_ ) + { + depthStencil = depthStencil_; + } + + ClearValue& setColor( ClearColorValue color_ ) + { + color = color_; + return *this; + } + + ClearValue& setDepthStencil( ClearDepthStencilValue depthStencil_ ) + { + depthStencil = depthStencil_; + return *this; + } + + operator VkClearValue const& () const + { + return *reinterpret_cast(this); + } + +#ifdef VULKAN_HPP_HAS_UNRESTRICTED_UNIONS + ClearColorValue color; + ClearDepthStencilValue depthStencil; +#else + VkClearColorValue color; + VkClearDepthStencilValue depthStencil; +#endif // VULKAN_HPP_HAS_UNRESTRICTED_UNIONS + }; + + struct PhysicalDeviceFeatures + { + PhysicalDeviceFeatures( Bool32 robustBufferAccess_ = 0, Bool32 fullDrawIndexUint32_ = 0, Bool32 imageCubeArray_ = 0, Bool32 independentBlend_ = 0, Bool32 geometryShader_ = 0, Bool32 tessellationShader_ = 0, Bool32 sampleRateShading_ = 0, Bool32 dualSrcBlend_ = 0, Bool32 logicOp_ = 0, Bool32 multiDrawIndirect_ = 0, Bool32 drawIndirectFirstInstance_ = 0, Bool32 depthClamp_ = 0, Bool32 depthBiasClamp_ = 0, Bool32 fillModeNonSolid_ = 0, Bool32 depthBounds_ = 0, Bool32 wideLines_ = 0, Bool32 largePoints_ = 0, Bool32 alphaToOne_ = 0, Bool32 multiViewport_ = 0, Bool32 samplerAnisotropy_ = 0, Bool32 textureCompressionETC2_ = 0, Bool32 textureCompressionASTC_LDR_ = 0, Bool32 textureCompressionBC_ = 0, Bool32 occlusionQueryPrecise_ = 0, Bool32 pipelineStatisticsQuery_ = 0, Bool32 vertexPipelineStoresAndAtomics_ = 0, Bool32 fragmentStoresAndAtomics_ = 0, Bool32 shaderTessellationAndGeometryPointSize_ = 0, Bool32 shaderImageGatherExtended_ = 0, Bool32 shaderStorageImageExtendedFormats_ = 0, Bool32 shaderStorageImageMultisample_ = 0, Bool32 shaderStorageImageReadWithoutFormat_ = 0, Bool32 shaderStorageImageWriteWithoutFormat_ = 0, Bool32 shaderUniformBufferArrayDynamicIndexing_ = 0, Bool32 shaderSampledImageArrayDynamicIndexing_ = 0, Bool32 shaderStorageBufferArrayDynamicIndexing_ = 0, Bool32 shaderStorageImageArrayDynamicIndexing_ = 0, Bool32 shaderClipDistance_ = 0, Bool32 shaderCullDistance_ = 0, Bool32 shaderFloat64_ = 0, Bool32 shaderInt64_ = 0, Bool32 shaderInt16_ = 0, Bool32 shaderResourceResidency_ = 0, Bool32 shaderResourceMinLod_ = 0, Bool32 sparseBinding_ = 0, Bool32 sparseResidencyBuffer_ = 0, Bool32 sparseResidencyImage2D_ = 0, Bool32 sparseResidencyImage3D_ = 0, Bool32 sparseResidency2Samples_ = 0, Bool32 sparseResidency4Samples_ = 0, Bool32 sparseResidency8Samples_ = 0, Bool32 sparseResidency16Samples_ = 0, Bool32 sparseResidencyAliased_ = 0, Bool32 variableMultisampleRate_ = 0, Bool32 inheritedQueries_ = 0 ) + : robustBufferAccess( robustBufferAccess_ ) + , fullDrawIndexUint32( fullDrawIndexUint32_ ) + , imageCubeArray( imageCubeArray_ ) + , independentBlend( independentBlend_ ) + , geometryShader( geometryShader_ ) + , tessellationShader( tessellationShader_ ) + , sampleRateShading( sampleRateShading_ ) + , dualSrcBlend( dualSrcBlend_ ) + , logicOp( logicOp_ ) + , multiDrawIndirect( multiDrawIndirect_ ) + , drawIndirectFirstInstance( drawIndirectFirstInstance_ ) + , depthClamp( depthClamp_ ) + , depthBiasClamp( depthBiasClamp_ ) + , fillModeNonSolid( fillModeNonSolid_ ) + , depthBounds( depthBounds_ ) + , wideLines( wideLines_ ) + , largePoints( largePoints_ ) + , alphaToOne( alphaToOne_ ) + , multiViewport( multiViewport_ ) + , samplerAnisotropy( samplerAnisotropy_ ) + , textureCompressionETC2( textureCompressionETC2_ ) + , textureCompressionASTC_LDR( textureCompressionASTC_LDR_ ) + , textureCompressionBC( textureCompressionBC_ ) + , occlusionQueryPrecise( occlusionQueryPrecise_ ) + , pipelineStatisticsQuery( pipelineStatisticsQuery_ ) + , vertexPipelineStoresAndAtomics( vertexPipelineStoresAndAtomics_ ) + , fragmentStoresAndAtomics( fragmentStoresAndAtomics_ ) + , shaderTessellationAndGeometryPointSize( shaderTessellationAndGeometryPointSize_ ) + , shaderImageGatherExtended( shaderImageGatherExtended_ ) + , shaderStorageImageExtendedFormats( shaderStorageImageExtendedFormats_ ) + , shaderStorageImageMultisample( shaderStorageImageMultisample_ ) + , shaderStorageImageReadWithoutFormat( shaderStorageImageReadWithoutFormat_ ) + , shaderStorageImageWriteWithoutFormat( shaderStorageImageWriteWithoutFormat_ ) + , shaderUniformBufferArrayDynamicIndexing( shaderUniformBufferArrayDynamicIndexing_ ) + , shaderSampledImageArrayDynamicIndexing( shaderSampledImageArrayDynamicIndexing_ ) + , shaderStorageBufferArrayDynamicIndexing( shaderStorageBufferArrayDynamicIndexing_ ) + , shaderStorageImageArrayDynamicIndexing( shaderStorageImageArrayDynamicIndexing_ ) + , shaderClipDistance( shaderClipDistance_ ) + , shaderCullDistance( shaderCullDistance_ ) + , shaderFloat64( shaderFloat64_ ) + , shaderInt64( shaderInt64_ ) + , shaderInt16( shaderInt16_ ) + , shaderResourceResidency( shaderResourceResidency_ ) + , shaderResourceMinLod( shaderResourceMinLod_ ) + , sparseBinding( sparseBinding_ ) + , sparseResidencyBuffer( sparseResidencyBuffer_ ) + , sparseResidencyImage2D( sparseResidencyImage2D_ ) + , sparseResidencyImage3D( sparseResidencyImage3D_ ) + , sparseResidency2Samples( sparseResidency2Samples_ ) + , sparseResidency4Samples( sparseResidency4Samples_ ) + , sparseResidency8Samples( sparseResidency8Samples_ ) + , sparseResidency16Samples( sparseResidency16Samples_ ) + , sparseResidencyAliased( sparseResidencyAliased_ ) + , variableMultisampleRate( variableMultisampleRate_ ) + , inheritedQueries( inheritedQueries_ ) + { + } + + PhysicalDeviceFeatures( VkPhysicalDeviceFeatures const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures) ); + } + + PhysicalDeviceFeatures& operator=( VkPhysicalDeviceFeatures const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures) ); + return *this; + } + + PhysicalDeviceFeatures& setRobustBufferAccess( Bool32 robustBufferAccess_ ) + { + robustBufferAccess = robustBufferAccess_; + return *this; + } + + PhysicalDeviceFeatures& setFullDrawIndexUint32( Bool32 fullDrawIndexUint32_ ) + { + fullDrawIndexUint32 = fullDrawIndexUint32_; + return *this; + } + + PhysicalDeviceFeatures& setImageCubeArray( Bool32 imageCubeArray_ ) + { + imageCubeArray = imageCubeArray_; + return *this; + } + + PhysicalDeviceFeatures& setIndependentBlend( Bool32 independentBlend_ ) + { + independentBlend = independentBlend_; + return *this; + } + + PhysicalDeviceFeatures& setGeometryShader( Bool32 geometryShader_ ) + { + geometryShader = geometryShader_; + return *this; + } + + PhysicalDeviceFeatures& setTessellationShader( Bool32 tessellationShader_ ) + { + tessellationShader = tessellationShader_; + return *this; + } + + PhysicalDeviceFeatures& setSampleRateShading( Bool32 sampleRateShading_ ) + { + sampleRateShading = sampleRateShading_; + return *this; + } + + PhysicalDeviceFeatures& setDualSrcBlend( Bool32 dualSrcBlend_ ) + { + dualSrcBlend = dualSrcBlend_; + return *this; + } + + PhysicalDeviceFeatures& setLogicOp( Bool32 logicOp_ ) + { + logicOp = logicOp_; + return *this; + } + + PhysicalDeviceFeatures& setMultiDrawIndirect( Bool32 multiDrawIndirect_ ) + { + multiDrawIndirect = multiDrawIndirect_; + return *this; + } + + PhysicalDeviceFeatures& setDrawIndirectFirstInstance( Bool32 drawIndirectFirstInstance_ ) + { + drawIndirectFirstInstance = drawIndirectFirstInstance_; + return *this; + } + + PhysicalDeviceFeatures& setDepthClamp( Bool32 depthClamp_ ) + { + depthClamp = depthClamp_; + return *this; + } + + PhysicalDeviceFeatures& setDepthBiasClamp( Bool32 depthBiasClamp_ ) + { + depthBiasClamp = depthBiasClamp_; + return *this; + } + + PhysicalDeviceFeatures& setFillModeNonSolid( Bool32 fillModeNonSolid_ ) + { + fillModeNonSolid = fillModeNonSolid_; + return *this; + } + + PhysicalDeviceFeatures& setDepthBounds( Bool32 depthBounds_ ) + { + depthBounds = depthBounds_; + return *this; + } + + PhysicalDeviceFeatures& setWideLines( Bool32 wideLines_ ) + { + wideLines = wideLines_; + return *this; + } + + PhysicalDeviceFeatures& setLargePoints( Bool32 largePoints_ ) + { + largePoints = largePoints_; + return *this; + } + + PhysicalDeviceFeatures& setAlphaToOne( Bool32 alphaToOne_ ) + { + alphaToOne = alphaToOne_; + return *this; + } + + PhysicalDeviceFeatures& setMultiViewport( Bool32 multiViewport_ ) + { + multiViewport = multiViewport_; + return *this; + } + + PhysicalDeviceFeatures& setSamplerAnisotropy( Bool32 samplerAnisotropy_ ) + { + samplerAnisotropy = samplerAnisotropy_; + return *this; + } + + PhysicalDeviceFeatures& setTextureCompressionETC2( Bool32 textureCompressionETC2_ ) + { + textureCompressionETC2 = textureCompressionETC2_; + return *this; + } + + PhysicalDeviceFeatures& setTextureCompressionASTC_LDR( Bool32 textureCompressionASTC_LDR_ ) + { + textureCompressionASTC_LDR = textureCompressionASTC_LDR_; + return *this; + } + + PhysicalDeviceFeatures& setTextureCompressionBC( Bool32 textureCompressionBC_ ) + { + textureCompressionBC = textureCompressionBC_; + return *this; + } + + PhysicalDeviceFeatures& setOcclusionQueryPrecise( Bool32 occlusionQueryPrecise_ ) + { + occlusionQueryPrecise = occlusionQueryPrecise_; + return *this; + } + + PhysicalDeviceFeatures& setPipelineStatisticsQuery( Bool32 pipelineStatisticsQuery_ ) + { + pipelineStatisticsQuery = pipelineStatisticsQuery_; + return *this; + } + + PhysicalDeviceFeatures& setVertexPipelineStoresAndAtomics( Bool32 vertexPipelineStoresAndAtomics_ ) + { + vertexPipelineStoresAndAtomics = vertexPipelineStoresAndAtomics_; + return *this; + } + + PhysicalDeviceFeatures& setFragmentStoresAndAtomics( Bool32 fragmentStoresAndAtomics_ ) + { + fragmentStoresAndAtomics = fragmentStoresAndAtomics_; + return *this; + } + + PhysicalDeviceFeatures& setShaderTessellationAndGeometryPointSize( Bool32 shaderTessellationAndGeometryPointSize_ ) + { + shaderTessellationAndGeometryPointSize = shaderTessellationAndGeometryPointSize_; + return *this; + } + + PhysicalDeviceFeatures& setShaderImageGatherExtended( Bool32 shaderImageGatherExtended_ ) + { + shaderImageGatherExtended = shaderImageGatherExtended_; + return *this; + } + + PhysicalDeviceFeatures& setShaderStorageImageExtendedFormats( Bool32 shaderStorageImageExtendedFormats_ ) + { + shaderStorageImageExtendedFormats = shaderStorageImageExtendedFormats_; + return *this; + } + + PhysicalDeviceFeatures& setShaderStorageImageMultisample( Bool32 shaderStorageImageMultisample_ ) + { + shaderStorageImageMultisample = shaderStorageImageMultisample_; + return *this; + } + + PhysicalDeviceFeatures& setShaderStorageImageReadWithoutFormat( Bool32 shaderStorageImageReadWithoutFormat_ ) + { + shaderStorageImageReadWithoutFormat = shaderStorageImageReadWithoutFormat_; + return *this; + } + + PhysicalDeviceFeatures& setShaderStorageImageWriteWithoutFormat( Bool32 shaderStorageImageWriteWithoutFormat_ ) + { + shaderStorageImageWriteWithoutFormat = shaderStorageImageWriteWithoutFormat_; + return *this; + } + + PhysicalDeviceFeatures& setShaderUniformBufferArrayDynamicIndexing( Bool32 shaderUniformBufferArrayDynamicIndexing_ ) + { + shaderUniformBufferArrayDynamicIndexing = shaderUniformBufferArrayDynamicIndexing_; + return *this; + } + + PhysicalDeviceFeatures& setShaderSampledImageArrayDynamicIndexing( Bool32 shaderSampledImageArrayDynamicIndexing_ ) + { + shaderSampledImageArrayDynamicIndexing = shaderSampledImageArrayDynamicIndexing_; + return *this; + } + + PhysicalDeviceFeatures& setShaderStorageBufferArrayDynamicIndexing( Bool32 shaderStorageBufferArrayDynamicIndexing_ ) + { + shaderStorageBufferArrayDynamicIndexing = shaderStorageBufferArrayDynamicIndexing_; + return *this; + } + + PhysicalDeviceFeatures& setShaderStorageImageArrayDynamicIndexing( Bool32 shaderStorageImageArrayDynamicIndexing_ ) + { + shaderStorageImageArrayDynamicIndexing = shaderStorageImageArrayDynamicIndexing_; + return *this; + } + + PhysicalDeviceFeatures& setShaderClipDistance( Bool32 shaderClipDistance_ ) + { + shaderClipDistance = shaderClipDistance_; + return *this; + } + + PhysicalDeviceFeatures& setShaderCullDistance( Bool32 shaderCullDistance_ ) + { + shaderCullDistance = shaderCullDistance_; + return *this; + } + + PhysicalDeviceFeatures& setShaderFloat64( Bool32 shaderFloat64_ ) + { + shaderFloat64 = shaderFloat64_; + return *this; + } + + PhysicalDeviceFeatures& setShaderInt64( Bool32 shaderInt64_ ) + { + shaderInt64 = shaderInt64_; + return *this; + } + + PhysicalDeviceFeatures& setShaderInt16( Bool32 shaderInt16_ ) + { + shaderInt16 = shaderInt16_; + return *this; + } + + PhysicalDeviceFeatures& setShaderResourceResidency( Bool32 shaderResourceResidency_ ) + { + shaderResourceResidency = shaderResourceResidency_; + return *this; + } + + PhysicalDeviceFeatures& setShaderResourceMinLod( Bool32 shaderResourceMinLod_ ) + { + shaderResourceMinLod = shaderResourceMinLod_; + return *this; + } + + PhysicalDeviceFeatures& setSparseBinding( Bool32 sparseBinding_ ) + { + sparseBinding = sparseBinding_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidencyBuffer( Bool32 sparseResidencyBuffer_ ) + { + sparseResidencyBuffer = sparseResidencyBuffer_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidencyImage2D( Bool32 sparseResidencyImage2D_ ) + { + sparseResidencyImage2D = sparseResidencyImage2D_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidencyImage3D( Bool32 sparseResidencyImage3D_ ) + { + sparseResidencyImage3D = sparseResidencyImage3D_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidency2Samples( Bool32 sparseResidency2Samples_ ) + { + sparseResidency2Samples = sparseResidency2Samples_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidency4Samples( Bool32 sparseResidency4Samples_ ) + { + sparseResidency4Samples = sparseResidency4Samples_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidency8Samples( Bool32 sparseResidency8Samples_ ) + { + sparseResidency8Samples = sparseResidency8Samples_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidency16Samples( Bool32 sparseResidency16Samples_ ) + { + sparseResidency16Samples = sparseResidency16Samples_; + return *this; + } + + PhysicalDeviceFeatures& setSparseResidencyAliased( Bool32 sparseResidencyAliased_ ) + { + sparseResidencyAliased = sparseResidencyAliased_; + return *this; + } + + PhysicalDeviceFeatures& setVariableMultisampleRate( Bool32 variableMultisampleRate_ ) + { + variableMultisampleRate = variableMultisampleRate_; + return *this; + } + + PhysicalDeviceFeatures& setInheritedQueries( Bool32 inheritedQueries_ ) + { + inheritedQueries = inheritedQueries_; + return *this; + } + + operator const VkPhysicalDeviceFeatures&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceFeatures const& rhs ) const + { + return ( robustBufferAccess == rhs.robustBufferAccess ) + && ( fullDrawIndexUint32 == rhs.fullDrawIndexUint32 ) + && ( imageCubeArray == rhs.imageCubeArray ) + && ( independentBlend == rhs.independentBlend ) + && ( geometryShader == rhs.geometryShader ) + && ( tessellationShader == rhs.tessellationShader ) + && ( sampleRateShading == rhs.sampleRateShading ) + && ( dualSrcBlend == rhs.dualSrcBlend ) + && ( logicOp == rhs.logicOp ) + && ( multiDrawIndirect == rhs.multiDrawIndirect ) + && ( drawIndirectFirstInstance == rhs.drawIndirectFirstInstance ) + && ( depthClamp == rhs.depthClamp ) + && ( depthBiasClamp == rhs.depthBiasClamp ) + && ( fillModeNonSolid == rhs.fillModeNonSolid ) + && ( depthBounds == rhs.depthBounds ) + && ( wideLines == rhs.wideLines ) + && ( largePoints == rhs.largePoints ) + && ( alphaToOne == rhs.alphaToOne ) + && ( multiViewport == rhs.multiViewport ) + && ( samplerAnisotropy == rhs.samplerAnisotropy ) + && ( textureCompressionETC2 == rhs.textureCompressionETC2 ) + && ( textureCompressionASTC_LDR == rhs.textureCompressionASTC_LDR ) + && ( textureCompressionBC == rhs.textureCompressionBC ) + && ( occlusionQueryPrecise == rhs.occlusionQueryPrecise ) + && ( pipelineStatisticsQuery == rhs.pipelineStatisticsQuery ) + && ( vertexPipelineStoresAndAtomics == rhs.vertexPipelineStoresAndAtomics ) + && ( fragmentStoresAndAtomics == rhs.fragmentStoresAndAtomics ) + && ( shaderTessellationAndGeometryPointSize == rhs.shaderTessellationAndGeometryPointSize ) + && ( shaderImageGatherExtended == rhs.shaderImageGatherExtended ) + && ( shaderStorageImageExtendedFormats == rhs.shaderStorageImageExtendedFormats ) + && ( shaderStorageImageMultisample == rhs.shaderStorageImageMultisample ) + && ( shaderStorageImageReadWithoutFormat == rhs.shaderStorageImageReadWithoutFormat ) + && ( shaderStorageImageWriteWithoutFormat == rhs.shaderStorageImageWriteWithoutFormat ) + && ( shaderUniformBufferArrayDynamicIndexing == rhs.shaderUniformBufferArrayDynamicIndexing ) + && ( shaderSampledImageArrayDynamicIndexing == rhs.shaderSampledImageArrayDynamicIndexing ) + && ( shaderStorageBufferArrayDynamicIndexing == rhs.shaderStorageBufferArrayDynamicIndexing ) + && ( shaderStorageImageArrayDynamicIndexing == rhs.shaderStorageImageArrayDynamicIndexing ) + && ( shaderClipDistance == rhs.shaderClipDistance ) + && ( shaderCullDistance == rhs.shaderCullDistance ) + && ( shaderFloat64 == rhs.shaderFloat64 ) + && ( shaderInt64 == rhs.shaderInt64 ) + && ( shaderInt16 == rhs.shaderInt16 ) + && ( shaderResourceResidency == rhs.shaderResourceResidency ) + && ( shaderResourceMinLod == rhs.shaderResourceMinLod ) + && ( sparseBinding == rhs.sparseBinding ) + && ( sparseResidencyBuffer == rhs.sparseResidencyBuffer ) + && ( sparseResidencyImage2D == rhs.sparseResidencyImage2D ) + && ( sparseResidencyImage3D == rhs.sparseResidencyImage3D ) + && ( sparseResidency2Samples == rhs.sparseResidency2Samples ) + && ( sparseResidency4Samples == rhs.sparseResidency4Samples ) + && ( sparseResidency8Samples == rhs.sparseResidency8Samples ) + && ( sparseResidency16Samples == rhs.sparseResidency16Samples ) + && ( sparseResidencyAliased == rhs.sparseResidencyAliased ) + && ( variableMultisampleRate == rhs.variableMultisampleRate ) + && ( inheritedQueries == rhs.inheritedQueries ); + } + + bool operator!=( PhysicalDeviceFeatures const& rhs ) const + { + return !operator==( rhs ); + } + + Bool32 robustBufferAccess; + Bool32 fullDrawIndexUint32; + Bool32 imageCubeArray; + Bool32 independentBlend; + Bool32 geometryShader; + Bool32 tessellationShader; + Bool32 sampleRateShading; + Bool32 dualSrcBlend; + Bool32 logicOp; + Bool32 multiDrawIndirect; + Bool32 drawIndirectFirstInstance; + Bool32 depthClamp; + Bool32 depthBiasClamp; + Bool32 fillModeNonSolid; + Bool32 depthBounds; + Bool32 wideLines; + Bool32 largePoints; + Bool32 alphaToOne; + Bool32 multiViewport; + Bool32 samplerAnisotropy; + Bool32 textureCompressionETC2; + Bool32 textureCompressionASTC_LDR; + Bool32 textureCompressionBC; + Bool32 occlusionQueryPrecise; + Bool32 pipelineStatisticsQuery; + Bool32 vertexPipelineStoresAndAtomics; + Bool32 fragmentStoresAndAtomics; + Bool32 shaderTessellationAndGeometryPointSize; + Bool32 shaderImageGatherExtended; + Bool32 shaderStorageImageExtendedFormats; + Bool32 shaderStorageImageMultisample; + Bool32 shaderStorageImageReadWithoutFormat; + Bool32 shaderStorageImageWriteWithoutFormat; + Bool32 shaderUniformBufferArrayDynamicIndexing; + Bool32 shaderSampledImageArrayDynamicIndexing; + Bool32 shaderStorageBufferArrayDynamicIndexing; + Bool32 shaderStorageImageArrayDynamicIndexing; + Bool32 shaderClipDistance; + Bool32 shaderCullDistance; + Bool32 shaderFloat64; + Bool32 shaderInt64; + Bool32 shaderInt16; + Bool32 shaderResourceResidency; + Bool32 shaderResourceMinLod; + Bool32 sparseBinding; + Bool32 sparseResidencyBuffer; + Bool32 sparseResidencyImage2D; + Bool32 sparseResidencyImage3D; + Bool32 sparseResidency2Samples; + Bool32 sparseResidency4Samples; + Bool32 sparseResidency8Samples; + Bool32 sparseResidency16Samples; + Bool32 sparseResidencyAliased; + Bool32 variableMultisampleRate; + Bool32 inheritedQueries; + }; + static_assert( sizeof( PhysicalDeviceFeatures ) == sizeof( VkPhysicalDeviceFeatures ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceSparseProperties + { + operator const VkPhysicalDeviceSparseProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceSparseProperties const& rhs ) const + { + return ( residencyStandard2DBlockShape == rhs.residencyStandard2DBlockShape ) + && ( residencyStandard2DMultisampleBlockShape == rhs.residencyStandard2DMultisampleBlockShape ) + && ( residencyStandard3DBlockShape == rhs.residencyStandard3DBlockShape ) + && ( residencyAlignedMipSize == rhs.residencyAlignedMipSize ) + && ( residencyNonResidentStrict == rhs.residencyNonResidentStrict ); + } + + bool operator!=( PhysicalDeviceSparseProperties const& rhs ) const + { + return !operator==( rhs ); + } + + Bool32 residencyStandard2DBlockShape; + Bool32 residencyStandard2DMultisampleBlockShape; + Bool32 residencyStandard3DBlockShape; + Bool32 residencyAlignedMipSize; + Bool32 residencyNonResidentStrict; + }; + static_assert( sizeof( PhysicalDeviceSparseProperties ) == sizeof( VkPhysicalDeviceSparseProperties ), "struct and wrapper have different size!" ); + + struct DrawIndirectCommand + { + DrawIndirectCommand( uint32_t vertexCount_ = 0, uint32_t instanceCount_ = 0, uint32_t firstVertex_ = 0, uint32_t firstInstance_ = 0 ) + : vertexCount( vertexCount_ ) + , instanceCount( instanceCount_ ) + , firstVertex( firstVertex_ ) + , firstInstance( firstInstance_ ) + { + } + + DrawIndirectCommand( VkDrawIndirectCommand const & rhs ) + { + memcpy( this, &rhs, sizeof(DrawIndirectCommand) ); + } + + DrawIndirectCommand& operator=( VkDrawIndirectCommand const & rhs ) + { + memcpy( this, &rhs, sizeof(DrawIndirectCommand) ); + return *this; + } + + DrawIndirectCommand& setVertexCount( uint32_t vertexCount_ ) + { + vertexCount = vertexCount_; + return *this; + } + + DrawIndirectCommand& setInstanceCount( uint32_t instanceCount_ ) + { + instanceCount = instanceCount_; + return *this; + } + + DrawIndirectCommand& setFirstVertex( uint32_t firstVertex_ ) + { + firstVertex = firstVertex_; + return *this; + } + + DrawIndirectCommand& setFirstInstance( uint32_t firstInstance_ ) + { + firstInstance = firstInstance_; + return *this; + } + + operator const VkDrawIndirectCommand&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DrawIndirectCommand const& rhs ) const + { + return ( vertexCount == rhs.vertexCount ) + && ( instanceCount == rhs.instanceCount ) + && ( firstVertex == rhs.firstVertex ) + && ( firstInstance == rhs.firstInstance ); + } + + bool operator!=( DrawIndirectCommand const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t vertexCount; + uint32_t instanceCount; + uint32_t firstVertex; + uint32_t firstInstance; + }; + static_assert( sizeof( DrawIndirectCommand ) == sizeof( VkDrawIndirectCommand ), "struct and wrapper have different size!" ); + + struct DrawIndexedIndirectCommand + { + DrawIndexedIndirectCommand( uint32_t indexCount_ = 0, uint32_t instanceCount_ = 0, uint32_t firstIndex_ = 0, int32_t vertexOffset_ = 0, uint32_t firstInstance_ = 0 ) + : indexCount( indexCount_ ) + , instanceCount( instanceCount_ ) + , firstIndex( firstIndex_ ) + , vertexOffset( vertexOffset_ ) + , firstInstance( firstInstance_ ) + { + } + + DrawIndexedIndirectCommand( VkDrawIndexedIndirectCommand const & rhs ) + { + memcpy( this, &rhs, sizeof(DrawIndexedIndirectCommand) ); + } + + DrawIndexedIndirectCommand& operator=( VkDrawIndexedIndirectCommand const & rhs ) + { + memcpy( this, &rhs, sizeof(DrawIndexedIndirectCommand) ); + return *this; + } + + DrawIndexedIndirectCommand& setIndexCount( uint32_t indexCount_ ) + { + indexCount = indexCount_; + return *this; + } + + DrawIndexedIndirectCommand& setInstanceCount( uint32_t instanceCount_ ) + { + instanceCount = instanceCount_; + return *this; + } + + DrawIndexedIndirectCommand& setFirstIndex( uint32_t firstIndex_ ) + { + firstIndex = firstIndex_; + return *this; + } + + DrawIndexedIndirectCommand& setVertexOffset( int32_t vertexOffset_ ) + { + vertexOffset = vertexOffset_; + return *this; + } + + DrawIndexedIndirectCommand& setFirstInstance( uint32_t firstInstance_ ) + { + firstInstance = firstInstance_; + return *this; + } + + operator const VkDrawIndexedIndirectCommand&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DrawIndexedIndirectCommand const& rhs ) const + { + return ( indexCount == rhs.indexCount ) + && ( instanceCount == rhs.instanceCount ) + && ( firstIndex == rhs.firstIndex ) + && ( vertexOffset == rhs.vertexOffset ) + && ( firstInstance == rhs.firstInstance ); + } + + bool operator!=( DrawIndexedIndirectCommand const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t indexCount; + uint32_t instanceCount; + uint32_t firstIndex; + int32_t vertexOffset; + uint32_t firstInstance; + }; + static_assert( sizeof( DrawIndexedIndirectCommand ) == sizeof( VkDrawIndexedIndirectCommand ), "struct and wrapper have different size!" ); + + struct DispatchIndirectCommand + { + DispatchIndirectCommand( uint32_t x_ = 0, uint32_t y_ = 0, uint32_t z_ = 0 ) + : x( x_ ) + , y( y_ ) + , z( z_ ) + { + } + + DispatchIndirectCommand( VkDispatchIndirectCommand const & rhs ) + { + memcpy( this, &rhs, sizeof(DispatchIndirectCommand) ); + } + + DispatchIndirectCommand& operator=( VkDispatchIndirectCommand const & rhs ) + { + memcpy( this, &rhs, sizeof(DispatchIndirectCommand) ); + return *this; + } + + DispatchIndirectCommand& setX( uint32_t x_ ) + { + x = x_; + return *this; + } + + DispatchIndirectCommand& setY( uint32_t y_ ) + { + y = y_; + return *this; + } + + DispatchIndirectCommand& setZ( uint32_t z_ ) + { + z = z_; + return *this; + } + + operator const VkDispatchIndirectCommand&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DispatchIndirectCommand const& rhs ) const + { + return ( x == rhs.x ) + && ( y == rhs.y ) + && ( z == rhs.z ); + } + + bool operator!=( DispatchIndirectCommand const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t x; + uint32_t y; + uint32_t z; + }; + static_assert( sizeof( DispatchIndirectCommand ) == sizeof( VkDispatchIndirectCommand ), "struct and wrapper have different size!" ); + + struct DisplayPlanePropertiesKHR + { + operator const VkDisplayPlanePropertiesKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayPlanePropertiesKHR const& rhs ) const + { + return ( currentDisplay == rhs.currentDisplay ) + && ( currentStackIndex == rhs.currentStackIndex ); + } + + bool operator!=( DisplayPlanePropertiesKHR const& rhs ) const + { + return !operator==( rhs ); + } + + DisplayKHR currentDisplay; + uint32_t currentStackIndex; + }; + static_assert( sizeof( DisplayPlanePropertiesKHR ) == sizeof( VkDisplayPlanePropertiesKHR ), "struct and wrapper have different size!" ); + + struct DisplayModeParametersKHR + { + DisplayModeParametersKHR( Extent2D visibleRegion_ = Extent2D(), uint32_t refreshRate_ = 0 ) + : visibleRegion( visibleRegion_ ) + , refreshRate( refreshRate_ ) + { + } + + DisplayModeParametersKHR( VkDisplayModeParametersKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayModeParametersKHR) ); + } + + DisplayModeParametersKHR& operator=( VkDisplayModeParametersKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayModeParametersKHR) ); + return *this; + } + + DisplayModeParametersKHR& setVisibleRegion( Extent2D visibleRegion_ ) + { + visibleRegion = visibleRegion_; + return *this; + } + + DisplayModeParametersKHR& setRefreshRate( uint32_t refreshRate_ ) + { + refreshRate = refreshRate_; + return *this; + } + + operator const VkDisplayModeParametersKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayModeParametersKHR const& rhs ) const + { + return ( visibleRegion == rhs.visibleRegion ) + && ( refreshRate == rhs.refreshRate ); + } + + bool operator!=( DisplayModeParametersKHR const& rhs ) const + { + return !operator==( rhs ); + } + + Extent2D visibleRegion; + uint32_t refreshRate; + }; + static_assert( sizeof( DisplayModeParametersKHR ) == sizeof( VkDisplayModeParametersKHR ), "struct and wrapper have different size!" ); + + struct DisplayModePropertiesKHR + { + operator const VkDisplayModePropertiesKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayModePropertiesKHR const& rhs ) const + { + return ( displayMode == rhs.displayMode ) + && ( parameters == rhs.parameters ); + } + + bool operator!=( DisplayModePropertiesKHR const& rhs ) const + { + return !operator==( rhs ); + } + + DisplayModeKHR displayMode; + DisplayModeParametersKHR parameters; + }; + static_assert( sizeof( DisplayModePropertiesKHR ) == sizeof( VkDisplayModePropertiesKHR ), "struct and wrapper have different size!" ); + + enum class ImageLayout + { + eUndefined = VK_IMAGE_LAYOUT_UNDEFINED, + eGeneral = VK_IMAGE_LAYOUT_GENERAL, + eColorAttachmentOptimal = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + eDepthStencilAttachmentOptimal = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + eDepthStencilReadOnlyOptimal = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, + eShaderReadOnlyOptimal = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + eTransferSrcOptimal = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + eTransferDstOptimal = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + ePreinitialized = VK_IMAGE_LAYOUT_PREINITIALIZED, + ePresentSrcKHR = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + }; + + struct DescriptorImageInfo + { + DescriptorImageInfo( Sampler sampler_ = Sampler(), ImageView imageView_ = ImageView(), ImageLayout imageLayout_ = ImageLayout::eUndefined ) + : sampler( sampler_ ) + , imageView( imageView_ ) + , imageLayout( imageLayout_ ) + { + } + + DescriptorImageInfo( VkDescriptorImageInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorImageInfo) ); + } + + DescriptorImageInfo& operator=( VkDescriptorImageInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorImageInfo) ); + return *this; + } + + DescriptorImageInfo& setSampler( Sampler sampler_ ) + { + sampler = sampler_; + return *this; + } + + DescriptorImageInfo& setImageView( ImageView imageView_ ) + { + imageView = imageView_; + return *this; + } + + DescriptorImageInfo& setImageLayout( ImageLayout imageLayout_ ) + { + imageLayout = imageLayout_; + return *this; + } + + operator const VkDescriptorImageInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DescriptorImageInfo const& rhs ) const + { + return ( sampler == rhs.sampler ) + && ( imageView == rhs.imageView ) + && ( imageLayout == rhs.imageLayout ); + } + + bool operator!=( DescriptorImageInfo const& rhs ) const + { + return !operator==( rhs ); + } + + Sampler sampler; + ImageView imageView; + ImageLayout imageLayout; + }; + static_assert( sizeof( DescriptorImageInfo ) == sizeof( VkDescriptorImageInfo ), "struct and wrapper have different size!" ); + + struct AttachmentReference + { + AttachmentReference( uint32_t attachment_ = 0, ImageLayout layout_ = ImageLayout::eUndefined ) + : attachment( attachment_ ) + , layout( layout_ ) + { + } + + AttachmentReference( VkAttachmentReference const & rhs ) + { + memcpy( this, &rhs, sizeof(AttachmentReference) ); + } + + AttachmentReference& operator=( VkAttachmentReference const & rhs ) + { + memcpy( this, &rhs, sizeof(AttachmentReference) ); + return *this; + } + + AttachmentReference& setAttachment( uint32_t attachment_ ) + { + attachment = attachment_; + return *this; + } + + AttachmentReference& setLayout( ImageLayout layout_ ) + { + layout = layout_; + return *this; + } + + operator const VkAttachmentReference&() const + { + return *reinterpret_cast(this); + } + + bool operator==( AttachmentReference const& rhs ) const + { + return ( attachment == rhs.attachment ) + && ( layout == rhs.layout ); + } + + bool operator!=( AttachmentReference const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t attachment; + ImageLayout layout; + }; + static_assert( sizeof( AttachmentReference ) == sizeof( VkAttachmentReference ), "struct and wrapper have different size!" ); + + enum class AttachmentLoadOp + { + eLoad = VK_ATTACHMENT_LOAD_OP_LOAD, + eClear = VK_ATTACHMENT_LOAD_OP_CLEAR, + eDontCare = VK_ATTACHMENT_LOAD_OP_DONT_CARE + }; + + enum class AttachmentStoreOp + { + eStore = VK_ATTACHMENT_STORE_OP_STORE, + eDontCare = VK_ATTACHMENT_STORE_OP_DONT_CARE + }; + + enum class ImageType + { + e1D = VK_IMAGE_TYPE_1D, + e2D = VK_IMAGE_TYPE_2D, + e3D = VK_IMAGE_TYPE_3D + }; + + enum class ImageTiling + { + eOptimal = VK_IMAGE_TILING_OPTIMAL, + eLinear = VK_IMAGE_TILING_LINEAR + }; + + enum class ImageViewType + { + e1D = VK_IMAGE_VIEW_TYPE_1D, + e2D = VK_IMAGE_VIEW_TYPE_2D, + e3D = VK_IMAGE_VIEW_TYPE_3D, + eCube = VK_IMAGE_VIEW_TYPE_CUBE, + e1DArray = VK_IMAGE_VIEW_TYPE_1D_ARRAY, + e2DArray = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + eCubeArray = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY + }; + + enum class CommandBufferLevel + { + ePrimary = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + eSecondary = VK_COMMAND_BUFFER_LEVEL_SECONDARY + }; + + enum class ComponentSwizzle + { + eIdentity = VK_COMPONENT_SWIZZLE_IDENTITY, + eZero = VK_COMPONENT_SWIZZLE_ZERO, + eOne = VK_COMPONENT_SWIZZLE_ONE, + eR = VK_COMPONENT_SWIZZLE_R, + eG = VK_COMPONENT_SWIZZLE_G, + eB = VK_COMPONENT_SWIZZLE_B, + eA = VK_COMPONENT_SWIZZLE_A + }; + + struct ComponentMapping + { + ComponentMapping( ComponentSwizzle r_ = ComponentSwizzle::eIdentity, ComponentSwizzle g_ = ComponentSwizzle::eIdentity, ComponentSwizzle b_ = ComponentSwizzle::eIdentity, ComponentSwizzle a_ = ComponentSwizzle::eIdentity ) + : r( r_ ) + , g( g_ ) + , b( b_ ) + , a( a_ ) + { + } + + ComponentMapping( VkComponentMapping const & rhs ) + { + memcpy( this, &rhs, sizeof(ComponentMapping) ); + } + + ComponentMapping& operator=( VkComponentMapping const & rhs ) + { + memcpy( this, &rhs, sizeof(ComponentMapping) ); + return *this; + } + + ComponentMapping& setR( ComponentSwizzle r_ ) + { + r = r_; + return *this; + } + + ComponentMapping& setG( ComponentSwizzle g_ ) + { + g = g_; + return *this; + } + + ComponentMapping& setB( ComponentSwizzle b_ ) + { + b = b_; + return *this; + } + + ComponentMapping& setA( ComponentSwizzle a_ ) + { + a = a_; + return *this; + } + + operator const VkComponentMapping&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ComponentMapping const& rhs ) const + { + return ( r == rhs.r ) + && ( g == rhs.g ) + && ( b == rhs.b ) + && ( a == rhs.a ); + } + + bool operator!=( ComponentMapping const& rhs ) const + { + return !operator==( rhs ); + } + + ComponentSwizzle r; + ComponentSwizzle g; + ComponentSwizzle b; + ComponentSwizzle a; + }; + static_assert( sizeof( ComponentMapping ) == sizeof( VkComponentMapping ), "struct and wrapper have different size!" ); + + enum class DescriptorType + { + eSampler = VK_DESCRIPTOR_TYPE_SAMPLER, + eCombinedImageSampler = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + eSampledImage = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + eStorageImage = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + eUniformTexelBuffer = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + eStorageTexelBuffer = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + eUniformBuffer = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + eStorageBuffer = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + eUniformBufferDynamic = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + eStorageBufferDynamic = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, + eInputAttachment = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT + }; + + struct DescriptorPoolSize + { + DescriptorPoolSize( DescriptorType type_ = DescriptorType::eSampler, uint32_t descriptorCount_ = 0 ) + : type( type_ ) + , descriptorCount( descriptorCount_ ) + { + } + + DescriptorPoolSize( VkDescriptorPoolSize const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorPoolSize) ); + } + + DescriptorPoolSize& operator=( VkDescriptorPoolSize const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorPoolSize) ); + return *this; + } + + DescriptorPoolSize& setType( DescriptorType type_ ) + { + type = type_; + return *this; + } + + DescriptorPoolSize& setDescriptorCount( uint32_t descriptorCount_ ) + { + descriptorCount = descriptorCount_; + return *this; + } + + operator const VkDescriptorPoolSize&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DescriptorPoolSize const& rhs ) const + { + return ( type == rhs.type ) + && ( descriptorCount == rhs.descriptorCount ); + } + + bool operator!=( DescriptorPoolSize const& rhs ) const + { + return !operator==( rhs ); + } + + DescriptorType type; + uint32_t descriptorCount; + }; + static_assert( sizeof( DescriptorPoolSize ) == sizeof( VkDescriptorPoolSize ), "struct and wrapper have different size!" ); + + enum class QueryType + { + eOcclusion = VK_QUERY_TYPE_OCCLUSION, + ePipelineStatistics = VK_QUERY_TYPE_PIPELINE_STATISTICS, + eTimestamp = VK_QUERY_TYPE_TIMESTAMP + }; + + enum class BorderColor + { + eFloatTransparentBlack = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + eIntTransparentBlack = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, + eFloatOpaqueBlack = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + eIntOpaqueBlack = VK_BORDER_COLOR_INT_OPAQUE_BLACK, + eFloatOpaqueWhite = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + eIntOpaqueWhite = VK_BORDER_COLOR_INT_OPAQUE_WHITE + }; + + enum class PipelineBindPoint + { + eGraphics = VK_PIPELINE_BIND_POINT_GRAPHICS, + eCompute = VK_PIPELINE_BIND_POINT_COMPUTE + }; + + struct SubpassDescription + { + SubpassDescription( SubpassDescriptionFlags flags_ = SubpassDescriptionFlags(), PipelineBindPoint pipelineBindPoint_ = PipelineBindPoint::eGraphics, uint32_t inputAttachmentCount_ = 0, const AttachmentReference* pInputAttachments_ = nullptr, uint32_t colorAttachmentCount_ = 0, const AttachmentReference* pColorAttachments_ = nullptr, const AttachmentReference* pResolveAttachments_ = nullptr, const AttachmentReference* pDepthStencilAttachment_ = nullptr, uint32_t preserveAttachmentCount_ = 0, const uint32_t* pPreserveAttachments_ = nullptr ) + : flags( flags_ ) + , pipelineBindPoint( pipelineBindPoint_ ) + , inputAttachmentCount( inputAttachmentCount_ ) + , pInputAttachments( pInputAttachments_ ) + , colorAttachmentCount( colorAttachmentCount_ ) + , pColorAttachments( pColorAttachments_ ) + , pResolveAttachments( pResolveAttachments_ ) + , pDepthStencilAttachment( pDepthStencilAttachment_ ) + , preserveAttachmentCount( preserveAttachmentCount_ ) + , pPreserveAttachments( pPreserveAttachments_ ) + { + } + + SubpassDescription( VkSubpassDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(SubpassDescription) ); + } + + SubpassDescription& operator=( VkSubpassDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(SubpassDescription) ); + return *this; + } + + SubpassDescription& setFlags( SubpassDescriptionFlags flags_ ) + { + flags = flags_; + return *this; + } + + SubpassDescription& setPipelineBindPoint( PipelineBindPoint pipelineBindPoint_ ) + { + pipelineBindPoint = pipelineBindPoint_; + return *this; + } + + SubpassDescription& setInputAttachmentCount( uint32_t inputAttachmentCount_ ) + { + inputAttachmentCount = inputAttachmentCount_; + return *this; + } + + SubpassDescription& setPInputAttachments( const AttachmentReference* pInputAttachments_ ) + { + pInputAttachments = pInputAttachments_; + return *this; + } + + SubpassDescription& setColorAttachmentCount( uint32_t colorAttachmentCount_ ) + { + colorAttachmentCount = colorAttachmentCount_; + return *this; + } + + SubpassDescription& setPColorAttachments( const AttachmentReference* pColorAttachments_ ) + { + pColorAttachments = pColorAttachments_; + return *this; + } + + SubpassDescription& setPResolveAttachments( const AttachmentReference* pResolveAttachments_ ) + { + pResolveAttachments = pResolveAttachments_; + return *this; + } + + SubpassDescription& setPDepthStencilAttachment( const AttachmentReference* pDepthStencilAttachment_ ) + { + pDepthStencilAttachment = pDepthStencilAttachment_; + return *this; + } + + SubpassDescription& setPreserveAttachmentCount( uint32_t preserveAttachmentCount_ ) + { + preserveAttachmentCount = preserveAttachmentCount_; + return *this; + } + + SubpassDescription& setPPreserveAttachments( const uint32_t* pPreserveAttachments_ ) + { + pPreserveAttachments = pPreserveAttachments_; + return *this; + } + + operator const VkSubpassDescription&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SubpassDescription const& rhs ) const + { + return ( flags == rhs.flags ) + && ( pipelineBindPoint == rhs.pipelineBindPoint ) + && ( inputAttachmentCount == rhs.inputAttachmentCount ) + && ( pInputAttachments == rhs.pInputAttachments ) + && ( colorAttachmentCount == rhs.colorAttachmentCount ) + && ( pColorAttachments == rhs.pColorAttachments ) + && ( pResolveAttachments == rhs.pResolveAttachments ) + && ( pDepthStencilAttachment == rhs.pDepthStencilAttachment ) + && ( preserveAttachmentCount == rhs.preserveAttachmentCount ) + && ( pPreserveAttachments == rhs.pPreserveAttachments ); + } + + bool operator!=( SubpassDescription const& rhs ) const + { + return !operator==( rhs ); + } + + SubpassDescriptionFlags flags; + PipelineBindPoint pipelineBindPoint; + uint32_t inputAttachmentCount; + const AttachmentReference* pInputAttachments; + uint32_t colorAttachmentCount; + const AttachmentReference* pColorAttachments; + const AttachmentReference* pResolveAttachments; + const AttachmentReference* pDepthStencilAttachment; + uint32_t preserveAttachmentCount; + const uint32_t* pPreserveAttachments; + }; + static_assert( sizeof( SubpassDescription ) == sizeof( VkSubpassDescription ), "struct and wrapper have different size!" ); + + enum class PipelineCacheHeaderVersion + { + eOne = VK_PIPELINE_CACHE_HEADER_VERSION_ONE + }; + + enum class PrimitiveTopology + { + ePointList = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + eLineList = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, + eLineStrip = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, + eTriangleList = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + eTriangleStrip = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + eTriangleFan = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, + eLineListWithAdjacency = VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, + eLineStripWithAdjacency = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, + eTriangleListWithAdjacency = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, + eTriangleStripWithAdjacency = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, + ePatchList = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST + }; + + enum class SharingMode + { + eExclusive = VK_SHARING_MODE_EXCLUSIVE, + eConcurrent = VK_SHARING_MODE_CONCURRENT + }; + + enum class IndexType + { + eUint16 = VK_INDEX_TYPE_UINT16, + eUint32 = VK_INDEX_TYPE_UINT32 + }; + + enum class Filter + { + eNearest = VK_FILTER_NEAREST, + eLinear = VK_FILTER_LINEAR, + eCubicIMG = VK_FILTER_CUBIC_IMG + }; + + enum class SamplerMipmapMode + { + eNearest = VK_SAMPLER_MIPMAP_MODE_NEAREST, + eLinear = VK_SAMPLER_MIPMAP_MODE_LINEAR + }; + + enum class SamplerAddressMode + { + eRepeat = VK_SAMPLER_ADDRESS_MODE_REPEAT, + eMirroredRepeat = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, + eClampToEdge = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + eClampToBorder = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + eMirrorClampToEdge = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE + }; + + enum class CompareOp + { + eNever = VK_COMPARE_OP_NEVER, + eLess = VK_COMPARE_OP_LESS, + eEqual = VK_COMPARE_OP_EQUAL, + eLessOrEqual = VK_COMPARE_OP_LESS_OR_EQUAL, + eGreater = VK_COMPARE_OP_GREATER, + eNotEqual = VK_COMPARE_OP_NOT_EQUAL, + eGreaterOrEqual = VK_COMPARE_OP_GREATER_OR_EQUAL, + eAlways = VK_COMPARE_OP_ALWAYS + }; + + enum class PolygonMode + { + eFill = VK_POLYGON_MODE_FILL, + eLine = VK_POLYGON_MODE_LINE, + ePoint = VK_POLYGON_MODE_POINT + }; + + enum class CullModeFlagBits + { + eNone = VK_CULL_MODE_NONE, + eFront = VK_CULL_MODE_FRONT_BIT, + eBack = VK_CULL_MODE_BACK_BIT, + eFrontAndBack = VK_CULL_MODE_FRONT_AND_BACK + }; + + using CullModeFlags = Flags; + + VULKAN_HPP_INLINE CullModeFlags operator|( CullModeFlagBits bit0, CullModeFlagBits bit1 ) + { + return CullModeFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE CullModeFlags operator~( CullModeFlagBits bits ) + { + return ~( CullModeFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(CullModeFlagBits::eNone) | VkFlags(CullModeFlagBits::eFront) | VkFlags(CullModeFlagBits::eBack) | VkFlags(CullModeFlagBits::eFrontAndBack) + }; + }; + + enum class FrontFace + { + eCounterClockwise = VK_FRONT_FACE_COUNTER_CLOCKWISE, + eClockwise = VK_FRONT_FACE_CLOCKWISE + }; + + enum class BlendFactor + { + eZero = VK_BLEND_FACTOR_ZERO, + eOne = VK_BLEND_FACTOR_ONE, + eSrcColor = VK_BLEND_FACTOR_SRC_COLOR, + eOneMinusSrcColor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, + eDstColor = VK_BLEND_FACTOR_DST_COLOR, + eOneMinusDstColor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, + eSrcAlpha = VK_BLEND_FACTOR_SRC_ALPHA, + eOneMinusSrcAlpha = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + eDstAlpha = VK_BLEND_FACTOR_DST_ALPHA, + eOneMinusDstAlpha = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, + eConstantColor = VK_BLEND_FACTOR_CONSTANT_COLOR, + eOneMinusConstantColor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, + eConstantAlpha = VK_BLEND_FACTOR_CONSTANT_ALPHA, + eOneMinusConstantAlpha = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, + eSrcAlphaSaturate = VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, + eSrc1Color = VK_BLEND_FACTOR_SRC1_COLOR, + eOneMinusSrc1Color = VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, + eSrc1Alpha = VK_BLEND_FACTOR_SRC1_ALPHA, + eOneMinusSrc1Alpha = VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA + }; + + enum class BlendOp + { + eAdd = VK_BLEND_OP_ADD, + eSubtract = VK_BLEND_OP_SUBTRACT, + eReverseSubtract = VK_BLEND_OP_REVERSE_SUBTRACT, + eMin = VK_BLEND_OP_MIN, + eMax = VK_BLEND_OP_MAX + }; + + enum class StencilOp + { + eKeep = VK_STENCIL_OP_KEEP, + eZero = VK_STENCIL_OP_ZERO, + eReplace = VK_STENCIL_OP_REPLACE, + eIncrementAndClamp = VK_STENCIL_OP_INCREMENT_AND_CLAMP, + eDecrementAndClamp = VK_STENCIL_OP_DECREMENT_AND_CLAMP, + eInvert = VK_STENCIL_OP_INVERT, + eIncrementAndWrap = VK_STENCIL_OP_INCREMENT_AND_WRAP, + eDecrementAndWrap = VK_STENCIL_OP_DECREMENT_AND_WRAP + }; + + struct StencilOpState + { + StencilOpState( StencilOp failOp_ = StencilOp::eKeep, StencilOp passOp_ = StencilOp::eKeep, StencilOp depthFailOp_ = StencilOp::eKeep, CompareOp compareOp_ = CompareOp::eNever, uint32_t compareMask_ = 0, uint32_t writeMask_ = 0, uint32_t reference_ = 0 ) + : failOp( failOp_ ) + , passOp( passOp_ ) + , depthFailOp( depthFailOp_ ) + , compareOp( compareOp_ ) + , compareMask( compareMask_ ) + , writeMask( writeMask_ ) + , reference( reference_ ) + { + } + + StencilOpState( VkStencilOpState const & rhs ) + { + memcpy( this, &rhs, sizeof(StencilOpState) ); + } + + StencilOpState& operator=( VkStencilOpState const & rhs ) + { + memcpy( this, &rhs, sizeof(StencilOpState) ); + return *this; + } + + StencilOpState& setFailOp( StencilOp failOp_ ) + { + failOp = failOp_; + return *this; + } + + StencilOpState& setPassOp( StencilOp passOp_ ) + { + passOp = passOp_; + return *this; + } + + StencilOpState& setDepthFailOp( StencilOp depthFailOp_ ) + { + depthFailOp = depthFailOp_; + return *this; + } + + StencilOpState& setCompareOp( CompareOp compareOp_ ) + { + compareOp = compareOp_; + return *this; + } + + StencilOpState& setCompareMask( uint32_t compareMask_ ) + { + compareMask = compareMask_; + return *this; + } + + StencilOpState& setWriteMask( uint32_t writeMask_ ) + { + writeMask = writeMask_; + return *this; + } + + StencilOpState& setReference( uint32_t reference_ ) + { + reference = reference_; + return *this; + } + + operator const VkStencilOpState&() const + { + return *reinterpret_cast(this); + } + + bool operator==( StencilOpState const& rhs ) const + { + return ( failOp == rhs.failOp ) + && ( passOp == rhs.passOp ) + && ( depthFailOp == rhs.depthFailOp ) + && ( compareOp == rhs.compareOp ) + && ( compareMask == rhs.compareMask ) + && ( writeMask == rhs.writeMask ) + && ( reference == rhs.reference ); + } + + bool operator!=( StencilOpState const& rhs ) const + { + return !operator==( rhs ); + } + + StencilOp failOp; + StencilOp passOp; + StencilOp depthFailOp; + CompareOp compareOp; + uint32_t compareMask; + uint32_t writeMask; + uint32_t reference; + }; + static_assert( sizeof( StencilOpState ) == sizeof( VkStencilOpState ), "struct and wrapper have different size!" ); + + enum class LogicOp + { + eClear = VK_LOGIC_OP_CLEAR, + eAnd = VK_LOGIC_OP_AND, + eAndReverse = VK_LOGIC_OP_AND_REVERSE, + eCopy = VK_LOGIC_OP_COPY, + eAndInverted = VK_LOGIC_OP_AND_INVERTED, + eNoOp = VK_LOGIC_OP_NO_OP, + eXor = VK_LOGIC_OP_XOR, + eOr = VK_LOGIC_OP_OR, + eNor = VK_LOGIC_OP_NOR, + eEquivalent = VK_LOGIC_OP_EQUIVALENT, + eInvert = VK_LOGIC_OP_INVERT, + eOrReverse = VK_LOGIC_OP_OR_REVERSE, + eCopyInverted = VK_LOGIC_OP_COPY_INVERTED, + eOrInverted = VK_LOGIC_OP_OR_INVERTED, + eNand = VK_LOGIC_OP_NAND, + eSet = VK_LOGIC_OP_SET + }; + + enum class InternalAllocationType + { + eExecutable = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE + }; + + enum class SystemAllocationScope + { + eCommand = VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, + eObject = VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, + eCache = VK_SYSTEM_ALLOCATION_SCOPE_CACHE, + eDevice = VK_SYSTEM_ALLOCATION_SCOPE_DEVICE, + eInstance = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE + }; + + enum class PhysicalDeviceType + { + eOther = VK_PHYSICAL_DEVICE_TYPE_OTHER, + eIntegratedGpu = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, + eDiscreteGpu = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, + eVirtualGpu = VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU, + eCpu = VK_PHYSICAL_DEVICE_TYPE_CPU + }; + + enum class VertexInputRate + { + eVertex = VK_VERTEX_INPUT_RATE_VERTEX, + eInstance = VK_VERTEX_INPUT_RATE_INSTANCE + }; + + struct VertexInputBindingDescription + { + VertexInputBindingDescription( uint32_t binding_ = 0, uint32_t stride_ = 0, VertexInputRate inputRate_ = VertexInputRate::eVertex ) + : binding( binding_ ) + , stride( stride_ ) + , inputRate( inputRate_ ) + { + } + + VertexInputBindingDescription( VkVertexInputBindingDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(VertexInputBindingDescription) ); + } + + VertexInputBindingDescription& operator=( VkVertexInputBindingDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(VertexInputBindingDescription) ); + return *this; + } + + VertexInputBindingDescription& setBinding( uint32_t binding_ ) + { + binding = binding_; + return *this; + } + + VertexInputBindingDescription& setStride( uint32_t stride_ ) + { + stride = stride_; + return *this; + } + + VertexInputBindingDescription& setInputRate( VertexInputRate inputRate_ ) + { + inputRate = inputRate_; + return *this; + } + + operator const VkVertexInputBindingDescription&() const + { + return *reinterpret_cast(this); + } + + bool operator==( VertexInputBindingDescription const& rhs ) const + { + return ( binding == rhs.binding ) + && ( stride == rhs.stride ) + && ( inputRate == rhs.inputRate ); + } + + bool operator!=( VertexInputBindingDescription const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t binding; + uint32_t stride; + VertexInputRate inputRate; + }; + static_assert( sizeof( VertexInputBindingDescription ) == sizeof( VkVertexInputBindingDescription ), "struct and wrapper have different size!" ); + + enum class Format + { + eUndefined = VK_FORMAT_UNDEFINED, + eR4G4UnormPack8 = VK_FORMAT_R4G4_UNORM_PACK8, + eR4G4B4A4UnormPack16 = VK_FORMAT_R4G4B4A4_UNORM_PACK16, + eB4G4R4A4UnormPack16 = VK_FORMAT_B4G4R4A4_UNORM_PACK16, + eR5G6B5UnormPack16 = VK_FORMAT_R5G6B5_UNORM_PACK16, + eB5G6R5UnormPack16 = VK_FORMAT_B5G6R5_UNORM_PACK16, + eR5G5B5A1UnormPack16 = VK_FORMAT_R5G5B5A1_UNORM_PACK16, + eB5G5R5A1UnormPack16 = VK_FORMAT_B5G5R5A1_UNORM_PACK16, + eA1R5G5B5UnormPack16 = VK_FORMAT_A1R5G5B5_UNORM_PACK16, + eR8Unorm = VK_FORMAT_R8_UNORM, + eR8Snorm = VK_FORMAT_R8_SNORM, + eR8Uscaled = VK_FORMAT_R8_USCALED, + eR8Sscaled = VK_FORMAT_R8_SSCALED, + eR8Uint = VK_FORMAT_R8_UINT, + eR8Sint = VK_FORMAT_R8_SINT, + eR8Srgb = VK_FORMAT_R8_SRGB, + eR8G8Unorm = VK_FORMAT_R8G8_UNORM, + eR8G8Snorm = VK_FORMAT_R8G8_SNORM, + eR8G8Uscaled = VK_FORMAT_R8G8_USCALED, + eR8G8Sscaled = VK_FORMAT_R8G8_SSCALED, + eR8G8Uint = VK_FORMAT_R8G8_UINT, + eR8G8Sint = VK_FORMAT_R8G8_SINT, + eR8G8Srgb = VK_FORMAT_R8G8_SRGB, + eR8G8B8Unorm = VK_FORMAT_R8G8B8_UNORM, + eR8G8B8Snorm = VK_FORMAT_R8G8B8_SNORM, + eR8G8B8Uscaled = VK_FORMAT_R8G8B8_USCALED, + eR8G8B8Sscaled = VK_FORMAT_R8G8B8_SSCALED, + eR8G8B8Uint = VK_FORMAT_R8G8B8_UINT, + eR8G8B8Sint = VK_FORMAT_R8G8B8_SINT, + eR8G8B8Srgb = VK_FORMAT_R8G8B8_SRGB, + eB8G8R8Unorm = VK_FORMAT_B8G8R8_UNORM, + eB8G8R8Snorm = VK_FORMAT_B8G8R8_SNORM, + eB8G8R8Uscaled = VK_FORMAT_B8G8R8_USCALED, + eB8G8R8Sscaled = VK_FORMAT_B8G8R8_SSCALED, + eB8G8R8Uint = VK_FORMAT_B8G8R8_UINT, + eB8G8R8Sint = VK_FORMAT_B8G8R8_SINT, + eB8G8R8Srgb = VK_FORMAT_B8G8R8_SRGB, + eR8G8B8A8Unorm = VK_FORMAT_R8G8B8A8_UNORM, + eR8G8B8A8Snorm = VK_FORMAT_R8G8B8A8_SNORM, + eR8G8B8A8Uscaled = VK_FORMAT_R8G8B8A8_USCALED, + eR8G8B8A8Sscaled = VK_FORMAT_R8G8B8A8_SSCALED, + eR8G8B8A8Uint = VK_FORMAT_R8G8B8A8_UINT, + eR8G8B8A8Sint = VK_FORMAT_R8G8B8A8_SINT, + eR8G8B8A8Srgb = VK_FORMAT_R8G8B8A8_SRGB, + eB8G8R8A8Unorm = VK_FORMAT_B8G8R8A8_UNORM, + eB8G8R8A8Snorm = VK_FORMAT_B8G8R8A8_SNORM, + eB8G8R8A8Uscaled = VK_FORMAT_B8G8R8A8_USCALED, + eB8G8R8A8Sscaled = VK_FORMAT_B8G8R8A8_SSCALED, + eB8G8R8A8Uint = VK_FORMAT_B8G8R8A8_UINT, + eB8G8R8A8Sint = VK_FORMAT_B8G8R8A8_SINT, + eB8G8R8A8Srgb = VK_FORMAT_B8G8R8A8_SRGB, + eA8B8G8R8UnormPack32 = VK_FORMAT_A8B8G8R8_UNORM_PACK32, + eA8B8G8R8SnormPack32 = VK_FORMAT_A8B8G8R8_SNORM_PACK32, + eA8B8G8R8UscaledPack32 = VK_FORMAT_A8B8G8R8_USCALED_PACK32, + eA8B8G8R8SscaledPack32 = VK_FORMAT_A8B8G8R8_SSCALED_PACK32, + eA8B8G8R8UintPack32 = VK_FORMAT_A8B8G8R8_UINT_PACK32, + eA8B8G8R8SintPack32 = VK_FORMAT_A8B8G8R8_SINT_PACK32, + eA8B8G8R8SrgbPack32 = VK_FORMAT_A8B8G8R8_SRGB_PACK32, + eA2R10G10B10UnormPack32 = VK_FORMAT_A2R10G10B10_UNORM_PACK32, + eA2R10G10B10SnormPack32 = VK_FORMAT_A2R10G10B10_SNORM_PACK32, + eA2R10G10B10UscaledPack32 = VK_FORMAT_A2R10G10B10_USCALED_PACK32, + eA2R10G10B10SscaledPack32 = VK_FORMAT_A2R10G10B10_SSCALED_PACK32, + eA2R10G10B10UintPack32 = VK_FORMAT_A2R10G10B10_UINT_PACK32, + eA2R10G10B10SintPack32 = VK_FORMAT_A2R10G10B10_SINT_PACK32, + eA2B10G10R10UnormPack32 = VK_FORMAT_A2B10G10R10_UNORM_PACK32, + eA2B10G10R10SnormPack32 = VK_FORMAT_A2B10G10R10_SNORM_PACK32, + eA2B10G10R10UscaledPack32 = VK_FORMAT_A2B10G10R10_USCALED_PACK32, + eA2B10G10R10SscaledPack32 = VK_FORMAT_A2B10G10R10_SSCALED_PACK32, + eA2B10G10R10UintPack32 = VK_FORMAT_A2B10G10R10_UINT_PACK32, + eA2B10G10R10SintPack32 = VK_FORMAT_A2B10G10R10_SINT_PACK32, + eR16Unorm = VK_FORMAT_R16_UNORM, + eR16Snorm = VK_FORMAT_R16_SNORM, + eR16Uscaled = VK_FORMAT_R16_USCALED, + eR16Sscaled = VK_FORMAT_R16_SSCALED, + eR16Uint = VK_FORMAT_R16_UINT, + eR16Sint = VK_FORMAT_R16_SINT, + eR16Sfloat = VK_FORMAT_R16_SFLOAT, + eR16G16Unorm = VK_FORMAT_R16G16_UNORM, + eR16G16Snorm = VK_FORMAT_R16G16_SNORM, + eR16G16Uscaled = VK_FORMAT_R16G16_USCALED, + eR16G16Sscaled = VK_FORMAT_R16G16_SSCALED, + eR16G16Uint = VK_FORMAT_R16G16_UINT, + eR16G16Sint = VK_FORMAT_R16G16_SINT, + eR16G16Sfloat = VK_FORMAT_R16G16_SFLOAT, + eR16G16B16Unorm = VK_FORMAT_R16G16B16_UNORM, + eR16G16B16Snorm = VK_FORMAT_R16G16B16_SNORM, + eR16G16B16Uscaled = VK_FORMAT_R16G16B16_USCALED, + eR16G16B16Sscaled = VK_FORMAT_R16G16B16_SSCALED, + eR16G16B16Uint = VK_FORMAT_R16G16B16_UINT, + eR16G16B16Sint = VK_FORMAT_R16G16B16_SINT, + eR16G16B16Sfloat = VK_FORMAT_R16G16B16_SFLOAT, + eR16G16B16A16Unorm = VK_FORMAT_R16G16B16A16_UNORM, + eR16G16B16A16Snorm = VK_FORMAT_R16G16B16A16_SNORM, + eR16G16B16A16Uscaled = VK_FORMAT_R16G16B16A16_USCALED, + eR16G16B16A16Sscaled = VK_FORMAT_R16G16B16A16_SSCALED, + eR16G16B16A16Uint = VK_FORMAT_R16G16B16A16_UINT, + eR16G16B16A16Sint = VK_FORMAT_R16G16B16A16_SINT, + eR16G16B16A16Sfloat = VK_FORMAT_R16G16B16A16_SFLOAT, + eR32Uint = VK_FORMAT_R32_UINT, + eR32Sint = VK_FORMAT_R32_SINT, + eR32Sfloat = VK_FORMAT_R32_SFLOAT, + eR32G32Uint = VK_FORMAT_R32G32_UINT, + eR32G32Sint = VK_FORMAT_R32G32_SINT, + eR32G32Sfloat = VK_FORMAT_R32G32_SFLOAT, + eR32G32B32Uint = VK_FORMAT_R32G32B32_UINT, + eR32G32B32Sint = VK_FORMAT_R32G32B32_SINT, + eR32G32B32Sfloat = VK_FORMAT_R32G32B32_SFLOAT, + eR32G32B32A32Uint = VK_FORMAT_R32G32B32A32_UINT, + eR32G32B32A32Sint = VK_FORMAT_R32G32B32A32_SINT, + eR32G32B32A32Sfloat = VK_FORMAT_R32G32B32A32_SFLOAT, + eR64Uint = VK_FORMAT_R64_UINT, + eR64Sint = VK_FORMAT_R64_SINT, + eR64Sfloat = VK_FORMAT_R64_SFLOAT, + eR64G64Uint = VK_FORMAT_R64G64_UINT, + eR64G64Sint = VK_FORMAT_R64G64_SINT, + eR64G64Sfloat = VK_FORMAT_R64G64_SFLOAT, + eR64G64B64Uint = VK_FORMAT_R64G64B64_UINT, + eR64G64B64Sint = VK_FORMAT_R64G64B64_SINT, + eR64G64B64Sfloat = VK_FORMAT_R64G64B64_SFLOAT, + eR64G64B64A64Uint = VK_FORMAT_R64G64B64A64_UINT, + eR64G64B64A64Sint = VK_FORMAT_R64G64B64A64_SINT, + eR64G64B64A64Sfloat = VK_FORMAT_R64G64B64A64_SFLOAT, + eB10G11R11UfloatPack32 = VK_FORMAT_B10G11R11_UFLOAT_PACK32, + eE5B9G9R9UfloatPack32 = VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, + eD16Unorm = VK_FORMAT_D16_UNORM, + eX8D24UnormPack32 = VK_FORMAT_X8_D24_UNORM_PACK32, + eD32Sfloat = VK_FORMAT_D32_SFLOAT, + eS8Uint = VK_FORMAT_S8_UINT, + eD16UnormS8Uint = VK_FORMAT_D16_UNORM_S8_UINT, + eD24UnormS8Uint = VK_FORMAT_D24_UNORM_S8_UINT, + eD32SfloatS8Uint = VK_FORMAT_D32_SFLOAT_S8_UINT, + eBc1RgbUnormBlock = VK_FORMAT_BC1_RGB_UNORM_BLOCK, + eBc1RgbSrgbBlock = VK_FORMAT_BC1_RGB_SRGB_BLOCK, + eBc1RgbaUnormBlock = VK_FORMAT_BC1_RGBA_UNORM_BLOCK, + eBc1RgbaSrgbBlock = VK_FORMAT_BC1_RGBA_SRGB_BLOCK, + eBc2UnormBlock = VK_FORMAT_BC2_UNORM_BLOCK, + eBc2SrgbBlock = VK_FORMAT_BC2_SRGB_BLOCK, + eBc3UnormBlock = VK_FORMAT_BC3_UNORM_BLOCK, + eBc3SrgbBlock = VK_FORMAT_BC3_SRGB_BLOCK, + eBc4UnormBlock = VK_FORMAT_BC4_UNORM_BLOCK, + eBc4SnormBlock = VK_FORMAT_BC4_SNORM_BLOCK, + eBc5UnormBlock = VK_FORMAT_BC5_UNORM_BLOCK, + eBc5SnormBlock = VK_FORMAT_BC5_SNORM_BLOCK, + eBc6HUfloatBlock = VK_FORMAT_BC6H_UFLOAT_BLOCK, + eBc6HSfloatBlock = VK_FORMAT_BC6H_SFLOAT_BLOCK, + eBc7UnormBlock = VK_FORMAT_BC7_UNORM_BLOCK, + eBc7SrgbBlock = VK_FORMAT_BC7_SRGB_BLOCK, + eEtc2R8G8B8UnormBlock = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, + eEtc2R8G8B8SrgbBlock = VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, + eEtc2R8G8B8A1UnormBlock = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, + eEtc2R8G8B8A1SrgbBlock = VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + eEtc2R8G8B8A8UnormBlock = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, + eEtc2R8G8B8A8SrgbBlock = VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, + eEacR11UnormBlock = VK_FORMAT_EAC_R11_UNORM_BLOCK, + eEacR11SnormBlock = VK_FORMAT_EAC_R11_SNORM_BLOCK, + eEacR11G11UnormBlock = VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + eEacR11G11SnormBlock = VK_FORMAT_EAC_R11G11_SNORM_BLOCK, + eAstc4x4UnormBlock = VK_FORMAT_ASTC_4x4_UNORM_BLOCK, + eAstc4x4SrgbBlock = VK_FORMAT_ASTC_4x4_SRGB_BLOCK, + eAstc5x4UnormBlock = VK_FORMAT_ASTC_5x4_UNORM_BLOCK, + eAstc5x4SrgbBlock = VK_FORMAT_ASTC_5x4_SRGB_BLOCK, + eAstc5x5UnormBlock = VK_FORMAT_ASTC_5x5_UNORM_BLOCK, + eAstc5x5SrgbBlock = VK_FORMAT_ASTC_5x5_SRGB_BLOCK, + eAstc6x5UnormBlock = VK_FORMAT_ASTC_6x5_UNORM_BLOCK, + eAstc6x5SrgbBlock = VK_FORMAT_ASTC_6x5_SRGB_BLOCK, + eAstc6x6UnormBlock = VK_FORMAT_ASTC_6x6_UNORM_BLOCK, + eAstc6x6SrgbBlock = VK_FORMAT_ASTC_6x6_SRGB_BLOCK, + eAstc8x5UnormBlock = VK_FORMAT_ASTC_8x5_UNORM_BLOCK, + eAstc8x5SrgbBlock = VK_FORMAT_ASTC_8x5_SRGB_BLOCK, + eAstc8x6UnormBlock = VK_FORMAT_ASTC_8x6_UNORM_BLOCK, + eAstc8x6SrgbBlock = VK_FORMAT_ASTC_8x6_SRGB_BLOCK, + eAstc8x8UnormBlock = VK_FORMAT_ASTC_8x8_UNORM_BLOCK, + eAstc8x8SrgbBlock = VK_FORMAT_ASTC_8x8_SRGB_BLOCK, + eAstc10x5UnormBlock = VK_FORMAT_ASTC_10x5_UNORM_BLOCK, + eAstc10x5SrgbBlock = VK_FORMAT_ASTC_10x5_SRGB_BLOCK, + eAstc10x6UnormBlock = VK_FORMAT_ASTC_10x6_UNORM_BLOCK, + eAstc10x6SrgbBlock = VK_FORMAT_ASTC_10x6_SRGB_BLOCK, + eAstc10x8UnormBlock = VK_FORMAT_ASTC_10x8_UNORM_BLOCK, + eAstc10x8SrgbBlock = VK_FORMAT_ASTC_10x8_SRGB_BLOCK, + eAstc10x10UnormBlock = VK_FORMAT_ASTC_10x10_UNORM_BLOCK, + eAstc10x10SrgbBlock = VK_FORMAT_ASTC_10x10_SRGB_BLOCK, + eAstc12x10UnormBlock = VK_FORMAT_ASTC_12x10_UNORM_BLOCK, + eAstc12x10SrgbBlock = VK_FORMAT_ASTC_12x10_SRGB_BLOCK, + eAstc12x12UnormBlock = VK_FORMAT_ASTC_12x12_UNORM_BLOCK, + eAstc12x12SrgbBlock = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + ePvrtc12BppUnormBlockIMG = VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, + ePvrtc14BppUnormBlockIMG = VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, + ePvrtc22BppUnormBlockIMG = VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, + ePvrtc24BppUnormBlockIMG = VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, + ePvrtc12BppSrgbBlockIMG = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, + ePvrtc14BppSrgbBlockIMG = VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, + ePvrtc22BppSrgbBlockIMG = VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, + ePvrtc24BppSrgbBlockIMG = VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG + }; + + struct VertexInputAttributeDescription + { + VertexInputAttributeDescription( uint32_t location_ = 0, uint32_t binding_ = 0, Format format_ = Format::eUndefined, uint32_t offset_ = 0 ) + : location( location_ ) + , binding( binding_ ) + , format( format_ ) + , offset( offset_ ) + { + } + + VertexInputAttributeDescription( VkVertexInputAttributeDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(VertexInputAttributeDescription) ); + } + + VertexInputAttributeDescription& operator=( VkVertexInputAttributeDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(VertexInputAttributeDescription) ); + return *this; + } + + VertexInputAttributeDescription& setLocation( uint32_t location_ ) + { + location = location_; + return *this; + } + + VertexInputAttributeDescription& setBinding( uint32_t binding_ ) + { + binding = binding_; + return *this; + } + + VertexInputAttributeDescription& setFormat( Format format_ ) + { + format = format_; + return *this; + } + + VertexInputAttributeDescription& setOffset( uint32_t offset_ ) + { + offset = offset_; + return *this; + } + + operator const VkVertexInputAttributeDescription&() const + { + return *reinterpret_cast(this); + } + + bool operator==( VertexInputAttributeDescription const& rhs ) const + { + return ( location == rhs.location ) + && ( binding == rhs.binding ) + && ( format == rhs.format ) + && ( offset == rhs.offset ); + } + + bool operator!=( VertexInputAttributeDescription const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t location; + uint32_t binding; + Format format; + uint32_t offset; + }; + static_assert( sizeof( VertexInputAttributeDescription ) == sizeof( VkVertexInputAttributeDescription ), "struct and wrapper have different size!" ); + + enum class StructureType + { + eApplicationInfo = VK_STRUCTURE_TYPE_APPLICATION_INFO, + eInstanceCreateInfo = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + eDeviceQueueCreateInfo = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + eDeviceCreateInfo = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + eSubmitInfo = VK_STRUCTURE_TYPE_SUBMIT_INFO, + eMemoryAllocateInfo = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + eMappedMemoryRange = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + eBindSparseInfo = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, + eFenceCreateInfo = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + eSemaphoreCreateInfo = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + eEventCreateInfo = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, + eQueryPoolCreateInfo = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, + eBufferCreateInfo = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + eBufferViewCreateInfo = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, + eImageCreateInfo = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + eImageViewCreateInfo = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + eShaderModuleCreateInfo = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + ePipelineCacheCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, + ePipelineShaderStageCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + ePipelineVertexInputStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + ePipelineInputAssemblyStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + ePipelineTessellationStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, + ePipelineViewportStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + ePipelineRasterizationStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + ePipelineMultisampleStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + ePipelineDepthStencilStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + ePipelineColorBlendStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + ePipelineDynamicStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + eGraphicsPipelineCreateInfo = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + eComputePipelineCreateInfo = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + ePipelineLayoutCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + eSamplerCreateInfo = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + eDescriptorSetLayoutCreateInfo = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + eDescriptorPoolCreateInfo = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + eDescriptorSetAllocateInfo = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + eWriteDescriptorSet = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + eCopyDescriptorSet = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, + eFramebufferCreateInfo = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + eRenderPassCreateInfo = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + eCommandPoolCreateInfo = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + eCommandBufferAllocateInfo = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + eCommandBufferInheritanceInfo = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + eCommandBufferBeginInfo = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + eRenderPassBeginInfo = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + eBufferMemoryBarrier = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + eImageMemoryBarrier = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + eMemoryBarrier = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + eLoaderInstanceCreateInfo = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO, + eLoaderDeviceCreateInfo = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, + eSwapchainCreateInfoKHR = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + ePresentInfoKHR = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + eDisplayModeCreateInfoKHR = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR, + eDisplaySurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR, + eDisplayPresentInfoKHR = VK_STRUCTURE_TYPE_DISPLAY_PRESENT_INFO_KHR, + eXlibSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + eXcbSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, + eWaylandSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, + eMirSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR, + eAndroidSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, + eWin32SurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + eDebugReportCallbackCreateInfoEXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + ePipelineRasterizationStateRasterizationOrderAMD = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD, + eDebugMarkerObjectNameInfoEXT = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT, + eDebugMarkerObjectTagInfoEXT = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT, + eDebugMarkerMarkerInfoEXT = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT, + eDedicatedAllocationImageCreateInfoNV = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV, + eDedicatedAllocationBufferCreateInfoNV = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV, + eDedicatedAllocationMemoryAllocateInfoNV = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV, + eExternalMemoryImageCreateInfoNV = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV, + eExportMemoryAllocateInfoNV = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV, + eImportMemoryWin32HandleInfoNV = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV, + eExportMemoryWin32HandleInfoNV = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV, + eWin32KeyedMutexAcquireReleaseInfoNV = VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV, + ePhysicalDeviceFeatures2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, + ePhysicalDeviceProperties2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, + eFormatProperties2KHR = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR, + eImageFormatProperties2KHR = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR, + ePhysicalDeviceImageFormatInfo2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR, + eQueueFamilyProperties2KHR = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR, + ePhysicalDeviceMemoryProperties2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR, + eSparseImageFormatProperties2KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR, + ePhysicalDeviceSparseImageFormatInfo2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR, + eValidationFlagsEXT = VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT, + eViSurfaceCreateInfoNN = VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN, + eObjectTableCreateInfoNVX = VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX, + eIndirectCommandsLayoutCreateInfoNVX = VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX, + eCmdProcessCommandsInfoNVX = VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX, + eCmdReserveSpaceForCommandsInfoNVX = VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX, + eDeviceGeneratedCommandsLimitsNVX = VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX, + eDeviceGeneratedCommandsFeaturesNVX = VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX, + eSurfaceCapabilities2EXT = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT, + eDisplayPowerInfoEXT = VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT, + eDeviceEventInfoEXT = VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT, + eDisplayEventInfoEXT = VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT, + eSwapchainCounterCreateInfoEXT = VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT + }; + + struct ApplicationInfo + { + ApplicationInfo( const char* pApplicationName_ = nullptr, uint32_t applicationVersion_ = 0, const char* pEngineName_ = nullptr, uint32_t engineVersion_ = 0, uint32_t apiVersion_ = 0 ) + : sType( StructureType::eApplicationInfo ) + , pNext( nullptr ) + , pApplicationName( pApplicationName_ ) + , applicationVersion( applicationVersion_ ) + , pEngineName( pEngineName_ ) + , engineVersion( engineVersion_ ) + , apiVersion( apiVersion_ ) + { + } + + ApplicationInfo( VkApplicationInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ApplicationInfo) ); + } + + ApplicationInfo& operator=( VkApplicationInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ApplicationInfo) ); + return *this; + } + + ApplicationInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ApplicationInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ApplicationInfo& setPApplicationName( const char* pApplicationName_ ) + { + pApplicationName = pApplicationName_; + return *this; + } + + ApplicationInfo& setApplicationVersion( uint32_t applicationVersion_ ) + { + applicationVersion = applicationVersion_; + return *this; + } + + ApplicationInfo& setPEngineName( const char* pEngineName_ ) + { + pEngineName = pEngineName_; + return *this; + } + + ApplicationInfo& setEngineVersion( uint32_t engineVersion_ ) + { + engineVersion = engineVersion_; + return *this; + } + + ApplicationInfo& setApiVersion( uint32_t apiVersion_ ) + { + apiVersion = apiVersion_; + return *this; + } + + operator const VkApplicationInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ApplicationInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( pApplicationName == rhs.pApplicationName ) + && ( applicationVersion == rhs.applicationVersion ) + && ( pEngineName == rhs.pEngineName ) + && ( engineVersion == rhs.engineVersion ) + && ( apiVersion == rhs.apiVersion ); + } + + bool operator!=( ApplicationInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + const char* pApplicationName; + uint32_t applicationVersion; + const char* pEngineName; + uint32_t engineVersion; + uint32_t apiVersion; + }; + static_assert( sizeof( ApplicationInfo ) == sizeof( VkApplicationInfo ), "struct and wrapper have different size!" ); + + struct DeviceQueueCreateInfo + { + DeviceQueueCreateInfo( DeviceQueueCreateFlags flags_ = DeviceQueueCreateFlags(), uint32_t queueFamilyIndex_ = 0, uint32_t queueCount_ = 0, const float* pQueuePriorities_ = nullptr ) + : sType( StructureType::eDeviceQueueCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , queueFamilyIndex( queueFamilyIndex_ ) + , queueCount( queueCount_ ) + , pQueuePriorities( pQueuePriorities_ ) + { + } + + DeviceQueueCreateInfo( VkDeviceQueueCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceQueueCreateInfo) ); + } + + DeviceQueueCreateInfo& operator=( VkDeviceQueueCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceQueueCreateInfo) ); + return *this; + } + + DeviceQueueCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DeviceQueueCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DeviceQueueCreateInfo& setFlags( DeviceQueueCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + DeviceQueueCreateInfo& setQueueFamilyIndex( uint32_t queueFamilyIndex_ ) + { + queueFamilyIndex = queueFamilyIndex_; + return *this; + } + + DeviceQueueCreateInfo& setQueueCount( uint32_t queueCount_ ) + { + queueCount = queueCount_; + return *this; + } + + DeviceQueueCreateInfo& setPQueuePriorities( const float* pQueuePriorities_ ) + { + pQueuePriorities = pQueuePriorities_; + return *this; + } + + operator const VkDeviceQueueCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DeviceQueueCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( queueFamilyIndex == rhs.queueFamilyIndex ) + && ( queueCount == rhs.queueCount ) + && ( pQueuePriorities == rhs.pQueuePriorities ); + } + + bool operator!=( DeviceQueueCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DeviceQueueCreateFlags flags; + uint32_t queueFamilyIndex; + uint32_t queueCount; + const float* pQueuePriorities; + }; + static_assert( sizeof( DeviceQueueCreateInfo ) == sizeof( VkDeviceQueueCreateInfo ), "struct and wrapper have different size!" ); + + struct DeviceCreateInfo + { + DeviceCreateInfo( DeviceCreateFlags flags_ = DeviceCreateFlags(), uint32_t queueCreateInfoCount_ = 0, const DeviceQueueCreateInfo* pQueueCreateInfos_ = nullptr, uint32_t enabledLayerCount_ = 0, const char* const* ppEnabledLayerNames_ = nullptr, uint32_t enabledExtensionCount_ = 0, const char* const* ppEnabledExtensionNames_ = nullptr, const PhysicalDeviceFeatures* pEnabledFeatures_ = nullptr ) + : sType( StructureType::eDeviceCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , queueCreateInfoCount( queueCreateInfoCount_ ) + , pQueueCreateInfos( pQueueCreateInfos_ ) + , enabledLayerCount( enabledLayerCount_ ) + , ppEnabledLayerNames( ppEnabledLayerNames_ ) + , enabledExtensionCount( enabledExtensionCount_ ) + , ppEnabledExtensionNames( ppEnabledExtensionNames_ ) + , pEnabledFeatures( pEnabledFeatures_ ) + { + } + + DeviceCreateInfo( VkDeviceCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceCreateInfo) ); + } + + DeviceCreateInfo& operator=( VkDeviceCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceCreateInfo) ); + return *this; + } + + DeviceCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DeviceCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DeviceCreateInfo& setFlags( DeviceCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + DeviceCreateInfo& setQueueCreateInfoCount( uint32_t queueCreateInfoCount_ ) + { + queueCreateInfoCount = queueCreateInfoCount_; + return *this; + } + + DeviceCreateInfo& setPQueueCreateInfos( const DeviceQueueCreateInfo* pQueueCreateInfos_ ) + { + pQueueCreateInfos = pQueueCreateInfos_; + return *this; + } + + DeviceCreateInfo& setEnabledLayerCount( uint32_t enabledLayerCount_ ) + { + enabledLayerCount = enabledLayerCount_; + return *this; + } + + DeviceCreateInfo& setPpEnabledLayerNames( const char* const* ppEnabledLayerNames_ ) + { + ppEnabledLayerNames = ppEnabledLayerNames_; + return *this; + } + + DeviceCreateInfo& setEnabledExtensionCount( uint32_t enabledExtensionCount_ ) + { + enabledExtensionCount = enabledExtensionCount_; + return *this; + } + + DeviceCreateInfo& setPpEnabledExtensionNames( const char* const* ppEnabledExtensionNames_ ) + { + ppEnabledExtensionNames = ppEnabledExtensionNames_; + return *this; + } + + DeviceCreateInfo& setPEnabledFeatures( const PhysicalDeviceFeatures* pEnabledFeatures_ ) + { + pEnabledFeatures = pEnabledFeatures_; + return *this; + } + + operator const VkDeviceCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DeviceCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( queueCreateInfoCount == rhs.queueCreateInfoCount ) + && ( pQueueCreateInfos == rhs.pQueueCreateInfos ) + && ( enabledLayerCount == rhs.enabledLayerCount ) + && ( ppEnabledLayerNames == rhs.ppEnabledLayerNames ) + && ( enabledExtensionCount == rhs.enabledExtensionCount ) + && ( ppEnabledExtensionNames == rhs.ppEnabledExtensionNames ) + && ( pEnabledFeatures == rhs.pEnabledFeatures ); + } + + bool operator!=( DeviceCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DeviceCreateFlags flags; + uint32_t queueCreateInfoCount; + const DeviceQueueCreateInfo* pQueueCreateInfos; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; + const PhysicalDeviceFeatures* pEnabledFeatures; + }; + static_assert( sizeof( DeviceCreateInfo ) == sizeof( VkDeviceCreateInfo ), "struct and wrapper have different size!" ); + + struct InstanceCreateInfo + { + InstanceCreateInfo( InstanceCreateFlags flags_ = InstanceCreateFlags(), const ApplicationInfo* pApplicationInfo_ = nullptr, uint32_t enabledLayerCount_ = 0, const char* const* ppEnabledLayerNames_ = nullptr, uint32_t enabledExtensionCount_ = 0, const char* const* ppEnabledExtensionNames_ = nullptr ) + : sType( StructureType::eInstanceCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , pApplicationInfo( pApplicationInfo_ ) + , enabledLayerCount( enabledLayerCount_ ) + , ppEnabledLayerNames( ppEnabledLayerNames_ ) + , enabledExtensionCount( enabledExtensionCount_ ) + , ppEnabledExtensionNames( ppEnabledExtensionNames_ ) + { + } + + InstanceCreateInfo( VkInstanceCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(InstanceCreateInfo) ); + } + + InstanceCreateInfo& operator=( VkInstanceCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(InstanceCreateInfo) ); + return *this; + } + + InstanceCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + InstanceCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + InstanceCreateInfo& setFlags( InstanceCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + InstanceCreateInfo& setPApplicationInfo( const ApplicationInfo* pApplicationInfo_ ) + { + pApplicationInfo = pApplicationInfo_; + return *this; + } + + InstanceCreateInfo& setEnabledLayerCount( uint32_t enabledLayerCount_ ) + { + enabledLayerCount = enabledLayerCount_; + return *this; + } + + InstanceCreateInfo& setPpEnabledLayerNames( const char* const* ppEnabledLayerNames_ ) + { + ppEnabledLayerNames = ppEnabledLayerNames_; + return *this; + } + + InstanceCreateInfo& setEnabledExtensionCount( uint32_t enabledExtensionCount_ ) + { + enabledExtensionCount = enabledExtensionCount_; + return *this; + } + + InstanceCreateInfo& setPpEnabledExtensionNames( const char* const* ppEnabledExtensionNames_ ) + { + ppEnabledExtensionNames = ppEnabledExtensionNames_; + return *this; + } + + operator const VkInstanceCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( InstanceCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( pApplicationInfo == rhs.pApplicationInfo ) + && ( enabledLayerCount == rhs.enabledLayerCount ) + && ( ppEnabledLayerNames == rhs.ppEnabledLayerNames ) + && ( enabledExtensionCount == rhs.enabledExtensionCount ) + && ( ppEnabledExtensionNames == rhs.ppEnabledExtensionNames ); + } + + bool operator!=( InstanceCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + InstanceCreateFlags flags; + const ApplicationInfo* pApplicationInfo; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; + }; + static_assert( sizeof( InstanceCreateInfo ) == sizeof( VkInstanceCreateInfo ), "struct and wrapper have different size!" ); + + struct MemoryAllocateInfo + { + MemoryAllocateInfo( DeviceSize allocationSize_ = 0, uint32_t memoryTypeIndex_ = 0 ) + : sType( StructureType::eMemoryAllocateInfo ) + , pNext( nullptr ) + , allocationSize( allocationSize_ ) + , memoryTypeIndex( memoryTypeIndex_ ) + { + } + + MemoryAllocateInfo( VkMemoryAllocateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(MemoryAllocateInfo) ); + } + + MemoryAllocateInfo& operator=( VkMemoryAllocateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(MemoryAllocateInfo) ); + return *this; + } + + MemoryAllocateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + MemoryAllocateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + MemoryAllocateInfo& setAllocationSize( DeviceSize allocationSize_ ) + { + allocationSize = allocationSize_; + return *this; + } + + MemoryAllocateInfo& setMemoryTypeIndex( uint32_t memoryTypeIndex_ ) + { + memoryTypeIndex = memoryTypeIndex_; + return *this; + } + + operator const VkMemoryAllocateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( MemoryAllocateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( allocationSize == rhs.allocationSize ) + && ( memoryTypeIndex == rhs.memoryTypeIndex ); + } + + bool operator!=( MemoryAllocateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DeviceSize allocationSize; + uint32_t memoryTypeIndex; + }; + static_assert( sizeof( MemoryAllocateInfo ) == sizeof( VkMemoryAllocateInfo ), "struct and wrapper have different size!" ); + + struct MappedMemoryRange + { + MappedMemoryRange( DeviceMemory memory_ = DeviceMemory(), DeviceSize offset_ = 0, DeviceSize size_ = 0 ) + : sType( StructureType::eMappedMemoryRange ) + , pNext( nullptr ) + , memory( memory_ ) + , offset( offset_ ) + , size( size_ ) + { + } + + MappedMemoryRange( VkMappedMemoryRange const & rhs ) + { + memcpy( this, &rhs, sizeof(MappedMemoryRange) ); + } + + MappedMemoryRange& operator=( VkMappedMemoryRange const & rhs ) + { + memcpy( this, &rhs, sizeof(MappedMemoryRange) ); + return *this; + } + + MappedMemoryRange& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + MappedMemoryRange& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + MappedMemoryRange& setMemory( DeviceMemory memory_ ) + { + memory = memory_; + return *this; + } + + MappedMemoryRange& setOffset( DeviceSize offset_ ) + { + offset = offset_; + return *this; + } + + MappedMemoryRange& setSize( DeviceSize size_ ) + { + size = size_; + return *this; + } + + operator const VkMappedMemoryRange&() const + { + return *reinterpret_cast(this); + } + + bool operator==( MappedMemoryRange const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( memory == rhs.memory ) + && ( offset == rhs.offset ) + && ( size == rhs.size ); + } + + bool operator!=( MappedMemoryRange const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DeviceMemory memory; + DeviceSize offset; + DeviceSize size; + }; + static_assert( sizeof( MappedMemoryRange ) == sizeof( VkMappedMemoryRange ), "struct and wrapper have different size!" ); + + struct WriteDescriptorSet + { + WriteDescriptorSet( DescriptorSet dstSet_ = DescriptorSet(), uint32_t dstBinding_ = 0, uint32_t dstArrayElement_ = 0, uint32_t descriptorCount_ = 0, DescriptorType descriptorType_ = DescriptorType::eSampler, const DescriptorImageInfo* pImageInfo_ = nullptr, const DescriptorBufferInfo* pBufferInfo_ = nullptr, const BufferView* pTexelBufferView_ = nullptr ) + : sType( StructureType::eWriteDescriptorSet ) + , pNext( nullptr ) + , dstSet( dstSet_ ) + , dstBinding( dstBinding_ ) + , dstArrayElement( dstArrayElement_ ) + , descriptorCount( descriptorCount_ ) + , descriptorType( descriptorType_ ) + , pImageInfo( pImageInfo_ ) + , pBufferInfo( pBufferInfo_ ) + , pTexelBufferView( pTexelBufferView_ ) + { + } + + WriteDescriptorSet( VkWriteDescriptorSet const & rhs ) + { + memcpy( this, &rhs, sizeof(WriteDescriptorSet) ); + } + + WriteDescriptorSet& operator=( VkWriteDescriptorSet const & rhs ) + { + memcpy( this, &rhs, sizeof(WriteDescriptorSet) ); + return *this; + } + + WriteDescriptorSet& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + WriteDescriptorSet& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + WriteDescriptorSet& setDstSet( DescriptorSet dstSet_ ) + { + dstSet = dstSet_; + return *this; + } + + WriteDescriptorSet& setDstBinding( uint32_t dstBinding_ ) + { + dstBinding = dstBinding_; + return *this; + } + + WriteDescriptorSet& setDstArrayElement( uint32_t dstArrayElement_ ) + { + dstArrayElement = dstArrayElement_; + return *this; + } + + WriteDescriptorSet& setDescriptorCount( uint32_t descriptorCount_ ) + { + descriptorCount = descriptorCount_; + return *this; + } + + WriteDescriptorSet& setDescriptorType( DescriptorType descriptorType_ ) + { + descriptorType = descriptorType_; + return *this; + } + + WriteDescriptorSet& setPImageInfo( const DescriptorImageInfo* pImageInfo_ ) + { + pImageInfo = pImageInfo_; + return *this; + } + + WriteDescriptorSet& setPBufferInfo( const DescriptorBufferInfo* pBufferInfo_ ) + { + pBufferInfo = pBufferInfo_; + return *this; + } + + WriteDescriptorSet& setPTexelBufferView( const BufferView* pTexelBufferView_ ) + { + pTexelBufferView = pTexelBufferView_; + return *this; + } + + operator const VkWriteDescriptorSet&() const + { + return *reinterpret_cast(this); + } + + bool operator==( WriteDescriptorSet const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( dstSet == rhs.dstSet ) + && ( dstBinding == rhs.dstBinding ) + && ( dstArrayElement == rhs.dstArrayElement ) + && ( descriptorCount == rhs.descriptorCount ) + && ( descriptorType == rhs.descriptorType ) + && ( pImageInfo == rhs.pImageInfo ) + && ( pBufferInfo == rhs.pBufferInfo ) + && ( pTexelBufferView == rhs.pTexelBufferView ); + } + + bool operator!=( WriteDescriptorSet const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; + DescriptorType descriptorType; + const DescriptorImageInfo* pImageInfo; + const DescriptorBufferInfo* pBufferInfo; + const BufferView* pTexelBufferView; + }; + static_assert( sizeof( WriteDescriptorSet ) == sizeof( VkWriteDescriptorSet ), "struct and wrapper have different size!" ); + + struct CopyDescriptorSet + { + CopyDescriptorSet( DescriptorSet srcSet_ = DescriptorSet(), uint32_t srcBinding_ = 0, uint32_t srcArrayElement_ = 0, DescriptorSet dstSet_ = DescriptorSet(), uint32_t dstBinding_ = 0, uint32_t dstArrayElement_ = 0, uint32_t descriptorCount_ = 0 ) + : sType( StructureType::eCopyDescriptorSet ) + , pNext( nullptr ) + , srcSet( srcSet_ ) + , srcBinding( srcBinding_ ) + , srcArrayElement( srcArrayElement_ ) + , dstSet( dstSet_ ) + , dstBinding( dstBinding_ ) + , dstArrayElement( dstArrayElement_ ) + , descriptorCount( descriptorCount_ ) + { + } + + CopyDescriptorSet( VkCopyDescriptorSet const & rhs ) + { + memcpy( this, &rhs, sizeof(CopyDescriptorSet) ); + } + + CopyDescriptorSet& operator=( VkCopyDescriptorSet const & rhs ) + { + memcpy( this, &rhs, sizeof(CopyDescriptorSet) ); + return *this; + } + + CopyDescriptorSet& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + CopyDescriptorSet& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + CopyDescriptorSet& setSrcSet( DescriptorSet srcSet_ ) + { + srcSet = srcSet_; + return *this; + } + + CopyDescriptorSet& setSrcBinding( uint32_t srcBinding_ ) + { + srcBinding = srcBinding_; + return *this; + } + + CopyDescriptorSet& setSrcArrayElement( uint32_t srcArrayElement_ ) + { + srcArrayElement = srcArrayElement_; + return *this; + } + + CopyDescriptorSet& setDstSet( DescriptorSet dstSet_ ) + { + dstSet = dstSet_; + return *this; + } + + CopyDescriptorSet& setDstBinding( uint32_t dstBinding_ ) + { + dstBinding = dstBinding_; + return *this; + } + + CopyDescriptorSet& setDstArrayElement( uint32_t dstArrayElement_ ) + { + dstArrayElement = dstArrayElement_; + return *this; + } + + CopyDescriptorSet& setDescriptorCount( uint32_t descriptorCount_ ) + { + descriptorCount = descriptorCount_; + return *this; + } + + operator const VkCopyDescriptorSet&() const + { + return *reinterpret_cast(this); + } + + bool operator==( CopyDescriptorSet const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( srcSet == rhs.srcSet ) + && ( srcBinding == rhs.srcBinding ) + && ( srcArrayElement == rhs.srcArrayElement ) + && ( dstSet == rhs.dstSet ) + && ( dstBinding == rhs.dstBinding ) + && ( dstArrayElement == rhs.dstArrayElement ) + && ( descriptorCount == rhs.descriptorCount ); + } + + bool operator!=( CopyDescriptorSet const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DescriptorSet srcSet; + uint32_t srcBinding; + uint32_t srcArrayElement; + DescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; + }; + static_assert( sizeof( CopyDescriptorSet ) == sizeof( VkCopyDescriptorSet ), "struct and wrapper have different size!" ); + + struct BufferViewCreateInfo + { + BufferViewCreateInfo( BufferViewCreateFlags flags_ = BufferViewCreateFlags(), Buffer buffer_ = Buffer(), Format format_ = Format::eUndefined, DeviceSize offset_ = 0, DeviceSize range_ = 0 ) + : sType( StructureType::eBufferViewCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , buffer( buffer_ ) + , format( format_ ) + , offset( offset_ ) + , range( range_ ) + { + } + + BufferViewCreateInfo( VkBufferViewCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferViewCreateInfo) ); + } + + BufferViewCreateInfo& operator=( VkBufferViewCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferViewCreateInfo) ); + return *this; + } + + BufferViewCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + BufferViewCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + BufferViewCreateInfo& setFlags( BufferViewCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + BufferViewCreateInfo& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + BufferViewCreateInfo& setFormat( Format format_ ) + { + format = format_; + return *this; + } + + BufferViewCreateInfo& setOffset( DeviceSize offset_ ) + { + offset = offset_; + return *this; + } + + BufferViewCreateInfo& setRange( DeviceSize range_ ) + { + range = range_; + return *this; + } + + operator const VkBufferViewCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( BufferViewCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( buffer == rhs.buffer ) + && ( format == rhs.format ) + && ( offset == rhs.offset ) + && ( range == rhs.range ); + } + + bool operator!=( BufferViewCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + BufferViewCreateFlags flags; + Buffer buffer; + Format format; + DeviceSize offset; + DeviceSize range; + }; + static_assert( sizeof( BufferViewCreateInfo ) == sizeof( VkBufferViewCreateInfo ), "struct and wrapper have different size!" ); + + struct ShaderModuleCreateInfo + { + ShaderModuleCreateInfo( ShaderModuleCreateFlags flags_ = ShaderModuleCreateFlags(), size_t codeSize_ = 0, const uint32_t* pCode_ = nullptr ) + : sType( StructureType::eShaderModuleCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , codeSize( codeSize_ ) + , pCode( pCode_ ) + { + } + + ShaderModuleCreateInfo( VkShaderModuleCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ShaderModuleCreateInfo) ); + } + + ShaderModuleCreateInfo& operator=( VkShaderModuleCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ShaderModuleCreateInfo) ); + return *this; + } + + ShaderModuleCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ShaderModuleCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ShaderModuleCreateInfo& setFlags( ShaderModuleCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + ShaderModuleCreateInfo& setCodeSize( size_t codeSize_ ) + { + codeSize = codeSize_; + return *this; + } + + ShaderModuleCreateInfo& setPCode( const uint32_t* pCode_ ) + { + pCode = pCode_; + return *this; + } + + operator const VkShaderModuleCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ShaderModuleCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( codeSize == rhs.codeSize ) + && ( pCode == rhs.pCode ); + } + + bool operator!=( ShaderModuleCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ShaderModuleCreateFlags flags; + size_t codeSize; + const uint32_t* pCode; + }; + static_assert( sizeof( ShaderModuleCreateInfo ) == sizeof( VkShaderModuleCreateInfo ), "struct and wrapper have different size!" ); + + struct DescriptorSetAllocateInfo + { + DescriptorSetAllocateInfo( DescriptorPool descriptorPool_ = DescriptorPool(), uint32_t descriptorSetCount_ = 0, const DescriptorSetLayout* pSetLayouts_ = nullptr ) + : sType( StructureType::eDescriptorSetAllocateInfo ) + , pNext( nullptr ) + , descriptorPool( descriptorPool_ ) + , descriptorSetCount( descriptorSetCount_ ) + , pSetLayouts( pSetLayouts_ ) + { + } + + DescriptorSetAllocateInfo( VkDescriptorSetAllocateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorSetAllocateInfo) ); + } + + DescriptorSetAllocateInfo& operator=( VkDescriptorSetAllocateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorSetAllocateInfo) ); + return *this; + } + + DescriptorSetAllocateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DescriptorSetAllocateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DescriptorSetAllocateInfo& setDescriptorPool( DescriptorPool descriptorPool_ ) + { + descriptorPool = descriptorPool_; + return *this; + } + + DescriptorSetAllocateInfo& setDescriptorSetCount( uint32_t descriptorSetCount_ ) + { + descriptorSetCount = descriptorSetCount_; + return *this; + } + + DescriptorSetAllocateInfo& setPSetLayouts( const DescriptorSetLayout* pSetLayouts_ ) + { + pSetLayouts = pSetLayouts_; + return *this; + } + + operator const VkDescriptorSetAllocateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DescriptorSetAllocateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( descriptorPool == rhs.descriptorPool ) + && ( descriptorSetCount == rhs.descriptorSetCount ) + && ( pSetLayouts == rhs.pSetLayouts ); + } + + bool operator!=( DescriptorSetAllocateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DescriptorPool descriptorPool; + uint32_t descriptorSetCount; + const DescriptorSetLayout* pSetLayouts; + }; + static_assert( sizeof( DescriptorSetAllocateInfo ) == sizeof( VkDescriptorSetAllocateInfo ), "struct and wrapper have different size!" ); + + struct PipelineVertexInputStateCreateInfo + { + PipelineVertexInputStateCreateInfo( PipelineVertexInputStateCreateFlags flags_ = PipelineVertexInputStateCreateFlags(), uint32_t vertexBindingDescriptionCount_ = 0, const VertexInputBindingDescription* pVertexBindingDescriptions_ = nullptr, uint32_t vertexAttributeDescriptionCount_ = 0, const VertexInputAttributeDescription* pVertexAttributeDescriptions_ = nullptr ) + : sType( StructureType::ePipelineVertexInputStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , vertexBindingDescriptionCount( vertexBindingDescriptionCount_ ) + , pVertexBindingDescriptions( pVertexBindingDescriptions_ ) + , vertexAttributeDescriptionCount( vertexAttributeDescriptionCount_ ) + , pVertexAttributeDescriptions( pVertexAttributeDescriptions_ ) + { + } + + PipelineVertexInputStateCreateInfo( VkPipelineVertexInputStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineVertexInputStateCreateInfo) ); + } + + PipelineVertexInputStateCreateInfo& operator=( VkPipelineVertexInputStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineVertexInputStateCreateInfo) ); + return *this; + } + + PipelineVertexInputStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineVertexInputStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineVertexInputStateCreateInfo& setFlags( PipelineVertexInputStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineVertexInputStateCreateInfo& setVertexBindingDescriptionCount( uint32_t vertexBindingDescriptionCount_ ) + { + vertexBindingDescriptionCount = vertexBindingDescriptionCount_; + return *this; + } + + PipelineVertexInputStateCreateInfo& setPVertexBindingDescriptions( const VertexInputBindingDescription* pVertexBindingDescriptions_ ) + { + pVertexBindingDescriptions = pVertexBindingDescriptions_; + return *this; + } + + PipelineVertexInputStateCreateInfo& setVertexAttributeDescriptionCount( uint32_t vertexAttributeDescriptionCount_ ) + { + vertexAttributeDescriptionCount = vertexAttributeDescriptionCount_; + return *this; + } + + PipelineVertexInputStateCreateInfo& setPVertexAttributeDescriptions( const VertexInputAttributeDescription* pVertexAttributeDescriptions_ ) + { + pVertexAttributeDescriptions = pVertexAttributeDescriptions_; + return *this; + } + + operator const VkPipelineVertexInputStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineVertexInputStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( vertexBindingDescriptionCount == rhs.vertexBindingDescriptionCount ) + && ( pVertexBindingDescriptions == rhs.pVertexBindingDescriptions ) + && ( vertexAttributeDescriptionCount == rhs.vertexAttributeDescriptionCount ) + && ( pVertexAttributeDescriptions == rhs.pVertexAttributeDescriptions ); + } + + bool operator!=( PipelineVertexInputStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineVertexInputStateCreateFlags flags; + uint32_t vertexBindingDescriptionCount; + const VertexInputBindingDescription* pVertexBindingDescriptions; + uint32_t vertexAttributeDescriptionCount; + const VertexInputAttributeDescription* pVertexAttributeDescriptions; + }; + static_assert( sizeof( PipelineVertexInputStateCreateInfo ) == sizeof( VkPipelineVertexInputStateCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineInputAssemblyStateCreateInfo + { + PipelineInputAssemblyStateCreateInfo( PipelineInputAssemblyStateCreateFlags flags_ = PipelineInputAssemblyStateCreateFlags(), PrimitiveTopology topology_ = PrimitiveTopology::ePointList, Bool32 primitiveRestartEnable_ = 0 ) + : sType( StructureType::ePipelineInputAssemblyStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , topology( topology_ ) + , primitiveRestartEnable( primitiveRestartEnable_ ) + { + } + + PipelineInputAssemblyStateCreateInfo( VkPipelineInputAssemblyStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineInputAssemblyStateCreateInfo) ); + } + + PipelineInputAssemblyStateCreateInfo& operator=( VkPipelineInputAssemblyStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineInputAssemblyStateCreateInfo) ); + return *this; + } + + PipelineInputAssemblyStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineInputAssemblyStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineInputAssemblyStateCreateInfo& setFlags( PipelineInputAssemblyStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineInputAssemblyStateCreateInfo& setTopology( PrimitiveTopology topology_ ) + { + topology = topology_; + return *this; + } + + PipelineInputAssemblyStateCreateInfo& setPrimitiveRestartEnable( Bool32 primitiveRestartEnable_ ) + { + primitiveRestartEnable = primitiveRestartEnable_; + return *this; + } + + operator const VkPipelineInputAssemblyStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineInputAssemblyStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( topology == rhs.topology ) + && ( primitiveRestartEnable == rhs.primitiveRestartEnable ); + } + + bool operator!=( PipelineInputAssemblyStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineInputAssemblyStateCreateFlags flags; + PrimitiveTopology topology; + Bool32 primitiveRestartEnable; + }; + static_assert( sizeof( PipelineInputAssemblyStateCreateInfo ) == sizeof( VkPipelineInputAssemblyStateCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineTessellationStateCreateInfo + { + PipelineTessellationStateCreateInfo( PipelineTessellationStateCreateFlags flags_ = PipelineTessellationStateCreateFlags(), uint32_t patchControlPoints_ = 0 ) + : sType( StructureType::ePipelineTessellationStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , patchControlPoints( patchControlPoints_ ) + { + } + + PipelineTessellationStateCreateInfo( VkPipelineTessellationStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineTessellationStateCreateInfo) ); + } + + PipelineTessellationStateCreateInfo& operator=( VkPipelineTessellationStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineTessellationStateCreateInfo) ); + return *this; + } + + PipelineTessellationStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineTessellationStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineTessellationStateCreateInfo& setFlags( PipelineTessellationStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineTessellationStateCreateInfo& setPatchControlPoints( uint32_t patchControlPoints_ ) + { + patchControlPoints = patchControlPoints_; + return *this; + } + + operator const VkPipelineTessellationStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineTessellationStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( patchControlPoints == rhs.patchControlPoints ); + } + + bool operator!=( PipelineTessellationStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineTessellationStateCreateFlags flags; + uint32_t patchControlPoints; + }; + static_assert( sizeof( PipelineTessellationStateCreateInfo ) == sizeof( VkPipelineTessellationStateCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineViewportStateCreateInfo + { + PipelineViewportStateCreateInfo( PipelineViewportStateCreateFlags flags_ = PipelineViewportStateCreateFlags(), uint32_t viewportCount_ = 0, const Viewport* pViewports_ = nullptr, uint32_t scissorCount_ = 0, const Rect2D* pScissors_ = nullptr ) + : sType( StructureType::ePipelineViewportStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , viewportCount( viewportCount_ ) + , pViewports( pViewports_ ) + , scissorCount( scissorCount_ ) + , pScissors( pScissors_ ) + { + } + + PipelineViewportStateCreateInfo( VkPipelineViewportStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineViewportStateCreateInfo) ); + } + + PipelineViewportStateCreateInfo& operator=( VkPipelineViewportStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineViewportStateCreateInfo) ); + return *this; + } + + PipelineViewportStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineViewportStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineViewportStateCreateInfo& setFlags( PipelineViewportStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineViewportStateCreateInfo& setViewportCount( uint32_t viewportCount_ ) + { + viewportCount = viewportCount_; + return *this; + } + + PipelineViewportStateCreateInfo& setPViewports( const Viewport* pViewports_ ) + { + pViewports = pViewports_; + return *this; + } + + PipelineViewportStateCreateInfo& setScissorCount( uint32_t scissorCount_ ) + { + scissorCount = scissorCount_; + return *this; + } + + PipelineViewportStateCreateInfo& setPScissors( const Rect2D* pScissors_ ) + { + pScissors = pScissors_; + return *this; + } + + operator const VkPipelineViewportStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineViewportStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( viewportCount == rhs.viewportCount ) + && ( pViewports == rhs.pViewports ) + && ( scissorCount == rhs.scissorCount ) + && ( pScissors == rhs.pScissors ); + } + + bool operator!=( PipelineViewportStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineViewportStateCreateFlags flags; + uint32_t viewportCount; + const Viewport* pViewports; + uint32_t scissorCount; + const Rect2D* pScissors; + }; + static_assert( sizeof( PipelineViewportStateCreateInfo ) == sizeof( VkPipelineViewportStateCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineRasterizationStateCreateInfo + { + PipelineRasterizationStateCreateInfo( PipelineRasterizationStateCreateFlags flags_ = PipelineRasterizationStateCreateFlags(), Bool32 depthClampEnable_ = 0, Bool32 rasterizerDiscardEnable_ = 0, PolygonMode polygonMode_ = PolygonMode::eFill, CullModeFlags cullMode_ = CullModeFlags(), FrontFace frontFace_ = FrontFace::eCounterClockwise, Bool32 depthBiasEnable_ = 0, float depthBiasConstantFactor_ = 0, float depthBiasClamp_ = 0, float depthBiasSlopeFactor_ = 0, float lineWidth_ = 0 ) + : sType( StructureType::ePipelineRasterizationStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , depthClampEnable( depthClampEnable_ ) + , rasterizerDiscardEnable( rasterizerDiscardEnable_ ) + , polygonMode( polygonMode_ ) + , cullMode( cullMode_ ) + , frontFace( frontFace_ ) + , depthBiasEnable( depthBiasEnable_ ) + , depthBiasConstantFactor( depthBiasConstantFactor_ ) + , depthBiasClamp( depthBiasClamp_ ) + , depthBiasSlopeFactor( depthBiasSlopeFactor_ ) + , lineWidth( lineWidth_ ) + { + } + + PipelineRasterizationStateCreateInfo( VkPipelineRasterizationStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineRasterizationStateCreateInfo) ); + } + + PipelineRasterizationStateCreateInfo& operator=( VkPipelineRasterizationStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineRasterizationStateCreateInfo) ); + return *this; + } + + PipelineRasterizationStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setFlags( PipelineRasterizationStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setDepthClampEnable( Bool32 depthClampEnable_ ) + { + depthClampEnable = depthClampEnable_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setRasterizerDiscardEnable( Bool32 rasterizerDiscardEnable_ ) + { + rasterizerDiscardEnable = rasterizerDiscardEnable_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setPolygonMode( PolygonMode polygonMode_ ) + { + polygonMode = polygonMode_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setCullMode( CullModeFlags cullMode_ ) + { + cullMode = cullMode_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setFrontFace( FrontFace frontFace_ ) + { + frontFace = frontFace_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setDepthBiasEnable( Bool32 depthBiasEnable_ ) + { + depthBiasEnable = depthBiasEnable_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setDepthBiasConstantFactor( float depthBiasConstantFactor_ ) + { + depthBiasConstantFactor = depthBiasConstantFactor_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setDepthBiasClamp( float depthBiasClamp_ ) + { + depthBiasClamp = depthBiasClamp_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setDepthBiasSlopeFactor( float depthBiasSlopeFactor_ ) + { + depthBiasSlopeFactor = depthBiasSlopeFactor_; + return *this; + } + + PipelineRasterizationStateCreateInfo& setLineWidth( float lineWidth_ ) + { + lineWidth = lineWidth_; + return *this; + } + + operator const VkPipelineRasterizationStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineRasterizationStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( depthClampEnable == rhs.depthClampEnable ) + && ( rasterizerDiscardEnable == rhs.rasterizerDiscardEnable ) + && ( polygonMode == rhs.polygonMode ) + && ( cullMode == rhs.cullMode ) + && ( frontFace == rhs.frontFace ) + && ( depthBiasEnable == rhs.depthBiasEnable ) + && ( depthBiasConstantFactor == rhs.depthBiasConstantFactor ) + && ( depthBiasClamp == rhs.depthBiasClamp ) + && ( depthBiasSlopeFactor == rhs.depthBiasSlopeFactor ) + && ( lineWidth == rhs.lineWidth ); + } + + bool operator!=( PipelineRasterizationStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineRasterizationStateCreateFlags flags; + Bool32 depthClampEnable; + Bool32 rasterizerDiscardEnable; + PolygonMode polygonMode; + CullModeFlags cullMode; + FrontFace frontFace; + Bool32 depthBiasEnable; + float depthBiasConstantFactor; + float depthBiasClamp; + float depthBiasSlopeFactor; + float lineWidth; + }; + static_assert( sizeof( PipelineRasterizationStateCreateInfo ) == sizeof( VkPipelineRasterizationStateCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineDepthStencilStateCreateInfo + { + PipelineDepthStencilStateCreateInfo( PipelineDepthStencilStateCreateFlags flags_ = PipelineDepthStencilStateCreateFlags(), Bool32 depthTestEnable_ = 0, Bool32 depthWriteEnable_ = 0, CompareOp depthCompareOp_ = CompareOp::eNever, Bool32 depthBoundsTestEnable_ = 0, Bool32 stencilTestEnable_ = 0, StencilOpState front_ = StencilOpState(), StencilOpState back_ = StencilOpState(), float minDepthBounds_ = 0, float maxDepthBounds_ = 0 ) + : sType( StructureType::ePipelineDepthStencilStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , depthTestEnable( depthTestEnable_ ) + , depthWriteEnable( depthWriteEnable_ ) + , depthCompareOp( depthCompareOp_ ) + , depthBoundsTestEnable( depthBoundsTestEnable_ ) + , stencilTestEnable( stencilTestEnable_ ) + , front( front_ ) + , back( back_ ) + , minDepthBounds( minDepthBounds_ ) + , maxDepthBounds( maxDepthBounds_ ) + { + } + + PipelineDepthStencilStateCreateInfo( VkPipelineDepthStencilStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineDepthStencilStateCreateInfo) ); + } + + PipelineDepthStencilStateCreateInfo& operator=( VkPipelineDepthStencilStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineDepthStencilStateCreateInfo) ); + return *this; + } + + PipelineDepthStencilStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setFlags( PipelineDepthStencilStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setDepthTestEnable( Bool32 depthTestEnable_ ) + { + depthTestEnable = depthTestEnable_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setDepthWriteEnable( Bool32 depthWriteEnable_ ) + { + depthWriteEnable = depthWriteEnable_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setDepthCompareOp( CompareOp depthCompareOp_ ) + { + depthCompareOp = depthCompareOp_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setDepthBoundsTestEnable( Bool32 depthBoundsTestEnable_ ) + { + depthBoundsTestEnable = depthBoundsTestEnable_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setStencilTestEnable( Bool32 stencilTestEnable_ ) + { + stencilTestEnable = stencilTestEnable_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setFront( StencilOpState front_ ) + { + front = front_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setBack( StencilOpState back_ ) + { + back = back_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setMinDepthBounds( float minDepthBounds_ ) + { + minDepthBounds = minDepthBounds_; + return *this; + } + + PipelineDepthStencilStateCreateInfo& setMaxDepthBounds( float maxDepthBounds_ ) + { + maxDepthBounds = maxDepthBounds_; + return *this; + } + + operator const VkPipelineDepthStencilStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineDepthStencilStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( depthTestEnable == rhs.depthTestEnable ) + && ( depthWriteEnable == rhs.depthWriteEnable ) + && ( depthCompareOp == rhs.depthCompareOp ) + && ( depthBoundsTestEnable == rhs.depthBoundsTestEnable ) + && ( stencilTestEnable == rhs.stencilTestEnable ) + && ( front == rhs.front ) + && ( back == rhs.back ) + && ( minDepthBounds == rhs.minDepthBounds ) + && ( maxDepthBounds == rhs.maxDepthBounds ); + } + + bool operator!=( PipelineDepthStencilStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineDepthStencilStateCreateFlags flags; + Bool32 depthTestEnable; + Bool32 depthWriteEnable; + CompareOp depthCompareOp; + Bool32 depthBoundsTestEnable; + Bool32 stencilTestEnable; + StencilOpState front; + StencilOpState back; + float minDepthBounds; + float maxDepthBounds; + }; + static_assert( sizeof( PipelineDepthStencilStateCreateInfo ) == sizeof( VkPipelineDepthStencilStateCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineCacheCreateInfo + { + PipelineCacheCreateInfo( PipelineCacheCreateFlags flags_ = PipelineCacheCreateFlags(), size_t initialDataSize_ = 0, const void* pInitialData_ = nullptr ) + : sType( StructureType::ePipelineCacheCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , initialDataSize( initialDataSize_ ) + , pInitialData( pInitialData_ ) + { + } + + PipelineCacheCreateInfo( VkPipelineCacheCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineCacheCreateInfo) ); + } + + PipelineCacheCreateInfo& operator=( VkPipelineCacheCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineCacheCreateInfo) ); + return *this; + } + + PipelineCacheCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineCacheCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineCacheCreateInfo& setFlags( PipelineCacheCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineCacheCreateInfo& setInitialDataSize( size_t initialDataSize_ ) + { + initialDataSize = initialDataSize_; + return *this; + } + + PipelineCacheCreateInfo& setPInitialData( const void* pInitialData_ ) + { + pInitialData = pInitialData_; + return *this; + } + + operator const VkPipelineCacheCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineCacheCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( initialDataSize == rhs.initialDataSize ) + && ( pInitialData == rhs.pInitialData ); + } + + bool operator!=( PipelineCacheCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineCacheCreateFlags flags; + size_t initialDataSize; + const void* pInitialData; + }; + static_assert( sizeof( PipelineCacheCreateInfo ) == sizeof( VkPipelineCacheCreateInfo ), "struct and wrapper have different size!" ); + + struct SamplerCreateInfo + { + SamplerCreateInfo( SamplerCreateFlags flags_ = SamplerCreateFlags(), Filter magFilter_ = Filter::eNearest, Filter minFilter_ = Filter::eNearest, SamplerMipmapMode mipmapMode_ = SamplerMipmapMode::eNearest, SamplerAddressMode addressModeU_ = SamplerAddressMode::eRepeat, SamplerAddressMode addressModeV_ = SamplerAddressMode::eRepeat, SamplerAddressMode addressModeW_ = SamplerAddressMode::eRepeat, float mipLodBias_ = 0, Bool32 anisotropyEnable_ = 0, float maxAnisotropy_ = 0, Bool32 compareEnable_ = 0, CompareOp compareOp_ = CompareOp::eNever, float minLod_ = 0, float maxLod_ = 0, BorderColor borderColor_ = BorderColor::eFloatTransparentBlack, Bool32 unnormalizedCoordinates_ = 0 ) + : sType( StructureType::eSamplerCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , magFilter( magFilter_ ) + , minFilter( minFilter_ ) + , mipmapMode( mipmapMode_ ) + , addressModeU( addressModeU_ ) + , addressModeV( addressModeV_ ) + , addressModeW( addressModeW_ ) + , mipLodBias( mipLodBias_ ) + , anisotropyEnable( anisotropyEnable_ ) + , maxAnisotropy( maxAnisotropy_ ) + , compareEnable( compareEnable_ ) + , compareOp( compareOp_ ) + , minLod( minLod_ ) + , maxLod( maxLod_ ) + , borderColor( borderColor_ ) + , unnormalizedCoordinates( unnormalizedCoordinates_ ) + { + } + + SamplerCreateInfo( VkSamplerCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SamplerCreateInfo) ); + } + + SamplerCreateInfo& operator=( VkSamplerCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SamplerCreateInfo) ); + return *this; + } + + SamplerCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + SamplerCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + SamplerCreateInfo& setFlags( SamplerCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + SamplerCreateInfo& setMagFilter( Filter magFilter_ ) + { + magFilter = magFilter_; + return *this; + } + + SamplerCreateInfo& setMinFilter( Filter minFilter_ ) + { + minFilter = minFilter_; + return *this; + } + + SamplerCreateInfo& setMipmapMode( SamplerMipmapMode mipmapMode_ ) + { + mipmapMode = mipmapMode_; + return *this; + } + + SamplerCreateInfo& setAddressModeU( SamplerAddressMode addressModeU_ ) + { + addressModeU = addressModeU_; + return *this; + } + + SamplerCreateInfo& setAddressModeV( SamplerAddressMode addressModeV_ ) + { + addressModeV = addressModeV_; + return *this; + } + + SamplerCreateInfo& setAddressModeW( SamplerAddressMode addressModeW_ ) + { + addressModeW = addressModeW_; + return *this; + } + + SamplerCreateInfo& setMipLodBias( float mipLodBias_ ) + { + mipLodBias = mipLodBias_; + return *this; + } + + SamplerCreateInfo& setAnisotropyEnable( Bool32 anisotropyEnable_ ) + { + anisotropyEnable = anisotropyEnable_; + return *this; + } + + SamplerCreateInfo& setMaxAnisotropy( float maxAnisotropy_ ) + { + maxAnisotropy = maxAnisotropy_; + return *this; + } + + SamplerCreateInfo& setCompareEnable( Bool32 compareEnable_ ) + { + compareEnable = compareEnable_; + return *this; + } + + SamplerCreateInfo& setCompareOp( CompareOp compareOp_ ) + { + compareOp = compareOp_; + return *this; + } + + SamplerCreateInfo& setMinLod( float minLod_ ) + { + minLod = minLod_; + return *this; + } + + SamplerCreateInfo& setMaxLod( float maxLod_ ) + { + maxLod = maxLod_; + return *this; + } + + SamplerCreateInfo& setBorderColor( BorderColor borderColor_ ) + { + borderColor = borderColor_; + return *this; + } + + SamplerCreateInfo& setUnnormalizedCoordinates( Bool32 unnormalizedCoordinates_ ) + { + unnormalizedCoordinates = unnormalizedCoordinates_; + return *this; + } + + operator const VkSamplerCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SamplerCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( magFilter == rhs.magFilter ) + && ( minFilter == rhs.minFilter ) + && ( mipmapMode == rhs.mipmapMode ) + && ( addressModeU == rhs.addressModeU ) + && ( addressModeV == rhs.addressModeV ) + && ( addressModeW == rhs.addressModeW ) + && ( mipLodBias == rhs.mipLodBias ) + && ( anisotropyEnable == rhs.anisotropyEnable ) + && ( maxAnisotropy == rhs.maxAnisotropy ) + && ( compareEnable == rhs.compareEnable ) + && ( compareOp == rhs.compareOp ) + && ( minLod == rhs.minLod ) + && ( maxLod == rhs.maxLod ) + && ( borderColor == rhs.borderColor ) + && ( unnormalizedCoordinates == rhs.unnormalizedCoordinates ); + } + + bool operator!=( SamplerCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + SamplerCreateFlags flags; + Filter magFilter; + Filter minFilter; + SamplerMipmapMode mipmapMode; + SamplerAddressMode addressModeU; + SamplerAddressMode addressModeV; + SamplerAddressMode addressModeW; + float mipLodBias; + Bool32 anisotropyEnable; + float maxAnisotropy; + Bool32 compareEnable; + CompareOp compareOp; + float minLod; + float maxLod; + BorderColor borderColor; + Bool32 unnormalizedCoordinates; + }; + static_assert( sizeof( SamplerCreateInfo ) == sizeof( VkSamplerCreateInfo ), "struct and wrapper have different size!" ); + + struct CommandBufferAllocateInfo + { + CommandBufferAllocateInfo( CommandPool commandPool_ = CommandPool(), CommandBufferLevel level_ = CommandBufferLevel::ePrimary, uint32_t commandBufferCount_ = 0 ) + : sType( StructureType::eCommandBufferAllocateInfo ) + , pNext( nullptr ) + , commandPool( commandPool_ ) + , level( level_ ) + , commandBufferCount( commandBufferCount_ ) + { + } + + CommandBufferAllocateInfo( VkCommandBufferAllocateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandBufferAllocateInfo) ); + } + + CommandBufferAllocateInfo& operator=( VkCommandBufferAllocateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandBufferAllocateInfo) ); + return *this; + } + + CommandBufferAllocateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + CommandBufferAllocateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + CommandBufferAllocateInfo& setCommandPool( CommandPool commandPool_ ) + { + commandPool = commandPool_; + return *this; + } + + CommandBufferAllocateInfo& setLevel( CommandBufferLevel level_ ) + { + level = level_; + return *this; + } + + CommandBufferAllocateInfo& setCommandBufferCount( uint32_t commandBufferCount_ ) + { + commandBufferCount = commandBufferCount_; + return *this; + } + + operator const VkCommandBufferAllocateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( CommandBufferAllocateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( commandPool == rhs.commandPool ) + && ( level == rhs.level ) + && ( commandBufferCount == rhs.commandBufferCount ); + } + + bool operator!=( CommandBufferAllocateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + CommandPool commandPool; + CommandBufferLevel level; + uint32_t commandBufferCount; + }; + static_assert( sizeof( CommandBufferAllocateInfo ) == sizeof( VkCommandBufferAllocateInfo ), "struct and wrapper have different size!" ); + + struct RenderPassBeginInfo + { + RenderPassBeginInfo( RenderPass renderPass_ = RenderPass(), Framebuffer framebuffer_ = Framebuffer(), Rect2D renderArea_ = Rect2D(), uint32_t clearValueCount_ = 0, const ClearValue* pClearValues_ = nullptr ) + : sType( StructureType::eRenderPassBeginInfo ) + , pNext( nullptr ) + , renderPass( renderPass_ ) + , framebuffer( framebuffer_ ) + , renderArea( renderArea_ ) + , clearValueCount( clearValueCount_ ) + , pClearValues( pClearValues_ ) + { + } + + RenderPassBeginInfo( VkRenderPassBeginInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(RenderPassBeginInfo) ); + } + + RenderPassBeginInfo& operator=( VkRenderPassBeginInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(RenderPassBeginInfo) ); + return *this; + } + + RenderPassBeginInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + RenderPassBeginInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + RenderPassBeginInfo& setRenderPass( RenderPass renderPass_ ) + { + renderPass = renderPass_; + return *this; + } + + RenderPassBeginInfo& setFramebuffer( Framebuffer framebuffer_ ) + { + framebuffer = framebuffer_; + return *this; + } + + RenderPassBeginInfo& setRenderArea( Rect2D renderArea_ ) + { + renderArea = renderArea_; + return *this; + } + + RenderPassBeginInfo& setClearValueCount( uint32_t clearValueCount_ ) + { + clearValueCount = clearValueCount_; + return *this; + } + + RenderPassBeginInfo& setPClearValues( const ClearValue* pClearValues_ ) + { + pClearValues = pClearValues_; + return *this; + } + + operator const VkRenderPassBeginInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( RenderPassBeginInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( renderPass == rhs.renderPass ) + && ( framebuffer == rhs.framebuffer ) + && ( renderArea == rhs.renderArea ) + && ( clearValueCount == rhs.clearValueCount ) + && ( pClearValues == rhs.pClearValues ); + } + + bool operator!=( RenderPassBeginInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + RenderPass renderPass; + Framebuffer framebuffer; + Rect2D renderArea; + uint32_t clearValueCount; + const ClearValue* pClearValues; + }; + static_assert( sizeof( RenderPassBeginInfo ) == sizeof( VkRenderPassBeginInfo ), "struct and wrapper have different size!" ); + + struct EventCreateInfo + { + EventCreateInfo( EventCreateFlags flags_ = EventCreateFlags() ) + : sType( StructureType::eEventCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + { + } + + EventCreateInfo( VkEventCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(EventCreateInfo) ); + } + + EventCreateInfo& operator=( VkEventCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(EventCreateInfo) ); + return *this; + } + + EventCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + EventCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + EventCreateInfo& setFlags( EventCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + operator const VkEventCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( EventCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ); + } + + bool operator!=( EventCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + EventCreateFlags flags; + }; + static_assert( sizeof( EventCreateInfo ) == sizeof( VkEventCreateInfo ), "struct and wrapper have different size!" ); + + struct SemaphoreCreateInfo + { + SemaphoreCreateInfo( SemaphoreCreateFlags flags_ = SemaphoreCreateFlags() ) + : sType( StructureType::eSemaphoreCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + { + } + + SemaphoreCreateInfo( VkSemaphoreCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SemaphoreCreateInfo) ); + } + + SemaphoreCreateInfo& operator=( VkSemaphoreCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SemaphoreCreateInfo) ); + return *this; + } + + SemaphoreCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + SemaphoreCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + SemaphoreCreateInfo& setFlags( SemaphoreCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + operator const VkSemaphoreCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SemaphoreCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ); + } + + bool operator!=( SemaphoreCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + SemaphoreCreateFlags flags; + }; + static_assert( sizeof( SemaphoreCreateInfo ) == sizeof( VkSemaphoreCreateInfo ), "struct and wrapper have different size!" ); + + struct FramebufferCreateInfo + { + FramebufferCreateInfo( FramebufferCreateFlags flags_ = FramebufferCreateFlags(), RenderPass renderPass_ = RenderPass(), uint32_t attachmentCount_ = 0, const ImageView* pAttachments_ = nullptr, uint32_t width_ = 0, uint32_t height_ = 0, uint32_t layers_ = 0 ) + : sType( StructureType::eFramebufferCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , renderPass( renderPass_ ) + , attachmentCount( attachmentCount_ ) + , pAttachments( pAttachments_ ) + , width( width_ ) + , height( height_ ) + , layers( layers_ ) + { + } + + FramebufferCreateInfo( VkFramebufferCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(FramebufferCreateInfo) ); + } + + FramebufferCreateInfo& operator=( VkFramebufferCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(FramebufferCreateInfo) ); + return *this; + } + + FramebufferCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + FramebufferCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + FramebufferCreateInfo& setFlags( FramebufferCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + FramebufferCreateInfo& setRenderPass( RenderPass renderPass_ ) + { + renderPass = renderPass_; + return *this; + } + + FramebufferCreateInfo& setAttachmentCount( uint32_t attachmentCount_ ) + { + attachmentCount = attachmentCount_; + return *this; + } + + FramebufferCreateInfo& setPAttachments( const ImageView* pAttachments_ ) + { + pAttachments = pAttachments_; + return *this; + } + + FramebufferCreateInfo& setWidth( uint32_t width_ ) + { + width = width_; + return *this; + } + + FramebufferCreateInfo& setHeight( uint32_t height_ ) + { + height = height_; + return *this; + } + + FramebufferCreateInfo& setLayers( uint32_t layers_ ) + { + layers = layers_; + return *this; + } + + operator const VkFramebufferCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( FramebufferCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( renderPass == rhs.renderPass ) + && ( attachmentCount == rhs.attachmentCount ) + && ( pAttachments == rhs.pAttachments ) + && ( width == rhs.width ) + && ( height == rhs.height ) + && ( layers == rhs.layers ); + } + + bool operator!=( FramebufferCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + FramebufferCreateFlags flags; + RenderPass renderPass; + uint32_t attachmentCount; + const ImageView* pAttachments; + uint32_t width; + uint32_t height; + uint32_t layers; + }; + static_assert( sizeof( FramebufferCreateInfo ) == sizeof( VkFramebufferCreateInfo ), "struct and wrapper have different size!" ); + + struct DisplayModeCreateInfoKHR + { + DisplayModeCreateInfoKHR( DisplayModeCreateFlagsKHR flags_ = DisplayModeCreateFlagsKHR(), DisplayModeParametersKHR parameters_ = DisplayModeParametersKHR() ) + : sType( StructureType::eDisplayModeCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , parameters( parameters_ ) + { + } + + DisplayModeCreateInfoKHR( VkDisplayModeCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayModeCreateInfoKHR) ); + } + + DisplayModeCreateInfoKHR& operator=( VkDisplayModeCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayModeCreateInfoKHR) ); + return *this; + } + + DisplayModeCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DisplayModeCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DisplayModeCreateInfoKHR& setFlags( DisplayModeCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + DisplayModeCreateInfoKHR& setParameters( DisplayModeParametersKHR parameters_ ) + { + parameters = parameters_; + return *this; + } + + operator const VkDisplayModeCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayModeCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( parameters == rhs.parameters ); + } + + bool operator!=( DisplayModeCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DisplayModeCreateFlagsKHR flags; + DisplayModeParametersKHR parameters; + }; + static_assert( sizeof( DisplayModeCreateInfoKHR ) == sizeof( VkDisplayModeCreateInfoKHR ), "struct and wrapper have different size!" ); + + struct DisplayPresentInfoKHR + { + DisplayPresentInfoKHR( Rect2D srcRect_ = Rect2D(), Rect2D dstRect_ = Rect2D(), Bool32 persistent_ = 0 ) + : sType( StructureType::eDisplayPresentInfoKHR ) + , pNext( nullptr ) + , srcRect( srcRect_ ) + , dstRect( dstRect_ ) + , persistent( persistent_ ) + { + } + + DisplayPresentInfoKHR( VkDisplayPresentInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayPresentInfoKHR) ); + } + + DisplayPresentInfoKHR& operator=( VkDisplayPresentInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayPresentInfoKHR) ); + return *this; + } + + DisplayPresentInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DisplayPresentInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DisplayPresentInfoKHR& setSrcRect( Rect2D srcRect_ ) + { + srcRect = srcRect_; + return *this; + } + + DisplayPresentInfoKHR& setDstRect( Rect2D dstRect_ ) + { + dstRect = dstRect_; + return *this; + } + + DisplayPresentInfoKHR& setPersistent( Bool32 persistent_ ) + { + persistent = persistent_; + return *this; + } + + operator const VkDisplayPresentInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayPresentInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( srcRect == rhs.srcRect ) + && ( dstRect == rhs.dstRect ) + && ( persistent == rhs.persistent ); + } + + bool operator!=( DisplayPresentInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Rect2D srcRect; + Rect2D dstRect; + Bool32 persistent; + }; + static_assert( sizeof( DisplayPresentInfoKHR ) == sizeof( VkDisplayPresentInfoKHR ), "struct and wrapper have different size!" ); + +#ifdef VK_USE_PLATFORM_ANDROID_KHR + struct AndroidSurfaceCreateInfoKHR + { + AndroidSurfaceCreateInfoKHR( AndroidSurfaceCreateFlagsKHR flags_ = AndroidSurfaceCreateFlagsKHR(), ANativeWindow* window_ = nullptr ) + : sType( StructureType::eAndroidSurfaceCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , window( window_ ) + { + } + + AndroidSurfaceCreateInfoKHR( VkAndroidSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(AndroidSurfaceCreateInfoKHR) ); + } + + AndroidSurfaceCreateInfoKHR& operator=( VkAndroidSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(AndroidSurfaceCreateInfoKHR) ); + return *this; + } + + AndroidSurfaceCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + AndroidSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + AndroidSurfaceCreateInfoKHR& setFlags( AndroidSurfaceCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + AndroidSurfaceCreateInfoKHR& setWindow( ANativeWindow* window_ ) + { + window = window_; + return *this; + } + + operator const VkAndroidSurfaceCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( AndroidSurfaceCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( window == rhs.window ); + } + + bool operator!=( AndroidSurfaceCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + AndroidSurfaceCreateFlagsKHR flags; + ANativeWindow* window; + }; + static_assert( sizeof( AndroidSurfaceCreateInfoKHR ) == sizeof( VkAndroidSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + +#ifdef VK_USE_PLATFORM_MIR_KHR + struct MirSurfaceCreateInfoKHR + { + MirSurfaceCreateInfoKHR( MirSurfaceCreateFlagsKHR flags_ = MirSurfaceCreateFlagsKHR(), MirConnection* connection_ = nullptr, MirSurface* mirSurface_ = nullptr ) + : sType( StructureType::eMirSurfaceCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , connection( connection_ ) + , mirSurface( mirSurface_ ) + { + } + + MirSurfaceCreateInfoKHR( VkMirSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(MirSurfaceCreateInfoKHR) ); + } + + MirSurfaceCreateInfoKHR& operator=( VkMirSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(MirSurfaceCreateInfoKHR) ); + return *this; + } + + MirSurfaceCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + MirSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + MirSurfaceCreateInfoKHR& setFlags( MirSurfaceCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + MirSurfaceCreateInfoKHR& setConnection( MirConnection* connection_ ) + { + connection = connection_; + return *this; + } + + MirSurfaceCreateInfoKHR& setMirSurface( MirSurface* mirSurface_ ) + { + mirSurface = mirSurface_; + return *this; + } + + operator const VkMirSurfaceCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( MirSurfaceCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( connection == rhs.connection ) + && ( mirSurface == rhs.mirSurface ); + } + + bool operator!=( MirSurfaceCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + MirSurfaceCreateFlagsKHR flags; + MirConnection* connection; + MirSurface* mirSurface; + }; + static_assert( sizeof( MirSurfaceCreateInfoKHR ) == sizeof( VkMirSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_MIR_KHR*/ + +#ifdef VK_USE_PLATFORM_VI_NN + struct ViSurfaceCreateInfoNN + { + ViSurfaceCreateInfoNN( ViSurfaceCreateFlagsNN flags_ = ViSurfaceCreateFlagsNN(), void* window_ = nullptr ) + : sType( StructureType::eViSurfaceCreateInfoNN ) + , pNext( nullptr ) + , flags( flags_ ) + , window( window_ ) + { + } + + ViSurfaceCreateInfoNN( VkViSurfaceCreateInfoNN const & rhs ) + { + memcpy( this, &rhs, sizeof(ViSurfaceCreateInfoNN) ); + } + + ViSurfaceCreateInfoNN& operator=( VkViSurfaceCreateInfoNN const & rhs ) + { + memcpy( this, &rhs, sizeof(ViSurfaceCreateInfoNN) ); + return *this; + } + + ViSurfaceCreateInfoNN& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ViSurfaceCreateInfoNN& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ViSurfaceCreateInfoNN& setFlags( ViSurfaceCreateFlagsNN flags_ ) + { + flags = flags_; + return *this; + } + + ViSurfaceCreateInfoNN& setWindow( void* window_ ) + { + window = window_; + return *this; + } + + operator const VkViSurfaceCreateInfoNN&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ViSurfaceCreateInfoNN const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( window == rhs.window ); + } + + bool operator!=( ViSurfaceCreateInfoNN const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ViSurfaceCreateFlagsNN flags; + void* window; + }; + static_assert( sizeof( ViSurfaceCreateInfoNN ) == sizeof( VkViSurfaceCreateInfoNN ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_VI_NN*/ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + struct WaylandSurfaceCreateInfoKHR + { + WaylandSurfaceCreateInfoKHR( WaylandSurfaceCreateFlagsKHR flags_ = WaylandSurfaceCreateFlagsKHR(), struct wl_display* display_ = nullptr, struct wl_surface* surface_ = nullptr ) + : sType( StructureType::eWaylandSurfaceCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , display( display_ ) + , surface( surface_ ) + { + } + + WaylandSurfaceCreateInfoKHR( VkWaylandSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(WaylandSurfaceCreateInfoKHR) ); + } + + WaylandSurfaceCreateInfoKHR& operator=( VkWaylandSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(WaylandSurfaceCreateInfoKHR) ); + return *this; + } + + WaylandSurfaceCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + WaylandSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + WaylandSurfaceCreateInfoKHR& setFlags( WaylandSurfaceCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + WaylandSurfaceCreateInfoKHR& setDisplay( struct wl_display* display_ ) + { + display = display_; + return *this; + } + + WaylandSurfaceCreateInfoKHR& setSurface( struct wl_surface* surface_ ) + { + surface = surface_; + return *this; + } + + operator const VkWaylandSurfaceCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( WaylandSurfaceCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( display == rhs.display ) + && ( surface == rhs.surface ); + } + + bool operator!=( WaylandSurfaceCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + WaylandSurfaceCreateFlagsKHR flags; + struct wl_display* display; + struct wl_surface* surface; + }; + static_assert( sizeof( WaylandSurfaceCreateInfoKHR ) == sizeof( VkWaylandSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + struct Win32SurfaceCreateInfoKHR + { + Win32SurfaceCreateInfoKHR( Win32SurfaceCreateFlagsKHR flags_ = Win32SurfaceCreateFlagsKHR(), HINSTANCE hinstance_ = 0, HWND hwnd_ = 0 ) + : sType( StructureType::eWin32SurfaceCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , hinstance( hinstance_ ) + , hwnd( hwnd_ ) + { + } + + Win32SurfaceCreateInfoKHR( VkWin32SurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(Win32SurfaceCreateInfoKHR) ); + } + + Win32SurfaceCreateInfoKHR& operator=( VkWin32SurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(Win32SurfaceCreateInfoKHR) ); + return *this; + } + + Win32SurfaceCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + Win32SurfaceCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + Win32SurfaceCreateInfoKHR& setFlags( Win32SurfaceCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + Win32SurfaceCreateInfoKHR& setHinstance( HINSTANCE hinstance_ ) + { + hinstance = hinstance_; + return *this; + } + + Win32SurfaceCreateInfoKHR& setHwnd( HWND hwnd_ ) + { + hwnd = hwnd_; + return *this; + } + + operator const VkWin32SurfaceCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Win32SurfaceCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( hinstance == rhs.hinstance ) + && ( hwnd == rhs.hwnd ); + } + + bool operator!=( Win32SurfaceCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Win32SurfaceCreateFlagsKHR flags; + HINSTANCE hinstance; + HWND hwnd; + }; + static_assert( sizeof( Win32SurfaceCreateInfoKHR ) == sizeof( VkWin32SurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifdef VK_USE_PLATFORM_XLIB_KHR + struct XlibSurfaceCreateInfoKHR + { + XlibSurfaceCreateInfoKHR( XlibSurfaceCreateFlagsKHR flags_ = XlibSurfaceCreateFlagsKHR(), Display* dpy_ = nullptr, Window window_ = 0 ) + : sType( StructureType::eXlibSurfaceCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , dpy( dpy_ ) + , window( window_ ) + { + } + + XlibSurfaceCreateInfoKHR( VkXlibSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(XlibSurfaceCreateInfoKHR) ); + } + + XlibSurfaceCreateInfoKHR& operator=( VkXlibSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(XlibSurfaceCreateInfoKHR) ); + return *this; + } + + XlibSurfaceCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + XlibSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + XlibSurfaceCreateInfoKHR& setFlags( XlibSurfaceCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + XlibSurfaceCreateInfoKHR& setDpy( Display* dpy_ ) + { + dpy = dpy_; + return *this; + } + + XlibSurfaceCreateInfoKHR& setWindow( Window window_ ) + { + window = window_; + return *this; + } + + operator const VkXlibSurfaceCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( XlibSurfaceCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( dpy == rhs.dpy ) + && ( window == rhs.window ); + } + + bool operator!=( XlibSurfaceCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + XlibSurfaceCreateFlagsKHR flags; + Display* dpy; + Window window; + }; + static_assert( sizeof( XlibSurfaceCreateInfoKHR ) == sizeof( VkXlibSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ + +#ifdef VK_USE_PLATFORM_XCB_KHR + struct XcbSurfaceCreateInfoKHR + { + XcbSurfaceCreateInfoKHR( XcbSurfaceCreateFlagsKHR flags_ = XcbSurfaceCreateFlagsKHR(), xcb_connection_t* connection_ = nullptr, xcb_window_t window_ = 0 ) + : sType( StructureType::eXcbSurfaceCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , connection( connection_ ) + , window( window_ ) + { + } + + XcbSurfaceCreateInfoKHR( VkXcbSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(XcbSurfaceCreateInfoKHR) ); + } + + XcbSurfaceCreateInfoKHR& operator=( VkXcbSurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(XcbSurfaceCreateInfoKHR) ); + return *this; + } + + XcbSurfaceCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + XcbSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + XcbSurfaceCreateInfoKHR& setFlags( XcbSurfaceCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + XcbSurfaceCreateInfoKHR& setConnection( xcb_connection_t* connection_ ) + { + connection = connection_; + return *this; + } + + XcbSurfaceCreateInfoKHR& setWindow( xcb_window_t window_ ) + { + window = window_; + return *this; + } + + operator const VkXcbSurfaceCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( XcbSurfaceCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( connection == rhs.connection ) + && ( window == rhs.window ); + } + + bool operator!=( XcbSurfaceCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + XcbSurfaceCreateFlagsKHR flags; + xcb_connection_t* connection; + xcb_window_t window; + }; + static_assert( sizeof( XcbSurfaceCreateInfoKHR ) == sizeof( VkXcbSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_XCB_KHR*/ + + struct DebugMarkerMarkerInfoEXT + { + DebugMarkerMarkerInfoEXT( const char* pMarkerName_ = nullptr, std::array const& color_ = { { 0, 0, 0, 0 } } ) + : sType( StructureType::eDebugMarkerMarkerInfoEXT ) + , pNext( nullptr ) + , pMarkerName( pMarkerName_ ) + { + memcpy( &color, color_.data(), 4 * sizeof( float ) ); + } + + DebugMarkerMarkerInfoEXT( VkDebugMarkerMarkerInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugMarkerMarkerInfoEXT) ); + } + + DebugMarkerMarkerInfoEXT& operator=( VkDebugMarkerMarkerInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugMarkerMarkerInfoEXT) ); + return *this; + } + + DebugMarkerMarkerInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DebugMarkerMarkerInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DebugMarkerMarkerInfoEXT& setPMarkerName( const char* pMarkerName_ ) + { + pMarkerName = pMarkerName_; + return *this; + } + + DebugMarkerMarkerInfoEXT& setColor( std::array color_ ) + { + memcpy( &color, color_.data(), 4 * sizeof( float ) ); + return *this; + } + + operator const VkDebugMarkerMarkerInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DebugMarkerMarkerInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( pMarkerName == rhs.pMarkerName ) + && ( memcmp( color, rhs.color, 4 * sizeof( float ) ) == 0 ); + } + + bool operator!=( DebugMarkerMarkerInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + const char* pMarkerName; + float color[4]; + }; + static_assert( sizeof( DebugMarkerMarkerInfoEXT ) == sizeof( VkDebugMarkerMarkerInfoEXT ), "struct and wrapper have different size!" ); + + struct DedicatedAllocationImageCreateInfoNV + { + DedicatedAllocationImageCreateInfoNV( Bool32 dedicatedAllocation_ = 0 ) + : sType( StructureType::eDedicatedAllocationImageCreateInfoNV ) + , pNext( nullptr ) + , dedicatedAllocation( dedicatedAllocation_ ) + { + } + + DedicatedAllocationImageCreateInfoNV( VkDedicatedAllocationImageCreateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(DedicatedAllocationImageCreateInfoNV) ); + } + + DedicatedAllocationImageCreateInfoNV& operator=( VkDedicatedAllocationImageCreateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(DedicatedAllocationImageCreateInfoNV) ); + return *this; + } + + DedicatedAllocationImageCreateInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DedicatedAllocationImageCreateInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DedicatedAllocationImageCreateInfoNV& setDedicatedAllocation( Bool32 dedicatedAllocation_ ) + { + dedicatedAllocation = dedicatedAllocation_; + return *this; + } + + operator const VkDedicatedAllocationImageCreateInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DedicatedAllocationImageCreateInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( dedicatedAllocation == rhs.dedicatedAllocation ); + } + + bool operator!=( DedicatedAllocationImageCreateInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Bool32 dedicatedAllocation; + }; + static_assert( sizeof( DedicatedAllocationImageCreateInfoNV ) == sizeof( VkDedicatedAllocationImageCreateInfoNV ), "struct and wrapper have different size!" ); + + struct DedicatedAllocationBufferCreateInfoNV + { + DedicatedAllocationBufferCreateInfoNV( Bool32 dedicatedAllocation_ = 0 ) + : sType( StructureType::eDedicatedAllocationBufferCreateInfoNV ) + , pNext( nullptr ) + , dedicatedAllocation( dedicatedAllocation_ ) + { + } + + DedicatedAllocationBufferCreateInfoNV( VkDedicatedAllocationBufferCreateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(DedicatedAllocationBufferCreateInfoNV) ); + } + + DedicatedAllocationBufferCreateInfoNV& operator=( VkDedicatedAllocationBufferCreateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(DedicatedAllocationBufferCreateInfoNV) ); + return *this; + } + + DedicatedAllocationBufferCreateInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DedicatedAllocationBufferCreateInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DedicatedAllocationBufferCreateInfoNV& setDedicatedAllocation( Bool32 dedicatedAllocation_ ) + { + dedicatedAllocation = dedicatedAllocation_; + return *this; + } + + operator const VkDedicatedAllocationBufferCreateInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DedicatedAllocationBufferCreateInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( dedicatedAllocation == rhs.dedicatedAllocation ); + } + + bool operator!=( DedicatedAllocationBufferCreateInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Bool32 dedicatedAllocation; + }; + static_assert( sizeof( DedicatedAllocationBufferCreateInfoNV ) == sizeof( VkDedicatedAllocationBufferCreateInfoNV ), "struct and wrapper have different size!" ); + + struct DedicatedAllocationMemoryAllocateInfoNV + { + DedicatedAllocationMemoryAllocateInfoNV( Image image_ = Image(), Buffer buffer_ = Buffer() ) + : sType( StructureType::eDedicatedAllocationMemoryAllocateInfoNV ) + , pNext( nullptr ) + , image( image_ ) + , buffer( buffer_ ) + { + } + + DedicatedAllocationMemoryAllocateInfoNV( VkDedicatedAllocationMemoryAllocateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(DedicatedAllocationMemoryAllocateInfoNV) ); + } + + DedicatedAllocationMemoryAllocateInfoNV& operator=( VkDedicatedAllocationMemoryAllocateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(DedicatedAllocationMemoryAllocateInfoNV) ); + return *this; + } + + DedicatedAllocationMemoryAllocateInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DedicatedAllocationMemoryAllocateInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DedicatedAllocationMemoryAllocateInfoNV& setImage( Image image_ ) + { + image = image_; + return *this; + } + + DedicatedAllocationMemoryAllocateInfoNV& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + operator const VkDedicatedAllocationMemoryAllocateInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DedicatedAllocationMemoryAllocateInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( image == rhs.image ) + && ( buffer == rhs.buffer ); + } + + bool operator!=( DedicatedAllocationMemoryAllocateInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Image image; + Buffer buffer; + }; + static_assert( sizeof( DedicatedAllocationMemoryAllocateInfoNV ) == sizeof( VkDedicatedAllocationMemoryAllocateInfoNV ), "struct and wrapper have different size!" ); + +#ifdef VK_USE_PLATFORM_WIN32_KHR + struct ExportMemoryWin32HandleInfoNV + { + ExportMemoryWin32HandleInfoNV( const SECURITY_ATTRIBUTES* pAttributes_ = nullptr, DWORD dwAccess_ = 0 ) + : sType( StructureType::eExportMemoryWin32HandleInfoNV ) + , pNext( nullptr ) + , pAttributes( pAttributes_ ) + , dwAccess( dwAccess_ ) + { + } + + ExportMemoryWin32HandleInfoNV( VkExportMemoryWin32HandleInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ExportMemoryWin32HandleInfoNV) ); + } + + ExportMemoryWin32HandleInfoNV& operator=( VkExportMemoryWin32HandleInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ExportMemoryWin32HandleInfoNV) ); + return *this; + } + + ExportMemoryWin32HandleInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ExportMemoryWin32HandleInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ExportMemoryWin32HandleInfoNV& setPAttributes( const SECURITY_ATTRIBUTES* pAttributes_ ) + { + pAttributes = pAttributes_; + return *this; + } + + ExportMemoryWin32HandleInfoNV& setDwAccess( DWORD dwAccess_ ) + { + dwAccess = dwAccess_; + return *this; + } + + operator const VkExportMemoryWin32HandleInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ExportMemoryWin32HandleInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( pAttributes == rhs.pAttributes ) + && ( dwAccess == rhs.dwAccess ); + } + + bool operator!=( ExportMemoryWin32HandleInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + const SECURITY_ATTRIBUTES* pAttributes; + DWORD dwAccess; + }; + static_assert( sizeof( ExportMemoryWin32HandleInfoNV ) == sizeof( VkExportMemoryWin32HandleInfoNV ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + struct Win32KeyedMutexAcquireReleaseInfoNV + { + Win32KeyedMutexAcquireReleaseInfoNV( uint32_t acquireCount_ = 0, const DeviceMemory* pAcquireSyncs_ = nullptr, const uint64_t* pAcquireKeys_ = nullptr, const uint32_t* pAcquireTimeoutMilliseconds_ = nullptr, uint32_t releaseCount_ = 0, const DeviceMemory* pReleaseSyncs_ = nullptr, const uint64_t* pReleaseKeys_ = nullptr ) + : sType( StructureType::eWin32KeyedMutexAcquireReleaseInfoNV ) + , pNext( nullptr ) + , acquireCount( acquireCount_ ) + , pAcquireSyncs( pAcquireSyncs_ ) + , pAcquireKeys( pAcquireKeys_ ) + , pAcquireTimeoutMilliseconds( pAcquireTimeoutMilliseconds_ ) + , releaseCount( releaseCount_ ) + , pReleaseSyncs( pReleaseSyncs_ ) + , pReleaseKeys( pReleaseKeys_ ) + { + } + + Win32KeyedMutexAcquireReleaseInfoNV( VkWin32KeyedMutexAcquireReleaseInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(Win32KeyedMutexAcquireReleaseInfoNV) ); + } + + Win32KeyedMutexAcquireReleaseInfoNV& operator=( VkWin32KeyedMutexAcquireReleaseInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(Win32KeyedMutexAcquireReleaseInfoNV) ); + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setAcquireCount( uint32_t acquireCount_ ) + { + acquireCount = acquireCount_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setPAcquireSyncs( const DeviceMemory* pAcquireSyncs_ ) + { + pAcquireSyncs = pAcquireSyncs_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setPAcquireKeys( const uint64_t* pAcquireKeys_ ) + { + pAcquireKeys = pAcquireKeys_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setPAcquireTimeoutMilliseconds( const uint32_t* pAcquireTimeoutMilliseconds_ ) + { + pAcquireTimeoutMilliseconds = pAcquireTimeoutMilliseconds_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setReleaseCount( uint32_t releaseCount_ ) + { + releaseCount = releaseCount_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setPReleaseSyncs( const DeviceMemory* pReleaseSyncs_ ) + { + pReleaseSyncs = pReleaseSyncs_; + return *this; + } + + Win32KeyedMutexAcquireReleaseInfoNV& setPReleaseKeys( const uint64_t* pReleaseKeys_ ) + { + pReleaseKeys = pReleaseKeys_; + return *this; + } + + operator const VkWin32KeyedMutexAcquireReleaseInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( Win32KeyedMutexAcquireReleaseInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( acquireCount == rhs.acquireCount ) + && ( pAcquireSyncs == rhs.pAcquireSyncs ) + && ( pAcquireKeys == rhs.pAcquireKeys ) + && ( pAcquireTimeoutMilliseconds == rhs.pAcquireTimeoutMilliseconds ) + && ( releaseCount == rhs.releaseCount ) + && ( pReleaseSyncs == rhs.pReleaseSyncs ) + && ( pReleaseKeys == rhs.pReleaseKeys ); + } + + bool operator!=( Win32KeyedMutexAcquireReleaseInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + uint32_t acquireCount; + const DeviceMemory* pAcquireSyncs; + const uint64_t* pAcquireKeys; + const uint32_t* pAcquireTimeoutMilliseconds; + uint32_t releaseCount; + const DeviceMemory* pReleaseSyncs; + const uint64_t* pReleaseKeys; + }; + static_assert( sizeof( Win32KeyedMutexAcquireReleaseInfoNV ) == sizeof( VkWin32KeyedMutexAcquireReleaseInfoNV ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + + struct DeviceGeneratedCommandsFeaturesNVX + { + DeviceGeneratedCommandsFeaturesNVX( Bool32 computeBindingPointSupport_ = 0 ) + : sType( StructureType::eDeviceGeneratedCommandsFeaturesNVX ) + , pNext( nullptr ) + , computeBindingPointSupport( computeBindingPointSupport_ ) + { + } + + DeviceGeneratedCommandsFeaturesNVX( VkDeviceGeneratedCommandsFeaturesNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsFeaturesNVX) ); + } + + DeviceGeneratedCommandsFeaturesNVX& operator=( VkDeviceGeneratedCommandsFeaturesNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsFeaturesNVX) ); + return *this; + } + + DeviceGeneratedCommandsFeaturesNVX& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DeviceGeneratedCommandsFeaturesNVX& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DeviceGeneratedCommandsFeaturesNVX& setComputeBindingPointSupport( Bool32 computeBindingPointSupport_ ) + { + computeBindingPointSupport = computeBindingPointSupport_; + return *this; + } + + operator const VkDeviceGeneratedCommandsFeaturesNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DeviceGeneratedCommandsFeaturesNVX const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( computeBindingPointSupport == rhs.computeBindingPointSupport ); + } + + bool operator!=( DeviceGeneratedCommandsFeaturesNVX const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Bool32 computeBindingPointSupport; + }; + static_assert( sizeof( DeviceGeneratedCommandsFeaturesNVX ) == sizeof( VkDeviceGeneratedCommandsFeaturesNVX ), "struct and wrapper have different size!" ); + + struct DeviceGeneratedCommandsLimitsNVX + { + DeviceGeneratedCommandsLimitsNVX( uint32_t maxIndirectCommandsLayoutTokenCount_ = 0, uint32_t maxObjectEntryCounts_ = 0, uint32_t minSequenceCountBufferOffsetAlignment_ = 0, uint32_t minSequenceIndexBufferOffsetAlignment_ = 0, uint32_t minCommandsTokenBufferOffsetAlignment_ = 0 ) + : sType( StructureType::eDeviceGeneratedCommandsLimitsNVX ) + , pNext( nullptr ) + , maxIndirectCommandsLayoutTokenCount( maxIndirectCommandsLayoutTokenCount_ ) + , maxObjectEntryCounts( maxObjectEntryCounts_ ) + , minSequenceCountBufferOffsetAlignment( minSequenceCountBufferOffsetAlignment_ ) + , minSequenceIndexBufferOffsetAlignment( minSequenceIndexBufferOffsetAlignment_ ) + , minCommandsTokenBufferOffsetAlignment( minCommandsTokenBufferOffsetAlignment_ ) + { + } + + DeviceGeneratedCommandsLimitsNVX( VkDeviceGeneratedCommandsLimitsNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsLimitsNVX) ); + } + + DeviceGeneratedCommandsLimitsNVX& operator=( VkDeviceGeneratedCommandsLimitsNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsLimitsNVX) ); + return *this; + } + + DeviceGeneratedCommandsLimitsNVX& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DeviceGeneratedCommandsLimitsNVX& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DeviceGeneratedCommandsLimitsNVX& setMaxIndirectCommandsLayoutTokenCount( uint32_t maxIndirectCommandsLayoutTokenCount_ ) + { + maxIndirectCommandsLayoutTokenCount = maxIndirectCommandsLayoutTokenCount_; + return *this; + } + + DeviceGeneratedCommandsLimitsNVX& setMaxObjectEntryCounts( uint32_t maxObjectEntryCounts_ ) + { + maxObjectEntryCounts = maxObjectEntryCounts_; + return *this; + } + + DeviceGeneratedCommandsLimitsNVX& setMinSequenceCountBufferOffsetAlignment( uint32_t minSequenceCountBufferOffsetAlignment_ ) + { + minSequenceCountBufferOffsetAlignment = minSequenceCountBufferOffsetAlignment_; + return *this; + } + + DeviceGeneratedCommandsLimitsNVX& setMinSequenceIndexBufferOffsetAlignment( uint32_t minSequenceIndexBufferOffsetAlignment_ ) + { + minSequenceIndexBufferOffsetAlignment = minSequenceIndexBufferOffsetAlignment_; + return *this; + } + + DeviceGeneratedCommandsLimitsNVX& setMinCommandsTokenBufferOffsetAlignment( uint32_t minCommandsTokenBufferOffsetAlignment_ ) + { + minCommandsTokenBufferOffsetAlignment = minCommandsTokenBufferOffsetAlignment_; + return *this; + } + + operator const VkDeviceGeneratedCommandsLimitsNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DeviceGeneratedCommandsLimitsNVX const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( maxIndirectCommandsLayoutTokenCount == rhs.maxIndirectCommandsLayoutTokenCount ) + && ( maxObjectEntryCounts == rhs.maxObjectEntryCounts ) + && ( minSequenceCountBufferOffsetAlignment == rhs.minSequenceCountBufferOffsetAlignment ) + && ( minSequenceIndexBufferOffsetAlignment == rhs.minSequenceIndexBufferOffsetAlignment ) + && ( minCommandsTokenBufferOffsetAlignment == rhs.minCommandsTokenBufferOffsetAlignment ); + } + + bool operator!=( DeviceGeneratedCommandsLimitsNVX const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + uint32_t maxIndirectCommandsLayoutTokenCount; + uint32_t maxObjectEntryCounts; + uint32_t minSequenceCountBufferOffsetAlignment; + uint32_t minSequenceIndexBufferOffsetAlignment; + uint32_t minCommandsTokenBufferOffsetAlignment; + }; + static_assert( sizeof( DeviceGeneratedCommandsLimitsNVX ) == sizeof( VkDeviceGeneratedCommandsLimitsNVX ), "struct and wrapper have different size!" ); + + struct CmdReserveSpaceForCommandsInfoNVX + { + CmdReserveSpaceForCommandsInfoNVX( ObjectTableNVX objectTable_ = ObjectTableNVX(), IndirectCommandsLayoutNVX indirectCommandsLayout_ = IndirectCommandsLayoutNVX(), uint32_t maxSequencesCount_ = 0 ) + : sType( StructureType::eCmdReserveSpaceForCommandsInfoNVX ) + , pNext( nullptr ) + , objectTable( objectTable_ ) + , indirectCommandsLayout( indirectCommandsLayout_ ) + , maxSequencesCount( maxSequencesCount_ ) + { + } + + CmdReserveSpaceForCommandsInfoNVX( VkCmdReserveSpaceForCommandsInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(CmdReserveSpaceForCommandsInfoNVX) ); + } + + CmdReserveSpaceForCommandsInfoNVX& operator=( VkCmdReserveSpaceForCommandsInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(CmdReserveSpaceForCommandsInfoNVX) ); + return *this; + } + + CmdReserveSpaceForCommandsInfoNVX& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + CmdReserveSpaceForCommandsInfoNVX& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + CmdReserveSpaceForCommandsInfoNVX& setObjectTable( ObjectTableNVX objectTable_ ) + { + objectTable = objectTable_; + return *this; + } + + CmdReserveSpaceForCommandsInfoNVX& setIndirectCommandsLayout( IndirectCommandsLayoutNVX indirectCommandsLayout_ ) + { + indirectCommandsLayout = indirectCommandsLayout_; + return *this; + } + + CmdReserveSpaceForCommandsInfoNVX& setMaxSequencesCount( uint32_t maxSequencesCount_ ) + { + maxSequencesCount = maxSequencesCount_; + return *this; + } + + operator const VkCmdReserveSpaceForCommandsInfoNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( CmdReserveSpaceForCommandsInfoNVX const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( objectTable == rhs.objectTable ) + && ( indirectCommandsLayout == rhs.indirectCommandsLayout ) + && ( maxSequencesCount == rhs.maxSequencesCount ); + } + + bool operator!=( CmdReserveSpaceForCommandsInfoNVX const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ObjectTableNVX objectTable; + IndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t maxSequencesCount; + }; + static_assert( sizeof( CmdReserveSpaceForCommandsInfoNVX ) == sizeof( VkCmdReserveSpaceForCommandsInfoNVX ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceFeatures2KHR + { + PhysicalDeviceFeatures2KHR( PhysicalDeviceFeatures features_ = PhysicalDeviceFeatures() ) + : sType( StructureType::ePhysicalDeviceFeatures2KHR ) + , pNext( nullptr ) + , features( features_ ) + { + } + + PhysicalDeviceFeatures2KHR( VkPhysicalDeviceFeatures2KHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures2KHR) ); + } + + PhysicalDeviceFeatures2KHR& operator=( VkPhysicalDeviceFeatures2KHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures2KHR) ); + return *this; + } + + PhysicalDeviceFeatures2KHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PhysicalDeviceFeatures2KHR& setPNext( void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PhysicalDeviceFeatures2KHR& setFeatures( PhysicalDeviceFeatures features_ ) + { + features = features_; + return *this; + } + + operator const VkPhysicalDeviceFeatures2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceFeatures2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( features == rhs.features ); + } + + bool operator!=( PhysicalDeviceFeatures2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + PhysicalDeviceFeatures features; + }; + static_assert( sizeof( PhysicalDeviceFeatures2KHR ) == sizeof( VkPhysicalDeviceFeatures2KHR ), "struct and wrapper have different size!" ); + + enum class SubpassContents + { + eInline = VK_SUBPASS_CONTENTS_INLINE, + eSecondaryCommandBuffers = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS + }; + + struct PresentInfoKHR + { + PresentInfoKHR( uint32_t waitSemaphoreCount_ = 0, const Semaphore* pWaitSemaphores_ = nullptr, uint32_t swapchainCount_ = 0, const SwapchainKHR* pSwapchains_ = nullptr, const uint32_t* pImageIndices_ = nullptr, Result* pResults_ = nullptr ) + : sType( StructureType::ePresentInfoKHR ) + , pNext( nullptr ) + , waitSemaphoreCount( waitSemaphoreCount_ ) + , pWaitSemaphores( pWaitSemaphores_ ) + , swapchainCount( swapchainCount_ ) + , pSwapchains( pSwapchains_ ) + , pImageIndices( pImageIndices_ ) + , pResults( pResults_ ) + { + } + + PresentInfoKHR( VkPresentInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PresentInfoKHR) ); + } + + PresentInfoKHR& operator=( VkPresentInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PresentInfoKHR) ); + return *this; + } + + PresentInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PresentInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PresentInfoKHR& setWaitSemaphoreCount( uint32_t waitSemaphoreCount_ ) + { + waitSemaphoreCount = waitSemaphoreCount_; + return *this; + } + + PresentInfoKHR& setPWaitSemaphores( const Semaphore* pWaitSemaphores_ ) + { + pWaitSemaphores = pWaitSemaphores_; + return *this; + } + + PresentInfoKHR& setSwapchainCount( uint32_t swapchainCount_ ) + { + swapchainCount = swapchainCount_; + return *this; + } + + PresentInfoKHR& setPSwapchains( const SwapchainKHR* pSwapchains_ ) + { + pSwapchains = pSwapchains_; + return *this; + } + + PresentInfoKHR& setPImageIndices( const uint32_t* pImageIndices_ ) + { + pImageIndices = pImageIndices_; + return *this; + } + + PresentInfoKHR& setPResults( Result* pResults_ ) + { + pResults = pResults_; + return *this; + } + + operator const VkPresentInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PresentInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( waitSemaphoreCount == rhs.waitSemaphoreCount ) + && ( pWaitSemaphores == rhs.pWaitSemaphores ) + && ( swapchainCount == rhs.swapchainCount ) + && ( pSwapchains == rhs.pSwapchains ) + && ( pImageIndices == rhs.pImageIndices ) + && ( pResults == rhs.pResults ); + } + + bool operator!=( PresentInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + uint32_t waitSemaphoreCount; + const Semaphore* pWaitSemaphores; + uint32_t swapchainCount; + const SwapchainKHR* pSwapchains; + const uint32_t* pImageIndices; + Result* pResults; + }; + static_assert( sizeof( PresentInfoKHR ) == sizeof( VkPresentInfoKHR ), "struct and wrapper have different size!" ); + + enum class DynamicState + { + eViewport = VK_DYNAMIC_STATE_VIEWPORT, + eScissor = VK_DYNAMIC_STATE_SCISSOR, + eLineWidth = VK_DYNAMIC_STATE_LINE_WIDTH, + eDepthBias = VK_DYNAMIC_STATE_DEPTH_BIAS, + eBlendConstants = VK_DYNAMIC_STATE_BLEND_CONSTANTS, + eDepthBounds = VK_DYNAMIC_STATE_DEPTH_BOUNDS, + eStencilCompareMask = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, + eStencilWriteMask = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, + eStencilReference = VK_DYNAMIC_STATE_STENCIL_REFERENCE + }; + + struct PipelineDynamicStateCreateInfo + { + PipelineDynamicStateCreateInfo( PipelineDynamicStateCreateFlags flags_ = PipelineDynamicStateCreateFlags(), uint32_t dynamicStateCount_ = 0, const DynamicState* pDynamicStates_ = nullptr ) + : sType( StructureType::ePipelineDynamicStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , dynamicStateCount( dynamicStateCount_ ) + , pDynamicStates( pDynamicStates_ ) + { + } + + PipelineDynamicStateCreateInfo( VkPipelineDynamicStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineDynamicStateCreateInfo) ); + } + + PipelineDynamicStateCreateInfo& operator=( VkPipelineDynamicStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineDynamicStateCreateInfo) ); + return *this; + } + + PipelineDynamicStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineDynamicStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineDynamicStateCreateInfo& setFlags( PipelineDynamicStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineDynamicStateCreateInfo& setDynamicStateCount( uint32_t dynamicStateCount_ ) + { + dynamicStateCount = dynamicStateCount_; + return *this; + } + + PipelineDynamicStateCreateInfo& setPDynamicStates( const DynamicState* pDynamicStates_ ) + { + pDynamicStates = pDynamicStates_; + return *this; + } + + operator const VkPipelineDynamicStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineDynamicStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( dynamicStateCount == rhs.dynamicStateCount ) + && ( pDynamicStates == rhs.pDynamicStates ); + } + + bool operator!=( PipelineDynamicStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineDynamicStateCreateFlags flags; + uint32_t dynamicStateCount; + const DynamicState* pDynamicStates; + }; + static_assert( sizeof( PipelineDynamicStateCreateInfo ) == sizeof( VkPipelineDynamicStateCreateInfo ), "struct and wrapper have different size!" ); + + enum class QueueFlagBits + { + eGraphics = VK_QUEUE_GRAPHICS_BIT, + eCompute = VK_QUEUE_COMPUTE_BIT, + eTransfer = VK_QUEUE_TRANSFER_BIT, + eSparseBinding = VK_QUEUE_SPARSE_BINDING_BIT + }; + + using QueueFlags = Flags; + + VULKAN_HPP_INLINE QueueFlags operator|( QueueFlagBits bit0, QueueFlagBits bit1 ) + { + return QueueFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE QueueFlags operator~( QueueFlagBits bits ) + { + return ~( QueueFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(QueueFlagBits::eGraphics) | VkFlags(QueueFlagBits::eCompute) | VkFlags(QueueFlagBits::eTransfer) | VkFlags(QueueFlagBits::eSparseBinding) + }; + }; + + struct QueueFamilyProperties + { + operator const VkQueueFamilyProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( QueueFamilyProperties const& rhs ) const + { + return ( queueFlags == rhs.queueFlags ) + && ( queueCount == rhs.queueCount ) + && ( timestampValidBits == rhs.timestampValidBits ) + && ( minImageTransferGranularity == rhs.minImageTransferGranularity ); + } + + bool operator!=( QueueFamilyProperties const& rhs ) const + { + return !operator==( rhs ); + } + + QueueFlags queueFlags; + uint32_t queueCount; + uint32_t timestampValidBits; + Extent3D minImageTransferGranularity; + }; + static_assert( sizeof( QueueFamilyProperties ) == sizeof( VkQueueFamilyProperties ), "struct and wrapper have different size!" ); + + struct QueueFamilyProperties2KHR + { + operator const VkQueueFamilyProperties2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( QueueFamilyProperties2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( queueFamilyProperties == rhs.queueFamilyProperties ); + } + + bool operator!=( QueueFamilyProperties2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + QueueFamilyProperties queueFamilyProperties; + }; + static_assert( sizeof( QueueFamilyProperties2KHR ) == sizeof( VkQueueFamilyProperties2KHR ), "struct and wrapper have different size!" ); + + enum class MemoryPropertyFlagBits + { + eDeviceLocal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + eHostVisible = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + eHostCoherent = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + eHostCached = VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + eLazilyAllocated = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT + }; + + using MemoryPropertyFlags = Flags; + + VULKAN_HPP_INLINE MemoryPropertyFlags operator|( MemoryPropertyFlagBits bit0, MemoryPropertyFlagBits bit1 ) + { + return MemoryPropertyFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE MemoryPropertyFlags operator~( MemoryPropertyFlagBits bits ) + { + return ~( MemoryPropertyFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(MemoryPropertyFlagBits::eDeviceLocal) | VkFlags(MemoryPropertyFlagBits::eHostVisible) | VkFlags(MemoryPropertyFlagBits::eHostCoherent) | VkFlags(MemoryPropertyFlagBits::eHostCached) | VkFlags(MemoryPropertyFlagBits::eLazilyAllocated) + }; + }; + + struct MemoryType + { + operator const VkMemoryType&() const + { + return *reinterpret_cast(this); + } + + bool operator==( MemoryType const& rhs ) const + { + return ( propertyFlags == rhs.propertyFlags ) + && ( heapIndex == rhs.heapIndex ); + } + + bool operator!=( MemoryType const& rhs ) const + { + return !operator==( rhs ); + } + + MemoryPropertyFlags propertyFlags; + uint32_t heapIndex; + }; + static_assert( sizeof( MemoryType ) == sizeof( VkMemoryType ), "struct and wrapper have different size!" ); + + enum class MemoryHeapFlagBits + { + eDeviceLocal = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT + }; + + using MemoryHeapFlags = Flags; + + VULKAN_HPP_INLINE MemoryHeapFlags operator|( MemoryHeapFlagBits bit0, MemoryHeapFlagBits bit1 ) + { + return MemoryHeapFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE MemoryHeapFlags operator~( MemoryHeapFlagBits bits ) + { + return ~( MemoryHeapFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(MemoryHeapFlagBits::eDeviceLocal) + }; + }; + + struct MemoryHeap + { + operator const VkMemoryHeap&() const + { + return *reinterpret_cast(this); + } + + bool operator==( MemoryHeap const& rhs ) const + { + return ( size == rhs.size ) + && ( flags == rhs.flags ); + } + + bool operator!=( MemoryHeap const& rhs ) const + { + return !operator==( rhs ); + } + + DeviceSize size; + MemoryHeapFlags flags; + }; + static_assert( sizeof( MemoryHeap ) == sizeof( VkMemoryHeap ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceMemoryProperties + { + operator const VkPhysicalDeviceMemoryProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceMemoryProperties const& rhs ) const + { + return ( memoryTypeCount == rhs.memoryTypeCount ) + && ( memcmp( memoryTypes, rhs.memoryTypes, VK_MAX_MEMORY_TYPES * sizeof( MemoryType ) ) == 0 ) + && ( memoryHeapCount == rhs.memoryHeapCount ) + && ( memcmp( memoryHeaps, rhs.memoryHeaps, VK_MAX_MEMORY_HEAPS * sizeof( MemoryHeap ) ) == 0 ); + } + + bool operator!=( PhysicalDeviceMemoryProperties const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t memoryTypeCount; + MemoryType memoryTypes[VK_MAX_MEMORY_TYPES]; + uint32_t memoryHeapCount; + MemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; + }; + static_assert( sizeof( PhysicalDeviceMemoryProperties ) == sizeof( VkPhysicalDeviceMemoryProperties ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceMemoryProperties2KHR + { + operator const VkPhysicalDeviceMemoryProperties2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceMemoryProperties2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( memoryProperties == rhs.memoryProperties ); + } + + bool operator!=( PhysicalDeviceMemoryProperties2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + PhysicalDeviceMemoryProperties memoryProperties; + }; + static_assert( sizeof( PhysicalDeviceMemoryProperties2KHR ) == sizeof( VkPhysicalDeviceMemoryProperties2KHR ), "struct and wrapper have different size!" ); + + enum class AccessFlagBits + { + eIndirectCommandRead = VK_ACCESS_INDIRECT_COMMAND_READ_BIT, + eIndexRead = VK_ACCESS_INDEX_READ_BIT, + eVertexAttributeRead = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, + eUniformRead = VK_ACCESS_UNIFORM_READ_BIT, + eInputAttachmentRead = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, + eShaderRead = VK_ACCESS_SHADER_READ_BIT, + eShaderWrite = VK_ACCESS_SHADER_WRITE_BIT, + eColorAttachmentRead = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, + eColorAttachmentWrite = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + eDepthStencilAttachmentRead = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + eDepthStencilAttachmentWrite = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + eTransferRead = VK_ACCESS_TRANSFER_READ_BIT, + eTransferWrite = VK_ACCESS_TRANSFER_WRITE_BIT, + eHostRead = VK_ACCESS_HOST_READ_BIT, + eHostWrite = VK_ACCESS_HOST_WRITE_BIT, + eMemoryRead = VK_ACCESS_MEMORY_READ_BIT, + eMemoryWrite = VK_ACCESS_MEMORY_WRITE_BIT, + eCommandProcessReadNVX = VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX, + eCommandProcessWriteNVX = VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX + }; + + using AccessFlags = Flags; + + VULKAN_HPP_INLINE AccessFlags operator|( AccessFlagBits bit0, AccessFlagBits bit1 ) + { + return AccessFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE AccessFlags operator~( AccessFlagBits bits ) + { + return ~( AccessFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(AccessFlagBits::eIndirectCommandRead) | VkFlags(AccessFlagBits::eIndexRead) | VkFlags(AccessFlagBits::eVertexAttributeRead) | VkFlags(AccessFlagBits::eUniformRead) | VkFlags(AccessFlagBits::eInputAttachmentRead) | VkFlags(AccessFlagBits::eShaderRead) | VkFlags(AccessFlagBits::eShaderWrite) | VkFlags(AccessFlagBits::eColorAttachmentRead) | VkFlags(AccessFlagBits::eColorAttachmentWrite) | VkFlags(AccessFlagBits::eDepthStencilAttachmentRead) | VkFlags(AccessFlagBits::eDepthStencilAttachmentWrite) | VkFlags(AccessFlagBits::eTransferRead) | VkFlags(AccessFlagBits::eTransferWrite) | VkFlags(AccessFlagBits::eHostRead) | VkFlags(AccessFlagBits::eHostWrite) | VkFlags(AccessFlagBits::eMemoryRead) | VkFlags(AccessFlagBits::eMemoryWrite) | VkFlags(AccessFlagBits::eCommandProcessReadNVX) | VkFlags(AccessFlagBits::eCommandProcessWriteNVX) + }; + }; + + struct MemoryBarrier + { + MemoryBarrier( AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags() ) + : sType( StructureType::eMemoryBarrier ) + , pNext( nullptr ) + , srcAccessMask( srcAccessMask_ ) + , dstAccessMask( dstAccessMask_ ) + { + } + + MemoryBarrier( VkMemoryBarrier const & rhs ) + { + memcpy( this, &rhs, sizeof(MemoryBarrier) ); + } + + MemoryBarrier& operator=( VkMemoryBarrier const & rhs ) + { + memcpy( this, &rhs, sizeof(MemoryBarrier) ); + return *this; + } + + MemoryBarrier& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + MemoryBarrier& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + MemoryBarrier& setSrcAccessMask( AccessFlags srcAccessMask_ ) + { + srcAccessMask = srcAccessMask_; + return *this; + } + + MemoryBarrier& setDstAccessMask( AccessFlags dstAccessMask_ ) + { + dstAccessMask = dstAccessMask_; + return *this; + } + + operator const VkMemoryBarrier&() const + { + return *reinterpret_cast(this); + } + + bool operator==( MemoryBarrier const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( srcAccessMask == rhs.srcAccessMask ) + && ( dstAccessMask == rhs.dstAccessMask ); + } + + bool operator!=( MemoryBarrier const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + AccessFlags srcAccessMask; + AccessFlags dstAccessMask; + }; + static_assert( sizeof( MemoryBarrier ) == sizeof( VkMemoryBarrier ), "struct and wrapper have different size!" ); + + struct BufferMemoryBarrier + { + BufferMemoryBarrier( AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags(), uint32_t srcQueueFamilyIndex_ = 0, uint32_t dstQueueFamilyIndex_ = 0, Buffer buffer_ = Buffer(), DeviceSize offset_ = 0, DeviceSize size_ = 0 ) + : sType( StructureType::eBufferMemoryBarrier ) + , pNext( nullptr ) + , srcAccessMask( srcAccessMask_ ) + , dstAccessMask( dstAccessMask_ ) + , srcQueueFamilyIndex( srcQueueFamilyIndex_ ) + , dstQueueFamilyIndex( dstQueueFamilyIndex_ ) + , buffer( buffer_ ) + , offset( offset_ ) + , size( size_ ) + { + } + + BufferMemoryBarrier( VkBufferMemoryBarrier const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferMemoryBarrier) ); + } + + BufferMemoryBarrier& operator=( VkBufferMemoryBarrier const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferMemoryBarrier) ); + return *this; + } + + BufferMemoryBarrier& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + BufferMemoryBarrier& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + BufferMemoryBarrier& setSrcAccessMask( AccessFlags srcAccessMask_ ) + { + srcAccessMask = srcAccessMask_; + return *this; + } + + BufferMemoryBarrier& setDstAccessMask( AccessFlags dstAccessMask_ ) + { + dstAccessMask = dstAccessMask_; + return *this; + } + + BufferMemoryBarrier& setSrcQueueFamilyIndex( uint32_t srcQueueFamilyIndex_ ) + { + srcQueueFamilyIndex = srcQueueFamilyIndex_; + return *this; + } + + BufferMemoryBarrier& setDstQueueFamilyIndex( uint32_t dstQueueFamilyIndex_ ) + { + dstQueueFamilyIndex = dstQueueFamilyIndex_; + return *this; + } + + BufferMemoryBarrier& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + BufferMemoryBarrier& setOffset( DeviceSize offset_ ) + { + offset = offset_; + return *this; + } + + BufferMemoryBarrier& setSize( DeviceSize size_ ) + { + size = size_; + return *this; + } + + operator const VkBufferMemoryBarrier&() const + { + return *reinterpret_cast(this); + } + + bool operator==( BufferMemoryBarrier const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( srcAccessMask == rhs.srcAccessMask ) + && ( dstAccessMask == rhs.dstAccessMask ) + && ( srcQueueFamilyIndex == rhs.srcQueueFamilyIndex ) + && ( dstQueueFamilyIndex == rhs.dstQueueFamilyIndex ) + && ( buffer == rhs.buffer ) + && ( offset == rhs.offset ) + && ( size == rhs.size ); + } + + bool operator!=( BufferMemoryBarrier const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + AccessFlags srcAccessMask; + AccessFlags dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + Buffer buffer; + DeviceSize offset; + DeviceSize size; + }; + static_assert( sizeof( BufferMemoryBarrier ) == sizeof( VkBufferMemoryBarrier ), "struct and wrapper have different size!" ); + + enum class BufferUsageFlagBits + { + eTransferSrc = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + eTransferDst = VK_BUFFER_USAGE_TRANSFER_DST_BIT, + eUniformTexelBuffer = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, + eStorageTexelBuffer = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, + eUniformBuffer = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + eStorageBuffer = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + eIndexBuffer = VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + eVertexBuffer = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + eIndirectBuffer = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT + }; + + using BufferUsageFlags = Flags; + + VULKAN_HPP_INLINE BufferUsageFlags operator|( BufferUsageFlagBits bit0, BufferUsageFlagBits bit1 ) + { + return BufferUsageFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE BufferUsageFlags operator~( BufferUsageFlagBits bits ) + { + return ~( BufferUsageFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(BufferUsageFlagBits::eTransferSrc) | VkFlags(BufferUsageFlagBits::eTransferDst) | VkFlags(BufferUsageFlagBits::eUniformTexelBuffer) | VkFlags(BufferUsageFlagBits::eStorageTexelBuffer) | VkFlags(BufferUsageFlagBits::eUniformBuffer) | VkFlags(BufferUsageFlagBits::eStorageBuffer) | VkFlags(BufferUsageFlagBits::eIndexBuffer) | VkFlags(BufferUsageFlagBits::eVertexBuffer) | VkFlags(BufferUsageFlagBits::eIndirectBuffer) + }; + }; + + enum class BufferCreateFlagBits + { + eSparseBinding = VK_BUFFER_CREATE_SPARSE_BINDING_BIT, + eSparseResidency = VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, + eSparseAliased = VK_BUFFER_CREATE_SPARSE_ALIASED_BIT + }; + + using BufferCreateFlags = Flags; + + VULKAN_HPP_INLINE BufferCreateFlags operator|( BufferCreateFlagBits bit0, BufferCreateFlagBits bit1 ) + { + return BufferCreateFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE BufferCreateFlags operator~( BufferCreateFlagBits bits ) + { + return ~( BufferCreateFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(BufferCreateFlagBits::eSparseBinding) | VkFlags(BufferCreateFlagBits::eSparseResidency) | VkFlags(BufferCreateFlagBits::eSparseAliased) + }; + }; + + struct BufferCreateInfo + { + BufferCreateInfo( BufferCreateFlags flags_ = BufferCreateFlags(), DeviceSize size_ = 0, BufferUsageFlags usage_ = BufferUsageFlags(), SharingMode sharingMode_ = SharingMode::eExclusive, uint32_t queueFamilyIndexCount_ = 0, const uint32_t* pQueueFamilyIndices_ = nullptr ) + : sType( StructureType::eBufferCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , size( size_ ) + , usage( usage_ ) + , sharingMode( sharingMode_ ) + , queueFamilyIndexCount( queueFamilyIndexCount_ ) + , pQueueFamilyIndices( pQueueFamilyIndices_ ) + { + } + + BufferCreateInfo( VkBufferCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferCreateInfo) ); + } + + BufferCreateInfo& operator=( VkBufferCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferCreateInfo) ); + return *this; + } + + BufferCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + BufferCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + BufferCreateInfo& setFlags( BufferCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + BufferCreateInfo& setSize( DeviceSize size_ ) + { + size = size_; + return *this; + } + + BufferCreateInfo& setUsage( BufferUsageFlags usage_ ) + { + usage = usage_; + return *this; + } + + BufferCreateInfo& setSharingMode( SharingMode sharingMode_ ) + { + sharingMode = sharingMode_; + return *this; + } + + BufferCreateInfo& setQueueFamilyIndexCount( uint32_t queueFamilyIndexCount_ ) + { + queueFamilyIndexCount = queueFamilyIndexCount_; + return *this; + } + + BufferCreateInfo& setPQueueFamilyIndices( const uint32_t* pQueueFamilyIndices_ ) + { + pQueueFamilyIndices = pQueueFamilyIndices_; + return *this; + } + + operator const VkBufferCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( BufferCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( size == rhs.size ) + && ( usage == rhs.usage ) + && ( sharingMode == rhs.sharingMode ) + && ( queueFamilyIndexCount == rhs.queueFamilyIndexCount ) + && ( pQueueFamilyIndices == rhs.pQueueFamilyIndices ); + } + + bool operator!=( BufferCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + BufferCreateFlags flags; + DeviceSize size; + BufferUsageFlags usage; + SharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; + }; + static_assert( sizeof( BufferCreateInfo ) == sizeof( VkBufferCreateInfo ), "struct and wrapper have different size!" ); + + enum class ShaderStageFlagBits + { + eVertex = VK_SHADER_STAGE_VERTEX_BIT, + eTessellationControl = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, + eTessellationEvaluation = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, + eGeometry = VK_SHADER_STAGE_GEOMETRY_BIT, + eFragment = VK_SHADER_STAGE_FRAGMENT_BIT, + eCompute = VK_SHADER_STAGE_COMPUTE_BIT, + eAllGraphics = VK_SHADER_STAGE_ALL_GRAPHICS, + eAll = VK_SHADER_STAGE_ALL + }; + + using ShaderStageFlags = Flags; + + VULKAN_HPP_INLINE ShaderStageFlags operator|( ShaderStageFlagBits bit0, ShaderStageFlagBits bit1 ) + { + return ShaderStageFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ShaderStageFlags operator~( ShaderStageFlagBits bits ) + { + return ~( ShaderStageFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ShaderStageFlagBits::eVertex) | VkFlags(ShaderStageFlagBits::eTessellationControl) | VkFlags(ShaderStageFlagBits::eTessellationEvaluation) | VkFlags(ShaderStageFlagBits::eGeometry) | VkFlags(ShaderStageFlagBits::eFragment) | VkFlags(ShaderStageFlagBits::eCompute) | VkFlags(ShaderStageFlagBits::eAllGraphics) | VkFlags(ShaderStageFlagBits::eAll) + }; + }; + + struct DescriptorSetLayoutBinding + { + DescriptorSetLayoutBinding( uint32_t binding_ = 0, DescriptorType descriptorType_ = DescriptorType::eSampler, uint32_t descriptorCount_ = 0, ShaderStageFlags stageFlags_ = ShaderStageFlags(), const Sampler* pImmutableSamplers_ = nullptr ) + : binding( binding_ ) + , descriptorType( descriptorType_ ) + , descriptorCount( descriptorCount_ ) + , stageFlags( stageFlags_ ) + , pImmutableSamplers( pImmutableSamplers_ ) + { + } + + DescriptorSetLayoutBinding( VkDescriptorSetLayoutBinding const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorSetLayoutBinding) ); + } + + DescriptorSetLayoutBinding& operator=( VkDescriptorSetLayoutBinding const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorSetLayoutBinding) ); + return *this; + } + + DescriptorSetLayoutBinding& setBinding( uint32_t binding_ ) + { + binding = binding_; + return *this; + } + + DescriptorSetLayoutBinding& setDescriptorType( DescriptorType descriptorType_ ) + { + descriptorType = descriptorType_; + return *this; + } + + DescriptorSetLayoutBinding& setDescriptorCount( uint32_t descriptorCount_ ) + { + descriptorCount = descriptorCount_; + return *this; + } + + DescriptorSetLayoutBinding& setStageFlags( ShaderStageFlags stageFlags_ ) + { + stageFlags = stageFlags_; + return *this; + } + + DescriptorSetLayoutBinding& setPImmutableSamplers( const Sampler* pImmutableSamplers_ ) + { + pImmutableSamplers = pImmutableSamplers_; + return *this; + } + + operator const VkDescriptorSetLayoutBinding&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DescriptorSetLayoutBinding const& rhs ) const + { + return ( binding == rhs.binding ) + && ( descriptorType == rhs.descriptorType ) + && ( descriptorCount == rhs.descriptorCount ) + && ( stageFlags == rhs.stageFlags ) + && ( pImmutableSamplers == rhs.pImmutableSamplers ); + } + + bool operator!=( DescriptorSetLayoutBinding const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t binding; + DescriptorType descriptorType; + uint32_t descriptorCount; + ShaderStageFlags stageFlags; + const Sampler* pImmutableSamplers; + }; + static_assert( sizeof( DescriptorSetLayoutBinding ) == sizeof( VkDescriptorSetLayoutBinding ), "struct and wrapper have different size!" ); + + struct DescriptorSetLayoutCreateInfo + { + DescriptorSetLayoutCreateInfo( DescriptorSetLayoutCreateFlags flags_ = DescriptorSetLayoutCreateFlags(), uint32_t bindingCount_ = 0, const DescriptorSetLayoutBinding* pBindings_ = nullptr ) + : sType( StructureType::eDescriptorSetLayoutCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , bindingCount( bindingCount_ ) + , pBindings( pBindings_ ) + { + } + + DescriptorSetLayoutCreateInfo( VkDescriptorSetLayoutCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorSetLayoutCreateInfo) ); + } + + DescriptorSetLayoutCreateInfo& operator=( VkDescriptorSetLayoutCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorSetLayoutCreateInfo) ); + return *this; + } + + DescriptorSetLayoutCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DescriptorSetLayoutCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DescriptorSetLayoutCreateInfo& setFlags( DescriptorSetLayoutCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + DescriptorSetLayoutCreateInfo& setBindingCount( uint32_t bindingCount_ ) + { + bindingCount = bindingCount_; + return *this; + } + + DescriptorSetLayoutCreateInfo& setPBindings( const DescriptorSetLayoutBinding* pBindings_ ) + { + pBindings = pBindings_; + return *this; + } + + operator const VkDescriptorSetLayoutCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DescriptorSetLayoutCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( bindingCount == rhs.bindingCount ) + && ( pBindings == rhs.pBindings ); + } + + bool operator!=( DescriptorSetLayoutCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DescriptorSetLayoutCreateFlags flags; + uint32_t bindingCount; + const DescriptorSetLayoutBinding* pBindings; + }; + static_assert( sizeof( DescriptorSetLayoutCreateInfo ) == sizeof( VkDescriptorSetLayoutCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineShaderStageCreateInfo + { + PipelineShaderStageCreateInfo( PipelineShaderStageCreateFlags flags_ = PipelineShaderStageCreateFlags(), ShaderStageFlagBits stage_ = ShaderStageFlagBits::eVertex, ShaderModule module_ = ShaderModule(), const char* pName_ = nullptr, const SpecializationInfo* pSpecializationInfo_ = nullptr ) + : sType( StructureType::ePipelineShaderStageCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , stage( stage_ ) + , module( module_ ) + , pName( pName_ ) + , pSpecializationInfo( pSpecializationInfo_ ) + { + } + + PipelineShaderStageCreateInfo( VkPipelineShaderStageCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineShaderStageCreateInfo) ); + } + + PipelineShaderStageCreateInfo& operator=( VkPipelineShaderStageCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineShaderStageCreateInfo) ); + return *this; + } + + PipelineShaderStageCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineShaderStageCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineShaderStageCreateInfo& setFlags( PipelineShaderStageCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineShaderStageCreateInfo& setStage( ShaderStageFlagBits stage_ ) + { + stage = stage_; + return *this; + } + + PipelineShaderStageCreateInfo& setModule( ShaderModule module_ ) + { + module = module_; + return *this; + } + + PipelineShaderStageCreateInfo& setPName( const char* pName_ ) + { + pName = pName_; + return *this; + } + + PipelineShaderStageCreateInfo& setPSpecializationInfo( const SpecializationInfo* pSpecializationInfo_ ) + { + pSpecializationInfo = pSpecializationInfo_; + return *this; + } + + operator const VkPipelineShaderStageCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineShaderStageCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( stage == rhs.stage ) + && ( module == rhs.module ) + && ( pName == rhs.pName ) + && ( pSpecializationInfo == rhs.pSpecializationInfo ); + } + + bool operator!=( PipelineShaderStageCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineShaderStageCreateFlags flags; + ShaderStageFlagBits stage; + ShaderModule module; + const char* pName; + const SpecializationInfo* pSpecializationInfo; + }; + static_assert( sizeof( PipelineShaderStageCreateInfo ) == sizeof( VkPipelineShaderStageCreateInfo ), "struct and wrapper have different size!" ); + + struct PushConstantRange + { + PushConstantRange( ShaderStageFlags stageFlags_ = ShaderStageFlags(), uint32_t offset_ = 0, uint32_t size_ = 0 ) + : stageFlags( stageFlags_ ) + , offset( offset_ ) + , size( size_ ) + { + } + + PushConstantRange( VkPushConstantRange const & rhs ) + { + memcpy( this, &rhs, sizeof(PushConstantRange) ); + } + + PushConstantRange& operator=( VkPushConstantRange const & rhs ) + { + memcpy( this, &rhs, sizeof(PushConstantRange) ); + return *this; + } + + PushConstantRange& setStageFlags( ShaderStageFlags stageFlags_ ) + { + stageFlags = stageFlags_; + return *this; + } + + PushConstantRange& setOffset( uint32_t offset_ ) + { + offset = offset_; + return *this; + } + + PushConstantRange& setSize( uint32_t size_ ) + { + size = size_; + return *this; + } + + operator const VkPushConstantRange&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PushConstantRange const& rhs ) const + { + return ( stageFlags == rhs.stageFlags ) + && ( offset == rhs.offset ) + && ( size == rhs.size ); + } + + bool operator!=( PushConstantRange const& rhs ) const + { + return !operator==( rhs ); + } + + ShaderStageFlags stageFlags; + uint32_t offset; + uint32_t size; + }; + static_assert( sizeof( PushConstantRange ) == sizeof( VkPushConstantRange ), "struct and wrapper have different size!" ); + + struct PipelineLayoutCreateInfo + { + PipelineLayoutCreateInfo( PipelineLayoutCreateFlags flags_ = PipelineLayoutCreateFlags(), uint32_t setLayoutCount_ = 0, const DescriptorSetLayout* pSetLayouts_ = nullptr, uint32_t pushConstantRangeCount_ = 0, const PushConstantRange* pPushConstantRanges_ = nullptr ) + : sType( StructureType::ePipelineLayoutCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , setLayoutCount( setLayoutCount_ ) + , pSetLayouts( pSetLayouts_ ) + , pushConstantRangeCount( pushConstantRangeCount_ ) + , pPushConstantRanges( pPushConstantRanges_ ) + { + } + + PipelineLayoutCreateInfo( VkPipelineLayoutCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineLayoutCreateInfo) ); + } + + PipelineLayoutCreateInfo& operator=( VkPipelineLayoutCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineLayoutCreateInfo) ); + return *this; + } + + PipelineLayoutCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineLayoutCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineLayoutCreateInfo& setFlags( PipelineLayoutCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineLayoutCreateInfo& setSetLayoutCount( uint32_t setLayoutCount_ ) + { + setLayoutCount = setLayoutCount_; + return *this; + } + + PipelineLayoutCreateInfo& setPSetLayouts( const DescriptorSetLayout* pSetLayouts_ ) + { + pSetLayouts = pSetLayouts_; + return *this; + } + + PipelineLayoutCreateInfo& setPushConstantRangeCount( uint32_t pushConstantRangeCount_ ) + { + pushConstantRangeCount = pushConstantRangeCount_; + return *this; + } + + PipelineLayoutCreateInfo& setPPushConstantRanges( const PushConstantRange* pPushConstantRanges_ ) + { + pPushConstantRanges = pPushConstantRanges_; + return *this; + } + + operator const VkPipelineLayoutCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineLayoutCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( setLayoutCount == rhs.setLayoutCount ) + && ( pSetLayouts == rhs.pSetLayouts ) + && ( pushConstantRangeCount == rhs.pushConstantRangeCount ) + && ( pPushConstantRanges == rhs.pPushConstantRanges ); + } + + bool operator!=( PipelineLayoutCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineLayoutCreateFlags flags; + uint32_t setLayoutCount; + const DescriptorSetLayout* pSetLayouts; + uint32_t pushConstantRangeCount; + const PushConstantRange* pPushConstantRanges; + }; + static_assert( sizeof( PipelineLayoutCreateInfo ) == sizeof( VkPipelineLayoutCreateInfo ), "struct and wrapper have different size!" ); + + enum class ImageUsageFlagBits + { + eTransferSrc = VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + eTransferDst = VK_IMAGE_USAGE_TRANSFER_DST_BIT, + eSampled = VK_IMAGE_USAGE_SAMPLED_BIT, + eStorage = VK_IMAGE_USAGE_STORAGE_BIT, + eColorAttachment = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + eDepthStencilAttachment = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + eTransientAttachment = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, + eInputAttachment = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT + }; + + using ImageUsageFlags = Flags; + + VULKAN_HPP_INLINE ImageUsageFlags operator|( ImageUsageFlagBits bit0, ImageUsageFlagBits bit1 ) + { + return ImageUsageFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ImageUsageFlags operator~( ImageUsageFlagBits bits ) + { + return ~( ImageUsageFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ImageUsageFlagBits::eTransferSrc) | VkFlags(ImageUsageFlagBits::eTransferDst) | VkFlags(ImageUsageFlagBits::eSampled) | VkFlags(ImageUsageFlagBits::eStorage) | VkFlags(ImageUsageFlagBits::eColorAttachment) | VkFlags(ImageUsageFlagBits::eDepthStencilAttachment) | VkFlags(ImageUsageFlagBits::eTransientAttachment) | VkFlags(ImageUsageFlagBits::eInputAttachment) + }; + }; + + enum class ImageCreateFlagBits + { + eSparseBinding = VK_IMAGE_CREATE_SPARSE_BINDING_BIT, + eSparseResidency = VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, + eSparseAliased = VK_IMAGE_CREATE_SPARSE_ALIASED_BIT, + eMutableFormat = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, + eCubeCompatible = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, + e2DArrayCompatibleKHR = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR + }; + + using ImageCreateFlags = Flags; + + VULKAN_HPP_INLINE ImageCreateFlags operator|( ImageCreateFlagBits bit0, ImageCreateFlagBits bit1 ) + { + return ImageCreateFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ImageCreateFlags operator~( ImageCreateFlagBits bits ) + { + return ~( ImageCreateFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ImageCreateFlagBits::eSparseBinding) | VkFlags(ImageCreateFlagBits::eSparseResidency) | VkFlags(ImageCreateFlagBits::eSparseAliased) | VkFlags(ImageCreateFlagBits::eMutableFormat) | VkFlags(ImageCreateFlagBits::eCubeCompatible) | VkFlags(ImageCreateFlagBits::e2DArrayCompatibleKHR) + }; + }; + + struct PhysicalDeviceImageFormatInfo2KHR + { + PhysicalDeviceImageFormatInfo2KHR( Format format_ = Format::eUndefined, ImageType type_ = ImageType::e1D, ImageTiling tiling_ = ImageTiling::eOptimal, ImageUsageFlags usage_ = ImageUsageFlags(), ImageCreateFlags flags_ = ImageCreateFlags() ) + : sType( StructureType::ePhysicalDeviceImageFormatInfo2KHR ) + , pNext( nullptr ) + , format( format_ ) + , type( type_ ) + , tiling( tiling_ ) + , usage( usage_ ) + , flags( flags_ ) + { + } + + PhysicalDeviceImageFormatInfo2KHR( VkPhysicalDeviceImageFormatInfo2KHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceImageFormatInfo2KHR) ); + } + + PhysicalDeviceImageFormatInfo2KHR& operator=( VkPhysicalDeviceImageFormatInfo2KHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceImageFormatInfo2KHR) ); + return *this; + } + + PhysicalDeviceImageFormatInfo2KHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PhysicalDeviceImageFormatInfo2KHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PhysicalDeviceImageFormatInfo2KHR& setFormat( Format format_ ) + { + format = format_; + return *this; + } + + PhysicalDeviceImageFormatInfo2KHR& setType( ImageType type_ ) + { + type = type_; + return *this; + } + + PhysicalDeviceImageFormatInfo2KHR& setTiling( ImageTiling tiling_ ) + { + tiling = tiling_; + return *this; + } + + PhysicalDeviceImageFormatInfo2KHR& setUsage( ImageUsageFlags usage_ ) + { + usage = usage_; + return *this; + } + + PhysicalDeviceImageFormatInfo2KHR& setFlags( ImageCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + operator const VkPhysicalDeviceImageFormatInfo2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceImageFormatInfo2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( format == rhs.format ) + && ( type == rhs.type ) + && ( tiling == rhs.tiling ) + && ( usage == rhs.usage ) + && ( flags == rhs.flags ); + } + + bool operator!=( PhysicalDeviceImageFormatInfo2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Format format; + ImageType type; + ImageTiling tiling; + ImageUsageFlags usage; + ImageCreateFlags flags; + }; + static_assert( sizeof( PhysicalDeviceImageFormatInfo2KHR ) == sizeof( VkPhysicalDeviceImageFormatInfo2KHR ), "struct and wrapper have different size!" ); + + enum class PipelineCreateFlagBits + { + eDisableOptimization = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT, + eAllowDerivatives = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT, + eDerivative = VK_PIPELINE_CREATE_DERIVATIVE_BIT + }; + + using PipelineCreateFlags = Flags; + + VULKAN_HPP_INLINE PipelineCreateFlags operator|( PipelineCreateFlagBits bit0, PipelineCreateFlagBits bit1 ) + { + return PipelineCreateFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE PipelineCreateFlags operator~( PipelineCreateFlagBits bits ) + { + return ~( PipelineCreateFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(PipelineCreateFlagBits::eDisableOptimization) | VkFlags(PipelineCreateFlagBits::eAllowDerivatives) | VkFlags(PipelineCreateFlagBits::eDerivative) + }; + }; + + struct ComputePipelineCreateInfo + { + ComputePipelineCreateInfo( PipelineCreateFlags flags_ = PipelineCreateFlags(), PipelineShaderStageCreateInfo stage_ = PipelineShaderStageCreateInfo(), PipelineLayout layout_ = PipelineLayout(), Pipeline basePipelineHandle_ = Pipeline(), int32_t basePipelineIndex_ = 0 ) + : sType( StructureType::eComputePipelineCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , stage( stage_ ) + , layout( layout_ ) + , basePipelineHandle( basePipelineHandle_ ) + , basePipelineIndex( basePipelineIndex_ ) + { + } + + ComputePipelineCreateInfo( VkComputePipelineCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ComputePipelineCreateInfo) ); + } + + ComputePipelineCreateInfo& operator=( VkComputePipelineCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ComputePipelineCreateInfo) ); + return *this; + } + + ComputePipelineCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ComputePipelineCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ComputePipelineCreateInfo& setFlags( PipelineCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + ComputePipelineCreateInfo& setStage( PipelineShaderStageCreateInfo stage_ ) + { + stage = stage_; + return *this; + } + + ComputePipelineCreateInfo& setLayout( PipelineLayout layout_ ) + { + layout = layout_; + return *this; + } + + ComputePipelineCreateInfo& setBasePipelineHandle( Pipeline basePipelineHandle_ ) + { + basePipelineHandle = basePipelineHandle_; + return *this; + } + + ComputePipelineCreateInfo& setBasePipelineIndex( int32_t basePipelineIndex_ ) + { + basePipelineIndex = basePipelineIndex_; + return *this; + } + + operator const VkComputePipelineCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ComputePipelineCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( stage == rhs.stage ) + && ( layout == rhs.layout ) + && ( basePipelineHandle == rhs.basePipelineHandle ) + && ( basePipelineIndex == rhs.basePipelineIndex ); + } + + bool operator!=( ComputePipelineCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineCreateFlags flags; + PipelineShaderStageCreateInfo stage; + PipelineLayout layout; + Pipeline basePipelineHandle; + int32_t basePipelineIndex; + }; + static_assert( sizeof( ComputePipelineCreateInfo ) == sizeof( VkComputePipelineCreateInfo ), "struct and wrapper have different size!" ); + + enum class ColorComponentFlagBits + { + eR = VK_COLOR_COMPONENT_R_BIT, + eG = VK_COLOR_COMPONENT_G_BIT, + eB = VK_COLOR_COMPONENT_B_BIT, + eA = VK_COLOR_COMPONENT_A_BIT + }; + + using ColorComponentFlags = Flags; + + VULKAN_HPP_INLINE ColorComponentFlags operator|( ColorComponentFlagBits bit0, ColorComponentFlagBits bit1 ) + { + return ColorComponentFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ColorComponentFlags operator~( ColorComponentFlagBits bits ) + { + return ~( ColorComponentFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ColorComponentFlagBits::eR) | VkFlags(ColorComponentFlagBits::eG) | VkFlags(ColorComponentFlagBits::eB) | VkFlags(ColorComponentFlagBits::eA) + }; + }; + + struct PipelineColorBlendAttachmentState + { + PipelineColorBlendAttachmentState( Bool32 blendEnable_ = 0, BlendFactor srcColorBlendFactor_ = BlendFactor::eZero, BlendFactor dstColorBlendFactor_ = BlendFactor::eZero, BlendOp colorBlendOp_ = BlendOp::eAdd, BlendFactor srcAlphaBlendFactor_ = BlendFactor::eZero, BlendFactor dstAlphaBlendFactor_ = BlendFactor::eZero, BlendOp alphaBlendOp_ = BlendOp::eAdd, ColorComponentFlags colorWriteMask_ = ColorComponentFlags() ) + : blendEnable( blendEnable_ ) + , srcColorBlendFactor( srcColorBlendFactor_ ) + , dstColorBlendFactor( dstColorBlendFactor_ ) + , colorBlendOp( colorBlendOp_ ) + , srcAlphaBlendFactor( srcAlphaBlendFactor_ ) + , dstAlphaBlendFactor( dstAlphaBlendFactor_ ) + , alphaBlendOp( alphaBlendOp_ ) + , colorWriteMask( colorWriteMask_ ) + { + } + + PipelineColorBlendAttachmentState( VkPipelineColorBlendAttachmentState const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineColorBlendAttachmentState) ); + } + + PipelineColorBlendAttachmentState& operator=( VkPipelineColorBlendAttachmentState const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineColorBlendAttachmentState) ); + return *this; + } + + PipelineColorBlendAttachmentState& setBlendEnable( Bool32 blendEnable_ ) + { + blendEnable = blendEnable_; + return *this; + } + + PipelineColorBlendAttachmentState& setSrcColorBlendFactor( BlendFactor srcColorBlendFactor_ ) + { + srcColorBlendFactor = srcColorBlendFactor_; + return *this; + } + + PipelineColorBlendAttachmentState& setDstColorBlendFactor( BlendFactor dstColorBlendFactor_ ) + { + dstColorBlendFactor = dstColorBlendFactor_; + return *this; + } + + PipelineColorBlendAttachmentState& setColorBlendOp( BlendOp colorBlendOp_ ) + { + colorBlendOp = colorBlendOp_; + return *this; + } + + PipelineColorBlendAttachmentState& setSrcAlphaBlendFactor( BlendFactor srcAlphaBlendFactor_ ) + { + srcAlphaBlendFactor = srcAlphaBlendFactor_; + return *this; + } + + PipelineColorBlendAttachmentState& setDstAlphaBlendFactor( BlendFactor dstAlphaBlendFactor_ ) + { + dstAlphaBlendFactor = dstAlphaBlendFactor_; + return *this; + } + + PipelineColorBlendAttachmentState& setAlphaBlendOp( BlendOp alphaBlendOp_ ) + { + alphaBlendOp = alphaBlendOp_; + return *this; + } + + PipelineColorBlendAttachmentState& setColorWriteMask( ColorComponentFlags colorWriteMask_ ) + { + colorWriteMask = colorWriteMask_; + return *this; + } + + operator const VkPipelineColorBlendAttachmentState&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineColorBlendAttachmentState const& rhs ) const + { + return ( blendEnable == rhs.blendEnable ) + && ( srcColorBlendFactor == rhs.srcColorBlendFactor ) + && ( dstColorBlendFactor == rhs.dstColorBlendFactor ) + && ( colorBlendOp == rhs.colorBlendOp ) + && ( srcAlphaBlendFactor == rhs.srcAlphaBlendFactor ) + && ( dstAlphaBlendFactor == rhs.dstAlphaBlendFactor ) + && ( alphaBlendOp == rhs.alphaBlendOp ) + && ( colorWriteMask == rhs.colorWriteMask ); + } + + bool operator!=( PipelineColorBlendAttachmentState const& rhs ) const + { + return !operator==( rhs ); + } + + Bool32 blendEnable; + BlendFactor srcColorBlendFactor; + BlendFactor dstColorBlendFactor; + BlendOp colorBlendOp; + BlendFactor srcAlphaBlendFactor; + BlendFactor dstAlphaBlendFactor; + BlendOp alphaBlendOp; + ColorComponentFlags colorWriteMask; + }; + static_assert( sizeof( PipelineColorBlendAttachmentState ) == sizeof( VkPipelineColorBlendAttachmentState ), "struct and wrapper have different size!" ); + + struct PipelineColorBlendStateCreateInfo + { + PipelineColorBlendStateCreateInfo( PipelineColorBlendStateCreateFlags flags_ = PipelineColorBlendStateCreateFlags(), Bool32 logicOpEnable_ = 0, LogicOp logicOp_ = LogicOp::eClear, uint32_t attachmentCount_ = 0, const PipelineColorBlendAttachmentState* pAttachments_ = nullptr, std::array const& blendConstants_ = { { 0, 0, 0, 0 } } ) + : sType( StructureType::ePipelineColorBlendStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , logicOpEnable( logicOpEnable_ ) + , logicOp( logicOp_ ) + , attachmentCount( attachmentCount_ ) + , pAttachments( pAttachments_ ) + { + memcpy( &blendConstants, blendConstants_.data(), 4 * sizeof( float ) ); + } + + PipelineColorBlendStateCreateInfo( VkPipelineColorBlendStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineColorBlendStateCreateInfo) ); + } + + PipelineColorBlendStateCreateInfo& operator=( VkPipelineColorBlendStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineColorBlendStateCreateInfo) ); + return *this; + } + + PipelineColorBlendStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineColorBlendStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineColorBlendStateCreateInfo& setFlags( PipelineColorBlendStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineColorBlendStateCreateInfo& setLogicOpEnable( Bool32 logicOpEnable_ ) + { + logicOpEnable = logicOpEnable_; + return *this; + } + + PipelineColorBlendStateCreateInfo& setLogicOp( LogicOp logicOp_ ) + { + logicOp = logicOp_; + return *this; + } + + PipelineColorBlendStateCreateInfo& setAttachmentCount( uint32_t attachmentCount_ ) + { + attachmentCount = attachmentCount_; + return *this; + } + + PipelineColorBlendStateCreateInfo& setPAttachments( const PipelineColorBlendAttachmentState* pAttachments_ ) + { + pAttachments = pAttachments_; + return *this; + } + + PipelineColorBlendStateCreateInfo& setBlendConstants( std::array blendConstants_ ) + { + memcpy( &blendConstants, blendConstants_.data(), 4 * sizeof( float ) ); + return *this; + } + + operator const VkPipelineColorBlendStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineColorBlendStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( logicOpEnable == rhs.logicOpEnable ) + && ( logicOp == rhs.logicOp ) + && ( attachmentCount == rhs.attachmentCount ) + && ( pAttachments == rhs.pAttachments ) + && ( memcmp( blendConstants, rhs.blendConstants, 4 * sizeof( float ) ) == 0 ); + } + + bool operator!=( PipelineColorBlendStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineColorBlendStateCreateFlags flags; + Bool32 logicOpEnable; + LogicOp logicOp; + uint32_t attachmentCount; + const PipelineColorBlendAttachmentState* pAttachments; + float blendConstants[4]; + }; + static_assert( sizeof( PipelineColorBlendStateCreateInfo ) == sizeof( VkPipelineColorBlendStateCreateInfo ), "struct and wrapper have different size!" ); + + enum class FenceCreateFlagBits + { + eSignaled = VK_FENCE_CREATE_SIGNALED_BIT + }; + + using FenceCreateFlags = Flags; + + VULKAN_HPP_INLINE FenceCreateFlags operator|( FenceCreateFlagBits bit0, FenceCreateFlagBits bit1 ) + { + return FenceCreateFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE FenceCreateFlags operator~( FenceCreateFlagBits bits ) + { + return ~( FenceCreateFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(FenceCreateFlagBits::eSignaled) + }; + }; + + struct FenceCreateInfo + { + FenceCreateInfo( FenceCreateFlags flags_ = FenceCreateFlags() ) + : sType( StructureType::eFenceCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + { + } + + FenceCreateInfo( VkFenceCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(FenceCreateInfo) ); + } + + FenceCreateInfo& operator=( VkFenceCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(FenceCreateInfo) ); + return *this; + } + + FenceCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + FenceCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + FenceCreateInfo& setFlags( FenceCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + operator const VkFenceCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( FenceCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ); + } + + bool operator!=( FenceCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + FenceCreateFlags flags; + }; + static_assert( sizeof( FenceCreateInfo ) == sizeof( VkFenceCreateInfo ), "struct and wrapper have different size!" ); + + enum class FormatFeatureFlagBits + { + eSampledImage = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, + eStorageImage = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, + eStorageImageAtomic = VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT, + eUniformTexelBuffer = VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT, + eStorageTexelBuffer = VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT, + eStorageTexelBufferAtomic = VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT, + eVertexBuffer = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT, + eColorAttachment = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, + eColorAttachmentBlend = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT, + eDepthStencilAttachment = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, + eBlitSrc = VK_FORMAT_FEATURE_BLIT_SRC_BIT, + eBlitDst = VK_FORMAT_FEATURE_BLIT_DST_BIT, + eSampledImageFilterLinear = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, + eSampledImageFilterCubicIMG = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG, + eTransferSrcKHR = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR, + eTransferDstKHR = VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR + }; + + using FormatFeatureFlags = Flags; + + VULKAN_HPP_INLINE FormatFeatureFlags operator|( FormatFeatureFlagBits bit0, FormatFeatureFlagBits bit1 ) + { + return FormatFeatureFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE FormatFeatureFlags operator~( FormatFeatureFlagBits bits ) + { + return ~( FormatFeatureFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(FormatFeatureFlagBits::eSampledImage) | VkFlags(FormatFeatureFlagBits::eStorageImage) | VkFlags(FormatFeatureFlagBits::eStorageImageAtomic) | VkFlags(FormatFeatureFlagBits::eUniformTexelBuffer) | VkFlags(FormatFeatureFlagBits::eStorageTexelBuffer) | VkFlags(FormatFeatureFlagBits::eStorageTexelBufferAtomic) | VkFlags(FormatFeatureFlagBits::eVertexBuffer) | VkFlags(FormatFeatureFlagBits::eColorAttachment) | VkFlags(FormatFeatureFlagBits::eColorAttachmentBlend) | VkFlags(FormatFeatureFlagBits::eDepthStencilAttachment) | VkFlags(FormatFeatureFlagBits::eBlitSrc) | VkFlags(FormatFeatureFlagBits::eBlitDst) | VkFlags(FormatFeatureFlagBits::eSampledImageFilterLinear) | VkFlags(FormatFeatureFlagBits::eSampledImageFilterCubicIMG) | VkFlags(FormatFeatureFlagBits::eTransferSrcKHR) | VkFlags(FormatFeatureFlagBits::eTransferDstKHR) + }; + }; + + struct FormatProperties + { + operator const VkFormatProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( FormatProperties const& rhs ) const + { + return ( linearTilingFeatures == rhs.linearTilingFeatures ) + && ( optimalTilingFeatures == rhs.optimalTilingFeatures ) + && ( bufferFeatures == rhs.bufferFeatures ); + } + + bool operator!=( FormatProperties const& rhs ) const + { + return !operator==( rhs ); + } + + FormatFeatureFlags linearTilingFeatures; + FormatFeatureFlags optimalTilingFeatures; + FormatFeatureFlags bufferFeatures; + }; + static_assert( sizeof( FormatProperties ) == sizeof( VkFormatProperties ), "struct and wrapper have different size!" ); + + struct FormatProperties2KHR + { + operator const VkFormatProperties2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( FormatProperties2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( formatProperties == rhs.formatProperties ); + } + + bool operator!=( FormatProperties2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + FormatProperties formatProperties; + }; + static_assert( sizeof( FormatProperties2KHR ) == sizeof( VkFormatProperties2KHR ), "struct and wrapper have different size!" ); + + enum class QueryControlFlagBits + { + ePrecise = VK_QUERY_CONTROL_PRECISE_BIT + }; + + using QueryControlFlags = Flags; + + VULKAN_HPP_INLINE QueryControlFlags operator|( QueryControlFlagBits bit0, QueryControlFlagBits bit1 ) + { + return QueryControlFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE QueryControlFlags operator~( QueryControlFlagBits bits ) + { + return ~( QueryControlFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(QueryControlFlagBits::ePrecise) + }; + }; + + enum class QueryResultFlagBits + { + e64 = VK_QUERY_RESULT_64_BIT, + eWait = VK_QUERY_RESULT_WAIT_BIT, + eWithAvailability = VK_QUERY_RESULT_WITH_AVAILABILITY_BIT, + ePartial = VK_QUERY_RESULT_PARTIAL_BIT + }; + + using QueryResultFlags = Flags; + + VULKAN_HPP_INLINE QueryResultFlags operator|( QueryResultFlagBits bit0, QueryResultFlagBits bit1 ) + { + return QueryResultFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE QueryResultFlags operator~( QueryResultFlagBits bits ) + { + return ~( QueryResultFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(QueryResultFlagBits::e64) | VkFlags(QueryResultFlagBits::eWait) | VkFlags(QueryResultFlagBits::eWithAvailability) | VkFlags(QueryResultFlagBits::ePartial) + }; + }; + + enum class CommandBufferUsageFlagBits + { + eOneTimeSubmit = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + eRenderPassContinue = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, + eSimultaneousUse = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT + }; + + using CommandBufferUsageFlags = Flags; + + VULKAN_HPP_INLINE CommandBufferUsageFlags operator|( CommandBufferUsageFlagBits bit0, CommandBufferUsageFlagBits bit1 ) + { + return CommandBufferUsageFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE CommandBufferUsageFlags operator~( CommandBufferUsageFlagBits bits ) + { + return ~( CommandBufferUsageFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(CommandBufferUsageFlagBits::eOneTimeSubmit) | VkFlags(CommandBufferUsageFlagBits::eRenderPassContinue) | VkFlags(CommandBufferUsageFlagBits::eSimultaneousUse) + }; + }; + + enum class QueryPipelineStatisticFlagBits + { + eInputAssemblyVertices = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT, + eInputAssemblyPrimitives = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT, + eVertexShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT, + eGeometryShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT, + eGeometryShaderPrimitives = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT, + eClippingInvocations = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT, + eClippingPrimitives = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT, + eFragmentShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT, + eTessellationControlShaderPatches = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT, + eTessellationEvaluationShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT, + eComputeShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT + }; + + using QueryPipelineStatisticFlags = Flags; + + VULKAN_HPP_INLINE QueryPipelineStatisticFlags operator|( QueryPipelineStatisticFlagBits bit0, QueryPipelineStatisticFlagBits bit1 ) + { + return QueryPipelineStatisticFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE QueryPipelineStatisticFlags operator~( QueryPipelineStatisticFlagBits bits ) + { + return ~( QueryPipelineStatisticFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(QueryPipelineStatisticFlagBits::eInputAssemblyVertices) | VkFlags(QueryPipelineStatisticFlagBits::eInputAssemblyPrimitives) | VkFlags(QueryPipelineStatisticFlagBits::eVertexShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eGeometryShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eGeometryShaderPrimitives) | VkFlags(QueryPipelineStatisticFlagBits::eClippingInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eClippingPrimitives) | VkFlags(QueryPipelineStatisticFlagBits::eFragmentShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eTessellationControlShaderPatches) | VkFlags(QueryPipelineStatisticFlagBits::eTessellationEvaluationShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eComputeShaderInvocations) + }; + }; + + struct CommandBufferInheritanceInfo + { + CommandBufferInheritanceInfo( RenderPass renderPass_ = RenderPass(), uint32_t subpass_ = 0, Framebuffer framebuffer_ = Framebuffer(), Bool32 occlusionQueryEnable_ = 0, QueryControlFlags queryFlags_ = QueryControlFlags(), QueryPipelineStatisticFlags pipelineStatistics_ = QueryPipelineStatisticFlags() ) + : sType( StructureType::eCommandBufferInheritanceInfo ) + , pNext( nullptr ) + , renderPass( renderPass_ ) + , subpass( subpass_ ) + , framebuffer( framebuffer_ ) + , occlusionQueryEnable( occlusionQueryEnable_ ) + , queryFlags( queryFlags_ ) + , pipelineStatistics( pipelineStatistics_ ) + { + } + + CommandBufferInheritanceInfo( VkCommandBufferInheritanceInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandBufferInheritanceInfo) ); + } + + CommandBufferInheritanceInfo& operator=( VkCommandBufferInheritanceInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandBufferInheritanceInfo) ); + return *this; + } + + CommandBufferInheritanceInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + CommandBufferInheritanceInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + CommandBufferInheritanceInfo& setRenderPass( RenderPass renderPass_ ) + { + renderPass = renderPass_; + return *this; + } + + CommandBufferInheritanceInfo& setSubpass( uint32_t subpass_ ) + { + subpass = subpass_; + return *this; + } + + CommandBufferInheritanceInfo& setFramebuffer( Framebuffer framebuffer_ ) + { + framebuffer = framebuffer_; + return *this; + } + + CommandBufferInheritanceInfo& setOcclusionQueryEnable( Bool32 occlusionQueryEnable_ ) + { + occlusionQueryEnable = occlusionQueryEnable_; + return *this; + } + + CommandBufferInheritanceInfo& setQueryFlags( QueryControlFlags queryFlags_ ) + { + queryFlags = queryFlags_; + return *this; + } + + CommandBufferInheritanceInfo& setPipelineStatistics( QueryPipelineStatisticFlags pipelineStatistics_ ) + { + pipelineStatistics = pipelineStatistics_; + return *this; + } + + operator const VkCommandBufferInheritanceInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( CommandBufferInheritanceInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( renderPass == rhs.renderPass ) + && ( subpass == rhs.subpass ) + && ( framebuffer == rhs.framebuffer ) + && ( occlusionQueryEnable == rhs.occlusionQueryEnable ) + && ( queryFlags == rhs.queryFlags ) + && ( pipelineStatistics == rhs.pipelineStatistics ); + } + + bool operator!=( CommandBufferInheritanceInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + RenderPass renderPass; + uint32_t subpass; + Framebuffer framebuffer; + Bool32 occlusionQueryEnable; + QueryControlFlags queryFlags; + QueryPipelineStatisticFlags pipelineStatistics; + }; + static_assert( sizeof( CommandBufferInheritanceInfo ) == sizeof( VkCommandBufferInheritanceInfo ), "struct and wrapper have different size!" ); + + struct CommandBufferBeginInfo + { + CommandBufferBeginInfo( CommandBufferUsageFlags flags_ = CommandBufferUsageFlags(), const CommandBufferInheritanceInfo* pInheritanceInfo_ = nullptr ) + : sType( StructureType::eCommandBufferBeginInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , pInheritanceInfo( pInheritanceInfo_ ) + { + } + + CommandBufferBeginInfo( VkCommandBufferBeginInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandBufferBeginInfo) ); + } + + CommandBufferBeginInfo& operator=( VkCommandBufferBeginInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandBufferBeginInfo) ); + return *this; + } + + CommandBufferBeginInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + CommandBufferBeginInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + CommandBufferBeginInfo& setFlags( CommandBufferUsageFlags flags_ ) + { + flags = flags_; + return *this; + } + + CommandBufferBeginInfo& setPInheritanceInfo( const CommandBufferInheritanceInfo* pInheritanceInfo_ ) + { + pInheritanceInfo = pInheritanceInfo_; + return *this; + } + + operator const VkCommandBufferBeginInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( CommandBufferBeginInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( pInheritanceInfo == rhs.pInheritanceInfo ); + } + + bool operator!=( CommandBufferBeginInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + CommandBufferUsageFlags flags; + const CommandBufferInheritanceInfo* pInheritanceInfo; + }; + static_assert( sizeof( CommandBufferBeginInfo ) == sizeof( VkCommandBufferBeginInfo ), "struct and wrapper have different size!" ); + + struct QueryPoolCreateInfo + { + QueryPoolCreateInfo( QueryPoolCreateFlags flags_ = QueryPoolCreateFlags(), QueryType queryType_ = QueryType::eOcclusion, uint32_t queryCount_ = 0, QueryPipelineStatisticFlags pipelineStatistics_ = QueryPipelineStatisticFlags() ) + : sType( StructureType::eQueryPoolCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , queryType( queryType_ ) + , queryCount( queryCount_ ) + , pipelineStatistics( pipelineStatistics_ ) + { + } + + QueryPoolCreateInfo( VkQueryPoolCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(QueryPoolCreateInfo) ); + } + + QueryPoolCreateInfo& operator=( VkQueryPoolCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(QueryPoolCreateInfo) ); + return *this; + } + + QueryPoolCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + QueryPoolCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + QueryPoolCreateInfo& setFlags( QueryPoolCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + QueryPoolCreateInfo& setQueryType( QueryType queryType_ ) + { + queryType = queryType_; + return *this; + } + + QueryPoolCreateInfo& setQueryCount( uint32_t queryCount_ ) + { + queryCount = queryCount_; + return *this; + } + + QueryPoolCreateInfo& setPipelineStatistics( QueryPipelineStatisticFlags pipelineStatistics_ ) + { + pipelineStatistics = pipelineStatistics_; + return *this; + } + + operator const VkQueryPoolCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( QueryPoolCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( queryType == rhs.queryType ) + && ( queryCount == rhs.queryCount ) + && ( pipelineStatistics == rhs.pipelineStatistics ); + } + + bool operator!=( QueryPoolCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + QueryPoolCreateFlags flags; + QueryType queryType; + uint32_t queryCount; + QueryPipelineStatisticFlags pipelineStatistics; + }; + static_assert( sizeof( QueryPoolCreateInfo ) == sizeof( VkQueryPoolCreateInfo ), "struct and wrapper have different size!" ); + + enum class ImageAspectFlagBits + { + eColor = VK_IMAGE_ASPECT_COLOR_BIT, + eDepth = VK_IMAGE_ASPECT_DEPTH_BIT, + eStencil = VK_IMAGE_ASPECT_STENCIL_BIT, + eMetadata = VK_IMAGE_ASPECT_METADATA_BIT + }; + + using ImageAspectFlags = Flags; + + VULKAN_HPP_INLINE ImageAspectFlags operator|( ImageAspectFlagBits bit0, ImageAspectFlagBits bit1 ) + { + return ImageAspectFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ImageAspectFlags operator~( ImageAspectFlagBits bits ) + { + return ~( ImageAspectFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ImageAspectFlagBits::eColor) | VkFlags(ImageAspectFlagBits::eDepth) | VkFlags(ImageAspectFlagBits::eStencil) | VkFlags(ImageAspectFlagBits::eMetadata) + }; + }; + + struct ImageSubresource + { + ImageSubresource( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t mipLevel_ = 0, uint32_t arrayLayer_ = 0 ) + : aspectMask( aspectMask_ ) + , mipLevel( mipLevel_ ) + , arrayLayer( arrayLayer_ ) + { + } + + ImageSubresource( VkImageSubresource const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageSubresource) ); + } + + ImageSubresource& operator=( VkImageSubresource const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageSubresource) ); + return *this; + } + + ImageSubresource& setAspectMask( ImageAspectFlags aspectMask_ ) + { + aspectMask = aspectMask_; + return *this; + } + + ImageSubresource& setMipLevel( uint32_t mipLevel_ ) + { + mipLevel = mipLevel_; + return *this; + } + + ImageSubresource& setArrayLayer( uint32_t arrayLayer_ ) + { + arrayLayer = arrayLayer_; + return *this; + } + + operator const VkImageSubresource&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageSubresource const& rhs ) const + { + return ( aspectMask == rhs.aspectMask ) + && ( mipLevel == rhs.mipLevel ) + && ( arrayLayer == rhs.arrayLayer ); + } + + bool operator!=( ImageSubresource const& rhs ) const + { + return !operator==( rhs ); + } + + ImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t arrayLayer; + }; + static_assert( sizeof( ImageSubresource ) == sizeof( VkImageSubresource ), "struct and wrapper have different size!" ); + + struct ImageSubresourceLayers + { + ImageSubresourceLayers( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t mipLevel_ = 0, uint32_t baseArrayLayer_ = 0, uint32_t layerCount_ = 0 ) + : aspectMask( aspectMask_ ) + , mipLevel( mipLevel_ ) + , baseArrayLayer( baseArrayLayer_ ) + , layerCount( layerCount_ ) + { + } + + ImageSubresourceLayers( VkImageSubresourceLayers const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageSubresourceLayers) ); + } + + ImageSubresourceLayers& operator=( VkImageSubresourceLayers const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageSubresourceLayers) ); + return *this; + } + + ImageSubresourceLayers& setAspectMask( ImageAspectFlags aspectMask_ ) + { + aspectMask = aspectMask_; + return *this; + } + + ImageSubresourceLayers& setMipLevel( uint32_t mipLevel_ ) + { + mipLevel = mipLevel_; + return *this; + } + + ImageSubresourceLayers& setBaseArrayLayer( uint32_t baseArrayLayer_ ) + { + baseArrayLayer = baseArrayLayer_; + return *this; + } + + ImageSubresourceLayers& setLayerCount( uint32_t layerCount_ ) + { + layerCount = layerCount_; + return *this; + } + + operator const VkImageSubresourceLayers&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageSubresourceLayers const& rhs ) const + { + return ( aspectMask == rhs.aspectMask ) + && ( mipLevel == rhs.mipLevel ) + && ( baseArrayLayer == rhs.baseArrayLayer ) + && ( layerCount == rhs.layerCount ); + } + + bool operator!=( ImageSubresourceLayers const& rhs ) const + { + return !operator==( rhs ); + } + + ImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t baseArrayLayer; + uint32_t layerCount; + }; + static_assert( sizeof( ImageSubresourceLayers ) == sizeof( VkImageSubresourceLayers ), "struct and wrapper have different size!" ); + + struct ImageSubresourceRange + { + ImageSubresourceRange( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t baseMipLevel_ = 0, uint32_t levelCount_ = 0, uint32_t baseArrayLayer_ = 0, uint32_t layerCount_ = 0 ) + : aspectMask( aspectMask_ ) + , baseMipLevel( baseMipLevel_ ) + , levelCount( levelCount_ ) + , baseArrayLayer( baseArrayLayer_ ) + , layerCount( layerCount_ ) + { + } + + ImageSubresourceRange( VkImageSubresourceRange const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageSubresourceRange) ); + } + + ImageSubresourceRange& operator=( VkImageSubresourceRange const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageSubresourceRange) ); + return *this; + } + + ImageSubresourceRange& setAspectMask( ImageAspectFlags aspectMask_ ) + { + aspectMask = aspectMask_; + return *this; + } + + ImageSubresourceRange& setBaseMipLevel( uint32_t baseMipLevel_ ) + { + baseMipLevel = baseMipLevel_; + return *this; + } + + ImageSubresourceRange& setLevelCount( uint32_t levelCount_ ) + { + levelCount = levelCount_; + return *this; + } + + ImageSubresourceRange& setBaseArrayLayer( uint32_t baseArrayLayer_ ) + { + baseArrayLayer = baseArrayLayer_; + return *this; + } + + ImageSubresourceRange& setLayerCount( uint32_t layerCount_ ) + { + layerCount = layerCount_; + return *this; + } + + operator const VkImageSubresourceRange&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageSubresourceRange const& rhs ) const + { + return ( aspectMask == rhs.aspectMask ) + && ( baseMipLevel == rhs.baseMipLevel ) + && ( levelCount == rhs.levelCount ) + && ( baseArrayLayer == rhs.baseArrayLayer ) + && ( layerCount == rhs.layerCount ); + } + + bool operator!=( ImageSubresourceRange const& rhs ) const + { + return !operator==( rhs ); + } + + ImageAspectFlags aspectMask; + uint32_t baseMipLevel; + uint32_t levelCount; + uint32_t baseArrayLayer; + uint32_t layerCount; + }; + static_assert( sizeof( ImageSubresourceRange ) == sizeof( VkImageSubresourceRange ), "struct and wrapper have different size!" ); + + struct ImageMemoryBarrier + { + ImageMemoryBarrier( AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags(), ImageLayout oldLayout_ = ImageLayout::eUndefined, ImageLayout newLayout_ = ImageLayout::eUndefined, uint32_t srcQueueFamilyIndex_ = 0, uint32_t dstQueueFamilyIndex_ = 0, Image image_ = Image(), ImageSubresourceRange subresourceRange_ = ImageSubresourceRange() ) + : sType( StructureType::eImageMemoryBarrier ) + , pNext( nullptr ) + , srcAccessMask( srcAccessMask_ ) + , dstAccessMask( dstAccessMask_ ) + , oldLayout( oldLayout_ ) + , newLayout( newLayout_ ) + , srcQueueFamilyIndex( srcQueueFamilyIndex_ ) + , dstQueueFamilyIndex( dstQueueFamilyIndex_ ) + , image( image_ ) + , subresourceRange( subresourceRange_ ) + { + } + + ImageMemoryBarrier( VkImageMemoryBarrier const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageMemoryBarrier) ); + } + + ImageMemoryBarrier& operator=( VkImageMemoryBarrier const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageMemoryBarrier) ); + return *this; + } + + ImageMemoryBarrier& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ImageMemoryBarrier& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ImageMemoryBarrier& setSrcAccessMask( AccessFlags srcAccessMask_ ) + { + srcAccessMask = srcAccessMask_; + return *this; + } + + ImageMemoryBarrier& setDstAccessMask( AccessFlags dstAccessMask_ ) + { + dstAccessMask = dstAccessMask_; + return *this; + } + + ImageMemoryBarrier& setOldLayout( ImageLayout oldLayout_ ) + { + oldLayout = oldLayout_; + return *this; + } + + ImageMemoryBarrier& setNewLayout( ImageLayout newLayout_ ) + { + newLayout = newLayout_; + return *this; + } + + ImageMemoryBarrier& setSrcQueueFamilyIndex( uint32_t srcQueueFamilyIndex_ ) + { + srcQueueFamilyIndex = srcQueueFamilyIndex_; + return *this; + } + + ImageMemoryBarrier& setDstQueueFamilyIndex( uint32_t dstQueueFamilyIndex_ ) + { + dstQueueFamilyIndex = dstQueueFamilyIndex_; + return *this; + } + + ImageMemoryBarrier& setImage( Image image_ ) + { + image = image_; + return *this; + } + + ImageMemoryBarrier& setSubresourceRange( ImageSubresourceRange subresourceRange_ ) + { + subresourceRange = subresourceRange_; + return *this; + } + + operator const VkImageMemoryBarrier&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageMemoryBarrier const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( srcAccessMask == rhs.srcAccessMask ) + && ( dstAccessMask == rhs.dstAccessMask ) + && ( oldLayout == rhs.oldLayout ) + && ( newLayout == rhs.newLayout ) + && ( srcQueueFamilyIndex == rhs.srcQueueFamilyIndex ) + && ( dstQueueFamilyIndex == rhs.dstQueueFamilyIndex ) + && ( image == rhs.image ) + && ( subresourceRange == rhs.subresourceRange ); + } + + bool operator!=( ImageMemoryBarrier const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + AccessFlags srcAccessMask; + AccessFlags dstAccessMask; + ImageLayout oldLayout; + ImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + Image image; + ImageSubresourceRange subresourceRange; + }; + static_assert( sizeof( ImageMemoryBarrier ) == sizeof( VkImageMemoryBarrier ), "struct and wrapper have different size!" ); + + struct ImageViewCreateInfo + { + ImageViewCreateInfo( ImageViewCreateFlags flags_ = ImageViewCreateFlags(), Image image_ = Image(), ImageViewType viewType_ = ImageViewType::e1D, Format format_ = Format::eUndefined, ComponentMapping components_ = ComponentMapping(), ImageSubresourceRange subresourceRange_ = ImageSubresourceRange() ) + : sType( StructureType::eImageViewCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , image( image_ ) + , viewType( viewType_ ) + , format( format_ ) + , components( components_ ) + , subresourceRange( subresourceRange_ ) + { + } + + ImageViewCreateInfo( VkImageViewCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageViewCreateInfo) ); + } + + ImageViewCreateInfo& operator=( VkImageViewCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageViewCreateInfo) ); + return *this; + } + + ImageViewCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ImageViewCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ImageViewCreateInfo& setFlags( ImageViewCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + ImageViewCreateInfo& setImage( Image image_ ) + { + image = image_; + return *this; + } + + ImageViewCreateInfo& setViewType( ImageViewType viewType_ ) + { + viewType = viewType_; + return *this; + } + + ImageViewCreateInfo& setFormat( Format format_ ) + { + format = format_; + return *this; + } + + ImageViewCreateInfo& setComponents( ComponentMapping components_ ) + { + components = components_; + return *this; + } + + ImageViewCreateInfo& setSubresourceRange( ImageSubresourceRange subresourceRange_ ) + { + subresourceRange = subresourceRange_; + return *this; + } + + operator const VkImageViewCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageViewCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( image == rhs.image ) + && ( viewType == rhs.viewType ) + && ( format == rhs.format ) + && ( components == rhs.components ) + && ( subresourceRange == rhs.subresourceRange ); + } + + bool operator!=( ImageViewCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ImageViewCreateFlags flags; + Image image; + ImageViewType viewType; + Format format; + ComponentMapping components; + ImageSubresourceRange subresourceRange; + }; + static_assert( sizeof( ImageViewCreateInfo ) == sizeof( VkImageViewCreateInfo ), "struct and wrapper have different size!" ); + + struct ImageCopy + { + ImageCopy( ImageSubresourceLayers srcSubresource_ = ImageSubresourceLayers(), Offset3D srcOffset_ = Offset3D(), ImageSubresourceLayers dstSubresource_ = ImageSubresourceLayers(), Offset3D dstOffset_ = Offset3D(), Extent3D extent_ = Extent3D() ) + : srcSubresource( srcSubresource_ ) + , srcOffset( srcOffset_ ) + , dstSubresource( dstSubresource_ ) + , dstOffset( dstOffset_ ) + , extent( extent_ ) + { + } + + ImageCopy( VkImageCopy const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageCopy) ); + } + + ImageCopy& operator=( VkImageCopy const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageCopy) ); + return *this; + } + + ImageCopy& setSrcSubresource( ImageSubresourceLayers srcSubresource_ ) + { + srcSubresource = srcSubresource_; + return *this; + } + + ImageCopy& setSrcOffset( Offset3D srcOffset_ ) + { + srcOffset = srcOffset_; + return *this; + } + + ImageCopy& setDstSubresource( ImageSubresourceLayers dstSubresource_ ) + { + dstSubresource = dstSubresource_; + return *this; + } + + ImageCopy& setDstOffset( Offset3D dstOffset_ ) + { + dstOffset = dstOffset_; + return *this; + } + + ImageCopy& setExtent( Extent3D extent_ ) + { + extent = extent_; + return *this; + } + + operator const VkImageCopy&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageCopy const& rhs ) const + { + return ( srcSubresource == rhs.srcSubresource ) + && ( srcOffset == rhs.srcOffset ) + && ( dstSubresource == rhs.dstSubresource ) + && ( dstOffset == rhs.dstOffset ) + && ( extent == rhs.extent ); + } + + bool operator!=( ImageCopy const& rhs ) const + { + return !operator==( rhs ); + } + + ImageSubresourceLayers srcSubresource; + Offset3D srcOffset; + ImageSubresourceLayers dstSubresource; + Offset3D dstOffset; + Extent3D extent; + }; + static_assert( sizeof( ImageCopy ) == sizeof( VkImageCopy ), "struct and wrapper have different size!" ); + + struct ImageBlit + { + ImageBlit( ImageSubresourceLayers srcSubresource_ = ImageSubresourceLayers(), std::array const& srcOffsets_ = { { Offset3D(), Offset3D() } }, ImageSubresourceLayers dstSubresource_ = ImageSubresourceLayers(), std::array const& dstOffsets_ = { { Offset3D(), Offset3D() } } ) + : srcSubresource( srcSubresource_ ) + , dstSubresource( dstSubresource_ ) + { + memcpy( &srcOffsets, srcOffsets_.data(), 2 * sizeof( Offset3D ) ); + memcpy( &dstOffsets, dstOffsets_.data(), 2 * sizeof( Offset3D ) ); + } + + ImageBlit( VkImageBlit const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageBlit) ); + } + + ImageBlit& operator=( VkImageBlit const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageBlit) ); + return *this; + } + + ImageBlit& setSrcSubresource( ImageSubresourceLayers srcSubresource_ ) + { + srcSubresource = srcSubresource_; + return *this; + } + + ImageBlit& setSrcOffsets( std::array srcOffsets_ ) + { + memcpy( &srcOffsets, srcOffsets_.data(), 2 * sizeof( Offset3D ) ); + return *this; + } + + ImageBlit& setDstSubresource( ImageSubresourceLayers dstSubresource_ ) + { + dstSubresource = dstSubresource_; + return *this; + } + + ImageBlit& setDstOffsets( std::array dstOffsets_ ) + { + memcpy( &dstOffsets, dstOffsets_.data(), 2 * sizeof( Offset3D ) ); + return *this; + } + + operator const VkImageBlit&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageBlit const& rhs ) const + { + return ( srcSubresource == rhs.srcSubresource ) + && ( memcmp( srcOffsets, rhs.srcOffsets, 2 * sizeof( Offset3D ) ) == 0 ) + && ( dstSubresource == rhs.dstSubresource ) + && ( memcmp( dstOffsets, rhs.dstOffsets, 2 * sizeof( Offset3D ) ) == 0 ); + } + + bool operator!=( ImageBlit const& rhs ) const + { + return !operator==( rhs ); + } + + ImageSubresourceLayers srcSubresource; + Offset3D srcOffsets[2]; + ImageSubresourceLayers dstSubresource; + Offset3D dstOffsets[2]; + }; + static_assert( sizeof( ImageBlit ) == sizeof( VkImageBlit ), "struct and wrapper have different size!" ); + + struct BufferImageCopy + { + BufferImageCopy( DeviceSize bufferOffset_ = 0, uint32_t bufferRowLength_ = 0, uint32_t bufferImageHeight_ = 0, ImageSubresourceLayers imageSubresource_ = ImageSubresourceLayers(), Offset3D imageOffset_ = Offset3D(), Extent3D imageExtent_ = Extent3D() ) + : bufferOffset( bufferOffset_ ) + , bufferRowLength( bufferRowLength_ ) + , bufferImageHeight( bufferImageHeight_ ) + , imageSubresource( imageSubresource_ ) + , imageOffset( imageOffset_ ) + , imageExtent( imageExtent_ ) + { + } + + BufferImageCopy( VkBufferImageCopy const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferImageCopy) ); + } + + BufferImageCopy& operator=( VkBufferImageCopy const & rhs ) + { + memcpy( this, &rhs, sizeof(BufferImageCopy) ); + return *this; + } + + BufferImageCopy& setBufferOffset( DeviceSize bufferOffset_ ) + { + bufferOffset = bufferOffset_; + return *this; + } + + BufferImageCopy& setBufferRowLength( uint32_t bufferRowLength_ ) + { + bufferRowLength = bufferRowLength_; + return *this; + } + + BufferImageCopy& setBufferImageHeight( uint32_t bufferImageHeight_ ) + { + bufferImageHeight = bufferImageHeight_; + return *this; + } + + BufferImageCopy& setImageSubresource( ImageSubresourceLayers imageSubresource_ ) + { + imageSubresource = imageSubresource_; + return *this; + } + + BufferImageCopy& setImageOffset( Offset3D imageOffset_ ) + { + imageOffset = imageOffset_; + return *this; + } + + BufferImageCopy& setImageExtent( Extent3D imageExtent_ ) + { + imageExtent = imageExtent_; + return *this; + } + + operator const VkBufferImageCopy&() const + { + return *reinterpret_cast(this); + } + + bool operator==( BufferImageCopy const& rhs ) const + { + return ( bufferOffset == rhs.bufferOffset ) + && ( bufferRowLength == rhs.bufferRowLength ) + && ( bufferImageHeight == rhs.bufferImageHeight ) + && ( imageSubresource == rhs.imageSubresource ) + && ( imageOffset == rhs.imageOffset ) + && ( imageExtent == rhs.imageExtent ); + } + + bool operator!=( BufferImageCopy const& rhs ) const + { + return !operator==( rhs ); + } + + DeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + ImageSubresourceLayers imageSubresource; + Offset3D imageOffset; + Extent3D imageExtent; + }; + static_assert( sizeof( BufferImageCopy ) == sizeof( VkBufferImageCopy ), "struct and wrapper have different size!" ); + + struct ImageResolve + { + ImageResolve( ImageSubresourceLayers srcSubresource_ = ImageSubresourceLayers(), Offset3D srcOffset_ = Offset3D(), ImageSubresourceLayers dstSubresource_ = ImageSubresourceLayers(), Offset3D dstOffset_ = Offset3D(), Extent3D extent_ = Extent3D() ) + : srcSubresource( srcSubresource_ ) + , srcOffset( srcOffset_ ) + , dstSubresource( dstSubresource_ ) + , dstOffset( dstOffset_ ) + , extent( extent_ ) + { + } + + ImageResolve( VkImageResolve const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageResolve) ); + } + + ImageResolve& operator=( VkImageResolve const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageResolve) ); + return *this; + } + + ImageResolve& setSrcSubresource( ImageSubresourceLayers srcSubresource_ ) + { + srcSubresource = srcSubresource_; + return *this; + } + + ImageResolve& setSrcOffset( Offset3D srcOffset_ ) + { + srcOffset = srcOffset_; + return *this; + } + + ImageResolve& setDstSubresource( ImageSubresourceLayers dstSubresource_ ) + { + dstSubresource = dstSubresource_; + return *this; + } + + ImageResolve& setDstOffset( Offset3D dstOffset_ ) + { + dstOffset = dstOffset_; + return *this; + } + + ImageResolve& setExtent( Extent3D extent_ ) + { + extent = extent_; + return *this; + } + + operator const VkImageResolve&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageResolve const& rhs ) const + { + return ( srcSubresource == rhs.srcSubresource ) + && ( srcOffset == rhs.srcOffset ) + && ( dstSubresource == rhs.dstSubresource ) + && ( dstOffset == rhs.dstOffset ) + && ( extent == rhs.extent ); + } + + bool operator!=( ImageResolve const& rhs ) const + { + return !operator==( rhs ); + } + + ImageSubresourceLayers srcSubresource; + Offset3D srcOffset; + ImageSubresourceLayers dstSubresource; + Offset3D dstOffset; + Extent3D extent; + }; + static_assert( sizeof( ImageResolve ) == sizeof( VkImageResolve ), "struct and wrapper have different size!" ); + + struct ClearAttachment + { + ClearAttachment( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t colorAttachment_ = 0, ClearValue clearValue_ = ClearValue() ) + : aspectMask( aspectMask_ ) + , colorAttachment( colorAttachment_ ) + , clearValue( clearValue_ ) + { + } + + ClearAttachment( VkClearAttachment const & rhs ) + { + memcpy( this, &rhs, sizeof(ClearAttachment) ); + } + + ClearAttachment& operator=( VkClearAttachment const & rhs ) + { + memcpy( this, &rhs, sizeof(ClearAttachment) ); + return *this; + } + + ClearAttachment& setAspectMask( ImageAspectFlags aspectMask_ ) + { + aspectMask = aspectMask_; + return *this; + } + + ClearAttachment& setColorAttachment( uint32_t colorAttachment_ ) + { + colorAttachment = colorAttachment_; + return *this; + } + + ClearAttachment& setClearValue( ClearValue clearValue_ ) + { + clearValue = clearValue_; + return *this; + } + + operator const VkClearAttachment&() const + { + return *reinterpret_cast(this); + } + + ImageAspectFlags aspectMask; + uint32_t colorAttachment; + ClearValue clearValue; + }; + static_assert( sizeof( ClearAttachment ) == sizeof( VkClearAttachment ), "struct and wrapper have different size!" ); + + enum class SparseImageFormatFlagBits + { + eSingleMiptail = VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT, + eAlignedMipSize = VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT, + eNonstandardBlockSize = VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT + }; + + using SparseImageFormatFlags = Flags; + + VULKAN_HPP_INLINE SparseImageFormatFlags operator|( SparseImageFormatFlagBits bit0, SparseImageFormatFlagBits bit1 ) + { + return SparseImageFormatFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE SparseImageFormatFlags operator~( SparseImageFormatFlagBits bits ) + { + return ~( SparseImageFormatFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(SparseImageFormatFlagBits::eSingleMiptail) | VkFlags(SparseImageFormatFlagBits::eAlignedMipSize) | VkFlags(SparseImageFormatFlagBits::eNonstandardBlockSize) + }; + }; + + struct SparseImageFormatProperties + { + operator const VkSparseImageFormatProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseImageFormatProperties const& rhs ) const + { + return ( aspectMask == rhs.aspectMask ) + && ( imageGranularity == rhs.imageGranularity ) + && ( flags == rhs.flags ); + } + + bool operator!=( SparseImageFormatProperties const& rhs ) const + { + return !operator==( rhs ); + } + + ImageAspectFlags aspectMask; + Extent3D imageGranularity; + SparseImageFormatFlags flags; + }; + static_assert( sizeof( SparseImageFormatProperties ) == sizeof( VkSparseImageFormatProperties ), "struct and wrapper have different size!" ); + + struct SparseImageMemoryRequirements + { + operator const VkSparseImageMemoryRequirements&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseImageMemoryRequirements const& rhs ) const + { + return ( formatProperties == rhs.formatProperties ) + && ( imageMipTailFirstLod == rhs.imageMipTailFirstLod ) + && ( imageMipTailSize == rhs.imageMipTailSize ) + && ( imageMipTailOffset == rhs.imageMipTailOffset ) + && ( imageMipTailStride == rhs.imageMipTailStride ); + } + + bool operator!=( SparseImageMemoryRequirements const& rhs ) const + { + return !operator==( rhs ); + } + + SparseImageFormatProperties formatProperties; + uint32_t imageMipTailFirstLod; + DeviceSize imageMipTailSize; + DeviceSize imageMipTailOffset; + DeviceSize imageMipTailStride; + }; + static_assert( sizeof( SparseImageMemoryRequirements ) == sizeof( VkSparseImageMemoryRequirements ), "struct and wrapper have different size!" ); + + struct SparseImageFormatProperties2KHR + { + operator const VkSparseImageFormatProperties2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseImageFormatProperties2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( properties == rhs.properties ); + } + + bool operator!=( SparseImageFormatProperties2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + SparseImageFormatProperties properties; + }; + static_assert( sizeof( SparseImageFormatProperties2KHR ) == sizeof( VkSparseImageFormatProperties2KHR ), "struct and wrapper have different size!" ); + + enum class SparseMemoryBindFlagBits + { + eMetadata = VK_SPARSE_MEMORY_BIND_METADATA_BIT + }; + + using SparseMemoryBindFlags = Flags; + + VULKAN_HPP_INLINE SparseMemoryBindFlags operator|( SparseMemoryBindFlagBits bit0, SparseMemoryBindFlagBits bit1 ) + { + return SparseMemoryBindFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE SparseMemoryBindFlags operator~( SparseMemoryBindFlagBits bits ) + { + return ~( SparseMemoryBindFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(SparseMemoryBindFlagBits::eMetadata) + }; + }; + + struct SparseMemoryBind + { + SparseMemoryBind( DeviceSize resourceOffset_ = 0, DeviceSize size_ = 0, DeviceMemory memory_ = DeviceMemory(), DeviceSize memoryOffset_ = 0, SparseMemoryBindFlags flags_ = SparseMemoryBindFlags() ) + : resourceOffset( resourceOffset_ ) + , size( size_ ) + , memory( memory_ ) + , memoryOffset( memoryOffset_ ) + , flags( flags_ ) + { + } + + SparseMemoryBind( VkSparseMemoryBind const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseMemoryBind) ); + } + + SparseMemoryBind& operator=( VkSparseMemoryBind const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseMemoryBind) ); + return *this; + } + + SparseMemoryBind& setResourceOffset( DeviceSize resourceOffset_ ) + { + resourceOffset = resourceOffset_; + return *this; + } + + SparseMemoryBind& setSize( DeviceSize size_ ) + { + size = size_; + return *this; + } + + SparseMemoryBind& setMemory( DeviceMemory memory_ ) + { + memory = memory_; + return *this; + } + + SparseMemoryBind& setMemoryOffset( DeviceSize memoryOffset_ ) + { + memoryOffset = memoryOffset_; + return *this; + } + + SparseMemoryBind& setFlags( SparseMemoryBindFlags flags_ ) + { + flags = flags_; + return *this; + } + + operator const VkSparseMemoryBind&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseMemoryBind const& rhs ) const + { + return ( resourceOffset == rhs.resourceOffset ) + && ( size == rhs.size ) + && ( memory == rhs.memory ) + && ( memoryOffset == rhs.memoryOffset ) + && ( flags == rhs.flags ); + } + + bool operator!=( SparseMemoryBind const& rhs ) const + { + return !operator==( rhs ); + } + + DeviceSize resourceOffset; + DeviceSize size; + DeviceMemory memory; + DeviceSize memoryOffset; + SparseMemoryBindFlags flags; + }; + static_assert( sizeof( SparseMemoryBind ) == sizeof( VkSparseMemoryBind ), "struct and wrapper have different size!" ); + + struct SparseImageMemoryBind + { + SparseImageMemoryBind( ImageSubresource subresource_ = ImageSubresource(), Offset3D offset_ = Offset3D(), Extent3D extent_ = Extent3D(), DeviceMemory memory_ = DeviceMemory(), DeviceSize memoryOffset_ = 0, SparseMemoryBindFlags flags_ = SparseMemoryBindFlags() ) + : subresource( subresource_ ) + , offset( offset_ ) + , extent( extent_ ) + , memory( memory_ ) + , memoryOffset( memoryOffset_ ) + , flags( flags_ ) + { + } + + SparseImageMemoryBind( VkSparseImageMemoryBind const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseImageMemoryBind) ); + } + + SparseImageMemoryBind& operator=( VkSparseImageMemoryBind const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseImageMemoryBind) ); + return *this; + } + + SparseImageMemoryBind& setSubresource( ImageSubresource subresource_ ) + { + subresource = subresource_; + return *this; + } + + SparseImageMemoryBind& setOffset( Offset3D offset_ ) + { + offset = offset_; + return *this; + } + + SparseImageMemoryBind& setExtent( Extent3D extent_ ) + { + extent = extent_; + return *this; + } + + SparseImageMemoryBind& setMemory( DeviceMemory memory_ ) + { + memory = memory_; + return *this; + } + + SparseImageMemoryBind& setMemoryOffset( DeviceSize memoryOffset_ ) + { + memoryOffset = memoryOffset_; + return *this; + } + + SparseImageMemoryBind& setFlags( SparseMemoryBindFlags flags_ ) + { + flags = flags_; + return *this; + } + + operator const VkSparseImageMemoryBind&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseImageMemoryBind const& rhs ) const + { + return ( subresource == rhs.subresource ) + && ( offset == rhs.offset ) + && ( extent == rhs.extent ) + && ( memory == rhs.memory ) + && ( memoryOffset == rhs.memoryOffset ) + && ( flags == rhs.flags ); + } + + bool operator!=( SparseImageMemoryBind const& rhs ) const + { + return !operator==( rhs ); + } + + ImageSubresource subresource; + Offset3D offset; + Extent3D extent; + DeviceMemory memory; + DeviceSize memoryOffset; + SparseMemoryBindFlags flags; + }; + static_assert( sizeof( SparseImageMemoryBind ) == sizeof( VkSparseImageMemoryBind ), "struct and wrapper have different size!" ); + + struct SparseBufferMemoryBindInfo + { + SparseBufferMemoryBindInfo( Buffer buffer_ = Buffer(), uint32_t bindCount_ = 0, const SparseMemoryBind* pBinds_ = nullptr ) + : buffer( buffer_ ) + , bindCount( bindCount_ ) + , pBinds( pBinds_ ) + { + } + + SparseBufferMemoryBindInfo( VkSparseBufferMemoryBindInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseBufferMemoryBindInfo) ); + } + + SparseBufferMemoryBindInfo& operator=( VkSparseBufferMemoryBindInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseBufferMemoryBindInfo) ); + return *this; + } + + SparseBufferMemoryBindInfo& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + SparseBufferMemoryBindInfo& setBindCount( uint32_t bindCount_ ) + { + bindCount = bindCount_; + return *this; + } + + SparseBufferMemoryBindInfo& setPBinds( const SparseMemoryBind* pBinds_ ) + { + pBinds = pBinds_; + return *this; + } + + operator const VkSparseBufferMemoryBindInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseBufferMemoryBindInfo const& rhs ) const + { + return ( buffer == rhs.buffer ) + && ( bindCount == rhs.bindCount ) + && ( pBinds == rhs.pBinds ); + } + + bool operator!=( SparseBufferMemoryBindInfo const& rhs ) const + { + return !operator==( rhs ); + } + + Buffer buffer; + uint32_t bindCount; + const SparseMemoryBind* pBinds; + }; + static_assert( sizeof( SparseBufferMemoryBindInfo ) == sizeof( VkSparseBufferMemoryBindInfo ), "struct and wrapper have different size!" ); + + struct SparseImageOpaqueMemoryBindInfo + { + SparseImageOpaqueMemoryBindInfo( Image image_ = Image(), uint32_t bindCount_ = 0, const SparseMemoryBind* pBinds_ = nullptr ) + : image( image_ ) + , bindCount( bindCount_ ) + , pBinds( pBinds_ ) + { + } + + SparseImageOpaqueMemoryBindInfo( VkSparseImageOpaqueMemoryBindInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseImageOpaqueMemoryBindInfo) ); + } + + SparseImageOpaqueMemoryBindInfo& operator=( VkSparseImageOpaqueMemoryBindInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseImageOpaqueMemoryBindInfo) ); + return *this; + } + + SparseImageOpaqueMemoryBindInfo& setImage( Image image_ ) + { + image = image_; + return *this; + } + + SparseImageOpaqueMemoryBindInfo& setBindCount( uint32_t bindCount_ ) + { + bindCount = bindCount_; + return *this; + } + + SparseImageOpaqueMemoryBindInfo& setPBinds( const SparseMemoryBind* pBinds_ ) + { + pBinds = pBinds_; + return *this; + } + + operator const VkSparseImageOpaqueMemoryBindInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseImageOpaqueMemoryBindInfo const& rhs ) const + { + return ( image == rhs.image ) + && ( bindCount == rhs.bindCount ) + && ( pBinds == rhs.pBinds ); + } + + bool operator!=( SparseImageOpaqueMemoryBindInfo const& rhs ) const + { + return !operator==( rhs ); + } + + Image image; + uint32_t bindCount; + const SparseMemoryBind* pBinds; + }; + static_assert( sizeof( SparseImageOpaqueMemoryBindInfo ) == sizeof( VkSparseImageOpaqueMemoryBindInfo ), "struct and wrapper have different size!" ); + + struct SparseImageMemoryBindInfo + { + SparseImageMemoryBindInfo( Image image_ = Image(), uint32_t bindCount_ = 0, const SparseImageMemoryBind* pBinds_ = nullptr ) + : image( image_ ) + , bindCount( bindCount_ ) + , pBinds( pBinds_ ) + { + } + + SparseImageMemoryBindInfo( VkSparseImageMemoryBindInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseImageMemoryBindInfo) ); + } + + SparseImageMemoryBindInfo& operator=( VkSparseImageMemoryBindInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SparseImageMemoryBindInfo) ); + return *this; + } + + SparseImageMemoryBindInfo& setImage( Image image_ ) + { + image = image_; + return *this; + } + + SparseImageMemoryBindInfo& setBindCount( uint32_t bindCount_ ) + { + bindCount = bindCount_; + return *this; + } + + SparseImageMemoryBindInfo& setPBinds( const SparseImageMemoryBind* pBinds_ ) + { + pBinds = pBinds_; + return *this; + } + + operator const VkSparseImageMemoryBindInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SparseImageMemoryBindInfo const& rhs ) const + { + return ( image == rhs.image ) + && ( bindCount == rhs.bindCount ) + && ( pBinds == rhs.pBinds ); + } + + bool operator!=( SparseImageMemoryBindInfo const& rhs ) const + { + return !operator==( rhs ); + } + + Image image; + uint32_t bindCount; + const SparseImageMemoryBind* pBinds; + }; + static_assert( sizeof( SparseImageMemoryBindInfo ) == sizeof( VkSparseImageMemoryBindInfo ), "struct and wrapper have different size!" ); + + struct BindSparseInfo + { + BindSparseInfo( uint32_t waitSemaphoreCount_ = 0, const Semaphore* pWaitSemaphores_ = nullptr, uint32_t bufferBindCount_ = 0, const SparseBufferMemoryBindInfo* pBufferBinds_ = nullptr, uint32_t imageOpaqueBindCount_ = 0, const SparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds_ = nullptr, uint32_t imageBindCount_ = 0, const SparseImageMemoryBindInfo* pImageBinds_ = nullptr, uint32_t signalSemaphoreCount_ = 0, const Semaphore* pSignalSemaphores_ = nullptr ) + : sType( StructureType::eBindSparseInfo ) + , pNext( nullptr ) + , waitSemaphoreCount( waitSemaphoreCount_ ) + , pWaitSemaphores( pWaitSemaphores_ ) + , bufferBindCount( bufferBindCount_ ) + , pBufferBinds( pBufferBinds_ ) + , imageOpaqueBindCount( imageOpaqueBindCount_ ) + , pImageOpaqueBinds( pImageOpaqueBinds_ ) + , imageBindCount( imageBindCount_ ) + , pImageBinds( pImageBinds_ ) + , signalSemaphoreCount( signalSemaphoreCount_ ) + , pSignalSemaphores( pSignalSemaphores_ ) + { + } + + BindSparseInfo( VkBindSparseInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(BindSparseInfo) ); + } + + BindSparseInfo& operator=( VkBindSparseInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(BindSparseInfo) ); + return *this; + } + + BindSparseInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + BindSparseInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + BindSparseInfo& setWaitSemaphoreCount( uint32_t waitSemaphoreCount_ ) + { + waitSemaphoreCount = waitSemaphoreCount_; + return *this; + } + + BindSparseInfo& setPWaitSemaphores( const Semaphore* pWaitSemaphores_ ) + { + pWaitSemaphores = pWaitSemaphores_; + return *this; + } + + BindSparseInfo& setBufferBindCount( uint32_t bufferBindCount_ ) + { + bufferBindCount = bufferBindCount_; + return *this; + } + + BindSparseInfo& setPBufferBinds( const SparseBufferMemoryBindInfo* pBufferBinds_ ) + { + pBufferBinds = pBufferBinds_; + return *this; + } + + BindSparseInfo& setImageOpaqueBindCount( uint32_t imageOpaqueBindCount_ ) + { + imageOpaqueBindCount = imageOpaqueBindCount_; + return *this; + } + + BindSparseInfo& setPImageOpaqueBinds( const SparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds_ ) + { + pImageOpaqueBinds = pImageOpaqueBinds_; + return *this; + } + + BindSparseInfo& setImageBindCount( uint32_t imageBindCount_ ) + { + imageBindCount = imageBindCount_; + return *this; + } + + BindSparseInfo& setPImageBinds( const SparseImageMemoryBindInfo* pImageBinds_ ) + { + pImageBinds = pImageBinds_; + return *this; + } + + BindSparseInfo& setSignalSemaphoreCount( uint32_t signalSemaphoreCount_ ) + { + signalSemaphoreCount = signalSemaphoreCount_; + return *this; + } + + BindSparseInfo& setPSignalSemaphores( const Semaphore* pSignalSemaphores_ ) + { + pSignalSemaphores = pSignalSemaphores_; + return *this; + } + + operator const VkBindSparseInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( BindSparseInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( waitSemaphoreCount == rhs.waitSemaphoreCount ) + && ( pWaitSemaphores == rhs.pWaitSemaphores ) + && ( bufferBindCount == rhs.bufferBindCount ) + && ( pBufferBinds == rhs.pBufferBinds ) + && ( imageOpaqueBindCount == rhs.imageOpaqueBindCount ) + && ( pImageOpaqueBinds == rhs.pImageOpaqueBinds ) + && ( imageBindCount == rhs.imageBindCount ) + && ( pImageBinds == rhs.pImageBinds ) + && ( signalSemaphoreCount == rhs.signalSemaphoreCount ) + && ( pSignalSemaphores == rhs.pSignalSemaphores ); + } + + bool operator!=( BindSparseInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + uint32_t waitSemaphoreCount; + const Semaphore* pWaitSemaphores; + uint32_t bufferBindCount; + const SparseBufferMemoryBindInfo* pBufferBinds; + uint32_t imageOpaqueBindCount; + const SparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds; + uint32_t imageBindCount; + const SparseImageMemoryBindInfo* pImageBinds; + uint32_t signalSemaphoreCount; + const Semaphore* pSignalSemaphores; + }; + static_assert( sizeof( BindSparseInfo ) == sizeof( VkBindSparseInfo ), "struct and wrapper have different size!" ); + + enum class PipelineStageFlagBits + { + eTopOfPipe = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + eDrawIndirect = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, + eVertexInput = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, + eVertexShader = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + eTessellationControlShader = VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT, + eTessellationEvaluationShader = VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, + eGeometryShader = VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT, + eFragmentShader = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + eEarlyFragmentTests = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + eLateFragmentTests = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + eColorAttachmentOutput = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + eComputeShader = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + eTransfer = VK_PIPELINE_STAGE_TRANSFER_BIT, + eBottomOfPipe = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + eHost = VK_PIPELINE_STAGE_HOST_BIT, + eAllGraphics = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + eAllCommands = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + eCommandProcessNVX = VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX + }; + + using PipelineStageFlags = Flags; + + VULKAN_HPP_INLINE PipelineStageFlags operator|( PipelineStageFlagBits bit0, PipelineStageFlagBits bit1 ) + { + return PipelineStageFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE PipelineStageFlags operator~( PipelineStageFlagBits bits ) + { + return ~( PipelineStageFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(PipelineStageFlagBits::eTopOfPipe) | VkFlags(PipelineStageFlagBits::eDrawIndirect) | VkFlags(PipelineStageFlagBits::eVertexInput) | VkFlags(PipelineStageFlagBits::eVertexShader) | VkFlags(PipelineStageFlagBits::eTessellationControlShader) | VkFlags(PipelineStageFlagBits::eTessellationEvaluationShader) | VkFlags(PipelineStageFlagBits::eGeometryShader) | VkFlags(PipelineStageFlagBits::eFragmentShader) | VkFlags(PipelineStageFlagBits::eEarlyFragmentTests) | VkFlags(PipelineStageFlagBits::eLateFragmentTests) | VkFlags(PipelineStageFlagBits::eColorAttachmentOutput) | VkFlags(PipelineStageFlagBits::eComputeShader) | VkFlags(PipelineStageFlagBits::eTransfer) | VkFlags(PipelineStageFlagBits::eBottomOfPipe) | VkFlags(PipelineStageFlagBits::eHost) | VkFlags(PipelineStageFlagBits::eAllGraphics) | VkFlags(PipelineStageFlagBits::eAllCommands) | VkFlags(PipelineStageFlagBits::eCommandProcessNVX) + }; + }; + + enum class CommandPoolCreateFlagBits + { + eTransient = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + eResetCommandBuffer = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT + }; + + using CommandPoolCreateFlags = Flags; + + VULKAN_HPP_INLINE CommandPoolCreateFlags operator|( CommandPoolCreateFlagBits bit0, CommandPoolCreateFlagBits bit1 ) + { + return CommandPoolCreateFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE CommandPoolCreateFlags operator~( CommandPoolCreateFlagBits bits ) + { + return ~( CommandPoolCreateFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(CommandPoolCreateFlagBits::eTransient) | VkFlags(CommandPoolCreateFlagBits::eResetCommandBuffer) + }; + }; + + struct CommandPoolCreateInfo + { + CommandPoolCreateInfo( CommandPoolCreateFlags flags_ = CommandPoolCreateFlags(), uint32_t queueFamilyIndex_ = 0 ) + : sType( StructureType::eCommandPoolCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , queueFamilyIndex( queueFamilyIndex_ ) + { + } + + CommandPoolCreateInfo( VkCommandPoolCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandPoolCreateInfo) ); + } + + CommandPoolCreateInfo& operator=( VkCommandPoolCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(CommandPoolCreateInfo) ); + return *this; + } + + CommandPoolCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + CommandPoolCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + CommandPoolCreateInfo& setFlags( CommandPoolCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + CommandPoolCreateInfo& setQueueFamilyIndex( uint32_t queueFamilyIndex_ ) + { + queueFamilyIndex = queueFamilyIndex_; + return *this; + } + + operator const VkCommandPoolCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( CommandPoolCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( queueFamilyIndex == rhs.queueFamilyIndex ); + } + + bool operator!=( CommandPoolCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + CommandPoolCreateFlags flags; + uint32_t queueFamilyIndex; + }; + static_assert( sizeof( CommandPoolCreateInfo ) == sizeof( VkCommandPoolCreateInfo ), "struct and wrapper have different size!" ); + + enum class CommandPoolResetFlagBits + { + eReleaseResources = VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT + }; + + using CommandPoolResetFlags = Flags; + + VULKAN_HPP_INLINE CommandPoolResetFlags operator|( CommandPoolResetFlagBits bit0, CommandPoolResetFlagBits bit1 ) + { + return CommandPoolResetFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE CommandPoolResetFlags operator~( CommandPoolResetFlagBits bits ) + { + return ~( CommandPoolResetFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(CommandPoolResetFlagBits::eReleaseResources) + }; + }; + + enum class CommandBufferResetFlagBits + { + eReleaseResources = VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT + }; + + using CommandBufferResetFlags = Flags; + + VULKAN_HPP_INLINE CommandBufferResetFlags operator|( CommandBufferResetFlagBits bit0, CommandBufferResetFlagBits bit1 ) + { + return CommandBufferResetFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE CommandBufferResetFlags operator~( CommandBufferResetFlagBits bits ) + { + return ~( CommandBufferResetFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(CommandBufferResetFlagBits::eReleaseResources) + }; + }; + + enum class SampleCountFlagBits + { + e1 = VK_SAMPLE_COUNT_1_BIT, + e2 = VK_SAMPLE_COUNT_2_BIT, + e4 = VK_SAMPLE_COUNT_4_BIT, + e8 = VK_SAMPLE_COUNT_8_BIT, + e16 = VK_SAMPLE_COUNT_16_BIT, + e32 = VK_SAMPLE_COUNT_32_BIT, + e64 = VK_SAMPLE_COUNT_64_BIT + }; + + using SampleCountFlags = Flags; + + VULKAN_HPP_INLINE SampleCountFlags operator|( SampleCountFlagBits bit0, SampleCountFlagBits bit1 ) + { + return SampleCountFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE SampleCountFlags operator~( SampleCountFlagBits bits ) + { + return ~( SampleCountFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(SampleCountFlagBits::e1) | VkFlags(SampleCountFlagBits::e2) | VkFlags(SampleCountFlagBits::e4) | VkFlags(SampleCountFlagBits::e8) | VkFlags(SampleCountFlagBits::e16) | VkFlags(SampleCountFlagBits::e32) | VkFlags(SampleCountFlagBits::e64) + }; + }; + + struct ImageFormatProperties + { + operator const VkImageFormatProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageFormatProperties const& rhs ) const + { + return ( maxExtent == rhs.maxExtent ) + && ( maxMipLevels == rhs.maxMipLevels ) + && ( maxArrayLayers == rhs.maxArrayLayers ) + && ( sampleCounts == rhs.sampleCounts ) + && ( maxResourceSize == rhs.maxResourceSize ); + } + + bool operator!=( ImageFormatProperties const& rhs ) const + { + return !operator==( rhs ); + } + + Extent3D maxExtent; + uint32_t maxMipLevels; + uint32_t maxArrayLayers; + SampleCountFlags sampleCounts; + DeviceSize maxResourceSize; + }; + static_assert( sizeof( ImageFormatProperties ) == sizeof( VkImageFormatProperties ), "struct and wrapper have different size!" ); + + struct ImageCreateInfo + { + ImageCreateInfo( ImageCreateFlags flags_ = ImageCreateFlags(), ImageType imageType_ = ImageType::e1D, Format format_ = Format::eUndefined, Extent3D extent_ = Extent3D(), uint32_t mipLevels_ = 0, uint32_t arrayLayers_ = 0, SampleCountFlagBits samples_ = SampleCountFlagBits::e1, ImageTiling tiling_ = ImageTiling::eOptimal, ImageUsageFlags usage_ = ImageUsageFlags(), SharingMode sharingMode_ = SharingMode::eExclusive, uint32_t queueFamilyIndexCount_ = 0, const uint32_t* pQueueFamilyIndices_ = nullptr, ImageLayout initialLayout_ = ImageLayout::eUndefined ) + : sType( StructureType::eImageCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , imageType( imageType_ ) + , format( format_ ) + , extent( extent_ ) + , mipLevels( mipLevels_ ) + , arrayLayers( arrayLayers_ ) + , samples( samples_ ) + , tiling( tiling_ ) + , usage( usage_ ) + , sharingMode( sharingMode_ ) + , queueFamilyIndexCount( queueFamilyIndexCount_ ) + , pQueueFamilyIndices( pQueueFamilyIndices_ ) + , initialLayout( initialLayout_ ) + { + } + + ImageCreateInfo( VkImageCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageCreateInfo) ); + } + + ImageCreateInfo& operator=( VkImageCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(ImageCreateInfo) ); + return *this; + } + + ImageCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ImageCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ImageCreateInfo& setFlags( ImageCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + ImageCreateInfo& setImageType( ImageType imageType_ ) + { + imageType = imageType_; + return *this; + } + + ImageCreateInfo& setFormat( Format format_ ) + { + format = format_; + return *this; + } + + ImageCreateInfo& setExtent( Extent3D extent_ ) + { + extent = extent_; + return *this; + } + + ImageCreateInfo& setMipLevels( uint32_t mipLevels_ ) + { + mipLevels = mipLevels_; + return *this; + } + + ImageCreateInfo& setArrayLayers( uint32_t arrayLayers_ ) + { + arrayLayers = arrayLayers_; + return *this; + } + + ImageCreateInfo& setSamples( SampleCountFlagBits samples_ ) + { + samples = samples_; + return *this; + } + + ImageCreateInfo& setTiling( ImageTiling tiling_ ) + { + tiling = tiling_; + return *this; + } + + ImageCreateInfo& setUsage( ImageUsageFlags usage_ ) + { + usage = usage_; + return *this; + } + + ImageCreateInfo& setSharingMode( SharingMode sharingMode_ ) + { + sharingMode = sharingMode_; + return *this; + } + + ImageCreateInfo& setQueueFamilyIndexCount( uint32_t queueFamilyIndexCount_ ) + { + queueFamilyIndexCount = queueFamilyIndexCount_; + return *this; + } + + ImageCreateInfo& setPQueueFamilyIndices( const uint32_t* pQueueFamilyIndices_ ) + { + pQueueFamilyIndices = pQueueFamilyIndices_; + return *this; + } + + ImageCreateInfo& setInitialLayout( ImageLayout initialLayout_ ) + { + initialLayout = initialLayout_; + return *this; + } + + operator const VkImageCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( imageType == rhs.imageType ) + && ( format == rhs.format ) + && ( extent == rhs.extent ) + && ( mipLevels == rhs.mipLevels ) + && ( arrayLayers == rhs.arrayLayers ) + && ( samples == rhs.samples ) + && ( tiling == rhs.tiling ) + && ( usage == rhs.usage ) + && ( sharingMode == rhs.sharingMode ) + && ( queueFamilyIndexCount == rhs.queueFamilyIndexCount ) + && ( pQueueFamilyIndices == rhs.pQueueFamilyIndices ) + && ( initialLayout == rhs.initialLayout ); + } + + bool operator!=( ImageCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ImageCreateFlags flags; + ImageType imageType; + Format format; + Extent3D extent; + uint32_t mipLevels; + uint32_t arrayLayers; + SampleCountFlagBits samples; + ImageTiling tiling; + ImageUsageFlags usage; + SharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; + ImageLayout initialLayout; + }; + static_assert( sizeof( ImageCreateInfo ) == sizeof( VkImageCreateInfo ), "struct and wrapper have different size!" ); + + struct PipelineMultisampleStateCreateInfo + { + PipelineMultisampleStateCreateInfo( PipelineMultisampleStateCreateFlags flags_ = PipelineMultisampleStateCreateFlags(), SampleCountFlagBits rasterizationSamples_ = SampleCountFlagBits::e1, Bool32 sampleShadingEnable_ = 0, float minSampleShading_ = 0, const SampleMask* pSampleMask_ = nullptr, Bool32 alphaToCoverageEnable_ = 0, Bool32 alphaToOneEnable_ = 0 ) + : sType( StructureType::ePipelineMultisampleStateCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , rasterizationSamples( rasterizationSamples_ ) + , sampleShadingEnable( sampleShadingEnable_ ) + , minSampleShading( minSampleShading_ ) + , pSampleMask( pSampleMask_ ) + , alphaToCoverageEnable( alphaToCoverageEnable_ ) + , alphaToOneEnable( alphaToOneEnable_ ) + { + } + + PipelineMultisampleStateCreateInfo( VkPipelineMultisampleStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineMultisampleStateCreateInfo) ); + } + + PipelineMultisampleStateCreateInfo& operator=( VkPipelineMultisampleStateCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineMultisampleStateCreateInfo) ); + return *this; + } + + PipelineMultisampleStateCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setFlags( PipelineMultisampleStateCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setRasterizationSamples( SampleCountFlagBits rasterizationSamples_ ) + { + rasterizationSamples = rasterizationSamples_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setSampleShadingEnable( Bool32 sampleShadingEnable_ ) + { + sampleShadingEnable = sampleShadingEnable_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setMinSampleShading( float minSampleShading_ ) + { + minSampleShading = minSampleShading_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setPSampleMask( const SampleMask* pSampleMask_ ) + { + pSampleMask = pSampleMask_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setAlphaToCoverageEnable( Bool32 alphaToCoverageEnable_ ) + { + alphaToCoverageEnable = alphaToCoverageEnable_; + return *this; + } + + PipelineMultisampleStateCreateInfo& setAlphaToOneEnable( Bool32 alphaToOneEnable_ ) + { + alphaToOneEnable = alphaToOneEnable_; + return *this; + } + + operator const VkPipelineMultisampleStateCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineMultisampleStateCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( rasterizationSamples == rhs.rasterizationSamples ) + && ( sampleShadingEnable == rhs.sampleShadingEnable ) + && ( minSampleShading == rhs.minSampleShading ) + && ( pSampleMask == rhs.pSampleMask ) + && ( alphaToCoverageEnable == rhs.alphaToCoverageEnable ) + && ( alphaToOneEnable == rhs.alphaToOneEnable ); + } + + bool operator!=( PipelineMultisampleStateCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineMultisampleStateCreateFlags flags; + SampleCountFlagBits rasterizationSamples; + Bool32 sampleShadingEnable; + float minSampleShading; + const SampleMask* pSampleMask; + Bool32 alphaToCoverageEnable; + Bool32 alphaToOneEnable; + }; + static_assert( sizeof( PipelineMultisampleStateCreateInfo ) == sizeof( VkPipelineMultisampleStateCreateInfo ), "struct and wrapper have different size!" ); + + struct GraphicsPipelineCreateInfo + { + GraphicsPipelineCreateInfo( PipelineCreateFlags flags_ = PipelineCreateFlags(), uint32_t stageCount_ = 0, const PipelineShaderStageCreateInfo* pStages_ = nullptr, const PipelineVertexInputStateCreateInfo* pVertexInputState_ = nullptr, const PipelineInputAssemblyStateCreateInfo* pInputAssemblyState_ = nullptr, const PipelineTessellationStateCreateInfo* pTessellationState_ = nullptr, const PipelineViewportStateCreateInfo* pViewportState_ = nullptr, const PipelineRasterizationStateCreateInfo* pRasterizationState_ = nullptr, const PipelineMultisampleStateCreateInfo* pMultisampleState_ = nullptr, const PipelineDepthStencilStateCreateInfo* pDepthStencilState_ = nullptr, const PipelineColorBlendStateCreateInfo* pColorBlendState_ = nullptr, const PipelineDynamicStateCreateInfo* pDynamicState_ = nullptr, PipelineLayout layout_ = PipelineLayout(), RenderPass renderPass_ = RenderPass(), uint32_t subpass_ = 0, Pipeline basePipelineHandle_ = Pipeline(), int32_t basePipelineIndex_ = 0 ) + : sType( StructureType::eGraphicsPipelineCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , stageCount( stageCount_ ) + , pStages( pStages_ ) + , pVertexInputState( pVertexInputState_ ) + , pInputAssemblyState( pInputAssemblyState_ ) + , pTessellationState( pTessellationState_ ) + , pViewportState( pViewportState_ ) + , pRasterizationState( pRasterizationState_ ) + , pMultisampleState( pMultisampleState_ ) + , pDepthStencilState( pDepthStencilState_ ) + , pColorBlendState( pColorBlendState_ ) + , pDynamicState( pDynamicState_ ) + , layout( layout_ ) + , renderPass( renderPass_ ) + , subpass( subpass_ ) + , basePipelineHandle( basePipelineHandle_ ) + , basePipelineIndex( basePipelineIndex_ ) + { + } + + GraphicsPipelineCreateInfo( VkGraphicsPipelineCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(GraphicsPipelineCreateInfo) ); + } + + GraphicsPipelineCreateInfo& operator=( VkGraphicsPipelineCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(GraphicsPipelineCreateInfo) ); + return *this; + } + + GraphicsPipelineCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + GraphicsPipelineCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + GraphicsPipelineCreateInfo& setFlags( PipelineCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + GraphicsPipelineCreateInfo& setStageCount( uint32_t stageCount_ ) + { + stageCount = stageCount_; + return *this; + } + + GraphicsPipelineCreateInfo& setPStages( const PipelineShaderStageCreateInfo* pStages_ ) + { + pStages = pStages_; + return *this; + } + + GraphicsPipelineCreateInfo& setPVertexInputState( const PipelineVertexInputStateCreateInfo* pVertexInputState_ ) + { + pVertexInputState = pVertexInputState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPInputAssemblyState( const PipelineInputAssemblyStateCreateInfo* pInputAssemblyState_ ) + { + pInputAssemblyState = pInputAssemblyState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPTessellationState( const PipelineTessellationStateCreateInfo* pTessellationState_ ) + { + pTessellationState = pTessellationState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPViewportState( const PipelineViewportStateCreateInfo* pViewportState_ ) + { + pViewportState = pViewportState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPRasterizationState( const PipelineRasterizationStateCreateInfo* pRasterizationState_ ) + { + pRasterizationState = pRasterizationState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPMultisampleState( const PipelineMultisampleStateCreateInfo* pMultisampleState_ ) + { + pMultisampleState = pMultisampleState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPDepthStencilState( const PipelineDepthStencilStateCreateInfo* pDepthStencilState_ ) + { + pDepthStencilState = pDepthStencilState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPColorBlendState( const PipelineColorBlendStateCreateInfo* pColorBlendState_ ) + { + pColorBlendState = pColorBlendState_; + return *this; + } + + GraphicsPipelineCreateInfo& setPDynamicState( const PipelineDynamicStateCreateInfo* pDynamicState_ ) + { + pDynamicState = pDynamicState_; + return *this; + } + + GraphicsPipelineCreateInfo& setLayout( PipelineLayout layout_ ) + { + layout = layout_; + return *this; + } + + GraphicsPipelineCreateInfo& setRenderPass( RenderPass renderPass_ ) + { + renderPass = renderPass_; + return *this; + } + + GraphicsPipelineCreateInfo& setSubpass( uint32_t subpass_ ) + { + subpass = subpass_; + return *this; + } + + GraphicsPipelineCreateInfo& setBasePipelineHandle( Pipeline basePipelineHandle_ ) + { + basePipelineHandle = basePipelineHandle_; + return *this; + } + + GraphicsPipelineCreateInfo& setBasePipelineIndex( int32_t basePipelineIndex_ ) + { + basePipelineIndex = basePipelineIndex_; + return *this; + } + + operator const VkGraphicsPipelineCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( GraphicsPipelineCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( stageCount == rhs.stageCount ) + && ( pStages == rhs.pStages ) + && ( pVertexInputState == rhs.pVertexInputState ) + && ( pInputAssemblyState == rhs.pInputAssemblyState ) + && ( pTessellationState == rhs.pTessellationState ) + && ( pViewportState == rhs.pViewportState ) + && ( pRasterizationState == rhs.pRasterizationState ) + && ( pMultisampleState == rhs.pMultisampleState ) + && ( pDepthStencilState == rhs.pDepthStencilState ) + && ( pColorBlendState == rhs.pColorBlendState ) + && ( pDynamicState == rhs.pDynamicState ) + && ( layout == rhs.layout ) + && ( renderPass == rhs.renderPass ) + && ( subpass == rhs.subpass ) + && ( basePipelineHandle == rhs.basePipelineHandle ) + && ( basePipelineIndex == rhs.basePipelineIndex ); + } + + bool operator!=( GraphicsPipelineCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineCreateFlags flags; + uint32_t stageCount; + const PipelineShaderStageCreateInfo* pStages; + const PipelineVertexInputStateCreateInfo* pVertexInputState; + const PipelineInputAssemblyStateCreateInfo* pInputAssemblyState; + const PipelineTessellationStateCreateInfo* pTessellationState; + const PipelineViewportStateCreateInfo* pViewportState; + const PipelineRasterizationStateCreateInfo* pRasterizationState; + const PipelineMultisampleStateCreateInfo* pMultisampleState; + const PipelineDepthStencilStateCreateInfo* pDepthStencilState; + const PipelineColorBlendStateCreateInfo* pColorBlendState; + const PipelineDynamicStateCreateInfo* pDynamicState; + PipelineLayout layout; + RenderPass renderPass; + uint32_t subpass; + Pipeline basePipelineHandle; + int32_t basePipelineIndex; + }; + static_assert( sizeof( GraphicsPipelineCreateInfo ) == sizeof( VkGraphicsPipelineCreateInfo ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceLimits + { + operator const VkPhysicalDeviceLimits&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceLimits const& rhs ) const + { + return ( maxImageDimension1D == rhs.maxImageDimension1D ) + && ( maxImageDimension2D == rhs.maxImageDimension2D ) + && ( maxImageDimension3D == rhs.maxImageDimension3D ) + && ( maxImageDimensionCube == rhs.maxImageDimensionCube ) + && ( maxImageArrayLayers == rhs.maxImageArrayLayers ) + && ( maxTexelBufferElements == rhs.maxTexelBufferElements ) + && ( maxUniformBufferRange == rhs.maxUniformBufferRange ) + && ( maxStorageBufferRange == rhs.maxStorageBufferRange ) + && ( maxPushConstantsSize == rhs.maxPushConstantsSize ) + && ( maxMemoryAllocationCount == rhs.maxMemoryAllocationCount ) + && ( maxSamplerAllocationCount == rhs.maxSamplerAllocationCount ) + && ( bufferImageGranularity == rhs.bufferImageGranularity ) + && ( sparseAddressSpaceSize == rhs.sparseAddressSpaceSize ) + && ( maxBoundDescriptorSets == rhs.maxBoundDescriptorSets ) + && ( maxPerStageDescriptorSamplers == rhs.maxPerStageDescriptorSamplers ) + && ( maxPerStageDescriptorUniformBuffers == rhs.maxPerStageDescriptorUniformBuffers ) + && ( maxPerStageDescriptorStorageBuffers == rhs.maxPerStageDescriptorStorageBuffers ) + && ( maxPerStageDescriptorSampledImages == rhs.maxPerStageDescriptorSampledImages ) + && ( maxPerStageDescriptorStorageImages == rhs.maxPerStageDescriptorStorageImages ) + && ( maxPerStageDescriptorInputAttachments == rhs.maxPerStageDescriptorInputAttachments ) + && ( maxPerStageResources == rhs.maxPerStageResources ) + && ( maxDescriptorSetSamplers == rhs.maxDescriptorSetSamplers ) + && ( maxDescriptorSetUniformBuffers == rhs.maxDescriptorSetUniformBuffers ) + && ( maxDescriptorSetUniformBuffersDynamic == rhs.maxDescriptorSetUniformBuffersDynamic ) + && ( maxDescriptorSetStorageBuffers == rhs.maxDescriptorSetStorageBuffers ) + && ( maxDescriptorSetStorageBuffersDynamic == rhs.maxDescriptorSetStorageBuffersDynamic ) + && ( maxDescriptorSetSampledImages == rhs.maxDescriptorSetSampledImages ) + && ( maxDescriptorSetStorageImages == rhs.maxDescriptorSetStorageImages ) + && ( maxDescriptorSetInputAttachments == rhs.maxDescriptorSetInputAttachments ) + && ( maxVertexInputAttributes == rhs.maxVertexInputAttributes ) + && ( maxVertexInputBindings == rhs.maxVertexInputBindings ) + && ( maxVertexInputAttributeOffset == rhs.maxVertexInputAttributeOffset ) + && ( maxVertexInputBindingStride == rhs.maxVertexInputBindingStride ) + && ( maxVertexOutputComponents == rhs.maxVertexOutputComponents ) + && ( maxTessellationGenerationLevel == rhs.maxTessellationGenerationLevel ) + && ( maxTessellationPatchSize == rhs.maxTessellationPatchSize ) + && ( maxTessellationControlPerVertexInputComponents == rhs.maxTessellationControlPerVertexInputComponents ) + && ( maxTessellationControlPerVertexOutputComponents == rhs.maxTessellationControlPerVertexOutputComponents ) + && ( maxTessellationControlPerPatchOutputComponents == rhs.maxTessellationControlPerPatchOutputComponents ) + && ( maxTessellationControlTotalOutputComponents == rhs.maxTessellationControlTotalOutputComponents ) + && ( maxTessellationEvaluationInputComponents == rhs.maxTessellationEvaluationInputComponents ) + && ( maxTessellationEvaluationOutputComponents == rhs.maxTessellationEvaluationOutputComponents ) + && ( maxGeometryShaderInvocations == rhs.maxGeometryShaderInvocations ) + && ( maxGeometryInputComponents == rhs.maxGeometryInputComponents ) + && ( maxGeometryOutputComponents == rhs.maxGeometryOutputComponents ) + && ( maxGeometryOutputVertices == rhs.maxGeometryOutputVertices ) + && ( maxGeometryTotalOutputComponents == rhs.maxGeometryTotalOutputComponents ) + && ( maxFragmentInputComponents == rhs.maxFragmentInputComponents ) + && ( maxFragmentOutputAttachments == rhs.maxFragmentOutputAttachments ) + && ( maxFragmentDualSrcAttachments == rhs.maxFragmentDualSrcAttachments ) + && ( maxFragmentCombinedOutputResources == rhs.maxFragmentCombinedOutputResources ) + && ( maxComputeSharedMemorySize == rhs.maxComputeSharedMemorySize ) + && ( memcmp( maxComputeWorkGroupCount, rhs.maxComputeWorkGroupCount, 3 * sizeof( uint32_t ) ) == 0 ) + && ( maxComputeWorkGroupInvocations == rhs.maxComputeWorkGroupInvocations ) + && ( memcmp( maxComputeWorkGroupSize, rhs.maxComputeWorkGroupSize, 3 * sizeof( uint32_t ) ) == 0 ) + && ( subPixelPrecisionBits == rhs.subPixelPrecisionBits ) + && ( subTexelPrecisionBits == rhs.subTexelPrecisionBits ) + && ( mipmapPrecisionBits == rhs.mipmapPrecisionBits ) + && ( maxDrawIndexedIndexValue == rhs.maxDrawIndexedIndexValue ) + && ( maxDrawIndirectCount == rhs.maxDrawIndirectCount ) + && ( maxSamplerLodBias == rhs.maxSamplerLodBias ) + && ( maxSamplerAnisotropy == rhs.maxSamplerAnisotropy ) + && ( maxViewports == rhs.maxViewports ) + && ( memcmp( maxViewportDimensions, rhs.maxViewportDimensions, 2 * sizeof( uint32_t ) ) == 0 ) + && ( memcmp( viewportBoundsRange, rhs.viewportBoundsRange, 2 * sizeof( float ) ) == 0 ) + && ( viewportSubPixelBits == rhs.viewportSubPixelBits ) + && ( minMemoryMapAlignment == rhs.minMemoryMapAlignment ) + && ( minTexelBufferOffsetAlignment == rhs.minTexelBufferOffsetAlignment ) + && ( minUniformBufferOffsetAlignment == rhs.minUniformBufferOffsetAlignment ) + && ( minStorageBufferOffsetAlignment == rhs.minStorageBufferOffsetAlignment ) + && ( minTexelOffset == rhs.minTexelOffset ) + && ( maxTexelOffset == rhs.maxTexelOffset ) + && ( minTexelGatherOffset == rhs.minTexelGatherOffset ) + && ( maxTexelGatherOffset == rhs.maxTexelGatherOffset ) + && ( minInterpolationOffset == rhs.minInterpolationOffset ) + && ( maxInterpolationOffset == rhs.maxInterpolationOffset ) + && ( subPixelInterpolationOffsetBits == rhs.subPixelInterpolationOffsetBits ) + && ( maxFramebufferWidth == rhs.maxFramebufferWidth ) + && ( maxFramebufferHeight == rhs.maxFramebufferHeight ) + && ( maxFramebufferLayers == rhs.maxFramebufferLayers ) + && ( framebufferColorSampleCounts == rhs.framebufferColorSampleCounts ) + && ( framebufferDepthSampleCounts == rhs.framebufferDepthSampleCounts ) + && ( framebufferStencilSampleCounts == rhs.framebufferStencilSampleCounts ) + && ( framebufferNoAttachmentsSampleCounts == rhs.framebufferNoAttachmentsSampleCounts ) + && ( maxColorAttachments == rhs.maxColorAttachments ) + && ( sampledImageColorSampleCounts == rhs.sampledImageColorSampleCounts ) + && ( sampledImageIntegerSampleCounts == rhs.sampledImageIntegerSampleCounts ) + && ( sampledImageDepthSampleCounts == rhs.sampledImageDepthSampleCounts ) + && ( sampledImageStencilSampleCounts == rhs.sampledImageStencilSampleCounts ) + && ( storageImageSampleCounts == rhs.storageImageSampleCounts ) + && ( maxSampleMaskWords == rhs.maxSampleMaskWords ) + && ( timestampComputeAndGraphics == rhs.timestampComputeAndGraphics ) + && ( timestampPeriod == rhs.timestampPeriod ) + && ( maxClipDistances == rhs.maxClipDistances ) + && ( maxCullDistances == rhs.maxCullDistances ) + && ( maxCombinedClipAndCullDistances == rhs.maxCombinedClipAndCullDistances ) + && ( discreteQueuePriorities == rhs.discreteQueuePriorities ) + && ( memcmp( pointSizeRange, rhs.pointSizeRange, 2 * sizeof( float ) ) == 0 ) + && ( memcmp( lineWidthRange, rhs.lineWidthRange, 2 * sizeof( float ) ) == 0 ) + && ( pointSizeGranularity == rhs.pointSizeGranularity ) + && ( lineWidthGranularity == rhs.lineWidthGranularity ) + && ( strictLines == rhs.strictLines ) + && ( standardSampleLocations == rhs.standardSampleLocations ) + && ( optimalBufferCopyOffsetAlignment == rhs.optimalBufferCopyOffsetAlignment ) + && ( optimalBufferCopyRowPitchAlignment == rhs.optimalBufferCopyRowPitchAlignment ) + && ( nonCoherentAtomSize == rhs.nonCoherentAtomSize ); + } + + bool operator!=( PhysicalDeviceLimits const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t maxImageDimension1D; + uint32_t maxImageDimension2D; + uint32_t maxImageDimension3D; + uint32_t maxImageDimensionCube; + uint32_t maxImageArrayLayers; + uint32_t maxTexelBufferElements; + uint32_t maxUniformBufferRange; + uint32_t maxStorageBufferRange; + uint32_t maxPushConstantsSize; + uint32_t maxMemoryAllocationCount; + uint32_t maxSamplerAllocationCount; + DeviceSize bufferImageGranularity; + DeviceSize sparseAddressSpaceSize; + uint32_t maxBoundDescriptorSets; + uint32_t maxPerStageDescriptorSamplers; + uint32_t maxPerStageDescriptorUniformBuffers; + uint32_t maxPerStageDescriptorStorageBuffers; + uint32_t maxPerStageDescriptorSampledImages; + uint32_t maxPerStageDescriptorStorageImages; + uint32_t maxPerStageDescriptorInputAttachments; + uint32_t maxPerStageResources; + uint32_t maxDescriptorSetSamplers; + uint32_t maxDescriptorSetUniformBuffers; + uint32_t maxDescriptorSetUniformBuffersDynamic; + uint32_t maxDescriptorSetStorageBuffers; + uint32_t maxDescriptorSetStorageBuffersDynamic; + uint32_t maxDescriptorSetSampledImages; + uint32_t maxDescriptorSetStorageImages; + uint32_t maxDescriptorSetInputAttachments; + uint32_t maxVertexInputAttributes; + uint32_t maxVertexInputBindings; + uint32_t maxVertexInputAttributeOffset; + uint32_t maxVertexInputBindingStride; + uint32_t maxVertexOutputComponents; + uint32_t maxTessellationGenerationLevel; + uint32_t maxTessellationPatchSize; + uint32_t maxTessellationControlPerVertexInputComponents; + uint32_t maxTessellationControlPerVertexOutputComponents; + uint32_t maxTessellationControlPerPatchOutputComponents; + uint32_t maxTessellationControlTotalOutputComponents; + uint32_t maxTessellationEvaluationInputComponents; + uint32_t maxTessellationEvaluationOutputComponents; + uint32_t maxGeometryShaderInvocations; + uint32_t maxGeometryInputComponents; + uint32_t maxGeometryOutputComponents; + uint32_t maxGeometryOutputVertices; + uint32_t maxGeometryTotalOutputComponents; + uint32_t maxFragmentInputComponents; + uint32_t maxFragmentOutputAttachments; + uint32_t maxFragmentDualSrcAttachments; + uint32_t maxFragmentCombinedOutputResources; + uint32_t maxComputeSharedMemorySize; + uint32_t maxComputeWorkGroupCount[3]; + uint32_t maxComputeWorkGroupInvocations; + uint32_t maxComputeWorkGroupSize[3]; + uint32_t subPixelPrecisionBits; + uint32_t subTexelPrecisionBits; + uint32_t mipmapPrecisionBits; + uint32_t maxDrawIndexedIndexValue; + uint32_t maxDrawIndirectCount; + float maxSamplerLodBias; + float maxSamplerAnisotropy; + uint32_t maxViewports; + uint32_t maxViewportDimensions[2]; + float viewportBoundsRange[2]; + uint32_t viewportSubPixelBits; + size_t minMemoryMapAlignment; + DeviceSize minTexelBufferOffsetAlignment; + DeviceSize minUniformBufferOffsetAlignment; + DeviceSize minStorageBufferOffsetAlignment; + int32_t minTexelOffset; + uint32_t maxTexelOffset; + int32_t minTexelGatherOffset; + uint32_t maxTexelGatherOffset; + float minInterpolationOffset; + float maxInterpolationOffset; + uint32_t subPixelInterpolationOffsetBits; + uint32_t maxFramebufferWidth; + uint32_t maxFramebufferHeight; + uint32_t maxFramebufferLayers; + SampleCountFlags framebufferColorSampleCounts; + SampleCountFlags framebufferDepthSampleCounts; + SampleCountFlags framebufferStencilSampleCounts; + SampleCountFlags framebufferNoAttachmentsSampleCounts; + uint32_t maxColorAttachments; + SampleCountFlags sampledImageColorSampleCounts; + SampleCountFlags sampledImageIntegerSampleCounts; + SampleCountFlags sampledImageDepthSampleCounts; + SampleCountFlags sampledImageStencilSampleCounts; + SampleCountFlags storageImageSampleCounts; + uint32_t maxSampleMaskWords; + Bool32 timestampComputeAndGraphics; + float timestampPeriod; + uint32_t maxClipDistances; + uint32_t maxCullDistances; + uint32_t maxCombinedClipAndCullDistances; + uint32_t discreteQueuePriorities; + float pointSizeRange[2]; + float lineWidthRange[2]; + float pointSizeGranularity; + float lineWidthGranularity; + Bool32 strictLines; + Bool32 standardSampleLocations; + DeviceSize optimalBufferCopyOffsetAlignment; + DeviceSize optimalBufferCopyRowPitchAlignment; + DeviceSize nonCoherentAtomSize; + }; + static_assert( sizeof( PhysicalDeviceLimits ) == sizeof( VkPhysicalDeviceLimits ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceProperties + { + operator const VkPhysicalDeviceProperties&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceProperties const& rhs ) const + { + return ( apiVersion == rhs.apiVersion ) + && ( driverVersion == rhs.driverVersion ) + && ( vendorID == rhs.vendorID ) + && ( deviceID == rhs.deviceID ) + && ( deviceType == rhs.deviceType ) + && ( memcmp( deviceName, rhs.deviceName, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE * sizeof( char ) ) == 0 ) + && ( memcmp( pipelineCacheUUID, rhs.pipelineCacheUUID, VK_UUID_SIZE * sizeof( uint8_t ) ) == 0 ) + && ( limits == rhs.limits ) + && ( sparseProperties == rhs.sparseProperties ); + } + + bool operator!=( PhysicalDeviceProperties const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t apiVersion; + uint32_t driverVersion; + uint32_t vendorID; + uint32_t deviceID; + PhysicalDeviceType deviceType; + char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; + uint8_t pipelineCacheUUID[VK_UUID_SIZE]; + PhysicalDeviceLimits limits; + PhysicalDeviceSparseProperties sparseProperties; + }; + static_assert( sizeof( PhysicalDeviceProperties ) == sizeof( VkPhysicalDeviceProperties ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceProperties2KHR + { + operator const VkPhysicalDeviceProperties2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceProperties2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( properties == rhs.properties ); + } + + bool operator!=( PhysicalDeviceProperties2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + PhysicalDeviceProperties properties; + }; + static_assert( sizeof( PhysicalDeviceProperties2KHR ) == sizeof( VkPhysicalDeviceProperties2KHR ), "struct and wrapper have different size!" ); + + struct ImageFormatProperties2KHR + { + operator const VkImageFormatProperties2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImageFormatProperties2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( imageFormatProperties == rhs.imageFormatProperties ); + } + + bool operator!=( ImageFormatProperties2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + ImageFormatProperties imageFormatProperties; + }; + static_assert( sizeof( ImageFormatProperties2KHR ) == sizeof( VkImageFormatProperties2KHR ), "struct and wrapper have different size!" ); + + struct PhysicalDeviceSparseImageFormatInfo2KHR + { + PhysicalDeviceSparseImageFormatInfo2KHR( Format format_ = Format::eUndefined, ImageType type_ = ImageType::e1D, SampleCountFlagBits samples_ = SampleCountFlagBits::e1, ImageUsageFlags usage_ = ImageUsageFlags(), ImageTiling tiling_ = ImageTiling::eOptimal ) + : sType( StructureType::ePhysicalDeviceSparseImageFormatInfo2KHR ) + , pNext( nullptr ) + , format( format_ ) + , type( type_ ) + , samples( samples_ ) + , usage( usage_ ) + , tiling( tiling_ ) + { + } + + PhysicalDeviceSparseImageFormatInfo2KHR( VkPhysicalDeviceSparseImageFormatInfo2KHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceSparseImageFormatInfo2KHR) ); + } + + PhysicalDeviceSparseImageFormatInfo2KHR& operator=( VkPhysicalDeviceSparseImageFormatInfo2KHR const & rhs ) + { + memcpy( this, &rhs, sizeof(PhysicalDeviceSparseImageFormatInfo2KHR) ); + return *this; + } + + PhysicalDeviceSparseImageFormatInfo2KHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PhysicalDeviceSparseImageFormatInfo2KHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PhysicalDeviceSparseImageFormatInfo2KHR& setFormat( Format format_ ) + { + format = format_; + return *this; + } + + PhysicalDeviceSparseImageFormatInfo2KHR& setType( ImageType type_ ) + { + type = type_; + return *this; + } + + PhysicalDeviceSparseImageFormatInfo2KHR& setSamples( SampleCountFlagBits samples_ ) + { + samples = samples_; + return *this; + } + + PhysicalDeviceSparseImageFormatInfo2KHR& setUsage( ImageUsageFlags usage_ ) + { + usage = usage_; + return *this; + } + + PhysicalDeviceSparseImageFormatInfo2KHR& setTiling( ImageTiling tiling_ ) + { + tiling = tiling_; + return *this; + } + + operator const VkPhysicalDeviceSparseImageFormatInfo2KHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PhysicalDeviceSparseImageFormatInfo2KHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( format == rhs.format ) + && ( type == rhs.type ) + && ( samples == rhs.samples ) + && ( usage == rhs.usage ) + && ( tiling == rhs.tiling ); + } + + bool operator!=( PhysicalDeviceSparseImageFormatInfo2KHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + Format format; + ImageType type; + SampleCountFlagBits samples; + ImageUsageFlags usage; + ImageTiling tiling; + }; + static_assert( sizeof( PhysicalDeviceSparseImageFormatInfo2KHR ) == sizeof( VkPhysicalDeviceSparseImageFormatInfo2KHR ), "struct and wrapper have different size!" ); + + enum class AttachmentDescriptionFlagBits + { + eMayAlias = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT + }; + + using AttachmentDescriptionFlags = Flags; + + VULKAN_HPP_INLINE AttachmentDescriptionFlags operator|( AttachmentDescriptionFlagBits bit0, AttachmentDescriptionFlagBits bit1 ) + { + return AttachmentDescriptionFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE AttachmentDescriptionFlags operator~( AttachmentDescriptionFlagBits bits ) + { + return ~( AttachmentDescriptionFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(AttachmentDescriptionFlagBits::eMayAlias) + }; + }; + + struct AttachmentDescription + { + AttachmentDescription( AttachmentDescriptionFlags flags_ = AttachmentDescriptionFlags(), Format format_ = Format::eUndefined, SampleCountFlagBits samples_ = SampleCountFlagBits::e1, AttachmentLoadOp loadOp_ = AttachmentLoadOp::eLoad, AttachmentStoreOp storeOp_ = AttachmentStoreOp::eStore, AttachmentLoadOp stencilLoadOp_ = AttachmentLoadOp::eLoad, AttachmentStoreOp stencilStoreOp_ = AttachmentStoreOp::eStore, ImageLayout initialLayout_ = ImageLayout::eUndefined, ImageLayout finalLayout_ = ImageLayout::eUndefined ) + : flags( flags_ ) + , format( format_ ) + , samples( samples_ ) + , loadOp( loadOp_ ) + , storeOp( storeOp_ ) + , stencilLoadOp( stencilLoadOp_ ) + , stencilStoreOp( stencilStoreOp_ ) + , initialLayout( initialLayout_ ) + , finalLayout( finalLayout_ ) + { + } + + AttachmentDescription( VkAttachmentDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(AttachmentDescription) ); + } + + AttachmentDescription& operator=( VkAttachmentDescription const & rhs ) + { + memcpy( this, &rhs, sizeof(AttachmentDescription) ); + return *this; + } + + AttachmentDescription& setFlags( AttachmentDescriptionFlags flags_ ) + { + flags = flags_; + return *this; + } + + AttachmentDescription& setFormat( Format format_ ) + { + format = format_; + return *this; + } + + AttachmentDescription& setSamples( SampleCountFlagBits samples_ ) + { + samples = samples_; + return *this; + } + + AttachmentDescription& setLoadOp( AttachmentLoadOp loadOp_ ) + { + loadOp = loadOp_; + return *this; + } + + AttachmentDescription& setStoreOp( AttachmentStoreOp storeOp_ ) + { + storeOp = storeOp_; + return *this; + } + + AttachmentDescription& setStencilLoadOp( AttachmentLoadOp stencilLoadOp_ ) + { + stencilLoadOp = stencilLoadOp_; + return *this; + } + + AttachmentDescription& setStencilStoreOp( AttachmentStoreOp stencilStoreOp_ ) + { + stencilStoreOp = stencilStoreOp_; + return *this; + } + + AttachmentDescription& setInitialLayout( ImageLayout initialLayout_ ) + { + initialLayout = initialLayout_; + return *this; + } + + AttachmentDescription& setFinalLayout( ImageLayout finalLayout_ ) + { + finalLayout = finalLayout_; + return *this; + } + + operator const VkAttachmentDescription&() const + { + return *reinterpret_cast(this); + } + + bool operator==( AttachmentDescription const& rhs ) const + { + return ( flags == rhs.flags ) + && ( format == rhs.format ) + && ( samples == rhs.samples ) + && ( loadOp == rhs.loadOp ) + && ( storeOp == rhs.storeOp ) + && ( stencilLoadOp == rhs.stencilLoadOp ) + && ( stencilStoreOp == rhs.stencilStoreOp ) + && ( initialLayout == rhs.initialLayout ) + && ( finalLayout == rhs.finalLayout ); + } + + bool operator!=( AttachmentDescription const& rhs ) const + { + return !operator==( rhs ); + } + + AttachmentDescriptionFlags flags; + Format format; + SampleCountFlagBits samples; + AttachmentLoadOp loadOp; + AttachmentStoreOp storeOp; + AttachmentLoadOp stencilLoadOp; + AttachmentStoreOp stencilStoreOp; + ImageLayout initialLayout; + ImageLayout finalLayout; + }; + static_assert( sizeof( AttachmentDescription ) == sizeof( VkAttachmentDescription ), "struct and wrapper have different size!" ); + + enum class StencilFaceFlagBits + { + eFront = VK_STENCIL_FACE_FRONT_BIT, + eBack = VK_STENCIL_FACE_BACK_BIT, + eVkStencilFrontAndBack = VK_STENCIL_FRONT_AND_BACK + }; + + using StencilFaceFlags = Flags; + + VULKAN_HPP_INLINE StencilFaceFlags operator|( StencilFaceFlagBits bit0, StencilFaceFlagBits bit1 ) + { + return StencilFaceFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE StencilFaceFlags operator~( StencilFaceFlagBits bits ) + { + return ~( StencilFaceFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(StencilFaceFlagBits::eFront) | VkFlags(StencilFaceFlagBits::eBack) | VkFlags(StencilFaceFlagBits::eVkStencilFrontAndBack) + }; + }; + + enum class DescriptorPoolCreateFlagBits + { + eFreeDescriptorSet = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT + }; + + using DescriptorPoolCreateFlags = Flags; + + VULKAN_HPP_INLINE DescriptorPoolCreateFlags operator|( DescriptorPoolCreateFlagBits bit0, DescriptorPoolCreateFlagBits bit1 ) + { + return DescriptorPoolCreateFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE DescriptorPoolCreateFlags operator~( DescriptorPoolCreateFlagBits bits ) + { + return ~( DescriptorPoolCreateFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(DescriptorPoolCreateFlagBits::eFreeDescriptorSet) + }; + }; + + struct DescriptorPoolCreateInfo + { + DescriptorPoolCreateInfo( DescriptorPoolCreateFlags flags_ = DescriptorPoolCreateFlags(), uint32_t maxSets_ = 0, uint32_t poolSizeCount_ = 0, const DescriptorPoolSize* pPoolSizes_ = nullptr ) + : sType( StructureType::eDescriptorPoolCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , maxSets( maxSets_ ) + , poolSizeCount( poolSizeCount_ ) + , pPoolSizes( pPoolSizes_ ) + { + } + + DescriptorPoolCreateInfo( VkDescriptorPoolCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorPoolCreateInfo) ); + } + + DescriptorPoolCreateInfo& operator=( VkDescriptorPoolCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(DescriptorPoolCreateInfo) ); + return *this; + } + + DescriptorPoolCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DescriptorPoolCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DescriptorPoolCreateInfo& setFlags( DescriptorPoolCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + DescriptorPoolCreateInfo& setMaxSets( uint32_t maxSets_ ) + { + maxSets = maxSets_; + return *this; + } + + DescriptorPoolCreateInfo& setPoolSizeCount( uint32_t poolSizeCount_ ) + { + poolSizeCount = poolSizeCount_; + return *this; + } + + DescriptorPoolCreateInfo& setPPoolSizes( const DescriptorPoolSize* pPoolSizes_ ) + { + pPoolSizes = pPoolSizes_; + return *this; + } + + operator const VkDescriptorPoolCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DescriptorPoolCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( maxSets == rhs.maxSets ) + && ( poolSizeCount == rhs.poolSizeCount ) + && ( pPoolSizes == rhs.pPoolSizes ); + } + + bool operator!=( DescriptorPoolCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DescriptorPoolCreateFlags flags; + uint32_t maxSets; + uint32_t poolSizeCount; + const DescriptorPoolSize* pPoolSizes; + }; + static_assert( sizeof( DescriptorPoolCreateInfo ) == sizeof( VkDescriptorPoolCreateInfo ), "struct and wrapper have different size!" ); + + enum class DependencyFlagBits + { + eByRegion = VK_DEPENDENCY_BY_REGION_BIT + }; + + using DependencyFlags = Flags; + + VULKAN_HPP_INLINE DependencyFlags operator|( DependencyFlagBits bit0, DependencyFlagBits bit1 ) + { + return DependencyFlags( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE DependencyFlags operator~( DependencyFlagBits bits ) + { + return ~( DependencyFlags( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(DependencyFlagBits::eByRegion) + }; + }; + + struct SubpassDependency + { + SubpassDependency( uint32_t srcSubpass_ = 0, uint32_t dstSubpass_ = 0, PipelineStageFlags srcStageMask_ = PipelineStageFlags(), PipelineStageFlags dstStageMask_ = PipelineStageFlags(), AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags(), DependencyFlags dependencyFlags_ = DependencyFlags() ) + : srcSubpass( srcSubpass_ ) + , dstSubpass( dstSubpass_ ) + , srcStageMask( srcStageMask_ ) + , dstStageMask( dstStageMask_ ) + , srcAccessMask( srcAccessMask_ ) + , dstAccessMask( dstAccessMask_ ) + , dependencyFlags( dependencyFlags_ ) + { + } + + SubpassDependency( VkSubpassDependency const & rhs ) + { + memcpy( this, &rhs, sizeof(SubpassDependency) ); + } + + SubpassDependency& operator=( VkSubpassDependency const & rhs ) + { + memcpy( this, &rhs, sizeof(SubpassDependency) ); + return *this; + } + + SubpassDependency& setSrcSubpass( uint32_t srcSubpass_ ) + { + srcSubpass = srcSubpass_; + return *this; + } + + SubpassDependency& setDstSubpass( uint32_t dstSubpass_ ) + { + dstSubpass = dstSubpass_; + return *this; + } + + SubpassDependency& setSrcStageMask( PipelineStageFlags srcStageMask_ ) + { + srcStageMask = srcStageMask_; + return *this; + } + + SubpassDependency& setDstStageMask( PipelineStageFlags dstStageMask_ ) + { + dstStageMask = dstStageMask_; + return *this; + } + + SubpassDependency& setSrcAccessMask( AccessFlags srcAccessMask_ ) + { + srcAccessMask = srcAccessMask_; + return *this; + } + + SubpassDependency& setDstAccessMask( AccessFlags dstAccessMask_ ) + { + dstAccessMask = dstAccessMask_; + return *this; + } + + SubpassDependency& setDependencyFlags( DependencyFlags dependencyFlags_ ) + { + dependencyFlags = dependencyFlags_; + return *this; + } + + operator const VkSubpassDependency&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SubpassDependency const& rhs ) const + { + return ( srcSubpass == rhs.srcSubpass ) + && ( dstSubpass == rhs.dstSubpass ) + && ( srcStageMask == rhs.srcStageMask ) + && ( dstStageMask == rhs.dstStageMask ) + && ( srcAccessMask == rhs.srcAccessMask ) + && ( dstAccessMask == rhs.dstAccessMask ) + && ( dependencyFlags == rhs.dependencyFlags ); + } + + bool operator!=( SubpassDependency const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t srcSubpass; + uint32_t dstSubpass; + PipelineStageFlags srcStageMask; + PipelineStageFlags dstStageMask; + AccessFlags srcAccessMask; + AccessFlags dstAccessMask; + DependencyFlags dependencyFlags; + }; + static_assert( sizeof( SubpassDependency ) == sizeof( VkSubpassDependency ), "struct and wrapper have different size!" ); + + struct RenderPassCreateInfo + { + RenderPassCreateInfo( RenderPassCreateFlags flags_ = RenderPassCreateFlags(), uint32_t attachmentCount_ = 0, const AttachmentDescription* pAttachments_ = nullptr, uint32_t subpassCount_ = 0, const SubpassDescription* pSubpasses_ = nullptr, uint32_t dependencyCount_ = 0, const SubpassDependency* pDependencies_ = nullptr ) + : sType( StructureType::eRenderPassCreateInfo ) + , pNext( nullptr ) + , flags( flags_ ) + , attachmentCount( attachmentCount_ ) + , pAttachments( pAttachments_ ) + , subpassCount( subpassCount_ ) + , pSubpasses( pSubpasses_ ) + , dependencyCount( dependencyCount_ ) + , pDependencies( pDependencies_ ) + { + } + + RenderPassCreateInfo( VkRenderPassCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(RenderPassCreateInfo) ); + } + + RenderPassCreateInfo& operator=( VkRenderPassCreateInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(RenderPassCreateInfo) ); + return *this; + } + + RenderPassCreateInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + RenderPassCreateInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + RenderPassCreateInfo& setFlags( RenderPassCreateFlags flags_ ) + { + flags = flags_; + return *this; + } + + RenderPassCreateInfo& setAttachmentCount( uint32_t attachmentCount_ ) + { + attachmentCount = attachmentCount_; + return *this; + } + + RenderPassCreateInfo& setPAttachments( const AttachmentDescription* pAttachments_ ) + { + pAttachments = pAttachments_; + return *this; + } + + RenderPassCreateInfo& setSubpassCount( uint32_t subpassCount_ ) + { + subpassCount = subpassCount_; + return *this; + } + + RenderPassCreateInfo& setPSubpasses( const SubpassDescription* pSubpasses_ ) + { + pSubpasses = pSubpasses_; + return *this; + } + + RenderPassCreateInfo& setDependencyCount( uint32_t dependencyCount_ ) + { + dependencyCount = dependencyCount_; + return *this; + } + + RenderPassCreateInfo& setPDependencies( const SubpassDependency* pDependencies_ ) + { + pDependencies = pDependencies_; + return *this; + } + + operator const VkRenderPassCreateInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( RenderPassCreateInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( attachmentCount == rhs.attachmentCount ) + && ( pAttachments == rhs.pAttachments ) + && ( subpassCount == rhs.subpassCount ) + && ( pSubpasses == rhs.pSubpasses ) + && ( dependencyCount == rhs.dependencyCount ) + && ( pDependencies == rhs.pDependencies ); + } + + bool operator!=( RenderPassCreateInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + RenderPassCreateFlags flags; + uint32_t attachmentCount; + const AttachmentDescription* pAttachments; + uint32_t subpassCount; + const SubpassDescription* pSubpasses; + uint32_t dependencyCount; + const SubpassDependency* pDependencies; + }; + static_assert( sizeof( RenderPassCreateInfo ) == sizeof( VkRenderPassCreateInfo ), "struct and wrapper have different size!" ); + + enum class PresentModeKHR + { + eImmediate = VK_PRESENT_MODE_IMMEDIATE_KHR, + eMailbox = VK_PRESENT_MODE_MAILBOX_KHR, + eFifo = VK_PRESENT_MODE_FIFO_KHR, + eFifoRelaxed = VK_PRESENT_MODE_FIFO_RELAXED_KHR + }; + + enum class ColorSpaceKHR + { + eSrgbNonlinear = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + eDisplayP3LinearEXT = VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, + eDisplayP3NonlinearEXT = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, + eScrgbLinearEXT = VK_COLOR_SPACE_SCRGB_LINEAR_EXT, + eScrgbNonlinearEXT = VK_COLOR_SPACE_SCRGB_NONLINEAR_EXT, + eDciP3LinearEXT = VK_COLOR_SPACE_DCI_P3_LINEAR_EXT, + eDciP3NonlinearEXT = VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT, + eBt709LinearEXT = VK_COLOR_SPACE_BT709_LINEAR_EXT, + eBt709NonlinearEXT = VK_COLOR_SPACE_BT709_NONLINEAR_EXT, + eBt2020LinearEXT = VK_COLOR_SPACE_BT2020_LINEAR_EXT, + eBt2020NonlinearEXT = VK_COLOR_SPACE_BT2020_NONLINEAR_EXT, + eAdobergbLinearEXT = VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT, + eAdobergbNonlinearEXT = VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT + }; + + struct SurfaceFormatKHR + { + operator const VkSurfaceFormatKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SurfaceFormatKHR const& rhs ) const + { + return ( format == rhs.format ) + && ( colorSpace == rhs.colorSpace ); + } + + bool operator!=( SurfaceFormatKHR const& rhs ) const + { + return !operator==( rhs ); + } + + Format format; + ColorSpaceKHR colorSpace; + }; + static_assert( sizeof( SurfaceFormatKHR ) == sizeof( VkSurfaceFormatKHR ), "struct and wrapper have different size!" ); + + enum class DisplayPlaneAlphaFlagBitsKHR + { + eOpaque = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR, + eGlobal = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR, + ePerPixel = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR, + ePerPixelPremultiplied = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR + }; + + using DisplayPlaneAlphaFlagsKHR = Flags; + + VULKAN_HPP_INLINE DisplayPlaneAlphaFlagsKHR operator|( DisplayPlaneAlphaFlagBitsKHR bit0, DisplayPlaneAlphaFlagBitsKHR bit1 ) + { + return DisplayPlaneAlphaFlagsKHR( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE DisplayPlaneAlphaFlagsKHR operator~( DisplayPlaneAlphaFlagBitsKHR bits ) + { + return ~( DisplayPlaneAlphaFlagsKHR( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(DisplayPlaneAlphaFlagBitsKHR::eOpaque) | VkFlags(DisplayPlaneAlphaFlagBitsKHR::eGlobal) | VkFlags(DisplayPlaneAlphaFlagBitsKHR::ePerPixel) | VkFlags(DisplayPlaneAlphaFlagBitsKHR::ePerPixelPremultiplied) + }; + }; + + struct DisplayPlaneCapabilitiesKHR + { + operator const VkDisplayPlaneCapabilitiesKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayPlaneCapabilitiesKHR const& rhs ) const + { + return ( supportedAlpha == rhs.supportedAlpha ) + && ( minSrcPosition == rhs.minSrcPosition ) + && ( maxSrcPosition == rhs.maxSrcPosition ) + && ( minSrcExtent == rhs.minSrcExtent ) + && ( maxSrcExtent == rhs.maxSrcExtent ) + && ( minDstPosition == rhs.minDstPosition ) + && ( maxDstPosition == rhs.maxDstPosition ) + && ( minDstExtent == rhs.minDstExtent ) + && ( maxDstExtent == rhs.maxDstExtent ); + } + + bool operator!=( DisplayPlaneCapabilitiesKHR const& rhs ) const + { + return !operator==( rhs ); + } + + DisplayPlaneAlphaFlagsKHR supportedAlpha; + Offset2D minSrcPosition; + Offset2D maxSrcPosition; + Extent2D minSrcExtent; + Extent2D maxSrcExtent; + Offset2D minDstPosition; + Offset2D maxDstPosition; + Extent2D minDstExtent; + Extent2D maxDstExtent; + }; + static_assert( sizeof( DisplayPlaneCapabilitiesKHR ) == sizeof( VkDisplayPlaneCapabilitiesKHR ), "struct and wrapper have different size!" ); + + enum class CompositeAlphaFlagBitsKHR + { + eOpaque = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + ePreMultiplied = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + ePostMultiplied = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + eInherit = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR + }; + + using CompositeAlphaFlagsKHR = Flags; + + VULKAN_HPP_INLINE CompositeAlphaFlagsKHR operator|( CompositeAlphaFlagBitsKHR bit0, CompositeAlphaFlagBitsKHR bit1 ) + { + return CompositeAlphaFlagsKHR( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE CompositeAlphaFlagsKHR operator~( CompositeAlphaFlagBitsKHR bits ) + { + return ~( CompositeAlphaFlagsKHR( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(CompositeAlphaFlagBitsKHR::eOpaque) | VkFlags(CompositeAlphaFlagBitsKHR::ePreMultiplied) | VkFlags(CompositeAlphaFlagBitsKHR::ePostMultiplied) | VkFlags(CompositeAlphaFlagBitsKHR::eInherit) + }; + }; + + enum class SurfaceTransformFlagBitsKHR + { + eIdentity = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + eRotate90 = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, + eRotate180 = VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, + eRotate270 = VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, + eHorizontalMirror = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, + eHorizontalMirrorRotate90 = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR, + eHorizontalMirrorRotate180 = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR, + eHorizontalMirrorRotate270 = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR, + eInherit = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR + }; + + using SurfaceTransformFlagsKHR = Flags; + + VULKAN_HPP_INLINE SurfaceTransformFlagsKHR operator|( SurfaceTransformFlagBitsKHR bit0, SurfaceTransformFlagBitsKHR bit1 ) + { + return SurfaceTransformFlagsKHR( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE SurfaceTransformFlagsKHR operator~( SurfaceTransformFlagBitsKHR bits ) + { + return ~( SurfaceTransformFlagsKHR( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(SurfaceTransformFlagBitsKHR::eIdentity) | VkFlags(SurfaceTransformFlagBitsKHR::eRotate90) | VkFlags(SurfaceTransformFlagBitsKHR::eRotate180) | VkFlags(SurfaceTransformFlagBitsKHR::eRotate270) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirror) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270) | VkFlags(SurfaceTransformFlagBitsKHR::eInherit) + }; + }; + + struct DisplayPropertiesKHR + { + operator const VkDisplayPropertiesKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayPropertiesKHR const& rhs ) const + { + return ( display == rhs.display ) + && ( displayName == rhs.displayName ) + && ( physicalDimensions == rhs.physicalDimensions ) + && ( physicalResolution == rhs.physicalResolution ) + && ( supportedTransforms == rhs.supportedTransforms ) + && ( planeReorderPossible == rhs.planeReorderPossible ) + && ( persistentContent == rhs.persistentContent ); + } + + bool operator!=( DisplayPropertiesKHR const& rhs ) const + { + return !operator==( rhs ); + } + + DisplayKHR display; + const char* displayName; + Extent2D physicalDimensions; + Extent2D physicalResolution; + SurfaceTransformFlagsKHR supportedTransforms; + Bool32 planeReorderPossible; + Bool32 persistentContent; + }; + static_assert( sizeof( DisplayPropertiesKHR ) == sizeof( VkDisplayPropertiesKHR ), "struct and wrapper have different size!" ); + + struct DisplaySurfaceCreateInfoKHR + { + DisplaySurfaceCreateInfoKHR( DisplaySurfaceCreateFlagsKHR flags_ = DisplaySurfaceCreateFlagsKHR(), DisplayModeKHR displayMode_ = DisplayModeKHR(), uint32_t planeIndex_ = 0, uint32_t planeStackIndex_ = 0, SurfaceTransformFlagBitsKHR transform_ = SurfaceTransformFlagBitsKHR::eIdentity, float globalAlpha_ = 0, DisplayPlaneAlphaFlagBitsKHR alphaMode_ = DisplayPlaneAlphaFlagBitsKHR::eOpaque, Extent2D imageExtent_ = Extent2D() ) + : sType( StructureType::eDisplaySurfaceCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , displayMode( displayMode_ ) + , planeIndex( planeIndex_ ) + , planeStackIndex( planeStackIndex_ ) + , transform( transform_ ) + , globalAlpha( globalAlpha_ ) + , alphaMode( alphaMode_ ) + , imageExtent( imageExtent_ ) + { + } + + DisplaySurfaceCreateInfoKHR( VkDisplaySurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplaySurfaceCreateInfoKHR) ); + } + + DisplaySurfaceCreateInfoKHR& operator=( VkDisplaySurfaceCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplaySurfaceCreateInfoKHR) ); + return *this; + } + + DisplaySurfaceCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setFlags( DisplaySurfaceCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setDisplayMode( DisplayModeKHR displayMode_ ) + { + displayMode = displayMode_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setPlaneIndex( uint32_t planeIndex_ ) + { + planeIndex = planeIndex_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setPlaneStackIndex( uint32_t planeStackIndex_ ) + { + planeStackIndex = planeStackIndex_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setTransform( SurfaceTransformFlagBitsKHR transform_ ) + { + transform = transform_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setGlobalAlpha( float globalAlpha_ ) + { + globalAlpha = globalAlpha_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setAlphaMode( DisplayPlaneAlphaFlagBitsKHR alphaMode_ ) + { + alphaMode = alphaMode_; + return *this; + } + + DisplaySurfaceCreateInfoKHR& setImageExtent( Extent2D imageExtent_ ) + { + imageExtent = imageExtent_; + return *this; + } + + operator const VkDisplaySurfaceCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplaySurfaceCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( displayMode == rhs.displayMode ) + && ( planeIndex == rhs.planeIndex ) + && ( planeStackIndex == rhs.planeStackIndex ) + && ( transform == rhs.transform ) + && ( globalAlpha == rhs.globalAlpha ) + && ( alphaMode == rhs.alphaMode ) + && ( imageExtent == rhs.imageExtent ); + } + + bool operator!=( DisplaySurfaceCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DisplaySurfaceCreateFlagsKHR flags; + DisplayModeKHR displayMode; + uint32_t planeIndex; + uint32_t planeStackIndex; + SurfaceTransformFlagBitsKHR transform; + float globalAlpha; + DisplayPlaneAlphaFlagBitsKHR alphaMode; + Extent2D imageExtent; + }; + static_assert( sizeof( DisplaySurfaceCreateInfoKHR ) == sizeof( VkDisplaySurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); + + struct SurfaceCapabilitiesKHR + { + operator const VkSurfaceCapabilitiesKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SurfaceCapabilitiesKHR const& rhs ) const + { + return ( minImageCount == rhs.minImageCount ) + && ( maxImageCount == rhs.maxImageCount ) + && ( currentExtent == rhs.currentExtent ) + && ( minImageExtent == rhs.minImageExtent ) + && ( maxImageExtent == rhs.maxImageExtent ) + && ( maxImageArrayLayers == rhs.maxImageArrayLayers ) + && ( supportedTransforms == rhs.supportedTransforms ) + && ( currentTransform == rhs.currentTransform ) + && ( supportedCompositeAlpha == rhs.supportedCompositeAlpha ) + && ( supportedUsageFlags == rhs.supportedUsageFlags ); + } + + bool operator!=( SurfaceCapabilitiesKHR const& rhs ) const + { + return !operator==( rhs ); + } + + uint32_t minImageCount; + uint32_t maxImageCount; + Extent2D currentExtent; + Extent2D minImageExtent; + Extent2D maxImageExtent; + uint32_t maxImageArrayLayers; + SurfaceTransformFlagsKHR supportedTransforms; + SurfaceTransformFlagBitsKHR currentTransform; + CompositeAlphaFlagsKHR supportedCompositeAlpha; + ImageUsageFlags supportedUsageFlags; + }; + static_assert( sizeof( SurfaceCapabilitiesKHR ) == sizeof( VkSurfaceCapabilitiesKHR ), "struct and wrapper have different size!" ); + + struct SwapchainCreateInfoKHR + { + SwapchainCreateInfoKHR( SwapchainCreateFlagsKHR flags_ = SwapchainCreateFlagsKHR(), SurfaceKHR surface_ = SurfaceKHR(), uint32_t minImageCount_ = 0, Format imageFormat_ = Format::eUndefined, ColorSpaceKHR imageColorSpace_ = ColorSpaceKHR::eSrgbNonlinear, Extent2D imageExtent_ = Extent2D(), uint32_t imageArrayLayers_ = 0, ImageUsageFlags imageUsage_ = ImageUsageFlags(), SharingMode imageSharingMode_ = SharingMode::eExclusive, uint32_t queueFamilyIndexCount_ = 0, const uint32_t* pQueueFamilyIndices_ = nullptr, SurfaceTransformFlagBitsKHR preTransform_ = SurfaceTransformFlagBitsKHR::eIdentity, CompositeAlphaFlagBitsKHR compositeAlpha_ = CompositeAlphaFlagBitsKHR::eOpaque, PresentModeKHR presentMode_ = PresentModeKHR::eImmediate, Bool32 clipped_ = 0, SwapchainKHR oldSwapchain_ = SwapchainKHR() ) + : sType( StructureType::eSwapchainCreateInfoKHR ) + , pNext( nullptr ) + , flags( flags_ ) + , surface( surface_ ) + , minImageCount( minImageCount_ ) + , imageFormat( imageFormat_ ) + , imageColorSpace( imageColorSpace_ ) + , imageExtent( imageExtent_ ) + , imageArrayLayers( imageArrayLayers_ ) + , imageUsage( imageUsage_ ) + , imageSharingMode( imageSharingMode_ ) + , queueFamilyIndexCount( queueFamilyIndexCount_ ) + , pQueueFamilyIndices( pQueueFamilyIndices_ ) + , preTransform( preTransform_ ) + , compositeAlpha( compositeAlpha_ ) + , presentMode( presentMode_ ) + , clipped( clipped_ ) + , oldSwapchain( oldSwapchain_ ) + { + } + + SwapchainCreateInfoKHR( VkSwapchainCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(SwapchainCreateInfoKHR) ); + } + + SwapchainCreateInfoKHR& operator=( VkSwapchainCreateInfoKHR const & rhs ) + { + memcpy( this, &rhs, sizeof(SwapchainCreateInfoKHR) ); + return *this; + } + + SwapchainCreateInfoKHR& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + SwapchainCreateInfoKHR& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + SwapchainCreateInfoKHR& setFlags( SwapchainCreateFlagsKHR flags_ ) + { + flags = flags_; + return *this; + } + + SwapchainCreateInfoKHR& setSurface( SurfaceKHR surface_ ) + { + surface = surface_; + return *this; + } + + SwapchainCreateInfoKHR& setMinImageCount( uint32_t minImageCount_ ) + { + minImageCount = minImageCount_; + return *this; + } + + SwapchainCreateInfoKHR& setImageFormat( Format imageFormat_ ) + { + imageFormat = imageFormat_; + return *this; + } + + SwapchainCreateInfoKHR& setImageColorSpace( ColorSpaceKHR imageColorSpace_ ) + { + imageColorSpace = imageColorSpace_; + return *this; + } + + SwapchainCreateInfoKHR& setImageExtent( Extent2D imageExtent_ ) + { + imageExtent = imageExtent_; + return *this; + } + + SwapchainCreateInfoKHR& setImageArrayLayers( uint32_t imageArrayLayers_ ) + { + imageArrayLayers = imageArrayLayers_; + return *this; + } + + SwapchainCreateInfoKHR& setImageUsage( ImageUsageFlags imageUsage_ ) + { + imageUsage = imageUsage_; + return *this; + } + + SwapchainCreateInfoKHR& setImageSharingMode( SharingMode imageSharingMode_ ) + { + imageSharingMode = imageSharingMode_; + return *this; + } + + SwapchainCreateInfoKHR& setQueueFamilyIndexCount( uint32_t queueFamilyIndexCount_ ) + { + queueFamilyIndexCount = queueFamilyIndexCount_; + return *this; + } + + SwapchainCreateInfoKHR& setPQueueFamilyIndices( const uint32_t* pQueueFamilyIndices_ ) + { + pQueueFamilyIndices = pQueueFamilyIndices_; + return *this; + } + + SwapchainCreateInfoKHR& setPreTransform( SurfaceTransformFlagBitsKHR preTransform_ ) + { + preTransform = preTransform_; + return *this; + } + + SwapchainCreateInfoKHR& setCompositeAlpha( CompositeAlphaFlagBitsKHR compositeAlpha_ ) + { + compositeAlpha = compositeAlpha_; + return *this; + } + + SwapchainCreateInfoKHR& setPresentMode( PresentModeKHR presentMode_ ) + { + presentMode = presentMode_; + return *this; + } + + SwapchainCreateInfoKHR& setClipped( Bool32 clipped_ ) + { + clipped = clipped_; + return *this; + } + + SwapchainCreateInfoKHR& setOldSwapchain( SwapchainKHR oldSwapchain_ ) + { + oldSwapchain = oldSwapchain_; + return *this; + } + + operator const VkSwapchainCreateInfoKHR&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SwapchainCreateInfoKHR const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( surface == rhs.surface ) + && ( minImageCount == rhs.minImageCount ) + && ( imageFormat == rhs.imageFormat ) + && ( imageColorSpace == rhs.imageColorSpace ) + && ( imageExtent == rhs.imageExtent ) + && ( imageArrayLayers == rhs.imageArrayLayers ) + && ( imageUsage == rhs.imageUsage ) + && ( imageSharingMode == rhs.imageSharingMode ) + && ( queueFamilyIndexCount == rhs.queueFamilyIndexCount ) + && ( pQueueFamilyIndices == rhs.pQueueFamilyIndices ) + && ( preTransform == rhs.preTransform ) + && ( compositeAlpha == rhs.compositeAlpha ) + && ( presentMode == rhs.presentMode ) + && ( clipped == rhs.clipped ) + && ( oldSwapchain == rhs.oldSwapchain ); + } + + bool operator!=( SwapchainCreateInfoKHR const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + SwapchainCreateFlagsKHR flags; + SurfaceKHR surface; + uint32_t minImageCount; + Format imageFormat; + ColorSpaceKHR imageColorSpace; + Extent2D imageExtent; + uint32_t imageArrayLayers; + ImageUsageFlags imageUsage; + SharingMode imageSharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; + SurfaceTransformFlagBitsKHR preTransform; + CompositeAlphaFlagBitsKHR compositeAlpha; + PresentModeKHR presentMode; + Bool32 clipped; + SwapchainKHR oldSwapchain; + }; + static_assert( sizeof( SwapchainCreateInfoKHR ) == sizeof( VkSwapchainCreateInfoKHR ), "struct and wrapper have different size!" ); + + enum class DebugReportFlagBitsEXT + { + eInformation = VK_DEBUG_REPORT_INFORMATION_BIT_EXT, + eWarning = VK_DEBUG_REPORT_WARNING_BIT_EXT, + ePerformanceWarning = VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, + eError = VK_DEBUG_REPORT_ERROR_BIT_EXT, + eDebug = VK_DEBUG_REPORT_DEBUG_BIT_EXT + }; + + using DebugReportFlagsEXT = Flags; + + VULKAN_HPP_INLINE DebugReportFlagsEXT operator|( DebugReportFlagBitsEXT bit0, DebugReportFlagBitsEXT bit1 ) + { + return DebugReportFlagsEXT( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE DebugReportFlagsEXT operator~( DebugReportFlagBitsEXT bits ) + { + return ~( DebugReportFlagsEXT( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(DebugReportFlagBitsEXT::eInformation) | VkFlags(DebugReportFlagBitsEXT::eWarning) | VkFlags(DebugReportFlagBitsEXT::ePerformanceWarning) | VkFlags(DebugReportFlagBitsEXT::eError) | VkFlags(DebugReportFlagBitsEXT::eDebug) + }; + }; + + struct DebugReportCallbackCreateInfoEXT + { + DebugReportCallbackCreateInfoEXT( DebugReportFlagsEXT flags_ = DebugReportFlagsEXT(), PFN_vkDebugReportCallbackEXT pfnCallback_ = nullptr, void* pUserData_ = nullptr ) + : sType( StructureType::eDebugReportCallbackCreateInfoEXT ) + , pNext( nullptr ) + , flags( flags_ ) + , pfnCallback( pfnCallback_ ) + , pUserData( pUserData_ ) + { + } + + DebugReportCallbackCreateInfoEXT( VkDebugReportCallbackCreateInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugReportCallbackCreateInfoEXT) ); + } + + DebugReportCallbackCreateInfoEXT& operator=( VkDebugReportCallbackCreateInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugReportCallbackCreateInfoEXT) ); + return *this; + } + + DebugReportCallbackCreateInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DebugReportCallbackCreateInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DebugReportCallbackCreateInfoEXT& setFlags( DebugReportFlagsEXT flags_ ) + { + flags = flags_; + return *this; + } + + DebugReportCallbackCreateInfoEXT& setPfnCallback( PFN_vkDebugReportCallbackEXT pfnCallback_ ) + { + pfnCallback = pfnCallback_; + return *this; + } + + DebugReportCallbackCreateInfoEXT& setPUserData( void* pUserData_ ) + { + pUserData = pUserData_; + return *this; + } + + operator const VkDebugReportCallbackCreateInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DebugReportCallbackCreateInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( flags == rhs.flags ) + && ( pfnCallback == rhs.pfnCallback ) + && ( pUserData == rhs.pUserData ); + } + + bool operator!=( DebugReportCallbackCreateInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DebugReportFlagsEXT flags; + PFN_vkDebugReportCallbackEXT pfnCallback; + void* pUserData; + }; + static_assert( sizeof( DebugReportCallbackCreateInfoEXT ) == sizeof( VkDebugReportCallbackCreateInfoEXT ), "struct and wrapper have different size!" ); + + enum class DebugReportObjectTypeEXT + { + eUnknown = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, + eInstance = VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, + ePhysicalDevice = VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT, + eDevice = VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, + eQueue = VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT, + eSemaphore = VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, + eCommandBuffer = VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + eFence = VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT, + eDeviceMemory = VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT, + eBuffer = VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, + eImage = VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, + eEvent = VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT, + eQueryPool = VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT, + eBufferView = VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT, + eImageView = VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, + eShaderModule = VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, + ePipelineCache = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT, + ePipelineLayout = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT, + eRenderPass = VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, + ePipeline = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, + eDescriptorSetLayout = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT, + eSampler = VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT, + eDescriptorPool = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT, + eDescriptorSet = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, + eFramebuffer = VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, + eCommandPool = VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT, + eSurfaceKhr = VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT, + eSwapchainKhr = VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT, + eDebugReport = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, + eDisplayKhr = VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT, + eDisplayModeKhr = VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT, + eObjectTableNvx = VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT, + eIndirectCommandsLayoutNvx = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT + }; + + struct DebugMarkerObjectNameInfoEXT + { + DebugMarkerObjectNameInfoEXT( DebugReportObjectTypeEXT objectType_ = DebugReportObjectTypeEXT::eUnknown, uint64_t object_ = 0, const char* pObjectName_ = nullptr ) + : sType( StructureType::eDebugMarkerObjectNameInfoEXT ) + , pNext( nullptr ) + , objectType( objectType_ ) + , object( object_ ) + , pObjectName( pObjectName_ ) + { + } + + DebugMarkerObjectNameInfoEXT( VkDebugMarkerObjectNameInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugMarkerObjectNameInfoEXT) ); + } + + DebugMarkerObjectNameInfoEXT& operator=( VkDebugMarkerObjectNameInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugMarkerObjectNameInfoEXT) ); + return *this; + } + + DebugMarkerObjectNameInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DebugMarkerObjectNameInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DebugMarkerObjectNameInfoEXT& setObjectType( DebugReportObjectTypeEXT objectType_ ) + { + objectType = objectType_; + return *this; + } + + DebugMarkerObjectNameInfoEXT& setObject( uint64_t object_ ) + { + object = object_; + return *this; + } + + DebugMarkerObjectNameInfoEXT& setPObjectName( const char* pObjectName_ ) + { + pObjectName = pObjectName_; + return *this; + } + + operator const VkDebugMarkerObjectNameInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DebugMarkerObjectNameInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( objectType == rhs.objectType ) + && ( object == rhs.object ) + && ( pObjectName == rhs.pObjectName ); + } + + bool operator!=( DebugMarkerObjectNameInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DebugReportObjectTypeEXT objectType; + uint64_t object; + const char* pObjectName; + }; + static_assert( sizeof( DebugMarkerObjectNameInfoEXT ) == sizeof( VkDebugMarkerObjectNameInfoEXT ), "struct and wrapper have different size!" ); + + struct DebugMarkerObjectTagInfoEXT + { + DebugMarkerObjectTagInfoEXT( DebugReportObjectTypeEXT objectType_ = DebugReportObjectTypeEXT::eUnknown, uint64_t object_ = 0, uint64_t tagName_ = 0, size_t tagSize_ = 0, const void* pTag_ = nullptr ) + : sType( StructureType::eDebugMarkerObjectTagInfoEXT ) + , pNext( nullptr ) + , objectType( objectType_ ) + , object( object_ ) + , tagName( tagName_ ) + , tagSize( tagSize_ ) + , pTag( pTag_ ) + { + } + + DebugMarkerObjectTagInfoEXT( VkDebugMarkerObjectTagInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugMarkerObjectTagInfoEXT) ); + } + + DebugMarkerObjectTagInfoEXT& operator=( VkDebugMarkerObjectTagInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DebugMarkerObjectTagInfoEXT) ); + return *this; + } + + DebugMarkerObjectTagInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DebugMarkerObjectTagInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DebugMarkerObjectTagInfoEXT& setObjectType( DebugReportObjectTypeEXT objectType_ ) + { + objectType = objectType_; + return *this; + } + + DebugMarkerObjectTagInfoEXT& setObject( uint64_t object_ ) + { + object = object_; + return *this; + } + + DebugMarkerObjectTagInfoEXT& setTagName( uint64_t tagName_ ) + { + tagName = tagName_; + return *this; + } + + DebugMarkerObjectTagInfoEXT& setTagSize( size_t tagSize_ ) + { + tagSize = tagSize_; + return *this; + } + + DebugMarkerObjectTagInfoEXT& setPTag( const void* pTag_ ) + { + pTag = pTag_; + return *this; + } + + operator const VkDebugMarkerObjectTagInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DebugMarkerObjectTagInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( objectType == rhs.objectType ) + && ( object == rhs.object ) + && ( tagName == rhs.tagName ) + && ( tagSize == rhs.tagSize ) + && ( pTag == rhs.pTag ); + } + + bool operator!=( DebugMarkerObjectTagInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DebugReportObjectTypeEXT objectType; + uint64_t object; + uint64_t tagName; + size_t tagSize; + const void* pTag; + }; + static_assert( sizeof( DebugMarkerObjectTagInfoEXT ) == sizeof( VkDebugMarkerObjectTagInfoEXT ), "struct and wrapper have different size!" ); + + enum class DebugReportErrorEXT + { + eNone = VK_DEBUG_REPORT_ERROR_NONE_EXT, + eCallbackRef = VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT + }; + + enum class RasterizationOrderAMD + { + eStrict = VK_RASTERIZATION_ORDER_STRICT_AMD, + eRelaxed = VK_RASTERIZATION_ORDER_RELAXED_AMD + }; + + struct PipelineRasterizationStateRasterizationOrderAMD + { + PipelineRasterizationStateRasterizationOrderAMD( RasterizationOrderAMD rasterizationOrder_ = RasterizationOrderAMD::eStrict ) + : sType( StructureType::ePipelineRasterizationStateRasterizationOrderAMD ) + , pNext( nullptr ) + , rasterizationOrder( rasterizationOrder_ ) + { + } + + PipelineRasterizationStateRasterizationOrderAMD( VkPipelineRasterizationStateRasterizationOrderAMD const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineRasterizationStateRasterizationOrderAMD) ); + } + + PipelineRasterizationStateRasterizationOrderAMD& operator=( VkPipelineRasterizationStateRasterizationOrderAMD const & rhs ) + { + memcpy( this, &rhs, sizeof(PipelineRasterizationStateRasterizationOrderAMD) ); + return *this; + } + + PipelineRasterizationStateRasterizationOrderAMD& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + PipelineRasterizationStateRasterizationOrderAMD& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + PipelineRasterizationStateRasterizationOrderAMD& setRasterizationOrder( RasterizationOrderAMD rasterizationOrder_ ) + { + rasterizationOrder = rasterizationOrder_; + return *this; + } + + operator const VkPipelineRasterizationStateRasterizationOrderAMD&() const + { + return *reinterpret_cast(this); + } + + bool operator==( PipelineRasterizationStateRasterizationOrderAMD const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( rasterizationOrder == rhs.rasterizationOrder ); + } + + bool operator!=( PipelineRasterizationStateRasterizationOrderAMD const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + RasterizationOrderAMD rasterizationOrder; + }; + static_assert( sizeof( PipelineRasterizationStateRasterizationOrderAMD ) == sizeof( VkPipelineRasterizationStateRasterizationOrderAMD ), "struct and wrapper have different size!" ); + + enum class ExternalMemoryHandleTypeFlagBitsNV + { + eOpaqueWin32 = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV, + eOpaqueWin32Kmt = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV, + eD3D11Image = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV, + eD3D11ImageKmt = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV + }; + + using ExternalMemoryHandleTypeFlagsNV = Flags; + + VULKAN_HPP_INLINE ExternalMemoryHandleTypeFlagsNV operator|( ExternalMemoryHandleTypeFlagBitsNV bit0, ExternalMemoryHandleTypeFlagBitsNV bit1 ) + { + return ExternalMemoryHandleTypeFlagsNV( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ExternalMemoryHandleTypeFlagsNV operator~( ExternalMemoryHandleTypeFlagBitsNV bits ) + { + return ~( ExternalMemoryHandleTypeFlagsNV( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32) | VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32Kmt) | VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eD3D11Image) | VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eD3D11ImageKmt) + }; + }; + + struct ExternalMemoryImageCreateInfoNV + { + ExternalMemoryImageCreateInfoNV( ExternalMemoryHandleTypeFlagsNV handleTypes_ = ExternalMemoryHandleTypeFlagsNV() ) + : sType( StructureType::eExternalMemoryImageCreateInfoNV ) + , pNext( nullptr ) + , handleTypes( handleTypes_ ) + { + } + + ExternalMemoryImageCreateInfoNV( VkExternalMemoryImageCreateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ExternalMemoryImageCreateInfoNV) ); + } + + ExternalMemoryImageCreateInfoNV& operator=( VkExternalMemoryImageCreateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ExternalMemoryImageCreateInfoNV) ); + return *this; + } + + ExternalMemoryImageCreateInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ExternalMemoryImageCreateInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ExternalMemoryImageCreateInfoNV& setHandleTypes( ExternalMemoryHandleTypeFlagsNV handleTypes_ ) + { + handleTypes = handleTypes_; + return *this; + } + + operator const VkExternalMemoryImageCreateInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ExternalMemoryImageCreateInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( handleTypes == rhs.handleTypes ); + } + + bool operator!=( ExternalMemoryImageCreateInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ExternalMemoryHandleTypeFlagsNV handleTypes; + }; + static_assert( sizeof( ExternalMemoryImageCreateInfoNV ) == sizeof( VkExternalMemoryImageCreateInfoNV ), "struct and wrapper have different size!" ); + + struct ExportMemoryAllocateInfoNV + { + ExportMemoryAllocateInfoNV( ExternalMemoryHandleTypeFlagsNV handleTypes_ = ExternalMemoryHandleTypeFlagsNV() ) + : sType( StructureType::eExportMemoryAllocateInfoNV ) + , pNext( nullptr ) + , handleTypes( handleTypes_ ) + { + } + + ExportMemoryAllocateInfoNV( VkExportMemoryAllocateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ExportMemoryAllocateInfoNV) ); + } + + ExportMemoryAllocateInfoNV& operator=( VkExportMemoryAllocateInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ExportMemoryAllocateInfoNV) ); + return *this; + } + + ExportMemoryAllocateInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ExportMemoryAllocateInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ExportMemoryAllocateInfoNV& setHandleTypes( ExternalMemoryHandleTypeFlagsNV handleTypes_ ) + { + handleTypes = handleTypes_; + return *this; + } + + operator const VkExportMemoryAllocateInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ExportMemoryAllocateInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( handleTypes == rhs.handleTypes ); + } + + bool operator!=( ExportMemoryAllocateInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ExternalMemoryHandleTypeFlagsNV handleTypes; + }; + static_assert( sizeof( ExportMemoryAllocateInfoNV ) == sizeof( VkExportMemoryAllocateInfoNV ), "struct and wrapper have different size!" ); + +#ifdef VK_USE_PLATFORM_WIN32_KHR + struct ImportMemoryWin32HandleInfoNV + { + ImportMemoryWin32HandleInfoNV( ExternalMemoryHandleTypeFlagsNV handleType_ = ExternalMemoryHandleTypeFlagsNV(), HANDLE handle_ = 0 ) + : sType( StructureType::eImportMemoryWin32HandleInfoNV ) + , pNext( nullptr ) + , handleType( handleType_ ) + , handle( handle_ ) + { + } + + ImportMemoryWin32HandleInfoNV( VkImportMemoryWin32HandleInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ImportMemoryWin32HandleInfoNV) ); + } + + ImportMemoryWin32HandleInfoNV& operator=( VkImportMemoryWin32HandleInfoNV const & rhs ) + { + memcpy( this, &rhs, sizeof(ImportMemoryWin32HandleInfoNV) ); + return *this; + } + + ImportMemoryWin32HandleInfoNV& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ImportMemoryWin32HandleInfoNV& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ImportMemoryWin32HandleInfoNV& setHandleType( ExternalMemoryHandleTypeFlagsNV handleType_ ) + { + handleType = handleType_; + return *this; + } + + ImportMemoryWin32HandleInfoNV& setHandle( HANDLE handle_ ) + { + handle = handle_; + return *this; + } + + operator const VkImportMemoryWin32HandleInfoNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ImportMemoryWin32HandleInfoNV const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( handleType == rhs.handleType ) + && ( handle == rhs.handle ); + } + + bool operator!=( ImportMemoryWin32HandleInfoNV const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ExternalMemoryHandleTypeFlagsNV handleType; + HANDLE handle; + }; + static_assert( sizeof( ImportMemoryWin32HandleInfoNV ) == sizeof( VkImportMemoryWin32HandleInfoNV ), "struct and wrapper have different size!" ); +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + + enum class ExternalMemoryFeatureFlagBitsNV + { + eDedicatedOnly = VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV, + eExportable = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV, + eImportable = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV + }; + + using ExternalMemoryFeatureFlagsNV = Flags; + + VULKAN_HPP_INLINE ExternalMemoryFeatureFlagsNV operator|( ExternalMemoryFeatureFlagBitsNV bit0, ExternalMemoryFeatureFlagBitsNV bit1 ) + { + return ExternalMemoryFeatureFlagsNV( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ExternalMemoryFeatureFlagsNV operator~( ExternalMemoryFeatureFlagBitsNV bits ) + { + return ~( ExternalMemoryFeatureFlagsNV( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ExternalMemoryFeatureFlagBitsNV::eDedicatedOnly) | VkFlags(ExternalMemoryFeatureFlagBitsNV::eExportable) | VkFlags(ExternalMemoryFeatureFlagBitsNV::eImportable) + }; + }; + + struct ExternalImageFormatPropertiesNV + { + operator const VkExternalImageFormatPropertiesNV&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ExternalImageFormatPropertiesNV const& rhs ) const + { + return ( imageFormatProperties == rhs.imageFormatProperties ) + && ( externalMemoryFeatures == rhs.externalMemoryFeatures ) + && ( exportFromImportedHandleTypes == rhs.exportFromImportedHandleTypes ) + && ( compatibleHandleTypes == rhs.compatibleHandleTypes ); + } + + bool operator!=( ExternalImageFormatPropertiesNV const& rhs ) const + { + return !operator==( rhs ); + } + + ImageFormatProperties imageFormatProperties; + ExternalMemoryFeatureFlagsNV externalMemoryFeatures; + ExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes; + ExternalMemoryHandleTypeFlagsNV compatibleHandleTypes; + }; + static_assert( sizeof( ExternalImageFormatPropertiesNV ) == sizeof( VkExternalImageFormatPropertiesNV ), "struct and wrapper have different size!" ); + + enum class ValidationCheckEXT + { + eAll = VK_VALIDATION_CHECK_ALL_EXT + }; + + struct ValidationFlagsEXT + { + ValidationFlagsEXT( uint32_t disabledValidationCheckCount_ = 0, ValidationCheckEXT* pDisabledValidationChecks_ = nullptr ) + : sType( StructureType::eValidationFlagsEXT ) + , pNext( nullptr ) + , disabledValidationCheckCount( disabledValidationCheckCount_ ) + , pDisabledValidationChecks( pDisabledValidationChecks_ ) + { + } + + ValidationFlagsEXT( VkValidationFlagsEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(ValidationFlagsEXT) ); + } + + ValidationFlagsEXT& operator=( VkValidationFlagsEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(ValidationFlagsEXT) ); + return *this; + } + + ValidationFlagsEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ValidationFlagsEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ValidationFlagsEXT& setDisabledValidationCheckCount( uint32_t disabledValidationCheckCount_ ) + { + disabledValidationCheckCount = disabledValidationCheckCount_; + return *this; + } + + ValidationFlagsEXT& setPDisabledValidationChecks( ValidationCheckEXT* pDisabledValidationChecks_ ) + { + pDisabledValidationChecks = pDisabledValidationChecks_; + return *this; + } + + operator const VkValidationFlagsEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ValidationFlagsEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( disabledValidationCheckCount == rhs.disabledValidationCheckCount ) + && ( pDisabledValidationChecks == rhs.pDisabledValidationChecks ); + } + + bool operator!=( ValidationFlagsEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + uint32_t disabledValidationCheckCount; + ValidationCheckEXT* pDisabledValidationChecks; + }; + static_assert( sizeof( ValidationFlagsEXT ) == sizeof( VkValidationFlagsEXT ), "struct and wrapper have different size!" ); + + enum class IndirectCommandsLayoutUsageFlagBitsNVX + { + eUnorderedSequences = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX, + eSparseSequences = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX, + eEmptyExecutions = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX, + eIndexedSequences = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX + }; + + using IndirectCommandsLayoutUsageFlagsNVX = Flags; + + VULKAN_HPP_INLINE IndirectCommandsLayoutUsageFlagsNVX operator|( IndirectCommandsLayoutUsageFlagBitsNVX bit0, IndirectCommandsLayoutUsageFlagBitsNVX bit1 ) + { + return IndirectCommandsLayoutUsageFlagsNVX( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE IndirectCommandsLayoutUsageFlagsNVX operator~( IndirectCommandsLayoutUsageFlagBitsNVX bits ) + { + return ~( IndirectCommandsLayoutUsageFlagsNVX( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eUnorderedSequences) | VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eSparseSequences) | VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eEmptyExecutions) | VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eIndexedSequences) + }; + }; + + enum class ObjectEntryUsageFlagBitsNVX + { + eGraphics = VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX, + eCompute = VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX + }; + + using ObjectEntryUsageFlagsNVX = Flags; + + VULKAN_HPP_INLINE ObjectEntryUsageFlagsNVX operator|( ObjectEntryUsageFlagBitsNVX bit0, ObjectEntryUsageFlagBitsNVX bit1 ) + { + return ObjectEntryUsageFlagsNVX( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE ObjectEntryUsageFlagsNVX operator~( ObjectEntryUsageFlagBitsNVX bits ) + { + return ~( ObjectEntryUsageFlagsNVX( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(ObjectEntryUsageFlagBitsNVX::eGraphics) | VkFlags(ObjectEntryUsageFlagBitsNVX::eCompute) + }; + }; + + enum class IndirectCommandsTokenTypeNVX + { + eVkIndirectCommandsTokenPipeline = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX, + eVkIndirectCommandsTokenDescriptorSet = VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX, + eVkIndirectCommandsTokenIndexBuffer = VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX, + eVkIndirectCommandsTokenVertexBuffer = VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX, + eVkIndirectCommandsTokenPushConstant = VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX, + eVkIndirectCommandsTokenDrawIndexed = VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX, + eVkIndirectCommandsTokenDraw = VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX, + eVkIndirectCommandsTokenDispatch = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX + }; + + struct IndirectCommandsTokenNVX + { + IndirectCommandsTokenNVX( IndirectCommandsTokenTypeNVX tokenType_ = IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPipeline, Buffer buffer_ = Buffer(), DeviceSize offset_ = 0 ) + : tokenType( tokenType_ ) + , buffer( buffer_ ) + , offset( offset_ ) + { + } + + IndirectCommandsTokenNVX( VkIndirectCommandsTokenNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(IndirectCommandsTokenNVX) ); + } + + IndirectCommandsTokenNVX& operator=( VkIndirectCommandsTokenNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(IndirectCommandsTokenNVX) ); + return *this; + } + + IndirectCommandsTokenNVX& setTokenType( IndirectCommandsTokenTypeNVX tokenType_ ) + { + tokenType = tokenType_; + return *this; + } + + IndirectCommandsTokenNVX& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + IndirectCommandsTokenNVX& setOffset( DeviceSize offset_ ) + { + offset = offset_; + return *this; + } + + operator const VkIndirectCommandsTokenNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( IndirectCommandsTokenNVX const& rhs ) const + { + return ( tokenType == rhs.tokenType ) + && ( buffer == rhs.buffer ) + && ( offset == rhs.offset ); + } + + bool operator!=( IndirectCommandsTokenNVX const& rhs ) const + { + return !operator==( rhs ); + } + + IndirectCommandsTokenTypeNVX tokenType; + Buffer buffer; + DeviceSize offset; + }; + static_assert( sizeof( IndirectCommandsTokenNVX ) == sizeof( VkIndirectCommandsTokenNVX ), "struct and wrapper have different size!" ); + + struct IndirectCommandsLayoutTokenNVX + { + IndirectCommandsLayoutTokenNVX( IndirectCommandsTokenTypeNVX tokenType_ = IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPipeline, uint32_t bindingUnit_ = 0, uint32_t dynamicCount_ = 0, uint32_t divisor_ = 0 ) + : tokenType( tokenType_ ) + , bindingUnit( bindingUnit_ ) + , dynamicCount( dynamicCount_ ) + , divisor( divisor_ ) + { + } + + IndirectCommandsLayoutTokenNVX( VkIndirectCommandsLayoutTokenNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(IndirectCommandsLayoutTokenNVX) ); + } + + IndirectCommandsLayoutTokenNVX& operator=( VkIndirectCommandsLayoutTokenNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(IndirectCommandsLayoutTokenNVX) ); + return *this; + } + + IndirectCommandsLayoutTokenNVX& setTokenType( IndirectCommandsTokenTypeNVX tokenType_ ) + { + tokenType = tokenType_; + return *this; + } + + IndirectCommandsLayoutTokenNVX& setBindingUnit( uint32_t bindingUnit_ ) + { + bindingUnit = bindingUnit_; + return *this; + } + + IndirectCommandsLayoutTokenNVX& setDynamicCount( uint32_t dynamicCount_ ) + { + dynamicCount = dynamicCount_; + return *this; + } + + IndirectCommandsLayoutTokenNVX& setDivisor( uint32_t divisor_ ) + { + divisor = divisor_; + return *this; + } + + operator const VkIndirectCommandsLayoutTokenNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( IndirectCommandsLayoutTokenNVX const& rhs ) const + { + return ( tokenType == rhs.tokenType ) + && ( bindingUnit == rhs.bindingUnit ) + && ( dynamicCount == rhs.dynamicCount ) + && ( divisor == rhs.divisor ); + } + + bool operator!=( IndirectCommandsLayoutTokenNVX const& rhs ) const + { + return !operator==( rhs ); + } + + IndirectCommandsTokenTypeNVX tokenType; + uint32_t bindingUnit; + uint32_t dynamicCount; + uint32_t divisor; + }; + static_assert( sizeof( IndirectCommandsLayoutTokenNVX ) == sizeof( VkIndirectCommandsLayoutTokenNVX ), "struct and wrapper have different size!" ); + + struct IndirectCommandsLayoutCreateInfoNVX + { + IndirectCommandsLayoutCreateInfoNVX( PipelineBindPoint pipelineBindPoint_ = PipelineBindPoint::eGraphics, IndirectCommandsLayoutUsageFlagsNVX flags_ = IndirectCommandsLayoutUsageFlagsNVX(), uint32_t tokenCount_ = 0, const IndirectCommandsLayoutTokenNVX* pTokens_ = nullptr ) + : sType( StructureType::eIndirectCommandsLayoutCreateInfoNVX ) + , pNext( nullptr ) + , pipelineBindPoint( pipelineBindPoint_ ) + , flags( flags_ ) + , tokenCount( tokenCount_ ) + , pTokens( pTokens_ ) + { + } + + IndirectCommandsLayoutCreateInfoNVX( VkIndirectCommandsLayoutCreateInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(IndirectCommandsLayoutCreateInfoNVX) ); + } + + IndirectCommandsLayoutCreateInfoNVX& operator=( VkIndirectCommandsLayoutCreateInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(IndirectCommandsLayoutCreateInfoNVX) ); + return *this; + } + + IndirectCommandsLayoutCreateInfoNVX& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + IndirectCommandsLayoutCreateInfoNVX& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + IndirectCommandsLayoutCreateInfoNVX& setPipelineBindPoint( PipelineBindPoint pipelineBindPoint_ ) + { + pipelineBindPoint = pipelineBindPoint_; + return *this; + } + + IndirectCommandsLayoutCreateInfoNVX& setFlags( IndirectCommandsLayoutUsageFlagsNVX flags_ ) + { + flags = flags_; + return *this; + } + + IndirectCommandsLayoutCreateInfoNVX& setTokenCount( uint32_t tokenCount_ ) + { + tokenCount = tokenCount_; + return *this; + } + + IndirectCommandsLayoutCreateInfoNVX& setPTokens( const IndirectCommandsLayoutTokenNVX* pTokens_ ) + { + pTokens = pTokens_; + return *this; + } + + operator const VkIndirectCommandsLayoutCreateInfoNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( IndirectCommandsLayoutCreateInfoNVX const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( pipelineBindPoint == rhs.pipelineBindPoint ) + && ( flags == rhs.flags ) + && ( tokenCount == rhs.tokenCount ) + && ( pTokens == rhs.pTokens ); + } + + bool operator!=( IndirectCommandsLayoutCreateInfoNVX const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + PipelineBindPoint pipelineBindPoint; + IndirectCommandsLayoutUsageFlagsNVX flags; + uint32_t tokenCount; + const IndirectCommandsLayoutTokenNVX* pTokens; + }; + static_assert( sizeof( IndirectCommandsLayoutCreateInfoNVX ) == sizeof( VkIndirectCommandsLayoutCreateInfoNVX ), "struct and wrapper have different size!" ); + + enum class ObjectEntryTypeNVX + { + eVkObjectEntryDescriptorSet = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX, + eVkObjectEntryPipeline = VK_OBJECT_ENTRY_PIPELINE_NVX, + eVkObjectEntryIndexBuffer = VK_OBJECT_ENTRY_INDEX_BUFFER_NVX, + eVkObjectEntryVertexBuffer = VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX, + eVkObjectEntryPushConstant = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX + }; + + struct ObjectTableCreateInfoNVX + { + ObjectTableCreateInfoNVX( uint32_t objectCount_ = 0, const ObjectEntryTypeNVX* pObjectEntryTypes_ = nullptr, const uint32_t* pObjectEntryCounts_ = nullptr, const ObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags_ = nullptr, uint32_t maxUniformBuffersPerDescriptor_ = 0, uint32_t maxStorageBuffersPerDescriptor_ = 0, uint32_t maxStorageImagesPerDescriptor_ = 0, uint32_t maxSampledImagesPerDescriptor_ = 0, uint32_t maxPipelineLayouts_ = 0 ) + : sType( StructureType::eObjectTableCreateInfoNVX ) + , pNext( nullptr ) + , objectCount( objectCount_ ) + , pObjectEntryTypes( pObjectEntryTypes_ ) + , pObjectEntryCounts( pObjectEntryCounts_ ) + , pObjectEntryUsageFlags( pObjectEntryUsageFlags_ ) + , maxUniformBuffersPerDescriptor( maxUniformBuffersPerDescriptor_ ) + , maxStorageBuffersPerDescriptor( maxStorageBuffersPerDescriptor_ ) + , maxStorageImagesPerDescriptor( maxStorageImagesPerDescriptor_ ) + , maxSampledImagesPerDescriptor( maxSampledImagesPerDescriptor_ ) + , maxPipelineLayouts( maxPipelineLayouts_ ) + { + } + + ObjectTableCreateInfoNVX( VkObjectTableCreateInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableCreateInfoNVX) ); + } + + ObjectTableCreateInfoNVX& operator=( VkObjectTableCreateInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableCreateInfoNVX) ); + return *this; + } + + ObjectTableCreateInfoNVX& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + ObjectTableCreateInfoNVX& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + ObjectTableCreateInfoNVX& setObjectCount( uint32_t objectCount_ ) + { + objectCount = objectCount_; + return *this; + } + + ObjectTableCreateInfoNVX& setPObjectEntryTypes( const ObjectEntryTypeNVX* pObjectEntryTypes_ ) + { + pObjectEntryTypes = pObjectEntryTypes_; + return *this; + } + + ObjectTableCreateInfoNVX& setPObjectEntryCounts( const uint32_t* pObjectEntryCounts_ ) + { + pObjectEntryCounts = pObjectEntryCounts_; + return *this; + } + + ObjectTableCreateInfoNVX& setPObjectEntryUsageFlags( const ObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags_ ) + { + pObjectEntryUsageFlags = pObjectEntryUsageFlags_; + return *this; + } + + ObjectTableCreateInfoNVX& setMaxUniformBuffersPerDescriptor( uint32_t maxUniformBuffersPerDescriptor_ ) + { + maxUniformBuffersPerDescriptor = maxUniformBuffersPerDescriptor_; + return *this; + } + + ObjectTableCreateInfoNVX& setMaxStorageBuffersPerDescriptor( uint32_t maxStorageBuffersPerDescriptor_ ) + { + maxStorageBuffersPerDescriptor = maxStorageBuffersPerDescriptor_; + return *this; + } + + ObjectTableCreateInfoNVX& setMaxStorageImagesPerDescriptor( uint32_t maxStorageImagesPerDescriptor_ ) + { + maxStorageImagesPerDescriptor = maxStorageImagesPerDescriptor_; + return *this; + } + + ObjectTableCreateInfoNVX& setMaxSampledImagesPerDescriptor( uint32_t maxSampledImagesPerDescriptor_ ) + { + maxSampledImagesPerDescriptor = maxSampledImagesPerDescriptor_; + return *this; + } + + ObjectTableCreateInfoNVX& setMaxPipelineLayouts( uint32_t maxPipelineLayouts_ ) + { + maxPipelineLayouts = maxPipelineLayouts_; + return *this; + } + + operator const VkObjectTableCreateInfoNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ObjectTableCreateInfoNVX const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( objectCount == rhs.objectCount ) + && ( pObjectEntryTypes == rhs.pObjectEntryTypes ) + && ( pObjectEntryCounts == rhs.pObjectEntryCounts ) + && ( pObjectEntryUsageFlags == rhs.pObjectEntryUsageFlags ) + && ( maxUniformBuffersPerDescriptor == rhs.maxUniformBuffersPerDescriptor ) + && ( maxStorageBuffersPerDescriptor == rhs.maxStorageBuffersPerDescriptor ) + && ( maxStorageImagesPerDescriptor == rhs.maxStorageImagesPerDescriptor ) + && ( maxSampledImagesPerDescriptor == rhs.maxSampledImagesPerDescriptor ) + && ( maxPipelineLayouts == rhs.maxPipelineLayouts ); + } + + bool operator!=( ObjectTableCreateInfoNVX const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + uint32_t objectCount; + const ObjectEntryTypeNVX* pObjectEntryTypes; + const uint32_t* pObjectEntryCounts; + const ObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags; + uint32_t maxUniformBuffersPerDescriptor; + uint32_t maxStorageBuffersPerDescriptor; + uint32_t maxStorageImagesPerDescriptor; + uint32_t maxSampledImagesPerDescriptor; + uint32_t maxPipelineLayouts; + }; + static_assert( sizeof( ObjectTableCreateInfoNVX ) == sizeof( VkObjectTableCreateInfoNVX ), "struct and wrapper have different size!" ); + + struct ObjectTableEntryNVX + { + ObjectTableEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX() ) + : type( type_ ) + , flags( flags_ ) + { + } + + ObjectTableEntryNVX( VkObjectTableEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableEntryNVX) ); + } + + ObjectTableEntryNVX& operator=( VkObjectTableEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableEntryNVX) ); + return *this; + } + + ObjectTableEntryNVX& setType( ObjectEntryTypeNVX type_ ) + { + type = type_; + return *this; + } + + ObjectTableEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) + { + flags = flags_; + return *this; + } + + operator const VkObjectTableEntryNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ObjectTableEntryNVX const& rhs ) const + { + return ( type == rhs.type ) + && ( flags == rhs.flags ); + } + + bool operator!=( ObjectTableEntryNVX const& rhs ) const + { + return !operator==( rhs ); + } + + ObjectEntryTypeNVX type; + ObjectEntryUsageFlagsNVX flags; + }; + static_assert( sizeof( ObjectTableEntryNVX ) == sizeof( VkObjectTableEntryNVX ), "struct and wrapper have different size!" ); + + struct ObjectTablePipelineEntryNVX + { + ObjectTablePipelineEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), Pipeline pipeline_ = Pipeline() ) + : type( type_ ) + , flags( flags_ ) + , pipeline( pipeline_ ) + { + } + + ObjectTablePipelineEntryNVX( VkObjectTablePipelineEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTablePipelineEntryNVX) ); + } + + ObjectTablePipelineEntryNVX& operator=( VkObjectTablePipelineEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTablePipelineEntryNVX) ); + return *this; + } + + ObjectTablePipelineEntryNVX& setType( ObjectEntryTypeNVX type_ ) + { + type = type_; + return *this; + } + + ObjectTablePipelineEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) + { + flags = flags_; + return *this; + } + + ObjectTablePipelineEntryNVX& setPipeline( Pipeline pipeline_ ) + { + pipeline = pipeline_; + return *this; + } + + operator const VkObjectTablePipelineEntryNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ObjectTablePipelineEntryNVX const& rhs ) const + { + return ( type == rhs.type ) + && ( flags == rhs.flags ) + && ( pipeline == rhs.pipeline ); + } + + bool operator!=( ObjectTablePipelineEntryNVX const& rhs ) const + { + return !operator==( rhs ); + } + + ObjectEntryTypeNVX type; + ObjectEntryUsageFlagsNVX flags; + Pipeline pipeline; + }; + static_assert( sizeof( ObjectTablePipelineEntryNVX ) == sizeof( VkObjectTablePipelineEntryNVX ), "struct and wrapper have different size!" ); + + struct ObjectTableDescriptorSetEntryNVX + { + ObjectTableDescriptorSetEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), PipelineLayout pipelineLayout_ = PipelineLayout(), DescriptorSet descriptorSet_ = DescriptorSet() ) + : type( type_ ) + , flags( flags_ ) + , pipelineLayout( pipelineLayout_ ) + , descriptorSet( descriptorSet_ ) + { + } + + ObjectTableDescriptorSetEntryNVX( VkObjectTableDescriptorSetEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableDescriptorSetEntryNVX) ); + } + + ObjectTableDescriptorSetEntryNVX& operator=( VkObjectTableDescriptorSetEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableDescriptorSetEntryNVX) ); + return *this; + } + + ObjectTableDescriptorSetEntryNVX& setType( ObjectEntryTypeNVX type_ ) + { + type = type_; + return *this; + } + + ObjectTableDescriptorSetEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) + { + flags = flags_; + return *this; + } + + ObjectTableDescriptorSetEntryNVX& setPipelineLayout( PipelineLayout pipelineLayout_ ) + { + pipelineLayout = pipelineLayout_; + return *this; + } + + ObjectTableDescriptorSetEntryNVX& setDescriptorSet( DescriptorSet descriptorSet_ ) + { + descriptorSet = descriptorSet_; + return *this; + } + + operator const VkObjectTableDescriptorSetEntryNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ObjectTableDescriptorSetEntryNVX const& rhs ) const + { + return ( type == rhs.type ) + && ( flags == rhs.flags ) + && ( pipelineLayout == rhs.pipelineLayout ) + && ( descriptorSet == rhs.descriptorSet ); + } + + bool operator!=( ObjectTableDescriptorSetEntryNVX const& rhs ) const + { + return !operator==( rhs ); + } + + ObjectEntryTypeNVX type; + ObjectEntryUsageFlagsNVX flags; + PipelineLayout pipelineLayout; + DescriptorSet descriptorSet; + }; + static_assert( sizeof( ObjectTableDescriptorSetEntryNVX ) == sizeof( VkObjectTableDescriptorSetEntryNVX ), "struct and wrapper have different size!" ); + + struct ObjectTableVertexBufferEntryNVX + { + ObjectTableVertexBufferEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), Buffer buffer_ = Buffer() ) + : type( type_ ) + , flags( flags_ ) + , buffer( buffer_ ) + { + } + + ObjectTableVertexBufferEntryNVX( VkObjectTableVertexBufferEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableVertexBufferEntryNVX) ); + } + + ObjectTableVertexBufferEntryNVX& operator=( VkObjectTableVertexBufferEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableVertexBufferEntryNVX) ); + return *this; + } + + ObjectTableVertexBufferEntryNVX& setType( ObjectEntryTypeNVX type_ ) + { + type = type_; + return *this; + } + + ObjectTableVertexBufferEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) + { + flags = flags_; + return *this; + } + + ObjectTableVertexBufferEntryNVX& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + operator const VkObjectTableVertexBufferEntryNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ObjectTableVertexBufferEntryNVX const& rhs ) const + { + return ( type == rhs.type ) + && ( flags == rhs.flags ) + && ( buffer == rhs.buffer ); + } + + bool operator!=( ObjectTableVertexBufferEntryNVX const& rhs ) const + { + return !operator==( rhs ); + } + + ObjectEntryTypeNVX type; + ObjectEntryUsageFlagsNVX flags; + Buffer buffer; + }; + static_assert( sizeof( ObjectTableVertexBufferEntryNVX ) == sizeof( VkObjectTableVertexBufferEntryNVX ), "struct and wrapper have different size!" ); + + struct ObjectTableIndexBufferEntryNVX + { + ObjectTableIndexBufferEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), Buffer buffer_ = Buffer(), IndexType indexType_ = IndexType::eUint16 ) + : type( type_ ) + , flags( flags_ ) + , buffer( buffer_ ) + , indexType( indexType_ ) + { + } + + ObjectTableIndexBufferEntryNVX( VkObjectTableIndexBufferEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableIndexBufferEntryNVX) ); + } + + ObjectTableIndexBufferEntryNVX& operator=( VkObjectTableIndexBufferEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTableIndexBufferEntryNVX) ); + return *this; + } + + ObjectTableIndexBufferEntryNVX& setType( ObjectEntryTypeNVX type_ ) + { + type = type_; + return *this; + } + + ObjectTableIndexBufferEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) + { + flags = flags_; + return *this; + } + + ObjectTableIndexBufferEntryNVX& setBuffer( Buffer buffer_ ) + { + buffer = buffer_; + return *this; + } + + ObjectTableIndexBufferEntryNVX& setIndexType( IndexType indexType_ ) + { + indexType = indexType_; + return *this; + } + + operator const VkObjectTableIndexBufferEntryNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ObjectTableIndexBufferEntryNVX const& rhs ) const + { + return ( type == rhs.type ) + && ( flags == rhs.flags ) + && ( buffer == rhs.buffer ) + && ( indexType == rhs.indexType ); + } + + bool operator!=( ObjectTableIndexBufferEntryNVX const& rhs ) const + { + return !operator==( rhs ); + } + + ObjectEntryTypeNVX type; + ObjectEntryUsageFlagsNVX flags; + Buffer buffer; + IndexType indexType; + }; + static_assert( sizeof( ObjectTableIndexBufferEntryNVX ) == sizeof( VkObjectTableIndexBufferEntryNVX ), "struct and wrapper have different size!" ); + + struct ObjectTablePushConstantEntryNVX + { + ObjectTablePushConstantEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), PipelineLayout pipelineLayout_ = PipelineLayout(), ShaderStageFlags stageFlags_ = ShaderStageFlags() ) + : type( type_ ) + , flags( flags_ ) + , pipelineLayout( pipelineLayout_ ) + , stageFlags( stageFlags_ ) + { + } + + ObjectTablePushConstantEntryNVX( VkObjectTablePushConstantEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTablePushConstantEntryNVX) ); + } + + ObjectTablePushConstantEntryNVX& operator=( VkObjectTablePushConstantEntryNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(ObjectTablePushConstantEntryNVX) ); + return *this; + } + + ObjectTablePushConstantEntryNVX& setType( ObjectEntryTypeNVX type_ ) + { + type = type_; + return *this; + } + + ObjectTablePushConstantEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) + { + flags = flags_; + return *this; + } + + ObjectTablePushConstantEntryNVX& setPipelineLayout( PipelineLayout pipelineLayout_ ) + { + pipelineLayout = pipelineLayout_; + return *this; + } + + ObjectTablePushConstantEntryNVX& setStageFlags( ShaderStageFlags stageFlags_ ) + { + stageFlags = stageFlags_; + return *this; + } + + operator const VkObjectTablePushConstantEntryNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( ObjectTablePushConstantEntryNVX const& rhs ) const + { + return ( type == rhs.type ) + && ( flags == rhs.flags ) + && ( pipelineLayout == rhs.pipelineLayout ) + && ( stageFlags == rhs.stageFlags ); + } + + bool operator!=( ObjectTablePushConstantEntryNVX const& rhs ) const + { + return !operator==( rhs ); + } + + ObjectEntryTypeNVX type; + ObjectEntryUsageFlagsNVX flags; + PipelineLayout pipelineLayout; + ShaderStageFlags stageFlags; + }; + static_assert( sizeof( ObjectTablePushConstantEntryNVX ) == sizeof( VkObjectTablePushConstantEntryNVX ), "struct and wrapper have different size!" ); + + enum class SurfaceCounterFlagBitsEXT + { + eVblankExt = VK_SURFACE_COUNTER_VBLANK_EXT + }; + + using SurfaceCounterFlagsEXT = Flags; + + VULKAN_HPP_INLINE SurfaceCounterFlagsEXT operator|( SurfaceCounterFlagBitsEXT bit0, SurfaceCounterFlagBitsEXT bit1 ) + { + return SurfaceCounterFlagsEXT( bit0 ) | bit1; + } + + VULKAN_HPP_INLINE SurfaceCounterFlagsEXT operator~( SurfaceCounterFlagBitsEXT bits ) + { + return ~( SurfaceCounterFlagsEXT( bits ) ); + } + + template <> struct FlagTraits + { + enum + { + allFlags = VkFlags(SurfaceCounterFlagBitsEXT::eVblankExt) + }; + }; + + struct SurfaceCapabilities2EXT + { + operator const VkSurfaceCapabilities2EXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SurfaceCapabilities2EXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( minImageCount == rhs.minImageCount ) + && ( maxImageCount == rhs.maxImageCount ) + && ( currentExtent == rhs.currentExtent ) + && ( minImageExtent == rhs.minImageExtent ) + && ( maxImageExtent == rhs.maxImageExtent ) + && ( maxImageArrayLayers == rhs.maxImageArrayLayers ) + && ( supportedTransforms == rhs.supportedTransforms ) + && ( currentTransform == rhs.currentTransform ) + && ( supportedCompositeAlpha == rhs.supportedCompositeAlpha ) + && ( supportedUsageFlags == rhs.supportedUsageFlags ) + && ( supportedSurfaceCounters == rhs.supportedSurfaceCounters ); + } + + bool operator!=( SurfaceCapabilities2EXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + void* pNext; + uint32_t minImageCount; + uint32_t maxImageCount; + Extent2D currentExtent; + Extent2D minImageExtent; + Extent2D maxImageExtent; + uint32_t maxImageArrayLayers; + SurfaceTransformFlagsKHR supportedTransforms; + SurfaceTransformFlagBitsKHR currentTransform; + CompositeAlphaFlagsKHR supportedCompositeAlpha; + ImageUsageFlags supportedUsageFlags; + SurfaceCounterFlagsEXT supportedSurfaceCounters; + }; + static_assert( sizeof( SurfaceCapabilities2EXT ) == sizeof( VkSurfaceCapabilities2EXT ), "struct and wrapper have different size!" ); + + struct SwapchainCounterCreateInfoEXT + { + SwapchainCounterCreateInfoEXT( SurfaceCounterFlagsEXT surfaceCounters_ = SurfaceCounterFlagsEXT() ) + : sType( StructureType::eSwapchainCounterCreateInfoEXT ) + , pNext( nullptr ) + , surfaceCounters( surfaceCounters_ ) + { + } + + SwapchainCounterCreateInfoEXT( VkSwapchainCounterCreateInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(SwapchainCounterCreateInfoEXT) ); + } + + SwapchainCounterCreateInfoEXT& operator=( VkSwapchainCounterCreateInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(SwapchainCounterCreateInfoEXT) ); + return *this; + } + + SwapchainCounterCreateInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + SwapchainCounterCreateInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + SwapchainCounterCreateInfoEXT& setSurfaceCounters( SurfaceCounterFlagsEXT surfaceCounters_ ) + { + surfaceCounters = surfaceCounters_; + return *this; + } + + operator const VkSwapchainCounterCreateInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SwapchainCounterCreateInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( surfaceCounters == rhs.surfaceCounters ); + } + + bool operator!=( SwapchainCounterCreateInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + SurfaceCounterFlagsEXT surfaceCounters; + }; + static_assert( sizeof( SwapchainCounterCreateInfoEXT ) == sizeof( VkSwapchainCounterCreateInfoEXT ), "struct and wrapper have different size!" ); + + enum class DisplayPowerStateEXT + { + eOff = VK_DISPLAY_POWER_STATE_OFF_EXT, + eSuspend = VK_DISPLAY_POWER_STATE_SUSPEND_EXT, + eOn = VK_DISPLAY_POWER_STATE_ON_EXT + }; + + struct DisplayPowerInfoEXT + { + DisplayPowerInfoEXT( DisplayPowerStateEXT powerState_ = DisplayPowerStateEXT::eOff ) + : sType( StructureType::eDisplayPowerInfoEXT ) + , pNext( nullptr ) + , powerState( powerState_ ) + { + } + + DisplayPowerInfoEXT( VkDisplayPowerInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayPowerInfoEXT) ); + } + + DisplayPowerInfoEXT& operator=( VkDisplayPowerInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayPowerInfoEXT) ); + return *this; + } + + DisplayPowerInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DisplayPowerInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DisplayPowerInfoEXT& setPowerState( DisplayPowerStateEXT powerState_ ) + { + powerState = powerState_; + return *this; + } + + operator const VkDisplayPowerInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayPowerInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( powerState == rhs.powerState ); + } + + bool operator!=( DisplayPowerInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DisplayPowerStateEXT powerState; + }; + static_assert( sizeof( DisplayPowerInfoEXT ) == sizeof( VkDisplayPowerInfoEXT ), "struct and wrapper have different size!" ); + + enum class DeviceEventTypeEXT + { + eDisplayHotplug = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + }; + + struct DeviceEventInfoEXT + { + DeviceEventInfoEXT( DeviceEventTypeEXT deviceEvent_ = DeviceEventTypeEXT::eDisplayHotplug ) + : sType( StructureType::eDeviceEventInfoEXT ) + , pNext( nullptr ) + , deviceEvent( deviceEvent_ ) + { + } + + DeviceEventInfoEXT( VkDeviceEventInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceEventInfoEXT) ); + } + + DeviceEventInfoEXT& operator=( VkDeviceEventInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DeviceEventInfoEXT) ); + return *this; + } + + DeviceEventInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DeviceEventInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DeviceEventInfoEXT& setDeviceEvent( DeviceEventTypeEXT deviceEvent_ ) + { + deviceEvent = deviceEvent_; + return *this; + } + + operator const VkDeviceEventInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DeviceEventInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( deviceEvent == rhs.deviceEvent ); + } + + bool operator!=( DeviceEventInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DeviceEventTypeEXT deviceEvent; + }; + static_assert( sizeof( DeviceEventInfoEXT ) == sizeof( VkDeviceEventInfoEXT ), "struct and wrapper have different size!" ); + + enum class DisplayEventTypeEXT + { + eFirstPixelOut = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + }; + + struct DisplayEventInfoEXT + { + DisplayEventInfoEXT( DisplayEventTypeEXT displayEvent_ = DisplayEventTypeEXT::eFirstPixelOut ) + : sType( StructureType::eDisplayEventInfoEXT ) + , pNext( nullptr ) + , displayEvent( displayEvent_ ) + { + } + + DisplayEventInfoEXT( VkDisplayEventInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayEventInfoEXT) ); + } + + DisplayEventInfoEXT& operator=( VkDisplayEventInfoEXT const & rhs ) + { + memcpy( this, &rhs, sizeof(DisplayEventInfoEXT) ); + return *this; + } + + DisplayEventInfoEXT& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + DisplayEventInfoEXT& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + DisplayEventInfoEXT& setDisplayEvent( DisplayEventTypeEXT displayEvent_ ) + { + displayEvent = displayEvent_; + return *this; + } + + operator const VkDisplayEventInfoEXT&() const + { + return *reinterpret_cast(this); + } + + bool operator==( DisplayEventInfoEXT const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( displayEvent == rhs.displayEvent ); + } + + bool operator!=( DisplayEventInfoEXT const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + DisplayEventTypeEXT displayEvent; + }; + static_assert( sizeof( DisplayEventInfoEXT ) == sizeof( VkDisplayEventInfoEXT ), "struct and wrapper have different size!" ); + + VULKAN_HPP_INLINE Result enumerateInstanceLayerProperties( uint32_t* pPropertyCount, LayerProperties* pProperties ) + { + return static_cast( vkEnumerateInstanceLayerProperties( pPropertyCount, reinterpret_cast( pProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type enumerateInstanceLayerProperties() + { + std::vector properties; + uint32_t propertyCount; + Result result; + do + { + result = static_cast( vkEnumerateInstanceLayerProperties( &propertyCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && propertyCount ) + { + properties.resize( propertyCount ); + result = static_cast( vkEnumerateInstanceLayerProperties( &propertyCount, reinterpret_cast( properties.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( propertyCount <= properties.size() ); + properties.resize( propertyCount ); + return createResultValue( result, properties, "vk::enumerateInstanceLayerProperties" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + VULKAN_HPP_INLINE Result enumerateInstanceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, ExtensionProperties* pProperties ) + { + return static_cast( vkEnumerateInstanceExtensionProperties( pLayerName, pPropertyCount, reinterpret_cast( pProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type enumerateInstanceExtensionProperties( Optional layerName = nullptr ) + { + std::vector properties; + uint32_t propertyCount; + Result result; + do + { + result = static_cast( vkEnumerateInstanceExtensionProperties( layerName ? layerName->c_str() : nullptr, &propertyCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && propertyCount ) + { + properties.resize( propertyCount ); + result = static_cast( vkEnumerateInstanceExtensionProperties( layerName ? layerName->c_str() : nullptr, &propertyCount, reinterpret_cast( properties.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( propertyCount <= properties.size() ); + properties.resize( propertyCount ); + return createResultValue( result, properties, "vk::enumerateInstanceExtensionProperties" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + // forward declarations + struct CmdProcessCommandsInfoNVX; + + class CommandBuffer + { + public: + CommandBuffer() + : m_commandBuffer(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + CommandBuffer(VkCommandBuffer commandBuffer) + : m_commandBuffer(commandBuffer) + {} + + CommandBuffer& operator=(VkCommandBuffer commandBuffer) + { + m_commandBuffer = commandBuffer; + return *this; + } +#endif + + bool operator==(CommandBuffer const &rhs) const + { + return m_commandBuffer == rhs.m_commandBuffer; + } + + bool operator!=(CommandBuffer const &rhs) const + { + return m_commandBuffer != rhs.m_commandBuffer; + } + + bool operator<(CommandBuffer const &rhs) const + { + return m_commandBuffer < rhs.m_commandBuffer; + } + + Result begin( const CommandBufferBeginInfo* pBeginInfo ) const + { + return static_cast( vkBeginCommandBuffer( m_commandBuffer, reinterpret_cast( pBeginInfo ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type begin( const CommandBufferBeginInfo & beginInfo ) const + { + Result result = static_cast( vkBeginCommandBuffer( m_commandBuffer, reinterpret_cast( &beginInfo ) ) ); + return createResultValue( result, "vk::CommandBuffer::begin" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result end( ) const + { + return static_cast( vkEndCommandBuffer( m_commandBuffer ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type end() const + { + Result result = static_cast( vkEndCommandBuffer( m_commandBuffer ) ); + return createResultValue( result, "vk::CommandBuffer::end" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result reset( CommandBufferResetFlags flags ) const + { + return static_cast( vkResetCommandBuffer( m_commandBuffer, static_cast( flags ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type reset( CommandBufferResetFlags flags ) const + { + Result result = static_cast( vkResetCommandBuffer( m_commandBuffer, static_cast( flags ) ) ); + return createResultValue( result, "vk::CommandBuffer::reset" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void bindPipeline( PipelineBindPoint pipelineBindPoint, Pipeline pipeline ) const + { + vkCmdBindPipeline( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( pipeline ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void bindPipeline( PipelineBindPoint pipelineBindPoint, Pipeline pipeline ) const + { + vkCmdBindPipeline( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( pipeline ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void setViewport( uint32_t firstViewport, uint32_t viewportCount, const Viewport* pViewports ) const + { + vkCmdSetViewport( m_commandBuffer, firstViewport, viewportCount, reinterpret_cast( pViewports ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setViewport( uint32_t firstViewport, ArrayProxy viewports ) const + { + vkCmdSetViewport( m_commandBuffer, firstViewport, viewports.size() , reinterpret_cast( viewports.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void setScissor( uint32_t firstScissor, uint32_t scissorCount, const Rect2D* pScissors ) const + { + vkCmdSetScissor( m_commandBuffer, firstScissor, scissorCount, reinterpret_cast( pScissors ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setScissor( uint32_t firstScissor, ArrayProxy scissors ) const + { + vkCmdSetScissor( m_commandBuffer, firstScissor, scissors.size() , reinterpret_cast( scissors.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setLineWidth( float lineWidth ) const + { + vkCmdSetLineWidth( m_commandBuffer, lineWidth ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setLineWidth( float lineWidth ) const + { + vkCmdSetLineWidth( m_commandBuffer, lineWidth ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setDepthBias( float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor ) const + { + vkCmdSetDepthBias( m_commandBuffer, depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setDepthBias( float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor ) const + { + vkCmdSetDepthBias( m_commandBuffer, depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setBlendConstants( const float blendConstants[4] ) const + { + vkCmdSetBlendConstants( m_commandBuffer, blendConstants ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setBlendConstants( const float blendConstants[4] ) const + { + vkCmdSetBlendConstants( m_commandBuffer, blendConstants ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setDepthBounds( float minDepthBounds, float maxDepthBounds ) const + { + vkCmdSetDepthBounds( m_commandBuffer, minDepthBounds, maxDepthBounds ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setDepthBounds( float minDepthBounds, float maxDepthBounds ) const + { + vkCmdSetDepthBounds( m_commandBuffer, minDepthBounds, maxDepthBounds ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setStencilCompareMask( StencilFaceFlags faceMask, uint32_t compareMask ) const + { + vkCmdSetStencilCompareMask( m_commandBuffer, static_cast( faceMask ), compareMask ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setStencilCompareMask( StencilFaceFlags faceMask, uint32_t compareMask ) const + { + vkCmdSetStencilCompareMask( m_commandBuffer, static_cast( faceMask ), compareMask ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setStencilWriteMask( StencilFaceFlags faceMask, uint32_t writeMask ) const + { + vkCmdSetStencilWriteMask( m_commandBuffer, static_cast( faceMask ), writeMask ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setStencilWriteMask( StencilFaceFlags faceMask, uint32_t writeMask ) const + { + vkCmdSetStencilWriteMask( m_commandBuffer, static_cast( faceMask ), writeMask ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setStencilReference( StencilFaceFlags faceMask, uint32_t reference ) const + { + vkCmdSetStencilReference( m_commandBuffer, static_cast( faceMask ), reference ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setStencilReference( StencilFaceFlags faceMask, uint32_t reference ) const + { + vkCmdSetStencilReference( m_commandBuffer, static_cast( faceMask ), reference ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void bindDescriptorSets( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const DescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets ) const + { + vkCmdBindDescriptorSets( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( layout ), firstSet, descriptorSetCount, reinterpret_cast( pDescriptorSets ), dynamicOffsetCount, pDynamicOffsets ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void bindDescriptorSets( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t firstSet, ArrayProxy descriptorSets, ArrayProxy dynamicOffsets ) const + { + vkCmdBindDescriptorSets( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( layout ), firstSet, descriptorSets.size() , reinterpret_cast( descriptorSets.data() ), dynamicOffsets.size() , dynamicOffsets.data() ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void bindIndexBuffer( Buffer buffer, DeviceSize offset, IndexType indexType ) const + { + vkCmdBindIndexBuffer( m_commandBuffer, static_cast( buffer ), offset, static_cast( indexType ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void bindIndexBuffer( Buffer buffer, DeviceSize offset, IndexType indexType ) const + { + vkCmdBindIndexBuffer( m_commandBuffer, static_cast( buffer ), offset, static_cast( indexType ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void bindVertexBuffers( uint32_t firstBinding, uint32_t bindingCount, const Buffer* pBuffers, const DeviceSize* pOffsets ) const + { + vkCmdBindVertexBuffers( m_commandBuffer, firstBinding, bindingCount, reinterpret_cast( pBuffers ), pOffsets ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void bindVertexBuffers( uint32_t firstBinding, ArrayProxy buffers, ArrayProxy offsets ) const + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( buffers.size() == offsets.size() ); +#else + if ( buffers.size() != offsets.size() ) + { + throw std::logic_error( "vk::CommandBuffer::bindVertexBuffers: buffers.size() != offsets.size()" ); + } +#endif // VULKAN_HPP_NO_EXCEPTIONS + vkCmdBindVertexBuffers( m_commandBuffer, firstBinding, buffers.size() , reinterpret_cast( buffers.data() ), offsets.data() ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void draw( uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance ) const + { + vkCmdDraw( m_commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void draw( uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance ) const + { + vkCmdDraw( m_commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndexed( uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance ) const + { + vkCmdDrawIndexed( m_commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndexed( uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance ) const + { + vkCmdDrawIndexed( m_commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const + { + vkCmdDrawIndirect( m_commandBuffer, static_cast( buffer ), offset, drawCount, stride ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const + { + vkCmdDrawIndirect( m_commandBuffer, static_cast( buffer ), offset, drawCount, stride ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndexedIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const + { + vkCmdDrawIndexedIndirect( m_commandBuffer, static_cast( buffer ), offset, drawCount, stride ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndexedIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const + { + vkCmdDrawIndexedIndirect( m_commandBuffer, static_cast( buffer ), offset, drawCount, stride ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void dispatch( uint32_t x, uint32_t y, uint32_t z ) const + { + vkCmdDispatch( m_commandBuffer, x, y, z ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void dispatch( uint32_t x, uint32_t y, uint32_t z ) const + { + vkCmdDispatch( m_commandBuffer, x, y, z ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void dispatchIndirect( Buffer buffer, DeviceSize offset ) const + { + vkCmdDispatchIndirect( m_commandBuffer, static_cast( buffer ), offset ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void dispatchIndirect( Buffer buffer, DeviceSize offset ) const + { + vkCmdDispatchIndirect( m_commandBuffer, static_cast( buffer ), offset ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void copyBuffer( Buffer srcBuffer, Buffer dstBuffer, uint32_t regionCount, const BufferCopy* pRegions ) const + { + vkCmdCopyBuffer( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstBuffer ), regionCount, reinterpret_cast( pRegions ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void copyBuffer( Buffer srcBuffer, Buffer dstBuffer, ArrayProxy regions ) const + { + vkCmdCopyBuffer( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstBuffer ), regions.size() , reinterpret_cast( regions.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void copyImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageCopy* pRegions ) const + { + vkCmdCopyImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void copyImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const + { + vkCmdCopyImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void blitImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageBlit* pRegions, Filter filter ) const + { + vkCmdBlitImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ), static_cast( filter ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void blitImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions, Filter filter ) const + { + vkCmdBlitImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ), static_cast( filter ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void copyBufferToImage( Buffer srcBuffer, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const BufferImageCopy* pRegions ) const + { + vkCmdCopyBufferToImage( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void copyBufferToImage( Buffer srcBuffer, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const + { + vkCmdCopyBufferToImage( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void copyImageToBuffer( Image srcImage, ImageLayout srcImageLayout, Buffer dstBuffer, uint32_t regionCount, const BufferImageCopy* pRegions ) const + { + vkCmdCopyImageToBuffer( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstBuffer ), regionCount, reinterpret_cast( pRegions ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void copyImageToBuffer( Image srcImage, ImageLayout srcImageLayout, Buffer dstBuffer, ArrayProxy regions ) const + { + vkCmdCopyImageToBuffer( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstBuffer ), regions.size() , reinterpret_cast( regions.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void updateBuffer( Buffer dstBuffer, DeviceSize dstOffset, DeviceSize dataSize, const void* pData ) const + { + vkCmdUpdateBuffer( m_commandBuffer, static_cast( dstBuffer ), dstOffset, dataSize, pData ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template + void updateBuffer( Buffer dstBuffer, DeviceSize dstOffset, ArrayProxy data ) const + { + vkCmdUpdateBuffer( m_commandBuffer, static_cast( dstBuffer ), dstOffset, data.size() * sizeof( T ) , reinterpret_cast( data.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void fillBuffer( Buffer dstBuffer, DeviceSize dstOffset, DeviceSize size, uint32_t data ) const + { + vkCmdFillBuffer( m_commandBuffer, static_cast( dstBuffer ), dstOffset, size, data ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void fillBuffer( Buffer dstBuffer, DeviceSize dstOffset, DeviceSize size, uint32_t data ) const + { + vkCmdFillBuffer( m_commandBuffer, static_cast( dstBuffer ), dstOffset, size, data ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void clearColorImage( Image image, ImageLayout imageLayout, const ClearColorValue* pColor, uint32_t rangeCount, const ImageSubresourceRange* pRanges ) const + { + vkCmdClearColorImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( pColor ), rangeCount, reinterpret_cast( pRanges ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void clearColorImage( Image image, ImageLayout imageLayout, const ClearColorValue & color, ArrayProxy ranges ) const + { + vkCmdClearColorImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( &color ), ranges.size() , reinterpret_cast( ranges.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void clearDepthStencilImage( Image image, ImageLayout imageLayout, const ClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const ImageSubresourceRange* pRanges ) const + { + vkCmdClearDepthStencilImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( pDepthStencil ), rangeCount, reinterpret_cast( pRanges ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void clearDepthStencilImage( Image image, ImageLayout imageLayout, const ClearDepthStencilValue & depthStencil, ArrayProxy ranges ) const + { + vkCmdClearDepthStencilImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( &depthStencil ), ranges.size() , reinterpret_cast( ranges.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void clearAttachments( uint32_t attachmentCount, const ClearAttachment* pAttachments, uint32_t rectCount, const ClearRect* pRects ) const + { + vkCmdClearAttachments( m_commandBuffer, attachmentCount, reinterpret_cast( pAttachments ), rectCount, reinterpret_cast( pRects ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void clearAttachments( ArrayProxy attachments, ArrayProxy rects ) const + { + vkCmdClearAttachments( m_commandBuffer, attachments.size() , reinterpret_cast( attachments.data() ), rects.size() , reinterpret_cast( rects.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void resolveImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageResolve* pRegions ) const + { + vkCmdResolveImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void resolveImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const + { + vkCmdResolveImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setEvent( Event event, PipelineStageFlags stageMask ) const + { + vkCmdSetEvent( m_commandBuffer, static_cast( event ), static_cast( stageMask ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void setEvent( Event event, PipelineStageFlags stageMask ) const + { + vkCmdSetEvent( m_commandBuffer, static_cast( event ), static_cast( stageMask ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void resetEvent( Event event, PipelineStageFlags stageMask ) const + { + vkCmdResetEvent( m_commandBuffer, static_cast( event ), static_cast( stageMask ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void resetEvent( Event event, PipelineStageFlags stageMask ) const + { + vkCmdResetEvent( m_commandBuffer, static_cast( event ), static_cast( stageMask ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void waitEvents( uint32_t eventCount, const Event* pEvents, PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const MemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const BufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const ImageMemoryBarrier* pImageMemoryBarriers ) const + { + vkCmdWaitEvents( m_commandBuffer, eventCount, reinterpret_cast( pEvents ), static_cast( srcStageMask ), static_cast( dstStageMask ), memoryBarrierCount, reinterpret_cast( pMemoryBarriers ), bufferMemoryBarrierCount, reinterpret_cast( pBufferMemoryBarriers ), imageMemoryBarrierCount, reinterpret_cast( pImageMemoryBarriers ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void waitEvents( ArrayProxy events, PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, ArrayProxy memoryBarriers, ArrayProxy bufferMemoryBarriers, ArrayProxy imageMemoryBarriers ) const + { + vkCmdWaitEvents( m_commandBuffer, events.size() , reinterpret_cast( events.data() ), static_cast( srcStageMask ), static_cast( dstStageMask ), memoryBarriers.size() , reinterpret_cast( memoryBarriers.data() ), bufferMemoryBarriers.size() , reinterpret_cast( bufferMemoryBarriers.data() ), imageMemoryBarriers.size() , reinterpret_cast( imageMemoryBarriers.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void pipelineBarrier( PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, DependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const MemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const BufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const ImageMemoryBarrier* pImageMemoryBarriers ) const + { + vkCmdPipelineBarrier( m_commandBuffer, static_cast( srcStageMask ), static_cast( dstStageMask ), static_cast( dependencyFlags ), memoryBarrierCount, reinterpret_cast( pMemoryBarriers ), bufferMemoryBarrierCount, reinterpret_cast( pBufferMemoryBarriers ), imageMemoryBarrierCount, reinterpret_cast( pImageMemoryBarriers ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void pipelineBarrier( PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, DependencyFlags dependencyFlags, ArrayProxy memoryBarriers, ArrayProxy bufferMemoryBarriers, ArrayProxy imageMemoryBarriers ) const + { + vkCmdPipelineBarrier( m_commandBuffer, static_cast( srcStageMask ), static_cast( dstStageMask ), static_cast( dependencyFlags ), memoryBarriers.size() , reinterpret_cast( memoryBarriers.data() ), bufferMemoryBarriers.size() , reinterpret_cast( bufferMemoryBarriers.data() ), imageMemoryBarriers.size() , reinterpret_cast( imageMemoryBarriers.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void beginQuery( QueryPool queryPool, uint32_t query, QueryControlFlags flags ) const + { + vkCmdBeginQuery( m_commandBuffer, static_cast( queryPool ), query, static_cast( flags ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void beginQuery( QueryPool queryPool, uint32_t query, QueryControlFlags flags ) const + { + vkCmdBeginQuery( m_commandBuffer, static_cast( queryPool ), query, static_cast( flags ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void endQuery( QueryPool queryPool, uint32_t query ) const + { + vkCmdEndQuery( m_commandBuffer, static_cast( queryPool ), query ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void endQuery( QueryPool queryPool, uint32_t query ) const + { + vkCmdEndQuery( m_commandBuffer, static_cast( queryPool ), query ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void resetQueryPool( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount ) const + { + vkCmdResetQueryPool( m_commandBuffer, static_cast( queryPool ), firstQuery, queryCount ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void resetQueryPool( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount ) const + { + vkCmdResetQueryPool( m_commandBuffer, static_cast( queryPool ), firstQuery, queryCount ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void writeTimestamp( PipelineStageFlagBits pipelineStage, QueryPool queryPool, uint32_t query ) const + { + vkCmdWriteTimestamp( m_commandBuffer, static_cast( pipelineStage ), static_cast( queryPool ), query ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void writeTimestamp( PipelineStageFlagBits pipelineStage, QueryPool queryPool, uint32_t query ) const + { + vkCmdWriteTimestamp( m_commandBuffer, static_cast( pipelineStage ), static_cast( queryPool ), query ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void copyQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, Buffer dstBuffer, DeviceSize dstOffset, DeviceSize stride, QueryResultFlags flags ) const + { + vkCmdCopyQueryPoolResults( m_commandBuffer, static_cast( queryPool ), firstQuery, queryCount, static_cast( dstBuffer ), dstOffset, stride, static_cast( flags ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void copyQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, Buffer dstBuffer, DeviceSize dstOffset, DeviceSize stride, QueryResultFlags flags ) const + { + vkCmdCopyQueryPoolResults( m_commandBuffer, static_cast( queryPool ), firstQuery, queryCount, static_cast( dstBuffer ), dstOffset, stride, static_cast( flags ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void pushConstants( PipelineLayout layout, ShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues ) const + { + vkCmdPushConstants( m_commandBuffer, static_cast( layout ), static_cast( stageFlags ), offset, size, pValues ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template + void pushConstants( PipelineLayout layout, ShaderStageFlags stageFlags, uint32_t offset, ArrayProxy values ) const + { + vkCmdPushConstants( m_commandBuffer, static_cast( layout ), static_cast( stageFlags ), offset, values.size() * sizeof( T ) , reinterpret_cast( values.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void beginRenderPass( const RenderPassBeginInfo* pRenderPassBegin, SubpassContents contents ) const + { + vkCmdBeginRenderPass( m_commandBuffer, reinterpret_cast( pRenderPassBegin ), static_cast( contents ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void beginRenderPass( const RenderPassBeginInfo & renderPassBegin, SubpassContents contents ) const + { + vkCmdBeginRenderPass( m_commandBuffer, reinterpret_cast( &renderPassBegin ), static_cast( contents ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void nextSubpass( SubpassContents contents ) const + { + vkCmdNextSubpass( m_commandBuffer, static_cast( contents ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void nextSubpass( SubpassContents contents ) const + { + vkCmdNextSubpass( m_commandBuffer, static_cast( contents ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void endRenderPass( ) const + { + vkCmdEndRenderPass( m_commandBuffer ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void endRenderPass() const + { + vkCmdEndRenderPass( m_commandBuffer ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void executeCommands( uint32_t commandBufferCount, const CommandBuffer* pCommandBuffers ) const + { + vkCmdExecuteCommands( m_commandBuffer, commandBufferCount, reinterpret_cast( pCommandBuffers ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void executeCommands( ArrayProxy commandBuffers ) const + { + vkCmdExecuteCommands( m_commandBuffer, commandBuffers.size() , reinterpret_cast( commandBuffers.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void debugMarkerBeginEXT( DebugMarkerMarkerInfoEXT* pMarkerInfo ) const + { + vkCmdDebugMarkerBeginEXT( m_commandBuffer, reinterpret_cast( pMarkerInfo ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + DebugMarkerMarkerInfoEXT debugMarkerBeginEXT() const + { + DebugMarkerMarkerInfoEXT markerInfo; + vkCmdDebugMarkerBeginEXT( m_commandBuffer, reinterpret_cast( &markerInfo ) ); + return markerInfo; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void debugMarkerEndEXT( ) const + { + vkCmdDebugMarkerEndEXT( m_commandBuffer ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void debugMarkerEndEXT() const + { + vkCmdDebugMarkerEndEXT( m_commandBuffer ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void debugMarkerInsertEXT( DebugMarkerMarkerInfoEXT* pMarkerInfo ) const + { + vkCmdDebugMarkerInsertEXT( m_commandBuffer, reinterpret_cast( pMarkerInfo ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + DebugMarkerMarkerInfoEXT debugMarkerInsertEXT() const + { + DebugMarkerMarkerInfoEXT markerInfo; + vkCmdDebugMarkerInsertEXT( m_commandBuffer, reinterpret_cast( &markerInfo ) ); + return markerInfo; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const + { + vkCmdDrawIndirectCountAMD( m_commandBuffer, static_cast( buffer ), offset, static_cast( countBuffer ), countBufferOffset, maxDrawCount, stride ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const + { + vkCmdDrawIndirectCountAMD( m_commandBuffer, static_cast( buffer ), offset, static_cast( countBuffer ), countBufferOffset, maxDrawCount, stride ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndexedIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const + { + vkCmdDrawIndexedIndirectCountAMD( m_commandBuffer, static_cast( buffer ), offset, static_cast( countBuffer ), countBufferOffset, maxDrawCount, stride ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void drawIndexedIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const + { + vkCmdDrawIndexedIndirectCountAMD( m_commandBuffer, static_cast( buffer ), offset, static_cast( countBuffer ), countBufferOffset, maxDrawCount, stride ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void processCommandsNVX( const CmdProcessCommandsInfoNVX* pProcessCommandsInfo ) const + { + vkCmdProcessCommandsNVX( m_commandBuffer, reinterpret_cast( pProcessCommandsInfo ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void processCommandsNVX( const CmdProcessCommandsInfoNVX & processCommandsInfo ) const + { + vkCmdProcessCommandsNVX( m_commandBuffer, reinterpret_cast( &processCommandsInfo ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void reserveSpaceForCommandsNVX( const CmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo ) const + { + vkCmdReserveSpaceForCommandsNVX( m_commandBuffer, reinterpret_cast( pReserveSpaceInfo ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void reserveSpaceForCommandsNVX( const CmdReserveSpaceForCommandsInfoNVX & reserveSpaceInfo ) const + { + vkCmdReserveSpaceForCommandsNVX( m_commandBuffer, reinterpret_cast( &reserveSpaceInfo ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkCommandBuffer() const + { + return m_commandBuffer; + } + + explicit operator bool() const + { + return m_commandBuffer != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_commandBuffer == VK_NULL_HANDLE; + } + + private: + VkCommandBuffer m_commandBuffer; + }; + static_assert( sizeof( CommandBuffer ) == sizeof( VkCommandBuffer ), "handle and wrapper have different size!" ); + + struct SubmitInfo + { + SubmitInfo( uint32_t waitSemaphoreCount_ = 0, const Semaphore* pWaitSemaphores_ = nullptr, const PipelineStageFlags* pWaitDstStageMask_ = nullptr, uint32_t commandBufferCount_ = 0, const CommandBuffer* pCommandBuffers_ = nullptr, uint32_t signalSemaphoreCount_ = 0, const Semaphore* pSignalSemaphores_ = nullptr ) + : sType( StructureType::eSubmitInfo ) + , pNext( nullptr ) + , waitSemaphoreCount( waitSemaphoreCount_ ) + , pWaitSemaphores( pWaitSemaphores_ ) + , pWaitDstStageMask( pWaitDstStageMask_ ) + , commandBufferCount( commandBufferCount_ ) + , pCommandBuffers( pCommandBuffers_ ) + , signalSemaphoreCount( signalSemaphoreCount_ ) + , pSignalSemaphores( pSignalSemaphores_ ) + { + } + + SubmitInfo( VkSubmitInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SubmitInfo) ); + } + + SubmitInfo& operator=( VkSubmitInfo const & rhs ) + { + memcpy( this, &rhs, sizeof(SubmitInfo) ); + return *this; + } + + SubmitInfo& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + SubmitInfo& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + SubmitInfo& setWaitSemaphoreCount( uint32_t waitSemaphoreCount_ ) + { + waitSemaphoreCount = waitSemaphoreCount_; + return *this; + } + + SubmitInfo& setPWaitSemaphores( const Semaphore* pWaitSemaphores_ ) + { + pWaitSemaphores = pWaitSemaphores_; + return *this; + } + + SubmitInfo& setPWaitDstStageMask( const PipelineStageFlags* pWaitDstStageMask_ ) + { + pWaitDstStageMask = pWaitDstStageMask_; + return *this; + } + + SubmitInfo& setCommandBufferCount( uint32_t commandBufferCount_ ) + { + commandBufferCount = commandBufferCount_; + return *this; + } + + SubmitInfo& setPCommandBuffers( const CommandBuffer* pCommandBuffers_ ) + { + pCommandBuffers = pCommandBuffers_; + return *this; + } + + SubmitInfo& setSignalSemaphoreCount( uint32_t signalSemaphoreCount_ ) + { + signalSemaphoreCount = signalSemaphoreCount_; + return *this; + } + + SubmitInfo& setPSignalSemaphores( const Semaphore* pSignalSemaphores_ ) + { + pSignalSemaphores = pSignalSemaphores_; + return *this; + } + + operator const VkSubmitInfo&() const + { + return *reinterpret_cast(this); + } + + bool operator==( SubmitInfo const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( waitSemaphoreCount == rhs.waitSemaphoreCount ) + && ( pWaitSemaphores == rhs.pWaitSemaphores ) + && ( pWaitDstStageMask == rhs.pWaitDstStageMask ) + && ( commandBufferCount == rhs.commandBufferCount ) + && ( pCommandBuffers == rhs.pCommandBuffers ) + && ( signalSemaphoreCount == rhs.signalSemaphoreCount ) + && ( pSignalSemaphores == rhs.pSignalSemaphores ); + } + + bool operator!=( SubmitInfo const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + uint32_t waitSemaphoreCount; + const Semaphore* pWaitSemaphores; + const PipelineStageFlags* pWaitDstStageMask; + uint32_t commandBufferCount; + const CommandBuffer* pCommandBuffers; + uint32_t signalSemaphoreCount; + const Semaphore* pSignalSemaphores; + }; + static_assert( sizeof( SubmitInfo ) == sizeof( VkSubmitInfo ), "struct and wrapper have different size!" ); + + class Queue + { + public: + Queue() + : m_queue(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Queue(VkQueue queue) + : m_queue(queue) + {} + + Queue& operator=(VkQueue queue) + { + m_queue = queue; + return *this; + } +#endif + + bool operator==(Queue const &rhs) const + { + return m_queue == rhs.m_queue; + } + + bool operator!=(Queue const &rhs) const + { + return m_queue != rhs.m_queue; + } + + bool operator<(Queue const &rhs) const + { + return m_queue < rhs.m_queue; + } + + Result submit( uint32_t submitCount, const SubmitInfo* pSubmits, Fence fence ) const + { + return static_cast( vkQueueSubmit( m_queue, submitCount, reinterpret_cast( pSubmits ), static_cast( fence ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type submit( ArrayProxy submits, Fence fence ) const + { + Result result = static_cast( vkQueueSubmit( m_queue, submits.size() , reinterpret_cast( submits.data() ), static_cast( fence ) ) ); + return createResultValue( result, "vk::Queue::submit" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result waitIdle( ) const + { + return static_cast( vkQueueWaitIdle( m_queue ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type waitIdle() const + { + Result result = static_cast( vkQueueWaitIdle( m_queue ) ); + return createResultValue( result, "vk::Queue::waitIdle" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result bindSparse( uint32_t bindInfoCount, const BindSparseInfo* pBindInfo, Fence fence ) const + { + return static_cast( vkQueueBindSparse( m_queue, bindInfoCount, reinterpret_cast( pBindInfo ), static_cast( fence ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type bindSparse( ArrayProxy bindInfo, Fence fence ) const + { + Result result = static_cast( vkQueueBindSparse( m_queue, bindInfo.size() , reinterpret_cast( bindInfo.data() ), static_cast( fence ) ) ); + return createResultValue( result, "vk::Queue::bindSparse" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result presentKHR( const PresentInfoKHR* pPresentInfo ) const + { + return static_cast( vkQueuePresentKHR( m_queue, reinterpret_cast( pPresentInfo ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result presentKHR( const PresentInfoKHR & presentInfo ) const + { + Result result = static_cast( vkQueuePresentKHR( m_queue, reinterpret_cast( &presentInfo ) ) ); + return createResultValue( result, "vk::Queue::presentKHR", { Result::eSuccess, Result::eSuboptimalKHR } ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkQueue() const + { + return m_queue; + } + + explicit operator bool() const + { + return m_queue != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_queue == VK_NULL_HANDLE; + } + + private: + VkQueue m_queue; + }; + static_assert( sizeof( Queue ) == sizeof( VkQueue ), "handle and wrapper have different size!" ); + + class Device + { + public: + Device() + : m_device(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Device(VkDevice device) + : m_device(device) + {} + + Device& operator=(VkDevice device) + { + m_device = device; + return *this; + } +#endif + + bool operator==(Device const &rhs) const + { + return m_device == rhs.m_device; + } + + bool operator!=(Device const &rhs) const + { + return m_device != rhs.m_device; + } + + bool operator<(Device const &rhs) const + { + return m_device < rhs.m_device; + } + + PFN_vkVoidFunction getProcAddr( const char* pName ) const + { + return vkGetDeviceProcAddr( m_device, pName ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PFN_vkVoidFunction getProcAddr( const std::string & name ) const + { + return vkGetDeviceProcAddr( m_device, name.c_str() ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroy( const AllocationCallbacks* pAllocator ) const + { + vkDestroyDevice( m_device, reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroy( Optional allocator = nullptr ) const + { + vkDestroyDevice( m_device, reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getQueue( uint32_t queueFamilyIndex, uint32_t queueIndex, Queue* pQueue ) const + { + vkGetDeviceQueue( m_device, queueFamilyIndex, queueIndex, reinterpret_cast( pQueue ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + Queue getQueue( uint32_t queueFamilyIndex, uint32_t queueIndex ) const + { + Queue queue; + vkGetDeviceQueue( m_device, queueFamilyIndex, queueIndex, reinterpret_cast( &queue ) ); + return queue; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result waitIdle( ) const + { + return static_cast( vkDeviceWaitIdle( m_device ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type waitIdle() const + { + Result result = static_cast( vkDeviceWaitIdle( m_device ) ); + return createResultValue( result, "vk::Device::waitIdle" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result allocateMemory( const MemoryAllocateInfo* pAllocateInfo, const AllocationCallbacks* pAllocator, DeviceMemory* pMemory ) const + { + return static_cast( vkAllocateMemory( m_device, reinterpret_cast( pAllocateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pMemory ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type allocateMemory( const MemoryAllocateInfo & allocateInfo, Optional allocator = nullptr ) const + { + DeviceMemory memory; + Result result = static_cast( vkAllocateMemory( m_device, reinterpret_cast( &allocateInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &memory ) ) ); + return createResultValue( result, memory, "vk::Device::allocateMemory" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void freeMemory( DeviceMemory memory, const AllocationCallbacks* pAllocator ) const + { + vkFreeMemory( m_device, static_cast( memory ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void freeMemory( DeviceMemory memory, Optional allocator = nullptr ) const + { + vkFreeMemory( m_device, static_cast( memory ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result mapMemory( DeviceMemory memory, DeviceSize offset, DeviceSize size, MemoryMapFlags flags, void** ppData ) const + { + return static_cast( vkMapMemory( m_device, static_cast( memory ), offset, size, static_cast( flags ), ppData ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type mapMemory( DeviceMemory memory, DeviceSize offset, DeviceSize size, MemoryMapFlags flags = MemoryMapFlags() ) const + { + void* pData; + Result result = static_cast( vkMapMemory( m_device, static_cast( memory ), offset, size, static_cast( flags ), &pData ) ); + return createResultValue( result, pData, "vk::Device::mapMemory" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void unmapMemory( DeviceMemory memory ) const + { + vkUnmapMemory( m_device, static_cast( memory ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void unmapMemory( DeviceMemory memory ) const + { + vkUnmapMemory( m_device, static_cast( memory ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result flushMappedMemoryRanges( uint32_t memoryRangeCount, const MappedMemoryRange* pMemoryRanges ) const + { + return static_cast( vkFlushMappedMemoryRanges( m_device, memoryRangeCount, reinterpret_cast( pMemoryRanges ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type flushMappedMemoryRanges( ArrayProxy memoryRanges ) const + { + Result result = static_cast( vkFlushMappedMemoryRanges( m_device, memoryRanges.size() , reinterpret_cast( memoryRanges.data() ) ) ); + return createResultValue( result, "vk::Device::flushMappedMemoryRanges" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result invalidateMappedMemoryRanges( uint32_t memoryRangeCount, const MappedMemoryRange* pMemoryRanges ) const + { + return static_cast( vkInvalidateMappedMemoryRanges( m_device, memoryRangeCount, reinterpret_cast( pMemoryRanges ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type invalidateMappedMemoryRanges( ArrayProxy memoryRanges ) const + { + Result result = static_cast( vkInvalidateMappedMemoryRanges( m_device, memoryRanges.size() , reinterpret_cast( memoryRanges.data() ) ) ); + return createResultValue( result, "vk::Device::invalidateMappedMemoryRanges" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getMemoryCommitment( DeviceMemory memory, DeviceSize* pCommittedMemoryInBytes ) const + { + vkGetDeviceMemoryCommitment( m_device, static_cast( memory ), pCommittedMemoryInBytes ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + DeviceSize getMemoryCommitment( DeviceMemory memory ) const + { + DeviceSize committedMemoryInBytes; + vkGetDeviceMemoryCommitment( m_device, static_cast( memory ), &committedMemoryInBytes ); + return committedMemoryInBytes; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getBufferMemoryRequirements( Buffer buffer, MemoryRequirements* pMemoryRequirements ) const + { + vkGetBufferMemoryRequirements( m_device, static_cast( buffer ), reinterpret_cast( pMemoryRequirements ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + MemoryRequirements getBufferMemoryRequirements( Buffer buffer ) const + { + MemoryRequirements memoryRequirements; + vkGetBufferMemoryRequirements( m_device, static_cast( buffer ), reinterpret_cast( &memoryRequirements ) ); + return memoryRequirements; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result bindBufferMemory( Buffer buffer, DeviceMemory memory, DeviceSize memoryOffset ) const + { + return static_cast( vkBindBufferMemory( m_device, static_cast( buffer ), static_cast( memory ), memoryOffset ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type bindBufferMemory( Buffer buffer, DeviceMemory memory, DeviceSize memoryOffset ) const + { + Result result = static_cast( vkBindBufferMemory( m_device, static_cast( buffer ), static_cast( memory ), memoryOffset ) ); + return createResultValue( result, "vk::Device::bindBufferMemory" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getImageMemoryRequirements( Image image, MemoryRequirements* pMemoryRequirements ) const + { + vkGetImageMemoryRequirements( m_device, static_cast( image ), reinterpret_cast( pMemoryRequirements ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + MemoryRequirements getImageMemoryRequirements( Image image ) const + { + MemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements( m_device, static_cast( image ), reinterpret_cast( &memoryRequirements ) ); + return memoryRequirements; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result bindImageMemory( Image image, DeviceMemory memory, DeviceSize memoryOffset ) const + { + return static_cast( vkBindImageMemory( m_device, static_cast( image ), static_cast( memory ), memoryOffset ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type bindImageMemory( Image image, DeviceMemory memory, DeviceSize memoryOffset ) const + { + Result result = static_cast( vkBindImageMemory( m_device, static_cast( image ), static_cast( memory ), memoryOffset ) ); + return createResultValue( result, "vk::Device::bindImageMemory" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getImageSparseMemoryRequirements( Image image, uint32_t* pSparseMemoryRequirementCount, SparseImageMemoryRequirements* pSparseMemoryRequirements ) const + { + vkGetImageSparseMemoryRequirements( m_device, static_cast( image ), pSparseMemoryRequirementCount, reinterpret_cast( pSparseMemoryRequirements ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + std::vector getImageSparseMemoryRequirements( Image image ) const + { + std::vector sparseMemoryRequirements; + uint32_t sparseMemoryRequirementCount; + vkGetImageSparseMemoryRequirements( m_device, static_cast( image ), &sparseMemoryRequirementCount, nullptr ); + sparseMemoryRequirements.resize( sparseMemoryRequirementCount ); + vkGetImageSparseMemoryRequirements( m_device, static_cast( image ), &sparseMemoryRequirementCount, reinterpret_cast( sparseMemoryRequirements.data() ) ); + return sparseMemoryRequirements; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createFence( const FenceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const + { + return static_cast( vkCreateFence( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFence ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createFence( const FenceCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Fence fence; + Result result = static_cast( vkCreateFence( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &fence ) ) ); + return createResultValue( result, fence, "vk::Device::createFence" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyFence( Fence fence, const AllocationCallbacks* pAllocator ) const + { + vkDestroyFence( m_device, static_cast( fence ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyFence( Fence fence, Optional allocator = nullptr ) const + { + vkDestroyFence( m_device, static_cast( fence ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result resetFences( uint32_t fenceCount, const Fence* pFences ) const + { + return static_cast( vkResetFences( m_device, fenceCount, reinterpret_cast( pFences ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type resetFences( ArrayProxy fences ) const + { + Result result = static_cast( vkResetFences( m_device, fences.size() , reinterpret_cast( fences.data() ) ) ); + return createResultValue( result, "vk::Device::resetFences" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result getFenceStatus( Fence fence ) const + { + return static_cast( vkGetFenceStatus( m_device, static_cast( fence ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result getFenceStatus( Fence fence ) const + { + Result result = static_cast( vkGetFenceStatus( m_device, static_cast( fence ) ) ); + return createResultValue( result, "vk::Device::getFenceStatus", { Result::eSuccess, Result::eNotReady } ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result waitForFences( uint32_t fenceCount, const Fence* pFences, Bool32 waitAll, uint64_t timeout ) const + { + return static_cast( vkWaitForFences( m_device, fenceCount, reinterpret_cast( pFences ), waitAll, timeout ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result waitForFences( ArrayProxy fences, Bool32 waitAll, uint64_t timeout ) const + { + Result result = static_cast( vkWaitForFences( m_device, fences.size() , reinterpret_cast( fences.data() ), waitAll, timeout ) ); + return createResultValue( result, "vk::Device::waitForFences", { Result::eSuccess, Result::eTimeout } ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createSemaphore( const SemaphoreCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Semaphore* pSemaphore ) const + { + return static_cast( vkCreateSemaphore( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSemaphore ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createSemaphore( const SemaphoreCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Semaphore semaphore; + Result result = static_cast( vkCreateSemaphore( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &semaphore ) ) ); + return createResultValue( result, semaphore, "vk::Device::createSemaphore" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroySemaphore( Semaphore semaphore, const AllocationCallbacks* pAllocator ) const + { + vkDestroySemaphore( m_device, static_cast( semaphore ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroySemaphore( Semaphore semaphore, Optional allocator = nullptr ) const + { + vkDestroySemaphore( m_device, static_cast( semaphore ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createEvent( const EventCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Event* pEvent ) const + { + return static_cast( vkCreateEvent( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pEvent ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createEvent( const EventCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Event event; + Result result = static_cast( vkCreateEvent( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &event ) ) ); + return createResultValue( result, event, "vk::Device::createEvent" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyEvent( Event event, const AllocationCallbacks* pAllocator ) const + { + vkDestroyEvent( m_device, static_cast( event ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyEvent( Event event, Optional allocator = nullptr ) const + { + vkDestroyEvent( m_device, static_cast( event ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result getEventStatus( Event event ) const + { + return static_cast( vkGetEventStatus( m_device, static_cast( event ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result getEventStatus( Event event ) const + { + Result result = static_cast( vkGetEventStatus( m_device, static_cast( event ) ) ); + return createResultValue( result, "vk::Device::getEventStatus", { Result::eEventSet, Result::eEventReset } ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result setEvent( Event event ) const + { + return static_cast( vkSetEvent( m_device, static_cast( event ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type setEvent( Event event ) const + { + Result result = static_cast( vkSetEvent( m_device, static_cast( event ) ) ); + return createResultValue( result, "vk::Device::setEvent" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result resetEvent( Event event ) const + { + return static_cast( vkResetEvent( m_device, static_cast( event ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type resetEvent( Event event ) const + { + Result result = static_cast( vkResetEvent( m_device, static_cast( event ) ) ); + return createResultValue( result, "vk::Device::resetEvent" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createQueryPool( const QueryPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, QueryPool* pQueryPool ) const + { + return static_cast( vkCreateQueryPool( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pQueryPool ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createQueryPool( const QueryPoolCreateInfo & createInfo, Optional allocator = nullptr ) const + { + QueryPool queryPool; + Result result = static_cast( vkCreateQueryPool( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &queryPool ) ) ); + return createResultValue( result, queryPool, "vk::Device::createQueryPool" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyQueryPool( QueryPool queryPool, const AllocationCallbacks* pAllocator ) const + { + vkDestroyQueryPool( m_device, static_cast( queryPool ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyQueryPool( QueryPool queryPool, Optional allocator = nullptr ) const + { + vkDestroyQueryPool( m_device, static_cast( queryPool ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, DeviceSize stride, QueryResultFlags flags ) const + { + return static_cast( vkGetQueryPoolResults( m_device, static_cast( queryPool ), firstQuery, queryCount, dataSize, pData, stride, static_cast( flags ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template + Result getQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, ArrayProxy data, DeviceSize stride, QueryResultFlags flags ) const + { + Result result = static_cast( vkGetQueryPoolResults( m_device, static_cast( queryPool ), firstQuery, queryCount, data.size() * sizeof( T ) , reinterpret_cast( data.data() ), stride, static_cast( flags ) ) ); + return createResultValue( result, "vk::Device::getQueryPoolResults", { Result::eSuccess, Result::eNotReady } ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createBuffer( const BufferCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Buffer* pBuffer ) const + { + return static_cast( vkCreateBuffer( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pBuffer ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createBuffer( const BufferCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Buffer buffer; + Result result = static_cast( vkCreateBuffer( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &buffer ) ) ); + return createResultValue( result, buffer, "vk::Device::createBuffer" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyBuffer( Buffer buffer, const AllocationCallbacks* pAllocator ) const + { + vkDestroyBuffer( m_device, static_cast( buffer ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyBuffer( Buffer buffer, Optional allocator = nullptr ) const + { + vkDestroyBuffer( m_device, static_cast( buffer ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createBufferView( const BufferViewCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, BufferView* pView ) const + { + return static_cast( vkCreateBufferView( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pView ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createBufferView( const BufferViewCreateInfo & createInfo, Optional allocator = nullptr ) const + { + BufferView view; + Result result = static_cast( vkCreateBufferView( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &view ) ) ); + return createResultValue( result, view, "vk::Device::createBufferView" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyBufferView( BufferView bufferView, const AllocationCallbacks* pAllocator ) const + { + vkDestroyBufferView( m_device, static_cast( bufferView ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyBufferView( BufferView bufferView, Optional allocator = nullptr ) const + { + vkDestroyBufferView( m_device, static_cast( bufferView ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createImage( const ImageCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Image* pImage ) const + { + return static_cast( vkCreateImage( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pImage ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createImage( const ImageCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Image image; + Result result = static_cast( vkCreateImage( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &image ) ) ); + return createResultValue( result, image, "vk::Device::createImage" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyImage( Image image, const AllocationCallbacks* pAllocator ) const + { + vkDestroyImage( m_device, static_cast( image ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyImage( Image image, Optional allocator = nullptr ) const + { + vkDestroyImage( m_device, static_cast( image ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getImageSubresourceLayout( Image image, const ImageSubresource* pSubresource, SubresourceLayout* pLayout ) const + { + vkGetImageSubresourceLayout( m_device, static_cast( image ), reinterpret_cast( pSubresource ), reinterpret_cast( pLayout ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + SubresourceLayout getImageSubresourceLayout( Image image, const ImageSubresource & subresource ) const + { + SubresourceLayout layout; + vkGetImageSubresourceLayout( m_device, static_cast( image ), reinterpret_cast( &subresource ), reinterpret_cast( &layout ) ); + return layout; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createImageView( const ImageViewCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, ImageView* pView ) const + { + return static_cast( vkCreateImageView( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pView ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createImageView( const ImageViewCreateInfo & createInfo, Optional allocator = nullptr ) const + { + ImageView view; + Result result = static_cast( vkCreateImageView( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &view ) ) ); + return createResultValue( result, view, "vk::Device::createImageView" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyImageView( ImageView imageView, const AllocationCallbacks* pAllocator ) const + { + vkDestroyImageView( m_device, static_cast( imageView ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyImageView( ImageView imageView, Optional allocator = nullptr ) const + { + vkDestroyImageView( m_device, static_cast( imageView ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createShaderModule( const ShaderModuleCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, ShaderModule* pShaderModule ) const + { + return static_cast( vkCreateShaderModule( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pShaderModule ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createShaderModule( const ShaderModuleCreateInfo & createInfo, Optional allocator = nullptr ) const + { + ShaderModule shaderModule; + Result result = static_cast( vkCreateShaderModule( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &shaderModule ) ) ); + return createResultValue( result, shaderModule, "vk::Device::createShaderModule" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyShaderModule( ShaderModule shaderModule, const AllocationCallbacks* pAllocator ) const + { + vkDestroyShaderModule( m_device, static_cast( shaderModule ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyShaderModule( ShaderModule shaderModule, Optional allocator = nullptr ) const + { + vkDestroyShaderModule( m_device, static_cast( shaderModule ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createPipelineCache( const PipelineCacheCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, PipelineCache* pPipelineCache ) const + { + return static_cast( vkCreatePipelineCache( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelineCache ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createPipelineCache( const PipelineCacheCreateInfo & createInfo, Optional allocator = nullptr ) const + { + PipelineCache pipelineCache; + Result result = static_cast( vkCreatePipelineCache( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &pipelineCache ) ) ); + return createResultValue( result, pipelineCache, "vk::Device::createPipelineCache" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyPipelineCache( PipelineCache pipelineCache, const AllocationCallbacks* pAllocator ) const + { + vkDestroyPipelineCache( m_device, static_cast( pipelineCache ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyPipelineCache( PipelineCache pipelineCache, Optional allocator = nullptr ) const + { + vkDestroyPipelineCache( m_device, static_cast( pipelineCache ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getPipelineCacheData( PipelineCache pipelineCache, size_t* pDataSize, void* pData ) const + { + return static_cast( vkGetPipelineCacheData( m_device, static_cast( pipelineCache ), pDataSize, pData ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getPipelineCacheData( PipelineCache pipelineCache ) const + { + std::vector data; + size_t dataSize; + Result result; + do + { + result = static_cast( vkGetPipelineCacheData( m_device, static_cast( pipelineCache ), &dataSize, nullptr ) ); + if ( ( result == Result::eSuccess ) && dataSize ) + { + data.resize( dataSize ); + result = static_cast( vkGetPipelineCacheData( m_device, static_cast( pipelineCache ), &dataSize, reinterpret_cast( data.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( dataSize <= data.size() ); + data.resize( dataSize ); + return createResultValue( result, data, "vk::Device::getPipelineCacheData" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result mergePipelineCaches( PipelineCache dstCache, uint32_t srcCacheCount, const PipelineCache* pSrcCaches ) const + { + return static_cast( vkMergePipelineCaches( m_device, static_cast( dstCache ), srcCacheCount, reinterpret_cast( pSrcCaches ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type mergePipelineCaches( PipelineCache dstCache, ArrayProxy srcCaches ) const + { + Result result = static_cast( vkMergePipelineCaches( m_device, static_cast( dstCache ), srcCaches.size() , reinterpret_cast( srcCaches.data() ) ) ); + return createResultValue( result, "vk::Device::mergePipelineCaches" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createGraphicsPipelines( PipelineCache pipelineCache, uint32_t createInfoCount, const GraphicsPipelineCreateInfo* pCreateInfos, const AllocationCallbacks* pAllocator, Pipeline* pPipelines ) const + { + return static_cast( vkCreateGraphicsPipelines( m_device, static_cast( pipelineCache ), createInfoCount, reinterpret_cast( pCreateInfos ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelines ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type createGraphicsPipelines( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator = nullptr ) const + { + std::vector pipelines( createInfos.size() ); + Result result = static_cast( vkCreateGraphicsPipelines( m_device, static_cast( pipelineCache ), createInfos.size() , reinterpret_cast( createInfos.data() ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( pipelines.data() ) ) ); + return createResultValue( result, pipelines, "vk::Device::createGraphicsPipelines" ); + } + + ResultValueType::type createGraphicsPipeline( PipelineCache pipelineCache, const GraphicsPipelineCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Pipeline pipeline; + Result result = static_cast( vkCreateGraphicsPipelines( m_device, static_cast( pipelineCache ), 1 , reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &pipeline ) ) ); + return createResultValue( result, pipeline, "vk::Device::createGraphicsPipeline" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createComputePipelines( PipelineCache pipelineCache, uint32_t createInfoCount, const ComputePipelineCreateInfo* pCreateInfos, const AllocationCallbacks* pAllocator, Pipeline* pPipelines ) const + { + return static_cast( vkCreateComputePipelines( m_device, static_cast( pipelineCache ), createInfoCount, reinterpret_cast( pCreateInfos ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelines ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type createComputePipelines( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator = nullptr ) const + { + std::vector pipelines( createInfos.size() ); + Result result = static_cast( vkCreateComputePipelines( m_device, static_cast( pipelineCache ), createInfos.size() , reinterpret_cast( createInfos.data() ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( pipelines.data() ) ) ); + return createResultValue( result, pipelines, "vk::Device::createComputePipelines" ); + } + + ResultValueType::type createComputePipeline( PipelineCache pipelineCache, const ComputePipelineCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Pipeline pipeline; + Result result = static_cast( vkCreateComputePipelines( m_device, static_cast( pipelineCache ), 1 , reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &pipeline ) ) ); + return createResultValue( result, pipeline, "vk::Device::createComputePipeline" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyPipeline( Pipeline pipeline, const AllocationCallbacks* pAllocator ) const + { + vkDestroyPipeline( m_device, static_cast( pipeline ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyPipeline( Pipeline pipeline, Optional allocator = nullptr ) const + { + vkDestroyPipeline( m_device, static_cast( pipeline ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createPipelineLayout( const PipelineLayoutCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, PipelineLayout* pPipelineLayout ) const + { + return static_cast( vkCreatePipelineLayout( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelineLayout ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createPipelineLayout( const PipelineLayoutCreateInfo & createInfo, Optional allocator = nullptr ) const + { + PipelineLayout pipelineLayout; + Result result = static_cast( vkCreatePipelineLayout( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &pipelineLayout ) ) ); + return createResultValue( result, pipelineLayout, "vk::Device::createPipelineLayout" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyPipelineLayout( PipelineLayout pipelineLayout, const AllocationCallbacks* pAllocator ) const + { + vkDestroyPipelineLayout( m_device, static_cast( pipelineLayout ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyPipelineLayout( PipelineLayout pipelineLayout, Optional allocator = nullptr ) const + { + vkDestroyPipelineLayout( m_device, static_cast( pipelineLayout ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createSampler( const SamplerCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Sampler* pSampler ) const + { + return static_cast( vkCreateSampler( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSampler ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createSampler( const SamplerCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Sampler sampler; + Result result = static_cast( vkCreateSampler( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &sampler ) ) ); + return createResultValue( result, sampler, "vk::Device::createSampler" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroySampler( Sampler sampler, const AllocationCallbacks* pAllocator ) const + { + vkDestroySampler( m_device, static_cast( sampler ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroySampler( Sampler sampler, Optional allocator = nullptr ) const + { + vkDestroySampler( m_device, static_cast( sampler ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createDescriptorSetLayout( const DescriptorSetLayoutCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorSetLayout* pSetLayout ) const + { + return static_cast( vkCreateDescriptorSetLayout( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSetLayout ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createDescriptorSetLayout( const DescriptorSetLayoutCreateInfo & createInfo, Optional allocator = nullptr ) const + { + DescriptorSetLayout setLayout; + Result result = static_cast( vkCreateDescriptorSetLayout( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &setLayout ) ) ); + return createResultValue( result, setLayout, "vk::Device::createDescriptorSetLayout" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyDescriptorSetLayout( DescriptorSetLayout descriptorSetLayout, const AllocationCallbacks* pAllocator ) const + { + vkDestroyDescriptorSetLayout( m_device, static_cast( descriptorSetLayout ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyDescriptorSetLayout( DescriptorSetLayout descriptorSetLayout, Optional allocator = nullptr ) const + { + vkDestroyDescriptorSetLayout( m_device, static_cast( descriptorSetLayout ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createDescriptorPool( const DescriptorPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorPool* pDescriptorPool ) const + { + return static_cast( vkCreateDescriptorPool( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pDescriptorPool ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createDescriptorPool( const DescriptorPoolCreateInfo & createInfo, Optional allocator = nullptr ) const + { + DescriptorPool descriptorPool; + Result result = static_cast( vkCreateDescriptorPool( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &descriptorPool ) ) ); + return createResultValue( result, descriptorPool, "vk::Device::createDescriptorPool" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyDescriptorPool( DescriptorPool descriptorPool, const AllocationCallbacks* pAllocator ) const + { + vkDestroyDescriptorPool( m_device, static_cast( descriptorPool ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyDescriptorPool( DescriptorPool descriptorPool, Optional allocator = nullptr ) const + { + vkDestroyDescriptorPool( m_device, static_cast( descriptorPool ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result resetDescriptorPool( DescriptorPool descriptorPool, DescriptorPoolResetFlags flags ) const + { + return static_cast( vkResetDescriptorPool( m_device, static_cast( descriptorPool ), static_cast( flags ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type resetDescriptorPool( DescriptorPool descriptorPool, DescriptorPoolResetFlags flags = DescriptorPoolResetFlags() ) const + { + Result result = static_cast( vkResetDescriptorPool( m_device, static_cast( descriptorPool ), static_cast( flags ) ) ); + return createResultValue( result, "vk::Device::resetDescriptorPool" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result allocateDescriptorSets( const DescriptorSetAllocateInfo* pAllocateInfo, DescriptorSet* pDescriptorSets ) const + { + return static_cast( vkAllocateDescriptorSets( m_device, reinterpret_cast( pAllocateInfo ), reinterpret_cast( pDescriptorSets ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type allocateDescriptorSets( const DescriptorSetAllocateInfo & allocateInfo ) const + { + std::vector descriptorSets( allocateInfo.descriptorSetCount ); + Result result = static_cast( vkAllocateDescriptorSets( m_device, reinterpret_cast( &allocateInfo ), reinterpret_cast( descriptorSets.data() ) ) ); + return createResultValue( result, descriptorSets, "vk::Device::allocateDescriptorSets" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result freeDescriptorSets( DescriptorPool descriptorPool, uint32_t descriptorSetCount, const DescriptorSet* pDescriptorSets ) const + { + return static_cast( vkFreeDescriptorSets( m_device, static_cast( descriptorPool ), descriptorSetCount, reinterpret_cast( pDescriptorSets ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type freeDescriptorSets( DescriptorPool descriptorPool, ArrayProxy descriptorSets ) const + { + Result result = static_cast( vkFreeDescriptorSets( m_device, static_cast( descriptorPool ), descriptorSets.size() , reinterpret_cast( descriptorSets.data() ) ) ); + return createResultValue( result, "vk::Device::freeDescriptorSets" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void updateDescriptorSets( uint32_t descriptorWriteCount, const WriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const CopyDescriptorSet* pDescriptorCopies ) const + { + vkUpdateDescriptorSets( m_device, descriptorWriteCount, reinterpret_cast( pDescriptorWrites ), descriptorCopyCount, reinterpret_cast( pDescriptorCopies ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void updateDescriptorSets( ArrayProxy descriptorWrites, ArrayProxy descriptorCopies ) const + { + vkUpdateDescriptorSets( m_device, descriptorWrites.size() , reinterpret_cast( descriptorWrites.data() ), descriptorCopies.size() , reinterpret_cast( descriptorCopies.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createFramebuffer( const FramebufferCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Framebuffer* pFramebuffer ) const + { + return static_cast( vkCreateFramebuffer( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFramebuffer ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createFramebuffer( const FramebufferCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Framebuffer framebuffer; + Result result = static_cast( vkCreateFramebuffer( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &framebuffer ) ) ); + return createResultValue( result, framebuffer, "vk::Device::createFramebuffer" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyFramebuffer( Framebuffer framebuffer, const AllocationCallbacks* pAllocator ) const + { + vkDestroyFramebuffer( m_device, static_cast( framebuffer ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyFramebuffer( Framebuffer framebuffer, Optional allocator = nullptr ) const + { + vkDestroyFramebuffer( m_device, static_cast( framebuffer ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createRenderPass( const RenderPassCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, RenderPass* pRenderPass ) const + { + return static_cast( vkCreateRenderPass( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pRenderPass ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createRenderPass( const RenderPassCreateInfo & createInfo, Optional allocator = nullptr ) const + { + RenderPass renderPass; + Result result = static_cast( vkCreateRenderPass( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &renderPass ) ) ); + return createResultValue( result, renderPass, "vk::Device::createRenderPass" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyRenderPass( RenderPass renderPass, const AllocationCallbacks* pAllocator ) const + { + vkDestroyRenderPass( m_device, static_cast( renderPass ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyRenderPass( RenderPass renderPass, Optional allocator = nullptr ) const + { + vkDestroyRenderPass( m_device, static_cast( renderPass ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getRenderAreaGranularity( RenderPass renderPass, Extent2D* pGranularity ) const + { + vkGetRenderAreaGranularity( m_device, static_cast( renderPass ), reinterpret_cast( pGranularity ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + Extent2D getRenderAreaGranularity( RenderPass renderPass ) const + { + Extent2D granularity; + vkGetRenderAreaGranularity( m_device, static_cast( renderPass ), reinterpret_cast( &granularity ) ); + return granularity; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createCommandPool( const CommandPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, CommandPool* pCommandPool ) const + { + return static_cast( vkCreateCommandPool( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pCommandPool ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createCommandPool( const CommandPoolCreateInfo & createInfo, Optional allocator = nullptr ) const + { + CommandPool commandPool; + Result result = static_cast( vkCreateCommandPool( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &commandPool ) ) ); + return createResultValue( result, commandPool, "vk::Device::createCommandPool" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyCommandPool( CommandPool commandPool, const AllocationCallbacks* pAllocator ) const + { + vkDestroyCommandPool( m_device, static_cast( commandPool ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyCommandPool( CommandPool commandPool, Optional allocator = nullptr ) const + { + vkDestroyCommandPool( m_device, static_cast( commandPool ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result resetCommandPool( CommandPool commandPool, CommandPoolResetFlags flags ) const + { + return static_cast( vkResetCommandPool( m_device, static_cast( commandPool ), static_cast( flags ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type resetCommandPool( CommandPool commandPool, CommandPoolResetFlags flags ) const + { + Result result = static_cast( vkResetCommandPool( m_device, static_cast( commandPool ), static_cast( flags ) ) ); + return createResultValue( result, "vk::Device::resetCommandPool" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result allocateCommandBuffers( const CommandBufferAllocateInfo* pAllocateInfo, CommandBuffer* pCommandBuffers ) const + { + return static_cast( vkAllocateCommandBuffers( m_device, reinterpret_cast( pAllocateInfo ), reinterpret_cast( pCommandBuffers ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type allocateCommandBuffers( const CommandBufferAllocateInfo & allocateInfo ) const + { + std::vector commandBuffers( allocateInfo.commandBufferCount ); + Result result = static_cast( vkAllocateCommandBuffers( m_device, reinterpret_cast( &allocateInfo ), reinterpret_cast( commandBuffers.data() ) ) ); + return createResultValue( result, commandBuffers, "vk::Device::allocateCommandBuffers" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void freeCommandBuffers( CommandPool commandPool, uint32_t commandBufferCount, const CommandBuffer* pCommandBuffers ) const + { + vkFreeCommandBuffers( m_device, static_cast( commandPool ), commandBufferCount, reinterpret_cast( pCommandBuffers ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void freeCommandBuffers( CommandPool commandPool, ArrayProxy commandBuffers ) const + { + vkFreeCommandBuffers( m_device, static_cast( commandPool ), commandBuffers.size() , reinterpret_cast( commandBuffers.data() ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createSharedSwapchainsKHR( uint32_t swapchainCount, const SwapchainCreateInfoKHR* pCreateInfos, const AllocationCallbacks* pAllocator, SwapchainKHR* pSwapchains ) const + { + return static_cast( vkCreateSharedSwapchainsKHR( m_device, swapchainCount, reinterpret_cast( pCreateInfos ), reinterpret_cast( pAllocator ), reinterpret_cast( pSwapchains ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type createSharedSwapchainsKHR( ArrayProxy createInfos, Optional allocator = nullptr ) const + { + std::vector swapchains( createInfos.size() ); + Result result = static_cast( vkCreateSharedSwapchainsKHR( m_device, createInfos.size() , reinterpret_cast( createInfos.data() ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( swapchains.data() ) ) ); + return createResultValue( result, swapchains, "vk::Device::createSharedSwapchainsKHR" ); + } + + ResultValueType::type createSharedSwapchainKHR( const SwapchainCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SwapchainKHR swapchain; + Result result = static_cast( vkCreateSharedSwapchainsKHR( m_device, 1 , reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &swapchain ) ) ); + return createResultValue( result, swapchain, "vk::Device::createSharedSwapchainKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createSwapchainKHR( const SwapchainCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SwapchainKHR* pSwapchain ) const + { + return static_cast( vkCreateSwapchainKHR( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSwapchain ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createSwapchainKHR( const SwapchainCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SwapchainKHR swapchain; + Result result = static_cast( vkCreateSwapchainKHR( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &swapchain ) ) ); + return createResultValue( result, swapchain, "vk::Device::createSwapchainKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroySwapchainKHR( SwapchainKHR swapchain, const AllocationCallbacks* pAllocator ) const + { + vkDestroySwapchainKHR( m_device, static_cast( swapchain ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroySwapchainKHR( SwapchainKHR swapchain, Optional allocator = nullptr ) const + { + vkDestroySwapchainKHR( m_device, static_cast( swapchain ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getSwapchainImagesKHR( SwapchainKHR swapchain, uint32_t* pSwapchainImageCount, Image* pSwapchainImages ) const + { + return static_cast( vkGetSwapchainImagesKHR( m_device, static_cast( swapchain ), pSwapchainImageCount, reinterpret_cast( pSwapchainImages ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getSwapchainImagesKHR( SwapchainKHR swapchain ) const + { + std::vector swapchainImages; + uint32_t swapchainImageCount; + Result result; + do + { + result = static_cast( vkGetSwapchainImagesKHR( m_device, static_cast( swapchain ), &swapchainImageCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && swapchainImageCount ) + { + swapchainImages.resize( swapchainImageCount ); + result = static_cast( vkGetSwapchainImagesKHR( m_device, static_cast( swapchain ), &swapchainImageCount, reinterpret_cast( swapchainImages.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( swapchainImageCount <= swapchainImages.size() ); + swapchainImages.resize( swapchainImageCount ); + return createResultValue( result, swapchainImages, "vk::Device::getSwapchainImagesKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result acquireNextImageKHR( SwapchainKHR swapchain, uint64_t timeout, Semaphore semaphore, Fence fence, uint32_t* pImageIndex ) const + { + return static_cast( vkAcquireNextImageKHR( m_device, static_cast( swapchain ), timeout, static_cast( semaphore ), static_cast( fence ), pImageIndex ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValue acquireNextImageKHR( SwapchainKHR swapchain, uint64_t timeout, Semaphore semaphore, Fence fence ) const + { + uint32_t imageIndex; + Result result = static_cast( vkAcquireNextImageKHR( m_device, static_cast( swapchain ), timeout, static_cast( semaphore ), static_cast( fence ), &imageIndex ) ); + return createResultValue( result, imageIndex, "vk::Device::acquireNextImageKHR", { Result::eSuccess, Result::eTimeout, Result::eNotReady, Result::eSuboptimalKHR } ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result debugMarkerSetObjectNameEXT( DebugMarkerObjectNameInfoEXT* pNameInfo ) const + { + return static_cast( vkDebugMarkerSetObjectNameEXT( m_device, reinterpret_cast( pNameInfo ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type debugMarkerSetObjectNameEXT() const + { + DebugMarkerObjectNameInfoEXT nameInfo; + Result result = static_cast( vkDebugMarkerSetObjectNameEXT( m_device, reinterpret_cast( &nameInfo ) ) ); + return createResultValue( result, nameInfo, "vk::Device::debugMarkerSetObjectNameEXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result debugMarkerSetObjectTagEXT( DebugMarkerObjectTagInfoEXT* pTagInfo ) const + { + return static_cast( vkDebugMarkerSetObjectTagEXT( m_device, reinterpret_cast( pTagInfo ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type debugMarkerSetObjectTagEXT() const + { + DebugMarkerObjectTagInfoEXT tagInfo; + Result result = static_cast( vkDebugMarkerSetObjectTagEXT( m_device, reinterpret_cast( &tagInfo ) ) ); + return createResultValue( result, tagInfo, "vk::Device::debugMarkerSetObjectTagEXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + Result getMemoryWin32HandleNV( DeviceMemory memory, ExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle ) const + { + return static_cast( vkGetMemoryWin32HandleNV( m_device, static_cast( memory ), static_cast( handleType ), pHandle ) ); + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_WIN32_KHR + ResultValueType::type getMemoryWin32HandleNV( DeviceMemory memory, ExternalMemoryHandleTypeFlagsNV handleType ) const + { + HANDLE handle; + Result result = static_cast( vkGetMemoryWin32HandleNV( m_device, static_cast( memory ), static_cast( handleType ), &handle ) ); + return createResultValue( result, handle, "vk::Device::getMemoryWin32HandleNV" ); + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createIndirectCommandsLayoutNVX( const IndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const AllocationCallbacks* pAllocator, IndirectCommandsLayoutNVX* pIndirectCommandsLayout ) const + { + return static_cast( vkCreateIndirectCommandsLayoutNVX( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pIndirectCommandsLayout ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createIndirectCommandsLayoutNVX( const IndirectCommandsLayoutCreateInfoNVX & createInfo, Optional allocator = nullptr ) const + { + IndirectCommandsLayoutNVX indirectCommandsLayout; + Result result = static_cast( vkCreateIndirectCommandsLayoutNVX( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &indirectCommandsLayout ) ) ); + return createResultValue( result, indirectCommandsLayout, "vk::Device::createIndirectCommandsLayoutNVX" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyIndirectCommandsLayoutNVX( IndirectCommandsLayoutNVX indirectCommandsLayout, const AllocationCallbacks* pAllocator ) const + { + vkDestroyIndirectCommandsLayoutNVX( m_device, static_cast( indirectCommandsLayout ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyIndirectCommandsLayoutNVX( IndirectCommandsLayoutNVX indirectCommandsLayout, Optional allocator = nullptr ) const + { + vkDestroyIndirectCommandsLayoutNVX( m_device, static_cast( indirectCommandsLayout ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createObjectTableNVX( const ObjectTableCreateInfoNVX* pCreateInfo, const AllocationCallbacks* pAllocator, ObjectTableNVX* pObjectTable ) const + { + return static_cast( vkCreateObjectTableNVX( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pObjectTable ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createObjectTableNVX( const ObjectTableCreateInfoNVX & createInfo, Optional allocator = nullptr ) const + { + ObjectTableNVX objectTable; + Result result = static_cast( vkCreateObjectTableNVX( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &objectTable ) ) ); + return createResultValue( result, objectTable, "vk::Device::createObjectTableNVX" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyObjectTableNVX( ObjectTableNVX objectTable, const AllocationCallbacks* pAllocator ) const + { + vkDestroyObjectTableNVX( m_device, static_cast( objectTable ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyObjectTableNVX( ObjectTableNVX objectTable, Optional allocator = nullptr ) const + { + vkDestroyObjectTableNVX( m_device, static_cast( objectTable ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result registerObjectsNVX( ObjectTableNVX objectTable, uint32_t objectCount, const ObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices ) const + { + return static_cast( vkRegisterObjectsNVX( m_device, static_cast( objectTable ), objectCount, reinterpret_cast( ppObjectTableEntries ), pObjectIndices ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type registerObjectsNVX( ObjectTableNVX objectTable, ArrayProxy pObjectTableEntries, ArrayProxy objectIndices ) const + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( pObjectTableEntries.size() == objectIndices.size() ); +#else + if ( pObjectTableEntries.size() != objectIndices.size() ) + { + throw std::logic_error( "vk::Device::registerObjectsNVX: pObjectTableEntries.size() != objectIndices.size()" ); + } +#endif // VULKAN_HPP_NO_EXCEPTIONS + Result result = static_cast( vkRegisterObjectsNVX( m_device, static_cast( objectTable ), pObjectTableEntries.size() , reinterpret_cast( pObjectTableEntries.data() ), objectIndices.data() ) ); + return createResultValue( result, "vk::Device::registerObjectsNVX" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result unregisterObjectsNVX( ObjectTableNVX objectTable, uint32_t objectCount, const ObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices ) const + { + return static_cast( vkUnregisterObjectsNVX( m_device, static_cast( objectTable ), objectCount, reinterpret_cast( pObjectEntryTypes ), pObjectIndices ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type unregisterObjectsNVX( ObjectTableNVX objectTable, ArrayProxy objectEntryTypes, ArrayProxy objectIndices ) const + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( objectEntryTypes.size() == objectIndices.size() ); +#else + if ( objectEntryTypes.size() != objectIndices.size() ) + { + throw std::logic_error( "vk::Device::unregisterObjectsNVX: objectEntryTypes.size() != objectIndices.size()" ); + } +#endif // VULKAN_HPP_NO_EXCEPTIONS + Result result = static_cast( vkUnregisterObjectsNVX( m_device, static_cast( objectTable ), objectEntryTypes.size() , reinterpret_cast( objectEntryTypes.data() ), objectIndices.data() ) ); + return createResultValue( result, "vk::Device::unregisterObjectsNVX" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + void trimCommandPoolKHR( CommandPool commandPool, CommandPoolTrimFlagsKHR flags ) const + { + vkTrimCommandPoolKHR( m_device, static_cast( commandPool ), static_cast( flags ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void trimCommandPoolKHR( CommandPool commandPool, CommandPoolTrimFlagsKHR flags = CommandPoolTrimFlagsKHR() ) const + { + vkTrimCommandPoolKHR( m_device, static_cast( commandPool ), static_cast( flags ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result displayPowerControlEXT( DisplayKHR display, const DisplayPowerInfoEXT* pDisplayPowerInfo ) const + { + return static_cast( vkDisplayPowerControlEXT( m_device, static_cast( display ), reinterpret_cast( pDisplayPowerInfo ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type displayPowerControlEXT( DisplayKHR display, const DisplayPowerInfoEXT & displayPowerInfo ) const + { + Result result = static_cast( vkDisplayPowerControlEXT( m_device, static_cast( display ), reinterpret_cast( &displayPowerInfo ) ) ); + return createResultValue( result, "vk::Device::displayPowerControlEXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result registerEventEXT( const DeviceEventInfoEXT* pDeviceEventInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const + { + return static_cast( vkRegisterDeviceEventEXT( m_device, reinterpret_cast( pDeviceEventInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFence ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type registerEventEXT( const DeviceEventInfoEXT & deviceEventInfo, const AllocationCallbacks & allocator ) const + { + Fence fence; + Result result = static_cast( vkRegisterDeviceEventEXT( m_device, reinterpret_cast( &deviceEventInfo ), reinterpret_cast( &allocator ), reinterpret_cast( &fence ) ) ); + return createResultValue( result, fence, "vk::Device::registerEventEXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result registerDisplayEventEXT( DisplayKHR display, const DisplayEventInfoEXT* pDisplayEventInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const + { + return static_cast( vkRegisterDisplayEventEXT( m_device, static_cast( display ), reinterpret_cast( pDisplayEventInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFence ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type registerDisplayEventEXT( DisplayKHR display, const DisplayEventInfoEXT & displayEventInfo, const AllocationCallbacks & allocator ) const + { + Fence fence; + Result result = static_cast( vkRegisterDisplayEventEXT( m_device, static_cast( display ), reinterpret_cast( &displayEventInfo ), reinterpret_cast( &allocator ), reinterpret_cast( &fence ) ) ); + return createResultValue( result, fence, "vk::Device::registerDisplayEventEXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getSwapchainCounterEXT( SwapchainKHR swapchain, SurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue ) const + { + return static_cast( vkGetSwapchainCounterEXT( m_device, static_cast( swapchain ), static_cast( counter ), pCounterValue ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValue getSwapchainCounterEXT( SwapchainKHR swapchain, SurfaceCounterFlagBitsEXT counter ) const + { + uint64_t counterValue; + Result result = static_cast( vkGetSwapchainCounterEXT( m_device, static_cast( swapchain ), static_cast( counter ), &counterValue ) ); + return createResultValue( result, counterValue, "vk::Device::getSwapchainCounterEXT", { Result::eSuccess, Result::eErrorDeviceLost, Result::eErrorOutOfDateKHR } ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkDevice() const + { + return m_device; + } + + explicit operator bool() const + { + return m_device != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_device == VK_NULL_HANDLE; + } + + private: + VkDevice m_device; + }; + static_assert( sizeof( Device ) == sizeof( VkDevice ), "handle and wrapper have different size!" ); + + class PhysicalDevice + { + public: + PhysicalDevice() + : m_physicalDevice(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + PhysicalDevice(VkPhysicalDevice physicalDevice) + : m_physicalDevice(physicalDevice) + {} + + PhysicalDevice& operator=(VkPhysicalDevice physicalDevice) + { + m_physicalDevice = physicalDevice; + return *this; + } +#endif + + bool operator==(PhysicalDevice const &rhs) const + { + return m_physicalDevice == rhs.m_physicalDevice; + } + + bool operator!=(PhysicalDevice const &rhs) const + { + return m_physicalDevice != rhs.m_physicalDevice; + } + + bool operator<(PhysicalDevice const &rhs) const + { + return m_physicalDevice < rhs.m_physicalDevice; + } + + void getProperties( PhysicalDeviceProperties* pProperties ) const + { + vkGetPhysicalDeviceProperties( m_physicalDevice, reinterpret_cast( pProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PhysicalDeviceProperties getProperties() const + { + PhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties( m_physicalDevice, reinterpret_cast( &properties ) ); + return properties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getQueueFamilyProperties( uint32_t* pQueueFamilyPropertyCount, QueueFamilyProperties* pQueueFamilyProperties ) const + { + vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice, pQueueFamilyPropertyCount, reinterpret_cast( pQueueFamilyProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + std::vector getQueueFamilyProperties() const + { + std::vector queueFamilyProperties; + uint32_t queueFamilyPropertyCount; + vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice, &queueFamilyPropertyCount, nullptr ); + queueFamilyProperties.resize( queueFamilyPropertyCount ); + vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice, &queueFamilyPropertyCount, reinterpret_cast( queueFamilyProperties.data() ) ); + return queueFamilyProperties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getMemoryProperties( PhysicalDeviceMemoryProperties* pMemoryProperties ) const + { + vkGetPhysicalDeviceMemoryProperties( m_physicalDevice, reinterpret_cast( pMemoryProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PhysicalDeviceMemoryProperties getMemoryProperties() const + { + PhysicalDeviceMemoryProperties memoryProperties; + vkGetPhysicalDeviceMemoryProperties( m_physicalDevice, reinterpret_cast( &memoryProperties ) ); + return memoryProperties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getFeatures( PhysicalDeviceFeatures* pFeatures ) const + { + vkGetPhysicalDeviceFeatures( m_physicalDevice, reinterpret_cast( pFeatures ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PhysicalDeviceFeatures getFeatures() const + { + PhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures( m_physicalDevice, reinterpret_cast( &features ) ); + return features; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getFormatProperties( Format format, FormatProperties* pFormatProperties ) const + { + vkGetPhysicalDeviceFormatProperties( m_physicalDevice, static_cast( format ), reinterpret_cast( pFormatProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + FormatProperties getFormatProperties( Format format ) const + { + FormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties( m_physicalDevice, static_cast( format ), reinterpret_cast( &formatProperties ) ); + return formatProperties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getImageFormatProperties( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ImageFormatProperties* pImageFormatProperties ) const + { + return static_cast( vkGetPhysicalDeviceImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), reinterpret_cast( pImageFormatProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type getImageFormatProperties( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags ) const + { + ImageFormatProperties imageFormatProperties; + Result result = static_cast( vkGetPhysicalDeviceImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), reinterpret_cast( &imageFormatProperties ) ) ); + return createResultValue( result, imageFormatProperties, "vk::PhysicalDevice::getImageFormatProperties" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createDevice( const DeviceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Device* pDevice ) const + { + return static_cast( vkCreateDevice( m_physicalDevice, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pDevice ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createDevice( const DeviceCreateInfo & createInfo, Optional allocator = nullptr ) const + { + Device device; + Result result = static_cast( vkCreateDevice( m_physicalDevice, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &device ) ) ); + return createResultValue( result, device, "vk::PhysicalDevice::createDevice" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result enumerateDeviceLayerProperties( uint32_t* pPropertyCount, LayerProperties* pProperties ) const + { + return static_cast( vkEnumerateDeviceLayerProperties( m_physicalDevice, pPropertyCount, reinterpret_cast( pProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type enumerateDeviceLayerProperties() const + { + std::vector properties; + uint32_t propertyCount; + Result result; + do + { + result = static_cast( vkEnumerateDeviceLayerProperties( m_physicalDevice, &propertyCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && propertyCount ) + { + properties.resize( propertyCount ); + result = static_cast( vkEnumerateDeviceLayerProperties( m_physicalDevice, &propertyCount, reinterpret_cast( properties.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( propertyCount <= properties.size() ); + properties.resize( propertyCount ); + return createResultValue( result, properties, "vk::PhysicalDevice::enumerateDeviceLayerProperties" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result enumerateDeviceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, ExtensionProperties* pProperties ) const + { + return static_cast( vkEnumerateDeviceExtensionProperties( m_physicalDevice, pLayerName, pPropertyCount, reinterpret_cast( pProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type enumerateDeviceExtensionProperties( Optional layerName = nullptr ) const + { + std::vector properties; + uint32_t propertyCount; + Result result; + do + { + result = static_cast( vkEnumerateDeviceExtensionProperties( m_physicalDevice, layerName ? layerName->c_str() : nullptr, &propertyCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && propertyCount ) + { + properties.resize( propertyCount ); + result = static_cast( vkEnumerateDeviceExtensionProperties( m_physicalDevice, layerName ? layerName->c_str() : nullptr, &propertyCount, reinterpret_cast( properties.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( propertyCount <= properties.size() ); + properties.resize( propertyCount ); + return createResultValue( result, properties, "vk::PhysicalDevice::enumerateDeviceExtensionProperties" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getSparseImageFormatProperties( Format format, ImageType type, SampleCountFlagBits samples, ImageUsageFlags usage, ImageTiling tiling, uint32_t* pPropertyCount, SparseImageFormatProperties* pProperties ) const + { + vkGetPhysicalDeviceSparseImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( samples ), static_cast( usage ), static_cast( tiling ), pPropertyCount, reinterpret_cast( pProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + std::vector getSparseImageFormatProperties( Format format, ImageType type, SampleCountFlagBits samples, ImageUsageFlags usage, ImageTiling tiling ) const + { + std::vector properties; + uint32_t propertyCount; + vkGetPhysicalDeviceSparseImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( samples ), static_cast( usage ), static_cast( tiling ), &propertyCount, nullptr ); + properties.resize( propertyCount ); + vkGetPhysicalDeviceSparseImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( samples ), static_cast( usage ), static_cast( tiling ), &propertyCount, reinterpret_cast( properties.data() ) ); + return properties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getDisplayPropertiesKHR( uint32_t* pPropertyCount, DisplayPropertiesKHR* pProperties ) const + { + return static_cast( vkGetPhysicalDeviceDisplayPropertiesKHR( m_physicalDevice, pPropertyCount, reinterpret_cast( pProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getDisplayPropertiesKHR() const + { + std::vector properties; + uint32_t propertyCount; + Result result; + do + { + result = static_cast( vkGetPhysicalDeviceDisplayPropertiesKHR( m_physicalDevice, &propertyCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && propertyCount ) + { + properties.resize( propertyCount ); + result = static_cast( vkGetPhysicalDeviceDisplayPropertiesKHR( m_physicalDevice, &propertyCount, reinterpret_cast( properties.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( propertyCount <= properties.size() ); + properties.resize( propertyCount ); + return createResultValue( result, properties, "vk::PhysicalDevice::getDisplayPropertiesKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getDisplayPlanePropertiesKHR( uint32_t* pPropertyCount, DisplayPlanePropertiesKHR* pProperties ) const + { + return static_cast( vkGetPhysicalDeviceDisplayPlanePropertiesKHR( m_physicalDevice, pPropertyCount, reinterpret_cast( pProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getDisplayPlanePropertiesKHR() const + { + std::vector properties; + uint32_t propertyCount; + Result result; + do + { + result = static_cast( vkGetPhysicalDeviceDisplayPlanePropertiesKHR( m_physicalDevice, &propertyCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && propertyCount ) + { + properties.resize( propertyCount ); + result = static_cast( vkGetPhysicalDeviceDisplayPlanePropertiesKHR( m_physicalDevice, &propertyCount, reinterpret_cast( properties.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( propertyCount <= properties.size() ); + properties.resize( propertyCount ); + return createResultValue( result, properties, "vk::PhysicalDevice::getDisplayPlanePropertiesKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getDisplayPlaneSupportedDisplaysKHR( uint32_t planeIndex, uint32_t* pDisplayCount, DisplayKHR* pDisplays ) const + { + return static_cast( vkGetDisplayPlaneSupportedDisplaysKHR( m_physicalDevice, planeIndex, pDisplayCount, reinterpret_cast( pDisplays ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getDisplayPlaneSupportedDisplaysKHR( uint32_t planeIndex ) const + { + std::vector displays; + uint32_t displayCount; + Result result; + do + { + result = static_cast( vkGetDisplayPlaneSupportedDisplaysKHR( m_physicalDevice, planeIndex, &displayCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && displayCount ) + { + displays.resize( displayCount ); + result = static_cast( vkGetDisplayPlaneSupportedDisplaysKHR( m_physicalDevice, planeIndex, &displayCount, reinterpret_cast( displays.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( displayCount <= displays.size() ); + displays.resize( displayCount ); + return createResultValue( result, displays, "vk::PhysicalDevice::getDisplayPlaneSupportedDisplaysKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getDisplayModePropertiesKHR( DisplayKHR display, uint32_t* pPropertyCount, DisplayModePropertiesKHR* pProperties ) const + { + return static_cast( vkGetDisplayModePropertiesKHR( m_physicalDevice, static_cast( display ), pPropertyCount, reinterpret_cast( pProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getDisplayModePropertiesKHR( DisplayKHR display ) const + { + std::vector properties; + uint32_t propertyCount; + Result result; + do + { + result = static_cast( vkGetDisplayModePropertiesKHR( m_physicalDevice, static_cast( display ), &propertyCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && propertyCount ) + { + properties.resize( propertyCount ); + result = static_cast( vkGetDisplayModePropertiesKHR( m_physicalDevice, static_cast( display ), &propertyCount, reinterpret_cast( properties.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( propertyCount <= properties.size() ); + properties.resize( propertyCount ); + return createResultValue( result, properties, "vk::PhysicalDevice::getDisplayModePropertiesKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createDisplayModeKHR( DisplayKHR display, const DisplayModeCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, DisplayModeKHR* pMode ) const + { + return static_cast( vkCreateDisplayModeKHR( m_physicalDevice, static_cast( display ), reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pMode ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createDisplayModeKHR( DisplayKHR display, const DisplayModeCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + DisplayModeKHR mode; + Result result = static_cast( vkCreateDisplayModeKHR( m_physicalDevice, static_cast( display ), reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &mode ) ) ); + return createResultValue( result, mode, "vk::PhysicalDevice::createDisplayModeKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getDisplayPlaneCapabilitiesKHR( DisplayModeKHR mode, uint32_t planeIndex, DisplayPlaneCapabilitiesKHR* pCapabilities ) const + { + return static_cast( vkGetDisplayPlaneCapabilitiesKHR( m_physicalDevice, static_cast( mode ), planeIndex, reinterpret_cast( pCapabilities ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type getDisplayPlaneCapabilitiesKHR( DisplayModeKHR mode, uint32_t planeIndex ) const + { + DisplayPlaneCapabilitiesKHR capabilities; + Result result = static_cast( vkGetDisplayPlaneCapabilitiesKHR( m_physicalDevice, static_cast( mode ), planeIndex, reinterpret_cast( &capabilities ) ) ); + return createResultValue( result, capabilities, "vk::PhysicalDevice::getDisplayPlaneCapabilitiesKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_MIR_KHR + Bool32 getMirPresentationSupportKHR( uint32_t queueFamilyIndex, MirConnection* connection ) const + { + return vkGetPhysicalDeviceMirPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, connection ); + } +#endif /*VK_USE_PLATFORM_MIR_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_MIR_KHR + Bool32 getMirPresentationSupportKHR( uint32_t queueFamilyIndex, MirConnection & connection ) const + { + return vkGetPhysicalDeviceMirPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &connection ); + } +#endif /*VK_USE_PLATFORM_MIR_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getSurfaceSupportKHR( uint32_t queueFamilyIndex, SurfaceKHR surface, Bool32* pSupported ) const + { + return static_cast( vkGetPhysicalDeviceSurfaceSupportKHR( m_physicalDevice, queueFamilyIndex, static_cast( surface ), pSupported ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type getSurfaceSupportKHR( uint32_t queueFamilyIndex, SurfaceKHR surface ) const + { + Bool32 supported; + Result result = static_cast( vkGetPhysicalDeviceSurfaceSupportKHR( m_physicalDevice, queueFamilyIndex, static_cast( surface ), &supported ) ); + return createResultValue( result, supported, "vk::PhysicalDevice::getSurfaceSupportKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getSurfaceCapabilitiesKHR( SurfaceKHR surface, SurfaceCapabilitiesKHR* pSurfaceCapabilities ) const + { + return static_cast( vkGetPhysicalDeviceSurfaceCapabilitiesKHR( m_physicalDevice, static_cast( surface ), reinterpret_cast( pSurfaceCapabilities ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type getSurfaceCapabilitiesKHR( SurfaceKHR surface ) const + { + SurfaceCapabilitiesKHR surfaceCapabilities; + Result result = static_cast( vkGetPhysicalDeviceSurfaceCapabilitiesKHR( m_physicalDevice, static_cast( surface ), reinterpret_cast( &surfaceCapabilities ) ) ); + return createResultValue( result, surfaceCapabilities, "vk::PhysicalDevice::getSurfaceCapabilitiesKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getSurfaceFormatsKHR( SurfaceKHR surface, uint32_t* pSurfaceFormatCount, SurfaceFormatKHR* pSurfaceFormats ) const + { + return static_cast( vkGetPhysicalDeviceSurfaceFormatsKHR( m_physicalDevice, static_cast( surface ), pSurfaceFormatCount, reinterpret_cast( pSurfaceFormats ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getSurfaceFormatsKHR( SurfaceKHR surface ) const + { + std::vector surfaceFormats; + uint32_t surfaceFormatCount; + Result result; + do + { + result = static_cast( vkGetPhysicalDeviceSurfaceFormatsKHR( m_physicalDevice, static_cast( surface ), &surfaceFormatCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && surfaceFormatCount ) + { + surfaceFormats.resize( surfaceFormatCount ); + result = static_cast( vkGetPhysicalDeviceSurfaceFormatsKHR( m_physicalDevice, static_cast( surface ), &surfaceFormatCount, reinterpret_cast( surfaceFormats.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( surfaceFormatCount <= surfaceFormats.size() ); + surfaceFormats.resize( surfaceFormatCount ); + return createResultValue( result, surfaceFormats, "vk::PhysicalDevice::getSurfaceFormatsKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getSurfacePresentModesKHR( SurfaceKHR surface, uint32_t* pPresentModeCount, PresentModeKHR* pPresentModes ) const + { + return static_cast( vkGetPhysicalDeviceSurfacePresentModesKHR( m_physicalDevice, static_cast( surface ), pPresentModeCount, reinterpret_cast( pPresentModes ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type getSurfacePresentModesKHR( SurfaceKHR surface ) const + { + std::vector presentModes; + uint32_t presentModeCount; + Result result; + do + { + result = static_cast( vkGetPhysicalDeviceSurfacePresentModesKHR( m_physicalDevice, static_cast( surface ), &presentModeCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && presentModeCount ) + { + presentModes.resize( presentModeCount ); + result = static_cast( vkGetPhysicalDeviceSurfacePresentModesKHR( m_physicalDevice, static_cast( surface ), &presentModeCount, reinterpret_cast( presentModes.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( presentModeCount <= presentModes.size() ); + presentModes.resize( presentModeCount ); + return createResultValue( result, presentModes, "vk::PhysicalDevice::getSurfacePresentModesKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + Bool32 getWaylandPresentationSupportKHR( uint32_t queueFamilyIndex, struct wl_display* display ) const + { + return vkGetPhysicalDeviceWaylandPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, display ); + } +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + Bool32 getWaylandPresentationSupportKHR( uint32_t queueFamilyIndex, struct wl_display & display ) const + { + return vkGetPhysicalDeviceWaylandPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &display ); + } +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_WIN32_KHR + Bool32 getWin32PresentationSupportKHR( uint32_t queueFamilyIndex ) const + { + return vkGetPhysicalDeviceWin32PresentationSupportKHR( m_physicalDevice, queueFamilyIndex ); + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_WIN32_KHR + Bool32 getWin32PresentationSupportKHR( uint32_t queueFamilyIndex ) const + { + return vkGetPhysicalDeviceWin32PresentationSupportKHR( m_physicalDevice, queueFamilyIndex ); + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_XLIB_KHR + Bool32 getXlibPresentationSupportKHR( uint32_t queueFamilyIndex, Display* dpy, VisualID visualID ) const + { + return vkGetPhysicalDeviceXlibPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, dpy, visualID ); + } +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_XLIB_KHR + Bool32 getXlibPresentationSupportKHR( uint32_t queueFamilyIndex, Display & dpy, VisualID visualID ) const + { + return vkGetPhysicalDeviceXlibPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &dpy, visualID ); + } +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_XCB_KHR + Bool32 getXcbPresentationSupportKHR( uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id ) const + { + return vkGetPhysicalDeviceXcbPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, connection, visual_id ); + } +#endif /*VK_USE_PLATFORM_XCB_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_XCB_KHR + Bool32 getXcbPresentationSupportKHR( uint32_t queueFamilyIndex, xcb_connection_t & connection, xcb_visualid_t visual_id ) const + { + return vkGetPhysicalDeviceXcbPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &connection, visual_id ); + } +#endif /*VK_USE_PLATFORM_XCB_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getExternalImageFormatPropertiesNV( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ExternalMemoryHandleTypeFlagsNV externalHandleType, ExternalImageFormatPropertiesNV* pExternalImageFormatProperties ) const + { + return static_cast( vkGetPhysicalDeviceExternalImageFormatPropertiesNV( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), static_cast( externalHandleType ), reinterpret_cast( pExternalImageFormatProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type getExternalImageFormatPropertiesNV( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ExternalMemoryHandleTypeFlagsNV externalHandleType ) const + { + ExternalImageFormatPropertiesNV externalImageFormatProperties; + Result result = static_cast( vkGetPhysicalDeviceExternalImageFormatPropertiesNV( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), static_cast( externalHandleType ), reinterpret_cast( &externalImageFormatProperties ) ) ); + return createResultValue( result, externalImageFormatProperties, "vk::PhysicalDevice::getExternalImageFormatPropertiesNV" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getGeneratedCommandsPropertiesNVX( DeviceGeneratedCommandsFeaturesNVX* pFeatures, DeviceGeneratedCommandsLimitsNVX* pLimits ) const + { + vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( m_physicalDevice, reinterpret_cast( pFeatures ), reinterpret_cast( pLimits ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void getGeneratedCommandsPropertiesNVX( DeviceGeneratedCommandsFeaturesNVX & features, DeviceGeneratedCommandsLimitsNVX & limits ) const + { + vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( m_physicalDevice, reinterpret_cast( &features ), reinterpret_cast( &limits ) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getFeatures2KHR( PhysicalDeviceFeatures2KHR* pFeatures ) const + { + vkGetPhysicalDeviceFeatures2KHR( m_physicalDevice, reinterpret_cast( pFeatures ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PhysicalDeviceFeatures2KHR getFeatures2KHR() const + { + PhysicalDeviceFeatures2KHR features; + vkGetPhysicalDeviceFeatures2KHR( m_physicalDevice, reinterpret_cast( &features ) ); + return features; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getProperties2KHR( PhysicalDeviceProperties2KHR* pProperties ) const + { + vkGetPhysicalDeviceProperties2KHR( m_physicalDevice, reinterpret_cast( pProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PhysicalDeviceProperties2KHR getProperties2KHR() const + { + PhysicalDeviceProperties2KHR properties; + vkGetPhysicalDeviceProperties2KHR( m_physicalDevice, reinterpret_cast( &properties ) ); + return properties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getFormatProperties2KHR( Format format, FormatProperties2KHR* pFormatProperties ) const + { + vkGetPhysicalDeviceFormatProperties2KHR( m_physicalDevice, static_cast( format ), reinterpret_cast( pFormatProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + FormatProperties2KHR getFormatProperties2KHR( Format format ) const + { + FormatProperties2KHR formatProperties; + vkGetPhysicalDeviceFormatProperties2KHR( m_physicalDevice, static_cast( format ), reinterpret_cast( &formatProperties ) ); + return formatProperties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getImageFormatProperties2KHR( const PhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, ImageFormatProperties2KHR* pImageFormatProperties ) const + { + return static_cast( vkGetPhysicalDeviceImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( pImageFormatInfo ), reinterpret_cast( pImageFormatProperties ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type getImageFormatProperties2KHR( const PhysicalDeviceImageFormatInfo2KHR & imageFormatInfo ) const + { + ImageFormatProperties2KHR imageFormatProperties; + Result result = static_cast( vkGetPhysicalDeviceImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( &imageFormatInfo ), reinterpret_cast( &imageFormatProperties ) ) ); + return createResultValue( result, imageFormatProperties, "vk::PhysicalDevice::getImageFormatProperties2KHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getQueueFamilyProperties2KHR( uint32_t* pQueueFamilyPropertyCount, QueueFamilyProperties2KHR* pQueueFamilyProperties ) const + { + vkGetPhysicalDeviceQueueFamilyProperties2KHR( m_physicalDevice, pQueueFamilyPropertyCount, reinterpret_cast( pQueueFamilyProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + std::vector getQueueFamilyProperties2KHR() const + { + std::vector queueFamilyProperties; + uint32_t queueFamilyPropertyCount; + vkGetPhysicalDeviceQueueFamilyProperties2KHR( m_physicalDevice, &queueFamilyPropertyCount, nullptr ); + queueFamilyProperties.resize( queueFamilyPropertyCount ); + vkGetPhysicalDeviceQueueFamilyProperties2KHR( m_physicalDevice, &queueFamilyPropertyCount, reinterpret_cast( queueFamilyProperties.data() ) ); + return queueFamilyProperties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getMemoryProperties2KHR( PhysicalDeviceMemoryProperties2KHR* pMemoryProperties ) const + { + vkGetPhysicalDeviceMemoryProperties2KHR( m_physicalDevice, reinterpret_cast( pMemoryProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PhysicalDeviceMemoryProperties2KHR getMemoryProperties2KHR() const + { + PhysicalDeviceMemoryProperties2KHR memoryProperties; + vkGetPhysicalDeviceMemoryProperties2KHR( m_physicalDevice, reinterpret_cast( &memoryProperties ) ); + return memoryProperties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void getSparseImageFormatProperties2KHR( const PhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, SparseImageFormatProperties2KHR* pProperties ) const + { + vkGetPhysicalDeviceSparseImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( pFormatInfo ), pPropertyCount, reinterpret_cast( pProperties ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + std::vector getSparseImageFormatProperties2KHR( const PhysicalDeviceSparseImageFormatInfo2KHR & formatInfo ) const + { + std::vector properties; + uint32_t propertyCount; + vkGetPhysicalDeviceSparseImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( &formatInfo ), &propertyCount, nullptr ); + properties.resize( propertyCount ); + vkGetPhysicalDeviceSparseImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( &formatInfo ), &propertyCount, reinterpret_cast( properties.data() ) ); + return properties; + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE + Result releaseDisplayEXT( DisplayKHR display ) const + { + return static_cast( vkReleaseDisplayEXT( m_physicalDevice, static_cast( display ) ) ); + } +#endif /*!VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type releaseDisplayEXT( DisplayKHR display ) const + { + Result result = static_cast( vkReleaseDisplayEXT( m_physicalDevice, static_cast( display ) ) ); + return createResultValue( result, "vk::PhysicalDevice::releaseDisplayEXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + Result acquireXlibDisplayEXT( Display* dpy, DisplayKHR display ) const + { + return static_cast( vkAcquireXlibDisplayEXT( m_physicalDevice, dpy, static_cast( display ) ) ); + } +#endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + ResultValueType::type acquireXlibDisplayEXT( DisplayKHR display ) const + { + Display dpy; + Result result = static_cast( vkAcquireXlibDisplayEXT( m_physicalDevice, &dpy, static_cast( display ) ) ); + return createResultValue( result, dpy, "vk::PhysicalDevice::acquireXlibDisplayEXT" ); + } +#endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + Result getRandROutputDisplayEXT( Display* dpy, RROutput rrOutput, DisplayKHR* pDisplay ) const + { + return static_cast( vkGetRandROutputDisplayEXT( m_physicalDevice, dpy, rrOutput, reinterpret_cast( pDisplay ) ) ); + } +#endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT + ResultValueType::type getRandROutputDisplayEXT( Display & dpy, RROutput rrOutput, DisplayKHR & display ) const + { + Result result = static_cast( vkGetRandROutputDisplayEXT( m_physicalDevice, &dpy, rrOutput, reinterpret_cast( &display ) ) ); + return createResultValue( result, "vk::PhysicalDevice::getRandROutputDisplayEXT" ); + } +#endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result getSurfaceCapabilities2EXT( SurfaceKHR surface, SurfaceCapabilities2EXT* pSurfaceCapabilities ) const + { + return static_cast( vkGetPhysicalDeviceSurfaceCapabilities2EXT( m_physicalDevice, static_cast( surface ), reinterpret_cast( pSurfaceCapabilities ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type getSurfaceCapabilities2EXT( SurfaceKHR surface ) const + { + SurfaceCapabilities2EXT surfaceCapabilities; + Result result = static_cast( vkGetPhysicalDeviceSurfaceCapabilities2EXT( m_physicalDevice, static_cast( surface ), reinterpret_cast( &surfaceCapabilities ) ) ); + return createResultValue( result, surfaceCapabilities, "vk::PhysicalDevice::getSurfaceCapabilities2EXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkPhysicalDevice() const + { + return m_physicalDevice; + } + + explicit operator bool() const + { + return m_physicalDevice != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_physicalDevice == VK_NULL_HANDLE; + } + + private: + VkPhysicalDevice m_physicalDevice; + }; + static_assert( sizeof( PhysicalDevice ) == sizeof( VkPhysicalDevice ), "handle and wrapper have different size!" ); + + class Instance + { + public: + Instance() + : m_instance(VK_NULL_HANDLE) + {} + +#if defined(VULKAN_HPP_TYPESAFE_CONVERSION) + Instance(VkInstance instance) + : m_instance(instance) + {} + + Instance& operator=(VkInstance instance) + { + m_instance = instance; + return *this; + } +#endif + + bool operator==(Instance const &rhs) const + { + return m_instance == rhs.m_instance; + } + + bool operator!=(Instance const &rhs) const + { + return m_instance != rhs.m_instance; + } + + bool operator<(Instance const &rhs) const + { + return m_instance < rhs.m_instance; + } + + void destroy( const AllocationCallbacks* pAllocator ) const + { + vkDestroyInstance( m_instance, reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroy( Optional allocator = nullptr ) const + { + vkDestroyInstance( m_instance, reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result enumeratePhysicalDevices( uint32_t* pPhysicalDeviceCount, PhysicalDevice* pPhysicalDevices ) const + { + return static_cast( vkEnumeratePhysicalDevices( m_instance, pPhysicalDeviceCount, reinterpret_cast( pPhysicalDevices ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + template > + typename ResultValueType>::type enumeratePhysicalDevices() const + { + std::vector physicalDevices; + uint32_t physicalDeviceCount; + Result result; + do + { + result = static_cast( vkEnumeratePhysicalDevices( m_instance, &physicalDeviceCount, nullptr ) ); + if ( ( result == Result::eSuccess ) && physicalDeviceCount ) + { + physicalDevices.resize( physicalDeviceCount ); + result = static_cast( vkEnumeratePhysicalDevices( m_instance, &physicalDeviceCount, reinterpret_cast( physicalDevices.data() ) ) ); + } + } while ( result == Result::eIncomplete ); + assert( physicalDeviceCount <= physicalDevices.size() ); + physicalDevices.resize( physicalDeviceCount ); + return createResultValue( result, physicalDevices, "vk::Instance::enumeratePhysicalDevices" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + PFN_vkVoidFunction getProcAddr( const char* pName ) const + { + return vkGetInstanceProcAddr( m_instance, pName ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + PFN_vkVoidFunction getProcAddr( const std::string & name ) const + { + return vkGetInstanceProcAddr( m_instance, name.c_str() ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_ANDROID_KHR + Result createAndroidSurfaceKHR( const AndroidSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateAndroidSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } +#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_ANDROID_KHR + ResultValueType::type createAndroidSurfaceKHR( const AndroidSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateAndroidSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createAndroidSurfaceKHR" ); + } +#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createDisplayPlaneSurfaceKHR( const DisplaySurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateDisplayPlaneSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createDisplayPlaneSurfaceKHR( const DisplaySurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateDisplayPlaneSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createDisplayPlaneSurfaceKHR" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_MIR_KHR + Result createMirSurfaceKHR( const MirSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateMirSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } +#endif /*VK_USE_PLATFORM_MIR_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_MIR_KHR + ResultValueType::type createMirSurfaceKHR( const MirSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateMirSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createMirSurfaceKHR" ); + } +#endif /*VK_USE_PLATFORM_MIR_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroySurfaceKHR( SurfaceKHR surface, const AllocationCallbacks* pAllocator ) const + { + vkDestroySurfaceKHR( m_instance, static_cast( surface ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroySurfaceKHR( SurfaceKHR surface, Optional allocator = nullptr ) const + { + vkDestroySurfaceKHR( m_instance, static_cast( surface ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_VI_NN + Result createViSurfaceNN( const ViSurfaceCreateInfoNN* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateViSurfaceNN( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } +#endif /*VK_USE_PLATFORM_VI_NN*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_VI_NN + ResultValueType::type createViSurfaceNN( const ViSurfaceCreateInfoNN & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateViSurfaceNN( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createViSurfaceNN" ); + } +#endif /*VK_USE_PLATFORM_VI_NN*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + Result createWaylandSurfaceKHR( const WaylandSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateWaylandSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + ResultValueType::type createWaylandSurfaceKHR( const WaylandSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateWaylandSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createWaylandSurfaceKHR" ); + } +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + Result createWin32SurfaceKHR( const Win32SurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateWin32SurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_WIN32_KHR + ResultValueType::type createWin32SurfaceKHR( const Win32SurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateWin32SurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createWin32SurfaceKHR" ); + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_XLIB_KHR + Result createXlibSurfaceKHR( const XlibSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateXlibSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_XLIB_KHR + ResultValueType::type createXlibSurfaceKHR( const XlibSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateXlibSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createXlibSurfaceKHR" ); + } +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#ifdef VK_USE_PLATFORM_XCB_KHR + Result createXcbSurfaceKHR( const XcbSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const + { + return static_cast( vkCreateXcbSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); + } +#endif /*VK_USE_PLATFORM_XCB_KHR*/ + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE +#ifdef VK_USE_PLATFORM_XCB_KHR + ResultValueType::type createXcbSurfaceKHR( const XcbSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const + { + SurfaceKHR surface; + Result result = static_cast( vkCreateXcbSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &surface ) ) ); + return createResultValue( result, surface, "vk::Instance::createXcbSurfaceKHR" ); + } +#endif /*VK_USE_PLATFORM_XCB_KHR*/ +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + Result createDebugReportCallbackEXT( const DebugReportCallbackCreateInfoEXT* pCreateInfo, const AllocationCallbacks* pAllocator, DebugReportCallbackEXT* pCallback ) const + { + return static_cast( vkCreateDebugReportCallbackEXT( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pCallback ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + ResultValueType::type createDebugReportCallbackEXT( const DebugReportCallbackCreateInfoEXT & createInfo, Optional allocator = nullptr ) const + { + DebugReportCallbackEXT callback; + Result result = static_cast( vkCreateDebugReportCallbackEXT( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &callback ) ) ); + return createResultValue( result, callback, "vk::Instance::createDebugReportCallbackEXT" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void destroyDebugReportCallbackEXT( DebugReportCallbackEXT callback, const AllocationCallbacks* pAllocator ) const + { + vkDestroyDebugReportCallbackEXT( m_instance, static_cast( callback ), reinterpret_cast( pAllocator ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void destroyDebugReportCallbackEXT( DebugReportCallbackEXT callback, Optional allocator = nullptr ) const + { + vkDestroyDebugReportCallbackEXT( m_instance, static_cast( callback ), reinterpret_cast( static_cast( allocator)) ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + void debugReportMessageEXT( DebugReportFlagsEXT flags, DebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage ) const + { + vkDebugReportMessageEXT( m_instance, static_cast( flags ), static_cast( objectType ), object, location, messageCode, pLayerPrefix, pMessage ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + void debugReportMessageEXT( DebugReportFlagsEXT flags, DebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const std::string & layerPrefix, const std::string & message ) const + { +#ifdef VULKAN_HPP_NO_EXCEPTIONS + assert( layerPrefix.size() == message.size() ); +#else + if ( layerPrefix.size() != message.size() ) + { + throw std::logic_error( "vk::Instance::debugReportMessageEXT: layerPrefix.size() != message.size()" ); + } +#endif // VULKAN_HPP_NO_EXCEPTIONS + vkDebugReportMessageEXT( m_instance, static_cast( flags ), static_cast( objectType ), object, location, messageCode, layerPrefix.c_str(), message.c_str() ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + +#if !defined(VULKAN_HPP_TYPESAFE_CONVERSION) + explicit +#endif + operator VkInstance() const + { + return m_instance; + } + + explicit operator bool() const + { + return m_instance != VK_NULL_HANDLE; + } + + bool operator!() const + { + return m_instance == VK_NULL_HANDLE; + } + + private: + VkInstance m_instance; + }; + static_assert( sizeof( Instance ) == sizeof( VkInstance ), "handle and wrapper have different size!" ); + + struct CmdProcessCommandsInfoNVX + { + CmdProcessCommandsInfoNVX( ObjectTableNVX objectTable_ = ObjectTableNVX(), IndirectCommandsLayoutNVX indirectCommandsLayout_ = IndirectCommandsLayoutNVX(), uint32_t indirectCommandsTokenCount_ = 0, const IndirectCommandsTokenNVX* pIndirectCommandsTokens_ = nullptr, uint32_t maxSequencesCount_ = 0, CommandBuffer targetCommandBuffer_ = CommandBuffer(), Buffer sequencesCountBuffer_ = Buffer(), DeviceSize sequencesCountOffset_ = 0, Buffer sequencesIndexBuffer_ = Buffer(), DeviceSize sequencesIndexOffset_ = 0 ) + : sType( StructureType::eCmdProcessCommandsInfoNVX ) + , pNext( nullptr ) + , objectTable( objectTable_ ) + , indirectCommandsLayout( indirectCommandsLayout_ ) + , indirectCommandsTokenCount( indirectCommandsTokenCount_ ) + , pIndirectCommandsTokens( pIndirectCommandsTokens_ ) + , maxSequencesCount( maxSequencesCount_ ) + , targetCommandBuffer( targetCommandBuffer_ ) + , sequencesCountBuffer( sequencesCountBuffer_ ) + , sequencesCountOffset( sequencesCountOffset_ ) + , sequencesIndexBuffer( sequencesIndexBuffer_ ) + , sequencesIndexOffset( sequencesIndexOffset_ ) + { + } + + CmdProcessCommandsInfoNVX( VkCmdProcessCommandsInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(CmdProcessCommandsInfoNVX) ); + } + + CmdProcessCommandsInfoNVX& operator=( VkCmdProcessCommandsInfoNVX const & rhs ) + { + memcpy( this, &rhs, sizeof(CmdProcessCommandsInfoNVX) ); + return *this; + } + + CmdProcessCommandsInfoNVX& setSType( StructureType sType_ ) + { + sType = sType_; + return *this; + } + + CmdProcessCommandsInfoNVX& setPNext( const void* pNext_ ) + { + pNext = pNext_; + return *this; + } + + CmdProcessCommandsInfoNVX& setObjectTable( ObjectTableNVX objectTable_ ) + { + objectTable = objectTable_; + return *this; + } + + CmdProcessCommandsInfoNVX& setIndirectCommandsLayout( IndirectCommandsLayoutNVX indirectCommandsLayout_ ) + { + indirectCommandsLayout = indirectCommandsLayout_; + return *this; + } + + CmdProcessCommandsInfoNVX& setIndirectCommandsTokenCount( uint32_t indirectCommandsTokenCount_ ) + { + indirectCommandsTokenCount = indirectCommandsTokenCount_; + return *this; + } + + CmdProcessCommandsInfoNVX& setPIndirectCommandsTokens( const IndirectCommandsTokenNVX* pIndirectCommandsTokens_ ) + { + pIndirectCommandsTokens = pIndirectCommandsTokens_; + return *this; + } + + CmdProcessCommandsInfoNVX& setMaxSequencesCount( uint32_t maxSequencesCount_ ) + { + maxSequencesCount = maxSequencesCount_; + return *this; + } + + CmdProcessCommandsInfoNVX& setTargetCommandBuffer( CommandBuffer targetCommandBuffer_ ) + { + targetCommandBuffer = targetCommandBuffer_; + return *this; + } + + CmdProcessCommandsInfoNVX& setSequencesCountBuffer( Buffer sequencesCountBuffer_ ) + { + sequencesCountBuffer = sequencesCountBuffer_; + return *this; + } + + CmdProcessCommandsInfoNVX& setSequencesCountOffset( DeviceSize sequencesCountOffset_ ) + { + sequencesCountOffset = sequencesCountOffset_; + return *this; + } + + CmdProcessCommandsInfoNVX& setSequencesIndexBuffer( Buffer sequencesIndexBuffer_ ) + { + sequencesIndexBuffer = sequencesIndexBuffer_; + return *this; + } + + CmdProcessCommandsInfoNVX& setSequencesIndexOffset( DeviceSize sequencesIndexOffset_ ) + { + sequencesIndexOffset = sequencesIndexOffset_; + return *this; + } + + operator const VkCmdProcessCommandsInfoNVX&() const + { + return *reinterpret_cast(this); + } + + bool operator==( CmdProcessCommandsInfoNVX const& rhs ) const + { + return ( sType == rhs.sType ) + && ( pNext == rhs.pNext ) + && ( objectTable == rhs.objectTable ) + && ( indirectCommandsLayout == rhs.indirectCommandsLayout ) + && ( indirectCommandsTokenCount == rhs.indirectCommandsTokenCount ) + && ( pIndirectCommandsTokens == rhs.pIndirectCommandsTokens ) + && ( maxSequencesCount == rhs.maxSequencesCount ) + && ( targetCommandBuffer == rhs.targetCommandBuffer ) + && ( sequencesCountBuffer == rhs.sequencesCountBuffer ) + && ( sequencesCountOffset == rhs.sequencesCountOffset ) + && ( sequencesIndexBuffer == rhs.sequencesIndexBuffer ) + && ( sequencesIndexOffset == rhs.sequencesIndexOffset ); + } + + bool operator!=( CmdProcessCommandsInfoNVX const& rhs ) const + { + return !operator==( rhs ); + } + + private: + StructureType sType; + + public: + const void* pNext; + ObjectTableNVX objectTable; + IndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t indirectCommandsTokenCount; + const IndirectCommandsTokenNVX* pIndirectCommandsTokens; + uint32_t maxSequencesCount; + CommandBuffer targetCommandBuffer; + Buffer sequencesCountBuffer; + DeviceSize sequencesCountOffset; + Buffer sequencesIndexBuffer; + DeviceSize sequencesIndexOffset; + }; + static_assert( sizeof( CmdProcessCommandsInfoNVX ) == sizeof( VkCmdProcessCommandsInfoNVX ), "struct and wrapper have different size!" ); + + VULKAN_HPP_INLINE Result createInstance( const InstanceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Instance* pInstance ) + { + return static_cast( vkCreateInstance( reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pInstance ) ) ); + } + +#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE + VULKAN_HPP_INLINE ResultValueType::type createInstance( const InstanceCreateInfo & createInfo, Optional allocator = nullptr ) + { + Instance instance; + Result result = static_cast( vkCreateInstance( reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator)), reinterpret_cast( &instance ) ) ); + return createResultValue( result, instance, "vk::createInstance" ); + } +#endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ + + VULKAN_HPP_INLINE std::string to_string(FramebufferCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(FramebufferCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(QueryPoolCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(QueryPoolCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(RenderPassCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(RenderPassCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(SamplerCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(SamplerCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineLayoutCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineLayoutCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineCacheCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineCacheCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineDepthStencilStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineDepthStencilStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineDynamicStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineDynamicStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineColorBlendStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineColorBlendStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineMultisampleStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineMultisampleStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineRasterizationStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineRasterizationStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineViewportStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineViewportStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineTessellationStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineTessellationStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineInputAssemblyStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineInputAssemblyStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineVertexInputStateCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineVertexInputStateCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineShaderStageCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineShaderStageCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(DescriptorSetLayoutCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(DescriptorSetLayoutCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(BufferViewCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(BufferViewCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(InstanceCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(InstanceCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(DeviceCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(DeviceCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(DeviceQueueCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(DeviceQueueCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(ImageViewCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(ImageViewCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(SemaphoreCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(SemaphoreCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(ShaderModuleCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(ShaderModuleCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(EventCreateFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(EventCreateFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(MemoryMapFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(MemoryMapFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(SubpassDescriptionFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(SubpassDescriptionFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(DescriptorPoolResetFlagBits) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(DescriptorPoolResetFlags) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(SwapchainCreateFlagBitsKHR) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(SwapchainCreateFlagsKHR) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(DisplayModeCreateFlagBitsKHR) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(DisplayModeCreateFlagsKHR) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(DisplaySurfaceCreateFlagBitsKHR) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(DisplaySurfaceCreateFlagsKHR) + { + return "{}"; + } + +#ifdef VK_USE_PLATFORM_ANDROID_KHR + VULKAN_HPP_INLINE std::string to_string(AndroidSurfaceCreateFlagBitsKHR) + { + return "(void)"; + } +#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + +#ifdef VK_USE_PLATFORM_ANDROID_KHR + VULKAN_HPP_INLINE std::string to_string(AndroidSurfaceCreateFlagsKHR) + { + return "{}"; + } +#endif /*VK_USE_PLATFORM_ANDROID_KHR*/ + +#ifdef VK_USE_PLATFORM_MIR_KHR + VULKAN_HPP_INLINE std::string to_string(MirSurfaceCreateFlagBitsKHR) + { + return "(void)"; + } +#endif /*VK_USE_PLATFORM_MIR_KHR*/ + +#ifdef VK_USE_PLATFORM_MIR_KHR + VULKAN_HPP_INLINE std::string to_string(MirSurfaceCreateFlagsKHR) + { + return "{}"; + } +#endif /*VK_USE_PLATFORM_MIR_KHR*/ + +#ifdef VK_USE_PLATFORM_VI_NN + VULKAN_HPP_INLINE std::string to_string(ViSurfaceCreateFlagBitsNN) + { + return "(void)"; + } +#endif /*VK_USE_PLATFORM_VI_NN*/ + +#ifdef VK_USE_PLATFORM_VI_NN + VULKAN_HPP_INLINE std::string to_string(ViSurfaceCreateFlagsNN) + { + return "{}"; + } +#endif /*VK_USE_PLATFORM_VI_NN*/ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + VULKAN_HPP_INLINE std::string to_string(WaylandSurfaceCreateFlagBitsKHR) + { + return "(void)"; + } +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + VULKAN_HPP_INLINE std::string to_string(WaylandSurfaceCreateFlagsKHR) + { + return "{}"; + } +#endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + VULKAN_HPP_INLINE std::string to_string(Win32SurfaceCreateFlagBitsKHR) + { + return "(void)"; + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifdef VK_USE_PLATFORM_WIN32_KHR + VULKAN_HPP_INLINE std::string to_string(Win32SurfaceCreateFlagsKHR) + { + return "{}"; + } +#endif /*VK_USE_PLATFORM_WIN32_KHR*/ + +#ifdef VK_USE_PLATFORM_XLIB_KHR + VULKAN_HPP_INLINE std::string to_string(XlibSurfaceCreateFlagBitsKHR) + { + return "(void)"; + } +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ + +#ifdef VK_USE_PLATFORM_XLIB_KHR + VULKAN_HPP_INLINE std::string to_string(XlibSurfaceCreateFlagsKHR) + { + return "{}"; + } +#endif /*VK_USE_PLATFORM_XLIB_KHR*/ + +#ifdef VK_USE_PLATFORM_XCB_KHR + VULKAN_HPP_INLINE std::string to_string(XcbSurfaceCreateFlagBitsKHR) + { + return "(void)"; + } +#endif /*VK_USE_PLATFORM_XCB_KHR*/ + +#ifdef VK_USE_PLATFORM_XCB_KHR + VULKAN_HPP_INLINE std::string to_string(XcbSurfaceCreateFlagsKHR) + { + return "{}"; + } +#endif /*VK_USE_PLATFORM_XCB_KHR*/ + + VULKAN_HPP_INLINE std::string to_string(CommandPoolTrimFlagBitsKHR) + { + return "(void)"; + } + + VULKAN_HPP_INLINE std::string to_string(CommandPoolTrimFlagsKHR) + { + return "{}"; + } + + VULKAN_HPP_INLINE std::string to_string(ImageLayout value) + { + switch (value) + { + case ImageLayout::eUndefined: return "Undefined"; + case ImageLayout::eGeneral: return "General"; + case ImageLayout::eColorAttachmentOptimal: return "ColorAttachmentOptimal"; + case ImageLayout::eDepthStencilAttachmentOptimal: return "DepthStencilAttachmentOptimal"; + case ImageLayout::eDepthStencilReadOnlyOptimal: return "DepthStencilReadOnlyOptimal"; + case ImageLayout::eShaderReadOnlyOptimal: return "ShaderReadOnlyOptimal"; + case ImageLayout::eTransferSrcOptimal: return "TransferSrcOptimal"; + case ImageLayout::eTransferDstOptimal: return "TransferDstOptimal"; + case ImageLayout::ePreinitialized: return "Preinitialized"; + case ImageLayout::ePresentSrcKHR: return "PresentSrcKHR"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(AttachmentLoadOp value) + { + switch (value) + { + case AttachmentLoadOp::eLoad: return "Load"; + case AttachmentLoadOp::eClear: return "Clear"; + case AttachmentLoadOp::eDontCare: return "DontCare"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(AttachmentStoreOp value) + { + switch (value) + { + case AttachmentStoreOp::eStore: return "Store"; + case AttachmentStoreOp::eDontCare: return "DontCare"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ImageType value) + { + switch (value) + { + case ImageType::e1D: return "1D"; + case ImageType::e2D: return "2D"; + case ImageType::e3D: return "3D"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ImageTiling value) + { + switch (value) + { + case ImageTiling::eOptimal: return "Optimal"; + case ImageTiling::eLinear: return "Linear"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ImageViewType value) + { + switch (value) + { + case ImageViewType::e1D: return "1D"; + case ImageViewType::e2D: return "2D"; + case ImageViewType::e3D: return "3D"; + case ImageViewType::eCube: return "Cube"; + case ImageViewType::e1DArray: return "1DArray"; + case ImageViewType::e2DArray: return "2DArray"; + case ImageViewType::eCubeArray: return "CubeArray"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CommandBufferLevel value) + { + switch (value) + { + case CommandBufferLevel::ePrimary: return "Primary"; + case CommandBufferLevel::eSecondary: return "Secondary"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ComponentSwizzle value) + { + switch (value) + { + case ComponentSwizzle::eIdentity: return "Identity"; + case ComponentSwizzle::eZero: return "Zero"; + case ComponentSwizzle::eOne: return "One"; + case ComponentSwizzle::eR: return "R"; + case ComponentSwizzle::eG: return "G"; + case ComponentSwizzle::eB: return "B"; + case ComponentSwizzle::eA: return "A"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DescriptorType value) + { + switch (value) + { + case DescriptorType::eSampler: return "Sampler"; + case DescriptorType::eCombinedImageSampler: return "CombinedImageSampler"; + case DescriptorType::eSampledImage: return "SampledImage"; + case DescriptorType::eStorageImage: return "StorageImage"; + case DescriptorType::eUniformTexelBuffer: return "UniformTexelBuffer"; + case DescriptorType::eStorageTexelBuffer: return "StorageTexelBuffer"; + case DescriptorType::eUniformBuffer: return "UniformBuffer"; + case DescriptorType::eStorageBuffer: return "StorageBuffer"; + case DescriptorType::eUniformBufferDynamic: return "UniformBufferDynamic"; + case DescriptorType::eStorageBufferDynamic: return "StorageBufferDynamic"; + case DescriptorType::eInputAttachment: return "InputAttachment"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(QueryType value) + { + switch (value) + { + case QueryType::eOcclusion: return "Occlusion"; + case QueryType::ePipelineStatistics: return "PipelineStatistics"; + case QueryType::eTimestamp: return "Timestamp"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(BorderColor value) + { + switch (value) + { + case BorderColor::eFloatTransparentBlack: return "FloatTransparentBlack"; + case BorderColor::eIntTransparentBlack: return "IntTransparentBlack"; + case BorderColor::eFloatOpaqueBlack: return "FloatOpaqueBlack"; + case BorderColor::eIntOpaqueBlack: return "IntOpaqueBlack"; + case BorderColor::eFloatOpaqueWhite: return "FloatOpaqueWhite"; + case BorderColor::eIntOpaqueWhite: return "IntOpaqueWhite"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(PipelineBindPoint value) + { + switch (value) + { + case PipelineBindPoint::eGraphics: return "Graphics"; + case PipelineBindPoint::eCompute: return "Compute"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(PipelineCacheHeaderVersion value) + { + switch (value) + { + case PipelineCacheHeaderVersion::eOne: return "One"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(PrimitiveTopology value) + { + switch (value) + { + case PrimitiveTopology::ePointList: return "PointList"; + case PrimitiveTopology::eLineList: return "LineList"; + case PrimitiveTopology::eLineStrip: return "LineStrip"; + case PrimitiveTopology::eTriangleList: return "TriangleList"; + case PrimitiveTopology::eTriangleStrip: return "TriangleStrip"; + case PrimitiveTopology::eTriangleFan: return "TriangleFan"; + case PrimitiveTopology::eLineListWithAdjacency: return "LineListWithAdjacency"; + case PrimitiveTopology::eLineStripWithAdjacency: return "LineStripWithAdjacency"; + case PrimitiveTopology::eTriangleListWithAdjacency: return "TriangleListWithAdjacency"; + case PrimitiveTopology::eTriangleStripWithAdjacency: return "TriangleStripWithAdjacency"; + case PrimitiveTopology::ePatchList: return "PatchList"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SharingMode value) + { + switch (value) + { + case SharingMode::eExclusive: return "Exclusive"; + case SharingMode::eConcurrent: return "Concurrent"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(IndexType value) + { + switch (value) + { + case IndexType::eUint16: return "Uint16"; + case IndexType::eUint32: return "Uint32"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(Filter value) + { + switch (value) + { + case Filter::eNearest: return "Nearest"; + case Filter::eLinear: return "Linear"; + case Filter::eCubicIMG: return "CubicIMG"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SamplerMipmapMode value) + { + switch (value) + { + case SamplerMipmapMode::eNearest: return "Nearest"; + case SamplerMipmapMode::eLinear: return "Linear"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SamplerAddressMode value) + { + switch (value) + { + case SamplerAddressMode::eRepeat: return "Repeat"; + case SamplerAddressMode::eMirroredRepeat: return "MirroredRepeat"; + case SamplerAddressMode::eClampToEdge: return "ClampToEdge"; + case SamplerAddressMode::eClampToBorder: return "ClampToBorder"; + case SamplerAddressMode::eMirrorClampToEdge: return "MirrorClampToEdge"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CompareOp value) + { + switch (value) + { + case CompareOp::eNever: return "Never"; + case CompareOp::eLess: return "Less"; + case CompareOp::eEqual: return "Equal"; + case CompareOp::eLessOrEqual: return "LessOrEqual"; + case CompareOp::eGreater: return "Greater"; + case CompareOp::eNotEqual: return "NotEqual"; + case CompareOp::eGreaterOrEqual: return "GreaterOrEqual"; + case CompareOp::eAlways: return "Always"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(PolygonMode value) + { + switch (value) + { + case PolygonMode::eFill: return "Fill"; + case PolygonMode::eLine: return "Line"; + case PolygonMode::ePoint: return "Point"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CullModeFlagBits value) + { + switch (value) + { + case CullModeFlagBits::eNone: return "None"; + case CullModeFlagBits::eFront: return "Front"; + case CullModeFlagBits::eBack: return "Back"; + case CullModeFlagBits::eFrontAndBack: return "FrontAndBack"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CullModeFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & CullModeFlagBits::eNone) result += "None | "; + if (value & CullModeFlagBits::eFront) result += "Front | "; + if (value & CullModeFlagBits::eBack) result += "Back | "; + if (value & CullModeFlagBits::eFrontAndBack) result += "FrontAndBack | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(FrontFace value) + { + switch (value) + { + case FrontFace::eCounterClockwise: return "CounterClockwise"; + case FrontFace::eClockwise: return "Clockwise"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(BlendFactor value) + { + switch (value) + { + case BlendFactor::eZero: return "Zero"; + case BlendFactor::eOne: return "One"; + case BlendFactor::eSrcColor: return "SrcColor"; + case BlendFactor::eOneMinusSrcColor: return "OneMinusSrcColor"; + case BlendFactor::eDstColor: return "DstColor"; + case BlendFactor::eOneMinusDstColor: return "OneMinusDstColor"; + case BlendFactor::eSrcAlpha: return "SrcAlpha"; + case BlendFactor::eOneMinusSrcAlpha: return "OneMinusSrcAlpha"; + case BlendFactor::eDstAlpha: return "DstAlpha"; + case BlendFactor::eOneMinusDstAlpha: return "OneMinusDstAlpha"; + case BlendFactor::eConstantColor: return "ConstantColor"; + case BlendFactor::eOneMinusConstantColor: return "OneMinusConstantColor"; + case BlendFactor::eConstantAlpha: return "ConstantAlpha"; + case BlendFactor::eOneMinusConstantAlpha: return "OneMinusConstantAlpha"; + case BlendFactor::eSrcAlphaSaturate: return "SrcAlphaSaturate"; + case BlendFactor::eSrc1Color: return "Src1Color"; + case BlendFactor::eOneMinusSrc1Color: return "OneMinusSrc1Color"; + case BlendFactor::eSrc1Alpha: return "Src1Alpha"; + case BlendFactor::eOneMinusSrc1Alpha: return "OneMinusSrc1Alpha"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(BlendOp value) + { + switch (value) + { + case BlendOp::eAdd: return "Add"; + case BlendOp::eSubtract: return "Subtract"; + case BlendOp::eReverseSubtract: return "ReverseSubtract"; + case BlendOp::eMin: return "Min"; + case BlendOp::eMax: return "Max"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(StencilOp value) + { + switch (value) + { + case StencilOp::eKeep: return "Keep"; + case StencilOp::eZero: return "Zero"; + case StencilOp::eReplace: return "Replace"; + case StencilOp::eIncrementAndClamp: return "IncrementAndClamp"; + case StencilOp::eDecrementAndClamp: return "DecrementAndClamp"; + case StencilOp::eInvert: return "Invert"; + case StencilOp::eIncrementAndWrap: return "IncrementAndWrap"; + case StencilOp::eDecrementAndWrap: return "DecrementAndWrap"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(LogicOp value) + { + switch (value) + { + case LogicOp::eClear: return "Clear"; + case LogicOp::eAnd: return "And"; + case LogicOp::eAndReverse: return "AndReverse"; + case LogicOp::eCopy: return "Copy"; + case LogicOp::eAndInverted: return "AndInverted"; + case LogicOp::eNoOp: return "NoOp"; + case LogicOp::eXor: return "Xor"; + case LogicOp::eOr: return "Or"; + case LogicOp::eNor: return "Nor"; + case LogicOp::eEquivalent: return "Equivalent"; + case LogicOp::eInvert: return "Invert"; + case LogicOp::eOrReverse: return "OrReverse"; + case LogicOp::eCopyInverted: return "CopyInverted"; + case LogicOp::eOrInverted: return "OrInverted"; + case LogicOp::eNand: return "Nand"; + case LogicOp::eSet: return "Set"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(InternalAllocationType value) + { + switch (value) + { + case InternalAllocationType::eExecutable: return "Executable"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SystemAllocationScope value) + { + switch (value) + { + case SystemAllocationScope::eCommand: return "Command"; + case SystemAllocationScope::eObject: return "Object"; + case SystemAllocationScope::eCache: return "Cache"; + case SystemAllocationScope::eDevice: return "Device"; + case SystemAllocationScope::eInstance: return "Instance"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(PhysicalDeviceType value) + { + switch (value) + { + case PhysicalDeviceType::eOther: return "Other"; + case PhysicalDeviceType::eIntegratedGpu: return "IntegratedGpu"; + case PhysicalDeviceType::eDiscreteGpu: return "DiscreteGpu"; + case PhysicalDeviceType::eVirtualGpu: return "VirtualGpu"; + case PhysicalDeviceType::eCpu: return "Cpu"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(VertexInputRate value) + { + switch (value) + { + case VertexInputRate::eVertex: return "Vertex"; + case VertexInputRate::eInstance: return "Instance"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(Format value) + { + switch (value) + { + case Format::eUndefined: return "Undefined"; + case Format::eR4G4UnormPack8: return "R4G4UnormPack8"; + case Format::eR4G4B4A4UnormPack16: return "R4G4B4A4UnormPack16"; + case Format::eB4G4R4A4UnormPack16: return "B4G4R4A4UnormPack16"; + case Format::eR5G6B5UnormPack16: return "R5G6B5UnormPack16"; + case Format::eB5G6R5UnormPack16: return "B5G6R5UnormPack16"; + case Format::eR5G5B5A1UnormPack16: return "R5G5B5A1UnormPack16"; + case Format::eB5G5R5A1UnormPack16: return "B5G5R5A1UnormPack16"; + case Format::eA1R5G5B5UnormPack16: return "A1R5G5B5UnormPack16"; + case Format::eR8Unorm: return "R8Unorm"; + case Format::eR8Snorm: return "R8Snorm"; + case Format::eR8Uscaled: return "R8Uscaled"; + case Format::eR8Sscaled: return "R8Sscaled"; + case Format::eR8Uint: return "R8Uint"; + case Format::eR8Sint: return "R8Sint"; + case Format::eR8Srgb: return "R8Srgb"; + case Format::eR8G8Unorm: return "R8G8Unorm"; + case Format::eR8G8Snorm: return "R8G8Snorm"; + case Format::eR8G8Uscaled: return "R8G8Uscaled"; + case Format::eR8G8Sscaled: return "R8G8Sscaled"; + case Format::eR8G8Uint: return "R8G8Uint"; + case Format::eR8G8Sint: return "R8G8Sint"; + case Format::eR8G8Srgb: return "R8G8Srgb"; + case Format::eR8G8B8Unorm: return "R8G8B8Unorm"; + case Format::eR8G8B8Snorm: return "R8G8B8Snorm"; + case Format::eR8G8B8Uscaled: return "R8G8B8Uscaled"; + case Format::eR8G8B8Sscaled: return "R8G8B8Sscaled"; + case Format::eR8G8B8Uint: return "R8G8B8Uint"; + case Format::eR8G8B8Sint: return "R8G8B8Sint"; + case Format::eR8G8B8Srgb: return "R8G8B8Srgb"; + case Format::eB8G8R8Unorm: return "B8G8R8Unorm"; + case Format::eB8G8R8Snorm: return "B8G8R8Snorm"; + case Format::eB8G8R8Uscaled: return "B8G8R8Uscaled"; + case Format::eB8G8R8Sscaled: return "B8G8R8Sscaled"; + case Format::eB8G8R8Uint: return "B8G8R8Uint"; + case Format::eB8G8R8Sint: return "B8G8R8Sint"; + case Format::eB8G8R8Srgb: return "B8G8R8Srgb"; + case Format::eR8G8B8A8Unorm: return "R8G8B8A8Unorm"; + case Format::eR8G8B8A8Snorm: return "R8G8B8A8Snorm"; + case Format::eR8G8B8A8Uscaled: return "R8G8B8A8Uscaled"; + case Format::eR8G8B8A8Sscaled: return "R8G8B8A8Sscaled"; + case Format::eR8G8B8A8Uint: return "R8G8B8A8Uint"; + case Format::eR8G8B8A8Sint: return "R8G8B8A8Sint"; + case Format::eR8G8B8A8Srgb: return "R8G8B8A8Srgb"; + case Format::eB8G8R8A8Unorm: return "B8G8R8A8Unorm"; + case Format::eB8G8R8A8Snorm: return "B8G8R8A8Snorm"; + case Format::eB8G8R8A8Uscaled: return "B8G8R8A8Uscaled"; + case Format::eB8G8R8A8Sscaled: return "B8G8R8A8Sscaled"; + case Format::eB8G8R8A8Uint: return "B8G8R8A8Uint"; + case Format::eB8G8R8A8Sint: return "B8G8R8A8Sint"; + case Format::eB8G8R8A8Srgb: return "B8G8R8A8Srgb"; + case Format::eA8B8G8R8UnormPack32: return "A8B8G8R8UnormPack32"; + case Format::eA8B8G8R8SnormPack32: return "A8B8G8R8SnormPack32"; + case Format::eA8B8G8R8UscaledPack32: return "A8B8G8R8UscaledPack32"; + case Format::eA8B8G8R8SscaledPack32: return "A8B8G8R8SscaledPack32"; + case Format::eA8B8G8R8UintPack32: return "A8B8G8R8UintPack32"; + case Format::eA8B8G8R8SintPack32: return "A8B8G8R8SintPack32"; + case Format::eA8B8G8R8SrgbPack32: return "A8B8G8R8SrgbPack32"; + case Format::eA2R10G10B10UnormPack32: return "A2R10G10B10UnormPack32"; + case Format::eA2R10G10B10SnormPack32: return "A2R10G10B10SnormPack32"; + case Format::eA2R10G10B10UscaledPack32: return "A2R10G10B10UscaledPack32"; + case Format::eA2R10G10B10SscaledPack32: return "A2R10G10B10SscaledPack32"; + case Format::eA2R10G10B10UintPack32: return "A2R10G10B10UintPack32"; + case Format::eA2R10G10B10SintPack32: return "A2R10G10B10SintPack32"; + case Format::eA2B10G10R10UnormPack32: return "A2B10G10R10UnormPack32"; + case Format::eA2B10G10R10SnormPack32: return "A2B10G10R10SnormPack32"; + case Format::eA2B10G10R10UscaledPack32: return "A2B10G10R10UscaledPack32"; + case Format::eA2B10G10R10SscaledPack32: return "A2B10G10R10SscaledPack32"; + case Format::eA2B10G10R10UintPack32: return "A2B10G10R10UintPack32"; + case Format::eA2B10G10R10SintPack32: return "A2B10G10R10SintPack32"; + case Format::eR16Unorm: return "R16Unorm"; + case Format::eR16Snorm: return "R16Snorm"; + case Format::eR16Uscaled: return "R16Uscaled"; + case Format::eR16Sscaled: return "R16Sscaled"; + case Format::eR16Uint: return "R16Uint"; + case Format::eR16Sint: return "R16Sint"; + case Format::eR16Sfloat: return "R16Sfloat"; + case Format::eR16G16Unorm: return "R16G16Unorm"; + case Format::eR16G16Snorm: return "R16G16Snorm"; + case Format::eR16G16Uscaled: return "R16G16Uscaled"; + case Format::eR16G16Sscaled: return "R16G16Sscaled"; + case Format::eR16G16Uint: return "R16G16Uint"; + case Format::eR16G16Sint: return "R16G16Sint"; + case Format::eR16G16Sfloat: return "R16G16Sfloat"; + case Format::eR16G16B16Unorm: return "R16G16B16Unorm"; + case Format::eR16G16B16Snorm: return "R16G16B16Snorm"; + case Format::eR16G16B16Uscaled: return "R16G16B16Uscaled"; + case Format::eR16G16B16Sscaled: return "R16G16B16Sscaled"; + case Format::eR16G16B16Uint: return "R16G16B16Uint"; + case Format::eR16G16B16Sint: return "R16G16B16Sint"; + case Format::eR16G16B16Sfloat: return "R16G16B16Sfloat"; + case Format::eR16G16B16A16Unorm: return "R16G16B16A16Unorm"; + case Format::eR16G16B16A16Snorm: return "R16G16B16A16Snorm"; + case Format::eR16G16B16A16Uscaled: return "R16G16B16A16Uscaled"; + case Format::eR16G16B16A16Sscaled: return "R16G16B16A16Sscaled"; + case Format::eR16G16B16A16Uint: return "R16G16B16A16Uint"; + case Format::eR16G16B16A16Sint: return "R16G16B16A16Sint"; + case Format::eR16G16B16A16Sfloat: return "R16G16B16A16Sfloat"; + case Format::eR32Uint: return "R32Uint"; + case Format::eR32Sint: return "R32Sint"; + case Format::eR32Sfloat: return "R32Sfloat"; + case Format::eR32G32Uint: return "R32G32Uint"; + case Format::eR32G32Sint: return "R32G32Sint"; + case Format::eR32G32Sfloat: return "R32G32Sfloat"; + case Format::eR32G32B32Uint: return "R32G32B32Uint"; + case Format::eR32G32B32Sint: return "R32G32B32Sint"; + case Format::eR32G32B32Sfloat: return "R32G32B32Sfloat"; + case Format::eR32G32B32A32Uint: return "R32G32B32A32Uint"; + case Format::eR32G32B32A32Sint: return "R32G32B32A32Sint"; + case Format::eR32G32B32A32Sfloat: return "R32G32B32A32Sfloat"; + case Format::eR64Uint: return "R64Uint"; + case Format::eR64Sint: return "R64Sint"; + case Format::eR64Sfloat: return "R64Sfloat"; + case Format::eR64G64Uint: return "R64G64Uint"; + case Format::eR64G64Sint: return "R64G64Sint"; + case Format::eR64G64Sfloat: return "R64G64Sfloat"; + case Format::eR64G64B64Uint: return "R64G64B64Uint"; + case Format::eR64G64B64Sint: return "R64G64B64Sint"; + case Format::eR64G64B64Sfloat: return "R64G64B64Sfloat"; + case Format::eR64G64B64A64Uint: return "R64G64B64A64Uint"; + case Format::eR64G64B64A64Sint: return "R64G64B64A64Sint"; + case Format::eR64G64B64A64Sfloat: return "R64G64B64A64Sfloat"; + case Format::eB10G11R11UfloatPack32: return "B10G11R11UfloatPack32"; + case Format::eE5B9G9R9UfloatPack32: return "E5B9G9R9UfloatPack32"; + case Format::eD16Unorm: return "D16Unorm"; + case Format::eX8D24UnormPack32: return "X8D24UnormPack32"; + case Format::eD32Sfloat: return "D32Sfloat"; + case Format::eS8Uint: return "S8Uint"; + case Format::eD16UnormS8Uint: return "D16UnormS8Uint"; + case Format::eD24UnormS8Uint: return "D24UnormS8Uint"; + case Format::eD32SfloatS8Uint: return "D32SfloatS8Uint"; + case Format::eBc1RgbUnormBlock: return "Bc1RgbUnormBlock"; + case Format::eBc1RgbSrgbBlock: return "Bc1RgbSrgbBlock"; + case Format::eBc1RgbaUnormBlock: return "Bc1RgbaUnormBlock"; + case Format::eBc1RgbaSrgbBlock: return "Bc1RgbaSrgbBlock"; + case Format::eBc2UnormBlock: return "Bc2UnormBlock"; + case Format::eBc2SrgbBlock: return "Bc2SrgbBlock"; + case Format::eBc3UnormBlock: return "Bc3UnormBlock"; + case Format::eBc3SrgbBlock: return "Bc3SrgbBlock"; + case Format::eBc4UnormBlock: return "Bc4UnormBlock"; + case Format::eBc4SnormBlock: return "Bc4SnormBlock"; + case Format::eBc5UnormBlock: return "Bc5UnormBlock"; + case Format::eBc5SnormBlock: return "Bc5SnormBlock"; + case Format::eBc6HUfloatBlock: return "Bc6HUfloatBlock"; + case Format::eBc6HSfloatBlock: return "Bc6HSfloatBlock"; + case Format::eBc7UnormBlock: return "Bc7UnormBlock"; + case Format::eBc7SrgbBlock: return "Bc7SrgbBlock"; + case Format::eEtc2R8G8B8UnormBlock: return "Etc2R8G8B8UnormBlock"; + case Format::eEtc2R8G8B8SrgbBlock: return "Etc2R8G8B8SrgbBlock"; + case Format::eEtc2R8G8B8A1UnormBlock: return "Etc2R8G8B8A1UnormBlock"; + case Format::eEtc2R8G8B8A1SrgbBlock: return "Etc2R8G8B8A1SrgbBlock"; + case Format::eEtc2R8G8B8A8UnormBlock: return "Etc2R8G8B8A8UnormBlock"; + case Format::eEtc2R8G8B8A8SrgbBlock: return "Etc2R8G8B8A8SrgbBlock"; + case Format::eEacR11UnormBlock: return "EacR11UnormBlock"; + case Format::eEacR11SnormBlock: return "EacR11SnormBlock"; + case Format::eEacR11G11UnormBlock: return "EacR11G11UnormBlock"; + case Format::eEacR11G11SnormBlock: return "EacR11G11SnormBlock"; + case Format::eAstc4x4UnormBlock: return "Astc4x4UnormBlock"; + case Format::eAstc4x4SrgbBlock: return "Astc4x4SrgbBlock"; + case Format::eAstc5x4UnormBlock: return "Astc5x4UnormBlock"; + case Format::eAstc5x4SrgbBlock: return "Astc5x4SrgbBlock"; + case Format::eAstc5x5UnormBlock: return "Astc5x5UnormBlock"; + case Format::eAstc5x5SrgbBlock: return "Astc5x5SrgbBlock"; + case Format::eAstc6x5UnormBlock: return "Astc6x5UnormBlock"; + case Format::eAstc6x5SrgbBlock: return "Astc6x5SrgbBlock"; + case Format::eAstc6x6UnormBlock: return "Astc6x6UnormBlock"; + case Format::eAstc6x6SrgbBlock: return "Astc6x6SrgbBlock"; + case Format::eAstc8x5UnormBlock: return "Astc8x5UnormBlock"; + case Format::eAstc8x5SrgbBlock: return "Astc8x5SrgbBlock"; + case Format::eAstc8x6UnormBlock: return "Astc8x6UnormBlock"; + case Format::eAstc8x6SrgbBlock: return "Astc8x6SrgbBlock"; + case Format::eAstc8x8UnormBlock: return "Astc8x8UnormBlock"; + case Format::eAstc8x8SrgbBlock: return "Astc8x8SrgbBlock"; + case Format::eAstc10x5UnormBlock: return "Astc10x5UnormBlock"; + case Format::eAstc10x5SrgbBlock: return "Astc10x5SrgbBlock"; + case Format::eAstc10x6UnormBlock: return "Astc10x6UnormBlock"; + case Format::eAstc10x6SrgbBlock: return "Astc10x6SrgbBlock"; + case Format::eAstc10x8UnormBlock: return "Astc10x8UnormBlock"; + case Format::eAstc10x8SrgbBlock: return "Astc10x8SrgbBlock"; + case Format::eAstc10x10UnormBlock: return "Astc10x10UnormBlock"; + case Format::eAstc10x10SrgbBlock: return "Astc10x10SrgbBlock"; + case Format::eAstc12x10UnormBlock: return "Astc12x10UnormBlock"; + case Format::eAstc12x10SrgbBlock: return "Astc12x10SrgbBlock"; + case Format::eAstc12x12UnormBlock: return "Astc12x12UnormBlock"; + case Format::eAstc12x12SrgbBlock: return "Astc12x12SrgbBlock"; + case Format::ePvrtc12BppUnormBlockIMG: return "Pvrtc12BppUnormBlockIMG"; + case Format::ePvrtc14BppUnormBlockIMG: return "Pvrtc14BppUnormBlockIMG"; + case Format::ePvrtc22BppUnormBlockIMG: return "Pvrtc22BppUnormBlockIMG"; + case Format::ePvrtc24BppUnormBlockIMG: return "Pvrtc24BppUnormBlockIMG"; + case Format::ePvrtc12BppSrgbBlockIMG: return "Pvrtc12BppSrgbBlockIMG"; + case Format::ePvrtc14BppSrgbBlockIMG: return "Pvrtc14BppSrgbBlockIMG"; + case Format::ePvrtc22BppSrgbBlockIMG: return "Pvrtc22BppSrgbBlockIMG"; + case Format::ePvrtc24BppSrgbBlockIMG: return "Pvrtc24BppSrgbBlockIMG"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(StructureType value) + { + switch (value) + { + case StructureType::eApplicationInfo: return "ApplicationInfo"; + case StructureType::eInstanceCreateInfo: return "InstanceCreateInfo"; + case StructureType::eDeviceQueueCreateInfo: return "DeviceQueueCreateInfo"; + case StructureType::eDeviceCreateInfo: return "DeviceCreateInfo"; + case StructureType::eSubmitInfo: return "SubmitInfo"; + case StructureType::eMemoryAllocateInfo: return "MemoryAllocateInfo"; + case StructureType::eMappedMemoryRange: return "MappedMemoryRange"; + case StructureType::eBindSparseInfo: return "BindSparseInfo"; + case StructureType::eFenceCreateInfo: return "FenceCreateInfo"; + case StructureType::eSemaphoreCreateInfo: return "SemaphoreCreateInfo"; + case StructureType::eEventCreateInfo: return "EventCreateInfo"; + case StructureType::eQueryPoolCreateInfo: return "QueryPoolCreateInfo"; + case StructureType::eBufferCreateInfo: return "BufferCreateInfo"; + case StructureType::eBufferViewCreateInfo: return "BufferViewCreateInfo"; + case StructureType::eImageCreateInfo: return "ImageCreateInfo"; + case StructureType::eImageViewCreateInfo: return "ImageViewCreateInfo"; + case StructureType::eShaderModuleCreateInfo: return "ShaderModuleCreateInfo"; + case StructureType::ePipelineCacheCreateInfo: return "PipelineCacheCreateInfo"; + case StructureType::ePipelineShaderStageCreateInfo: return "PipelineShaderStageCreateInfo"; + case StructureType::ePipelineVertexInputStateCreateInfo: return "PipelineVertexInputStateCreateInfo"; + case StructureType::ePipelineInputAssemblyStateCreateInfo: return "PipelineInputAssemblyStateCreateInfo"; + case StructureType::ePipelineTessellationStateCreateInfo: return "PipelineTessellationStateCreateInfo"; + case StructureType::ePipelineViewportStateCreateInfo: return "PipelineViewportStateCreateInfo"; + case StructureType::ePipelineRasterizationStateCreateInfo: return "PipelineRasterizationStateCreateInfo"; + case StructureType::ePipelineMultisampleStateCreateInfo: return "PipelineMultisampleStateCreateInfo"; + case StructureType::ePipelineDepthStencilStateCreateInfo: return "PipelineDepthStencilStateCreateInfo"; + case StructureType::ePipelineColorBlendStateCreateInfo: return "PipelineColorBlendStateCreateInfo"; + case StructureType::ePipelineDynamicStateCreateInfo: return "PipelineDynamicStateCreateInfo"; + case StructureType::eGraphicsPipelineCreateInfo: return "GraphicsPipelineCreateInfo"; + case StructureType::eComputePipelineCreateInfo: return "ComputePipelineCreateInfo"; + case StructureType::ePipelineLayoutCreateInfo: return "PipelineLayoutCreateInfo"; + case StructureType::eSamplerCreateInfo: return "SamplerCreateInfo"; + case StructureType::eDescriptorSetLayoutCreateInfo: return "DescriptorSetLayoutCreateInfo"; + case StructureType::eDescriptorPoolCreateInfo: return "DescriptorPoolCreateInfo"; + case StructureType::eDescriptorSetAllocateInfo: return "DescriptorSetAllocateInfo"; + case StructureType::eWriteDescriptorSet: return "WriteDescriptorSet"; + case StructureType::eCopyDescriptorSet: return "CopyDescriptorSet"; + case StructureType::eFramebufferCreateInfo: return "FramebufferCreateInfo"; + case StructureType::eRenderPassCreateInfo: return "RenderPassCreateInfo"; + case StructureType::eCommandPoolCreateInfo: return "CommandPoolCreateInfo"; + case StructureType::eCommandBufferAllocateInfo: return "CommandBufferAllocateInfo"; + case StructureType::eCommandBufferInheritanceInfo: return "CommandBufferInheritanceInfo"; + case StructureType::eCommandBufferBeginInfo: return "CommandBufferBeginInfo"; + case StructureType::eRenderPassBeginInfo: return "RenderPassBeginInfo"; + case StructureType::eBufferMemoryBarrier: return "BufferMemoryBarrier"; + case StructureType::eImageMemoryBarrier: return "ImageMemoryBarrier"; + case StructureType::eMemoryBarrier: return "MemoryBarrier"; + case StructureType::eLoaderInstanceCreateInfo: return "LoaderInstanceCreateInfo"; + case StructureType::eLoaderDeviceCreateInfo: return "LoaderDeviceCreateInfo"; + case StructureType::eSwapchainCreateInfoKHR: return "SwapchainCreateInfoKHR"; + case StructureType::ePresentInfoKHR: return "PresentInfoKHR"; + case StructureType::eDisplayModeCreateInfoKHR: return "DisplayModeCreateInfoKHR"; + case StructureType::eDisplaySurfaceCreateInfoKHR: return "DisplaySurfaceCreateInfoKHR"; + case StructureType::eDisplayPresentInfoKHR: return "DisplayPresentInfoKHR"; + case StructureType::eXlibSurfaceCreateInfoKHR: return "XlibSurfaceCreateInfoKHR"; + case StructureType::eXcbSurfaceCreateInfoKHR: return "XcbSurfaceCreateInfoKHR"; + case StructureType::eWaylandSurfaceCreateInfoKHR: return "WaylandSurfaceCreateInfoKHR"; + case StructureType::eMirSurfaceCreateInfoKHR: return "MirSurfaceCreateInfoKHR"; + case StructureType::eAndroidSurfaceCreateInfoKHR: return "AndroidSurfaceCreateInfoKHR"; + case StructureType::eWin32SurfaceCreateInfoKHR: return "Win32SurfaceCreateInfoKHR"; + case StructureType::eDebugReportCallbackCreateInfoEXT: return "DebugReportCallbackCreateInfoEXT"; + case StructureType::ePipelineRasterizationStateRasterizationOrderAMD: return "PipelineRasterizationStateRasterizationOrderAMD"; + case StructureType::eDebugMarkerObjectNameInfoEXT: return "DebugMarkerObjectNameInfoEXT"; + case StructureType::eDebugMarkerObjectTagInfoEXT: return "DebugMarkerObjectTagInfoEXT"; + case StructureType::eDebugMarkerMarkerInfoEXT: return "DebugMarkerMarkerInfoEXT"; + case StructureType::eDedicatedAllocationImageCreateInfoNV: return "DedicatedAllocationImageCreateInfoNV"; + case StructureType::eDedicatedAllocationBufferCreateInfoNV: return "DedicatedAllocationBufferCreateInfoNV"; + case StructureType::eDedicatedAllocationMemoryAllocateInfoNV: return "DedicatedAllocationMemoryAllocateInfoNV"; + case StructureType::eExternalMemoryImageCreateInfoNV: return "ExternalMemoryImageCreateInfoNV"; + case StructureType::eExportMemoryAllocateInfoNV: return "ExportMemoryAllocateInfoNV"; + case StructureType::eImportMemoryWin32HandleInfoNV: return "ImportMemoryWin32HandleInfoNV"; + case StructureType::eExportMemoryWin32HandleInfoNV: return "ExportMemoryWin32HandleInfoNV"; + case StructureType::eWin32KeyedMutexAcquireReleaseInfoNV: return "Win32KeyedMutexAcquireReleaseInfoNV"; + case StructureType::ePhysicalDeviceFeatures2KHR: return "PhysicalDeviceFeatures2KHR"; + case StructureType::ePhysicalDeviceProperties2KHR: return "PhysicalDeviceProperties2KHR"; + case StructureType::eFormatProperties2KHR: return "FormatProperties2KHR"; + case StructureType::eImageFormatProperties2KHR: return "ImageFormatProperties2KHR"; + case StructureType::ePhysicalDeviceImageFormatInfo2KHR: return "PhysicalDeviceImageFormatInfo2KHR"; + case StructureType::eQueueFamilyProperties2KHR: return "QueueFamilyProperties2KHR"; + case StructureType::ePhysicalDeviceMemoryProperties2KHR: return "PhysicalDeviceMemoryProperties2KHR"; + case StructureType::eSparseImageFormatProperties2KHR: return "SparseImageFormatProperties2KHR"; + case StructureType::ePhysicalDeviceSparseImageFormatInfo2KHR: return "PhysicalDeviceSparseImageFormatInfo2KHR"; + case StructureType::eValidationFlagsEXT: return "ValidationFlagsEXT"; + case StructureType::eViSurfaceCreateInfoNN: return "ViSurfaceCreateInfoNN"; + case StructureType::eObjectTableCreateInfoNVX: return "ObjectTableCreateInfoNVX"; + case StructureType::eIndirectCommandsLayoutCreateInfoNVX: return "IndirectCommandsLayoutCreateInfoNVX"; + case StructureType::eCmdProcessCommandsInfoNVX: return "CmdProcessCommandsInfoNVX"; + case StructureType::eCmdReserveSpaceForCommandsInfoNVX: return "CmdReserveSpaceForCommandsInfoNVX"; + case StructureType::eDeviceGeneratedCommandsLimitsNVX: return "DeviceGeneratedCommandsLimitsNVX"; + case StructureType::eDeviceGeneratedCommandsFeaturesNVX: return "DeviceGeneratedCommandsFeaturesNVX"; + case StructureType::eSurfaceCapabilities2EXT: return "SurfaceCapabilities2EXT"; + case StructureType::eDisplayPowerInfoEXT: return "DisplayPowerInfoEXT"; + case StructureType::eDeviceEventInfoEXT: return "DeviceEventInfoEXT"; + case StructureType::eDisplayEventInfoEXT: return "DisplayEventInfoEXT"; + case StructureType::eSwapchainCounterCreateInfoEXT: return "SwapchainCounterCreateInfoEXT"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SubpassContents value) + { + switch (value) + { + case SubpassContents::eInline: return "Inline"; + case SubpassContents::eSecondaryCommandBuffers: return "SecondaryCommandBuffers"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DynamicState value) + { + switch (value) + { + case DynamicState::eViewport: return "Viewport"; + case DynamicState::eScissor: return "Scissor"; + case DynamicState::eLineWidth: return "LineWidth"; + case DynamicState::eDepthBias: return "DepthBias"; + case DynamicState::eBlendConstants: return "BlendConstants"; + case DynamicState::eDepthBounds: return "DepthBounds"; + case DynamicState::eStencilCompareMask: return "StencilCompareMask"; + case DynamicState::eStencilWriteMask: return "StencilWriteMask"; + case DynamicState::eStencilReference: return "StencilReference"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(QueueFlagBits value) + { + switch (value) + { + case QueueFlagBits::eGraphics: return "Graphics"; + case QueueFlagBits::eCompute: return "Compute"; + case QueueFlagBits::eTransfer: return "Transfer"; + case QueueFlagBits::eSparseBinding: return "SparseBinding"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(QueueFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & QueueFlagBits::eGraphics) result += "Graphics | "; + if (value & QueueFlagBits::eCompute) result += "Compute | "; + if (value & QueueFlagBits::eTransfer) result += "Transfer | "; + if (value & QueueFlagBits::eSparseBinding) result += "SparseBinding | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(MemoryPropertyFlagBits value) + { + switch (value) + { + case MemoryPropertyFlagBits::eDeviceLocal: return "DeviceLocal"; + case MemoryPropertyFlagBits::eHostVisible: return "HostVisible"; + case MemoryPropertyFlagBits::eHostCoherent: return "HostCoherent"; + case MemoryPropertyFlagBits::eHostCached: return "HostCached"; + case MemoryPropertyFlagBits::eLazilyAllocated: return "LazilyAllocated"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(MemoryPropertyFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & MemoryPropertyFlagBits::eDeviceLocal) result += "DeviceLocal | "; + if (value & MemoryPropertyFlagBits::eHostVisible) result += "HostVisible | "; + if (value & MemoryPropertyFlagBits::eHostCoherent) result += "HostCoherent | "; + if (value & MemoryPropertyFlagBits::eHostCached) result += "HostCached | "; + if (value & MemoryPropertyFlagBits::eLazilyAllocated) result += "LazilyAllocated | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(MemoryHeapFlagBits value) + { + switch (value) + { + case MemoryHeapFlagBits::eDeviceLocal: return "DeviceLocal"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(MemoryHeapFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & MemoryHeapFlagBits::eDeviceLocal) result += "DeviceLocal | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(AccessFlagBits value) + { + switch (value) + { + case AccessFlagBits::eIndirectCommandRead: return "IndirectCommandRead"; + case AccessFlagBits::eIndexRead: return "IndexRead"; + case AccessFlagBits::eVertexAttributeRead: return "VertexAttributeRead"; + case AccessFlagBits::eUniformRead: return "UniformRead"; + case AccessFlagBits::eInputAttachmentRead: return "InputAttachmentRead"; + case AccessFlagBits::eShaderRead: return "ShaderRead"; + case AccessFlagBits::eShaderWrite: return "ShaderWrite"; + case AccessFlagBits::eColorAttachmentRead: return "ColorAttachmentRead"; + case AccessFlagBits::eColorAttachmentWrite: return "ColorAttachmentWrite"; + case AccessFlagBits::eDepthStencilAttachmentRead: return "DepthStencilAttachmentRead"; + case AccessFlagBits::eDepthStencilAttachmentWrite: return "DepthStencilAttachmentWrite"; + case AccessFlagBits::eTransferRead: return "TransferRead"; + case AccessFlagBits::eTransferWrite: return "TransferWrite"; + case AccessFlagBits::eHostRead: return "HostRead"; + case AccessFlagBits::eHostWrite: return "HostWrite"; + case AccessFlagBits::eMemoryRead: return "MemoryRead"; + case AccessFlagBits::eMemoryWrite: return "MemoryWrite"; + case AccessFlagBits::eCommandProcessReadNVX: return "CommandProcessReadNVX"; + case AccessFlagBits::eCommandProcessWriteNVX: return "CommandProcessWriteNVX"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(AccessFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & AccessFlagBits::eIndirectCommandRead) result += "IndirectCommandRead | "; + if (value & AccessFlagBits::eIndexRead) result += "IndexRead | "; + if (value & AccessFlagBits::eVertexAttributeRead) result += "VertexAttributeRead | "; + if (value & AccessFlagBits::eUniformRead) result += "UniformRead | "; + if (value & AccessFlagBits::eInputAttachmentRead) result += "InputAttachmentRead | "; + if (value & AccessFlagBits::eShaderRead) result += "ShaderRead | "; + if (value & AccessFlagBits::eShaderWrite) result += "ShaderWrite | "; + if (value & AccessFlagBits::eColorAttachmentRead) result += "ColorAttachmentRead | "; + if (value & AccessFlagBits::eColorAttachmentWrite) result += "ColorAttachmentWrite | "; + if (value & AccessFlagBits::eDepthStencilAttachmentRead) result += "DepthStencilAttachmentRead | "; + if (value & AccessFlagBits::eDepthStencilAttachmentWrite) result += "DepthStencilAttachmentWrite | "; + if (value & AccessFlagBits::eTransferRead) result += "TransferRead | "; + if (value & AccessFlagBits::eTransferWrite) result += "TransferWrite | "; + if (value & AccessFlagBits::eHostRead) result += "HostRead | "; + if (value & AccessFlagBits::eHostWrite) result += "HostWrite | "; + if (value & AccessFlagBits::eMemoryRead) result += "MemoryRead | "; + if (value & AccessFlagBits::eMemoryWrite) result += "MemoryWrite | "; + if (value & AccessFlagBits::eCommandProcessReadNVX) result += "CommandProcessReadNVX | "; + if (value & AccessFlagBits::eCommandProcessWriteNVX) result += "CommandProcessWriteNVX | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(BufferUsageFlagBits value) + { + switch (value) + { + case BufferUsageFlagBits::eTransferSrc: return "TransferSrc"; + case BufferUsageFlagBits::eTransferDst: return "TransferDst"; + case BufferUsageFlagBits::eUniformTexelBuffer: return "UniformTexelBuffer"; + case BufferUsageFlagBits::eStorageTexelBuffer: return "StorageTexelBuffer"; + case BufferUsageFlagBits::eUniformBuffer: return "UniformBuffer"; + case BufferUsageFlagBits::eStorageBuffer: return "StorageBuffer"; + case BufferUsageFlagBits::eIndexBuffer: return "IndexBuffer"; + case BufferUsageFlagBits::eVertexBuffer: return "VertexBuffer"; + case BufferUsageFlagBits::eIndirectBuffer: return "IndirectBuffer"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(BufferUsageFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & BufferUsageFlagBits::eTransferSrc) result += "TransferSrc | "; + if (value & BufferUsageFlagBits::eTransferDst) result += "TransferDst | "; + if (value & BufferUsageFlagBits::eUniformTexelBuffer) result += "UniformTexelBuffer | "; + if (value & BufferUsageFlagBits::eStorageTexelBuffer) result += "StorageTexelBuffer | "; + if (value & BufferUsageFlagBits::eUniformBuffer) result += "UniformBuffer | "; + if (value & BufferUsageFlagBits::eStorageBuffer) result += "StorageBuffer | "; + if (value & BufferUsageFlagBits::eIndexBuffer) result += "IndexBuffer | "; + if (value & BufferUsageFlagBits::eVertexBuffer) result += "VertexBuffer | "; + if (value & BufferUsageFlagBits::eIndirectBuffer) result += "IndirectBuffer | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(BufferCreateFlagBits value) + { + switch (value) + { + case BufferCreateFlagBits::eSparseBinding: return "SparseBinding"; + case BufferCreateFlagBits::eSparseResidency: return "SparseResidency"; + case BufferCreateFlagBits::eSparseAliased: return "SparseAliased"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(BufferCreateFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & BufferCreateFlagBits::eSparseBinding) result += "SparseBinding | "; + if (value & BufferCreateFlagBits::eSparseResidency) result += "SparseResidency | "; + if (value & BufferCreateFlagBits::eSparseAliased) result += "SparseAliased | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ShaderStageFlagBits value) + { + switch (value) + { + case ShaderStageFlagBits::eVertex: return "Vertex"; + case ShaderStageFlagBits::eTessellationControl: return "TessellationControl"; + case ShaderStageFlagBits::eTessellationEvaluation: return "TessellationEvaluation"; + case ShaderStageFlagBits::eGeometry: return "Geometry"; + case ShaderStageFlagBits::eFragment: return "Fragment"; + case ShaderStageFlagBits::eCompute: return "Compute"; + case ShaderStageFlagBits::eAllGraphics: return "AllGraphics"; + case ShaderStageFlagBits::eAll: return "All"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ShaderStageFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & ShaderStageFlagBits::eVertex) result += "Vertex | "; + if (value & ShaderStageFlagBits::eTessellationControl) result += "TessellationControl | "; + if (value & ShaderStageFlagBits::eTessellationEvaluation) result += "TessellationEvaluation | "; + if (value & ShaderStageFlagBits::eGeometry) result += "Geometry | "; + if (value & ShaderStageFlagBits::eFragment) result += "Fragment | "; + if (value & ShaderStageFlagBits::eCompute) result += "Compute | "; + if (value & ShaderStageFlagBits::eAllGraphics) result += "AllGraphics | "; + if (value & ShaderStageFlagBits::eAll) result += "All | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ImageUsageFlagBits value) + { + switch (value) + { + case ImageUsageFlagBits::eTransferSrc: return "TransferSrc"; + case ImageUsageFlagBits::eTransferDst: return "TransferDst"; + case ImageUsageFlagBits::eSampled: return "Sampled"; + case ImageUsageFlagBits::eStorage: return "Storage"; + case ImageUsageFlagBits::eColorAttachment: return "ColorAttachment"; + case ImageUsageFlagBits::eDepthStencilAttachment: return "DepthStencilAttachment"; + case ImageUsageFlagBits::eTransientAttachment: return "TransientAttachment"; + case ImageUsageFlagBits::eInputAttachment: return "InputAttachment"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ImageUsageFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & ImageUsageFlagBits::eTransferSrc) result += "TransferSrc | "; + if (value & ImageUsageFlagBits::eTransferDst) result += "TransferDst | "; + if (value & ImageUsageFlagBits::eSampled) result += "Sampled | "; + if (value & ImageUsageFlagBits::eStorage) result += "Storage | "; + if (value & ImageUsageFlagBits::eColorAttachment) result += "ColorAttachment | "; + if (value & ImageUsageFlagBits::eDepthStencilAttachment) result += "DepthStencilAttachment | "; + if (value & ImageUsageFlagBits::eTransientAttachment) result += "TransientAttachment | "; + if (value & ImageUsageFlagBits::eInputAttachment) result += "InputAttachment | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ImageCreateFlagBits value) + { + switch (value) + { + case ImageCreateFlagBits::eSparseBinding: return "SparseBinding"; + case ImageCreateFlagBits::eSparseResidency: return "SparseResidency"; + case ImageCreateFlagBits::eSparseAliased: return "SparseAliased"; + case ImageCreateFlagBits::eMutableFormat: return "MutableFormat"; + case ImageCreateFlagBits::eCubeCompatible: return "CubeCompatible"; + case ImageCreateFlagBits::e2DArrayCompatibleKHR: return "2DArrayCompatibleKHR"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ImageCreateFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & ImageCreateFlagBits::eSparseBinding) result += "SparseBinding | "; + if (value & ImageCreateFlagBits::eSparseResidency) result += "SparseResidency | "; + if (value & ImageCreateFlagBits::eSparseAliased) result += "SparseAliased | "; + if (value & ImageCreateFlagBits::eMutableFormat) result += "MutableFormat | "; + if (value & ImageCreateFlagBits::eCubeCompatible) result += "CubeCompatible | "; + if (value & ImageCreateFlagBits::e2DArrayCompatibleKHR) result += "2DArrayCompatibleKHR | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineCreateFlagBits value) + { + switch (value) + { + case PipelineCreateFlagBits::eDisableOptimization: return "DisableOptimization"; + case PipelineCreateFlagBits::eAllowDerivatives: return "AllowDerivatives"; + case PipelineCreateFlagBits::eDerivative: return "Derivative"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(PipelineCreateFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & PipelineCreateFlagBits::eDisableOptimization) result += "DisableOptimization | "; + if (value & PipelineCreateFlagBits::eAllowDerivatives) result += "AllowDerivatives | "; + if (value & PipelineCreateFlagBits::eDerivative) result += "Derivative | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ColorComponentFlagBits value) + { + switch (value) + { + case ColorComponentFlagBits::eR: return "R"; + case ColorComponentFlagBits::eG: return "G"; + case ColorComponentFlagBits::eB: return "B"; + case ColorComponentFlagBits::eA: return "A"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ColorComponentFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & ColorComponentFlagBits::eR) result += "R | "; + if (value & ColorComponentFlagBits::eG) result += "G | "; + if (value & ColorComponentFlagBits::eB) result += "B | "; + if (value & ColorComponentFlagBits::eA) result += "A | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(FenceCreateFlagBits value) + { + switch (value) + { + case FenceCreateFlagBits::eSignaled: return "Signaled"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(FenceCreateFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & FenceCreateFlagBits::eSignaled) result += "Signaled | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(FormatFeatureFlagBits value) + { + switch (value) + { + case FormatFeatureFlagBits::eSampledImage: return "SampledImage"; + case FormatFeatureFlagBits::eStorageImage: return "StorageImage"; + case FormatFeatureFlagBits::eStorageImageAtomic: return "StorageImageAtomic"; + case FormatFeatureFlagBits::eUniformTexelBuffer: return "UniformTexelBuffer"; + case FormatFeatureFlagBits::eStorageTexelBuffer: return "StorageTexelBuffer"; + case FormatFeatureFlagBits::eStorageTexelBufferAtomic: return "StorageTexelBufferAtomic"; + case FormatFeatureFlagBits::eVertexBuffer: return "VertexBuffer"; + case FormatFeatureFlagBits::eColorAttachment: return "ColorAttachment"; + case FormatFeatureFlagBits::eColorAttachmentBlend: return "ColorAttachmentBlend"; + case FormatFeatureFlagBits::eDepthStencilAttachment: return "DepthStencilAttachment"; + case FormatFeatureFlagBits::eBlitSrc: return "BlitSrc"; + case FormatFeatureFlagBits::eBlitDst: return "BlitDst"; + case FormatFeatureFlagBits::eSampledImageFilterLinear: return "SampledImageFilterLinear"; + case FormatFeatureFlagBits::eSampledImageFilterCubicIMG: return "SampledImageFilterCubicIMG"; + case FormatFeatureFlagBits::eTransferSrcKHR: return "TransferSrcKHR"; + case FormatFeatureFlagBits::eTransferDstKHR: return "TransferDstKHR"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(FormatFeatureFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & FormatFeatureFlagBits::eSampledImage) result += "SampledImage | "; + if (value & FormatFeatureFlagBits::eStorageImage) result += "StorageImage | "; + if (value & FormatFeatureFlagBits::eStorageImageAtomic) result += "StorageImageAtomic | "; + if (value & FormatFeatureFlagBits::eUniformTexelBuffer) result += "UniformTexelBuffer | "; + if (value & FormatFeatureFlagBits::eStorageTexelBuffer) result += "StorageTexelBuffer | "; + if (value & FormatFeatureFlagBits::eStorageTexelBufferAtomic) result += "StorageTexelBufferAtomic | "; + if (value & FormatFeatureFlagBits::eVertexBuffer) result += "VertexBuffer | "; + if (value & FormatFeatureFlagBits::eColorAttachment) result += "ColorAttachment | "; + if (value & FormatFeatureFlagBits::eColorAttachmentBlend) result += "ColorAttachmentBlend | "; + if (value & FormatFeatureFlagBits::eDepthStencilAttachment) result += "DepthStencilAttachment | "; + if (value & FormatFeatureFlagBits::eBlitSrc) result += "BlitSrc | "; + if (value & FormatFeatureFlagBits::eBlitDst) result += "BlitDst | "; + if (value & FormatFeatureFlagBits::eSampledImageFilterLinear) result += "SampledImageFilterLinear | "; + if (value & FormatFeatureFlagBits::eSampledImageFilterCubicIMG) result += "SampledImageFilterCubicIMG | "; + if (value & FormatFeatureFlagBits::eTransferSrcKHR) result += "TransferSrcKHR | "; + if (value & FormatFeatureFlagBits::eTransferDstKHR) result += "TransferDstKHR | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(QueryControlFlagBits value) + { + switch (value) + { + case QueryControlFlagBits::ePrecise: return "Precise"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(QueryControlFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & QueryControlFlagBits::ePrecise) result += "Precise | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(QueryResultFlagBits value) + { + switch (value) + { + case QueryResultFlagBits::e64: return "64"; + case QueryResultFlagBits::eWait: return "Wait"; + case QueryResultFlagBits::eWithAvailability: return "WithAvailability"; + case QueryResultFlagBits::ePartial: return "Partial"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(QueryResultFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & QueryResultFlagBits::e64) result += "64 | "; + if (value & QueryResultFlagBits::eWait) result += "Wait | "; + if (value & QueryResultFlagBits::eWithAvailability) result += "WithAvailability | "; + if (value & QueryResultFlagBits::ePartial) result += "Partial | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(CommandBufferUsageFlagBits value) + { + switch (value) + { + case CommandBufferUsageFlagBits::eOneTimeSubmit: return "OneTimeSubmit"; + case CommandBufferUsageFlagBits::eRenderPassContinue: return "RenderPassContinue"; + case CommandBufferUsageFlagBits::eSimultaneousUse: return "SimultaneousUse"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CommandBufferUsageFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & CommandBufferUsageFlagBits::eOneTimeSubmit) result += "OneTimeSubmit | "; + if (value & CommandBufferUsageFlagBits::eRenderPassContinue) result += "RenderPassContinue | "; + if (value & CommandBufferUsageFlagBits::eSimultaneousUse) result += "SimultaneousUse | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(QueryPipelineStatisticFlagBits value) + { + switch (value) + { + case QueryPipelineStatisticFlagBits::eInputAssemblyVertices: return "InputAssemblyVertices"; + case QueryPipelineStatisticFlagBits::eInputAssemblyPrimitives: return "InputAssemblyPrimitives"; + case QueryPipelineStatisticFlagBits::eVertexShaderInvocations: return "VertexShaderInvocations"; + case QueryPipelineStatisticFlagBits::eGeometryShaderInvocations: return "GeometryShaderInvocations"; + case QueryPipelineStatisticFlagBits::eGeometryShaderPrimitives: return "GeometryShaderPrimitives"; + case QueryPipelineStatisticFlagBits::eClippingInvocations: return "ClippingInvocations"; + case QueryPipelineStatisticFlagBits::eClippingPrimitives: return "ClippingPrimitives"; + case QueryPipelineStatisticFlagBits::eFragmentShaderInvocations: return "FragmentShaderInvocations"; + case QueryPipelineStatisticFlagBits::eTessellationControlShaderPatches: return "TessellationControlShaderPatches"; + case QueryPipelineStatisticFlagBits::eTessellationEvaluationShaderInvocations: return "TessellationEvaluationShaderInvocations"; + case QueryPipelineStatisticFlagBits::eComputeShaderInvocations: return "ComputeShaderInvocations"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(QueryPipelineStatisticFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & QueryPipelineStatisticFlagBits::eInputAssemblyVertices) result += "InputAssemblyVertices | "; + if (value & QueryPipelineStatisticFlagBits::eInputAssemblyPrimitives) result += "InputAssemblyPrimitives | "; + if (value & QueryPipelineStatisticFlagBits::eVertexShaderInvocations) result += "VertexShaderInvocations | "; + if (value & QueryPipelineStatisticFlagBits::eGeometryShaderInvocations) result += "GeometryShaderInvocations | "; + if (value & QueryPipelineStatisticFlagBits::eGeometryShaderPrimitives) result += "GeometryShaderPrimitives | "; + if (value & QueryPipelineStatisticFlagBits::eClippingInvocations) result += "ClippingInvocations | "; + if (value & QueryPipelineStatisticFlagBits::eClippingPrimitives) result += "ClippingPrimitives | "; + if (value & QueryPipelineStatisticFlagBits::eFragmentShaderInvocations) result += "FragmentShaderInvocations | "; + if (value & QueryPipelineStatisticFlagBits::eTessellationControlShaderPatches) result += "TessellationControlShaderPatches | "; + if (value & QueryPipelineStatisticFlagBits::eTessellationEvaluationShaderInvocations) result += "TessellationEvaluationShaderInvocations | "; + if (value & QueryPipelineStatisticFlagBits::eComputeShaderInvocations) result += "ComputeShaderInvocations | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ImageAspectFlagBits value) + { + switch (value) + { + case ImageAspectFlagBits::eColor: return "Color"; + case ImageAspectFlagBits::eDepth: return "Depth"; + case ImageAspectFlagBits::eStencil: return "Stencil"; + case ImageAspectFlagBits::eMetadata: return "Metadata"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ImageAspectFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & ImageAspectFlagBits::eColor) result += "Color | "; + if (value & ImageAspectFlagBits::eDepth) result += "Depth | "; + if (value & ImageAspectFlagBits::eStencil) result += "Stencil | "; + if (value & ImageAspectFlagBits::eMetadata) result += "Metadata | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(SparseImageFormatFlagBits value) + { + switch (value) + { + case SparseImageFormatFlagBits::eSingleMiptail: return "SingleMiptail"; + case SparseImageFormatFlagBits::eAlignedMipSize: return "AlignedMipSize"; + case SparseImageFormatFlagBits::eNonstandardBlockSize: return "NonstandardBlockSize"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SparseImageFormatFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & SparseImageFormatFlagBits::eSingleMiptail) result += "SingleMiptail | "; + if (value & SparseImageFormatFlagBits::eAlignedMipSize) result += "AlignedMipSize | "; + if (value & SparseImageFormatFlagBits::eNonstandardBlockSize) result += "NonstandardBlockSize | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(SparseMemoryBindFlagBits value) + { + switch (value) + { + case SparseMemoryBindFlagBits::eMetadata: return "Metadata"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SparseMemoryBindFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & SparseMemoryBindFlagBits::eMetadata) result += "Metadata | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(PipelineStageFlagBits value) + { + switch (value) + { + case PipelineStageFlagBits::eTopOfPipe: return "TopOfPipe"; + case PipelineStageFlagBits::eDrawIndirect: return "DrawIndirect"; + case PipelineStageFlagBits::eVertexInput: return "VertexInput"; + case PipelineStageFlagBits::eVertexShader: return "VertexShader"; + case PipelineStageFlagBits::eTessellationControlShader: return "TessellationControlShader"; + case PipelineStageFlagBits::eTessellationEvaluationShader: return "TessellationEvaluationShader"; + case PipelineStageFlagBits::eGeometryShader: return "GeometryShader"; + case PipelineStageFlagBits::eFragmentShader: return "FragmentShader"; + case PipelineStageFlagBits::eEarlyFragmentTests: return "EarlyFragmentTests"; + case PipelineStageFlagBits::eLateFragmentTests: return "LateFragmentTests"; + case PipelineStageFlagBits::eColorAttachmentOutput: return "ColorAttachmentOutput"; + case PipelineStageFlagBits::eComputeShader: return "ComputeShader"; + case PipelineStageFlagBits::eTransfer: return "Transfer"; + case PipelineStageFlagBits::eBottomOfPipe: return "BottomOfPipe"; + case PipelineStageFlagBits::eHost: return "Host"; + case PipelineStageFlagBits::eAllGraphics: return "AllGraphics"; + case PipelineStageFlagBits::eAllCommands: return "AllCommands"; + case PipelineStageFlagBits::eCommandProcessNVX: return "CommandProcessNVX"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(PipelineStageFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & PipelineStageFlagBits::eTopOfPipe) result += "TopOfPipe | "; + if (value & PipelineStageFlagBits::eDrawIndirect) result += "DrawIndirect | "; + if (value & PipelineStageFlagBits::eVertexInput) result += "VertexInput | "; + if (value & PipelineStageFlagBits::eVertexShader) result += "VertexShader | "; + if (value & PipelineStageFlagBits::eTessellationControlShader) result += "TessellationControlShader | "; + if (value & PipelineStageFlagBits::eTessellationEvaluationShader) result += "TessellationEvaluationShader | "; + if (value & PipelineStageFlagBits::eGeometryShader) result += "GeometryShader | "; + if (value & PipelineStageFlagBits::eFragmentShader) result += "FragmentShader | "; + if (value & PipelineStageFlagBits::eEarlyFragmentTests) result += "EarlyFragmentTests | "; + if (value & PipelineStageFlagBits::eLateFragmentTests) result += "LateFragmentTests | "; + if (value & PipelineStageFlagBits::eColorAttachmentOutput) result += "ColorAttachmentOutput | "; + if (value & PipelineStageFlagBits::eComputeShader) result += "ComputeShader | "; + if (value & PipelineStageFlagBits::eTransfer) result += "Transfer | "; + if (value & PipelineStageFlagBits::eBottomOfPipe) result += "BottomOfPipe | "; + if (value & PipelineStageFlagBits::eHost) result += "Host | "; + if (value & PipelineStageFlagBits::eAllGraphics) result += "AllGraphics | "; + if (value & PipelineStageFlagBits::eAllCommands) result += "AllCommands | "; + if (value & PipelineStageFlagBits::eCommandProcessNVX) result += "CommandProcessNVX | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(CommandPoolCreateFlagBits value) + { + switch (value) + { + case CommandPoolCreateFlagBits::eTransient: return "Transient"; + case CommandPoolCreateFlagBits::eResetCommandBuffer: return "ResetCommandBuffer"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CommandPoolCreateFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & CommandPoolCreateFlagBits::eTransient) result += "Transient | "; + if (value & CommandPoolCreateFlagBits::eResetCommandBuffer) result += "ResetCommandBuffer | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(CommandPoolResetFlagBits value) + { + switch (value) + { + case CommandPoolResetFlagBits::eReleaseResources: return "ReleaseResources"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CommandPoolResetFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & CommandPoolResetFlagBits::eReleaseResources) result += "ReleaseResources | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(CommandBufferResetFlagBits value) + { + switch (value) + { + case CommandBufferResetFlagBits::eReleaseResources: return "ReleaseResources"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CommandBufferResetFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & CommandBufferResetFlagBits::eReleaseResources) result += "ReleaseResources | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(SampleCountFlagBits value) + { + switch (value) + { + case SampleCountFlagBits::e1: return "1"; + case SampleCountFlagBits::e2: return "2"; + case SampleCountFlagBits::e4: return "4"; + case SampleCountFlagBits::e8: return "8"; + case SampleCountFlagBits::e16: return "16"; + case SampleCountFlagBits::e32: return "32"; + case SampleCountFlagBits::e64: return "64"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SampleCountFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & SampleCountFlagBits::e1) result += "1 | "; + if (value & SampleCountFlagBits::e2) result += "2 | "; + if (value & SampleCountFlagBits::e4) result += "4 | "; + if (value & SampleCountFlagBits::e8) result += "8 | "; + if (value & SampleCountFlagBits::e16) result += "16 | "; + if (value & SampleCountFlagBits::e32) result += "32 | "; + if (value & SampleCountFlagBits::e64) result += "64 | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(AttachmentDescriptionFlagBits value) + { + switch (value) + { + case AttachmentDescriptionFlagBits::eMayAlias: return "MayAlias"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(AttachmentDescriptionFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & AttachmentDescriptionFlagBits::eMayAlias) result += "MayAlias | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(StencilFaceFlagBits value) + { + switch (value) + { + case StencilFaceFlagBits::eFront: return "Front"; + case StencilFaceFlagBits::eBack: return "Back"; + case StencilFaceFlagBits::eVkStencilFrontAndBack: return "VkStencilFrontAndBack"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(StencilFaceFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & StencilFaceFlagBits::eFront) result += "Front | "; + if (value & StencilFaceFlagBits::eBack) result += "Back | "; + if (value & StencilFaceFlagBits::eVkStencilFrontAndBack) result += "VkStencilFrontAndBack | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(DescriptorPoolCreateFlagBits value) + { + switch (value) + { + case DescriptorPoolCreateFlagBits::eFreeDescriptorSet: return "FreeDescriptorSet"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DescriptorPoolCreateFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & DescriptorPoolCreateFlagBits::eFreeDescriptorSet) result += "FreeDescriptorSet | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(DependencyFlagBits value) + { + switch (value) + { + case DependencyFlagBits::eByRegion: return "ByRegion"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DependencyFlags value) + { + if (!value) return "{}"; + std::string result; + if (value & DependencyFlagBits::eByRegion) result += "ByRegion | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(PresentModeKHR value) + { + switch (value) + { + case PresentModeKHR::eImmediate: return "Immediate"; + case PresentModeKHR::eMailbox: return "Mailbox"; + case PresentModeKHR::eFifo: return "Fifo"; + case PresentModeKHR::eFifoRelaxed: return "FifoRelaxed"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ColorSpaceKHR value) + { + switch (value) + { + case ColorSpaceKHR::eSrgbNonlinear: return "SrgbNonlinear"; + case ColorSpaceKHR::eDisplayP3LinearEXT: return "DisplayP3LinearEXT"; + case ColorSpaceKHR::eDisplayP3NonlinearEXT: return "DisplayP3NonlinearEXT"; + case ColorSpaceKHR::eScrgbLinearEXT: return "ScrgbLinearEXT"; + case ColorSpaceKHR::eScrgbNonlinearEXT: return "ScrgbNonlinearEXT"; + case ColorSpaceKHR::eDciP3LinearEXT: return "DciP3LinearEXT"; + case ColorSpaceKHR::eDciP3NonlinearEXT: return "DciP3NonlinearEXT"; + case ColorSpaceKHR::eBt709LinearEXT: return "Bt709LinearEXT"; + case ColorSpaceKHR::eBt709NonlinearEXT: return "Bt709NonlinearEXT"; + case ColorSpaceKHR::eBt2020LinearEXT: return "Bt2020LinearEXT"; + case ColorSpaceKHR::eBt2020NonlinearEXT: return "Bt2020NonlinearEXT"; + case ColorSpaceKHR::eAdobergbLinearEXT: return "AdobergbLinearEXT"; + case ColorSpaceKHR::eAdobergbNonlinearEXT: return "AdobergbNonlinearEXT"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DisplayPlaneAlphaFlagBitsKHR value) + { + switch (value) + { + case DisplayPlaneAlphaFlagBitsKHR::eOpaque: return "Opaque"; + case DisplayPlaneAlphaFlagBitsKHR::eGlobal: return "Global"; + case DisplayPlaneAlphaFlagBitsKHR::ePerPixel: return "PerPixel"; + case DisplayPlaneAlphaFlagBitsKHR::ePerPixelPremultiplied: return "PerPixelPremultiplied"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DisplayPlaneAlphaFlagsKHR value) + { + if (!value) return "{}"; + std::string result; + if (value & DisplayPlaneAlphaFlagBitsKHR::eOpaque) result += "Opaque | "; + if (value & DisplayPlaneAlphaFlagBitsKHR::eGlobal) result += "Global | "; + if (value & DisplayPlaneAlphaFlagBitsKHR::ePerPixel) result += "PerPixel | "; + if (value & DisplayPlaneAlphaFlagBitsKHR::ePerPixelPremultiplied) result += "PerPixelPremultiplied | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(CompositeAlphaFlagBitsKHR value) + { + switch (value) + { + case CompositeAlphaFlagBitsKHR::eOpaque: return "Opaque"; + case CompositeAlphaFlagBitsKHR::ePreMultiplied: return "PreMultiplied"; + case CompositeAlphaFlagBitsKHR::ePostMultiplied: return "PostMultiplied"; + case CompositeAlphaFlagBitsKHR::eInherit: return "Inherit"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(CompositeAlphaFlagsKHR value) + { + if (!value) return "{}"; + std::string result; + if (value & CompositeAlphaFlagBitsKHR::eOpaque) result += "Opaque | "; + if (value & CompositeAlphaFlagBitsKHR::ePreMultiplied) result += "PreMultiplied | "; + if (value & CompositeAlphaFlagBitsKHR::ePostMultiplied) result += "PostMultiplied | "; + if (value & CompositeAlphaFlagBitsKHR::eInherit) result += "Inherit | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(SurfaceTransformFlagBitsKHR value) + { + switch (value) + { + case SurfaceTransformFlagBitsKHR::eIdentity: return "Identity"; + case SurfaceTransformFlagBitsKHR::eRotate90: return "Rotate90"; + case SurfaceTransformFlagBitsKHR::eRotate180: return "Rotate180"; + case SurfaceTransformFlagBitsKHR::eRotate270: return "Rotate270"; + case SurfaceTransformFlagBitsKHR::eHorizontalMirror: return "HorizontalMirror"; + case SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90: return "HorizontalMirrorRotate90"; + case SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180: return "HorizontalMirrorRotate180"; + case SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270: return "HorizontalMirrorRotate270"; + case SurfaceTransformFlagBitsKHR::eInherit: return "Inherit"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SurfaceTransformFlagsKHR value) + { + if (!value) return "{}"; + std::string result; + if (value & SurfaceTransformFlagBitsKHR::eIdentity) result += "Identity | "; + if (value & SurfaceTransformFlagBitsKHR::eRotate90) result += "Rotate90 | "; + if (value & SurfaceTransformFlagBitsKHR::eRotate180) result += "Rotate180 | "; + if (value & SurfaceTransformFlagBitsKHR::eRotate270) result += "Rotate270 | "; + if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirror) result += "HorizontalMirror | "; + if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90) result += "HorizontalMirrorRotate90 | "; + if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180) result += "HorizontalMirrorRotate180 | "; + if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270) result += "HorizontalMirrorRotate270 | "; + if (value & SurfaceTransformFlagBitsKHR::eInherit) result += "Inherit | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(DebugReportFlagBitsEXT value) + { + switch (value) + { + case DebugReportFlagBitsEXT::eInformation: return "Information"; + case DebugReportFlagBitsEXT::eWarning: return "Warning"; + case DebugReportFlagBitsEXT::ePerformanceWarning: return "PerformanceWarning"; + case DebugReportFlagBitsEXT::eError: return "Error"; + case DebugReportFlagBitsEXT::eDebug: return "Debug"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DebugReportFlagsEXT value) + { + if (!value) return "{}"; + std::string result; + if (value & DebugReportFlagBitsEXT::eInformation) result += "Information | "; + if (value & DebugReportFlagBitsEXT::eWarning) result += "Warning | "; + if (value & DebugReportFlagBitsEXT::ePerformanceWarning) result += "PerformanceWarning | "; + if (value & DebugReportFlagBitsEXT::eError) result += "Error | "; + if (value & DebugReportFlagBitsEXT::eDebug) result += "Debug | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(DebugReportObjectTypeEXT value) + { + switch (value) + { + case DebugReportObjectTypeEXT::eUnknown: return "Unknown"; + case DebugReportObjectTypeEXT::eInstance: return "Instance"; + case DebugReportObjectTypeEXT::ePhysicalDevice: return "PhysicalDevice"; + case DebugReportObjectTypeEXT::eDevice: return "Device"; + case DebugReportObjectTypeEXT::eQueue: return "Queue"; + case DebugReportObjectTypeEXT::eSemaphore: return "Semaphore"; + case DebugReportObjectTypeEXT::eCommandBuffer: return "CommandBuffer"; + case DebugReportObjectTypeEXT::eFence: return "Fence"; + case DebugReportObjectTypeEXT::eDeviceMemory: return "DeviceMemory"; + case DebugReportObjectTypeEXT::eBuffer: return "Buffer"; + case DebugReportObjectTypeEXT::eImage: return "Image"; + case DebugReportObjectTypeEXT::eEvent: return "Event"; + case DebugReportObjectTypeEXT::eQueryPool: return "QueryPool"; + case DebugReportObjectTypeEXT::eBufferView: return "BufferView"; + case DebugReportObjectTypeEXT::eImageView: return "ImageView"; + case DebugReportObjectTypeEXT::eShaderModule: return "ShaderModule"; + case DebugReportObjectTypeEXT::ePipelineCache: return "PipelineCache"; + case DebugReportObjectTypeEXT::ePipelineLayout: return "PipelineLayout"; + case DebugReportObjectTypeEXT::eRenderPass: return "RenderPass"; + case DebugReportObjectTypeEXT::ePipeline: return "Pipeline"; + case DebugReportObjectTypeEXT::eDescriptorSetLayout: return "DescriptorSetLayout"; + case DebugReportObjectTypeEXT::eSampler: return "Sampler"; + case DebugReportObjectTypeEXT::eDescriptorPool: return "DescriptorPool"; + case DebugReportObjectTypeEXT::eDescriptorSet: return "DescriptorSet"; + case DebugReportObjectTypeEXT::eFramebuffer: return "Framebuffer"; + case DebugReportObjectTypeEXT::eCommandPool: return "CommandPool"; + case DebugReportObjectTypeEXT::eSurfaceKhr: return "SurfaceKhr"; + case DebugReportObjectTypeEXT::eSwapchainKhr: return "SwapchainKhr"; + case DebugReportObjectTypeEXT::eDebugReport: return "DebugReport"; + case DebugReportObjectTypeEXT::eDisplayKhr: return "DisplayKhr"; + case DebugReportObjectTypeEXT::eDisplayModeKhr: return "DisplayModeKhr"; + case DebugReportObjectTypeEXT::eObjectTableNvx: return "ObjectTableNvx"; + case DebugReportObjectTypeEXT::eIndirectCommandsLayoutNvx: return "IndirectCommandsLayoutNvx"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DebugReportErrorEXT value) + { + switch (value) + { + case DebugReportErrorEXT::eNone: return "None"; + case DebugReportErrorEXT::eCallbackRef: return "CallbackRef"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(RasterizationOrderAMD value) + { + switch (value) + { + case RasterizationOrderAMD::eStrict: return "Strict"; + case RasterizationOrderAMD::eRelaxed: return "Relaxed"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ExternalMemoryHandleTypeFlagBitsNV value) + { + switch (value) + { + case ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32: return "OpaqueWin32"; + case ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32Kmt: return "OpaqueWin32Kmt"; + case ExternalMemoryHandleTypeFlagBitsNV::eD3D11Image: return "D3D11Image"; + case ExternalMemoryHandleTypeFlagBitsNV::eD3D11ImageKmt: return "D3D11ImageKmt"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ExternalMemoryHandleTypeFlagsNV value) + { + if (!value) return "{}"; + std::string result; + if (value & ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32) result += "OpaqueWin32 | "; + if (value & ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32Kmt) result += "OpaqueWin32Kmt | "; + if (value & ExternalMemoryHandleTypeFlagBitsNV::eD3D11Image) result += "D3D11Image | "; + if (value & ExternalMemoryHandleTypeFlagBitsNV::eD3D11ImageKmt) result += "D3D11ImageKmt | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ExternalMemoryFeatureFlagBitsNV value) + { + switch (value) + { + case ExternalMemoryFeatureFlagBitsNV::eDedicatedOnly: return "DedicatedOnly"; + case ExternalMemoryFeatureFlagBitsNV::eExportable: return "Exportable"; + case ExternalMemoryFeatureFlagBitsNV::eImportable: return "Importable"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ExternalMemoryFeatureFlagsNV value) + { + if (!value) return "{}"; + std::string result; + if (value & ExternalMemoryFeatureFlagBitsNV::eDedicatedOnly) result += "DedicatedOnly | "; + if (value & ExternalMemoryFeatureFlagBitsNV::eExportable) result += "Exportable | "; + if (value & ExternalMemoryFeatureFlagBitsNV::eImportable) result += "Importable | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ValidationCheckEXT value) + { + switch (value) + { + case ValidationCheckEXT::eAll: return "All"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(IndirectCommandsLayoutUsageFlagBitsNVX value) + { + switch (value) + { + case IndirectCommandsLayoutUsageFlagBitsNVX::eUnorderedSequences: return "UnorderedSequences"; + case IndirectCommandsLayoutUsageFlagBitsNVX::eSparseSequences: return "SparseSequences"; + case IndirectCommandsLayoutUsageFlagBitsNVX::eEmptyExecutions: return "EmptyExecutions"; + case IndirectCommandsLayoutUsageFlagBitsNVX::eIndexedSequences: return "IndexedSequences"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(IndirectCommandsLayoutUsageFlagsNVX value) + { + if (!value) return "{}"; + std::string result; + if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eUnorderedSequences) result += "UnorderedSequences | "; + if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eSparseSequences) result += "SparseSequences | "; + if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eEmptyExecutions) result += "EmptyExecutions | "; + if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eIndexedSequences) result += "IndexedSequences | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(ObjectEntryUsageFlagBitsNVX value) + { + switch (value) + { + case ObjectEntryUsageFlagBitsNVX::eGraphics: return "Graphics"; + case ObjectEntryUsageFlagBitsNVX::eCompute: return "Compute"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ObjectEntryUsageFlagsNVX value) + { + if (!value) return "{}"; + std::string result; + if (value & ObjectEntryUsageFlagBitsNVX::eGraphics) result += "Graphics | "; + if (value & ObjectEntryUsageFlagBitsNVX::eCompute) result += "Compute | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(IndirectCommandsTokenTypeNVX value) + { + switch (value) + { + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPipeline: return "VkIndirectCommandsTokenPipeline"; + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDescriptorSet: return "VkIndirectCommandsTokenDescriptorSet"; + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenIndexBuffer: return "VkIndirectCommandsTokenIndexBuffer"; + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenVertexBuffer: return "VkIndirectCommandsTokenVertexBuffer"; + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPushConstant: return "VkIndirectCommandsTokenPushConstant"; + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDrawIndexed: return "VkIndirectCommandsTokenDrawIndexed"; + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDraw: return "VkIndirectCommandsTokenDraw"; + case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDispatch: return "VkIndirectCommandsTokenDispatch"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(ObjectEntryTypeNVX value) + { + switch (value) + { + case ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet: return "VkObjectEntryDescriptorSet"; + case ObjectEntryTypeNVX::eVkObjectEntryPipeline: return "VkObjectEntryPipeline"; + case ObjectEntryTypeNVX::eVkObjectEntryIndexBuffer: return "VkObjectEntryIndexBuffer"; + case ObjectEntryTypeNVX::eVkObjectEntryVertexBuffer: return "VkObjectEntryVertexBuffer"; + case ObjectEntryTypeNVX::eVkObjectEntryPushConstant: return "VkObjectEntryPushConstant"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SurfaceCounterFlagBitsEXT value) + { + switch (value) + { + case SurfaceCounterFlagBitsEXT::eVblankExt: return "VblankExt"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(SurfaceCounterFlagsEXT value) + { + if (!value) return "{}"; + std::string result; + if (value & SurfaceCounterFlagBitsEXT::eVblankExt) result += "VblankExt | "; + return "{" + result.substr(0, result.size() - 3) + "}"; + } + + VULKAN_HPP_INLINE std::string to_string(DisplayPowerStateEXT value) + { + switch (value) + { + case DisplayPowerStateEXT::eOff: return "Off"; + case DisplayPowerStateEXT::eSuspend: return "Suspend"; + case DisplayPowerStateEXT::eOn: return "On"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DeviceEventTypeEXT value) + { + switch (value) + { + case DeviceEventTypeEXT::eDisplayHotplug: return "DisplayHotplug"; + default: return "invalid"; + } + } + + VULKAN_HPP_INLINE std::string to_string(DisplayEventTypeEXT value) + { + switch (value) + { + case DisplayEventTypeEXT::eFirstPixelOut: return "FirstPixelOut"; + default: return "invalid"; + } + } + +} // namespace vk + +#endif diff --git a/test_data/sample_files/with (parens).cpp b/test_data/sample_files/with (parens).cpp new file mode 100644 index 0000000..b1b062f --- /dev/null +++ b/test_data/sample_files/with (parens).cpp @@ -0,0 +1,30 @@ +/* + +This is a file meant to be built in order to create +a tricky test for the jump to parser, by placing +parens inside the file name. Therefore it must +trigger a number of compilation errors. + +It should be accompanied with the approprioate build.bat + +*/ + +struct Important_Thing{ + char *ptr; +}; + +Important_Thing +foo(int x){ + Important_Thing thing; + thing.ptr = x; + thing.bar = foo; + return( +} + +int +main(int argc, char **argv){ + Important_Thing a = foo(3) = foo(4); + return(0); +} + + diff --git a/test_data/sample_files/word-controlled-test.txt b/test_data/sample_files/word-controlled-test.txt new file mode 100644 index 0000000..e5a13f1 --- /dev/null +++ b/test_data/sample_files/word-controlled-test.txt @@ -0,0 +1,11 @@ +a aa aaa aaaa aaaaa +a aa aaa aaaa aaaaa + + b bb bbbb bbbbbbbb bbbbbbbbbbbbbbbb + b bb bbbb bbbbbbbb bbbbbbbbbbbbbbbb + + a b c d e + a b c d e + +aaaa aaaa aaaa a +aaaa aaaa aaaa a diff --git a/test_data/sample_files/wrapped_lines.txt b/test_data/sample_files/wrapped_lines.txt new file mode 100644 index 0000000..e14a6c8 --- /dev/null +++ b/test_data/sample_files/wrapped_lines.txt @@ -0,0 +1,30 @@ + +Explenation for document: + +I am having an issue with wrapped lines... or so I am told. I have a few ideas on what might cause it, some being fairly difficult to fix others being quite straightforward. Either way I first need a test case to expose the problem, so I shall write a piece of fictional literature that has been on my mind in the recent past. + + +The story: + +It was my first time compiling a piece of software with an ancient C++ compiler. The complex rules of the compiler were hard to fully grasp, not to mention the emulated x64 chip. Still, I believed my program would read the smallest one thousand prime natural numbers, perhaps after a lot of trouble shooting. There was something thrilling about the way people use to construct software way back then. + +After that little experiment I thought I had a good grasp of the era. I thought certainly with these computational tools the ancient programmers could not have built any of the monuments we normally attribute to them. + +"There is clearly something about the historical record that does not add up", I thought to myself. "Perhaps in the early 21st century they had better tools than we thought... but how could that be? All the documents from the engineers responsible for creating tools like C++ are clearly dated for that time period. Is it possible that the documents we have now were altered accidentally or maliciously?" + +I tried not to spend time on hypothesise that would be untestable. So I turned my thoughts again to how to the basic facts of the era. I began thinking "It took me just ten days to create a C++ program to generate one thousand primes. Perhaps if I just run the numbers I will see that it actually makes sense... " but I ran the numbers, even taking the figure of one hundred and twenty million programmers at any given time, which is believed to be an accurate figure for that era, and quadrupling it, the amount of 'code' produced just did not add up. That calculation does not even take into account the intercomplexification that the software would have as it grew larger and how that would further slow the process." + +"Given the amount of software production we attribute to that time period, they must have had some better tool that slipped out of the historical record, that created the C++ programs for them." I had started suspecting this very early on. Actually, I almost resorted to this method myself for my prime program. "Still it does not really make sense. If they had a better tool for making the C++ programs, why could they not have just cut C++ out of their systems? Why did they spend so much time documenting and expanding it? And, ultimately, how could such a tool have been lost in the historical record?" + +Again it seemed like an unpromising path of speculation. There was no reason to believe such a tool existed. It seemed, to me, more likely that the dates in the documents were wrong, but that was not particularly satisfying either. + +There was plenty of record of the population interacting with all this inexplicable software too. There was no way this was some sort of propaganda piece. I looked through all sorts of commentaries from ancient 'databases'; accessing those was, by the way, another great ordeal of work. It was clear that the monumental software of the time, while inexplicably huge, had a number of problems. It was certainly not the smooth, beautiful, reliable, computation we enjoy today. The commentaries I looked at were full of methods to make better use of poorly functioning systems, and complaints about systems that ran too slowly for apparently undiagnosable reasons. + +I eventually found some samples of a C++ program code and I was totally baffled at what I discovered. Not that it revealed any new information, but what it suggested very perfectly fit with the belief that they really did hand write their C++ software and the dates in the source checked out with the dates in the standards. The source texts were often erradic having no particular rule about how things should be ordered. There were blank lines scattered all over the place, contributing nothing to the meaning of the software. There were even notes in natural language describing how code worked, sometimes leaving reminders about sensitive portions, and very often the notes served simply as a request that some of the source text be reexamined or deleted. + +There were plenty of other tools similar to C++, and I took a brief look into those tools too. All of them were basically the same concept as C++, requiring engineers to undergo the tedious task of spelling out all sorts of arcane details about what the software should do to achieve the desired effect. Among the commentaries there are thoughts from people who appear to be engineers about which tool is better for particular purposes or which tool is better over all, but I can see no reason to beleive that any of these tools were different in any way that would be important, they were all too primitive to account for the massive undertakings of the era. + +After studying this mystery for a while, I slowly started to realize that the ancient programmers really just wrote programs with their tools way more quickly than we could imagine. We assume that we are smarter than our ancestors because we are reaping the benefits of progress they did not have, but perhaps the convenience that surrounds us dulls our minds. Perhaps the ancient programmers worked hard and were a lot more clever than we realized... + +But if they were so clever and so capable to build these monuments by hand, why did humanity not arrive at modern style software sooner? + diff --git a/test_data/sample_files/zlib1.dll b/test_data/sample_files/zlib1.dll new file mode 100644 index 0000000..f01e65c Binary files /dev/null and b/test_data/sample_files/zlib1.dll differ